[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/resources/lib/jquery/ -> jquery.qunit.js (source)

   1  /*!
   2   * QUnit 1.14.0
   3   * http://qunitjs.com/
   4   *
   5   * Copyright 2013 jQuery Foundation and other contributors
   6   * Released under the MIT license
   7   * http://jquery.org/license
   8   *
   9   * Date: 2014-01-31T16:40Z
  10   */
  11  
  12  (function( window ) {
  13  
  14  var QUnit,
  15      assert,
  16      config,
  17      onErrorFnPrev,
  18      testId = 0,
  19      fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
  20      toString = Object.prototype.toString,
  21      hasOwn = Object.prototype.hasOwnProperty,
  22      // Keep a local reference to Date (GH-283)
  23      Date = window.Date,
  24      setTimeout = window.setTimeout,
  25      clearTimeout = window.clearTimeout,
  26      defined = {
  27          document: typeof window.document !== "undefined",
  28          setTimeout: typeof window.setTimeout !== "undefined",
  29          sessionStorage: (function() {
  30              var x = "qunit-test-string";
  31              try {
  32                  sessionStorage.setItem( x, x );
  33                  sessionStorage.removeItem( x );
  34                  return true;
  35              } catch( e ) {
  36                  return false;
  37              }
  38          }())
  39      },
  40      /**
  41       * Provides a normalized error string, correcting an issue
  42       * with IE 7 (and prior) where Error.prototype.toString is
  43       * not properly implemented
  44       *
  45       * Based on http://es5.github.com/#x15.11.4.4
  46       *
  47       * @param {String|Error} error
  48       * @return {String} error message
  49       */
  50      errorString = function( error ) {
  51          var name, message,
  52              errorString = error.toString();
  53          if ( errorString.substring( 0, 7 ) === "[object" ) {
  54              name = error.name ? error.name.toString() : "Error";
  55              message = error.message ? error.message.toString() : "";
  56              if ( name && message ) {
  57                  return name + ": " + message;
  58              } else if ( name ) {
  59                  return name;
  60              } else if ( message ) {
  61                  return message;
  62              } else {
  63                  return "Error";
  64              }
  65          } else {
  66              return errorString;
  67          }
  68      },
  69      /**
  70       * Makes a clone of an object using only Array or Object as base,
  71       * and copies over the own enumerable properties.
  72       *
  73       * @param {Object} obj
  74       * @return {Object} New object with only the own properties (recursively).
  75       */
  76      objectValues = function( obj ) {
  77          // Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392.
  78          /*jshint newcap: false */
  79          var key, val,
  80              vals = QUnit.is( "array", obj ) ? [] : {};
  81          for ( key in obj ) {
  82              if ( hasOwn.call( obj, key ) ) {
  83                  val = obj[key];
  84                  vals[key] = val === Object(val) ? objectValues(val) : val;
  85              }
  86          }
  87          return vals;
  88      };
  89  
  90  
  91  // Root QUnit object.
  92  // `QUnit` initialized at top of scope
  93  QUnit = {
  94  
  95      // call on start of module test to prepend name to all tests
  96      module: function( name, testEnvironment ) {
  97          config.currentModule = name;
  98          config.currentModuleTestEnvironment = testEnvironment;
  99          config.modules[name] = true;
 100      },
 101  
 102      asyncTest: function( testName, expected, callback ) {
 103          if ( arguments.length === 2 ) {
 104              callback = expected;
 105              expected = null;
 106          }
 107  
 108          QUnit.test( testName, expected, callback, true );
 109      },
 110  
 111      test: function( testName, expected, callback, async ) {
 112          var test,
 113              nameHtml = "<span class='test-name'>" + escapeText( testName ) + "</span>";
 114  
 115          if ( arguments.length === 2 ) {
 116              callback = expected;
 117              expected = null;
 118          }
 119  
 120          if ( config.currentModule ) {
 121              nameHtml = "<span class='module-name'>" + escapeText( config.currentModule ) + "</span>: " + nameHtml;
 122          }
 123  
 124          test = new Test({
 125              nameHtml: nameHtml,
 126              testName: testName,
 127              expected: expected,
 128              async: async,
 129              callback: callback,
 130              module: config.currentModule,
 131              moduleTestEnvironment: config.currentModuleTestEnvironment,
 132              stack: sourceFromStacktrace( 2 )
 133          });
 134  
 135          if ( !validTest( test ) ) {
 136              return;
 137          }
 138  
 139          test.queue();
 140      },
 141  
 142      // Specify the number of expected assertions to guarantee that failed test (no assertions are run at all) don't slip through.
 143      expect: function( asserts ) {
 144          if (arguments.length === 1) {
 145              config.current.expected = asserts;
 146          } else {
 147              return config.current.expected;
 148          }
 149      },
 150  
 151      start: function( count ) {
 152          // QUnit hasn't been initialized yet.
 153          // Note: RequireJS (et al) may delay onLoad
 154          if ( config.semaphore === undefined ) {
 155              QUnit.begin(function() {
 156                  // This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first
 157                  setTimeout(function() {
 158                      QUnit.start( count );
 159                  });
 160              });
 161              return;
 162          }
 163  
 164          config.semaphore -= count || 1;
 165          // don't start until equal number of stop-calls
 166          if ( config.semaphore > 0 ) {
 167              return;
 168          }
 169          // ignore if start is called more often then stop
 170          if ( config.semaphore < 0 ) {
 171              config.semaphore = 0;
 172              QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) );
 173              return;
 174          }
 175          // A slight delay, to avoid any current callbacks
 176          if ( defined.setTimeout ) {
 177              setTimeout(function() {
 178                  if ( config.semaphore > 0 ) {
 179                      return;
 180                  }
 181                  if ( config.timeout ) {
 182                      clearTimeout( config.timeout );
 183                  }
 184  
 185                  config.blocking = false;
 186                  process( true );
 187              }, 13);
 188          } else {
 189              config.blocking = false;
 190              process( true );
 191          }
 192      },
 193  
 194      stop: function( count ) {
 195          config.semaphore += count || 1;
 196          config.blocking = true;
 197  
 198          if ( config.testTimeout && defined.setTimeout ) {
 199              clearTimeout( config.timeout );
 200              config.timeout = setTimeout(function() {
 201                  QUnit.ok( false, "Test timed out" );
 202                  config.semaphore = 1;
 203                  QUnit.start();
 204              }, config.testTimeout );
 205          }
 206      }
 207  };
 208  
 209  // We use the prototype to distinguish between properties that should
 210  // be exposed as globals (and in exports) and those that shouldn't
 211  (function() {
 212      function F() {}
 213      F.prototype = QUnit;
 214      QUnit = new F();
 215      // Make F QUnit's constructor so that we can add to the prototype later
 216      QUnit.constructor = F;
 217  }());
 218  
 219  /**
 220   * Config object: Maintain internal state
 221   * Later exposed as QUnit.config
 222   * `config` initialized at top of scope
 223   */
 224  config = {
 225      // The queue of tests to run
 226      queue: [],
 227  
 228      // block until document ready
 229      blocking: true,
 230  
 231      // when enabled, show only failing tests
 232      // gets persisted through sessionStorage and can be changed in UI via checkbox
 233      hidepassed: false,
 234  
 235      // by default, run previously failed tests first
 236      // very useful in combination with "Hide passed tests" checked
 237      reorder: true,
 238  
 239      // by default, modify document.title when suite is done
 240      altertitle: true,
 241  
 242      // by default, scroll to top of the page when suite is done
 243      scrolltop: true,
 244  
 245      // when enabled, all tests must call expect()
 246      requireExpects: false,
 247  
 248      // add checkboxes that are persisted in the query-string
 249      // when enabled, the id is set to `true` as a `QUnit.config` property
 250      urlConfig: [
 251          {
 252              id: "noglobals",
 253              label: "Check for Globals",
 254              tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
 255          },
 256          {
 257              id: "notrycatch",
 258              label: "No try-catch",
 259              tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
 260          }
 261      ],
 262  
 263      // Set of all modules.
 264      modules: {},
 265  
 266      // logging callback queues
 267      begin: [],
 268      done: [],
 269      log: [],
 270      testStart: [],
 271      testDone: [],
 272      moduleStart: [],
 273      moduleDone: []
 274  };
 275  
 276  // Initialize more QUnit.config and QUnit.urlParams
 277  (function() {
 278      var i, current,
 279          location = window.location || { search: "", protocol: "file:" },
 280          params = location.search.slice( 1 ).split( "&" ),
 281          length = params.length,
 282          urlParams = {};
 283  
 284      if ( params[ 0 ] ) {
 285          for ( i = 0; i < length; i++ ) {
 286              current = params[ i ].split( "=" );
 287              current[ 0 ] = decodeURIComponent( current[ 0 ] );
 288  
 289              // allow just a key to turn on a flag, e.g., test.html?noglobals
 290              current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
 291              if ( urlParams[ current[ 0 ] ] ) {
 292                  urlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] );
 293              } else {
 294                  urlParams[ current[ 0 ] ] = current[ 1 ];
 295              }
 296          }
 297      }
 298  
 299      QUnit.urlParams = urlParams;
 300  
 301      // String search anywhere in moduleName+testName
 302      config.filter = urlParams.filter;
 303  
 304      // Exact match of the module name
 305      config.module = urlParams.module;
 306  
 307      config.testNumber = [];
 308      if ( urlParams.testNumber ) {
 309  
 310          // Ensure that urlParams.testNumber is an array
 311          urlParams.testNumber = [].concat( urlParams.testNumber );
 312          for ( i = 0; i < urlParams.testNumber.length; i++ ) {
 313              current = urlParams.testNumber[ i ];
 314              config.testNumber.push( parseInt( current, 10 ) );
 315          }
 316      }
 317  
 318      // Figure out if we're running the tests from a server or not
 319      QUnit.isLocal = location.protocol === "file:";
 320  }());
 321  
 322  extend( QUnit, {
 323  
 324      config: config,
 325  
 326      // Initialize the configuration options
 327      init: function() {
 328          extend( config, {
 329              stats: { all: 0, bad: 0 },
 330              moduleStats: { all: 0, bad: 0 },
 331              started: +new Date(),
 332              updateRate: 1000,
 333              blocking: false,
 334              autostart: true,
 335              autorun: false,
 336              filter: "",
 337              queue: [],
 338              semaphore: 1
 339          });
 340  
 341          var tests, banner, result,
 342              qunit = id( "qunit" );
 343  
 344          if ( qunit ) {
 345              qunit.innerHTML =
 346                  "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
 347                  "<h2 id='qunit-banner'></h2>" +
 348                  "<div id='qunit-testrunner-toolbar'></div>" +
 349                  "<h2 id='qunit-userAgent'></h2>" +
 350                  "<ol id='qunit-tests'></ol>";
 351          }
 352  
 353          tests = id( "qunit-tests" );
 354          banner = id( "qunit-banner" );
 355          result = id( "qunit-testresult" );
 356  
 357          if ( tests ) {
 358              tests.innerHTML = "";
 359          }
 360  
 361          if ( banner ) {
 362              banner.className = "";
 363          }
 364  
 365          if ( result ) {
 366              result.parentNode.removeChild( result );
 367          }
 368  
 369          if ( tests ) {
 370              result = document.createElement( "p" );
 371              result.id = "qunit-testresult";
 372              result.className = "result";
 373              tests.parentNode.insertBefore( result, tests );
 374              result.innerHTML = "Running...<br/>&nbsp;";
 375          }
 376      },
 377  
 378      // Resets the test setup. Useful for tests that modify the DOM.
 379      /*
 380      DEPRECATED: Use multiple tests instead of resetting inside a test.
 381      Use testStart or testDone for custom cleanup.
 382      This method will throw an error in 2.0, and will be removed in 2.1
 383      */
 384      reset: function() {
 385          var fixture = id( "qunit-fixture" );
 386          if ( fixture ) {
 387              fixture.innerHTML = config.fixture;
 388          }
 389      },
 390  
 391      // Safe object type checking
 392      is: function( type, obj ) {
 393          return QUnit.objectType( obj ) === type;
 394      },
 395  
 396      objectType: function( obj ) {
 397          if ( typeof obj === "undefined" ) {
 398              return "undefined";
 399          }
 400  
 401          // Consider: typeof null === object
 402          if ( obj === null ) {
 403              return "null";
 404          }
 405  
 406          var match = toString.call( obj ).match(/^\[object\s(.*)\]$/),
 407              type = match && match[1] || "";
 408  
 409          switch ( type ) {
 410              case "Number":
 411                  if ( isNaN(obj) ) {
 412                      return "nan";
 413                  }
 414                  return "number";
 415              case "String":
 416              case "Boolean":
 417              case "Array":
 418              case "Date":
 419              case "RegExp":
 420              case "Function":
 421                  return type.toLowerCase();
 422          }
 423          if ( typeof obj === "object" ) {
 424              return "object";
 425          }
 426          return undefined;
 427      },
 428  
 429      push: function( result, actual, expected, message ) {
 430          if ( !config.current ) {
 431              throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );
 432          }
 433  
 434          var output, source,
 435              details = {
 436                  module: config.current.module,
 437                  name: config.current.testName,
 438                  result: result,
 439                  message: message,
 440                  actual: actual,
 441                  expected: expected
 442              };
 443  
 444          message = escapeText( message ) || ( result ? "okay" : "failed" );
 445          message = "<span class='test-message'>" + message + "</span>";
 446          output = message;
 447  
 448          if ( !result ) {
 449              expected = escapeText( QUnit.jsDump.parse(expected) );
 450              actual = escapeText( QUnit.jsDump.parse(actual) );
 451              output += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + expected + "</pre></td></tr>";
 452  
 453              if ( actual !== expected ) {
 454                  output += "<tr class='test-actual'><th>Result: </th><td><pre>" + actual + "</pre></td></tr>";
 455                  output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>";
 456              }
 457  
 458              source = sourceFromStacktrace();
 459  
 460              if ( source ) {
 461                  details.source = source;
 462                  output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";
 463              }
 464  
 465              output += "</table>";
 466          }
 467  
 468          runLoggingCallbacks( "log", QUnit, details );
 469  
 470          config.current.assertions.push({
 471              result: !!result,
 472              message: output
 473          });
 474      },
 475  
 476      pushFailure: function( message, source, actual ) {
 477          if ( !config.current ) {
 478              throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );
 479          }
 480  
 481          var output,
 482              details = {
 483                  module: config.current.module,
 484                  name: config.current.testName,
 485                  result: false,
 486                  message: message
 487              };
 488  
 489          message = escapeText( message ) || "error";
 490          message = "<span class='test-message'>" + message + "</span>";
 491          output = message;
 492  
 493          output += "<table>";
 494  
 495          if ( actual ) {
 496              output += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText( actual ) + "</pre></td></tr>";
 497          }
 498  
 499          if ( source ) {
 500              details.source = source;
 501              output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";
 502          }
 503  
 504          output += "</table>";
 505  
 506          runLoggingCallbacks( "log", QUnit, details );
 507  
 508          config.current.assertions.push({
 509              result: false,
 510              message: output
 511          });
 512      },
 513  
 514      url: function( params ) {
 515          params = extend( extend( {}, QUnit.urlParams ), params );
 516          var key,
 517              querystring = "?";
 518  
 519          for ( key in params ) {
 520              if ( hasOwn.call( params, key ) ) {
 521                  querystring += encodeURIComponent( key ) + "=" +
 522                      encodeURIComponent( params[ key ] ) + "&";
 523              }
 524          }
 525          return window.location.protocol + "//" + window.location.host +
 526              window.location.pathname + querystring.slice( 0, -1 );
 527      },
 528  
 529      extend: extend,
 530      id: id,
 531      addEvent: addEvent,
 532      addClass: addClass,
 533      hasClass: hasClass,
 534      removeClass: removeClass
 535      // load, equiv, jsDump, diff: Attached later
 536  });
 537  
 538  /**
 539   * @deprecated: Created for backwards compatibility with test runner that set the hook function
 540   * into QUnit.{hook}, instead of invoking it and passing the hook function.
 541   * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
 542   * Doing this allows us to tell if the following methods have been overwritten on the actual
 543   * QUnit object.
 544   */
 545  extend( QUnit.constructor.prototype, {
 546  
 547      // Logging callbacks; all receive a single argument with the listed properties
 548      // run test/logs.html for any related changes
 549      begin: registerLoggingCallback( "begin" ),
 550  
 551      // done: { failed, passed, total, runtime }
 552      done: registerLoggingCallback( "done" ),
 553  
 554      // log: { result, actual, expected, message }
 555      log: registerLoggingCallback( "log" ),
 556  
 557      // testStart: { name }
 558      testStart: registerLoggingCallback( "testStart" ),
 559  
 560      // testDone: { name, failed, passed, total, runtime }
 561      testDone: registerLoggingCallback( "testDone" ),
 562  
 563      // moduleStart: { name }
 564      moduleStart: registerLoggingCallback( "moduleStart" ),
 565  
 566      // moduleDone: { name, failed, passed, total }
 567      moduleDone: registerLoggingCallback( "moduleDone" )
 568  });
 569  
 570  if ( !defined.document || document.readyState === "complete" ) {
 571      config.autorun = true;
 572  }
 573  
 574  QUnit.load = function() {
 575      runLoggingCallbacks( "begin", QUnit, {} );
 576  
 577      // Initialize the config, saving the execution queue
 578      var banner, filter, i, j, label, len, main, ol, toolbar, val, selection,
 579          urlConfigContainer, moduleFilter, userAgent,
 580          numModules = 0,
 581          moduleNames = [],
 582          moduleFilterHtml = "",
 583          urlConfigHtml = "",
 584          oldconfig = extend( {}, config );
 585  
 586      QUnit.init();
 587      extend(config, oldconfig);
 588  
 589      config.blocking = false;
 590  
 591      len = config.urlConfig.length;
 592  
 593      for ( i = 0; i < len; i++ ) {
 594          val = config.urlConfig[i];
 595          if ( typeof val === "string" ) {
 596              val = {
 597                  id: val,
 598                  label: val
 599              };
 600          }
 601          config[ val.id ] = QUnit.urlParams[ val.id ];
 602          if ( !val.value || typeof val.value === "string" ) {
 603              urlConfigHtml += "<input id='qunit-urlconfig-" + escapeText( val.id ) +
 604                  "' name='" + escapeText( val.id ) +
 605                  "' type='checkbox'" +
 606                  ( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
 607                  ( config[ val.id ] ? " checked='checked'" : "" ) +
 608                  " title='" + escapeText( val.tooltip ) +
 609                  "'><label for='qunit-urlconfig-" + escapeText( val.id ) +
 610                  "' title='" + escapeText( val.tooltip ) + "'>" + val.label + "</label>";
 611          } else {
 612              urlConfigHtml += "<label for='qunit-urlconfig-" + escapeText( val.id ) +
 613                  "' title='" + escapeText( val.tooltip ) +
 614                  "'>" + val.label +
 615                  ": </label><select id='qunit-urlconfig-" + escapeText( val.id ) +
 616                  "' name='" + escapeText( val.id ) +
 617                  "' title='" + escapeText( val.tooltip ) +
 618                  "'><option></option>";
 619              selection = false;
 620              if ( QUnit.is( "array", val.value ) ) {
 621                  for ( j = 0; j < val.value.length; j++ ) {
 622                      urlConfigHtml += "<option value='" + escapeText( val.value[j] ) + "'" +
 623                          ( config[ val.id ] === val.value[j] ?
 624                              (selection = true) && " selected='selected'" :
 625                              "" ) +
 626                          ">" + escapeText( val.value[j] ) + "</option>";
 627                  }
 628              } else {
 629                  for ( j in val.value ) {
 630                      if ( hasOwn.call( val.value, j ) ) {
 631                          urlConfigHtml += "<option value='" + escapeText( j ) + "'" +
 632                              ( config[ val.id ] === j ?
 633                                  (selection = true) && " selected='selected'" :
 634                                  "" ) +
 635                              ">" + escapeText( val.value[j] ) + "</option>";
 636                      }
 637                  }
 638              }
 639              if ( config[ val.id ] && !selection ) {
 640                  urlConfigHtml += "<option value='" + escapeText( config[ val.id ] ) +
 641                      "' selected='selected' disabled='disabled'>" +
 642                      escapeText( config[ val.id ] ) +
 643                      "</option>";
 644              }
 645              urlConfigHtml += "</select>";
 646          }
 647      }
 648      for ( i in config.modules ) {
 649          if ( config.modules.hasOwnProperty( i ) ) {
 650              moduleNames.push(i);
 651          }
 652      }
 653      numModules = moduleNames.length;
 654      moduleNames.sort( function( a, b ) {
 655          return a.localeCompare( b );
 656      });
 657      moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label><select id='qunit-modulefilter' name='modulefilter'><option value='' " +
 658          ( config.module === undefined  ? "selected='selected'" : "" ) +
 659          ">< All Modules ></option>";
 660  
 661  
 662      for ( i = 0; i < numModules; i++) {
 663              moduleFilterHtml += "<option value='" + escapeText( encodeURIComponent(moduleNames[i]) ) + "' " +
 664                  ( config.module === moduleNames[i] ? "selected='selected'" : "" ) +
 665                  ">" + escapeText(moduleNames[i]) + "</option>";
 666      }
 667      moduleFilterHtml += "</select>";
 668  
 669      // `userAgent` initialized at top of scope
 670      userAgent = id( "qunit-userAgent" );
 671      if ( userAgent ) {
 672          userAgent.innerHTML = navigator.userAgent;
 673      }
 674  
 675      // `banner` initialized at top of scope
 676      banner = id( "qunit-header" );
 677      if ( banner ) {
 678          banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) + "'>" + banner.innerHTML + "</a> ";
 679      }
 680  
 681      // `toolbar` initialized at top of scope
 682      toolbar = id( "qunit-testrunner-toolbar" );
 683      if ( toolbar ) {
 684          // `filter` initialized at top of scope
 685          filter = document.createElement( "input" );
 686          filter.type = "checkbox";
 687          filter.id = "qunit-filter-pass";
 688  
 689          addEvent( filter, "click", function() {
 690              var tmp,
 691                  ol = id( "qunit-tests" );
 692  
 693              if ( filter.checked ) {
 694                  ol.className = ol.className + " hidepass";
 695              } else {
 696                  tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
 697                  ol.className = tmp.replace( / hidepass /, " " );
 698              }
 699              if ( defined.sessionStorage ) {
 700                  if (filter.checked) {
 701                      sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
 702                  } else {
 703                      sessionStorage.removeItem( "qunit-filter-passed-tests" );
 704                  }
 705              }
 706          });
 707  
 708          if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
 709              filter.checked = true;
 710              // `ol` initialized at top of scope
 711              ol = id( "qunit-tests" );
 712              ol.className = ol.className + " hidepass";
 713          }
 714          toolbar.appendChild( filter );
 715  
 716          // `label` initialized at top of scope
 717          label = document.createElement( "label" );
 718          label.setAttribute( "for", "qunit-filter-pass" );
 719          label.setAttribute( "title", "Only show tests and assertions that fail. Stored in sessionStorage." );
 720          label.innerHTML = "Hide passed tests";
 721          toolbar.appendChild( label );
 722  
 723          urlConfigContainer = document.createElement("span");
 724          urlConfigContainer.innerHTML = urlConfigHtml;
 725          // For oldIE support:
 726          // * Add handlers to the individual elements instead of the container
 727          // * Use "click" instead of "change" for checkboxes
 728          // * Fallback from event.target to event.srcElement
 729          addEvents( urlConfigContainer.getElementsByTagName("input"), "click", function( event ) {
 730              var params = {},
 731                  target = event.target || event.srcElement;
 732              params[ target.name ] = target.checked ?
 733                  target.defaultValue || true :
 734                  undefined;
 735              window.location = QUnit.url( params );
 736          });
 737          addEvents( urlConfigContainer.getElementsByTagName("select"), "change", function( event ) {
 738              var params = {},
 739                  target = event.target || event.srcElement;
 740              params[ target.name ] = target.options[ target.selectedIndex ].value || undefined;
 741              window.location = QUnit.url( params );
 742          });
 743          toolbar.appendChild( urlConfigContainer );
 744  
 745          if (numModules > 1) {
 746              moduleFilter = document.createElement( "span" );
 747              moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
 748              moduleFilter.innerHTML = moduleFilterHtml;
 749              addEvent( moduleFilter.lastChild, "change", function() {
 750                  var selectBox = moduleFilter.getElementsByTagName("select")[0],
 751                      selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value);
 752  
 753                  window.location = QUnit.url({
 754                      module: ( selectedModule === "" ) ? undefined : selectedModule,
 755                      // Remove any existing filters
 756                      filter: undefined,
 757                      testNumber: undefined
 758                  });
 759              });
 760              toolbar.appendChild(moduleFilter);
 761          }
 762      }
 763  
 764      // `main` initialized at top of scope
 765      main = id( "qunit-fixture" );
 766      if ( main ) {
 767          config.fixture = main.innerHTML;
 768      }
 769  
 770      if ( config.autostart ) {
 771          QUnit.start();
 772      }
 773  };
 774  
 775  if ( defined.document ) {
 776      addEvent( window, "load", QUnit.load );
 777  }
 778  
 779  // `onErrorFnPrev` initialized at top of scope
 780  // Preserve other handlers
 781  onErrorFnPrev = window.onerror;
 782  
 783  // Cover uncaught exceptions
 784  // Returning true will suppress the default browser handler,
 785  // returning false will let it run.
 786  window.onerror = function ( error, filePath, linerNr ) {
 787      var ret = false;
 788      if ( onErrorFnPrev ) {
 789          ret = onErrorFnPrev( error, filePath, linerNr );
 790      }
 791  
 792      // Treat return value as window.onerror itself does,
 793      // Only do our handling if not suppressed.
 794      if ( ret !== true ) {
 795          if ( QUnit.config.current ) {
 796              if ( QUnit.config.current.ignoreGlobalErrors ) {
 797                  return true;
 798              }
 799              QUnit.pushFailure( error, filePath + ":" + linerNr );
 800          } else {
 801              QUnit.test( "global failure", extend( function() {
 802                  QUnit.pushFailure( error, filePath + ":" + linerNr );
 803              }, { validTest: validTest } ) );
 804          }
 805          return false;
 806      }
 807  
 808      return ret;
 809  };
 810  
 811  function done() {
 812      config.autorun = true;
 813  
 814      // Log the last module results
 815      if ( config.previousModule ) {
 816          runLoggingCallbacks( "moduleDone", QUnit, {
 817              name: config.previousModule,
 818              failed: config.moduleStats.bad,
 819              passed: config.moduleStats.all - config.moduleStats.bad,
 820              total: config.moduleStats.all
 821          });
 822      }
 823      delete config.previousModule;
 824  
 825      var i, key,
 826          banner = id( "qunit-banner" ),
 827          tests = id( "qunit-tests" ),
 828          runtime = +new Date() - config.started,
 829          passed = config.stats.all - config.stats.bad,
 830          html = [
 831              "Tests completed in ",
 832              runtime,
 833              " milliseconds.<br/>",
 834              "<span class='passed'>",
 835              passed,
 836              "</span> assertions of <span class='total'>",
 837              config.stats.all,
 838              "</span> passed, <span class='failed'>",
 839              config.stats.bad,
 840              "</span> failed."
 841          ].join( "" );
 842  
 843      if ( banner ) {
 844          banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );
 845      }
 846  
 847      if ( tests ) {
 848          id( "qunit-testresult" ).innerHTML = html;
 849      }
 850  
 851      if ( config.altertitle && defined.document && document.title ) {
 852          // show ✖ for good, ✔ for bad suite result in title
 853          // use escape sequences in case file gets loaded with non-utf-8-charset
 854          document.title = [
 855              ( config.stats.bad ? "\u2716" : "\u2714" ),
 856              document.title.replace( /^[\u2714\u2716] /i, "" )
 857          ].join( " " );
 858      }
 859  
 860      // clear own sessionStorage items if all tests passed
 861      if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {
 862          // `key` & `i` initialized at top of scope
 863          for ( i = 0; i < sessionStorage.length; i++ ) {
 864              key = sessionStorage.key( i++ );
 865              if ( key.indexOf( "qunit-test-" ) === 0 ) {
 866                  sessionStorage.removeItem( key );
 867              }
 868          }
 869      }
 870  
 871      // scroll back to top to show results
 872      if ( config.scrolltop && window.scrollTo ) {
 873          window.scrollTo(0, 0);
 874      }
 875  
 876      runLoggingCallbacks( "done", QUnit, {
 877          failed: config.stats.bad,
 878          passed: passed,
 879          total: config.stats.all,
 880          runtime: runtime
 881      });
 882  }
 883  
 884  /** @return Boolean: true if this test should be ran */
 885  function validTest( test ) {
 886      var include,
 887          filter = config.filter && config.filter.toLowerCase(),
 888          module = config.module && config.module.toLowerCase(),
 889          fullName = ( test.module + ": " + test.testName ).toLowerCase();
 890  
 891      // Internally-generated tests are always valid
 892      if ( test.callback && test.callback.validTest === validTest ) {
 893          delete test.callback.validTest;
 894          return true;
 895      }
 896  
 897      if ( config.testNumber.length > 0 ) {
 898          if ( inArray( test.testNumber, config.testNumber ) < 0 ) {
 899              return false;
 900          }
 901      }
 902  
 903      if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {
 904          return false;
 905      }
 906  
 907      if ( !filter ) {
 908          return true;
 909      }
 910  
 911      include = filter.charAt( 0 ) !== "!";
 912      if ( !include ) {
 913          filter = filter.slice( 1 );
 914      }
 915  
 916      // If the filter matches, we need to honour include
 917      if ( fullName.indexOf( filter ) !== -1 ) {
 918          return include;
 919      }
 920  
 921      // Otherwise, do the opposite
 922      return !include;
 923  }
 924  
 925  // so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)
 926  // Later Safari and IE10 are supposed to support error.stack as well
 927  // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
 928  function extractStacktrace( e, offset ) {
 929      offset = offset === undefined ? 3 : offset;
 930  
 931      var stack, include, i;
 932  
 933      if ( e.stacktrace ) {
 934          // Opera
 935          return e.stacktrace.split( "\n" )[ offset + 3 ];
 936      } else if ( e.stack ) {
 937          // Firefox, Chrome
 938          stack = e.stack.split( "\n" );
 939          if (/^error$/i.test( stack[0] ) ) {
 940              stack.shift();
 941          }
 942          if ( fileName ) {
 943              include = [];
 944              for ( i = offset; i < stack.length; i++ ) {
 945                  if ( stack[ i ].indexOf( fileName ) !== -1 ) {
 946                      break;
 947                  }
 948                  include.push( stack[ i ] );
 949              }
 950              if ( include.length ) {
 951                  return include.join( "\n" );
 952              }
 953          }
 954          return stack[ offset ];
 955      } else if ( e.sourceURL ) {
 956          // Safari, PhantomJS
 957          // hopefully one day Safari provides actual stacktraces
 958          // exclude useless self-reference for generated Error objects
 959          if ( /qunit.js$/.test( e.sourceURL ) ) {
 960              return;
 961          }
 962          // for actual exceptions, this is useful
 963          return e.sourceURL + ":" + e.line;
 964      }
 965  }
 966  function sourceFromStacktrace( offset ) {
 967      try {
 968          throw new Error();
 969      } catch ( e ) {
 970          return extractStacktrace( e, offset );
 971      }
 972  }
 973  
 974  /**
 975   * Escape text for attribute or text content.
 976   */
 977  function escapeText( s ) {
 978      if ( !s ) {
 979          return "";
 980      }
 981      s = s + "";
 982      // Both single quotes and double quotes (for attributes)
 983      return s.replace( /['"<>&]/g, function( s ) {
 984          switch( s ) {
 985              case "'":
 986                  return "&#039;";
 987              case "\"":
 988                  return "&quot;";
 989              case "<":
 990                  return "&lt;";
 991              case ">":
 992                  return "&gt;";
 993              case "&":
 994                  return "&amp;";
 995          }
 996      });
 997  }
 998  
 999  function synchronize( callback, last ) {
1000      config.queue.push( callback );
1001  
1002      if ( config.autorun && !config.blocking ) {
1003          process( last );
1004      }
1005  }
1006  
1007  function process( last ) {
1008  	function next() {
1009          process( last );
1010      }
1011      var start = new Date().getTime();
1012      config.depth = config.depth ? config.depth + 1 : 1;
1013  
1014      while ( config.queue.length && !config.blocking ) {
1015          if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
1016              config.queue.shift()();
1017          } else {
1018              setTimeout( next, 13 );
1019              break;
1020          }
1021      }
1022      config.depth--;
1023      if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
1024          done();
1025      }
1026  }
1027  
1028  function saveGlobal() {
1029      config.pollution = [];
1030  
1031      if ( config.noglobals ) {
1032          for ( var key in window ) {
1033              if ( hasOwn.call( window, key ) ) {
1034                  // in Opera sometimes DOM element ids show up here, ignore them
1035                  if ( /^qunit-test-output/.test( key ) ) {
1036                      continue;
1037                  }
1038                  config.pollution.push( key );
1039              }
1040          }
1041      }
1042  }
1043  
1044  function checkPollution() {
1045      var newGlobals,
1046          deletedGlobals,
1047          old = config.pollution;
1048  
1049      saveGlobal();
1050  
1051      newGlobals = diff( config.pollution, old );
1052      if ( newGlobals.length > 0 ) {
1053          QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );
1054      }
1055  
1056      deletedGlobals = diff( old, config.pollution );
1057      if ( deletedGlobals.length > 0 ) {
1058          QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );
1059      }
1060  }
1061  
1062  // returns a new Array with the elements that are in a but not in b
1063  function diff( a, b ) {
1064      var i, j,
1065          result = a.slice();
1066  
1067      for ( i = 0; i < result.length; i++ ) {
1068          for ( j = 0; j < b.length; j++ ) {
1069              if ( result[i] === b[j] ) {
1070                  result.splice( i, 1 );
1071                  i--;
1072                  break;
1073              }
1074          }
1075      }
1076      return result;
1077  }
1078  
1079  function extend( a, b ) {
1080      for ( var prop in b ) {
1081          if ( hasOwn.call( b, prop ) ) {
1082              // Avoid "Member not found" error in IE8 caused by messing with window.constructor
1083              if ( !( prop === "constructor" && a === window ) ) {
1084                  if ( b[ prop ] === undefined ) {
1085                      delete a[ prop ];
1086                  } else {
1087                      a[ prop ] = b[ prop ];
1088                  }
1089              }
1090          }
1091      }
1092  
1093      return a;
1094  }
1095  
1096  /**
1097   * @param {HTMLElement} elem
1098   * @param {string} type
1099   * @param {Function} fn
1100   */
1101  function addEvent( elem, type, fn ) {
1102      if ( elem.addEventListener ) {
1103  
1104          // Standards-based browsers
1105          elem.addEventListener( type, fn, false );
1106      } else if ( elem.attachEvent ) {
1107  
1108          // support: IE <9
1109          elem.attachEvent( "on" + type, fn );
1110      } else {
1111  
1112          // Caller must ensure support for event listeners is present
1113          throw new Error( "addEvent() was called in a context without event listener support" );
1114      }
1115  }
1116  
1117  /**
1118   * @param {Array|NodeList} elems
1119   * @param {string} type
1120   * @param {Function} fn
1121   */
1122  function addEvents( elems, type, fn ) {
1123      var i = elems.length;
1124      while ( i-- ) {
1125          addEvent( elems[i], type, fn );
1126      }
1127  }
1128  
1129  function hasClass( elem, name ) {
1130      return (" " + elem.className + " ").indexOf(" " + name + " ") > -1;
1131  }
1132  
1133  function addClass( elem, name ) {
1134      if ( !hasClass( elem, name ) ) {
1135          elem.className += (elem.className ? " " : "") + name;
1136      }
1137  }
1138  
1139  function removeClass( elem, name ) {
1140      var set = " " + elem.className + " ";
1141      // Class name may appear multiple times
1142      while ( set.indexOf(" " + name + " ") > -1 ) {
1143          set = set.replace(" " + name + " " , " ");
1144      }
1145      // If possible, trim it for prettiness, but not necessarily
1146      elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
1147  }
1148  
1149  function id( name ) {
1150      return defined.document && document.getElementById && document.getElementById( name );
1151  }
1152  
1153  function registerLoggingCallback( key ) {
1154      return function( callback ) {
1155          config[key].push( callback );
1156      };
1157  }
1158  
1159  // Supports deprecated method of completely overwriting logging callbacks
1160  function runLoggingCallbacks( key, scope, args ) {
1161      var i, callbacks;
1162      if ( QUnit.hasOwnProperty( key ) ) {
1163          QUnit[ key ].call(scope, args );
1164      } else {
1165          callbacks = config[ key ];
1166          for ( i = 0; i < callbacks.length; i++ ) {
1167              callbacks[ i ].call( scope, args );
1168          }
1169      }
1170  }
1171  
1172  // from jquery.js
1173  function inArray( elem, array ) {
1174      if ( array.indexOf ) {
1175          return array.indexOf( elem );
1176      }
1177  
1178      for ( var i = 0, length = array.length; i < length; i++ ) {
1179          if ( array[ i ] === elem ) {
1180              return i;
1181          }
1182      }
1183  
1184      return -1;
1185  }
1186  
1187  function Test( settings ) {
1188      extend( this, settings );
1189      this.assertions = [];
1190      this.testNumber = ++Test.count;
1191  }
1192  
1193  Test.count = 0;
1194  
1195  Test.prototype = {
1196      init: function() {
1197          var a, b, li,
1198              tests = id( "qunit-tests" );
1199  
1200          if ( tests ) {
1201              b = document.createElement( "strong" );
1202              b.innerHTML = this.nameHtml;
1203  
1204              // `a` initialized at top of scope
1205              a = document.createElement( "a" );
1206              a.innerHTML = "Rerun";
1207              a.href = QUnit.url({ testNumber: this.testNumber });
1208  
1209              li = document.createElement( "li" );
1210              li.appendChild( b );
1211              li.appendChild( a );
1212              li.className = "running";
1213              li.id = this.id = "qunit-test-output" + testId++;
1214  
1215              tests.appendChild( li );
1216          }
1217      },
1218      setup: function() {
1219          if (
1220              // Emit moduleStart when we're switching from one module to another
1221              this.module !== config.previousModule ||
1222                  // They could be equal (both undefined) but if the previousModule property doesn't
1223                  // yet exist it means this is the first test in a suite that isn't wrapped in a
1224                  // module, in which case we'll just emit a moduleStart event for 'undefined'.
1225                  // Without this, reporters can get testStart before moduleStart  which is a problem.
1226                  !hasOwn.call( config, "previousModule" )
1227          ) {
1228              if ( hasOwn.call( config, "previousModule" ) ) {
1229                  runLoggingCallbacks( "moduleDone", QUnit, {
1230                      name: config.previousModule,
1231                      failed: config.moduleStats.bad,
1232                      passed: config.moduleStats.all - config.moduleStats.bad,
1233                      total: config.moduleStats.all
1234                  });
1235              }
1236              config.previousModule = this.module;
1237              config.moduleStats = { all: 0, bad: 0 };
1238              runLoggingCallbacks( "moduleStart", QUnit, {
1239                  name: this.module
1240              });
1241          }
1242  
1243          config.current = this;
1244  
1245          this.testEnvironment = extend({
1246              setup: function() {},
1247              teardown: function() {}
1248          }, this.moduleTestEnvironment );
1249  
1250          this.started = +new Date();
1251          runLoggingCallbacks( "testStart", QUnit, {
1252              name: this.testName,
1253              module: this.module
1254          });
1255  
1256          /*jshint camelcase:false */
1257  
1258  
1259          /**
1260           * Expose the current test environment.
1261           *
1262           * @deprecated since 1.12.0: Use QUnit.config.current.testEnvironment instead.
1263           */
1264          QUnit.current_testEnvironment = this.testEnvironment;
1265  
1266          /*jshint camelcase:true */
1267  
1268          if ( !config.pollution ) {
1269              saveGlobal();
1270          }
1271          if ( config.notrycatch ) {
1272              this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
1273              return;
1274          }
1275          try {
1276              this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
1277          } catch( e ) {
1278              QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
1279          }
1280      },
1281      run: function() {
1282          config.current = this;
1283  
1284          var running = id( "qunit-testresult" );
1285  
1286          if ( running ) {
1287              running.innerHTML = "Running: <br/>" + this.nameHtml;
1288          }
1289  
1290          if ( this.async ) {
1291              QUnit.stop();
1292          }
1293  
1294          this.callbackStarted = +new Date();
1295  
1296          if ( config.notrycatch ) {
1297              this.callback.call( this.testEnvironment, QUnit.assert );
1298              this.callbackRuntime = +new Date() - this.callbackStarted;
1299              return;
1300          }
1301  
1302          try {
1303              this.callback.call( this.testEnvironment, QUnit.assert );
1304              this.callbackRuntime = +new Date() - this.callbackStarted;
1305          } catch( e ) {
1306              this.callbackRuntime = +new Date() - this.callbackStarted;
1307  
1308              QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
1309              // else next test will carry the responsibility
1310              saveGlobal();
1311  
1312              // Restart the tests if they're blocking
1313              if ( config.blocking ) {
1314                  QUnit.start();
1315              }
1316          }
1317      },
1318      teardown: function() {
1319          config.current = this;
1320          if ( config.notrycatch ) {
1321              if ( typeof this.callbackRuntime === "undefined" ) {
1322                  this.callbackRuntime = +new Date() - this.callbackStarted;
1323              }
1324              this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );
1325              return;
1326          } else {
1327              try {
1328                  this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );
1329              } catch( e ) {
1330                  QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
1331              }
1332          }
1333          checkPollution();
1334      },
1335      finish: function() {
1336          config.current = this;
1337          if ( config.requireExpects && this.expected === null ) {
1338              QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
1339          } else if ( this.expected !== null && this.expected !== this.assertions.length ) {
1340              QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
1341          } else if ( this.expected === null && !this.assertions.length ) {
1342              QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
1343          }
1344  
1345          var i, assertion, a, b, time, li, ol,
1346              test = this,
1347              good = 0,
1348              bad = 0,
1349              tests = id( "qunit-tests" );
1350  
1351          this.runtime = +new Date() - this.started;
1352          config.stats.all += this.assertions.length;
1353          config.moduleStats.all += this.assertions.length;
1354  
1355          if ( tests ) {
1356              ol = document.createElement( "ol" );
1357              ol.className = "qunit-assert-list";
1358  
1359              for ( i = 0; i < this.assertions.length; i++ ) {
1360                  assertion = this.assertions[i];
1361  
1362                  li = document.createElement( "li" );
1363                  li.className = assertion.result ? "pass" : "fail";
1364                  li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );
1365                  ol.appendChild( li );
1366  
1367                  if ( assertion.result ) {
1368                      good++;
1369                  } else {
1370                      bad++;
1371                      config.stats.bad++;
1372                      config.moduleStats.bad++;
1373                  }
1374              }
1375  
1376              // store result when possible
1377              if ( QUnit.config.reorder && defined.sessionStorage ) {
1378                  if ( bad ) {
1379                      sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );
1380                  } else {
1381                      sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );
1382                  }
1383              }
1384  
1385              if ( bad === 0 ) {
1386                  addClass( ol, "qunit-collapsed" );
1387              }
1388  
1389              // `b` initialized at top of scope
1390              b = document.createElement( "strong" );
1391              b.innerHTML = this.nameHtml + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
1392  
1393              addEvent(b, "click", function() {
1394                  var next = b.parentNode.lastChild,
1395                      collapsed = hasClass( next, "qunit-collapsed" );
1396                  ( collapsed ? removeClass : addClass )( next, "qunit-collapsed" );
1397              });
1398  
1399              addEvent(b, "dblclick", function( e ) {
1400                  var target = e && e.target ? e.target : window.event.srcElement;
1401                  if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) {
1402                      target = target.parentNode;
1403                  }
1404                  if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
1405                      window.location = QUnit.url({ testNumber: test.testNumber });
1406                  }
1407              });
1408  
1409              // `time` initialized at top of scope
1410              time = document.createElement( "span" );
1411              time.className = "runtime";
1412              time.innerHTML = this.runtime + " ms";
1413  
1414              // `li` initialized at top of scope
1415              li = id( this.id );
1416              li.className = bad ? "fail" : "pass";
1417              li.removeChild( li.firstChild );
1418              a = li.firstChild;
1419              li.appendChild( b );
1420              li.appendChild( a );
1421              li.appendChild( time );
1422              li.appendChild( ol );
1423  
1424          } else {
1425              for ( i = 0; i < this.assertions.length; i++ ) {
1426                  if ( !this.assertions[i].result ) {
1427                      bad++;
1428                      config.stats.bad++;
1429                      config.moduleStats.bad++;
1430                  }
1431              }
1432          }
1433  
1434          runLoggingCallbacks( "testDone", QUnit, {
1435              name: this.testName,
1436              module: this.module,
1437              failed: bad,
1438              passed: this.assertions.length - bad,
1439              total: this.assertions.length,
1440              runtime: this.runtime,
1441              // DEPRECATED: this property will be removed in 2.0.0, use runtime instead
1442              duration: this.runtime
1443          });
1444  
1445          QUnit.reset();
1446  
1447          config.current = undefined;
1448      },
1449  
1450      queue: function() {
1451          var bad,
1452              test = this;
1453  
1454          synchronize(function() {
1455              test.init();
1456          });
1457  		function run() {
1458              // each of these can by async
1459              synchronize(function() {
1460                  test.setup();
1461              });
1462              synchronize(function() {
1463                  test.run();
1464              });
1465              synchronize(function() {
1466                  test.teardown();
1467              });
1468              synchronize(function() {
1469                  test.finish();
1470              });
1471          }
1472  
1473          // `bad` initialized at top of scope
1474          // defer when previous test run passed, if storage is available
1475          bad = QUnit.config.reorder && defined.sessionStorage &&
1476                          +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
1477  
1478          if ( bad ) {
1479              run();
1480          } else {
1481              synchronize( run, true );
1482          }
1483      }
1484  };
1485  
1486  // `assert` initialized at top of scope
1487  // Assert helpers
1488  // All of these must either call QUnit.push() or manually do:
1489  // - runLoggingCallbacks( "log", .. );
1490  // - config.current.assertions.push({ .. });
1491  assert = QUnit.assert = {
1492      /**
1493       * Asserts rough true-ish result.
1494       * @name ok
1495       * @function
1496       * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
1497       */
1498      ok: function( result, msg ) {
1499          if ( !config.current ) {
1500              throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
1501          }
1502          result = !!result;
1503          msg = msg || ( result ? "okay" : "failed" );
1504  
1505          var source,
1506              details = {
1507                  module: config.current.module,
1508                  name: config.current.testName,
1509                  result: result,
1510                  message: msg
1511              };
1512  
1513          msg = "<span class='test-message'>" + escapeText( msg ) + "</span>";
1514  
1515          if ( !result ) {
1516              source = sourceFromStacktrace( 2 );
1517              if ( source ) {
1518                  details.source = source;
1519                  msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" +
1520                      escapeText( source ) +
1521                      "</pre></td></tr></table>";
1522              }
1523          }
1524          runLoggingCallbacks( "log", QUnit, details );
1525          config.current.assertions.push({
1526              result: result,
1527              message: msg
1528          });
1529      },
1530  
1531      /**
1532       * Assert that the first two arguments are equal, with an optional message.
1533       * Prints out both actual and expected values.
1534       * @name equal
1535       * @function
1536       * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
1537       */
1538      equal: function( actual, expected, message ) {
1539          /*jshint eqeqeq:false */
1540          QUnit.push( expected == actual, actual, expected, message );
1541      },
1542  
1543      /**
1544       * @name notEqual
1545       * @function
1546       */
1547      notEqual: function( actual, expected, message ) {
1548          /*jshint eqeqeq:false */
1549          QUnit.push( expected != actual, actual, expected, message );
1550      },
1551  
1552      /**
1553       * @name propEqual
1554       * @function
1555       */
1556      propEqual: function( actual, expected, message ) {
1557          actual = objectValues(actual);
1558          expected = objectValues(expected);
1559          QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
1560      },
1561  
1562      /**
1563       * @name notPropEqual
1564       * @function
1565       */
1566      notPropEqual: function( actual, expected, message ) {
1567          actual = objectValues(actual);
1568          expected = objectValues(expected);
1569          QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
1570      },
1571  
1572      /**
1573       * @name deepEqual
1574       * @function
1575       */
1576      deepEqual: function( actual, expected, message ) {
1577          QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
1578      },
1579  
1580      /**
1581       * @name notDeepEqual
1582       * @function
1583       */
1584      notDeepEqual: function( actual, expected, message ) {
1585          QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
1586      },
1587  
1588      /**
1589       * @name strictEqual
1590       * @function
1591       */
1592      strictEqual: function( actual, expected, message ) {
1593          QUnit.push( expected === actual, actual, expected, message );
1594      },
1595  
1596      /**
1597       * @name notStrictEqual
1598       * @function
1599       */
1600      notStrictEqual: function( actual, expected, message ) {
1601          QUnit.push( expected !== actual, actual, expected, message );
1602      },
1603  
1604      "throws": function( block, expected, message ) {
1605          var actual,
1606              expectedOutput = expected,
1607              ok = false;
1608  
1609          // 'expected' is optional
1610          if ( !message && typeof expected === "string" ) {
1611              message = expected;
1612              expected = null;
1613          }
1614  
1615          config.current.ignoreGlobalErrors = true;
1616          try {
1617              block.call( config.current.testEnvironment );
1618          } catch (e) {
1619              actual = e;
1620          }
1621          config.current.ignoreGlobalErrors = false;
1622  
1623          if ( actual ) {
1624  
1625              // we don't want to validate thrown error
1626              if ( !expected ) {
1627                  ok = true;
1628                  expectedOutput = null;
1629  
1630              // expected is an Error object
1631              } else if ( expected instanceof Error ) {
1632                  ok = actual instanceof Error &&
1633                       actual.name === expected.name &&
1634                       actual.message === expected.message;
1635  
1636              // expected is a regexp
1637              } else if ( QUnit.objectType( expected ) === "regexp" ) {
1638                  ok = expected.test( errorString( actual ) );
1639  
1640              // expected is a string
1641              } else if ( QUnit.objectType( expected ) === "string" ) {
1642                  ok = expected === errorString( actual );
1643  
1644              // expected is a constructor
1645              } else if ( actual instanceof expected ) {
1646                  ok = true;
1647  
1648              // expected is a validation function which returns true is validation passed
1649              } else if ( expected.call( {}, actual ) === true ) {
1650                  expectedOutput = null;
1651                  ok = true;
1652              }
1653  
1654              QUnit.push( ok, actual, expectedOutput, message );
1655          } else {
1656              QUnit.pushFailure( message, null, "No exception was thrown." );
1657          }
1658      }
1659  };
1660  
1661  /**
1662   * @deprecated since 1.8.0
1663   * Kept assertion helpers in root for backwards compatibility.
1664   */
1665  extend( QUnit.constructor.prototype, assert );
1666  
1667  /**
1668   * @deprecated since 1.9.0
1669   * Kept to avoid TypeErrors for undefined methods.
1670   */
1671  QUnit.constructor.prototype.raises = function() {
1672      QUnit.push( false, false, false, "QUnit.raises has been deprecated since 2012 (fad3c1ea), use QUnit.throws instead" );
1673  };
1674  
1675  /**
1676   * @deprecated since 1.0.0, replaced with error pushes since 1.3.0
1677   * Kept to avoid TypeErrors for undefined methods.
1678   */
1679  QUnit.constructor.prototype.equals = function() {
1680      QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
1681  };
1682  QUnit.constructor.prototype.same = function() {
1683      QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
1684  };
1685  
1686  // Test for equality any JavaScript type.
1687  // Author: Philippe Rathé <[email protected]>
1688  QUnit.equiv = (function() {
1689  
1690      // Call the o related callback with the given arguments.
1691  	function bindCallbacks( o, callbacks, args ) {
1692          var prop = QUnit.objectType( o );
1693          if ( prop ) {
1694              if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
1695                  return callbacks[ prop ].apply( callbacks, args );
1696              } else {
1697                  return callbacks[ prop ]; // or undefined
1698              }
1699          }
1700      }
1701  
1702      // the real equiv function
1703      var innerEquiv,
1704          // stack to decide between skip/abort functions
1705          callers = [],
1706          // stack to avoiding loops from circular referencing
1707          parents = [],
1708          parentsB = [],
1709  
1710          getProto = Object.getPrototypeOf || function ( obj ) {
1711              /*jshint camelcase:false */
1712              return obj.__proto__;
1713          },
1714          callbacks = (function () {
1715  
1716              // for string, boolean, number and null
1717  			function useStrictEquality( b, a ) {
1718                  /*jshint eqeqeq:false */
1719                  if ( b instanceof a.constructor || a instanceof b.constructor ) {
1720                      // to catch short annotation VS 'new' annotation of a
1721                      // declaration
1722                      // e.g. var i = 1;
1723                      // var j = new Number(1);
1724                      return a == b;
1725                  } else {
1726                      return a === b;
1727                  }
1728              }
1729  
1730              return {
1731                  "string": useStrictEquality,
1732                  "boolean": useStrictEquality,
1733                  "number": useStrictEquality,
1734                  "null": useStrictEquality,
1735                  "undefined": useStrictEquality,
1736  
1737                  "nan": function( b ) {
1738                      return isNaN( b );
1739                  },
1740  
1741                  "date": function( b, a ) {
1742                      return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
1743                  },
1744  
1745                  "regexp": function( b, a ) {
1746                      return QUnit.objectType( b ) === "regexp" &&
1747                          // the regex itself
1748                          a.source === b.source &&
1749                          // and its modifiers
1750                          a.global === b.global &&
1751                          // (gmi) ...
1752                          a.ignoreCase === b.ignoreCase &&
1753                          a.multiline === b.multiline &&
1754                          a.sticky === b.sticky;
1755                  },
1756  
1757                  // - skip when the property is a method of an instance (OOP)
1758                  // - abort otherwise,
1759                  // initial === would have catch identical references anyway
1760                  "function": function() {
1761                      var caller = callers[callers.length - 1];
1762                      return caller !== Object && typeof caller !== "undefined";
1763                  },
1764  
1765                  "array": function( b, a ) {
1766                      var i, j, len, loop, aCircular, bCircular;
1767  
1768                      // b could be an object literal here
1769                      if ( QUnit.objectType( b ) !== "array" ) {
1770                          return false;
1771                      }
1772  
1773                      len = a.length;
1774                      if ( len !== b.length ) {
1775                          // safe and faster
1776                          return false;
1777                      }
1778  
1779                      // track reference to avoid circular references
1780                      parents.push( a );
1781                      parentsB.push( b );
1782                      for ( i = 0; i < len; i++ ) {
1783                          loop = false;
1784                          for ( j = 0; j < parents.length; j++ ) {
1785                              aCircular = parents[j] === a[i];
1786                              bCircular = parentsB[j] === b[i];
1787                              if ( aCircular || bCircular ) {
1788                                  if ( a[i] === b[i] || aCircular && bCircular ) {
1789                                      loop = true;
1790                                  } else {
1791                                      parents.pop();
1792                                      parentsB.pop();
1793                                      return false;
1794                                  }
1795                              }
1796                          }
1797                          if ( !loop && !innerEquiv(a[i], b[i]) ) {
1798                              parents.pop();
1799                              parentsB.pop();
1800                              return false;
1801                          }
1802                      }
1803                      parents.pop();
1804                      parentsB.pop();
1805                      return true;
1806                  },
1807  
1808                  "object": function( b, a ) {
1809                      /*jshint forin:false */
1810                      var i, j, loop, aCircular, bCircular,
1811                          // Default to true
1812                          eq = true,
1813                          aProperties = [],
1814                          bProperties = [];
1815  
1816                      // comparing constructors is more strict than using
1817                      // instanceof
1818                      if ( a.constructor !== b.constructor ) {
1819                          // Allow objects with no prototype to be equivalent to
1820                          // objects with Object as their constructor.
1821                          if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||
1822                              ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {
1823                                  return false;
1824                          }
1825                      }
1826  
1827                      // stack constructor before traversing properties
1828                      callers.push( a.constructor );
1829  
1830                      // track reference to avoid circular references
1831                      parents.push( a );
1832                      parentsB.push( b );
1833  
1834                      // be strict: don't ensure hasOwnProperty and go deep
1835                      for ( i in a ) {
1836                          loop = false;
1837                          for ( j = 0; j < parents.length; j++ ) {
1838                              aCircular = parents[j] === a[i];
1839                              bCircular = parentsB[j] === b[i];
1840                              if ( aCircular || bCircular ) {
1841                                  if ( a[i] === b[i] || aCircular && bCircular ) {
1842                                      loop = true;
1843                                  } else {
1844                                      eq = false;
1845                                      break;
1846                                  }
1847                              }
1848                          }
1849                          aProperties.push(i);
1850                          if ( !loop && !innerEquiv(a[i], b[i]) ) {
1851                              eq = false;
1852                              break;
1853                          }
1854                      }
1855  
1856                      parents.pop();
1857                      parentsB.pop();
1858                      callers.pop(); // unstack, we are done
1859  
1860                      for ( i in b ) {
1861                          bProperties.push( i ); // collect b's properties
1862                      }
1863  
1864                      // Ensures identical properties name
1865                      return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
1866                  }
1867              };
1868          }());
1869  
1870      innerEquiv = function() { // can take multiple arguments
1871          var args = [].slice.apply( arguments );
1872          if ( args.length < 2 ) {
1873              return true; // end transition
1874          }
1875  
1876          return (function( a, b ) {
1877              if ( a === b ) {
1878                  return true; // catch the most you can
1879              } else if ( a === null || b === null || typeof a === "undefined" ||
1880                      typeof b === "undefined" ||
1881                      QUnit.objectType(a) !== QUnit.objectType(b) ) {
1882                  return false; // don't lose time with error prone cases
1883              } else {
1884                  return bindCallbacks(a, callbacks, [ b, a ]);
1885              }
1886  
1887              // apply transition with (1..n) arguments
1888          }( args[0], args[1] ) && innerEquiv.apply( this, args.splice(1, args.length - 1 )) );
1889      };
1890  
1891      return innerEquiv;
1892  }());
1893  
1894  /**
1895   * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
1896   * http://flesler.blogspot.com Licensed under BSD
1897   * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
1898   *
1899   * @projectDescription Advanced and extensible data dumping for Javascript.
1900   * @version 1.0.0
1901   * @author Ariel Flesler
1902   * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
1903   */
1904  QUnit.jsDump = (function() {
1905  	function quote( str ) {
1906          return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";
1907      }
1908  	function literal( o ) {
1909          return o + "";
1910      }
1911  	function join( pre, arr, post ) {
1912          var s = jsDump.separator(),
1913              base = jsDump.indent(),
1914              inner = jsDump.indent(1);
1915          if ( arr.join ) {
1916              arr = arr.join( "," + s + inner );
1917          }
1918          if ( !arr ) {
1919              return pre + post;
1920          }
1921          return [ pre, inner + arr, base + post ].join(s);
1922      }
1923  	function array( arr, stack ) {
1924          var i = arr.length, ret = new Array(i);
1925          this.up();
1926          while ( i-- ) {
1927              ret[i] = this.parse( arr[i] , undefined , stack);
1928          }
1929          this.down();
1930          return join( "[", ret, "]" );
1931      }
1932  
1933      var reName = /^function (\w+)/,
1934          jsDump = {
1935              // type is used mostly internally, you can fix a (custom)type in advance
1936              parse: function( obj, type, stack ) {
1937                  stack = stack || [ ];
1938                  var inStack, res,
1939                      parser = this.parsers[ type || this.typeOf(obj) ];
1940  
1941                  type = typeof parser;
1942                  inStack = inArray( obj, stack );
1943  
1944                  if ( inStack !== -1 ) {
1945                      return "recursion(" + (inStack - stack.length) + ")";
1946                  }
1947                  if ( type === "function" )  {
1948                      stack.push( obj );
1949                      res = parser.call( this, obj, stack );
1950                      stack.pop();
1951                      return res;
1952                  }
1953                  return ( type === "string" ) ? parser : this.parsers.error;
1954              },
1955              typeOf: function( obj ) {
1956                  var type;
1957                  if ( obj === null ) {
1958                      type = "null";
1959                  } else if ( typeof obj === "undefined" ) {
1960                      type = "undefined";
1961                  } else if ( QUnit.is( "regexp", obj) ) {
1962                      type = "regexp";
1963                  } else if ( QUnit.is( "date", obj) ) {
1964                      type = "date";
1965                  } else if ( QUnit.is( "function", obj) ) {
1966                      type = "function";
1967                  } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
1968                      type = "window";
1969                  } else if ( obj.nodeType === 9 ) {
1970                      type = "document";
1971                  } else if ( obj.nodeType ) {
1972                      type = "node";
1973                  } else if (
1974                      // native arrays
1975                      toString.call( obj ) === "[object Array]" ||
1976                      // NodeList objects
1977                      ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
1978                  ) {
1979                      type = "array";
1980                  } else if ( obj.constructor === Error.prototype.constructor ) {
1981                      type = "error";
1982                  } else {
1983                      type = typeof obj;
1984                  }
1985                  return type;
1986              },
1987              separator: function() {
1988                  return this.multiline ?    this.HTML ? "<br />" : "\n" : this.HTML ? "&nbsp;" : " ";
1989              },
1990              // extra can be a number, shortcut for increasing-calling-decreasing
1991              indent: function( extra ) {
1992                  if ( !this.multiline ) {
1993                      return "";
1994                  }
1995                  var chr = this.indentChar;
1996                  if ( this.HTML ) {
1997                      chr = chr.replace( /\t/g, "   " ).replace( / /g, "&nbsp;" );
1998                  }
1999                  return new Array( this.depth + ( extra || 0 ) ).join(chr);
2000              },
2001              up: function( a ) {
2002                  this.depth += a || 1;
2003              },
2004              down: function( a ) {
2005                  this.depth -= a || 1;
2006              },
2007              setParser: function( name, parser ) {
2008                  this.parsers[name] = parser;
2009              },
2010              // The next 3 are exposed so you can use them
2011              quote: quote,
2012              literal: literal,
2013              join: join,
2014              //
2015              depth: 1,
2016              // This is the list of parsers, to modify them, use jsDump.setParser
2017              parsers: {
2018                  window: "[Window]",
2019                  document: "[Document]",
2020                  error: function(error) {
2021                      return "Error(\"" + error.message + "\")";
2022                  },
2023                  unknown: "[Unknown]",
2024                  "null": "null",
2025                  "undefined": "undefined",
2026                  "function": function( fn ) {
2027                      var ret = "function",
2028                          // functions never have name in IE
2029                          name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
2030  
2031                      if ( name ) {
2032                          ret += " " + name;
2033                      }
2034                      ret += "( ";
2035  
2036                      ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" );
2037                      return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" );
2038                  },
2039                  array: array,
2040                  nodelist: array,
2041                  "arguments": array,
2042                  object: function( map, stack ) {
2043                      /*jshint forin:false */
2044                      var ret = [ ], keys, key, val, i;
2045                      QUnit.jsDump.up();
2046                      keys = [];
2047                      for ( key in map ) {
2048                          keys.push( key );
2049                      }
2050                      keys.sort();
2051                      for ( i = 0; i < keys.length; i++ ) {
2052                          key = keys[ i ];
2053                          val = map[ key ];
2054                          ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) );
2055                      }
2056                      QUnit.jsDump.down();
2057                      return join( "{", ret, "}" );
2058                  },
2059                  node: function( node ) {
2060                      var len, i, val,
2061                          open = QUnit.jsDump.HTML ? "&lt;" : "<",
2062                          close = QUnit.jsDump.HTML ? "&gt;" : ">",
2063                          tag = node.nodeName.toLowerCase(),
2064                          ret = open + tag,
2065                          attrs = node.attributes;
2066  
2067                      if ( attrs ) {
2068                          for ( i = 0, len = attrs.length; i < len; i++ ) {
2069                              val = attrs[i].nodeValue;
2070                              // IE6 includes all attributes in .attributes, even ones not explicitly set.
2071                              // Those have values like undefined, null, 0, false, "" or "inherit".
2072                              if ( val && val !== "inherit" ) {
2073                                  ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" );
2074                              }
2075                          }
2076                      }
2077                      ret += close;
2078  
2079                      // Show content of TextNode or CDATASection
2080                      if ( node.nodeType === 3 || node.nodeType === 4 ) {
2081                          ret += node.nodeValue;
2082                      }
2083  
2084                      return ret + open + "/" + tag + close;
2085                  },
2086                  // function calls it internally, it's the arguments part of the function
2087                  functionArgs: function( fn ) {
2088                      var args,
2089                          l = fn.length;
2090  
2091                      if ( !l ) {
2092                          return "";
2093                      }
2094  
2095                      args = new Array(l);
2096                      while ( l-- ) {
2097                          // 97 is 'a'
2098                          args[l] = String.fromCharCode(97+l);
2099                      }
2100                      return " " + args.join( ", " ) + " ";
2101                  },
2102                  // object calls it internally, the key part of an item in a map
2103                  key: quote,
2104                  // function calls it internally, it's the content of the function
2105                  functionCode: "[code]",
2106                  // node calls it internally, it's an html attribute value
2107                  attribute: quote,
2108                  string: quote,
2109                  date: quote,
2110                  regexp: literal,
2111                  number: literal,
2112                  "boolean": literal
2113              },
2114              // if true, entities are escaped ( <, >, \t, space and \n )
2115              HTML: false,
2116              // indentation unit
2117              indentChar: "  ",
2118              // if true, items in a collection, are separated by a \n, else just a space.
2119              multiline: true
2120          };
2121  
2122      return jsDump;
2123  }());
2124  
2125  /*
2126   * Javascript Diff Algorithm
2127   *  By John Resig (http://ejohn.org/)
2128   *  Modified by Chu Alan "sprite"
2129   *
2130   * Released under the MIT license.
2131   *
2132   * More Info:
2133   *  http://ejohn.org/projects/javascript-diff-algorithm/
2134   *
2135   * Usage: QUnit.diff(expected, actual)
2136   *
2137   * 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"
2138   */
2139  QUnit.diff = (function() {
2140      /*jshint eqeqeq:false, eqnull:true */
2141  	function diff( o, n ) {
2142          var i,
2143              ns = {},
2144              os = {};
2145  
2146          for ( i = 0; i < n.length; i++ ) {
2147              if ( !hasOwn.call( ns, n[i] ) ) {
2148                  ns[ n[i] ] = {
2149                      rows: [],
2150                      o: null
2151                  };
2152              }
2153              ns[ n[i] ].rows.push( i );
2154          }
2155  
2156          for ( i = 0; i < o.length; i++ ) {
2157              if ( !hasOwn.call( os, o[i] ) ) {
2158                  os[ o[i] ] = {
2159                      rows: [],
2160                      n: null
2161                  };
2162              }
2163              os[ o[i] ].rows.push( i );
2164          }
2165  
2166          for ( i in ns ) {
2167              if ( hasOwn.call( ns, i ) ) {
2168                  if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) {
2169                      n[ ns[i].rows[0] ] = {
2170                          text: n[ ns[i].rows[0] ],
2171                          row: os[i].rows[0]
2172                      };
2173                      o[ os[i].rows[0] ] = {
2174                          text: o[ os[i].rows[0] ],
2175                          row: ns[i].rows[0]
2176                      };
2177                  }
2178              }
2179          }
2180  
2181          for ( i = 0; i < n.length - 1; i++ ) {
2182              if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
2183                          n[ i + 1 ] == o[ n[i].row + 1 ] ) {
2184  
2185                  n[ i + 1 ] = {
2186                      text: n[ i + 1 ],
2187                      row: n[i].row + 1
2188                  };
2189                  o[ n[i].row + 1 ] = {
2190                      text: o[ n[i].row + 1 ],
2191                      row: i + 1
2192                  };
2193              }
2194          }
2195  
2196          for ( i = n.length - 1; i > 0; i-- ) {
2197              if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
2198                          n[ i - 1 ] == o[ n[i].row - 1 ]) {
2199  
2200                  n[ i - 1 ] = {
2201                      text: n[ i - 1 ],
2202                      row: n[i].row - 1
2203                  };
2204                  o[ n[i].row - 1 ] = {
2205                      text: o[ n[i].row - 1 ],
2206                      row: i - 1
2207                  };
2208              }
2209          }
2210  
2211          return {
2212              o: o,
2213              n: n
2214          };
2215      }
2216  
2217      return function( o, n ) {
2218          o = o.replace( /\s+$/, "" );
2219          n = n.replace( /\s+$/, "" );
2220  
2221          var i, pre,
2222              str = "",
2223              out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),
2224              oSpace = o.match(/\s+/g),
2225              nSpace = n.match(/\s+/g);
2226  
2227          if ( oSpace == null ) {
2228              oSpace = [ " " ];
2229          }
2230          else {
2231              oSpace.push( " " );
2232          }
2233  
2234          if ( nSpace == null ) {
2235              nSpace = [ " " ];
2236          }
2237          else {
2238              nSpace.push( " " );
2239          }
2240  
2241          if ( out.n.length === 0 ) {
2242              for ( i = 0; i < out.o.length; i++ ) {
2243                  str += "<del>" + out.o[i] + oSpace[i] + "</del>";
2244              }
2245          }
2246          else {
2247              if ( out.n[0].text == null ) {
2248                  for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {
2249                      str += "<del>" + out.o[n] + oSpace[n] + "</del>";
2250                  }
2251              }
2252  
2253              for ( i = 0; i < out.n.length; i++ ) {
2254                  if (out.n[i].text == null) {
2255                      str += "<ins>" + out.n[i] + nSpace[i] + "</ins>";
2256                  }
2257                  else {
2258                      // `pre` initialized at top of scope
2259                      pre = "";
2260  
2261                      for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
2262                          pre += "<del>" + out.o[n] + oSpace[n] + "</del>";
2263                      }
2264                      str += " " + out.n[i].text + nSpace[i] + pre;
2265                  }
2266              }
2267          }
2268  
2269          return str;
2270      };
2271  }());
2272  
2273  // For browser, export only select globals
2274  if ( typeof window !== "undefined" ) {
2275      extend( window, QUnit.constructor.prototype );
2276      window.QUnit = QUnit;
2277  }
2278  
2279  // For CommonJS environments, export everything
2280  if ( typeof module !== "undefined" && module.exports ) {
2281      module.exports = QUnit;
2282  }
2283  
2284  
2285  // Get a reference to the global object, like window in browsers
2286  }( (function() {
2287      return this;
2288  })() ));


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1