obi 0.0.1

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