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