diff --git a/package.json b/package.json index f50de102..905bad9e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "craftyjs", - "version": "0.6.3-beta", + "version": "0.6.3", "title": "Crafty game framework", "author": { "name": "Louis Stowasser", diff --git a/src/collision.js b/src/collision.js index d3019b79..8b8f71bd 100644 --- a/src/collision.js +++ b/src/collision.js @@ -596,21 +596,21 @@ Crafty.c("Collision", { normal.y /= length; //default min max - min1 = min2 = -1; - max1 = max2 = -1; + min1 = min2 = Infinity; + max1 = max2 = -Infinity; //project all vertices from poly1 onto axis for (j = 0; j < l; ++j) { dot = points1[j][0] * normal.x + points1[j][1] * normal.y; - if (dot > max1 || max1 === -1) max1 = dot; - if (dot < min1 || min1 === -1) min1 = dot; + if (dot > max1) max1 = dot; + if (dot < min1) min1 = dot; } //project all vertices from poly2 onto axis for (j = 0; j < k; ++j) { dot = points2[j][0] * normal.x + points2[j][1] * normal.y; - if (dot > max2 || max2 === -1) max2 = dot; - if (dot < min2 || min2 === -1) min2 = dot; + if (dot > max2) max2 = dot; + if (dot < min2 ) min2 = dot; } //calculate the minimum translation vector should be negative @@ -652,21 +652,21 @@ Crafty.c("Collision", { normal.y /= length; //default min max - min1 = min2 = -1; - max1 = max2 = -1; + min1 = min2 = Infinity; + max1 = max2 = -Infinity; //project all vertices from poly1 onto axis for (j = 0; j < l; ++j) { dot = points1[j][0] * normal.x + points1[j][1] * normal.y; - if (dot > max1 || max1 === -1) max1 = dot; - if (dot < min1 || min1 === -1) min1 = dot; + if (dot > max1) max1 = dot; + if (dot < min1) min1 = dot; } //project all vertices from poly2 onto axis for (j = 0; j < k; ++j) { dot = points2[j][0] * normal.x + points2[j][1] * normal.y; - if (dot > max2 || max2 === -1) max2 = dot; - if (dot < min2 || min2 === -1) min2 = dot; + if (dot > max2) max2 = dot; + if (dot < min2) min2 = dot; } //calculate the minimum translation vector should be negative diff --git a/src/core.js b/src/core.js index be58bfcb..1baafd60 100644 --- a/src/core.js +++ b/src/core.js @@ -39,25 +39,25 @@ var version = require('./version'); var Crafty = function (selector) { return new Crafty.fn.init(selector); -}, +}; // Internal variables - GUID, frame, components, entities, handlers, onloads, - slice, rlist, rspace, milliSecPerFrame; +var GUID, frame, components, entities, handlers, onloads, +slice, rlist, rspace, milliSecPerFrame; - initState = function () { - GUID = 1; //GUID for entity IDs - frame = 0; +components = {}; // Map of components and their functions +slice = Array.prototype.slice; +rlist = /\s*,\s*/; +rspace = /\s+/; - components = {}; //map of components and their functions - entities = {}; //map of entities and their data - handlers = {}; //global event handlers - onloads = []; //temporary storage of onload handlers +var initState = function () { + GUID = 1; // GUID for entity IDs + frame = 0; - slice = Array.prototype.slice; - rlist = /\s*,\s*/; - rspace = /\s+/; - }; + entities = {}; // Map of entities and their data + handlers = {}; // Global event handlers + onloads = []; // Temporary storage of onload handlers +}; initState(); @@ -637,24 +637,27 @@ Crafty.fn = Crafty.prototype = { bind: function (event, callback) { // (To learn how the handlers object works, see inline comment at Crafty.bind) + var h = handlers[event] || (handlers[event] = {}), callbacks; //optimization for 1 entity if (this.length === 1) { - if (!handlers[event]) handlers[event] = {}; - var h = handlers[event]; - - if (!h[this[0]]) h[this[0]] = []; //init handler array for entity - h[this[0]].push(callback); //add current callback + callbacks = h[this[0]]; + if (!callbacks) { + callbacks = h[this[0]] = []; //init handler array for entity + callbacks.depth = 0; // metadata indicating call depth + } + callbacks.push(callback); //add current callback return this; } this.each(function () { //init event collection - if (!handlers[event]) handlers[event] = {}; - var h = handlers[event]; - - if (!h[this[0]]) h[this[0]] = []; //init handler array for entity - h[this[0]].push(callback); //add current callback + callbacks = h[this[0]]; + if (!callbacks) { + callbacks = h[this[0]] = []; //init handler array for entity + callbacks.depth = 0; // metadata indicating call depth + } + callbacks.push(callback); //add current callback }); return this; }, @@ -715,7 +718,7 @@ Crafty.fn = Crafty.prototype = { unbind: function (event, callback) { // (To learn how the handlers object works, see inline comment at Crafty.bind) this.each(function () { - var hdl = handlers[event], + var hdl = handlers[event] || (handlers[event] = {}), i = 0, l, current; //if no events, cancel @@ -756,20 +759,24 @@ Crafty.fn = Crafty.prototype = { * Unlike DOM events, Crafty events are exectued synchronously. */ trigger: function (event, data) { + var h = handlers[event] || (handlers[event] = {}); // (To learn how the handlers object works, see inline comment at Crafty.bind) if (this.length === 1) { - //find the handlers assigned to the event and entity - if (handlers[event] && handlers[event][this[0]]) { - var callbacks = handlers[event][this[0]], - i; - for (i = 0; i < callbacks.length; i++) { - if (typeof callbacks[i] === "undefined") { + //find the handlers assigned to the entity + if (h && h[this[0]]) { + var callbacks = h[this[0]], + i, l=callbacks.length; + callbacks.depth++; + for (i = 0; i < l; i++) { + if (typeof callbacks[i] === "undefined" && callbacks.depth<=1) { callbacks.splice(i, 1); i--; + l--; } else { callbacks[i].call(this, data); } } + callbacks.depth--; } return this; } @@ -778,15 +785,18 @@ Crafty.fn = Crafty.prototype = { //find the handlers assigned to the event and entity if (handlers[event] && handlers[event][this[0]]) { var callbacks = handlers[event][this[0]], - i; - for (i = 0; i < callbacks.length; i++) { - if (typeof callbacks[i] === "undefined") { + i, l=callbacks.length; + callbacks.depth++; + for (i = 0; i < l; i++) { + if (typeof callbacks[i] === "undefined" && callbacks.depth<=1) { callbacks.splice(i, 1); i--; + l--; } else { callbacks[i].call(this, data); } } + callbacks.depth--; } }); return this; @@ -1456,7 +1466,7 @@ Crafty.extend({ trigger: function (event, data) { // (To learn how the handlers object works, see inline comment at Crafty.bind) - var hdl = handlers[event], + var hdl = handlers[event] || (handlers[event] = {}), h, i, l, callbacks, context; //loop over every object bound for (h in hdl) { @@ -1469,18 +1479,24 @@ Crafty.extend({ //if an entity, call with that context; else the global context if (entities[h]) context = Crafty(+h); - else + else if (h === 'global') context = Crafty; + else + continue; + callbacks.depth++; + l = callbacks.length; //loop over every handler within object - for (i = 0; i < callbacks.length; i++) { + for (i = 0; i < l; i++) { // Remove a callback if it has been deleted - if (typeof callbacks[i] === "undefined") { + if (typeof callbacks[i] === "undefined" && callbacks.depth <=1) { callbacks.splice(i, 1); i--; + l--; } else callbacks[i].call(context, data); } + callbacks.depth--; } }, @@ -1518,10 +1534,12 @@ Crafty.extend({ // // handlers[event][entityID or 'global'] === (Array of callback functions) - if (!handlers[event]) handlers[event] = {}; - var hdl = handlers[event]; + var hdl = handlers[event] || (handlers[event] = {}); - if (!hdl.global) hdl.global = []; + if (!hdl.global) { + hdl.global = []; + hdl.global.depth =0; + } hdl.global.push(callback); return callback; }, diff --git a/src/crafty.js b/src/crafty.js index f3d539bd..d3d32b2d 100644 --- a/src/crafty.js +++ b/src/crafty.js @@ -29,4 +29,6 @@ require('./time'); require('./version'); require('./viewport'); -window.Crafty = Crafty; \ No newline at end of file +if(window) window.Crafty = Crafty; + +module.exports = Crafty; \ No newline at end of file diff --git a/src/loader.js b/src/loader.js index f69ce95d..0deaafc5 100644 --- a/src/loader.js +++ b/src/loader.js @@ -155,8 +155,8 @@ Crafty.extend({ * "images" and "sprites" properties, only those you'll need. For example, if you don't need to preload * sprites, you can omit that property. * - * Default folders for storing and locating assets are 'assets/audio' and 'assets/images'. For changing these, - * use the function Crafty.paths. + * By default, Crafty will assume all files are in the current path. For changing these, + * use the function `Crafty.paths`. * * Files with suffixes in `image_whitelist` (case insensitive) will be loaded. * @@ -226,12 +226,12 @@ Crafty.extend({ */ load: function (data, oncomplete, onprogress, onerror) { - data = typeof data === "string"?JSON.parse(data):data; + data = (typeof data === "string" ? JSON.parse(data) : data); var j = 0, - total = (data.audio? Object.keys(data.audio).length : 0) + - (data.images? Object.keys(data.images).length : 0) + - (data.sprites? Object.keys(data.sprites).length : 0), + total = (data.audio ? Object.keys(data.audio).length : 0) + + (data.images ? Object.keys(data.images).length : 0) + + (data.sprites ? Object.keys(data.sprites).length : 0), current, fileUrl, obj, type, asset, audSupport = Crafty.support.audio, paths = Crafty.paths(), @@ -239,7 +239,7 @@ Crafty.extend({ return f.substr(f.lastIndexOf('.') + 1, 3).toLowerCase(); }, getFilePath = function(type,f) { - return f.search("://") === -1? (type=="audio"? paths.audio + f : paths.images + f) : f; + return (f.search("://") === -1 ? (type == "audio" ? paths.audio + f : paths.images + f) : f); }, // returns null if 'a' is not already a loaded asset, obj otherwise isAsset = function(a) { @@ -267,7 +267,7 @@ Crafty.extend({ if (this.removeEventListener) this.removeEventListener('canplaythrough', pro, false); - ++j; + j++; //if progress callback, give information of assets loaded, total and percent if (onprogress) onprogress({ @@ -320,7 +320,7 @@ Crafty.extend({ if (obj && obj.addEventListener) obj.addEventListener('canplaythrough', pro, false); } else { - asset = type === "sprites"? asset : current; + asset = (type === "sprites" ? asset : current); fileUrl = getFilePath(type, asset); if (isValidImage(asset)) { obj = isAsset(fileUrl); @@ -395,12 +395,12 @@ Crafty.extend({ */ removeAssets: function(data) { - data = typeof data === "string"?JSON.parse(data):data; + data = (typeof data === "string" ? JSON.parse(data) : data); var current, fileUrl, type, asset, paths = Crafty.paths(), getFilePath = function(type,f) { - return f.search("://") === -1? (type=="audio"? paths.audio + f : paths.images + f) : f; + return (f.search("://") === -1 ? (type == "audio" ? paths.audio + f : paths.images + f) : f); }; for (type in data) { @@ -422,7 +422,7 @@ Crafty.extend({ Crafty.audio.remove(asset); } } else { - asset = type === "sprites"? asset : current; + asset = (type === "sprites" ? asset : current); fileUrl = getFilePath(type, asset); if (Crafty.asset(fileUrl)) { if (type === "sprites") diff --git a/tests/2d.js b/tests/2d.js index 659fe4a5..48dabc20 100644 --- a/tests/2d.js +++ b/tests/2d.js @@ -255,7 +255,7 @@ // This test assumes that the "circles" are really octagons, as per Crafty.circle. - test("SAT", function() { + test("SAT overlap with circles", function() { var e = Crafty.e("2D, Collision"); var c1 = new Crafty.circle(100, 100, 10); var c2 = new Crafty.circle(100, 105, 10); @@ -263,6 +263,15 @@ }); + // Testcase from issue #828 by VHonzik + test("SAT overlap with rectangles", function() { + var e = Crafty.e("2D, Collision"); + var c1 = new Crafty.polygon([[0,1], [50, 1], [50, 51], [0, 51]]); + var c2 = new Crafty.polygon([[-10, -10], [-10, 10], [10, 10], [10, -10]]); + strictEqual(e._SAT(c1, c2) !== false, true, "Polygons should test as overlapping"); + + }); + test("adjustable boundary", function() { e = Crafty.e("2D").attr({ diff --git a/tests/core.js b/tests/core.js index 479f3cb1..bb83c215 100644 --- a/tests/core.js +++ b/tests/core.js @@ -123,6 +123,49 @@ }); + test("bind groups of entities", function() { + var e1 = Crafty.e("test"), e2 = Crafty.e("test"); + var test_callback = function(){ + this.test_flag = true; + }; + Crafty("test").bind("TestEvent", test_callback); + e1.trigger("TestEvent"); + strictEqual(e1.test_flag, true, "Entity event triggered on first entity"); + notStrictEqual(e2.test_flag, false, "Not triggered on second "); + + e1.test_flag = false; + + Crafty.trigger("TestEvent"); + strictEqual(e1.test_flag, true, "Global event triggered on first entity"); + strictEqual(e2.test_flag, true, "Global event triggered on second entity"); + + }); + + test("trigger groups of entities", function(){ + var e1 = Crafty.e("test"), e2 = Crafty.e("test"); + var test_callback = function(){ + this.test_flag = true; + }; + e1.bind("TestEvent", test_callback); + e2.bind("TestEvent", test_callback); + Crafty("test").trigger("TestEvent"); + strictEqual(e1.test_flag, true, "Triggered on first entity"); + strictEqual(e2.test_flag, true, "Triggered on second entity"); + }); + + test("bind to an event in response to that same event", function() { + var first = Crafty.e("test"), + triggered = 0; + function increment(){ triggered++; } + first.bind("myevent", function() { + increment(); + first.bind("myevent", increment); + }); + first.trigger("myevent"); + strictEqual(triggered, 1, "event added in response to an event should not be triggered by that same event"); + + }); + test("unbind", function() { var first = Crafty.e("test"); first.bind("myevent", function() { @@ -290,6 +333,18 @@ Crafty.unbind(frameFunction); }); + test("Crafty.stop(true)", function(){ + var test = Crafty.e('2D'); + Crafty.stop(true); + Crafty.init(); + + var newTest = Crafty.e('2D'); + var components = Crafty.components(); + + ok(Object.keys(components).length, + 'There should still be components after doing a hard reset'); + }); + module("Scenes"); test("Scene calling", function() {