ninjs 0.13.8 → 0.14.0

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