r101705 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r101704‎ | r101705 | r101706 >
Date:21:29, 2 November 2011
Author:tparscal
Status:ok
Tags:
Comment:
Copying jquery and qunit to VisualEditor extension
Modified paths:
  • /trunk/extensions/VisualEditor/modules/qunit.js (added) (history)

Diff [purge]

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

Status & tagging log