Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
| Download
Project: KOB1
Views: 16973/**1* QUnit v1.12.0 - A JavaScript Unit Testing Framework2*3* http://qunitjs.com4*5* Copyright 2013 jQuery Foundation and other contributors6* Released under the MIT license.7* https://jquery.org/license/8*/910(function( window ) {1112var QUnit,13assert,14config,15onErrorFnPrev,16testId = 0,17fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),18toString = Object.prototype.toString,19hasOwn = Object.prototype.hasOwnProperty,20// Keep a local reference to Date (GH-283)21Date = window.Date,22setTimeout = window.setTimeout,23defined = {24setTimeout: typeof window.setTimeout !== "undefined",25sessionStorage: (function() {26var x = "qunit-test-string";27try {28sessionStorage.setItem( x, x );29sessionStorage.removeItem( x );30return true;31} catch( e ) {32return false;33}34}())35},36/**37* Provides a normalized error string, correcting an issue38* with IE 7 (and prior) where Error.prototype.toString is39* not properly implemented40*41* Based on http://es5.github.com/#x15.11.4.442*43* @param {String|Error} error44* @return {String} error message45*/46errorString = function( error ) {47var name, message,48errorString = error.toString();49if ( errorString.substring( 0, 7 ) === "[object" ) {50name = error.name ? error.name.toString() : "Error";51message = error.message ? error.message.toString() : "";52if ( name && message ) {53return name + ": " + message;54} else if ( name ) {55return name;56} else if ( message ) {57return message;58} else {59return "Error";60}61} else {62return errorString;63}64},65/**66* Makes a clone of an object using only Array or Object as base,67* and copies over the own enumerable properties.68*69* @param {Object} obj70* @return {Object} New object with only the own properties (recursively).71*/72objectValues = function( obj ) {73// Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392.74/*jshint newcap: false */75var key, val,76vals = QUnit.is( "array", obj ) ? [] : {};77for ( key in obj ) {78if ( hasOwn.call( obj, key ) ) {79val = obj[key];80vals[key] = val === Object(val) ? objectValues(val) : val;81}82}83return vals;84};8586function Test( settings ) {87extend( this, settings );88this.assertions = [];89this.testNumber = ++Test.count;90}9192Test.count = 0;9394Test.prototype = {95init: function() {96var a, b, li,97tests = id( "qunit-tests" );9899if ( tests ) {100b = document.createElement( "strong" );101b.innerHTML = this.nameHtml;102103// `a` initialized at top of scope104a = document.createElement( "a" );105a.innerHTML = "Rerun";106a.href = QUnit.url({ testNumber: this.testNumber });107108li = document.createElement( "li" );109li.appendChild( b );110li.appendChild( a );111li.className = "running";112li.id = this.id = "qunit-test-output" + testId++;113114tests.appendChild( li );115}116},117setup: function() {118if (119// Emit moduleStart when we're switching from one module to another120this.module !== config.previousModule ||121// They could be equal (both undefined) but if the previousModule property doesn't122// yet exist it means this is the first test in a suite that isn't wrapped in a123// module, in which case we'll just emit a moduleStart event for 'undefined'.124// Without this, reporters can get testStart before moduleStart which is a problem.125!hasOwn.call( config, "previousModule" )126) {127if ( hasOwn.call( config, "previousModule" ) ) {128runLoggingCallbacks( "moduleDone", QUnit, {129name: config.previousModule,130failed: config.moduleStats.bad,131passed: config.moduleStats.all - config.moduleStats.bad,132total: config.moduleStats.all133});134}135config.previousModule = this.module;136config.moduleStats = { all: 0, bad: 0 };137runLoggingCallbacks( "moduleStart", QUnit, {138name: this.module139});140}141142config.current = this;143144this.testEnvironment = extend({145setup: function() {},146teardown: function() {}147}, this.moduleTestEnvironment );148149this.started = +new Date();150runLoggingCallbacks( "testStart", QUnit, {151name: this.testName,152module: this.module153});154155/*jshint camelcase:false */156157158/**159* Expose the current test environment.160*161* @deprecated since 1.12.0: Use QUnit.config.current.testEnvironment instead.162*/163QUnit.current_testEnvironment = this.testEnvironment;164165/*jshint camelcase:true */166167if ( !config.pollution ) {168saveGlobal();169}170if ( config.notrycatch ) {171this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );172return;173}174try {175this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );176} catch( e ) {177QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );178}179},180run: function() {181config.current = this;182183var running = id( "qunit-testresult" );184185if ( running ) {186running.innerHTML = "Running: <br/>" + this.nameHtml;187}188189if ( this.async ) {190QUnit.stop();191}192193this.callbackStarted = +new Date();194195if ( config.notrycatch ) {196this.callback.call( this.testEnvironment, QUnit.assert );197this.callbackRuntime = +new Date() - this.callbackStarted;198return;199}200201try {202this.callback.call( this.testEnvironment, QUnit.assert );203this.callbackRuntime = +new Date() - this.callbackStarted;204} catch( e ) {205this.callbackRuntime = +new Date() - this.callbackStarted;206207QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );208// else next test will carry the responsibility209saveGlobal();210211// Restart the tests if they're blocking212if ( config.blocking ) {213QUnit.start();214}215}216},217teardown: function() {218config.current = this;219if ( config.notrycatch ) {220if ( typeof this.callbackRuntime === "undefined" ) {221this.callbackRuntime = +new Date() - this.callbackStarted;222}223this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );224return;225} else {226try {227this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );228} catch( e ) {229QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );230}231}232checkPollution();233},234finish: function() {235config.current = this;236if ( config.requireExpects && this.expected === null ) {237QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );238} else if ( this.expected !== null && this.expected !== this.assertions.length ) {239QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );240} else if ( this.expected === null && !this.assertions.length ) {241QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );242}243244var i, assertion, a, b, time, li, ol,245test = this,246good = 0,247bad = 0,248tests = id( "qunit-tests" );249250this.runtime = +new Date() - this.started;251config.stats.all += this.assertions.length;252config.moduleStats.all += this.assertions.length;253254if ( tests ) {255ol = document.createElement( "ol" );256ol.className = "qunit-assert-list";257258for ( i = 0; i < this.assertions.length; i++ ) {259assertion = this.assertions[i];260261li = document.createElement( "li" );262li.className = assertion.result ? "pass" : "fail";263li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );264ol.appendChild( li );265266if ( assertion.result ) {267good++;268} else {269bad++;270config.stats.bad++;271config.moduleStats.bad++;272}273}274275// store result when possible276if ( QUnit.config.reorder && defined.sessionStorage ) {277if ( bad ) {278sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );279} else {280sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );281}282}283284if ( bad === 0 ) {285addClass( ol, "qunit-collapsed" );286}287288// `b` initialized at top of scope289b = document.createElement( "strong" );290b.innerHTML = this.nameHtml + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";291292addEvent(b, "click", function() {293var next = b.parentNode.lastChild,294collapsed = hasClass( next, "qunit-collapsed" );295( collapsed ? removeClass : addClass )( next, "qunit-collapsed" );296});297298addEvent(b, "dblclick", function( e ) {299var target = e && e.target ? e.target : window.event.srcElement;300if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) {301target = target.parentNode;302}303if ( window.location && target.nodeName.toLowerCase() === "strong" ) {304window.location = QUnit.url({ testNumber: test.testNumber });305}306});307308// `time` initialized at top of scope309time = document.createElement( "span" );310time.className = "runtime";311time.innerHTML = this.runtime + " ms";312313// `li` initialized at top of scope314li = id( this.id );315li.className = bad ? "fail" : "pass";316li.removeChild( li.firstChild );317a = li.firstChild;318li.appendChild( b );319li.appendChild( a );320li.appendChild( time );321li.appendChild( ol );322323} else {324for ( i = 0; i < this.assertions.length; i++ ) {325if ( !this.assertions[i].result ) {326bad++;327config.stats.bad++;328config.moduleStats.bad++;329}330}331}332333runLoggingCallbacks( "testDone", QUnit, {334name: this.testName,335module: this.module,336failed: bad,337passed: this.assertions.length - bad,338total: this.assertions.length,339duration: this.runtime340});341342QUnit.reset();343344config.current = undefined;345},346347queue: function() {348var bad,349test = this;350351synchronize(function() {352test.init();353});354function run() {355// each of these can by async356synchronize(function() {357test.setup();358});359synchronize(function() {360test.run();361});362synchronize(function() {363test.teardown();364});365synchronize(function() {366test.finish();367});368}369370// `bad` initialized at top of scope371// defer when previous test run passed, if storage is available372bad = QUnit.config.reorder && defined.sessionStorage &&373+sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );374375if ( bad ) {376run();377} else {378synchronize( run, true );379}380}381};382383// Root QUnit object.384// `QUnit` initialized at top of scope385QUnit = {386387// call on start of module test to prepend name to all tests388module: function( name, testEnvironment ) {389config.currentModule = name;390config.currentModuleTestEnvironment = testEnvironment;391config.modules[name] = true;392},393394asyncTest: function( testName, expected, callback ) {395if ( arguments.length === 2 ) {396callback = expected;397expected = null;398}399400QUnit.test( testName, expected, callback, true );401},402403test: function( testName, expected, callback, async ) {404var test,405nameHtml = "<span class='test-name'>" + escapeText( testName ) + "</span>";406407if ( arguments.length === 2 ) {408callback = expected;409expected = null;410}411412if ( config.currentModule ) {413nameHtml = "<span class='module-name'>" + escapeText( config.currentModule ) + "</span>: " + nameHtml;414}415416test = new Test({417nameHtml: nameHtml,418testName: testName,419expected: expected,420async: async,421callback: callback,422module: config.currentModule,423moduleTestEnvironment: config.currentModuleTestEnvironment,424stack: sourceFromStacktrace( 2 )425});426427if ( !validTest( test ) ) {428return;429}430431test.queue();432},433434// Specify the number of expected assertions to guarantee that failed test (no assertions are run at all) don't slip through.435expect: function( asserts ) {436if (arguments.length === 1) {437config.current.expected = asserts;438} else {439return config.current.expected;440}441},442443start: function( count ) {444// QUnit hasn't been initialized yet.445// Note: RequireJS (et al) may delay onLoad446if ( config.semaphore === undefined ) {447QUnit.begin(function() {448// This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first449setTimeout(function() {450QUnit.start( count );451});452});453return;454}455456config.semaphore -= count || 1;457// don't start until equal number of stop-calls458if ( config.semaphore > 0 ) {459return;460}461// ignore if start is called more often then stop462if ( config.semaphore < 0 ) {463config.semaphore = 0;464QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) );465return;466}467// A slight delay, to avoid any current callbacks468if ( defined.setTimeout ) {469setTimeout(function() {470if ( config.semaphore > 0 ) {471return;472}473if ( config.timeout ) {474clearTimeout( config.timeout );475}476477config.blocking = false;478process( true );479}, 13);480} else {481config.blocking = false;482process( true );483}484},485486stop: function( count ) {487config.semaphore += count || 1;488config.blocking = true;489490if ( config.testTimeout && defined.setTimeout ) {491clearTimeout( config.timeout );492config.timeout = setTimeout(function() {493QUnit.ok( false, "Test timed out" );494config.semaphore = 1;495QUnit.start();496}, config.testTimeout );497}498}499};500501// `assert` initialized at top of scope502// Assert helpers503// All of these must either call QUnit.push() or manually do:504// - runLoggingCallbacks( "log", .. );505// - config.current.assertions.push({ .. });506// We attach it to the QUnit object *after* we expose the public API,507// otherwise `assert` will become a global variable in browsers (#341).508assert = {509/**510* Asserts rough true-ish result.511* @name ok512* @function513* @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );514*/515ok: function( result, msg ) {516if ( !config.current ) {517throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );518}519result = !!result;520msg = msg || (result ? "okay" : "failed" );521522var source,523details = {524module: config.current.module,525name: config.current.testName,526result: result,527message: msg528};529530msg = "<span class='test-message'>" + escapeText( msg ) + "</span>";531532if ( !result ) {533source = sourceFromStacktrace( 2 );534if ( source ) {535details.source = source;536msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr></table>";537}538}539runLoggingCallbacks( "log", QUnit, details );540config.current.assertions.push({541result: result,542message: msg543});544},545546/**547* Assert that the first two arguments are equal, with an optional message.548* Prints out both actual and expected values.549* @name equal550* @function551* @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );552*/553equal: function( actual, expected, message ) {554/*jshint eqeqeq:false */555QUnit.push( expected == actual, actual, expected, message );556},557558/**559* @name notEqual560* @function561*/562notEqual: function( actual, expected, message ) {563/*jshint eqeqeq:false */564QUnit.push( expected != actual, actual, expected, message );565},566567/**568* @name propEqual569* @function570*/571propEqual: function( actual, expected, message ) {572actual = objectValues(actual);573expected = objectValues(expected);574QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );575},576577/**578* @name notPropEqual579* @function580*/581notPropEqual: function( actual, expected, message ) {582actual = objectValues(actual);583expected = objectValues(expected);584QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );585},586587/**588* @name deepEqual589* @function590*/591deepEqual: function( actual, expected, message ) {592QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );593},594595/**596* @name notDeepEqual597* @function598*/599notDeepEqual: function( actual, expected, message ) {600QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );601},602603/**604* @name strictEqual605* @function606*/607strictEqual: function( actual, expected, message ) {608QUnit.push( expected === actual, actual, expected, message );609},610611/**612* @name notStrictEqual613* @function614*/615notStrictEqual: function( actual, expected, message ) {616QUnit.push( expected !== actual, actual, expected, message );617},618619"throws": function( block, expected, message ) {620var actual,621expectedOutput = expected,622ok = false;623624// 'expected' is optional625if ( typeof expected === "string" ) {626message = expected;627expected = null;628}629630config.current.ignoreGlobalErrors = true;631try {632block.call( config.current.testEnvironment );633} catch (e) {634actual = e;635}636config.current.ignoreGlobalErrors = false;637638if ( actual ) {639// we don't want to validate thrown error640if ( !expected ) {641ok = true;642expectedOutput = null;643// expected is a regexp644} else if ( QUnit.objectType( expected ) === "regexp" ) {645ok = expected.test( errorString( actual ) );646// expected is a constructor647} else if ( actual instanceof expected ) {648ok = true;649// expected is a validation function which returns true is validation passed650} else if ( expected.call( {}, actual ) === true ) {651expectedOutput = null;652ok = true;653}654655QUnit.push( ok, actual, expectedOutput, message );656} else {657QUnit.pushFailure( message, null, "No exception was thrown." );658}659}660};661662/**663* @deprecated since 1.8.0664* Kept assertion helpers in root for backwards compatibility.665*/666extend( QUnit, assert );667668/**669* @deprecated since 1.9.0670* Kept root "raises()" for backwards compatibility.671* (Note that we don't introduce assert.raises).672*/673QUnit.raises = assert[ "throws" ];674675/**676* @deprecated since 1.0.0, replaced with error pushes since 1.3.0677* Kept to avoid TypeErrors for undefined methods.678*/679QUnit.equals = function() {680QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );681};682QUnit.same = function() {683QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );684};685686// We want access to the constructor's prototype687(function() {688function F() {}689F.prototype = QUnit;690QUnit = new F();691// Make F QUnit's constructor so that we can add to the prototype later692QUnit.constructor = F;693}());694695/**696* Config object: Maintain internal state697* Later exposed as QUnit.config698* `config` initialized at top of scope699*/700config = {701// The queue of tests to run702queue: [],703704// block until document ready705blocking: true,706707// when enabled, show only failing tests708// gets persisted through sessionStorage and can be changed in UI via checkbox709hidepassed: false,710711// by default, run previously failed tests first712// very useful in combination with "Hide passed tests" checked713reorder: true,714715// by default, modify document.title when suite is done716altertitle: true,717718// when enabled, all tests must call expect()719requireExpects: false,720721// add checkboxes that are persisted in the query-string722// when enabled, the id is set to `true` as a `QUnit.config` property723urlConfig: [724{725id: "noglobals",726label: "Check for Globals",727tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."728},729{730id: "notrycatch",731label: "No try-catch",732tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."733}734],735736// Set of all modules.737modules: {},738739// logging callback queues740begin: [],741done: [],742log: [],743testStart: [],744testDone: [],745moduleStart: [],746moduleDone: []747};748749// Export global variables, unless an 'exports' object exists,750// in that case we assume we're in CommonJS (dealt with on the bottom of the script)751if ( typeof exports === "undefined" ) {752extend( window, QUnit.constructor.prototype );753754// Expose QUnit object755window.QUnit = QUnit;756}757758// Initialize more QUnit.config and QUnit.urlParams759(function() {760var i,761location = window.location || { search: "", protocol: "file:" },762params = location.search.slice( 1 ).split( "&" ),763length = params.length,764urlParams = {},765current;766767if ( params[ 0 ] ) {768for ( i = 0; i < length; i++ ) {769current = params[ i ].split( "=" );770current[ 0 ] = decodeURIComponent( current[ 0 ] );771// allow just a key to turn on a flag, e.g., test.html?noglobals772current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;773urlParams[ current[ 0 ] ] = current[ 1 ];774}775}776777QUnit.urlParams = urlParams;778779// String search anywhere in moduleName+testName780config.filter = urlParams.filter;781782// Exact match of the module name783config.module = urlParams.module;784785config.testNumber = parseInt( urlParams.testNumber, 10 ) || null;786787// Figure out if we're running the tests from a server or not788QUnit.isLocal = location.protocol === "file:";789}());790791// Extend QUnit object,792// these after set here because they should not be exposed as global functions793extend( QUnit, {794assert: assert,795796config: config,797798// Initialize the configuration options799init: function() {800extend( config, {801stats: { all: 0, bad: 0 },802moduleStats: { all: 0, bad: 0 },803started: +new Date(),804updateRate: 1000,805blocking: false,806autostart: true,807autorun: false,808filter: "",809queue: [],810semaphore: 1811});812813var tests, banner, result,814qunit = id( "qunit" );815816if ( qunit ) {817qunit.innerHTML =818"<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +819"<h2 id='qunit-banner'></h2>" +820"<div id='qunit-testrunner-toolbar'></div>" +821"<h2 id='qunit-userAgent'></h2>" +822"<ol id='qunit-tests'></ol>";823}824825tests = id( "qunit-tests" );826banner = id( "qunit-banner" );827result = id( "qunit-testresult" );828829if ( tests ) {830tests.innerHTML = "";831}832833if ( banner ) {834banner.className = "";835}836837if ( result ) {838result.parentNode.removeChild( result );839}840841if ( tests ) {842result = document.createElement( "p" );843result.id = "qunit-testresult";844result.className = "result";845tests.parentNode.insertBefore( result, tests );846result.innerHTML = "Running...<br/> ";847}848},849850// Resets the test setup. Useful for tests that modify the DOM.851/*852DEPRECATED: Use multiple tests instead of resetting inside a test.853Use testStart or testDone for custom cleanup.854This method will throw an error in 2.0, and will be removed in 2.1855*/856reset: function() {857var fixture = id( "qunit-fixture" );858if ( fixture ) {859fixture.innerHTML = config.fixture;860}861},862863// Trigger an event on an element.864// @example triggerEvent( document.body, "click" );865triggerEvent: function( elem, type, event ) {866if ( document.createEvent ) {867event = document.createEvent( "MouseEvents" );868event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,8690, 0, 0, 0, 0, false, false, false, false, 0, null);870871elem.dispatchEvent( event );872} else if ( elem.fireEvent ) {873elem.fireEvent( "on" + type );874}875},876877// Safe object type checking878is: function( type, obj ) {879return QUnit.objectType( obj ) === type;880},881882objectType: function( obj ) {883if ( typeof obj === "undefined" ) {884return "undefined";885// consider: typeof null === object886}887if ( obj === null ) {888return "null";889}890891var match = toString.call( obj ).match(/^\[object\s(.*)\]$/),892type = match && match[1] || "";893894switch ( type ) {895case "Number":896if ( isNaN(obj) ) {897return "nan";898}899return "number";900case "String":901case "Boolean":902case "Array":903case "Date":904case "RegExp":905case "Function":906return type.toLowerCase();907}908if ( typeof obj === "object" ) {909return "object";910}911return undefined;912},913914push: function( result, actual, expected, message ) {915if ( !config.current ) {916throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );917}918919var output, source,920details = {921module: config.current.module,922name: config.current.testName,923result: result,924message: message,925actual: actual,926expected: expected927};928929message = escapeText( message ) || ( result ? "okay" : "failed" );930message = "<span class='test-message'>" + message + "</span>";931output = message;932933if ( !result ) {934expected = escapeText( QUnit.jsDump.parse(expected) );935actual = escapeText( QUnit.jsDump.parse(actual) );936output += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + expected + "</pre></td></tr>";937938if ( actual !== expected ) {939output += "<tr class='test-actual'><th>Result: </th><td><pre>" + actual + "</pre></td></tr>";940output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>";941}942943source = sourceFromStacktrace();944945if ( source ) {946details.source = source;947output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";948}949950output += "</table>";951}952953runLoggingCallbacks( "log", QUnit, details );954955config.current.assertions.push({956result: !!result,957message: output958});959},960961pushFailure: function( message, source, actual ) {962if ( !config.current ) {963throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );964}965966var output,967details = {968module: config.current.module,969name: config.current.testName,970result: false,971message: message972};973974message = escapeText( message ) || "error";975message = "<span class='test-message'>" + message + "</span>";976output = message;977978output += "<table>";979980if ( actual ) {981output += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText( actual ) + "</pre></td></tr>";982}983984if ( source ) {985details.source = source;986output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";987}988989output += "</table>";990991runLoggingCallbacks( "log", QUnit, details );992993config.current.assertions.push({994result: false,995message: output996});997},998999url: function( params ) {1000params = extend( extend( {}, QUnit.urlParams ), params );1001var key,1002querystring = "?";10031004for ( key in params ) {1005if ( hasOwn.call( params, key ) ) {1006querystring += encodeURIComponent( key ) + "=" +1007encodeURIComponent( params[ key ] ) + "&";1008}1009}1010return window.location.protocol + "//" + window.location.host +1011window.location.pathname + querystring.slice( 0, -1 );1012},10131014extend: extend,1015id: id,1016addEvent: addEvent,1017addClass: addClass,1018hasClass: hasClass,1019removeClass: removeClass1020// load, equiv, jsDump, diff: Attached later1021});10221023/**1024* @deprecated: Created for backwards compatibility with test runner that set the hook function1025* into QUnit.{hook}, instead of invoking it and passing the hook function.1026* QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.1027* Doing this allows us to tell if the following methods have been overwritten on the actual1028* QUnit object.1029*/1030extend( QUnit.constructor.prototype, {10311032// Logging callbacks; all receive a single argument with the listed properties1033// run test/logs.html for any related changes1034begin: registerLoggingCallback( "begin" ),10351036// done: { failed, passed, total, runtime }1037done: registerLoggingCallback( "done" ),10381039// log: { result, actual, expected, message }1040log: registerLoggingCallback( "log" ),10411042// testStart: { name }1043testStart: registerLoggingCallback( "testStart" ),10441045// testDone: { name, failed, passed, total, duration }1046testDone: registerLoggingCallback( "testDone" ),10471048// moduleStart: { name }1049moduleStart: registerLoggingCallback( "moduleStart" ),10501051// moduleDone: { name, failed, passed, total }1052moduleDone: registerLoggingCallback( "moduleDone" )1053});10541055if ( typeof document === "undefined" || document.readyState === "complete" ) {1056config.autorun = true;1057}10581059QUnit.load = function() {1060runLoggingCallbacks( "begin", QUnit, {} );10611062// Initialize the config, saving the execution queue1063var banner, filter, i, label, len, main, ol, toolbar, userAgent, val,1064urlConfigCheckboxesContainer, urlConfigCheckboxes, moduleFilter,1065numModules = 0,1066moduleNames = [],1067moduleFilterHtml = "",1068urlConfigHtml = "",1069oldconfig = extend( {}, config );10701071QUnit.init();1072extend(config, oldconfig);10731074config.blocking = false;10751076len = config.urlConfig.length;10771078for ( i = 0; i < len; i++ ) {1079val = config.urlConfig[i];1080if ( typeof val === "string" ) {1081val = {1082id: val,1083label: val,1084tooltip: "[no tooltip available]"1085};1086}1087config[ val.id ] = QUnit.urlParams[ val.id ];1088urlConfigHtml += "<input id='qunit-urlconfig-" + escapeText( val.id ) +1089"' name='" + escapeText( val.id ) +1090"' type='checkbox'" + ( config[ val.id ] ? " checked='checked'" : "" ) +1091" title='" + escapeText( val.tooltip ) +1092"'><label for='qunit-urlconfig-" + escapeText( val.id ) +1093"' title='" + escapeText( val.tooltip ) + "'>" + val.label + "</label>";1094}1095for ( i in config.modules ) {1096if ( config.modules.hasOwnProperty( i ) ) {1097moduleNames.push(i);1098}1099}1100numModules = moduleNames.length;1101moduleNames.sort( function( a, b ) {1102return a.localeCompare( b );1103});1104moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label><select id='qunit-modulefilter' name='modulefilter'><option value='' " +1105( config.module === undefined ? "selected='selected'" : "" ) +1106">< All Modules ></option>";110711081109for ( i = 0; i < numModules; i++) {1110moduleFilterHtml += "<option value='" + escapeText( encodeURIComponent(moduleNames[i]) ) + "' " +1111( config.module === moduleNames[i] ? "selected='selected'" : "" ) +1112">" + escapeText(moduleNames[i]) + "</option>";1113}1114moduleFilterHtml += "</select>";11151116// `userAgent` initialized at top of scope1117userAgent = id( "qunit-userAgent" );1118if ( userAgent ) {1119userAgent.innerHTML = navigator.userAgent;1120}11211122// `banner` initialized at top of scope1123banner = id( "qunit-header" );1124if ( banner ) {1125banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) + "'>" + banner.innerHTML + "</a> ";1126}11271128// `toolbar` initialized at top of scope1129toolbar = id( "qunit-testrunner-toolbar" );1130if ( toolbar ) {1131// `filter` initialized at top of scope1132filter = document.createElement( "input" );1133filter.type = "checkbox";1134filter.id = "qunit-filter-pass";11351136addEvent( filter, "click", function() {1137var tmp,1138ol = document.getElementById( "qunit-tests" );11391140if ( filter.checked ) {1141ol.className = ol.className + " hidepass";1142} else {1143tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";1144ol.className = tmp.replace( / hidepass /, " " );1145}1146if ( defined.sessionStorage ) {1147if (filter.checked) {1148sessionStorage.setItem( "qunit-filter-passed-tests", "true" );1149} else {1150sessionStorage.removeItem( "qunit-filter-passed-tests" );1151}1152}1153});11541155if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {1156filter.checked = true;1157// `ol` initialized at top of scope1158ol = document.getElementById( "qunit-tests" );1159ol.className = ol.className + " hidepass";1160}1161toolbar.appendChild( filter );11621163// `label` initialized at top of scope1164label = document.createElement( "label" );1165label.setAttribute( "for", "qunit-filter-pass" );1166label.setAttribute( "title", "Only show tests and assertions that fail. Stored in sessionStorage." );1167label.innerHTML = "Hide passed tests";1168toolbar.appendChild( label );11691170urlConfigCheckboxesContainer = document.createElement("span");1171urlConfigCheckboxesContainer.innerHTML = urlConfigHtml;1172urlConfigCheckboxes = urlConfigCheckboxesContainer.getElementsByTagName("input");1173// For oldIE support:1174// * Add handlers to the individual elements instead of the container1175// * Use "click" instead of "change"1176// * Fallback from event.target to event.srcElement1177addEvents( urlConfigCheckboxes, "click", function( event ) {1178var params = {},1179target = event.target || event.srcElement;1180params[ target.name ] = target.checked ? true : undefined;1181window.location = QUnit.url( params );1182});1183toolbar.appendChild( urlConfigCheckboxesContainer );11841185if (numModules > 1) {1186moduleFilter = document.createElement( "span" );1187moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );1188moduleFilter.innerHTML = moduleFilterHtml;1189addEvent( moduleFilter.lastChild, "change", function() {1190var selectBox = moduleFilter.getElementsByTagName("select")[0],1191selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value);11921193window.location = QUnit.url({1194module: ( selectedModule === "" ) ? undefined : selectedModule,1195// Remove any existing filters1196filter: undefined,1197testNumber: undefined1198});1199});1200toolbar.appendChild(moduleFilter);1201}1202}12031204// `main` initialized at top of scope1205main = id( "qunit-fixture" );1206if ( main ) {1207config.fixture = main.innerHTML;1208}12091210if ( config.autostart ) {1211QUnit.start();1212}1213};12141215addEvent( window, "load", QUnit.load );12161217// `onErrorFnPrev` initialized at top of scope1218// Preserve other handlers1219onErrorFnPrev = window.onerror;12201221// Cover uncaught exceptions1222// Returning true will suppress the default browser handler,1223// returning false will let it run.1224window.onerror = function ( error, filePath, linerNr ) {1225var ret = false;1226if ( onErrorFnPrev ) {1227ret = onErrorFnPrev( error, filePath, linerNr );1228}12291230// Treat return value as window.onerror itself does,1231// Only do our handling if not suppressed.1232if ( ret !== true ) {1233if ( QUnit.config.current ) {1234if ( QUnit.config.current.ignoreGlobalErrors ) {1235return true;1236}1237QUnit.pushFailure( error, filePath + ":" + linerNr );1238} else {1239QUnit.test( "global failure", extend( function() {1240QUnit.pushFailure( error, filePath + ":" + linerNr );1241}, { validTest: validTest } ) );1242}1243return false;1244}12451246return ret;1247};12481249function done() {1250config.autorun = true;12511252// Log the last module results1253if ( config.currentModule ) {1254runLoggingCallbacks( "moduleDone", QUnit, {1255name: config.currentModule,1256failed: config.moduleStats.bad,1257passed: config.moduleStats.all - config.moduleStats.bad,1258total: config.moduleStats.all1259});1260}1261delete config.previousModule;12621263var i, key,1264banner = id( "qunit-banner" ),1265tests = id( "qunit-tests" ),1266runtime = +new Date() - config.started,1267passed = config.stats.all - config.stats.bad,1268html = [1269"Tests completed in ",1270runtime,1271" milliseconds.<br/>",1272"<span class='passed'>",1273passed,1274"</span> assertions of <span class='total'>",1275config.stats.all,1276"</span> passed, <span class='failed'>",1277config.stats.bad,1278"</span> failed."1279].join( "" );12801281if ( banner ) {1282banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );1283}12841285if ( tests ) {1286id( "qunit-testresult" ).innerHTML = html;1287}12881289if ( config.altertitle && typeof document !== "undefined" && document.title ) {1290// show ✖ for good, ✔ for bad suite result in title1291// use escape sequences in case file gets loaded with non-utf-8-charset1292document.title = [1293( config.stats.bad ? "\u2716" : "\u2714" ),1294document.title.replace( /^[\u2714\u2716] /i, "" )1295].join( " " );1296}12971298// clear own sessionStorage items if all tests passed1299if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {1300// `key` & `i` initialized at top of scope1301for ( i = 0; i < sessionStorage.length; i++ ) {1302key = sessionStorage.key( i++ );1303if ( key.indexOf( "qunit-test-" ) === 0 ) {1304sessionStorage.removeItem( key );1305}1306}1307}13081309// scroll back to top to show results1310if ( window.scrollTo ) {1311window.scrollTo(0, 0);1312}13131314runLoggingCallbacks( "done", QUnit, {1315failed: config.stats.bad,1316passed: passed,1317total: config.stats.all,1318runtime: runtime1319});1320}13211322/** @return Boolean: true if this test should be ran */1323function validTest( test ) {1324var include,1325filter = config.filter && config.filter.toLowerCase(),1326module = config.module && config.module.toLowerCase(),1327fullName = (test.module + ": " + test.testName).toLowerCase();13281329// Internally-generated tests are always valid1330if ( test.callback && test.callback.validTest === validTest ) {1331delete test.callback.validTest;1332return true;1333}13341335if ( config.testNumber ) {1336return test.testNumber === config.testNumber;1337}13381339if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {1340return false;1341}13421343if ( !filter ) {1344return true;1345}13461347include = filter.charAt( 0 ) !== "!";1348if ( !include ) {1349filter = filter.slice( 1 );1350}13511352// If the filter matches, we need to honour include1353if ( fullName.indexOf( filter ) !== -1 ) {1354return include;1355}13561357// Otherwise, do the opposite1358return !include;1359}13601361// so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)1362// Later Safari and IE10 are supposed to support error.stack as well1363// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack1364function extractStacktrace( e, offset ) {1365offset = offset === undefined ? 3 : offset;13661367var stack, include, i;13681369if ( e.stacktrace ) {1370// Opera1371return e.stacktrace.split( "\n" )[ offset + 3 ];1372} else if ( e.stack ) {1373// Firefox, Chrome1374stack = e.stack.split( "\n" );1375if (/^error$/i.test( stack[0] ) ) {1376stack.shift();1377}1378if ( fileName ) {1379include = [];1380for ( i = offset; i < stack.length; i++ ) {1381if ( stack[ i ].indexOf( fileName ) !== -1 ) {1382break;1383}1384include.push( stack[ i ] );1385}1386if ( include.length ) {1387return include.join( "\n" );1388}1389}1390return stack[ offset ];1391} else if ( e.sourceURL ) {1392// Safari, PhantomJS1393// hopefully one day Safari provides actual stacktraces1394// exclude useless self-reference for generated Error objects1395if ( /qunit.js$/.test( e.sourceURL ) ) {1396return;1397}1398// for actual exceptions, this is useful1399return e.sourceURL + ":" + e.line;1400}1401}1402function sourceFromStacktrace( offset ) {1403try {1404throw new Error();1405} catch ( e ) {1406return extractStacktrace( e, offset );1407}1408}14091410/**1411* Escape text for attribute or text content.1412*/1413function escapeText( s ) {1414if ( !s ) {1415return "";1416}1417s = s + "";1418// Both single quotes and double quotes (for attributes)1419return s.replace( /['"<>&]/g, function( s ) {1420switch( s ) {1421case "'":1422return "'";1423case "\"":1424return """;1425case "<":1426return "<";1427case ">":1428return ">";1429case "&":1430return "&";1431}1432});1433}14341435function synchronize( callback, last ) {1436config.queue.push( callback );14371438if ( config.autorun && !config.blocking ) {1439process( last );1440}1441}14421443function process( last ) {1444function next() {1445process( last );1446}1447var start = new Date().getTime();1448config.depth = config.depth ? config.depth + 1 : 1;14491450while ( config.queue.length && !config.blocking ) {1451if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {1452config.queue.shift()();1453} else {1454setTimeout( next, 13 );1455break;1456}1457}1458config.depth--;1459if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {1460done();1461}1462}14631464function saveGlobal() {1465config.pollution = [];14661467if ( config.noglobals ) {1468for ( var key in window ) {1469if ( hasOwn.call( window, key ) ) {1470// in Opera sometimes DOM element ids show up here, ignore them1471if ( /^qunit-test-output/.test( key ) ) {1472continue;1473}1474config.pollution.push( key );1475}1476}1477}1478}14791480function checkPollution() {1481var newGlobals,1482deletedGlobals,1483old = config.pollution;14841485saveGlobal();14861487newGlobals = diff( config.pollution, old );1488if ( newGlobals.length > 0 ) {1489QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );1490}14911492deletedGlobals = diff( old, config.pollution );1493if ( deletedGlobals.length > 0 ) {1494QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );1495}1496}14971498// returns a new Array with the elements that are in a but not in b1499function diff( a, b ) {1500var i, j,1501result = a.slice();15021503for ( i = 0; i < result.length; i++ ) {1504for ( j = 0; j < b.length; j++ ) {1505if ( result[i] === b[j] ) {1506result.splice( i, 1 );1507i--;1508break;1509}1510}1511}1512return result;1513}15141515function extend( a, b ) {1516for ( var prop in b ) {1517if ( hasOwn.call( b, prop ) ) {1518// Avoid "Member not found" error in IE8 caused by messing with window.constructor1519if ( !( prop === "constructor" && a === window ) ) {1520if ( b[ prop ] === undefined ) {1521delete a[ prop ];1522} else {1523a[ prop ] = b[ prop ];1524}1525}1526}1527}15281529return a;1530}15311532/**1533* @param {HTMLElement} elem1534* @param {string} type1535* @param {Function} fn1536*/1537function addEvent( elem, type, fn ) {1538// Standards-based browsers1539if ( elem.addEventListener ) {1540elem.addEventListener( type, fn, false );1541// IE1542} else {1543elem.attachEvent( "on" + type, fn );1544}1545}15461547/**1548* @param {Array|NodeList} elems1549* @param {string} type1550* @param {Function} fn1551*/1552function addEvents( elems, type, fn ) {1553var i = elems.length;1554while ( i-- ) {1555addEvent( elems[i], type, fn );1556}1557}15581559function hasClass( elem, name ) {1560return (" " + elem.className + " ").indexOf(" " + name + " ") > -1;1561}15621563function addClass( elem, name ) {1564if ( !hasClass( elem, name ) ) {1565elem.className += (elem.className ? " " : "") + name;1566}1567}15681569function removeClass( elem, name ) {1570var set = " " + elem.className + " ";1571// Class name may appear multiple times1572while ( set.indexOf(" " + name + " ") > -1 ) {1573set = set.replace(" " + name + " " , " ");1574}1575// If possible, trim it for prettiness, but not necessarily1576elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");1577}15781579function id( name ) {1580return !!( typeof document !== "undefined" && document && document.getElementById ) &&1581document.getElementById( name );1582}15831584function registerLoggingCallback( key ) {1585return function( callback ) {1586config[key].push( callback );1587};1588}15891590// Supports deprecated method of completely overwriting logging callbacks1591function runLoggingCallbacks( key, scope, args ) {1592var i, callbacks;1593if ( QUnit.hasOwnProperty( key ) ) {1594QUnit[ key ].call(scope, args );1595} else {1596callbacks = config[ key ];1597for ( i = 0; i < callbacks.length; i++ ) {1598callbacks[ i ].call( scope, args );1599}1600}1601}16021603// Test for equality any JavaScript type.1604// Author: Philippe Rathé <[email protected]>1605QUnit.equiv = (function() {16061607// Call the o related callback with the given arguments.1608function bindCallbacks( o, callbacks, args ) {1609var prop = QUnit.objectType( o );1610if ( prop ) {1611if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {1612return callbacks[ prop ].apply( callbacks, args );1613} else {1614return callbacks[ prop ]; // or undefined1615}1616}1617}16181619// the real equiv function1620var innerEquiv,1621// stack to decide between skip/abort functions1622callers = [],1623// stack to avoiding loops from circular referencing1624parents = [],1625parentsB = [],16261627getProto = Object.getPrototypeOf || function ( obj ) {1628/*jshint camelcase:false */1629return obj.__proto__;1630},1631callbacks = (function () {16321633// for string, boolean, number and null1634function useStrictEquality( b, a ) {1635/*jshint eqeqeq:false */1636if ( b instanceof a.constructor || a instanceof b.constructor ) {1637// to catch short annotation VS 'new' annotation of a1638// declaration1639// e.g. var i = 1;1640// var j = new Number(1);1641return a == b;1642} else {1643return a === b;1644}1645}16461647return {1648"string": useStrictEquality,1649"boolean": useStrictEquality,1650"number": useStrictEquality,1651"null": useStrictEquality,1652"undefined": useStrictEquality,16531654"nan": function( b ) {1655return isNaN( b );1656},16571658"date": function( b, a ) {1659return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();1660},16611662"regexp": function( b, a ) {1663return QUnit.objectType( b ) === "regexp" &&1664// the regex itself1665a.source === b.source &&1666// and its modifiers1667a.global === b.global &&1668// (gmi) ...1669a.ignoreCase === b.ignoreCase &&1670a.multiline === b.multiline &&1671a.sticky === b.sticky;1672},16731674// - skip when the property is a method of an instance (OOP)1675// - abort otherwise,1676// initial === would have catch identical references anyway1677"function": function() {1678var caller = callers[callers.length - 1];1679return caller !== Object && typeof caller !== "undefined";1680},16811682"array": function( b, a ) {1683var i, j, len, loop, aCircular, bCircular;16841685// b could be an object literal here1686if ( QUnit.objectType( b ) !== "array" ) {1687return false;1688}16891690len = a.length;1691if ( len !== b.length ) {1692// safe and faster1693return false;1694}16951696// track reference to avoid circular references1697parents.push( a );1698parentsB.push( b );1699for ( i = 0; i < len; i++ ) {1700loop = false;1701for ( j = 0; j < parents.length; j++ ) {1702aCircular = parents[j] === a[i];1703bCircular = parentsB[j] === b[i];1704if ( aCircular || bCircular ) {1705if ( a[i] === b[i] || aCircular && bCircular ) {1706loop = true;1707} else {1708parents.pop();1709parentsB.pop();1710return false;1711}1712}1713}1714if ( !loop && !innerEquiv(a[i], b[i]) ) {1715parents.pop();1716parentsB.pop();1717return false;1718}1719}1720parents.pop();1721parentsB.pop();1722return true;1723},17241725"object": function( b, a ) {1726/*jshint forin:false */1727var i, j, loop, aCircular, bCircular,1728// Default to true1729eq = true,1730aProperties = [],1731bProperties = [];17321733// comparing constructors is more strict than using1734// instanceof1735if ( a.constructor !== b.constructor ) {1736// Allow objects with no prototype to be equivalent to1737// objects with Object as their constructor.1738if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||1739( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {1740return false;1741}1742}17431744// stack constructor before traversing properties1745callers.push( a.constructor );17461747// track reference to avoid circular references1748parents.push( a );1749parentsB.push( b );17501751// be strict: don't ensure hasOwnProperty and go deep1752for ( i in a ) {1753loop = false;1754for ( j = 0; j < parents.length; j++ ) {1755aCircular = parents[j] === a[i];1756bCircular = parentsB[j] === b[i];1757if ( aCircular || bCircular ) {1758if ( a[i] === b[i] || aCircular && bCircular ) {1759loop = true;1760} else {1761eq = false;1762break;1763}1764}1765}1766aProperties.push(i);1767if ( !loop && !innerEquiv(a[i], b[i]) ) {1768eq = false;1769break;1770}1771}17721773parents.pop();1774parentsB.pop();1775callers.pop(); // unstack, we are done17761777for ( i in b ) {1778bProperties.push( i ); // collect b's properties1779}17801781// Ensures identical properties name1782return eq && innerEquiv( aProperties.sort(), bProperties.sort() );1783}1784};1785}());17861787innerEquiv = function() { // can take multiple arguments1788var args = [].slice.apply( arguments );1789if ( args.length < 2 ) {1790return true; // end transition1791}17921793return (function( a, b ) {1794if ( a === b ) {1795return true; // catch the most you can1796} else if ( a === null || b === null || typeof a === "undefined" ||1797typeof b === "undefined" ||1798QUnit.objectType(a) !== QUnit.objectType(b) ) {1799return false; // don't lose time with error prone cases1800} else {1801return bindCallbacks(a, callbacks, [ b, a ]);1802}18031804// apply transition with (1..n) arguments1805}( args[0], args[1] ) && innerEquiv.apply( this, args.splice(1, args.length - 1 )) );1806};18071808return innerEquiv;1809}());18101811/**1812* jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |1813* http://flesler.blogspot.com Licensed under BSD1814* (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/20081815*1816* @projectDescription Advanced and extensible data dumping for Javascript.1817* @version 1.0.01818* @author Ariel Flesler1819* @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}1820*/1821QUnit.jsDump = (function() {1822function quote( str ) {1823return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";1824}1825function literal( o ) {1826return o + "";1827}1828function join( pre, arr, post ) {1829var s = jsDump.separator(),1830base = jsDump.indent(),1831inner = jsDump.indent(1);1832if ( arr.join ) {1833arr = arr.join( "," + s + inner );1834}1835if ( !arr ) {1836return pre + post;1837}1838return [ pre, inner + arr, base + post ].join(s);1839}1840function array( arr, stack ) {1841var i = arr.length, ret = new Array(i);1842this.up();1843while ( i-- ) {1844ret[i] = this.parse( arr[i] , undefined , stack);1845}1846this.down();1847return join( "[", ret, "]" );1848}18491850var reName = /^function (\w+)/,1851jsDump = {1852// type is used mostly internally, you can fix a (custom)type in advance1853parse: function( obj, type, stack ) {1854stack = stack || [ ];1855var inStack, res,1856parser = this.parsers[ type || this.typeOf(obj) ];18571858type = typeof parser;1859inStack = inArray( obj, stack );18601861if ( inStack !== -1 ) {1862return "recursion(" + (inStack - stack.length) + ")";1863}1864if ( type === "function" ) {1865stack.push( obj );1866res = parser.call( this, obj, stack );1867stack.pop();1868return res;1869}1870return ( type === "string" ) ? parser : this.parsers.error;1871},1872typeOf: function( obj ) {1873var type;1874if ( obj === null ) {1875type = "null";1876} else if ( typeof obj === "undefined" ) {1877type = "undefined";1878} else if ( QUnit.is( "regexp", obj) ) {1879type = "regexp";1880} else if ( QUnit.is( "date", obj) ) {1881type = "date";1882} else if ( QUnit.is( "function", obj) ) {1883type = "function";1884} else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {1885type = "window";1886} else if ( obj.nodeType === 9 ) {1887type = "document";1888} else if ( obj.nodeType ) {1889type = "node";1890} else if (1891// native arrays1892toString.call( obj ) === "[object Array]" ||1893// NodeList objects1894( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )1895) {1896type = "array";1897} else if ( obj.constructor === Error.prototype.constructor ) {1898type = "error";1899} else {1900type = typeof obj;1901}1902return type;1903},1904separator: function() {1905return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? " " : " ";1906},1907// extra can be a number, shortcut for increasing-calling-decreasing1908indent: function( extra ) {1909if ( !this.multiline ) {1910return "";1911}1912var chr = this.indentChar;1913if ( this.HTML ) {1914chr = chr.replace( /\t/g, " " ).replace( / /g, " " );1915}1916return new Array( this.depth + ( extra || 0 ) ).join(chr);1917},1918up: function( a ) {1919this.depth += a || 1;1920},1921down: function( a ) {1922this.depth -= a || 1;1923},1924setParser: function( name, parser ) {1925this.parsers[name] = parser;1926},1927// The next 3 are exposed so you can use them1928quote: quote,1929literal: literal,1930join: join,1931//1932depth: 1,1933// This is the list of parsers, to modify them, use jsDump.setParser1934parsers: {1935window: "[Window]",1936document: "[Document]",1937error: function(error) {1938return "Error(\"" + error.message + "\")";1939},1940unknown: "[Unknown]",1941"null": "null",1942"undefined": "undefined",1943"function": function( fn ) {1944var ret = "function",1945// functions never have name in IE1946name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];19471948if ( name ) {1949ret += " " + name;1950}1951ret += "( ";19521953ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" );1954return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" );1955},1956array: array,1957nodelist: array,1958"arguments": array,1959object: function( map, stack ) {1960/*jshint forin:false */1961var ret = [ ], keys, key, val, i;1962QUnit.jsDump.up();1963keys = [];1964for ( key in map ) {1965keys.push( key );1966}1967keys.sort();1968for ( i = 0; i < keys.length; i++ ) {1969key = keys[ i ];1970val = map[ key ];1971ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) );1972}1973QUnit.jsDump.down();1974return join( "{", ret, "}" );1975},1976node: function( node ) {1977var len, i, val,1978open = QUnit.jsDump.HTML ? "<" : "<",1979close = QUnit.jsDump.HTML ? ">" : ">",1980tag = node.nodeName.toLowerCase(),1981ret = open + tag,1982attrs = node.attributes;19831984if ( attrs ) {1985for ( i = 0, len = attrs.length; i < len; i++ ) {1986val = attrs[i].nodeValue;1987// IE6 includes all attributes in .attributes, even ones not explicitly set.1988// Those have values like undefined, null, 0, false, "" or "inherit".1989if ( val && val !== "inherit" ) {1990ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" );1991}1992}1993}1994ret += close;19951996// Show content of TextNode or CDATASection1997if ( node.nodeType === 3 || node.nodeType === 4 ) {1998ret += node.nodeValue;1999}20002001return ret + open + "/" + tag + close;2002},2003// function calls it internally, it's the arguments part of the function2004functionArgs: function( fn ) {2005var args,2006l = fn.length;20072008if ( !l ) {2009return "";2010}20112012args = new Array(l);2013while ( l-- ) {2014// 97 is 'a'2015args[l] = String.fromCharCode(97+l);2016}2017return " " + args.join( ", " ) + " ";2018},2019// object calls it internally, the key part of an item in a map2020key: quote,2021// function calls it internally, it's the content of the function2022functionCode: "[code]",2023// node calls it internally, it's an html attribute value2024attribute: quote,2025string: quote,2026date: quote,2027regexp: literal,2028number: literal,2029"boolean": literal2030},2031// if true, entities are escaped ( <, >, \t, space and \n )2032HTML: false,2033// indentation unit2034indentChar: " ",2035// if true, items in a collection, are separated by a \n, else just a space.2036multiline: true2037};20382039return jsDump;2040}());20412042// from jquery.js2043function inArray( elem, array ) {2044if ( array.indexOf ) {2045return array.indexOf( elem );2046}20472048for ( var i = 0, length = array.length; i < length; i++ ) {2049if ( array[ i ] === elem ) {2050return i;2051}2052}20532054return -1;2055}20562057/*2058* Javascript Diff Algorithm2059* By John Resig (http://ejohn.org/)2060* Modified by Chu Alan "sprite"2061*2062* Released under the MIT license.2063*2064* More Info:2065* http://ejohn.org/projects/javascript-diff-algorithm/2066*2067* Usage: QUnit.diff(expected, actual)2068*2069* 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"2070*/2071QUnit.diff = (function() {2072/*jshint eqeqeq:false, eqnull:true */2073function diff( o, n ) {2074var i,2075ns = {},2076os = {};20772078for ( i = 0; i < n.length; i++ ) {2079if ( !hasOwn.call( ns, n[i] ) ) {2080ns[ n[i] ] = {2081rows: [],2082o: null2083};2084}2085ns[ n[i] ].rows.push( i );2086}20872088for ( i = 0; i < o.length; i++ ) {2089if ( !hasOwn.call( os, o[i] ) ) {2090os[ o[i] ] = {2091rows: [],2092n: null2093};2094}2095os[ o[i] ].rows.push( i );2096}20972098for ( i in ns ) {2099if ( hasOwn.call( ns, i ) ) {2100if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) {2101n[ ns[i].rows[0] ] = {2102text: n[ ns[i].rows[0] ],2103row: os[i].rows[0]2104};2105o[ os[i].rows[0] ] = {2106text: o[ os[i].rows[0] ],2107row: ns[i].rows[0]2108};2109}2110}2111}21122113for ( i = 0; i < n.length - 1; i++ ) {2114if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&2115n[ i + 1 ] == o[ n[i].row + 1 ] ) {21162117n[ i + 1 ] = {2118text: n[ i + 1 ],2119row: n[i].row + 12120};2121o[ n[i].row + 1 ] = {2122text: o[ n[i].row + 1 ],2123row: i + 12124};2125}2126}21272128for ( i = n.length - 1; i > 0; i-- ) {2129if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&2130n[ i - 1 ] == o[ n[i].row - 1 ]) {21312132n[ i - 1 ] = {2133text: n[ i - 1 ],2134row: n[i].row - 12135};2136o[ n[i].row - 1 ] = {2137text: o[ n[i].row - 1 ],2138row: i - 12139};2140}2141}21422143return {2144o: o,2145n: n2146};2147}21482149return function( o, n ) {2150o = o.replace( /\s+$/, "" );2151n = n.replace( /\s+$/, "" );21522153var i, pre,2154str = "",2155out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),2156oSpace = o.match(/\s+/g),2157nSpace = n.match(/\s+/g);21582159if ( oSpace == null ) {2160oSpace = [ " " ];2161}2162else {2163oSpace.push( " " );2164}21652166if ( nSpace == null ) {2167nSpace = [ " " ];2168}2169else {2170nSpace.push( " " );2171}21722173if ( out.n.length === 0 ) {2174for ( i = 0; i < out.o.length; i++ ) {2175str += "<del>" + out.o[i] + oSpace[i] + "</del>";2176}2177}2178else {2179if ( out.n[0].text == null ) {2180for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {2181str += "<del>" + out.o[n] + oSpace[n] + "</del>";2182}2183}21842185for ( i = 0; i < out.n.length; i++ ) {2186if (out.n[i].text == null) {2187str += "<ins>" + out.n[i] + nSpace[i] + "</ins>";2188}2189else {2190// `pre` initialized at top of scope2191pre = "";21922193for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {2194pre += "<del>" + out.o[n] + oSpace[n] + "</del>";2195}2196str += " " + out.n[i].text + nSpace[i] + pre;2197}2198}2199}22002201return str;2202};2203}());22042205// for CommonJS environments, export everything2206if ( typeof exports !== "undefined" ) {2207extend( exports, QUnit.constructor.prototype );2208}22092210// get at whatever the global object is, like window in browsers2211}( (function() {return this;}.call()) ));22122213