1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
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 | toString = Object.prototype.toString,
|
26 | hasOwn = Object.prototype.hasOwnProperty;
|
27 |
|
28 | var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {
|
29 | this.name = name;
|
30 | this.testName = testName;
|
31 | this.expected = expected;
|
32 | this.testEnvironmentArg = testEnvironmentArg;
|
33 | this.async = async;
|
34 | this.callback = callback;
|
35 | this.assertions = [];
|
36 | };
|
37 | Test.prototype = {
|
38 | init: function() {
|
39 | var tests = id("qunit-tests");
|
40 | if (tests) {
|
41 | var b = document.createElement("strong");
|
42 | b.innerHTML = "Running " + this.name;
|
43 | var li = document.createElement("li");
|
44 | li.appendChild( b );
|
45 | li.className = "running";
|
46 | li.id = this.id = "test-output" + testId++;
|
47 | tests.appendChild( li );
|
48 | }
|
49 | },
|
50 | setup: function() {
|
51 | if (this.module != config.previousModule) {
|
52 | if ( config.previousModule ) {
|
53 | runLoggingCallbacks('moduleDone', QUnit, {
|
54 | name: config.previousModule,
|
55 | failed: config.moduleStats.bad,
|
56 | passed: config.moduleStats.all - config.moduleStats.bad,
|
57 | total: config.moduleStats.all
|
58 | } );
|
59 | }
|
60 | config.previousModule = this.module;
|
61 | config.moduleStats = { all: 0, bad: 0 };
|
62 | runLoggingCallbacks( 'moduleStart', QUnit, {
|
63 | name: this.module
|
64 | } );
|
65 | }
|
66 |
|
67 | config.current = this;
|
68 | this.testEnvironment = extend({
|
69 | setup: function() {},
|
70 | teardown: function() {}
|
71 | }, this.moduleTestEnvironment);
|
72 | if (this.testEnvironmentArg) {
|
73 | extend(this.testEnvironment, this.testEnvironmentArg);
|
74 | }
|
75 |
|
76 | runLoggingCallbacks( 'testStart', QUnit, {
|
77 | name: this.testName,
|
78 | module: this.module
|
79 | });
|
80 |
|
81 |
|
82 |
|
83 | QUnit.current_testEnvironment = this.testEnvironment;
|
84 |
|
85 | try {
|
86 | if ( !config.pollution ) {
|
87 | saveGlobal();
|
88 | }
|
89 |
|
90 | this.testEnvironment.setup.call(this.testEnvironment);
|
91 | } catch(e) {
|
92 | QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message );
|
93 | }
|
94 | },
|
95 | run: function() {
|
96 | config.current = this;
|
97 | if ( this.async ) {
|
98 | QUnit.stop();
|
99 | }
|
100 |
|
101 | if ( config.notrycatch ) {
|
102 | this.callback.call(this.testEnvironment);
|
103 | return;
|
104 | }
|
105 | try {
|
106 | this.callback.call(this.testEnvironment);
|
107 | } catch(e) {
|
108 | fail("Test " + this.testName + " died, exception and test follows", e, this.callback);
|
109 | QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) );
|
110 |
|
111 | saveGlobal();
|
112 |
|
113 |
|
114 | if ( config.blocking ) {
|
115 | QUnit.start();
|
116 | }
|
117 | }
|
118 | },
|
119 | teardown: function() {
|
120 | config.current = this;
|
121 | try {
|
122 | this.testEnvironment.teardown.call(this.testEnvironment);
|
123 | checkPollution();
|
124 | } catch(e) {
|
125 | QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message );
|
126 | }
|
127 | },
|
128 | finish: function() {
|
129 | config.current = this;
|
130 | if ( this.expected != null && this.expected != this.assertions.length ) {
|
131 | QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );
|
132 | }
|
133 |
|
134 | var good = 0, bad = 0,
|
135 | tests = id("qunit-tests");
|
136 |
|
137 | config.stats.all += this.assertions.length;
|
138 | config.moduleStats.all += this.assertions.length;
|
139 |
|
140 | if ( tests ) {
|
141 | var ol = document.createElement("ol");
|
142 |
|
143 | for ( var i = 0; i < this.assertions.length; i++ ) {
|
144 | var assertion = this.assertions[i];
|
145 |
|
146 | var li = document.createElement("li");
|
147 | li.className = assertion.result ? "pass" : "fail";
|
148 | li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed");
|
149 | ol.appendChild( li );
|
150 |
|
151 | if ( assertion.result ) {
|
152 | good++;
|
153 | } else {
|
154 | bad++;
|
155 | config.stats.bad++;
|
156 | config.moduleStats.bad++;
|
157 | }
|
158 | }
|
159 |
|
160 |
|
161 | if ( QUnit.config.reorder && defined.sessionStorage ) {
|
162 | if (bad) {
|
163 | sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad);
|
164 | } else {
|
165 | sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName);
|
166 | }
|
167 | }
|
168 |
|
169 | if (bad == 0) {
|
170 | ol.style.display = "none";
|
171 | }
|
172 |
|
173 | var b = document.createElement("strong");
|
174 | b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
|
175 |
|
176 | var a = document.createElement("a");
|
177 | a.innerHTML = "Rerun";
|
178 | a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
|
179 |
|
180 | addEvent(b, "click", function() {
|
181 | var next = b.nextSibling.nextSibling,
|
182 | display = next.style.display;
|
183 | next.style.display = display === "none" ? "block" : "none";
|
184 | });
|
185 |
|
186 | addEvent(b, "dblclick", function(e) {
|
187 | var target = e && e.target ? e.target : window.event.srcElement;
|
188 | if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
|
189 | target = target.parentNode;
|
190 | }
|
191 | if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
|
192 | window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
|
193 | }
|
194 | });
|
195 |
|
196 | var li = id(this.id);
|
197 | li.className = bad ? "fail" : "pass";
|
198 | li.removeChild( li.firstChild );
|
199 | li.appendChild( b );
|
200 | li.appendChild( a );
|
201 | li.appendChild( ol );
|
202 |
|
203 | } else {
|
204 | for ( var i = 0; i < this.assertions.length; i++ ) {
|
205 | if ( !this.assertions[i].result ) {
|
206 | bad++;
|
207 | config.stats.bad++;
|
208 | config.moduleStats.bad++;
|
209 | }
|
210 | }
|
211 | }
|
212 |
|
213 | try {
|
214 | QUnit.reset();
|
215 | } catch(e) {
|
216 | fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset);
|
217 | }
|
218 |
|
219 | runLoggingCallbacks( 'testDone', QUnit, {
|
220 | name: this.testName,
|
221 | module: this.module,
|
222 | failed: bad,
|
223 | passed: this.assertions.length - bad,
|
224 | total: this.assertions.length
|
225 | } );
|
226 | },
|
227 |
|
228 | queue: function() {
|
229 | var test = this;
|
230 | synchronize(function() {
|
231 | test.init();
|
232 | });
|
233 | function run() {
|
234 |
|
235 | synchronize(function() {
|
236 | test.setup();
|
237 | });
|
238 | synchronize(function() {
|
239 | test.run();
|
240 | });
|
241 | synchronize(function() {
|
242 | test.teardown();
|
243 | });
|
244 | synchronize(function() {
|
245 | test.finish();
|
246 | });
|
247 | }
|
248 |
|
249 | var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName);
|
250 | if (bad) {
|
251 | run();
|
252 | } else {
|
253 | synchronize(run, true);
|
254 | };
|
255 | }
|
256 |
|
257 | };
|
258 |
|
259 | var QUnit = {
|
260 |
|
261 |
|
262 | module: function(name, testEnvironment) {
|
263 | config.currentModule = name;
|
264 | config.currentModuleTestEnviroment = testEnvironment;
|
265 | },
|
266 |
|
267 | asyncTest: function(testName, expected, callback) {
|
268 | if ( arguments.length === 2 ) {
|
269 | callback = expected;
|
270 | expected = null;
|
271 | }
|
272 |
|
273 | QUnit.test(testName, expected, callback, true);
|
274 | },
|
275 |
|
276 | test: function(testName, expected, callback, async) {
|
277 | var name = '<span class="test-name">' + testName + '</span>', testEnvironmentArg;
|
278 |
|
279 | if ( arguments.length === 2 ) {
|
280 | callback = expected;
|
281 | expected = null;
|
282 | }
|
283 |
|
284 | if ( expected && typeof expected === 'object') {
|
285 | testEnvironmentArg = expected;
|
286 | expected = null;
|
287 | }
|
288 |
|
289 | if ( config.currentModule ) {
|
290 | name = '<span class="module-name">' + config.currentModule + "</span>: " + name;
|
291 | }
|
292 |
|
293 | if ( !validTest(config.currentModule + ": " + testName) ) {
|
294 | return;
|
295 | }
|
296 |
|
297 | var test = new Test(name, testName, expected, testEnvironmentArg, async, callback);
|
298 | test.module = config.currentModule;
|
299 | test.moduleTestEnvironment = config.currentModuleTestEnviroment;
|
300 | test.queue();
|
301 | },
|
302 |
|
303 | |
304 |
|
305 |
|
306 | expect: function(asserts) {
|
307 | config.current.expected = asserts;
|
308 | },
|
309 |
|
310 | |
311 |
|
312 |
|
313 |
|
314 | ok: function(a, msg) {
|
315 | a = !!a;
|
316 | var details = {
|
317 | result: a,
|
318 | message: msg
|
319 | };
|
320 | msg = escapeInnerText(msg);
|
321 | runLoggingCallbacks( 'log', QUnit, details );
|
322 | config.current.assertions.push({
|
323 | result: a,
|
324 | message: msg
|
325 | });
|
326 | },
|
327 |
|
328 | |
329 |
|
330 |
|
331 |
|
332 |
|
333 |
|
334 |
|
335 |
|
336 |
|
337 |
|
338 |
|
339 |
|
340 | equal: function(actual, expected, message) {
|
341 | QUnit.push(expected == actual, actual, expected, message);
|
342 | },
|
343 |
|
344 | notEqual: function(actual, expected, message) {
|
345 | QUnit.push(expected != actual, actual, expected, message);
|
346 | },
|
347 |
|
348 | deepEqual: function(actual, expected, message) {
|
349 | QUnit.push(QUnit.equiv(actual, expected), actual, expected, message);
|
350 | },
|
351 |
|
352 | notDeepEqual: function(actual, expected, message) {
|
353 | QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message);
|
354 | },
|
355 |
|
356 | strictEqual: function(actual, expected, message) {
|
357 | QUnit.push(expected === actual, actual, expected, message);
|
358 | },
|
359 |
|
360 | notStrictEqual: function(actual, expected, message) {
|
361 | QUnit.push(expected !== actual, actual, expected, message);
|
362 | },
|
363 |
|
364 | raises: function(block, expected, message) {
|
365 | var actual, ok = false;
|
366 |
|
367 | if (typeof expected === 'string') {
|
368 | message = expected;
|
369 | expected = null;
|
370 | }
|
371 |
|
372 | try {
|
373 | block();
|
374 | } catch (e) {
|
375 | actual = e;
|
376 | }
|
377 |
|
378 | if (actual) {
|
379 |
|
380 | if (!expected) {
|
381 | ok = true;
|
382 |
|
383 | } else if (QUnit.objectType(expected) === "regexp") {
|
384 | ok = expected.test(actual);
|
385 |
|
386 | } else if (actual instanceof expected) {
|
387 | ok = true;
|
388 |
|
389 | } else if (expected.call({}, actual) === true) {
|
390 | ok = true;
|
391 | }
|
392 | }
|
393 |
|
394 | QUnit.ok(ok, message);
|
395 | },
|
396 |
|
397 | start: function(count) {
|
398 | config.semaphore -= count || 1;
|
399 | if (config.semaphore > 0) {
|
400 |
|
401 | return;
|
402 | }
|
403 | if (config.semaphore < 0) {
|
404 |
|
405 | config.semaphore = 0;
|
406 | }
|
407 |
|
408 | if ( defined.setTimeout ) {
|
409 | window.setTimeout(function() {
|
410 | if (config.semaphore > 0) {
|
411 | return;
|
412 | }
|
413 | if ( config.timeout ) {
|
414 | clearTimeout(config.timeout);
|
415 | }
|
416 |
|
417 | config.blocking = false;
|
418 | process(true);
|
419 | }, 13);
|
420 | } else {
|
421 | config.blocking = false;
|
422 | process(true);
|
423 | }
|
424 | },
|
425 |
|
426 | stop: function(count) {
|
427 | config.semaphore += count || 1;
|
428 | config.blocking = true;
|
429 |
|
430 | if ( config.testTimeout && defined.setTimeout ) {
|
431 | clearTimeout(config.timeout);
|
432 | config.timeout = window.setTimeout(function() {
|
433 | QUnit.ok( false, "Test timed out" );
|
434 | config.semaphore = 1;
|
435 | QUnit.start();
|
436 | }, config.testTimeout);
|
437 | }
|
438 | }
|
439 | };
|
440 |
|
441 |
|
442 | (function() {
|
443 | function F(){};
|
444 | F.prototype = QUnit;
|
445 | QUnit = new F();
|
446 |
|
447 | QUnit.constructor = F;
|
448 | })();
|
449 |
|
450 |
|
451 | QUnit.equals = QUnit.equal;
|
452 | QUnit.same = QUnit.deepEqual;
|
453 |
|
454 |
|
455 | var config = {
|
456 |
|
457 | queue: [],
|
458 |
|
459 |
|
460 | blocking: true,
|
461 |
|
462 |
|
463 |
|
464 | hidepassed: false,
|
465 |
|
466 |
|
467 |
|
468 | reorder: true,
|
469 |
|
470 |
|
471 | altertitle: true,
|
472 |
|
473 | urlConfig: ['noglobals', 'notrycatch'],
|
474 |
|
475 |
|
476 | begin: [],
|
477 | done: [],
|
478 | log: [],
|
479 | testStart: [],
|
480 | testDone: [],
|
481 | moduleStart: [],
|
482 | moduleDone: []
|
483 | };
|
484 |
|
485 |
|
486 | (function() {
|
487 | var location = window.location || { search: "", protocol: "file:" },
|
488 | params = location.search.slice( 1 ).split( "&" ),
|
489 | length = params.length,
|
490 | urlParams = {},
|
491 | current;
|
492 |
|
493 | if ( params[ 0 ] ) {
|
494 | for ( var i = 0; i < length; i++ ) {
|
495 | current = params[ i ].split( "=" );
|
496 | current[ 0 ] = decodeURIComponent( current[ 0 ] );
|
497 |
|
498 | current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
|
499 | urlParams[ current[ 0 ] ] = current[ 1 ];
|
500 | }
|
501 | }
|
502 |
|
503 | QUnit.urlParams = urlParams;
|
504 | config.filter = urlParams.filter;
|
505 |
|
506 |
|
507 | QUnit.isLocal = !!(location.protocol === 'file:');
|
508 | })();
|
509 |
|
510 |
|
511 |
|
512 | if ( typeof exports === "undefined" || typeof require === "undefined" ) {
|
513 | extend(window, QUnit);
|
514 | window.QUnit = QUnit;
|
515 | } else {
|
516 | extend(exports, QUnit);
|
517 | exports.QUnit = QUnit;
|
518 | }
|
519 |
|
520 |
|
521 | extend(QUnit, {
|
522 | config: config,
|
523 |
|
524 |
|
525 | init: function() {
|
526 | extend(config, {
|
527 | stats: { all: 0, bad: 0 },
|
528 | moduleStats: { all: 0, bad: 0 },
|
529 | started: +new Date,
|
530 | updateRate: 1000,
|
531 | blocking: false,
|
532 | autostart: true,
|
533 | autorun: false,
|
534 | filter: "",
|
535 | queue: [],
|
536 | semaphore: 0
|
537 | });
|
538 |
|
539 | var tests = id( "qunit-tests" ),
|
540 | banner = id( "qunit-banner" ),
|
541 | result = id( "qunit-testresult" );
|
542 |
|
543 | if ( tests ) {
|
544 | tests.innerHTML = "";
|
545 | }
|
546 |
|
547 | if ( banner ) {
|
548 | banner.className = "";
|
549 | }
|
550 |
|
551 | if ( result ) {
|
552 | result.parentNode.removeChild( result );
|
553 | }
|
554 |
|
555 | if ( tests ) {
|
556 | result = document.createElement( "p" );
|
557 | result.id = "qunit-testresult";
|
558 | result.className = "result";
|
559 | tests.parentNode.insertBefore( result, tests );
|
560 | result.innerHTML = 'Running...<br/> ';
|
561 | }
|
562 | },
|
563 |
|
564 | |
565 |
|
566 |
|
567 |
|
568 |
|
569 | reset: function() {
|
570 | if ( window.jQuery ) {
|
571 | jQuery( "#qunit-fixture" ).html( config.fixture );
|
572 | } else {
|
573 | var main = id( 'qunit-fixture' );
|
574 | if ( main ) {
|
575 | main.innerHTML = config.fixture;
|
576 | }
|
577 | }
|
578 | },
|
579 |
|
580 | |
581 |
|
582 |
|
583 |
|
584 |
|
585 |
|
586 |
|
587 |
|
588 | triggerEvent: function( elem, type, event ) {
|
589 | if ( document.createEvent ) {
|
590 | event = document.createEvent("MouseEvents");
|
591 | event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
|
592 | 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
593 | elem.dispatchEvent( event );
|
594 |
|
595 | } else if ( elem.fireEvent ) {
|
596 | elem.fireEvent("on"+type);
|
597 | }
|
598 | },
|
599 |
|
600 |
|
601 | is: function( type, obj ) {
|
602 | return QUnit.objectType( obj ) == type;
|
603 | },
|
604 |
|
605 | objectType: function( obj ) {
|
606 | if (typeof obj === "undefined") {
|
607 | return "undefined";
|
608 |
|
609 |
|
610 | }
|
611 | if (obj === null) {
|
612 | return "null";
|
613 | }
|
614 |
|
615 | var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || '';
|
616 |
|
617 | switch (type) {
|
618 | case 'Number':
|
619 | if (isNaN(obj)) {
|
620 | return "nan";
|
621 | } else {
|
622 | return "number";
|
623 | }
|
624 | case 'String':
|
625 | case 'Boolean':
|
626 | case 'Array':
|
627 | case 'Date':
|
628 | case 'RegExp':
|
629 | case 'Function':
|
630 | return type.toLowerCase();
|
631 | }
|
632 | if (typeof obj === "object") {
|
633 | return "object";
|
634 | }
|
635 | return undefined;
|
636 | },
|
637 |
|
638 | push: function(result, actual, expected, message) {
|
639 | var details = {
|
640 | result: result,
|
641 | message: message,
|
642 | actual: actual,
|
643 | expected: expected
|
644 | };
|
645 |
|
646 | message = escapeInnerText(message) || (result ? "okay" : "failed");
|
647 | message = '<span class="test-message">' + message + "</span>";
|
648 | expected = escapeInnerText(QUnit.jsDump.parse(expected));
|
649 | actual = escapeInnerText(QUnit.jsDump.parse(actual));
|
650 | var output = message + '<table><tr class="test-expected"><th>Expected: </th><td><pre>' + expected + '</pre></td></tr>';
|
651 | if (actual != expected) {
|
652 | output += '<tr class="test-actual"><th>Result: </th><td><pre>' + actual + '</pre></td></tr>';
|
653 | output += '<tr class="test-diff"><th>Diff: </th><td><pre>' + QUnit.diff(expected, actual) +'</pre></td></tr>';
|
654 | }
|
655 | if (!result) {
|
656 | var source = sourceFromStacktrace();
|
657 | if (source) {
|
658 | details.source = source;
|
659 | output += '<tr class="test-source"><th>Source: </th><td><pre>' + escapeInnerText(source) + '</pre></td></tr>';
|
660 | }
|
661 | }
|
662 | output += "</table>";
|
663 |
|
664 | runLoggingCallbacks( 'log', QUnit, details );
|
665 |
|
666 | config.current.assertions.push({
|
667 | result: !!result,
|
668 | message: output
|
669 | });
|
670 | },
|
671 |
|
672 | url: function( params ) {
|
673 | params = extend( extend( {}, QUnit.urlParams ), params );
|
674 | var querystring = "?",
|
675 | key;
|
676 | for ( key in params ) {
|
677 | if ( !hasOwn.call( params, key ) ) {
|
678 | continue;
|
679 | }
|
680 | querystring += encodeURIComponent( key ) + "=" +
|
681 | encodeURIComponent( params[ key ] ) + "&";
|
682 | }
|
683 | return window.location.pathname + querystring.slice( 0, -1 );
|
684 | },
|
685 |
|
686 | extend: extend,
|
687 | id: id,
|
688 | addEvent: addEvent
|
689 | });
|
690 |
|
691 |
|
692 |
|
693 |
|
694 | extend(QUnit.constructor.prototype, {
|
695 |
|
696 |
|
697 | begin: registerLoggingCallback('begin'),
|
698 |
|
699 | done: registerLoggingCallback('done'),
|
700 |
|
701 | log: registerLoggingCallback('log'),
|
702 |
|
703 | testStart: registerLoggingCallback('testStart'),
|
704 |
|
705 | testDone: registerLoggingCallback('testDone'),
|
706 |
|
707 | moduleStart: registerLoggingCallback('moduleStart'),
|
708 |
|
709 | moduleDone: registerLoggingCallback('moduleDone')
|
710 | });
|
711 |
|
712 | if ( typeof document === "undefined" || document.readyState === "complete" ) {
|
713 | config.autorun = true;
|
714 | }
|
715 |
|
716 | QUnit.load = function() {
|
717 | runLoggingCallbacks( 'begin', QUnit, {} );
|
718 |
|
719 |
|
720 | var oldconfig = extend({}, config);
|
721 | QUnit.init();
|
722 | extend(config, oldconfig);
|
723 |
|
724 | config.blocking = false;
|
725 |
|
726 | var urlConfigHtml = '', len = config.urlConfig.length;
|
727 | for ( var i = 0, val; i < len, val = config.urlConfig[i]; i++ ) {
|
728 | config[val] = QUnit.urlParams[val];
|
729 | urlConfigHtml += '<label><input name="' + val + '" type="checkbox"' + ( config[val] ? ' checked="checked"' : '' ) + '>' + val + '</label>';
|
730 | }
|
731 |
|
732 | var userAgent = id("qunit-userAgent");
|
733 | if ( userAgent ) {
|
734 | userAgent.innerHTML = navigator.userAgent;
|
735 | }
|
736 | var banner = id("qunit-header");
|
737 | if ( banner ) {
|
738 | banner.innerHTML = '<a href="' + QUnit.url({ filter: undefined }) + '"> ' + banner.innerHTML + '</a> ' + urlConfigHtml;
|
739 | addEvent( banner, "change", function( event ) {
|
740 | var params = {};
|
741 | params[ event.target.name ] = event.target.checked ? true : undefined;
|
742 | window.location = QUnit.url( params );
|
743 | });
|
744 | }
|
745 |
|
746 | var toolbar = id("qunit-testrunner-toolbar");
|
747 | if ( toolbar ) {
|
748 | var filter = document.createElement("input");
|
749 | filter.type = "checkbox";
|
750 | filter.id = "qunit-filter-pass";
|
751 | addEvent( filter, "click", function() {
|
752 | var ol = document.getElementById("qunit-tests");
|
753 | if ( filter.checked ) {
|
754 | ol.className = ol.className + " hidepass";
|
755 | } else {
|
756 | var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
|
757 | ol.className = tmp.replace(/ hidepass /, " ");
|
758 | }
|
759 | if ( defined.sessionStorage ) {
|
760 | if (filter.checked) {
|
761 | sessionStorage.setItem("qunit-filter-passed-tests", "true");
|
762 | } else {
|
763 | sessionStorage.removeItem("qunit-filter-passed-tests");
|
764 | }
|
765 | }
|
766 | });
|
767 | if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) {
|
768 | filter.checked = true;
|
769 | var ol = document.getElementById("qunit-tests");
|
770 | ol.className = ol.className + " hidepass";
|
771 | }
|
772 | toolbar.appendChild( filter );
|
773 |
|
774 | var label = document.createElement("label");
|
775 | label.setAttribute("for", "qunit-filter-pass");
|
776 | label.innerHTML = "Hide passed tests";
|
777 | toolbar.appendChild( label );
|
778 | }
|
779 |
|
780 | var main = id('qunit-fixture');
|
781 | if ( main ) {
|
782 | config.fixture = main.innerHTML;
|
783 | }
|
784 |
|
785 | if (config.autostart) {
|
786 | QUnit.start();
|
787 | }
|
788 | };
|
789 |
|
790 | addEvent(window, "load", QUnit.load);
|
791 |
|
792 |
|
793 | window.onerror = function( message, file, line ) {
|
794 | if ( QUnit.config.current ) {
|
795 | ok( false, message + ", " + file + ":" + line );
|
796 | } else {
|
797 | test( "global failure", function() {
|
798 | ok( false, message + ", " + file + ":" + line );
|
799 | });
|
800 | }
|
801 | };
|
802 |
|
803 | function done() {
|
804 | config.autorun = true;
|
805 |
|
806 |
|
807 | if ( config.currentModule ) {
|
808 | runLoggingCallbacks( 'moduleDone', QUnit, {
|
809 | name: config.currentModule,
|
810 | failed: config.moduleStats.bad,
|
811 | passed: config.moduleStats.all - config.moduleStats.bad,
|
812 | total: config.moduleStats.all
|
813 | } );
|
814 | }
|
815 |
|
816 | var banner = id("qunit-banner"),
|
817 | tests = id("qunit-tests"),
|
818 | runtime = +new Date - config.started,
|
819 | passed = config.stats.all - config.stats.bad,
|
820 | html = [
|
821 | 'Tests completed in ',
|
822 | runtime,
|
823 | ' milliseconds.<br/>',
|
824 | '<span class="passed">',
|
825 | passed,
|
826 | '</span> tests of <span class="total">',
|
827 | config.stats.all,
|
828 | '</span> passed, <span class="failed">',
|
829 | config.stats.bad,
|
830 | '</span> failed.'
|
831 | ].join('');
|
832 |
|
833 | if ( banner ) {
|
834 | banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
|
835 | }
|
836 |
|
837 | if ( tests ) {
|
838 | id( "qunit-testresult" ).innerHTML = html;
|
839 | }
|
840 |
|
841 | if ( config.altertitle && typeof document !== "undefined" && document.title ) {
|
842 |
|
843 |
|
844 | document.title = [
|
845 | (config.stats.bad ? "\u2716" : "\u2714"),
|
846 | document.title.replace(/^[\u2714\u2716] /i, "")
|
847 | ].join(" ");
|
848 | }
|
849 |
|
850 | runLoggingCallbacks( 'done', QUnit, {
|
851 | failed: config.stats.bad,
|
852 | passed: passed,
|
853 | total: config.stats.all,
|
854 | runtime: runtime
|
855 | } );
|
856 | }
|
857 |
|
858 | function validTest( name ) {
|
859 | var filter = config.filter,
|
860 | run = false;
|
861 |
|
862 | if ( !filter ) {
|
863 | return true;
|
864 | }
|
865 |
|
866 | var not = filter.charAt( 0 ) === "!";
|
867 | if ( not ) {
|
868 | filter = filter.slice( 1 );
|
869 | }
|
870 |
|
871 | if ( name.indexOf( filter ) !== -1 ) {
|
872 | return !not;
|
873 | }
|
874 |
|
875 | if ( not ) {
|
876 | run = true;
|
877 | }
|
878 |
|
879 | return run;
|
880 | }
|
881 |
|
882 |
|
883 |
|
884 | function sourceFromStacktrace() {
|
885 | try {
|
886 | throw new Error();
|
887 | } catch ( e ) {
|
888 | if (e.stacktrace) {
|
889 |
|
890 | return e.stacktrace.split("\n")[6];
|
891 | } else if (e.stack) {
|
892 |
|
893 | return e.stack.split("\n")[4];
|
894 | } else if (e.sourceURL) {
|
895 |
|
896 |
|
897 |
|
898 | }
|
899 | }
|
900 | }
|
901 |
|
902 | function escapeInnerText(s) {
|
903 | if (!s) {
|
904 | return "";
|
905 | }
|
906 | s = s + "";
|
907 | return s.replace(/[\&<>]/g, function(s) {
|
908 | switch(s) {
|
909 | case "&": return "&";
|
910 | case "<": return "<";
|
911 | case ">": return ">";
|
912 | default: return s;
|
913 | }
|
914 | });
|
915 | }
|
916 |
|
917 | function synchronize( callback, last ) {
|
918 | config.queue.push( callback );
|
919 |
|
920 | if ( config.autorun && !config.blocking ) {
|
921 | process(last);
|
922 | }
|
923 | }
|
924 |
|
925 | function process( last ) {
|
926 | var start = new Date().getTime();
|
927 | config.depth = config.depth ? config.depth + 1 : 1;
|
928 |
|
929 | while ( config.queue.length && !config.blocking ) {
|
930 | if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
|
931 | config.queue.shift()();
|
932 | } else {
|
933 | window.setTimeout( function(){
|
934 | process( last );
|
935 | }, 13 );
|
936 | break;
|
937 | }
|
938 | }
|
939 | config.depth--;
|
940 | if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
|
941 | done();
|
942 | }
|
943 | }
|
944 |
|
945 | function saveGlobal() {
|
946 | config.pollution = [];
|
947 |
|
948 | if ( config.noglobals ) {
|
949 | for ( var key in window ) {
|
950 | if ( !hasOwn.call( window, key ) ) {
|
951 | continue;
|
952 | }
|
953 | config.pollution.push( key );
|
954 | }
|
955 | }
|
956 | }
|
957 |
|
958 | function checkPollution( name ) {
|
959 | var old = config.pollution;
|
960 | saveGlobal();
|
961 |
|
962 | var newGlobals = diff( config.pollution, old );
|
963 | if ( newGlobals.length > 0 ) {
|
964 | ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
|
965 | }
|
966 |
|
967 | var deletedGlobals = diff( old, config.pollution );
|
968 | if ( deletedGlobals.length > 0 ) {
|
969 | ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
|
970 | }
|
971 | }
|
972 |
|
973 |
|
974 | function diff( a, b ) {
|
975 | var result = a.slice();
|
976 | for ( var i = 0; i < result.length; i++ ) {
|
977 | for ( var j = 0; j < b.length; j++ ) {
|
978 | if ( result[i] === b[j] ) {
|
979 | result.splice(i, 1);
|
980 | i--;
|
981 | break;
|
982 | }
|
983 | }
|
984 | }
|
985 | return result;
|
986 | }
|
987 |
|
988 | function fail(message, exception, callback) {
|
989 | if ( typeof console !== "undefined" && console.error && console.warn ) {
|
990 | console.error(message);
|
991 | console.error(exception);
|
992 | console.warn(callback.toString());
|
993 |
|
994 | } else if ( window.opera && opera.postError ) {
|
995 | opera.postError(message, exception, callback.toString);
|
996 | }
|
997 | }
|
998 |
|
999 | function extend(a, b) {
|
1000 | for ( var prop in b ) {
|
1001 | if ( b[prop] === undefined ) {
|
1002 | delete a[prop];
|
1003 |
|
1004 |
|
1005 | } else if ( prop !== "constructor" || a !== window ) {
|
1006 | a[prop] = b[prop];
|
1007 | }
|
1008 | }
|
1009 |
|
1010 | return a;
|
1011 | }
|
1012 |
|
1013 | function addEvent(elem, type, fn) {
|
1014 | if ( elem.addEventListener ) {
|
1015 | elem.addEventListener( type, fn, false );
|
1016 | } else if ( elem.attachEvent ) {
|
1017 | elem.attachEvent( "on" + type, fn );
|
1018 | } else {
|
1019 | fn();
|
1020 | }
|
1021 | }
|
1022 |
|
1023 | function id(name) {
|
1024 | return !!(typeof document !== "undefined" && document && document.getElementById) &&
|
1025 | document.getElementById( name );
|
1026 | }
|
1027 |
|
1028 | function registerLoggingCallback(key){
|
1029 | return function(callback){
|
1030 | config[key].push( callback );
|
1031 | };
|
1032 | }
|
1033 |
|
1034 |
|
1035 | function runLoggingCallbacks(key, scope, args) {
|
1036 |
|
1037 | var callbacks;
|
1038 | if ( QUnit.hasOwnProperty(key) ) {
|
1039 | QUnit[key].call(scope, args);
|
1040 | } else {
|
1041 | callbacks = config[key];
|
1042 | for( var i = 0; i < callbacks.length; i++ ) {
|
1043 | callbacks[i].call( scope, args );
|
1044 | }
|
1045 | }
|
1046 | }
|
1047 |
|
1048 |
|
1049 |
|
1050 | QUnit.equiv = function () {
|
1051 |
|
1052 | var innerEquiv;
|
1053 | var callers = [];
|
1054 | var parents = [];
|
1055 |
|
1056 |
|
1057 | function bindCallbacks(o, callbacks, args) {
|
1058 | var prop = QUnit.objectType(o);
|
1059 | if (prop) {
|
1060 | if (QUnit.objectType(callbacks[prop]) === "function") {
|
1061 | return callbacks[prop].apply(callbacks, args);
|
1062 | } else {
|
1063 | return callbacks[prop];
|
1064 | }
|
1065 | }
|
1066 | }
|
1067 |
|
1068 | var callbacks = function () {
|
1069 |
|
1070 |
|
1071 | function useStrictEquality(b, a) {
|
1072 | if (b instanceof a.constructor || a instanceof b.constructor) {
|
1073 |
|
1074 |
|
1075 |
|
1076 |
|
1077 | return a == b;
|
1078 | } else {
|
1079 | return a === b;
|
1080 | }
|
1081 | }
|
1082 |
|
1083 | return {
|
1084 | "string" : useStrictEquality,
|
1085 | "boolean" : useStrictEquality,
|
1086 | "number" : useStrictEquality,
|
1087 | "null" : useStrictEquality,
|
1088 | "undefined" : useStrictEquality,
|
1089 |
|
1090 | "nan" : function(b) {
|
1091 | return isNaN(b);
|
1092 | },
|
1093 |
|
1094 | "date" : function(b, a) {
|
1095 | return QUnit.objectType(b) === "date"
|
1096 | && a.valueOf() === b.valueOf();
|
1097 | },
|
1098 |
|
1099 | "regexp" : function(b, a) {
|
1100 | return QUnit.objectType(b) === "regexp"
|
1101 | && a.source === b.source &&
|
1102 | a.global === b.global &&
|
1103 |
|
1104 | a.ignoreCase === b.ignoreCase
|
1105 | && a.multiline === b.multiline;
|
1106 | },
|
1107 |
|
1108 |
|
1109 |
|
1110 |
|
1111 | "function" : function() {
|
1112 | var caller = callers[callers.length - 1];
|
1113 | return caller !== Object && typeof caller !== "undefined";
|
1114 | },
|
1115 |
|
1116 | "array" : function(b, a) {
|
1117 | var i, j, loop;
|
1118 | var len;
|
1119 |
|
1120 |
|
1121 | if (!(QUnit.objectType(b) === "array")) {
|
1122 | return false;
|
1123 | }
|
1124 |
|
1125 | len = a.length;
|
1126 | if (len !== b.length) {
|
1127 | return false;
|
1128 | }
|
1129 |
|
1130 |
|
1131 | parents.push(a);
|
1132 | for (i = 0; i < len; i++) {
|
1133 | loop = false;
|
1134 | for (j = 0; j < parents.length; j++) {
|
1135 | if (parents[j] === a[i]) {
|
1136 | loop = true;
|
1137 | }
|
1138 | }
|
1139 | if (!loop && !innerEquiv(a[i], b[i])) {
|
1140 | parents.pop();
|
1141 | return false;
|
1142 | }
|
1143 | }
|
1144 | parents.pop();
|
1145 | return true;
|
1146 | },
|
1147 |
|
1148 | "object" : function(b, a) {
|
1149 | var i, j, loop;
|
1150 | var eq = true;
|
1151 | var aProperties = [], bProperties = [];
|
1152 |
|
1153 |
|
1154 |
|
1155 |
|
1156 | if (a.constructor !== b.constructor) {
|
1157 | return false;
|
1158 | }
|
1159 |
|
1160 |
|
1161 | callers.push(a.constructor);
|
1162 |
|
1163 | parents.push(a);
|
1164 |
|
1165 | for (i in a) {
|
1166 |
|
1167 | loop = false;
|
1168 | for (j = 0; j < parents.length; j++) {
|
1169 | if (parents[j] === a[i])
|
1170 | loop = true;
|
1171 |
|
1172 | }
|
1173 | aProperties.push(i);
|
1174 |
|
1175 | if (!loop && !innerEquiv(a[i], b[i])) {
|
1176 | eq = false;
|
1177 | break;
|
1178 | }
|
1179 | }
|
1180 |
|
1181 | callers.pop();
|
1182 | parents.pop();
|
1183 |
|
1184 | for (i in b) {
|
1185 | bProperties.push(i);
|
1186 | }
|
1187 |
|
1188 |
|
1189 | return eq
|
1190 | && innerEquiv(aProperties.sort(), bProperties
|
1191 | .sort());
|
1192 | }
|
1193 | };
|
1194 | }();
|
1195 |
|
1196 | innerEquiv = function() {
|
1197 | var args = Array.prototype.slice.apply(arguments);
|
1198 | if (args.length < 2) {
|
1199 | return true;
|
1200 | }
|
1201 |
|
1202 | return (function(a, b) {
|
1203 | if (a === b) {
|
1204 | return true;
|
1205 | } else if (a === null || b === null || typeof a === "undefined"
|
1206 | || typeof b === "undefined"
|
1207 | || QUnit.objectType(a) !== QUnit.objectType(b)) {
|
1208 | return false;
|
1209 | } else {
|
1210 | return bindCallbacks(a, callbacks, [ b, a ]);
|
1211 | }
|
1212 |
|
1213 |
|
1214 | })(args[0], args[1])
|
1215 | && arguments.callee.apply(this, args.splice(1,
|
1216 | args.length - 1));
|
1217 | };
|
1218 |
|
1219 | return innerEquiv;
|
1220 |
|
1221 | }();
|
1222 |
|
1223 |
|
1224 |
|
1225 |
|
1226 |
|
1227 |
|
1228 |
|
1229 |
|
1230 |
|
1231 |
|
1232 |
|
1233 | QUnit.jsDump = (function() {
|
1234 | function quote( str ) {
|
1235 | return '"' + str.toString().replace(/"/g, '\\"') + '"';
|
1236 | };
|
1237 | function literal( o ) {
|
1238 | return o + '';
|
1239 | };
|
1240 | function join( pre, arr, post ) {
|
1241 | var s = jsDump.separator(),
|
1242 | base = jsDump.indent(),
|
1243 | inner = jsDump.indent(1);
|
1244 | if ( arr.join )
|
1245 | arr = arr.join( ',' + s + inner );
|
1246 | if ( !arr )
|
1247 | return pre + post;
|
1248 | return [ pre, inner + arr, base + post ].join(s);
|
1249 | };
|
1250 | function array( arr, stack ) {
|
1251 | var i = arr.length, ret = Array(i);
|
1252 | this.up();
|
1253 | while ( i-- )
|
1254 | ret[i] = this.parse( arr[i] , undefined , stack);
|
1255 | this.down();
|
1256 | return join( '[', ret, ']' );
|
1257 | };
|
1258 |
|
1259 | var reName = /^function (\w+)/;
|
1260 |
|
1261 | var jsDump = {
|
1262 | parse:function( obj, type, stack ) {
|
1263 | stack = stack || [ ];
|
1264 | var parser = this.parsers[ type || this.typeOf(obj) ];
|
1265 | type = typeof parser;
|
1266 | var inStack = inArray(obj, stack);
|
1267 | if (inStack != -1) {
|
1268 | return 'recursion('+(inStack - stack.length)+')';
|
1269 | }
|
1270 |
|
1271 | if (type == 'function') {
|
1272 | stack.push(obj);
|
1273 | var res = parser.call( this, obj, stack );
|
1274 | stack.pop();
|
1275 | return res;
|
1276 | }
|
1277 |
|
1278 | return (type == 'string') ? parser : this.parsers.error;
|
1279 | },
|
1280 | typeOf:function( obj ) {
|
1281 | var type;
|
1282 | if ( obj === null ) {
|
1283 | type = "null";
|
1284 | } else if (typeof obj === "undefined") {
|
1285 | type = "undefined";
|
1286 | } else if (QUnit.is("RegExp", obj)) {
|
1287 | type = "regexp";
|
1288 | } else if (QUnit.is("Date", obj)) {
|
1289 | type = "date";
|
1290 | } else if (QUnit.is("Function", obj)) {
|
1291 | type = "function";
|
1292 | } else if (typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined") {
|
1293 | type = "window";
|
1294 | } else if (obj.nodeType === 9) {
|
1295 | type = "document";
|
1296 | } else if (obj.nodeType) {
|
1297 | type = "node";
|
1298 | } else if (
|
1299 |
|
1300 | toString.call( obj ) === "[object Array]" ||
|
1301 |
|
1302 | ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
|
1303 | ) {
|
1304 | type = "array";
|
1305 | } else {
|
1306 | type = typeof obj;
|
1307 | }
|
1308 | return type;
|
1309 | },
|
1310 | separator:function() {
|
1311 | return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? ' ' : ' ';
|
1312 | },
|
1313 | indent:function( extra ) {
|
1314 | if ( !this.multiline )
|
1315 | return '';
|
1316 | var chr = this.indentChar;
|
1317 | if ( this.HTML )
|
1318 | chr = chr.replace(/\t/g,' ').replace(/ /g,' ');
|
1319 | return Array( this._depth_ + (extra||0) ).join(chr);
|
1320 | },
|
1321 | up:function( a ) {
|
1322 | this._depth_ += a || 1;
|
1323 | },
|
1324 | down:function( a ) {
|
1325 | this._depth_ -= a || 1;
|
1326 | },
|
1327 | setParser:function( name, parser ) {
|
1328 | this.parsers[name] = parser;
|
1329 | },
|
1330 |
|
1331 | quote:quote,
|
1332 | literal:literal,
|
1333 | join:join,
|
1334 |
|
1335 | _depth_: 1,
|
1336 |
|
1337 | parsers:{
|
1338 | window: '[Window]',
|
1339 | document: '[Document]',
|
1340 | error:'[ERROR]',
|
1341 | unknown: '[Unknown]',
|
1342 | 'null':'null',
|
1343 | 'undefined':'undefined',
|
1344 | 'function':function( fn ) {
|
1345 | var ret = 'function',
|
1346 | name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];
|
1347 | if ( name )
|
1348 | ret += ' ' + name;
|
1349 | ret += '(';
|
1350 |
|
1351 | ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join('');
|
1352 | return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' );
|
1353 | },
|
1354 | array: array,
|
1355 | nodelist: array,
|
1356 | arguments: array,
|
1357 | object:function( map, stack ) {
|
1358 | var ret = [ ];
|
1359 | QUnit.jsDump.up();
|
1360 | for ( var key in map ) {
|
1361 | var val = map[key];
|
1362 | ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(val, undefined, stack));
|
1363 | }
|
1364 | QUnit.jsDump.down();
|
1365 | return join( '{', ret, '}' );
|
1366 | },
|
1367 | node:function( node ) {
|
1368 | var open = QUnit.jsDump.HTML ? '<' : '<',
|
1369 | close = QUnit.jsDump.HTML ? '>' : '>';
|
1370 |
|
1371 | var tag = node.nodeName.toLowerCase(),
|
1372 | ret = open + tag;
|
1373 |
|
1374 | for ( var a in QUnit.jsDump.DOMAttrs ) {
|
1375 | var val = node[QUnit.jsDump.DOMAttrs[a]];
|
1376 | if ( val )
|
1377 | ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' );
|
1378 | }
|
1379 | return ret + close + open + '/' + tag + close;
|
1380 | },
|
1381 | functionArgs:function( fn ) {
|
1382 | var l = fn.length;
|
1383 | if ( !l ) return '';
|
1384 |
|
1385 | var args = Array(l);
|
1386 | while ( l-- )
|
1387 | args[l] = String.fromCharCode(97+l);
|
1388 | return ' ' + args.join(', ') + ' ';
|
1389 | },
|
1390 | key:quote,
|
1391 | functionCode:'[code]',
|
1392 | attribute:quote,
|
1393 | string:quote,
|
1394 | date:quote,
|
1395 | regexp:literal,
|
1396 | number:literal,
|
1397 | 'boolean':literal
|
1398 | },
|
1399 | DOMAttrs:{
|
1400 | id:'id',
|
1401 | name:'name',
|
1402 | 'class':'className'
|
1403 | },
|
1404 | HTML:false,
|
1405 | indentChar:' ',
|
1406 | multiline:true
|
1407 | };
|
1408 |
|
1409 | return jsDump;
|
1410 | })();
|
1411 |
|
1412 |
|
1413 | function getText( elems ) {
|
1414 | var ret = "", elem;
|
1415 |
|
1416 | for ( var i = 0; elems[i]; i++ ) {
|
1417 | elem = elems[i];
|
1418 |
|
1419 |
|
1420 | if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
|
1421 | ret += elem.nodeValue;
|
1422 |
|
1423 |
|
1424 | } else if ( elem.nodeType !== 8 ) {
|
1425 | ret += getText( elem.childNodes );
|
1426 | }
|
1427 | }
|
1428 |
|
1429 | return ret;
|
1430 | };
|
1431 |
|
1432 |
|
1433 | function inArray( elem, array ) {
|
1434 | if ( array.indexOf ) {
|
1435 | return array.indexOf( elem );
|
1436 | }
|
1437 |
|
1438 | for ( var i = 0, length = array.length; i < length; i++ ) {
|
1439 | if ( array[ i ] === elem ) {
|
1440 | return i;
|
1441 | }
|
1442 | }
|
1443 |
|
1444 | return -1;
|
1445 | }
|
1446 |
|
1447 |
|
1448 |
|
1449 |
|
1450 |
|
1451 |
|
1452 |
|
1453 |
|
1454 |
|
1455 |
|
1456 |
|
1457 |
|
1458 |
|
1459 |
|
1460 |
|
1461 | QUnit.diff = (function() {
|
1462 | function diff(o, n) {
|
1463 | var ns = {};
|
1464 | var os = {};
|
1465 |
|
1466 | for (var i = 0; i < n.length; i++) {
|
1467 | if (ns[n[i]] == null)
|
1468 | ns[n[i]] = {
|
1469 | rows: [],
|
1470 | o: null
|
1471 | };
|
1472 | ns[n[i]].rows.push(i);
|
1473 | }
|
1474 |
|
1475 | for (var i = 0; i < o.length; i++) {
|
1476 | if (os[o[i]] == null)
|
1477 | os[o[i]] = {
|
1478 | rows: [],
|
1479 | n: null
|
1480 | };
|
1481 | os[o[i]].rows.push(i);
|
1482 | }
|
1483 |
|
1484 | for (var i in ns) {
|
1485 | if ( !hasOwn.call( ns, i ) ) {
|
1486 | continue;
|
1487 | }
|
1488 | if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) {
|
1489 | n[ns[i].rows[0]] = {
|
1490 | text: n[ns[i].rows[0]],
|
1491 | row: os[i].rows[0]
|
1492 | };
|
1493 | o[os[i].rows[0]] = {
|
1494 | text: o[os[i].rows[0]],
|
1495 | row: ns[i].rows[0]
|
1496 | };
|
1497 | }
|
1498 | }
|
1499 |
|
1500 | for (var i = 0; i < n.length - 1; i++) {
|
1501 | if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null &&
|
1502 | n[i + 1] == o[n[i].row + 1]) {
|
1503 | n[i + 1] = {
|
1504 | text: n[i + 1],
|
1505 | row: n[i].row + 1
|
1506 | };
|
1507 | o[n[i].row + 1] = {
|
1508 | text: o[n[i].row + 1],
|
1509 | row: i + 1
|
1510 | };
|
1511 | }
|
1512 | }
|
1513 |
|
1514 | for (var i = n.length - 1; i > 0; i--) {
|
1515 | if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null &&
|
1516 | n[i - 1] == o[n[i].row - 1]) {
|
1517 | n[i - 1] = {
|
1518 | text: n[i - 1],
|
1519 | row: n[i].row - 1
|
1520 | };
|
1521 | o[n[i].row - 1] = {
|
1522 | text: o[n[i].row - 1],
|
1523 | row: i - 1
|
1524 | };
|
1525 | }
|
1526 | }
|
1527 |
|
1528 | return {
|
1529 | o: o,
|
1530 | n: n
|
1531 | };
|
1532 | }
|
1533 |
|
1534 | return function(o, n) {
|
1535 | o = o.replace(/\s+$/, '');
|
1536 | n = n.replace(/\s+$/, '');
|
1537 | var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/));
|
1538 |
|
1539 | var str = "";
|
1540 |
|
1541 | var oSpace = o.match(/\s+/g);
|
1542 | if (oSpace == null) {
|
1543 | oSpace = [" "];
|
1544 | }
|
1545 | else {
|
1546 | oSpace.push(" ");
|
1547 | }
|
1548 | var nSpace = n.match(/\s+/g);
|
1549 | if (nSpace == null) {
|
1550 | nSpace = [" "];
|
1551 | }
|
1552 | else {
|
1553 | nSpace.push(" ");
|
1554 | }
|
1555 |
|
1556 | if (out.n.length == 0) {
|
1557 | for (var i = 0; i < out.o.length; i++) {
|
1558 | str += '<del>' + out.o[i] + oSpace[i] + "</del>";
|
1559 | }
|
1560 | }
|
1561 | else {
|
1562 | if (out.n[0].text == null) {
|
1563 | for (n = 0; n < out.o.length && out.o[n].text == null; n++) {
|
1564 | str += '<del>' + out.o[n] + oSpace[n] + "</del>";
|
1565 | }
|
1566 | }
|
1567 |
|
1568 | for (var i = 0; i < out.n.length; i++) {
|
1569 | if (out.n[i].text == null) {
|
1570 | str += '<ins>' + out.n[i] + nSpace[i] + "</ins>";
|
1571 | }
|
1572 | else {
|
1573 | var pre = "";
|
1574 |
|
1575 | for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) {
|
1576 | pre += '<del>' + out.o[n] + oSpace[n] + "</del>";
|
1577 | }
|
1578 | str += " " + out.n[i].text + nSpace[i] + pre;
|
1579 | }
|
1580 | }
|
1581 | }
|
1582 |
|
1583 | return str;
|
1584 | };
|
1585 | })();
|
1586 |
|
1587 | })(this);
|