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