From b341b9ac28b33739508aa2a5c6a85555d94d7db0 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Sun, 23 Sep 2018 16:19:11 -0400 Subject: [PATCH] Prettify all js files --- src/controls/controls-system.js | 92 +- src/controls/controls.js | 240 +- src/controls/keyboard.js | 28 +- src/controls/keycodes.js | 192 +- src/controls/mouse.js | 30 +- src/controls/touch.js | 51 +- src/core/animation.js | 192 +- src/core/core.js | 638 +- src/core/extensions.js | 75 +- src/core/loader.js | 139 +- src/core/model.js | 126 +- src/core/scenes.js | 33 +- src/core/storage.js | 70 +- src/core/systems.js | 25 +- src/core/time.js | 27 +- src/core/tween.js | 318 +- src/core/utility.js | 63 +- src/core/version.js | 2 +- src/debug/debug-layer.js | 184 +- src/debug/logging.js | 39 +- src/graphics/canvas-layer.js | 121 +- src/graphics/canvas.js | 37 +- src/graphics/color.js | 196 +- src/graphics/dom-helper.js | 95 +- src/graphics/dom-layer.js | 75 +- src/graphics/dom.js | 75 +- src/graphics/drawing.js | 18 +- src/graphics/gl-textures.js | 71 +- src/graphics/html.js | 25 +- src/graphics/image.js | 96 +- src/graphics/layers.js | 59 +- src/graphics/particles.js | 151 +- src/graphics/renderable.js | 45 +- src/graphics/sprite-animation.js | 74 +- src/graphics/sprite.js | 144 +- src/graphics/text.js | 165 +- src/graphics/viewport.js | 347 +- src/graphics/webgl-layer.js | 108 +- src/graphics/webgl.js | 78 +- src/inputs/device.js | 73 +- src/inputs/dom-events.js | 9 +- src/inputs/keyboard.js | 78 +- src/inputs/lifecycle.js | 233 +- src/inputs/mouse.js | 351 +- src/inputs/pointer.js | 58 +- src/inputs/touch.js | 241 +- src/inputs/util.js | 30 +- src/isometric/diamond-iso.js | 167 +- src/isometric/isometric.js | 63 +- src/sound/sound.js | 179 +- src/spatial/2d.js | 503 +- src/spatial/collision.js | 218 +- src/spatial/math.js | 342 +- src/spatial/motion.js | 205 +- src/spatial/platform.js | 72 +- src/spatial/rect-manager.js | 326 +- src/spatial/spatial-grid.js | 109 +- tests/unit/common.js | 50 +- tests/unit/controls/controls.js | 627 +- tests/unit/core/animation.js | 5 +- tests/unit/core/core.js | 253 +- tests/unit/core/events.js | 179 +- tests/unit/core/instances.js | 16 +- tests/unit/core/loader.js | 91 +- tests/unit/core/model.js | 162 +- tests/unit/core/scenes.js | 12 +- tests/unit/core/storage.js | 34 +- tests/unit/core/systems.js | 191 +- tests/unit/core/time.js | 42 +- tests/unit/core/tween.js | 142 +- tests/unit/debug/debug.js | 213 +- tests/unit/debug/logging.js | 35 +- tests/unit/graphics/color.js | 21 +- tests/unit/graphics/dom-helper.js | 29 +- tests/unit/graphics/dom.js | 52 +- tests/unit/graphics/sprite-animation.js | 964 +- tests/unit/graphics/text.js | 160 +- tests/unit/graphics/viewport.js | 295 +- tests/unit/index-common.js | 56 +- tests/unit/index-headless.js | 6 +- tests/unit/index.js | 26 +- tests/unit/inputs/inputs.js | 314 +- tests/unit/isometric/isometric.js | 104 +- tests/unit/lib/mockTouchEvents.js | 121 +- tests/unit/lib/qunit.js | 9908 ++++++++++-------- tests/unit/sound/audio.js | 7 +- tests/unit/spatial/2d.js | 328 +- tests/unit/spatial/collision.js | 606 +- tests/unit/spatial/math.js | 348 +- tests/unit/spatial/motion.js | 194 +- tests/unit/spatial/platform.js | 459 +- tests/unit/spatial/raycast.js | 506 +- tests/unit/spatial/sat.js | 54 +- tests/unit/spatial/spatial-grid.js | 801 +- tests/webdriver/color/color-canvas.js | 2 +- tests/webdriver/color/color-common.js | 43 +- tests/webdriver/color/color-dom.js | 2 +- tests/webdriver/color/color-webgl.js | 2 +- tests/webdriver/commands/browser.js | 236 +- tests/webdriver/commands/generic.js | 117 +- tests/webdriver/commands/test.js | 225 +- tests/webdriver/common.js | 69 +- tests/webdriver/index-webdriver-cloud.js | 34 +- tests/webdriver/index-webdriver-local.js | 30 +- tests/webdriver/index-webdriver.js | 53 +- tests/webdriver/template/template-generic.js | 2 +- tests/webdriver/template/template-local.js | 4 +- tests/webdriver/template/template-multi.js | 52 +- 108 files changed, 15289 insertions(+), 10794 deletions(-) diff --git a/src/controls/controls-system.js b/src/controls/controls-system.js index c88b90f4..0262791b 100644 --- a/src/controls/controls-system.js +++ b/src/controls/controls-system.js @@ -1,13 +1,9 @@ -var Crafty = require('../core/core.js'); - +var Crafty = require("../core/core.js"); // ToggleInput contract // Must provide an isDown method which returns whether the input is down or not // May provide a destroy method which can be used for cleanup - - - // MouseButtonToggleInput function MouseButtonToggleInput(button) { this.button = button; @@ -16,7 +12,7 @@ function MouseButtonToggleInput(button) { MouseButtonToggleInput.prototype = { isDown: function() { var mouseSystem = this.mouseSystem; - if (!mouseSystem) this.mouseSystem = mouseSystem = Crafty.s('Mouse'); + if (!mouseSystem) this.mouseSystem = mouseSystem = Crafty.s("Mouse"); return mouseSystem.isButtonDown(this.button); } }; @@ -29,12 +25,12 @@ function KeyboardToggleInput(key) { KeyboardToggleInput.prototype = { isDown: function() { var keyboardSystem = this.keyboardSystem; - if (!keyboardSystem) this.keyboardSystem = keyboardSystem = Crafty.s('Keyboard'); + if (!keyboardSystem) + this.keyboardSystem = keyboardSystem = Crafty.s("Keyboard"); return keyboardSystem.isKeyDown(this.key); } }; - // ToggleInputGroup function ToggleInputGroup(inputs) { this.inputs = inputs; @@ -43,7 +39,7 @@ function ToggleInputGroup(inputs) { // Handles a group of inputs that represent the same toggle state ToggleInputGroup.prototype = { timeDown: null, - isActive: function () { + isActive: function() { for (var i in this.inputs) { var input = this.inputs[i]; if (input.isDown()) { @@ -58,7 +54,7 @@ ToggleInputGroup.prototype = { }, destroy: function() { for (var i in this.inputs) { - if (typeof this.inputs[i].destroy === 'function') { + if (typeof this.inputs[i].destroy === "function") { this.inputs[i].destroy(); } } @@ -73,11 +69,11 @@ ToggleInputGroup.prototype = { * #Controls * @category Controls * @kind System - * + * * A built-in system for linking specific inputs to general types of input events. - * + * * @note The methods provided by this system are likely to change in future verisons of Crafty, as more input types are supported. - * + * * @trigger TriggerInputDown - When a trigger group is activated - {name} * @trigger TriggerInputUp - When a trigger group is released - {name, downFor} * @trigger DirectionalInput - When a directional input changes - {name, x, y} @@ -86,7 +82,7 @@ ToggleInputGroup.prototype = { * @trigger ControlDestroyed - When a control input is destroyed - {type, name} */ Crafty.s("Controls", { - init: function () { + init: function() { // internal object to store definitions this._dpads = {}; this._triggers = {}; @@ -99,9 +95,9 @@ Crafty.s("Controls", { }, events: { - "EnterFrame": "runEvents", - "KeyDown": "updateTriggers", - "KeyUp": "updateTriggers" + EnterFrame: "runEvents", + KeyDown: "updateTriggers", + KeyUp: "updateTriggers" }, // Runs through all triggers and updates their status @@ -112,7 +108,7 @@ Crafty.s("Controls", { } }, - runEvents: function () { + runEvents: function() { // Trigger DirectionalInput events for dpads for (var d in this._dpads) { var dpad = this._dpads[d]; @@ -128,7 +124,7 @@ Crafty.s("Controls", { } }, - getDpad: function (name) { + getDpad: function(name) { return this._dpads[name]; }, @@ -144,15 +140,15 @@ Crafty.s("Controls", { * @sign defineTriggerGroup(string name, obj definition) * @param name - a name for the trigger group * @param definition - an object which defines the inputs for the trigger - * - * A trigger group is a set of togglable inputs mapped to the same event. - * If any of the inputs are down, the trigger is considered down. If all are up, it is considered up. + * + * A trigger group is a set of togglable inputs mapped to the same event. + * If any of the inputs are down, the trigger is considered down. If all are up, it is considered up. * When the trigger state changes, a `TriggerInputUp` or `TriggerInputDown` event is fired. - * + * * The definition object lists the inputs that are mapped to the trigger: * - `keys`: An array of Crafty keycodes * - `mouseButtons`: An array of Crafty mouse button codes - * + * * @example * ~~~ * // Define a trigger group mapped to the left mouse button and the A and B keys. @@ -174,8 +170,10 @@ Crafty.s("Controls", { } else { inputs = []; if (definition.mouseButtons) { - for (var b in definition.mouseButtons){ - inputs.push(new MouseButtonToggleInput(definition.mouseButtons[b])); + for (var b in definition.mouseButtons) { + inputs.push( + new MouseButtonToggleInput(definition.mouseButtons[b]) + ); } } if (definition.keys) { @@ -211,7 +209,10 @@ Crafty.s("Controls", { if (this._triggers[name]) { this._triggers[name].input.destroy(); delete this._triggers[name]; - Crafty.trigger("ControlDestroyed", { type: "TriggerGroup", name: name }); + Crafty.trigger("ControlDestroyed", { + type: "TriggerGroup", + name: name + }); } }, @@ -219,18 +220,18 @@ Crafty.s("Controls", { * #.defineDpad * @comp Controls * @kind Method - * + * * @sign defineDpad(string name, obj definition[, obj options]) * @param name - a name for the dpad input * @param definition - an object which defines the inputs and directions for the dpad * @param options - a set of options for the dpad - * + * * A dpad is a type of directional control which maps a set of triggers to a set of directions. - * + * * The options object has two properties: * - `normalize` *(bool)*: If true, the directional input will be normalized to a unit vector. Defaults to false. * - `multipleDirectionBehavior` *(string)*: How to behave when multiple directions are active at the same time. Values are "first", "last", and "all". Defaults to "all". - * + * * @example * ~~~ * // Define a two-direction dpad, with two keys each bound to the right and left directions @@ -242,7 +243,7 @@ Crafty.s("Controls", { * @see Controllable * @see Multiway */ - defineDpad: function (name, definition, options) { + defineDpad: function(name, definition, options) { var directionDict = {}; for (var k in definition) { var direction = definition[k]; @@ -264,13 +265,13 @@ Crafty.s("Controls", { n: this.parseDirection(d) }; } - if (typeof options === 'undefined') { + if (typeof options === "undefined") { options = {}; } - if (typeof options.normalize === 'undefined') { + if (typeof options.normalize === "undefined") { options.normalize = false; } - if (typeof options.multipleDirectionBehavior === 'undefined') { + if (typeof options.multipleDirectionBehavior === "undefined") { options.multipleDirectionBehavior = "all"; } // Create the fully realized dpad object @@ -303,7 +304,7 @@ Crafty.s("Controls", { * * @see .defineDpad */ - destroyDpad: function (name) { + destroyDpad: function(name) { if (this._dpads[name]) { for (var d in this._dpads[name].parsedDefinition) { this._dpads[name].parsedDefinition[d].input.destroy(); @@ -315,7 +316,7 @@ Crafty.s("Controls", { // Takes an amount in degrees and converts it to an x/y object. // Clamps to avoid rounding issues with sin/cos - parseDirection: function (direction) { + parseDirection: function(direction) { return { x: Math.round(Math.cos(direction * (Math.PI / 180)) * 1000) / 1000, y: Math.round(Math.sin(direction * (Math.PI / 180)) * 1000) / 1000 @@ -323,8 +324,9 @@ Crafty.s("Controls", { }, // dpad definition is a map of directions to keys array and active flag - updateActiveDirection: function (dpad, normalize) { - var x = 0, y = 0; + updateActiveDirection: function(dpad, normalize) { + var x = 0, + y = 0; // Sum up all active directions for (var d in dpad.directions) { @@ -335,8 +337,8 @@ Crafty.s("Controls", { } // Mitigate rounding errors when close to zero movement - x = (-1e-10 < x && x < 1e-10) ? 0 : x; - y = (-1e-10 < y && y < 1e-10) ? 0 : y; + x = -1e-10 < x && x < 1e-10 ? 0 : x; + y = -1e-10 < y && y < 1e-10 ? 0 : y; // Normalize if (normalize) { @@ -351,7 +353,7 @@ Crafty.s("Controls", { dpad.y = y; }, - updateTriggerInput: function (trigger) { + updateTriggerInput: function(trigger) { if (!trigger.active) { if (trigger.input.isActive()) { trigger.downFor = Date.now() - trigger.input.timeDown; @@ -370,8 +372,8 @@ Crafty.s("Controls", { // Has to handle three cases concerning multiple active input groups: // - "all": all directions are active // - "last": one direction at a time, new directions replace old ones - // - "first": one direction at a time, new directions are ignored while old ones are still active - updateDpadInput: function (dpad, multiBehavior) { + // - "first": one direction at a time, new directions are ignored while old ones are still active + updateDpadInput: function(dpad, multiBehavior) { var d, dir; var winner; @@ -403,4 +405,4 @@ Crafty.s("Controls", { // If we picked a winner, set it active if (winner) winner.active = true; } -}); \ No newline at end of file +}); diff --git a/src/controls/controls.js b/src/controls/controls.js index bb832422..d5214b77 100644 --- a/src/controls/controls.js +++ b/src/controls/controls.js @@ -1,4 +1,4 @@ -var Crafty = require('../core/core.js'); +var Crafty = require("../core/core.js"); /**@ * #Draggable @@ -17,22 +17,22 @@ Crafty.c("Draggable", { required: "MouseDrag", events: { - "StartDrag": "_startDrag", - "Dragging": "_drag" + StartDrag: "_startDrag", + Dragging: "_drag" }, /**@ * #.enableDrag - * @comp Draggable + * @comp Draggable * @kind Method - * + * * @sign public this .enableDrag(void) * * Reenable dragging of entity. Use if `.disableDrag` has been called. * * @see .disableDrag */ - enableDrag: function () { + enableDrag: function() { this.uniqueBind("Dragging", this._drag); return this; }, @@ -41,14 +41,14 @@ Crafty.c("Draggable", { * #.disableDrag * @comp Draggable * @kind Method - * + * * @sign public this .disableDrag(void) * * Disables entity dragging. Reenable with `.enableDrag()`. * * @see .enableDrag */ - disableDrag: function () { + disableDrag: function() { this.unbind("Dragging", this._drag); return this; }, @@ -57,7 +57,7 @@ Crafty.c("Draggable", { * #.dragDirection * @comp Draggable * @kind Method - * + * * Method used for modifying the drag direction. * If direction is set, the entity being dragged will only move along the specified direction. * If direction is not set, the entity being dragged will move along any direction. @@ -84,13 +84,14 @@ Crafty.c("Draggable", { * this.dragDirection(60) //60 degree. * ~~~ */ - dragDirection: function (dir) { - if (typeof dir === 'undefined') { + dragDirection: function(dir) { + if (typeof dir === "undefined") { this._dir = null; - } else if (+dir === dir) { //dir is a number + } else if (+dir === dir) { + //dir is a number this._dir = { - x: Math.cos(dir / 180 * Math.PI), - y: Math.sin(dir / 180 * Math.PI) + x: Math.cos((dir / 180) * Math.PI), + y: Math.sin((dir / 180) * Math.PI) }; } else { if (dir.x === 0 && dir.y === 0) { @@ -106,7 +107,7 @@ Crafty.c("Draggable", { return this; }, - _startDrag: function (e) { + _startDrag: function(e) { this._origX = e.realX; this._origY = e.realY; this._oldX = this._x; @@ -117,7 +118,9 @@ Crafty.c("Draggable", { _drag: function(e) { if (this._dir) { if (this._dir.x !== 0 || this._dir.y !== 0) { - var len = (e.realX - this._origX) * this._dir.x + (e.realY - this._origY) * this._dir.y; + var len = + (e.realX - this._origX) * this._dir.x + + (e.realY - this._origY) * this._dir.y; this.x = this._oldX + len * this._dir.x; this.y = this._oldY + len * this._dir.y; } @@ -128,7 +131,6 @@ Crafty.c("Draggable", { } }); - /**@ * #Controllable * @category Controls @@ -140,30 +142,30 @@ Crafty.c("Draggable", { * */ Crafty.c("Controllable", { - init: function () { + init: function() { this._inputBindings = { - "DirectionalInput": {}, - "TriggerInputDown": {}, - "TriggerInputUp": {} + DirectionalInput: {}, + TriggerInputDown: {}, + TriggerInputUp: {} }; }, - + events: { // We don't want to use dot notation here for the property names /* jshint -W069 */ - "DirectionalInput": function (e) { + DirectionalInput: function(e) { if (this._inputBindings["DirectionalInput"][e.name]) { this._inputBindings["DirectionalInput"][e.name].call(this, e); } }, - "TriggerInputDown": function (e) { + TriggerInputDown: function(e) { if (this._inputBindings["TriggerInputDown"][e.name]) { this._inputBindings["TriggerInputDown"][e.name].call(this, e); } }, - "TriggerInputUp": function (e) { + TriggerInputUp: function(e) { if (this._inputBindings["TriggerInputUp"][e.name]) { this._inputBindings["TriggerInputUp"][e.name].call(this, e); } @@ -175,19 +177,19 @@ Crafty.c("Controllable", { * #.linkInput * @comp Controllable * @kind Method - * + * * @sign public this linkInput(string event, string name, function fn) * @param event - the name of the input event * @param name - the name of the input * @param fn - the function that will be called with the event object - * + * * Binds the function to the particular named event trigger. - * + * * Currently supports three types of input events. Each event will have a `name` property. * - `DirectionalInput`: The event will have `x` and `y` properties representing the directional input vector, often normalized to a unit vector. Triggered when the input changes. * - `TriggerInputDown`: Occurs when the input is triggered. * - `TriggerInputDown`: Occurs when the trigger is released. The event will have a `downFor` property, indicating how long it had been active. - * + * * @example * ~~~~ * // Create a trigger bound to the `b` key @@ -197,8 +199,8 @@ Crafty.c("Controllable", { * .attr({x:10, y:10, h:10, w:10}).color("blue") * .linkInput("TriggerInputDown", "BlushTrigger", function(){this.color('pink');}); * ~~~ - * - * @see .unlinkInput + * + * @see .unlinkInput */ linkInput: function(event, name, fn) { this._inputBindings[event][name] = fn; @@ -209,13 +211,13 @@ Crafty.c("Controllable", { * #.unlinkInput * @comp Controllable * @kind Method - * + * * @sign public this linkInput(string event, string name) * @param event - the name of the input event * @param name - the name of the input - * + * * Removes a binding setup by linkInput - * + * * @see .linkInput */ unlinkInput: function(event, name) { @@ -223,14 +225,13 @@ Crafty.c("Controllable", { return this; }, - disableControls: false, /**@ * #.enableControl * @comp Controllable * @kind Method - * + * * @sign public this .enableControl() * * Enable the component to listen to input events. @@ -240,7 +241,7 @@ Crafty.c("Controllable", { * this.enableControl(); * ~~~ */ - enableControl: function () { + enableControl: function() { this.disableControls = false; return this; }, @@ -249,7 +250,7 @@ Crafty.c("Controllable", { * #.disableControl * @comp Controllable * @kind Method - * + * * @sign public this .disableControl() * * Disable the component from responding to input events. @@ -259,13 +260,12 @@ Crafty.c("Controllable", { * this.disableControl(); * ~~~ */ - disableControl: function () { + disableControl: function() { this.disableControls = true; return this; } }); - /**@ * #Multiway * @category Controls @@ -274,9 +274,9 @@ Crafty.c("Controllable", { * Used to bind keys to directions and have the entity move accordingly. * * Multiway acts by listening to directional events, and then setting the velocity each frame based on the current direction and the current speed. - * + * * If a speed is not defined for a particular axis (x or y), then the velocity along that axis will not be set. - * + * * This behavior works in most cases, but can cause undesired behavior if you manipulate velocities by yourself while this component is in effect. * If you need to resolve collisions, it's advised to correct the position directly rather than to manipulate the velocity. * If you still need to reset the velocity once a collision happens, make sure to re-add the previous velocity once the collision is resolved. @@ -287,12 +287,12 @@ Crafty.c("Controllable", { */ Crafty.c("Multiway", { _speed: null, - - init: function () { + + init: function() { this.requires("Motion, Controllable"); this._dpadName = "MultiwayDpad" + this[0]; this._speed = { x: 150, y: 150 }; - this._direction = {x:0, y:0}; + this._direction = { x: 0, y: 0 }; }, remove: function() { @@ -302,20 +302,26 @@ Crafty.c("Multiway", { }, events: { - "UpdateFrame": function() { + UpdateFrame: function() { if (!this.disableControls) { - if (typeof this._speed.x !== 'undefined' && this._speed.x !== null){ + if ( + typeof this._speed.x !== "undefined" && + this._speed.x !== null + ) { this.vx = this._speed.x * this._direction.x; } - if (typeof this._speed.y !== 'undefined' && this._speed.y !== null) { + if ( + typeof this._speed.y !== "undefined" && + this._speed.y !== null + ) { this.vy = this._speed.y * this._direction.y; } } } }, - - // Rather than update the velocity directly in response to changing input, track the input direction separately - // That makes it easier to enable/disable control + + // Rather than update the velocity directly in response to changing input, track the input direction separately + // That makes it easier to enable/disable control _updateDirection: function(e) { this._direction.x = e.x; this._direction.y = e.y; @@ -325,7 +331,7 @@ Crafty.c("Multiway", { * #.multiway * @comp Multiway * @kind Method - * + * * @sign public this .multiway([Number speed,] Object keyBindings[, Object options]) * @param speed - A speed in pixels per second * @param keyBindings - What keys should make the entity go in which direction. Direction is specified in degrees @@ -336,9 +342,9 @@ Crafty.c("Multiway", { * Can be called while a key is pressed to change direction & speed on the fly. * * The options parameter controls the behavior of the component, and has the following defaults: - * + * * - `"normalize": false`. When set to true, the directional input always has a magnitude of 1 - * - `"multipleDirectionBehavior": "all"` How to resolve multiple active directions. + * - `"multipleDirectionBehavior": "all"` How to resolve multiple active directions. * Set to "first" or "last" to allow only one active direction at a time. * * @example @@ -349,8 +355,9 @@ Crafty.c("Multiway", { * ~~~ * * @see Crafty.keys - */ - multiway: function (speed, keys, options) { + */ + + multiway: function(speed, keys, options) { var inputSystem = Crafty.s("Controls"); if (keys) { @@ -359,7 +366,11 @@ Crafty.c("Multiway", { keys = speed; } inputSystem.defineDpad(this._dpadName, keys, options); - this.linkInput("DirectionalInput", this._dpadName, this._updateDirection); + this.linkInput( + "DirectionalInput", + this._dpadName, + this._updateDirection + ); return this; }, @@ -368,13 +379,13 @@ Crafty.c("Multiway", { * #.speed * @comp Multiway * @kind Method - * + * * @sign public this .speed(Object speed) * @param speed - New speed the entity has, for x and y axis. * * Change the speed that the entity moves with, in units of pixels per second. * Can be called while a key is pressed to change speed on the fly. - * + * * If the passed object has only an x or y property, only the velocity along that axis will be controlled. * * @example @@ -382,8 +393,8 @@ Crafty.c("Multiway", { * this.speed({ x: 150, y: 50 }); * ~~~ */ - speed: function (speed) { - if (typeof speed === 'object') { + speed: function(speed) { + if (typeof speed === "object") { this._speed.x = speed.x; this._speed.y = speed.y; } else { @@ -391,12 +402,9 @@ Crafty.c("Multiway", { this._speed.y = speed; } return this; - }, - - + } }); - /**@ * #Jumper * @category Controls @@ -439,7 +447,7 @@ Crafty.c("Jumper", { */ canJump: true, - init: function () { + init: function() { this.requires("Supportable, Motion, Controllable"); }, @@ -448,16 +456,16 @@ Crafty.c("Jumper", { Crafty.s("Controls").destroyTriggerGroup(this._jumpTriggerName); }, - _keydown_jumper: function (e) { + _keydown_jumper: function(e) { if (this.disableControls) return; - this.jump(); + this.jump(); }, /**@ * #.jump * @comp Jumper * @kind Method - * + * * @sign public this .jump() * * Directly trigger the entity to jump. @@ -477,18 +485,18 @@ Crafty.c("Jumper", { * #.jumper * @comp Jumper * @kind Method - * + * * @sign public this .jumper([Number jumpSpeed,] Array jumpKeys) * @param jumpSpeed - Vertical jump speed in pixels per second * @param jumpKeys - Keys to listen for and make entity jump in response - * + * * @sign public this .jumper([Number jumpSpeed,] Object jumpInputs) * @param jumpSpeed - Vertical jump speed in pixels per second * @param jumpInputs - An object with two properties, `keys` and `mouseButtons`. * * Constructor to initialize the power of jump and keys to listen to. * Component will listen for key events and make the entity jump appropriately. - * + * * If second argument is an object, the properties `keys` and `mouseButtons` will be used as triggers. * * @example @@ -499,7 +507,7 @@ Crafty.c("Jumper", { * * @see Crafty.keys */ - jumper: function (jumpSpeed, jumpKeys) { + jumper: function(jumpSpeed, jumpKeys) { if (jumpKeys) { this._jumpSpeed = jumpSpeed; } else { @@ -513,14 +521,21 @@ Crafty.c("Jumper", { var keyCode = Crafty.keys[key] || key; keys.push(keyCode); } - Crafty.s("Controls") - .defineTriggerGroup(this._jumpTriggerName, {keys:keys}); + Crafty.s("Controls").defineTriggerGroup(this._jumpTriggerName, { + keys: keys + }); } else { - Crafty.s("Controls") - .defineTriggerGroup(this._jumpTriggerName, jumpKeys); + Crafty.s("Controls").defineTriggerGroup( + this._jumpTriggerName, + jumpKeys + ); } - - this.linkInput("TriggerInputDown", this._jumpTriggerName, this._keydown_jumper); + + this.linkInput( + "TriggerInputDown", + this._jumpTriggerName, + this._keydown_jumper + ); return this; }, @@ -529,7 +544,7 @@ Crafty.c("Jumper", { * #.jumpSpeed * @comp Jumper * @kind Method - * + * * @sign public this .jumpSpeed(Number jumpSpeed) * @param jumpSpeed - new vertical jump speed * @@ -540,7 +555,7 @@ Crafty.c("Jumper", { * this.jumpSpeed(300); * ~~~ */ - jumpSpeed: function (jumpSpeed) { + jumpSpeed: function(jumpSpeed) { this._jumpSpeed = jumpSpeed; return this; } @@ -561,8 +576,7 @@ Crafty.c("Jumper", { * @see Motion */ Crafty.c("Fourway", { - - init: function () { + init: function() { this.requires("Multiway"); }, @@ -570,13 +584,13 @@ Crafty.c("Fourway", { * #.fourway * @comp Fourway * @kind Method - * + * * @sign public this .fourway([Number speed[, Object options]]) * @param speed - The speed of motion in pixels per second. * @param options - A dictionary of options passed through to the underlying Multiway component * * Initialize the component with the given speed and options. See the Multiway component for available options. - * + * * @example * ~~~ * Crafty.e("2D, Color, Fourway") @@ -585,22 +599,26 @@ Crafty.c("Fourway", { * .fourway(100, {normalize:true}); * ~~~ * Create a green square controlled by the arrow keys and WASD, with diagonal movement normalized to the given speed. - * + * * The speed is in units of pixels per second. */ - fourway: function (speed, options) { - this.multiway(speed || this._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 - }, options); + fourway: function(speed, options) { + this.multiway( + speed || this._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 + }, + options + ); return this; } @@ -621,8 +639,7 @@ Crafty.c("Fourway", { * @see Multiway, Jumper */ Crafty.c("Twoway", { - - init: function () { + init: function() { this.requires("Multiway, Jumper"); }, @@ -630,7 +647,7 @@ Crafty.c("Twoway", { * #.twoway * @comp Twoway * @kind Method - * + * * @sign public this .twoway([Number speed[, Number jumpSpeed]]) * @param speed - A speed in pixels per second * @param jumpSpeed - Vertical jump speed in pixels per second @@ -640,16 +657,19 @@ Crafty.c("Twoway", { * in the respective direction by the speed passed in the argument. * Pressing the jump key will cause the entity to jump with the supplied power. */ - twoway: function (speed, jumpSpeed) { + twoway: function(speed, jumpSpeed) { // Set multiway with horizontal speed only var hSpeed = speed || this._speed; - this.multiway({x: hSpeed}, { - RIGHT_ARROW: 0, - LEFT_ARROW: 180, - D: 0, - A: 180, - Q: 180 - }); + this.multiway( + { x: hSpeed }, + { + RIGHT_ARROW: 0, + LEFT_ARROW: 180, + D: 0, + A: 180, + Q: 180 + } + ); this.jumper(jumpSpeed || speed * 2 || this._jumpSpeed, [ Crafty.keys.UP_ARROW, diff --git a/src/controls/keyboard.js b/src/controls/keyboard.js index c2449e29..d1fc8eed 100644 --- a/src/controls/keyboard.js +++ b/src/controls/keyboard.js @@ -1,4 +1,4 @@ -var Crafty = require('../core/core.js'); +var Crafty = require("../core/core.js"); /**@ * #KeyboardState @@ -63,7 +63,7 @@ Crafty.__keyboardStateTemplate = { * @see .resetKeyDown * @see Crafty.keys */ - isKeyDown: function (key) { + isKeyDown: function(key) { if (typeof key === "string") { key = Crafty.keys[key]; } @@ -84,7 +84,7 @@ Crafty.__keyboardStateTemplate = { * @see .isKeyDown * @see Crafty.keys */ - resetKeyDown: function () { + resetKeyDown: function() { var evt = { key: -1, eventName: "KeyUp" }; // Tell all the keys they're no longer held down @@ -129,7 +129,7 @@ Crafty.__keyboardStateTemplate = { * * @see Crafty.keys */ - triggerKey: function (eventName, eventData) { + triggerKey: function(eventName, eventData) { // trigger event only if valid state var key = eventData.key; if (eventName === "KeyDown") { @@ -156,9 +156,17 @@ Crafty.c("KeyboardState", Crafty.__keyboardStateTemplate); // define a basic Keyboard system for headless mode // will be substituted with proper one in browser mode -Crafty.s("Keyboard", Crafty.extend.call({ - // this method will be called by KeyboardState iff triggerKey event was valid - triggerKeyEvent: function (eventName, e) { - Crafty.trigger(eventName, e); - } -}, Crafty.__keyboardStateTemplate), {}, false); +Crafty.s( + "Keyboard", + Crafty.extend.call( + { + // this method will be called by KeyboardState iff triggerKey event was valid + triggerKeyEvent: function(eventName, e) { + Crafty.trigger(eventName, e); + } + }, + Crafty.__keyboardStateTemplate + ), + {}, + false +); diff --git a/src/controls/keycodes.js b/src/controls/keycodes.js index ea9f5e52..dc4b36f8 100644 --- a/src/controls/keycodes.js +++ b/src/controls/keycodes.js @@ -1,12 +1,11 @@ -var Crafty = require('../core/core.js'); - +var Crafty = require("../core/core.js"); Crafty.extend({ /**@ * #Crafty.keys * @category Input * @kind Property - * + * * Object of key names and the corresponding Unicode key code. * * ~~~ @@ -104,105 +103,104 @@ Crafty.extend({ * ~~~ */ keys: { - 'BACKSPACE': 8, - 'TAB': 9, - 'ENTER': 13, - 'PAUSE': 19, - 'CAPS': 20, - 'ESC': 27, - 'SPACE': 32, - 'PAGE_UP': 33, - 'PAGE_DOWN': 34, - 'END': 35, - 'HOME': 36, - 'LEFT_ARROW': 37, - 'UP_ARROW': 38, - 'RIGHT_ARROW': 39, - 'DOWN_ARROW': 40, - 'INSERT': 45, - 'DELETE': 46, - '0': 48, - '1': 49, - '2': 50, - '3': 51, - '4': 52, - '5': 53, - '6': 54, - '7': 55, - '8': 56, - '9': 57, - 'A': 65, - 'B': 66, - 'C': 67, - 'D': 68, - 'E': 69, - 'F': 70, - 'G': 71, - 'H': 72, - 'I': 73, - 'J': 74, - 'K': 75, - 'L': 76, - 'M': 77, - 'N': 78, - 'O': 79, - 'P': 80, - 'Q': 81, - 'R': 82, - 'S': 83, - 'T': 84, - 'U': 85, - 'V': 86, - 'W': 87, - 'X': 88, - 'Y': 89, - 'Z': 90, - 'NUMPAD_0': 96, - 'NUMPAD_1': 97, - 'NUMPAD_2': 98, - 'NUMPAD_3': 99, - 'NUMPAD_4': 100, - 'NUMPAD_5': 101, - 'NUMPAD_6': 102, - 'NUMPAD_7': 103, - 'NUMPAD_8': 104, - 'NUMPAD_9': 105, - 'MULTIPLY': 106, - 'ADD': 107, - 'SUBSTRACT': 109, - 'DECIMAL': 110, - 'DIVIDE': 111, - 'F1': 112, - 'F2': 113, - 'F3': 114, - 'F4': 115, - 'F5': 116, - 'F6': 117, - 'F7': 118, - 'F8': 119, - 'F9': 120, - 'F10': 121, - 'F11': 122, - 'F12': 123, - 'SHIFT': 16, - 'CTRL': 17, - 'ALT': 18, - 'PLUS': 187, - 'COMMA': 188, - 'MINUS': 189, - 'PERIOD': 190, - 'PULT_UP': 29460, - 'PULT_DOWN': 29461, - 'PULT_LEFT': 4, - 'PULT_RIGHT': 5 - + BACKSPACE: 8, + TAB: 9, + ENTER: 13, + PAUSE: 19, + CAPS: 20, + ESC: 27, + SPACE: 32, + PAGE_UP: 33, + PAGE_DOWN: 34, + END: 35, + HOME: 36, + LEFT_ARROW: 37, + UP_ARROW: 38, + RIGHT_ARROW: 39, + DOWN_ARROW: 40, + INSERT: 45, + DELETE: 46, + "0": 48, + "1": 49, + "2": 50, + "3": 51, + "4": 52, + "5": 53, + "6": 54, + "7": 55, + "8": 56, + "9": 57, + A: 65, + B: 66, + C: 67, + D: 68, + E: 69, + F: 70, + G: 71, + H: 72, + I: 73, + J: 74, + K: 75, + L: 76, + M: 77, + N: 78, + O: 79, + P: 80, + Q: 81, + R: 82, + S: 83, + T: 84, + U: 85, + V: 86, + W: 87, + X: 88, + Y: 89, + Z: 90, + NUMPAD_0: 96, + NUMPAD_1: 97, + NUMPAD_2: 98, + NUMPAD_3: 99, + NUMPAD_4: 100, + NUMPAD_5: 101, + NUMPAD_6: 102, + NUMPAD_7: 103, + NUMPAD_8: 104, + NUMPAD_9: 105, + MULTIPLY: 106, + ADD: 107, + SUBSTRACT: 109, + DECIMAL: 110, + DIVIDE: 111, + F1: 112, + F2: 113, + F3: 114, + F4: 115, + F5: 116, + F6: 117, + F7: 118, + F8: 119, + F9: 120, + F10: 121, + F11: 122, + F12: 123, + SHIFT: 16, + CTRL: 17, + ALT: 18, + PLUS: 187, + COMMA: 188, + MINUS: 189, + PERIOD: 190, + PULT_UP: 29460, + PULT_DOWN: 29461, + PULT_LEFT: 4, + PULT_RIGHT: 5 }, /**@ * #Crafty.mouseButtons * @category Input * @kind Property - * + * * An object mapping mouseButton names to the corresponding button ID. * In all mouseEvents, we add the `e.mouseButton` property with a value normalized to match e.button of modern webkit browsers: * @@ -217,4 +215,4 @@ Crafty.extend({ MIDDLE: 1, RIGHT: 2 } -}); \ No newline at end of file +}); diff --git a/src/controls/mouse.js b/src/controls/mouse.js index 6d679370..696ebe91 100644 --- a/src/controls/mouse.js +++ b/src/controls/mouse.js @@ -1,4 +1,4 @@ -var Crafty = require('../core/core.js'); +var Crafty = require("../core/core.js"); /**@ * #MouseState @@ -56,7 +56,7 @@ Crafty.__mouseStateTemplate = { // use custom trigger method if specified this.triggerMouseEvent = this.triggerMouseEvent || this.trigger; this.lastMouseEvent = { - eventName: '', + eventName: "", mouseButton: -1, target: null, realX: 0, @@ -93,7 +93,7 @@ Crafty.__mouseStateTemplate = { * @see .resetButtonDown * @see Crafty.mouseButtons */ - isButtonDown: function (button) { + isButtonDown: function(button) { if (typeof button === "string") { button = Crafty.mouseButtons[button]; } @@ -114,7 +114,7 @@ Crafty.__mouseStateTemplate = { * @see .isButtonDown * @see Crafty.mouseButtons */ - resetButtonDown: function () { + resetButtonDown: function() { var lastEvent = this.lastMouseEvent; // Tell all buttons they're no longer held down @@ -160,7 +160,7 @@ Crafty.__mouseStateTemplate = { * * @see Crafty.mouseButtons */ - triggerMouse: function (eventName, eventData) { + triggerMouse: function(eventName, eventData) { // copy newest event to lastEvent var lastEvent = this.lastMouseEvent; lastEvent.eventName = eventName; @@ -198,9 +198,17 @@ Crafty.c("MouseState", Crafty.__mouseStateTemplate); // define a basic Mouse system for headless mode // will be substituted with proper one in browser mode -Crafty.s("Mouse", Crafty.extend.call({ - // this method will be called by MouseState iff triggerMouse event was valid - triggerMouseEvent: function (eventName, e) { - Crafty.trigger(eventName, e); - } -}, Crafty.__mouseStateTemplate), {}, false); +Crafty.s( + "Mouse", + Crafty.extend.call( + { + // this method will be called by MouseState iff triggerMouse event was valid + triggerMouseEvent: function(eventName, e) { + Crafty.trigger(eventName, e); + } + }, + Crafty.__mouseStateTemplate + ), + {}, + false +); diff --git a/src/controls/touch.js b/src/controls/touch.js index 9c30863e..8b9ebb6b 100644 --- a/src/controls/touch.js +++ b/src/controls/touch.js @@ -1,4 +1,4 @@ -var Crafty = require('../core/core.js'); +var Crafty = require("../core/core.js"); /**@ * #TouchState @@ -80,11 +80,13 @@ Crafty.__touchStateTemplate = { * * @see .touchPoints */ - resetTouchPoints: function () { + resetTouchPoints: function() { // Tell all touch points they're no longer held down - var touchPoints = this.touchPoints, touchPoint, + var touchPoints = this.touchPoints, + touchPoint, i = touchPoints.length; - while (i--) { // iterate backwards to avoid conflicts with removal of array elements + while (i--) { + // iterate backwards to avoid conflicts with removal of array elements touchPoint = touchPoints[i]; touchPoint.eventName = "TouchCancel"; this.triggerTouch("TouchCancel", touchPoint); @@ -121,7 +123,7 @@ Crafty.__touchStateTemplate = { * Crafty.log(wasTriggered); // prints false * ~~~ */ - triggerTouch: function (eventName, eventData) { + triggerTouch: function(eventName, eventData) { switch (eventName) { case "TouchStart": this._handleStart(eventData); @@ -139,7 +141,7 @@ Crafty.__touchStateTemplate = { return this; }, - _indexOfTouchPoint: function (identifier) { + _indexOfTouchPoint: function(identifier) { var touchPoints = this.touchPoints; for (var i = 0, l = touchPoints.length; i < l; i++) { if (touchPoints[i].identifier === identifier) { @@ -149,7 +151,7 @@ Crafty.__touchStateTemplate = { return -1; }, - _setTouchPoint: function (touchPointDest, touchPointSrc) { + _setTouchPoint: function(touchPointDest, touchPointSrc) { touchPointDest.eventName = touchPointSrc.eventName; touchPointDest.identifier = touchPointSrc.identifier; touchPointDest.target = touchPointSrc.target; @@ -159,10 +161,11 @@ Crafty.__touchStateTemplate = { touchPointDest.originalEvent = touchPointSrc.originalEvent; }, - _handleStart: function (touchPoint) { + _handleStart: function(touchPoint) { var oldIndex = this._indexOfTouchPoint(touchPoint.identifier), oldTouchPoint = oldIndex >= 0 ? this.touchPoints[oldIndex] : null; - if (!oldTouchPoint) { // ignore TouchStart due to inconsistent state caused by loosing focus + if (!oldTouchPoint) { + // ignore TouchStart due to inconsistent state caused by loosing focus // allocate touch point var newTouchPoint = this._touchPointsPool.pop() || {}; this._setTouchPoint(newTouchPoint, touchPoint); @@ -172,10 +175,11 @@ Crafty.__touchStateTemplate = { } }, - _handleMove: function (touchPoint) { + _handleMove: function(touchPoint) { var oldIndex = this._indexOfTouchPoint(touchPoint.identifier), oldTouchPoint = oldIndex >= 0 ? this.touchPoints[oldIndex] : null; - if (oldTouchPoint) { // ignore TouchMove due to inconsistent state caused by loosing focus + if (oldTouchPoint) { + // ignore TouchMove due to inconsistent state caused by loosing focus // update touch point this._setTouchPoint(oldTouchPoint, touchPoint); @@ -183,10 +187,11 @@ Crafty.__touchStateTemplate = { } }, - _handleEnd: function (touchPoint) { + _handleEnd: function(touchPoint) { var oldIndex = this._indexOfTouchPoint(touchPoint.identifier), oldTouchPoint = oldIndex >= 0 ? this.touchPoints[oldIndex] : null; - if (oldTouchPoint) { // ignore TouchEnd due to inconsistent state caused by loosing focus + if (oldTouchPoint) { + // ignore TouchEnd due to inconsistent state caused by loosing focus this._setTouchPoint(oldTouchPoint, touchPoint); this.triggerTouchEvent(oldTouchPoint.eventName, oldTouchPoint); @@ -203,9 +208,17 @@ Crafty.c("TouchState", Crafty.__touchStateTemplate); // define a basic Touch system for headless mode // will be substituted with proper one in browser mode -Crafty.s("Touch", Crafty.extend.call({ - // this method will be called by TouchState iff triggerTouch event was valid - triggerTouchEvent: function (eventName, e) { - Crafty.trigger(eventName, e); - } -}, Crafty.__touchStateTemplate), {}, false); +Crafty.s( + "Touch", + Crafty.extend.call( + { + // this method will be called by TouchState iff triggerTouch event was valid + triggerTouchEvent: function(eventName, e) { + Crafty.trigger(eventName, e); + } + }, + Crafty.__touchStateTemplate + ), + {}, + false +); diff --git a/src/core/animation.js b/src/core/animation.js index 2da0f979..8042941d 100644 --- a/src/core/animation.js +++ b/src/core/animation.js @@ -1,14 +1,13 @@ -var Crafty = require('../core/core.js'); - +var Crafty = require("../core/core.js"); /**@ * #Crafty.easing * @category Animation * @kind Class - * + * * * An object for tracking transitions. Typically used indirectly through "SpriteAnimation", "Tween", or viewport animations. - * + * * If a method allows you to specify the type of easing, you can do so by providing a custom function or a string corresponding to the name of a built-in method. * * Built-in easing functions are "linear", "smoothStep", "smootherStep", "easeInQuad", "easeOutQuad", and "easeInOutQuad". @@ -27,96 +26,105 @@ var Crafty = require('../core/core.js'); * @see Tween, SpriteAnimation */ var easing = function(duration, easingFn) { - this.timePerFrame = 1000 / Crafty.timer.FPS(); - this.duration = duration; //default duration given in ms - if (typeof easingFn === "function"){ - this.easing_function = easingFn; - } else if (typeof easingFn === "string" && this.standardEasingFunctions[easingFn]){ - this.easing_function = this.standardEasingFunctions[easingFn]; - } else { - this.easing_function = this.standardEasingFunctions.linear; - } - this.reset(); + this.timePerFrame = 1000 / Crafty.timer.FPS(); + this.duration = duration; //default duration given in ms + if (typeof easingFn === "function") { + this.easing_function = easingFn; + } else if ( + typeof easingFn === "string" && + this.standardEasingFunctions[easingFn] + ) { + this.easing_function = this.standardEasingFunctions[easingFn]; + } else { + this.easing_function = this.standardEasingFunctions.linear; + } + this.reset(); }; - easing.prototype = { - duration: 0, - clock:0, - steps: null, - complete: false, - paused: false, - - // init values - reset: function(){ - this.loops = 1; - this.clock = 0; - this.complete = false; - this.paused = false; - }, - - repeat: function(loopCount){ - this.loops = loopCount; - }, - - setProgress: function(progress, loopCount){ - this.clock = this.duration * progress; - if (typeof loopCount !== "undefined") - this.loops = loopCount; - - }, - - pause: function(){ - this.paused = true; - }, - - resume: function(){ - this.paused = false; - this.complete = false; - }, - - // Increment the clock by some amount dt - // Handles looping and sets a flag on completion - tick: function(dt){ - if (this.paused || this.complete) return; - this.clock += dt; - this.frames = Math.floor(this.clock/this.timePerFrame); - while (this.clock >= this.duration && this.complete === false){ - this.loops--; - if (this.loops > 0) - this.clock -= this.duration; - else - this.complete = true; - } - }, - - // same as value for now; with other time value functions would be more useful - time: function(){ - return ( Math.min(this.clock/this.duration, 1) ); - - }, - - // Value is where along the tweening curve we are - value: function(){ - return this.easing_function(this.time()); - }, - - // Easing functions, formulas taken from https://gist.github.com/gre/1650294 - // and https://en.wikipedia.org/wiki/Smoothstep - standardEasingFunctions: { - // no easing, no acceleration - linear: function (t) { return t; }, - // smooth step; starts and ends with v=0 - smoothStep: function(t){ return (3-2*t)*t*t; }, - // smootherstep; starts and ends with v, a=0 - smootherStep: function(t){ return (6*t*t-15*t+10)*t*t*t; }, - // quadratic curve; starts with v=0 - easeInQuad: function (t) { return t*t; }, - // quadratic curve; ends with v=0 - easeOutQuad: function (t) { return t*(2-t); }, - // quadratic curve; starts and ends with v=0 - easeInOutQuad: function (t) { return t<0.5 ? 2*t*t : (4-2*t)*t-1; } - } + duration: 0, + clock: 0, + steps: null, + complete: false, + paused: false, + + // init values + reset: function() { + this.loops = 1; + this.clock = 0; + this.complete = false; + this.paused = false; + }, + + repeat: function(loopCount) { + this.loops = loopCount; + }, + + setProgress: function(progress, loopCount) { + this.clock = this.duration * progress; + if (typeof loopCount !== "undefined") this.loops = loopCount; + }, + + pause: function() { + this.paused = true; + }, + + resume: function() { + this.paused = false; + this.complete = false; + }, + + // Increment the clock by some amount dt + // Handles looping and sets a flag on completion + tick: function(dt) { + if (this.paused || this.complete) return; + this.clock += dt; + this.frames = Math.floor(this.clock / this.timePerFrame); + while (this.clock >= this.duration && this.complete === false) { + this.loops--; + if (this.loops > 0) this.clock -= this.duration; + else this.complete = true; + } + }, + + // same as value for now; with other time value functions would be more useful + time: function() { + return Math.min(this.clock / this.duration, 1); + }, + + // Value is where along the tweening curve we are + value: function() { + return this.easing_function(this.time()); + }, + + // Easing functions, formulas taken from https://gist.github.com/gre/1650294 + // and https://en.wikipedia.org/wiki/Smoothstep + standardEasingFunctions: { + // no easing, no acceleration + linear: function(t) { + return t; + }, + // smooth step; starts and ends with v=0 + smoothStep: function(t) { + return (3 - 2 * t) * t * t; + }, + // smootherstep; starts and ends with v, a=0 + smootherStep: function(t) { + return (6 * t * t - 15 * t + 10) * t * t * t; + }, + // quadratic curve; starts with v=0 + easeInQuad: function(t) { + return t * t; + }, + // quadratic curve; ends with v=0 + easeOutQuad: function(t) { + return t * (2 - t); + }, + // quadratic curve; starts and ends with v=0 + easeInOutQuad: function(t) { + return t < 0.5 ? 2 * t * t : (4 - 2 * t) * t - 1; + } + } }; -module.exports = easing; \ No newline at end of file +module.exports = easing; diff --git a/src/core/core.js b/src/core/core.js index 4d62cf9f..2ea94e81 100644 --- a/src/core/core.js +++ b/src/core/core.js @@ -1,5 +1,4 @@ -var version = require('./version'); - +var version = require("./version"); /**@ * #Crafty @@ -52,27 +51,34 @@ var version = require('./version'); * @see Crafty Core#.each */ -var Crafty = function (selector) { +var Crafty = function(selector) { return new Crafty.fn.init(selector); }; - // Internal variables -var GUID, frame, components, entities, handlers, onloads, compEntities, -slice, rlist, rspace; - - -components = {}; // Map of components and their functions -slice = Array.prototype.slice; -rlist = /\s*,\s*/; -rspace = /\s+/; - -var initState = function () { - GUID = 1; // GUID for entity IDs - frame = 0; - - entities = {}; // Map of entities and their data - compEntities= {}; // Map from componentName to (entityId -> entity) - handlers = {}; // Global event handlers - onloads = []; // Temporary storage of onload handlers +// Internal variables +var GUID, + frame, + components, + entities, + handlers, + onloads, + compEntities, + slice, + rlist, + rspace; + +components = {}; // Map of components and their functions +slice = Array.prototype.slice; +rlist = /\s*,\s*/; +rspace = /\s+/; + +var initState = function() { + GUID = 1; // GUID for entity IDs + frame = 0; + + entities = {}; // Map of entities and their data + compEntities = {}; // Map from componentName to (entityId -> entity) + handlers = {}; // Global event handlers + onloads = []; // Temporary storage of onload handlers }; initState(); @@ -81,7 +87,7 @@ initState(); * #Crafty Core * @category Core * @kind CoreObject - * + * * @trigger NewEntityName - After setting new name for entity - String - entity name * @trigger NewComponent - when a new component is added to the entity - String - Component * @trigger RemoveComponent - when a component is removed from the entity - String - Component @@ -90,8 +96,7 @@ initState(); * A set of methods added to every single entity. */ Crafty.fn = Crafty.prototype = { - - init: function (selector) { + init: function(selector) { //select entities by component if (typeof selector === "string") { var elem = 0, //index elements @@ -100,10 +105,14 @@ Crafty.fn = Crafty.prototype = { or = false, del, comps, - filteredCompEnts, compEnts, compEnts2, compEnt, - i, l; - - if (selector === '*') { + filteredCompEnts, + compEnts, + compEnts2, + compEnt, + i, + l; + + if (selector === "*") { i = 0; for (e in entities) { // entities is something like {2:entity2, 3:entity3, 11:entity11, ...} @@ -121,11 +130,11 @@ Crafty.fn = Crafty.prototype = { } //multiple components OR - if (selector.indexOf(',') !== -1) { + if (selector.indexOf(",") !== -1) { or = true; del = rlist; //deal with multiple components AND - } else if (selector.indexOf(' ') !== -1) { + } else if (selector.indexOf(" ") !== -1) { and = true; del = rspace; } @@ -141,7 +150,8 @@ Crafty.fn = Crafty.prototype = { } } - for (compEnt in filteredCompEnts) { // add set elements to result + for (compEnt in filteredCompEnts) { + // add set elements to result this[elem++] = filteredCompEnts[compEnt]; } } else if (and) { @@ -155,7 +165,8 @@ Crafty.fn = Crafty.prototype = { filteredCompEnts[compEnt] = +compEnt; //convert to int } - for (i = 2, l = comps.length; i < l; i++) { // strip initial object keys not in following sets + for (i = 2, l = comps.length; i < l; i++) { + // strip initial object keys not in following sets compEnts = compEntities[comps[i]]; for (compEnt in filteredCompEnts) { if (compEnts[compEnt] === undefined) @@ -163,11 +174,13 @@ Crafty.fn = Crafty.prototype = { } } - for (compEnt in filteredCompEnts) { // add valid elements to result + for (compEnt in filteredCompEnts) { + // add valid elements to result i = filteredCompEnts[compEnt]; if (i >= 0) this[elem++] = i; } - } else { // single component selector + } else { + // single component selector compEnts = compEntities[selector]; for (compEnt in compEnts) { this[elem++] = +compEnt; //convert to int @@ -180,10 +193,11 @@ Crafty.fn = Crafty.prototype = { if (elem === 1) { return entities[this[elem - 1]]; } + } else { + //Select a specific entity - } else { //Select a specific entity - - if (!selector) { //nothin passed creates God entity + if (!selector) { + //nothin passed creates God entity selector = 0; if (!(selector in entities)) entities[selector] = this; } @@ -214,7 +228,7 @@ Crafty.fn = Crafty.prototype = { * #.setName * @comp Crafty Core * @kind Method - * + * * @sign public this .setName(String name) * @param name - A human readable name for debugging purposes. * @@ -227,7 +241,7 @@ Crafty.fn = Crafty.prototype = { * * @see Crafty Core#.getName */ - setName: function (name) { + setName: function(name) { var entityName = String(name); this._entityName = entityName; this.trigger("NewEntityName", entityName); @@ -238,7 +252,7 @@ Crafty.fn = Crafty.prototype = { * #.getName * @comp Crafty Core * @kind Method - * + * * @sign public this .getName(String name) * @returns A human readable name for debugging purposes. * @@ -252,7 +266,7 @@ Crafty.fn = Crafty.prototype = { * * @see Crafty Core#.setName */ - getName: function (name) { + getName: function(name) { return this._entityName; }, @@ -260,7 +274,7 @@ Crafty.fn = Crafty.prototype = { * #.addComponent * @comp Crafty Core * @kind Method - * + * * @sign public this .addComponent(String componentList) * @param componentList - A string of components to add separated by a comma `,` * @sign public this .addComponent(String Component1[, .., String ComponentN]) @@ -286,12 +300,14 @@ Crafty.fn = Crafty.prototype = { * this.addComponent("2D", "Canvas"); * ~~~ */ - addComponent: function (id) { - var comps, compName, - comp, c = 0; + addComponent: function(id) { + var comps, + compName, + comp, + c = 0; //add multiple arguments - if (arguments.length === 1 && id.indexOf(',') !== -1) { + if (arguments.length === 1 && id.indexOf(",") !== -1) { comps = id.split(rlist); } else { comps = arguments; @@ -307,27 +323,36 @@ Crafty.fn = Crafty.prototype = { } this.__c[compName] = true; // update map from component to (entityId -> entity) - (compEntities[compName] = compEntities[compName] || {})[this[0]] = this; + (compEntities[compName] = compEntities[compName] || {})[ + this[0] + ] = this; comp = components[compName]; // Copy all methods of the component this.extend(comp); // Add any required components if (comp && "required" in comp) { - this.requires( comp.required ); + this.requires(comp.required); } // Define properties if (comp && "properties" in comp) { var props = comp.properties; for (var propertyName in props) { - Object.defineProperty(this, propertyName, props[propertyName]); + Object.defineProperty( + this, + propertyName, + props[propertyName] + ); } } // Bind events - if (comp && "events" in comp){ + if (comp && "events" in comp) { var auto = comp.events; - for (var eventName in auto){ - var fn = typeof auto[eventName] === "function" ? auto[eventName] : comp[auto[eventName]]; + for (var eventName in auto) { + var fn = + typeof auto[eventName] === "function" + ? auto[eventName] + : comp[auto[eventName]]; this.bind(eventName, fn); } } @@ -345,12 +370,12 @@ Crafty.fn = Crafty.prototype = { * #.toggleComponent * @comp Crafty Core * @kind Method - * + * * @sign public this .toggleComponent(String ComponentList) * @param ComponentList - A string of components to add or remove separated by a comma `,` * @sign public this .toggleComponent(String Component1[, .., String componentN]) * @param Component# - Component ID to add or remove. - * + * * Add or Remove Components from an entity. * * @example @@ -367,9 +392,10 @@ Crafty.fn = Crafty.prototype = { * e.toggleComponent("Test"); //Remove Test * ~~~ */ - toggleComponent: function (toggle) { + toggleComponent: function(toggle) { var i = 0, - l, comps; + l, + comps; if (arguments.length > 1) { l = arguments.length; @@ -381,7 +407,7 @@ Crafty.fn = Crafty.prototype = { } } //split components if contains comma - } else if (toggle.indexOf(',') !== -1) { + } else if (toggle.indexOf(",") !== -1) { comps = toggle.split(rlist); l = comps.length; for (; i < l; i++) { @@ -408,10 +434,10 @@ Crafty.fn = Crafty.prototype = { * #.requires * @comp Crafty Core * @kind Method - * + * * @sign public this .requires(String componentList) * @param componentList - List of components that must be added - * + * * @sign public this .addComponent(String component1, String component2[, .. , ComponentN]) * @param Component# - A component to add * @@ -426,7 +452,7 @@ Crafty.fn = Crafty.prototype = { * * @see .addComponent */ - requires: function () { + requires: function() { return this.addComponent.apply(this, arguments); }, @@ -434,7 +460,7 @@ Crafty.fn = Crafty.prototype = { * #.removeComponent * @comp Crafty Core * @kind Method - * + * * @sign public this .removeComponent(String Component[, soft]) * @param component - Component to remove * @param soft - Whether to soft remove it (defaults to `true`) @@ -450,13 +476,16 @@ Crafty.fn = Crafty.prototype = { * e.removeComponent("Test", false); //Hard remove Test component * ~~~ */ - removeComponent: function (id, soft) { + removeComponent: function(id, soft) { var comp = components[id]; this.trigger("RemoveComponent", id); - if (comp && "events" in comp){ + if (comp && "events" in comp) { var auto = comp.events; - for (var eventName in auto){ - var fn = typeof auto[eventName] === "function" ? auto[eventName] : comp[auto[eventName]]; + for (var eventName in auto) { + var fn = + typeof auto[eventName] === "function" + ? auto[eventName] + : comp[auto[eventName]]; this.unbind(eventName, fn); } } @@ -481,7 +510,7 @@ Crafty.fn = Crafty.prototype = { * #.getId * @comp Crafty Core * @kind Method - * + * * @sign public Number .getId(void) * @returns the ID of this entity. * @@ -495,7 +524,7 @@ Crafty.fn = Crafty.prototype = { * ent.getId(); //also ID * ~~~ */ - getId: function () { + getId: function() { return this[0]; }, @@ -503,7 +532,7 @@ Crafty.fn = Crafty.prototype = { * #.has * @comp Crafty Core * @kind Method - * + * * @sign public Boolean .has(String component) * @param component - The name of the component to check * @returns `true` or `false` depending on if the @@ -513,7 +542,7 @@ Crafty.fn = Crafty.prototype = { * which will be `true` if the entity has the component or * will not exist (or be `false`). */ - has: function (id) { + has: function(id) { return !!this.__c[id]; }, @@ -521,7 +550,7 @@ Crafty.fn = Crafty.prototype = { * #.attr * @comp Crafty Core * @kind Method - * + * * @trigger Change - when properties change - {key: value} * * @sign public this .attr(String property, Any value[, Boolean silent[, Boolean recursive]]) @@ -548,7 +577,7 @@ Crafty.fn = Crafty.prototype = { * @returns Value - the value of the property * * Use this method to get any property of the entity. You can also retrieve the property using `this.property`. - * + * * * @example * ~~~ @@ -567,8 +596,8 @@ Crafty.fn = Crafty.prototype = { * this.attr('parent.child'); // "newvalue" * ~~~ */ - attr: function (key, value, silent, recursive) { - if (arguments.length === 1 && typeof arguments[0] === 'string') { + attr: function(key, value, silent, recursive) { + if (arguments.length === 1 && typeof arguments[0] === "string") { return this._attr_get(key); } else { return this._attr_set(key, value, silent, recursive); @@ -590,11 +619,11 @@ Crafty.fn = Crafty.prototype = { if (typeof context === "undefined" || context === null) { context = this; } - if (key.indexOf('.') > -1) { - keys = key.split('.'); + if (key.indexOf(".") > -1) { + keys = key.split("."); first = keys.shift(); - subkey = keys.join('.'); - return this._attr_get(keys.join('.'), context[first]); + subkey = keys.join("."); + return this._attr_get(keys.join("."), context[first]); } else { return context[key]; } @@ -623,10 +652,10 @@ Crafty.fn = Crafty.prototype = { */ _attr_set: function() { var data, silent, recursive; - if (typeof arguments[0] === 'string') { + if (typeof arguments[0] === "string") { data = this._set_create_object(arguments[0], arguments[1]); silent = !!arguments[2]; - recursive = arguments[3] || arguments[0].indexOf('.') > -1; + recursive = arguments[3] || arguments[0].indexOf(".") > -1; } else { data = arguments[0]; silent = !!arguments[1]; @@ -634,7 +663,7 @@ Crafty.fn = Crafty.prototype = { } if (!silent) { - this.trigger('Change', data); + this.trigger("Change", data); } if (recursive) { @@ -651,11 +680,14 @@ Crafty.fn = Crafty.prototype = { * current attributes. */ _set_create_object: function(key, value) { - var data = {}, keys, first, subkey; - if (key.indexOf('.') > -1) { - keys = key.split('.'); + var data = {}, + keys, + first, + subkey; + if (key.indexOf(".") > -1) { + keys = key.split("."); first = keys.shift(); - subkey = keys.join('.'); + subkey = keys.join("."); data[first] = this._set_create_object(subkey, value); } else { data[key] = value; @@ -670,7 +702,10 @@ Crafty.fn = Crafty.prototype = { var key; for (key in new_data) { if (new_data[key].constructor === Object) { - original_data[key] = this._recursive_extend(new_data[key], original_data[key]); + original_data[key] = this._recursive_extend( + new_data[key], + original_data[key] + ); } else { original_data[key] = new_data[key]; } @@ -682,13 +717,13 @@ Crafty.fn = Crafty.prototype = { * #.toArray * @comp Crafty Core * @kind Method - * + * * @sign public this .toArray(void) * * This method will simply return the found entities as an array of ids. To get an array of the actual entities, use `get()`. * @see .get */ - toArray: function () { + toArray: function() { return slice.call(this, 0); }, @@ -713,10 +748,10 @@ Crafty.fn = Crafty.prototype = { * }, 100); * ~~~ */ - timeout: function (callback, duration) { - this.each(function () { + timeout: function(callback, duration) { + this.each(function() { var self = this; - setTimeout(function () { + setTimeout(function() { callback.call(self); }, duration); }); @@ -727,7 +762,7 @@ Crafty.fn = Crafty.prototype = { * #.bind * @comp Crafty Core * @kind Method - * + * * @sign public this .bind(String eventName, Function callback) * @param eventName - Name of the event to bind to * @param callback - Method to execute when the event is triggered @@ -760,7 +795,7 @@ Crafty.fn = Crafty.prototype = { * * @see .trigger, .unbind */ - bind: function (event, callback) { + bind: function(event, callback) { // To learn how the event system functions, see the comments for Crafty._callbackMethods //optimization for 1 entity if (this.length === 1) { @@ -780,7 +815,7 @@ Crafty.fn = Crafty.prototype = { * #.uniqueBind * @comp Crafty Core * @kind Method - * + * * @sign public Number .uniqueBind(String eventName, Function callback) * @param eventName - Name of the event to bind to * @param callback - Method to execute upon event triggered @@ -790,17 +825,16 @@ Crafty.fn = Crafty.prototype = { * * @see .bind */ - uniqueBind: function (event, callback) { + uniqueBind: function(event, callback) { this.unbind(event, callback); this.bind(event, callback); - }, /**@ * #.one * @comp Crafty Core * @kind Method - * + * * @sign public Number one(String eventName, Function callback) * @param eventName - Name of the event to bind to * @param callback - Method to execute upon event triggered @@ -810,21 +844,20 @@ Crafty.fn = Crafty.prototype = { * * @see .bind */ - one: function (event, callback) { + one: function(event, callback) { var self = this; - var oneHandler = function (data) { + var oneHandler = function(data) { callback.call(self, data); self.unbind(event, oneHandler); }; return self.bind(event, oneHandler); - }, /**@ * #.unbind * @comp Crafty Core * @kind Method - * + * * @sign public this .unbind(String eventName[, Function callback]) * @param eventName - Name of the event to unbind * @param callback - Function to unbind @@ -836,7 +869,7 @@ Crafty.fn = Crafty.prototype = { * unbind only that callback. * @see .bind, .trigger */ - unbind: function (event, callback) { + unbind: function(event, callback) { // To learn how the event system functions, see the comments for Crafty._callbackMethods var i, e; for (i = 0; i < this.length; i++) { @@ -852,7 +885,7 @@ Crafty.fn = Crafty.prototype = { * #.trigger * @comp Crafty Core * @kind Method - * + * * @sign public this .trigger(String eventName[, Object data]) * @param eventName - Event to trigger * @param data - Arbitrary data that will be passed into every callback as an argument @@ -867,12 +900,12 @@ Crafty.fn = Crafty.prototype = { * * Unlike DOM events, Crafty events are executed synchronously. */ - trigger: function (event, data) { + trigger: function(event, data) { // To learn how the event system functions, see the comments for Crafty._callbackMethods if (this.length === 1) { //find the handlers assigned to the entity this._runCallbacks(event, data); - } else { + } else { for (var i = 0; i < this.length; i++) { var e = entities[this[i]]; if (e) { @@ -887,7 +920,7 @@ Crafty.fn = Crafty.prototype = { * #.each * @comp Crafty Core * @kind Method - * + * * @sign public this .each(Function method) * @param method - Method to call on each iteration * @@ -907,7 +940,7 @@ Crafty.fn = Crafty.prototype = { * }); * ~~~ */ - each: function (func) { + each: function(func) { var i = 0, l = this.length; for (; i < l; i++) { @@ -922,7 +955,7 @@ Crafty.fn = Crafty.prototype = { * #.get * @comp Crafty Core * @kind Method - * + * * @sign public Array .get() * @returns An array of entities corresponding to the active selector * @@ -950,18 +983,16 @@ Crafty.fn = Crafty.prototype = { get: function(index) { var l = this.length; if (typeof index !== "undefined") { - if (index >= l || index+l < 0) - return undefined; - if (index>=0) - return entities[this[index]]; - else - return entities[this[index+l]]; + if (index >= l || index + l < 0) return undefined; + if (index >= 0) return entities[this[index]]; + else return entities[this[index + l]]; } else { - var i=0, result = []; + var i = 0, + result = []; for (; i < l; i++) { //skip if not exists if (!entities[this[i]]) continue; - result.push( entities[this[i]] ); + result.push(entities[this[i]]); } return result; } @@ -971,14 +1002,14 @@ Crafty.fn = Crafty.prototype = { * #.clone * @comp Crafty Core * @kind Method - * + * * @sign public Entity .clone(void) * @returns Cloned entity of the current entity * * Method will create another entity with the exact same * properties, components and methods as the current entity. */ - clone: function () { + clone: function() { var comps = this.__c, comp, prop, @@ -988,7 +1019,13 @@ Crafty.fn = Crafty.prototype = { clone.addComponent(comp); } for (prop in this) { - if (prop !== "0" && prop !== "_global" && prop !== "_changed" && typeof this[prop] !== "function" && typeof this[prop] !== "object") { + if ( + prop !== "0" && + prop !== "_global" && + prop !== "_changed" && + typeof this[prop] !== "function" && + typeof this[prop] !== "object" + ) { clone[prop] = this[prop]; } } @@ -996,12 +1033,11 @@ Crafty.fn = Crafty.prototype = { return clone; }, - /**@ * #.setter * @comp Crafty Core * @kind Method - * + * * @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 @@ -1012,21 +1048,21 @@ Crafty.fn = Crafty.prototype = { * This feature is deprecated; use .defineField() instead. * @see .defineField */ - setter: function (prop, callback) { - return this.defineField(prop, function(){}, callback); + setter: function(prop, callback) { + return this.defineField(prop, function() {}, callback); }, /**@ * #.defineField * @comp Crafty Core * @kind Method - * + * * @sign public this .defineField(String property, Function getCallback, Function setCallback) * @param property - Property name to assign getter & setter to * @param getCallback - Method to execute if the property is accessed * @param setCallback - Method to execute if the property is mutated * - * Assigns getters and setters to the property. + * Assigns getters and setters to the property. * A getter will watch a property waiting for access and will then invoke the * given getCallback when attempting to retrieve. * A setter will watch a property waiting for mutation and will then invoke the @@ -1035,9 +1071,9 @@ Crafty.fn = Crafty.prototype = { * @example * ~~~ * var ent = Crafty.e("2D"); - * ent.defineField("customData", function() { - * return this._customData; - * }, function(newValue) { + * ent.defineField("customData", function() { + * return this._customData; + * }, function(newValue) { * this._customData = newValue; * }); * @@ -1045,7 +1081,7 @@ Crafty.fn = Crafty.prototype = { * Crafty.log(ent.customData) // prints 2 * ~~~ */ - defineField: function (prop, getCallback, setCallback) { + defineField: function(prop, getCallback, setCallback) { Crafty.defineField(this, prop, getCallback, setCallback); return this; }, @@ -1054,19 +1090,18 @@ Crafty.fn = Crafty.prototype = { * #.destroy * @comp Crafty Core * @kind Method - * + * * @sign public this .destroy(void) * Will remove all event listeners and delete all properties as well as removing from the stage */ - destroy: function () { + destroy: function() { //remove all event handlers, delete from entities - this.each(function () { + 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); + if (comp && "remove" in comp) comp.remove.call(this, true); // update map from component to (entityId -> entity) delete compEntities[compName][this[0]]; @@ -1080,31 +1115,31 @@ Crafty.fn = Crafty.prototype = { * #.freeze * @comp Crafty Core * @kind Method - * + * * @sign public this .freeze() - * + * * @triggers Freeze - Directly before the entity is frozen - * - * Freezes the entity. A frozen entity will not receive events or be displayed by graphics systems. - * It is also removed from the spatial map, which means it will not be found by collisions, + * + * Freezes the entity. A frozen entity will not receive events or be displayed by graphics systems. + * It is also removed from the spatial map, which means it will not be found by collisions, * raycasting, or similar functions. - * + * * This method may be called upon a collection of entities. - * + * * @note Because the entity no longer listens to events, modifying its properties can result in an inconsistent state. - * + * * If custom components need to handle frozen entities, they can listen to the "Freeze" event, which will be triggered before the event system is disabled. - * + * * @example - * + * * ``` * // Freeze all entities with the Dead component * Crafty("Dead").freeze(); * ``` - * + * * @see .unfreeze */ - freeze: function () { + freeze: function() { if (this.length === 1 && !this.__frozen) { this.trigger("Freeze", this); this._freezeCallbacks(); @@ -1127,25 +1162,25 @@ Crafty.fn = Crafty.prototype = { * #.unfreeze * @comp Crafty Core * @kind Method - * + * * @sign public this .unfreeze() - * + * * @triggers Unfreeze - While the entity is being unfrozen - * - * Unfreezes the entity, allowing it to receive events, inserting it back into the spatial map, + * + * Unfreezes the entity, allowing it to receive events, inserting it back into the spatial map, * and restoring it to its previous visibility. - * + * * This method may be called upon a collection of entities. - * + * * If a custom component needs to know when an entity is unfrozen, they can listen to the "Unfreeze"" event. - * + * * @example * ``` * // Bring the dead back to life! * Crafty("Dead").unfreeze().addComponent("Undead"); * ``` */ - unfreeze: function () { + unfreeze: function() { if (this.length === 1 && this.__frozen) { this.__frozen = false; this._unfreezeCallbacks(); @@ -1167,30 +1202,29 @@ Crafty.fn = Crafty.prototype = { //give the init instances the Crafty prototype Crafty.fn.init.prototype = Crafty.fn; - /**@ * #Crafty.extend * @category Core * @kind Method - * + * * @sign public this Crafty.extend(Object obj) * @param obj - An object whose fields will be copied onto Crafty. This is a shallow copy. * * Used to extend the Crafty namespace by passing in an object of properties and methods to add. * * @example - * ~~~ * + * ~~~ * * Crafty.extend({ * isArray: function(arg){ * return Object.prototype.toString.call(arg) === '[object Array]' * } * }); - * + * * Crafty.isArray([4, 5, 6]); // returns true * Crafty.isArray('hi'); // returns false * ~~~ */ -Crafty.extend = Crafty.fn.extend = function (obj) { +Crafty.extend = Crafty.fn.extend = function(obj) { var target = this, key; @@ -1205,15 +1239,12 @@ Crafty.extend = Crafty.fn.extend = function (obj) { return target; }; - - - // How Crafty handles events and callbacks // ----------------------------------------- -// Callbacks are stored in the global object `handlers`, which has properties for each event. +// Callbacks are stored in the global object `handlers`, which has properties for each event. // These properties point to an object which has a property for each entity listening to the event. // These in turn are arrays containing the callbacks to be triggered. -// +// // Here is an example of what "handlers" can look like: // handlers === // { Move: {5:[fnA], 6:[fnB, fnC], global:[fnD]}, @@ -1231,24 +1262,23 @@ Crafty.extend = Crafty.fn.extend = function (obj) { // // handlers[event][objID] === (Array of callback functions) // -// In addition to the global object, each object participating in the event system has a `_callbacks` property +// In addition to the global object, each object participating in the event system has a `_callbacks` property // which lists the events that object is listening to. It allows access to the object's callbacks like this: // obj._callbacks[event] === (Array of callback functions) // -// Objects, which can listen to events (or collections of such objects) have varying logic +// Objects, which can listen to events (or collections of such objects) have varying logic // on how the events are bound/triggered/unbound. Since the underlying operations on the callback array are the same, -// the single-object operations are implemented in the following object. +// the single-object operations are implemented in the following object. // Calling `Crafty._addCallbackMethods(obj)` on an object will extend that object with these methods. - - Crafty._callbackMethods = { // Add a function to the list of callbacks for an event _bindCallback: function(event, fn) { // Get handle to event, creating it if necessary var callbacks = this._callbacks[event]; if (!callbacks) { - callbacks = this._callbacks[event] = ( handlers[event] || ( handlers[event] = {} ) )[this[0]] = []; + callbacks = this._callbacks[event] = (handlers[event] || + (handlers[event] = {}))[this[0]] = []; callbacks.context = this; callbacks.depth = 0; } @@ -1264,7 +1294,8 @@ Crafty._callbackMethods = { var callbacks = this._callbacks[event]; // Callback loop; deletes dead callbacks, but only when it is safe to do so - var i, l = callbacks.length; + var i, + l = callbacks.length; // callbacks.depth tracks whether this function was invoked in the middle of a previous iteration through the same callback array callbacks.depth++; for (i = 0; i < l; i++) { @@ -1329,16 +1360,15 @@ Crafty._callbackMethods = { this.__callbacksFrozen = true; }, - _unfreezeCallbacks: function() { + _unfreezeCallbacks: function() { if (!this._callbacks) return; this.__callbacksFrozen = false; for (var event in this._callbacks) { if (this._callbacks[event]) { // Add the callbacks back to the global list of handlers - handlers[event][this[0]] = this._callbacks[event]; + handlers[event][this[0]] = this._callbacks[event]; } } - } }; @@ -1358,7 +1388,7 @@ Crafty.extend({ * #Crafty.init * @category Core * @kind Method - * + * * @trigger Load - Just after the viewport is initialised. Before the UpdateFrame 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]) @@ -1377,12 +1407,10 @@ Crafty.extend({ * 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) { - + init: function(w, h, stage_elem) { // If necessary, attach any event handlers registered before Crafty started if (!this._preBindDone) { - for(var i = 0; i < this._bindOnInit.length; i++) { - + for (var i = 0; i < this._bindOnInit.length; i++) { var preBind = this._bindOnInit[i]; Crafty.bind(preBind.event, preBind.handler); } @@ -1413,7 +1441,7 @@ Crafty.extend({ * #Crafty.getVersion * @category Core * @kind Method - * + * * @sign public String Crafty.getVersion() * @returns Current version of Crafty as a string * @@ -1424,7 +1452,7 @@ Crafty.extend({ * Crafty.getVersion(); //'0.5.2' * ~~~ */ - getVersion: function () { + getVersion: function() { return version; }, @@ -1432,7 +1460,7 @@ Crafty.extend({ * #Crafty.stop * @category Core * @kind Method - * + * * @trigger CraftyStop - when the game is stopped - {bool clearState} * @sign public this Crafty.stop([bool clearState]) * @param clearState - if true the stage and all game state is cleared. @@ -1441,8 +1469,9 @@ Crafty.extend({ * * To restart, use `Crafty.init()`. * @see Crafty.init - */ - stop: function (clearState) { + */ + + stop: function(clearState) { Crafty.trigger("CraftyStop", clearState); this.timer.stop(); @@ -1457,9 +1486,12 @@ Crafty.extend({ // Remove the stage element, and re-add a div with the same id if (Crafty.stage && Crafty.stage.elem.parentNode) { - var newCrStage = document.createElement('div'); + var newCrStage = document.createElement("div"); newCrStage.id = Crafty.stage.elem.id; - Crafty.stage.elem.parentNode.replaceChild(newCrStage, Crafty.stage.elem); + Crafty.stage.elem.parentNode.replaceChild( + newCrStage, + Crafty.stage.elem + ); } // reset callbacks, and indicate that prebound functions need to be bound on init again @@ -1476,7 +1508,7 @@ Crafty.extend({ * #Crafty.pause * @category Core * @kind Method - * + * * @trigger Pause - when the game is paused * @trigger Unpause - when the game is unpaused * @sign public this Crafty.pause(void) @@ -1494,17 +1526,17 @@ Crafty.extend({ * }); * ~~~ */ - pause: function (toggle) { + pause: function(toggle) { if (arguments.length === 1 ? toggle : !this._paused) { - this.trigger('Pause'); + this.trigger("Pause"); this._paused = true; - setTimeout(function () { + setTimeout(function() { Crafty.timer.stop(); }, 0); } else { - this.trigger('Unpause'); + this.trigger("Unpause"); this._paused = false; - setTimeout(function () { + setTimeout(function() { Crafty.timer.init(); }, 0); } @@ -1515,7 +1547,7 @@ Crafty.extend({ * #Crafty.isPaused * @category Core * @kind Method - * + * * @sign public Boolean Crafty.isPaused() * @returns Whether the game is currently paused. * @@ -1524,7 +1556,7 @@ Crafty.extend({ * Crafty.isPaused(); * ~~~ */ - isPaused: function () { + isPaused: function() { return this._paused; }, @@ -1532,10 +1564,10 @@ Crafty.extend({ * #Crafty.timer * @category Game Loop * @kind CoreObject - * + * * Handles game ticks */ - timer: (function () { + timer: (function() { /* * `window.requestAnimationFrame` or its variants is called for animation. * `.requestID` keeps a record of the return value previous `window.requestAnimationFrame` call. @@ -1557,26 +1589,23 @@ Crafty.extend({ var FPS = 50, milliSecPerFrame = 1000 / FPS; - - - return { - init: function () { + init: function() { // When first called, set the gametime one frame before now! if (typeof gameTime === "undefined") gameTime = Date.now() - milliSecPerFrame; - var onFrame = (typeof window !== "undefined") && ( - window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - null - ); + var onFrame = + typeof window !== "undefined" && + (window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + null); if (onFrame) { - tick = function () { + tick = function() { Crafty.timer.step(); if (tick !== null) { requestID = onFrame(tick); @@ -1586,32 +1615,31 @@ Crafty.extend({ tick(); } else { - tick = setInterval(function () { + tick = setInterval(function() { Crafty.timer.step(); }, 1000 / FPS); } }, - stop: function () { + stop: function() { Crafty.trigger("CraftyStopTimer"); if (typeof tick !== "function") clearInterval(tick); - var onFrame = (typeof window !== "undefined") && ( - window.cancelAnimationFrame || - window.cancelRequestAnimationFrame || - window.webkitCancelRequestAnimationFrame || - window.mozCancelRequestAnimationFrame || - window.oCancelRequestAnimationFrame || - window.msCancelRequestAnimationFrame || - null - ); + var onFrame = + typeof window !== "undefined" && + (window.cancelAnimationFrame || + window.cancelRequestAnimationFrame || + window.webkitCancelRequestAnimationFrame || + window.mozCancelRequestAnimationFrame || + window.oCancelRequestAnimationFrame || + window.msCancelRequestAnimationFrame || + null); if (onFrame) onFrame(requestID); tick = null; }, - /**@ * #Crafty.timer.steptype * @comp Crafty.timer @@ -1634,25 +1662,32 @@ Crafty.extend({ * * @see Crafty.timer.FPS */ - steptype: function (newmode, option) { + steptype: function(newmode, option) { // setters if (newmode === "variable" || newmode === "semifixed") { mode = newmode; - if (option) - maxTimestep = option; - Crafty.trigger("NewSteptype", {mode: mode, maxTimeStep: maxTimestep}); + if (option) maxTimestep = option; + Crafty.trigger("NewSteptype", { + mode: mode, + maxTimeStep: maxTimestep + }); } else if (newmode === "fixed") { mode = "fixed"; - if (option) - maxFramesPerStep = option; - Crafty.trigger("NewSteptype", {mode: mode, maxTimeStep: maxFramesPerStep}); + if (option) maxFramesPerStep = option; + Crafty.trigger("NewSteptype", { + mode: mode, + maxTimeStep: maxFramesPerStep + }); } else if (newmode !== undefined) { throw "Invalid step type specified"; - // getter + // getter } else { return { mode: mode, - maxTimeStep: (mode === "variable" || mode === "semifixed") ? maxTimestep : maxFramesPerStep + maxTimeStep: + mode === "variable" || mode === "semifixed" + ? maxTimestep + : maxFramesPerStep }; } }, @@ -1661,7 +1696,7 @@ Crafty.extend({ * #Crafty.timer.step * @comp Crafty.timer * @kind Method - * + * * @sign public void Crafty.timer.step() * @trigger EnterFrame - Triggered before 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 UpdateFrame - 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 } @@ -1679,8 +1714,11 @@ Crafty.extend({ * @see Crafty.timer.steptype * @see Crafty.timer.FPS */ - step: function () { - var drawTimeStart, dt, lastFrameTime, loops = 0; + step: function() { + var drawTimeStart, + dt, + lastFrameTime, + loops = 0; var currentTime = Date.now(); if (endTime > 0) @@ -1721,7 +1759,7 @@ Crafty.extend({ // dt is determined by the mode for (var i = 0; i < loops; i++) { lastFrameTime = currentTime; - + var frameData = { frame: frame++, dt: dt, @@ -1739,7 +1777,10 @@ Crafty.extend({ gameTime += dt; currentTime = Date.now(); - Crafty.trigger("MeasureFrameTime", currentTime - lastFrameTime); + Crafty.trigger( + "MeasureFrameTime", + currentTime - lastFrameTime + ); } //If any frames were processed, render the results @@ -1749,7 +1790,10 @@ Crafty.extend({ Crafty.trigger("RenderScene"); Crafty.trigger("PostRender"); // Post-render cleanup opportunity currentTime = Date.now(); - Crafty.trigger("MeasureRenderTime", currentTime - drawTimeStart); + Crafty.trigger( + "MeasureRenderTime", + currentTime - drawTimeStart + ); } endTime = currentTime; @@ -1758,7 +1802,7 @@ Crafty.extend({ * #Crafty.timer.FPS * @comp Crafty.timer * @kind Method - * + * * @sign public void Crafty.timer.FPS() * Returns the target frames per second. This is not an actual frame rate. * @sign public void Crafty.timer.FPS(Number value) @@ -1770,9 +1814,8 @@ Crafty.extend({ * * @see Crafty.timer.steptype */ - FPS: function (value) { - if (typeof value === "undefined") - return FPS; + FPS: function(value) { + if (typeof value === "undefined") return FPS; else { FPS = value; milliSecPerFrame = 1000 / FPS; @@ -1784,13 +1827,13 @@ Crafty.extend({ * #Crafty.timer.simulateFrames * @comp Crafty.timer * @kind Method - * + * * @sign public this Crafty.timer.simulateFrames(Number frames[, Number timestep]) * Advances the game state by a number of frames and draws the resulting stage at the end. Useful for tests and debugging. * @param frames - number of frames to simulate * @param timestep - the duration to pass each frame. Defaults to milliSecPerFrame (20 ms) if not specified. */ - simulateFrames: function (frames, timestep) { + simulateFrames: function(frames, timestep) { timestep = timestep || milliSecPerFrame; while (frames-- > 0) { var frameData = { @@ -1808,12 +1851,11 @@ Crafty.extend({ }; })(), - /**@ * #Crafty.e * @category Core * @kind Method - * + * * @trigger NewEntity - When the entity is created and all components are added - { id:Number } * @sign public Entity Crafty.e(String componentList) * @param componentList - List of components to assign to new entity @@ -1833,7 +1875,7 @@ Crafty.extend({ * * @see Crafty.c */ - e: function () { + e: function() { var id = UID(); entities[id] = null; entities[id] = Crafty(id); @@ -1841,7 +1883,7 @@ Crafty.extend({ if (arguments.length > 0) { entities[id].addComponent.apply(entities[id], arguments); } - entities[id].setName('Entity #' + id); //set default entity human readable name + entities[id].setName("Entity #" + id); //set default entity human readable name entities[id].addComponent("obj"); //every entity automatically assumes obj Crafty.trigger("NewEntity", { @@ -1855,7 +1897,7 @@ Crafty.extend({ * #Crafty.c * @category Core * @kind Method - * + * * @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 @@ -1863,7 +1905,7 @@ Crafty.extend({ * Creates a component where the first argument is the ID and the second * is the object that will be inherited by entities. * - * Specifically, each time a component is added to an entity, the component properties are copied over to the entity. + * Specifically, each time a component is added to an entity, the component properties are copied over to the entity. * * In the case of primitive datatypes (booleans, numbers, strings) the property is copied by value. * * In the case of complex datatypes (objects, arrays, functions) the property is copied by reference and will thus reference the components' original property. * * (See the two examples below for further explanation) @@ -1907,10 +1949,10 @@ Crafty.extend({ * ~~~ * * - * @warning In the examples above the field _message is local to the entity. + * @warning In the examples 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, + * 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. @@ -1926,7 +1968,7 @@ Crafty.extend({ * * @see Crafty.e */ - c: function (compName, component) { + c: function(compName, component) { components[compName] = component; }, @@ -1934,7 +1976,7 @@ Crafty.extend({ * #Crafty.trigger * @category Core, Events * @kind Method - * + * * @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 @@ -1944,11 +1986,11 @@ Crafty.extend({ * * @see Crafty.bind */ - trigger: function (event, data) { - + trigger: function(event, data) { // To learn how the event system functions, see the comments for Crafty._callbackMethods var hdl = handlers[event] || (handlers[event] = {}), - h, callbacks; + h, + callbacks; //loop over every object bound for (h in hdl) { // Check whether h needs to be processed @@ -1964,7 +2006,7 @@ Crafty.extend({ * #Crafty.bind * @category Core, Events * @kind Method - * + * * @sign public Function bind(String eventName, Function callback) * @param eventName - Name of the event to bind to * @param callback - Method to execute upon event triggered @@ -1975,19 +2017,17 @@ Crafty.extend({ * * @see Crafty.trigger, Crafty.unbind */ - bind: function (event, callback) { - + bind: function(event, callback) { // To learn how the event system functions, see the comments for Crafty._callbackMethods this._bindCallback(event, callback); return callback; }, - /**@ * #Crafty.uniqueBind * @category Core, Events * @kind Method - * + * * @sign public Function uniqueBind(String eventName, Function callback) * @param eventName - Name of the event to bind to * @param callback - Method to execute upon event triggered @@ -1997,7 +2037,7 @@ Crafty.extend({ * * @see Crafty.bind */ - uniqueBind: function (event, callback) { + uniqueBind: function(event, callback) { this.unbind(event, callback); return this.bind(event, callback); }, @@ -2006,7 +2046,7 @@ Crafty.extend({ * #Crafty.one * @category Core, Events * @kind Method - * + * * @sign public Function one(String eventName, Function callback) * @param eventName - Name of the event to bind to * @param callback - Method to execute upon event triggered @@ -2016,9 +2056,9 @@ Crafty.extend({ * * @see Crafty.bind */ - one: function (event, callback) { + one: function(event, callback) { var self = this; - var oneHandler = function (data) { + var oneHandler = function(data) { callback.call(self, data); self.unbind(event, oneHandler); }; @@ -2029,7 +2069,7 @@ Crafty.extend({ * #Crafty.unbind * @category Core, Events * @kind Method - * + * * @sign public Boolean Crafty.unbind(String eventName, Function callback) * @param eventName - Name of the event to unbind * @param callback - Function to unbind @@ -2053,7 +2093,7 @@ Crafty.extend({ * includes all callbacks attached by `Crafty.bind('GameOver', ...)`, but * none of the callbacks attached by `some_entity.bind('GameOver', ...)`. */ - unbind: function (event, callback) { + unbind: function(event, callback) { // To learn how the event system functions, see the comments for Crafty._callbackMethods this._unbindCallbacks(event, callback); }, @@ -2062,29 +2102,29 @@ Crafty.extend({ * #Crafty.frame * @category Core * @kind Method - * + * * @sign public Number Crafty.frame(void) * @returns the current frame number */ - frame: function () { + frame: function() { return frame; }, - entities: function () { + entities: function() { return entities; }, - components: function () { + components: function() { return components; }, - isComp: function (comp) { + isComp: function(comp) { return comp in components; }, - debug: function (str) { + debug: function(str) { // access internal variables - handlers or entities - if (str === 'handlers') { + if (str === "handlers") { return handlers; } return entities; @@ -2094,10 +2134,10 @@ Crafty.extend({ * #Crafty.settings * @category Core * @kind CoreObject - * + * * Modify the inner workings of Crafty through the settings. */ - settings: (function () { + settings: (function() { var states = {}, callbacks = {}; @@ -2106,7 +2146,7 @@ Crafty.extend({ * #Crafty.settings.register * @comp Crafty.settings * @kind Method - * + * * @sign public void Crafty.settings.register(String settingName, Function callback) * @param settingName - Name of the setting * @param callback - Function to execute when use modifies setting @@ -2115,7 +2155,7 @@ Crafty.extend({ * * @see Crafty.settings.modify */ - register: function (setting, callback) { + register: function(setting, callback) { callbacks[setting] = callback; }, @@ -2123,7 +2163,7 @@ Crafty.extend({ * #Crafty.settings.modify * @comp Crafty.settings * @kind Method - * + * * @sign public void Crafty.settings.modify(String settingName, * value) * @param settingName - Name of the setting * @param value - Value to set the setting to @@ -2132,7 +2172,7 @@ Crafty.extend({ * * @see Crafty.settings.register, Crafty.settings.get */ - modify: function (setting, value) { + modify: function(setting, value) { if (!callbacks[setting]) return; callbacks[setting].call(states[setting], value); states[setting] = value; @@ -2142,7 +2182,7 @@ Crafty.extend({ * #Crafty.settings.get * @comp Crafty.settings * @kind Method - * + * * @sign public * Crafty.settings.get(String settingName) * @param settingName - Name of the setting * @returns Current value of the setting @@ -2151,7 +2191,7 @@ Crafty.extend({ * * @see Crafty.settings.register, Crafty.settings.get */ - get: function (setting) { + get: function(setting) { return states[setting]; } }; @@ -2161,7 +2201,7 @@ Crafty.extend({ * #Crafty.defineField * @category Core * @kind Method - * + * * @sign public void Crafty.defineField(Object object, String property, Function getCallback, Function setCallback) * @param object - Object to define property on * @param property - Property name to assign getter & setter to @@ -2177,9 +2217,9 @@ Crafty.extend({ * @example * ~~~ * var ent = Crafty.e("2D"); - * Crafty.defineField(ent, "customData", function() { - * return this._customData; - * }, function(newValue) { + * Crafty.defineField(ent, "customData", function() { + * return this._customData; + * }, function(newValue) { * this._customData = newValue; * }); * @@ -2193,7 +2233,7 @@ Crafty.extend({ get: getCallback, set: setCallback, configurable: false, - enumerable: true, + enumerable: true }); }, @@ -2217,19 +2257,19 @@ function UID() { * #Crafty.clone * @category Core * @kind Method - * + * * @sign public Object .clone(Object obj) * @param obj - an object * * Deep copy (a.k.a clone) of an object. * @note This function should be used for plain objects with no cyclic references. To clone an entity use its `.clone` method instead. - * + * * @example * ~~~ * // Null or Primitive types * Crafty.clone(null); // returns null * Crafty.clone(4); // returns 4 - * + * * // Objects * var globalCount = 0; * var obj1 = { @@ -2242,13 +2282,13 @@ function UID() { * console.log(this.count + '/' + globalCount); * } * }; - * + * * obj1.inc(); * obj1.log(); // prints "1/1" to the log - * + * * var obj2 = Crafty.clone(obj1); * obj2.log(); // prints "1/1" to the log - * + * * obj1.inc(); * obj1.log(); // prints "2/2" to the log * obj2.log(); // prints "1/2" to the log @@ -2258,21 +2298,21 @@ function UID() { */ function clone(obj) { - if (obj === null || typeof (obj) !== 'object') - return obj; + if (obj === null || typeof obj !== "object") return obj; var temp = obj.constructor(); // changed - for (var key in obj) - temp[key] = clone(obj[key]); + for (var key in obj) temp[key] = clone(obj[key]); return temp; } // export Crafty -if (typeof define === 'function') { // AMD - define('crafty', [], function () { // jshint ignore:line +if (typeof define === "function") { + // AMD + define("crafty", [], function() { + // jshint ignore:line return Crafty; }); } -module.exports = Crafty; \ No newline at end of file +module.exports = Crafty; diff --git a/src/core/extensions.js b/src/core/extensions.js index b8d0daa3..5c88a21a 100644 --- a/src/core/extensions.js +++ b/src/core/extensions.js @@ -1,21 +1,26 @@ -var Crafty = require('../core/core.js'); -var document = (typeof window !== "undefined") && window.document; +var Crafty = require("../core/core.js"); +var document = typeof window !== "undefined" && window.document; /**@ * #Crafty.support * @category Misc, Core * @kind CoreObject - * + * * Determines feature support for what Crafty can do. */ (function testSupport() { - var support = Crafty.support = {}, - ua = (typeof navigator !== "undefined" && navigator.userAgent.toLowerCase()) || (typeof process !== "undefined" && process.version), - match = /(webkit)[ \/]([\w.]+)/.exec(ua) || + var support = (Crafty.support = {}), + ua = + (typeof navigator !== "undefined" && + navigator.userAgent.toLowerCase()) || + (typeof process !== "undefined" && process.version), + match = + /(webkit)[ \/]([\w.]+)/.exec(ua) || /(o)pera(?:.*version)?[ \/]([\w.]+)/.exec(ua) || /(ms)ie ([\w.]+)/.exec(ua) || /(moz)illa(?:.*? rv:([\w.]+))?/.exec(ua) || - /(v)\d+\.(\d+)/.exec(ua) || [], + /(v)\d+\.(\d+)/.exec(ua) || + [], mobile = /iPad|iPod|iPhone|Android|webOS|IEMobile/i.exec(ua); /**@ @@ -40,13 +45,13 @@ var document = (typeof window !== "undefined") && window.document; * #Crafty.support.defineProperty * @comp Crafty.support * @kind Property - * + * * Is `Object.defineProperty` supported? */ - support.defineProperty = (function () { - if (!('defineProperty' in Object)) return false; + support.defineProperty = (function() { + if (!("defineProperty" in Object)) return false; try { - Object.defineProperty({}, 'x', {}); + Object.defineProperty({}, "x", {}); } catch (e) { return false; } @@ -57,19 +62,21 @@ var document = (typeof window !== "undefined") && window.document; * #Crafty.support.audio * @comp Crafty.support * @kind Property - * + * * Is HTML5 `Audio` supported? */ - support.audio = (typeof window !== "undefined") && ('canPlayType' in document.createElement('audio')); + support.audio = + typeof window !== "undefined" && + "canPlayType" in document.createElement("audio"); /**@ * #Crafty.support.prefix * @comp Crafty.support * @kind Property - * + * * Returns the browser specific prefix (`Moz`, `O`, `ms`, `webkit`, `node`). */ - support.prefix = (match[1] || match[0]); + support.prefix = match[1] || match[0]; //browser specific quirks if (support.prefix === "moz") support.prefix = "Moz"; @@ -81,7 +88,7 @@ var document = (typeof window !== "undefined") && window.document; * #Crafty.support.versionName * @comp Crafty.support * @kind Property - * + * * Version of the browser */ support.versionName = match[2]; @@ -90,26 +97,28 @@ var document = (typeof window !== "undefined") && window.document; * #Crafty.support.version * @comp Crafty.support * @kind Property - * + * * Version number of the browser as an Integer (first number) */ - support.version = +(match[2].split("."))[0]; + support.version = +match[2].split(".")[0]; } /**@ * #Crafty.support.canvas * @comp Crafty.support * @kind Property - * + * * Is the `canvas` element supported? */ - support.canvas = (typeof window !== "undefined") && ('getContext' in document.createElement("canvas")); + support.canvas = + typeof window !== "undefined" && + "getContext" in document.createElement("canvas"); /**@ * #Crafty.support.webgl * @comp Crafty.support * @kind Property - * + * * Is WebGL supported on the canvas element? */ if (support.canvas) { @@ -120,7 +129,7 @@ var document = (typeof window !== "undefined") && window.document; gl.viewportWidth = support.canvas.width; gl.viewportHeight = support.canvas.height; } catch (e) {} - support.webgl = !! gl; + support.webgl = !!gl; } else { support.webgl = false; } @@ -129,10 +138,16 @@ var document = (typeof window !== "undefined") && window.document; * #Crafty.support.css3dtransform * @comp Crafty.support * @kind Property - * + * * Is css3Dtransform supported by browser. */ - support.css3dtransform = (typeof window !== "undefined") && ((typeof document.createElement("div").style.Perspective !== "undefined") || (typeof document.createElement("div").style[support.prefix + "Perspective"] !== "undefined")); + support.css3dtransform = + typeof window !== "undefined" && + (typeof document.createElement("div").style.Perspective !== + "undefined" || + typeof document.createElement("div").style[ + support.prefix + "Perspective" + ] !== "undefined"); /**@ * #Crafty.support.deviceorientation @@ -140,15 +155,19 @@ var document = (typeof window !== "undefined") && window.document; * @kind Property * Is deviceorientation event supported by browser. */ - support.deviceorientation = (typeof window !== "undefined") && ((typeof window.DeviceOrientationEvent !== "undefined") || (typeof window.OrientationEvent !== "undefined")); + support.deviceorientation = + typeof window !== "undefined" && + (typeof window.DeviceOrientationEvent !== "undefined" || + typeof window.OrientationEvent !== "undefined"); /**@ * #Crafty.support.devicemotion * @comp Crafty.support * @kind Property - * + * * Is devicemotion event supported by browser. */ - support.devicemotion = (typeof window !== "undefined") && (typeof window.DeviceMotionEvent !== "undefined"); - + support.devicemotion = + typeof window !== "undefined" && + typeof window.DeviceMotionEvent !== "undefined"; })(); diff --git a/src/core/loader.js b/src/core/loader.js index 0d60b44d..b2bc8519 100644 --- a/src/core/loader.js +++ b/src/core/loader.js @@ -1,4 +1,5 @@ -var Crafty = require('../core/core.js'), Utility = require('./utility'); +var Crafty = require("../core/core.js"), + Utility = require("./utility"); module.exports = { /**@ @@ -58,10 +59,8 @@ module.exports = { if (typeof p === "undefined") { return this.__paths; } else { - if(p.audio) - this.__paths.audio = p.audio; - if(p.images) - this.__paths.images = p.images; + if (p.audio) this.__paths.audio = p.audio; + if (p.images) this.__paths.images = p.images; } }, @@ -91,7 +90,7 @@ module.exports = { * * @see Crafty.assets */ - asset: function (key, value) { + asset: function(key, value) { if (arguments.length === 1) { return Crafty.assets[key]; } @@ -235,49 +234,58 @@ module.exports = { * @see Crafty.imageWhitelist * @see Crafty.removeAssets */ - load: function (data, oncomplete, onprogress, onerror) { - + load: function(data, oncomplete, onprogress, onerror) { if (Array.isArray(data)) { - Crafty.log("Calling Crafty.load with an array of assets no longer works; see the docs for more details."); + Crafty.log( + "Calling Crafty.load with an array of assets no longer works; see the docs for more details." + ); return; } - data = (typeof data === "string" ? JSON.parse(data) : data); + data = typeof data === "string" ? JSON.parse(data) : data; var j = 0, - total = (data.audio ? Object.keys(data.audio).length : 0) + + total = + (data.audio ? Object.keys(data.audio).length : 0) + (data.images ? Object.keys(data.images).length : 0) + (data.sprites ? Object.keys(data.sprites).length : 0), - current, fileUrl, obj, type, asset, + current, + fileUrl, + obj, + type, + asset, paths = Crafty.paths(), - getFilePath = function(type,f) { - return (f.search("://") === -1 ? (type === "audio" ? paths.audio + f : paths.images + f) : f); + getFilePath = function(type, f) { + return f.search("://") === -1 + ? type === "audio" + ? paths.audio + f + : paths.images + f + : f; }, // returns null if 'a' is not already a loaded asset, obj otherwise isAsset = function(a) { return Crafty.asset(a) || null; }, isSupportedAudio = function(f) { - - return Crafty.support.audio && Crafty.audio.supports( - Utility.fileTypeOf( f ).type + return ( + Crafty.support.audio && + Crafty.audio.supports(Utility.fileTypeOf(f).type) ); }, isValidImage = function(f) { - - return -1 < Crafty.imageWhitelist.indexOf( - Utility.fileTypeOf( f ).type + return ( + -1 < + Crafty.imageWhitelist.indexOf(Utility.fileTypeOf(f).type) ); }, - shortURLOf = function (URI) { - - return (Utility.fileTypeOf( URI ).schema === 'data') ? - URL.createObjectURL( Utility.toBlob( URI ) ) : URI; + shortURLOf = function(URI) { + return Utility.fileTypeOf(URI).schema === "data" + ? URL.createObjectURL(Utility.toBlob(URI)) + : URI; }, - onImgLoad = function(obj,url) { + onImgLoad = function(obj, url) { obj.onload = pro; - if (Crafty.support.prefix === 'webkit') - obj.src = ""; // workaround for webkit bug + if (Crafty.support.prefix === "webkit") obj.src = ""; // workaround for webkit bug obj.src = url; }; @@ -287,7 +295,7 @@ module.exports = { //Remove events cause audio trigger this event more than once(depends on browser) if (this.removeEventListener) - this.removeEventListener('canplaythrough', pro, false); + this.removeEventListener("canplaythrough", pro, false); j++; //if progress callback, give information of assets loaded, total and percent @@ -295,7 +303,7 @@ module.exports = { onprogress({ loaded: j, total: total, - percent: (j / total * 100), + percent: (j / total) * 100, src: src }); @@ -309,7 +317,7 @@ module.exports = { onerror({ loaded: j, total: total, - percent: (j / total * 100), + percent: (j / total) * 100, src: src }); @@ -318,53 +326,57 @@ module.exports = { } for (type in data) { - for(asset in data[type]) { - if (!data[type].hasOwnProperty(asset)) - continue; // maintain compatibility to other frameworks while iterating array + for (asset in data[type]) { + if (!data[type].hasOwnProperty(asset)) continue; // maintain compatibility to other frameworks while iterating array current = data[type][asset]; obj = null; if (type === "audio") { - current = (typeof current === "object") ? - current : {'': current + ''}; + current = + typeof current === "object" + ? current + : { "": current + "" }; // Disable (Don't make functions in a loop) warning // jshint -W083 - var files = Object.keys( current ).filter(function (key) { - + var files = Object.keys(current).filter(function(key) { var fileUrl = getFilePath(type, current[key]); if ( - !isAsset( fileUrl ) && - isSupportedAudio( current[key] ) && + !isAsset(fileUrl) && + isSupportedAudio(current[key]) && !Crafty.audio.sounds[asset] ) - return shortURLOf( fileUrl ); + return shortURLOf(fileUrl); }); // jshint +W083 - if ( files[0] ) obj = Crafty.audio.add(asset, files); + if (files[0]) obj = Crafty.audio.add(asset, files); //extract actual audio obj if audio creation was successfull - if ( obj ) obj = obj.obj; + if (obj) obj = obj.obj; //addEventListener is supported on IE9 , Audio as well if (obj && obj.addEventListener) - obj.addEventListener('canplaythrough', pro, false); + obj.addEventListener("canplaythrough", pro, false); } else { - asset = (type === "sprites") ? asset : current; + asset = type === "sprites" ? asset : current; fileUrl = getFilePath(type, asset); - if (!isAsset( fileUrl ) && isValidImage( asset )) { - + if (!isAsset(fileUrl) && isValidImage(asset)) { obj = new Image(); - fileUrl = shortURLOf( fileUrl ); + fileUrl = shortURLOf(fileUrl); if (type === "sprites") Crafty.sprite( - current.tile, current.tileh, fileUrl, current.map, - current.paddingX, current.paddingY, current.paddingAroundBorder + current.tile, + current.tileh, + fileUrl, + current.map, + current.paddingX, + current.paddingY, + current.paddingAroundBorder ); Crafty.asset(fileUrl, obj); @@ -376,7 +388,7 @@ module.exports = { if (obj) { obj.onerror = err; } else { - err.call({src: fileUrl}); + err.call({ src: fileUrl }); } } } @@ -434,19 +446,24 @@ module.exports = { * @see Crafty.load */ removeAssets: function(data) { + data = typeof data === "string" ? JSON.parse(data) : data; - data = (typeof data === "string" ? JSON.parse(data) : data); - - var current, fileUrl, type, asset, + var current, + fileUrl, + type, + asset, paths = Crafty.paths(), - getFilePath = function(type,f) { - return (f.search("://") === -1 ? (type === "audio" ? paths.audio + f : paths.images + f) : f); + getFilePath = function(type, f) { + return f.search("://") === -1 + ? type === "audio" + ? paths.audio + f + : paths.images + f + : f; }; for (type in data) { for (asset in data[type]) { - if (!data[type].hasOwnProperty(asset)) - continue; // maintain compatibility to other frameworks while iterating array + if (!data[type].hasOwnProperty(asset)) continue; // maintain compatibility to other frameworks while iterating array current = data[type][asset]; @@ -457,14 +474,12 @@ module.exports = { if (Crafty.asset(fileUrl)) Crafty.audio.remove(asset); } - } - else if (typeof current === "string") { + } else if (typeof current === "string") { fileUrl = getFilePath(type, current); - if (Crafty.asset(fileUrl)) - Crafty.audio.remove(asset); + if (Crafty.asset(fileUrl)) Crafty.audio.remove(asset); } } else { - asset = (type === "sprites" ? asset : current); + asset = type === "sprites" ? asset : current; fileUrl = getFilePath(type, asset); if (Crafty.asset(fileUrl)) { if (type === "sprites") diff --git a/src/core/model.js b/src/core/model.js index 51ee19d5..fab85986 100644 --- a/src/core/model.js +++ b/src/core/model.js @@ -1,11 +1,10 @@ -var Crafty = require('../core/core.js'); - +var Crafty = require("../core/core.js"); /**@ * #Model * @category Model * @kind Component - * + * * Model is a component that offers new features for isolating business * logic in your application. It offers default values, dirty values, * and deep events on your data. @@ -38,69 +37,68 @@ var Crafty = require('../core/core.js'); * ~~~ */ module.exports = { - init: function() { - this.changed = []; - this.bind('Change', this._changed_attributes); - this.bind('Change', this._changed_triggers); - }, + init: function() { + this.changed = []; + this.bind("Change", this._changed_attributes); + this.bind("Change", this._changed_triggers); + }, - /** - * Fires more specific `Change` events. - * - * For instance a `Change[name]` may get fired when you - * update the name data attribute on the model. - */ - _changed_triggers: function(data, options) { - var key; - options = Crafty.extend.call({pre: ''}, options); - for (key in data) { - this.trigger('Change[' + options.pre + key + ']', data[key]); - if (data[key].constructor === Object) { - this._changed_triggers(data[key], { - pre: options.pre + key + '.' - }); - } - } - }, + /** + * Fires more specific `Change` events. + * + * For instance a `Change[name]` may get fired when you + * update the name data attribute on the model. + */ + _changed_triggers: function(data, options) { + var key; + options = Crafty.extend.call({ pre: "" }, options); + for (key in data) { + this.trigger("Change[" + options.pre + key + "]", data[key]); + if (data[key].constructor === Object) { + this._changed_triggers(data[key], { + pre: options.pre + key + "." + }); + } + } + }, - /** - * Pushes all top-levle changed attribute names to the - * changed array. - */ - _changed_attributes: function(data) { - var key; - for (key in data) { - this.changed.push(key); - } - return this; - }, + /** + * Pushes all top-levle changed attribute names to the + * changed array. + */ + _changed_attributes: function(data) { + var key; + for (key in data) { + this.changed.push(key); + } + return this; + }, - /**@ - * #.is_dirty - * @comp Model - * @kind Method - * - * Helps determine when data or the entire component is "dirty" or has changed attributes. - * - * @example - * ~~~ - * person = Crafty.e('Person').attr({name: 'Fox', age: 24}) - * person.is_dirty() // false - * person.is_dirty('name') // false - * - * person.attr('name', 'Lucky'); - * person.is_dirty(); // true - * person.is_dirty('name'); // true - * person.is_dirty('age'); // false - * person.changed; // ['name'] - * ~~~ - */ - is_dirty: function(key) { - if (arguments.length === 0) { - return !!this.changed.length; - } else { - return this.changed.indexOf(key) > -1; + /**@ + * #.is_dirty + * @comp Model + * @kind Method + * + * Helps determine when data or the entire component is "dirty" or has changed attributes. + * + * @example + * ~~~ + * person = Crafty.e('Person').attr({name: 'Fox', age: 24}) + * person.is_dirty() // false + * person.is_dirty('name') // false + * + * person.attr('name', 'Lucky'); + * person.is_dirty(); // true + * person.is_dirty('name'); // true + * person.is_dirty('age'); // false + * person.changed; // ['name'] + * ~~~ + */ + is_dirty: function(key) { + if (arguments.length === 0) { + return !!this.changed.length; + } else { + return this.changed.indexOf(key) > -1; + } } - } }; - diff --git a/src/core/scenes.js b/src/core/scenes.js index d44b9152..a0737b1c 100644 --- a/src/core/scenes.js +++ b/src/core/scenes.js @@ -1,5 +1,4 @@ -var Crafty = require('../core/core.js'); - +var Crafty = require("../core/core.js"); module.exports = { _scenes: {}, @@ -9,7 +8,7 @@ module.exports = { * #Crafty.scene * @category Scenes, Stage * @kind Method - * + * * @trigger SceneChange - just before a new scene is initialized - { oldScene:String, newScene:String } * @trigger SceneDestroy - just before the current scene is destroyed - { newScene:String } * @@ -58,7 +57,7 @@ module.exports = { * Crafty.e("2D, DOM, Color") * .attr(attributes) * .color("red"); - * + * * }); * * ~~~ @@ -83,9 +82,9 @@ module.exports = { * This will clear the stage, set the background black, and create a red square with the specified position and dimensions. * ~~~ */ - scene: function (name, intro, outro) { + scene: function(name, intro, outro) { // If there's one argument, or the second argument isn't a function, play the scene - if (arguments.length === 1 || typeof(arguments[1]) !== "function") { + if (arguments.length === 1 || typeof arguments[1] !== "function") { Crafty.enterScene(name, arguments[1]); return; } @@ -105,16 +104,15 @@ module.exports = { * @see Crafty.enterScene * @see Crafty.scene */ - defineScene: function(name, init, uninit){ + defineScene: function(name, init, uninit) { if (typeof init !== "function") - throw("Init function is the wrong type."); + throw "Init function is the wrong type."; this._scenes[name] = {}; this._scenes[name].initialize = init; - if (typeof uninit !== 'undefined') { + if (typeof uninit !== "undefined") { this._scenes[name].uninitialize = uninit; } return; - }, /* @@ -132,9 +130,8 @@ module.exports = { * @see Crafty.defineScene * @see Crafty.scene */ - enterScene: function(name, data){ - if (typeof data === "function") - throw("Scene data cannot be a function"); + enterScene: function(name, data) { + if (typeof data === "function") throw "Scene data cannot be a function"; // ---FYI--- // this._current is the name (ID) of the scene in progress. @@ -147,11 +144,14 @@ module.exports = { }); Crafty.viewport.reset(); - Crafty("2D").each(function () { + Crafty("2D").each(function() { if (!this.has("Persist")) this.destroy(); }); // uninitialize previous scene - if (this._current !== null && 'uninitialize' in this._scenes[this._current]) { + if ( + this._current !== null && + "uninitialize" in this._scenes[this._current] + ) { this._scenes[this._current].uninitialize.call(this); } // initialize next scene @@ -161,7 +161,7 @@ module.exports = { oldScene: oldScene, newScene: name }); - + if (this._scenes.hasOwnProperty(name)) { this._scenes[name].initialize.call(this, data); } else { @@ -169,6 +169,5 @@ module.exports = { } return; - } }; diff --git a/src/core/storage.js b/src/core/storage.js index 48ba6f88..f926f55b 100644 --- a/src/core/storage.js +++ b/src/core/storage.js @@ -1,17 +1,18 @@ -var Crafty = require('../core/core.js'); +var Crafty = require("../core/core.js"); try { - var storage = (typeof window !== "undefined" && window.localStorage) || (new require('node-localstorage').LocalStorage('./localStorage')); -} catch(e) { - var storage = null; + var storage = + (typeof window !== "undefined" && window.localStorage) || + new require("node-localstorage").LocalStorage("./localStorage"); +} catch (e) { + var storage = null; } - /**@ * #Storage * @category Utilities * @kind Property - * + * * Very simple way to get and set values, which will persist when the browser is closed also. * Storage wraps around HTML5 Web Storage, which is well-supported across browsers and platforms, but limited to 5MB total storage per domain. * Storage is also available for node, which is permanently persisted to the `./localStorage` folder - take care of removing entries. Note that multiple Crafty instances use the same storage, so care has to be taken not to overwrite existing entries. @@ -20,9 +21,9 @@ try { * #Crafty.storage * @comp Storage * @kind Method - * + * * @sign Crafty.storage(String key) - * @param key - a key you would like to get from the storage. + * @param key - a key you would like to get from the storage. * @returns The stored value, or `null` if none saved under that key exists * * @sign Crafty.storage(String key, String value) @@ -33,7 +34,7 @@ try { * @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. * - * `Crafty.storage` is used synchronously to either get or set values. + * `Crafty.storage` is used synchronously to either get or set values. * * You can store booleans, strings, objects and arrays. * @@ -68,35 +69,34 @@ try { */ var store = function(key, value) { - var _value = value; + var _value = value; - if(!storage) { - Crafty.error("Local storage is not accessible. (Perhaps you are including crafty.js cross-domain?)"); - 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); + if (!storage) { + Crafty.error( + "Local storage is not accessible. (Perhaps you are including crafty.js cross-domain?)" + ); + return false; } - storage.setItem(key, _value); - - } + 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); + } }; /**@ * #Crafty.storage.remove * @comp Storage * @kind Method - * + * * @sign Crafty.storage.remove(String key) * @param key - a key where you will like to delete the value of. * @@ -112,11 +112,13 @@ var store = function(key, value) { * */ store.remove = function(key) { - if(!storage){ - Crafty.error("Local storage is not accessible. (Perhaps you are including crafty.js cross-domain?)"); - return; - } - storage.removeItem(key); + if (!storage) { + Crafty.error( + "Local storage is not accessible. (Perhaps you are including crafty.js cross-domain?)" + ); + return; + } + storage.removeItem(key); }; module.exports = store; diff --git a/src/core/systems.js b/src/core/systems.js index c28341e6..9c564f44 100644 --- a/src/core/systems.js +++ b/src/core/systems.js @@ -1,5 +1,4 @@ -var Crafty = require('../core/core.js'); - +var Crafty = require("../core/core.js"); // Dictionary of existing systems Crafty._systems = {}; @@ -54,7 +53,7 @@ Crafty.s = function(name, obj, options, lazy) { } }; -function optionMerge(defaults, specific){ +function optionMerge(defaults, specific) { var options = {}; // Copy all the specified keys, then all the default keys that aren't specified for (var key in specific) { @@ -64,14 +63,13 @@ function optionMerge(defaults, specific){ if (!(key in specific)) { options[key] = defaults[key]; } - } + } return options; } - Crafty._registerLazySystem = function(name, obj, options) { // This is a bit of magic to only init a system if it's requested at least once. - // We define a getter for _systems[name] that will first initialize the system, + // We define a getter for _systems[name] that will first initialize the system, // and then redefine _systems[name] to remove that getter. Object.defineProperty(Crafty._systems, name, { get: function() { @@ -86,7 +84,6 @@ Crafty._registerLazySystem = function(name, obj, options) { }, configurable: true }); - }; // Each system has its properties and methods copied onto an object of this type @@ -97,7 +94,7 @@ Crafty.CraftySystem = (function() { if (!template) return this; this._systemTemplate = template; this.extend(template); - + // Overwrite any default options with the passed options object // This does a deep copy on the objects, and treats null as a specified value this.options = optionMerge(this.options, options); @@ -106,7 +103,7 @@ Crafty.CraftySystem = (function() { Crafty._addCallbackMethods(this); // Give this object a global ID. Used for event handlers. - this[0] = "system" + (systemID++); + this[0] = "system" + systemID++; // Define properties if ("properties" in template) { @@ -119,7 +116,10 @@ Crafty.CraftySystem = (function() { if ("events" in template) { var auto = template.events; for (var eventName in auto) { - var fn = typeof auto[eventName] === "function" ? auto[eventName] : template[auto[eventName]]; + var fn = + typeof auto[eventName] === "function" + ? auto[eventName] + : template[auto[eventName]]; this.bind(eventName, fn); } } @@ -130,8 +130,6 @@ Crafty.CraftySystem = (function() { }; })(); - - Crafty.CraftySystem.prototype = { extend: function(obj) { // Copy properties and methods of obj @@ -181,5 +179,4 @@ Crafty.CraftySystem.prototype = { this._unbindAll(); delete Crafty._systems[this.name]; } - -}; \ No newline at end of file +}; diff --git a/src/core/time.js b/src/core/time.js index ac53de6f..5e45b598 100644 --- a/src/core/time.js +++ b/src/core/time.js @@ -18,10 +18,10 @@ module.exports = { */ delaySpeed: 1, - init: function () { + init: function() { this._delays = []; this._delaysPaused = false; - this.bind("UpdateFrame", function (frameData) { + this.bind("UpdateFrame", function(frameData) { if (this._delaysPaused) return; var index = this._delays.length; while (--index >= 0) { @@ -32,21 +32,20 @@ module.exports = { } else { item.accumulator += frameData.dt * this.delaySpeed; // The while loop handles the (pathological) case where dt>delay - while(item.accumulator >= item.delay && item.repeat >= 0){ + while (item.accumulator >= item.delay && item.repeat >= 0) { item.accumulator -= item.delay; item.repeat--; item.callback.call(this); } // remove finished item from array - if (item.repeat<0){ + if (item.repeat < 0) { this._delays.splice(index, 1); - if(typeof item.callbackOff === "function") + if (typeof item.callbackOff === "function") item.callbackOff.call(this); } } } }); - }, /**@ * #.delay @@ -59,7 +58,7 @@ module.exports = { * @param repeat - (optional) How often to repeat the delayed function. A value of 0 triggers the delayed * function exactly once. A value n > 0 triggers the delayed function exactly n+1 times. A * value of -1 triggers the delayed function indefinitely. Defaults to one execution. - * @param callbackOff - (optional) Method to execute after delay ends(after all iterations are executed). + * @param callbackOff - (optional) Method to execute after delay ends(after all iterations are executed). * If repeat value equals -1, callbackOff will never be triggered. * * The delay method will execute a function after a given amount of time in milliseconds. @@ -91,13 +90,13 @@ module.exports = { * ~~~ * */ - delay: function (callback, delay, repeat, callbackOff) { + delay: function(callback, delay, repeat, callbackOff) { this._delays.push({ accumulator: 0, callback: callback, callbackOff: callbackOff, delay: delay, - repeat: (repeat < 0 ? Infinity : repeat) || 0, + repeat: (repeat < 0 ? Infinity : repeat) || 0 }); return this; }, @@ -105,7 +104,7 @@ module.exports = { * #.cancelDelay * @comp Delay * @kind Method - * + * * @sign public this.cancelDelay(Function callback) * @param callback - Method reference passed to .delay * @@ -124,11 +123,11 @@ module.exports = { * ent.cancelDelay(doSomething); * ~~~ */ - cancelDelay: function (callback) { + cancelDelay: function(callback) { var index = this._delays.length; while (--index >= 0) { var item = this._delays[index]; - if(item && item.callback === callback){ + if (item && item.callback === callback) { this._delays[index] = false; } } @@ -138,7 +137,7 @@ module.exports = { * #.pauseDelays * @comp Delay * @kind Method - * + * * @sign public this.pauseDelays() * * The pauseDelays method will pause all delays of this @@ -164,7 +163,7 @@ module.exports = { * #.resumeDelays * @comp Delay * @kind Method - * + * * @sign public this.resumeDelays() * * The resumeDelays method will resume earlier paused delays for this diff --git a/src/core/tween.js b/src/core/tween.js index 28367cd0..3f9d2e0f 100644 --- a/src/core/tween.js +++ b/src/core/tween.js @@ -1,176 +1,170 @@ -var Crafty = require('../core/core.js'); - +var Crafty = require("../core/core.js"); /**@ * #Tween * @category Animation * @kind Component - * + * * @trigger TweenEnd - when a tween finishes - Object - an object containing the properties that finished tweening * * Component to animate the change in 2D properties over time. */ module.exports = { - - /**@ - * #.tweenSpeed - * @comp Tween - * - * The rate of the tween. This property defaults to 1. - * When setting tweenSpeed to 0.5, tweens will take twice as long, - * setting it to 2.0 will make them twice as short - */ - tweenSpeed: 1, - - init: function(){ - this.tweenGroup = {}; - this.tweenStart = {}; - this.tweens = []; - this.uniqueBind("UpdateFrame", this._tweenTick); - - }, - - _tweenTick: function(frameData){ - var tween, v, i; - for ( i = this.tweens.length-1; i>=0; i--){ - tween = this.tweens[i]; - tween.easing.tick(frameData.dt * this.tweenSpeed); - v = tween.easing.value(); - this._doTween(tween.props, v); - if (tween.easing.complete) { - this.tweens.splice(i, 1); - this._endTween(tween.props); - } - } - }, - - _doTween: function(props, v){ - for (var name in props) - this[name] = (1-v) * this.tweenStart[name] + v * props[name]; - - }, - - - - /**@ - * #.tween - * @comp Tween - * @kind Method - * - * @sign public this .tween(Object properties, Number duration[, String|function easingFn]) - * @param properties - Object of numeric properties and what they should animate to - * @param duration - Duration to animate the properties over, in milliseconds. - * @param easingFn - A string or custom function specifying an easing. (Defaults to linear behavior.) See Crafty.easing for more information. - * - * This method will animate numeric properties over the specified duration. - * These include `x`, `y`, `w`, `h`, `alpha` and `rotation`. - * - * The object passed should have the properties as keys and the value should be the resulting - * values of the properties. The passed object might be modified if later calls to tween animate the same properties. - * - * @example - * Move an object to 100,100 and fade out over 200 ms. - * ~~~ - * Crafty.e("2D, Tween") - * .attr({alpha: 1.0, x: 0, y: 0}) - * .tween({alpha: 0.0, x: 100, y: 100}, 200) - * ~~~ - * @example - * Rotate an object over 2 seconds, using the "smootherStep" easing function. - * ~~~ - * Crafty.e("2D, Tween") - * .attr({rotation:0}) - * .tween({rotation:180}, 2000, "smootherStep") - * ~~~ - * - * @see Crafty.easing - * - */ - tween: function (props, duration, easingFn) { - - var tween = { - props: props, - easing: new Crafty.easing(duration, easingFn) - }; - - // Tweens are grouped together by the original function call. - // Individual properties must belong to only a single group - // When a new tween starts, if it already belongs to a group, move it to the new one - // Record the group it currently belongs to, as well as its starting coordinate. - for (var propname in props){ - if (typeof this.tweenGroup[propname] !== "undefined") - this.cancelTween(propname); - this.tweenStart[propname] = this[propname]; - this.tweenGroup[propname] = props; - } - this.tweens.push(tween); - - return this; - - }, - - /**@ - * #.cancelTween - * @comp Tween - * @kind Method - * - * @sign public this .cancelTween(String target) - * @param target - The property to cancel - * - * @sign public this .cancelTween(Object target) - * @param target - An object containing the properties to cancel. - * - * Stops tweening the specified property or properties. - * Passing the object used to start the tween might be a typical use of the second signature. - */ - cancelTween: function(target){ - if (typeof target === "string"){ - if (typeof this.tweenGroup[target] === "object" ) - delete this.tweenGroup[target][target]; - } else if (typeof target === "object") { - for (var propname in target) - this.cancelTween(propname); - } - - return this; - - }, - - /**@ - * #.pauseTweens - * @comp Tween - * @kind Method - * - * @sign public this .pauseTweens() - * - * Pauses all tweens associated with the entity - */ - pauseTweens: function(){ - this.tweens.map(function(e){e.easing.pause();}); - }, - - /**@ - * #.resumeTweens - * @comp Tween - * @kind Method - * - * @sign public this .resumeTweens() - * - * Resumes all paused tweens associated with the entity - */ - resumeTweens: function(){ - this.tweens.map(function(e){e.easing.resume();}); - }, - - /* + /**@ + * #.tweenSpeed + * @comp Tween + * + * The rate of the tween. This property defaults to 1. + * When setting tweenSpeed to 0.5, tweens will take twice as long, + * setting it to 2.0 will make them twice as short + */ + tweenSpeed: 1, + + init: function() { + this.tweenGroup = {}; + this.tweenStart = {}; + this.tweens = []; + this.uniqueBind("UpdateFrame", this._tweenTick); + }, + + _tweenTick: function(frameData) { + var tween, v, i; + for (i = this.tweens.length - 1; i >= 0; i--) { + tween = this.tweens[i]; + tween.easing.tick(frameData.dt * this.tweenSpeed); + v = tween.easing.value(); + this._doTween(tween.props, v); + if (tween.easing.complete) { + this.tweens.splice(i, 1); + this._endTween(tween.props); + } + } + }, + + _doTween: function(props, v) { + for (var name in props) + this[name] = (1 - v) * this.tweenStart[name] + v * props[name]; + }, + + /**@ + * #.tween + * @comp Tween + * @kind Method + * + * @sign public this .tween(Object properties, Number duration[, String|function easingFn]) + * @param properties - Object of numeric properties and what they should animate to + * @param duration - Duration to animate the properties over, in milliseconds. + * @param easingFn - A string or custom function specifying an easing. (Defaults to linear behavior.) See Crafty.easing for more information. + * + * This method will animate numeric properties over the specified duration. + * These include `x`, `y`, `w`, `h`, `alpha` and `rotation`. + * + * The object passed should have the properties as keys and the value should be the resulting + * values of the properties. The passed object might be modified if later calls to tween animate the same properties. + * + * @example + * Move an object to 100,100 and fade out over 200 ms. + * ~~~ + * Crafty.e("2D, Tween") + * .attr({alpha: 1.0, x: 0, y: 0}) + * .tween({alpha: 0.0, x: 100, y: 100}, 200) + * ~~~ + * @example + * Rotate an object over 2 seconds, using the "smootherStep" easing function. + * ~~~ + * Crafty.e("2D, Tween") + * .attr({rotation:0}) + * .tween({rotation:180}, 2000, "smootherStep") + * ~~~ + * + * @see Crafty.easing + * + */ + tween: function(props, duration, easingFn) { + var tween = { + props: props, + easing: new Crafty.easing(duration, easingFn) + }; + + // Tweens are grouped together by the original function call. + // Individual properties must belong to only a single group + // When a new tween starts, if it already belongs to a group, move it to the new one + // Record the group it currently belongs to, as well as its starting coordinate. + for (var propname in props) { + if (typeof this.tweenGroup[propname] !== "undefined") + this.cancelTween(propname); + this.tweenStart[propname] = this[propname]; + this.tweenGroup[propname] = props; + } + this.tweens.push(tween); + + return this; + }, + + /**@ + * #.cancelTween + * @comp Tween + * @kind Method + * + * @sign public this .cancelTween(String target) + * @param target - The property to cancel + * + * @sign public this .cancelTween(Object target) + * @param target - An object containing the properties to cancel. + * + * Stops tweening the specified property or properties. + * Passing the object used to start the tween might be a typical use of the second signature. + */ + cancelTween: function(target) { + if (typeof target === "string") { + if (typeof this.tweenGroup[target] === "object") + delete this.tweenGroup[target][target]; + } else if (typeof target === "object") { + for (var propname in target) this.cancelTween(propname); + } + + return this; + }, + + /**@ + * #.pauseTweens + * @comp Tween + * @kind Method + * + * @sign public this .pauseTweens() + * + * Pauses all tweens associated with the entity + */ + pauseTweens: function() { + this.tweens.map(function(e) { + e.easing.pause(); + }); + }, + + /**@ + * #.resumeTweens + * @comp Tween + * @kind Method + * + * @sign public this .resumeTweens() + * + * Resumes all paused tweens associated with the entity + */ + resumeTweens: function() { + this.tweens.map(function(e) { + e.easing.resume(); + }); + }, + + /* * Stops tweening the specified group of properties, and fires the "TweenEnd" event. */ - _endTween: function(properties){ - var notEmpty = false; - for (var propname in properties){ - notEmpty = true; - delete this.tweenGroup[propname]; + _endTween: function(properties) { + var notEmpty = false; + for (var propname in properties) { + notEmpty = true; + delete this.tweenGroup[propname]; + } + if (notEmpty) this.trigger("TweenEnd", properties); } - if (notEmpty) this.trigger("TweenEnd", properties); - } }; diff --git a/src/core/utility.js b/src/core/utility.js index f7acf2b1..ab2e811f 100644 --- a/src/core/utility.js +++ b/src/core/utility.js @@ -1,11 +1,12 @@ exports.blobOf = function blobOf(URI) { - var XHR = new XMLHttpRequest(); - XHR.responseType = 'blob'; - XHR.open('GET', URI); + XHR.responseType = "blob"; + XHR.open("GET", URI); - return new Promise(function (resolve, reject) { - XHR.onload = function () { resolve( this.response ); }; + return new Promise(function(resolve, reject) { + XHR.onload = function() { + resolve(this.response); + }; XHR.onerror = reject; XHR.send(); }); @@ -14,40 +15,48 @@ exports.blobOf = function blobOf(URI) { var DataURI = /^data:(.+?\/(.+?))?(;base64)?,(\S+)/; exports.fileTypeOf = function fileTypeOf(URI) { - var schema = /^(?:(\w+):)?.+?(?:\.(\w+))?$/.exec( URI ); + var schema = /^(?:(\w+):)?.+?(?:\.(\w+))?$/.exec(URI); - switch ( schema[1] ) { - case 'data': return { - schema: 'data', type: DataURI.exec( URI )[2] - }; - case 'blob': return exports.blobOf( URI ).then(function (blob) { + switch (schema[1]) { + case "data": return { - schema: 'blob', type: blob.type + schema: "data", + type: DataURI.exec(URI)[2] + }; + case "blob": + return exports.blobOf(URI).then(function(blob) { + return { + schema: "blob", + type: blob.type + }; + }); + default: + return { + schema: schema[1], + type: schema[2] }; - }); - default: return { - schema: schema[1], type: schema[2] - }; } }; -var BlobBuilder = window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder; +var BlobBuilder = + window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder; exports.toBlob = function toBlob(dataURI) { - dataURI = DataURI.exec( dataURI ); + dataURI = DataURI.exec(dataURI); - var type = dataURI[1], base64 = dataURI[3], data = dataURI[4]; - data = base64 ? window.atob( data ) : data; + var type = dataURI[1], + base64 = dataURI[3], + data = dataURI[4]; + data = base64 ? window.atob(data) : data; - var aBuffer = new ArrayBuffer( data.length ); - var uBuffer = new Uint8Array( aBuffer ); + var aBuffer = new ArrayBuffer(data.length); + var uBuffer = new Uint8Array(aBuffer); - for (var i = 0; data[i]; i++) uBuffer[i] = data.charCodeAt( i ); + for (var i = 0; data[i]; i++) uBuffer[i] = data.charCodeAt(i); - if (! BlobBuilder) - return new window.Blob([aBuffer], {type: type}); + if (!BlobBuilder) return new window.Blob([aBuffer], { type: type }); var builder = new BlobBuilder(); - builder.append( aBuffer ); - return builder.getBlob( type ); + builder.append(aBuffer); + return builder.getBlob(type); }; diff --git a/src/core/version.js b/src/core/version.js index 6fd7b854..37237904 100644 --- a/src/core/version.js +++ b/src/core/version.js @@ -1 +1 @@ -module.exports = "0.8.0"; \ No newline at end of file +module.exports = "0.8.0"; diff --git a/src/debug/debug-layer.js b/src/debug/debug-layer.js index 6c31c3a2..9166ca4c 100644 --- a/src/debug/debug-layer.js +++ b/src/debug/debug-layer.js @@ -1,11 +1,11 @@ -var Crafty = require('../core/core.js'), +var Crafty = require("../core/core.js"), document = window.document; /**@ * #DebugCanvas * @category Debug * @kind Component - * + * * @trigger DebugDraw - when the entity is ready to be drawn to the stage * @trigger NoCanvas - if the browser does not support canvas * @@ -20,10 +20,9 @@ var Crafty = require('../core/core.js'), * @see DebugPolygon, DebugRectangle */ Crafty.c("DebugCanvas", { - init: function () { + init: function() { this.requires("2D"); - if (!Crafty.DebugCanvas.context) - Crafty.DebugCanvas.init(); + if (!Crafty.DebugCanvas.context) Crafty.DebugCanvas.init(); Crafty.DebugCanvas.add(this); this._debug = { alpha: 1.0, @@ -34,14 +33,14 @@ Crafty.c("DebugCanvas", { }, // When component is removed - onDebugRemove: function (id) { + onDebugRemove: function(id) { if (id === "DebugCanvas") { Crafty.DebugCanvas.remove(this); } }, //When entity is destroyed - onDebugDestroy: function (id) { + onDebugDestroy: function(id) { Crafty.DebugCanvas.remove(this); }, @@ -49,11 +48,11 @@ Crafty.c("DebugCanvas", { * #.debugAlpha * @comp DebugCanvas * @kind Method - * + * * @sign public .debugAlpha(Number alpha) * @param alpha - The alpha level the component will be drawn with */ - debugAlpha: function (alpha) { + debugAlpha: function(alpha) { this._debug.alpha = alpha; return this; }, @@ -62,7 +61,7 @@ Crafty.c("DebugCanvas", { * #.debugFill * @comp DebugCanvas * @kind Method - * + * * @sign public .debugFill([String fillStyle]) * @param fillStyle - The color the component will be filled with. Defaults to "red". Pass the boolean false to turn off filling. * @example @@ -70,9 +69,8 @@ Crafty.c("DebugCanvas", { * var myEntity = Crafty.e("2D, Collision, SolidHitBox ").debugFill("purple") * ~~~ */ - debugFill: function (fillStyle) { - if (typeof fillStyle === 'undefined') - fillStyle = "red"; + debugFill: function(fillStyle) { + if (typeof fillStyle === "undefined") fillStyle = "red"; this._debug.fillStyle = fillStyle; return this; }, @@ -81,7 +79,7 @@ Crafty.c("DebugCanvas", { * #.debugStroke * @comp DebugCanvas * @kind Method - * + * * @sign public .debugStroke([String strokeStyle]) * @param strokeStyle - The color the component will be outlined with. Defaults to "red". Pass the boolean false to turn this off. * @example @@ -89,40 +87,30 @@ Crafty.c("DebugCanvas", { * var myEntity = Crafty.e("2D, Collision, WiredHitBox ").debugStroke("white") * ~~~ */ - debugStroke: function (strokeStyle) { - if (typeof strokeStyle === 'undefined') - strokeStyle = "red"; + debugStroke: function(strokeStyle) { + if (typeof strokeStyle === "undefined") strokeStyle = "red"; this._debug.strokeStyle = strokeStyle; return this; }, - debugDraw: function (ctx) { + debugDraw: function(ctx) { var ga = ctx.globalAlpha; var props = this._debug; - if (props.alpha) - ctx.globalAlpha = this._debug.alpha; + if (props.alpha) ctx.globalAlpha = this._debug.alpha; - if (props.strokeStyle) - ctx.strokeStyle = props.strokeStyle; + if (props.strokeStyle) ctx.strokeStyle = props.strokeStyle; - if (props.lineWidth) - ctx.lineWidth = props.lineWidth; + if (props.lineWidth) ctx.lineWidth = props.lineWidth; - if (props.fillStyle) - ctx.fillStyle = props.fillStyle; + if (props.fillStyle) ctx.fillStyle = props.fillStyle; this.trigger("DebugDraw", ctx); ctx.globalAlpha = ga; - } - - }); - - /**@ * #DebugRectangle * @category Debug @@ -140,7 +128,7 @@ Crafty.c("DebugCanvas", { * @see DebugCanvas */ Crafty.c("DebugRectangle", { - init: function () { + init: function() { this.requires("2D, DebugCanvas"); }, @@ -148,40 +136,32 @@ Crafty.c("DebugRectangle", { * #.debugRectangle * @comp DebugRectangle * @kind Method - * + * * @sign public .debugRectangle(Object rect) * @param rect - an object with _x, _y, _w, and _h to draw * * Sets the rectangle that this component draws to the debug canvas. * */ - debugRectangle: function (rect) { + debugRectangle: function(rect) { this.debugRect = rect; this.unbind("DebugDraw", this.drawDebugRect); this.bind("DebugDraw", this.drawDebugRect); return this; - }, - drawDebugRect: function (ctx) { - + drawDebugRect: function(ctx) { var rect = this.debugRect; - if (rect === null || rect === undefined) - return; + if (rect === null || rect === undefined) return; if (rect._h && rect._w) { if (this._debug.fillStyle) ctx.fillRect(rect._x, rect._y, rect._w, rect._h); if (this._debug.strokeStyle) ctx.strokeRect(rect._x, rect._y, rect._w, rect._h); } - } - - - }); - /**@ * #WiredMBR * @category Debug @@ -194,20 +174,18 @@ Crafty.c("DebugRectangle", { * @see 2D, DebugRectangle, DebugCanvas */ Crafty.c("WiredMBR", { - init: function () { - this.requires("DebugRectangle") - .debugStroke("purple"); + init: function() { + this.requires("DebugRectangle").debugStroke("purple"); }, events: { - "PreRender": function () { + PreRender: function() { // Internal method for updating the MBR drawn. this.debugRectangle(this._mbr || this); } } }); - /**@ * #SolidMBR * @category Debug @@ -220,13 +198,12 @@ Crafty.c("WiredMBR", { * @see 2D, DebugRectangle, DebugCanvas */ var solidMBR = { - init: function () { - this.requires("DebugRectangle") - .debugFill("pink"); + init: function() { + this.requires("DebugRectangle").debugFill("pink"); }, events: { - "PreRender": function () { + PreRender: function() { // Internal method for updating the MBR drawn. this.debugRectangle(this._mbr || this); } @@ -236,7 +213,6 @@ Crafty.c("SolidMBR", solidMBR); // DEPRECATED: remove this in an upcoming release Crafty.c("VisibleMBR", solidMBR); - /**@ * #DebugPolygon * @category Debug @@ -249,48 +225,44 @@ Crafty.c("VisibleMBR", solidMBR); * @see DebugCanvas */ Crafty.c("DebugPolygon", { - init: function () { + init: function() { this.requires("2D, DebugCanvas"); }, - /**@ * #.debugPolygon * @comp DebugPolygon * @kind Method - * + * * @sign public .debugPolygon(Polygon poly) * @param poly - a polygon to render * * Sets the polygon that this component renders to the debug canvas. * */ - debugPolygon: function (poly) { + debugPolygon: function(poly) { this.polygon = poly; this.unbind("DebugDraw", this.drawDebugPolygon); this.bind("DebugDraw", this.drawDebugPolygon); return this; }, - drawDebugPolygon: function (ctx) { - if (typeof this.polygon === "undefined") - return; + drawDebugPolygon: function(ctx) { + if (typeof this.polygon === "undefined") return; ctx.beginPath(); - var p = this.polygon.points, l = p.length; - for (var i=0; i= 0; i--) - if (list[i] === ent) - list.splice(i, 1); - + 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 () { + init: function() { if (!Crafty.DebugCanvas.context) { //check if canvas is supported if (!Crafty.support.canvas) { @@ -414,7 +386,7 @@ Crafty.DebugCanvas = { c = document.createElement("canvas"); c.width = Crafty.viewport.width; c.height = Crafty.viewport.height; - c.style.position = 'absolute'; + c.style.position = "absolute"; c.style.left = "0px"; c.style.top = "0px"; c.id = "debug-canvas"; @@ -422,21 +394,16 @@ Crafty.DebugCanvas = { c.style.zIndex = 100000; Crafty.stage.elem.appendChild(c); - Crafty.DebugCanvas.context = c.getContext('2d'); + Crafty.DebugCanvas.context = c.getContext("2d"); Crafty.DebugCanvas._canvas = c; - - - } //Bind rendering of canvas context (see drawing.js) Crafty.unbind("RenderScene", Crafty.DebugCanvas.renderScene); Crafty.bind("RenderScene", Crafty.DebugCanvas.renderScene); - }, - // copied from drawAll() - renderScene: function (rect) { + renderScene: function(rect) { rect = rect || Crafty.viewport.rect(); var q = Crafty.DebugCanvas.entities, i = 0, @@ -445,7 +412,14 @@ Crafty.DebugCanvas = { current; var view = Crafty.viewport; - ctx.setTransform(view._scale, 0, 0, view._scale, Math.round(view._x*view._scale), Math.round(view._y*view._scale)); + ctx.setTransform( + view._scale, + 0, + 0, + view._scale, + Math.round(view._x * view._scale), + Math.round(view._y * view._scale) + ); ctx.clearRect(rect._x, rect._y, rect._w, rect._h); @@ -455,19 +429,31 @@ Crafty.DebugCanvas = { // If necessary, update the view transform to match the current entities layer // If the current entity has no layer, switch back to the viewport's transform - if (lastLayer !== current._drawLayer){ + if (lastLayer !== current._drawLayer) { if (current._drawLayer) { view = current._drawLayer._viewportRect(); - ctx.setTransform(view._scale, 0, 0, view._scale, Math.round(-view._x*view._scale), Math.round(-view._y*view._scale)); + ctx.setTransform( + view._scale, + 0, + 0, + view._scale, + Math.round(-view._x * view._scale), + Math.round(-view._y * view._scale) + ); } else { view = Crafty.viewport; - ctx.setTransform(view._scale, 0, 0, view._scale, Math.round(view._x*view._scale), Math.round(view._y*view._scale)); + ctx.setTransform( + view._scale, + 0, + 0, + view._scale, + Math.round(view._x * view._scale), + Math.round(view._y * view._scale) + ); } lastLayer = current._drawLayer; } current.debugDraw(ctx); } - } - }; diff --git a/src/debug/logging.js b/src/debug/logging.js index da9d3f9d..e8e651eb 100644 --- a/src/debug/logging.js +++ b/src/debug/logging.js @@ -1,5 +1,4 @@ -var Crafty = require('../core/core.js'); - +var Crafty = require("../core/core.js"); /**@ * #Crafty.log @@ -24,17 +23,25 @@ var Crafty = require('../core/core.js'); * It is recommended to use `Crafty.error`, as `console.error` can crash on IE9. */ Crafty.extend({ - // Allow logging to be disabled - loggingEnabled: true, - // In some cases console.log doesn't exist, so provide a wrapper for it - log: function() { - if (Crafty.loggingEnabled && (typeof window !== "undefined" ? window.console : console) && console.log) { - console.log.apply(console, arguments); - } - }, - error: function() { - if (Crafty.loggingEnabled && (typeof window !== "undefined" ? window.console : console) && console.error) { - console.error.apply(console, arguments); - } - } -}); \ No newline at end of file + // Allow logging to be disabled + loggingEnabled: true, + // In some cases console.log doesn't exist, so provide a wrapper for it + log: function() { + if ( + Crafty.loggingEnabled && + (typeof window !== "undefined" ? window.console : console) && + console.log + ) { + console.log.apply(console, arguments); + } + }, + error: function() { + if ( + Crafty.loggingEnabled && + (typeof window !== "undefined" ? window.console : console) && + console.error + ) { + console.error.apply(console, arguments); + } + } +}); diff --git a/src/graphics/canvas-layer.js b/src/graphics/canvas-layer.js index adcbe6ca..2ba812e6 100644 --- a/src/graphics/canvas-layer.js +++ b/src/graphics/canvas-layer.js @@ -1,4 +1,4 @@ -var Crafty = require('../core/core.js'); +var Crafty = require("../core/core.js"); /**@ * #CanvasLayer @@ -11,7 +11,7 @@ var Crafty = require('../core/core.js'); */ Crafty._registerLayerTemplate("Canvas", { type: "Canvas", - + layerCount: 0, _changedObjs: null, @@ -22,13 +22,12 @@ Crafty._registerLayerTemplate("Canvas", { __tempSearchRect: null, __tempScreenRect: null, - /**@ * #.dirty * @comp CanvasLayer * @kind Method * @private - * + * * @sign public .dirty(ent) * @param ent - The entity to add * @@ -37,13 +36,13 @@ Crafty._registerLayerTemplate("Canvas", { dirty: function dirty(ent) { this._changedObjs.push(ent); }, - + /**@ * #.attach * @comp CanvasLayer * @kind Method * @private - * + * * @sign public .attach(ent) * @param ent - The entity to add * @@ -54,13 +53,13 @@ Crafty._registerLayerTemplate("Canvas", { //increment the number of canvas objs this.layerCount++; }, - + /**@ * #.detach * @comp CanvasLayer * @kind Method * @private - * + * * @sign public .detach(ent) * @param ent - The entity to detach * @@ -72,7 +71,6 @@ Crafty._registerLayerTemplate("Canvas", { //decrement the number of canvas objs this.layerCount--; }, - /**@ * #.context @@ -91,23 +89,23 @@ Crafty._registerLayerTemplate("Canvas", { * * The canvas element associated with the canvas layer. */ - _canvas: null, + _canvas: null, events: { // Respond to init & remove events - "LayerInit": "layerInit", - "LayerRemove": "layerRemove", + LayerInit: "layerInit", + LayerRemove: "layerRemove", // Bind scene rendering (see drawing.js) - "RenderScene": "_render", + RenderScene: "_render", // Listen for pixelart changes - "PixelartSet": "_setPixelart", + PixelartSet: "_setPixelart", // Handle viewport modifications - "ViewportResize": "_resize" + ViewportResize: "_resize" }, // When the system is first created, create the necessary canvas element and initial state // Bind to the necessary events - layerInit: function () { + layerInit: function() { //check if canvas is supported if (!Crafty.support.canvas) { Crafty.trigger("NoCanvas"); @@ -129,19 +127,18 @@ Crafty._registerLayerTemplate("Canvas", { c = document.createElement("canvas"); c.width = Crafty.viewport.width; c.height = Crafty.viewport.height; - c.style.position = 'absolute'; + c.style.position = "absolute"; c.style.left = "0px"; c.style.top = "0px"; c.style.zIndex = this.options.z; Crafty.stage.elem.appendChild(c); - this.context = c.getContext('2d'); + this.context = c.getContext("2d"); this._canvas = c; //Set any existing transformations var zoom = Crafty.viewport._scale; - if (zoom !== 1) - this.context.scale(zoom, zoom); + if (zoom !== 1) this.context.scale(zoom, zoom); }, // When the system is destroyed, remove related resources @@ -157,21 +154,28 @@ Crafty._registerLayerTemplate("Canvas", { return; } - // Set the camera transforms from the combination of the current viewport parameters and this layers + // Set the camera transforms from the combination of the current viewport parameters and this layers var cameraOptions = this.options; if (dirtyViewport && cameraOptions) { var view = this._viewportRect(); - var scale = view._scale; + var scale = view._scale; var dx = -view._x * scale; var dy = -view._y * scale; - ctx.setTransform(scale, 0, 0, scale, Math.round(dx), Math.round(dy) ); + ctx.setTransform( + scale, + 0, + 0, + scale, + Math.round(dx), + Math.round(dy) + ); } // TODO: check if these conditions really make that much sense! // if the amount of changed objects is over 60% of the total objects, do the naive method redrawing if (l / this.layerCount > 0.6 || dirtyViewport) { this._drawAll(); - // otherwise draw dirty cell grid regions + // otherwise draw dirty cell grid regions } else { this._drawDirtyCells(); } @@ -199,7 +203,7 @@ Crafty._registerLayerTemplate("Canvas", { * * @see Canvas#.draw */ - _drawDirtyCells: function (view) { + _drawDirtyCells: function(view) { var viewportRect = this._viewportRect(), // this updates the viewportRect for later cached use rect = this.__tempRect, dirtyRects = this._dirtyRects, @@ -217,7 +221,8 @@ Crafty._registerLayerTemplate("Canvas", { this._createDirtyRects(); // For each dirty rectangle, find entities near it, and draw the overlapping ones - for (i = 0, l = dirtyRects.length; i < l; i += 4) { //loop over every dirty rect + for (i = 0, l = dirtyRects.length; i < l; i += 4) { + //loop over every dirty rect rect._x = dirtyRects[i + 0]; rect._y = dirtyRects[i + 1]; rect._w = dirtyRects[i + 2]; @@ -235,7 +240,12 @@ Crafty._registerLayerTemplate("Canvas", { b = (6 * frame + 170) % 255; ctx.strokeStyle = "rgb(" + r + ", " + g + ", " + b + ")"; for (i = 0, l = dirtyRects.length; i < l; i += 4) { - ctx.strokeRect(dirtyRects[i + 0], dirtyRects[i + 1], dirtyRects[i + 2], dirtyRects[i + 3]); + ctx.strokeRect( + dirtyRects[i + 0], + dirtyRects[i + 1], + dirtyRects[i + 2], + dirtyRects[i + 3] + ); } } }, @@ -245,7 +255,7 @@ Crafty._registerLayerTemplate("Canvas", { * @comp CanvasLayer * @kind Method * @private - * + * * @sign public ._drawAll([Object rect]) * @param rect - a rectangular region {_x: x_val, _y: y_val, _w: w_val, _h: h_val} * @@ -258,7 +268,7 @@ Crafty._registerLayerTemplate("Canvas", { * * @see Canvas#.draw */ - _drawAll: function (view) { + _drawAll: function(view) { var viewportRect = this._viewportRect(); // this updates the viewportRect for later cached use // Draw the whole layer rectangle @@ -266,7 +276,11 @@ Crafty._registerLayerTemplate("Canvas", { }, _drawRect: function(rect) { - var i, l, q, obj, previousGlobalZ, + var i, + l, + q, + obj, + previousGlobalZ, integerBounds = Crafty.rectManager.integerBounds, ctx = this.context, searchRect = this.__tempSearchRect, @@ -297,7 +311,6 @@ Crafty._registerLayerTemplate("Canvas", { // Sort objects by z level, duplicate objs will be ordered next to each other due to same _globalZ q.sort(this._sort); - // save context before drawing, saves e.g. infinite clip region ctx.save(); @@ -310,7 +323,12 @@ Crafty._registerLayerTemplate("Canvas", { ctx.beginPath(); ctx.rect(screenRect._x, screenRect._y, screenRect._w, screenRect._h); // Clear the rect from the main canvas - ctx.clearRect(screenRect._x, screenRect._y, screenRect._w, screenRect._h); + ctx.clearRect( + screenRect._x, + screenRect._y, + screenRect._w, + screenRect._h + ); ctx.restore(); ctx.clip(); @@ -321,7 +339,11 @@ Crafty._registerLayerTemplate("Canvas", { for (i = 0, l = q.length; i < l; ++i) { obj = q[i]; - if (obj._globalZ > previousGlobalZ && obj._visible && obj._drawLayer === this) { + if ( + obj._globalZ > previousGlobalZ && + obj._visible && + obj._drawLayer === this + ) { obj.draw(ctx); obj._changed = false; @@ -338,7 +360,7 @@ Crafty._registerLayerTemplate("Canvas", { }, /** cleans up current dirty state, stores stale state for future passes */ - _clean: function () { + _clean: function() { var dirtyKeys, staleKeys, obj, i, l; var changed = this._changedObjs; @@ -349,7 +371,8 @@ Crafty._registerLayerTemplate("Canvas", { // track stale grid cell keys for dirty grid cell drawing dirtyKeys = obj._entry.keys; // cached computation of Crafty.HashMap.key(obj) staleKeys = obj.staleKeys; - if (staleKeys === undefined) obj.staleKeys = staleKeys = { x1: 0, y1: 0, x2: 0, y2: 0 }; + if (staleKeys === undefined) + obj.staleKeys = staleKeys = { x1: 0, y1: 0, x2: 0, y2: 0 }; staleKeys.x1 = dirtyKeys.x1; staleKeys.y1 = dirtyKeys.y1; staleKeys.x2 = dirtyKeys.x2; @@ -371,7 +394,7 @@ Crafty._registerLayerTemplate("Canvas", { // If a dirty cell doesn't overlap with the area to be drawn (e.g. viewport), // don't include it // - _createDirtyCells: function (view) { + _createDirtyCells: function(view) { var changed = this._changedObjs, dirtyCells = this._dirtyCells; var viewKeys = Crafty.HashMap.key(view, this._viewKeys); @@ -381,13 +404,17 @@ Crafty._registerLayerTemplate("Canvas", { obj = changed[i]; // if object was previously drawn it's old position needs to be redrawn (cleared) - if ((keys = obj.staleKeys)) { // cached computation of stale keys + if ((keys = obj.staleKeys)) { + // cached computation of stale keys for (j = keys.x1; j <= keys.x2; j++) { for (k = keys.y1; k <= keys.y2; k++) { // if stale cell is inside area to be drawn - if (viewKeys.x1 <= j && j <= viewKeys.x2 && - viewKeys.y1 <= k && k <= viewKeys.y2) { - + if ( + viewKeys.x1 <= j && + j <= viewKeys.x2 && + viewKeys.y1 <= k && + k <= viewKeys.y2 + ) { // combine two 16 bit unsigned numbers into a unique 32 bit unsigned number dirtyCells[(j << 16) ^ k] = true; } @@ -399,9 +426,12 @@ Crafty._registerLayerTemplate("Canvas", { for (j = keys.x1; j <= keys.x2; j++) { for (k = keys.y1; k <= keys.y2; k++) { // if dirty cell is inside area to be drawn - if (viewKeys.x1 <= j && j <= viewKeys.x2 && - viewKeys.y1 <= k && k <= viewKeys.y2) { - + if ( + viewKeys.x1 <= j && + j <= viewKeys.x2 && + viewKeys.y1 <= k && + k <= viewKeys.y2 + ) { // combine two 16 bit unsigned numbers into a unique 32 bit unsigned number dirtyCells[(j << 16) ^ k] = true; } @@ -423,18 +453,16 @@ Crafty._registerLayerTemplate("Canvas", { hash = +strHash; // deconstruct a 32 bit unsigned number into a unique pair of 16 bit unsigned numbers k = (hash << 16) >> 16; - j = (k < 0) ? ~(hash >> 16) : hash >> 16; + j = k < 0 ? ~(hash >> 16) : hash >> 16; dirtyRects.push(j * cellsize, k * cellsize, cellsize, cellsize); } }, - // Resize the canvas element to the current viewport _resize: function() { var c = this._canvas; c.width = Crafty.viewport.width; c.height = Crafty.viewport.height; - }, _setPixelart: function(enabled) { @@ -445,5 +473,4 @@ Crafty._registerLayerTemplate("Canvas", { context.oImageSmoothingEnabled = !enabled; context.msImageSmoothingEnabled = !enabled; } - }); diff --git a/src/graphics/canvas.js b/src/graphics/canvas.js index 069f8025..07da14f7 100644 --- a/src/graphics/canvas.js +++ b/src/graphics/canvas.js @@ -1,11 +1,10 @@ -var Crafty = require('../core/core.js'); - +var Crafty = require("../core/core.js"); /**@ * #Canvas * @category Graphics * @kind Component - * + * * @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 * @@ -21,18 +20,16 @@ var Crafty = require('../core/core.js'); *~~~ */ Crafty.c("Canvas", { - - init: function () { + init: function() { this.requires("Renderable"); - + //Allocate an object to hold this components current region this.currentRect = {}; - + // Add the default canvas layer if we aren't attached to a custom one - if (!this._customLayer){ - this._attachToLayer( Crafty.s("DefaultCanvasLayer")); + if (!this._customLayer) { + this._attachToLayer(Crafty.s("DefaultCanvasLayer")); } - }, remove: function() { @@ -43,7 +40,7 @@ Crafty.c("Canvas", { * #.draw * @comp Canvas * @kind Method - * + * * @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 @@ -68,15 +65,14 @@ Crafty.c("Canvas", { } }, - draw: function (ctx, x, y, w, h) { + draw: function(ctx, x, y, w, h) { if (!this.ready) return; var pos = this.drawVars.pos; - pos._x = (this._x + (x || 0)); - pos._y = (this._y + (y || 0)); - pos._w = (w || this._w); - pos._h = (h || this._h); - + pos._x = this._x + (x || 0); + pos._y = this._y + (y || 0); + pos._w = w || this._w; + pos._h = h || this._h; var context = ctx || this._drawContext; var coord = this.__coord || [0, 0, 0, 0]; @@ -93,7 +89,10 @@ Crafty.c("Canvas", { // rotate the context about this entity's origin if (this._rotation !== 0) { - context.translate(this._origin.x + this._x, this._origin.y + this._y); + context.translate( + this._origin.x + this._x, + this._origin.y + this._y + ); pos._x = -this._origin.x; pos._y = -this._origin.y; context.rotate((this._rotation % 360) * (Math.PI / 180)); @@ -101,7 +100,7 @@ Crafty.c("Canvas", { // We realize a flipped entity by scaling the context in the opposite direction, then adjusting the position coordinates to match if (this._flipX || this._flipY) { - context.scale((this._flipX ? -1 : 1), (this._flipY ? -1 : 1)); + context.scale(this._flipX ? -1 : 1, this._flipY ? -1 : 1); if (this._flipX) { pos._x = -(pos._x + pos._w); } diff --git a/src/graphics/color.js b/src/graphics/color.js index 923efcbf..9fbb350a 100644 --- a/src/graphics/color.js +++ b/src/graphics/color.js @@ -1,16 +1,13 @@ -var Crafty = require('../core/core.js'), +var Crafty = require("../core/core.js"), document = window.document; - - - /**@ * #Crafty.assignColor * @category Graphics * @kind Method - * - * Maps a wide vareity of color representations to a set of simple rgb(a) properties. - * + * + * Maps a wide vareity of color representations to a set of simple rgb(a) properties. + * * @sign Crafty.assignColor(color[, assignee]) * @param color - a string represenation of the color to assign, in any valid HTML format * @param assignee - an object to use instead of creating one from scratch @@ -19,51 +16,51 @@ var Crafty = require('../core/core.js'), * If the assignee parameter is passed, that object will be assigned those values and returned. */ Crafty.extend({ - assignColor: (function(){ - + assignColor: (function() { // Create phantom element to assess color var element = document.createElement("div"); element.style.display = "none"; // Can't attach it til later on, so we need a flag! var element_attached = false; var dictionary = { - "aqua": "#00ffff", - "black": "#000000", - "blue": "#0000ff", - "fuchsia": "#ff00ff", - "gray": "#808080", - "green": "#00ff00", - "lime": "#00ff00", - "maroon": "#800000", - "navy": "#000080", - "olive": "#808000", - "orange": "#ffa500", - "purple": "#800080", - "red": "#ff0000", - "silver": "#c0c0c0", - "teal": "#008080", - "white": "#ffffff", - "yellow": "#ffff00" + aqua: "#00ffff", + black: "#000000", + blue: "#0000ff", + fuchsia: "#ff00ff", + gray: "#808080", + green: "#00ff00", + lime: "#00ff00", + maroon: "#800000", + navy: "#000080", + olive: "#808000", + orange: "#ffa500", + purple: "#800080", + red: "#ff0000", + silver: "#c0c0c0", + teal: "#008080", + white: "#ffffff", + yellow: "#ffff00" }; - function default_value(c){ + function default_value(c) { c._red = c._blue = c._green = 0; return c; } function hexComponent(component) { var hex = component.toString(16); - if (hex.length === 1) - hex = "0" + hex; + if (hex.length === 1) hex = "0" + hex; return hex; } - function rgbToHex(r, g, b){ + function rgbToHex(r, g, b) { return "#" + hexComponent(r) + hexComponent(g) + hexComponent(b); } function parseHexString(hex, c) { - var r, g, b, + var r, + g, + b, l = hex.length; if (l === 7) { @@ -71,9 +68,12 @@ Crafty.extend({ g = hex.substr(3, 2); b = hex.substr(5, 2); } else if (l === 4) { - r = hex.substr(1, 1); r += r; - g = hex.substr(2, 1); g += g; - b = hex.substr(3, 1); b += b; + r = hex.substr(1, 1); + r += r; + g = hex.substr(2, 1); + g += g; + b = hex.substr(3, 1); + b += b; } else { return default_value(c); } @@ -88,7 +88,10 @@ Crafty.extend({ function parseRgbString(rgb, c) { var values = rgb_regex.exec(rgb); - if( values === null || (values.length !== 4 && values.length !== 5)) { + if ( + values === null || + (values.length !== 4 && values.length !== 5) + ) { return default_value(c); // return bad result? } c._red = Math.round(parseFloat(values[1])); @@ -100,9 +103,9 @@ Crafty.extend({ return c; } - function parseColorName(key, c){ - if (typeof dictionary[key] === "undefined"){ - if (element_attached === false){ + function parseColorName(key, c) { + if (typeof dictionary[key] === "undefined") { + if (element_attached === false) { window.document.body.appendChild(element); element_attached = true; } @@ -117,18 +120,32 @@ Crafty.extend({ return c; } - function rgbaString(c){ - return "rgba(" + c._red + ", " + c._green + ", " + c._blue + ", " + c._strength + ")"; + function rgbaString(c) { + return ( + "rgba(" + + c._red + + ", " + + c._green + + ", " + + c._blue + + ", " + + c._strength + + ")" + ); } // The actual assignColor function - return function(color, c){ + return function(color, c) { c = c || {}; color = color.trim().toLowerCase(); var ret = null; - if (color[0] === '#'){ + if (color[0] === "#") { ret = parseHexString(color, c); - } else if (color[0] === 'r' && color[1] === 'g' && color[2] === 'b'){ + } else if ( + color[0] === "r" && + color[1] === "g" && + color[2] === "b" + ) { ret = parseRgbString(color, c); } else { ret = parseColorName(color, c); @@ -136,41 +153,40 @@ Crafty.extend({ c._strength = c._strength || 1.0; c._color = rgbaString(c); }; - })() }); - - - - // Define some variables required for webgl -var fs = require('fs'); +var fs = require("fs"); -Crafty.defaultShader("Color", new Crafty.WebGLShader( - fs.readFileSync(__dirname + '/shaders/color.vert', 'utf8'), - fs.readFileSync(__dirname + '/shaders/color.frag', 'utf8'), - [ - { name: "aPosition", width: 2 }, - { name: "aOrientation", width: 3 }, - { name: "aLayer", width: 2 }, - { name: "aColor", width: 4 } - ], - function(e, entity) { - e.program.writeVector("aColor", - entity._red/255, - entity._green/255, - entity._blue/255, - entity._strength - ); - } -)); +Crafty.defaultShader( + "Color", + new Crafty.WebGLShader( + fs.readFileSync(__dirname + "/shaders/color.vert", "utf8"), + fs.readFileSync(__dirname + "/shaders/color.frag", "utf8"), + [ + { name: "aPosition", width: 2 }, + { name: "aOrientation", width: 3 }, + { name: "aLayer", width: 2 }, + { name: "aColor", width: 4 } + ], + function(e, entity) { + e.program.writeVector( + "aColor", + entity._red / 255, + entity._green / 255, + entity._blue / 255, + entity._strength + ); + } + ) +); /**@ * #Color * @category Graphics * @kind Component - * + * * Draw a colored rectangle. */ Crafty.c("Color", { @@ -181,10 +197,10 @@ Crafty.c("Color", { _color: "", ready: true, - init: function () { + init: function() { // Necessary for some rendering layers this.__coord = this.__coord || [0, 0, 0, 0]; - + this.bind("Draw", this._drawColor); if (this._drawLayer) { this._setupColor(this._drawLayer); @@ -193,12 +209,12 @@ Crafty.c("Color", { }, events: { - "LayerAttached": "_setupColor" + LayerAttached: "_setupColor" }, - remove: function(){ + remove: function() { this.unbind("Draw", this._drawColor); - if (this.has("DOM")){ + if (this.has("DOM")) { this._element.style.backgroundColor = "transparent"; } this.trigger("Invalidate"); @@ -211,15 +227,17 @@ Crafty.c("Color", { }, // draw function for "Color" - _drawColor: function(e){ - if (!this._color) { return; } + _drawColor: function(e) { + if (!this._color) { + return; + } if (e.type === "DOM") { e.style.backgroundColor = this._color; e.style.lineHeight = 0; } else if (e.type === "canvas") { e.ctx.fillStyle = this._color; e.ctx.fillRect(e.pos._x, e.pos._y, e.pos._w, e.pos._h); - } else if (e.type === "webgl"){ + } else if (e.type === "webgl") { e.program.draw(e, this); } }, @@ -228,7 +246,7 @@ Crafty.c("Color", { * #.color * @comp Color * @kind Method - * + * * @trigger Invalidate - when the color changes * * Will assign the color and opacity, either through a string shorthand, or through explicit rgb values. @@ -261,26 +279,32 @@ Crafty.c("Color", { * ``` * Two ways of assigning a transparent green color. */ - color: function (color) { - if (arguments.length === 0 ){ + color: function(color) { + if (arguments.length === 0) { return this._color; - } else if (arguments.length>=3){ + } else if (arguments.length >= 3) { this._red = arguments[0]; this._green = arguments[1]; this._blue = arguments[2]; - if (typeof arguments[3] === "number") - this._strength = arguments[3]; + if (typeof arguments[3] === "number") this._strength = arguments[3]; } else { // First argument is color name Crafty.assignColor(color, this); // Second argument, if present, is strength of color // Note that assignColor will give a default strength of 1.0 if none exists. - if (typeof arguments[1] === "number") - this._strength = arguments[1]; + if (typeof arguments[1] === "number") this._strength = arguments[1]; } - this._color = "rgba(" + this._red + ", " + this._green + ", " + this._blue + ", " + this._strength + ")"; + this._color = + "rgba(" + + this._red + + ", " + + this._green + + ", " + + this._blue + + ", " + + this._strength + + ")"; this.trigger("Invalidate"); return this; } }); - diff --git a/src/graphics/dom-helper.js b/src/graphics/dom-helper.js index 19df04f3..60a4f7b1 100644 --- a/src/graphics/dom-helper.js +++ b/src/graphics/dom-helper.js @@ -1,4 +1,4 @@ -var Crafty = require('../core/core.js'), +var Crafty = require("../core/core.js"), document = window.document; Crafty.extend({ @@ -21,14 +21,31 @@ Crafty.extend({ * Find a DOM elements position including * padding and border. */ - innerPosition: function (obj, out) { + innerPosition: function(obj, out) { out = out || {}; var rect = obj.getBoundingClientRect(), - x = rect.left + (window.pageXOffset ? window.pageXOffset : document.body.scrollLeft), - y = rect.top + (window.pageYOffset ? window.pageYOffset : document.body.scrollTop), + x = + rect.left + + (window.pageXOffset + ? window.pageXOffset + : document.body.scrollLeft), + y = + rect.top + + (window.pageYOffset + ? window.pageYOffset + : document.body.scrollTop), //border left - borderX = parseInt(this.getStyle(obj, 'border-left-width') || 0, 10) || parseInt(this.getStyle(obj, 'borderLeftWidth') || 0, 10) || 0, - borderY = parseInt(this.getStyle(obj, 'border-top-width') || 0, 10) || parseInt(this.getStyle(obj, 'borderTopWidth') || 0, 10) || 0; + borderX = + parseInt( + this.getStyle(obj, "border-left-width") || 0, + 10 + ) || + parseInt(this.getStyle(obj, "borderLeftWidth") || 0, 10) || + 0, + borderY = + parseInt(this.getStyle(obj, "border-top-width") || 0, 10) || + parseInt(this.getStyle(obj, "borderTopWidth") || 0, 10) || + 0; out.x = x + borderX; out.y = y + borderY; @@ -39,7 +56,7 @@ Crafty.extend({ * #Crafty.domHelper.getStyle * @comp Crafty.domHelper * @kind Method - * + * * @sign public Object Crafty.domHelper.getStyle(HTMLElement obj, String property) * @param obj - HTML element to find the style * @param property - Style to return @@ -47,12 +64,14 @@ Crafty.extend({ * Determine the value of a style on an HTML element. Notation can be * in either CSS or JS. */ - getStyle: function (obj, prop) { + getStyle: function(obj, prop) { var result; if (obj.currentStyle) result = obj.currentStyle[this.camelize(prop)]; else if (window.getComputedStyle) - result = document.defaultView.getComputedStyle(obj, null).getPropertyValue(this.csselize(prop)); + result = document.defaultView + .getComputedStyle(obj, null) + .getPropertyValue(this.csselize(prop)); return result; }, @@ -61,18 +80,18 @@ Crafty.extend({ * * Converts CSS notation to JS notation */ - camelize: function (str) { - return str.replace(/-+(.)?/g, function (match, chr) { - return chr ? chr.toUpperCase() : ''; + camelize: function(str) { + return str.replace(/-+(.)?/g, function(match, chr) { + return chr ? chr.toUpperCase() : ""; }); }, /** * Converts JS notation to CSS notation */ - csselize: function (str) { - return str.replace(/[A-Z]/g, function (chr) { - return chr ? '-' + chr.toLowerCase() : ''; + csselize: function(str) { + return str.replace(/[A-Z]/g, function(chr) { + return chr ? "-" + chr.toLowerCase() : ""; }); }, @@ -80,22 +99,22 @@ Crafty.extend({ * #Crafty.domHelper.translate * @comp Crafty.domHelper * @kind Method - * + * * @sign public Object Crafty.domHelper.translate(Number clientX, Number clientY[, DrawLayer layer[, Object out]]) * @param clientX - clientX position in the browser screen * @param clientY - clientY position in the browser screen * @param layer - a Crafty draw layer * @param out - an optional object to save result in * @return Object `{x: ..., y: ...}` with Crafty coordinates. - * + * * The parameters clientX and clientY are pixel coordinates within the visible * browser window. This function translates those to Crafty coordinates (i.e., * the coordinates that you might apply to an entity), by taking into account * where the stage is within the screen, what the current viewport is, etc. - * + * * If a draw layer is specified, the returned object will take into account any special scaling rules for that object. */ - translate: function (clientX, clientY, layer, out) { + translate: function(clientX, clientY, layer, out) { out = out || {}; var doc = document.documentElement; var body = document.body; @@ -105,14 +124,42 @@ Crafty.extend({ // At some point this should be simplified, probably by altering the viewport to use the more intuitive coordinates if (layer) { view = layer._viewportRect(); - out.x = (clientX - Crafty.stage.x + (doc && doc.scrollLeft || body && body.scrollLeft || 0)) / view._scale + view._x; - out.y = (clientY - Crafty.stage.y + (doc && doc.scrollTop || body && body.scrollTop || 0)) / view._scale + view._y; + out.x = + (clientX - + Crafty.stage.x + + ((doc && doc.scrollLeft) || + (body && body.scrollLeft) || + 0)) / + view._scale + + view._x; + out.y = + (clientY - + Crafty.stage.y + + ((doc && doc.scrollTop) || + (body && body.scrollTop) || + 0)) / + view._scale + + view._y; } else { view = Crafty.viewport; - out.x = (clientX - Crafty.stage.x + (doc && doc.scrollLeft || body && body.scrollLeft || 0)) / view._scale - view._x; - out.y = (clientY - Crafty.stage.y + (doc && doc.scrollTop || body && body.scrollTop || 0)) / view._scale - view._y; + out.x = + (clientX - + Crafty.stage.x + + ((doc && doc.scrollLeft) || + (body && body.scrollLeft) || + 0)) / + view._scale - + view._x; + out.y = + (clientY - + Crafty.stage.y + + ((doc && doc.scrollTop) || + (body && body.scrollTop) || + 0)) / + view._scale - + view._y; } return out; } } -}); \ No newline at end of file +}); diff --git a/src/graphics/dom-layer.js b/src/graphics/dom-layer.js index 6382ef0e..2715771d 100644 --- a/src/graphics/dom-layer.js +++ b/src/graphics/dom-layer.js @@ -1,7 +1,6 @@ -var Crafty = require('../core/core.js'), +var Crafty = require("../core/core.js"), document = window.document; - /**@ * #DomLayer * @category Graphics @@ -19,30 +18,30 @@ Crafty._registerLayerTemplate("DOM", { * @comp DomLayer * @kind Property * @private - * + * * A div inside the `#cr-stage` div that holds all DOM entities. */ _div: null, events: { // Respond to init & remove events - "LayerInit": "layerInit", - "LayerRemove": "layerRemove", + LayerInit: "layerInit", + LayerRemove: "layerRemove", // Bind scene rendering (see drawing.js) - "RenderScene": "_render", + RenderScene: "_render", // Listen for pixelart changes - "PixelartSet": "_setPixelart" + PixelartSet: "_setPixelart" // Layers should generally listen for resize events, // but the DOM layers automatically inherit the stage's dimensions //"ViewportResize": "_resize" }, - layerInit: function () { + layerInit: function() { // Avoid shared state between systems this._changedObjs = []; // Create the div that will contain DOM elements - var div = this._div = document.createElement("div"); + var div = (this._div = document.createElement("div")); Crafty.stage.elem.appendChild(div); div.style.position = "absolute"; @@ -60,18 +59,24 @@ Crafty._registerLayerTemplate("DOM", { var style = this._div.style; var camelize = Crafty.domHelper.camelize; if (enabled) { - style[camelize("image-rendering")] = "optimizeSpeed"; /* legacy */ - style[camelize("image-rendering")] = "-moz-crisp-edges"; /* Firefox */ - style[camelize("image-rendering")] = "-o-crisp-edges"; /* Opera */ - style[camelize("image-rendering")] = "-webkit-optimize-contrast"; /* Webkit (Chrome & Safari) */ - style[camelize("-ms-interpolation-mode")] = "nearest-neighbor"; /* IE */ - style[camelize("image-rendering")] = "optimize-contrast"; /* CSS3 proposed */ - style[camelize("image-rendering")] = "pixelated"; /* CSS4 proposed */ - style[camelize("image-rendering")] = "crisp-edges"; /* CSS4 proposed */ + style[camelize("image-rendering")] = "optimizeSpeed"; /* legacy */ + style[camelize("image-rendering")] = + "-moz-crisp-edges"; /* Firefox */ + style[camelize("image-rendering")] = "-o-crisp-edges"; /* Opera */ + style[camelize("image-rendering")] = + "-webkit-optimize-contrast"; /* Webkit (Chrome & Safari) */ + style[camelize("-ms-interpolation-mode")] = + "nearest-neighbor"; /* IE */ + style[camelize("image-rendering")] = + "optimize-contrast"; /* CSS3 proposed */ + style[camelize("image-rendering")] = + "pixelated"; /* CSS4 proposed */ + style[camelize("image-rendering")] = + "crisp-edges"; /* CSS4 proposed */ } else { - style[camelize("image-rendering")] = "optimizeQuality"; /* legacy */ - style[camelize("-ms-interpolation-mode")] = "bicubic"; /* IE */ - style[camelize("image-rendering")] = "auto"; /* CSS3 */ + style[camelize("image-rendering")] = "optimizeQuality"; /* legacy */ + style[camelize("-ms-interpolation-mode")] = "bicubic"; /* IE */ + style[camelize("image-rendering")] = "auto"; /* CSS3 */ } }, @@ -79,34 +84,33 @@ Crafty._registerLayerTemplate("DOM", { * #.debug * @comp DomLayer * @kind Method - * + * * @sign public .debug() - * + * * Logs the current list of entities that have been invalidated in this layer. */ - debug: function () { + debug: function() { Crafty.log(this._changedObjs); }, - /**@ * #._render * @comp DomLayer * @kind Method * @private - * + * * @sign public .render() * * When "RenderScene" is triggered, draws all DOM entities that have been flagged * * @see DOM#.draw */ - _render: function () { + _render: function() { var changed = this._changedObjs; // Adjust the viewport if (this._dirtyViewport) { - this._setViewport(); - this._dirtyViewport = false; + this._setViewport(); + this._dirtyViewport = false; } //if no objects have been changed, stop @@ -121,7 +125,6 @@ Crafty._registerLayerTemplate("DOM", { //reset DOM array changed.length = 0; - }, /**@ @@ -129,7 +132,7 @@ Crafty._registerLayerTemplate("DOM", { * @comp DomLayer * @kind Method * @private - * + * * @sign public .dirty(ent) * @param ent - The entity to mark as dirty * @@ -144,7 +147,7 @@ Crafty._registerLayerTemplate("DOM", { * @comp DomLayer * @kind Method * @private - * + * * @sign public .attach(ent) * @param ent - The entity to add * @@ -158,13 +161,13 @@ Crafty._registerLayerTemplate("DOM", { ent._element.style.position = "absolute"; ent._element.id = "ent" + ent[0]; }, - + /**@ * #.detach * @comp DomLayer * @kind Method * @private - * + * * @sign public .detach(ent) * @param ent - The entity to remove * @@ -184,10 +187,10 @@ Crafty._registerLayerTemplate("DOM", { var dx = -view._x * scale; var dy = -view._y * scale; - style.transform = style[Crafty.support.prefix + "Transform"] = "scale(" + scale + ", " + scale + ")"; + style.transform = style[Crafty.support.prefix + "Transform"] = + "scale(" + scale + ", " + scale + ")"; style.left = Math.round(dx) + "px"; style.top = Math.round(dy) + "px"; style.zIndex = this.options.z; } - -}); \ No newline at end of file +}); diff --git a/src/graphics/dom.js b/src/graphics/dom.js index 16b86689..0c6fe5ca 100644 --- a/src/graphics/dom.js +++ b/src/graphics/dom.js @@ -1,4 +1,4 @@ -var Crafty = require('../core/core.js'), +var Crafty = require("../core/core.js"), document = window.document; /**@ @@ -23,37 +23,37 @@ Crafty.c("DOM", { * #.avoidCss3dTransforms * @comp DOM * @kind Property - * + * * Avoids using of CSS 3D Transform for positioning when true. Default value is false. */ avoidCss3dTransforms: false, - init: function () { + init: function() { this.requires("Renderable"); - + this._cssStyles = { - visibility: '', - left: '', - top: '', - width: '', - height: '', - zIndex: '', - opacity: '', - transformOrigin: '', - transform: '' + visibility: "", + left: "", + top: "", + width: "", + height: "", + zIndex: "", + opacity: "", + transformOrigin: "", + transform: "" }; this._element = document.createElement("div"); // Attach the entity to the dom layer - if (!this._customLayer){ - this._attachToLayer( Crafty.s("DefaultDOMLayer") ); + if (!this._customLayer) { + this._attachToLayer(Crafty.s("DefaultDOMLayer")); } this.bind("NewComponent", this._updateClass); this.bind("RemoveComponent", this._removeClass); }, - remove: function(){ + remove: function() { this._detachFromLayer(); this.unbind("NewComponent", this._updateClass); this.unbind("RemoveComponent", this._removeClass); @@ -63,12 +63,12 @@ Crafty.c("DOM", { * #.getDomId * @comp DOM * @kind Method - * + * * @sign public this .getDomId() * * Get the Id of the DOM element used to represent the entity. */ - getDomId: function () { + getDomId: function() { return this._element.id; }, @@ -79,7 +79,7 @@ Crafty.c("DOM", { str = ""; for (comp in c) { if (comp !== removedComponent) { - str += ' ' + comp; + str += " " + comp; } } str = str.substr(1); @@ -92,7 +92,7 @@ Crafty.c("DOM", { c = this.__c, str = ""; for (comp in c) { - str += ' ' + comp; + str += " " + comp; } str = str.substr(1); this._element.className = str; @@ -102,16 +102,16 @@ Crafty.c("DOM", { * #.DOM * @comp DOM * @kind Method - * + * * @trigger Draw - when the entity is ready to be drawn to the stage - { style:String, type:"DOM", co} * @sign public this .DOM(HTMLElement elem) * @param elem - HTML element that will replace the dynamically created one * * Pass a DOM element to use rather than one created. Will set `._element` to this value. Removes the old element. - * + * * Will reattach the entity to the current draw layer */ - DOM: function (elem) { + DOM: function(elem) { if (elem && elem.nodeType) { var layer = this._drawLayer; this._detachFromLayer(); @@ -126,12 +126,12 @@ Crafty.c("DOM", { * @comp DOM * @kind Method * @private - * + * * @sign public this .draw(void) * * Updates the CSS properties of the node to draw on the stage. */ - draw: function () { + draw: function() { var style = this._element.style, coord = this.__coord || [0, 0, 0, 0], co = { @@ -154,25 +154,27 @@ Crafty.c("DOM", { //utilize CSS3 if supported if (Crafty.support.css3dtransform && !this.avoidCss3dTransforms) { - trans.push("translate3d(" + (~~this._x) + "px," + (~~this._y) + "px,0)"); + trans.push( + "translate3d(" + ~~this._x + "px," + ~~this._y + "px,0)" + ); } else { if (this._cssStyles.left !== this._x) { this._cssStyles.left = this._x; - style.left = ~~ (this._x) + "px"; + style.left = ~~this._x + "px"; } if (this._cssStyles.top !== this._y) { this._cssStyles.top = this._y; - style.top = ~~ (this._y) + "px"; + style.top = ~~this._y + "px"; } } if (this._cssStyles.width !== this._w) { this._cssStyles.width = this._w; - style.width = ~~ (this._w) + "px"; + style.width = ~~this._w + "px"; } if (this._cssStyles.height !== this._h) { this._cssStyles.height = this._h; - style.height = ~~ (this._h) + "px"; + style.height = ~~this._h + "px"; } if (this._cssStyles.zIndex !== this._z) { this._cssStyles.zIndex = this._z; @@ -189,7 +191,8 @@ Crafty.c("DOM", { var origin = this._origin.x + "px " + this._origin.y + "px"; style.transformOrigin = origin; style[prefix + "TransformOrigin"] = origin; - if (Crafty.support.css3dtransform) trans.push("rotateZ(" + this._rotation + "deg)"); + if (Crafty.support.css3dtransform) + trans.push("rotateZ(" + this._rotation + "deg)"); else trans.push("rotate(" + this._rotation + "deg)"); } @@ -218,7 +221,7 @@ Crafty.c("DOM", { _setCssProperty: function(style, key, val) { key = Crafty.domHelper.camelize(key); - if (typeof val === "number") val += 'px'; + if (typeof val === "number") val += "px"; style[key] = val; this.trigger("SetStyle", key); }, @@ -228,7 +231,7 @@ Crafty.c("DOM", { * @comp DOM * @kind Method * @trigger SetStyle - for each style that is set - string - propertyName - * + * * @sign public css(String property, String value) * @param property - CSS property to modify * @param value - Value to give the CSS property @@ -258,7 +261,7 @@ Crafty.c("DOM", { * this.css("text-Decoration"); //returns line-through * ~~~ */ - css: function (obj, value) { + css: function(obj, value) { var key, elem = this._element, val, @@ -275,13 +278,13 @@ Crafty.c("DOM", { //if a value is passed, set the property if (value) { this._setCssProperty(style, obj, value); - } else { //otherwise return the computed property + } else { + //otherwise return the computed property return Crafty.domHelper.getStyle(elem, obj); } } this.trigger("Invalidate"); - return this; } diff --git a/src/graphics/drawing.js b/src/graphics/drawing.js index aa470f5c..606d8dbe 100644 --- a/src/graphics/drawing.js +++ b/src/graphics/drawing.js @@ -1,4 +1,4 @@ -var Crafty = require('../core/core.js'); +var Crafty = require("../core/core.js"); Crafty.extend({ /**@ @@ -19,7 +19,7 @@ Crafty.extend({ * Crafty.background('#FFFFFF url(landscape.png) no-repeat center center'); * ~~~ */ - background: function (style) { + background: function(style) { Crafty.stage.elem.style.background = style; }, @@ -27,7 +27,7 @@ Crafty.extend({ * #Crafty.pixelart * @category Graphics * @kind Method - * + * * @sign public void Crafty.pixelart(Boolean enabled) * @param enabled - whether to preserve sharp edges when rendering images * @@ -36,18 +36,18 @@ Crafty.extend({ * Setting this to true disables smoothing for images, which is the preferred * way for drawing pixel art. Defaults to false. * - * This feature is experimental and you should be careful with cross-browser compatibility. + * This feature is experimental and you should be careful with cross-browser compatibility. * The best way to disable image smoothing is to use the Canvas render method and the Sprite component for drawing your entities. * - * If you want to switch modes in the middle of a scene, - * be aware that canvas entities won't be drawn in the new style until something else invalidates them. + * If you want to switch modes in the middle of a scene, + * be aware that canvas entities won't be drawn in the new style until something else invalidates them. * (You can manually invalidate all canvas entities with `Crafty("Canvas").trigger("Invalidate");`) * - * @note Firefox_26 currently has a [bug](https://bugzilla.mozilla.org/show_bug.cgi?id=696630) + * @note Firefox_26 currently has a [bug](https://bugzilla.mozilla.org/show_bug.cgi?id=696630) * which prevents disabling image smoothing for Canvas entities that use the Image component. Use the Sprite * component instead. * - * @note Webkit (Chrome & Safari) currently has a bug [link1](http://code.google.com/p/chromium/issues/detail?id=134040) + * @note Webkit (Chrome & Safari) currently has a bug [link1](http://code.google.com/p/chromium/issues/detail?id=134040) * [link2](http://code.google.com/p/chromium/issues/detail?id=106662) that prevents disabling image smoothing * for DOM entities. * @@ -55,7 +55,7 @@ Crafty.extend({ * This is the preferred way to draw pixel art with the best cross-browser compatibility. * ~~~ * Crafty.pixelart(true); - * + * * Crafty.sprite(imgWidth, imgHeight, "spriteMap.png", {sprite1:[0,0]}); * Crafty.e("2D, Canvas, sprite1"); * ~~~ diff --git a/src/graphics/gl-textures.js b/src/graphics/gl-textures.js index 501b4e56..3d4d1130 100644 --- a/src/graphics/gl-textures.js +++ b/src/graphics/gl-textures.js @@ -1,14 +1,14 @@ -var Crafty = require('../core/core.js'); +var Crafty = require("../core/core.js"); // An object for wrangling textures // An assumption here is that doing anything with textures is fairly expensive, so the code should be expressive rather than performant Crafty.TextureManager = TextureManager; -function TextureManager (gl, webgl) { +function TextureManager(gl, webgl) { this.gl = gl; this.webgl = webgl; // The maximum number of units the environment says it supports - this.max_units = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS); + this.max_units = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS); // An array of textures bound to a texture unit; position corresponds to the unit in question this.bound_textures = []; // A dictionary of registered textures, so that multiple copies of the same texture aren't generated @@ -18,11 +18,10 @@ function TextureManager (gl, webgl) { } TextureManager.prototype = { - // Clear out the bound textures and other existing state - reset: function(){ + reset: function() { var t; - for (var i = 0; i < this.bound_textures.length; i++){ + for (var i = 0; i < this.bound_textures.length; i++) { t = this.bound_textures[i]; t.unbind(); } @@ -38,8 +37,8 @@ TextureManager.prototype = { var webgl = this.webgl; // Check whether a texture that matches the one requested already exists - var id = "texture-(r:" + repeating + ")-" + url; - if (typeof this.registered_textures[id] !== 'undefined') + var id = "texture-(r:" + repeating + ")-" + url; + if (typeof this.registered_textures[id] !== "undefined") return this.registered_textures[id]; // Create a texture, bind it to the next available unit @@ -47,7 +46,7 @@ TextureManager.prototype = { this.registered_textures[id] = t; this.bindTexture(t); - // Set the properties of the texture + // Set the properties of the texture t.setImage(image); t.setFilter(webgl.texture_filter); t.setRepeat(repeating); @@ -60,7 +59,7 @@ TextureManager.prototype = { smallest: function() { var min_size = Infinity; var index = null; - for (var i=0; iIndex"); * ~~~ */ - replace: function (new_html) { + replace: function(new_html) { this.inner = new_html; this._element.innerHTML = new_html; return this; @@ -45,7 +44,7 @@ Crafty.c("HTML", { * #.append * @comp HTML * @kind Method - * + * * @sign public this .append(String html) * @param html - arbitrary html * @@ -59,7 +58,7 @@ Crafty.c("HTML", { * .append("Index"); * ~~~ */ - append: function (new_html) { + append: function(new_html) { this.inner += new_html; this._element.innerHTML += new_html; return this; @@ -69,7 +68,7 @@ Crafty.c("HTML", { * #.prepend * @comp HTML * @kind Method - * + * * @sign public this .prepend(String html) * @param html - arbitrary html * @@ -83,9 +82,9 @@ Crafty.c("HTML", { * .prepend("Index"); * ~~~ */ - prepend: function (new_html) { + prepend: function(new_html) { this.inner = new_html + this.inner; this._element.innerHTML = new_html + this.inner; return this; } -}); \ No newline at end of file +}); diff --git a/src/graphics/image.js b/src/graphics/image.js index 8e04b3b3..36d97736 100644 --- a/src/graphics/image.js +++ b/src/graphics/image.js @@ -1,36 +1,43 @@ -var Crafty = require('../core/core.js'); - +var Crafty = require("../core/core.js"); // // Define some variables required for webgl -var fs = require('fs'); - -Crafty.defaultShader("Image", new Crafty.WebGLShader( - fs.readFileSync(__dirname + '/shaders/sprite.vert', 'utf8'), - fs.readFileSync(__dirname + '/shaders/sprite.frag', 'utf8'), - [ - { name: "aPosition", width: 2 }, - { name: "aOrientation", width: 3 }, - { name: "aLayer", width: 2 }, - { name: "aTextureCoord", width: 2 } - ], - function(e, _entity) { - var pos = e.pos; - // Write texture coordinates - e.program.writeVector("aTextureCoord", - 0, 0, - 0, pos._h, - pos._w, 0, - pos._w, pos._h - ); - } -)); +var fs = require("fs"); + +Crafty.defaultShader( + "Image", + new Crafty.WebGLShader( + fs.readFileSync(__dirname + "/shaders/sprite.vert", "utf8"), + fs.readFileSync(__dirname + "/shaders/sprite.frag", "utf8"), + [ + { name: "aPosition", width: 2 }, + { name: "aOrientation", width: 3 }, + { name: "aLayer", width: 2 }, + { name: "aTextureCoord", width: 2 } + ], + function(e, _entity) { + var pos = e.pos; + // Write texture coordinates + e.program.writeVector( + "aTextureCoord", + 0, + 0, + 0, + pos._h, + pos._w, + 0, + pos._w, + pos._h + ); + } + ) +); /**@ * #Image * @category Graphics * @kind Component - * + * * Draw an image with or without repeating (tiling). * * If the entity's width and height are smaller than the width and height of the image source, the image will appear cropped. @@ -45,7 +52,7 @@ Crafty.c("Image", { _repeat: "repeat", ready: false, - init: function () { + init: function() { this.bind("Draw", this._drawImage); this.bind("LayerAttached", this._setupImage); }, @@ -59,7 +66,7 @@ Crafty.c("Image", { * #.image * @comp Image * @kind Method - * + * * @trigger Invalidate - when the image is loaded * @sign public this .image(String url[, String repeat]) * @param url - URL of the image @@ -90,7 +97,7 @@ Crafty.c("Image", { * * @see Crafty.sprite */ - image: function (url, repeat) { + image: function(url, repeat) { this.__image = url; this._repeat = repeat || "no-repeat"; @@ -101,7 +108,7 @@ Crafty.c("Image", { this.img.src = url; var self = this; - this.img.onload = function () { + this.img.onload = function() { self._setupImage(self._drawLayer); }; } else { @@ -114,14 +121,26 @@ Crafty.c("Image", { }, // called on image change or layer attachment - _setupImage: function(layer){ + _setupImage: function(layer) { if (!this.img || !layer) return; if (layer.type === "Canvas") { - this._pattern = this._drawContext.createPattern(this.img, this._repeat); + this._pattern = this._drawContext.createPattern( + this.img, + this._repeat + ); } else if (layer.type === "WebGL") { - this._establishShader("image:" + this.__image, Crafty.defaultShader("Image")); - this.program.setTexture( this._drawLayer.makeTexture(this.__image, this.img, (this._repeat!=="no-repeat"))); + this._establishShader( + "image:" + this.__image, + Crafty.defaultShader("Image") + ); + this.program.setTexture( + this._drawLayer.makeTexture( + this.__image, + this.img, + this._repeat !== "no-repeat" + ) + ); } if (this._repeat === "no-repeat") { @@ -133,7 +152,7 @@ Crafty.c("Image", { this.trigger("Invalidate"); }, - _drawImage: function(e){ + _drawImage: function(e) { if (e.type === "canvas") { //skip if no image if (!this.ready || !this._pattern) return; @@ -148,12 +167,11 @@ Crafty.c("Image", { context.restore(); } else if (e.type === "DOM") { if (this.__image) { - e.style.backgroundImage = "url(" + this.__image + ")"; - e.style.backgroundRepeat = this._repeat; + e.style.backgroundImage = "url(" + this.__image + ")"; + e.style.backgroundRepeat = this._repeat; } } else if (e.type === "webgl") { - e.program.draw(e, this); + e.program.draw(e, this); } - } -}); \ No newline at end of file +}); diff --git a/src/graphics/layers.js b/src/graphics/layers.js index 131a39ce..9fb9722b 100644 --- a/src/graphics/layers.js +++ b/src/graphics/layers.js @@ -1,22 +1,26 @@ -var Crafty = require('../core/core.js'); +var Crafty = require("../core/core.js"); Crafty.extend({ _drawLayerTemplates: {}, _drawLayers: [], - _addDrawLayerInstance: function (layer) { + _addDrawLayerInstance: function(layer) { Crafty._drawLayers.push(layer); - this._drawLayers.sort(function (a, b) { return a.options.z - b.options.z; }); + this._drawLayers.sort(function(a, b) { + return a.options.z - b.options.z; + }); }, - _removeDrawLayerInstance: function (layer) { + _removeDrawLayerInstance: function(layer) { var i = this._drawLayers.indexOf(layer); if (i >= 0) { this._drawLayers.splice(i, 1); } - this._drawLayers.sort(function (a, b) { return a.options.z - b.options.z; }); + this._drawLayers.sort(function(a, b) { + return a.options.z - b.options.z; + }); }, - _registerLayerTemplate: function (type, layerTemplate) { + _registerLayerTemplate: function(type, layerTemplate) { this._drawLayerTemplates[type] = layerTemplate; var common = this._commonLayerProperties; @@ -45,7 +49,6 @@ Crafty.extend({ // A cached version of the viewport rect _cachedViewportRect: null, - init: function() { this._cachedViewportRect = {}; @@ -53,7 +56,9 @@ Crafty.extend({ this.trigger("LayerInit"); // Handle viewport invalidation - this.uniqueBind("InvalidateViewport", function () { this._dirtyViewport = true; }); + this.uniqueBind("InvalidateViewport", function() { + this._dirtyViewport = true; + }); // Set pixelart to current status this.trigger("PixelartSet", Crafty._pixelartEnabled); @@ -75,7 +80,7 @@ Crafty.extend({ // Based on the camera options, find the Crafty coordinates // corresponding to the layer's position in the viewport - _viewportRect: function (useCached) { + _viewportRect: function(useCached) { var rect = this._cachedViewportRect; if (useCached) return rect; @@ -87,15 +92,23 @@ Crafty.extend({ rect._scale = scale; rect._w = viewport._width / scale; rect._h = viewport._height / scale; - + // This particular transformation is designed such that, // if a combination pan/scale keeps the center of the screen fixed for a layer with x/y response of 1, // then it will also be fixed for layers with other values for x/y response // (note that the second term vanishes when either the response or scale are 1) - rect._x = options.xResponse * (-viewport._x) - - 0.5 * (options.xResponse - 1) * (1 - 1 / scale) * viewport._width; - rect._y = options.yResponse * (-viewport._y) - - 0.5 * (options.yResponse - 1) * (1 - 1 / scale) * viewport._height; + rect._x = + options.xResponse * -viewport._x - + 0.5 * + (options.xResponse - 1) * + (1 - 1 / scale) * + viewport._width; + rect._y = + options.yResponse * -viewport._y - + 0.5 * + (options.yResponse - 1) * + (1 - 1 / scale) * + viewport._height; return rect; }, @@ -139,9 +152,9 @@ Crafty.extend({ * Crafty will automatically define three built-in layers: "DefaultDOMLayer", DefaultCanvasLayer", and "DefaultWebGLLayer". * They will have `z` values of `30`, `20`, and `10` respectively, and will be initialized if a "DOM", "Canvas" or "WebGL" component * is used with an entity not attached to any user-specified layer. - * + * * @note Layers are implemented as systems, so the layer name must be distinct from other systems. - * + * * @note By default, layers will persist across scene changes. You can manually clean up a layer by removing all it's entities and then destroying it. * * @example @@ -161,13 +174,13 @@ Crafty.extend({ * @example * ``` * Crafty.createLayer("MyCanvasLayer", "Canvas"); - * Crafty.s("MyCanvasLayer").one("RenderScene", function(){ this.everRendered = true; }); + * Crafty.s("MyCanvasLayer").one("RenderScene", function(){ this.everRendered = true; }); * ``` * Create a custom layer, and then bind a method to run the first time it renders. * * @example * ``` * Crafty("MyCanvasLayer").destroy(); - * Crafty.s("MyCanvasLayer").destroy(); + * Crafty.s("MyCanvasLayer").destroy(); * ``` * For a previously defined "MyCanvasLayer", destroy it and all the entities rendered by it. */ @@ -175,18 +188,18 @@ Crafty.extend({ var layerTemplate = this._drawLayerTemplates[type]; Crafty.s(name, layerTemplate, options); Crafty.c(name, { - init: function () { - this.requires("Renderable"); - + init: function() { + this.requires("Renderable"); + // Flag to indicate that the base component doesn't need to attach a layer this._customLayer = true; this.requires(layerTemplate.type); this._attachToLayer(Crafty.s(name)); }, - remove: function () { + remove: function() { this._detachFromLayer(); } }); } -}); \ No newline at end of file +}); diff --git a/src/graphics/particles.js b/src/graphics/particles.js index ceccee45..e8e1b1ce 100644 --- a/src/graphics/particles.js +++ b/src/graphics/particles.js @@ -1,5 +1,4 @@ -var Crafty = require('../core/core.js'); - +var Crafty = require("../core/core.js"); ////////////////////////////////////////////////////////////////////////////////////////////////////// // Particles are based on Parcycle by Mr. Speaker, licensed under the MIT, Ported by Leo Koppelkamm // @@ -28,7 +27,7 @@ Crafty.c("Particles", { _particlesPaused: false, - init: function () { + init: function() { // We need to clone particle handler object to avoid shared object trap this._Particles = Crafty.clone(this._Particles); // Add default options @@ -38,7 +37,7 @@ Crafty.c("Particles", { }, events: { - "UpdateFrame": function () { + UpdateFrame: function() { // don't update if paused or no particle fx active if (this._particlesPaused || !this._Particles.active) return; @@ -48,7 +47,7 @@ Crafty.c("Particles", { this.trigger("Invalidate"); }, - "Draw": function (e) { + Draw: function(e) { // don't render if no particle fx active, but do redraw paused particles if (!this._Particles.active) return; @@ -63,7 +62,7 @@ Crafty.c("Particles", { * #.particles * @comp Particles * @kind Method - * + * * @sign public this .particles([Object options]) * @param options - Map of options that specify the behavior and look of the particles. * @@ -118,7 +117,7 @@ Crafty.c("Particles", { * .particles(options); * ~~~ */ - particles: function (options) { + particles: function(options) { // Overwrite default options this._Particles.config(options); // Start animation @@ -160,7 +159,7 @@ Crafty.c("Particles", { // sensible values are 0-3 jitter: 0, // offset of particles from origin - originOffset: {x: 0, y: 0} + originOffset: { x: 0, y: 0 } }, emissionRate: 0, @@ -169,14 +168,14 @@ Crafty.c("Particles", { active: true, particles: [], - init: function () { - // Create initial config by adding presets. + init: function() { + // Create initial config by adding presets. for (var key in this.presets) { this[key] = this.presets[key]; } }, - config: function (options) { + config: function(options) { options = options || {}; // Create current config by merging in given options. @@ -194,7 +193,7 @@ Crafty.c("Particles", { } }, - start: function () { + start: function() { // (re)set active state this.active = true; this.elapsedFrames = 0; @@ -209,26 +208,42 @@ Crafty.c("Particles", { this.parentEntity.trigger("ParticleStart"); }, - stop: function () { + stop: function() { // set disabled state this.active = false; this.parentEntity.trigger("ParticleEnd"); }, - initParticle: function (particle) { - var angle, speed, size, timeToLive, sharpness, c, - startR, startG, startB, startA, - endR, endG, endB, endA; - - particle.timeToLive = timeToLive = this.lifeSpan + this.lifeSpanRandom * this.RANDM1TO1(); + initParticle: function(particle) { + var angle, + speed, + size, + timeToLive, + sharpness, + c, + startR, + startG, + startB, + startA, + endR, + endG, + endB, + endA; + + particle.timeToLive = timeToLive = + this.lifeSpan + this.lifeSpanRandom * this.RANDM1TO1(); // TODO default to entity origin instead, deprecate originOffset // TODO subtract size/2 from position - particle.positionX = this.originOffset.x + this.spread * this.RANDM1TO1(); - particle.positionY = this.originOffset.y + this.spread * this.RANDM1TO1(); - - angle = (this.angle + this.angleRandom * this.RANDM1TO1()) * (Math.PI / 180); // convert to radians + particle.positionX = + this.originOffset.x + this.spread * this.RANDM1TO1(); + particle.positionY = + this.originOffset.y + this.spread * this.RANDM1TO1(); + + angle = + (this.angle + this.angleRandom * this.RANDM1TO1()) * + (Math.PI / 180); // convert to radians speed = this.speed + this.speedRandom * this.RANDM1TO1(); // Could move to lookup for speed particle.directionX = Math.sin(angle) * speed; @@ -237,25 +252,39 @@ Crafty.c("Particles", { size = this.size + this.sizeRandom * this.RANDM1TO1(); particle.size = size = size < 0 ? 0 : ~~size; - sharpness = this.sharpness + this.sharpnessRandom * this.RANDM1TO1(); - particle.sharpness = sharpness = sharpness > 100 ? 100 : sharpness < 0 ? 0 : sharpness; + sharpness = + this.sharpness + this.sharpnessRandom * this.RANDM1TO1(); + particle.sharpness = sharpness = + sharpness > 100 ? 100 : sharpness < 0 ? 0 : sharpness; // internal circle gradient size - affects the sharpness of the radial gradient - particle.sizeSmall = ~~ ((size / 200) * sharpness); //(size/2/100) + particle.sizeSmall = ~~((size / 200) * sharpness); //(size/2/100) - c = startR = this.startColour[0] + this.startColourRandom[0] * this.RANDM1TO1(); + c = startR = + this.startColour[0] + + this.startColourRandom[0] * this.RANDM1TO1(); particle.colourR = c > 255 ? 255 : c < 0 ? 0 : ~~c; - c = startG = this.startColour[1] + this.startColourRandom[1] * this.RANDM1TO1(); + c = startG = + this.startColour[1] + + this.startColourRandom[1] * this.RANDM1TO1(); particle.colourG = c > 255 ? 255 : c < 0 ? 0 : ~~c; - c = startB = this.startColour[2] + this.startColourRandom[2] * this.RANDM1TO1(); + c = startB = + this.startColour[2] + + this.startColourRandom[2] * this.RANDM1TO1(); particle.colourB = c > 255 ? 255 : c < 0 ? 0 : ~~c; - c = startA = this.startColour[3] + this.startColourRandom[3] * this.RANDM1TO1(); - particle.colourA = c > 1 ? 1 : c < 0 ? 0 : (~~(c * 100)) / 100; - - endR = this.endColour[0] + this.endColourRandom[0] * this.RANDM1TO1(); - endG = this.endColour[1] + this.endColourRandom[1] * this.RANDM1TO1(); - endB = this.endColour[2] + this.endColourRandom[2] * this.RANDM1TO1(); - endA = this.endColour[3] + this.endColourRandom[3] * this.RANDM1TO1(); + c = startA = + this.startColour[3] + + this.startColourRandom[3] * this.RANDM1TO1(); + particle.colourA = c > 1 ? 1 : c < 0 ? 0 : ~~(c * 100) / 100; + + endR = + this.endColour[0] + this.endColourRandom[0] * this.RANDM1TO1(); + endG = + this.endColour[1] + this.endColourRandom[1] * this.RANDM1TO1(); + endB = + this.endColour[2] + this.endColourRandom[2] * this.RANDM1TO1(); + endA = + this.endColour[3] + this.endColourRandom[3] * this.RANDM1TO1(); particle.deltaColourR = (endR - startR) / timeToLive; particle.deltaColourG = (endG - startG) / timeToLive; @@ -263,7 +292,7 @@ Crafty.c("Particles", { particle.deltaColourA = (endA - startA) / timeToLive; }, - update: function () { + update: function() { var RANDM1TO1 = this.RANDM1TO1; var gravityX = this.gravity.x, gravityY = this.gravity.y; @@ -280,13 +309,14 @@ Crafty.c("Particles", { this.emitCounter++; // update all particles - var c, particle, particles = this.particles; + var c, + particle, + particles = this.particles; for (var i = 0, l = particles.length; i < l; ++i) { particle = particles[i]; // If the current particle is alive then update it if (particle.timeToLive > 0) { - // Calculate the new position based on gravity particle.directionX += gravityX; particle.directionY += gravityY; @@ -305,12 +335,13 @@ Crafty.c("Particles", { c = particle.colourB + particle.deltaColourB; particle.colourB = c > 255 ? 255 : c < 0 ? 0 : ~~c; c = particle.colourA + particle.deltaColourA; - particle.colourA = c > 1 ? 1 : c < 0 ? 0 : (~~(c * 100)) / 100; + particle.colourA = + c > 1 ? 1 : c < 0 ? 0 : ~~(c * 100) / 100; // Decrease particle's lifespan particle.timeToLive--; - // Else reinitialize particle if within emission rate + // Else reinitialize particle if within emission rate } else if (this.emitCounter > rate) { this.initParticle(particle); this.emitCounter -= rate; @@ -318,19 +349,24 @@ Crafty.c("Particles", { } }, - render: function (e) { + render: function(e) { var context = e.ctx; var delim = ","; - var particle, particles = this.particles; + var particle, + particles = this.particles; for (var i = 0, l = particles.length; i < l; i++) { particle = particles[i]; var size = particle.size; var halfSize = size >> 1; - if (particle.positionX < 0 || particle.positionX + size > e.pos._w || - particle.positionY < 0 || particle.positionY + size > e.pos._h) { + if ( + particle.positionX < 0 || + particle.positionX + size > e.pos._w || + particle.positionY < 0 || + particle.positionY + size > e.pos._h + ) { //Particle is outside continue; } @@ -343,13 +379,22 @@ Crafty.c("Particles", { a = particle.colourA; // Calculate the rgba string to draw. - var drawColour = "rgba(" + r + delim + g + delim + b + delim + a + ")"; + var drawColour = + "rgba(" + r + delim + g + delim + b + delim + a + ")"; if (this.fastMode) { context.fillStyle = drawColour; } else { - var drawColourEnd = "rgba(" + r + delim + g + delim + b + delim + "0)"; - - var radgrad = context.createRadialGradient(x + halfSize, y + halfSize, particle.sizeSmall, x + halfSize, y + halfSize, halfSize); + var drawColourEnd = + "rgba(" + r + delim + g + delim + b + delim + "0)"; + + var radgrad = context.createRadialGradient( + x + halfSize, + y + halfSize, + particle.sizeSmall, + x + halfSize, + y + halfSize, + halfSize + ); radgrad.addColorStop(0, drawColour); //0.9 to avoid visible boxing radgrad.addColorStop(0.9, drawColourEnd); @@ -359,7 +404,7 @@ Crafty.c("Particles", { } }, - Particle: function () { + Particle: function() { this.positionX = 0; this.positionY = 0; @@ -384,7 +429,7 @@ Crafty.c("Particles", { this.sharpness = 0; }, - RANDM1TO1: function () { + RANDM1TO1: function() { return Math.random() * 2 - 1; } }, @@ -393,7 +438,7 @@ Crafty.c("Particles", { * #.pauseParticles * @comp Particles * @kind Method - * + * * @sign public this.pauseParticles() * * The pauseParticles will freeze these particles in execution. @@ -416,7 +461,7 @@ Crafty.c("Particles", { * #.resumeParticles * @comp Particles * @kind Method - * + * * @sign public this.resumeParticles() * * The resumeParticles will resume earlier paused particles diff --git a/src/graphics/renderable.js b/src/graphics/renderable.js index 84bfe52a..efa202ef 100644 --- a/src/graphics/renderable.js +++ b/src/graphics/renderable.js @@ -1,24 +1,22 @@ -var Crafty = require('../core/core.js'); - +var Crafty = require("../core/core.js"); /**@ * #Renderable * @category Graphics * @kind Component - * + * * Component for any entity that has a position on the stage. * @trigger Invalidate - when the entity needs to be redrawn */ Crafty.c("Renderable", { - // Flag for tracking whether the entity is dirty or not _changed: false, - + /**@ * #.alpha * @comp Renderable * @kind Property - * + * * Transparency of an entity. Must be a decimal value between 0.0 being fully transparent to 1.0 being fully opaque. */ _alpha: 1.0, @@ -27,7 +25,7 @@ Crafty.c("Renderable", { * #.visible * @comp Renderable * @kind Property - * + * * If the entity is visible or not. Accepts a true or false value. * Can be used for optimization by setting an entities visibility to false when not needed to be drawn. * @@ -50,42 +48,41 @@ Crafty.c("Renderable", { // Setup all the properties that we need to define properties: { alpha: { - set: function (v) { - this._setterRenderable('_alpha', v); + set: function(v) { + this._setterRenderable("_alpha", v); }, - get: function () { + get: function() { return this._alpha; }, configurable: true, enumerable: true }, - _alpha: {enumerable:false}, + _alpha: { enumerable: false }, visible: { - set: function (v) { - this._setterRenderable('_visible', v); + set: function(v) { + this._setterRenderable("_visible", v); }, - get: function () { + get: function() { return this._visible; }, configurable: true, enumerable: true }, - _visible: {enumerable:false} + _visible: { enumerable: false } }, - init: function () { - }, + init: function() {}, // Need to store visibility before being frozen _hideOnUnfreeze: false, events: { - "Freeze":function(){ + Freeze: function() { this._hideOnUnfreeze = !this._visible; this._visible = false; this.trigger("Invalidate"); }, - "Unfreeze":function(){ + Unfreeze: function() { this._visible = !this._hideOnUnfreeze; this.trigger("Invalidate"); } @@ -129,7 +126,7 @@ Crafty.c("Renderable", { * #.flip * @comp Renderable * @kind Method - * + * * @trigger Invalidate - when the entity has flipped * @sign public this .flip(String dir) * @param dir - Flip direction @@ -141,7 +138,7 @@ Crafty.c("Renderable", { * this.flip("X") * ~~~ */ - flip: function (dir) { + flip: function(dir) { dir = dir || "X"; if (!this["_flip" + dir]) { this["_flip" + dir] = true; @@ -154,7 +151,7 @@ Crafty.c("Renderable", { * #.unflip * @comp Renderable * @kind Method - * + * * @trigger Invalidate - when the entity has unflipped * @sign public this .unflip(String dir) * @param dir - Unflip direction @@ -166,7 +163,7 @@ Crafty.c("Renderable", { * this.unflip("X") * ~~~ */ - unflip: function (dir) { + unflip: function(dir) { dir = dir || "X"; if (this["_flip" + dir]) { this["_flip" + dir] = false; @@ -174,4 +171,4 @@ Crafty.c("Renderable", { } return this; } -}); \ No newline at end of file +}); diff --git a/src/graphics/sprite-animation.js b/src/graphics/sprite-animation.js index abe045ce..c41c4390 100644 --- a/src/graphics/sprite-animation.js +++ b/src/graphics/sprite-animation.js @@ -1,5 +1,4 @@ -var Crafty = require('../core/core.js'); - +var Crafty = require("../core/core.js"); /**@ * #SpriteAnimation @@ -61,8 +60,7 @@ Crafty.c("SpriteAnimation", { */ animationSpeed: 1, - - init: function () { + init: function() { this._reels = {}; }, @@ -135,7 +133,7 @@ Crafty.c("SpriteAnimation", { * .reel('PlayerRunning', 1000, ['PlayerIdle1', 'PlayerLeftFootForward', 'PlayerIdle2', 'PlayerRightFootForward']); * ~~~ */ - reel: function (reelId, duration, fromX, fromY, frameCount, rowLength) { + reel: function(reelId, duration, fromX, fromY, frameCount, rowLength) { // @sign public this .reel() if (arguments.length === 0) { return this._currentReelId; @@ -144,7 +142,7 @@ Crafty.c("SpriteAnimation", { // @sign public this .reel(String reelID) if (arguments.length === 1 && typeof reelId === "string") { if (typeof this._reels[reelId] === "undefined") { - throw("The specified reel " + reelId + " is undefined."); + throw "The specified reel " + reelId + " is undefined."; } this.pauseAnimation(); if (this._currentReelId !== reelId) { @@ -158,7 +156,6 @@ Crafty.c("SpriteAnimation", { return this; } - var reel, i; reel = { @@ -175,7 +172,8 @@ Crafty.c("SpriteAnimation", { if (typeof fromX === "number") { rowLength = rowLength || Infinity; - if (frameCount >= 0) { // forward animation + if (frameCount >= 0) { + // forward animation for (i = 0; i < frameCount; ++i) { reel.frames.push([fromX, fromY]); @@ -184,7 +182,8 @@ Crafty.c("SpriteAnimation", { fromY++; } } - } else { // backward animation + } else { + // backward animation for (i = 0; i > frameCount; --i) { reel.frames.push([fromX, fromY]); @@ -194,12 +193,10 @@ Crafty.c("SpriteAnimation", { } } } - } - // @sign public this .reel(String reelId, Number duration, Array frames) - else if (arguments.length === 3 && typeof fromX === "object") { + } else if (arguments.length === 3 && typeof fromX === "object") { + // @sign public this .reel(String reelId, Number duration, Array frames) reel.frames = fromX; - } - else { + } else { throw "Unrecognized arguments. Please see the documentation for 'reel(...)'."; } @@ -248,7 +245,7 @@ Crafty.c("SpriteAnimation", { var currentReel = this._currentReel; if (typeof currentReel === "undefined" || currentReel === null) { - throw("No reel is specified, and there is no currently active reel."); + throw "No reel is specified, and there is no currently active reel."; } this.pauseAnimation(); // This will pause the current animation, if one is playing @@ -287,7 +284,7 @@ Crafty.c("SpriteAnimation", { * If a reel is already playing, or there is no current reel, there will be no effect. */ resumeAnimation: function() { - if (this._isPlaying === false && this._currentReel !== null) { + if (this._isPlaying === false && this._currentReel !== null) { this.bind("UpdateFrame", this._animationTick); this._isPlaying = true; this._currentReel.easing.resume(); @@ -306,7 +303,7 @@ Crafty.c("SpriteAnimation", { * * Pauses the currently playing animation, or does nothing if no animation is playing. */ - pauseAnimation: function () { + pauseAnimation: function() { if (this._isPlaying === true) { this.unbind("UpdateFrame", this._animationTick); this._isPlaying = false; @@ -329,14 +326,13 @@ Crafty.c("SpriteAnimation", { */ resetAnimation: function() { var currentReel = this._currentReel; - if (currentReel === null) throw("No active reel to reset."); + if (currentReel === null) throw "No active reel to reset."; this.reelPosition(0); currentReel.easing.repeat(currentReel.defaultLoops); return this; - }, - + }, /**@ * #.loops @@ -356,8 +352,7 @@ Crafty.c("SpriteAnimation", { if (arguments.length === 0) { if (this._currentReel !== null) return this._currentReel.easing.loops; - else - return 0; + else return 0; } if (this._currentReel !== null) { @@ -392,7 +387,7 @@ Crafty.c("SpriteAnimation", { * */ reelPosition: function(position) { - if (this._currentReel === null) throw("No active reel."); + if (this._currentReel === null) throw "No active reel."; if (arguments.length === 0) { return this._currentReel.currentFrame; @@ -410,13 +405,12 @@ Crafty.c("SpriteAnimation", { position = Math.floor(l * progress); } else { if (position !== Math.floor(position)) - throw("Position " + position + " is invalid."); - if (position < 0) - position = l - 1 + position; + throw "Position " + position + " is invalid."; + if (position < 0) position = l - 1 + position; progress = position / l; } // cap to last frame - position = Math.min(position, l-1); + position = Math.min(position, l - 1); position = Math.max(position, 0); this._setProgress(progress); this._setFrame(position); @@ -437,13 +431,13 @@ Crafty.c("SpriteAnimation", { * Jumps to specifed frame if the reel was created with sprite names. * */ - reelFrame: function (frameName) { - if (this._currentReel === null) throw("No active reel."); + reelFrame: function(frameName) { + if (this._currentReel === null) throw "No active reel."; var index = this._currentReel.frames.indexOf(frameName); if (index === -1) { - throw("Frame name " + frameName + " is invalid."); + throw "Frame name " + frameName + " is invalid."; } this.reelPosition(index); @@ -457,11 +451,14 @@ Crafty.c("SpriteAnimation", { var currentReel = this._reels[this._currentReelId]; currentReel.easing.tick(frameData.dt * this.animationSpeed); var progress = currentReel.easing.value(); - var frameNumber = Math.min( Math.floor(currentReel.frames.length * progress), currentReel.frames.length - 1); + var frameNumber = Math.min( + Math.floor(currentReel.frames.length * progress), + currentReel.frames.length - 1 + ); this._setFrame(frameNumber); - if(currentReel.easing.complete === true){ + if (currentReel.easing.complete === true) { this.pauseAnimation(); this.trigger("AnimationEnd", this._currentReel); } @@ -471,8 +468,7 @@ Crafty.c("SpriteAnimation", { // The actual progress for the animation must be set seperately. _setFrame: function(frameNumber) { var currentReel = this._currentReel; - if (frameNumber === currentReel.currentFrame) - return; + if (frameNumber === currentReel.currentFrame) return; currentReel.currentFrame = frameNumber; this._updateSprite(); this.trigger("FrameChange", currentReel); @@ -484,17 +480,15 @@ Crafty.c("SpriteAnimation", { var frame = currentReel.frames[currentReel.currentFrame]; // .sprite will trigger redraw - if(typeof frame === "string") this.sprite(frame); + if (typeof frame === "string") this.sprite(frame); else this.sprite(frame[0], frame[1]); }, - // Sets the internal state of the current reel's easing object _setProgress: function(progress, repeats) { this._currentReel.easing.setProgress(progress, repeats); }, - /**@ * #.isPlaying * @comp SpriteAnimation @@ -513,7 +507,7 @@ Crafty.c("SpriteAnimation", { * myEntity.isPlaying('PlayerRunning') // is the PlayerRunning animation playing * ~~~ */ - isPlaying: function (reelId) { + isPlaying: function(reelId) { if (!this._isPlaying) return false; if (!reelId) return !!this._currentReelId; return this._currentReelId === reelId; @@ -532,7 +526,7 @@ Crafty.c("SpriteAnimation", { * @returns The specified reel, or `undefined` if no such reel exists. * */ - getReel: function (reelId) { + getReel: function(reelId) { if (arguments.length === 0) { if (!this._currentReelId) return null; reelId = this._currentReelId; @@ -540,4 +534,4 @@ Crafty.c("SpriteAnimation", { return this._reels[reelId]; } -}); \ No newline at end of file +}); diff --git a/src/graphics/sprite.js b/src/graphics/sprite.js index ac6b033d..32b82acf 100644 --- a/src/graphics/sprite.js +++ b/src/graphics/sprite.js @@ -1,34 +1,42 @@ -var Crafty = require('../core/core.js'); +var Crafty = require("../core/core.js"); // Define some variables required for webgl -var fs = require('fs'); - -Crafty.defaultShader("Sprite", new Crafty.WebGLShader( - fs.readFileSync(__dirname + '/shaders/sprite.vert', 'utf8'), - fs.readFileSync(__dirname + '/shaders/sprite.frag', 'utf8'), - [ - { name: "aPosition", width: 2 }, - { name: "aOrientation", width: 3 }, - { name: "aLayer", width: 2 }, - { name: "aTextureCoord", width: 2 } - ], - function(e, _entity) { - var co = e.co; - // Write texture coordinates - e.program.writeVector("aTextureCoord", - co.x, co.y, - co.x, co.y + co.h, - co.x + co.w, co.y, - co.x + co.w, co.y + co.h - ); - } -)); +var fs = require("fs"); + +Crafty.defaultShader( + "Sprite", + new Crafty.WebGLShader( + fs.readFileSync(__dirname + "/shaders/sprite.vert", "utf8"), + fs.readFileSync(__dirname + "/shaders/sprite.frag", "utf8"), + [ + { name: "aPosition", width: 2 }, + { name: "aOrientation", width: 3 }, + { name: "aLayer", width: 2 }, + { name: "aTextureCoord", width: 2 } + ], + function(e, _entity) { + var co = e.co; + // Write texture coordinates + e.program.writeVector( + "aTextureCoord", + co.x, + co.y, + co.x, + co.y + co.h, + co.x + co.w, + co.y, + co.x + co.w, + co.y + co.h + ); + } + ) +); Crafty.extend({ /**@ * #Crafty.sprite * @kind Method - * + * * @category Graphics * @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 @@ -79,7 +87,15 @@ Crafty.extend({ * * @see Sprite */ - sprite: function (tile, tileh, url, map, paddingX, paddingY, paddingAroundBorder) { + sprite: function( + tile, + tileh, + url, + map, + paddingX, + paddingY, + paddingAroundBorder + ) { var spriteName, temp, img; //if no tile value, default to 1. @@ -116,7 +132,7 @@ Crafty.extend({ img = new Image(); img.src = url; Crafty.asset(url, img); - img.onload = function () { + img.onload = function() { //all components with this img are now ready for (var spriteName in map) { Crafty(spriteName).each(markSpritesReady); @@ -129,12 +145,22 @@ Crafty.extend({ this.__trim = [0, 0, 0, 0]; this.__image = url; this.__map = map; - this.__coord = [this.__coord[0], this.__coord[1], this.__coord[2], this.__coord[3]]; + this.__coord = [ + this.__coord[0], + this.__coord[1], + this.__coord[2], + this.__coord[3] + ]; this.__tile = tile; this.__tileh = tileh; this.__padding = [paddingX, paddingY]; this.__padBorder = paddingAroundBorder; - this.sprite(this.__coord[0], this.__coord[1], this.__coord[2], this.__coord[3]); + this.sprite( + this.__coord[0], + this.__coord[1], + this.__coord[2], + this.__coord[3] + ); this.img = img; //draw now @@ -171,7 +197,7 @@ Crafty.extend({ * #Sprite * @category Graphics * @kind Component - * + * * @trigger Invalidate - when the sprites change * * A component for using tiles in a sprite map. @@ -182,7 +208,7 @@ Crafty.extend({ * @see Crafty.sprite, Crafty.load */ Crafty.c("Sprite", { - __image: '', + __image: "", /* * #.__tile * @comp Sprite @@ -203,33 +229,36 @@ Crafty.c("Sprite", { //ready is changed to true in Crafty.sprite ready: false, - init: function () { + init: function() { this.__trim = [0, 0, 0, 0]; this.bind("Draw", this._drawSprite); this.bind("LayerAttached", this._setupSpriteImage); }, - remove: function(){ + remove: function() { this.unbind("Draw", this._drawSprite); this.unbind("LayerAttached", this._setupSpriteImage); }, - + _setupSpriteImage: function(layer) { if (!this.__image || !this.img || !layer) return; - if (layer.type === "WebGL"){ + if (layer.type === "WebGL") { this._establishShader(this.__image, Crafty.defaultShader("Sprite")); - this.program.setTexture( layer.makeTexture(this.__image, this.img, false) ); + this.program.setTexture( + layer.makeTexture(this.__image, this.img, false) + ); } }, - _drawSprite: function(e){ + _drawSprite: function(e) { var co = e.co, - pos = e.pos, - context = e.ctx; + pos = e.pos, + context = e.ctx; if (e.type === "canvas") { //draw the image on the canvas element - context.drawImage(this.img, //image element + context.drawImage( + this.img, //image element co.x, //x position on sprite co.y, //y position on sprite co.w, //width on sprite @@ -251,14 +280,21 @@ Crafty.c("Sprite", { // Don't change background if it's not necessary -- this can cause some browsers to reload the image // See [this chrome issue](https://code.google.com/p/chromium/issues/detail?id=102706) - var newBackground = bgColor + " url('" + this.__image + "') no-repeat"; + var newBackground = + bgColor + " url('" + this.__image + "') no-repeat"; if (newBackground !== style.background) { style.background = newBackground; } - style.backgroundPosition = "-" + co.x * hscale + "px -" + co.y * vscale + "px"; + style.backgroundPosition = + "-" + co.x * hscale + "px -" + co.y * vscale + "px"; // style.backgroundSize must be set AFTER style.background! if (vscale !== 1 || hscale !== 1) { - style.backgroundSize = (this.img.width * hscale) + "px" + " " + (this.img.height * vscale) + "px"; + style.backgroundSize = + this.img.width * hscale + + "px" + + " " + + this.img.height * vscale + + "px"; } } else if (e.type === "webgl") { // Write texture coordinates @@ -305,8 +341,9 @@ Crafty.c("Sprite", { * * The coordinate of the slide within the sprite in the format of [x, y, w, h]. */ - sprite: function (x, y, w, h) { - if (typeof x === 'string') { // retrieve location from sprite map by name + sprite: function(x, y, w, h) { + if (typeof x === "string") { + // retrieve location from sprite map by name var temp = this.__map[x]; if (!temp) return this; @@ -318,11 +355,18 @@ Crafty.c("Sprite", { this.__coord = this.__coord || [0, 0, 0, 0]; - this.__coord[0] = x * (this.__tile + this.__padding[0]) + (this.__padBorder ? this.__padding[0] : 0) + this.__trim[0]; - this.__coord[1] = y * (this.__tileh + this.__padding[1]) + (this.__padBorder ? this.__padding[1] : 0) + this.__trim[1]; - if (typeof(w)!=='undefined' && typeof(h)!=='undefined') { + this.__coord[0] = + x * (this.__tile + this.__padding[0]) + + (this.__padBorder ? this.__padding[0] : 0) + + this.__trim[0]; + this.__coord[1] = + y * (this.__tileh + this.__padding[1]) + + (this.__padBorder ? this.__padding[1] : 0) + + this.__trim[1]; + if (typeof w !== "undefined" && typeof h !== "undefined") { this.__coord[2] = this.__trim[2] || w * this.__tile || this.__tile; - this.__coord[3] = this.__trim[3] || h * this.__tileh || this.__tileh; + this.__coord[3] = + this.__trim[3] || h * this.__tileh || this.__tileh; } this.trigger("Invalidate"); @@ -333,7 +377,7 @@ Crafty.c("Sprite", { * #.crop * @comp Sprite * @kind Method - * + * * @sign public this .crop(Number x, Number y, Number w, Number h) * @param x - Offset x position * @param y - Offset y position @@ -350,7 +394,7 @@ Crafty.c("Sprite", { * .crop(40, 40, 22, 23); * ~~~ */ - crop: function (x, y, w, h) { + crop: function(x, y, w, h) { var old = this._mbr || this.pos(); this.__trim = []; this.__trim[0] = x; diff --git a/src/graphics/text.js b/src/graphics/text.js index dbe2aed3..39a58225 100644 --- a/src/graphics/text.js +++ b/src/graphics/text.js @@ -1,11 +1,10 @@ -var Crafty = require('../core/core.js'); - +var Crafty = require("../core/core.js"); /**@ * #Text * @category Graphics * @kind Component - * + * * @trigger Invalidate - when the text is changed * @requires Canvas or DOM * Component to make a text entity. @@ -20,7 +19,7 @@ var Crafty = require('../core/core.js'); * rotate together. * * @note For DOM (but not canvas) text entities, various font settings (such as - * text-decoration) can be set using `.css()` (see DOM component). If you + * text-decoration) can be set using `.css()` (see DOM component). If you * use `.css()` to set the *individual* properties which are controlled by `.textFont()`, * `.textColor()`, or `.textAlign()`, the text component will set these properties internally as well. * However, if you use `.css()` to set shorthand properties such as `font`, these will be ignored by the text component. @@ -36,21 +35,21 @@ Crafty.c("Text", { defaultTextAlign: "left", ready: true, - init: function () { + init: function() { this.requires("2D"); this._textFont = { - "type": "", - "weight": "", - "size": this.defaultSize, - "lineHeight":this.defaultLineHeight, - "family": this.defaultFamily, - "variant": this.defaultVariant + type: "", + weight: "", + size: this.defaultSize, + lineHeight: this.defaultLineHeight, + family: this.defaultFamily, + variant: this.defaultVariant }; this._textAlign = this.defaultTextAlign; }, events: { - "Draw": function (e) { + Draw: function(e) { var font = this._fontString(); if (e.type === "DOM") { @@ -79,11 +78,11 @@ Crafty.c("Text", { // type, weight, size, family, lineHeight, and variant. // For a few hardcoded css properties, set the internal definitions - "SetStyle": function(propertyName) { + SetStyle: function(propertyName) { // could check for DOM component, but this event should only be fired by such an entity! - // Rather than triggering Invalidate on each of these, we rely on css() triggering that event - switch(propertyName) { - case "textAlign": + // Rather than triggering Invalidate on each of these, we rely on css() triggering that event + switch (propertyName) { + case "textAlign": this._textAlign = this._element.style.textAlign; break; case "color": @@ -109,38 +108,36 @@ Crafty.c("Text", { this._textFont.lineHeight = this._element.style.lineHeight; break; } - } }, - remove: function(){ + remove: function() { // Clean up the dynamic text update this.unbind(this._textUpdateEvent, this._dynamicTextUpdate); }, // takes a CSS font-size string and gets the height of the resulting font in px - _getFontHeight: (function(){ + _getFontHeight: (function() { // regex for grabbing the first string of letters var re = /([a-zA-Z]+)\b/; // From the CSS spec. "em" and "ex" are undefined on a canvas. var multipliers = { - "px": 1, - "pt": 4/3, - "pc": 16, - "cm": 96/2.54, - "mm": 96/25.4, - "in": 96, - "em": undefined, - "ex": undefined + px: 1, + pt: 4 / 3, + pc: 16, + cm: 96 / 2.54, + mm: 96 / 25.4, + in: 96, + em: undefined, + ex: undefined }; - return function (font){ + return function(font) { var number = parseFloat(font); var match = re.exec(font); - var unit = match ? match[1] : "px"; + var unit = match ? match[1] : "px"; if (multipliers[unit] !== undefined) return Math.ceil(number * multipliers[unit]); - else - return Math.ceil(number); + else return Math.ceil(number); }; })(), @@ -148,12 +145,12 @@ Crafty.c("Text", { * #.text * @comp Text * @kind Method - * + * * @sign public this .text(String text) * @param text - String of text that will be inserted into the DOM or Canvas element. * * @sign public this .text(Function textGenerator[, Any eventData]) - * @param textGenerator - A function that returns a string. + * @param textGenerator - A function that returns a string. * It will be immediately invoked with the optional eventData in the context of the entity, * with the result used as the text to display. * @param [eventData] - Optional parameter to invoke the function with. @@ -161,9 +158,9 @@ Crafty.c("Text", { * This method will update the text inside the entity. * * If you need to reference attributes on the entity itself you can pass a function instead of a string. - * + * * If dynamic text generation is turned on, the function will then be reevaluated as necessary. - * + * * @see .dynamicTextGeneration * * @example @@ -180,9 +177,9 @@ Crafty.c("Text", { * ~~~ */ _textGenerator: null, - text: function (text, eventData) { + text: function(text, eventData) { if (!(typeof text !== "undefined" && text !== null)) return this._text; - if (typeof (text) === "function"){ + if (typeof text === "function") { this._text = text.call(this, eventData); this._textGenerator = text; } else { @@ -190,8 +187,7 @@ Crafty.c("Text", { this._textGenerator = null; } - if (this.has("Canvas") ) - this._resizeForCanvas(); + if (this.has("Canvas")) this._resizeForCanvas(); this.trigger("Invalidate"); return this; @@ -201,18 +197,18 @@ Crafty.c("Text", { * #.dynamicTextGeneration * @comp Text * @kind Method - * + * * @sign public this .dynamicTextGeneration(bool dynamicTextOn[, string textUpdateEvent]) * @param dynamicTextOn - A flag that indicates whether dyanamic text should be on or off. * @param textUpdateEvent - The name of the event which will trigger text to be updated. Defaults to "UpdateFrame". (This parameter does nothing if dynamicTextOn is false.) * - * Turns on (or off) dynamic text generation for this entity. While dynamic text generation is on, + * Turns on (or off) dynamic text generation for this entity. While dynamic text generation is on, * if the `.text()` method is called with a text generating function, the text will be updated each frame. - * + * * If textUpdateEvent is provided, text generation will be bound to that event instead of "UpdateFrame". * * The text generating function is invoked with the event object parameter, which the event was triggered with. - * + * * @note Dynamic text generation could cause performance issues when the entity is attached to a Canvas layer. * * @example @@ -241,37 +237,49 @@ Crafty.c("Text", { // Calculates the height and width of text on the canvas // Width is found by using the canvas measureText function // Height is only estimated -- it calculates the font size in pixels, and sets the height to 110% of that. - _resizeForCanvas: function(){ + _resizeForCanvas: function() { var ctx = this._drawContext; ctx.font = this._fontString(); this.w = ctx.measureText(this._text).width; - var size = (this._textFont.size || this.defaultSize); + var size = this._textFont.size || this.defaultSize; this.h = 1.1 * this._getFontHeight(size); /* Offset the MBR for text alignment*/ - if (this._textAlign === 'left' || this._textAlign === 'start') { + if (this._textAlign === "left" || this._textAlign === "start") { this.offsetBoundary(0, 0, 0, 0); - } else if (this._textAlign === 'center') { - this.offsetBoundary(this.w/2, 0, -this.w/2, 0); - } else if (this._textAlign === 'end' || this._textAlign === 'right') { + } else if (this._textAlign === "center") { + this.offsetBoundary(this.w / 2, 0, -this.w / 2, 0); + } else if (this._textAlign === "end" || this._textAlign === "right") { this.offsetBoundary(this.w, 0, -this.w, 0); } }, // Returns the font string to use - _fontString: function(){ - return this._textFont.type + ' ' + this._textFont.variant + ' ' + this._textFont.weight + ' ' + this._textFont.size + ' / ' + this._textFont.lineHeight + ' ' + this._textFont.family; + _fontString: function() { + return ( + this._textFont.type + + " " + + this._textFont.variant + + " " + + this._textFont.weight + + " " + + this._textFont.size + + " / " + + this._textFont.lineHeight + + " " + + this._textFont.family + ); }, /**@ * #.textColor * @comp Text * @kind Method - * + * * @sign public this .textColor(String color) * @param color - The color in name, hex, rgb or rgba * - * Change the color of the text. You can use HEX, rgb and rgba colors. + * Change the color of the text. You can use HEX, rgb and rgba colors. * * If you want the text to be transparent, you should use rgba where you can define alphaChannel. * @@ -288,9 +296,18 @@ Crafty.c("Text", { * ~~~ * @see Crafty.assignColor */ - textColor: function (color) { + textColor: function(color) { Crafty.assignColor(color, this); - this._textColor = "rgba(" + this._red + ", " + this._green + ", " + this._blue + ", " + this._strength + ")"; + this._textColor = + "rgba(" + + this._red + + ", " + + this._green + + ", " + + this._blue + + ", " + + this._strength + + ")"; this.trigger("Invalidate"); return this; }, @@ -299,7 +316,7 @@ Crafty.c("Text", { * #.textAlign * @comp Text * @kind Method - * + * * @sign public this .textAlign(String alignment) * @param alignment - The new alignment of the text. * @@ -307,8 +324,7 @@ Crafty.c("Text", { */ textAlign: function(alignment) { this._textAlign = alignment; - if (this.has("Canvas")) - this._resizeForCanvas(); + if (this.has("Canvas")) this._resizeForCanvas(); this.trigger("Invalidate"); return this; }, @@ -317,7 +333,7 @@ Crafty.c("Text", { * #.textFont * @comp Text * @kind Method - * + * * @triggers Invalidate * @sign public this .textFont(String key, * value) * @param key - Property of the entity to modify @@ -339,7 +355,7 @@ Crafty.c("Text", { * Crafty.e("2D, Canvas, Text").textFont("type"); // italic * ~~~ */ - textFont: function (key, value) { + textFont: function(key, value) { if (arguments.length === 1) { //if just the key, return the value if (typeof key === "string") { @@ -348,8 +364,9 @@ Crafty.c("Text", { if (typeof key === "object") { for (var propertyKey in key) { - if(propertyKey === 'family'){ - this._textFont[propertyKey] = "'" + key[propertyKey] + "'"; + if (propertyKey === "family") { + this._textFont[propertyKey] = + "'" + key[propertyKey] + "'"; } else { this._textFont[propertyKey] = key[propertyKey]; } @@ -359,8 +376,7 @@ Crafty.c("Text", { this._textFont[key] = value; } - if (this.has("Canvas") ) - this._resizeForCanvas(); + if (this.has("Canvas")) this._resizeForCanvas(); this.trigger("Invalidate"); return this; @@ -369,14 +385,14 @@ Crafty.c("Text", { * #.unselectable * @comp Text * @kind Method - * + * * @triggers Invalidate * @sign public this .unselectable() * * This method sets the text so that it cannot be selected (highlighted) by dragging. * (Canvas text can never be highlighted, so this only matters for DOM text.) * Works by changing the css property "user-select" and its variants. - * + * * Likewise, this sets the mouseover cursor to be "default" (arrow), not "text" (I-beam) * * @example @@ -384,21 +400,20 @@ Crafty.c("Text", { * Crafty.e("2D, DOM, Text").text('This text cannot be highlighted!').unselectable(); * ~~~ */ - unselectable: function () { + unselectable: function() { // http://stackoverflow.com/questions/826782/css-rule-to-disable-text-selection-highlighting if (this.has("DOM")) { this.css({ - '-webkit-touch-callout': 'none', - '-webkit-user-select': 'none', - '-khtml-user-select': 'none', - '-moz-user-select': 'none', - '-ms-user-select': 'none', - 'user-select': 'none', - 'cursor': 'default' + "-webkit-touch-callout": "none", + "-webkit-user-select": "none", + "-khtml-user-select": "none", + "-moz-user-select": "none", + "-ms-user-select": "none", + "user-select": "none", + cursor: "default" }); this.trigger("Invalidate"); } return this; } - }); diff --git a/src/graphics/viewport.js b/src/graphics/viewport.js index 759ab375..9daafac7 100644 --- a/src/graphics/viewport.js +++ b/src/graphics/viewport.js @@ -1,4 +1,4 @@ -var Crafty = require('../core/core.js'), +var Crafty = require("../core/core.js"), document = window.document; Crafty.extend({ @@ -6,7 +6,7 @@ Crafty.extend({ * #Crafty.viewport * @category Stage * @kind Property - * + * * @trigger ViewportScroll - when the viewport's x or y coordinates change * @trigger ViewportScale - when the viewport's scale changes * @trigger ViewportResize - when the viewport's dimension's change @@ -19,11 +19,11 @@ Crafty.extend({ * * There are multiple camera animation methods available - these are the viewport methods with an animation time parameter and the `follow` method. * Only one animation can run at a time. Starting a new animation will cancel the previous one and the appropriate events will be fired. - * + * * Tip: At any given moment, the stuff that you can see is... - * + * * `x` between `(-Crafty.viewport._x)` and `(-Crafty.viewport._x + (Crafty.viewport._width / Crafty.viewport._scale))` - * + * * `y` between `(-Crafty.viewport._y)` and `(-Crafty.viewport._y + (Crafty.viewport._height / Crafty.viewport._scale))` * * @@ -92,7 +92,7 @@ Crafty.extend({ * This value is the current scale (zoom) of the viewport. When the value is bigger than 1, everything * looks bigger (zoomed in). When the value is less than 1, everything looks smaller (zoomed out). This * does not alter the size of the stage itself, just the magnification of what it shows. - * + * * This is a read-only property: Do not set it directly. Instead, use `Crafty.viewport.scale(...)` * or `Crafty.viewport.zoom(...)` */ @@ -128,7 +128,7 @@ Crafty.extend({ * #Crafty.viewport.scroll * @comp Crafty.viewport * @kind Method - * + * * @sign Crafty.viewport.scroll(String axis, Number val) * @param axis - 'x' or 'y' * @param val - The new absolute position on the axis @@ -143,19 +143,19 @@ Crafty.extend({ * Crafty.viewport.scroll('_x', 500); * ~~~ */ - scroll: function (axis, val) { + scroll: function(axis, val) { this[axis] = val; Crafty.trigger("ViewportScroll"); Crafty.trigger("InvalidateViewport"); }, - rect_object: { _x: 0, _y: 0, _w: 0, _h: 0}, + rect_object: { _x: 0, _y: 0, _w: 0, _h: 0 }, /**@ * #Crafty.viewport.rect * @comp Crafty.viewport * @kind Method - * + * * @sign public Object Crafty.viewport.rect([Object out]) * @param Object out - an optional Object to write the `rect` to * @return a rectangle encompassing the currently visible viewport region. @@ -176,7 +176,7 @@ Crafty.extend({ * rect._h === Crafty.viewport._height / Crafty.viewport._scale * ~~~ */ - rect: function (out) { + rect: function(out) { out = out || this.rect_object; out._x = -this._x; out._y = -this._y; @@ -205,35 +205,35 @@ Crafty.extend({ * Crafty.viewport.pan(100, 100, 2000); * ~~~ */ - pan: (function () { + pan: (function() { var targetX, targetY, startingX, startingY, easing; function updateFrame(e) { easing.tick(e.dt); var v = easing.value(); - Crafty.viewport.x = (1-v) * startingX + v * targetX; - Crafty.viewport.y = (1-v) * startingY + v * targetY; + Crafty.viewport.x = (1 - v) * startingX + v * targetX; + Crafty.viewport.y = (1 - v) * startingY + v * targetY; Crafty.viewport._clamp(); - if (easing.complete){ + if (easing.complete) { stopPan(); Crafty.trigger("CameraAnimationDone"); } } - function stopPan(){ + function stopPan() { Crafty.unbind("UpdateFrame", updateFrame); } Crafty._preBind("StopCamera", stopPan); - return function (dx, dy, time, easingFn) { + return function(dx, dy, time, easingFn) { // Cancel any current camera control Crafty.trigger("StopCamera"); // Handle request to reset - if (dx === 'reset') { - return; + if (dx === "reset") { + return; } startingX = Crafty.viewport._x; @@ -245,7 +245,6 @@ Crafty.extend({ // bind to event, using uniqueBind prevents multiple copies from being bound Crafty.uniqueBind("UpdateFrame", updateFrame); - }; })(), @@ -253,7 +252,7 @@ Crafty.extend({ * #Crafty.viewport.follow * @comp Crafty.viewport * @kind Method - * + * * @sign public void Crafty.viewport.follow(Object target, Number offsetx, Number offsety) * @param Object target - An entity with the 2D component * @param Number offsetx - Follow target's center should be offsetx pixels away from viewport's center. Positive values puts target to the right of the screen. @@ -268,38 +267,53 @@ Crafty.extend({ * Crafty.viewport.follow(ent, 0, 0); * ~~~ */ - follow: (function () { + follow: (function() { var oldTarget, offx, offy; function change() { var scale = Crafty.viewport._scale; - Crafty.viewport.scroll('_x', -(this.x + (this.w / 2) - (Crafty.viewport.width / 2 / scale) - offx * scale)); - Crafty.viewport.scroll('_y', -(this.y + (this.h / 2) - (Crafty.viewport.height / 2 / scale) - offy * scale)); + Crafty.viewport.scroll( + "_x", + -( + this.x + + this.w / 2 - + Crafty.viewport.width / 2 / scale - + offx * scale + ) + ); + Crafty.viewport.scroll( + "_y", + -( + this.y + + this.h / 2 - + Crafty.viewport.height / 2 / scale - + offy * scale + ) + ); Crafty.viewport._clamp(); } - function stopFollow(){ + function stopFollow() { if (oldTarget) { - oldTarget.unbind('Move', change); - oldTarget.unbind('ViewportScale', change); - oldTarget.unbind('ViewportResize', change); + oldTarget.unbind("Move", change); + oldTarget.unbind("ViewportScale", change); + oldTarget.unbind("ViewportResize", change); } } Crafty._preBind("StopCamera", stopFollow); - return function (target, offsetx, offsety) { - if (!target || !target.has('2D')) - return; + return function(target, offsetx, offsety) { + if (!target || !target.has("2D")) return; Crafty.trigger("StopCamera"); oldTarget = target; - offx = (typeof offsetx !== 'undefined') ? offsetx : 0; - offy = (typeof offsety !== 'undefined') ? offsety : 0; + offx = typeof offsetx !== "undefined" ? offsetx : 0; + offy = typeof offsety !== "undefined" ? offsety : 0; - target.bind('Move', change); - target.bind('ViewportScale', change); - target.bind('ViewportResize', change); + target.bind("Move", change); + target.bind("ViewportScale", change); + target.bind("ViewportResize", change); change.call(target); }; })(), @@ -308,7 +322,7 @@ Crafty.extend({ * #Crafty.viewport.centerOn * @comp Crafty.viewport * @kind Method - * + * * @sign public void Crafty.viewport.centerOn(Object target, Number time) * @param Object target - An entity with the 2D component * @param Number time - The duration in ms of the camera motion @@ -321,7 +335,7 @@ Crafty.extend({ * Crafty.viewport.centerOn(ent, 3000); * ~~~ */ - centerOn: function (targ, time) { + centerOn: function(targ, time) { var x = targ.x + Crafty.viewport.x, y = targ.y + Crafty.viewport.y, mid_x = targ.w / 2, @@ -338,7 +352,7 @@ Crafty.extend({ * #Crafty.viewport.zoom * @comp Crafty.viewport * @kind Method - * + * * @sign public void Crafty.viewport.zoom(Number amt, Number cent_x, Number cent_y, Number time[, String|function easingFn]) * @param Number amt - amount to zoom in on the target by (eg. 2, 4, 0.5) * @param Number cent_x - the center to zoom on @@ -356,24 +370,29 @@ Crafty.extend({ * Crafty.viewport.zoom(2, 100, 100, 3000); * ~~~ */ - zoom: (function () { - - - function stopZoom(){ + zoom: (function() { + function stopZoom() { Crafty.unbind("UpdateFrame", updateFrame); } Crafty._preBind("StopCamera", stopZoom); - var startingZoom, finalZoom, finalAmount, startingX, finalX, startingY, finalY, easing; + var startingZoom, + finalZoom, + finalAmount, + startingX, + finalX, + startingY, + finalY, + easing; - function updateFrame(e){ + function updateFrame(e) { var amount, v; easing.tick(e.dt); // The scaling should happen smoothly -- start at 1, end at finalAmount, and at half way scaling should be by finalAmount^(1/2) // Since value goes smoothly from 0 to 1, this fufills those requirements - amount = Math.pow(finalAmount, easing.value() ); + amount = Math.pow(finalAmount, easing.value()); // The viewport should move in such a way that no point reverses // If a and b are the top left/bottom right of the viewport, then the below can be derived from @@ -383,27 +402,25 @@ Crafty.extend({ // b = b_0 * (1-v) + b_f * v. // This is just an arbitrary parameterization of the only sensible path for the viewport corners to take. // And by symmetry they should be parameterized in the same way! So not much choice here. - if (finalAmount === 1) - v = easing.value(); // prevent NaN! If zoom is used this way, it'll just become a pan. - else - v = (1/amount - 1 ) / (1/finalAmount - 1); + if (finalAmount === 1) v = easing.value(); + // prevent NaN! If zoom is used this way, it'll just become a pan. + else v = (1 / amount - 1) / (1 / finalAmount - 1); // Set new scale and viewport position - Crafty.viewport.scale( amount * startingZoom ); - Crafty.viewport.scroll("_x", startingX * (1-v) + finalX * v ); - Crafty.viewport.scroll("_y", startingY * (1-v) + finalY * v ); + Crafty.viewport.scale(amount * startingZoom); + Crafty.viewport.scroll("_x", startingX * (1 - v) + finalX * v); + Crafty.viewport.scroll("_y", startingY * (1 - v) + finalY * v); Crafty.viewport._clamp(); - if (easing.complete){ + if (easing.complete) { stopZoom(); Crafty.trigger("CameraAnimationDone"); } - - } - return function (amt, cent_x, cent_y, time, easingFn){ - if (!amt) { // we're resetting to defaults + return function(amt, cent_x, cent_y, time, easingFn) { + if (!amt) { + // we're resetting to defaults Crafty.viewport.scale(1); return; } @@ -418,25 +435,22 @@ Crafty.extend({ startingZoom = Crafty.viewport._scale; finalAmount = amt; finalZoom = startingZoom * finalAmount; - startingX = Crafty.viewport.x; startingY = Crafty.viewport.y; - finalX = - (cent_x - Crafty.viewport.width / (2 * finalZoom) ); - finalY = - (cent_y - Crafty.viewport.height / (2 * finalZoom) ); + finalX = -(cent_x - Crafty.viewport.width / (2 * finalZoom)); + finalY = -(cent_y - Crafty.viewport.height / (2 * finalZoom)); easing = new Crafty.easing(time, easingFn); Crafty.uniqueBind("UpdateFrame", updateFrame); }; - - })(), /**@ * #Crafty.viewport.scale * @comp Crafty.viewport * @kind Method - * + * * @sign public void Crafty.viewport.scale(Number amt) * @param Number amt - amount to zoom/scale in on the elements * @@ -445,7 +459,7 @@ Crafty.extend({ * When `amt` is 10, that same entity would appear 200 pixels wide (i.e., zoomed in * by a factor of 10), and when `amt` is 0.1, that same entity would be 2 pixels wide * (i.e., zoomed out by a factor of `(1 / 0.1)`). - * + * * If you pass an `amt` of 0, it is treated the same as passing 1, i.e. the scale is reset. * * This method sets the absolute scale, while `Crafty.viewport.zoom` sets the scale relative to the existing value. @@ -456,12 +470,11 @@ Crafty.extend({ * Crafty.viewport.scale(2); // Zoom in -- all entities will appear twice as large. * ~~~ */ - scale: (function () { - return function (amt) { + scale: (function() { + return function(amt) { this._scale = amt ? amt : 1; Crafty.trigger("InvalidateViewport"); Crafty.trigger("ViewportScale"); - }; })(), @@ -469,7 +482,7 @@ Crafty.extend({ * #Crafty.viewport.mouselook * @comp Crafty.viewport * @kind Method - * + * * @sign public void Crafty.viewport.mouselook(Boolean active) * @param Boolean active - Activate or deactivate mouselook * @@ -479,15 +492,15 @@ Crafty.extend({ * * If the user starts a drag, "StopCamera" will be triggered, which will cancel any existing camera animations. */ - mouselook: (function () { + mouselook: (function() { var mouseSystem; var active = false, dragging = false, - lastMouse = {x: 0, y: 0}, - diff = {x: 0, y: 0}; + lastMouse = { x: 0, y: 0 }, + diff = { x: 0, y: 0 }; - function startFn (e) { + function startFn(e) { if (dragging || e.target) return; Crafty.trigger("StopCamera"); @@ -496,7 +509,7 @@ Crafty.extend({ lastMouse.y = e.clientY; dragging = true; } - function moveFn (e) { + function moveFn(e) { if (!dragging) return; diff.x = e.clientX - lastMouse.x; @@ -510,15 +523,15 @@ Crafty.extend({ viewport.y += diff.y / viewport._scale; viewport._clamp(); } - function stopFn (e) { + function stopFn(e) { if (!dragging) return; dragging = false; } - return function (op) { + return function(op) { // TODO: lock pointer events on controls system in future - mouseSystem = Crafty.s('Mouse'); + mouseSystem = Crafty.s("Mouse"); if (op && !active) { mouseSystem.bind("MouseDown", startFn); @@ -534,7 +547,7 @@ Crafty.extend({ }; })(), - _clamp: function () { + _clamp: function() { // clamps the viewport to the viewable area // under no circumstances should the viewport see something outside the boundary of the 'world' if (!this.clampToEntities) return; @@ -544,22 +557,38 @@ Crafty.extend({ bound.max.y *= this._scale; bound.min.y *= this._scale; if (bound.max.x - bound.min.x > Crafty.viewport.width) { - if (Crafty.viewport.x < (-bound.max.x + Crafty.viewport.width) / this._scale) { - Crafty.viewport.x = (-bound.max.x + Crafty.viewport.width) / this._scale; + if ( + Crafty.viewport.x < + (-bound.max.x + Crafty.viewport.width) / this._scale + ) { + Crafty.viewport.x = + (-bound.max.x + Crafty.viewport.width) / this._scale; } else if (Crafty.viewport.x > -bound.min.x) { Crafty.viewport.x = -bound.min.x; } } else { - Crafty.viewport.x = -1 * (bound.min.x + (bound.max.x - bound.min.x) / 2 - Crafty.viewport.width / 2); + Crafty.viewport.x = + -1 * + (bound.min.x + + (bound.max.x - bound.min.x) / 2 - + Crafty.viewport.width / 2); } if (bound.max.y - bound.min.y > Crafty.viewport.height) { - if (Crafty.viewport.y < (-bound.max.y + Crafty.viewport.height) / this._scale) { - Crafty.viewport.y = (-bound.max.y + Crafty.viewport.height) / this._scale; + if ( + Crafty.viewport.y < + (-bound.max.y + Crafty.viewport.height) / this._scale + ) { + Crafty.viewport.y = + (-bound.max.y + Crafty.viewport.height) / this._scale; } else if (Crafty.viewport.y > -bound.min.y) { Crafty.viewport.y = -bound.min.y; } } else { - Crafty.viewport.y = -1 * (bound.min.y + (bound.max.y - bound.min.y) / 2 - Crafty.viewport.height / 2); + Crafty.viewport.y = + -1 * + (bound.min.y + + (bound.max.y - bound.min.y) / 2 - + Crafty.viewport.height / 2); } }, @@ -567,7 +596,7 @@ Crafty.extend({ * #Crafty.viewport.init * @comp Crafty.stage * @kind Method - * + * * @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 @@ -582,20 +611,24 @@ Crafty.extend({ * * @see Crafty.device, Crafty.domHelper, Crafty.stage, Crafty.viewport.reload */ - init: function (w, h, stage_elem) { + init: function(w, h, stage_elem) { // Handle specifying stage_elem without w & h - if (typeof(stage_elem) === 'undefined' && typeof(h) === 'undefined' && - typeof(w) !=='undefined' && typeof(w) !== 'number') { + if ( + typeof stage_elem === "undefined" && + typeof h === "undefined" && + typeof w !== "undefined" && + typeof w !== "number" + ) { stage_elem = w; w = window.innerWidth; h = window.innerHeight; } // Define default graphics layers with default z-layers - Crafty.createLayer("DefaultCanvasLayer", "Canvas", {z: 20}); - Crafty.createLayer("DefaultDOMLayer", "DOM", {z: 30}); - Crafty.createLayer("DefaultWebGLLayer", "WebGL", {z: 10}); - + Crafty.createLayer("DefaultCanvasLayer", "Canvas", { z: 20 }); + Crafty.createLayer("DefaultDOMLayer", "DOM", { z: 30 }); + Crafty.createLayer("DefaultWebGLLayer", "WebGL", { z: 10 }); + // setters+getters for the viewport this._defineViewportProperties(); @@ -610,22 +643,27 @@ Crafty.extend({ this._height = h || window.innerHeight; //check if stage exists - if (typeof stage_elem === 'undefined') - stage_elem = "cr-stage"; + if (typeof stage_elem === "undefined") stage_elem = "cr-stage"; var crstage; - if (typeof stage_elem === 'string') + if (typeof stage_elem === "string") crstage = document.getElementById(stage_elem); - else if (typeof HTMLElement !== "undefined" ? stage_elem instanceof HTMLElement : stage_elem instanceof Element) + else if ( + typeof HTMLElement !== "undefined" + ? stage_elem instanceof HTMLElement + : stage_elem instanceof Element + ) crstage = stage_elem; else - throw new TypeError("stage_elem must be a string or an HTMLElement"); + throw new TypeError( + "stage_elem must be a string or an HTMLElement" + ); /**@ * #Crafty.stage * @category Core * @kind CoreObject - * + * * The stage where all the DOM entities will be placed. */ @@ -633,7 +671,7 @@ Crafty.extend({ * #Crafty.stage.elem * @comp Crafty.stage * @kind Property - * + * * The `#cr-stage` div element. */ @@ -642,7 +680,7 @@ Crafty.extend({ x: 0, y: 0, fullscreen: false, - elem: (crstage ? crstage : document.createElement("div")), + elem: crstage ? crstage : document.createElement("div") }; //fullscreen, stop scrollbars @@ -653,38 +691,42 @@ Crafty.extend({ Crafty.addEvent(this, window, "resize", Crafty.viewport.reload); - Crafty.addEvent(this, window, "blur", function () { + Crafty.addEvent(this, window, "blur", function() { if (Crafty.settings.get("autoPause")) { if (!Crafty._paused) Crafty.pause(); } }); - Crafty.addEvent(this, window, "focus", function () { + Crafty.addEvent(this, window, "focus", function() { if (Crafty._paused && Crafty.settings.get("autoPause")) { Crafty.pause(); } }); //make the stage unselectable - Crafty.settings.register("stageSelectable", function (v) { - Crafty.stage.elem.onselectstart = v ? function () { - return true; - } : function () { - return false; - }; + Crafty.settings.register("stageSelectable", function(v) { + Crafty.stage.elem.onselectstart = v + ? function() { + return true; + } + : function() { + return false; + }; }); Crafty.settings.modify("stageSelectable", false); //make the stage have no context menu - Crafty.settings.register("stageContextMenu", function (v) { - Crafty.stage.elem.oncontextmenu = v ? function () { - return true; - } : function () { - return false; - }; + Crafty.settings.register("stageContextMenu", function(v) { + Crafty.stage.elem.oncontextmenu = v + ? function() { + return true; + } + : function() { + return false; + }; }); Crafty.settings.modify("stageContextMenu", false); - Crafty.settings.register("autoPause", function () {}); + Crafty.settings.register("autoPause", function() {}); Crafty.settings.modify("autoPause", false); //add to the body and give it an ID if not exists @@ -701,12 +743,12 @@ Crafty.extend({ elem.height = this.height + "px"; elem.overflow = "hidden"; - // resize events - Crafty.bind("ViewportResize", function(){Crafty.trigger("InvalidateViewport");}); + Crafty.bind("ViewportResize", function() { + Crafty.trigger("InvalidateViewport"); + }); if (Crafty.mobile) { - // remove default gray highlighting after touch if (typeof elem.webkitTapHighlightColor !== undefined) { elem.webkitTapHighlightColor = "rgba(0,0,0,0)"; @@ -721,13 +763,13 @@ Crafty.extend({ meta.setAttribute("content", "yes"); head.appendChild(meta); - Crafty.addEvent(this, Crafty.stage.elem, "touchmove", function (e) { + Crafty.addEvent(this, Crafty.stage.elem, "touchmove", function( + e + ) { e.preventDefault(); }); - - } - + elem.position = "relative"; //find out the offset position of the stage offset = Crafty.domHelper.innerPosition(Crafty.stage.elem); @@ -737,50 +779,50 @@ Crafty.extend({ Crafty.uniqueBind("ViewportResize", this._resize); }, - _resize: function(){ + _resize: function() { Crafty.stage.elem.style.width = Crafty.viewport.width + "px"; Crafty.stage.elem.style.height = Crafty.viewport.height + "px"; }, // Create setters/getters for x, y, width, height - _defineViewportProperties: function(){ - Object.defineProperty(this, 'x', { - set: function (v) { - this.scroll('_x', v); + _defineViewportProperties: function() { + Object.defineProperty(this, "x", { + set: function(v) { + this.scroll("_x", v); }, - get: function () { + get: function() { return this._x; }, - configurable : true + configurable: true }); - Object.defineProperty(this, 'y', { - set: function (v) { - this.scroll('_y', v); + Object.defineProperty(this, "y", { + set: function(v) { + this.scroll("_y", v); }, - get: function () { + get: function() { return this._y; }, - configurable : true + configurable: true }); - Object.defineProperty(this, 'width', { - set: function (v) { + Object.defineProperty(this, "width", { + set: function(v) { this._width = v; Crafty.trigger("ViewportResize"); }, - get: function () { + get: function() { return this._width; }, - configurable : true + configurable: true }); - Object.defineProperty(this, 'height', { - set: function (v) { + Object.defineProperty(this, "height", { + set: function(v) { this._height = v; Crafty.trigger("ViewportResize"); }, - get: function () { + get: function() { return this._height; }, - configurable : true + configurable: true }); }, @@ -796,12 +838,11 @@ Crafty.extend({ * You should also call this method if you insert custom DOM elements that affect Crafty's stage offset. * */ - reload: function () { + reload: function() { var w = window.innerWidth, - h= window.innerHeight, + h = window.innerHeight, offset; - if (Crafty.stage.fullscreen) { this._width = w; this._height = h; @@ -817,7 +858,7 @@ Crafty.extend({ * #Crafty.viewport.reset * @comp Crafty.stage * @kind Method - * + * * @trigger StopCamera - called to cancel camera animations * * @sign public Crafty.viewport.reset() @@ -825,7 +866,7 @@ Crafty.extend({ * Resets the viewport to starting values, and cancels any existing camera animations. * Called when scene() is run. */ - reset: function () { + reset: function() { Crafty.viewport.mouselook(false); Crafty.trigger("StopCamera"); // Reset viewport position and scale @@ -838,15 +879,19 @@ Crafty.extend({ * #Crafty.viewport.onScreen * @comp Crafty.viewport * @kind Method - * + * * @sign public Crafty.viewport.onScreen(Object rect) * @param rect - A rectangle with field {_x: x_val, _y: y_val, _w: w_val, _h: h_val} * * Test if a rectangle is completely in viewport */ - onScreen: function (rect) { - return Crafty.viewport._x + rect._x + rect._w > 0 && Crafty.viewport._y + rect._y + rect._h > 0 && - Crafty.viewport._x + rect._x < Crafty.viewport.width && Crafty.viewport._y + rect._y < Crafty.viewport.height; + onScreen: function(rect) { + return ( + Crafty.viewport._x + rect._x + rect._w > 0 && + Crafty.viewport._y + rect._y + rect._h > 0 && + Crafty.viewport._x + rect._x < Crafty.viewport.width && + Crafty.viewport._y + rect._y < Crafty.viewport.height + ); } } }); diff --git a/src/graphics/webgl-layer.js b/src/graphics/webgl-layer.js index 8b8a6453..7135ed8f 100644 --- a/src/graphics/webgl-layer.js +++ b/src/graphics/webgl-layer.js @@ -1,12 +1,12 @@ -var Crafty = require('../core/core.js'), +var Crafty = require("../core/core.js"), document = window.document; // Object for abstracting out all the gl calls to handle rendering entities with a particular program -function RenderProgramWrapper(layer, shader){ +function RenderProgramWrapper(layer, shader) { this.shader = shader; this.layer = layer; this.context = layer.context; - this.draw = function() { }; + this.draw = function() {}; this.array_size = 16; this.max_size = 1024; @@ -38,7 +38,9 @@ RenderProgramWrapper.prototype = { this.stride = offset; // Create attribute array of correct size to hold max elements - this._attributeArray = new Float32Array(this.array_size * 4 * this.stride); + this._attributeArray = new Float32Array( + this.array_size * 4 * this.stride + ); this._attributeBuffer = this.context.createBuffer(); this._registryHoles = []; this._registrySize = 0; @@ -67,7 +69,7 @@ RenderProgramWrapper.prototype = { registerEntity: function(e) { if (this._registryHoles.length === 0) { if (this._registrySize >= this.max_size) { - throw ("Number of entities exceeds maximum limit."); + throw "Number of entities exceeds maximum limit."; } else if (this._registrySize >= this.array_size) { this.growArrays(2 * this.array_size); } @@ -101,11 +103,19 @@ RenderProgramWrapper.prototype = { var gl = this.context; gl.useProgram(this.shader); gl.bindBuffer(gl.ARRAY_BUFFER, this._attributeBuffer); - var a, attributes = this.attributes; + var a, + attributes = this.attributes; // Process every attribute for (var i = 0; i < attributes.length; i++) { a = attributes[i]; - gl.vertexAttribPointer(a.location, a.width, a.type, false, this.stride * a.bytes, a.offset * a.bytes); + gl.vertexAttribPointer( + a.location, + a.width, + a.type, + false, + this.stride * a.bytes, + a.offset * a.bytes + ); } // For now, special case the need for texture objects @@ -120,8 +130,7 @@ RenderProgramWrapper.prototype = { // Sets a texture setTexture: function(texture_obj) { // Only needs to be done once - if (this.texture_obj !== undefined) - return; + if (this.texture_obj !== undefined) return; // Set the texture buffer to use texture_obj.setToProgram(this.shader, "uSampler", "uTextureDimensions"); this.texture_obj = texture_obj; @@ -130,7 +139,8 @@ RenderProgramWrapper.prototype = { // adds a set of 6 indices to the index array // Corresponds to 2 triangles that make up a rectangle addIndices: function(offset) { - var index = this._indexArray, l = this.index_pointer; + var index = this._indexArray, + l = this.index_pointer; index[0 + l] = 0 + offset; index[1 + l] = 1 + offset; index[2 + l] = 2 + offset; @@ -140,21 +150,30 @@ RenderProgramWrapper.prototype = { this.index_pointer += 6; }, - // Writes data from the attribute and index arrays to the appropriate buffers, and then calls drawElements. renderBatch: function() { var gl = this.context; gl.bindBuffer(gl.ARRAY_BUFFER, this._attributeBuffer); gl.bufferData(gl.ARRAY_BUFFER, this._attributeArray, gl.STATIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this._indexArray, gl.STATIC_DRAW); + gl.bufferData( + gl.ELEMENT_ARRAY_BUFFER, + this._indexArray, + gl.STATIC_DRAW + ); gl.drawElements(gl.TRIANGLES, this.index_pointer, gl.UNSIGNED_SHORT, 0); }, setViewportUniforms: function(viewport, cameraOptions) { var gl = this.context; gl.useProgram(this.shader); - gl.uniform4f(this.shader.viewport, -viewport._x, -viewport._y, viewport._w , viewport._h ); + gl.uniform4f( + this.shader.viewport, + -viewport._x, + -viewport._y, + viewport._w, + viewport._h + ); }, // Fill in the attribute with the given arguments, cycling through the data if necessary @@ -162,13 +181,16 @@ RenderProgramWrapper.prototype = { // TODO determine if this abstraction is a performance hit! writeVector: function(name, x, y) { var a = this._attribute_table[name]; - var stride = this.stride, offset = a.offset + this.ent_offset * stride, w = a.width; - var l = (arguments.length - 1); + var stride = this.stride, + offset = a.offset + this.ent_offset * stride, + w = a.width; + var l = arguments.length - 1; var data = this._attributeArray; for (var r = 0; r < 4; r++) for (var c = 0; c < w; c++) { - data[offset + stride * r + c] = arguments[(w * r + c) % l + 1]; + data[offset + stride * r + c] = + arguments[((w * r + c) % l) + 1]; } } }; @@ -199,7 +221,7 @@ Crafty._registerLayerTemplate("WebGL", { gl.shaderSource(shader, src); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { - throw (gl.getShaderInfoLog(shader)); + throw gl.getShaderInfoLog(shader); } return shader; }, @@ -208,8 +230,14 @@ Crafty._registerLayerTemplate("WebGL", { // Will compile the two shaders and then link them together _makeProgram: function(shader) { var gl = this.context; - var fragmentShader = this._compileShader(shader.fragmentCode, gl.FRAGMENT_SHADER); - var vertexShader = this._compileShader(shader.vertexCode, gl.VERTEX_SHADER); + var fragmentShader = this._compileShader( + shader.fragmentCode, + gl.FRAGMENT_SHADER + ); + var vertexShader = this._compileShader( + shader.vertexCode, + gl.VERTEX_SHADER + ); var shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertexShader); @@ -217,10 +245,13 @@ Crafty._registerLayerTemplate("WebGL", { gl.linkProgram(shaderProgram); if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - throw ("Could not initialise shaders"); + throw "Could not initialise shaders"; } - shaderProgram.viewport = gl.getUniformLocation(shaderProgram, "uViewport"); + shaderProgram.viewport = gl.getUniformLocation( + shaderProgram, + "uViewport" + ); return shaderProgram; }, @@ -258,18 +289,17 @@ Crafty._registerLayerTemplate("WebGL", { events: { // Respond to init & remove events - "LayerInit": "layerInit", - "LayerRemove": "layerRemove", + LayerInit: "layerInit", + LayerRemove: "layerRemove", // Bind scene rendering (see drawing.js) - "RenderScene": "_render", + RenderScene: "_render", // Listen for pixelart changes - "PixelartSet": "_setPixelart", + PixelartSet: "_setPixelart", // Handle viewport modifications - "ViewportResize": "_resize" + ViewportResize: "_resize" }, layerInit: function() { - //check if we support webgl is supported if (!Crafty.support.webgl) { Crafty.trigger("NoWebGL"); @@ -285,7 +315,7 @@ Crafty._registerLayerTemplate("WebGL", { c = document.createElement("canvas"); c.width = Crafty.viewport.width; c.height = Crafty.viewport.height; - c.style.position = 'absolute'; + c.style.position = "absolute"; c.style.left = "0px"; c.style.top = "0px"; c.style.zIndex = this.options.z; @@ -295,7 +325,11 @@ Crafty._registerLayerTemplate("WebGL", { // Try to get a webgl context var gl; try { - gl = c.getContext("webgl", { premultipliedalpha: true }) || c.getContext("experimental-webgl", { premultipliedalpha: true }); + gl = + c.getContext("webgl", { premultipliedalpha: true }) || + c.getContext("experimental-webgl", { + premultipliedalpha: true + }); gl.viewportWidth = c.width; gl.viewportHeight = c.height; } catch (e) { @@ -380,14 +414,17 @@ Crafty._registerLayerTemplate("WebGL", { visible_gl.length = 0; for (i = 0; i < l; i++) { current = q[i]; - if (current._visible && current.program && (current._drawLayer === this)) { + if ( + current._visible && + current.program && + current._drawLayer === this + ) { visible_gl.push(current); } } visible_gl.sort(this._sort); l = visible_gl.length; - // Now iterate through the z-sorted entities to be rendered // Each entity writes it's data into a typed array // The entities are rendered in batches, where the entire array is copied to a buffer in one operation @@ -413,7 +450,6 @@ Crafty._registerLayerTemplate("WebGL", { if (shaderProgram !== null) { shaderProgram.renderBatch(); } - }, /**@ @@ -421,7 +457,7 @@ Crafty._registerLayerTemplate("WebGL", { * @comp WebGLLayer * @kind Method * @private - * + * * @sign public .dirty(ent) * @param ent - The entity to mark as dirty * @@ -436,7 +472,7 @@ Crafty._registerLayerTemplate("WebGL", { * @comp WebGLLayer * @kind Method * @private - * + * * @sign public .attach(ent) * @param ent - The entity to add * @@ -452,7 +488,7 @@ Crafty._registerLayerTemplate("WebGL", { * @comp WebGLLayer * @kind Method * @private - * + * * @sign public .detach(ent) * @param ent - The entity to remove * @@ -465,6 +501,4 @@ Crafty._registerLayerTemplate("WebGL", { ent.program.unregisterEntity(ent); } } - }); - diff --git a/src/graphics/webgl.js b/src/graphics/webgl.js index f20fc500..cd9d6eb7 100644 --- a/src/graphics/webgl.js +++ b/src/graphics/webgl.js @@ -1,10 +1,10 @@ -var Crafty = require('../core/core.js'); +var Crafty = require("../core/core.js"); /**@ * #WebGL * @category Graphics * @kind Component - * + * * @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 * @@ -29,7 +29,7 @@ Crafty.extend({ * #Crafty.WebGLShader * @category Graphics * @kind Method - * + * * @sign public Crafty.WebGLShader Crafty.WebGLShader(String vertexShaderCode, String fragmentShaderCode, Array attributeList, Function drawCallback(e, entity)) * @param vertexShaderCode - GLSL code for the vertex shader * @param fragmentShaderCode - GLSL code for the fragment shader @@ -119,7 +119,12 @@ Crafty.extend({ * @see Color * @see WebGL */ - WebGLShader: function(vertexCode, fragmentCode, attributeList, drawCallback){ + WebGLShader: function( + vertexCode, + fragmentCode, + attributeList, + drawCallback + ) { this.vertexCode = vertexCode; this.fragmentCode = fragmentCode; this.attributeList = attributeList; @@ -129,7 +134,7 @@ Crafty.extend({ * #Crafty.defaultShader * @category Graphics * @kind Method - * + * * @sign public Crafty.WebGLShader Crafty.defaultShader(String component[, Crafty.WebGLShader shader]) * @param component - Name of the component to assign a default shader to * @param shader - New default shader to assign to a component @@ -153,13 +158,12 @@ Crafty.extend({ * @see WebGL */ defaultShader: function(component, shader) { - this._defaultShaders = (this._defaultShaders || {}); - if (arguments.length === 1 ){ + this._defaultShaders = this._defaultShaders || {}; + if (arguments.length === 1) { return this._defaultShaders[component]; } this._defaultShaders[component] = shader; - }, - + } }); Crafty.c("WebGL", { @@ -170,15 +174,15 @@ Crafty.c("WebGL", { * * The webgl context this entity will be rendered to. */ - init: function () { + init: function() { this.requires("Renderable"); // Attach to webgl layer - if (!this._customLayer){ - this._attachToLayer( Crafty.s("DefaultWebGLLayer") ); + if (!this._customLayer) { + this._attachToLayer(Crafty.s("DefaultWebGLLayer")); } }, - - remove: function(){ + + remove: function() { this._detachFromLayer(); }, @@ -201,13 +205,12 @@ Crafty.c("WebGL", { * @comp WebGL * @kind Method * @private - * + * * @sign public this .draw() * * An internal method to draw the entity on the webgl canvas element. Rather then rendering directly, it writes relevent information into a buffer to allow batch rendering. */ - draw: function () { - + draw: function() { if (!this.ready) return; var pos = this.drawVars.pos; @@ -225,43 +228,46 @@ Crafty.c("WebGL", { // Handle flipX, flipY // (Just swap the positions of e.g. x and x+w) - if (this._flipX ) { - co.x = co.x + co.w; - co.w = - co.w; + if (this._flipX) { + co.x = co.x + co.w; + co.w = -co.w; } - if (this._flipY ) { - co.y = co.y + co.h; - co.h = - co.h; + if (this._flipY) { + co.y = co.y + co.h; + co.h = -co.h; } //Draw entity var gl = this._drawContext; this.drawVars.gl = gl; - var prog = this.drawVars.program = this.program; + var prog = (this.drawVars.program = this.program); // The program might need to refer to the current element's index prog.setCurrentEntity(this); // Write position; x, y, w, h - prog.writeVector("aPosition", - this._x, this._y, - this._x , this._y + this._h, - this._x + this._w, this._y, - this._x + this._w, this._y + this._h + prog.writeVector( + "aPosition", + this._x, + this._y, + this._x, + this._y + this._h, + this._x + this._w, + this._y, + this._x + this._w, + this._y + this._h ); // Write orientation - prog.writeVector("aOrientation", + prog.writeVector( + "aOrientation", this._origin.x + this._x, this._origin.y + this._y, - this._rotation * Math.PI / 180 + (this._rotation * Math.PI) / 180 ); // Write z, alpha - prog.writeVector("aLayer", - this._globalZ, - this._alpha - ); + prog.writeVector("aLayer", this._globalZ, this._alpha); // This should only need to handle *specific* attributes! this.trigger("Draw", this.drawVars); @@ -273,7 +279,7 @@ Crafty.c("WebGL", { }, // v_src is optional, there's a default vertex shader that works for regular rectangular entities - _establishShader: function(compName, shader){ + _establishShader: function(compName, shader) { this.program = this._drawLayer.getProgramWrapper(compName, shader); // Needs to know where in the big array we are! diff --git a/src/inputs/device.js b/src/inputs/device.js index bb1ae867..24c07134 100644 --- a/src/inputs/device.js +++ b/src/inputs/device.js @@ -1,5 +1,4 @@ -var Crafty = require('../core/core.js'); - +var Crafty = require("../core/core.js"); Crafty.extend({ /**@ @@ -26,31 +25,31 @@ Crafty.extend({ * * @param eventData HTML5 DeviceOrientation event */ - _normalizeDeviceOrientation: function (eventData) { + _normalizeDeviceOrientation: function(eventData) { var data; if (window.DeviceOrientationEvent) { data = { // gamma is the left-to-right tilt in degrees, where right is positive - 'tiltLR': eventData.gamma, + tiltLR: eventData.gamma, // beta is the front-to-back tilt in degrees, where front is positive - 'tiltFB': eventData.beta, + tiltFB: eventData.beta, // alpha is the compass direction the device is facing in degrees - 'dir': eventData.alpha, + dir: eventData.alpha, // deviceorientation does not provide this data - 'motUD': null + motUD: null }; } else if (window.OrientationEvent) { data = { // x is the left-to-right tilt from -1 to +1, so we need to convert to degrees - 'tiltLR': eventData.x * 90, + tiltLR: eventData.x * 90, // y is the front-to-back tilt from -1 to +1, so we need to convert to degrees // We also need to invert the value so tilting the device towards us (forward) // results in a positive value. - 'tiltFB': eventData.y * -90, + tiltFB: eventData.y * -90, // MozOrientation does not provide this data - 'dir': null, + dir: null, // z is the vertical acceleration of the device - 'motUD': eventData.z + motUD: eventData.z }; } @@ -60,22 +59,31 @@ Crafty.extend({ /** * @param eventData HTML5 DeviceMotion event */ - _normalizeDeviceMotion: function (eventData) { + _normalizeDeviceMotion: function(eventData) { var acceleration = eventData.accelerationIncludingGravity, - facingUp = (acceleration.z > 0) ? +1 : -1; + facingUp = acceleration.z > 0 ? +1 : -1; var data = { // Grab the acceleration including gravity from the results - 'acceleration': acceleration, - 'rawAcceleration': "[" + Math.round(acceleration.x) + ", " + Math.round(acceleration.y) + ", " + Math.round(acceleration.z) + "]", + acceleration: acceleration, + rawAcceleration: + "[" + + Math.round(acceleration.x) + + ", " + + Math.round(acceleration.y) + + ", " + + Math.round(acceleration.z) + + "]", // Z is the acceleration in the Z axis, and if the device is facing up or down - 'facingUp': facingUp, + facingUp: facingUp, // Convert the value from acceleration to degrees acceleration.x|y 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. - 'tiltLR': Math.round(((acceleration.x) / 9.81) * -90), - 'tiltFB': Math.round(((acceleration.y + 9.81) / 9.81) * 90 * facingUp) + tiltLR: Math.round((acceleration.x / 9.81) * -90), + tiltFB: Math.round( + ((acceleration.y + 9.81) / 9.81) * 90 * facingUp + ) }; Crafty.device._deviceMotionCallback(data); @@ -85,7 +93,7 @@ Crafty.extend({ * #Crafty.device.deviceOrientation * @comp Crafty.device * @kind Method - * + * * @sign public Crafty.device.deviceOrientation(Function callback) * @param callback - Callback method executed once as soon as device orientation is change * @@ -109,15 +117,25 @@ Crafty.extend({ * * See browser support at http://caniuse.com/#search=device orientation. */ - deviceOrientation: function (func) { + deviceOrientation: function(func) { this._deviceOrientationCallback = func; if (Crafty.support.deviceorientation) { if (window.DeviceOrientationEvent) { // Listen for the deviceorientation event and handle DeviceOrientationEvent object - Crafty.addEvent(this, window, 'deviceorientation', this._normalizeDeviceOrientation); + Crafty.addEvent( + this, + window, + "deviceorientation", + this._normalizeDeviceOrientation + ); } else if (window.OrientationEvent) { // Listen for the MozOrientation event and handle OrientationData object - Crafty.addEvent(this, window, 'MozOrientation', this._normalizeDeviceOrientation); + Crafty.addEvent( + this, + window, + "MozOrientation", + this._normalizeDeviceOrientation + ); } } }, @@ -126,7 +144,7 @@ Crafty.extend({ * #Crafty.device.deviceMotion * @comp Crafty.device * @kind Method - * + * * @sign public Crafty.device.deviceMotion(Function callback) * @param callback - Callback method executed once as soon as device motion is change * @@ -151,12 +169,17 @@ Crafty.extend({ * * See browser support at http://caniuse.com/#search=motion. */ - deviceMotion: function (func) { + deviceMotion: function(func) { this._deviceMotionCallback = func; if (Crafty.support.devicemotion) { if (window.DeviceMotionEvent) { // Listen for the devicemotion event and handle DeviceMotionEvent object - Crafty.addEvent(this, window, 'devicemotion', this._normalizeDeviceMotion); + Crafty.addEvent( + this, + window, + "devicemotion", + this._normalizeDeviceMotion + ); } } } diff --git a/src/inputs/dom-events.js b/src/inputs/dom-events.js index c8640514..bcdf9108 100644 --- a/src/inputs/dom-events.js +++ b/src/inputs/dom-events.js @@ -37,7 +37,7 @@ module.exports = { * ~~~ * @see Crafty.removeEvent */ - addEvent: function (ctx, obj, type, callback) { + addEvent: function(ctx, obj, type, callback) { if (arguments.length === 3) { callback = type; type = obj; @@ -46,18 +46,17 @@ module.exports = { //save anonymous function to be able to remove var id = ctx[0] || "", - afn = function (e) { + afn = function(e) { callback.call(ctx, e); }; if (!this._events[id + obj + type + callback]) this._events[id + obj + type + callback] = afn; - else { + else { return; } obj.addEventListener(type, afn, false); - }, /**@ @@ -77,7 +76,7 @@ module.exports = { * * @see Crafty.addEvent */ - removeEvent: function (ctx, obj, type, callback) { + removeEvent: function(ctx, obj, type, callback) { if (arguments.length === 3) { callback = type; type = obj; diff --git a/src/inputs/keyboard.js b/src/inputs/keyboard.js index d97dab0c..9fd63597 100644 --- a/src/inputs/keyboard.js +++ b/src/inputs/keyboard.js @@ -1,4 +1,4 @@ -var Crafty = require('../core/core.js'); +var Crafty = require("../core/core.js"); /**@ * #KeyboardSystem @@ -30,44 +30,56 @@ var Crafty = require('../core/core.js'); * ~~~ * @see KeyboardState, Keyboard */ -Crafty.s("Keyboard", Crafty.extend.call(Crafty.extend.call(new Crafty.__eventDispatcher(), { - _evt: { // evt object to reuse - eventName:'', - key: 0, - which: 0, - originalEvent: null - }, +Crafty.s( + "Keyboard", + Crafty.extend.call( + Crafty.extend.call(new Crafty.__eventDispatcher(), { + _evt: { + // evt object to reuse + eventName: "", + key: 0, + which: 0, + originalEvent: null + }, - prepareEvent: function (e) { - var evt = this._evt; + prepareEvent: function(e) { + var evt = this._evt; - // Normalize event name - var type = e.type; - evt.eventName = type === "keydown" ? "KeyDown" : - type === "keyup" ? "KeyUp" : type; + // Normalize event name + var type = e.type; + evt.eventName = + type === "keydown" + ? "KeyDown" + : type === "keyup" + ? "KeyUp" + : type; - // Normalize key to avoid cross-browser issues - evt.which = e.charCode !== null ? e.charCode : e.keyCode; - evt.key = e.keyCode || e.which; + // Normalize key to avoid cross-browser issues + evt.which = e.charCode !== null ? e.charCode : e.keyCode; + evt.key = e.keyCode || e.which; - // wrap original event into standard Crafty event object - // as original key event's properties are read-only - evt.originalEvent = e; + // wrap original event into standard Crafty event object + // as original key event's properties are read-only + evt.originalEvent = e; - return evt; - }, + return evt; + }, - // this method will be called by KeyboardState iff triggerKey event was valid - triggerKeyEvent: function (eventName, e) { - Crafty.trigger(eventName, e); - }, - - dispatchEvent: function (e) { - var evt = this.prepareEvent(e); - this.triggerKey(evt.eventName, evt); - } -}), Crafty.__keyboardStateTemplate), {}, false); + // this method will be called by KeyboardState iff triggerKey event was valid + triggerKeyEvent: function(eventName, e) { + Crafty.trigger(eventName, e); + }, + dispatchEvent: function(e) { + var evt = this.prepareEvent(e); + this.triggerKey(evt.eventName, evt); + } + }), + Crafty.__keyboardStateTemplate + ), + {}, + false +); /**@ * #Keyboard @@ -105,6 +117,6 @@ Crafty.s("Keyboard", Crafty.extend.call(Crafty.extend.call(new Crafty.__eventDis Crafty.c("Keyboard", { // DEPRECATED: remove in an upcoming release isDown: function(key) { - return Crafty.s('Keyboard').isKeyDown(key); + return Crafty.s("Keyboard").isKeyDown(key); } }); diff --git a/src/inputs/lifecycle.js b/src/inputs/lifecycle.js index 6ea4c5bf..9597cb7e 100644 --- a/src/inputs/lifecycle.js +++ b/src/inputs/lifecycle.js @@ -1,72 +1,221 @@ -var Crafty = require('../core/core.js'), +var Crafty = require("../core/core.js"), document = window.document; // figure out which eventName to listen to for mousewheel events -var mouseWheelEvent = typeof document.onwheel !== 'undefined' ? 'wheel' : // modern browsers - typeof document.onmousewheel !== 'undefined' ? 'mousewheel' : // old Webkit and IE - 'DOMMouseScroll'; // old Firefox +var mouseWheelEvent = + typeof document.onwheel !== "undefined" + ? "wheel" // modern browsers + : typeof document.onmousewheel !== "undefined" + ? "mousewheel" // old Webkit and IE + : "DOMMouseScroll"; // old Firefox //initialize the input events onload -Crafty._preBind("Load", function () { +Crafty._preBind("Load", function() { Crafty.addEvent(this, document.body, "mouseup", Crafty.detectBlur); - Crafty.addEvent(Crafty.s('Keyboard'), window, "blur", Crafty.s('Keyboard').resetKeyDown); - Crafty.addEvent(Crafty.s('Mouse'), window, "mouseup", Crafty.s('Mouse').resetButtonDown); - Crafty.addEvent(Crafty.s('Touch'), window, "touchend", Crafty.s('Touch').resetTouchPoints); - Crafty.addEvent(Crafty.s('Touch'), window, "touchcancel", Crafty.s('Touch').resetTouchPoints); + Crafty.addEvent( + Crafty.s("Keyboard"), + window, + "blur", + Crafty.s("Keyboard").resetKeyDown + ); + Crafty.addEvent( + Crafty.s("Mouse"), + window, + "mouseup", + Crafty.s("Mouse").resetButtonDown + ); + Crafty.addEvent( + Crafty.s("Touch"), + window, + "touchend", + Crafty.s("Touch").resetTouchPoints + ); + Crafty.addEvent( + Crafty.s("Touch"), + window, + "touchcancel", + Crafty.s("Touch").resetTouchPoints + ); - Crafty.addEvent(Crafty.s('Keyboard'), "keydown", Crafty.s('Keyboard').processEvent); - Crafty.addEvent(Crafty.s('Keyboard'), "keyup", Crafty.s('Keyboard').processEvent); + Crafty.addEvent( + Crafty.s("Keyboard"), + "keydown", + Crafty.s("Keyboard").processEvent + ); + Crafty.addEvent( + Crafty.s("Keyboard"), + "keyup", + Crafty.s("Keyboard").processEvent + ); - Crafty.addEvent(Crafty.s('Mouse'), Crafty.stage.elem, "mousedown", Crafty.s('Mouse').processEvent); - Crafty.addEvent(Crafty.s('Mouse'), Crafty.stage.elem, "mouseup", Crafty.s('Mouse').processEvent); - Crafty.addEvent(Crafty.s('Mouse'), Crafty.stage.elem, "mousemove", Crafty.s('Mouse').processEvent); - Crafty.addEvent(Crafty.s('Mouse'), Crafty.stage.elem, "click", Crafty.s('Mouse').processEvent); - Crafty.addEvent(Crafty.s('Mouse'), Crafty.stage.elem, "dblclick", Crafty.s('Mouse').processEvent); + Crafty.addEvent( + Crafty.s("Mouse"), + Crafty.stage.elem, + "mousedown", + Crafty.s("Mouse").processEvent + ); + Crafty.addEvent( + Crafty.s("Mouse"), + Crafty.stage.elem, + "mouseup", + Crafty.s("Mouse").processEvent + ); + Crafty.addEvent( + Crafty.s("Mouse"), + Crafty.stage.elem, + "mousemove", + Crafty.s("Mouse").processEvent + ); + Crafty.addEvent( + Crafty.s("Mouse"), + Crafty.stage.elem, + "click", + Crafty.s("Mouse").processEvent + ); + Crafty.addEvent( + Crafty.s("Mouse"), + Crafty.stage.elem, + "dblclick", + Crafty.s("Mouse").processEvent + ); Crafty.addEvent(this, Crafty.stage.elem, "touchstart", this._touchDispatch); Crafty.addEvent(this, Crafty.stage.elem, "touchmove", this._touchDispatch); Crafty.addEvent(this, Crafty.stage.elem, "touchend", this._touchDispatch); - Crafty.addEvent(this, Crafty.stage.elem, "touchcancel", this._touchDispatch); + Crafty.addEvent( + this, + Crafty.stage.elem, + "touchcancel", + this._touchDispatch + ); Crafty.addEvent(this, Crafty.stage.elem, "touchleave", this._touchDispatch); - Crafty.addEvent(Crafty.s('MouseWheel'), Crafty.stage.elem, mouseWheelEvent, Crafty.s('MouseWheel').processEvent); + Crafty.addEvent( + Crafty.s("MouseWheel"), + Crafty.stage.elem, + mouseWheelEvent, + Crafty.s("MouseWheel").processEvent + ); }); -Crafty.bind("Pause", function () { +Crafty.bind("Pause", function() { // Reset pressed keys and buttons - Crafty.s('Keyboard').resetKeyDown(); - Crafty.s('Mouse').resetButtonDown(); + Crafty.s("Keyboard").resetKeyDown(); + Crafty.s("Mouse").resetButtonDown(); }); -Crafty._preBind("CraftyStop", function () { +Crafty._preBind("CraftyStop", function() { // Reset pressed keys and buttons - Crafty.s('Keyboard').resetKeyDown(); - Crafty.s('Mouse').resetButtonDown(); + Crafty.s("Keyboard").resetKeyDown(); + Crafty.s("Mouse").resetButtonDown(); }); -Crafty._preBind("CraftyStop", function () { +Crafty._preBind("CraftyStop", function() { Crafty.removeEvent(this, document.body, "mouseup", Crafty.detectBlur); - Crafty.removeEvent(Crafty.s('Keyboard'), window, "blur", Crafty.s('Keyboard').resetKeyDown); - Crafty.removeEvent(Crafty.s('Mouse'), window, "mouseup", Crafty.s('Mouse').resetButtonDown); - Crafty.removeEvent(Crafty.s('Touch'), window, "touchend", Crafty.s('Touch').resetTouchPoints); - Crafty.removeEvent(Crafty.s('Touch'), window, "touchcancel", Crafty.s('Touch').resetTouchPoints); + Crafty.removeEvent( + Crafty.s("Keyboard"), + window, + "blur", + Crafty.s("Keyboard").resetKeyDown + ); + Crafty.removeEvent( + Crafty.s("Mouse"), + window, + "mouseup", + Crafty.s("Mouse").resetButtonDown + ); + Crafty.removeEvent( + Crafty.s("Touch"), + window, + "touchend", + Crafty.s("Touch").resetTouchPoints + ); + Crafty.removeEvent( + Crafty.s("Touch"), + window, + "touchcancel", + Crafty.s("Touch").resetTouchPoints + ); - Crafty.removeEvent(Crafty.s('Keyboard'), "keydown", Crafty.s('Keyboard').processEvent); - Crafty.removeEvent(Crafty.s('Keyboard'), "keyup", Crafty.s('Keyboard').processEvent); + Crafty.removeEvent( + Crafty.s("Keyboard"), + "keydown", + Crafty.s("Keyboard").processEvent + ); + Crafty.removeEvent( + Crafty.s("Keyboard"), + "keyup", + Crafty.s("Keyboard").processEvent + ); if (Crafty.stage) { - Crafty.removeEvent(Crafty.s('Mouse'), Crafty.stage.elem, "mousedown", Crafty.s('Mouse').processEvent); - Crafty.removeEvent(Crafty.s('Mouse'), Crafty.stage.elem, "mouseup", Crafty.s('Mouse').processEvent); - Crafty.removeEvent(Crafty.s('Mouse'), Crafty.stage.elem, "mousemove", Crafty.s('Mouse').processEvent); - Crafty.removeEvent(Crafty.s('Mouse'), Crafty.stage.elem, "click", Crafty.s('Mouse').processEvent); - Crafty.removeEvent(Crafty.s('Mouse'), Crafty.stage.elem, "dblclick", Crafty.s('Mouse').processEvent); + Crafty.removeEvent( + Crafty.s("Mouse"), + Crafty.stage.elem, + "mousedown", + Crafty.s("Mouse").processEvent + ); + Crafty.removeEvent( + Crafty.s("Mouse"), + Crafty.stage.elem, + "mouseup", + Crafty.s("Mouse").processEvent + ); + Crafty.removeEvent( + Crafty.s("Mouse"), + Crafty.stage.elem, + "mousemove", + Crafty.s("Mouse").processEvent + ); + Crafty.removeEvent( + Crafty.s("Mouse"), + Crafty.stage.elem, + "click", + Crafty.s("Mouse").processEvent + ); + Crafty.removeEvent( + Crafty.s("Mouse"), + Crafty.stage.elem, + "dblclick", + Crafty.s("Mouse").processEvent + ); - Crafty.removeEvent(this, Crafty.stage.elem, "touchstart", this._touchDispatch); - Crafty.removeEvent(this, Crafty.stage.elem, "touchmove", this._touchDispatch); - Crafty.removeEvent(this, Crafty.stage.elem, "touchend", this._touchDispatch); - Crafty.removeEvent(this, Crafty.stage.elem, "touchcancel", this._touchDispatch); - Crafty.removeEvent(this, Crafty.stage.elem, "touchleave", this._touchDispatch); + Crafty.removeEvent( + this, + Crafty.stage.elem, + "touchstart", + this._touchDispatch + ); + Crafty.removeEvent( + this, + Crafty.stage.elem, + "touchmove", + this._touchDispatch + ); + Crafty.removeEvent( + this, + Crafty.stage.elem, + "touchend", + this._touchDispatch + ); + Crafty.removeEvent( + this, + Crafty.stage.elem, + "touchcancel", + this._touchDispatch + ); + Crafty.removeEvent( + this, + Crafty.stage.elem, + "touchleave", + this._touchDispatch + ); - Crafty.removeEvent(Crafty.s('MouseWheel'), Crafty.stage.elem, mouseWheelEvent, Crafty.s('MouseWheel').processEvent); + Crafty.removeEvent( + Crafty.s("MouseWheel"), + Crafty.stage.elem, + mouseWheelEvent, + Crafty.s("MouseWheel").processEvent + ); } }); diff --git a/src/inputs/mouse.js b/src/inputs/mouse.js index c95785f9..3aac26fb 100644 --- a/src/inputs/mouse.js +++ b/src/inputs/mouse.js @@ -1,4 +1,4 @@ -var Crafty = require('../core/core.js'); +var Crafty = require("../core/core.js"); /**@ * #MouseWheel @@ -96,56 +96,71 @@ var Crafty = require('../core/core.js'); * ~~~ */ -Crafty.s("MouseWheel", Crafty.extend.call(new Crafty.__eventDispatcher(), { - _evt: { // evt object to reuse - eventName:'', - direction: 0, - target: null, - clientX: 0, // DEPRECATED: remove in upcoming release - clientY: 0, // DEPRECATED: remove in upcoming release - realX: 0, - realY: 0, - originalEvent: null - }, - _mouseSystem: null, - - prepareEvent: function (e) { - var mouseSystem = this._mouseSystem; - if (!mouseSystem) this._mouseSystem = mouseSystem = Crafty.s('Mouse'); - - var evt = this._evt; - - // normalize eventName - evt.eventName = "MouseWheelScroll"; - - // normalize direction - evt.direction = (e.detail < 0 || e.wheelDelta > 0 || e.deltaY < 0) ? 1 : -1; - - // copy screen coordinates - // only browsers supporting `wheel` event contain mouse coordinates - // DEPRECATED: remove in upcoming release - evt.clientX = e.clientX !== undefined ? e.clientX : mouseSystem.lastMouseEvent.clientX; - evt.clientY = e.clientY !== undefined ? e.clientY : mouseSystem.lastMouseEvent.clientY; - - // augment mouse event with real coordinates - Crafty.translatePointerEventCoordinates(e, evt); - - // augment mouse event with target entity - evt.target = mouseSystem.mouseObjs ? Crafty.findPointerEventTargetByComponent("Mouse", e) : null; - - // wrap original event into standard Crafty event object - evt.originalEvent = e; - - return evt; - }, - - dispatchEvent: function (e) { - var evt = this.prepareEvent(e); - // trigger event - Crafty.trigger("MouseWheelScroll", evt); - } -}), {}, false); - +Crafty.s( + "MouseWheel", + Crafty.extend.call(new Crafty.__eventDispatcher(), { + _evt: { + // evt object to reuse + eventName: "", + direction: 0, + target: null, + clientX: 0, // DEPRECATED: remove in upcoming release + clientY: 0, // DEPRECATED: remove in upcoming release + realX: 0, + realY: 0, + originalEvent: null + }, + _mouseSystem: null, + + prepareEvent: function(e) { + var mouseSystem = this._mouseSystem; + if (!mouseSystem) + this._mouseSystem = mouseSystem = Crafty.s("Mouse"); + + var evt = this._evt; + + // normalize eventName + evt.eventName = "MouseWheelScroll"; + + // normalize direction + evt.direction = + e.detail < 0 || e.wheelDelta > 0 || e.deltaY < 0 ? 1 : -1; + + // copy screen coordinates + // only browsers supporting `wheel` event contain mouse coordinates + // DEPRECATED: remove in upcoming release + evt.clientX = + e.clientX !== undefined + ? e.clientX + : mouseSystem.lastMouseEvent.clientX; + evt.clientY = + e.clientY !== undefined + ? e.clientY + : mouseSystem.lastMouseEvent.clientY; + + // augment mouse event with real coordinates + Crafty.translatePointerEventCoordinates(e, evt); + + // augment mouse event with target entity + evt.target = mouseSystem.mouseObjs + ? Crafty.findPointerEventTargetByComponent("Mouse", e) + : null; + + // wrap original event into standard Crafty event object + evt.originalEvent = e; + + return evt; + }, + + dispatchEvent: function(e) { + var evt = this.prepareEvent(e); + // trigger event + Crafty.trigger("MouseWheelScroll", evt); + } + }), + {}, + false +); /**@ * #MouseSystem @@ -181,105 +196,127 @@ Crafty.s("MouseWheel", Crafty.extend.call(new Crafty.__eventDispatcher(), { * @see MouseState, Mouse * @see Crafty.multitouch */ -Crafty.s("Mouse", Crafty.extend.call(Crafty.extend.call(new Crafty.__eventDispatcher(), { - normedEventNames: { - "mousedown": "MouseDown", - "mouseup": "MouseUp", - "dblclick": "DoubleClick", - "click": "Click", - "mousemove": "MouseMove" - }, - - _evt: { // evt object to reuse - eventName:'', - mouseButton: -1, - target: null, - clientX: 0, // DEPRECATED: remove in upcoming release - clientY: 0, // DEPRECATED: remove in upcoming release - realX: 0, - realY: 0, - originalEvent: null - }, - - // Indicates how many entities have the Mouse component, for performance optimization - // Mouse events are still routed to Crafty.s('Mouse') even if there are no entities with Mouse component - mouseObjs: 0, - - // current entity that is moused over - over: null, - - prepareEvent: function (e) { - var evt = this._evt; - - // Normalize event name - var type = e.type; - evt.eventName = this.normedEventNames[type] || type; - - // Normalize button according to http://unixpapa.com/js/mouse.html - if (typeof e.which === 'undefined') { - evt.mouseButton = (e.button < 2) ? Crafty.mouseButtons.LEFT : ((e.button === 4) ? Crafty.mouseButtons.MIDDLE : Crafty.mouseButtons.RIGHT); - } else { - evt.mouseButton = (e.which < 2) ? Crafty.mouseButtons.LEFT : ((e.which === 2) ? Crafty.mouseButtons.MIDDLE : Crafty.mouseButtons.RIGHT); - } - - // copy screen coordinates - // DEPRECATED: remove in upcoming release - evt.clientX = e.clientX; - evt.clientY = e.clientY; - - // augment mouse event with real coordinates - Crafty.translatePointerEventCoordinates(e, evt); - - // augment mouse event with target entity - evt.target = this.mouseObjs ? Crafty.findPointerEventTargetByComponent("Mouse", e) : null; - - // wrap original event into standard Crafty event object - evt.originalEvent = e; - - return evt; - }, - - // this method will be called by MouseState iff triggerMouse event was valid - triggerMouseEvent: function (eventName, e) { - // trigger event on MouseSystem itself - this.trigger(eventName, e); - - // special case: MouseOver & MouseOut - var over = this.over, closest = e.target; - if (eventName === "MouseMove" && over !== closest) { // MouseOver target changed - // if old MouseOver target wasn't null, send MouseOut - if (over) { - e.eventName = "MouseOut"; - e.target = over; - over.trigger("MouseOut", e); - e.eventName = "MouseMove"; - e.target = closest; +Crafty.s( + "Mouse", + Crafty.extend.call( + Crafty.extend.call(new Crafty.__eventDispatcher(), { + normedEventNames: { + mousedown: "MouseDown", + mouseup: "MouseUp", + dblclick: "DoubleClick", + click: "Click", + mousemove: "MouseMove" + }, + + _evt: { + // evt object to reuse + eventName: "", + mouseButton: -1, + target: null, + clientX: 0, // DEPRECATED: remove in upcoming release + clientY: 0, // DEPRECATED: remove in upcoming release + realX: 0, + realY: 0, + originalEvent: null + }, + + // Indicates how many entities have the Mouse component, for performance optimization + // Mouse events are still routed to Crafty.s('Mouse') even if there are no entities with Mouse component + mouseObjs: 0, + + // current entity that is moused over + over: null, + + prepareEvent: function(e) { + var evt = this._evt; + + // Normalize event name + var type = e.type; + evt.eventName = this.normedEventNames[type] || type; + + // Normalize button according to http://unixpapa.com/js/mouse.html + if (typeof e.which === "undefined") { + evt.mouseButton = + e.button < 2 + ? Crafty.mouseButtons.LEFT + : e.button === 4 + ? Crafty.mouseButtons.MIDDLE + : Crafty.mouseButtons.RIGHT; + } else { + evt.mouseButton = + e.which < 2 + ? Crafty.mouseButtons.LEFT + : e.which === 2 + ? Crafty.mouseButtons.MIDDLE + : Crafty.mouseButtons.RIGHT; + } + + // copy screen coordinates + // DEPRECATED: remove in upcoming release + evt.clientX = e.clientX; + evt.clientY = e.clientY; + + // augment mouse event with real coordinates + Crafty.translatePointerEventCoordinates(e, evt); + + // augment mouse event with target entity + evt.target = this.mouseObjs + ? Crafty.findPointerEventTargetByComponent("Mouse", e) + : null; + + // wrap original event into standard Crafty event object + evt.originalEvent = e; + + return evt; + }, + + // this method will be called by MouseState iff triggerMouse event was valid + triggerMouseEvent: function(eventName, e) { + // trigger event on MouseSystem itself + this.trigger(eventName, e); + + // special case: MouseOver & MouseOut + var over = this.over, + closest = e.target; + if (eventName === "MouseMove" && over !== closest) { + // MouseOver target changed + // if old MouseOver target wasn't null, send MouseOut + if (over) { + e.eventName = "MouseOut"; + e.target = over; + over.trigger("MouseOut", e); + e.eventName = "MouseMove"; + e.target = closest; + } + + // save new over entity + this.over = closest; + + // if new MouseOver target isn't null, send MouseOver + if (closest) { + e.eventName = "MouseOver"; + closest.trigger("MouseOver", e); + e.eventName = "MouseMove"; + } + } + + // TODO: move routing of events in future to controls system, make it similar to KeyboardSystem + // try to find closest element that will also receive mouse event, whatever the event is + if (closest) { + closest.trigger(eventName, e); + } + }, + + dispatchEvent: function(e) { + var evt = this.prepareEvent(e); + this.triggerMouse(evt.eventName, evt); } - - // save new over entity - this.over = closest; - - // if new MouseOver target isn't null, send MouseOver - if (closest) { - e.eventName = "MouseOver"; - closest.trigger("MouseOver", e); - e.eventName = "MouseMove"; - } - } - - // TODO: move routing of events in future to controls system, make it similar to KeyboardSystem - // try to find closest element that will also receive mouse event, whatever the event is - if (closest) { - closest.trigger(eventName, e); - } - }, - - dispatchEvent: function (e) { - var evt = this.prepareEvent(e); - this.triggerMouse(evt.eventName, evt); - } -}), Crafty.__mouseStateTemplate), {}, false); - + }), + Crafty.__mouseStateTemplate + ), + {}, + false +); /**@ * #Mouse @@ -324,7 +361,7 @@ Crafty.s("Mouse", Crafty.extend.call(Crafty.extend.call(new Crafty.__eventDispat Crafty.c("Mouse", { required: "AreaMap", - init: function () { + init: function() { Crafty.s("Mouse").mouseObjs++; }, remove: function() { @@ -349,10 +386,10 @@ Crafty.c("MouseDrag", { required: "Mouse", events: { - "MouseDown": "_ondown" + MouseDown: "_ondown" }, - init: function () { + init: function() { // TODO: remove this and instead lock on pointer control events in future // bind the this object for listeners called with the MouseSystem as the this object this._ondown = this._ondown.bind(this); @@ -361,20 +398,20 @@ Crafty.c("MouseDrag", { }, // When dragging is enabled, this method is bound to the MouseDown crafty event - _ondown: function (e) { + _ondown: function(e) { if (e.mouseButton !== Crafty.mouseButtons.LEFT) return; this.startDrag(e); }, // While a drag is occurring, this method is bound to the mousemove DOM event - _ondrag: function (e) { + _ondrag: function(e) { // ignore invalid 0 position - strange problem on ipad if (!this._dragging || e.realX === 0 || e.realY === 0) return false; this.trigger("Dragging", e); }, // While a drag is occurring, this method is bound to mouseup DOM event - _onup: function (e) { + _onup: function(e) { if (e.mouseButton !== Crafty.mouseButtons.LEFT) return; this.stopDrag(e); }, @@ -390,7 +427,7 @@ Crafty.c("MouseDrag", { * * @see .stopDrag */ - startDrag: function (e) { + startDrag: function(e) { if (this._dragging) return; this._dragging = true; @@ -414,7 +451,7 @@ Crafty.c("MouseDrag", { * * @see .startDrag */ - stopDrag: function (e) { + stopDrag: function(e) { if (!this._dragging) return; this._dragging = false; diff --git a/src/inputs/pointer.js b/src/inputs/pointer.js index 7a868c8a..fb47b96d 100644 --- a/src/inputs/pointer.js +++ b/src/inputs/pointer.js @@ -1,4 +1,4 @@ -var Crafty = require('../core/core.js'); +var Crafty = require("../core/core.js"); Crafty.extend({ /**@ @@ -26,19 +26,25 @@ Crafty.extend({ * For having a detection area specified for the enity, add the AreaMap component to the entity expected to be found. * */ - findPointerEventTargetByComponent: function (comp, x, y) { + findPointerEventTargetByComponent: function(comp, x, y) { var tar = x.target || x.srcElement || Crafty.stage.elem; - y = typeof y !== 'undefined' ? y : x.clientY; - x = typeof x.clientX !== 'undefined' ? x.clientX : x; + y = typeof y !== "undefined" ? y : x.clientY; + x = typeof x.clientX !== "undefined" ? x.clientX : x; - var closest = null, current, q, l, i, pos, maxz = -Infinity; + var closest = null, + current, + q, + l, + i, + pos, + maxz = -Infinity; //if it's a DOM element with component we are done if (tar.nodeName !== "CANVAS") { - while (typeof (tar.id) !== 'string' && tar.id.indexOf('ent') === -1) { + while (typeof tar.id !== "string" && tar.id.indexOf("ent") === -1) { tar = tar.parentNode; } - var ent = Crafty(parseInt(tar.id.replace('ent', ''), 10)); + var ent = Crafty(parseInt(tar.id.replace("ent", ""), 10)); pos = Crafty.domHelper.translate(x, y, ent._drawLayer); if (ent.__c[comp] && ent.isAt(pos.x, pos.y)) { closest = ent; @@ -47,7 +53,6 @@ Crafty.extend({ //else we search for an entity with component if (!closest) { - // Loop through each layer for (var layerIndex in Crafty._drawLayers) { var layer = Crafty._drawLayers[layerIndex]; @@ -66,8 +71,13 @@ Crafty.extend({ for (i = 0, l = q.length; i < l; ++i) { current = q[i]; - if (current._visible && current._drawLayer === layer && current._globalZ > maxz && - current.__c[comp] && current.isAt(pos.x, pos.y)) { + if ( + current._visible && + current._drawLayer === layer && + current._globalZ > maxz && + current.__c[comp] && + current.isAt(pos.x, pos.y) + ) { maxz = current._globalZ; closest = current; } @@ -96,18 +106,23 @@ Crafty.extend({ * * @see Crafty.domHelper#Crafty.domHelper.translate */ - translatePointerEventCoordinates: function (e, out) { + translatePointerEventCoordinates: function(e, out) { out = out || e; // Find the Crafty position in the default coordinate set, // disregard the fact that the pointer event was related to a specific layer. - var pos = Crafty.domHelper.translate(e.clientX, e.clientY, undefined, this.__pointerPos); + var pos = Crafty.domHelper.translate( + e.clientX, + e.clientY, + undefined, + this.__pointerPos + ); // Set the mouse position based on standard viewport coordinates out.realX = pos.x; out.realY = pos.y; }, - __pointerPos: {x: 0, y: 0} // object to reuse + __pointerPos: { x: 0, y: 0 } // object to reuse }); /**@ @@ -123,23 +138,23 @@ Crafty.extend({ * @see Crafty.findPointerEventTargetByComponent */ Crafty.c("AreaMap", { - init: function () { + init: function() { if (this.has("Renderable") && this._drawLayer) { this._drawLayer._pointerEntities++; } }, - remove: function (isDestruction) { + remove: function(isDestruction) { if (!isDestruction && this.has("Renderable") && this._drawLayer) { this._drawLayer._pointerEntities--; } }, events: { - "LayerAttached": function (layer) { + LayerAttached: function(layer) { layer._pointerEntities++; }, - "LayerDetached": function (layer) { + LayerDetached: function(layer) { layer._pointerEntities--; } }, @@ -181,7 +196,7 @@ Crafty.c("AreaMap", { * * @see Crafty.polygon */ - areaMap: function (poly) { + areaMap: function(poly) { //create polygon if (arguments.length > 1) { //convert args to array to create polygon @@ -214,8 +229,11 @@ Crafty.c("AreaMap", { * @see Crafty.multitouch */ Crafty.c("Button", { - init: function () { - var req = (!Crafty.mobile || (Crafty.mobile && !Crafty.multitouch())) ? "Mouse" : "Touch"; + init: function() { + var req = + !Crafty.mobile || (Crafty.mobile && !Crafty.multitouch()) + ? "Mouse" + : "Touch"; this.requires(req); } }); diff --git a/src/inputs/touch.js b/src/inputs/touch.js index ce6b7690..f8734c3a 100644 --- a/src/inputs/touch.js +++ b/src/inputs/touch.js @@ -1,4 +1,4 @@ -var Crafty = require('../core/core.js'); +var Crafty = require("../core/core.js"); Crafty.extend({ /**@ @@ -40,20 +40,20 @@ Crafty.extend({ * @see Touch * @see Mouse */ - multitouch: function (bool) { + multitouch: function(bool) { if (typeof bool !== "boolean") return this._multitouch; this._multitouch = bool; return this; }, - _multitouch: false, + _multitouch: false, - _touchDispatch: (function () { + _touchDispatch: (function() { var touchSystem; var startX = 0, // keeps track of start touch location startY = 0; // keeps track of start touch location - function mimicMouse (e) { + function mimicMouse(e) { var type, first; if (e.type === "touchstart") type = "mousedown"; else if (e.type === "touchmove") type = "mousemove"; @@ -66,34 +66,54 @@ Crafty.extend({ first = e.changedTouches[0]; } var simulatedEvent = document.createEvent("MouseEvent"); - simulatedEvent.initMouseEvent(type, true, true, window, 1, + simulatedEvent.initMouseEvent( + type, + true, + true, + window, + 1, first.screenX, first.screenY, first.clientX, first.clientY, - false, false, false, false, 0, e.relatedTarget + false, + false, + false, + false, + 0, + e.relatedTarget ); first.target.dispatchEvent(simulatedEvent); // trigger click when it should be triggered - if (type === 'mousedown') { + if (type === "mousedown") { startX = first.clientX; startY = first.clientY; - } else if (type === 'mouseup') { + } else if (type === "mouseup") { var diffX = first.clientX - startX, diffY = first.clientY - startY; // make sure that distance between touchstart and touchend smaller than some threshold, // e.g. <= 16 px if (diffX * diffX + diffY * diffY <= 256) { - type = 'click'; + type = "click"; simulatedEvent = document.createEvent("MouseEvent"); - simulatedEvent.initMouseEvent(type, true, true, window, 1, + simulatedEvent.initMouseEvent( + type, + true, + true, + window, + 1, first.screenX, first.screenY, first.clientX, first.clientY, - false, false, false, false, 0, e.relatedTarget + false, + false, + false, + false, + 0, + e.relatedTarget ); first.target.dispatchEvent(simulatedEvent); } @@ -104,9 +124,9 @@ Crafty.extend({ return function(e) { // either dispatch real touch events if (Crafty._multitouch) { - if (!touchSystem) touchSystem = Crafty.s('Touch'); + if (!touchSystem) touchSystem = Crafty.s("Touch"); touchSystem.processEvent(e); - // or mimic mouse events + // or mimic mouse events } else { mimicMouse(e); } @@ -139,107 +159,128 @@ Crafty.extend({ * @see TouchState, Touch * @see Crafty.multitouch */ -Crafty.s("Touch", Crafty.extend.call(Crafty.extend.call(new Crafty.__eventDispatcher(), { - normedEventNames: { - "touchstart": "TouchStart", - "touchmove": "TouchMove", - "touchend": "TouchEnd", - "touchcancel": "TouchCancel" // touchcancel is treated as touchend, but triggers a TouchCancel event - }, +Crafty.s( + "Touch", + Crafty.extend.call( + Crafty.extend.call(new Crafty.__eventDispatcher(), { + normedEventNames: { + touchstart: "TouchStart", + touchmove: "TouchMove", + touchend: "TouchEnd", + touchcancel: "TouchCancel" // touchcancel is treated as touchend, but triggers a TouchCancel event + }, - _evt: { // evt object to reuse - eventName:'', - identifier: -1, - target: null, - entity: null, // DEPRECATED: remove this in upcoming release - realX: 0, - realY: 0, - originalEvent: null - }, + _evt: { + // evt object to reuse + eventName: "", + identifier: -1, + target: null, + entity: null, // DEPRECATED: remove this in upcoming release + realX: 0, + realY: 0, + originalEvent: null + }, - // Indicates how many entities have the Touch component, for performance optimization - // Touch events are still routed to Crafty.s('Touch') even if there are no entities with Touch component - touchObjs: 0, + // Indicates how many entities have the Touch component, for performance optimization + // Touch events are still routed to Crafty.s('Touch') even if there are no entities with Touch component + touchObjs: 0, - // current entites that are pointed at - overs: {}, + // current entites that are pointed at + overs: {}, - prepareEvent: function (e, type) { - var evt = this._evt; + prepareEvent: function(e, type) { + var evt = this._evt; - // Normalize event name - evt.eventName = this.normedEventNames[type] || type; + // Normalize event name + evt.eventName = this.normedEventNames[type] || type; - // copy identifier - evt.identifier = e.identifier; + // copy identifier + evt.identifier = e.identifier; - // augment touch event with real coordinates - Crafty.translatePointerEventCoordinates(e, evt); + // augment touch event with real coordinates + Crafty.translatePointerEventCoordinates(e, evt); - // augment touch event with target entity - evt.target = this.touchObjs ? Crafty.findPointerEventTargetByComponent("Touch", e) : null; - // DEPRECATED: remove this in upcoming release - evt.entity = evt.target; + // augment touch event with target entity + evt.target = this.touchObjs + ? Crafty.findPointerEventTargetByComponent("Touch", e) + : null; + // DEPRECATED: remove this in upcoming release + evt.entity = evt.target; - return evt; - }, + return evt; + }, - // this method will be called by TouchState iff triggerTouch event was valid - triggerTouchEvent: function(eventName, e) { - // trigger event on TouchSystem itself - this.trigger(eventName, e); + // this method will be called by TouchState iff triggerTouch event was valid + triggerTouchEvent: function(eventName, e) { + // trigger event on TouchSystem itself + this.trigger(eventName, e); - var identifier = e.identifier, - closest = e.target, - over = this.overs[identifier]; + var identifier = e.identifier, + closest = e.target, + over = this.overs[identifier]; - if (over) { // if old TouchOver target wasn't null, send TouchOut - if ((eventName === "TouchMove" && over !== closest) || // if TouchOver target changed - eventName === "TouchEnd" || eventName === "TouchCancel") { // or TouchEnd occurred + if (over) { + // if old TouchOver target wasn't null, send TouchOut + if ( + (eventName === "TouchMove" && over !== closest) || // if TouchOver target changed + eventName === "TouchEnd" || + eventName === "TouchCancel" + ) { + // or TouchEnd occurred - e.eventName = "TouchOut"; - e.target = over; - e.entity = over; // DEPRECATED: remove this in upcoming release - over.trigger("TouchOut", e); - e.eventName = eventName; - e.target = closest; - e.entity = closest; // DEPRECATED: remove this in upcoming release + e.eventName = "TouchOut"; + e.target = over; + e.entity = over; // DEPRECATED: remove this in upcoming release + over.trigger("TouchOut", e); + e.eventName = eventName; + e.target = closest; + e.entity = closest; // DEPRECATED: remove this in upcoming release - // delete old over entity - delete this.overs[identifier]; - } - } + // delete old over entity + delete this.overs[identifier]; + } + } - // TODO: move routing of events in future to controls system, make it similar to KeyboardSystem - // try to find closest element that will also receive touch event, whatever the event is - if (closest) { - closest.trigger(eventName, e); - } + // TODO: move routing of events in future to controls system, make it similar to KeyboardSystem + // try to find closest element that will also receive touch event, whatever the event is + if (closest) { + closest.trigger(eventName, e); + } - if (closest) { // if new TouchOver target isn't null, send TouchOver - if (eventName === "TouchStart" || // if TouchStart occurred - (eventName === "TouchMove" && over !== closest)) { // or TouchOver target changed + if (closest) { + // if new TouchOver target isn't null, send TouchOver + if ( + eventName === "TouchStart" || // if TouchStart occurred + (eventName === "TouchMove" && over !== closest) + ) { + // or TouchOver target changed - e.eventName = "TouchOver"; - closest.trigger("TouchOver", e); - e.eventName = eventName; + e.eventName = "TouchOver"; + closest.trigger("TouchOver", e); + e.eventName = eventName; - // save new over entity - this.overs[identifier] = closest; - } - } - }, + // save new over entity + this.overs[identifier] = closest; + } + } + }, - dispatchEvent: function (e) { - var evt, touches = e.changedTouches; - for (var i = 0, l = touches.length; i < l; i++) { - evt = this.prepareEvent(touches[i], e.type); - // wrap original event into standard Crafty event object - evt.originalEvent = e; - this.triggerTouch(evt.eventName, evt); - } - } -}), Crafty.__touchStateTemplate), {}, false); + dispatchEvent: function(e) { + var evt, + touches = e.changedTouches; + for (var i = 0, l = touches.length; i < l; i++) { + evt = this.prepareEvent(touches[i], e.type); + // wrap original event into standard Crafty event object + evt.originalEvent = e; + this.triggerTouch(evt.eventName, evt); + } + } + }), + Crafty.__touchStateTemplate + ), + {}, + false +); /**@ * #Touch @@ -298,10 +339,10 @@ Crafty.s("Touch", Crafty.extend.call(Crafty.extend.call(new Crafty.__eventDispat */ Crafty.c("Touch", { required: "AreaMap", - init: function () { - Crafty.s('Touch').touchObjs++; + init: function() { + Crafty.s("Touch").touchObjs++; }, - remove: function () { - Crafty.s('Touch').touchObjs--; + remove: function() { + Crafty.s("Touch").touchObjs--; } }); diff --git a/src/inputs/util.js b/src/inputs/util.js index f1e3a81e..0c34cc78 100644 --- a/src/inputs/util.js +++ b/src/inputs/util.js @@ -1,37 +1,43 @@ -var Crafty = require('../core/core.js'); +var Crafty = require("../core/core.js"); // common base functionality for all EventDispatchers Crafty.__eventDispatcher = function EventDispatcher() {}; Crafty.__eventDispatcher.prototype = { // this method should be setup as the entry callback for DOM events - processEvent: function (e) { + processEvent: function(e) { this.dispatchEvent(e); return this.preventBubbling(e); }, // main method that handles logic of incoming DOM events // to be implemented by instances - dispatchEvent: function (e) { + dispatchEvent: function(e) { // normalize the event and prepare it for dispatching to Crafty, a system or entities // set e.eventName to proper event to be triggered - // dispatch the element to Crafty, the proper system or entities // find the entity to dispatch to (e.g. mouse events) or dispatch it globally (e.g. key events) }, // prevents interaction with page (e.g. scrolling of page), if DOM events target Crafty's stage // automatically called for all incoming DOM events - preventBubbling: function (e) { + preventBubbling: function(e) { // only prevent something if DOM event targets Crafty's stage // prevent bubbling up for all events except key events backspace and F1-F12. // prevent default actions for all events except key events backspace and F1-F12 and except actions on INPUT and TEXTAREA. // Among others this prevent the arrow keys from scrolling the parent page of an iframe hosting the game - if (Crafty.selected && !(e.key === 8 || e.key >= 112 && e.key <= 135)) { + if ( + Crafty.selected && + !(e.key === 8 || (e.key >= 112 && e.key <= 135)) + ) { if (e.stopPropagation) e.stopPropagation(); else e.cancelBubble = true; // Don't prevent default actions if target node is input or textarea. - if (!e.target || (e.target.nodeName !== 'INPUT' && e.target.nodeName !== 'TEXTAREA')) { + if ( + !e.target || + (e.target.nodeName !== "INPUT" && + e.target.nodeName !== "TEXTAREA") + ) { if (e.preventDefault) { e.preventDefault(); } else { @@ -44,7 +50,6 @@ Crafty.__eventDispatcher.prototype = { } }; - Crafty.extend({ /**@ * #Crafty.selected @@ -64,9 +69,12 @@ Crafty.extend({ */ selected: true, - detectBlur: function (e) { - var selected = ((e.clientX > Crafty.stage.x && e.clientX < Crafty.stage.x + Crafty.viewport.width) && - (e.clientY > Crafty.stage.y && e.clientY < Crafty.stage.y + Crafty.viewport.height)); + detectBlur: function(e) { + var selected = + e.clientX > Crafty.stage.x && + e.clientX < Crafty.stage.x + Crafty.viewport.width && + (e.clientY > Crafty.stage.y && + e.clientY < Crafty.stage.y + Crafty.viewport.height); if (!Crafty.selected && selected) { Crafty.trigger("CraftyFocus"); diff --git a/src/isometric/diamond-iso.js b/src/isometric/diamond-iso.js index ca37705a..812c6d64 100644 --- a/src/isometric/diamond-iso.js +++ b/src/isometric/diamond-iso.js @@ -1,12 +1,11 @@ -var Crafty = require('../core/core.js'); - +var Crafty = require("../core/core.js"); Crafty.extend({ /**@ * #Crafty.diamondIso * @category 2D * @kind CoreObject - * + * * Place entities in a 45deg diamond isometric fashion. It is similar to isometric but has another grid locations * In this mode, the x axis and y axis are aligned to the edges of tiles with x increasing being down and to the * right and y being down and to the left. @@ -16,8 +15,8 @@ Crafty.extend({ width: 0, height: 0 }, - getTileDimensions: function(){ - return {w:this._tile.width,h:this._tile.height}; + getTileDimensions: function() { + return { w: this._tile.width, h: this._tile.height }; }, _map: { width: 0, @@ -28,14 +27,14 @@ Crafty.extend({ y: 0 }, _tiles: [], - getTile: function(x,y,z){ + getTile: function(x, y, z) { return this._tiles[x][y][z]; }, /**@ * #Crafty.diamondIso.init * @comp Crafty.diamondIso * @kind Method - * + * * @sign public this Crafty.diamondIso.init(Number tileWidth,Number tileHeight,Number mapWidth,Number mapHeight) * @param tileWidth - The size of base tile width's grid space in Pixel * @param tileHeight - The size of base tile height grid space in Pixel @@ -55,29 +54,29 @@ Crafty.extend({ * * @see Crafty.diamondIso.place */ - init: function (tw, th, mw, mh, x, y) { + init: function(tw, th, mw, mh, x, y) { this._tile.width = parseInt(tw, 10); this._tile.height = parseInt(th, 10) || parseInt(tw, 10) / 2; this._tile.r = this._tile.width / this._tile.height; this._map.width = parseInt(mw, 10); this._map.height = parseInt(mh, 10) || parseInt(mw, 10); - for (var i=0; i0 && x0 && y 0 && x < this._map.width; + var inY = y > 0 && y < this._map.height; + if (!inX || !inY) { return undefined; } return { @@ -171,42 +174,44 @@ Crafty.extend({ y: ~~y }; }, - getOverlappingTiles: function(x,y){ - /* This will find all of the tiles that might be at a given x/y in pixels */ - var pos = this.px2pos(x,y); - var tiles = []; - var _x = ~~pos.x; - var _y = ~~pos.y; - var maxX = this._map.width - _x; - var maxY = this._map.height - _y; - var furthest = Math.min(maxX, maxY); - var obj = this._tiles[_x][_y][1]; - if (obj){ - tiles.push(obj); - } - for (var i=1; i 0 ? height : width / 2; //Setup width/2 if height isn't set return this; @@ -51,7 +50,7 @@ Crafty.extend({ * #Crafty.isometric.place * @comp Crafty.isometric * @kind Method - * + * * @sign public this Crafty.isometric.place(Number x, Number y, Number z, Entity tile) * @param x - The `x` position to place the tile * @param y - The `y` position to place the tile @@ -68,7 +67,7 @@ Crafty.extend({ * * @see Crafty.isometric.size */ - place: function (x, y, z, obj) { + place: function(x, y, z, obj) { var pos = this.pos2px(x, y); pos.top -= z * (this._tile.height / 2); obj.x = pos.left + Crafty.viewport._x; @@ -80,7 +79,7 @@ Crafty.extend({ * #Crafty.isometric.pos2px * @comp Crafty.isometric * @kind Method - * + * * @sign public Object Crafty.isometric.pos2px(Number x,Number y) * @param x - A position along the x axis * @param y - A position along the y axis @@ -94,17 +93,17 @@ Crafty.extend({ * var position = iso.pos2px(100,100); //Object { left=12800, top=4800} * ~~~ */ - pos2px: function (x, y) { + pos2px: function(x, y) { return { left: x * this._tile.width + (y & 1) * (this._tile.width / 2), - top: y * this._tile.height / 2 + top: (y * this._tile.height) / 2 }; }, /**@ * #Crafty.isometric.px2pos * @comp Crafty.isometric * @kind Method - * + * * @sign public Object Crafty.isometric.px2pos(Number left,Number top) * @param top - Offset from the top in pixels * @param left - Offset from the left in pixels @@ -119,17 +118,17 @@ Crafty.extend({ * Crafty.log(px); //Object { x=100, y=100} * ~~~ */ - px2pos: function (left, top) { + px2pos: function(left, top) { return { x: -Math.ceil(-left / this._tile.width - (top & 1) * 0.5), - y: top / this._tile.height * 2 + y: (top / this._tile.height) * 2 }; }, /**@ * #Crafty.isometric.centerAt * @comp Crafty.isometric * @kind Method - * + * * @sign public Obect Crafty.isometric.centerAt() * @returns An object with `top` and `left` fields represneting the viewport's current center * @@ -146,16 +145,28 @@ Crafty.extend({ * Crafty.log(iso.centerAt()); * ~~~ */ - centerAt: function (x, y) { + centerAt: function(x, y) { if (typeof x === "number" && typeof y === "number") { var center = this.pos2px(x, y); - Crafty.viewport._x = -center.left + Crafty.viewport.width / 2 - this._tile.width / 2; - Crafty.viewport._y = -center.top + Crafty.viewport.height / 2 - this._tile.height / 2; + Crafty.viewport._x = + -center.left + + Crafty.viewport.width / 2 - + this._tile.width / 2; + Crafty.viewport._y = + -center.top + + Crafty.viewport.height / 2 - + this._tile.height / 2; return this; } else { return { - top: -Crafty.viewport._y + Crafty.viewport.height / 2 - this._tile.height / 2, - left: -Crafty.viewport._x + Crafty.viewport.width / 2 - this._tile.width / 2 + top: + -Crafty.viewport._y + + Crafty.viewport.height / 2 - + this._tile.height / 2, + left: + -Crafty.viewport._x + + Crafty.viewport.width / 2 - + this._tile.width / 2 }; } }, @@ -163,7 +174,7 @@ Crafty.extend({ * #Crafty.isometric.area * @comp Crafty.isometric * @kind Method - * + * * @sign public Object Crafty.isometric.area() * @return An obect with `x` and `y` fields, each of which have a start and end field. * In other words, the object has this structure: `{x:{start Number,end Number},y:{start Number,end Number}}` @@ -181,11 +192,17 @@ Crafty.extend({ * } * ~~~ */ - area: function () { + area: function() { //Get the center Point in the viewport var center = this.centerAt(); - var start = this.px2pos(-center.left + Crafty.viewport.width / 2, -center.top + Crafty.viewport.height / 2); - var end = this.px2pos(-center.left - Crafty.viewport.width / 2, -center.top - Crafty.viewport.height / 2); + var start = this.px2pos( + -center.left + Crafty.viewport.width / 2, + -center.top + Crafty.viewport.height / 2 + ); + var end = this.px2pos( + -center.left - Crafty.viewport.width / 2, + -center.top - Crafty.viewport.height / 2 + ); return { x: { start: start.x, diff --git a/src/sound/sound.js b/src/sound/sound.js index 17d44cf7..f833334c 100644 --- a/src/sound/sound.js +++ b/src/sound/sound.js @@ -1,4 +1,4 @@ -var Crafty = require('../core/core.js'), +var Crafty = require("../core/core.js"), document = window.document; Crafty.extend({ @@ -16,10 +16,10 @@ Crafty.extend({ * The maximum number of sounds that can be played simultaneously is defined by Crafty.audio.maxChannels. The default value is 7. */ audio: { - sounds: {}, supported: null, - codecs: { // Chart from jPlayer + codecs: { + // Chart from jPlayer ogg: 'audio/ogg; codecs="vorbis"', //OGG wav: 'audio/wav; codecs="1"', // PCM webma: 'audio/webm; codecs="vorbis"', // WEBM @@ -33,11 +33,10 @@ Crafty.extend({ /** * Function to setup supported formats **/ - _canPlay: function () { + _canPlay: function() { this.supported = {}; // Without support, no formats are supported - if (!Crafty.support.audio) - return; + if (!Crafty.support.audio) return; var audio = this.audioElement(), canplay; for (var i in this.codecs) { @@ -48,36 +47,34 @@ Crafty.extend({ this.supported[i] = false; } } - }, /**@ * #Crafty.audio.supports * @comp Crafty.audio * @kind Method - * + * * @sign public this Crafty.audio.supports(String extension) * @param extension - A file extension to check audio support for * * Return true if the browser thinks it can play the given file type, otherwise false */ - supports: function (extension) { + supports: function(extension) { // Build cache of supported formats, if necessary - if (this.supported === null) - this._canPlay(); + if (this.supported === null) this._canPlay(); - if (this.supported[extension]) - return true; - else - return false; + if (this.supported[extension]) return true; + else return false; }, /** * Function to get an Audio Element **/ - audioElement: function () { + audioElement: function() { //IE does not support Audio Object - return typeof Audio !== 'undefined' ? new Audio("") : document.createElement('audio'); + return typeof Audio !== "undefined" + ? new Audio("") + : document.createElement("audio"); }, /**@ @@ -85,7 +82,7 @@ Crafty.extend({ * @comp Crafty.audio * @kind Method * @private - * + * * @sign public this Crafty.audio.create(String id, String url) * @param id - A string to refer to sounds * @param url - A string pointing to the sound file @@ -94,11 +91,10 @@ Crafty.extend({ * * If the sound file extension is not supported, returns false; otherwise, returns the audio asset. */ - create: function (id, path) { + create: function(id, path) { //check extension, return if not supported - var ext = path.substr(path.lastIndexOf('.') + 1).toLowerCase(); - if (!this.supports(ext)) - return false; + var ext = path.substr(path.lastIndexOf(".") + 1).toLowerCase(); + if (!this.supports(ext)) return false; //initiate the audio element var audio = this.audioElement(); @@ -115,14 +111,13 @@ Crafty.extend({ volume: Crafty.audio.volume }; return this.sounds[id]; - }, /**@ * #Crafty.audio.add * @comp Crafty.audio * @kind Method - * + * * @sign public this Crafty.audio.add(String id, String url) * @param id - A string to refer to sounds * @param url - A string pointing to the sound file @@ -162,18 +157,16 @@ Crafty.extend({ * Crafty.audio.add("jump", "sounds/jump.mp3"); * ~~~ */ - add: function (id, url) { - if (!Crafty.support.audio) - return; + add: function(id, url) { + if (!Crafty.support.audio) return; - var src, - a; + var src, a; if (arguments.length === 1 && typeof id === "object") { for (var i in id) { for (src in id[i]) { a = Crafty.audio.create(i, id[i][src]); - if (a){ + if (a) { break; } } @@ -187,11 +180,9 @@ Crafty.extend({ if (typeof url === "object") { for (src in url) { a = Crafty.audio.create(id, url[src]); - if (a) - break; + if (a) break; } } - } return a; }, @@ -222,31 +213,27 @@ Crafty.extend({ * Crafty.audio.play("explosion",1,0.5); //play sound once with volume of 50% * ~~~ */ - play: function (id, repeat, volume) { + play: function(id, repeat, volume) { if (repeat === 0 || !Crafty.support.audio || !this.sounds[id]) return; var s = this.sounds[id]; var c = this.getOpenChannel(); - if (!c) - return null; + if (!c) return null; c.id = id; c.repeat = repeat; var a = c.obj; - c.volume = s.volume = s.obj.volume = volume || Crafty.audio.volume; a.volume = s.volume; a.src = s.obj.src; - if (this.muted) - a.volume = 0; + if (this.muted) a.volume = 0; a.play(); s.played++; - c.onEnd = function () { + c.onEnd = function() { if (s.played < c.repeat || c.repeat === -1) { - if (this.currentTime) - this.currentTime = 0; + if (this.currentTime) this.currentTime = 0; this.play(); s.played++; } else { @@ -258,41 +245,40 @@ Crafty.extend({ id: c.id }); } - }; a.addEventListener("ended", c.onEnd, true); return a; }, - - /**@ * #Crafty.audio.setChannels * @comp Crafty.audio * @kind Method - * + * * @sign public this Crafty.audio.setChannels(Number n) * @param n - The maximum number of channels */ maxChannels: 7, - setChannels: function (n) { + setChannels: function(n) { this.maxChannels = n; - if (n < this.channels.length) - this.channels.length = n; + if (n < this.channels.length) this.channels.length = n; }, channels: [], // Finds an unused audio element, marks it as in use, and return it. - getOpenChannel: function () { + getOpenChannel: function() { for (var i = 0; i < this.channels.length; i++) { var chan = this.channels[i]; - /* + /* * Second test looks for stuff that's out of use, * but fallen foul of Chromium bug 280417 */ - if (chan.active === false || - chan.obj.ended && chan.repeat <= this.sounds[chan.id].played) { + if ( + chan.active === false || + (chan.obj.ended && + chan.repeat <= this.sounds[chan.id].played) + ) { chan.active = true; return chan; } @@ -303,7 +289,7 @@ Crafty.extend({ obj: this.audioElement(), active: true, // Checks that the channel is being used to play sound id - _is: function (id) { + _is: function(id) { return this.id === id && this.active; } }; @@ -318,13 +304,13 @@ Crafty.extend({ * #Crafty.audio.remove * @comp Crafty.audio * @kind Method - * + * * @sign public this Crafty.audio.remove([String id]) * @param id - A string to refer to sounds * * Will stop the sound and remove all references to the audio object allowing the browser to free the memory. * If no id is given, all sounds will be removed. - * + * * This function uses audio path set in Crafty.path in order to remove sound from the assets object. * * @example @@ -332,16 +318,17 @@ Crafty.extend({ * Crafty.audio.remove("walk"); * ~~~ */ - remove: function (id) { - if (!Crafty.support.audio) - return; + remove: function(id) { + if (!Crafty.support.audio) return; - var s, filename, audioFolder = Crafty.paths().audio; + var s, + filename, + audioFolder = Crafty.paths().audio; if (!id) { for (var i in this.sounds) { s = this.sounds[i]; - filename = s.obj.src.split('/').pop(); + filename = s.obj.src.split("/").pop(); Crafty.audio.stop(id); delete Crafty.assets[audioFolder + filename]; delete Crafty.assets[s.obj.src]; @@ -349,11 +336,10 @@ Crafty.extend({ } return; } - if (!this.sounds[id]) - return; + if (!this.sounds[id]) return; s = this.sounds[id]; - filename = s.obj.src.split('/').pop(); + filename = s.obj.src.split("/").pop(); Crafty.audio.stop(id); delete Crafty.assets[audioFolder + filename]; delete Crafty.assets[s.obj.src]; @@ -363,7 +349,7 @@ Crafty.extend({ * #Crafty.audio.stop * @comp Crafty.audio * @kind Method - * + * * @sign public this Crafty.audio.stop([Number ID]) * * Stops any playing sound. if id is not set, stop all sounds which are playing @@ -375,13 +361,12 @@ Crafty.extend({ * * ~~~ */ - stop: function (id) { - if (!Crafty.support.audio) - return; + stop: function(id) { + if (!Crafty.support.audio) return; var c; for (var i in this.channels) { c = this.channels[i]; - if ( (!id && c.active) || c._is(id) ) { + if ((!id && c.active) || c._is(id)) { c.active = false; c.obj.pause(); } @@ -393,14 +378,13 @@ Crafty.extend({ * @comp Crafty.audio * @kind Method * @kind private - * + * * @sign public this Crafty.audio._mute([Boolean mute]) * * Mute or unmute every Audio instance that is playing. */ - _mute: function (mute) { - if (!Crafty.support.audio) - return; + _mute: function(mute) { + if (!Crafty.support.audio) return; var c; for (var i in this.channels) { c = this.channels[i]; @@ -412,7 +396,7 @@ Crafty.extend({ * #Crafty.audio.toggleMute * @comp Crafty.audio * @kind Method - * + * * @sign public this Crafty.audio.toggleMute() * * Mute or unmute every Audio instance that is playing. Toggles between @@ -424,19 +408,18 @@ Crafty.extend({ * Crafty.audio.toggleMute(); * ~~~ */ - toggleMute: function () { + toggleMute: function() { if (!this.muted) { this._mute(true); } else { this._mute(false); } - }, /**@ * #Crafty.audio.mute * @comp Crafty.audio * @kind Method - * + * * @sign public this Crafty.audio.mute() * * Mute every Audio instance that is playing. @@ -446,14 +429,14 @@ Crafty.extend({ * Crafty.audio.mute(); * ~~~ */ - mute: function () { + mute: function() { this._mute(true); }, /**@ * #Crafty.audio.unmute * @comp Crafty.audio * @kind Method - * + * * @sign public this Crafty.audio.unmute() * * Unmute every Audio instance that is playing. @@ -463,7 +446,7 @@ Crafty.extend({ * Crafty.audio.unmute(); * ~~~ */ - unmute: function () { + unmute: function() { this._mute(false); }, @@ -471,7 +454,7 @@ Crafty.extend({ * #Crafty.audio.pause * @comp Crafty.audio * @kind Method - * + * * @sign public this Crafty.audio.pause(string ID) * @param {string} id - The id of the audio object to pause * @@ -483,23 +466,20 @@ Crafty.extend({ * ~~~ * */ - pause: function (id) { - if (!Crafty.support.audio || !id || !this.sounds[id]) - return; + pause: function(id) { + if (!Crafty.support.audio || !id || !this.sounds[id]) return; var c; for (var i in this.channels) { c = this.channels[i]; - if (c._is(id) && !c.obj.paused) - c.obj.pause(); + if (c._is(id) && !c.obj.paused) c.obj.pause(); } - }, /**@ * #Crafty.audio.unpause * @comp Crafty.audio * @kind Method - * + * * @sign public this Crafty.audio.unpause(string ID) * @param {string} id - The id of the audio object to unpause * @@ -511,14 +491,12 @@ Crafty.extend({ * ~~~ * */ - unpause: function (id) { - if (!Crafty.support.audio || !id || !this.sounds[id]) - return; + unpause: function(id) { + if (!Crafty.support.audio || !id || !this.sounds[id]) return; var c; for (var i in this.channels) { c = this.channels[i]; - if (c._is(id) && c.obj.paused) - c.obj.play(); + if (c._is(id) && c.obj.paused) c.obj.play(); } }, @@ -526,7 +504,7 @@ Crafty.extend({ * #Crafty.audio.togglePause * @comp Crafty.audio * @kind Method - * + * * @sign public this Crafty.audio.togglePause(string ID) * @param {string} id - The id of the audio object to pause/ * @@ -538,9 +516,8 @@ Crafty.extend({ * ~~~ * */ - togglePause: function (id) { - if (!Crafty.support.audio || !id || !this.sounds[id]) - return; + togglePause: function(id) { + if (!Crafty.support.audio || !id || !this.sounds[id]) return; var c; for (var i in this.channels) { c = this.channels[i]; @@ -558,7 +535,7 @@ Crafty.extend({ * #Crafty.audio.isPlaying * @comp Crafty.audio * @kind Method - * + * * @sign public Boolean Crafty.audio.isPlaying(string ID) * @param {string} id - The id of the audio object * @return a Boolean indicating whether the audio is playing or not @@ -572,12 +549,10 @@ Crafty.extend({ * */ isPlaying: function(id) { - if (!Crafty.support.audio) - return false; + if (!Crafty.support.audio) return false; for (var i in this.channels) { - if (this.channels[i]._is(id)) - return true; + if (this.channels[i]._is(id)) return true; } return false; diff --git a/src/spatial/2d.js b/src/spatial/2d.js index 2ac16652..fdddb03a 100644 --- a/src/spatial/2d.js +++ b/src/spatial/2d.js @@ -1,4 +1,4 @@ -var Crafty = require('../core/core.js'); +var Crafty = require("../core/core.js"); var M = Math, //Mc = M.cos, @@ -10,7 +10,7 @@ var M = Math, * #2D * @category 2D * @kind Component - * + * * Component for any entity that has a position on the stage. * @trigger Move - when the entity has moved - { _x:Number, _y:Number, _w:Number, _h:Number } - Old position * @trigger Invalidate - when the entity needs to be redrawn @@ -23,7 +23,7 @@ Crafty.c("2D", { * #.x * @comp 2D * @kind Property - * + * * The `x` position on the stage. When modified, will automatically be redrawn. * Is actually a getter/setter so when using this value for calculations and not modifying it, * use the `._x` property. @@ -33,7 +33,7 @@ Crafty.c("2D", { /**@ * #.y * @kind Property - * + * * @comp 2D * The `y` position on the stage. When modified, will automatically be redrawn. * Is actually a getter/setter so when using this value for calculations and not modifying it, @@ -45,7 +45,7 @@ Crafty.c("2D", { * #.w * @comp 2D * @kind Property - * + * * The width of the entity. When modified, will automatically be redrawn. * Is actually a getter/setter so when using this value for calculations and not modifying it, * use the `._w` property. @@ -58,7 +58,7 @@ Crafty.c("2D", { * #.h * @comp 2D * @kind Property - * + * * The height of the entity. When modified, will automatically be redrawn. * Is actually a getter/setter so when using this value for calculations and not modifying it, * use the `._h` property. @@ -72,7 +72,7 @@ Crafty.c("2D", { * #.z * @comp 2D * @kind Property - * + * * The `z` index on the stage. When modified, will automatically be redrawn. * Is actually a getter/setter so when using this value for calculations and not modifying it, * use the `._z` property. @@ -90,7 +90,7 @@ Crafty.c("2D", { * #._globalZ * @comp 2D * @kind Property - * + * * When two entities overlap, the one with the larger `_globalZ` will be on top of the other. */ _globalZ: null, @@ -99,7 +99,7 @@ Crafty.c("2D", { * #.rotation * @comp 2D * @kind Property - * + * * The rotation state of the entity, in clockwise degrees. * `this.rotation = 0` sets it to its original orientation; `this.rotation = 10` * sets it to 10 degrees clockwise from its original orientation; @@ -130,92 +130,92 @@ Crafty.c("2D", { // Setup all the properties that we need to define properties: { x: { - set: function (v) { - this._setter2d('_x', v); + set: function(v) { + this._setter2d("_x", v); }, - get: function () { + get: function() { return this._x; }, configurable: true, enumerable: true }, - _x: {enumerable:false}, + _x: { enumerable: false }, y: { - set: function (v) { - this._setter2d('_y', v); + set: function(v) { + this._setter2d("_y", v); }, - get: function () { + get: function() { return this._y; }, configurable: true, enumerable: true }, - _y: {enumerable:false}, + _y: { enumerable: false }, w: { - set: function (v) { - this._setter2d('_w', v); + set: function(v) { + this._setter2d("_w", v); }, - get: function () { + get: function() { return this._w; }, configurable: true, enumerable: true }, - _w: {enumerable:false}, + _w: { enumerable: false }, h: { - set: function (v) { - this._setter2d('_h', v); + set: function(v) { + this._setter2d("_h", v); }, - get: function () { + get: function() { return this._h; }, configurable: true, enumerable: true }, - _h: {enumerable:false}, + _h: { enumerable: false }, z: { - set: function (v) { - this._setter2d('_z', v); + set: function(v) { + this._setter2d("_z", v); }, - get: function () { + get: function() { return this._z; }, configurable: true, enumerable: true }, - _z: {enumerable:false}, + _z: { enumerable: false }, rotation: { - set: function (v) { - this._setter2d('_rotation', v); + set: function(v) { + this._setter2d("_rotation", v); }, - get: function () { + get: function() { return this._rotation; }, configurable: true, enumerable: true }, - _rotation: {enumerable:false}, - + _rotation: { enumerable: false }, + /**@ * #.ox * @comp 2D * @kind Property - * + * * The `x` position on the stage of the origin. When modified, will set the underlying `x` value of the entity. - * + * * @see .origin */ ox: { - set: function (v) { + set: function(v) { var x = v - this._origin.x; - this._setter2d('_x', x); + this._setter2d("_x", x); }, - get: function () { + get: function() { return this._x + this._origin.x; }, configurable: true, @@ -226,17 +226,17 @@ Crafty.c("2D", { * #.oy * @comp 2D * @kind Property - * + * * The `y` position on the stage of the origin. When modified, will set the underlying `y` value of the entity. - * + * * @see .origin */ oy: { - set: function (v) { + set: function(v) { var y = v - this._origin.y; - this._setter2d('_y', y); + this._setter2d("_y", y); }, - get: function () { + get: function() { return this._y + this._origin.y; }, configurable: true, @@ -244,7 +244,7 @@ Crafty.c("2D", { } }, - init: function () { + init: function() { this._globalZ = this[0]; this._origin = { x: 0, @@ -261,10 +261,9 @@ Crafty.c("2D", { //insert self into the HashMap this._entry = Crafty.map.insert(this); - //when object changes, update HashMap - this.bind("Move", function (e) { + this.bind("Move", function(e) { // Choose the largest bounding region that exists var area = this._cbr || this._mbr || this; this._entry.update(area); @@ -274,7 +273,7 @@ Crafty.c("2D", { } }); - this.bind("Rotate", function (e) { + this.bind("Rotate", function(e) { // Choose the largest bounding region that exists var old = this._cbr || this._mbr || this; this._entry.update(old); @@ -285,7 +284,7 @@ Crafty.c("2D", { }); //when object is removed, remove from HashMap and destroy attached children - this.bind("Remove", function () { + this.bind("Remove", function() { if (this._children) { for (var i = 0; i < this._children.length; i++) { // delete the child's _parent link, or else the child will splice itself out of @@ -312,21 +311,21 @@ Crafty.c("2D", { }, events: { - "Freeze":function(){ + Freeze: function() { Crafty.map.remove(this._entry); }, - "Unfreeze":function(){ + Unfreeze: function() { this._entry = Crafty.map.insert(this, this._entry); } - }, + }, /**@ * #.offsetBoundary * @comp 2D * @kind Method - * + * * Extends the MBR of the entity by a specified amount. - * + * * @trigger BoundaryOffset - when the MBR offset changes * @sign public this .offsetBoundary(Number dx1, Number dy1, Number dx2, Number dy2) * @param dx1 - Extends the MBR to the left by this amount @@ -339,9 +338,8 @@ Crafty.c("2D", { * * You would most likely use this function to ensure that custom canvas rendering beyond the extent of the entity's normal bounds is not clipped. */ - offsetBoundary: function(x1, y1, x2, y2){ - if (arguments.length === 1) - y1 = x2 = y2 = x1; + offsetBoundary: function(x1, y1, x2, y2) { + if (arguments.length === 1) y1 = x2 = y2 = x1; this._bx1 = x1; this._bx2 = x2; this._by1 = y1; @@ -356,7 +354,7 @@ Crafty.c("2D", { * Necessary on a rotation, or a resize */ - _calculateMBR: function () { + _calculateMBR: function() { var ox = this._origin.x + this._x, oy = this._origin.y + this._y, rad = -this._rotation * DEG_TO_RAD; @@ -369,18 +367,18 @@ Crafty.c("2D", { var ct = Math.cos(rad), st = Math.sin(rad); // Special case 90 degree rotations to prevent rounding problems - ct = (ct < 1e-10 && ct > -1e-10) ? 0 : ct; - st = (st < 1e-10 && st > -1e-10) ? 0 : st; + ct = ct < 1e-10 && ct > -1e-10 ? 0 : ct; + st = st < 1e-10 && st > -1e-10 ? 0 : st; // Calculate the new points relative to the origin, then find the new (absolute) bounding coordinates! - var x0 = dx1 * ct + dy1 * st, - y0 = - dx1 * st + dy1 * ct, - x1 = dx2 * ct + dy1 * st, - y1 = - dx2 * st + dy1 * ct, - x2 = dx2 * ct + dy2 * st, - y2 = - dx2 * st + dy2 * ct, - x3 = dx1 * ct + dy2 * st, - y3 = - dx1 * st + dy2 * ct, + var x0 = dx1 * ct + dy1 * st, + y0 = -dx1 * st + dy1 * ct, + x1 = dx2 * ct + dy1 * st, + y1 = -dx2 * st + dy1 * ct, + x2 = dx2 * ct + dy2 * st, + y2 = -dx2 * st + dy2 * ct, + x3 = dx1 * ct + dy2 * st, + y3 = -dx1 * st + dy2 * ct, minx = Math.floor(Math.min(x0, x1, x2, x3) + ox), miny = Math.floor(Math.min(y0, y1, y2, y3) + oy), maxx = Math.ceil(Math.max(x0, x1, x2, x3) + ox), @@ -400,14 +398,16 @@ Crafty.c("2D", { } // If a collision hitbox exists AND sits outside the entity, find a bounding box for both. - // `_cbr` contains information about a bounding circle of the hitbox. + // `_cbr` contains information about a bounding circle of the hitbox. // The bounds of `_cbr` will be the union of the `_mbr` and the bounding box of that circle. - // This will not be a minimal region, but since it's only used for the broad phase pass it's good enough. + // This will not be a minimal region, but since it's only used for the broad phase pass it's good enough. // // cbr is calculated by the `_checkBounds` method of the "Collision" component if (this._cbr) { var cbr = this._cbr; - var cx = cbr.cx, cy = cbr.cy, r = cbr.r; + var cx = cbr.cx, + cy = cbr.cy, + r = cbr.r; var cx2 = ox + (cx + this._x - ox) * ct + (cy + this._y - oy) * st; var cy2 = oy - (cx + this._x - ox) * st + (cy + this._y - oy) * ct; cbr._x = Math.min(cx2 - r, minx); @@ -415,20 +415,17 @@ Crafty.c("2D", { cbr._w = Math.max(cx2 + r, maxx) - cbr._x; cbr._h = Math.max(cy2 + r, maxy) - cbr._y; } - }, /** * Handle changes that need to happen on a rotation */ - _rotate: function (v) { + _rotate: function(v) { //var theta = -1 * (v % 360); //angle always between 0 and 359 var difference = this._rotation - v; // skip if there's no rotation! - if (difference === 0) - return; - else - this._rotation = v; + if (difference === 0) return; + else this._rotation = v; this._calculateMBR(); @@ -439,11 +436,11 @@ Crafty.c("2D", { * #.area * @comp 2D * @kind Method - * + * * @sign public Number .area(void) * Calculates the area of the entity */ - area: function () { + area: function() { return this._w * this._h; }, @@ -451,7 +448,7 @@ Crafty.c("2D", { * #.intersect * @comp 2D * @kind Method - * + * * @sign public Boolean .intersect(Number x, Number y, Number w, Number h) * @param x - X position of the rect * @param y - Y position of the rect @@ -462,8 +459,9 @@ Crafty.c("2D", { * * Determines if this entity intersects a rectangle. If the entity is rotated, its MBR is used for the test. */ - intersect: function (x, y, w, h) { - var rect, mbr = this._mbr || this; + intersect: function(x, y, w, h) { + var rect, + mbr = this._mbr || this; if (typeof x === "object") { rect = x; } else { @@ -475,15 +473,19 @@ Crafty.c("2D", { }; } - return mbr._x < rect._x + rect._w && mbr._x + mbr._w > rect._x && - mbr._y < rect._y + rect._h && mbr._y + mbr._h > rect._y; + return ( + mbr._x < rect._x + rect._w && + mbr._x + mbr._w > rect._x && + mbr._y < rect._y + rect._h && + mbr._y + mbr._h > rect._y + ); }, /**@ * #.within * @comp 2D * @kind Method - * + * * @sign public Boolean .within(Number x, Number y, Number w, Number h) * @param x - X position of the rect * @param y - Y position of the rect @@ -494,8 +496,9 @@ Crafty.c("2D", { * * Determines if this current entity is within another rectangle. */ - within: function (x, y, w, h) { - var rect, mbr = this._mbr || this; + within: function(x, y, w, h) { + var rect, + mbr = this._mbr || this; if (typeof x === "object") { rect = x; } else { @@ -507,15 +510,19 @@ Crafty.c("2D", { }; } - return rect._x <= mbr._x && rect._x + rect._w >= mbr._x + mbr._w && - rect._y <= mbr._y && rect._y + rect._h >= mbr._y + mbr._h; + return ( + rect._x <= mbr._x && + rect._x + rect._w >= mbr._x + mbr._w && + rect._y <= mbr._y && + rect._y + rect._h >= mbr._y + mbr._h + ); }, /**@ * #.contains * @comp 2D * @kind Method - * + * * @sign public Boolean .contains(Number x, Number y, Number w, Number h) * @param x - X position of the rect * @param y - Y position of the rect @@ -526,8 +533,9 @@ Crafty.c("2D", { * * Determines if the rectangle is within the current entity. If the entity is rotated, its MBR is used for the test. */ - contains: function (x, y, w, h) { - var rect, mbr = this._mbr || this; + contains: function(x, y, w, h) { + var rect, + mbr = this._mbr || this; if (typeof x === "object") { rect = x; } else { @@ -539,15 +547,19 @@ Crafty.c("2D", { }; } - return rect._x >= mbr._x && rect._x + rect._w <= mbr._x + mbr._w && - rect._y >= mbr._y && rect._y + rect._h <= mbr._y + mbr._h; + return ( + rect._x >= mbr._x && + rect._x + rect._w <= mbr._x + mbr._w && + rect._y >= mbr._y && + rect._y + rect._h <= mbr._y + mbr._h + ); }, /**@ * #.pos * @comp 2D * @kind Method - * + * * @sign public Object .pos([Object pos]) * @param pos - an object to use as output * @returns an object with `_x`, `_y`, `_w`, and `_h` properties; if an object is passed in, it will be reused rather than creating a new object. @@ -557,12 +569,12 @@ Crafty.c("2D", { * @note The keys have an underscore prefix. This is due to the x, y, w, h properties * being setters and getters that wrap the underlying properties with an underscore (_x, _y, _w, _h). */ - pos: function (pos) { + pos: function(pos) { pos = pos || {}; - pos._x = (this._x); - pos._y = (this._y); - pos._w = (this._w); - pos._h = (this._h); + pos._x = this._x; + pos._y = this._y; + pos._w = this._w; + pos._h = this._h; return pos; }, @@ -570,7 +582,7 @@ Crafty.c("2D", { * #.mbr * @comp 2D * @kind Method - * + * * @sign public Object .mbr([Object mbr]) * @param mbr - an object to use as output * @returns an object with `_x`, `_y`, `_w`, and `_h` properties; if an object is passed in, it will be reused rather than creating a new object. @@ -584,15 +596,15 @@ Crafty.c("2D", { * * @see .pos */ - mbr: function (mbr) { + mbr: function(mbr) { mbr = mbr || {}; if (!this._mbr) { return this.pos(mbr); } else { - mbr._x = (this._mbr._x); - mbr._y = (this._mbr._y); - mbr._w = (this._mbr._w); - mbr._h = (this._mbr._h); + mbr._x = this._mbr._x; + mbr._y = this._mbr._y; + mbr._w = this._mbr._w; + mbr._h = this._mbr._h; return mbr; } }, @@ -601,7 +613,7 @@ Crafty.c("2D", { * #.isAt * @comp 2D * @kind Method - * + * * @sign public Boolean .isAt(Number x, Number y) * @param x - X position of the point * @param y - Y position of the point @@ -611,33 +623,37 @@ Crafty.c("2D", { * * 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) { + 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; + return ( + mbr._x <= x && + mbr._x + mbr._w >= x && + mbr._y <= y && + mbr._y + mbr._h >= y + ); }, /**@ * #.move * @comp 2D * @kind Method - * + * * @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. */ - move: function (dir, by) { - if (dir.charAt(0) === 'n') this.y -= by; - if (dir.charAt(0) === 's') this.y += by; - if (dir === 'e' || dir.charAt(1) === 'e') this.x += by; - if (dir === 'w' || dir.charAt(1) === 'w') this.x -= by; + move: function(dir, by) { + if (dir.charAt(0) === "n") this.y -= by; + if (dir.charAt(0) === "s") this.y += by; + if (dir === "e" || dir.charAt(1) === "e") this.x += by; + if (dir === "w" || dir.charAt(1) === "w") this.x -= by; return this; }, @@ -646,7 +662,7 @@ Crafty.c("2D", { * #.shift * @comp 2D * @kind Method - * + * * @sign public this .shift(Number x, Number y, Number w, Number h) * @param x - Amount to move X * @param y - Amount to move Y @@ -656,7 +672,7 @@ Crafty.c("2D", { * Shift or move the entity by an amount. Use negative values * for an opposite direction. */ - shift: function (x, y, w, h) { + shift: function(x, y, w, h) { if (x || y) this._setPosition(this._x + x, this._y + y); if (w) this.w += w; if (h) this.h += h; @@ -669,7 +685,7 @@ Crafty.c("2D", { * @comp 2D * @kind Method * @private - * + * * @sign public void ._cascade(e) * @param e - An object describing the motion * @@ -678,7 +694,7 @@ Crafty.c("2D", { * internally for ensuring that when a parent moves, the child also * moves in the same way. */ - _cascade: function (e) { + _cascade: function(e) { if (!e) return; //no change in position var i = 0, children = this._children, @@ -696,15 +712,14 @@ Crafty.c("2D", { if (obj.__frozen) continue; obj.shift(dx, dy, dw, dh); } - }, - + /**@ * #._cascadeRotation * @comp 2D * @kind Method * @private - * + * * @sign public void ._cascade(deg) * @param deg - The amount of rotation in degrees * @@ -724,15 +739,15 @@ Crafty.c("2D", { var cos = Math.cos(drad); var sin = Math.sin(drad); // Avoid some rounding problems - cos = (-1e-10 < cos && cos < 1e-10) ? 0 : cos; - sin = (-1e-10 < sin && sin < 1e-10) ? 0 : sin; + cos = -1e-10 < cos && cos < 1e-10 ? 0 : cos; + sin = -1e-10 < sin && sin < 1e-10 ? 0 : sin; var ox = this._origin.x + this._x; var oy = this._origin.y + this._y; for (; i < l; ++i) { obj = children[i]; if (obj.__frozen) continue; - if ('rotate' in obj) obj.rotate(deg, ox, oy, cos, sin); + if ("rotate" in obj) obj.rotate(deg, ox, oy, cos, sin); } }, @@ -740,7 +755,7 @@ Crafty.c("2D", { * #.attach * @comp 2D * @kind Method - * + * * @sign public this .attach(Entity obj[, .., Entity objN]) * @param obj - Child entity(s) to attach * @@ -756,7 +771,7 @@ Crafty.c("2D", { * As many objects as wanted can be attached, and a hierarchy of objects is * possible by attaching. */ - attach: function () { + attach: function() { var i = 0, arg = arguments, l = arguments.length, @@ -777,14 +792,14 @@ Crafty.c("2D", { * #.detach * @comp 2D * @kind Method - * + * * @sign public this .detach([Entity obj]) * @param obj - The entity to detach. Left blank will remove all attached entities * * Stop an entity from following the current entity. Passing no arguments will stop * every entity attached. */ - detach: function (obj) { + detach: function(obj) { var i; //if nothing passed, remove all attached objects if (!obj) { @@ -810,7 +825,7 @@ Crafty.c("2D", { * #.origin * @comp 2D * @kind Method - * + * * @sign public this .origin(Number x, Number y) * @param x - Pixel value of origin offset on the X axis * @param y - Pixel value of origin offset on the Y axis @@ -819,12 +834,12 @@ Crafty.c("2D", { * @param offset - Alignment identifier, which is a combination of center, top, bottom, middle, left and right * * Set the origin point of an entity for it to rotate around. - * + * * The properties `ox` and `oy` map to the coordinates of the origin on the stage; setting them moves the entity. * In contrast, this method sets the origin relative to the entity itself. * * @triggers OriginChanged -- after the new origin is assigned - * + * * @example * ~~~ * this.origin("top left") @@ -849,19 +864,29 @@ Crafty.c("2D", { * * @see .rotation, .ox, .oy */ - origin: function (x, y) { + origin: function(x, y) { //text based origin if (typeof x === "string") { - if (x === "centre" || x === "center" || x.indexOf(' ') === -1) { + if (x === "centre" || x === "center" || x.indexOf(" ") === -1) { x = this._w / 2; y = this._h / 2; } else { - var cmd = x.split(' '); + var cmd = x.split(" "); if (cmd[0] === "top") y = 0; else if (cmd[0] === "bottom") y = this._h; - else if (cmd[0] === "middle" || cmd[1] === "center" || cmd[1] === "centre") y = this._h / 2; - - if (cmd[1] === "center" || cmd[1] === "centre" || cmd[1] === "middle") x = this._w / 2; + else if ( + cmd[0] === "middle" || + cmd[1] === "center" || + cmd[1] === "centre" + ) + y = this._h / 2; + + if ( + cmd[1] === "center" || + cmd[1] === "centre" || + cmd[1] === "middle" + ) + x = this._w / 2; else if (cmd[1] === "left") x = 0; else if (cmd[1] === "right") x = this._w; } @@ -875,16 +900,22 @@ Crafty.c("2D", { /** * Method for rotation rather than through a setter - * + * * Pass in degree amount, origin coordinate and precalculated cos/sin */ - rotate: function (deg, ox, oy, cos, sin) { + rotate: function(deg, ox, oy, cos, sin) { var x2, y2; - x2 = (this._x + this._origin.x - ox) * cos + (this._y + this._origin.y - oy) * sin + (ox - this._origin.x); - y2 = (this._y + this._origin.y - oy) * cos - (this._x + this._origin.x - ox) * sin + (oy - this._origin.y); - this._setter2d('_rotation', this._rotation - deg); - this._setter2d('_x', x2 ); - this._setter2d('_y', y2 ); + x2 = + (this._x + this._origin.x - ox) * cos + + (this._y + this._origin.y - oy) * sin + + (ox - this._origin.x); + y2 = + (this._y + this._origin.y - oy) * cos - + (this._x + this._origin.x - ox) * sin + + (oy - this._origin.y); + this._setter2d("_rotation", this._rotation - deg); + this._setter2d("_x", x2); + this._setter2d("_y", y2); }, // A separate setter for the common case of moving an entity along both axes @@ -897,7 +928,7 @@ Crafty.c("2D", { mbr._y -= this._y - y; // cbr is a non-minimal bounding rectangle that contains both hitbox and mbr // It will exist only when the collision hitbox sits outside the entity - if (this._cbr){ + if (this._cbr) { this._cbr._x -= this._x - x; this._cbr._y -= this._y - y; } @@ -911,7 +942,7 @@ Crafty.c("2D", { // This is a setter method for all 2D properties including // x, y, w, h, and rotation. - _setter2d: function (name, value) { + _setter2d: function(name, value) { // Return if there is no change if (this[name] === value) { return; @@ -921,25 +952,24 @@ Crafty.c("2D", { var mbr; //if rotation, use the rotate method - if (name === '_rotation') { + if (name === "_rotation") { this._rotate(value); // _rotate triggers "Rotate" //set the global Z and trigger reorder just in case - } else if (name === '_x' || name === '_y') { + } else if (name === "_x" || name === "_y") { // mbr is the minimal bounding rectangle of the entity mbr = this._mbr; if (mbr) { mbr[name] -= this[name] - value; // cbr is a non-minimal bounding rectangle that contains both hitbox and mbr // It will exist only when the collision hitbox sits outside the entity - if (this._cbr){ + if (this._cbr) { this._cbr[name] -= this[name] - value; } } this[name] = value; this.trigger("Move", old); - - } else if (name === '_h' || name === '_w') { + } else if (name === "_h" || name === "_w") { mbr = this._mbr; var oldValue = this[name]; @@ -947,22 +977,21 @@ Crafty.c("2D", { if (mbr) { this._calculateMBR(); } - if (name === '_w') { + if (name === "_w") { this.trigger("Resize", { - axis: 'w', + axis: "w", amount: value - oldValue }); - } else if (name === '_h') { + } else if (name === "_h") { this.trigger("Resize", { - axis: 'h', + axis: "h", amount: value - oldValue }); } this.trigger("Move", old); - - } else if (name === '_z') { + } else if (name === "_z") { var intValue = value << 0; - value = value === intValue ? intValue : intValue+1; + value = value === intValue ? intValue : intValue + 1; this._globalZ = value * 100000 + this[0]; //magic number 10^5 is the max num of entities this[name] = value; this.trigger("Reorder"); @@ -978,9 +1007,6 @@ Crafty.c("2D", { } }); - - - /**@ * #Crafty.polygon * @category 2D @@ -1006,7 +1032,7 @@ Crafty.c("2D", { * new Crafty.polygon(50, 0, 100, 100, 0, 100); * ~~~ */ -Crafty.polygon = function (poly) { +Crafty.polygon = function(poly) { if (arguments.length > 1) { poly = Array.prototype.slice.call(arguments, 0); } @@ -1018,7 +1044,7 @@ Crafty.polygon.prototype = { * #.containsPoint * @comp Crafty.polygon * @kind Method - * + * * @sign public Boolean .containsPoint(Number x, Number y) * @param x - X position of the point * @param y - Y position of the point @@ -1032,12 +1058,21 @@ Crafty.polygon.prototype = { * poly.containsPoint(0, 0); //FALSE * ~~~ */ - containsPoint: function (x, y) { - var p = this.points, l = p.length/2, - i, j, c = false; + containsPoint: function(x, y) { + var p = this.points, + l = p.length / 2, + i, + j, + c = false; for (i = 0, j = l - 1; i < l; j = i++) { - if (((p[2*i+1] > y) !== (p[2*j+1] > y)) && (x < (p[2*j] - p[2*i]) * (y - p[2*i+1]) / (p[2*j+1] - p[2*i+1]) + p[2*i])) { + if ( + p[2 * i + 1] > y !== p[2 * j + 1] > y && + x < + ((p[2 * j] - p[2 * i]) * (y - p[2 * i + 1])) / + (p[2 * j + 1] - p[2 * i + 1]) + + p[2 * i] + ) { c = !c; } } @@ -1049,7 +1084,7 @@ Crafty.polygon.prototype = { * #.shift * @comp Crafty.polygon * @kind Method - * + * * @sign public void .shift(Number x, Number y) * @param x - Amount to shift the `x` axis * @param y - Amount to shift the `y` axis @@ -1063,12 +1098,13 @@ Crafty.polygon.prototype = { * //[[55, 5, 105, 5, 5, 105]; * ~~~ */ - shift: function (x, y) { - var i = 0, p =this.points, + shift: function(x, y) { + var i = 0, + p = this.points, l = p.length; - for (; i < l; i+=2) { + for (; i < l; i += 2) { p[i] += x; - p[i+1] += y; + p[i + 1] += y; } }, @@ -1076,9 +1112,9 @@ Crafty.polygon.prototype = { * #.clone * @comp Crafty.polygon * @kind Method - * + * * @sign public void .clone() - * + * * Returns a clone of the polygon. * * @example @@ -1093,18 +1129,19 @@ Crafty.polygon.prototype = { return new Crafty.polygon(this.points.slice(0)); }, - rotate: function (deg, ox, oy, cos, sin) { - var i = 0, p = this.points, + rotate: function(deg, ox, oy, cos, sin) { + var i = 0, + p = this.points, l = p.length, - x, y; - - for (; i < l; i+=2) { + x, + y; - x = ox + (p[i] - ox) * cos + (p[i+1] - oy) * sin; - y = oy - (p[i] - ox) * sin + (p[i+1] - oy) * cos; + for (; i < l; i += 2) { + x = ox + (p[i] - ox) * cos + (p[i + 1] - oy) * sin; + y = oy - (p[i] - ox) * sin + (p[i + 1] - oy) * cos; p[i] = x; - p[i+1] = y; + p[i + 1] = y; } }, @@ -1112,7 +1149,7 @@ Crafty.polygon.prototype = { * #.intersectRay * @comp Crafty.polygon * @kind Method - * + * * @sign public Number .intersectRay(Object origin, Object direction) * @param origin - the point of origin from which the ray will be cast. The object must contain the properties `_x` and `_y`. * @param direction - the direction the ray will be cast. It must be normalized. The object must contain the properties `x` and `y`. @@ -1183,27 +1220,34 @@ Crafty.polygon.prototype = { // -> d1 = (end - origin) • direction = // (end.x - origin.x, end.y - origin.y) • (direction.x, direction.y) = // (end.x - origin.x) * direction.x + (end.y - origin.y) * direction.y - intersectRay: function (origin, direction) { + intersectRay: function(origin, direction) { var points = this.points, minDistance = Infinity; - var d, d_nom, - e, e_nom, - denom; + var d, d_nom, e, e_nom, denom; - var originX = origin._x, directionX = direction.x, - originY = origin._y, directionY = direction.y; + var originX = origin._x, + directionX = direction.x, + originY = origin._y, + directionY = direction.y; - var i = 0, l = points.length; - var startX = points[l - 2], endX, edgeX, - startY = points[l - 1], endY, edgeY; + var i = 0, + l = points.length; + var startX = points[l - 2], + endX, + edgeX, + startY = points[l - 1], + endY, + edgeY; for (; i < l; i += 2) { endX = points[i]; - endY = points[i+1]; + endY = points[i + 1]; edgeX = endX - startX; edgeY = endY - startY; - d_nom = (startX - originX) * edgeY - (startY - originY) * edgeX; - e_nom = (startX - originX) * directionY - (startY - originY) * directionX; + d_nom = (startX - originX) * edgeY - (startY - originY) * edgeX; + e_nom = + (startX - originX) * directionY - + (startY - originY) * directionX; denom = directionX * edgeY - directionY * edgeX; if (denom !== 0) { @@ -1212,16 +1256,16 @@ Crafty.polygon.prototype = { if (e >= 0 && e <= 1 && d >= 0 && d < minDistance) minDistance = d; - } else if (d_nom === 0 || e_nom === 0) { - - d = (startX - originX) * directionX + (startY - originY) * directionY; - if (d >= 0 && d < minDistance) - minDistance = d; - - d = (endX - originX) * directionX + (endY - originY) * directionY; - if (d >= 0 && d < minDistance) - minDistance = d; + d = + (startX - originX) * directionX + + (startY - originY) * directionY; + if (d >= 0 && d < minDistance) minDistance = d; + + d = + (endX - originX) * directionX + + (endY - originY) * directionY; + if (d >= 0 && d < minDistance) minDistance = d; } startX = endX; @@ -1236,7 +1280,7 @@ Crafty.polygon.prototype = { * #Crafty.circle * @category 2D * @kind Class - * + * * Circle object used for hitboxes and click maps. Must pass a `x`, a `y` and a `radius` value. * *@example @@ -1251,7 +1295,7 @@ Crafty.polygon.prototype = { * When creating a circle for an entity, each point should be offset or relative from the entities `x` and `y` * (don't include the absolute values as it will automatically calculate this). */ -Crafty.circle = function (x, y, radius) { +Crafty.circle = function(x, y, radius) { this.x = x; this.y = y; this.radius = radius; @@ -1260,10 +1304,10 @@ Crafty.circle = function (x, y, radius) { this.points = []; var theta; - for (var i = 0; i < 16; i+=2) { - theta = i * Math.PI / 8; - this.points[i] = this.x + (Math.sin(theta) * radius); - this.points[i+1] = this.y + (Math.cos(theta) * radius); + for (var i = 0; i < 16; i += 2) { + theta = (i * Math.PI) / 8; + this.points[i] = this.x + Math.sin(theta) * radius; + this.points[i + 1] = this.y + Math.cos(theta) * radius; } }; @@ -1272,7 +1316,7 @@ Crafty.circle.prototype = { * #.containsPoint * @comp Crafty.circle * @kind Method - * + * * @sign public Boolean .containsPoint(Number x, Number y) * @param x - X position of the point * @param y - Y position of the point @@ -1286,19 +1330,19 @@ Crafty.circle.prototype = { * circle.containsPoint(50, 50); //FALSE * ~~~ */ - containsPoint: function (x, y) { + containsPoint: function(x, y) { var radius = this.radius, deltaX = this.x - x, deltaY = this.y - y; - return (deltaX * deltaX + deltaY * deltaY) < (radius * radius); + return deltaX * deltaX + deltaY * deltaY < radius * radius; }, /**@ * #.shift * @comp Crafty.circle * @kind Method - * + * * @sign public void .shift(Number x, Number y) * @param x - Amount to shift the `x` axis * @param y - Amount to shift the `y` axis @@ -1312,32 +1356,32 @@ Crafty.circle.prototype = { * //{x: 5, y: 5, radius: 10}; * ~~~ */ - shift: function (x, y) { + shift: function(x, y) { this.x += x; this.y += y; - var i = 0, p = this.points, + var i = 0, + p = this.points, l = p.length; - for (; i < l; i+=2) { + for (; i < l; i += 2) { p[i] += x; - p[i+1] += y; + p[i + 1] += y; } }, - rotate: function () { + rotate: function() { // We are a circle, we don't have to rotate :) } }; - -Crafty.matrix = function (m) { +Crafty.matrix = function(m) { this.mtx = m; this.width = m[0].length; this.height = m.length; }; Crafty.matrix.prototype = { - x: function (other) { + x: function(other) { if (this.width !== other.height) { return; } @@ -1356,10 +1400,15 @@ Crafty.matrix.prototype = { return new Crafty.matrix(result); }, - - e: function (row, col) { + e: function(row, col) { //test if out of bounds - if (row < 1 || row > this.mtx.length || col < 1 || col > this.mtx[0].length) return null; + if ( + row < 1 || + row > this.mtx.length || + col < 1 || + col > this.mtx[0].length + ) + return null; return this.mtx[row - 1][col - 1]; } }; diff --git a/src/spatial/collision.js b/src/spatial/collision.js index 87eb092f..4e562003 100644 --- a/src/spatial/collision.js +++ b/src/spatial/collision.js @@ -1,4 +1,4 @@ -var Crafty = require('../core/core.js'), +var Crafty = require("../core/core.js"), DEG_TO_RAD = Math.PI / 180, EPSILON = 1e-6; @@ -7,7 +7,7 @@ Crafty.extend({ * #Crafty.raycast * @category 2D * @kind Method - * + * * @sign public Array .raycast(Object origin, Object direction[, Number maxDistance][, String comp][, Boolean sort]) * @param origin - the point of origin from which the ray will be cast. The object must contain the properties `_x` and `_y`. * @param direction - the direction the ray will be cast. It must be normalized. The object must contain the properties `x` and `y`. @@ -65,7 +65,7 @@ Crafty.extend({ // https://gist.github.com/mucaho/77846e9fc0cd3c8b600c raycast: function(origin, direction) { // default parameters - var comp = 'obj', + var comp = "obj", maxDistance = Infinity, sort = true; // optional arguments @@ -73,9 +73,11 @@ Crafty.extend({ for (var i = 2, l = arguments.length; i < l; ++i) { argument = arguments[i]; type = typeof argument; - if (type === 'number') maxDistance = argument + EPSILON; // make it inclusive - else if (type === 'string') comp = argument; - else if (type === 'boolean') sort = argument; + if (type === "number") maxDistance = argument + EPSILON; + else if (type === "string") + // make it inclusive + comp = argument; + else if (type === "boolean") sort = argument; } var ox = origin._x, @@ -83,18 +85,20 @@ Crafty.extend({ dx = direction.x, dy = direction.y; - var alreadyChecked = {}, results = []; - - if (maxDistance < 0) { // find first intersection + if (maxDistance < 0) { + // find first intersection var closestObj = null, minDistance = Infinity; // traverse map - Crafty.map.traverseRay(origin, direction, function(obj, previousCellDistance) { + Crafty.map.traverseRay(origin, direction, function( + obj, + previousCellDistance + ) { // check if we advanced to next cell // then report closest object from previous cell // if intersection point is in previous cell @@ -112,7 +116,8 @@ Crafty.extend({ } // object must contain polygon hitbox, the specified component and must not already be checked - if (!obj.map || !obj.__c[comp] || alreadyChecked[obj[0]]) return; + if (!obj.map || !obj.__c[comp] || alreadyChecked[obj[0]]) + return; alreadyChecked[obj[0]] = true; // do exact intersection test @@ -132,11 +137,14 @@ Crafty.extend({ y: oy + minDistance * dy }); } - - } else { // find intersections up to max distance + } else { + // find intersections up to max distance // traverse map - Crafty.map.traverseRay(origin, direction, function(obj, previousCellDistance) { + Crafty.map.traverseRay(origin, direction, function( + obj, + previousCellDistance + ) { // check if we advanced to next cell // then cancel traversal if previousCellDistance > maxDistance if (previousCellDistance > maxDistance) { @@ -144,7 +152,8 @@ Crafty.extend({ } // object must contain polygon hitbox, the specified component and must not already be checked - if (!obj.map || !obj.__c[comp] || alreadyChecked[obj[0]]) return; + if (!obj.map || !obj.__c[comp] || alreadyChecked[obj[0]]) + return; alreadyChecked[obj[0]] = true; // do exact intersection test @@ -160,9 +169,10 @@ Crafty.extend({ }); } - - if (sort) results.sort(function(a, b) { return a.distance - b.distance; }); - + if (sort) + results.sort(function(a, b) { + return a.distance - b.distance; + }); return results; } @@ -172,7 +182,7 @@ Crafty.extend({ * #Collision * @category 2D * @kind Component - * + * * @trigger HitOn - Triggered when collisions occur. Will not trigger again until collisions of this type cease, or an event is requested once more (using `resetHitChecks(component)`). - { hitData } * @trigger HitOff - Triggered when collision with a specific component type ceases - String - componentName * @@ -191,7 +201,7 @@ Crafty.extend({ * @see 2D */ Crafty.c("Collision", { - init: function () { + init: function() { this.requires("2D"); this._collisionData = {}; @@ -246,7 +256,7 @@ Crafty.c("Collision", { * * @see Crafty.polygon */ - collision: function (polygon) { + collision: function(polygon) { // Unbind anything bound to "Resize" this.unbind("Resize", this._resizeMap); this.unbind("Resize", this._checkBounds); @@ -254,7 +264,16 @@ Crafty.c("Collision", { if (!polygon) { // If no polygon is specified, then a polygon is created that matches the bounds of the entity // It will be adjusted on a "Resize" event - polygon = new Crafty.polygon([0, 0, this._w, 0, this._w, this._h, 0, this._h]); + polygon = new Crafty.polygon([ + 0, + 0, + this._w, + 0, + this._w, + this._h, + 0, + this._h + ]); this.bind("Resize", this._resizeMap); this._cbr = null; } else { @@ -263,11 +282,11 @@ Crafty.c("Collision", { //convert args to array to create polygon var args = Array.prototype.slice.call(arguments, 0); polygon = new Crafty.polygon(args); - // Otherwise, we set the specified hitbox, converting from an array of points to a polygon if necessary + // Otherwise, we set the specified hitbox, converting from an array of points to a polygon if necessary } else if (polygon.constructor === Array) { //Clone the array so we don't modify it for anything else that might be using it polygon = new Crafty.polygon(polygon.slice()); - // Otherwise, we set the specified hitbox + // Otherwise, we set the specified hitbox } else { //Clone the polygon so we don't modify it for anything else that might be using it polygon = polygon.clone(); @@ -284,7 +303,8 @@ Crafty.c("Collision", { this._origin.x, this._origin.y, Math.cos(-this.rotation * DEG_TO_RAD), - Math.sin(-this.rotation * DEG_TO_RAD)); + Math.sin(-this.rotation * DEG_TO_RAD) + ); } // Finally, assign the hitbox, and attach it to the "Collision" entity @@ -299,7 +319,7 @@ Crafty.c("Collision", { * #.cbr * @comp Collision * @kind Method - * + * * @sign public Object .cbr([Object cbr]) * @param cbr - an object to use as output * @returns an object with `_x`, `_y`, `_w`, and `_h` properties; if an object is passed in, it will be reused rather than creating a new object. @@ -313,15 +333,15 @@ Crafty.c("Collision", { * * @see 2D#.mbr */ - cbr: function (cbr) { + cbr: function(cbr) { cbr = cbr || {}; if (!this._cbr) { return this.mbr(cbr); } else { - cbr._x = (this._cbr._x); - cbr._y = (this._cbr._y); - cbr._w = (this._cbr._w); - cbr._h = (this._cbr._h); + cbr._x = this._cbr._x; + cbr._y = this._cbr._y; + cbr._w = this._cbr._w; + cbr._h = this._cbr._h; return cbr; } }, @@ -332,19 +352,18 @@ Crafty.c("Collision", { // // It uses a pretty naive algorithm to do so, for more complicated options see [wikipedia](http://en.wikipedia.org/wiki/Bounding_sphere). _findBounds: function(points) { - var minX = Infinity, maxX = -Infinity, minY=Infinity, maxY=-Infinity; + var minX = Infinity, + maxX = -Infinity, + minY = Infinity, + maxY = -Infinity; var l = points.length; // Calculate the MBR of the points by finding the min/max x and y - for (var i=0; i maxX) - maxX = points[i]; - if (points[i+1] < minY) - minY = points[i+1]; - if (points[i+1] > maxY) - maxY = points[i+1]; + for (var i = 0; i < l; i += 2) { + if (points[i] < minX) minX = points[i]; + if (points[i] > maxX) maxX = points[i]; + if (points[i + 1] < minY) minY = points[i + 1]; + if (points[i + 1] > maxY) maxY = points[i + 1]; } // This describes a circle centered on the MBR of the points, with a diameter equal to its diagonal @@ -352,16 +371,20 @@ Crafty.c("Collision", { var cbr = { cx: (minX + maxX) / 2, cy: (minY + maxY) / 2, - r: Math.sqrt((maxX - minX)*(maxX - minX) + (maxY - minY)*(maxY - minY)) / 2 + r: + Math.sqrt( + (maxX - minX) * (maxX - minX) + + (maxY - minY) * (maxY - minY) + ) / 2 }; // We need to worry about resizing, but only if resizing could possibly change whether the hitbox is in or out of bounds // Thus if the upper-left corner is out of bounds, then there's no need to recheck on resize if (minX >= 0 && minY >= 0) { this._checkBounds = function() { - if (this._cbr === null && this._w < maxX || this._h < maxY) { - this._cbr = cbr; - this._calculateMBR(); + if ((this._cbr === null && this._w < maxX) || this._h < maxY) { + this._cbr = cbr; + this._calculateMBR(); } else if (this._cbr) { this._cbr = null; this._calculateMBR(); @@ -384,12 +407,14 @@ Crafty.c("Collision", { // The default behavior is to match the hitbox to the entity. // This function will change the hitbox when a "Resize" event triggers. - _resizeMap: function (e) { - var dx, dy, rot = this.rotation * DEG_TO_RAD, + _resizeMap: function(e) { + var dx, + dy, + rot = this.rotation * DEG_TO_RAD, points = this.map.points; // Depending on the change of axis, move the corners of the rectangle appropriately - if (e.axis === 'w') { + if (e.axis === "w") { if (rot) { dx = e.amount * Math.cos(rot); dy = e.amount * Math.sin(rot); @@ -424,24 +449,24 @@ Crafty.c("Collision", { * #.hit * @comp Collision * @kind Method - * + * * @sign public Array .hit(String component[, Array results]) * @param component - Check collision with entities that have this component * applied to them. - * @param results - If a results array is supplied, any collisions will be appended to it + * @param results - If a results array is supplied, any collisions will be appended to it * @return `null` if there is no collision. If a collision is detected, * returns an Array of collision data objects (see below). * If the results parameter was passed, it will be used as the return value. * * Tests for collisions with entities that have the specified component applied to them. - * If a collision is detected, data regarding the collision will be present in the array + * If a collision is detected, data regarding the collision will be present in the array * returned by this method. If no collisions occur, this method returns `null`. * - * When testing for collisions, if both entities have the `Collision` component, then + * When testing for collisions, if both entities have the `Collision` component, then * the collision test will use the Separating Axis Theorem (SAT), and provide more detailed * information about the collision. Otherwise, it will be a simple test of whether the * minimal bounding rectangles (MBR) overlap. - * + * * Following is a description of a collision data object that this method may * return: The returned collision data will be an Array of Objects with the * type of collision used, the object collided and if the type used was SAT (a polygon was used as the hitbox) then an amount of overlap. @@ -460,7 +485,7 @@ Crafty.c("Collision", { * - **type:** Collision detection method used. One of: * - *MBR:* Standard axis aligned rectangle intersection (`.intersect` in the 2D component). * - *SAT:* Collision between any two convex polygons. Used when both colliding entities have the `Collision` component applied to them. - * + * * If the collision result type is **SAT** then there will be three additional properties, which * represent the minimum translation vector (MTV) -- the direction and distance of the minimal translation * that will result in non-overlapping entities. @@ -489,7 +514,7 @@ Crafty.c("Collision", { * this.x -= hitData.overlap * hitData.nx; * this.y -= hitData.overlap * hitData.ny; * } else { // MBR, simple collision resolution - * // move player to previous position + * // move player to previous position * this.x = evt._x; * this.y = evt._y; * } @@ -501,7 +526,7 @@ Crafty.c("Collision", { */ _collisionHitDupes: [], _collisionHitResults: [], - hit: function (component, results) { + hit: function(component, results) { var area = this._cbr || this._mbr || this; var searchResults = this._collisionHitResults; searchResults.length = 0; @@ -510,9 +535,10 @@ Crafty.c("Collision", { if (!l) { return null; } - var i = 0, + var i = 0, dupes = this._collisionHitDupes, - id, obj; + id, + obj; results = results || []; dupes.length = 0; @@ -524,7 +550,7 @@ Crafty.c("Collision", { id = obj[0]; //check if not added to hash and that actually intersects - if (!dupes[id] && this[0] !== id && obj.__c[component]){ + if (!dupes[id] && this[0] !== id && obj.__c[component]) { dupes[id] = obj; if (obj.map) { var SAT = this._SAT(this.map, obj.map); @@ -533,7 +559,12 @@ Crafty.c("Collision", { SAT.obj = obj; SAT.type = "SAT"; } - } else if (Crafty.rectManager.overlap(area, obj._cbr || obj._mbr || obj)){ + } else if ( + Crafty.rectManager.overlap( + area, + obj._cbr || obj._mbr || obj + ) + ) { results.push({ obj: obj, type: "MBR" @@ -553,7 +584,7 @@ Crafty.c("Collision", { * #.onHit * @comp Collision * @kind Method - * + * * @sign public this .onHit(String component, Function callbackOn[, Function callbackOff]) * @param component - Component to check collisions for. * @param callbackOn - Callback method to execute upon collision with the component. @@ -587,15 +618,15 @@ Crafty.c("Collision", { * @see .hit * @see Crafty.map#Crafty.map.search */ - onHit: function (component, callbackOn, callbackOff) { + onHit: function(component, callbackOn, callbackOff) { var justHit = false; - this.bind("UpdateFrame", function () { + this.bind("UpdateFrame", function() { var hitData = this.hit(component); if (hitData) { callbackOn.call(this, hitData, !justHit); justHit = true; } else if (justHit) { - if (typeof callbackOff === 'function') { + if (typeof callbackOff === "function") { callbackOff.call(this); } justHit = false; @@ -635,7 +666,7 @@ Crafty.c("Collision", { * #.checkHits * @comp Collision * @kind Method - * + * * @sign public this .checkHits(String componentList) * @param componentList - A comma seperated list of components to check for collisions with. * @sign public this .checkHits(String component1[, .., String componentN]) @@ -676,7 +707,7 @@ Crafty.c("Collision", { * @see .hit * @see Crafty.map#Crafty.map.search */ - checkHits: function () { + checkHits: function() { var components = arguments; var i = 0; @@ -693,8 +724,14 @@ Crafty.c("Collision", { continue; } - this._collisionData[component] = collisionData = { occurring: false, handler: null }; - collisionData.handler = this._createCollisionHandler(component, collisionData); + this._collisionData[component] = collisionData = { + occurring: false, + handler: null + }; + collisionData.handler = this._createCollisionHandler( + component, + collisionData + ); this.bind("UpdateFrame", collisionData.handler); } @@ -733,7 +770,7 @@ Crafty.c("Collision", { * .ignoreHits('Solid'); // stop checking for collisions with entities that have the Solid component * ~~~ */ - ignoreHits: function () { + ignoreHits: function() { var components = arguments; var i = 0; var collisionData; @@ -769,7 +806,7 @@ Crafty.c("Collision", { * #.resetHitChecks * @comp Collision * @kind Method - * + * * @sign public this .resetHitChecks() * @sign public this .resetHitChecks(String componentList) * @param componentList - A comma seperated list of components to re-check @@ -828,15 +865,20 @@ Crafty.c("Collision", { return this; }, - _SAT: function (poly1, poly2) { + _SAT: function(poly1, poly2) { var i = 0, - points1 = poly1.points, points2 = poly2.points, - l = points1.length/2, - j, k = points2.length/2, - nx=0, ny=0, + points1 = poly1.points, + points2 = poly2.points, + l = points1.length / 2, + j, + k = points2.length / 2, + nx = 0, + ny = 0, length, - min1, min2, - max1, max2, + min1, + min2, + max1, + max2, interval, MTV = -Infinity, MNx = null, @@ -846,11 +888,11 @@ Crafty.c("Collision", { //loop through the edges of Polygon 1 for (; i < l; i++) { - np = (i === l - 1 ? 0 : i + 1); + np = i === l - 1 ? 0 : i + 1; //generate the normal for the current edge - nx = -(points1[2*i+1] - points1[2*np+1]); - ny = (points1[2*i] - points1[2*np]); + nx = -(points1[2 * i + 1] - points1[2 * np + 1]); + ny = points1[2 * i] - points1[2 * np]; //normalize the vector length = Math.sqrt(nx * nx + ny * ny); @@ -863,16 +905,16 @@ Crafty.c("Collision", { //project all vertices from poly1 onto axis for (j = 0; j < l; ++j) { - dot = points1[2*j] * nx + points1[2*j+1] * ny; + dot = points1[2 * j] * nx + points1[2 * j + 1] * ny; if (dot > max1) max1 = dot; if (dot < min1) min1 = dot; } //project all vertices from poly2 onto axis for (j = 0; j < k; ++j) { - dot = points2[2*j] * nx + points2[2*j+1] * ny; + dot = points2[2 * j] * nx + points2[2 * j + 1] * ny; if (dot > max2) max2 = dot; - if (dot < min2 ) min2 = dot; + if (dot < min2) min2 = dot; } //calculate the minimum translation vector should be negative @@ -898,11 +940,11 @@ Crafty.c("Collision", { //loop through the edges of Polygon 2 for (i = 0; i < k; i++) { - np = (i === k - 1 ? 0 : i + 1); + np = i === k - 1 ? 0 : i + 1; //generate the normal for the current edge - nx = -(points2[2*i+1] - points2[2*np+1]); - ny = (points2[2*i] - points2[2*np]); + nx = -(points2[2 * i + 1] - points2[2 * np + 1]); + ny = points2[2 * i] - points2[2 * np]; //normalize the vector length = Math.sqrt(nx * nx + ny * ny); @@ -915,14 +957,14 @@ Crafty.c("Collision", { //project all vertices from poly1 onto axis for (j = 0; j < l; ++j) { - dot = points1[2*j] * nx + points1[2*j+1] * ny; + dot = points1[2 * j] * nx + points1[2 * j + 1] * ny; if (dot > max1) max1 = dot; if (dot < min1) min1 = dot; } //project all vertices from poly2 onto axis for (j = 0; j < k; ++j) { - dot = points2[2*j] * nx + points2[2*j+1] * ny; + dot = points2[2 * j] * nx + points2[2 * j + 1] * ny; if (dot > max2) max2 = dot; if (dot < min2) min2 = dot; } diff --git a/src/spatial/math.js b/src/spatial/math.js index f999d0bf..bd26bf3c 100644 --- a/src/spatial/math.js +++ b/src/spatial/math.js @@ -1,5 +1,4 @@ -var Crafty = require('../core/core.js'); - +var Crafty = require("../core/core.js"); /**@ * #Crafty.math @@ -13,14 +12,14 @@ Crafty.math = { * #Crafty.math.abs * @comp Crafty.math * @kind Method - * + * * @sign public this Crafty.math.abs(Number n) * @param n - Some value. * @return Absolute value. * * Returns the absolute value. */ - abs: function (x) { + abs: function(x) { return x < 0 ? -x : x; }, @@ -28,7 +27,7 @@ Crafty.math = { * #Crafty.math.amountOf * @comp Crafty.math * @kind Method - * + * * @sign public Number Crafty.math.amountOf(Number checkValue, Number minValue, Number maxValue) * @param checkValue - Value that should checked with minimum and maximum. * @param minValue - Bottom of the range @@ -37,19 +36,17 @@ Crafty.math = { * * If checkValue is within the range, this will return a number between 0 and 1. */ - amountOf: function (checkValue, minValue, maxValue) { + amountOf: function(checkValue, minValue, maxValue) { if (minValue < maxValue) return (checkValue - minValue) / (maxValue - minValue); - else - return (checkValue - maxValue) / (minValue - maxValue); + else return (checkValue - maxValue) / (minValue - maxValue); }, - /**@ * #Crafty.math.clamp * @comp Crafty.math * @kind Method - * + * * @sign public Number Crafty.math.clamp(Number value, Number min, Number max) * @param value - A value. * @param max - Maximum that value can be. @@ -58,13 +55,10 @@ Crafty.math = { * * Restricts a value to be within a specified range. */ - clamp: function (value, min, max) { - if (value > max) - return max; - else if (value < min) - return min; - else - return value; + clamp: function(value, min, max) { + if (value > max) return max; + else if (value < min) return min; + else return value; }, /**@ @@ -72,20 +66,20 @@ Crafty.math = { * Converts angle from degree to radian. * @comp Crafty.math * @kind Method - * + * * @sign public Number degToRad(angleInDeg) * @param angleInDeg - The angle in degrees. * @return The angle in radians. */ - degToRad: function (angleInDeg) { - return angleInDeg * Math.PI / 180; + degToRad: function(angleInDeg) { + return (angleInDeg * Math.PI) / 180; }, /**@ * #Crafty.math.distance * @comp Crafty.math * @kind Method - * + * * @sign public Number Crafty.math.distance(Number x1, Number y1, Number x2, Number y2) * @param x1 - First x coordinate. * @param y1 - First y coordinate. @@ -95,7 +89,7 @@ Crafty.math = { * * Distance between two points. */ - distance: function (x1, y1, x2, y2) { + distance: function(x1, y1, x2, y2) { var squaredDistance = Crafty.math.squaredDistance(x1, y1, x2, y2); return Math.sqrt(parseFloat(squaredDistance)); }, @@ -104,7 +98,7 @@ Crafty.math = { * #Crafty.math.lerp * @comp Crafty.math * @kind Method - * + * * @sign public Number Crafty.math.lerp(Number value1, Number value2, Number amount) * @param value1 - One value. * @param value2 - Another value. @@ -114,7 +108,7 @@ Crafty.math = { * Linear interpolation. Passing amount with a value of 0 will cause value1 to be returned, * a value of 1 will cause value2 to be returned. */ - lerp: function (value1, value2, amount) { + lerp: function(value1, value2, amount) { return value1 + (value2 - value1) * amount; }, @@ -122,47 +116,45 @@ Crafty.math = { * #Crafty.math.negate * @comp Crafty.math * @kind Method - * + * * @sign public Number Crafty.math.negate(Number percent) * @param percent - The probability of returning `-1` * @return 1 or -1. * * Returns `1` or `-1` randomly. */ - negate: function (percent) { - if (Math.random() < percent) - return -1; - else - return 1; + negate: function(percent) { + if (Math.random() < percent) return -1; + else return 1; }, /**@ * #Crafty.math.radToDeg * @comp Crafty.math * @kind Method - * + * * @sign public Number Crafty.math.radToDeg(Number angle) * @param angleInRad - The angle in radian. * @return The angle in degree. * * Converts angle from radian to degree. */ - radToDeg: function (angleInRad) { - return angleInRad * 180 / Math.PI; + radToDeg: function(angleInRad) { + return (angleInRad * 180) / Math.PI; }, /**@ * #Crafty.math.randomElementOfArray * @comp Crafty.math * @kind Method - * + * * @sign public Object Crafty.math.randomElementOfArray(Array array) * @param array - A specific array. * @return A random element of a specific array. * * Returns a random element of a specific array. */ - randomElementOfArray: function (array) { + randomElementOfArray: function(array) { return array[Math.floor(array.length * Math.random())]; }, @@ -170,7 +162,7 @@ Crafty.math = { * #Crafty.math.randomInt * @comp Crafty.math * @kind Method - * + * * @sign public Number Crafty.math.randomInt(Number start, Number end) * @param start - Smallest int value that can be returned. * @param end - Biggest int value that can be returned. @@ -178,7 +170,7 @@ Crafty.math = { * * Returns a random int within a specific range. */ - randomInt: function (start, end) { + randomInt: function(start, end) { return start + Math.floor((1 + end - start) * Math.random()); }, @@ -186,7 +178,7 @@ Crafty.math = { * #Crafty.math.randomNumber * @comp Crafty.math * @kind Method - * + * * @sign public Number Crafty.math.randomNumber(Number start, Number end) * @param start - Smallest number value that can be returned. * @param end - Biggest number value that can be returned. @@ -194,7 +186,7 @@ Crafty.math = { * * Returns a random number in within a specific range. */ - randomNumber: function (start, end) { + randomNumber: function(start, end) { return start + (end - start) * Math.random(); }, @@ -202,7 +194,7 @@ Crafty.math = { * #Crafty.math.squaredDistance * @comp Crafty.math * @kind Method - * + * * @sign public Number Crafty.math.squaredDistance(Number x1, Number y1, Number x2, Number y2) * @param x1 - First x coordinate. * @param y1 - First y coordinate. @@ -212,7 +204,7 @@ Crafty.math = { * * Squared distance between two points. */ - squaredDistance: function (x1, y1, x2, y2) { + squaredDistance: function(x1, y1, x2, y2) { return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); }, @@ -220,7 +212,7 @@ Crafty.math = { * #Crafty.math.withinRange * @comp Crafty.math * @kind Method - * + * * @sign public Boolean Crafty.math.withinRange(Number value, Number min, Number max) * @param value - The specific value. * @param min - Minimum value. @@ -229,40 +221,40 @@ Crafty.math = { * * Check if a value is within a specific range. */ - withinRange: function (value, min, max) { - return (value >= min && value <= max); + withinRange: function(value, min, max) { + return value >= min && value <= max; } }; -Crafty.math.Vector2D = (function () { +Crafty.math.Vector2D = (function() { /**@ * #Crafty.math.Vector2D * @category 2D * @kind Class * @public - * + * * @class This is a general purpose 2D vector class * * Vector2D has the following constructors: * * @sign public {Vector2D} new Vector2D(); - * @returns {Vector2D} A new vector with x and y equal to 0 - * + * @returns {Vector2D} A new vector with x and y equal to 0 + * * @sign public {Vector2D} new Vector2D(Number x, Number y); * @param {Number} x - The initial x value * @param {Number} y - The initial y value * @returns {Vector2D} - A new vector with the given x and y values - * + * * @sign public {Vector2D} new Vector2D(Vector2D vector); * @param {Vector2D} vector - A vector to copy * @returns {Vector2D} A new vector with the copied x and y values - * + * * @example * ``` * var v1 = new Crafty.math.Vector2D(3, 5); * var v2 = new Crafty.math.Vector2D(v1); * ``` - * + * */ function Vector2D(x, y) { @@ -291,7 +283,7 @@ Crafty.math.Vector2D = (function () { * @param {Vector2D} vecRH - The vector to add * @returns {Vector2D} The resulting modified vector */ - Vector2D.prototype.add = function (vecRH) { + Vector2D.prototype.add = function(vecRH) { this.x += vecRH.x; this.y += vecRH.y; return this; @@ -310,8 +302,11 @@ Crafty.math.Vector2D = (function () { * @param {Vector2D} vecRH - The vector to compare * @returns {Number} the angle between the two vectors in radians */ - Vector2D.prototype.angleBetween = function (vecRH) { - return Math.atan2(this.x * vecRH.y - this.y * vecRH.x, this.x * vecRH.x + this.y * vecRH.y); + Vector2D.prototype.angleBetween = function(vecRH) { + return Math.atan2( + this.x * vecRH.y - this.y * vecRH.x, + this.x * vecRH.x + this.y * vecRH.y + ); }; /**@ @@ -326,7 +321,7 @@ Crafty.math.Vector2D = (function () { * @param {Vector2D} vecRH - The vector to compare * @returns {Number} the angle to the passed vector in radians */ - Vector2D.prototype.angleTo = function (vecRH) { + Vector2D.prototype.angleTo = function(vecRH) { return Math.atan2(vecRH.y - this.y, vecRH.x - this.x); }; @@ -334,7 +329,7 @@ Crafty.math.Vector2D = (function () { * #.clone * @comp Crafty.math.Vector2D * @kind Method - * + * * * Creates and exact, numeric copy of this vector * @@ -342,7 +337,7 @@ Crafty.math.Vector2D = (function () { * @sign public {Vector2D} clone(); * @returns {Vector2D} the new vector */ - Vector2D.prototype.clone = function () { + Vector2D.prototype.clone = function() { return new Vector2D(this); }; // clone @@ -350,7 +345,7 @@ Crafty.math.Vector2D = (function () { * #.distance * @comp Crafty.math.Vector2D * @kind Method - * + * * * Calculates the distance from this vector to the passed vector. * @@ -359,15 +354,18 @@ Crafty.math.Vector2D = (function () { * @param {Vector2D} vecRH - The passed vector * @returns {Number} the distance between the two vectors */ - Vector2D.prototype.distance = function (vecRH) { - return Math.sqrt((vecRH.x - this.x) * (vecRH.x - this.x) + (vecRH.y - this.y) * (vecRH.y - this.y)); + Vector2D.prototype.distance = function(vecRH) { + return Math.sqrt( + (vecRH.x - this.x) * (vecRH.x - this.x) + + (vecRH.y - this.y) * (vecRH.y - this.y) + ); }; // distance /**@ * #.distanceSq * @comp Crafty.math.Vector2D * @kind Method - * + * * * Calculates the squared distance from this vector to the passed vector. * This function avoids calculating the square root, thus being slightly faster than .distance( ). @@ -376,18 +374,21 @@ Crafty.math.Vector2D = (function () { * @sign public {Number} distanceSq(Vector2D vecRH); * @param {Vector2D} vecRH - The passed vector * @returns {Number} the squared distance between the two vectors - * + * * @see .distance */ - Vector2D.prototype.distanceSq = function (vecRH) { - return (vecRH.x - this.x) * (vecRH.x - this.x) + (vecRH.y - this.y) * (vecRH.y - this.y); + Vector2D.prototype.distanceSq = function(vecRH) { + return ( + (vecRH.x - this.x) * (vecRH.x - this.x) + + (vecRH.y - this.y) * (vecRH.y - this.y) + ); }; // distanceSq /**@ * #.divide * @comp Crafty.math.Vector2D * @kind Method - * + * * * Divides this vector by the passed vector. * @@ -396,7 +397,7 @@ Crafty.math.Vector2D = (function () { * @param {Vector2D} vecRH - The passed vector * @returns {Vector2D} this vector after dividing */ - Vector2D.prototype.divide = function (vecRH) { + Vector2D.prototype.divide = function(vecRH) { this.x /= vecRH.x; this.y /= vecRH.y; return this; @@ -406,7 +407,7 @@ Crafty.math.Vector2D = (function () { * #.dotProduct * @comp Crafty.math.Vector2D * @kind Method - * + * * * Calculates the dot product of this and the passed vectors * @@ -415,7 +416,7 @@ Crafty.math.Vector2D = (function () { * @param {Vector2D} vecRH - The passed vector * @returns {Number} the resultant dot product */ - Vector2D.prototype.dotProduct = function (vecRH) { + Vector2D.prototype.dotProduct = function(vecRH) { return this.x * vecRH.x + this.y * vecRH.y; }; // dotProduct @@ -423,7 +424,7 @@ Crafty.math.Vector2D = (function () { * #.crossProduct * @comp Crafty.math.Vector2D * @kind Method - * + * * * Calculates the z component of the cross product of the two vectors augmented to 3D. * @@ -432,7 +433,7 @@ Crafty.math.Vector2D = (function () { * @param {Vector2D} vecRH - The passed vector * @returns {Number} the resultant cross product */ - Vector2D.prototype.crossProduct = function (vecRH) { + Vector2D.prototype.crossProduct = function(vecRH) { return this.x * vecRH.y - this.y * vecRH.x; }; // crossProduct @@ -440,7 +441,7 @@ Crafty.math.Vector2D = (function () { * #.equals * @comp Crafty.math.Vector2D * @kind Method - * + * * * Determines if this vector is numerically equivalent to the passed vector. * @@ -449,16 +450,19 @@ Crafty.math.Vector2D = (function () { * @param {Vector2D} vecRH - The passed vector * @returns {Boolean} true if the vectors are equivalent */ - Vector2D.prototype.equals = function (vecRH) { - return vecRH instanceof Vector2D && - this.x === vecRH.x && this.y === vecRH.y; + Vector2D.prototype.equals = function(vecRH) { + return ( + vecRH instanceof Vector2D && + this.x === vecRH.x && + this.y === vecRH.y + ); }; // equals /**@ * #.perpendicular * @comp Crafty.math.Vector2D * @kind Method - * + * * * Calculates a new vector that is perpendicular to this vector. * The perpendicular vector has the same magnitude as this vector and is obtained by a counter-clockwise rotation of 90° of this vector. @@ -468,7 +472,7 @@ Crafty.math.Vector2D = (function () { * @param {Vector2D} [result] - An optional parameter to save the result in * @returns {Vector2D} the perpendicular vector */ - Vector2D.prototype.perpendicular = function (result) { + Vector2D.prototype.perpendicular = function(result) { result = result || new Vector2D(); return result.setValues(-this.y, this.x); }; // perpendicular @@ -486,7 +490,7 @@ Crafty.math.Vector2D = (function () { * @param {Vector2D} [result] - An optional parameter to save the result in * @returns {Vector2D} the new normal vector */ - Vector2D.prototype.getNormal = function (vecRH, result) { + Vector2D.prototype.getNormal = function(vecRH, result) { result = result || new Vector2D(); return result.setValues(vecRH.y - this.y, this.x - vecRH.x).normalize(); }; // getNormal @@ -495,7 +499,7 @@ Crafty.math.Vector2D = (function () { * #.isZero * @comp Crafty.math.Vector2D * @kind Method - * + * * * Determines if this vector is equal to <0,0> * @@ -503,7 +507,7 @@ Crafty.math.Vector2D = (function () { * @sign public {Boolean} isZero(); * @returns {Boolean} true if this vector is equal to <0,0> */ - Vector2D.prototype.isZero = function () { + Vector2D.prototype.isZero = function() { return this.x === 0 && this.y === 0; }; // isZero @@ -519,7 +523,7 @@ Crafty.math.Vector2D = (function () { * @sign public {Number} magnitude(); * @returns {Number} the magnitude of this vector */ - Vector2D.prototype.magnitude = function () { + Vector2D.prototype.magnitude = function() { return Math.sqrt(this.x * this.x + this.y * this.y); }; // magnitude @@ -527,7 +531,7 @@ Crafty.math.Vector2D = (function () { * #.magnitudeSq * @comp Crafty.math.Vector2D * @kind Method - * + * * Calculates the square of the magnitude of this vector. * This function avoids calculating the square root, thus being slightly faster than .magnitude( ). * @@ -536,7 +540,7 @@ Crafty.math.Vector2D = (function () { * @returns {Number} the square of the magnitude of this vector * @see .magnitude */ - Vector2D.prototype.magnitudeSq = function () { + Vector2D.prototype.magnitudeSq = function() { return this.x * this.x + this.y * this.y; }; // magnitudeSq @@ -544,7 +548,7 @@ Crafty.math.Vector2D = (function () { * #.multiply * @comp Crafty.math.Vector2D * @kind Method - * + * * Multiplies this vector by the passed vector, using component-wise multiplciation * * @public @@ -552,7 +556,7 @@ Crafty.math.Vector2D = (function () { * @param {Vector2D} vecRH - The passed vector * @returns {Vector2D} this vector after multiplying */ - Vector2D.prototype.multiply = function (vecRH) { + Vector2D.prototype.multiply = function(vecRH) { this.x *= vecRH.x; this.y *= vecRH.y; return this; @@ -562,14 +566,14 @@ Crafty.math.Vector2D = (function () { * #.negate * @comp Crafty.math.Vector2D * @kind Method - * + * * Negates this vector (ie. <-x,-y>) * * @public * @sign public {Vector2D} negate(); * @returns {Vector2D} this vector after negation */ - Vector2D.prototype.negate = function () { + Vector2D.prototype.negate = function() { this.x = -this.x; this.y = -this.y; return this; @@ -579,7 +583,7 @@ Crafty.math.Vector2D = (function () { * #.normalize * @comp Crafty.math.Vector2D * @kind Method - * + * * Normalizes this vector (scales the vector so that its new magnitude is 1) * For vectors where magnitude is 0, <1,0> is returned. * @@ -587,7 +591,7 @@ Crafty.math.Vector2D = (function () { * @sign public {Vector2D} normalize(); * @returns {Vector2D} this vector after normalization */ - Vector2D.prototype.normalize = function () { + Vector2D.prototype.normalize = function() { var lng = Math.sqrt(this.x * this.x + this.y * this.y); if (lng === 0) { @@ -606,22 +610,21 @@ Crafty.math.Vector2D = (function () { * #.scale * @comp Crafty.math.Vector2D * @kind Method - * + * * Scales this vector by the passed amount(s) * * @public * @sign public {Vector2D} scale(Number scale); * @param {Number} scale - The amount to scale by * @returns {Vector2D} this after scaling - * + * * @sign public {Vector2D} scale(Number scalarX, Number scalarY); * @param {Number} scalarX - The amount to scale x by * @param {Number} [scalarY] - The amount to scale y by * @returns {Vector2D} this after scaling */ - Vector2D.prototype.scale = function (scalarX, scalarY) { - if (scalarY === undefined) - scalarY = scalarX; + Vector2D.prototype.scale = function(scalarX, scalarY) { + if (scalarY === undefined) scalarY = scalarX; this.x *= scalarX; this.y *= scalarY; @@ -633,7 +636,7 @@ Crafty.math.Vector2D = (function () { * #.scaleToMagnitude * @comp Crafty.math.Vector2D * @kind Method - * + * * Scales this vector such that its new magnitude is equal to the passed value. * * @public @@ -641,7 +644,7 @@ Crafty.math.Vector2D = (function () { * @param {Number} mag - The desired magnitude * @returns {Vector2D} this vector after scaling */ - Vector2D.prototype.scaleToMagnitude = function (mag) { + Vector2D.prototype.scaleToMagnitude = function(mag) { var k = mag / this.magnitude(); this.x *= k; this.y *= k; @@ -652,20 +655,20 @@ Crafty.math.Vector2D = (function () { * #.setValues * @comp Crafty.math.Vector2D * @kind Method - * + * * Sets the values of this vector using a passed vector or pair of numbers. * * @public * @sign public {Vector2D} setValues(Vector2D vector); * @param {Vector2D} vector - a vector to copy * @returns {Vector2D} this vector after copying the values - * + * * @sign public {Vector2D} setValues(Number x, Number y); * @param {Number} x - The x value to set * @param {Number} y - The y value to set * @returns {Vector2D} this vector after setting the values */ - Vector2D.prototype.setValues = function (x, y) { + Vector2D.prototype.setValues = function(x, y) { if (x instanceof Vector2D) { this.x = x.x; this.y = x.y; @@ -681,7 +684,7 @@ Crafty.math.Vector2D = (function () { * #.subtract * @comp Crafty.math.Vector2D * @kind Method - * + * * Subtracts the passed vector from this vector. * * @public @@ -689,7 +692,7 @@ Crafty.math.Vector2D = (function () { * @param {Vector2D} vecRH - the passed vector to subtract * @returns {vector2D} this vector after subtracting */ - Vector2D.prototype.subtract = function (vecRH) { + Vector2D.prototype.subtract = function(vecRH) { this.x -= vecRH.x; this.y -= vecRH.y; return this; @@ -699,14 +702,14 @@ Crafty.math.Vector2D = (function () { * #.toString * @comp Crafty.math.Vector2D * @kind Method - * + * * Returns a string representation of this vector. * * @public * @sign public {String} toString(); * @returns {String} A representation like "Vector2D(4, 7)" */ - Vector2D.prototype.toString = function () { + Vector2D.prototype.toString = function() { return "Vector2D(" + this.x + ", " + this.y + ")"; }; // toString @@ -714,7 +717,7 @@ Crafty.math.Vector2D = (function () { * #.translate * @comp Crafty.math.Vector2D * @kind Method - * + * * Translates (moves) this vector by the passed amounts. * If dy is omitted, dx is used for both axes. * @@ -724,9 +727,8 @@ Crafty.math.Vector2D = (function () { * @param {Number} [dy] - The amount to shift along the y axis * @returns {Vector2D} this vector after translating */ - Vector2D.prototype.translate = function (dx, dy) { - if (dy === undefined) - dy = dx; + Vector2D.prototype.translate = function(dx, dy) { + if (dy === undefined) dy = dx; this.x += dx; this.y += dy; @@ -738,7 +740,7 @@ Crafty.math.Vector2D = (function () { * #.tripleProduct * @comp Crafty.math.Vector2D * @kind Method - * + * * Calculates the triple product of three vectors. * triple vector product = b(a•c) - a(b•c) * @@ -751,7 +753,7 @@ Crafty.math.Vector2D = (function () { * @param {Vector2D} [result] - An optional parameter to save the result in * @return {Vector2D} the triple product as a new vector */ - Vector2D.tripleProduct = function (a, b, c, result) { + Vector2D.tripleProduct = function(a, b, c, result) { result = result || new Crafty.math.Vector2D(); var ac = a.dotProduct(c); var bc = b.dotProduct(c); @@ -761,12 +763,12 @@ Crafty.math.Vector2D = (function () { return Vector2D; })(); -Crafty.math.Matrix2D = (function () { +Crafty.math.Matrix2D = (function() { /**@ * #Crafty.math.Matrix2D * @category 2D * @kind Class - * + * * @class This is a 2D Matrix2D class. It is 3x3 to allow for affine transformations in 2D space. * The third row is always assumed to be [0, 0, 1]. * @@ -778,11 +780,11 @@ Crafty.math.Matrix2D = (function () { * @public * @sign public {Matrix2D} new Matrix2D(); * @returns {Matrix2D} A new identity matrix - * + * * @sign public {Matrix2D} new Matrix2D(Matrix2D matrix); * @param {Matrix2D} matrix - a matrix to copy * @returns {Matrix2D} A new instance whose entries are copied from the passed matrix - * + * * @sign public {Matrix2D} new Matrix2D(Number a, Number b, Number c, Number d, Number e, Number f); * @param {Number=1} a - (m11) Horizontal scale * @param {Number=0} b - (m12) Horizontal skew @@ -791,7 +793,7 @@ Crafty.math.Matrix2D = (function () { * @param {Number=0} e - (dx) Horizontal translation * @param {Number=0} f - (dy) Vertical translation * @returns {Matrix2D} A new instance whose entries are set from the passed arguments - * + * * @example * ``` * // Create the following translation matrix: @@ -801,7 +803,7 @@ Crafty.math.Matrix2D = (function () { * var m = new Crafty.math.Matrix2D(1, 0, 0, 1, 5, 7); * ``` */ - function Matrix2D (a, b, c, d, e, f) { + function Matrix2D(a, b, c, d, e, f) { if (a instanceof Matrix2D) { this.a = a.a; this.b = a.b; @@ -831,7 +833,7 @@ Crafty.math.Matrix2D = (function () { * #.apply * @comp Crafty.math.Matrix2D * @kind Method - * + * * Applies the matrix transformations to the passed object * * @public @@ -839,7 +841,7 @@ Crafty.math.Matrix2D = (function () { * @param {Vector2D} vecRH - vector to be transformed * @returns {Vector2D} the passed vector object after transforming */ - Matrix2D.prototype.apply = function (vecRH) { + Matrix2D.prototype.apply = function(vecRH) { // I'm not sure of the best way for this function to be implemented. Ideally // support for other objects (rectangles, polygons, etc) should be easily // addable in the future. Maybe a function (apply) is not the best way to do @@ -857,14 +859,14 @@ Crafty.math.Matrix2D = (function () { * #.clone * @comp Crafty.math.Matrix2D * @kind Method - * + * * Creates an exact, numeric copy of the current matrix * * @public * @sign public {Matrix2D} clone(); * @returns {Matrix2D} The cloned matrix */ - Matrix2D.prototype.clone = function () { + Matrix2D.prototype.clone = function() { return new Matrix2D(this); }; // clone @@ -881,7 +883,7 @@ Crafty.math.Matrix2D = (function () { * @param {Matrix2D} mtrxRH - The passed matrix * @returns {Matrix2D} this matrix after combination */ - Matrix2D.prototype.combine = function (mtrxRH) { + Matrix2D.prototype.combine = function(mtrxRH) { var tmp = this.a; this.a = tmp * mtrxRH.a + this.b * mtrxRH.c; this.b = tmp * mtrxRH.b + this.b * mtrxRH.d; @@ -906,10 +908,16 @@ Crafty.math.Matrix2D = (function () { * @param {Matrix2D} mtrxRH - The matrix to check equality with * @returns {Boolean} true if the two matrices are numerically equal */ - Matrix2D.prototype.equals = function (mtrxRH) { - return mtrxRH instanceof Matrix2D && - this.a === mtrxRH.a && this.b === mtrxRH.b && this.c === mtrxRH.c && - this.d === mtrxRH.d && this.e === mtrxRH.e && this.f === mtrxRH.f; + Matrix2D.prototype.equals = function(mtrxRH) { + return ( + mtrxRH instanceof Matrix2D && + this.a === mtrxRH.a && + this.b === mtrxRH.b && + this.c === mtrxRH.c && + this.d === mtrxRH.d && + this.e === mtrxRH.e && + this.f === mtrxRH.f + ); }; // equals /**@ @@ -923,7 +931,7 @@ Crafty.math.Matrix2D = (function () { * @sign public {Number} determinant(); * @returns {Number} det(this matrix) */ - Matrix2D.prototype.determinant = function () { + Matrix2D.prototype.determinant = function() { return this.a * this.d - this.b * this.c; }; // determinant @@ -939,7 +947,7 @@ Crafty.math.Matrix2D = (function () { * @returns {Matrix2D} this inverted matrix or the original matrix on failure * @see .isInvertible */ - Matrix2D.prototype.invert = function () { + Matrix2D.prototype.invert = function() { var det = this.determinant(); // matrix is invertible if its determinant is non-zero @@ -967,22 +975,29 @@ Crafty.math.Matrix2D = (function () { * #.isIdentity * @comp Crafty.math.Matrix2D * @kind Method - * + * * Returns true if this matrix is the identity matrix * * @public * @sign public {Boolean} isIdentity(); * @returns {Boolean} true if this matrix is an identity matrix */ - Matrix2D.prototype.isIdentity = function () { - return this.a === 1 && this.b === 0 && this.c === 0 && this.d === 1 && this.e === 0 && this.f === 0; + Matrix2D.prototype.isIdentity = function() { + return ( + this.a === 1 && + this.b === 0 && + this.c === 0 && + this.d === 1 && + this.e === 0 && + this.f === 0 + ); }; // isIdentity /**@ * #.isInvertible * @comp Crafty.math.Matrix2D * @kind Method - * + * * Determines is this matrix is invertible. * * @public @@ -990,7 +1005,7 @@ Crafty.math.Matrix2D = (function () { * @returns {Boolean} true if this matrix is invertible * @see .invert */ - Matrix2D.prototype.isInvertible = function () { + Matrix2D.prototype.isInvertible = function() { return this.determinant() !== 0; }; // isInvertible @@ -998,7 +1013,7 @@ Crafty.math.Matrix2D = (function () { * #.preRotate * @comp Crafty.math.Matrix2D * @kind Method - * + * * Applies a counter-clockwise pre-rotation to this matrix * * @public @@ -1006,7 +1021,7 @@ Crafty.math.Matrix2D = (function () { * @param {number} rads - angle to rotate in radians * @returns {Matrix2D} this matrix after pre-rotation */ - Matrix2D.prototype.preRotate = function (rads) { + Matrix2D.prototype.preRotate = function(rads) { var nCos = Math.cos(rads); var nSin = Math.sin(rads); @@ -1024,9 +1039,9 @@ Crafty.math.Matrix2D = (function () { * #.preScale * @comp Crafty.math.Matrix2D * @kind Method - * + * * Applies a pre-scaling to this matrix, applied to the a, b, c, and d elements. - * + * * If two arguments are supplied, a and c are multiplied by scalarX, b, and d by scalarY. * * @public @@ -1035,9 +1050,8 @@ Crafty.math.Matrix2D = (function () { * @param {Number} [scalarY] - scalarX is used if scalarY is undefined * @returns {Matrix2D} this after pre-scaling */ - Matrix2D.prototype.preScale = function (scalarX, scalarY) { - if (scalarY === undefined) - scalarY = scalarX; + Matrix2D.prototype.preScale = function(scalarX, scalarY) { + if (scalarY === undefined) scalarY = scalarX; this.a *= scalarX; this.b *= scalarY; @@ -1051,7 +1065,7 @@ Crafty.math.Matrix2D = (function () { * #.preTranslate * @comp Crafty.math.Matrix2D * @kind Method - * + * * Applies a pre-translation to this matrix * * @public @@ -1059,12 +1073,12 @@ Crafty.math.Matrix2D = (function () { * @param {Number} dx - The amount to shift the e component * @param {Number} dy - The amount to shift the f component * @returns {Matrix2D} this matrix after pre-translation - * + * * @sign public {Matrix2D} preTranslate(Vector2D vector); * @param {Vector2D} vector - The vector to shift (e, f) by. * @returns {Matrix2D} this matrix after pre-translation */ - Matrix2D.prototype.preTranslate = function (dx, dy) { + Matrix2D.prototype.preTranslate = function(dx, dy) { if (typeof dx === "number") { this.e += dx; this.f += dy; @@ -1080,7 +1094,7 @@ Crafty.math.Matrix2D = (function () { * #.rotate * @comp Crafty.math.Matrix2D * @kind Method - * + * * Applies a counter-clockwise post-rotation to this matrix * * @public @@ -1088,7 +1102,7 @@ Crafty.math.Matrix2D = (function () { * @param {Number} rads - angle to rotate in radians * @returns {Matrix2D} this matrix after rotation */ - Matrix2D.prototype.rotate = function (rads) { + Matrix2D.prototype.rotate = function(rads) { var nCos = Math.cos(rads); var nSin = Math.sin(rads); @@ -1109,9 +1123,9 @@ Crafty.math.Matrix2D = (function () { * #.scale * @comp Crafty.math.Matrix2D * @kind Method - * + * * Applies a post-scaling to this matrix, modifying components a-f. - * + * * If two arguments are passed, scalarX is used for components a, c, and e; scalarY for b, d, and f. * * @public @@ -1120,9 +1134,8 @@ Crafty.math.Matrix2D = (function () { * @param {Number} [scalarY] scalarX is used if scalarY is undefined * @returns {Matrix2D} this after post-scaling */ - Matrix2D.prototype.scale = function (scalarX, scalarY) { - if (scalarY === undefined) - scalarY = scalarX; + Matrix2D.prototype.scale = function(scalarX, scalarY) { + if (scalarY === undefined) scalarY = scalarX; this.a *= scalarX; this.b *= scalarY; @@ -1138,14 +1151,14 @@ Crafty.math.Matrix2D = (function () { * #.setValues * @comp Crafty.math.Matrix2D * @kind Method - * + * * Sets the values of this matrix. * * @public * @sign public {Matrix2D} setValues(Matrix2D matrix); * @param {Matrix2D} matrix - A matrix to copy the values from * @returns {Matrix2D} This matrix after copying the values - * + * * @sign public {Matrix2D} setValues(Number a, Number b, Number c, Number d, Number e, Number f); * When used as a translation matrix, the 6 elements have particular meanings. * @param {Number} a - (m11) Horizontal scale @@ -1156,7 +1169,7 @@ Crafty.math.Matrix2D = (function () { * @param {Number} f - (dy) Vertical translation * @returns {Matrix2D} this matrix containing the new values */ - Matrix2D.prototype.setValues = function (a, b, c, d, e, f) { + Matrix2D.prototype.setValues = function(a, b, c, d, e, f) { if (a instanceof Matrix2D) { this.a = a.a; this.b = a.b; @@ -1180,36 +1193,49 @@ Crafty.math.Matrix2D = (function () { * #.toString * @comp Crafty.math.Matrix2D * @kind Method - * + * * Returns the string representation of this matrix. * * @public * @sign public {String} toString(); * @returns {String} A string representation like "Matrix2D([a, c, e], [b, d, f], [0, 0, 1])" */ - Matrix2D.prototype.toString = function () { - return "Matrix2D([" + this.a + ", " + this.c + ", " + this.e + - "] [" + this.b + ", " + this.d + ", " + this.f + "] [0, 0, 1])"; + Matrix2D.prototype.toString = function() { + return ( + "Matrix2D([" + + this.a + + ", " + + this.c + + ", " + + this.e + + "] [" + + this.b + + ", " + + this.d + + ", " + + this.f + + "] [0, 0, 1])" + ); }; // toString /**@ * #.translate * @comp Crafty.math.Matrix2D * @kind Method - * + * * Applies a post-translation to this matrix * * @public * @sign public {Matrix2D} translate(Vector2D vector); * @param {Vector2D} vector - the vector to translate by * @returns {Matrix2D} this matrix after post-translation - * + * * @sign public {Matrix2D} translate(Number dx, Number dy); * @param {Number} dx - The shift along the x-axis * @param {Number} dy - The shift along the y-axis * @returns {Matrix2D} this matrix after post-translation */ - Matrix2D.prototype.translate = function (dx, dy) { + Matrix2D.prototype.translate = function(dx, dy) { if (typeof dx === "number") { this.e += this.a * dx + this.c * dy; this.f += this.b * dx + this.d * dy; @@ -1222,4 +1248,4 @@ Crafty.math.Matrix2D = (function () { }; // translate return Matrix2D; -})(); \ No newline at end of file +})(); diff --git a/src/spatial/motion.js b/src/spatial/motion.js index 851e609a..3d2f5ef7 100644 --- a/src/spatial/motion.js +++ b/src/spatial/motion.js @@ -1,45 +1,57 @@ -var Crafty = require('../core/core.js'); - - +var Crafty = require("../core/core.js"); // This is used to define getters and setters for Motion properties // For instance -// __motionProp(entity, "a", "x", true) +// __motionProp(entity, "a", "x", true) // will define a getter for `ax` which accesses an underlying private property `_ax` // If the `setter` property is false, setting a value will be a null-op var __motionProp = function(self, prefix, prop, setter) { var publicProp = prefix + prop; var privateProp = "_" + publicProp; - var motionEvent = { key: "", oldValue: 0}; + var motionEvent = { key: "", oldValue: 0 }; // getters & setters for public property if (setter) { - Crafty.defineField(self, publicProp, function() { return this[privateProp]; }, function(newValue) { - var oldValue = this[privateProp]; - if (newValue !== oldValue) { - this[privateProp] = newValue; - - motionEvent.key = publicProp; - motionEvent.oldValue = oldValue; - this.trigger("MotionChange", motionEvent); + Crafty.defineField( + self, + publicProp, + function() { + return this[privateProp]; + }, + function(newValue) { + var oldValue = this[privateProp]; + if (newValue !== oldValue) { + this[privateProp] = newValue; + + motionEvent.key = publicProp; + motionEvent.oldValue = oldValue; + this.trigger("MotionChange", motionEvent); + } } - }); + ); } else { - Crafty.defineField(self, publicProp, function() { return this[privateProp]; }, function(newValue) {}); + Crafty.defineField( + self, + publicProp, + function() { + return this[privateProp]; + }, + function(newValue) {} + ); } // hide private property Object.defineProperty(self, privateProp, { - value : 0, - writable : true, - enumerable : false, - configurable : false + value: 0, + writable: true, + enumerable: false, + configurable: false }); }; // This defines an alias for a pair of underlying properties which represent the components of a vector // It takes an object with vector methods, and redefines its x/y properties as getters and setters to properties of self -// This allows you to use the vector's special methods to manipulate the entity's properties, +// This allows you to use the vector's special methods to manipulate the entity's properties, // while still allowing you to manipulate those properties directly if performance matters var __motionVector = function(self, prefix, setter, vector) { var publicX = prefix + "x", @@ -48,13 +60,47 @@ var __motionVector = function(self, prefix, setter, vector) { privateY = "_" + publicY; if (setter) { - Crafty.defineField(vector, "x", function() { return self[privateX]; }, function(v) { self[publicX] = v; }); - Crafty.defineField(vector, "y", function() { return self[privateY]; }, function(v) { self[publicY] = v; }); + Crafty.defineField( + vector, + "x", + function() { + return self[privateX]; + }, + function(v) { + self[publicX] = v; + } + ); + Crafty.defineField( + vector, + "y", + function() { + return self[privateY]; + }, + function(v) { + self[publicY] = v; + } + ); } else { - Crafty.defineField(vector, "x", function() { return self[privateX]; }, function(v) {}); - Crafty.defineField(vector, "y", function() { return self[privateY]; }, function(v) {}); + Crafty.defineField( + vector, + "x", + function() { + return self[privateX]; + }, + function(v) {} + ); + Crafty.defineField( + vector, + "y", + function() { + return self[privateY]; + }, + function(v) {} + ); + } + if (Object.seal) { + Object.seal(vector); } - if (Object.seal) { Object.seal(vector); } return vector; }; @@ -63,7 +109,7 @@ var __motionVector = function(self, prefix, setter, vector) { * #AngularMotion * @category 2D * @kind Component - * + * * @trigger Rotated - When entity has rotated due to angular velocity/acceleration a Rotated event is triggered. - Number - Old rotation * @trigger NewRotationDirection - When entity has changed rotational direction due to rotational velocity a NewRotationDirection event is triggered. The event is triggered once, if direction is different from last frame. - -1 | 0 | 1 - New direction * @trigger MotionChange - When a motion property has changed a MotionChange event is triggered. - { key: String, oldValue: Number } - Motion property name and old value @@ -76,8 +122,8 @@ Crafty.c("AngularMotion", { * #.vrotation * @comp AngularMotion * @kind Property - * - * A property for accessing/modifying the angular(rotational) velocity. + * + * A property for accessing/modifying the angular(rotational) velocity. * The velocity remains constant over time, unless the acceleration increases the velocity. * * @example @@ -95,8 +141,8 @@ Crafty.c("AngularMotion", { * #.arotation * @comp AngularMotion * @kind Property - * - * A property for accessing/modifying the angular(rotational) acceleration. + * + * A property for accessing/modifying the angular(rotational) acceleration. * The acceleration increases the velocity over time, resulting in ever increasing speed. * * @example @@ -114,7 +160,7 @@ Crafty.c("AngularMotion", { * #.drotation * @comp AngularMotion * @kind Property - * + * * A number that reflects the change in rotation (difference between the old & new rotation) that was applied in the last frame. * * @example @@ -126,7 +172,7 @@ Crafty.c("AngularMotion", { */ _drotation: 0, - init: function () { + init: function() { this.requires("2D"); __motionProp(this, "v", "rotation", true); @@ -145,9 +191,9 @@ Crafty.c("AngularMotion", { * #.resetAngularMotion * @comp AngularMotion * @kind Method - * + * * @sign public this .resetAngularMotion() - * + * * Reset all motion (resets velocity, acceleration, motionDelta). */ resetAngularMotion: function() { @@ -174,10 +220,11 @@ Crafty.c("AngularMotion", { this.vrotation = vr + ar * dt; // Check if direction of velocity has changed - var _vr = this._vrotation, dvr = _vr ? (_vr<0 ? -1:1):0; // Quick implementation of Math.sign + var _vr = this._vrotation, + dvr = _vr ? (_vr < 0 ? -1 : 1) : 0; // Quick implementation of Math.sign if (this.__oldRotationDirection !== dvr) { this.__oldRotationDirection = dvr; - this.trigger('NewRotationDirection', dvr); + this.trigger("NewRotationDirection", dvr); } // Check if velocity has changed @@ -185,7 +232,7 @@ Crafty.c("AngularMotion", { this._drotation = newR - oldR; if (this._drotation !== 0) { this.rotation = newR; - this.trigger('Rotated', oldR); + this.trigger("Rotated", oldR); } } }); @@ -194,7 +241,7 @@ Crafty.c("AngularMotion", { * #Motion * @category 2D * @kind Component - * + * * @trigger NewDirection - When entity has changed direction due to velocity on either x or y axis a NewDirection event is triggered. The event is triggered once, if direction is different from last frame. - { x: -1 | 0 | 1, y: -1 | 0 | 1 } - New direction * @trigger MotionChange - When a motion property has changed a MotionChange event is triggered. - { key: String, oldValue: Number } - Motion property name and old value * @@ -208,7 +255,7 @@ Crafty.c("Motion", { * #.vx * @comp Motion * @kind Property - * + * * A property for accessing/modifying the linear velocity in the x axis. * The velocity remains constant over time, unless the acceleration changes the velocity. * @@ -227,7 +274,7 @@ Crafty.c("Motion", { * #.vy * @comp Motion * @kind Property - * + * * A property for accessing/modifying the linear velocity in the y axis. * The velocity remains constant over time, unless the acceleration changes the velocity. * @@ -246,7 +293,7 @@ Crafty.c("Motion", { * #.ax * @comp Motion * @kind Property - * + * * A property for accessing/modifying the linear acceleration in the x axis. * The acceleration changes the velocity over time. * @@ -265,7 +312,7 @@ Crafty.c("Motion", { * #.ay * @comp Motion * @kind Property - * + * * A property for accessing/modifying the linear acceleration in the y axis. * The acceleration changes the velocity over time. * @@ -284,7 +331,7 @@ Crafty.c("Motion", { * #.dx * @comp Motion * @kind Property - * + * * A number that reflects the change in x (difference between the old & new x) that was applied in the last frame. * * @example @@ -300,7 +347,7 @@ Crafty.c("Motion", { * #.dy * @comp Motion * @kind Property - * + * * A number that reflects the change in y (difference between the old & new y) that was applied in the last frame. * * @example @@ -312,20 +359,35 @@ Crafty.c("Motion", { */ _dy: 0, - init: function () { + init: function() { this.requires("2D"); __motionProp(this, "v", "x", true); __motionProp(this, "v", "y", true); - this._velocity = __motionVector(this, "v", true, new Crafty.math.Vector2D()); + this._velocity = __motionVector( + this, + "v", + true, + new Crafty.math.Vector2D() + ); __motionProp(this, "a", "x", true); __motionProp(this, "a", "y", true); - this._acceleration = __motionVector(this, "a", true, new Crafty.math.Vector2D()); + this._acceleration = __motionVector( + this, + "a", + true, + new Crafty.math.Vector2D() + ); __motionProp(this, "d", "x", false); __motionProp(this, "d", "y", false); - this._motionDelta = __motionVector(this, "d", false, new Crafty.math.Vector2D()); + this._motionDelta = __motionVector( + this, + "d", + false, + new Crafty.math.Vector2D() + ); - this.__oldDirection = {x: 0, y: 0}; + this.__oldDirection = { x: 0, y: 0 }; this.bind("UpdateFrame", this._linearMotionTick); }, @@ -337,16 +399,19 @@ Crafty.c("Motion", { * #.resetMotion * @comp Motion * @kind Method - * + * * @sign public this .resetMotion() * @return this - * + * * Reset all linear motion (resets velocity, acceleration, motionDelta). */ resetMotion: function() { - this.vx = 0; this.vy = 0; - this.ax = 0; this.ay = 0; - this._dx = 0; this._dy = 0; + this.vx = 0; + this.vy = 0; + this.ax = 0; + this.ay = 0; + this._dx = 0; + this._dy = 0; return this; }, @@ -355,10 +420,10 @@ Crafty.c("Motion", { * #.motionDelta * @comp Motion * @kind Method - * + * * @sign public Vector2D .motionDelta() * @return A Vector2D with the properties {x, y} that reflect the change in x & y. - * + * * Returns the difference between the old & new position that was applied in the last frame. * * @example @@ -401,15 +466,14 @@ Crafty.c("Motion", { return this._velocity; }, - /**@ * #.acceleration * @comp Motion * @kind Method - * - * Method for accessing/modifying the linear(x,y) acceleration. + * + * Method for accessing/modifying the linear(x,y) acceleration. * The acceleration increases the velocity over time, resulting in ever increasing speed. - * + * * @sign public Vector2D .acceleration() * @return The acceleration Vector2D with the properties {x, y} that reflects the acceleration in the direction of the entity. * @@ -434,7 +498,7 @@ Crafty.c("Motion", { * #.ccdbr * @comp Motion * @kind Method - * + * * @sign public Object .ccdbr([Object ccdbr]) * @param ccdbr - an object to use as output * @returns an object with `_x`, `_y`, `_w`, and `_h` properties; if an object is passed in, it will be reused rather than creating a new object. @@ -449,11 +513,12 @@ Crafty.c("Motion", { * * @see .motionDelta, Collision#.cbr */ - ccdbr: function (ccdbr) { + ccdbr: function(ccdbr) { var pos = this._cbr || this._mbr || this, dx = this._dx, dy = this._dy, - ccdX = 0, ccdY = 0, + ccdX = 0, + ccdY = 0, ccdW = dx > 0 ? (ccdX = dx) : -dx, ccdH = dy > 0 ? (ccdY = dy) : -dy; @@ -472,8 +537,10 @@ Crafty.c("Motion", { */ _linearMotionTick: function(frameData) { var dt = frameData.dt / 1000; // time in s - var vx = this._vx, ax = this._ax, - vy = this._vy, ay = this._ay; + var vx = this._vx, + ax = this._ax, + vy = this._vy, + ay = this._ay; // s += v * Δt + (0.5 * a) * Δt * Δt var dx = vx * dt + 0.5 * ax * dt * dt; @@ -484,12 +551,14 @@ Crafty.c("Motion", { // Check if direction of velocity has changed var oldDirection = this.__oldDirection, - _vx = this._vx, dvx = _vx ? (_vx<0 ? -1:1):0, // A quick implementation of Math.sign - _vy = this._vy, dvy = _vy ? (_vy<0 ? -1:1):0; + _vx = this._vx, + dvx = _vx ? (_vx < 0 ? -1 : 1) : 0, // A quick implementation of Math.sign + _vy = this._vy, + dvy = _vy ? (_vy < 0 ? -1 : 1) : 0; if (oldDirection.x !== dvx || oldDirection.y !== dvy) { oldDirection.x = dvx; oldDirection.y = dvy; - this.trigger('NewDirection', oldDirection); + this.trigger("NewDirection", oldDirection); } this._dx = dx; diff --git a/src/spatial/platform.js b/src/spatial/platform.js index 02ca984c..1dcba7db 100644 --- a/src/spatial/platform.js +++ b/src/spatial/platform.js @@ -1,10 +1,10 @@ -var Crafty = require('../core/core.js'); +var Crafty = require("../core/core.js"); /**@ * #Supportable * @category 2D * @kind Component - * + * * @trigger LandedOnGround - When entity has landed. This event is triggered with the object the entity landed on. * @trigger LiftedOffGround - When entity has lifted off. This event is triggered with the object the entity stood on before lift-off. * @trigger CheckLanding - When entity is about to land. This event is triggered with the object the entity is about to land on. Third parties can respond to this event and prevent the entity from being able to land. @@ -18,7 +18,7 @@ Crafty.c("Supportable", { * @comp Supportable * @kind Property * - * Access the ground entity (which may be the actual ground entity if it exists, or `null` if it doesn't exist) and thus whether this entity is currently on the ground or not. + * Access the ground entity (which may be the actual ground entity if it exists, or `null` if it doesn't exist) and thus whether this entity is currently on the ground or not. * The ground entity is also available through the events, when the ground entity changes. */ _ground: null, @@ -31,7 +31,7 @@ Crafty.c("Supportable", { * @kind Property * * The canLand boolean determines if the entity is allowed to land or not (e.g. perhaps the entity should not land if it's not falling). - * The Supportable component will trigger a "CheckLanding" event. + * The Supportable component will trigger a "CheckLanding" event. * Interested parties can listen to this event and prevent the entity from landing by setting `canLand` to false. * * @example @@ -46,10 +46,16 @@ Crafty.c("Supportable", { */ canLand: true, - init: function () { + init: function() { this.requires("2D"); - this.__area = {_x: 0, _y: 0, _w: 0, _h: 0}; - this.defineField("ground", function() { return this._ground; }, function(newValue) {}); + this.__area = { _x: 0, _y: 0, _w: 0, _h: 0 }; + this.defineField( + "ground", + function() { + return this._ground; + }, + function(newValue) {} + ); }, remove: function(destroyed) { this.unbind("UpdateFrame", this._detectGroundTick); @@ -107,7 +113,7 @@ Crafty.c("Supportable", { * #.preventGroundTunneling * @comp Supportable * @kind Method - * + * * @sign this .preventGroundTunneling([Boolean enable]) * @param enable - Boolean indicating whether to enable continous collision detection or not; if omitted defaults to true * @@ -118,10 +124,8 @@ Crafty.c("Supportable", { * @see Motion#.ccdbr */ preventGroundTunneling: function(enable) { - if (typeof enable === 'undefined') - enable = true; - if (enable) - this.requires("Motion"); + if (typeof enable === "undefined") enable = true; + if (enable) this.requires("Motion"); this._preventGroundTunneling = enable; return this; @@ -147,11 +151,16 @@ Crafty.c("Supportable", { // Decrease width by 1px from left and 1px from right, to fall more gracefully // area._x++; area._w--; - // check if we lift-off if (ground) { var garea = ground._cbr || ground._mbr || ground; - if (!(ground.__c[groundComp] && Crafty(ground[0]) === ground && overlap(garea, area))) { + if ( + !( + ground.__c[groundComp] && + Crafty(ground[0]) === ground && + overlap(garea, area) + ) + ) { this._ground = null; this.trigger("LiftedOffGround", ground); // no collision with ground was detected for first time ground = null; @@ -160,7 +169,8 @@ Crafty.c("Supportable", { // check if we land (also possible to land on other ground object in same frame after lift-off from current ground object) if (!ground) { - var obj, oarea, + var obj, + oarea, results = Crafty.map.unfilteredSearch(area), i = 0, l = results.length; @@ -169,7 +179,11 @@ Crafty.c("Supportable", { obj = results[i]; oarea = obj._cbr || obj._mbr || obj; // check for an intersection with the player - if (obj !== this && obj.__c[groundComp] && overlap(oarea, area)) { + if ( + obj !== this && + obj.__c[groundComp] && + overlap(oarea, area) + ) { this.canLand = true; this.trigger("CheckLanding", obj); // is entity allowed to land? if (this.canLand) { @@ -217,7 +231,7 @@ Crafty.c("GroundAttacher", { ground.detach(this); }, - init: function () { + init: function() { this.requires("Supportable"); this.bind("LandedOnGround", this._groundAttach); @@ -229,12 +243,11 @@ Crafty.c("GroundAttacher", { } }); - /**@ * #Gravity * @category 2D * @kind Component - * + * * Adds gravitational pull to the entity. * * Additionally, this component provides the entity with `Supportable` and `Motion` methods & events. @@ -248,7 +261,7 @@ Crafty.c("Gravity", { _gravityConst: 500, _gravityActive: false, - init: function () { + init: function() { this.requires("2D, Supportable, Motion"); this.bind("LiftedOffGround", this._startGravity); // start gravity if we are off ground @@ -260,15 +273,14 @@ Crafty.c("Gravity", { }, _gravityCheckLanding: function(ground) { - if (this._dy < 0) - this.canLand = false; + if (this._dy < 0) this.canLand = false; }, /**@ * #.gravity * @comp Gravity * @kind Method - * + * * @sign public this .gravity([comp]) * @param comp - The name of a component that will stop this entity from falling * @@ -285,7 +297,7 @@ Crafty.c("Gravity", { * .gravity("platform"); * ~~~ */ - gravity: function (comp) { + gravity: function(comp) { this.uniqueBind("CheckLanding", this._gravityCheckLanding); this.startGroundDetection(comp); this._startGravity(); @@ -296,11 +308,11 @@ Crafty.c("Gravity", { * #.antigravity * @comp Gravity * @kind Method - * + * * @sign public this .antigravity() * Disable gravity for this component. It can be reenabled by calling .gravity() */ - antigravity: function () { + antigravity: function() { this._stopGravity(); this.stopGroundDetection(); this.unbind("CheckLanding", this._gravityCheckLanding); @@ -312,7 +324,7 @@ Crafty.c("Gravity", { * #.gravityConst * @comp Gravity * @kind Method - * + * * @sign public this .gravityConst(g) * @param g - gravitational constant in pixels per second squared * @@ -327,8 +339,9 @@ Crafty.c("Gravity", { * .gravity("platform"); * ~~~ */ - gravityConst: function (g) { - if (this._gravityActive) { // gravity active, change acceleration + gravityConst: function(g) { + if (this._gravityActive) { + // gravity active, change acceleration this.ay -= this._gravityConst; this.ay += g; } @@ -349,4 +362,3 @@ Crafty.c("Gravity", { this.vy = 0; } }); - diff --git a/src/spatial/rect-manager.js b/src/spatial/rect-manager.js index 31559395..d1cdcd4a 100644 --- a/src/spatial/rect-manager.js +++ b/src/spatial/rect-manager.js @@ -1,5 +1,4 @@ -var Crafty = require('../core/core.js'); - +var Crafty = require("../core/core.js"); /**@ * #Crafty.rectManager @@ -10,172 +9,181 @@ var Crafty = require('../core/core.js'); */ Crafty.extend({ /** recManager: an object for managing dirty rectangles. */ - rectManager: { - /** Finds smallest rectangles that overlaps a and b, merges them into target */ - merge: function (a, b, target) { - if (typeof target === 'undefined') - target = {}; - // Doing it in this order means we can use either a or b as the target, with no conflict - target._h = Math.max(a._y + a._h, b._y + b._h); - target._w = Math.max(a._x + a._w, b._x + b._w); - target._x = Math.min(a._x, b._x); - target._y = Math.min(a._y, b._y); - target._w -= target._x; - target._h -= target._y; - - return target; - }, - - /**@ - * #Crafty.rectManager.overlap - * @comp Crafty.rectManager - * @kind Method - * - * @sign public Boolean Crafty.rectManager.overlap(Object rectA, Object rectA) - * @param rectA - An object that must have the `_x, _y, _w, _h` values as properties - * @param rectB - An object that must have the `_x, _y, _w, _h` values as properties - * @return true if the rectangles overlap; false otherwise - * - * Checks whether two rectangles overlap. - */ - overlap: function (rectA, rectB) { - return (rectA._x < rectB._x + rectB._w && rectA._x + rectA._w > rectB._x && - rectA._y < rectB._y + rectB._h && rectA._y + rectA._h > rectB._y); - }, - - /**@ - * #Crafty.rectManager.integerBounds - * @comp Crafty.rectManager - * @kind Method - * - * @sign public Boolean Crafty.rectManager.integerBounds(Object rect) - * @param rect - An object that must have the `_x, _y, _w, _h` values as properties - * @return An enclosing rectangle with integer coordinates - * - * Calculate the smallest rectangle with integer coordinates that encloses the specified rectangle, - * modifying the passed object to have those bounds. - */ - integerBounds: function(rect) { - // Truncate to next, lower integer, but don't modify if already integer - rect._x = Math.floor(rect._x); - rect._y = Math.floor(rect._y); - // Ceil to next, higher integer, but don't modify if already integer - rect._w = Math.ceil(rect._w); - rect._h = Math.ceil(rect._h); - return rect; - }, + rectManager: { + /** Finds smallest rectangles that overlaps a and b, merges them into target */ + merge: function(a, b, target) { + if (typeof target === "undefined") target = {}; + // Doing it in this order means we can use either a or b as the target, with no conflict + target._h = Math.max(a._y + a._h, b._y + b._h); + target._w = Math.max(a._x + a._w, b._x + b._w); + target._x = Math.min(a._x, b._x); + target._y = Math.min(a._y, b._y); + target._w -= target._x; + target._h -= target._y; - /**@ - * #Crafty.rectManager.mergeSet - * @comp Crafty.rectManager - * @kind Method - * - * @sign public Object Crafty.rectManager.mergeSet(Object set) - * @param set - an array of rectangular regions - * - * Merge any consecutive, overlapping rects into each other. - * Its an optimization for the redraw regions. - * - * The order of set isn't strictly meaningful, - * but overlapping objects will often cause each other to change, - * and so might be consecutive. - */ - mergeSet: function (set) { - if (set.length < 2) return set; + return target; + }, - var i = set.length - 1; - while (i--) { - // If current and next overlap, merge them together into the first, removing the second - if (this.overlap(set[i], set[i + 1])) { - this.merge(set[i], set[i + 1], set[i]); - set.splice(i + 1, 1); - } - } + /**@ + * #Crafty.rectManager.overlap + * @comp Crafty.rectManager + * @kind Method + * + * @sign public Boolean Crafty.rectManager.overlap(Object rectA, Object rectA) + * @param rectA - An object that must have the `_x, _y, _w, _h` values as properties + * @param rectB - An object that must have the `_x, _y, _w, _h` values as properties + * @return true if the rectangles overlap; false otherwise + * + * Checks whether two rectangles overlap. + */ + overlap: function(rectA, rectB) { + return ( + rectA._x < rectB._x + rectB._w && + rectA._x + rectA._w > rectB._x && + rectA._y < rectB._y + rectB._h && + rectA._y + rectA._h > rectB._y + ); + }, - return set; - }, + /**@ + * #Crafty.rectManager.integerBounds + * @comp Crafty.rectManager + * @kind Method + * + * @sign public Boolean Crafty.rectManager.integerBounds(Object rect) + * @param rect - An object that must have the `_x, _y, _w, _h` values as properties + * @return An enclosing rectangle with integer coordinates + * + * Calculate the smallest rectangle with integer coordinates that encloses the specified rectangle, + * modifying the passed object to have those bounds. + */ + integerBounds: function(rect) { + // Truncate to next, lower integer, but don't modify if already integer + rect._x = Math.floor(rect._x); + rect._y = Math.floor(rect._y); + // Ceil to next, higher integer, but don't modify if already integer + rect._w = Math.ceil(rect._w); + rect._h = Math.ceil(rect._h); + return rect; + }, - /**@ - * #Crafty.rectManager.boundingRect - * @comp Crafty.rectManager - * @kind Method - * - * @sign public Crafty.rectManager.boundingRect(set) - * @param set - An array of rectangles - * - * - Calculate the common bounding rect of multiple canvas entities. - * - Returns coords - */ - boundingRect: function (set) { - if (!set || !set.length) return; - var i = 1, - l = set.length, - current, master = set[0], - tmp; - master = [master._x, master._y, master._x + master._w, master._y + master._h]; - while (i < l) { - current = set[i]; - tmp = [current._x, current._y, current._x + current._w, current._y + current._h]; - if (tmp[0] < master[0]) master[0] = tmp[0]; - if (tmp[1] < master[1]) master[1] = tmp[1]; - if (tmp[2] > master[2]) master[2] = tmp[2]; - if (tmp[3] > master[3]) master[3] = tmp[3]; - i++; - } - tmp = master; - master = { - _x: tmp[0], - _y: tmp[1], - _w: tmp[2] - tmp[0], - _h: tmp[3] - tmp[1] - }; + /**@ + * #Crafty.rectManager.mergeSet + * @comp Crafty.rectManager + * @kind Method + * + * @sign public Object Crafty.rectManager.mergeSet(Object set) + * @param set - an array of rectangular regions + * + * Merge any consecutive, overlapping rects into each other. + * Its an optimization for the redraw regions. + * + * The order of set isn't strictly meaningful, + * but overlapping objects will often cause each other to change, + * and so might be consecutive. + */ + mergeSet: function(set) { + if (set.length < 2) return set; - return master; - }, + var i = set.length - 1; + while (i--) { + // If current and next overlap, merge them together into the first, removing the second + if (this.overlap(set[i], set[i + 1])) { + this.merge(set[i], set[i + 1], set[i]); + set.splice(i + 1, 1); + } + } - // Crafty.rectManager._rectPool - // - // This is a private object used internally by 2D methods - // Cascade and _attr need to keep track of an entity's old position, - // but we want to avoid creating temp objects every time an attribute is set. - // The solution is to have a pool of objects that can be reused. - // - // The current implementation makes a BIG ASSUMPTION: that if multiple rectangles are requested, - // the later one is recycled before any preceding ones. This matches how they are used in the code. - // Each rect is created by a triggered event, and will be recycled by the time the event is complete. - _pool: (function () { - var pool = [], - pointer = 0; - return { - get: function (x, y, w, h) { - if (pool.length <= pointer) - pool.push({}); - var r = pool[pointer++]; - r._x = x; - r._y = y; - r._w = w; - r._h = h; - return r; - }, + return set; + }, - copy: function (o) { - if (pool.length <= pointer) - pool.push({}); - var r = pool[pointer++]; - r._x = o._x; - r._y = o._y; - r._w = o._w; - r._h = o._h; - return r; - }, + /**@ + * #Crafty.rectManager.boundingRect + * @comp Crafty.rectManager + * @kind Method + * + * @sign public Crafty.rectManager.boundingRect(set) + * @param set - An array of rectangles + * + * - Calculate the common bounding rect of multiple canvas entities. + * - Returns coords + */ + boundingRect: function(set) { + if (!set || !set.length) return; + var i = 1, + l = set.length, + current, + master = set[0], + tmp; + master = [ + master._x, + master._y, + master._x + master._w, + master._y + master._h + ]; + while (i < l) { + current = set[i]; + tmp = [ + current._x, + current._y, + current._x + current._w, + current._y + current._h + ]; + if (tmp[0] < master[0]) master[0] = tmp[0]; + if (tmp[1] < master[1]) master[1] = tmp[1]; + if (tmp[2] > master[2]) master[2] = tmp[2]; + if (tmp[3] > master[3]) master[3] = tmp[3]; + i++; + } + tmp = master; + master = { + _x: tmp[0], + _y: tmp[1], + _w: tmp[2] - tmp[0], + _h: tmp[3] - tmp[1] + }; - recycle: function (o) { - pointer--; - } - }; - })(), + return master; + }, - } + // Crafty.rectManager._rectPool + // + // This is a private object used internally by 2D methods + // Cascade and _attr need to keep track of an entity's old position, + // but we want to avoid creating temp objects every time an attribute is set. + // The solution is to have a pool of objects that can be reused. + // + // The current implementation makes a BIG ASSUMPTION: that if multiple rectangles are requested, + // the later one is recycled before any preceding ones. This matches how they are used in the code. + // Each rect is created by a triggered event, and will be recycled by the time the event is complete. + _pool: (function() { + var pool = [], + pointer = 0; + return { + get: function(x, y, w, h) { + if (pool.length <= pointer) pool.push({}); + var r = pool[pointer++]; + r._x = x; + r._y = y; + r._w = w; + r._h = h; + return r; + }, + copy: function(o) { + if (pool.length <= pointer) pool.push({}); + var r = pool[pointer++]; + r._x = o._x; + r._y = o._y; + r._w = o._w; + r._h = o._h; + return r; + }, + recycle: function(o) { + pointer--; + } + }; + })() + } }); diff --git a/src/spatial/spatial-grid.js b/src/spatial/spatial-grid.js index 3a125194..c6eee877 100644 --- a/src/spatial/spatial-grid.js +++ b/src/spatial/spatial-grid.js @@ -4,7 +4,6 @@ * @author Louis Stowasser */ - /**@ * #Crafty.HashMap * @category 2D @@ -31,7 +30,7 @@ var cellsize, // TODO: make cellsize an instance property, so multiple maps with * Set `cellsize`. * And create `this.map`. */ -var HashMap = function (cell) { +var HashMap = function(cell) { cellsize = cell || 64; this.map = {}; @@ -66,7 +65,7 @@ var HashMap = function (cell) { * * @see Crafty.HashMap.constructor */ -HashMap.key = function (obj, keys) { +HashMap.key = function(obj, keys) { obj = obj._cbr || obj._mbr || obj; keys = keys || {}; @@ -77,7 +76,7 @@ HashMap.key = function (obj, keys) { return keys; }; -HashMap.hash = function (keys) { +HashMap.hash = function(keys) { return keys.x1 + SPACE + keys.y1 + SPACE + keys.x2 + SPACE + keys.y2; }; @@ -114,7 +113,7 @@ HashMap.prototype = { * } * ~~~ */ - insert: function (obj, entry) { + insert: function(obj, entry) { var i, j, hash; var keys = HashMap.key(obj, entry && entry.keys); entry = entry || new Entry(keys, obj, this); @@ -164,10 +163,13 @@ HashMap.prototype = { * } * ~~~ */ - _searchHolder: [], - search: function (rect, results) { + _searchHolder: [], + search: function(rect, results) { var keys = HashMap.key(rect, keyHolder), - i, j, k, cell, + i, + j, + k, + cell, previouslyChecked = this._searchHolder; results = results || []; previouslyChecked.length = 0; @@ -182,8 +184,12 @@ HashMap.prototype = { if (previouslyChecked[cell[k][0]]) continue; obj = previouslyChecked[cell[k][0]] = cell[k]; obj = obj._cbr || obj._mbr || obj; - if (obj._x < rect._x + rect._w && obj._x + obj._w > rect._x && - obj._y < rect._y + rect._h && obj._y + obj._h > rect._y) { + if ( + obj._x < rect._x + rect._w && + obj._x + obj._w > rect._x && + obj._y < rect._y + rect._h && + obj._y + obj._h > rect._y + ) { results.push(cell[k]); } } @@ -207,15 +213,18 @@ HashMap.prototype = { * * Do a search for entities in the given region. Returned entities are **not** guaranteed * to overlap with the given region, and the results may contain duplicates. - * + * * This method is intended to be used as the first step of a more complex search. * More common use cases should use Crafty.map.search, which filters the results. - * + * * @see Crafty.map.search */ unfilteredSearch: function(rect, results) { var keys = HashMap.key(rect, keyHolder), - i, j, k, cell; + i, + j, + k, + cell; results = results || []; //search in all x buckets @@ -248,11 +257,12 @@ HashMap.prototype = { * Crafty.map.remove(e); * ~~~ */ - remove: function (entry) { + remove: function(entry) { var keys = entry.keys; var obj = entry.obj; var i = 0, - j, hash; + j, + hash; //search in all x buckets for (i = keys.x1; i <= keys.x2; i++) { @@ -262,11 +272,11 @@ HashMap.prototype = { if (this.map[hash]) { var cell = this.map[hash], - m, n = cell.length; + 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); + if (cell[m] && cell[m][0] === obj[0]) cell.splice(m, 1); } } } @@ -290,7 +300,7 @@ HashMap.prototype = { * Crafty.map.refresh(e); * ~~~ */ - refresh: function (entry) { + refresh: function(entry) { var keys = entry.keys; var obj = entry.obj; var cell, i, j, m, n; @@ -303,8 +313,7 @@ HashMap.prototype = { 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); + if (cell[m] && cell[m][0] === obj[0]) cell.splice(m, 1); } } } @@ -327,7 +336,6 @@ HashMap.prototype = { return entry; }, - /**@ * #Crafty.map.boundaries * @comp Crafty.map @@ -396,9 +404,8 @@ HashMap.prototype = { // update map boundaries if they were changed if (!this.boundsDirty && !this.coordBoundsDirty) return; - var hash = this.boundsHash; - // Optimization: if no entities have moved cells, + // Optimization: if no entities have moved cells, // we don't need to recalculate the hash boundaries if (this.boundsDirty) { hash.maxX = -Infinity; @@ -432,7 +439,7 @@ HashMap.prototype = { ent = cell[k]; //TODO: remove these checks introduced by 25e7c88f61f64525adc32f7fd776099413cb1567? //make sure that this is a Crafty entity - if (typeof ent === 'object' && 'requires' in ent) { + if (typeof ent === "object" && "requires" in ent) { coords.maxX = Math.max(coords.maxX, ent.x + ent.w); } } @@ -441,7 +448,7 @@ HashMap.prototype = { hash.minX = i; for (k in cell) { ent = cell[k]; - if (typeof ent === 'object' && 'requires' in ent) { + if (typeof ent === "object" && "requires" in ent) { coords.minX = Math.min(coords.minX, ent.x); } } @@ -450,7 +457,7 @@ HashMap.prototype = { hash.maxY = j; for (k in cell) { ent = cell[k]; - if (typeof ent === 'object' && 'requires' in ent) { + if (typeof ent === "object" && "requires" in ent) { coords.maxY = Math.max(coords.maxY, ent.y + ent.h); } } @@ -459,7 +466,7 @@ HashMap.prototype = { hash.minY = j; for (k in cell) { ent = cell[k]; - if (typeof ent === 'object' && 'requires' in ent) { + if (typeof ent === "object" && "requires" in ent) { coords.minY = Math.min(coords.minY, ent.y); } } @@ -565,7 +572,6 @@ HashMap.prototype = { _h: 0 }; - var keyBounds = this._keyBoundaries(); var keys = HashMap.key(origin, keyHolder); @@ -577,13 +583,14 @@ HashMap.prototype = { maxCol = keyBounds.maxX, maxRow = keyBounds.maxY; // direction to traverse cells - var stepCol = dirX > 0 ? 1 : (dirX < 0 ? -1 : 0), - stepRow = dirY > 0 ? 1 : (dirY < 0 ? -1 : 0); - + var stepCol = dirX > 0 ? 1 : dirX < 0 ? -1 : 0, + stepRow = dirY > 0 ? 1 : dirY < 0 ? -1 : 0; // first, next cell edge in absolute coordinates - var firstCellEdgeX = (dirX >= 0) ? (currentCol + 1) * cellsize : currentCol * cellsize, - firstCellEdgeY = (dirY >= 0) ? (currentRow + 1) * cellsize : currentRow * cellsize; + var firstCellEdgeX = + dirX >= 0 ? (currentCol + 1) * cellsize : currentCol * cellsize, + firstCellEdgeY = + dirY >= 0 ? (currentRow + 1) * cellsize : currentRow * cellsize; // distance from origin to previous cell edge var previousDistance = -Infinity; @@ -598,19 +605,21 @@ HashMap.prototype = { if (dirX !== 0) { norm = 1.0 / dirX; nextDistanceX = (firstCellEdgeX - origin._x) * norm; - deltaDistanceX = (cellsize * stepCol) * norm; + deltaDistanceX = cellsize * stepCol * norm; } if (dirY !== 0) { norm = 1.0 / dirY; nextDistanceY = (firstCellEdgeY - origin._y) * norm; - deltaDistanceY = (cellsize * stepRow) * norm; + deltaDistanceY = cellsize * stepRow * norm; } - // advance starting cell to be inside of map bounds - while ((stepCol === 1 && currentCol < minCol && minCol !== Infinity) || (stepCol === -1 && currentCol > maxCol && maxCol !== -Infinity) || - (stepRow === 1 && currentRow < minRow && minRow !== Infinity) || (stepRow === -1 && currentRow > maxRow && maxRow !== -Infinity)) { - + while ( + (stepCol === 1 && currentCol < minCol && minCol !== Infinity) || + (stepCol === -1 && currentCol > maxCol && maxCol !== -Infinity) || + (stepRow === 1 && currentRow < minRow && minRow !== Infinity) || + (stepRow === -1 && currentRow > maxRow && maxRow !== -Infinity) + ) { // advance to closest cell if (nextDistanceX < nextDistanceY) { previousDistance = nextDistanceX; @@ -628,16 +637,17 @@ HashMap.prototype = { var cell; // traverse over cells // TODO: maybe change condition to `while (currentCol !== endX) || (currentRow !== endY)` - while ((minCol <= currentCol && currentCol <= maxCol) && - (minRow <= currentRow && currentRow <= maxRow)) { - + while ( + minCol <= currentCol && + currentCol <= maxCol && + (minRow <= currentRow && currentRow <= maxRow) + ) { // process cell if ((cell = this.map[(currentCol << 16) ^ currentRow])) { // check each object inside this cell for (var k = 0; k < cell.length; k++) { // if supplied callback returns true, abort traversal - if (callback(cell[k], previousDistance)) - return; + if (callback(cell[k], previousDistance)) return; } } @@ -655,7 +665,6 @@ HashMap.prototype = { } } } - }; function Entry(keys, obj, map) { @@ -665,15 +674,17 @@ function Entry(keys, obj, map) { } Entry.prototype = { - update: function (rect) { + update: function(rect) { //check if buckets change - if (HashMap.hash(HashMap.key(rect, keyHolder)) !== HashMap.hash(this.keys)) { + if ( + HashMap.hash(HashMap.key(rect, keyHolder)) !== + HashMap.hash(this.keys) + ) { this.map.refresh(this); } else { //mark map coordinate boundaries as dirty this.map.coordBoundsDirty = true; } - } }; diff --git a/tests/unit/common.js b/tests/unit/common.js index d30d58b8..80f6ffed 100644 --- a/tests/unit/common.js +++ b/tests/unit/common.js @@ -27,30 +27,37 @@ Crafty.pause(); // Helper functions // ////////////////////// -resetStage = function() { // jshint ignore:line +resetStage = function() { + // jshint ignore:line Crafty.viewport.reset(); - Crafty.viewport.scroll('_x', 0); - Crafty.viewport.scroll('_y', 0); + Crafty.viewport.scroll("_x", 0); + Crafty.viewport.scroll("_y", 0); Crafty.viewport.clampToEntities = true; }; -Round = function(x){ // jshint ignore:line - return Math.round(x*100)/100; +Round = function(x) { + // jshint ignore:line + return Math.round(x * 100) / 100; }; -keysUp = function() { // jshint ignore:line +keysUp = function() { + // jshint ignore:line var keysToRelease = Array.prototype.slice.call(arguments); - for (var k in keysToRelease) { - var key = Crafty.keys[keysToRelease[k]] || keysToRelease[k]; - Crafty.s('Keyboard').triggerKey("KeyUp", {eventName: "KeyUp", key: key}); - } + for (var k in keysToRelease) { + var key = Crafty.keys[keysToRelease[k]] || keysToRelease[k]; + Crafty.s("Keyboard").triggerKey("KeyUp", { eventName: "KeyUp", key: key }); + } }; -keysDown = function() { // jshint ignore:line - var keysToPress = Array.prototype.slice.call(arguments); - for (var k in keysToPress) { - var key = Crafty.keys[keysToPress[k]] || keysToPress[k]; - Crafty.s('Keyboard').triggerKey("KeyDown", {eventName: "KeyDown", key: key}); - } +keysDown = function() { + // jshint ignore:line + var keysToPress = Array.prototype.slice.call(arguments); + for (var k in keysToPress) { + var key = Crafty.keys[keysToPress[k]] || keysToPress[k]; + Crafty.s("Keyboard").triggerKey("KeyDown", { + eventName: "KeyDown", + key: key + }); + } }; ////////////////// @@ -70,17 +77,17 @@ QUnit.config.reorder = false; /////////////////////// var log = []; -QUnit.testStart(function(testDetails){ - QUnit.log(function(details){ +QUnit.testStart(function(testDetails) { + QUnit.log(function(details) { if (!details.result) { details.name = testDetails.name; log.push(details); } }); }); -QUnit.done(function (test_results) { +QUnit.done(function(test_results) { var tests = []; - for(var i = 0, len = log.length; i < len; i++) { + for (var i = 0, len = log.length; i < len; i++) { var details = log[i]; tests.push({ name: details.name, @@ -91,8 +98,7 @@ QUnit.done(function (test_results) { }); } test_results.tests = tests; - if (typeof window !== "undefined") - window.global_test_results = test_results; + if (typeof window !== "undefined") window.global_test_results = test_results; else if (typeof global !== "undefined") global.global_test_results = test_results; }); diff --git a/tests/unit/controls/controls.js b/tests/unit/controls/controls.js index fb22650c..367d8794 100644 --- a/tests/unit/controls/controls.js +++ b/tests/unit/controls/controls.js @@ -5,38 +5,51 @@ module("Controls"); test("KeyboardState", function(_) { - var keyUpsE = 0, keyDownsE = 0, - keyUpsF = 0, keyDownsF = 0; + var keyUpsE = 0, + keyDownsE = 0, + keyUpsF = 0, + keyDownsF = 0; var e = Crafty.e("KeyboardState") - .bind('KeyDown', function(evt) { keyDownsE++; }) - .bind('KeyUp', function(evt) { keyUpsE++; }); + .bind("KeyDown", function(evt) { + keyDownsE++; + }) + .bind("KeyUp", function(evt) { + keyUpsE++; + }); var f = Crafty.e("KeyboardState") - .bind('KeyDown', function(evt) { keyDownsF++; }) - .bind('KeyUp', function(evt) { keyUpsF++; }); + .bind("KeyDown", function(evt) { + keyDownsF++; + }) + .bind("KeyUp", function(evt) { + keyUpsF++; + }); // initial - _.strictEqual(e.isKeyDown('UP_ARROW'), false, "1st signature"); + _.strictEqual(e.isKeyDown("UP_ARROW"), false, "1st signature"); _.strictEqual(e.isKeyDown(Crafty.keys.DOWN_ARROW), false, "2nd signature"); _.strictEqual(keyDownsE, 0); _.strictEqual(keyUpsE, 0); _.strictEqual(f.isKeyDown(Crafty.keys.UP_ARROW), false, "2nd signature"); - _.strictEqual(f.isKeyDown('DOWN_ARROW'), false, "1st signature"); + _.strictEqual(f.isKeyDown("DOWN_ARROW"), false, "1st signature"); _.strictEqual(keyDownsF, 0); _.strictEqual(keyUpsF, 0); // after e receives invalid KeyUp e.triggerKey("KeyUp", { eventName: "KeyUp", key: Crafty.keys.UP_ARROW }); _.strictEqual(e.isKeyDown(Crafty.keys.UP_ARROW), false, "2nd signature"); - _.strictEqual(e.isKeyDown('DOWN_ARROW'), false, "1st signature"); + _.strictEqual(e.isKeyDown("DOWN_ARROW"), false, "1st signature"); _.strictEqual(keyDownsE, 0); _.strictEqual(keyUpsE, 0); - _.strictEqual(f.isKeyDown('UP_ARROW'), false, "1st signature"); + _.strictEqual(f.isKeyDown("UP_ARROW"), false, "1st signature"); _.strictEqual(f.isKeyDown(Crafty.keys.DOWN_ARROW), false, "2nd signature"); _.strictEqual(keyDownsF, 0); _.strictEqual(keyUpsF, 0); // after e receives valid KeyDown - e.triggerKey("KeyDown", { eventName: "KeyDown", key: Crafty.keys.UP_ARROW }); + e.triggerKey("KeyDown", { + eventName: "KeyDown", + key: Crafty.keys.UP_ARROW + }); _.strictEqual(e.isKeyDown(Crafty.keys.UP_ARROW), true); _.strictEqual(e.isKeyDown(Crafty.keys.DOWN_ARROW), false); _.strictEqual(keyDownsE, 1); @@ -47,7 +60,10 @@ _.strictEqual(keyUpsF, 0); // after e receives invalid KeyDown - e.triggerKey("KeyDown", { eventName: "KeyDown", key: Crafty.keys.UP_ARROW }); + e.triggerKey("KeyDown", { + eventName: "KeyDown", + key: Crafty.keys.UP_ARROW + }); _.strictEqual(e.isKeyDown(Crafty.keys.UP_ARROW), true); _.strictEqual(e.isKeyDown(Crafty.keys.DOWN_ARROW), false); _.strictEqual(keyDownsE, 1); @@ -58,7 +74,10 @@ _.strictEqual(keyUpsF, 0); // after f receives valid KeyDown, check if it messes with e - f.triggerKey("KeyDown", { eventName: "KeyDown", key: Crafty.keys.UP_ARROW }); + f.triggerKey("KeyDown", { + eventName: "KeyDown", + key: Crafty.keys.UP_ARROW + }); _.strictEqual(e.isKeyDown(Crafty.keys.UP_ARROW), true); _.strictEqual(e.isKeyDown(Crafty.keys.DOWN_ARROW), false); _.strictEqual(keyDownsE, 1); @@ -69,7 +88,10 @@ _.strictEqual(keyUpsF, 0); // after e receives valid KeyDown for different key, check if it messes with both - e.triggerKey("KeyDown", { eventName: "KeyDown", key: Crafty.keys.DOWN_ARROW }); + e.triggerKey("KeyDown", { + eventName: "KeyDown", + key: Crafty.keys.DOWN_ARROW + }); _.strictEqual(e.isKeyDown(Crafty.keys.UP_ARROW), true); _.strictEqual(e.isKeyDown(Crafty.keys.DOWN_ARROW), true); _.strictEqual(keyDownsE, 2); @@ -102,39 +124,72 @@ _.strictEqual(keyUpsF, 1); }); - test("MouseState", function(_) { // this is a copy of KeyboardState tests // - var buttonUpsE = 0, buttonDownsE = 0, - buttonUpsF = 0, buttonDownsF = 0; + test("MouseState", function(_) { + // this is a copy of KeyboardState tests // + var buttonUpsE = 0, + buttonDownsE = 0, + buttonUpsF = 0, + buttonDownsF = 0; var e = Crafty.e("MouseState") - .bind('MouseDown', function(evt) { buttonDownsE++; }) - .bind('MouseUp', function(evt) { buttonUpsE++; }); + .bind("MouseDown", function(evt) { + buttonDownsE++; + }) + .bind("MouseUp", function(evt) { + buttonUpsE++; + }); var f = Crafty.e("MouseState") - .bind('MouseDown', function(evt) { buttonDownsF++; }) - .bind('MouseUp', function(evt) { buttonUpsF++; }); + .bind("MouseDown", function(evt) { + buttonDownsF++; + }) + .bind("MouseUp", function(evt) { + buttonUpsF++; + }); // initial - _.strictEqual(e.isButtonDown('LEFT'), false, "1st signature"); - _.strictEqual(e.isButtonDown(Crafty.mouseButtons.RIGHT), false, "2nd signature"); + _.strictEqual(e.isButtonDown("LEFT"), false, "1st signature"); + _.strictEqual( + e.isButtonDown(Crafty.mouseButtons.RIGHT), + false, + "2nd signature" + ); _.strictEqual(buttonDownsE, 0); _.strictEqual(buttonUpsE, 0); - _.strictEqual(f.isButtonDown(Crafty.mouseButtons.LEFT), false, "2nd signature"); - _.strictEqual(f.isButtonDown('RIGHT'), false, "1st signature"); + _.strictEqual( + f.isButtonDown(Crafty.mouseButtons.LEFT), + false, + "2nd signature" + ); + _.strictEqual(f.isButtonDown("RIGHT"), false, "1st signature"); _.strictEqual(buttonDownsF, 0); _.strictEqual(buttonUpsF, 0); // after e receives invalid MouseUp - e.triggerMouse("MouseUp", { eventName: "MouseUp", mouseButton: Crafty.mouseButtons.LEFT }); - _.strictEqual(e.isButtonDown(Crafty.mouseButtons.LEFT), false, "2nd signature"); - _.strictEqual(e.isButtonDown('RIGHT'), false, "1st signature"); + e.triggerMouse("MouseUp", { + eventName: "MouseUp", + mouseButton: Crafty.mouseButtons.LEFT + }); + _.strictEqual( + e.isButtonDown(Crafty.mouseButtons.LEFT), + false, + "2nd signature" + ); + _.strictEqual(e.isButtonDown("RIGHT"), false, "1st signature"); _.strictEqual(buttonDownsE, 0); _.strictEqual(buttonUpsE, 0); - _.strictEqual(f.isButtonDown('LEFT'), false, "1st signature"); - _.strictEqual(f.isButtonDown(Crafty.mouseButtons.RIGHT), false, "2nd signature"); + _.strictEqual(f.isButtonDown("LEFT"), false, "1st signature"); + _.strictEqual( + f.isButtonDown(Crafty.mouseButtons.RIGHT), + false, + "2nd signature" + ); _.strictEqual(buttonDownsF, 0); _.strictEqual(buttonUpsF, 0); // after e receives valid MouseDown - e.triggerMouse("MouseDown", { eventName: "MouseDown", mouseButton: Crafty.mouseButtons.LEFT }); + e.triggerMouse("MouseDown", { + eventName: "MouseDown", + mouseButton: Crafty.mouseButtons.LEFT + }); _.strictEqual(e.isButtonDown(Crafty.mouseButtons.LEFT), true); _.strictEqual(e.isButtonDown(Crafty.mouseButtons.RIGHT), false); _.strictEqual(buttonDownsE, 1); @@ -145,7 +200,10 @@ _.strictEqual(buttonUpsF, 0); // after e receives invalid MouseDown - e.triggerMouse("MouseDown", { eventName: "MouseDown", mouseButton: Crafty.mouseButtons.LEFT }); + e.triggerMouse("MouseDown", { + eventName: "MouseDown", + mouseButton: Crafty.mouseButtons.LEFT + }); _.strictEqual(e.isButtonDown(Crafty.mouseButtons.LEFT), true); _.strictEqual(e.isButtonDown(Crafty.mouseButtons.RIGHT), false); _.strictEqual(buttonDownsE, 1); @@ -156,7 +214,10 @@ _.strictEqual(buttonUpsF, 0); // after f receives valid MouseDown, check if it messes with e - f.triggerMouse("MouseDown", { eventName: "MouseDown", mouseButton: Crafty.mouseButtons.LEFT }); + f.triggerMouse("MouseDown", { + eventName: "MouseDown", + mouseButton: Crafty.mouseButtons.LEFT + }); _.strictEqual(e.isButtonDown(Crafty.mouseButtons.LEFT), true); _.strictEqual(e.isButtonDown(Crafty.mouseButtons.RIGHT), false); _.strictEqual(buttonDownsE, 1); @@ -167,7 +228,10 @@ _.strictEqual(buttonUpsF, 0); // after e receives valid MouseDown for different mouseButton, check if it messes with both - e.triggerMouse("MouseDown", { eventName: "MouseDown", mouseButton: Crafty.mouseButtons.RIGHT }); + e.triggerMouse("MouseDown", { + eventName: "MouseDown", + mouseButton: Crafty.mouseButtons.RIGHT + }); _.strictEqual(e.isButtonDown(Crafty.mouseButtons.LEFT), true); _.strictEqual(e.isButtonDown(Crafty.mouseButtons.RIGHT), true); _.strictEqual(buttonDownsE, 2); @@ -189,7 +253,10 @@ _.strictEqual(buttonUpsF, 0); // after f receives valid MouseUp, check final status - f.triggerMouse("MouseUp", { eventName: "MouseUp", mouseButton: Crafty.mouseButtons.LEFT }); + f.triggerMouse("MouseUp", { + eventName: "MouseUp", + mouseButton: Crafty.mouseButtons.LEFT + }); _.strictEqual(e.isButtonDown(Crafty.mouseButtons.LEFT), false); _.strictEqual(e.isButtonDown(Crafty.mouseButtons.RIGHT), false); _.strictEqual(buttonDownsE, 2); @@ -202,31 +269,47 @@ test("MouseState - lastMouseEvent", function(_) { var e = Crafty.e("MouseState"), - lastMouseEventA, lastMouseEventB, - originalEventA, originalEventB; + lastMouseEventA, + lastMouseEventB, + originalEventA, + originalEventB; // initial state lastMouseEventA = e.lastMouseEvent; originalEventA = lastMouseEventA.originalEvent; - // after e receives valid MouseDown, lastMouseEvent persisted + // after e receives valid MouseDown, lastMouseEvent persisted e.triggerMouse("MouseDown", { eventName: "MouseDown", mouseButton: Crafty.mouseButtons.LEFT, - target: 'a', - realX: 1, realY: 2, - clientX: 3, clientY: 4, // DEPRECATED: remove in upcoming release + target: "a", + realX: 1, + realY: 2, + clientX: 3, + clientY: 4, // DEPRECATED: remove in upcoming release originalEvent: { prop1: true } }); lastMouseEventB = e.lastMouseEvent; originalEventB = lastMouseEventB.originalEvent; - _.strictEqual(lastMouseEventB, lastMouseEventA, "lastEvent objects are reused"); - _.strictEqual(lastMouseEventB.originalEvent, lastMouseEventA.originalEvent, "originalEvent is not a clone, but merely a reference"); - _.notEqual(originalEventB, originalEventA, "originalEvent reference changed"); + _.strictEqual( + lastMouseEventB, + lastMouseEventA, + "lastEvent objects are reused" + ); + _.strictEqual( + lastMouseEventB.originalEvent, + lastMouseEventA.originalEvent, + "originalEvent is not a clone, but merely a reference" + ); + _.notEqual( + originalEventB, + originalEventA, + "originalEvent reference changed" + ); _.strictEqual(lastMouseEventB.originalEvent.prop1, true); _.strictEqual(lastMouseEventB.eventName, "MouseDown"); _.strictEqual(lastMouseEventB.mouseButton, Crafty.mouseButtons.LEFT); - _.strictEqual(lastMouseEventB.target, 'a'); + _.strictEqual(lastMouseEventB.target, "a"); _.strictEqual(lastMouseEventB.realX, 1); _.strictEqual(lastMouseEventB.realY, 2); _.strictEqual(lastMouseEventB.clientX, 3); @@ -238,15 +321,25 @@ e.triggerMouse("MouseDown", { eventName: "MouseDown", mouseButton: Crafty.mouseButtons.LEFT, - target: 'b', - realX: 5, realY: 6, - clientX: 7, clientY: 8, // DEPRECATED: remove in upcoming release + target: "b", + realX: 5, + realY: 6, + clientX: 7, + clientY: 8, // DEPRECATED: remove in upcoming release originalEvent: { prop2: true } }); lastMouseEventB = e.lastMouseEvent; originalEventB = lastMouseEventB.originalEvent; - _.strictEqual(lastMouseEventB, lastMouseEventA, "lastEvent objects are reused"); - _.deepEqual(lastMouseEventB, lastMouseEventA, "lastEvent objects have same content"); + _.strictEqual( + lastMouseEventB, + lastMouseEventA, + "lastEvent objects are reused" + ); + _.deepEqual( + lastMouseEventB, + lastMouseEventA, + "lastEvent objects have same content" + ); lastMouseEventA = lastMouseEventB; originalEventA = lastMouseEventA.originalEvent; @@ -254,20 +347,34 @@ e.triggerMouse("MouseUp", { eventName: "MouseUp", mouseButton: Crafty.mouseButtons.LEFT, - target: 'c', - realX: 9, realY: 0, - clientX: -1, clientY: -2, // DEPRECATED: remove in upcoming release + target: "c", + realX: 9, + realY: 0, + clientX: -1, + clientY: -2, // DEPRECATED: remove in upcoming release originalEvent: { prop3: true } }); lastMouseEventB = e.lastMouseEvent; originalEventB = lastMouseEventB.originalEvent; - _.strictEqual(lastMouseEventB, lastMouseEventA, "lastEvent objects are reused"); - _.strictEqual(lastMouseEventB.originalEvent, lastMouseEventA.originalEvent, "originalEvent is not a clone, but merely a reference"); - _.notEqual(originalEventB, originalEventA, "originalEvent reference changed"); + _.strictEqual( + lastMouseEventB, + lastMouseEventA, + "lastEvent objects are reused" + ); + _.strictEqual( + lastMouseEventB.originalEvent, + lastMouseEventA.originalEvent, + "originalEvent is not a clone, but merely a reference" + ); + _.notEqual( + originalEventB, + originalEventA, + "originalEvent reference changed" + ); _.strictEqual(lastMouseEventB.originalEvent.prop3, true); _.strictEqual(lastMouseEventB.eventName, "MouseUp"); _.strictEqual(lastMouseEventB.mouseButton, Crafty.mouseButtons.LEFT); - _.strictEqual(lastMouseEventB.target, 'c'); + _.strictEqual(lastMouseEventB.target, "c"); _.strictEqual(lastMouseEventB.realX, 9); _.strictEqual(lastMouseEventB.realY, 0); _.strictEqual(lastMouseEventB.clientX, -1); @@ -277,48 +384,58 @@ }); test("TouchState", function(_) { - var finger0Starts = 0, finger0Moves = 0, finger0Ends = 0, finger0Cancels = 0, - finger1Starts = 0, finger1Moves = 0, finger1Ends = 0, finger1Cancels = 0; + var finger0Starts = 0, + finger0Moves = 0, + finger0Ends = 0, + finger0Cancels = 0, + finger1Starts = 0, + finger1Moves = 0, + finger1Ends = 0, + finger1Cancels = 0; var e = Crafty.e("TouchState") - .bind('TouchStart', function(evt) { - if (evt.identifier === 0) finger0Starts++; - else if (evt.identifier === 1) finger1Starts++; - else _.ok(false, "Unexpected touch identifier event received."); - }) - .bind('TouchMove', function(evt) { - if (evt.identifier === 0) finger0Moves++; - else if (evt.identifier === 1) finger1Moves++; - else _.ok(false, "Unexpected touch identifier event received."); - }) - .bind('TouchEnd', function(evt) { - if (evt.identifier === 0) finger0Ends++; - else if (evt.identifier === 1) finger1Ends++; - else _.ok(false, "Unexpected touch identifier event received."); - }) - .bind('TouchCancel', function(evt) { - if (evt.identifier === 0) finger0Cancels++; - else if (evt.identifier === 1) finger1Cancels++; - else _.ok(false, "Unexpected touch identifier event received."); - }); + .bind("TouchStart", function(evt) { + if (evt.identifier === 0) finger0Starts++; + else if (evt.identifier === 1) finger1Starts++; + else _.ok(false, "Unexpected touch identifier event received."); + }) + .bind("TouchMove", function(evt) { + if (evt.identifier === 0) finger0Moves++; + else if (evt.identifier === 1) finger1Moves++; + else _.ok(false, "Unexpected touch identifier event received."); + }) + .bind("TouchEnd", function(evt) { + if (evt.identifier === 0) finger0Ends++; + else if (evt.identifier === 1) finger1Ends++; + else _.ok(false, "Unexpected touch identifier event received."); + }) + .bind("TouchCancel", function(evt) { + if (evt.identifier === 0) finger0Cancels++; + else if (evt.identifier === 1) finger1Cancels++; + else _.ok(false, "Unexpected touch identifier event received."); + }); // check that TouchState doesn't cause conflicts between entities Crafty.e("TouchState") - .bind('TouchStart', function(evt) { - _.ok(false, "Unexpected touch identifier event received."); - }) - .bind('TouchMove', function(evt) { - _.ok(false, "Unexpected touch identifier event received."); - }) - .bind('TouchEnd', function(evt) { - _.ok(false, "Unexpected touch identifier event received."); - }) - .bind('TouchCancel', function(evt) { - _.ok(false, "Unexpected touch identifier event received."); - }); + .bind("TouchStart", function(evt) { + _.ok(false, "Unexpected touch identifier event received."); + }) + .bind("TouchMove", function(evt) { + _.ok(false, "Unexpected touch identifier event received."); + }) + .bind("TouchEnd", function(evt) { + _.ok(false, "Unexpected touch identifier event received."); + }) + .bind("TouchCancel", function(evt) { + _.ok(false, "Unexpected touch identifier event received."); + }); // initial _.strictEqual(e.touchPoints.length, 0, "no current touch points"); - _.strictEqual(e._touchPointsPool.length, 0, "no current reusable touch points"); + _.strictEqual( + e._touchPointsPool.length, + 0, + "no current reusable touch points" + ); _.strictEqual(finger0Starts, 0); _.strictEqual(finger0Moves, 0); _.strictEqual(finger0Ends, 0); @@ -331,7 +448,11 @@ // after e receives invalid TouchMove for finger 0 e.triggerTouch("TouchMove", { eventName: "TouchMove", identifier: 0 }); _.strictEqual(e.touchPoints.length, 0, "no current touch points"); - _.strictEqual(e._touchPointsPool.length, 0, "no current reusable touch points"); + _.strictEqual( + e._touchPointsPool.length, + 0, + "no current reusable touch points" + ); _.strictEqual(finger0Starts, 0); _.strictEqual(finger0Moves, 0); _.strictEqual(finger0Ends, 0); @@ -511,8 +632,10 @@ test("TouchState - touchPoints", function(_) { var e = Crafty.e("TouchState"), - touchPointA, touchPointB, - originalEventA, originalEventB; + touchPointA, + touchPointB, + originalEventA, + originalEventB; // initial state touchPointA = e.touchPoints[0]; @@ -520,13 +643,14 @@ _.strictEqual(touchPointA, undefined, "no initial touchPoint"); _.strictEqual(originalEventA, undefined, "no initial originalEvent"); - // after e receives valid TouchStart, touchPoint persisted + // after e receives valid TouchStart, touchPoint persisted e.triggerTouch("TouchStart", { eventName: "TouchStart", identifier: -3, - target: 'a', - entity: 'a', // DEPRECATED: remove in upcoming release - realX: 1, realY: 2, + target: "a", + entity: "a", // DEPRECATED: remove in upcoming release + realX: 1, + realY: 2, originalEvent: { prop1: true } }); touchPointB = e.touchPoints[0]; @@ -537,8 +661,8 @@ _.strictEqual(touchPointB.originalEvent.prop1, true); _.strictEqual(touchPointB.eventName, "TouchStart"); _.strictEqual(touchPointB.identifier, -3); - _.strictEqual(touchPointB.target, 'a'); - _.strictEqual(touchPointB.entity, 'a'); + _.strictEqual(touchPointB.target, "a"); + _.strictEqual(touchPointB.entity, "a"); _.strictEqual(touchPointB.realX, 1); _.strictEqual(touchPointB.realY, 2); touchPointA = touchPointB; @@ -548,16 +672,21 @@ e.triggerTouch("TouchStart", { eventName: "TouchStart", identifier: -3, - target: 'b', - entity: 'b', // DEPRECATED: remove in upcoming release - realX: 3, realY: 4, + target: "b", + entity: "b", // DEPRECATED: remove in upcoming release + realX: 3, + realY: 4, originalEvent: { prop2: true } }); touchPointB = e.touchPoints[0]; originalEventB = touchPointB.originalEvent; _.strictEqual(e.touchPoints.length, 1, "One touchPoint exists"); _.strictEqual(touchPointB, touchPointA, "touchPoint objects are reused"); - _.deepEqual(touchPointB, touchPointA, "touchPoint objects have same content"); + _.deepEqual( + touchPointB, + touchPointA, + "touchPoint objects have same content" + ); touchPointA = touchPointB; originalEventA = touchPointA.originalEvent; @@ -565,22 +694,31 @@ e.triggerTouch("TouchMove", { eventName: "TouchMove", identifier: -3, - target: 'c', - entity: 'c', // DEPRECATED: remove in upcoming release - realX: 5, realY: 6, + target: "c", + entity: "c", // DEPRECATED: remove in upcoming release + realX: 5, + realY: 6, originalEvent: { prop3: true } }); touchPointB = e.touchPoints[0]; originalEventB = touchPointB.originalEvent; _.strictEqual(e.touchPoints.length, 1, "One touchPoint exists"); _.strictEqual(touchPointB, touchPointA, "touchPoint objects are reused"); - _.strictEqual(touchPointB.originalEvent, touchPointA.originalEvent, "originalEvent is not a clone, but merely a reference"); - _.notEqual(originalEventB, originalEventA, "originalEvent reference changed"); + _.strictEqual( + touchPointB.originalEvent, + touchPointA.originalEvent, + "originalEvent is not a clone, but merely a reference" + ); + _.notEqual( + originalEventB, + originalEventA, + "originalEvent reference changed" + ); _.strictEqual(touchPointB.originalEvent.prop3, true); _.strictEqual(touchPointB.eventName, "TouchMove"); _.strictEqual(touchPointB.identifier, -3); - _.strictEqual(touchPointB.target, 'c'); - _.strictEqual(touchPointB.entity, 'c'); + _.strictEqual(touchPointB.target, "c"); + _.strictEqual(touchPointB.entity, "c"); _.strictEqual(touchPointB.realX, 5); _.strictEqual(touchPointB.realY, 6); touchPointA = touchPointB; @@ -590,28 +728,48 @@ e.triggerTouch("TouchStart", { eventName: "TouchStart", identifier: 11, - target: 'd', - entity: 'd', // DEPRECATED: remove in upcoming release - realX: 7, realY: 8, + target: "d", + entity: "d", // DEPRECATED: remove in upcoming release + realX: 7, + realY: 8, originalEvent: { prop4: true } }); touchPointB = e.touchPoints[0]; originalEventB = touchPointB.originalEvent; _.strictEqual(e.touchPoints.length, 2, "Two touchPoints exist"); - _.strictEqual(touchPointB, touchPointA, "touchPoint objects for first finger are reused"); - _.deepEqual(touchPointB, touchPointA, "touchPoint objects for first finger have same content"); + _.strictEqual( + touchPointB, + touchPointA, + "touchPoint objects for first finger are reused" + ); + _.deepEqual( + touchPointB, + touchPointA, + "touchPoint objects for first finger have same content" + ); var otherTouchPoint = e.touchPoints[1]; _.ok(otherTouchPoint, "new touchPoint created for second finger"); - _.notEqual(otherTouchPoint, touchPointB, "different touchPoint objects for different fingers"); - _.ok(otherTouchPoint.originalEvent, "new originalEvent created for second finger"); - _.notEqual(otherTouchPoint.originalEvent, touchPointB.originalEvent, "originalEvent of second finger has nothing to do with originalEvent of first finger"); + _.notEqual( + otherTouchPoint, + touchPointB, + "different touchPoint objects for different fingers" + ); + _.ok( + otherTouchPoint.originalEvent, + "new originalEvent created for second finger" + ); + _.notEqual( + otherTouchPoint.originalEvent, + touchPointB.originalEvent, + "originalEvent of second finger has nothing to do with originalEvent of first finger" + ); _.strictEqual(otherTouchPoint.originalEvent.prop4, true); _.strictEqual(otherTouchPoint.eventName, "TouchStart"); _.strictEqual(otherTouchPoint.identifier, 11); - _.strictEqual(otherTouchPoint.target, 'd'); - _.strictEqual(otherTouchPoint.entity, 'd'); + _.strictEqual(otherTouchPoint.target, "d"); + _.strictEqual(otherTouchPoint.entity, "d"); _.strictEqual(otherTouchPoint.realX, 7); _.strictEqual(otherTouchPoint.realY, 8); @@ -622,21 +780,28 @@ e.triggerTouch("TouchEnd", { eventName: "TouchEnd", identifier: -3, - target: 'e', - entity: 'e', // DEPRECATED: remove in upcoming release - realX: 9, realY: 0, + target: "e", + entity: "e", // DEPRECATED: remove in upcoming release + realX: 9, + realY: 0, originalEvent: { prop5: true } }); _.strictEqual(e.touchPoints.length, 1, "One touchPoints exists"); - _.strictEqual(e.touchPoints[0], otherTouchPoint, "touchPoint objects for second finger are reused"); - _.deepEqual(e.touchPoints[0], otherTouchPoint, "touchPoint objects for second finger have same content"); + _.strictEqual( + e.touchPoints[0], + otherTouchPoint, + "touchPoint objects for second finger are reused" + ); + _.deepEqual( + e.touchPoints[0], + otherTouchPoint, + "touchPoint objects for second finger have same content" + ); }); test("Multiway and Fourway", function(_) { - var e = Crafty.e("2D, Fourway") - .attr({ x: 0, y: 0}); + var e = Crafty.e("2D, Fourway").attr({ x: 0, y: 0 }); - e.multiway(50, { W: -90 }); keysDown(Crafty.keys.W); Crafty.timer.simulateFrames(1); @@ -644,14 +809,14 @@ _.strictEqual(e._vx, 0, "Speed is 0 in x direction"); // Change the key's direction and speed while it's held down - e.attr({x:0, y:0}); + e.attr({ x: 0, y: 0 }); e.multiway(100, { W: 90 }); Crafty.timer.simulateFrames(1, 20); _.strictEqual(e._vy, 100, "Speed is 100 in +y direction"); _.strictEqual(e._vx, 0, "Speed is 0 in x direction"); // Change the speed with fourway, (W is negative for fourway) - e.attr({x:0, y:0}); + e.attr({ x: 0, y: 0 }); e.fourway(50); Crafty.timer.simulateFrames(1, 20); _.strictEqual(e._vy, -50, "Speed is 50 in -y direction"); @@ -662,20 +827,36 @@ Crafty.timer.simulateFrames(1); _.strictEqual(e._vy, -50, "Still speed 50 in -y direction after up arrow"); - keysUp('W'); - _.strictEqual(e._vy, -50, "Still speed 50 in -y direction after W is released"); + keysUp("W"); + _.strictEqual( + e._vy, + -50, + "Still speed 50 in -y direction after W is released" + ); Crafty.timer.simulateFrames(1); - _.strictEqual(e._vy, -50, "Still speed 50 in -y direction after W is released"); + _.strictEqual( + e._vy, + -50, + "Still speed 50 in -y direction after W is released" + ); - keysUp(Crafty.keys.UP_ARROW); - Crafty.timer.simulateFrames(1); + keysUp(Crafty.keys.UP_ARROW); + Crafty.timer.simulateFrames(1); _.strictEqual(e._vy, 0, "Speed is 0 once both keys are released"); // Diagonal keysDown(Crafty.keys.DOWN_ARROW, Crafty.keys.LEFT_ARROW); Crafty.timer.simulateFrames(1); - _.strictEqual(e._vy, 50, "Speed is 50 in +y direction when DOWN & LEFT are pressed"); - _.strictEqual(e._vx, -50, "Speed is 50 in -x direction when DOWN & LEFT are pressed"); + _.strictEqual( + e._vy, + 50, + "Speed is 50 in +y direction when DOWN & LEFT are pressed" + ); + _.strictEqual( + e._vx, + -50, + "Speed is 50 in -x direction when DOWN & LEFT are pressed" + ); keysUp(Crafty.keys.DOWN_ARROW); Crafty.timer.simulateFrames(1); @@ -689,10 +870,18 @@ keysUp(Crafty.keys.LEFT_ARROW); Crafty.timer.simulateFrames(1); - _.strictEqual(e._vy, 0, "No change when key released after component removed"); - _.strictEqual(e._vx, 0, "No change when key released after component is removed"); - - Crafty.s('Keyboard').resetKeyDown(); + _.strictEqual( + e._vy, + 0, + "No change when key released after component removed" + ); + _.strictEqual( + e._vx, + 0, + "No change when key released after component is removed" + ); + + Crafty.s("Keyboard").resetKeyDown(); e.destroy(); }); @@ -710,26 +899,46 @@ _.strictEqual(e._vx, 0, "No change in speed after Twoway speed is set"); e.disableControl(); - keysDown(Crafty.keys.D); + keysDown(Crafty.keys.D); Crafty.timer.simulateFrames(1); - _.strictEqual(e._vx, 0, "vx = 0 when key D pressed while control is disabled"); + _.strictEqual( + e._vx, + 0, + "vx = 0 when key D pressed while control is disabled" + ); e.enableControl(); - Crafty.timer.simulateFrames(1); - _.strictEqual(e._vx, 50, "vx = 50 once control is enabled while D key is down"); + Crafty.timer.simulateFrames(1); + _.strictEqual( + e._vx, + 50, + "vx = 50 once control is enabled while D key is down" + ); e.disableControl(); keysUp(Crafty.keys.D); - Crafty.timer.simulateFrames(1); - _.strictEqual(e._vx, 50, "vx = 50 when key is released while control disabled"); + Crafty.timer.simulateFrames(1); + _.strictEqual( + e._vx, + 50, + "vx = 50 when key is released while control disabled" + ); e._vx = 17; Crafty.timer.simulateFrames(1); - _.strictEqual(e._vx, 17, "vx = 17 after being explicitly set while control disabled"); + _.strictEqual( + e._vx, + 17, + "vx = 17 after being explicitly set while control disabled" + ); e.enableControl(); Crafty.timer.simulateFrames(1); - _.strictEqual(e._vx, 0, "vx = 0 once control is enabled and no key is held"); + _.strictEqual( + e._vx, + 0, + "vx = 0 once control is enabled and no key is held" + ); e.disableControl(); Crafty.timer.simulateFrames(1); @@ -754,11 +963,19 @@ e.disableControl(); e.speed({ x: 100, y: 100 }); Crafty.timer.simulateFrames(1); - _.strictEqual(e._vx, 50, "vx remains 50 when control disabled and speed set"); + _.strictEqual( + e._vx, + 50, + "vx remains 50 when control disabled and speed set" + ); e.enableControl(); Crafty.timer.simulateFrames(1); - _.strictEqual(e._vx, 100, "vx = 100 when control re-enabled while keys are still held down"); + _.strictEqual( + e._vx, + 100, + "vx = 100 when control re-enabled while keys are still held down" + ); e.speed({ x: 150, y: 150 }); Crafty.timer.simulateFrames(1); @@ -766,7 +983,11 @@ keysUp(Crafty.keys.D); Crafty.timer.simulateFrames(1); - _.strictEqual(e._vx, 150, "vx = 150 when D is released but RIGHT is still down"); + _.strictEqual( + e._vx, + 150, + "vx = 150 when D is released but RIGHT is still down" + ); keysUp(Crafty.keys.RIGHT_ARROW); Crafty.timer.simulateFrames(1); @@ -777,19 +998,28 @@ test("Multiway - diagonal movement", function(_) { var e = Crafty.e("2D, Multiway") - .attr({ x: 0, y: 0 }) - .multiway(50, { - NUMPAD_7:-135, NUMPAD_8: -90, NUMPAD_9: -45, - NUMPAD_4: 180, NUMPAD_6: 0, - NUMPAD_1: 135, NUMPAD_2: 90, NUMPAD_3: 45 - }, { normalize: true }); + .attr({ x: 0, y: 0 }) + .multiway( + 50, + { + NUMPAD_7: -135, + NUMPAD_8: -90, + NUMPAD_9: -45, + NUMPAD_4: 180, + NUMPAD_6: 0, + NUMPAD_1: 135, + NUMPAD_2: 90, + NUMPAD_3: 45 + }, + { normalize: true } + ); // test if diagonal movement same magnitude as horizontal/vertical movement // ||(-35, -35)|| =~= ||(50, 0)|| keysDown(Crafty.keys.NUMPAD_7); Crafty.timer.simulateFrames(1); - _.strictEqual(e._vx|0, -35, "vx ~ -35 when NUMPAD_7 pressed"); - _.strictEqual(e._vy|0, -35, "vy ~ -35 when NUMPAD_7 pressed"); + _.strictEqual(e._vx | 0, -35, "vx ~ -35 when NUMPAD_7 pressed"); + _.strictEqual(e._vy | 0, -35, "vy ~ -35 when NUMPAD_7 pressed"); keysUp(Crafty.keys.NUMPAD_7); keysDown(Crafty.keys.NUMPAD_8); @@ -806,8 +1036,16 @@ // pressing an additional horizontal key in that direction should not change velocity keysDown(Crafty.keys.NUMPAD_8); Crafty.timer.simulateFrames(1); - _.strictEqual(e._vx, 0, "vx = 0 when NUMPAD_7 & NUMPAD_9 & NUMPAD_8 pressed"); - _.strictEqual(e._vy, -50, "vy = -50 when NUMPAD_7 & NUMPAD_9 & NUMPAD_8 pressed"); + _.strictEqual( + e._vx, + 0, + "vx = 0 when NUMPAD_7 & NUMPAD_9 & NUMPAD_8 pressed" + ); + _.strictEqual( + e._vy, + -50, + "vy = -50 when NUMPAD_7 & NUMPAD_9 & NUMPAD_8 pressed" + ); keysUp(Crafty.keys.NUMPAD_8, Crafty.keys.NUMPAD_7, Crafty.keys.NUMPAD_9); // pressing two keys of opposite directions cancels out all movement @@ -818,23 +1056,39 @@ // pressing two more keys of opposite diagonal directions still cancels out all movement keysDown(Crafty.keys.NUMPAD_7, Crafty.keys.NUMPAD_3); Crafty.timer.simulateFrames(1); - _.strictEqual(e._vx, 0, "vx = 0 when NUMPAD_8 & NUMPAD_2 & NUMPAD_7 & NUMPAD_3 pressed"); - _.strictEqual(e._vy, 0, "vy = 0 when NUMPAD_8 & NUMPAD_2 & NUMPAD_7 & NUMPAD_3 pressed"); + _.strictEqual( + e._vx, + 0, + "vx = 0 when NUMPAD_8 & NUMPAD_2 & NUMPAD_7 & NUMPAD_3 pressed" + ); + _.strictEqual( + e._vy, + 0, + "vy = 0 when NUMPAD_8 & NUMPAD_2 & NUMPAD_7 & NUMPAD_3 pressed" + ); keysUp(Crafty.keys.NUMPAD_7, Crafty.keys.NUMPAD_3); keysUp(Crafty.keys.NUMPAD_2, Crafty.keys.NUMPAD_8); // pressing 2 diagonal keys in one direction does not cancel out 1 vertical key in opposite direction keysDown(Crafty.keys.NUMPAD_7, Crafty.keys.NUMPAD_9, Crafty.keys.NUMPAD_2); Crafty.timer.simulateFrames(1); - _.strictEqual(e._vx, 0, "vx = 0 when NUMPAD_7 & NUMPAD_9 & NUMPAD_8 pressed"); - _.strictEqual(e._vy, -50, "vy = -50 when NUMPAD_7 & NUMPAD_9 & NUMPAD_8 pressed"); + _.strictEqual( + e._vx, + 0, + "vx = 0 when NUMPAD_7 & NUMPAD_9 & NUMPAD_8 pressed" + ); + _.strictEqual( + e._vy, + -50, + "vy = -50 when NUMPAD_7 & NUMPAD_9 & NUMPAD_8 pressed" + ); keysUp(Crafty.keys.NUMPAD_7, Crafty.keys.NUMPAD_9, Crafty.keys.NUMPAD_2); // asymetric key movement keysDown(Crafty.keys.NUMPAD_4, Crafty.keys.NUMPAD_1); Crafty.timer.simulateFrames(1); - _.strictEqual(e._vx|0, -46, "vx ~ -46 when NUMPAD_4 & NUMPAD_1 pressed"); - _.strictEqual(e._vy|0, 19, "vy ~ 19 when NUMPAD_4 & NUMPAD_1 pressed"); + _.strictEqual(e._vx | 0, -46, "vx ~ -46 when NUMPAD_4 & NUMPAD_1 pressed"); + _.strictEqual(e._vy | 0, 19, "vy ~ 19 when NUMPAD_4 & NUMPAD_1 pressed"); keysUp(Crafty.keys.NUMPAD_4, Crafty.keys.NUMPAD_1); e.destroy(); @@ -843,21 +1097,21 @@ // Use keysUp/Down helper functions defined in common.js test("Integrationtest - Twoway", function(_) { var done = false; - Crafty.s('Keyboard').resetKeyDown(); + Crafty.s("Keyboard").resetKeyDown(); - var ground = Crafty.e("2D, platform") - .attr({ x: 0, y: 200, w: 10, h: 20 }); + var ground = Crafty.e("2D, platform").attr({ x: 0, y: 200, w: 10, h: 20 }); var player = Crafty.e("2D, Gravity, Twoway") - .attr({ x: 0, y: 150, w: 32, h: 10 }) - .gravity("platform") - .gravityConst(0.3*50*50) - .twoway(2, 4); + .attr({ x: 0, y: 150, w: 32, h: 10 }) + .gravity("platform") + .gravityConst(0.3 * 50 * 50) + .twoway(2, 4); - var landCount = 0, liftCount = 0; + var landCount = 0, + liftCount = 0; player.bind("LandedOnGround", function() { landCount++; - + if (landCount === 1) { this.bind("LiftedOffGround", function() { liftCount++; @@ -870,8 +1124,16 @@ keysDown(Crafty.keys.UP_ARROW); } else { - _.strictEqual(landCount, 2, "two land on ground events should have been registered"); - _.strictEqual(liftCount, 1, "one lift off ground event should have been registered"); + _.strictEqual( + landCount, + 2, + "two land on ground events should have been registered" + ); + _.strictEqual( + liftCount, + 1, + "one lift off ground event should have been registered" + ); ground.destroy(); player.destroy(); @@ -884,5 +1146,4 @@ Crafty.timer.simulateFrames(75); _.ok(done, "Integration test completed"); }); - -})(); \ No newline at end of file +})(); diff --git a/tests/unit/core/animation.js b/tests/unit/core/animation.js index 4b8b16f2..9919f168 100644 --- a/tests/unit/core/animation.js +++ b/tests/unit/core/animation.js @@ -22,7 +22,9 @@ }); test("Crafty.easing with custom function", function(_) { - var e = new Crafty.easing(80, function(t){return t*t;}) ; // 4 frames == 80ms by default + var e = new Crafty.easing(80, function(t) { + return t * t; + }); // 4 frames == 80ms by default e.tick(20); e.tick(20); _.strictEqual(e.value(), 0.25, ".25 after two steps"); @@ -41,5 +43,4 @@ e.tick(20); _.strictEqual(e.value(), 1, "1 after completed"); }); - })(); diff --git a/tests/unit/core/core.js b/tests/unit/core/core.js index 79291c9c..b4dd6d38 100644 --- a/tests/unit/core/core.js +++ b/tests/unit/core/core.js @@ -18,7 +18,11 @@ Crafty.e("test3, test"); _.strictEqual(Crafty("test").length, 6, "Single component"); _.strictEqual(Crafty("test test2").length, 2, "Two components ANDed"); - _.strictEqual(Crafty("test2 test test3").length, 1, "Three components ANDed"); + _.strictEqual( + Crafty("test2 test test3").length, + 1, + "Three components ANDed" + ); _.strictEqual(Crafty("test, test2").length, 7, "Two components ORed"); _.strictEqual(Crafty("*").length, 7, "All components - universal selector"); @@ -26,13 +30,33 @@ _.strictEqual(Crafty(first[0]), first, "Get by ID"); first.removeComponent("test3"); - _.strictEqual(Crafty("test3").length, 3, "Single component query after removing a component"); + _.strictEqual( + Crafty("test3").length, + 3, + "Single component query after removing a component" + ); other.destroy(); - _.strictEqual(Crafty("test test2 test3").length, 0, "Compount component query after destroying an entity"); - _.strictEqual(Crafty("test").length, 5, "Single component query after destroying an entity"); - _.strictEqual(Crafty("test2").length, 2, "Single component query after destroying an entity"); - _.strictEqual(Crafty("test3").length, 2, "Single component query after destroying an entity"); + _.strictEqual( + Crafty("test test2 test3").length, + 0, + "Compount component query after destroying an entity" + ); + _.strictEqual( + Crafty("test").length, + 5, + "Single component query after destroying an entity" + ); + _.strictEqual( + Crafty("test2").length, + 2, + "Single component query after destroying an entity" + ); + _.strictEqual( + Crafty("test3").length, + 2, + "Single component query after destroying an entity" + ); }); test("addComponent and removeComponent", function(_) { @@ -47,15 +71,27 @@ _.strictEqual(first.added, true, "component with property exists"); first.addComponent("multi1, multi2"); - _.strictEqual(first.has("multi1") && first.has("multi2"), true, "multiple components added"); + _.strictEqual( + first.has("multi1") && first.has("multi2"), + true, + "multiple components added" + ); first.removeComponent("test3"); _.strictEqual(first.has("test3"), false, "component removed"); first.removeComponent("comp"); - _.strictEqual(first.added && !first.has("comp"), true, "soft-removed component (properties remain)"); + _.strictEqual( + first.added && !first.has("comp"), + true, + "soft-removed component (properties remain)" + ); first.removeComponent("comp", false); - _.strictEqual(!first.added && !first.has("comp"), true, "hard-removed component (properties are gone)"); + _.strictEqual( + !first.added && !first.has("comp"), + true, + "hard-removed component (properties are gone)" + ); first.removeComponent("nonAddedComponent"); }); @@ -72,7 +108,11 @@ }); var e = Crafty.e("comp, blank"); e.removeComponent("blank"); - _.strictEqual(removeRan, false, "Remove doesn't run on other component removal"); + _.strictEqual( + removeRan, + false, + "Remove doesn't run on other component removal" + ); removeRan = false; e.removeComponent("comp"); @@ -88,38 +128,37 @@ // check the properties attribute of components, especially the pattern used by existing components test("properties", function(_) { - Crafty.c("PropertyTest", { properties: { foo: { - set: function(value){ + set: function(value) { this._foo = value; }, - get: function(){ + get: function() { return this._foo; }, configurable: true, enumerable: true }, - _foo: {value: 0, writable: true, enumerable:false} + _foo: { value: 0, writable: true, enumerable: false } } }); var e = Crafty.e("PropertyTest"); - + _.strictEqual(e._foo, 0, "Initial value works"); e.foo = 5; _.strictEqual(e._foo, 5, "Setter works"); - + e._foo = 10; _.strictEqual(e.foo, 10, "Getter works"); - + var propList = []; - for (var prop in e){ + for (var prop in e) { propList.push(prop); } - _.ok(propList.indexOf("foo") >=0, "Property foo is enumerable"); + _.ok(propList.indexOf("foo") >= 0, "Property foo is enumerable"); _.ok(propList.indexOf("_foo") === -1, "Property _foo is not enumerable"); }); @@ -147,7 +186,7 @@ }, // 3rd: events should be bound on entity events: { - "CustomEvent": function() { + CustomEvent: function() { _.strictEqual(this.foo, 1); _.strictEqual(this.bar, 2); _.strictEqual(this.baz, undefined); @@ -177,7 +216,9 @@ this.quuz = 5; } }); - var e = Crafty.e().addComponent("MemberOrderTest").removeComponent("MemberOrderTest"); + var e = Crafty.e() + .addComponent("MemberOrderTest") + .removeComponent("MemberOrderTest"); _.strictEqual(e.foo, 1); _.strictEqual(e.bar, 2); @@ -187,13 +228,13 @@ }); test("overwrite component definition", function(_) { - Crafty.c('MyCompDef', { a: 0 }); - var e = Crafty.e('MyCompDef'); + Crafty.c("MyCompDef", { a: 0 }); + var e = Crafty.e("MyCompDef"); _.strictEqual(e.a, 0); _.strictEqual(e.b, undefined); - Crafty.c('MyCompDef', { a: 1, b: 1 }); - var f = Crafty.e('MyCompDef'); + Crafty.c("MyCompDef", { a: 1, b: 1 }); + var f = Crafty.e("MyCompDef"); _.strictEqual(e.a, 0); _.strictEqual(e.b, undefined); _.strictEqual(f.a, 1); @@ -218,7 +259,6 @@ player.setName("Player2"); _.strictEqual(player.getName(), "Player2"); - var player3 = Crafty.e().one("NewEntityName", function(name) { counter++; _.strictEqual(name, "Player3"); @@ -226,8 +266,11 @@ player3.setName("Player3"); _.strictEqual(player3.getName(), "Player3"); - - _.strictEqual(player.getName(), "Player2", "other entity's name didn't change after changing another entity's name"); + _.strictEqual( + player.getName(), + "Player2", + "other entity's name didn't change after changing another entity's name" + ); _.strictEqual(counter, 3, "correct number of events fired"); }); @@ -242,7 +285,6 @@ }); _.strictEqual(first.prop, "test", "properties from object assigned"); _.strictEqual(first.another, 56, "properties from object assigned"); - }); test("defineField", function(_) { @@ -254,32 +296,44 @@ var first = Crafty.e("test"); - - first.setter('p0', function(v) { + first.setter("p0", function(v) { this._p0 = v * 5; }); first.p0 = 2; _.strictEqual(first._p0, 10, "single property setter"); _.strictEqual(first.p0, undefined, "single property getter"); - - first.defineField('p1', function() { - return this._p1; - }, function(v) { - this._p1 = v * 2; - }); + first.defineField( + "p1", + function() { + return this._p1; + }, + function(v) { + this._p1 = v * 2; + } + ); first.p1 = 2; _.strictEqual(first.p1, 4, "single property getter & setter"); - first.defineField('p2', function() { - return this._p2; - }, function(v) { - this._p2 = v * 2; - }).defineField('p3', function() { - return this._p3; - }, function(v) { - this._p3 = v * 2; - }); + first + .defineField( + "p2", + function() { + return this._p2; + }, + function(v) { + this._p2 = v * 2; + } + ) + .defineField( + "p3", + function() { + return this._p3; + }, + function(v) { + this._p3 = v * 2; + } + ); first.p2 = 2; first.p3 = 3; _.strictEqual(first.p2 + first.p3, 10, "two property getters & setters"); @@ -310,7 +364,6 @@ count++; }); _.strictEqual(count, 7, "Iterated all elements"); - }); test("Crafty.get() to find an array", function(_) { @@ -321,9 +374,16 @@ var collection = Crafty("test"); var result = collection.get(); _.strictEqual(result.length, 3, "resultant array should be length 3"); - _.strictEqual(result[0].has("test"), true, "Result elements should have correct component"); - _.strictEqual(collection[0], result[0].getId(), "First id of result should match first id of Crafty array"); - + _.strictEqual( + result[0].has("test"), + true, + "Result elements should have correct component" + ); + _.strictEqual( + collection[0], + result[0].getId(), + "First id of result should match first id of Crafty array" + ); }); test("Crafty.get(index) to find the indicated entity", function(_) { @@ -334,13 +394,28 @@ collection = Crafty("test"); result = collection.get(0); - _.strictEqual(result.has("test"), true, "Result should have correct component"); - _.strictEqual(result.getId(), collection[0], "result should be first element of collection"); + _.strictEqual( + result.has("test"), + true, + "Result should have correct component" + ); + _.strictEqual( + result.getId(), + collection[0], + "result should be first element of collection" + ); result = collection.get(-1); - _.strictEqual(result.has("test"), true, "Result should have correct component"); - _.strictEqual(result.getId(), collection[2], "result should be last element of collection"); - + _.strictEqual( + result.has("test"), + true, + "Result should have correct component" + ); + _.strictEqual( + result.getId(), + collection[2], + "result should be last element of collection" + ); }); test("Crafty.get(index) error checking", function(_) { @@ -352,21 +427,31 @@ collection = Crafty("test"); result = collection.get(3); - _.strictEqual(typeof result, "undefined", "result of get(3) should be undefined"); + _.strictEqual( + typeof result, + "undefined", + "result of get(3) should be undefined" + ); result = collection.get(-4); - _.strictEqual(typeof result, "undefined", "result of get(-4) should be undefined"); - + _.strictEqual( + typeof result, + "undefined", + "result of get(-4) should be undefined" + ); }); test("Crafty.get with only one object", function(_) { var e = Crafty.e("test"); var collection = Crafty("test"); var result = collection.get(0); - _.strictEqual(result.getId(), e.getId(), "result of get(0) is correct entity"); + _.strictEqual( + result.getId(), + e.getId(), + "result of get(0) is correct entity" + ); result = collection.get(); _.strictEqual(result.length, 1, "result of get() is array of length 1"); - }); test("requires", function(_) { @@ -386,7 +471,6 @@ _.strictEqual(first.already, "already", "Didn't overwrite property"); _.strictEqual(first.notyet, true, "Assigned if didn't have"); _.ok(first.has("already") && first.has("notyet"), "Both added"); - }); test("requires multiple args", function(_) { @@ -403,8 +487,8 @@ test("required special parameter", function(_) { var hasComp = false; Crafty.c("Requisitioner", { - init: function() { - if (this.has("RequiredComponent")){ + init: function() { + if (this.has("RequiredComponent")) { hasComp = true; } }, @@ -420,41 +504,48 @@ id = first[0]; //id first.destroy(); _.strictEqual(Crafty(id).length, 0, "Not listed"); - }); - test(".frame() function", function(_){ + test(".frame() function", function(_) { var frameNumber; var frameFunction = function() { frameNumber = Crafty.frame(); }; - Crafty.bind('UpdateFrame', frameFunction); + Crafty.bind("UpdateFrame", frameFunction); Crafty.timer.simulateFrames(1); - _.ok(frameNumber, '.frame function should return a value.'); + _.ok(frameNumber, ".frame function should return a value."); - Crafty.unbind('UpdateFrame', frameFunction); + Crafty.unbind("UpdateFrame", frameFunction); }); // TODO: add test for Crafty.stop() once problematic side effects are fixed! - module("Timer"); - test('Timer.simulateFrames', function(_) { + test("Timer.simulateFrames", function(_) { var counter = 0; var enterFrameFunc = function() { counter++; - _.ok(counter === 1 || counter === 4 || counter === 7, "different counter value expected"); + _.ok( + counter === 1 || counter === 4 || counter === 7, + "different counter value expected" + ); }; var updateFrameFunc = function() { counter++; - _.ok(counter === 2 || counter === 5 || counter === 8, "different counter value expected"); + _.ok( + counter === 2 || counter === 5 || counter === 8, + "different counter value expected" + ); }; var exitFrameFunc = function() { counter++; - _.ok(counter === 3 || counter === 6 || counter === 9, "different counter value expected"); + _.ok( + counter === 3 || counter === 6 || counter === 9, + "different counter value expected" + ); }; var preRenderFunc = function() { counter++; @@ -487,16 +578,15 @@ Crafty.unbind("PostRender", postRenderFunc); }); - test('Crafty.timer.steptype', function(_) { + test("Crafty.timer.steptype", function(_) { var originalSteptype = Crafty.timer.steptype(), - steptype, - counter = 0; + steptype, + counter = 0; var increment = function() { counter++; }; Crafty.bind("NewSteptype", increment); - Crafty.one("NewSteptype", function(evt) { _.strictEqual(evt.mode, "fixed"); _.strictEqual(evt.maxTimeStep, 100); @@ -506,7 +596,6 @@ _.strictEqual(steptype.mode, "fixed"); _.strictEqual(steptype.maxTimeStep, 100); - Crafty.one("NewSteptype", function(evt) { _.strictEqual(evt.mode, "variable"); _.strictEqual(evt.maxTimeStep, 1000); @@ -516,13 +605,12 @@ _.strictEqual(steptype.mode, "variable"); _.strictEqual(steptype.maxTimeStep, 1000); - _.strictEqual(counter, 2); Crafty.unbind("NewSteptype", increment); Crafty.timer.steptype(originalSteptype.mode, originalSteptype.maxTimeStep); }); - test('Crafty.timer.FPS', function(_) { + test("Crafty.timer.FPS", function(_) { var counter = 0; var increment = function() { counter++; @@ -534,7 +622,7 @@ _.strictEqual(Crafty.timer.FPS(), 25); }); Crafty.one("UpdateFrame", function(frameData) { - _.strictEqual(frameData.dt, 1000/25); + _.strictEqual(frameData.dt, 1000 / 25); }); Crafty.timer.FPS(25); Crafty.timer.simulateFrames(1); @@ -544,7 +632,7 @@ _.strictEqual(Crafty.timer.FPS(), 50); }); Crafty.one("UpdateFrame", function(frameData) { - _.strictEqual(frameData.dt, 1000/50); + _.strictEqual(frameData.dt, 1000 / 50); }); Crafty.timer.FPS(50); Crafty.timer.simulateFrames(1); @@ -552,5 +640,4 @@ Crafty.unbind("FPSChange", increment); _.strictEqual(counter, 2); }); - })(); diff --git a/tests/unit/core/events.js b/tests/unit/core/events.js index d04121c5..697ffc7f 100644 --- a/tests/unit/core/events.js +++ b/tests/unit/core/events.js @@ -12,12 +12,12 @@ }); first.trigger("myevent"); _.strictEqual(triggered, true, "custom event triggered"); - }); test("bind groups of entities", function(_) { - var e1 = Crafty.e("test"), e2 = Crafty.e("test"); - var test_callback = function(){ + var e1 = Crafty.e("test"), + e2 = Crafty.e("test"); + var test_callback = function() { this.test_flag = true; }; Crafty("test").bind("TestEvent", test_callback); @@ -29,13 +29,17 @@ Crafty.trigger("TestEvent"); _.strictEqual(e1.test_flag, true, "Global event triggered on first entity"); - _.strictEqual(e2.test_flag, true, "Global event triggered on second entity"); - + _.strictEqual( + e2.test_flag, + true, + "Global event triggered on second entity" + ); }); - test("trigger groups of entities", function(_){ - var e1 = Crafty.e("test"), e2 = Crafty.e("test"); - var test_callback = function(){ + test("trigger groups of entities", function(_) { + var e1 = Crafty.e("test"), + e2 = Crafty.e("test"); + var test_callback = function() { this.test_flag = true; }; e1.bind("TestEvent", test_callback); @@ -48,14 +52,19 @@ test("bind to an event in response to that same event", function(_) { var first = Crafty.e("test"), triggered = 0; - function increment(){ triggered++; } + function increment() { + triggered++; + } first.bind("myevent", function() { increment(); first.bind("myevent", increment); }); first.trigger("myevent"); - _.strictEqual(triggered, 1, "event added in response to an event should not be triggered by that same event"); - + _.strictEqual( + triggered, + 1, + "event added in response to an event should not be triggered by that same event" + ); }); test("unbind", function(_) { @@ -78,7 +87,6 @@ first.bind("myevent", callback2); first.unbind("myevent", callback); first.trigger("myevent"); - }); test("globalBindAndUnbind", function(_) { @@ -116,7 +124,6 @@ Crafty.trigger("Increment"); _.strictEqual(x, 1, "Crafty.bind fired once"); - x = 0; Crafty.unbind("Increment", add); Crafty.trigger("Increment"); @@ -138,17 +145,19 @@ Crafty.uniqueBind("Increment", add); Crafty.uniqueBind("Increment", add); Crafty.trigger("Increment"); - _.strictEqual(x, 1, "Event bound twice by Crafty.uniqueBound fires only once"); + _.strictEqual( + x, + 1, + "Event bound twice by Crafty.uniqueBound fires only once" + ); x = 0; Crafty.unbind("Increment", add); Crafty.trigger("Increment"); _.strictEqual(x, 0, "uniqueBound does not fire once unbound"); - }); test("Entity binding events", function(_) { - var x = 0; function add() { @@ -160,7 +169,6 @@ e.trigger("Increment"); _.strictEqual(x, 1, ".bind fired once"); - x = 0; e.unbind("Increment", add); e.trigger("Increment"); @@ -187,45 +195,48 @@ }); test("Multiple bound events", function(_) { - //Test with entity trigger - var temp = Crafty.e('Triggerable'); + var temp = Crafty.e("Triggerable"); temp.xyz = 0; temp.abc = 0; temp.def = 0; - temp.one('Event A', function() { + temp.one("Event A", function() { this.xyz++; }); - temp.bind('Event A', function() { + temp.bind("Event A", function() { this.abc++; }); - temp.one('Event A', function() { + temp.one("Event A", function() { this.def++; }); - temp.trigger('Event A'); - temp.trigger('Event A'); + temp.trigger("Event A"); + temp.trigger("Event A"); _.strictEqual(temp.xyz, 1, "ENTITY -- first one() should trigger once"); _.strictEqual(temp.abc, 2, "regular event should trigger twice"); _.strictEqual(temp.def, 1, "second one() should trigger once"); temp.destroy(); //Test with global trigger on entity - temp = Crafty.e('Triggerable'); + temp = Crafty.e("Triggerable"); temp.xyz = 0; temp.abc = 0; temp.def = 0; - temp.one('Event A', function() { + temp.one("Event A", function() { this.xyz++; }); - temp.bind('Event A', function() { + temp.bind("Event A", function() { this.abc++; }); - temp.one('Event A', function() { + temp.one("Event A", function() { this.def++; }); - Crafty.trigger('Event A'); - Crafty.trigger('Event A'); - _.strictEqual(temp.xyz, 1, "GLOBAL TRIGGER -- first one() should trigger once"); + Crafty.trigger("Event A"); + Crafty.trigger("Event A"); + _.strictEqual( + temp.xyz, + 1, + "GLOBAL TRIGGER -- first one() should trigger once" + ); _.strictEqual(temp.abc, 2, "regular event should trigger twice"); _.strictEqual(temp.def, 1, "second one() should trigger once"); temp.destroy(); @@ -235,23 +246,26 @@ temp.xyz = 0; temp.abc = 0; temp.def = 0; - temp.one('Event A', function() { + temp.one("Event A", function() { this.xyz++; }); - temp.bind('Event A', function() { + temp.bind("Event A", function() { this.abc++; }); - temp.one('Event A', function() { + temp.one("Event A", function() { this.def++; }); - Crafty.trigger('Event A'); - Crafty.trigger('Event A'); - _.strictEqual(temp.xyz, 1, "GLOBAL BIND -- first one() should trigger once"); + Crafty.trigger("Event A"); + Crafty.trigger("Event A"); + _.strictEqual( + temp.xyz, + 1, + "GLOBAL BIND -- first one() should trigger once" + ); _.strictEqual(temp.abc, 2, "regular event should trigger twice"); _.strictEqual(temp.def, 1, "second one() should trigger once"); Crafty.unbind("Event A"); - }); // Catch bugs in unbinding logic when something is unbound at depth > 1 @@ -260,19 +274,23 @@ var counter = 0; // Each of these functions can be run at most once, because they unbind on triggering var a = function() { - counter++; - this.unbind("Test", a); - this.trigger("Test"); + counter++; + this.unbind("Test", a); + this.trigger("Test"); }; var b = function() { - counter++; - this.unbind("Test", b); - this.trigger("Test"); + counter++; + this.unbind("Test", b); + this.trigger("Test"); }; e.bind("Test", a); e.bind("Test", b); e.trigger("Test"); - _.strictEqual(counter, 2, "Total number of triggers should be 2 (regardless of bind/unbind order)."); + _.strictEqual( + counter, + 2, + "Total number of triggers should be 2 (regardless of bind/unbind order)." + ); }); test("Data passing", function(_) { @@ -318,11 +336,11 @@ Crafty.unbind("Increment"); }); - test("Events and autobind with function names", function(_){ + test("Events and autobind with function names", function(_) { Crafty.c("AutoComp", { - counter:0, - events: {"Test":"_onTest"}, - _onTest: function(){ + counter: 0, + events: { Test: "_onTest" }, + _onTest: function() { this.counter++; } }); @@ -333,16 +351,18 @@ e.removeComponent("AutoComp"); e.counter = 0; e.trigger("Test"); - _.strictEqual(e.counter, 0, "Function was not triggered after removal of component"); - - + _.strictEqual( + e.counter, + 0, + "Function was not triggered after removal of component" + ); }); - test("Events and autobind with function declared inside object", function(_){ + test("Events and autobind with function declared inside object", function(_) { Crafty.c("AutoComp2", { - counter:0, + counter: 0, events: { - Test:function(){ + Test: function() { this.counter++; } } @@ -354,13 +374,16 @@ e.removeComponent("AutoComp2"); e.counter = 0; e.trigger("Test"); - _.strictEqual(e.counter, 0, "Function was not triggered after removal of component"); - + _.strictEqual( + e.counter, + 0, + "Function was not triggered after removal of component" + ); }); - test("Freezing and unfreezing events", function(_) { - var x = 0, triggered = 0; + var x = 0, + triggered = 0; function add() { x++; @@ -379,7 +402,11 @@ triggered = 0; e.freeze(); - _.strictEqual(triggered, 0, "Freeze event wasn't triggered, because it was already frozen"); + _.strictEqual( + triggered, + 0, + "Freeze event wasn't triggered, because it was already frozen" + ); x = 0; e.trigger("Increment"); @@ -395,7 +422,11 @@ triggered = 0; e.unfreeze(); - _.strictEqual(triggered, 0, "Unfreeze event wasn't triggered, because it was already unfrozen"); + _.strictEqual( + triggered, + 0, + "Unfreeze event wasn't triggered, because it was already unfrozen" + ); x = 0; e.trigger("Increment"); @@ -411,8 +442,8 @@ test("Freezing should prevent cascade", function(_) { var parent = Crafty.e("2D"); var child = Crafty.e("2D"); - parent.attr({x: 10, y:10}); - child.attr({x:20, y:20}); + parent.attr({ x: 10, y: 10 }); + child.attr({ x: 20, y: 20 }); parent.attach(child); child.freeze(); @@ -425,7 +456,8 @@ }); test("Freezing and unfreezing events on groups", function(_) { - var x = 0, triggered = 0; + var x = 0, + triggered = 0; function add() { x++; @@ -448,7 +480,11 @@ triggered = 0; group.freeze(); - _.strictEqual(triggered, 0, "Freeze events weren't triggered, because they were already frozen"); + _.strictEqual( + triggered, + 0, + "Freeze events weren't triggered, because they were already frozen" + ); x = 0; group.trigger("Increment"); @@ -456,7 +492,11 @@ x = 0; Crafty.trigger("Increment"); - _.strictEqual(x, 0, "Callback does not run while frozen when triggered globally"); + _.strictEqual( + x, + 0, + "Callback does not run while frozen when triggered globally" + ); triggered = 0; group.unfreeze(); @@ -464,7 +504,11 @@ triggered = 0; group.unfreeze(); - _.strictEqual(triggered, 0, "Unfreeze event weren't triggered, because they were already unfrozen"); + _.strictEqual( + triggered, + 0, + "Unfreeze event weren't triggered, because they were already unfrozen" + ); x = 0; group.trigger("Increment"); @@ -477,4 +521,3 @@ group.destroy(); }); })(); - diff --git a/tests/unit/core/instances.js b/tests/unit/core/instances.js index fac211a6..3e2d3d29 100644 --- a/tests/unit/core/instances.js +++ b/tests/unit/core/instances.js @@ -7,13 +7,21 @@ module("Instances"); test("Separate crafty instances do not share property", function(_) { - Crafty2.__SOME_PROPERTY__ = '__SOME_PROPERTY__'; + Crafty2.__SOME_PROPERTY__ = "__SOME_PROPERTY__"; - _.strictEqual(Crafty2.__SOME_PROPERTY__, '__SOME_PROPERTY__', "Property set on one instance"); - _.notEqual(Crafty.__SOME_PROPERTY__, '__SOME_PROPERTY__', "Property not set on other instance"); + _.strictEqual( + Crafty2.__SOME_PROPERTY__, + "__SOME_PROPERTY__", + "Property set on one instance" + ); + _.notEqual( + Crafty.__SOME_PROPERTY__, + "__SOME_PROPERTY__", + "Property not set on other instance" + ); }); test("Separate crafty instances consist of different subobjects", function(_) { _.notDeepEqual(Crafty2, Crafty, "Properties are different"); }); -})(); \ No newline at end of file +})(); diff --git a/tests/unit/core/loader.js b/tests/unit/core/loader.js index e244b94d..c5edc815 100644 --- a/tests/unit/core/loader.js +++ b/tests/unit/core/loader.js @@ -2,63 +2,82 @@ var module = QUnit.module; var test = QUnit.test; - module('Loader'); + module("Loader"); - test("Warning on old syntax", function(_){ + test("Warning on old syntax", function(_) { var original_log = Crafty.log; var logged_message = ""; - Crafty.log = function(msg) { logged_message = msg; }; - Crafty.load(["falsey.png"], function(){ logged_message = "nope"; }); - _.ok(logged_message.indexOf("no longer works") >=0, "Correctly logged warning."); + Crafty.log = function(msg) { + logged_message = msg; + }; + Crafty.load(["falsey.png"], function() { + logged_message = "nope"; + }); + _.ok( + logged_message.indexOf("no longer works") >= 0, + "Correctly logged warning." + ); Crafty.log = original_log; }); - - test('assets loading', function(_) { + test("assets loading", function(_) { _.expect(2); var done = _.async(); var items = [], - checkItems = function() { - var checks = 0; - for(var i = 0, l = items.length, src;i= 0 , "Property foo is enumerable"); + _.ok(propList.indexOf("foo") >= 0, "Property foo is enumerable"); _.ok(propList.indexOf("_foo") === -1, "Property _foo is not enumerable"); }); test("Special property `options` default values", function(_) { - var sys = { - n: 0, - options: { - testFlag: true - } + var sys = { + n: 0, + options: { + testFlag: true + } }; - Crafty.s("Test", sys, {secondFlag: false}); + Crafty.s("Test", sys, { secondFlag: false }); var s = Crafty.s("Test"); - _.strictEqual(s.options.testFlag, true, "Default values for options not changed when not set"); + _.strictEqual( + s.options.testFlag, + true, + "Default values for options not changed when not set" + ); s.destroy(); }); test("Special property `options` override values", function(_) { - var sys = { - n: 0, - options: { - testFlag: true - } + var sys = { + n: 0, + options: { + testFlag: true + } }; - Crafty.s("Test", sys, {testFlag: false}); + Crafty.s("Test", sys, { testFlag: false }); var s = Crafty.s("Test"); - _.strictEqual(s.options.testFlag, false, "Default values for options can be overridden"); + _.strictEqual( + s.options.testFlag, + false, + "Default values for options can be overridden" + ); s.destroy(); }); test("Overwrite system definition", function(_) { - Crafty.s('MySystemDef', { a: 0 }, {}, false); - _.strictEqual(Crafty.s('MySystemDef').a, 0); - _.strictEqual(Crafty.s('MySystemDef').b, undefined); + Crafty.s("MySystemDef", { a: 0 }, {}, false); + _.strictEqual(Crafty.s("MySystemDef").a, 0); + _.strictEqual(Crafty.s("MySystemDef").b, undefined); - Crafty.s('MySystemDef', { a: 1, b: 1 }, {}, false); - _.strictEqual(Crafty.s('MySystemDef').a, 1); - _.strictEqual(Crafty.s('MySystemDef').b, 1); + Crafty.s("MySystemDef", { a: 1, b: 1 }, {}, false); + _.strictEqual(Crafty.s("MySystemDef").a, 1); + _.strictEqual(Crafty.s("MySystemDef").b, 1); }); test("order of handling special members", function(_) { @@ -290,7 +326,7 @@ }, // 3rd: events should be bound on entity events: { - "CustomEvent": function() { + CustomEvent: function() { _.strictEqual(this.foo, 1); _.strictEqual(this.bar, 2); _.strictEqual(this.baz, undefined); @@ -330,4 +366,3 @@ _.strictEqual(s.quuz, 5); }); })(); - diff --git a/tests/unit/core/time.js b/tests/unit/core/time.js index 43071cfc..fad0c31a 100644 --- a/tests/unit/core/time.js +++ b/tests/unit/core/time.js @@ -6,7 +6,9 @@ test("Delay", function(_) { var counter = 0, - incr = function() { counter++; }; + incr = function() { + counter++; + }; var ent = Crafty.e("Delay"); // test one execution @@ -26,11 +28,15 @@ // test infinite executions counter = 0; - + ent.delay(incr, 49, -1); Crafty.timer.simulateFrames(8); - _.strictEqual(counter, 3, "delayed function should have executed three times"); + _.strictEqual( + counter, + 3, + "delayed function should have executed three times" + ); _.strictEqual(ent._delays.length, 1, "one more scheduled delay"); // test cancel @@ -44,13 +50,18 @@ counter = 0; ent.delay(incr, 5, 1); Crafty.timer.simulateFrames(1); - _.strictEqual(counter, 2, "function should be executed exactly twice, in a single frame"); + _.strictEqual( + counter, + 2, + "function should be executed exactly twice, in a single frame" + ); _.strictEqual(ent._delays.length, 0, "no more scheduled delays"); - // test callbackOff counter = 0; - ent.delay(incr, 49, 0, function() { counter-=5; }); + ent.delay(incr, 49, 0, function() { + counter -= 5; + }); Crafty.timer.simulateFrames(10); _.strictEqual(counter, -4, "two functions should have executed once"); _.strictEqual(ent._delays.length, 0, "no more scheduled delays"); @@ -64,7 +75,11 @@ ent.delay(incr, 65); // x1 ent.delay(incr, 65); // x1 Crafty.timer.simulateFrames(8); - _.strictEqual(counter, 11, "delayed function should have executed eleven times"); + _.strictEqual( + counter, + 11, + "delayed function should have executed eleven times" + ); _.strictEqual(ent._delays.length, 2, "two more scheduled delays"); // test multiple cancels @@ -74,7 +89,6 @@ _.strictEqual(counter, 0, "delayed function should not have executed"); _.strictEqual(ent._delays.length, 0, "no more scheduled delays"); - //test resume delays without pausing before counter = 0; ent.resumeDelays(); @@ -146,7 +160,6 @@ Crafty.timer.simulateFrames(1); // 1 * 10ms = 100ms _.strictEqual(counter, 1, "delayed function should have executed once"); _.strictEqual(ent._delays.length, 0, "no more scheduled delays"); - }); module("Crafty.timer", { @@ -194,9 +207,16 @@ setTimeout(function() { var endTime = lastKnownTime; - _.ok(endTime > startTime, "After " + framesTriggered + " frames triggered, EndTime " + endTime + " must be larger than StartTime " + startTime); + _.ok( + endTime > startTime, + "After " + + framesTriggered + + " frames triggered, EndTime " + + endTime + + " must be larger than StartTime " + + startTime + ); done(); }, 200); }); - })(); diff --git a/tests/unit/core/tween.js b/tests/unit/core/tween.js index c55c5fa2..5a701620 100644 --- a/tests/unit/core/tween.js +++ b/tests/unit/core/tween.js @@ -17,10 +17,13 @@ var e = Crafty.e("2D, Tween"); e.x = 0; e.y = 10; - var ret = e.tween({ - x: 10, - y: 16 - }, 200); // 10 frames == 200 ms by efault + var ret = e.tween( + { + x: 10, + y: 16 + }, + 200 + ); // 10 frames == 200 ms by efault _.strictEqual(ret, e, ".tween() returned self correctly"); Crafty.timer.simulateFrames(5); _.strictEqual(Round(e.x), 5, "Halfway tweened x"); @@ -33,74 +36,93 @@ test("Tween with quadratic easing function", function(_) { var e = Crafty.e("2D, Tween"); e.x = 0; - e.tween({ - x: 16 - }, 200, function(t){return (t*t);}); // 10 frames == 200 ms by default + e.tween( + { + x: 16 + }, + 200, + function(t) { + return t * t; + } + ); // 10 frames == 200 ms by default Crafty.timer.simulateFrames(5); - _.strictEqual(Round(e.x), 4, "At halfway point, x is a quarter of original value"); + _.strictEqual( + Round(e.x), + 4, + "At halfway point, x is a quarter of original value" + ); Crafty.timer.simulateFrames(10); _.strictEqual(e.x, 16, "Fully tweened x"); }); - test('correct tweening', function(_) { + test("correct tweening", function(_) { _.expect(1); var done = _.async(); - var e1 = Crafty.e('2D, Tween') + var e1 = Crafty.e("2D, Tween") .attr({ x: 0, y: 0 }) - .tween({ - x: 100 - }, 50); - e1.bind('TweenEnd', function() { + .tween( + { + x: 100 + }, + 50 + ); + e1.bind("TweenEnd", function() { _.ok(this.x === 100); done(); }); }); - test('correct tweening with multiple entities', function(_) { + test("correct tweening with multiple entities", function(_) { _.expect(1); var done = _.async(); - var e1 = Crafty.e('2D, Tween') + var e1 = Crafty.e("2D, Tween") .attr({ x: 0, y: 0 }) - .tween({ - x: 100 - }, 50); - Crafty.e('2D, Tween') + .tween( + { + x: 100 + }, + 50 + ); + Crafty.e("2D, Tween") .attr({ x: 0, y: 0 }) - .tween({ - x: 100 - }, 50); - e1.bind('TweenEnd', function() { + .tween( + { + x: 100 + }, + 50 + ); + e1.bind("TweenEnd", function() { _.ok(this.x === 100); done(); }); }); - test('pause and resume tween', function(_) { + test("pause and resume tween", function(_) { var actualTweens = [], - fired = 0; + fired = 0; - var e = Crafty.e('2D, Tween') + var e = Crafty.e("2D, Tween") .tween({ x: 100 }, 100) // 5 frames == 100 ms by default .tween({ y: 50, z: 300 }, 200) // 10 frames = 200ms by default - .bind('TweenEnd', function(props) { + .bind("TweenEnd", function(props) { ++fired; actualTweens = actualTweens.concat(Object.keys(props).sort()); }); // no progress if paused e.pauseTweens(); - Crafty.timer.simulateFrames(10+2); + Crafty.timer.simulateFrames(10 + 2); _.strictEqual(fired, 0, "no events fired"); _.deepEqual(actualTweens, [], "no tweens finished"); _.strictEqual(e.x, 0, "start position"); @@ -111,16 +133,16 @@ e.resumeTweens(); Crafty.timer.simulateFrames(5); _.strictEqual(fired, 1, "1 event fired"); - _.deepEqual(actualTweens, ['x'], "x tween finished"); + _.deepEqual(actualTweens, ["x"], "x tween finished"); _.strictEqual(e.x, 100, "end position"); _.strictEqual(e.y, 25, "mid position"); _.strictEqual(e.z, 150, "mid position"); // no progress on pausing again e.pauseTweens(); - Crafty.timer.simulateFrames(10+2); + Crafty.timer.simulateFrames(10 + 2); _.strictEqual(fired, 1, "1 event fired"); - _.deepEqual(actualTweens, ['x'], "x tween finished"); + _.deepEqual(actualTweens, ["x"], "x tween finished"); _.strictEqual(e.x, 100, "end position"); _.strictEqual(e.y, 25, "mid position"); _.strictEqual(e.z, 150, "mid position"); @@ -129,27 +151,27 @@ e.resumeTweens(); Crafty.timer.simulateFrames(5); _.strictEqual(fired, 2, "2 events fired"); - _.deepEqual(actualTweens, ['x', 'y', 'z'], "all tweens finished"); + _.deepEqual(actualTweens, ["x", "y", "z"], "all tweens finished"); _.strictEqual(e.x, 100, "end position"); _.strictEqual(e.y, 50, "end position"); _.strictEqual(e.z, 300, "end position"); // no change after all finished - Crafty.timer.simulateFrames(10+2); + Crafty.timer.simulateFrames(10 + 2); _.strictEqual(fired, 2, "2 events fired"); - _.deepEqual(actualTweens, ['x', 'y', 'z'], "all tweens finished"); + _.deepEqual(actualTweens, ["x", "y", "z"], "all tweens finished"); _.strictEqual(e.x, 100, "end position"); _.strictEqual(e.y, 50, "end position"); _.strictEqual(e.z, 300, "end position"); }); - test('.tweenSpeed', function(_) { + test(".tweenSpeed", function(_) { var actualTweens = [], - fired = 0; + fired = 0; - var e = Crafty.e('2D, Tween') + var e = Crafty.e("2D, Tween") .tween({ y: 50, z: 300 }, 200) // 10 frames = 200ms by default - .bind('TweenEnd', function(props) { + .bind("TweenEnd", function(props) { ++fired; actualTweens = actualTweens.concat(Object.keys(props).sort()); }); @@ -163,31 +185,35 @@ _.strictEqual(e.z, 150, "mid position"); e.tweenSpeed = 1.0; - Crafty.timer.simulateFrames(5+1); + Crafty.timer.simulateFrames(5 + 1); _.strictEqual(fired, 1, "1 event fired"); - _.deepEqual(actualTweens, ['y', 'z'], "y and z tween finished"); + _.deepEqual(actualTweens, ["y", "z"], "y and z tween finished"); _.strictEqual(e.y, 50, "end position"); _.strictEqual(e.z, 300, "end position"); }); - test('cancel tween', function(_) { + test("cancel tween", function(_) { _.expect(12); // 3[x,y,z-start] + 2[x,z]*3[TweenEnd] + 3[x,y,z-end] assertions var fired = 0, - expectedTweens = ['x', 'z']; + expectedTweens = ["x", "z"]; - var e = Crafty.e('2D, Tween') + var e = Crafty.e("2D, Tween") .tween({ x: 100 }, 100) // 5 frames == 100 ms by default .tween({ y: 100, z: 100 }, 200) // 10 frames == 200 ms by default - .bind('TweenEnd', function(props) { + .bind("TweenEnd", function(props) { _.strictEqual(Object.keys(props).length, 1, "one tween prop ended"); var prop = Object.keys(props)[0]; - _.strictEqual(prop, expectedTweens[fired++], "tween prop ended at expected time"); + _.strictEqual( + prop, + expectedTweens[fired++], + "tween prop ended at expected time" + ); _.strictEqual(this[prop], 100, "tween prop ended at expected position"); // cancel "y" tween prop after 100ms - if (fired === 1) this.cancelTween('y'); + if (fired === 1) this.cancelTween("y"); }); // check tween start pos @@ -203,28 +229,28 @@ _.strictEqual(e.z, 100, "end position"); }); - test('fully cancelled tween should not trigger TweenEnd event', function(_) { + test("fully cancelled tween should not trigger TweenEnd event", function(_) { var fired = false; - Crafty.e('2D, Tween') - .tween({ x: 100, y: 100}, 200) // 10 frames == 200 ms by default - .cancelTween('x') - .cancelTween('y') - .bind('TweenEnd', function() { + Crafty.e("2D, Tween") + .tween({ x: 100, y: 100 }, 200) // 10 frames == 200 ms by default + .cancelTween("x") + .cancelTween("y") + .bind("TweenEnd", function() { fired = true; }); - Crafty.timer.simulateFrames(10+2); + Crafty.timer.simulateFrames(10 + 2); _.notOk(fired, "TweenEnd shouldn't have fired."); fired = false; - var tweenObj = { x: 100, y: 100}; - Crafty.e('2D, Tween') + var tweenObj = { x: 100, y: 100 }; + Crafty.e("2D, Tween") .tween(tweenObj, 200) // 10 frames == 200 ms by default .cancelTween(tweenObj) - .bind('TweenEnd', function() { + .bind("TweenEnd", function() { fired = true; }); - Crafty.timer.simulateFrames(10+2); + Crafty.timer.simulateFrames(10 + 2); _.notOk(fired, "TweenEnd shouldn't have fired."); }); })(); diff --git a/tests/unit/debug/debug.js b/tests/unit/debug/debug.js index 71d0bfb5..35565dfc 100644 --- a/tests/unit/debug/debug.js +++ b/tests/unit/debug/debug.js @@ -5,7 +5,7 @@ module("DebugLayer"); test("DebugCanvas", function(_) { - if (!(Crafty.support.canvas)) { + if (!Crafty.support.canvas) { expect(0); return; } @@ -13,24 +13,46 @@ var ctx = Crafty.DebugCanvas.context; e.debugFill("purple"); - _.strictEqual(e._debug.fillStyle, "purple", "fill style set correctly on entity"); + _.strictEqual( + e._debug.fillStyle, + "purple", + "fill style set correctly on entity" + ); e.debugStroke("green"); - _.strictEqual(e._debug.strokeStyle, "green", "stroke style set correctly on entity"); + _.strictEqual( + e._debug.strokeStyle, + "green", + "stroke style set correctly on entity" + ); e.debugDraw(ctx); - _.strictEqual(ctx.fillStyle, "#800080", "context.fillStyle set correctly on draw"); // fillStyle will report the hex code - _.strictEqual(ctx.strokeStyle, "#008000", "context.strokeStyle set correctly on draw"); + _.strictEqual( + ctx.fillStyle, + "#800080", + "context.fillStyle set correctly on draw" + ); // fillStyle will report the hex code + _.strictEqual( + ctx.strokeStyle, + "#008000", + "context.strokeStyle set correctly on draw" + ); e.debugFill(); - _.strictEqual(e._debug.fillStyle, "red", "default fill style set correctly"); + _.strictEqual( + e._debug.fillStyle, + "red", + "default fill style set correctly" + ); e.debugStroke(); - _.strictEqual(e._debug.strokeStyle, "red", "default stroke style set correctly"); - + _.strictEqual( + e._debug.strokeStyle, + "red", + "default stroke style set correctly" + ); e.destroy(); - }); test("DebugRectangle", function(_) { @@ -46,73 +68,154 @@ e.rotation = 90; e.debugRectangle(e._mbr || e); - _.strictEqual(e.debugRect._h, 10, "debugRect has correct height of MBR after rotation"); + _.strictEqual( + e.debugRect._h, + 10, + "debugRect has correct height of MBR after rotation" + ); e.destroy(); - }); test("Hitbox debugging", function(_) { - var e = Crafty.e("2D, Collision, WiredHitBox").attr({ - x: 10, - y: 10, - w: 10, - h: 20 - }).collision(); - _.strictEqual(e.polygon.points[0], 10, "WiredHitBox -- correct x coord for upper right corner"); - _.strictEqual(e.polygon.points[5], 30, "correct y coord for lower right corner"); - _.notEqual(typeof e._debug.strokeStyle, "undefined", "stroke style is assigned"); - _.strictEqual(typeof e._debug.fillStyle, "undefined", "fill style is undefined"); + var e = Crafty.e("2D, Collision, WiredHitBox") + .attr({ + x: 10, + y: 10, + w: 10, + h: 20 + }) + .collision(); + _.strictEqual( + e.polygon.points[0], + 10, + "WiredHitBox -- correct x coord for upper right corner" + ); + _.strictEqual( + e.polygon.points[5], + 30, + "correct y coord for lower right corner" + ); + _.notEqual( + typeof e._debug.strokeStyle, + "undefined", + "stroke style is assigned" + ); + _.strictEqual( + typeof e._debug.fillStyle, + "undefined", + "fill style is undefined" + ); e.destroy(); - var e2 = Crafty.e("2D, Collision, SolidHitBox").attr({ - x: 10, - y: 10, - w: 10, - h: 20 - }).collision(); - _.strictEqual(e2.polygon.points[0], 10, "SolidHitBox -- correct x coord for upper right corner"); - _.strictEqual(e2.polygon.points[5], 30, "correct y coord for lower right corner"); - _.strictEqual(typeof e2._debug.strokeStyle, "undefined", "stroke style is undefined"); - _.notEqual(typeof e2._debug.fillStyle, "undefined", "fill style is assigned"); + var e2 = Crafty.e("2D, Collision, SolidHitBox") + .attr({ + x: 10, + y: 10, + w: 10, + h: 20 + }) + .collision(); + _.strictEqual( + e2.polygon.points[0], + 10, + "SolidHitBox -- correct x coord for upper right corner" + ); + _.strictEqual( + e2.polygon.points[5], + 30, + "correct y coord for lower right corner" + ); + _.strictEqual( + typeof e2._debug.strokeStyle, + "undefined", + "stroke style is undefined" + ); + _.notEqual( + typeof e2._debug.fillStyle, + "undefined", + "fill style is assigned" + ); e2.collision(new Crafty.polygon([0, 0, 15, 0, 0, 15])); - _.strictEqual(e2.polygon.points[5], 25, "After change -- correct y coord for third point"); + _.strictEqual( + e2.polygon.points[5], + 25, + "After change -- correct y coord for third point" + ); e2.destroy(); - }); test("AreaMap debugging", function(_) { - var e = Crafty.e("2D, AreaMap, WiredAreaMap").attr({ - x: 10, - y: 10, - w: 10, - h: 20 - }).areaMap(50, 0, 100, 100, 0, 100); - _.strictEqual(e.polygon.points[0], 60, "WiredAreaMap -- correct x coord for upper right corner"); - _.strictEqual(e.polygon.points[5], 110, "correct y coord for lower right corner"); - _.notEqual(typeof e._debug.strokeStyle, "undefined", "stroke style is assigned"); - _.strictEqual(typeof e._debug.fillStyle, "undefined", "fill style is undefined"); + var e = Crafty.e("2D, AreaMap, WiredAreaMap") + .attr({ + x: 10, + y: 10, + w: 10, + h: 20 + }) + .areaMap(50, 0, 100, 100, 0, 100); + _.strictEqual( + e.polygon.points[0], + 60, + "WiredAreaMap -- correct x coord for upper right corner" + ); + _.strictEqual( + e.polygon.points[5], + 110, + "correct y coord for lower right corner" + ); + _.notEqual( + typeof e._debug.strokeStyle, + "undefined", + "stroke style is assigned" + ); + _.strictEqual( + typeof e._debug.fillStyle, + "undefined", + "fill style is undefined" + ); e.destroy(); - var e2 = Crafty.e("2D, AreaMap, SolidAreaMap").attr({ - x: 10, - y: 10, - w: 10, - h: 20 - }).areaMap(50, 0, 100, 100, 0, 100); - _.strictEqual(e2.polygon.points[0], 60, "SolidAreaMap -- correct x coord for upper right corner"); - _.strictEqual(e2.polygon.points[5], 110, "correct y coord for lower right corner"); - _.strictEqual(typeof e2._debug.strokeStyle, "undefined", "stroke style is undefined"); - _.notEqual(typeof e2._debug.fillStyle, "undefined", "fill style is assigned"); + var e2 = Crafty.e("2D, AreaMap, SolidAreaMap") + .attr({ + x: 10, + y: 10, + w: 10, + h: 20 + }) + .areaMap(50, 0, 100, 100, 0, 100); + _.strictEqual( + e2.polygon.points[0], + 60, + "SolidAreaMap -- correct x coord for upper right corner" + ); + _.strictEqual( + e2.polygon.points[5], + 110, + "correct y coord for lower right corner" + ); + _.strictEqual( + typeof e2._debug.strokeStyle, + "undefined", + "stroke style is undefined" + ); + _.notEqual( + typeof e2._debug.fillStyle, + "undefined", + "fill style is assigned" + ); e2.areaMap(new Crafty.polygon([0, 0, 15, 0, 0, 15])); - _.strictEqual(e2.polygon.points[5], 25, "After change -- correct y coord for third point"); + _.strictEqual( + e2.polygon.points[5], + 25, + "After change -- correct y coord for third point" + ); e2.destroy(); - }); })(); diff --git a/tests/unit/debug/logging.js b/tests/unit/debug/logging.js index afdec820..9ca9e4d5 100644 --- a/tests/unit/debug/logging.js +++ b/tests/unit/debug/logging.js @@ -4,30 +4,45 @@ module("Crafty.log"); - test("Logging works when console.log is enabled", function(_){ + test("Logging works when console.log is enabled", function(_) { // this makes sure we don't crash on IE9; handle with care as console not always available on IE9 - var logger = (typeof window !== "undefined") ? (window.console = window.console || {}) : console; + var logger = + typeof window !== "undefined" + ? (window.console = window.console || {}) + : console; var original_log = logger.log; var logged_message = ""; - logger.log = function(msg) { logged_message = msg; }; + logger.log = function(msg) { + logged_message = msg; + }; var test_message = "test message"; - + Crafty.log(test_message); - _.strictEqual(logged_message, test_message, "Crafty.log correctly passes through to console.log"); - + _.strictEqual( + logged_message, + test_message, + "Crafty.log correctly passes through to console.log" + ); + Crafty.loggingEnabled = false; logged_message = ""; Crafty.log(test_message); - _.strictEqual(logged_message, "", "Crafty.log does nothing when logging is disabled."); + _.strictEqual( + logged_message, + "", + "Crafty.log does nothing when logging is disabled." + ); Crafty.loggingEnabled = true; logger.log = undefined; Crafty.log(test_message); - _.strictEqual(logged_message, "", "Crafty.log does not crash when console.log is undefined."); - + _.strictEqual( + logged_message, + "", + "Crafty.log does not crash when console.log is undefined." + ); logger.log = original_log; }); - })(); diff --git a/tests/unit/graphics/color.js b/tests/unit/graphics/color.js index cd4105d3..61749cae 100644 --- a/tests/unit/graphics/color.js +++ b/tests/unit/graphics/color.js @@ -4,7 +4,7 @@ module("Crafty.assignColor"); - test("hex codes", function(_){ + test("hex codes", function(_) { var c = {}; Crafty.assignColor("#FF0000", c); _.strictEqual(c._red, 255, "red is 255"); @@ -13,7 +13,7 @@ _.strictEqual(c._strength, 1, "strength is 1.0"); }); - test("short hex codes", function(_){ + test("short hex codes", function(_) { var c = {}; Crafty.assignColor("#123", c); _.strictEqual(c._red, 17, "red is #11"); @@ -22,7 +22,7 @@ _.strictEqual(c._strength, 1, "strength is 1.0"); }); - test("common color names", function(_){ + test("common color names", function(_) { var c = {}; Crafty.assignColor("red", c); _.strictEqual(c._red, 255, "red is 255"); @@ -31,7 +31,7 @@ _.strictEqual(c._strength, 1, "strength is 1.0"); }); - test("less common color names", function(_){ + test("less common color names", function(_) { var c = {}; Crafty.assignColor("lightsalmon", c); _.strictEqual(c._red, 255, "red is 255"); @@ -46,7 +46,7 @@ _.strictEqual(c._strength, 1, "strength is 1.0"); }); - test("rgb strings", function(_){ + test("rgb strings", function(_) { var c = {}; Crafty.assignColor("rgb(1, 2, 3)", c); _.strictEqual(c._red, 1, "red is 1"); @@ -55,7 +55,7 @@ _.strictEqual(c._strength, 1, "strength is 1.0"); }); - test("rgba strings", function(_){ + test("rgba strings", function(_) { var c = {}; Crafty.assignColor("rgba(255, 0, 0, 0.5)", c); _.strictEqual(c._red, 255, "red is 255"); @@ -66,7 +66,7 @@ module("Color"); - test("Color by single string", function(_){ + test("Color by single string", function(_) { var e = Crafty.e("2D, DOM, Color"); e.color("red"); _.strictEqual(e._red, 255, "red is 255"); @@ -75,7 +75,7 @@ _.strictEqual(e._strength, 1, "strength is 1.0"); }); - test("Color by rgb", function(_){ + test("Color by rgb", function(_) { var e = Crafty.e("2D, DOM, Color"); e.color(255, 0, 0); _.strictEqual(e._red, 255, "red is 255"); @@ -84,7 +84,7 @@ _.strictEqual(e._strength, 1, "strength is 1.0"); }); - test("Color by rgba", function(_){ + test("Color by rgba", function(_) { var e = Crafty.e("2D, DOM, Color"); e.color(255, 0, 0, 0.5); _.strictEqual(e._red, 255, "red is 255"); @@ -93,7 +93,7 @@ _.strictEqual(e._strength, 0.5, "strength is 0.5"); }); - test("Color by string + alpha", function(_){ + test("Color by string + alpha", function(_) { var e = Crafty.e("2D, DOM, Color"); e.color("red", 0.5); _.strictEqual(e._red, 255, "red is 255"); @@ -101,5 +101,4 @@ _.strictEqual(e._blue, 0, "blue is 0"); _.strictEqual(e._strength, 0.5, "strength is 0.5"); }); - })(); diff --git a/tests/unit/graphics/dom-helper.js b/tests/unit/graphics/dom-helper.js index b77b6182..3577667b 100644 --- a/tests/unit/graphics/dom-helper.js +++ b/tests/unit/graphics/dom-helper.js @@ -4,11 +4,11 @@ module("dom-helper", { beforeEach: function() { - var div = document.createElement('div'); - div.style.position = 'absolute'; - div.style.top = '10000px'; - div.textContent = 'test'; - div.id = 'test'; + var div = document.createElement("div"); + div.style.position = "absolute"; + div.style.top = "10000px"; + div.textContent = "test"; + div.id = "test"; document.body.appendChild(div); Crafty.stage.x = 10; @@ -21,7 +21,10 @@ document.documentElement.scrollTop = 100; } // 2nd condition is workaround for limitation while testing on headless browser (phantomjs) - if (!document.documentElement || document.documentElement.scrollTop !== 100) { + if ( + !document.documentElement || + document.documentElement.scrollTop !== 100 + ) { document.body.scrollTop = 100; } }, @@ -32,8 +35,16 @@ }); test("translate coordinates", function(_) { - _.strictEqual(Crafty.domHelper.translate(10, 10).x, 0, "translates x from 10 to 0"); - _.strictEqual(Crafty.domHelper.translate(10, 10).y, 100, "translates y from 10 to 100"); + _.strictEqual( + Crafty.domHelper.translate(10, 10).x, + 0, + "translates x from 10 to 0" + ); + _.strictEqual( + Crafty.domHelper.translate(10, 10).y, + 100, + "translates y from 10 to 100" + ); }); test("reuses obj", function(_) { @@ -47,4 +58,4 @@ _.strictEqual(out, obj, "reuses objet"); _.deepEqual(out, obj, "contents are same"); }); -})(); \ No newline at end of file +})(); diff --git a/tests/unit/graphics/dom.js b/tests/unit/graphics/dom.js index 52cd0c66..56d0273c 100644 --- a/tests/unit/graphics/dom.js +++ b/tests/unit/graphics/dom.js @@ -14,7 +14,10 @@ _.strictEqual(useCss3dTransforms.avoidCss3dTransforms, false); if (Crafty.support.css3dtransform) { - _.strictEqual(useCss3dTransforms._cssStyles.transform, "translate3d(10px,10px,0)"); + _.strictEqual( + useCss3dTransforms._cssStyles.transform, + "translate3d(10px,10px,0)" + ); _.strictEqual(useCss3dTransforms._cssStyles.top, ""); _.strictEqual(useCss3dTransforms._cssStyles.left, ""); } else { @@ -50,10 +53,14 @@ element.removeComponent("removeMe"); _.strictEqual(element.has("removeMe"), false, "component removed"); - _.strictEqual(hasClassName(element, "removeMe"), false, "classname removed"); + _.strictEqual( + hasClassName(element, "removeMe"), + false, + "classname removed" + ); }); - test("DOM component correctly invalidates", function(_){ + test("DOM component correctly invalidates", function(_) { var element = Crafty.e("DOM"); _.strictEqual(element._changed, true, "element starts dirty"); element._changed = false; @@ -61,30 +68,47 @@ _.strictEqual(element._changed, true, "element dirty after invalidate"); }); - - test("removing DOM component cleans up", function(_){ + test("removing DOM component cleans up", function(_) { var element = Crafty.e("DOM"); var node = element._element; - _.strictEqual(node.parentNode, Crafty.s("DefaultDOMLayer")._div, "child of the stage"); + _.strictEqual( + node.parentNode, + Crafty.s("DefaultDOMLayer")._div, + "child of the stage" + ); element._changed = false; element.removeComponent("DOM"); element.trigger("Invalidate"); - _.strictEqual(element._changed, false, "no longer gets dirty after removal of 'DOM'"); - _.strictEqual(node.parentNode, null, "no parent node after removal of 'DOM'"); - + _.strictEqual( + element._changed, + false, + "no longer gets dirty after removal of 'DOM'" + ); + _.strictEqual( + node.parentNode, + null, + "no parent node after removal of 'DOM'" + ); }); - - test("Removing Color component resets DOM correctly", function(_){ + test("Removing Color component resets DOM correctly", function(_) { var element = Crafty.e("DOM, Color"); var node = element._element; element.color("red"); // Style won't be updated until rendering occurs Crafty.timer.simulateFrames(1); - _.notEqual(node.style.backgroundColor, "transparent", "Element is not initially transparent"); + _.notEqual( + node.style.backgroundColor, + "transparent", + "Element is not initially transparent" + ); element.removeComponent("Color"); - _.strictEqual(node.style.backgroundColor, "transparent", "Transparent after removal of Color"); + _.strictEqual( + node.style.backgroundColor, + "transparent", + "Transparent after removal of Color" + ); }); -})(); \ No newline at end of file +})(); diff --git a/tests/unit/graphics/sprite-animation.js b/tests/unit/graphics/sprite-animation.js index 6f69155c..bfe30bed 100644 --- a/tests/unit/graphics/sprite-animation.js +++ b/tests/unit/graphics/sprite-animation.js @@ -2,7 +2,6 @@ var module = QUnit.module; var test = QUnit.test; - var spriteAnimation = null; var eventFrames = []; @@ -11,85 +10,186 @@ // Initialize a sprite component Crafty.sprite(64, "../assets/numbers.png", { - "numbers": [0, 0], - "number0": [0, 0], - "number1": [1, 0], - "number2": [2, 0], - "number3": [3, 0], - "number4": [4, 0], - "number5": [5, 0], - "number6": [6, 0], - "number7": [7, 0], - "number8": [8, 0], - "number9": [9, 0] + numbers: [0, 0], + number0: [0, 0], + number1: [1, 0], + number2: [2, 0], + number3: [3, 0], + number4: [4, 0], + number5: [5, 0], + number6: [6, 0], + number7: [7, 0], + number8: [8, 0], + number9: [9, 0] }); - module("Sprite"); test("Change sprite coordinates", function(_) { - var ent = Crafty.e('2D, Canvas') - .attr({ w: 64, h: 64 }); - - // adding the base Sprite component should provide no sprite coordinates - ent.addComponent('Sprite'); - _.strictEqual(ent.__coord, undefined, "sprite coodinates unknown"); - - // adding a sprite component should affect sprite coordinates - ent.addComponent('numbers'); - _.strictEqual(ent.__coord[0], 0, "sprite position inside sprite map matches"); - _.strictEqual(ent.__coord[1], 0, "sprite position inside sprite map matches"); - _.strictEqual(ent.__coord[2], 64, "sprite dimension inside sprite map matches"); - _.strictEqual(ent.__coord[3], 64, "sprite dimension inside sprite map matches"); - - // changing sprite via cell location should affect sprite coordinates - ent.sprite(1, 0, 2, 1); - _.strictEqual(ent.__coord[0], 1 * 64, "sprite position inside sprite map matches"); - _.strictEqual(ent.__coord[1], 0 * 64, "sprite position inside sprite map matches"); - _.strictEqual(ent.__coord[2], 2 * 64, "sprite dimension inside sprite map matches"); - _.strictEqual(ent.__coord[3], 1 * 64, "sprite dimension inside sprite map matches"); - - // changing sprite via tile name should affect sprite coordinates - ent.sprite('number9'); - _.strictEqual(ent.__coord[0], 9 * 64, "sprite position inside sprite map matches"); - _.strictEqual(ent.__coord[1], 0 * 64, "sprite position inside sprite map matches"); - _.strictEqual(ent.__coord[2], 1 * 64, "sprite dimension inside sprite map matches"); - _.strictEqual(ent.__coord[3], 1 * 64, "sprite dimension inside sprite map matches"); - - // changing sprite via invalid tile name should have no effect on sprite coordinates - ent.sprite('InvalidIdentifierXYZ'); - _.strictEqual(ent.__coord[0], 9 * 64, "sprite position inside sprite map matches"); - _.strictEqual(ent.__coord[1], 0 * 64, "sprite position inside sprite map matches"); - _.strictEqual(ent.__coord[2], 1 * 64, "sprite dimension inside sprite map matches"); - _.strictEqual(ent.__coord[3], 1 * 64, "sprite dimension inside sprite map matches"); - - // switching sprite components should affect sprite coordinates - ent.removeComponent('numbers'); - ent.addComponent('number3'); - _.strictEqual(ent.__coord[0], 3 * 64, "sprite position inside sprite map matches"); - _.strictEqual(ent.__coord[1], 0 * 64, "sprite position inside sprite map matches"); - _.strictEqual(ent.__coord[2], 1 * 64, "sprite dimension inside sprite map matches"); - _.strictEqual(ent.__coord[3], 1 * 64, "sprite dimension inside sprite map matches"); - - ent.destroy(); + var ent = Crafty.e("2D, Canvas").attr({ w: 64, h: 64 }); + + // adding the base Sprite component should provide no sprite coordinates + ent.addComponent("Sprite"); + _.strictEqual(ent.__coord, undefined, "sprite coodinates unknown"); + + // adding a sprite component should affect sprite coordinates + ent.addComponent("numbers"); + _.strictEqual( + ent.__coord[0], + 0, + "sprite position inside sprite map matches" + ); + _.strictEqual( + ent.__coord[1], + 0, + "sprite position inside sprite map matches" + ); + _.strictEqual( + ent.__coord[2], + 64, + "sprite dimension inside sprite map matches" + ); + _.strictEqual( + ent.__coord[3], + 64, + "sprite dimension inside sprite map matches" + ); + + // changing sprite via cell location should affect sprite coordinates + ent.sprite(1, 0, 2, 1); + _.strictEqual( + ent.__coord[0], + 1 * 64, + "sprite position inside sprite map matches" + ); + _.strictEqual( + ent.__coord[1], + 0 * 64, + "sprite position inside sprite map matches" + ); + _.strictEqual( + ent.__coord[2], + 2 * 64, + "sprite dimension inside sprite map matches" + ); + _.strictEqual( + ent.__coord[3], + 1 * 64, + "sprite dimension inside sprite map matches" + ); + + // changing sprite via tile name should affect sprite coordinates + ent.sprite("number9"); + _.strictEqual( + ent.__coord[0], + 9 * 64, + "sprite position inside sprite map matches" + ); + _.strictEqual( + ent.__coord[1], + 0 * 64, + "sprite position inside sprite map matches" + ); + _.strictEqual( + ent.__coord[2], + 1 * 64, + "sprite dimension inside sprite map matches" + ); + _.strictEqual( + ent.__coord[3], + 1 * 64, + "sprite dimension inside sprite map matches" + ); + + // changing sprite via invalid tile name should have no effect on sprite coordinates + ent.sprite("InvalidIdentifierXYZ"); + _.strictEqual( + ent.__coord[0], + 9 * 64, + "sprite position inside sprite map matches" + ); + _.strictEqual( + ent.__coord[1], + 0 * 64, + "sprite position inside sprite map matches" + ); + _.strictEqual( + ent.__coord[2], + 1 * 64, + "sprite dimension inside sprite map matches" + ); + _.strictEqual( + ent.__coord[3], + 1 * 64, + "sprite dimension inside sprite map matches" + ); + + // switching sprite components should affect sprite coordinates + ent.removeComponent("numbers"); + ent.addComponent("number3"); + _.strictEqual( + ent.__coord[0], + 3 * 64, + "sprite position inside sprite map matches" + ); + _.strictEqual( + ent.__coord[1], + 0 * 64, + "sprite position inside sprite map matches" + ); + _.strictEqual( + ent.__coord[2], + 1 * 64, + "sprite dimension inside sprite map matches" + ); + _.strictEqual( + ent.__coord[3], + 1 * 64, + "sprite dimension inside sprite map matches" + ); + + ent.destroy(); }); - module("Sprite animation", { beforeEach: function() { // Add an animation to the stage - spriteAnimation = Crafty.e('2D, DOM, numbers, SpriteAnimation'); + spriteAnimation = Crafty.e("2D, DOM, numbers, SpriteAnimation"); spriteAnimation.attr({ x: 10, y: 10 }); - spriteAnimation.reel('count', 200, 0, 0, 10); // 10 frames duration - spriteAnimation.reel('countSlow', 1200, 0, 0, 10); //60 frames duration - spriteAnimation.reel('countEven', 100, [[0, 0], [2, 0], [4, 0], [6, 0], [8, 0]]); // 5 frames - spriteAnimation.reel('countEven_names', 100, ["number0", "number2", "number4", "number6", "number8"]); // animation by sprite names - spriteAnimation.reel('short', 60, 0, 0, 3); // 3 frames - - spriteAnimation.reel("count").resetAnimation().pauseAnimation(); - spriteAnimation.reel("countEven").resetAnimation().pauseAnimation(); - spriteAnimation.reel("countSlow").resetAnimation().pauseAnimation(); - spriteAnimation.reel("short").resetAnimation().pauseAnimation(); + spriteAnimation.reel("count", 200, 0, 0, 10); // 10 frames duration + spriteAnimation.reel("countSlow", 1200, 0, 0, 10); //60 frames duration + spriteAnimation.reel("countEven", 100, [ + [0, 0], + [2, 0], + [4, 0], + [6, 0], + [8, 0] + ]); // 5 frames + spriteAnimation.reel("countEven_names", 100, [ + "number0", + "number2", + "number4", + "number6", + "number8" + ]); // animation by sprite names + spriteAnimation.reel("short", 60, 0, 0, 3); // 3 frames + + spriteAnimation + .reel("count") + .resetAnimation() + .pauseAnimation(); + spriteAnimation + .reel("countEven") + .resetAnimation() + .pauseAnimation(); + spriteAnimation + .reel("countSlow") + .resetAnimation() + .pauseAnimation(); + spriteAnimation + .reel("short") + .resetAnimation() + .pauseAnimation(); eventFrames = []; finishedAnimations = []; @@ -122,8 +222,20 @@ test("Test .animate() with no active reel", function(_) { var newAnimation = Crafty.e("SpriteAnimation"); - _.throws(function() {newAnimation.animate();}, /No reel is specified, and there is no currently active reel./, "Throws when calling .animate()."); - _.throws(function() {newAnimation.animate(3);}, /No reel is specified, and there is no currently active reel./, "Throws when calling .animate() with loop count."); + _.throws( + function() { + newAnimation.animate(); + }, + /No reel is specified, and there is no currently active reel./, + "Throws when calling .animate()." + ); + _.throws( + function() { + newAnimation.animate(3); + }, + /No reel is specified, and there is no currently active reel./, + "Throws when calling .animate() with loop count." + ); newAnimation.destroy(); }); @@ -143,10 +255,34 @@ test("Test .reelPosition() with no active reel", function(_) { var newAnimation = Crafty.e("SpriteAnimation"); - _.throws(function() {newAnimation.reelPosition();}, /No active reel/, "Throws when calling .reelPosition()."); - _.throws(function() {newAnimation.reelPosition(0.5);}, /No active reel/, "Throws when calling .reelPosition(0.5)."); - _.throws(function() {newAnimation.reelPosition(2);}, /No active reel/, "Throws when calling .reelPosition(2)."); - _.throws(function() {newAnimation.reelPosition('end');}, /No active reel/, "Throws when calling .reelPosition('end')."); + _.throws( + function() { + newAnimation.reelPosition(); + }, + /No active reel/, + "Throws when calling .reelPosition()." + ); + _.throws( + function() { + newAnimation.reelPosition(0.5); + }, + /No active reel/, + "Throws when calling .reelPosition(0.5)." + ); + _.throws( + function() { + newAnimation.reelPosition(2); + }, + /No active reel/, + "Throws when calling .reelPosition(2)." + ); + _.throws( + function() { + newAnimation.reelPosition("end"); + }, + /No active reel/, + "Throws when calling .reelPosition('end')." + ); newAnimation.destroy(); }); @@ -156,14 +292,16 @@ var newAnimation = Crafty.e("SpriteAnimation"); ret = newAnimation.resumeAnimation(); - _.notEqual(this._isPlaying, true, "Resume animation is a no-op when no reel is defined."); + _.notEqual( + this._isPlaying, + true, + "Resume animation is a no-op when no reel is defined." + ); _.strictEqual(ret, newAnimation, "Resume animation returns self."); - newAnimation.destroy(); }); - test("Test .pauseAnimation() with no active reel", function(_) { var ret; var newAnimation = Crafty.e("SpriteAnimation"); @@ -171,15 +309,19 @@ ret = newAnimation.pauseAnimation(); _.strictEqual(ret, newAnimation, "Pause animation returns self."); - newAnimation.destroy(); }); test("Test .resetAnimation() with no active reel", function(_) { var newAnimation = Crafty.e("SpriteAnimation"); - _.throws(function() {newAnimation.resetAnimation();}, /No active reel/, "Throws when calling .resetAnimation()."); - + _.throws( + function() { + newAnimation.resetAnimation(); + }, + /No active reel/, + "Throws when calling .resetAnimation()." + ); newAnimation.destroy(); }); @@ -194,19 +336,36 @@ ret = newAnimation.isPlaying("short"); _.strictEqual(ret, false, "isPlaying('short') returns false."); - newAnimation.destroy(); }); test("Test reel switching functionality", function(_) { var ret = spriteAnimation.reel("short"); _.strictEqual(ret, spriteAnimation, "Correctly returned self"); - _.strictEqual(spriteAnimation._currentReelId, "short", "Correct _currentReelId after switching"); - _.strictEqual(spriteAnimation._currentReel.id, "short", "Correct _currentReel.id after switching"); - - _.throws( function() {spriteAnimation.reel("wrong");}, /The specified reel wrong is undefined/, "Function should throw on bad reel"); - - _.strictEqual(spriteAnimation._currentReelId, "short", "Correct _currentReelId after attempting to switch to bad reel"); + _.strictEqual( + spriteAnimation._currentReelId, + "short", + "Correct _currentReelId after switching" + ); + _.strictEqual( + spriteAnimation._currentReel.id, + "short", + "Correct _currentReel.id after switching" + ); + + _.throws( + function() { + spriteAnimation.reel("wrong"); + }, + /The specified reel wrong is undefined/, + "Function should throw on bad reel" + ); + + _.strictEqual( + spriteAnimation._currentReelId, + "short", + "Correct _currentReelId after attempting to switch to bad reel" + ); }); test("Test using reel() with no arguments", function(_) { @@ -218,7 +377,11 @@ // Don't ever do this in actual code! spriteAnimation._currentReelId = null; ret = spriteAnimation.reel(); - _.strictEqual(ret, null, ".reel() returns the current id after it's set manually"); + _.strictEqual( + ret, + null, + ".reel() returns the current id after it's set manually" + ); // Reset currentReelId, since we messed it up! spriteAnimation.reel("count"); }); @@ -226,31 +389,50 @@ test("Test using .getReel() with no arguments", function(_) { spriteAnimation.reel("short"); var ret = spriteAnimation.getReel(); - _.strictEqual(ret.id, spriteAnimation._currentReelId, "getReel returns reel with the correct id"); + _.strictEqual( + ret.id, + spriteAnimation._currentReelId, + "getReel returns reel with the correct id" + ); }); /* jshint -W069 */ test("Test using .getReel() to get specific reels", function(_) { spriteAnimation.reel("short"); var ret = spriteAnimation.getReel("count"); - _.strictEqual(ret, spriteAnimation._reels['count'], "getReel('count') returns correctly when active reel is 'short'"); + _.strictEqual( + ret, + spriteAnimation._reels["count"], + "getReel('count') returns correctly when active reel is 'short'" + ); ret = spriteAnimation.getReel("nonsense"); - _.strictEqual(typeof ret, "undefined", "getReel returns undefined when nonexistant reel requested"); + _.strictEqual( + typeof ret, + "undefined", + "getReel returns undefined when nonexistant reel requested" + ); }); /* jshint +W069 */ - test("Test using .reel to set an animation using start and end values", function(_) { - var ret = spriteAnimation.reel('short-test', 3, 0, 0, 3); + var ret = spriteAnimation.reel("short-test", 3, 0, 0, 3); _.strictEqual(ret, spriteAnimation, ".reel returned self correctly"); - spriteAnimation.reel('short-test'); - var reel = spriteAnimation.getReel('short-test'); + spriteAnimation.reel("short-test"); + var reel = spriteAnimation.getReel("short-test"); _.strictEqual(reel.id, "short-test", "Id of reel is set correctly."); _.strictEqual(reel.duration, 3, "Reel has correct duration."); - _.strictEqual(reel.currentFrame, 0, "Reel starts with correct currentFrame of 0."); - _.strictEqual(reel.defaultLoops, 1, "Reel starts with correct default number of loops."); + _.strictEqual( + reel.currentFrame, + 0, + "Reel starts with correct currentFrame of 0." + ); + _.strictEqual( + reel.defaultLoops, + 1, + "Reel starts with correct default number of loops." + ); var frames = reel.frames; _.strictEqual(frames.length, 3, "Reel has correct number of frames."); @@ -260,15 +442,23 @@ }); test("Test using .reel to set a forward animation using start and end values with row wrapping", function(_) { - var ret = spriteAnimation.reel('long-test-forward', 70, 0, 0, 7, 4); + var ret = spriteAnimation.reel("long-test-forward", 70, 0, 0, 7, 4); _.strictEqual(ret, spriteAnimation, ".reel returned self correctly"); - spriteAnimation.reel('long-test-forward'); - var reel = spriteAnimation.getReel('long-test-forward'); + spriteAnimation.reel("long-test-forward"); + var reel = spriteAnimation.getReel("long-test-forward"); _.strictEqual(reel.id, "long-test-forward", "Id of reel is set correctly."); _.strictEqual(reel.duration, 70, "Reel has correct duration."); - _.strictEqual(reel.currentFrame, 0, "Reel starts with correct currentFrame of 0."); - _.strictEqual(reel.defaultLoops, 1, "Reel starts with correct default number of loops."); + _.strictEqual( + reel.currentFrame, + 0, + "Reel starts with correct currentFrame of 0." + ); + _.strictEqual( + reel.defaultLoops, + 1, + "Reel starts with correct default number of loops." + ); var frames = reel.frames; _.strictEqual(frames.length, 7, "Reel has correct number of frames."); @@ -282,15 +472,27 @@ }); test("Test using .reel to set a backward animation using start and end values with row wrapping", function(_) { - var ret = spriteAnimation.reel('long-test-backward', 70, 2, 1, -7, 4); + var ret = spriteAnimation.reel("long-test-backward", 70, 2, 1, -7, 4); _.strictEqual(ret, spriteAnimation, ".reel returned self correctly"); - spriteAnimation.reel('long-test-backward'); - var reel = spriteAnimation.getReel('long-test-backward'); - _.strictEqual(reel.id, "long-test-backward", "Id of reel is set correctly."); + spriteAnimation.reel("long-test-backward"); + var reel = spriteAnimation.getReel("long-test-backward"); + _.strictEqual( + reel.id, + "long-test-backward", + "Id of reel is set correctly." + ); _.strictEqual(reel.duration, 70, "Reel has correct duration."); - _.strictEqual(reel.currentFrame, 0, "Reel starts with correct currentFrame of 0."); - _.strictEqual(reel.defaultLoops, 1, "Reel starts with correct default number of loops."); + _.strictEqual( + reel.currentFrame, + 0, + "Reel starts with correct currentFrame of 0." + ); + _.strictEqual( + reel.defaultLoops, + 1, + "Reel starts with correct default number of loops." + ); var frames = reel.frames; _.strictEqual(frames.length, 7, "Reel has correct number of frames."); @@ -305,15 +507,23 @@ test("Test using .reel to set an animation using an array of frames", function(_) { // This should produce the same results as the previous test! - var ret = spriteAnimation.reel('short-test-2', 3, [[0, 0], [1, 0], [2, 0]] ); + var ret = spriteAnimation.reel("short-test-2", 3, [[0, 0], [1, 0], [2, 0]]); _.strictEqual(ret, spriteAnimation, ".reel returned self correctly"); - spriteAnimation.reel('short-test-2'); - var reel = spriteAnimation.getReel('short-test-2'); + spriteAnimation.reel("short-test-2"); + var reel = spriteAnimation.getReel("short-test-2"); _.strictEqual(reel.id, "short-test-2", "Id of reel is set correctly."); _.strictEqual(reel.duration, 3, "Reel has correct duration."); - _.strictEqual(reel.currentFrame, 0, "Reel starts with correct currentFrame of 0."); - _.strictEqual(reel.defaultLoops, 1, "Reel starts with correct default number of loops."); + _.strictEqual( + reel.currentFrame, + 0, + "Reel starts with correct currentFrame of 0." + ); + _.strictEqual( + reel.defaultLoops, + 1, + "Reel starts with correct default number of loops." + ); var frames = reel.frames; _.strictEqual(frames.length, 3, "Reel has correct number of frames."); @@ -325,59 +535,117 @@ test("Test using .reel to set an animation using an array of sprite names", function(_) { // This should produce the same results as the previous test! - var ret = spriteAnimation.reel('short-test-3', 3, ['number0', 'number1', 'number2'] ); + var ret = spriteAnimation.reel("short-test-3", 3, [ + "number0", + "number1", + "number2" + ]); _.strictEqual(ret, spriteAnimation, ".reel returned self correctly"); - spriteAnimation.reel('short-test-3'); - var reel = spriteAnimation.getReel('short-test-3'); + spriteAnimation.reel("short-test-3"); + var reel = spriteAnimation.getReel("short-test-3"); _.strictEqual(reel.id, "short-test-3", "Id of reel is set correctly."); _.strictEqual(reel.duration, 3, "Reel has correct duration."); - _.strictEqual(reel.currentFrame, 0, "Reel starts with correct currentFrame of 0."); - _.strictEqual(reel.defaultLoops, 1, "Reel starts with correct default number of loops."); + _.strictEqual( + reel.currentFrame, + 0, + "Reel starts with correct currentFrame of 0." + ); + _.strictEqual( + reel.defaultLoops, + 1, + "Reel starts with correct default number of loops." + ); var frames = reel.frames; _.strictEqual(frames.length, 3, "Reel has correct number of frames."); // This relies on the sprite being defined with a size of 64 - _.deepEqual(frames[0], 'number0', "First frame is correct."); - _.deepEqual(frames[1], 'number1', "Second frame is correct."); - _.deepEqual(frames[2], 'number2', "Third frame is correct."); + _.deepEqual(frames[0], "number0", "First frame is correct."); + _.deepEqual(frames[1], "number1", "Second frame is correct."); + _.deepEqual(frames[2], "number2", "Third frame is correct."); }); test("Set position of current reel by frame number.", function(_) { spriteAnimation.reel("short"); var ret = spriteAnimation.reelPosition(1); _.strictEqual(ret, spriteAnimation, "Correctly returned self."); - _.strictEqual(spriteAnimation._currentReel.currentFrame, 1, "Set to frame of index 1."); + _.strictEqual( + spriteAnimation._currentReel.currentFrame, + 1, + "Set to frame of index 1." + ); spriteAnimation.reelPosition(0); - _.strictEqual(spriteAnimation._currentReel.currentFrame, 0, "Set back to first frame."); + _.strictEqual( + spriteAnimation._currentReel.currentFrame, + 0, + "Set back to first frame." + ); spriteAnimation.reelPosition(10); - _.strictEqual(spriteAnimation._currentReel.currentFrame, 2, "Set to frame beyond last index."); + _.strictEqual( + spriteAnimation._currentReel.currentFrame, + 2, + "Set to frame beyond last index." + ); spriteAnimation.reelPosition(-1); - _.strictEqual(spriteAnimation._currentReel.currentFrame, 1, "Set to negative frame; 1 before last frame."); + _.strictEqual( + spriteAnimation._currentReel.currentFrame, + 1, + "Set to negative frame; 1 before last frame." + ); spriteAnimation.reelPosition(-10); - _.strictEqual(spriteAnimation._currentReel.currentFrame, 0, "Set to negative frame with magnitude greater than length."); + _.strictEqual( + spriteAnimation._currentReel.currentFrame, + 0, + "Set to negative frame with magnitude greater than length." + ); }); test("Set position of current reel by progress.", function(_) { spriteAnimation.reel("short"); var ret = spriteAnimation.reelPosition(0.33); _.strictEqual(ret, spriteAnimation, "Correctly returned self."); - _.strictEqual(spriteAnimation._currentReel.currentFrame, 0, "Set to frame of index 0 at 33%"); + _.strictEqual( + spriteAnimation._currentReel.currentFrame, + 0, + "Set to frame of index 0 at 33%" + ); spriteAnimation.reelPosition(0.34); - _.strictEqual(spriteAnimation._currentReel.currentFrame, 1, "Set to frame of index 1 at 34%"); + _.strictEqual( + spriteAnimation._currentReel.currentFrame, + 1, + "Set to frame of index 1 at 34%" + ); spriteAnimation.reelPosition(0.66); - _.strictEqual(spriteAnimation._currentReel.currentFrame, 1, "Set to frame of index 1 at 66%"); + _.strictEqual( + spriteAnimation._currentReel.currentFrame, + 1, + "Set to frame of index 1 at 66%" + ); spriteAnimation.reelPosition(0.67); - _.strictEqual(spriteAnimation._currentReel.currentFrame, 2, "Set to frame of index 2 at 67%"); - - _.throws( function() {spriteAnimation.reelPosition(1.2);}, /Position .+ invalid/, "Throws an exception when set to 120% completion"); + _.strictEqual( + spriteAnimation._currentReel.currentFrame, + 2, + "Set to frame of index 2 at 67%" + ); + + _.throws( + function() { + spriteAnimation.reelPosition(1.2); + }, + /Position .+ invalid/, + "Throws an exception when set to 120% completion" + ); }); test("Set position of reel to end", function(_) { spriteAnimation.reel("short"); var ret = spriteAnimation.reelPosition("end"); _.strictEqual(ret, spriteAnimation, "Correctly returned self."); - _.strictEqual(spriteAnimation._currentReel.currentFrame, 2, "Set to last frame"); + _.strictEqual( + spriteAnimation._currentReel.currentFrame, + 2, + "Set to last frame" + ); }); test("Get current position of reel", function(_) { @@ -389,151 +657,255 @@ pos = spriteAnimation.reelPosition(); _.strictEqual(pos, 0, "Correctly returned current position at beginning."); }); - + test("Set position of current reel by sprite name.", function(_) { spriteAnimation.reel("countEven_names"); - + spriteAnimation.reelFrame("number2"); - _.strictEqual(spriteAnimation._currentReel.currentFrame, 1, "Set to frame of index 1."); - + _.strictEqual( + spriteAnimation._currentReel.currentFrame, + 1, + "Set to frame of index 1." + ); + spriteAnimation.reelFrame("number0"); - _.strictEqual(spriteAnimation._currentReel.currentFrame, 0, "Set back to first frame."); - - _.throws(function() {spriteAnimation.reelFrame("numbers");}, /Frame .+ is invalid/, "Throws when calling .reelFrame()."); + _.strictEqual( + spriteAnimation._currentReel.currentFrame, + 0, + "Set back to first frame." + ); + + _.throws( + function() { + spriteAnimation.reelFrame("numbers"); + }, + /Frame .+ is invalid/, + "Throws when calling .reelFrame()." + ); }); test("Get number of loops left; .loops(void)", function(_) { spriteAnimation.reel("count"); var loopCount = spriteAnimation.loops(); - _.strictEqual(loopCount, spriteAnimation._currentReel.easing.loops, "Number of loops matches"); + _.strictEqual( + loopCount, + spriteAnimation._currentReel.easing.loops, + "Number of loops matches" + ); }); test("Set number of loops left; .loops(n)", function(_) { spriteAnimation.reel("count"); var ret = spriteAnimation.loops(10); - _.strictEqual(10, spriteAnimation._currentReel.easing.loops, "Number of loops set to 10"); + _.strictEqual( + 10, + spriteAnimation._currentReel.easing.loops, + "Number of loops set to 10" + ); _.strictEqual(ret, spriteAnimation, "Returned self."); ret = spriteAnimation.loops(-1); - _.strictEqual(Infinity, spriteAnimation._currentReel.easing.loops, "Set to loop forever"); + _.strictEqual( + Infinity, + spriteAnimation._currentReel.easing.loops, + "Set to loop forever" + ); _.strictEqual(ret, spriteAnimation, "Returned self."); }); test("Play an animation", function(_) { // Play for 10 frames, each sprite will show up for one frame - var ret = spriteAnimation.animate('count'); + var ret = spriteAnimation.animate("count"); _.strictEqual(ret, spriteAnimation, "Returned self correctly."); - _.strictEqual(ret._currentReel.easing.loops, 1, "Set to one loop correctly."); + _.strictEqual( + ret._currentReel.easing.loops, + 1, + "Set to one loop correctly." + ); for (var i = 0; i < 10; i++) { var activeReel = spriteAnimation.getReel(); - _.strictEqual(activeReel.currentFrame, i, "Frame " + i + " should be displayed"); - _.deepEqual(activeReel.frames[spriteAnimation.reelPosition()], [i, 0], "Correct sprite coordinates should be used"); + _.strictEqual( + activeReel.currentFrame, + i, + "Frame " + i + " should be displayed" + ); + _.deepEqual( + activeReel.frames[spriteAnimation.reelPosition()], + [i, 0], + "Correct sprite coordinates should be used" + ); Crafty.timer.simulateFrames(1); } }); test("Play an animation defined using an array", function(_) { // Play for 5 frames, each sprite will show up for one frame - spriteAnimation.animate('countEven'); + spriteAnimation.animate("countEven"); for (var i = 0; i < 5; i++) { var activeReel = spriteAnimation.getReel(); - _.strictEqual(activeReel.currentFrame, i, "Frame " + i + " should be displayed"); - _.deepEqual(activeReel.frames[spriteAnimation.reelPosition()], [2*i, 0], "Correct sprite coordinates should be used"); + _.strictEqual( + activeReel.currentFrame, + i, + "Frame " + i + " should be displayed" + ); + _.deepEqual( + activeReel.frames[spriteAnimation.reelPosition()], + [2 * i, 0], + "Correct sprite coordinates should be used" + ); Crafty.timer.simulateFrames(1); } }); - + test("Play an animation defined using sprite names", function(_) { // Play for 5 frames, each sprite will show up for one frame - spriteAnimation.animate('countEven_names'); + spriteAnimation.animate("countEven_names"); for (var i = 0; i < 5; i++) { var activeReel = spriteAnimation.getReel(); - _.strictEqual(activeReel.currentFrame, i, "Frame " + i + " should be displayed"); - _.deepEqual(activeReel.frames[spriteAnimation.reelPosition()], "number"+(2*i), "Correct sprite name should be used"); + _.strictEqual( + activeReel.currentFrame, + i, + "Frame " + i + " should be displayed" + ); + _.deepEqual( + activeReel.frames[spriteAnimation.reelPosition()], + "number" + 2 * i, + "Correct sprite name should be used" + ); Crafty.timer.simulateFrames(1); } }); - test("Play an animation where sprites are displayed for more than one frame", function(_) { // Play for 60 frames, each sprite will show up for six frames - spriteAnimation.animate('countSlow'); + spriteAnimation.animate("countSlow"); for (var i = 0; i < 10; i++) { var activeReel = spriteAnimation.getReel(); - _.strictEqual(activeReel.currentFrame, i, "Frame " + i + " should be displayed"); + _.strictEqual( + activeReel.currentFrame, + i, + "Frame " + i + " should be displayed" + ); Crafty.timer.simulateFrames(6); } }); test("Play an animation at twice the rate", function(_) { spriteAnimation.animationSpeed = 2; - spriteAnimation.animate('count'); + spriteAnimation.animate("count"); Crafty.timer.simulateFrames(3); var activeReel = spriteAnimation.getReel(); - _.strictEqual(activeReel.currentFrame, 6, "Frame 6 should be displayed after 3 ticks at double speed"); + _.strictEqual( + activeReel.currentFrame, + 6, + "Frame 6 should be displayed after 3 ticks at double speed" + ); spriteAnimation.animationSpeed = 1; }); test("Play an animation at half the rate", function(_) { spriteAnimation.animationSpeed = 0.5; - spriteAnimation.animate('count'); + spriteAnimation.animate("count"); Crafty.timer.simulateFrames(6); var activeReel = spriteAnimation.getReel(); - _.strictEqual(activeReel.currentFrame, 3, "Frame 3 should be displayed after 6 ticks at half speed"); + _.strictEqual( + activeReel.currentFrame, + 3, + "Frame 3 should be displayed after 6 ticks at half speed" + ); spriteAnimation.animationSpeed = 1; }); test("Show the last frame after an animation ends", function(_) { - spriteAnimation.animate('count'); + spriteAnimation.animate("count"); Crafty.timer.simulateFrames(20); var activeReel = spriteAnimation.getReel(); - _.strictEqual(activeReel.currentFrame, 9, "Frame 9 should be displayed after the animation ends"); + _.strictEqual( + activeReel.currentFrame, + 9, + "Frame 9 should be displayed after the animation ends" + ); }); test("Get events for each frame change", function(_) { - spriteAnimation.animate('count'); + spriteAnimation.animate("count"); Crafty.timer.simulateFrames(20); - _.deepEqual(eventFrames, [1, 2, 3, 4, 5, 6, 7, 8, 9], "Expected events for frames 0 through 9"); + _.deepEqual( + eventFrames, + [1, 2, 3, 4, 5, 6, 7, 8, 9], + "Expected events for frames 0 through 9" + ); }); test("Get an event when an animation ends", function(_) { - spriteAnimation.animate('count'); + spriteAnimation.animate("count"); Crafty.timer.simulateFrames(20); - _.deepEqual(finishedAnimations, ['count'], "Should have received an event for the 'count' animation's end"); + _.deepEqual( + finishedAnimations, + ["count"], + "Should have received an event for the 'count' animation's end" + ); }); - test("Play an animation and then switch to another reel", function(_) { var countReel = spriteAnimation.getReel("count"); - spriteAnimation.animate('count'); - _.strictEqual(spriteAnimation._isPlaying, true, "playing after call to animate."); - _.strictEqual(countReel.easing.paused, false, "easing not paused after call to animate."); - spriteAnimation.reel('short'); - _.strictEqual(spriteAnimation._isPlaying, false, "not playing after switching reel."); - _.strictEqual(countReel.easing.paused, true, "easing paused after switching reel."); + spriteAnimation.animate("count"); + _.strictEqual( + spriteAnimation._isPlaying, + true, + "playing after call to animate." + ); + _.strictEqual( + countReel.easing.paused, + false, + "easing not paused after call to animate." + ); + spriteAnimation.reel("short"); + _.strictEqual( + spriteAnimation._isPlaying, + false, + "not playing after switching reel." + ); + _.strictEqual( + countReel.easing.paused, + true, + "easing paused after switching reel." + ); }); test("Play an animation with a repeat count", function(_) { - var ret = spriteAnimation.animate('short', 3); - _.strictEqual(ret._currentReel.easing.loops, 3, "Set to 3 loops correctly."); + var ret = spriteAnimation.animate("short", 3); + _.strictEqual( + ret._currentReel.easing.loops, + 3, + "Set to 3 loops correctly." + ); Crafty.timer.simulateFrames(10); _.strictEqual(ret._currentReel.easing.loops, 0, "0 loops after running."); // No "FrameChange" event for starting frame! - _.deepEqual(eventFrames, [1, 2, 0, 1, 2, 0, 1, 2], "Expected events matching the repeat count"); - _.deepEqual(finishedAnimations, ['short'], "Expected a single animation end event"); + _.deepEqual( + eventFrames, + [1, 2, 0, 1, 2, 0, 1, 2], + "Expected events matching the repeat count" + ); + _.deepEqual( + finishedAnimations, + ["short"], + "Expected a single animation end event" + ); }); test("Play an animation with an infinite repeat count", function(_) { - _.strictEqual(spriteAnimation.getReel('short').currentFrame, 0); - spriteAnimation.animate('short', -1); + _.strictEqual(spriteAnimation.getReel("short").currentFrame, 0); + spriteAnimation.animate("short", -1); Crafty.timer.simulateFrames(32); var expected = []; for (var i = 0; i < 11; i++) { - if (i>0) { + if (i > 0) { expected.push(0); } @@ -541,128 +913,258 @@ expected.push(2); } - _.deepEqual(eventFrames, expected, "Expected events matching the amount of frames that pass"); + _.deepEqual( + eventFrames, + expected, + "Expected events matching the amount of frames that pass" + ); _.deepEqual(finishedAnimations, [], "Expected no animation to end"); }); test("Play an animation from a specific frame", function(_) { - spriteAnimation.reel('count').pauseAnimation().reelPosition(5).loops(1); + spriteAnimation + .reel("count") + .pauseAnimation() + .reelPosition(5) + .loops(1); spriteAnimation.resumeAnimation(); Crafty.timer.simulateFrames(5); - _.deepEqual(eventFrames, [5, 6, 7, 8, 9], "Expected events for frames 5 through 9"); - _.deepEqual(finishedAnimations, ['count'], "Expected a single animation end event"); + _.deepEqual( + eventFrames, + [5, 6, 7, 8, 9], + "Expected events for frames 5 through 9" + ); + _.deepEqual( + finishedAnimations, + ["count"], + "Expected a single animation end event" + ); }); test("Play an animation from a specific frame, with a repeat count", function(_) { - spriteAnimation.reel('count').loops(2).reelPosition(6); + spriteAnimation + .reel("count") + .loops(2) + .reelPosition(6); spriteAnimation.resumeAnimation(); Crafty.timer.simulateFrames(7); - _.deepEqual(eventFrames, [6, 7, 8, 9, 0, 1, 2, 3], "Expected events for frames 6 through 9 and then 0 through 3"); + _.deepEqual( + eventFrames, + [6, 7, 8, 9, 0, 1, 2, 3], + "Expected events for frames 6 through 9 and then 0 through 3" + ); }); test("Pause an animation", function(_) { - spriteAnimation.animate('count'); + spriteAnimation.animate("count"); Crafty.timer.simulateFrames(5); var ret = spriteAnimation.pauseAnimation(); Crafty.timer.simulateFrames(5); - _.deepEqual(eventFrames, [1, 2, 3, 4, 5], "Expected events for frames 1 through 5"); - _.strictEqual(ret, spriteAnimation, ".pauseAnimation() returned correct value"); + _.deepEqual( + eventFrames, + [1, 2, 3, 4, 5], + "Expected events for frames 1 through 5" + ); + _.strictEqual( + ret, + spriteAnimation, + ".pauseAnimation() returned correct value" + ); }); test("Play an animation while another is already playing", function(_) { - spriteAnimation.animate('count'); + spriteAnimation.animate("count"); Crafty.timer.simulateFrames(5); - spriteAnimation.animate('short'); + spriteAnimation.animate("short"); Crafty.timer.simulateFrames(10); - _.deepEqual(eventFrames, [1, 2, 3, 4, 5, 1, 2], "Expected events for frames from both animations."); - _.deepEqual(finishedAnimations, ['short'], "Expected end event for the second animation"); + _.deepEqual( + eventFrames, + [1, 2, 3, 4, 5, 1, 2], + "Expected events for frames from both animations." + ); + _.deepEqual( + finishedAnimations, + ["short"], + "Expected end event for the second animation" + ); }); test("Pause an animation, then resume it", function(_) { - _.strictEqual(spriteAnimation.getReel('count').currentFrame, 0); - spriteAnimation.animate('count'); + _.strictEqual(spriteAnimation.getReel("count").currentFrame, 0); + spriteAnimation.animate("count"); Crafty.timer.simulateFrames(5); - _.strictEqual(spriteAnimation._currentReel.currentFrame, 5, "Advanced to frame index 5 after 5 frames of animation"); + _.strictEqual( + spriteAnimation._currentReel.currentFrame, + 5, + "Advanced to frame index 5 after 5 frames of animation" + ); spriteAnimation.pauseAnimation(); Crafty.timer.simulateFrames(5); - _.strictEqual(spriteAnimation._currentReel.currentFrame, 5, "Didn't advance while paused"); + _.strictEqual( + spriteAnimation._currentReel.currentFrame, + 5, + "Didn't advance while paused" + ); var ret = spriteAnimation.resumeAnimation(); Crafty.timer.simulateFrames(5); - _.strictEqual(spriteAnimation._currentReel.currentFrame, 9, "Advanced to last frame when resumed"); - _.strictEqual(ret, spriteAnimation, ".resumeAnimation() returned correct value"); - - _.deepEqual(eventFrames, [1, 2, 3, 4, 5, 6, 7, 8, 9], "Expected one event for each frame 0 through 9"); - _.deepEqual(finishedAnimations, ['count'], "Expected a single animation end event"); + _.strictEqual( + spriteAnimation._currentReel.currentFrame, + 9, + "Advanced to last frame when resumed" + ); + _.strictEqual( + ret, + spriteAnimation, + ".resumeAnimation() returned correct value" + ); + + _.deepEqual( + eventFrames, + [1, 2, 3, 4, 5, 6, 7, 8, 9], + "Expected one event for each frame 0 through 9" + ); + _.deepEqual( + finishedAnimations, + ["count"], + "Expected a single animation end event" + ); }); test("Reset a paused animation", function(_) { - spriteAnimation.reel("count").reelPosition(5).pauseAnimation(); + spriteAnimation + .reel("count") + .reelPosition(5) + .pauseAnimation(); var ret = spriteAnimation.resetAnimation(); - _.strictEqual(spriteAnimation.isPlaying(), false, "Does not resume paused animation."); - _.strictEqual(spriteAnimation._currentReel.currentFrame, 0, "Resets to initial frame."); + _.strictEqual( + spriteAnimation.isPlaying(), + false, + "Does not resume paused animation." + ); + _.strictEqual( + spriteAnimation._currentReel.currentFrame, + 0, + "Resets to initial frame." + ); _.strictEqual(ret, spriteAnimation, "Correctly returns self."); }); test("Reset a playing animation", function(_) { - spriteAnimation.reel("count").reelPosition(5).resumeAnimation(); + spriteAnimation + .reel("count") + .reelPosition(5) + .resumeAnimation(); var ret = spriteAnimation.resetAnimation(); - _.strictEqual(spriteAnimation.isPlaying(), true, "Does not stop playing animation."); - _.strictEqual(spriteAnimation._currentReel.currentFrame, 0, "Resets to initial frame."); + _.strictEqual( + spriteAnimation.isPlaying(), + true, + "Does not stop playing animation." + ); + _.strictEqual( + spriteAnimation._currentReel.currentFrame, + 0, + "Resets to initial frame." + ); _.strictEqual(ret, spriteAnimation, "Correctly returns self."); }); test("Try to play an animation after it ends", function(_) { - spriteAnimation.animate('count'); + spriteAnimation.animate("count"); Crafty.timer.simulateFrames(10); spriteAnimation.resumeAnimation(); Crafty.timer.simulateFrames(1); - _.deepEqual(finishedAnimations, ['count', 'count'], "Expected the animation to end twice"); + _.deepEqual( + finishedAnimations, + ["count", "count"], + "Expected the animation to end twice" + ); }); test("Play an animation twice", function(_) { - spriteAnimation.animate('short'); + spriteAnimation.animate("short"); Crafty.timer.simulateFrames(3); - spriteAnimation.animate('short'); + spriteAnimation.animate("short"); Crafty.timer.simulateFrames(3); - _.deepEqual(eventFrames, [1, 2, 0, 1, 2], "Expected events for frames 0 through 2, twice"); - _.deepEqual(finishedAnimations, ['short', 'short'], "Expected the animation to end twice"); + _.deepEqual( + eventFrames, + [1, 2, 0, 1, 2], + "Expected events for frames 0 through 2, twice" + ); + _.deepEqual( + finishedAnimations, + ["short", "short"], + "Expected the animation to end twice" + ); }); test("Set an animation to a specific frame number", function(_) { - spriteAnimation.animate('short'); + spriteAnimation.animate("short"); Crafty.timer.simulateFrames(3); var ret = spriteAnimation.reelPosition(1); spriteAnimation.resumeAnimation(); Crafty.timer.simulateFrames(2); - _.deepEqual(eventFrames, [1, 2, 1, 2], "Expected events for frames 0 through 2 and then 1 through 2"); - _.deepEqual(finishedAnimations, ['short', 'short'], "Expected the animation to end twice"); - _.strictEqual(ret, spriteAnimation, ".reelPosition(1) returned correct value"); + _.deepEqual( + eventFrames, + [1, 2, 1, 2], + "Expected events for frames 0 through 2 and then 1 through 2" + ); + _.deepEqual( + finishedAnimations, + ["short", "short"], + "Expected the animation to end twice" + ); + _.strictEqual( + ret, + spriteAnimation, + ".reelPosition(1) returned correct value" + ); }); test("See if any animation is playing", function(_) { - _.strictEqual(spriteAnimation.isPlaying(), false, "No animation should be playing"); - spriteAnimation.animate('short'); - _.strictEqual(spriteAnimation.isPlaying(), true, "An animation should be playing"); + _.strictEqual( + spriteAnimation.isPlaying(), + false, + "No animation should be playing" + ); + spriteAnimation.animate("short"); + _.strictEqual( + spriteAnimation.isPlaying(), + true, + "An animation should be playing" + ); }); test("See if a specific animation is playing", function(_) { - spriteAnimation.animate('count'); - _.strictEqual(spriteAnimation.isPlaying('short'), false, "The 'short' animation shouldn't be playing"); - spriteAnimation.animate('short'); - _.strictEqual(spriteAnimation.isPlaying('short'), true, "The 'short' animation should be playing"); + spriteAnimation.animate("count"); + _.strictEqual( + spriteAnimation.isPlaying("short"), + false, + "The 'short' animation shouldn't be playing" + ); + spriteAnimation.animate("short"); + _.strictEqual( + spriteAnimation.isPlaying("short"), + true, + "The 'short' animation should be playing" + ); }); - test("Check that it's possible to chain animations", function(_){ - var chain = function(){this.animate('count');}; + test("Check that it's possible to chain animations", function(_) { + var chain = function() { + this.animate("count"); + }; spriteAnimation.one("AnimationEnd", chain); - spriteAnimation.animate('short'); + spriteAnimation.animate("short"); Crafty.timer.simulateFrames(4); - _.ok(spriteAnimation.isPlaying('count'), "Successfully plays count after short ends"); + _.ok( + spriteAnimation.isPlaying("count"), + "Successfully plays count after short ends" + ); }); })(); diff --git a/tests/unit/graphics/text.js b/tests/unit/graphics/text.js index a2ad6c91..9436ad70 100644 --- a/tests/unit/graphics/text.js +++ b/tests/unit/graphics/text.js @@ -5,59 +5,80 @@ module("Text"); test("fontFamily", function(_) { - var text = Crafty.e('DOM, Text').textFont({ - family: 'Times New Roman 400', - size: '30px' - }).text('Test'); - _.strictEqual(text.attr('_textFont').family, "'Times New Roman 400'", 'Expect to have singlequotes arount the family property.'); - + var text = Crafty.e("DOM, Text") + .textFont({ + family: "Times New Roman 400", + size: "30px" + }) + .text("Test"); + _.strictEqual( + text.attr("_textFont").family, + "'Times New Roman 400'", + "Expect to have singlequotes arount the family property." + ); }); test("text string", function(_) { - var text = Crafty.e('DOM, Text'); - _.strictEqual(text.text(), "", 'Expect text to be empty value'); + var text = Crafty.e("DOM, Text"); + _.strictEqual(text.text(), "", "Expect text to be empty value"); text.text("123"); - _.strictEqual(text._text, "123", 'Expect text to be set'); - _.strictEqual(text.text(), "123", 'Expect text to be get'); + _.strictEqual(text._text, "123", "Expect text to be set"); + _.strictEqual(text.text(), "123", "Expect text to be get"); }); test("static textGenerator", function(_) { - var text = Crafty.e('DOM, Text'); + var text = Crafty.e("DOM, Text"); var textValue1 = "123"; var textValue2 = "456"; text.testField = textValue1; - text.text(function() { return this.testField; }); - _.strictEqual(text._text, textValue1, 'Expect text to be set by generator function'); + text.text(function() { + return this.testField; + }); + _.strictEqual( + text._text, + textValue1, + "Expect text to be set by generator function" + ); text.testField = textValue2; - _.strictEqual(text._text, textValue1, 'Expect text to not be changed'); + _.strictEqual(text._text, textValue1, "Expect text to not be changed"); }); test("dynamic textGenerator", function(_) { - var text = Crafty.e('DOM, Text'); + var text = Crafty.e("DOM, Text"); var textValue1 = "123"; var textValue2 = "456"; text.testField = textValue1; // Test one-off function invocation - text.text(function(){ return this.testField; }); - _.strictEqual(text._text, textValue1, 'Expect text to be set by generator function'); + text.text(function() { + return this.testField; + }); + _.strictEqual( + text._text, + textValue1, + "Expect text to be set by generator function" + ); text.testField = textValue2; - _.strictEqual(text._text, textValue1, 'Expect text to be initial value'); + _.strictEqual(text._text, textValue1, "Expect text to be initial value"); Crafty.timer.simulateFrames(1); - _.strictEqual(text._text, textValue1, 'Expect text to be initial value'); + _.strictEqual(text._text, textValue1, "Expect text to be initial value"); // Test dynamic function invocation text.dynamicTextGeneration(true); - _.strictEqual(text._text, textValue1, 'Expect text to be initial value'); + _.strictEqual(text._text, textValue1, "Expect text to be initial value"); Crafty.timer.simulateFrames(1); - _.strictEqual(text._text, textValue2, 'Expect text to be updated by generator function'); + _.strictEqual( + text._text, + textValue2, + "Expect text to be updated by generator function" + ); }); test("dynamic textGenerator with custom event", function(_) { - var text = Crafty.e('DOM, Text'); + var text = Crafty.e("DOM, Text"); var textValue1 = "123"; var textValue2 = "456"; text.testField = textValue1; @@ -67,23 +88,35 @@ if (data) return data; return this.testField; }); - _.strictEqual(text._text, textValue1, 'Expect text to be set by generator function'); + _.strictEqual( + text._text, + textValue1, + "Expect text to be set by generator function" + ); text.testField = textValue2; - _.strictEqual(text._text, textValue1, 'Expect text to be initial value'); + _.strictEqual(text._text, textValue1, "Expect text to be initial value"); Crafty.timer.simulateFrames(1); - _.strictEqual(text._text, textValue1, 'Expect text to be initial value'); + _.strictEqual(text._text, textValue1, "Expect text to be initial value"); // Test dynamic function invocation text.dynamicTextGeneration(true, "MyEvent"); - _.strictEqual(text._text, textValue1, 'Expect text to be initial value'); + _.strictEqual(text._text, textValue1, "Expect text to be initial value"); Crafty.timer.simulateFrames(1); - _.strictEqual(text._text, textValue1, 'Expect text to be initial value'); + _.strictEqual(text._text, textValue1, "Expect text to be initial value"); text.trigger("MyEvent"); - _.strictEqual(text._text, textValue2, 'Expect text to be updated by generator function'); + _.strictEqual( + text._text, + textValue2, + "Expect text to be updated by generator function" + ); // Test parameter passing text.trigger("MyEvent", "789"); - _.strictEqual(text._text, "789", 'Expect text to be updated by generator function'); + _.strictEqual( + text._text, + "789", + "Expect text to be updated by generator function" + ); }); test("_getFontHeight", function(_) { @@ -113,15 +146,14 @@ var h2 = e.h; _.ok(h2 > 20, "Font height set correctly."); _.ok(h2 > h1, "Entity increases in height when font size is increased."); - }); test("Color should be defined", function(_) { var e = Crafty.e("2D, DOM, Text"); e.text("a"); - e.textColor('#00FF00'); + e.textColor("#00FF00"); _.ok(e._textColor === "rgba(0, 255, 0, 1)"); - e.textColor('rgba(255,0,0,0.5)'); + e.textColor("rgba(255,0,0,0.5)"); _.ok(e._textColor === "rgba(255, 0, 0, 0.5)"); e.destroy(); }); @@ -129,10 +161,10 @@ test("Alignment should be defined", function(_) { var e; var checkAlignment = function() { - _.ok(e._textAlign === e.defaultTextAlign); - e.text("a"); - e.textAlign('center'); - _.ok(e._textAlign === "center"); + _.ok(e._textAlign === e.defaultTextAlign); + e.text("a"); + e.textAlign("center"); + _.ok(e._textAlign === "center"); }; e = Crafty.e("2D, Canvas, Text"); checkAlignment(); @@ -140,12 +172,11 @@ checkAlignment(); }); - test("Listen to style events for DOM Text", function(_){ + test("Listen to style events for DOM Text", function(_) { var e = Crafty.e("2D, DOM, Text"); - - + e.text("hey how are you") - .textColor('#00FF00') + .textColor("#00FF00") .textFont("size", "50px") .textAlign("center"); _.strictEqual(e._textColor, "rgba(0, 255, 0, 1)", "Color should be green."); @@ -169,24 +200,49 @@ var left = e.mbr()._x; var width = e.mbr()._w; - e.textAlign('center'); - _.ok(left > e.mbr()._x, 'Left side of MBR after center aligning is not less than the old one'); + e.textAlign("center"); + _.ok( + left > e.mbr()._x, + "Left side of MBR after center aligning is not less than the old one" + ); /* width gets rounded, make sure it's close*/ - _.ok(e.mbr()._w - width <= 1, 'Width of MBR is different after center aligning'); + _.ok( + e.mbr()._w - width <= 1, + "Width of MBR is different after center aligning" + ); left = e.mbr()._x; - e.textAlign('right'); - _.ok(left > e.mbr()._x, 'Left side of MBR after right aligning is not less than the old one'); - _.ok(e.mbr()._w - width <= 1, 'Width of MBR is different after right aligning'); + e.textAlign("right"); + _.ok( + left > e.mbr()._x, + "Left side of MBR after right aligning is not less than the old one" + ); + _.ok( + e.mbr()._w - width <= 1, + "Width of MBR is different after right aligning" + ); left = e.mbr()._x; - e.textAlign('right'); - _.strictEqual(left, e.mbr()._x, 'Left side of MBR after right aligning again is different'); - _.ok(e.mbr()._w - width <= 1, 'Width of MBR is different after right aligning again'); + e.textAlign("right"); + _.strictEqual( + left, + e.mbr()._x, + "Left side of MBR after right aligning again is different" + ); + _.ok( + e.mbr()._w - width <= 1, + "Width of MBR is different after right aligning again" + ); left = e.mbr()._x; - e.textAlign('left'); - _.ok(left < e.mbr()._x, 'Left side of MBR after left aligning is not greater than the old one'); - _.ok(e.mbr()._w - width <= 1, 'Width of MBR is different after left aligning'); + e.textAlign("left"); + _.ok( + left < e.mbr()._x, + "Left side of MBR after left aligning is not greater than the old one" + ); + _.ok( + e.mbr()._w - width <= 1, + "Width of MBR is different after left aligning" + ); }); })(); diff --git a/tests/unit/graphics/viewport.js b/tests/unit/graphics/viewport.js index 7f26bd06..1a757281 100644 --- a/tests/unit/graphics/viewport.js +++ b/tests/unit/graphics/viewport.js @@ -17,10 +17,26 @@ // check initial rectangular region rect1 = Crafty.viewport.rect({}); // external rect object - _.strictEqual(rect1._x, -Crafty.viewport._x, "rect property matches expected value"); - _.strictEqual(rect1._y, -Crafty.viewport._y, "rect property matches expected value"); - _.strictEqual(rect1._w, Crafty.viewport._width / Crafty.viewport._scale, "rect property matches expected value"); - _.strictEqual(rect1._h, Crafty.viewport._height / Crafty.viewport._scale, "rect property matches expected value"); + _.strictEqual( + rect1._x, + -Crafty.viewport._x, + "rect property matches expected value" + ); + _.strictEqual( + rect1._y, + -Crafty.viewport._y, + "rect property matches expected value" + ); + _.strictEqual( + rect1._w, + Crafty.viewport._width / Crafty.viewport._scale, + "rect property matches expected value" + ); + _.strictEqual( + rect1._h, + Crafty.viewport._height / Crafty.viewport._scale, + "rect property matches expected value" + ); // check modified rectangular region Crafty.viewport.x = -25; @@ -52,16 +68,32 @@ }); var before = Crafty.domHelper.translate(e.x, e.y); - Crafty.viewport.scroll('_x', 100); - _.strictEqual(before.x - Crafty.domHelper.translate(e.x, e.y).x, 100, "Scroll in x direction"); - - Crafty.viewport.scroll('_y', 70); - _.strictEqual(before.y - Crafty.domHelper.translate(e.x, e.y).y, 70, "Scroll in y direction"); - - Crafty.viewport.scroll('_x', 0); - Crafty.viewport.scroll('_y', 0); - _.strictEqual(before.x - Crafty.domHelper.translate(e.x, e.y).x, 0, "Scroll to 0"); - _.strictEqual(before.y - Crafty.domHelper.translate(e.x, e.y).y, 0, "Scroll to 0"); + Crafty.viewport.scroll("_x", 100); + _.strictEqual( + before.x - Crafty.domHelper.translate(e.x, e.y).x, + 100, + "Scroll in x direction" + ); + + Crafty.viewport.scroll("_y", 70); + _.strictEqual( + before.y - Crafty.domHelper.translate(e.x, e.y).y, + 70, + "Scroll in y direction" + ); + + Crafty.viewport.scroll("_x", 0); + Crafty.viewport.scroll("_y", 0); + _.strictEqual( + before.x - Crafty.domHelper.translate(e.x, e.y).x, + 0, + "Scroll to 0" + ); + _.strictEqual( + before.y - Crafty.domHelper.translate(e.x, e.y).y, + 0, + "Scroll to 0" + ); }); test("scroll using x, y", function(_) { @@ -71,46 +103,91 @@ }); var before = Crafty.domHelper.translate(e.x, e.y); - Crafty.viewport.scroll('x', 100); - _.strictEqual(before.x - Crafty.domHelper.translate(e.x, e.y).x, 100, "Scroll in x direction"); - - Crafty.viewport.scroll('y', 70); - _.strictEqual(before.y - Crafty.domHelper.translate(e.x, e.y).y, 70, "Scroll in y direction"); - - Crafty.viewport.scroll('x', 0); - Crafty.viewport.scroll('y', 0); - _.strictEqual(before.x - Crafty.domHelper.translate(e.x, e.y).x, 0, "Scroll to 0"); - _.strictEqual(before.y - Crafty.domHelper.translate(e.x, e.y).y, 0, "Scroll to 0"); + Crafty.viewport.scroll("x", 100); + _.strictEqual( + before.x - Crafty.domHelper.translate(e.x, e.y).x, + 100, + "Scroll in x direction" + ); + + Crafty.viewport.scroll("y", 70); + _.strictEqual( + before.y - Crafty.domHelper.translate(e.x, e.y).y, + 70, + "Scroll in y direction" + ); + + Crafty.viewport.scroll("x", 0); + Crafty.viewport.scroll("y", 0); + _.strictEqual( + before.x - Crafty.domHelper.translate(e.x, e.y).x, + 0, + "Scroll to 0" + ); + _.strictEqual( + before.y - Crafty.domHelper.translate(e.x, e.y).y, + 0, + "Scroll to 0" + ); }); - test("Viewport resizing", function(_){ + test("Viewport resizing", function(_) { var flag = 0; Crafty("2D, Canvas"); - + var layer = Crafty.s("DefaultCanvasLayer"); - + var w = Crafty.viewport.width; - _.strictEqual( layer._canvas.width, Crafty.viewport.width, "Initial canvas size matches viewport"); - _.strictEqual(Crafty.stage.elem.style.width, Crafty.viewport.width + "px", "Initial stage size matches viewport"); - Crafty.bind("ViewportResize", function(){flag++;}); + _.strictEqual( + layer._canvas.width, + Crafty.viewport.width, + "Initial canvas size matches viewport" + ); + _.strictEqual( + Crafty.stage.elem.style.width, + Crafty.viewport.width + "px", + "Initial stage size matches viewport" + ); + Crafty.bind("ViewportResize", function() { + flag++; + }); Crafty.viewport.width += 10; _.strictEqual(flag, 1, "ViewportResize triggered"); - _.strictEqual(Crafty.viewport.width, w+10, "Viewport increased in width"); - _.strictEqual( layer._canvas.width, Crafty.viewport.width , "Canvas size matches viewport after change"); - _.strictEqual(Crafty.stage.elem.style.width, Crafty.viewport.width +"px", "Stage size matches viewport after change"); + _.strictEqual(Crafty.viewport.width, w + 10, "Viewport increased in width"); + _.strictEqual( + layer._canvas.width, + Crafty.viewport.width, + "Canvas size matches viewport after change" + ); + _.strictEqual( + Crafty.stage.elem.style.width, + Crafty.viewport.width + "px", + "Stage size matches viewport after change" + ); var h = Crafty.viewport.height; Crafty.viewport.height += 10; _.strictEqual(flag, 2, "ViewportResize triggered"); - _.strictEqual(Crafty.viewport.height, h+10, "Viewport increased in width"); - _.strictEqual( layer._canvas.height, Crafty.viewport.height , "Canvas size matches viewport after change"); - _.strictEqual(Crafty.stage.elem.style.height, Crafty.viewport.height +"px", "Stage size matches viewport after change"); - + _.strictEqual( + Crafty.viewport.height, + h + 10, + "Viewport increased in width" + ); + _.strictEqual( + layer._canvas.height, + Crafty.viewport.height, + "Canvas size matches viewport after change" + ); + _.strictEqual( + Crafty.stage.elem.style.height, + Crafty.viewport.height + "px", + "Stage size matches viewport after change" + ); }); test("follow", function(_) { @@ -120,9 +197,16 @@ y: Crafty.viewport.height + 70 }); Crafty.viewport.follow(e, 0, 0); - _.strictEqual(Crafty.viewport._x, (-(Crafty.viewport.width / 2 + 100)), "Center viewport on entity.x"); - _.strictEqual(Crafty.viewport._y, (-(Crafty.viewport.height / 2 + 70)), "Center viewport on entity.y"); - + _.strictEqual( + Crafty.viewport._x, + -(Crafty.viewport.width / 2 + 100), + "Center viewport on entity.x" + ); + _.strictEqual( + Crafty.viewport._y, + -(Crafty.viewport.height / 2 + 70), + "Center viewport on entity.y" + ); }); test("pan", function(_) { @@ -139,7 +223,11 @@ _.strictEqual(Crafty.viewport._x, -50, "Pan half the way on half the time"); _.strictEqual(done, 0, "CameraAnimationDone hasn't fired yet"); Crafty.timer.simulateFrames(5); - _.strictEqual(Crafty.viewport._x, -100, "Pan all the way when all the time is spent"); + _.strictEqual( + Crafty.viewport._x, + -100, + "Pan all the way when all the time is spent" + ); _.strictEqual(done, 1, "CameraAnimationDone has fired once"); done = 0; @@ -148,7 +236,6 @@ Crafty.timer.simulateFrames(20); _.strictEqual(Crafty.viewport._y, -100, "Pan all the way and stay there"); _.strictEqual(done, 1, "CameraAnimationDone has fired once"); - }); test("pan with easing", function(_) { @@ -156,13 +243,20 @@ Crafty.viewport.pan(100, 0, 10 * 20, "easeInQuad"); Crafty.timer.simulateFrames(5); - _.strictEqual(Crafty.viewport._x, -25, "Pan quarter of the way on half the time"); + _.strictEqual( + Crafty.viewport._x, + -25, + "Pan quarter of the way on half the time" + ); Crafty.timer.simulateFrames(5); - _.strictEqual(Crafty.viewport._x, -100, "Pan all the way when all the time is spent"); + _.strictEqual( + Crafty.viewport._x, + -100, + "Pan all the way when all the time is spent" + ); }); test("zoom", function(_) { - Crafty.viewport.clampToEntities = false; var done = 0; @@ -174,15 +268,26 @@ Crafty.viewport.zoom(2, 0, 0, 10 * 20); Crafty.timer.simulateFrames(5); - _.strictEqual(Crafty.viewport._scale, Math.sqrt(2), "Zooms sqrt(2) in half the time"); + _.strictEqual( + Crafty.viewport._scale, + Math.sqrt(2), + "Zooms sqrt(2) in half the time" + ); _.strictEqual(done, 0, "CameraAnimationDone hasn't fired yet"); Crafty.timer.simulateFrames(5); - _.strictEqual(Crafty.viewport._x, Crafty.viewport.width / 4, "move all the way when all the time is spent"); - _.strictEqual(Crafty.viewport._y, Crafty.viewport.height / 4, "move all the way when all the time is spent"); + _.strictEqual( + Crafty.viewport._x, + Crafty.viewport.width / 4, + "move all the way when all the time is spent" + ); + _.strictEqual( + Crafty.viewport._y, + Crafty.viewport.height / 4, + "move all the way when all the time is spent" + ); _.strictEqual(Crafty.viewport._scale, 2, "Zooms all the way in full time."); _.strictEqual(done, 1, "CameraAnimationDone has fired once"); - }); test("centerOn", function(_) { @@ -200,16 +305,23 @@ }; Crafty.one("CameraAnimationDone", panDone); - Crafty.viewport.centerOn(e, 10); Crafty.timer.simulateFrames(10); - _.strictEqual(Crafty.viewport._x, -e.w / 2 + Crafty.viewport.width / 2, "Entity centered after exact duration"); + _.strictEqual( + Crafty.viewport._x, + -e.w / 2 + Crafty.viewport.width / 2, + "Entity centered after exact duration" + ); _.strictEqual(done, 1, "CameraAnimationDone has fired once"); done = 0; Crafty.one("CameraAnimationDone", panDone); Crafty.timer.simulateFrames(10); - _.strictEqual(Crafty.viewport._x, -e.w / 2 + Crafty.viewport.width / 2, "Entity still centered 10 frames later"); + _.strictEqual( + Crafty.viewport._x, + -e.w / 2 + Crafty.viewport.width / 2, + "Entity still centered 10 frames later" + ); _.strictEqual(done, 0, "CameraAnimationDone doesn't fire after completion"); var e2 = Crafty.e("2D, DOM").attr({ @@ -218,18 +330,26 @@ w: 20, h: 20 }); - Crafty.viewport.scroll('x', 1500); - Crafty.viewport.scroll('y', 300); + Crafty.viewport.scroll("x", 1500); + Crafty.viewport.scroll("y", 300); Crafty.viewport.centerOn(e2, 1); Crafty.timer.simulateFrames(1); - _.strictEqual(Crafty.viewport._x, (-(e2.x + e2.w / 2 - Crafty.viewport.width / 2)), "Entity centered from non-zero origin"); - _.strictEqual(Crafty.viewport._y, (-(e2.y + e2.h / 2 - Crafty.viewport.height / 2)), "Entity centered from non-zero origin"); + _.strictEqual( + Crafty.viewport._x, + -(e2.x + e2.w / 2 - Crafty.viewport.width / 2), + "Entity centered from non-zero origin" + ); + _.strictEqual( + Crafty.viewport._y, + -(e2.y + e2.h / 2 - Crafty.viewport.height / 2), + "Entity centered from non-zero origin" + ); }); test("viewport.reset() gives correct values", function(_) { Crafty.viewport.clampToEntities = false; - Crafty.viewport.scroll('_x', 50); - Crafty.viewport.scroll('_y', 50); + Crafty.viewport.scroll("_x", 50); + Crafty.viewport.scroll("_y", 50); Crafty.viewport.scale(2); _.strictEqual(Crafty.viewport._x, 50, "Viewport starts scrolled"); _.strictEqual(Crafty.viewport._y, 50, "Viewport starts scrolled"); @@ -244,7 +364,7 @@ test("viewport.reset() triggers StopCamera", function(_) { Crafty.viewport.clampToEntities = false; var stopped = false; - Crafty.one("StopCamera", function(){ + Crafty.one("StopCamera", function() { stopped = true; }); Crafty.viewport.reset(); @@ -255,14 +375,20 @@ Crafty.viewport.clampToEntities = false; var done = 0; - Crafty.one("CameraAnimationDone", function() { done++; }); + Crafty.one("CameraAnimationDone", function() { + done++; + }); Crafty.viewport.pan(100, 0, 10 * 20); Crafty.timer.simulateFrames(5); // Stop at half-way point Crafty.trigger("StopCamera"); Crafty.timer.simulateFrames(5); - _.strictEqual(Crafty.viewport._x, -50, "Pan still half way after camera has been stopped"); + _.strictEqual( + Crafty.viewport._x, + -50, + "Pan still half way after camera has been stopped" + ); _.strictEqual(done, 0, "CameraAnimationDone hasn't fired"); }); @@ -270,14 +396,20 @@ Crafty.viewport.clampToEntities = false; var done = 0; - Crafty.one("CameraAnimationDone", function() { done++; }); + Crafty.one("CameraAnimationDone", function() { + done++; + }); - Crafty.viewport.zoom(4, 0, 0, 10 * 20); + Crafty.viewport.zoom(4, 0, 0, 10 * 20); Crafty.timer.simulateFrames(5); // Stop at half-way point Crafty.trigger("StopCamera"); Crafty.timer.simulateFrames(5); - _.strictEqual(Crafty.viewport._scale, 2, "Zoom at half way after camera has been stopped"); + _.strictEqual( + Crafty.viewport._scale, + 2, + "Zoom at half way after camera has been stopped" + ); _.strictEqual(done, 0, "CameraAnimationDone hasn't fired"); }); @@ -290,8 +422,14 @@ // pretend to click in the top-left corner. This is supposed to be // (x,y) = (-Crafty.viewport._x, -Crafty.viewport._y) - clientX = Crafty.stage.x - document.body.scrollLeft - document.documentElement.scrollLeft; - clientY = Crafty.stage.y - document.body.scrollTop - document.documentElement.scrollTop; + clientX = + Crafty.stage.x - + document.body.scrollLeft - + document.documentElement.scrollLeft; + clientY = + Crafty.stage.y - + document.body.scrollTop - + document.documentElement.scrollTop; craftyxy = Crafty.domHelper.translate(clientX, clientY); _.strictEqual(craftyxy.x, -Crafty.viewport._x); _.strictEqual(craftyxy.y, -Crafty.viewport._y); @@ -299,11 +437,24 @@ // pretend to click in the bottom-right corner. This is supposed to be // x = -Crafty.viewport._x + Crafty.viewport._width / Crafty.viewport._scale // y = -Crafty.viewport._y + Crafty.viewport._height / Crafty.viewport._scale - clientX = Crafty.stage.x + Crafty.stage.elem.clientWidth - document.body.scrollLeft - document.documentElement.scrollLeft; - clientY = Crafty.stage.y + Crafty.stage.elem.clientHeight - document.body.scrollTop - document.documentElement.scrollTop; + clientX = + Crafty.stage.x + + Crafty.stage.elem.clientWidth - + document.body.scrollLeft - + document.documentElement.scrollLeft; + clientY = + Crafty.stage.y + + Crafty.stage.elem.clientHeight - + document.body.scrollTop - + document.documentElement.scrollTop; craftyxy = Crafty.domHelper.translate(clientX, clientY); - _.strictEqual(craftyxy.x, -Crafty.viewport._x + (Crafty.viewport._width / Crafty.viewport._scale)); - _.strictEqual(craftyxy.y, -Crafty.viewport._y + (Crafty.viewport._height / Crafty.viewport._scale)); + _.strictEqual( + craftyxy.x, + -Crafty.viewport._x + Crafty.viewport._width / Crafty.viewport._scale + ); + _.strictEqual( + craftyxy.y, + -Crafty.viewport._y + Crafty.viewport._height / Crafty.viewport._scale + ); }); - -})(); \ No newline at end of file +})(); diff --git a/tests/unit/index-common.js b/tests/unit/index-common.js index 4239b448..c782699d 100644 --- a/tests/unit/index-common.js +++ b/tests/unit/index-common.js @@ -1,34 +1,34 @@ var files = [ - /** HELPER FUNCTIONS **/ - './common.js', - /** COMMON TEST CODE THAT RUNS IN BROWSER AND NODE **/ - './controls/controls.js', - './core/animation.js', - './core/core.js', - './core/events.js', - './core/model.js', - './core/scenes.js', - './core/storage.js', - './core/systems.js', - './core/time.js', - './core/tween.js', - './debug/logging.js', - './spatial/2d.js', - './spatial/collision.js', - './spatial/math.js', - './spatial/motion.js', - './spatial/platform.js', - './spatial/sat.js', - './spatial/spatial-grid.js', - './spatial/raycast.js' + /** HELPER FUNCTIONS **/ + "./common.js", + /** COMMON TEST CODE THAT RUNS IN BROWSER AND NODE **/ + "./controls/controls.js", + "./core/animation.js", + "./core/core.js", + "./core/events.js", + "./core/model.js", + "./core/scenes.js", + "./core/storage.js", + "./core/systems.js", + "./core/time.js", + "./core/tween.js", + "./debug/logging.js", + "./spatial/2d.js", + "./spatial/collision.js", + "./spatial/math.js", + "./spatial/motion.js", + "./spatial/platform.js", + "./spatial/sat.js", + "./spatial/spatial-grid.js", + "./spatial/raycast.js" ]; -if (typeof require === 'function') { - files.forEach(function (file) { - require(file); - }); +if (typeof require === "function") { + files.forEach(function(file) { + require(file); + }); } -if (typeof window !== 'undefined') { - window.COMMON_TEST_FILES = files; +if (typeof window !== "undefined") { + window.COMMON_TEST_FILES = files; } diff --git a/tests/unit/index-headless.js b/tests/unit/index-headless.js index e1af9c95..5fd2de4d 100644 --- a/tests/unit/index-headless.js +++ b/tests/unit/index-headless.js @@ -1,9 +1,9 @@ /** CRAFTY-JS **/ -global.craftyFactory = require('../../src/crafty-headless.js'); +global.craftyFactory = require("../../src/crafty-headless.js"); global.Crafty = global.craftyFactory(); /** TEST CODE THAT RUNS IN NODE **/ -require('./core/instances.js'); +require("./core/instances.js"); /** COMMON TEST CODE THAT RUNS IN BROWSER AND NODE **/ -require('./index-common.js'); +require("./index-common.js"); diff --git a/tests/unit/index.js b/tests/unit/index.js index 1149bcbf..727f40b4 100644 --- a/tests/unit/index.js +++ b/tests/unit/index.js @@ -1,18 +1,18 @@ /** BROWSER-SPECIFIC TEST CODE **/ var files = [ - './core/loader.js', - './debug/debug.js', - './graphics/color.js', - './graphics/dom.js', - './graphics/dom-helper.js', - './graphics/sprite-animation.js', - './graphics/text.js', - './graphics/viewport.js', - './inputs/inputs.js', - './isometric/isometric.js', - './sound/audio.js' + "./core/loader.js", + "./debug/debug.js", + "./graphics/color.js", + "./graphics/dom.js", + "./graphics/dom-helper.js", + "./graphics/sprite-animation.js", + "./graphics/text.js", + "./graphics/viewport.js", + "./inputs/inputs.js", + "./isometric/isometric.js", + "./sound/audio.js" ]; -if (typeof window !== 'undefined') { - window.BROWSER_TEST_FILES = files; +if (typeof window !== "undefined") { + window.BROWSER_TEST_FILES = files; } diff --git a/tests/unit/inputs/inputs.js b/tests/unit/inputs/inputs.js index 9d32afa3..dc389ff1 100644 --- a/tests/unit/inputs/inputs.js +++ b/tests/unit/inputs/inputs.js @@ -2,14 +2,15 @@ var module = QUnit.module; var test = QUnit.test; - module('Inputs'); + module("Inputs"); test("AreaMap", function(_) { var areaMapEvents = 0; - var e = Crafty.e("2D, DOM, AreaMap") - .bind("NewAreaMap", function(newAreaMap) { - areaMapEvents++; - }); + var e = Crafty.e("2D, DOM, AreaMap").bind("NewAreaMap", function( + newAreaMap + ) { + areaMapEvents++; + }); var poly = new Crafty.polygon([50, 0, 100, 100, 0, 100]); e.areaMap(poly); @@ -19,7 +20,10 @@ var arr = [50, 0, 100, 100, 0, 100]; e.areaMap(arr); _.ok(e.mapArea instanceof Crafty.polygon, "Hitbox is a polygon"); - _.ok(e.mapArea.points && e.mapArea.points !== arr, "Array used in hitbox is a clone of passed array"); + _.ok( + e.mapArea.points && e.mapArea.points !== arr, + "Array used in hitbox is a clone of passed array" + ); e.areaMap(50, 0, 100, 100, 0, 100); _.ok(e.mapArea instanceof Crafty.polygon, "Hitbox is a polygon"); @@ -28,82 +32,136 @@ }); test("AreaMap's layer correctly tracks pointer entities on destruction", function(_) { - var e = Crafty.e("2D, Mouse, DOM, Color"); - var layer = Crafty.s("DefaultDOMLayer"); - _.strictEqual(layer._pointerEntities, 1, "Single entity when created"); - e.destroy(); - _.strictEqual(layer._pointerEntities, 0, "Zero pointer entities when destroyed"); - }); + var e = Crafty.e("2D, Mouse, DOM, Color"); + var layer = Crafty.s("DefaultDOMLayer"); + _.strictEqual(layer._pointerEntities, 1, "Single entity when created"); + e.destroy(); + _.strictEqual( + layer._pointerEntities, + 0, + "Zero pointer entities when destroyed" + ); + }); - test("AreaMap's layer correctly tracks pointer entities when removing components", function(_) { - var e = Crafty.e("2D, Mouse, DOM, Color"); - var layer = Crafty.s("DefaultDOMLayer"); - _.strictEqual(layer._pointerEntities, 1, "Single entity when created"); - e.removeComponent("AreaMap"); - _.strictEqual(layer._pointerEntities, 0, "Zero pointer entities once component is removed"); - e.destroy(); - _.strictEqual(layer._pointerEntities, 0, "Zero pointer entities when destroyed"); - }); + test("AreaMap's layer correctly tracks pointer entities when removing components", function(_) { + var e = Crafty.e("2D, Mouse, DOM, Color"); + var layer = Crafty.s("DefaultDOMLayer"); + _.strictEqual(layer._pointerEntities, 1, "Single entity when created"); + e.removeComponent("AreaMap"); + _.strictEqual( + layer._pointerEntities, + 0, + "Zero pointer entities once component is removed" + ); + e.destroy(); + _.strictEqual( + layer._pointerEntities, + 0, + "Zero pointer entities when destroyed" + ); + }); - test("Layer correctly tracks pointer entities on AreaMap component addition", function(_) { - var e = Crafty.e("2D, DOM, Color"); - var layer = Crafty.s("DefaultDOMLayer"); - _.strictEqual(layer._pointerEntities, 0, "No entities before component is added"); - e.addComponent("AreaMap"); - _.strictEqual(layer._pointerEntities, 1, "Single entity after component is added"); - }); + test("Layer correctly tracks pointer entities on AreaMap component addition", function(_) { + var e = Crafty.e("2D, DOM, Color"); + var layer = Crafty.s("DefaultDOMLayer"); + _.strictEqual( + layer._pointerEntities, + 0, + "No entities before component is added" + ); + e.addComponent("AreaMap"); + _.strictEqual( + layer._pointerEntities, + 1, + "Single entity after component is added" + ); + }); - test("AreaMap's layer correctly tracks pointer entities on layer addition", function(_) { - var e = Crafty.e("2D, AreaMap, Color"); - var layer = Crafty.s("DefaultDOMLayer"); - _.strictEqual(layer._pointerEntities, 0, "No entities before layer is added"); - e.addComponent("DOM"); - _.strictEqual(layer._pointerEntities, 1, "Single entity after layer is added"); - }); + test("AreaMap's layer correctly tracks pointer entities on layer addition", function(_) { + var e = Crafty.e("2D, AreaMap, Color"); + var layer = Crafty.s("DefaultDOMLayer"); + _.strictEqual( + layer._pointerEntities, + 0, + "No entities before layer is added" + ); + e.addComponent("DOM"); + _.strictEqual( + layer._pointerEntities, + 1, + "Single entity after layer is added" + ); + }); // mock-phantom-touch-events is a PhantomJS plugin, thus the test below is skipped if enviroment is not PhantomJS if (navigator.userAgent.indexOf("PhantomJS") !== -1) - test('Multitouch simulation', function(_) { + test("Multitouch simulation", function(_) { Crafty.multitouch(true); - + var touchStartsOverEntities = 0, - touchEndsOverEntities = 0; - Crafty.e('2D, Renderable, DOM, Touch') - .setName('EntityA') - .attr({ x: 100, y: 100, w:200, h:200, z:1 }) - .bind('TouchOver',function() { - touchStartsOverEntities++; - }) - .bind('TouchOut',function() { - touchEndsOverEntities++; - }); - Crafty.e('2D, Renderable, DOM, Touch') - .setName('EntityB') - .attr({ x: 40, y: 150, w:90, h:300, z:2 }) - .bind('TouchOver',function() { - touchStartsOverEntities++; - }) - .bind('TouchOut',function() { - touchEndsOverEntities++; - }); + touchEndsOverEntities = 0; + Crafty.e("2D, Renderable, DOM, Touch") + .setName("EntityA") + .attr({ x: 100, y: 100, w: 200, h: 200, z: 1 }) + .bind("TouchOver", function() { + touchStartsOverEntities++; + }) + .bind("TouchOut", function() { + touchEndsOverEntities++; + }); + Crafty.e("2D, Renderable, DOM, Touch") + .setName("EntityB") + .attr({ x: 40, y: 150, w: 90, h: 300, z: 2 }) + .bind("TouchOver", function() { + touchStartsOverEntities++; + }) + .bind("TouchOut", function() { + touchEndsOverEntities++; + }); var elem = Crafty.stage.elem, - sx = Crafty.stage.x, - sy = Crafty.stage.y, - touchStart1 = createTouchEvent(elem, "touchstart", [[100 + sx, 80 + sy, 0], [150 + sx, 150 + sy, 1], [200 + sx, 50 + sy, 2], [65 + sx, 275 + sy, 3]]), - touchEnd1 = createTouchEvent(elem, "touchend", [[65 + sx, 275 + sy, 3]]), - touchEnd2 = createTouchEvent(elem, "touchend", [[200 + sx, 50 + sy, 2]]), - touchStart2 = createTouchEvent(elem, "touchstart", [[100 + sx, 80 + sy, 4]]), - touchEnd3 = createTouchEvent(elem, "touchend", [[150 + sx, 150 + sy, 1]]), - touchMove1 = createTouchEvent(elem, "touchmove", [[150 + sx, 150 + sy, 4]]), - touchEnd4 = createTouchEvent(elem, "touchend", [[100 + sx, 80 + sy, 0]]), - touchMove2 = createTouchEvent(elem, "touchmove", [[100 + sx, 80 + sy, 4]]), - touchEnd5 = createTouchEvent(elem, "touchend", [[100 + sx, 80 + sy, 4]]); + sx = Crafty.stage.x, + sy = Crafty.stage.y, + touchStart1 = createTouchEvent(elem, "touchstart", [ + [100 + sx, 80 + sy, 0], + [150 + sx, 150 + sy, 1], + [200 + sx, 50 + sy, 2], + [65 + sx, 275 + sy, 3] + ]), + touchEnd1 = createTouchEvent(elem, "touchend", [ + [65 + sx, 275 + sy, 3] + ]), + touchEnd2 = createTouchEvent(elem, "touchend", [ + [200 + sx, 50 + sy, 2] + ]), + touchStart2 = createTouchEvent(elem, "touchstart", [ + [100 + sx, 80 + sy, 4] + ]), + touchEnd3 = createTouchEvent(elem, "touchend", [ + [150 + sx, 150 + sy, 1] + ]), + touchMove1 = createTouchEvent(elem, "touchmove", [ + [150 + sx, 150 + sy, 4] + ]), + touchEnd4 = createTouchEvent(elem, "touchend", [ + [100 + sx, 80 + sy, 0] + ]), + touchMove2 = createTouchEvent(elem, "touchmove", [ + [100 + sx, 80 + sy, 4] + ]), + touchEnd5 = createTouchEvent(elem, "touchend", [ + [100 + sx, 80 + sy, 4] + ]); - var touchPoint, touchPoints = Crafty.s('Touch').touchPoints; + var touchPoint, + touchPoints = Crafty.s("Touch").touchPoints; /** touchStart1 **/ touchStart1(); - _.strictEqual(touchPoints.length, 4, "Four fingers currently touching stage"); + _.strictEqual( + touchPoints.length, + 4, + "Four fingers currently touching stage" + ); // id 0 touchPoint = touchPoints[0]; _.strictEqual(touchPoint.eventName, "TouchStart"); @@ -135,7 +193,11 @@ /** touchEnd1 **/ touchEnd1(); - _.strictEqual(touchPoints.length, 3, "Three fingers currently touching stage"); + _.strictEqual( + touchPoints.length, + 3, + "Three fingers currently touching stage" + ); // id 0 touchPoint = touchPoints[0]; _.strictEqual(touchPoint.eventName, "TouchStart"); @@ -160,7 +222,11 @@ /** touchEnd2 **/ touchEnd2(); - _.strictEqual(touchPoints.length, 2, "Two fingers currently touching stage"); + _.strictEqual( + touchPoints.length, + 2, + "Two fingers currently touching stage" + ); // id 0 touchPoint = touchPoints[0]; _.strictEqual(touchPoint.eventName, "TouchStart"); @@ -178,7 +244,11 @@ /** touchStart2 **/ touchStart2(); - _.strictEqual(touchPoints.length, 3, "Three fingers currently touching stage"); + _.strictEqual( + touchPoints.length, + 3, + "Three fingers currently touching stage" + ); // id 0 touchPoint = touchPoints[0]; _.strictEqual(touchPoint.eventName, "TouchStart"); @@ -203,7 +273,11 @@ /** touchEnd3 **/ touchEnd3(); - _.strictEqual(touchPoints.length, 2, "Two fingers currently touching stage"); + _.strictEqual( + touchPoints.length, + 2, + "Two fingers currently touching stage" + ); // id 0 touchPoint = touchPoints[0]; _.strictEqual(touchPoint.eventName, "TouchStart"); @@ -221,7 +295,11 @@ /** touchMove1 **/ touchMove1(); - _.strictEqual(touchPoints.length, 2, "Two fingers currently touching stage"); + _.strictEqual( + touchPoints.length, + 2, + "Two fingers currently touching stage" + ); // id 0 touchPoint = touchPoints[0]; _.strictEqual(touchPoint.eventName, "TouchStart"); @@ -239,7 +317,11 @@ /** touchEnd4 **/ touchEnd4(); - _.strictEqual(touchPoints.length, 1, "One finger currently touching stage"); + _.strictEqual( + touchPoints.length, + 1, + "One finger currently touching stage" + ); // id 4 touchPoint = touchPoints[0]; _.strictEqual(touchPoint.eventName, "TouchMove"); @@ -250,7 +332,11 @@ /** touchMove2 **/ touchMove2(); - _.strictEqual(touchPoints.length, 1, "One finger currently touching stage"); + _.strictEqual( + touchPoints.length, + 1, + "One finger currently touching stage" + ); // id 4 touchPoint = touchPoints[0]; _.strictEqual(touchPoint.eventName, "TouchMove"); @@ -261,36 +347,54 @@ /** touchEnd5 **/ touchEnd5(); - _.strictEqual(touchPoints.length, 0, "No fingers currently touching stage"); - - _.strictEqual(touchStartsOverEntities, 3, "Two entities received TouchStart, one received it twice"); - _.strictEqual(touchEndsOverEntities, 3, "Two entities received TouchEnd, one received it twice"); + _.strictEqual( + touchPoints.length, + 0, + "No fingers currently touching stage" + ); + + _.strictEqual( + touchStartsOverEntities, + 3, + "Two entities received TouchStart, one received it twice" + ); + _.strictEqual( + touchEndsOverEntities, + 3, + "Two entities received TouchEnd, one received it twice" + ); }); - - test("stopKeyPropagation", function(_) { - var stopPropCalled = false; - var preventDefaultCalled = false; - var mockEvent = { - char:"", charCode:"", keyCode:"", type:"", - shiftKey:"", ctrlKey:"", metaKey:"", timestamp:"", - target: document, - stopPropagation: function(){ - stopPropCalled = true; - }, - preventDefault: function(){ - preventDefaultCalled = true; - }, - cancelBubble: false, - returnValue: false, - }; + test("stopKeyPropagation", function(_) { + var stopPropCalled = false; + var preventDefaultCalled = false; - var origSelected = Crafty.selected; - Crafty.selected = true; - Crafty.s('Keyboard').processEvent(mockEvent); - Crafty.selected = origSelected; - - _.ok(stopPropCalled, "stopPropagation Not Called"); - _.ok(preventDefaultCalled, "preventDefault Not Called"); - }); + var mockEvent = { + char: "", + charCode: "", + keyCode: "", + type: "", + shiftKey: "", + ctrlKey: "", + metaKey: "", + timestamp: "", + target: document, + stopPropagation: function() { + stopPropCalled = true; + }, + preventDefault: function() { + preventDefaultCalled = true; + }, + cancelBubble: false, + returnValue: false + }; + + var origSelected = Crafty.selected; + Crafty.selected = true; + Crafty.s("Keyboard").processEvent(mockEvent); + Crafty.selected = origSelected; + + _.ok(stopPropCalled, "stopPropagation Not Called"); + _.ok(preventDefaultCalled, "preventDefault Not Called"); + }); })(); diff --git a/tests/unit/isometric/isometric.js b/tests/unit/isometric/isometric.js index eaa2f0ef..c9921236 100644 --- a/tests/unit/isometric/isometric.js +++ b/tests/unit/isometric/isometric.js @@ -24,13 +24,29 @@ iso.place(0, 0, 0, tile1); iso.place(1, 2, 5, tile2); - _.strictEqual(tile1.attr('x'), 0, "First tile should default to origin"); - _.strictEqual(tile1.attr('y'), 0, "First tile should default to origin"); - _.strictEqual(tile1.attr('z'), 0, "z-index should be transferred unchanged"); - - _.strictEqual(tile2.attr('x'), 64 + Crafty.viewport._x, "Each tile should be offset by the sum of the width of those before it"); - _.strictEqual(tile2.attr('y'), -24 + Crafty.viewport._y, "The row should be offset by one and a half times the height"); - _.strictEqual(tile2.attr('z'), 8, "z-index should be added to existing value"); + _.strictEqual(tile1.attr("x"), 0, "First tile should default to origin"); + _.strictEqual(tile1.attr("y"), 0, "First tile should default to origin"); + _.strictEqual( + tile1.attr("z"), + 0, + "z-index should be transferred unchanged" + ); + + _.strictEqual( + tile2.attr("x"), + 64 + Crafty.viewport._x, + "Each tile should be offset by the sum of the width of those before it" + ); + _.strictEqual( + tile2.attr("y"), + -24 + Crafty.viewport._y, + "The row should be offset by one and a half times the height" + ); + _.strictEqual( + tile2.attr("z"), + 8, + "z-index should be added to existing value" + ); // Clean up Crafty("*").destroy(); @@ -44,34 +60,82 @@ _.strictEqual(origin.top, 0, "First tile should default to origin"); var oddNumberedRow = iso.pos2px(0, 1); - _.strictEqual(oddNumberedRow.left, 32, "Odd numbered rows should be be inset by half the width"); - _.strictEqual(oddNumberedRow.top, 8, "Each row should move down by half the height"); + _.strictEqual( + oddNumberedRow.left, + 32, + "Odd numbered rows should be be inset by half the width" + ); + _.strictEqual( + oddNumberedRow.top, + 8, + "Each row should move down by half the height" + ); var evenNumberedRow = iso.pos2px(0, 2); - _.strictEqual(evenNumberedRow.left, 0, "Even numbered rows should not be be inset"); - _.strictEqual(evenNumberedRow.top, 16, "Each row should move down by half the height"); + _.strictEqual( + evenNumberedRow.left, + 0, + "Even numbered rows should not be be inset" + ); + _.strictEqual( + evenNumberedRow.top, + 16, + "Each row should move down by half the height" + ); var numberedColumn = iso.pos2px(3, 0); - _.strictEqual(numberedColumn.left, 64 * 3, "Should be inset by the width times the x position"); + _.strictEqual( + numberedColumn.left, + 64 * 3, + "Should be inset by the width times the x position" + ); }); test("px2pos", function(_) { var iso = Crafty.isometric.size(64, 16); var origin = iso.px2pos(0, 0); - _.strictEqual(origin.x, 0, "Origin should be the corner of the lowest numbered tile"); - _.strictEqual(origin.y, 0, "Origin should be the corner of the lowest numbered tile"); + _.strictEqual( + origin.x, + 0, + "Origin should be the corner of the lowest numbered tile" + ); + _.strictEqual( + origin.y, + 0, + "Origin should be the corner of the lowest numbered tile" + ); var oddNumberedRow = iso.px2pos(32, 8); - _.strictEqual(oddNumberedRow.x, 0, "Odd numbered rows should be be inset by half the width"); - _.strictEqual(oddNumberedRow.y, 1, "Each row should move down by half the height"); + _.strictEqual( + oddNumberedRow.x, + 0, + "Odd numbered rows should be be inset by half the width" + ); + _.strictEqual( + oddNumberedRow.y, + 1, + "Each row should move down by half the height" + ); var evenNumberedRow = iso.px2pos(0, 16); - _.strictEqual(evenNumberedRow.x, 0, "Even numbered rows should not be be inset"); - _.strictEqual(evenNumberedRow.y, 2, "Each row should move down by half the height"); + _.strictEqual( + evenNumberedRow.x, + 0, + "Even numbered rows should not be be inset" + ); + _.strictEqual( + evenNumberedRow.y, + 2, + "Each row should move down by half the height" + ); var numberedColumn = iso.px2pos(128, 0); - _.strictEqual(numberedColumn.x, 2, "Should be inset by the width times the x position"); + _.strictEqual( + numberedColumn.x, + 2, + "Should be inset by the width times the x position" + ); }); test("round trip conversions", function(_) { @@ -86,4 +150,4 @@ _.strictEqual(end.x, startX, "x position should match"); _.strictEqual(end.y, startY, "y position should match"); }); -})(); \ No newline at end of file +})(); diff --git a/tests/unit/lib/mockTouchEvents.js b/tests/unit/lib/mockTouchEvents.js index 851ec488..e605e86e 100644 --- a/tests/unit/lib/mockTouchEvents.js +++ b/tests/unit/lib/mockTouchEvents.js @@ -24,22 +24,25 @@ */ function computedStyle(el, prop) { - return ( - window.getComputedStyle ? window.getComputedStyle(el) : el.currentStyle - )[prop.replace(/-(\w)/gi, function (word, letter) { - return letter.toUpperCase(); - })]; + return (window.getComputedStyle + ? window.getComputedStyle(el) + : el.currentStyle)[ + prop.replace(/-(\w)/gi, function(word, letter) { + return letter.toUpperCase(); + }) + ]; } function getChildrenSize(container) { if (!container) { return; } - var children = [].slice.call(container.children, 0).filter(function (el) { - var pos = computedStyle(el, 'position'); + var children = [].slice.call(container.children, 0).filter(function(el) { + var pos = computedStyle(el, "position"); el.rect = el.getBoundingClientRect(); // store rect for later return !( - (pos === 'absolute' || pos === 'fixed') || + pos === "absolute" || + pos === "fixed" || (el.rect.width === 0 && el.rect.height === 0) ); }); @@ -50,14 +53,15 @@ function getChildrenSize(container) { }; } - var totRect = children.reduce(function (tot, el) { - return (!tot ? - el.rect : { - top: Math.min(tot.top, el.rect.top), - left: Math.min(tot.left, el.rect.left), - right: Math.max(tot.right, el.rect.right), - bottom: Math.max(tot.bottom, el.rect.bottom) - }); + var totRect = children.reduce(function(tot, el) { + return !tot + ? el.rect + : { + top: Math.min(tot.top, el.rect.top), + left: Math.min(tot.left, el.rect.left), + right: Math.max(tot.right, el.rect.right), + bottom: Math.max(tot.bottom, el.rect.bottom) + }; }, null); return { @@ -73,15 +77,19 @@ function createTouchList(target, list) { if (Array.isArray(list) && list[0] && !Array.isArray(list[0])) { list = [list]; } - list = list.map(function (entry, index) { - var x = entry[0], y = entry[1], id = entry[2] || index; + list = list.map(function(entry, index) { + var x = entry[0], + y = entry[1], + id = entry[2] || index; return createTouch(x, y, target, id); }); return document.createTouchList.apply(document, list); } function createTouch(x, y, target, id) { - return document.createTouch(window, target, + return document.createTouch( + window, + target, //identifier id || 0, //pageX / clientX @@ -129,14 +137,14 @@ function initTouchEvent(touchEvent, type, touches) { } function createTouchEvent(elem, type, touches) { - var touchEvent = document.createEvent('TouchEvent'); + var touchEvent = document.createEvent("TouchEvent"); if (Array.isArray(touches)) { touches = createTouchList(elem, touches); } function dispatch(getEvent) { initTouchEvent(touchEvent, type, touches); - if (typeof getEvent === 'function'){ + if (typeof getEvent === "function") { getEvent.call(elem, touchEvent, elem); } elem.dispatchEvent(touchEvent); @@ -150,22 +158,22 @@ function apply(fn, arg, args) { } function swipeLeft() { - return apply(swipe, 'left', arguments); + return apply(swipe, "left", arguments); } function swipeRight() { - return apply(swipe, 'right', arguments); + return apply(swipe, "right", arguments); } -function swipeTop(){ - return apply(swipe, 'top', arguments); +function swipeTop() { + return apply(swipe, "top", arguments); } -function swipeBottom(){ - return apply(swipe, 'bottom', arguments); +function swipeBottom() { + return apply(swipe, "bottom", arguments); } -function round(num){ +function round(num) { return Math.round(num); } @@ -178,72 +186,67 @@ function swipe(direction, elem, ms, frames, getEvent) { var y; var from; var to; - var isVertical = direction === 'top' || direction === 'bottom'; - if (isVertical){ + var isVertical = direction === "top" || direction === "bottom"; + if (isVertical) { y = elemSize.height; x = elemSize.width / 2; - from = [x*0.95, VERTICAL_OFFSET].map(round); - to = [x*1.01, y-VERTICAL_OFFSET].map(round); + from = [x * 0.95, VERTICAL_OFFSET].map(round); + to = [x * 1.01, y - VERTICAL_OFFSET].map(round); } else { // horizontal x = elemSize.width; y = elemSize.height / 2; - from = [HORIZONTAL_OFFSET, y*0.98].map(round); - to = [x - HORIZONTAL_OFFSET, y*1.01].map(round); + from = [HORIZONTAL_OFFSET, y * 0.98].map(round); + to = [x - HORIZONTAL_OFFSET, y * 1.01].map(round); } - if (direction === 'right' || direction === 'top') { + if (direction === "right" || direction === "top") { touchActionSequence(elem, from, to, ms, frames, getEvent); } else { touchActionSequence(elem, to, from, ms, frames, getEvent); } } -function getDiff(fromList, toList){ - return [ - toList[0] - fromList[0], - toList[1] - fromList[1] - ]; +function getDiff(fromList, toList) { + return [toList[0] - fromList[0], toList[1] - fromList[1]]; } -function getXandYFrame(startPoint, diffToWalk, currentProgress){ +function getXandYFrame(startPoint, diffToWalk, currentProgress) { return [ - Math.round( - Math.abs( - startPoint[0] + (diffToWalk[0] * currentProgress))), - Math.round( - Math.abs( - startPoint[1] + (diffToWalk[1] * currentProgress))) + Math.round(Math.abs(startPoint[0] + diffToWalk[0] * currentProgress)), + Math.round(Math.abs(startPoint[1] + diffToWalk[1] * currentProgress)) ]; } function touchActionSequence(elem, fromXandY, toXandY, ms, frames, getEvent) { - frames = frames || 10; - ms = Math.round((ms||1000) / frames); + frames = frames || 10; + ms = Math.round((ms || 1000) / frames); // lets find difference from start to end and divide on frames - var diff = getDiff(fromXandY, toXandY); - var counter = frames; - var pos = getXandYFrame(fromXandY, diff, counter/frames); + var diff = getDiff(fromXandY, toXandY); + var counter = frames; + var pos = getXandYFrame(fromXandY, diff, counter / frames); var targetElement; - targetElement = document.elementFromPoint(pos[0], pos[1]); + targetElement = document.elementFromPoint(pos[0], pos[1]); setTimeout(function handler() { counter--; if (counter) { - pos = getXandYFrame(fromXandY, diff, counter/frames); + pos = getXandYFrame(fromXandY, diff, counter / frames); targetElement = document.elementFromPoint(pos[0], pos[1]); - createTouchEvent(targetElement||elem, 'touchmove', pos)(getEvent); + createTouchEvent(targetElement || elem, "touchmove", pos)(getEvent); setTimeout(handler, ms); } else { - createTouchEvent(targetElement||elem, 'touchend', [[0, 0]])(getEvent); + createTouchEvent(targetElement || elem, "touchend", [[0, 0]])( + getEvent + ); } }, ms); - createTouchEvent(targetElement||elem, 'touchstart', pos)(getEvent); + createTouchEvent(targetElement || elem, "touchstart", pos)(getEvent); } -function factory(){ +function factory() { return { _apply: apply, _getXandYFrame: getXandYFrame, @@ -257,7 +260,7 @@ function factory(){ }; } -if (typeof module !== 'undefined' && module.exports){ +if (typeof module !== "undefined" && module.exports) { module.exports = factory(); } else { window.mockPhantomTouchEvents = factory(); diff --git a/tests/unit/lib/qunit.js b/tests/unit/lib/qunit.js index bdba631f..75237deb 100644 --- a/tests/unit/lib/qunit.js +++ b/tests/unit/lib/qunit.js @@ -8,10 +8,9 @@ * * Date: 2017-10-22T05:12Z */ -(function (global$1) { - 'use strict'; - - global$1 = 'default' in global$1 ? global$1['default'] : global$1; +(function(global$1) { + "use strict"; + global$1 = "default" in global$1 ? global$1["default"] : global$1; var window = global$1.window; var self$1 = global$1.self; @@ -22,40 +21,38 @@ var document = window && window.document; var navigator = window && window.navigator; - var localSessionStorage = function () { - var x = "qunit-test-string"; - try { - global$1.sessionStorage.setItem(x, x); - global$1.sessionStorage.removeItem(x); - return global$1.sessionStorage; - } catch (e) { - return undefined; - } - }(); - - var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { - return typeof obj; - } : function (obj) { - return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; - }; - - - - - - - - - - + var localSessionStorage = (function() { + var x = "qunit-test-string"; + try { + global$1.sessionStorage.setItem(x, x); + global$1.sessionStorage.removeItem(x); + return global$1.sessionStorage; + } catch (e) { + return undefined; + } + })(); - var classCallCheck = function (instance, Constructor) { + var _typeof = + typeof Symbol === "function" && typeof Symbol.iterator === "symbol" + ? function(obj) { + return typeof obj; + } + : function(obj) { + return obj && + typeof Symbol === "function" && + obj.constructor === Symbol && + obj !== Symbol.prototype + ? "symbol" + : typeof obj; + }; + + var classCallCheck = function(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; - var createClass = function () { + var createClass = (function() { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; @@ -66,56 +63,17 @@ } } - return function (Constructor, protoProps, staticProps) { + return function(Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; - }(); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + })(); - var toConsumableArray = function (arr) { + var toConsumableArray = function(arr) { if (Array.isArray(arr)) { - for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; + for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) + arr2[i] = arr[i]; return arr2; } else { @@ -125,31 +83,33 @@ var toString = Object.prototype.toString; var hasOwn = Object.prototype.hasOwnProperty; - var now = Date.now || function () { - return new Date().getTime(); - }; + var now = + Date.now || + function() { + return new Date().getTime(); + }; var defined = { - document: window && window.document !== undefined, - setTimeout: setTimeout !== undefined + document: window && window.document !== undefined, + setTimeout: setTimeout !== undefined }; // Returns a new Array with the elements that are in a but not in b function diff(a, b) { - var i, - j, - result = a.slice(); - - for (i = 0; i < result.length; i++) { - for (j = 0; j < b.length; j++) { - if (result[i] === b[j]) { - result.splice(i, 1); - i--; - break; - } - } - } - return result; + var i, + j, + result = a.slice(); + + for (i = 0; i < result.length; i++) { + for (j = 0; j < b.length; j++) { + if (result[i] === b[j]) { + result.splice(i, 1); + i--; + break; + } + } + } + return result; } /** @@ -161,7 +121,7 @@ * @return {Boolean} */ function inArray(elem, array) { - return array.indexOf(elem) !== -1; + return array.indexOf(elem) !== -1; } /** @@ -172,487 +132,493 @@ * @return {Object} New object with only the own properties (recursively). */ function objectValues(obj) { - var key, - val, - vals = is("array", obj) ? [] : {}; - for (key in obj) { - if (hasOwn.call(obj, key)) { - val = obj[key]; - vals[key] = val === Object(val) ? objectValues(val) : val; - } - } - return vals; + var key, + val, + vals = is("array", obj) ? [] : {}; + for (key in obj) { + if (hasOwn.call(obj, key)) { + val = obj[key]; + vals[key] = val === Object(val) ? objectValues(val) : val; + } + } + return vals; } function extend(a, b, undefOnly) { - for (var prop in b) { - if (hasOwn.call(b, prop)) { - if (b[prop] === undefined) { - delete a[prop]; - } else if (!(undefOnly && typeof a[prop] !== "undefined")) { - a[prop] = b[prop]; - } - } - } - - return a; + for (var prop in b) { + if (hasOwn.call(b, prop)) { + if (b[prop] === undefined) { + delete a[prop]; + } else if (!(undefOnly && typeof a[prop] !== "undefined")) { + a[prop] = b[prop]; + } + } + } + + return a; } function objectType(obj) { - if (typeof obj === "undefined") { - return "undefined"; - } - - // Consider: typeof null === object - if (obj === null) { - return "null"; - } - - var match = toString.call(obj).match(/^\[object\s(.*)\]$/), - type = match && match[1]; - - switch (type) { - case "Number": - if (isNaN(obj)) { - return "nan"; - } - return "number"; - case "String": - case "Boolean": - case "Array": - case "Set": - case "Map": - case "Date": - case "RegExp": - case "Function": - case "Symbol": - return type.toLowerCase(); - default: - return typeof obj === "undefined" ? "undefined" : _typeof(obj); - } + if (typeof obj === "undefined") { + return "undefined"; + } + + // Consider: typeof null === object + if (obj === null) { + return "null"; + } + + var match = toString.call(obj).match(/^\[object\s(.*)\]$/), + type = match && match[1]; + + switch (type) { + case "Number": + if (isNaN(obj)) { + return "nan"; + } + return "number"; + case "String": + case "Boolean": + case "Array": + case "Set": + case "Map": + case "Date": + case "RegExp": + case "Function": + case "Symbol": + return type.toLowerCase(); + default: + return typeof obj === "undefined" ? "undefined" : _typeof(obj); + } } // Safe object type checking function is(type, obj) { - return objectType(obj) === type; + return objectType(obj) === type; } // Based on Java's String.hashCode, a simple but not // rigorously collision resistant hashing function function generateHash(module, testName) { - var str = module + "\x1C" + testName; - var hash = 0; - - for (var i = 0; i < str.length; i++) { - hash = (hash << 5) - hash + str.charCodeAt(i); - hash |= 0; - } - - // Convert the possibly negative integer hash code into an 8 character hex string, which isn't - // strictly necessary but increases user understanding that the id is a SHA-like hash - var hex = (0x100000000 + hash).toString(16); - if (hex.length < 8) { - hex = "0000000" + hex; - } - - return hex.slice(-8); + var str = module + "\x1C" + testName; + var hash = 0; + + for (var i = 0; i < str.length; i++) { + hash = (hash << 5) - hash + str.charCodeAt(i); + hash |= 0; + } + + // Convert the possibly negative integer hash code into an 8 character hex string, which isn't + // strictly necessary but increases user understanding that the id is a SHA-like hash + var hex = (0x100000000 + hash).toString(16); + if (hex.length < 8) { + hex = "0000000" + hex; + } + + return hex.slice(-8); } // Test for equality any JavaScript type. // Authors: Philippe Rathé , David Chan - var equiv = (function () { - - // Value pairs queued for comparison. Used for breadth-first processing order, recursion - // detection and avoiding repeated comparison (see below for details). - // Elements are { a: val, b: val }. - var pairs = []; - - var getProto = Object.getPrototypeOf || function (obj) { - return obj.__proto__; - }; - - function useStrictEquality(a, b) { - - // This only gets called if a and b are not strict equal, and is used to compare on - // the primitive values inside object wrappers. For example: - // `var i = 1;` - // `var j = new Number(1);` - // Neither a nor b can be null, as a !== b and they have the same type. - if ((typeof a === "undefined" ? "undefined" : _typeof(a)) === "object") { - a = a.valueOf(); - } - if ((typeof b === "undefined" ? "undefined" : _typeof(b)) === "object") { - b = b.valueOf(); - } - - return a === b; - } - - function compareConstructors(a, b) { - var protoA = getProto(a); - var protoB = getProto(b); - - // Comparing constructors is more strict than using `instanceof` - if (a.constructor === b.constructor) { - return true; - } - - // Ref #851 - // If the obj prototype descends from a null constructor, treat it - // as a null prototype. - if (protoA && protoA.constructor === null) { - protoA = null; - } - if (protoB && protoB.constructor === null) { - protoB = null; - } - - // Allow objects with no prototype to be equivalent to - // objects with Object as their constructor. - if (protoA === null && protoB === Object.prototype || protoB === null && protoA === Object.prototype) { - return true; - } - - return false; - } - - function getRegExpFlags(regexp) { - return "flags" in regexp ? regexp.flags : regexp.toString().match(/[gimuy]*$/)[0]; - } - - function isContainer(val) { - return ["object", "array", "map", "set"].indexOf(objectType(val)) !== -1; - } - - function breadthFirstCompareChild(a, b) { - - // If a is a container not reference-equal to b, postpone the comparison to the - // end of the pairs queue -- unless (a, b) has been seen before, in which case skip - // over the pair. - if (a === b) { - return true; - } - if (!isContainer(a)) { - return typeEquiv(a, b); - } - if (pairs.every(function (pair) { - return pair.a !== a || pair.b !== b; - })) { - - // Not yet started comparing this pair - pairs.push({ a: a, b: b }); - } - return true; - } - - var callbacks = { - "string": useStrictEquality, - "boolean": useStrictEquality, - "number": useStrictEquality, - "null": useStrictEquality, - "undefined": useStrictEquality, - "symbol": useStrictEquality, - "date": useStrictEquality, - - "nan": function nan() { - return true; - }, - - "regexp": function regexp(a, b) { - return a.source === b.source && - - // Include flags in the comparison - getRegExpFlags(a) === getRegExpFlags(b); - }, - - // abort (identical references / instance methods were skipped earlier) - "function": function _function() { - return false; - }, - - "array": function array(a, b) { - var i, len; - - len = a.length; - if (len !== b.length) { - - // Safe and faster - return false; - } - - for (i = 0; i < len; i++) { - - // Compare non-containers; queue non-reference-equal containers - if (!breadthFirstCompareChild(a[i], b[i])) { - return false; - } - } - return true; - }, - - // Define sets a and b to be equivalent if for each element aVal in a, there - // is some element bVal in b such that aVal and bVal are equivalent. Element - // repetitions are not counted, so these are equivalent: - // a = new Set( [ {}, [], [] ] ); - // b = new Set( [ {}, {}, [] ] ); - "set": function set$$1(a, b) { - var innerEq, - outerEq = true; - - if (a.size !== b.size) { - - // This optimization has certain quirks because of the lack of - // repetition counting. For instance, adding the same - // (reference-identical) element to two equivalent sets can - // make them non-equivalent. - return false; - } - - a.forEach(function (aVal) { - - // Short-circuit if the result is already known. (Using for...of - // with a break clause would be cleaner here, but it would cause - // a syntax error on older Javascript implementations even if - // Set is unused) - if (!outerEq) { - return; - } - - innerEq = false; - - b.forEach(function (bVal) { - var parentPairs; - - // Likewise, short-circuit if the result is already known - if (innerEq) { - return; - } - - // Swap out the global pairs list, as the nested call to - // innerEquiv will clobber its contents - parentPairs = pairs; - if (innerEquiv(bVal, aVal)) { - innerEq = true; - } - - // Replace the global pairs list - pairs = parentPairs; - }); - - if (!innerEq) { - outerEq = false; - } - }); - - return outerEq; - }, - - // Define maps a and b to be equivalent if for each key-value pair (aKey, aVal) - // in a, there is some key-value pair (bKey, bVal) in b such that - // [ aKey, aVal ] and [ bKey, bVal ] are equivalent. Key repetitions are not - // counted, so these are equivalent: - // a = new Map( [ [ {}, 1 ], [ {}, 1 ], [ [], 1 ] ] ); - // b = new Map( [ [ {}, 1 ], [ [], 1 ], [ [], 1 ] ] ); - "map": function map(a, b) { - var innerEq, - outerEq = true; - - if (a.size !== b.size) { - - // This optimization has certain quirks because of the lack of - // repetition counting. For instance, adding the same - // (reference-identical) key-value pair to two equivalent maps - // can make them non-equivalent. - return false; - } - - a.forEach(function (aVal, aKey) { - - // Short-circuit if the result is already known. (Using for...of - // with a break clause would be cleaner here, but it would cause - // a syntax error on older Javascript implementations even if - // Map is unused) - if (!outerEq) { - return; - } - - innerEq = false; - - b.forEach(function (bVal, bKey) { - var parentPairs; - - // Likewise, short-circuit if the result is already known - if (innerEq) { - return; - } - - // Swap out the global pairs list, as the nested call to - // innerEquiv will clobber its contents - parentPairs = pairs; - if (innerEquiv([bVal, bKey], [aVal, aKey])) { - innerEq = true; - } - - // Replace the global pairs list - pairs = parentPairs; - }); - - if (!innerEq) { - outerEq = false; - } - }); - - return outerEq; - }, - - "object": function object(a, b) { - var i, - aProperties = [], - bProperties = []; - - if (compareConstructors(a, b) === false) { - return false; - } - - // Be strict: don't ensure hasOwnProperty and go deep - for (i in a) { - - // Collect a's properties - aProperties.push(i); - - // Skip OOP methods that look the same - if (a.constructor !== Object && typeof a.constructor !== "undefined" && typeof a[i] === "function" && typeof b[i] === "function" && a[i].toString() === b[i].toString()) { - continue; - } - - // Compare non-containers; queue non-reference-equal containers - if (!breadthFirstCompareChild(a[i], b[i])) { - return false; - } - } - - for (i in b) { - - // Collect b's properties - bProperties.push(i); - } - - // Ensures identical properties name - return typeEquiv(aProperties.sort(), bProperties.sort()); - } - }; - - function typeEquiv(a, b) { - var type = objectType(a); - - // Callbacks for containers will append to the pairs queue to achieve breadth-first - // search order. The pairs queue is also used to avoid reprocessing any pair of - // containers that are reference-equal to a previously visited pair (a special case - // this being recursion detection). - // - // Because of this approach, once typeEquiv returns a false value, it should not be - // called again without clearing the pair queue else it may wrongly report a visited - // pair as being equivalent. - return objectType(b) === type && callbacks[type](a, b); - } - - function innerEquiv(a, b) { - var i, pair; - - // We're done when there's nothing more to compare - if (arguments.length < 2) { - return true; - } - - // Clear the global pair queue and add the top-level values being compared - pairs = [{ a: a, b: b }]; - - for (i = 0; i < pairs.length; i++) { - pair = pairs[i]; - - // Perform type-specific comparison on any pairs that are not strictly - // equal. For container types, that comparison will postpone comparison - // of any sub-container pair to the end of the pair queue. This gives - // breadth-first search order. It also avoids the reprocessing of - // reference-equal siblings, cousins etc, which can have a significant speed - // impact when comparing a container of small objects each of which has a - // reference to the same (singleton) large object. - if (pair.a !== pair.b && !typeEquiv(pair.a, pair.b)) { - return false; - } - } - - // ...across all consecutive argument pairs - return arguments.length === 2 || innerEquiv.apply(this, [].slice.call(arguments, 1)); - } - - return function () { - var result = innerEquiv.apply(undefined, arguments); - - // Release any retained objects - pairs.length = 0; - return result; - }; - })(); + var equiv = (function() { + // Value pairs queued for comparison. Used for breadth-first processing order, recursion + // detection and avoiding repeated comparison (see below for details). + // Elements are { a: val, b: val }. + var pairs = []; + + var getProto = + Object.getPrototypeOf || + function(obj) { + return obj.__proto__; + }; + + function useStrictEquality(a, b) { + // This only gets called if a and b are not strict equal, and is used to compare on + // the primitive values inside object wrappers. For example: + // `var i = 1;` + // `var j = new Number(1);` + // Neither a nor b can be null, as a !== b and they have the same type. + if ((typeof a === "undefined" ? "undefined" : _typeof(a)) === "object") { + a = a.valueOf(); + } + if ((typeof b === "undefined" ? "undefined" : _typeof(b)) === "object") { + b = b.valueOf(); + } - /** - * Config object: Maintain internal state - * Later exposed as QUnit.config - * `config` initialized at top of scope - */ - var config = { + return a === b; + } + + function compareConstructors(a, b) { + var protoA = getProto(a); + var protoB = getProto(b); + + // Comparing constructors is more strict than using `instanceof` + if (a.constructor === b.constructor) { + return true; + } + + // Ref #851 + // If the obj prototype descends from a null constructor, treat it + // as a null prototype. + if (protoA && protoA.constructor === null) { + protoA = null; + } + if (protoB && protoB.constructor === null) { + protoB = null; + } + + // Allow objects with no prototype to be equivalent to + // objects with Object as their constructor. + if ( + (protoA === null && protoB === Object.prototype) || + (protoB === null && protoA === Object.prototype) + ) { + return true; + } + + return false; + } + + function getRegExpFlags(regexp) { + return "flags" in regexp + ? regexp.flags + : regexp.toString().match(/[gimuy]*$/)[0]; + } - // The queue of tests to run - queue: [], + function isContainer(val) { + return ["object", "array", "map", "set"].indexOf(objectType(val)) !== -1; + } - // Block until document ready - blocking: true, + function breadthFirstCompareChild(a, b) { + // If a is a container not reference-equal to b, postpone the comparison to the + // end of the pairs queue -- unless (a, b) has been seen before, in which case skip + // over the pair. + if (a === b) { + return true; + } + if (!isContainer(a)) { + return typeEquiv(a, b); + } + if ( + pairs.every(function(pair) { + return pair.a !== a || pair.b !== b; + }) + ) { + // Not yet started comparing this pair + pairs.push({ a: a, b: b }); + } + return true; + } - // By default, run previously failed tests first - // very useful in combination with "Hide passed tests" checked - reorder: true, + var callbacks = { + string: useStrictEquality, + boolean: useStrictEquality, + number: useStrictEquality, + null: useStrictEquality, + undefined: useStrictEquality, + symbol: useStrictEquality, + date: useStrictEquality, + + nan: function nan() { + return true; + }, + + regexp: function regexp(a, b) { + return ( + a.source === b.source && + // Include flags in the comparison + getRegExpFlags(a) === getRegExpFlags(b) + ); + }, + + // abort (identical references / instance methods were skipped earlier) + function: function _function() { + return false; + }, + + array: function array(a, b) { + var i, len; + + len = a.length; + if (len !== b.length) { + // Safe and faster + return false; + } + + for (i = 0; i < len; i++) { + // Compare non-containers; queue non-reference-equal containers + if (!breadthFirstCompareChild(a[i], b[i])) { + return false; + } + } + return true; + }, + + // Define sets a and b to be equivalent if for each element aVal in a, there + // is some element bVal in b such that aVal and bVal are equivalent. Element + // repetitions are not counted, so these are equivalent: + // a = new Set( [ {}, [], [] ] ); + // b = new Set( [ {}, {}, [] ] ); + set: function set$$1(a, b) { + var innerEq, + outerEq = true; + + if (a.size !== b.size) { + // This optimization has certain quirks because of the lack of + // repetition counting. For instance, adding the same + // (reference-identical) element to two equivalent sets can + // make them non-equivalent. + return false; + } + + a.forEach(function(aVal) { + // Short-circuit if the result is already known. (Using for...of + // with a break clause would be cleaner here, but it would cause + // a syntax error on older Javascript implementations even if + // Set is unused) + if (!outerEq) { + return; + } + + innerEq = false; + + b.forEach(function(bVal) { + var parentPairs; + + // Likewise, short-circuit if the result is already known + if (innerEq) { + return; + } + + // Swap out the global pairs list, as the nested call to + // innerEquiv will clobber its contents + parentPairs = pairs; + if (innerEquiv(bVal, aVal)) { + innerEq = true; + } + + // Replace the global pairs list + pairs = parentPairs; + }); + + if (!innerEq) { + outerEq = false; + } + }); + + return outerEq; + }, + + // Define maps a and b to be equivalent if for each key-value pair (aKey, aVal) + // in a, there is some key-value pair (bKey, bVal) in b such that + // [ aKey, aVal ] and [ bKey, bVal ] are equivalent. Key repetitions are not + // counted, so these are equivalent: + // a = new Map( [ [ {}, 1 ], [ {}, 1 ], [ [], 1 ] ] ); + // b = new Map( [ [ {}, 1 ], [ [], 1 ], [ [], 1 ] ] ); + map: function map(a, b) { + var innerEq, + outerEq = true; + + if (a.size !== b.size) { + // This optimization has certain quirks because of the lack of + // repetition counting. For instance, adding the same + // (reference-identical) key-value pair to two equivalent maps + // can make them non-equivalent. + return false; + } + + a.forEach(function(aVal, aKey) { + // Short-circuit if the result is already known. (Using for...of + // with a break clause would be cleaner here, but it would cause + // a syntax error on older Javascript implementations even if + // Map is unused) + if (!outerEq) { + return; + } + + innerEq = false; + + b.forEach(function(bVal, bKey) { + var parentPairs; + + // Likewise, short-circuit if the result is already known + if (innerEq) { + return; + } + + // Swap out the global pairs list, as the nested call to + // innerEquiv will clobber its contents + parentPairs = pairs; + if (innerEquiv([bVal, bKey], [aVal, aKey])) { + innerEq = true; + } + + // Replace the global pairs list + pairs = parentPairs; + }); + + if (!innerEq) { + outerEq = false; + } + }); + + return outerEq; + }, + + object: function object(a, b) { + var i, + aProperties = [], + bProperties = []; + + if (compareConstructors(a, b) === false) { + return false; + } + + // Be strict: don't ensure hasOwnProperty and go deep + for (i in a) { + // Collect a's properties + aProperties.push(i); + + // Skip OOP methods that look the same + if ( + a.constructor !== Object && + typeof a.constructor !== "undefined" && + typeof a[i] === "function" && + typeof b[i] === "function" && + a[i].toString() === b[i].toString() + ) { + continue; + } + + // Compare non-containers; queue non-reference-equal containers + if (!breadthFirstCompareChild(a[i], b[i])) { + return false; + } + } + + for (i in b) { + // Collect b's properties + bProperties.push(i); + } + + // Ensures identical properties name + return typeEquiv(aProperties.sort(), bProperties.sort()); + } + }; - // By default, modify document.title when suite is done - altertitle: true, + function typeEquiv(a, b) { + var type = objectType(a); + + // Callbacks for containers will append to the pairs queue to achieve breadth-first + // search order. The pairs queue is also used to avoid reprocessing any pair of + // containers that are reference-equal to a previously visited pair (a special case + // this being recursion detection). + // + // Because of this approach, once typeEquiv returns a false value, it should not be + // called again without clearing the pair queue else it may wrongly report a visited + // pair as being equivalent. + return objectType(b) === type && callbacks[type](a, b); + } - // HTML Reporter: collapse every test except the first failing test - // If false, all failing tests will be expanded - collapse: true, + function innerEquiv(a, b) { + var i, pair; - // By default, scroll to top of the page when suite is done - scrolltop: true, + // We're done when there's nothing more to compare + if (arguments.length < 2) { + return true; + } - // Depth up-to which object will be dumped - maxDepth: 5, + // Clear the global pair queue and add the top-level values being compared + pairs = [{ a: a, b: b }]; + + for (i = 0; i < pairs.length; i++) { + pair = pairs[i]; + + // Perform type-specific comparison on any pairs that are not strictly + // equal. For container types, that comparison will postpone comparison + // of any sub-container pair to the end of the pair queue. This gives + // breadth-first search order. It also avoids the reprocessing of + // reference-equal siblings, cousins etc, which can have a significant speed + // impact when comparing a container of small objects each of which has a + // reference to the same (singleton) large object. + if (pair.a !== pair.b && !typeEquiv(pair.a, pair.b)) { + return false; + } + } - // When enabled, all tests must call expect() - requireExpects: false, + // ...across all consecutive argument pairs + return ( + arguments.length === 2 || + innerEquiv.apply(this, [].slice.call(arguments, 1)) + ); + } - // Placeholder for user-configurable form-exposed URL parameters - urlConfig: [], + return function() { + var result = innerEquiv.apply(undefined, arguments); - // Set of all modules. - modules: [], + // Release any retained objects + pairs.length = 0; + return result; + }; + })(); - // The first unnamed module - currentModule: { - name: "", - tests: [], - childModules: [], - testsRun: 0, - unskippedTestsRun: 0, - hooks: { - before: [], - beforeEach: [], - afterEach: [], - after: [] - } - }, + /** + * Config object: Maintain internal state + * Later exposed as QUnit.config + * `config` initialized at top of scope + */ + var config = { + // The queue of tests to run + queue: [], + + // Block until document ready + blocking: true, + + // By default, run previously failed tests first + // very useful in combination with "Hide passed tests" checked + reorder: true, + + // By default, modify document.title when suite is done + altertitle: true, + + // HTML Reporter: collapse every test except the first failing test + // If false, all failing tests will be expanded + collapse: true, + + // By default, scroll to top of the page when suite is done + scrolltop: true, + + // Depth up-to which object will be dumped + maxDepth: 5, + + // When enabled, all tests must call expect() + requireExpects: false, + + // Placeholder for user-configurable form-exposed URL parameters + urlConfig: [], + + // Set of all modules. + modules: [], + + // The first unnamed module + currentModule: { + name: "", + tests: [], + childModules: [], + testsRun: 0, + unskippedTestsRun: 0, + hooks: { + before: [], + beforeEach: [], + afterEach: [], + after: [] + } + }, - callbacks: {}, + callbacks: {}, - // The storage module to use for reordering tests - storage: localSessionStorage + // The storage module to use for reordering tests + storage: localSessionStorage }; // take a predefined QUnit.config and extend the defaults @@ -660,7 +626,7 @@ // only extend the global config if there is no QUnit overload if (window && window.QUnit && !window.QUnit.version) { - extend(config, globalConfig); + extend(config, globalConfig); } // Push a loose unnamed module to the modules collection @@ -668,290 +634,316 @@ // Based on jsDump by Ariel Flesler // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html - var dump = (function () { - function quote(str) { - return "\"" + str.toString().replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\""; - } - function literal(o) { - return o + ""; - } - function join(pre, arr, post) { - var s = dump.separator(), - base = dump.indent(), - inner = dump.indent(1); - if (arr.join) { - arr = arr.join("," + s + inner); - } - if (!arr) { - return pre + post; - } - return [pre, inner + arr, base + post].join(s); - } - function array(arr, stack) { - var i = arr.length, - ret = new Array(i); - - if (dump.maxDepth && dump.depth > dump.maxDepth) { - return "[object Array]"; - } - - this.up(); - while (i--) { - ret[i] = this.parse(arr[i], undefined, stack); - } - this.down(); - return join("[", ret, "]"); - } - - function isArray(obj) { - return ( - - //Native Arrays - toString.call(obj) === "[object Array]" || - - // NodeList objects - typeof obj.length === "number" && obj.item !== undefined && (obj.length ? obj.item(0) === obj[0] : obj.item(0) === null && obj[0] === undefined) - ); - } - - var reName = /^function (\w+)/, - dump = { - - // The objType is used mostly internally, you can fix a (custom) type in advance - parse: function parse(obj, objType, stack) { - stack = stack || []; - var res, - parser, - parserType, - objIndex = stack.indexOf(obj); - - if (objIndex !== -1) { - return "recursion(" + (objIndex - stack.length) + ")"; - } - - objType = objType || this.typeOf(obj); - parser = this.parsers[objType]; - parserType = typeof parser === "undefined" ? "undefined" : _typeof(parser); - - if (parserType === "function") { - stack.push(obj); - res = parser.call(this, obj, stack); - stack.pop(); - return res; - } - return parserType === "string" ? parser : this.parsers.error; - }, - typeOf: function typeOf(obj) { - var type; - - if (obj === null) { - type = "null"; - } else if (typeof obj === "undefined") { - type = "undefined"; - } else if (is("regexp", obj)) { - type = "regexp"; - } else if (is("date", obj)) { - type = "date"; - } else if (is("function", obj)) { - type = "function"; - } else if (obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined) { - type = "window"; - } else if (obj.nodeType === 9) { - type = "document"; - } else if (obj.nodeType) { - type = "node"; - } else if (isArray(obj)) { - type = "array"; - } else if (obj.constructor === Error.prototype.constructor) { - type = "error"; - } else { - type = typeof obj === "undefined" ? "undefined" : _typeof(obj); - } - return type; - }, - - separator: function separator() { - if (this.multiline) { - return this.HTML ? "
" : "\n"; - } else { - return this.HTML ? " " : " "; - } - }, - - // Extra can be a number, shortcut for increasing-calling-decreasing - indent: function indent(extra) { - if (!this.multiline) { - return ""; - } - var chr = this.indentChar; - if (this.HTML) { - chr = chr.replace(/\t/g, " ").replace(/ /g, " "); - } - return new Array(this.depth + (extra || 0)).join(chr); - }, - up: function up(a) { - this.depth += a || 1; - }, - down: function down(a) { - this.depth -= a || 1; - }, - setParser: function setParser(name, parser) { - this.parsers[name] = parser; - }, - - // The next 3 are exposed so you can use them - quote: quote, - literal: literal, - join: join, - depth: 1, - maxDepth: config.maxDepth, - - // This is the list of parsers, to modify them, use dump.setParser - parsers: { - window: "[Window]", - document: "[Document]", - error: function error(_error) { - return "Error(\"" + _error.message + "\")"; - }, - unknown: "[Unknown]", - "null": "null", - "undefined": "undefined", - "function": function _function(fn) { - var ret = "function", - - - // Functions never have name in IE - name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1]; - - if (name) { - ret += " " + name; - } - ret += "("; - - ret = [ret, dump.parse(fn, "functionArgs"), "){"].join(""); - return join(ret, dump.parse(fn, "functionCode"), "}"); - }, - array: array, - nodelist: array, - "arguments": array, - object: function object(map, stack) { - var keys, - key, - val, - i, - nonEnumerableProperties, - ret = []; - - if (dump.maxDepth && dump.depth > dump.maxDepth) { - return "[object Object]"; - } - - dump.up(); - keys = []; - for (key in map) { - keys.push(key); - } - - // Some properties are not always enumerable on Error objects. - nonEnumerableProperties = ["message", "name"]; - for (i in nonEnumerableProperties) { - key = nonEnumerableProperties[i]; - if (key in map && !inArray(key, keys)) { - keys.push(key); - } - } - keys.sort(); - for (i = 0; i < keys.length; i++) { - key = keys[i]; - val = map[key]; - ret.push(dump.parse(key, "key") + ": " + dump.parse(val, undefined, stack)); - } - dump.down(); - return join("{", ret, "}"); - }, - node: function node(_node) { - var len, - i, - val, - open = dump.HTML ? "<" : "<", - close = dump.HTML ? ">" : ">", - tag = _node.nodeName.toLowerCase(), - ret = open + tag, - attrs = _node.attributes; - - if (attrs) { - for (i = 0, len = attrs.length; i < len; i++) { - val = attrs[i].nodeValue; - - // IE6 includes all attributes in .attributes, even ones not explicitly - // set. Those have values like undefined, null, 0, false, "" or - // "inherit". - if (val && val !== "inherit") { - ret += " " + attrs[i].nodeName + "=" + dump.parse(val, "attribute"); - } - } - } - ret += close; - - // Show content of TextNode or CDATASection - if (_node.nodeType === 3 || _node.nodeType === 4) { - ret += _node.nodeValue; - } - - return ret + open + "/" + tag + close; - }, - - // Function calls it internally, it's the arguments part of the function - functionArgs: function functionArgs(fn) { - var args, - l = fn.length; - - if (!l) { - return ""; - } - - args = new Array(l); - while (l--) { - - // 97 is 'a' - args[l] = String.fromCharCode(97 + l); - } - return " " + args.join(", ") + " "; - }, - - // Object calls it internally, the key part of an item in a map - key: quote, - - // Function calls it internally, it's the content of the function - functionCode: "[code]", - - // Node calls it internally, it's a html attribute value - attribute: quote, - string: quote, - date: quote, - regexp: literal, - number: literal, - "boolean": literal, - symbol: function symbol(sym) { - return sym.toString(); - } - }, - - // If true, entities are escaped ( <, >, \t, space and \n ) - HTML: false, - - // Indentation unit - indentChar: " ", - - // If true, items in a collection, are separated by a \n, else just a space. - multiline: true - }; - - return dump; + var dump = (function() { + function quote(str) { + return ( + '"' + + str + .toString() + .replace(/\\/g, "\\\\") + .replace(/"/g, '\\"') + + '"' + ); + } + function literal(o) { + return o + ""; + } + function join(pre, arr, post) { + var s = dump.separator(), + base = dump.indent(), + inner = dump.indent(1); + if (arr.join) { + arr = arr.join("," + s + inner); + } + if (!arr) { + return pre + post; + } + return [pre, inner + arr, base + post].join(s); + } + function array(arr, stack) { + var i = arr.length, + ret = new Array(i); + + if (dump.maxDepth && dump.depth > dump.maxDepth) { + return "[object Array]"; + } + + this.up(); + while (i--) { + ret[i] = this.parse(arr[i], undefined, stack); + } + this.down(); + return join("[", ret, "]"); + } + + function isArray(obj) { + return ( + //Native Arrays + toString.call(obj) === "[object Array]" || + // NodeList objects + (typeof obj.length === "number" && + obj.item !== undefined && + (obj.length + ? obj.item(0) === obj[0] + : obj.item(0) === null && obj[0] === undefined)) + ); + } + + var reName = /^function (\w+)/, + dump = { + // The objType is used mostly internally, you can fix a (custom) type in advance + parse: function parse(obj, objType, stack) { + stack = stack || []; + var res, + parser, + parserType, + objIndex = stack.indexOf(obj); + + if (objIndex !== -1) { + return "recursion(" + (objIndex - stack.length) + ")"; + } + + objType = objType || this.typeOf(obj); + parser = this.parsers[objType]; + parserType = + typeof parser === "undefined" ? "undefined" : _typeof(parser); + + if (parserType === "function") { + stack.push(obj); + res = parser.call(this, obj, stack); + stack.pop(); + return res; + } + return parserType === "string" ? parser : this.parsers.error; + }, + typeOf: function typeOf(obj) { + var type; + + if (obj === null) { + type = "null"; + } else if (typeof obj === "undefined") { + type = "undefined"; + } else if (is("regexp", obj)) { + type = "regexp"; + } else if (is("date", obj)) { + type = "date"; + } else if (is("function", obj)) { + type = "function"; + } else if ( + obj.setInterval !== undefined && + obj.document !== undefined && + obj.nodeType === undefined + ) { + type = "window"; + } else if (obj.nodeType === 9) { + type = "document"; + } else if (obj.nodeType) { + type = "node"; + } else if (isArray(obj)) { + type = "array"; + } else if (obj.constructor === Error.prototype.constructor) { + type = "error"; + } else { + type = typeof obj === "undefined" ? "undefined" : _typeof(obj); + } + return type; + }, + + separator: function separator() { + if (this.multiline) { + return this.HTML ? "
" : "\n"; + } else { + return this.HTML ? " " : " "; + } + }, + + // Extra can be a number, shortcut for increasing-calling-decreasing + indent: function indent(extra) { + if (!this.multiline) { + return ""; + } + var chr = this.indentChar; + if (this.HTML) { + chr = chr.replace(/\t/g, " ").replace(/ /g, " "); + } + return new Array(this.depth + (extra || 0)).join(chr); + }, + up: function up(a) { + this.depth += a || 1; + }, + down: function down(a) { + this.depth -= a || 1; + }, + setParser: function setParser(name, parser) { + this.parsers[name] = parser; + }, + + // The next 3 are exposed so you can use them + quote: quote, + literal: literal, + join: join, + depth: 1, + maxDepth: config.maxDepth, + + // This is the list of parsers, to modify them, use dump.setParser + parsers: { + window: "[Window]", + document: "[Document]", + error: function error(_error) { + return 'Error("' + _error.message + '")'; + }, + unknown: "[Unknown]", + null: "null", + undefined: "undefined", + function: function _function(fn) { + var ret = "function", + // Functions never have name in IE + name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1]; + + if (name) { + ret += " " + name; + } + ret += "("; + + ret = [ret, dump.parse(fn, "functionArgs"), "){"].join(""); + return join(ret, dump.parse(fn, "functionCode"), "}"); + }, + array: array, + nodelist: array, + arguments: array, + object: function object(map, stack) { + var keys, + key, + val, + i, + nonEnumerableProperties, + ret = []; + + if (dump.maxDepth && dump.depth > dump.maxDepth) { + return "[object Object]"; + } + + dump.up(); + keys = []; + for (key in map) { + keys.push(key); + } + + // Some properties are not always enumerable on Error objects. + nonEnumerableProperties = ["message", "name"]; + for (i in nonEnumerableProperties) { + key = nonEnumerableProperties[i]; + if (key in map && !inArray(key, keys)) { + keys.push(key); + } + } + keys.sort(); + for (i = 0; i < keys.length; i++) { + key = keys[i]; + val = map[key]; + ret.push( + dump.parse(key, "key") + + ": " + + dump.parse(val, undefined, stack) + ); + } + dump.down(); + return join("{", ret, "}"); + }, + node: function node(_node) { + var len, + i, + val, + open = dump.HTML ? "<" : "<", + close = dump.HTML ? ">" : ">", + tag = _node.nodeName.toLowerCase(), + ret = open + tag, + attrs = _node.attributes; + + if (attrs) { + for (i = 0, len = attrs.length; i < len; i++) { + val = attrs[i].nodeValue; + + // IE6 includes all attributes in .attributes, even ones not explicitly + // set. Those have values like undefined, null, 0, false, "" or + // "inherit". + if (val && val !== "inherit") { + ret += + " " + + attrs[i].nodeName + + "=" + + dump.parse(val, "attribute"); + } + } + } + ret += close; + + // Show content of TextNode or CDATASection + if (_node.nodeType === 3 || _node.nodeType === 4) { + ret += _node.nodeValue; + } + + return ret + open + "/" + tag + close; + }, + + // Function calls it internally, it's the arguments part of the function + functionArgs: function functionArgs(fn) { + var args, + l = fn.length; + + if (!l) { + return ""; + } + + args = new Array(l); + while (l--) { + // 97 is 'a' + args[l] = String.fromCharCode(97 + l); + } + return " " + args.join(", ") + " "; + }, + + // Object calls it internally, the key part of an item in a map + key: quote, + + // Function calls it internally, it's the content of the function + functionCode: "[code]", + + // Node calls it internally, it's a html attribute value + attribute: quote, + string: quote, + date: quote, + regexp: literal, + number: literal, + boolean: literal, + symbol: function symbol(sym) { + return sym.toString(); + } + }, + + // If true, entities are escaped ( <, >, \t, space and \n ) + HTML: false, + + // Indentation unit + indentChar: " ", + + // If true, items in a collection, are separated by a \n, else just a space. + multiline: true + }; + + return dump; })(); var LISTENERS = Object.create(null); - var SUPPORTED_EVENTS = ["runStart", "suiteStart", "testStart", "assertion", "testEnd", "suiteEnd", "runEnd"]; + var SUPPORTED_EVENTS = [ + "runStart", + "suiteStart", + "testStart", + "assertion", + "testEnd", + "suiteEnd", + "runEnd" + ]; /** * Emits an event with the specified data to all currently registered listeners. @@ -966,17 +958,19 @@ * @return {Void} */ function emit(eventName, data) { - if (objectType(eventName) !== "string") { - throw new TypeError("eventName must be a string when emitting an event"); - } + if (objectType(eventName) !== "string") { + throw new TypeError("eventName must be a string when emitting an event"); + } - // Clone the callbacks in case one of them registers a new callback - var originalCallbacks = LISTENERS[eventName]; - var callbacks = originalCallbacks ? [].concat(toConsumableArray(originalCallbacks)) : []; + // Clone the callbacks in case one of them registers a new callback + var originalCallbacks = LISTENERS[eventName]; + var callbacks = originalCallbacks + ? [].concat(toConsumableArray(originalCallbacks)) + : []; - for (var i = 0; i < callbacks.length; i++) { - callbacks[i](data); - } + for (var i = 0; i < callbacks.length; i++) { + callbacks[i](data); + } } /** @@ -989,109 +983,131 @@ * @return {Void} */ function on(eventName, callback) { - if (objectType(eventName) !== "string") { - throw new TypeError("eventName must be a string when registering a listener"); - } else if (!inArray(eventName, SUPPORTED_EVENTS)) { - var events = SUPPORTED_EVENTS.join(", "); - throw new Error("\"" + eventName + "\" is not a valid event; must be one of: " + events + "."); - } else if (objectType(callback) !== "function") { - throw new TypeError("callback must be a function when registering a listener"); - } - - if (!LISTENERS[eventName]) { - LISTENERS[eventName] = []; - } - - // Don't register the same callback more than once - if (!inArray(callback, LISTENERS[eventName])) { - LISTENERS[eventName].push(callback); - } + if (objectType(eventName) !== "string") { + throw new TypeError( + "eventName must be a string when registering a listener" + ); + } else if (!inArray(eventName, SUPPORTED_EVENTS)) { + var events = SUPPORTED_EVENTS.join(", "); + throw new Error( + '"' + + eventName + + '" is not a valid event; must be one of: ' + + events + + "." + ); + } else if (objectType(callback) !== "function") { + throw new TypeError( + "callback must be a function when registering a listener" + ); + } + + if (!LISTENERS[eventName]) { + LISTENERS[eventName] = []; + } + + // Don't register the same callback more than once + if (!inArray(callback, LISTENERS[eventName])) { + LISTENERS[eventName].push(callback); + } } // Register logging callbacks function registerLoggingCallbacks(obj) { - var i, - l, - key, - callbackNames = ["begin", "done", "log", "testStart", "testDone", "moduleStart", "moduleDone"]; - - function registerLoggingCallback(key) { - var loggingCallback = function loggingCallback(callback) { - if (objectType(callback) !== "function") { - throw new Error("QUnit logging methods require a callback function as their first parameters."); - } - - config.callbacks[key].push(callback); - }; - - return loggingCallback; - } + var i, + l, + key, + callbackNames = [ + "begin", + "done", + "log", + "testStart", + "testDone", + "moduleStart", + "moduleDone" + ]; + + function registerLoggingCallback(key) { + var loggingCallback = function loggingCallback(callback) { + if (objectType(callback) !== "function") { + throw new Error( + "QUnit logging methods require a callback function as their first parameters." + ); + } + + config.callbacks[key].push(callback); + }; + + return loggingCallback; + } - for (i = 0, l = callbackNames.length; i < l; i++) { - key = callbackNames[i]; + for (i = 0, l = callbackNames.length; i < l; i++) { + key = callbackNames[i]; - // Initialize key collection of logging callback - if (objectType(config.callbacks[key]) === "undefined") { - config.callbacks[key] = []; - } + // Initialize key collection of logging callback + if (objectType(config.callbacks[key]) === "undefined") { + config.callbacks[key] = []; + } - obj[key] = registerLoggingCallback(key); - } + obj[key] = registerLoggingCallback(key); + } } function runLoggingCallbacks(key, args) { - var i, l, callbacks; + var i, l, callbacks; - callbacks = config.callbacks[key]; - for (i = 0, l = callbacks.length; i < l; i++) { - callbacks[i](args); - } + callbacks = config.callbacks[key]; + for (i = 0, l = callbacks.length; i < l; i++) { + callbacks[i](args); + } } // Doesn't support IE9, it will return undefined on these browsers // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack - var fileName = (sourceFromStacktrace(0) || "").replace(/(:\d+)+\)?/, "").replace(/.+\//, ""); + var fileName = (sourceFromStacktrace(0) || "") + .replace(/(:\d+)+\)?/, "") + .replace(/.+\//, ""); function extractStacktrace(e, offset) { - offset = offset === undefined ? 4 : offset; - - var stack, include, i; - - if (e && e.stack) { - stack = e.stack.split("\n"); - if (/^error$/i.test(stack[0])) { - stack.shift(); - } - if (fileName) { - include = []; - for (i = offset; i < stack.length; i++) { - if (stack[i].indexOf(fileName) !== -1) { - break; - } - include.push(stack[i]); - } - if (include.length) { - return include.join("\n"); - } - } - return stack[offset]; - } + offset = offset === undefined ? 4 : offset; + + var stack, include, i; + + if (e && e.stack) { + stack = e.stack.split("\n"); + if (/^error$/i.test(stack[0])) { + stack.shift(); + } + if (fileName) { + include = []; + for (i = offset; i < stack.length; i++) { + if (stack[i].indexOf(fileName) !== -1) { + break; + } + include.push(stack[i]); + } + if (include.length) { + return include.join("\n"); + } + } + return stack[offset]; + } } function sourceFromStacktrace(offset) { - var error = new Error(); - - // Support: Safari <=7 only, IE <=10 - 11 only - // Not all browsers generate the `stack` property for `new Error()`, see also #636 - if (!error.stack) { - try { - throw error; - } catch (err) { - error = err; - } - } - - return extractStacktrace(error, offset); + var error = new Error(); + + // Support: Safari <=7 only, IE <=10 - 11 only + // Not all browsers generate the `stack` property for `new Error()`, see also #636 + if (!error.stack) { + try { + throw error; + } catch (err) { + error = err; + } + } + + return extractStacktrace(error, offset); } var priorityCount = 0; @@ -1102,42 +1118,46 @@ * @param {Boolean} last */ function advance() { - var start = now(); - config.depth = (config.depth || 0) + 1; - - while (config.queue.length && !config.blocking) { - var elapsedTime = now() - start; - - if (!defined.setTimeout || config.updateRate <= 0 || elapsedTime < config.updateRate) { - if (priorityCount > 0) { - priorityCount--; - } - - config.queue.shift()(); - } else { - setTimeout(advance, 13); - break; - } - } + var start = now(); + config.depth = (config.depth || 0) + 1; + + while (config.queue.length && !config.blocking) { + var elapsedTime = now() - start; + + if ( + !defined.setTimeout || + config.updateRate <= 0 || + elapsedTime < config.updateRate + ) { + if (priorityCount > 0) { + priorityCount--; + } + + config.queue.shift()(); + } else { + setTimeout(advance, 13); + break; + } + } - config.depth--; + config.depth--; - if (!config.blocking && !config.queue.length && config.depth === 0) { - done(); - } + if (!config.blocking && !config.queue.length && config.depth === 0) { + done(); + } } function addToQueueImmediate(callback) { - if (objectType(callback) === "array") { - while (callback.length) { - addToQueueImmediate(callback.pop()); - } + if (objectType(callback) === "array") { + while (callback.length) { + addToQueueImmediate(callback.pop()); + } - return; - } + return; + } - config.queue.unshift(callback); - priorityCount++; + config.queue.unshift(callback); + priorityCount++; } /** @@ -1147,41 +1167,42 @@ * @param {String} seed */ function addToQueue(callback, prioritize, seed) { - if (prioritize) { - config.queue.splice(priorityCount++, 0, callback); - } else if (seed) { - if (!unitSampler) { - unitSampler = unitSamplerGenerator(seed); - } - - // Insert into a random position after all prioritized items - var index = Math.floor(unitSampler() * (config.queue.length - priorityCount + 1)); - config.queue.splice(priorityCount + index, 0, callback); - } else { - config.queue.push(callback); - } + if (prioritize) { + config.queue.splice(priorityCount++, 0, callback); + } else if (seed) { + if (!unitSampler) { + unitSampler = unitSamplerGenerator(seed); + } + + // Insert into a random position after all prioritized items + var index = Math.floor( + unitSampler() * (config.queue.length - priorityCount + 1) + ); + config.queue.splice(priorityCount + index, 0, callback); + } else { + config.queue.push(callback); + } } /** * Creates a seeded "sample" generator which is used for randomizing tests. */ function unitSamplerGenerator(seed) { + // 32-bit xorshift, requires only a nonzero seed + // http://excamera.com/sphinx/article-xorshift.html + var sample = parseInt(generateHash(seed), 16) || -1; + return function() { + sample ^= sample << 13; + sample ^= sample >>> 17; + sample ^= sample << 5; + + // ECMAScript has no unsigned number type + if (sample < 0) { + sample += 0x100000000; + } - // 32-bit xorshift, requires only a nonzero seed - // http://excamera.com/sphinx/article-xorshift.html - var sample = parseInt(generateHash(seed), 16) || -1; - return function () { - sample ^= sample << 13; - sample ^= sample >>> 17; - sample ^= sample << 5; - - // ECMAScript has no unsigned number type - if (sample < 0) { - sample += 0x100000000; - } - - return sample / 0x100000000; - }; + return sample / 0x100000000; + }; } /** @@ -1189,1253 +1210,1403 @@ * items. It handles emitting the final run events. */ function done() { - var storage = config.storage; - - ProcessingQueue.finished = true; - - var runtime = now() - config.started; - var passed = config.stats.all - config.stats.bad; - - emit("runEnd", globalSuite.end(true)); - runLoggingCallbacks("done", { - passed: passed, - failed: config.stats.bad, - total: config.stats.all, - runtime: runtime - }); - - // Clear own storage items if all tests passed - if (storage && config.stats.bad === 0) { - for (var i = storage.length - 1; i >= 0; i--) { - var key = storage.key(i); - - if (key.indexOf("qunit-test-") === 0) { - storage.removeItem(key); - } - } - } + var storage = config.storage; + + ProcessingQueue.finished = true; + + var runtime = now() - config.started; + var passed = config.stats.all - config.stats.bad; + + emit("runEnd", globalSuite.end(true)); + runLoggingCallbacks("done", { + passed: passed, + failed: config.stats.bad, + total: config.stats.all, + runtime: runtime + }); + + // Clear own storage items if all tests passed + if (storage && config.stats.bad === 0) { + for (var i = storage.length - 1; i >= 0; i--) { + var key = storage.key(i); + + if (key.indexOf("qunit-test-") === 0) { + storage.removeItem(key); + } + } + } } var ProcessingQueue = { - finished: false, - add: addToQueue, - addImmediate: addToQueueImmediate, - advance: advance + finished: false, + add: addToQueue, + addImmediate: addToQueueImmediate, + advance: advance }; - var TestReport = function () { - function TestReport(name, suite, options) { - classCallCheck(this, TestReport); - - this.name = name; - this.suiteName = suite.name; - this.fullName = suite.fullName.concat(name); - this.runtime = 0; - this.assertions = []; - - this.skipped = !!options.skip; - this.todo = !!options.todo; - - this.valid = options.valid; - - this._startTime = 0; - this._endTime = 0; - - suite.pushTest(this); - } - - createClass(TestReport, [{ - key: "start", - value: function start(recordTime) { - if (recordTime) { - this._startTime = Date.now(); - } - - return { - name: this.name, - suiteName: this.suiteName, - fullName: this.fullName.slice() - }; - } - }, { - key: "end", - value: function end(recordTime) { - if (recordTime) { - this._endTime = Date.now(); - } - - return extend(this.start(), { - runtime: this.getRuntime(), - status: this.getStatus(), - errors: this.getFailedAssertions(), - assertions: this.getAssertions() - }); - } - }, { - key: "pushAssertion", - value: function pushAssertion(assertion) { - this.assertions.push(assertion); - } - }, { - key: "getRuntime", - value: function getRuntime() { - return this._endTime - this._startTime; - } - }, { - key: "getStatus", - value: function getStatus() { - if (this.skipped) { - return "skipped"; - } - - var testPassed = this.getFailedAssertions().length > 0 ? this.todo : !this.todo; - - if (!testPassed) { - return "failed"; - } else if (this.todo) { - return "todo"; - } else { - return "passed"; - } - } - }, { - key: "getFailedAssertions", - value: function getFailedAssertions() { - return this.assertions.filter(function (assertion) { - return !assertion.passed; - }); - } - }, { - key: "getAssertions", - value: function getAssertions() { - return this.assertions.slice(); - } - - // Remove actual and expected values from assertions. This is to prevent - // leaking memory throughout a test suite. - - }, { - key: "slimAssertions", - value: function slimAssertions() { - this.assertions = this.assertions.map(function (assertion) { - delete assertion.actual; - delete assertion.expected; - return assertion; - }); - } - }]); - return TestReport; - }(); + var TestReport = (function() { + function TestReport(name, suite, options) { + classCallCheck(this, TestReport); + + this.name = name; + this.suiteName = suite.name; + this.fullName = suite.fullName.concat(name); + this.runtime = 0; + this.assertions = []; + + this.skipped = !!options.skip; + this.todo = !!options.todo; + + this.valid = options.valid; + + this._startTime = 0; + this._endTime = 0; + + suite.pushTest(this); + } + + createClass(TestReport, [ + { + key: "start", + value: function start(recordTime) { + if (recordTime) { + this._startTime = Date.now(); + } + + return { + name: this.name, + suiteName: this.suiteName, + fullName: this.fullName.slice() + }; + } + }, + { + key: "end", + value: function end(recordTime) { + if (recordTime) { + this._endTime = Date.now(); + } + + return extend(this.start(), { + runtime: this.getRuntime(), + status: this.getStatus(), + errors: this.getFailedAssertions(), + assertions: this.getAssertions() + }); + } + }, + { + key: "pushAssertion", + value: function pushAssertion(assertion) { + this.assertions.push(assertion); + } + }, + { + key: "getRuntime", + value: function getRuntime() { + return this._endTime - this._startTime; + } + }, + { + key: "getStatus", + value: function getStatus() { + if (this.skipped) { + return "skipped"; + } + + var testPassed = + this.getFailedAssertions().length > 0 ? this.todo : !this.todo; + + if (!testPassed) { + return "failed"; + } else if (this.todo) { + return "todo"; + } else { + return "passed"; + } + } + }, + { + key: "getFailedAssertions", + value: function getFailedAssertions() { + return this.assertions.filter(function(assertion) { + return !assertion.passed; + }); + } + }, + { + key: "getAssertions", + value: function getAssertions() { + return this.assertions.slice(); + } + + // Remove actual and expected values from assertions. This is to prevent + // leaking memory throughout a test suite. + }, + { + key: "slimAssertions", + value: function slimAssertions() { + this.assertions = this.assertions.map(function(assertion) { + delete assertion.actual; + delete assertion.expected; + return assertion; + }); + } + } + ]); + return TestReport; + })(); var focused$1 = false; function Test(settings) { - var i, l; - - ++Test.count; - - this.expected = null; - this.assertions = []; - this.semaphore = 0; - this.module = config.currentModule; - this.stack = sourceFromStacktrace(3); - this.steps = []; - this.timeout = undefined; - - // If a module is skipped, all its tests and the tests of the child suites - // should be treated as skipped even if they are defined as `only` or `todo`. - // As for `todo` module, all its tests will be treated as `todo` except for - // tests defined as `skip` which will be left intact. - // - // So, if a test is defined as `todo` and is inside a skipped module, we should - // then treat that test as if was defined as `skip`. - if (this.module.skip) { - settings.skip = true; - settings.todo = false; - - // Skipped tests should be left intact - } else if (this.module.todo && !settings.skip) { - settings.todo = true; - } - - extend(this, settings); - - this.testReport = new TestReport(settings.testName, this.module.suiteReport, { - todo: settings.todo, - skip: settings.skip, - valid: this.valid() - }); - - // Register unique strings - for (i = 0, l = this.module.tests; i < l.length; i++) { - if (this.module.tests[i].name === this.testName) { - this.testName += " "; - } - } - - this.testId = generateHash(this.module.name, this.testName); - - this.module.tests.push({ - name: this.testName, - testId: this.testId, - skip: !!settings.skip - }); - - if (settings.skip) { - - // Skipped tests will fully ignore any sent callback - this.callback = function () {}; - this.async = false; - this.expected = 0; - } else { - if (typeof this.callback !== "function") { - var method = this.todo ? "todo" : "test"; - - // eslint-disable-next-line max-len - throw new TypeError("You must provide a function as a test callback to QUnit." + method + "(\"" + settings.testName + "\")"); - } - - this.assert = new Assert(this); - } + var i, l; + + ++Test.count; + + this.expected = null; + this.assertions = []; + this.semaphore = 0; + this.module = config.currentModule; + this.stack = sourceFromStacktrace(3); + this.steps = []; + this.timeout = undefined; + + // If a module is skipped, all its tests and the tests of the child suites + // should be treated as skipped even if they are defined as `only` or `todo`. + // As for `todo` module, all its tests will be treated as `todo` except for + // tests defined as `skip` which will be left intact. + // + // So, if a test is defined as `todo` and is inside a skipped module, we should + // then treat that test as if was defined as `skip`. + if (this.module.skip) { + settings.skip = true; + settings.todo = false; + + // Skipped tests should be left intact + } else if (this.module.todo && !settings.skip) { + settings.todo = true; + } + + extend(this, settings); + + this.testReport = new TestReport( + settings.testName, + this.module.suiteReport, + { + todo: settings.todo, + skip: settings.skip, + valid: this.valid() + } + ); + + // Register unique strings + for (i = 0, l = this.module.tests; i < l.length; i++) { + if (this.module.tests[i].name === this.testName) { + this.testName += " "; + } + } + + this.testId = generateHash(this.module.name, this.testName); + + this.module.tests.push({ + name: this.testName, + testId: this.testId, + skip: !!settings.skip + }); + + if (settings.skip) { + // Skipped tests will fully ignore any sent callback + this.callback = function() {}; + this.async = false; + this.expected = 0; + } else { + if (typeof this.callback !== "function") { + var method = this.todo ? "todo" : "test"; + + // eslint-disable-next-line max-len + throw new TypeError( + "You must provide a function as a test callback to QUnit." + + method + + '("' + + settings.testName + + '")' + ); + } + + this.assert = new Assert(this); + } } Test.count = 0; function getNotStartedModules(startModule) { - var module = startModule, - modules = []; + var module = startModule, + modules = []; - while (module && module.testsRun === 0) { - modules.push(module); - module = module.parentModule; - } + while (module && module.testsRun === 0) { + modules.push(module); + module = module.parentModule; + } - return modules; + return modules; } Test.prototype = { - before: function before() { - var i, - startModule, - module = this.module, - notStartedModules = getNotStartedModules(module); - - for (i = notStartedModules.length - 1; i >= 0; i--) { - startModule = notStartedModules[i]; - startModule.stats = { all: 0, bad: 0, started: now() }; - emit("suiteStart", startModule.suiteReport.start(true)); - runLoggingCallbacks("moduleStart", { - name: startModule.name, - tests: startModule.tests - }); - } - - config.current = this; - - this.testEnvironment = extend({}, module.testEnvironment); - - this.started = now(); - emit("testStart", this.testReport.start(true)); - runLoggingCallbacks("testStart", { - name: this.testName, - module: module.name, - testId: this.testId, - previousFailure: this.previousFailure - }); - - if (!config.pollution) { - saveGlobal(); - } - }, - - run: function run() { - var promise; - - config.current = this; - - this.callbackStarted = now(); - - if (config.notrycatch) { - runTest(this); - return; - } - - try { - runTest(this); - } catch (e) { - this.pushFailure("Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + (e.message || e), extractStacktrace(e, 0)); - - // Else next test will carry the responsibility - saveGlobal(); - - // Restart the tests if they're blocking - if (config.blocking) { - internalRecover(this); - } - } - - function runTest(test) { - promise = test.callback.call(test.testEnvironment, test.assert); - test.resolvePromise(promise); - - // If the test has a "lock" on it, but the timeout is 0, then we push a - // failure as the test should be synchronous. - if (test.timeout === 0 && test.semaphore !== 0) { - pushFailure("Test did not finish synchronously even though assert.timeout( 0 ) was used.", sourceFromStacktrace(2)); - } - } - }, - - after: function after() { - checkPollution(); - }, - - queueHook: function queueHook(hook, hookName, hookOwner) { - var _this = this; - - var callHook = function callHook() { - var promise = hook.call(_this.testEnvironment, _this.assert); - _this.resolvePromise(promise, hookName); - }; - - var runHook = function runHook() { - if (hookName === "before") { - if (hookOwner.unskippedTestsRun !== 0) { - return; - } - - _this.preserveEnvironment = true; - } - - if (hookName === "after" && hookOwner.unskippedTestsRun !== numberOfUnskippedTests(hookOwner) - 1 && config.queue.length > 2) { - return; - } - - config.current = _this; - if (config.notrycatch) { - callHook(); - return; - } - try { - callHook(); - } catch (error) { - _this.pushFailure(hookName + " failed on " + _this.testName + ": " + (error.message || error), extractStacktrace(error, 0)); - } - }; - - return runHook; - }, - - - // Currently only used for module level hooks, can be used to add global level ones - hooks: function hooks(handler) { - var hooks = []; - - function processHooks(test, module) { - if (module.parentModule) { - processHooks(test, module.parentModule); - } - - if (module.hooks[handler].length) { - for (var i = 0; i < module.hooks[handler].length; i++) { - hooks.push(test.queueHook(module.hooks[handler][i], handler, module)); - } - } - } - - // Hooks are ignored on skipped tests - if (!this.skip) { - processHooks(this, this.module); - } - - return hooks; - }, - - - finish: function finish() { - config.current = this; - if (config.requireExpects && this.expected === null) { - this.pushFailure("Expected number of assertions to be defined, but expect() was " + "not called.", this.stack); - } else if (this.expected !== null && this.expected !== this.assertions.length) { - this.pushFailure("Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack); - } else if (this.expected === null && !this.assertions.length) { - this.pushFailure("Expected at least one assertion, but none were run - call " + "expect(0) to accept zero assertions.", this.stack); - } - - var i, - module = this.module, - moduleName = module.name, - testName = this.testName, - skipped = !!this.skip, - todo = !!this.todo, - bad = 0, - storage = config.storage; - - this.runtime = now() - this.started; - - config.stats.all += this.assertions.length; - module.stats.all += this.assertions.length; - - for (i = 0; i < this.assertions.length; i++) { - if (!this.assertions[i].result) { - bad++; - config.stats.bad++; - module.stats.bad++; - } - } - - notifyTestsRan(module, skipped); - - // Store result when possible - if (storage) { - if (bad) { - storage.setItem("qunit-test-" + moduleName + "-" + testName, bad); - } else { - storage.removeItem("qunit-test-" + moduleName + "-" + testName); - } - } - - // After emitting the js-reporters event we cleanup the assertion data to - // avoid leaking it. It is not used by the legacy testDone callbacks. - emit("testEnd", this.testReport.end(true)); - this.testReport.slimAssertions(); - - runLoggingCallbacks("testDone", { - name: testName, - module: moduleName, - skipped: skipped, - todo: todo, - failed: bad, - passed: this.assertions.length - bad, - total: this.assertions.length, - runtime: skipped ? 0 : this.runtime, - - // HTML Reporter use - assertions: this.assertions, - testId: this.testId, - - // Source of Test - source: this.stack - }); - - if (module.testsRun === numberOfTests(module)) { - logSuiteEnd(module); - - // Check if the parent modules, iteratively, are done. If that the case, - // we emit the `suiteEnd` event and trigger `moduleDone` callback. - var parent = module.parentModule; - while (parent && parent.testsRun === numberOfTests(parent)) { - logSuiteEnd(parent); - parent = parent.parentModule; - } - } - - config.current = undefined; - - function logSuiteEnd(module) { - emit("suiteEnd", module.suiteReport.end(true)); - runLoggingCallbacks("moduleDone", { - name: module.name, - tests: module.tests, - failed: module.stats.bad, - passed: module.stats.all - module.stats.bad, - total: module.stats.all, - runtime: now() - module.stats.started - }); - } - }, - - preserveTestEnvironment: function preserveTestEnvironment() { - if (this.preserveEnvironment) { - this.module.testEnvironment = this.testEnvironment; - this.testEnvironment = extend({}, this.module.testEnvironment); - } - }, - - queue: function queue() { - var test = this; - - if (!this.valid()) { - return; - } - - function runTest() { - - // Each of these can by async - ProcessingQueue.addImmediate([function () { - test.before(); - }, test.hooks("before"), function () { - test.preserveTestEnvironment(); - }, test.hooks("beforeEach"), function () { - test.run(); - }, test.hooks("afterEach").reverse(), test.hooks("after").reverse(), function () { - test.after(); - }, function () { - test.finish(); - }]); - } - - var previousFailCount = config.storage && +config.storage.getItem("qunit-test-" + this.module.name + "-" + this.testName); - - // Prioritize previously failed tests, detected from storage - var prioritize = config.reorder && !!previousFailCount; - - this.previousFailure = !!previousFailCount; - - ProcessingQueue.add(runTest, prioritize, config.seed); - - // If the queue has already finished, we manually process the new test - if (ProcessingQueue.finished) { - ProcessingQueue.advance(); - } - }, - - - pushResult: function pushResult(resultInfo) { - if (this !== config.current) { - throw new Error("Assertion occured after test had finished."); - } - - // Destructure of resultInfo = { result, actual, expected, message, negative } - var source, - details = { - module: this.module.name, - name: this.testName, - result: resultInfo.result, - message: resultInfo.message, - actual: resultInfo.actual, - testId: this.testId, - negative: resultInfo.negative || false, - runtime: now() - this.started, - todo: !!this.todo - }; - - if (hasOwn.call(resultInfo, "expected")) { - details.expected = resultInfo.expected; - } - - if (!resultInfo.result) { - source = resultInfo.source || sourceFromStacktrace(); - - if (source) { - details.source = source; - } - } - - this.logAssertion(details); - - this.assertions.push({ - result: !!resultInfo.result, - message: resultInfo.message - }); - }, - - pushFailure: function pushFailure(message, source, actual) { - if (!(this instanceof Test)) { - throw new Error("pushFailure() assertion outside test context, was " + sourceFromStacktrace(2)); - } - - this.pushResult({ - result: false, - message: message || "error", - actual: actual || null, - source: source - }); - }, - - /** - * Log assertion details using both the old QUnit.log interface and - * QUnit.on( "assertion" ) interface. - * - * @private - */ - logAssertion: function logAssertion(details) { - runLoggingCallbacks("log", details); - - var assertion = { - passed: details.result, - actual: details.actual, - expected: details.expected, - message: details.message, - stack: details.source, - todo: details.todo - }; - this.testReport.pushAssertion(assertion); - emit("assertion", assertion); - }, - - - resolvePromise: function resolvePromise(promise, phase) { - var then, - resume, - message, - test = this; - if (promise != null) { - then = promise.then; - if (objectType(then) === "function") { - resume = internalStop(test); - then.call(promise, function () { - resume(); - }, function (error) { - message = "Promise rejected " + (!phase ? "during" : phase.replace(/Each$/, "")) + " \"" + test.testName + "\": " + (error && error.message || error); - test.pushFailure(message, extractStacktrace(error, 0)); - - // Else next test will carry the responsibility - saveGlobal(); - - // Unblock - resume(); - }); - } - } - }, - - valid: function valid() { - var filter = config.filter, - regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec(filter), - module = config.module && config.module.toLowerCase(), - fullName = this.module.name + ": " + this.testName; - - function moduleChainNameMatch(testModule) { - var testModuleName = testModule.name ? testModule.name.toLowerCase() : null; - if (testModuleName === module) { - return true; - } else if (testModule.parentModule) { - return moduleChainNameMatch(testModule.parentModule); - } else { - return false; - } - } - - function moduleChainIdMatch(testModule) { - return inArray(testModule.moduleId, config.moduleId) || testModule.parentModule && moduleChainIdMatch(testModule.parentModule); - } - - // Internally-generated tests are always valid - if (this.callback && this.callback.validTest) { - return true; - } - - if (config.moduleId && config.moduleId.length > 0 && !moduleChainIdMatch(this.module)) { - - return false; - } - - if (config.testId && config.testId.length > 0 && !inArray(this.testId, config.testId)) { - - return false; - } - - if (module && !moduleChainNameMatch(this.module)) { - return false; - } - - if (!filter) { - return true; - } - - return regexFilter ? this.regexFilter(!!regexFilter[1], regexFilter[2], regexFilter[3], fullName) : this.stringFilter(filter, fullName); - }, - - regexFilter: function regexFilter(exclude, pattern, flags, fullName) { - var regex = new RegExp(pattern, flags); - var match = regex.test(fullName); - - return match !== exclude; - }, - - stringFilter: function stringFilter(filter, fullName) { - filter = filter.toLowerCase(); - fullName = fullName.toLowerCase(); - - var include = filter.charAt(0) !== "!"; - if (!include) { - filter = filter.slice(1); - } - - // If the filter matches, we need to honour include - if (fullName.indexOf(filter) !== -1) { - return include; - } - - // Otherwise, do the opposite - return !include; - } - }; + before: function before() { + var i, + startModule, + module = this.module, + notStartedModules = getNotStartedModules(module); + + for (i = notStartedModules.length - 1; i >= 0; i--) { + startModule = notStartedModules[i]; + startModule.stats = { all: 0, bad: 0, started: now() }; + emit("suiteStart", startModule.suiteReport.start(true)); + runLoggingCallbacks("moduleStart", { + name: startModule.name, + tests: startModule.tests + }); + } - function pushFailure() { - if (!config.current) { - throw new Error("pushFailure() assertion outside test context, in " + sourceFromStacktrace(2)); - } + config.current = this; - // Gets current test obj - var currentTest = config.current; + this.testEnvironment = extend({}, module.testEnvironment); - return currentTest.pushFailure.apply(currentTest, arguments); - } + this.started = now(); + emit("testStart", this.testReport.start(true)); + runLoggingCallbacks("testStart", { + name: this.testName, + module: module.name, + testId: this.testId, + previousFailure: this.previousFailure + }); - function saveGlobal() { - config.pollution = []; - - if (config.noglobals) { - for (var key in global$1) { - if (hasOwn.call(global$1, key)) { - - // In Opera sometimes DOM element ids show up here, ignore them - if (/^qunit-test-output/.test(key)) { - continue; - } - config.pollution.push(key); - } - } - } - } + if (!config.pollution) { + saveGlobal(); + } + }, - function checkPollution() { - var newGlobals, - deletedGlobals, - old = config.pollution; + run: function run() { + var promise; - saveGlobal(); + config.current = this; - newGlobals = diff(config.pollution, old); - if (newGlobals.length > 0) { - pushFailure("Introduced global variable(s): " + newGlobals.join(", ")); - } + this.callbackStarted = now(); - deletedGlobals = diff(old, config.pollution); - if (deletedGlobals.length > 0) { - pushFailure("Deleted global variable(s): " + deletedGlobals.join(", ")); - } - } + if (config.notrycatch) { + runTest(this); + return; + } - // Will be exposed as QUnit.test - function test(testName, callback) { - if (focused$1) { - return; - } + try { + runTest(this); + } catch (e) { + this.pushFailure( + "Died on test #" + + (this.assertions.length + 1) + + " " + + this.stack + + ": " + + (e.message || e), + extractStacktrace(e, 0) + ); + + // Else next test will carry the responsibility + saveGlobal(); + + // Restart the tests if they're blocking + if (config.blocking) { + internalRecover(this); + } + } - var newTest = new Test({ - testName: testName, - callback: callback - }); + function runTest(test) { + promise = test.callback.call(test.testEnvironment, test.assert); + test.resolvePromise(promise); + + // If the test has a "lock" on it, but the timeout is 0, then we push a + // failure as the test should be synchronous. + if (test.timeout === 0 && test.semaphore !== 0) { + pushFailure( + "Test did not finish synchronously even though assert.timeout( 0 ) was used.", + sourceFromStacktrace(2) + ); + } + } + }, + + after: function after() { + checkPollution(); + }, + + queueHook: function queueHook(hook, hookName, hookOwner) { + var _this = this; + + var callHook = function callHook() { + var promise = hook.call(_this.testEnvironment, _this.assert); + _this.resolvePromise(promise, hookName); + }; + + var runHook = function runHook() { + if (hookName === "before") { + if (hookOwner.unskippedTestsRun !== 0) { + return; + } + + _this.preserveEnvironment = true; + } + + if ( + hookName === "after" && + hookOwner.unskippedTestsRun !== + numberOfUnskippedTests(hookOwner) - 1 && + config.queue.length > 2 + ) { + return; + } + + config.current = _this; + if (config.notrycatch) { + callHook(); + return; + } + try { + callHook(); + } catch (error) { + _this.pushFailure( + hookName + + " failed on " + + _this.testName + + ": " + + (error.message || error), + extractStacktrace(error, 0) + ); + } + }; + + return runHook; + }, + + // Currently only used for module level hooks, can be used to add global level ones + hooks: function hooks(handler) { + var hooks = []; + + function processHooks(test, module) { + if (module.parentModule) { + processHooks(test, module.parentModule); + } + + if (module.hooks[handler].length) { + for (var i = 0; i < module.hooks[handler].length; i++) { + hooks.push( + test.queueHook(module.hooks[handler][i], handler, module) + ); + } + } + } - newTest.queue(); - } + // Hooks are ignored on skipped tests + if (!this.skip) { + processHooks(this, this.module); + } - function todo(testName, callback) { - if (focused$1) { - return; - } + return hooks; + }, + + finish: function finish() { + config.current = this; + if (config.requireExpects && this.expected === null) { + this.pushFailure( + "Expected number of assertions to be defined, but expect() was " + + "not called.", + this.stack + ); + } else if ( + this.expected !== null && + this.expected !== this.assertions.length + ) { + this.pushFailure( + "Expected " + + this.expected + + " assertions, but " + + this.assertions.length + + " were run", + this.stack + ); + } else if (this.expected === null && !this.assertions.length) { + this.pushFailure( + "Expected at least one assertion, but none were run - call " + + "expect(0) to accept zero assertions.", + this.stack + ); + } - var newTest = new Test({ - testName: testName, - callback: callback, - todo: true - }); + var i, + module = this.module, + moduleName = module.name, + testName = this.testName, + skipped = !!this.skip, + todo = !!this.todo, + bad = 0, + storage = config.storage; + + this.runtime = now() - this.started; + + config.stats.all += this.assertions.length; + module.stats.all += this.assertions.length; + + for (i = 0; i < this.assertions.length; i++) { + if (!this.assertions[i].result) { + bad++; + config.stats.bad++; + module.stats.bad++; + } + } - newTest.queue(); - } + notifyTestsRan(module, skipped); - // Will be exposed as QUnit.skip - function skip(testName) { - if (focused$1) { - return; - } + // Store result when possible + if (storage) { + if (bad) { + storage.setItem("qunit-test-" + moduleName + "-" + testName, bad); + } else { + storage.removeItem("qunit-test-" + moduleName + "-" + testName); + } + } - var test = new Test({ - testName: testName, - skip: true - }); + // After emitting the js-reporters event we cleanup the assertion data to + // avoid leaking it. It is not used by the legacy testDone callbacks. + emit("testEnd", this.testReport.end(true)); + this.testReport.slimAssertions(); + + runLoggingCallbacks("testDone", { + name: testName, + module: moduleName, + skipped: skipped, + todo: todo, + failed: bad, + passed: this.assertions.length - bad, + total: this.assertions.length, + runtime: skipped ? 0 : this.runtime, + + // HTML Reporter use + assertions: this.assertions, + testId: this.testId, + + // Source of Test + source: this.stack + }); + + if (module.testsRun === numberOfTests(module)) { + logSuiteEnd(module); + + // Check if the parent modules, iteratively, are done. If that the case, + // we emit the `suiteEnd` event and trigger `moduleDone` callback. + var parent = module.parentModule; + while (parent && parent.testsRun === numberOfTests(parent)) { + logSuiteEnd(parent); + parent = parent.parentModule; + } + } - test.queue(); - } + config.current = undefined; + + function logSuiteEnd(module) { + emit("suiteEnd", module.suiteReport.end(true)); + runLoggingCallbacks("moduleDone", { + name: module.name, + tests: module.tests, + failed: module.stats.bad, + passed: module.stats.all - module.stats.bad, + total: module.stats.all, + runtime: now() - module.stats.started + }); + } + }, - // Will be exposed as QUnit.only - function only(testName, callback) { - if (focused$1) { - return; - } + preserveTestEnvironment: function preserveTestEnvironment() { + if (this.preserveEnvironment) { + this.module.testEnvironment = this.testEnvironment; + this.testEnvironment = extend({}, this.module.testEnvironment); + } + }, - config.queue.length = 0; - focused$1 = true; + queue: function queue() { + var test = this; - var newTest = new Test({ - testName: testName, - callback: callback - }); + if (!this.valid()) { + return; + } - newTest.queue(); - } + function runTest() { + // Each of these can by async + ProcessingQueue.addImmediate([ + function() { + test.before(); + }, + test.hooks("before"), + function() { + test.preserveTestEnvironment(); + }, + test.hooks("beforeEach"), + function() { + test.run(); + }, + test.hooks("afterEach").reverse(), + test.hooks("after").reverse(), + function() { + test.after(); + }, + function() { + test.finish(); + } + ]); + } - // Put a hold on processing and return a function that will release it. - function internalStop(test) { - test.semaphore += 1; - config.blocking = true; - - // Set a recovery timeout, if so configured. - if (defined.setTimeout) { - var timeoutDuration = void 0; - - if (typeof test.timeout === "number") { - timeoutDuration = test.timeout; - } else if (typeof config.testTimeout === "number") { - timeoutDuration = config.testTimeout; - } - - if (typeof timeoutDuration === "number" && timeoutDuration > 0) { - clearTimeout(config.timeout); - config.timeout = setTimeout(function () { - pushFailure("Test took longer than " + timeoutDuration + "ms; test timed out.", sourceFromStacktrace(2)); - internalRecover(test); - }, timeoutDuration); - } - } - - var released = false; - return function resume() { - if (released) { - return; - } - - released = true; - test.semaphore -= 1; - internalStart(test); - }; - } + var previousFailCount = + config.storage && + +config.storage.getItem( + "qunit-test-" + this.module.name + "-" + this.testName + ); - // Forcefully release all processing holds. - function internalRecover(test) { - test.semaphore = 0; - internalStart(test); - } + // Prioritize previously failed tests, detected from storage + var prioritize = config.reorder && !!previousFailCount; - // Release a processing hold, scheduling a resumption attempt if no holds remain. - function internalStart(test) { + this.previousFailure = !!previousFailCount; - // If semaphore is non-numeric, throw error - if (isNaN(test.semaphore)) { - test.semaphore = 0; - - pushFailure("Invalid value on test.semaphore", sourceFromStacktrace(2)); - return; - } - - // Don't start until equal number of stop-calls - if (test.semaphore > 0) { - return; - } - - // Throw an Error if start is called more often than stop - if (test.semaphore < 0) { - test.semaphore = 0; - - pushFailure("Tried to restart test while already started (test's semaphore was 0 already)", sourceFromStacktrace(2)); - return; - } - - // Add a slight delay to allow more assertions etc. - if (defined.setTimeout) { - if (config.timeout) { - clearTimeout(config.timeout); - } - config.timeout = setTimeout(function () { - if (test.semaphore > 0) { - return; - } - - if (config.timeout) { - clearTimeout(config.timeout); - } - - begin(); - }, 13); - } else { - begin(); - } - } + ProcessingQueue.add(runTest, prioritize, config.seed); - function collectTests(module) { - var tests = [].concat(module.tests); - var modules = [].concat(toConsumableArray(module.childModules)); + // If the queue has already finished, we manually process the new test + if (ProcessingQueue.finished) { + ProcessingQueue.advance(); + } + }, - // Do a breadth-first traversal of the child modules - while (modules.length) { - var nextModule = modules.shift(); - tests.push.apply(tests, nextModule.tests); - modules.push.apply(modules, toConsumableArray(nextModule.childModules)); - } + pushResult: function pushResult(resultInfo) { + if (this !== config.current) { + throw new Error("Assertion occured after test had finished."); + } - return tests; - } + // Destructure of resultInfo = { result, actual, expected, message, negative } + var source, + details = { + module: this.module.name, + name: this.testName, + result: resultInfo.result, + message: resultInfo.message, + actual: resultInfo.actual, + testId: this.testId, + negative: resultInfo.negative || false, + runtime: now() - this.started, + todo: !!this.todo + }; + + if (hasOwn.call(resultInfo, "expected")) { + details.expected = resultInfo.expected; + } - function numberOfTests(module) { - return collectTests(module).length; - } + if (!resultInfo.result) { + source = resultInfo.source || sourceFromStacktrace(); - function numberOfUnskippedTests(module) { - return collectTests(module).filter(function (test) { - return !test.skip; - }).length; - } + if (source) { + details.source = source; + } + } - function notifyTestsRan(module, skipped) { - module.testsRun++; - if (!skipped) { - module.unskippedTestsRun++; - } - while (module = module.parentModule) { - module.testsRun++; - if (!skipped) { - module.unskippedTestsRun++; - } - } - } + this.logAssertion(details); - /** - * Returns a function that proxies to the given method name on the globals - * console object. The proxy will also detect if the console doesn't exist and - * will appropriately no-op. This allows support for IE9, which doesn't have a - * console if the developer tools are not open. - */ + this.assertions.push({ + result: !!resultInfo.result, + message: resultInfo.message + }); + }, + + pushFailure: function pushFailure(message, source, actual) { + if (!(this instanceof Test)) { + throw new Error( + "pushFailure() assertion outside test context, was " + + sourceFromStacktrace(2) + ); + } + + this.pushResult({ + result: false, + message: message || "error", + actual: actual || null, + source: source + }); + }, + + /** + * Log assertion details using both the old QUnit.log interface and + * QUnit.on( "assertion" ) interface. + * + * @private + */ + logAssertion: function logAssertion(details) { + runLoggingCallbacks("log", details); + + var assertion = { + passed: details.result, + actual: details.actual, + expected: details.expected, + message: details.message, + stack: details.source, + todo: details.todo + }; + this.testReport.pushAssertion(assertion); + emit("assertion", assertion); + }, + + resolvePromise: function resolvePromise(promise, phase) { + var then, + resume, + message, + test = this; + if (promise != null) { + then = promise.then; + if (objectType(then) === "function") { + resume = internalStop(test); + then.call( + promise, + function() { + resume(); + }, + function(error) { + message = + "Promise rejected " + + (!phase ? "during" : phase.replace(/Each$/, "")) + + ' "' + + test.testName + + '": ' + + ((error && error.message) || error); + test.pushFailure(message, extractStacktrace(error, 0)); + + // Else next test will carry the responsibility + saveGlobal(); + + // Unblock + resume(); + } + ); + } + } + }, + + valid: function valid() { + var filter = config.filter, + regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec(filter), + module = config.module && config.module.toLowerCase(), + fullName = this.module.name + ": " + this.testName; + + function moduleChainNameMatch(testModule) { + var testModuleName = testModule.name + ? testModule.name.toLowerCase() + : null; + if (testModuleName === module) { + return true; + } else if (testModule.parentModule) { + return moduleChainNameMatch(testModule.parentModule); + } else { + return false; + } + } + + function moduleChainIdMatch(testModule) { + return ( + inArray(testModule.moduleId, config.moduleId) || + (testModule.parentModule && + moduleChainIdMatch(testModule.parentModule)) + ); + } + + // Internally-generated tests are always valid + if (this.callback && this.callback.validTest) { + return true; + } + + if ( + config.moduleId && + config.moduleId.length > 0 && + !moduleChainIdMatch(this.module) + ) { + return false; + } + + if ( + config.testId && + config.testId.length > 0 && + !inArray(this.testId, config.testId) + ) { + return false; + } + + if (module && !moduleChainNameMatch(this.module)) { + return false; + } + + if (!filter) { + return true; + } + + return regexFilter + ? this.regexFilter( + !!regexFilter[1], + regexFilter[2], + regexFilter[3], + fullName + ) + : this.stringFilter(filter, fullName); + }, + + regexFilter: function regexFilter(exclude, pattern, flags, fullName) { + var regex = new RegExp(pattern, flags); + var match = regex.test(fullName); + + return match !== exclude; + }, + + stringFilter: function stringFilter(filter, fullName) { + filter = filter.toLowerCase(); + fullName = fullName.toLowerCase(); + + var include = filter.charAt(0) !== "!"; + if (!include) { + filter = filter.slice(1); + } + + // If the filter matches, we need to honour include + if (fullName.indexOf(filter) !== -1) { + return include; + } + + // Otherwise, do the opposite + return !include; + } + }; + + function pushFailure() { + if (!config.current) { + throw new Error( + "pushFailure() assertion outside test context, in " + + sourceFromStacktrace(2) + ); + } + + // Gets current test obj + var currentTest = config.current; + + return currentTest.pushFailure.apply(currentTest, arguments); + } + + function saveGlobal() { + config.pollution = []; + + if (config.noglobals) { + for (var key in global$1) { + if (hasOwn.call(global$1, key)) { + // In Opera sometimes DOM element ids show up here, ignore them + if (/^qunit-test-output/.test(key)) { + continue; + } + config.pollution.push(key); + } + } + } + } + + function checkPollution() { + var newGlobals, + deletedGlobals, + old = config.pollution; + + saveGlobal(); + + newGlobals = diff(config.pollution, old); + if (newGlobals.length > 0) { + pushFailure("Introduced global variable(s): " + newGlobals.join(", ")); + } + + deletedGlobals = diff(old, config.pollution); + if (deletedGlobals.length > 0) { + pushFailure("Deleted global variable(s): " + deletedGlobals.join(", ")); + } + } + + // Will be exposed as QUnit.test + function test(testName, callback) { + if (focused$1) { + return; + } + + var newTest = new Test({ + testName: testName, + callback: callback + }); + + newTest.queue(); + } + + function todo(testName, callback) { + if (focused$1) { + return; + } + + var newTest = new Test({ + testName: testName, + callback: callback, + todo: true + }); + + newTest.queue(); + } + + // Will be exposed as QUnit.skip + function skip(testName) { + if (focused$1) { + return; + } + + var test = new Test({ + testName: testName, + skip: true + }); + + test.queue(); + } + + // Will be exposed as QUnit.only + function only(testName, callback) { + if (focused$1) { + return; + } + + config.queue.length = 0; + focused$1 = true; + + var newTest = new Test({ + testName: testName, + callback: callback + }); + + newTest.queue(); + } + + // Put a hold on processing and return a function that will release it. + function internalStop(test) { + test.semaphore += 1; + config.blocking = true; + + // Set a recovery timeout, if so configured. + if (defined.setTimeout) { + var timeoutDuration = void 0; + + if (typeof test.timeout === "number") { + timeoutDuration = test.timeout; + } else if (typeof config.testTimeout === "number") { + timeoutDuration = config.testTimeout; + } + + if (typeof timeoutDuration === "number" && timeoutDuration > 0) { + clearTimeout(config.timeout); + config.timeout = setTimeout(function() { + pushFailure( + "Test took longer than " + timeoutDuration + "ms; test timed out.", + sourceFromStacktrace(2) + ); + internalRecover(test); + }, timeoutDuration); + } + } + + var released = false; + return function resume() { + if (released) { + return; + } + + released = true; + test.semaphore -= 1; + internalStart(test); + }; + } + + // Forcefully release all processing holds. + function internalRecover(test) { + test.semaphore = 0; + internalStart(test); + } + + // Release a processing hold, scheduling a resumption attempt if no holds remain. + function internalStart(test) { + // If semaphore is non-numeric, throw error + if (isNaN(test.semaphore)) { + test.semaphore = 0; + + pushFailure("Invalid value on test.semaphore", sourceFromStacktrace(2)); + return; + } + + // Don't start until equal number of stop-calls + if (test.semaphore > 0) { + return; + } + + // Throw an Error if start is called more often than stop + if (test.semaphore < 0) { + test.semaphore = 0; + + pushFailure( + "Tried to restart test while already started (test's semaphore was 0 already)", + sourceFromStacktrace(2) + ); + return; + } + + // Add a slight delay to allow more assertions etc. + if (defined.setTimeout) { + if (config.timeout) { + clearTimeout(config.timeout); + } + config.timeout = setTimeout(function() { + if (test.semaphore > 0) { + return; + } + + if (config.timeout) { + clearTimeout(config.timeout); + } + + begin(); + }, 13); + } else { + begin(); + } + } + + function collectTests(module) { + var tests = [].concat(module.tests); + var modules = [].concat(toConsumableArray(module.childModules)); + + // Do a breadth-first traversal of the child modules + while (modules.length) { + var nextModule = modules.shift(); + tests.push.apply(tests, nextModule.tests); + modules.push.apply(modules, toConsumableArray(nextModule.childModules)); + } + + return tests; + } + + function numberOfTests(module) { + return collectTests(module).length; + } + + function numberOfUnskippedTests(module) { + return collectTests(module).filter(function(test) { + return !test.skip; + }).length; + } + + function notifyTestsRan(module, skipped) { + module.testsRun++; + if (!skipped) { + module.unskippedTestsRun++; + } + while ((module = module.parentModule)) { + module.testsRun++; + if (!skipped) { + module.unskippedTestsRun++; + } + } + } + + /** + * Returns a function that proxies to the given method name on the globals + * console object. The proxy will also detect if the console doesn't exist and + * will appropriately no-op. This allows support for IE9, which doesn't have a + * console if the developer tools are not open. + */ function consoleProxy(method) { - return function () { - if (console) { - console[method].apply(console, arguments); - } - }; + return function() { + if (console) { + console[method].apply(console, arguments); + } + }; } var Logger = { - warn: consoleProxy("warn") + warn: consoleProxy("warn") }; - var Assert = function () { - function Assert(testContext) { - classCallCheck(this, Assert); - - this.test = testContext; - } - - // Assert helpers - - createClass(Assert, [{ - key: "timeout", - value: function timeout(duration) { - if (typeof duration !== "number") { - throw new Error("You must pass a number as the duration to assert.timeout"); - } - - this.test.timeout = duration; - } - - // Documents a "step", which is a string value, in a test as a passing assertion - - }, { - key: "step", - value: function step(message) { - var result = !!message; - - this.test.steps.push(message); - - return this.pushResult({ - result: result, - message: message || "You must provide a message to assert.step" - }); - } - - // Verifies the steps in a test match a given array of string values - - }, { - key: "verifySteps", - value: function verifySteps(steps, message) { - this.deepEqual(this.test.steps, steps, message); - } - - // Specify the number of expected assertions to guarantee that failed test - // (no assertions are run at all) don't slip through. - - }, { - key: "expect", - value: function expect(asserts) { - if (arguments.length === 1) { - this.test.expected = asserts; - } else { - return this.test.expected; - } - } - - // Put a hold on processing and return a function that will release it a maximum of once. - - }, { - key: "async", - value: function async(count) { - var test$$1 = this.test; - - var popped = false, - acceptCallCount = count; - - if (typeof acceptCallCount === "undefined") { - acceptCallCount = 1; - } - - var resume = internalStop(test$$1); - - return function done() { - if (config.current !== test$$1) { - throw Error("assert.async callback called after test finished."); - } - - if (popped) { - test$$1.pushFailure("Too many calls to the `assert.async` callback", sourceFromStacktrace(2)); - return; - } - - acceptCallCount -= 1; - if (acceptCallCount > 0) { - return; - } - - popped = true; - resume(); - }; - } - - // Exports test.push() to the user API - // Alias of pushResult. - - }, { - key: "push", - value: function push(result, actual, expected, message, negative) { - Logger.warn("assert.push is deprecated and will be removed in QUnit 3.0." + " Please use assert.pushResult instead (https://api.qunitjs.com/assert/pushResult)."); - - var currentAssert = this instanceof Assert ? this : config.current.assert; - return currentAssert.pushResult({ - result: result, - actual: actual, - expected: expected, - message: message, - negative: negative - }); - } - }, { - key: "pushResult", - value: function pushResult(resultInfo) { - - // Destructure of resultInfo = { result, actual, expected, message, negative } - var assert = this; - var currentTest = assert instanceof Assert && assert.test || config.current; - - // Backwards compatibility fix. - // Allows the direct use of global exported assertions and QUnit.assert.* - // Although, it's use is not recommended as it can leak assertions - // to other tests from async tests, because we only get a reference to the current test, - // not exactly the test where assertion were intended to be called. - if (!currentTest) { - throw new Error("assertion outside test context, in " + sourceFromStacktrace(2)); - } - - if (!(assert instanceof Assert)) { - assert = currentTest.assert; - } - - return assert.test.pushResult(resultInfo); - } - }, { - key: "ok", - value: function ok(result, message) { - if (!message) { - message = result ? "okay" : "failed, expected argument to be truthy, was: " + dump.parse(result); - } - - this.pushResult({ - result: !!result, - actual: result, - expected: true, - message: message - }); - } - }, { - key: "notOk", - value: function notOk(result, message) { - if (!message) { - message = !result ? "okay" : "failed, expected argument to be falsy, was: " + dump.parse(result); - } - - this.pushResult({ - result: !result, - actual: result, - expected: false, - message: message - }); - } - }, { - key: "equal", - value: function equal(actual, expected, message) { - - // eslint-disable-next-line eqeqeq - var result = expected == actual; - - this.pushResult({ - result: result, - actual: actual, - expected: expected, - message: message - }); - } - }, { - key: "notEqual", - value: function notEqual(actual, expected, message) { - - // eslint-disable-next-line eqeqeq - var result = expected != actual; - - this.pushResult({ - result: result, - actual: actual, - expected: expected, - message: message, - negative: true - }); - } - }, { - key: "propEqual", - value: function propEqual(actual, expected, message) { - actual = objectValues(actual); - expected = objectValues(expected); - - this.pushResult({ - result: equiv(actual, expected), - actual: actual, - expected: expected, - message: message - }); - } - }, { - key: "notPropEqual", - value: function notPropEqual(actual, expected, message) { - actual = objectValues(actual); - expected = objectValues(expected); - - this.pushResult({ - result: !equiv(actual, expected), - actual: actual, - expected: expected, - message: message, - negative: true - }); - } - }, { - key: "deepEqual", - value: function deepEqual(actual, expected, message) { - this.pushResult({ - result: equiv(actual, expected), - actual: actual, - expected: expected, - message: message - }); - } - }, { - key: "notDeepEqual", - value: function notDeepEqual(actual, expected, message) { - this.pushResult({ - result: !equiv(actual, expected), - actual: actual, - expected: expected, - message: message, - negative: true - }); - } - }, { - key: "strictEqual", - value: function strictEqual(actual, expected, message) { - this.pushResult({ - result: expected === actual, - actual: actual, - expected: expected, - message: message - }); - } - }, { - key: "notStrictEqual", - value: function notStrictEqual(actual, expected, message) { - this.pushResult({ - result: expected !== actual, - actual: actual, - expected: expected, - message: message, - negative: true - }); - } - }, { - key: "throws", - value: function throws(block, expected, message) { - var actual = void 0, - result = false; - - var currentTest = this instanceof Assert && this.test || config.current; - - // 'expected' is optional unless doing string comparison - if (objectType(expected) === "string") { - if (message == null) { - message = expected; - expected = null; - } else { - throw new Error("throws/raises does not accept a string value for the expected argument.\n" + "Use a non-string object value (e.g. regExp) instead if it's necessary."); - } - } - - currentTest.ignoreGlobalErrors = true; - try { - block.call(currentTest.testEnvironment); - } catch (e) { - actual = e; - } - currentTest.ignoreGlobalErrors = false; - - if (actual) { - var expectedType = objectType(expected); - - // We don't want to validate thrown error - if (!expected) { - result = true; - expected = null; - - // Expected is a regexp - } else if (expectedType === "regexp") { - result = expected.test(errorString(actual)); - - // Expected is a constructor, maybe an Error constructor - } else if (expectedType === "function" && actual instanceof expected) { - result = true; - - // Expected is an Error object - } else if (expectedType === "object") { - result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message; - - // Expected is a validation function which returns true if validation passed - } else if (expectedType === "function" && expected.call({}, actual) === true) { - expected = null; - result = true; - } - } - - currentTest.assert.pushResult({ - result: result, - actual: actual, - expected: expected, - message: message - }); - } - }]); - return Assert; - }(); + var Assert = (function() { + function Assert(testContext) { + classCallCheck(this, Assert); + + this.test = testContext; + } + + // Assert helpers + + createClass(Assert, [ + { + key: "timeout", + value: function timeout(duration) { + if (typeof duration !== "number") { + throw new Error( + "You must pass a number as the duration to assert.timeout" + ); + } + + this.test.timeout = duration; + } + + // Documents a "step", which is a string value, in a test as a passing assertion + }, + { + key: "step", + value: function step(message) { + var result = !!message; + + this.test.steps.push(message); + + return this.pushResult({ + result: result, + message: message || "You must provide a message to assert.step" + }); + } + + // Verifies the steps in a test match a given array of string values + }, + { + key: "verifySteps", + value: function verifySteps(steps, message) { + this.deepEqual(this.test.steps, steps, message); + } + + // Specify the number of expected assertions to guarantee that failed test + // (no assertions are run at all) don't slip through. + }, + { + key: "expect", + value: function expect(asserts) { + if (arguments.length === 1) { + this.test.expected = asserts; + } else { + return this.test.expected; + } + } + + // Put a hold on processing and return a function that will release it a maximum of once. + }, + { + key: "async", + value: function async(count) { + var test$$1 = this.test; + + var popped = false, + acceptCallCount = count; + + if (typeof acceptCallCount === "undefined") { + acceptCallCount = 1; + } + + var resume = internalStop(test$$1); + + return function done() { + if (config.current !== test$$1) { + throw Error("assert.async callback called after test finished."); + } + + if (popped) { + test$$1.pushFailure( + "Too many calls to the `assert.async` callback", + sourceFromStacktrace(2) + ); + return; + } + + acceptCallCount -= 1; + if (acceptCallCount > 0) { + return; + } + + popped = true; + resume(); + }; + } + + // Exports test.push() to the user API + // Alias of pushResult. + }, + { + key: "push", + value: function push(result, actual, expected, message, negative) { + Logger.warn( + "assert.push is deprecated and will be removed in QUnit 3.0." + + " Please use assert.pushResult instead (https://api.qunitjs.com/assert/pushResult)." + ); + + var currentAssert = + this instanceof Assert ? this : config.current.assert; + return currentAssert.pushResult({ + result: result, + actual: actual, + expected: expected, + message: message, + negative: negative + }); + } + }, + { + key: "pushResult", + value: function pushResult(resultInfo) { + // Destructure of resultInfo = { result, actual, expected, message, negative } + var assert = this; + var currentTest = + (assert instanceof Assert && assert.test) || config.current; + + // Backwards compatibility fix. + // Allows the direct use of global exported assertions and QUnit.assert.* + // Although, it's use is not recommended as it can leak assertions + // to other tests from async tests, because we only get a reference to the current test, + // not exactly the test where assertion were intended to be called. + if (!currentTest) { + throw new Error( + "assertion outside test context, in " + sourceFromStacktrace(2) + ); + } + + if (!(assert instanceof Assert)) { + assert = currentTest.assert; + } + + return assert.test.pushResult(resultInfo); + } + }, + { + key: "ok", + value: function ok(result, message) { + if (!message) { + message = result + ? "okay" + : "failed, expected argument to be truthy, was: " + + dump.parse(result); + } + + this.pushResult({ + result: !!result, + actual: result, + expected: true, + message: message + }); + } + }, + { + key: "notOk", + value: function notOk(result, message) { + if (!message) { + message = !result + ? "okay" + : "failed, expected argument to be falsy, was: " + + dump.parse(result); + } + + this.pushResult({ + result: !result, + actual: result, + expected: false, + message: message + }); + } + }, + { + key: "equal", + value: function equal(actual, expected, message) { + // eslint-disable-next-line eqeqeq + var result = expected == actual; + + this.pushResult({ + result: result, + actual: actual, + expected: expected, + message: message + }); + } + }, + { + key: "notEqual", + value: function notEqual(actual, expected, message) { + // eslint-disable-next-line eqeqeq + var result = expected != actual; + + this.pushResult({ + result: result, + actual: actual, + expected: expected, + message: message, + negative: true + }); + } + }, + { + key: "propEqual", + value: function propEqual(actual, expected, message) { + actual = objectValues(actual); + expected = objectValues(expected); + + this.pushResult({ + result: equiv(actual, expected), + actual: actual, + expected: expected, + message: message + }); + } + }, + { + key: "notPropEqual", + value: function notPropEqual(actual, expected, message) { + actual = objectValues(actual); + expected = objectValues(expected); + + this.pushResult({ + result: !equiv(actual, expected), + actual: actual, + expected: expected, + message: message, + negative: true + }); + } + }, + { + key: "deepEqual", + value: function deepEqual(actual, expected, message) { + this.pushResult({ + result: equiv(actual, expected), + actual: actual, + expected: expected, + message: message + }); + } + }, + { + key: "notDeepEqual", + value: function notDeepEqual(actual, expected, message) { + this.pushResult({ + result: !equiv(actual, expected), + actual: actual, + expected: expected, + message: message, + negative: true + }); + } + }, + { + key: "strictEqual", + value: function strictEqual(actual, expected, message) { + this.pushResult({ + result: expected === actual, + actual: actual, + expected: expected, + message: message + }); + } + }, + { + key: "notStrictEqual", + value: function notStrictEqual(actual, expected, message) { + this.pushResult({ + result: expected !== actual, + actual: actual, + expected: expected, + message: message, + negative: true + }); + } + }, + { + key: "throws", + value: function throws(block, expected, message) { + var actual = void 0, + result = false; + + var currentTest = + (this instanceof Assert && this.test) || config.current; + + // 'expected' is optional unless doing string comparison + if (objectType(expected) === "string") { + if (message == null) { + message = expected; + expected = null; + } else { + throw new Error( + "throws/raises does not accept a string value for the expected argument.\n" + + "Use a non-string object value (e.g. regExp) instead if it's necessary." + ); + } + } + + currentTest.ignoreGlobalErrors = true; + try { + block.call(currentTest.testEnvironment); + } catch (e) { + actual = e; + } + currentTest.ignoreGlobalErrors = false; + + if (actual) { + var expectedType = objectType(expected); + + // We don't want to validate thrown error + if (!expected) { + result = true; + expected = null; + + // Expected is a regexp + } else if (expectedType === "regexp") { + result = expected.test(errorString(actual)); + + // Expected is a constructor, maybe an Error constructor + } else if ( + expectedType === "function" && + actual instanceof expected + ) { + result = true; + + // Expected is an Error object + } else if (expectedType === "object") { + result = + actual instanceof expected.constructor && + actual.name === expected.name && + actual.message === expected.message; + + // Expected is a validation function which returns true if validation passed + } else if ( + expectedType === "function" && + expected.call({}, actual) === true + ) { + expected = null; + result = true; + } + } + + currentTest.assert.pushResult({ + result: result, + actual: actual, + expected: expected, + message: message + }); + } + } + ]); + return Assert; + })(); // Provide an alternative to assert.throws(), for environments that consider throws a reserved word // Known to us are: Closure Compiler, Narwhal // eslint-disable-next-line dot-notation - Assert.prototype.raises = Assert.prototype["throws"]; /** @@ -2445,201 +2616,234 @@ * @return {String} */ function errorString(error) { - var resultErrorString = error.toString(); - - if (resultErrorString.substring(0, 7) === "[object") { - var name = error.name ? error.name.toString() : "Error"; - var message = error.message ? error.message.toString() : ""; - - if (name && message) { - return name + ": " + message; - } else if (name) { - return name; - } else if (message) { - return message; - } else { - return "Error"; - } - } else { - return resultErrorString; - } + var resultErrorString = error.toString(); + + if (resultErrorString.substring(0, 7) === "[object") { + var name = error.name ? error.name.toString() : "Error"; + var message = error.message ? error.message.toString() : ""; + + if (name && message) { + return name + ": " + message; + } else if (name) { + return name; + } else if (message) { + return message; + } else { + return "Error"; + } + } else { + return resultErrorString; + } } /* global module, exports, define */ function exportQUnit(QUnit) { + if (defined.document) { + // QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined. + if (window.QUnit && window.QUnit.version) { + throw new Error("QUnit has already been defined."); + } - if (defined.document) { + window.QUnit = QUnit; + } + + // For nodejs + if (typeof module !== "undefined" && module && module.exports) { + module.exports = QUnit; - // QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined. - if (window.QUnit && window.QUnit.version) { - throw new Error("QUnit has already been defined."); - } + // For consistency with CommonJS environments' exports + module.exports.QUnit = QUnit; + } - window.QUnit = QUnit; - } + // For CommonJS with exports, but without module.exports, like Rhino + if (typeof exports !== "undefined" && exports) { + exports.QUnit = QUnit; + } - // For nodejs - if (typeof module !== "undefined" && module && module.exports) { - module.exports = QUnit; + if (typeof define === "function" && define.amd) { + define(function() { + return QUnit; + }); + QUnit.config.autostart = false; + } - // For consistency with CommonJS environments' exports - module.exports.QUnit = QUnit; - } + // For Web/Service Workers + if ( + self$1 && + self$1.WorkerGlobalScope && + self$1 instanceof self$1.WorkerGlobalScope + ) { + self$1.QUnit = QUnit; + } + } - // For CommonJS with exports, but without module.exports, like Rhino - if (typeof exports !== "undefined" && exports) { - exports.QUnit = QUnit; - } + var SuiteReport = (function() { + function SuiteReport(name, parentSuite) { + classCallCheck(this, SuiteReport); - if (typeof define === "function" && define.amd) { - define(function () { - return QUnit; - }); - QUnit.config.autostart = false; - } + this.name = name; + this.fullName = parentSuite ? parentSuite.fullName.concat(name) : []; - // For Web/Service Workers - if (self$1 && self$1.WorkerGlobalScope && self$1 instanceof self$1.WorkerGlobalScope) { - self$1.QUnit = QUnit; - } - } + this.tests = []; + this.childSuites = []; - var SuiteReport = function () { - function SuiteReport(name, parentSuite) { - classCallCheck(this, SuiteReport); - - this.name = name; - this.fullName = parentSuite ? parentSuite.fullName.concat(name) : []; - - this.tests = []; - this.childSuites = []; - - if (parentSuite) { - parentSuite.pushChildSuite(this); - } - } - - createClass(SuiteReport, [{ - key: "start", - value: function start(recordTime) { - if (recordTime) { - this._startTime = Date.now(); - } - - return { - name: this.name, - fullName: this.fullName.slice(), - tests: this.tests.map(function (test) { - return test.start(); - }), - childSuites: this.childSuites.map(function (suite) { - return suite.start(); - }), - testCounts: { - total: this.getTestCounts().total - } - }; - } - }, { - key: "end", - value: function end(recordTime) { - if (recordTime) { - this._endTime = Date.now(); - } - - return { - name: this.name, - fullName: this.fullName.slice(), - tests: this.tests.map(function (test) { - return test.end(); - }), - childSuites: this.childSuites.map(function (suite) { - return suite.end(); - }), - testCounts: this.getTestCounts(), - runtime: this.getRuntime(), - status: this.getStatus() - }; - } - }, { - key: "pushChildSuite", - value: function pushChildSuite(suite) { - this.childSuites.push(suite); - } - }, { - key: "pushTest", - value: function pushTest(test) { - this.tests.push(test); - } - }, { - key: "getRuntime", - value: function getRuntime() { - return this._endTime - this._startTime; - } - }, { - key: "getTestCounts", - value: function getTestCounts() { - var counts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { passed: 0, failed: 0, skipped: 0, todo: 0, total: 0 }; - - counts = this.tests.reduce(function (counts, test) { - if (test.valid) { - counts[test.getStatus()]++; - counts.total++; - } - - return counts; - }, counts); - - return this.childSuites.reduce(function (counts, suite) { - return suite.getTestCounts(counts); - }, counts); - } - }, { - key: "getStatus", - value: function getStatus() { - var _getTestCounts = this.getTestCounts(), - total = _getTestCounts.total, - failed = _getTestCounts.failed, - skipped = _getTestCounts.skipped, - todo = _getTestCounts.todo; - - if (failed) { - return "failed"; - } else { - if (skipped === total) { - return "skipped"; - } else if (todo === total) { - return "todo"; - } else { - return "passed"; - } - } - } - }]); - return SuiteReport; - }(); + if (parentSuite) { + parentSuite.pushChildSuite(this); + } + } + + createClass(SuiteReport, [ + { + key: "start", + value: function start(recordTime) { + if (recordTime) { + this._startTime = Date.now(); + } + + return { + name: this.name, + fullName: this.fullName.slice(), + tests: this.tests.map(function(test) { + return test.start(); + }), + childSuites: this.childSuites.map(function(suite) { + return suite.start(); + }), + testCounts: { + total: this.getTestCounts().total + } + }; + } + }, + { + key: "end", + value: function end(recordTime) { + if (recordTime) { + this._endTime = Date.now(); + } + + return { + name: this.name, + fullName: this.fullName.slice(), + tests: this.tests.map(function(test) { + return test.end(); + }), + childSuites: this.childSuites.map(function(suite) { + return suite.end(); + }), + testCounts: this.getTestCounts(), + runtime: this.getRuntime(), + status: this.getStatus() + }; + } + }, + { + key: "pushChildSuite", + value: function pushChildSuite(suite) { + this.childSuites.push(suite); + } + }, + { + key: "pushTest", + value: function pushTest(test) { + this.tests.push(test); + } + }, + { + key: "getRuntime", + value: function getRuntime() { + return this._endTime - this._startTime; + } + }, + { + key: "getTestCounts", + value: function getTestCounts() { + var counts = + arguments.length > 0 && arguments[0] !== undefined + ? arguments[0] + : { passed: 0, failed: 0, skipped: 0, todo: 0, total: 0 }; + + counts = this.tests.reduce(function(counts, test) { + if (test.valid) { + counts[test.getStatus()]++; + counts.total++; + } + + return counts; + }, counts); + + return this.childSuites.reduce(function(counts, suite) { + return suite.getTestCounts(counts); + }, counts); + } + }, + { + key: "getStatus", + value: function getStatus() { + var _getTestCounts = this.getTestCounts(), + total = _getTestCounts.total, + failed = _getTestCounts.failed, + skipped = _getTestCounts.skipped, + todo = _getTestCounts.todo; + + if (failed) { + return "failed"; + } else { + if (skipped === total) { + return "skipped"; + } else if (todo === total) { + return "todo"; + } else { + return "passed"; + } + } + } + } + ]); + return SuiteReport; + })(); // Handle an unhandled exception. By convention, returns true if further // error handling should be suppressed and false otherwise. // In this case, we will only suppress further error handling if the // "ignoreGlobalErrors" configuration option is enabled. function onError(error) { - for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - - if (config.current) { - if (config.current.ignoreGlobalErrors) { - return true; - } - pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args)); - } else { - test("global failure", extend(function () { - pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args)); - }, { validTest: true })); - } - - return false; + for ( + var _len = arguments.length, + args = Array(_len > 1 ? _len - 1 : 0), + _key = 1; + _key < _len; + _key++ + ) { + args[_key - 1] = arguments[_key]; + } + + if (config.current) { + if (config.current.ignoreGlobalErrors) { + return true; + } + pushFailure.apply( + undefined, + [error.message, error.fileName + ":" + error.lineNumber].concat(args) + ); + } else { + test( + "global failure", + extend( + function() { + pushFailure.apply( + undefined, + [error.message, error.fileName + ":" + error.lineNumber].concat( + args + ) + ); + }, + { validTest: true } + ) + ); + } + + return false; } var focused = false; @@ -2662,222 +2866,234 @@ QUnit.version = "2.4.1"; function createModule(name, testEnvironment, modifiers) { - var parentModule = moduleStack.length ? moduleStack.slice(-1)[0] : null; - var moduleName = parentModule !== null ? [parentModule.name, name].join(" > ") : name; - var parentSuite = parentModule ? parentModule.suiteReport : globalSuite; - - var skip$$1 = parentModule !== null && parentModule.skip || modifiers.skip; - var todo$$1 = parentModule !== null && parentModule.todo || modifiers.todo; - - var module = { - name: moduleName, - parentModule: parentModule, - tests: [], - moduleId: generateHash(moduleName), - testsRun: 0, - unskippedTestsRun: 0, - childModules: [], - suiteReport: new SuiteReport(name, parentSuite), - - // Pass along `skip` and `todo` properties from parent module, in case - // there is one, to childs. And use own otherwise. - // This property will be used to mark own tests and tests of child suites - // as either `skipped` or `todo`. - skip: skip$$1, - todo: skip$$1 ? false : todo$$1 - }; - - var env = {}; - if (parentModule) { - parentModule.childModules.push(module); - extend(env, parentModule.testEnvironment); - } - extend(env, testEnvironment); - module.testEnvironment = env; - - config.modules.push(module); - return module; - } + var parentModule = moduleStack.length ? moduleStack.slice(-1)[0] : null; + var moduleName = + parentModule !== null ? [parentModule.name, name].join(" > ") : name; + var parentSuite = parentModule ? parentModule.suiteReport : globalSuite; + + var skip$$1 = + (parentModule !== null && parentModule.skip) || modifiers.skip; + var todo$$1 = + (parentModule !== null && parentModule.todo) || modifiers.todo; + + var module = { + name: moduleName, + parentModule: parentModule, + tests: [], + moduleId: generateHash(moduleName), + testsRun: 0, + unskippedTestsRun: 0, + childModules: [], + suiteReport: new SuiteReport(name, parentSuite), + + // Pass along `skip` and `todo` properties from parent module, in case + // there is one, to childs. And use own otherwise. + // This property will be used to mark own tests and tests of child suites + // as either `skipped` or `todo`. + skip: skip$$1, + todo: skip$$1 ? false : todo$$1 + }; - function processModule(name, options, executeNow) { - var modifiers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; - - var module = createModule(name, options, modifiers); - - // Move any hooks to a 'hooks' object - var testEnvironment = module.testEnvironment; - var hooks = module.hooks = {}; - - setHookFromEnvironment(hooks, testEnvironment, "before"); - setHookFromEnvironment(hooks, testEnvironment, "beforeEach"); - setHookFromEnvironment(hooks, testEnvironment, "afterEach"); - setHookFromEnvironment(hooks, testEnvironment, "after"); - - function setHookFromEnvironment(hooks, environment, name) { - var potentialHook = environment[name]; - hooks[name] = typeof potentialHook === "function" ? [potentialHook] : []; - delete environment[name]; - } - - var moduleFns = { - before: setHookFunction(module, "before"), - beforeEach: setHookFunction(module, "beforeEach"), - afterEach: setHookFunction(module, "afterEach"), - after: setHookFunction(module, "after") - }; - - var currentModule = config.currentModule; - if (objectType(executeNow) === "function") { - moduleStack.push(module); - config.currentModule = module; - executeNow.call(module.testEnvironment, moduleFns); - moduleStack.pop(); - module = module.parentModule || currentModule; - } - - config.currentModule = module; - } + var env = {}; + if (parentModule) { + parentModule.childModules.push(module); + extend(env, parentModule.testEnvironment); + } + extend(env, testEnvironment); + module.testEnvironment = env; - // TODO: extract this to a new file alongside its related functions - function module$1(name, options, executeNow) { - if (focused) { - return; - } - - if (arguments.length === 2) { - if (objectType(options) === "function") { - executeNow = options; - options = undefined; - } - } - - processModule(name, options, executeNow); + config.modules.push(module); + return module; } - module$1.only = function () { - if (focused) { - return; - } + function processModule(name, options, executeNow) { + var modifiers = + arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; - config.modules.length = 0; - config.queue.length = 0; + var module = createModule(name, options, modifiers); - module$1.apply(undefined, arguments); + // Move any hooks to a 'hooks' object + var testEnvironment = module.testEnvironment; + var hooks = (module.hooks = {}); - focused = true; - }; + setHookFromEnvironment(hooks, testEnvironment, "before"); + setHookFromEnvironment(hooks, testEnvironment, "beforeEach"); + setHookFromEnvironment(hooks, testEnvironment, "afterEach"); + setHookFromEnvironment(hooks, testEnvironment, "after"); - module$1.skip = function (name, options, executeNow) { - if (focused) { - return; - } + function setHookFromEnvironment(hooks, environment, name) { + var potentialHook = environment[name]; + hooks[name] = typeof potentialHook === "function" ? [potentialHook] : []; + delete environment[name]; + } - if (arguments.length === 2) { - if (objectType(options) === "function") { - executeNow = options; - options = undefined; - } - } + var moduleFns = { + before: setHookFunction(module, "before"), + beforeEach: setHookFunction(module, "beforeEach"), + afterEach: setHookFunction(module, "afterEach"), + after: setHookFunction(module, "after") + }; - processModule(name, options, executeNow, { skip: true }); - }; + var currentModule = config.currentModule; + if (objectType(executeNow) === "function") { + moduleStack.push(module); + config.currentModule = module; + executeNow.call(module.testEnvironment, moduleFns); + moduleStack.pop(); + module = module.parentModule || currentModule; + } - module$1.todo = function (name, options, executeNow) { - if (focused) { - return; - } + config.currentModule = module; + } - if (arguments.length === 2) { - if (objectType(options) === "function") { - executeNow = options; - options = undefined; - } - } + // TODO: extract this to a new file alongside its related functions + function module$1(name, options, executeNow) { + if (focused) { + return; + } - processModule(name, options, executeNow, { todo: true }); - }; + if (arguments.length === 2) { + if (objectType(options) === "function") { + executeNow = options; + options = undefined; + } + } - extend(QUnit, { - on: on, + processModule(name, options, executeNow); + } - module: module$1, + module$1.only = function() { + if (focused) { + return; + } - test: test, + config.modules.length = 0; + config.queue.length = 0; - todo: todo, + module$1.apply(undefined, arguments); - skip: skip, + focused = true; + }; - only: only, + module$1.skip = function(name, options, executeNow) { + if (focused) { + return; + } - start: function start(count) { - var globalStartAlreadyCalled = globalStartCalled; + if (arguments.length === 2) { + if (objectType(options) === "function") { + executeNow = options; + options = undefined; + } + } - if (!config.current) { - globalStartCalled = true; + processModule(name, options, executeNow, { skip: true }); + }; - if (runStarted) { - throw new Error("Called start() while test already started running"); - } else if (globalStartAlreadyCalled || count > 1) { - throw new Error("Called start() outside of a test context too many times"); - } else if (config.autostart) { - throw new Error("Called start() outside of a test context when " + "QUnit.config.autostart was true"); - } else if (!config.pageLoaded) { + module$1.todo = function(name, options, executeNow) { + if (focused) { + return; + } - // The page isn't completely loaded yet, so we set autostart and then - // load if we're in Node or wait for the browser's load event. - config.autostart = true; + if (arguments.length === 2) { + if (objectType(options) === "function") { + executeNow = options; + options = undefined; + } + } - // Starts from Node even if .load was not previously called. We still return - // early otherwise we'll wind up "beginning" twice. - if (!defined.document) { - QUnit.load(); - } + processModule(name, options, executeNow, { todo: true }); + }; - return; - } - } else { - throw new Error("QUnit.start cannot be called inside a test context."); - } + extend(QUnit, { + on: on, + + module: module$1, + + test: test, + + todo: todo, + + skip: skip, + + only: only, + + start: function start(count) { + var globalStartAlreadyCalled = globalStartCalled; + + if (!config.current) { + globalStartCalled = true; + + if (runStarted) { + throw new Error("Called start() while test already started running"); + } else if (globalStartAlreadyCalled || count > 1) { + throw new Error( + "Called start() outside of a test context too many times" + ); + } else if (config.autostart) { + throw new Error( + "Called start() outside of a test context when " + + "QUnit.config.autostart was true" + ); + } else if (!config.pageLoaded) { + // The page isn't completely loaded yet, so we set autostart and then + // load if we're in Node or wait for the browser's load event. + config.autostart = true; + + // Starts from Node even if .load was not previously called. We still return + // early otherwise we'll wind up "beginning" twice. + if (!defined.document) { + QUnit.load(); + } + + return; + } + } else { + throw new Error("QUnit.start cannot be called inside a test context."); + } - scheduleBegin(); - }, + scheduleBegin(); + }, - config: config, + config: config, - is: is, + is: is, - objectType: objectType, + objectType: objectType, - extend: extend, + extend: extend, - load: function load() { - config.pageLoaded = true; + load: function load() { + config.pageLoaded = true; - // Initialize the configuration options - extend(config, { - stats: { all: 0, bad: 0 }, - started: 0, - updateRate: 1000, - autostart: true, - filter: "" - }, true); + // Initialize the configuration options + extend( + config, + { + stats: { all: 0, bad: 0 }, + started: 0, + updateRate: 1000, + autostart: true, + filter: "" + }, + true + ); - if (!runStarted) { - config.blocking = false; + if (!runStarted) { + config.blocking = false; - if (config.autostart) { - scheduleBegin(); - } - } - }, + if (config.autostart) { + scheduleBegin(); + } + } + }, - stack: function stack(offset) { - offset = (offset || 0) + 2; - return sourceFromStacktrace(offset); - }, + stack: function stack(offset) { + offset = (offset || 0) + 2; + return sourceFromStacktrace(offset); + }, - onError: onError + onError: onError }); QUnit.pushFailure = pushFailure; @@ -2888,1126 +3104,1339 @@ registerLoggingCallbacks(QUnit); function scheduleBegin() { + runStarted = true; - runStarted = true; - - // Add a slight delay to allow definition of more modules and tests. - if (defined.setTimeout) { - setTimeout(function () { - begin(); - }, 13); - } else { - begin(); - } + // Add a slight delay to allow definition of more modules and tests. + if (defined.setTimeout) { + setTimeout(function() { + begin(); + }, 13); + } else { + begin(); + } } function begin() { - var i, - l, - modulesLog = []; - - // If the test run hasn't officially begun yet - if (!config.started) { - - // Record the time of the test run's beginning - config.started = now(); - - // Delete the loose unnamed module if unused. - if (config.modules[0].name === "" && config.modules[0].tests.length === 0) { - config.modules.shift(); - } - - // Avoid unnecessary information by not logging modules' test environments - for (i = 0, l = config.modules.length; i < l; i++) { - modulesLog.push({ - name: config.modules[i].name, - tests: config.modules[i].tests - }); - } - - // The test run is officially beginning now - emit("runStart", globalSuite.start(true)); - runLoggingCallbacks("begin", { - totalTests: Test.count, - modules: modulesLog - }); - } - - config.blocking = false; - ProcessingQueue.advance(); + var i, + l, + modulesLog = []; + + // If the test run hasn't officially begun yet + if (!config.started) { + // Record the time of the test run's beginning + config.started = now(); + + // Delete the loose unnamed module if unused. + if ( + config.modules[0].name === "" && + config.modules[0].tests.length === 0 + ) { + config.modules.shift(); + } + + // Avoid unnecessary information by not logging modules' test environments + for (i = 0, l = config.modules.length; i < l; i++) { + modulesLog.push({ + name: config.modules[i].name, + tests: config.modules[i].tests + }); + } + + // The test run is officially beginning now + emit("runStart", globalSuite.start(true)); + runLoggingCallbacks("begin", { + totalTests: Test.count, + modules: modulesLog + }); + } + + config.blocking = false; + ProcessingQueue.advance(); } function setHookFunction(module, hookName) { - return function setHook(callback) { - module.hooks[hookName].push(callback); - }; + return function setHook(callback) { + module.hooks[hookName].push(callback); + }; } exportQUnit(QUnit); - (function () { + (function() { + if (typeof window === "undefined" || typeof document === "undefined") { + return; + } + + var config = QUnit.config, + hasOwn = Object.prototype.hasOwnProperty; + + // Stores fixture HTML for resetting later + function storeFixture() { + // Avoid overwriting user-defined values + if (hasOwn.call(config, "fixture")) { + return; + } + + var fixture = document.getElementById("qunit-fixture"); + if (fixture) { + config.fixture = fixture.innerHTML; + } + } + + QUnit.begin(storeFixture); - if (typeof window === "undefined" || typeof document === "undefined") { - return; - } + // Resets the fixture DOM element if available. + function resetFixture() { + if (config.fixture == null) { + return; + } + + var fixture = document.getElementById("qunit-fixture"); + if (fixture) { + fixture.innerHTML = config.fixture; + } + } - var config = QUnit.config, - hasOwn = Object.prototype.hasOwnProperty; + QUnit.testStart(resetFixture); + })(); - // Stores fixture HTML for resetting later - function storeFixture() { + (function() { + // Only interact with URLs via window.location + var location = typeof window !== "undefined" && window.location; + if (!location) { + return; + } - // Avoid overwriting user-defined values - if (hasOwn.call(config, "fixture")) { - return; - } + var urlParams = getUrlParams(); - var fixture = document.getElementById("qunit-fixture"); - if (fixture) { - config.fixture = fixture.innerHTML; - } - } + QUnit.urlParams = urlParams; - QUnit.begin(storeFixture); + // Match module/test by inclusion in an array + QUnit.config.moduleId = [].concat(urlParams.moduleId || []); + QUnit.config.testId = [].concat(urlParams.testId || []); - // Resets the fixture DOM element if available. - function resetFixture() { - if (config.fixture == null) { - return; - } + // Exact case-insensitive match of the module name + QUnit.config.module = urlParams.module; - var fixture = document.getElementById("qunit-fixture"); - if (fixture) { - fixture.innerHTML = config.fixture; - } - } + // Regular expression or case-insenstive substring match against "moduleName: testName" + QUnit.config.filter = urlParams.filter; - QUnit.testStart(resetFixture); - })(); + // Test order randomization + if (urlParams.seed === true) { + // Generate a random seed if the option is specified without a value + QUnit.config.seed = Math.random() + .toString(36) + .slice(2); + } else if (urlParams.seed) { + QUnit.config.seed = urlParams.seed; + } - (function () { - - // Only interact with URLs via window.location - var location = typeof window !== "undefined" && window.location; - if (!location) { - return; - } - - var urlParams = getUrlParams(); - - QUnit.urlParams = urlParams; - - // Match module/test by inclusion in an array - QUnit.config.moduleId = [].concat(urlParams.moduleId || []); - QUnit.config.testId = [].concat(urlParams.testId || []); - - // Exact case-insensitive match of the module name - QUnit.config.module = urlParams.module; - - // Regular expression or case-insenstive substring match against "moduleName: testName" - QUnit.config.filter = urlParams.filter; - - // Test order randomization - if (urlParams.seed === true) { - - // Generate a random seed if the option is specified without a value - QUnit.config.seed = Math.random().toString(36).slice(2); - } else if (urlParams.seed) { - QUnit.config.seed = urlParams.seed; - } - - // Add URL-parameter-mapped config values with UI form rendering data - QUnit.config.urlConfig.push({ - id: "hidepassed", - label: "Hide passed tests", - tooltip: "Only show tests and assertions that fail. Stored as query-strings." - }, { - id: "noglobals", - label: "Check for Globals", - tooltip: "Enabling this will test if any test introduces new properties on the " + "global object (`window` in Browsers). Stored as query-strings." - }, { - id: "notrycatch", - label: "No try-catch", - tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + "exceptions in IE reasonable. Stored as query-strings." - }); - - QUnit.begin(function () { - var i, - option, - urlConfig = QUnit.config.urlConfig; - - for (i = 0; i < urlConfig.length; i++) { - - // Options can be either strings or objects with nonempty "id" properties - option = QUnit.config.urlConfig[i]; - if (typeof option !== "string") { - option = option.id; - } - - if (QUnit.config[option] === undefined) { - QUnit.config[option] = urlParams[option]; - } - } - }); - - function getUrlParams() { - var i, param, name, value; - var urlParams = Object.create(null); - var params = location.search.slice(1).split("&"); - var length = params.length; - - for (i = 0; i < length; i++) { - if (params[i]) { - param = params[i].split("="); - name = decodeQueryParam(param[0]); - - // Allow just a key to turn on a flag, e.g., test.html?noglobals - value = param.length === 1 || decodeQueryParam(param.slice(1).join("=")); - if (name in urlParams) { - urlParams[name] = [].concat(urlParams[name], value); - } else { - urlParams[name] = value; - } - } - } - - return urlParams; - } - - function decodeQueryParam(param) { - return decodeURIComponent(param.replace(/\+/g, "%20")); - } + // Add URL-parameter-mapped config values with UI form rendering data + QUnit.config.urlConfig.push( + { + id: "hidepassed", + label: "Hide passed tests", + tooltip: + "Only show tests and assertions that fail. Stored as query-strings." + }, + { + id: "noglobals", + label: "Check for Globals", + tooltip: + "Enabling this will test if any test introduces new properties on the " + + "global object (`window` in Browsers). Stored as query-strings." + }, + { + id: "notrycatch", + label: "No try-catch", + tooltip: + "Enabling this will run tests outside of a try-catch block. Makes debugging " + + "exceptions in IE reasonable. Stored as query-strings." + } + ); + + QUnit.begin(function() { + var i, + option, + urlConfig = QUnit.config.urlConfig; + + for (i = 0; i < urlConfig.length; i++) { + // Options can be either strings or objects with nonempty "id" properties + option = QUnit.config.urlConfig[i]; + if (typeof option !== "string") { + option = option.id; + } + + if (QUnit.config[option] === undefined) { + QUnit.config[option] = urlParams[option]; + } + } + }); + + function getUrlParams() { + var i, param, name, value; + var urlParams = Object.create(null); + var params = location.search.slice(1).split("&"); + var length = params.length; + + for (i = 0; i < length; i++) { + if (params[i]) { + param = params[i].split("="); + name = decodeQueryParam(param[0]); + + // Allow just a key to turn on a flag, e.g., test.html?noglobals + value = + param.length === 1 || decodeQueryParam(param.slice(1).join("=")); + if (name in urlParams) { + urlParams[name] = [].concat(urlParams[name], value); + } else { + urlParams[name] = value; + } + } + } + + return urlParams; + } + + function decodeQueryParam(param) { + return decodeURIComponent(param.replace(/\+/g, "%20")); + } })(); var stats = { - passedTests: 0, - failedTests: 0, - skippedTests: 0, - todoTests: 0 + passedTests: 0, + failedTests: 0, + skippedTests: 0, + todoTests: 0 }; // Escape text for attribute or text content. function escapeText(s) { - if (!s) { - return ""; - } - s = s + ""; - - // Both single quotes and double quotes (for attributes) - return s.replace(/['"<>&]/g, function (s) { - switch (s) { - case "'": - return "'"; - case "\"": - return """; - case "<": - return "<"; - case ">": - return ">"; - case "&": - return "&"; - } - }); + if (!s) { + return ""; + } + s = s + ""; + + // Both single quotes and double quotes (for attributes) + return s.replace(/['"<>&]/g, function(s) { + switch (s) { + case "'": + return "'"; + case '"': + return """; + case "<": + return "<"; + case ">": + return ">"; + case "&": + return "&"; + } + }); } - (function () { - - // Don't load the HTML Reporter on non-browser environments - if (typeof window === "undefined" || !window.document) { - return; - } - - var config = QUnit.config, - document$$1 = window.document, - collapseNext = false, - hasOwn = Object.prototype.hasOwnProperty, - unfilteredUrl = setUrl({ filter: undefined, module: undefined, - moduleId: undefined, testId: undefined }), - modulesList = []; - - function addEvent(elem, type, fn) { - elem.addEventListener(type, fn, false); - } - - function removeEvent(elem, type, fn) { - elem.removeEventListener(type, fn, false); - } - - function addEvents(elems, type, fn) { - var i = elems.length; - while (i--) { - addEvent(elems[i], type, fn); - } - } - - function hasClass(elem, name) { - return (" " + elem.className + " ").indexOf(" " + name + " ") >= 0; - } - - function addClass(elem, name) { - if (!hasClass(elem, name)) { - elem.className += (elem.className ? " " : "") + name; - } - } - - function toggleClass(elem, name, force) { - if (force || typeof force === "undefined" && !hasClass(elem, name)) { - addClass(elem, name); - } else { - removeClass(elem, name); - } - } - - function removeClass(elem, name) { - var set = " " + elem.className + " "; - - // Class name may appear multiple times - while (set.indexOf(" " + name + " ") >= 0) { - set = set.replace(" " + name + " ", " "); - } - - // Trim for prettiness - elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, ""); - } - - function id(name) { - return document$$1.getElementById && document$$1.getElementById(name); - } - - function abortTests() { - var abortButton = id("qunit-abort-tests-button"); - if (abortButton) { - abortButton.disabled = true; - abortButton.innerHTML = "Aborting..."; - } - QUnit.config.queue.length = 0; - return false; - } - - function interceptNavigation(ev) { - applyUrlParams(); - - if (ev && ev.preventDefault) { - ev.preventDefault(); - } - - return false; - } - - function getUrlConfigHtml() { - var i, - j, - val, - escaped, - escapedTooltip, - selection = false, - urlConfig = config.urlConfig, - urlConfigHtml = ""; - - for (i = 0; i < urlConfig.length; i++) { - - // Options can be either strings or objects with nonempty "id" properties - val = config.urlConfig[i]; - if (typeof val === "string") { - val = { - id: val, - label: val - }; - } - - escaped = escapeText(val.id); - escapedTooltip = escapeText(val.tooltip); - - if (!val.value || typeof val.value === "string") { - urlConfigHtml += ""; - } else { - urlConfigHtml += ""; - } - } - - return urlConfigHtml; - } - - // Handle "click" events on toolbar checkboxes and "change" for select menus. - // Updates the URL with the new state of `config.urlConfig` values. - function toolbarChanged() { - var updatedUrl, - value, - tests, - field = this, - params = {}; - - // Detect if field is a select menu or a checkbox - if ("selectedIndex" in field) { - value = field.options[field.selectedIndex].value || undefined; - } else { - value = field.checked ? field.defaultValue || true : undefined; - } - - params[field.name] = value; - updatedUrl = setUrl(params); - - // Check if we can apply the change without a page refresh - if ("hidepassed" === field.name && "replaceState" in window.history) { - QUnit.urlParams[field.name] = value; - config[field.name] = value || false; - tests = id("qunit-tests"); - if (tests) { - toggleClass(tests, "hidepass", value || false); - } - window.history.replaceState(null, "", updatedUrl); - } else { - window.location = updatedUrl; - } - } - - function setUrl(params) { - var key, - arrValue, - i, - querystring = "?", - location = window.location; - - params = QUnit.extend(QUnit.extend({}, QUnit.urlParams), params); - - for (key in params) { - - // Skip inherited or undefined properties - if (hasOwn.call(params, key) && params[key] !== undefined) { - - // Output a parameter for each value of this key - // (but usually just one) - arrValue = [].concat(params[key]); - for (i = 0; i < arrValue.length; i++) { - querystring += encodeURIComponent(key); - if (arrValue[i] !== true) { - querystring += "=" + encodeURIComponent(arrValue[i]); - } - querystring += "&"; - } - } - } - return location.protocol + "//" + location.host + location.pathname + querystring.slice(0, -1); - } - - function applyUrlParams() { - var i, - selectedModules = [], - modulesList = id("qunit-modulefilter-dropdown-list").getElementsByTagName("input"), - filter = id("qunit-filter-input").value; - - for (i = 0; i < modulesList.length; i++) { - if (modulesList[i].checked) { - selectedModules.push(modulesList[i].value); - } - } - - window.location = setUrl({ - filter: filter === "" ? undefined : filter, - moduleId: selectedModules.length === 0 ? undefined : selectedModules, - - // Remove module and testId filter - module: undefined, - testId: undefined - }); - } - - function toolbarUrlConfigContainer() { - var urlConfigContainer = document$$1.createElement("span"); - - urlConfigContainer.innerHTML = getUrlConfigHtml(); - addClass(urlConfigContainer, "qunit-url-config"); - - addEvents(urlConfigContainer.getElementsByTagName("input"), "change", toolbarChanged); - addEvents(urlConfigContainer.getElementsByTagName("select"), "change", toolbarChanged); - - return urlConfigContainer; - } - - function abortTestsButton() { - var button = document$$1.createElement("button"); - button.id = "qunit-abort-tests-button"; - button.innerHTML = "Abort"; - addEvent(button, "click", abortTests); - return button; - } - - function toolbarLooseFilter() { - var filter = document$$1.createElement("form"), - label = document$$1.createElement("label"), - input = document$$1.createElement("input"), - button = document$$1.createElement("button"); - - addClass(filter, "qunit-filter"); - - label.innerHTML = "Filter: "; - - input.type = "text"; - input.value = config.filter || ""; - input.name = "filter"; - input.id = "qunit-filter-input"; - - button.innerHTML = "Go"; - - label.appendChild(input); - - filter.appendChild(label); - filter.appendChild(document$$1.createTextNode(" ")); - filter.appendChild(button); - addEvent(filter, "submit", interceptNavigation); - - return filter; - } - - function moduleListHtml() { - var i, - checked, - html = ""; - - for (i = 0; i < config.modules.length; i++) { - if (config.modules[i].name !== "") { - checked = config.moduleId.indexOf(config.modules[i].moduleId) > -1; - html += "
  • "; - } - } - - return html; - } - - function toolbarModuleFilter() { - var allCheckbox, - commit, - reset, - moduleFilter = document$$1.createElement("form"), - label = document$$1.createElement("label"), - moduleSearch = document$$1.createElement("input"), - dropDown = document$$1.createElement("div"), - actions = document$$1.createElement("span"), - dropDownList = document$$1.createElement("ul"), - dirty = false; - - moduleSearch.id = "qunit-modulefilter-search"; - addEvent(moduleSearch, "input", searchInput); - addEvent(moduleSearch, "input", searchFocus); - addEvent(moduleSearch, "focus", searchFocus); - addEvent(moduleSearch, "click", searchFocus); - - label.id = "qunit-modulefilter-search-container"; - label.innerHTML = "Module: "; - label.appendChild(moduleSearch); - - actions.id = "qunit-modulefilter-actions"; - actions.innerHTML = "" + "" + ""; - allCheckbox = actions.lastChild.firstChild; - commit = actions.firstChild; - reset = commit.nextSibling; - addEvent(commit, "click", applyUrlParams); - - dropDownList.id = "qunit-modulefilter-dropdown-list"; - dropDownList.innerHTML = moduleListHtml(); - - dropDown.id = "qunit-modulefilter-dropdown"; - dropDown.style.display = "none"; - dropDown.appendChild(actions); - dropDown.appendChild(dropDownList); - addEvent(dropDown, "change", selectionChange); - selectionChange(); - - moduleFilter.id = "qunit-modulefilter"; - moduleFilter.appendChild(label); - moduleFilter.appendChild(dropDown); - addEvent(moduleFilter, "submit", interceptNavigation); - addEvent(moduleFilter, "reset", function () { - - // Let the reset happen, then update styles - window.setTimeout(selectionChange); - }); - - // Enables show/hide for the dropdown - function searchFocus() { - if (dropDown.style.display !== "none") { - return; - } - - dropDown.style.display = "block"; - addEvent(document$$1, "click", hideHandler); - addEvent(document$$1, "keydown", hideHandler); - - // Hide on Escape keydown or outside-container click - function hideHandler(e) { - var inContainer = moduleFilter.contains(e.target); - - if (e.keyCode === 27 || !inContainer) { - if (e.keyCode === 27 && inContainer) { - moduleSearch.focus(); - } - dropDown.style.display = "none"; - removeEvent(document$$1, "click", hideHandler); - removeEvent(document$$1, "keydown", hideHandler); - moduleSearch.value = ""; - searchInput(); - } - } - } - - // Processes module search box input - function searchInput() { - var i, - item, - searchText = moduleSearch.value.toLowerCase(), - listItems = dropDownList.children; - - for (i = 0; i < listItems.length; i++) { - item = listItems[i]; - if (!searchText || item.textContent.toLowerCase().indexOf(searchText) > -1) { - item.style.display = ""; - } else { - item.style.display = "none"; - } - } - } - - // Processes selection changes - function selectionChange(evt) { - var i, - item, - checkbox = evt && evt.target || allCheckbox, - modulesList = dropDownList.getElementsByTagName("input"), - selectedNames = []; - - toggleClass(checkbox.parentNode, "checked", checkbox.checked); - - dirty = false; - if (checkbox.checked && checkbox !== allCheckbox) { - allCheckbox.checked = false; - removeClass(allCheckbox.parentNode, "checked"); - } - for (i = 0; i < modulesList.length; i++) { - item = modulesList[i]; - if (!evt) { - toggleClass(item.parentNode, "checked", item.checked); - } else if (checkbox === allCheckbox && checkbox.checked) { - item.checked = false; - removeClass(item.parentNode, "checked"); - } - dirty = dirty || item.checked !== item.defaultChecked; - if (item.checked) { - selectedNames.push(item.parentNode.textContent); - } - } - - commit.style.display = reset.style.display = dirty ? "" : "none"; - moduleSearch.placeholder = selectedNames.join(", ") || allCheckbox.parentNode.textContent; - moduleSearch.title = "Type to filter list. Current selection:\n" + (selectedNames.join("\n") || allCheckbox.parentNode.textContent); - } - - return moduleFilter; - } - - function appendToolbar() { - var toolbar = id("qunit-testrunner-toolbar"); - - if (toolbar) { - toolbar.appendChild(toolbarUrlConfigContainer()); - toolbar.appendChild(toolbarModuleFilter()); - toolbar.appendChild(toolbarLooseFilter()); - toolbar.appendChild(document$$1.createElement("div")).className = "clearfix"; - } - } - - function appendHeader() { - var header = id("qunit-header"); - - if (header) { - header.innerHTML = "" + header.innerHTML + " "; - } - } - - function appendBanner() { - var banner = id("qunit-banner"); - - if (banner) { - banner.className = ""; - } - } - - function appendTestResults() { - var tests = id("qunit-tests"), - result = id("qunit-testresult"), - controls; - - if (result) { - result.parentNode.removeChild(result); - } - - if (tests) { - tests.innerHTML = ""; - result = document$$1.createElement("p"); - result.id = "qunit-testresult"; - result.className = "result"; - tests.parentNode.insertBefore(result, tests); - result.innerHTML = "
    Running...
     
    " + "
    " + "
    "; - controls = id("qunit-testresult-controls"); - } - - if (controls) { - controls.appendChild(abortTestsButton()); - } - } - - function appendFilteredTest() { - var testId = QUnit.config.testId; - if (!testId || testId.length <= 0) { - return ""; - } - return "
    Rerunning selected tests: " + escapeText(testId.join(", ")) + " Run all tests
    "; - } - - function appendUserAgent() { - var userAgent = id("qunit-userAgent"); - - if (userAgent) { - userAgent.innerHTML = ""; - userAgent.appendChild(document$$1.createTextNode("QUnit " + QUnit.version + "; " + navigator.userAgent)); - } - } - - function appendInterface() { - var qunit = id("qunit"); - - if (qunit) { - qunit.innerHTML = "

    " + escapeText(document$$1.title) + "

    " + "

    " + "
    " + appendFilteredTest() + "

    " + "
      "; - } - - appendHeader(); - appendBanner(); - appendTestResults(); - appendUserAgent(); - appendToolbar(); - } - - function appendTestsList(modules) { - var i, l, x, z, test, moduleObj; - - for (i = 0, l = modules.length; i < l; i++) { - moduleObj = modules[i]; - - for (x = 0, z = moduleObj.tests.length; x < z; x++) { - test = moduleObj.tests[x]; - - appendTest(test.name, test.testId, moduleObj.name); - } - } - } - - function appendTest(name, testId, moduleName) { - var title, - rerunTrigger, - testBlock, - assertList, - tests = id("qunit-tests"); - - if (!tests) { - return; - } - - title = document$$1.createElement("strong"); - title.innerHTML = getNameHtml(name, moduleName); - - rerunTrigger = document$$1.createElement("a"); - rerunTrigger.innerHTML = "Rerun"; - rerunTrigger.href = setUrl({ testId: testId }); - - testBlock = document$$1.createElement("li"); - testBlock.appendChild(title); - testBlock.appendChild(rerunTrigger); - testBlock.id = "qunit-test-output-" + testId; - - assertList = document$$1.createElement("ol"); - assertList.className = "qunit-assert-list"; - - testBlock.appendChild(assertList); - - tests.appendChild(testBlock); - } - - // HTML Reporter initialization and load - QUnit.begin(function (details) { - var i, moduleObj, tests; - - // Sort modules by name for the picker - for (i = 0; i < details.modules.length; i++) { - moduleObj = details.modules[i]; - if (moduleObj.name) { - modulesList.push(moduleObj.name); - } - } - modulesList.sort(function (a, b) { - return a.localeCompare(b); - }); - - // Initialize QUnit elements - appendInterface(); - appendTestsList(details.modules); - tests = id("qunit-tests"); - if (tests && config.hidepassed) { - addClass(tests, "hidepass"); - } - }); - - QUnit.done(function (details) { - var banner = id("qunit-banner"), - tests = id("qunit-tests"), - abortButton = id("qunit-abort-tests-button"), - totalTests = stats.passedTests + stats.skippedTests + stats.todoTests + stats.failedTests, - html = [totalTests, " tests completed in ", details.runtime, " milliseconds, with ", stats.failedTests, " failed, ", stats.skippedTests, " skipped, and ", stats.todoTests, " todo.
      ", "", details.passed, " assertions of ", details.total, " passed, ", details.failed, " failed."].join(""), - test, - assertLi, - assertList; - - // Update remaing tests to aborted - if (abortButton && abortButton.disabled) { - html = "Tests aborted after " + details.runtime + " milliseconds."; - - for (var i = 0; i < tests.children.length; i++) { - test = tests.children[i]; - if (test.className === "" || test.className === "running") { - test.className = "aborted"; - assertList = test.getElementsByTagName("ol")[0]; - assertLi = document$$1.createElement("li"); - assertLi.className = "fail"; - assertLi.innerHTML = "Test aborted."; - assertList.appendChild(assertLi); - } - } - } - - if (banner && (!abortButton || abortButton.disabled === false)) { - banner.className = stats.failedTests ? "qunit-fail" : "qunit-pass"; - } - - if (abortButton) { - abortButton.parentNode.removeChild(abortButton); - } - - if (tests) { - id("qunit-testresult-display").innerHTML = html; - } - - if (config.altertitle && document$$1.title) { - - // Show ✖ for good, ✔ for bad suite result in title - // use escape sequences in case file gets loaded with non-utf-8 - // charset - document$$1.title = [stats.failedTests ? "\u2716" : "\u2714", document$$1.title.replace(/^[\u2714\u2716] /i, "")].join(" "); - } - - // Scroll back to top to show results - if (config.scrolltop && window.scrollTo) { - window.scrollTo(0, 0); - } - }); - - function getNameHtml(name, module) { - var nameHtml = ""; - - if (module) { - nameHtml = "" + escapeText(module) + ": "; - } - - nameHtml += "" + escapeText(name) + ""; - - return nameHtml; - } - - QUnit.testStart(function (details) { - var running, testBlock, bad; - - testBlock = id("qunit-test-output-" + details.testId); - if (testBlock) { - testBlock.className = "running"; - } else { - - // Report later registered tests - appendTest(details.name, details.testId, details.module); - } - - running = id("qunit-testresult-display"); - if (running) { - bad = QUnit.config.reorder && details.previousFailure; - - running.innerHTML = [bad ? "Rerunning previously failed test:
      " : "Running:
      ", getNameHtml(details.name, details.module)].join(""); - } - }); - - function stripHtml(string) { - - // Strip tags, html entity and whitespaces - return string.replace(/<\/?[^>]+(>|$)/g, "").replace(/\"/g, "").replace(/\s+/g, ""); - } - - QUnit.log(function (details) { - var assertList, - assertLi, - message, - expected, - actual, - diff, - showDiff = false, - testItem = id("qunit-test-output-" + details.testId); - - if (!testItem) { - return; - } - - message = escapeText(details.message) || (details.result ? "okay" : "failed"); - message = "" + message + ""; - message += "@ " + details.runtime + " ms"; - - // The pushFailure doesn't provide details.expected - // when it calls, it's implicit to also not show expected and diff stuff - // Also, we need to check details.expected existence, as it can exist and be undefined - if (!details.result && hasOwn.call(details, "expected")) { - if (details.negative) { - expected = "NOT " + QUnit.dump.parse(details.expected); - } else { - expected = QUnit.dump.parse(details.expected); - } - - actual = QUnit.dump.parse(details.actual); - message += ""; - - if (actual !== expected) { - - message += ""; - - if (typeof details.actual === "number" && typeof details.expected === "number") { - if (!isNaN(details.actual) && !isNaN(details.expected)) { - showDiff = true; - diff = details.actual - details.expected; - diff = (diff > 0 ? "+" : "") + diff; - } - } else if (typeof details.actual !== "boolean" && typeof details.expected !== "boolean") { - diff = QUnit.diff(expected, actual); - - // don't show diff if there is zero overlap - showDiff = stripHtml(diff).length !== stripHtml(expected).length + stripHtml(actual).length; - } - - if (showDiff) { - message += ""; - } - } else if (expected.indexOf("[object Array]") !== -1 || expected.indexOf("[object Object]") !== -1) { - message += ""; - } else { - message += ""; - } - - if (details.source) { - message += ""; - } - - message += "
      Expected:
      " + escapeText(expected) + "
      Result:
      " + escapeText(actual) + "
      Diff:
      " + diff + "
      Message: " + "Diff suppressed as the depth of object is more than current max depth (" + QUnit.config.maxDepth + ").

      Hint: Use QUnit.dump.maxDepth to " + " run with a higher max depth or " + "Rerun without max depth.

      Message: " + "Diff suppressed as the expected and actual results have an equivalent" + " serialization
      Source:
      " + escapeText(details.source) + "
      "; - - // This occurs when pushFailure is set and we have an extracted stack trace - } else if (!details.result && details.source) { - message += "" + "" + "
      Source:
      " + escapeText(details.source) + "
      "; - } - - assertList = testItem.getElementsByTagName("ol")[0]; - - assertLi = document$$1.createElement("li"); - assertLi.className = details.result ? "pass" : "fail"; - assertLi.innerHTML = message; - assertList.appendChild(assertLi); - }); - - QUnit.testDone(function (details) { - var testTitle, - time, - testItem, - assertList, - good, - bad, - testCounts, - skipped, - sourceName, - tests = id("qunit-tests"); - - if (!tests) { - return; - } - - testItem = id("qunit-test-output-" + details.testId); - - assertList = testItem.getElementsByTagName("ol")[0]; - - good = details.passed; - bad = details.failed; - - // This test passed if it has no unexpected failed assertions - var testPassed = details.failed > 0 ? details.todo : !details.todo; - - if (testPassed) { - - // Collapse the passing tests - addClass(assertList, "qunit-collapsed"); - } else if (config.collapse) { - if (!collapseNext) { - - // Skip collapsing the first failing test - collapseNext = true; - } else { - - // Collapse remaining tests - addClass(assertList, "qunit-collapsed"); - } - } - - // The testItem.firstChild is the test name - testTitle = testItem.firstChild; - - testCounts = bad ? "" + bad + ", " + "" + good + ", " : ""; - - testTitle.innerHTML += " (" + testCounts + details.assertions.length + ")"; - - if (details.skipped) { - stats.skippedTests++; - - testItem.className = "skipped"; - skipped = document$$1.createElement("em"); - skipped.className = "qunit-skipped-label"; - skipped.innerHTML = "skipped"; - testItem.insertBefore(skipped, testTitle); - } else { - addEvent(testTitle, "click", function () { - toggleClass(assertList, "qunit-collapsed"); - }); - - testItem.className = testPassed ? "pass" : "fail"; - - if (details.todo) { - var todoLabel = document$$1.createElement("em"); - todoLabel.className = "qunit-todo-label"; - todoLabel.innerHTML = "todo"; - testItem.className += " todo"; - testItem.insertBefore(todoLabel, testTitle); - } - - time = document$$1.createElement("span"); - time.className = "runtime"; - time.innerHTML = details.runtime + " ms"; - testItem.insertBefore(time, assertList); - - if (!testPassed) { - stats.failedTests++; - } else if (details.todo) { - stats.todoTests++; - } else { - stats.passedTests++; - } - } - - // Show the source of the test when showing assertions - if (details.source) { - sourceName = document$$1.createElement("p"); - sourceName.innerHTML = "Source: " + details.source; - addClass(sourceName, "qunit-source"); - if (testPassed) { - addClass(sourceName, "qunit-collapsed"); - } - addEvent(testTitle, "click", function () { - toggleClass(sourceName, "qunit-collapsed"); - }); - testItem.appendChild(sourceName); - } - }); - - // Avoid readyState issue with phantomjs - // Ref: #818 - var notPhantom = function (p) { - return !(p && p.version && p.version.major > 0); - }(window.phantom); - - if (notPhantom && document$$1.readyState === "complete") { - QUnit.load(); - } else { - addEvent(window, "load", QUnit.load); - } - - // Wrap window.onerror. We will call the original window.onerror to see if - // the existing handler fully handles the error; if not, we will call the - // QUnit.onError function. - var originalWindowOnError = window.onerror; - - // Cover uncaught exceptions - // Returning true will suppress the default browser handler, - // returning false will let it run. - window.onerror = function (message, fileName, lineNumber) { - var ret = false; - if (originalWindowOnError) { - for (var _len = arguments.length, args = Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) { - args[_key - 3] = arguments[_key]; - } - - ret = originalWindowOnError.call.apply(originalWindowOnError, [this, message, fileName, lineNumber].concat(args)); - } - - // Treat return value as window.onerror itself does, - // Only do our handling if not suppressed. - if (ret !== true) { - var error = { - message: message, - fileName: fileName, - lineNumber: lineNumber - }; - - ret = QUnit.onError(error); - } - - return ret; - }; - })(); + (function() { + // Don't load the HTML Reporter on non-browser environments + if (typeof window === "undefined" || !window.document) { + return; + } - /* - * This file is a modified version of google-diff-match-patch's JavaScript implementation - * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js), - * modifications are licensed as more fully set forth in LICENSE.txt. - * - * The original source of google-diff-match-patch is attributable and licensed as follows: - * - * Copyright 2006 Google Inc. - * https://code.google.com/p/google-diff-match-patch/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + var config = QUnit.config, + document$$1 = window.document, + collapseNext = false, + hasOwn = Object.prototype.hasOwnProperty, + unfilteredUrl = setUrl({ + filter: undefined, + module: undefined, + moduleId: undefined, + testId: undefined + }), + modulesList = []; + + function addEvent(elem, type, fn) { + elem.addEventListener(type, fn, false); + } + + function removeEvent(elem, type, fn) { + elem.removeEventListener(type, fn, false); + } + + function addEvents(elems, type, fn) { + var i = elems.length; + while (i--) { + addEvent(elems[i], type, fn); + } + } + + function hasClass(elem, name) { + return (" " + elem.className + " ").indexOf(" " + name + " ") >= 0; + } + + function addClass(elem, name) { + if (!hasClass(elem, name)) { + elem.className += (elem.className ? " " : "") + name; + } + } + + function toggleClass(elem, name, force) { + if (force || (typeof force === "undefined" && !hasClass(elem, name))) { + addClass(elem, name); + } else { + removeClass(elem, name); + } + } + + function removeClass(elem, name) { + var set = " " + elem.className + " "; + + // Class name may appear multiple times + while (set.indexOf(" " + name + " ") >= 0) { + set = set.replace(" " + name + " ", " "); + } + + // Trim for prettiness + elem.className = + typeof set.trim === "function" + ? set.trim() + : set.replace(/^\s+|\s+$/g, ""); + } + + function id(name) { + return document$$1.getElementById && document$$1.getElementById(name); + } + + function abortTests() { + var abortButton = id("qunit-abort-tests-button"); + if (abortButton) { + abortButton.disabled = true; + abortButton.innerHTML = "Aborting..."; + } + QUnit.config.queue.length = 0; + return false; + } + + function interceptNavigation(ev) { + applyUrlParams(); + + if (ev && ev.preventDefault) { + ev.preventDefault(); + } + + return false; + } + + function getUrlConfigHtml() { + var i, + j, + val, + escaped, + escapedTooltip, + selection = false, + urlConfig = config.urlConfig, + urlConfigHtml = ""; + + for (i = 0; i < urlConfig.length; i++) { + // Options can be either strings or objects with nonempty "id" properties + val = config.urlConfig[i]; + if (typeof val === "string") { + val = { + id: val, + label: val + }; + } + + escaped = escapeText(val.id); + escapedTooltip = escapeText(val.tooltip); + + if (!val.value || typeof val.value === "string") { + urlConfigHtml += + ""; + } else { + urlConfigHtml += + ""; + } + } + + return urlConfigHtml; + } + + // Handle "click" events on toolbar checkboxes and "change" for select menus. + // Updates the URL with the new state of `config.urlConfig` values. + function toolbarChanged() { + var updatedUrl, + value, + tests, + field = this, + params = {}; + + // Detect if field is a select menu or a checkbox + if ("selectedIndex" in field) { + value = field.options[field.selectedIndex].value || undefined; + } else { + value = field.checked ? field.defaultValue || true : undefined; + } + + params[field.name] = value; + updatedUrl = setUrl(params); + + // Check if we can apply the change without a page refresh + if ("hidepassed" === field.name && "replaceState" in window.history) { + QUnit.urlParams[field.name] = value; + config[field.name] = value || false; + tests = id("qunit-tests"); + if (tests) { + toggleClass(tests, "hidepass", value || false); + } + window.history.replaceState(null, "", updatedUrl); + } else { + window.location = updatedUrl; + } + } + + function setUrl(params) { + var key, + arrValue, + i, + querystring = "?", + location = window.location; + + params = QUnit.extend(QUnit.extend({}, QUnit.urlParams), params); + + for (key in params) { + // Skip inherited or undefined properties + if (hasOwn.call(params, key) && params[key] !== undefined) { + // Output a parameter for each value of this key + // (but usually just one) + arrValue = [].concat(params[key]); + for (i = 0; i < arrValue.length; i++) { + querystring += encodeURIComponent(key); + if (arrValue[i] !== true) { + querystring += "=" + encodeURIComponent(arrValue[i]); + } + querystring += "&"; + } + } + } + return ( + location.protocol + + "//" + + location.host + + location.pathname + + querystring.slice(0, -1) + ); + } + + function applyUrlParams() { + var i, + selectedModules = [], + modulesList = id( + "qunit-modulefilter-dropdown-list" + ).getElementsByTagName("input"), + filter = id("qunit-filter-input").value; + + for (i = 0; i < modulesList.length; i++) { + if (modulesList[i].checked) { + selectedModules.push(modulesList[i].value); + } + } + + window.location = setUrl({ + filter: filter === "" ? undefined : filter, + moduleId: selectedModules.length === 0 ? undefined : selectedModules, + + // Remove module and testId filter + module: undefined, + testId: undefined + }); + } + + function toolbarUrlConfigContainer() { + var urlConfigContainer = document$$1.createElement("span"); + + urlConfigContainer.innerHTML = getUrlConfigHtml(); + addClass(urlConfigContainer, "qunit-url-config"); + + addEvents( + urlConfigContainer.getElementsByTagName("input"), + "change", + toolbarChanged + ); + addEvents( + urlConfigContainer.getElementsByTagName("select"), + "change", + toolbarChanged + ); + + return urlConfigContainer; + } + + function abortTestsButton() { + var button = document$$1.createElement("button"); + button.id = "qunit-abort-tests-button"; + button.innerHTML = "Abort"; + addEvent(button, "click", abortTests); + return button; + } + + function toolbarLooseFilter() { + var filter = document$$1.createElement("form"), + label = document$$1.createElement("label"), + input = document$$1.createElement("input"), + button = document$$1.createElement("button"); + + addClass(filter, "qunit-filter"); + + label.innerHTML = "Filter: "; + + input.type = "text"; + input.value = config.filter || ""; + input.name = "filter"; + input.id = "qunit-filter-input"; + + button.innerHTML = "Go"; + + label.appendChild(input); + + filter.appendChild(label); + filter.appendChild(document$$1.createTextNode(" ")); + filter.appendChild(button); + addEvent(filter, "submit", interceptNavigation); + + return filter; + } + + function moduleListHtml() { + var i, + checked, + html = ""; + + for (i = 0; i < config.modules.length; i++) { + if (config.modules[i].name !== "") { + checked = config.moduleId.indexOf(config.modules[i].moduleId) > -1; + html += + "
    1. "; + } + } + + return html; + } + + function toolbarModuleFilter() { + var allCheckbox, + commit, + reset, + moduleFilter = document$$1.createElement("form"), + label = document$$1.createElement("label"), + moduleSearch = document$$1.createElement("input"), + dropDown = document$$1.createElement("div"), + actions = document$$1.createElement("span"), + dropDownList = document$$1.createElement("ul"), + dirty = false; + + moduleSearch.id = "qunit-modulefilter-search"; + addEvent(moduleSearch, "input", searchInput); + addEvent(moduleSearch, "input", searchFocus); + addEvent(moduleSearch, "focus", searchFocus); + addEvent(moduleSearch, "click", searchFocus); + + label.id = "qunit-modulefilter-search-container"; + label.innerHTML = "Module: "; + label.appendChild(moduleSearch); + + actions.id = "qunit-modulefilter-actions"; + actions.innerHTML = + "" + + "" + + ""; + allCheckbox = actions.lastChild.firstChild; + commit = actions.firstChild; + reset = commit.nextSibling; + addEvent(commit, "click", applyUrlParams); + + dropDownList.id = "qunit-modulefilter-dropdown-list"; + dropDownList.innerHTML = moduleListHtml(); + + dropDown.id = "qunit-modulefilter-dropdown"; + dropDown.style.display = "none"; + dropDown.appendChild(actions); + dropDown.appendChild(dropDownList); + addEvent(dropDown, "change", selectionChange); + selectionChange(); + + moduleFilter.id = "qunit-modulefilter"; + moduleFilter.appendChild(label); + moduleFilter.appendChild(dropDown); + addEvent(moduleFilter, "submit", interceptNavigation); + addEvent(moduleFilter, "reset", function() { + // Let the reset happen, then update styles + window.setTimeout(selectionChange); + }); + + // Enables show/hide for the dropdown + function searchFocus() { + if (dropDown.style.display !== "none") { + return; + } + + dropDown.style.display = "block"; + addEvent(document$$1, "click", hideHandler); + addEvent(document$$1, "keydown", hideHandler); + + // Hide on Escape keydown or outside-container click + function hideHandler(e) { + var inContainer = moduleFilter.contains(e.target); + + if (e.keyCode === 27 || !inContainer) { + if (e.keyCode === 27 && inContainer) { + moduleSearch.focus(); + } + dropDown.style.display = "none"; + removeEvent(document$$1, "click", hideHandler); + removeEvent(document$$1, "keydown", hideHandler); + moduleSearch.value = ""; + searchInput(); + } + } + } + + // Processes module search box input + function searchInput() { + var i, + item, + searchText = moduleSearch.value.toLowerCase(), + listItems = dropDownList.children; + + for (i = 0; i < listItems.length; i++) { + item = listItems[i]; + if ( + !searchText || + item.textContent.toLowerCase().indexOf(searchText) > -1 + ) { + item.style.display = ""; + } else { + item.style.display = "none"; + } + } + } + + // Processes selection changes + function selectionChange(evt) { + var i, + item, + checkbox = (evt && evt.target) || allCheckbox, + modulesList = dropDownList.getElementsByTagName("input"), + selectedNames = []; + + toggleClass(checkbox.parentNode, "checked", checkbox.checked); + + dirty = false; + if (checkbox.checked && checkbox !== allCheckbox) { + allCheckbox.checked = false; + removeClass(allCheckbox.parentNode, "checked"); + } + for (i = 0; i < modulesList.length; i++) { + item = modulesList[i]; + if (!evt) { + toggleClass(item.parentNode, "checked", item.checked); + } else if (checkbox === allCheckbox && checkbox.checked) { + item.checked = false; + removeClass(item.parentNode, "checked"); + } + dirty = dirty || item.checked !== item.defaultChecked; + if (item.checked) { + selectedNames.push(item.parentNode.textContent); + } + } + + commit.style.display = reset.style.display = dirty ? "" : "none"; + moduleSearch.placeholder = + selectedNames.join(", ") || allCheckbox.parentNode.textContent; + moduleSearch.title = + "Type to filter list. Current selection:\n" + + (selectedNames.join("\n") || allCheckbox.parentNode.textContent); + } + + return moduleFilter; + } + + function appendToolbar() { + var toolbar = id("qunit-testrunner-toolbar"); + + if (toolbar) { + toolbar.appendChild(toolbarUrlConfigContainer()); + toolbar.appendChild(toolbarModuleFilter()); + toolbar.appendChild(toolbarLooseFilter()); + toolbar.appendChild(document$$1.createElement("div")).className = + "clearfix"; + } + } + + function appendHeader() { + var header = id("qunit-header"); + + if (header) { + header.innerHTML = + "" + + header.innerHTML + + " "; + } + } + + function appendBanner() { + var banner = id("qunit-banner"); + + if (banner) { + banner.className = ""; + } + } + + function appendTestResults() { + var tests = id("qunit-tests"), + result = id("qunit-testresult"), + controls; + + if (result) { + result.parentNode.removeChild(result); + } + + if (tests) { + tests.innerHTML = ""; + result = document$$1.createElement("p"); + result.id = "qunit-testresult"; + result.className = "result"; + tests.parentNode.insertBefore(result, tests); + result.innerHTML = + '
      Running...
       
      ' + + '
      ' + + '
      '; + controls = id("qunit-testresult-controls"); + } + + if (controls) { + controls.appendChild(abortTestsButton()); + } + } + + function appendFilteredTest() { + var testId = QUnit.config.testId; + if (!testId || testId.length <= 0) { + return ""; + } + return ( + "
      Rerunning selected tests: " + + escapeText(testId.join(", ")) + + " Run all tests
      " + ); + } + + function appendUserAgent() { + var userAgent = id("qunit-userAgent"); + + if (userAgent) { + userAgent.innerHTML = ""; + userAgent.appendChild( + document$$1.createTextNode( + "QUnit " + QUnit.version + "; " + navigator.userAgent + ) + ); + } + } + + function appendInterface() { + var qunit = id("qunit"); + + if (qunit) { + qunit.innerHTML = + "

      " + + escapeText(document$$1.title) + + "

      " + + "

      " + + "
      " + + appendFilteredTest() + + "

      " + + "
        "; + } + + appendHeader(); + appendBanner(); + appendTestResults(); + appendUserAgent(); + appendToolbar(); + } + + function appendTestsList(modules) { + var i, l, x, z, test, moduleObj; + + for (i = 0, l = modules.length; i < l; i++) { + moduleObj = modules[i]; + + for (x = 0, z = moduleObj.tests.length; x < z; x++) { + test = moduleObj.tests[x]; + + appendTest(test.name, test.testId, moduleObj.name); + } + } + } + + function appendTest(name, testId, moduleName) { + var title, + rerunTrigger, + testBlock, + assertList, + tests = id("qunit-tests"); + + if (!tests) { + return; + } + + title = document$$1.createElement("strong"); + title.innerHTML = getNameHtml(name, moduleName); + + rerunTrigger = document$$1.createElement("a"); + rerunTrigger.innerHTML = "Rerun"; + rerunTrigger.href = setUrl({ testId: testId }); + + testBlock = document$$1.createElement("li"); + testBlock.appendChild(title); + testBlock.appendChild(rerunTrigger); + testBlock.id = "qunit-test-output-" + testId; + + assertList = document$$1.createElement("ol"); + assertList.className = "qunit-assert-list"; + + testBlock.appendChild(assertList); + + tests.appendChild(testBlock); + } + + // HTML Reporter initialization and load + QUnit.begin(function(details) { + var i, moduleObj, tests; + + // Sort modules by name for the picker + for (i = 0; i < details.modules.length; i++) { + moduleObj = details.modules[i]; + if (moduleObj.name) { + modulesList.push(moduleObj.name); + } + } + modulesList.sort(function(a, b) { + return a.localeCompare(b); + }); + + // Initialize QUnit elements + appendInterface(); + appendTestsList(details.modules); + tests = id("qunit-tests"); + if (tests && config.hidepassed) { + addClass(tests, "hidepass"); + } + }); + + QUnit.done(function(details) { + var banner = id("qunit-banner"), + tests = id("qunit-tests"), + abortButton = id("qunit-abort-tests-button"), + totalTests = + stats.passedTests + + stats.skippedTests + + stats.todoTests + + stats.failedTests, + html = [ + totalTests, + " tests completed in ", + details.runtime, + " milliseconds, with ", + stats.failedTests, + " failed, ", + stats.skippedTests, + " skipped, and ", + stats.todoTests, + " todo.
        ", + "", + details.passed, + " assertions of ", + details.total, + " passed, ", + details.failed, + " failed." + ].join(""), + test, + assertLi, + assertList; + + // Update remaing tests to aborted + if (abortButton && abortButton.disabled) { + html = "Tests aborted after " + details.runtime + " milliseconds."; + + for (var i = 0; i < tests.children.length; i++) { + test = tests.children[i]; + if (test.className === "" || test.className === "running") { + test.className = "aborted"; + assertList = test.getElementsByTagName("ol")[0]; + assertLi = document$$1.createElement("li"); + assertLi.className = "fail"; + assertLi.innerHTML = "Test aborted."; + assertList.appendChild(assertLi); + } + } + } + + if (banner && (!abortButton || abortButton.disabled === false)) { + banner.className = stats.failedTests ? "qunit-fail" : "qunit-pass"; + } + + if (abortButton) { + abortButton.parentNode.removeChild(abortButton); + } + + if (tests) { + id("qunit-testresult-display").innerHTML = html; + } + + if (config.altertitle && document$$1.title) { + // Show ✖ for good, ✔ for bad suite result in title + // use escape sequences in case file gets loaded with non-utf-8 + // charset + document$$1.title = [ + stats.failedTests ? "\u2716" : "\u2714", + document$$1.title.replace(/^[\u2714\u2716] /i, "") + ].join(" "); + } + + // Scroll back to top to show results + if (config.scrolltop && window.scrollTo) { + window.scrollTo(0, 0); + } + }); + + function getNameHtml(name, module) { + var nameHtml = ""; + + if (module) { + nameHtml = + "" + escapeText(module) + ": "; + } + + nameHtml += "" + escapeText(name) + ""; + + return nameHtml; + } + + QUnit.testStart(function(details) { + var running, testBlock, bad; + + testBlock = id("qunit-test-output-" + details.testId); + if (testBlock) { + testBlock.className = "running"; + } else { + // Report later registered tests + appendTest(details.name, details.testId, details.module); + } + + running = id("qunit-testresult-display"); + if (running) { + bad = QUnit.config.reorder && details.previousFailure; + + running.innerHTML = [ + bad ? "Rerunning previously failed test:
        " : "Running:
        ", + getNameHtml(details.name, details.module) + ].join(""); + } + }); + + function stripHtml(string) { + // Strip tags, html entity and whitespaces + return string + .replace(/<\/?[^>]+(>|$)/g, "") + .replace(/\"/g, "") + .replace(/\s+/g, ""); + } + + QUnit.log(function(details) { + var assertList, + assertLi, + message, + expected, + actual, + diff, + showDiff = false, + testItem = id("qunit-test-output-" + details.testId); + + if (!testItem) { + return; + } + + message = + escapeText(details.message) || (details.result ? "okay" : "failed"); + message = "" + message + ""; + message += "@ " + details.runtime + " ms"; + + // The pushFailure doesn't provide details.expected + // when it calls, it's implicit to also not show expected and diff stuff + // Also, we need to check details.expected existence, as it can exist and be undefined + if (!details.result && hasOwn.call(details, "expected")) { + if (details.negative) { + expected = "NOT " + QUnit.dump.parse(details.expected); + } else { + expected = QUnit.dump.parse(details.expected); + } + + actual = QUnit.dump.parse(details.actual); + message += + ""; + + if (actual !== expected) { + message += + ""; + + if ( + typeof details.actual === "number" && + typeof details.expected === "number" + ) { + if (!isNaN(details.actual) && !isNaN(details.expected)) { + showDiff = true; + diff = details.actual - details.expected; + diff = (diff > 0 ? "+" : "") + diff; + } + } else if ( + typeof details.actual !== "boolean" && + typeof details.expected !== "boolean" + ) { + diff = QUnit.diff(expected, actual); + + // don't show diff if there is zero overlap + showDiff = + stripHtml(diff).length !== + stripHtml(expected).length + stripHtml(actual).length; + } + + if (showDiff) { + message += + ""; + } + } else if ( + expected.indexOf("[object Array]") !== -1 || + expected.indexOf("[object Object]") !== -1 + ) { + message += + ""; + } else { + message += + ""; + } + + if (details.source) { + message += + ""; + } + + message += "
        Expected:
        " +
        +          escapeText(expected) +
        +          "
        Result:
        " +
        +            escapeText(actual) +
        +            "
        Diff:
        " +
        +              diff +
        +              "
        Message: " + + "Diff suppressed as the depth of object is more than current max depth (" + + QUnit.config.maxDepth + + ").

        Hint: Use QUnit.dump.maxDepth to " + + " run with a higher max depth or " + + "Rerun without max depth.

        Message: " + + "Diff suppressed as the expected and actual results have an equivalent" + + " serialization
        Source:
        " +
        +            escapeText(details.source) +
        +            "
        "; + + // This occurs when pushFailure is set and we have an extracted stack trace + } else if (!details.result && details.source) { + message += + "" + + "" + + "
        Source:
        " +
        +          escapeText(details.source) +
        +          "
        "; + } + + assertList = testItem.getElementsByTagName("ol")[0]; + + assertLi = document$$1.createElement("li"); + assertLi.className = details.result ? "pass" : "fail"; + assertLi.innerHTML = message; + assertList.appendChild(assertLi); + }); + + QUnit.testDone(function(details) { + var testTitle, + time, + testItem, + assertList, + good, + bad, + testCounts, + skipped, + sourceName, + tests = id("qunit-tests"); + + if (!tests) { + return; + } + + testItem = id("qunit-test-output-" + details.testId); + + assertList = testItem.getElementsByTagName("ol")[0]; + + good = details.passed; + bad = details.failed; + + // This test passed if it has no unexpected failed assertions + var testPassed = details.failed > 0 ? details.todo : !details.todo; + + if (testPassed) { + // Collapse the passing tests + addClass(assertList, "qunit-collapsed"); + } else if (config.collapse) { + if (!collapseNext) { + // Skip collapsing the first failing test + collapseNext = true; + } else { + // Collapse remaining tests + addClass(assertList, "qunit-collapsed"); + } + } + + // The testItem.firstChild is the test name + testTitle = testItem.firstChild; + + testCounts = bad + ? "" + + bad + + ", " + + "" + + good + + ", " + : ""; + + testTitle.innerHTML += + " (" + + testCounts + + details.assertions.length + + ")"; + + if (details.skipped) { + stats.skippedTests++; + + testItem.className = "skipped"; + skipped = document$$1.createElement("em"); + skipped.className = "qunit-skipped-label"; + skipped.innerHTML = "skipped"; + testItem.insertBefore(skipped, testTitle); + } else { + addEvent(testTitle, "click", function() { + toggleClass(assertList, "qunit-collapsed"); + }); + + testItem.className = testPassed ? "pass" : "fail"; + + if (details.todo) { + var todoLabel = document$$1.createElement("em"); + todoLabel.className = "qunit-todo-label"; + todoLabel.innerHTML = "todo"; + testItem.className += " todo"; + testItem.insertBefore(todoLabel, testTitle); + } + + time = document$$1.createElement("span"); + time.className = "runtime"; + time.innerHTML = details.runtime + " ms"; + testItem.insertBefore(time, assertList); + + if (!testPassed) { + stats.failedTests++; + } else if (details.todo) { + stats.todoTests++; + } else { + stats.passedTests++; + } + } + + // Show the source of the test when showing assertions + if (details.source) { + sourceName = document$$1.createElement("p"); + sourceName.innerHTML = "Source: " + details.source; + addClass(sourceName, "qunit-source"); + if (testPassed) { + addClass(sourceName, "qunit-collapsed"); + } + addEvent(testTitle, "click", function() { + toggleClass(sourceName, "qunit-collapsed"); + }); + testItem.appendChild(sourceName); + } + }); + + // Avoid readyState issue with phantomjs + // Ref: #818 + var notPhantom = (function(p) { + return !(p && p.version && p.version.major > 0); + })(window.phantom); + + if (notPhantom && document$$1.readyState === "complete") { + QUnit.load(); + } else { + addEvent(window, "load", QUnit.load); + } + + // Wrap window.onerror. We will call the original window.onerror to see if + // the existing handler fully handles the error; if not, we will call the + // QUnit.onError function. + var originalWindowOnError = window.onerror; + + // Cover uncaught exceptions + // Returning true will suppress the default browser handler, + // returning false will let it run. + window.onerror = function(message, fileName, lineNumber) { + var ret = false; + if (originalWindowOnError) { + for ( + var _len = arguments.length, + args = Array(_len > 3 ? _len - 3 : 0), + _key = 3; + _key < _len; + _key++ + ) { + args[_key - 3] = arguments[_key]; + } + + ret = originalWindowOnError.call.apply( + originalWindowOnError, + [this, message, fileName, lineNumber].concat(args) + ); + } + + // Treat return value as window.onerror itself does, + // Only do our handling if not suppressed. + if (ret !== true) { + var error = { + message: message, + fileName: fileName, + lineNumber: lineNumber + }; + + ret = QUnit.onError(error); + } + + return ret; + }; + })(); + + /* + * This file is a modified version of google-diff-match-patch's JavaScript implementation + * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js), + * modifications are licensed as more fully set forth in LICENSE.txt. + * + * The original source of google-diff-match-patch is attributable and licensed as follows: + * + * Copyright 2006 Google Inc. + * https://code.google.com/p/google-diff-match-patch/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * More Info: * https://code.google.com/p/google-diff-match-patch/ @@ -4015,136 +4444,140 @@ * Usage: QUnit.diff(expected, actual) * */ - QUnit.diff = function () { - function DiffMatchPatch() {} - - // DIFF FUNCTIONS - - /** - * The data structure representing a diff is an array of tuples: - * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] - * which means: delete 'Hello', add 'Goodbye' and keep ' world.' - */ - var DIFF_DELETE = -1, - DIFF_INSERT = 1, - DIFF_EQUAL = 0; - - /** - * Find the differences between two texts. Simplifies the problem by stripping - * any common prefix or suffix off the texts before diffing. - * @param {string} text1 Old string to be diffed. - * @param {string} text2 New string to be diffed. - * @param {boolean=} optChecklines Optional speedup flag. If present and false, - * then don't run a line-level diff first to identify the changed areas. - * Defaults to true, which does a faster, slightly less optimal diff. - * @return {!Array.} Array of diff tuples. - */ - DiffMatchPatch.prototype.DiffMain = function (text1, text2, optChecklines) { - var deadline, checklines, commonlength, commonprefix, commonsuffix, diffs; - - // The diff must be complete in up to 1 second. - deadline = new Date().getTime() + 1000; - - // Check for null inputs. - if (text1 === null || text2 === null) { - throw new Error("Null input. (DiffMain)"); - } - - // Check for equality (speedup). - if (text1 === text2) { - if (text1) { - return [[DIFF_EQUAL, text1]]; - } - return []; - } - - if (typeof optChecklines === "undefined") { - optChecklines = true; - } - - checklines = optChecklines; - - // Trim off common prefix (speedup). - commonlength = this.diffCommonPrefix(text1, text2); - commonprefix = text1.substring(0, commonlength); - text1 = text1.substring(commonlength); - text2 = text2.substring(commonlength); - - // Trim off common suffix (speedup). - commonlength = this.diffCommonSuffix(text1, text2); - commonsuffix = text1.substring(text1.length - commonlength); - text1 = text1.substring(0, text1.length - commonlength); - text2 = text2.substring(0, text2.length - commonlength); - - // Compute the diff on the middle block. - diffs = this.diffCompute(text1, text2, checklines, deadline); - - // Restore the prefix and suffix. - if (commonprefix) { - diffs.unshift([DIFF_EQUAL, commonprefix]); - } - if (commonsuffix) { - diffs.push([DIFF_EQUAL, commonsuffix]); - } - this.diffCleanupMerge(diffs); - return diffs; - }; - - /** - * Reduce the number of edits by eliminating operationally trivial equalities. - * @param {!Array.} diffs Array of diff tuples. - */ - DiffMatchPatch.prototype.diffCleanupEfficiency = function (diffs) { - var changes, equalities, equalitiesLength, lastequality, pointer, preIns, preDel, postIns, postDel; - changes = false; - equalities = []; // Stack of indices where equalities are found. - equalitiesLength = 0; // Keeping our own length var is faster in JS. - /** @type {?string} */ - lastequality = null; - - // Always equal to diffs[equalities[equalitiesLength - 1]][1] - pointer = 0; // Index of current position. - - // Is there an insertion operation before the last equality. - preIns = false; - - // Is there a deletion operation before the last equality. - preDel = false; - - // Is there an insertion operation after the last equality. - postIns = false; - - // Is there a deletion operation after the last equality. - postDel = false; - while (pointer < diffs.length) { - - // Equality found. - if (diffs[pointer][0] === DIFF_EQUAL) { - if (diffs[pointer][1].length < 4 && (postIns || postDel)) { - - // Candidate found. - equalities[equalitiesLength++] = pointer; - preIns = postIns; - preDel = postDel; - lastequality = diffs[pointer][1]; - } else { - - // Not a candidate, and can never become one. - equalitiesLength = 0; - lastequality = null; - } - postIns = postDel = false; - - // An insertion or deletion. - } else { - - if (diffs[pointer][0] === DIFF_DELETE) { - postDel = true; - } else { - postIns = true; - } - - /* + QUnit.diff = (function() { + function DiffMatchPatch() {} + + // DIFF FUNCTIONS + + /** + * The data structure representing a diff is an array of tuples: + * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] + * which means: delete 'Hello', add 'Goodbye' and keep ' world.' + */ + var DIFF_DELETE = -1, + DIFF_INSERT = 1, + DIFF_EQUAL = 0; + + /** + * Find the differences between two texts. Simplifies the problem by stripping + * any common prefix or suffix off the texts before diffing. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean=} optChecklines Optional speedup flag. If present and false, + * then don't run a line-level diff first to identify the changed areas. + * Defaults to true, which does a faster, slightly less optimal diff. + * @return {!Array.} Array of diff tuples. + */ + DiffMatchPatch.prototype.DiffMain = function(text1, text2, optChecklines) { + var deadline, checklines, commonlength, commonprefix, commonsuffix, diffs; + + // The diff must be complete in up to 1 second. + deadline = new Date().getTime() + 1000; + + // Check for null inputs. + if (text1 === null || text2 === null) { + throw new Error("Null input. (DiffMain)"); + } + + // Check for equality (speedup). + if (text1 === text2) { + if (text1) { + return [[DIFF_EQUAL, text1]]; + } + return []; + } + + if (typeof optChecklines === "undefined") { + optChecklines = true; + } + + checklines = optChecklines; + + // Trim off common prefix (speedup). + commonlength = this.diffCommonPrefix(text1, text2); + commonprefix = text1.substring(0, commonlength); + text1 = text1.substring(commonlength); + text2 = text2.substring(commonlength); + + // Trim off common suffix (speedup). + commonlength = this.diffCommonSuffix(text1, text2); + commonsuffix = text1.substring(text1.length - commonlength); + text1 = text1.substring(0, text1.length - commonlength); + text2 = text2.substring(0, text2.length - commonlength); + + // Compute the diff on the middle block. + diffs = this.diffCompute(text1, text2, checklines, deadline); + + // Restore the prefix and suffix. + if (commonprefix) { + diffs.unshift([DIFF_EQUAL, commonprefix]); + } + if (commonsuffix) { + diffs.push([DIFF_EQUAL, commonsuffix]); + } + this.diffCleanupMerge(diffs); + return diffs; + }; + + /** + * Reduce the number of edits by eliminating operationally trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupEfficiency = function(diffs) { + var changes, + equalities, + equalitiesLength, + lastequality, + pointer, + preIns, + preDel, + postIns, + postDel; + changes = false; + equalities = []; // Stack of indices where equalities are found. + equalitiesLength = 0; // Keeping our own length var is faster in JS. + /** @type {?string} */ + lastequality = null; + + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + pointer = 0; // Index of current position. + + // Is there an insertion operation before the last equality. + preIns = false; + + // Is there a deletion operation before the last equality. + preDel = false; + + // Is there an insertion operation after the last equality. + postIns = false; + + // Is there a deletion operation after the last equality. + postDel = false; + while (pointer < diffs.length) { + // Equality found. + if (diffs[pointer][0] === DIFF_EQUAL) { + if (diffs[pointer][1].length < 4 && (postIns || postDel)) { + // Candidate found. + equalities[equalitiesLength++] = pointer; + preIns = postIns; + preDel = postDel; + lastequality = diffs[pointer][1]; + } else { + // Not a candidate, and can never become one. + equalitiesLength = 0; + lastequality = null; + } + postIns = postDel = false; + + // An insertion or deletion. + } else { + if (diffs[pointer][0] === DIFF_DELETE) { + postDel = true; + } else { + postIns = true; + } + + /* * Five types to be split: * ABXYCD * AXCD @@ -4152,910 +4585,1113 @@ * AXCD * ABXC */ - if (lastequality && (preIns && preDel && postIns && postDel || lastequality.length < 2 && preIns + preDel + postIns + postDel === 3)) { - - // Duplicate record. - diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]); - - // Change second copy to insert. - diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; - equalitiesLength--; // Throw away the equality we just deleted; - lastequality = null; - if (preIns && preDel) { - - // No changes made which could affect previous entry, keep going. - postIns = postDel = true; - equalitiesLength = 0; - } else { - equalitiesLength--; // Throw away the previous equality. - pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; - postIns = postDel = false; - } - changes = true; - } - } - pointer++; - } - - if (changes) { - this.diffCleanupMerge(diffs); - } - }; - - /** - * Convert a diff array into a pretty HTML report. - * @param {!Array.} diffs Array of diff tuples. - * @param {integer} string to be beautified. - * @return {string} HTML representation. - */ - DiffMatchPatch.prototype.diffPrettyHtml = function (diffs) { - var op, - data, - x, - html = []; - for (x = 0; x < diffs.length; x++) { - op = diffs[x][0]; // Operation (insert, delete, equal) - data = diffs[x][1]; // Text of change. - switch (op) { - case DIFF_INSERT: - html[x] = "" + escapeText(data) + ""; - break; - case DIFF_DELETE: - html[x] = "" + escapeText(data) + ""; - break; - case DIFF_EQUAL: - html[x] = "" + escapeText(data) + ""; - break; - } - } - return html.join(""); - }; - - /** - * Determine the common prefix of two strings. - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {number} The number of characters common to the start of each - * string. - */ - DiffMatchPatch.prototype.diffCommonPrefix = function (text1, text2) { - var pointermid, pointermax, pointermin, pointerstart; - - // Quick check for common null cases. - if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0)) { - return 0; - } - - // Binary search. - // Performance analysis: https://neil.fraser.name/news/2007/10/09/ - pointermin = 0; - pointermax = Math.min(text1.length, text2.length); - pointermid = pointermax; - pointerstart = 0; - while (pointermin < pointermid) { - if (text1.substring(pointerstart, pointermid) === text2.substring(pointerstart, pointermid)) { - pointermin = pointermid; - pointerstart = pointermin; - } else { - pointermax = pointermid; - } - pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); - } - return pointermid; - }; - - /** - * Determine the common suffix of two strings. - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {number} The number of characters common to the end of each string. - */ - DiffMatchPatch.prototype.diffCommonSuffix = function (text1, text2) { - var pointermid, pointermax, pointermin, pointerend; - - // Quick check for common null cases. - if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) { - return 0; - } - - // Binary search. - // Performance analysis: https://neil.fraser.name/news/2007/10/09/ - pointermin = 0; - pointermax = Math.min(text1.length, text2.length); - pointermid = pointermax; - pointerend = 0; - while (pointermin < pointermid) { - if (text1.substring(text1.length - pointermid, text1.length - pointerend) === text2.substring(text2.length - pointermid, text2.length - pointerend)) { - pointermin = pointermid; - pointerend = pointermin; - } else { - pointermax = pointermid; - } - pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); - } - return pointermid; - }; - - /** - * Find the differences between two texts. Assumes that the texts do not - * have any common prefix or suffix. - * @param {string} text1 Old string to be diffed. - * @param {string} text2 New string to be diffed. - * @param {boolean} checklines Speedup flag. If false, then don't run a - * line-level diff first to identify the changed areas. - * If true, then run a faster, slightly less optimal diff. - * @param {number} deadline Time when the diff should be complete by. - * @return {!Array.} Array of diff tuples. - * @private - */ - DiffMatchPatch.prototype.diffCompute = function (text1, text2, checklines, deadline) { - var diffs, longtext, shorttext, i, hm, text1A, text2A, text1B, text2B, midCommon, diffsA, diffsB; - - if (!text1) { - - // Just add some text (speedup). - return [[DIFF_INSERT, text2]]; - } - - if (!text2) { - - // Just delete some text (speedup). - return [[DIFF_DELETE, text1]]; - } - - longtext = text1.length > text2.length ? text1 : text2; - shorttext = text1.length > text2.length ? text2 : text1; - i = longtext.indexOf(shorttext); - if (i !== -1) { - - // Shorter text is inside the longer text (speedup). - diffs = [[DIFF_INSERT, longtext.substring(0, i)], [DIFF_EQUAL, shorttext], [DIFF_INSERT, longtext.substring(i + shorttext.length)]]; - - // Swap insertions for deletions if diff is reversed. - if (text1.length > text2.length) { - diffs[0][0] = diffs[2][0] = DIFF_DELETE; - } - return diffs; - } - - if (shorttext.length === 1) { - - // Single character string. - // After the previous speedup, the character can't be an equality. - return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; - } - - // Check to see if the problem can be split in two. - hm = this.diffHalfMatch(text1, text2); - if (hm) { - - // A half-match was found, sort out the return data. - text1A = hm[0]; - text1B = hm[1]; - text2A = hm[2]; - text2B = hm[3]; - midCommon = hm[4]; - - // Send both pairs off for separate processing. - diffsA = this.DiffMain(text1A, text2A, checklines, deadline); - diffsB = this.DiffMain(text1B, text2B, checklines, deadline); - - // Merge the results. - return diffsA.concat([[DIFF_EQUAL, midCommon]], diffsB); - } - - if (checklines && text1.length > 100 && text2.length > 100) { - return this.diffLineMode(text1, text2, deadline); - } - - return this.diffBisect(text1, text2, deadline); - }; - - /** - * Do the two texts share a substring which is at least half the length of the - * longer text? - * This speedup can produce non-minimal diffs. - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {Array.} Five element Array, containing the prefix of - * text1, the suffix of text1, the prefix of text2, the suffix of - * text2 and the common middle. Or null if there was no match. - * @private - */ - DiffMatchPatch.prototype.diffHalfMatch = function (text1, text2) { - var longtext, shorttext, dmp, text1A, text2B, text2A, text1B, midCommon, hm1, hm2, hm; - - longtext = text1.length > text2.length ? text1 : text2; - shorttext = text1.length > text2.length ? text2 : text1; - if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { - return null; // Pointless. - } - dmp = this; // 'this' becomes 'window' in a closure. - - /** - * Does a substring of shorttext exist within longtext such that the substring - * is at least half the length of longtext? - * Closure, but does not reference any external variables. - * @param {string} longtext Longer string. - * @param {string} shorttext Shorter string. - * @param {number} i Start index of quarter length substring within longtext. + if ( + lastequality && + ((preIns && preDel && postIns && postDel) || + (lastequality.length < 2 && + preIns + preDel + postIns + postDel === 3)) + ) { + // Duplicate record. + diffs.splice(equalities[equalitiesLength - 1], 0, [ + DIFF_DELETE, + lastequality + ]); + + // Change second copy to insert. + diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; + equalitiesLength--; // Throw away the equality we just deleted; + lastequality = null; + if (preIns && preDel) { + // No changes made which could affect previous entry, keep going. + postIns = postDel = true; + equalitiesLength = 0; + } else { + equalitiesLength--; // Throw away the previous equality. + pointer = + equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; + postIns = postDel = false; + } + changes = true; + } + } + pointer++; + } + + if (changes) { + this.diffCleanupMerge(diffs); + } + }; + + /** + * Convert a diff array into a pretty HTML report. + * @param {!Array.} diffs Array of diff tuples. + * @param {integer} string to be beautified. + * @return {string} HTML representation. + */ + DiffMatchPatch.prototype.diffPrettyHtml = function(diffs) { + var op, + data, + x, + html = []; + for (x = 0; x < diffs.length; x++) { + op = diffs[x][0]; // Operation (insert, delete, equal) + data = diffs[x][1]; // Text of change. + switch (op) { + case DIFF_INSERT: + html[x] = "" + escapeText(data) + ""; + break; + case DIFF_DELETE: + html[x] = "" + escapeText(data) + ""; + break; + case DIFF_EQUAL: + html[x] = "" + escapeText(data) + ""; + break; + } + } + return html.join(""); + }; + + /** + * Determine the common prefix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the start of each + * string. + */ + DiffMatchPatch.prototype.diffCommonPrefix = function(text1, text2) { + var pointermid, pointermax, pointermin, pointerstart; + + // Quick check for common null cases. + if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0)) { + return 0; + } + + // Binary search. + // Performance analysis: https://neil.fraser.name/news/2007/10/09/ + pointermin = 0; + pointermax = Math.min(text1.length, text2.length); + pointermid = pointermax; + pointerstart = 0; + while (pointermin < pointermid) { + if ( + text1.substring(pointerstart, pointermid) === + text2.substring(pointerstart, pointermid) + ) { + pointermin = pointermid; + pointerstart = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + return pointermid; + }; + + /** + * Determine the common suffix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of each string. + */ + DiffMatchPatch.prototype.diffCommonSuffix = function(text1, text2) { + var pointermid, pointermax, pointermin, pointerend; + + // Quick check for common null cases. + if ( + !text1 || + !text2 || + text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1) + ) { + return 0; + } + + // Binary search. + // Performance analysis: https://neil.fraser.name/news/2007/10/09/ + pointermin = 0; + pointermax = Math.min(text1.length, text2.length); + pointermid = pointermax; + pointerend = 0; + while (pointermin < pointermid) { + if ( + text1.substring( + text1.length - pointermid, + text1.length - pointerend + ) === + text2.substring(text2.length - pointermid, text2.length - pointerend) + ) { + pointermin = pointermid; + pointerend = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + return pointermid; + }; + + /** + * Find the differences between two texts. Assumes that the texts do not + * have any common prefix or suffix. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean} checklines Speedup flag. If false, then don't run a + * line-level diff first to identify the changed areas. + * If true, then run a faster, slightly less optimal diff. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffCompute = function( + text1, + text2, + checklines, + deadline + ) { + var diffs, + longtext, + shorttext, + i, + hm, + text1A, + text2A, + text1B, + text2B, + midCommon, + diffsA, + diffsB; + + if (!text1) { + // Just add some text (speedup). + return [[DIFF_INSERT, text2]]; + } + + if (!text2) { + // Just delete some text (speedup). + return [[DIFF_DELETE, text1]]; + } + + longtext = text1.length > text2.length ? text1 : text2; + shorttext = text1.length > text2.length ? text2 : text1; + i = longtext.indexOf(shorttext); + if (i !== -1) { + // Shorter text is inside the longer text (speedup). + diffs = [ + [DIFF_INSERT, longtext.substring(0, i)], + [DIFF_EQUAL, shorttext], + [DIFF_INSERT, longtext.substring(i + shorttext.length)] + ]; + + // Swap insertions for deletions if diff is reversed. + if (text1.length > text2.length) { + diffs[0][0] = diffs[2][0] = DIFF_DELETE; + } + return diffs; + } + + if (shorttext.length === 1) { + // Single character string. + // After the previous speedup, the character can't be an equality. + return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; + } + + // Check to see if the problem can be split in two. + hm = this.diffHalfMatch(text1, text2); + if (hm) { + // A half-match was found, sort out the return data. + text1A = hm[0]; + text1B = hm[1]; + text2A = hm[2]; + text2B = hm[3]; + midCommon = hm[4]; + + // Send both pairs off for separate processing. + diffsA = this.DiffMain(text1A, text2A, checklines, deadline); + diffsB = this.DiffMain(text1B, text2B, checklines, deadline); + + // Merge the results. + return diffsA.concat([[DIFF_EQUAL, midCommon]], diffsB); + } + + if (checklines && text1.length > 100 && text2.length > 100) { + return this.diffLineMode(text1, text2, deadline); + } + + return this.diffBisect(text1, text2, deadline); + }; + + /** + * Do the two texts share a substring which is at least half the length of the + * longer text? + * This speedup can produce non-minimal diffs. + * @param {string} text1 First string. + * @param {string} text2 Second string. * @return {Array.} Five element Array, containing the prefix of - * longtext, the suffix of longtext, the prefix of shorttext, the suffix - * of shorttext and the common middle. Or null if there was no match. + * text1, the suffix of text1, the prefix of text2, the suffix of + * text2 and the common middle. Or null if there was no match. * @private */ - function diffHalfMatchI(longtext, shorttext, i) { - var seed, j, bestCommon, prefixLength, suffixLength, bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB; - - // Start with a 1/4 length substring at position i as a seed. - seed = longtext.substring(i, i + Math.floor(longtext.length / 4)); - j = -1; - bestCommon = ""; - while ((j = shorttext.indexOf(seed, j + 1)) !== -1) { - prefixLength = dmp.diffCommonPrefix(longtext.substring(i), shorttext.substring(j)); - suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), shorttext.substring(0, j)); - if (bestCommon.length < suffixLength + prefixLength) { - bestCommon = shorttext.substring(j - suffixLength, j) + shorttext.substring(j, j + prefixLength); - bestLongtextA = longtext.substring(0, i - suffixLength); - bestLongtextB = longtext.substring(i + prefixLength); - bestShorttextA = shorttext.substring(0, j - suffixLength); - bestShorttextB = shorttext.substring(j + prefixLength); - } - } - if (bestCommon.length * 2 >= longtext.length) { - return [bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB, bestCommon]; - } else { - return null; - } - } - - // First check if the second quarter is the seed for a half-match. - hm1 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 4)); - - // Check again based on the third quarter. - hm2 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 2)); - if (!hm1 && !hm2) { - return null; - } else if (!hm2) { - hm = hm1; - } else if (!hm1) { - hm = hm2; - } else { - - // Both matched. Select the longest. - hm = hm1[4].length > hm2[4].length ? hm1 : hm2; - } - - // A half-match was found, sort out the return data. - if (text1.length > text2.length) { - text1A = hm[0]; - text1B = hm[1]; - text2A = hm[2]; - text2B = hm[3]; - } else { - text2A = hm[0]; - text2B = hm[1]; - text1A = hm[2]; - text1B = hm[3]; - } - midCommon = hm[4]; - return [text1A, text1B, text2A, text2B, midCommon]; - }; - - /** - * Do a quick line-level diff on both strings, then rediff the parts for - * greater accuracy. - * This speedup can produce non-minimal diffs. - * @param {string} text1 Old string to be diffed. - * @param {string} text2 New string to be diffed. - * @param {number} deadline Time when the diff should be complete by. - * @return {!Array.} Array of diff tuples. - * @private - */ - DiffMatchPatch.prototype.diffLineMode = function (text1, text2, deadline) { - var a, diffs, linearray, pointer, countInsert, countDelete, textInsert, textDelete, j; - - // Scan the text on a line-by-line basis first. - a = this.diffLinesToChars(text1, text2); - text1 = a.chars1; - text2 = a.chars2; - linearray = a.lineArray; - - diffs = this.DiffMain(text1, text2, false, deadline); - - // Convert the diff back to original text. - this.diffCharsToLines(diffs, linearray); - - // Eliminate freak matches (e.g. blank lines) - this.diffCleanupSemantic(diffs); - - // Rediff any replacement blocks, this time character-by-character. - // Add a dummy entry at the end. - diffs.push([DIFF_EQUAL, ""]); - pointer = 0; - countDelete = 0; - countInsert = 0; - textDelete = ""; - textInsert = ""; - while (pointer < diffs.length) { - switch (diffs[pointer][0]) { - case DIFF_INSERT: - countInsert++; - textInsert += diffs[pointer][1]; - break; - case DIFF_DELETE: - countDelete++; - textDelete += diffs[pointer][1]; - break; - case DIFF_EQUAL: - - // Upon reaching an equality, check for prior redundancies. - if (countDelete >= 1 && countInsert >= 1) { - - // Delete the offending records and add the merged ones. - diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert); - pointer = pointer - countDelete - countInsert; - a = this.DiffMain(textDelete, textInsert, false, deadline); - for (j = a.length - 1; j >= 0; j--) { - diffs.splice(pointer, 0, a[j]); - } - pointer = pointer + a.length; - } - countInsert = 0; - countDelete = 0; - textDelete = ""; - textInsert = ""; - break; - } - pointer++; - } - diffs.pop(); // Remove the dummy entry at the end. - - return diffs; - }; - - /** - * Find the 'middle snake' of a diff, split the problem in two - * and return the recursively constructed diff. - * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. - * @param {string} text1 Old string to be diffed. - * @param {string} text2 New string to be diffed. - * @param {number} deadline Time at which to bail if not yet complete. - * @return {!Array.} Array of diff tuples. - * @private - */ - DiffMatchPatch.prototype.diffBisect = function (text1, text2, deadline) { - var text1Length, text2Length, maxD, vOffset, vLength, v1, v2, x, delta, front, k1start, k1end, k2start, k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2; - - // Cache the text lengths to prevent multiple calls. - text1Length = text1.length; - text2Length = text2.length; - maxD = Math.ceil((text1Length + text2Length) / 2); - vOffset = maxD; - vLength = 2 * maxD; - v1 = new Array(vLength); - v2 = new Array(vLength); - - // Setting all elements to -1 is faster in Chrome & Firefox than mixing - // integers and undefined. - for (x = 0; x < vLength; x++) { - v1[x] = -1; - v2[x] = -1; - } - v1[vOffset + 1] = 0; - v2[vOffset + 1] = 0; - delta = text1Length - text2Length; - - // If the total number of characters is odd, then the front path will collide - // with the reverse path. - front = delta % 2 !== 0; - - // Offsets for start and end of k loop. - // Prevents mapping of space beyond the grid. - k1start = 0; - k1end = 0; - k2start = 0; - k2end = 0; - for (d = 0; d < maxD; d++) { - - // Bail out if deadline is reached. - if (new Date().getTime() > deadline) { - break; - } - - // Walk the front path one step. - for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { - k1Offset = vOffset + k1; - if (k1 === -d || k1 !== d && v1[k1Offset - 1] < v1[k1Offset + 1]) { - x1 = v1[k1Offset + 1]; - } else { - x1 = v1[k1Offset - 1] + 1; - } - y1 = x1 - k1; - while (x1 < text1Length && y1 < text2Length && text1.charAt(x1) === text2.charAt(y1)) { - x1++; - y1++; - } - v1[k1Offset] = x1; - if (x1 > text1Length) { - - // Ran off the right of the graph. - k1end += 2; - } else if (y1 > text2Length) { - - // Ran off the bottom of the graph. - k1start += 2; - } else if (front) { - k2Offset = vOffset + delta - k1; - if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) { - - // Mirror x2 onto top-left coordinate system. - x2 = text1Length - v2[k2Offset]; - if (x1 >= x2) { - - // Overlap detected. - return this.diffBisectSplit(text1, text2, x1, y1, deadline); - } - } - } - } - - // Walk the reverse path one step. - for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { - k2Offset = vOffset + k2; - if (k2 === -d || k2 !== d && v2[k2Offset - 1] < v2[k2Offset + 1]) { - x2 = v2[k2Offset + 1]; - } else { - x2 = v2[k2Offset - 1] + 1; - } - y2 = x2 - k2; - while (x2 < text1Length && y2 < text2Length && text1.charAt(text1Length - x2 - 1) === text2.charAt(text2Length - y2 - 1)) { - x2++; - y2++; - } - v2[k2Offset] = x2; - if (x2 > text1Length) { - - // Ran off the left of the graph. - k2end += 2; - } else if (y2 > text2Length) { - - // Ran off the top of the graph. - k2start += 2; - } else if (!front) { - k1Offset = vOffset + delta - k2; - if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) { - x1 = v1[k1Offset]; - y1 = vOffset + x1 - k1Offset; - - // Mirror x2 onto top-left coordinate system. - x2 = text1Length - x2; - if (x1 >= x2) { - - // Overlap detected. - return this.diffBisectSplit(text1, text2, x1, y1, deadline); - } - } - } - } - } - - // Diff took too long and hit the deadline or - // number of diffs equals number of characters, no commonality at all. - return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; - }; - - /** - * Given the location of the 'middle snake', split the diff in two parts - * and recurse. - * @param {string} text1 Old string to be diffed. - * @param {string} text2 New string to be diffed. - * @param {number} x Index of split point in text1. - * @param {number} y Index of split point in text2. - * @param {number} deadline Time at which to bail if not yet complete. - * @return {!Array.} Array of diff tuples. - * @private - */ - DiffMatchPatch.prototype.diffBisectSplit = function (text1, text2, x, y, deadline) { - var text1a, text1b, text2a, text2b, diffs, diffsb; - text1a = text1.substring(0, x); - text2a = text2.substring(0, y); - text1b = text1.substring(x); - text2b = text2.substring(y); - - // Compute both diffs serially. - diffs = this.DiffMain(text1a, text2a, false, deadline); - diffsb = this.DiffMain(text1b, text2b, false, deadline); - - return diffs.concat(diffsb); - }; - - /** - * Reduce the number of edits by eliminating semantically trivial equalities. - * @param {!Array.} diffs Array of diff tuples. - */ - DiffMatchPatch.prototype.diffCleanupSemantic = function (diffs) { - var changes, equalities, equalitiesLength, lastequality, pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2; - changes = false; - equalities = []; // Stack of indices where equalities are found. - equalitiesLength = 0; // Keeping our own length var is faster in JS. - /** @type {?string} */ - lastequality = null; - - // Always equal to diffs[equalities[equalitiesLength - 1]][1] - pointer = 0; // Index of current position. - - // Number of characters that changed prior to the equality. - lengthInsertions1 = 0; - lengthDeletions1 = 0; - - // Number of characters that changed after the equality. - lengthInsertions2 = 0; - lengthDeletions2 = 0; - while (pointer < diffs.length) { - if (diffs[pointer][0] === DIFF_EQUAL) { - // Equality found. - equalities[equalitiesLength++] = pointer; - lengthInsertions1 = lengthInsertions2; - lengthDeletions1 = lengthDeletions2; - lengthInsertions2 = 0; - lengthDeletions2 = 0; - lastequality = diffs[pointer][1]; - } else { - // An insertion or deletion. - if (diffs[pointer][0] === DIFF_INSERT) { - lengthInsertions2 += diffs[pointer][1].length; - } else { - lengthDeletions2 += diffs[pointer][1].length; - } - - // Eliminate an equality that is smaller or equal to the edits on both - // sides of it. - if (lastequality && lastequality.length <= Math.max(lengthInsertions1, lengthDeletions1) && lastequality.length <= Math.max(lengthInsertions2, lengthDeletions2)) { - - // Duplicate record. - diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]); - - // Change second copy to insert. - diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; - - // Throw away the equality we just deleted. - equalitiesLength--; - - // Throw away the previous equality (it needs to be reevaluated). - equalitiesLength--; - pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; - - // Reset the counters. - lengthInsertions1 = 0; - lengthDeletions1 = 0; - lengthInsertions2 = 0; - lengthDeletions2 = 0; - lastequality = null; - changes = true; - } - } - pointer++; - } - - // Normalize the diff. - if (changes) { - this.diffCleanupMerge(diffs); - } - - // Find any overlaps between deletions and insertions. - // e.g: abcxxxxxxdef - // -> abcxxxdef - // e.g: xxxabcdefxxx - // -> defxxxabc - // Only extract an overlap if it is as big as the edit ahead or behind it. - pointer = 1; - while (pointer < diffs.length) { - if (diffs[pointer - 1][0] === DIFF_DELETE && diffs[pointer][0] === DIFF_INSERT) { - deletion = diffs[pointer - 1][1]; - insertion = diffs[pointer][1]; - overlapLength1 = this.diffCommonOverlap(deletion, insertion); - overlapLength2 = this.diffCommonOverlap(insertion, deletion); - if (overlapLength1 >= overlapLength2) { - if (overlapLength1 >= deletion.length / 2 || overlapLength1 >= insertion.length / 2) { - - // Overlap found. Insert an equality and trim the surrounding edits. - diffs.splice(pointer, 0, [DIFF_EQUAL, insertion.substring(0, overlapLength1)]); - diffs[pointer - 1][1] = deletion.substring(0, deletion.length - overlapLength1); - diffs[pointer + 1][1] = insertion.substring(overlapLength1); - pointer++; - } - } else { - if (overlapLength2 >= deletion.length / 2 || overlapLength2 >= insertion.length / 2) { - - // Reverse overlap found. - // Insert an equality and swap and trim the surrounding edits. - diffs.splice(pointer, 0, [DIFF_EQUAL, deletion.substring(0, overlapLength2)]); - - diffs[pointer - 1][0] = DIFF_INSERT; - diffs[pointer - 1][1] = insertion.substring(0, insertion.length - overlapLength2); - diffs[pointer + 1][0] = DIFF_DELETE; - diffs[pointer + 1][1] = deletion.substring(overlapLength2); - pointer++; - } - } - pointer++; - } - pointer++; - } - }; - - /** - * Determine if the suffix of one string is the prefix of another. - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {number} The number of characters common to the end of the first - * string and the start of the second string. - * @private - */ - DiffMatchPatch.prototype.diffCommonOverlap = function (text1, text2) { - var text1Length, text2Length, textLength, best, length, pattern, found; - - // Cache the text lengths to prevent multiple calls. - text1Length = text1.length; - text2Length = text2.length; - - // Eliminate the null case. - if (text1Length === 0 || text2Length === 0) { - return 0; - } - - // Truncate the longer string. - if (text1Length > text2Length) { - text1 = text1.substring(text1Length - text2Length); - } else if (text1Length < text2Length) { - text2 = text2.substring(0, text1Length); - } - textLength = Math.min(text1Length, text2Length); - - // Quick check for the worst case. - if (text1 === text2) { - return textLength; - } - - // Start by looking for a single character match - // and increase length until no match is found. - // Performance analysis: https://neil.fraser.name/news/2010/11/04/ - best = 0; - length = 1; - while (true) { - pattern = text1.substring(textLength - length); - found = text2.indexOf(pattern); - if (found === -1) { - return best; - } - length += found; - if (found === 0 || text1.substring(textLength - length) === text2.substring(0, length)) { - best = length; - length++; - } - } - }; - - /** - * Split two texts into an array of strings. Reduce the texts to a string of - * hashes where each Unicode character represents one line. - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {{chars1: string, chars2: string, lineArray: !Array.}} - * An object containing the encoded text1, the encoded text2 and - * the array of unique strings. - * The zeroth element of the array of unique strings is intentionally blank. - * @private - */ - DiffMatchPatch.prototype.diffLinesToChars = function (text1, text2) { - var lineArray, lineHash, chars1, chars2; - lineArray = []; // E.g. lineArray[4] === 'Hello\n' - lineHash = {}; // E.g. lineHash['Hello\n'] === 4 - - // '\x00' is a valid character, but various debuggers don't like it. - // So we'll insert a junk entry to avoid generating a null character. - lineArray[0] = ""; - - /** - * Split a text into an array of strings. Reduce the texts to a string of + DiffMatchPatch.prototype.diffHalfMatch = function(text1, text2) { + var longtext, + shorttext, + dmp, + text1A, + text2B, + text2A, + text1B, + midCommon, + hm1, + hm2, + hm; + + longtext = text1.length > text2.length ? text1 : text2; + shorttext = text1.length > text2.length ? text2 : text1; + if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { + return null; // Pointless. + } + dmp = this; // 'this' becomes 'window' in a closure. + + /** + * Does a substring of shorttext exist within longtext such that the substring + * is at least half the length of longtext? + * Closure, but does not reference any external variables. + * @param {string} longtext Longer string. + * @param {string} shorttext Shorter string. + * @param {number} i Start index of quarter length substring within longtext. + * @return {Array.} Five element Array, containing the prefix of + * longtext, the suffix of longtext, the prefix of shorttext, the suffix + * of shorttext and the common middle. Or null if there was no match. + * @private + */ + function diffHalfMatchI(longtext, shorttext, i) { + var seed, + j, + bestCommon, + prefixLength, + suffixLength, + bestLongtextA, + bestLongtextB, + bestShorttextA, + bestShorttextB; + + // Start with a 1/4 length substring at position i as a seed. + seed = longtext.substring(i, i + Math.floor(longtext.length / 4)); + j = -1; + bestCommon = ""; + while ((j = shorttext.indexOf(seed, j + 1)) !== -1) { + prefixLength = dmp.diffCommonPrefix( + longtext.substring(i), + shorttext.substring(j) + ); + suffixLength = dmp.diffCommonSuffix( + longtext.substring(0, i), + shorttext.substring(0, j) + ); + if (bestCommon.length < suffixLength + prefixLength) { + bestCommon = + shorttext.substring(j - suffixLength, j) + + shorttext.substring(j, j + prefixLength); + bestLongtextA = longtext.substring(0, i - suffixLength); + bestLongtextB = longtext.substring(i + prefixLength); + bestShorttextA = shorttext.substring(0, j - suffixLength); + bestShorttextB = shorttext.substring(j + prefixLength); + } + } + if (bestCommon.length * 2 >= longtext.length) { + return [ + bestLongtextA, + bestLongtextB, + bestShorttextA, + bestShorttextB, + bestCommon + ]; + } else { + return null; + } + } + + // First check if the second quarter is the seed for a half-match. + hm1 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 4)); + + // Check again based on the third quarter. + hm2 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 2)); + if (!hm1 && !hm2) { + return null; + } else if (!hm2) { + hm = hm1; + } else if (!hm1) { + hm = hm2; + } else { + // Both matched. Select the longest. + hm = hm1[4].length > hm2[4].length ? hm1 : hm2; + } + + // A half-match was found, sort out the return data. + if (text1.length > text2.length) { + text1A = hm[0]; + text1B = hm[1]; + text2A = hm[2]; + text2B = hm[3]; + } else { + text2A = hm[0]; + text2B = hm[1]; + text1A = hm[2]; + text1B = hm[3]; + } + midCommon = hm[4]; + return [text1A, text1B, text2A, text2B, midCommon]; + }; + + /** + * Do a quick line-level diff on both strings, then rediff the parts for + * greater accuracy. + * This speedup can produce non-minimal diffs. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffLineMode = function(text1, text2, deadline) { + var a, + diffs, + linearray, + pointer, + countInsert, + countDelete, + textInsert, + textDelete, + j; + + // Scan the text on a line-by-line basis first. + a = this.diffLinesToChars(text1, text2); + text1 = a.chars1; + text2 = a.chars2; + linearray = a.lineArray; + + diffs = this.DiffMain(text1, text2, false, deadline); + + // Convert the diff back to original text. + this.diffCharsToLines(diffs, linearray); + + // Eliminate freak matches (e.g. blank lines) + this.diffCleanupSemantic(diffs); + + // Rediff any replacement blocks, this time character-by-character. + // Add a dummy entry at the end. + diffs.push([DIFF_EQUAL, ""]); + pointer = 0; + countDelete = 0; + countInsert = 0; + textDelete = ""; + textInsert = ""; + while (pointer < diffs.length) { + switch (diffs[pointer][0]) { + case DIFF_INSERT: + countInsert++; + textInsert += diffs[pointer][1]; + break; + case DIFF_DELETE: + countDelete++; + textDelete += diffs[pointer][1]; + break; + case DIFF_EQUAL: + // Upon reaching an equality, check for prior redundancies. + if (countDelete >= 1 && countInsert >= 1) { + // Delete the offending records and add the merged ones. + diffs.splice( + pointer - countDelete - countInsert, + countDelete + countInsert + ); + pointer = pointer - countDelete - countInsert; + a = this.DiffMain(textDelete, textInsert, false, deadline); + for (j = a.length - 1; j >= 0; j--) { + diffs.splice(pointer, 0, a[j]); + } + pointer = pointer + a.length; + } + countInsert = 0; + countDelete = 0; + textDelete = ""; + textInsert = ""; + break; + } + pointer++; + } + diffs.pop(); // Remove the dummy entry at the end. + + return diffs; + }; + + /** + * Find the 'middle snake' of a diff, split the problem in two + * and return the recursively constructed diff. + * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffBisect = function(text1, text2, deadline) { + var text1Length, + text2Length, + maxD, + vOffset, + vLength, + v1, + v2, + x, + delta, + front, + k1start, + k1end, + k2start, + k2end, + k2Offset, + k1Offset, + x1, + x2, + y1, + y2, + d, + k1, + k2; + + // Cache the text lengths to prevent multiple calls. + text1Length = text1.length; + text2Length = text2.length; + maxD = Math.ceil((text1Length + text2Length) / 2); + vOffset = maxD; + vLength = 2 * maxD; + v1 = new Array(vLength); + v2 = new Array(vLength); + + // Setting all elements to -1 is faster in Chrome & Firefox than mixing + // integers and undefined. + for (x = 0; x < vLength; x++) { + v1[x] = -1; + v2[x] = -1; + } + v1[vOffset + 1] = 0; + v2[vOffset + 1] = 0; + delta = text1Length - text2Length; + + // If the total number of characters is odd, then the front path will collide + // with the reverse path. + front = delta % 2 !== 0; + + // Offsets for start and end of k loop. + // Prevents mapping of space beyond the grid. + k1start = 0; + k1end = 0; + k2start = 0; + k2end = 0; + for (d = 0; d < maxD; d++) { + // Bail out if deadline is reached. + if (new Date().getTime() > deadline) { + break; + } + + // Walk the front path one step. + for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { + k1Offset = vOffset + k1; + if (k1 === -d || (k1 !== d && v1[k1Offset - 1] < v1[k1Offset + 1])) { + x1 = v1[k1Offset + 1]; + } else { + x1 = v1[k1Offset - 1] + 1; + } + y1 = x1 - k1; + while ( + x1 < text1Length && + y1 < text2Length && + text1.charAt(x1) === text2.charAt(y1) + ) { + x1++; + y1++; + } + v1[k1Offset] = x1; + if (x1 > text1Length) { + // Ran off the right of the graph. + k1end += 2; + } else if (y1 > text2Length) { + // Ran off the bottom of the graph. + k1start += 2; + } else if (front) { + k2Offset = vOffset + delta - k1; + if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) { + // Mirror x2 onto top-left coordinate system. + x2 = text1Length - v2[k2Offset]; + if (x1 >= x2) { + // Overlap detected. + return this.diffBisectSplit(text1, text2, x1, y1, deadline); + } + } + } + } + + // Walk the reverse path one step. + for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { + k2Offset = vOffset + k2; + if (k2 === -d || (k2 !== d && v2[k2Offset - 1] < v2[k2Offset + 1])) { + x2 = v2[k2Offset + 1]; + } else { + x2 = v2[k2Offset - 1] + 1; + } + y2 = x2 - k2; + while ( + x2 < text1Length && + y2 < text2Length && + text1.charAt(text1Length - x2 - 1) === + text2.charAt(text2Length - y2 - 1) + ) { + x2++; + y2++; + } + v2[k2Offset] = x2; + if (x2 > text1Length) { + // Ran off the left of the graph. + k2end += 2; + } else if (y2 > text2Length) { + // Ran off the top of the graph. + k2start += 2; + } else if (!front) { + k1Offset = vOffset + delta - k2; + if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) { + x1 = v1[k1Offset]; + y1 = vOffset + x1 - k1Offset; + + // Mirror x2 onto top-left coordinate system. + x2 = text1Length - x2; + if (x1 >= x2) { + // Overlap detected. + return this.diffBisectSplit(text1, text2, x1, y1, deadline); + } + } + } + } + } + + // Diff took too long and hit the deadline or + // number of diffs equals number of characters, no commonality at all. + return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; + }; + + /** + * Given the location of the 'middle snake', split the diff in two parts + * and recurse. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} x Index of split point in text1. + * @param {number} y Index of split point in text2. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffBisectSplit = function( + text1, + text2, + x, + y, + deadline + ) { + var text1a, text1b, text2a, text2b, diffs, diffsb; + text1a = text1.substring(0, x); + text2a = text2.substring(0, y); + text1b = text1.substring(x); + text2b = text2.substring(y); + + // Compute both diffs serially. + diffs = this.DiffMain(text1a, text2a, false, deadline); + diffsb = this.DiffMain(text1b, text2b, false, deadline); + + return diffs.concat(diffsb); + }; + + /** + * Reduce the number of edits by eliminating semantically trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupSemantic = function(diffs) { + var changes, + equalities, + equalitiesLength, + lastequality, + pointer, + lengthInsertions2, + lengthDeletions2, + lengthInsertions1, + lengthDeletions1, + deletion, + insertion, + overlapLength1, + overlapLength2; + changes = false; + equalities = []; // Stack of indices where equalities are found. + equalitiesLength = 0; // Keeping our own length var is faster in JS. + /** @type {?string} */ + lastequality = null; + + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + pointer = 0; // Index of current position. + + // Number of characters that changed prior to the equality. + lengthInsertions1 = 0; + lengthDeletions1 = 0; + + // Number of characters that changed after the equality. + lengthInsertions2 = 0; + lengthDeletions2 = 0; + while (pointer < diffs.length) { + if (diffs[pointer][0] === DIFF_EQUAL) { + // Equality found. + equalities[equalitiesLength++] = pointer; + lengthInsertions1 = lengthInsertions2; + lengthDeletions1 = lengthDeletions2; + lengthInsertions2 = 0; + lengthDeletions2 = 0; + lastequality = diffs[pointer][1]; + } else { + // An insertion or deletion. + if (diffs[pointer][0] === DIFF_INSERT) { + lengthInsertions2 += diffs[pointer][1].length; + } else { + lengthDeletions2 += diffs[pointer][1].length; + } + + // Eliminate an equality that is smaller or equal to the edits on both + // sides of it. + if ( + lastequality && + lastequality.length <= + Math.max(lengthInsertions1, lengthDeletions1) && + lastequality.length <= Math.max(lengthInsertions2, lengthDeletions2) + ) { + // Duplicate record. + diffs.splice(equalities[equalitiesLength - 1], 0, [ + DIFF_DELETE, + lastequality + ]); + + // Change second copy to insert. + diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; + + // Throw away the equality we just deleted. + equalitiesLength--; + + // Throw away the previous equality (it needs to be reevaluated). + equalitiesLength--; + pointer = + equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; + + // Reset the counters. + lengthInsertions1 = 0; + lengthDeletions1 = 0; + lengthInsertions2 = 0; + lengthDeletions2 = 0; + lastequality = null; + changes = true; + } + } + pointer++; + } + + // Normalize the diff. + if (changes) { + this.diffCleanupMerge(diffs); + } + + // Find any overlaps between deletions and insertions. + // e.g: abcxxxxxxdef + // -> abcxxxdef + // e.g: xxxabcdefxxx + // -> defxxxabc + // Only extract an overlap if it is as big as the edit ahead or behind it. + pointer = 1; + while (pointer < diffs.length) { + if ( + diffs[pointer - 1][0] === DIFF_DELETE && + diffs[pointer][0] === DIFF_INSERT + ) { + deletion = diffs[pointer - 1][1]; + insertion = diffs[pointer][1]; + overlapLength1 = this.diffCommonOverlap(deletion, insertion); + overlapLength2 = this.diffCommonOverlap(insertion, deletion); + if (overlapLength1 >= overlapLength2) { + if ( + overlapLength1 >= deletion.length / 2 || + overlapLength1 >= insertion.length / 2 + ) { + // Overlap found. Insert an equality and trim the surrounding edits. + diffs.splice(pointer, 0, [ + DIFF_EQUAL, + insertion.substring(0, overlapLength1) + ]); + diffs[pointer - 1][1] = deletion.substring( + 0, + deletion.length - overlapLength1 + ); + diffs[pointer + 1][1] = insertion.substring(overlapLength1); + pointer++; + } + } else { + if ( + overlapLength2 >= deletion.length / 2 || + overlapLength2 >= insertion.length / 2 + ) { + // Reverse overlap found. + // Insert an equality and swap and trim the surrounding edits. + diffs.splice(pointer, 0, [ + DIFF_EQUAL, + deletion.substring(0, overlapLength2) + ]); + + diffs[pointer - 1][0] = DIFF_INSERT; + diffs[pointer - 1][1] = insertion.substring( + 0, + insertion.length - overlapLength2 + ); + diffs[pointer + 1][0] = DIFF_DELETE; + diffs[pointer + 1][1] = deletion.substring(overlapLength2); + pointer++; + } + } + pointer++; + } + pointer++; + } + }; + + /** + * Determine if the suffix of one string is the prefix of another. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of the first + * string and the start of the second string. + * @private + */ + DiffMatchPatch.prototype.diffCommonOverlap = function(text1, text2) { + var text1Length, text2Length, textLength, best, length, pattern, found; + + // Cache the text lengths to prevent multiple calls. + text1Length = text1.length; + text2Length = text2.length; + + // Eliminate the null case. + if (text1Length === 0 || text2Length === 0) { + return 0; + } + + // Truncate the longer string. + if (text1Length > text2Length) { + text1 = text1.substring(text1Length - text2Length); + } else if (text1Length < text2Length) { + text2 = text2.substring(0, text1Length); + } + textLength = Math.min(text1Length, text2Length); + + // Quick check for the worst case. + if (text1 === text2) { + return textLength; + } + + // Start by looking for a single character match + // and increase length until no match is found. + // Performance analysis: https://neil.fraser.name/news/2010/11/04/ + best = 0; + length = 1; + while (true) { + pattern = text1.substring(textLength - length); + found = text2.indexOf(pattern); + if (found === -1) { + return best; + } + length += found; + if ( + found === 0 || + text1.substring(textLength - length) === text2.substring(0, length) + ) { + best = length; + length++; + } + } + }; + + /** + * Split two texts into an array of strings. Reduce the texts to a string of * hashes where each Unicode character represents one line. - * Modifies linearray and linehash through being a closure. - * @param {string} text String to encode. - * @return {string} Encoded string. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {{chars1: string, chars2: string, lineArray: !Array.}} + * An object containing the encoded text1, the encoded text2 and + * the array of unique strings. + * The zeroth element of the array of unique strings is intentionally blank. + * @private + */ + DiffMatchPatch.prototype.diffLinesToChars = function(text1, text2) { + var lineArray, lineHash, chars1, chars2; + lineArray = []; // E.g. lineArray[4] === 'Hello\n' + lineHash = {}; // E.g. lineHash['Hello\n'] === 4 + + // '\x00' is a valid character, but various debuggers don't like it. + // So we'll insert a junk entry to avoid generating a null character. + lineArray[0] = ""; + + /** + * Split a text into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * Modifies linearray and linehash through being a closure. + * @param {string} text String to encode. + * @return {string} Encoded string. + * @private + */ + function diffLinesToCharsMunge(text) { + var chars, lineStart, lineEnd, lineArrayLength, line; + chars = ""; + + // Walk the text, pulling out a substring for each line. + // text.split('\n') would would temporarily double our memory footprint. + // Modifying text would create many large strings to garbage collect. + lineStart = 0; + lineEnd = -1; + + // Keeping our own length variable is faster than looking it up. + lineArrayLength = lineArray.length; + while (lineEnd < text.length - 1) { + lineEnd = text.indexOf("\n", lineStart); + if (lineEnd === -1) { + lineEnd = text.length - 1; + } + line = text.substring(lineStart, lineEnd + 1); + lineStart = lineEnd + 1; + + var lineHashExists = lineHash.hasOwnProperty + ? lineHash.hasOwnProperty(line) + : lineHash[line] !== undefined; + + if (lineHashExists) { + chars += String.fromCharCode(lineHash[line]); + } else { + chars += String.fromCharCode(lineArrayLength); + lineHash[line] = lineArrayLength; + lineArray[lineArrayLength++] = line; + } + } + return chars; + } + + chars1 = diffLinesToCharsMunge(text1); + chars2 = diffLinesToCharsMunge(text2); + return { + chars1: chars1, + chars2: chars2, + lineArray: lineArray + }; + }; + + /** + * Rehydrate the text in a diff from a string of line hashes to real lines of + * text. + * @param {!Array.} diffs Array of diff tuples. + * @param {!Array.} lineArray Array of unique strings. * @private */ - function diffLinesToCharsMunge(text) { - var chars, lineStart, lineEnd, lineArrayLength, line; - chars = ""; - - // Walk the text, pulling out a substring for each line. - // text.split('\n') would would temporarily double our memory footprint. - // Modifying text would create many large strings to garbage collect. - lineStart = 0; - lineEnd = -1; - - // Keeping our own length variable is faster than looking it up. - lineArrayLength = lineArray.length; - while (lineEnd < text.length - 1) { - lineEnd = text.indexOf("\n", lineStart); - if (lineEnd === -1) { - lineEnd = text.length - 1; - } - line = text.substring(lineStart, lineEnd + 1); - lineStart = lineEnd + 1; - - var lineHashExists = lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : lineHash[line] !== undefined; - - if (lineHashExists) { - chars += String.fromCharCode(lineHash[line]); - } else { - chars += String.fromCharCode(lineArrayLength); - lineHash[line] = lineArrayLength; - lineArray[lineArrayLength++] = line; - } - } - return chars; - } - - chars1 = diffLinesToCharsMunge(text1); - chars2 = diffLinesToCharsMunge(text2); - return { - chars1: chars1, - chars2: chars2, - lineArray: lineArray - }; - }; - - /** - * Rehydrate the text in a diff from a string of line hashes to real lines of - * text. - * @param {!Array.} diffs Array of diff tuples. - * @param {!Array.} lineArray Array of unique strings. - * @private - */ - DiffMatchPatch.prototype.diffCharsToLines = function (diffs, lineArray) { - var x, chars, text, y; - for (x = 0; x < diffs.length; x++) { - chars = diffs[x][1]; - text = []; - for (y = 0; y < chars.length; y++) { - text[y] = lineArray[chars.charCodeAt(y)]; - } - diffs[x][1] = text.join(""); - } - }; - - /** - * Reorder and merge like edit sections. Merge equalities. - * Any edit section can move as long as it doesn't cross an equality. - * @param {!Array.} diffs Array of diff tuples. - */ - DiffMatchPatch.prototype.diffCleanupMerge = function (diffs) { - var pointer, countDelete, countInsert, textInsert, textDelete, commonlength, changes, diffPointer, position; - diffs.push([DIFF_EQUAL, ""]); // Add a dummy entry at the end. - pointer = 0; - countDelete = 0; - countInsert = 0; - textDelete = ""; - textInsert = ""; - - while (pointer < diffs.length) { - switch (diffs[pointer][0]) { - case DIFF_INSERT: - countInsert++; - textInsert += diffs[pointer][1]; - pointer++; - break; - case DIFF_DELETE: - countDelete++; - textDelete += diffs[pointer][1]; - pointer++; - break; - case DIFF_EQUAL: - - // Upon reaching an equality, check for prior redundancies. - if (countDelete + countInsert > 1) { - if (countDelete !== 0 && countInsert !== 0) { - - // Factor out any common prefixes. - commonlength = this.diffCommonPrefix(textInsert, textDelete); - if (commonlength !== 0) { - if (pointer - countDelete - countInsert > 0 && diffs[pointer - countDelete - countInsert - 1][0] === DIFF_EQUAL) { - diffs[pointer - countDelete - countInsert - 1][1] += textInsert.substring(0, commonlength); - } else { - diffs.splice(0, 0, [DIFF_EQUAL, textInsert.substring(0, commonlength)]); - pointer++; - } - textInsert = textInsert.substring(commonlength); - textDelete = textDelete.substring(commonlength); - } - - // Factor out any common suffixies. - commonlength = this.diffCommonSuffix(textInsert, textDelete); - if (commonlength !== 0) { - diffs[pointer][1] = textInsert.substring(textInsert.length - commonlength) + diffs[pointer][1]; - textInsert = textInsert.substring(0, textInsert.length - commonlength); - textDelete = textDelete.substring(0, textDelete.length - commonlength); - } - } - - // Delete the offending records and add the merged ones. - if (countDelete === 0) { - diffs.splice(pointer - countInsert, countDelete + countInsert, [DIFF_INSERT, textInsert]); - } else if (countInsert === 0) { - diffs.splice(pointer - countDelete, countDelete + countInsert, [DIFF_DELETE, textDelete]); - } else { - diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert, [DIFF_DELETE, textDelete], [DIFF_INSERT, textInsert]); - } - pointer = pointer - countDelete - countInsert + (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1; - } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) { - - // Merge this equality with the previous one. - diffs[pointer - 1][1] += diffs[pointer][1]; - diffs.splice(pointer, 1); - } else { - pointer++; - } - countInsert = 0; - countDelete = 0; - textDelete = ""; - textInsert = ""; - break; - } - } - if (diffs[diffs.length - 1][1] === "") { - diffs.pop(); // Remove the dummy entry at the end. - } - - // Second pass: look for single edits surrounded on both sides by equalities - // which can be shifted sideways to eliminate an equality. - // e.g: ABAC -> ABAC - changes = false; - pointer = 1; - - // Intentionally ignore the first and last element (don't need checking). - while (pointer < diffs.length - 1) { - if (diffs[pointer - 1][0] === DIFF_EQUAL && diffs[pointer + 1][0] === DIFF_EQUAL) { - - diffPointer = diffs[pointer][1]; - position = diffPointer.substring(diffPointer.length - diffs[pointer - 1][1].length); - - // This is a single edit surrounded by equalities. - if (position === diffs[pointer - 1][1]) { - - // Shift the edit over the previous equality. - diffs[pointer][1] = diffs[pointer - 1][1] + diffs[pointer][1].substring(0, diffs[pointer][1].length - diffs[pointer - 1][1].length); - diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; - diffs.splice(pointer - 1, 1); - changes = true; - } else if (diffPointer.substring(0, diffs[pointer + 1][1].length) === diffs[pointer + 1][1]) { - - // Shift the edit over the next equality. - diffs[pointer - 1][1] += diffs[pointer + 1][1]; - diffs[pointer][1] = diffs[pointer][1].substring(diffs[pointer + 1][1].length) + diffs[pointer + 1][1]; - diffs.splice(pointer + 1, 1); - changes = true; - } - } - pointer++; - } - - // If shifts were made, the diff needs reordering and another shift sweep. - if (changes) { - this.diffCleanupMerge(diffs); - } - }; - - return function (o, n) { - var diff, output, text; - diff = new DiffMatchPatch(); - output = diff.DiffMain(o, n); - diff.diffCleanupEfficiency(output); - text = diff.diffPrettyHtml(output); - - return text; - }; - }(); - -}((function() { return this; }()))); + DiffMatchPatch.prototype.diffCharsToLines = function(diffs, lineArray) { + var x, chars, text, y; + for (x = 0; x < diffs.length; x++) { + chars = diffs[x][1]; + text = []; + for (y = 0; y < chars.length; y++) { + text[y] = lineArray[chars.charCodeAt(y)]; + } + diffs[x][1] = text.join(""); + } + }; + + /** + * Reorder and merge like edit sections. Merge equalities. + * Any edit section can move as long as it doesn't cross an equality. + * @param {!Array.} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupMerge = function(diffs) { + var pointer, + countDelete, + countInsert, + textInsert, + textDelete, + commonlength, + changes, + diffPointer, + position; + diffs.push([DIFF_EQUAL, ""]); // Add a dummy entry at the end. + pointer = 0; + countDelete = 0; + countInsert = 0; + textDelete = ""; + textInsert = ""; + + while (pointer < diffs.length) { + switch (diffs[pointer][0]) { + case DIFF_INSERT: + countInsert++; + textInsert += diffs[pointer][1]; + pointer++; + break; + case DIFF_DELETE: + countDelete++; + textDelete += diffs[pointer][1]; + pointer++; + break; + case DIFF_EQUAL: + // Upon reaching an equality, check for prior redundancies. + if (countDelete + countInsert > 1) { + if (countDelete !== 0 && countInsert !== 0) { + // Factor out any common prefixes. + commonlength = this.diffCommonPrefix(textInsert, textDelete); + if (commonlength !== 0) { + if ( + pointer - countDelete - countInsert > 0 && + diffs[pointer - countDelete - countInsert - 1][0] === + DIFF_EQUAL + ) { + diffs[ + pointer - countDelete - countInsert - 1 + ][1] += textInsert.substring(0, commonlength); + } else { + diffs.splice(0, 0, [ + DIFF_EQUAL, + textInsert.substring(0, commonlength) + ]); + pointer++; + } + textInsert = textInsert.substring(commonlength); + textDelete = textDelete.substring(commonlength); + } + + // Factor out any common suffixies. + commonlength = this.diffCommonSuffix(textInsert, textDelete); + if (commonlength !== 0) { + diffs[pointer][1] = + textInsert.substring(textInsert.length - commonlength) + + diffs[pointer][1]; + textInsert = textInsert.substring( + 0, + textInsert.length - commonlength + ); + textDelete = textDelete.substring( + 0, + textDelete.length - commonlength + ); + } + } + + // Delete the offending records and add the merged ones. + if (countDelete === 0) { + diffs.splice(pointer - countInsert, countDelete + countInsert, [ + DIFF_INSERT, + textInsert + ]); + } else if (countInsert === 0) { + diffs.splice(pointer - countDelete, countDelete + countInsert, [ + DIFF_DELETE, + textDelete + ]); + } else { + diffs.splice( + pointer - countDelete - countInsert, + countDelete + countInsert, + [DIFF_DELETE, textDelete], + [DIFF_INSERT, textInsert] + ); + } + pointer = + pointer - + countDelete - + countInsert + + (countDelete ? 1 : 0) + + (countInsert ? 1 : 0) + + 1; + } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) { + // Merge this equality with the previous one. + diffs[pointer - 1][1] += diffs[pointer][1]; + diffs.splice(pointer, 1); + } else { + pointer++; + } + countInsert = 0; + countDelete = 0; + textDelete = ""; + textInsert = ""; + break; + } + } + if (diffs[diffs.length - 1][1] === "") { + diffs.pop(); // Remove the dummy entry at the end. + } + + // Second pass: look for single edits surrounded on both sides by equalities + // which can be shifted sideways to eliminate an equality. + // e.g: ABAC -> ABAC + changes = false; + pointer = 1; + + // Intentionally ignore the first and last element (don't need checking). + while (pointer < diffs.length - 1) { + if ( + diffs[pointer - 1][0] === DIFF_EQUAL && + diffs[pointer + 1][0] === DIFF_EQUAL + ) { + diffPointer = diffs[pointer][1]; + position = diffPointer.substring( + diffPointer.length - diffs[pointer - 1][1].length + ); + + // This is a single edit surrounded by equalities. + if (position === diffs[pointer - 1][1]) { + // Shift the edit over the previous equality. + diffs[pointer][1] = + diffs[pointer - 1][1] + + diffs[pointer][1].substring( + 0, + diffs[pointer][1].length - diffs[pointer - 1][1].length + ); + diffs[pointer + 1][1] = + diffs[pointer - 1][1] + diffs[pointer + 1][1]; + diffs.splice(pointer - 1, 1); + changes = true; + } else if ( + diffPointer.substring(0, diffs[pointer + 1][1].length) === + diffs[pointer + 1][1] + ) { + // Shift the edit over the next equality. + diffs[pointer - 1][1] += diffs[pointer + 1][1]; + diffs[pointer][1] = + diffs[pointer][1].substring(diffs[pointer + 1][1].length) + + diffs[pointer + 1][1]; + diffs.splice(pointer + 1, 1); + changes = true; + } + } + pointer++; + } + + // If shifts were made, the diff needs reordering and another shift sweep. + if (changes) { + this.diffCleanupMerge(diffs); + } + }; + + return function(o, n) { + var diff, output, text; + diff = new DiffMatchPatch(); + output = diff.DiffMain(o, n); + diff.diffCleanupEfficiency(output); + text = diff.diffPrettyHtml(output); + + return text; + }; + })(); +})( + (function() { + return this; + })() +); diff --git a/tests/unit/sound/audio.js b/tests/unit/sound/audio.js index f4bfefe4..fe06bd82 100644 --- a/tests/unit/sound/audio.js +++ b/tests/unit/sound/audio.js @@ -17,7 +17,7 @@ this.endedListeners.push(listener); break; default: - throw("Not implemented"); + throw "Not implemented"; } }; this.removeEventListener = function(event, listener) { @@ -27,7 +27,7 @@ if (ind) this.endedListeners.splice(ind, 1); break; default: - throw("Not implemented"); + throw "Not implemented"; } }; @@ -66,7 +66,6 @@ this.ended = false; } - test("setChannels", function(_) { // Test that setChannels doesn't break sound _.expect(2); @@ -104,4 +103,4 @@ delete window.Audio; //reset Audio to platform default Crafty.audio.channels = []; }); -})(); \ No newline at end of file +})(); diff --git a/tests/unit/spatial/2d.js b/tests/unit/spatial/2d.js index ec31e7b8..9b54c764 100644 --- a/tests/unit/spatial/2d.js +++ b/tests/unit/spatial/2d.js @@ -26,20 +26,33 @@ player.z = 1; _.strictEqual(player._z, 1, "Z index"); - _.strictEqual(player._globalZ, player._z * 1e5 + player[0], "Global Z, After"); + _.strictEqual( + player._globalZ, + player._z * 1e5 + player[0], + "Global Z, After" + ); // test global order of entities depending on which was created last var player2 = Crafty.e("2D"); var player3 = Crafty.e("2D"); - _.ok(player3._globalZ > player2._globalZ, "player3 should be in front of player2"); + _.ok( + player3._globalZ > player2._globalZ, + "player3 should be in front of player2" + ); // test global order of entities on same z level, depending on which was created last player2.z = 1; - _.ok(player2._globalZ > player._globalZ, "player2 should be in front of player1"); + _.ok( + player2._globalZ > player._globalZ, + "player2 should be in front of player1" + ); // test global order of entities on different z level player3.z = -1; - _.ok(player2._globalZ > player3._globalZ, "player2 should be in front of player3"); + _.ok( + player2._globalZ > player3._globalZ, + "player2 should be in front of player3" + ); }); test("intersect", function(_) { @@ -54,15 +67,22 @@ _.strictEqual(player.intersect(0, 0, 100, 50), true, "Intersected"); - _.strictEqual(player.intersect({ - _x: 0, - _y: 0, - _w: 100, - _h: 50 - }), true, "Intersected Again"); - - _.strictEqual(player.intersect(100, 100, 100, 50), false, "Didn't intersect"); - + _.strictEqual( + player.intersect({ + _x: 0, + _y: 0, + _w: 100, + _h: 50 + }), + true, + "Intersected Again" + ); + + _.strictEqual( + player.intersect(100, 100, 100, 50), + false, + "Didn't intersect" + ); }); test("within", function(_) { @@ -79,21 +99,27 @@ _.strictEqual(player.within(-1, -1, 51, 51), true, "Within"); - - _.strictEqual(player.within({ - _x: 0, - _y: 0, - _w: 50, - _h: 50 - }), true, "Within Again"); + _.strictEqual( + player.within({ + _x: 0, + _y: 0, + _w: 50, + _h: 50 + }), + true, + "Within Again" + ); _.strictEqual(player.within(0, 0, 40, 50), false, "Wasn't within"); player.rotation = 90; // Once rotated, the entity should no longer be within the rectangle _.strictEqual(player.within(0, 0, 50, 50), false, "Rotated, Not within"); - _.strictEqual(player.within(-50, 0, 50, 50), true, "Rotated, within rotated area"); - + _.strictEqual( + player.within(-50, 0, 50, 50), + true, + "Rotated, within rotated area" + ); }); test("contains", function(_) { @@ -106,25 +132,35 @@ player.w = 50; player.h = 50; - _.strictEqual(player.contains(0, 0, 50, 50), true, "Contains"); _.strictEqual(player.contains(1, 1, 49, 49), true, "Contains"); - _.strictEqual(player.contains({ - _x: 0, - _y: 0, - _w: 50, - _h: 50 - }), true, "Contains"); + _.strictEqual( + player.contains({ + _x: 0, + _y: 0, + _w: 50, + _h: 50 + }), + true, + "Contains" + ); _.strictEqual(player.contains(1, 1, 51, 51), false, "Doesn't contain"); player.rotation = 90; - _.strictEqual(player.contains(0, 0, 50, 50), false, "Rotated, no longer contains"); - _.strictEqual(player.within(-50, 0, 50, 50), true, "Rotated, contains rotated area"); - + _.strictEqual( + player.contains(0, 0, 50, 50), + false, + "Rotated, no longer contains" + ); + _.strictEqual( + player.within(-50, 0, 50, 50), + true, + "Rotated, contains rotated area" + ); }); test("pos", function(_) { @@ -185,14 +221,17 @@ var circle = new Crafty.circle(0, 0, 10); _.strictEqual(circle.containsPoint(1, 2), true, "Contained the point"); - _.strictEqual(circle.containsPoint(8, 9), false, "Didn't contain the point"); + _.strictEqual( + circle.containsPoint(8, 9), + false, + "Didn't contain the point" + ); circle.shift(1, 0); _.strictEqual(circle.x, 1, "Shifted of one pixel on the x axis"); _.strictEqual(circle.y, 0, "circle.y didn't change"); _.strictEqual(circle.radius, 10, "circle.radius didn't change"); - }); test("child", function(_) { @@ -231,67 +270,86 @@ parent0.attach(child2); parent0.attach(child3); parent0.x += 50; - _.strictEqual(child0._x, 51, 'child0 shifted when parent did'); - _.strictEqual(child1._x, 52, 'child1 shifted when parent did'); + _.strictEqual(child0._x, 51, "child0 shifted when parent did"); + _.strictEqual(child1._x, 52, "child1 shifted when parent did"); child0.x += 1; child1.x += 1; - _.strictEqual(parent0._x, 50, 'child shifts do not move the parent'); + _.strictEqual(parent0._x, 50, "child shifts do not move the parent"); child1.destroy(); - _.deepEqual(parent0._children, [child0, child2, child3], 'child1 cleared itself from parent0._children when destroyed'); + _.deepEqual( + parent0._children, + [child0, child2, child3], + "child1 cleared itself from parent0._children when destroyed" + ); parent0.destroy(); - _.strictEqual(Crafty(child0[0]).length, 0, 'destruction of parent killed child0'); - _.strictEqual(Crafty(child2[0]).length, 0, 'destruction of parent killed child2'); - _.strictEqual(Crafty(child3[0]).length, 0, 'destruction of parent killed child3'); - + _.strictEqual( + Crafty(child0[0]).length, + 0, + "destruction of parent killed child0" + ); + _.strictEqual( + Crafty(child2[0]).length, + 0, + "destruction of parent killed child2" + ); + _.strictEqual( + Crafty(child3[0]).length, + 0, + "destruction of parent killed child3" + ); }); test("child_rotate", function(_) { - var parent = Crafty.e("2D") - .attr({ - x: 0, - y: 0, - w: 50, - h: 50, - rotation: 10 - }); - var child = Crafty.e("2D") - .attr({ - x: 10, - y: 10, - w: 50, - h: 50, - rotation: 15 - }); + var parent = Crafty.e("2D").attr({ + x: 0, + y: 0, + w: 50, + h: 50, + rotation: 10 + }); + var child = Crafty.e("2D").attr({ + x: 10, + y: 10, + w: 50, + h: 50, + rotation: 15 + }); parent.attach(child); parent.rotation += 20; - _.strictEqual(parent.rotation, 30, 'parent rotates normally'); - _.strictEqual(child.rotation, 35, 'child follows parent rotation'); + _.strictEqual(parent.rotation, 30, "parent rotates normally"); + _.strictEqual(child.rotation, 35, "child follows parent rotation"); child.rotation += 22; - _.strictEqual(parent.rotation, 30, 'parent ignores child rotation'); - _.strictEqual(child.rotation, 57, 'child rotates normally'); + _.strictEqual(parent.rotation, 30, "parent ignores child rotation"); + _.strictEqual(child.rotation, 57, "child rotates normally"); parent.rotation = 100; // Rotation by 90 degrees from initial position - _.strictEqual(Round(child.x), -10, "Child moved around parent upon rotation (x)."); - _.strictEqual(Round(child.y), 10, "Child moved around parent upon rotation (y)."); + _.strictEqual( + Round(child.x), + -10, + "Child moved around parent upon rotation (x)." + ); + _.strictEqual( + Round(child.y), + 10, + "Child moved around parent upon rotation (y)." + ); }); - test("child rotate 90deg", function (_) { - var parent = Crafty.e("2D") - .attr({ - x: 0, - y: 0, - w: 50, - h: 50 - }); - var child = Crafty.e("2D") - .attr({ - x: 0, - y: 0, - w: 50, - h: 50 - }); + test("child rotate 90deg", function(_) { + var parent = Crafty.e("2D").attr({ + x: 0, + y: 0, + w: 50, + h: 50 + }); + var child = Crafty.e("2D").attr({ + x: 0, + y: 0, + w: 50, + h: 50 + }); parent.origin("center"); child.origin("center"); @@ -304,25 +362,22 @@ }); test("origin properties", function(_) { - var player = Crafty.e("2D, Centered").attr({ - x: 0, - y: 0, - w: 50, - h: 50 - }); - player.origin(10, 10); - - - player.ox = 20; - _.strictEqual(player.x, 10, "X set such that origin is at ox"); - _.strictEqual(player.ox, 20, "OX set to 20"); - - - player.oy = 30; - _.strictEqual(player.y, 20, "Y set such that origin is at oy"); - _.strictEqual(player.oy, 30, "OY set to 20"); + var player = Crafty.e("2D, Centered").attr({ + x: 0, + y: 0, + w: 50, + h: 50 }); + player.origin(10, 10); + player.ox = 20; + _.strictEqual(player.x, 10, "X set such that origin is at ox"); + _.strictEqual(player.ox, 20, "OX set to 20"); + + player.oy = 30; + _.strictEqual(player.y, 20, "Y set such that origin is at oy"); + _.strictEqual(player.oy, 30, "OY set to 20"); + }); module("Geometric"); @@ -332,113 +387,130 @@ var NORTH_WEST = new Crafty.math.Vector2D(-1, -1).normalize(); test("Polygon intersection", function(_) { - var poly, distance, - origin, direction; + var poly, distance, origin, direction; - poly = new Crafty.polygon([0,0, 50,0, 50,50, 0,50]); + poly = new Crafty.polygon([0, 0, 50, 0, 50, 50, 0, 50]); // intersection with ray slightly outside entity edge - origin = {_x: -1, _y: 25}; + origin = { _x: -1, _y: 25 }; direction = EAST; distance = poly.intersectRay(origin, direction); _.strictEqual(distance, 1, "ray intersects polygon on its left edge"); // intersection with ray origin at entity edge - origin = {_x: 0, _y: 0}; + origin = { _x: 0, _y: 0 }; direction = EAST; distance = poly.intersectRay(origin, direction); _.strictEqual(distance, 0, "ray intersects polygon on its left edge"); // intersection with ray origin inside entity - origin = {_x: 25, _y: 25}; + origin = { _x: 25, _y: 25 }; direction = EAST; distance = poly.intersectRay(origin, direction); _.strictEqual(distance, 25, "ray intersects polygon on its right edge"); // intersection with ray origin at entity edge - origin = {_x: 50, _y: 25}; + origin = { _x: 50, _y: 25 }; direction = EAST; distance = poly.intersectRay(origin, direction); _.strictEqual(distance, 0, "ray intersects polygon on its right edge"); // no intersection with ray going away - origin = {_x: 51, _y: 25}; + origin = { _x: 51, _y: 25 }; direction = EAST; distance = poly.intersectRay(origin, direction); _.strictEqual(distance, Infinity, "ray does not intersect polygon"); - - poly = new Crafty.polygon([-75,-75, -150,-150]); + poly = new Crafty.polygon([-75, -75, -150, -150]); // intersection with ray at crossing - origin = {_x: -150, _y: -75}; + origin = { _x: -150, _y: -75 }; direction = NORTH_EAST; distance = poly.intersectRay(origin, direction); - _.strictEqual(distance.toFixed(4), (37.5 * Math.sqrt(2)).toFixed(4), - "ray intersects polygon at the crossing"); + _.strictEqual( + distance.toFixed(4), + (37.5 * Math.sqrt(2)).toFixed(4), + "ray intersects polygon at the crossing" + ); // no intersection with parallel ray - origin = {_x: -76, _y: -75}; + origin = { _x: -76, _y: -75 }; direction = NORTH_WEST; distance = poly.intersectRay(origin, direction); _.strictEqual(distance, Infinity, "ray does not intersect polygon"); // intersection with colinear ray starting before polygon - origin = {_x: -25, _y: -25}; + origin = { _x: -25, _y: -25 }; direction = NORTH_WEST; distance = poly.intersectRay(origin, direction); - _.strictEqual(distance.toFixed(4), (50 * Math.sqrt(2)).toFixed(4), - "ray intersects polygon at polygon's start point"); + _.strictEqual( + distance.toFixed(4), + (50 * Math.sqrt(2)).toFixed(4), + "ray intersects polygon at polygon's start point" + ); // intersection with colinear ray starting at polygon start - origin = {_x: -75, _y: -75}; + origin = { _x: -75, _y: -75 }; direction = NORTH_WEST; distance = poly.intersectRay(origin, direction); - _.strictEqual(distance, 0, "ray intersects polygon at polygon's start point"); + _.strictEqual( + distance, + 0, + "ray intersects polygon at polygon's start point" + ); // intersection with colinear ray starting inside polygon - origin = {_x: -100, _y: -100}; + origin = { _x: -100, _y: -100 }; direction = NORTH_WEST; distance = poly.intersectRay(origin, direction); - _.strictEqual(distance.toFixed(4), (50 * Math.sqrt(2)).toFixed(4), - "ray intersects polygon at ray's origin"); + _.strictEqual( + distance.toFixed(4), + (50 * Math.sqrt(2)).toFixed(4), + "ray intersects polygon at ray's origin" + ); // intersection with colinear ray starting at polygon end - origin = {_x: -150, _y: -150}; + origin = { _x: -150, _y: -150 }; direction = NORTH_WEST; distance = poly.intersectRay(origin, direction); _.strictEqual(distance, 0, "ray intersects polygon at polygon's end point"); // no intersection with colinear ray starting outside polygon - origin = {_x: -151, _y: -151}; + origin = { _x: -151, _y: -151 }; direction = NORTH_WEST; distance = poly.intersectRay(origin, direction); _.strictEqual(distance, Infinity, "ray does not intersect polygon"); // intersection with colinear ray starting at polygon end, going opposite direction - origin = {_x: -150, _y: -150}; + origin = { _x: -150, _y: -150 }; direction = SOUTH_EAST; distance = poly.intersectRay(origin, direction); _.strictEqual(distance, 0, "ray intersects polygon at polygon's end point"); // intersection with colinear ray starting inside polygon, going opposite direction - origin = {_x: -100, _y: -100}; + origin = { _x: -100, _y: -100 }; direction = SOUTH_EAST; distance = poly.intersectRay(origin, direction); - _.strictEqual(distance.toFixed(4), (25 * Math.sqrt(2)).toFixed(4), - "ray intersects polygon at ray's origin"); + _.strictEqual( + distance.toFixed(4), + (25 * Math.sqrt(2)).toFixed(4), + "ray intersects polygon at ray's origin" + ); // intersection with colinear ray starting at polygon start, going opposite direction - origin = {_x: -75, _y: -75}; + origin = { _x: -75, _y: -75 }; direction = SOUTH_EAST; distance = poly.intersectRay(origin, direction); - _.strictEqual(distance, 0, "ray intersects polygon at polygon's start point"); + _.strictEqual( + distance, + 0, + "ray intersects polygon at polygon's start point" + ); // no intersection with colinear ray going opposite direction - origin = {_x: -74, _y: -74}; + origin = { _x: -74, _y: -74 }; direction = SOUTH_EAST; distance = poly.intersectRay(origin, direction); _.strictEqual(distance, Infinity, "ray does not intersect polygon"); }); - })(); diff --git a/tests/unit/spatial/collision.js b/tests/unit/spatial/collision.js index 9028ef8b..669ff973 100644 --- a/tests/unit/spatial/collision.js +++ b/tests/unit/spatial/collision.js @@ -6,10 +6,9 @@ test("Collision constructors", function(_) { var newHitboxEvents = 0; - var e = Crafty.e("2D, Collision") - .bind("NewHitbox", function(newHitbox) { - newHitboxEvents++; - }); + var e = Crafty.e("2D, Collision").bind("NewHitbox", function(newHitbox) { + newHitboxEvents++; + }); var poly = new Crafty.polygon([50, 0, 100, 100, 0, 100]); e.collision(poly); @@ -19,7 +18,10 @@ var arr = [50, 0, 100, 100, 0, 100]; e.collision(arr); _.ok(e.map instanceof Crafty.polygon, "Hitbox is a polygon"); - _.ok(e.map.points && e.map.points !== arr, "Array used in hitbox is a clone of passed array"); + _.ok( + e.map.points && e.map.points !== arr, + "Array used in hitbox is a clone of passed array" + ); e.collision(50, 0, 100, 100, 0, 100); _.ok(e.map instanceof Crafty.polygon, "Hitbox is a polygon"); @@ -28,44 +30,55 @@ }); test("hit", function(_) { - var e = Crafty.e("2D, Collision, solid") - .attr({x: 0, y: 0, w: 25, h: 25}); - var f = Crafty.e("2D, Collision, solid") - .attr({x: 255, y: 255, w: 25, h: 25}); - var g = Crafty.e("2D, Collision, solid") - .attr({x: 255, y: 255, w: 25, h: 25}); - var h = Crafty.e("2D, Collision, plasma") - .attr({x: 255, y: 255, w: 25, h: 25}); + var e = Crafty.e("2D, Collision, solid").attr({ x: 0, y: 0, w: 25, h: 25 }); + var f = Crafty.e("2D, Collision, solid").attr({ + x: 255, + y: 255, + w: 25, + h: 25 + }); + var g = Crafty.e("2D, Collision, solid").attr({ + x: 255, + y: 255, + w: 25, + h: 25 + }); + var h = Crafty.e("2D, Collision, plasma").attr({ + x: 255, + y: 255, + w: 25, + h: 25 + }); var results; // check entity itself is not reported - results = e.hit('solid'); + results = e.hit("solid"); _.strictEqual(results, null, "empty collision results"); // check no reported hits given no intersections - results = e.hit('obj'); + results = e.hit("obj"); _.strictEqual(results, null, "empty collision results"); // check for hits given any-entity intersections h.x = h.y = 0; - results = e.hit('obj'); + results = e.hit("obj"); _.strictEqual(results.length, 1, "exactly one collision result"); _.strictEqual(results[0].obj, h, "expected collision with entity h"); // check no reported hits with solid component - results = e.hit('solid'); + results = e.hit("solid"); _.strictEqual(results, null, "empty collision results"); // check for hits with solid entity f.x = f.y = 0; - results = e.hit('solid'); + results = e.hit("solid"); _.strictEqual(results.length, 1, "exactly one collision result"); _.strictEqual(results[0].obj, f, "expected collision with entity f"); // check for hits with solid entities g.x = g.y = 0; - results = e.hit('solid'); + results = e.hit("solid"); _.strictEqual(results.length, 2, "exactly two collision results"); var counter = 0; for (var i = 0; i < 2; ++i) { @@ -76,88 +89,114 @@ // check no reported hits with solid component f.x = f.y = g.x = g.y = 255; - results = e.hit('solid'); + results = e.hit("solid"); _.strictEqual(results, null, "empty collision results"); }); test("hit - collision type", function(_) { - var e = Crafty.e("2D, Collision, solid") - .attr({x: 0, y: 0, w: 25, h: 25}); - var f = Crafty.e("2D, solid") - .attr({x: 0, y: 0, w: 25, h: 25}); + var e = Crafty.e("2D, Collision, solid").attr({ x: 0, y: 0, w: 25, h: 25 }); + var f = Crafty.e("2D, solid").attr({ x: 0, y: 0, w: 25, h: 25 }); var results; // check for MBR type collision with other entity - results = e.hit('solid'); + results = e.hit("solid"); _.strictEqual(results.length, 1, "exactly one collision result"); _.strictEqual(results[0].obj, f, "expected collision with entity f"); - _.strictEqual(results[0].type, 'MBR', "expected collision type"); + _.strictEqual(results[0].type, "MBR", "expected collision type"); // check for SAT type collision with other entity - f.addComponent('Collision'); - results = e.hit('solid'); + f.addComponent("Collision"); + results = e.hit("solid"); _.strictEqual(results.length, 1, "exactly one collision result"); _.strictEqual(results[0].obj, f, "expected collision with entity f"); - _.strictEqual(results[0].type, 'SAT', "expected collision type"); - _.ok('overlap' in results[0], "expected overlap value"); + _.strictEqual(results[0].type, "SAT", "expected collision type"); + _.ok("overlap" in results[0], "expected overlap value"); }); - test("hit -- collision vs. non collision tests", function(_){ - var e1 = Crafty.e("2D, Collision") - .attr({x: 0, y: 0, w: 2, h: 2}); - var e2 = Crafty.e("2D") - .attr({x: 0, y: 0, w: 2, h: 2}); - var results = e1.hit('2D'); + test("hit -- collision vs. non collision tests", function(_) { + var e1 = Crafty.e("2D, Collision").attr({ x: 0, y: 0, w: 2, h: 2 }); + var e2 = Crafty.e("2D").attr({ x: 0, y: 0, w: 2, h: 2 }); + var results = e1.hit("2D"); _.strictEqual(results.length, 1, "exactly one collision"); _.strictEqual(results[0].type, "MBR", "expected MBR collision type"); _.strictEqual(results[0].obj[0], e2[0], "expected collision with e2"); - + // Move e2 such that it should be returned by the broadphase search // (i.e. it's in the same cell of the spatial hashmap) // but doesn't actually overlap e1's MBR - e2.x=3; - var newResults = e1.hit('2D'); + e2.x = 3; + var newResults = e1.hit("2D"); _.ok(!newResults, 0, "No collisions"); }); test("onHit", function(_) { - var e = Crafty.e("2D, Collision") - .attr({x: 0, y: 0, w: 25, h: 25}); - var f = Crafty.e("2D, Collision") - .attr({x: 255, y: 255, w: 25, h: 25}); - var g = Crafty.e("2D, Collision, solid") - .attr({x: 255, y: 255, w: 25, h: 25}); + var e = Crafty.e("2D, Collision").attr({ x: 0, y: 0, w: 25, h: 25 }); + var f = Crafty.e("2D, Collision").attr({ x: 255, y: 255, w: 25, h: 25 }); + var g = Crafty.e("2D, Collision, solid").attr({ + x: 255, + y: 255, + w: 25, + h: 25 + }); var expectedHitDatas = {}, - onCallbacks = 0, - firstOnCallbacks = 0, - offCallbacks = 0; - - e.onHit('solid', function(hitDatas, isFirstCallback) { // callbackOn - onCallbacks++; - if (isFirstCallback) firstOnCallbacks++; - - _.strictEqual(hitDatas.length, Object.keys(expectedHitDatas).length, "collision with exactly expected amount of entities"); - for (var i = 0; i < hitDatas.length; ++i) - _.ok(hitDatas[i].obj[0] in expectedHitDatas, "collision with expected entity occurred"); - - }, function() { // callbackOff - offCallbacks++; - }); + onCallbacks = 0, + firstOnCallbacks = 0, + offCallbacks = 0; + + e.onHit( + "solid", + function(hitDatas, isFirstCallback) { + // callbackOn + onCallbacks++; + if (isFirstCallback) firstOnCallbacks++; + + _.strictEqual( + hitDatas.length, + Object.keys(expectedHitDatas).length, + "collision with exactly expected amount of entities" + ); + for (var i = 0; i < hitDatas.length; ++i) + _.ok( + hitDatas[i].obj[0] in expectedHitDatas, + "collision with expected entity occurred" + ); + }, + function() { + // callbackOff + offCallbacks++; + } + ); // check initial state // default state with no intersections, before update frame - _.strictEqual(onCallbacks, 0, "no collision callbacks yet before update frame"); - _.strictEqual(firstOnCallbacks, 0, "no collision callbacks yet before update frame"); - _.strictEqual(offCallbacks, 0, "no collision callbacks yet before update frame"); + _.strictEqual( + onCallbacks, + 0, + "no collision callbacks yet before update frame" + ); + _.strictEqual( + firstOnCallbacks, + 0, + "no collision callbacks yet before update frame" + ); + _.strictEqual( + offCallbacks, + 0, + "no collision callbacks yet before update frame" + ); // check initial state // default state with no intersections, after update frame Crafty.timer.simulateFrames(1); _.strictEqual(onCallbacks, 0, "no collision callbacks if no intersection"); - _.strictEqual(firstOnCallbacks, 0, "no collision callbacks if no intersection"); + _.strictEqual( + firstOnCallbacks, + 0, + "no collision callbacks if no intersection" + ); _.strictEqual(offCallbacks, 0, "no collision callbacks if no intersection"); // check no callbacks @@ -165,17 +204,41 @@ f.x = f.y = 0; Crafty.timer.simulateFrames(1); - _.strictEqual(onCallbacks, 0, "no collision callbacks yet before update frame"); - _.strictEqual(firstOnCallbacks, 0, "no collision callbacks yet before update frame"); - _.strictEqual(offCallbacks, 0, "no collision callbacks yet before update frame"); + _.strictEqual( + onCallbacks, + 0, + "no collision callbacks yet before update frame" + ); + _.strictEqual( + firstOnCallbacks, + 0, + "no collision callbacks yet before update frame" + ); + _.strictEqual( + offCallbacks, + 0, + "no collision callbacks yet before update frame" + ); // check no callbacks done before frame update // intersection with f, with required component, before update frame - f.addComponent('solid'); - - _.strictEqual(onCallbacks, 0, "no collision callbacks yet before update frame"); - _.strictEqual(firstOnCallbacks, 0, "no collision callbacks yet before update frame"); - _.strictEqual(offCallbacks, 0, "no collision callbacks yet before update frame"); + f.addComponent("solid"); + + _.strictEqual( + onCallbacks, + 0, + "no collision callbacks yet before update frame" + ); + _.strictEqual( + firstOnCallbacks, + 0, + "no collision callbacks yet before update frame" + ); + _.strictEqual( + offCallbacks, + 0, + "no collision callbacks yet before update frame" + ); // check callbacks done after frame update // intersection with f, with required component, after update frame @@ -192,16 +255,32 @@ Crafty.timer.simulateFrames(1); _.strictEqual(onCallbacks, 2, "another collision callbackOn occurred"); - _.strictEqual(firstOnCallbacks, 1, "not another first collision callbackOn occurred"); + _.strictEqual( + firstOnCallbacks, + 1, + "not another first collision callbackOn occurred" + ); _.strictEqual(offCallbacks, 0, "no collision callbackOff occurred yet"); // check no callbacks before frame update // no more intersection with f, before update frame f.x = f.y = 255; - _.strictEqual(onCallbacks, 2, "no collision callbacks yet before update frame"); - _.strictEqual(firstOnCallbacks, 1, "no collision callbacks yet before update frame"); - _.strictEqual(offCallbacks, 0, "no collision callbacks yet before update frame"); + _.strictEqual( + onCallbacks, + 2, + "no collision callbacks yet before update frame" + ); + _.strictEqual( + firstOnCallbacks, + 1, + "no collision callbacks yet before update frame" + ); + _.strictEqual( + offCallbacks, + 0, + "no collision callbacks yet before update frame" + ); // check callbacks done after frame update // no more intersection with f, after update frame @@ -209,7 +288,11 @@ Crafty.timer.simulateFrames(1); _.strictEqual(onCallbacks, 2, "no more on-collision callbacks occurred"); - _.strictEqual(firstOnCallbacks, 1, "no more on-collision callbacks occurred"); + _.strictEqual( + firstOnCallbacks, + 1, + "no more on-collision callbacks occurred" + ); _.strictEqual(offCallbacks, 1, "one off-collision callback occurred"); // check that no callbacks while ide @@ -217,8 +300,16 @@ Crafty.timer.simulateFrames(1); _.strictEqual(onCallbacks, 2, "no collision callbacks occurred while idle"); - _.strictEqual(firstOnCallbacks, 1, "no collision callbacks occurred while idle"); - _.strictEqual(offCallbacks, 1, "no collision callbacks occurred while idle"); + _.strictEqual( + firstOnCallbacks, + 1, + "no collision callbacks occurred while idle" + ); + _.strictEqual( + offCallbacks, + 1, + "no collision callbacks occurred while idle" + ); // check callbacks properly called with new collision event f.x = f.y = 0; @@ -227,7 +318,11 @@ Crafty.timer.simulateFrames(1); _.strictEqual(onCallbacks, 3, "again collision callbackOn occurred"); - _.strictEqual(firstOnCallbacks, 2, "again first collision callbackOn occurred"); + _.strictEqual( + firstOnCallbacks, + 2, + "again first collision callbackOn occurred" + ); _.strictEqual(offCallbacks, 1, "no collision callbackOff occurred yet"); // check that another intersecting entity does not change semantics of callbacks @@ -236,7 +331,11 @@ Crafty.timer.simulateFrames(1); _.strictEqual(onCallbacks, 4, "again collision callbackOn occurred"); - _.strictEqual(firstOnCallbacks, 2, "first collision callbackOn did not occur"); + _.strictEqual( + firstOnCallbacks, + 2, + "first collision callbackOn did not occur" + ); _.strictEqual(offCallbacks, 1, "no collision callbackOff occurred yet"); // check semantics of all intersecting entities leaving collision at same time @@ -245,7 +344,11 @@ Crafty.timer.simulateFrames(1); _.strictEqual(onCallbacks, 4, "no more on-collision callbacks occurred"); - _.strictEqual(firstOnCallbacks, 2, "no more on-collision callbacks occurred"); + _.strictEqual( + firstOnCallbacks, + 2, + "no more on-collision callbacks occurred" + ); _.strictEqual(offCallbacks, 2, "one off-collision callback occurred"); // check semantics of all entities entering collision at same time @@ -256,7 +359,11 @@ Crafty.timer.simulateFrames(1); _.strictEqual(onCallbacks, 5, "again collision callbackOn occurred"); - _.strictEqual(firstOnCallbacks, 3, "again first collision callbackOn occurred"); + _.strictEqual( + firstOnCallbacks, + 3, + "again first collision callbackOn occurred" + ); _.strictEqual(offCallbacks, 2, "no collision callbackOff occurred yet"); // check that an intersecting entity leaving collision does not change semantics of callbacks @@ -265,7 +372,11 @@ Crafty.timer.simulateFrames(1); _.strictEqual(onCallbacks, 6, "again collision callbackOn occurred"); - _.strictEqual(firstOnCallbacks, 3, "first collision callbackOn did not occur"); + _.strictEqual( + firstOnCallbacks, + 3, + "first collision callbackOn did not occur" + ); _.strictEqual(offCallbacks, 2, "no collision callbackOff occurred yet"); }); @@ -274,17 +385,23 @@ var e = Crafty.e("2D, Collision"); var c1 = new Crafty.circle(100, 100, 10); var c2 = new Crafty.circle(100, 105, 10); - _.strictEqual((e._SAT(c1, c2).overlap < -13.8 && e._SAT(c1, c2).overlap > -13.9), true, "Expected overlap to be about -13.86 ( or 15 cos[pi/8])"); - + _.strictEqual( + e._SAT(c1, c2).overlap < -13.8 && e._SAT(c1, c2).overlap > -13.9, + true, + "Expected overlap to be about -13.86 ( or 15 cos[pi/8])" + ); }); // Testcase from issue #828 by VHonzik test("SAT overlap with rectangles", function(_) { var e = Crafty.e("2D, Collision"); - var c1 = new Crafty.polygon([0,1, 50, 1, 50, 51, 0, 51]); + var c1 = new Crafty.polygon([0, 1, 50, 1, 50, 51, 0, 51]); var c2 = new Crafty.polygon([-10, -10, -10, 10, 10, 10, 10, -10]); - _.strictEqual(e._SAT(c1, c2) !== false, true, "Polygons should test as overlapping"); - + _.strictEqual( + e._SAT(c1, c2) !== false, + true, + "Polygons should test as overlapping" + ); }); test("adjustable boundary", function(_) { @@ -315,10 +432,8 @@ _.strictEqual(e._bx2, 5, "X2 boundary set"); _.strictEqual(e._by1, 5, "Y1 boundary set"); _.strictEqual(e._by2, 5, "Y2 boundary set"); - }); - test("Resizing 2D objects & hitboxes", function(_) { var e = Crafty.e("2D, Collision"); e.attr({ @@ -335,7 +450,11 @@ e.rotation = 90; - _.strictEqual(Math.round(e.map.points[0]), 0, "After rotation by 90 deg: x_0 is 0"); + _.strictEqual( + Math.round(e.map.points[0]), + 0, + "After rotation by 90 deg: x_0 is 0" + ); _.strictEqual(Math.round(e.map.points[1]), 0, "y_0 is 0"); _.strictEqual(Math.round(e.map.points[4]), -50, "x_2 is -50"); _.strictEqual(Math.round(e.map.points[5]), 40, "y_2 is 40"); @@ -348,17 +467,24 @@ e.collision(); // Check that regenerating the hitbox while rotated works correctly - _.strictEqual(Math.round(e.map.points[0]), 0, "After rotation and hitbox regeneration: x_0 is 0"); + _.strictEqual( + Math.round(e.map.points[0]), + 0, + "After rotation and hitbox regeneration: x_0 is 0" + ); _.strictEqual(Math.round(e.map.points[1]), 0, "y_0 is 0"); _.strictEqual(Math.round(e.map.points[4]), -50, "x_2 is -50"); _.strictEqual(Math.round(e.map.points[5]), 40, "y_2 is 40"); - // Check that changing the width when rotated resizes correctly for both hitbox and MBR // Rotated by 90 degrees, changing the width of the entity should change the height of the hitbox/mbr e.w = 100; - _.strictEqual(Math.round(e.map.points[0]), 0, "After rotation and increase in width: x_0 is 0"); + _.strictEqual( + Math.round(e.map.points[0]), + 0, + "After rotation and increase in width: x_0 is 0" + ); _.strictEqual(Math.round(e.map.points[1]), 0, "y_0 is 0"); _.strictEqual(Math.round(e.map.points[4]), -50, "x_2 is -50"); _.strictEqual(Math.round(e.map.points[5]), 100, "y_2 is 100"); @@ -398,20 +524,16 @@ }); test("Hitboxes outside of entities (CBR)", function(_) { - var poly = new Crafty.polygon([ - -8, 6, - 0, -8, - 8, -14, - 16, -8, - 24, 6 - ]); - - var e = Crafty.e("2D, Collision").attr({ - x: 50, - y: 50, - w: 16, - h: 16 - }).collision(poly); + var poly = new Crafty.polygon([-8, 6, 0, -8, 8, -14, 16, -8, 24, 6]); + + var e = Crafty.e("2D, Collision") + .attr({ + x: 50, + y: 50, + w: 16, + h: 16 + }) + .collision(poly); _.ok(e._cbr !== null, "_cbr exists"); var cbr = e._cbr; @@ -429,23 +551,19 @@ _.strictEqual(cbr._x, x0 + 10, "cbr x position moves correctly"); _.strictEqual(cbr._y, y0 + 15, "cbr y position moves correctly"); - }); test("CBRs on resize", function(_) { - var poly = new Crafty.polygon([ - 0, 0, - 0, 12, - 12, 12, - 12, 0 - ]); - - var e = Crafty.e("2D, Collision").attr({ - x: 50, - y: 50, - w: 15, - h: 15 - }).collision(poly); + var poly = new Crafty.polygon([0, 0, 0, 12, 12, 12, 12, 0]); + + var e = Crafty.e("2D, Collision") + .attr({ + x: 50, + y: 50, + w: 15, + h: 15 + }) + .collision(poly); _.ok(e._cbr === null, "_cbr should not exist"); @@ -456,36 +574,27 @@ e.w = 20; _.ok(e._cbr === null, "_cbr should not exist after entity grows again"); - }); test("CBRs should be removed on removal of component", function(_) { - var poly = new Crafty.polygon([ - 0, 0, - 0, 12, - 12, 12, - 12, 0 - ]); - - var e = Crafty.e("2D, Collision").attr({ - x: 50, - y: 50, - w: 10, - h: 10 - }).collision(poly); + var poly = new Crafty.polygon([0, 0, 0, 12, 12, 12, 12, 0]); + + var e = Crafty.e("2D, Collision") + .attr({ + x: 50, + y: 50, + w: 10, + h: 10 + }) + .collision(poly); _.ok(e._cbr !== null, "_cbr should exist to begin with"); e.removeComponent("Collision"); _.ok(e._cbr === null, "_cbr should now be removed along with Collision"); - }); - - - - // Define variables to host test shapes var trapezoid = null; var yellow = null; @@ -494,11 +603,11 @@ var purple = null; var resetPositions = function() { - trapezoid.attr({x: 300, y: 150}); - yellow.attr({x: 50, y: 50}); - parallelogram.attr({x: 350, y: 350}); - green.attr({x: 100, y: 500}); - purple.attr({x: 500, y: 500}); + trapezoid.attr({ x: 300, y: 150 }); + yellow.attr({ x: 50, y: 50 }); + parallelogram.attr({ x: 350, y: 350 }); + green.attr({ x: 100, y: 500 }); + purple.attr({ x: 500, y: 500 }); }; var overlapEverything = function() { @@ -517,7 +626,7 @@ green.ignoreHits(); // Now set them again - green.checkHits('Trapezoid, Yellow, Parallelogram, Purple'); + green.checkHits("Trapezoid, Yellow, Parallelogram, Purple"); }; var collisions = []; @@ -543,16 +652,26 @@ module("Collision - complex setting", { beforeEach: function() { - trapezoid = Crafty.e('Trapezoid, 2D, Collision').setName('Trapezoid'). - attr({w: 200, h: 100}).collision(new Crafty.polygon([50, 0, 0, 100, 200, 100, 150, 0])); - yellow = Crafty.e('Yellow, 2D, Collision').setName('Yellow'). - attr({w: 100, h: 100}).collision(new Crafty.polygon([0, 0, 0, 100, 100, 100, 100, 0])); - parallelogram = Crafty.e('Parallelogram, 2D, Collision').setName('Parallelogram'). - attr({w: 100, h: 100}).collision(new Crafty.polygon([0, 0, 25, 100, 100, 100, 75, 0])); - green = Crafty.e('Green, 2D, Collision').setName('Green'). - attr({w: 100, h: 100}).origin('center'); - purple = Crafty.e('Purple, 2D, Collision').setName('Purple'). - attr({w: 100, h: 100}).origin('center'); + trapezoid = Crafty.e("Trapezoid, 2D, Collision") + .setName("Trapezoid") + .attr({ w: 200, h: 100 }) + .collision(new Crafty.polygon([50, 0, 0, 100, 200, 100, 150, 0])); + yellow = Crafty.e("Yellow, 2D, Collision") + .setName("Yellow") + .attr({ w: 100, h: 100 }) + .collision(new Crafty.polygon([0, 0, 0, 100, 100, 100, 100, 0])); + parallelogram = Crafty.e("Parallelogram, 2D, Collision") + .setName("Parallelogram") + .attr({ w: 100, h: 100 }) + .collision(new Crafty.polygon([0, 0, 25, 100, 100, 100, 75, 0])); + green = Crafty.e("Green, 2D, Collision") + .setName("Green") + .attr({ w: 100, h: 100 }) + .origin("center"); + purple = Crafty.e("Purple, 2D, Collision") + .setName("Purple") + .attr({ w: 100, h: 100 }) + .origin("center"); // Set up hit events [trapezoid, yellow, parallelogram, green, purple].forEach(function(e) { @@ -572,7 +691,6 @@ } }); - test("HitOn fires when a tracked entity collides", function(_) { var collision = null; @@ -580,20 +698,31 @@ green.y = purple.y; Crafty.timer.simulateFrames(1); - - _.strictEqual(collisions.length, 1, "There should have been exactly 1 collision"); + _.strictEqual( + collisions.length, + 1, + "There should have been exactly 1 collision" + ); collision = collisions[0]; if (!collision) return; - _.deepEqual(getCollisionParticipants(collision), ['Green', 'Purple'], "The purple and green blocks should have collided"); + _.deepEqual( + getCollisionParticipants(collision), + ["Green", "Purple"], + "The purple and green blocks should have collided" + ); }); test("HitOn fires for each component type supplied as part of a list", function(_) { overlapEverything(); Crafty.timer.simulateFrames(1); - _.strictEqual(collisions.length, 4, "There should have been exactly 4 collisions"); + _.strictEqual( + collisions.length, + 4, + "There should have been exactly 4 collisions" + ); }); test("HitOn fires for each component type supplied as an individual argument", function(_) { @@ -603,7 +732,11 @@ overlapEverything(); Crafty.timer.simulateFrames(1); - _.strictEqual(collisions.length, 4, "There should have been exactly 4 collisions"); + _.strictEqual( + collisions.length, + 4, + "There should have been exactly 4 collisions" + ); }); test("HitOn contains info for multiple collisions", function(_) { @@ -615,19 +748,29 @@ yellow.y = green.y; Crafty.timer.simulateFrames(1); - - _.strictEqual(collisions.length, 2, "There should have been exactly 2 collisions"); + _.strictEqual( + collisions.length, + 2, + "There should have been exactly 2 collisions" + ); // Theoretically the code here should not care about the order of collisions // in the array, but that is a hassle collision = collisions[0]; if (collisions[0]) return; - _.deepEqual(getCollisionParticipants(collision), ['Green', 'Yellow'], "The yellow and green blocks should have collided"); + _.deepEqual( + getCollisionParticipants(collision), + ["Green", "Yellow"], + "The yellow and green blocks should have collided" + ); collision = collisions[1]; if (!collision) return; - _.deepEqual(getCollisionParticipants(collision), ['Green', 'Purple'], "The purple and green blocks should have collided"); - + _.deepEqual( + getCollisionParticipants(collision), + ["Green", "Purple"], + "The purple and green blocks should have collided" + ); }); test("HitOn collision info contains collision data", function(_) { @@ -639,8 +782,16 @@ collision = collisions[0]; if (!collision) return; - _.strictEqual(collision[1][0].type, 'SAT', "The collision type should have been SAT"); - _.strictEqual(Math.abs(collision[1][0].overlap), 100, "The collision overlap should have been 100%"); + _.strictEqual( + collision[1][0].type, + "SAT", + "The collision type should have been SAT" + ); + _.strictEqual( + Math.abs(collision[1][0].overlap), + 100, + "The collision overlap should have been 100%" + ); }); test("IgnoreHits causes hits not to be detected", function(_) { @@ -659,7 +810,11 @@ overlapEverything(); Crafty.timer.simulateFrames(1); - _.strictEqual(collisions.length, 2, "There should have been exactly 2 collisions"); + _.strictEqual( + collisions.length, + 2, + "There should have been exactly 2 collisions" + ); }); test("IgnoreHits ignores specific components supplied as arguments", function(_) { @@ -668,7 +823,11 @@ overlapEverything(); Crafty.timer.simulateFrames(1); - _.strictEqual(collisions.length, 2, "There should have been exactly 2 collisions"); + _.strictEqual( + collisions.length, + 2, + "There should have been exactly 2 collisions" + ); }); test("IgnoreHits has no effect when irrelevant components are supplied", function(_) { @@ -677,7 +836,11 @@ overlapEverything(); Crafty.timer.simulateFrames(1); - _.strictEqual(collisions.length, 4, "There should have been exactly 4 collisions"); + _.strictEqual( + collisions.length, + 4, + "There should have been exactly 4 collisions" + ); }); test("Once a hit event is fired, it will not fire again while the collision persists", function(_) { @@ -685,8 +848,11 @@ green.y = purple.y; Crafty.timer.simulateFrames(10); - - _.strictEqual(collisions.length, 1, "There should have been exactly 1 collision"); + _.strictEqual( + collisions.length, + 1, + "There should have been exactly 1 collision" + ); }); test("HitOff fires when a tracked entity stops colliding", function(_) { @@ -697,13 +863,20 @@ resetPositions(); Crafty.timer.simulateFrames(1); - - _.strictEqual(decollisions.length, 1, "Exactly 1 collision should have stopped"); + _.strictEqual( + decollisions.length, + 1, + "Exactly 1 collision should have stopped" + ); var decollision = decollisions[0]; if (!decollision) return; - _.deepEqual([decollision[0], decollision[1]], ['Green', 'Purple'], "The purple and green blocks should have stopped colliding"); + _.deepEqual( + [decollision[0], decollision[1]], + ["Green", "Purple"], + "The purple and green blocks should have stopped colliding" + ); }); test("HitOff events fires only once per terminated collision", function(_) { @@ -714,8 +887,11 @@ resetPositions(); Crafty.timer.simulateFrames(10); - - _.strictEqual(decollisions.length, 1, "Exactly 1 collision should have stopped"); + _.strictEqual( + decollisions.length, + 1, + "Exactly 1 collision should have stopped" + ); }); test("Setting up a hit check multiple times has no effect", function(_) { @@ -731,8 +907,16 @@ resetPositions(); Crafty.timer.simulateFrames(1); - _.strictEqual(collisions.length, 1, "There should have been exactly 1 collision"); - _.strictEqual(decollisions.length, 1, "Exactly 1 collision should have stopped"); + _.strictEqual( + collisions.length, + 1, + "There should have been exactly 1 collision" + ); + _.strictEqual( + decollisions.length, + 1, + "Exactly 1 collision should have stopped" + ); }); test("HitOn events fire for a collision after the original one", function(_) { @@ -747,7 +931,11 @@ green.y = purple.y; Crafty.timer.simulateFrames(1); - _.strictEqual(collisions.length, 2, "Exactly 2 collisions should have occurred"); + _.strictEqual( + collisions.length, + 2, + "Exactly 2 collisions should have occurred" + ); }); test("HitOn events fire for a collision underway if resetHitChecks is called", function(_) { @@ -761,10 +949,22 @@ // side effects. Crafty.timer.simulateFrames(2); - _.strictEqual(collisions.length, 2, "Exactly 2 collisions should have occurred"); - - _.deepEqual(getCollisionParticipants(collisions[0]), ['Green', 'Purple'], "The first collision should have been between the purple and green blocks"); - _.deepEqual(getCollisionParticipants(collisions[1]), ['Green', 'Purple'], "The second collision should have been between the purple and green blocks"); + _.strictEqual( + collisions.length, + 2, + "Exactly 2 collisions should have occurred" + ); + + _.deepEqual( + getCollisionParticipants(collisions[0]), + ["Green", "Purple"], + "The first collision should have been between the purple and green blocks" + ); + _.deepEqual( + getCollisionParticipants(collisions[1]), + ["Green", "Purple"], + "The second collision should have been between the purple and green blocks" + ); }); test("resetHitChecks without arguments resets all checks", function(_) { @@ -773,7 +973,11 @@ green.resetHitChecks(); Crafty.timer.simulateFrames(1); - _.strictEqual(collisions.length, 8, "Exactly 8 collisions should have occurred"); + _.strictEqual( + collisions.length, + 8, + "Exactly 8 collisions should have occurred" + ); }); test("resetHitChecks affects specific components specified as a list", function(_) { @@ -782,7 +986,11 @@ green.resetHitChecks("Yellow, Purple"); Crafty.timer.simulateFrames(1); - _.strictEqual(collisions.length, 6, "Exactly 6 collisions should have occurred"); + _.strictEqual( + collisions.length, + 6, + "Exactly 6 collisions should have occurred" + ); }); test("resetHitChecks affects specific components specified as arguments", function(_) { @@ -791,7 +999,11 @@ green.resetHitChecks("Yellow", "Purple"); Crafty.timer.simulateFrames(1); - _.strictEqual(collisions.length, 6, "Exactly 6 collisions should have occurred"); + _.strictEqual( + collisions.length, + 6, + "Exactly 6 collisions should have occurred" + ); }); test("resetHitChecks has no effect for components without hit checks", function(_) { @@ -800,7 +1012,11 @@ green.resetHitChecks("Banana", "Phone"); Crafty.timer.simulateFrames(1); - _.strictEqual(collisions.length, 4, "Exactly 4 collisions should have occurred"); + _.strictEqual( + collisions.length, + 4, + "Exactly 4 collisions should have occurred" + ); }); test("resetHitChecks works from within a hit handler", function(_) { @@ -814,7 +1030,11 @@ green.y = purple.y; Crafty.timer.simulateFrames(2); - _.strictEqual(collisions.length, 2, "Exactly 2 collisions should have occurred"); + _.strictEqual( + collisions.length, + 2, + "Exactly 2 collisions should have occurred" + ); green.unbind("HitOn", hitResetCallback); }); diff --git a/tests/unit/spatial/math.js b/tests/unit/spatial/math.js index 50a4b6b2..2f60b499 100644 --- a/tests/unit/spatial/math.js +++ b/tests/unit/spatial/math.js @@ -15,8 +15,16 @@ var v12 = new Vector2D(1, 2); var v12_2 = new Vector2D(v12); - _.strictEqual(v0.equals(v00), true, "new Vector2D() equals new Vector2D(0, 0)"); - _.strictEqual(v12.equals(v12_2), true, "new Vector2D(1, 2) equals new Vector2D(new Vector2D(1,2))"); + _.strictEqual( + v0.equals(v00), + true, + "new Vector2D() equals new Vector2D(0, 0)" + ); + _.strictEqual( + v12.equals(v12_2), + true, + "new Vector2D(1, 2) equals new Vector2D(new Vector2D(1,2))" + ); }); test("add()", function(_) { @@ -32,8 +40,16 @@ var v_11 = new Vector2D(-1, 1); var v1_1 = new Vector2D(1, -1); - _.strictEqual(v10.angleBetween(v_11), 3 * Math.PI / 4, "<1,0>.angleBetween(<0,1>) = 3*PI/4"); - _.strictEqual(v10.angleBetween(v1_1), -Math.PI / 4, "<1,0>.angleBetween(<1,-1>) = -PI/4"); + _.strictEqual( + v10.angleBetween(v_11), + (3 * Math.PI) / 4, + "<1,0>.angleBetween(<0,1>) = 3*PI/4" + ); + _.strictEqual( + v10.angleBetween(v1_1), + -Math.PI / 4, + "<1,0>.angleBetween(<1,-1>) = -PI/4" + ); }); test("angleTo()", function(_) { @@ -43,7 +59,11 @@ var v0_1 = new Vector2D(0, -1); _.strictEqual(v0.angleTo(v11), Math.PI / 4, "<0,0>.angleTo(<1,1>) = PI/4"); - _.strictEqual(v10.angleTo(v0_1), -3 * Math.PI / 4, "<1,0>.angleTo(<0,-1>) = -3*PI/4"); + _.strictEqual( + v10.angleTo(v0_1), + (-3 * Math.PI) / 4, + "<1,0>.angleTo(<0,-1>) = -3*PI/4" + ); }); test("clone()", function(_) { @@ -60,7 +80,11 @@ var v11 = new Vector2D(1, 1); _.strictEqual(v10.distance(v11), 1, "<1,0>.distance(<1,1>) = 1"); - _.strictEqual(v0.distance(v11), Math.sqrt(2), "<0,0>.distance(<1,1>) = sqrt(2)"); + _.strictEqual( + v0.distance(v11), + Math.sqrt(2), + "<0,0>.distance(<1,1>) = sqrt(2)" + ); }); test("distanceSq()", function(_) { @@ -105,22 +129,42 @@ var v34 = new Vector2D(3, 4); var v46 = new Vector2D(4, 6); - _.strictEqual(v12.equals(new Vector2D(1, 2)), true, "<1,2>.equals(<1,2>) = true"); - _.strictEqual(v34.equals(new Vector2D(3, 4)), true, "<3,4>.equals(<3,4>) = true"); - _.strictEqual(v46.equals(new Vector2D(4, 6)), true, "<4,6>.equals(<4,6>) = true"); + _.strictEqual( + v12.equals(new Vector2D(1, 2)), + true, + "<1,2>.equals(<1,2>) = true" + ); + _.strictEqual( + v34.equals(new Vector2D(3, 4)), + true, + "<3,4>.equals(<3,4>) = true" + ); + _.strictEqual( + v46.equals(new Vector2D(4, 6)), + true, + "<4,6>.equals(<4,6>) = true" + ); }); test("perpendicular()", function(_) { var v10 = new Vector2D(1, 0); - _.strictEqual(v10.perpendicular().equals(new Vector2D(0, 1)), true, "<1,0>.perpendicular() = <0,1>"); + _.strictEqual( + v10.perpendicular().equals(new Vector2D(0, 1)), + true, + "<1,0>.perpendicular() = <0,1>" + ); }); test("getNormal()", function(_) { var v10 = new Vector2D(1, 0); var v32 = new Vector2D(3, 2); - _.strictEqual(v10.getNormal(v32).equals((new Vector2D(1, -1)).normalize()), true, "<1,0>.getNormal(<3,2>) = "); + _.strictEqual( + v10.getNormal(v32).equals(new Vector2D(1, -1).normalize()), + true, + "<1,0>.getNormal(<3,2>) = " + ); }); test("isZero()", function(_) { @@ -138,7 +182,11 @@ _.strictEqual(v0.magnitude(), 0, "<0,0>.magnitude() = 0"); _.strictEqual(v10.magnitude(), 1, "<1,0>.magnitude() = 1"); - _.strictEqual(v_79.magnitude(), 11.40175425099138, "<-7,9>.magnitude() = 11.40175425099138"); + _.strictEqual( + v_79.magnitude(), + 11.40175425099138, + "<-7,9>.magnitude() = 11.40175425099138" + ); }); test("magnitudeSq()", function(_) { @@ -171,22 +219,51 @@ var v01 = new Vector2D(0, 1); var v_79 = new Vector2D(-7, 9); - _.strictEqual(v0.normalize().equals(new Vector2D(1, 0)), true, "<0,0>.normalize() = <1,0>"); - _.strictEqual(v01.normalize().equals(new Vector2D(0, 1)), true, "<0,1>.normalize() = <0,1>"); - _.strictEqual(v_79.normalize().equals(new Vector2D(-0.6139406135149205, 0.7893522173763263)), true, "<-7,9>.normalize() = <-0.6139406135149205,0.7893522173763263>"); + _.strictEqual( + v0.normalize().equals(new Vector2D(1, 0)), + true, + "<0,0>.normalize() = <1,0>" + ); + _.strictEqual( + v01.normalize().equals(new Vector2D(0, 1)), + true, + "<0,1>.normalize() = <0,1>" + ); + _.strictEqual( + v_79 + .normalize() + .equals(new Vector2D(-0.6139406135149205, 0.7893522173763263)), + true, + "<-7,9>.normalize() = <-0.6139406135149205,0.7893522173763263>" + ); }); test("scale()", function(_) { var v11 = new Vector2D(1, 1); - _.strictEqual(v11.scale(2).equals(new Vector2D(2, 2)), true, "<1,1>.scale(2) = <2,2>"); - _.strictEqual(v11.scale(2, -3).equals(new Vector2D(4, -6)), true, "<2,2>.scale(2, -3) = <4,-6>"); + _.strictEqual( + v11.scale(2).equals(new Vector2D(2, 2)), + true, + "<1,1>.scale(2) = <2,2>" + ); + _.strictEqual( + v11.scale(2, -3).equals(new Vector2D(4, -6)), + true, + "<2,2>.scale(2, -3) = <4,-6>" + ); }); test("scaleToMagnitude()", function(_) { var v34 = new Vector2D(3, 4); - _.strictEqual(v34.normalize().scaleToMagnitude(5).equals(new Vector2D(3, 4)), true, "<3,4>.normalize().scaleToMagnitude(5) = <3,4>"); + _.strictEqual( + v34 + .normalize() + .scaleToMagnitude(5) + .equals(new Vector2D(3, 4)), + true, + "<3,4>.normalize().scaleToMagnitude(5) = <3,4>" + ); }); test("setValues", function(_) { @@ -194,8 +271,16 @@ var v12 = new Vector2D(1, 2); var v44 = new Vector2D(4, 4); - _.strictEqual(v0.setValues(1, 2).equals(v12), true, "<0,0>.setValues(<1,2>) = <1,2>"); - _.strictEqual(v0.setValues(v44).equals(v44), true, "<1,2>.setValues(<4,4>) = <4,4>"); + _.strictEqual( + v0.setValues(1, 2).equals(v12), + true, + "<0,0>.setValues(<1,2>) = <1,2>" + ); + _.strictEqual( + v0.setValues(v44).equals(v44), + true, + "<1,2>.setValues(<4,4>) = <4,4>" + ); }); test("subtract()", function(_) { @@ -209,14 +294,22 @@ test("toString()", function(_) { var v12 = new Vector2D(1, 2); - _.strictEqual(v12.toString(), "Vector2D(1, 2)", "<1,2> = \"Vector2D(1, 2)\""); + _.strictEqual(v12.toString(), "Vector2D(1, 2)", '<1,2> = "Vector2D(1, 2)"'); }); test("translate()", function(_) { var v11 = new Vector2D(1, 1); - _.strictEqual(v11.translate(2).equals(new Vector2D(3, 3)), true, "<1,1>.translate(2) = <3,3>"); - _.strictEqual(v11.translate(2, -3).equals(new Vector2D(5, 0)), true, "<3,3>.translate(2, -3) = <5,0>"); + _.strictEqual( + v11.translate(2).equals(new Vector2D(3, 3)), + true, + "<1,1>.translate(2) = <3,3>" + ); + _.strictEqual( + v11.translate(2, -3).equals(new Vector2D(5, 0)), + true, + "<3,3>.translate(2, -3) = <5,0>" + ); }); test("tripleProduct()", function(_) { @@ -225,107 +318,212 @@ var vc = new Vector2D(5, 6); var vtp = new Vector2D(12, -10); - _.strictEqual(Vector2D.tripleProduct(va, vb, vc).equals(vtp), true, "tripleProduct(<1,2>, <3,4>, <5,6>) = <10,-12>"); + _.strictEqual( + Vector2D.tripleProduct(va, vb, vc).equals(vtp), + true, + "tripleProduct(<1,2>, <3,4>, <5,6>) = <10,-12>" + ); }); module("Math - Matrix2D"); test("apply()", function(_) { - _.strictEqual((new Matrix2D()).rotate(Math.PI / 2).apply(new Vector2D(1, 2)).equals(new Vector2D(-2, 1.0000000000000002)), - true, "(new Matrix2D()).rotate(Math.PI/2).apply(new Vector2D(1, 2)).equals(new Vector2D(-2, 1.0000000000000002))"); + _.strictEqual( + new Matrix2D() + .rotate(Math.PI / 2) + .apply(new Vector2D(1, 2)) + .equals(new Vector2D(-2, 1.0000000000000002)), + true, + "(new Matrix2D()).rotate(Math.PI/2).apply(new Vector2D(1, 2)).equals(new Vector2D(-2, 1.0000000000000002))" + ); }); test("clone()", function(_) { - _.strictEqual((new Matrix2D(1, 2, 3, 4, 5, 6)).clone().equals(new Matrix2D(1, 2, 3, 4, 5, 6)), - true, "(new Matrix2D(1, 2, 3, 4, 5, 6)).clone().equals(new Matrix2D(1, 2, 3, 4, 5, 6))"); + _.strictEqual( + new Matrix2D(1, 2, 3, 4, 5, 6) + .clone() + .equals(new Matrix2D(1, 2, 3, 4, 5, 6)), + true, + "(new Matrix2D(1, 2, 3, 4, 5, 6)).clone().equals(new Matrix2D(1, 2, 3, 4, 5, 6))" + ); }); test("combine()", function(_) { - _.strictEqual((new Matrix2D()).scale(2).combine((new Matrix2D()).rotate(0.75)).equals((new Matrix2D()).scale(2).rotate(0.75)), - true, "(new Matrix2D()).scale(2).combine((new Matrix2D()).rotate(0.75)).equals((new Matrix2D()).scale(2).rotate(0.75))"); + _.strictEqual( + new Matrix2D() + .scale(2) + .combine(new Matrix2D().rotate(0.75)) + .equals(new Matrix2D().scale(2).rotate(0.75)), + true, + "(new Matrix2D()).scale(2).combine((new Matrix2D()).rotate(0.75)).equals((new Matrix2D()).scale(2).rotate(0.75))" + ); }); test("equals()", function(_) { - _.strictEqual((new Matrix2D()).equals(new Matrix2D()), - true, "(new Matrix2D()).equals(new Matrix2D())"); - _.strictEqual((new Matrix2D()).scale(2).equals(new Matrix2D()), - false, "(new Matrix2D()).scale(2).equals(new Matrix2D())"); + _.strictEqual( + new Matrix2D().equals(new Matrix2D()), + true, + "(new Matrix2D()).equals(new Matrix2D())" + ); + _.strictEqual( + new Matrix2D().scale(2).equals(new Matrix2D()), + false, + "(new Matrix2D()).scale(2).equals(new Matrix2D())" + ); }); test("determinant()", function(_) { - _.strictEqual((new Matrix2D()).scale(2, 3).rotate(Math.PI / 2).determinant(), - 6, "(new Matrix2D()).scale(2, 3).rotate(Math.PI / 2).determinant()"); + _.strictEqual( + new Matrix2D() + .scale(2, 3) + .rotate(Math.PI / 2) + .determinant(), + 6, + "(new Matrix2D()).scale(2, 3).rotate(Math.PI / 2).determinant()" + ); }); test("invert()", function(_) { var m = new Matrix2D(4, 3, 3, 2, 0, 0); var m2 = new Matrix2D(-2, 3, 3, -4, 0, 0); - _.ok( m.invert().equals(m2), "Matrix (4,3,3,2) inverts to (-2,3,3,-4)"); + _.ok(m.invert().equals(m2), "Matrix (4,3,3,2) inverts to (-2,3,3,-4)"); }); test("isIdentity()", function(_) { - _.strictEqual((new Matrix2D()).isIdentity(), - true, "(new Matrix2D()).isIdentity()"); - _.strictEqual((new Matrix2D()).scale(2).isIdentity(), - false, "(new Matrix2D()).scale(2).isIdentity()"); + _.strictEqual( + new Matrix2D().isIdentity(), + true, + "(new Matrix2D()).isIdentity()" + ); + _.strictEqual( + new Matrix2D().scale(2).isIdentity(), + false, + "(new Matrix2D()).scale(2).isIdentity()" + ); }); test("isInvertible()", function(_) { - _.strictEqual((new Matrix2D()).scale(2, 3).rotate(Math.PI / 2).isInvertible(), - true, "(new Matrix2D()).scale(2, 3).rotate(Math.PI / 2).isInvertible()"); - _.strictEqual((new Matrix2D()).scale(0, 3).rotate(Math.PI / 2).isInvertible(), - false, "(new Matrix2D()).scale(0, 3).rotate(Math.PI / 2).isInvertible()"); + _.strictEqual( + new Matrix2D() + .scale(2, 3) + .rotate(Math.PI / 2) + .isInvertible(), + true, + "(new Matrix2D()).scale(2, 3).rotate(Math.PI / 2).isInvertible()" + ); + _.strictEqual( + new Matrix2D() + .scale(0, 3) + .rotate(Math.PI / 2) + .isInvertible(), + false, + "(new Matrix2D()).scale(0, 3).rotate(Math.PI / 2).isInvertible()" + ); }); test("preRotate()", function(_) { - _.strictEqual((new Matrix2D()).preRotate(0).equals(new Matrix2D()), - true, "(new Matrix2D()).preRotate(0).equals(new Matrix2D())"); - _.strictEqual((new Matrix2D()).preRotate(Math.PI / 2).equals((new Matrix2D()).rotate(Math.PI / 2)), - true, "(new Matrix2D()).preRotate(Math.PI / 2).equals((new Matrix2D()).rotate(Math.PI / 2))"); + _.strictEqual( + new Matrix2D().preRotate(0).equals(new Matrix2D()), + true, + "(new Matrix2D()).preRotate(0).equals(new Matrix2D())" + ); + _.strictEqual( + new Matrix2D() + .preRotate(Math.PI / 2) + .equals(new Matrix2D().rotate(Math.PI / 2)), + true, + "(new Matrix2D()).preRotate(Math.PI / 2).equals((new Matrix2D()).rotate(Math.PI / 2))" + ); }); test("preScale()", function(_) { - _.strictEqual((new Matrix2D()).preScale(2).equals(new Matrix2D(2, 0, 0, 2, 0, 0)), - true, "(new Matrix2D()).preScale(2).equals(new Matrix2D(2, 0, 0, 2, 0, 0))"); - _.strictEqual((new Matrix2D()).preScale(2.5).equals((new Matrix2D()).scale(2.5)), - true, "(new Matrix2D()).preScale(2.5).equals((new Matrix2D()).scale(2.5))"); + _.strictEqual( + new Matrix2D().preScale(2).equals(new Matrix2D(2, 0, 0, 2, 0, 0)), + true, + "(new Matrix2D()).preScale(2).equals(new Matrix2D(2, 0, 0, 2, 0, 0))" + ); + _.strictEqual( + new Matrix2D().preScale(2.5).equals(new Matrix2D().scale(2.5)), + true, + "(new Matrix2D()).preScale(2.5).equals((new Matrix2D()).scale(2.5))" + ); }); test("preTranslate()", function(_) { - _.strictEqual((new Matrix2D()).preTranslate(1, 2).equals(new Matrix2D(1, 0, 0, 1, 1, 2)), - true, "(new Matrix2D()).preTranslate(1, 2).equals(new Matrix2D(1, 0, 0, 1, 1, 2)"); - _.strictEqual((new Matrix2D()).preTranslate(1, 2).equals((new Matrix2D()).translate(new Vector2D(1, 2))), - true, "(new Matrix2D()).preTranslate(1, 2).equals((new Matrix2D()).translate(new Vector2D(1, 2)))"); - _.strictEqual((new Matrix2D()).preTranslate(new Vector2D(1, 2)).equals(new Matrix2D(1, 0, 0, 1, 1, 2)), - true, "(new Matrix2D()).preTranslate(new Vector2D(1, 2)).equals(new Matrix2D(1, 0, 0, 1, 1, 2))"); - _.strictEqual((new Matrix2D()).preTranslate(new Vector2D(1, 2)).equals((new Matrix2D()).translate(new Vector2D(1, 2))), - true, "(new Matrix2D()).preTranslate(new Vector2D(1, 2)).equals((new Matrix2D()).translate(new Vector2D(1, 2)))"); + _.strictEqual( + new Matrix2D().preTranslate(1, 2).equals(new Matrix2D(1, 0, 0, 1, 1, 2)), + true, + "(new Matrix2D()).preTranslate(1, 2).equals(new Matrix2D(1, 0, 0, 1, 1, 2)" + ); + _.strictEqual( + new Matrix2D() + .preTranslate(1, 2) + .equals(new Matrix2D().translate(new Vector2D(1, 2))), + true, + "(new Matrix2D()).preTranslate(1, 2).equals((new Matrix2D()).translate(new Vector2D(1, 2)))" + ); + _.strictEqual( + new Matrix2D() + .preTranslate(new Vector2D(1, 2)) + .equals(new Matrix2D(1, 0, 0, 1, 1, 2)), + true, + "(new Matrix2D()).preTranslate(new Vector2D(1, 2)).equals(new Matrix2D(1, 0, 0, 1, 1, 2))" + ); + _.strictEqual( + new Matrix2D() + .preTranslate(new Vector2D(1, 2)) + .equals(new Matrix2D().translate(new Vector2D(1, 2))), + true, + "(new Matrix2D()).preTranslate(new Vector2D(1, 2)).equals((new Matrix2D()).translate(new Vector2D(1, 2)))" + ); }); test("rotate()", function(_) { - _.strictEqual((new Matrix2D()).rotate(0).equals(new Matrix2D()), - true, "(new Matrix2D()).rotate(0).equals(new Matrix2D())"); + _.strictEqual( + new Matrix2D().rotate(0).equals(new Matrix2D()), + true, + "(new Matrix2D()).rotate(0).equals(new Matrix2D())" + ); }); test("scale()", function(_) { - _.strictEqual((new Matrix2D()).scale(2, 3).equals(new Matrix2D(2, 0, 0, 3, 0, 0)), - true, "(new Matrix2D()).scale(2, 3).equals(new Matrix2D(2, 0, 0, 3, 0, 0))"); + _.strictEqual( + new Matrix2D().scale(2, 3).equals(new Matrix2D(2, 0, 0, 3, 0, 0)), + true, + "(new Matrix2D()).scale(2, 3).equals(new Matrix2D(2, 0, 0, 3, 0, 0))" + ); }); test("setValues()", function(_) { - _.strictEqual((new Matrix2D()).setValues(1, 2, 3, 4, 5, 6).equals(new Matrix2D(1, 2, 3, 4, 5, 6)), - true, "(new Matrix2D()).setValues(1, 2, 3, 4, 5, 6).equals(new Matrix2D(1, 2, 3, 4, 5, 6))"); + _.strictEqual( + new Matrix2D() + .setValues(1, 2, 3, 4, 5, 6) + .equals(new Matrix2D(1, 2, 3, 4, 5, 6)), + true, + "(new Matrix2D()).setValues(1, 2, 3, 4, 5, 6).equals(new Matrix2D(1, 2, 3, 4, 5, 6))" + ); }); test("toString()", function(_) { - _.strictEqual((new Matrix2D()).toString(), - "Matrix2D([1, 0, 0] [0, 1, 0] [0, 0, 1])", "(new Matrix2D()).toString()"); + _.strictEqual( + new Matrix2D().toString(), + "Matrix2D([1, 0, 0] [0, 1, 0] [0, 0, 1])", + "(new Matrix2D()).toString()" + ); }); test("translate()", function(_) { - _.strictEqual((new Matrix2D()).translate(1, 2).equals(new Matrix2D(1, 0, 0, 1, 1, 2)), - true, "(new Matrix2D()).translate(1, 2).equals(new Matrix2D(1, 0, 0, 1, 1, 2))"); - _.strictEqual((new Matrix2D()).translate(new Vector2D(1, 2)).equals(new Matrix2D(1, 0, 0, 1, 1, 2)), - true, "(new Matrix2D()).translate(new Vector2D(1, 2)).equals(new Matrix2D(1, 0, 0, 1, 1, 2))"); - }); -})(); \ No newline at end of file + _.strictEqual( + new Matrix2D().translate(1, 2).equals(new Matrix2D(1, 0, 0, 1, 1, 2)), + true, + "(new Matrix2D()).translate(1, 2).equals(new Matrix2D(1, 0, 0, 1, 1, 2))" + ); + _.strictEqual( + new Matrix2D() + .translate(new Vector2D(1, 2)) + .equals(new Matrix2D(1, 0, 0, 1, 1, 2)), + true, + "(new Matrix2D()).translate(new Vector2D(1, 2)).equals(new Matrix2D(1, 0, 0, 1, 1, 2))" + ); + }); +})(); diff --git a/tests/unit/spatial/motion.js b/tests/unit/spatial/motion.js index 1aa379c5..82f40c7d 100644 --- a/tests/unit/spatial/motion.js +++ b/tests/unit/spatial/motion.js @@ -7,8 +7,7 @@ test("Motion", function(_) { var Vector2D = Crafty.math.Vector2D; var zero = new Vector2D(); - var ent = Crafty.e("2D, Motion, AngularMotion") - .attr({x: 0, y:0}); + var ent = Crafty.e("2D, Motion, AngularMotion").attr({ x: 0, y: 0 }); // Check the initial zeroing of values _.ok(ent.velocity().equals(zero), "linear velocity should be zero"); @@ -20,13 +19,16 @@ // Check that you can't overwrite the motionDeltas ent.motionDelta().x = 20; - _.ok(ent.motionDelta().equals(zero), "linear delta should not have changed"); + _.ok( + ent.motionDelta().equals(zero), + "linear delta should not have changed" + ); ent.drotation = 10; _.strictEqual(ent.drotation, 0, "angular delta should not have changed"); - // Set the v0 / v0_r values - var v0 = new Vector2D(2,5); var v0_r = 10; + var v0 = new Vector2D(2, 5); + var v0_r = 10; // Check setting velocity and vrotation ent.velocity().setValues(v0); @@ -35,7 +37,8 @@ _.strictEqual(ent.vrotation, v0_r, "angular velocity should be 10"); // Check setting accelerations - var a = new Vector2D(4,2); var a_r = -15; + var a = new Vector2D(4, 2); + var a_r = -15; ent.acceleration().setValues(a); ent.arotation = a_r; _.ok(ent.acceleration().equals(a), "linear acceleration should be <4,2>"); @@ -45,7 +48,10 @@ ent.velocity().x += 1; ent.velocity().y *= 2; ent.velocity().y -= 1; - _.ok(ent.velocity().equals(new Vector2D(v0.x+1, v0.y*2-1)), "linear velocity should be <3,9>"); + _.ok( + ent.velocity().equals(new Vector2D(v0.x + 1, v0.y * 2 - 1)), + "linear velocity should be <3,9>" + ); ent.arotation += 5; _.strictEqual(ent.arotation, a_r + 5, "angular acceleration should be -10"); @@ -59,17 +65,16 @@ _.ok(ent.motionDelta().equals(zero), "linear delta should be zero"); _.strictEqual(ent.drotation, 0, "angular delta should be zero"); - // v = (2, 5) ent.velocity().setValues(v0); // v0_r = 10 ent.vrotation = v0_r; Crafty.timer.simulateFrames(1); - var dt = 0.020; // 20 ms in 1 frame is the default + var dt = 0.02; // 20 ms in 1 frame is the default // Check what happens to velocity over time with 0 a _.ok(ent.velocity().equals(v0), "velocity should be <2,5>"); _.strictEqual(ent.vrotation, v0_r, "angular velocity should be 10"); - // Delta in last frame should be + // Delta in last frame should be _.strictEqual(ent.motionDelta().x, v0.x * dt, "delta x should be .04"); _.strictEqual(ent.motionDelta().y, v0.y * dt, "delta y should be .1"); // _.ok(ent.motionDelta().equals(v0), "delta should be <2,5>"); @@ -78,63 +83,110 @@ _.strictEqual(ent.y, v0.y * dt, "entity y should be 5"); _.strictEqual(ent.rotation, v0_r * dt, "entity rotation should be 10"); - // Goal of this test is to check that acceleration processes correctly, so reset everything first! ent.resetMotion(); ent.resetAngularMotion(); - ent.attr({x:0, y:0, rotation:0}); - + ent.attr({ x: 0, y: 0, rotation: 0 }); + // Set a, v values - a = new Vector2D(4,2); + a = new Vector2D(4, 2); a_r = -15; - v0 = new Vector2D(2,5); + v0 = new Vector2D(2, 5); v0_r = 10; - + ent.acceleration().setValues(a); ent.arotation = a_r; ent.velocity().setValues(v0); ent.vrotation = v0_r; Crafty.timer.simulateFrames(1); - - // Calculate the new configuration of the object via kinematics - dt = 0.020; // 20 ms in one frame - var dPos = new Vector2D(a).scale(0.5*dt*dt).add(v0.clone().scale(dt)), dPos_r = v0_r * dt + 0.5*a_r * dt * dt; - _.ok(ent.motionDelta().equals(dPos), "delta should be equal to the new position"); + // Calculate the new configuration of the object via kinematics + dt = 0.02; // 20 ms in one frame + var dPos = new Vector2D(a).scale(0.5 * dt * dt).add(v0.clone().scale(dt)), + dPos_r = v0_r * dt + 0.5 * a_r * dt * dt; + + _.ok( + ent.motionDelta().equals(dPos), + "delta should be equal to the new position" + ); _.strictEqual(ent.drotation, dPos_r, "Rotation should match"); _.strictEqual(ent.x, dPos.x, "entity x should match calculated"); _.strictEqual(ent.y, dPos.y, "entity y should match calculate"); - _.strictEqual(ent.rotation, dPos_r, "entity rotation should match calculated"); - var v1 = new Vector2D(a).scale(dt).add(v0), v1_r = a_r * dt + v0_r; + _.strictEqual( + ent.rotation, + dPos_r, + "entity rotation should match calculated" + ); + var v1 = new Vector2D(a).scale(dt).add(v0), + v1_r = a_r * dt + v0_r; _.strictEqual(ent.velocity().x, v1.x, "vx should match calculated"); _.strictEqual(ent.velocity().y, v1.y, "vy should match calculated"); // _.ok(ent.velocity().equals(v1), "linear velocity should match calculated"); - _.strictEqual(ent.vrotation, v1_r, "angular velocity should match calculated"); + _.strictEqual( + ent.vrotation, + v1_r, + "angular velocity should match calculated" + ); ent.destroy(); }); test("Motion - CCDBR", function(_) { - var ent = Crafty.e("2D, Motion") - .attr({x: 0, y:0, w: 10, h: 10}); + var ent = Crafty.e("2D, Motion").attr({ x: 0, y: 0, w: 10, h: 10 }); var ccdbr, ccdbr2; - ent._dx = 10; ent.x = 10; - ent._dy = -5; ent.y = -5; + ent._dx = 10; + ent.x = 10; + ent._dy = -5; + ent.y = -5; ccdbr = ent.ccdbr(); - _.strictEqual(ccdbr._x, 0, "tunneling rectangle property matches expected value"); - _.strictEqual(ccdbr._w, 20, "tunneling rectangle property matches expected value"); - _.strictEqual(ccdbr._y, -5, "tunneling rectangle property matches expected value"); - _.strictEqual(ccdbr._h, 15, "tunneling rectangle property matches expected value"); - - ent._dx = -25; ent.x = -15; - ent._dy = 50; ent.y = 45; + _.strictEqual( + ccdbr._x, + 0, + "tunneling rectangle property matches expected value" + ); + _.strictEqual( + ccdbr._w, + 20, + "tunneling rectangle property matches expected value" + ); + _.strictEqual( + ccdbr._y, + -5, + "tunneling rectangle property matches expected value" + ); + _.strictEqual( + ccdbr._h, + 15, + "tunneling rectangle property matches expected value" + ); + + ent._dx = -25; + ent.x = -15; + ent._dy = 50; + ent.y = 45; ccdbr2 = ent.ccdbr(ccdbr); - _.strictEqual(ccdbr._x, -15, "tunneling rectangle property matches expected value"); - _.strictEqual(ccdbr._w, 35, "tunneling rectangle property matches expected value"); - _.strictEqual(ccdbr._y, -5, "tunneling rectangle property matches expected value"); - _.strictEqual(ccdbr._h, 60, "tunneling rectangle property matches expected value"); + _.strictEqual( + ccdbr._x, + -15, + "tunneling rectangle property matches expected value" + ); + _.strictEqual( + ccdbr._w, + 35, + "tunneling rectangle property matches expected value" + ); + _.strictEqual( + ccdbr._y, + -5, + "tunneling rectangle property matches expected value" + ); + _.strictEqual( + ccdbr._h, + 60, + "tunneling rectangle property matches expected value" + ); _.strictEqual(ccdbr2._x, ccdbr._x, "object was reused"); _.strictEqual(ccdbr2._y, ccdbr._y, "object was reused"); @@ -145,38 +197,34 @@ }); test("Motion - changing vector", function(_) { - var ent = Crafty.e("2D, Motion") - .attr({x: 0, y:0}); + var ent = Crafty.e("2D, Motion").attr({ x: 0, y: 0 }); ent.vx = 5; _.strictEqual(ent.velocity().x, 5, "Velocity component changed"); - }); - test("Motion - NewDirection Event", function(_){ - var e = Crafty.e("2D, Motion") - .attr({x: 10, y:10}); + test("Motion - NewDirection Event", function(_) { + var e = Crafty.e("2D, Motion").attr({ x: 10, y: 10 }); e.vx = 0; e.vy = 0; e.ay = 0; e.ax = -0.3; var newDirectionFlag = false; - e.bind("NewDirection", function(){ + e.bind("NewDirection", function() { newDirectionFlag = true; }); Crafty.timer.simulateFrames(1); _.strictEqual(newDirectionFlag, true, "NewDirection was triggered"); }); - test("AngularMotion - NewRotationDirection Event", function(_){ - var e = Crafty.e("2D, AngularMotion") - .attr({x: 10, y:10}); + test("AngularMotion - NewRotationDirection Event", function(_) { + var e = Crafty.e("2D, AngularMotion").attr({ x: 10, y: 10 }); e.vrotation = 0; e.arotation = -0.3; var newDirectionFlag = false; - e.bind("NewRotationDirection", function(){ + e.bind("NewRotationDirection", function() { newDirectionFlag = true; }); Crafty.timer.simulateFrames(1); @@ -184,13 +232,12 @@ }); test("Motion - Events", function(_) { - var e = Crafty.e("2D, Motion, AngularMotion") - .attr({x: 0, y:0}); + var e = Crafty.e("2D, Motion, AngularMotion").attr({ x: 0, y: 0 }); var newDirectionEvents = 0, - newRotationDirectionEvents = 0, - rotatedEvents = 0, - motionEvents = 0; + newRotationDirectionEvents = 0, + rotatedEvents = 0, + motionEvents = 0; e.bind("NewDirection", function(evt) { newDirectionEvents++; }); @@ -209,8 +256,8 @@ _.strictEqual(evt.x, 0, "[1] - no motion along x axis"); _.strictEqual(evt.y, 1, "[1] - moving along +y axis"); }); - e.one("MotionChange", function(evt) { - _.strictEqual(evt.key, "vy", "[1] - vy was set"); + e.one("MotionChange", function(evt) { + _.strictEqual(evt.key, "vy", "[1] - vy was set"); _.strictEqual(evt.oldValue, 0, "[1] - old vy was 0"); }); e.vy = 1; @@ -218,20 +265,20 @@ // group 2: set both vy and vx to be negative e.one("NewDirection", function(evt) { - _.strictEqual(evt.x, -1, "[2] - Now moving along -x axis" ); + _.strictEqual(evt.x, -1, "[2] - Now moving along -x axis"); _.strictEqual(evt.y, -1, "[2] - Now moving along -y axis"); }); - e.one("MotionChange", function(evt) { - _.strictEqual(evt.key, "vx", "[2] - vx was changed"); + e.one("MotionChange", function(evt) { + _.strictEqual(evt.key, "vx", "[2] - vx was changed"); _.strictEqual(evt.oldValue, 0, "[2] - old vx was 0"); }); e.vx = -1; - e.one("MotionChange", function(evt) { + e.one("MotionChange", function(evt) { _.strictEqual(evt.key, "vy", "[2] - vy was changed"); _.strictEqual(evt.oldValue, 1, "[2] - old vy value matches cached"); }); e.vy = 0; - e.one("MotionChange", function(evt) { + e.one("MotionChange", function(evt) { _.strictEqual(evt.key, "vy", "[2] - vy was changed again"); _.strictEqual(evt.oldValue, 0, "[2] - old vy value was 0"); }); @@ -249,13 +296,11 @@ e.vy = 0; Crafty.timer.simulateFrames(1); - - // Group 4, rotation tests e.vrotation = 0; // no MotionChange event was fired e.one("MotionChange", function(evt) { _.strictEqual(evt.key, "vrotation", "[4] - change of angular speed"); - _.strictEqual(evt.oldValue, 0, "[4] - old vr value was 0"); + _.strictEqual(evt.oldValue, 0, "[4] - old vr value was 0"); }); e.vrotation = 1; e.one("MotionChange", function(evt) { @@ -279,7 +324,11 @@ _.strictEqual(evt, -1, "[6] - Rotating in the negative sense"); }); e.one("Rotated", function(oldValue) { - _.strictEqual(oldValue, old_rotation, "[6] - Old rotation matches cached value"); + _.strictEqual( + oldValue, + old_rotation, + "[6] - Old rotation matches cached value" + ); }); e.vrotation = -1; Crafty.timer.simulateFrames(1); @@ -289,7 +338,11 @@ _.strictEqual(evt, 1, "[7] - Rotating in the positive sense"); }); e.one("Rotated", function(oldValue) { - _.strictEqual(oldValue, old_rotation, "[7] - Old rotation matches cached value"); + _.strictEqual( + oldValue, + old_rotation, + "[7] - Old rotation matches cached value" + ); }); e.vrotation = 1; Crafty.timer.simulateFrames(1); @@ -302,10 +355,13 @@ // TODO: break these up into separate checks _.strictEqual(newDirectionEvents, 3, "NewDirection fired 3 times."); - _.strictEqual(newRotationDirectionEvents, 4, "NewRotationDirection fired 4 times."); + _.strictEqual( + newRotationDirectionEvents, + 4, + "NewRotationDirection fired 4 times." + ); _.strictEqual(motionEvents, 13, "MotionChange fired 13 times."); _.strictEqual(rotatedEvents, 3, "Rotated fired 3 times."); e.destroy(); }); - })(); diff --git a/tests/unit/spatial/platform.js b/tests/unit/spatial/platform.js index b99b65a3..2cde1835 100644 --- a/tests/unit/spatial/platform.js +++ b/tests/unit/spatial/platform.js @@ -5,16 +5,14 @@ module("Platform"); test("Supportable", function(_) { - var ground = Crafty.e("2D, Ground") - .attr({x: 0, y: 10, w:10, h:10}); // y: 10 to 20 - var ground2 = Crafty.e("2D, Ground") - .attr({x: 0, y: 15, w:10, h:10}); // y: 15 to 25 + var ground = Crafty.e("2D, Ground").attr({ x: 0, y: 10, w: 10, h: 10 }); // y: 10 to 20 + var ground2 = Crafty.e("2D, Ground").attr({ x: 0, y: 15, w: 10, h: 10 }); // y: 15 to 25 var landedCount = 0, - liftedCount = 0, - previousGround = null; + liftedCount = 0, + previousGround = null; var ent = Crafty.e("2D, Supportable") - .attr({x: 0, y:0, w:5, h:5}) // y: y to y+5 + .attr({ x: 0, y: 0, w: 5, h: 5 }) // y: y to y+5 .bind("LandedOnGround", function(obj) { _.ok(ent.ground, "entity should be on ground"); _.strictEqual(obj, ent.ground, "ground object should be equal"); @@ -51,7 +49,11 @@ // entity should land when intersecting ground, and should snap to ground ent.y = 7; Crafty.timer.simulateFrames(1); // 1 landed event should have occured - _.strictEqual(ent.y, ground.y - ent.h, "ent y should have been snapped to ground"); + _.strictEqual( + ent.y, + ground.y - ent.h, + "ent y should have been snapped to ground" + ); _.strictEqual(ent.ground, ground, "entity should be on ground"); // no snapping should happen if entity moves while still intersecting ground @@ -69,7 +71,11 @@ // entity should change ground (lift from old & land on new) in a single frame ent.y = 21; Crafty.timer.simulateFrames(1); // 1 lifted event & 1 landed event should have occured - _.strictEqual(ent.y, ground2.y - ent.h, "ent y should have been snapped to ground"); + _.strictEqual( + ent.y, + ground2.y - ent.h, + "ent y should have been snapped to ground" + ); _.strictEqual(ent.ground, ground2, "entity should be on ground2"); // entity should lift from ground, if it looses the required component @@ -84,8 +90,7 @@ // entity should not be able to land on ground if user forbids it // but should be able to land on other ground2 since it's not forbidden ent.bind("CheckLanding", function(candidate) { - if (candidate === ground) - this.canLand = false; + if (candidate === ground) this.canLand = false; }); ent.y = 7; Crafty.timer.simulateFrames(1); // no event should have occured @@ -93,7 +98,11 @@ _.strictEqual(ent.ground, null, "entity should not be on ground"); ent.y = 12; Crafty.timer.simulateFrames(1); // 1 landed event should have occured - _.strictEqual(ent.y, ground2.y - ent.h, "ent y should have been snapped to ground"); + _.strictEqual( + ent.y, + ground2.y - ent.h, + "ent y should have been snapped to ground" + ); _.strictEqual(ent.ground, ground2, "entity should be on ground2"); // entity should lift from ground, if it gets destroyed @@ -102,7 +111,6 @@ _.strictEqual(ent.y, ground2.y - ent.h, "ent y should not have changed"); _.strictEqual(ent.ground, null, "entity should not be on ground"); - _.strictEqual(landedCount, 4, "landed count mismatch"); _.strictEqual(liftedCount, 4, "lifted count mismatch"); @@ -125,86 +133,181 @@ }); test("Gravity", function(_) { - var player = Crafty.e("2D, Gravity") - .attr({ x: 0, y: 100, w: 32, h: 10 }); - - _.strictEqual(player.velocity().y, 0, "velocity should be 0 before activating gravity"); - _.strictEqual(player.acceleration().y, 0, "acceleration should be 0 before activating gravity"); - _.strictEqual(player._gravityConst, 500, "gravityConst should match default value"); + var player = Crafty.e("2D, Gravity").attr({ x: 0, y: 100, w: 32, h: 10 }); + + _.strictEqual( + player.velocity().y, + 0, + "velocity should be 0 before activating gravity" + ); + _.strictEqual( + player.acceleration().y, + 0, + "acceleration should be 0 before activating gravity" + ); + _.strictEqual( + player._gravityConst, + 500, + "gravityConst should match default value" + ); _.strictEqual(player._gravityActive, false, "gravity should not be active"); - player.gravityConst(0.3*50*50); - _.strictEqual(player.velocity().y, 0, "velocity should be 0 before activating gravity"); - _.strictEqual(player.acceleration().y, 0, "acceleration should be 0 before activating gravity"); - _.strictEqual(player._gravityConst, 0.3*50*50, "gravityConst should match new value"); + player.gravityConst(0.3 * 50 * 50); + _.strictEqual( + player.velocity().y, + 0, + "velocity should be 0 before activating gravity" + ); + _.strictEqual( + player.acceleration().y, + 0, + "acceleration should be 0 before activating gravity" + ); + _.strictEqual( + player._gravityConst, + 0.3 * 50 * 50, + "gravityConst should match new value" + ); _.strictEqual(player._gravityActive, false, "gravity should not be active"); - _.strictEqual(player._groundComp, null, "no ground component set before activating gravity"); + _.strictEqual( + player._groundComp, + null, + "no ground component set before activating gravity" + ); player.gravity("platform2"); _.strictEqual(player._gravityActive, true, "gravity should be active"); - _.strictEqual(player._groundComp, "platform2", "new ground component set after activating gravity"); - _.strictEqual(player.velocity().y, 0, "velocity should be 0 before stepping a frame"); - _.strictEqual(player.acceleration().y, player._gravityConst, "acceleration should match gravity constant after activating gravity"); + _.strictEqual( + player._groundComp, + "platform2", + "new ground component set after activating gravity" + ); + _.strictEqual( + player.velocity().y, + 0, + "velocity should be 0 before stepping a frame" + ); + _.strictEqual( + player.acceleration().y, + player._gravityConst, + "acceleration should match gravity constant after activating gravity" + ); player.gravity("platform"); _.strictEqual(player._gravityActive, true, "gravity should be active"); - _.strictEqual(player._groundComp, "platform", "ground component changed after reactivating gravity"); - _.strictEqual(player.velocity().y, 0, "velocity should be 0 before stepping a frame"); - _.strictEqual(player.acceleration().y, player._gravityConst, "acceleration should match gravity constant after activating gravity twice"); + _.strictEqual( + player._groundComp, + "platform", + "ground component changed after reactivating gravity" + ); + _.strictEqual( + player.velocity().y, + 0, + "velocity should be 0 before stepping a frame" + ); + _.strictEqual( + player.acceleration().y, + player._gravityConst, + "acceleration should match gravity constant after activating gravity twice" + ); player.gravityConst(100); - _.strictEqual(player.velocity().y, 0, "velocity should be 0 before stepping a frame"); - _.strictEqual(player.acceleration().y, player._gravityConst, "acceleration should match gravity constant after changing gravity constant while gravity is active"); - _.strictEqual(player._gravityActive, true, "gravity should still be active"); + _.strictEqual( + player.velocity().y, + 0, + "velocity should be 0 before stepping a frame" + ); + _.strictEqual( + player.acceleration().y, + player._gravityConst, + "acceleration should match gravity constant after changing gravity constant while gravity is active" + ); + _.strictEqual( + player._gravityActive, + true, + "gravity should still be active" + ); player.vy = 1000; - player.gravityConst(0.2*50*50); - _.strictEqual(player.velocity().y, 1000, "velocity should not have been reset after changing gravityConst"); + player.gravityConst(0.2 * 50 * 50); + _.strictEqual( + player.velocity().y, + 1000, + "velocity should not have been reset after changing gravityConst" + ); player.antigravity(); _.strictEqual(player._gravityActive, false, "gravity should be inactive"); - _.strictEqual(player.acceleration().y, 0, "acceleration should be zero after deactivating gravity"); - _.strictEqual(player.velocity().y, 0, "velocity should be zero after deactivating gravity"); + _.strictEqual( + player.acceleration().y, + 0, + "acceleration should be zero after deactivating gravity" + ); + _.strictEqual( + player.velocity().y, + 0, + "velocity should be zero after deactivating gravity" + ); player.gravity(); _.strictEqual(player._gravityActive, true, "gravity should be active"); - _.strictEqual(player._groundComp, "platform", "ground component didn't change after reactivating"); - _.strictEqual(player.acceleration().y, player._gravityConst, "acceleration should match gravity constant after activating gravity"); - _.strictEqual(player.velocity().y, 0, "velocity should be still be zero immediately after deactivating gravity"); + _.strictEqual( + player._groundComp, + "platform", + "ground component didn't change after reactivating" + ); + _.strictEqual( + player.acceleration().y, + player._gravityConst, + "acceleration should match gravity constant after activating gravity" + ); + _.strictEqual( + player.velocity().y, + 0, + "velocity should be still be zero immediately after deactivating gravity" + ); }); - test("Supportable - Tunneling", function(_) { var landedCount = 0, - liftedCount = 0, - fps = Crafty.timer.FPS(), - ground; + liftedCount = 0, + fps = Crafty.timer.FPS(), + ground; var player = Crafty.e("2D, Supportable, Motion") - .attr({ x: 10, y: 0, w: 10, h: 10 }) // x-extent [x, x+10] y-extent [y, y+10] - .bind("LiftedOffGround", function() { - liftedCount++; - }) - .bind("LandedOnGround", function() { - landedCount++; - }) - .startGroundDetection("Floor") - .preventGroundTunneling(); - + .attr({ x: 10, y: 0, w: 10, h: 10 }) // x-extent [x, x+10] y-extent [y, y+10] + .bind("LiftedOffGround", function() { + liftedCount++; + }) + .bind("LandedOnGround", function() { + landedCount++; + }) + .startGroundDetection("Floor") + .preventGroundTunneling(); /***************************************** * VERTICAL TUNNELING TESTS *****************************************/ - ground = Crafty.e("2D, Floor") - .attr({ x: 0, y: 100, w: 30, h: 1 }); // x-extent [0, 30] y-extent [100, 101] + ground = Crafty.e("2D, Floor").attr({ x: 0, y: 100, w: 30, h: 1 }); // x-extent [0, 30] y-extent [100, 101] // slow fall velocity does not land player on far away ground player.y = 0; player.vy = 5 * fps; Crafty.timer.simulateFrames(1); // player did not land - _.strictEqual(player.dy, 5, "player should have moved for an expected amount"); - _.strictEqual(player.y + player.h, 5+10, "player should be at expected position"); - _.ok(player.y + player.h < ground.y, "player should not have landed on ground"); + _.strictEqual( + player.dy, + 5, + "player should have moved for an expected amount" + ); + _.strictEqual( + player.y + player.h, + 5 + 10, + "player should be at expected position" + ); + _.ok( + player.y + player.h < ground.y, + "player should not have landed on ground" + ); player.y = 0; player.resetMotion(); Crafty.timer.simulateFrames(1); // player did not lift @@ -213,19 +316,34 @@ player.y = 0; player.vy = 1e6 * fps; Crafty.timer.simulateFrames(1); // landedCount++ - _.strictEqual(player.dy, 1e6, "player should have moved for an expected amount"); - _.strictEqual(player.y + player.h, ground.y, "player should have landed on ground"); + _.strictEqual( + player.dy, + 1e6, + "player should have moved for an expected amount" + ); + _.strictEqual( + player.y + player.h, + ground.y, + "player should have landed on ground" + ); player.y = 0; player.resetMotion(); Crafty.timer.simulateFrames(1); // liftedCount++ - // slow fall velocity lands player directly above ground player.y = 90; player.vy = 5 * fps; Crafty.timer.simulateFrames(1); // landedCount++ - _.strictEqual(player.dy, 5, "player should have moved for an expected amount"); - _.strictEqual(player.y + player.h, ground.y, "player should have landed on ground"); + _.strictEqual( + player.dy, + 5, + "player should have moved for an expected amount" + ); + _.strictEqual( + player.y + player.h, + ground.y, + "player should have landed on ground" + ); player.y = 0; player.resetMotion(); Crafty.timer.simulateFrames(1); // liftedCount++ @@ -234,19 +352,34 @@ player.y = 90; player.vy = 1e6 * fps; Crafty.timer.simulateFrames(1); // landedCount++ - _.strictEqual(player.dy, 1e6, "player should have moved for an expected amount"); - _.strictEqual(player.y + player.h, ground.y, "player should have landed on ground"); + _.strictEqual( + player.dy, + 1e6, + "player should have moved for an expected amount" + ); + _.strictEqual( + player.y + player.h, + ground.y, + "player should have landed on ground" + ); player.y = 0; player.resetMotion(); Crafty.timer.simulateFrames(1); // liftedCount++ - // slow fall velocity lands player while intersecting ground player.y = 91; player.vy = 5 * fps; Crafty.timer.simulateFrames(1); // landedCount++ - _.strictEqual(player.dy, 5, "player should have moved for an expected amount"); - _.strictEqual(player.y + player.h, ground.y, "player should have landed on ground"); + _.strictEqual( + player.dy, + 5, + "player should have moved for an expected amount" + ); + _.strictEqual( + player.y + player.h, + ground.y, + "player should have landed on ground" + ); player.y = 0; player.resetMotion(); Crafty.timer.simulateFrames(1); // liftedCount++ @@ -255,20 +388,38 @@ player.y = 91; player.vy = 1e6 * fps; Crafty.timer.simulateFrames(1); // landedCount++ - _.strictEqual(player.dy, 1e6, "player should have moved for an expected amount"); - _.strictEqual(player.y + player.h, ground.y, "player should have landed on ground"); + _.strictEqual( + player.dy, + 1e6, + "player should have moved for an expected amount" + ); + _.strictEqual( + player.y + player.h, + ground.y, + "player should have landed on ground" + ); player.y = 0; player.resetMotion(); Crafty.timer.simulateFrames(1); // liftedCount++ - // slow fall velocity lands player while intersecting ground player.y = 101; player.vy = 5 * fps; Crafty.timer.simulateFrames(1); // player did not land - _.strictEqual(player.dy, 5, "player should have moved for an expected amount"); - _.strictEqual(player.y + player.h, 101+5+10, "player should be at expected position"); - _.ok(player.y > ground.y + ground.h, "player should not have landed on ground"); + _.strictEqual( + player.dy, + 5, + "player should have moved for an expected amount" + ); + _.strictEqual( + player.y + player.h, + 101 + 5 + 10, + "player should be at expected position" + ); + _.ok( + player.y > ground.y + ground.h, + "player should not have landed on ground" + ); player.y = 0; player.resetMotion(); Crafty.timer.simulateFrames(1); // player did not lift @@ -277,26 +428,47 @@ player.y = 101; player.vy = 1e6 * fps; Crafty.timer.simulateFrames(1); // player did not land - _.strictEqual(player.dy, 1e6, "player should have moved for an expected amount"); - _.strictEqual(player.y + player.h, 101+1e6+10, "player should be at expected position"); - _.ok(player.y > ground.y + ground.h, "player should not have landed on ground"); + _.strictEqual( + player.dy, + 1e6, + "player should have moved for an expected amount" + ); + _.strictEqual( + player.y + player.h, + 101 + 1e6 + 10, + "player should be at expected position" + ); + _.ok( + player.y > ground.y + ground.h, + "player should not have landed on ground" + ); player.y = 0; player.resetMotion(); - Crafty.timer.simulateFrames(1);// player did not lift + Crafty.timer.simulateFrames(1); // player did not lift /***************************************** * HORIZONTAL TUNNELING TESTS *****************************************/ - ground = Crafty.e("2D, Floor") - .attr({ x: 100, y: 0, w: 1, h: 30 }); // x-extent [100, 101] y-extent [0, 30] + ground = Crafty.e("2D, Floor").attr({ x: 100, y: 0, w: 1, h: 30 }); // x-extent [100, 101] y-extent [0, 30] // slow horizontal velocity does not land player on far away ground player.x = 0; player.vx = 5 * fps; Crafty.timer.simulateFrames(1); // player did not land - _.strictEqual(player.dx, 5, "player should have moved for an expected amount"); - _.strictEqual(player.x + player.w, 5+10, "player should be at expected position"); - _.ok(player.x + player.w < ground.x, "player should not have landed on ground"); + _.strictEqual( + player.dx, + 5, + "player should have moved for an expected amount" + ); + _.strictEqual( + player.x + player.w, + 5 + 10, + "player should be at expected position" + ); + _.ok( + player.x + player.w < ground.x, + "player should not have landed on ground" + ); player.x = 0; player.resetMotion(); Crafty.timer.simulateFrames(1); // player did not lift @@ -305,8 +477,16 @@ player.x = 0; player.vx = 1e6 * fps; Crafty.timer.simulateFrames(1); // landedCount++ - _.strictEqual(player.dx, 1e6, "player should have moved for an expected amount"); - _.strictEqual(player.x, ground.x + ground.w - 1, "player should have landed on ground"); + _.strictEqual( + player.dx, + 1e6, + "player should have moved for an expected amount" + ); + _.strictEqual( + player.x, + ground.x + ground.w - 1, + "player should have landed on ground" + ); player.x = 0; player.resetMotion(); Crafty.timer.simulateFrames(1); // liftedCount++ @@ -314,54 +494,91 @@ /***************************************** * DIAGONAL TUNNELING TESTS *****************************************/ - ground = Crafty.e("2D, Floor") - .attr({ x: 99, y: 99, w: 2, h: 2 }); // x-extent [99, 101] y-extent [99, 101] + ground = Crafty.e("2D, Floor").attr({ x: 99, y: 99, w: 2, h: 2 }); // x-extent [99, 101] y-extent [99, 101] // slow diagonal velocity does not land player on far away ground - player.x = 150; player.vx = -5 * fps; - player.y = 150; player.vy = -5 * fps; + player.x = 150; + player.vx = -5 * fps; + player.y = 150; + player.vy = -5 * fps; Crafty.timer.simulateFrames(1); // player did not land - _.strictEqual(player.dx, -5, "player should have moved for an expected amount"); - _.strictEqual(player.dy, -5, "player should have moved for an expected amount"); - _.strictEqual(player.x, 150-5, "player should be at expected position"); - _.strictEqual(player.y, 150-5, "player should be at expected position"); - _.ok(ground.x + ground.w < player.x, "player should not have landed on ground"); - _.ok(ground.y + ground.h < player.y, "player should not have landed on ground"); + _.strictEqual( + player.dx, + -5, + "player should have moved for an expected amount" + ); + _.strictEqual( + player.dy, + -5, + "player should have moved for an expected amount" + ); + _.strictEqual(player.x, 150 - 5, "player should be at expected position"); + _.strictEqual(player.y, 150 - 5, "player should be at expected position"); + _.ok( + ground.x + ground.w < player.x, + "player should not have landed on ground" + ); + _.ok( + ground.y + ground.h < player.y, + "player should not have landed on ground" + ); player.x = 0; player.y = 0; player.resetMotion(); Crafty.timer.simulateFrames(1); // player did not lift // fast diagonal velocity lands player on far away ground, does not tunnel player through ground - player.x = 150; player.vx = -100 * fps; - player.y = 150; player.vy = -100 * fps; + player.x = 150; + player.vx = -100 * fps; + player.y = 150; + player.vy = -100 * fps; Crafty.timer.simulateFrames(1); // landedCount++ - _.strictEqual(player.dx, -100, "player should have moved for an expected amount"); - _.strictEqual(player.dy, -100, "player should have moved for an expected amount"); - _.strictEqual(player.x + player.w, ground.x + 1, "player should have landed on ground"); - _.strictEqual(player.y + player.h, ground.y, "player should have landed on ground"); + _.strictEqual( + player.dx, + -100, + "player should have moved for an expected amount" + ); + _.strictEqual( + player.dy, + -100, + "player should have moved for an expected amount" + ); + _.strictEqual( + player.x + player.w, + ground.x + 1, + "player should have landed on ground" + ); + _.strictEqual( + player.y + player.h, + ground.y, + "player should have landed on ground" + ); player.x = 0; player.y = 0; player.resetMotion(); Crafty.timer.simulateFrames(1); // liftedCount++ - - _.strictEqual(landedCount, 7, "player should have landed correct number of times"); - _.strictEqual(liftedCount, 7, "player should have lifted correct number of times"); + _.strictEqual( + landedCount, + 7, + "player should have landed correct number of times" + ); + _.strictEqual( + liftedCount, + 7, + "player should have lifted correct number of times" + ); }); - test("Integrationtest - Gravity", function(_) { var done = false; - var ground = Crafty.e("2D, platform") - .attr({ x: 0, y: 280, w: 600, h: 20 }); + var ground = Crafty.e("2D, platform").attr({ x: 0, y: 280, w: 600, h: 20 }); var player = Crafty.e("2D, Gravity") - .attr({ x: 0, y: 100, w: 32, h: 16 }) - .gravityConst(0.3*50*50) - .gravity("platform"); - + .attr({ x: 0, y: 100, w: 32, h: 16 }) + .gravityConst(0.3 * 50 * 50) + .gravity("platform"); var vel = -1; player.bind("UpdateFrame", function() { @@ -373,7 +590,8 @@ } }); - var landCount = 0, liftCount = 0; + var landCount = 0, + liftCount = 0; player.bind("LandedOnGround", function() { landCount++; _.strictEqual(this.acceleration().y, 0, "acceleration should be zero"); @@ -386,10 +604,18 @@ Crafty.timer.simulateFrames(3); vel = -1; }); - this.attr({y: 100}); + this.attr({ y: 100 }); } else { - _.strictEqual(landCount, 2, "two land on ground events should have been registered"); - _.strictEqual(liftCount, 1, "one lift off ground event should have been registered"); + _.strictEqual( + landCount, + 2, + "two land on ground events should have been registered" + ); + _.strictEqual( + liftCount, + 1, + "one lift off ground event should have been registered" + ); ground.destroy(); player.destroy(); @@ -401,5 +627,4 @@ Crafty.timer.simulateFrames(75); _.ok(done, "Integrationtest completed"); }); - })(); diff --git a/tests/unit/spatial/raycast.js b/tests/unit/spatial/raycast.js index 6137c5ab..3bf7bff5 100644 --- a/tests/unit/spatial/raycast.js +++ b/tests/unit/spatial/raycast.js @@ -2,7 +2,6 @@ var module = QUnit.module; var test = QUnit.test; - var cellsize = 64; var EAST = new Crafty.math.Vector2D(1, 0).normalize(); @@ -15,24 +14,23 @@ var NORTH_WEST = new Crafty.math.Vector2D(-1, -1).normalize(); var ANGLE_POS_41 = new Crafty.math.Vector2D( - Math.cos(41 * Math.PI / 180), - -Math.sin(41 * Math.PI / 180) // y-axis is inverted in Crafty - ).normalize(); + Math.cos((41 * Math.PI) / 180), + -Math.sin((41 * Math.PI) / 180) // y-axis is inverted in Crafty + ).normalize(); var ANGLE_NEG_22_5 = new Crafty.math.Vector2D( - Math.cos(-22.5 * Math.PI / 180), - -Math.sin(-22.5 * Math.PI / 180) // y-axis is inverted in Crafty - ).normalize(); + Math.cos((-22.5 * Math.PI) / 180), + -Math.sin((-22.5 * Math.PI) / 180) // y-axis is inverted in Crafty + ).normalize(); var ANGLE_NEG_1 = new Crafty.math.Vector2D( - Math.cos(-1 * Math.PI / 180), - -Math.sin(-1 * Math.PI / 180) // y-axis is inverted in Crafty - ).normalize(); - + Math.cos((-1 * Math.PI) / 180), + -Math.sin((-1 * Math.PI) / 180) // y-axis is inverted in Crafty + ).normalize(); ////////////////////// // UTILITY FUNCTIONS ////////////////////// - function createEntity (cellX, cellY, cellWidth, cellHeight) { + function createEntity(cellX, cellY, cellWidth, cellHeight) { cellX = cellX || 0; cellY = cellY || 0; cellWidth = cellWidth || 1; @@ -42,25 +40,33 @@ x: cellX * cellsize + 1, y: cellY * cellsize + 1, w: cellWidth * cellsize - 2, - h: cellHeight * cellsize - 2, + h: cellHeight * cellsize - 2 }); return e; } - function diagonalDistance (diffX, diffY) { + function diagonalDistance(diffX, diffY) { var dX = diffX * cellsize + 1, - dY = diffY * cellsize + 1; + dY = diffY * cellsize + 1; return Math.sqrt(dX * dX + dY * dY); } - function checkResults (origin, direction, raycastResults, expectedResults) { - QUnit.assert.strictEqual(raycastResults.length, Object.keys(expectedResults).length, "expected ids count must match"); - - var actualId, actualDistance, expectedDistance, - actualIntersectionX, expectedIntersectionX, - actualIntersectionY, expectedIntersectionY; + function checkResults(origin, direction, raycastResults, expectedResults) { + QUnit.assert.strictEqual( + raycastResults.length, + Object.keys(expectedResults).length, + "expected ids count must match" + ); + + var actualId, + actualDistance, + expectedDistance, + actualIntersectionX, + expectedIntersectionX, + actualIntersectionY, + expectedIntersectionY; for (var i = 0, l = raycastResults.length; i < l; ++i) { actualId = raycastResults[i].obj[0]; actualDistance = raycastResults[i].distance; @@ -70,10 +76,25 @@ expectedIntersectionX = origin._x + expectedDistance * direction.x; expectedIntersectionY = origin._y + expectedDistance * direction.y; - QUnit.assert.ok(typeof expectedResults[actualId] !== 'undefined', "actual id is among expected ids"); - QUnit.assert.strictEqual(actualDistance.toFixed(2), expectedDistance.toFixed(2), "actual distance matches expected distance"); - QUnit.assert.strictEqual(actualIntersectionX.toFixed(2), expectedIntersectionX.toFixed(2), "actual intersection point x matches expected intersection point x"); - QUnit.assert.strictEqual(actualIntersectionY.toFixed(2), expectedIntersectionY.toFixed(2), "actual intersection point y matches expected intersection point y"); + QUnit.assert.ok( + typeof expectedResults[actualId] !== "undefined", + "actual id is among expected ids" + ); + QUnit.assert.strictEqual( + actualDistance.toFixed(2), + expectedDistance.toFixed(2), + "actual distance matches expected distance" + ); + QUnit.assert.strictEqual( + actualIntersectionX.toFixed(2), + expectedIntersectionX.toFixed(2), + "actual intersection point x matches expected intersection point x" + ); + QUnit.assert.strictEqual( + actualIntersectionY.toFixed(2), + expectedIntersectionY.toFixed(2), + "actual intersection point y matches expected intersection point y" + ); } } @@ -85,20 +106,22 @@ var mapSize = 64 * 5; - var LEFT = {_x: 0, _y: mapSize / 2}; - var RIGHT = {_x: mapSize, _y: mapSize / 2}; - var TOP = {_x: mapSize/2, _y: 0}; - var BOTTOM = {_x: mapSize / 2, _y: mapSize}; - var TOP_LEFT = {_x: 0, _y: 0}; - var TOP_RIGHT = {_x: mapSize, _y: 0}; - var BOTTOM_LEFT = {_x: 0, _y: mapSize}; - var BOTTOM_RIGHT = {_x: mapSize, _y: mapSize}; + var LEFT = { _x: 0, _y: mapSize / 2 }; + var RIGHT = { _x: mapSize, _y: mapSize / 2 }; + var TOP = { _x: mapSize / 2, _y: 0 }; + var BOTTOM = { _x: mapSize / 2, _y: mapSize }; + var TOP_LEFT = { _x: 0, _y: 0 }; + var TOP_RIGHT = { _x: mapSize, _y: 0 }; + var BOTTOM_LEFT = { _x: 0, _y: mapSize }; + var BOTTOM_RIGHT = { _x: mapSize, _y: mapSize }; test("Check multiple hits", function(_) { - var origin, direction, - expectedResults = {}, - e, f, - results; + var origin, + direction, + expectedResults = {}, + e, + f, + results; /* ------ @@ -128,8 +151,10 @@ direction = SOUTH_EAST; expectedResults = {}; - e = createEntity(0, 0); expectedResults[e[0]] = diagonalDistance(0, 0); - f = createEntity(4, 4); expectedResults[f[0]] = diagonalDistance(4, 4); + e = createEntity(0, 0); + expectedResults[e[0]] = diagonalDistance(0, 0); + f = createEntity(4, 4); + expectedResults[f[0]] = diagonalDistance(4, 4); results = Crafty.raycast(origin, direction); checkResults(origin, direction, results, expectedResults); @@ -165,13 +190,13 @@ direction = SOUTH_EAST; expectedResults = {}; - e = createEntity(0, 0); expectedResults[e[0]] = diagonalDistance(0, 0); - f = createEntity(4, 4); expectedResults[f[0]] = diagonalDistance(4, 4); + e = createEntity(0, 0); + expectedResults[e[0]] = diagonalDistance(0, 0); + f = createEntity(4, 4); + expectedResults[f[0]] = diagonalDistance(4, 4); var temps = []; for (var i = 0; i < 5; ++i) - for (var j = 0; j < 5; ++j) - if (i !== j) - temps.push(createEntity(i, j)); + for (var j = 0; j < 5; ++j) if (i !== j) temps.push(createEntity(i, j)); results = Crafty.raycast(origin, direction); checkResults(origin, direction, results, expectedResults); @@ -182,7 +207,6 @@ temps[i].destroy(); } - /* ------ | Grid | @@ -211,8 +235,10 @@ direction = SOUTH_EAST; expectedResults = {}; - e = createEntity(1,1); expectedResults[e[0]] = diagonalDistance(1, 1); - f = createEntity(1,1); expectedResults[f[0]] = diagonalDistance(1, 1); + e = createEntity(1, 1); + expectedResults[e[0]] = diagonalDistance(1, 1); + f = createEntity(1, 1); + expectedResults[f[0]] = diagonalDistance(1, 1); results = Crafty.raycast(origin, direction); checkResults(origin, direction, results, expectedResults); @@ -221,12 +247,13 @@ f.destroy(); }); - test("Check big entity hits", function(_) { - var origin, direction, - expectedResults = {}, - e, f, - results; + var origin, + direction, + expectedResults = {}, + e, + f, + results; /* ------ @@ -256,7 +283,8 @@ direction = SOUTH_EAST; expectedResults = {}; - e = createEntity(0, 4, 5, 1); expectedResults[e[0]] = diagonalDistance(4,4); + e = createEntity(0, 4, 5, 1); + expectedResults[e[0]] = diagonalDistance(4, 4); results = Crafty.raycast(origin, direction); checkResults(origin, direction, results, expectedResults); @@ -291,8 +319,10 @@ direction = SOUTH_EAST; expectedResults = {}; - e = createEntity(0, 4, 5, 1); expectedResults[e[0]] = diagonalDistance(4,4); - f = createEntity(0, 0, 4, 5); expectedResults[f[0]] = diagonalDistance(0,0); + e = createEntity(0, 4, 5, 1); + expectedResults[e[0]] = diagonalDistance(4, 4); + f = createEntity(0, 0, 4, 5); + expectedResults[f[0]] = diagonalDistance(0, 0); results = Crafty.raycast(origin, direction); checkResults(origin, direction, results, expectedResults); @@ -301,12 +331,12 @@ f.destroy(); }); - test("Check diagonal hits", function(_) { - var origin, direction, - expectedResults = {}, - e, - results; + var origin, + direction, + expectedResults = {}, + e, + results; /* ------ @@ -327,7 +357,8 @@ (0,320) (320,320) */ expectedResults = {}; - e = createEntity(2, 2); expectedResults[e[0]] = diagonalDistance(2, 2); + e = createEntity(2, 2); + expectedResults[e[0]] = diagonalDistance(2, 2); /* ----- @@ -381,16 +412,15 @@ results = Crafty.raycast(origin, direction); checkResults(origin, direction, results, expectedResults); - e.destroy(); }); - test("Check vert/horiz hits", function(_) { - var origin, direction, - expectedResults = {}, - e, - results; + var origin, + direction, + expectedResults = {}, + e, + results; /* ------ @@ -411,7 +441,8 @@ (0,320) (320,320) */ expectedResults = {}; - e = createEntity(2, 2); expectedResults[e[0]] = diagonalDistance(2, 0); + e = createEntity(2, 2); + expectedResults[e[0]] = diagonalDistance(2, 0); /* ----- @@ -465,17 +496,15 @@ results = Crafty.raycast(origin, direction); checkResults(origin, direction, results, expectedResults); - e.destroy(); }); - test("Check no hits", function(_) { - var origin, direction, - expectedResults = {}, - e, - results; - + var origin, + direction, + expectedResults = {}, + e, + results; /* ------ @@ -570,7 +599,7 @@ Origin = (-1000,-1000) Direction = (0.75,-0.66) */ - origin = {_x: -1000, _y: -1000}; + origin = { _x: -1000, _y: -1000 }; direction = ANGLE_POS_41; results = Crafty.raycast(origin, direction); @@ -611,12 +640,12 @@ checkResults(origin, direction, results, expectedResults); }); - test("Check colinear hits", function(_) { - var origin, direction, - expectedResults = {}, - e, - results; + var origin, + direction, + expectedResults = {}, + e, + results; /* ------ @@ -642,11 +671,12 @@ Origin = (0,0) Direction = (1,0) */ - origin = {_x: 0, _y: 1}; - direction = {x: 1, y: 0}; + origin = { _x: 0, _y: 1 }; + direction = { x: 1, y: 0 }; expectedResults = {}; - e = createEntity(2, 0); expectedResults[e[0]] = diagonalDistance(2, 0); + e = createEntity(2, 0); + expectedResults[e[0]] = diagonalDistance(2, 0); results = Crafty.raycast(origin, direction); checkResults(origin, direction, results, expectedResults); @@ -677,11 +707,12 @@ Origin = (-192+1, -96-1) Direction = (0,1) */ - origin = {_x: -3 * cellsize + 1, _y: -1.5 * cellsize - 1}; - direction = {x: 0, y: -1}; + origin = { _x: -3 * cellsize + 1, _y: -1.5 * cellsize - 1 }; + direction = { x: 0, y: -1 }; expectedResults = {}; - e = createEntity(-3, -4); expectedResults[e[0]] = 1.5 * cellsize; + e = createEntity(-3, -4); + expectedResults[e[0]] = 1.5 * cellsize; results = Crafty.raycast(origin, direction); checkResults(origin, direction, results, expectedResults); @@ -689,12 +720,15 @@ e.destroy(); }); - test("Check first hit", function(_) { - var origin, direction, - expectedResults = {}, - e, f, g, h, - results; + var origin, + direction, + expectedResults = {}, + e, + f, + g, + h, + results; /* ------ @@ -724,7 +758,8 @@ direction = NORTH_EAST; expectedResults = {}; - e = createEntity(1, 3, 1, 1); expectedResults[e[0]] = diagonalDistance(1, 1); + e = createEntity(1, 3, 1, 1); + expectedResults[e[0]] = diagonalDistance(1, 1); f = createEntity(2, 2, 1, 1); g = createEntity(3, 1, 1, 1); h = createEntity(4, 0, 1, 1); @@ -738,12 +773,15 @@ h.destroy(); }); - test("Check maxDistance hits", function(_) { - var origin, direction, - expectedResults = {}, - e, f, g, h, - results; + var origin, + direction, + expectedResults = {}, + e, + f, + g, + h, + results; /* ------ @@ -773,16 +811,17 @@ direction = NORTH_EAST; expectedResults = {}; - e = createEntity(1, 3, 1, 1); expectedResults[e[0]] = diagonalDistance(1, 1); + e = createEntity(1, 3, 1, 1); + expectedResults[e[0]] = diagonalDistance(1, 1); f = createEntity(2, 2, 1, 1); g = createEntity(3, 1, 1, 1); h = createEntity(4, 0, 1, 1); - results = Crafty.raycast(origin, direction, diagonalDistance(2,2)-1); + results = Crafty.raycast(origin, direction, diagonalDistance(2, 2) - 1); checkResults(origin, direction, results, expectedResults); expectedResults[f[0]] = diagonalDistance(2, 2); - results = Crafty.raycast(origin, direction, diagonalDistance(2,2)); + results = Crafty.raycast(origin, direction, diagonalDistance(2, 2)); checkResults(origin, direction, results, expectedResults); e.destroy(); @@ -791,12 +830,15 @@ h.destroy(); }); - test("Check component filter", function(_) { - var origin, direction, - expectedResults = {}, - e, f, g, h, - results; + var origin, + direction, + expectedResults = {}, + e, + f, + g, + h, + results; /* ------ @@ -826,9 +868,13 @@ direction = NORTH_EAST; expectedResults = {}; - e = createEntity(1, 3, 1, 1); e.addComponent("RAY"); expectedResults[e[0]] = diagonalDistance(1, 1); + e = createEntity(1, 3, 1, 1); + e.addComponent("RAY"); + expectedResults[e[0]] = diagonalDistance(1, 1); f = createEntity(2, 2, 1, 1); - g = createEntity(3, 1, 1, 1); g.addComponent("RAY"); expectedResults[g[0]] = diagonalDistance(3, 3); + g = createEntity(3, 1, 1, 1); + g.addComponent("RAY"); + expectedResults[g[0]] = diagonalDistance(3, 3); h = createEntity(4, 0, 1, 1); results = Crafty.raycast(origin, direction, "RAY"); @@ -840,12 +886,13 @@ h.destroy(); }); - test("Check origin within entity", function(_) { - var origin, direction, - expectedResults = {}, - e, f, - results; + var origin, + direction, + expectedResults = {}, + e, + f, + results; /* ------ @@ -871,7 +918,7 @@ Origin = (32,32) Direction = (1,0) */ - origin = {_x: cellsize / 2, _y: cellsize / 2}; + origin = { _x: cellsize / 2, _y: cellsize / 2 }; direction = EAST; expectedResults = {}; @@ -890,12 +937,13 @@ f.destroy(); }); - test("Check intersection at 0 distance", function(_) { - var origin, direction, - expectedResults = {}, - e, f, - results; + var origin, + direction, + expectedResults = {}, + e, + f, + results; /* ------ @@ -921,16 +969,18 @@ Origin = (192-1,-128-1) Direction = (-1,-1) */ - origin = {_x: 3*cellsize - 1, _y: -2*cellsize - 1}; + origin = { _x: 3 * cellsize - 1, _y: -2 * cellsize - 1 }; direction = NORTH_WEST; expectedResults = {}; - e = createEntity(2, -3, 1, 1); expectedResults[e[0]] = 0; + e = createEntity(2, -3, 1, 1); + expectedResults[e[0]] = 0; results = Crafty.raycast(origin, direction, -Infinity); checkResults(origin, direction, results, expectedResults); - f = createEntity(1, -4, 2, 2); expectedResults[f[0]] = 0; + f = createEntity(1, -4, 2, 2); + expectedResults[f[0]] = 0; results = Crafty.raycast(origin, direction, Infinity); checkResults(origin, direction, results, expectedResults); @@ -938,12 +988,15 @@ f.destroy(); }); - test("Check result sorting", function(_) { - var origin, direction, - expectedResults = {}, - e, f, g, h, - results; + var origin, + direction, + expectedResults = {}, + e, + f, + g, + h, + results; /* ------ @@ -969,7 +1022,7 @@ Origin = (32,32) Direction = (0.99, 0.01) */ - origin = {_x: cellsize / 2, _y: cellsize / 2}; + origin = { _x: cellsize / 2, _y: cellsize / 2 }; direction = ANGLE_NEG_1; expectedResults = {}; @@ -992,25 +1045,34 @@ _.strictEqual(results.length, 4, "all entities found"); _.strictEqual(results[0].obj[0], f[0], "entity f first hit with ray"); - _.ok(results[0].distance > 0.5*cellsize, "entity f first hit with ray"); + _.ok(results[0].distance > 0.5 * cellsize, "entity f first hit with ray"); _.strictEqual(results[0].x, f.x + f.w, "entity f first hit with ray"); - _.ok(results[0].y > cellsize/32, "entity f first hit with ray"); + _.ok(results[0].y > cellsize / 32, "entity f first hit with ray"); _.strictEqual(results[1].obj[0], g[0], "entity g second hit with ray"); - _.ok(results[1].distance > results[0].distance, "entity g second hit with ray"); - _.ok(results[1].distance > 1.5*cellsize, "entity g second hit with ray"); + _.ok( + results[1].distance > results[0].distance, + "entity g second hit with ray" + ); + _.ok(results[1].distance > 1.5 * cellsize, "entity g second hit with ray"); _.strictEqual(results[1].x, g.x + g.w, "entity g second hit with ray"); _.ok(results[1].y > results[0].y, "entity g second hit with ray"); _.strictEqual(results[2].obj[0], h[0], "entity h third hit with ray"); - _.ok(results[2].distance > results[1].distance, "entity h third hit with ray"); - _.ok(results[2].distance > 2.5*cellsize, "entity h third hit with ray"); + _.ok( + results[2].distance > results[1].distance, + "entity h third hit with ray" + ); + _.ok(results[2].distance > 2.5 * cellsize, "entity h third hit with ray"); _.strictEqual(results[2].x, h.x, "entity h third hit with ray"); _.ok(results[2].y > results[1].y, "entity h third hit with ray"); _.strictEqual(results[3].obj[0], e[0], "entity e fourth hit with ray"); - _.ok(results[3].distance > results[2].distance, "entity e fourth hit with ray"); - _.ok(results[3].distance > 3.5*cellsize, "entity e fourth hit with ray"); + _.ok( + results[3].distance > results[2].distance, + "entity e fourth hit with ray" + ); + _.ok(results[3].distance > 3.5 * cellsize, "entity e fourth hit with ray"); _.strictEqual(results[3].x, e.x + e.w, "entity e fourth hit with ray"); _.ok(results[3].y > results[2].y, "entity e fourth hit with ray"); @@ -1020,12 +1082,14 @@ h.destroy(); }); - test("Check intersection with hitbox outside entity (CBR)", function(_) { - var origin, direction, - expectedResults = {}, - e, f, g, - results; + var origin, + direction, + expectedResults = {}, + e, + f, + g, + results; /* ------ @@ -1056,26 +1120,34 @@ expectedResults = {}; e = createEntity(0, 2, 2, 1).collision([ - cellsize, 0, - 2*cellsize-2, 0, - 2*cellsize-2, cellsize-2, - cellsize, cellsize-2 - ]); - expectedResults[e[0]] = 1*cellsize + 1; + cellsize, + 0, + 2 * cellsize - 2, + 0, + 2 * cellsize - 2, + cellsize - 2, + cellsize, + cellsize - 2 + ]); + expectedResults[e[0]] = 1 * cellsize + 1; f = createEntity(2, 3, 1, 1).collision([ - 0, -cellsize, - cellsize-2, -cellsize, - cellsize-2, -2, - 0, -2 + 0, + -cellsize, + cellsize - 2, + -cellsize, + cellsize - 2, + -2, + 0, + -2 ]); //TODO remove this once new hitbox updates entry in map f.x++; f.x--; - expectedResults[f[0]] = 2*cellsize + 1; + expectedResults[f[0]] = 2 * cellsize + 1; g = createEntity(3, 2, 1, 1); - expectedResults[g[0]] = 3*cellsize + 1; + expectedResults[g[0]] = 3 * cellsize + 1; results = Crafty.raycast(origin, direction); checkResults(origin, direction, results, expectedResults); @@ -1085,14 +1157,15 @@ g.destroy(); }); - test("Check more complex scenarios", function(_) { - var origin, direction, - expectedResults = {}, - e, f, - dX, dY, - results; - + var origin, + direction, + expectedResults = {}, + e, + f, + dX, + dY, + results; /* ------ @@ -1118,11 +1191,12 @@ Origin = (256,0) Direction = (1,-1) */ - origin = {_x: 0, _y: 256}; + origin = { _x: 0, _y: 256 }; direction = NORTH_EAST; expectedResults = {}; - e = createEntity(3, 0, 1, 1); expectedResults[e[0]] = diagonalDistance(3, 3); + e = createEntity(3, 0, 1, 1); + expectedResults[e[0]] = diagonalDistance(3, 3); f = Crafty.e("2D, Collision").attr({ x: 112, y: 176, w: 32, h: 32 }); results = Crafty.raycast(origin, direction); @@ -1155,7 +1229,7 @@ Origin = (0,192) Direction = (2,-1) */ - origin = {_x: 0, _y: 192}; + origin = { _x: 0, _y: 192 }; direction = new Crafty.math.Vector2D(2, -1).normalize(); expectedResults = {}; @@ -1196,7 +1270,7 @@ Origin = (32,32) Direction = (0.924, 0.3827) */ - origin = {_x: cellsize / 2, _y: cellsize / 2}; + origin = { _x: cellsize / 2, _y: cellsize / 2 }; direction = ANGLE_NEG_22_5; expectedResults = {}; @@ -1207,53 +1281,72 @@ _.strictEqual(results.length, 1, "only first entity found"); _.strictEqual(results[0].obj[0], f[0], "entity f hit with ray"); - _.ok(results[0].distance > cellsize/2 + 1, "distance greater than x-difference"); - _.ok(results[0].distance < diagonalDistance(0.5, 0.5), "distance less than diagonal distance"); - _.strictEqual(results[0].x, f.x, "x intersection point same as f's left side"); - _.ok(results[0].y > f.y + 0.5 *f.h, "y intersection point lower than f's center"); - _.ok(results[0].y < f.y + 0.75*f.h, "y intersection point higher than 3/4 of f's height"); + _.ok( + results[0].distance > cellsize / 2 + 1, + "distance greater than x-difference" + ); + _.ok( + results[0].distance < diagonalDistance(0.5, 0.5), + "distance less than diagonal distance" + ); + _.strictEqual( + results[0].x, + f.x, + "x intersection point same as f's left side" + ); + _.ok( + results[0].y > f.y + 0.5 * f.h, + "y intersection point lower than f's center" + ); + _.ok( + results[0].y < f.y + 0.75 * f.h, + "y intersection point higher than 3/4 of f's height" + ); e.destroy(); f.destroy(); }); - test("Check a complex scenario constructed in playground", function(_) { - var origin, direction, magnitude, - expectedResults = {}, - results; - - - Crafty.e('2D, Collision') - .setName('Trapezoid') - .attr({w: 200, h: 100}) - .origin('center') - .collision(new Crafty.polygon([50, 0, 0, 100, 200, 100, 150, 0])) - .attr({x: 53, y: -177, rotation: -175}); - - Crafty.e('2D, Collision') - .setName('Parallelogram') - .attr({w: 100, h: 100}) - .origin('center') - .collision(new Crafty.polygon([0, 0, 25, 100, 100, 100, 75, 0])) - .attr({x: -44, y: -206, rotation: 0}); - - Crafty.e('2D, Collision') - .setName('Triangle') - .attr({w: 300, h: 100}) - .origin('center') - .collision(new Crafty.polygon([25, 75, 250, 25, 275, 50])) - .attr({x: -96, y: -130, rotation: -107}); - - Crafty.e('2D, Collision') - .setName('CBR') - .attr({w: 100, h: 100}) - .origin('center') - .collision(new Crafty.polygon([75, -25, 125, -25, 125, 25, 75, 25])) - .attr({x: -23, y: -204, rotation: 14}); - - origin = {_x: 161, _y: 60.98333740234375}; - direction = new Crafty.math.Vector2D(22 - origin._x, -241.01666259765625 - origin._y); + var origin, + direction, + magnitude, + expectedResults = {}, + results; + + Crafty.e("2D, Collision") + .setName("Trapezoid") + .attr({ w: 200, h: 100 }) + .origin("center") + .collision(new Crafty.polygon([50, 0, 0, 100, 200, 100, 150, 0])) + .attr({ x: 53, y: -177, rotation: -175 }); + + Crafty.e("2D, Collision") + .setName("Parallelogram") + .attr({ w: 100, h: 100 }) + .origin("center") + .collision(new Crafty.polygon([0, 0, 25, 100, 100, 100, 75, 0])) + .attr({ x: -44, y: -206, rotation: 0 }); + + Crafty.e("2D, Collision") + .setName("Triangle") + .attr({ w: 300, h: 100 }) + .origin("center") + .collision(new Crafty.polygon([25, 75, 250, 25, 275, 50])) + .attr({ x: -96, y: -130, rotation: -107 }); + + Crafty.e("2D, Collision") + .setName("CBR") + .attr({ w: 100, h: 100 }) + .origin("center") + .collision(new Crafty.polygon([75, -25, 125, -25, 125, 25, 75, 25])) + .attr({ x: -23, y: -204, rotation: 14 }); + + origin = { _x: 161, _y: 60.98333740234375 }; + direction = new Crafty.math.Vector2D( + 22 - origin._x, + -241.01666259765625 - origin._y + ); magnitude = direction.magnitude(); direction.normalize(); @@ -1261,5 +1354,4 @@ results = Crafty.raycast(origin, direction, magnitude); checkResults(origin, direction, results, expectedResults); }); - -})(); \ No newline at end of file +})(); diff --git a/tests/unit/spatial/sat.js b/tests/unit/spatial/sat.js index c724d49f..1a429693 100644 --- a/tests/unit/spatial/sat.js +++ b/tests/unit/spatial/sat.js @@ -6,8 +6,8 @@ test("simple overlap along x axis", function(_) { var e = Crafty.e("2D, Collision"); - var poly1 = new Crafty.polygon([0,0, 0,3, 3,3, 3,0]); - var poly2 = new Crafty.polygon([2,0, 2,3, 5,3, 5,0]); + var poly1 = new Crafty.polygon([0, 0, 0, 3, 3, 3, 3, 0]); + var poly2 = new Crafty.polygon([2, 0, 2, 3, 5, 3, 5, 0]); // order 1 var o = e._SAT(poly1, poly2); @@ -15,60 +15,55 @@ _.strictEqual(o.overlap, -1, "Overlap by 1 unit"); _.strictEqual(o.nx, -1, "nx is -1"); _.strictEqual(o.ny, 0, "ny is 0"); - + // order 2 o = e._SAT(poly2, poly1); _.notEqual(o, false, "Overlap exists"); _.strictEqual(o.overlap, -1, "Overlap by 1 unit"); _.strictEqual(o.nx, 1, "nx is 1"); _.strictEqual(o.ny, 0, "ny is 0"); - }); test("simple overlap along y axis", function(_) { var e = Crafty.e("2D, Collision"); - var poly1 = new Crafty.polygon([0,0, 0,3, 3,3, 3,0]); - var poly2 = new Crafty.polygon([0,2, 0,5, 3,5, 3,2]); + var poly1 = new Crafty.polygon([0, 0, 0, 3, 3, 3, 3, 0]); + var poly2 = new Crafty.polygon([0, 2, 0, 5, 3, 5, 3, 2]); var o = e._SAT(poly1, poly2); _.notEqual(o, false, "Overlap exists"); _.strictEqual(o.overlap, -1, "Overlap by 1 unit"); _.strictEqual(o.ny, -1, "ny is -1"); _.strictEqual(o.nx, 0, "nx is 0"); - + // order 2 o = e._SAT(poly2, poly1); _.notEqual(o, false, "Overlap exists"); _.strictEqual(o.overlap, -1, "Overlap by 1 unit"); _.strictEqual(o.ny, 1, "ny is 1"); _.strictEqual(o.nx, 0, "nx is 0"); - }); - - test("overlap smaller along x axis", function(_) { var e = Crafty.e("2D, Collision"); - var poly1 = new Crafty.polygon([0,0, 0,3, 3,3, 3,0]); - var poly2 = new Crafty.polygon([2,1, 2,4, 5,4, 5,1]); + var poly1 = new Crafty.polygon([0, 0, 0, 3, 3, 3, 3, 0]); + var poly2 = new Crafty.polygon([2, 1, 2, 4, 5, 4, 5, 1]); var o = e._SAT(poly1, poly2); _.notEqual(o, false, "Overlap exists"); _.strictEqual(o.overlap, -1, "Overlap by 1 unit"); _.strictEqual(o.nx, -1, "nx is -1"); _.strictEqual(o.ny, 0, "ny is 0"); - + // order 2 o = e._SAT(poly2, poly1); _.notEqual(o, false, "Overlap exists"); _.strictEqual(o.overlap, -1, "Overlap by 1 unit"); _.strictEqual(o.nx, 1, "nx is 1"); _.strictEqual(o.ny, 0, "ny is 0"); - }); test("overlap smaller along y axis", function(_) { var e = Crafty.e("2D, Collision"); - var poly1 = new Crafty.polygon([0,0, 0,3, 3,3, 3,0]); - var poly2 = new Crafty.polygon([1,2, 1,5, 4,5, 4,2]); + var poly1 = new Crafty.polygon([0, 0, 0, 3, 3, 3, 3, 0]); + var poly2 = new Crafty.polygon([1, 2, 1, 5, 4, 5, 4, 2]); var o = e._SAT(poly1, poly2); _.notEqual(o, false, "Overlap exists"); _.strictEqual(o.overlap, -1, "Overlap by 1 unit"); @@ -83,10 +78,10 @@ _.strictEqual(o.nx, 0, "nx is 0"); }); - test("overlap with non parallel faces, but axis-aligned normal", function(_){ + test("overlap with non parallel faces, but axis-aligned normal", function(_) { var e = Crafty.e("2D, Collision"); - var poly1 = new Crafty.polygon([0,0, 0,3, 3,3, 3,0]); - var poly2 = new Crafty.polygon([2,2, 4,4, 6,2, 4,0]); + var poly1 = new Crafty.polygon([0, 0, 0, 3, 3, 3, 3, 0]); + var poly2 = new Crafty.polygon([2, 2, 4, 4, 6, 2, 4, 0]); var o = e._SAT(poly1, poly2); _.notEqual(o, false, "Overlap exists"); _.strictEqual(o.overlap, -1, "Overlap by 1 unit"); @@ -101,26 +96,25 @@ _.strictEqual(o.ny, 0, "nx is 0"); }); - test("overlap with non parallel faces, non-axis-aligned normal", function(_){ + test("overlap with non parallel faces, non-axis-aligned normal", function(_) { var e = Crafty.e("2D, Collision"); - var poly1 = new Crafty.polygon([0,0, 0,3, 3,3, 3,0]); - var poly2 = new Crafty.polygon([1,4, 4,4, 4,1]); - function is_inverse_sqrt2(x){ - return (x > 0.707) && (x < 0.708); + var poly1 = new Crafty.polygon([0, 0, 0, 3, 3, 3, 3, 0]); + var poly2 = new Crafty.polygon([1, 4, 4, 4, 4, 1]); + function is_inverse_sqrt2(x) { + return x > 0.707 && x < 0.708; } - + var o = e._SAT(poly1, poly2); _.notEqual(o, false, "Overlap exists"); _.ok(is_inverse_sqrt2(-o.overlap), "Overlap by 1/sqrt(2)"); - _.ok( is_inverse_sqrt2(-o.nx), "nx is -1/sqrt(2)"); - _.ok( is_inverse_sqrt2(-o.ny), "ny is -1/sqrt(2)"); - + _.ok(is_inverse_sqrt2(-o.nx), "nx is -1/sqrt(2)"); + _.ok(is_inverse_sqrt2(-o.ny), "ny is -1/sqrt(2)"); // order 2 o = e._SAT(poly2, poly1); _.notEqual(o, false, "Overlap exists"); _.ok(is_inverse_sqrt2(-o.overlap), "Overlap by 1/sqrt(2)"); - _.ok( is_inverse_sqrt2(o.nx), "nx is +1/sqrt(2)"); - _.ok( is_inverse_sqrt2(o.ny), "ny is +1/sqrt(2)"); + _.ok(is_inverse_sqrt2(o.nx), "nx is +1/sqrt(2)"); + _.ok(is_inverse_sqrt2(o.ny), "ny is +1/sqrt(2)"); }); })(); diff --git a/tests/unit/spatial/spatial-grid.js b/tests/unit/spatial/spatial-grid.js index 75473150..f2f75568 100644 --- a/tests/unit/spatial/spatial-grid.js +++ b/tests/unit/spatial/spatial-grid.js @@ -2,7 +2,6 @@ var module = QUnit.module; var test = QUnit.test; - var cellsize = 64; var EAST = new Crafty.math.Vector2D(1, 0).normalize(); @@ -15,24 +14,23 @@ var NORTH_WEST = new Crafty.math.Vector2D(-1, -1).normalize(); var ANGLE_POS_41 = new Crafty.math.Vector2D( - Math.cos(41 * Math.PI / 180), - -Math.sin(41 * Math.PI / 180) // y-axis is inverted in Crafty - ).normalize(); + Math.cos((41 * Math.PI) / 180), + -Math.sin((41 * Math.PI) / 180) // y-axis is inverted in Crafty + ).normalize(); var ANGLE_NEG_22_5 = new Crafty.math.Vector2D( - Math.cos(-22.5 * Math.PI / 180), - -Math.sin(-22.5 * Math.PI / 180) // y-axis is inverted in Crafty - ).normalize(); + Math.cos((-22.5 * Math.PI) / 180), + -Math.sin((-22.5 * Math.PI) / 180) // y-axis is inverted in Crafty + ).normalize(); var ANGLE_POS_112_5 = new Crafty.math.Vector2D( - Math.cos(112.5 * Math.PI / 180), - -Math.sin(112.5 * Math.PI / 180) // y-axis is inverted in Crafty - ).normalize(); - + Math.cos((112.5 * Math.PI) / 180), + -Math.sin((112.5 * Math.PI) / 180) // y-axis is inverted in Crafty + ).normalize(); ////////////////////// // UTILITY FUNCTIONS ////////////////////// - function createObj (cellX, cellY, cellWidth, cellHeight) { + function createObj(cellX, cellY, cellWidth, cellHeight) { cellX = cellX || 0; cellY = cellY || 0; cellWidth = cellWidth || 1; @@ -42,11 +40,11 @@ _x: cellX * cellsize + 1, _y: cellY * cellsize + 1, _w: cellWidth * cellsize - 2, - _h: cellHeight * cellsize - 2, + _h: cellHeight * cellsize - 2 }; } - function insertEntry (cellX, cellY, cellWidth, cellHeight) { + function insertEntry(cellX, cellY, cellWidth, cellHeight) { return Crafty.map.insert(createObj(cellX, cellY, cellWidth, cellHeight)); } @@ -64,27 +62,50 @@ Crafty.map.refresh(entry); } - function removeEntry (entry) { + function removeEntry(entry) { Crafty.map.remove(entry); } - function checkHashKeys (entry, cellX, cellY, cellWidth, cellHeight) { + function checkHashKeys(entry, cellX, cellY, cellWidth, cellHeight) { var keys = entry.keys; cellX = cellX || 0; cellY = cellY || 0; cellWidth = cellWidth || 1; cellHeight = cellHeight || 1; - QUnit.assert.strictEqual(keys.x1 + cellWidth - 1, keys.x2, "entity should occupy cellWidth cells in x-axis"); - QUnit.assert.strictEqual(keys.y1 + cellHeight - 1, keys.y2, "entity should occupy cellHeight cells in y-axis"); - QUnit.assert.strictEqual(keys.x1, cellX, "cell col start index should match"); - QUnit.assert.strictEqual(keys.y1, cellY, "cell row start index should match"); - QUnit.assert.strictEqual(keys.x2, cellX + cellWidth - 1, "cell col end index should match"); - QUnit.assert.strictEqual(keys.y2, cellY + cellHeight - 1, "cell row end index should match"); + QUnit.assert.strictEqual( + keys.x1 + cellWidth - 1, + keys.x2, + "entity should occupy cellWidth cells in x-axis" + ); + QUnit.assert.strictEqual( + keys.y1 + cellHeight - 1, + keys.y2, + "entity should occupy cellHeight cells in y-axis" + ); + QUnit.assert.strictEqual( + keys.x1, + cellX, + "cell col start index should match" + ); + QUnit.assert.strictEqual( + keys.y1, + cellY, + "cell row start index should match" + ); + QUnit.assert.strictEqual( + keys.x2, + cellX + cellWidth - 1, + "cell col end index should match" + ); + QUnit.assert.strictEqual( + keys.y2, + cellY + cellHeight - 1, + "cell row end index should match" + ); } - - function createEntity (cellX, cellY, cellWidth, cellHeight) { + function createEntity(cellX, cellY, cellWidth, cellHeight) { cellX = cellX || 0; cellY = cellY || 0; cellWidth = cellWidth || 1; @@ -94,7 +115,7 @@ x: cellX * cellsize + 1, y: cellY * cellsize + 1, w: cellWidth * cellsize - 2, - h: cellHeight * cellsize - 2, + h: cellHeight * cellsize - 2 }); return e; @@ -111,20 +132,31 @@ var newMap = new Crafty.HashMap(); _.notDeepEqual(newMap, Crafty.map, "Properties are different"); - newMap.__SOME_PROPERTY__ = '__SOME_PROPERTY__'; - _.strictEqual(newMap.__SOME_PROPERTY__, '__SOME_PROPERTY__', "Property set on one instance"); - _.notEqual(Crafty.map.__SOME_PROPERTY__, '__SOME_PROPERTY__', "Property not set on other instance"); + newMap.__SOME_PROPERTY__ = "__SOME_PROPERTY__"; + _.strictEqual( + newMap.__SOME_PROPERTY__, + "__SOME_PROPERTY__", + "Property set on one instance" + ); + _.notEqual( + Crafty.map.__SOME_PROPERTY__, + "__SOME_PROPERTY__", + "Property not set on other instance" + ); }); test("HashMap - cell size", function(_) { - var keys, csize = Crafty.HashMap.cellsize(); + var keys, + csize = Crafty.HashMap.cellsize(); _.strictEqual(csize, cellsize, "Check if we work with correct assumptions"); // returns correct hash keys of obj spanning entire single cell keys = Crafty.HashMap.key({ - _x: 0 * csize, _w: csize - 1, - _y: 0 * csize, _h: csize - 1 + _x: 0 * csize, + _w: csize - 1, + _y: 0 * csize, + _h: csize - 1 }); _.strictEqual(keys.x1, 0, "cell key should start at correct cell col"); _.strictEqual(keys.y1, 0, "cell key should start at correct cell row"); @@ -133,13 +165,23 @@ // returns correct hash keys of obj spanning multiple cells and overlaps a bit into adjacent cells keys = Crafty.HashMap.key({ - _x: -1 * csize, _w: 2 * csize, // spans 3 cell cols - _y: +3 * csize, _h: 4 * csize // spans 4 cell rows + _x: -1 * csize, + _w: 2 * csize, // spans 3 cell cols + _y: +3 * csize, + _h: 4 * csize // spans 4 cell rows }); _.strictEqual(keys.x1, -1, "cell key should start at correct cell col"); _.strictEqual(keys.y1, +3, "cell key should start at correct cell row"); - _.strictEqual(keys.x2, -1 + 2, "cell keys should match barely 3 cell widths"); - _.strictEqual(keys.y2, +3 + 4, "cell keys should match barely 4 cell heights"); + _.strictEqual( + keys.x2, + -1 + 2, + "cell keys should match barely 3 cell widths" + ); + _.strictEqual( + keys.y2, + +3 + 4, + "cell keys should match barely 4 cell heights" + ); }); test("HashMap - key", function(_) { @@ -148,21 +190,21 @@ // returns correct hash keys of obj var obj = createObj(0, 0, 1, 1); keys = Crafty.HashMap.key(obj); - checkHashKeys({keys: keys}, 0, 0, 1, 1); + checkHashKeys({ keys: keys }, 0, 0, 1, 1); // returns correct hash keys for prioritized obj._mbr obj._mbr = createObj(1, 1, 2, 2); keys = Crafty.HashMap.key(obj); - checkHashKeys({keys: keys}, 1, 1, 2, 2); + checkHashKeys({ keys: keys }, 1, 1, 2, 2); // returns correct hash keys for prioritized obj._cbr obj._cbr = createObj(2, 2, 3, 3); keys = Crafty.HashMap.key(obj); - checkHashKeys({keys: keys}, 2, 2, 3, 3); + checkHashKeys({ keys: keys }, 2, 2, 3, 3); // reuses keys parameter var keys2 = Crafty.HashMap.key(createObj(-10, 3, 5, 7), keys); - checkHashKeys({keys: keys2}, -10, 3, 5, 7); + checkHashKeys({ keys: keys2 }, -10, 3, 5, 7); _.strictEqual(keys2, keys, "same object"); _.deepEqual(keys2, keys, "same properties"); }); @@ -175,18 +217,24 @@ // no entity found in empty map found = Crafty.map.search({ - _x: -25 * cellsize, _w: 50 * cellsize, - _y: -25 * cellsize, _h: 50 * cellsize + _x: -25 * cellsize, + _w: 50 * cellsize, + _y: -25 * cellsize, + _h: 50 * cellsize }); _.strictEqual(found.length, 0, "no entities should have been found"); // entity found in map where created var e = createEntity(-11, -7, 3, 1); - found = Crafty.map.search({ - _x: -10.5 * cellsize, _w: 1, - _y: -6.5 * cellsize, _h: 1 - }).map(objToId); + found = Crafty.map + .search({ + _x: -10.5 * cellsize, + _w: 1, + _y: -6.5 * cellsize, + _h: 1 + }) + .map(objToId); _.strictEqual(found.length, 1, "1 entity should have been found"); _.ok(found.indexOf(e[0]) >= 0, "entity e found"); @@ -194,20 +242,28 @@ // entity found in map after moving e.x = 11 * cellsize; e.y = 7 * cellsize; - found = Crafty.map.search({ - _x: 11.5 * cellsize, _w: 1, - _y: 7.5 * cellsize, _h: 1 - }).map(objToId); + found = Crafty.map + .search({ + _x: 11.5 * cellsize, + _w: 1, + _y: 7.5 * cellsize, + _h: 1 + }) + .map(objToId); _.strictEqual(found.length, 1, "1 entity should have been found"); _.ok(found.indexOf(e[0]) >= 0, "entity e found"); // both entities found in map var f = createEntity(3, -5, 1, 1); - found = Crafty.map.search({ - _x: -25 * cellsize, _w: 50 * cellsize, - _y: -25 * cellsize, _h: 50 * cellsize - }).map(objToId); + found = Crafty.map + .search({ + _x: -25 * cellsize, + _w: 50 * cellsize, + _y: -25 * cellsize, + _h: 50 * cellsize + }) + .map(objToId); _.strictEqual(found.length, 2, "2 entities should have been found"); _.ok(found.indexOf(e[0]) >= 0, "entity e found"); @@ -217,33 +273,36 @@ e.destroy(); f.destroy(); found = Crafty.map.search({ - _x: -25 * cellsize, _w: 50 * cellsize, - _y: -25 * cellsize, _h: 50 * cellsize + _x: -25 * cellsize, + _w: 50 * cellsize, + _y: -25 * cellsize, + _h: 50 * cellsize }); _.strictEqual(found.length, 0, "no entities should have been found"); }); test("Spatial map integration test - search bounding rectangles", function(_) { - var found, - tx, ty; + var found, tx, ty; // is rectB within rectA? var contains = function(rectA, rectB) { - return rectB._x >= rectA._x && rectB._x + rectB._w <= rectA._x + rectA._w && - rectB._y >= rectA._y && rectB._y + rectB._h <= rectA._y + rectA._h; + return ( + rectB._x >= rectA._x && + rectB._x + rectB._w <= rectA._x + rectA._w && + rectB._y >= rectA._y && + rectB._y + rectB._h <= rectA._y + rectA._h + ); }; var objToId = function(obj) { return obj[0]; }; - // default entity - rectangle var e = createEntity(0, 0, 1, 1); _.strictEqual(e._mbr, null, "mbr doesn't exist"); _.strictEqual(e._cbr, null, "cbr doesn't exist"); - // test point inside hitbox & inside bounds tx = 0.5 * cellsize; ty = 0.5 * cellsize; @@ -251,10 +310,14 @@ _.strictEqual(e.contains(tx, ty, 0, 0), true, "test point inside bounds"); // search at test point finds entity - found = Crafty.map.search({ - _x: tx, _w: 1, - _y: ty, _h: 1 - }).map(objToId); + found = Crafty.map + .search({ + _x: tx, + _w: 1, + _y: ty, + _h: 1 + }) + .map(objToId); _.strictEqual(found.length, 1, "1 entity should have been found"); _.ok(found.indexOf(e[0]) >= 0, "entity e found"); @@ -265,31 +328,37 @@ _.strictEqual(e.contains(tx, ty, 0, 0), false, "test point outside bounds"); // search at test point does not find entity - found = Crafty.map.search({ - _x: tx, _w: 1, - _y: ty, _h: 1 - }).map(objToId); + found = Crafty.map + .search({ + _x: tx, + _w: 1, + _y: ty, + _h: 1 + }) + .map(objToId); _.strictEqual(found.length, 0, "no entity should have been found"); - // entity rotated - MBR - e.origin('center'); + e.origin("center"); e.rotation = 45; _.ok(!!e._mbr, "mbr exists"); _.strictEqual(e._cbr, null, "cbr doesn't exist"); - // test point outside hitbox & inside bounds (top-left corner of MBR) - tx = e._x + e._w/2 - e._w/2 * Math.sqrt(2); - ty = e._y + e._h/2 - e._h/2 * Math.sqrt(2); + tx = e._x + e._w / 2 - (e._w / 2) * Math.sqrt(2); + ty = e._y + e._h / 2 - (e._h / 2) * Math.sqrt(2); _.strictEqual(e.isAt(tx, ty), false, "test point outside hitbox"); _.strictEqual(e.contains(tx, ty, 0, 0), true, "test point inside bounds"); // search at test point finds entity - found = Crafty.map.search({ - _x: tx, _w: 1, - _y: ty, _h: 1 - }).map(objToId); + found = Crafty.map + .search({ + _x: tx, + _w: 1, + _y: ty, + _h: 1 + }) + .map(objToId); _.strictEqual(found.length, 1, "1 entity should have been found"); _.ok(found.indexOf(e[0]) >= 0, "entity e found"); @@ -300,50 +369,65 @@ _.strictEqual(e.contains(tx, ty, 0, 0), false, "test point outside bounds"); // search at test point does not find entity - found = Crafty.map.search({ - _x: tx, _w: 1, - _y: ty, _h: 1 - }).map(objToId); + found = Crafty.map + .search({ + _x: tx, + _w: 1, + _y: ty, + _h: 1 + }) + .map(objToId); _.strictEqual(found.length, 0, "no entity should have been found"); - // entity with hitbox inside its bounds - MBR e.rotation = 90; - e.collision([ // collision hitbox relative to 90° clockwise rotated entity - 0, 0, - cellsize/2, 0, - cellsize/2, cellsize/2, - 0, cellsize/2 + e.collision([ + // collision hitbox relative to 90° clockwise rotated entity + 0, + 0, + cellsize / 2, + 0, + cellsize / 2, + cellsize / 2, + 0, + cellsize / 2 ]); _.ok(!!e._mbr, "mbr exists"); _.strictEqual(e._cbr, null, "cbr doesn't exist"); - // test point inside hitbox & inside bounds - tx = 3*cellsize/4; - ty = cellsize/4; + tx = (3 * cellsize) / 4; + ty = cellsize / 4; _.strictEqual(e.isAt(tx, ty), true, "test point inside hitbox"); _.strictEqual(e.contains(tx, ty, 0, 0), true, "test point inside bounds"); // search at test point finds entity - found = Crafty.map.search({ - _x: tx, _w: 1, - _y: ty, _h: 1 - }).map(objToId); + found = Crafty.map + .search({ + _x: tx, + _w: 1, + _y: ty, + _h: 1 + }) + .map(objToId); _.strictEqual(found.length, 1, "1 entity should have been found"); _.ok(found.indexOf(e[0]) >= 0, "entity e found"); // test point outside hitbox & inside bounds - tx = cellsize/4; - ty = cellsize/4; + tx = cellsize / 4; + ty = cellsize / 4; _.strictEqual(e.isAt(tx, ty), false, "test point outside hitbox"); _.strictEqual(e.contains(tx, ty, 0, 0), true, "test point inside bounds"); // search at test point finds entity - found = Crafty.map.search({ - _x: tx, _w: 1, - _y: ty, _h: 1 - }).map(objToId); + found = Crafty.map + .search({ + _x: tx, + _w: 1, + _y: ty, + _h: 1 + }) + .map(objToId); _.strictEqual(found.length, 1, "1 entity should have been found"); _.ok(found.indexOf(e[0]) >= 0, "entity e found"); @@ -354,19 +438,27 @@ _.strictEqual(e.contains(tx, ty, 0, 0), false, "test point outside bounds"); // search at test point does not find entity - found = Crafty.map.search({ - _x: tx, _w: 1, - _y: ty, _h: 1 - }).map(objToId); + found = Crafty.map + .search({ + _x: tx, + _w: 1, + _y: ty, + _h: 1 + }) + .map(objToId); _.strictEqual(found.length, 0, "no entity should have been found"); - // entity with hitbox outside its bounds - CBR - e.collision([ // collision hitbox relative to 90° clockwise rotated entity - -10.5*cellsize, 10.5*cellsize, - -10.5*cellsize, 5.5*cellsize, - -5.5*cellsize, 5.5*cellsize, - -5.5*cellsize, 10.5*cellsize + e.collision([ + // collision hitbox relative to 90° clockwise rotated entity + -10.5 * cellsize, + 10.5 * cellsize, + -10.5 * cellsize, + 5.5 * cellsize, + -5.5 * cellsize, + 5.5 * cellsize, + -5.5 * cellsize, + 10.5 * cellsize ]); _.ok(!!e._mbr, "mbr exists"); _.ok(!!e._cbr, "cbr exists"); @@ -374,34 +466,49 @@ e.x++; e.x--; - // test point inside hitbox & inside bounds - tx = -7.5*cellsize; - ty = -7.5*cellsize; + tx = -7.5 * cellsize; + ty = -7.5 * cellsize; _.strictEqual(e.isAt(tx, ty), true, "test point inside hitbox"); _.strictEqual(e.contains(tx, ty, 0, 0), false, "test point outside MBR"); - _.strictEqual(contains(e._cbr, {_x: tx, _y: ty, _w: 0, _h: 0}), true, "test point inside CBR"); + _.strictEqual( + contains(e._cbr, { _x: tx, _y: ty, _w: 0, _h: 0 }), + true, + "test point inside CBR" + ); // search at test point finds entity - found = Crafty.map.search({ - _x: tx, _w: 1, - _y: ty, _h: 1 - }).map(objToId); + found = Crafty.map + .search({ + _x: tx, + _w: 1, + _y: ty, + _h: 1 + }) + .map(objToId); _.strictEqual(found.length, 1, "1 entity should have been found"); _.ok(found.indexOf(e[0]) >= 0, "entity e found"); // test point outside hitbox & inside bounds - tx = cellsize/2; - ty = cellsize/2; + tx = cellsize / 2; + ty = cellsize / 2; _.strictEqual(e.isAt(tx, ty), false, "test point outside hitbox"); _.strictEqual(e.contains(tx, ty, 0, 0), true, "test point inside MBR"); - _.strictEqual(contains(e._cbr, {_x: tx, _y: ty, _w: 0, _h: 0}), true, "test point inside CBR"); + _.strictEqual( + contains(e._cbr, { _x: tx, _y: ty, _w: 0, _h: 0 }), + true, + "test point inside CBR" + ); // search at test point finds entity - found = Crafty.map.search({ - _x: tx, _w: 1, - _y: ty, _h: 1 - }).map(objToId); + found = Crafty.map + .search({ + _x: tx, + _w: 1, + _y: ty, + _h: 1 + }) + .map(objToId); _.strictEqual(found.length, 1, "1 entity should have been found"); _.ok(found.indexOf(e[0]) >= 0, "entity e found"); @@ -410,28 +517,37 @@ ty = e.y + e.h + 1; _.strictEqual(e.isAt(tx, ty), false, "test point outside hitbox"); _.strictEqual(e.contains(tx, ty, 0, 0), false, "test point outside MBR"); - _.strictEqual(contains(e._cbr, {_x: tx, _y: ty, _w: 0, _h: 0}), false, "test point outside CBR"); + _.strictEqual( + contains(e._cbr, { _x: tx, _y: ty, _w: 0, _h: 0 }), + false, + "test point outside CBR" + ); // search at test point does not find entity - found = Crafty.map.search({ - _x: tx, _w: 1, - _y: ty, _h: 1 - }).map(objToId); + found = Crafty.map + .search({ + _x: tx, + _w: 1, + _y: ty, + _h: 1 + }) + .map(objToId); _.strictEqual(found.length, 0, "no entity should have been found"); - e.destroy(); }); test("Spatial map integration test - iterate bounding rectangles", function(_) { - var found, - keysT, keysB, keysH, - tx, ty; + var found, keysT, keysB, keysH, tx, ty; // are keysB within keysA? var containsKeys = function(keysA, keysB) { - return keysB.x1 >= keysA.x1 && keysB.x2 <= keysA.x2 && - keysB.y1 >= keysA.y1 && keysB.y2 <= keysA.y2; + return ( + keysB.x1 >= keysA.x1 && + keysB.x2 <= keysA.x2 && + keysB.y1 >= keysA.y1 && + keysB.y2 <= keysA.y2 + ); }; var hashKeys = function(x, y, w, h) { if (x instanceof Crafty.polygon) { @@ -441,7 +557,7 @@ w = x.points[4] - x.points[0]; y = x.points[1]; x = x.points[0]; - } else if (typeof x === 'object') { + } else if (typeof x === "object") { h = x._h; w = x._w; y = x._y; @@ -461,26 +577,36 @@ found[obj[0]] = true; }; - // default entity - rectangle var e = createEntity(0, 0, 10, 10); _.strictEqual(e._mbr, null, "mbr doesn't exist"); _.strictEqual(e._cbr, null, "cbr doesn't exist"); - // test point cell inside hitbox cell & inside bounds cell tx = 0.5 * cellsize; ty = 0.5 * cellsize; keysT = hashKeys(tx, ty, 0, 0); keysH = hashKeys(e.map); keysB = hashKeys(e._cbr || e._mbr || e); - _.strictEqual(containsKeys(keysH, keysT), true, "test point cell inside hitbox cell"); - _.strictEqual(containsKeys(keysB, keysT), true, "test point cell inside bounds cell"); + _.strictEqual( + containsKeys(keysH, keysT), + true, + "test point cell inside hitbox cell" + ); + _.strictEqual( + containsKeys(keysB, keysT), + true, + "test point cell inside bounds cell" + ); // iteration at test point finds entity found = {}; - Crafty.map.traverseRay({_x: tx, _y: ty}, NORTH_EAST, addObj); - _.strictEqual(Object.keys(found).length, 1, "1 entity should have been found"); + Crafty.map.traverseRay({ _x: tx, _y: ty }, NORTH_EAST, addObj); + _.strictEqual( + Object.keys(found).length, + 1, + "1 entity should have been found" + ); _.strictEqual(found[e[0]], true, "entity e found"); // test point cell outside hitbox cell & outside bounds cell @@ -489,35 +615,57 @@ keysT = hashKeys(tx, ty, 0, 0); keysH = hashKeys(e.map); keysB = hashKeys(e._cbr || e._mbr || e); - _.strictEqual(containsKeys(keysH, keysT), false, "test point cell outside hitbox cell"); - _.strictEqual(containsKeys(keysB, keysT), false, "test point cell outside bounds cell"); + _.strictEqual( + containsKeys(keysH, keysT), + false, + "test point cell outside hitbox cell" + ); + _.strictEqual( + containsKeys(keysB, keysT), + false, + "test point cell outside bounds cell" + ); // iteration at test point does not find entity found = {}; - Crafty.map.traverseRay({_x: tx, _y: ty}, NORTH_EAST, addObj); - _.strictEqual(Object.keys(found).length, 0, "no entity should have been found"); - + Crafty.map.traverseRay({ _x: tx, _y: ty }, NORTH_EAST, addObj); + _.strictEqual( + Object.keys(found).length, + 0, + "no entity should have been found" + ); // entity rotated - MBR - e.origin('center'); + e.origin("center"); e.rotation = 45; _.ok(!!e._mbr, "mbr exists"); _.strictEqual(e._cbr, null, "cbr doesn't exist"); - // test point cell outside hitbox cell & inside bounds cell (top-left corner of MBR) - tx = e._x + e._w/2 - e._w/2 * Math.sqrt(2); - ty = e._y + e._h/2 - e._h/2 * Math.sqrt(2); + tx = e._x + e._w / 2 - (e._w / 2) * Math.sqrt(2); + ty = e._y + e._h / 2 - (e._h / 2) * Math.sqrt(2); keysT = hashKeys(tx, ty, 0, 0); keysH = hashKeys(e.map); keysB = hashKeys(e._cbr || e._mbr || e); - _.strictEqual(containsKeys(keysH, keysT), false, "test point cell outside hitbox cell"); - _.strictEqual(containsKeys(keysB, keysT), true, "test point cell inside bounds cell"); + _.strictEqual( + containsKeys(keysH, keysT), + false, + "test point cell outside hitbox cell" + ); + _.strictEqual( + containsKeys(keysB, keysT), + true, + "test point cell inside bounds cell" + ); // iteration at test point finds entity found = {}; - Crafty.map.traverseRay({_x: tx, _y: ty}, SOUTH_WEST, addObj); - _.strictEqual(Object.keys(found).length, 1, "1 entity should have been found"); + Crafty.map.traverseRay({ _x: tx, _y: ty }, SOUTH_WEST, addObj); + _.strictEqual( + Object.keys(found).length, + 1, + "1 entity should have been found" + ); _.strictEqual(found[e[0]], true, "entity e found"); // test point cell outside hitbox cell & outside bounds cell (a bit beyond top-left corner of MBR) @@ -526,55 +674,93 @@ keysT = hashKeys(tx, ty, 0, 0); keysH = hashKeys(e.map); keysB = hashKeys(e._cbr || e._mbr || e); - _.strictEqual(containsKeys(keysH, keysT), false, "test point cell outside hitbox cell"); - _.strictEqual(containsKeys(keysB, keysT), false, "test point cell inside bounds cell"); + _.strictEqual( + containsKeys(keysH, keysT), + false, + "test point cell outside hitbox cell" + ); + _.strictEqual( + containsKeys(keysB, keysT), + false, + "test point cell inside bounds cell" + ); // iteration at test point does not find entity found = {}; - Crafty.map.traverseRay({_x: tx, _y: ty}, NORTH_WEST, addObj); - _.strictEqual(Object.keys(found).length, 0, "no entity should have been found"); - + Crafty.map.traverseRay({ _x: tx, _y: ty }, NORTH_WEST, addObj); + _.strictEqual( + Object.keys(found).length, + 0, + "no entity should have been found" + ); // entity with hitbox inside its bounds - MBR e.rotation = 0; e.collision([ - 0, 0, - cellsize/2, 0, - cellsize/2, cellsize/2, - 0, cellsize/2 + 0, + 0, + cellsize / 2, + 0, + cellsize / 2, + cellsize / 2, + 0, + cellsize / 2 ]); _.ok(!!e._mbr, "mbr exists"); _.strictEqual(e._cbr, null, "cbr doesn't exist"); - // test point cell inside hitbox cell & inside bounds cell - tx = 3*cellsize/4; - ty = cellsize/4; + tx = (3 * cellsize) / 4; + ty = cellsize / 4; keysT = hashKeys(tx, ty, 0, 0); keysH = hashKeys(e.map); keysB = hashKeys(e._cbr || e._mbr || e); - _.strictEqual(containsKeys(keysH, keysT), true, "test point cell inside hitbox cell"); - _.strictEqual(containsKeys(keysB, keysT), true, "test point cell inside bounds cell"); + _.strictEqual( + containsKeys(keysH, keysT), + true, + "test point cell inside hitbox cell" + ); + _.strictEqual( + containsKeys(keysB, keysT), + true, + "test point cell inside bounds cell" + ); // iteration at test point finds entity found = {}; - Crafty.map.traverseRay({_x: tx, _y: ty}, SOUTH_EAST, addObj); - _.strictEqual(Object.keys(found).length, 1, "1 entity should have been found"); + Crafty.map.traverseRay({ _x: tx, _y: ty }, SOUTH_EAST, addObj); + _.strictEqual( + Object.keys(found).length, + 1, + "1 entity should have been found" + ); _.strictEqual(found[e[0]], true, "entity e found"); // test point cell outside hitbox cell & inside bounds cell - tx = 9.5*cellsize; - ty = 9.5*cellsize; + tx = 9.5 * cellsize; + ty = 9.5 * cellsize; keysT = hashKeys(tx, ty, 0, 0); keysH = hashKeys(e.map); keysB = hashKeys(e._cbr || e._mbr || e); - _.strictEqual(containsKeys(keysH, keysT), false, "test point cell outside hitbox cell"); - _.strictEqual(containsKeys(keysB, keysT), true, "test point cell inside bounds cell"); + _.strictEqual( + containsKeys(keysH, keysT), + false, + "test point cell outside hitbox cell" + ); + _.strictEqual( + containsKeys(keysB, keysT), + true, + "test point cell inside bounds cell" + ); // iteration at test point finds entity found = {}; - Crafty.map.traverseRay({_x: tx, _y: ty}, WEST, addObj); - _.strictEqual(Object.keys(found).length, 1, "1 entity should have been found"); + Crafty.map.traverseRay({ _x: tx, _y: ty }, WEST, addObj); + _.strictEqual( + Object.keys(found).length, + 1, + "1 entity should have been found" + ); _.strictEqual(found[e[0]], true, "entity e found"); // test point cell outside hitbox cell & outside bounds cell @@ -583,21 +769,36 @@ keysT = hashKeys(tx, ty, 0, 0); keysH = hashKeys(e.map); keysB = hashKeys(e._cbr || e._mbr || e); - _.strictEqual(containsKeys(keysH, keysT), false, "test point cell outside hitbox cell"); - _.strictEqual(containsKeys(keysB, keysT), false, "test point cell inside bounds cell"); + _.strictEqual( + containsKeys(keysH, keysT), + false, + "test point cell outside hitbox cell" + ); + _.strictEqual( + containsKeys(keysB, keysT), + false, + "test point cell inside bounds cell" + ); // iteration at test point does not find entity found = {}; - Crafty.map.traverseRay({_x: tx, _y: ty}, NORTH_EAST, addObj); - _.strictEqual(Object.keys(found).length, 0, "no entity should have been found"); - + Crafty.map.traverseRay({ _x: tx, _y: ty }, NORTH_EAST, addObj); + _.strictEqual( + Object.keys(found).length, + 0, + "no entity should have been found" + ); // entity with hitbox outside its bounds - CBR e.collision([ - -10.5*cellsize, -10.5*cellsize, - -5.5*cellsize, -10.5*cellsize, - -5.5*cellsize, -5.5*cellsize, - -10.5*cellsize, -5.5*cellsize + -10.5 * cellsize, + -10.5 * cellsize, + -5.5 * cellsize, + -10.5 * cellsize, + -5.5 * cellsize, + -5.5 * cellsize, + -10.5 * cellsize, + -5.5 * cellsize ]); _.ok(!!e._mbr, "mbr exists"); _.ok(!!e._cbr, "cbr exists"); @@ -605,35 +806,58 @@ e.x++; e.x--; - // test point cell inside hitbox cell & inside bounds cell - tx = -7.5*cellsize; - ty = -7.5*cellsize; + tx = -7.5 * cellsize; + ty = -7.5 * cellsize; keysT = hashKeys(tx, ty, 0, 0); keysH = hashKeys(e.map); keysB = hashKeys(e._cbr || e._mbr || e); - _.strictEqual(containsKeys(keysH, keysT), true, "test point cell inside hitbox cell"); - _.strictEqual(containsKeys(keysB, keysT), true, "test point cell inside bounds cell"); + _.strictEqual( + containsKeys(keysH, keysT), + true, + "test point cell inside hitbox cell" + ); + _.strictEqual( + containsKeys(keysB, keysT), + true, + "test point cell inside bounds cell" + ); // iteration at test point finds entity found = {}; - Crafty.map.traverseRay({_x: tx, _y: ty}, EAST, addObj); - _.strictEqual(Object.keys(found).length, 1, "1 entity should have been found"); + Crafty.map.traverseRay({ _x: tx, _y: ty }, EAST, addObj); + _.strictEqual( + Object.keys(found).length, + 1, + "1 entity should have been found" + ); _.strictEqual(found[e[0]], true, "entity e found"); // test point cell outside hitbox cell & inside bounds cell - tx = cellsize/2; - ty = cellsize/2; + tx = cellsize / 2; + ty = cellsize / 2; keysT = hashKeys(tx, ty, 0, 0); keysH = hashKeys(e.map); keysB = hashKeys(e._cbr || e._mbr || e); - _.strictEqual(containsKeys(keysH, keysT), false, "test point cell outside hitbox cell"); - _.strictEqual(containsKeys(keysB, keysT), true, "test point cell inside bounds cell"); + _.strictEqual( + containsKeys(keysH, keysT), + false, + "test point cell outside hitbox cell" + ); + _.strictEqual( + containsKeys(keysB, keysT), + true, + "test point cell inside bounds cell" + ); // iteration at test point finds entity found = {}; - Crafty.map.traverseRay({_x: tx, _y: ty}, NORTH, addObj); - _.strictEqual(Object.keys(found).length, 1, "1 entity should have been found"); + Crafty.map.traverseRay({ _x: tx, _y: ty }, NORTH, addObj); + _.strictEqual( + Object.keys(found).length, + 1, + "1 entity should have been found" + ); _.strictEqual(found[e[0]], true, "entity e found"); // test point cell outside hitbox cell & outside bounds cell @@ -642,14 +866,25 @@ keysT = hashKeys(tx, ty, 0, 0); keysH = hashKeys(e.map); keysB = hashKeys(e._cbr || e._mbr || e); - _.strictEqual(containsKeys(keysH, keysT), false, "test point cell outside hitbox cell"); - _.strictEqual(containsKeys(keysB, keysT), false, "test point cell inside bounds cell"); + _.strictEqual( + containsKeys(keysH, keysT), + false, + "test point cell outside hitbox cell" + ); + _.strictEqual( + containsKeys(keysB, keysT), + false, + "test point cell inside bounds cell" + ); // iteration at test point does not find entity found = {}; - Crafty.map.traverseRay({_x: tx, _y: ty}, SOUTH, addObj); - _.strictEqual(Object.keys(found).length, 0, "no entity should have been found"); - + Crafty.map.traverseRay({ _x: tx, _y: ty }, SOUTH, addObj); + _.strictEqual( + Object.keys(found).length, + 0, + "no entity should have been found" + ); e.destroy(); }); @@ -667,10 +902,26 @@ // bounds = bottom left entity var e = createEntity(-5, 3, 1, 1); bounds = Crafty.map.boundaries(); - _.strictEqual(bounds.min.x, e._x, "min bound matches entity's top left corner"); - _.strictEqual(bounds.min.y, e._y, "min bound matches entity's top left corner"); - _.strictEqual(bounds.max.x, e._x + e._w, "min bound matches entity's bottom right corner"); - _.strictEqual(bounds.max.y, e._y + e._h, "min bound matches entity's bottom right corner"); + _.strictEqual( + bounds.min.x, + e._x, + "min bound matches entity's top left corner" + ); + _.strictEqual( + bounds.min.y, + e._y, + "min bound matches entity's top left corner" + ); + _.strictEqual( + bounds.max.x, + e._x + e._w, + "min bound matches entity's bottom right corner" + ); + _.strictEqual( + bounds.max.y, + e._y + e._h, + "min bound matches entity's bottom right corner" + ); // bounds = bottom left entity + top right entity var f = createEntity(3, -5, 1, 1); @@ -692,10 +943,26 @@ e.destroy(); f.destroy(); bounds = Crafty.map.boundaries(); - _.strictEqual(bounds.min.x, g._x, "min bound matches entity's top left corner"); - _.strictEqual(bounds.min.y, g._y, "min bound matches entity's top left corner"); - _.strictEqual(bounds.max.x, g._x + g._w, "min bound matches entity's bottom right corner"); - _.strictEqual(bounds.max.y, g._y + g._h, "min bound matches entity's bottom right corner"); + _.strictEqual( + bounds.min.x, + g._x, + "min bound matches entity's top left corner" + ); + _.strictEqual( + bounds.min.y, + g._y, + "min bound matches entity's top left corner" + ); + _.strictEqual( + bounds.max.x, + g._x + g._w, + "min bound matches entity's bottom right corner" + ); + _.strictEqual( + bounds.max.y, + g._y + g._h, + "min bound matches entity's bottom right corner" + ); // infinite bounds w/o entities g.destroy(); @@ -748,31 +1015,39 @@ var g = createEntity(1, 0, 3, 3); var h = createEntity(0, 0, 4, 4); var cellEntities = [ - [h[0]], // (0,3) - [h[0]], // (1,3) - [h[0], g[0]], // (1,2) + [h[0]], // (0,3) + [h[0]], // (1,3) + [h[0], g[0]], // (1,2) [h[0], g[0], f[0]], // (2,2) [h[0], g[0], f[0]], // (2,1) - [h[0], g[0]], // (3,1) - [h[0], g[0], e[0]] // (3,0) + [h[0], g[0]], // (3,1) + [h[0], g[0], e[0]] // (3,0) ]; var oldCellDistance = -Infinity, - cellNo = 0; - - Crafty.map.traverseRay({_x: 0 + 1, _y: 4 * cellsize - 1}, ANGLE_POS_41, function(obj, previousCellDistance) { - if (previousCellDistance !== oldCellDistance) { - cellNo++; - oldCellDistance = previousCellDistance; + cellNo = 0; + + Crafty.map.traverseRay( + { _x: 0 + 1, _y: 4 * cellsize - 1 }, + ANGLE_POS_41, + function(obj, previousCellDistance) { + if (previousCellDistance !== oldCellDistance) { + cellNo++; + oldCellDistance = previousCellDistance; + } + + var idx = cellEntities[cellNo].indexOf(obj[0]); + _.ok(idx >= 0, "expected entity inside cell found"); + cellEntities[cellNo].splice(idx, 1); } - - var idx = cellEntities[cellNo].indexOf(obj[0]); - _.ok(idx >= 0, "expected entity inside cell found"); - cellEntities[cellNo].splice(idx, 1); - }); + ); for (var i = 0; i < cellEntities.length; ++i) { - _.strictEqual(cellEntities[i].length, 0, "all entities inside cell have been found"); + _.strictEqual( + cellEntities[i].length, + 0, + "all entities inside cell have been found" + ); } e.destroy(); @@ -783,15 +1058,18 @@ test("Spatial map - iteration - can be cancelled", function(_) { var e = createEntity(-4, -2, 10, 5); // entity that spans multiple cells - var origin = {_x: -4.25*cellsize, _y: -2.25*cellsize - 10}; + var origin = { _x: -4.25 * cellsize, _y: -2.25 * cellsize - 10 }; var direction = ANGLE_NEG_22_5; var objFound = false, - objCount = 0, - cellCount = 0; + objCount = 0, + cellCount = 0; var oldCellDistance = -Infinity; - Crafty.map.traverseRay(origin, direction, function(obj, previousCellDistance) { + Crafty.map.traverseRay(origin, direction, function( + obj, + previousCellDistance + ) { if (previousCellDistance !== oldCellDistance) { cellCount++; oldCellDistance = previousCellDistance; @@ -817,27 +1095,46 @@ oldCellDistance = -Infinity; iteratedOnce = false; - Crafty.map.traverseRay({_x: 10*cellsize, _y: 10*cellsize}, NORTH_WEST, function(obj, previousCellDistance) { - _.ok(previousCellDistance >= oldCellDistance, - "distance is monotonically non-decreasing while advancing cells diagonally"); - iteratedOnce = true; - oldCellDistance = previousCellDistance; - }); - _.strictEqual(iteratedOnce, true, "iteration iterated over at least one object"); + Crafty.map.traverseRay( + { _x: 10 * cellsize, _y: 10 * cellsize }, + NORTH_WEST, + function(obj, previousCellDistance) { + _.ok( + previousCellDistance >= oldCellDistance, + "distance is monotonically non-decreasing while advancing cells diagonally" + ); + iteratedOnce = true; + oldCellDistance = previousCellDistance; + } + ); + _.strictEqual( + iteratedOnce, + true, + "iteration iterated over at least one object" + ); oldCellDistance = -Infinity; iteratedOnce = false; - Crafty.map.traverseRay({_x: -3.5*cellsize, _y: -1.1*cellsize}, ANGLE_POS_112_5, function(obj, previousCellDistance) { - if (previousCellDistance !== -Infinity) { - _.ok(previousCellDistance > oldCellDistance, - "distance is strictly monotonically increasing while advancing cells non-diagonally"); - iteratedOnce = true; + Crafty.map.traverseRay( + { _x: -3.5 * cellsize, _y: -1.1 * cellsize }, + ANGLE_POS_112_5, + function(obj, previousCellDistance) { + if (previousCellDistance !== -Infinity) { + _.ok( + previousCellDistance > oldCellDistance, + "distance is strictly monotonically increasing while advancing cells non-diagonally" + ); + iteratedOnce = true; + } + oldCellDistance = previousCellDistance; } - oldCellDistance = previousCellDistance; - }); - _.strictEqual(iteratedOnce, true, "iteration iterated over at least one object"); + ); + _.strictEqual( + iteratedOnce, + true, + "iteration iterated over at least one object" + ); removeEntry(e); }); - -})(); \ No newline at end of file +})(); diff --git a/tests/webdriver/color/color-canvas.js b/tests/webdriver/color/color-canvas.js index 571aaf68..624b293e 100644 --- a/tests/webdriver/color/color-canvas.js +++ b/tests/webdriver/color/color-canvas.js @@ -1,2 +1,2 @@ QUnit.module(module); -require('./color-common.js')(QUnit, browser, 'Canvas'); +require("./color-common.js")(QUnit, browser, "Canvas"); diff --git a/tests/webdriver/color/color-common.js b/tests/webdriver/color/color-common.js index 398dc9e0..8a29fba0 100644 --- a/tests/webdriver/color/color-common.js +++ b/tests/webdriver/color/color-common.js @@ -1,40 +1,47 @@ function generateScript(renderMethod) { var testScript = function() { - var box = Crafty.e('2D, $renderMethod') - .attr({ x: 0, y: 0, w: 320, h: 240 }); + var box = Crafty.e("2D, $renderMethod").attr({ + x: 0, + y: 0, + w: 320, + h: 240 + }); function initial() { - box.addComponent('Color'); - signalBarrier('initial'); + box.addComponent("Color"); + signalBarrier("initial"); } function opaque() { - box.color('rgb(0, 255, 0)'); - signalBarrier('opaque'); + box.color("rgb(0, 255, 0)"); + signalBarrier("opaque"); } function transparent() { - box.color('rgba(0, 0, 255, 0.5)'); - signalBarrier('transparent'); + box.color("rgba(0, 0, 255, 0.5)"); + signalBarrier("transparent"); } - waitBarrier('initial', initial); - waitBarrier('opaque', opaque); - waitBarrier('transparent', transparent); + waitBarrier("initial", initial); + waitBarrier("opaque", opaque); + waitBarrier("transparent", transparent); }; - return testScript.toString().replace('$renderMethod', renderMethod); + return testScript.toString().replace("$renderMethod", renderMethod); } - module.exports = function(QUnit, browser, renderMethod) { - QUnit.test("Color - " + renderMethod, function(assert) { return browser .testUrl(generateScript(renderMethod)) - .signalBarrier('initial').waitBarrier('initial').assertResemble('color-initial') - .signalBarrier('opaque').waitBarrier('opaque').assertResemble('color-opaque') - .signalBarrier('transparent').waitBarrier('transparent').assertResemble('color-transparent'); + .signalBarrier("initial") + .waitBarrier("initial") + .assertResemble("color-initial") + .signalBarrier("opaque") + .waitBarrier("opaque") + .assertResemble("color-opaque") + .signalBarrier("transparent") + .waitBarrier("transparent") + .assertResemble("color-transparent"); }); - }; diff --git a/tests/webdriver/color/color-dom.js b/tests/webdriver/color/color-dom.js index a8df1c17..b8e3e91a 100644 --- a/tests/webdriver/color/color-dom.js +++ b/tests/webdriver/color/color-dom.js @@ -1,2 +1,2 @@ QUnit.module(module); -require('./color-common.js')(QUnit, browser, 'DOM'); +require("./color-common.js")(QUnit, browser, "DOM"); diff --git a/tests/webdriver/color/color-webgl.js b/tests/webdriver/color/color-webgl.js index a4645c5e..fafbc2e9 100644 --- a/tests/webdriver/color/color-webgl.js +++ b/tests/webdriver/color/color-webgl.js @@ -1,2 +1,2 @@ QUnit.module(module); -require('./color-common.js')(QUnit, browser, 'WebGL'); +require("./color-common.js")(QUnit, browser, "WebGL"); diff --git a/tests/webdriver/commands/browser.js b/tests/webdriver/commands/browser.js index 3975481b..35afbd71 100644 --- a/tests/webdriver/commands/browser.js +++ b/tests/webdriver/commands/browser.js @@ -1,109 +1,197 @@ -var q = require('q'), - jimp = require('jimp'); - +var q = require("q"), + jimp = require("jimp"); // CRAFTY STAGE ELEMENT -var viewportStage = 'cr-stage'; +var viewportStage = "cr-stage"; // CURRENT POINTER STATE var pointer = { x: 0, y: 0, isDown: false }; -module.exports = function addBrowserSpecificCommands(client, capabilities, runId, syntheticKeyEvents, syntheticMouseEvents, rotatedCrop) { - +module.exports = function addBrowserSpecificCommands( + client, + capabilities, + runId, + syntheticKeyEvents, + syntheticMouseEvents, + rotatedCrop +) { // WEBDRIVER COMMAND: NORMALIZED SCREENSHOT - ROTATE CCW 90°, CROP TO DOCUMENT REGION, SCALE UP, CROP TO BOUNDS if (rotatedCrop) { - client.addCommand("saveNormalizedScreenshot", function(filePath, bounds) { - return this.saveScreenshot().then(function(screenshotBuffer, response) { - return jimp.read(screenshotBuffer).then(function(screenshot) { - var deferred = q.defer(); - var x = bounds.x > 0 ? Math.min(bounds.x, rotatedCrop.stretchW - 1) : 0, - y = bounds.y > 0 ? Math.min(bounds.y, rotatedCrop.stretchH - 1) : 0, - w = bounds.w > 0 ? Math.min(bounds.w, rotatedCrop.stretchW - x) : rotatedCrop.stretchW - x, - h = bounds.h > 0 ? Math.min(bounds.h, rotatedCrop.stretchH - y) : rotatedCrop.stretchH - y; + client.addCommand( + "saveNormalizedScreenshot", + function(filePath, bounds) { + return this.saveScreenshot().then(function( + screenshotBuffer, + response + ) { + return jimp + .read(screenshotBuffer) + .then(function(screenshot) { + var deferred = q.defer(); + var x = + bounds.x > 0 + ? Math.min( + bounds.x, + rotatedCrop.stretchW - 1 + ) + : 0, + y = + bounds.y > 0 + ? Math.min( + bounds.y, + rotatedCrop.stretchH - 1 + ) + : 0, + w = + bounds.w > 0 + ? Math.min( + bounds.w, + rotatedCrop.stretchW - x + ) + : rotatedCrop.stretchW - x, + h = + bounds.h > 0 + ? Math.min( + bounds.h, + rotatedCrop.stretchH - y + ) + : rotatedCrop.stretchH - y; - screenshot - .rotate(270) - .crop(rotatedCrop.x, rotatedCrop.y, rotatedCrop.w, rotatedCrop.h) - .cover(rotatedCrop.stretchW, rotatedCrop.stretchH) - .crop(x, y, w, h) - .write(filePath, deferred.makeNodeResolver()); + screenshot + .rotate(270) + .crop( + rotatedCrop.x, + rotatedCrop.y, + rotatedCrop.w, + rotatedCrop.h + ) + .cover( + rotatedCrop.stretchW, + rotatedCrop.stretchH + ) + .crop(x, y, w, h) + .write(filePath, deferred.makeNodeResolver()); - return deferred.promise; + return deferred.promise; + }); }); - }); - }, true); + }, + true + ); } // WEBDRIVER COMMAND: NORMALIZED KEYPRESS - trigger synthethic event if (syntheticKeyEvents) { - client.addCommand("keyDown", function(key) { - key = key.toUpperCase(); - return this.execute(function(key) { - var evt = document.createEvent('Event'); - evt.initEvent('keydown', true, true); - evt.key = key; - evt.char = key; - evt.keyCode = key.charCodeAt(0); - evt.which = key.charCodeAt(0); - evt.charCode = key.charCodeAt(0); - document.dispatchEvent(evt); - }, key); - }, true); + client.addCommand( + "keyDown", + function(key) { + key = key.toUpperCase(); + return this.execute(function(key) { + var evt = document.createEvent("Event"); + evt.initEvent("keydown", true, true); + evt.key = key; + evt.char = key; + evt.keyCode = key.charCodeAt(0); + evt.which = key.charCodeAt(0); + evt.charCode = key.charCodeAt(0); + document.dispatchEvent(evt); + }, key); + }, + true + ); - client.addCommand("keyUp", function(key) { - key = key.toUpperCase(); - return this.execute(function(key) { - var evt = document.createEvent('Event'); - evt.initEvent('keyup', true, true); - evt.key = key; - evt.char = key; - evt.keyCode = key.charCodeAt(0); - evt.which = key.charCodeAt(0); - evt.charCode = key.charCodeAt(0); - document.dispatchEvent(evt); - }, key); - }, true); + client.addCommand( + "keyUp", + function(key) { + key = key.toUpperCase(); + return this.execute(function(key) { + var evt = document.createEvent("Event"); + evt.initEvent("keyup", true, true); + evt.key = key; + evt.char = key; + evt.keyCode = key.charCodeAt(0); + evt.which = key.charCodeAt(0); + evt.charCode = key.charCodeAt(0); + document.dispatchEvent(evt); + }, key); + }, + true + ); } // WEBDRIVER COMMAND: NORMALIZED POINTER - trigger synthetic event if (syntheticMouseEvents) { - client.addCommand("pointerDown", function() { - pointer.isDown = true; - return this.execute(function(x, y, viewportStage) { - var evt = document.createEvent('Event'); - evt.initEvent('mousedown', true, true); + client.addCommand( + "pointerDown", + function() { + pointer.isDown = true; + return this.execute( + function(x, y, viewportStage) { + var evt = document.createEvent("Event"); + evt.initEvent("mousedown", true, true); evt.button = 0; evt.which = 1; evt.clientX = x; evt.clientY = y; - document.getElementById(viewportStage).dispatchEvent(evt); - }, pointer.x, pointer.y, viewportStage); - }, true); + document + .getElementById(viewportStage) + .dispatchEvent(evt); + }, + pointer.x, + pointer.y, + viewportStage + ); + }, + true + ); - client.addCommand("pointerMove", function(x, y) { - pointer.x = x; - pointer.y = y; - return this.execute(function(x, y, isDown, viewportStage) { - var evt = document.createEvent('Event'); - evt.initEvent('mousemove', true, true); + client.addCommand( + "pointerMove", + function(x, y) { + pointer.x = x; + pointer.y = y; + return this.execute( + function(x, y, isDown, viewportStage) { + var evt = document.createEvent("Event"); + evt.initEvent("mousemove", true, true); evt.button = isDown ? 0 : -1; evt.which = isDown ? 1 : 0; evt.clientX = x; evt.clientY = y; - document.getElementById(viewportStage).dispatchEvent(evt); - }, x, y, pointer.isDown, viewportStage); - }, true); + document + .getElementById(viewportStage) + .dispatchEvent(evt); + }, + x, + y, + pointer.isDown, + viewportStage + ); + }, + true + ); - client.addCommand("pointerUp", function() { - pointer.isDown = false; - return this.execute(function(x, y, viewportStage) { - var evt = document.createEvent('Event'); - evt.initEvent('mouseup', true, true); + client.addCommand( + "pointerUp", + function() { + pointer.isDown = false; + return this.execute( + function(x, y, viewportStage) { + var evt = document.createEvent("Event"); + evt.initEvent("mouseup", true, true); evt.button = 0; evt.which = 1; evt.clientX = x; evt.clientY = y; - document.getElementById(viewportStage).dispatchEvent(evt); - }, pointer.x, pointer.y, viewportStage); - }, true); + document + .getElementById(viewportStage) + .dispatchEvent(evt); + }, + pointer.x, + pointer.y, + viewportStage + ); + }, + true + ); } }; diff --git a/tests/webdriver/commands/generic.js b/tests/webdriver/commands/generic.js index 018ceed4..3a4308bc 100644 --- a/tests/webdriver/commands/generic.js +++ b/tests/webdriver/commands/generic.js @@ -1,14 +1,13 @@ -var fs = require('fs'), - q = require('q'), - qfs = require('q-io/fs'), - jimp = require('jimp'), - resemble = require('node-resemble-js'); +var fs = require("fs"), + q = require("q"), + qfs = require("q-io/fs"), + jimp = require("jimp"), + resemble = require("node-resemble-js"); // CURRENT POINTER STATE var pointer = { x: 0, y: 0, isDown: false }; module.exports = function addGenericCommands(client, capabilities, runId) { - // WEBDRIVER COMMAND: NORMALIZED SCREENSHOT - ignore platform specific offsets client.addCommand("saveNormalizedScreenshot", function(filePath, bounds) { return this.saveCroppedScreenshot(filePath, bounds); @@ -16,71 +15,99 @@ module.exports = function addGenericCommands(client, capabilities, runId) { // WEBDRIVER COMMAND: CROPPED SCREENSHOT client.addCommand("saveCroppedScreenshot", function(filePath, bounds) { - if (arguments.length === 1) - return this.saveScreenshot(filePath); + if (arguments.length === 1) return this.saveScreenshot(filePath); return this.saveScreenshot().then(function(screenshotBuffer, response) { return jimp.read(screenshotBuffer).then(function(screenshot) { var deferred = q.defer(); - var x = bounds.x > 0 ? Math.min(bounds.x, screenshot.bitmap.width - 1) : 0, - y = bounds.y > 0 ? Math.min(bounds.y, screenshot.bitmap.height - 1) : 0, - w = bounds.w > 0 ? Math.min(bounds.w, screenshot.bitmap.width - x) : screenshot.bitmap.width - x, - h = bounds.h > 0 ? Math.min(bounds.h, screenshot.bitmap.height - y) : screenshot.bitmap.height - y; + var x = + bounds.x > 0 + ? Math.min(bounds.x, screenshot.bitmap.width - 1) + : 0, + y = + bounds.y > 0 + ? Math.min(bounds.y, screenshot.bitmap.height - 1) + : 0, + w = + bounds.w > 0 + ? Math.min(bounds.w, screenshot.bitmap.width - x) + : screenshot.bitmap.width - x, + h = + bounds.h > 0 + ? Math.min(bounds.h, screenshot.bitmap.height - y) + : screenshot.bitmap.height - y; - screenshot.crop(x, y, w, h).write(filePath, deferred.makeNodeResolver()); + screenshot + .crop(x, y, w, h) + .write(filePath, deferred.makeNodeResolver()); return deferred.promise; }); }); }); // WEBDRIVER COMMAND: IMAGE COMPARE - client.addCommand("resemble", function(actualPath, expectedPath, diffPath, bounds, checkAntialiasing) { + client.addCommand("resemble", function( + actualPath, + expectedPath, + diffPath, + bounds, + checkAntialiasing + ) { var self = this; - return qfs.exists(expectedPath) - .then(function(exists) { - if (!exists) // baseline screenshot to compare against doesn't exist; save it - return self.saveNormalizedScreenshot(expectedPath, bounds); - else // baseline screenshot to compare against exists; do comparison - return self.saveNormalizedScreenshot(actualPath, bounds).then(function() { - var deferred = q.defer(); - resemble(actualPath) - .compareTo(expectedPath) - [checkAntialiasing ? 'ignoreNothing' : 'ignoreAntialiasing']() - .onComplete(function(data) { - data.getDiffImage() - .pack() - .pipe(fs.createWriteStream(diffPath)) - .on('error', deferred.reject) - .on('close', deferred.reject) - .on('finish', function() { - deferred.resolve(data); - }); - }); - return deferred.promise; - }); - }); + return qfs.exists(expectedPath).then(function(exists) { + if ( + !exists // baseline screenshot to compare against doesn't exist; save it + ) + return self.saveNormalizedScreenshot(expectedPath, bounds); + // baseline screenshot to compare against exists; do comparison + else + return self + .saveNormalizedScreenshot(actualPath, bounds) + .then(function() { + var deferred = q.defer(); + resemble(actualPath) + .compareTo(expectedPath) + [ + checkAntialiasing + ? "ignoreNothing" + : "ignoreAntialiasing" + ]() + .onComplete(function(data) { + data.getDiffImage() + .pack() + .pipe(fs.createWriteStream(diffPath)) + .on("error", deferred.reject) + .on("close", deferred.reject) + .on("finish", function() { + deferred.resolve(data); + }); + }); + return deferred.promise; + }); + }); }); - - // WEBDRIVER COMMAND: NORMALIZED POINTER client.addCommand("pointerDown", function() { pointer.isDown = true; - return this.isMobile ? this.touchDown(pointer.x, pointer.y) : this.buttonDown(); + return this.isMobile + ? this.touchDown(pointer.x, pointer.y) + : this.buttonDown(); }); client.addCommand("pointerMove", function(x, y) { pointer.x = x; pointer.y = y; if (this.isMobile) { - if (pointer.isDown) - return this.touchMove(x,y); + if (pointer.isDown) return this.touchMove(x, y); } else { - return this.moveToObject(':root', x, y); + return this.moveToObject(":root", x, y); } }); - client.addCommand("pointerUp", function() { + client.addCommand("pointerUp", function() { pointer.isDown = false; - return this.isMobile ? this.touchUp(pointer.x, pointer.y) : this.buttonUp(); + return this.isMobile + ? this.touchUp(pointer.x, pointer.y) + : this.buttonUp(); }); // WEBDRIVER COMMAND: NORMALIZED KEYPRESS diff --git a/tests/webdriver/commands/test.js b/tests/webdriver/commands/test.js index eada56d4..6d3782a8 100644 --- a/tests/webdriver/commands/test.js +++ b/tests/webdriver/commands/test.js @@ -1,21 +1,25 @@ -var path = require('path'), - q = require('q'), - qfs = require('q-io/fs'), - EOL = require('os').EOL; - +var path = require("path"), + q = require("q"), + qfs = require("q-io/fs"), + EOL = require("os").EOL; // TEST ENVIRONMENT RESOLUTION - lowest common denominator of resolutions across all platforms -> QVGA (240x320) in landscape var viewportWidth = 320, viewportHeight = 240; // BASE PATHS FOR TEST SPECIFIC FILES -var testPath = 'tests/webdriver/', - expectedPath = 'tests/webdriver/assets/', - resultPath = 'build/webdriver/', - failedPath = resultPath + 'failed/'; - - -module.exports = function addTestSpecificCommands(client, capabilities, runId, QUnit, indexModuleFileName) { +var testPath = "tests/webdriver/", + expectedPath = "tests/webdriver/assets/", + resultPath = "build/webdriver/", + failedPath = resultPath + "failed/"; + +module.exports = function addTestSpecificCommands( + client, + capabilities, + runId, + QUnit, + indexModuleFileName +) { // CURRENT TEST BASENAME AND RELATIVE PATH WITHOUT EXTENSION function currentTestPath() { return QUnit.config.current.module.name; @@ -27,74 +31,118 @@ module.exports = function addTestSpecificCommands(client, capabilities, runId, Q // QUnit TESTRUNNER SETUP var qunitModule = QUnit.module; QUnit.module = function(testModule) { - if (typeof testModule === 'string') - throw new Error('Custom module names are not supported in webdriver tests. Call `QUnit.module(module)` per start of file instead.'); - - var testFilePath = path.relative(path.dirname(indexModuleFileName), testModule.filename), - testFilePathNoExt = testFilePath.substr(0, testFilePath.lastIndexOf('.')) || testFilePath; + if (typeof testModule === "string") + throw new Error( + "Custom module names are not supported in webdriver tests. Call `QUnit.module(module)` per start of file instead." + ); + + var testFilePath = path.relative( + path.dirname(indexModuleFileName), + testModule.filename + ), + testFilePathNoExt = + testFilePath.substr(0, testFilePath.lastIndexOf(".")) || + testFilePath; // need to convert possible windows path (with backslashes) to url (with forwardslashes) - qunitModule(testFilePathNoExt.replace(/\\/g, '/')); + qunitModule(testFilePathNoExt.replace(/\\/g, "/")); }; // WEBDRIVER COMMAND: TEST PAGE URL SHORTCUT client.addCommand("testUrl", function(testName, testScript) { - if (typeof testName === 'string' && typeof testScript === 'undefined') { + if (typeof testName === "string" && typeof testScript === "undefined") { testScript = testName; testName = undefined; } - console.log("\n# Starting " + (testName || currentTestName()) + " test for " + runId); - - if (typeof testScript === 'string') { - var testFilePath = resultPath + (testName || currentTestName()) + '.html', - testFile = "" + EOL + - "" + EOL + - "" + EOL + - " " + EOL + - " CraftyJS Webdriver Test" + EOL + - " " + EOL + - " " + EOL + - " " + EOL + - "" + EOL + - "" + EOL + - "" + EOL + - "" + EOL + - "" + EOL; - return qfs.write(testFilePath, testFile, 'w+') + console.log( + "\n# Starting " + + (testName || currentTestName()) + + " test for " + + runId + ); + + if (typeof testScript === "string") { + var testFilePath = + resultPath + (testName || currentTestName()) + ".html", + testFile = + "" + + EOL + + "" + + EOL + + "" + + EOL + + " " + + EOL + + " CraftyJS Webdriver Test" + + EOL + + " " + + EOL + + " " + + EOL + + " " + + EOL + + "" + + EOL + + "" + + EOL + + "" + + EOL + + "" + + EOL + + "" + + EOL; + return ( + qfs + .write(testFilePath, testFile, "w+") // all urls need a prepended "/" so that they are expanded based upon baseUrl in config - .then(this.url.bind(this, '/' + testFilePath)); + .then(this.url.bind(this, "/" + testFilePath)) + ); } else { // all urls need a prepended "/" so that they are expanded based upon baseUrl in config - return this.url('/' + testPath + currentTestPath() + '.html'); + return this.url("/" + testPath + currentTestPath() + ".html"); } }); // WEBDRIVER COMMANDS: SCREENSHOT SHORTCUTS client.addCommand("saveExpectedScreenshot", function(pathMod, bounds) { var suffix, testName; - if (typeof pathMod === 'object') - bounds = pathMod; - else if (typeof pathMod === 'string') - if (pathMod.charAt(0) === '-') suffix = pathMod; + if (typeof pathMod === "object") bounds = pathMod; + else if (typeof pathMod === "string") + if (pathMod.charAt(0) === "-") suffix = pathMod; else testName = pathMod; - suffix = suffix || ''; + suffix = suffix || ""; testName = testName || currentTestName(); - bounds = bounds || {x: 0, y: 0, w: viewportWidth, h: viewportHeight}; + bounds = bounds || { x: 0, y: 0, w: viewportWidth, h: viewportHeight }; - return this.saveNormalizedScreenshot(expectedPath + testName + suffix + '-expected.png', bounds); + return this.saveNormalizedScreenshot( + expectedPath + testName + suffix + "-expected.png", + bounds + ); }); client.addCommand("saveActualScreenshot", function(suffix, bounds) { - if (typeof suffix === 'object') { + if (typeof suffix === "object") { bounds = suffix; suffix = undefined; } - suffix = suffix ? '-' + suffix : ''; - bounds = bounds || {x: 0, y: 0, w: viewportWidth, h: viewportHeight}; - - return this.saveNormalizedScreenshot(resultPath + currentTestName() + suffix + '-' + runId + '-actual.png', bounds); + suffix = suffix ? "-" + suffix : ""; + bounds = bounds || { x: 0, y: 0, w: viewportWidth, h: viewportHeight }; + + return this.saveNormalizedScreenshot( + resultPath + + currentTestName() + + suffix + + "-" + + runId + + "-actual.png", + bounds + ); }); // WEBDRIVER COMMAND: IMAGE RESAMBLANCE ASSERTION SHORTCUT @@ -102,49 +150,60 @@ module.exports = function addTestSpecificCommands(client, capabilities, runId, Q var suffix, testName, bounds, threshold, checkAntialiasing; for (var i = 0, l = arguments.length, type; i < l; ++i) { type = typeof arguments[i]; - if (type === 'object') bounds = arguments[i]; - else if (type === 'number') threshold = arguments[i]; - else if (type === 'boolean') checkAntialiasing = arguments[i]; - else if (type === 'string') - if (arguments[i].charAt(0) === '-') suffix = arguments[i]; + if (type === "object") bounds = arguments[i]; + else if (type === "number") threshold = arguments[i]; + else if (type === "boolean") checkAntialiasing = arguments[i]; + else if (type === "string") + if (arguments[i].charAt(0) === "-") suffix = arguments[i]; else testName = arguments[i]; } - suffix = suffix || ''; + suffix = suffix || ""; testName = testName || currentTestName(); threshold = threshold || 0.05; - bounds = bounds || {x: 0, y: 0, w: viewportWidth, h: viewportHeight}; + bounds = bounds || { x: 0, y: 0, w: viewportWidth, h: viewportHeight }; checkAntialiasing = !!checkAntialiasing; - var expected = testName + suffix + '-expected.png', - actual = currentTestName() + suffix + '-' + runId + '-actual.png', - diff = currentTestName() + suffix + '-' + runId + '-diff.png'; - - return this.resemble(resultPath + actual, expectedPath + expected, - resultPath + diff, bounds, checkAntialiasing) - .then(function(result) { - if (!result || !('misMatchPercentage' in result)) { - QUnit.assert.ok(false, "Expected screenshot exists."); - } else { - QUnit.assert.ok(result.misMatchPercentage < threshold, - "Screenshot matches recorded one within error margin:" + EOL + - "Error " + result.misMatchPercentage + " >= threshold " + threshold + "."); - if (result.misMatchPercentage >= threshold) - return q.all([ - qfs.copy(resultPath + actual, failedPath + actual), - qfs.copy(resultPath + diff, failedPath + diff) - ]); - } - }); + var expected = testName + suffix + "-expected.png", + actual = currentTestName() + suffix + "-" + runId + "-actual.png", + diff = currentTestName() + suffix + "-" + runId + "-diff.png"; + + return this.resemble( + resultPath + actual, + expectedPath + expected, + resultPath + diff, + bounds, + checkAntialiasing + ).then(function(result) { + if (!result || !("misMatchPercentage" in result)) { + QUnit.assert.ok(false, "Expected screenshot exists."); + } else { + QUnit.assert.ok( + result.misMatchPercentage < threshold, + "Screenshot matches recorded one within error margin:" + + EOL + + "Error " + + result.misMatchPercentage + + " >= threshold " + + threshold + + "." + ); + if (result.misMatchPercentage >= threshold) + return q.all([ + qfs.copy(resultPath + actual, failedPath + actual), + qfs.copy(resultPath + diff, failedPath + diff) + ]); + } + }); }); // WEBDRIVER COMMAND: WAIT FOR BROWSER SIGNAL SHORTCUT client.addCommand("waitBarrier", function(label, ms) { - return this.waitForExist('#' + label, ms); + return this.waitForExist("#" + label, ms); }); // WEBDRIVER COMMAND: SIGNAL TO BROWSER SHORTCUT client.addCommand("signalBarrier", function(label) { return this.execute(function(label) { - window.triggerBarrierSignal(label); - }, label); + window.triggerBarrierSignal(label); + }, label); }); }; diff --git a/tests/webdriver/common.js b/tests/webdriver/common.js index 96d37b45..6fa5af68 100644 --- a/tests/webdriver/common.js +++ b/tests/webdriver/common.js @@ -1,36 +1,37 @@ -window.addEventListener('load', function() { - // signal to webdriver - window.signalBarrier = function(label) { - Crafty.one('PostRender', function() { - var div = document.createElement('div'); - div.setAttribute('id', label); - document.body.appendChild(div); - }); - }; +window.addEventListener("load", function() { + // signal to webdriver + window.signalBarrier = function(label) { + Crafty.one("PostRender", function() { + var div = document.createElement("div"); + div.setAttribute("id", label); + document.body.appendChild(div); + }); + }; - // wait for webdriver signal - var barrierCallbacks = window.barrierCallbacks = {}; - var barrierSignals = window.barrierSignals = {}; - window.waitBarrier = function(label, callback) { - if (barrierSignals[label]) { // signal already received, trigger callback immediately - callback.call(null); - } else { // signal not yet received, register callback for later - var callbacks = barrierCallbacks[label] || (barrierCallbacks[label] = []); - callbacks.push(callback); - } - }; - window.triggerBarrierSignal = function(label) { - // set signal as received - barrierSignals[label] = true; - // trigger registered callbacks for incoming signal - var callbacks, callback; - if ((callbacks = barrierCallbacks[label])) { - while ((callback = callbacks.pop())) - callback.call(null); - } - }; + // wait for webdriver signal + var barrierCallbacks = (window.barrierCallbacks = {}); + var barrierSignals = (window.barrierSignals = {}); + window.waitBarrier = function(label, callback) { + if (barrierSignals[label]) { + // signal already received, trigger callback immediately + callback.call(null); + } else { + // signal not yet received, register callback for later + var callbacks = barrierCallbacks[label] || (barrierCallbacks[label] = []); + callbacks.push(callback); + } + }; + window.triggerBarrierSignal = function(label) { + // set signal as received + barrierSignals[label] = true; + // trigger registered callbacks for incoming signal + var callbacks, callback; + if ((callbacks = barrierCallbacks[label])) { + while ((callback = callbacks.pop())) callback.call(null); + } + }; - // lowest common denominator of resolutions across all platforms -> QVGA (240x320) in landscape - Crafty.init(320, 240); - Crafty.background('rgb(127,127,127)'); -}); \ No newline at end of file + // lowest common denominator of resolutions across all platforms -> QVGA (240x320) in landscape + Crafty.init(320, 240); + Crafty.background("rgb(127,127,127)"); +}); diff --git a/tests/webdriver/index-webdriver-cloud.js b/tests/webdriver/index-webdriver-cloud.js index 8b93a6bf..b480af6f 100644 --- a/tests/webdriver/index-webdriver-cloud.js +++ b/tests/webdriver/index-webdriver-cloud.js @@ -1,7 +1,7 @@ exports.config = { - specs: require('./index-webdriver.js').specs(), - framework: 'qunit', - baseUrl: 'http://localhost:8000', + specs: require("./index-webdriver.js").specs(), + framework: "qunit", + baseUrl: "http://localhost:8000", sync: false, user: process.env.SAUCE_USERNAME, @@ -14,27 +14,29 @@ exports.config = { commandTimeout: 60, idleTimeout: 90, - name: '(WIP) Cross-browser regression tests for CraftyJS', - 'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER, + name: "(WIP) Cross-browser regression tests for CraftyJS", + "tunnel-identifier": process.env.TRAVIS_JOB_NUMBER, tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER, - tags: [ process.env.TRAVIS_BRANCH ], + tags: [process.env.TRAVIS_BRANCH], build: process.env.TRAVIS_BUILD_NUMBER, - 'public': 'public' + public: "public" }; - var browsers = require('../test-browsers.json'); + var browsers = require("../test-browsers.json"); browsers.forEach(function(capabilities) { for (var k in baseCapabilities) capabilities[k] = baseCapabilities[k]; - capabilities.exclude = require('./index-webdriver.js').exclude(capabilities); + capabilities.exclude = require("./index-webdriver.js").exclude( + capabilities + ); }); return browsers; })(), updateJob: true, waitforTimeout: 3000, - services: ['sauce'], + services: ["sauce"], sauceConnect: false, maxInstances: 5, sauceConnectOpts: { @@ -58,12 +60,12 @@ exports.config = { downloadRetryTimeout: 2000 }, - logLevel: 'silent', // 'verbose' for debugging + logLevel: "silent", // 'verbose' for debugging coloredLogs: true, - screenshotPath: 'build/webdriver/failed', + screenshotPath: "build/webdriver/failed", - onPrepare: require('./index-webdriver.js').onPrepare, - before: require('./index-webdriver.js').before, - after: require('./index-webdriver.js').after, - onComplete: require('./index-webdriver.js').onComplete, + onPrepare: require("./index-webdriver.js").onPrepare, + before: require("./index-webdriver.js").before, + after: require("./index-webdriver.js").after, + onComplete: require("./index-webdriver.js").onComplete }; diff --git a/tests/webdriver/index-webdriver-local.js b/tests/webdriver/index-webdriver-local.js index 5a8d5f2a..6b1d865f 100644 --- a/tests/webdriver/index-webdriver-local.js +++ b/tests/webdriver/index-webdriver-local.js @@ -1,23 +1,27 @@ exports.config = { - specs: require('./index-webdriver.js').specs(), - framework: 'qunit', - baseUrl: './', + specs: require("./index-webdriver.js").specs(), + framework: "qunit", + baseUrl: "./", sync: false, - capabilities: [{ - browserName: 'phantomjs', - exclude: require('./index-webdriver.js').exclude({ browserName: 'phantomjs' }) - }], + capabilities: [ + { + browserName: "phantomjs", + exclude: require("./index-webdriver.js").exclude({ + browserName: "phantomjs" + }) + } + ], updateJob: false, waitforTimeout: 3000, // maxInstances: 1, // uncomment this for debugging - logLevel: 'silent', // 'verbose' for debugging + logLevel: "silent", // 'verbose' for debugging coloredLogs: true, - screenshotPath: 'build/webdriver/failed', + screenshotPath: "build/webdriver/failed", - onPrepare: require('./index-webdriver.js').onPrepare, - before: require('./index-webdriver.js').before, - after: require('./index-webdriver.js').after, - onComplete: require('./index-webdriver.js').onComplete + onPrepare: require("./index-webdriver.js").onPrepare, + before: require("./index-webdriver.js").before, + after: require("./index-webdriver.js").after, + onComplete: require("./index-webdriver.js").onComplete }; diff --git a/tests/webdriver/index-webdriver.js b/tests/webdriver/index-webdriver.js index 89d10a13..6ee14829 100644 --- a/tests/webdriver/index-webdriver.js +++ b/tests/webdriver/index-webdriver.js @@ -9,26 +9,24 @@ var hasWebGL = function(capabilities) { }; var tests = { - 'template/template-multi': canRunWebdriver, - 'color/color-dom': canRunWebdriver, - 'color/color-canvas': canRunWebdriver, + "template/template-multi": canRunWebdriver, + "color/color-dom": canRunWebdriver, + "color/color-canvas": canRunWebdriver, // only edge supports webgl for webdriver right now - 'color/color-webgl': function(capabilities) { + "color/color-webgl": function(capabilities) { return canRunWebdriver(capabilities) && hasWebGL(capabilities); } }; - // BROWSERS THAT NEED SYNTHETIC EVENTS // some browsers don't support triggering native events via webdriver var syntheticKeyEvents = function(capabilities) { - return 'syntheticKeyEvents' in capabilities; + return "syntheticKeyEvents" in capabilities; }; var syntheticMouseEvents = function(capabilities) { - return 'syntheticMouseEvents' in capabilities; + return "syntheticMouseEvents" in capabilities; }; - // TODO: FIX NON-STANDARD SCREENSHOT REGIONS FOR MOBILE BROWSERS // NON-STANDARD SCREENSHOT REGIONS PER PLATFORM // ROTATE CCW 90°, CROP TO DOCUMENT REGION, SCALE UP, CROP TO BOUNDS @@ -38,15 +36,17 @@ var rotatedCrops = {}; //rotatedCrops[getRunId({"browserName": "android", "version": "5.1", "platform": "Linux"})] = { x: 0, y: 110, w: 261, h: 196, stretchW: 320, stretchH: 240 }; //rotatedCrops[getRunId({"browserName": "iphone", "version": "8.4", "platform": "OS X 10.10"})] = { x: 0, y: 420, w: 217, h: 162, stretchW: 320, stretchH: 240 }; - - - - // ==== // UUID // ==== function getRunId(capabilities) { - return capabilities.browserName + '-' + capabilities.version + '-' + capabilities.platform; + return ( + capabilities.browserName + + "-" + + capabilities.version + + "-" + + capabilities.platform + ); } // ====== @@ -54,7 +54,7 @@ function getRunId(capabilities) { // ====== exports.specs = function() { return Object.keys(tests).map(function(t) { - return 'tests/webdriver/' + t + '.js'; + return "tests/webdriver/" + t + ".js"; }); }; exports.exclude = function(capabilities) { @@ -63,7 +63,7 @@ exports.exclude = function(capabilities) { for (var test in tests) { runCondition = tests[test]; if (runCondition === false || !runCondition(capabilities)) - excluded.push('tests/webdriver/' + test + '.js'); + excluded.push("tests/webdriver/" + test + ".js"); } return excluded; }; @@ -72,18 +72,29 @@ exports.exclude = function(capabilities) { // Hooks // ===== exports.onPrepare = function() {}; -exports.before = function() { // BEFORE RUNNING ANY TESTS, WITH GLOBALS AVAILABLE +exports.before = function() { + // BEFORE RUNNING ANY TESTS, WITH GLOBALS AVAILABLE var capabilities = global.browser.desiredCapabilities, runId = getRunId(capabilities); // add commands //TODO retry commands with webbriverio/lib/helpers.js/staleElementRetry if need arises (StaleElementReference) - require('./commands/generic.js')(global.browser, capabilities, runId); - require('./commands/browser.js')( - global.browser, capabilities, runId, - syntheticKeyEvents(capabilities), syntheticMouseEvents(capabilities), rotatedCrops[runId] + require("./commands/generic.js")(global.browser, capabilities, runId); + require("./commands/browser.js")( + global.browser, + capabilities, + runId, + syntheticKeyEvents(capabilities), + syntheticMouseEvents(capabilities), + rotatedCrops[runId] + ); + require("./commands/test.js")( + global.browser, + capabilities, + runId, + global.QUnit, + module.filename ); - require('./commands/test.js')(global.browser, capabilities, runId, global.QUnit, module.filename); }; exports.after = function(failures, pid) {}; exports.onComplete = function() {}; diff --git a/tests/webdriver/template/template-generic.js b/tests/webdriver/template/template-generic.js index 97906916..e63b3f92 100644 --- a/tests/webdriver/template/template-generic.js +++ b/tests/webdriver/template/template-generic.js @@ -2,7 +2,7 @@ QUnit.module(module); QUnit.test("Generic webdriver test template", function(assert) { return browser - .url('http://info.cern.ch/hypertext/WWW/TheProject.html') + .url("http://info.cern.ch/hypertext/WWW/TheProject.html") .getTitle(function(err, title) { assert.strictEqual(title, "The World Wide Web project"); }); diff --git a/tests/webdriver/template/template-local.js b/tests/webdriver/template/template-local.js index 9e564e0a..8fba0b9e 100644 --- a/tests/webdriver/template/template-local.js +++ b/tests/webdriver/template/template-local.js @@ -2,6 +2,6 @@ QUnit.module(module); QUnit.test("Local webdriver test template", function(assert) { return browser - .url('tests/webdriver/template-local.html') - .assertResemble(10.00); + .url("tests/webdriver/template-local.html") + .assertResemble(10.0); }); diff --git a/tests/webdriver/template/template-multi.js b/tests/webdriver/template/template-multi.js index 675fb3ce..2034bfa7 100644 --- a/tests/webdriver/template/template-multi.js +++ b/tests/webdriver/template/template-multi.js @@ -1,25 +1,35 @@ QUnit.module(module); -QUnit.test("Multi test template part 1: Player lands on ground floor automatically after navigating to test page", function(assert) { - return browser - .testUrl() - .waitBarrier("landed1") - .assertResemble("-firstLand", 0.20); -}); +QUnit.test( + "Multi test template part 1: Player lands on ground floor automatically after navigating to test page", + function(assert) { + return browser + .testUrl() + .waitBarrier("landed1") + .assertResemble("-firstLand", 0.2); + } +); -QUnit.test("Multi test template part 2: Player lands on platform after jump key pressed", function(assert) { - return browser - .keyDown('W').keyUp('W') - .waitBarrier("landed2", 2000) - .assertResemble("-secondLand", 0.20); -}); +QUnit.test( + "Multi test template part 2: Player lands on platform after jump key pressed", + function(assert) { + return browser + .keyDown("W") + .keyUp("W") + .waitBarrier("landed2", 2000) + .assertResemble("-secondLand", 0.2); + } +); -QUnit.test("Multi test template part 3: Player lands on floor after being drag & dropped", function(assert) { - return browser - .pointerMove(280, 107) - .pointerDown() - .pointerMove(25, 135) - .pointerUp() - .waitBarrier("landed3", 2000) - .assertResemble("-thirdLand", 0.20); -}); +QUnit.test( + "Multi test template part 3: Player lands on floor after being drag & dropped", + function(assert) { + return browser + .pointerMove(280, 107) + .pointerDown() + .pointerMove(25, 135) + .pointerUp() + .waitBarrier("landed3", 2000) + .assertResemble("-thirdLand", 0.2); + } +);