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