~caneypuggies/reformedchurcheslocator/couchapp-backbone

« back to all changes in this revision

Viewing changes to _attachments/js/vendor/bootstrap/js/tests/vendor/qunit.js

  • Committer: Tim Black
  • Date: 2013-09-16 22:50:16 UTC
  • Revision ID: tim@alwaysreformed.com-20130916225016-zk8jiba25z33ew7h
Versioned Bower vendor directory

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * QUnit - A JavaScript Unit Testing Framework
 
3
 *
 
4
 * http://docs.jquery.com/QUnit
 
5
 *
 
6
 * Copyright (c) 2012 John Resig, Jörn Zaefferer
 
7
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 
8
 * or GPL (GPL-LICENSE.txt) licenses.
 
9
 */
 
10
 
 
11
(function(window) {
 
12
 
 
13
var defined = {
 
14
        setTimeout: typeof window.setTimeout !== "undefined",
 
15
        sessionStorage: (function() {
 
16
                try {
 
17
                        return !!sessionStorage.getItem;
 
18
                } catch(e) {
 
19
                        return false;
 
20
                }
 
21
        })()
 
22
};
 
23
 
 
24
var testId = 0;
 
25
 
 
26
var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {
 
27
        this.name = name;
 
28
        this.testName = testName;
 
29
        this.expected = expected;
 
30
        this.testEnvironmentArg = testEnvironmentArg;
 
31
        this.async = async;
 
32
        this.callback = callback;
 
33
        this.assertions = [];
 
34
};
 
35
Test.prototype = {
 
36
        init: function() {
 
37
                var tests = id("qunit-tests");
 
38
                if (tests) {
 
39
                        var b = document.createElement("strong");
 
40
                                b.innerHTML = "Running " + this.name;
 
41
                        var li = document.createElement("li");
 
42
                                li.appendChild( b );
 
43
                                li.className = "running";
 
44
                                li.id = this.id = "test-output" + testId++;
 
45
                        tests.appendChild( li );
 
46
                }
 
47
        },
 
48
        setup: function() {
 
49
                if (this.module != config.previousModule) {
 
50
                        if ( config.previousModule ) {
 
51
                                QUnit.moduleDone( {
 
52
                                        name: config.previousModule,
 
53
                                        failed: config.moduleStats.bad,
 
54
                                        passed: config.moduleStats.all - config.moduleStats.bad,
 
55
                                        total: config.moduleStats.all
 
56
                                } );
 
57
                        }
 
58
                        config.previousModule = this.module;
 
59
                        config.moduleStats = { all: 0, bad: 0 };
 
60
                        QUnit.moduleStart( {
 
61
                                name: this.module
 
62
                        } );
 
63
                }
 
64
 
 
65
                config.current = this;
 
66
                this.testEnvironment = extend({
 
67
                        setup: function() {},
 
68
                        teardown: function() {}
 
69
                }, this.moduleTestEnvironment);
 
70
                if (this.testEnvironmentArg) {
 
71
                        extend(this.testEnvironment, this.testEnvironmentArg);
 
72
                }
 
73
 
 
74
                QUnit.testStart( {
 
75
                        name: this.testName
 
76
                } );
 
77
 
 
78
                // allow utility functions to access the current test environment
 
79
                // TODO why??
 
80
                QUnit.current_testEnvironment = this.testEnvironment;
 
81
 
 
82
                try {
 
83
                        if ( !config.pollution ) {
 
84
                                saveGlobal();
 
85
                        }
 
86
 
 
87
                        this.testEnvironment.setup.call(this.testEnvironment);
 
88
                } catch(e) {
 
89
                        QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message );
 
90
                }
 
91
        },
 
92
        run: function() {
 
93
                if ( this.async ) {
 
94
                        QUnit.stop();
 
95
                }
 
96
 
 
97
                if ( config.notrycatch ) {
 
98
                        this.callback.call(this.testEnvironment);
 
99
                        return;
 
100
                }
 
101
                try {
 
102
                        this.callback.call(this.testEnvironment);
 
103
                } catch(e) {
 
104
                        fail("Test " + this.testName + " died, exception and test follows", e, this.callback);
 
105
                        QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) );
 
106
                        // else next test will carry the responsibility
 
107
                        saveGlobal();
 
108
 
 
109
                        // Restart the tests if they're blocking
 
110
                        if ( config.blocking ) {
 
111
                                start();
 
112
                        }
 
113
                }
 
114
        },
 
115
        teardown: function() {
 
116
                try {
 
117
                        this.testEnvironment.teardown.call(this.testEnvironment);
 
118
                        checkPollution();
 
119
                } catch(e) {
 
120
                        QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message );
 
121
                }
 
122
        },
 
123
        finish: function() {
 
124
                if ( this.expected && this.expected != this.assertions.length ) {
 
125
                        QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );
 
126
                }
 
127
 
 
128
                var good = 0, bad = 0,
 
129
                        tests = id("qunit-tests");
 
130
 
 
131
                config.stats.all += this.assertions.length;
 
132
                config.moduleStats.all += this.assertions.length;
 
133
 
 
134
                if ( tests ) {
 
135
                        var ol = document.createElement("ol");
 
136
 
 
137
                        for ( var i = 0; i < this.assertions.length; i++ ) {
 
138
                                var assertion = this.assertions[i];
 
139
 
 
140
                                var li = document.createElement("li");
 
141
                                li.className = assertion.result ? "pass" : "fail";
 
142
                                li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed");
 
143
                                ol.appendChild( li );
 
144
 
 
145
                                if ( assertion.result ) {
 
146
                                        good++;
 
147
                                } else {
 
148
                                        bad++;
 
149
                                        config.stats.bad++;
 
150
                                        config.moduleStats.bad++;
 
151
                                }
 
152
                        }
 
153
 
 
154
                        // store result when possible
 
155
                        if ( QUnit.config.reorder && defined.sessionStorage ) {
 
156
                                if (bad) {
 
157
                                        sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad);
 
158
                                } else {
 
159
                                        sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName);
 
160
                                }
 
161
                        }
 
162
 
 
163
                        if (bad == 0) {
 
164
                                ol.style.display = "none";
 
165
                        }
 
166
 
 
167
                        var b = document.createElement("strong");
 
168
                        b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
 
169
 
 
170
                        var a = document.createElement("a");
 
171
                        a.innerHTML = "Rerun";
 
172
                        a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
 
173
 
 
174
                        addEvent(b, "click", function() {
 
175
                                var next = b.nextSibling.nextSibling,
 
176
                                        display = next.style.display;
 
177
                                next.style.display = display === "none" ? "block" : "none";
 
178
                        });
 
179
 
 
180
                        addEvent(b, "dblclick", function(e) {
 
181
                                var target = e && e.target ? e.target : window.event.srcElement;
 
182
                                if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
 
183
                                        target = target.parentNode;
 
184
                                }
 
185
                                if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
 
186
                                        window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
 
187
                                }
 
188
                        });
 
189
 
 
190
                        var li = id(this.id);
 
191
                        li.className = bad ? "fail" : "pass";
 
192
                        li.removeChild( li.firstChild );
 
193
                        li.appendChild( b );
 
194
                        li.appendChild( a );
 
195
                        li.appendChild( ol );
 
196
 
 
197
                } else {
 
198
                        for ( var i = 0; i < this.assertions.length; i++ ) {
 
199
                                if ( !this.assertions[i].result ) {
 
200
                                        bad++;
 
201
                                        config.stats.bad++;
 
202
                                        config.moduleStats.bad++;
 
203
                                }
 
204
                        }
 
205
                }
 
206
 
 
207
                try {
 
208
                        QUnit.reset();
 
209
                } catch(e) {
 
210
                        fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset);
 
211
                }
 
212
 
 
213
                QUnit.testDone( {
 
214
                        name: this.testName,
 
215
                        failed: bad,
 
216
                        passed: this.assertions.length - bad,
 
217
                        total: this.assertions.length
 
218
                } );
 
219
        },
 
220
 
 
221
        queue: function() {
 
222
                var test = this;
 
223
                synchronize(function() {
 
224
                        test.init();
 
225
                });
 
226
                function run() {
 
227
                        // each of these can by async
 
228
                        synchronize(function() {
 
229
                                test.setup();
 
230
                        });
 
231
                        synchronize(function() {
 
232
                                test.run();
 
233
                        });
 
234
                        synchronize(function() {
 
235
                                test.teardown();
 
236
                        });
 
237
                        synchronize(function() {
 
238
                                test.finish();
 
239
                        });
 
240
                }
 
241
                // defer when previous test run passed, if storage is available
 
242
                var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName);
 
243
                if (bad) {
 
244
                        run();
 
245
                } else {
 
246
                        synchronize(run);
 
247
                };
 
248
        }
 
249
 
 
250
};
 
251
 
 
252
var QUnit = {
 
253
 
 
254
        // call on start of module test to prepend name to all tests
 
255
        module: function(name, testEnvironment) {
 
256
                config.currentModule = name;
 
257
                config.currentModuleTestEnviroment = testEnvironment;
 
258
        },
 
259
 
 
260
        asyncTest: function(testName, expected, callback) {
 
261
                if ( arguments.length === 2 ) {
 
262
                        callback = expected;
 
263
                        expected = 0;
 
264
                }
 
265
 
 
266
                QUnit.test(testName, expected, callback, true);
 
267
        },
 
268
 
 
269
        test: function(testName, expected, callback, async) {
 
270
                var name = '<span class="test-name">' + testName + '</span>', testEnvironmentArg;
 
271
 
 
272
                if ( arguments.length === 2 ) {
 
273
                        callback = expected;
 
274
                        expected = null;
 
275
                }
 
276
                // is 2nd argument a testEnvironment?
 
277
                if ( expected && typeof expected === 'object') {
 
278
                        testEnvironmentArg = expected;
 
279
                        expected = null;
 
280
                }
 
281
 
 
282
                if ( config.currentModule ) {
 
283
                        name = '<span class="module-name">' + config.currentModule + "</span>: " + name;
 
284
                }
 
285
 
 
286
                if ( !validTest(config.currentModule + ": " + testName) ) {
 
287
                        return;
 
288
                }
 
289
 
 
290
                var test = new Test(name, testName, expected, testEnvironmentArg, async, callback);
 
291
                test.module = config.currentModule;
 
292
                test.moduleTestEnvironment = config.currentModuleTestEnviroment;
 
293
                test.queue();
 
294
        },
 
295
 
 
296
        /**
 
297
         * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
 
298
         */
 
299
        expect: function(asserts) {
 
300
                config.current.expected = asserts;
 
301
        },
 
302
 
 
303
        /**
 
304
         * Asserts true.
 
305
         * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
 
306
         */
 
307
        ok: function(a, msg) {
 
308
                a = !!a;
 
309
                var details = {
 
310
                        result: a,
 
311
                        message: msg
 
312
                };
 
313
                msg = escapeHtml(msg);
 
314
                QUnit.log(details);
 
315
                config.current.assertions.push({
 
316
                        result: a,
 
317
                        message: msg
 
318
                });
 
319
        },
 
320
 
 
321
        /**
 
322
         * Checks that the first two arguments are equal, with an optional message.
 
323
         * Prints out both actual and expected values.
 
324
         *
 
325
         * Prefered to ok( actual == expected, message )
 
326
         *
 
327
         * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
 
328
         *
 
329
         * @param Object actual
 
330
         * @param Object expected
 
331
         * @param String message (optional)
 
332
         */
 
333
        equal: function(actual, expected, message) {
 
334
                QUnit.push(expected == actual, actual, expected, message);
 
335
        },
 
336
 
 
337
        notEqual: function(actual, expected, message) {
 
338
                QUnit.push(expected != actual, actual, expected, message);
 
339
        },
 
340
 
 
341
        deepEqual: function(actual, expected, message) {
 
342
                QUnit.push(QUnit.equiv(actual, expected), actual, expected, message);
 
343
        },
 
344
 
 
345
        notDeepEqual: function(actual, expected, message) {
 
346
                QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message);
 
347
        },
 
348
 
 
349
        strictEqual: function(actual, expected, message) {
 
350
                QUnit.push(expected === actual, actual, expected, message);
 
351
        },
 
352
 
 
353
        notStrictEqual: function(actual, expected, message) {
 
354
                QUnit.push(expected !== actual, actual, expected, message);
 
355
        },
 
356
 
 
357
        raises: function(block, expected, message) {
 
358
                var actual, ok = false;
 
359
 
 
360
                if (typeof expected === 'string') {
 
361
                        message = expected;
 
362
                        expected = null;
 
363
                }
 
364
 
 
365
                try {
 
366
                        block();
 
367
                } catch (e) {
 
368
                        actual = e;
 
369
                }
 
370
 
 
371
                if (actual) {
 
372
                        // we don't want to validate thrown error
 
373
                        if (!expected) {
 
374
                                ok = true;
 
375
                        // expected is a regexp
 
376
                        } else if (QUnit.objectType(expected) === "regexp") {
 
377
                                ok = expected.test(actual);
 
378
                        // expected is a constructor
 
379
                        } else if (actual instanceof expected) {
 
380
                                ok = true;
 
381
                        // expected is a validation function which returns true is validation passed
 
382
                        } else if (expected.call({}, actual) === true) {
 
383
                                ok = true;
 
384
                        }
 
385
                }
 
386
 
 
387
                QUnit.ok(ok, message);
 
388
        },
 
389
 
 
390
        start: function() {
 
391
                config.semaphore--;
 
392
                if (config.semaphore > 0) {
 
393
                        // don't start until equal number of stop-calls
 
394
                        return;
 
395
                }
 
396
                if (config.semaphore < 0) {
 
397
                        // ignore if start is called more often then stop
 
398
                        config.semaphore = 0;
 
399
                }
 
400
                // A slight delay, to avoid any current callbacks
 
401
                if ( defined.setTimeout ) {
 
402
                        window.setTimeout(function() {
 
403
                                if (config.semaphore > 0) {
 
404
                                        return;
 
405
                                }
 
406
                                if ( config.timeout ) {
 
407
                                        clearTimeout(config.timeout);
 
408
                                }
 
409
 
 
410
                                config.blocking = false;
 
411
                                process();
 
412
                        }, 13);
 
413
                } else {
 
414
                        config.blocking = false;
 
415
                        process();
 
416
                }
 
417
        },
 
418
 
 
419
        stop: function(timeout) {
 
420
                config.semaphore++;
 
421
                config.blocking = true;
 
422
 
 
423
                if ( timeout && defined.setTimeout ) {
 
424
                        clearTimeout(config.timeout);
 
425
                        config.timeout = window.setTimeout(function() {
 
426
                                QUnit.ok( false, "Test timed out" );
 
427
                                QUnit.start();
 
428
                        }, timeout);
 
429
                }
 
430
        }
 
431
};
 
432
 
 
433
// Backwards compatibility, deprecated
 
434
QUnit.equals = QUnit.equal;
 
435
QUnit.same = QUnit.deepEqual;
 
436
 
 
437
// Maintain internal state
 
438
var config = {
 
439
        // The queue of tests to run
 
440
        queue: [],
 
441
 
 
442
        // block until document ready
 
443
        blocking: true,
 
444
 
 
445
        // when enabled, show only failing tests
 
446
        // gets persisted through sessionStorage and can be changed in UI via checkbox
 
447
        hidepassed: false,
 
448
 
 
449
        // by default, run previously failed tests first
 
450
        // very useful in combination with "Hide passed tests" checked
 
451
        reorder: true,
 
452
 
 
453
        // by default, modify document.title when suite is done
 
454
        altertitle: true,
 
455
 
 
456
        urlConfig: ['noglobals', 'notrycatch']
 
457
};
 
458
 
 
459
// Load paramaters
 
460
(function() {
 
461
        var location = window.location || { search: "", protocol: "file:" },
 
462
                params = location.search.slice( 1 ).split( "&" ),
 
463
                length = params.length,
 
464
                urlParams = {},
 
465
                current;
 
466
 
 
467
        if ( params[ 0 ] ) {
 
468
                for ( var i = 0; i < length; i++ ) {
 
469
                        current = params[ i ].split( "=" );
 
470
                        current[ 0 ] = decodeURIComponent( current[ 0 ] );
 
471
                        // allow just a key to turn on a flag, e.g., test.html?noglobals
 
472
                        current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
 
473
                        urlParams[ current[ 0 ] ] = current[ 1 ];
 
474
                }
 
475
        }
 
476
 
 
477
        QUnit.urlParams = urlParams;
 
478
        config.filter = urlParams.filter;
 
479
 
 
480
        // Figure out if we're running the tests from a server or not
 
481
        QUnit.isLocal = !!(location.protocol === 'file:');
 
482
})();
 
483
 
 
484
// Expose the API as global variables, unless an 'exports'
 
485
// object exists, in that case we assume we're in CommonJS
 
486
if ( typeof exports === "undefined" || typeof require === "undefined" ) {
 
487
        extend(window, QUnit);
 
488
        window.QUnit = QUnit;
 
489
} else {
 
490
        extend(exports, QUnit);
 
491
        exports.QUnit = QUnit;
 
492
}
 
493
 
 
494
// define these after exposing globals to keep them in these QUnit namespace only
 
495
extend(QUnit, {
 
496
        config: config,
 
497
 
 
498
        // Initialize the configuration options
 
499
        init: function() {
 
500
                extend(config, {
 
501
                        stats: { all: 0, bad: 0 },
 
502
                        moduleStats: { all: 0, bad: 0 },
 
503
                        started: +new Date,
 
504
                        updateRate: 1000,
 
505
                        blocking: false,
 
506
                        autostart: true,
 
507
                        autorun: false,
 
508
                        filter: "",
 
509
                        queue: [],
 
510
                        semaphore: 0
 
511
                });
 
512
 
 
513
                var tests = id( "qunit-tests" ),
 
514
                        banner = id( "qunit-banner" ),
 
515
                        result = id( "qunit-testresult" );
 
516
 
 
517
                if ( tests ) {
 
518
                        tests.innerHTML = "";
 
519
                }
 
520
 
 
521
                if ( banner ) {
 
522
                        banner.className = "";
 
523
                }
 
524
 
 
525
                if ( result ) {
 
526
                        result.parentNode.removeChild( result );
 
527
                }
 
528
 
 
529
                if ( tests ) {
 
530
                        result = document.createElement( "p" );
 
531
                        result.id = "qunit-testresult";
 
532
                        result.className = "result";
 
533
                        tests.parentNode.insertBefore( result, tests );
 
534
                        result.innerHTML = 'Running...<br/>&nbsp;';
 
535
                }
 
536
        },
 
537
 
 
538
        /**
 
539
         * Resets the test setup. Useful for tests that modify the DOM.
 
540
         *
 
541
         * If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
 
542
         */
 
543
        reset: function() {
 
544
                if ( window.jQuery ) {
 
545
                        jQuery( "#qunit-fixture" ).html( config.fixture );
 
546
                } else {
 
547
                        var main = id( 'qunit-fixture' );
 
548
                        if ( main ) {
 
549
                                main.innerHTML = config.fixture;
 
550
                        }
 
551
                }
 
552
        },
 
553
 
 
554
        /**
 
555
         * Trigger an event on an element.
 
556
         *
 
557
         * @example triggerEvent( document.body, "click" );
 
558
         *
 
559
         * @param DOMElement elem
 
560
         * @param String type
 
561
         */
 
562
        triggerEvent: function( elem, type, event ) {
 
563
                if ( document.createEvent ) {
 
564
                        event = document.createEvent("MouseEvents");
 
565
                        event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
 
566
                                0, 0, 0, 0, 0, false, false, false, false, 0, null);
 
567
                        elem.dispatchEvent( event );
 
568
 
 
569
                } else if ( elem.fireEvent ) {
 
570
                        elem.fireEvent("on"+type);
 
571
                }
 
572
        },
 
573
 
 
574
        // Safe object type checking
 
575
        is: function( type, obj ) {
 
576
                return QUnit.objectType( obj ) == type;
 
577
        },
 
578
 
 
579
        objectType: function( obj ) {
 
580
                if (typeof obj === "undefined") {
 
581
                                return "undefined";
 
582
 
 
583
                // consider: typeof null === object
 
584
                }
 
585
                if (obj === null) {
 
586
                                return "null";
 
587
                }
 
588
 
 
589
                var type = Object.prototype.toString.call( obj )
 
590
                        .match(/^\[object\s(.*)\]$/)[1] || '';
 
591
 
 
592
                switch (type) {
 
593
                                case 'Number':
 
594
                                                if (isNaN(obj)) {
 
595
                                                                return "nan";
 
596
                                                } else {
 
597
                                                                return "number";
 
598
                                                }
 
599
                                case 'String':
 
600
                                case 'Boolean':
 
601
                                case 'Array':
 
602
                                case 'Date':
 
603
                                case 'RegExp':
 
604
                                case 'Function':
 
605
                                                return type.toLowerCase();
 
606
                }
 
607
                if (typeof obj === "object") {
 
608
                                return "object";
 
609
                }
 
610
                return undefined;
 
611
        },
 
612
 
 
613
        push: function(result, actual, expected, message) {
 
614
                var details = {
 
615
                        result: result,
 
616
                        message: message,
 
617
                        actual: actual,
 
618
                        expected: expected
 
619
                };
 
620
 
 
621
                message = escapeHtml(message) || (result ? "okay" : "failed");
 
622
                message = '<span class="test-message">' + message + "</span>";
 
623
                expected = escapeHtml(QUnit.jsDump.parse(expected));
 
624
                actual = escapeHtml(QUnit.jsDump.parse(actual));
 
625
                var output = message + '<table><tr class="test-expected"><th>Expected: </th><td><pre>' + expected + '</pre></td></tr>';
 
626
                if (actual != expected) {
 
627
                        output += '<tr class="test-actual"><th>Result: </th><td><pre>' + actual + '</pre></td></tr>';
 
628
                        output += '<tr class="test-diff"><th>Diff: </th><td><pre>' + QUnit.diff(expected, actual) +'</pre></td></tr>';
 
629
                }
 
630
                if (!result) {
 
631
                        var source = sourceFromStacktrace();
 
632
                        if (source) {
 
633
                                details.source = source;
 
634
                                output += '<tr class="test-source"><th>Source: </th><td><pre>' + escapeHtml(source) + '</pre></td></tr>';
 
635
                        }
 
636
                }
 
637
                output += "</table>";
 
638
 
 
639
                QUnit.log(details);
 
640
 
 
641
                config.current.assertions.push({
 
642
                        result: !!result,
 
643
                        message: output
 
644
                });
 
645
        },
 
646
 
 
647
        url: function( params ) {
 
648
                params = extend( extend( {}, QUnit.urlParams ), params );
 
649
                var querystring = "?",
 
650
                        key;
 
651
                for ( key in params ) {
 
652
                        querystring += encodeURIComponent( key ) + "=" +
 
653
                                encodeURIComponent( params[ key ] ) + "&";
 
654
                }
 
655
                return window.location.pathname + querystring.slice( 0, -1 );
 
656
        },
 
657
 
 
658
        extend: extend,
 
659
        id: id,
 
660
        addEvent: addEvent,
 
661
 
 
662
        // Logging callbacks; all receive a single argument with the listed properties
 
663
        // run test/logs.html for any related changes
 
664
        begin: function() {},
 
665
        // done: { failed, passed, total, runtime }
 
666
        done: function() {},
 
667
        // log: { result, actual, expected, message }
 
668
        log: function() {},
 
669
        // testStart: { name }
 
670
        testStart: function() {},
 
671
        // testDone: { name, failed, passed, total }
 
672
        testDone: function() {},
 
673
        // moduleStart: { name }
 
674
        moduleStart: function() {},
 
675
        // moduleDone: { name, failed, passed, total }
 
676
        moduleDone: function() {}
 
677
});
 
678
 
 
679
if ( typeof document === "undefined" || document.readyState === "complete" ) {
 
680
        config.autorun = true;
 
681
}
 
682
 
 
683
QUnit.load = function() {
 
684
        QUnit.begin({});
 
685
 
 
686
        // Initialize the config, saving the execution queue
 
687
        var oldconfig = extend({}, config);
 
688
        QUnit.init();
 
689
        extend(config, oldconfig);
 
690
 
 
691
        config.blocking = false;
 
692
 
 
693
        var urlConfigHtml = '', len = config.urlConfig.length;
 
694
        for ( var i = 0, val; i < len, val = config.urlConfig[i]; i++ ) {
 
695
                config[val] = QUnit.urlParams[val];
 
696
                urlConfigHtml += '<label><input name="' + val + '" type="checkbox"' + ( config[val] ? ' checked="checked"' : '' ) + '>' + val + '</label>';
 
697
        }
 
698
 
 
699
        var userAgent = id("qunit-userAgent");
 
700
        if ( userAgent ) {
 
701
                userAgent.innerHTML = navigator.userAgent;
 
702
        }
 
703
        var banner = id("qunit-header");
 
704
        if ( banner ) {
 
705
                banner.innerHTML = '<a href="' + QUnit.url({ filter: undefined }) + '"> ' + banner.innerHTML + '</a> ' + urlConfigHtml;
 
706
                addEvent( banner, "change", function( event ) {
 
707
                        var params = {};
 
708
                        params[ event.target.name ] = event.target.checked ? true : undefined;
 
709
                        window.location = QUnit.url( params );
 
710
                });
 
711
        }
 
712
 
 
713
        var toolbar = id("qunit-testrunner-toolbar");
 
714
        if ( toolbar ) {
 
715
                var filter = document.createElement("input");
 
716
                filter.type = "checkbox";
 
717
                filter.id = "qunit-filter-pass";
 
718
                addEvent( filter, "click", function() {
 
719
                        var ol = document.getElementById("qunit-tests");
 
720
                        if ( filter.checked ) {
 
721
                                ol.className = ol.className + " hidepass";
 
722
                        } else {
 
723
                                var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
 
724
                                ol.className = tmp.replace(/ hidepass /, " ");
 
725
                        }
 
726
                        if ( defined.sessionStorage ) {
 
727
                                if (filter.checked) {
 
728
                                        sessionStorage.setItem("qunit-filter-passed-tests", "true");
 
729
                                } else {
 
730
                                        sessionStorage.removeItem("qunit-filter-passed-tests");
 
731
                                }
 
732
                        }
 
733
                });
 
734
                if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) {
 
735
                        filter.checked = true;
 
736
                        var ol = document.getElementById("qunit-tests");
 
737
                        ol.className = ol.className + " hidepass";
 
738
                }
 
739
                toolbar.appendChild( filter );
 
740
 
 
741
                var label = document.createElement("label");
 
742
                label.setAttribute("for", "qunit-filter-pass");
 
743
                label.innerHTML = "Hide passed tests";
 
744
                toolbar.appendChild( label );
 
745
        }
 
746
 
 
747
        var main = id('qunit-fixture');
 
748
        if ( main ) {
 
749
                config.fixture = main.innerHTML;
 
750
        }
 
751
 
 
752
        if (config.autostart) {
 
753
                QUnit.start();
 
754
        }
 
755
};
 
756
 
 
757
addEvent(window, "load", QUnit.load);
 
758
 
 
759
function done() {
 
760
        config.autorun = true;
 
761
 
 
762
        // Log the last module results
 
763
        if ( config.currentModule ) {
 
764
                QUnit.moduleDone( {
 
765
                        name: config.currentModule,
 
766
                        failed: config.moduleStats.bad,
 
767
                        passed: config.moduleStats.all - config.moduleStats.bad,
 
768
                        total: config.moduleStats.all
 
769
                } );
 
770
        }
 
771
 
 
772
        var banner = id("qunit-banner"),
 
773
                tests = id("qunit-tests"),
 
774
                runtime = +new Date - config.started,
 
775
                passed = config.stats.all - config.stats.bad,
 
776
                html = [
 
777
                        'Tests completed in ',
 
778
                        runtime,
 
779
                        ' milliseconds.<br/>',
 
780
                        '<span class="passed">',
 
781
                        passed,
 
782
                        '</span> tests of <span class="total">',
 
783
                        config.stats.all,
 
784
                        '</span> passed, <span class="failed">',
 
785
                        config.stats.bad,
 
786
                        '</span> failed.'
 
787
                ].join('');
 
788
 
 
789
        if ( banner ) {
 
790
                banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
 
791
        }
 
792
 
 
793
        if ( tests ) {
 
794
                id( "qunit-testresult" ).innerHTML = html;
 
795
        }
 
796
 
 
797
        if ( config.altertitle && typeof document !== "undefined" && document.title ) {
 
798
                // show âś– for good, âś” for bad suite result in title
 
799
                // use escape sequences in case file gets loaded with non-utf-8-charset
 
800
                document.title = [
 
801
                        (config.stats.bad ? "\u2716" : "\u2714"),
 
802
                        document.title.replace(/^[\u2714\u2716] /i, "")
 
803
                ].join(" ");
 
804
        }
 
805
 
 
806
        QUnit.done( {
 
807
                failed: config.stats.bad,
 
808
                passed: passed,
 
809
                total: config.stats.all,
 
810
                runtime: runtime
 
811
        } );
 
812
}
 
813
 
 
814
function validTest( name ) {
 
815
        var filter = config.filter,
 
816
                run = false;
 
817
 
 
818
        if ( !filter ) {
 
819
                return true;
 
820
        }
 
821
 
 
822
        var not = filter.charAt( 0 ) === "!";
 
823
        if ( not ) {
 
824
                filter = filter.slice( 1 );
 
825
        }
 
826
 
 
827
        if ( name.indexOf( filter ) !== -1 ) {
 
828
                return !not;
 
829
        }
 
830
 
 
831
        if ( not ) {
 
832
                run = true;
 
833
        }
 
834
 
 
835
        return run;
 
836
}
 
837
 
 
838
// so far supports only Firefox, Chrome and Opera (buggy)
 
839
// could be extended in the future to use something like https://github.com/csnover/TraceKit
 
840
function sourceFromStacktrace() {
 
841
        try {
 
842
                throw new Error();
 
843
        } catch ( e ) {
 
844
                if (e.stacktrace) {
 
845
                        // Opera
 
846
                        return e.stacktrace.split("\n")[6];
 
847
                } else if (e.stack) {
 
848
                        // Firefox, Chrome
 
849
                        return e.stack.split("\n")[4];
 
850
                } else if (e.sourceURL) {
 
851
                        // Safari, PhantomJS
 
852
                        // TODO sourceURL points at the 'throw new Error' line above, useless
 
853
                        //return e.sourceURL + ":" + e.line;
 
854
                }
 
855
        }
 
856
}
 
857
 
 
858
function escapeHtml(s) {
 
859
        if (!s) {
 
860
                return "";
 
861
        }
 
862
        s = s + "";
 
863
        return s.replace(/[\&"<>\\]/g, function(s) {
 
864
                switch(s) {
 
865
                        case "&": return "&amp;";
 
866
                        case "\\": return "\\\\";
 
867
                        case '"': return '\"';
 
868
                        case "<": return "&lt;";
 
869
                        case ">": return "&gt;";
 
870
                        default: return s;
 
871
                }
 
872
        });
 
873
}
 
874
 
 
875
function synchronize( callback ) {
 
876
        config.queue.push( callback );
 
877
 
 
878
        if ( config.autorun && !config.blocking ) {
 
879
                process();
 
880
        }
 
881
}
 
882
 
 
883
function process() {
 
884
        var start = (new Date()).getTime();
 
885
 
 
886
        while ( config.queue.length && !config.blocking ) {
 
887
                if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {
 
888
                        config.queue.shift()();
 
889
                } else {
 
890
                        window.setTimeout( process, 13 );
 
891
                        break;
 
892
                }
 
893
        }
 
894
        if (!config.blocking && !config.queue.length) {
 
895
                done();
 
896
        }
 
897
}
 
898
 
 
899
function saveGlobal() {
 
900
        config.pollution = [];
 
901
 
 
902
        if ( config.noglobals ) {
 
903
                for ( var key in window ) {
 
904
                        config.pollution.push( key );
 
905
                }
 
906
        }
 
907
}
 
908
 
 
909
function checkPollution( name ) {
 
910
        var old = config.pollution;
 
911
        saveGlobal();
 
912
 
 
913
        var newGlobals = diff( config.pollution, old );
 
914
        if ( newGlobals.length > 0 ) {
 
915
                ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
 
916
        }
 
917
 
 
918
        var deletedGlobals = diff( old, config.pollution );
 
919
        if ( deletedGlobals.length > 0 ) {
 
920
                ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
 
921
        }
 
922
}
 
923
 
 
924
// returns a new Array with the elements that are in a but not in b
 
925
function diff( a, b ) {
 
926
        var result = a.slice();
 
927
        for ( var i = 0; i < result.length; i++ ) {
 
928
                for ( var j = 0; j < b.length; j++ ) {
 
929
                        if ( result[i] === b[j] ) {
 
930
                                result.splice(i, 1);
 
931
                                i--;
 
932
                                break;
 
933
                        }
 
934
                }
 
935
        }
 
936
        return result;
 
937
}
 
938
 
 
939
function fail(message, exception, callback) {
 
940
        if ( typeof console !== "undefined" && console.error && console.warn ) {
 
941
                console.error(message);
 
942
                console.error(exception);
 
943
                console.warn(callback.toString());
 
944
 
 
945
        } else if ( window.opera && opera.postError ) {
 
946
                opera.postError(message, exception, callback.toString);
 
947
        }
 
948
}
 
949
 
 
950
function extend(a, b) {
 
951
        for ( var prop in b ) {
 
952
                if ( b[prop] === undefined ) {
 
953
                        delete a[prop];
 
954
                } else {
 
955
                        a[prop] = b[prop];
 
956
                }
 
957
        }
 
958
 
 
959
        return a;
 
960
}
 
961
 
 
962
function addEvent(elem, type, fn) {
 
963
        if ( elem.addEventListener ) {
 
964
                elem.addEventListener( type, fn, false );
 
965
        } else if ( elem.attachEvent ) {
 
966
                elem.attachEvent( "on" + type, fn );
 
967
        } else {
 
968
                fn();
 
969
        }
 
970
}
 
971
 
 
972
function id(name) {
 
973
        return !!(typeof document !== "undefined" && document && document.getElementById) &&
 
974
                document.getElementById( name );
 
975
}
 
976
 
 
977
// Test for equality any JavaScript type.
 
978
// Discussions and reference: http://philrathe.com/articles/equiv
 
979
// Test suites: http://philrathe.com/tests/equiv
 
980
// Author: Philippe RathĂ© <prathe@gmail.com>
 
981
QUnit.equiv = function () {
 
982
 
 
983
        var innerEquiv; // the real equiv function
 
984
        var callers = []; // stack to decide between skip/abort functions
 
985
        var parents = []; // stack to avoiding loops from circular referencing
 
986
 
 
987
        // Call the o related callback with the given arguments.
 
988
        function bindCallbacks(o, callbacks, args) {
 
989
                var prop = QUnit.objectType(o);
 
990
                if (prop) {
 
991
                        if (QUnit.objectType(callbacks[prop]) === "function") {
 
992
                                return callbacks[prop].apply(callbacks, args);
 
993
                        } else {
 
994
                                return callbacks[prop]; // or undefined
 
995
                        }
 
996
                }
 
997
        }
 
998
 
 
999
        var callbacks = function () {
 
1000
 
 
1001
                // for string, boolean, number and null
 
1002
                function useStrictEquality(b, a) {
 
1003
                        if (b instanceof a.constructor || a instanceof b.constructor) {
 
1004
                                // to catch short annotaion VS 'new' annotation of a
 
1005
                                // declaration
 
1006
                                // e.g. var i = 1;
 
1007
                                // var j = new Number(1);
 
1008
                                return a == b;
 
1009
                        } else {
 
1010
                                return a === b;
 
1011
                        }
 
1012
                }
 
1013
 
 
1014
                return {
 
1015
                        "string" : useStrictEquality,
 
1016
                        "boolean" : useStrictEquality,
 
1017
                        "number" : useStrictEquality,
 
1018
                        "null" : useStrictEquality,
 
1019
                        "undefined" : useStrictEquality,
 
1020
 
 
1021
                        "nan" : function(b) {
 
1022
                                return isNaN(b);
 
1023
                        },
 
1024
 
 
1025
                        "date" : function(b, a) {
 
1026
                                return QUnit.objectType(b) === "date"
 
1027
                                                && a.valueOf() === b.valueOf();
 
1028
                        },
 
1029
 
 
1030
                        "regexp" : function(b, a) {
 
1031
                                return QUnit.objectType(b) === "regexp"
 
1032
                                                && a.source === b.source && // the regex itself
 
1033
                                                a.global === b.global && // and its modifers
 
1034
                                                                                                        // (gmi) ...
 
1035
                                                a.ignoreCase === b.ignoreCase
 
1036
                                                && a.multiline === b.multiline;
 
1037
                        },
 
1038
 
 
1039
                        // - skip when the property is a method of an instance (OOP)
 
1040
                        // - abort otherwise,
 
1041
                        // initial === would have catch identical references anyway
 
1042
                        "function" : function() {
 
1043
                                var caller = callers[callers.length - 1];
 
1044
                                return caller !== Object && typeof caller !== "undefined";
 
1045
                        },
 
1046
 
 
1047
                        "array" : function(b, a) {
 
1048
                                var i, j, loop;
 
1049
                                var len;
 
1050
 
 
1051
                                // b could be an object literal here
 
1052
                                if (!(QUnit.objectType(b) === "array")) {
 
1053
                                        return false;
 
1054
                                }
 
1055
 
 
1056
                                len = a.length;
 
1057
                                if (len !== b.length) { // safe and faster
 
1058
                                        return false;
 
1059
                                }
 
1060
 
 
1061
                                // track reference to avoid circular references
 
1062
                                parents.push(a);
 
1063
                                for (i = 0; i < len; i++) {
 
1064
                                        loop = false;
 
1065
                                        for (j = 0; j < parents.length; j++) {
 
1066
                                                if (parents[j] === a[i]) {
 
1067
                                                        loop = true;// dont rewalk array
 
1068
                                                }
 
1069
                                        }
 
1070
                                        if (!loop && !innerEquiv(a[i], b[i])) {
 
1071
                                                parents.pop();
 
1072
                                                return false;
 
1073
                                        }
 
1074
                                }
 
1075
                                parents.pop();
 
1076
                                return true;
 
1077
                        },
 
1078
 
 
1079
                        "object" : function(b, a) {
 
1080
                                var i, j, loop;
 
1081
                                var eq = true; // unless we can proove it
 
1082
                                var aProperties = [], bProperties = []; // collection of
 
1083
                                                                                                                // strings
 
1084
 
 
1085
                                // comparing constructors is more strict than using
 
1086
                                // instanceof
 
1087
                                if (a.constructor !== b.constructor) {
 
1088
                                        return false;
 
1089
                                }
 
1090
 
 
1091
                                // stack constructor before traversing properties
 
1092
                                callers.push(a.constructor);
 
1093
                                // track reference to avoid circular references
 
1094
                                parents.push(a);
 
1095
 
 
1096
                                for (i in a) { // be strict: don't ensures hasOwnProperty
 
1097
                                                                // and go deep
 
1098
                                        loop = false;
 
1099
                                        for (j = 0; j < parents.length; j++) {
 
1100
                                                if (parents[j] === a[i])
 
1101
                                                        loop = true; // don't go down the same path
 
1102
                                                                                        // twice
 
1103
                                        }
 
1104
                                        aProperties.push(i); // collect a's properties
 
1105
 
 
1106
                                        if (!loop && !innerEquiv(a[i], b[i])) {
 
1107
                                                eq = false;
 
1108
                                                break;
 
1109
                                        }
 
1110
                                }
 
1111
 
 
1112
                                callers.pop(); // unstack, we are done
 
1113
                                parents.pop();
 
1114
 
 
1115
                                for (i in b) {
 
1116
                                        bProperties.push(i); // collect b's properties
 
1117
                                }
 
1118
 
 
1119
                                // Ensures identical properties name
 
1120
                                return eq
 
1121
                                                && innerEquiv(aProperties.sort(), bProperties
 
1122
                                                                .sort());
 
1123
                        }
 
1124
                };
 
1125
        }();
 
1126
 
 
1127
        innerEquiv = function() { // can take multiple arguments
 
1128
                var args = Array.prototype.slice.apply(arguments);
 
1129
                if (args.length < 2) {
 
1130
                        return true; // end transition
 
1131
                }
 
1132
 
 
1133
                return (function(a, b) {
 
1134
                        if (a === b) {
 
1135
                                return true; // catch the most you can
 
1136
                        } else if (a === null || b === null || typeof a === "undefined"
 
1137
                                        || typeof b === "undefined"
 
1138
                                        || QUnit.objectType(a) !== QUnit.objectType(b)) {
 
1139
                                return false; // don't lose time with error prone cases
 
1140
                        } else {
 
1141
                                return bindCallbacks(a, callbacks, [ b, a ]);
 
1142
                        }
 
1143
 
 
1144
                        // apply transition with (1..n) arguments
 
1145
                })(args[0], args[1])
 
1146
                                && arguments.callee.apply(this, args.splice(1,
 
1147
                                                args.length - 1));
 
1148
        };
 
1149
 
 
1150
        return innerEquiv;
 
1151
 
 
1152
}();
 
1153
 
 
1154
/**
 
1155
 * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
 
1156
 * http://flesler.blogspot.com Licensed under BSD
 
1157
 * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
 
1158
 *
 
1159
 * @projectDescription Advanced and extensible data dumping for Javascript.
 
1160
 * @version 1.0.0
 
1161
 * @author Ariel Flesler
 
1162
 * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
 
1163
 */
 
1164
QUnit.jsDump = (function() {
 
1165
        function quote( str ) {
 
1166
                return '"' + str.toString().replace(/"/g, '\\"') + '"';
 
1167
        };
 
1168
        function literal( o ) {
 
1169
                return o + '';
 
1170
        };
 
1171
        function join( pre, arr, post ) {
 
1172
                var s = jsDump.separator(),
 
1173
                        base = jsDump.indent(),
 
1174
                        inner = jsDump.indent(1);
 
1175
                if ( arr.join )
 
1176
                        arr = arr.join( ',' + s + inner );
 
1177
                if ( !arr )
 
1178
                        return pre + post;
 
1179
                return [ pre, inner + arr, base + post ].join(s);
 
1180
        };
 
1181
        function array( arr, stack ) {
 
1182
                var i = arr.length, ret = Array(i);
 
1183
                this.up();
 
1184
                while ( i-- )
 
1185
                        ret[i] = this.parse( arr[i] , undefined , stack);
 
1186
                this.down();
 
1187
                return join( '[', ret, ']' );
 
1188
        };
 
1189
 
 
1190
        var reName = /^function (\w+)/;
 
1191
 
 
1192
        var jsDump = {
 
1193
                parse:function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance
 
1194
                        stack = stack || [ ];
 
1195
                        var parser = this.parsers[ type || this.typeOf(obj) ];
 
1196
                        type = typeof parser;
 
1197
                        var inStack = inArray(obj, stack);
 
1198
                        if (inStack != -1) {
 
1199
                                return 'recursion('+(inStack - stack.length)+')';
 
1200
                        }
 
1201
                        //else
 
1202
                        if (type == 'function')  {
 
1203
                                        stack.push(obj);
 
1204
                                        var res = parser.call( this, obj, stack );
 
1205
                                        stack.pop();
 
1206
                                        return res;
 
1207
                        }
 
1208
                        // else
 
1209
                        return (type == 'string') ? parser : this.parsers.error;
 
1210
                },
 
1211
                typeOf:function( obj ) {
 
1212
                        var type;
 
1213
                        if ( obj === null ) {
 
1214
                                type = "null";
 
1215
                        } else if (typeof obj === "undefined") {
 
1216
                                type = "undefined";
 
1217
                        } else if (QUnit.is("RegExp", obj)) {
 
1218
                                type = "regexp";
 
1219
                        } else if (QUnit.is("Date", obj)) {
 
1220
                                type = "date";
 
1221
                        } else if (QUnit.is("Function", obj)) {
 
1222
                                type = "function";
 
1223
                        } else if (typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined") {
 
1224
                                type = "window";
 
1225
                        } else if (obj.nodeType === 9) {
 
1226
                                type = "document";
 
1227
                        } else if (obj.nodeType) {
 
1228
                                type = "node";
 
1229
                        } else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) {
 
1230
                                type = "array";
 
1231
                        } else {
 
1232
                                type = typeof obj;
 
1233
                        }
 
1234
                        return type;
 
1235
                },
 
1236
                separator:function() {
 
1237
                        return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? '&nbsp;' : ' ';
 
1238
                },
 
1239
                indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
 
1240
                        if ( !this.multiline )
 
1241
                                return '';
 
1242
                        var chr = this.indentChar;
 
1243
                        if ( this.HTML )
 
1244
                                chr = chr.replace(/\t/g,'   ').replace(/ /g,'&nbsp;');
 
1245
                        return Array( this._depth_ + (extra||0) ).join(chr);
 
1246
                },
 
1247
                up:function( a ) {
 
1248
                        this._depth_ += a || 1;
 
1249
                },
 
1250
                down:function( a ) {
 
1251
                        this._depth_ -= a || 1;
 
1252
                },
 
1253
                setParser:function( name, parser ) {
 
1254
                        this.parsers[name] = parser;
 
1255
                },
 
1256
                // The next 3 are exposed so you can use them
 
1257
                quote:quote,
 
1258
                literal:literal,
 
1259
                join:join,
 
1260
                //
 
1261
                _depth_: 1,
 
1262
                // This is the list of parsers, to modify them, use jsDump.setParser
 
1263
                parsers:{
 
1264
                        window: '[Window]',
 
1265
                        document: '[Document]',
 
1266
                        error:'[ERROR]', //when no parser is found, shouldn't happen
 
1267
                        unknown: '[Unknown]',
 
1268
                        'null':'null',
 
1269
                        'undefined':'undefined',
 
1270
                        'function':function( fn ) {
 
1271
                                var ret = 'function',
 
1272
                                        name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
 
1273
                                if ( name )
 
1274
                                        ret += ' ' + name;
 
1275
                                ret += '(';
 
1276
 
 
1277
                                ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join('');
 
1278
                                return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' );
 
1279
                        },
 
1280
                        array: array,
 
1281
                        nodelist: array,
 
1282
                        arguments: array,
 
1283
                        object:function( map, stack ) {
 
1284
                                var ret = [ ];
 
1285
                                QUnit.jsDump.up();
 
1286
                                for ( var key in map ) {
 
1287
                                    var val = map[key];
 
1288
                                        ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(val, undefined, stack));
 
1289
                }
 
1290
                                QUnit.jsDump.down();
 
1291
                                return join( '{', ret, '}' );
 
1292
                        },
 
1293
                        node:function( node ) {
 
1294
                                var open = QUnit.jsDump.HTML ? '&lt;' : '<',
 
1295
                                        close = QUnit.jsDump.HTML ? '&gt;' : '>';
 
1296
 
 
1297
                                var tag = node.nodeName.toLowerCase(),
 
1298
                                        ret = open + tag;
 
1299
 
 
1300
                                for ( var a in QUnit.jsDump.DOMAttrs ) {
 
1301
                                        var val = node[QUnit.jsDump.DOMAttrs[a]];
 
1302
                                        if ( val )
 
1303
                                                ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' );
 
1304
                                }
 
1305
                                return ret + close + open + '/' + tag + close;
 
1306
                        },
 
1307
                        functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function
 
1308
                                var l = fn.length;
 
1309
                                if ( !l ) return '';
 
1310
 
 
1311
                                var args = Array(l);
 
1312
                                while ( l-- )
 
1313
                                        args[l] = String.fromCharCode(97+l);//97 is 'a'
 
1314
                                return ' ' + args.join(', ') + ' ';
 
1315
                        },
 
1316
                        key:quote, //object calls it internally, the key part of an item in a map
 
1317
                        functionCode:'[code]', //function calls it internally, it's the content of the function
 
1318
                        attribute:quote, //node calls it internally, it's an html attribute value
 
1319
                        string:quote,
 
1320
                        date:quote,
 
1321
                        regexp:literal, //regex
 
1322
                        number:literal,
 
1323
                        'boolean':literal
 
1324
                },
 
1325
                DOMAttrs:{//attributes to dump from nodes, name=>realName
 
1326
                        id:'id',
 
1327
                        name:'name',
 
1328
                        'class':'className'
 
1329
                },
 
1330
                HTML:false,//if true, entities are escaped ( <, >, \t, space and \n )
 
1331
                indentChar:'  ',//indentation unit
 
1332
                multiline:true //if true, items in a collection, are separated by a \n, else just a space.
 
1333
        };
 
1334
 
 
1335
        return jsDump;
 
1336
})();
 
1337
 
 
1338
// from Sizzle.js
 
1339
function getText( elems ) {
 
1340
        var ret = "", elem;
 
1341
 
 
1342
        for ( var i = 0; elems[i]; i++ ) {
 
1343
                elem = elems[i];
 
1344
 
 
1345
                // Get the text from text nodes and CDATA nodes
 
1346
                if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
 
1347
                        ret += elem.nodeValue;
 
1348
 
 
1349
                // Traverse everything else, except comment nodes
 
1350
                } else if ( elem.nodeType !== 8 ) {
 
1351
                        ret += getText( elem.childNodes );
 
1352
                }
 
1353
        }
 
1354
 
 
1355
        return ret;
 
1356
};
 
1357
 
 
1358
//from jquery.js
 
1359
function inArray( elem, array ) {
 
1360
        if ( array.indexOf ) {
 
1361
                return array.indexOf( elem );
 
1362
        }
 
1363
 
 
1364
        for ( var i = 0, length = array.length; i < length; i++ ) {
 
1365
                if ( array[ i ] === elem ) {
 
1366
                        return i;
 
1367
                }
 
1368
        }
 
1369
 
 
1370
        return -1;
 
1371
}
 
1372
 
 
1373
/*
 
1374
 * Javascript Diff Algorithm
 
1375
 *  By John Resig (http://ejohn.org/)
 
1376
 *  Modified by Chu Alan "sprite"
 
1377
 *
 
1378
 * Released under the MIT license.
 
1379
 *
 
1380
 * More Info:
 
1381
 *  http://ejohn.org/projects/javascript-diff-algorithm/
 
1382
 *
 
1383
 * Usage: QUnit.diff(expected, actual)
 
1384
 *
 
1385
 * 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"
 
1386
 */
 
1387
QUnit.diff = (function() {
 
1388
        function diff(o, n) {
 
1389
                var ns = {};
 
1390
                var os = {};
 
1391
 
 
1392
                for (var i = 0; i < n.length; i++) {
 
1393
                        if (ns[n[i]] == null)
 
1394
                                ns[n[i]] = {
 
1395
                                        rows: [],
 
1396
                                        o: null
 
1397
                                };
 
1398
                        ns[n[i]].rows.push(i);
 
1399
                }
 
1400
 
 
1401
                for (var i = 0; i < o.length; i++) {
 
1402
                        if (os[o[i]] == null)
 
1403
                                os[o[i]] = {
 
1404
                                        rows: [],
 
1405
                                        n: null
 
1406
                                };
 
1407
                        os[o[i]].rows.push(i);
 
1408
                }
 
1409
 
 
1410
                for (var i in ns) {
 
1411
                        if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) {
 
1412
                                n[ns[i].rows[0]] = {
 
1413
                                        text: n[ns[i].rows[0]],
 
1414
                                        row: os[i].rows[0]
 
1415
                                };
 
1416
                                o[os[i].rows[0]] = {
 
1417
                                        text: o[os[i].rows[0]],
 
1418
                                        row: ns[i].rows[0]
 
1419
                                };
 
1420
                        }
 
1421
                }
 
1422
 
 
1423
                for (var i = 0; i < n.length - 1; i++) {
 
1424
                        if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null &&
 
1425
                        n[i + 1] == o[n[i].row + 1]) {
 
1426
                                n[i + 1] = {
 
1427
                                        text: n[i + 1],
 
1428
                                        row: n[i].row + 1
 
1429
                                };
 
1430
                                o[n[i].row + 1] = {
 
1431
                                        text: o[n[i].row + 1],
 
1432
                                        row: i + 1
 
1433
                                };
 
1434
                        }
 
1435
                }
 
1436
 
 
1437
                for (var i = n.length - 1; i > 0; i--) {
 
1438
                        if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null &&
 
1439
                        n[i - 1] == o[n[i].row - 1]) {
 
1440
                                n[i - 1] = {
 
1441
                                        text: n[i - 1],
 
1442
                                        row: n[i].row - 1
 
1443
                                };
 
1444
                                o[n[i].row - 1] = {
 
1445
                                        text: o[n[i].row - 1],
 
1446
                                        row: i - 1
 
1447
                                };
 
1448
                        }
 
1449
                }
 
1450
 
 
1451
                return {
 
1452
                        o: o,
 
1453
                        n: n
 
1454
                };
 
1455
        }
 
1456
 
 
1457
        return function(o, n) {
 
1458
                o = o.replace(/\s+$/, '');
 
1459
                n = n.replace(/\s+$/, '');
 
1460
                var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/));
 
1461
 
 
1462
                var str = "";
 
1463
 
 
1464
                var oSpace = o.match(/\s+/g);
 
1465
                if (oSpace == null) {
 
1466
                        oSpace = [" "];
 
1467
                }
 
1468
                else {
 
1469
                        oSpace.push(" ");
 
1470
                }
 
1471
                var nSpace = n.match(/\s+/g);
 
1472
                if (nSpace == null) {
 
1473
                        nSpace = [" "];
 
1474
                }
 
1475
                else {
 
1476
                        nSpace.push(" ");
 
1477
                }
 
1478
 
 
1479
                if (out.n.length == 0) {
 
1480
                        for (var i = 0; i < out.o.length; i++) {
 
1481
                                str += '<del>' + out.o[i] + oSpace[i] + "</del>";
 
1482
                        }
 
1483
                }
 
1484
                else {
 
1485
                        if (out.n[0].text == null) {
 
1486
                                for (n = 0; n < out.o.length && out.o[n].text == null; n++) {
 
1487
                                        str += '<del>' + out.o[n] + oSpace[n] + "</del>";
 
1488
                                }
 
1489
                        }
 
1490
 
 
1491
                        for (var i = 0; i < out.n.length; i++) {
 
1492
                                if (out.n[i].text == null) {
 
1493
                                        str += '<ins>' + out.n[i] + nSpace[i] + "</ins>";
 
1494
                                }
 
1495
                                else {
 
1496
                                        var pre = "";
 
1497
 
 
1498
                                        for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) {
 
1499
                                                pre += '<del>' + out.o[n] + oSpace[n] + "</del>";
 
1500
                                        }
 
1501
                                        str += " " + out.n[i].text + nSpace[i] + pre;
 
1502
                                }
 
1503
                        }
 
1504
                }
 
1505
 
 
1506
                return str;
 
1507
        };
 
1508
})();
 
1509
 
 
1510
})(this);