/[projects]/misc/horsensspejder-web/jquery/jquery-ui-1.10.3/external/qunit.js
ViewVC logotype

Annotation of /misc/horsensspejder-web/jquery/jquery-ui-1.10.3/external/qunit.js

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20