react / wstein / node_modules / jest-cli / node_modules / cover / node_modules / underscore.string / test / test_underscore / vendor / qunit.js
80698 views/*1* QUnit - A JavaScript Unit Testing Framework2*3* http://docs.jquery.com/QUnit4*5* Copyright (c) 2009 John Resig, Jörn Zaefferer6* Dual licensed under the MIT (MIT-LICENSE.txt)7* and GPL (GPL-LICENSE.txt) licenses.8*/910(function(window) {1112var QUnit = {1314// Initialize the configuration options15init: function init() {16config = {17stats: { all: 0, bad: 0 },18moduleStats: { all: 0, bad: 0 },19started: +new Date,20blocking: false,21autorun: false,22assertions: [],23filters: [],24queue: []25};2627var tests = id("qunit-tests"),28banner = id("qunit-banner"),29result = id("qunit-testresult");3031if ( tests ) {32tests.innerHTML = "";33}3435if ( banner ) {36banner.className = "";37}3839if ( result ) {40result.parentNode.removeChild( result );41}42},4344// call on start of module test to prepend name to all tests45module: function module(name, testEnvironment) {4647synchronize(function() {48if ( config.currentModule ) {49QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );50}5152config.currentModule = name;53config.moduleTestEnvironment = testEnvironment;54config.moduleStats = { all: 0, bad: 0 };5556QUnit.moduleStart( name, testEnvironment );57});58},5960asyncTest: function asyncTest(testName, expected, callback) {61if ( arguments.length === 2 ) {62callback = expected;63expected = 0;64}6566QUnit.test(testName, expected, callback, true);67},6869test: function test(testName, expected, callback, async) {70var name = testName, testEnvironment = {};7172if ( arguments.length === 2 ) {73callback = expected;74expected = null;75}7677if ( config.currentModule ) {78name = config.currentModule + " module: " + name;79}8081if ( !validTest(name) ) {82return;83}8485synchronize(function() {86QUnit.testStart( testName );8788testEnvironment = extend({89setup: function() {},90teardown: function() {}91}, config.moduleTestEnvironment);9293config.assertions = [];94config.expected = null;9596if ( arguments.length >= 3 ) {97config.expected = callback;98callback = arguments[2];99}100101try {102if ( !config.pollution ) {103saveGlobal();104}105106testEnvironment.setup.call(testEnvironment);107} catch(e) {108QUnit.ok( false, "Setup failed on " + name + ": " + e.message );109}110111if ( async ) {112QUnit.stop();113}114115try {116callback.call(testEnvironment);117} catch(e) {118fail("Test " + name + " died, exception and test follows", e, callback);119QUnit.ok( false, "Died on test #" + (config.assertions.length + 1) + ": " + e.message );120// else next test will carry the responsibility121saveGlobal();122123// Restart the tests if they're blocking124if ( config.blocking ) {125start();126}127}128});129130synchronize(function() {131try {132checkPollution();133testEnvironment.teardown.call(testEnvironment);134} catch(e) {135QUnit.ok( false, "Teardown failed on " + name + ": " + e.message );136}137138try {139QUnit.reset();140} catch(e) {141fail("reset() failed, following Test " + name + ", exception and reset fn follows", e, reset);142}143144if ( config.expected && config.expected != config.assertions.length ) {145QUnit.ok( false, "Expected " + config.expected + " assertions, but " + config.assertions.length + " were run" );146}147148var good = 0, bad = 0,149tests = id("qunit-tests");150151config.stats.all += config.assertions.length;152config.moduleStats.all += config.assertions.length;153154if ( tests ) {155var ol = document.createElement("ol");156ol.style.display = "none";157158for ( var i = 0; i < config.assertions.length; i++ ) {159var assertion = config.assertions[i];160161var li = document.createElement("li");162li.className = assertion.result ? "pass" : "fail";163li.innerHTML = assertion.message || "(no message)";164ol.appendChild( li );165166if ( assertion.result ) {167good++;168} else {169bad++;170config.stats.bad++;171config.moduleStats.bad++;172}173}174175var b = document.createElement("strong");176b.innerHTML = name + " <b style='color:black;'>(<b class='fail'>" + bad + "</b>, <b class='pass'>" + good + "</b>, " + config.assertions.length + ")</b>";177178addEvent(b, "click", function() {179var next = b.nextSibling, display = next.style.display;180next.style.display = display === "none" ? "block" : "none";181});182183addEvent(b, "dblclick", function(e) {184var target = (e || window.event).target;185if ( target.nodeName.toLowerCase() === "strong" ) {186var text = "", node = target.firstChild;187188while ( node.nodeType === 3 ) {189text += node.nodeValue;190node = node.nextSibling;191}192193text = text.replace(/(^\s*|\s*$)/g, "");194195if ( window.location ) {196window.location.href = window.location.href.match(/^(.+?)(\?.*)?$/)[1] + "?" + encodeURIComponent(text);197}198}199});200201var li = document.createElement("li");202li.className = bad ? "fail" : "pass";203li.appendChild( b );204li.appendChild( ol );205tests.appendChild( li );206207if ( bad ) {208var toolbar = id("qunit-testrunner-toolbar");209if ( toolbar ) {210toolbar.style.display = "block";211id("qunit-filter-pass").disabled = null;212id("qunit-filter-missing").disabled = null;213}214}215216} else {217for ( var i = 0; i < config.assertions.length; i++ ) {218if ( !config.assertions[i].result ) {219bad++;220config.stats.bad++;221config.moduleStats.bad++;222}223}224}225226QUnit.testDone( testName, bad, config.assertions.length );227228if ( !window.setTimeout && !config.queue.length ) {229done();230}231});232233if ( window.setTimeout && !config.doneTimer ) {234config.doneTimer = window.setTimeout(function(){235if ( !config.queue.length ) {236done();237} else {238synchronize( done );239}240}, 13);241}242},243244/**245* Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.246*/247expect: function expect(asserts) {248config.expected = asserts;249},250251/**252* Asserts true.253* @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );254*/255ok: function ok(a, msg) {256QUnit.log(a, msg);257258config.assertions.push({259result: !!a,260message: msg261});262},263264/**265* Checks that the first two arguments are equal, with an optional message.266* Prints out both actual and expected values.267*268* Prefered to ok( actual == expected, message )269*270* @example equals( format("Received {0} bytes.", 2), "Received 2 bytes." );271*272* @param Object actual273* @param Object expected274* @param String message (optional)275*/276equals: function equals(actual, expected, message) {277push(expected == actual, actual, expected, message);278},279280same: function(a, b, message) {281push(QUnit.equiv(a, b), a, b, message);282},283284start: function start() {285// A slight delay, to avoid any current callbacks286if ( window.setTimeout ) {287window.setTimeout(function() {288if ( config.timeout ) {289clearTimeout(config.timeout);290}291292config.blocking = false;293process();294}, 13);295} else {296config.blocking = false;297process();298}299},300301stop: function stop(timeout) {302config.blocking = true;303304if ( timeout && window.setTimeout ) {305config.timeout = window.setTimeout(function() {306QUnit.ok( false, "Test timed out" );307QUnit.start();308}, timeout);309}310},311312/**313* Resets the test setup. Useful for tests that modify the DOM.314*/315reset: function reset() {316if ( window.jQuery ) {317jQuery("#main").html( config.fixture );318jQuery.event.global = {};319jQuery.ajaxSettings = extend({}, config.ajaxSettings);320}321},322323/**324* Trigger an event on an element.325*326* @example triggerEvent( document.body, "click" );327*328* @param DOMElement elem329* @param String type330*/331triggerEvent: function triggerEvent( elem, type, event ) {332if ( document.createEvent ) {333event = document.createEvent("MouseEvents");334event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,3350, 0, 0, 0, 0, false, false, false, false, 0, null);336elem.dispatchEvent( event );337338} else if ( elem.fireEvent ) {339elem.fireEvent("on"+type);340}341},342343// Logging callbacks344done: function done(failures, total) {},345log: function log(result, message) {},346testStart: function testStart(name) {},347testDone: function testDone(name, failures, total) {},348moduleStart: function moduleStart(name, testEnvironment) {},349moduleDone: function moduleDone(name, failures, total) {}350};351352// Maintain internal state353var config = {354// The queue of tests to run355queue: [],356357// block until document ready358blocking: true359};360361// Load paramaters362(function() {363var location = window.location || { search: "", protocol: "file:" },364GETParams = location.search.slice(1).split('&');365366for ( var i = 0; i < GETParams.length; i++ ) {367GETParams[i] = decodeURIComponent( GETParams[i] );368if ( GETParams[i] === "noglobals" ) {369GETParams.splice( i, 1 );370i--;371config.noglobals = true;372}373}374375// restrict modules/tests by get parameters376config.filters = GETParams;377378// Figure out if we're running the tests from a server or not379QUnit.isLocal = !!(location.protocol === 'file:');380})();381382// Expose the API as global variables, unless an 'exports'383// object exists, in that case we assume we're in CommonJS384if ( typeof exports === "undefined" || typeof require === "undefined" ) {385extend(window, QUnit);386window.QUnit = QUnit;387} else {388extend(exports, QUnit);389exports.QUnit = QUnit;390}391392if ( typeof document === "undefined" || document.readyState === "complete" ) {393config.autorun = true;394}395396addEvent(window, "load", function() {397// Initialize the config, saving the execution queue398var oldconfig = extend({}, config);399QUnit.init();400extend(config, oldconfig);401402config.blocking = false;403404var userAgent = id("qunit-userAgent");405if ( userAgent ) {406userAgent.innerHTML = navigator.userAgent;407}408409var toolbar = id("qunit-testrunner-toolbar");410if ( toolbar ) {411toolbar.style.display = "none";412413var filter = document.createElement("input");414filter.type = "checkbox";415filter.id = "qunit-filter-pass";416filter.disabled = true;417addEvent( filter, "click", function() {418var li = document.getElementsByTagName("li");419for ( var i = 0; i < li.length; i++ ) {420if ( li[i].className.indexOf("pass") > -1 ) {421li[i].style.display = filter.checked ? "none" : "block";422}423}424});425toolbar.appendChild( filter );426427var label = document.createElement("label");428label.setAttribute("for", "filter-pass");429label.innerHTML = "Hide passed tests";430toolbar.appendChild( label );431432var missing = document.createElement("input");433missing.type = "checkbox";434missing.id = "qunit-filter-missing";435missing.disabled = true;436addEvent( missing, "click", function() {437var li = document.getElementsByTagName("li");438for ( var i = 0; i < li.length; i++ ) {439if ( li[i].className.indexOf("fail") > -1 && li[i].innerHTML.indexOf('missing test - untested code is broken code') > - 1 ) {440li[i].parentNode.parentNode.style.display = missing.checked ? "none" : "block";441}442}443});444toolbar.appendChild( missing );445446label = document.createElement("label");447label.setAttribute("for", "filter-missing");448label.innerHTML = "Hide missing tests (untested code is broken code)";449toolbar.appendChild( label );450}451452var main = id('main');453if ( main ) {454config.fixture = main.innerHTML;455}456457if ( window.jQuery ) {458config.ajaxSettings = window.jQuery.ajaxSettings;459}460461QUnit.start();462});463464function done() {465if ( config.doneTimer && window.clearTimeout ) {466window.clearTimeout( config.doneTimer );467config.doneTimer = null;468}469470if ( config.queue.length ) {471config.doneTimer = window.setTimeout(function(){472if ( !config.queue.length ) {473done();474} else {475synchronize( done );476}477}, 13);478479return;480}481482config.autorun = true;483484// Log the last module results485if ( config.currentModule ) {486QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );487}488489var banner = id("qunit-banner"),490tests = id("qunit-tests"),491html = ['Tests completed in ',492+new Date - config.started, ' milliseconds.<br/>',493'<span class="bad">', config.stats.all - config.stats.bad, '</span> tests of <span class="all">', config.stats.all, '</span> passed, ', config.stats.bad,' failed.'].join('');494495if ( banner ) {496banner.className += " " + (config.stats.bad ? "fail" : "pass");497}498499if ( tests ) {500var result = id("qunit-testresult");501502if ( !result ) {503result = document.createElement("p");504result.id = "qunit-testresult";505result.className = "result";506tests.parentNode.insertBefore( result, tests.nextSibling );507}508509result.innerHTML = html;510}511512QUnit.done( config.stats.bad, config.stats.all );513}514515function validTest( name ) {516var i = config.filters.length,517run = false;518519if ( !i ) {520return true;521}522523while ( i-- ) {524var filter = config.filters[i],525not = filter.charAt(0) == '!';526527if ( not ) {528filter = filter.slice(1);529}530531if ( name.indexOf(filter) !== -1 ) {532return !not;533}534535if ( not ) {536run = true;537}538}539540return run;541}542543function push(result, actual, expected, message) {544message = message || (result ? "okay" : "failed");545QUnit.ok( result, result ? message + ": " + expected : message + ", expected: " + QUnit.jsDump.parse(expected) + " result: " + QUnit.jsDump.parse(actual) );546}547548function synchronize( callback ) {549config.queue.push( callback );550551if ( config.autorun && !config.blocking ) {552process();553}554}555556function process() {557while ( config.queue.length && !config.blocking ) {558config.queue.shift()();559}560}561562function saveGlobal() {563config.pollution = [];564565if ( config.noglobals ) {566for ( var key in window ) {567config.pollution.push( key );568}569}570}571572function checkPollution( name ) {573var old = config.pollution;574saveGlobal();575576var newGlobals = diff( old, config.pollution );577if ( newGlobals.length > 0 ) {578ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );579config.expected++;580}581582var deletedGlobals = diff( config.pollution, old );583if ( deletedGlobals.length > 0 ) {584ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );585config.expected++;586}587}588589// returns a new Array with the elements that are in a but not in b590function diff( a, b ) {591var result = a.slice();592for ( var i = 0; i < result.length; i++ ) {593for ( var j = 0; j < b.length; j++ ) {594if ( result[i] === b[j] ) {595result.splice(i, 1);596i--;597break;598}599}600}601return result;602}603604function fail(message, exception, callback) {605if ( typeof console !== "undefined" && console.error && console.warn ) {606console.error(message);607console.error(exception);608console.warn(callback.toString());609610} else if ( window.opera && opera.postError ) {611opera.postError(message, exception, callback.toString);612}613}614615function extend(a, b) {616for ( var prop in b ) {617a[prop] = b[prop];618}619620return a;621}622623function addEvent(elem, type, fn) {624if ( elem.addEventListener ) {625elem.addEventListener( type, fn, false );626} else if ( elem.attachEvent ) {627elem.attachEvent( "on" + type, fn );628} else {629fn();630}631}632633function id(name) {634return !!(typeof document !== "undefined" && document && document.getElementById) &&635document.getElementById( name );636}637638// Test for equality any JavaScript type.639// Discussions and reference: http://philrathe.com/articles/equiv640// Test suites: http://philrathe.com/tests/equiv641// Author: Philippe Rathé <[email protected]>642QUnit.equiv = function () {643644var innerEquiv; // the real equiv function645var callers = []; // stack to decide between skip/abort functions646647648// Determine what is o.649function hoozit(o) {650if (o.constructor === String) {651return "string";652653} else if (o.constructor === Boolean) {654return "boolean";655656} else if (o.constructor === Number) {657658if (isNaN(o)) {659return "nan";660} else {661return "number";662}663664} else if (typeof o === "undefined") {665return "undefined";666667// consider: typeof null === object668} else if (o === null) {669return "null";670671// consider: typeof [] === object672} else if (o instanceof Array) {673return "array";674675// consider: typeof new Date() === object676} else if (o instanceof Date) {677return "date";678679// consider: /./ instanceof Object;680// /./ instanceof RegExp;681// typeof /./ === "function"; // => false in IE and Opera,682// true in FF and Safari683} else if (o instanceof RegExp) {684return "regexp";685686} else if (typeof o === "object") {687return "object";688689} else if (o instanceof Function) {690return "function";691} else {692return undefined;693}694}695696// Call the o related callback with the given arguments.697function handleEvents(o, callbacks, args) {698var prop = hoozit(o);699if (prop) {700if (hoozit(callbacks[prop]) === "function") {701return callbacks[prop].apply(callbacks, args);702} else {703return callbacks[prop]; // or undefined704}705}706}707708var callbacks = function () {709710// for string, boolean, number and null711function useStrictEquality(b, a) {712if (b instanceof a.constructor || a instanceof b.constructor) {713// to catch short annotaion VS 'new' annotation of a declaration714// e.g. var i = 1;715// var j = new Number(1);716return a == b;717} else {718return a === b;719}720}721722return {723"string": useStrictEquality,724"boolean": useStrictEquality,725"number": useStrictEquality,726"null": useStrictEquality,727"undefined": useStrictEquality,728729"nan": function (b) {730return isNaN(b);731},732733"date": function (b, a) {734return hoozit(b) === "date" && a.valueOf() === b.valueOf();735},736737"regexp": function (b, a) {738return hoozit(b) === "regexp" &&739a.source === b.source && // the regex itself740a.global === b.global && // and its modifers (gmi) ...741a.ignoreCase === b.ignoreCase &&742a.multiline === b.multiline;743},744745// - skip when the property is a method of an instance (OOP)746// - abort otherwise,747// initial === would have catch identical references anyway748"function": function () {749var caller = callers[callers.length - 1];750return caller !== Object &&751typeof caller !== "undefined";752},753754"array": function (b, a) {755var i;756var len;757758// b could be an object literal here759if ( ! (hoozit(b) === "array")) {760return false;761}762763len = a.length;764if (len !== b.length) { // safe and faster765return false;766}767for (i = 0; i < len; i++) {768if ( ! innerEquiv(a[i], b[i])) {769return false;770}771}772return true;773},774775"object": function (b, a) {776var i;777var eq = true; // unless we can proove it778var aProperties = [], bProperties = []; // collection of strings779780// comparing constructors is more strict than using instanceof781if ( a.constructor !== b.constructor) {782return false;783}784785// stack constructor before traversing properties786callers.push(a.constructor);787788for (i in a) { // be strict: don't ensures hasOwnProperty and go deep789790aProperties.push(i); // collect a's properties791792if ( ! innerEquiv(a[i], b[i])) {793eq = false;794}795}796797callers.pop(); // unstack, we are done798799for (i in b) {800bProperties.push(i); // collect b's properties801}802803// Ensures identical properties name804return eq && innerEquiv(aProperties.sort(), bProperties.sort());805}806};807}();808809innerEquiv = function () { // can take multiple arguments810var args = Array.prototype.slice.apply(arguments);811if (args.length < 2) {812return true; // end transition813}814815return (function (a, b) {816if (a === b) {817return true; // catch the most you can818} else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || hoozit(a) !== hoozit(b)) {819return false; // don't lose time with error prone cases820} else {821return handleEvents(a, callbacks, [b, a]);822}823824// apply transition with (1..n) arguments825})(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));826};827828return innerEquiv;829830}();831832/**833* jsDump834* Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com835* Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)836* Date: 5/15/2008837* @projectDescription Advanced and extensible data dumping for Javascript.838* @version 1.0.0839* @author Ariel Flesler840* @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}841*/842QUnit.jsDump = (function() {843function quote( str ) {844return '"' + str.toString().replace(/"/g, '\\"') + '"';845};846function literal( o ) {847return o + '';848};849function join( pre, arr, post ) {850var s = jsDump.separator(),851base = jsDump.indent(),852inner = jsDump.indent(1);853if ( arr.join )854arr = arr.join( ',' + s + inner );855if ( !arr )856return pre + post;857return [ pre, inner + arr, base + post ].join(s);858};859function array( arr ) {860var i = arr.length, ret = Array(i);861this.up();862while ( i-- )863ret[i] = this.parse( arr[i] );864this.down();865return join( '[', ret, ']' );866};867868var reName = /^function (\w+)/;869870var jsDump = {871parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance872var parser = this.parsers[ type || this.typeOf(obj) ];873type = typeof parser;874875return type == 'function' ? parser.call( this, obj ) :876type == 'string' ? parser :877this.parsers.error;878},879typeOf:function( obj ) {880var type = typeof obj,881f = 'function';//we'll use it 3 times, save it882return type != 'object' && type != f ? type :883!obj ? 'null' :884obj.exec ? 'regexp' :// some browsers (FF) consider regexps functions885obj.getHours ? 'date' :886obj.scrollBy ? 'window' :887obj.nodeName == '#document' ? 'document' :888obj.nodeName ? 'node' :889obj.item ? 'nodelist' : // Safari reports nodelists as functions890obj.callee ? 'arguments' :891obj.call || obj.constructor != Array && //an array would also fall on this hack892(obj+'').indexOf(f) != -1 ? f : //IE reports functions like alert, as objects893'length' in obj ? 'array' :894type;895},896separator:function() {897return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? ' ' : ' ';898},899indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing900if ( !this.multiline )901return '';902var chr = this.indentChar;903if ( this.HTML )904chr = chr.replace(/\t/g,' ').replace(/ /g,' ');905return Array( this._depth_ + (extra||0) ).join(chr);906},907up:function( a ) {908this._depth_ += a || 1;909},910down:function( a ) {911this._depth_ -= a || 1;912},913setParser:function( name, parser ) {914this.parsers[name] = parser;915},916// The next 3 are exposed so you can use them917quote:quote,918literal:literal,919join:join,920//921_depth_: 1,922// This is the list of parsers, to modify them, use jsDump.setParser923parsers:{924window: '[Window]',925document: '[Document]',926error:'[ERROR]', //when no parser is found, shouldn't happen927unknown: '[Unknown]',928'null':'null',929undefined:'undefined',930'function':function( fn ) {931var ret = 'function',932name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE933if ( name )934ret += ' ' + name;935ret += '(';936937ret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join('');938return join( ret, this.parse(fn,'functionCode'), '}' );939},940array: array,941nodelist: array,942arguments: array,943object:function( map ) {944var ret = [ ];945this.up();946for ( var key in map )947ret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) );948this.down();949return join( '{', ret, '}' );950},951node:function( node ) {952var open = this.HTML ? '<' : '<',953close = this.HTML ? '>' : '>';954955var tag = node.nodeName.toLowerCase(),956ret = open + tag;957958for ( var a in this.DOMAttrs ) {959var val = node[this.DOMAttrs[a]];960if ( val )961ret += ' ' + a + '=' + this.parse( val, 'attribute' );962}963return ret + close + open + '/' + tag + close;964},965functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function966var l = fn.length;967if ( !l ) return '';968969var args = Array(l);970while ( l-- )971args[l] = String.fromCharCode(97+l);//97 is 'a'972return ' ' + args.join(', ') + ' ';973},974key:quote, //object calls it internally, the key part of an item in a map975functionCode:'[code]', //function calls it internally, it's the content of the function976attribute:quote, //node calls it internally, it's an html attribute value977string:quote,978date:quote,979regexp:literal, //regex980number:literal,981'boolean':literal982},983DOMAttrs:{//attributes to dump from nodes, name=>realName984id:'id',985name:'name',986'class':'className'987},988HTML:true,//if true, entities are escaped ( <, >, \t, space and \n )989indentChar:' ',//indentation unit990multiline:true //if true, items in a collection, are separated by a \n, else just a space.991};992993return jsDump;994})();995996})(this);/*997* QUnit - A JavaScript Unit Testing Framework998*999* http://docs.jquery.com/QUnit1000*1001* Copyright (c) 2009 John Resig, Jörn Zaefferer1002* Dual licensed under the MIT (MIT-LICENSE.txt)1003* and GPL (GPL-LICENSE.txt) licenses.1004*/10051006(function(window) {10071008var defined = {1009setTimeout: typeof window.setTimeout !== "undefined",1010sessionStorage: (function() {1011try {1012return !!sessionStorage.getItem;1013} catch(e){1014return false;1015}1016})()1017}10181019var testId = 0;10201021var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {1022this.name = name;1023this.testName = testName;1024this.expected = expected;1025this.testEnvironmentArg = testEnvironmentArg;1026this.async = async;1027this.callback = callback;1028this.assertions = [];1029};1030Test.prototype = {1031init: function() {1032var tests = id("qunit-tests");1033if (tests) {1034var b = document.createElement("strong");1035b.innerHTML = "Running " + this.name;1036var li = document.createElement("li");1037li.appendChild( b );1038li.id = this.id = "test-output" + testId++;1039tests.appendChild( li );1040}1041},1042setup: function() {1043if (this.module != config.previousModule) {1044if ( this.previousModule ) {1045QUnit.moduleDone( this.module, config.moduleStats.bad, config.moduleStats.all );1046}1047config.previousModule = this.module;1048config.moduleStats = { all: 0, bad: 0 };1049QUnit.moduleStart( this.module, this.moduleTestEnvironment );1050}10511052config.current = this;1053this.testEnvironment = extend({1054setup: function() {},1055teardown: function() {}1056}, this.moduleTestEnvironment);1057if (this.testEnvironmentArg) {1058extend(this.testEnvironment, this.testEnvironmentArg);1059}10601061QUnit.testStart( this.testName, this.testEnvironment );10621063// allow utility functions to access the current test environment1064// TODO why??1065QUnit.current_testEnvironment = this.testEnvironment;10661067try {1068if ( !config.pollution ) {1069saveGlobal();1070}10711072this.testEnvironment.setup.call(this.testEnvironment);1073} catch(e) {1074// TODO use testName instead of name for no-markup message?1075QUnit.ok( false, "Setup failed on " + this.name + ": " + e.message );1076}1077},1078run: function() {1079if ( this.async ) {1080QUnit.stop();1081}10821083try {1084this.callback.call(this.testEnvironment);1085} catch(e) {1086// TODO use testName instead of name for no-markup message?1087fail("Test " + this.name + " died, exception and test follows", e, this.callback);1088QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) );1089// else next test will carry the responsibility1090saveGlobal();10911092// Restart the tests if they're blocking1093if ( config.blocking ) {1094start();1095}1096}1097},1098teardown: function() {1099try {1100checkPollution();1101this.testEnvironment.teardown.call(this.testEnvironment);1102} catch(e) {1103// TODO use testName instead of name for no-markup message?1104QUnit.ok( false, "Teardown failed on " + this.name + ": " + e.message );1105}1106},1107finish: function() {1108if ( this.expected && this.expected != this.assertions.length ) {1109QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );1110}11111112var good = 0, bad = 0,1113tests = id("qunit-tests");11141115config.stats.all += this.assertions.length;1116config.moduleStats.all += this.assertions.length;11171118if ( tests ) {1119var ol = document.createElement("ol");11201121for ( var i = 0; i < this.assertions.length; i++ ) {1122var assertion = this.assertions[i];11231124var li = document.createElement("li");1125li.className = assertion.result ? "pass" : "fail";1126li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed");1127ol.appendChild( li );11281129if ( assertion.result ) {1130good++;1131} else {1132bad++;1133config.stats.bad++;1134config.moduleStats.bad++;1135}1136}11371138// store result when possible1139defined.sessionStorage && sessionStorage.setItem("qunit-" + this.testName, bad);11401141if (bad == 0) {1142ol.style.display = "none";1143}11441145var b = document.createElement("strong");1146b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";11471148addEvent(b, "click", function() {1149var next = b.nextSibling, display = next.style.display;1150next.style.display = display === "none" ? "block" : "none";1151});11521153addEvent(b, "dblclick", function(e) {1154var target = e && e.target ? e.target : window.event.srcElement;1155if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {1156target = target.parentNode;1157}1158if ( window.location && target.nodeName.toLowerCase() === "strong" ) {1159window.location.search = "?" + encodeURIComponent(getText([target]).replace(/\(.+\)$/, "").replace(/(^\s*|\s*$)/g, ""));1160}1161});11621163var li = id(this.id);1164li.className = bad ? "fail" : "pass";1165li.style.display = resultDisplayStyle(!bad);1166li.removeChild( li.firstChild );1167li.appendChild( b );1168li.appendChild( ol );11691170if ( bad ) {1171var toolbar = id("qunit-testrunner-toolbar");1172if ( toolbar ) {1173toolbar.style.display = "block";1174id("qunit-filter-pass").disabled = null;1175}1176}11771178} else {1179for ( var i = 0; i < this.assertions.length; i++ ) {1180if ( !this.assertions[i].result ) {1181bad++;1182config.stats.bad++;1183config.moduleStats.bad++;1184}1185}1186}11871188try {1189QUnit.reset();1190} catch(e) {1191// TODO use testName instead of name for no-markup message?1192fail("reset() failed, following Test " + this.name + ", exception and reset fn follows", e, QUnit.reset);1193}11941195QUnit.testDone( this.testName, bad, this.assertions.length );1196},11971198queue: function() {1199var test = this;1200synchronize(function() {1201test.init();1202});1203function run() {1204// each of these can by async1205synchronize(function() {1206test.setup();1207});1208synchronize(function() {1209test.run();1210});1211synchronize(function() {1212test.teardown();1213});1214synchronize(function() {1215test.finish();1216});1217}1218// defer when previous test run passed, if storage is available1219var bad = defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.testName);1220if (bad) {1221run();1222} else {1223synchronize(run);1224};1225}12261227}12281229var QUnit = {12301231// call on start of module test to prepend name to all tests1232module: function(name, testEnvironment) {1233config.previousModule = config.currentModule;1234config.currentModule = name;1235config.currentModuleTestEnviroment = testEnvironment;1236},12371238asyncTest: function(testName, expected, callback) {1239if ( arguments.length === 2 ) {1240callback = expected;1241expected = 0;1242}12431244QUnit.test(testName, expected, callback, true);1245},12461247test: function(testName, expected, callback, async) {1248var name = '<span class="test-name">' + testName + '</span>', testEnvironmentArg;12491250if ( arguments.length === 2 ) {1251callback = expected;1252expected = null;1253}1254// is 2nd argument a testEnvironment?1255if ( expected && typeof expected === 'object') {1256testEnvironmentArg = expected;1257expected = null;1258}12591260if ( config.currentModule ) {1261name = '<span class="module-name">' + config.currentModule + "</span>: " + name;1262}12631264if ( !validTest(config.currentModule + ": " + testName) ) {1265return;1266}12671268var test = new Test(name, testName, expected, testEnvironmentArg, async, callback);1269test.previousModule = config.previousModule;1270test.module = config.currentModule;1271test.moduleTestEnvironment = config.currentModuleTestEnviroment;1272test.queue();1273},12741275/**1276* Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.1277*/1278expect: function(asserts) {1279config.current.expected = asserts;1280},12811282/**1283* Asserts true.1284* @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );1285*/1286ok: function(a, msg) {1287a = !!a;1288var details = {1289result: a,1290message: msg1291};1292msg = escapeHtml(msg);1293QUnit.log(a, msg, details);1294config.current.assertions.push({1295result: a,1296message: msg1297});1298},12991300/**1301* Checks that the first two arguments are equal, with an optional message.1302* Prints out both actual and expected values.1303*1304* Prefered to ok( actual == expected, message )1305*1306* @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );1307*1308* @param Object actual1309* @param Object expected1310* @param String message (optional)1311*/1312equal: function(actual, expected, message) {1313QUnit.push(expected == actual, actual, expected, message);1314},13151316notEqual: function(actual, expected, message) {1317QUnit.push(expected != actual, actual, expected, message);1318},13191320deepEqual: function(actual, expected, message) {1321QUnit.push(QUnit.equiv(actual, expected), actual, expected, message);1322},13231324notDeepEqual: function(actual, expected, message) {1325QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message);1326},13271328strictEqual: function(actual, expected, message) {1329QUnit.push(expected === actual, actual, expected, message);1330},13311332notStrictEqual: function(actual, expected, message) {1333QUnit.push(expected !== actual, actual, expected, message);1334},13351336raises: function(block, expected, message) {1337var actual, ok = false;13381339if (typeof expected === 'string') {1340message = expected;1341expected = null;1342}13431344try {1345block();1346} catch (e) {1347actual = e;1348}13491350if (actual) {1351// we don't want to validate thrown error1352if (!expected) {1353ok = true;1354// expected is a regexp1355} else if (QUnit.objectType(expected) === "regexp") {1356ok = expected.test(actual);1357// expected is a constructor1358} else if (actual instanceof expected) {1359ok = true;1360// expected is a validation function which returns true is validation passed1361} else if (expected.call({}, actual) === true) {1362ok = true;1363}1364}13651366QUnit.ok(ok, message);1367},13681369start: function() {1370// A slight delay, to avoid any current callbacks1371if ( defined.setTimeout ) {1372window.setTimeout(function() {1373if ( config.timeout ) {1374clearTimeout(config.timeout);1375}13761377config.blocking = false;1378process();1379}, 13);1380} else {1381config.blocking = false;1382process();1383}1384},13851386stop: function(timeout) {1387config.blocking = true;13881389if ( timeout && defined.setTimeout ) {1390config.timeout = window.setTimeout(function() {1391QUnit.ok( false, "Test timed out" );1392QUnit.start();1393}, timeout);1394}1395}13961397};13981399// Backwards compatibility, deprecated1400QUnit.equals = QUnit.equal;1401QUnit.same = QUnit.deepEqual;14021403// Maintain internal state1404var config = {1405// The queue of tests to run1406queue: [],14071408// block until document ready1409blocking: true1410};14111412// Load paramaters1413(function() {1414var location = window.location || { search: "", protocol: "file:" },1415GETParams = location.search.slice(1).split('&');14161417for ( var i = 0; i < GETParams.length; i++ ) {1418GETParams[i] = decodeURIComponent( GETParams[i] );1419if ( GETParams[i] === "noglobals" ) {1420GETParams.splice( i, 1 );1421i--;1422config.noglobals = true;1423} else if ( GETParams[i].search('=') > -1 ) {1424GETParams.splice( i, 1 );1425i--;1426}1427}14281429// restrict modules/tests by get parameters1430config.filters = GETParams;14311432// Figure out if we're running the tests from a server or not1433QUnit.isLocal = !!(location.protocol === 'file:');1434})();14351436// Expose the API as global variables, unless an 'exports'1437// object exists, in that case we assume we're in CommonJS1438if ( typeof exports === "undefined" || typeof require === "undefined" ) {1439extend(window, QUnit);1440window.QUnit = QUnit;1441} else {1442extend(exports, QUnit);1443exports.QUnit = QUnit;1444}14451446// define these after exposing globals to keep them in these QUnit namespace only1447extend(QUnit, {1448config: config,14491450// Initialize the configuration options1451init: function() {1452extend(config, {1453stats: { all: 0, bad: 0 },1454moduleStats: { all: 0, bad: 0 },1455started: +new Date,1456updateRate: 1000,1457blocking: false,1458autostart: true,1459autorun: false,1460filters: [],1461queue: []1462});14631464var tests = id("qunit-tests"),1465banner = id("qunit-banner"),1466result = id("qunit-testresult");14671468if ( tests ) {1469tests.innerHTML = "";1470}14711472if ( banner ) {1473banner.className = "";1474}14751476if ( result ) {1477result.parentNode.removeChild( result );1478}1479},14801481/**1482* Resets the test setup. Useful for tests that modify the DOM.1483*1484* If jQuery is available, uses jQuery's html(), otherwise just innerHTML.1485*/1486reset: function() {1487if ( window.jQuery ) {1488jQuery( "#main, #qunit-fixture" ).html( config.fixture );1489} else {1490var main = id( 'main' ) || id( 'qunit-fixture' );1491if ( main ) {1492main.innerHTML = config.fixture;1493}1494}1495},14961497/**1498* Trigger an event on an element.1499*1500* @example triggerEvent( document.body, "click" );1501*1502* @param DOMElement elem1503* @param String type1504*/1505triggerEvent: function( elem, type, event ) {1506if ( document.createEvent ) {1507event = document.createEvent("MouseEvents");1508event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,15090, 0, 0, 0, 0, false, false, false, false, 0, null);1510elem.dispatchEvent( event );15111512} else if ( elem.fireEvent ) {1513elem.fireEvent("on"+type);1514}1515},15161517// Safe object type checking1518is: function( type, obj ) {1519return QUnit.objectType( obj ) == type;1520},15211522objectType: function( obj ) {1523if (typeof obj === "undefined") {1524return "undefined";15251526// consider: typeof null === object1527}1528if (obj === null) {1529return "null";1530}15311532var type = Object.prototype.toString.call( obj )1533.match(/^\[object\s(.*)\]$/)[1] || '';15341535switch (type) {1536case 'Number':1537if (isNaN(obj)) {1538return "nan";1539} else {1540return "number";1541}1542case 'String':1543case 'Boolean':1544case 'Array':1545case 'Date':1546case 'RegExp':1547case 'Function':1548return type.toLowerCase();1549}1550if (typeof obj === "object") {1551return "object";1552}1553return undefined;1554},15551556push: function(result, actual, expected, message) {1557var details = {1558result: result,1559message: message,1560actual: actual,1561expected: expected1562};15631564message = escapeHtml(message) || (result ? "okay" : "failed");1565message = '<span class="test-message">' + message + "</span>";1566expected = escapeHtml(QUnit.jsDump.parse(expected));1567actual = escapeHtml(QUnit.jsDump.parse(actual));1568var output = message + '<table><tr class="test-expected"><th>Expected: </th><td><pre>' + expected + '</pre></td></tr>';1569if (actual != expected) {1570output += '<tr class="test-actual"><th>Result: </th><td><pre>' + actual + '</pre></td></tr>';1571output += '<tr class="test-diff"><th>Diff: </th><td><pre>' + QUnit.diff(expected, actual) +'</pre></td></tr>';1572}1573if (!result) {1574var source = sourceFromStacktrace();1575if (source) {1576details.source = source;1577output += '<tr class="test-source"><th>Source: </th><td><pre>' + source +'</pre></td></tr>';1578}1579}1580output += "</table>";15811582QUnit.log(result, message, details);15831584config.current.assertions.push({1585result: !!result,1586message: output1587});1588},15891590// Logging callbacks1591begin: function() {},1592done: function(failures, total) {},1593log: function(result, message) {},1594testStart: function(name, testEnvironment) {},1595testDone: function(name, failures, total) {},1596moduleStart: function(name, testEnvironment) {},1597moduleDone: function(name, failures, total) {}1598});15991600if ( typeof document === "undefined" || document.readyState === "complete" ) {1601config.autorun = true;1602}16031604addEvent(window, "load", function() {1605QUnit.begin();16061607// Initialize the config, saving the execution queue1608var oldconfig = extend({}, config);1609QUnit.init();1610extend(config, oldconfig);16111612config.blocking = false;16131614var userAgent = id("qunit-userAgent");1615if ( userAgent ) {1616userAgent.innerHTML = navigator.userAgent;1617}1618var banner = id("qunit-header");1619if ( banner ) {1620var paramsIndex = location.href.lastIndexOf(location.search);1621if ( paramsIndex > -1 ) {1622var mainPageLocation = location.href.slice(0, paramsIndex);1623if ( mainPageLocation == location.href ) {1624banner.innerHTML = '<a href=""> ' + banner.innerHTML + '</a> ';1625} else {1626var testName = decodeURIComponent(location.search.slice(1));1627banner.innerHTML = '<a href="' + mainPageLocation + '">' + banner.innerHTML + '</a> › <a href="">' + testName + '</a>';1628}1629}1630}16311632var toolbar = id("qunit-testrunner-toolbar");1633if ( toolbar ) {1634toolbar.style.display = "none";16351636var filter = document.createElement("input");1637filter.type = "checkbox";1638filter.id = "qunit-filter-pass";1639filter.disabled = true;1640addEvent( filter, "click", function() {1641var li = document.getElementsByTagName("li");1642for ( var i = 0; i < li.length; i++ ) {1643if ( li[i].className.indexOf("pass") > -1 ) {1644li[i].style.display = filter.checked ? "none" : "";1645}1646}1647});1648toolbar.appendChild( filter );16491650var label = document.createElement("label");1651label.setAttribute("for", "qunit-filter-pass");1652label.innerHTML = "Hide passed tests";1653toolbar.appendChild( label );1654}16551656var main = id('main') || id('qunit-fixture');1657if ( main ) {1658config.fixture = main.innerHTML;1659}16601661if (config.autostart) {1662QUnit.start();1663}1664});16651666function done() {1667config.autorun = true;16681669// Log the last module results1670if ( config.currentModule ) {1671QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );1672}16731674var banner = id("qunit-banner"),1675tests = id("qunit-tests"),1676html = ['Tests completed in ',1677+new Date - config.started, ' milliseconds.<br/>',1678'<span class="passed">', config.stats.all - config.stats.bad, '</span> tests of <span class="total">', config.stats.all, '</span> passed, <span class="failed">', config.stats.bad,'</span> failed.'].join('');16791680if ( banner ) {1681banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");1682}16831684if ( tests ) {1685var result = id("qunit-testresult");16861687if ( !result ) {1688result = document.createElement("p");1689result.id = "qunit-testresult";1690result.className = "result";1691tests.parentNode.insertBefore( result, tests.nextSibling );1692}16931694result.innerHTML = html;1695}16961697QUnit.done( config.stats.bad, config.stats.all );1698}16991700function validTest( name ) {1701var i = config.filters.length,1702run = false;17031704if ( !i ) {1705return true;1706}17071708while ( i-- ) {1709var filter = config.filters[i],1710not = filter.charAt(0) == '!';17111712if ( not ) {1713filter = filter.slice(1);1714}17151716if ( name.indexOf(filter) !== -1 ) {1717return !not;1718}17191720if ( not ) {1721run = true;1722}1723}17241725return run;1726}17271728// so far supports only Firefox, Chrome and Opera (buggy)1729// could be extended in the future to use something like https://github.com/csnover/TraceKit1730function sourceFromStacktrace() {1731try {1732throw new Error();1733} catch ( e ) {1734if (e.stacktrace) {1735// Opera1736return e.stacktrace.split("\n")[6];1737} else if (e.stack) {1738// Firefox, Chrome1739return e.stack.split("\n")[4];1740}1741}1742}17431744function resultDisplayStyle(passed) {1745return passed && id("qunit-filter-pass") && id("qunit-filter-pass").checked ? 'none' : '';1746}17471748function escapeHtml(s) {1749if (!s) {1750return "";1751}1752s = s + "";1753return s.replace(/[\&"<>\\]/g, function(s) {1754switch(s) {1755case "&": return "&";1756case "\\": return "\\\\";1757case '"': return '\"';1758case "<": return "<";1759case ">": return ">";1760default: return s;1761}1762});1763}17641765function synchronize( callback ) {1766config.queue.push( callback );17671768if ( config.autorun && !config.blocking ) {1769process();1770}1771}17721773function process() {1774var start = (new Date()).getTime();17751776while ( config.queue.length && !config.blocking ) {1777if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {1778config.queue.shift()();1779} else {1780window.setTimeout( process, 13 );1781break;1782}1783}1784if (!config.blocking && !config.queue.length) {1785done();1786}1787}17881789function saveGlobal() {1790config.pollution = [];17911792if ( config.noglobals ) {1793for ( var key in window ) {1794config.pollution.push( key );1795}1796}1797}17981799function checkPollution( name ) {1800var old = config.pollution;1801saveGlobal();18021803var newGlobals = diff( old, config.pollution );1804if ( newGlobals.length > 0 ) {1805ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );1806config.current.expected++;1807}18081809var deletedGlobals = diff( config.pollution, old );1810if ( deletedGlobals.length > 0 ) {1811ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );1812config.current.expected++;1813}1814}18151816// returns a new Array with the elements that are in a but not in b1817function diff( a, b ) {1818var result = a.slice();1819for ( var i = 0; i < result.length; i++ ) {1820for ( var j = 0; j < b.length; j++ ) {1821if ( result[i] === b[j] ) {1822result.splice(i, 1);1823i--;1824break;1825}1826}1827}1828return result;1829}18301831function fail(message, exception, callback) {1832if ( typeof console !== "undefined" && console.error && console.warn ) {1833console.error(message);1834console.error(exception);1835console.warn(callback.toString());18361837} else if ( window.opera && opera.postError ) {1838opera.postError(message, exception, callback.toString);1839}1840}18411842function extend(a, b) {1843for ( var prop in b ) {1844a[prop] = b[prop];1845}18461847return a;1848}18491850function addEvent(elem, type, fn) {1851if ( elem.addEventListener ) {1852elem.addEventListener( type, fn, false );1853} else if ( elem.attachEvent ) {1854elem.attachEvent( "on" + type, fn );1855} else {1856fn();1857}1858}18591860function id(name) {1861return !!(typeof document !== "undefined" && document && document.getElementById) &&1862document.getElementById( name );1863}18641865// Test for equality any JavaScript type.1866// Discussions and reference: http://philrathe.com/articles/equiv1867// Test suites: http://philrathe.com/tests/equiv1868// Author: Philippe Rathé <[email protected]>1869QUnit.equiv = function () {18701871var innerEquiv; // the real equiv function1872var callers = []; // stack to decide between skip/abort functions1873var parents = []; // stack to avoiding loops from circular referencing18741875// Call the o related callback with the given arguments.1876function bindCallbacks(o, callbacks, args) {1877var prop = QUnit.objectType(o);1878if (prop) {1879if (QUnit.objectType(callbacks[prop]) === "function") {1880return callbacks[prop].apply(callbacks, args);1881} else {1882return callbacks[prop]; // or undefined1883}1884}1885}18861887var callbacks = function () {18881889// for string, boolean, number and null1890function useStrictEquality(b, a) {1891if (b instanceof a.constructor || a instanceof b.constructor) {1892// to catch short annotaion VS 'new' annotation of a declaration1893// e.g. var i = 1;1894// var j = new Number(1);1895return a == b;1896} else {1897return a === b;1898}1899}19001901return {1902"string": useStrictEquality,1903"boolean": useStrictEquality,1904"number": useStrictEquality,1905"null": useStrictEquality,1906"undefined": useStrictEquality,19071908"nan": function (b) {1909return isNaN(b);1910},19111912"date": function (b, a) {1913return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf();1914},19151916"regexp": function (b, a) {1917return QUnit.objectType(b) === "regexp" &&1918a.source === b.source && // the regex itself1919a.global === b.global && // and its modifers (gmi) ...1920a.ignoreCase === b.ignoreCase &&1921a.multiline === b.multiline;1922},19231924// - skip when the property is a method of an instance (OOP)1925// - abort otherwise,1926// initial === would have catch identical references anyway1927"function": function () {1928var caller = callers[callers.length - 1];1929return caller !== Object &&1930typeof caller !== "undefined";1931},19321933"array": function (b, a) {1934var i, j, loop;1935var len;19361937// b could be an object literal here1938if ( ! (QUnit.objectType(b) === "array")) {1939return false;1940}19411942len = a.length;1943if (len !== b.length) { // safe and faster1944return false;1945}19461947//track reference to avoid circular references1948parents.push(a);1949for (i = 0; i < len; i++) {1950loop = false;1951for(j=0;j<parents.length;j++){1952if(parents[j] === a[i]){1953loop = true;//dont rewalk array1954}1955}1956if (!loop && ! innerEquiv(a[i], b[i])) {1957parents.pop();1958return false;1959}1960}1961parents.pop();1962return true;1963},19641965"object": function (b, a) {1966var i, j, loop;1967var eq = true; // unless we can proove it1968var aProperties = [], bProperties = []; // collection of strings19691970// comparing constructors is more strict than using instanceof1971if ( a.constructor !== b.constructor) {1972return false;1973}19741975// stack constructor before traversing properties1976callers.push(a.constructor);1977//track reference to avoid circular references1978parents.push(a);19791980for (i in a) { // be strict: don't ensures hasOwnProperty and go deep1981loop = false;1982for(j=0;j<parents.length;j++){1983if(parents[j] === a[i])1984loop = true; //don't go down the same path twice1985}1986aProperties.push(i); // collect a's properties19871988if (!loop && ! innerEquiv(a[i], b[i])) {1989eq = false;1990break;1991}1992}19931994callers.pop(); // unstack, we are done1995parents.pop();19961997for (i in b) {1998bProperties.push(i); // collect b's properties1999}20002001// Ensures identical properties name2002return eq && innerEquiv(aProperties.sort(), bProperties.sort());2003}2004};2005}();20062007innerEquiv = function () { // can take multiple arguments2008var args = Array.prototype.slice.apply(arguments);2009if (args.length < 2) {2010return true; // end transition2011}20122013return (function (a, b) {2014if (a === b) {2015return true; // catch the most you can2016} else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || QUnit.objectType(a) !== QUnit.objectType(b)) {2017return false; // don't lose time with error prone cases2018} else {2019return bindCallbacks(a, callbacks, [b, a]);2020}20212022// apply transition with (1..n) arguments2023})(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));2024};20252026return innerEquiv;20272028}();20292030/**2031* jsDump2032* Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com2033* Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)2034* Date: 5/15/20082035* @projectDescription Advanced and extensible data dumping for Javascript.2036* @version 1.0.02037* @author Ariel Flesler2038* @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}2039*/2040QUnit.jsDump = (function() {2041function quote( str ) {2042return '"' + str.toString().replace(/"/g, '\\"') + '"';2043};2044function literal( o ) {2045return o + '';2046};2047function join( pre, arr, post ) {2048var s = jsDump.separator(),2049base = jsDump.indent(),2050inner = jsDump.indent(1);2051if ( arr.join )2052arr = arr.join( ',' + s + inner );2053if ( !arr )2054return pre + post;2055return [ pre, inner + arr, base + post ].join(s);2056};2057function array( arr ) {2058var i = arr.length, ret = Array(i);2059this.up();2060while ( i-- )2061ret[i] = this.parse( arr[i] );2062this.down();2063return join( '[', ret, ']' );2064};20652066var reName = /^function (\w+)/;20672068var jsDump = {2069parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance2070var parser = this.parsers[ type || this.typeOf(obj) ];2071type = typeof parser;20722073return type == 'function' ? parser.call( this, obj ) :2074type == 'string' ? parser :2075this.parsers.error;2076},2077typeOf:function( obj ) {2078var type;2079if ( obj === null ) {2080type = "null";2081} else if (typeof obj === "undefined") {2082type = "undefined";2083} else if (QUnit.is("RegExp", obj)) {2084type = "regexp";2085} else if (QUnit.is("Date", obj)) {2086type = "date";2087} else if (QUnit.is("Function", obj)) {2088type = "function";2089} else if (typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined") {2090type = "window";2091} else if (obj.nodeType === 9) {2092type = "document";2093} else if (obj.nodeType) {2094type = "node";2095} else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) {2096type = "array";2097} else {2098type = typeof obj;2099}2100return type;2101},2102separator:function() {2103return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? ' ' : ' ';2104},2105indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing2106if ( !this.multiline )2107return '';2108var chr = this.indentChar;2109if ( this.HTML )2110chr = chr.replace(/\t/g,' ').replace(/ /g,' ');2111return Array( this._depth_ + (extra||0) ).join(chr);2112},2113up:function( a ) {2114this._depth_ += a || 1;2115},2116down:function( a ) {2117this._depth_ -= a || 1;2118},2119setParser:function( name, parser ) {2120this.parsers[name] = parser;2121},2122// The next 3 are exposed so you can use them2123quote:quote,2124literal:literal,2125join:join,2126//2127_depth_: 1,2128// This is the list of parsers, to modify them, use jsDump.setParser2129parsers:{2130window: '[Window]',2131document: '[Document]',2132error:'[ERROR]', //when no parser is found, shouldn't happen2133unknown: '[Unknown]',2134'null':'null',2135undefined:'undefined',2136'function':function( fn ) {2137var ret = 'function',2138name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE2139if ( name )2140ret += ' ' + name;2141ret += '(';21422143ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join('');2144return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' );2145},2146array: array,2147nodelist: array,2148arguments: array,2149object:function( map ) {2150var ret = [ ];2151QUnit.jsDump.up();2152for ( var key in map )2153ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(map[key]) );2154QUnit.jsDump.down();2155return join( '{', ret, '}' );2156},2157node:function( node ) {2158var open = QUnit.jsDump.HTML ? '<' : '<',2159close = QUnit.jsDump.HTML ? '>' : '>';21602161var tag = node.nodeName.toLowerCase(),2162ret = open + tag;21632164for ( var a in QUnit.jsDump.DOMAttrs ) {2165var val = node[QUnit.jsDump.DOMAttrs[a]];2166if ( val )2167ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' );2168}2169return ret + close + open + '/' + tag + close;2170},2171functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function2172var l = fn.length;2173if ( !l ) return '';21742175var args = Array(l);2176while ( l-- )2177args[l] = String.fromCharCode(97+l);//97 is 'a'2178return ' ' + args.join(', ') + ' ';2179},2180key:quote, //object calls it internally, the key part of an item in a map2181functionCode:'[code]', //function calls it internally, it's the content of the function2182attribute:quote, //node calls it internally, it's an html attribute value2183string:quote,2184date:quote,2185regexp:literal, //regex2186number:literal,2187'boolean':literal2188},2189DOMAttrs:{//attributes to dump from nodes, name=>realName2190id:'id',2191name:'name',2192'class':'className'2193},2194HTML:false,//if true, entities are escaped ( <, >, \t, space and \n )2195indentChar:' ',//indentation unit2196multiline:true //if true, items in a collection, are separated by a \n, else just a space.2197};21982199return jsDump;2200})();22012202// from Sizzle.js2203function getText( elems ) {2204var ret = "", elem;22052206for ( var i = 0; elems[i]; i++ ) {2207elem = elems[i];22082209// Get the text from text nodes and CDATA nodes2210if ( elem.nodeType === 3 || elem.nodeType === 4 ) {2211ret += elem.nodeValue;22122213// Traverse everything else, except comment nodes2214} else if ( elem.nodeType !== 8 ) {2215ret += getText( elem.childNodes );2216}2217}22182219return ret;2220};22212222/*2223* Javascript Diff Algorithm2224* By John Resig (http://ejohn.org/)2225* Modified by Chu Alan "sprite"2226*2227* Released under the MIT license.2228*2229* More Info:2230* http://ejohn.org/projects/javascript-diff-algorithm/2231*2232* Usage: QUnit.diff(expected, actual)2233*2234* 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"2235*/2236QUnit.diff = (function() {2237function diff(o, n){2238var ns = new Object();2239var os = new Object();22402241for (var i = 0; i < n.length; i++) {2242if (ns[n[i]] == null)2243ns[n[i]] = {2244rows: new Array(),2245o: null2246};2247ns[n[i]].rows.push(i);2248}22492250for (var i = 0; i < o.length; i++) {2251if (os[o[i]] == null)2252os[o[i]] = {2253rows: new Array(),2254n: null2255};2256os[o[i]].rows.push(i);2257}22582259for (var i in ns) {2260if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) {2261n[ns[i].rows[0]] = {2262text: n[ns[i].rows[0]],2263row: os[i].rows[0]2264};2265o[os[i].rows[0]] = {2266text: o[os[i].rows[0]],2267row: ns[i].rows[0]2268};2269}2270}22712272for (var i = 0; i < n.length - 1; i++) {2273if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null &&2274n[i + 1] == o[n[i].row + 1]) {2275n[i + 1] = {2276text: n[i + 1],2277row: n[i].row + 12278};2279o[n[i].row + 1] = {2280text: o[n[i].row + 1],2281row: i + 12282};2283}2284}22852286for (var i = n.length - 1; i > 0; i--) {2287if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null &&2288n[i - 1] == o[n[i].row - 1]) {2289n[i - 1] = {2290text: n[i - 1],2291row: n[i].row - 12292};2293o[n[i].row - 1] = {2294text: o[n[i].row - 1],2295row: i - 12296};2297}2298}22992300return {2301o: o,2302n: n2303};2304}23052306return function(o, n){2307o = o.replace(/\s+$/, '');2308n = n.replace(/\s+$/, '');2309var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/));23102311var str = "";23122313var oSpace = o.match(/\s+/g);2314if (oSpace == null) {2315oSpace = [" "];2316}2317else {2318oSpace.push(" ");2319}2320var nSpace = n.match(/\s+/g);2321if (nSpace == null) {2322nSpace = [" "];2323}2324else {2325nSpace.push(" ");2326}23272328if (out.n.length == 0) {2329for (var i = 0; i < out.o.length; i++) {2330str += '<del>' + out.o[i] + oSpace[i] + "</del>";2331}2332}2333else {2334if (out.n[0].text == null) {2335for (n = 0; n < out.o.length && out.o[n].text == null; n++) {2336str += '<del>' + out.o[n] + oSpace[n] + "</del>";2337}2338}23392340for (var i = 0; i < out.n.length; i++) {2341if (out.n[i].text == null) {2342str += '<ins>' + out.n[i] + nSpace[i] + "</ins>";2343}2344else {2345var pre = "";23462347for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) {2348pre += '<del>' + out.o[n] + oSpace[n] + "</del>";2349}2350str += " " + out.n[i].text + nSpace[i] + pre;2351}2352}2353}23542355return str;2356};2357})();23582359})(this);23602361