[ Index ]

PHP Cross Reference of vtigercrm-6.1.0

title

Body

[close]

/libraries/bootstrap/js/tests/vendor/ -> qunit.js (source)

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


Generated: Fri Nov 28 20:08:37 2014 Cross-referenced by PHPXref 0.7.1