~ubuntu-branches/ubuntu/precise/horizon/precise-updates

« back to all changes in this revision

Viewing changes to openstack-dashboard/dashboard/static/dashboard/js/bootstrap/tests/vendor/qunit.js

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2012-02-24 10:49:27 UTC
  • mfrom: (1.1.7)
  • Revision ID: package-import@ubuntu.com-20120224104927-0v71grkyxtu106l4
Tags: 2012.1~e4~20120224.1386-0ubuntu1
New upstream version. 

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);
 
 
b'\\ No newline at end of file'