From dffb1b49172bad8b2532a19605e588be7ec30f92 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Fri, 29 Nov 2013 15:09:02 -0500 Subject: [PATCH 01/61] Fix bug in steptype() There were two bugs: the function argument shadowed the mode variable, and the semifixed mode would never actually be set. --- src/core.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core.js b/src/core.js index 5bf142c0..f89b358c 100644 --- a/src/core.js +++ b/src/core.js @@ -1040,14 +1040,14 @@ Crafty.extend({ * */ - steptype: function (mode, option) { - if (mode === "variable" || mode === "semifixed") { - mode = "variable"; + steptype: function (newmode, option) { + if (newmode === "variable" || newmode === "semifixed") { + mode = newmode; if (option) maxTimestep = option; - } else if (mode === "fixed") { - mode = mode; + } else if (newmode === "fixed") { + mode = "fixed"; if (option) maxFramesPerStep = option; } else { From cbea3e672a73a74ddecaad9c537819a59c2fb00f Mon Sep 17 00:00:00 2001 From: pawurb Date: Sat, 30 Nov 2013 12:23:29 +0100 Subject: [PATCH 02/61] Delete trailing spaces. --- Gruntfile.js | 6 +- README.md | 2 +- build/api-gen.coffee | 28 ++++---- build/template.html | 2 +- changelog.txt | 2 +- src/2D.js | 8 +-- src/animation.js | 68 +++++++++--------- src/collision.js | 2 +- src/core.js | 6 +- src/diamondiso.js | 2 +- src/drawing.js | 2 +- src/isometric.js | 2 +- src/storage.js | 2 +- src/text.js | 6 +- src/viewport.js | 2 +- tests/animation/animation.html | 10 +-- tests/animation/sprite-animation.js | 26 +++---- tests/camera.html | 12 ++-- tests/core.html | 104 ++++++++++++++-------------- tests/dom.html | 12 ++-- tests/events.html | 26 +++---- tests/isometric.html | 46 ++++++------ tests/loader.html | 2 +- tests/stage.html | 14 ++-- tests/storage.html | 4 +- tests/text.html | 4 +- tests/touchevents.html | 4 +- 27 files changed, 202 insertions(+), 202 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index e88c0d86..f03a5f18 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -95,7 +95,7 @@ module.exports = function (grunt) { 'tests/text.html', 'tests/dom.html' ] - }, + }, jsvalidate: { files: "crafty.js" @@ -115,10 +115,10 @@ module.exports = function (grunt) { grunt.registerTask('version', 'Takes the version into src/version.js', function() { fs.writeFileSync('src/version.js', 'module.exports = "' + version + '";'); }); - + // Build development grunt.registerTask('build:dev', ['browserify:debug', 'usebanner']); - + // Build release grunt.registerTask('build:release', ['browserify:dist', 'usebanner']); diff --git a/README.md b/README.md index b4e1d6ae..c76687da 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ Crafty.e("RightPoints, DOM, 2D, Text") ##Developing If you want to fix a bug, please submit a pull request against the development branch. Some guides to help you can be found [on the wiki](https://github.com/craftyjs/Crafty/wiki) - + If you would like to make larger contributions please catch us in the [forum](https://groups.google.com/forum/?fromgroups#!forum/craftyjs) and we will help you get started. Much appreciated :-) diff --git a/build/api-gen.coffee b/build/api-gen.coffee index 566a2a39..24286b15 100644 --- a/build/api-gen.coffee +++ b/build/api-gen.coffee @@ -38,7 +38,7 @@ processBlock = (block)-> if not Table.comps[block.comp] Table.comps[block.name] = {name:block.name, parts:[]} Table.comps[block.name].block = block - + # Having a component tag means it's part of a component page if block.comp if not Table.comps[block.comp] @@ -123,10 +123,10 @@ class DocBlock else return false - # Method for returning the documentation for this block + # Method for returning the documentation for this block getContent: ()-> if @isFunctionTag(@prevTag) then @code.push("") - return @code.join("\n") + triggerBlock(@triggers) + seeBlock(@see) + return @code.join("\n") + triggerBlock(@triggers) + seeBlock(@see) # parse js file parseJS = (path) -> @@ -140,7 +140,7 @@ parseJS = (path) -> block = new DocBlock() block.file = path block.line = ln - open = true + open = true # process if open block.processLine(line) @@ -153,18 +153,18 @@ parseJS = (path) -> if block.categories.length is 0 and block.comp is null console.log("No component or category for block at #{block.file}:#{block.line+1} (#{block.name})") data.push block - + cleanName = (name) -> name.replace(".", "-") triggerBlock = (triggers, noheader)-> - return '' if triggers?.length is 0 - if noheader then block = "
" else block = "\n

Events

\n
" + return '' if triggers?.length is 0 + if noheader then block = "
" else block = "\n

Events

\n
" for t in triggers if t.objProp? - block+= "
#{t.event} [#{t.objName}: #{t.objProp}]
#{t.description}
\n" + block+= "
#{t.event} [#{t.objName}: #{t.objProp}]
#{t.description}
\n" else - block+= "
#{t.event}
#{t.description}
\n" + block+= "
#{t.event}
#{t.description}
\n" block+="
" return block @@ -185,9 +185,9 @@ createPage = (page)-> eventContent = "##{page.name}\n" + triggerBlock(page.block.triggers, true) partContent = "" - if page.parts.length + if page.parts.length partList = "

Properties and Methods

    " - for part in page.parts + for part in page.parts clName = cleanName(part.name) partList += """
  • #{part.name}
  • """ partContent += "\n\n
    \n\nBack to top

    #{part.name}

    \n" + marked(part.getContent()) + "\n\n
    \n\n" @@ -219,7 +219,7 @@ saveMd = (data) -> nav_html+="
  • #{page.name}
  • " # Pages can be listed multiple times in the nav, but we should only generate them once - continue if page.flagged is true + continue if page.flagged is true page.flagged = true # Make page @@ -246,7 +246,7 @@ saveMd = (data) -> for page in pages html = template.replace("CONTENT_DIV", page.content_html).replace("NAV_DIV", nav_html) writeOut(page.name, html) - + return @@ -267,7 +267,7 @@ document = (files, output, template, version, callback)-> dirOut = output versionString = version for file in files - parseJS file + parseJS file # Save the data to files saveMd data diff --git a/build/template.html b/build/template.html index e86344c2..c973e7fc 100644 --- a/build/template.html +++ b/build/template.html @@ -1,4 +1,4 @@ ---- +--- layout: default --- diff --git a/changelog.txt b/changelog.txt index 54b3d5a1..bd4751fa 100644 --- a/changelog.txt +++ b/changelog.txt @@ -140,7 +140,7 @@ v0.3.2 * Fixed a collision bug -v0.3.1 +v0.3.1 * Window resize even on fullscreen * Use scrollTop and scrollLeft diff --git a/src/2D.js b/src/2D.js index a4c4bf0f..f7fe9777 100644 --- a/src/2D.js +++ b/src/2D.js @@ -1,14 +1,14 @@ var Crafty = require('./core.js'), document = window.document, HashMap = require('./HashMap.js'); -// Crafty._rectPool +// Crafty._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 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. Crafty._rectPool = (function () { @@ -940,7 +940,7 @@ Crafty.c("2D", { y2 = (this._y + this._origin.y - e.o.y) * e.cos - (this._x + this._origin.x - e.o.x) * e.sin + (e.o.y - this._origin.y); this._attr('_rotation', this._rotation - e.deg); this._attr('_x', x2 ); - this._attr('_y', y2 ); + this._attr('_y', y2 ); }, /**@ @@ -1112,7 +1112,7 @@ Crafty.c("Gravity", { if (hit) { //stop falling if found and player is moving down if (this._falling && ((this._gy > this._jumpSpeed) || !this._up)){ this.stopFalling(hit); - } + } } else { this._falling = true; //keep falling otherwise } diff --git a/src/animation.js b/src/animation.js index 6fc2a912..ab5af2bf 100644 --- a/src/animation.js +++ b/src/animation.js @@ -14,13 +14,13 @@ Crafty.easing.prototype = { steps: null, complete: false, paused: false, - - // init values - reset: function(){ + + // init values + reset: function(){ this.loops = 1; this.clock = 0; this.complete = false; - this.paused = false; + this.paused = false; }, repeat: function(loopCount){ @@ -31,7 +31,7 @@ Crafty.easing.prototype = { this.clock = this.duration * progress; if (typeof loopCount !== "undefined") this.loops = loopCount; - + }, pause: function(){ @@ -61,7 +61,7 @@ Crafty.easing.prototype = { // 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 @@ -89,14 +89,14 @@ Crafty.easing.prototype = { * Must be applied to an entity that has a sprite-map component. * * To define an animation, see the `reel` method. To play an animation, see the `animate` method. -* +* * A reel is an object that contains the animation frames and current state for an animation. The reel object has the following properties: * @param id: (String) - the name of the reel * @param frames: (Array) - A list of frames in the format [xpos, ypos] * @param currentFrame: (Number) - The index of the current frame -* @param easing: (Crafty.easing object) - The object that handles the internal progress of the animation. +* @param easing: (Crafty.easing object) - The object that handles the internal progress of the animation. * @param duration: (Number) - The duration in milliseconds. -* +* * Many animation related events pass a reel object as data. As typical with events, this should be treated as read only data that might be later altered by the entity. If you wish to preserve the data, make a copy of it. * * @see crafty.sprite @@ -119,7 +119,7 @@ Crafty.c("SpriteAnimation", { /* * The currently active reel. - * This value is `null` if no reel is active. + * This value is `null` if no reel is active. */ _currentReel: null, @@ -215,7 +215,7 @@ Crafty.c("SpriteAnimation", { id: reelId, frames: [], currentFrame: 0, - easing: new Crafty.easing(duration), + easing: new Crafty.easing(duration), defaultLoops: 1 }; @@ -265,7 +265,7 @@ Crafty.c("SpriteAnimation", { * * Play one of the reels previously defined through `.reel(...)`. Simply pass the name of the reel. If you wish the * animation to play multiple times in succession, pass in the amount of times as an additional parameter. - * To have the animation repeat indefinitely, pass in `-1`. + * To have the animation repeat indefinitely, pass in `-1`. * * If another animation is currently playing, it will be paused. * @@ -304,7 +304,7 @@ Crafty.c("SpriteAnimation", { this.pauseAnimation(); // This will pause the current animation, if one is playing // Handle repeats; if loopCount is undefined and reelID is a number, calling with that signature - if (typeof loopCount === "undefined") + if (typeof loopCount === "undefined") if (typeof reelId === "number") loopCount = reelId; else @@ -312,9 +312,9 @@ Crafty.c("SpriteAnimation", { // set the animation to the beginning currentReel.easing.reset(); - - // user provided loop count. + + // user provided loop count. this.loops(loopCount); // trigger the necessary events and switch to the first frame @@ -333,7 +333,7 @@ Crafty.c("SpriteAnimation", { * @comp SpriteAnimation * @sign public this .resumeAnimation() * - * This will resume animation of the current reel from its current state. + * This will resume animation of the current reel from its current state. * If a reel is already playing, or there is no current reel, there will be no effect. */ resumeAnimation: function() { @@ -342,8 +342,8 @@ Crafty.c("SpriteAnimation", { this._isPlaying = true; this._currentReel.easing.resume(); this.trigger("StartAnimation", this._currentReel); - } - return this; + } + return this; }, /**@ @@ -368,7 +368,7 @@ Crafty.c("SpriteAnimation", { * @sign public this .resetAnimation() * * Resets the current animation to its initial state. Resets the number of loops to the last specified value, which defaults to 1. - * + * * Neither pauses nor resumes the current animation. */ resetAnimation: function(){ @@ -387,7 +387,7 @@ Crafty.c("SpriteAnimation", { * @sign public this .loops(Number loopCount) * @param loopCount - The number of times to play the animation * - * Sets the number of times the animation will loop for. + * Sets the number of times the animation will loop for. * If called while an animation is in progress, the current state will be considered the first loop. * * @sign public Number .loops() @@ -425,10 +425,10 @@ Crafty.c("SpriteAnimation", { * * @sign public this .reelPosition(String position) * Jumps to the specified position. The only currently accepted value is "end", which will jump to the end of the reel. - * + * * @sign public Number .reelPosition() * @returns The current frame number - * + * */ reelPosition: function(position) { if (this._currentReel === null) @@ -447,7 +447,7 @@ Crafty.c("SpriteAnimation", { position = Math.floor(l * progress); } else { if (position !== Math.floor(position)) - throw("Position " + position + " is invalid."); + throw("Position " + position + " is invalid."); if (position < 0) position = l - 1 + position; progress = position / l; @@ -459,10 +459,10 @@ Crafty.c("SpriteAnimation", { this._setFrame(position); return this; - + }, - + // Bound to "EnterFrame". Progresses the animation by dt, changing the frame if necessary. _animationTick: function(frameData) { var currentReel = this._reels[this._currentReelId]; @@ -480,7 +480,7 @@ Crafty.c("SpriteAnimation", { - + // Set the current frame and update the displayed sprite // The actual progress for the animation must be set seperately. @@ -490,7 +490,7 @@ Crafty.c("SpriteAnimation", { return; currentReel.currentFrame = frameNumber; this._updateSprite(); - this.trigger("FrameChange", currentReel); + this.trigger("FrameChange", currentReel); }, // Update the displayed sprite. @@ -539,9 +539,9 @@ Crafty.c("SpriteAnimation", { * @comp SpriteAnimation * @sign public Reel .getReel() * @returns The current reel, or null if there is no active reel - * + * * @sign public Reel .getReel(reelId) - * @param reelId - The id of the reel to fetch. + * @param reelId - The id of the reel to fetch. * @returns The specified reel, or `undefined` if no such reel exists. * */ @@ -626,7 +626,7 @@ Crafty.c("Tween", { * */ tween: function (props, duration) { - + var tween = { props: props, easing: new Crafty.easing(duration) @@ -643,7 +643,7 @@ Crafty.c("Tween", { this.tweenGroup[propname] = props; } this.tweens.push(tween); - + return this; }, @@ -653,11 +653,11 @@ Crafty.c("Tween", { * @comp Tween * @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. + * 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){ @@ -670,7 +670,7 @@ Crafty.c("Tween", { } return this; - + }, /* diff --git a/src/collision.js b/src/collision.js index bf5aeb17..4d2b1240 100644 --- a/src/collision.js +++ b/src/collision.js @@ -86,7 +86,7 @@ Crafty.c("Collision", { }, - // Change the hitbox when a "Resize" event triggers. + // Change the hitbox when a "Resize" event triggers. _resizeMap: function (e) { var dx, dy, rot = this.rotation * DEG_TO_RAD, diff --git a/src/core.js b/src/core.js index 5bf142c0..bf9417fe 100644 --- a/src/core.js +++ b/src/core.js @@ -28,7 +28,7 @@ var version = require('./version'); * ~~~ * Passing an integer will select the entity with that `ID`. */ - + var Crafty = function (selector) { return new Crafty.fn.init(selector); }, @@ -464,7 +464,7 @@ Crafty.fn = Crafty.prototype = { * @sign public this .timeout(Function callback, Number delay) * @param callback - Method to execute after given amount of milliseconds * @param delay - Amount of milliseconds to execute the method - * + * * The delay method will execute a function after a given amount of time in milliseconds. * * Essentially a wrapper for `setTimeout`. @@ -1339,7 +1339,7 @@ Crafty.extend({ // entity6.trigger('Move')), it causes the execution of fnB() and fnC(). When // the Move event is triggered globally (i.e. Crafty.trigger('Move')), it // will execute fnA, fnB, fnC, fnD. - // + // // In this example, "this" is bound to entity #6 whenever fnB() is executed, and // "this" is bound to Crafty whenever fnD() is executed. // diff --git a/src/diamondiso.js b/src/diamondiso.js index 246c358f..29579f4d 100644 --- a/src/diamondiso.js +++ b/src/diamondiso.js @@ -107,7 +107,7 @@ Crafty.extend({ vp._w += (this._tile.width / 2 + ow); vp._h += (this._tile.height / 2 + oh); /* Crafty.viewport.x = -vp._x; - Crafty.viewport.y = -vp._y; + Crafty.viewport.y = -vp._y; Crafty.viewport.width = vp._w; Crafty.viewport.height = vp._h; */ diff --git a/src/drawing.js b/src/drawing.js index dc7cd551..2efb6ddf 100644 --- a/src/drawing.js +++ b/src/drawing.js @@ -370,7 +370,7 @@ Crafty.DrawManager = (function () { target._y = Math.min(a._y, b._y); target._w -= target._x; target._h -= target._y; - + return target; }, diff --git a/src/isometric.js b/src/isometric.js index 13268014..b3c59199 100644 --- a/src/isometric.js +++ b/src/isometric.js @@ -111,7 +111,7 @@ Crafty.extend({ return { x: -Math.ceil(-left / this._tile.width - (top & 1) * 0.5), y: top / this._tile.height * 2 - }; + }; }, /**@ * #Crafty.isometric.centerAt diff --git a/src/storage.js b/src/storage.js index 563d4eb8..5cdcebed 100644 --- a/src/storage.js +++ b/src/storage.js @@ -53,7 +53,7 @@ var Crafty = require('./core.js'), * @param key - A unique key to search for * @param type - 'save' or 'cache' * @param callback - Do things with the data you get back - * + * * Loads a piece of data from the database. * Entities will be reconstructed from the serialized string diff --git a/src/text.js b/src/text.js index 89c5540c..21402bff 100644 --- a/src/text.js +++ b/src/text.js @@ -77,11 +77,11 @@ Crafty.c("Text", { "cm": 96/2.54, "mm": 96/25.4, "in": 96, - "em": undefined, + "em": undefined, "ex": undefined }; return function (font){ - var number = parseFloat(font); + var number = parseFloat(font); var match = re.exec(font); var unit = match ? match[1] : "px"; if (multipliers[unit] !== undefined) @@ -206,7 +206,7 @@ Crafty.c("Text", { if(propertyKey == 'family'){ this._textFont[propertyKey] = "'" + key[propertyKey] + "'"; } else { - this._textFont[propertyKey] = key[propertyKey]; + this._textFont[propertyKey] = key[propertyKey]; } } } diff --git a/src/viewport.js b/src/viewport.js index f9446ee1..875b44fd 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -64,7 +64,7 @@ Crafty.extend({ * @comp Crafty.viewport * * A rectangle which defines the bounds of the viewport. - * It should be an object with two properties, `max` and `min`, + * It should be an object with two properties, `max` and `min`, * which are each an object with `x` and `y` properties. * * If this property is null, Crafty uses the bounding box of all the items diff --git a/tests/animation/animation.html b/tests/animation/animation.html index f22e5c78..17c022d0 100644 --- a/tests/animation/animation.html +++ b/tests/animation/animation.html @@ -25,10 +25,10 @@

    Sprite animation playground:

    - + - +
@@ -36,11 +36,11 @@

Sprite animation playground:

- + - + - +
diff --git a/tests/animation/sprite-animation.js b/tests/animation/sprite-animation.js index c0c412cc..b8d3c8c9 100644 --- a/tests/animation/sprite-animation.js +++ b/tests/animation/sprite-animation.js @@ -42,7 +42,7 @@ test("Test .getReel() with no active reel", function(){ test("Test .reel() with no active reel", function() { var ret; var newAnimation = Crafty.e("SpriteAnimation"); - + ret = newAnimation.reel(); strictEqual(ret, null, "reel() returns null"); @@ -52,7 +52,7 @@ test("Test .reel() with no active reel", function() { 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.") @@ -121,7 +121,7 @@ test("Test .resetAnimation() with no active reel", function() { newAnimation.destroy(); }); - + test("Test .isPlaying() with no active reel", function() { @@ -162,19 +162,19 @@ test("Test reel switching functionality", function(){ equal(spriteAnimation._currentReelId, "short", "Correct _currentReelId after switching"); equal(spriteAnimation._currentReel.id, "short", "Correct _currentReel.id after switching"); var e =""; - + throws( function(){spriteAnimation.reel("wrong");}, /The specified reel wrong is undefined/, "Function should throw on bad reel"); - + equal(spriteAnimation._currentReelId, "short", "Correct _currentReelId after attempting to switch to bad reel"); - - + + }); test("Test using reel() with no arguments", function(){ spriteAnimation.reel("count"); var ret = spriteAnimation.reel(); equal(ret, "count", ".reel() returns the current id"); - + // Test setting reel id manually, since that's what reel() should return // Don't ever do this in actual code! spriteAnimation._currentReelId = null; @@ -183,7 +183,7 @@ test("Test using reel() with no arguments", function(){ // Reset currentReelId, since we messed it up! spriteAnimation.reel("count"); - + }); test("Test using .getReel() with no arguments", function(){ @@ -210,7 +210,7 @@ test("Test using .reel to set an animation using start and end values", function spriteAnimation.reel('short-test'); var reel = spriteAnimation.getReel('short-test'); equal(reel.id, "short-test", "Id of reel is set correctly."); - + equal(reel.duration, 3, "Reel has correct duration.") equal(reel.currentFrame, 0, "Reel starts with correct currentFrame of 0."); equal(reel.defaultLoops, 1, "Reel starts with correct default number of loops."); @@ -230,7 +230,7 @@ test("Test using .reel to set an animation using an array of frames", function() spriteAnimation.reel('short-test-2'); var reel = spriteAnimation.getReel('short-test-2'); equal(reel.id, "short-test-2", "Id of reel is set correctly."); - + equal(reel.duration, 3, "Reel has correct duration.") equal(reel.currentFrame, 0, "Reel starts with correct currentFrame of 0."); equal(reel.defaultLoops, 1, "Reel starts with correct default number of loops."); @@ -241,7 +241,7 @@ test("Test using .reel to set an animation using an array of frames", function() deepEqual(frames[0], [0, 0], "First frame is correct."); deepEqual(frames[1], [64, 0], "Second frame is correct."); deepEqual(frames[2], [128, 0], "Third frame is correct."); - + }) module("Reel state"); @@ -414,7 +414,7 @@ test("Play an animation with an infinite repeat count", function() { expected.push(0); expected.push(1); expected.push(2); - } + } deepEqual(eventFrames, expected, "Expected events matching the amount of frames that pass"); deepEqual(finishedAnimations, [], "Expected no animation to end"); diff --git a/tests/camera.html b/tests/camera.html index 951472fc..96cb3baf 100644 --- a/tests/camera.html +++ b/tests/camera.html @@ -13,15 +13,15 @@
  • (DD) Click is not working, Zoom is working, Hotspot visible
  • - +
  • (CC) Click is working, Zoom is not working, Hotspot visible
  • - +
  • (CD) Click is not working, Zoom is not working, Hotspot is visible and zooming.
  • - +
  • (DC) Click is not working (wrong hotspot), Zoom is working, Hotspot invisible
  • @@ -38,9 +38,9 @@ } , imageEntities = '2D, DOM, Image' , hotspotEntities = '2D, DOM, Color, Mouse'; - - - + + + config.imagePath = 'http://placekitten.com/' + config.playgroundWidth + '/' + config.playgroundHeight Crafty.scene('main', function() { diff --git a/tests/core.html b/tests/core.html index 90bf3400..4d241d0a 100755 --- a/tests/core.html +++ b/tests/core.html @@ -1,4 +1,4 @@ - @@ -12,7 +12,7 @@ security settings when firefox opens an HTML file saved on your computer; it does not affect your security when you are browsing the internet.] * To run the tests in chrome: You need to pass a flag when opening chrome. - For example, in Linux, you would run this command in a Linux terminal: + For example, in Linux, you would run this command in a Linux terminal: chromium-browser /path/to/core.html --allow-file-access-from-files --> @@ -33,14 +33,14 @@ $(document).ready(function() { Crafty.init(500,500); - + module("CORE"); test("getVersion", function() { - + ok(Crafty.getVersion(), "The actual library version"); }); - + test("selectors", function() { var first = Crafty.e("test"); Crafty.e("test"); @@ -52,29 +52,29 @@ strictEqual( Crafty("test").length, 6, "Single component" ); strictEqual( Crafty("test test2").length, 2, "Two components ANDed" ); strictEqual( Crafty("test, test2").length, 7, "Two components ORed" ); - + strictEqual( Crafty("*").length, 7, "All components - universal selector" ); - + strictEqual( Crafty(first[0]), first, "Get by ID" ); // Clean up Crafty("*").destroy(); }); - + test("addComponent and removeComponent", function() { var first = Crafty.e("test"); Crafty.c("comp", {added: true}); first.addComponent("test3"); strictEqual( first.has("test3"), true, "component added" ); - + first.addComponent("comp"); strictEqual( first.added, true, "component with property exists" ); - + first.addComponent("multi1, multi2"); 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)" ); first.removeComponent("comp", false); @@ -101,19 +101,19 @@ e.removeComponent("comp"); strictEqual( removeRan, true, "Remove runs on correct component removal"); strictEqual( destroyFlag, false, "Destroy flag false on regular removal"); - + removeRan = false; e.addComponent("comp"); e.destroy() strictEqual( removeRan, true, "Remove runs on component destrution"); strictEqual( destroyFlag, true, "Destroy flag true on destruction"); }); - + test("attr", function() { var first = Crafty.e("test"); first.attr("single", true); strictEqual( first.single, true, "single attribute assigned" ); - + first.attr({prop: "test", another: 56}); strictEqual( first.prop, "test", "properties from object assigned" ); strictEqual( first.another, 56, "properties from object assigned" ); @@ -139,7 +139,7 @@ // Clean up Crafty("*").destroy(); }); - + test("bind", function () { var first = Crafty.e("test"), triggered = false; @@ -159,15 +159,15 @@ }); first.unbind("myevent"); first.trigger("myevent"); - + function callback() { ok(false, "This should also not be triggered (unbind by FN)"); } - + function callback2() { ok(true, "This should be triggered"); } - + first.bind("myevent", callback); first.bind("myevent", callback2); first.unbind("myevent", callback); @@ -175,7 +175,7 @@ // Clean up Crafty("*").destroy(); }); - + test("globalBindAndUnbind", function () { var flag = 0; var add_1 = function () {flag += 1;}; @@ -193,7 +193,7 @@ Crafty.trigger("theglobalevent"); strictEqual(flag, 221, "global event full unbinding worked"); }); - + test("each", function() { var count = 0; Crafty.e("test"); @@ -203,7 +203,7 @@ Crafty.e("test, test2"); Crafty.e("test, test2"); Crafty.e("test2"); - + Crafty("test").each(function() { count++; }); @@ -217,7 +217,7 @@ // Clean up Crafty("*").destroy(); }); - + test("requires", function() { var first = Crafty.e("test"); Crafty.c("already", { @@ -226,19 +226,19 @@ Crafty.c("notyet", { notyet: true }); - + first.addComponent("already"); first.already = "already"; - + first.requires("already, notyet"); - + 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"); // Clean up Crafty("*").destroy(); }); - + test("destroy", function() { var first = Crafty.e("test"), id = first[0]; //id @@ -247,34 +247,34 @@ // Clean up Crafty("*").destroy(); }); - + module("2D"); - + test("position", function() { var no_getter_setters = !(Crafty.support.setter || Crafty.support.defineProperty); var player = Crafty.e("2D, DOM, Color").attr({w:50, h: 50}).color("red"); player.x += 50; if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(player._x, 50, "X moved"); - + player.y += 50; if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(player._y, 50, "Y moved"); - + player.w += 50; if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(player._w, 100, "Width increase"); - + player.h += 50; if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(player._h, 100, "Height increase"); - + strictEqual(player._globalZ, player[0], "Global Z, Before"); - + player.z = 1; if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(player._z, 1, "Z index"); - + var global_z_guess; if (player[0] < 10) { global_z_guess = parseInt('10000' + player[0], 10); @@ -285,7 +285,7 @@ // Clean up Crafty("*").destroy(); }); - + test("intersect", function() { var no_getter_setters = !(Crafty.support.setter || Crafty.support.defineProperty); var player = Crafty.e("2D, DOM, Color").attr({w:50, h: 50}).color("red"); @@ -312,7 +312,7 @@ player.w = 50; player.h = 50; if (no_getter_setters) { Crafty.trigger("EnterFrame"); } - + strictEqual(player.within(0, 0, 50, 50), true, "Within"); strictEqual(player.within(-1, -1, 51, 51), true, "Within"); @@ -329,7 +329,7 @@ strictEqual(player.within(-50, 0, 50, 50), true, "Rotated, within rotated area"); - + // Clean up Crafty("*").destroy(); }); @@ -342,7 +342,7 @@ player.w = 50; player.h = 50; if (no_getter_setters) { Crafty.trigger("EnterFrame"); } - + strictEqual(player.contains(0, 0, 50, 50), true, "Contains"); @@ -357,25 +357,25 @@ strictEqual(player.contains(0, 0, 50, 50), false, "Rotated, no longer contains"); strictEqual(player.within(-50, 0, 50, 50), true, "Rotated, contains rotated area"); - + // Clean up Crafty("*").destroy(); }); - + test("circle", function() { var no_getter_setters = !(Crafty.support.setter || Crafty.support.defineProperty); var player = Crafty.e("2D, DOM, Color").attr({w:50, h: 50}).color("red"); var circle = new Crafty.circle(0, 0, 10); if (no_getter_setters) { Crafty.trigger("EnterFrame"); } - + strictEqual(circle.containsPoint(1, 2), true, "Contained the point"); strictEqual(circle.containsPoint(8, 9), false, "Didn't contain the point"); - + circle.shift(1, 0); if (no_getter_setters) { Crafty.trigger("EnterFrame"); } - + 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"); @@ -505,9 +505,9 @@ test("Resizing 2D objects & hitboxes", function(){ var no_getter_setters = !(Crafty.support.setter || Crafty.support.defineProperty); var e = Crafty.e("2D, Collision"); - e.attr({x:0, y:0, w:40, h:50}) + e.attr({x:0, y:0, w:40, h:50}) if (no_getter_setters) { Crafty.trigger("EnterFrame"); } - + equal(e.map.points[0][0], 0, "Before rotation: x_0 is 0") equal(e.map.points[0][1], 0, "y_0 is 0") equal(e.map.points[2][0], 40, "x_2 is 40") @@ -550,7 +550,7 @@ equal( Math.round(e._mbr._h), 100, "_mbr._h is 100" ); equal( Math.round(e._mbr._x), -50, "_mbr._x is -50"); equal( Math.round(e._mbr._y), 0, "_mbr._y is 0"); - + e.destroy(); }); @@ -570,15 +570,15 @@ equal(e._debug.strokeStyle, "green", "stroke style set correctly on entity"); e.debugDraw(ctx); - equal(ctx.fillStyle, "#800080", "context.fillStyle set correctly on draw"); // fillStyle will report the hex code - equal(ctx.strokeStyle, "#008000", "context.strokeStyle set correctly on draw"); + equal(ctx.fillStyle, "#800080", "context.fillStyle set correctly on draw"); // fillStyle will report the hex code + equal(ctx.strokeStyle, "#008000", "context.strokeStyle set correctly on draw"); e.debugFill(); equal(e._debug.fillStyle, "red", "default fill style set correctly"); e.debugStroke(); equal(e._debug.strokeStyle, "red", "default stroke style set correctly"); - + e.destroy(); @@ -638,7 +638,7 @@ module("Easing"); test("Crafty.easing duration", function() { var e = new Crafty.easing(80); // 4 frames == 80ms by default - equal(e.duration, 80, "Default duration in ms"); + equal(e.duration, 80, "Default duration in ms"); }) test("Crafty.easing", function() { var e = new Crafty.easing(80); // 4 frames == 80ms by default @@ -651,7 +651,7 @@ e.tick(20); equal( e.value(), 1, "Remains 1 after completion") }) - + test("Tween", function(){ var e = Crafty.e("2D, Tween") e.x = 0; @@ -670,7 +670,7 @@ }); - +

    Crafty: Core

    diff --git a/tests/dom.html b/tests/dom.html index 461efbcc..56414f0d 100644 --- a/tests/dom.html +++ b/tests/dom.html @@ -1,4 +1,4 @@ - @@ -12,7 +12,7 @@ security settings when firefox opens an HTML file saved on your computer; it does not affect your security when you are browsing the internet.] * To run the tests in chrome: You need to pass a flag when opening chrome. - For example, in Linux, you would run this command in a Linux terminal: + For example, in Linux, you would run this command in a Linux terminal: chromium-browser /path/to/core.html --allow-file-access-from-files --> @@ -26,14 +26,14 @@ $(document).ready(function() { Crafty.init(500,500); - + module("DOM"); - + test("avoidCss3dTransforms", function() { var useCss3dTransforms = Crafty.e("2D, DOM") .attr({x: 10, y: 10}) .draw(); - + strictEqual(useCss3dTransforms.avoidCss3dTransforms, false); strictEqual(useCss3dTransforms._cssStyles.transform, "translate3d(10px,10px,0)"); strictEqual(useCss3dTransforms._cssStyles.top, ""); @@ -52,7 +52,7 @@ }); }); - +

    Crafty: Core

    diff --git a/tests/events.html b/tests/events.html index 73168b37..ef0a4d82 100644 --- a/tests/events.html +++ b/tests/events.html @@ -1,4 +1,4 @@ - @@ -12,7 +12,7 @@ security settings when firefox opens an HTML file saved on your computer; it does not affect your security when you are browsing the internet.] * To run the tests in chrome: You need to pass a flag when opening chrome. - For example, in Linux, you would run this command in a Linux terminal: + For example, in Linux, you would run this command in a Linux terminal: chromium-browser /path/to/core.html --allow-file-access-from-files --> @@ -26,9 +26,9 @@ $(document).ready(function() { Crafty.init(500,500); - + module("EVENTS"); - + test("Global binding events", function() { var x = 0; function add(){x++} @@ -36,7 +36,7 @@ Crafty.bind("Increment", add) Crafty.trigger("Increment") strictEqual(x, 1, "Crafty.bind fired once"); - + x = 0; Crafty.unbind("Increment", add) @@ -54,7 +54,7 @@ Crafty.uniqueBind("Increment", add); Crafty.trigger("Increment"); strictEqual(x, 1, "Event bound twice by Crafty.uniqueBound fires only once"); - + x = 0; Crafty.unbind("Increment", add); Crafty.trigger("Increment") @@ -63,7 +63,7 @@ }); test("Entity binding events", function() { - + var x = 0; function add(){x++} var e = Crafty.e("Triggerable") @@ -71,7 +71,7 @@ e.bind("Increment", add) e.trigger("Increment") strictEqual(x, 1, ".bind fired once"); - + x = 0; e.unbind("Increment", add) @@ -89,7 +89,7 @@ e.uniqueBind("Increment", add); e.trigger("Increment"); strictEqual(x, 1, "Event bound twice by .uniqueBound fires only once"); - + x = 0; e.unbind("Increment", add); e.trigger("Increment") @@ -145,13 +145,13 @@ strictEqual(temp.def, 1, "second one() should trigger once"); Crafty.unbind("Event A") - + }); test("Data passing", function(){ var x = 0, e; function add(data){x+=data.amount} - + x = 0; e = Crafty.e("Triggerable") e.bind("Increment", add) @@ -182,10 +182,10 @@ }); - + }); - +

    Crafty: Events

    diff --git a/tests/isometric.html b/tests/isometric.html index 255ba4c4..2c5c789f 100644 --- a/tests/isometric.html +++ b/tests/isometric.html @@ -1,4 +1,4 @@ - @@ -12,7 +12,7 @@ security settings when firefox opens an HTML file saved on your computer; it does not affect your security when you are browsing the internet.] * To run the tests in chrome: You need to pass a flag when opening chrome. - For example, in Linux, you would run this command in a Linux terminal: + For example, in Linux, you would run this command in a Linux terminal: chromium-browser /path/to/core.html --allow-file-access-from-files --> @@ -26,83 +26,83 @@ $(document).ready(function() { Crafty.init(500,500); - + module("ISOMETRIC"); test("place tile", function () { var iso = Crafty.isometric.size(64,16); - + var tile1 = Crafty.e("2D, DOM, Color").attr({x:0, y:0, w:64, h:16}).color("red"); var tile2 = Crafty.e("2D, DOM, Color").attr({x:100, y:100, z:3, w:64, h:16}).color("blue"); - + iso.place(0,0,0,tile1); iso.place(1,2,5,tile2); - + equal(tile1.attr('x'), 0, "First tile should default to origin"); equal(tile1.attr('y'), 0, "First tile should default to origin"); equal(tile1.attr('z'), 0, "z-index should be transferred unchanged"); - + equal(tile2.attr('x'), 64 + Crafty.viewport._x, "Each tile should be offset by the sum of the width of those before it"); equal(tile2.attr('y'), -24 + Crafty.viewport._y, "The row should be offset by one and a half times the height"); equal(tile2.attr('z'), 8, "z-index should be added to existing value"); - + // Clean up Crafty("*").destroy(); }); - + test("pos2px", function () { var iso = Crafty.isometric.size(64,16); - + var origin = iso.pos2px(0,0); equal(origin.left, 0, "First tile should default to origin"); equal(origin.top, 0, "First tile should default to origin"); - + var oddNumberedRow = iso.pos2px(0,1); equal(oddNumberedRow.left, 32, "Odd numbered rows should be be inset by half the width"); equal(oddNumberedRow.top, 8, "Each row should move down by half the height"); - + var evenNumberedRow = iso.pos2px(0,2); equal(evenNumberedRow.left, 0, "Even numbered rows should not be be inset"); equal(evenNumberedRow.top, 16, "Each row should move down by half the height"); - + var numberedColumn = iso.pos2px(3,0); equal(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); equal(origin.x, 0, "Origin should be the corner of the lowest numbered tile"); equal(origin.y, 0, "Origin should be the corner of the lowest numbered tile"); - + var oddNumberedRow = iso.px2pos(32, 8); equal(oddNumberedRow.x, 0, "Odd numbered rows should be be inset by half the width"); equal(oddNumberedRow.y, 1, "Each row should move down by half the height"); - + var evenNumberedRow = iso.px2pos(0,16); equal(evenNumberedRow.x, 0, "Even numbered rows should not be be inset"); equal(evenNumberedRow.y, 2, "Each row should move down by half the height"); - + var numberedColumn = iso.px2pos(128,0); equal(numberedColumn.x, 2, "Should be inset by the width times the x position"); }); - + test("round trip conversions", function () { var iso = Crafty.isometric.size(64,16); - + var startX = 14; var startY = 21; - + var startingPoint = iso.pos2px(startX,startY); var end = iso.px2pos(startingPoint.left, startingPoint.top); - + equal(end.x, startX, "x position should match"); equal(end.y, startY, "y position should match"); }); }); - +

    Crafty: Core

    diff --git a/tests/loader.html b/tests/loader.html index ead04651..04a13615 100644 --- a/tests/loader.html +++ b/tests/loader.html @@ -12,7 +12,7 @@ security settings when firefox opens an HTML file saved on your computer; it does not affect your security when you are browsing the internet.] * To run the tests in chrome: You need to pass a flag when opening chrome. - For example, in Linux, you would run this command in a Linux terminal: + For example, in Linux, you would run this command in a Linux terminal: chromium-browser /path/to/core.html --allow-file-access-from-files --> diff --git a/tests/stage.html b/tests/stage.html index cf25c774..43829410 100644 --- a/tests/stage.html +++ b/tests/stage.html @@ -1,4 +1,4 @@ - @@ -62,7 +62,7 @@ Crafty.viewport.follow(e, 0, 0); equal(Crafty.viewport._x, -100, "Center viewport on entity.x but take clamp into account"); equal(Crafty.viewport._y, -70, "Center viewport on entity.y but take clamp into account"); - + //Make large enough so the viewport won't get clamped Crafty.e("2D, DOM").attr({ x: 0, y: 0, w: Crafty.viewport.width * 2 + 50, h: Crafty.viewport.height*2+35 }); Crafty.viewport.follow(e, 0, 0); @@ -97,7 +97,7 @@ Crafty.timer.simulateFrames(10); equal(Crafty.viewport._x, -e.w / 2 + Crafty.viewport.width / 2, "Entity still centered 10 frames later"); - Crafty.viewport.clampToEntities = false; + Crafty.viewport.clampToEntities = false; var e2 = Crafty.e("2D, DOM").attr({ x: 450, y: 450, w: 20, h: 20 }); Crafty.viewport.scroll('x', 1500); Crafty.viewport.scroll('y', 300); @@ -105,8 +105,8 @@ Crafty.timer.simulateFrames(1); equal(Crafty.viewport._x, Math.floor(-(e2.x + e2.w/2 - Crafty.viewport.width / 2)), "Entity centered from non-zero origin"); equal(Crafty.viewport._y, Math.floor(-(e2.y + e2.h/2 - Crafty.viewport.height / 2)), "Entity centered from non-zero origin"); - - Crafty.viewport.clampToEntities = true; + + Crafty.viewport.clampToEntities = true; Crafty.viewport.scroll('x', 0); Crafty.viewport.scroll('y', 0); }); @@ -122,7 +122,7 @@ lastKnownTime = params.gameTime; } }); - + setTimeout(function () { var endTime = lastKnownTime; ok(endTime > startTime, "EndTime " + endTime + " must be larger than StartTime " + startTime); @@ -133,7 +133,7 @@ }); - +

    Crafty: Stage

    diff --git a/tests/storage.html b/tests/storage.html index 88b982a6..5515888c 100644 --- a/tests/storage.html +++ b/tests/storage.html @@ -1,4 +1,4 @@ - @@ -72,7 +72,7 @@ }); - +

    Crafty: Core

    diff --git a/tests/text.html b/tests/text.html index dae3c237..6e36aa4a 100644 --- a/tests/text.html +++ b/tests/text.html @@ -1,4 +1,4 @@ - @@ -53,7 +53,7 @@ }); - +

    Crafty: Core

    diff --git a/tests/touchevents.html b/tests/touchevents.html index fd8f3089..2525e259 100644 --- a/tests/touchevents.html +++ b/tests/touchevents.html @@ -16,13 +16,13 @@ this.requires('2D, DOM, Color, Mouse, Draggable'); }, }); - + Crafty.scene('main', function() { Crafty.e("Test") .attr({x: 100, y: 100, w:100, h:100}) .color('red'); }); - + Crafty.scene('main'); \ No newline at end of file From e36132ef26134bcf9b09e246768eb14d579254f9 Mon Sep 17 00:00:00 2001 From: DanDanDan Date: Mon, 25 Nov 2013 20:52:43 +0100 Subject: [PATCH 03/61] Fix tweening of same properties by different entities, add tests --- Gruntfile.js | 3 +- src/animation.js | 8 ++---- tests/core.html | 17 ----------- tests/tween.html | 73 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 23 deletions(-) create mode 100644 tests/tween.html diff --git a/Gruntfile.js b/Gruntfile.js index e88c0d86..00d33275 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -93,7 +93,8 @@ module.exports = function (grunt) { 'tests/isometric.html', 'tests/loader.html', 'tests/text.html', - 'tests/dom.html' + 'tests/dom.html', + 'tests/tween.html' ] }, diff --git a/src/animation.js b/src/animation.js index 6fc2a912..96a19a59 100644 --- a/src/animation.js +++ b/src/animation.js @@ -563,12 +563,10 @@ Crafty.c("SpriteAnimation", { * Component to animate the change in 2D properties over time. */ Crafty.c("Tween", { - _step: null, - _numProps: 0, - tweenStart:{}, - tweenGroup:{}, init: function(){ + this.tweenGroup = {}; + this.tweenStart = {}; this.tweens = []; this.bind("EnterFrame", this._tweenTick); @@ -682,4 +680,4 @@ Crafty.c("Tween", { } this.trigger("TweenEnd", properties); } -}); \ No newline at end of file +}); diff --git a/tests/core.html b/tests/core.html index 90bf3400..82428a83 100755 --- a/tests/core.html +++ b/tests/core.html @@ -651,23 +651,6 @@ e.tick(20); equal( e.value(), 1, "Remains 1 after completion") }) - - test("Tween", function(){ - 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 - equal(ret, e, ".tween() returned self correctly"); - Crafty.timer.simulateFrames(5); - equal(Round(e.x), 5, "Halfway tweened x"); - equal(Round(e.y), 13, "Halfway tweened y"); - Crafty.timer.simulateFrames(10); - equal(e.x, 10, "Fully tweened x"); - equal(e.y, 16, "Fully tweened y"); - - }) - - }); diff --git a/tests/tween.html b/tests/tween.html new file mode 100644 index 00000000..af79d761 --- /dev/null +++ b/tests/tween.html @@ -0,0 +1,73 @@ + + + + + + + + + + + + + +

    Crafty: Tween

    +

    +
    +

    +
      +
      test markup, will be hidden
      + + From 51d76eabd0ca99459f195c93a28cd2a6b3b3436d Mon Sep 17 00:00:00 2001 From: Dhruv Kapadia Date: Tue, 3 Dec 2013 17:26:02 -0500 Subject: [PATCH 04/61] Removing a component should actually remove the class from DOM --- src/DOM.js | 15 ++++++++++++++- tests/dom.html | 13 +++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/DOM.js b/src/DOM.js index c1a7fd05..cb447e0d 100644 --- a/src/DOM.js +++ b/src/DOM.js @@ -58,7 +58,20 @@ Crafty.c("DOM", { this._element.className = str; } - this.bind("NewComponent", updateClass).bind("RemoveComponent", updateClass); + function removeClass(removedComponent) { + var i = 0, + c = this.__c, + str = ""; + for (i in c) { + if(i != removedComponent) { + str += ' ' + i; + } + } + str = str.substr(1); + this._element.className = str; + } + + this.bind("NewComponent", updateClass).bind("RemoveComponent", removeClass); if (Crafty.support.prefix === "ms" && Crafty.support.version < 9) { this._filters = {}; diff --git a/tests/dom.html b/tests/dom.html index 461efbcc..25819aad 100644 --- a/tests/dom.html +++ b/tests/dom.html @@ -50,6 +50,19 @@ // Clean up Crafty("*").destroy(); }); + test("removeComponent removes element class", function() { + var element = Crafty.e("DOM"); + hasClassName = function(el, name) { + return el._element.className.indexOf(name) >= 0; + }; + element.addComponent("removeMe"); + strictEqual(element.has("removeMe"), true, "component added"); + strictEqual(hasClassName(element, "removeMe"), true, "classname added"); + + element.removeComponent("removeMe"); + strictEqual(element.has("removeMe"), false, "component removed"); + strictEqual(hasClassName(element, "removeMe"), false, "classname removed"); + }); }); From fbcdcc2bc5ae527b2e4caee00ea3aadb4fb4546c Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Fri, 29 Nov 2013 14:58:59 -0500 Subject: [PATCH 05/61] Bump version to 0.6.0 --- bower.json | 2 +- package.json | 2 +- src/version.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bower.json b/bower.json index 337cca1a..a714aed5 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "crafty", "main": "crafty.js", - "version": "0.5.4", + "version": "0.6", "repository": { "type": "git", "url": "git://github.com/craftyjs/Crafty.git" diff --git a/package.json b/package.json index bbb3f4c4..b1fa5766 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "crafty", - "version": "0.5.4", + "version": "0.6.0", "title": "Crafty game framework", "author": { "name": "Louis Stowasser", diff --git a/src/version.js b/src/version.js index ccea08e1..218121bd 100644 --- a/src/version.js +++ b/src/version.js @@ -1 +1 @@ -module.exports = "0.5.4"; \ No newline at end of file +module.exports = "0.6.0"; \ No newline at end of file From 115bc99387354a42114e9123d5250ab1eda8b898 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Wed, 4 Dec 2013 17:19:31 -0500 Subject: [PATCH 06/61] Allow animation speed to be varied This adds an `animationSpeed` property to "SpriteAnimation", which controls the rate of playback. Includes a couple of tests. --- src/animation.js | 11 ++++++++++- tests/animation/sprite-animation.js | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/animation.js b/src/animation.js index 96a19a59..c86ee6e9 100644 --- a/src/animation.js +++ b/src/animation.js @@ -128,6 +128,14 @@ Crafty.c("SpriteAnimation", { */ _isPlaying: false, + /**@ + * #.animationSpeed + * @comp SpriteAnimation + * + * The playback rate of the animation. This property defaults to 1. + */ + animationSpeed: 1, + init: function () { this._reels = {}; @@ -464,9 +472,10 @@ Crafty.c("SpriteAnimation", { // Bound to "EnterFrame". Progresses the animation by dt, changing the frame if necessary. + // dt is multiplied by the animationSpeed property _animationTick: function(frameData) { var currentReel = this._reels[this._currentReelId]; - currentReel.easing.tick(frameData.dt); + 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); diff --git a/tests/animation/sprite-animation.js b/tests/animation/sprite-animation.js index c0c412cc..28d844eb 100644 --- a/tests/animation/sprite-animation.js +++ b/tests/animation/sprite-animation.js @@ -357,6 +357,24 @@ test("Play an animation where sprites are displayed for more than one frame", fu } }); +test("Play an animation at twice the rate", function(){ + spriteAnimation.animationSpeed = 2; + spriteAnimation.animate('count'); + Crafty.timer.simulateFrames(3); + var activeReel = spriteAnimation.getReel(); + equal(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'); + Crafty.timer.simulateFrames(6); + var activeReel = spriteAnimation.getReel(); + equal(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'); Crafty.timer.simulateFrames(20); From 359f845c726face4a7518dcdc4f9b2ff85447249 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Fri, 15 Nov 2013 14:58:12 -0500 Subject: [PATCH 07/61] Move Crafty.scene into a new separate file Crafty.scene should not be in drawing.js. This commit moves it into a new file, "scene.js". --- package.json | 1 + src/drawing.js | 97 ----------------------------------------------- src/scenes.js | 101 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 97 deletions(-) create mode 100644 src/scenes.js diff --git a/package.json b/package.json index b1fa5766..1df9f7ea 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "src/core.js", "src/HashMap.js", "src/2D.js", + "src/scenes.js", "src/collision.js", "src/DOM.js", "src/html.js", diff --git a/src/drawing.js b/src/drawing.js index dc7cd551..b105a979 100644 --- a/src/drawing.js +++ b/src/drawing.js @@ -204,103 +204,6 @@ Crafty.c("Image", { }); Crafty.extend({ - _scenes: {}, - _current: null, - - /**@ - * #Crafty.scene - * @category Scenes, Stage - * @trigger SceneChange - just before a new scene is initialized - { oldScene:String, newScene:String } - * @trigger SceneDestroy - just before the current scene is destroyed - { newScene:String } - * @sign public void Crafty.scene(String sceneName, Function init[, Function uninit]) - * @param sceneName - Name of the scene to add - * @param init - Function to execute when scene is played - * @param uninit - Function to execute before next scene is played, after entities with `2D` are destroyed - * @sign public void Crafty.scene(String sceneName) - * @param sceneName - Name of scene to play - * - * Method to create scenes on the stage. Pass an ID and function to register a scene. - * - * To play a scene, just pass the ID. When a scene is played, all - * previously-created entities with the `2D` component are destroyed. The - * viewport is also reset. - * - * If you want some entities to persist over scenes (as in, not be destroyed) - * simply add the component `Persist`. - * - * @example - * ~~~ - * Crafty.scene("loading", function() { - * Crafty.background("#000"); - * Crafty.e("2D, DOM, Text") - * .attr({ w: 100, h: 20, x: 150, y: 120 }) - * .text("Loading") - * .css({ "text-align": "center"}) - * .textColor("#FFFFFF"); - * }); - * - * Crafty.scene("UFO_dance", - * function() {Crafty.background("#444"); Crafty.e("UFO");}, - * function() {...send message to server...}); - * ~~~ - * This defines (but does not play) two scenes as discussed below. - * ~~~ - * Crafty.scene("loading"); - * ~~~ - * This command will clear the stage by destroying all `2D` entities (except - * those with the `Persist` component). Then it will set the background to - * black and display the text "Loading". - * ~~~ - * Crafty.scene("UFO_dance"); - * ~~~ - * This command will clear the stage by destroying all `2D` entities (except - * those with the `Persist` component). Then it will set the background to - * gray and create a UFO entity. Finally, the next time the game encounters - * another command of the form `Crafty.scene(scene_name)` (if ever), then the - * game will send a message to the server. - */ - scene: function (name, intro, outro) { - // ---FYI--- - // this._current is the name (ID) of the scene in progress. - // this._scenes is an object like the following: - // {'Opening scene': {'initialize': fnA, 'uninitialize': fnB}, - // 'Another scene': {'initialize': fnC, 'uninitialize': fnD}} - - // If there's one argument, play the scene - if (arguments.length === 1) { - Crafty.trigger("SceneDestroy", { - newScene: name - }); - Crafty.viewport.reset(); - - Crafty("2D").each(function () { - if (!this.has("Persist")) this.destroy(); - }); - // uninitialize previous scene - if (this._current !== null && 'uninitialize' in this._scenes[this._current]) { - this._scenes[this._current].uninitialize.call(this); - } - // initialize next scene - var oldScene = this._current; - this._current = name; - Crafty.trigger("SceneChange", { - oldScene: oldScene, - newScene: name - }); - this._scenes[name].initialize.call(this); - - return; - } - - // If there is more than one argument, add the scene information to _scenes - this._scenes[name] = {}; - this._scenes[name].initialize = intro; - if (typeof outro !== 'undefined') { - this._scenes[name].uninitialize = outro; - } - return; - }, - /**@ * #Crafty.toRGB * @category Graphics diff --git a/src/scenes.js b/src/scenes.js new file mode 100644 index 00000000..e76799fe --- /dev/null +++ b/src/scenes.js @@ -0,0 +1,101 @@ +var Crafty = require('./core.js'), + document = window.document; + +Crafty.extend({ + _scenes: {}, + _current: null, + + /**@ + * #Crafty.scene + * @category Scenes, Stage + * @trigger SceneChange - just before a new scene is initialized - { oldScene:String, newScene:String } + * @trigger SceneDestroy - just before the current scene is destroyed - { newScene:String } + * @sign public void Crafty.scene(String sceneName, Function init[, Function uninit]) + * @param sceneName - Name of the scene to add + * @param init - Function to execute when scene is played + * @param uninit - Function to execute before next scene is played, after entities with `2D` are destroyed + * @sign public void Crafty.scene(String sceneName) + * @param sceneName - Name of scene to play + * + * Method to create scenes on the stage. Pass an ID and function to register a scene. + * + * To play a scene, just pass the ID. When a scene is played, all + * previously-created entities with the `2D` component are destroyed. The + * viewport is also reset. + * + * If you want some entities to persist over scenes (as in, not be destroyed) + * simply add the component `Persist`. + * + * @example + * ~~~ + * Crafty.scene("loading", function() { + * Crafty.background("#000"); + * Crafty.e("2D, DOM, Text") + * .attr({ w: 100, h: 20, x: 150, y: 120 }) + * .text("Loading") + * .css({ "text-align": "center"}) + * .textColor("#FFFFFF"); + * }); + * + * Crafty.scene("UFO_dance", + * function() {Crafty.background("#444"); Crafty.e("UFO");}, + * function() {...send message to server...}); + * ~~~ + * This defines (but does not play) two scenes as discussed below. + * ~~~ + * Crafty.scene("loading"); + * ~~~ + * This command will clear the stage by destroying all `2D` entities (except + * those with the `Persist` component). Then it will set the background to + * black and display the text "Loading". + * ~~~ + * Crafty.scene("UFO_dance"); + * ~~~ + * This command will clear the stage by destroying all `2D` entities (except + * those with the `Persist` component). Then it will set the background to + * gray and create a UFO entity. Finally, the next time the game encounters + * another command of the form `Crafty.scene(scene_name)` (if ever), then the + * game will send a message to the server. + */ + scene: function (name, intro, outro) { + // ---FYI--- + // this._current is the name (ID) of the scene in progress. + // this._scenes is an object like the following: + // {'Opening scene': {'initialize': fnA, 'uninitialize': fnB}, + // 'Another scene': {'initialize': fnC, 'uninitialize': fnD}} + + // If there's one argument, play the scene + if (arguments.length === 1) { + Crafty.trigger("SceneDestroy", { + newScene: name + }); + Crafty.viewport.reset(); + + Crafty("2D").each(function () { + if (!this.has("Persist")) this.destroy(); + }); + // uninitialize previous scene + if (this._current !== null && 'uninitialize' in this._scenes[this._current]) { + this._scenes[this._current].uninitialize.call(this); + } + // initialize next scene + var oldScene = this._current; + this._current = name; + Crafty.trigger("SceneChange", { + oldScene: oldScene, + newScene: name + }); + this._scenes[name].initialize.call(this); + + return; + } + + // If there is more than one argument, add the scene information to _scenes + this._scenes[name] = {}; + this._scenes[name].initialize = intro; + if (typeof outro !== 'undefined') { + this._scenes[name].uninitialize = outro; + } + return; + } +}); \ No newline at end of file From 152209cf5cf8884577647178688a351c6b2f9c90 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Fri, 15 Nov 2013 15:24:53 -0500 Subject: [PATCH 08/61] Extend Crafty.scene to allow passing parameters This commit allows a scene to be called with an optional parameter, which is passed to the init function of the scene. It also adds unit tests for both the new and original functionality of Crafty.scene. --- src/scenes.js | 26 +++++++++++++++++++++++--- tests/core.html | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/scenes.js b/src/scenes.js index e76799fe..c05d4631 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -10,12 +10,15 @@ Crafty.extend({ * @category Scenes, Stage * @trigger SceneChange - just before a new scene is initialized - { oldScene:String, newScene:String } * @trigger SceneDestroy - just before the current scene is destroyed - { newScene:String } + * * @sign public void Crafty.scene(String sceneName, Function init[, Function uninit]) * @param sceneName - Name of the scene to add * @param init - Function to execute when scene is played * @param uninit - Function to execute before next scene is played, after entities with `2D` are destroyed - * @sign public void Crafty.scene(String sceneName) + * + * @sign public void Crafty.scene(String sceneName[, Data]) * @param sceneName - Name of scene to play + * @param Data - The init function of the scene will be called with this data as its parameter. Can be of any type other than a function. * * Method to create scenes on the stage. Pass an ID and function to register a scene. * @@ -23,6 +26,8 @@ Crafty.extend({ * previously-created entities with the `2D` component are destroyed. The * viewport is also reset. * + * You can optionally specify an arugment that will be passed to the scene's init function. + * * If you want some entities to persist over scenes (as in, not be destroyed) * simply add the component `Persist`. * @@ -40,6 +45,16 @@ Crafty.extend({ * Crafty.scene("UFO_dance", * function() {Crafty.background("#444"); Crafty.e("UFO");}, * function() {...send message to server...}); + * + * // An example of an init function which accepts arguments, in this case an object. + * Crafty.scene("square", function(attributes) { + * Crafty.background("#000"); + * Crafty.e("2D, DOM, Color") + * .attr(attributes) + * .color("red"); + * + * }); + * * ~~~ * This defines (but does not play) two scenes as discussed below. * ~~~ @@ -56,6 +71,11 @@ Crafty.extend({ * gray and create a UFO entity. Finally, the next time the game encounters * another command of the form `Crafty.scene(scene_name)` (if ever), then the * game will send a message to the server. + * ~~~ + * Crafty.scene("square", {x:10, y:10, w:20, h:20}); + * ~~~ + * 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) { // ---FYI--- @@ -65,7 +85,7 @@ Crafty.extend({ // 'Another scene': {'initialize': fnC, 'uninitialize': fnD}} // If there's one argument, play the scene - if (arguments.length === 1) { + if (arguments.length === 1 || typeof(arguments[1]) !== "function") { Crafty.trigger("SceneDestroy", { newScene: name }); @@ -85,7 +105,7 @@ Crafty.extend({ oldScene: oldScene, newScene: name }); - this._scenes[name].initialize.call(this); + this._scenes[name].initialize.call(this, arguments[1]); return; } diff --git a/tests/core.html b/tests/core.html index 82428a83..1854a61c 100755 --- a/tests/core.html +++ b/tests/core.html @@ -247,6 +247,46 @@ // Clean up Crafty("*").destroy(); }); + + module("Scenes"); + + test("Scene calling", function(){ + var x = 0; + var sceneInit = function(){ x = 13;} + Crafty.scene("test-call", sceneInit); + Crafty.scene("test-call"); + equal(x, 13, "Scene called succesfully.") + }); + + test("Scene parameters", function(){ + var x = 0; + var paramTaker = function(y){ x = y;} + Crafty.scene("test-param", paramTaker); + Crafty.scene("test-param", 11); + equal(x, 11, "Scene called succesfully with parameter.") + }); + + test("Calling a scene destroys 2D entities", function(){ + var e = Crafty.e("2D"); + var sceneInit = function(){}; + Crafty.scene("test-destroy", sceneInit); + Crafty.scene("test-destroy"); + var l = Crafty("2D").length; + equal(l, 0, "2D entity destroyed on scene change."); + + Crafty("*").destroy(); + }); + + test("Calling a scene doesn't destroy 2D entities with Persist", function(){ + var e = Crafty.e("2D, Persist"); + var sceneInit = function(){}; + Crafty.scene("test-persist", sceneInit); + Crafty.scene("test-persist"); + var l = Crafty("2D").length; + equal(l, 1, "Persist entity remains on scene change."); + + Crafty("*").destroy(); + }); module("2D"); From b5a17305a17434d0c62926a111255de9bf365919 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Wed, 20 Nov 2013 19:59:23 -0500 Subject: [PATCH 09/61] Create .defineScene and .enterScene functions This patch splits the functionality of scene into two functions: defineScene and enterScene. It also adds tests for this functionality, as well as checking uninit. --- src/scenes.js | 113 +++++++++++++++++++++++++++++++++--------------- tests/core.html | 14 ++++++ 2 files changed, 91 insertions(+), 36 deletions(-) diff --git a/src/scenes.js b/src/scenes.js index c05d4631..1fa39ef8 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -15,10 +15,12 @@ Crafty.extend({ * @param sceneName - Name of the scene to add * @param init - Function to execute when scene is played * @param uninit - Function to execute before next scene is played, after entities with `2D` are destroyed + * This is equivalent to calling `Crafty.defineScene`. * * @sign public void Crafty.scene(String sceneName[, Data]) * @param sceneName - Name of scene to play * @param Data - The init function of the scene will be called with this data as its parameter. Can be of any type other than a function. + * This is equivalent to calling `Crafty.enterScene`. * * Method to create scenes on the stage. Pass an ID and function to register a scene. * @@ -33,7 +35,7 @@ Crafty.extend({ * * @example * ~~~ - * Crafty.scene("loading", function() { + * Crafty.defineScene("loading", function() { * Crafty.background("#000"); * Crafty.e("2D, DOM, Text") * .attr({ w: 100, h: 20, x: 150, y: 120 }) @@ -42,12 +44,12 @@ Crafty.extend({ * .textColor("#FFFFFF"); * }); * - * Crafty.scene("UFO_dance", + * Crafty.defineScene("UFO_dance", * function() {Crafty.background("#444"); Crafty.e("UFO");}, * function() {...send message to server...}); * * // An example of an init function which accepts arguments, in this case an object. - * Crafty.scene("square", function(attributes) { + * Crafty.defineScene("square", function(attributes) { * Crafty.background("#000"); * Crafty.e("2D, DOM, Color") * .attr(attributes) @@ -58,13 +60,13 @@ Crafty.extend({ * ~~~ * This defines (but does not play) two scenes as discussed below. * ~~~ - * Crafty.scene("loading"); + * Crafty.enterScene("loading"); * ~~~ * This command will clear the stage by destroying all `2D` entities (except * those with the `Persist` component). Then it will set the background to * black and display the text "Loading". * ~~~ - * Crafty.scene("UFO_dance"); + * Crafty.enterScene("UFO_dance"); * ~~~ * This command will clear the stage by destroying all `2D` entities (except * those with the `Persist` component). Then it will set the background to @@ -72,50 +74,89 @@ Crafty.extend({ * another command of the form `Crafty.scene(scene_name)` (if ever), then the * game will send a message to the server. * ~~~ - * Crafty.scene("square", {x:10, y:10, w:20, h:20}); + * Crafty.enterScene("square", {x:10, y:10, w:20, h:20}); * ~~~ * 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) { + // If there's one argument, or the second argument isn't a function, play the scene + if (arguments.length === 1 || typeof(arguments[1]) !== "function") { + Crafty.enterScene(name, arguments[1]); + return; + } + // Otherwise, this is a call to create a scene + Crafty.defineScene(name, intro, outro); + }, + + /* + * #Crafty.defineScene + * @category Scenes, Stage + * + * @sign public void Crafty.enterScene(String name[, Data]) + * @param name - Name of the scene to run. + * @param Data - The init function of the scene will be called with this data as its parameter. Can be of any type other than a function. + * + * @see Crafty.enterScene + * @see Crafty.scene + */ + defineScene: function(name, init, uninit){ + if (typeof init !== "function") + throw("Init function is the wrong type."); + this._scenes[name] = {}; + this._scenes[name].initialize = init; + if (typeof uninit !== 'undefined') { + this._scenes[name].uninitialize = uninit; + } + return; + + }, + + /* + * #Crafty.enterScene + * @category Scenes, Stage + * @trigger SceneChange - just before a new scene is initialized - { oldScene:String, newScene:String } + * @trigger SceneDestroy - just before the current scene is destroyed - { newScene:String } + * + * @sign public void Crafty.enterScene(String name[, Data]) + * @param name - Name of the scene to run. + * @param Data - The init function of the scene will be called with this data as its parameter. Can be of any type other than a function. + * + * @see Crafty.defineScene + * @see Crafty.scene + */ + 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. // this._scenes is an object like the following: // {'Opening scene': {'initialize': fnA, 'uninitialize': fnB}, // 'Another scene': {'initialize': fnC, 'uninitialize': fnD}} - // If there's one argument, play the scene - if (arguments.length === 1 || typeof(arguments[1]) !== "function") { - Crafty.trigger("SceneDestroy", { - newScene: name - }); - Crafty.viewport.reset(); - - Crafty("2D").each(function () { - if (!this.has("Persist")) this.destroy(); - }); - // uninitialize previous scene - if (this._current !== null && 'uninitialize' in this._scenes[this._current]) { - this._scenes[this._current].uninitialize.call(this); - } - // initialize next scene - var oldScene = this._current; - this._current = name; - Crafty.trigger("SceneChange", { - oldScene: oldScene, - newScene: name - }); - this._scenes[name].initialize.call(this, arguments[1]); + Crafty.trigger("SceneDestroy", { + newScene: name + }); + Crafty.viewport.reset(); - return; + Crafty("2D").each(function () { + if (!this.has("Persist")) this.destroy(); + }); + // uninitialize previous scene + if (this._current !== null && 'uninitialize' in this._scenes[this._current]) { + this._scenes[this._current].uninitialize.call(this); } + // initialize next scene + var oldScene = this._current; + this._current = name; + Crafty.trigger("SceneChange", { + oldScene: oldScene, + newScene: name + }); + this._scenes[name].initialize.call(this, data); + + return; - // If there is more than one argument, add the scene information to _scenes - this._scenes[name] = {}; - this._scenes[name].initialize = intro; - if (typeof outro !== 'undefined') { - this._scenes[name].uninitialize = outro; - } - return; } }); \ No newline at end of file diff --git a/tests/core.html b/tests/core.html index 1854a61c..0659c705 100755 --- a/tests/core.html +++ b/tests/core.html @@ -287,6 +287,20 @@ Crafty("*").destroy(); }); + + + test("Scene uninit function called", function(){ + var x = 0; + var y = 0; + var sceneInit = function(){ x = 13;} + var sceneUninit = function(){ x = 20;} + var sceneGame = function(){ y = 5;} + Crafty.defineScene("test-uninit", sceneInit, sceneUninit); + Crafty.defineScene("game", sceneGame); + Crafty.enterScene("test-uninit"); + Crafty.enterScene("game"); + equal(x, 20, "Uninit scene called successfully when chanced to another scene"); + }); module("2D"); From 41b94129e0f111c60845ac454b21f579d7bfb8c2 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Thu, 5 Dec 2013 23:26:28 -0500 Subject: [PATCH 10/61] Use official syntax for setting CSS properties. --- src/drawing.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/drawing.js b/src/drawing.js index dc7cd551..b9026d75 100644 --- a/src/drawing.js +++ b/src/drawing.js @@ -122,8 +122,8 @@ Crafty.c("Image", { context.restore(); } else if (e.type === "DOM") { if (this.__image) { - e.style["background-image"] = "url(" + this.__image + ")"; - e.style["background-repeat"] = this._repeat; + e.style.backgroundImage = "url(" + this.__image + ")"; + e.style.backgroundRepeat = this._repeat; } } }; From 22e9618df8b13e4097f9980f23ec8de31c504e08 Mon Sep 17 00:00:00 2001 From: Giovanni Cappellotto Date: Fri, 6 Dec 2013 19:00:37 +0100 Subject: [PATCH 11/61] Fix failing DOM test on IE Note: the `avoidCss3dTransforms` property doesn't have any effect on browsers that don't css3dtransform. --- tests/dom.html | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/dom.html b/tests/dom.html index 25819aad..b72e46e1 100644 --- a/tests/dom.html +++ b/tests/dom.html @@ -28,16 +28,22 @@ Crafty.init(500,500); module("DOM"); - + test("avoidCss3dTransforms", function() { var useCss3dTransforms = Crafty.e("2D, DOM") .attr({x: 10, y: 10}) .draw(); strictEqual(useCss3dTransforms.avoidCss3dTransforms, false); - strictEqual(useCss3dTransforms._cssStyles.transform, "translate3d(10px,10px,0)"); - strictEqual(useCss3dTransforms._cssStyles.top, ""); - strictEqual(useCss3dTransforms._cssStyles.left, ""); + if (Crafty.support.css3dtransform) { + strictEqual(useCss3dTransforms._cssStyles.transform, "translate3d(10px,10px,0)"); + strictEqual(useCss3dTransforms._cssStyles.top, ""); + strictEqual(useCss3dTransforms._cssStyles.left, ""); + } else { + strictEqual(avoidCss3dTransforms._cssStyles.transform, ""); + strictEqual(avoidCss3dTransforms._cssStyles.top, 10); + strictEqual(avoidCss3dTransforms._cssStyles.left, 10); + } var avoidCss3dTransforms = Crafty.e("2D, DOM") .attr({x: 10, y: 10, avoidCss3dTransforms: true}) @@ -50,6 +56,7 @@ // Clean up Crafty("*").destroy(); }); + test("removeComponent removes element class", function() { var element = Crafty.e("DOM"); hasClassName = function(el, name) { From bf04e0c00e3e1f390a68e623fdbfd7b83c01d40a Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Fri, 6 Dec 2013 13:43:32 -0500 Subject: [PATCH 12/61] Semantic versioning in bower.js --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index a714aed5..de311677 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "crafty", "main": "crafty.js", - "version": "0.6", + "version": "0.6.0", "repository": { "type": "git", "url": "git://github.com/craftyjs/Crafty.git" From 182b534f8f8ad2f51ed8a57458f75207cacb56c8 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Fri, 6 Dec 2013 16:05:16 -0500 Subject: [PATCH 13/61] Bump version to 0.6.1 --- bower.json | 2 +- package.json | 2 +- src/version.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bower.json b/bower.json index de311677..0ae34de8 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "crafty", "main": "crafty.js", - "version": "0.6.0", + "version": "0.6.1", "repository": { "type": "git", "url": "git://github.com/craftyjs/Crafty.git" diff --git a/package.json b/package.json index 1df9f7ea..442104e7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "crafty", - "version": "0.6.0", + "version": "0.6.1", "title": "Crafty game framework", "author": { "name": "Louis Stowasser", diff --git a/src/version.js b/src/version.js index 218121bd..30bb93b6 100644 --- a/src/version.js +++ b/src/version.js @@ -1 +1 @@ -module.exports = "0.6.0"; \ No newline at end of file +module.exports = "0.6.1"; \ No newline at end of file From 4640b4a0eda7fe36490c5c2563221d637a77d8b0 Mon Sep 17 00:00:00 2001 From: Kevin Simper Date: Fri, 6 Dec 2013 23:09:24 +0100 Subject: [PATCH 14/61] Default actions is not ignored on input and textareas. --- src/controls.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/controls.js b/src/controls.js index 4a76de01..1de0c771 100644 --- a/src/controls.js +++ b/src/controls.js @@ -228,8 +228,14 @@ Crafty.extend({ first.target.dispatchEvent(simulatedEvent); } - if (e.preventDefault) e.preventDefault(); - else e.returnValue = false; + //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.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } + } }, From 970a4669bcbdb8a0f4eb456dff5f714582ad7884 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Fri, 6 Dec 2013 17:44:28 -0500 Subject: [PATCH 15/61] Prevent _clamp from altering bounds It's currently possible to specify the viewport bounds, but the _clamp function will overwrite them. This simply changes how _clamp compares/adjusts the viewport so that the bounds aren't modified. --- src/viewport.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/viewport.js b/src/viewport.js index f9446ee1..6f228f56 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -410,10 +410,8 @@ Crafty.extend({ bound.max.y *= this._zoom; bound.min.y *= this._zoom; if (bound.max.x - bound.min.x > Crafty.viewport.width) { - bound.max.x -= Crafty.viewport.width; - - if (Crafty.viewport.x < -bound.max.x) { - Crafty.viewport.x = -bound.max.x; + if (Crafty.viewport.x < -bound.max.x + Crafty.viewport.width) { + Crafty.viewport.x = -bound.max.x + Crafty.viewport.width; } else if (Crafty.viewport.x > -bound.min.x) { Crafty.viewport.x = -bound.min.x; } @@ -421,10 +419,8 @@ Crafty.extend({ 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) { - bound.max.y -= Crafty.viewport.height; - - if (Crafty.viewport.y < -bound.max.y) { - Crafty.viewport.y = -bound.max.y; + if (Crafty.viewport.y < -bound.max.y + Crafty.viewport.height) { + Crafty.viewport.y = -bound.max.y + Crafty.viewport.height; } else if (Crafty.viewport.y > -bound.min.y) { Crafty.viewport.y = -bound.min.y; } From f505850e06eeb5bf13bba7545b6b6123cead76f8 Mon Sep 17 00:00:00 2001 From: pawurb Date: Mon, 9 Dec 2013 18:54:49 +0100 Subject: [PATCH 16/61] Added checking trailing spaces to jshint. --- Gruntfile.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Gruntfile.js b/Gruntfile.js index f03a5f18..f9428125 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -78,6 +78,7 @@ module.exports = function (grunt) { jshint: { files: ['Gruntfile.js', 'src/**/*.js'], options: { + trailing: true, globals: { } } From 0dcb33e25747f774a3edd97c309e2c817e159d3f Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Tue, 10 Dec 2013 13:20:39 -0500 Subject: [PATCH 17/61] Fix how drawing code handles scale The existing draw code doesn't take into account that the scaling happens before translation. This patch fixes that by multiplying the translation by the current scale. --- src/drawing.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/drawing.js b/src/drawing.js index 6033297a..29e71257 100644 --- a/src/drawing.js +++ b/src/drawing.js @@ -520,7 +520,7 @@ Crafty.DrawManager = (function () { if (dirtyViewport) { var view = Crafty.viewport; - ctx.setTransform(view._scale, 0, 0, view._scale, view.x, view.y); + ctx.setTransform(view._scale, 0, 0, view._scale, view._x*view._scale, view._y*view._scale); } //if the amount of changed objects is over 60% of the total objects @@ -629,8 +629,8 @@ Crafty.DrawManager = (function () { view = Crafty.viewport; style.transform = style[Crafty.support.prefix + "Transform"] = "scale(" + view._scale + ", " + view._scale + ")"; - style.left = view.x + "px"; - style.top = view.y + "px"; + style.left = view.x * view._scale + "px"; + style.top = view.y * view._scale + "px"; style.zIndex = 10; } From 0ad5c872ba1166533f3e4d17872982a4933c1b5b Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Tue, 10 Dec 2013 13:25:14 -0500 Subject: [PATCH 18/61] Rework several viewport animation functions - Changes pan and zoom to work with Crafty.easing. (Time based instead of tick based.) - Makes sure the zoom animation is nice and smooth. (Surprisingly tricky) - Adds a new "StopCamera" event for stopping any extant camera animation (and uses it when a new animation is requested.) - Fires an "CameraAnimationDone" event when a camera animation completes Fixes #163, #285, #339, and #616 --- src/viewport.js | 260 ++++++++++++++++++++++++++--------------------- tests/stage.html | 87 +++++++++++++--- 2 files changed, 215 insertions(+), 132 deletions(-) diff --git a/src/viewport.js b/src/viewport.js index 6f228f56..92b5905c 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -8,6 +8,8 @@ Crafty.extend({ * @trigger ViewportScroll - when the viewport's x or y coordinates change * @trigger ViewportScale - when the viewport's scale changes * @trigger InvalidateViewport - when the viewport changes + * @trigger StopCamera - when any camera animations should stop, such as at the start of a new animation. + * @trigger CameraAnimationDone - when a camera animation comes reaches completion * * Viewport is essentially a 2D camera looking at the stage. Can be moved which * in turn will react just like a camera moving in that direction. @@ -68,7 +70,11 @@ Crafty.extend({ * which are each an object with `x` and `y` properties. * * If this property is null, Crafty uses the bounding box of all the items - * on the stage. This is the initial value. + * on the stage. This is the initial value. (To prevent this behavior, set `Crafty.viewport.clampToEntities` to `false`) + * + * If you wish to bound the viewport along one axis but not the other, you can use `-Infinity` and `+Infinity` as bounds. + * + * @see Crafty.viewport.clampToEntities * * @example * Set the bounds to a 500 by 500 square: @@ -82,9 +88,9 @@ Crafty.extend({ /**@ * #Crafty.viewport.scroll * @comp Crafty.viewport - * @sign Crafty.viewport.scroll(String axis, Number v) + * @sign Crafty.viewport.scroll(String axis, Number val) * @param axis - 'x' or 'y' - * @param v - The new absolute position on the axis + * @param val - The new absolute position on the axis * * Will move the viewport to the position given on the specified axis * @@ -96,69 +102,74 @@ Crafty.extend({ * Crafty.viewport.scroll('_x', 500); * ~~~ */ - scroll: function (axis, v) { - v = Math.floor(v); - this[axis] = v; + scroll: function (axis, val) { + this[axis] = val; Crafty.trigger("ViewportScroll"); Crafty.trigger("InvalidateViewport"); }, rect: function () { return { - _x: -this._x / this._scale, - _y: -this._y / this._scale, + _x: -this._x, + _y: -this._y, _w: this.width / this._scale, _h: this.height / this._scale }; }, - /**@ + /**@ + * #Crafty.viewport.pan * @comp Crafty.viewport * @sign public void Crafty.viewport.pan(String axis, Number v, Number time) * @param String axis - 'x' or 'y'. The axis to move the camera on * @param Number v - the distance to move the camera by - * @param Number time - The duration in frames for the entire camera movement + * @param Number time - The duration in ms for the entire camera movement * - * Pans the camera a given number of pixels over a given number of frames + * Pans the camera a given number of pixels over the specified time */ pan: (function () { var tweens = {}, i, bound = false; + var targetX, targetY, startingX, startingY, easing; function enterFrame(e) { - var l = 0; - for (var i in tweens) { - var prop = tweens[i]; - if (prop.remTime > 0) { - prop.current += prop.diff; - prop.remTime--; - Crafty.viewport[i] = Math.floor(prop.current); - l++; - } else { - delete tweens[i]; - } + 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._clamp(); + + if (easing.complete){ + stopPan(); + Crafty.trigger("CameraAnimationDone"); } - if (l) Crafty.viewport._clamp(); } - return function (axis, v, time) { - Crafty.viewport.follow(); - if (axis == 'reset') { - for (var i in tweens) { - tweens[i].remTime = 0; - } - return; - } - if (time === 0) time = 1; - tweens[axis] = { - diff: -v / time, - current: Crafty.viewport[axis], - remTime: time - }; - if (!bound) { - Crafty.bind("EnterFrame", enterFrame); - bound = true; + function stopPan(){ + Crafty.unbind("EnterFrame", enterFrame); + } + + Crafty.bind("StopCamera", stopPan); + + return function (dx, dy, time) { + // Cancel any current camera control + Crafty.trigger("StopCamera"); + + // Handle request to reset + if (dx == 'reset') { + return; } + + startingX = Crafty.viewport._x; + startingY = Crafty.viewport._y; + targetX = startingX - dx; + targetY = startingY - dy; + + easing = new Crafty.easing(time); + + // bind to event, using uniqueBind prevents multiple copies from being bound + Crafty.uniqueBind("EnterFrame", enterFrame); + }; })(), @@ -188,12 +199,17 @@ Crafty.extend({ Crafty.viewport._clamp(); } - return function (target, offsetx, offsety) { + function stopFollow(){ if (oldTarget) oldTarget.unbind('Change', change); + } + + Crafty.bind("StopCamera", stopFollow); + + return function (target, offsetx, offsety) { if (!target || !target.has('2D')) return; - Crafty.viewport.pan('reset'); + Crafty.trigger("StopCamera"); oldTarget = target; offx = (typeof offsetx != 'undefined') ? offsetx : 0; @@ -209,9 +225,9 @@ Crafty.extend({ * @comp Crafty.viewport * @sign public void Crafty.viewport.centerOn(Object target, Number time) * @param Object target - An entity with the 2D component - * @param Number time - The number of frames to perform the centering over + * @param Number time - The duration in ms of the camera motion * - * Centers the viewport on the given entity + * Centers the viewport on the given entity. */ centerOn: function (targ, time) { var x = targ.x + Crafty.viewport.x, @@ -223,9 +239,7 @@ Crafty.extend({ new_x = x + mid_x - cent_x, new_y = y + mid_y - cent_y; - Crafty.viewport.pan('reset'); - Crafty.viewport.pan('x', new_x, time); - Crafty.viewport.pan('y', new_y, time); + Crafty.viewport.pan(new_x, new_y, time); }, /**@ * #Crafty.viewport._zoom @@ -242,80 +256,87 @@ Crafty.extend({ * @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 * @param Number cent_y - the center to zoom on - * @param Number time - the duration in frames of the entire zoom operation + * @param Number time - the duration in ms of the entire zoom operation * * Zooms the camera in on a given point. amt > 1 will bring the camera closer to the subject - * amt < 1 will bring it farther away. amt = 0 will do nothing. + * amt < 1 will bring it farther away. amt = 0 will reset to the default zoom level * Zooming is multiplicative. To reset the zoom amount, pass 0. */ zoom: (function () { - var zoom = 1, - zoom_tick = 0, - dur = 0, - prop = Crafty.support.prefix + "Transform", - bound = false, - act = {}, - prct = {}; - // what's going on: - // 1. Get the original point as a percentage of the stage - // 2. Scale the stage - // 3. Get the new size of the stage - // 4. Get the absolute position of our point using previous percentage - // 4. Offset inner by that much - - function enterFrame() { - if (dur > 0) { - if (isFinite(Crafty.viewport._zoom)) zoom = Crafty.viewport._zoom; - var old = { - width: act.width * zoom, - height: act.height * zoom - }; - zoom += zoom_tick; - Crafty.viewport._zoom = zoom; - var new_s = { - width: act.width * zoom, - height: act.height * zoom - }, - diff = { - width: new_s.width - old.width, - height: new_s.height - old.height - }; - Crafty.stage.inner.style[prop] = 'scale(' + zoom + ',' + zoom + ')'; - if (Crafty.canvas._canvas) { - var czoom = zoom / (zoom - zoom_tick); - Crafty.canvas.context.scale(czoom, czoom); - Crafty.trigger("InvalidateViewport"); - } - Crafty.viewport.x -= diff.width * prct.width; - Crafty.viewport.y -= diff.height * prct.height; - dur--; + + + function stopZoom(){ + Crafty.unbind("EnterFrame", enterFrame); + } + Crafty.bind("StopCamera", stopZoom); + + var startingZoom, finalZoom, finalAmount, startingX, finalX, startingY, finalY, easing; + + function enterFrame(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() ); + + // 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 + // (a_0-b_0)/(a-b) = amount, + // and the assumption that both a and b have the same form + // a = a_0 * (1-v) + a_f * v, + // 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); + + // 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._clamp(); + + if (easing.complete){ + stopZoom(); + Crafty.trigger("CameraAnimationDone"); } + + } - return function (amt, cent_x, cent_y, time) { - var bounds = this.bounds || Crafty.map.boundaries(), - final_zoom = amt ? zoom * amt : 1; + return function (amt, cent_x, cent_y, time){ if (!amt) { // we're resetting to defaults - zoom = 1; - this._zoom = 1; + Crafty.viewport.scale(1); + return; + } + + if (arguments.length <= 2) { + time = cent_x; + cent_x = Crafty.viewport.x - Crafty.viewport.width; + cent_y = Crafty.viewport.y - Crafty.viewport.height; } - act.width = bounds.max.x - bounds.min.x; - act.height = bounds.max.y - bounds.min.y; + Crafty.trigger("StopCamera"); + startingZoom = Crafty.viewport._zoom; + finalAmount = amt; + finalZoom = startingZoom * finalAmount; + - prct.width = cent_x / act.width; - prct.height = cent_y / act.height; + startingX = Crafty.viewport.x; + startingY = Crafty.viewport.y; + finalX = - (cent_x - Crafty.viewport.width / (2 * finalZoom) ); + finalY = - (cent_y - Crafty.viewport.height / (2 * finalZoom) ); - if (time === 0) time = 1; - zoom_tick = (final_zoom - zoom) / time; - dur = time; + easing = new Crafty.easing(time); - Crafty.viewport.pan('reset'); - if (!bound) { - Crafty.bind('EnterFrame', enterFrame); - bound = true; - } + Crafty.uniqueBind("EnterFrame", enterFrame); }; + + })(), /**@ * #Crafty.viewport.scale @@ -323,9 +344,12 @@ Crafty.extend({ * @sign public void Crafty.viewport.scale(Number amt) * @param Number amt - amount to zoom/scale in on the element on the viewport by (eg. 2, 4, 0.5) * - * Zooms/scale the camera. amt > 1 increase all entities on stage + * Adjusts the. amt > 1 increase all entities on stage * amt < 1 will reduce all entities on stage. amt = 0 will reset the zoom/scale. - * Zooming/scaling is multiplicative. To reset the zoom/scale amount, pass 0. + * To reset the scale amount, pass 0. + * + * This method sets the absolute scale, while `Crafty.viewport.zoom` sets the scale relative to the existing value. + * @see Crafty.viewport.zoom * * @example * ~~~ @@ -334,9 +358,7 @@ Crafty.extend({ */ scale: (function () { return function (amt) { - var bounds = this.bounds || Crafty.map.boundaries(), - final_zoom = amt ? amt : 1; - + var final_zoom = amt ? amt : 1; this._zoom = final_zoom; this._scale = final_zoom; @@ -354,12 +376,17 @@ Crafty.extend({ * Toggle mouselook on the current viewport. * Simply call this function and the user will be able to * drag the viewport around. + * + * If the user starts a drag, "StopCamera" will be triggered, which will cancel any existing camera animations. */ mouselook: (function () { var active = false, dragging = false, lastMouse = {}; old = {}; + function stopLook(){ + dragging = false; + } return function (op, arg) { @@ -390,6 +417,7 @@ Crafty.extend({ Crafty.viewport._clamp(); break; case 'start': + Crafty.trigger("StopCamera"); lastMouse.x = arg.clientX; lastMouse.y = arg.clientY; dragging = true; @@ -691,17 +719,17 @@ Crafty.extend({ /**@ * #Crafty.viewport.reset * @comp Crafty.stage + * @trigger StopCamera - called to cancel camera animations * * @sign public Crafty.viewport.reset() * - * Resets the viewport to starting values + * Resets the viewport to starting values, and cancels any existing camera animations. * Called when scene() is run. */ reset: function () { - Crafty.viewport.pan('reset'); - Crafty.viewport.follow(); - Crafty.viewport.mouselook('stop'); - Crafty.viewport.scale(); + Crafty.viewport.mouselook("stop"); + Crafty.trigger("StopCamera"); + Crafty.viewport.scale(1); } } }); diff --git a/tests/stage.html b/tests/stage.html index cf25c774..71de9860 100644 --- a/tests/stage.html +++ b/tests/stage.html @@ -11,6 +11,15 @@ $(document).ready(function () { Crafty.init(); + var reset = function(){ + Crafty("*").destroy(); + Crafty.viewport.reset(); + Crafty.viewport.scroll('_x', 0); + Crafty.viewport.scroll('_y', 0); + Crafty.viewport.clampToEntities = true; + + } + test("simulateFrames", function() { var framesPlayed = 0; Crafty.bind("EnterFrame", function () { @@ -23,7 +32,10 @@ equal(framesPlayed, 101, "101 frames should have been simulated"); }) - module("Viewport"); + + + + module("Viewport", {setup:reset}); test("scroll using _x, _y", function () { var e = Crafty.e("2D, DOM").attr({ x: 50, y: 50 }); @@ -58,53 +70,96 @@ }); test("follow", function () { + Crafty.viewport.clampToEntities = false; var e = Crafty.e("2D, DOM").attr({ x: Crafty.viewport.width + 100, y: Crafty.viewport.height + 70 }); Crafty.viewport.follow(e, 0, 0); - equal(Crafty.viewport._x, -100, "Center viewport on entity.x but take clamp into account"); - equal(Crafty.viewport._y, -70, "Center viewport on entity.y but take clamp into account"); - - //Make large enough so the viewport won't get clamped - Crafty.e("2D, DOM").attr({ x: 0, y: 0, w: Crafty.viewport.width * 2 + 50, h: Crafty.viewport.height*2+35 }); - Crafty.viewport.follow(e, 0, 0); - equal(Crafty.viewport._x, Math.floor(-(Crafty.viewport.width/2 + 100)), "Center viewport on entity.x"); - equal(Crafty.viewport._y, Math.floor(-(Crafty.viewport.height / 2 + 70)), "Center viewport on entity.y"); + equal(Crafty.viewport._x, (-(Crafty.viewport.width/2 + 100)), "Center viewport on entity.x"); + equal(Crafty.viewport._y, (-(Crafty.viewport.height / 2 + 70)), "Center viewport on entity.y"); - Crafty.viewport.scroll('x', 0); - Crafty.viewport.scroll('y', 0); }); test("pan", function () { + Crafty.viewport.clampToEntities = false; Crafty.e("2D, DOM").attr({ x: 0, y: 0, w: Crafty.viewport.width * 2, h: Crafty.viewport.height * 2 }); - Crafty.viewport.pan("x", 100, 10); + + var done = 0; + var panDone = function(){ done++ }; + Crafty.one("CameraAnimationDone", panDone); + + Crafty.viewport.pan(100, 0, 10 * 20); Crafty.timer.simulateFrames(5); equal(Crafty.viewport._x, -50, "Pan half the way on half the time"); + equal(done, 0, "CameraAnimationDone hasn't fired yet"); Crafty.timer.simulateFrames(5); equal(Crafty.viewport._x, -100, "Pan all the way when all the time is spent"); + equal(done, 1, "CameraAnimationDone has fired once"); - Crafty.viewport.pan("y", 100, 10); + done = 0; + Crafty.one("CameraAnimationDone", panDone); + Crafty.viewport.pan(0, 100, 10); Crafty.timer.simulateFrames(20); equal(Crafty.viewport._y, -100, "Pan all the way and stay there"); + equal(done, 1, "CameraAnimationDone has fired once"); + + Crafty.viewport.scroll('x', 0); + Crafty.viewport.scroll('y', 0); + }); + + test("zoom", function () { + + Crafty.viewport.clampToEntities = false; + + var done = 0; + var panDone = function(){ done++ }; + Crafty.one("CameraAnimationDone", panDone); + Crafty.e("2D, DOM").attr({ x: 0, y: 0, w: Crafty.viewport.width * 2, h: Crafty.viewport.height * 2 }); Crafty.viewport.scroll('x', 0); Crafty.viewport.scroll('y', 0); + Crafty.viewport.scale(1); + + Crafty.viewport.zoom(2, 0, 0, 10 * 20); + Crafty.timer.simulateFrames(5); + + equal(Crafty.viewport._scale, Math.sqrt(2), "Zooms sqrt(2) in half the time"); + equal(done, 0, "CameraAnimationDone hasn't fired yet"); + + Crafty.timer.simulateFrames(5); + equal(Crafty.viewport._x, Crafty.viewport.width/4, "move all the way when all the time is spent"); + equal(Crafty.viewport._y, Crafty.viewport.height/4, "move all the way when all the time is spent"); + equal(Crafty.viewport._scale, 2, "Zooms all the way in full time."); + equal(done, 1, "CameraAnimationDone has fired once"); + }); test("centerOn", function () { var e = Crafty.e("2D, DOM").attr({ x: 0, y: 0, w: Crafty.viewport.width * 2, h: Crafty.viewport.height * 2 }); + Crafty.viewport.clampToEntities = false; + + var done = 0; + var panDone = function(){ done++ }; + Crafty.one("CameraAnimationDone", panDone); + + Crafty.viewport.centerOn(e, 10); Crafty.timer.simulateFrames(10); + equal(Crafty.viewport._x, -e.w / 2 + Crafty.viewport.width / 2, "Entity centered after exact duration"); + equal(done, 1, "CameraAnimationDone has fired once"); + done = 0; + Crafty.one("CameraAnimationDone", panDone); Crafty.timer.simulateFrames(10); equal(Crafty.viewport._x, -e.w / 2 + Crafty.viewport.width / 2, "Entity still centered 10 frames later"); + equal(done, 0, "CameraAnimationDone doesn't fire after completion"); - Crafty.viewport.clampToEntities = false; + var e2 = Crafty.e("2D, DOM").attr({ x: 450, y: 450, w: 20, h: 20 }); Crafty.viewport.scroll('x', 1500); Crafty.viewport.scroll('y', 300); Crafty.viewport.centerOn(e2, 1); Crafty.timer.simulateFrames(1); - equal(Crafty.viewport._x, Math.floor(-(e2.x + e2.w/2 - Crafty.viewport.width / 2)), "Entity centered from non-zero origin"); - equal(Crafty.viewport._y, Math.floor(-(e2.y + e2.h/2 - Crafty.viewport.height / 2)), "Entity centered from non-zero origin"); + equal(Crafty.viewport._x, (-(e2.x + e2.w/2 - Crafty.viewport.width / 2)), "Entity centered from non-zero origin"); + equal(Crafty.viewport._y, (-(e2.y + e2.h/2 - Crafty.viewport.height / 2)), "Entity centered from non-zero origin"); Crafty.viewport.clampToEntities = true; Crafty.viewport.scroll('x', 0); From cf09928a34eda07755ee006861cf8a8635e24d05 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Tue, 10 Dec 2013 22:19:31 -0500 Subject: [PATCH 19/61] Add support for line-height and variant to the Text component These work using DOM, but not canvas. The textFont method now covers all the possible parts of the [font declaration.](https://developer.mozilla.org/en-US/docs/Web/CSS/font). Fixes #494. --- src/text.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/text.js b/src/text.js index 89c5540c..2cdb0b9b 100644 --- a/src/text.js +++ b/src/text.js @@ -28,6 +28,8 @@ Crafty.c("Text", { _text: "", defaultSize: "10px", defaultFamily: "sans-serif", + defaultVariant: "normal", + defaultLineHeight: "normal", ready: true, init: function () { @@ -36,7 +38,9 @@ Crafty.c("Text", { "type": "", "weight": "", "size": this.defaultSize, - "family": this.defaultFamily + "lineHeight":this.defaultLineHeight, + "family": this.defaultFamily, + "variant": this.defaultVariant }; this.bind("Draw", function (e) { @@ -143,9 +147,8 @@ Crafty.c("Text", { // Returns the font string to use _fontString: function(){ - return this._textFont.type + ' ' + this._textFont.weight + ' ' + this._textFont.size + ' ' + this._textFont.family; + return this._textFont.type + ' ' + this._textFont.variant + ' ' + this._textFont.weight + ' ' + this._textFont.size + ' / ' + this._textFont.lineHeight + ' ' + this._textFont.family; }, - /**@ * #.textColor * @comp Text @@ -183,7 +186,9 @@ Crafty.c("Text", { * @sign public this .textFont(Object map) * @param map - Object where the key is the property to modify and the value as the property value * - * Use this method to set font property of the text entity. + * Use this method to set font property of the text entity. Possible values are: type, weight, size, family, lineHeight, and variant. + * + * When rendered by the canvas, lineHeight and variant will be ignored. * * @example * ~~~ From 45f881f667fa61a17e1fe9af170942293e39dd08 Mon Sep 17 00:00:00 2001 From: Henio Date: Sun, 8 Dec 2013 19:39:56 -0300 Subject: [PATCH 20/61] Twoway requires Gravity; Gravity and Twoway trigger 'Moved' events --- src/2D.js | 3 +++ src/controls.js | 9 +++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/2D.js b/src/2D.js index a4c4bf0f..aff000ef 100644 --- a/src/2D.js +++ b/src/2D.js @@ -1012,6 +1012,8 @@ Crafty.c("2D", { /**@ * #Gravity * @category 2D + * @trigger Moved - When entity has moved on y-axis a Moved event is triggered with an object specifying the old position {x: old_x, y: old_y} + * * Adds gravitational pull to the entity. */ Crafty.c("Gravity", { @@ -1079,6 +1081,7 @@ Crafty.c("Gravity", { //if falling, move the players Y this._gy += this._gravityConst; this.y += this._gy; + this.trigger('Moved', { x: this._x, y: this._y - this._gy }); } else { this._gy = 0; //reset change in y } diff --git a/src/controls.js b/src/controls.js index 1de0c771..e564e477 100644 --- a/src/controls.js +++ b/src/controls.js @@ -871,17 +871,17 @@ Crafty.c("Fourway", { /**@ * #Twoway * @category Input + * @trigger NewDirection - When direction changes a NewDirection event is triggered with an object detailing the new direction: {x: x_movement, y: y_movement}. This is consistent with Fourway and Multiway components. + * @trigger Moved - When entity has moved on x-axis a Moved event is triggered with an object specifying the old position {x: old_x, y: old_y} + * * Move an entity left or right using the arrow keys or `D` and `A` and jump using up arrow or `W`. - * - * When direction changes a NewDirection event is triggered with an object detailing the new direction: {x: x_movement, y: y_movement}. This is consistent with Fourway and Multiway components. - * When entity has moved on x-axis a Moved event is triggered with an object specifying the old position {x: old_x, y: old_y} */ Crafty.c("Twoway", { _speed: 3, _up: false, init: function () { - this.requires("Fourway, Keyboard"); + this.requires("Fourway, Keyboard, Gravity"); }, /**@ @@ -925,6 +925,7 @@ Crafty.c("Twoway", { if (this._up) { this.y -= this._jumpSpeed; this._falling = true; + this.trigger('Moved', { x: this._x, y: this._y + this._jumpSpeed }); } }).bind("KeyDown", function (e) { if (e.key === Crafty.keys.UP_ARROW || e.key === Crafty.keys.W || e.key === Crafty.keys.Z) From e48de66fed471b543802aecb4eb873637365b135 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Tue, 10 Dec 2013 19:57:33 -0500 Subject: [PATCH 21/61] Add get() method to crafty core This adds a get method that will convert selections to arrays of entities. It can also be used to fetch a particular specified entity by index. It will work as expected an a single entity. This is modeled directly after jquery's get method: http://api.jquery.com/jQuery.get/ Documentation about the new feature is added (it is now referenced by the Crafty Core docs, as well as .toArray). Includes a few tests added to core.html. This fixes #349 --- src/core.js | 58 +++++++++++++++++++++++++++++++++++++++++++++- tests/core.html | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/src/core.js b/src/core.js index f89b358c..c6da81bc 100644 --- a/src/core.js +++ b/src/core.js @@ -27,6 +27,14 @@ var version = require('./version'); * Crafty(1) * ~~~ * Passing an integer will select the entity with that `ID`. + * + * To work directly with an array of entities, use the `get()` method on a selection. + * To call a function in the context of each entity, use the `.each()` method. + * + * The event related methods such as `bind` and `trigger` will work on selections of entities. + * + * @see .get + * @see .each */ var Crafty = function (selector) { @@ -452,7 +460,8 @@ Crafty.fn = Crafty.prototype = { * @comp Crafty Core * @sign public this .toArray(void) * - * This method will simply return the found entities as an array. + * 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 () { return slice.call(this, 0); @@ -711,6 +720,53 @@ Crafty.fn = Crafty.prototype = { return this; }, + /**@ + * #.get + * @comp Crafty Core + * @sign public Array .get() + * @returns An array of entities corresponding to the active selector + * + * @sign public Entity .get(Number index) + * @returns an entity belonging to the current selection + * @param index - The index of the entity to return. If negative, counts back from the end of the array. + * + * + * @example + * Get an array containing every "2D" entity + * ~~~ + * var arr = Crafty("2D").get() + * ~~~ + * Get the first entity matching the selector + * ~~~ + * // equivalent to Crafty("2D").get()[0], but doesn't create a new array + * var e = Crafty("2D").get(0) + * ~~~ + * Get the last "2D" entity matching the selector + * ~~~ + * var e = Crafty("2D").get(-1) + * ~~~ + * + */ + 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]]; + } else { + var i=0, result = []; + for (; i < l; i++) { + //skip if not exists + if (!entities[this[i]]) continue; + result.push( entities[this[i]] ); + } + return result; + } + }, + /**@ * #.clone * @comp Crafty Core diff --git a/tests/core.html b/tests/core.html index 0659c705..ae235675 100755 --- a/tests/core.html +++ b/tests/core.html @@ -217,6 +217,67 @@ // Clean up Crafty("*").destroy(); }); + + test("Crafty.get() to find an array", function(){ + Crafty.e("test"); + Crafty.e("test"); + Crafty.e("test"); + + var collection = Crafty("test"); + var result = collection.get(); + equal(result.length, 3, "resultant array should be length 3"); + equal(result[0].has("test"), true, "Result elements should have correct component"); + equal(collection[0], result[0].getId(), "First id of result should match first id of Crafty array"); + + Crafty("*").destroy(); + }) + + test("Crafty.get(index) to find the indicated entity", function(){ + Crafty.e("test"); + Crafty.e("test"); + Crafty.e("test"); + var collection, result; + + collection = Crafty("test"); + result = collection.get(0); + equal(result.has("test"), true, "Result should have correct component"); + equal(result.getId(), collection[0], "result should be first element of collection"); + + result = collection.get(-1); + equal(result.has("test"), true, "Result should have correct component"); + equal(result.getId(), collection[2], "result should be last element of collection"); + + Crafty("*").destroy(); + }) + + test("Crafty.get(index) error checking", function(){ + Crafty.e("test"); + Crafty.e("test"); + Crafty.e("test"); + var collection, result; + + collection = Crafty("test"); + + result = collection.get(3); + equal(typeof result, "undefined", "result of get(3) should be undefined") + + result = collection.get(-4); + equal(typeof result, "undefined", "result of get(-4) should be undefined") + + + + Crafty("*").destroy(); + }) + + test("Crafty.get with only one object", function(){ + var e = Crafty.e("test"); + var collection = Crafty("test"); + result = collection.get(0); + equal(result.getId(), e.getId(), "result of get(0) is correct entity") + result = collection.get(); + equal(result.length, 1, "result of get() is array of length 1"); + Crafty("*").destroy(); + }) test("requires", function() { var first = Crafty.e("test"); From 7d0e4d2052cb439b130b431ca32e632046dc98b8 Mon Sep 17 00:00:00 2001 From: Giovanni Cappellotto Date: Sat, 14 Dec 2013 17:51:37 +0100 Subject: [PATCH 22/61] Fix example indentation and code style --- src/controls.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/controls.js b/src/controls.js index e564e477..ff47b6ca 100644 --- a/src/controls.js +++ b/src/controls.js @@ -252,14 +252,14 @@ Crafty.extend({ * .attr({x: 100, y: 100, w: 50, h: 50}) * .color("red") * .bind('KeyDown', function(e) { - * if(e.key == Crafty.keys['LEFT_ARROW']) { - * this.x=this.x-1; - * } else if (e.key == Crafty.keys['RIGHT_ARROW']) { - * this.x=this.x+1; - * } else if (e.key == Crafty.keys['UP_ARROW']) { - * this.y=this.y-1; - * } else if (e.key == Crafty.keys['DOWN_ARROW']) { - * this.y=this.y+1; + * if(e.key == Crafty.keys.LEFT_ARROW) { + * this.x = this.x-1; + * } else if (e.key == Crafty.keys.RIGHT_ARROW) { + * this.x = this.x+1; + * } else if (e.key == Crafty.keys.UP_ARROW) { + * this.y = this.y-1; + * } else if (e.key == Crafty.keys.DOWN_ARROW) { + * this.y = this.y+1; * } * }); * ~~~ From 1054abe3addaf8db6a6703f8c4c464907703b9c0 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Tue, 17 Dec 2013 23:10:42 -0500 Subject: [PATCH 23/61] Remove getter/setter ie8 fallbacks --- src/2D.js | 82 ------------------------------------------------- src/core.js | 28 +---------------- src/viewport.js | 13 -------- 3 files changed, 1 insertion(+), 122 deletions(-) diff --git a/src/2D.js b/src/2D.js index aff000ef..2a598c6c 100644 --- a/src/2D.js +++ b/src/2D.js @@ -318,81 +318,6 @@ Crafty.c("2D", { }); }, - _defineGetterSetter_fallback: function () { - //set the public properties to the current private properties - this.x = this._x; - this.y = this._y; - this.w = this._w; - this.h = this._h; - this.z = this._z; - this.rotation = this._rotation; - this.alpha = this._alpha; - this.visible = this._visible; - - //on every frame check for a difference in any property - this.bind("EnterFrame", function () { - //if there are differences between the public and private properties - if (this.x !== this._x || this.y !== this._y || - this.w !== this._w || this.h !== this._h || - this.z !== this._z || this.rotation !== this._rotation || - this.alpha !== this._alpha || this.visible !== this._visible) { - - //save the old positions - var old = Crafty._rectPool.copy(this); - - //if rotation has changed, use the private rotate method - if (this.rotation !== this._rotation) { - this._rotate(this.rotation); - } else { - //update the MBR - var mbr = this._mbr, - moved = false; - // If the browser doesn't have getters or setters, - // {x, y, w, h, z} and {_x, _y, _w, _h, _z} may be out of sync, - // in which case t checks if they are different on tick and executes the Change event. - if (mbr) { //check each value to see which has changed - if (this.x !== this._x) { - mbr._x -= this.x - this._x; - moved = true; - } else if (this.y !== this._y) { - mbr._y -= this.y - this._y; - moved = true; - } else if (this.w !== this._w) { - mbr._w -= this.w - this._w; - moved = true; - } else if (this.h !== this._h) { - mbr._h -= this.h - this._h; - moved = true; - } else if (this.z !== this._z) { - mbr._z -= this.z - this._z; - moved = true; - } - } - - //if the moved flag is true, trigger a move - if (moved) this.trigger("Move", old); - } - - //set the public properties to the private properties - this._x = this.x; - this._y = this.y; - this._w = this.w; - this._h = this.h; - this._z = this.z; - this._rotation = this.rotation; - this._alpha = this.alpha; - this._visible = this.visible; - - //trigger the changes - this.trigger("Change", old); - //without this entities weren't added correctly to Crafty.map.map in IE8. - //not entirely sure this is the best way to fix it though - this.trigger("Move", old); - Crafty._rectPool.recycle(old); - } - }); - }, - init: function () { this._globalZ = this[0]; this._origin = { @@ -406,13 +331,6 @@ Crafty.c("2D", { } else if (Crafty.support.defineProperty) { //IE9 supports Object.defineProperty this._defineGetterSetter_defineProperty(); - } else { - /* - If no setters and getters are supported (e.g. IE8) supports, - check on every frame for a difference between this._(x|y|w|h|z...) - and this.(x|y|w|h|z) and update accordingly. - */ - this._defineGetterSetter_fallback(); } //insert self into the HashMap diff --git a/src/core.js b/src/core.js index f89b358c..03605781 100644 --- a/src/core.js +++ b/src/core.js @@ -34,7 +34,7 @@ var Crafty = function (selector) { }, // Internal variables GUID, frame, components, entities, handlers, onloads, - noSetter, slice, rlist, rspace, milliSecPerFrame; + slice, rlist, rspace, milliSecPerFrame; initState = function () { @@ -747,8 +747,6 @@ Crafty.fn = Crafty.prototype = { * Will watch a property waiting for modification and will then invoke the * given callback when attempting to modify. * - * *Note: Support in IE<9 is slightly different. The method will be executed - * after the property has been set* */ setter: function (prop, callback) { if (Crafty.support.setter) { @@ -758,12 +756,6 @@ Crafty.fn = Crafty.prototype = { set: callback, configurable: true }); - } else { - noSetter.push({ - prop: prop, - obj: this, - fn: callback - }); } return this; }, @@ -1573,24 +1565,6 @@ function clone(obj) { return temp; } -Crafty.bind("Load", function () { - if (!Crafty.support.setter && Crafty.support.defineProperty) { - noSetter = []; - Crafty.bind("EnterFrame", function () { - var i = 0, - l = noSetter.length, - current; - for (; i < l; ++i) { - current = noSetter[i]; - if (current.obj[current.prop] !== current.obj['_' + current.prop]) { - current.fn.call(current.obj, current.obj[current.prop]); - } - } - }); - } -}); - - // export Crafty if (typeof define === 'function') { // AMD define('crafty', [], function () { diff --git a/src/viewport.js b/src/viewport.js index 92b5905c..78474d3c 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -665,19 +665,6 @@ Crafty.extend({ }, configurable : true }); - } else { - // IE8 has no getter/setters -- Check for an update each frame. - this.x = this._x; - this.y = this._y; - Crafty.bind("EnterFrame", function () { - if (Crafty.viewport._x !== Crafty.viewport.x) { - Crafty.viewport.scroll('_x', Crafty.viewport.x); - } - - if (Crafty.viewport._y !== Crafty.viewport.y) { - Crafty.viewport.scroll('_y', Crafty.viewport.y); - } - }); } }, From 18ea5abbddc3dc020a7184bebc8e23480c53dd2b Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Tue, 17 Dec 2013 23:14:43 -0500 Subject: [PATCH 24/61] Remove old IE specific filter code from DOM.js --- src/DOM.js | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/src/DOM.js b/src/DOM.js index cb447e0d..390e3733 100644 --- a/src/DOM.js +++ b/src/DOM.js @@ -185,17 +185,6 @@ Crafty.c("DOM", { style[prefix + "Opacity"] = this._alpha; } - //if not version 9 of IE - if (prefix === "ms" && Crafty.support.version < 9) { - //for IE version 8, use ImageTransform filter - if (Crafty.support.version === 8) { - this._filters.alpha = "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + (this._alpha * 100) + ")"; // first! - //all other versions use filter - } else { - this._filters.alpha = "alpha(opacity=" + (this._alpha * 100) + ")"; - } - } - if (this._mbr) { var origin = this._origin.x + "px " + this._origin.y + "px"; style.transformOrigin = origin; @@ -206,21 +195,10 @@ Crafty.c("DOM", { if (this._flipX) { trans.push("scaleX(-1)"); - if (prefix === "ms" && Crafty.support.version < 9) { - this._filters.flipX = "fliph"; - } } if (this._flipY) { trans.push("scaleY(-1)"); - if (prefix === "ms" && Crafty.support.version < 9) { - this._filters.flipY = "flipv"; - } - } - - //apply the filters if IE - if (prefix === "ms" && Crafty.support.version < 9) { - this.applyFilters(); } if (this._cssStyles.transform != trans.join(" ")) { @@ -238,18 +216,6 @@ Crafty.c("DOM", { return this; }, - applyFilters: function () { - this._element.style.filter = ""; - var str = ""; - - for (var filter in this._filters) { - if (!this._filters.hasOwnProperty(filter)) continue; - str += this._filters[filter] + " "; - } - - this._element.style.filter = str; - }, - /**@ * #.undraw * @comp DOM From 3a1bd26cf2205485fe34142f63a6476ac8bb61bb Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Tue, 17 Dec 2013 23:15:34 -0500 Subject: [PATCH 25/61] Remove IE6 background hack --- src/DOM.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/DOM.js b/src/DOM.js index 390e3733..4ca96707 100644 --- a/src/DOM.js +++ b/src/DOM.js @@ -290,12 +290,6 @@ Crafty.c("DOM", { } }); -/** - * Fix IE6 background flickering - */ -try { - document.execCommand("BackgroundImageCache", false, true); -} catch (e) {} Crafty.extend({ /**@ From 0b44d552bc9f95c15b7a007be4669647b1404f87 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Tue, 17 Dec 2013 23:20:22 -0500 Subject: [PATCH 26/61] Remove documentation note about ie8 --- src/drawing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drawing.js b/src/drawing.js index 29e71257..83171738 100644 --- a/src/drawing.js +++ b/src/drawing.js @@ -32,7 +32,7 @@ Crafty.c("Color", { * Will create a rectangle of solid color for the entity, or return the color if no argument is given. * * The argument must be a color readable depending on which browser you - * choose to support. IE 8 and below doesn't support the rgb() syntax. + * choose to support. * * @example * ~~~ From 8d3bfefc27cac103ef7b1ec88295f6f46701c2f2 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Tue, 17 Dec 2013 23:28:55 -0500 Subject: [PATCH 27/61] Remove IE8 specific rotation code and setup --- src/2D.js | 8 +------- src/DOM.js | 15 --------------- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/src/2D.js b/src/2D.js index 2a598c6c..beed4ef9 100644 --- a/src/2D.js +++ b/src/2D.js @@ -457,13 +457,7 @@ Crafty.c("2D", { sin: Math.sin(drad), deg: difference, rad: drad, - o: o, - matrix: { - M11: ct, - M12: st, - M21: -st, - M22: ct - } + o: o }); }, diff --git a/src/DOM.js b/src/DOM.js index 4ca96707..f38436fd 100644 --- a/src/DOM.js +++ b/src/DOM.js @@ -73,21 +73,6 @@ Crafty.c("DOM", { this.bind("NewComponent", updateClass).bind("RemoveComponent", removeClass); - if (Crafty.support.prefix === "ms" && Crafty.support.version < 9) { - this._filters = {}; - - this.bind("Rotate", function (e) { - var m = e.matrix, - elem = this._element.style, - M11 = m.M11.toFixed(8), - M12 = m.M12.toFixed(8), - M21 = m.M21.toFixed(8), - M22 = m.M22.toFixed(8); - - this._filters.rotation = "progid:DXImageTransform.Microsoft.Matrix(M11=" + M11 + ", M12=" + M12 + ", M21=" + M21 + ", M22=" + M22 + ",sizingMethod='auto expand')"; - }); - } - this.bind("Remove", this.undraw); this.bind("RemoveComponent", function (compName) { if (compName === "DOM") From d6e499461e0b36958ca6a132afafd1de5952bc18 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Wed, 18 Dec 2013 03:44:26 -0500 Subject: [PATCH 28/61] Remove IE8 test logic --- tests/core.html | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/tests/core.html b/tests/core.html index 0659c705..b167020c 100755 --- a/tests/core.html +++ b/tests/core.html @@ -305,28 +305,22 @@ module("2D"); test("position", function() { - var no_getter_setters = !(Crafty.support.setter || Crafty.support.defineProperty); var player = Crafty.e("2D, DOM, Color").attr({w:50, h: 50}).color("red"); player.x += 50; - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(player._x, 50, "X moved"); player.y += 50; - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(player._y, 50, "Y moved"); player.w += 50; - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(player._w, 100, "Width increase"); player.h += 50; - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(player._h, 100, "Height increase"); strictEqual(player._globalZ, player[0], "Global Z, Before"); player.z = 1; - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(player._z, 1, "Z index"); var global_z_guess; @@ -341,13 +335,11 @@ }); test("intersect", function() { - var no_getter_setters = !(Crafty.support.setter || Crafty.support.defineProperty); var player = Crafty.e("2D, DOM, Color").attr({w:50, h: 50}).color("red"); player.x = 0; player.y = 0; player.w = 50; player.h = 50; - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(player.intersect(0, 0, 100, 50), true, "Intersected"); @@ -359,13 +351,11 @@ }); test("within", function() { - var no_getter_setters = !(Crafty.support.setter || Crafty.support.defineProperty); var player = Crafty.e("2D, DOM, Color").attr({w:50, h: 50}); player.x = 0; player.y = 0; player.w = 50; player.h = 50; - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(player.within(0, 0, 50, 50), true, "Within"); @@ -377,7 +367,6 @@ 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 - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(player.within(0, 0, 50, 50), false, "Rotated, Not within"); strictEqual(player.within(-50, 0, 50, 50), true, "Rotated, within rotated area"); @@ -389,13 +378,11 @@ }); test("contains", function() { - var no_getter_setters = !(Crafty.support.setter || Crafty.support.defineProperty); var player = Crafty.e("2D, DOM, Color").attr({w:50, h: 50}); player.x = 0; player.y = 0; player.w = 50; player.h = 50; - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(player.contains(0, 0, 50, 50), true, "Contains"); @@ -407,7 +394,6 @@ strictEqual(player.contains(1, 1, 51, 51), false, "Doesn't contain"); player.rotation = 90; - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(player.contains(0, 0, 50, 50), false, "Rotated, no longer contains"); strictEqual(player.within(-50, 0, 50, 50), true, "Rotated, contains rotated area"); @@ -419,16 +405,13 @@ test("circle", function() { - var no_getter_setters = !(Crafty.support.setter || Crafty.support.defineProperty); var player = Crafty.e("2D, DOM, Color").attr({w:50, h: 50}).color("red"); var circle = new Crafty.circle(0, 0, 10); - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(circle.containsPoint(1, 2), true, "Contained the point"); strictEqual(circle.containsPoint(8, 9), false, "Didn't contain the point"); circle.shift(1, 0); - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(circle.x, 1, "Shifted of one pixel on the x axis"); strictEqual(circle.y, 0, "circle.y didn't change"); @@ -438,7 +421,6 @@ }); test("child", function () { - var no_getter_setters = !(Crafty.support.setter || Crafty.support.defineProperty); var parent0 = Crafty.e("2D, DOM, Color").attr({x:0, y:0, w:50, h:50}).color("red"); var child0 = Crafty.e("2D, DOM, Color").attr({x:1, y:1, w:50, h:50}).color("red"); var child1 = Crafty.e("2D, DOM, Color").attr({x:2, y:2, w:50, h:50}).color("red"); @@ -453,12 +435,10 @@ parent0.attach(child2); parent0.attach(child3); parent0.x += 50; - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(child0._x, 51, 'child0 shifted when parent did'); strictEqual(child1._x, 52, 'child1 shifted when parent did'); child0.x += 1; child1.x += 1; - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } 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'); @@ -472,7 +452,6 @@ test("child_rotate", function () { var Round = function(x){ return Math.round(x*100)/100 }; - var no_getter_setters = !(Crafty.support.setter || Crafty.support.defineProperty); var parent = Crafty.e("2D, DOM, Color") .attr({x:0, y:0, w:50, h:50, rotation:10}) .color("red"); @@ -482,17 +461,14 @@ parent.attach(child); parent.rotation += 20; - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual(parent.rotation, 30, 'parent rotates normally'); strictEqual(child.rotation, 35, 'child follows parent rotation'); child.rotation += 22; - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } 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 - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } strictEqual( Round(child.x), -10, "Child moved around parent upon rotation (x).") strictEqual( Round(child.y), 10, "Child moved around parent upon rotation (y).") @@ -514,11 +490,9 @@ test("disableControl and enableControl", function () { - var no_getter_setters = !(Crafty.support.setter || Crafty.support.defineProperty); var e = Crafty.e("2D, Twoway") .attr({ x: 0 }) .twoway(1); - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } equal(e._movement.x, 0); equal(e._x, 0); @@ -557,10 +531,8 @@ test("Resizing 2D objects & hitboxes", function(){ - var no_getter_setters = !(Crafty.support.setter || Crafty.support.defineProperty); var e = Crafty.e("2D, Collision"); e.attr({x:0, y:0, w:40, h:50}) - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } equal(e.map.points[0][0], 0, "Before rotation: x_0 is 0") equal(e.map.points[0][1], 0, "y_0 is 0") @@ -568,7 +540,6 @@ equal(e.map.points[2][1], 50, "y_2 is 50") e.rotation = 90; - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } equal( Math.round(e.map.points[0][0]), 0, "After rotation by 90 deg: x_0 is 0") equal( Math.round(e.map.points[0][1]), 0, "y_0 is 0") @@ -592,7 +563,6 @@ // 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; - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } equal( Math.round(e.map.points[0][0]), 0, "After rotation and increase in width: x_0 is 0") equal( Math.round(e.map.points[0][1]), 0, "y_0 is 0") @@ -639,18 +609,13 @@ }); test("VisibleMBR and DebugRect", function(){ - var no_getter_setters = !(Crafty.support.setter || Crafty.support.defineProperty); var e = Crafty.e("2D, VisibleMBR").attr({x:10, y:10, w:10, h:20}); - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } e._assignRect(); - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } equal(e.debugRect._x, 10, "debugRect has correct x coord"); equal(e.debugRect._h, 20, "debugRect has correct height"); e.rotation = 90; - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } e._assignRect(); - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } equal(e.debugRect._h, 10, "debugRect has correct height of MBR after rotation"); e.destroy(); @@ -658,11 +623,8 @@ }); test("Hitbox debugging", function(){ - var no_getter_setters = !(Crafty.support.setter || Crafty.support.defineProperty); var e = Crafty.e("2D, Collision, WiredHitBox").attr({x:10, y:10, w:10, h:20}).collision(); - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } e.matchHitBox(); // only necessary until collision works properly! - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } equal(e.polygon.points[0][0], 10, "WiredHitBox -- correct x coord for upper right corner"); equal(e.polygon.points[2][1], 30, "correct y coord for lower right corner"); notEqual(typeof e._debug.strokeStyle, "undefined" , "stroke style is assigned"); @@ -671,18 +633,14 @@ e.destroy(); var e = Crafty.e("2D, Collision, SolidHitBox").attr({x:10, y:10, w:10, h:20}).collision(); - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } e.matchHitBox(); // only necessary until collision works properly! - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } equal(e.polygon.points[0][0], 10, "SolidHitBox -- correct x coord for upper right corner"); equal(e.polygon.points[2][1], 30, "correct y coord for lower right corner"); equal(typeof e._debug.strokeStyle, "undefined" , "stroke style is undefined"); notEqual(typeof e._debug.fillStyle, "undefined", "fill style is assigned"); e.collision( new Crafty.polygon([0, 0], [15, 0], [0, 15]) ); - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } e.matchHitBox(); - if (no_getter_setters) { Crafty.trigger("EnterFrame"); } equal(e.polygon.points[2][1], 25, "After change -- correct y coord for third point"); e.destroy(); From f6d0a9da8643c380a7140e7f465438067903d22e Mon Sep 17 00:00:00 2001 From: mucaho Date: Fri, 20 Dec 2013 00:09:47 +0100 Subject: [PATCH 29/61] extracted code from extensions.js moved keys & mouseButtons to keycodes.js moved zeroFill to 2D.js (only 2D.js uses zeroFill) --- package.json | 3 +- src/2D.js | 8 ++ src/extensions.js | 221 ---------------------------------------------- src/keycodes.js | 216 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 226 insertions(+), 222 deletions(-) create mode 100644 src/keycodes.js diff --git a/package.json b/package.json index 442104e7..f4ecb5ca 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,8 @@ "src/loader.js", "src/math.js", "src/time.js", - "src/DebugLayer.js" + "src/DebugLayer.js", + "src/keycodes.js" ], "dependencies": { "grunt": "~0.4.1", diff --git a/src/2D.js b/src/2D.js index aff000ef..3b09a9bc 100644 --- a/src/2D.js +++ b/src/2D.js @@ -57,6 +57,14 @@ var M = Math, PI = M.PI, DEG_TO_RAD = PI / 180; +Crafty.extend({ + zeroFill: function (number, width) { + width -= number.toString().length; + if (width > 0) + return new Array(width + (/\./.test(number) ? 2 : 1)).join('0') + number; + return number.toString(); + } +}); /**@ * #2D diff --git a/src/extensions.js b/src/extensions.js index 015fde2f..132160af 100644 --- a/src/extensions.js +++ b/src/extensions.js @@ -136,13 +136,6 @@ var Crafty = require('./core.js'), })(); Crafty.extend({ - zeroFill: function (number, width) { - width -= number.toString().length; - if (width > 0) - return new Array(width + (/\./.test(number) ? 2 : 1)).join('0') + number; - return number.toString(); - }, - /**@ * #Crafty.sprite * @category Graphics @@ -384,219 +377,5 @@ Crafty.extend({ */ background: function (style) { Crafty.stage.elem.style.background = style; - }, - - - - /**@ - * #Crafty.keys - * @category Input - * Object of key names and the corresponding key code. - * - * ~~~ - * 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 - * ~~~ - */ - 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 - - }, - - /**@ - * #Crafty.mouseButtons - * @category Input - * Object of mouseButton names and the corresponding button ID. - * In all mouseEvents we add the e.mouseButton property with a value normalized to match e.button of modern webkit - * - * ~~~ - * LEFT: 0, - * MIDDLE: 1, - * RIGHT: 2 - * ~~~ - */ - mouseButtons: { - LEFT: 0, - MIDDLE: 1, - RIGHT: 2 } }); \ No newline at end of file diff --git a/src/keycodes.js b/src/keycodes.js new file mode 100644 index 00000000..c29c7667 --- /dev/null +++ b/src/keycodes.js @@ -0,0 +1,216 @@ +var Crafty = require('./core.js'), + document = window.document; + +Crafty.extend({ + /**@ + * #Crafty.keys + * @category Input + * Object of key names and the corresponding key code. + * + * ~~~ + * 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 + * ~~~ + */ + 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 + + }, + + /**@ + * #Crafty.mouseButtons + * @category Input + * Object of mouseButton names and the corresponding button ID. + * In all mouseEvents we add the e.mouseButton property with a value normalized to match e.button of modern webkit + * + * ~~~ + * LEFT: 0, + * MIDDLE: 1, + * RIGHT: 2 + * ~~~ + */ + mouseButtons: { + LEFT: 0, + MIDDLE: 1, + RIGHT: 2 + } +}); \ No newline at end of file From 0a422d629906ff529792c23dec5384a1de29352e Mon Sep 17 00:00:00 2001 From: mucaho Date: Fri, 20 Dec 2013 03:00:33 +0100 Subject: [PATCH 30/61] fix jump while falling fixes #419 --- src/controls.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controls.js b/src/controls.js index ff47b6ca..ff5ba2ec 100644 --- a/src/controls.js +++ b/src/controls.js @@ -925,10 +925,10 @@ Crafty.c("Twoway", { if (this._up) { this.y -= this._jumpSpeed; this._falling = true; - this.trigger('Moved', { x: this._x, y: this._y + this._jumpSpeed }); + this.trigger('Moved', { x: this._x, y: this._y + this._jumpSpeed }); } }).bind("KeyDown", function (e) { - if (e.key === Crafty.keys.UP_ARROW || e.key === Crafty.keys.W || e.key === Crafty.keys.Z) + if (!this._falling && (e.key === Crafty.keys.UP_ARROW || e.key === Crafty.keys.W || e.key === Crafty.keys.Z)) this._up = true; }); From 261a0673714411d210cc6e046c708812b981300f Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Tue, 17 Dec 2013 02:39:13 -0500 Subject: [PATCH 31/61] Allow visibility MBR to be set manually This patch adds properites to "2D" that indicate how far beyond an object's default dimensions the visible MBR should be. These can be set with the method offsetBoundary. --- src/2D.js | 73 +++++++++++++++++++++++++++++++++++++------------ src/canvas.js | 4 +-- tests/core.html | 34 +++++++++++++++++++++++ 3 files changed, 92 insertions(+), 19 deletions(-) diff --git a/src/2D.js b/src/2D.js index beed4ef9..bfc8b3c9 100644 --- a/src/2D.js +++ b/src/2D.js @@ -324,6 +324,13 @@ Crafty.c("2D", { x: 0, y: 0 }; + + // offsets for the basic bounding box + this._bx1 = 0; + this._bx2 = 0; + this._by1 = 0; + this._by2 = 0; + this._children = []; if (Crafty.support.setter) { @@ -383,34 +390,66 @@ Crafty.c("2D", { }, + /**@ + * #.offsetBoundary + * @comp 2D + * 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 + * @param dy1 - Extends the MBR upward by this amount + * @param dx2 - Extends the MBR to the right by this amount + * @param dy2 - Extends the MBR downward by this amount + * + * @sign public this .offsetBoundary(Number offset) + * @param offset - Extend the MBR in all directions by this amount + * + * 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; + this._bx1 = x1; + this._bx2 = x2; + this._by1 = y1; + this._by2 = y2; + this.trigger("BoundaryOffset"); + this._calculateMBR(this._origin.x + this._x, this._origin.y + this._y, -this._rotation * DEG_TO_RAD); + return this; + }, + /** * Calculates the MBR when rotated some number of radians about an origin point o. - * Necessary on a rotation, or a resize (when already rotated) + * Necessary on a rotation, or a resize */ _calculateMBR: function (ox, oy, rad) { - if (rad === 0) { - this._mbr = null; - return; - } + // axis-aligned (unrotated) coordinates, relative to the origin point + var dx1 = this._x - this._bx1 - ox, + dx2 = this._x + this._w + this._bx2 - ox, + dy1 = this._y - this._by1 - oy, + dy2 = this._y + this._h + this._by2 - oy; 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; - var x0 = ox + (this._x - ox) * ct + (this._y - oy) * st, - y0 = oy - (this._x - ox) * st + (this._y - oy) * ct, - x1 = ox + (this._x + this._w - ox) * ct + (this._y - oy) * st, - y1 = oy - (this._x + this._w - ox) * st + (this._y - oy) * ct, - x2 = ox + (this._x + this._w - ox) * ct + (this._y + this._h - oy) * st, - y2 = oy - (this._x + this._w - ox) * st + (this._y + this._h - oy) * ct, - x3 = ox + (this._x - ox) * ct + (this._y + this._h - oy) * st, - y3 = oy - (this._x - ox) * st + (this._y + this._h - oy) * ct, - minx = Math.floor(Math.min(x0, x1, x2, x3)), - miny = Math.floor(Math.min(y0, y1, y2, y3)), - maxx = Math.ceil(Math.max(x0, x1, x2, x3)), - maxy = Math.ceil(Math.max(y0, y1, y2, y3)); + + // 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, + 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), + maxy = Math.ceil(Math.max(y0, y1, y2, y3) + oy); if (!this._mbr) { this._mbr = { _x: minx, diff --git a/src/canvas.js b/src/canvas.js index 1fabe253..3b62dd08 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -102,7 +102,7 @@ Crafty.c("Canvas", { co.w = w || coord[2]; co.h = h || coord[3]; - if (this._mbr) { + if (this._rotation !== 0) { context.save(); context.translate(this._origin.x + this._x, this._origin.y + this._y); @@ -134,7 +134,7 @@ Crafty.c("Canvas", { this.drawVars.ctx = context; this.trigger("Draw", this.drawVars); - if (this._mbr || (this._flipX || this._flipY)) { + if (this._rotation !== 0 || (this._flipX || this._flipY)) { context.restore(); } if (globalpha) { diff --git a/tests/core.html b/tests/core.html index 74ed3701..50a1f716 100755 --- a/tests/core.html +++ b/tests/core.html @@ -550,6 +550,40 @@ }); + test("adjustable boundary", function(){ + e = Crafty.e("2D").attr({ + x: 10, y:10, w: 10, h: 10 + }) + + // Four argument version + e.offsetBoundary(10, 1, 3, 0); + equal(e._bx1, 10, "X1 boundary set"); + equal(e._bx2, 3, "X2 boundary set"); + equal(e._by1, 1, "Y1 boundary set"); + equal(e._by2, 0, "Y2 boundary set"); + + e._calculateMBR(10, 10, 0) + + var mbr = e._mbr; + + equal(mbr._h, 11, "MBR height uses boundaries (11)"); + equal(mbr._w, 23, "MBR width uses boundaries (23)"); + + // One argument version + e.offsetBoundary(5); + equal(e._bx1, 5, "X1 boundary set"); + equal(e._bx2, 5, "X2 boundary set"); + equal(e._by1, 5, "Y1 boundary set"); + equal(e._by2, 5, "Y2 boundary set"); + + + + Crafty("*").destroy(); + + + }) + + test("disableControl and enableControl", function () { var e = Crafty.e("2D, Twoway") .attr({ x: 0 }) From 58773546f657b9df271a75e7b49d2f595680f045 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Tue, 17 Dec 2013 16:00:42 -0500 Subject: [PATCH 32/61] Fix problem with DOM sprite animation in webkit browsers The backgroundPosition CSS attribute isn't always getting set correctly in Safari. This seems to be some sort of hard to trigger webkit-only bug that occurs when we do sprite animation, but not when setting regular sprite positions. I was unable to produce a simple test case without using Crafty, so I'm not sure what the exact problem is. This "fixes" things by setting the background position separately from the rest of the background properties. --- src/sprite.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sprite.js b/src/sprite.js index 1e8576bb..4c102b96 100644 --- a/src/sprite.js +++ b/src/sprite.js @@ -56,7 +56,8 @@ Crafty.c("Sprite", { hscale = this._w / co.w, style = this._element.style; - style.background = style.backgroundColor + " url('" + this.__image + "') no-repeat -" + co.x * hscale + "px -" + co.y * vscale + "px"; + style.background = style.backgroundColor + " url('" + this.__image + "') no-repeat"; + 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"; From 51e556176e460aed4ff0947f5f86dc1c28f80ec4 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Fri, 27 Dec 2013 15:26:02 -0500 Subject: [PATCH 33/61] Remove stray occurrences of toX in code and docs Both the docs and the code still referenced toX -- the former causes confusion, the latter an accidental global variable! --- src/animation.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/animation.js b/src/animation.js index c86ee6e9..f01fdef7 100644 --- a/src/animation.js +++ b/src/animation.js @@ -182,10 +182,10 @@ Crafty.c("SpriteAnimation", { * PlayerSprite: [0,0] * }); * - * // Define an animation on the second row of the sprite map (y = 1) + * // Define an animation on the second row of the sprite map (fromY = 1) * // from the left most sprite (fromX = 0) to the fourth sprite - * // on that row (toX = 3), with a duration of 1 second - * Crafty.e("2D, DOM, SpriteAnimation, PlayerSprite").reel('PlayerRunning', 1000, 0, 1, 3); + * // on that row (frameCount = 4), with a duration of 1 second + * Crafty.e("2D, DOM, SpriteAnimation, PlayerSprite").reel('PlayerRunning', 1000, 0, 1, 4); * * // This is the same animation definition, but using the alternative method * Crafty.e("2D, DOM, SpriteAnimation, PlayerSprite").reel('PlayerRunning', 1000, [[0, 1], [1, 1], [2, 1], [3, 1]]); @@ -229,7 +229,7 @@ Crafty.c("SpriteAnimation", { reel.duration = reel.easing.duration; - // @sign public this .reel(String reelId, Number duration, Number fromX, Number y, Number toX) + // @sign public this .reel(String reelId, Number duration, Number fromX, Number fromY, Number frameDuration) if (typeof fromX === "number") { i = fromX; y = fromY; @@ -246,11 +246,8 @@ Crafty.c("SpriteAnimation", { } // @sign public this .reel(String reelId, Number duration, Array frames) else if (arguments.length === 3 && typeof fromX === "object") { - - i = 0; - toX = fromX.length - 1; - - for (; i <= toX; i++) { + // In this case, fromX is actually the array of frames + for (i=0; i < fromX.length; i++) { pos = fromX[i]; reel.frames.push([pos[0] * tile, pos[1] * tileh]); } From 36d266124d1b4c7ad72ec5107721609b91f938d3 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Tue, 17 Dec 2013 02:39:13 -0500 Subject: [PATCH 34/61] Adjust bounding region to include hitbox when it exists outside of the entity Introduce a new bounding rectangle `._cbr` (collision bounding rectangle) that is guaranteed to include both the entity and it's hitbox. When the hitbox sits inside the entity, the CBR will not be used. Also, simplify the way `_calculateMBR` is called. A follow up patch could optimize that function for the case where no rotation exists. This patch also adds tests and a small note to the docs that external hitboxes might have a small performance penalty. --- src/2D.js | 43 ++++++++++++++++---- src/collision.js | 102 ++++++++++++++++++++++++++++++++++++++++++----- tests/core.html | 86 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 214 insertions(+), 17 deletions(-) diff --git a/src/2D.js b/src/2D.js index 75d029b1..c2e92d2d 100644 --- a/src/2D.js +++ b/src/2D.js @@ -353,7 +353,8 @@ Crafty.c("2D", { //when object changes, update HashMap this.bind("Move", function (e) { - var area = this._mbr || this; + // Choose the largest bounding region that exists + var area = this._cbr || this._mbr || this; this._entry.update(area); // Move children (if any) by the same amount if (this._children.length > 0) { @@ -362,7 +363,8 @@ Crafty.c("2D", { }); this.bind("Rotate", function (e) { - var old = this._mbr || this; + // Choose the largest bounding region that exists + var old = this._cbr || this._mbr || this; this._entry.update(old); // Rotate children (if any) by the same amount if (this._children.length > 0) { @@ -423,7 +425,7 @@ Crafty.c("2D", { this._by1 = y1; this._by2 = y2; this.trigger("BoundaryOffset"); - this._calculateMBR(this._origin.x + this._x, this._origin.y + this._y, -this._rotation * DEG_TO_RAD); + this._calculateMBR(); return this; }, @@ -432,7 +434,10 @@ Crafty.c("2D", { * Necessary on a rotation, or a resize */ - _calculateMBR: function (ox, oy, rad) { + _calculateMBR: function () { + var ox = this._origin.x + this._x, + oy = this._origin.y + this._y, + rad = -this._rotation * DEG_TO_RAD; // axis-aligned (unrotated) coordinates, relative to the origin point var dx1 = this._x - this._bx1 - ox, dx2 = this._x + this._w + this._bx2 - ox, @@ -472,6 +477,23 @@ Crafty.c("2D", { this._mbr._h = maxy - miny; } + // 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. + // 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. + // + // 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 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); + cbr._y = Math.min(cy2 - r, miny); + cbr._w = Math.max(cx2 + r, maxx) - cbr._x; + cbr._h = Math.max(cy2 + r, maxy) - cbr._y; + } + }, /** @@ -483,6 +505,8 @@ Crafty.c("2D", { // skip if there's no rotation! if (difference === 0) return; + else + this._rotation = v; //Calculate the new MBR var rad = theta * DEG_TO_RAD, @@ -491,7 +515,7 @@ Crafty.c("2D", { y: this._origin.y + this._y }; - this._calculateMBR(o.x, o.y, rad); + this._calculateMBR(); //trigger "Rotate" event @@ -926,10 +950,15 @@ Crafty.c("2D", { this.trigger("reorder"); //if the rect bounds change, update the MBR and trigger move } 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-minmal bounding rectangle that contains both hitbox and mbr + // It will exist only when the collision hitbox sits outside the entity + if (this._cbr){ + this._cbr[name] -= this[name] - value; + } } this[name] = value; @@ -941,7 +970,7 @@ Crafty.c("2D", { var oldValue = this[name]; this[name] = value; if (mbr) { - this._calculateMBR(this._origin.x + this._x, this._origin.y + this._y, -this._rotation * DEG_TO_RAD); + this._calculateMBR(); } if (name === '_w') { this.trigger("Resize", { diff --git a/src/collision.js b/src/collision.js index 4d2b1240..55da6d2a 100644 --- a/src/collision.js +++ b/src/collision.js @@ -17,11 +17,17 @@ Crafty.c("Collision", { */ init: function () { this.requires("2D"); - var area = this._mbr || this; - this.collision(); }, + + // Run by Crafty when the component is removed + remove: function() { + this._cbr = null; + this.unbind("Resize", this._resizeMap); + this.unbind("Resize", this._checkBounds); + }, + /**@ * #.collision * @comp Collision @@ -43,6 +49,8 @@ Crafty.c("Collision", { * * If no parameter is passed, the x, y, w, h properties of the entity will be used, and the hitbox will be resized when the entity is. * + * If a hitbox is set that is outside of the bounds of the entity itself, there will be a small performance penalty as it is tracked separately. + * * @example * ~~~ * Crafty.e("2D, Collision").collision( @@ -55,18 +63,32 @@ Crafty.c("Collision", { * @see Crafty.polygon */ collision: function (poly) { + // Unbind anything bound to "Resize" this.unbind("Resize", this._resizeMap); + this.unbind("Resize", this._checkBounds); + + + if (!poly) { + // 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 poly = new Crafty.polygon([0, 0], [this._w, 0], [this._w, this._h], [0, this._h]); this.bind("Resize", this._resizeMap); + this._cbr = null; + } else { + // Otherwise, we set the specified hitbox, converting from a list of arguments to a polygon if necessary + if (arguments.length > 1) { + //convert args to array to create polygon + var args = Array.prototype.slice.call(arguments, 0); + poly = new Crafty.polygon(args); + } + // Check to see if the polygon sits outside the entity, and set _cbr appropriately + // On resize, the new bounds will be checked if necessary + this._findBounds(poly.points); } - if (arguments.length > 1) { - //convert args to array to create polygon - var args = Array.prototype.slice.call(arguments, 0); - poly = new Crafty.polygon(args); - } + // If the entity is currently rotated, the points in the hitbox must also be rotated if (this.rotation) { poly.rotate({ cos: Math.cos(-this.rotation * DEG_TO_RAD), @@ -78,6 +100,7 @@ Crafty.c("Collision", { }); } + // Finally, assign the hitbox, and attach it to the "Collision" entity this.map = poly; this.attach(this.map); this.map.shift(this._x, this._y); @@ -86,7 +109,66 @@ Crafty.c("Collision", { }, - // Change the hitbox when a "Resize" event triggers. + // If the hitbox is set by hand, it might extend beyond the entity. + // In such a case, we need to track this separately. + // This function finds a (non-minimal) bounding circle around the hitbox. + // + // 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 p; + + // Calculate the MBR of the points by finding the min/max x and y + for (var i=0; i maxX) + maxX = p[0]; + if (p[1] < minY) + minY = p[1]; + if (p[1] > maxY) + maxY = p[1]; + } + + // This describes a circle centered on the MBR of the points, with a diameter equal to its diagonal + // It will be used to find a rough bounding box round the points, even if they've been rotated + var cbr = { + cx: (minX + maxX) / 2, + cy: (minY + maxY) / 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(); + } else if (this._cbr) { + this._cbr = null; + this._calculateMBR(); + } + }; + this.bind("Resize", this._checkBounds); + } + + // If the hitbox is within the entity, _cbr is null + // Otherwise, set it, and immediately calculate the bounding box. + if (minX >= 0 && minY >= 0 && maxX <= this._w && maxY <= this._h){ + this._cbr = null; + return false; + } else { + this._cbr = cbr; + this._calculateMBR(); + return true; + } + + }, + + // 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, @@ -152,7 +234,7 @@ Crafty.c("Collision", { * @see .onHit, 2D */ hit: function (comp) { - var area = this._mbr || this, + var area = this._cbr || this._mbr || this, results = Crafty.map.search(area, false), i = 0, l = results.length, @@ -167,7 +249,7 @@ Crafty.c("Collision", { for (; i < l; ++i) { obj = results[i]; - oarea = obj._mbr || obj; //use the mbr + oarea = obj._cbr || obj._mbr || obj; //use the mbr if (!obj) continue; id = obj[0]; diff --git a/tests/core.html b/tests/core.html index 2684c4a9..91456da3 100755 --- a/tests/core.html +++ b/tests/core.html @@ -673,6 +673,92 @@ e.destroy(); }); + 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); + + ok(e._cbr !== null, "_cbr exists"); + var cbr = e._cbr; + // Test whether cbr actually bounds hitbox+object + ok(cbr._x <= 42, "cbr x position correct"); + ok(cbr._y <= 36, "cbr y position correct"); + ok(cbr._x + cbr._w >= 74, "cbr width correct"); + ok(cbr._y + cbr._h >= 66, "cbr height correct"); + + var x0 = cbr._x, y0 = cbr._y; + + e.x += 10; + e.y += 15; + + equal(cbr._x, x0 + 10, "cbr x position moves correctly"); + equal(cbr._y, y0 + 15, "cbr y position moves correctly"); + Crafty("*").destroy(); + }) + + 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); + + ok(e._cbr === null, "_cbr should not exist"); + + e.w = 10; + + ok(e._cbr !== null, "_cbr should now exist after entity shrinks"); + + e.w = 20; + + ok(e._cbr === null, "_cbr should not exist after entity grows again"); + + Crafty("*").destroy(); + }) + + 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); + + 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"); + + + }) + module("DebugLayer"); test("DebugCanvas", function(){ if (!(Crafty.support.canvas)) { From e2db48b255ab2fac92993f32b04f4068b0b926dd Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Sun, 29 Dec 2013 21:45:49 -0500 Subject: [PATCH 35/61] Edit docs for formatting There are some formatting issues on several doc pages. This patch is for cleaning up formatting and (a few) grammar issues. The various math pages have some pretty big problems, so that's not handled here. --- src/2D.js | 2 +- src/DOM.js | 5 +++-- src/DebugLayer.js | 6 ++++-- src/HashMap.js | 2 -- src/canvas.js | 5 +++-- src/collision.js | 2 +- src/controls.js | 17 ++++++++--------- src/core.js | 21 ++++++++++----------- src/drawing.js | 16 ++++++---------- src/html.js | 6 +++--- src/keycodes.js | 4 ++-- src/sound.js | 6 +++--- src/viewport.js | 13 ++++++------- 13 files changed, 50 insertions(+), 55 deletions(-) diff --git a/src/2D.js b/src/2D.js index 75d029b1..2af7c264 100644 --- a/src/2D.js +++ b/src/2D.js @@ -72,7 +72,7 @@ Crafty.extend({ * 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 Change - when the entity has moved - { _x:Number, _y:Number, _w:Number, _h:Number } - Old position - * @trigger Rotate - when the entity is rotated - { cos:Number, sin:Number, deg:Number, rad:Number, o: {x:Number, y:Number}, matrix: {M11, M12, M21, M22} } + * @trigger Rotate - when the entity is rotated - { cos:Number, sin:Number, deg:Number, rad:Number, o: {x:Number, y:Number}} */ Crafty.c("2D", { /**@ diff --git a/src/DOM.js b/src/DOM.js index f38436fd..cfa4a35c 100644 --- a/src/DOM.js +++ b/src/DOM.js @@ -218,10 +218,11 @@ Crafty.c("DOM", { /**@ * #.css * @comp DOM - * @sign public * css(String property, String value) + * @sign public css(String property, String value) * @param property - CSS property to modify * @param value - Value to give the CSS property - * @sign public * css(Object map) + * + * @sign public css(Object map) * @param map - Object where the key is the CSS property and the value is CSS value * * Apply CSS styles to the element. diff --git a/src/DebugLayer.js b/src/DebugLayer.js index 228ac18c..a777611e 100644 --- a/src/DebugLayer.js +++ b/src/DebugLayer.js @@ -56,9 +56,10 @@ Crafty.c("DebugCanvas", { * @comp DebugCanvas * @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 * ~~~ * var myEntity = Crafty.e("2D, Collision, SolidHitBox ").debugFill("purple") - *~~~ + * ~~~ */ debugFill: function (fillStyle) { if (typeof fillStyle === 'undefined') @@ -72,9 +73,10 @@ Crafty.c("DebugCanvas", { * @comp DebugCanvas * @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 * ~~~ * var myEntity = Crafty.e("2D, Collision, WiredHitBox ").debugStroke("white") - *~~~ + * ~~~ */ debugStroke: function (strokeStyle) { if (typeof strokeStyle === 'undefined') diff --git a/src/HashMap.js b/src/HashMap.js index 223933cd..d9d973e1 100644 --- a/src/HashMap.js +++ b/src/HashMap.js @@ -307,10 +307,8 @@ var Crafty = require('./core.js'), * @category 2D * Broad-phase collision detection engine. See background information at * - * ~~~ * - [N Tutorial B - Broad-Phase Collision](http://www.metanetsoftware.com/technique/tutorialB.html) * - [Broad-Phase Collision Detection with CUDA](http.developer.nvidia.com/GPUGems3/gpugems3_ch32.html) - * ~~~ * @see Crafty.map */ diff --git a/src/canvas.js b/src/canvas.js index 3b62dd08..44260acb 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -13,8 +13,9 @@ var Crafty = require('./core.js'), * * Create a canvas entity like this * ~~~ - * var myEntity = Crafty.e("2D, Canvas, Color").color("green") - * .attr({x: 13, y: 37, w: 42, h: 42}); + * var myEntity = Crafty.e("2D, Canvas, Color") + * .color("green") + * .attr({x: 13, y: 37, w: 42, h: 42}); *~~~ */ Crafty.c("Canvas", { diff --git a/src/collision.js b/src/collision.js index 4d2b1240..5e56cf9d 100644 --- a/src/collision.js +++ b/src/collision.js @@ -142,7 +142,7 @@ Crafty.c("Collision", { * ~~~ * [{ * obj: [entity], - * type "MBR" or "SAT", + * type: "MBR" or "SAT", * overlap: [number] * }] * ~~~ diff --git a/src/controls.js b/src/controls.js index ff5ba2ec..67b0631c 100644 --- a/src/controls.js +++ b/src/controls.js @@ -55,9 +55,12 @@ Crafty.extend({ * * Notable properties of a MouseEvent e: * ~~~ - * e.clientX, e.clientY //(x,y) coordinates of mouse event in web browser screen space - * e.realX, e.realY //(x,y) coordinates of mouse event in world/viewport space - * e.mouseButton // Normalized mouse button according to Crafty.mouseButtons + * //(x,y) coordinates of mouse event in web browser screen space + * e.clientX, e.clientY + * //(x,y) coordinates of mouse event in world/viewport space + * e.realX, e.realY + * // Normalized mouse button according to Crafty.mouseButtons + * e.mouseButton * ~~~ * @see Crafty.touchDispatch */ @@ -181,7 +184,7 @@ Crafty.extend({ * * TouchEvents have a different structure then MouseEvents. * The relevant data lives in e.changedTouches[0]. - * To normalize TouchEvents we catch em and dispatch a mock MouseEvent instead. + * To normalize TouchEvents we catch them and dispatch a mock MouseEvent instead. * * @see Crafty.mouseDispatch */ @@ -378,11 +381,9 @@ Crafty.bind("CraftyStop", function () { * @trigger MouseMove - when the mouse is over the entity and moves - MouseEvent * Crafty adds the mouseButton property to MouseEvents that match one of * - * ~~~ * - Crafty.mouseButtons.LEFT * - Crafty.mouseButtons.RIGHT * - Crafty.mouseButtons.MIDDLE - * ~~~ * * @example * ~~~ @@ -893,10 +894,8 @@ Crafty.c("Twoway", { * * Constructor to initialize the speed and power of jump. Component will * listen for key events and move the entity appropriately. This includes - * ~~~ - * `Up Arrow`, `Right Arrow`, `Left Arrow` as well as W, A, D. Used with the + * `Up Arrow`, `Right Arrow`, `Left Arrow` as well as `W`, `A`, `D`. Used with the * `gravity` component to simulate jumping. - * ~~~ * * The key presses will move the entity in that direction by the speed passed in * the argument. Pressing the `Up Arrow` or `W` will cause the entity to jump. diff --git a/src/core.js b/src/core.js index 03466179..ad3ca4aa 100644 --- a/src/core.js +++ b/src/core.js @@ -843,9 +843,12 @@ Crafty.fn = Crafty.prototype = { //give the init instances the Crafty prototype Crafty.fn.init.prototype = Crafty.fn; -/** - * Extension method to extend the namespace and - * selector instances + +/**@ + * #Crafty.extend + * @category Core + * Used to extend the Crafty namespace. + * */ Crafty.extend = Crafty.fn.extend = function (obj) { var target = this, @@ -862,11 +865,7 @@ Crafty.extend = Crafty.fn.extend = function (obj) { return target; }; -/**@ - * #Crafty.extend - * @category Core - * Used to extend the Crafty namespace. - */ + Crafty.extend({ /**@ * #Crafty.init @@ -1077,8 +1076,8 @@ Crafty.extend({ /**@ * #Crafty.timer.steptype * @comp Crafty.timer - * Can be called to set the type of timestep the game loop uses * @sign public void Crafty.timer.steptype(mode [, maxTimeStep]) + * Can be called to set the type of timestep the game loop uses * @param mode - the type of time loop. Allowed values are "fixed", "semifixed", and "variable". Crafty defaults to "fixed". * @param mode - For "fixed", sets the max number of frames per step. For "variable" and "semifixed", sets the maximum time step allowed. * @@ -1203,8 +1202,8 @@ Crafty.extend({ /**@ * #Crafty.timer.simulateFrames * @comp Crafty.timer - * Advances the game state by a number of frames and draws the resulting stage at the end. Useful for tests and debugging. * @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. */ @@ -1270,7 +1269,7 @@ Crafty.extend({ * @category Core * @sign public void Crafty.c(String name, Object component) * @param name - Name of the component - * @param component - Object with the components properties and methods + * @param component - Object with the component's properties and methods * Creates a component where the first argument is the ID and the second * is the object that will be inherited by entities. * diff --git a/src/drawing.js b/src/drawing.js index 2ae59bd2..25248167 100644 --- a/src/drawing.js +++ b/src/drawing.js @@ -35,10 +35,10 @@ Crafty.c("Color", { * choose to support. * * @example - * ~~~ + * ``` * Crafty.e("2D, DOM, Color") * .color("#969696"); - * ~~~ + * ``` */ color: function (color) { if (!color) return this._color; @@ -426,10 +426,9 @@ Crafty.DrawManager = (function () { * @comp Crafty.DrawManager * @sign public Crafty.DrawManager.drawAll([Object rect]) * @param rect - a rectangular region {_x: x_val, _y: y_val, _w: w_val, _h: h_val} - * ~~~ + * * - If rect is omitted, redraw within the viewport * - If rect is provided, redraw within the rect - * ~~~ */ drawAll: function (rect) { rect = rect || Crafty.viewport.rect(); @@ -457,10 +456,9 @@ Crafty.DrawManager = (function () { * @comp Crafty.DrawManager * @sign public Crafty.DrawManager.boundingRect(set) * @param set - Undocumented - * ~~~ + * * - Calculate the common bounding rect of multiple canvas entities. * - Returns coords - * ~~~ */ boundingRect: function (set) { if (!set || !set.length) return; @@ -496,12 +494,11 @@ Crafty.DrawManager = (function () { * #Crafty.DrawManager.renderCanvas * @comp Crafty.DrawManager * @sign public Crafty.DrawManager.renderCanvas() - * ~~~ + * * - Triggered by the "RenderScene" event * - If the number of rects is over 60% of the total number of objects * do the naive method redrawing `Crafty.DrawManager.drawAll` * - Otherwise, clear the dirty regions, and redraw entities overlapping the dirty regions. - * ~~~ * * @see Canvas.draw */ @@ -616,9 +613,8 @@ Crafty.DrawManager = (function () { * #Crafty.DrawManager.renderDOM * @comp Crafty.DrawManager * @sign public Crafty.DrawManager.renderDOM() - * ~~~ + * * When "RenderScene" is triggered, draws all DOM entities that have been flagged - * ~~~ * * @see DOM.draw */ diff --git a/src/html.js b/src/html.js index 842b898e..daadc349 100644 --- a/src/html.js +++ b/src/html.js @@ -26,7 +26,7 @@ Crafty.c("HTML", { * ~~~ * Crafty.e("HTML") * .attr({x:20, y:20, w:100, h:100}) - * .replace("Crafty.js"); + * .replace("Index"); * ~~~ */ replace: function (new_html) { @@ -48,7 +48,7 @@ Crafty.c("HTML", { * ~~~ * Crafty.e("HTML") * .attr({x:20, y:20, w:100, h:100}) - * .append("Crafty.js"); + * .append("Index"); * ~~~ */ append: function (new_html) { @@ -70,7 +70,7 @@ Crafty.c("HTML", { * ~~~ * Crafty.e("HTML") * .attr({x:20, y:20, w:100, h:100}) - * .prepend("Crafty.js"); + * .prepend("Index"); * ~~~ */ prepend: function (new_html) { diff --git a/src/keycodes.js b/src/keycodes.js index c29c7667..7a731642 100644 --- a/src/keycodes.js +++ b/src/keycodes.js @@ -199,8 +199,8 @@ Crafty.extend({ /**@ * #Crafty.mouseButtons * @category Input - * Object of mouseButton names and the corresponding button ID. - * In all mouseEvents we add the e.mouseButton property with a value normalized to match e.button of modern webkit + * 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: * * ~~~ * LEFT: 0, diff --git a/src/sound.js b/src/sound.js index 7e84a9a3..e01ec945 100644 --- a/src/sound.js +++ b/src/sound.js @@ -436,6 +436,7 @@ Crafty.extend({ * #Crafty.audio.pause * @comp Crafty.audio * @sign public this Crafty.audio.pause(string ID) + * @param {string} id - The id of the audio object to pause * * Pause the Audio instance specified by id param. * @@ -444,7 +445,6 @@ Crafty.extend({ * Crafty.audio.pause('music'); * ~~~ * - * @param {string} id The id of the audio object to pause */ pause: function (id) { if (!Crafty.support.audio || !id || !this.sounds[id]) @@ -462,6 +462,7 @@ Crafty.extend({ * #Crafty.audio.unpause * @comp Crafty.audio * @sign public this Crafty.audio.unpause(string ID) + * @param {string} id - The id of the audio object to unpause * * Resume playing the Audio instance specified by id param. * @@ -470,7 +471,6 @@ Crafty.extend({ * Crafty.audio.unpause('music'); * ~~~ * - * @param {string} id The id of the audio object to unpause */ unpause: function (id) { if (!Crafty.support.audio || !id || !this.sounds[id]) @@ -487,6 +487,7 @@ Crafty.extend({ * #Crafty.audio.togglePause * @comp Crafty.audio * @sign public this Crafty.audio.togglePause(string ID) + * @param {string} id - The id of the audio object to pause/ * * Toggle the pause status of the Audio instance specified by id param. * @@ -495,7 +496,6 @@ Crafty.extend({ * Crafty.audio.togglePause('music'); * ~~~ * - * @param {string} id The id of the audio object to pause/unpause */ togglePause: function (id) { if (!Crafty.support.audio || !id || !this.sounds[id]) diff --git a/src/viewport.js b/src/viewport.js index df974d32..8319295e 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -509,13 +509,12 @@ Crafty.extend({ * `Crafty.stage.inner` is a div inside the `#cr-stage` div that holds all DOM entities. * If you use canvas, a `canvas` element is created at the same level in the dom * as the the `Crafty.stage.inner` div. So the hierarchy in the DOM is - * - * `Crafty.stage.elem` - * - * - * - `Crafty.stage.inner` (a div HTMLElement) - * - * - `Crafty.canvas._canvas` (a canvas HTMLElement) + * + * ~~~ + * Crafty.stage.elem + * - Crafty.stage.inner (a div HTMLElement) + * - Crafty.canvas._canvas (a canvas HTMLElement) + * ~~~ */ //create stage div to contain everything From 59c6c1c38f32bdb2e26e57e439867fc74263aada Mon Sep 17 00:00:00 2001 From: Keenan Brock Date: Wed, 1 Jan 2014 12:33:39 -0500 Subject: [PATCH 36/61] bind returns the callback Now you can unbind the return value from a bind. (original behavior) --- src/core.js | 13 ++++++------- tests/events.html | 6 ++++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/core.js b/src/core.js index ad3ca4aa..48ba6f37 100644 --- a/src/core.js +++ b/src/core.js @@ -1366,7 +1366,7 @@ Crafty.extend({ * @sign public Number bind(String eventName, Function callback) * @param eventName - Name of the event to bind to * @param callback - Method to execute upon event triggered - * @returns ID of the current callback used to unbind + * @returns callback function which can be used for unbind * * Binds to a global event. Method will be executed when `Crafty.trigger` is used * with the event name. @@ -1398,7 +1398,8 @@ Crafty.extend({ var hdl = handlers[event]; if (!hdl.global) hdl.global = []; - return hdl.global.push(callback) - 1; + hdl.global.push(callback); + return callback; }, @@ -1408,7 +1409,7 @@ Crafty.extend({ * @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 - * @returns ID of the current callback used to unbind + * @returns callback function which can be used for unbind * * Works like Crafty.bind, but prevents a callback from being bound multiple times. * @@ -1416,8 +1417,7 @@ Crafty.extend({ */ uniqueBind: function (event, callback) { this.unbind(event, callback); - this.bind(event, callback); - + return this.bind(event, callback); }, /**@ @@ -1426,7 +1426,7 @@ Crafty.extend({ * @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 - * @returns ID of the current callback used to unbind + * @returns callback function which can be used for unbind * * Works like Crafty.bind, but will be unbound once the event triggers. * @@ -1439,7 +1439,6 @@ Crafty.extend({ self.unbind(event, oneHandler); }; return self.bind(event, oneHandler); - }, /**@ diff --git a/tests/events.html b/tests/events.html index ef0a4d82..37e24307 100644 --- a/tests/events.html +++ b/tests/events.html @@ -43,6 +43,12 @@ Crafty.trigger("Increment") strictEqual(x, 0, "Crafty.bind does not fire once unbound"); + x = 0; + var ref = Crafty.bind("Increment", add) + Crafty.unbind("Increment", ref) + Crafty.trigger("Increment") + strictEqual(x, 0, "Crafty.bind does not fire once unbound via reference"); + x = 0; Crafty.one("Increment", add) Crafty.trigger("Increment") From b1b566e03cbd0f78157944a51f34a2f96a20f346 Mon Sep 17 00:00:00 2001 From: Kevin Simper Date: Thu, 2 Jan 2014 10:37:21 +0100 Subject: [PATCH 37/61] Moved all the test to seperate javascript files --- tests/core.js | 941 +++++++++++++++++++++++++++++++++++++++++++++ tests/dom.js | 50 +++ tests/events.js | 190 +++++++++ tests/isometric.js | 84 ++++ tests/loader.js | 14 + tests/math.js | 310 +++++++++++++++ tests/sound.js | 102 +++++ tests/stage.js | 208 ++++++++++ tests/storage.js | 73 ++++ tests/text.js | 42 ++ tests/tween.js | 58 +++ 11 files changed, 2072 insertions(+) create mode 100644 tests/core.js create mode 100644 tests/dom.js create mode 100644 tests/events.js create mode 100644 tests/isometric.js create mode 100644 tests/loader.js create mode 100644 tests/math.js create mode 100644 tests/sound.js create mode 100644 tests/stage.js create mode 100644 tests/storage.js create mode 100644 tests/text.js create mode 100644 tests/tween.js diff --git a/tests/core.js b/tests/core.js new file mode 100644 index 00000000..80296850 --- /dev/null +++ b/tests/core.js @@ -0,0 +1,941 @@ +module("CORE"); + +test("getVersion", function() { + + ok(Crafty.getVersion(), "The actual library version"); +}); + +test("selectors", function() { + var first = Crafty.e("test"); + Crafty.e("test"); + Crafty.e("test"); + Crafty.e("test"); + Crafty.e("test, test2"); + Crafty.e("test, test2"); + Crafty.e("test2"); + strictEqual(Crafty("test").length, 6, "Single component"); + strictEqual(Crafty("test test2").length, 2, "Two components ANDed"); + strictEqual(Crafty("test, test2").length, 7, "Two components ORed"); + + strictEqual(Crafty("*").length, 7, "All components - universal selector"); + + strictEqual(Crafty(first[0]), first, "Get by ID"); + // Clean up + Crafty("*").destroy(); +}); + +test("addComponent and removeComponent", function() { + var first = Crafty.e("test"); + Crafty.c("comp", { + added: true + }); + first.addComponent("test3"); + strictEqual(first.has("test3"), true, "component added"); + + first.addComponent("comp"); + strictEqual(first.added, true, "component with property exists"); + + first.addComponent("multi1, multi2"); + 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)"); + first.removeComponent("comp", false); + strictEqual(!first.added && !first.has("comp"), true, "hard-removed component (properties are gone)"); + + // Clean up + Crafty("*").destroy(); +}); + +test("remove", function() { + var removeRan = false, + destroyFlag = false; + + Crafty.c("comp", { + remove: function(destroyed) { + removeRan = true; + destroyFlag = destroyed; + } + }) + var e = Crafty.e("comp, blank"); + e.removeComponent("blank"); + strictEqual(removeRan, false, "Remove doesn't run on other component removal"); + + removeRan = false; + e.removeComponent("comp"); + strictEqual(removeRan, true, "Remove runs on correct component removal"); + strictEqual(destroyFlag, false, "Destroy flag false on regular removal"); + + removeRan = false; + e.addComponent("comp"); + e.destroy() + strictEqual(removeRan, true, "Remove runs on component destrution"); + strictEqual(destroyFlag, true, "Destroy flag true on destruction"); +}); + +test("attr", function() { + var first = Crafty.e("test"); + first.attr("single", true); + strictEqual(first.single, true, "single attribute assigned"); + + first.attr({ + prop: "test", + another: 56 + }); + strictEqual(first.prop, "test", "properties from object assigned"); + strictEqual(first.another, 56, "properties from object assigned"); + // Clean up + Crafty("*").destroy(); +}); + +test("setter", function() { + if (!(Crafty.support.setter || Crafty.support.defineProperty)) { + // IE8 has a setter() function but it behaves differently. No test is currently written for IE8. + expect(0); + return; + } + var first = Crafty.e("test"); + first.setter('p1', function(v) { + this._p1 = v * 2 + }); + first.p1 = 2; + strictEqual(first._p1, 4, "single property setter"); + + first.setter('p2', function(v) { + this._p2 = v * 2 + }).setter('p3', function(v) { + this._p3 = v * 2 + }); + first.p2 = 2; + first.p3 = 3; + strictEqual(first._p2 + first._p3, 10, "two property setters"); + // Clean up + Crafty("*").destroy(); +}); + +test("bind", function() { + var first = Crafty.e("test"), + triggered = false; + first.bind("myevent", function() { + triggered = true; + }); + first.trigger("myevent"); + strictEqual(triggered, true, "custom event triggered"); + // Clean up + Crafty("*").destroy(); +}); + +test("unbind", function() { + var first = Crafty.e("test"); + first.bind("myevent", function() { + ok(false, "This should not be triggered (unbinding all)"); + }); + first.unbind("myevent"); + first.trigger("myevent"); + + function callback() { + ok(false, "This should also not be triggered (unbind by FN)"); + } + + function callback2() { + ok(true, "This should be triggered"); + } + + first.bind("myevent", callback); + first.bind("myevent", callback2); + first.unbind("myevent", callback); + first.trigger("myevent"); + // Clean up + Crafty("*").destroy(); +}); + +test("globalBindAndUnbind", function() { + var flag = 0; + var add_1 = function() { + flag += 1; + }; + var add_10 = function() { + flag += 10; + }; + var add_100 = function() { + flag += 100; + }; + Crafty.bind("theglobalevent", add_1); + Crafty.bind("theglobalevent", add_10); + Crafty.bind("theglobalevent", add_100); + Crafty.trigger("theglobalevent"); + strictEqual(flag, 111, "global event binding worked"); + Crafty.unbind("theglobalevent", add_1); + Crafty.trigger("theglobalevent"); + strictEqual(flag, 221, "global event single-function unbinding worked"); + Crafty.unbind("theglobalevent"); + Crafty.trigger("theglobalevent"); + strictEqual(flag, 221, "global event full unbinding worked"); +}); + +test("each", function() { + var count = 0; + Crafty.e("test"); + Crafty.e("test"); + Crafty.e("test"); + Crafty.e("test"); + Crafty.e("test, test2"); + Crafty.e("test, test2"); + Crafty.e("test2"); + + Crafty("test").each(function() { + count++; + }); + strictEqual(count, 6, "Iterated all elements with certain component"); + + count = 0; + Crafty("*").each(function() { + count++; + }); + strictEqual(count, 7, "Iterated all elements"); + // Clean up + Crafty("*").destroy(); +}); + +test("Crafty.get() to find an array", function() { + Crafty.e("test"); + Crafty.e("test"); + Crafty.e("test"); + + var collection = Crafty("test"); + var result = collection.get(); + equal(result.length, 3, "resultant array should be length 3"); + equal(result[0].has("test"), true, "Result elements should have correct component"); + equal(collection[0], result[0].getId(), "First id of result should match first id of Crafty array"); + + Crafty("*").destroy(); +}) + +test("Crafty.get(index) to find the indicated entity", function() { + Crafty.e("test"); + Crafty.e("test"); + Crafty.e("test"); + var collection, result; + + collection = Crafty("test"); + result = collection.get(0); + equal(result.has("test"), true, "Result should have correct component"); + equal(result.getId(), collection[0], "result should be first element of collection"); + + result = collection.get(-1); + equal(result.has("test"), true, "Result should have correct component"); + equal(result.getId(), collection[2], "result should be last element of collection"); + + Crafty("*").destroy(); +}) + +test("Crafty.get(index) error checking", function() { + Crafty.e("test"); + Crafty.e("test"); + Crafty.e("test"); + var collection, result; + + collection = Crafty("test"); + + result = collection.get(3); + equal(typeof result, "undefined", "result of get(3) should be undefined") + + result = collection.get(-4); + equal(typeof result, "undefined", "result of get(-4) should be undefined") + + + + Crafty("*").destroy(); +}) + +test("Crafty.get with only one object", function() { + var e = Crafty.e("test"); + var collection = Crafty("test"); + result = collection.get(0); + equal(result.getId(), e.getId(), "result of get(0) is correct entity") + result = collection.get(); + equal(result.length, 1, "result of get() is array of length 1"); + Crafty("*").destroy(); +}) + +test("requires", function() { + var first = Crafty.e("test"); + Crafty.c("already", { + already: true + }); + Crafty.c("notyet", { + notyet: true + }); + + first.addComponent("already"); + first.already = "already"; + + first.requires("already, notyet"); + + 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"); + // Clean up + Crafty("*").destroy(); +}); + +test("destroy", function() { + var first = Crafty.e("test"), + id = first[0]; //id + first.destroy(); + strictEqual(Crafty(id).length, 0, "Not listed"); + // Clean up + Crafty("*").destroy(); +}); + +module("Scenes"); + +test("Scene calling", function() { + var x = 0; + var sceneInit = function() { + x = 13; + } + Crafty.scene("test-call", sceneInit); + Crafty.scene("test-call"); + equal(x, 13, "Scene called succesfully.") +}); + +test("Scene parameters", function() { + var x = 0; + var paramTaker = function(y) { + x = y; + } + Crafty.scene("test-param", paramTaker); + Crafty.scene("test-param", 11); + equal(x, 11, "Scene called succesfully with parameter.") +}); + +test("Calling a scene destroys 2D entities", function() { + var e = Crafty.e("2D"); + var sceneInit = function() {}; + Crafty.scene("test-destroy", sceneInit); + Crafty.scene("test-destroy"); + var l = Crafty("2D").length; + equal(l, 0, "2D entity destroyed on scene change."); + + Crafty("*").destroy(); +}); + +test("Calling a scene doesn't destroy 2D entities with Persist", function() { + var e = Crafty.e("2D, Persist"); + var sceneInit = function() {}; + Crafty.scene("test-persist", sceneInit); + Crafty.scene("test-persist"); + var l = Crafty("2D").length; + equal(l, 1, "Persist entity remains on scene change."); + + Crafty("*").destroy(); +}); + + +test("Scene uninit function called", function() { + var x = 0; + var y = 0; + var sceneInit = function() { + x = 13; + } + var sceneUninit = function() { + x = 20; + } + var sceneGame = function() { + y = 5; + } + Crafty.defineScene("test-uninit", sceneInit, sceneUninit); + Crafty.defineScene("game", sceneGame); + Crafty.enterScene("test-uninit"); + Crafty.enterScene("game"); + equal(x, 20, "Uninit scene called successfully when chanced to another scene"); +}); + +module("2D"); + +test("position", function() { + var player = Crafty.e("2D, DOM, Color").attr({ + w: 50, + h: 50 + }).color("red"); + player.x += 50; + strictEqual(player._x, 50, "X moved"); + + player.y += 50; + strictEqual(player._y, 50, "Y moved"); + + player.w += 50; + strictEqual(player._w, 100, "Width increase"); + + player.h += 50; + strictEqual(player._h, 100, "Height increase"); + + strictEqual(player._globalZ, player[0], "Global Z, Before"); + + player.z = 1; + strictEqual(player._z, 1, "Z index"); + + var global_z_guess; + if (player[0] < 10) { + global_z_guess = parseInt('10000' + player[0], 10); + } else { + global_z_guess = parseInt('1000' + player[0], 10); + } + strictEqual(player._globalZ, global_z_guess, "Global Z, After"); + // Clean up + Crafty("*").destroy(); +}); + +test("intersect", function() { + var player = Crafty.e("2D, DOM, Color").attr({ + w: 50, + h: 50 + }).color("red"); + player.x = 0; + player.y = 0; + player.w = 50; + player.h = 50; + + 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"); + // Clean up + Crafty("*").destroy(); +}); + +test("within", function() { + var player = Crafty.e("2D, DOM, Color").attr({ + w: 50, + h: 50 + }); + player.x = 0; + player.y = 0; + player.w = 50; + player.h = 50; + + strictEqual(player.within(0, 0, 50, 50), true, "Within"); + + 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(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"); + + + + // Clean up + Crafty("*").destroy(); +}); + +test("contains", function() { + var player = Crafty.e("2D, DOM, Color").attr({ + w: 50, + h: 50 + }); + player.x = 0; + player.y = 0; + 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(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"); + + // Clean up + Crafty("*").destroy(); +}); + + + +test("circle", function() { + var player = Crafty.e("2D, DOM, Color").attr({ + w: 50, + h: 50 + }).color("red"); + 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"); + + 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"); + // Clean up + Crafty("*").destroy(); +}); + +test("child", function() { + var parent0 = Crafty.e("2D, DOM, Color").attr({ + x: 0, + y: 0, + w: 50, + h: 50 + }).color("red"); + var child0 = Crafty.e("2D, DOM, Color").attr({ + x: 1, + y: 1, + w: 50, + h: 50 + }).color("red"); + var child1 = Crafty.e("2D, DOM, Color").attr({ + x: 2, + y: 2, + w: 50, + h: 50 + }).color("red"); + var child2 = Crafty.e("2D, DOM, Color").attr({ + x: 3, + y: 3, + w: 50, + h: 50 + }).color("red"); + var child3 = Crafty.e("2D, DOM, Color").attr({ + x: 4, + y: 4, + w: 50, + h: 50 + }).color("red"); + var child0_ID = child0[0]; + var child1_ID = child1[0]; + var child2_ID = child2[0]; + var child3_ID = child3[0]; + parent0.attach(child0); + parent0.attach(child1); + 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'); + child0.x += 1; + child1.x += 1; + 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'); + parent0.destroy(); + strictEqual(Crafty(child0_ID).length, 0, 'destruction of parent killed child0'); + strictEqual(Crafty(child2_ID).length, 0, 'destruction of parent killed child2'); + strictEqual(Crafty(child3_ID).length, 0, 'destruction of parent killed child3'); + // Clean up + Crafty("*").destroy(); +}); + +test("child_rotate", function() { + var Round = function(x) { + return Math.round(x * 100) / 100 + }; + var parent = Crafty.e("2D, DOM, Color") + .attr({ + x: 0, + y: 0, + w: 50, + h: 50, + rotation: 10 + }) + .color("red"); + var child = Crafty.e("2D, DOM, Color") + .attr({ + x: 10, + y: 10, + w: 50, + h: 50, + rotation: 15 + }) + .color("red"); + parent.attach(child); + + parent.rotation += 20; + 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'); + + 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).") + + // Clean up + Crafty("*").destroy(); +}); + + + +// This test assumes that the "circles" are really octagons, as per Crafty.circle. +test("SAT", function() { + 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])") + // Clean up + Crafty("*").destroy(); +}); + + +test("adjustable boundary", function() { + e = Crafty.e("2D").attr({ + x: 10, + y: 10, + w: 10, + h: 10 + }) + + // Four argument version + e.offsetBoundary(10, 1, 3, 0); + equal(e._bx1, 10, "X1 boundary set"); + equal(e._bx2, 3, "X2 boundary set"); + equal(e._by1, 1, "Y1 boundary set"); + equal(e._by2, 0, "Y2 boundary set"); + + e._calculateMBR(10, 10, 0) + + var mbr = e._mbr; + + equal(mbr._h, 11, "MBR height uses boundaries (11)"); + equal(mbr._w, 23, "MBR width uses boundaries (23)"); + + // One argument version + e.offsetBoundary(5); + equal(e._bx1, 5, "X1 boundary set"); + equal(e._bx2, 5, "X2 boundary set"); + equal(e._by1, 5, "Y1 boundary set"); + equal(e._by2, 5, "Y2 boundary set"); + + + + Crafty("*").destroy(); + + +}) + + +test("disableControl and enableControl", function() { + var e = Crafty.e("2D, Twoway") + .attr({ + x: 0 + }) + .twoway(1); + + equal(e._movement.x, 0); + equal(e._x, 0); + Crafty.trigger('KeyDown', { + key: Crafty.keys.D + }); + Crafty.trigger('EnterFrame'); + equal(e._movement.x, 1); + equal(e._x, 1); + + e.disableControl(); + Crafty.trigger('EnterFrame'); + equal(e._movement.x, 1); + equal(e._x, 1); + + Crafty.trigger('KeyUp', { + key: Crafty.keys.D + }); + Crafty.trigger('EnterFrame'); + equal(e._movement.x, 0); + equal(e._x, 1); + + e.enableControl(); + Crafty.trigger('EnterFrame'); + equal(e._movement.x, 0); + equal(e._x, 1); + + Crafty.trigger('KeyDown', { + key: Crafty.keys.D + }); + Crafty.trigger('EnterFrame'); + equal(e._movement.x, 1); + equal(e._x, 2); + + Crafty.trigger('KeyUp', { + key: Crafty.keys.D + }); + Crafty.trigger('EnterFrame'); + equal(e._movement.x, 0); + equal(e._x, 2); + + e.destroy(); +}); + + +test("Resizing 2D objects & hitboxes", function() { + var e = Crafty.e("2D, Collision"); + e.attr({ + x: 0, + y: 0, + w: 40, + h: 50 + }) + + equal(e.map.points[0][0], 0, "Before rotation: x_0 is 0") + equal(e.map.points[0][1], 0, "y_0 is 0") + equal(e.map.points[2][0], 40, "x_2 is 40") + equal(e.map.points[2][1], 50, "y_2 is 50") + + e.rotation = 90; + + equal(Math.round(e.map.points[0][0]), 0, "After rotation by 90 deg: x_0 is 0") + equal(Math.round(e.map.points[0][1]), 0, "y_0 is 0") + equal(Math.round(e.map.points[2][0]), -50, "x_2 is -50") + equal(Math.round(e.map.points[2][1]), 40, "y_2 is 40") + + // After rotation the MBR will have changed + equal(Math.round(e._mbr._w), 50, "_mbr._w is 50"); + equal(Math.round(e._mbr._h), 40, "_mbr._h is 40"); + equal(Math.round(e._mbr._x), -50, "_mbr._x is -50"); + equal(Math.round(e._mbr._y), 0, "_mbr._y is 0"); + + e.collision(); // Check that regenerating the hitbox while rotated works correctly + + equal(Math.round(e.map.points[0][0]), 0, "After rotation and hitbox regeneration: x_0 is 0") + equal(Math.round(e.map.points[0][1]), 0, "y_0 is 0") + equal(Math.round(e.map.points[2][0]), -50, "x_2 is -50") + equal(Math.round(e.map.points[2][1]), 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; + + equal(Math.round(e.map.points[0][0]), 0, "After rotation and increase in width: x_0 is 0") + equal(Math.round(e.map.points[0][1]), 0, "y_0 is 0") + equal(Math.round(e.map.points[2][0]), -50, "x_2 is -50") + equal(Math.round(e.map.points[2][1]), 100, "y_2 is 100") + + // After rotation the MBR will have changed + equal(Math.round(e._mbr._w), 50, "_mbr._w is 50"); + equal(Math.round(e._mbr._h), 100, "_mbr._h is 100"); + equal(Math.round(e._mbr._x), -50, "_mbr._x is -50"); + equal(Math.round(e._mbr._y), 0, "_mbr._y is 0"); + + e.destroy(); +}); + +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); + + ok(e._cbr !== null, "_cbr exists"); + var cbr = e._cbr; + // Test whether cbr actually bounds hitbox+object + ok(cbr._x <= 42, "cbr x position correct"); + ok(cbr._y <= 36, "cbr y position correct"); + ok(cbr._x + cbr._w >= 74, "cbr width correct"); + ok(cbr._y + cbr._h >= 66, "cbr height correct"); + + var x0 = cbr._x, + y0 = cbr._y; + + e.x += 10; + e.y += 15; + + equal(cbr._x, x0 + 10, "cbr x position moves correctly"); + equal(cbr._y, y0 + 15, "cbr y position moves correctly"); + Crafty("*").destroy(); +}) + +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); + + ok(e._cbr === null, "_cbr should not exist"); + + e.w = 10; + + ok(e._cbr !== null, "_cbr should now exist after entity shrinks"); + + e.w = 20; + + ok(e._cbr === null, "_cbr should not exist after entity grows again"); + + Crafty("*").destroy(); +}) + +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); + + 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"); + + +}) + +module("DebugLayer"); +test("DebugCanvas", function() { + if (!(Crafty.support.canvas)) { + expect(0); + return; + } + var e = Crafty.e("2D, DebugCanvas"); + var ctx = Crafty.DebugCanvas.context; + + e.debugFill("purple"); + equal(e._debug.fillStyle, "purple", "fill style set correctly on entity"); + + e.debugStroke("green") + equal(e._debug.strokeStyle, "green", "stroke style set correctly on entity"); + + e.debugDraw(ctx); + equal(ctx.fillStyle, "#800080", "context.fillStyle set correctly on draw"); // fillStyle will report the hex code + equal(ctx.strokeStyle, "#008000", "context.strokeStyle set correctly on draw"); + + e.debugFill(); + equal(e._debug.fillStyle, "red", "default fill style set correctly"); + + e.debugStroke(); + equal(e._debug.strokeStyle, "red", "default stroke style set correctly"); + + + e.destroy(); + +}); + +test("VisibleMBR and DebugRect", function() { + var e = Crafty.e("2D, VisibleMBR").attr({ + x: 10, + y: 10, + w: 10, + h: 20 + }); + e._assignRect(); + equal(e.debugRect._x, 10, "debugRect has correct x coord"); + equal(e.debugRect._h, 20, "debugRect has correct height"); + + e.rotation = 90; + e._assignRect(); + equal(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(); + e.matchHitBox(); // only necessary until collision works properly! + equal(e.polygon.points[0][0], 10, "WiredHitBox -- correct x coord for upper right corner"); + equal(e.polygon.points[2][1], 30, "correct y coord for lower right corner"); + notEqual(typeof e._debug.strokeStyle, "undefined", "stroke style is assigned"); + equal(typeof e._debug.fillStyle, "undefined", "fill style is undefined"); + + e.destroy(); + + var e = Crafty.e("2D, Collision, SolidHitBox").attr({ + x: 10, + y: 10, + w: 10, + h: 20 + }).collision(); + e.matchHitBox(); // only necessary until collision works properly! + equal(e.polygon.points[0][0], 10, "SolidHitBox -- correct x coord for upper right corner"); + equal(e.polygon.points[2][1], 30, "correct y coord for lower right corner"); + equal(typeof e._debug.strokeStyle, "undefined", "stroke style is undefined"); + notEqual(typeof e._debug.fillStyle, "undefined", "fill style is assigned"); + + e.collision(new Crafty.polygon([0, 0], [15, 0], [0, 15])); + e.matchHitBox(); + equal(e.polygon.points[2][1], 25, "After change -- correct y coord for third point"); + + e.destroy(); + +}); + +module("Easing"); +test("Crafty.easing duration", function() { + var e = new Crafty.easing(80); // 4 frames == 80ms by default + equal(e.duration, 80, "Default duration in ms"); +}) +test("Crafty.easing", function() { + var e = new Crafty.easing(80); // 4 frames == 80ms by default + e.tick(20); + e.tick(20); + equal(e.value(), 0.5, ".5 after two steps"); + e.tick(20); + e.tick(20); + equal(e.value(), 1, "1 after completed") + e.tick(20); + equal(e.value(), 1, "Remains 1 after completion") +}) \ No newline at end of file diff --git a/tests/dom.js b/tests/dom.js new file mode 100644 index 00000000..7e9a2fe4 --- /dev/null +++ b/tests/dom.js @@ -0,0 +1,50 @@ +module("DOM"); + +test("avoidCss3dTransforms", function() { + var useCss3dTransforms = Crafty.e("2D, DOM") + .attr({ + x: 10, + y: 10 + }) + .draw(); + + strictEqual(useCss3dTransforms.avoidCss3dTransforms, false); + if (Crafty.support.css3dtransform) { + strictEqual(useCss3dTransforms._cssStyles.transform, "translate3d(10px,10px,0)"); + strictEqual(useCss3dTransforms._cssStyles.top, ""); + strictEqual(useCss3dTransforms._cssStyles.left, ""); + } else { + strictEqual(avoidCss3dTransforms._cssStyles.transform, ""); + strictEqual(avoidCss3dTransforms._cssStyles.top, 10); + strictEqual(avoidCss3dTransforms._cssStyles.left, 10); + } + + var avoidCss3dTransforms = Crafty.e("2D, DOM") + .attr({ + x: 10, + y: 10, + avoidCss3dTransforms: true + }) + .draw(); + + strictEqual(avoidCss3dTransforms.avoidCss3dTransforms, true); + strictEqual(avoidCss3dTransforms._cssStyles.transform, ""); + strictEqual(avoidCss3dTransforms._cssStyles.top, 10); + strictEqual(avoidCss3dTransforms._cssStyles.left, 10); + // Clean up + Crafty("*").destroy(); +}); + +test("removeComponent removes element class", function() { + var element = Crafty.e("DOM"); + hasClassName = function(el, name) { + return el._element.className.indexOf(name) >= 0; + }; + element.addComponent("removeMe"); + strictEqual(element.has("removeMe"), true, "component added"); + strictEqual(hasClassName(element, "removeMe"), true, "classname added"); + + element.removeComponent("removeMe"); + strictEqual(element.has("removeMe"), false, "component removed"); + strictEqual(hasClassName(element, "removeMe"), false, "classname removed"); +}); \ No newline at end of file diff --git a/tests/events.js b/tests/events.js new file mode 100644 index 00000000..8d2f5531 --- /dev/null +++ b/tests/events.js @@ -0,0 +1,190 @@ +module("EVENTS"); + +test("Global binding events", function() { + var x = 0; + + function add() { + x++ + } + + Crafty.bind("Increment", add) + Crafty.trigger("Increment") + strictEqual(x, 1, "Crafty.bind fired once"); + + + x = 0; + Crafty.unbind("Increment", add) + Crafty.trigger("Increment") + strictEqual(x, 0, "Crafty.bind does not fire once unbound"); + + x = 0; + Crafty.one("Increment", add) + Crafty.trigger("Increment") + Crafty.trigger("Increment") + strictEqual(x, 1, "Event bound by Crafty.one fires exactly once"); + + x = 0; + Crafty.uniqueBind("Increment", add); + Crafty.uniqueBind("Increment", add); + Crafty.trigger("Increment"); + 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() { + x++ + } + var e = Crafty.e("Triggerable") + + e.bind("Increment", add) + e.trigger("Increment") + strictEqual(x, 1, ".bind fired once"); + + + x = 0; + e.unbind("Increment", add) + e.trigger("Increment") + strictEqual(x, 0, ".bind does not fire once unbound"); + + x = 0; + e.one("Increment", add) + e.trigger("Increment") + e.trigger("Increment") + strictEqual(x, 1, "Event bound by .one fires exactly once"); + + x = 0; + e.uniqueBind("Increment", add); + e.uniqueBind("Increment", add); + e.trigger("Increment"); + strictEqual(x, 1, "Event bound twice by .uniqueBound fires only once"); + + x = 0; + e.unbind("Increment", add); + e.trigger("Increment") + strictEqual(x, 0, "uniqueBound does not fire once unbound"); + + e.destroy(); +}); + +test("Multiple bound events", function() { + + //Test with entity trigger + var temp = Crafty.e('Triggerable'); + temp.xyz = 0; + temp.abc = 0; + temp.def = 0; + temp.one('Event A', function() { + this.xyz++; + }); + temp.bind('Event A', function() { + this.abc++; + }); + temp.one('Event A', function() { + this.def++; + }); + 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.xyz = 0; + temp.abc = 0; + temp.def = 0; + temp.one('Event A', function() { + this.xyz++; + }); + temp.bind('Event A', function() { + this.abc++; + }); + 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"); + strictEqual(temp.abc, 2, "regular event should trigger twice"); + strictEqual(temp.def, 1, "second one() should trigger once"); + temp.destroy(); + + //Test with global trigger, events bound on global + temp = Crafty; + temp.xyz = 0; + temp.abc = 0; + temp.def = 0; + temp.one('Event A', function() { + this.xyz++; + }); + temp.bind('Event A', function() { + this.abc++; + }); + 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"); + strictEqual(temp.abc, 2, "regular event should trigger twice"); + strictEqual(temp.def, 1, "second one() should trigger once"); + + Crafty.unbind("Event A") + +}); + +test("Data passing", function() { + var x = 0, + e; + + function add(data) { + x += data.amount + } + + x = 0; + e = Crafty.e("Triggerable") + e.bind("Increment", add) + e.trigger("Increment", { + amount: 2 + }) + strictEqual(x, 2, "data passed correctly with .bind"); + e.destroy(); + + x = 0; + e = Crafty.e("Triggerable") + e.one("Increment", add) + e.trigger("Increment", { + amount: 2 + }) + strictEqual(x, 2, "data passed correctly with .one"); + e.destroy(); + + x = 0; + Crafty.bind("Increment", add) + Crafty.trigger("Increment", { + amount: 2 + }) + strictEqual(x, 2, "data passed correctly with Crafty.bind"); + Crafty.unbind("Increment") + + x = 0; + Crafty.one("Increment", add) + Crafty.trigger("Increment", { + amount: 3 + }) + strictEqual(x, 3, "data passed correctly with Crafty.one"); + Crafty.unbind("Increment") + + + +}); \ No newline at end of file diff --git a/tests/isometric.js b/tests/isometric.js new file mode 100644 index 00000000..bfa01727 --- /dev/null +++ b/tests/isometric.js @@ -0,0 +1,84 @@ +module("ISOMETRIC"); + +test("place tile", function() { + var iso = Crafty.isometric.size(64, 16); + + var tile1 = Crafty.e("2D, DOM, Color").attr({ + x: 0, + y: 0, + w: 64, + h: 16 + }).color("red"); + var tile2 = Crafty.e("2D, DOM, Color").attr({ + x: 100, + y: 100, + z: 3, + w: 64, + h: 16 + }).color("blue"); + + iso.place(0, 0, 0, tile1); + iso.place(1, 2, 5, tile2); + + equal(tile1.attr('x'), 0, "First tile should default to origin"); + equal(tile1.attr('y'), 0, "First tile should default to origin"); + equal(tile1.attr('z'), 0, "z-index should be transferred unchanged"); + + equal(tile2.attr('x'), 64 + Crafty.viewport._x, "Each tile should be offset by the sum of the width of those before it"); + equal(tile2.attr('y'), -24 + Crafty.viewport._y, "The row should be offset by one and a half times the height"); + equal(tile2.attr('z'), 8, "z-index should be added to existing value"); + + // Clean up + Crafty("*").destroy(); +}); + +test("pos2px", function() { + var iso = Crafty.isometric.size(64, 16); + + var origin = iso.pos2px(0, 0); + equal(origin.left, 0, "First tile should default to origin"); + equal(origin.top, 0, "First tile should default to origin"); + + var oddNumberedRow = iso.pos2px(0, 1); + equal(oddNumberedRow.left, 32, "Odd numbered rows should be be inset by half the width"); + equal(oddNumberedRow.top, 8, "Each row should move down by half the height"); + + var evenNumberedRow = iso.pos2px(0, 2); + equal(evenNumberedRow.left, 0, "Even numbered rows should not be be inset"); + equal(evenNumberedRow.top, 16, "Each row should move down by half the height"); + + var numberedColumn = iso.pos2px(3, 0); + equal(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); + equal(origin.x, 0, "Origin should be the corner of the lowest numbered tile"); + equal(origin.y, 0, "Origin should be the corner of the lowest numbered tile"); + + var oddNumberedRow = iso.px2pos(32, 8); + equal(oddNumberedRow.x, 0, "Odd numbered rows should be be inset by half the width"); + equal(oddNumberedRow.y, 1, "Each row should move down by half the height"); + + var evenNumberedRow = iso.px2pos(0, 16); + equal(evenNumberedRow.x, 0, "Even numbered rows should not be be inset"); + equal(evenNumberedRow.y, 2, "Each row should move down by half the height"); + + var numberedColumn = iso.px2pos(128, 0); + equal(numberedColumn.x, 2, "Should be inset by the width times the x position"); +}); + +test("round trip conversions", function() { + var iso = Crafty.isometric.size(64, 16); + + var startX = 14; + var startY = 21; + + var startingPoint = iso.pos2px(startX, startY); + var end = iso.px2pos(startingPoint.left, startingPoint.top); + + equal(end.x, startX, "x position should match"); + equal(end.y, startY, "y position should match"); +}); \ No newline at end of file diff --git a/tests/loader.js b/tests/loader.js new file mode 100644 index 00000000..928bbd49 --- /dev/null +++ b/tests/loader.js @@ -0,0 +1,14 @@ +module('LOADER'); + +asyncTest('assets loading', function() { + expect(1); + + var onload; + + Crafty.load(['assets/100x100.png'], function() { + ok(typeof onload !== 'undefined' && onload.src.match('assets/100x100.png'), '100x100.png has been loaded'); + start(); + }, function(data) { + onload = data; + }); +}); \ No newline at end of file diff --git a/tests/math.js b/tests/math.js new file mode 100644 index 00000000..21f20351 --- /dev/null +++ b/tests/math.js @@ -0,0 +1,310 @@ +var Matrix2D = Crafty.math.Matrix2D; +var Vector2D = Crafty.math.Vector2D; + +// tests for general functions should go here (.abs(), .amountOf(), etc) + +module("Vector2D"); + +test("constructor", function() { + var v0 = new Vector2D(); + var v00 = new Vector2D(0, 0); + var v12 = new Vector2D(1, 2); + var v12_2 = new Vector2D(v12); + + equal(v0.equals(v00), true, "new Vector2D() equals new Vector2D(0, 0)"); + equal(v12.equals(v12_2), true, "new Vector2D(1, 2) equals new Vector2D(new Vector2D(1,2))"); +}); + +test("add()", function() { + var v12 = new Vector2D(1, 2); + var v34 = new Vector2D(3, 4); + var v = new Vector2D(4, 6); + + equal(v12.add(v34).equals(v), true, "<1,2> + <3,4> = <4,6>"); +}); + +test("angleBetween()", function() { + var v10 = new Vector2D(1, 0); + var v_11 = new Vector2D(-1, 1); + var v1_1 = new Vector2D(1, -1); + + equal(v10.angleBetween(v_11), 3 * Math.PI / 4, "<1,0>.angleBetween(<0,1>) = 3*PI/4"); + equal(v10.angleBetween(v1_1), -Math.PI / 4, "<1,0>.angleBetween(<1,-1>) = -PI/4"); +}); + +test("angleTo()", function() { + var v0 = new Vector2D(); + var v11 = new Vector2D(1, 1); + var v10 = new Vector2D(1, 0); + var v0_1 = new Vector2D(0, -1); + + equal(v0.angleTo(v11), Math.PI / 4, "<0,0>.angleTo(<1,1>) = PI/4"); + equal(v10.angleTo(v0_1), -3 * Math.PI / 4, "<1,0>.angleTo(<0,-1>) = -3*PI/4"); +}); + +test("clone()", function() { + var v0 = new Vector2D(); + var v3_7 = new Vector2D(3, -7); + + equal(v0.equals(v0.clone()), true, "<0,0> = <0,0>.clone()"); + equal(v3_7.clone().equals(v3_7), true, "<3,-7>.clone() = <3,-7>"); +}); + +test("distance()", function() { + var v0 = new Vector2D(); + var v10 = new Vector2D(1, 0); + var v11 = new Vector2D(1, 1); + + equal(v10.distance(v11), 1, "<1,0>.distance(<1,1>) = 1"); + equal(v0.distance(v11), Math.sqrt(2), "<0,0>.distance(<1,1>) = sqrt(2)"); +}); + +test("distanceSq()", function() { + var v0 = new Vector2D(); + var v10 = new Vector2D(1, 0); + var v11 = new Vector2D(1, 1); + + equal(v10.distanceSq(v11), 1, "<1,0>.distanceSq(<1,1>) = 1"); + equal(v0.distanceSq(v11), 2, "<0,0>.distanceSq(<1,1>) = 2"); +}); + +test("divide()", function() { + var v12 = new Vector2D(1, 2); + var v34 = new Vector2D(3, 4); + var v = new Vector2D(1 / 3, 2 / 4); + + equal(v12.divide(v34).equals(v), true, "<1,2> / <3,4> = <1/3,1/2>"); +}); + +test("dotProduct()", function() { + var v12 = new Vector2D(1, 2); + var v34 = new Vector2D(3, 4); + var v46 = new Vector2D(4, 6); + + equal(v12.dotProduct(v34), 11, "<1,2>.dotProduct(<3,4>) = 11"); + equal(v34.dotProduct(v46), 36, "<3,4>.dotProduct(<4,6>) = 36"); + equal(v46.dotProduct(v12), 16, "<4,6>.dotProduct(<1,2>) = 16"); +}); + +test("equals()", function() { + var v12 = new Vector2D(1, 2); + var v34 = new Vector2D(3, 4); + var v46 = new Vector2D(4, 6); + + equal(v12.equals(new Vector2D(1, 2)), true, "<1,2>.equals(<1,2>) = true"); + equal(v34.equals(new Vector2D(3, 4)), true, "<3,4>.equals(<3,4>) = true"); + equal(v46.equals(new Vector2D(4, 6)), true, "<4,6>.equals(<4,6>) = true"); +}); + +test("getNormal()", function() { + var v10 = new Vector2D(1, 0); + var v32 = new Vector2D(3, 2); + + equal(v10.getNormal().equals(new Vector2D(0, 1)), true, "<1,0>.getNormal() = <0,1>"); + equal(v10.getNormal(v32).equals((new Vector2D(1, -1)).normalize()), true, "<1,0>.getNormal(<3,2>) = "); +}); + +test("isZero()", function() { + var v0 = new Vector2D(); + var v10 = new Vector2D(1, 0); + + equal(v0.isZero(), true, "<0,0>.isZero() = true"); + equal(v10.isZero(), false, "<1,0>.isZero() = false"); +}); + +test("magnitude()", function() { + var v0 = new Vector2D(); + var v10 = new Vector2D(1, 0); + var v_79 = new Vector2D(-7, 9); + + equal(v0.magnitude(), 0, "<0,0>.magnitude() = 0"); + equal(v10.magnitude(), 1, "<1,0>.magnitude() = 1"); + equal(v_79.magnitude(), 11.40175425099138, "<-7,9>.magnitude() = 11.40175425099138"); +}); + +test("magnitudeSq()", function() { + var v0 = new Vector2D(); + var v10 = new Vector2D(1, 0); + var v_79 = new Vector2D(-7, 9); + + equal(v0.magnitudeSq(), 0, "<0,0>.magnitudeSq() = 0"); + equal(v10.magnitudeSq(), 1, "<1,0>.magnitudeSq() = 1"); + equal(v_79.magnitudeSq(), 130, "<-7,9>.magnitudeSq() = 130"); +}); + +test("multiply()", function() { + var v12 = new Vector2D(1, 2); + var v34 = new Vector2D(3, 4); + var v = new Vector2D(3, 8); + + equal(v12.multiply(v34).equals(v), true, "<1,2> * <3,4> = <3,8>"); +}); + +test("negate()", function() { + var v_79 = new Vector2D(-7, 9); + var v7_9 = new Vector2D(7, -9); + + equal(v_79.negate().equals(v7_9), true, "<-7,9>.negate() = <7,-9>"); +}); + +test("normalize()", function() { + var v0 = new Vector2D(); + var v01 = new Vector2D(0, 1); + var v_79 = new Vector2D(-7, 9); + + equal(v0.normalize().equals(new Vector2D(1, 0)), true, "<0,0>.normalize() = <1,0>"); + equal(v01.normalize().equals(new Vector2D(0, 1)), true, "<1,0>.normalize() = <0,1>"); + equal(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); + + equal(v11.scale(2).equals(new Vector2D(2, 2)), true, "<1,1>.scale(2) = <2,2>"); + equal(v11.scale(2, -3).equals(new Vector2D(4, -6)), true, "<2,2>.scale(2, -3) = <4,-6>"); +}); + +test("magnitudeSq()", function() { + var v34 = new Vector2D(3, 4); + + equal(v34.normalize().scaleToMagnitude(5).equals(new Vector2D(3, 4)), true, "<3,4>.normalize().scaleToMagnitude(5) = <3,4>"); +}); + +test("setValues", function() { + var v0 = new Vector2D(); + var v12 = new Vector2D(1, 2); + var v44 = new Vector2D(4, 4); + + equal(v0.setValues(1, 2).equals(v12), true, "<0,0>.setValues(<1,2>) = <1,2>"); + equal(v0.setValues(v44).equals(v44), true, "<1,2>.setValues(<4,4>) = <4,4>"); +}); + +test("subtract()", function() { + var v12 = new Vector2D(1, 2); + var v34 = new Vector2D(3, 4); + var v = new Vector2D(-2, -2); + + equal(v12.subtract(v34).equals(v), true, "<1,2> - <3,4> = <-2,-2>"); +}); + +test("toString()", function() { + var v12 = new Vector2D(1, 2); + + equal(v12.toString(), "Vector2D(1, 2)", "<1,2> = \"Vector2D(1, 2)\""); +}); + +test("translate()", function() { + var v11 = new Vector2D(1, 1); + + equal(v11.translate(2).equals(new Vector2D(3, 3)), true, "<1,1>.translate(2) = <3,3>"); + equal(v11.translate(2, -3).equals(new Vector2D(5, 0)), true, "<2,2>.translate(2, -3) = <5,0>"); +}); + +test("tripleProduct()", function() { + var va = new Vector2D(1, 2); + var vb = new Vector2D(3, 4); + var vc = new Vector2D(5, 6); + var vtp = new Vector2D(12, -10); + + equal(Vector2D.tripleProduct(va, vb, vc).equals(vtp), true, "tripleProduct(<1,2>, <3,4>, <5,6>) = <10,-12>"); +}); + +module("Matrix2D"); + +test("apply()", function() { + equal((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() { + equal((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() { + equal((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() { + equal((new Matrix2D()).equals(new Matrix2D()), + true, "(new Matrix2D()).equals(new Matrix2D())"); + equal((new Matrix2D()).scale(2).equals(new Matrix2D()), + false, "(new Matrix2D()).scale(2).equals(new Matrix2D())"); +}); + +test("determinant()", function() { + equal((new Matrix2D()).scale(2, 3).rotate(Math.PI / 2).determinant(), + 6, "(new Matrix2D()).scale(2, 3).rotate(Math.PI / 2).determinant()"); +}); + +test("invert()", function() { + equal((new Matrix2D()).scale(2, 3).rotate(Math.PI / 2).invert().equals(new Matrix2D(3.061616997868383e-17, -0.3333333333333333, 0.5, 2.041077998578922e-17, 0, 0)), + true, "(new Matrix2D()).scale(2, 3).rotate(Math.PI / 2).invert().equals(new Matrix2D(3.061616997868383e-17, -0.3333333333333333, 0.5, 2.041077998578922e-17, 0, 0))"); +}); + +test("isIdentity()", function() { + equal((new Matrix2D()).isIdentity(), + true, "(new Matrix2D()).isIdentity()"); + equal((new Matrix2D()).scale(2).isIdentity(), + false, "(new Matrix2D()).scale(2).isIdentity()"); +}); + +test("isInvertible()", function() { + equal((new Matrix2D()).scale(2, 3).rotate(Math.PI / 2).isInvertible(), + true, "(new Matrix2D()).scale(2, 3).rotate(Math.PI / 2).isInvertible()"); + equal((new Matrix2D()).scale(0, 3).rotate(Math.PI / 2).isInvertible(), + false, "(new Matrix2D()).scale(0, 3).rotate(Math.PI / 2).isInvertible()"); +}); + +test("preRotate()", function() { + equal((new Matrix2D()).preRotate(0).equals(new Matrix2D()), + true, "(new Matrix2D()).preRotate(0).equals(new Matrix2D())"); + equal((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() { + equal((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))"); + equal((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() { + equal((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)"); + equal((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)))"); + equal((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))"); + equal((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() { + equal((new Matrix2D()).rotate(0).equals(new Matrix2D()), + true, "(new Matrix2D()).rotate(0).equals(new Matrix2D())"); +}); + +test("scale()", function() { + equal((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() { + equal((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() { + equal((new Matrix2D()).toString(), + "Matrix2D([1, 0, 0] [0, 1, 0] [0, 0, 1])", "(new Matrix2D()).toString()"); +}); + +test("translate()", function() { + equal((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))"); + equal((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 diff --git a/tests/sound.js b/tests/sound.js new file mode 100644 index 00000000..4a870e98 --- /dev/null +++ b/tests/sound.js @@ -0,0 +1,102 @@ +module("Audio"); + +//Set up some test fixtures +function MockAudio() { + var self = this + this.endedListeners = [] + this.canPlayType = function() { + return true + } + this.addEventListener = function(event, listener) { + switch (event) { + case "ended": + this.endedListeners.push(listener) + break + default: + throw new Exception("Not implemented") + } + } + this.removeEventListener = function(event, listener) { + switch (event) { + case "ended": + var ind = this.endedListeners.indexOf(listener) + if (ind) this.endedListeners.splice(ind, 1); + break + default: + throw new Exception("Not implemented") + } + } + + function fireEnded() { + setTimeout(function() { + self.ended = true + self.endedListeners.forEach(function(f) { + f.call(self) + }) + }, 0) + } + this.play = function() { + if (this.src) { + fireEnded() + } + } + this.pause = function() {} + this.ended = false +} + +function ChromeBuggedAudio() { + var self = this + this.canPlayType = function() { + return true + } + this.addEventListener = function(event, listener) {} + this.removeEventListener = function(event, listener) {} + this.play = function() { + if (this.src) { + self.ended = true + self.src = null + ok(true, "Audio played") + } + } + this.pause = function() {} + this.ended = false +} + +Crafty.init(); + +asyncTest("setChannels", function() { + // Test that setChannels doesn't break sound + expect(2) + window.Audio = MockAudio + Crafty.support.audio = true + Crafty.audio.setChannels(5) + Crafty.audio.add("mockSound", ["sound.ogg"]) + var a = Crafty.audio.play("mockSound", 1) + ok(typeof a === "object", "Type of a is object: " + a) + a.addEventListener("ended", function() { + ok(true, "Sound played") + delete window.Audio //reset Audio to platform default + Crafty.audio.channels = [] + Crafty("*").destroy(); + start() + }) +}); + +test("chromeBug", function() { + // Test that we don't exhaust our audio channels if Chrome bug 280417 + // eats our "ended" events + expect(10) + window.Audio = ChromeBuggedAudio + Crafty.support.audio = true + Crafty.audio.setChannels(1) + Crafty.support.audio = true + Crafty.audio.add("mockSound", ["sound.ogg"]) + + var a + for (var i = 0; i < 10; i++) { + a = Crafty.audio.play("mockSound", 1) // This will trigger an assertion + } + delete window.Audio //reset Audio to platform default + Crafty.audio.channels = [] + Crafty("*").destroy(); +}); \ No newline at end of file diff --git a/tests/stage.js b/tests/stage.js new file mode 100644 index 00000000..3f6975bf --- /dev/null +++ b/tests/stage.js @@ -0,0 +1,208 @@ +var reset = function() { + Crafty("*").destroy(); + Crafty.viewport.reset(); + Crafty.viewport.scroll('_x', 0); + Crafty.viewport.scroll('_y', 0); + Crafty.viewport.clampToEntities = true; + +} + +test("simulateFrames", function() { + var framesPlayed = 0; + Crafty.bind("EnterFrame", function() { + framesPlayed++; + }); + Crafty.timer.simulateFrames(1); + equal(framesPlayed, 1, "A frame should have been simulated"); + + Crafty.timer.simulateFrames(100); + equal(framesPlayed, 101, "101 frames should have been simulated"); +}) + + + +module("Viewport", { + setup: reset +}); + +test("scroll using _x, _y", function() { + var e = Crafty.e("2D, DOM").attr({ + x: 50, + y: 50 + }); + var before = Crafty.DOM.translate(e.x, e.y); + + Crafty.viewport.scroll('_x', 100); + equal(before.x - Crafty.DOM.translate(e.x, e.y).x, 100, "Scroll in x direction"); + + Crafty.viewport.scroll('_y', 70); + equal(before.y - Crafty.DOM.translate(e.x, e.y).y, 70, "Scroll in y direction"); + + Crafty.viewport.scroll('_x', 0); + Crafty.viewport.scroll('_y', 0); + equal(before.x - Crafty.DOM.translate(e.x, e.y).x, 0, "Scroll to 0"); + equal(before.y - Crafty.DOM.translate(e.x, e.y).y, 0, "Scroll to 0"); +}); + +test("scroll using x, y", function() { + var e = Crafty.e("2D, DOM").attr({ + x: 50, + y: 50 + }); + var before = Crafty.DOM.translate(e.x, e.y); + + Crafty.viewport.scroll('x', 100); + equal(before.x - Crafty.DOM.translate(e.x, e.y).x, 100, "Scroll in x direction"); + + Crafty.viewport.scroll('y', 70); + equal(before.y - Crafty.DOM.translate(e.x, e.y).y, 70, "Scroll in y direction"); + + Crafty.viewport.scroll('x', 0); + Crafty.viewport.scroll('y', 0); + equal(before.x - Crafty.DOM.translate(e.x, e.y).x, 0, "Scroll to 0"); + equal(before.y - Crafty.DOM.translate(e.x, e.y).y, 0, "Scroll to 0"); +}); + +test("follow", function() { + Crafty.viewport.clampToEntities = false; + var e = Crafty.e("2D, DOM").attr({ + x: Crafty.viewport.width + 100, + y: Crafty.viewport.height + 70 + }); + Crafty.viewport.follow(e, 0, 0); + equal(Crafty.viewport._x, (-(Crafty.viewport.width / 2 + 100)), "Center viewport on entity.x"); + equal(Crafty.viewport._y, (-(Crafty.viewport.height / 2 + 70)), "Center viewport on entity.y"); + +}); + +test("pan", function() { + Crafty.viewport.clampToEntities = false; + Crafty.e("2D, DOM").attr({ + x: 0, + y: 0, + w: Crafty.viewport.width * 2, + h: Crafty.viewport.height * 2 + }); + + var done = 0; + var panDone = function() { + done++ + }; + Crafty.one("CameraAnimationDone", panDone); + + Crafty.viewport.pan(100, 0, 10 * 20); + Crafty.timer.simulateFrames(5); + equal(Crafty.viewport._x, -50, "Pan half the way on half the time"); + equal(done, 0, "CameraAnimationDone hasn't fired yet"); + Crafty.timer.simulateFrames(5); + equal(Crafty.viewport._x, -100, "Pan all the way when all the time is spent"); + equal(done, 1, "CameraAnimationDone has fired once"); + + done = 0; + Crafty.one("CameraAnimationDone", panDone); + Crafty.viewport.pan(0, 100, 10); + Crafty.timer.simulateFrames(20); + equal(Crafty.viewport._y, -100, "Pan all the way and stay there"); + equal(done, 1, "CameraAnimationDone has fired once"); + + Crafty.viewport.scroll('x', 0); + Crafty.viewport.scroll('y', 0); +}); + +test("zoom", function() { + + Crafty.viewport.clampToEntities = false; + + var done = 0; + var panDone = function() { + done++ + }; + Crafty.one("CameraAnimationDone", panDone); + + Crafty.e("2D, DOM").attr({ + x: 0, + y: 0, + w: Crafty.viewport.width * 2, + h: Crafty.viewport.height * 2 + }); + Crafty.viewport.scroll('x', 0); + Crafty.viewport.scroll('y', 0); + Crafty.viewport.scale(1); + + Crafty.viewport.zoom(2, 0, 0, 10 * 20); + Crafty.timer.simulateFrames(5); + + equal(Crafty.viewport._scale, Math.sqrt(2), "Zooms sqrt(2) in half the time"); + equal(done, 0, "CameraAnimationDone hasn't fired yet"); + + Crafty.timer.simulateFrames(5); + equal(Crafty.viewport._x, Crafty.viewport.width / 4, "move all the way when all the time is spent"); + equal(Crafty.viewport._y, Crafty.viewport.height / 4, "move all the way when all the time is spent"); + equal(Crafty.viewport._scale, 2, "Zooms all the way in full time."); + equal(done, 1, "CameraAnimationDone has fired once"); + +}); + +test("centerOn", function() { + var e = Crafty.e("2D, DOM").attr({ + x: 0, + y: 0, + w: Crafty.viewport.width * 2, + h: Crafty.viewport.height * 2 + }); + Crafty.viewport.clampToEntities = false; + + var done = 0; + var panDone = function() { + done++ + }; + Crafty.one("CameraAnimationDone", panDone); + + + Crafty.viewport.centerOn(e, 10); + Crafty.timer.simulateFrames(10); + + equal(Crafty.viewport._x, -e.w / 2 + Crafty.viewport.width / 2, "Entity centered after exact duration"); + equal(done, 1, "CameraAnimationDone has fired once"); + done = 0; + Crafty.one("CameraAnimationDone", panDone); + Crafty.timer.simulateFrames(10); + equal(Crafty.viewport._x, -e.w / 2 + Crafty.viewport.width / 2, "Entity still centered 10 frames later"); + equal(done, 0, "CameraAnimationDone doesn't fire after completion"); + + var e2 = Crafty.e("2D, DOM").attr({ + x: 450, + y: 450, + w: 20, + h: 20 + }); + Crafty.viewport.scroll('x', 1500); + Crafty.viewport.scroll('y', 300); + Crafty.viewport.centerOn(e2, 1); + Crafty.timer.simulateFrames(1); + equal(Crafty.viewport._x, (-(e2.x + e2.w / 2 - Crafty.viewport.width / 2)), "Entity centered from non-zero origin"); + equal(Crafty.viewport._y, (-(e2.y + e2.h / 2 - Crafty.viewport.height / 2)), "Entity centered from non-zero origin"); + + Crafty.viewport.clampToEntities = true; + Crafty.viewport.scroll('x', 0); + Crafty.viewport.scroll('y', 0); +}); + +module("Crafty.timer") +test("curTime", 1, function() { + var startTime, lastKnownTime; + Crafty.e("").bind("EnterFrame", function(params) { + if (!startTime) { + startTime = params.gameTime; + } else { + lastKnownTime = params.gameTime; + } + }); + + setTimeout(function() { + var endTime = lastKnownTime; + ok(endTime > startTime, "EndTime " + endTime + " must be larger than StartTime " + startTime); + start(); + }, 100); + stop(); // pause the QUnit so the timeout has time to complete. +}); \ No newline at end of file diff --git a/tests/storage.js b/tests/storage.js new file mode 100644 index 00000000..be75b1e1 --- /dev/null +++ b/tests/storage.js @@ -0,0 +1,73 @@ +module("Storage"); +test("saveAndLoadObject", function() { + Crafty.storage.open("MyGame"); + Crafty.storage.save("LeaderBoard", "save", { + name: "Matthew", + score: 150 + }); + stop(); + Crafty.storage.load("LeaderBoard", "save", function(lb) { + equal(lb["name"], "Matthew"); + start(); + }); +}); + +test("saveAndLoadArray", function() { + Crafty.storage.open("MyGame1"); + Crafty.storage.save("LeaderBoard", "save", [{ + name: "Matthew", + score: 150 + }, { + name: "Louis", + score: 17 + }]); + stop(); + Crafty.storage.load("LeaderBoard", "save", function(lb) { + equal(lb[1]["name"], "Louis"); + equal(lb.length, 2); + start(); + }); +}); + +test("saveAndLoadEntity", function() { + Crafty.storage.open("MyGame2"); + Crafty.storage.save("Hero", "save", Crafty.e("2D, DOM").attr({ + x: 20, + y: 137 + })); + stop(); + Crafty.storage.load("Hero", "save", function(hero) { + console.log(hero); + ok(hero.__c["2D"]); + ok(hero.__c["DOM"]) + equal(hero.x, 0, "Entity state is not saved"); + start(); + }); +}); + +test("individualNamespaces", function() { + Crafty.storage.open("MyGame3"); + Crafty.storage.save("LeaderBoard", "save", { + name: "Matthew", + score: 150 + }); + + + Crafty.storage.open("MyGame4"); + Crafty.storage.save("LeaderBoard", "save", { + name: "Louis", + score: 150 + }); + + stop(); + Crafty.storage.load("LeaderBoard", "save", function(lb) { + equal(lb["name"], "Louis"); + start(); + }); + + Crafty.storage.open("MyGame3"); + Crafty.storage.load("LeaderBoard", "save", function(lb) { + equal(lb["name"], "Matthew"); + start(); + }); +}) \ No newline at end of file diff --git a/tests/text.js b/tests/text.js new file mode 100644 index 00000000..7caec16b --- /dev/null +++ b/tests/text.js @@ -0,0 +1,42 @@ +module("Text"); +test("fontFamily", function() { + var text = Crafty.e('DOM, Text').textFont({ + family: 'Times New Roman 400', + size: '30px' + }).text('Test'); + equal(text.attr('_textFont')['family'], "'Times New Roman 400'", 'Expect to have singlequotes arount the family property.'); + + Crafty("*").destroy(); +}); + +test("_getFontHeight", function() { + var e = Crafty.e("Text"); + var h = e._getFontHeight("10px"); + equal(h, 10, "Font height is 10 pixels"); + h = e._getFontHeight("10in"); + equal(h, 960, "Font height is 960 pixels"); + Crafty("*").destroy(); +}) + +test("Width of canvas element", function() { + var e = Crafty.e("2D, Canvas, Text"); + e.text("a"); + var w1 = e.w; + e.text("abc"); + var w2 = e.w; + ok(w2 > w1, "Entity increases in width when text is changed.") + Crafty("*").destroy(); +}) + +test("Height of canvas element", function() { + var e = Crafty.e("2D, Canvas, Text"); + e.text("a"); + e.textFont("size", "10"); + var h1 = e.h; + ok(h1 > 10, "Font height set correctly.") + e.textFont("size", "20"); + var h2 = e.h; + ok(h2 > 20, "Font height set correctly.") + ok(h2 > h1, "Entity increases in height when font size is increased.") + Crafty("*").destroy(); +}) \ No newline at end of file diff --git a/tests/tween.js b/tests/tween.js new file mode 100644 index 00000000..f25c1722 --- /dev/null +++ b/tests/tween.js @@ -0,0 +1,58 @@ +module("Tween"); +test("Tween", function() { + 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 + equal(ret, e, ".tween() returned self correctly"); + Crafty.timer.simulateFrames(5); + equal(Round(e.x), 5, "Halfway tweened x"); + equal(Round(e.y), 13, "Halfway tweened y"); + Crafty.timer.simulateFrames(10); + equal(e.x, 10, "Fully tweened x"); + equal(e.y, 16, "Fully tweened y"); +}); +asyncTest('correct tweening', function() { + expect(1); + + var e1 = Crafty.e('2D, Tween') + .attr({ + x: 0, + y: 0 + }) + .tween({ + x: 100 + }, 50); + e1.bind('TweenEnd', function() { + ok(this.x === 100); + start(); + }); +}); + +asyncTest('correct tweening with multiple entities', function() { + expect(1); + + var e1 = Crafty.e('2D, Tween') + .attr({ + x: 0, + y: 0 + }) + .tween({ + x: 100 + }, 50); + var e2 = Crafty.e('2D, Tween') + .attr({ + x: 0, + y: 0 + }) + .tween({ + x: 100 + }, 50); + e1.bind('TweenEnd', function() { + ok(this.x === 100); + start(); + }); +}); \ No newline at end of file From 34c0df994221ce19563e40327760caa2ca50a544 Mon Sep 17 00:00:00 2001 From: Kevin Simper Date: Thu, 2 Jan 2014 10:38:06 +0100 Subject: [PATCH 38/61] Made one index.html file which is easier to maintain and made it use HTML5 document mode --- Gruntfile.js | 12 +------ tests/index.html | 58 ++++++++++++++++++++++++++++++++ tests/lib/modernizr-2.7.1.min.js | 4 +++ 3 files changed, 63 insertions(+), 11 deletions(-) create mode 100755 tests/index.html create mode 100644 tests/lib/modernizr-2.7.1.min.js diff --git a/Gruntfile.js b/Gruntfile.js index 6e7a6cca..12779a80 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -86,17 +86,7 @@ module.exports = function (grunt) { qunit: { all: [ - 'tests/core.html', - 'tests/animation/animation.html', - 'tests/stage.html', - 'tests/events.html', - 'tests/math.html', - 'tests/isometric.html', - 'tests/loader.html', - 'tests/text.html', - 'tests/dom.html', - 'tests/tween.html', - 'tests/sound.html' + 'tests/index.js' ] }, diff --git a/tests/index.html b/tests/index.html new file mode 100755 index 00000000..65eba487 --- /dev/null +++ b/tests/index.html @@ -0,0 +1,58 @@ + + + + + + + + + + +   + + + + + + + +

      Crafty: Core

      +

      +
      +

      +
        +
        test markup, will be hidden
        + + + + + + + + + + + + + + + + + + + + diff --git a/tests/lib/modernizr-2.7.1.min.js b/tests/lib/modernizr-2.7.1.min.js new file mode 100644 index 00000000..58c6829b --- /dev/null +++ b/tests/lib/modernizr-2.7.1.min.js @@ -0,0 +1,4 @@ +/* Modernizr 2.7.1 (Custom Build) | MIT & BSD + * Build: http://modernizr.com/download/#-fontface-backgroundsize-borderimage-borderradius-boxshadow-flexbox-hsla-multiplebgs-opacity-rgba-textshadow-cssanimations-csscolumns-generatedcontent-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-applicationcache-canvas-canvastext-draganddrop-hashchange-history-audio-video-indexeddb-input-inputtypes-localstorage-postmessage-sessionstorage-websockets-websqldatabase-webworkers-geolocation-inlinesvg-smil-svg-svgclippaths-touch-webgl-shiv-mq-cssclasses-addtest-prefixed-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-load + */ +;window.Modernizr=function(a,b,c){function D(a){j.cssText=a}function E(a,b){return D(n.join(a+";")+(b||""))}function F(a,b){return typeof a===b}function G(a,b){return!!~(""+a).indexOf(b)}function H(a,b){for(var d in a){var e=a[d];if(!G(e,"-")&&j[e]!==c)return b=="pfx"?e:!0}return!1}function I(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:F(f,"function")?f.bind(d||b):f}return!1}function J(a,b,c){var d=a.charAt(0).toUpperCase()+a.slice(1),e=(a+" "+p.join(d+" ")+d).split(" ");return F(b,"string")||F(b,"undefined")?H(e,b):(e=(a+" "+q.join(d+" ")+d).split(" "),I(e,b,c))}function K(){e.input=function(c){for(var d=0,e=c.length;d',a,""].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},z=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b).matches;var d;return y("@media "+b+" { #"+h+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},A=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;return f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=F(e[d],"function"),F(e[d],"undefined")||(e[d]=c),e.removeAttribute(d))),e=null,f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),B={}.hasOwnProperty,C;!F(B,"undefined")&&!F(B.call,"undefined")?C=function(a,b){return B.call(a,b)}:C=function(a,b){return b in a&&F(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=w.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(w.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(w.call(arguments)))};return e}),s.flexbox=function(){return J("flexWrap")},s.canvas=function(){var a=b.createElement("canvas");return!!a.getContext&&!!a.getContext("2d")},s.canvastext=function(){return!!e.canvas&&!!F(b.createElement("canvas").getContext("2d").fillText,"function")},s.webgl=function(){return!!a.WebGLRenderingContext},s.touch=function(){var c;return"ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch?c=!0:y(["@media (",n.join("touch-enabled),("),h,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(a){c=a.offsetTop===9}),c},s.geolocation=function(){return"geolocation"in navigator},s.postmessage=function(){return!!a.postMessage},s.websqldatabase=function(){return!!a.openDatabase},s.indexedDB=function(){return!!J("indexedDB",a)},s.hashchange=function(){return A("hashchange",a)&&(b.documentMode===c||b.documentMode>7)},s.history=function(){return!!a.history&&!!history.pushState},s.draganddrop=function(){var a=b.createElement("div");return"draggable"in a||"ondragstart"in a&&"ondrop"in a},s.websockets=function(){return"WebSocket"in a||"MozWebSocket"in a},s.rgba=function(){return D("background-color:rgba(150,255,150,.5)"),G(j.backgroundColor,"rgba")},s.hsla=function(){return D("background-color:hsla(120,40%,100%,.5)"),G(j.backgroundColor,"rgba")||G(j.backgroundColor,"hsla")},s.multiplebgs=function(){return D("background:url(https://),url(https://),red url(https://)"),/(url\s*\(.*?){3}/.test(j.background)},s.backgroundsize=function(){return J("backgroundSize")},s.borderimage=function(){return J("borderImage")},s.borderradius=function(){return J("borderRadius")},s.boxshadow=function(){return J("boxShadow")},s.textshadow=function(){return b.createElement("div").style.textShadow===""},s.opacity=function(){return E("opacity:.55"),/^0.55$/.test(j.opacity)},s.cssanimations=function(){return J("animationName")},s.csscolumns=function(){return J("columnCount")},s.cssgradients=function(){var a="background-image:",b="gradient(linear,left top,right bottom,from(#9f9),to(white));",c="linear-gradient(left top,#9f9, white);";return D((a+"-webkit- ".split(" ").join(b+a)+n.join(c+a)).slice(0,-a.length)),G(j.backgroundImage,"gradient")},s.cssreflections=function(){return J("boxReflect")},s.csstransforms=function(){return!!J("transform")},s.csstransforms3d=function(){var a=!!J("perspective");return a&&"webkitPerspective"in g.style&&y("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(b,c){a=b.offsetLeft===9&&b.offsetHeight===3}),a},s.csstransitions=function(){return J("transition")},s.fontface=function(){var a;return y('@font-face {font-family:"font";src:url("https://")}',function(c,d){var e=b.getElementById("smodernizr"),f=e.sheet||e.styleSheet,g=f?f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"":"";a=/src/i.test(g)&&g.indexOf(d.split(" ")[0])===0}),a},s.generatedcontent=function(){var a;return y(["#",h,"{font:0/0 a}#",h,':after{content:"',l,'";visibility:hidden;font:3px/1 a}'].join(""),function(b){a=b.offsetHeight>=3}),a},s.video=function(){var a=b.createElement("video"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),c.h264=a.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),c.webm=a.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,"")}catch(d){}return c},s.audio=function(){var a=b.createElement("audio"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),c.mp3=a.canPlayType("audio/mpeg;").replace(/^no$/,""),c.wav=a.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),c.m4a=(a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;")).replace(/^no$/,"")}catch(d){}return c},s.localstorage=function(){try{return localStorage.setItem(h,h),localStorage.removeItem(h),!0}catch(a){return!1}},s.sessionstorage=function(){try{return sessionStorage.setItem(h,h),sessionStorage.removeItem(h),!0}catch(a){return!1}},s.webworkers=function(){return!!a.Worker},s.applicationcache=function(){return!!a.applicationCache},s.svg=function(){return!!b.createElementNS&&!!b.createElementNS(r.svg,"svg").createSVGRect},s.inlinesvg=function(){var a=b.createElement("div");return a.innerHTML="",(a.firstChild&&a.firstChild.namespaceURI)==r.svg},s.smil=function(){return!!b.createElementNS&&/SVGAnimate/.test(m.call(b.createElementNS(r.svg,"animate")))},s.svgclippaths=function(){return!!b.createElementNS&&/SVGClipPath/.test(m.call(b.createElementNS(r.svg,"clipPath")))};for(var L in s)C(s,L)&&(x=L.toLowerCase(),e[x]=s[L](),v.push((e[x]?"":"no-")+x));return e.input||K(),e.addTest=function(a,b){if(typeof a=="object")for(var d in a)C(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},D(""),i=k=null,function(a,b){function l(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function m(){var a=s.elements;return typeof a=="string"?a.split(" "):a}function n(a){var b=j[a[h]];return b||(b={},i++,a[h]=i,j[i]=b),b}function o(a,c,d){c||(c=b);if(k)return c.createElement(a);d||(d=n(c));var g;return d.cache[a]?g=d.cache[a].cloneNode():f.test(a)?g=(d.cache[a]=d.createElem(a)).cloneNode():g=d.createElem(a),g.canHaveChildren&&!e.test(a)&&!g.tagUrn?d.frag.appendChild(g):g}function p(a,c){a||(a=b);if(k)return a.createDocumentFragment();c=c||n(a);var d=c.frag.cloneNode(),e=0,f=m(),g=f.length;for(;e",g="hidden"in a,k=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){g=!0,k=!0}})();var s={elements:d.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:c,shivCSS:d.shivCSS!==!1,supportsUnknownElements:k,shivMethods:d.shivMethods!==!1,type:"default",shivDocument:r,createElement:o,createDocumentFragment:p};a.html5=s,r(b)}(this,b),e._version=d,e._prefixes=n,e._domPrefixes=q,e._cssomPrefixes=p,e.mq=z,e.hasEvent=A,e.testProp=function(a){return H([a])},e.testAllProps=J,e.testStyles=y,e.prefixed=function(a,b,c){return b?J(a,b,c):J(a,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+v.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f Date: Thu, 2 Jan 2014 15:09:19 +0100 Subject: [PATCH 39/61] Add grunt-contrib-connect package and connect task `grunt connect` task can be used to run a local HTTP server. --- Gruntfile.js | 10 +++++++++- package.json | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index 6e7a6cca..1c589043 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -104,6 +104,14 @@ module.exports = function (grunt) { files: "crafty.js" }, + connect: { + server: { + options: { + keepalive: true + } + } + } + }); // Load the plugin that provides the "uglify" task. @@ -111,6 +119,7 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-qunit'); grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-contrib-connect'); grunt.loadNpmTasks('grunt-jsvalidate'); grunt.loadNpmTasks('grunt-browserify'); grunt.loadNpmTasks('grunt-banner'); @@ -140,5 +149,4 @@ module.exports = function (grunt) { // Run only tests grunt.registerTask('validate', ['qunit']); - }; diff --git a/package.json b/package.json index f4ecb5ca..79e38c92 100644 --- a/package.json +++ b/package.json @@ -73,5 +73,8 @@ "grunt-browserify": "~1.2.9", "grunt-banner": "~0.2.0", "grunt-contrib-watch": "~0.5.3" + }, + "devDependencies": { + "grunt-contrib-connect": "~0.6.0" } } From 580559db92a6e4f5e38e363b69affff739dcbf6f Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Sat, 4 Jan 2014 18:48:11 -0500 Subject: [PATCH 40/61] Very basic contributing guidlines --- CONTRIBUTING.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..6ee021dd --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,12 @@ +# How to contribute + +Hi, thanks for contributing to Crafty! We've got guidlines on + +- [How to build Crafty](https://github.com/craftyjs/Crafty/wiki/Building) +- [What workflow to use](https://github.com/craftyjs/Crafty/wiki/Workflow) + +## Quick summary + +- You'll need node, npm, and grunt-cli (installed globally) to work with Crafty's dev tools +- With those already installed, `npm install` from Crafty's directory will setup everything else you need +- Once you've made any changes, then `grunt check` will build and test your new version of Crafty. \ No newline at end of file From 05ed63e27fa66e975d8e9e312777e00605e4c006 Mon Sep 17 00:00:00 2001 From: mucaho Date: Sun, 5 Jan 2014 09:38:23 +0100 Subject: [PATCH 41/61] added Crafty.pixelart --- src/drawing.js | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/drawing.js b/src/drawing.js index 83171738..2e9db493 100644 --- a/src/drawing.js +++ b/src/drawing.js @@ -652,3 +652,63 @@ Crafty.DrawManager = (function () { }; })(); + +Crafty.extend({ + /**@ + * #Crafty.pixelart + * @category Graphics + * @sign public void Crafty.pixelart(Boolean enabled) + * + * Sets the image smoothing for drawing images (for both DOM and Canvas). + * 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. + * The best way to disable image smoothing is to use the Canvas render method and the Sprite component for drawing your entities. + * + * This method will have no effect for Canvas image smoothing if the canvas is not initialized yet. + * + * Note that 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 that 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. + * + * @example + * This is the preferred way to draw pixel art with the best cross-browser compatibility. + * ~~~ + * Crafty.canvas.init(); + * Crafty.pixelart(true); + * + * Crafty.sprite(imgWidth, imgHeight, "spriteMap.png", {sprite1:[0,0]}); + * Crafty.e("2D, Canvas, sprite1"); + * ~~~ + */ + pixelart: function(enabled) { + var context = Crafty.canvas.context; + if (context) { + context.imageSmoothingEnabled = !enabled; + context.mozImageSmoothingEnabled = !enabled; + context.webkitImageSmoothingEnabled = !enabled; + context.oImageSmoothingEnabled = !enabled; + context.msImageSmoothingEnabled = !enabled; + } + + var style = Crafty.stage.inner.style; + if (enabled) { + style[Crafty.DOM.camelize("image-rendering")] = "optimizeSpeed"; /* legacy */ + style[Crafty.DOM.camelize("image-rendering")] = "-moz-crisp-edges"; /* Firefox */ + style[Crafty.DOM.camelize("image-rendering")] = "-o-crisp-edges"; /* Opera */ + style[Crafty.DOM.camelize("image-rendering")] = "-webkit-optimize-contrast"; /* Webkit (Chrome & Safari) */ + style[Crafty.DOM.camelize("-ms-interpolation-mode")] = "nearest-neighbor"; /* IE */ + style[Crafty.DOM.camelize("image-rendering")] = "optimize-contrast"; /* CSS3 proposed */ + style[Crafty.DOM.camelize("image-rendering")] = "pixelated"; /* CSS4 proposed */ + style[Crafty.DOM.camelize("image-rendering")] = "crisp-edges"; /* CSS4 proposed */ + } else { + style[Crafty.DOM.camelize("image-rendering")] = "optimizeQuality"; /* legacy */ + style[Crafty.DOM.camelize("-ms-interpolation-mode")] = "bicubic"; /* IE */ + style[Crafty.DOM.camelize("image-rendering")] = "auto"; /* CSS3 */ + } + } +}); From ef61158dcb36e4c29cd19e295ca7fb3f21777c1d Mon Sep 17 00:00:00 2001 From: Kevin Simper Date: Thu, 2 Jan 2014 10:38:32 +0100 Subject: [PATCH 42/61] Use spaces instead of tabs in touchevents.html (+1 squashed commit) Squashed commits: [1f7cac0] Removed the all html documents --- tests/core.html | 859 ----------------------------------------- tests/dom.html | 85 ---- tests/events.html | 198 ---------- tests/isometric.html | 115 ------ tests/loader.html | 56 --- tests/math.html | 1 - tests/playground.html | 7 - tests/sound.html | 122 ------ tests/stage.html | 200 ---------- tests/storage.html | 85 ---- tests/text.html | 66 ---- tests/touchevents.html | 36 +- tests/tween.html | 73 ---- 13 files changed, 18 insertions(+), 1885 deletions(-) delete mode 100755 tests/core.html delete mode 100644 tests/dom.html delete mode 100644 tests/events.html delete mode 100644 tests/isometric.html delete mode 100644 tests/loader.html delete mode 100644 tests/math.html delete mode 100755 tests/playground.html delete mode 100644 tests/sound.html delete mode 100644 tests/stage.html delete mode 100644 tests/storage.html delete mode 100644 tests/text.html delete mode 100644 tests/tween.html diff --git a/tests/core.html b/tests/core.html deleted file mode 100755 index 91456da3..00000000 --- a/tests/core.html +++ /dev/null @@ -1,859 +0,0 @@ - - - - - - - - - - - - - - - -

        Crafty: Core

        -

        -
        -

        -
          -
          test markup, will be hidden
          - - diff --git a/tests/dom.html b/tests/dom.html deleted file mode 100644 index a8bc1e26..00000000 --- a/tests/dom.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - -

          Crafty: Core

          -

          -
          -

          -
            -
            test markup, will be hidden
            - - diff --git a/tests/events.html b/tests/events.html deleted file mode 100644 index ef0a4d82..00000000 --- a/tests/events.html +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - - - - - - - - - - -

            Crafty: Events

            -

            -
            -

            -
              -
              test markup, will be hidden
              - - diff --git a/tests/isometric.html b/tests/isometric.html deleted file mode 100644 index 2c5c789f..00000000 --- a/tests/isometric.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - - - - - - - - - - -

              Crafty: Core

              -

              -
              -

              -
                -
                test markup, will be hidden
                - - diff --git a/tests/loader.html b/tests/loader.html deleted file mode 100644 index 04a13615..00000000 --- a/tests/loader.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - -

                Crafty: Loader

                -

                -
                -

                -
                  -
                  test markup, will be hidden
                  - - diff --git a/tests/math.html b/tests/math.html deleted file mode 100644 index c040f275..00000000 --- a/tests/math.html +++ /dev/null @@ -1 +0,0 @@ -

                  Crafty: Stage

                    test markup, will be hidden
                    \ No newline at end of file diff --git a/tests/playground.html b/tests/playground.html deleted file mode 100755 index 35bbadc4..00000000 --- a/tests/playground.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/tests/sound.html b/tests/sound.html deleted file mode 100644 index 3c1ff098..00000000 --- a/tests/sound.html +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - - - - - -

                    Crafty: Core

                    -

                    -
                    -

                    -
                      -
                      test markup, will be hidden
                      - - diff --git a/tests/stage.html b/tests/stage.html deleted file mode 100644 index 84a8d184..00000000 --- a/tests/stage.html +++ /dev/null @@ -1,200 +0,0 @@ - - - - - - - - - - - - -

                      Crafty: Stage

                      -

                      -
                      -

                      -
                        -
                        test markup, will be hidden
                        - - diff --git a/tests/storage.html b/tests/storage.html deleted file mode 100644 index 5515888c..00000000 --- a/tests/storage.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - -

                        Crafty: Core

                        -

                        -
                        -

                        -
                          -
                          test markup, will be hidden
                          - - diff --git a/tests/text.html b/tests/text.html deleted file mode 100644 index 6e36aa4a..00000000 --- a/tests/text.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - -

                          Crafty: Core

                          -

                          -
                          -

                          -
                            -
                            test markup, will be hidden
                            - - diff --git a/tests/touchevents.html b/tests/touchevents.html index 2525e259..ee85c449 100644 --- a/tests/touchevents.html +++ b/tests/touchevents.html @@ -1,28 +1,28 @@ - - + + \ No newline at end of file diff --git a/tests/tween.html b/tests/tween.html deleted file mode 100644 index af79d761..00000000 --- a/tests/tween.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - -

                            Crafty: Tween

                            -

                            -
                            -

                            -
                              -
                              test markup, will be hidden
                              - - From e9e21aa9e389b2d8e7702c93e28dc9a2068859ee Mon Sep 17 00:00:00 2001 From: Kevin Simper Date: Thu, 2 Jan 2014 11:08:04 +0100 Subject: [PATCH 43/61] Correct the name of the modules --- tests/core.js | 2 +- tests/events.js | 2 +- tests/isometric.js | 2 +- tests/loader.js | 2 +- tests/math.js | 2 +- tests/sound.js | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/core.js b/tests/core.js index 80296850..0df8def8 100644 --- a/tests/core.js +++ b/tests/core.js @@ -1,4 +1,4 @@ -module("CORE"); +module("Core"); test("getVersion", function() { diff --git a/tests/events.js b/tests/events.js index 8d2f5531..629674be 100644 --- a/tests/events.js +++ b/tests/events.js @@ -1,4 +1,4 @@ -module("EVENTS"); +module("Events"); test("Global binding events", function() { var x = 0; diff --git a/tests/isometric.js b/tests/isometric.js index bfa01727..59e9cf6a 100644 --- a/tests/isometric.js +++ b/tests/isometric.js @@ -1,4 +1,4 @@ -module("ISOMETRIC"); +module("Isometric"); test("place tile", function() { var iso = Crafty.isometric.size(64, 16); diff --git a/tests/loader.js b/tests/loader.js index 928bbd49..33fcd358 100644 --- a/tests/loader.js +++ b/tests/loader.js @@ -1,4 +1,4 @@ -module('LOADER'); +module('Loader'); asyncTest('assets loading', function() { expect(1); diff --git a/tests/math.js b/tests/math.js index 21f20351..fa82703e 100644 --- a/tests/math.js +++ b/tests/math.js @@ -3,7 +3,7 @@ var Vector2D = Crafty.math.Vector2D; // tests for general functions should go here (.abs(), .amountOf(), etc) -module("Vector2D"); +module("Math - Vector2D"); test("constructor", function() { var v0 = new Vector2D(); diff --git a/tests/sound.js b/tests/sound.js index 4a870e98..d408eee9 100644 --- a/tests/sound.js +++ b/tests/sound.js @@ -1,4 +1,4 @@ -module("Audio"); +module("Sound"); //Set up some test fixtures function MockAudio() { From 07a054f2a9a203048be05f756e61fc96cb4eabbb Mon Sep 17 00:00:00 2001 From: Kevin Simper Date: Thu, 2 Jan 2014 11:39:18 +0100 Subject: [PATCH 44/61] Makes the module cleanup after themselves instead of doing it every time in the test manually --- tests/2d.js | 471 ++++++++++++++++++++++++++++++++++++++ tests/core.js | 556 ++++----------------------------------------- tests/dom.js | 10 +- tests/events.js | 10 +- tests/index.html | 2 +- tests/isometric.js | 10 +- tests/loader.js | 10 +- tests/math.js | 10 +- tests/sound.js | 11 +- tests/stage.js | 18 +- tests/text.js | 16 +- tests/tween.js | 11 +- 12 files changed, 609 insertions(+), 526 deletions(-) create mode 100644 tests/2d.js diff --git a/tests/2d.js b/tests/2d.js new file mode 100644 index 00000000..9869ece3 --- /dev/null +++ b/tests/2d.js @@ -0,0 +1,471 @@ +module("2D", { + setup: function() { + // prepare something for all following tests + }, + teardown: function() { + // clean up after each test + Crafty("*").destroy(); + } +}); + +test("position", function() { + var player = Crafty.e("2D, DOM, Color").attr({ + w: 50, + h: 50 + }).color("red"); + player.x += 50; + strictEqual(player._x, 50, "X moved"); + + player.y += 50; + strictEqual(player._y, 50, "Y moved"); + + player.w += 50; + strictEqual(player._w, 100, "Width increase"); + + player.h += 50; + strictEqual(player._h, 100, "Height increase"); + + strictEqual(player._globalZ, player[0], "Global Z, Before"); + + player.z = 1; + strictEqual(player._z, 1, "Z index"); + + var global_z_guess; + if (player[0] < 10) { + global_z_guess = parseInt('10000' + player[0], 10); + } else { + global_z_guess = parseInt('1000' + player[0], 10); + } + strictEqual(player._globalZ, global_z_guess, "Global Z, After"); + +}); + +test("intersect", function() { + var player = Crafty.e("2D, DOM, Color").attr({ + w: 50, + h: 50 + }).color("red"); + player.x = 0; + player.y = 0; + player.w = 50; + player.h = 50; + + 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"); + +}); + +test("within", function() { + var player = Crafty.e("2D, DOM, Color").attr({ + w: 50, + h: 50 + }); + player.x = 0; + player.y = 0; + player.w = 50; + player.h = 50; + + strictEqual(player.within(0, 0, 50, 50), true, "Within"); + + 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(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"); + +}); + +test("contains", function() { + var player = Crafty.e("2D, DOM, Color").attr({ + w: 50, + h: 50 + }); + player.x = 0; + player.y = 0; + 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(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"); + +}); + + + +test("circle", function() { + var player = Crafty.e("2D, DOM, Color").attr({ + w: 50, + h: 50 + }).color("red"); + 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"); + + 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() { + var parent0 = Crafty.e("2D, DOM, Color").attr({ + x: 0, + y: 0, + w: 50, + h: 50 + }).color("red"); + var child0 = Crafty.e("2D, DOM, Color").attr({ + x: 1, + y: 1, + w: 50, + h: 50 + }).color("red"); + var child1 = Crafty.e("2D, DOM, Color").attr({ + x: 2, + y: 2, + w: 50, + h: 50 + }).color("red"); + var child2 = Crafty.e("2D, DOM, Color").attr({ + x: 3, + y: 3, + w: 50, + h: 50 + }).color("red"); + var child3 = Crafty.e("2D, DOM, Color").attr({ + x: 4, + y: 4, + w: 50, + h: 50 + }).color("red"); + var child0_ID = child0[0]; + var child1_ID = child1[0]; + var child2_ID = child2[0]; + var child3_ID = child3[0]; + parent0.attach(child0); + parent0.attach(child1); + 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'); + child0.x += 1; + child1.x += 1; + 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'); + parent0.destroy(); + strictEqual(Crafty(child0_ID).length, 0, 'destruction of parent killed child0'); + strictEqual(Crafty(child2_ID).length, 0, 'destruction of parent killed child2'); + strictEqual(Crafty(child3_ID).length, 0, 'destruction of parent killed child3'); + +}); + +test("child_rotate", function() { + var Round = function(x) { + return Math.round(x * 100) / 100 + }; + var parent = Crafty.e("2D, DOM, Color") + .attr({ + x: 0, + y: 0, + w: 50, + h: 50, + rotation: 10 + }) + .color("red"); + var child = Crafty.e("2D, DOM, Color") + .attr({ + x: 10, + y: 10, + w: 50, + h: 50, + rotation: 15 + }) + .color("red"); + parent.attach(child); + + parent.rotation += 20; + 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'); + + 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).") + +}); + + + +// This test assumes that the "circles" are really octagons, as per Crafty.circle. +test("SAT", function() { + 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])") + +}); + + +test("adjustable boundary", function() { + e = Crafty.e("2D").attr({ + x: 10, + y: 10, + w: 10, + h: 10 + }) + + // Four argument version + e.offsetBoundary(10, 1, 3, 0); + equal(e._bx1, 10, "X1 boundary set"); + equal(e._bx2, 3, "X2 boundary set"); + equal(e._by1, 1, "Y1 boundary set"); + equal(e._by2, 0, "Y2 boundary set"); + + e._calculateMBR(10, 10, 0) + + var mbr = e._mbr; + + equal(mbr._h, 11, "MBR height uses boundaries (11)"); + equal(mbr._w, 23, "MBR width uses boundaries (23)"); + + // One argument version + e.offsetBoundary(5); + equal(e._bx1, 5, "X1 boundary set"); + equal(e._bx2, 5, "X2 boundary set"); + equal(e._by1, 5, "Y1 boundary set"); + equal(e._by2, 5, "Y2 boundary set"); + +}) + + +test("disableControl and enableControl", function() { + var e = Crafty.e("2D, Twoway") + .attr({ + x: 0 + }) + .twoway(1); + + equal(e._movement.x, 0); + equal(e._x, 0); + Crafty.trigger('KeyDown', { + key: Crafty.keys.D + }); + Crafty.trigger('EnterFrame'); + equal(e._movement.x, 1); + equal(e._x, 1); + + e.disableControl(); + Crafty.trigger('EnterFrame'); + equal(e._movement.x, 1); + equal(e._x, 1); + + Crafty.trigger('KeyUp', { + key: Crafty.keys.D + }); + Crafty.trigger('EnterFrame'); + equal(e._movement.x, 0); + equal(e._x, 1); + + e.enableControl(); + Crafty.trigger('EnterFrame'); + equal(e._movement.x, 0); + equal(e._x, 1); + + Crafty.trigger('KeyDown', { + key: Crafty.keys.D + }); + Crafty.trigger('EnterFrame'); + equal(e._movement.x, 1); + equal(e._x, 2); + + Crafty.trigger('KeyUp', { + key: Crafty.keys.D + }); + Crafty.trigger('EnterFrame'); + equal(e._movement.x, 0); + equal(e._x, 2); + + e.destroy(); +}); + + +test("Resizing 2D objects & hitboxes", function() { + var e = Crafty.e("2D, Collision"); + e.attr({ + x: 0, + y: 0, + w: 40, + h: 50 + }) + + equal(e.map.points[0][0], 0, "Before rotation: x_0 is 0") + equal(e.map.points[0][1], 0, "y_0 is 0") + equal(e.map.points[2][0], 40, "x_2 is 40") + equal(e.map.points[2][1], 50, "y_2 is 50") + + e.rotation = 90; + + equal(Math.round(e.map.points[0][0]), 0, "After rotation by 90 deg: x_0 is 0") + equal(Math.round(e.map.points[0][1]), 0, "y_0 is 0") + equal(Math.round(e.map.points[2][0]), -50, "x_2 is -50") + equal(Math.round(e.map.points[2][1]), 40, "y_2 is 40") + + // After rotation the MBR will have changed + equal(Math.round(e._mbr._w), 50, "_mbr._w is 50"); + equal(Math.round(e._mbr._h), 40, "_mbr._h is 40"); + equal(Math.round(e._mbr._x), -50, "_mbr._x is -50"); + equal(Math.round(e._mbr._y), 0, "_mbr._y is 0"); + + e.collision(); // Check that regenerating the hitbox while rotated works correctly + + equal(Math.round(e.map.points[0][0]), 0, "After rotation and hitbox regeneration: x_0 is 0") + equal(Math.round(e.map.points[0][1]), 0, "y_0 is 0") + equal(Math.round(e.map.points[2][0]), -50, "x_2 is -50") + equal(Math.round(e.map.points[2][1]), 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; + + equal(Math.round(e.map.points[0][0]), 0, "After rotation and increase in width: x_0 is 0") + equal(Math.round(e.map.points[0][1]), 0, "y_0 is 0") + equal(Math.round(e.map.points[2][0]), -50, "x_2 is -50") + equal(Math.round(e.map.points[2][1]), 100, "y_2 is 100") + + // After rotation the MBR will have changed + equal(Math.round(e._mbr._w), 50, "_mbr._w is 50"); + equal(Math.round(e._mbr._h), 100, "_mbr._h is 100"); + equal(Math.round(e._mbr._x), -50, "_mbr._x is -50"); + equal(Math.round(e._mbr._y), 0, "_mbr._y is 0"); + + e.destroy(); +}); + +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); + + ok(e._cbr !== null, "_cbr exists"); + var cbr = e._cbr; + // Test whether cbr actually bounds hitbox+object + ok(cbr._x <= 42, "cbr x position correct"); + ok(cbr._y <= 36, "cbr y position correct"); + ok(cbr._x + cbr._w >= 74, "cbr width correct"); + ok(cbr._y + cbr._h >= 66, "cbr height correct"); + + var x0 = cbr._x, + y0 = cbr._y; + + e.x += 10; + e.y += 15; + + equal(cbr._x, x0 + 10, "cbr x position moves correctly"); + equal(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); + + ok(e._cbr === null, "_cbr should not exist"); + + e.w = 10; + + ok(e._cbr !== null, "_cbr should now exist after entity shrinks"); + + 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); + + 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"); + +}); \ No newline at end of file diff --git a/tests/core.js b/tests/core.js index 0df8def8..3e93162d 100644 --- a/tests/core.js +++ b/tests/core.js @@ -1,4 +1,12 @@ -module("Core"); +module("Core", { + setup: function() { + // prepare something for all following tests + }, + teardown: function() { + // clean up after each test + Crafty("*").destroy(); + } +}); test("getVersion", function() { @@ -20,8 +28,7 @@ test("selectors", function() { strictEqual(Crafty("*").length, 7, "All components - universal selector"); strictEqual(Crafty(first[0]), first, "Get by ID"); - // Clean up - Crafty("*").destroy(); + }); test("addComponent and removeComponent", function() { @@ -46,8 +53,6 @@ test("addComponent and removeComponent", function() { first.removeComponent("comp", false); strictEqual(!first.added && !first.has("comp"), true, "hard-removed component (properties are gone)"); - // Clean up - Crafty("*").destroy(); }); test("remove", function() { @@ -87,8 +92,7 @@ test("attr", function() { }); strictEqual(first.prop, "test", "properties from object assigned"); strictEqual(first.another, 56, "properties from object assigned"); - // Clean up - Crafty("*").destroy(); + }); test("setter", function() { @@ -112,8 +116,7 @@ test("setter", function() { first.p2 = 2; first.p3 = 3; strictEqual(first._p2 + first._p3, 10, "two property setters"); - // Clean up - Crafty("*").destroy(); + }); test("bind", function() { @@ -124,8 +127,7 @@ test("bind", function() { }); first.trigger("myevent"); strictEqual(triggered, true, "custom event triggered"); - // Clean up - Crafty("*").destroy(); + }); test("unbind", function() { @@ -148,8 +150,7 @@ test("unbind", function() { first.bind("myevent", callback2); first.unbind("myevent", callback); first.trigger("myevent"); - // Clean up - Crafty("*").destroy(); + }); test("globalBindAndUnbind", function() { @@ -196,8 +197,7 @@ test("each", function() { count++; }); strictEqual(count, 7, "Iterated all elements"); - // Clean up - Crafty("*").destroy(); + }); test("Crafty.get() to find an array", function() { @@ -211,7 +211,6 @@ test("Crafty.get() to find an array", function() { equal(result[0].has("test"), true, "Result elements should have correct component"); equal(collection[0], result[0].getId(), "First id of result should match first id of Crafty array"); - Crafty("*").destroy(); }) test("Crafty.get(index) to find the indicated entity", function() { @@ -229,7 +228,6 @@ test("Crafty.get(index) to find the indicated entity", function() { equal(result.has("test"), true, "Result should have correct component"); equal(result.getId(), collection[2], "result should be last element of collection"); - Crafty("*").destroy(); }) test("Crafty.get(index) error checking", function() { @@ -246,9 +244,6 @@ test("Crafty.get(index) error checking", function() { result = collection.get(-4); equal(typeof result, "undefined", "result of get(-4) should be undefined") - - - Crafty("*").destroy(); }) test("Crafty.get with only one object", function() { @@ -258,7 +253,7 @@ test("Crafty.get with only one object", function() { equal(result.getId(), e.getId(), "result of get(0) is correct entity") result = collection.get(); equal(result.length, 1, "result of get() is array of length 1"); - Crafty("*").destroy(); + }) test("requires", function() { @@ -278,8 +273,7 @@ test("requires", function() { 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"); - // Clean up - Crafty("*").destroy(); + }); test("destroy", function() { @@ -287,11 +281,18 @@ test("destroy", function() { id = first[0]; //id first.destroy(); strictEqual(Crafty(id).length, 0, "Not listed"); - // Clean up - Crafty("*").destroy(); + }); -module("Scenes"); +module("Scenes", { + setup: function() { + // prepare something for all following tests + }, + teardown: function() { + // clean up after each test + Crafty("*").destroy(); + } +}); test("Scene calling", function() { var x = 0; @@ -301,6 +302,7 @@ test("Scene calling", function() { Crafty.scene("test-call", sceneInit); Crafty.scene("test-call"); equal(x, 13, "Scene called succesfully.") + }); test("Scene parameters", function() { @@ -321,7 +323,6 @@ test("Calling a scene destroys 2D entities", function() { var l = Crafty("2D").length; equal(l, 0, "2D entity destroyed on scene change."); - Crafty("*").destroy(); }); test("Calling a scene doesn't destroy 2D entities with Persist", function() { @@ -332,7 +333,6 @@ test("Calling a scene doesn't destroy 2D entities with Persist", function() { var l = Crafty("2D").length; equal(l, 1, "Persist entity remains on scene change."); - Crafty("*").destroy(); }); @@ -353,493 +353,20 @@ test("Scene uninit function called", function() { Crafty.enterScene("test-uninit"); Crafty.enterScene("game"); equal(x, 20, "Uninit scene called successfully when chanced to another scene"); -}); - -module("2D"); - -test("position", function() { - var player = Crafty.e("2D, DOM, Color").attr({ - w: 50, - h: 50 - }).color("red"); - player.x += 50; - strictEqual(player._x, 50, "X moved"); - - player.y += 50; - strictEqual(player._y, 50, "Y moved"); - - player.w += 50; - strictEqual(player._w, 100, "Width increase"); - - player.h += 50; - strictEqual(player._h, 100, "Height increase"); - - strictEqual(player._globalZ, player[0], "Global Z, Before"); - - player.z = 1; - strictEqual(player._z, 1, "Z index"); - - var global_z_guess; - if (player[0] < 10) { - global_z_guess = parseInt('10000' + player[0], 10); - } else { - global_z_guess = parseInt('1000' + player[0], 10); - } - strictEqual(player._globalZ, global_z_guess, "Global Z, After"); - // Clean up - Crafty("*").destroy(); -}); - -test("intersect", function() { - var player = Crafty.e("2D, DOM, Color").attr({ - w: 50, - h: 50 - }).color("red"); - player.x = 0; - player.y = 0; - player.w = 50; - player.h = 50; - - 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"); - // Clean up - Crafty("*").destroy(); -}); - -test("within", function() { - var player = Crafty.e("2D, DOM, Color").attr({ - w: 50, - h: 50 - }); - player.x = 0; - player.y = 0; - player.w = 50; - player.h = 50; - - strictEqual(player.within(0, 0, 50, 50), true, "Within"); - - 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(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"); - - - - // Clean up - Crafty("*").destroy(); -}); - -test("contains", function() { - var player = Crafty.e("2D, DOM, Color").attr({ - w: 50, - h: 50 - }); - player.x = 0; - player.y = 0; - 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(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"); - - // Clean up - Crafty("*").destroy(); -}); - - - -test("circle", function() { - var player = Crafty.e("2D, DOM, Color").attr({ - w: 50, - h: 50 - }).color("red"); - 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"); - - 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"); - // Clean up - Crafty("*").destroy(); -}); - -test("child", function() { - var parent0 = Crafty.e("2D, DOM, Color").attr({ - x: 0, - y: 0, - w: 50, - h: 50 - }).color("red"); - var child0 = Crafty.e("2D, DOM, Color").attr({ - x: 1, - y: 1, - w: 50, - h: 50 - }).color("red"); - var child1 = Crafty.e("2D, DOM, Color").attr({ - x: 2, - y: 2, - w: 50, - h: 50 - }).color("red"); - var child2 = Crafty.e("2D, DOM, Color").attr({ - x: 3, - y: 3, - w: 50, - h: 50 - }).color("red"); - var child3 = Crafty.e("2D, DOM, Color").attr({ - x: 4, - y: 4, - w: 50, - h: 50 - }).color("red"); - var child0_ID = child0[0]; - var child1_ID = child1[0]; - var child2_ID = child2[0]; - var child3_ID = child3[0]; - parent0.attach(child0); - parent0.attach(child1); - 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'); - child0.x += 1; - child1.x += 1; - 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'); - parent0.destroy(); - strictEqual(Crafty(child0_ID).length, 0, 'destruction of parent killed child0'); - strictEqual(Crafty(child2_ID).length, 0, 'destruction of parent killed child2'); - strictEqual(Crafty(child3_ID).length, 0, 'destruction of parent killed child3'); - // Clean up - Crafty("*").destroy(); -}); - -test("child_rotate", function() { - var Round = function(x) { - return Math.round(x * 100) / 100 - }; - var parent = Crafty.e("2D, DOM, Color") - .attr({ - x: 0, - y: 0, - w: 50, - h: 50, - rotation: 10 - }) - .color("red"); - var child = Crafty.e("2D, DOM, Color") - .attr({ - x: 10, - y: 10, - w: 50, - h: 50, - rotation: 15 - }) - .color("red"); - parent.attach(child); - - parent.rotation += 20; - 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'); - - 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).") - - // Clean up - Crafty("*").destroy(); -}); - - - -// This test assumes that the "circles" are really octagons, as per Crafty.circle. -test("SAT", function() { - 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])") - // Clean up - Crafty("*").destroy(); -}); - - -test("adjustable boundary", function() { - e = Crafty.e("2D").attr({ - x: 10, - y: 10, - w: 10, - h: 10 - }) - - // Four argument version - e.offsetBoundary(10, 1, 3, 0); - equal(e._bx1, 10, "X1 boundary set"); - equal(e._bx2, 3, "X2 boundary set"); - equal(e._by1, 1, "Y1 boundary set"); - equal(e._by2, 0, "Y2 boundary set"); - - e._calculateMBR(10, 10, 0) - - var mbr = e._mbr; - - equal(mbr._h, 11, "MBR height uses boundaries (11)"); - equal(mbr._w, 23, "MBR width uses boundaries (23)"); - - // One argument version - e.offsetBoundary(5); - equal(e._bx1, 5, "X1 boundary set"); - equal(e._bx2, 5, "X2 boundary set"); - equal(e._by1, 5, "Y1 boundary set"); - equal(e._by2, 5, "Y2 boundary set"); - - - - Crafty("*").destroy(); - - -}) - - -test("disableControl and enableControl", function() { - var e = Crafty.e("2D, Twoway") - .attr({ - x: 0 - }) - .twoway(1); - - equal(e._movement.x, 0); - equal(e._x, 0); - Crafty.trigger('KeyDown', { - key: Crafty.keys.D - }); - Crafty.trigger('EnterFrame'); - equal(e._movement.x, 1); - equal(e._x, 1); - - e.disableControl(); - Crafty.trigger('EnterFrame'); - equal(e._movement.x, 1); - equal(e._x, 1); - - Crafty.trigger('KeyUp', { - key: Crafty.keys.D - }); - Crafty.trigger('EnterFrame'); - equal(e._movement.x, 0); - equal(e._x, 1); - - e.enableControl(); - Crafty.trigger('EnterFrame'); - equal(e._movement.x, 0); - equal(e._x, 1); - Crafty.trigger('KeyDown', { - key: Crafty.keys.D - }); - Crafty.trigger('EnterFrame'); - equal(e._movement.x, 1); - equal(e._x, 2); - - Crafty.trigger('KeyUp', { - key: Crafty.keys.D - }); - Crafty.trigger('EnterFrame'); - equal(e._movement.x, 0); - equal(e._x, 2); - - e.destroy(); }); -test("Resizing 2D objects & hitboxes", function() { - var e = Crafty.e("2D, Collision"); - e.attr({ - x: 0, - y: 0, - w: 40, - h: 50 - }) - - equal(e.map.points[0][0], 0, "Before rotation: x_0 is 0") - equal(e.map.points[0][1], 0, "y_0 is 0") - equal(e.map.points[2][0], 40, "x_2 is 40") - equal(e.map.points[2][1], 50, "y_2 is 50") - - e.rotation = 90; - - equal(Math.round(e.map.points[0][0]), 0, "After rotation by 90 deg: x_0 is 0") - equal(Math.round(e.map.points[0][1]), 0, "y_0 is 0") - equal(Math.round(e.map.points[2][0]), -50, "x_2 is -50") - equal(Math.round(e.map.points[2][1]), 40, "y_2 is 40") - - // After rotation the MBR will have changed - equal(Math.round(e._mbr._w), 50, "_mbr._w is 50"); - equal(Math.round(e._mbr._h), 40, "_mbr._h is 40"); - equal(Math.round(e._mbr._x), -50, "_mbr._x is -50"); - equal(Math.round(e._mbr._y), 0, "_mbr._y is 0"); - - e.collision(); // Check that regenerating the hitbox while rotated works correctly - - equal(Math.round(e.map.points[0][0]), 0, "After rotation and hitbox regeneration: x_0 is 0") - equal(Math.round(e.map.points[0][1]), 0, "y_0 is 0") - equal(Math.round(e.map.points[2][0]), -50, "x_2 is -50") - equal(Math.round(e.map.points[2][1]), 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; - - equal(Math.round(e.map.points[0][0]), 0, "After rotation and increase in width: x_0 is 0") - equal(Math.round(e.map.points[0][1]), 0, "y_0 is 0") - equal(Math.round(e.map.points[2][0]), -50, "x_2 is -50") - equal(Math.round(e.map.points[2][1]), 100, "y_2 is 100") - - // After rotation the MBR will have changed - equal(Math.round(e._mbr._w), 50, "_mbr._w is 50"); - equal(Math.round(e._mbr._h), 100, "_mbr._h is 100"); - equal(Math.round(e._mbr._x), -50, "_mbr._x is -50"); - equal(Math.round(e._mbr._y), 0, "_mbr._y is 0"); - - e.destroy(); +module("DebugLayer", { + setup: function() { + // prepare something for all following tests + }, + teardown: function() { + // clean up after each test + Crafty("*").destroy(); + } }); -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); - - ok(e._cbr !== null, "_cbr exists"); - var cbr = e._cbr; - // Test whether cbr actually bounds hitbox+object - ok(cbr._x <= 42, "cbr x position correct"); - ok(cbr._y <= 36, "cbr y position correct"); - ok(cbr._x + cbr._w >= 74, "cbr width correct"); - ok(cbr._y + cbr._h >= 66, "cbr height correct"); - - var x0 = cbr._x, - y0 = cbr._y; - - e.x += 10; - e.y += 15; - - equal(cbr._x, x0 + 10, "cbr x position moves correctly"); - equal(cbr._y, y0 + 15, "cbr y position moves correctly"); - Crafty("*").destroy(); -}) - -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); - - ok(e._cbr === null, "_cbr should not exist"); - - e.w = 10; - - ok(e._cbr !== null, "_cbr should now exist after entity shrinks"); - - e.w = 20; - - ok(e._cbr === null, "_cbr should not exist after entity grows again"); - - Crafty("*").destroy(); -}) - -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); - - 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"); - - -}) - -module("DebugLayer"); test("DebugCanvas", function() { if (!(Crafty.support.canvas)) { expect(0); @@ -923,7 +450,16 @@ test("Hitbox debugging", function() { }); -module("Easing"); +module("Easing", { + setup: function() { + // prepare something for all following tests + }, + teardown: function() { + // clean up after each test + Crafty("*").destroy(); + } +}); + test("Crafty.easing duration", function() { var e = new Crafty.easing(80); // 4 frames == 80ms by default equal(e.duration, 80, "Default duration in ms"); diff --git a/tests/dom.js b/tests/dom.js index 7e9a2fe4..4e597b60 100644 --- a/tests/dom.js +++ b/tests/dom.js @@ -1,4 +1,12 @@ -module("DOM"); +module("DOM", { + setup: function() { + // prepare something for all following tests + }, + teardown: function() { + // clean up after each test + Crafty("*").destroy(); + } +}); test("avoidCss3dTransforms", function() { var useCss3dTransforms = Crafty.e("2D, DOM") diff --git a/tests/events.js b/tests/events.js index 629674be..21bc7247 100644 --- a/tests/events.js +++ b/tests/events.js @@ -1,4 +1,12 @@ -module("Events"); +module("Events", { + setup: function() { + // prepare something for all following tests + }, + teardown: function() { + // clean up after each test + Crafty("*").destroy(); + } +}); test("Global binding events", function() { var x = 0; diff --git a/tests/index.html b/tests/index.html index 65eba487..2e3f0f1d 100755 --- a/tests/index.html +++ b/tests/index.html @@ -43,6 +43,7 @@

                              + @@ -50,7 +51,6 @@

                              - diff --git a/tests/isometric.js b/tests/isometric.js index 59e9cf6a..2aadff12 100644 --- a/tests/isometric.js +++ b/tests/isometric.js @@ -1,4 +1,12 @@ -module("Isometric"); +module("Isometric", { + setup: function() { + // prepare something for all following tests + }, + teardown: function() { + // clean up after each test + Crafty("*").destroy(); + } +}); test("place tile", function() { var iso = Crafty.isometric.size(64, 16); diff --git a/tests/loader.js b/tests/loader.js index 33fcd358..45e5b971 100644 --- a/tests/loader.js +++ b/tests/loader.js @@ -1,4 +1,12 @@ -module('Loader'); +module('Loader', { + setup: function() { + // prepare something for all following tests + }, + teardown: function() { + // clean up after each test + Crafty("*").destroy(); + } +}); asyncTest('assets loading', function() { expect(1); diff --git a/tests/math.js b/tests/math.js index fa82703e..88b8c2a4 100644 --- a/tests/math.js +++ b/tests/math.js @@ -3,7 +3,15 @@ var Vector2D = Crafty.math.Vector2D; // tests for general functions should go here (.abs(), .amountOf(), etc) -module("Math - Vector2D"); +module("Math - Vector2D", { + setup: function() { + // prepare something for all following tests + }, + teardown: function() { + // clean up after each test + Crafty("*").destroy(); + } +}); test("constructor", function() { var v0 = new Vector2D(); diff --git a/tests/sound.js b/tests/sound.js index d408eee9..b97c123d 100644 --- a/tests/sound.js +++ b/tests/sound.js @@ -1,4 +1,12 @@ -module("Sound"); +module("Sound", { + setup: function() { + // prepare something for all following tests + }, + teardown: function() { + // clean up after each test + Crafty("*").destroy(); + } +}); //Set up some test fixtures function MockAudio() { @@ -62,7 +70,6 @@ function ChromeBuggedAudio() { this.ended = false } -Crafty.init(); asyncTest("setChannels", function() { // Test that setChannels doesn't break sound diff --git a/tests/stage.js b/tests/stage.js index 3f6975bf..d25f62f3 100644 --- a/tests/stage.js +++ b/tests/stage.js @@ -22,7 +22,13 @@ test("simulateFrames", function() { module("Viewport", { - setup: reset + setup: function() { + reset(); + }, + teardown: function() { + // clean up after each test + Crafty("*").destroy(); + } }); test("scroll using _x, _y", function() { @@ -188,7 +194,15 @@ test("centerOn", function() { Crafty.viewport.scroll('y', 0); }); -module("Crafty.timer") +module("Crafty.timer", { + setup: function() { + // prepare something for all following tests + }, + teardown: function() { + // clean up after each test + Crafty("*").destroy(); + } +}); test("curTime", 1, function() { var startTime, lastKnownTime; Crafty.e("").bind("EnterFrame", function(params) { diff --git a/tests/text.js b/tests/text.js index 7caec16b..575cce7f 100644 --- a/tests/text.js +++ b/tests/text.js @@ -1,4 +1,13 @@ -module("Text"); +module("Text", { + setup: function() { + // prepare something for all following tests + }, + teardown: function() { + // clean up after each test + Crafty("*").destroy(); + } +}); + test("fontFamily", function() { var text = Crafty.e('DOM, Text').textFont({ family: 'Times New Roman 400', @@ -6,7 +15,6 @@ test("fontFamily", function() { }).text('Test'); equal(text.attr('_textFont')['family'], "'Times New Roman 400'", 'Expect to have singlequotes arount the family property.'); - Crafty("*").destroy(); }); test("_getFontHeight", function() { @@ -15,7 +23,6 @@ test("_getFontHeight", function() { equal(h, 10, "Font height is 10 pixels"); h = e._getFontHeight("10in"); equal(h, 960, "Font height is 960 pixels"); - Crafty("*").destroy(); }) test("Width of canvas element", function() { @@ -25,7 +32,6 @@ test("Width of canvas element", function() { e.text("abc"); var w2 = e.w; ok(w2 > w1, "Entity increases in width when text is changed.") - Crafty("*").destroy(); }) test("Height of canvas element", function() { @@ -38,5 +44,5 @@ test("Height of canvas element", function() { var h2 = e.h; ok(h2 > 20, "Font height set correctly.") ok(h2 > h1, "Entity increases in height when font size is increased.") - Crafty("*").destroy(); + }) \ No newline at end of file diff --git a/tests/tween.js b/tests/tween.js index f25c1722..0e986ea0 100644 --- a/tests/tween.js +++ b/tests/tween.js @@ -1,4 +1,13 @@ -module("Tween"); +module("Tween", { + setup: function() { + // prepare something for all following tests + }, + teardown: function() { + // clean up after each test + Crafty("*").destroy(); + } +}); + test("Tween", function() { var e = Crafty.e("2D, Tween") e.x = 0; From 5517bdac1ab248c42cb6642cb0b06e4a961befcd Mon Sep 17 00:00:00 2001 From: Kevin Simper Date: Thu, 2 Jan 2014 11:41:35 +0100 Subject: [PATCH 45/61] Human error (+1 squashed commit) Squashed commits: [e630496] HelperFunctions --- Gruntfile.js | 2 +- tests/index.html | 1 + tests/lib/helperFunctions.js | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 tests/lib/helperFunctions.js diff --git a/Gruntfile.js b/Gruntfile.js index 12779a80..a55ba0e8 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -86,7 +86,7 @@ module.exports = function (grunt) { qunit: { all: [ - 'tests/index.js' + 'tests/index.html' ] }, diff --git a/tests/index.html b/tests/index.html index 2e3f0f1d..06c07d7c 100755 --- a/tests/index.html +++ b/tests/index.html @@ -23,6 +23,7 @@   + diff --git a/tests/lib/helperFunctions.js b/tests/lib/helperFunctions.js new file mode 100644 index 00000000..b2057ded --- /dev/null +++ b/tests/lib/helperFunctions.js @@ -0,0 +1,3 @@ +Round = function(x){ + return Math.round(x*100)/100; +}; \ No newline at end of file From 8d2cfa5ce260b14624d06bf34a4af15806fcc3d2 Mon Sep 17 00:00:00 2001 From: Kevin Simper Date: Thu, 2 Jan 2014 12:46:45 +0100 Subject: [PATCH 46/61] Rename sound to Audio as it is named in Crafty and HTML5 (+1 squashed commit) Squashed commits: [7f79c58] Semicolons in sound.js --- tests/audio.js | 107 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/sound.js | 109 ------------------------------------------------- 2 files changed, 107 insertions(+), 109 deletions(-) create mode 100644 tests/audio.js delete mode 100644 tests/sound.js diff --git a/tests/audio.js b/tests/audio.js new file mode 100644 index 00000000..7c33fecb --- /dev/null +++ b/tests/audio.js @@ -0,0 +1,107 @@ +module("Audio", { + setup: function() { + // prepare something for all following tests + }, + teardown: function() { + // clean up after each test + Crafty("*").destroy(); + } +}); + +//Set up some test fixtures +function MockAudio() { + var self = this; + this.endedListeners = []; + this.canPlayType = function() { + return true; + }; + this.addEventListener = function(event, listener) { + switch (event) { + case "ended": + this.endedListeners.push(listener); + break; + default: + throw new Exception("Not implemented"); + } + } + this.removeEventListener = function(event, listener) { + switch (event) { + case "ended": + var ind = this.endedListeners.indexOf(listener); + if (ind) this.endedListeners.splice(ind, 1); + break; + default: + throw new Exception("Not implemented"); + } + } + + function fireEnded() { + setTimeout(function() { + self.ended = true; + self.endedListeners.forEach(function(f) { + f.call(self); + }) + }, 0); + } + this.play = function() { + if (this.src) { + fireEnded(); + } + }; + this.pause = function() {}; + this.ended = false; +} + +function ChromeBuggedAudio() { + var self = this; + this.canPlayType = function() { + return true; + }; + this.addEventListener = function(event, listener) {}; + this.removeEventListener = function(event, listener) {}; + this.play = function() { + if (this.src) { + self.ended = true; + self.src = null; + ok(true, "Audio played"); + } + }; + this.pause = function() {}; + this.ended = false; +} + + +asyncTest("setChannels", function() { + // Test that setChannels doesn't break sound + expect(2); + window.Audio = MockAudio; + Crafty.support.audio = true; + Crafty.audio.setChannels(5); + Crafty.audio.add("mockSound", ["sound.ogg"]); + var a = Crafty.audio.play("mockSound", 1); + ok(typeof a === "object", "Type of a is object: " + a); + a.addEventListener("ended", function() { + ok(true, "Sound played"); + delete window.Audio; //reset Audio to platform default + Crafty.audio.channels = []; + start(); + }) +}); + +test("chromeBug", function() { + // Test that we don't exhaust our audio channels if Chrome bug 280417 + // eats our "ended" events + expect(10); + window.Audio = ChromeBuggedAudio; + Crafty.support.audio = true; + Crafty.audio.setChannels(1); + Crafty.support.audio = true; + Crafty.audio.add("mockSound", ["sound.ogg"]); + + var a; + for (var i = 0; i < 10; i++) { + a = Crafty.audio.play("mockSound", 1); // This will trigger an assertion + } + delete window.Audio; //reset Audio to platform default + Crafty.audio.channels = []; +}); \ No newline at end of file diff --git a/tests/sound.js b/tests/sound.js deleted file mode 100644 index b97c123d..00000000 --- a/tests/sound.js +++ /dev/null @@ -1,109 +0,0 @@ -module("Sound", { - setup: function() { - // prepare something for all following tests - }, - teardown: function() { - // clean up after each test - Crafty("*").destroy(); - } -}); - -//Set up some test fixtures -function MockAudio() { - var self = this - this.endedListeners = [] - this.canPlayType = function() { - return true - } - this.addEventListener = function(event, listener) { - switch (event) { - case "ended": - this.endedListeners.push(listener) - break - default: - throw new Exception("Not implemented") - } - } - this.removeEventListener = function(event, listener) { - switch (event) { - case "ended": - var ind = this.endedListeners.indexOf(listener) - if (ind) this.endedListeners.splice(ind, 1); - break - default: - throw new Exception("Not implemented") - } - } - - function fireEnded() { - setTimeout(function() { - self.ended = true - self.endedListeners.forEach(function(f) { - f.call(self) - }) - }, 0) - } - this.play = function() { - if (this.src) { - fireEnded() - } - } - this.pause = function() {} - this.ended = false -} - -function ChromeBuggedAudio() { - var self = this - this.canPlayType = function() { - return true - } - this.addEventListener = function(event, listener) {} - this.removeEventListener = function(event, listener) {} - this.play = function() { - if (this.src) { - self.ended = true - self.src = null - ok(true, "Audio played") - } - } - this.pause = function() {} - this.ended = false -} - - -asyncTest("setChannels", function() { - // Test that setChannels doesn't break sound - expect(2) - window.Audio = MockAudio - Crafty.support.audio = true - Crafty.audio.setChannels(5) - Crafty.audio.add("mockSound", ["sound.ogg"]) - var a = Crafty.audio.play("mockSound", 1) - ok(typeof a === "object", "Type of a is object: " + a) - a.addEventListener("ended", function() { - ok(true, "Sound played") - delete window.Audio //reset Audio to platform default - Crafty.audio.channels = [] - Crafty("*").destroy(); - start() - }) -}); - -test("chromeBug", function() { - // Test that we don't exhaust our audio channels if Chrome bug 280417 - // eats our "ended" events - expect(10) - window.Audio = ChromeBuggedAudio - Crafty.support.audio = true - Crafty.audio.setChannels(1) - Crafty.support.audio = true - Crafty.audio.add("mockSound", ["sound.ogg"]) - - var a - for (var i = 0; i < 10; i++) { - a = Crafty.audio.play("mockSound", 1) // This will trigger an assertion - } - delete window.Audio //reset Audio to platform default - Crafty.audio.channels = [] - Crafty("*").destroy(); -}); \ No newline at end of file From e7b1fe0df9eb2714da7ae942fa262868fdf6cf18 Mon Sep 17 00:00:00 2001 From: Kevin Simper Date: Thu, 2 Jan 2014 13:03:07 +0100 Subject: [PATCH 47/61] JSValidate and JSHint on the test also to ensure quality --- Gruntfile.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index a55ba0e8..391ceb01 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -76,7 +76,7 @@ module.exports = function (grunt) { }, jshint: { - files: ['Gruntfile.js', 'src/**/*.js'], + files: ['Gruntfile.js', 'src/**/*.js', 'tests/*.js'], options: { trailing: true, globals: { @@ -91,7 +91,7 @@ module.exports = function (grunt) { }, jsvalidate: { - files: "crafty.js" + files: ["crafty.js", 'tests/*.js'] }, }); From 742294b4601feadbc230ada3a502e7f25669b0bb Mon Sep 17 00:00:00 2001 From: Kevin Simper Date: Thu, 2 Jan 2014 13:19:22 +0100 Subject: [PATCH 48/61] JSHint fixes on the test files --- tests/2d.js | 53 ++++++++++++++++++------------------- tests/audio.js | 8 +++--- tests/core.js | 69 ++++++++++++++++++++++++------------------------ tests/events.js | 66 ++++++++++++++++++++++----------------------- tests/stage.js | 13 ++++----- tests/storage.js | 12 ++++----- tests/text.js | 16 +++++------ tests/tween.js | 2 +- 8 files changed, 117 insertions(+), 122 deletions(-) diff --git a/tests/2d.js b/tests/2d.js index 9869ece3..065bd1bb 100644 --- a/tests/2d.js +++ b/tests/2d.js @@ -200,9 +200,6 @@ test("child", function() { }); test("child_rotate", function() { - var Round = function(x) { - return Math.round(x * 100) / 100 - }; var parent = Crafty.e("2D, DOM, Color") .attr({ x: 0, @@ -232,8 +229,8 @@ test("child_rotate", function() { 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)."); }); @@ -244,7 +241,7 @@ test("SAT", function() { 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])"); }); @@ -255,7 +252,7 @@ test("adjustable boundary", function() { y: 10, w: 10, h: 10 - }) + }); // Four argument version e.offsetBoundary(10, 1, 3, 0); @@ -264,7 +261,7 @@ test("adjustable boundary", function() { equal(e._by1, 1, "Y1 boundary set"); equal(e._by2, 0, "Y2 boundary set"); - e._calculateMBR(10, 10, 0) + e._calculateMBR(10, 10, 0); var mbr = e._mbr; @@ -278,7 +275,7 @@ test("adjustable boundary", function() { equal(e._by1, 5, "Y1 boundary set"); equal(e._by2, 5, "Y2 boundary set"); -}) +}); test("disableControl and enableControl", function() { @@ -339,19 +336,19 @@ test("Resizing 2D objects & hitboxes", function() { y: 0, w: 40, h: 50 - }) + }); - equal(e.map.points[0][0], 0, "Before rotation: x_0 is 0") - equal(e.map.points[0][1], 0, "y_0 is 0") - equal(e.map.points[2][0], 40, "x_2 is 40") - equal(e.map.points[2][1], 50, "y_2 is 50") + equal(e.map.points[0][0], 0, "Before rotation: x_0 is 0"); + equal(e.map.points[0][1], 0, "y_0 is 0"); + equal(e.map.points[2][0], 40, "x_2 is 40"); + equal(e.map.points[2][1], 50, "y_2 is 50"); e.rotation = 90; - equal(Math.round(e.map.points[0][0]), 0, "After rotation by 90 deg: x_0 is 0") - equal(Math.round(e.map.points[0][1]), 0, "y_0 is 0") - equal(Math.round(e.map.points[2][0]), -50, "x_2 is -50") - equal(Math.round(e.map.points[2][1]), 40, "y_2 is 40") + equal(Math.round(e.map.points[0][0]), 0, "After rotation by 90 deg: x_0 is 0"); + equal(Math.round(e.map.points[0][1]), 0, "y_0 is 0"); + equal(Math.round(e.map.points[2][0]), -50, "x_2 is -50"); + equal(Math.round(e.map.points[2][1]), 40, "y_2 is 40"); // After rotation the MBR will have changed equal(Math.round(e._mbr._w), 50, "_mbr._w is 50"); @@ -361,20 +358,20 @@ test("Resizing 2D objects & hitboxes", function() { e.collision(); // Check that regenerating the hitbox while rotated works correctly - equal(Math.round(e.map.points[0][0]), 0, "After rotation and hitbox regeneration: x_0 is 0") - equal(Math.round(e.map.points[0][1]), 0, "y_0 is 0") - equal(Math.round(e.map.points[2][0]), -50, "x_2 is -50") - equal(Math.round(e.map.points[2][1]), 40, "y_2 is 40") + equal(Math.round(e.map.points[0][0]), 0, "After rotation and hitbox regeneration: x_0 is 0"); + equal(Math.round(e.map.points[0][1]), 0, "y_0 is 0"); + equal(Math.round(e.map.points[2][0]), -50, "x_2 is -50"); + equal(Math.round(e.map.points[2][1]), 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; - equal(Math.round(e.map.points[0][0]), 0, "After rotation and increase in width: x_0 is 0") - equal(Math.round(e.map.points[0][1]), 0, "y_0 is 0") - equal(Math.round(e.map.points[2][0]), -50, "x_2 is -50") - equal(Math.round(e.map.points[2][1]), 100, "y_2 is 100") + equal(Math.round(e.map.points[0][0]), 0, "After rotation and increase in width: x_0 is 0"); + equal(Math.round(e.map.points[0][1]), 0, "y_0 is 0"); + equal(Math.round(e.map.points[2][0]), -50, "x_2 is -50"); + equal(Math.round(e.map.points[2][1]), 100, "y_2 is 100"); // After rotation the MBR will have changed equal(Math.round(e._mbr._w), 50, "_mbr._w is 50"); @@ -418,7 +415,7 @@ test("Hitboxes outside of entities (CBR)", function() { equal(cbr._x, x0 + 10, "cbr x position moves correctly"); equal(cbr._y, y0 + 15, "cbr y position moves correctly"); -}) +}); test("CBRs on resize", function() { var poly = new Crafty.polygon([ @@ -445,7 +442,7 @@ test("CBRs on resize", function() { 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([ diff --git a/tests/audio.js b/tests/audio.js index 7c33fecb..29f09f76 100644 --- a/tests/audio.js +++ b/tests/audio.js @@ -23,7 +23,7 @@ function MockAudio() { default: throw new Exception("Not implemented"); } - } + }; this.removeEventListener = function(event, listener) { switch (event) { case "ended": @@ -33,14 +33,14 @@ function MockAudio() { default: throw new Exception("Not implemented"); } - } + }; function fireEnded() { setTimeout(function() { self.ended = true; self.endedListeners.forEach(function(f) { f.call(self); - }) + }); }, 0); } this.play = function() { @@ -85,7 +85,7 @@ asyncTest("setChannels", function() { delete window.Audio; //reset Audio to platform default Crafty.audio.channels = []; start(); - }) + }); }); test("chromeBug", function() { diff --git a/tests/core.js b/tests/core.js index 3e93162d..3f25d2df 100644 --- a/tests/core.js +++ b/tests/core.js @@ -64,7 +64,7 @@ test("remove", function() { removeRan = true; destroyFlag = destroyed; } - }) + }); var e = Crafty.e("comp, blank"); e.removeComponent("blank"); strictEqual(removeRan, false, "Remove doesn't run on other component removal"); @@ -76,7 +76,7 @@ test("remove", function() { removeRan = false; e.addComponent("comp"); - e.destroy() + e.destroy(); strictEqual(removeRan, true, "Remove runs on component destrution"); strictEqual(destroyFlag, true, "Destroy flag true on destruction"); }); @@ -103,15 +103,15 @@ test("setter", function() { } var first = Crafty.e("test"); first.setter('p1', function(v) { - this._p1 = v * 2 + this._p1 = v * 2; }); first.p1 = 2; strictEqual(first._p1, 4, "single property setter"); first.setter('p2', function(v) { - this._p2 = v * 2 + this._p2 = v * 2; }).setter('p3', function(v) { - this._p3 = v * 2 + this._p3 = v * 2; }); first.p2 = 2; first.p3 = 3; @@ -211,7 +211,7 @@ test("Crafty.get() to find an array", function() { equal(result[0].has("test"), true, "Result elements should have correct component"); equal(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() { Crafty.e("test"); @@ -228,7 +228,7 @@ test("Crafty.get(index) to find the indicated entity", function() { equal(result.has("test"), true, "Result should have correct component"); equal(result.getId(), collection[2], "result should be last element of collection"); -}) +}); test("Crafty.get(index) error checking", function() { Crafty.e("test"); @@ -239,22 +239,22 @@ test("Crafty.get(index) error checking", function() { collection = Crafty("test"); result = collection.get(3); - equal(typeof result, "undefined", "result of get(3) should be undefined") + equal(typeof result, "undefined", "result of get(3) should be undefined"); result = collection.get(-4); - equal(typeof result, "undefined", "result of get(-4) should be undefined") + equal(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"); result = collection.get(0); - equal(result.getId(), e.getId(), "result of get(0) is correct entity") + equal(result.getId(), e.getId(), "result of get(0) is correct entity"); result = collection.get(); equal(result.length, 1, "result of get() is array of length 1"); -}) +}); test("requires", function() { var first = Crafty.e("test"); @@ -298,10 +298,10 @@ test("Scene calling", function() { var x = 0; var sceneInit = function() { x = 13; - } + }; Crafty.scene("test-call", sceneInit); Crafty.scene("test-call"); - equal(x, 13, "Scene called succesfully.") + equal(x, 13, "Scene called succesfully."); }); @@ -309,10 +309,10 @@ test("Scene parameters", function() { var x = 0; var paramTaker = function(y) { x = y; - } + }; Crafty.scene("test-param", paramTaker); Crafty.scene("test-param", 11); - equal(x, 11, "Scene called succesfully with parameter.") + equal(x, 11, "Scene called succesfully with parameter."); }); test("Calling a scene destroys 2D entities", function() { @@ -341,13 +341,13 @@ test("Scene uninit function called", function() { var y = 0; var sceneInit = function() { x = 13; - } + }; var sceneUninit = function() { x = 20; - } + }; var sceneGame = function() { y = 5; - } + }; Crafty.defineScene("test-uninit", sceneInit, sceneUninit); Crafty.defineScene("game", sceneGame); Crafty.enterScene("test-uninit"); @@ -378,7 +378,7 @@ test("DebugCanvas", function() { e.debugFill("purple"); equal(e._debug.fillStyle, "purple", "fill style set correctly on entity"); - e.debugStroke("green") + e.debugStroke("green"); equal(e._debug.strokeStyle, "green", "stroke style set correctly on entity"); e.debugDraw(ctx); @@ -430,23 +430,23 @@ test("Hitbox debugging", function() { e.destroy(); - var e = Crafty.e("2D, Collision, SolidHitBox").attr({ + var e2 = Crafty.e("2D, Collision, SolidHitBox").attr({ x: 10, y: 10, w: 10, h: 20 }).collision(); - e.matchHitBox(); // only necessary until collision works properly! - equal(e.polygon.points[0][0], 10, "SolidHitBox -- correct x coord for upper right corner"); - equal(e.polygon.points[2][1], 30, "correct y coord for lower right corner"); - equal(typeof e._debug.strokeStyle, "undefined", "stroke style is undefined"); - notEqual(typeof e._debug.fillStyle, "undefined", "fill style is assigned"); + e2.matchHitBox(); // only necessary until collision works properly! + equal(e2.polygon.points[0][0], 10, "SolidHitBox -- correct x coord for upper right corner"); + equal(e2.polygon.points[2][1], 30, "correct y coord for lower right corner"); + equal(typeof e2._debug.strokeStyle, "undefined", "stroke style is undefined"); + notEqual(typeof e2._debug.fillStyle, "undefined", "fill style is assigned"); - e.collision(new Crafty.polygon([0, 0], [15, 0], [0, 15])); - e.matchHitBox(); - equal(e.polygon.points[2][1], 25, "After change -- correct y coord for third point"); + e2.collision(new Crafty.polygon([0, 0], [15, 0], [0, 15])); + e2.matchHitBox(); + equal(e2.polygon.points[2][1], 25, "After change -- correct y coord for third point"); - e.destroy(); + e2.destroy(); }); @@ -463,7 +463,8 @@ module("Easing", { test("Crafty.easing duration", function() { var e = new Crafty.easing(80); // 4 frames == 80ms by default equal(e.duration, 80, "Default duration in ms"); -}) +}); + test("Crafty.easing", function() { var e = new Crafty.easing(80); // 4 frames == 80ms by default e.tick(20); @@ -471,7 +472,7 @@ test("Crafty.easing", function() { equal(e.value(), 0.5, ".5 after two steps"); e.tick(20); e.tick(20); - equal(e.value(), 1, "1 after completed") + equal(e.value(), 1, "1 after completed"); e.tick(20); - equal(e.value(), 1, "Remains 1 after completion") -}) \ No newline at end of file + equal(e.value(), 1, "Remains 1 after completion"); +}); \ No newline at end of file diff --git a/tests/events.js b/tests/events.js index 21bc7247..8e8b2b1e 100644 --- a/tests/events.js +++ b/tests/events.js @@ -12,23 +12,23 @@ test("Global binding events", function() { var x = 0; function add() { - x++ + x++; } - Crafty.bind("Increment", add) - Crafty.trigger("Increment") + Crafty.bind("Increment", add); + Crafty.trigger("Increment"); strictEqual(x, 1, "Crafty.bind fired once"); x = 0; - Crafty.unbind("Increment", add) - Crafty.trigger("Increment") + Crafty.unbind("Increment", add); + Crafty.trigger("Increment"); strictEqual(x, 0, "Crafty.bind does not fire once unbound"); x = 0; - Crafty.one("Increment", add) - Crafty.trigger("Increment") - Crafty.trigger("Increment") + Crafty.one("Increment", add); + Crafty.trigger("Increment"); + Crafty.trigger("Increment"); strictEqual(x, 1, "Event bound by Crafty.one fires exactly once"); x = 0; @@ -39,7 +39,7 @@ test("Global binding events", function() { x = 0; Crafty.unbind("Increment", add); - Crafty.trigger("Increment") + Crafty.trigger("Increment"); strictEqual(x, 0, "uniqueBound does not fire once unbound"); }); @@ -49,24 +49,24 @@ test("Entity binding events", function() { var x = 0; function add() { - x++ + x++; } - var e = Crafty.e("Triggerable") + var e = Crafty.e("Triggerable"); - e.bind("Increment", add) - e.trigger("Increment") + e.bind("Increment", add); + e.trigger("Increment"); strictEqual(x, 1, ".bind fired once"); x = 0; - e.unbind("Increment", add) - e.trigger("Increment") + e.unbind("Increment", add); + e.trigger("Increment"); strictEqual(x, 0, ".bind does not fire once unbound"); x = 0; - e.one("Increment", add) - e.trigger("Increment") - e.trigger("Increment") + e.one("Increment", add); + e.trigger("Increment"); + e.trigger("Increment"); strictEqual(x, 1, "Event bound by .one fires exactly once"); x = 0; @@ -77,7 +77,7 @@ test("Entity binding events", function() { x = 0; e.unbind("Increment", add); - e.trigger("Increment") + e.trigger("Increment"); strictEqual(x, 0, "uniqueBound does not fire once unbound"); e.destroy(); @@ -147,7 +147,7 @@ test("Multiple bound events", function() { strictEqual(temp.abc, 2, "regular event should trigger twice"); strictEqual(temp.def, 1, "second one() should trigger once"); - Crafty.unbind("Event A") + Crafty.unbind("Event A"); }); @@ -156,42 +156,42 @@ test("Data passing", function() { e; function add(data) { - x += data.amount + x += data.amount; } x = 0; - e = Crafty.e("Triggerable") - e.bind("Increment", add) + e = Crafty.e("Triggerable"); + e.bind("Increment", add); e.trigger("Increment", { amount: 2 - }) + }); strictEqual(x, 2, "data passed correctly with .bind"); e.destroy(); x = 0; - e = Crafty.e("Triggerable") - e.one("Increment", add) + e = Crafty.e("Triggerable"); + e.one("Increment", add); e.trigger("Increment", { amount: 2 - }) + }); strictEqual(x, 2, "data passed correctly with .one"); e.destroy(); x = 0; - Crafty.bind("Increment", add) + Crafty.bind("Increment", add); Crafty.trigger("Increment", { amount: 2 - }) + }); strictEqual(x, 2, "data passed correctly with Crafty.bind"); - Crafty.unbind("Increment") + Crafty.unbind("Increment"); x = 0; - Crafty.one("Increment", add) + Crafty.one("Increment", add); Crafty.trigger("Increment", { amount: 3 - }) + }); strictEqual(x, 3, "data passed correctly with Crafty.one"); - Crafty.unbind("Increment") + Crafty.unbind("Increment"); diff --git a/tests/stage.js b/tests/stage.js index d25f62f3..2da503d9 100644 --- a/tests/stage.js +++ b/tests/stage.js @@ -1,11 +1,10 @@ var reset = function() { - Crafty("*").destroy(); Crafty.viewport.reset(); Crafty.viewport.scroll('_x', 0); Crafty.viewport.scroll('_y', 0); Crafty.viewport.clampToEntities = true; -} +}; test("simulateFrames", function() { var framesPlayed = 0; @@ -17,9 +16,7 @@ test("simulateFrames", function() { Crafty.timer.simulateFrames(100); equal(framesPlayed, 101, "101 frames should have been simulated"); -}) - - +}); module("Viewport", { setup: function() { @@ -92,7 +89,7 @@ test("pan", function() { var done = 0; var panDone = function() { - done++ + done++; }; Crafty.one("CameraAnimationDone", panDone); @@ -121,7 +118,7 @@ test("zoom", function() { var done = 0; var panDone = function() { - done++ + done++; }; Crafty.one("CameraAnimationDone", panDone); @@ -160,7 +157,7 @@ test("centerOn", function() { var done = 0; var panDone = function() { - done++ + done++; }; Crafty.one("CameraAnimationDone", panDone); diff --git a/tests/storage.js b/tests/storage.js index be75b1e1..a67c692f 100644 --- a/tests/storage.js +++ b/tests/storage.js @@ -7,7 +7,7 @@ test("saveAndLoadObject", function() { }); stop(); Crafty.storage.load("LeaderBoard", "save", function(lb) { - equal(lb["name"], "Matthew"); + equal(lb.name, "Matthew"); start(); }); }); @@ -23,7 +23,7 @@ test("saveAndLoadArray", function() { }]); stop(); Crafty.storage.load("LeaderBoard", "save", function(lb) { - equal(lb[1]["name"], "Louis"); + equal(lb[1].name, "Louis"); equal(lb.length, 2); start(); }); @@ -39,7 +39,7 @@ test("saveAndLoadEntity", function() { Crafty.storage.load("Hero", "save", function(hero) { console.log(hero); ok(hero.__c["2D"]); - ok(hero.__c["DOM"]) + ok(hero.__c.DOM); equal(hero.x, 0, "Entity state is not saved"); start(); }); @@ -61,13 +61,13 @@ test("individualNamespaces", function() { stop(); Crafty.storage.load("LeaderBoard", "save", function(lb) { - equal(lb["name"], "Louis"); + equal(lb.name, "Louis"); start(); }); Crafty.storage.open("MyGame3"); Crafty.storage.load("LeaderBoard", "save", function(lb) { - equal(lb["name"], "Matthew"); + equal(lb.name, "Matthew"); start(); }); -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/tests/text.js b/tests/text.js index 575cce7f..aefbd9f8 100644 --- a/tests/text.js +++ b/tests/text.js @@ -13,7 +13,7 @@ test("fontFamily", function() { family: 'Times New Roman 400', size: '30px' }).text('Test'); - equal(text.attr('_textFont')['family'], "'Times New Roman 400'", 'Expect to have singlequotes arount the family property.'); + equal(text.attr('_textFont').family, "'Times New Roman 400'", 'Expect to have singlequotes arount the family property.'); }); @@ -23,7 +23,7 @@ test("_getFontHeight", function() { equal(h, 10, "Font height is 10 pixels"); h = e._getFontHeight("10in"); equal(h, 960, "Font height is 960 pixels"); -}) +}); test("Width of canvas element", function() { var e = Crafty.e("2D, Canvas, Text"); @@ -31,18 +31,18 @@ test("Width of canvas element", function() { var w1 = e.w; e.text("abc"); var w2 = e.w; - ok(w2 > w1, "Entity increases in width when text is changed.") -}) + ok(w2 > w1, "Entity increases in width when text is changed."); +}); test("Height of canvas element", function() { var e = Crafty.e("2D, Canvas, Text"); e.text("a"); e.textFont("size", "10"); var h1 = e.h; - ok(h1 > 10, "Font height set correctly.") + ok(h1 > 10, "Font height set correctly."); e.textFont("size", "20"); var h2 = e.h; - ok(h2 > 20, "Font height set correctly.") - ok(h2 > h1, "Entity increases in height when font size is increased.") + ok(h2 > 20, "Font height set correctly."); + ok(h2 > h1, "Entity increases in height when font size is increased."); -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/tests/tween.js b/tests/tween.js index 0e986ea0..0c49a97b 100644 --- a/tests/tween.js +++ b/tests/tween.js @@ -9,7 +9,7 @@ module("Tween", { }); test("Tween", function() { - var e = Crafty.e("2D, Tween") + var e = Crafty.e("2D, Tween"); e.x = 0; e.y = 10; var ret = e.tween({ From 8021787b6db91ce94fc7204dbeaace3249748e72 Mon Sep 17 00:00:00 2001 From: Kevin Simper Date: Thu, 2 Jan 2014 13:34:18 +0100 Subject: [PATCH 49/61] Include the animation tests, as they have their own HTML for test (+2 squashed commits) Squashed commits: [68da0e6] Use Apostrophe instead [58272c2] Changed name of the index.html Qunit test markup --- Gruntfile.js | 5 +++-- tests/index.html | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 391ceb01..00b48f0a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -86,12 +86,13 @@ module.exports = function (grunt) { qunit: { all: [ - 'tests/index.html' + 'tests/index.html', + 'tests/animation/animation.html' ] }, jsvalidate: { - files: ["crafty.js", 'tests/*.js'] + files: ['crafty.js', 'tests/*.js'] }, }); diff --git a/tests/index.html b/tests/index.html index 06c07d7c..dc435be1 100755 --- a/tests/index.html +++ b/tests/index.html @@ -29,7 +29,7 @@ -

                              Crafty: Core

                              +

                              Crafty.js Test Suite

                              From 409ab21f1fa09705c3c6f9c33589b1e36e67bf80 Mon Sep 17 00:00:00 2001 From: mucaho Date: Tue, 24 Dec 2013 11:10:26 +0100 Subject: [PATCH 50/61] abstracted sprite coord computation --- src/animation.js | 20 +++++--------------- src/extensions.js | 16 +++++++--------- src/sprite.js | 21 ++++++++++++--------- tests/animation/sprite-animation.js | 8 ++++---- 4 files changed, 28 insertions(+), 37 deletions(-) diff --git a/src/animation.js b/src/animation.js index c148a7d5..e115723f 100644 --- a/src/animation.js +++ b/src/animation.js @@ -213,11 +213,7 @@ Crafty.c("SpriteAnimation", { } - var reel, i, tile, tileh, pos; - - // Get the dimensions of a single frame, as defind in Sprite component. - tile = this.__tile + parseInt(this.__padding[0] || 0, 10); - tileh = this.__tileh + parseInt(this.__padding[1] || 0, 10); + var reel, i; reel = { id: reelId, @@ -235,22 +231,18 @@ Crafty.c("SpriteAnimation", { y = fromY; if (frameCount >= 0) { for (; i < fromX + frameCount ; i++) { - reel.frames.push([i * tile, y * tileh]); + reel.frames.push([i, y]); } } else { for (; i > fromX + frameCount; i--) { - reel.frames.push([i * tile, y * tileh]); + reel.frames.push([i, y]); } } } // @sign public this .reel(String reelId, Number duration, Array frames) else if (arguments.length === 3 && typeof fromX === "object") { - // In this case, fromX is actually the array of frames - for (i=0; i < fromX.length; i++) { - pos = fromX[i]; - reel.frames.push([pos[0] * tile, pos[1] * tileh]); - } + reel.frames = fromX; } else { throw "Urecognized arguments. Please see the documentation for 'reel(...)'."; @@ -503,9 +495,7 @@ Crafty.c("SpriteAnimation", { _updateSprite: function() { var currentReel = this._currentReel; var pos = currentReel.frames[currentReel.currentFrame]; - this.__coord[0] = pos[0]; - this.__coord[1] = pos[1]; - this.trigger("Change"); // needed to trigger a redraw + this.sprite(pos[0], pos[1]); // .sprite will trigger redraw }, diff --git a/src/extensions.js b/src/extensions.js index 132160af..7b50d793 100644 --- a/src/extensions.js +++ b/src/extensions.js @@ -139,13 +139,14 @@ Crafty.extend({ /**@ * #Crafty.sprite * @category Graphics - * @sign public this Crafty.sprite([Number tile, [Number tileh]], String url, Object map[, Number paddingX[, Number paddingY]]) + * @sign public this Crafty.sprite([Number tile, [Number tileh]], String url, Object map[, Number paddingX[, Number paddingY[, Boolean paddingAroundBorder]]]) * @param tile - Tile size of the sprite map, defaults to 1 * @param tileh - Height of the tile; if provided, tile is interpreted as the width * @param url - URL of the sprite image * @param map - Object where the key is what becomes a new component and the value points to a position on the sprite map * @param paddingX - Horizontal space in between tiles. Defaults to 0. * @param paddingY - Vertical space in between tiles. Defaults to paddingX. + * @param paddingAroundBorder - If padding should be applied around the border of the sprite sheet. If enabled the first tile starts at (paddingX,paddingY) instead of (0,0). Defaults to false. * Generates components based on positions in a sprite image to be applied to entities. * * Accepts a tile size, URL and map for the name of the sprite and its position. @@ -186,7 +187,7 @@ Crafty.extend({ * * @see Sprite */ - sprite: function (tile, tileh, url, map, paddingX, paddingY) { + sprite: function (tile, tileh, url, map, paddingX, paddingY, paddingAroundBorder) { var spriteName, temp, x, y, w, h, img; //if no tile value, default to 1. @@ -235,12 +236,13 @@ Crafty.extend({ this.requires("2D, Sprite"); this.__trim = [0, 0, 0, 0]; this.__image = url; - 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.img = img; - //draw now if (this.img.complete && this.img.width > 0) { this.ready = true; @@ -256,15 +258,11 @@ Crafty.extend({ if (!map.hasOwnProperty(spriteName)) continue; temp = map[spriteName]; - x = temp[0] * (tile + paddingX); - y = temp[1] * (tileh + paddingY); - w = temp[2] * tile || tile; - h = temp[3] * tileh || tileh; //generates sprite components for each tile in the map Crafty.c(spriteName, { ready: false, - __coord: [x, y, w, h], + __coord: [temp[0], temp[1], temp[2] || 1, temp[3] || 1], init: sharedSpriteInit }); diff --git a/src/sprite.js b/src/sprite.js index 4c102b96..5a3ff9aa 100644 --- a/src/sprite.js +++ b/src/sprite.js @@ -73,13 +73,13 @@ Crafty.c("Sprite", { /**@ * #.sprite * @comp Sprite - * @sign public this .sprite(Number x, Number y, Number w, Number h) + * @sign public this .sprite(Number x, Number y[, Number w, Number h]) * @param x - X cell position * @param y - Y cell position - * @param w - Width in cells - * @param h - Height in cells + * @param w - Width in cells. Optional. + * @param h - Height in cells. Optional. * - * Uses a new location on the sprite map as its sprite. + * Uses a new location on the sprite map as its sprite. If w or h are ommitted, the width and height are not changed. * * Values should be in tiles or cells (not pixels). * @@ -97,11 +97,14 @@ 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) { - this.__coord = [x * (this.__tile + this.__padding[0]) + this.__trim[0], - y * (this.__tileh + this.__padding[1]) + this.__trim[1], - this.__trim[2] || w * this.__tile || this.__tile, - this.__trim[3] || h * this.__tileh || this.__tileh - ]; + 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[2] = this.__trim[2] || w * this.__tile || this.__tile; + this.__coord[3] = this.__trim[3] || h * this.__tileh || this.__tileh; + } this.trigger("Change"); return this; diff --git a/tests/animation/sprite-animation.js b/tests/animation/sprite-animation.js index edf0f5bc..95731aab 100644 --- a/tests/animation/sprite-animation.js +++ b/tests/animation/sprite-animation.js @@ -218,8 +218,8 @@ test("Test using .reel to set an animation using start and end values", function var frames = reel.frames; equal(frames.length, 3, "Reel has correct number of frames."); deepEqual(frames[0], [0, 0], "First frame is correct."); - deepEqual(frames[1], [64, 0], "Second frame is correct."); - deepEqual(frames[2], [128, 0], "Third frame is correct."); + deepEqual(frames[1], [1, 0], "Second frame is correct."); + deepEqual(frames[2], [2, 0], "Third frame is correct."); }) @@ -239,8 +239,8 @@ test("Test using .reel to set an animation using an array of frames", function() equal(frames.length, 3, "Reel has correct number of frames."); // This relies on the sprite being defined with a size of 64 deepEqual(frames[0], [0, 0], "First frame is correct."); - deepEqual(frames[1], [64, 0], "Second frame is correct."); - deepEqual(frames[2], [128, 0], "Third frame is correct."); + deepEqual(frames[1], [1, 0], "Second frame is correct."); + deepEqual(frames[2], [2, 0], "Third frame is correct."); }) From d7309dec1503127746ef12326fd9db56abe21e86 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Sat, 4 Jan 2014 17:26:11 -0500 Subject: [PATCH 51/61] Fix the matrix inversion test to avoid floating point issues. --- tests/math.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/math.js b/tests/math.js index 88b8c2a4..ecd9cb4a 100644 --- a/tests/math.js +++ b/tests/math.js @@ -247,8 +247,9 @@ test("determinant()", function() { }); test("invert()", function() { - equal((new Matrix2D()).scale(2, 3).rotate(Math.PI / 2).invert().equals(new Matrix2D(3.061616997868383e-17, -0.3333333333333333, 0.5, 2.041077998578922e-17, 0, 0)), - true, "(new Matrix2D()).scale(2, 3).rotate(Math.PI / 2).invert().equals(new Matrix2D(3.061616997868383e-17, -0.3333333333333333, 0.5, 2.041077998578922e-17, 0, 0))"); + 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)"); }); test("isIdentity()", function() { From 088d0e0423823aac96709fc46b8f74734bb6cb1a Mon Sep 17 00:00:00 2001 From: mucaho Date: Mon, 6 Jan 2014 19:04:38 +0100 Subject: [PATCH 52/61] fix jshint warnings --- tests/events.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/events.js b/tests/events.js index 78a90650..b0d6dacd 100644 --- a/tests/events.js +++ b/tests/events.js @@ -26,9 +26,9 @@ test("Global binding events", function() { strictEqual(x, 0, "Crafty.bind does not fire once unbound"); x = 0; - var ref = Crafty.bind("Increment", add) - Crafty.unbind("Increment", ref) - Crafty.trigger("Increment") + var ref = Crafty.bind("Increment", add); + Crafty.unbind("Increment", ref); + Crafty.trigger("Increment"); strictEqual(x, 0, "Crafty.bind does not fire once unbound via reference"); x = 0; From 435b5259662238c6b71582f46d3f89a4485c5646 Mon Sep 17 00:00:00 2001 From: Kevin Simper Date: Fri, 10 Jan 2014 10:04:56 +0100 Subject: [PATCH 53/61] Failing test for the Crafty.frame(); --- tests/core.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/core.js b/tests/core.js index 3f25d2df..410e0736 100644 --- a/tests/core.js +++ b/tests/core.js @@ -284,6 +284,19 @@ test("destroy", function() { }); +test(".frame() function", function(){ + var frameNumber; + var frameFunction = function() { + frameNumber = Crafty.frame(); + }; + Crafty.bind('EnterFrame', frameFunction); + Crafty.timer.simulateFrames(1); + + ok(frameNumber, '.frame function should return a value.'); + + Crafty.unbind(frameFunction); +}); + module("Scenes", { setup: function() { // prepare something for all following tests From dc11c9fa2730d0176c1f5fa5b14ae4105ec2b1a6 Mon Sep 17 00:00:00 2001 From: Kevin Simper Date: Fri, 10 Jan 2014 10:07:19 +0100 Subject: [PATCH 54/61] Remove frame variable so the crafty scope can access frame variable. Fixed #688 --- src/core.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core.js b/src/core.js index 48ba6f37..bd6fe16d 100644 --- a/src/core.js +++ b/src/core.js @@ -46,7 +46,8 @@ var Crafty = function (selector) { initState = function () { - GUID = 1; //GUID for entity IDs + GUID = 1, //GUID for entity IDs + frame = 0; components = {}; //map of components and their functions entities = {}; //map of entities and their data @@ -1019,8 +1020,7 @@ Crafty.extend({ // variables used by the game loop to track state var endTime = 0, timeSlip = 0, - gameTime, - frame = 0; + gameTime; // Controls the target rate of fixed mode loop. Set these with the Crafty.timer.FPS function var FPS = 50, From 15f2970e2a3927a71834d4e123d9c5edc726c889 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Fri, 10 Jan 2014 18:03:17 -0500 Subject: [PATCH 55/61] Abstract viewport resizing Add setters/getters for the viewport, and trigger a "ViewportResize" event. Use that event to alter the dimensions of the stage and canvas appropriately. --- src/DOM.js | 11 ++++++-- src/canvas.js | 13 +++++++-- src/viewport.js | 70 +++++++++++++++++++++++++++++++++++++++---------- tests/stage.js | 29 ++++++++++++++++++++ 4 files changed, 105 insertions(+), 18 deletions(-) diff --git a/src/DOM.js b/src/DOM.js index cfa4a35c..bb3f4783 100644 --- a/src/DOM.js +++ b/src/DOM.js @@ -298,8 +298,15 @@ Crafty.extend({ this.height = window.innerHeight || (window.document.documentElement.clientHeight || window.document.body.clientHeight); // Bind scene rendering (see drawing.js) - Crafty.unbind("RenderScene", Crafty.DrawManager.renderDOM); - Crafty.bind("RenderScene", Crafty.DrawManager.renderDOM); + Crafty.uniqueBind("RenderScene", Crafty.DrawManager.renderDOM); + // Resize the viewport + Crafty.uniqueBind("ViewportResize", this._resize); + + }, + + _resize: function(){ + Crafty.stage.elem.style.width = Crafty.viewport.width + "px"; + Crafty.stage.elem.style.height = Crafty.viewport.height + "px"; }, width: 0, diff --git a/src/canvas.js b/src/canvas.js index 44260acb..c11e29c6 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -207,8 +207,17 @@ Crafty.extend({ Crafty.canvas.context.scale(zoom, zoom); //Bind rendering of canvas context (see drawing.js) - Crafty.unbind("RenderScene", Crafty.DrawManager.renderCanvas); - Crafty.bind("RenderScene", Crafty.DrawManager.renderCanvas); + Crafty.uniqueBind("RenderScene", Crafty.DrawManager.renderCanvas); + + Crafty.uniqueBind("ViewportResize", this._resize); + }, + + // Resize the canvas element to the current viewport + _resize: function() { + var c = Crafty.canvas._canvas; + c.width = Crafty.viewport.width; + c.height = Crafty.viewport.height; + } } diff --git a/src/viewport.js b/src/viewport.js index 8319295e..3d77db67 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -7,6 +7,7 @@ Crafty.extend({ * @category Stage * @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 * @trigger InvalidateViewport - when the viewport changes * @trigger StopCamera - when any camera animations should stop, such as at the start of a new animation. * @trigger CameraAnimationDone - when a camera animation comes reaches completion @@ -25,8 +26,8 @@ Crafty.extend({ * For development it can be useful to set this to false. */ clampToEntities: true, - width: 0, - height: 0, + _width: 0, + _height: 0, /**@ * #Crafty.viewport.x * @comp Crafty.viewport @@ -475,9 +476,12 @@ Crafty.extend({ init: function (w, h, stage_elem) { Crafty.DOM.window.init(); + // setters+getters for the viewport + this._defineViewportProperties(); + //fullscreen if mobile or not specified - this.width = (!w || Crafty.mobile) ? Crafty.DOM.window.width : w; - this.height = (!h || Crafty.mobile) ? Crafty.DOM.window.height : h; + this._width = (!w || Crafty.mobile) ? Crafty.DOM.window.width : w; + this._height = (!h || Crafty.mobile) ? Crafty.DOM.window.height : h; //check if stage exists if (typeof stage_elem === 'undefined') @@ -587,6 +591,10 @@ Crafty.extend({ elem.height = this.height + "px"; elem.overflow = "hidden"; + + // resize events + Crafty.bind("ViewportResize", function(){Crafty.trigger("InvalidateViewport");}); + if (Crafty.mobile) { elem.position = "absolute"; elem.left = "0px"; @@ -629,6 +637,11 @@ Crafty.extend({ Crafty.stage.y = offset.y; } + + }, + + // Create setters/getters for x, y, width, height + _defineViewportProperties: function(){ if (Crafty.support.setter) { //define getters and setters to scroll the viewport this.__defineSetter__('x', function (v) { @@ -637,12 +650,28 @@ Crafty.extend({ this.__defineSetter__('y', function (v) { this.scroll('_y', v); }); + this.__defineSetter__('width', function (v) { + this._width = v; + Crafty.trigger("ViewportResize"); + }); + this.__defineSetter__('height', function (v) { + this._height = v; + Crafty.trigger("ViewportResize"); + }); this.__defineGetter__('x', function () { return this._x; }); this.__defineGetter__('y', function () { return this._y; }); + this.__defineGetter__('width', function () { + return this._width; + }); + this.__defineGetter__('height', function () { + return this._height; + }); + + //IE9 } else if (Crafty.support.defineProperty) { @@ -664,6 +693,26 @@ Crafty.extend({ }, configurable : true }); + Object.defineProperty(this, 'width', { + set: function (v) { + this._width = v; + Crafty.trigger("ViewportResize"); + }, + get: function () { + return this._width; + }, + configurable : true + }); + Object.defineProperty(this, 'height', { + set: function (v) { + this._height = v; + Crafty.trigger("ViewportResize"); + }, + get: function () { + return this._height; + }, + configurable : true + }); } }, @@ -685,16 +734,9 @@ Crafty.extend({ if (Crafty.stage.fullscreen) { - this.width = w; - this.height = h; - Crafty.stage.elem.style.width = w + "px"; - Crafty.stage.elem.style.height = h + "px"; - - if (Crafty.canvas._canvas) { - Crafty.canvas._canvas.width = w; - Crafty.canvas._canvas.height = h; - Crafty.trigger("InvalidateViewport"); - } + this._width = w; + this._height = h; + Crafty.trigger("ViewportResize"); } offset = Crafty.DOM.inner(Crafty.stage.elem); diff --git a/tests/stage.js b/tests/stage.js index 2da503d9..e1957aed 100644 --- a/tests/stage.js +++ b/tests/stage.js @@ -66,6 +66,35 @@ test("scroll using x, y", function() { equal(before.y - Crafty.DOM.translate(e.x, e.y).y, 0, "Scroll to 0"); }); +test("Viewport resizing", function(){ + var flag = 0; + var e = Crafty("2D, Canvas"); + Crafty.canvas.init(); + + var w = Crafty.viewport.width; + + equal( Crafty.canvas._canvas.width, Crafty.viewport.width, "Initial canvas size matches viewport"); + equal(Crafty.stage.elem.style.width, Crafty.viewport.width + "px", "Initial stage size matches viewport"); + Crafty.bind("ViewportResize", function(){flag++;}); + + Crafty.viewport.width += 10; + + equal(flag, 1, "ViewportResize triggered"); + equal(Crafty.viewport.width, w+10, "Viewport increased in width"); + equal( Crafty.canvas._canvas.width, Crafty.viewport.width , "Canvas size matches viewport after change"); + equal(Crafty.stage.elem.style.width, Crafty.viewport.width +"px", "Stage size matches viewport after change"); + + var h = Crafty.viewport.height; + + Crafty.viewport.height += 10; + + equal(flag, 2, "ViewportResize triggered"); + equal(Crafty.viewport.height, h+10, "Viewport increased in width"); + equal( Crafty.canvas._canvas.height, Crafty.viewport.height , "Canvas size matches viewport after change"); + equal(Crafty.stage.elem.style.height, Crafty.viewport.height +"px", "Stage size matches viewport after change"); + +}); + test("follow", function() { Crafty.viewport.clampToEntities = false; var e = Crafty.e("2D, DOM").attr({ From fadef8896b13abc21352278f6a9fb27dff653e3d Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Mon, 13 Jan 2014 03:14:35 -0500 Subject: [PATCH 56/61] Fix shared coord issue --- src/extensions.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/extensions.js b/src/extensions.js index 7b50d793..9327b0ba 100644 --- a/src/extensions.js +++ b/src/extensions.js @@ -236,6 +236,7 @@ Crafty.extend({ this.requires("2D, Sprite"); this.__trim = [0, 0, 0, 0]; this.__image = url; + this.__coord = [this.__coord[0], this.__coord[1], this.__coord[2], this.__coord[3]]; this.__tile = tile; this.__tileh = tileh; this.__padding = [paddingX, paddingY]; From bdbf1e3d5e34b655d23a1e293a6ecfaf2e71e941 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Mon, 13 Jan 2014 16:44:55 -0500 Subject: [PATCH 57/61] Switch redraw event name to "Invalidate" Currently we use "Change" to indicate both when an attribute has been changed by `attr`, and to indicate when an entity needs to be redrawn. This patch switches the name of the latter type of event to "Invalidate". Since every other instance of "Invalidate" doesn't pass any date, this no longer passes the old coordinates when an entity is invalidated becuase of a move -- the event "Move" already covers that case. --- src/2D.js | 14 +++++++------- src/DOM.js | 4 ++-- src/canvas.js | 2 +- src/drawing.js | 14 +++++++------- src/extensions.js | 4 ++-- src/sprite.js | 6 +++--- src/text.js | 14 +++++++------- src/viewport.js | 4 ++-- 8 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/2D.js b/src/2D.js index 76ad38d7..46824cd2 100644 --- a/src/2D.js +++ b/src/2D.js @@ -71,7 +71,7 @@ Crafty.extend({ * @category 2D * 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 Change - when the entity has moved - { _x:Number, _y:Number, _w:Number, _h:Number } - Old position + * @trigger Invalidate - when the entity needs to be redrawn * @trigger Rotate - when the entity is rotated - { cos:Number, sin:Number, deg:Number, rad:Number, o: {x:Number, y:Number}} */ Crafty.c("2D", { @@ -871,7 +871,7 @@ Crafty.c("2D", { /**@ * #.flip * @comp 2D - * @trigger Change - when the entity has flipped + * @trigger Invalidate - when the entity has flipped * @sign public this .flip(String dir) * @param dir - Flip direction * @@ -886,7 +886,7 @@ Crafty.c("2D", { dir = dir || "X"; if (!this["_flip" + dir]) { this["_flip" + dir] = true; - this.trigger("Change"); + this.trigger("Invalidate"); } return this; }, @@ -894,7 +894,7 @@ Crafty.c("2D", { /**@ * #.unflip * @comp 2D - * @trigger Change - when the entity has unflipped + * @trigger Invalidate - when the entity has unflipped * @sign public this .unflip(String dir) * @param dir - Unflip direction * @@ -909,7 +909,7 @@ Crafty.c("2D", { dir = dir || "X"; if (this["_flip" + dir]) { this["_flip" + dir] = false; - this.trigger("Change"); + this.trigger("Invalidate"); } return this; }, @@ -990,8 +990,8 @@ Crafty.c("2D", { //everything will assume the value this[name] = value; - //trigger a change - this.trigger("Change", old); + // flag for redraw + this.trigger("Invalidate"); Crafty._rectPool.recycle(old); } diff --git a/src/DOM.js b/src/DOM.js index cfa4a35c..8cdc999c 100644 --- a/src/DOM.js +++ b/src/DOM.js @@ -40,7 +40,7 @@ Crafty.c("DOM", { this._element.style.position = "absolute"; this._element.id = "ent" + this[0]; - this.bind("Change", function () { + this.bind("Invalidate", function () { if (!this._changed) { this._changed = true; Crafty.DrawManager.addDom(this); @@ -270,7 +270,7 @@ Crafty.c("DOM", { } } - this.trigger("Change"); + this.trigger("Invalidate"); return this; } diff --git a/src/canvas.js b/src/canvas.js index 44260acb..9d77427a 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -32,7 +32,7 @@ Crafty.c("Canvas", { this._changed = true; Crafty.DrawManager.addCanvas(this); - this.bind("Change", function (e) { + this.bind("Invalidate", function (e) { //flag if changed if (this._changed === false) { this._changed = true; diff --git a/src/drawing.js b/src/drawing.js index 24913e8e..cc7b2276 100644 --- a/src/drawing.js +++ b/src/drawing.js @@ -25,7 +25,7 @@ Crafty.c("Color", { /**@ * #.color * @comp Color - * @trigger Change - when the color changes + * @trigger Invalidate - when the color changes * @sign public this .color(String color) * @sign public String .color() * @param color - Color of the rectangle @@ -43,7 +43,7 @@ Crafty.c("Color", { color: function (color) { if (!color) return this._color; this._color = color; - this.trigger("Change"); + this.trigger("Invalidate"); return this; } }); @@ -75,7 +75,7 @@ Crafty.c("Tint", { /**@ * #.tint * @comp Tint - * @trigger Change - when the tint is applied + * @trigger Invalidate - when the tint is applied * @sign public this .tint(String color, Number strength) * @param color - The color in hexadecimal * @param strength - Level of opacity @@ -92,7 +92,7 @@ Crafty.c("Tint", { this._strength = strength; this._color = Crafty.toRGB(color, this._strength); - this.trigger("Change"); + this.trigger("Invalidate"); return this; } }); @@ -136,7 +136,7 @@ Crafty.c("Image", { /**@ * #.image * @comp Image - * @trigger Change - when the image is loaded + * @trigger Invalidate - when the image is loaded * @sign public this .image(String url[, String repeat]) * @param url - URL of the image * @param repeat - If the image should be repeated to fill the entity. @@ -183,7 +183,7 @@ Crafty.c("Image", { self.h = self.img.height; } - self.trigger("Change"); + self.trigger("Invalidate"); }; return this; @@ -197,7 +197,7 @@ Crafty.c("Image", { } - this.trigger("Change"); + this.trigger("Invalidate"); return this; } diff --git a/src/extensions.js b/src/extensions.js index 7b50d793..3ef2b786 100644 --- a/src/extensions.js +++ b/src/extensions.js @@ -216,7 +216,7 @@ Crafty.extend({ var markSpritesReady = function() { this.ready = true; - this.trigger("Change"); + this.trigger("Invalidate"); }; img = Crafty.asset(url); @@ -246,7 +246,7 @@ Crafty.extend({ //draw now if (this.img.complete && this.img.width > 0) { this.ready = true; - this.trigger("Change"); + this.trigger("Invalidate"); } //set the width and height to the sprite size diff --git a/src/sprite.js b/src/sprite.js index 5a3ff9aa..af86bf72 100644 --- a/src/sprite.js +++ b/src/sprite.js @@ -4,7 +4,7 @@ var Crafty = require('./core.js'), /**@ * #Sprite * @category Graphics - * @trigger Change - when the sprites change + * @trigger Invalidate - when the sprites change * Component for using tiles in a sprite map. */ Crafty.c("Sprite", { @@ -106,7 +106,7 @@ Crafty.c("Sprite", { this.__coord[3] = this.__trim[3] || h * this.__tileh || this.__tileh; } - this.trigger("Change"); + this.trigger("Invalidate"); return this; }, @@ -144,7 +144,7 @@ Crafty.c("Sprite", { this._w = w; this._h = h; - this.trigger("Change", old); + this.trigger("Invalidate", old); return this; } }); \ No newline at end of file diff --git a/src/text.js b/src/text.js index 641c0ab1..a9959658 100644 --- a/src/text.js +++ b/src/text.js @@ -4,7 +4,7 @@ var Crafty = require('./core.js'), /**@ * #Text * @category Graphics - * @trigger Change - when the text is changed + * @trigger Invalidate - when the text is changed * @requires Canvas or DOM * Component to make a text entity. * @@ -129,7 +129,7 @@ Crafty.c("Text", { if (this.has("Canvas") ) this._resizeForCanvas(); - this.trigger("Change"); + this.trigger("Invalidate"); return this; }, @@ -171,14 +171,14 @@ Crafty.c("Text", { textColor: function (color, strength) { this._strength = strength; this._textColor = Crafty.toRGB(color, this._strength); - this.trigger("Change"); + this.trigger("Invalidate"); return this; }, /**@ * #.textFont * @comp Text - * @triggers Change + * @triggers Invalidate * @sign public this .textFont(String key, * value) * @param key - Property of the entity to modify * @param value - Value to set the property to @@ -222,13 +222,13 @@ Crafty.c("Text", { if (this.has("Canvas") ) this._resizeForCanvas(); - this.trigger("Change"); + this.trigger("Invalidate"); return this; }, /**@ * #.unselectable * @comp Text - * @triggers Change + * @triggers Invalidate * @sign public this .unselectable() * * This method sets the text so that it cannot be selected (highlighted) by dragging. @@ -251,7 +251,7 @@ Crafty.c("Text", { '-ms-user-select': 'none', 'user-select': 'none' }); - this.trigger("Change"); + this.trigger("Invalidate"); } return this; } diff --git a/src/viewport.js b/src/viewport.js index 8319295e..41bfd073 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -201,7 +201,7 @@ Crafty.extend({ function stopFollow(){ if (oldTarget) - oldTarget.unbind('Change', change); + oldTarget.unbind('Move', change); } Crafty.bind("StopCamera", stopFollow); @@ -215,7 +215,7 @@ Crafty.extend({ offx = (typeof offsetx != 'undefined') ? offsetx : 0; offy = (typeof offsety != 'undefined') ? offsety : 0; - target.bind('Change', change); + target.bind('Move', change); change.call(target); }; })(), From ffba1c0c8eeede128b48bb83ce61018e291f1ef2 Mon Sep 17 00:00:00 2001 From: Kevin Simper Date: Sun, 12 Jan 2014 00:25:02 +0100 Subject: [PATCH 58/61] More documentation for storage function --- src/storage.js | 621 ++++--------------------------------------------- 1 file changed, 41 insertions(+), 580 deletions(-) diff --git a/src/storage.js b/src/storage.js index 5cdcebed..c6c5791f 100644 --- a/src/storage.js +++ b/src/storage.js @@ -8,608 +8,69 @@ var Crafty = require('./core.js'), /**@ * #Storage * @category Utilities - * Utility to allow data to be saved to a permanent storage solution: IndexedDB, WebSql, localstorage or cookies + * Very simple way to get and set values, which will persist when the browser is closed also. */ /**@ - * #.open + * #.storage * @comp Storage - * @sign .open(String gameName) - * @param gameName - a machine readable string to uniquely identify your game + * @sign .storage(String key) + * @param key - a key you would like to get from the storage. It will return null if the key does not exists. + * @sign .storage(String key, String value) + * @param key - the key you would like to save the data under. + * @param value - the value you would like to save. + * @sign .storage(String key, [Object value, Array value, Boolean value]) + * @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. * - * Opens a connection to the database. If the best they have is localstorage or lower, it does nothing + * Storage function is very simple and can be used to either get or set values. + * You can store both booleans, strings, objects and arrays. * * @example - * Open a database + * Get an already stored value * ~~~ - * Crafty.storage.open('MyGame'); + * var playername = Crafty.storage('playername'); * ~~~ - */ -/**@ - * #.save - * @comp Storage - * @sign .save(String key, String type, Mixed data) - * @param key - A unique key for identifying this piece of data - * @param type - 'save' or 'cache' - * @param data - Some kind of data. - * - * Saves a piece of data to the database. Can be anything, although entities are preferred. - * For all storage methods but IndexedDB, the data will be serialized as a string - * During serialization, an entity's SaveData event will be triggered. - * Components should implement a SaveData handler and attach the necessary information to the passed object * * @example - * Saves an entity to the database + * Save a value * ~~~ - * var ent = Crafty.e("2D, DOM") - * .attr({x: 20, y: 20, w: 100, h:100}); - * Crafty.storage.open('MyGame'); - * Crafty.storage.save('MyEntity', 'save', ent); + * Crafty.storage('playername', 'Hero'); * ~~~ - */ -/**@ - * #.load - * @comp Storage - * @sign .load(String key, String type) - * @param key - A unique key to search for - * @param type - 'save' or 'cache' - * @param callback - Do things with the data you get back - * - * Loads a piece of data from the database. - * Entities will be reconstructed from the serialized string - - * @example - * Loads an entity from the database - * ~~~ - * Crafty.storage.open('MyGame'); - * Crafty.storage.load('MyEntity', 'save', function (data) { // do things }); - * ~~~ - */ -/**@ - * #.getAllKeys - * @comp Storage - * @sign .getAllKeys(String type) - * @param type - 'save' or 'cache' - * Gets all the keys for a given type - - * @example - * Gets all the save games saved - * ~~~ - * Crafty.storage.open('MyGame'); - * var saves = Crafty.storage.getAllKeys('save'); - * ~~~ - */ -/**@ - * #.external - * @comp Storage - * @sign .external(String url) - * @param url - URL to an external to save games too - * - * Enables and sets the url for saving games to an external server - * - * @example - * Save an entity to an external server - * ~~~ - * Crafty.storage.external('http://somewhere.com/server.php'); - * Crafty.storage.open('MyGame'); - * var ent = Crafty.e('2D, DOM') - * .attr({x: 20, y: 20, w: 100, h:100}); - * Crafty.storage.save('save01', 'save', ent); - * ~~~ - */ -/**@ - * #SaveData event - * @comp Storage - * @param data - An object containing all of the data to be serialized - * @param prepare - The function to prepare an entity for serialization - * - * Any data a component wants to save when it's serialized should be added to this object. - * Straight attribute should be set in data.attr. - * Anything that requires a special handler should be set in a unique property. * * @example - * Saves the innerHTML of an entity + * Test to see if a value is already there. * ~~~ - * Crafty.e("2D DOM").bind("SaveData", function (data, prepare) { - * data.attr.x = this.x; - * data.attr.y = this.y; - * data.dom = this.element.innerHTML; - * }); + * var heroname = Crafty.storage('name'); + * if(!heroname){ + * // Maybe ask the player what their name is here + * heroname = 'Guest'; + * } + * // Do something with heroname * ~~~ */ -/**@ - * #LoadData event - * @comp Storage - * @param data - An object containing all the data that been saved - * @param process - The function to turn a string into an entity - * - * Handlers for processing any data that needs more than straight assignment - * - * Note that data stored in the .attr object is automatically added to the entity. - * It does not need to be handled here - * - * @example - * ~~~ - * Sets the innerHTML from a saved entity - * Crafty.e("2D DOM").bind("LoadData", function (data, process) { - * this.element.innerHTML = data.dom; - * }); - * ~~~ - */ -Crafty.storage = (function () { - var db = null, - url, gameName, timestamps = {}, - transactionType = { - READ: "readonly", - READ_WRITE: "readwrite" - }; - - /* - * Processes a retrieved object. - * Creates an entity if it is one - */ - - function process(obj) { - if (obj.c) { - var d = Crafty.e(obj.c) - .attr(obj.attr) - .trigger('LoadData', obj, process); - return d; - } else if (typeof obj == 'object') { - for (var prop in obj) { - obj[prop] = process(obj[prop]); - } - } - return obj; - } - - function unserialize(str) { - if (typeof str != 'string') return null; - var data = (JSON ? JSON.parse(str) : eval('(' + str + ')')); - return process(data); - } - /* recursive function - * searches for entities in an object and processes them for serialization - */ +Crafty.storage = function(key, value){ + var storage = window.localStorage, + _value = value; - function prep(obj) { - if (obj.__c) { - // object is entity - var data = { - c: [], - attr: {} - }; - obj.trigger("SaveData", data, prep); - for (var i in obj.__c) { - data.c.push(i); - } - data.c = data.c.join(', '); - obj = data; - } else if (typeof obj == 'object') { - // recurse and look for entities - for (var prop in obj) { - obj[prop] = prep(obj[prop]); - } - } - return obj; + if(arguments.length === 1) { + try { + return JSON.parse(storage.getItem(key)); } - - function serialize(e) { - if (JSON) { - var data = prep(e); - return JSON.stringify(data); - } else { - alert("Crafty does not support saving on your browser. Please upgrade to a newer browser."); - return false; - } + catch (e) { + return storage.getItem(key); } - - // for saving a game to a central server - - function external(setUrl) { - url = setUrl; + } else { + if(typeof value === "object") { + _value = JSON.stringify(value); } - function openExternal() { - if (1 && typeof url == "undefined") return; - // get the timestamps for external saves and compare them to local - // if the external is newer, load it + storage.setItem(key, _value); + + } - var xml = new XMLHttpRequest(); - xhr.open("POST", url); - xhr.onreadystatechange = function (evt) { - if (xhr.readyState == 4) { - if (xhr.status == 200) { - var data = eval("(" + xhr.responseText + ")"); - for (var i in data) { - if (Crafty.storage.check(data[i].key, data[i].timestamp)) { - loadExternal(data[i].key); - } - } - } - } - }; - xhr.send("mode=timestamps&game=" + gameName); - } +}; - function saveExternal(key, data, ts) { - if (1 && typeof url == "undefined") return; - var xhr = new XMLHttpRequest(); - xhr.open("POST", url); - xhr.send("mode=save&key=" + key + "&data=" + encodeURIComponent(data) + "&ts=" + ts + "&game=" + gameName); - } - - function loadExternal(key) { - if (1 && typeof url == "undefined") return; - var xhr = new XMLHttpRequest(); - xhr.open("POST", url); - xhr.onreadystatechange = function (evt) { - if (xhr.readyState == 4) { - if (xhr.status == 200) { - var data = eval("(" + xhr.responseText + ")"); - Crafty.storage.save(key, 'save', data); - } - } - }; - xhr.send("mode=load&key=" + key + "&game=" + gameName); - } - - /** - * get timestamp - */ - - function ts() { - var d = new Date(); - return d.getTime(); - } - - // everyone names their object different. Fix that nonsense. - if (typeof indexedDB != 'object') { - window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; - window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction; - window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange; - - /* Numeric constants for transaction type are deprecated - * Ensure that the script will work consistenly for recent and legacy browser versions - */ - if (typeof IDBTransaction == 'object') { - transactionType.READ = IDBTransaction.READ || IDBTransaction.readonly || transactionType.READ || 'read'; - transactionType.READ_WRITE = IDBTransaction.READ_WRITE || IDBTransaction.readwrite || transactionType.READ_WRITE || 'readwrite'; - } - } - - if (typeof indexedDB == 'object') { - - return { - open: function (gameName_n) { - gameName = gameName_n; - var stores = []; - - if (arguments.length == 1) { - stores.push('save'); - stores.push('cache'); - } else { - stores = arguments; - stores.shift(); - stores.push('save'); - stores.push('cache'); - } - if (db === null) { - var request = indexedDB.open(gameName); - request.onsuccess = function (e) { - db = e.target.result; - getTimestamps(); - openExternal(); - }; - request.onupgradeneeded = function (e) { - createStores(); - }; - } else { - createStores(); - getTimestamps(); - openExternal(); - } - - // get all the timestamps for existing keys - - function getTimestamps() { - try { - var trans = db.transaction(['save'], "read"), - store = trans.objectStore('save'), - request = store.getAll(); - request.onsuccess = function (e) { - var i = 0, - a = event.target.result, - l = a.length; - for (; i < l; i++) { - timestamps[a[i].key] = a[i].timestamp; - } - }; - } catch (e) {} - } - - function createStores() { - var request = db.setVersion("1.0"); - request.onsuccess = function (e) { - for (var i = 0; i < stores.length; i++) { - var st = stores[i]; - if (db.objectStoreNames.contains(st)) continue; - var store = db.createObjectStore(st, { - keyPath: "key" - }); - } - }; - } - }, - - save: function (key, type, data, callback) { - if (db === null) { - setTimeout(function () { - Crafty.storage.save(key, type, data); - }, 1); - return; - } - - var str = serialize(data), - t = ts(); - if (type == 'save') saveExternal(key, str, t); - try { - var request = db.transaction([type], transactionType.READ_WRITE).objectStore(type).add({ - "data": str, - "timestamp": t, - "key": key - }); - if (typeof callback == 'function') { - request.onsuccess = callback; - } - } catch (e) { - console.error(e); - } - }, - - load: function (key, type, callback) { - if (db === null) { - setTimeout(function () { - Crafty.storage.load(key, type, callback); - }, 1); - return; - } - try { - var request = db.transaction([type], transactionType.READ).objectStore(type).get(key); - request.onsuccess = function (e) { - callback(unserialize(e.target.result.data)); - }; - } catch (e) { - console.error(e); - } - }, - - getAllKeys: function (type, callback) { - if (db === null) { - setTimeout(function () { - Crafty.storage.getAllkeys(type, callback); - }, 1); - } - try { - var request = db.transaction([type], transactionType.READ).objectStore(type).openCursor(), - res = []; - request.onsuccess = function (e) { - var cursor = e.target.result; - if (cursor) { - res.push(cursor.key); - // 'continue' is a reserved word, so .continue() causes IE8 to completely bark with "SCRIPT1010: Expected identifier". - cursor['continue'](); - } else { - callback(res); - } - }; - } catch (e) { - console.error(e); - } - }, - - check: function (key, timestamp) { - return (timestamps[key] > timestamp); - }, - - external: external - }; - } else if (typeof openDatabase == 'function') { - return { - open: function (gameName_n) { - gameName = gameName_n; - if (arguments.length == 1) { - db = { - save: openDatabase(gameName_n + '_save', '1.0', 'Saves games for ' + gameName_n, 5 * 1024 * 1024), - cache: openDatabase(gameName_n + '_cache', '1.0', 'Cache for ' + gameName_n, 5 * 1024 * 1024) - }; - } else { - // allows for any other types that can be thought of - var args = arguments, - i = 0; - args.shift(); - for (; i < args.length; i++) { - if (typeof db[args[i]] == 'undefined') - db[args[i]] = openDatabase(gameName + '_' + args[i], '1.0', type, 5 * 1024 * 1024); - } - } - - db.save.transaction(function (tx) { - tx.executeSql('SELECT key, timestamp FROM data', [], function (tx, res) { - var i = 0, - a = res.rows, - l = a.length; - for (; i < l; i++) { - timestamps[a.item(i).key] = a.item(i).timestamp; - } - }); - }); - }, - - save: function (key, type, data) { - if (typeof db[type] == 'undefined' && gameName !== '') { - this.open(gameName, type); - } - - var str = serialize(data), - t = ts(); - if (type == 'save') saveExternal(key, str, t); - db[type].transaction(function (tx) { - tx.executeSql('CREATE TABLE IF NOT EXISTS data (key unique, text, timestamp)'); - tx.executeSql('SELECT * FROM data WHERE key = ?', [key], function (tx, results) { - if (results.rows.length) { - tx.executeSql('UPDATE data SET text = ?, timestamp = ? WHERE key = ?', [str, t, key]); - } else { - tx.executeSql('INSERT INTO data VALUES (?, ?, ?)', [key, str, t]); - } - }); - }); - }, - - load: function (key, type, callback) { - if (typeof db[type] === 'undefined') { - setTimeout(function () { - Crafty.storage.load(key, type, callback); - }, 1); - return; - } - db[type].transaction(function (tx) { - tx.executeSql('SELECT text FROM data WHERE key = ?', [key], function (tx, results) { - if (results.rows.length) { - res = unserialize(results.rows.item(0).text); - callback(res); - } - }); - }); - }, - - getAllKeys: function (type, callback) { - if (typeof db[type] === 'undefined') { - setTimeout(function () { - Crafty.storage.getAllKeys(type, callback); - }, 1); - return; - } - db[type].transaction(function (tx) { - tx.executeSql('SELECT key FROM data', [], function (tx, results) { - callback(results.rows); - }); - }); - }, - - check: function (key, timestamp) { - return (timestamps[key] > timestamp); - }, - - external: external - }; - } else if (typeof window.localStorage == 'object') { - return { - open: function (gameName_n) { - gameName = gameName_n; - }, - - save: function (key, type, data) { - var k = gameName + '.' + type + '.' + key, - str = serialize(data), - t = ts(); - if (type == 'save') saveExternal(key, str, t); - window.localStorage[k] = str; - if (type == 'save') - window.localStorage[k + '.ts'] = t; - }, - - load: function (key, type, callback) { - var k = gameName + '.' + type + '.' + key, - str = window.localStorage[k]; - - callback(unserialize(str)); - }, - - getAllKeys: function (type, callback) { - var res = {}, output = [], - header = gameName + '.' + type; - for (var i in window.localStorage) { - if (i.indexOf(header) != -1) { - var key = i.replace(header, '').replace('.ts', ''); - res[key] = true; - } - } - for (i in res) { - output.push(i); - } - callback(output); - }, - - check: function (key, timestamp) { - var ts = window.localStorage[gameName + '.save.' + key + '.ts']; - - return (parseInt(timestamp, 10) > parseInt(ts, 10)); - }, - - external: external - }; - } else { - // default fallback to cookies - return { - open: function (gameName_n) { - gameName = gameName_n; - }, - - save: function (key, type, data) { - // cookies are very limited in space. we can only keep saves there - if (type != 'save') return; - var str = serialize(data), - t = ts(); - if (type == 'save') saveExternal(key, str, t); - document.cookie = gameName + '_' + key + '=' + str + '; ' + gameName + '_' + key + '_ts=' + t + '; expires=Thur, 31 Dec 2099 23:59:59 UTC; path=/'; - }, - - load: function (key, type, callback) { - if (type != 'save') return; - var reg = new RegExp(gameName + '_' + key + '=[^;]*'), - result = reg.exec(document.cookie), - data = unserialize(result[0].replace(gameName + '_' + key + '=', '')); - - callback(data); - }, - - getAllKeys: function (type, callback) { - if (type != 'save') return; - var reg = new RegExp(gameName + '_[^_=]', 'g'), - matches = reg.exec(document.cookie), - i = 0, - l = matches.length, - res = {}, output = []; - for (; i < l; i++) { - var key = matches[i].replace(gameName + '_', ''); - res[key] = true; - } - for (i in res) { - output.push(i); - } - callback(output); - }, - - check: function (key, timestamp) { - var header = gameName + '_' + key + '_ts', - reg = new RegExp(header + '=[^;]'), - result = reg.exec(document.cookie), - ts = result[0].replace(header + '=', ''); - - return (parseInt(timestamp, 10) > parseInt(ts, 10)); - }, - - external: external - }; - } - /* template - return { - open: function (gameName) { - }, - save: function (key, type, data) { - }, - load: function (key, type, callback) { - }, - }*/ -})(); \ No newline at end of file +Crafty.storage.remove = function(key){ + window.localStorage.removeItem(key); +}; \ No newline at end of file From 3558a92fe23df2f801e1c8e357b01972a3dce09d Mon Sep 17 00:00:00 2001 From: Kevin Simper Date: Sun, 12 Jan 2014 00:25:39 +0100 Subject: [PATCH 59/61] Added the storage to the test again New tests for storage.js --- tests/index.html | 1 + tests/storage.js | 88 +++++++++++++----------------------------------- 2 files changed, 25 insertions(+), 64 deletions(-) diff --git a/tests/index.html b/tests/index.html index dc435be1..4a1dfdf7 100755 --- a/tests/index.html +++ b/tests/index.html @@ -54,6 +54,7 @@

                              + diff --git a/tests/storage.js b/tests/storage.js index a67c692f..da43b83e 100644 --- a/tests/storage.js +++ b/tests/storage.js @@ -1,73 +1,33 @@ -module("Storage"); -test("saveAndLoadObject", function() { - Crafty.storage.open("MyGame"); - Crafty.storage.save("LeaderBoard", "save", { - name: "Matthew", - score: 150 - }); - stop(); - Crafty.storage.load("LeaderBoard", "save", function(lb) { - equal(lb.name, "Matthew"); - start(); - }); +module("Storage", { + setup: function() { + reset(); + }, + teardown: function() { + // clean up after each test + Crafty("*").destroy(); + } }); -test("saveAndLoadArray", function() { - Crafty.storage.open("MyGame1"); - Crafty.storage.save("LeaderBoard", "save", [{ - name: "Matthew", - score: 150 - }, { - name: "Louis", - score: 17 - }]); - stop(); - Crafty.storage.load("LeaderBoard", "save", function(lb) { - equal(lb[1].name, "Louis"); - equal(lb.length, 2); - start(); - }); -}); +test('get a value', function(){ + Crafty.storage('name', 'test'); + var name = Crafty.storage('name'); -test("saveAndLoadEntity", function() { - Crafty.storage.open("MyGame2"); - Crafty.storage.save("Hero", "save", Crafty.e("2D, DOM").attr({ - x: 20, - y: 137 - })); - stop(); - Crafty.storage.load("Hero", "save", function(hero) { - console.log(hero); - ok(hero.__c["2D"]); - ok(hero.__c.DOM); - equal(hero.x, 0, "Entity state is not saved"); - start(); - }); -}); + equal(name, 'test', 'the values should be equal'); -test("individualNamespaces", function() { - Crafty.storage.open("MyGame3"); - Crafty.storage.save("LeaderBoard", "save", { - name: "Matthew", - score: 150 - }); + Crafty.storage.remove('name'); +}); +test('get null when a value does not exist', function(){ + var name = Crafty.storage('notexisting'); + equal(name, null, 'should be null'); +}); - Crafty.storage.open("MyGame4"); - Crafty.storage.save("LeaderBoard", "save", { - name: "Louis", - score: 150 - }); +test('remove an value', function(){ + var person = Crafty.storage('person', 'test'); + equal(Crafty.storage('person'), 'test', 'person should be defined'); - stop(); - Crafty.storage.load("LeaderBoard", "save", function(lb) { - equal(lb.name, "Louis"); - start(); - }); + Crafty.storage.remove('person'); - Crafty.storage.open("MyGame3"); - Crafty.storage.load("LeaderBoard", "save", function(lb) { - equal(lb.name, "Matthew"); - start(); - }); + var savedperson = Crafty.storage('person'); + equal(savedperson, null, 'should be null because we just removed the value'); }); \ No newline at end of file From f61e5f55eba050a67de346a9b5d685f2a806e787 Mon Sep 17 00:00:00 2001 From: Kevin Simper Date: Wed, 15 Jan 2014 00:37:33 +0100 Subject: [PATCH 60/61] Updated the documentation for the storage function explaining when to use it If the localStorage object is not supported return false. Added documentation for remove function --- src/storage.js | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/storage.js b/src/storage.js index c6c5791f..66962158 100644 --- a/src/storage.js +++ b/src/storage.js @@ -1,10 +1,6 @@ var Crafty = require('./core.js'), document = window.document; -// Directive for jshint to ignore evals -// eval is used to support IE8, so this can be removed if we decide to drop that -/* jshint evil:true */ - /**@ * #Storage * @category Utilities @@ -25,6 +21,8 @@ var Crafty = require('./core.js'), * Storage function is very simple and can be used to either get or set values. * You can store both booleans, strings, objects and arrays. * + * Please note: You should not store data, while the game is playing, as it can cause the game to slow down. You should load data when you start the game, or when the user for an example click a "Save gameprocess" button. + * * @example * Get an already stored value * ~~~ @@ -53,6 +51,10 @@ Crafty.storage = function(key, value){ var storage = window.localStorage, _value = value; + if(!storage){ + return false; + } + if(arguments.length === 1) { try { return JSON.parse(storage.getItem(key)); @@ -70,7 +72,23 @@ Crafty.storage = function(key, value){ } }; - +/**@ + * #.storage.remove + * @comp Storage + * @sign .storage.remove(String key) + * @param key - a key where you will like to delete the value of. + * + * Generally you do not need to remove values from localStorage, but if you do + * store large amount of text, or want to unset something you can do that with + * this function. + * + * @example + * Get an already stored value + * ~~~ + * Crafty.storage.remove('playername'); + * ~~~ + * + */ Crafty.storage.remove = function(key){ window.localStorage.removeItem(key); }; \ No newline at end of file From 56b3f0fb073d19351227031f7c938395961310b1 Mon Sep 17 00:00:00 2001 From: Kevin Simper Date: Wed, 15 Jan 2014 12:38:16 +0100 Subject: [PATCH 61/61] Move sprite function to sprite.js --- src/extensions.js | 138 +-------------------------------------------- src/sprite.js | 139 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 137 deletions(-) diff --git a/src/extensions.js b/src/extensions.js index 372b8de7..aabeece1 100644 --- a/src/extensions.js +++ b/src/extensions.js @@ -134,144 +134,8 @@ var Crafty = require('./core.js'), support.devicemotion = (typeof window.DeviceMotionEvent !== "undefined"); })(); -Crafty.extend({ - - /**@ - * #Crafty.sprite - * @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 - * @param tileh - Height of the tile; if provided, tile is interpreted as the width - * @param url - URL of the sprite image - * @param map - Object where the key is what becomes a new component and the value points to a position on the sprite map - * @param paddingX - Horizontal space in between tiles. Defaults to 0. - * @param paddingY - Vertical space in between tiles. Defaults to paddingX. - * @param paddingAroundBorder - If padding should be applied around the border of the sprite sheet. If enabled the first tile starts at (paddingX,paddingY) instead of (0,0). Defaults to false. - * Generates components based on positions in a sprite image to be applied to entities. - * - * Accepts a tile size, URL and map for the name of the sprite and its position. - * - * The position must be an array containing the position of the sprite where index `0` - * is the `x` position, `1` is the `y` position and optionally `2` is the width and `3` - * is the height. If the sprite map has padding, pass the values for the `x` padding - * or `y` padding. If they are the same, just add one value. - * - * If the sprite image has no consistent tile size, `1` or no argument need be - * passed for tile size. - * - * Entities that add the generated components are also given the `2D` component, and - * a component called `Sprite`. - * - * @example - * ~~~ - * Crafty.sprite("imgs/spritemap6.png", {flower:[0,0,20,30]}); - * var flower_entity = Crafty.e("2D, DOM, flower"); - * ~~~ - * The first line creates a component called `flower` associated with the sub-image of - * spritemap6.png with top-left corner (0,0), width 20 pixels, and height 30 pixels. - * The second line creates an entity with that image. (Note: The `2D` is not really - * necessary here, because adding the `flower` component automatically also adds the - * `2D` component.) - * ~~~ - * Crafty.sprite(50, "imgs/spritemap6.png", {flower:[0,0], grass:[0,1,3,1]}); - * ~~~ - * In this case, the `flower` component is pixels 0 <= x < 50, 0 <= y < 50, and the - * `grass` component is pixels 0 <= x < 150, 50 <= y < 100. (The `3` means grass has a - * width of 3 tiles, i.e. 150 pixels.) - * ~~~ - * Crafty.sprite(50, 100, "imgs/spritemap6.png", {flower:[0,0], grass:[0,1]}, 10); - * ~~~ - * In this case, each tile is 50x100, and there is a spacing of 10 pixels between - * consecutive tiles. So `flower` is pixels 0 <= x < 50, 0 <= y < 100, and `grass` is - * pixels 0 <= x < 50, 110 <= y < 210. - * - * @see Sprite - */ - sprite: function (tile, tileh, url, map, paddingX, paddingY, paddingAroundBorder) { - var spriteName, temp, x, y, w, h, img; - - //if no tile value, default to 1. - //(if the first passed argument is a string, it must be the url.) - if (typeof tile === "string") { - paddingY = paddingX; - paddingX = map; - map = tileh; - url = tile; - tile = 1; - tileh = 1; - } - - if (typeof tileh == "string") { - paddingY = paddingX; - paddingX = map; - map = url; - url = tileh; - tileh = tile; - } - - //if no paddingY, use paddingX - if (!paddingY && paddingX) paddingY = paddingX; - paddingX = parseInt(paddingX || 0, 10); //just incase - paddingY = parseInt(paddingY || 0, 10); - - var markSpritesReady = function() { - this.ready = true; - this.trigger("Invalidate"); - }; - - img = Crafty.asset(url); - if (!img) { - img = new Image(); - img.src = url; - Crafty.asset(url, img); - img.onload = function () { - //all components with this img are now ready - for (var spriteName in map) { - Crafty(spriteName).each(markSpritesReady); - } - }; - } - - var sharedSpriteInit = function() { - this.requires("2D, Sprite"); - this.__trim = [0, 0, 0, 0]; - this.__image = url; - 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.img = img; - //draw now - if (this.img.complete && this.img.width > 0) { - this.ready = true; - this.trigger("Invalidate"); - } - - //set the width and height to the sprite size - this.w = this.__coord[2]; - this.h = this.__coord[3]; - }; - - for (spriteName in map) { - if (!map.hasOwnProperty(spriteName)) continue; - - temp = map[spriteName]; - - //generates sprite components for each tile in the map - Crafty.c(spriteName, { - ready: false, - __coord: [temp[0], temp[1], temp[2] || 1, temp[3] || 1], - - init: sharedSpriteInit - }); - } - - return this; - }, +Crafty.extend({ _events: {}, /**@ diff --git a/src/sprite.js b/src/sprite.js index af86bf72..dba19be8 100644 --- a/src/sprite.js +++ b/src/sprite.js @@ -1,6 +1,145 @@ var Crafty = require('./core.js'), document = window.document; +Crafty.extend({ + + /**@ + * #Crafty.sprite + * @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 + * @param tileh - Height of the tile; if provided, tile is interpreted as the width + * @param url - URL of the sprite image + * @param map - Object where the key is what becomes a new component and the value points to a position on the sprite map + * @param paddingX - Horizontal space in between tiles. Defaults to 0. + * @param paddingY - Vertical space in between tiles. Defaults to paddingX. + * @param paddingAroundBorder - If padding should be applied around the border of the sprite sheet. If enabled the first tile starts at (paddingX,paddingY) instead of (0,0). Defaults to false. + * Generates components based on positions in a sprite image to be applied to entities. + * + * Accepts a tile size, URL and map for the name of the sprite and its position. + * + * The position must be an array containing the position of the sprite where index `0` + * is the `x` position, `1` is the `y` position and optionally `2` is the width and `3` + * is the height. If the sprite map has padding, pass the values for the `x` padding + * or `y` padding. If they are the same, just add one value. + * + * If the sprite image has no consistent tile size, `1` or no argument need be + * passed for tile size. + * + * Entities that add the generated components are also given the `2D` component, and + * a component called `Sprite`. + * + * @example + * ~~~ + * Crafty.sprite("imgs/spritemap6.png", {flower:[0,0,20,30]}); + * var flower_entity = Crafty.e("2D, DOM, flower"); + * ~~~ + * The first line creates a component called `flower` associated with the sub-image of + * spritemap6.png with top-left corner (0,0), width 20 pixels, and height 30 pixels. + * The second line creates an entity with that image. (Note: The `2D` is not really + * necessary here, because adding the `flower` component automatically also adds the + * `2D` component.) + * ~~~ + * Crafty.sprite(50, "imgs/spritemap6.png", {flower:[0,0], grass:[0,1,3,1]}); + * ~~~ + * In this case, the `flower` component is pixels 0 <= x < 50, 0 <= y < 50, and the + * `grass` component is pixels 0 <= x < 150, 50 <= y < 100. (The `3` means grass has a + * width of 3 tiles, i.e. 150 pixels.) + * ~~~ + * Crafty.sprite(50, 100, "imgs/spritemap6.png", {flower:[0,0], grass:[0,1]}, 10); + * ~~~ + * In this case, each tile is 50x100, and there is a spacing of 10 pixels between + * consecutive tiles. So `flower` is pixels 0 <= x < 50, 0 <= y < 100, and `grass` is + * pixels 0 <= x < 50, 110 <= y < 210. + * + * @see Sprite + */ + sprite: function (tile, tileh, url, map, paddingX, paddingY, paddingAroundBorder) { + var spriteName, temp, x, y, w, h, img; + + //if no tile value, default to 1. + //(if the first passed argument is a string, it must be the url.) + if (typeof tile === "string") { + paddingY = paddingX; + paddingX = map; + map = tileh; + url = tile; + tile = 1; + tileh = 1; + } + + if (typeof tileh == "string") { + paddingY = paddingX; + paddingX = map; + map = url; + url = tileh; + tileh = tile; + } + + //if no paddingY, use paddingX + if (!paddingY && paddingX) paddingY = paddingX; + paddingX = parseInt(paddingX || 0, 10); //just incase + paddingY = parseInt(paddingY || 0, 10); + + var markSpritesReady = function() { + this.ready = true; + this.trigger("Invalidate"); + }; + + img = Crafty.asset(url); + if (!img) { + img = new Image(); + img.src = url; + Crafty.asset(url, img); + img.onload = function () { + //all components with this img are now ready + for (var spriteName in map) { + Crafty(spriteName).each(markSpritesReady); + } + }; + } + + var sharedSpriteInit = function() { + this.requires("2D, Sprite"); + this.__trim = [0, 0, 0, 0]; + this.__image = url; + 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.img = img; + //draw now + if (this.img.complete && this.img.width > 0) { + this.ready = true; + this.trigger("Invalidate"); + } + + //set the width and height to the sprite size + this.w = this.__coord[2]; + this.h = this.__coord[3]; + }; + + for (spriteName in map) { + if (!map.hasOwnProperty(spriteName)) continue; + + temp = map[spriteName]; + + //generates sprite components for each tile in the map + Crafty.c(spriteName, { + ready: false, + __coord: [temp[0], temp[1], temp[2] || 1, temp[3] || 1], + + init: sharedSpriteInit + }); + } + + return this; + } +}); + /**@ * #Sprite * @category Graphics