local_time 1.0.2 → 1.0.3

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