github.com/sentienttechnologies/studio-go-runner@v0.0.0-20201118202441-6d21f2ced8ee/docs/slides/test/qunit-1.12.0.js (about)

     1  /**
     2   * QUnit v1.12.0 - A JavaScript Unit Testing Framework
     3   *
     4   * http://qunitjs.com
     5   *
     6   * Copyright 2013 jQuery Foundation and other contributors
     7   * Released under the MIT license.
     8   * https://jquery.org/license/
     9   */
    10  
    11  (function( window ) {
    12  
    13  var QUnit,
    14  	assert,
    15  	config,
    16  	onErrorFnPrev,
    17  	testId = 0,
    18  	fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
    19  	toString = Object.prototype.toString,
    20  	hasOwn = Object.prototype.hasOwnProperty,
    21  	// Keep a local reference to Date (GH-283)
    22  	Date = window.Date,
    23  	setTimeout = window.setTimeout,
    24  	defined = {
    25  		setTimeout: typeof window.setTimeout !== "undefined",
    26  		sessionStorage: (function() {
    27  			var x = "qunit-test-string";
    28  			try {
    29  				sessionStorage.setItem( x, x );
    30  				sessionStorage.removeItem( x );
    31  				return true;
    32  			} catch( e ) {
    33  				return false;
    34  			}
    35  		}())
    36  	},
    37  	/**
    38  	 * Provides a normalized error string, correcting an issue
    39  	 * with IE 7 (and prior) where Error.prototype.toString is
    40  	 * not properly implemented
    41  	 *
    42  	 * Based on http://es5.github.com/#x15.11.4.4
    43  	 *
    44  	 * @param {String|Error} error
    45  	 * @return {String} error message
    46  	 */
    47  	errorString = function( error ) {
    48  		var name, message,
    49  			errorString = error.toString();
    50  		if ( errorString.substring( 0, 7 ) === "[object" ) {
    51  			name = error.name ? error.name.toString() : "Error";
    52  			message = error.message ? error.message.toString() : "";
    53  			if ( name && message ) {
    54  				return name + ": " + message;
    55  			} else if ( name ) {
    56  				return name;
    57  			} else if ( message ) {
    58  				return message;
    59  			} else {
    60  				return "Error";
    61  			}
    62  		} else {
    63  			return errorString;
    64  		}
    65  	},
    66  	/**
    67  	 * Makes a clone of an object using only Array or Object as base,
    68  	 * and copies over the own enumerable properties.
    69  	 *
    70  	 * @param {Object} obj
    71  	 * @return {Object} New object with only the own properties (recursively).
    72  	 */
    73  	objectValues = function( obj ) {
    74  		// Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392.
    75  		/*jshint newcap: false */
    76  		var key, val,
    77  			vals = QUnit.is( "array", obj ) ? [] : {};
    78  		for ( key in obj ) {
    79  			if ( hasOwn.call( obj, key ) ) {
    80  				val = obj[key];
    81  				vals[key] = val === Object(val) ? objectValues(val) : val;
    82  			}
    83  		}
    84  		return vals;
    85  	};
    86  
    87  function Test( settings ) {
    88  	extend( this, settings );
    89  	this.assertions = [];
    90  	this.testNumber = ++Test.count;
    91  }
    92  
    93  Test.count = 0;
    94  
    95  Test.prototype = {
    96  	init: function() {
    97  		var a, b, li,
    98  			tests = id( "qunit-tests" );
    99  
   100  		if ( tests ) {
   101  			b = document.createElement( "strong" );
   102  			b.innerHTML = this.nameHtml;
   103  
   104  			// `a` initialized at top of scope
   105  			a = document.createElement( "a" );
   106  			a.innerHTML = "Rerun";
   107  			a.href = QUnit.url({ testNumber: this.testNumber });
   108  
   109  			li = document.createElement( "li" );
   110  			li.appendChild( b );
   111  			li.appendChild( a );
   112  			li.className = "running";
   113  			li.id = this.id = "qunit-test-output" + testId++;
   114  
   115  			tests.appendChild( li );
   116  		}
   117  	},
   118  	setup: function() {
   119  		if (
   120  			// Emit moduleStart when we're switching from one module to another
   121  			this.module !== config.previousModule ||
   122  				// They could be equal (both undefined) but if the previousModule property doesn't
   123  				// yet exist it means this is the first test in a suite that isn't wrapped in a
   124  				// module, in which case we'll just emit a moduleStart event for 'undefined'.
   125  				// Without this, reporters can get testStart before moduleStart  which is a problem.
   126  				!hasOwn.call( config, "previousModule" )
   127  		) {
   128  			if ( hasOwn.call( config, "previousModule" ) ) {
   129  				runLoggingCallbacks( "moduleDone", QUnit, {
   130  					name: config.previousModule,
   131  					failed: config.moduleStats.bad,
   132  					passed: config.moduleStats.all - config.moduleStats.bad,
   133  					total: config.moduleStats.all
   134  				});
   135  			}
   136  			config.previousModule = this.module;
   137  			config.moduleStats = { all: 0, bad: 0 };
   138  			runLoggingCallbacks( "moduleStart", QUnit, {
   139  				name: this.module
   140  			});
   141  		}
   142  
   143  		config.current = this;
   144  
   145  		this.testEnvironment = extend({
   146  			setup: function() {},
   147  			teardown: function() {}
   148  		}, this.moduleTestEnvironment );
   149  
   150  		this.started = +new Date();
   151  		runLoggingCallbacks( "testStart", QUnit, {
   152  			name: this.testName,
   153  			module: this.module
   154  		});
   155  
   156  		/*jshint camelcase:false */
   157  
   158  
   159  		/**
   160  		 * Expose the current test environment.
   161  		 *
   162  		 * @deprecated since 1.12.0: Use QUnit.config.current.testEnvironment instead.
   163  		 */
   164  		QUnit.current_testEnvironment = this.testEnvironment;
   165  
   166  		/*jshint camelcase:true */
   167  
   168  		if ( !config.pollution ) {
   169  			saveGlobal();
   170  		}
   171  		if ( config.notrycatch ) {
   172  			this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
   173  			return;
   174  		}
   175  		try {
   176  			this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
   177  		} catch( e ) {
   178  			QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
   179  		}
   180  	},
   181  	run: function() {
   182  		config.current = this;
   183  
   184  		var running = id( "qunit-testresult" );
   185  
   186  		if ( running ) {
   187  			running.innerHTML = "Running: <br/>" + this.nameHtml;
   188  		}
   189  
   190  		if ( this.async ) {
   191  			QUnit.stop();
   192  		}
   193  
   194  		this.callbackStarted = +new Date();
   195  
   196  		if ( config.notrycatch ) {
   197  			this.callback.call( this.testEnvironment, QUnit.assert );
   198  			this.callbackRuntime = +new Date() - this.callbackStarted;
   199  			return;
   200  		}
   201  
   202  		try {
   203  			this.callback.call( this.testEnvironment, QUnit.assert );
   204  			this.callbackRuntime = +new Date() - this.callbackStarted;
   205  		} catch( e ) {
   206  			this.callbackRuntime = +new Date() - this.callbackStarted;
   207  
   208  			QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
   209  			// else next test will carry the responsibility
   210  			saveGlobal();
   211  
   212  			// Restart the tests if they're blocking
   213  			if ( config.blocking ) {
   214  				QUnit.start();
   215  			}
   216  		}
   217  	},
   218  	teardown: function() {
   219  		config.current = this;
   220  		if ( config.notrycatch ) {
   221  			if ( typeof this.callbackRuntime === "undefined" ) {
   222  				this.callbackRuntime = +new Date() - this.callbackStarted;
   223  			}
   224  			this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );
   225  			return;
   226  		} else {
   227  			try {
   228  				this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );
   229  			} catch( e ) {
   230  				QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
   231  			}
   232  		}
   233  		checkPollution();
   234  	},
   235  	finish: function() {
   236  		config.current = this;
   237  		if ( config.requireExpects && this.expected === null ) {
   238  			QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
   239  		} else if ( this.expected !== null && this.expected !== this.assertions.length ) {
   240  			QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
   241  		} else if ( this.expected === null && !this.assertions.length ) {
   242  			QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
   243  		}
   244  
   245  		var i, assertion, a, b, time, li, ol,
   246  			test = this,
   247  			good = 0,
   248  			bad = 0,
   249  			tests = id( "qunit-tests" );
   250  
   251  		this.runtime = +new Date() - this.started;
   252  		config.stats.all += this.assertions.length;
   253  		config.moduleStats.all += this.assertions.length;
   254  
   255  		if ( tests ) {
   256  			ol = document.createElement( "ol" );
   257  			ol.className = "qunit-assert-list";
   258  
   259  			for ( i = 0; i < this.assertions.length; i++ ) {
   260  				assertion = this.assertions[i];
   261  
   262  				li = document.createElement( "li" );
   263  				li.className = assertion.result ? "pass" : "fail";
   264  				li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );
   265  				ol.appendChild( li );
   266  
   267  				if ( assertion.result ) {
   268  					good++;
   269  				} else {
   270  					bad++;
   271  					config.stats.bad++;
   272  					config.moduleStats.bad++;
   273  				}
   274  			}
   275  
   276  			// store result when possible
   277  			if ( QUnit.config.reorder && defined.sessionStorage ) {
   278  				if ( bad ) {
   279  					sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );
   280  				} else {
   281  					sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );
   282  				}
   283  			}
   284  
   285  			if ( bad === 0 ) {
   286  				addClass( ol, "qunit-collapsed" );
   287  			}
   288  
   289  			// `b` initialized at top of scope
   290  			b = document.createElement( "strong" );
   291  			b.innerHTML = this.nameHtml + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
   292  
   293  			addEvent(b, "click", function() {
   294  				var next = b.parentNode.lastChild,
   295  					collapsed = hasClass( next, "qunit-collapsed" );
   296  				( collapsed ? removeClass : addClass )( next, "qunit-collapsed" );
   297  			});
   298  
   299  			addEvent(b, "dblclick", function( e ) {
   300  				var target = e && e.target ? e.target : window.event.srcElement;
   301  				if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) {
   302  					target = target.parentNode;
   303  				}
   304  				if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
   305  					window.location = QUnit.url({ testNumber: test.testNumber });
   306  				}
   307  			});
   308  
   309  			// `time` initialized at top of scope
   310  			time = document.createElement( "span" );
   311  			time.className = "runtime";
   312  			time.innerHTML = this.runtime + " ms";
   313  
   314  			// `li` initialized at top of scope
   315  			li = id( this.id );
   316  			li.className = bad ? "fail" : "pass";
   317  			li.removeChild( li.firstChild );
   318  			a = li.firstChild;
   319  			li.appendChild( b );
   320  			li.appendChild( a );
   321  			li.appendChild( time );
   322  			li.appendChild( ol );
   323  
   324  		} else {
   325  			for ( i = 0; i < this.assertions.length; i++ ) {
   326  				if ( !this.assertions[i].result ) {
   327  					bad++;
   328  					config.stats.bad++;
   329  					config.moduleStats.bad++;
   330  				}
   331  			}
   332  		}
   333  
   334  		runLoggingCallbacks( "testDone", QUnit, {
   335  			name: this.testName,
   336  			module: this.module,
   337  			failed: bad,
   338  			passed: this.assertions.length - bad,
   339  			total: this.assertions.length,
   340  			duration: this.runtime
   341  		});
   342  
   343  		QUnit.reset();
   344  
   345  		config.current = undefined;
   346  	},
   347  
   348  	queue: function() {
   349  		var bad,
   350  			test = this;
   351  
   352  		synchronize(function() {
   353  			test.init();
   354  		});
   355  		function run() {
   356  			// each of these can by async
   357  			synchronize(function() {
   358  				test.setup();
   359  			});
   360  			synchronize(function() {
   361  				test.run();
   362  			});
   363  			synchronize(function() {
   364  				test.teardown();
   365  			});
   366  			synchronize(function() {
   367  				test.finish();
   368  			});
   369  		}
   370  
   371  		// `bad` initialized at top of scope
   372  		// defer when previous test run passed, if storage is available
   373  		bad = QUnit.config.reorder && defined.sessionStorage &&
   374  						+sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
   375  
   376  		if ( bad ) {
   377  			run();
   378  		} else {
   379  			synchronize( run, true );
   380  		}
   381  	}
   382  };
   383  
   384  // Root QUnit object.
   385  // `QUnit` initialized at top of scope
   386  QUnit = {
   387  
   388  	// call on start of module test to prepend name to all tests
   389  	module: function( name, testEnvironment ) {
   390  		config.currentModule = name;
   391  		config.currentModuleTestEnvironment = testEnvironment;
   392  		config.modules[name] = true;
   393  	},
   394  
   395  	asyncTest: function( testName, expected, callback ) {
   396  		if ( arguments.length === 2 ) {
   397  			callback = expected;
   398  			expected = null;
   399  		}
   400  
   401  		QUnit.test( testName, expected, callback, true );
   402  	},
   403  
   404  	test: function( testName, expected, callback, async ) {
   405  		var test,
   406  			nameHtml = "<span class='test-name'>" + escapeText( testName ) + "</span>";
   407  
   408  		if ( arguments.length === 2 ) {
   409  			callback = expected;
   410  			expected = null;
   411  		}
   412  
   413  		if ( config.currentModule ) {
   414  			nameHtml = "<span class='module-name'>" + escapeText( config.currentModule ) + "</span>: " + nameHtml;
   415  		}
   416  
   417  		test = new Test({
   418  			nameHtml: nameHtml,
   419  			testName: testName,
   420  			expected: expected,
   421  			async: async,
   422  			callback: callback,
   423  			module: config.currentModule,
   424  			moduleTestEnvironment: config.currentModuleTestEnvironment,
   425  			stack: sourceFromStacktrace( 2 )
   426  		});
   427  
   428  		if ( !validTest( test ) ) {
   429  			return;
   430  		}
   431  
   432  		test.queue();
   433  	},
   434  
   435  	// Specify the number of expected assertions to guarantee that failed test (no assertions are run at all) don't slip through.
   436  	expect: function( asserts ) {
   437  		if (arguments.length === 1) {
   438  			config.current.expected = asserts;
   439  		} else {
   440  			return config.current.expected;
   441  		}
   442  	},
   443  
   444  	start: function( count ) {
   445  		// QUnit hasn't been initialized yet.
   446  		// Note: RequireJS (et al) may delay onLoad
   447  		if ( config.semaphore === undefined ) {
   448  			QUnit.begin(function() {
   449  				// This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first
   450  				setTimeout(function() {
   451  					QUnit.start( count );
   452  				});
   453  			});
   454  			return;
   455  		}
   456  
   457  		config.semaphore -= count || 1;
   458  		// don't start until equal number of stop-calls
   459  		if ( config.semaphore > 0 ) {
   460  			return;
   461  		}
   462  		// ignore if start is called more often then stop
   463  		if ( config.semaphore < 0 ) {
   464  			config.semaphore = 0;
   465  			QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) );
   466  			return;
   467  		}
   468  		// A slight delay, to avoid any current callbacks
   469  		if ( defined.setTimeout ) {
   470  			setTimeout(function() {
   471  				if ( config.semaphore > 0 ) {
   472  					return;
   473  				}
   474  				if ( config.timeout ) {
   475  					clearTimeout( config.timeout );
   476  				}
   477  
   478  				config.blocking = false;
   479  				process( true );
   480  			}, 13);
   481  		} else {
   482  			config.blocking = false;
   483  			process( true );
   484  		}
   485  	},
   486  
   487  	stop: function( count ) {
   488  		config.semaphore += count || 1;
   489  		config.blocking = true;
   490  
   491  		if ( config.testTimeout && defined.setTimeout ) {
   492  			clearTimeout( config.timeout );
   493  			config.timeout = setTimeout(function() {
   494  				QUnit.ok( false, "Test timed out" );
   495  				config.semaphore = 1;
   496  				QUnit.start();
   497  			}, config.testTimeout );
   498  		}
   499  	}
   500  };
   501  
   502  // `assert` initialized at top of scope
   503  // Assert helpers
   504  // All of these must either call QUnit.push() or manually do:
   505  // - runLoggingCallbacks( "log", .. );
   506  // - config.current.assertions.push({ .. });
   507  // We attach it to the QUnit object *after* we expose the public API,
   508  // otherwise `assert` will become a global variable in browsers (#341).
   509  assert = {
   510  	/**
   511  	 * Asserts rough true-ish result.
   512  	 * @name ok
   513  	 * @function
   514  	 * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
   515  	 */
   516  	ok: function( result, msg ) {
   517  		if ( !config.current ) {
   518  			throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
   519  		}
   520  		result = !!result;
   521  		msg = msg || (result ? "okay" : "failed" );
   522  
   523  		var source,
   524  			details = {
   525  				module: config.current.module,
   526  				name: config.current.testName,
   527  				result: result,
   528  				message: msg
   529  			};
   530  
   531  		msg = "<span class='test-message'>" + escapeText( msg ) + "</span>";
   532  
   533  		if ( !result ) {
   534  			source = sourceFromStacktrace( 2 );
   535  			if ( source ) {
   536  				details.source = source;
   537  				msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr></table>";
   538  			}
   539  		}
   540  		runLoggingCallbacks( "log", QUnit, details );
   541  		config.current.assertions.push({
   542  			result: result,
   543  			message: msg
   544  		});
   545  	},
   546  
   547  	/**
   548  	 * Assert that the first two arguments are equal, with an optional message.
   549  	 * Prints out both actual and expected values.
   550  	 * @name equal
   551  	 * @function
   552  	 * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
   553  	 */
   554  	equal: function( actual, expected, message ) {
   555  		/*jshint eqeqeq:false */
   556  		QUnit.push( expected == actual, actual, expected, message );
   557  	},
   558  
   559  	/**
   560  	 * @name notEqual
   561  	 * @function
   562  	 */
   563  	notEqual: function( actual, expected, message ) {
   564  		/*jshint eqeqeq:false */
   565  		QUnit.push( expected != actual, actual, expected, message );
   566  	},
   567  
   568  	/**
   569  	 * @name propEqual
   570  	 * @function
   571  	 */
   572  	propEqual: function( actual, expected, message ) {
   573  		actual = objectValues(actual);
   574  		expected = objectValues(expected);
   575  		QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
   576  	},
   577  
   578  	/**
   579  	 * @name notPropEqual
   580  	 * @function
   581  	 */
   582  	notPropEqual: function( actual, expected, message ) {
   583  		actual = objectValues(actual);
   584  		expected = objectValues(expected);
   585  		QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
   586  	},
   587  
   588  	/**
   589  	 * @name deepEqual
   590  	 * @function
   591  	 */
   592  	deepEqual: function( actual, expected, message ) {
   593  		QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
   594  	},
   595  
   596  	/**
   597  	 * @name notDeepEqual
   598  	 * @function
   599  	 */
   600  	notDeepEqual: function( actual, expected, message ) {
   601  		QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
   602  	},
   603  
   604  	/**
   605  	 * @name strictEqual
   606  	 * @function
   607  	 */
   608  	strictEqual: function( actual, expected, message ) {
   609  		QUnit.push( expected === actual, actual, expected, message );
   610  	},
   611  
   612  	/**
   613  	 * @name notStrictEqual
   614  	 * @function
   615  	 */
   616  	notStrictEqual: function( actual, expected, message ) {
   617  		QUnit.push( expected !== actual, actual, expected, message );
   618  	},
   619  
   620  	"throws": function( block, expected, message ) {
   621  		var actual,
   622  			expectedOutput = expected,
   623  			ok = false;
   624  
   625  		// 'expected' is optional
   626  		if ( typeof expected === "string" ) {
   627  			message = expected;
   628  			expected = null;
   629  		}
   630  
   631  		config.current.ignoreGlobalErrors = true;
   632  		try {
   633  			block.call( config.current.testEnvironment );
   634  		} catch (e) {
   635  			actual = e;
   636  		}
   637  		config.current.ignoreGlobalErrors = false;
   638  
   639  		if ( actual ) {
   640  			// we don't want to validate thrown error
   641  			if ( !expected ) {
   642  				ok = true;
   643  				expectedOutput = null;
   644  			// expected is a regexp
   645  			} else if ( QUnit.objectType( expected ) === "regexp" ) {
   646  				ok = expected.test( errorString( actual ) );
   647  			// expected is a constructor
   648  			} else if ( actual instanceof expected ) {
   649  				ok = true;
   650  			// expected is a validation function which returns true is validation passed
   651  			} else if ( expected.call( {}, actual ) === true ) {
   652  				expectedOutput = null;
   653  				ok = true;
   654  			}
   655  
   656  			QUnit.push( ok, actual, expectedOutput, message );
   657  		} else {
   658  			QUnit.pushFailure( message, null, "No exception was thrown." );
   659  		}
   660  	}
   661  };
   662  
   663  /**
   664   * @deprecated since 1.8.0
   665   * Kept assertion helpers in root for backwards compatibility.
   666   */
   667  extend( QUnit, assert );
   668  
   669  /**
   670   * @deprecated since 1.9.0
   671   * Kept root "raises()" for backwards compatibility.
   672   * (Note that we don't introduce assert.raises).
   673   */
   674  QUnit.raises = assert[ "throws" ];
   675  
   676  /**
   677   * @deprecated since 1.0.0, replaced with error pushes since 1.3.0
   678   * Kept to avoid TypeErrors for undefined methods.
   679   */
   680  QUnit.equals = function() {
   681  	QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
   682  };
   683  QUnit.same = function() {
   684  	QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
   685  };
   686  
   687  // We want access to the constructor's prototype
   688  (function() {
   689  	function F() {}
   690  	F.prototype = QUnit;
   691  	QUnit = new F();
   692  	// Make F QUnit's constructor so that we can add to the prototype later
   693  	QUnit.constructor = F;
   694  }());
   695  
   696  /**
   697   * Config object: Maintain internal state
   698   * Later exposed as QUnit.config
   699   * `config` initialized at top of scope
   700   */
   701  config = {
   702  	// The queue of tests to run
   703  	queue: [],
   704  
   705  	// block until document ready
   706  	blocking: true,
   707  
   708  	// when enabled, show only failing tests
   709  	// gets persisted through sessionStorage and can be changed in UI via checkbox
   710  	hidepassed: false,
   711  
   712  	// by default, run previously failed tests first
   713  	// very useful in combination with "Hide passed tests" checked
   714  	reorder: true,
   715  
   716  	// by default, modify document.title when suite is done
   717  	altertitle: true,
   718  
   719  	// when enabled, all tests must call expect()
   720  	requireExpects: false,
   721  
   722  	// add checkboxes that are persisted in the query-string
   723  	// when enabled, the id is set to `true` as a `QUnit.config` property
   724  	urlConfig: [
   725  		{
   726  			id: "noglobals",
   727  			label: "Check for Globals",
   728  			tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
   729  		},
   730  		{
   731  			id: "notrycatch",
   732  			label: "No try-catch",
   733  			tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
   734  		}
   735  	],
   736  
   737  	// Set of all modules.
   738  	modules: {},
   739  
   740  	// logging callback queues
   741  	begin: [],
   742  	done: [],
   743  	log: [],
   744  	testStart: [],
   745  	testDone: [],
   746  	moduleStart: [],
   747  	moduleDone: []
   748  };
   749  
   750  // Export global variables, unless an 'exports' object exists,
   751  // in that case we assume we're in CommonJS (dealt with on the bottom of the script)
   752  if ( typeof exports === "undefined" ) {
   753  	extend( window, QUnit.constructor.prototype );
   754  
   755  	// Expose QUnit object
   756  	window.QUnit = QUnit;
   757  }
   758  
   759  // Initialize more QUnit.config and QUnit.urlParams
   760  (function() {
   761  	var i,
   762  		location = window.location || { search: "", protocol: "file:" },
   763  		params = location.search.slice( 1 ).split( "&" ),
   764  		length = params.length,
   765  		urlParams = {},
   766  		current;
   767  
   768  	if ( params[ 0 ] ) {
   769  		for ( i = 0; i < length; i++ ) {
   770  			current = params[ i ].split( "=" );
   771  			current[ 0 ] = decodeURIComponent( current[ 0 ] );
   772  			// allow just a key to turn on a flag, e.g., test.html?noglobals
   773  			current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
   774  			urlParams[ current[ 0 ] ] = current[ 1 ];
   775  		}
   776  	}
   777  
   778  	QUnit.urlParams = urlParams;
   779  
   780  	// String search anywhere in moduleName+testName
   781  	config.filter = urlParams.filter;
   782  
   783  	// Exact match of the module name
   784  	config.module = urlParams.module;
   785  
   786  	config.testNumber = parseInt( urlParams.testNumber, 10 ) || null;
   787  
   788  	// Figure out if we're running the tests from a server or not
   789  	QUnit.isLocal = location.protocol === "file:";
   790  }());
   791  
   792  // Extend QUnit object,
   793  // these after set here because they should not be exposed as global functions
   794  extend( QUnit, {
   795  	assert: assert,
   796  
   797  	config: config,
   798  
   799  	// Initialize the configuration options
   800  	init: function() {
   801  		extend( config, {
   802  			stats: { all: 0, bad: 0 },
   803  			moduleStats: { all: 0, bad: 0 },
   804  			started: +new Date(),
   805  			updateRate: 1000,
   806  			blocking: false,
   807  			autostart: true,
   808  			autorun: false,
   809  			filter: "",
   810  			queue: [],
   811  			semaphore: 1
   812  		});
   813  
   814  		var tests, banner, result,
   815  			qunit = id( "qunit" );
   816  
   817  		if ( qunit ) {
   818  			qunit.innerHTML =
   819  				"<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
   820  				"<h2 id='qunit-banner'></h2>" +
   821  				"<div id='qunit-testrunner-toolbar'></div>" +
   822  				"<h2 id='qunit-userAgent'></h2>" +
   823  				"<ol id='qunit-tests'></ol>";
   824  		}
   825  
   826  		tests = id( "qunit-tests" );
   827  		banner = id( "qunit-banner" );
   828  		result = id( "qunit-testresult" );
   829  
   830  		if ( tests ) {
   831  			tests.innerHTML = "";
   832  		}
   833  
   834  		if ( banner ) {
   835  			banner.className = "";
   836  		}
   837  
   838  		if ( result ) {
   839  			result.parentNode.removeChild( result );
   840  		}
   841  
   842  		if ( tests ) {
   843  			result = document.createElement( "p" );
   844  			result.id = "qunit-testresult";
   845  			result.className = "result";
   846  			tests.parentNode.insertBefore( result, tests );
   847  			result.innerHTML = "Running...<br/>&nbsp;";
   848  		}
   849  	},
   850  
   851  	// Resets the test setup. Useful for tests that modify the DOM.
   852  	/*
   853  	DEPRECATED: Use multiple tests instead of resetting inside a test.
   854  	Use testStart or testDone for custom cleanup.
   855  	This method will throw an error in 2.0, and will be removed in 2.1
   856  	*/
   857  	reset: function() {
   858  		var fixture = id( "qunit-fixture" );
   859  		if ( fixture ) {
   860  			fixture.innerHTML = config.fixture;
   861  		}
   862  	},
   863  
   864  	// Trigger an event on an element.
   865  	// @example triggerEvent( document.body, "click" );
   866  	triggerEvent: function( elem, type, event ) {
   867  		if ( document.createEvent ) {
   868  			event = document.createEvent( "MouseEvents" );
   869  			event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
   870  				0, 0, 0, 0, 0, false, false, false, false, 0, null);
   871  
   872  			elem.dispatchEvent( event );
   873  		} else if ( elem.fireEvent ) {
   874  			elem.fireEvent( "on" + type );
   875  		}
   876  	},
   877  
   878  	// Safe object type checking
   879  	is: function( type, obj ) {
   880  		return QUnit.objectType( obj ) === type;
   881  	},
   882  
   883  	objectType: function( obj ) {
   884  		if ( typeof obj === "undefined" ) {
   885  				return "undefined";
   886  		// consider: typeof null === object
   887  		}
   888  		if ( obj === null ) {
   889  				return "null";
   890  		}
   891  
   892  		var match = toString.call( obj ).match(/^\[object\s(.*)\]$/),
   893  			type = match && match[1] || "";
   894  
   895  		switch ( type ) {
   896  			case "Number":
   897  				if ( isNaN(obj) ) {
   898  					return "nan";
   899  				}
   900  				return "number";
   901  			case "String":
   902  			case "Boolean":
   903  			case "Array":
   904  			case "Date":
   905  			case "RegExp":
   906  			case "Function":
   907  				return type.toLowerCase();
   908  		}
   909  		if ( typeof obj === "object" ) {
   910  			return "object";
   911  		}
   912  		return undefined;
   913  	},
   914  
   915  	push: function( result, actual, expected, message ) {
   916  		if ( !config.current ) {
   917  			throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );
   918  		}
   919  
   920  		var output, source,
   921  			details = {
   922  				module: config.current.module,
   923  				name: config.current.testName,
   924  				result: result,
   925  				message: message,
   926  				actual: actual,
   927  				expected: expected
   928  			};
   929  
   930  		message = escapeText( message ) || ( result ? "okay" : "failed" );
   931  		message = "<span class='test-message'>" + message + "</span>";
   932  		output = message;
   933  
   934  		if ( !result ) {
   935  			expected = escapeText( QUnit.jsDump.parse(expected) );
   936  			actual = escapeText( QUnit.jsDump.parse(actual) );
   937  			output += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + expected + "</pre></td></tr>";
   938  
   939  			if ( actual !== expected ) {
   940  				output += "<tr class='test-actual'><th>Result: </th><td><pre>" + actual + "</pre></td></tr>";
   941  				output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>";
   942  			}
   943  
   944  			source = sourceFromStacktrace();
   945  
   946  			if ( source ) {
   947  				details.source = source;
   948  				output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";
   949  			}
   950  
   951  			output += "</table>";
   952  		}
   953  
   954  		runLoggingCallbacks( "log", QUnit, details );
   955  
   956  		config.current.assertions.push({
   957  			result: !!result,
   958  			message: output
   959  		});
   960  	},
   961  
   962  	pushFailure: function( message, source, actual ) {
   963  		if ( !config.current ) {
   964  			throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );
   965  		}
   966  
   967  		var output,
   968  			details = {
   969  				module: config.current.module,
   970  				name: config.current.testName,
   971  				result: false,
   972  				message: message
   973  			};
   974  
   975  		message = escapeText( message ) || "error";
   976  		message = "<span class='test-message'>" + message + "</span>";
   977  		output = message;
   978  
   979  		output += "<table>";
   980  
   981  		if ( actual ) {
   982  			output += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText( actual ) + "</pre></td></tr>";
   983  		}
   984  
   985  		if ( source ) {
   986  			details.source = source;
   987  			output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";
   988  		}
   989  
   990  		output += "</table>";
   991  
   992  		runLoggingCallbacks( "log", QUnit, details );
   993  
   994  		config.current.assertions.push({
   995  			result: false,
   996  			message: output
   997  		});
   998  	},
   999  
  1000  	url: function( params ) {
  1001  		params = extend( extend( {}, QUnit.urlParams ), params );
  1002  		var key,
  1003  			querystring = "?";
  1004  
  1005  		for ( key in params ) {
  1006  			if ( hasOwn.call( params, key ) ) {
  1007  				querystring += encodeURIComponent( key ) + "=" +
  1008  					encodeURIComponent( params[ key ] ) + "&";
  1009  			}
  1010  		}
  1011  		return window.location.protocol + "//" + window.location.host +
  1012  			window.location.pathname + querystring.slice( 0, -1 );
  1013  	},
  1014  
  1015  	extend: extend,
  1016  	id: id,
  1017  	addEvent: addEvent,
  1018  	addClass: addClass,
  1019  	hasClass: hasClass,
  1020  	removeClass: removeClass
  1021  	// load, equiv, jsDump, diff: Attached later
  1022  });
  1023  
  1024  /**
  1025   * @deprecated: Created for backwards compatibility with test runner that set the hook function
  1026   * into QUnit.{hook}, instead of invoking it and passing the hook function.
  1027   * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
  1028   * Doing this allows us to tell if the following methods have been overwritten on the actual
  1029   * QUnit object.
  1030   */
  1031  extend( QUnit.constructor.prototype, {
  1032  
  1033  	// Logging callbacks; all receive a single argument with the listed properties
  1034  	// run test/logs.html for any related changes
  1035  	begin: registerLoggingCallback( "begin" ),
  1036  
  1037  	// done: { failed, passed, total, runtime }
  1038  	done: registerLoggingCallback( "done" ),
  1039  
  1040  	// log: { result, actual, expected, message }
  1041  	log: registerLoggingCallback( "log" ),
  1042  
  1043  	// testStart: { name }
  1044  	testStart: registerLoggingCallback( "testStart" ),
  1045  
  1046  	// testDone: { name, failed, passed, total, duration }
  1047  	testDone: registerLoggingCallback( "testDone" ),
  1048  
  1049  	// moduleStart: { name }
  1050  	moduleStart: registerLoggingCallback( "moduleStart" ),
  1051  
  1052  	// moduleDone: { name, failed, passed, total }
  1053  	moduleDone: registerLoggingCallback( "moduleDone" )
  1054  });
  1055  
  1056  if ( typeof document === "undefined" || document.readyState === "complete" ) {
  1057  	config.autorun = true;
  1058  }
  1059  
  1060  QUnit.load = function() {
  1061  	runLoggingCallbacks( "begin", QUnit, {} );
  1062  
  1063  	// Initialize the config, saving the execution queue
  1064  	var banner, filter, i, label, len, main, ol, toolbar, userAgent, val,
  1065  		urlConfigCheckboxesContainer, urlConfigCheckboxes, moduleFilter,
  1066  		numModules = 0,
  1067  		moduleNames = [],
  1068  		moduleFilterHtml = "",
  1069  		urlConfigHtml = "",
  1070  		oldconfig = extend( {}, config );
  1071  
  1072  	QUnit.init();
  1073  	extend(config, oldconfig);
  1074  
  1075  	config.blocking = false;
  1076  
  1077  	len = config.urlConfig.length;
  1078  
  1079  	for ( i = 0; i < len; i++ ) {
  1080  		val = config.urlConfig[i];
  1081  		if ( typeof val === "string" ) {
  1082  			val = {
  1083  				id: val,
  1084  				label: val,
  1085  				tooltip: "[no tooltip available]"
  1086  			};
  1087  		}
  1088  		config[ val.id ] = QUnit.urlParams[ val.id ];
  1089  		urlConfigHtml += "<input id='qunit-urlconfig-" + escapeText( val.id ) +
  1090  			"' name='" + escapeText( val.id ) +
  1091  			"' type='checkbox'" + ( config[ val.id ] ? " checked='checked'" : "" ) +
  1092  			" title='" + escapeText( val.tooltip ) +
  1093  			"'><label for='qunit-urlconfig-" + escapeText( val.id ) +
  1094  			"' title='" + escapeText( val.tooltip ) + "'>" + val.label + "</label>";
  1095  	}
  1096  	for ( i in config.modules ) {
  1097  		if ( config.modules.hasOwnProperty( i ) ) {
  1098  			moduleNames.push(i);
  1099  		}
  1100  	}
  1101  	numModules = moduleNames.length;
  1102  	moduleNames.sort( function( a, b ) {
  1103  		return a.localeCompare( b );
  1104  	});
  1105  	moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label><select id='qunit-modulefilter' name='modulefilter'><option value='' " +
  1106  		( config.module === undefined  ? "selected='selected'" : "" ) +
  1107  		">< All Modules ></option>";
  1108  
  1109  
  1110  	for ( i = 0; i < numModules; i++) {
  1111  			moduleFilterHtml += "<option value='" + escapeText( encodeURIComponent(moduleNames[i]) ) + "' " +
  1112  				( config.module === moduleNames[i] ? "selected='selected'" : "" ) +
  1113  				">" + escapeText(moduleNames[i]) + "</option>";
  1114  	}
  1115  	moduleFilterHtml += "</select>";
  1116  
  1117  	// `userAgent` initialized at top of scope
  1118  	userAgent = id( "qunit-userAgent" );
  1119  	if ( userAgent ) {
  1120  		userAgent.innerHTML = navigator.userAgent;
  1121  	}
  1122  
  1123  	// `banner` initialized at top of scope
  1124  	banner = id( "qunit-header" );
  1125  	if ( banner ) {
  1126  		banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) + "'>" + banner.innerHTML + "</a> ";
  1127  	}
  1128  
  1129  	// `toolbar` initialized at top of scope
  1130  	toolbar = id( "qunit-testrunner-toolbar" );
  1131  	if ( toolbar ) {
  1132  		// `filter` initialized at top of scope
  1133  		filter = document.createElement( "input" );
  1134  		filter.type = "checkbox";
  1135  		filter.id = "qunit-filter-pass";
  1136  
  1137  		addEvent( filter, "click", function() {
  1138  			var tmp,
  1139  				ol = document.getElementById( "qunit-tests" );
  1140  
  1141  			if ( filter.checked ) {
  1142  				ol.className = ol.className + " hidepass";
  1143  			} else {
  1144  				tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
  1145  				ol.className = tmp.replace( / hidepass /, " " );
  1146  			}
  1147  			if ( defined.sessionStorage ) {
  1148  				if (filter.checked) {
  1149  					sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
  1150  				} else {
  1151  					sessionStorage.removeItem( "qunit-filter-passed-tests" );
  1152  				}
  1153  			}
  1154  		});
  1155  
  1156  		if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
  1157  			filter.checked = true;
  1158  			// `ol` initialized at top of scope
  1159  			ol = document.getElementById( "qunit-tests" );
  1160  			ol.className = ol.className + " hidepass";
  1161  		}
  1162  		toolbar.appendChild( filter );
  1163  
  1164  		// `label` initialized at top of scope
  1165  		label = document.createElement( "label" );
  1166  		label.setAttribute( "for", "qunit-filter-pass" );
  1167  		label.setAttribute( "title", "Only show tests and assertions that fail. Stored in sessionStorage." );
  1168  		label.innerHTML = "Hide passed tests";
  1169  		toolbar.appendChild( label );
  1170  
  1171  		urlConfigCheckboxesContainer = document.createElement("span");
  1172  		urlConfigCheckboxesContainer.innerHTML = urlConfigHtml;
  1173  		urlConfigCheckboxes = urlConfigCheckboxesContainer.getElementsByTagName("input");
  1174  		// For oldIE support:
  1175  		// * Add handlers to the individual elements instead of the container
  1176  		// * Use "click" instead of "change"
  1177  		// * Fallback from event.target to event.srcElement
  1178  		addEvents( urlConfigCheckboxes, "click", function( event ) {
  1179  			var params = {},
  1180  				target = event.target || event.srcElement;
  1181  			params[ target.name ] = target.checked ? true : undefined;
  1182  			window.location = QUnit.url( params );
  1183  		});
  1184  		toolbar.appendChild( urlConfigCheckboxesContainer );
  1185  
  1186  		if (numModules > 1) {
  1187  			moduleFilter = document.createElement( "span" );
  1188  			moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
  1189  			moduleFilter.innerHTML = moduleFilterHtml;
  1190  			addEvent( moduleFilter.lastChild, "change", function() {
  1191  				var selectBox = moduleFilter.getElementsByTagName("select")[0],
  1192  					selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value);
  1193  
  1194  				window.location = QUnit.url({
  1195  					module: ( selectedModule === "" ) ? undefined : selectedModule,
  1196  					// Remove any existing filters
  1197  					filter: undefined,
  1198  					testNumber: undefined
  1199  				});
  1200  			});
  1201  			toolbar.appendChild(moduleFilter);
  1202  		}
  1203  	}
  1204  
  1205  	// `main` initialized at top of scope
  1206  	main = id( "qunit-fixture" );
  1207  	if ( main ) {
  1208  		config.fixture = main.innerHTML;
  1209  	}
  1210  
  1211  	if ( config.autostart ) {
  1212  		QUnit.start();
  1213  	}
  1214  };
  1215  
  1216  addEvent( window, "load", QUnit.load );
  1217  
  1218  // `onErrorFnPrev` initialized at top of scope
  1219  // Preserve other handlers
  1220  onErrorFnPrev = window.onerror;
  1221  
  1222  // Cover uncaught exceptions
  1223  // Returning true will suppress the default browser handler,
  1224  // returning false will let it run.
  1225  window.onerror = function ( error, filePath, linerNr ) {
  1226  	var ret = false;
  1227  	if ( onErrorFnPrev ) {
  1228  		ret = onErrorFnPrev( error, filePath, linerNr );
  1229  	}
  1230  
  1231  	// Treat return value as window.onerror itself does,
  1232  	// Only do our handling if not suppressed.
  1233  	if ( ret !== true ) {
  1234  		if ( QUnit.config.current ) {
  1235  			if ( QUnit.config.current.ignoreGlobalErrors ) {
  1236  				return true;
  1237  			}
  1238  			QUnit.pushFailure( error, filePath + ":" + linerNr );
  1239  		} else {
  1240  			QUnit.test( "global failure", extend( function() {
  1241  				QUnit.pushFailure( error, filePath + ":" + linerNr );
  1242  			}, { validTest: validTest } ) );
  1243  		}
  1244  		return false;
  1245  	}
  1246  
  1247  	return ret;
  1248  };
  1249  
  1250  function done() {
  1251  	config.autorun = true;
  1252  
  1253  	// Log the last module results
  1254  	if ( config.currentModule ) {
  1255  		runLoggingCallbacks( "moduleDone", QUnit, {
  1256  			name: config.currentModule,
  1257  			failed: config.moduleStats.bad,
  1258  			passed: config.moduleStats.all - config.moduleStats.bad,
  1259  			total: config.moduleStats.all
  1260  		});
  1261  	}
  1262  	delete config.previousModule;
  1263  
  1264  	var i, key,
  1265  		banner = id( "qunit-banner" ),
  1266  		tests = id( "qunit-tests" ),
  1267  		runtime = +new Date() - config.started,
  1268  		passed = config.stats.all - config.stats.bad,
  1269  		html = [
  1270  			"Tests completed in ",
  1271  			runtime,
  1272  			" milliseconds.<br/>",
  1273  			"<span class='passed'>",
  1274  			passed,
  1275  			"</span> assertions of <span class='total'>",
  1276  			config.stats.all,
  1277  			"</span> passed, <span class='failed'>",
  1278  			config.stats.bad,
  1279  			"</span> failed."
  1280  		].join( "" );
  1281  
  1282  	if ( banner ) {
  1283  		banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );
  1284  	}
  1285  
  1286  	if ( tests ) {
  1287  		id( "qunit-testresult" ).innerHTML = html;
  1288  	}
  1289  
  1290  	if ( config.altertitle && typeof document !== "undefined" && document.title ) {
  1291  		// show ✖ for good, ✔ for bad suite result in title
  1292  		// use escape sequences in case file gets loaded with non-utf-8-charset
  1293  		document.title = [
  1294  			( config.stats.bad ? "\u2716" : "\u2714" ),
  1295  			document.title.replace( /^[\u2714\u2716] /i, "" )
  1296  		].join( " " );
  1297  	}
  1298  
  1299  	// clear own sessionStorage items if all tests passed
  1300  	if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {
  1301  		// `key` & `i` initialized at top of scope
  1302  		for ( i = 0; i < sessionStorage.length; i++ ) {
  1303  			key = sessionStorage.key( i++ );
  1304  			if ( key.indexOf( "qunit-test-" ) === 0 ) {
  1305  				sessionStorage.removeItem( key );
  1306  			}
  1307  		}
  1308  	}
  1309  
  1310  	// scroll back to top to show results
  1311  	if ( window.scrollTo ) {
  1312  		window.scrollTo(0, 0);
  1313  	}
  1314  
  1315  	runLoggingCallbacks( "done", QUnit, {
  1316  		failed: config.stats.bad,
  1317  		passed: passed,
  1318  		total: config.stats.all,
  1319  		runtime: runtime
  1320  	});
  1321  }
  1322  
  1323  /** @return Boolean: true if this test should be ran */
  1324  function validTest( test ) {
  1325  	var include,
  1326  		filter = config.filter && config.filter.toLowerCase(),
  1327  		module = config.module && config.module.toLowerCase(),
  1328  		fullName = (test.module + ": " + test.testName).toLowerCase();
  1329  
  1330  	// Internally-generated tests are always valid
  1331  	if ( test.callback && test.callback.validTest === validTest ) {
  1332  		delete test.callback.validTest;
  1333  		return true;
  1334  	}
  1335  
  1336  	if ( config.testNumber ) {
  1337  		return test.testNumber === config.testNumber;
  1338  	}
  1339  
  1340  	if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {
  1341  		return false;
  1342  	}
  1343  
  1344  	if ( !filter ) {
  1345  		return true;
  1346  	}
  1347  
  1348  	include = filter.charAt( 0 ) !== "!";
  1349  	if ( !include ) {
  1350  		filter = filter.slice( 1 );
  1351  	}
  1352  
  1353  	// If the filter matches, we need to honour include
  1354  	if ( fullName.indexOf( filter ) !== -1 ) {
  1355  		return include;
  1356  	}
  1357  
  1358  	// Otherwise, do the opposite
  1359  	return !include;
  1360  }
  1361  
  1362  // so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)
  1363  // Later Safari and IE10 are supposed to support error.stack as well
  1364  // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
  1365  function extractStacktrace( e, offset ) {
  1366  	offset = offset === undefined ? 3 : offset;
  1367  
  1368  	var stack, include, i;
  1369  
  1370  	if ( e.stacktrace ) {
  1371  		// Opera
  1372  		return e.stacktrace.split( "\n" )[ offset + 3 ];
  1373  	} else if ( e.stack ) {
  1374  		// Firefox, Chrome
  1375  		stack = e.stack.split( "\n" );
  1376  		if (/^error$/i.test( stack[0] ) ) {
  1377  			stack.shift();
  1378  		}
  1379  		if ( fileName ) {
  1380  			include = [];
  1381  			for ( i = offset; i < stack.length; i++ ) {
  1382  				if ( stack[ i ].indexOf( fileName ) !== -1 ) {
  1383  					break;
  1384  				}
  1385  				include.push( stack[ i ] );
  1386  			}
  1387  			if ( include.length ) {
  1388  				return include.join( "\n" );
  1389  			}
  1390  		}
  1391  		return stack[ offset ];
  1392  	} else if ( e.sourceURL ) {
  1393  		// Safari, PhantomJS
  1394  		// hopefully one day Safari provides actual stacktraces
  1395  		// exclude useless self-reference for generated Error objects
  1396  		if ( /qunit.js$/.test( e.sourceURL ) ) {
  1397  			return;
  1398  		}
  1399  		// for actual exceptions, this is useful
  1400  		return e.sourceURL + ":" + e.line;
  1401  	}
  1402  }
  1403  function sourceFromStacktrace( offset ) {
  1404  	try {
  1405  		throw new Error();
  1406  	} catch ( e ) {
  1407  		return extractStacktrace( e, offset );
  1408  	}
  1409  }
  1410  
  1411  /**
  1412   * Escape text for attribute or text content.
  1413   */
  1414  function escapeText( s ) {
  1415  	if ( !s ) {
  1416  		return "";
  1417  	}
  1418  	s = s + "";
  1419  	// Both single quotes and double quotes (for attributes)
  1420  	return s.replace( /['"<>&]/g, function( s ) {
  1421  		switch( s ) {
  1422  			case "'":
  1423  				return "&#039;";
  1424  			case "\"":
  1425  				return "&quot;";
  1426  			case "<":
  1427  				return "&lt;";
  1428  			case ">":
  1429  				return "&gt;";
  1430  			case "&":
  1431  				return "&amp;";
  1432  		}
  1433  	});
  1434  }
  1435  
  1436  function synchronize( callback, last ) {
  1437  	config.queue.push( callback );
  1438  
  1439  	if ( config.autorun && !config.blocking ) {
  1440  		process( last );
  1441  	}
  1442  }
  1443  
  1444  function process( last ) {
  1445  	function next() {
  1446  		process( last );
  1447  	}
  1448  	var start = new Date().getTime();
  1449  	config.depth = config.depth ? config.depth + 1 : 1;
  1450  
  1451  	while ( config.queue.length && !config.blocking ) {
  1452  		if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
  1453  			config.queue.shift()();
  1454  		} else {
  1455  			setTimeout( next, 13 );
  1456  			break;
  1457  		}
  1458  	}
  1459  	config.depth--;
  1460  	if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
  1461  		done();
  1462  	}
  1463  }
  1464  
  1465  function saveGlobal() {
  1466  	config.pollution = [];
  1467  
  1468  	if ( config.noglobals ) {
  1469  		for ( var key in window ) {
  1470  			if ( hasOwn.call( window, key ) ) {
  1471  				// in Opera sometimes DOM element ids show up here, ignore them
  1472  				if ( /^qunit-test-output/.test( key ) ) {
  1473  					continue;
  1474  				}
  1475  				config.pollution.push( key );
  1476  			}
  1477  		}
  1478  	}
  1479  }
  1480  
  1481  function checkPollution() {
  1482  	var newGlobals,
  1483  		deletedGlobals,
  1484  		old = config.pollution;
  1485  
  1486  	saveGlobal();
  1487  
  1488  	newGlobals = diff( config.pollution, old );
  1489  	if ( newGlobals.length > 0 ) {
  1490  		QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );
  1491  	}
  1492  
  1493  	deletedGlobals = diff( old, config.pollution );
  1494  	if ( deletedGlobals.length > 0 ) {
  1495  		QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );
  1496  	}
  1497  }
  1498  
  1499  // returns a new Array with the elements that are in a but not in b
  1500  function diff( a, b ) {
  1501  	var i, j,
  1502  		result = a.slice();
  1503  
  1504  	for ( i = 0; i < result.length; i++ ) {
  1505  		for ( j = 0; j < b.length; j++ ) {
  1506  			if ( result[i] === b[j] ) {
  1507  				result.splice( i, 1 );
  1508  				i--;
  1509  				break;
  1510  			}
  1511  		}
  1512  	}
  1513  	return result;
  1514  }
  1515  
  1516  function extend( a, b ) {
  1517  	for ( var prop in b ) {
  1518  		if ( hasOwn.call( b, prop ) ) {
  1519  			// Avoid "Member not found" error in IE8 caused by messing with window.constructor
  1520  			if ( !( prop === "constructor" && a === window ) ) {
  1521  				if ( b[ prop ] === undefined ) {
  1522  					delete a[ prop ];
  1523  				} else {
  1524  					a[ prop ] = b[ prop ];
  1525  				}
  1526  			}
  1527  		}
  1528  	}
  1529  
  1530  	return a;
  1531  }
  1532  
  1533  /**
  1534   * @param {HTMLElement} elem
  1535   * @param {string} type
  1536   * @param {Function} fn
  1537   */
  1538  function addEvent( elem, type, fn ) {
  1539  	// Standards-based browsers
  1540  	if ( elem.addEventListener ) {
  1541  		elem.addEventListener( type, fn, false );
  1542  	// IE
  1543  	} else {
  1544  		elem.attachEvent( "on" + type, fn );
  1545  	}
  1546  }
  1547  
  1548  /**
  1549   * @param {Array|NodeList} elems
  1550   * @param {string} type
  1551   * @param {Function} fn
  1552   */
  1553  function addEvents( elems, type, fn ) {
  1554  	var i = elems.length;
  1555  	while ( i-- ) {
  1556  		addEvent( elems[i], type, fn );
  1557  	}
  1558  }
  1559  
  1560  function hasClass( elem, name ) {
  1561  	return (" " + elem.className + " ").indexOf(" " + name + " ") > -1;
  1562  }
  1563  
  1564  function addClass( elem, name ) {
  1565  	if ( !hasClass( elem, name ) ) {
  1566  		elem.className += (elem.className ? " " : "") + name;
  1567  	}
  1568  }
  1569  
  1570  function removeClass( elem, name ) {
  1571  	var set = " " + elem.className + " ";
  1572  	// Class name may appear multiple times
  1573  	while ( set.indexOf(" " + name + " ") > -1 ) {
  1574  		set = set.replace(" " + name + " " , " ");
  1575  	}
  1576  	// If possible, trim it for prettiness, but not necessarily
  1577  	elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
  1578  }
  1579  
  1580  function id( name ) {
  1581  	return !!( typeof document !== "undefined" && document && document.getElementById ) &&
  1582  		document.getElementById( name );
  1583  }
  1584  
  1585  function registerLoggingCallback( key ) {
  1586  	return function( callback ) {
  1587  		config[key].push( callback );
  1588  	};
  1589  }
  1590  
  1591  // Supports deprecated method of completely overwriting logging callbacks
  1592  function runLoggingCallbacks( key, scope, args ) {
  1593  	var i, callbacks;
  1594  	if ( QUnit.hasOwnProperty( key ) ) {
  1595  		QUnit[ key ].call(scope, args );
  1596  	} else {
  1597  		callbacks = config[ key ];
  1598  		for ( i = 0; i < callbacks.length; i++ ) {
  1599  			callbacks[ i ].call( scope, args );
  1600  		}
  1601  	}
  1602  }
  1603  
  1604  // Test for equality any JavaScript type.
  1605  // Author: Philippe Rathé <prathe@gmail.com>
  1606  QUnit.equiv = (function() {
  1607  
  1608  	// Call the o related callback with the given arguments.
  1609  	function bindCallbacks( o, callbacks, args ) {
  1610  		var prop = QUnit.objectType( o );
  1611  		if ( prop ) {
  1612  			if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
  1613  				return callbacks[ prop ].apply( callbacks, args );
  1614  			} else {
  1615  				return callbacks[ prop ]; // or undefined
  1616  			}
  1617  		}
  1618  	}
  1619  
  1620  	// the real equiv function
  1621  	var innerEquiv,
  1622  		// stack to decide between skip/abort functions
  1623  		callers = [],
  1624  		// stack to avoiding loops from circular referencing
  1625  		parents = [],
  1626  		parentsB = [],
  1627  
  1628  		getProto = Object.getPrototypeOf || function ( obj ) {
  1629  			/*jshint camelcase:false */
  1630  			return obj.__proto__;
  1631  		},
  1632  		callbacks = (function () {
  1633  
  1634  			// for string, boolean, number and null
  1635  			function useStrictEquality( b, a ) {
  1636  				/*jshint eqeqeq:false */
  1637  				if ( b instanceof a.constructor || a instanceof b.constructor ) {
  1638  					// to catch short annotation VS 'new' annotation of a
  1639  					// declaration
  1640  					// e.g. var i = 1;
  1641  					// var j = new Number(1);
  1642  					return a == b;
  1643  				} else {
  1644  					return a === b;
  1645  				}
  1646  			}
  1647  
  1648  			return {
  1649  				"string": useStrictEquality,
  1650  				"boolean": useStrictEquality,
  1651  				"number": useStrictEquality,
  1652  				"null": useStrictEquality,
  1653  				"undefined": useStrictEquality,
  1654  
  1655  				"nan": function( b ) {
  1656  					return isNaN( b );
  1657  				},
  1658  
  1659  				"date": function( b, a ) {
  1660  					return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
  1661  				},
  1662  
  1663  				"regexp": function( b, a ) {
  1664  					return QUnit.objectType( b ) === "regexp" &&
  1665  						// the regex itself
  1666  						a.source === b.source &&
  1667  						// and its modifiers
  1668  						a.global === b.global &&
  1669  						// (gmi) ...
  1670  						a.ignoreCase === b.ignoreCase &&
  1671  						a.multiline === b.multiline &&
  1672  						a.sticky === b.sticky;
  1673  				},
  1674  
  1675  				// - skip when the property is a method of an instance (OOP)
  1676  				// - abort otherwise,
  1677  				// initial === would have catch identical references anyway
  1678  				"function": function() {
  1679  					var caller = callers[callers.length - 1];
  1680  					return caller !== Object && typeof caller !== "undefined";
  1681  				},
  1682  
  1683  				"array": function( b, a ) {
  1684  					var i, j, len, loop, aCircular, bCircular;
  1685  
  1686  					// b could be an object literal here
  1687  					if ( QUnit.objectType( b ) !== "array" ) {
  1688  						return false;
  1689  					}
  1690  
  1691  					len = a.length;
  1692  					if ( len !== b.length ) {
  1693  						// safe and faster
  1694  						return false;
  1695  					}
  1696  
  1697  					// track reference to avoid circular references
  1698  					parents.push( a );
  1699  					parentsB.push( b );
  1700  					for ( i = 0; i < len; i++ ) {
  1701  						loop = false;
  1702  						for ( j = 0; j < parents.length; j++ ) {
  1703  							aCircular = parents[j] === a[i];
  1704  							bCircular = parentsB[j] === b[i];
  1705  							if ( aCircular || bCircular ) {
  1706  								if ( a[i] === b[i] || aCircular && bCircular ) {
  1707  									loop = true;
  1708  								} else {
  1709  									parents.pop();
  1710  									parentsB.pop();
  1711  									return false;
  1712  								}
  1713  							}
  1714  						}
  1715  						if ( !loop && !innerEquiv(a[i], b[i]) ) {
  1716  							parents.pop();
  1717  							parentsB.pop();
  1718  							return false;
  1719  						}
  1720  					}
  1721  					parents.pop();
  1722  					parentsB.pop();
  1723  					return true;
  1724  				},
  1725  
  1726  				"object": function( b, a ) {
  1727  					/*jshint forin:false */
  1728  					var i, j, loop, aCircular, bCircular,
  1729  						// Default to true
  1730  						eq = true,
  1731  						aProperties = [],
  1732  						bProperties = [];
  1733  
  1734  					// comparing constructors is more strict than using
  1735  					// instanceof
  1736  					if ( a.constructor !== b.constructor ) {
  1737  						// Allow objects with no prototype to be equivalent to
  1738  						// objects with Object as their constructor.
  1739  						if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||
  1740  							( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {
  1741  								return false;
  1742  						}
  1743  					}
  1744  
  1745  					// stack constructor before traversing properties
  1746  					callers.push( a.constructor );
  1747  
  1748  					// track reference to avoid circular references
  1749  					parents.push( a );
  1750  					parentsB.push( b );
  1751  
  1752  					// be strict: don't ensure hasOwnProperty and go deep
  1753  					for ( i in a ) {
  1754  						loop = false;
  1755  						for ( j = 0; j < parents.length; j++ ) {
  1756  							aCircular = parents[j] === a[i];
  1757  							bCircular = parentsB[j] === b[i];
  1758  							if ( aCircular || bCircular ) {
  1759  								if ( a[i] === b[i] || aCircular && bCircular ) {
  1760  									loop = true;
  1761  								} else {
  1762  									eq = false;
  1763  									break;
  1764  								}
  1765  							}
  1766  						}
  1767  						aProperties.push(i);
  1768  						if ( !loop && !innerEquiv(a[i], b[i]) ) {
  1769  							eq = false;
  1770  							break;
  1771  						}
  1772  					}
  1773  
  1774  					parents.pop();
  1775  					parentsB.pop();
  1776  					callers.pop(); // unstack, we are done
  1777  
  1778  					for ( i in b ) {
  1779  						bProperties.push( i ); // collect b's properties
  1780  					}
  1781  
  1782  					// Ensures identical properties name
  1783  					return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
  1784  				}
  1785  			};
  1786  		}());
  1787  
  1788  	innerEquiv = function() { // can take multiple arguments
  1789  		var args = [].slice.apply( arguments );
  1790  		if ( args.length < 2 ) {
  1791  			return true; // end transition
  1792  		}
  1793  
  1794  		return (function( a, b ) {
  1795  			if ( a === b ) {
  1796  				return true; // catch the most you can
  1797  			} else if ( a === null || b === null || typeof a === "undefined" ||
  1798  					typeof b === "undefined" ||
  1799  					QUnit.objectType(a) !== QUnit.objectType(b) ) {
  1800  				return false; // don't lose time with error prone cases
  1801  			} else {
  1802  				return bindCallbacks(a, callbacks, [ b, a ]);
  1803  			}
  1804  
  1805  			// apply transition with (1..n) arguments
  1806  		}( args[0], args[1] ) && innerEquiv.apply( this, args.splice(1, args.length - 1 )) );
  1807  	};
  1808  
  1809  	return innerEquiv;
  1810  }());
  1811  
  1812  /**
  1813   * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
  1814   * http://flesler.blogspot.com Licensed under BSD
  1815   * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
  1816   *
  1817   * @projectDescription Advanced and extensible data dumping for Javascript.
  1818   * @version 1.0.0
  1819   * @author Ariel Flesler
  1820   * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
  1821   */
  1822  QUnit.jsDump = (function() {
  1823  	function quote( str ) {
  1824  		return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";
  1825  	}
  1826  	function literal( o ) {
  1827  		return o + "";
  1828  	}
  1829  	function join( pre, arr, post ) {
  1830  		var s = jsDump.separator(),
  1831  			base = jsDump.indent(),
  1832  			inner = jsDump.indent(1);
  1833  		if ( arr.join ) {
  1834  			arr = arr.join( "," + s + inner );
  1835  		}
  1836  		if ( !arr ) {
  1837  			return pre + post;
  1838  		}
  1839  		return [ pre, inner + arr, base + post ].join(s);
  1840  	}
  1841  	function array( arr, stack ) {
  1842  		var i = arr.length, ret = new Array(i);
  1843  		this.up();
  1844  		while ( i-- ) {
  1845  			ret[i] = this.parse( arr[i] , undefined , stack);
  1846  		}
  1847  		this.down();
  1848  		return join( "[", ret, "]" );
  1849  	}
  1850  
  1851  	var reName = /^function (\w+)/,
  1852  		jsDump = {
  1853  			// type is used mostly internally, you can fix a (custom)type in advance
  1854  			parse: function( obj, type, stack ) {
  1855  				stack = stack || [ ];
  1856  				var inStack, res,
  1857  					parser = this.parsers[ type || this.typeOf(obj) ];
  1858  
  1859  				type = typeof parser;
  1860  				inStack = inArray( obj, stack );
  1861  
  1862  				if ( inStack !== -1 ) {
  1863  					return "recursion(" + (inStack - stack.length) + ")";
  1864  				}
  1865  				if ( type === "function" )  {
  1866  					stack.push( obj );
  1867  					res = parser.call( this, obj, stack );
  1868  					stack.pop();
  1869  					return res;
  1870  				}
  1871  				return ( type === "string" ) ? parser : this.parsers.error;
  1872  			},
  1873  			typeOf: function( obj ) {
  1874  				var type;
  1875  				if ( obj === null ) {
  1876  					type = "null";
  1877  				} else if ( typeof obj === "undefined" ) {
  1878  					type = "undefined";
  1879  				} else if ( QUnit.is( "regexp", obj) ) {
  1880  					type = "regexp";
  1881  				} else if ( QUnit.is( "date", obj) ) {
  1882  					type = "date";
  1883  				} else if ( QUnit.is( "function", obj) ) {
  1884  					type = "function";
  1885  				} else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
  1886  					type = "window";
  1887  				} else if ( obj.nodeType === 9 ) {
  1888  					type = "document";
  1889  				} else if ( obj.nodeType ) {
  1890  					type = "node";
  1891  				} else if (
  1892  					// native arrays
  1893  					toString.call( obj ) === "[object Array]" ||
  1894  					// NodeList objects
  1895  					( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
  1896  				) {
  1897  					type = "array";
  1898  				} else if ( obj.constructor === Error.prototype.constructor ) {
  1899  					type = "error";
  1900  				} else {
  1901  					type = typeof obj;
  1902  				}
  1903  				return type;
  1904  			},
  1905  			separator: function() {
  1906  				return this.multiline ?	this.HTML ? "<br />" : "\n" : this.HTML ? "&nbsp;" : " ";
  1907  			},
  1908  			// extra can be a number, shortcut for increasing-calling-decreasing
  1909  			indent: function( extra ) {
  1910  				if ( !this.multiline ) {
  1911  					return "";
  1912  				}
  1913  				var chr = this.indentChar;
  1914  				if ( this.HTML ) {
  1915  					chr = chr.replace( /\t/g, "   " ).replace( / /g, "&nbsp;" );
  1916  				}
  1917  				return new Array( this.depth + ( extra || 0 ) ).join(chr);
  1918  			},
  1919  			up: function( a ) {
  1920  				this.depth += a || 1;
  1921  			},
  1922  			down: function( a ) {
  1923  				this.depth -= a || 1;
  1924  			},
  1925  			setParser: function( name, parser ) {
  1926  				this.parsers[name] = parser;
  1927  			},
  1928  			// The next 3 are exposed so you can use them
  1929  			quote: quote,
  1930  			literal: literal,
  1931  			join: join,
  1932  			//
  1933  			depth: 1,
  1934  			// This is the list of parsers, to modify them, use jsDump.setParser
  1935  			parsers: {
  1936  				window: "[Window]",
  1937  				document: "[Document]",
  1938  				error: function(error) {
  1939  					return "Error(\"" + error.message + "\")";
  1940  				},
  1941  				unknown: "[Unknown]",
  1942  				"null": "null",
  1943  				"undefined": "undefined",
  1944  				"function": function( fn ) {
  1945  					var ret = "function",
  1946  						// functions never have name in IE
  1947  						name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
  1948  
  1949  					if ( name ) {
  1950  						ret += " " + name;
  1951  					}
  1952  					ret += "( ";
  1953  
  1954  					ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" );
  1955  					return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" );
  1956  				},
  1957  				array: array,
  1958  				nodelist: array,
  1959  				"arguments": array,
  1960  				object: function( map, stack ) {
  1961  					/*jshint forin:false */
  1962  					var ret = [ ], keys, key, val, i;
  1963  					QUnit.jsDump.up();
  1964  					keys = [];
  1965  					for ( key in map ) {
  1966  						keys.push( key );
  1967  					}
  1968  					keys.sort();
  1969  					for ( i = 0; i < keys.length; i++ ) {
  1970  						key = keys[ i ];
  1971  						val = map[ key ];
  1972  						ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) );
  1973  					}
  1974  					QUnit.jsDump.down();
  1975  					return join( "{", ret, "}" );
  1976  				},
  1977  				node: function( node ) {
  1978  					var len, i, val,
  1979  						open = QUnit.jsDump.HTML ? "&lt;" : "<",
  1980  						close = QUnit.jsDump.HTML ? "&gt;" : ">",
  1981  						tag = node.nodeName.toLowerCase(),
  1982  						ret = open + tag,
  1983  						attrs = node.attributes;
  1984  
  1985  					if ( attrs ) {
  1986  						for ( i = 0, len = attrs.length; i < len; i++ ) {
  1987  							val = attrs[i].nodeValue;
  1988  							// IE6 includes all attributes in .attributes, even ones not explicitly set.
  1989  							// Those have values like undefined, null, 0, false, "" or "inherit".
  1990  							if ( val && val !== "inherit" ) {
  1991  								ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" );
  1992  							}
  1993  						}
  1994  					}
  1995  					ret += close;
  1996  
  1997  					// Show content of TextNode or CDATASection
  1998  					if ( node.nodeType === 3 || node.nodeType === 4 ) {
  1999  						ret += node.nodeValue;
  2000  					}
  2001  
  2002  					return ret + open + "/" + tag + close;
  2003  				},
  2004  				// function calls it internally, it's the arguments part of the function
  2005  				functionArgs: function( fn ) {
  2006  					var args,
  2007  						l = fn.length;
  2008  
  2009  					if ( !l ) {
  2010  						return "";
  2011  					}
  2012  
  2013  					args = new Array(l);
  2014  					while ( l-- ) {
  2015  						// 97 is 'a'
  2016  						args[l] = String.fromCharCode(97+l);
  2017  					}
  2018  					return " " + args.join( ", " ) + " ";
  2019  				},
  2020  				// object calls it internally, the key part of an item in a map
  2021  				key: quote,
  2022  				// function calls it internally, it's the content of the function
  2023  				functionCode: "[code]",
  2024  				// node calls it internally, it's an html attribute value
  2025  				attribute: quote,
  2026  				string: quote,
  2027  				date: quote,
  2028  				regexp: literal,
  2029  				number: literal,
  2030  				"boolean": literal
  2031  			},
  2032  			// if true, entities are escaped ( <, >, \t, space and \n )
  2033  			HTML: false,
  2034  			// indentation unit
  2035  			indentChar: "  ",
  2036  			// if true, items in a collection, are separated by a \n, else just a space.
  2037  			multiline: true
  2038  		};
  2039  
  2040  	return jsDump;
  2041  }());
  2042  
  2043  // from jquery.js
  2044  function inArray( elem, array ) {
  2045  	if ( array.indexOf ) {
  2046  		return array.indexOf( elem );
  2047  	}
  2048  
  2049  	for ( var i = 0, length = array.length; i < length; i++ ) {
  2050  		if ( array[ i ] === elem ) {
  2051  			return i;
  2052  		}
  2053  	}
  2054  
  2055  	return -1;
  2056  }
  2057  
  2058  /*
  2059   * Javascript Diff Algorithm
  2060   *  By John Resig (http://ejohn.org/)
  2061   *  Modified by Chu Alan "sprite"
  2062   *
  2063   * Released under the MIT license.
  2064   *
  2065   * More Info:
  2066   *  http://ejohn.org/projects/javascript-diff-algorithm/
  2067   *
  2068   * Usage: QUnit.diff(expected, actual)
  2069   *
  2070   * 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"
  2071   */
  2072  QUnit.diff = (function() {
  2073  	/*jshint eqeqeq:false, eqnull:true */
  2074  	function diff( o, n ) {
  2075  		var i,
  2076  			ns = {},
  2077  			os = {};
  2078  
  2079  		for ( i = 0; i < n.length; i++ ) {
  2080  			if ( !hasOwn.call( ns, n[i] ) ) {
  2081  				ns[ n[i] ] = {
  2082  					rows: [],
  2083  					o: null
  2084  				};
  2085  			}
  2086  			ns[ n[i] ].rows.push( i );
  2087  		}
  2088  
  2089  		for ( i = 0; i < o.length; i++ ) {
  2090  			if ( !hasOwn.call( os, o[i] ) ) {
  2091  				os[ o[i] ] = {
  2092  					rows: [],
  2093  					n: null
  2094  				};
  2095  			}
  2096  			os[ o[i] ].rows.push( i );
  2097  		}
  2098  
  2099  		for ( i in ns ) {
  2100  			if ( hasOwn.call( ns, i ) ) {
  2101  				if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) {
  2102  					n[ ns[i].rows[0] ] = {
  2103  						text: n[ ns[i].rows[0] ],
  2104  						row: os[i].rows[0]
  2105  					};
  2106  					o[ os[i].rows[0] ] = {
  2107  						text: o[ os[i].rows[0] ],
  2108  						row: ns[i].rows[0]
  2109  					};
  2110  				}
  2111  			}
  2112  		}
  2113  
  2114  		for ( i = 0; i < n.length - 1; i++ ) {
  2115  			if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
  2116  						n[ i + 1 ] == o[ n[i].row + 1 ] ) {
  2117  
  2118  				n[ i + 1 ] = {
  2119  					text: n[ i + 1 ],
  2120  					row: n[i].row + 1
  2121  				};
  2122  				o[ n[i].row + 1 ] = {
  2123  					text: o[ n[i].row + 1 ],
  2124  					row: i + 1
  2125  				};
  2126  			}
  2127  		}
  2128  
  2129  		for ( i = n.length - 1; i > 0; i-- ) {
  2130  			if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
  2131  						n[ i - 1 ] == o[ n[i].row - 1 ]) {
  2132  
  2133  				n[ i - 1 ] = {
  2134  					text: n[ i - 1 ],
  2135  					row: n[i].row - 1
  2136  				};
  2137  				o[ n[i].row - 1 ] = {
  2138  					text: o[ n[i].row - 1 ],
  2139  					row: i - 1
  2140  				};
  2141  			}
  2142  		}
  2143  
  2144  		return {
  2145  			o: o,
  2146  			n: n
  2147  		};
  2148  	}
  2149  
  2150  	return function( o, n ) {
  2151  		o = o.replace( /\s+$/, "" );
  2152  		n = n.replace( /\s+$/, "" );
  2153  
  2154  		var i, pre,
  2155  			str = "",
  2156  			out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),
  2157  			oSpace = o.match(/\s+/g),
  2158  			nSpace = n.match(/\s+/g);
  2159  
  2160  		if ( oSpace == null ) {
  2161  			oSpace = [ " " ];
  2162  		}
  2163  		else {
  2164  			oSpace.push( " " );
  2165  		}
  2166  
  2167  		if ( nSpace == null ) {
  2168  			nSpace = [ " " ];
  2169  		}
  2170  		else {
  2171  			nSpace.push( " " );
  2172  		}
  2173  
  2174  		if ( out.n.length === 0 ) {
  2175  			for ( i = 0; i < out.o.length; i++ ) {
  2176  				str += "<del>" + out.o[i] + oSpace[i] + "</del>";
  2177  			}
  2178  		}
  2179  		else {
  2180  			if ( out.n[0].text == null ) {
  2181  				for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {
  2182  					str += "<del>" + out.o[n] + oSpace[n] + "</del>";
  2183  				}
  2184  			}
  2185  
  2186  			for ( i = 0; i < out.n.length; i++ ) {
  2187  				if (out.n[i].text == null) {
  2188  					str += "<ins>" + out.n[i] + nSpace[i] + "</ins>";
  2189  				}
  2190  				else {
  2191  					// `pre` initialized at top of scope
  2192  					pre = "";
  2193  
  2194  					for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
  2195  						pre += "<del>" + out.o[n] + oSpace[n] + "</del>";
  2196  					}
  2197  					str += " " + out.n[i].text + nSpace[i] + pre;
  2198  				}
  2199  			}
  2200  		}
  2201  
  2202  		return str;
  2203  	};
  2204  }());
  2205  
  2206  // for CommonJS environments, export everything
  2207  if ( typeof exports !== "undefined" ) {
  2208  	extend( exports, QUnit.constructor.prototype );
  2209  }
  2210  
  2211  // get at whatever the global object is, like window in browsers
  2212  }( (function() {return this;}.call()) ));