Skip to content
This repository
  • 9 commits
  • 26 files changed
  • 0 comments
  • 1 contributor
40 LICENSE
... ... @@ -1,20 +1,20 @@
1   -Copyright (c) 2010-2011 Ryan Williams
2   -
3   -Permission is hereby granted, free of charge, to any person obtaining
4   -a copy of this software and associated documentation files (the
5   -"Software"), to deal in the Software without restriction, including
6   -without limitation the rights to use, copy, modify, merge, publish,
7   -distribute, sublicense, and/or sell copies of the Software, and to
8   -permit persons to whom the Software is furnished to do so, subject to
9   -the following conditions:
10   -
11   -The above copyright notice and this permission notice shall be
12   -included in all copies or substantial portions of the Software.
13   -
14   -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15   -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16   -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17   -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18   -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19   -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20   -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  1 +Copyright (c) 2010-2012 Ryan Williams
  2 +
  3 +Permission is hereby granted, free of charge, to any person obtaining
  4 +a copy of this software and associated documentation files (the
  5 +"Software"), to deal in the Software without restriction, including
  6 +without limitation the rights to use, copy, modify, merge, publish,
  7 +distribute, sublicense, and/or sell copies of the Software, and to
  8 +permit persons to whom the Software is furnished to do so, subject to
  9 +the following conditions:
  10 +
  11 +The above copyright notice and this permission notice shall be
  12 +included in all copies or substantial portions of the Software.
  13 +
  14 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17 +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  18 +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  19 +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20 +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2  lib/cocos2d/app_init.js.template
@@ -80,7 +80,7 @@
80 80 }
81 81
82 82 function addScript (document, script, callback) {
83   - s = document.createElement('script')
  83 + var s = document.createElement('script')
84 84 s.type = 'text/javascript'
85 85 s.defer = true
86 86 s.onload = callback
2  package.json
@@ -18,7 +18,7 @@
18 18 , "url": "https://github.com/ryanwilliams/cocos2d-javascript/blob/master/LICENSE"
19 19 }]
20 20
21   -, "dependencies": { "jah": "0.2.2"
  21 +, "dependencies": { "jah": "0.2.3"
22 22 }
23 23
24 24 , "preferGlobal": true
4 src/Director.js
@@ -220,7 +220,7 @@ Director.inherit(Object, /** @lends cocos.Director# */ {
220 220 this._viewScale = new geo.Size(width / viewWidth, height / viewHeight)
221 221
222 222
223   - if (FLIP_Y_AXIS) {
  223 + if (cc.FLIP_Y_AXIS) {
224 224 this.context.translate(0, height)
225 225 this.context.scale(1, -1)
226 226 }
@@ -555,7 +555,7 @@ Director.inherit(Object, /** @lends cocos.Director# */ {
555 555 }
556 556
557 557 var p = geo.ccpSub(loc, ccp(x, y))
558   - if (FLIP_Y_AXIS) {
  558 + if (cc.FLIP_Y_AXIS) {
559 559 p.y = this._viewSize.height - p.y
560 560 }
561 561
4 src/EventDispatcher.js
@@ -150,7 +150,7 @@ EventDispatcher.inherit(Object, /** @lends cocos.EventDispatcher# */ {
150 150 if (this._previousMouseMovePosition) {
151 151 evt.deltaX = evt.clientX - this._previousMouseMovePosition.x
152 152 evt.deltaY = evt.clientY - this._previousMouseMovePosition.y
153   - if (FLIP_Y_AXIS) {
  153 + if (cc.FLIP_Y_AXIS) {
154 154 evt.deltaY *= -1
155 155 }
156 156 } else {
@@ -177,7 +177,7 @@ EventDispatcher.inherit(Object, /** @lends cocos.EventDispatcher# */ {
177 177 if (this._previousMouseDragPosition) {
178 178 evt.deltaX = evt.clientX - this._previousMouseDragPosition.x
179 179 evt.deltaY = evt.clientY - this._previousMouseDragPosition.y
180   - if (FLIP_Y_AXIS) {
  180 + if (cc.FLIP_Y_AXIS) {
181 181 evt.deltaY *= -1
182 182 }
183 183 } else {
6 src/SpriteFrameCache.js
@@ -85,7 +85,7 @@ SpriteFrameCache.inherit(Object, /** @lends cocos.SpriteFrameCache# */ {
85 85 //console.log("cocos2d: WARNING: originalWidth/Height not found on the CCSpriteFrame. AnchorPoint won't work as expected. Regenerate the .plist")
86 86 }
87 87
88   - if (FLIP_Y_AXIS) {
  88 + if (cc.FLIP_Y_AXIS) {
89 89 oy *= -1
90 90 }
91 91
@@ -108,7 +108,7 @@ SpriteFrameCache.inherit(Object, /** @lends cocos.SpriteFrameCache# */ {
108 108 offset = geo.pointFromString(frameDict.offset),
109 109 sourceSize = geo.sizeFromString(frameDict.sourceSize)
110 110
111   - if (FLIP_Y_AXIS) {
  111 + if (cc.FLIP_Y_AXIS) {
112 112 offset.y *= -1
113 113 }
114 114
@@ -129,7 +129,7 @@ SpriteFrameCache.inherit(Object, /** @lends cocos.SpriteFrameCache# */ {
129 129 textureRotated = frameDict.textureRotated
130 130
131 131
132   - if (FLIP_Y_AXIS) {
  132 + if (cc.FLIP_Y_AXIS) {
133 133 spriteOffset.y *= -1
134 134 }
135 135
47 src/TMXXMLParser.js
... ... @@ -1,13 +1,14 @@
1 1 'use strict'
2 2
3   -var util = require('util'),
4   - path = require('path'),
5   - ccp = require('geometry').ccp,
6   - base64 = require('base64'),
7   - gzip = require('gzip'),
8   - TMXOrientationOrtho = require('./TMXOrientation').TMXOrientationOrtho,
9   - TMXOrientationHex = require('./TMXOrientation').TMXOrientationHex,
10   - TMXOrientationIso = require('./TMXOrientation').TMXOrientationIso
  3 +var util = require('util')
  4 + , uri = require('uri')
  5 + , path = require('path')
  6 + , ccp = require('geometry').ccp
  7 + , base64 = require('base64')
  8 + , gzip = require('gzip')
  9 + , TMXOrientationOrtho = require('./TMXOrientation').TMXOrientationOrtho
  10 + , TMXOrientationHex = require('./TMXOrientation').TMXOrientationHex
  11 + , TMXOrientationIso = require('./TMXOrientation').TMXOrientationIso
11 12
12 13 /**
13 14 * @class
@@ -137,15 +138,15 @@ TMXObjectGroup.inherit(Object, /** @lends cocos.TMXObjectGroup# */ {
137 138 *
138 139 * @param {String} tmxFile The file path of the TMX file to load
139 140 */
140   -function TMXMapInfo (tmxFile) {
  141 +function TMXMapInfo (opts) {
141 142 this.tilesets = []
142 143 this.layers = []
143 144 this.objectGroups = []
144 145 this.properties = {}
145 146 this.tileProperties = {}
146   - this.filename = tmxFile
  147 + this.filename = opts.filename || opts.file || ''
147 148
148   - this.parseXMLFile(tmxFile)
  149 + this.parseXMLFile(opts)
149 150 }
150 151
151 152 TMXMapInfo.inherit(Object, /** @lends cocos.TMXMapInfo# */ {
@@ -159,9 +160,14 @@ TMXMapInfo.inherit(Object, /** @lends cocos.TMXMapInfo# */ {
159 160 properties: null,
160 161 tileProperties: null,
161 162
162   - parseXMLFile: function (xmlFile) {
163   - var parser = new DOMParser(),
164   - doc = parser.parseFromString(resource(xmlFile), 'text/xml')
  163 + parseXMLFile: function (opts) {
  164 + var parser = new DOMParser()
  165 + , doc
  166 +
  167 + if (opts.file)
  168 + doc = parser.parseFromString(resource(opts.file), 'text/xml')
  169 + else if (opts.xml)
  170 + doc = parser.parseFromString(opts.xml, 'text/xml')
165 171
166 172 // PARSE <map>
167 173 var map = doc.documentElement
@@ -198,7 +204,8 @@ TMXMapInfo.inherit(Object, /** @lends cocos.TMXMapInfo# */ {
198 204 // happen AFTER 'firstGID' is obtained because firstGID is stored
199 205 // in the main .tmx file, not the .tsx
200 206 if (externalTilesetName) {
201   - var externalTilesetPath = path.join(path.dirname(xmlFile), externalTilesetName)
  207 + // FIXME needs to support opts.url too
  208 + var externalTilesetPath = path.join(path.dirname(opts.file), externalTilesetName)
202 209 t = parser.parseFromString(resource(externalTilesetPath), 'text/xml').documentElement
203 210 }
204 211
@@ -220,7 +227,13 @@ TMXMapInfo.inherit(Object, /** @lends cocos.TMXMapInfo# */ {
220 227 if (externalTilesetName) {
221 228 tileset.sourceImage = path.join(path.dirname(externalTilesetPath), image.getAttribute('source'))
222 229 } else {
223   - tileset.sourceImage = path.join(path.dirname(this.filename), image.getAttribute('source'))
  230 + // Check of URL or file path
  231 + if (uri.isURL(this.filename)) {
  232 + var base = path.dirname(this.filename)
  233 + tileset.sourceImage = path.dirname(this.filename) + '/' + image.getAttribute('source')
  234 + } else {
  235 + tileset.sourceImage = path.join(path.dirname(this.filename), image.getAttribute('source'))
  236 + }
224 237 }
225 238
226 239 this.tilesets.push(tileset)
@@ -338,7 +351,7 @@ TMXMapInfo.inherit(Object, /** @lends cocos.TMXMapInfo# */ {
338 351 height : parseInt(object.getAttribute('height'), 10)
339 352 }
340 353
341   - if (FLIP_Y_AXIS) {
  354 + if (cc.FLIP_Y_AXIS) {
342 355 objectValue.y = (this.mapSize.height * this.tileSize.height) - objectValue.y - objectValue.height
343 356 }
344 357
18 src/Texture2D.js
@@ -13,11 +13,16 @@ var util = require('util'),
13 13 * @opt {Texture2D|HTMLImageElement} [data] Image data to read from
14 14 */
15 15 function Texture2D (opts) {
16   - var file = opts.file,
17   - data = opts.data,
18   - texture = opts.texture
  16 + var file = opts.file
  17 + , url = opts.url
  18 + , data = opts.data
  19 + , texture = opts.texture
19 20
20   - if (file) {
  21 + if (url) {
  22 + this.name = url
  23 + data = new Image()
  24 + data.src = url
  25 + } else if (file) {
21 26 this.name = file
22 27 data = resource(file)
23 28 } else if (texture) {
@@ -28,8 +33,11 @@ function Texture2D (opts) {
28 33 this.size = {width: 0, height: 0}
29 34
30 35 if (data instanceof RemoteResource) {
31   - events.addListenerOnce(data, 'load', this.dataDidLoad.bind(this))
  36 + events.addListenerOnce(data, 'load', this.dataDidLoad.bind(this, data))
32 37 this.imgElement = data.load()
  38 + } else if (url) {
  39 + this.imgElement = data
  40 + this.imgElement.onload = this.dataDidLoad.bind(this, data)
33 41 } else {
34 42 this.imgElement = data
35 43 this.dataDidLoad(data)
23 src/TextureAtlas.js
... ... @@ -1,6 +1,7 @@
1 1 'use strict'
2 2
3 3 var util = require('util'),
  4 + events = require('events'),
4 5 Texture2D = require('./Texture2D').Texture2D
5 6
6 7
@@ -17,21 +18,26 @@ var util = require('util'),
17 18 *
18 19 * @memberOf cocos
19 20 *
20   - * @opt {String} file The file path of the image to use as a texture
  21 + * @opt {String} [url] Remote URL of the image to use as a texture
  22 + * @opt {String} [file] Local file path of the image to use as a texture
21 23 * @opt {Texture2D|HTMLImageElement} [data] Image data to read from
22 24 * @opt {CanvasElement} [canvas] A canvas to use as a texture
23 25 */
24 26 function TextureAtlas (opts) {
25   - var file = opts.file,
26   - data = opts.data,
27   - texture = opts.texture,
28   - canvas = opts.canvas
  27 + var file = opts.file
  28 + , url = opts.url
  29 + , data = opts.data
  30 + , texture = opts.texture
  31 + , canvas = opts.canvas
29 32
30 33 if (canvas) {
31 34 // If we've been given a canvas element then we'll use that for our image
32 35 this.imgElement = canvas
33 36 } else {
34   - texture = new Texture2D({texture: texture, file: file, data: data})
  37 + texture = new Texture2D({ url: url, texture: texture, file: file, data: data })
  38 + events.addListenerOnce(texture, 'load', function () {
  39 + events.trigger(this, 'load')
  40 + }.bind(this))
35 41 this.texture = texture
36 42 this.imgElement = texture.imgElement
37 43 }
@@ -79,7 +85,7 @@ TextureAtlas.inherit(Object, /** @lends cocos.TextureAtlas# */ {
79 85 var scaleX = 1
80 86 var scaleY = 1
81 87
82   - if (FLIP_Y_AXIS) {
  88 + if (cc.FLIP_Y_AXIS) {
83 89 dy -= dh
84 90 dh *= -1
85 91 }
@@ -97,6 +103,7 @@ TextureAtlas.inherit(Object, /** @lends cocos.TextureAtlas# */ {
97 103
98 104 ctx.scale(scaleX, scaleY)
99 105
  106 +
100 107 var img = this.imgElement
101 108 ctx.drawImage(img,
102 109 sx, sy, // Draw slice from x,y
@@ -105,7 +112,7 @@ TextureAtlas.inherit(Object, /** @lends cocos.TextureAtlas# */ {
105 112 dw, dh // Draw size
106 113 )
107 114
108   - if (FLIP_Y_AXIS) {
  115 + if (cc.FLIP_Y_AXIS) {
109 116 ctx.scale(1, -1)
110 117 } else {
111 118 ctx.scale(1, 1)
4 src/globals.js
... ... @@ -1,4 +0,0 @@
1   -module.exports = { FLIP_Y_AXIS: false
2   - , ENABLE_WEB_GL: false
3   - , SHOW_REDRAW_REGIONS: false
4   - }
2  src/index.js
@@ -3,7 +3,7 @@
3 3 var util = require('util'),
4 4 path = require('path')
5 5
6   -var modules = 'TextureAtlas Texture2D SpriteFrame SpriteFrameCache Director Animation AnimationCache Scheduler ActionManager TMXXMLParser'.split(' ')
  6 +var modules = 'nodes actions TextureAtlas Texture2D SpriteFrame SpriteFrameCache Director Animation AnimationCache Scheduler ActionManager TMXXMLParser'.split(' ')
7 7
8 8 /**
9 9 * @namespace All cocos2d objects live in this namespace
19 src/init.js
... ... @@ -1,21 +1,28 @@
1 1 var path = require('path')
2 2
3 3 exports.main = function () {
  4 + if (typeof cc == 'undefined') {
  5 + window.cc = {}
  6 + }
  7 +
4 8 require.paths.push(path.join(__dirname, 'libs'))
5 9
6 10 require('./remote_resources_patch')
7 11
8 12 require('./js_extensions')
9 13
10   - // Link to the parent window's XHR object, IE9 will fail with cross-origin
11   - // errors if we don't.
12   - window.XMLHttpRequest = parent.XMLHttpRequest
  14 + if (!cc.STANDALONE) {
  15 + // Link to the parent window's XHR object, IE9 will fail with cross-origin
  16 + // errors if we don't.
  17 + window.XMLHttpRequest = parent.XMLHttpRequest
  18 + }
  19 +
13 20
14 21 // Load default cocos2d config
15 22 var config = require('./config')
16 23 for (var k in config) {
17 24 if (config.hasOwnProperty(k)) {
18   - window[k] = config[k]
  25 + cc[k] = config[k]
19 26 }
20 27 }
21 28
@@ -24,12 +31,12 @@ exports.main = function () {
24 31 config = require('/config')
25 32 for (var k in config) {
26 33 if (config.hasOwnProperty(k)) {
27   - window[k] = config[k]
  34 + cc[k] = config[k]
28 35 }
29 36 }
30 37 }
31 38
32   - if (ENABLE_DEPRECATED_METHODS) {
  39 + if (cc.ENABLE_DEPRECATED_METHODS) {
33 40 require('./legacy')
34 41 }
35 42 };
1,415 src/libs/qunit.js
... ... @@ -1,1415 +0,0 @@
1   -/*
2   - * QUnit - A JavaScript Unit Testing Framework
3   - *
4   - * http://docs.jquery.com/QUnit
5   - *
6   - * Copyright (c) 2011 John Resig, Jörn Zaefferer
7   - * Dual licensed under the MIT (MIT-LICENSE.txt)
8   - * or GPL (GPL-LICENSE.txt) licenses.
9   - */
10   -
11   -(function(window) {
12   -
13   -var defined = {
14   - setTimeout: typeof window.setTimeout !== "undefined",
15   - sessionStorage: (function() {
16   - try {
17   - return !!sessionStorage.getItem;
18   - } catch(e){
19   - return false;
20   - }
21   - })()
22   -}
23   -
24   -var testId = 0;
25   -
26   -var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {
27   - this.name = name;
28   - this.testName = testName;
29   - this.expected = expected;
30   - this.testEnvironmentArg = testEnvironmentArg;
31   - this.async = async;
32   - this.callback = callback;
33   - this.assertions = [];
34   -};
35   -Test.prototype = {
36   - init: function() {
37   - var tests = id("qunit-tests");
38   - if (tests) {
39   - var b = document.createElement("strong");
40   - b.innerHTML = "Running " + this.name;
41   - var li = document.createElement("li");
42   - li.appendChild( b );
43   - li.id = this.id = "test-output" + testId++;
44   - tests.appendChild( li );
45   - }
46   - },
47   - setup: function() {
48   - if (this.module != config.previousModule) {
49   - if ( config.previousModule ) {
50   - QUnit.moduleDone( {
51   - name: config.previousModule,
52   - failed: config.moduleStats.bad,
53   - passed: config.moduleStats.all - config.moduleStats.bad,
54   - total: config.moduleStats.all
55   - } );
56   - }
57   - config.previousModule = this.module;
58   - config.moduleStats = { all: 0, bad: 0 };
59   - QUnit.moduleStart( {
60   - name: this.module
61   - } );
62   - }
63   -
64   - config.current = this;
65   - this.testEnvironment = extend({
66   - setup: function() {},
67   - teardown: function() {}
68   - }, this.moduleTestEnvironment);
69   - if (this.testEnvironmentArg) {
70   - extend(this.testEnvironment, this.testEnvironmentArg);
71   - }
72   -
73   - QUnit.testStart( {
74   - name: this.testName
75   - } );
76   -
77   - // allow utility functions to access the current test environment
78   - // TODO why??
79   - QUnit.current_testEnvironment = this.testEnvironment;
80   -
81   - try {
82   - if ( !config.pollution ) {
83   - saveGlobal();
84   - }
85   -
86   - this.testEnvironment.setup.call(this.testEnvironment);
87   - } catch(e) {
88   - QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message );
89   - }
90   - },
91   - run: function() {
92   - if ( this.async ) {
93   - QUnit.stop();
94   - }
95   -
96   - if ( config.notrycatch ) {
97   - this.callback.call(this.testEnvironment);
98   - return;
99   - }
100   - try {
101   - this.callback.call(this.testEnvironment);
102   - } catch(e) {
103   - fail("Test " + this.testName + " died, exception and test follows", e, this.callback);
104   - QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) );
105   - // else next test will carry the responsibility
106   - saveGlobal();
107   -
108   - // Restart the tests if they're blocking
109   - if ( config.blocking ) {
110   - start();
111   - }
112   - }
113   - },
114   - teardown: function() {
115   - try {
116   - checkPollution();
117   - this.testEnvironment.teardown.call(this.testEnvironment);
118   - } catch(e) {
119   - QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message );
120   - }
121   - },
122   - finish: function() {
123   - if ( this.expected && this.expected != this.assertions.length ) {
124   - QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );
125   - }
126   -
127   - var good = 0, bad = 0,
128   - tests = id("qunit-tests");
129   -
130   - config.stats.all += this.assertions.length;
131   - config.moduleStats.all += this.assertions.length;
132   -
133   - if ( tests ) {
134   - var ol = document.createElement("ol");
135   -
136   - for ( var i = 0; i < this.assertions.length; i++ ) {
137   - var assertion = this.assertions[i];
138   -
139   - var li = document.createElement("li");
140   - li.className = assertion.result ? "pass" : "fail";
141   - li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed");
142   - ol.appendChild( li );
143   -
144   - if ( assertion.result ) {
145   - good++;
146   - } else {
147   - bad++;
148   - config.stats.bad++;
149   - config.moduleStats.bad++;
150   - }
151   - }
152   -
153   - // store result when possible
154   - defined.sessionStorage && sessionStorage.setItem("qunit-" + this.testName, bad);
155   -
156   - if (bad == 0) {
157   - ol.style.display = "none";
158   - }
159   -
160   - var b = document.createElement("strong");
161   - b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
162   -
163   - addEvent(b, "click", function() {
164   - var next = b.nextSibling, display = next.style.display;
165   - next.style.display = display === "none" ? "block" : "none";
166   - });
167   -
168   - addEvent(b, "dblclick", function(e) {
169   - var target = e && e.target ? e.target : window.event.srcElement;
170   - if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
171   - target = target.parentNode;
172   - }
173   - if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
174   - window.location.search = "?" + encodeURIComponent(getText([target]).replace(/\(.+\)$/, "").replace(/(^\s*|\s*$)/g, ""));
175   - }
176   - });
177   -
178   - var li = id(this.id);
179   - li.className = bad ? "fail" : "pass";
180   - li.style.display = resultDisplayStyle(!bad);
181   - li.removeChild( li.firstChild );
182   - li.appendChild( b );
183   - li.appendChild( ol );
184   -
185   - } else {
186   - for ( var i = 0; i < this.assertions.length; i++ ) {
187   - if ( !this.assertions[i].result ) {
188   - bad++;
189   - config.stats.bad++;
190   - config.moduleStats.bad++;
191   - }
192   - }
193   - }
194   -
195   - try {
196   - QUnit.reset();
197   - } catch(e) {
198   - fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset);
199   - }
200   -
201   - QUnit.testDone( {
202   - name: this.testName,
203   - failed: bad,
204   - passed: this.assertions.length - bad,
205   - total: this.assertions.length
206   - } );
207   - },
208   -
209   - queue: function() {
210   - var test = this;
211   - synchronize(function() {
212   - test.init();
213   - });
214   - function run() {
215   - // each of these can by async
216   - synchronize(function() {
217   - test.setup();
218   - });
219   - synchronize(function() {
220   - test.run();
221   - });
222   - synchronize(function() {
223   - test.teardown();
224   - });
225   - synchronize(function() {
226   - test.finish();
227   - });
228   - }
229   - // defer when previous test run passed, if storage is available
230   - var bad = defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.testName);
231   - if (bad) {
232   - run();
233   - } else {
234   - synchronize(run);
235   - };
236   - }
237   -
238   -}
239   -
240   -var QUnit = {
241   -
242   - // call on start of module test to prepend name to all tests
243   - module: function(name, testEnvironment) {
244   - config.currentModule = name;
245   - config.currentModuleTestEnviroment = testEnvironment;
246   - },
247   -
248   - asyncTest: function(testName, expected, callback) {
249   - if ( arguments.length === 2 ) {
250   - callback = expected;
251   - expected = 0;
252   - }
253   -
254   - QUnit.test(testName, expected, callback, true);
255   - },
256   -
257   - test: function(testName, expected, callback, async) {
258   - var name = '<span class="test-name">' + testName + '</span>', testEnvironmentArg;
259   -
260   - if ( arguments.length === 2 ) {
261   - callback = expected;
262   - expected = null;
263   - }
264   - // is 2nd argument a testEnvironment?
265   - if ( expected && typeof expected === 'object') {
266   - testEnvironmentArg = expected;
267   - expected = null;
268   - }
269   -
270   - if ( config.currentModule ) {
271   - name = '<span class="module-name">' + config.currentModule + "</span>: " + name;
272   - }
273   -
274   - if ( !validTest(config.currentModule + ": " + testName) ) {
275   - return;
276   - }
277   -
278   - var test = new Test(name, testName, expected, testEnvironmentArg, async, callback);
279   - test.module = config.currentModule;
280   - test.moduleTestEnvironment = config.currentModuleTestEnviroment;
281   - test.queue();
282   - },
283   -
284   - /**
285   - * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
286   - */
287   - expect: function(asserts) {
288   - config.current.expected = asserts;
289   - },
290   -
291   - /**
292   - * Asserts true.
293   - * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
294   - */
295   - ok: function(a, msg) {
296   - a = !!a;
297   - var details = {
298   - result: a,
299   - message: msg
300   - };
301   - msg = escapeHtml(msg);
302   - QUnit.log(details);
303   - config.current.assertions.push({
304   - result: a,
305   - message: msg
306   - });
307   - },
308   -
309   - /**
310   - * Checks that the first two arguments are equal, with an optional message.
311   - * Prints out both actual and expected values.
312   - *
313   - * Prefered to ok( actual == expected, message )
314   - *
315   - * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
316   - *
317   - * @param Object actual
318   - * @param Object expected
319   - * @param String message (optional)
320   - */
321   - equal: function(actual, expected, message) {
322   - QUnit.push(expected == actual, actual, expected, message);
323   - },
324   -
325   - notEqual: function(actual, expected, message) {
326   - QUnit.push(expected != actual, actual, expected, message);
327   - },
328   -
329   - deepEqual: function(actual, expected, message) {
330   - QUnit.push(QUnit.equiv(actual, expected), actual, expected, message);
331   - },
332   -
333   - notDeepEqual: function(actual, expected, message) {
334   - QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message);
335   - },
336   -
337   - strictEqual: function(actual, expected, message) {
338   - QUnit.push(expected === actual, actual, expected, message);
339   - },
340   -
341   - notStrictEqual: function(actual, expected, message) {
342   - QUnit.push(expected !== actual, actual, expected, message);
343   - },
344   -
345   - raises: function(block, expected, message) {
346   - var actual, ok = false;
347   -
348   - if (typeof expected === 'string') {
349   - message = expected;
350   - expected = null;
351   - }
352   -
353   - try {
354   - block();
355   - } catch (e) {
356   - actual = e;
357   - }
358   -
359   - if (actual) {
360   - // we don't want to validate thrown error
361   - if (!expected) {
362   - ok = true;
363   - // expected is a regexp
364   - } else if (QUnit.objectType(expected) === "regexp") {
365   - ok = expected.test(actual);
366   - // expected is a constructor
367   - } else if (actual instanceof expected) {
368   - ok = true;
369   - // expected is a validation function which returns true is validation passed
370   - } else if (expected.call({}, actual) === true) {
371   - ok = true;
372   - }
373   - }
374   -
375   - QUnit.ok(ok, message);
376   - },
377   -
378   - start: function() {
379   - config.semaphore--;
380   - if (config.semaphore > 0) {
381   - // don't start until equal number of stop-calls
382   - return;
383   - }
384   - if (config.semaphore < 0) {
385   - // ignore if start is called more often then stop
386   - config.semaphore = 0;
387   - }
388   - // A slight delay, to avoid any current callbacks
389   - if ( defined.setTimeout ) {
390   - window.setTimeout(function() {
391   - if ( config.timeout ) {
392   - clearTimeout(config.timeout);
393   - }
394   -
395   - config.blocking = false;
396   - process();
397   - }, 13);
398   - } else {
399   - config.blocking = false;
400   - process();
401   - }
402   - },
403   -
404   - stop: function(timeout) {
405   - config.semaphore++;
406   - config.blocking = true;
407   -
408   - if ( timeout && defined.setTimeout ) {
409   - clearTimeout(config.timeout);
410   - config.timeout = window.setTimeout(function() {
411   - QUnit.ok( false, "Test timed out" );
412   - QUnit.start();
413   - }, timeout);
414   - }
415   - }
416   -
417   -};
418   -
419   -// Backwards compatibility, deprecated
420   -QUnit.equals = QUnit.equal;
421   -QUnit.same = QUnit.deepEqual;
422   -
423   -// Maintain internal state
424   -var config = {
425   - // The queue of tests to run
426   - queue: [],
427   -
428   - // block until document ready
429   - blocking: true
430   -};
431   -
432   -// Load paramaters
433   -(function() {
434   - var location = window.location || { search: "", protocol: "file:" },
435   - GETParams = location.search.slice(1).split('&');
436   -
437   - for ( var i = 0; i < GETParams.length; i++ ) {
438   - GETParams[i] = decodeURIComponent( GETParams[i] );
439   - if ( GETParams[i] === "noglobals" ) {
440   - GETParams.splice( i, 1 );
441   - i--;
442   - config.noglobals = true;
443   - } else if ( GETParams[i] === "notrycatch" ) {
444   - GETParams.splice( i, 1 );
445   - i--;
446   - config.notrycatch = true;
447   - } else if ( GETParams[i].search('=') > -1 ) {
448   - GETParams.splice( i, 1 );
449   - i--;
450   - }
451   - }
452   -
453   - // restrict modules/tests by get parameters
454   - config.filters = GETParams;
455   -
456   - // Figure out if we're running the tests from a server or not
457   - QUnit.isLocal = !!(location.protocol === 'file:');
458   -})();
459   -
460   -// Expose the API as global variables, unless an 'exports'
461   -// object exists, in that case we assume we're in CommonJS
462   -if ( typeof exports === "undefined" || typeof require === "undefined" ) {
463   - extend(window, QUnit);
464   - window.QUnit = QUnit;
465   -} else {
466   - extend(exports, QUnit);
467   - exports.QUnit = QUnit;
468   -}
469   -
470   -// define these after exposing globals to keep them in these QUnit namespace only
471   -extend(QUnit, {
472   - config: config,
473   -
474   - // Initialize the configuration options
475   - init: function() {
476   - extend(config, {
477   - stats: { all: 0, bad: 0 },
478   - moduleStats: { all: 0, bad: 0 },
479   - started: +new Date,
480   - updateRate: 1000,
481   - blocking: false,
482   - autostart: true,
483   - autorun: false,
484   - filters: [],
485   - queue: [],
486   - semaphore: 0
487   - });
488   -
489   - var tests = id("qunit-tests"),
490   - banner = id("qunit-banner"),
491   - result = id("qunit-testresult");
492   -
493   - if ( tests ) {
494   - tests.innerHTML = "";
495   - }
496   -
497   - if ( banner ) {
498   - banner.className = "";
499   - }
500   -
501   - if ( result ) {
502   - result.parentNode.removeChild( result );
503   - }
504   - },
505   -
506   - /**
507   - * Resets the test setup. Useful for tests that modify the DOM.
508   - *
509   - * If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
510   - */
511   - reset: function() {
512   - if ( window.jQuery ) {
513   - jQuery( "#main, #qunit-fixture" ).html( config.fixture );
514   - } else {
515   - var main = id( 'main' ) || id( 'qunit-fixture' );
516   - if ( main ) {
517   - main.innerHTML = config.fixture;
518   - }
519   - }
520   - },
521   -
522   - /**
523   - * Trigger an event on an element.
524   - *
525   - * @example triggerEvent( document.body, "click" );
526   - *
527   - * @param DOMElement elem
528   - * @param String type
529   - */
530   - triggerEvent: function( elem, type, event ) {
531   - if ( document.createEvent ) {
532   - event = document.createEvent("MouseEvents");
533   - event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
534   - 0, 0, 0, 0, 0, false, false, false, false, 0, null);
535   - elem.dispatchEvent( event );
536   -
537   - } else if ( elem.fireEvent ) {
538   - elem.fireEvent("on"+type);
539   - }
540   - },
541   -
542   - // Safe object type checking
543   - is: function( type, obj ) {
544   - return QUnit.objectType( obj ) == type;
545   - },
546   -
547   - objectType: function( obj ) {
548   - if (typeof obj === "undefined") {
549   - return "undefined";
550   -
551   - // consider: typeof null === object
552   - }
553   - if (obj === null) {
554   - return "null";
555   - }
556   -
557   - var type = Object.prototype.toString.call( obj )
558   - .match(/^\[object\s(.*)\]$/)[1] || '';
559   -
560   - switch (type) {
561   - case 'Number':
562   - if (isNaN(obj)) {
563   - return "nan";
564   - } else {
565   - return "number";
566   - }
567   - case 'String':
568   - case 'Boolean':
569   - case 'Array':
570   - case 'Date':
571   - case 'RegExp':
572   - case 'Function':
573   - return type.toLowerCase();
574   - }
575   - if (typeof obj === "object") {
576   - return "object";
577   - }
578   - return undefined;
579   - },
580   -
581   - push: function(result, actual, expected, message) {
582   - var details = {
583   - result: result,
584   - message: message,
585   - actual: actual,
586   - expected: expected
587   - };
588   -
589   - message = escapeHtml(message) || (result ? "okay" : "failed");
590   - message = '<span class="test-message">' + message + "</span>";
591   - expected = escapeHtml(QUnit.jsDump.parse(expected));
592   - actual = escapeHtml(QUnit.jsDump.parse(actual));
593   - var output = message + '<table><tr class="test-expected"><th>Expected: </th><td><pre>' + expected + '</pre></td></tr>';
594   - if (actual != expected) {
595   - output += '<tr class="test-actual"><th>Result: </th><td><pre>' + actual + '</pre></td></tr>';
596   - output += '<tr class="test-diff"><th>Diff: </th><td><pre>' + QUnit.diff(expected, actual) +'</pre></td></tr>';
597   - }
598   - if (!result) {
599   - var source = sourceFromStacktrace();
600   - if (source) {
601   - details.source = source;
602   - output += '<tr class="test-source"><th>Source: </th><td><pre>' + source +'</pre></td></tr>';
603   - }
604   - }
605   - output += "</table>";
606   -
607   - QUnit.log(details);
608   -
609   - config.current.assertions.push({
610   - result: !!result,
611   - message: output
612   - });
613   - },
614   -
615   - // Logging callbacks; all receive a single argument with the listed properties
616   - // run test/logs.html for any related changes
617   - begin: function() {},
618   - // done: { failed, passed, total, runtime }
619   - done: function() {},
620   - // log: { result, actual, expected, message }
621   - log: function() {},
622   - // testStart: { name }
623   - testStart: function() {},
624   - // testDone: { name, failed, passed, total }
625   - testDone: function() {},
626   - // moduleStart: { name }
627   - moduleStart: function() {},
628   - // moduleDone: { name, failed, passed, total }
629   - moduleDone: function() {}
630   -});
631   -
632   -if ( typeof document === "undefined" || document.readyState === "complete" ) {
633   - config.autorun = true;
634   -}
635   -
636   -addEvent(window, "load", function() {
637   - QUnit.begin({});
638   -
639   - // Initialize the config, saving the execution queue
640   - var oldconfig = extend({}, config);
641   - QUnit.init();
642   - extend(config, oldconfig);
643   -
644   - config.blocking = false;
645   -
646   - var userAgent = id("qunit-userAgent");
647   - if ( userAgent ) {
648   - userAgent.innerHTML = navigator.userAgent;
649   - }
650   - var banner = id("qunit-header");
651   - if ( banner ) {
652   - var paramsIndex = location.href.lastIndexOf(location.search);
653   - if ( paramsIndex > -1 ) {
654   - var mainPageLocation = location.href.slice(0, paramsIndex);
655   - if ( mainPageLocation == location.href ) {
656   - banner.innerHTML = '<a href=""> ' + banner.innerHTML + '</a> ';
657   - } else {
658   - var testName = decodeURIComponent(location.search.slice(1));
659   - banner.innerHTML = '<a href="' + mainPageLocation + '">' + banner.innerHTML + '</a> &#8250; <a href="">' + testName + '</a>';
660   - }
661   - }
662   - }
663   -
664   - var toolbar = id("qunit-testrunner-toolbar");
665   - if ( toolbar ) {
666   - var filter = document.createElement("input");
667   - filter.type = "checkbox";
668   - filter.id = "qunit-filter-pass";
669   - addEvent( filter, "click", function() {
670   - var li = document.getElementsByTagName("li");
671   - for ( var i = 0; i < li.length; i++ ) {
672   - if ( li[i].className.indexOf("pass") > -1 ) {
673   - li[i].style.display = filter.checked ? "none" : "";
674   - }
675   - }
676   - if ( defined.sessionStorage ) {
677   - sessionStorage.setItem("qunit-filter-passed-tests", filter.checked ? "true" : "");
678   - }
679   - });
680   - if ( defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) {
681   - filter.checked = true;
682   - }
683   - toolbar.appendChild( filter );
684   -
685   - var label = document.createElement("label");
686   - label.setAttribute("for", "qunit-filter-pass");
687   - label.innerHTML = "Hide passed tests";
688   - toolbar.appendChild( label );
689   - }
690   -
691   - var main = id('main') || id('qunit-fixture');
692   - if ( main ) {
693   - config.fixture = main.innerHTML;
694   - }
695   -
696   - if (config.autostart) {
697   - QUnit.start();
698   - }
699   -});
700   -
701   -function done() {
702   - config.autorun = true;
703   -
704   - // Log the last module results
705   - if ( config.currentModule ) {
706   - QUnit.moduleDone( {
707   - name: config.currentModule,
708   - failed: config.moduleStats.bad,
709   - passed: config.moduleStats.all - config.moduleStats.bad,
710   - total: config.moduleStats.all
711   - } );
712   - }
713   -
714   - var banner = id("qunit-banner"),
715   - tests = id("qunit-tests"),
716   - runtime = +new Date - config.started,
717   - passed = config.stats.all - config.stats.bad,
718   - html = [
719   - 'Tests completed in ',
720   - runtime,
721   - ' milliseconds.<br/>',
722   - '<span class="passed">',
723   - passed,
724   - '</span> tests of <span class="total">',
725   - config.stats.all,
726   - '</span> passed, <span class="failed">',
727   - config.stats.bad,
728   - '</span> failed.'
729   - ].join('');
730   -
731   - if ( banner ) {
732   - banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
733   - }
734   -
735   - if ( tests ) {
736   - var result = id("qunit-testresult");
737   -
738   - if ( !result ) {
739   - result = document.createElement("p");
740   - result.id = "qunit-testresult";
741   - result.className = "result";
742   - tests.parentNode.insertBefore( result, tests.nextSibling );
743   - }
744   -
745   - result.innerHTML = html;
746   - }
747   -
748   - QUnit.done( {
749   - failed: config.stats.bad,
750   - passed: passed,
751   - total: config.stats.all,
752   - runtime: runtime
753   - } );
754   -}
755   -
756   -function validTest( name ) {
757   - var i = config.filters.length,
758   - run = false;
759   -
760   - if ( !i ) {
761   - return true;
762   - }
763   -
764   - while ( i-- ) {
765   - var filter = config.filters[i],
766   - not = filter.charAt(0) == '!';
767   -
768   - if ( not ) {
769   - filter = filter.slice(1);
770   - }
771   -
772   - if ( name.indexOf(filter) !== -1 ) {
773   - return !not;
774   - }
775   -
776   - if ( not ) {
777   - run = true;
778   - }
779   - }
780   -
781   - return run;
782   -}
783   -
784   -// so far supports only Firefox, Chrome and Opera (buggy)
785   -// could be extended in the future to use something like https://github.com/csnover/TraceKit
786   -function sourceFromStacktrace() {
787   - try {
788   - throw new Error();
789   - } catch ( e ) {
790   - if (e.stacktrace) {
791   - // Opera
792   - return e.stacktrace.split("\n")[6];
793   - } else if (e.stack) {
794   - // Firefox, Chrome
795   - return e.stack.split("\n")[4];
796   - }
797   - }
798   -}
799   -
800   -function resultDisplayStyle(passed) {
801   - return passed && id("qunit-filter-pass") && id("qunit-filter-pass").checked ? 'none' : '';
802   -}
803   -
804   -function escapeHtml(s) {
805   - if (!s) {
806   - return "";
807   - }
808   - s = s + "";
809   - return s.replace(/[\&"<>\\]/g, function(s) {
810   - switch(s) {
811   - case "&": return "&amp;";
812   - case "\\": return "\\\\";
813   - case '"': return '\"';
814   - case "<": return "&lt;";
815   - case ">": return "&gt;";
816   - default: return s;
817   - }
818   - });
819   -}
820   -
821   -function synchronize( callback ) {
822   - config.queue.push( callback );
823   -
824   - if ( config.autorun && !config.blocking ) {
825   - process();
826   - }
827   -}
828   -
829   -function process() {
830   - var start = (new Date()).getTime();
831   -
832   - while ( config.queue.length && !config.blocking ) {
833   - if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {
834   - config.queue.shift()();
835   - } else {
836   - window.setTimeout( process, 13 );
837   - break;
838   - }
839   - }
840   - if (!config.blocking && !config.queue.length) {
841   - done();
842   - }
843   -}
844   -
845   -function saveGlobal() {
846   - config.pollution = [];
847   -
848   - if ( config.noglobals ) {
849   - for ( var key in window ) {
850   - config.pollution.push( key );
851   - }
852   - }
853   -}
854   -
855   -function checkPollution( name ) {
856   - var old = config.pollution;
857   - saveGlobal();
858   -
859   - var newGlobals = diff( old, config.pollution );
860   - if ( newGlobals.length > 0 ) {
861   - ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
862   - config.current.expected++;
863   - }
864   -
865   - var deletedGlobals = diff( config.pollution, old );
866   - if ( deletedGlobals.length > 0 ) {
867   - ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
868   - config.current.expected++;
869   - }
870   -}
871   -
872   -// returns a new Array with the elements that are in a but not in b
873   -function diff( a, b ) {
874   - var result = a.slice();
875   - for ( var i = 0; i < result.length; i++ ) {
876   - for ( var j = 0; j < b.length; j++ ) {
877   - if ( result[i] === b[j] ) {
878   - result.splice(i, 1);
879   - i--;
880   - break;
881   - }
882   - }
883   - }
884   - return result;
885   -}
886   -
887   -function fail(message, exception, callback) {
888   - if ( typeof console !== "undefined" && console.error && console.warn ) {
889   - console.error(message);
890   - console.error(exception);
891   - console.warn(callback.toString());
892   -
893   - } else if ( window.opera && opera.postError ) {
894   - opera.postError(message, exception, callback.toString);
895   - }
896   -}
897   -
898   -function extend(a, b) {
899   - for ( var prop in b ) {
900   - a[prop] = b[prop];
901   - }
902   -
903   - return a;
904   -}
905   -
906   -function addEvent(elem, type, fn) {
907   - if ( elem.addEventListener ) {
908   - elem.addEventListener( type, fn, false );
909   - } else if ( elem.attachEvent ) {
910   - elem.attachEvent( "on" + type, fn );
911   - } else {
912   - fn();
913   - }
914   -}
915   -
916   -function id(name) {
917   - return !!(typeof document !== "undefined" && document && document.getElementById) &&
918   - document.getElementById( name );
919   -}
920   -
921   -// Test for equality any JavaScript type.
922   -// Discussions and reference: http://philrathe.com/articles/equiv
923   -// Test suites: http://philrathe.com/tests/equiv
924   -// Author: Philippe Rathé <prathe@gmail.com>
925   -QUnit.equiv = function () {
926   -
927   - var innerEquiv; // the real equiv function
928   - var callers = []; // stack to decide between skip/abort functions
929   - var parents = []; // stack to avoiding loops from circular referencing
930   -
931   - // Call the o related callback with the given arguments.
932   - function bindCallbacks(o, callbacks, args) {
933   - var prop = QUnit.objectType(o);
934   - if (prop) {
935   - if (QUnit.objectType(callbacks[prop]) === "function") {
936   - return callbacks[prop].apply(callbacks, args);
937   - } else {
938   - return callbacks[prop]; // or undefined
939   - }
940   - }
941   - }
942   -
943   - var callbacks = function () {
944   -
945   - // for string, boolean, number and null
946   - function useStrictEquality(b, a) {
947   - if (b instanceof a.constructor || a instanceof b.constructor) {
948   - // to catch short annotaion VS 'new' annotation of a declaration
949   - // e.g. var i = 1;
950   - // var j = new Number(1);
951   - return a == b;
952   - } else {
953   - return a === b;
954   - }
955   - }
956   -
957   - return {
958   - "string": useStrictEquality,
959   - "boolean": useStrictEquality,
960   - "number": useStrictEquality,
961   - "null": useStrictEquality,
962   - "undefined": useStrictEquality,
963   -
964   - "nan": function (b) {
965   - return isNaN(b);
966   - },
967   -
968   - "date": function (b, a) {
969   - return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf();
970   - },
971   -
972   - "regexp": function (b, a) {
973   - return QUnit.objectType(b) === "regexp" &&
974   - a.source === b.source && // the regex itself
975   - a.global === b.global && // and its modifers (gmi) ...
976   - a.ignoreCase === b.ignoreCase &&
977   - a.multiline === b.multiline;
978   - },
979   -
980   - // - skip when the property is a method of an instance (OOP)
981   - // - abort otherwise,
982   - // initial === would have catch identical references anyway
983   - "function": function () {
984   - var caller = callers[callers.length - 1];
985   - return caller !== Object &&
986   - typeof caller !== "undefined";
987   - },
988   -
989   - "array": function (b, a) {
990   - var i, j, loop;
991   - var len;
992   -
993   - // b could be an object literal here
994   - if ( ! (QUnit.objectType(b) === "array")) {
995   - return false;
996   - }
997   -
998   - len = a.length;
999   - if (len !== b.length) { // safe and faster
1000   - return false;
1001   - }
1002   -
1003   - //track reference to avoid circular references
1004   - parents.push(a);
1005   - for (i = 0; i < len; i++) {
1006   - loop = false;
1007   - for(j=0;j<parents.length;j++){
1008   - if(parents[j] === a[i]){
1009   - loop = true;//dont rewalk array
1010   - }
1011   - }
1012   - if (!loop && ! innerEquiv(a[i], b[i])) {
1013   - parents.pop();
1014   - return false;
1015   - }
1016   - }
1017   - parents.pop();
1018   - return true;
1019   - },
1020   -
1021   - "object": function (b, a) {
1022   - var i, j, loop;
1023   - var eq = true; // unless we can proove it
1024   - var aProperties = [], bProperties = []; // collection of strings
1025   -
1026   - // comparing constructors is more strict than using instanceof
1027   - if ( a.constructor !== b.constructor) {
1028   - return false;
1029   - }
1030   -
1031   - // stack constructor before traversing properties
1032   - callers.push(a.constructor);
1033   - //track reference to avoid circular references
1034   - parents.push(a);
1035   -
1036   - for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
1037   - loop = false;
1038   - for(j=0;j<parents.length;j++){
1039   - if(parents[j] === a[i])
1040   - loop = true; //don't go down the same path twice
1041   - }
1042   - aProperties.push(i); // collect a's properties
1043   -
1044   - if (!loop && ! innerEquiv(a[i], b[i])) {
1045   - eq = false;
1046   - break;
1047   - }
1048   - }
1049   -
1050   - callers.pop(); // unstack, we are done
1051   - parents.pop();
1052   -
1053   - for (i in b) {
1054   - bProperties.push(i); // collect b's properties
1055   - }
1056   -
1057   - // Ensures identical properties name
1058   - return eq && innerEquiv(aProperties.sort(), bProperties.sort());
1059   - }
1060   - };
1061   - }();
1062   -
1063   - innerEquiv = function () { // can take multiple arguments
1064   - var args = Array.prototype.slice.apply(arguments);
1065   - if (args.length < 2) {
1066   - return true; // end transition
1067   - }
1068   -
1069   - return (function (a, b) {
1070   - if (a === b) {
1071   - return true; // catch the most you can
1072   - } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || QUnit.objectType(a) !== QUnit.objectType(b)) {
1073   - return false; // don't lose time with error prone cases
1074   - } else {
1075   - return bindCallbacks(a, callbacks, [b, a]);
1076   - }
1077   -
1078   - // apply transition with (1..n) arguments
1079   - })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
1080   - };
1081   -
1082   - return innerEquiv;
1083   -
1084   -}();
1085   -
1086   -/**
1087   - * jsDump
1088   - * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
1089   - * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
1090   - * Date: 5/15/2008
1091   - * @projectDescription Advanced and extensible data dumping for Javascript.
1092   - * @version 1.0.0
1093   - * @author Ariel Flesler
1094   - * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
1095   - */
1096   -QUnit.jsDump = (function() {
1097   - function quote( str ) {
1098   - return '"' + str.toString().replace(/"/g, '\\"') + '"';
1099   - };
1100   - function literal( o ) {
1101   - return o + '';
1102   - };
1103   - function join( pre, arr, post ) {
1104   - var s = jsDump.separator(),
1105   - base = jsDump.indent(),
1106   - inner = jsDump.indent(1);
1107   - if ( arr.join )
1108   - arr = arr.join( ',' + s + inner );
1109   - if ( !arr )
1110   - return pre + post;
1111   - return [ pre, inner + arr, base + post ].join(s);
1112   - };
1113   - function array( arr ) {
1114   - var i = arr.length, ret = Array(i);
1115   - this.up();
1116   - while ( i-- )
1117   - ret[i] = this.parse( arr[i] );
1118   - this.down();
1119   - return join( '[', ret, ']' );
1120   - };
1121   -
1122   - var reName = /^function (\w+)/;
1123   -
1124   - var jsDump = {
1125   - parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance
1126   - var parser = this.parsers[ type || this.typeOf(obj) ];
1127   - type = typeof parser;
1128   -
1129