corrade-nucleus-nucleons – Blame information for rev 20

Subversion Repositories:
Rev:
Rev Author Line No. Line
20 office 1 /*!
2 * QUnit 1.17.1
3 * http://qunitjs.com/
4 *
5 * Copyright jQuery Foundation and other contributors
6 * Released under the MIT license
7 * http://jquery.org/license
8 *
9 * Date: 2015-01-20T19:39Z
10 */
11  
12 (function( window ) {
13  
14 var QUnit,
15 config,
16 onErrorFnPrev,
17 loggingCallbacks = {},
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 now = Date.now || function() {
24 return new Date().getTime();
25 },
26 globalStartCalled = false,
27 runStarted = false,
28 setTimeout = window.setTimeout,
29 clearTimeout = window.clearTimeout,
30 defined = {
31 document: window.document !== undefined,
32 setTimeout: window.setTimeout !== undefined,
33 sessionStorage: (function() {
34 var x = "qunit-test-string";
35 try {
36 sessionStorage.setItem( x, x );
37 sessionStorage.removeItem( x );
38 return true;
39 } catch ( e ) {
40 return false;
41 }
42 }())
43 },
44 /**
45 * Provides a normalized error string, correcting an issue
46 * with IE 7 (and prior) where Error.prototype.toString is
47 * not properly implemented
48 *
49 * Based on http://es5.github.com/#x15.11.4.4
50 *
51 * @param {String|Error} error
52 * @return {String} error message
53 */
54 errorString = function( error ) {
55 var name, message,
56 errorString = error.toString();
57 if ( errorString.substring( 0, 7 ) === "[object" ) {
58 name = error.name ? error.name.toString() : "Error";
59 message = error.message ? error.message.toString() : "";
60 if ( name && message ) {
61 return name + ": " + message;
62 } else if ( name ) {
63 return name;
64 } else if ( message ) {
65 return message;
66 } else {
67 return "Error";
68 }
69 } else {
70 return errorString;
71 }
72 },
73 /**
74 * Makes a clone of an object using only Array or Object as base,
75 * and copies over the own enumerable properties.
76 *
77 * @param {Object} obj
78 * @return {Object} New object with only the own properties (recursively).
79 */
80 objectValues = function( obj ) {
81 var key, val,
82 vals = QUnit.is( "array", obj ) ? [] : {};
83 for ( key in obj ) {
84 if ( hasOwn.call( obj, key ) ) {
85 val = obj[ key ];
86 vals[ key ] = val === Object( val ) ? objectValues( val ) : val;
87 }
88 }
89 return vals;
90 };
91  
92 QUnit = {};
93  
94 /**
95 * Config object: Maintain internal state
96 * Later exposed as QUnit.config
97 * `config` initialized at top of scope
98 */
99 config = {
100 // The queue of tests to run
101 queue: [],
102  
103 // block until document ready
104 blocking: true,
105  
106 // by default, run previously failed tests first
107 // very useful in combination with "Hide passed tests" checked
108 reorder: true,
109  
110 // by default, modify document.title when suite is done
111 altertitle: true,
112  
113 // by default, scroll to top of the page when suite is done
114 scrolltop: true,
115  
116 // when enabled, all tests must call expect()
117 requireExpects: false,
118  
119 // add checkboxes that are persisted in the query-string
120 // when enabled, the id is set to `true` as a `QUnit.config` property
121 urlConfig: [
122 {
123 id: "hidepassed",
124 label: "Hide passed tests",
125 tooltip: "Only show tests and assertions that fail. Stored as query-strings."
126 },
127 {
128 id: "noglobals",
129 label: "Check for Globals",
130 tooltip: "Enabling this will test if any test introduces new properties on the " +
131 "`window` object. Stored as query-strings."
132 },
133 {
134 id: "notrycatch",
135 label: "No try-catch",
136 tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " +
137 "exceptions in IE reasonable. Stored as query-strings."
138 }
139 ],
140  
141 // Set of all modules.
142 modules: [],
143  
144 // The first unnamed module
145 currentModule: {
146 name: "",
147 tests: []
148 },
149  
150 callbacks: {}
151 };
152  
153 // Push a loose unnamed module to the modules collection
154 config.modules.push( config.currentModule );
155  
156 // Initialize more QUnit.config and QUnit.urlParams
157 (function() {
158 var i, current,
159 location = window.location || { search: "", protocol: "file:" },
160 params = location.search.slice( 1 ).split( "&" ),
161 length = params.length,
162 urlParams = {};
163  
164 if ( params[ 0 ] ) {
165 for ( i = 0; i < length; i++ ) {
166 current = params[ i ].split( "=" );
167 current[ 0 ] = decodeURIComponent( current[ 0 ] );
168  
169 // allow just a key to turn on a flag, e.g., test.html?noglobals
170 current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
171 if ( urlParams[ current[ 0 ] ] ) {
172 urlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] );
173 } else {
174 urlParams[ current[ 0 ] ] = current[ 1 ];
175 }
176 }
177 }
178  
179 if ( urlParams.filter === true ) {
180 delete urlParams.filter;
181 }
182  
183 QUnit.urlParams = urlParams;
184  
185 // String search anywhere in moduleName+testName
186 config.filter = urlParams.filter;
187  
188 config.testId = [];
189 if ( urlParams.testId ) {
190  
191 // Ensure that urlParams.testId is an array
192 urlParams.testId = [].concat( urlParams.testId );
193 for ( i = 0; i < urlParams.testId.length; i++ ) {
194 config.testId.push( urlParams.testId[ i ] );
195 }
196 }
197  
198 // Figure out if we're running the tests from a server or not
199 QUnit.isLocal = location.protocol === "file:";
200 }());
201  
202 // Root QUnit object.
203 // `QUnit` initialized at top of scope
204 extend( QUnit, {
205  
206 // call on start of module test to prepend name to all tests
207 module: function( name, testEnvironment ) {
208 var currentModule = {
209 name: name,
210 testEnvironment: testEnvironment,
211 tests: []
212 };
213  
214 // DEPRECATED: handles setup/teardown functions,
215 // beforeEach and afterEach should be used instead
216 if ( testEnvironment && testEnvironment.setup ) {
217 testEnvironment.beforeEach = testEnvironment.setup;
218 delete testEnvironment.setup;
219 }
220 if ( testEnvironment && testEnvironment.teardown ) {
221 testEnvironment.afterEach = testEnvironment.teardown;
222 delete testEnvironment.teardown;
223 }
224  
225 config.modules.push( currentModule );
226 config.currentModule = currentModule;
227 },
228  
229 // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0.
230 asyncTest: function( testName, expected, callback ) {
231 if ( arguments.length === 2 ) {
232 callback = expected;
233 expected = null;
234 }
235  
236 QUnit.test( testName, expected, callback, true );
237 },
238  
239 test: function( testName, expected, callback, async ) {
240 var test;
241  
242 if ( arguments.length === 2 ) {
243 callback = expected;
244 expected = null;
245 }
246  
247 test = new Test({
248 testName: testName,
249 expected: expected,
250 async: async,
251 callback: callback
252 });
253  
254 test.queue();
255 },
256  
257 skip: function( testName ) {
258 var test = new Test({
259 testName: testName,
260 skip: true
261 });
262  
263 test.queue();
264 },
265  
266 // DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0.
267 // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior.
268 start: function( count ) {
269 var globalStartAlreadyCalled = globalStartCalled;
270  
271 if ( !config.current ) {
272 globalStartCalled = true;
273  
274 if ( runStarted ) {
275 throw new Error( "Called start() outside of a test context while already started" );
276 } else if ( globalStartAlreadyCalled || count > 1 ) {
277 throw new Error( "Called start() outside of a test context too many times" );
278 } else if ( config.autostart ) {
279 throw new Error( "Called start() outside of a test context when " +
280 "QUnit.config.autostart was true" );
281 } else if ( !config.pageLoaded ) {
282  
283 // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it
284 config.autostart = true;
285 return;
286 }
287 } else {
288  
289 // If a test is running, adjust its semaphore
290 config.current.semaphore -= count || 1;
291  
292 // Don't start until equal number of stop-calls
293 if ( config.current.semaphore > 0 ) {
294 return;
295 }
296  
297 // throw an Error if start is called more often than stop
298 if ( config.current.semaphore < 0 ) {
299 config.current.semaphore = 0;
300  
301 QUnit.pushFailure(
302 "Called start() while already started (test's semaphore was 0 already)",
303 sourceFromStacktrace( 2 )
304 );
305 return;
306 }
307 }
308  
309 resumeProcessing();
310 },
311  
312 // DEPRECATED: QUnit.stop() will be removed in QUnit 2.0.
313 stop: function( count ) {
314  
315 // If there isn't a test running, don't allow QUnit.stop() to be called
316 if ( !config.current ) {
317 throw new Error( "Called stop() outside of a test context" );
318 }
319  
320 // If a test is running, adjust its semaphore
321 config.current.semaphore += count || 1;
322  
323 pauseProcessing();
324 },
325  
326 config: config,
327  
328 // Safe object type checking
329 is: function( type, obj ) {
330 return QUnit.objectType( obj ) === type;
331 },
332  
333 objectType: function( obj ) {
334 if ( typeof obj === "undefined" ) {
335 return "undefined";
336 }
337  
338 // Consider: typeof null === object
339 if ( obj === null ) {
340 return "null";
341 }
342  
343 var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ),
344 type = match && match[ 1 ] || "";
345  
346 switch ( type ) {
347 case "Number":
348 if ( isNaN( obj ) ) {
349 return "nan";
350 }
351 return "number";
352 case "String":
353 case "Boolean":
354 case "Array":
355 case "Date":
356 case "RegExp":
357 case "Function":
358 return type.toLowerCase();
359 }
360 if ( typeof obj === "object" ) {
361 return "object";
362 }
363 return undefined;
364 },
365  
366 extend: extend,
367  
368 load: function() {
369 config.pageLoaded = true;
370  
371 // Initialize the configuration options
372 extend( config, {
373 stats: { all: 0, bad: 0 },
374 moduleStats: { all: 0, bad: 0 },
375 started: 0,
376 updateRate: 1000,
377 autostart: true,
378 filter: ""
379 }, true );
380  
381 config.blocking = false;
382  
383 if ( config.autostart ) {
384 resumeProcessing();
385 }
386 }
387 });
388  
389 // Register logging callbacks
390 (function() {
391 var i, l, key,
392 callbacks = [ "begin", "done", "log", "testStart", "testDone",
393 "moduleStart", "moduleDone" ];
394  
395 function registerLoggingCallback( key ) {
396 var loggingCallback = function( callback ) {
397 if ( QUnit.objectType( callback ) !== "function" ) {
398 throw new Error(
399 "QUnit logging methods require a callback function as their first parameters."
400 );
401 }
402  
403 config.callbacks[ key ].push( callback );
404 };
405  
406 // DEPRECATED: This will be removed on QUnit 2.0.0+
407 // Stores the registered functions allowing restoring
408 // at verifyLoggingCallbacks() if modified
409 loggingCallbacks[ key ] = loggingCallback;
410  
411 return loggingCallback;
412 }
413  
414 for ( i = 0, l = callbacks.length; i < l; i++ ) {
415 key = callbacks[ i ];
416  
417 // Initialize key collection of logging callback
418 if ( QUnit.objectType( config.callbacks[ key ] ) === "undefined" ) {
419 config.callbacks[ key ] = [];
420 }
421  
422 QUnit[ key ] = registerLoggingCallback( key );
423 }
424 })();
425  
426 // `onErrorFnPrev` initialized at top of scope
427 // Preserve other handlers
428 onErrorFnPrev = window.onerror;
429  
430 // Cover uncaught exceptions
431 // Returning true will suppress the default browser handler,
432 // returning false will let it run.
433 window.onerror = function( error, filePath, linerNr ) {
434 var ret = false;
435 if ( onErrorFnPrev ) {
436 ret = onErrorFnPrev( error, filePath, linerNr );
437 }
438  
439 // Treat return value as window.onerror itself does,
440 // Only do our handling if not suppressed.
441 if ( ret !== true ) {
442 if ( QUnit.config.current ) {
443 if ( QUnit.config.current.ignoreGlobalErrors ) {
444 return true;
445 }
446 QUnit.pushFailure( error, filePath + ":" + linerNr );
447 } else {
448 QUnit.test( "global failure", extend(function() {
449 QUnit.pushFailure( error, filePath + ":" + linerNr );
450 }, { validTest: true } ) );
451 }
452 return false;
453 }
454  
455 return ret;
456 };
457  
458 function done() {
459 var runtime, passed;
460  
461 config.autorun = true;
462  
463 // Log the last module results
464 if ( config.previousModule ) {
465 runLoggingCallbacks( "moduleDone", {
466 name: config.previousModule.name,
467 tests: config.previousModule.tests,
468 failed: config.moduleStats.bad,
469 passed: config.moduleStats.all - config.moduleStats.bad,
470 total: config.moduleStats.all,
471 runtime: now() - config.moduleStats.started
472 });
473 }
474 delete config.previousModule;
475  
476 runtime = now() - config.started;
477 passed = config.stats.all - config.stats.bad;
478  
479 runLoggingCallbacks( "done", {
480 failed: config.stats.bad,
481 passed: passed,
482 total: config.stats.all,
483 runtime: runtime
484 });
485 }
486  
487 // Doesn't support IE6 to IE9
488 // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
489 function extractStacktrace( e, offset ) {
490 offset = offset === undefined ? 4 : offset;
491  
492 var stack, include, i;
493  
494 if ( e.stacktrace ) {
495  
496 // Opera 12.x
497 return e.stacktrace.split( "\n" )[ offset + 3 ];
498 } else if ( e.stack ) {
499  
500 // Firefox, Chrome, Safari 6+, IE10+, PhantomJS and Node
501 stack = e.stack.split( "\n" );
502 if ( /^error$/i.test( stack[ 0 ] ) ) {
503 stack.shift();
504 }
505 if ( fileName ) {
506 include = [];
507 for ( i = offset; i < stack.length; i++ ) {
508 if ( stack[ i ].indexOf( fileName ) !== -1 ) {
509 break;
510 }
511 include.push( stack[ i ] );
512 }
513 if ( include.length ) {
514 return include.join( "\n" );
515 }
516 }
517 return stack[ offset ];
518 } else if ( e.sourceURL ) {
519  
520 // Safari < 6
521 // exclude useless self-reference for generated Error objects
522 if ( /qunit.js$/.test( e.sourceURL ) ) {
523 return;
524 }
525  
526 // for actual exceptions, this is useful
527 return e.sourceURL + ":" + e.line;
528 }
529 }
530  
531 function sourceFromStacktrace( offset ) {
532 var e = new Error();
533 if ( !e.stack ) {
534 try {
535 throw e;
536 } catch ( err ) {
537 // This should already be true in most browsers
538 e = err;
539 }
540 }
541 return extractStacktrace( e, offset );
542 }
543  
544 function synchronize( callback, last ) {
545 if ( QUnit.objectType( callback ) === "array" ) {
546 while ( callback.length ) {
547 synchronize( callback.shift() );
548 }
549 return;
550 }
551 config.queue.push( callback );
552  
553 if ( config.autorun && !config.blocking ) {
554 process( last );
555 }
556 }
557  
558 function process( last ) {
559 function next() {
560 process( last );
561 }
562 var start = now();
563 config.depth = ( config.depth || 0 ) + 1;
564  
565 while ( config.queue.length && !config.blocking ) {
566 if ( !defined.setTimeout || config.updateRate <= 0 ||
567 ( ( now() - start ) < config.updateRate ) ) {
568 if ( config.current ) {
569  
570 // Reset async tracking for each phase of the Test lifecycle
571 config.current.usedAsync = false;
572 }
573 config.queue.shift()();
574 } else {
575 setTimeout( next, 13 );
576 break;
577 }
578 }
579 config.depth--;
580 if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
581 done();
582 }
583 }
584  
585 function begin() {
586 var i, l,
587 modulesLog = [];
588  
589 // If the test run hasn't officially begun yet
590 if ( !config.started ) {
591  
592 // Record the time of the test run's beginning
593 config.started = now();
594  
595 verifyLoggingCallbacks();
596  
597 // Delete the loose unnamed module if unused.
598 if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) {
599 config.modules.shift();
600 }
601  
602 // Avoid unnecessary information by not logging modules' test environments
603 for ( i = 0, l = config.modules.length; i < l; i++ ) {
604 modulesLog.push({
605 name: config.modules[ i ].name,
606 tests: config.modules[ i ].tests
607 });
608 }
609  
610 // The test run is officially beginning now
611 runLoggingCallbacks( "begin", {
612 totalTests: Test.count,
613 modules: modulesLog
614 });
615 }
616  
617 config.blocking = false;
618 process( true );
619 }
620  
621 function resumeProcessing() {
622 runStarted = true;
623  
624 // A slight delay to allow this iteration of the event loop to finish (more assertions, etc.)
625 if ( defined.setTimeout ) {
626 setTimeout(function() {
627 if ( config.current && config.current.semaphore > 0 ) {
628 return;
629 }
630 if ( config.timeout ) {
631 clearTimeout( config.timeout );
632 }
633  
634 begin();
635 }, 13 );
636 } else {
637 begin();
638 }
639 }
640  
641 function pauseProcessing() {
642 config.blocking = true;
643  
644 if ( config.testTimeout && defined.setTimeout ) {
645 clearTimeout( config.timeout );
646 config.timeout = setTimeout(function() {
647 if ( config.current ) {
648 config.current.semaphore = 0;
649 QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) );
650 } else {
651 throw new Error( "Test timed out" );
652 }
653 resumeProcessing();
654 }, config.testTimeout );
655 }
656 }
657  
658 function saveGlobal() {
659 config.pollution = [];
660  
661 if ( config.noglobals ) {
662 for ( var key in window ) {
663 if ( hasOwn.call( window, key ) ) {
664 // in Opera sometimes DOM element ids show up here, ignore them
665 if ( /^qunit-test-output/.test( key ) ) {
666 continue;
667 }
668 config.pollution.push( key );
669 }
670 }
671 }
672 }
673  
674 function checkPollution() {
675 var newGlobals,
676 deletedGlobals,
677 old = config.pollution;
678  
679 saveGlobal();
680  
681 newGlobals = diff( config.pollution, old );
682 if ( newGlobals.length > 0 ) {
683 QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) );
684 }
685  
686 deletedGlobals = diff( old, config.pollution );
687 if ( deletedGlobals.length > 0 ) {
688 QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) );
689 }
690 }
691  
692 // returns a new Array with the elements that are in a but not in b
693 function diff( a, b ) {
694 var i, j,
695 result = a.slice();
696  
697 for ( i = 0; i < result.length; i++ ) {
698 for ( j = 0; j < b.length; j++ ) {
699 if ( result[ i ] === b[ j ] ) {
700 result.splice( i, 1 );
701 i--;
702 break;
703 }
704 }
705 }
706 return result;
707 }
708  
709 function extend( a, b, undefOnly ) {
710 for ( var prop in b ) {
711 if ( hasOwn.call( b, prop ) ) {
712  
713 // Avoid "Member not found" error in IE8 caused by messing with window.constructor
714 if ( !( prop === "constructor" && a === window ) ) {
715 if ( b[ prop ] === undefined ) {
716 delete a[ prop ];
717 } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) {
718 a[ prop ] = b[ prop ];
719 }
720 }
721 }
722 }
723  
724 return a;
725 }
726  
727 function runLoggingCallbacks( key, args ) {
728 var i, l, callbacks;
729  
730 callbacks = config.callbacks[ key ];
731 for ( i = 0, l = callbacks.length; i < l; i++ ) {
732 callbacks[ i ]( args );
733 }
734 }
735  
736 // DEPRECATED: This will be removed on 2.0.0+
737 // This function verifies if the loggingCallbacks were modified by the user
738 // If so, it will restore it, assign the given callback and print a console warning
739 function verifyLoggingCallbacks() {
740 var loggingCallback, userCallback;
741  
742 for ( loggingCallback in loggingCallbacks ) {
743 if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) {
744  
745 userCallback = QUnit[ loggingCallback ];
746  
747 // Restore the callback function
748 QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ];
749  
750 // Assign the deprecated given callback
751 QUnit[ loggingCallback ]( userCallback );
752  
753 if ( window.console && window.console.warn ) {
754 window.console.warn(
755 "QUnit." + loggingCallback + " was replaced with a new value.\n" +
756 "Please, check out the documentation on how to apply logging callbacks.\n" +
757 "Reference: http://api.qunitjs.com/category/callbacks/"
758 );
759 }
760 }
761 }
762 }
763  
764 // from jquery.js
765 function inArray( elem, array ) {
766 if ( array.indexOf ) {
767 return array.indexOf( elem );
768 }
769  
770 for ( var i = 0, length = array.length; i < length; i++ ) {
771 if ( array[ i ] === elem ) {
772 return i;
773 }
774 }
775  
776 return -1;
777 }
778  
779 function Test( settings ) {
780 var i, l;
781  
782 ++Test.count;
783  
784 extend( this, settings );
785 this.assertions = [];
786 this.semaphore = 0;
787 this.usedAsync = false;
788 this.module = config.currentModule;
789 this.stack = sourceFromStacktrace( 3 );
790  
791 // Register unique strings
792 for ( i = 0, l = this.module.tests; i < l.length; i++ ) {
793 if ( this.module.tests[ i ].name === this.testName ) {
794 this.testName += " ";
795 }
796 }
797  
798 this.testId = generateHash( this.module.name, this.testName );
799  
800 this.module.tests.push({
801 name: this.testName,
802 testId: this.testId
803 });
804  
805 if ( settings.skip ) {
806  
807 // Skipped tests will fully ignore any sent callback
808 this.callback = function() {};
809 this.async = false;
810 this.expected = 0;
811 } else {
812 this.assert = new Assert( this );
813 }
814 }
815  
816 Test.count = 0;
817  
818 Test.prototype = {
819 before: function() {
820 if (
821  
822 // Emit moduleStart when we're switching from one module to another
823 this.module !== config.previousModule ||
824  
825 // They could be equal (both undefined) but if the previousModule property doesn't
826 // yet exist it means this is the first test in a suite that isn't wrapped in a
827 // module, in which case we'll just emit a moduleStart event for 'undefined'.
828 // Without this, reporters can get testStart before moduleStart which is a problem.
829 !hasOwn.call( config, "previousModule" )
830 ) {
831 if ( hasOwn.call( config, "previousModule" ) ) {
832 runLoggingCallbacks( "moduleDone", {
833 name: config.previousModule.name,
834 tests: config.previousModule.tests,
835 failed: config.moduleStats.bad,
836 passed: config.moduleStats.all - config.moduleStats.bad,
837 total: config.moduleStats.all,
838 runtime: now() - config.moduleStats.started
839 });
840 }
841 config.previousModule = this.module;
842 config.moduleStats = { all: 0, bad: 0, started: now() };
843 runLoggingCallbacks( "moduleStart", {
844 name: this.module.name,
845 tests: this.module.tests
846 });
847 }
848  
849 config.current = this;
850  
851 this.testEnvironment = extend( {}, this.module.testEnvironment );
852 delete this.testEnvironment.beforeEach;
853 delete this.testEnvironment.afterEach;
854  
855 this.started = now();
856 runLoggingCallbacks( "testStart", {
857 name: this.testName,
858 module: this.module.name,
859 testId: this.testId
860 });
861  
862 if ( !config.pollution ) {
863 saveGlobal();
864 }
865 },
866  
867 run: function() {
868 var promise;
869  
870 config.current = this;
871  
872 if ( this.async ) {
873 QUnit.stop();
874 }
875  
876 this.callbackStarted = now();
877  
878 if ( config.notrycatch ) {
879 promise = this.callback.call( this.testEnvironment, this.assert );
880 this.resolvePromise( promise );
881 return;
882 }
883  
884 try {
885 promise = this.callback.call( this.testEnvironment, this.assert );
886 this.resolvePromise( promise );
887 } catch ( e ) {
888 this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " +
889 this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
890  
891 // else next test will carry the responsibility
892 saveGlobal();
893  
894 // Restart the tests if they're blocking
895 if ( config.blocking ) {
896 QUnit.start();
897 }
898 }
899 },
900  
901 after: function() {
902 checkPollution();
903 },
904  
905 queueHook: function( hook, hookName ) {
906 var promise,
907 test = this;
908 return function runHook() {
909 config.current = test;
910 if ( config.notrycatch ) {
911 promise = hook.call( test.testEnvironment, test.assert );
912 test.resolvePromise( promise, hookName );
913 return;
914 }
915 try {
916 promise = hook.call( test.testEnvironment, test.assert );
917 test.resolvePromise( promise, hookName );
918 } catch ( error ) {
919 test.pushFailure( hookName + " failed on " + test.testName + ": " +
920 ( error.message || error ), extractStacktrace( error, 0 ) );
921 }
922 };
923 },
924  
925 // Currently only used for module level hooks, can be used to add global level ones
926 hooks: function( handler ) {
927 var hooks = [];
928  
929 // Hooks are ignored on skipped tests
930 if ( this.skip ) {
931 return hooks;
932 }
933  
934 if ( this.module.testEnvironment &&
935 QUnit.objectType( this.module.testEnvironment[ handler ] ) === "function" ) {
936 hooks.push( this.queueHook( this.module.testEnvironment[ handler ], handler ) );
937 }
938  
939 return hooks;
940 },
941  
942 finish: function() {
943 config.current = this;
944 if ( config.requireExpects && this.expected === null ) {
945 this.pushFailure( "Expected number of assertions to be defined, but expect() was " +
946 "not called.", this.stack );
947 } else if ( this.expected !== null && this.expected !== this.assertions.length ) {
948 this.pushFailure( "Expected " + this.expected + " assertions, but " +
949 this.assertions.length + " were run", this.stack );
950 } else if ( this.expected === null && !this.assertions.length ) {
951 this.pushFailure( "Expected at least one assertion, but none were run - call " +
952 "expect(0) to accept zero assertions.", this.stack );
953 }
954  
955 var i,
956 bad = 0;
957  
958 this.runtime = now() - this.started;
959 config.stats.all += this.assertions.length;
960 config.moduleStats.all += this.assertions.length;
961  
962 for ( i = 0; i < this.assertions.length; i++ ) {
963 if ( !this.assertions[ i ].result ) {
964 bad++;
965 config.stats.bad++;
966 config.moduleStats.bad++;
967 }
968 }
969  
970 runLoggingCallbacks( "testDone", {
971 name: this.testName,
972 module: this.module.name,
973 skipped: !!this.skip,
974 failed: bad,
975 passed: this.assertions.length - bad,
976 total: this.assertions.length,
977 runtime: this.runtime,
978  
979 // HTML Reporter use
980 assertions: this.assertions,
981 testId: this.testId,
982  
983 // DEPRECATED: this property will be removed in 2.0.0, use runtime instead
984 duration: this.runtime
985 });
986  
987 // QUnit.reset() is deprecated and will be replaced for a new
988 // fixture reset function on QUnit 2.0/2.1.
989 // It's still called here for backwards compatibility handling
990 QUnit.reset();
991  
992 config.current = undefined;
993 },
994  
995 queue: function() {
996 var bad,
997 test = this;
998  
999 if ( !this.valid() ) {
1000 return;
1001 }
1002  
1003 function run() {
1004  
1005 // each of these can by async
1006 synchronize([
1007 function() {
1008 test.before();
1009 },
1010  
1011 test.hooks( "beforeEach" ),
1012  
1013 function() {
1014 test.run();
1015 },
1016  
1017 test.hooks( "afterEach" ).reverse(),
1018  
1019 function() {
1020 test.after();
1021 },
1022 function() {
1023 test.finish();
1024 }
1025 ]);
1026 }
1027  
1028 // `bad` initialized at top of scope
1029 // defer when previous test run passed, if storage is available
1030 bad = QUnit.config.reorder && defined.sessionStorage &&
1031 +sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName );
1032  
1033 if ( bad ) {
1034 run();
1035 } else {
1036 synchronize( run, true );
1037 }
1038 },
1039  
1040 push: function( result, actual, expected, message ) {
1041 var source,
1042 details = {
1043 module: this.module.name,
1044 name: this.testName,
1045 result: result,
1046 message: message,
1047 actual: actual,
1048 expected: expected,
1049 testId: this.testId,
1050 runtime: now() - this.started
1051 };
1052  
1053 if ( !result ) {
1054 source = sourceFromStacktrace();
1055  
1056 if ( source ) {
1057 details.source = source;
1058 }
1059 }
1060  
1061 runLoggingCallbacks( "log", details );
1062  
1063 this.assertions.push({
1064 result: !!result,
1065 message: message
1066 });
1067 },
1068  
1069 pushFailure: function( message, source, actual ) {
1070 if ( !this instanceof Test ) {
1071 throw new Error( "pushFailure() assertion outside test context, was " +
1072 sourceFromStacktrace( 2 ) );
1073 }
1074  
1075 var details = {
1076 module: this.module.name,
1077 name: this.testName,
1078 result: false,
1079 message: message || "error",
1080 actual: actual || null,
1081 testId: this.testId,
1082 runtime: now() - this.started
1083 };
1084  
1085 if ( source ) {
1086 details.source = source;
1087 }
1088  
1089 runLoggingCallbacks( "log", details );
1090  
1091 this.assertions.push({
1092 result: false,
1093 message: message
1094 });
1095 },
1096  
1097 resolvePromise: function( promise, phase ) {
1098 var then, message,
1099 test = this;
1100 if ( promise != null ) {
1101 then = promise.then;
1102 if ( QUnit.objectType( then ) === "function" ) {
1103 QUnit.stop();
1104 then.call(
1105 promise,
1106 QUnit.start,
1107 function( error ) {
1108 message = "Promise rejected " +
1109 ( !phase ? "during" : phase.replace( /Each$/, "" ) ) +
1110 " " + test.testName + ": " + ( error.message || error );
1111 test.pushFailure( message, extractStacktrace( error, 0 ) );
1112  
1113 // else next test will carry the responsibility
1114 saveGlobal();
1115  
1116 // Unblock
1117 QUnit.start();
1118 }
1119 );
1120 }
1121 }
1122 },
1123  
1124 valid: function() {
1125 var include,
1126 filter = config.filter,
1127 module = QUnit.urlParams.module && QUnit.urlParams.module.toLowerCase(),
1128 fullName = ( this.module.name + ": " + this.testName ).toLowerCase();
1129  
1130 // Internally-generated tests are always valid
1131 if ( this.callback && this.callback.validTest ) {
1132 return true;
1133 }
1134  
1135 if ( config.testId.length > 0 && inArray( this.testId, config.testId ) < 0 ) {
1136 return false;
1137 }
1138  
1139 if ( module && ( !this.module.name || this.module.name.toLowerCase() !== module ) ) {
1140 return false;
1141 }
1142  
1143 if ( !filter ) {
1144 return true;
1145 }
1146  
1147 include = filter.charAt( 0 ) !== "!";
1148 if ( !include ) {
1149 filter = filter.toLowerCase().slice( 1 );
1150 }
1151  
1152 // If the filter matches, we need to honour include
1153 if ( fullName.indexOf( filter ) !== -1 ) {
1154 return include;
1155 }
1156  
1157 // Otherwise, do the opposite
1158 return !include;
1159 }
1160  
1161 };
1162  
1163 // Resets the test setup. Useful for tests that modify the DOM.
1164 /*
1165 DEPRECATED: Use multiple tests instead of resetting inside a test.
1166 Use testStart or testDone for custom cleanup.
1167 This method will throw an error in 2.0, and will be removed in 2.1
1168 */
1169 QUnit.reset = function() {
1170  
1171 // Return on non-browser environments
1172 // This is necessary to not break on node tests
1173 if ( typeof window === "undefined" ) {
1174 return;
1175 }
1176  
1177 var fixture = defined.document && document.getElementById &&
1178 document.getElementById( "qunit-fixture" );
1179  
1180 if ( fixture ) {
1181 fixture.innerHTML = config.fixture;
1182 }
1183 };
1184  
1185 QUnit.pushFailure = function() {
1186 if ( !QUnit.config.current ) {
1187 throw new Error( "pushFailure() assertion outside test context, in " +
1188 sourceFromStacktrace( 2 ) );
1189 }
1190  
1191 // Gets current test obj
1192 var currentTest = QUnit.config.current;
1193  
1194 return currentTest.pushFailure.apply( currentTest, arguments );
1195 };
1196  
1197 // Based on Java's String.hashCode, a simple but not
1198 // rigorously collision resistant hashing function
1199 function generateHash( module, testName ) {
1200 var hex,
1201 i = 0,
1202 hash = 0,
1203 str = module + "\x1C" + testName,
1204 len = str.length;
1205  
1206 for ( ; i < len; i++ ) {
1207 hash = ( ( hash << 5 ) - hash ) + str.charCodeAt( i );
1208 hash |= 0;
1209 }
1210  
1211 // Convert the possibly negative integer hash code into an 8 character hex string, which isn't
1212 // strictly necessary but increases user understanding that the id is a SHA-like hash
1213 hex = ( 0x100000000 + hash ).toString( 16 );
1214 if ( hex.length < 8 ) {
1215 hex = "0000000" + hex;
1216 }
1217  
1218 return hex.slice( -8 );
1219 }
1220  
1221 function Assert( testContext ) {
1222 this.test = testContext;
1223 }
1224  
1225 // Assert helpers
1226 QUnit.assert = Assert.prototype = {
1227  
1228 // Specify the number of expected assertions to guarantee that failed test
1229 // (no assertions are run at all) don't slip through.
1230 expect: function( asserts ) {
1231 if ( arguments.length === 1 ) {
1232 this.test.expected = asserts;
1233 } else {
1234 return this.test.expected;
1235 }
1236 },
1237  
1238 // Increment this Test's semaphore counter, then return a single-use function that
1239 // decrements that counter a maximum of once.
1240 async: function() {
1241 var test = this.test,
1242 popped = false;
1243  
1244 test.semaphore += 1;
1245 test.usedAsync = true;
1246 pauseProcessing();
1247  
1248 return function done() {
1249 if ( !popped ) {
1250 test.semaphore -= 1;
1251 popped = true;
1252 resumeProcessing();
1253 } else {
1254 test.pushFailure( "Called the callback returned from `assert.async` more than once",
1255 sourceFromStacktrace( 2 ) );
1256 }
1257 };
1258 },
1259  
1260 // Exports test.push() to the user API
1261 push: function( /* result, actual, expected, message */ ) {
1262 var assert = this,
1263 currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current;
1264  
1265 // Backwards compatibility fix.
1266 // Allows the direct use of global exported assertions and QUnit.assert.*
1267 // Although, it's use is not recommended as it can leak assertions
1268 // to other tests from async tests, because we only get a reference to the current test,
1269 // not exactly the test where assertion were intended to be called.
1270 if ( !currentTest ) {
1271 throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) );
1272 }
1273  
1274 if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) {
1275 currentTest.pushFailure( "Assertion after the final `assert.async` was resolved",
1276 sourceFromStacktrace( 2 ) );
1277  
1278 // Allow this assertion to continue running anyway...
1279 }
1280  
1281 if ( !( assert instanceof Assert ) ) {
1282 assert = currentTest.assert;
1283 }
1284 return assert.test.push.apply( assert.test, arguments );
1285 },
1286  
1287 /**
1288 * Asserts rough true-ish result.
1289 * @name ok
1290 * @function
1291 * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
1292 */
1293 ok: function( result, message ) {
1294 message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " +
1295 QUnit.dump.parse( result ) );
1296 this.push( !!result, result, true, message );
1297 },
1298  
1299 /**
1300 * Assert that the first two arguments are equal, with an optional message.
1301 * Prints out both actual and expected values.
1302 * @name equal
1303 * @function
1304 * @example equal( format( "{0} bytes.", 2), "2 bytes.", "replaces {0} with next argument" );
1305 */
1306 equal: function( actual, expected, message ) {
1307 /*jshint eqeqeq:false */
1308 this.push( expected == actual, actual, expected, message );
1309 },
1310  
1311 /**
1312 * @name notEqual
1313 * @function
1314 */
1315 notEqual: function( actual, expected, message ) {
1316 /*jshint eqeqeq:false */
1317 this.push( expected != actual, actual, expected, message );
1318 },
1319  
1320 /**
1321 * @name propEqual
1322 * @function
1323 */
1324 propEqual: function( actual, expected, message ) {
1325 actual = objectValues( actual );
1326 expected = objectValues( expected );
1327 this.push( QUnit.equiv( actual, expected ), actual, expected, message );
1328 },
1329  
1330 /**
1331 * @name notPropEqual
1332 * @function
1333 */
1334 notPropEqual: function( actual, expected, message ) {
1335 actual = objectValues( actual );
1336 expected = objectValues( expected );
1337 this.push( !QUnit.equiv( actual, expected ), actual, expected, message );
1338 },
1339  
1340 /**
1341 * @name deepEqual
1342 * @function
1343 */
1344 deepEqual: function( actual, expected, message ) {
1345 this.push( QUnit.equiv( actual, expected ), actual, expected, message );
1346 },
1347  
1348 /**
1349 * @name notDeepEqual
1350 * @function
1351 */
1352 notDeepEqual: function( actual, expected, message ) {
1353 this.push( !QUnit.equiv( actual, expected ), actual, expected, message );
1354 },
1355  
1356 /**
1357 * @name strictEqual
1358 * @function
1359 */
1360 strictEqual: function( actual, expected, message ) {
1361 this.push( expected === actual, actual, expected, message );
1362 },
1363  
1364 /**
1365 * @name notStrictEqual
1366 * @function
1367 */
1368 notStrictEqual: function( actual, expected, message ) {
1369 this.push( expected !== actual, actual, expected, message );
1370 },
1371  
1372 "throws": function( block, expected, message ) {
1373 var actual, expectedType,
1374 expectedOutput = expected,
1375 ok = false;
1376  
1377 // 'expected' is optional unless doing string comparison
1378 if ( message == null && typeof expected === "string" ) {
1379 message = expected;
1380 expected = null;
1381 }
1382  
1383 this.test.ignoreGlobalErrors = true;
1384 try {
1385 block.call( this.test.testEnvironment );
1386 } catch (e) {
1387 actual = e;
1388 }
1389 this.test.ignoreGlobalErrors = false;
1390  
1391 if ( actual ) {
1392 expectedType = QUnit.objectType( expected );
1393  
1394 // we don't want to validate thrown error
1395 if ( !expected ) {
1396 ok = true;
1397 expectedOutput = null;
1398  
1399 // expected is a regexp
1400 } else if ( expectedType === "regexp" ) {
1401 ok = expected.test( errorString( actual ) );
1402  
1403 // expected is a string
1404 } else if ( expectedType === "string" ) {
1405 ok = expected === errorString( actual );
1406  
1407 // expected is a constructor, maybe an Error constructor
1408 } else if ( expectedType === "function" && actual instanceof expected ) {
1409 ok = true;
1410  
1411 // expected is an Error object
1412 } else if ( expectedType === "object" ) {
1413 ok = actual instanceof expected.constructor &&
1414 actual.name === expected.name &&
1415 actual.message === expected.message;
1416  
1417 // expected is a validation function which returns true if validation passed
1418 } else if ( expectedType === "function" && expected.call( {}, actual ) === true ) {
1419 expectedOutput = null;
1420 ok = true;
1421 }
1422  
1423 this.push( ok, actual, expectedOutput, message );
1424 } else {
1425 this.test.pushFailure( message, null, "No exception was thrown." );
1426 }
1427 }
1428 };
1429  
1430 // Provide an alternative to assert.throws(), for enviroments that consider throws a reserved word
1431 // Known to us are: Closure Compiler, Narwhal
1432 (function() {
1433 /*jshint sub:true */
1434 Assert.prototype.raises = Assert.prototype[ "throws" ];
1435 }());
1436  
1437 // Test for equality any JavaScript type.
1438 // Author: Philippe Rathé <prathe@gmail.com>
1439 QUnit.equiv = (function() {
1440  
1441 // Call the o related callback with the given arguments.
1442 function bindCallbacks( o, callbacks, args ) {
1443 var prop = QUnit.objectType( o );
1444 if ( prop ) {
1445 if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
1446 return callbacks[ prop ].apply( callbacks, args );
1447 } else {
1448 return callbacks[ prop ]; // or undefined
1449 }
1450 }
1451 }
1452  
1453 // the real equiv function
1454 var innerEquiv,
1455  
1456 // stack to decide between skip/abort functions
1457 callers = [],
1458  
1459 // stack to avoiding loops from circular referencing
1460 parents = [],
1461 parentsB = [],
1462  
1463 getProto = Object.getPrototypeOf || function( obj ) {
1464 /* jshint camelcase: false, proto: true */
1465 return obj.__proto__;
1466 },
1467 callbacks = (function() {
1468  
1469 // for string, boolean, number and null
1470 function useStrictEquality( b, a ) {
1471  
1472 /*jshint eqeqeq:false */
1473 if ( b instanceof a.constructor || a instanceof b.constructor ) {
1474  
1475 // to catch short annotation VS 'new' annotation of a
1476 // declaration
1477 // e.g. var i = 1;
1478 // var j = new Number(1);
1479 return a == b;
1480 } else {
1481 return a === b;
1482 }
1483 }
1484  
1485 return {
1486 "string": useStrictEquality,
1487 "boolean": useStrictEquality,
1488 "number": useStrictEquality,
1489 "null": useStrictEquality,
1490 "undefined": useStrictEquality,
1491  
1492 "nan": function( b ) {
1493 return isNaN( b );
1494 },
1495  
1496 "date": function( b, a ) {
1497 return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
1498 },
1499  
1500 "regexp": function( b, a ) {
1501 return QUnit.objectType( b ) === "regexp" &&
1502  
1503 // the regex itself
1504 a.source === b.source &&
1505  
1506 // and its modifiers
1507 a.global === b.global &&
1508  
1509 // (gmi) ...
1510 a.ignoreCase === b.ignoreCase &&
1511 a.multiline === b.multiline &&
1512 a.sticky === b.sticky;
1513 },
1514  
1515 // - skip when the property is a method of an instance (OOP)
1516 // - abort otherwise,
1517 // initial === would have catch identical references anyway
1518 "function": function() {
1519 var caller = callers[ callers.length - 1 ];
1520 return caller !== Object && typeof caller !== "undefined";
1521 },
1522  
1523 "array": function( b, a ) {
1524 var i, j, len, loop, aCircular, bCircular;
1525  
1526 // b could be an object literal here
1527 if ( QUnit.objectType( b ) !== "array" ) {
1528 return false;
1529 }
1530  
1531 len = a.length;
1532 if ( len !== b.length ) {
1533 // safe and faster
1534 return false;
1535 }
1536  
1537 // track reference to avoid circular references
1538 parents.push( a );
1539 parentsB.push( b );
1540 for ( i = 0; i < len; i++ ) {
1541 loop = false;
1542 for ( j = 0; j < parents.length; j++ ) {
1543 aCircular = parents[ j ] === a[ i ];
1544 bCircular = parentsB[ j ] === b[ i ];
1545 if ( aCircular || bCircular ) {
1546 if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
1547 loop = true;
1548 } else {
1549 parents.pop();
1550 parentsB.pop();
1551 return false;
1552 }
1553 }
1554 }
1555 if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
1556 parents.pop();
1557 parentsB.pop();
1558 return false;
1559 }
1560 }
1561 parents.pop();
1562 parentsB.pop();
1563 return true;
1564 },
1565  
1566 "object": function( b, a ) {
1567  
1568 /*jshint forin:false */
1569 var i, j, loop, aCircular, bCircular,
1570 // Default to true
1571 eq = true,
1572 aProperties = [],
1573 bProperties = [];
1574  
1575 // comparing constructors is more strict than using
1576 // instanceof
1577 if ( a.constructor !== b.constructor ) {
1578  
1579 // Allow objects with no prototype to be equivalent to
1580 // objects with Object as their constructor.
1581 if ( !( ( getProto( a ) === null && getProto( b ) === Object.prototype ) ||
1582 ( getProto( b ) === null && getProto( a ) === Object.prototype ) ) ) {
1583 return false;
1584 }
1585 }
1586  
1587 // stack constructor before traversing properties
1588 callers.push( a.constructor );
1589  
1590 // track reference to avoid circular references
1591 parents.push( a );
1592 parentsB.push( b );
1593  
1594 // be strict: don't ensure hasOwnProperty and go deep
1595 for ( i in a ) {
1596 loop = false;
1597 for ( j = 0; j < parents.length; j++ ) {
1598 aCircular = parents[ j ] === a[ i ];
1599 bCircular = parentsB[ j ] === b[ i ];
1600 if ( aCircular || bCircular ) {
1601 if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
1602 loop = true;
1603 } else {
1604 eq = false;
1605 break;
1606 }
1607 }
1608 }
1609 aProperties.push( i );
1610 if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
1611 eq = false;
1612 break;
1613 }
1614 }
1615  
1616 parents.pop();
1617 parentsB.pop();
1618 callers.pop(); // unstack, we are done
1619  
1620 for ( i in b ) {
1621 bProperties.push( i ); // collect b's properties
1622 }
1623  
1624 // Ensures identical properties name
1625 return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
1626 }
1627 };
1628 }());
1629  
1630 innerEquiv = function() { // can take multiple arguments
1631 var args = [].slice.apply( arguments );
1632 if ( args.length < 2 ) {
1633 return true; // end transition
1634 }
1635  
1636 return ( (function( a, b ) {
1637 if ( a === b ) {
1638 return true; // catch the most you can
1639 } else if ( a === null || b === null || typeof a === "undefined" ||
1640 typeof b === "undefined" ||
1641 QUnit.objectType( a ) !== QUnit.objectType( b ) ) {
1642  
1643 // don't lose time with error prone cases
1644 return false;
1645 } else {
1646 return bindCallbacks( a, callbacks, [ b, a ] );
1647 }
1648  
1649 // apply transition with (1..n) arguments
1650 }( args[ 0 ], args[ 1 ] ) ) &&
1651 innerEquiv.apply( this, args.splice( 1, args.length - 1 ) ) );
1652 };
1653  
1654 return innerEquiv;
1655 }());
1656  
1657 // Based on jsDump by Ariel Flesler
1658 // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
1659 QUnit.dump = (function() {
1660 function quote( str ) {
1661 return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";
1662 }
1663 function literal( o ) {
1664 return o + "";
1665 }
1666 function join( pre, arr, post ) {
1667 var s = dump.separator(),
1668 base = dump.indent(),
1669 inner = dump.indent( 1 );
1670 if ( arr.join ) {
1671 arr = arr.join( "," + s + inner );
1672 }
1673 if ( !arr ) {
1674 return pre + post;
1675 }
1676 return [ pre, inner + arr, base + post ].join( s );
1677 }
1678 function array( arr, stack ) {
1679 var i = arr.length,
1680 ret = new Array( i );
1681  
1682 if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
1683 return "[object Array]";
1684 }
1685  
1686 this.up();
1687 while ( i-- ) {
1688 ret[ i ] = this.parse( arr[ i ], undefined, stack );
1689 }
1690 this.down();
1691 return join( "[", ret, "]" );
1692 }
1693  
1694 var reName = /^function (\w+)/,
1695 dump = {
1696  
1697 // objType is used mostly internally, you can fix a (custom) type in advance
1698 parse: function( obj, objType, stack ) {
1699 stack = stack || [];
1700 var res, parser, parserType,
1701 inStack = inArray( obj, stack );
1702  
1703 if ( inStack !== -1 ) {
1704 return "recursion(" + ( inStack - stack.length ) + ")";
1705 }
1706  
1707 objType = objType || this.typeOf( obj );
1708 parser = this.parsers[ objType ];
1709 parserType = typeof parser;
1710  
1711 if ( parserType === "function" ) {
1712 stack.push( obj );
1713 res = parser.call( this, obj, stack );
1714 stack.pop();
1715 return res;
1716 }
1717 return ( parserType === "string" ) ? parser : this.parsers.error;
1718 },
1719 typeOf: function( obj ) {
1720 var type;
1721 if ( obj === null ) {
1722 type = "null";
1723 } else if ( typeof obj === "undefined" ) {
1724 type = "undefined";
1725 } else if ( QUnit.is( "regexp", obj ) ) {
1726 type = "regexp";
1727 } else if ( QUnit.is( "date", obj ) ) {
1728 type = "date";
1729 } else if ( QUnit.is( "function", obj ) ) {
1730 type = "function";
1731 } else if ( obj.setInterval !== undefined &&
1732 obj.document !== undefined &&
1733 obj.nodeType === undefined ) {
1734 type = "window";
1735 } else if ( obj.nodeType === 9 ) {
1736 type = "document";
1737 } else if ( obj.nodeType ) {
1738 type = "node";
1739 } else if (
1740  
1741 // native arrays
1742 toString.call( obj ) === "[object Array]" ||
1743  
1744 // NodeList objects
1745 ( typeof obj.length === "number" && obj.item !== undefined &&
1746 ( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null &&
1747 obj[ 0 ] === undefined ) ) )
1748 ) {
1749 type = "array";
1750 } else if ( obj.constructor === Error.prototype.constructor ) {
1751 type = "error";
1752 } else {
1753 type = typeof obj;
1754 }
1755 return type;
1756 },
1757 separator: function() {
1758 return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&#160;" : " ";
1759 },
1760 // extra can be a number, shortcut for increasing-calling-decreasing
1761 indent: function( extra ) {
1762 if ( !this.multiline ) {
1763 return "";
1764 }
1765 var chr = this.indentChar;
1766 if ( this.HTML ) {
1767 chr = chr.replace( /\t/g, " " ).replace( / /g, "&#160;" );
1768 }
1769 return new Array( this.depth + ( extra || 0 ) ).join( chr );
1770 },
1771 up: function( a ) {
1772 this.depth += a || 1;
1773 },
1774 down: function( a ) {
1775 this.depth -= a || 1;
1776 },
1777 setParser: function( name, parser ) {
1778 this.parsers[ name ] = parser;
1779 },
1780 // The next 3 are exposed so you can use them
1781 quote: quote,
1782 literal: literal,
1783 join: join,
1784 //
1785 depth: 1,
1786 maxDepth: 5,
1787  
1788 // This is the list of parsers, to modify them, use dump.setParser
1789 parsers: {
1790 window: "[Window]",
1791 document: "[Document]",
1792 error: function( error ) {
1793 return "Error(\"" + error.message + "\")";
1794 },
1795 unknown: "[Unknown]",
1796 "null": "null",
1797 "undefined": "undefined",
1798 "function": function( fn ) {
1799 var ret = "function",
1800  
1801 // functions never have name in IE
1802 name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ];
1803  
1804 if ( name ) {
1805 ret += " " + name;
1806 }
1807 ret += "( ";
1808  
1809 ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" );
1810 return join( ret, dump.parse( fn, "functionCode" ), "}" );
1811 },
1812 array: array,
1813 nodelist: array,
1814 "arguments": array,
1815 object: function( map, stack ) {
1816 var keys, key, val, i, nonEnumerableProperties,
1817 ret = [];
1818  
1819 if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
1820 return "[object Object]";
1821 }
1822  
1823 dump.up();
1824 keys = [];
1825 for ( key in map ) {
1826 keys.push( key );
1827 }
1828  
1829 // Some properties are not always enumerable on Error objects.
1830 nonEnumerableProperties = [ "message", "name" ];
1831 for ( i in nonEnumerableProperties ) {
1832 key = nonEnumerableProperties[ i ];
1833 if ( key in map && !( key in keys ) ) {
1834 keys.push( key );
1835 }
1836 }
1837 keys.sort();
1838 for ( i = 0; i < keys.length; i++ ) {
1839 key = keys[ i ];
1840 val = map[ key ];
1841 ret.push( dump.parse( key, "key" ) + ": " +
1842 dump.parse( val, undefined, stack ) );
1843 }
1844 dump.down();
1845 return join( "{", ret, "}" );
1846 },
1847 node: function( node ) {
1848 var len, i, val,
1849 open = dump.HTML ? "&lt;" : "<",
1850 close = dump.HTML ? "&gt;" : ">",
1851 tag = node.nodeName.toLowerCase(),
1852 ret = open + tag,
1853 attrs = node.attributes;
1854  
1855 if ( attrs ) {
1856 for ( i = 0, len = attrs.length; i < len; i++ ) {
1857 val = attrs[ i ].nodeValue;
1858  
1859 // IE6 includes all attributes in .attributes, even ones not explicitly
1860 // set. Those have values like undefined, null, 0, false, "" or
1861 // "inherit".
1862 if ( val && val !== "inherit" ) {
1863 ret += " " + attrs[ i ].nodeName + "=" +
1864 dump.parse( val, "attribute" );
1865 }
1866 }
1867 }
1868 ret += close;
1869  
1870 // Show content of TextNode or CDATASection
1871 if ( node.nodeType === 3 || node.nodeType === 4 ) {
1872 ret += node.nodeValue;
1873 }
1874  
1875 return ret + open + "/" + tag + close;
1876 },
1877  
1878 // function calls it internally, it's the arguments part of the function
1879 functionArgs: function( fn ) {
1880 var args,
1881 l = fn.length;
1882  
1883 if ( !l ) {
1884 return "";
1885 }
1886  
1887 args = new Array( l );
1888 while ( l-- ) {
1889  
1890 // 97 is 'a'
1891 args[ l ] = String.fromCharCode( 97 + l );
1892 }
1893 return " " + args.join( ", " ) + " ";
1894 },
1895 // object calls it internally, the key part of an item in a map
1896 key: quote,
1897 // function calls it internally, it's the content of the function
1898 functionCode: "[code]",
1899 // node calls it internally, it's an html attribute value
1900 attribute: quote,
1901 string: quote,
1902 date: quote,
1903 regexp: literal,
1904 number: literal,
1905 "boolean": literal
1906 },
1907 // if true, entities are escaped ( <, >, \t, space and \n )
1908 HTML: false,
1909 // indentation unit
1910 indentChar: " ",
1911 // if true, items in a collection, are separated by a \n, else just a space.
1912 multiline: true
1913 };
1914  
1915 return dump;
1916 }());
1917  
1918 // back compat
1919 QUnit.jsDump = QUnit.dump;
1920  
1921 // For browser, export only select globals
1922 if ( typeof window !== "undefined" ) {
1923  
1924 // Deprecated
1925 // Extend assert methods to QUnit and Global scope through Backwards compatibility
1926 (function() {
1927 var i,
1928 assertions = Assert.prototype;
1929  
1930 function applyCurrent( current ) {
1931 return function() {
1932 var assert = new Assert( QUnit.config.current );
1933 current.apply( assert, arguments );
1934 };
1935 }
1936  
1937 for ( i in assertions ) {
1938 QUnit[ i ] = applyCurrent( assertions[ i ] );
1939 }
1940 })();
1941  
1942 (function() {
1943 var i, l,
1944 keys = [
1945 "test",
1946 "module",
1947 "expect",
1948 "asyncTest",
1949 "start",
1950 "stop",
1951 "ok",
1952 "equal",
1953 "notEqual",
1954 "propEqual",
1955 "notPropEqual",
1956 "deepEqual",
1957 "notDeepEqual",
1958 "strictEqual",
1959 "notStrictEqual",
1960 "throws"
1961 ];
1962  
1963 for ( i = 0, l = keys.length; i < l; i++ ) {
1964 window[ keys[ i ] ] = QUnit[ keys[ i ] ];
1965 }
1966 })();
1967  
1968 window.QUnit = QUnit;
1969 }
1970  
1971 // For nodejs
1972 if ( typeof module !== "undefined" && module && module.exports ) {
1973 module.exports = QUnit;
1974  
1975 // For consistency with CommonJS environments' exports
1976 module.exports.QUnit = QUnit;
1977 }
1978  
1979 // For CommonJS with exports, but without module.exports, like Rhino
1980 if ( typeof exports !== "undefined" && exports ) {
1981 exports.QUnit = QUnit;
1982 }
1983  
1984 // Get a reference to the global object, like window in browsers
1985 }( (function() {
1986 return this;
1987 })() ));
1988  
1989 /*istanbul ignore next */
1990 // jscs:disable maximumLineLength
1991 /*
1992 * Javascript Diff Algorithm
1993 * By John Resig (http://ejohn.org/)
1994 * Modified by Chu Alan "sprite"
1995 *
1996 * Released under the MIT license.
1997 *
1998 * More Info:
1999 * http://ejohn.org/projects/javascript-diff-algorithm/
2000 *
2001 * Usage: QUnit.diff(expected, actual)
2002 *
2003 * 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"
2004 */
2005 QUnit.diff = (function() {
2006 var hasOwn = Object.prototype.hasOwnProperty;
2007  
2008 /*jshint eqeqeq:false, eqnull:true */
2009 function diff( o, n ) {
2010 var i,
2011 ns = {},
2012 os = {};
2013  
2014 for ( i = 0; i < n.length; i++ ) {
2015 if ( !hasOwn.call( ns, n[ i ] ) ) {
2016 ns[ n[ i ] ] = {
2017 rows: [],
2018 o: null
2019 };
2020 }
2021 ns[ n[ i ] ].rows.push( i );
2022 }
2023  
2024 for ( i = 0; i < o.length; i++ ) {
2025 if ( !hasOwn.call( os, o[ i ] ) ) {
2026 os[ o[ i ] ] = {
2027 rows: [],
2028 n: null
2029 };
2030 }
2031 os[ o[ i ] ].rows.push( i );
2032 }
2033  
2034 for ( i in ns ) {
2035 if ( hasOwn.call( ns, i ) ) {
2036 if ( ns[ i ].rows.length === 1 && hasOwn.call( os, i ) && os[ i ].rows.length === 1 ) {
2037 n[ ns[ i ].rows[ 0 ] ] = {
2038 text: n[ ns[ i ].rows[ 0 ] ],
2039 row: os[ i ].rows[ 0 ]
2040 };
2041 o[ os[ i ].rows[ 0 ] ] = {
2042 text: o[ os[ i ].rows[ 0 ] ],
2043 row: ns[ i ].rows[ 0 ]
2044 };
2045 }
2046 }
2047 }
2048  
2049 for ( i = 0; i < n.length - 1; i++ ) {
2050 if ( n[ i ].text != null && n[ i + 1 ].text == null && n[ i ].row + 1 < o.length && o[ n[ i ].row + 1 ].text == null &&
2051 n[ i + 1 ] == o[ n[ i ].row + 1 ] ) {
2052  
2053 n[ i + 1 ] = {
2054 text: n[ i + 1 ],
2055 row: n[ i ].row + 1
2056 };
2057 o[ n[ i ].row + 1 ] = {
2058 text: o[ n[ i ].row + 1 ],
2059 row: i + 1
2060 };
2061 }
2062 }
2063  
2064 for ( i = n.length - 1; i > 0; i-- ) {
2065 if ( n[ i ].text != null && n[ i - 1 ].text == null && n[ i ].row > 0 && o[ n[ i ].row - 1 ].text == null &&
2066 n[ i - 1 ] == o[ n[ i ].row - 1 ] ) {
2067  
2068 n[ i - 1 ] = {
2069 text: n[ i - 1 ],
2070 row: n[ i ].row - 1
2071 };
2072 o[ n[ i ].row - 1 ] = {
2073 text: o[ n[ i ].row - 1 ],
2074 row: i - 1
2075 };
2076 }
2077 }
2078  
2079 return {
2080 o: o,
2081 n: n
2082 };
2083 }
2084  
2085 return function( o, n ) {
2086 o = o.replace( /\s+$/, "" );
2087 n = n.replace( /\s+$/, "" );
2088  
2089 var i, pre,
2090 str = "",
2091 out = diff( o === "" ? [] : o.split( /\s+/ ), n === "" ? [] : n.split( /\s+/ ) ),
2092 oSpace = o.match( /\s+/g ),
2093 nSpace = n.match( /\s+/g );
2094  
2095 if ( oSpace == null ) {
2096 oSpace = [ " " ];
2097 } else {
2098 oSpace.push( " " );
2099 }
2100  
2101 if ( nSpace == null ) {
2102 nSpace = [ " " ];
2103 } else {
2104 nSpace.push( " " );
2105 }
2106  
2107 if ( out.n.length === 0 ) {
2108 for ( i = 0; i < out.o.length; i++ ) {
2109 str += "<del>" + out.o[ i ] + oSpace[ i ] + "</del>";
2110 }
2111 } else {
2112 if ( out.n[ 0 ].text == null ) {
2113 for ( n = 0; n < out.o.length && out.o[ n ].text == null; n++ ) {
2114 str += "<del>" + out.o[ n ] + oSpace[ n ] + "</del>";
2115 }
2116 }
2117  
2118 for ( i = 0; i < out.n.length; i++ ) {
2119 if ( out.n[ i ].text == null ) {
2120 str += "<ins>" + out.n[ i ] + nSpace[ i ] + "</ins>";
2121 } else {
2122  
2123 // `pre` initialized at top of scope
2124 pre = "";
2125  
2126 for ( n = out.n[ i ].row + 1; n < out.o.length && out.o[ n ].text == null; n++ ) {
2127 pre += "<del>" + out.o[ n ] + oSpace[ n ] + "</del>";
2128 }
2129 str += " " + out.n[ i ].text + nSpace[ i ] + pre;
2130 }
2131 }
2132 }
2133  
2134 return str;
2135 };
2136 }());
2137 // jscs:enable
2138  
2139 (function() {
2140  
2141 // Deprecated QUnit.init - Ref #530
2142 // Re-initialize the configuration options
2143 QUnit.init = function() {
2144 var tests, banner, result, qunit,
2145 config = QUnit.config;
2146  
2147 config.stats = { all: 0, bad: 0 };
2148 config.moduleStats = { all: 0, bad: 0 };
2149 config.started = 0;
2150 config.updateRate = 1000;
2151 config.blocking = false;
2152 config.autostart = true;
2153 config.autorun = false;
2154 config.filter = "";
2155 config.queue = [];
2156  
2157 // Return on non-browser environments
2158 // This is necessary to not break on node tests
2159 if ( typeof window === "undefined" ) {
2160 return;
2161 }
2162  
2163 qunit = id( "qunit" );
2164 if ( qunit ) {
2165 qunit.innerHTML =
2166 "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
2167 "<h2 id='qunit-banner'></h2>" +
2168 "<div id='qunit-testrunner-toolbar'></div>" +
2169 "<h2 id='qunit-userAgent'></h2>" +
2170 "<ol id='qunit-tests'></ol>";
2171 }
2172  
2173 tests = id( "qunit-tests" );
2174 banner = id( "qunit-banner" );
2175 result = id( "qunit-testresult" );
2176  
2177 if ( tests ) {
2178 tests.innerHTML = "";
2179 }
2180  
2181 if ( banner ) {
2182 banner.className = "";
2183 }
2184  
2185 if ( result ) {
2186 result.parentNode.removeChild( result );
2187 }
2188  
2189 if ( tests ) {
2190 result = document.createElement( "p" );
2191 result.id = "qunit-testresult";
2192 result.className = "result";
2193 tests.parentNode.insertBefore( result, tests );
2194 result.innerHTML = "Running...<br />&#160;";
2195 }
2196 };
2197  
2198 // Don't load the HTML Reporter on non-Browser environments
2199 if ( typeof window === "undefined" ) {
2200 return;
2201 }
2202  
2203 var config = QUnit.config,
2204 hasOwn = Object.prototype.hasOwnProperty,
2205 defined = {
2206 document: window.document !== undefined,
2207 sessionStorage: (function() {
2208 var x = "qunit-test-string";
2209 try {
2210 sessionStorage.setItem( x, x );
2211 sessionStorage.removeItem( x );
2212 return true;
2213 } catch ( e ) {
2214 return false;
2215 }
2216 }())
2217 },
2218 modulesList = [];
2219  
2220 /**
2221 * Escape text for attribute or text content.
2222 */
2223 function escapeText( s ) {
2224 if ( !s ) {
2225 return "";
2226 }
2227 s = s + "";
2228  
2229 // Both single quotes and double quotes (for attributes)
2230 return s.replace( /['"<>&]/g, function( s ) {
2231 <> switch ( s ) {
2232 <> case "'":
2233 <> return "&#039;";
2234 <> case "\"":
2235 <> return "&quot;";
2236 <> case "<":
2237 <> return "&lt;";
2238 <> case ">":
2239 <> return "&gt;";
2240 <> case "&":
2241 <> return "&amp;";
2242 <> }
2243 <> });
2244 <>}
2245  
2246 <>/**
2247 <> * @param {HTMLElement} elem
2248 <> * @param {string} type
2249 <> * @param {Function} fn
2250 <> */
2251 <>function addEvent( elem, type, fn ) {
2252 <> if ( elem.addEventListener ) {
2253  
2254 <> // Standards-based browsers
2255 <> elem.addEventListener( type, fn, false );
2256 <> } else if ( elem.attachEvent ) {
2257  
2258 <> // support: IE <9
2259 <> elem.attachEvent( "on" + type, fn );
2260 <> }
2261 <>}
2262  
2263 <>/**
2264 <> * @param {Array|NodeList} elems
2265 <> * @param {string} type
2266 <> * @param {Function} fn
2267 <> */
2268 <>function addEvents( elems, type, fn ) {
2269 <> var i = elems.length;
2270 <> while ( i-- ) {
2271 <> addEvent( elems[ i ], type, fn );
2272 <> }
2273 <>}
2274  
2275 <>function hasClass( elem, name ) {
2276 <> return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0;
2277 <>}
2278  
2279 <>function addClass( elem, name ) {
2280 <> if ( !hasClass( elem, name ) ) {
2281 <> elem.className += ( elem.className ? " " : "" ) + name;
2282 <> }
2283 <>}
2284  
2285 <>function toggleClass( elem, name ) {
2286 <> if ( hasClass( elem, name ) ) {
2287 <> removeClass( elem, name );
2288 <> } else {
2289 <> addClass( elem, name );
2290 <> }
2291 <>}
2292  
2293 <>function removeClass( elem, name ) {
2294 <> var set = " " + elem.className + " ";
2295  
2296 <> // Class name may appear multiple times
2297 <> while ( set.indexOf( " " + name + " " ) >= 0 ) {
2298 <> set = set.replace( " " + name + " ", " " );
2299 <> }
2300  
2301 <> // trim for prettiness
2302 <> elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" );
2303 <>}
2304  
2305 <>function id( name ) {
2306 <> return defined.document && document.getElementById && document.getElementById( name );
2307 <>}
2308  
2309 <>function getUrlConfigHtml() {
2310 <> var i, j, val,
2311 <> escaped, escapedTooltip,
2312 <> selection = false,
2313 <> len = config.urlConfig.length,
2314 <> urlConfigHtml = "";
2315  
2316 <> for ( i = 0; i < len; i++ ) {
2317 <> val = config.urlConfig[ i ];
2318 <> if ( typeof val === "string" ) {
2319 <> val = {
2320 <> id: val,
2321 <> label: val
2322 <> };
2323 <> }
2324  
2325 <> escaped = escapeText( val.id );
2326 <> escapedTooltip = escapeText( val.tooltip );
2327  
2328 <> if ( config[ val.id ] === undefined ) {
2329 <> config[ val.id ] = QUnit.urlParams[ val.id ];
2330 <> }
2331  
2332 <> if ( !val.value || typeof val.value === "string" ) {
2333 <> urlConfigHtml += "<input id='qunit-urlconfig-" + escaped +
2334 <> "' name='" + escaped + "' type='checkbox'" +
2335 <> ( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
2336 <> ( config[ val.id ] ? " checked='checked'" : "" ) +
2337 <> " title='" + escapedTooltip + "' /><label for='qunit-urlconfig-" + escaped +
2338 <> "' title='" + escapedTooltip + "'>" + val.label + "</label>";
2339 <> } else {
2340 <> urlConfigHtml += "<label for='qunit-urlconfig-" + escaped +
2341 <> "' title='" + escapedTooltip + "'>" + val.label +
2342 <> ": </label><select id='qunit-urlconfig-" + escaped +
2343 <> "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
2344  
2345 <> if ( QUnit.is( "array", val.value ) ) {
2346 <> for ( j = 0; j < val.value.length; j++ ) {
2347 <> escaped = escapeText( val.value[ j ] );
2348 <> urlConfigHtml += "<option value='" + escaped + "'" +
2349 <> ( config[ val.id ] === val.value[ j ] ?
2350 <> ( selection = true ) && " selected='selected'" : "" ) +
2351 <> ">" + escaped + "</option>";
2352 <> }
2353 <> } else {
2354 <> for ( j in val.value ) {
2355 <> if ( hasOwn.call( val.value, j ) ) {
2356 <> urlConfigHtml += "<option value='" + escapeText( j ) + "'" +
2357 <> ( config[ val.id ] === j ?
2358 <> ( selection = true ) && " selected='selected'" : "" ) +
2359 <> ">" + escapeText( val.value[ j ] ) + "</option>";
2360 <> }
2361 <> }
2362 <> }
2363 <> if ( config[ val.id ] && !selection ) {
2364 <> escaped = escapeText( config[ val.id ] );
2365 <> urlConfigHtml += "<option value='" + escaped +
2366 <> "' selected='selected' disabled='disabled'>" + escaped + "</option>";
2367 <> }
2368 <> urlConfigHtml += "</select>";
2369 <> }
2370 <> }
2371  
2372 <> return urlConfigHtml;
2373 <>}
2374  
2375 <>// Handle "click" events on toolbar checkboxes and "change" for select menus.
2376 <>// Updates the URL with the new state of `config.urlConfig` values.
2377 <>function toolbarChanged() {
2378 <> var updatedUrl, value,
2379 <> field = this,
2380 <> params = {};
2381  
2382 <> // Detect if field is a select menu or a checkbox
2383 <> if ( "selectedIndex" in field ) {
2384 <> value = field.options[ field.selectedIndex ].value || undefined;
2385 <> } else {
2386 <> value = field.checked ? ( field.defaultValue || true ) : undefined;
2387 <> }
2388  
2389 <> params[ field.name ] = value;
2390 <> updatedUrl = setUrl( params );
2391  
2392 <> if ( "hidepassed" === field.name && "replaceState" in window.history ) {
2393 <> config[ field.name ] = value || false;
2394 <> if ( value ) {
2395 <> addClass( id( "qunit-tests" ), "hidepass" );
2396 <> } else {
2397 <> removeClass( id( "qunit-tests" ), "hidepass" );
2398 <> }
2399  
2400 <> // It is not necessary to refresh the whole page
2401 <> window.history.replaceState( null, "", updatedUrl );
2402 <> } else {
2403 <> window.location = updatedUrl;
2404 <> }
2405 <>}
2406  
2407 <>function setUrl( params ) {
2408 <> var key,
2409 <> querystring = "?";
2410  
2411 <> params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params );
2412  
2413 <> for ( key in params ) {
2414 <> if ( hasOwn.call( params, key ) ) {
2415 <> if ( params[ key ] === undefined ) {
2416 <> continue;
2417 <> }
2418 <> querystring += encodeURIComponent( key );
2419 <> if ( params[ key ] !== true ) {
2420 <> querystring += "=" + encodeURIComponent( params[ key ] );
2421 <> }
2422 <> querystring += "&";
2423 <> }
2424 <> }
2425 <> return location.protocol + "//" + location.host +
2426 <> location.pathname + querystring.slice( 0, -1 );
2427 <>}
2428  
2429 <>function applyUrlParams() {
2430 <> var selectBox = id( "qunit-modulefilter" ),
2431 <> selection = decodeURIComponent( selectBox.options[ selectBox.selectedIndex ].value ),
2432 <> filter = id( "qunit-filter-input" ).value;
2433  
2434 <> window.location = setUrl({
2435 <> module: ( selection === "" ) ? undefined : selection,
2436 <> filter: ( filter === "" ) ? undefined : filter,
2437  
2438 <> // Remove testId filter
2439 <> testId: undefined
2440 <> });
2441 <>}
2442  
2443 <>function toolbarUrlConfigContainer() {
2444 <> var urlConfigContainer = document.createElement( "span" );
2445  
2446 <> urlConfigContainer.innerHTML = getUrlConfigHtml();
2447 <> addClass( urlConfigContainer, "qunit-url-config" );
2448  
2449 <> // For oldIE support:
2450 <> // * Add handlers to the individual elements instead of the container
2451 <> // * Use "click" instead of "change" for checkboxes
2452 <> addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged );
2453 <> addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged );
2454  
2455 <> return urlConfigContainer;
2456 <>}
2457  
2458 <>function toolbarLooseFilter() {
2459 <> var filter = document.createElement( "form" ),
2460 <> label = document.createElement( "label" ),
2461 <> input = document.createElement( "input" ),
2462 <> button = document.createElement( "button" );
2463  
2464 <> addClass( filter, "qunit-filter" );
2465  
2466 <> label.innerHTML = "Filter: ";
2467  
2468 <> input.type = "text";
2469 <> input.value = config.filter || "";
2470 <> input.name = "filter";
2471 <> input.id = "qunit-filter-input";
2472  
2473 <> button.innerHTML = "Go";
2474  
2475 <> label.appendChild( input );
2476  
2477 <> filter.appendChild( label );
2478 <> filter.appendChild( button );
2479 <> addEvent( filter, "submit", function( ev ) {
2480 <> applyUrlParams();
2481  
2482 <> if ( ev && ev.preventDefault ) {
2483 <> ev.preventDefault();
2484 <> }
2485  
2486 <> return false;
2487 <> });
2488  
2489 <> return filter;
2490 <>}
2491  
2492 <>function toolbarModuleFilterHtml() {
2493 <> var i,
2494 <> moduleFilterHtml = "";
2495  
2496 <> if ( !modulesList.length ) {
2497 <> return false;
2498 <> }
2499  
2500 <> modulesList.sort(function( a, b ) {
2501 <> return a.localeCompare( b );
2502 <> });
2503  
2504 <> moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" +
2505 <> "<select id='qunit-modulefilter' name='modulefilter'><option value='' " +
2506 <> ( QUnit.urlParams.module === undefined ? "selected='selected'" : "" ) +
2507 <> ">< All Modules ></option>";
2508  
2509 <> for ( i = 0; i < modulesList.length; i++ ) {
2510 <> moduleFilterHtml += "<option value='" +
2511 <> escapeText( encodeURIComponent( modulesList[ i ] ) ) + "' " +
2512 <> ( QUnit.urlParams.module === modulesList[ i ] ? "selected='selected'" : "" ) +
2513 <> ">" + escapeText( modulesList[ i ] ) + "</option>";
2514 <> }
2515 <> moduleFilterHtml += "</select>";
2516  
2517 <> return moduleFilterHtml;
2518 <>}
2519  
2520 <>function toolbarModuleFilter() {
2521 <> var toolbar = id( "qunit-testrunner-toolbar" ),
2522 <> moduleFilter = document.createElement( "span" ),
2523 <> moduleFilterHtml = toolbarModuleFilterHtml();
2524  
2525 <> if ( !toolbar || !moduleFilterHtml ) {
2526 <> return false;
2527 <> }
2528  
2529 <> moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
2530 <> moduleFilter.innerHTML = moduleFilterHtml;
2531  
2532 <> addEvent( moduleFilter.lastChild, "change", applyUrlParams );
2533  
2534 <> toolbar.appendChild( moduleFilter );
2535 <>}
2536  
2537 <>function appendToolbar() {
2538 <> var toolbar = id( "qunit-testrunner-toolbar" );
2539  
2540 <> if ( toolbar ) {
2541 <> toolbar.appendChild( toolbarUrlConfigContainer() );
2542 <> toolbar.appendChild( toolbarLooseFilter() );
2543 <> }
2544 <>}
2545  
2546 <>function appendHeader() {
2547 <> var header = id( "qunit-header" );
2548  
2549 <> if ( header ) {
2550 <> header.innerHTML = "<a href='" +
2551 <> setUrl({ filter: undefined, module: undefined, testId: undefined }) +
2552 <> "'>" + header.innerHTML + "</a> ";
2553 <> }
2554 <>}
2555  
2556 <>function appendBanner() {
2557 <> var banner = id( "qunit-banner" );
2558  
2559 <> if ( banner ) {
2560 <> banner.className = "";
2561 <> }
2562 <>}
2563  
2564 <>function appendTestResults() {
2565 <> var tests = id( "qunit-tests" ),
2566 <> result = id( "qunit-testresult" );
2567  
2568 <> if ( result ) {
2569 <> result.parentNode.removeChild( result );
2570 <> }
2571  
2572 <> if ( tests ) {
2573 <> tests.innerHTML = "";
2574 <> result = document.createElement( "p" );
2575 <> result.id = "qunit-testresult";
2576 <> result.className = "result";
2577 <> tests.parentNode.insertBefore( result, tests );
2578 <> result.innerHTML = "Running...<br />&#160;";
2579 <> }
2580 <>}
2581  
2582 <>function storeFixture() {
2583 <> var fixture = id( "qunit-fixture" );
2584 <> if ( fixture ) {
2585 <> config.fixture = fixture.innerHTML;
2586 <> }
2587 <>}
2588  
2589 <>function appendUserAgent() {
2590 <> var userAgent = id( "qunit-userAgent" );
2591 <> if ( userAgent ) {
2592 <> userAgent.innerHTML = "";
2593 <> userAgent.appendChild( document.createTextNode( navigator.userAgent ) );
2594 <> }
2595 <>}
2596  
2597 <>function appendTestsList( modules ) {
2598 <> var i, l, x, z, test, moduleObj;
2599  
2600 <> for ( i = 0, l = modules.length; i < l; i++ ) {
2601 <> moduleObj = modules[ i ];
2602  
2603 <> if ( moduleObj.name ) {
2604 <> modulesList.push( moduleObj.name );
2605 <> }
2606  
2607 <> for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) {
2608 <> test = moduleObj.tests[ x ];
2609  
2610 <> appendTest( test.name, test.testId, moduleObj.name );
2611 <> }
2612 <> }
2613 <>}
2614  
2615 <>function appendTest( name, testId, moduleName ) {
2616 <> var title, rerunTrigger, testBlock, assertList,
2617 <> tests = id( "qunit-tests" );
2618  
2619 <> if ( !tests ) {
2620 <> return;
2621 <> }
2622  
2623 <> title = document.createElement( "strong" );
2624 <> title.innerHTML = getNameHtml( name, moduleName );
2625  
2626 <> rerunTrigger = document.createElement( "a" );
2627 <> rerunTrigger.innerHTML = "Rerun";
2628 <> rerunTrigger.href = setUrl({ testId: testId });
2629  
2630 <> testBlock = document.createElement( "li" );
2631 <> testBlock.appendChild( title );
2632 <> testBlock.appendChild( rerunTrigger );
2633 <> testBlock.id = "qunit-test-output-" + testId;
2634  
2635 <> assertList = document.createElement( "ol" );
2636 <> assertList.className = "qunit-assert-list";
2637  
2638 <> testBlock.appendChild( assertList );
2639  
2640 <> tests.appendChild( testBlock );
2641 <>}
2642  
2643 <>// HTML Reporter initialization and load
2644 <>QUnit.begin(function( details ) {
2645 <> var qunit = id( "qunit" );
2646  
2647 <> // Fixture is the only one necessary to run without the #qunit element
2648 <> storeFixture();
2649  
2650 <> if ( qunit ) {
2651 <> qunit.innerHTML =
2652 <> "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
2653 <> "<h2 id='qunit-banner'></h2>" +
2654 <> "<div id='qunit-testrunner-toolbar'></div>" +
2655 <> "<h2 id='qunit-userAgent'></h2>" +
2656 <> "<ol id='qunit-tests'></ol>";
2657 <> }
2658  
2659 <> appendHeader();
2660 <> appendBanner();
2661 <> appendTestResults();
2662 <> appendUserAgent();
2663 <> appendToolbar();
2664 <> appendTestsList( details.modules );
2665 <> toolbarModuleFilter();
2666  
2667 <> if ( qunit && config.hidepassed ) {
2668 <> addClass( qunit.lastChild, "hidepass" );
2669 <> }
2670 <>});
2671  
2672 <>QUnit.done(function( details ) {
2673 <> var i, key,
2674 <> banner = id( "qunit-banner" ),
2675 <> tests = id( "qunit-tests" ),
2676 <> html = [
2677 <> "Tests completed in ",
2678 <> details.runtime,
2679 <> " milliseconds.<br />",
2680 <> "<span class='passed'>",
2681 <> details.passed,
2682 <> "</span> assertions of <span class='total'>",
2683 <> details.total,
2684 <> "</span> passed, <span class='failed'>",
2685 <> details.failed,
2686 <> "</span> failed."
2687 <> ].join( "" );
2688  
2689 <> if ( banner ) {
2690 <> banner.className = details.failed ? "qunit-fail" : "qunit-pass";
2691 <> }
2692  
2693 <> if ( tests ) {
2694 <> id( "qunit-testresult" ).innerHTML = html;
2695 <> }
2696  
2697 <> if ( config.altertitle && defined.document && document.title ) {
2698  
2699 <> // show ✖ for good, ✔ for bad suite result in title
2700 <> // use escape sequences in case file gets loaded with non-utf-8-charset
2701 <> document.title = [
2702 <> ( details.failed ? "\u2716" : "\u2714" ),
2703 <> document.title.replace( /^[\u2714\u2716] /i, "" )
2704 <> ].join( " " );
2705 <> }
2706  
2707 <> // clear own sessionStorage items if all tests passed
2708 <> if ( config.reorder && defined.sessionStorage && details.failed === 0 ) {
2709 <> for ( i = 0; i < sessionStorage.length; i++ ) {
2710 <> key = sessionStorage.key( i++ );
2711 <> if ( key.indexOf( "qunit-test-" ) === 0 ) {
2712 <> sessionStorage.removeItem( key );
2713 <> }
2714 <> }
2715 <> }
2716  
2717 <> // scroll back to top to show results
2718 <> if ( config.scrolltop && window.scrollTo ) {
2719 <> window.scrollTo( 0, 0 );
2720 <> }
2721 <>});
2722  
2723 <>function getNameHtml( name, module ) {
2724 <> var nameHtml = "";
2725  
2726 <> if ( module ) {
2727 <> nameHtml = "<span class='module-name'>" + escapeText( module ) + "</span>: ";
2728 <> }
2729  
2730 <> nameHtml += "<span class='test-name'>" + escapeText( name ) + "</span>";
2731  
2732 <> return nameHtml;
2733 <>}
2734  
2735 <>QUnit.testStart(function( details ) {
2736 <> var running, testBlock;
2737  
2738 <> testBlock = id( "qunit-test-output-" + details.testId );
2739 <> if ( testBlock ) {
2740 <> testBlock.className = "running";
2741 <> } else {
2742  
2743 <> // Report later registered tests
2744 <> appendTest( details.name, details.testId, details.module );
2745 <> }
2746  
2747 <> running = id( "qunit-testresult" );
2748 <> if ( running ) {
2749 <> running.innerHTML = "Running: <br />" + getNameHtml( details.name, details.module );
2750 <> }
2751  
2752 <>});
2753  
2754 <>QUnit.log(function( details ) {
2755 <> var assertList, assertLi,
2756 <> message, expected, actual,
2757 <> testItem = id( "qunit-test-output-" + details.testId );
2758  
2759 <> if ( !testItem ) {
2760 <> return;
2761 <> }
2762  
2763 <> message = escapeText( details.message ) || ( details.result ? "okay" : "failed" );
2764 <> message = "<span class='test-message'>" + message + "</span>";
2765 <> message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
2766  
2767 <> // pushFailure doesn't provide details.expected
2768 <> // when it calls, it's implicit to also not show expected and diff stuff
2769 <> // Also, we need to check details.expected existence, as it can exist and be undefined
2770 <> if ( !details.result && hasOwn.call( details, "expected" ) ) {
2771 <> expected = escapeText( QUnit.dump.parse( details.expected ) );
2772 <> actual = escapeText( QUnit.dump.parse( details.actual ) );
2773 <> message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" +
2774 <> expected +
2775 <> "</pre></td></tr>";
2776  
2777 <> if ( actual !== expected ) {
2778 <> message += "<tr class='test-actual'><th>Result: </th><td><pre>" +
2779 <> actual + "</pre></td></tr>" +
2780 <> "<tr class='test-diff'><th>Diff: </th><td><pre>" +
2781 <> QUnit.diff( expected, actual ) + "</pre></td></tr>";
2782 <> }
2783  
2784 <> if ( details.source ) {
2785 <> message += "<tr class='test-source'><th>Source: </th><td><pre>" +
2786 <> escapeText( details.source ) + "</pre></td></tr>";
2787 <> }
2788  
2789 <> message += "</table>";
2790  
2791 <> // this occours when pushFailure is set and we have an extracted stack trace
2792 <> } else if ( !details.result && details.source ) {
2793 <> message += "<table>" +
2794 <> "<tr class='test-source'><th>Source: </th><td><pre>" +
2795 <> escapeText( details.source ) + "</pre></td></tr>" +
2796 <> "</table>";
2797 <> }
2798  
2799 <> assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
2800  
2801 <> assertLi = document.createElement( "li" );
2802 <> assertLi.className = details.result ? "pass" : "fail";
2803 <> assertLi.innerHTML = message;
2804 <> assertList.appendChild( assertLi );
2805 <>});
2806  
2807 <>QUnit.testDone(function( details ) {
2808 <> var testTitle, time, testItem, assertList,
2809 <> good, bad, testCounts, skipped,
2810 <> tests = id( "qunit-tests" );
2811  
2812 <> if ( !tests ) {
2813 <> return;
2814 <> }
2815  
2816 <> testItem = id( "qunit-test-output-" + details.testId );
2817  
2818 <> assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
2819  
2820 <> good = details.passed;
2821 <> bad = details.failed;
2822  
2823 <> // store result when possible
2824 <> if ( config.reorder && defined.sessionStorage ) {
2825 <> if ( bad ) {
2826 <> sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad );
2827 <> } else {
2828 <> sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name );
2829 <> }
2830 <> }
2831  
2832 <> if ( bad === 0 ) {
2833 <> addClass( assertList, "qunit-collapsed" );
2834 <> }
2835  
2836 <> // testItem.firstChild is the test name
2837 <> testTitle = testItem.firstChild;
2838  
2839 <> testCounts = bad ?
2840 <> "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " :
2841 <> "";
2842  
2843 <> testTitle.innerHTML += " <b class='counts'>(" + testCounts +
2844 <> details.assertions.length + ")</b>";
2845  
2846 <> if ( details.skipped ) {
2847 <> testItem.className = "skipped";
2848 <> skipped = document.createElement( "em" );
2849 <> skipped.className = "qunit-skipped-label";
2850 <> skipped.innerHTML = "skipped";
2851 <> testItem.insertBefore( skipped, testTitle );
2852 <> } else {
2853 <> addEvent( testTitle, "click", function() {
2854 <> toggleClass( assertList, "qunit-collapsed" );
2855 <> });
2856  
2857 <> testItem.className = bad ? "fail" : "pass";
2858  
2859 <> time = document.createElement( "span" );
2860 <> time.className = "runtime";
2861 <> time.innerHTML = details.runtime + " ms";
2862 <> testItem.insertBefore( time, assertList );
2863 <> }
2864 <>});
2865  
2866 <>if ( !defined.document || document.readyState === "complete" ) {
2867 <> config.pageLoaded = true;
2868 <> config.autorun = true;
2869 <>}
2870  
2871 <>if ( defined.document ) {
2872 <> addEvent( window, "load", QUnit.load );
2873 <>}
2874  
2875 <>})();