| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182 | /** * QUnit v1.12.0pre - A JavaScript Unit Testing Framework * * http://qunitjs.com * * Copyright 2012 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license */(function( window ) {var QUnit,	assert,	config,	onErrorFnPrev,	testId = 0,	fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),	toString = Object.prototype.toString,	hasOwn = Object.prototype.hasOwnProperty,	// Keep a local reference to Date (GH-283)	Date = window.Date,	defined = {		setTimeout: typeof window.setTimeout !== "undefined",		sessionStorage: (function() {			var x = "qunit-test-string";			try {				sessionStorage.setItem( x, x );				sessionStorage.removeItem( x );				return true;			} catch( e ) {				return false;			}		}())	},	/**	 * Provides a normalized error string, correcting an issue	 * with IE 7 (and prior) where Error.prototype.toString is	 * not properly implemented	 *	 * Based on http://es5.github.com/#x15.11.4.4	 *	 * @param {String|Error} error	 * @return {String} error message	 */	errorString = function( error ) {		var name, message,			errorString = error.toString();		if ( errorString.substring( 0, 7 ) === "[object" ) {			name = error.name ? error.name.toString() : "Error";			message = error.message ? error.message.toString() : "";			if ( name && message ) {				return name + ": " + message;			} else if ( name ) {				return name;			} else if ( message ) {				return message;			} else {				return "Error";			}		} else {			return errorString;		}	},	/**	 * Makes a clone of an object using only Array or Object as base,	 * and copies over the own enumerable properties.	 *	 * @param {Object} obj	 * @return {Object} New object with only the own properties (recursively).	 */	objectValues = function( obj ) {		// Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392.		/*jshint newcap: false */		var key, val,			vals = QUnit.is( "array", obj ) ? [] : {};		for ( key in obj ) {			if ( hasOwn.call( obj, key ) ) {				val = obj[key];				vals[key] = val === Object(val) ? objectValues(val) : val;			}		}		return vals;	};function Test( settings ) {	extend( this, settings );	this.assertions = [];	this.testNumber = ++Test.count;}Test.count = 0;Test.prototype = {	init: function() {		var a, b, li,			tests = id( "qunit-tests" );		if ( tests ) {			b = document.createElement( "strong" );			b.innerHTML = this.nameHtml;			// `a` initialized at top of scope			a = document.createElement( "a" );			a.innerHTML = "Rerun";			a.href = QUnit.url({ testNumber: this.testNumber });			li = document.createElement( "li" );			li.appendChild( b );			li.appendChild( a );			li.className = "running";			li.id = this.id = "qunit-test-output" + testId++;			tests.appendChild( li );		}	},	setup: function() {		if ( this.module !== config.previousModule ) {			if ( config.previousModule ) {				runLoggingCallbacks( "moduleDone", QUnit, {					name: config.previousModule,					failed: config.moduleStats.bad,					passed: config.moduleStats.all - config.moduleStats.bad,					total: config.moduleStats.all				});			}			config.previousModule = this.module;			config.moduleStats = { all: 0, bad: 0 };			runLoggingCallbacks( "moduleStart", QUnit, {				name: this.module			});		} else if ( config.autorun ) {			runLoggingCallbacks( "moduleStart", QUnit, {				name: this.module			});		}		config.current = this;		this.testEnvironment = extend({			setup: function() {},			teardown: function() {}		}, this.moduleTestEnvironment );		this.started = +new Date();		runLoggingCallbacks( "testStart", QUnit, {			name: this.testName,			module: this.module		});		// allow utility functions to access the current test environment		// TODO why??		QUnit.current_testEnvironment = this.testEnvironment;		if ( !config.pollution ) {			saveGlobal();		}		if ( config.notrycatch ) {			this.testEnvironment.setup.call( this.testEnvironment );			return;		}		try {			this.testEnvironment.setup.call( this.testEnvironment );		} catch( e ) {			QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );		}	},	run: function() {		config.current = this;		var running = id( "qunit-testresult" );		if ( running ) {			running.innerHTML = "Running: <br/>" + this.nameHtml;		}		if ( this.async ) {			QUnit.stop();		}		this.callbackStarted = +new Date();		if ( config.notrycatch ) {			this.callback.call( this.testEnvironment, QUnit.assert );			this.callbackRuntime = +new Date() - this.callbackStarted;			return;		}		try {			this.callback.call( this.testEnvironment, QUnit.assert );			this.callbackRuntime = +new Date() - this.callbackStarted;		} catch( e ) {			this.callbackRuntime = +new Date() - this.callbackStarted;			QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );			// else next test will carry the responsibility			saveGlobal();			// Restart the tests if they're blocking			if ( config.blocking ) {				QUnit.start();			}		}	},	teardown: function() {		config.current = this;		if ( config.notrycatch ) {			if ( typeof this.callbackRuntime === "undefined" ) {				this.callbackRuntime = +new Date() - this.callbackStarted;			}			this.testEnvironment.teardown.call( this.testEnvironment );			return;		} else {			try {				this.testEnvironment.teardown.call( this.testEnvironment );			} catch( e ) {				QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );			}		}		checkPollution();	},	finish: function() {		config.current = this;		if ( config.requireExpects && this.expected === null ) {			QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );		} else if ( this.expected !== null && this.expected !== this.assertions.length ) {			QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );		} else if ( this.expected === null && !this.assertions.length ) {			QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );		}		var i, assertion, a, b, time, li, ol,			test = this,			good = 0,			bad = 0,			tests = id( "qunit-tests" );		this.runtime = +new Date() - this.started;		config.stats.all += this.assertions.length;		config.moduleStats.all += this.assertions.length;		if ( tests ) {			ol = document.createElement( "ol" );			ol.className = "qunit-assert-list";			for ( i = 0; i < this.assertions.length; i++ ) {				assertion = this.assertions[i];				li = document.createElement( "li" );				li.className = assertion.result ? "pass" : "fail";				li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );				ol.appendChild( li );				if ( assertion.result ) {					good++;				} else {					bad++;					config.stats.bad++;					config.moduleStats.bad++;				}			}			// store result when possible			if ( QUnit.config.reorder && defined.sessionStorage ) {				if ( bad ) {					sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );				} else {					sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );				}			}			if ( bad === 0 ) {				addClass( ol, "qunit-collapsed" );			}			// `b` initialized at top of scope			b = document.createElement( "strong" );			b.innerHTML = this.nameHtml + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";			addEvent(b, "click", function() {				var next = b.parentNode.lastChild,					collapsed = hasClass( next, "qunit-collapsed" );				( collapsed ? removeClass : addClass )( next, "qunit-collapsed" );			});			addEvent(b, "dblclick", function( e ) {				var target = e && e.target ? e.target : window.event.srcElement;				if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) {					target = target.parentNode;				}				if ( window.location && target.nodeName.toLowerCase() === "strong" ) {					window.location = QUnit.url({ testNumber: test.testNumber });				}			});			// `time` initialized at top of scope			time = document.createElement( "span" );			time.className = "runtime";			time.innerHTML = this.runtime + " ms";			// `li` initialized at top of scope			li = id( this.id );			li.className = bad ? "fail" : "pass";			li.removeChild( li.firstChild );			a = li.firstChild;			li.appendChild( b );			li.appendChild( a );			li.appendChild( time );			li.appendChild( ol );		} else {			for ( i = 0; i < this.assertions.length; i++ ) {				if ( !this.assertions[i].result ) {					bad++;					config.stats.bad++;					config.moduleStats.bad++;				}			}		}		runLoggingCallbacks( "testDone", QUnit, {			name: this.testName,			module: this.module,			failed: bad,			passed: this.assertions.length - bad,			total: this.assertions.length,			duration: this.runtime		});		QUnit.reset();		config.current = undefined;	},	queue: function() {		var bad,			test = this;		synchronize(function() {			test.init();		});		function run() {			// each of these can by async			synchronize(function() {				test.setup();			});			synchronize(function() {				test.run();			});			synchronize(function() {				test.teardown();			});			synchronize(function() {				test.finish();			});		}		// `bad` initialized at top of scope		// defer when previous test run passed, if storage is available		bad = QUnit.config.reorder && defined.sessionStorage &&						+sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );		if ( bad ) {			run();		} else {			synchronize( run, true );		}	}};// Root QUnit object.// `QUnit` initialized at top of scopeQUnit = {	// call on start of module test to prepend name to all tests	module: function( name, testEnvironment ) {		config.currentModule = name;		config.currentModuleTestEnvironment = testEnvironment;		config.modules[name] = true;	},	asyncTest: function( testName, expected, callback ) {		if ( arguments.length === 2 ) {			callback = expected;			expected = null;		}		QUnit.test( testName, expected, callback, true );	},	test: function( testName, expected, callback, async ) {		var test,			nameHtml = "<span class='test-name'>" + escapeText( testName ) + "</span>";		if ( arguments.length === 2 ) {			callback = expected;			expected = null;		}		if ( config.currentModule ) {			nameHtml = "<span class='module-name'>" + escapeText( config.currentModule ) + "</span>: " + nameHtml;		}		test = new Test({			nameHtml: nameHtml,			testName: testName,			expected: expected,			async: async,			callback: callback,			module: config.currentModule,			moduleTestEnvironment: config.currentModuleTestEnvironment,			stack: sourceFromStacktrace( 2 )		});		if ( !validTest( test ) ) {			return;		}		test.queue();	},	// Specify the number of expected assertions to guarantee that failed test (no assertions are run at all) don't slip through.	expect: function( asserts ) {		if (arguments.length === 1) {			config.current.expected = asserts;		} else {			return config.current.expected;		}	},	start: function( count ) {		// QUnit hasn't been initialized yet.		// Note: RequireJS (et al) may delay onLoad		if ( config.semaphore === undefined ) {			QUnit.begin(function() {				// This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first				setTimeout(function() {					QUnit.start( count );				});			});			return;		}		config.semaphore -= count || 1;		// don't start until equal number of stop-calls		if ( config.semaphore > 0 ) {			return;		}		// ignore if start is called more often then stop		if ( config.semaphore < 0 ) {			config.semaphore = 0;			QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) );			return;		}		// A slight delay, to avoid any current callbacks		if ( defined.setTimeout ) {			window.setTimeout(function() {				if ( config.semaphore > 0 ) {					return;				}				if ( config.timeout ) {					clearTimeout( config.timeout );				}				config.blocking = false;				process( true );			}, 13);		} else {			config.blocking = false;			process( true );		}	},	stop: function( count ) {		config.semaphore += count || 1;		config.blocking = true;		if ( config.testTimeout && defined.setTimeout ) {			clearTimeout( config.timeout );			config.timeout = window.setTimeout(function() {				QUnit.ok( false, "Test timed out" );				config.semaphore = 1;				QUnit.start();			}, config.testTimeout );		}	}};// `assert` initialized at top of scope// Assert helpers// All of these must either call QUnit.push() or manually do:// - runLoggingCallbacks( "log", .. );// - config.current.assertions.push({ .. });// We attach it to the QUnit object *after* we expose the public API,// otherwise `assert` will become a global variable in browsers (#341).assert = {	/**	 * Asserts rough true-ish result.	 * @name ok	 * @function	 * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );	 */	ok: function( result, msg ) {		if ( !config.current ) {			throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );		}		result = !!result;		var source,			details = {				module: config.current.module,				name: config.current.testName,				result: result,				message: msg			};		msg = escapeText( msg || (result ? "okay" : "failed" ) );		msg = "<span class='test-message'>" + msg + "</span>";		if ( !result ) {			source = sourceFromStacktrace( 2 );			if ( source ) {				details.source = source;				msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr></table>";			}		}		runLoggingCallbacks( "log", QUnit, details );		config.current.assertions.push({			result: result,			message: msg		});	},	/**	 * Assert that the first two arguments are equal, with an optional message.	 * Prints out both actual and expected values.	 * @name equal	 * @function	 * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );	 */	equal: function( actual, expected, message ) {		/*jshint eqeqeq:false */		QUnit.push( expected == actual, actual, expected, message );	},	/**	 * @name notEqual	 * @function	 */	notEqual: function( actual, expected, message ) {		/*jshint eqeqeq:false */		QUnit.push( expected != actual, actual, expected, message );	},	/**	 * @name propEqual	 * @function	 */	propEqual: function( actual, expected, message ) {		actual = objectValues(actual);		expected = objectValues(expected);		QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );	},	/**	 * @name notPropEqual	 * @function	 */	notPropEqual: function( actual, expected, message ) {		actual = objectValues(actual);		expected = objectValues(expected);		QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );	},	/**	 * @name deepEqual	 * @function	 */	deepEqual: function( actual, expected, message ) {		QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );	},	/**	 * @name notDeepEqual	 * @function	 */	notDeepEqual: function( actual, expected, message ) {		QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );	},	/**	 * @name strictEqual	 * @function	 */	strictEqual: function( actual, expected, message ) {		QUnit.push( expected === actual, actual, expected, message );	},	/**	 * @name notStrictEqual	 * @function	 */	notStrictEqual: function( actual, expected, message ) {		QUnit.push( expected !== actual, actual, expected, message );	},	"throws": function( block, expected, message ) {		var actual,			expectedOutput = expected,			ok = false;		// 'expected' is optional		if ( typeof expected === "string" ) {			message = expected;			expected = null;		}		config.current.ignoreGlobalErrors = true;		try {			block.call( config.current.testEnvironment );		} catch (e) {			actual = e;		}		config.current.ignoreGlobalErrors = false;		if ( actual ) {			// we don't want to validate thrown error			if ( !expected ) {				ok = true;				expectedOutput = null;			// expected is a regexp			} else if ( QUnit.objectType( expected ) === "regexp" ) {				ok = expected.test( errorString( actual ) );			// expected is a constructor			} else if ( actual instanceof expected ) {				ok = true;			// expected is a validation function which returns true is validation passed			} else if ( expected.call( {}, actual ) === true ) {				expectedOutput = null;				ok = true;			}			QUnit.push( ok, actual, expectedOutput, message );		} else {			QUnit.pushFailure( message, null, 'No exception was thrown.' );		}	}};/** * @deprecated since 1.8.0 * Kept assertion helpers in root for backwards compatibility. */extend( QUnit, assert );/** * @deprecated since 1.9.0 * Kept root "raises()" for backwards compatibility. * (Note that we don't introduce assert.raises). */QUnit.raises = assert[ "throws" ];/** * @deprecated since 1.0.0, replaced with error pushes since 1.3.0 * Kept to avoid TypeErrors for undefined methods. */QUnit.equals = function() {	QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );};QUnit.same = function() {	QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );};// We want access to the constructor's prototype(function() {	function F() {}	F.prototype = QUnit;	QUnit = new F();	// Make F QUnit's constructor so that we can add to the prototype later	QUnit.constructor = F;}());/** * Config object: Maintain internal state * Later exposed as QUnit.config * `config` initialized at top of scope */config = {	// The queue of tests to run	queue: [],	// block until document ready	blocking: true,	// when enabled, show only failing tests	// gets persisted through sessionStorage and can be changed in UI via checkbox	hidepassed: false,	// by default, run previously failed tests first	// very useful in combination with "Hide passed tests" checked	reorder: true,	// by default, modify document.title when suite is done	altertitle: true,	// when enabled, all tests must call expect()	requireExpects: false,	// add checkboxes that are persisted in the query-string	// when enabled, the id is set to `true` as a `QUnit.config` property	urlConfig: [		{			id: "noglobals",			label: "Check for Globals",			tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."		},		{			id: "notrycatch",			label: "No try-catch",			tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."		}	],	// Set of all modules.	modules: {},	// logging callback queues	begin: [],	done: [],	log: [],	testStart: [],	testDone: [],	moduleStart: [],	moduleDone: []};// Export global variables, unless an 'exports' object exists,// in that case we assume we're in CommonJS (dealt with on the bottom of the script)if ( typeof exports === "undefined" ) {	extend( window, QUnit );	// Expose QUnit object	window.QUnit = QUnit;}// Initialize more QUnit.config and QUnit.urlParams(function() {	var i,		location = window.location || { search: "", protocol: "file:" },		params = location.search.slice( 1 ).split( "&" ),		length = params.length,		urlParams = {},		current;	if ( params[ 0 ] ) {		for ( i = 0; i < length; i++ ) {			current = params[ i ].split( "=" );			current[ 0 ] = decodeURIComponent( current[ 0 ] );			// allow just a key to turn on a flag, e.g., test.html?noglobals			current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;			urlParams[ current[ 0 ] ] = current[ 1 ];		}	}	QUnit.urlParams = urlParams;	// String search anywhere in moduleName+testName	config.filter = urlParams.filter;	// Exact match of the module name	config.module = urlParams.module;	config.testNumber = parseInt( urlParams.testNumber, 10 ) || null;	// Figure out if we're running the tests from a server or not	QUnit.isLocal = location.protocol === "file:";}());// Extend QUnit object,// these after set here because they should not be exposed as global functionsextend( QUnit, {	assert: assert,	config: config,	// Initialize the configuration options	init: function() {		extend( config, {			stats: { all: 0, bad: 0 },			moduleStats: { all: 0, bad: 0 },			started: +new Date(),			updateRate: 1000,			blocking: false,			autostart: true,			autorun: false,			filter: "",			queue: [],			semaphore: 1		});		var tests, banner, result,			qunit = id( "qunit" );		if ( qunit ) {			qunit.innerHTML =				"<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +				"<h2 id='qunit-banner'></h2>" +				"<div id='qunit-testrunner-toolbar'></div>" +				"<h2 id='qunit-userAgent'></h2>" +				"<ol id='qunit-tests'></ol>";		}		tests = id( "qunit-tests" );		banner = id( "qunit-banner" );		result = id( "qunit-testresult" );		if ( tests ) {			tests.innerHTML = "";		}		if ( banner ) {			banner.className = "";		}		if ( result ) {			result.parentNode.removeChild( result );		}		if ( tests ) {			result = document.createElement( "p" );			result.id = "qunit-testresult";			result.className = "result";			tests.parentNode.insertBefore( result, tests );			result.innerHTML = "Running...<br/> ";		}	},	// Resets the test setup. Useful for tests that modify the DOM.	reset: function() {		var fixture = id( "qunit-fixture" );		if ( fixture ) {			fixture.innerHTML = config.fixture;		}	},	// Trigger an event on an element.	// @example triggerEvent( document.body, "click" );	triggerEvent: function( elem, type, event ) {		if ( document.createEvent ) {			event = document.createEvent( "MouseEvents" );			event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,				0, 0, 0, 0, 0, false, false, false, false, 0, null);			elem.dispatchEvent( event );		} else if ( elem.fireEvent ) {			elem.fireEvent( "on" + type );		}	},	// Safe object type checking	is: function( type, obj ) {		return QUnit.objectType( obj ) === type;	},	objectType: function( obj ) {		if ( typeof obj === "undefined" ) {				return "undefined";		// consider: typeof null === object		}		if ( obj === null ) {				return "null";		}		var match = toString.call( obj ).match(/^\[object\s(.*)\]$/),			type = match && match[1] || "";		switch ( type ) {			case "Number":				if ( isNaN(obj) ) {					return "nan";				}				return "number";			case "String":			case "Boolean":			case "Array":			case "Date":			case "RegExp":			case "Function":				return type.toLowerCase();		}		if ( typeof obj === "object" ) {			return "object";		}		return undefined;	},	push: function( result, actual, expected, message ) {		if ( !config.current ) {			throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );		}		var output, source,			details = {				module: config.current.module,				name: config.current.testName,				result: result,				message: message,				actual: actual,				expected: expected			};		message = escapeText( message ) || ( result ? "okay" : "failed" );		message = "<span class='test-message'>" + message + "</span>";		output = message;		if ( !result ) {			expected = escapeText( QUnit.jsDump.parse(expected) );			actual = escapeText( QUnit.jsDump.parse(actual) );			output += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + expected + "</pre></td></tr>";			if ( actual !== expected ) {				output += "<tr class='test-actual'><th>Result: </th><td><pre>" + actual + "</pre></td></tr>";				output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>";			}			source = sourceFromStacktrace();			if ( source ) {				details.source = source;				output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";			}			output += "</table>";		}		runLoggingCallbacks( "log", QUnit, details );		config.current.assertions.push({			result: !!result,			message: output		});	},	pushFailure: function( message, source, actual ) {		if ( !config.current ) {			throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );		}		var output,			details = {				module: config.current.module,				name: config.current.testName,				result: false,				message: message			};		message = escapeText( message ) || "error";		message = "<span class='test-message'>" + message + "</span>";		output = message;		output += "<table>";		if ( actual ) {			output += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText( actual ) + "</pre></td></tr>";		}		if ( source ) {			details.source = source;			output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";		}		output += "</table>";		runLoggingCallbacks( "log", QUnit, details );		config.current.assertions.push({			result: false,			message: output		});	},	url: function( params ) {		params = extend( extend( {}, QUnit.urlParams ), params );		var key,			querystring = "?";		for ( key in params ) {			if ( !hasOwn.call( params, key ) ) {				continue;			}			querystring += encodeURIComponent( key ) + "=" +				encodeURIComponent( params[ key ] ) + "&";		}		return window.location.protocol + "//" + window.location.host +			window.location.pathname + querystring.slice( 0, -1 );	},	extend: extend,	id: id,	addEvent: addEvent,	addClass: addClass,	hasClass: hasClass,	removeClass: removeClass	// load, equiv, jsDump, diff: Attached later});/** * @deprecated: Created for backwards compatibility with test runner that set the hook function * into QUnit.{hook}, instead of invoking it and passing the hook function. * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here. * Doing this allows us to tell if the following methods have been overwritten on the actual * QUnit object. */extend( QUnit.constructor.prototype, {	// Logging callbacks; all receive a single argument with the listed properties	// run test/logs.html for any related changes	begin: registerLoggingCallback( "begin" ),	// done: { failed, passed, total, runtime }	done: registerLoggingCallback( "done" ),	// log: { result, actual, expected, message }	log: registerLoggingCallback( "log" ),	// testStart: { name }	testStart: registerLoggingCallback( "testStart" ),	// testDone: { name, failed, passed, total, duration }	testDone: registerLoggingCallback( "testDone" ),	// moduleStart: { name }	moduleStart: registerLoggingCallback( "moduleStart" ),	// moduleDone: { name, failed, passed, total }	moduleDone: registerLoggingCallback( "moduleDone" )});if ( typeof document === "undefined" || document.readyState === "complete" ) {	config.autorun = true;}QUnit.load = function() {	runLoggingCallbacks( "begin", QUnit, {} );	// Initialize the config, saving the execution queue	var banner, filter, i, label, len, main, ol, toolbar, userAgent, val,		urlConfigCheckboxesContainer, urlConfigCheckboxes, moduleFilter,		numModules = 0,		moduleNames = [],		moduleFilterHtml = "",		urlConfigHtml = "",		oldconfig = extend( {}, config );	QUnit.init();	extend(config, oldconfig);	config.blocking = false;	len = config.urlConfig.length;	for ( i = 0; i < len; i++ ) {		val = config.urlConfig[i];		if ( typeof val === "string" ) {			val = {				id: val,				label: val,				tooltip: "[no tooltip available]"			};		}		config[ val.id ] = QUnit.urlParams[ val.id ];		urlConfigHtml += "<input id='qunit-urlconfig-" + escapeText( val.id ) +			"' name='" + escapeText( val.id ) +			"' type='checkbox'" + ( config[ val.id ] ? " checked='checked'" : "" ) +			" title='" + escapeText( val.tooltip ) +			"'><label for='qunit-urlconfig-" + escapeText( val.id ) +			"' title='" + escapeText( val.tooltip ) + "'>" + val.label + "</label>";	}	for ( i in config.modules ) {		if ( config.modules.hasOwnProperty( i ) ) {			moduleNames.push(i);		}	}	numModules = moduleNames.length;	moduleNames.sort( function( a, b ) {		return a.localeCompare( b );	});	moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label><select id='qunit-modulefilter' name='modulefilter'><option value='' " +		( config.module === undefined  ? "selected='selected'" : "" ) +		">< All Modules ></option>";	for ( i = 0; i < numModules; i++) {			moduleFilterHtml += "<option value='" + escapeText( encodeURIComponent(moduleNames[i]) ) + "' " +				( config.module === moduleNames[i] ? "selected='selected'" : "" ) +				">" + escapeText(moduleNames[i]) + "</option>";	}	moduleFilterHtml += "</select>";	// `userAgent` initialized at top of scope	userAgent = id( "qunit-userAgent" );	if ( userAgent ) {		userAgent.innerHTML = navigator.userAgent;	}	// `banner` initialized at top of scope	banner = id( "qunit-header" );	if ( banner ) {		banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) + "'>" + banner.innerHTML + "</a> ";	}	// `toolbar` initialized at top of scope	toolbar = id( "qunit-testrunner-toolbar" );	if ( toolbar ) {		// `filter` initialized at top of scope		filter = document.createElement( "input" );		filter.type = "checkbox";		filter.id = "qunit-filter-pass";		addEvent( filter, "click", function() {			var tmp,				ol = document.getElementById( "qunit-tests" );			if ( filter.checked ) {				ol.className = ol.className + " hidepass";			} else {				tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";				ol.className = tmp.replace( / hidepass /, " " );			}			if ( defined.sessionStorage ) {				if (filter.checked) {					sessionStorage.setItem( "qunit-filter-passed-tests", "true" );				} else {					sessionStorage.removeItem( "qunit-filter-passed-tests" );				}			}		});		if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {			filter.checked = true;			// `ol` initialized at top of scope			ol = document.getElementById( "qunit-tests" );			ol.className = ol.className + " hidepass";		}		toolbar.appendChild( filter );		// `label` initialized at top of scope		label = document.createElement( "label" );		label.setAttribute( "for", "qunit-filter-pass" );		label.setAttribute( "title", "Only show tests and assertions that fail. Stored in sessionStorage." );		label.innerHTML = "Hide passed tests";		toolbar.appendChild( label );		urlConfigCheckboxesContainer = document.createElement("span");		urlConfigCheckboxesContainer.innerHTML = urlConfigHtml;		urlConfigCheckboxes = urlConfigCheckboxesContainer.getElementsByTagName("input");		// For oldIE support:		// * Add handlers to the individual elements instead of the container		// * Use "click" instead of "change"		// * Fallback from event.target to event.srcElement		addEvents( urlConfigCheckboxes, "click", function( event ) {			var params = {},				target = event.target || event.srcElement;			params[ target.name ] = target.checked ? true : undefined;			window.location = QUnit.url( params );		});		toolbar.appendChild( urlConfigCheckboxesContainer );		if (numModules > 1) {			moduleFilter = document.createElement( 'span' );			moduleFilter.setAttribute( 'id', 'qunit-modulefilter-container' );			moduleFilter.innerHTML = moduleFilterHtml;			addEvent( moduleFilter.lastChild, "change", function() {				var selectBox = moduleFilter.getElementsByTagName("select")[0],					selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value);				window.location = QUnit.url( { module: ( selectedModule === "" ) ? undefined : selectedModule } );			});			toolbar.appendChild(moduleFilter);		}	}	// `main` initialized at top of scope	main = id( "qunit-fixture" );	if ( main ) {		config.fixture = main.innerHTML;	}	if ( config.autostart ) {		QUnit.start();	}};addEvent( window, "load", QUnit.load );// `onErrorFnPrev` initialized at top of scope// Preserve other handlersonErrorFnPrev = window.onerror;// Cover uncaught exceptions// Returning true will suppress the default browser handler,// returning false will let it run.window.onerror = function ( error, filePath, linerNr ) {	var ret = false;	if ( onErrorFnPrev ) {		ret = onErrorFnPrev( error, filePath, linerNr );	}	// Treat return value as window.onerror itself does,	// Only do our handling if not suppressed.	if ( ret !== true ) {		if ( QUnit.config.current ) {			if ( QUnit.config.current.ignoreGlobalErrors ) {				return true;			}			QUnit.pushFailure( error, filePath + ":" + linerNr );		} else {			QUnit.test( "global failure", extend( function() {				QUnit.pushFailure( error, filePath + ":" + linerNr );			}, { validTest: validTest } ) );		}		return false;	}	return ret;};function done() {	config.autorun = true;	// Log the last module results	if ( config.currentModule ) {		runLoggingCallbacks( "moduleDone", QUnit, {			name: config.currentModule,			failed: config.moduleStats.bad,			passed: config.moduleStats.all - config.moduleStats.bad,			total: config.moduleStats.all		});	}	var i, key,		banner = id( "qunit-banner" ),		tests = id( "qunit-tests" ),		runtime = +new Date() - config.started,		passed = config.stats.all - config.stats.bad,		html = [			"Tests completed in ",			runtime,			" milliseconds.<br/>",			"<span class='passed'>",			passed,			"</span> assertions of <span class='total'>",			config.stats.all,			"</span> passed, <span class='failed'>",			config.stats.bad,			"</span> failed."		].join( "" );	if ( banner ) {		banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );	}	if ( tests ) {		id( "qunit-testresult" ).innerHTML = html;	}	if ( config.altertitle && typeof document !== "undefined" && document.title ) {		// show ✖ for good, ✔ for bad suite result in title		// use escape sequences in case file gets loaded with non-utf-8-charset		document.title = [			( config.stats.bad ? "\u2716" : "\u2714" ),			document.title.replace( /^[\u2714\u2716] /i, "" )		].join( " " );	}	// clear own sessionStorage items if all tests passed	if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {		// `key` & `i` initialized at top of scope		for ( i = 0; i < sessionStorage.length; i++ ) {			key = sessionStorage.key( i++ );			if ( key.indexOf( "qunit-test-" ) === 0 ) {				sessionStorage.removeItem( key );			}		}	}	// scroll back to top to show results	if ( window.scrollTo ) {		window.scrollTo(0, 0);	}	runLoggingCallbacks( "done", QUnit, {		failed: config.stats.bad,		passed: passed,		total: config.stats.all,		runtime: runtime	});}/** @return Boolean: true if this test should be ran */function validTest( test ) {	var include,		filter = config.filter && config.filter.toLowerCase(),		module = config.module && config.module.toLowerCase(),		fullName = (test.module + ": " + test.testName).toLowerCase();	// Internally-generated tests are always valid	if ( test.callback && test.callback.validTest === validTest ) {		delete test.callback.validTest;		return true;	}	if ( config.testNumber ) {		return test.testNumber === config.testNumber;	}	if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {		return false;	}	if ( !filter ) {		return true;	}	include = filter.charAt( 0 ) !== "!";	if ( !include ) {		filter = filter.slice( 1 );	}	// If the filter matches, we need to honour include	if ( fullName.indexOf( filter ) !== -1 ) {		return include;	}	// Otherwise, do the opposite	return !include;}// so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)// Later Safari and IE10 are supposed to support error.stack as well// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stackfunction extractStacktrace( e, offset ) {	offset = offset === undefined ? 3 : offset;	var stack, include, i;	if ( e.stacktrace ) {		// Opera		return e.stacktrace.split( "\n" )[ offset + 3 ];	} else if ( e.stack ) {		// Firefox, Chrome		stack = e.stack.split( "\n" );		if (/^error$/i.test( stack[0] ) ) {			stack.shift();		}		if ( fileName ) {			include = [];			for ( i = offset; i < stack.length; i++ ) {				if ( stack[ i ].indexOf( fileName ) !== -1 ) {					break;				}				include.push( stack[ i ] );			}			if ( include.length ) {				return include.join( "\n" );			}		}		return stack[ offset ];	} else if ( e.sourceURL ) {		// Safari, PhantomJS		// hopefully one day Safari provides actual stacktraces		// exclude useless self-reference for generated Error objects		if ( /qunit.js$/.test( e.sourceURL ) ) {			return;		}		// for actual exceptions, this is useful		return e.sourceURL + ":" + e.line;	}}function sourceFromStacktrace( offset ) {	try {		throw new Error();	} catch ( e ) {		return extractStacktrace( e, offset );	}}/** * Escape text for attribute or text content. */function escapeText( s ) {	if ( !s ) {		return "";	}	s = s + "";	// Both single quotes and double quotes (for attributes)	return s.replace( /['"<>&]/g, function( s ) {		switch( s ) {			case '\'':				return ''';			case '"':				return '"';			case '<':				return '<';			case '>':				return '>';			case '&':				return '&';		}	});}function synchronize( callback, last ) {	config.queue.push( callback );	if ( config.autorun && !config.blocking ) {		process( last );	}}function process( last ) {	function next() {		process( last );	}	var start = new Date().getTime();	config.depth = config.depth ? config.depth + 1 : 1;	while ( config.queue.length && !config.blocking ) {		if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {			config.queue.shift()();		} else {			window.setTimeout( next, 13 );			break;		}	}	config.depth--;	if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {		done();	}}function saveGlobal() {	config.pollution = [];	if ( config.noglobals ) {		for ( var key in window ) {			// in Opera sometimes DOM element ids show up here, ignore them			if ( !hasOwn.call( window, key ) || /^qunit-test-output/.test( key ) ) {				continue;			}			config.pollution.push( key );		}	}}function checkPollution() {	var newGlobals,		deletedGlobals,		old = config.pollution;	saveGlobal();	newGlobals = diff( config.pollution, old );	if ( newGlobals.length > 0 ) {		QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );	}	deletedGlobals = diff( old, config.pollution );	if ( deletedGlobals.length > 0 ) {		QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );	}}// returns a new Array with the elements that are in a but not in bfunction diff( a, b ) {	var i, j,		result = a.slice();	for ( i = 0; i < result.length; i++ ) {		for ( j = 0; j < b.length; j++ ) {			if ( result[i] === b[j] ) {				result.splice( i, 1 );				i--;				break;			}		}	}	return result;}function extend( a, b ) {	for ( var prop in b ) {		if ( b[ prop ] === undefined ) {			delete a[ prop ];		// Avoid "Member not found" error in IE8 caused by setting window.constructor		} else if ( prop !== "constructor" || a !== window ) {			a[ prop ] = b[ prop ];		}	}	return a;}/** * @param {HTMLElement} elem * @param {string} type * @param {Function} fn */function addEvent( elem, type, fn ) {	// Standards-based browsers	if ( elem.addEventListener ) {		elem.addEventListener( type, fn, false );	// IE	} else {		elem.attachEvent( "on" + type, fn );	}}/** * @param {Array|NodeList} elems * @param {string} type * @param {Function} fn */function addEvents( elems, type, fn ) {	var i = elems.length;	while ( i-- ) {		addEvent( elems[i], type, fn );	}}function hasClass( elem, name ) {	return (" " + elem.className + " ").indexOf(" " + name + " ") > -1;}function addClass( elem, name ) {	if ( !hasClass( elem, name ) ) {		elem.className += (elem.className ? " " : "") + name;	}}function removeClass( elem, name ) {	var set = " " + elem.className + " ";	// Class name may appear multiple times	while ( set.indexOf(" " + name + " ") > -1 ) {		set = set.replace(" " + name + " " , " ");	}	// If possible, trim it for prettiness, but not necessarily	elem.className = window.jQuery ? jQuery.trim( set ) : ( set.trim ? set.trim() : set );}function id( name ) {	return !!( typeof document !== "undefined" && document && document.getElementById ) &&		document.getElementById( name );}function registerLoggingCallback( key ) {	return function( callback ) {		config[key].push( callback );	};}// Supports deprecated method of completely overwriting logging callbacksfunction runLoggingCallbacks( key, scope, args ) {	var i, callbacks;	if ( QUnit.hasOwnProperty( key ) ) {		QUnit[ key ].call(scope, args );	} else {		callbacks = config[ key ];		for ( i = 0; i < callbacks.length; i++ ) {			callbacks[ i ].call( scope, args );		}	}}// Test for equality any JavaScript type.// Author: Philippe Rathé <prathe@gmail.com>QUnit.equiv = (function() {	// Call the o related callback with the given arguments.	function bindCallbacks( o, callbacks, args ) {		var prop = QUnit.objectType( o );		if ( prop ) {			if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {				return callbacks[ prop ].apply( callbacks, args );			} else {				return callbacks[ prop ]; // or undefined			}		}	}	// the real equiv function	var innerEquiv,		// stack to decide between skip/abort functions		callers = [],		// stack to avoiding loops from circular referencing		parents = [],		parentsB = [],		getProto = Object.getPrototypeOf || function ( obj ) {			return obj.__proto__;		},		callbacks = (function () {			// for string, boolean, number and null			function useStrictEquality( b, a ) {				/*jshint eqeqeq:false */				if ( b instanceof a.constructor || a instanceof b.constructor ) {					// to catch short annotation VS 'new' annotation of a					// declaration					// e.g. var i = 1;					// var j = new Number(1);					return a == b;				} else {					return a === b;				}			}			return {				"string": useStrictEquality,				"boolean": useStrictEquality,				"number": useStrictEquality,				"null": useStrictEquality,				"undefined": useStrictEquality,				"nan": function( b ) {					return isNaN( b );				},				"date": function( b, a ) {					return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();				},				"regexp": function( b, a ) {					return QUnit.objectType( b ) === "regexp" &&						// the regex itself						a.source === b.source &&						// and its modifiers						a.global === b.global &&						// (gmi) ...						a.ignoreCase === b.ignoreCase &&						a.multiline === b.multiline &&						a.sticky === b.sticky;				},				// - skip when the property is a method of an instance (OOP)				// - abort otherwise,				// initial === would have catch identical references anyway				"function": function() {					var caller = callers[callers.length - 1];					return caller !== Object && typeof caller !== "undefined";				},				"array": function( b, a ) {					var i, j, len, loop, aCircular, bCircular;					// b could be an object literal here					if ( QUnit.objectType( b ) !== "array" ) {						return false;					}					len = a.length;					if ( len !== b.length ) {						// safe and faster						return false;					}					// track reference to avoid circular references					parents.push( a );					parentsB.push( b );					for ( i = 0; i < len; i++ ) {						loop = false;						for ( j = 0; j < parents.length; j++ ) {							aCircular = parents[j] === a[i];							bCircular = parentsB[j] === b[i];							if ( aCircular || bCircular ) {								if ( a[i] === b[i] || aCircular && bCircular ) {									loop = true;								} else {									parents.pop();									parentsB.pop();									return false;								}							}						}						if ( !loop && !innerEquiv(a[i], b[i]) ) {							parents.pop();							parentsB.pop();							return false;						}					}					parents.pop();					parentsB.pop();					return true;				},				"object": function( b, a ) {					var i, j, loop, aCircular, bCircular,						// Default to true						eq = true,						aProperties = [],						bProperties = [];					// comparing constructors is more strict than using					// instanceof					if ( a.constructor !== b.constructor ) {						// Allow objects with no prototype to be equivalent to						// objects with Object as their constructor.						if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||							( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {								return false;						}					}					// stack constructor before traversing properties					callers.push( a.constructor );					// track reference to avoid circular references					parents.push( a );					parentsB.push( b );					// be strict: don't ensures hasOwnProperty and go deep					for ( i in a ) {						loop = false;						for ( j = 0; j < parents.length; j++ ) {							aCircular = parents[j] === a[i];							bCircular = parentsB[j] === b[i];							if ( aCircular || bCircular ) {								if ( a[i] === b[i] || aCircular && bCircular ) {									loop = true;								} else {									eq = false;									break;								}							}						}						aProperties.push(i);						if ( !loop && !innerEquiv(a[i], b[i]) ) {							eq = false;							break;						}					}					parents.pop();					parentsB.pop();					callers.pop(); // unstack, we are done					for ( i in b ) {						bProperties.push( i ); // collect b's properties					}					// Ensures identical properties name					return eq && innerEquiv( aProperties.sort(), bProperties.sort() );				}			};		}());	innerEquiv = function() { // can take multiple arguments		var args = [].slice.apply( arguments );		if ( args.length < 2 ) {			return true; // end transition		}		return (function( a, b ) {			if ( a === b ) {				return true; // catch the most you can			} else if ( a === null || b === null || typeof a === "undefined" ||					typeof b === "undefined" ||					QUnit.objectType(a) !== QUnit.objectType(b) ) {				return false; // don't lose time with error prone cases			} else {				return bindCallbacks(a, callbacks, [ b, a ]);			}			// apply transition with (1..n) arguments		}( args[0], args[1] ) && arguments.callee.apply( this, args.splice(1, args.length - 1 )) );	};	return innerEquiv;}());/** * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | * http://flesler.blogspot.com Licensed under BSD * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008 * * @projectDescription Advanced and extensible data dumping for Javascript. * @version 1.0.0 * @author Ariel Flesler * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} */QUnit.jsDump = (function() {	function quote( str ) {		return '"' + str.toString().replace( /"/g, '\\"' ) + '"';	}	function literal( o ) {		return o + "";	}	function join( pre, arr, post ) {		var s = jsDump.separator(),			base = jsDump.indent(),			inner = jsDump.indent(1);		if ( arr.join ) {			arr = arr.join( "," + s + inner );		}		if ( !arr ) {			return pre + post;		}		return [ pre, inner + arr, base + post ].join(s);	}	function array( arr, stack ) {		var i = arr.length, ret = new Array(i);		this.up();		while ( i-- ) {			ret[i] = this.parse( arr[i] , undefined , stack);		}		this.down();		return join( "[", ret, "]" );	}	var reName = /^function (\w+)/,		jsDump = {			// type is used mostly internally, you can fix a (custom)type in advance			parse: function( obj, type, stack ) {				stack = stack || [ ];				var inStack, res,					parser = this.parsers[ type || this.typeOf(obj) ];				type = typeof parser;				inStack = inArray( obj, stack );				if ( inStack !== -1 ) {					return "recursion(" + (inStack - stack.length) + ")";				}				if ( type === "function" )  {					stack.push( obj );					res = parser.call( this, obj, stack );					stack.pop();					return res;				}				return ( type === "string" ) ? parser : this.parsers.error;			},			typeOf: function( obj ) {				var type;				if ( obj === null ) {					type = "null";				} else if ( typeof obj === "undefined" ) {					type = "undefined";				} else if ( QUnit.is( "regexp", obj) ) {					type = "regexp";				} else if ( QUnit.is( "date", obj) ) {					type = "date";				} else if ( QUnit.is( "function", obj) ) {					type = "function";				} else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {					type = "window";				} else if ( obj.nodeType === 9 ) {					type = "document";				} else if ( obj.nodeType ) {					type = "node";				} else if (					// native arrays					toString.call( obj ) === "[object Array]" ||					// NodeList objects					( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )				) {					type = "array";				} else if ( obj.constructor === Error.prototype.constructor ) {					type = "error";				} else {					type = typeof obj;				}				return type;			},			separator: function() {				return this.multiline ?	this.HTML ? "<br />" : "\n" : this.HTML ? " " : " ";			},			// extra can be a number, shortcut for increasing-calling-decreasing			indent: function( extra ) {				if ( !this.multiline ) {					return "";				}				var chr = this.indentChar;				if ( this.HTML ) {					chr = chr.replace( /\t/g, "   " ).replace( / /g, " " );				}				return new Array( this._depth_ + (extra||0) ).join(chr);			},			up: function( a ) {				this._depth_ += a || 1;			},			down: function( a ) {				this._depth_ -= a || 1;			},			setParser: function( name, parser ) {				this.parsers[name] = parser;			},			// The next 3 are exposed so you can use them			quote: quote,			literal: literal,			join: join,			//			_depth_: 1,			// This is the list of parsers, to modify them, use jsDump.setParser			parsers: {				window: "[Window]",				document: "[Document]",				error: function(error) {					return "Error(\"" + error.message + "\")";				},				unknown: "[Unknown]",				"null": "null",				"undefined": "undefined",				"function": function( fn ) {					var ret = "function",						// functions never have name in IE						name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];					if ( name ) {						ret += " " + name;					}					ret += "( ";					ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" );					return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" );				},				array: array,				nodelist: array,				"arguments": array,				object: function( map, stack ) {					var ret = [ ], keys, key, val, i;					QUnit.jsDump.up();					keys = [];					for ( key in map ) {						keys.push( key );					}					keys.sort();					for ( i = 0; i < keys.length; i++ ) {						key = keys[ i ];						val = map[ key ];						ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) );					}					QUnit.jsDump.down();					return join( "{", ret, "}" );				},				node: function( node ) {					var len, i, val,						open = QUnit.jsDump.HTML ? "<" : "<",						close = QUnit.jsDump.HTML ? ">" : ">",						tag = node.nodeName.toLowerCase(),						ret = open + tag,						attrs = node.attributes;					if ( attrs ) {						for ( i = 0, len = attrs.length; i < len; i++ ) {							val = attrs[i].nodeValue;							// IE6 includes all attributes in .attributes, even ones not explicitly set.							// Those have values like undefined, null, 0, false, "" or "inherit".							if ( val && val !== "inherit" ) {								ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" );							}						}					}					ret += close;					// Show content of TextNode or CDATASection					if ( node.nodeType === 3 || node.nodeType === 4 ) {						ret += node.nodeValue;					}					return ret + open + "/" + tag + close;				},				// function calls it internally, it's the arguments part of the function				functionArgs: function( fn ) {					var args,						l = fn.length;					if ( !l ) {						return "";					}					args = new Array(l);					while ( l-- ) {						// 97 is 'a'						args[l] = String.fromCharCode(97+l);					}					return " " + args.join( ", " ) + " ";				},				// object calls it internally, the key part of an item in a map				key: quote,				// function calls it internally, it's the content of the function				functionCode: "[code]",				// node calls it internally, it's an html attribute value				attribute: quote,				string: quote,				date: quote,				regexp: literal,				number: literal,				"boolean": literal			},			// if true, entities are escaped ( <, >, \t, space and \n )			HTML: false,			// indentation unit			indentChar: "  ",			// if true, items in a collection, are separated by a \n, else just a space.			multiline: true		};	return jsDump;}());// from jquery.jsfunction inArray( elem, array ) {	if ( array.indexOf ) {		return array.indexOf( elem );	}	for ( var i = 0, length = array.length; i < length; i++ ) {		if ( array[ i ] === elem ) {			return i;		}	}	return -1;}/* * Javascript Diff Algorithm *  By John Resig (http://ejohn.org/) *  Modified by Chu Alan "sprite" * * Released under the MIT license. * * More Info: *  http://ejohn.org/projects/javascript-diff-algorithm/ * * Usage: QUnit.diff(expected, actual) * * 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" */QUnit.diff = (function() {	/*jshint eqeqeq:false, eqnull:true */	function diff( o, n ) {		var i,			ns = {},			os = {};		for ( i = 0; i < n.length; i++ ) {			if ( !hasOwn.call( ns, n[i] ) ) {				ns[ n[i] ] = {					rows: [],					o: null				};			}			ns[ n[i] ].rows.push( i );		}		for ( i = 0; i < o.length; i++ ) {			if ( !hasOwn.call( os, o[i] ) ) {				os[ o[i] ] = {					rows: [],					n: null				};			}			os[ o[i] ].rows.push( i );		}		for ( i in ns ) {			if ( !hasOwn.call( ns, i ) ) {				continue;			}			if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) {				n[ ns[i].rows[0] ] = {					text: n[ ns[i].rows[0] ],					row: os[i].rows[0]				};				o[ os[i].rows[0] ] = {					text: o[ os[i].rows[0] ],					row: ns[i].rows[0]				};			}		}		for ( i = 0; i < n.length - 1; i++ ) {			if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&						n[ i + 1 ] == o[ n[i].row + 1 ] ) {				n[ i + 1 ] = {					text: n[ i + 1 ],					row: n[i].row + 1				};				o[ n[i].row + 1 ] = {					text: o[ n[i].row + 1 ],					row: i + 1				};			}		}		for ( i = n.length - 1; i > 0; i-- ) {			if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&						n[ i - 1 ] == o[ n[i].row - 1 ]) {				n[ i - 1 ] = {					text: n[ i - 1 ],					row: n[i].row - 1				};				o[ n[i].row - 1 ] = {					text: o[ n[i].row - 1 ],					row: i - 1				};			}		}		return {			o: o,			n: n		};	}	return function( o, n ) {		o = o.replace( /\s+$/, "" );		n = n.replace( /\s+$/, "" );		var i, pre,			str = "",			out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),			oSpace = o.match(/\s+/g),			nSpace = n.match(/\s+/g);		if ( oSpace == null ) {			oSpace = [ " " ];		}		else {			oSpace.push( " " );		}		if ( nSpace == null ) {			nSpace = [ " " ];		}		else {			nSpace.push( " " );		}		if ( out.n.length === 0 ) {			for ( i = 0; i < out.o.length; i++ ) {				str += "<del>" + out.o[i] + oSpace[i] + "</del>";			}		}		else {			if ( out.n[0].text == null ) {				for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {					str += "<del>" + out.o[n] + oSpace[n] + "</del>";				}			}			for ( i = 0; i < out.n.length; i++ ) {				if (out.n[i].text == null) {					str += "<ins>" + out.n[i] + nSpace[i] + "</ins>";				}				else {					// `pre` initialized at top of scope					pre = "";					for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {						pre += "<del>" + out.o[n] + oSpace[n] + "</del>";					}					str += " " + out.n[i].text + nSpace[i] + pre;				}			}		}		return str;	};}());// for CommonJS environments, export everythingif ( typeof exports !== "undefined" ) {	extend( exports, QUnit );}// get at whatever the global object is, like window in browsers}( (function() {return this;}.call()) ));
 |