bootstrap-source/bootstrap-3.0.3/js/tests/vendor/qunit.js
author stetrabby <info@trabucchi.de>
Fri, 20 Dec 2013 22:49:16 +0100
changeset 54 0ded9d7748b7
permissions -rwxr-xr-x
initial less based on the pymove3d.css
info@54
     1
/**
info@54
     2
 * QUnit - A JavaScript Unit Testing Framework
info@54
     3
 *
info@54
     4
 * http://docs.jquery.com/QUnit
info@54
     5
 *
info@54
     6
 * Copyright (c) 2012 John Resig, Jörn Zaefferer
info@54
     7
 * Dual licensed under the MIT (MIT-LICENSE.txt)
info@54
     8
 * or GPL (GPL-LICENSE.txt) licenses.
info@54
     9
 */
info@54
    10
info@54
    11
(function(window) {
info@54
    12
info@54
    13
var defined = {
info@54
    14
	setTimeout: typeof window.setTimeout !== "undefined",
info@54
    15
	sessionStorage: (function() {
info@54
    16
		try {
info@54
    17
			return !!sessionStorage.getItem;
info@54
    18
		} catch(e) {
info@54
    19
			return false;
info@54
    20
		}
info@54
    21
	})()
info@54
    22
};
info@54
    23
info@54
    24
var testId = 0;
info@54
    25
info@54
    26
var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {
info@54
    27
	this.name = name;
info@54
    28
	this.testName = testName;
info@54
    29
	this.expected = expected;
info@54
    30
	this.testEnvironmentArg = testEnvironmentArg;
info@54
    31
	this.async = async;
info@54
    32
	this.callback = callback;
info@54
    33
	this.assertions = [];
info@54
    34
};
info@54
    35
Test.prototype = {
info@54
    36
	init: function() {
info@54
    37
		var tests = id("qunit-tests");
info@54
    38
		if (tests) {
info@54
    39
			var b = document.createElement("strong");
info@54
    40
				b.innerHTML = "Running " + this.name;
info@54
    41
			var li = document.createElement("li");
info@54
    42
				li.appendChild( b );
info@54
    43
				li.className = "running";
info@54
    44
				li.id = this.id = "test-output" + testId++;
info@54
    45
			tests.appendChild( li );
info@54
    46
		}
info@54
    47
	},
info@54
    48
	setup: function() {
info@54
    49
		if (this.module != config.previousModule) {
info@54
    50
			if ( config.previousModule ) {
info@54
    51
				QUnit.moduleDone( {
info@54
    52
					name: config.previousModule,
info@54
    53
					failed: config.moduleStats.bad,
info@54
    54
					passed: config.moduleStats.all - config.moduleStats.bad,
info@54
    55
					total: config.moduleStats.all
info@54
    56
				} );
info@54
    57
			}
info@54
    58
			config.previousModule = this.module;
info@54
    59
			config.moduleStats = { all: 0, bad: 0 };
info@54
    60
			QUnit.moduleStart( {
info@54
    61
				name: this.module
info@54
    62
			} );
info@54
    63
		}
info@54
    64
info@54
    65
		config.current = this;
info@54
    66
		this.testEnvironment = extend({
info@54
    67
			setup: function() {},
info@54
    68
			teardown: function() {}
info@54
    69
		}, this.moduleTestEnvironment);
info@54
    70
		if (this.testEnvironmentArg) {
info@54
    71
			extend(this.testEnvironment, this.testEnvironmentArg);
info@54
    72
		}
info@54
    73
info@54
    74
		QUnit.testStart( {
info@54
    75
			name: this.testName
info@54
    76
		} );
info@54
    77
info@54
    78
		// allow utility functions to access the current test environment
info@54
    79
		// TODO why??
info@54
    80
		QUnit.current_testEnvironment = this.testEnvironment;
info@54
    81
info@54
    82
		try {
info@54
    83
			if ( !config.pollution ) {
info@54
    84
				saveGlobal();
info@54
    85
			}
info@54
    86
info@54
    87
			this.testEnvironment.setup.call(this.testEnvironment);
info@54
    88
		} catch(e) {
info@54
    89
			QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message );
info@54
    90
		}
info@54
    91
	},
info@54
    92
	run: function() {
info@54
    93
		if ( this.async ) {
info@54
    94
			QUnit.stop();
info@54
    95
		}
info@54
    96
info@54
    97
		if ( config.notrycatch ) {
info@54
    98
			this.callback.call(this.testEnvironment);
info@54
    99
			return;
info@54
   100
		}
info@54
   101
		try {
info@54
   102
			this.callback.call(this.testEnvironment);
info@54
   103
		} catch(e) {
info@54
   104
			fail("Test " + this.testName + " died, exception and test follows", e, this.callback);
info@54
   105
			QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) );
info@54
   106
			// else next test will carry the responsibility
info@54
   107
			saveGlobal();
info@54
   108
info@54
   109
			// Restart the tests if they're blocking
info@54
   110
			if ( config.blocking ) {
info@54
   111
				start();
info@54
   112
			}
info@54
   113
		}
info@54
   114
	},
info@54
   115
	teardown: function() {
info@54
   116
		try {
info@54
   117
			this.testEnvironment.teardown.call(this.testEnvironment);
info@54
   118
			checkPollution();
info@54
   119
		} catch(e) {
info@54
   120
			QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message );
info@54
   121
		}
info@54
   122
	},
info@54
   123
	finish: function() {
info@54
   124
		if ( this.expected && this.expected != this.assertions.length ) {
info@54
   125
			QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );
info@54
   126
		}
info@54
   127
info@54
   128
		var good = 0, bad = 0,
info@54
   129
			tests = id("qunit-tests");
info@54
   130
info@54
   131
		config.stats.all += this.assertions.length;
info@54
   132
		config.moduleStats.all += this.assertions.length;
info@54
   133
info@54
   134
		if ( tests ) {
info@54
   135
			var ol = document.createElement("ol");
info@54
   136
info@54
   137
			for ( var i = 0; i < this.assertions.length; i++ ) {
info@54
   138
				var assertion = this.assertions[i];
info@54
   139
info@54
   140
				var li = document.createElement("li");
info@54
   141
				li.className = assertion.result ? "pass" : "fail";
info@54
   142
				li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed");
info@54
   143
				ol.appendChild( li );
info@54
   144
info@54
   145
				if ( assertion.result ) {
info@54
   146
					good++;
info@54
   147
				} else {
info@54
   148
					bad++;
info@54
   149
					config.stats.bad++;
info@54
   150
					config.moduleStats.bad++;
info@54
   151
				}
info@54
   152
			}
info@54
   153
info@54
   154
			// store result when possible
info@54
   155
			if ( QUnit.config.reorder && defined.sessionStorage ) {
info@54
   156
				if (bad) {
info@54
   157
					sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad);
info@54
   158
				} else {
info@54
   159
					sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName);
info@54
   160
				}
info@54
   161
			}
info@54
   162
info@54
   163
			if (bad == 0) {
info@54
   164
				ol.style.display = "none";
info@54
   165
			}
info@54
   166
info@54
   167
			var b = document.createElement("strong");
info@54
   168
			b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
info@54
   169
info@54
   170
			var a = document.createElement("a");
info@54
   171
			a.innerHTML = "Rerun";
info@54
   172
			a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
info@54
   173
info@54
   174
			addEvent(b, "click", function() {
info@54
   175
				var next = b.nextSibling.nextSibling,
info@54
   176
					display = next.style.display;
info@54
   177
				next.style.display = display === "none" ? "block" : "none";
info@54
   178
			});
info@54
   179
info@54
   180
			addEvent(b, "dblclick", function(e) {
info@54
   181
				var target = e && e.target ? e.target : window.event.srcElement;
info@54
   182
				if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
info@54
   183
					target = target.parentNode;
info@54
   184
				}
info@54
   185
				if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
info@54
   186
					window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
info@54
   187
				}
info@54
   188
			});
info@54
   189
info@54
   190
			var li = id(this.id);
info@54
   191
			li.className = bad ? "fail" : "pass";
info@54
   192
			li.removeChild( li.firstChild );
info@54
   193
			li.appendChild( b );
info@54
   194
			li.appendChild( a );
info@54
   195
			li.appendChild( ol );
info@54
   196
info@54
   197
		} else {
info@54
   198
			for ( var i = 0; i < this.assertions.length; i++ ) {
info@54
   199
				if ( !this.assertions[i].result ) {
info@54
   200
					bad++;
info@54
   201
					config.stats.bad++;
info@54
   202
					config.moduleStats.bad++;
info@54
   203
				}
info@54
   204
			}
info@54
   205
		}
info@54
   206
info@54
   207
		try {
info@54
   208
			QUnit.reset();
info@54
   209
		} catch(e) {
info@54
   210
			fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset);
info@54
   211
		}
info@54
   212
info@54
   213
		QUnit.testDone( {
info@54
   214
			name: this.testName,
info@54
   215
			failed: bad,
info@54
   216
			passed: this.assertions.length - bad,
info@54
   217
			total: this.assertions.length
info@54
   218
		} );
info@54
   219
	},
info@54
   220
info@54
   221
	queue: function() {
info@54
   222
		var test = this;
info@54
   223
		synchronize(function() {
info@54
   224
			test.init();
info@54
   225
		});
info@54
   226
		function run() {
info@54
   227
			// each of these can by async
info@54
   228
			synchronize(function() {
info@54
   229
				test.setup();
info@54
   230
			});
info@54
   231
			synchronize(function() {
info@54
   232
				test.run();
info@54
   233
			});
info@54
   234
			synchronize(function() {
info@54
   235
				test.teardown();
info@54
   236
			});
info@54
   237
			synchronize(function() {
info@54
   238
				test.finish();
info@54
   239
			});
info@54
   240
		}
info@54
   241
		// defer when previous test run passed, if storage is available
info@54
   242
		var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName);
info@54
   243
		if (bad) {
info@54
   244
			run();
info@54
   245
		} else {
info@54
   246
			synchronize(run);
info@54
   247
		};
info@54
   248
	}
info@54
   249
info@54
   250
};
info@54
   251
info@54
   252
var QUnit = {
info@54
   253
info@54
   254
	// call on start of module test to prepend name to all tests
info@54
   255
	module: function(name, testEnvironment) {
info@54
   256
		config.currentModule = name;
info@54
   257
		config.currentModuleTestEnviroment = testEnvironment;
info@54
   258
	},
info@54
   259
info@54
   260
	asyncTest: function(testName, expected, callback) {
info@54
   261
		if ( arguments.length === 2 ) {
info@54
   262
			callback = expected;
info@54
   263
			expected = 0;
info@54
   264
		}
info@54
   265
info@54
   266
		QUnit.test(testName, expected, callback, true);
info@54
   267
	},
info@54
   268
info@54
   269
	test: function(testName, expected, callback, async) {
info@54
   270
		var name = '<span class="test-name">' + testName + '</span>', testEnvironmentArg;
info@54
   271
info@54
   272
		if ( arguments.length === 2 ) {
info@54
   273
			callback = expected;
info@54
   274
			expected = null;
info@54
   275
		}
info@54
   276
		// is 2nd argument a testEnvironment?
info@54
   277
		if ( expected && typeof expected === 'object') {
info@54
   278
			testEnvironmentArg = expected;
info@54
   279
			expected = null;
info@54
   280
		}
info@54
   281
info@54
   282
		if ( config.currentModule ) {
info@54
   283
			name = '<span class="module-name">' + config.currentModule + "</span>: " + name;
info@54
   284
		}
info@54
   285
info@54
   286
		if ( !validTest(config.currentModule + ": " + testName) ) {
info@54
   287
			return;
info@54
   288
		}
info@54
   289
info@54
   290
		var test = new Test(name, testName, expected, testEnvironmentArg, async, callback);
info@54
   291
		test.module = config.currentModule;
info@54
   292
		test.moduleTestEnvironment = config.currentModuleTestEnviroment;
info@54
   293
		test.queue();
info@54
   294
	},
info@54
   295
info@54
   296
	/**
info@54
   297
	 * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
info@54
   298
	 */
info@54
   299
	expect: function(asserts) {
info@54
   300
		config.current.expected = asserts;
info@54
   301
	},
info@54
   302
info@54
   303
	/**
info@54
   304
	 * Asserts true.
info@54
   305
	 * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
info@54
   306
	 */
info@54
   307
	ok: function(a, msg) {
info@54
   308
		a = !!a;
info@54
   309
		var details = {
info@54
   310
			result: a,
info@54
   311
			message: msg
info@54
   312
		};
info@54
   313
		msg = escapeHtml(msg);
info@54
   314
		QUnit.log(details);
info@54
   315
		config.current.assertions.push({
info@54
   316
			result: a,
info@54
   317
			message: msg
info@54
   318
		});
info@54
   319
	},
info@54
   320
info@54
   321
	/**
info@54
   322
	 * Checks that the first two arguments are equal, with an optional message.
info@54
   323
	 * Prints out both actual and expected values.
info@54
   324
	 *
info@54
   325
	 * Prefered to ok( actual == expected, message )
info@54
   326
	 *
info@54
   327
	 * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
info@54
   328
	 *
info@54
   329
	 * @param Object actual
info@54
   330
	 * @param Object expected
info@54
   331
	 * @param String message (optional)
info@54
   332
	 */
info@54
   333
	equal: function(actual, expected, message) {
info@54
   334
		QUnit.push(expected == actual, actual, expected, message);
info@54
   335
	},
info@54
   336
info@54
   337
	notEqual: function(actual, expected, message) {
info@54
   338
		QUnit.push(expected != actual, actual, expected, message);
info@54
   339
	},
info@54
   340
info@54
   341
	deepEqual: function(actual, expected, message) {
info@54
   342
		QUnit.push(QUnit.equiv(actual, expected), actual, expected, message);
info@54
   343
	},
info@54
   344
info@54
   345
	notDeepEqual: function(actual, expected, message) {
info@54
   346
		QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message);
info@54
   347
	},
info@54
   348
info@54
   349
	strictEqual: function(actual, expected, message) {
info@54
   350
		QUnit.push(expected === actual, actual, expected, message);
info@54
   351
	},
info@54
   352
info@54
   353
	notStrictEqual: function(actual, expected, message) {
info@54
   354
		QUnit.push(expected !== actual, actual, expected, message);
info@54
   355
	},
info@54
   356
info@54
   357
	raises: function(block, expected, message) {
info@54
   358
		var actual, ok = false;
info@54
   359
info@54
   360
		if (typeof expected === 'string') {
info@54
   361
			message = expected;
info@54
   362
			expected = null;
info@54
   363
		}
info@54
   364
info@54
   365
		try {
info@54
   366
			block();
info@54
   367
		} catch (e) {
info@54
   368
			actual = e;
info@54
   369
		}
info@54
   370
info@54
   371
		if (actual) {
info@54
   372
			// we don't want to validate thrown error
info@54
   373
			if (!expected) {
info@54
   374
				ok = true;
info@54
   375
			// expected is a regexp
info@54
   376
			} else if (QUnit.objectType(expected) === "regexp") {
info@54
   377
				ok = expected.test(actual);
info@54
   378
			// expected is a constructor
info@54
   379
			} else if (actual instanceof expected) {
info@54
   380
				ok = true;
info@54
   381
			// expected is a validation function which returns true is validation passed
info@54
   382
			} else if (expected.call({}, actual) === true) {
info@54
   383
				ok = true;
info@54
   384
			}
info@54
   385
		}
info@54
   386
info@54
   387
		QUnit.ok(ok, message);
info@54
   388
	},
info@54
   389
info@54
   390
	start: function() {
info@54
   391
		config.semaphore--;
info@54
   392
		if (config.semaphore > 0) {
info@54
   393
			// don't start until equal number of stop-calls
info@54
   394
			return;
info@54
   395
		}
info@54
   396
		if (config.semaphore < 0) {
info@54
   397
			// ignore if start is called more often then stop
info@54
   398
			config.semaphore = 0;
info@54
   399
		}
info@54
   400
		// A slight delay, to avoid any current callbacks
info@54
   401
		if ( defined.setTimeout ) {
info@54
   402
			window.setTimeout(function() {
info@54
   403
				if (config.semaphore > 0) {
info@54
   404
					return;
info@54
   405
				}
info@54
   406
				if ( config.timeout ) {
info@54
   407
					clearTimeout(config.timeout);
info@54
   408
				}
info@54
   409
info@54
   410
				config.blocking = false;
info@54
   411
				process();
info@54
   412
			}, 13);
info@54
   413
		} else {
info@54
   414
			config.blocking = false;
info@54
   415
			process();
info@54
   416
		}
info@54
   417
	},
info@54
   418
info@54
   419
	stop: function(timeout) {
info@54
   420
		config.semaphore++;
info@54
   421
		config.blocking = true;
info@54
   422
info@54
   423
		if ( timeout && defined.setTimeout ) {
info@54
   424
			clearTimeout(config.timeout);
info@54
   425
			config.timeout = window.setTimeout(function() {
info@54
   426
				QUnit.ok( false, "Test timed out" );
info@54
   427
				QUnit.start();
info@54
   428
			}, timeout);
info@54
   429
		}
info@54
   430
	}
info@54
   431
};
info@54
   432
info@54
   433
// Backwards compatibility, deprecated
info@54
   434
QUnit.equals = QUnit.equal;
info@54
   435
QUnit.same = QUnit.deepEqual;
info@54
   436
info@54
   437
// Maintain internal state
info@54
   438
var config = {
info@54
   439
	// The queue of tests to run
info@54
   440
	queue: [],
info@54
   441
info@54
   442
	// block until document ready
info@54
   443
	blocking: true,
info@54
   444
info@54
   445
	// when enabled, show only failing tests
info@54
   446
	// gets persisted through sessionStorage and can be changed in UI via checkbox
info@54
   447
	hidepassed: false,
info@54
   448
info@54
   449
	// by default, run previously failed tests first
info@54
   450
	// very useful in combination with "Hide passed tests" checked
info@54
   451
	reorder: true,
info@54
   452
info@54
   453
	// by default, modify document.title when suite is done
info@54
   454
	altertitle: true,
info@54
   455
info@54
   456
	urlConfig: ['noglobals', 'notrycatch']
info@54
   457
};
info@54
   458
info@54
   459
// Load paramaters
info@54
   460
(function() {
info@54
   461
	var location = window.location || { search: "", protocol: "file:" },
info@54
   462
		params = location.search.slice( 1 ).split( "&" ),
info@54
   463
		length = params.length,
info@54
   464
		urlParams = {},
info@54
   465
		current;
info@54
   466
info@54
   467
	if ( params[ 0 ] ) {
info@54
   468
		for ( var i = 0; i < length; i++ ) {
info@54
   469
			current = params[ i ].split( "=" );
info@54
   470
			current[ 0 ] = decodeURIComponent( current[ 0 ] );
info@54
   471
			// allow just a key to turn on a flag, e.g., test.html?noglobals
info@54
   472
			current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
info@54
   473
			urlParams[ current[ 0 ] ] = current[ 1 ];
info@54
   474
		}
info@54
   475
	}
info@54
   476
info@54
   477
	QUnit.urlParams = urlParams;
info@54
   478
	config.filter = urlParams.filter;
info@54
   479
info@54
   480
	// Figure out if we're running the tests from a server or not
info@54
   481
	QUnit.isLocal = !!(location.protocol === 'file:');
info@54
   482
})();
info@54
   483
info@54
   484
// Expose the API as global variables, unless an 'exports'
info@54
   485
// object exists, in that case we assume we're in CommonJS
info@54
   486
if ( typeof exports === "undefined" || typeof require === "undefined" ) {
info@54
   487
	extend(window, QUnit);
info@54
   488
	window.QUnit = QUnit;
info@54
   489
} else {
info@54
   490
	extend(exports, QUnit);
info@54
   491
	exports.QUnit = QUnit;
info@54
   492
}
info@54
   493
info@54
   494
// define these after exposing globals to keep them in these QUnit namespace only
info@54
   495
extend(QUnit, {
info@54
   496
	config: config,
info@54
   497
info@54
   498
	// Initialize the configuration options
info@54
   499
	init: function() {
info@54
   500
		extend(config, {
info@54
   501
			stats: { all: 0, bad: 0 },
info@54
   502
			moduleStats: { all: 0, bad: 0 },
info@54
   503
			started: +new Date,
info@54
   504
			updateRate: 1000,
info@54
   505
			blocking: false,
info@54
   506
			autostart: true,
info@54
   507
			autorun: false,
info@54
   508
			filter: "",
info@54
   509
			queue: [],
info@54
   510
			semaphore: 0
info@54
   511
		});
info@54
   512
info@54
   513
		var tests = id( "qunit-tests" ),
info@54
   514
			banner = id( "qunit-banner" ),
info@54
   515
			result = id( "qunit-testresult" );
info@54
   516
info@54
   517
		if ( tests ) {
info@54
   518
			tests.innerHTML = "";
info@54
   519
		}
info@54
   520
info@54
   521
		if ( banner ) {
info@54
   522
			banner.className = "";
info@54
   523
		}
info@54
   524
info@54
   525
		if ( result ) {
info@54
   526
			result.parentNode.removeChild( result );
info@54
   527
		}
info@54
   528
info@54
   529
		if ( tests ) {
info@54
   530
			result = document.createElement( "p" );
info@54
   531
			result.id = "qunit-testresult";
info@54
   532
			result.className = "result";
info@54
   533
			tests.parentNode.insertBefore( result, tests );
info@54
   534
			result.innerHTML = 'Running...<br/>&nbsp;';
info@54
   535
		}
info@54
   536
	},
info@54
   537
info@54
   538
	/**
info@54
   539
	 * Resets the test setup. Useful for tests that modify the DOM.
info@54
   540
	 *
info@54
   541
	 * If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
info@54
   542
	 */
info@54
   543
	reset: function() {
info@54
   544
		if ( window.jQuery ) {
info@54
   545
			jQuery( "#qunit-fixture" ).html( config.fixture );
info@54
   546
		} else {
info@54
   547
			var main = id( 'qunit-fixture' );
info@54
   548
			if ( main ) {
info@54
   549
				main.innerHTML = config.fixture;
info@54
   550
			}
info@54
   551
		}
info@54
   552
	},
info@54
   553
info@54
   554
	/**
info@54
   555
	 * Trigger an event on an element.
info@54
   556
	 *
info@54
   557
	 * @example triggerEvent( document.body, "click" );
info@54
   558
	 *
info@54
   559
	 * @param DOMElement elem
info@54
   560
	 * @param String type
info@54
   561
	 */
info@54
   562
	triggerEvent: function( elem, type, event ) {
info@54
   563
		if ( document.createEvent ) {
info@54
   564
			event = document.createEvent("MouseEvents");
info@54
   565
			event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
info@54
   566
				0, 0, 0, 0, 0, false, false, false, false, 0, null);
info@54
   567
			elem.dispatchEvent( event );
info@54
   568
info@54
   569
		} else if ( elem.fireEvent ) {
info@54
   570
			elem.fireEvent("on"+type);
info@54
   571
		}
info@54
   572
	},
info@54
   573
info@54
   574
	// Safe object type checking
info@54
   575
	is: function( type, obj ) {
info@54
   576
		return QUnit.objectType( obj ) == type;
info@54
   577
	},
info@54
   578
info@54
   579
	objectType: function( obj ) {
info@54
   580
		if (typeof obj === "undefined") {
info@54
   581
				return "undefined";
info@54
   582
info@54
   583
		// consider: typeof null === object
info@54
   584
		}
info@54
   585
		if (obj === null) {
info@54
   586
				return "null";
info@54
   587
		}
info@54
   588
info@54
   589
		var type = Object.prototype.toString.call( obj )
info@54
   590
			.match(/^\[object\s(.*)\]$/)[1] || '';
info@54
   591
info@54
   592
		switch (type) {
info@54
   593
				case 'Number':
info@54
   594
						if (isNaN(obj)) {
info@54
   595
								return "nan";
info@54
   596
						} else {
info@54
   597
								return "number";
info@54
   598
						}
info@54
   599
				case 'String':
info@54
   600
				case 'Boolean':
info@54
   601
				case 'Array':
info@54
   602
				case 'Date':
info@54
   603
				case 'RegExp':
info@54
   604
				case 'Function':
info@54
   605
						return type.toLowerCase();
info@54
   606
		}
info@54
   607
		if (typeof obj === "object") {
info@54
   608
				return "object";
info@54
   609
		}
info@54
   610
		return undefined;
info@54
   611
	},
info@54
   612
info@54
   613
	push: function(result, actual, expected, message) {
info@54
   614
		var details = {
info@54
   615
			result: result,
info@54
   616
			message: message,
info@54
   617
			actual: actual,
info@54
   618
			expected: expected
info@54
   619
		};
info@54
   620
info@54
   621
		message = escapeHtml(message) || (result ? "okay" : "failed");
info@54
   622
		message = '<span class="test-message">' + message + "</span>";
info@54
   623
		expected = escapeHtml(QUnit.jsDump.parse(expected));
info@54
   624
		actual = escapeHtml(QUnit.jsDump.parse(actual));
info@54
   625
		var output = message + '<table><tr class="test-expected"><th>Expected: </th><td><pre>' + expected + '</pre></td></tr>';
info@54
   626
		if (actual != expected) {
info@54
   627
			output += '<tr class="test-actual"><th>Result: </th><td><pre>' + actual + '</pre></td></tr>';
info@54
   628
			output += '<tr class="test-diff"><th>Diff: </th><td><pre>' + QUnit.diff(expected, actual) +'</pre></td></tr>';
info@54
   629
		}
info@54
   630
		if (!result) {
info@54
   631
			var source = sourceFromStacktrace();
info@54
   632
			if (source) {
info@54
   633
				details.source = source;
info@54
   634
				output += '<tr class="test-source"><th>Source: </th><td><pre>' + escapeHtml(source) + '</pre></td></tr>';
info@54
   635
			}
info@54
   636
		}
info@54
   637
		output += "</table>";
info@54
   638
info@54
   639
		QUnit.log(details);
info@54
   640
info@54
   641
		config.current.assertions.push({
info@54
   642
			result: !!result,
info@54
   643
			message: output
info@54
   644
		});
info@54
   645
	},
info@54
   646
info@54
   647
	url: function( params ) {
info@54
   648
		params = extend( extend( {}, QUnit.urlParams ), params );
info@54
   649
		var querystring = "?",
info@54
   650
			key;
info@54
   651
		for ( key in params ) {
info@54
   652
			querystring += encodeURIComponent( key ) + "=" +
info@54
   653
				encodeURIComponent( params[ key ] ) + "&";
info@54
   654
		}
info@54
   655
		return window.location.pathname + querystring.slice( 0, -1 );
info@54
   656
	},
info@54
   657
info@54
   658
	extend: extend,
info@54
   659
	id: id,
info@54
   660
	addEvent: addEvent,
info@54
   661
info@54
   662
	// Logging callbacks; all receive a single argument with the listed properties
info@54
   663
	// run test/logs.html for any related changes
info@54
   664
	begin: function() {},
info@54
   665
	// done: { failed, passed, total, runtime }
info@54
   666
	done: function() {},
info@54
   667
	// log: { result, actual, expected, message }
info@54
   668
	log: function() {},
info@54
   669
	// testStart: { name }
info@54
   670
	testStart: function() {},
info@54
   671
	// testDone: { name, failed, passed, total }
info@54
   672
	testDone: function() {},
info@54
   673
	// moduleStart: { name }
info@54
   674
	moduleStart: function() {},
info@54
   675
	// moduleDone: { name, failed, passed, total }
info@54
   676
	moduleDone: function() {}
info@54
   677
});
info@54
   678
info@54
   679
if ( typeof document === "undefined" || document.readyState === "complete" ) {
info@54
   680
	config.autorun = true;
info@54
   681
}
info@54
   682
info@54
   683
QUnit.load = function() {
info@54
   684
	QUnit.begin({});
info@54
   685
info@54
   686
	// Initialize the config, saving the execution queue
info@54
   687
	var oldconfig = extend({}, config);
info@54
   688
	QUnit.init();
info@54
   689
	extend(config, oldconfig);
info@54
   690
info@54
   691
	config.blocking = false;
info@54
   692
info@54
   693
	var urlConfigHtml = '', len = config.urlConfig.length;
info@54
   694
	for ( var i = 0, val; i < len, val = config.urlConfig[i]; i++ ) {
info@54
   695
		config[val] = QUnit.urlParams[val];
info@54
   696
		urlConfigHtml += '<label><input name="' + val + '" type="checkbox"' + ( config[val] ? ' checked="checked"' : '' ) + '>' + val + '</label>';
info@54
   697
	}
info@54
   698
info@54
   699
	var userAgent = id("qunit-userAgent");
info@54
   700
	if ( userAgent ) {
info@54
   701
		userAgent.innerHTML = navigator.userAgent;
info@54
   702
	}
info@54
   703
	var banner = id("qunit-header");
info@54
   704
	if ( banner ) {
info@54
   705
		banner.innerHTML = '<a href="' + QUnit.url({ filter: undefined }) + '"> ' + banner.innerHTML + '</a> ' + urlConfigHtml;
info@54
   706
		addEvent( banner, "change", function( event ) {
info@54
   707
			var params = {};
info@54
   708
			params[ event.target.name ] = event.target.checked ? true : undefined;
info@54
   709
			window.location = QUnit.url( params );
info@54
   710
		});
info@54
   711
	}
info@54
   712
info@54
   713
	var toolbar = id("qunit-testrunner-toolbar");
info@54
   714
	if ( toolbar ) {
info@54
   715
		var filter = document.createElement("input");
info@54
   716
		filter.type = "checkbox";
info@54
   717
		filter.id = "qunit-filter-pass";
info@54
   718
		addEvent( filter, "click", function() {
info@54
   719
			var ol = document.getElementById("qunit-tests");
info@54
   720
			if ( filter.checked ) {
info@54
   721
				ol.className = ol.className + " hidepass";
info@54
   722
			} else {
info@54
   723
				var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
info@54
   724
				ol.className = tmp.replace(/ hidepass /, " ");
info@54
   725
			}
info@54
   726
			if ( defined.sessionStorage ) {
info@54
   727
				if (filter.checked) {
info@54
   728
					sessionStorage.setItem("qunit-filter-passed-tests", "true");
info@54
   729
				} else {
info@54
   730
					sessionStorage.removeItem("qunit-filter-passed-tests");
info@54
   731
				}
info@54
   732
			}
info@54
   733
		});
info@54
   734
		if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) {
info@54
   735
			filter.checked = true;
info@54
   736
			var ol = document.getElementById("qunit-tests");
info@54
   737
			ol.className = ol.className + " hidepass";
info@54
   738
		}
info@54
   739
		toolbar.appendChild( filter );
info@54
   740
info@54
   741
		var label = document.createElement("label");
info@54
   742
		label.setAttribute("for", "qunit-filter-pass");
info@54
   743
		label.innerHTML = "Hide passed tests";
info@54
   744
		toolbar.appendChild( label );
info@54
   745
	}
info@54
   746
info@54
   747
	var main = id('qunit-fixture');
info@54
   748
	if ( main ) {
info@54
   749
		config.fixture = main.innerHTML;
info@54
   750
	}
info@54
   751
info@54
   752
	if (config.autostart) {
info@54
   753
		QUnit.start();
info@54
   754
	}
info@54
   755
};
info@54
   756
info@54
   757
addEvent(window, "load", QUnit.load);
info@54
   758
info@54
   759
function done() {
info@54
   760
	config.autorun = true;
info@54
   761
info@54
   762
	// Log the last module results
info@54
   763
	if ( config.currentModule ) {
info@54
   764
		QUnit.moduleDone( {
info@54
   765
			name: config.currentModule,
info@54
   766
			failed: config.moduleStats.bad,
info@54
   767
			passed: config.moduleStats.all - config.moduleStats.bad,
info@54
   768
			total: config.moduleStats.all
info@54
   769
		} );
info@54
   770
	}
info@54
   771
info@54
   772
	var banner = id("qunit-banner"),
info@54
   773
		tests = id("qunit-tests"),
info@54
   774
		runtime = +new Date - config.started,
info@54
   775
		passed = config.stats.all - config.stats.bad,
info@54
   776
		html = [
info@54
   777
			'Tests completed in ',
info@54
   778
			runtime,
info@54
   779
			' milliseconds.<br/>',
info@54
   780
			'<span class="passed">',
info@54
   781
			passed,
info@54
   782
			'</span> tests of <span class="total">',
info@54
   783
			config.stats.all,
info@54
   784
			'</span> passed, <span class="failed">',
info@54
   785
			config.stats.bad,
info@54
   786
			'</span> failed.'
info@54
   787
		].join('');
info@54
   788
info@54
   789
	if ( banner ) {
info@54
   790
		banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
info@54
   791
	}
info@54
   792
info@54
   793
	if ( tests ) {
info@54
   794
		id( "qunit-testresult" ).innerHTML = html;
info@54
   795
	}
info@54
   796
info@54
   797
	if ( config.altertitle && typeof document !== "undefined" && document.title ) {
info@54
   798
		// show ✖ for good, ✔ for bad suite result in title
info@54
   799
		// use escape sequences in case file gets loaded with non-utf-8-charset
info@54
   800
		document.title = [
info@54
   801
			(config.stats.bad ? "\u2716" : "\u2714"),
info@54
   802
			document.title.replace(/^[\u2714\u2716] /i, "")
info@54
   803
		].join(" ");
info@54
   804
	}
info@54
   805
info@54
   806
	QUnit.done( {
info@54
   807
		failed: config.stats.bad,
info@54
   808
		passed: passed,
info@54
   809
		total: config.stats.all,
info@54
   810
		runtime: runtime
info@54
   811
	} );
info@54
   812
}
info@54
   813
info@54
   814
function validTest( name ) {
info@54
   815
	var filter = config.filter,
info@54
   816
		run = false;
info@54
   817
info@54
   818
	if ( !filter ) {
info@54
   819
		return true;
info@54
   820
	}
info@54
   821
info@54
   822
	var not = filter.charAt( 0 ) === "!";
info@54
   823
	if ( not ) {
info@54
   824
		filter = filter.slice( 1 );
info@54
   825
	}
info@54
   826
info@54
   827
	if ( name.indexOf( filter ) !== -1 ) {
info@54
   828
		return !not;
info@54
   829
	}
info@54
   830
info@54
   831
	if ( not ) {
info@54
   832
		run = true;
info@54
   833
	}
info@54
   834
info@54
   835
	return run;
info@54
   836
}
info@54
   837
info@54
   838
// so far supports only Firefox, Chrome and Opera (buggy)
info@54
   839
// could be extended in the future to use something like https://github.com/csnover/TraceKit
info@54
   840
function sourceFromStacktrace() {
info@54
   841
	try {
info@54
   842
		throw new Error();
info@54
   843
	} catch ( e ) {
info@54
   844
		if (e.stacktrace) {
info@54
   845
			// Opera
info@54
   846
			return e.stacktrace.split("\n")[6];
info@54
   847
		} else if (e.stack) {
info@54
   848
			// Firefox, Chrome
info@54
   849
			return e.stack.split("\n")[4];
info@54
   850
		} else if (e.sourceURL) {
info@54
   851
			// Safari, PhantomJS
info@54
   852
			// TODO sourceURL points at the 'throw new Error' line above, useless
info@54
   853
			//return e.sourceURL + ":" + e.line;
info@54
   854
		}
info@54
   855
	}
info@54
   856
}
info@54
   857
info@54
   858
function escapeHtml(s) {
info@54
   859
	if (!s) {
info@54
   860
		return "";
info@54
   861
	}
info@54
   862
	s = s + "";
info@54
   863
	return s.replace(/[\&"<>\\]/g, function(s) {
info@54
   864
		switch(s) {
info@54
   865
			case "&": return "&amp;";
info@54
   866
			case "\\": return "\\\\";
info@54
   867
			case '"': return '\"';
info@54
   868
			case "<": return "&lt;";
info@54
   869
			case ">": return "&gt;";
info@54
   870
			default: return s;
info@54
   871
		}
info@54
   872
	});
info@54
   873
}
info@54
   874
info@54
   875
function synchronize( callback ) {
info@54
   876
	config.queue.push( callback );
info@54
   877
info@54
   878
	if ( config.autorun && !config.blocking ) {
info@54
   879
		process();
info@54
   880
	}
info@54
   881
}
info@54
   882
info@54
   883
function process() {
info@54
   884
	var start = (new Date()).getTime();
info@54
   885
info@54
   886
	while ( config.queue.length && !config.blocking ) {
info@54
   887
		if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {
info@54
   888
			config.queue.shift()();
info@54
   889
		} else {
info@54
   890
			window.setTimeout( process, 13 );
info@54
   891
			break;
info@54
   892
		}
info@54
   893
	}
info@54
   894
	if (!config.blocking && !config.queue.length) {
info@54
   895
		done();
info@54
   896
	}
info@54
   897
}
info@54
   898
info@54
   899
function saveGlobal() {
info@54
   900
	config.pollution = [];
info@54
   901
info@54
   902
	if ( config.noglobals ) {
info@54
   903
		for ( var key in window ) {
info@54
   904
			config.pollution.push( key );
info@54
   905
		}
info@54
   906
	}
info@54
   907
}
info@54
   908
info@54
   909
function checkPollution( name ) {
info@54
   910
	var old = config.pollution;
info@54
   911
	saveGlobal();
info@54
   912
info@54
   913
	var newGlobals = diff( config.pollution, old );
info@54
   914
	if ( newGlobals.length > 0 ) {
info@54
   915
		ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
info@54
   916
	}
info@54
   917
info@54
   918
	var deletedGlobals = diff( old, config.pollution );
info@54
   919
	if ( deletedGlobals.length > 0 ) {
info@54
   920
		ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
info@54
   921
	}
info@54
   922
}
info@54
   923
info@54
   924
// returns a new Array with the elements that are in a but not in b
info@54
   925
function diff( a, b ) {
info@54
   926
	var result = a.slice();
info@54
   927
	for ( var i = 0; i < result.length; i++ ) {
info@54
   928
		for ( var j = 0; j < b.length; j++ ) {
info@54
   929
			if ( result[i] === b[j] ) {
info@54
   930
				result.splice(i, 1);
info@54
   931
				i--;
info@54
   932
				break;
info@54
   933
			}
info@54
   934
		}
info@54
   935
	}
info@54
   936
	return result;
info@54
   937
}
info@54
   938
info@54
   939
function fail(message, exception, callback) {
info@54
   940
	if ( typeof console !== "undefined" && console.error && console.warn ) {
info@54
   941
		console.error(message);
info@54
   942
		console.error(exception);
info@54
   943
		console.warn(callback.toString());
info@54
   944
info@54
   945
	} else if ( window.opera && opera.postError ) {
info@54
   946
		opera.postError(message, exception, callback.toString);
info@54
   947
	}
info@54
   948
}
info@54
   949
info@54
   950
function extend(a, b) {
info@54
   951
	for ( var prop in b ) {
info@54
   952
		if ( b[prop] === undefined ) {
info@54
   953
			delete a[prop];
info@54
   954
		} else {
info@54
   955
			a[prop] = b[prop];
info@54
   956
		}
info@54
   957
	}
info@54
   958
info@54
   959
	return a;
info@54
   960
}
info@54
   961
info@54
   962
function addEvent(elem, type, fn) {
info@54
   963
	if ( elem.addEventListener ) {
info@54
   964
		elem.addEventListener( type, fn, false );
info@54
   965
	} else if ( elem.attachEvent ) {
info@54
   966
		elem.attachEvent( "on" + type, fn );
info@54
   967
	} else {
info@54
   968
		fn();
info@54
   969
	}
info@54
   970
}
info@54
   971
info@54
   972
function id(name) {
info@54
   973
	return !!(typeof document !== "undefined" && document && document.getElementById) &&
info@54
   974
		document.getElementById( name );
info@54
   975
}
info@54
   976
info@54
   977
// Test for equality any JavaScript type.
info@54
   978
// Discussions and reference: http://philrathe.com/articles/equiv
info@54
   979
// Test suites: http://philrathe.com/tests/equiv
info@54
   980
// Author: Philippe Rathé <prathe@gmail.com>
info@54
   981
QUnit.equiv = function () {
info@54
   982
info@54
   983
	var innerEquiv; // the real equiv function
info@54
   984
	var callers = []; // stack to decide between skip/abort functions
info@54
   985
	var parents = []; // stack to avoiding loops from circular referencing
info@54
   986
info@54
   987
	// Call the o related callback with the given arguments.
info@54
   988
	function bindCallbacks(o, callbacks, args) {
info@54
   989
		var prop = QUnit.objectType(o);
info@54
   990
		if (prop) {
info@54
   991
			if (QUnit.objectType(callbacks[prop]) === "function") {
info@54
   992
				return callbacks[prop].apply(callbacks, args);
info@54
   993
			} else {
info@54
   994
				return callbacks[prop]; // or undefined
info@54
   995
			}
info@54
   996
		}
info@54
   997
	}
info@54
   998
info@54
   999
	var callbacks = function () {
info@54
  1000
info@54
  1001
		// for string, boolean, number and null
info@54
  1002
		function useStrictEquality(b, a) {
info@54
  1003
			if (b instanceof a.constructor || a instanceof b.constructor) {
info@54
  1004
				// to catch short annotaion VS 'new' annotation of a
info@54
  1005
				// declaration
info@54
  1006
				// e.g. var i = 1;
info@54
  1007
				// var j = new Number(1);
info@54
  1008
				return a == b;
info@54
  1009
			} else {
info@54
  1010
				return a === b;
info@54
  1011
			}
info@54
  1012
		}
info@54
  1013
info@54
  1014
		return {
info@54
  1015
			"string" : useStrictEquality,
info@54
  1016
			"boolean" : useStrictEquality,
info@54
  1017
			"number" : useStrictEquality,
info@54
  1018
			"null" : useStrictEquality,
info@54
  1019
			"undefined" : useStrictEquality,
info@54
  1020
info@54
  1021
			"nan" : function(b) {
info@54
  1022
				return isNaN(b);
info@54
  1023
			},
info@54
  1024
info@54
  1025
			"date" : function(b, a) {
info@54
  1026
				return QUnit.objectType(b) === "date"
info@54
  1027
						&& a.valueOf() === b.valueOf();
info@54
  1028
			},
info@54
  1029
info@54
  1030
			"regexp" : function(b, a) {
info@54
  1031
				return QUnit.objectType(b) === "regexp"
info@54
  1032
						&& a.source === b.source && // the regex itself
info@54
  1033
						a.global === b.global && // and its modifers
info@54
  1034
													// (gmi) ...
info@54
  1035
						a.ignoreCase === b.ignoreCase
info@54
  1036
						&& a.multiline === b.multiline;
info@54
  1037
			},
info@54
  1038
info@54
  1039
			// - skip when the property is a method of an instance (OOP)
info@54
  1040
			// - abort otherwise,
info@54
  1041
			// initial === would have catch identical references anyway
info@54
  1042
			"function" : function() {
info@54
  1043
				var caller = callers[callers.length - 1];
info@54
  1044
				return caller !== Object && typeof caller !== "undefined";
info@54
  1045
			},
info@54
  1046
info@54
  1047
			"array" : function(b, a) {
info@54
  1048
				var i, j, loop;
info@54
  1049
				var len;
info@54
  1050
info@54
  1051
				// b could be an object literal here
info@54
  1052
				if (!(QUnit.objectType(b) === "array")) {
info@54
  1053
					return false;
info@54
  1054
				}
info@54
  1055
info@54
  1056
				len = a.length;
info@54
  1057
				if (len !== b.length) { // safe and faster
info@54
  1058
					return false;
info@54
  1059
				}
info@54
  1060
info@54
  1061
				// track reference to avoid circular references
info@54
  1062
				parents.push(a);
info@54
  1063
				for (i = 0; i < len; i++) {
info@54
  1064
					loop = false;
info@54
  1065
					for (j = 0; j < parents.length; j++) {
info@54
  1066
						if (parents[j] === a[i]) {
info@54
  1067
							loop = true;// dont rewalk array
info@54
  1068
						}
info@54
  1069
					}
info@54
  1070
					if (!loop && !innerEquiv(a[i], b[i])) {
info@54
  1071
						parents.pop();
info@54
  1072
						return false;
info@54
  1073
					}
info@54
  1074
				}
info@54
  1075
				parents.pop();
info@54
  1076
				return true;
info@54
  1077
			},
info@54
  1078
info@54
  1079
			"object" : function(b, a) {
info@54
  1080
				var i, j, loop;
info@54
  1081
				var eq = true; // unless we can proove it
info@54
  1082
				var aProperties = [], bProperties = []; // collection of
info@54
  1083
														// strings
info@54
  1084
info@54
  1085
				// comparing constructors is more strict than using
info@54
  1086
				// instanceof
info@54
  1087
				if (a.constructor !== b.constructor) {
info@54
  1088
					return false;
info@54
  1089
				}
info@54
  1090
info@54
  1091
				// stack constructor before traversing properties
info@54
  1092
				callers.push(a.constructor);
info@54
  1093
				// track reference to avoid circular references
info@54
  1094
				parents.push(a);
info@54
  1095
info@54
  1096
				for (i in a) { // be strict: don't ensures hasOwnProperty
info@54
  1097
								// and go deep
info@54
  1098
					loop = false;
info@54
  1099
					for (j = 0; j < parents.length; j++) {
info@54
  1100
						if (parents[j] === a[i])
info@54
  1101
							loop = true; // don't go down the same path
info@54
  1102
											// twice
info@54
  1103
					}
info@54
  1104
					aProperties.push(i); // collect a's properties
info@54
  1105
info@54
  1106
					if (!loop && !innerEquiv(a[i], b[i])) {
info@54
  1107
						eq = false;
info@54
  1108
						break;
info@54
  1109
					}
info@54
  1110
				}
info@54
  1111
info@54
  1112
				callers.pop(); // unstack, we are done
info@54
  1113
				parents.pop();
info@54
  1114
info@54
  1115
				for (i in b) {
info@54
  1116
					bProperties.push(i); // collect b's properties
info@54
  1117
				}
info@54
  1118
info@54
  1119
				// Ensures identical properties name
info@54
  1120
				return eq
info@54
  1121
						&& innerEquiv(aProperties.sort(), bProperties
info@54
  1122
								.sort());
info@54
  1123
			}
info@54
  1124
		};
info@54
  1125
	}();
info@54
  1126
info@54
  1127
	innerEquiv = function() { // can take multiple arguments
info@54
  1128
		var args = Array.prototype.slice.apply(arguments);
info@54
  1129
		if (args.length < 2) {
info@54
  1130
			return true; // end transition
info@54
  1131
		}
info@54
  1132
info@54
  1133
		return (function(a, b) {
info@54
  1134
			if (a === b) {
info@54
  1135
				return true; // catch the most you can
info@54
  1136
			} else if (a === null || b === null || typeof a === "undefined"
info@54
  1137
					|| typeof b === "undefined"
info@54
  1138
					|| QUnit.objectType(a) !== QUnit.objectType(b)) {
info@54
  1139
				return false; // don't lose time with error prone cases
info@54
  1140
			} else {
info@54
  1141
				return bindCallbacks(a, callbacks, [ b, a ]);
info@54
  1142
			}
info@54
  1143
info@54
  1144
			// apply transition with (1..n) arguments
info@54
  1145
		})(args[0], args[1])
info@54
  1146
				&& arguments.callee.apply(this, args.splice(1,
info@54
  1147
						args.length - 1));
info@54
  1148
	};
info@54
  1149
info@54
  1150
	return innerEquiv;
info@54
  1151
info@54
  1152
}();
info@54
  1153
info@54
  1154
/**
info@54
  1155
 * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
info@54
  1156
 * http://flesler.blogspot.com Licensed under BSD
info@54
  1157
 * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
info@54
  1158
 *
info@54
  1159
 * @projectDescription Advanced and extensible data dumping for Javascript.
info@54
  1160
 * @version 1.0.0
info@54
  1161
 * @author Ariel Flesler
info@54
  1162
 * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
info@54
  1163
 */
info@54
  1164
QUnit.jsDump = (function() {
info@54
  1165
	function quote( str ) {
info@54
  1166
		return '"' + str.toString().replace(/"/g, '\\"') + '"';
info@54
  1167
	};
info@54
  1168
	function literal( o ) {
info@54
  1169
		return o + '';
info@54
  1170
	};
info@54
  1171
	function join( pre, arr, post ) {
info@54
  1172
		var s = jsDump.separator(),
info@54
  1173
			base = jsDump.indent(),
info@54
  1174
			inner = jsDump.indent(1);
info@54
  1175
		if ( arr.join )
info@54
  1176
			arr = arr.join( ',' + s + inner );
info@54
  1177
		if ( !arr )
info@54
  1178
			return pre + post;
info@54
  1179
		return [ pre, inner + arr, base + post ].join(s);
info@54
  1180
	};
info@54
  1181
	function array( arr, stack ) {
info@54
  1182
		var i = arr.length, ret = Array(i);
info@54
  1183
		this.up();
info@54
  1184
		while ( i-- )
info@54
  1185
			ret[i] = this.parse( arr[i] , undefined , stack);
info@54
  1186
		this.down();
info@54
  1187
		return join( '[', ret, ']' );
info@54
  1188
	};
info@54
  1189
info@54
  1190
	var reName = /^function (\w+)/;
info@54
  1191
info@54
  1192
	var jsDump = {
info@54
  1193
		parse:function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance
info@54
  1194
			stack = stack || [ ];
info@54
  1195
			var parser = this.parsers[ type || this.typeOf(obj) ];
info@54
  1196
			type = typeof parser;
info@54
  1197
			var inStack = inArray(obj, stack);
info@54
  1198
			if (inStack != -1) {
info@54
  1199
				return 'recursion('+(inStack - stack.length)+')';
info@54
  1200
			}
info@54
  1201
			//else
info@54
  1202
			if (type == 'function')  {
info@54
  1203
					stack.push(obj);
info@54
  1204
					var res = parser.call( this, obj, stack );
info@54
  1205
					stack.pop();
info@54
  1206
					return res;
info@54
  1207
			}
info@54
  1208
			// else
info@54
  1209
			return (type == 'string') ? parser : this.parsers.error;
info@54
  1210
		},
info@54
  1211
		typeOf:function( obj ) {
info@54
  1212
			var type;
info@54
  1213
			if ( obj === null ) {
info@54
  1214
				type = "null";
info@54
  1215
			} else if (typeof obj === "undefined") {
info@54
  1216
				type = "undefined";
info@54
  1217
			} else if (QUnit.is("RegExp", obj)) {
info@54
  1218
				type = "regexp";
info@54
  1219
			} else if (QUnit.is("Date", obj)) {
info@54
  1220
				type = "date";
info@54
  1221
			} else if (QUnit.is("Function", obj)) {
info@54
  1222
				type = "function";
info@54
  1223
			} else if (typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined") {
info@54
  1224
				type = "window";
info@54
  1225
			} else if (obj.nodeType === 9) {
info@54
  1226
				type = "document";
info@54
  1227
			} else if (obj.nodeType) {
info@54
  1228
				type = "node";
info@54
  1229
			} else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) {
info@54
  1230
				type = "array";
info@54
  1231
			} else {
info@54
  1232
				type = typeof obj;
info@54
  1233
			}
info@54
  1234
			return type;
info@54
  1235
		},
info@54
  1236
		separator:function() {
info@54
  1237
			return this.multiline ?	this.HTML ? '<br />' : '\n' : this.HTML ? '&nbsp;' : ' ';
info@54
  1238
		},
info@54
  1239
		indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
info@54
  1240
			if ( !this.multiline )
info@54
  1241
				return '';
info@54
  1242
			var chr = this.indentChar;
info@54
  1243
			if ( this.HTML )
info@54
  1244
				chr = chr.replace(/\t/g,'   ').replace(/ /g,'&nbsp;');
info@54
  1245
			return Array( this._depth_ + (extra||0) ).join(chr);
info@54
  1246
		},
info@54
  1247
		up:function( a ) {
info@54
  1248
			this._depth_ += a || 1;
info@54
  1249
		},
info@54
  1250
		down:function( a ) {
info@54
  1251
			this._depth_ -= a || 1;
info@54
  1252
		},
info@54
  1253
		setParser:function( name, parser ) {
info@54
  1254
			this.parsers[name] = parser;
info@54
  1255
		},
info@54
  1256
		// The next 3 are exposed so you can use them
info@54
  1257
		quote:quote,
info@54
  1258
		literal:literal,
info@54
  1259
		join:join,
info@54
  1260
		//
info@54
  1261
		_depth_: 1,
info@54
  1262
		// This is the list of parsers, to modify them, use jsDump.setParser
info@54
  1263
		parsers:{
info@54
  1264
			window: '[Window]',
info@54
  1265
			document: '[Document]',
info@54
  1266
			error:'[ERROR]', //when no parser is found, shouldn't happen
info@54
  1267
			unknown: '[Unknown]',
info@54
  1268
			'null':'null',
info@54
  1269
			'undefined':'undefined',
info@54
  1270
			'function':function( fn ) {
info@54
  1271
				var ret = 'function',
info@54
  1272
					name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
info@54
  1273
				if ( name )
info@54
  1274
					ret += ' ' + name;
info@54
  1275
				ret += '(';
info@54
  1276
info@54
  1277
				ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join('');
info@54
  1278
				return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' );
info@54
  1279
			},
info@54
  1280
			array: array,
info@54
  1281
			nodelist: array,
info@54
  1282
			arguments: array,
info@54
  1283
			object:function( map, stack ) {
info@54
  1284
				var ret = [ ];
info@54
  1285
				QUnit.jsDump.up();
info@54
  1286
				for ( var key in map ) {
info@54
  1287
				    var val = map[key];
info@54
  1288
					ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(val, undefined, stack));
info@54
  1289
                }
info@54
  1290
				QUnit.jsDump.down();
info@54
  1291
				return join( '{', ret, '}' );
info@54
  1292
			},
info@54
  1293
			node:function( node ) {
info@54
  1294
				var open = QUnit.jsDump.HTML ? '&lt;' : '<',
info@54
  1295
					close = QUnit.jsDump.HTML ? '&gt;' : '>';
info@54
  1296
info@54
  1297
				var tag = node.nodeName.toLowerCase(),
info@54
  1298
					ret = open + tag;
info@54
  1299
info@54
  1300
				for ( var a in QUnit.jsDump.DOMAttrs ) {
info@54
  1301
					var val = node[QUnit.jsDump.DOMAttrs[a]];
info@54
  1302
					if ( val )
info@54
  1303
						ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' );
info@54
  1304
				}
info@54
  1305
				return ret + close + open + '/' + tag + close;
info@54
  1306
			},
info@54
  1307
			functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function
info@54
  1308
				var l = fn.length;
info@54
  1309
				if ( !l ) return '';
info@54
  1310
info@54
  1311
				var args = Array(l);
info@54
  1312
				while ( l-- )
info@54
  1313
					args[l] = String.fromCharCode(97+l);//97 is 'a'
info@54
  1314
				return ' ' + args.join(', ') + ' ';
info@54
  1315
			},
info@54
  1316
			key:quote, //object calls it internally, the key part of an item in a map
info@54
  1317
			functionCode:'[code]', //function calls it internally, it's the content of the function
info@54
  1318
			attribute:quote, //node calls it internally, it's an html attribute value
info@54
  1319
			string:quote,
info@54
  1320
			date:quote,
info@54
  1321
			regexp:literal, //regex
info@54
  1322
			number:literal,
info@54
  1323
			'boolean':literal
info@54
  1324
		},
info@54
  1325
		DOMAttrs:{//attributes to dump from nodes, name=>realName
info@54
  1326
			id:'id',
info@54
  1327
			name:'name',
info@54
  1328
			'class':'className'
info@54
  1329
		},
info@54
  1330
		HTML:false,//if true, entities are escaped ( <, >, \t, space and \n )
info@54
  1331
		indentChar:'  ',//indentation unit
info@54
  1332
		multiline:true //if true, items in a collection, are separated by a \n, else just a space.
info@54
  1333
	};
info@54
  1334
info@54
  1335
	return jsDump;
info@54
  1336
})();
info@54
  1337
info@54
  1338
// from Sizzle.js
info@54
  1339
function getText( elems ) {
info@54
  1340
	var ret = "", elem;
info@54
  1341
info@54
  1342
	for ( var i = 0; elems[i]; i++ ) {
info@54
  1343
		elem = elems[i];
info@54
  1344
info@54
  1345
		// Get the text from text nodes and CDATA nodes
info@54
  1346
		if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
info@54
  1347
			ret += elem.nodeValue;
info@54
  1348
info@54
  1349
		// Traverse everything else, except comment nodes
info@54
  1350
		} else if ( elem.nodeType !== 8 ) {
info@54
  1351
			ret += getText( elem.childNodes );
info@54
  1352
		}
info@54
  1353
	}
info@54
  1354
info@54
  1355
	return ret;
info@54
  1356
};
info@54
  1357
info@54
  1358
//from jquery.js
info@54
  1359
function inArray( elem, array ) {
info@54
  1360
	if ( array.indexOf ) {
info@54
  1361
		return array.indexOf( elem );
info@54
  1362
	}
info@54
  1363
info@54
  1364
	for ( var i = 0, length = array.length; i < length; i++ ) {
info@54
  1365
		if ( array[ i ] === elem ) {
info@54
  1366
			return i;
info@54
  1367
		}
info@54
  1368
	}
info@54
  1369
info@54
  1370
	return -1;
info@54
  1371
}
info@54
  1372
info@54
  1373
/*
info@54
  1374
 * Javascript Diff Algorithm
info@54
  1375
 *  By John Resig (http://ejohn.org/)
info@54
  1376
 *  Modified by Chu Alan "sprite"
info@54
  1377
 *
info@54
  1378
 * Released under the MIT license.
info@54
  1379
 *
info@54
  1380
 * More Info:
info@54
  1381
 *  http://ejohn.org/projects/javascript-diff-algorithm/
info@54
  1382
 *
info@54
  1383
 * Usage: QUnit.diff(expected, actual)
info@54
  1384
 *
info@54
  1385
 * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the  quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
info@54
  1386
 */
info@54
  1387
QUnit.diff = (function() {
info@54
  1388
	function diff(o, n) {
info@54
  1389
		var ns = {};
info@54
  1390
		var os = {};
info@54
  1391
info@54
  1392
		for (var i = 0; i < n.length; i++) {
info@54
  1393
			if (ns[n[i]] == null)
info@54
  1394
				ns[n[i]] = {
info@54
  1395
					rows: [],
info@54
  1396
					o: null
info@54
  1397
				};
info@54
  1398
			ns[n[i]].rows.push(i);
info@54
  1399
		}
info@54
  1400
info@54
  1401
		for (var i = 0; i < o.length; i++) {
info@54
  1402
			if (os[o[i]] == null)
info@54
  1403
				os[o[i]] = {
info@54
  1404
					rows: [],
info@54
  1405
					n: null
info@54
  1406
				};
info@54
  1407
			os[o[i]].rows.push(i);
info@54
  1408
		}
info@54
  1409
info@54
  1410
		for (var i in ns) {
info@54
  1411
			if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) {
info@54
  1412
				n[ns[i].rows[0]] = {
info@54
  1413
					text: n[ns[i].rows[0]],
info@54
  1414
					row: os[i].rows[0]
info@54
  1415
				};
info@54
  1416
				o[os[i].rows[0]] = {
info@54
  1417
					text: o[os[i].rows[0]],
info@54
  1418
					row: ns[i].rows[0]
info@54
  1419
				};
info@54
  1420
			}
info@54
  1421
		}
info@54
  1422
info@54
  1423
		for (var i = 0; i < n.length - 1; i++) {
info@54
  1424
			if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null &&
info@54
  1425
			n[i + 1] == o[n[i].row + 1]) {
info@54
  1426
				n[i + 1] = {
info@54
  1427
					text: n[i + 1],
info@54
  1428
					row: n[i].row + 1
info@54
  1429
				};
info@54
  1430
				o[n[i].row + 1] = {
info@54
  1431
					text: o[n[i].row + 1],
info@54
  1432
					row: i + 1
info@54
  1433
				};
info@54
  1434
			}
info@54
  1435
		}
info@54
  1436
info@54
  1437
		for (var i = n.length - 1; i > 0; i--) {
info@54
  1438
			if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null &&
info@54
  1439
			n[i - 1] == o[n[i].row - 1]) {
info@54
  1440
				n[i - 1] = {
info@54
  1441
					text: n[i - 1],
info@54
  1442
					row: n[i].row - 1
info@54
  1443
				};
info@54
  1444
				o[n[i].row - 1] = {
info@54
  1445
					text: o[n[i].row - 1],
info@54
  1446
					row: i - 1
info@54
  1447
				};
info@54
  1448
			}
info@54
  1449
		}
info@54
  1450
info@54
  1451
		return {
info@54
  1452
			o: o,
info@54
  1453
			n: n
info@54
  1454
		};
info@54
  1455
	}
info@54
  1456
info@54
  1457
	return function(o, n) {
info@54
  1458
		o = o.replace(/\s+$/, '');
info@54
  1459
		n = n.replace(/\s+$/, '');
info@54
  1460
		var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/));
info@54
  1461
info@54
  1462
		var str = "";
info@54
  1463
info@54
  1464
		var oSpace = o.match(/\s+/g);
info@54
  1465
		if (oSpace == null) {
info@54
  1466
			oSpace = [" "];
info@54
  1467
		}
info@54
  1468
		else {
info@54
  1469
			oSpace.push(" ");
info@54
  1470
		}
info@54
  1471
		var nSpace = n.match(/\s+/g);
info@54
  1472
		if (nSpace == null) {
info@54
  1473
			nSpace = [" "];
info@54
  1474
		}
info@54
  1475
		else {
info@54
  1476
			nSpace.push(" ");
info@54
  1477
		}
info@54
  1478
info@54
  1479
		if (out.n.length == 0) {
info@54
  1480
			for (var i = 0; i < out.o.length; i++) {
info@54
  1481
				str += '<del>' + out.o[i] + oSpace[i] + "</del>";
info@54
  1482
			}
info@54
  1483
		}
info@54
  1484
		else {
info@54
  1485
			if (out.n[0].text == null) {
info@54
  1486
				for (n = 0; n < out.o.length && out.o[n].text == null; n++) {
info@54
  1487
					str += '<del>' + out.o[n] + oSpace[n] + "</del>";
info@54
  1488
				}
info@54
  1489
			}
info@54
  1490
info@54
  1491
			for (var i = 0; i < out.n.length; i++) {
info@54
  1492
				if (out.n[i].text == null) {
info@54
  1493
					str += '<ins>' + out.n[i] + nSpace[i] + "</ins>";
info@54
  1494
				}
info@54
  1495
				else {
info@54
  1496
					var pre = "";
info@54
  1497
info@54
  1498
					for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) {
info@54
  1499
						pre += '<del>' + out.o[n] + oSpace[n] + "</del>";
info@54
  1500
					}
info@54
  1501
					str += " " + out.n[i].text + nSpace[i] + pre;
info@54
  1502
				}
info@54
  1503
			}
info@54
  1504
		}
info@54
  1505
info@54
  1506
		return str;
info@54
  1507
	};
info@54
  1508
})();
info@54
  1509
info@54
  1510
})(this);
Impressum Datenschutzerklärung