| LEFT | RIGHT | 
|---|
| (no file at all) |  | 
|  | 1 /*! | 
|  | 2  * QUnit 1.17.1 | 
|  | 3  * http://qunitjs.com/ | 
|  | 4  * | 
|  | 5  * Copyright jQuery Foundation and other contributors | 
|  | 6  * Released under the MIT license | 
|  | 7  * http://jquery.org/license | 
|  | 8  * | 
|  | 9  * Date: 2015-01-20T19:39Z | 
|  | 10  */ | 
|  | 11 | 
|  | 12 (function( window ) { | 
|  | 13 | 
|  | 14 var QUnit, | 
|  | 15         config, | 
|  | 16         onErrorFnPrev, | 
|  | 17         loggingCallbacks = {}, | 
|  | 18         fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, ""
       ).replace( /.+\//, "" ), | 
|  | 19         toString = Object.prototype.toString, | 
|  | 20         hasOwn = Object.prototype.hasOwnProperty, | 
|  | 21         // Keep a local reference to Date (GH-283) | 
|  | 22         Date = window.Date, | 
|  | 23         now = Date.now || function() { | 
|  | 24                 return new Date().getTime(); | 
|  | 25         }, | 
|  | 26         globalStartCalled = false, | 
|  | 27         runStarted = false, | 
|  | 28         setTimeout = window.setTimeout, | 
|  | 29         clearTimeout = window.clearTimeout, | 
|  | 30         defined = { | 
|  | 31                 document: window.document !== undefined, | 
|  | 32                 setTimeout: window.setTimeout !== undefined, | 
|  | 33                 sessionStorage: (function() { | 
|  | 34                         var x = "qunit-test-string"; | 
|  | 35                         try { | 
|  | 36                                 sessionStorage.setItem( x, x ); | 
|  | 37                                 sessionStorage.removeItem( x ); | 
|  | 38                                 return true; | 
|  | 39                         } catch ( e ) { | 
|  | 40                                 return false; | 
|  | 41                         } | 
|  | 42                 }()) | 
|  | 43         }, | 
|  | 44         /** | 
|  | 45          * Provides a normalized error string, correcting an issue | 
|  | 46          * with IE 7 (and prior) where Error.prototype.toString is | 
|  | 47          * not properly implemented | 
|  | 48          * | 
|  | 49          * Based on http://es5.github.com/#x15.11.4.4 | 
|  | 50          * | 
|  | 51          * @param {String|Error} error | 
|  | 52          * @return {String} error message | 
|  | 53          */ | 
|  | 54         errorString = function( error ) { | 
|  | 55                 var name, message, | 
|  | 56                         errorString = error.toString(); | 
|  | 57                 if ( errorString.substring( 0, 7 ) === "[object" ) { | 
|  | 58                         name = error.name ? error.name.toString() : "Error"; | 
|  | 59                         message = error.message ? error.message.toString() : ""; | 
|  | 60                         if ( name && message ) { | 
|  | 61                                 return name + ": " + message; | 
|  | 62                         } else if ( name ) { | 
|  | 63                                 return name; | 
|  | 64                         } else if ( message ) { | 
|  | 65                                 return message; | 
|  | 66                         } else { | 
|  | 67                                 return "Error"; | 
|  | 68                         } | 
|  | 69                 } else { | 
|  | 70                         return errorString; | 
|  | 71                 } | 
|  | 72         }, | 
|  | 73         /** | 
|  | 74          * Makes a clone of an object using only Array or Object as base, | 
|  | 75          * and copies over the own enumerable properties. | 
|  | 76          * | 
|  | 77          * @param {Object} obj | 
|  | 78          * @return {Object} New object with only the own properties (recursively
      ). | 
|  | 79          */ | 
|  | 80         objectValues = function( obj ) { | 
|  | 81                 var key, val, | 
|  | 82                         vals = QUnit.is( "array", obj ) ? [] : {}; | 
|  | 83                 for ( key in obj ) { | 
|  | 84                         if ( hasOwn.call( obj, key ) ) { | 
|  | 85                                 val = obj[ key ]; | 
|  | 86                                 vals[ key ] = val === Object( val ) ? objectValu
      es( val ) : val; | 
|  | 87                         } | 
|  | 88                 } | 
|  | 89                 return vals; | 
|  | 90         }; | 
|  | 91 | 
|  | 92 QUnit = {}; | 
|  | 93 | 
|  | 94 /** | 
|  | 95  * Config object: Maintain internal state | 
|  | 96  * Later exposed as QUnit.config | 
|  | 97  * `config` initialized at top of scope | 
|  | 98  */ | 
|  | 99 config = { | 
|  | 100         // The queue of tests to run | 
|  | 101         queue: [], | 
|  | 102 | 
|  | 103         // block until document ready | 
|  | 104         blocking: true, | 
|  | 105 | 
|  | 106         // by default, run previously failed tests first | 
|  | 107         // very useful in combination with "Hide passed tests" checked | 
|  | 108         reorder: true, | 
|  | 109 | 
|  | 110         // by default, modify document.title when suite is done | 
|  | 111         altertitle: true, | 
|  | 112 | 
|  | 113         // by default, scroll to top of the page when suite is done | 
|  | 114         scrolltop: true, | 
|  | 115 | 
|  | 116         // when enabled, all tests must call expect() | 
|  | 117         requireExpects: false, | 
|  | 118 | 
|  | 119         // add checkboxes that are persisted in the query-string | 
|  | 120         // when enabled, the id is set to `true` as a `QUnit.config` property | 
|  | 121         urlConfig: [ | 
|  | 122                 { | 
|  | 123                         id: "hidepassed", | 
|  | 124                         label: "Hide passed tests", | 
|  | 125                         tooltip: "Only show tests and assertions that fail. Stor
      ed as query-strings." | 
|  | 126                 }, | 
|  | 127                 { | 
|  | 128                         id: "noglobals", | 
|  | 129                         label: "Check for Globals", | 
|  | 130                         tooltip: "Enabling this will test if any test introduces
       new properties on the " + | 
|  | 131                                 "`window` object. Stored as query-strings." | 
|  | 132                 }, | 
|  | 133                 { | 
|  | 134                         id: "notrycatch", | 
|  | 135                         label: "No try-catch", | 
|  | 136                         tooltip: "Enabling this will run tests outside of a try-
      catch block. Makes debugging " + | 
|  | 137                                 "exceptions in IE reasonable. Stored as query-st
      rings." | 
|  | 138                 } | 
|  | 139         ], | 
|  | 140 | 
|  | 141         // Set of all modules. | 
|  | 142         modules: [], | 
|  | 143 | 
|  | 144         // The first unnamed module | 
|  | 145         currentModule: { | 
|  | 146                 name: "", | 
|  | 147                 tests: [] | 
|  | 148         }, | 
|  | 149 | 
|  | 150         callbacks: {} | 
|  | 151 }; | 
|  | 152 | 
|  | 153 // Push a loose unnamed module to the modules collection | 
|  | 154 config.modules.push( config.currentModule ); | 
|  | 155 | 
|  | 156 // Initialize more QUnit.config and QUnit.urlParams | 
|  | 157 (function() { | 
|  | 158         var i, current, | 
|  | 159                 location = window.location || { search: "", protocol: "file:" }, | 
|  | 160                 params = location.search.slice( 1 ).split( "&" ), | 
|  | 161                 length = params.length, | 
|  | 162                 urlParams = {}; | 
|  | 163 | 
|  | 164         if ( params[ 0 ] ) { | 
|  | 165                 for ( i = 0; i < length; i++ ) { | 
|  | 166                         current = params[ i ].split( "=" ); | 
|  | 167                         current[ 0 ] = decodeURIComponent( current[ 0 ] ); | 
|  | 168 | 
|  | 169                         // allow just a key to turn on a flag, e.g., test.html?n
      oglobals | 
|  | 170                         current[ 1 ] = current[ 1 ] ? decodeURIComponent( curren
      t[ 1 ] ) : true; | 
|  | 171                         if ( urlParams[ current[ 0 ] ] ) { | 
|  | 172                                 urlParams[ current[ 0 ] ] = [].concat( urlParams
      [ current[ 0 ] ], current[ 1 ] ); | 
|  | 173                         } else { | 
|  | 174                                 urlParams[ current[ 0 ] ] = current[ 1 ]; | 
|  | 175                         } | 
|  | 176                 } | 
|  | 177         } | 
|  | 178 | 
|  | 179         if ( urlParams.filter === true ) { | 
|  | 180                 delete urlParams.filter; | 
|  | 181         } | 
|  | 182 | 
|  | 183         QUnit.urlParams = urlParams; | 
|  | 184 | 
|  | 185         // String search anywhere in moduleName+testName | 
|  | 186         config.filter = urlParams.filter; | 
|  | 187 | 
|  | 188         config.testId = []; | 
|  | 189         if ( urlParams.testId ) { | 
|  | 190 | 
|  | 191                 // Ensure that urlParams.testId is an array | 
|  | 192                 urlParams.testId = [].concat( urlParams.testId ); | 
|  | 193                 for ( i = 0; i < urlParams.testId.length; i++ ) { | 
|  | 194                         config.testId.push( urlParams.testId[ i ] ); | 
|  | 195                 } | 
|  | 196         } | 
|  | 197 | 
|  | 198         // Figure out if we're running the tests from a server or not | 
|  | 199         QUnit.isLocal = location.protocol === "file:"; | 
|  | 200 }()); | 
|  | 201 | 
|  | 202 // Root QUnit object. | 
|  | 203 // `QUnit` initialized at top of scope | 
|  | 204 extend( QUnit, { | 
|  | 205 | 
|  | 206         // call on start of module test to prepend name to all tests | 
|  | 207         module: function( name, testEnvironment ) { | 
|  | 208                 var currentModule = { | 
|  | 209                         name: name, | 
|  | 210                         testEnvironment: testEnvironment, | 
|  | 211                         tests: [] | 
|  | 212                 }; | 
|  | 213 | 
|  | 214                 // DEPRECATED: handles setup/teardown functions, | 
|  | 215                 // beforeEach and afterEach should be used instead | 
|  | 216                 if ( testEnvironment && testEnvironment.setup ) { | 
|  | 217                         testEnvironment.beforeEach = testEnvironment.setup; | 
|  | 218                         delete testEnvironment.setup; | 
|  | 219                 } | 
|  | 220                 if ( testEnvironment && testEnvironment.teardown ) { | 
|  | 221                         testEnvironment.afterEach = testEnvironment.teardown; | 
|  | 222                         delete testEnvironment.teardown; | 
|  | 223                 } | 
|  | 224 | 
|  | 225                 config.modules.push( currentModule ); | 
|  | 226                 config.currentModule = currentModule; | 
|  | 227         }, | 
|  | 228 | 
|  | 229         // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0. | 
|  | 230         asyncTest: function( testName, expected, callback ) { | 
|  | 231                 if ( arguments.length === 2 ) { | 
|  | 232                         callback = expected; | 
|  | 233                         expected = null; | 
|  | 234                 } | 
|  | 235 | 
|  | 236                 QUnit.test( testName, expected, callback, true ); | 
|  | 237         }, | 
|  | 238 | 
|  | 239         test: function( testName, expected, callback, async ) { | 
|  | 240                 var test; | 
|  | 241 | 
|  | 242                 if ( arguments.length === 2 ) { | 
|  | 243                         callback = expected; | 
|  | 244                         expected = null; | 
|  | 245                 } | 
|  | 246 | 
|  | 247                 test = new Test({ | 
|  | 248                         testName: testName, | 
|  | 249                         expected: expected, | 
|  | 250                         async: async, | 
|  | 251                         callback: callback | 
|  | 252                 }); | 
|  | 253 | 
|  | 254                 test.queue(); | 
|  | 255         }, | 
|  | 256 | 
|  | 257         skip: function( testName ) { | 
|  | 258                 var test = new Test({ | 
|  | 259                         testName: testName, | 
|  | 260                         skip: true | 
|  | 261                 }); | 
|  | 262 | 
|  | 263                 test.queue(); | 
|  | 264         }, | 
|  | 265 | 
|  | 266         // DEPRECATED: The functionality of QUnit.start() will be altered in QUn
      it 2.0. | 
|  | 267         // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostar
      t` blocking behavior. | 
|  | 268         start: function( count ) { | 
|  | 269                 var globalStartAlreadyCalled = globalStartCalled; | 
|  | 270 | 
|  | 271                 if ( !config.current ) { | 
|  | 272                         globalStartCalled = true; | 
|  | 273 | 
|  | 274                         if ( runStarted ) { | 
|  | 275                                 throw new Error( "Called start() outside of a te
      st context while already started" ); | 
|  | 276                         } else if ( globalStartAlreadyCalled || count > 1 ) { | 
|  | 277                                 throw new Error( "Called start() outside of a te
      st context too many times" ); | 
|  | 278                         } else if ( config.autostart ) { | 
|  | 279                                 throw new Error( "Called start() outside of a te
      st context when " + | 
|  | 280                                         "QUnit.config.autostart was true" ); | 
|  | 281                         } else if ( !config.pageLoaded ) { | 
|  | 282 | 
|  | 283                                 // The page isn't completely loaded yet, so bail
       out and let `QUnit.load` handle it | 
|  | 284                                 config.autostart = true; | 
|  | 285                                 return; | 
|  | 286                         } | 
|  | 287                 } else { | 
|  | 288 | 
|  | 289                         // If a test is running, adjust its semaphore | 
|  | 290                         config.current.semaphore -= count || 1; | 
|  | 291 | 
|  | 292                         // Don't start until equal number of stop-calls | 
|  | 293                         if ( config.current.semaphore > 0 ) { | 
|  | 294                                 return; | 
|  | 295                         } | 
|  | 296 | 
|  | 297                         // throw an Error if start is called more often than sto
      p | 
|  | 298                         if ( config.current.semaphore < 0 ) { | 
|  | 299                                 config.current.semaphore = 0; | 
|  | 300 | 
|  | 301                                 QUnit.pushFailure( | 
|  | 302                                         "Called start() while already started (t
      est's semaphore was 0 already)", | 
|  | 303                                         sourceFromStacktrace( 2 ) | 
|  | 304                                 ); | 
|  | 305                                 return; | 
|  | 306                         } | 
|  | 307                 } | 
|  | 308 | 
|  | 309                 resumeProcessing(); | 
|  | 310         }, | 
|  | 311 | 
|  | 312         // DEPRECATED: QUnit.stop() will be removed in QUnit 2.0. | 
|  | 313         stop: function( count ) { | 
|  | 314 | 
|  | 315                 // If there isn't a test running, don't allow QUnit.stop() to be
       called | 
|  | 316                 if ( !config.current ) { | 
|  | 317                         throw new Error( "Called stop() outside of a test contex
      t" ); | 
|  | 318                 } | 
|  | 319 | 
|  | 320                 // If a test is running, adjust its semaphore | 
|  | 321                 config.current.semaphore += count || 1; | 
|  | 322 | 
|  | 323                 pauseProcessing(); | 
|  | 324         }, | 
|  | 325 | 
|  | 326         config: config, | 
|  | 327 | 
|  | 328         // Safe object type checking | 
|  | 329         is: function( type, obj ) { | 
|  | 330                 return QUnit.objectType( obj ) === type; | 
|  | 331         }, | 
|  | 332 | 
|  | 333         objectType: function( obj ) { | 
|  | 334                 if ( typeof obj === "undefined" ) { | 
|  | 335                         return "undefined"; | 
|  | 336                 } | 
|  | 337 | 
|  | 338                 // Consider: typeof null === object | 
|  | 339                 if ( obj === null ) { | 
|  | 340                         return "null"; | 
|  | 341                 } | 
|  | 342 | 
|  | 343                 var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ), | 
|  | 344                         type = match && match[ 1 ] || ""; | 
|  | 345 | 
|  | 346                 switch ( type ) { | 
|  | 347                         case "Number": | 
|  | 348                                 if ( isNaN( obj ) ) { | 
|  | 349                                         return "nan"; | 
|  | 350                                 } | 
|  | 351                                 return "number"; | 
|  | 352                         case "String": | 
|  | 353                         case "Boolean": | 
|  | 354                         case "Array": | 
|  | 355                         case "Date": | 
|  | 356                         case "RegExp": | 
|  | 357                         case "Function": | 
|  | 358                                 return type.toLowerCase(); | 
|  | 359                 } | 
|  | 360                 if ( typeof obj === "object" ) { | 
|  | 361                         return "object"; | 
|  | 362                 } | 
|  | 363                 return undefined; | 
|  | 364         }, | 
|  | 365 | 
|  | 366         extend: extend, | 
|  | 367 | 
|  | 368         load: function() { | 
|  | 369                 config.pageLoaded = true; | 
|  | 370 | 
|  | 371                 // Initialize the configuration options | 
|  | 372                 extend( config, { | 
|  | 373                         stats: { all: 0, bad: 0 }, | 
|  | 374                         moduleStats: { all: 0, bad: 0 }, | 
|  | 375                         started: 0, | 
|  | 376                         updateRate: 1000, | 
|  | 377                         autostart: true, | 
|  | 378                         filter: "" | 
|  | 379                 }, true ); | 
|  | 380 | 
|  | 381                 config.blocking = false; | 
|  | 382 | 
|  | 383                 if ( config.autostart ) { | 
|  | 384                         resumeProcessing(); | 
|  | 385                 } | 
|  | 386         } | 
|  | 387 }); | 
|  | 388 | 
|  | 389 // Register logging callbacks | 
|  | 390 (function() { | 
|  | 391         var i, l, key, | 
|  | 392                 callbacks = [ "begin", "done", "log", "testStart", "testDone", | 
|  | 393                         "moduleStart", "moduleDone" ]; | 
|  | 394 | 
|  | 395         function registerLoggingCallback( key ) { | 
|  | 396                 var loggingCallback = function( callback ) { | 
|  | 397                         if ( QUnit.objectType( callback ) !== "function" ) { | 
|  | 398                                 throw new Error( | 
|  | 399                                         "QUnit logging methods require a callbac
      k function as their first parameters." | 
|  | 400                                 ); | 
|  | 401                         } | 
|  | 402 | 
|  | 403                         config.callbacks[ key ].push( callback ); | 
|  | 404                 }; | 
|  | 405 | 
|  | 406                 // DEPRECATED: This will be removed on QUnit 2.0.0+ | 
|  | 407                 // Stores the registered functions allowing restoring | 
|  | 408                 // at verifyLoggingCallbacks() if modified | 
|  | 409                 loggingCallbacks[ key ] = loggingCallback; | 
|  | 410 | 
|  | 411                 return loggingCallback; | 
|  | 412         } | 
|  | 413 | 
|  | 414         for ( i = 0, l = callbacks.length; i < l; i++ ) { | 
|  | 415                 key = callbacks[ i ]; | 
|  | 416 | 
|  | 417                 // Initialize key collection of logging callback | 
|  | 418                 if ( QUnit.objectType( config.callbacks[ key ] ) === "undefined"
       ) { | 
|  | 419                         config.callbacks[ key ] = []; | 
|  | 420                 } | 
|  | 421 | 
|  | 422                 QUnit[ key ] = registerLoggingCallback( key ); | 
|  | 423         } | 
|  | 424 })(); | 
|  | 425 | 
|  | 426 // `onErrorFnPrev` initialized at top of scope | 
|  | 427 // Preserve other handlers | 
|  | 428 onErrorFnPrev = window.onerror; | 
|  | 429 | 
|  | 430 // Cover uncaught exceptions | 
|  | 431 // Returning true will suppress the default browser handler, | 
|  | 432 // returning false will let it run. | 
|  | 433 window.onerror = function( error, filePath, linerNr ) { | 
|  | 434         var ret = false; | 
|  | 435         if ( onErrorFnPrev ) { | 
|  | 436                 ret = onErrorFnPrev( error, filePath, linerNr ); | 
|  | 437         } | 
|  | 438 | 
|  | 439         // Treat return value as window.onerror itself does, | 
|  | 440         // Only do our handling if not suppressed. | 
|  | 441         if ( ret !== true ) { | 
|  | 442                 if ( QUnit.config.current ) { | 
|  | 443                         if ( QUnit.config.current.ignoreGlobalErrors ) { | 
|  | 444                                 return true; | 
|  | 445                         } | 
|  | 446                         QUnit.pushFailure( error, filePath + ":" + linerNr ); | 
|  | 447                 } else { | 
|  | 448                         QUnit.test( "global failure", extend(function() { | 
|  | 449                                 QUnit.pushFailure( error, filePath + ":" + liner
      Nr ); | 
|  | 450                         }, { validTest: true } ) ); | 
|  | 451                 } | 
|  | 452                 return false; | 
|  | 453         } | 
|  | 454 | 
|  | 455         return ret; | 
|  | 456 }; | 
|  | 457 | 
|  | 458 function done() { | 
|  | 459         var runtime, passed; | 
|  | 460 | 
|  | 461         config.autorun = true; | 
|  | 462 | 
|  | 463         // Log the last module results | 
|  | 464         if ( config.previousModule ) { | 
|  | 465                 runLoggingCallbacks( "moduleDone", { | 
|  | 466                         name: config.previousModule.name, | 
|  | 467                         tests: config.previousModule.tests, | 
|  | 468                         failed: config.moduleStats.bad, | 
|  | 469                         passed: config.moduleStats.all - config.moduleStats.bad, | 
|  | 470                         total: config.moduleStats.all, | 
|  | 471                         runtime: now() - config.moduleStats.started | 
|  | 472                 }); | 
|  | 473         } | 
|  | 474         delete config.previousModule; | 
|  | 475 | 
|  | 476         runtime = now() - config.started; | 
|  | 477         passed = config.stats.all - config.stats.bad; | 
|  | 478 | 
|  | 479         runLoggingCallbacks( "done", { | 
|  | 480                 failed: config.stats.bad, | 
|  | 481                 passed: passed, | 
|  | 482                 total: config.stats.all, | 
|  | 483                 runtime: runtime | 
|  | 484         }); | 
|  | 485 } | 
|  | 486 | 
|  | 487 // Doesn't support IE6 to IE9 | 
|  | 488 // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects
      /Error/Stack | 
|  | 489 function extractStacktrace( e, offset ) { | 
|  | 490         offset = offset === undefined ? 4 : offset; | 
|  | 491 | 
|  | 492         var stack, include, i; | 
|  | 493 | 
|  | 494         if ( e.stacktrace ) { | 
|  | 495 | 
|  | 496                 // Opera 12.x | 
|  | 497                 return e.stacktrace.split( "\n" )[ offset + 3 ]; | 
|  | 498         } else if ( e.stack ) { | 
|  | 499 | 
|  | 500                 // Firefox, Chrome, Safari 6+, IE10+, PhantomJS and Node | 
|  | 501                 stack = e.stack.split( "\n" ); | 
|  | 502                 if ( /^error$/i.test( stack[ 0 ] ) ) { | 
|  | 503                         stack.shift(); | 
|  | 504                 } | 
|  | 505                 if ( fileName ) { | 
|  | 506                         include = []; | 
|  | 507                         for ( i = offset; i < stack.length; i++ ) { | 
|  | 508                                 if ( stack[ i ].indexOf( fileName ) !== -1 ) { | 
|  | 509                                         break; | 
|  | 510                                 } | 
|  | 511                                 include.push( stack[ i ] ); | 
|  | 512                         } | 
|  | 513                         if ( include.length ) { | 
|  | 514                                 return include.join( "\n" ); | 
|  | 515                         } | 
|  | 516                 } | 
|  | 517                 return stack[ offset ]; | 
|  | 518         } else if ( e.sourceURL ) { | 
|  | 519 | 
|  | 520                 // Safari < 6 | 
|  | 521                 // exclude useless self-reference for generated Error objects | 
|  | 522                 if ( /qunit.js$/.test( e.sourceURL ) ) { | 
|  | 523                         return; | 
|  | 524                 } | 
|  | 525 | 
|  | 526                 // for actual exceptions, this is useful | 
|  | 527                 return e.sourceURL + ":" + e.line; | 
|  | 528         } | 
|  | 529 } | 
|  | 530 | 
|  | 531 function sourceFromStacktrace( offset ) { | 
|  | 532         var e = new Error(); | 
|  | 533         if ( !e.stack ) { | 
|  | 534                 try { | 
|  | 535                         throw e; | 
|  | 536                 } catch ( err ) { | 
|  | 537                         // This should already be true in most browsers | 
|  | 538                         e = err; | 
|  | 539                 } | 
|  | 540         } | 
|  | 541         return extractStacktrace( e, offset ); | 
|  | 542 } | 
|  | 543 | 
|  | 544 function synchronize( callback, last ) { | 
|  | 545         if ( QUnit.objectType( callback ) === "array" ) { | 
|  | 546                 while ( callback.length ) { | 
|  | 547                         synchronize( callback.shift() ); | 
|  | 548                 } | 
|  | 549                 return; | 
|  | 550         } | 
|  | 551         config.queue.push( callback ); | 
|  | 552 | 
|  | 553         if ( config.autorun && !config.blocking ) { | 
|  | 554                 process( last ); | 
|  | 555         } | 
|  | 556 } | 
|  | 557 | 
|  | 558 function process( last ) { | 
|  | 559         function next() { | 
|  | 560                 process( last ); | 
|  | 561         } | 
|  | 562         var start = now(); | 
|  | 563         config.depth = ( config.depth || 0 ) + 1; | 
|  | 564 | 
|  | 565         while ( config.queue.length && !config.blocking ) { | 
|  | 566                 if ( !defined.setTimeout || config.updateRate <= 0 || | 
|  | 567                                 ( ( now() - start ) < config.updateRate ) ) { | 
|  | 568                         if ( config.current ) { | 
|  | 569 | 
|  | 570                                 // Reset async tracking for each phase of the Te
      st lifecycle | 
|  | 571                                 config.current.usedAsync = false; | 
|  | 572                         } | 
|  | 573                         config.queue.shift()(); | 
|  | 574                 } else { | 
|  | 575                         setTimeout( next, 13 ); | 
|  | 576                         break; | 
|  | 577                 } | 
|  | 578         } | 
|  | 579         config.depth--; | 
|  | 580         if ( last && !config.blocking && !config.queue.length && config.depth ==
      = 0 ) { | 
|  | 581                 done(); | 
|  | 582         } | 
|  | 583 } | 
|  | 584 | 
|  | 585 function begin() { | 
|  | 586         var i, l, | 
|  | 587                 modulesLog = []; | 
|  | 588 | 
|  | 589         // If the test run hasn't officially begun yet | 
|  | 590         if ( !config.started ) { | 
|  | 591 | 
|  | 592                 // Record the time of the test run's beginning | 
|  | 593                 config.started = now(); | 
|  | 594 | 
|  | 595                 verifyLoggingCallbacks(); | 
|  | 596 | 
|  | 597                 // Delete the loose unnamed module if unused. | 
|  | 598                 if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].test
      s.length === 0 ) { | 
|  | 599                         config.modules.shift(); | 
|  | 600                 } | 
|  | 601 | 
|  | 602                 // Avoid unnecessary information by not logging modules' test en
      vironments | 
|  | 603                 for ( i = 0, l = config.modules.length; i < l; i++ ) { | 
|  | 604                         modulesLog.push({ | 
|  | 605                                 name: config.modules[ i ].name, | 
|  | 606                                 tests: config.modules[ i ].tests | 
|  | 607                         }); | 
|  | 608                 } | 
|  | 609 | 
|  | 610                 // The test run is officially beginning now | 
|  | 611                 runLoggingCallbacks( "begin", { | 
|  | 612                         totalTests: Test.count, | 
|  | 613                         modules: modulesLog | 
|  | 614                 }); | 
|  | 615         } | 
|  | 616 | 
|  | 617         config.blocking = false; | 
|  | 618         process( true ); | 
|  | 619 } | 
|  | 620 | 
|  | 621 function resumeProcessing() { | 
|  | 622         runStarted = true; | 
|  | 623 | 
|  | 624         // A slight delay to allow this iteration of the event loop to finish (m
      ore assertions, etc.) | 
|  | 625         if ( defined.setTimeout ) { | 
|  | 626                 setTimeout(function() { | 
|  | 627                         if ( config.current && config.current.semaphore > 0 ) { | 
|  | 628                                 return; | 
|  | 629                         } | 
|  | 630                         if ( config.timeout ) { | 
|  | 631                                 clearTimeout( config.timeout ); | 
|  | 632                         } | 
|  | 633 | 
|  | 634                         begin(); | 
|  | 635                 }, 13 ); | 
|  | 636         } else { | 
|  | 637                 begin(); | 
|  | 638         } | 
|  | 639 } | 
|  | 640 | 
|  | 641 function pauseProcessing() { | 
|  | 642         config.blocking = true; | 
|  | 643 | 
|  | 644         if ( config.testTimeout && defined.setTimeout ) { | 
|  | 645                 clearTimeout( config.timeout ); | 
|  | 646                 config.timeout = setTimeout(function() { | 
|  | 647                         if ( config.current ) { | 
|  | 648                                 config.current.semaphore = 0; | 
|  | 649                                 QUnit.pushFailure( "Test timed out", sourceFromS
      tacktrace( 2 ) ); | 
|  | 650                         } else { | 
|  | 651                                 throw new Error( "Test timed out" ); | 
|  | 652                         } | 
|  | 653                         resumeProcessing(); | 
|  | 654                 }, config.testTimeout ); | 
|  | 655         } | 
|  | 656 } | 
|  | 657 | 
|  | 658 function saveGlobal() { | 
|  | 659         config.pollution = []; | 
|  | 660 | 
|  | 661         if ( config.noglobals ) { | 
|  | 662                 for ( var key in window ) { | 
|  | 663                         if ( hasOwn.call( window, key ) ) { | 
|  | 664                                 // in Opera sometimes DOM element ids show up he
      re, ignore them | 
|  | 665                                 if ( /^qunit-test-output/.test( key ) ) { | 
|  | 666                                         continue; | 
|  | 667                                 } | 
|  | 668                                 config.pollution.push( key ); | 
|  | 669                         } | 
|  | 670                 } | 
|  | 671         } | 
|  | 672 } | 
|  | 673 | 
|  | 674 function checkPollution() { | 
|  | 675         var newGlobals, | 
|  | 676                 deletedGlobals, | 
|  | 677                 old = config.pollution; | 
|  | 678 | 
|  | 679         saveGlobal(); | 
|  | 680 | 
|  | 681         newGlobals = diff( config.pollution, old ); | 
|  | 682         if ( newGlobals.length > 0 ) { | 
|  | 683                 QUnit.pushFailure( "Introduced global variable(s): " + newGlobal
      s.join( ", " ) ); | 
|  | 684         } | 
|  | 685 | 
|  | 686         deletedGlobals = diff( old, config.pollution ); | 
|  | 687         if ( deletedGlobals.length > 0 ) { | 
|  | 688                 QUnit.pushFailure( "Deleted global variable(s): " + deletedGloba
      ls.join( ", " ) ); | 
|  | 689         } | 
|  | 690 } | 
|  | 691 | 
|  | 692 // returns a new Array with the elements that are in a but not in b | 
|  | 693 function diff( a, b ) { | 
|  | 694         var i, j, | 
|  | 695                 result = a.slice(); | 
|  | 696 | 
|  | 697         for ( i = 0; i < result.length; i++ ) { | 
|  | 698                 for ( j = 0; j < b.length; j++ ) { | 
|  | 699                         if ( result[ i ] === b[ j ] ) { | 
|  | 700                                 result.splice( i, 1 ); | 
|  | 701                                 i--; | 
|  | 702                                 break; | 
|  | 703                         } | 
|  | 704                 } | 
|  | 705         } | 
|  | 706         return result; | 
|  | 707 } | 
|  | 708 | 
|  | 709 function extend( a, b, undefOnly ) { | 
|  | 710         for ( var prop in b ) { | 
|  | 711                 if ( hasOwn.call( b, prop ) ) { | 
|  | 712 | 
|  | 713                         // Avoid "Member not found" error in IE8 caused by messi
      ng with window.constructor | 
|  | 714                         if ( !( prop === "constructor" && a === window ) ) { | 
|  | 715                                 if ( b[ prop ] === undefined ) { | 
|  | 716                                         delete a[ prop ]; | 
|  | 717                                 } else if ( !( undefOnly && typeof a[ prop ] !==
       "undefined" ) ) { | 
|  | 718                                         a[ prop ] = b[ prop ]; | 
|  | 719                                 } | 
|  | 720                         } | 
|  | 721                 } | 
|  | 722         } | 
|  | 723 | 
|  | 724         return a; | 
|  | 725 } | 
|  | 726 | 
|  | 727 function runLoggingCallbacks( key, args ) { | 
|  | 728         var i, l, callbacks; | 
|  | 729 | 
|  | 730         callbacks = config.callbacks[ key ]; | 
|  | 731         for ( i = 0, l = callbacks.length; i < l; i++ ) { | 
|  | 732                 callbacks[ i ]( args ); | 
|  | 733         } | 
|  | 734 } | 
|  | 735 | 
|  | 736 // DEPRECATED: This will be removed on 2.0.0+ | 
|  | 737 // This function verifies if the loggingCallbacks were modified by the user | 
|  | 738 // If so, it will restore it, assign the given callback and print a console warn
      ing | 
|  | 739 function verifyLoggingCallbacks() { | 
|  | 740         var loggingCallback, userCallback; | 
|  | 741 | 
|  | 742         for ( loggingCallback in loggingCallbacks ) { | 
|  | 743                 if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallb
      ack ] ) { | 
|  | 744 | 
|  | 745                         userCallback = QUnit[ loggingCallback ]; | 
|  | 746 | 
|  | 747                         // Restore the callback function | 
|  | 748                         QUnit[ loggingCallback ] = loggingCallbacks[ loggingCall
      back ]; | 
|  | 749 | 
|  | 750                         // Assign the deprecated given callback | 
|  | 751                         QUnit[ loggingCallback ]( userCallback ); | 
|  | 752 | 
|  | 753                         if ( window.console && window.console.warn ) { | 
|  | 754                                 window.console.warn( | 
|  | 755                                         "QUnit." + loggingCallback + " was repla
      ced with a new value.\n" + | 
|  | 756                                         "Please, check out the documentation on 
      how to apply logging callbacks.\n" + | 
|  | 757                                         "Reference: http://api.qunitjs.com/categ
      ory/callbacks/" | 
|  | 758                                 ); | 
|  | 759                         } | 
|  | 760                 } | 
|  | 761         } | 
|  | 762 } | 
|  | 763 | 
|  | 764 // from jquery.js | 
|  | 765 function inArray( elem, array ) { | 
|  | 766         if ( array.indexOf ) { | 
|  | 767                 return array.indexOf( elem ); | 
|  | 768         } | 
|  | 769 | 
|  | 770         for ( var i = 0, length = array.length; i < length; i++ ) { | 
|  | 771                 if ( array[ i ] === elem ) { | 
|  | 772                         return i; | 
|  | 773                 } | 
|  | 774         } | 
|  | 775 | 
|  | 776         return -1; | 
|  | 777 } | 
|  | 778 | 
|  | 779 function Test( settings ) { | 
|  | 780         var i, l; | 
|  | 781 | 
|  | 782         ++Test.count; | 
|  | 783 | 
|  | 784         extend( this, settings ); | 
|  | 785         this.assertions = []; | 
|  | 786         this.semaphore = 0; | 
|  | 787         this.usedAsync = false; | 
|  | 788         this.module = config.currentModule; | 
|  | 789         this.stack = sourceFromStacktrace( 3 ); | 
|  | 790 | 
|  | 791         // Register unique strings | 
|  | 792         for ( i = 0, l = this.module.tests; i < l.length; i++ ) { | 
|  | 793                 if ( this.module.tests[ i ].name === this.testName ) { | 
|  | 794                         this.testName += " "; | 
|  | 795                 } | 
|  | 796         } | 
|  | 797 | 
|  | 798         this.testId = generateHash( this.module.name, this.testName ); | 
|  | 799 | 
|  | 800         this.module.tests.push({ | 
|  | 801                 name: this.testName, | 
|  | 802                 testId: this.testId | 
|  | 803         }); | 
|  | 804 | 
|  | 805         if ( settings.skip ) { | 
|  | 806 | 
|  | 807                 // Skipped tests will fully ignore any sent callback | 
|  | 808                 this.callback = function() {}; | 
|  | 809                 this.async = false; | 
|  | 810                 this.expected = 0; | 
|  | 811         } else { | 
|  | 812                 this.assert = new Assert( this ); | 
|  | 813         } | 
|  | 814 } | 
|  | 815 | 
|  | 816 Test.count = 0; | 
|  | 817 | 
|  | 818 Test.prototype = { | 
|  | 819         before: function() { | 
|  | 820                 if ( | 
|  | 821 | 
|  | 822                         // Emit moduleStart when we're switching from one module
       to another | 
|  | 823                         this.module !== config.previousModule || | 
|  | 824 | 
|  | 825                                 // They could be equal (both undefined) but if t
      he previousModule property doesn't | 
|  | 826                                 // yet exist it means this is the first test in 
      a suite that isn't wrapped in a | 
|  | 827                                 // module, in which case we'll just emit a modul
      eStart event for 'undefined'. | 
|  | 828                                 // Without this, reporters can get testStart bef
      ore moduleStart  which is a problem. | 
|  | 829                                 !hasOwn.call( config, "previousModule" ) | 
|  | 830                 ) { | 
|  | 831                         if ( hasOwn.call( config, "previousModule" ) ) { | 
|  | 832                                 runLoggingCallbacks( "moduleDone", { | 
|  | 833                                         name: config.previousModule.name, | 
|  | 834                                         tests: config.previousModule.tests, | 
|  | 835                                         failed: config.moduleStats.bad, | 
|  | 836                                         passed: config.moduleStats.all - config.
      moduleStats.bad, | 
|  | 837                                         total: config.moduleStats.all, | 
|  | 838                                         runtime: now() - config.moduleStats.star
      ted | 
|  | 839                                 }); | 
|  | 840                         } | 
|  | 841                         config.previousModule = this.module; | 
|  | 842                         config.moduleStats = { all: 0, bad: 0, started: now() }; | 
|  | 843                         runLoggingCallbacks( "moduleStart", { | 
|  | 844                                 name: this.module.name, | 
|  | 845                                 tests: this.module.tests | 
|  | 846                         }); | 
|  | 847                 } | 
|  | 848 | 
|  | 849                 config.current = this; | 
|  | 850 | 
|  | 851                 this.testEnvironment = extend( {}, this.module.testEnvironment )
      ; | 
|  | 852                 delete this.testEnvironment.beforeEach; | 
|  | 853                 delete this.testEnvironment.afterEach; | 
|  | 854 | 
|  | 855                 this.started = now(); | 
|  | 856                 runLoggingCallbacks( "testStart", { | 
|  | 857                         name: this.testName, | 
|  | 858                         module: this.module.name, | 
|  | 859                         testId: this.testId | 
|  | 860                 }); | 
|  | 861 | 
|  | 862                 if ( !config.pollution ) { | 
|  | 863                         saveGlobal(); | 
|  | 864                 } | 
|  | 865         }, | 
|  | 866 | 
|  | 867         run: function() { | 
|  | 868                 var promise; | 
|  | 869 | 
|  | 870                 config.current = this; | 
|  | 871 | 
|  | 872                 if ( this.async ) { | 
|  | 873                         QUnit.stop(); | 
|  | 874                 } | 
|  | 875 | 
|  | 876                 this.callbackStarted = now(); | 
|  | 877 | 
|  | 878                 if ( config.notrycatch ) { | 
|  | 879                         promise = this.callback.call( this.testEnvironment, this
      .assert ); | 
|  | 880                         this.resolvePromise( promise ); | 
|  | 881                         return; | 
|  | 882                 } | 
|  | 883 | 
|  | 884                 try { | 
|  | 885                         promise = this.callback.call( this.testEnvironment, this
      .assert ); | 
|  | 886                         this.resolvePromise( promise ); | 
|  | 887                 } catch ( e ) { | 
|  | 888                         this.pushFailure( "Died on test #" + ( this.assertions.l
      ength + 1 ) + " " + | 
|  | 889                                 this.stack + ": " + ( e.message || e ), extractS
      tacktrace( e, 0 ) ); | 
|  | 890 | 
|  | 891                         // else next test will carry the responsibility | 
|  | 892                         saveGlobal(); | 
|  | 893 | 
|  | 894                         // Restart the tests if they're blocking | 
|  | 895                         if ( config.blocking ) { | 
|  | 896                                 QUnit.start(); | 
|  | 897                         } | 
|  | 898                 } | 
|  | 899         }, | 
|  | 900 | 
|  | 901         after: function() { | 
|  | 902                 checkPollution(); | 
|  | 903         }, | 
|  | 904 | 
|  | 905         queueHook: function( hook, hookName ) { | 
|  | 906                 var promise, | 
|  | 907                         test = this; | 
|  | 908                 return function runHook() { | 
|  | 909                         config.current = test; | 
|  | 910                         if ( config.notrycatch ) { | 
|  | 911                                 promise = hook.call( test.testEnvironment, test.
      assert ); | 
|  | 912                                 test.resolvePromise( promise, hookName ); | 
|  | 913                                 return; | 
|  | 914                         } | 
|  | 915                         try { | 
|  | 916                                 promise = hook.call( test.testEnvironment, test.
      assert ); | 
|  | 917                                 test.resolvePromise( promise, hookName ); | 
|  | 918                         } catch ( error ) { | 
|  | 919                                 test.pushFailure( hookName + " failed on " + tes
      t.testName + ": " + | 
|  | 920                                         ( error.message || error ), extractStack
      trace( error, 0 ) ); | 
|  | 921                         } | 
|  | 922                 }; | 
|  | 923         }, | 
|  | 924 | 
|  | 925         // Currently only used for module level hooks, can be used to add global
       level ones | 
|  | 926         hooks: function( handler ) { | 
|  | 927                 var hooks = []; | 
|  | 928 | 
|  | 929                 // Hooks are ignored on skipped tests | 
|  | 930                 if ( this.skip ) { | 
|  | 931                         return hooks; | 
|  | 932                 } | 
|  | 933 | 
|  | 934                 if ( this.module.testEnvironment && | 
|  | 935                                 QUnit.objectType( this.module.testEnvironment[ h
      andler ] ) === "function" ) { | 
|  | 936                         hooks.push( this.queueHook( this.module.testEnvironment[
       handler ], handler ) ); | 
|  | 937                 } | 
|  | 938 | 
|  | 939                 return hooks; | 
|  | 940         }, | 
|  | 941 | 
|  | 942         finish: function() { | 
|  | 943                 config.current = this; | 
|  | 944                 if ( config.requireExpects && this.expected === null ) { | 
|  | 945                         this.pushFailure( "Expected number of assertions to be d
      efined, but expect() was " + | 
|  | 946                                 "not called.", this.stack ); | 
|  | 947                 } else if ( this.expected !== null && this.expected !== this.ass
      ertions.length ) { | 
|  | 948                         this.pushFailure( "Expected " + this.expected + " assert
      ions, but " + | 
|  | 949                                 this.assertions.length + " were run", this.stack
       ); | 
|  | 950                 } else if ( this.expected === null && !this.assertions.length ) 
      { | 
|  | 951                         this.pushFailure( "Expected at least one assertion, but 
      none were run - call " + | 
|  | 952                                 "expect(0) to accept zero assertions.", this.sta
      ck ); | 
|  | 953                 } | 
|  | 954 | 
|  | 955                 var i, | 
|  | 956                         bad = 0; | 
|  | 957 | 
|  | 958                 this.runtime = now() - this.started; | 
|  | 959                 config.stats.all += this.assertions.length; | 
|  | 960                 config.moduleStats.all += this.assertions.length; | 
|  | 961 | 
|  | 962                 for ( i = 0; i < this.assertions.length; i++ ) { | 
|  | 963                         if ( !this.assertions[ i ].result ) { | 
|  | 964                                 bad++; | 
|  | 965                                 config.stats.bad++; | 
|  | 966                                 config.moduleStats.bad++; | 
|  | 967                         } | 
|  | 968                 } | 
|  | 969 | 
|  | 970                 runLoggingCallbacks( "testDone", { | 
|  | 971                         name: this.testName, | 
|  | 972                         module: this.module.name, | 
|  | 973                         skipped: !!this.skip, | 
|  | 974                         failed: bad, | 
|  | 975                         passed: this.assertions.length - bad, | 
|  | 976                         total: this.assertions.length, | 
|  | 977                         runtime: this.runtime, | 
|  | 978 | 
|  | 979                         // HTML Reporter use | 
|  | 980                         assertions: this.assertions, | 
|  | 981                         testId: this.testId, | 
|  | 982 | 
|  | 983                         // DEPRECATED: this property will be removed in 2.0.0, u
      se runtime instead | 
|  | 984                         duration: this.runtime | 
|  | 985                 }); | 
|  | 986 | 
|  | 987                 // QUnit.reset() is deprecated and will be replaced for a new | 
|  | 988                 // fixture reset function on QUnit 2.0/2.1. | 
|  | 989                 // It's still called here for backwards compatibility handling | 
|  | 990                 QUnit.reset(); | 
|  | 991 | 
|  | 992                 config.current = undefined; | 
|  | 993         }, | 
|  | 994 | 
|  | 995         queue: function() { | 
|  | 996                 var bad, | 
|  | 997                         test = this; | 
|  | 998 | 
|  | 999                 if ( !this.valid() ) { | 
|  | 1000                         return; | 
|  | 1001                 } | 
|  | 1002 | 
|  | 1003                 function run() { | 
|  | 1004 | 
|  | 1005                         // each of these can by async | 
|  | 1006                         synchronize([ | 
|  | 1007                                 function() { | 
|  | 1008                                         test.before(); | 
|  | 1009                                 }, | 
|  | 1010 | 
|  | 1011                                 test.hooks( "beforeEach" ), | 
|  | 1012 | 
|  | 1013                                 function() { | 
|  | 1014                                         test.run(); | 
|  | 1015                                 }, | 
|  | 1016 | 
|  | 1017                                 test.hooks( "afterEach" ).reverse(), | 
|  | 1018 | 
|  | 1019                                 function() { | 
|  | 1020                                         test.after(); | 
|  | 1021                                 }, | 
|  | 1022                                 function() { | 
|  | 1023                                         test.finish(); | 
|  | 1024                                 } | 
|  | 1025                         ]); | 
|  | 1026                 } | 
|  | 1027 | 
|  | 1028                 // `bad` initialized at top of scope | 
|  | 1029                 // defer when previous test run passed, if storage is available | 
|  | 1030                 bad = QUnit.config.reorder && defined.sessionStorage && | 
|  | 1031                                 +sessionStorage.getItem( "qunit-test-" + this.mo
      dule.name + "-" + this.testName ); | 
|  | 1032 | 
|  | 1033                 if ( bad ) { | 
|  | 1034                         run(); | 
|  | 1035                 } else { | 
|  | 1036                         synchronize( run, true ); | 
|  | 1037                 } | 
|  | 1038         }, | 
|  | 1039 | 
|  | 1040         push: function( result, actual, expected, message ) { | 
|  | 1041                 var source, | 
|  | 1042                         details = { | 
|  | 1043                                 module: this.module.name, | 
|  | 1044                                 name: this.testName, | 
|  | 1045                                 result: result, | 
|  | 1046                                 message: message, | 
|  | 1047                                 actual: actual, | 
|  | 1048                                 expected: expected, | 
|  | 1049                                 testId: this.testId, | 
|  | 1050                                 runtime: now() - this.started | 
|  | 1051                         }; | 
|  | 1052 | 
|  | 1053                 if ( !result ) { | 
|  | 1054                         source = sourceFromStacktrace(); | 
|  | 1055 | 
|  | 1056                         if ( source ) { | 
|  | 1057                                 details.source = source; | 
|  | 1058                         } | 
|  | 1059                 } | 
|  | 1060 | 
|  | 1061                 runLoggingCallbacks( "log", details ); | 
|  | 1062 | 
|  | 1063                 this.assertions.push({ | 
|  | 1064                         result: !!result, | 
|  | 1065                         message: message | 
|  | 1066                 }); | 
|  | 1067         }, | 
|  | 1068 | 
|  | 1069         pushFailure: function( message, source, actual ) { | 
|  | 1070                 if ( !this instanceof Test ) { | 
|  | 1071                         throw new Error( "pushFailure() assertion outside test c
      ontext, was " + | 
|  | 1072                                 sourceFromStacktrace( 2 ) ); | 
|  | 1073                 } | 
|  | 1074 | 
|  | 1075                 var details = { | 
|  | 1076                                 module: this.module.name, | 
|  | 1077                                 name: this.testName, | 
|  | 1078                                 result: false, | 
|  | 1079                                 message: message || "error", | 
|  | 1080                                 actual: actual || null, | 
|  | 1081                                 testId: this.testId, | 
|  | 1082                                 runtime: now() - this.started | 
|  | 1083                         }; | 
|  | 1084 | 
|  | 1085                 if ( source ) { | 
|  | 1086                         details.source = source; | 
|  | 1087                 } | 
|  | 1088 | 
|  | 1089                 runLoggingCallbacks( "log", details ); | 
|  | 1090 | 
|  | 1091                 this.assertions.push({ | 
|  | 1092                         result: false, | 
|  | 1093                         message: message | 
|  | 1094                 }); | 
|  | 1095         }, | 
|  | 1096 | 
|  | 1097         resolvePromise: function( promise, phase ) { | 
|  | 1098                 var then, message, | 
|  | 1099                         test = this; | 
|  | 1100                 if ( promise != null ) { | 
|  | 1101                         then = promise.then; | 
|  | 1102                         if ( QUnit.objectType( then ) === "function" ) { | 
|  | 1103                                 QUnit.stop(); | 
|  | 1104                                 then.call( | 
|  | 1105                                         promise, | 
|  | 1106                                         QUnit.start, | 
|  | 1107                                         function( error ) { | 
|  | 1108                                                 message = "Promise rejected " + | 
|  | 1109                                                         ( !phase ? "during" : ph
      ase.replace( /Each$/, "" ) ) + | 
|  | 1110                                                         " " + test.testName + ":
       " + ( error.message || error ); | 
|  | 1111                                                 test.pushFailure( message, extra
      ctStacktrace( error, 0 ) ); | 
|  | 1112 | 
|  | 1113                                                 // else next test will carry the
       responsibility | 
|  | 1114                                                 saveGlobal(); | 
|  | 1115 | 
|  | 1116                                                 // Unblock | 
|  | 1117                                                 QUnit.start(); | 
|  | 1118                                         } | 
|  | 1119                                 ); | 
|  | 1120                         } | 
|  | 1121                 } | 
|  | 1122         }, | 
|  | 1123 | 
|  | 1124         valid: function() { | 
|  | 1125                 var include, | 
|  | 1126                         filter = config.filter, | 
|  | 1127                         module = QUnit.urlParams.module && QUnit.urlParams.modul
      e.toLowerCase(), | 
|  | 1128                         fullName = ( this.module.name + ": " + this.testName ).t
      oLowerCase(); | 
|  | 1129 | 
|  | 1130                 // Internally-generated tests are always valid | 
|  | 1131                 if ( this.callback && this.callback.validTest ) { | 
|  | 1132                         return true; | 
|  | 1133                 } | 
|  | 1134 | 
|  | 1135                 if ( config.testId.length > 0 && inArray( this.testId, config.te
      stId ) < 0 ) { | 
|  | 1136                         return false; | 
|  | 1137                 } | 
|  | 1138 | 
|  | 1139                 if ( module && ( !this.module.name || this.module.name.toLowerCa
      se() !== module ) ) { | 
|  | 1140                         return false; | 
|  | 1141                 } | 
|  | 1142 | 
|  | 1143                 if ( !filter ) { | 
|  | 1144                         return true; | 
|  | 1145                 } | 
|  | 1146 | 
|  | 1147                 include = filter.charAt( 0 ) !== "!"; | 
|  | 1148                 if ( !include ) { | 
|  | 1149                         filter = filter.toLowerCase().slice( 1 ); | 
|  | 1150                 } | 
|  | 1151 | 
|  | 1152                 // If the filter matches, we need to honour include | 
|  | 1153                 if ( fullName.indexOf( filter ) !== -1 ) { | 
|  | 1154                         return include; | 
|  | 1155                 } | 
|  | 1156 | 
|  | 1157                 // Otherwise, do the opposite | 
|  | 1158                 return !include; | 
|  | 1159         } | 
|  | 1160 | 
|  | 1161 }; | 
|  | 1162 | 
|  | 1163 // Resets the test setup. Useful for tests that modify the DOM. | 
|  | 1164 /* | 
|  | 1165 DEPRECATED: Use multiple tests instead of resetting inside a test. | 
|  | 1166 Use testStart or testDone for custom cleanup. | 
|  | 1167 This method will throw an error in 2.0, and will be removed in 2.1 | 
|  | 1168 */ | 
|  | 1169 QUnit.reset = function() { | 
|  | 1170 | 
|  | 1171         // Return on non-browser environments | 
|  | 1172         // This is necessary to not break on node tests | 
|  | 1173         if ( typeof window === "undefined" ) { | 
|  | 1174                 return; | 
|  | 1175         } | 
|  | 1176 | 
|  | 1177         var fixture = defined.document && document.getElementById && | 
|  | 1178                         document.getElementById( "qunit-fixture" ); | 
|  | 1179 | 
|  | 1180         if ( fixture ) { | 
|  | 1181                 fixture.innerHTML = config.fixture; | 
|  | 1182         } | 
|  | 1183 }; | 
|  | 1184 | 
|  | 1185 QUnit.pushFailure = function() { | 
|  | 1186         if ( !QUnit.config.current ) { | 
|  | 1187                 throw new Error( "pushFailure() assertion outside test context, 
      in " + | 
|  | 1188                         sourceFromStacktrace( 2 ) ); | 
|  | 1189         } | 
|  | 1190 | 
|  | 1191         // Gets current test obj | 
|  | 1192         var currentTest = QUnit.config.current; | 
|  | 1193 | 
|  | 1194         return currentTest.pushFailure.apply( currentTest, arguments ); | 
|  | 1195 }; | 
|  | 1196 | 
|  | 1197 // Based on Java's String.hashCode, a simple but not | 
|  | 1198 // rigorously collision resistant hashing function | 
|  | 1199 function generateHash( module, testName ) { | 
|  | 1200         var hex, | 
|  | 1201                 i = 0, | 
|  | 1202                 hash = 0, | 
|  | 1203                 str = module + "\x1C" + testName, | 
|  | 1204                 len = str.length; | 
|  | 1205 | 
|  | 1206         for ( ; i < len; i++ ) { | 
|  | 1207                 hash  = ( ( hash << 5 ) - hash ) + str.charCodeAt( i ); | 
|  | 1208                 hash |= 0; | 
|  | 1209         } | 
|  | 1210 | 
|  | 1211         // Convert the possibly negative integer hash code into an 8 character h
      ex string, which isn't | 
|  | 1212         // strictly necessary but increases user understanding that the id is a 
      SHA-like hash | 
|  | 1213         hex = ( 0x100000000 + hash ).toString( 16 ); | 
|  | 1214         if ( hex.length < 8 ) { | 
|  | 1215                 hex = "0000000" + hex; | 
|  | 1216         } | 
|  | 1217 | 
|  | 1218         return hex.slice( -8 ); | 
|  | 1219 } | 
|  | 1220 | 
|  | 1221 function Assert( testContext ) { | 
|  | 1222         this.test = testContext; | 
|  | 1223 } | 
|  | 1224 | 
|  | 1225 // Assert helpers | 
|  | 1226 QUnit.assert = Assert.prototype = { | 
|  | 1227 | 
|  | 1228         // Specify the number of expected assertions to guarantee that failed te
      st | 
|  | 1229         // (no assertions are run at all) don't slip through. | 
|  | 1230         expect: function( asserts ) { | 
|  | 1231                 if ( arguments.length === 1 ) { | 
|  | 1232                         this.test.expected = asserts; | 
|  | 1233                 } else { | 
|  | 1234                         return this.test.expected; | 
|  | 1235                 } | 
|  | 1236         }, | 
|  | 1237 | 
|  | 1238         // Increment this Test's semaphore counter, then return a single-use fun
      ction that | 
|  | 1239         // decrements that counter a maximum of once. | 
|  | 1240         async: function() { | 
|  | 1241                 var test = this.test, | 
|  | 1242                         popped = false; | 
|  | 1243 | 
|  | 1244                 test.semaphore += 1; | 
|  | 1245                 test.usedAsync = true; | 
|  | 1246                 pauseProcessing(); | 
|  | 1247 | 
|  | 1248                 return function done() { | 
|  | 1249                         if ( !popped ) { | 
|  | 1250                                 test.semaphore -= 1; | 
|  | 1251                                 popped = true; | 
|  | 1252                                 resumeProcessing(); | 
|  | 1253                         } else { | 
|  | 1254                                 test.pushFailure( "Called the callback returned 
      from `assert.async` more than once", | 
|  | 1255                                         sourceFromStacktrace( 2 ) ); | 
|  | 1256                         } | 
|  | 1257                 }; | 
|  | 1258         }, | 
|  | 1259 | 
|  | 1260         // Exports test.push() to the user API | 
|  | 1261         push: function( /* result, actual, expected, message */ ) { | 
|  | 1262                 var assert = this, | 
|  | 1263                         currentTest = ( assert instanceof Assert && assert.test 
      ) || QUnit.config.current; | 
|  | 1264 | 
|  | 1265                 // Backwards compatibility fix. | 
|  | 1266                 // Allows the direct use of global exported assertions and QUnit
      .assert.* | 
|  | 1267                 // Although, it's use is not recommended as it can leak assertio
      ns | 
|  | 1268                 // to other tests from async tests, because we only get a refere
      nce to the current test, | 
|  | 1269                 // not exactly the test where assertion were intended to be call
      ed. | 
|  | 1270                 if ( !currentTest ) { | 
|  | 1271                         throw new Error( "assertion outside test context, in " +
       sourceFromStacktrace( 2 ) ); | 
|  | 1272                 } | 
|  | 1273 | 
|  | 1274                 if ( currentTest.usedAsync === true && currentTest.semaphore ===
       0 ) { | 
|  | 1275                         currentTest.pushFailure( "Assertion after the final `ass
      ert.async` was resolved", | 
|  | 1276                                 sourceFromStacktrace( 2 ) ); | 
|  | 1277 | 
|  | 1278                         // Allow this assertion to continue running anyway... | 
|  | 1279                 } | 
|  | 1280 | 
|  | 1281                 if ( !( assert instanceof Assert ) ) { | 
|  | 1282                         assert = currentTest.assert; | 
|  | 1283                 } | 
|  | 1284                 return assert.test.push.apply( assert.test, arguments ); | 
|  | 1285         }, | 
|  | 1286 | 
|  | 1287         /** | 
|  | 1288          * Asserts rough true-ish result. | 
|  | 1289          * @name ok | 
|  | 1290          * @function | 
|  | 1291          * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" 
      ); | 
|  | 1292          */ | 
|  | 1293         ok: function( result, message ) { | 
|  | 1294                 message = message || ( result ? "okay" : "failed, expected argum
      ent to be truthy, was: " + | 
|  | 1295                         QUnit.dump.parse( result ) ); | 
|  | 1296                 this.push( !!result, result, true, message ); | 
|  | 1297         }, | 
|  | 1298 | 
|  | 1299         /** | 
|  | 1300          * Assert that the first two arguments are equal, with an optional messa
      ge. | 
|  | 1301          * Prints out both actual and expected values. | 
|  | 1302          * @name equal | 
|  | 1303          * @function | 
|  | 1304          * @example equal( format( "{0} bytes.", 2), "2 bytes.", "replaces {0} w
      ith next argument" ); | 
|  | 1305          */ | 
|  | 1306         equal: function( actual, expected, message ) { | 
|  | 1307                 /*jshint eqeqeq:false */ | 
|  | 1308                 this.push( expected == actual, actual, expected, message ); | 
|  | 1309         }, | 
|  | 1310 | 
|  | 1311         /** | 
|  | 1312          * @name notEqual | 
|  | 1313          * @function | 
|  | 1314          */ | 
|  | 1315         notEqual: function( actual, expected, message ) { | 
|  | 1316                 /*jshint eqeqeq:false */ | 
|  | 1317                 this.push( expected != actual, actual, expected, message ); | 
|  | 1318         }, | 
|  | 1319 | 
|  | 1320         /** | 
|  | 1321          * @name propEqual | 
|  | 1322          * @function | 
|  | 1323          */ | 
|  | 1324         propEqual: function( actual, expected, message ) { | 
|  | 1325                 actual = objectValues( actual ); | 
|  | 1326                 expected = objectValues( expected ); | 
|  | 1327                 this.push( QUnit.equiv( actual, expected ), actual, expected, me
      ssage ); | 
|  | 1328         }, | 
|  | 1329 | 
|  | 1330         /** | 
|  | 1331          * @name notPropEqual | 
|  | 1332          * @function | 
|  | 1333          */ | 
|  | 1334         notPropEqual: function( actual, expected, message ) { | 
|  | 1335                 actual = objectValues( actual ); | 
|  | 1336                 expected = objectValues( expected ); | 
|  | 1337                 this.push( !QUnit.equiv( actual, expected ), actual, expected, m
      essage ); | 
|  | 1338         }, | 
|  | 1339 | 
|  | 1340         /** | 
|  | 1341          * @name deepEqual | 
|  | 1342          * @function | 
|  | 1343          */ | 
|  | 1344         deepEqual: function( actual, expected, message ) { | 
|  | 1345                 this.push( QUnit.equiv( actual, expected ), actual, expected, me
      ssage ); | 
|  | 1346         }, | 
|  | 1347 | 
|  | 1348         /** | 
|  | 1349          * @name notDeepEqual | 
|  | 1350          * @function | 
|  | 1351          */ | 
|  | 1352         notDeepEqual: function( actual, expected, message ) { | 
|  | 1353                 this.push( !QUnit.equiv( actual, expected ), actual, expected, m
      essage ); | 
|  | 1354         }, | 
|  | 1355 | 
|  | 1356         /** | 
|  | 1357          * @name strictEqual | 
|  | 1358          * @function | 
|  | 1359          */ | 
|  | 1360         strictEqual: function( actual, expected, message ) { | 
|  | 1361                 this.push( expected === actual, actual, expected, message ); | 
|  | 1362         }, | 
|  | 1363 | 
|  | 1364         /** | 
|  | 1365          * @name notStrictEqual | 
|  | 1366          * @function | 
|  | 1367          */ | 
|  | 1368         notStrictEqual: function( actual, expected, message ) { | 
|  | 1369                 this.push( expected !== actual, actual, expected, message ); | 
|  | 1370         }, | 
|  | 1371 | 
|  | 1372         "throws": function( block, expected, message ) { | 
|  | 1373                 var actual, expectedType, | 
|  | 1374                         expectedOutput = expected, | 
|  | 1375                         ok = false; | 
|  | 1376 | 
|  | 1377                 // 'expected' is optional unless doing string comparison | 
|  | 1378                 if ( message == null && typeof expected === "string" ) { | 
|  | 1379                         message = expected; | 
|  | 1380                         expected = null; | 
|  | 1381                 } | 
|  | 1382 | 
|  | 1383                 this.test.ignoreGlobalErrors = true; | 
|  | 1384                 try { | 
|  | 1385                         block.call( this.test.testEnvironment ); | 
|  | 1386                 } catch (e) { | 
|  | 1387                         actual = e; | 
|  | 1388                 } | 
|  | 1389                 this.test.ignoreGlobalErrors = false; | 
|  | 1390 | 
|  | 1391                 if ( actual ) { | 
|  | 1392                         expectedType = QUnit.objectType( expected ); | 
|  | 1393 | 
|  | 1394                         // we don't want to validate thrown error | 
|  | 1395                         if ( !expected ) { | 
|  | 1396                                 ok = true; | 
|  | 1397                                 expectedOutput = null; | 
|  | 1398 | 
|  | 1399                         // expected is a regexp | 
|  | 1400                         } else if ( expectedType === "regexp" ) { | 
|  | 1401                                 ok = expected.test( errorString( actual ) ); | 
|  | 1402 | 
|  | 1403                         // expected is a string | 
|  | 1404                         } else if ( expectedType === "string" ) { | 
|  | 1405                                 ok = expected === errorString( actual ); | 
|  | 1406 | 
|  | 1407                         // expected is a constructor, maybe an Error constructor | 
|  | 1408                         } else if ( expectedType === "function" && actual instan
      ceof expected ) { | 
|  | 1409                                 ok = true; | 
|  | 1410 | 
|  | 1411                         // expected is an Error object | 
|  | 1412                         } else if ( expectedType === "object" ) { | 
|  | 1413                                 ok = actual instanceof expected.constructor && | 
|  | 1414                                         actual.name === expected.name && | 
|  | 1415                                         actual.message === expected.message; | 
|  | 1416 | 
|  | 1417                         // expected is a validation function which returns true 
      if validation passed | 
|  | 1418                         } else if ( expectedType === "function" && expected.call
      ( {}, actual ) === true ) { | 
|  | 1419                                 expectedOutput = null; | 
|  | 1420                                 ok = true; | 
|  | 1421                         } | 
|  | 1422 | 
|  | 1423                         this.push( ok, actual, expectedOutput, message ); | 
|  | 1424                 } else { | 
|  | 1425                         this.test.pushFailure( message, null, "No exception was 
      thrown." ); | 
|  | 1426                 } | 
|  | 1427         } | 
|  | 1428 }; | 
|  | 1429 | 
|  | 1430 // Provide an alternative to assert.throws(), for enviroments that consider thro
      ws a reserved word | 
|  | 1431 // Known to us are: Closure Compiler, Narwhal | 
|  | 1432 (function() { | 
|  | 1433         /*jshint sub:true */ | 
|  | 1434         Assert.prototype.raises = Assert.prototype[ "throws" ]; | 
|  | 1435 }()); | 
|  | 1436 | 
|  | 1437 // Test for equality any JavaScript type. | 
|  | 1438 // Author: Philippe Rathé <prathe@gmail.com> | 
|  | 1439 QUnit.equiv = (function() { | 
|  | 1440 | 
|  | 1441         // Call the o related callback with the given arguments. | 
|  | 1442         function bindCallbacks( o, callbacks, args ) { | 
|  | 1443                 var prop = QUnit.objectType( o ); | 
|  | 1444                 if ( prop ) { | 
|  | 1445                         if ( QUnit.objectType( callbacks[ prop ] ) === "function
      " ) { | 
|  | 1446                                 return callbacks[ prop ].apply( callbacks, args 
      ); | 
|  | 1447                         } else { | 
|  | 1448                                 return callbacks[ prop ]; // or undefined | 
|  | 1449                         } | 
|  | 1450                 } | 
|  | 1451         } | 
|  | 1452 | 
|  | 1453         // the real equiv function | 
|  | 1454         var innerEquiv, | 
|  | 1455 | 
|  | 1456                 // stack to decide between skip/abort functions | 
|  | 1457                 callers = [], | 
|  | 1458 | 
|  | 1459                 // stack to avoiding loops from circular referencing | 
|  | 1460                 parents = [], | 
|  | 1461                 parentsB = [], | 
|  | 1462 | 
|  | 1463                 getProto = Object.getPrototypeOf || function( obj ) { | 
|  | 1464                         /* jshint camelcase: false, proto: true */ | 
|  | 1465                         return obj.__proto__; | 
|  | 1466                 }, | 
|  | 1467                 callbacks = (function() { | 
|  | 1468 | 
|  | 1469                         // for string, boolean, number and null | 
|  | 1470                         function useStrictEquality( b, a ) { | 
|  | 1471 | 
|  | 1472                                 /*jshint eqeqeq:false */ | 
|  | 1473                                 if ( b instanceof a.constructor || a instanceof 
      b.constructor ) { | 
|  | 1474 | 
|  | 1475                                         // to catch short annotation VS 'new' an
      notation of a | 
|  | 1476                                         // declaration | 
|  | 1477                                         // e.g. var i = 1; | 
|  | 1478                                         // var j = new Number(1); | 
|  | 1479                                         return a == b; | 
|  | 1480                                 } else { | 
|  | 1481                                         return a === b; | 
|  | 1482                                 } | 
|  | 1483                         } | 
|  | 1484 | 
|  | 1485                         return { | 
|  | 1486                                 "string": useStrictEquality, | 
|  | 1487                                 "boolean": useStrictEquality, | 
|  | 1488                                 "number": useStrictEquality, | 
|  | 1489                                 "null": useStrictEquality, | 
|  | 1490                                 "undefined": useStrictEquality, | 
|  | 1491 | 
|  | 1492                                 "nan": function( b ) { | 
|  | 1493                                         return isNaN( b ); | 
|  | 1494                                 }, | 
|  | 1495 | 
|  | 1496                                 "date": function( b, a ) { | 
|  | 1497                                         return QUnit.objectType( b ) === "date" 
      && a.valueOf() === b.valueOf(); | 
|  | 1498                                 }, | 
|  | 1499 | 
|  | 1500                                 "regexp": function( b, a ) { | 
|  | 1501                                         return QUnit.objectType( b ) === "regexp
      " && | 
|  | 1502 | 
|  | 1503                                                 // the regex itself | 
|  | 1504                                                 a.source === b.source && | 
|  | 1505 | 
|  | 1506                                                 // and its modifiers | 
|  | 1507                                                 a.global === b.global && | 
|  | 1508 | 
|  | 1509                                                 // (gmi) ... | 
|  | 1510                                                 a.ignoreCase === b.ignoreCase && | 
|  | 1511                                                 a.multiline === b.multiline && | 
|  | 1512                                                 a.sticky === b.sticky; | 
|  | 1513                                 }, | 
|  | 1514 | 
|  | 1515                                 // - skip when the property is a method of an in
      stance (OOP) | 
|  | 1516                                 // - abort otherwise, | 
|  | 1517                                 // initial === would have catch identical refere
      nces anyway | 
|  | 1518                                 "function": function() { | 
|  | 1519                                         var caller = callers[ callers.length - 1
       ]; | 
|  | 1520                                         return caller !== Object && typeof calle
      r !== "undefined"; | 
|  | 1521                                 }, | 
|  | 1522 | 
|  | 1523                                 "array": function( b, a ) { | 
|  | 1524                                         var i, j, len, loop, aCircular, bCircula
      r; | 
|  | 1525 | 
|  | 1526                                         // b could be an object literal here | 
|  | 1527                                         if ( QUnit.objectType( b ) !== "array" )
       { | 
|  | 1528                                                 return false; | 
|  | 1529                                         } | 
|  | 1530 | 
|  | 1531                                         len = a.length; | 
|  | 1532                                         if ( len !== b.length ) { | 
|  | 1533                                                 // safe and faster | 
|  | 1534                                                 return false; | 
|  | 1535                                         } | 
|  | 1536 | 
|  | 1537                                         // track reference to avoid circular ref
      erences | 
|  | 1538                                         parents.push( a ); | 
|  | 1539                                         parentsB.push( b ); | 
|  | 1540                                         for ( i = 0; i < len; i++ ) { | 
|  | 1541                                                 loop = false; | 
|  | 1542                                                 for ( j = 0; j < parents.length;
       j++ ) { | 
|  | 1543                                                         aCircular = parents[ j ]
       === a[ i ]; | 
|  | 1544                                                         bCircular = parentsB[ j 
      ] === b[ i ]; | 
|  | 1545                                                         if ( aCircular || bCircu
      lar ) { | 
|  | 1546                                                                 if ( a[ i ] === 
      b[ i ] || aCircular && bCircular ) { | 
|  | 1547                                                                         loop = t
      rue; | 
|  | 1548                                                                 } else { | 
|  | 1549                                                                         parents.
      pop(); | 
|  | 1550                                                                         parentsB
      .pop(); | 
|  | 1551                                                                         return f
      alse; | 
|  | 1552                                                                 } | 
|  | 1553                                                         } | 
|  | 1554                                                 } | 
|  | 1555                                                 if ( !loop && !innerEquiv( a[ i 
      ], b[ i ] ) ) { | 
|  | 1556                                                         parents.pop(); | 
|  | 1557                                                         parentsB.pop(); | 
|  | 1558                                                         return false; | 
|  | 1559                                                 } | 
|  | 1560                                         } | 
|  | 1561                                         parents.pop(); | 
|  | 1562                                         parentsB.pop(); | 
|  | 1563                                         return true; | 
|  | 1564                                 }, | 
|  | 1565 | 
|  | 1566                                 "object": function( b, a ) { | 
|  | 1567 | 
|  | 1568                                         /*jshint forin:false */ | 
|  | 1569                                         var i, j, loop, aCircular, bCircular, | 
|  | 1570                                                 // Default to true | 
|  | 1571                                                 eq = true, | 
|  | 1572                                                 aProperties = [], | 
|  | 1573                                                 bProperties = []; | 
|  | 1574 | 
|  | 1575                                         // comparing constructors is more strict
       than using | 
|  | 1576                                         // instanceof | 
|  | 1577                                         if ( a.constructor !== b.constructor ) { | 
|  | 1578 | 
|  | 1579                                                 // Allow objects with no prototy
      pe to be equivalent to | 
|  | 1580                                                 // objects with Object as their 
      constructor. | 
|  | 1581                                                 if ( !( ( getProto( a ) === null
       && getProto( b ) === Object.prototype ) || | 
|  | 1582                                                         ( getProto( b ) === null
       && getProto( a ) === Object.prototype ) ) ) { | 
|  | 1583                                                         return false; | 
|  | 1584                                                 } | 
|  | 1585                                         } | 
|  | 1586 | 
|  | 1587                                         // stack constructor before traversing p
      roperties | 
|  | 1588                                         callers.push( a.constructor ); | 
|  | 1589 | 
|  | 1590                                         // track reference to avoid circular ref
      erences | 
|  | 1591                                         parents.push( a ); | 
|  | 1592                                         parentsB.push( b ); | 
|  | 1593 | 
|  | 1594                                         // be strict: don't ensure hasOwnPropert
      y and go deep | 
|  | 1595                                         for ( i in a ) { | 
|  | 1596                                                 loop = false; | 
|  | 1597                                                 for ( j = 0; j < parents.length;
       j++ ) { | 
|  | 1598                                                         aCircular = parents[ j ]
       === a[ i ]; | 
|  | 1599                                                         bCircular = parentsB[ j 
      ] === b[ i ]; | 
|  | 1600                                                         if ( aCircular || bCircu
      lar ) { | 
|  | 1601                                                                 if ( a[ i ] === 
      b[ i ] || aCircular && bCircular ) { | 
|  | 1602                                                                         loop = t
      rue; | 
|  | 1603                                                                 } else { | 
|  | 1604                                                                         eq = fal
      se; | 
|  | 1605                                                                         break; | 
|  | 1606                                                                 } | 
|  | 1607                                                         } | 
|  | 1608                                                 } | 
|  | 1609                                                 aProperties.push( i ); | 
|  | 1610                                                 if ( !loop && !innerEquiv( a[ i 
      ], b[ i ] ) ) { | 
|  | 1611                                                         eq = false; | 
|  | 1612                                                         break; | 
|  | 1613                                                 } | 
|  | 1614                                         } | 
|  | 1615 | 
|  | 1616                                         parents.pop(); | 
|  | 1617                                         parentsB.pop(); | 
|  | 1618                                         callers.pop(); // unstack, we are done | 
|  | 1619 | 
|  | 1620                                         for ( i in b ) { | 
|  | 1621                                                 bProperties.push( i ); // collec
      t b's properties | 
|  | 1622                                         } | 
|  | 1623 | 
|  | 1624                                         // Ensures identical properties name | 
|  | 1625                                         return eq && innerEquiv( aProperties.sor
      t(), bProperties.sort() ); | 
|  | 1626                                 } | 
|  | 1627                         }; | 
|  | 1628                 }()); | 
|  | 1629 | 
|  | 1630         innerEquiv = function() { // can take multiple arguments | 
|  | 1631                 var args = [].slice.apply( arguments ); | 
|  | 1632                 if ( args.length < 2 ) { | 
|  | 1633                         return true; // end transition | 
|  | 1634                 } | 
|  | 1635 | 
|  | 1636                 return ( (function( a, b ) { | 
|  | 1637                         if ( a === b ) { | 
|  | 1638                                 return true; // catch the most you can | 
|  | 1639                         } else if ( a === null || b === null || typeof a === "un
      defined" || | 
|  | 1640                                         typeof b === "undefined" || | 
|  | 1641                                         QUnit.objectType( a ) !== QUnit.objectTy
      pe( b ) ) { | 
|  | 1642 | 
|  | 1643                                 // don't lose time with error prone cases | 
|  | 1644                                 return false; | 
|  | 1645                         } else { | 
|  | 1646                                 return bindCallbacks( a, callbacks, [ b, a ] ); | 
|  | 1647                         } | 
|  | 1648 | 
|  | 1649                         // apply transition with (1..n) arguments | 
|  | 1650                 }( args[ 0 ], args[ 1 ] ) ) && | 
|  | 1651                         innerEquiv.apply( this, args.splice( 1, args.length - 1 
      ) ) ); | 
|  | 1652         }; | 
|  | 1653 | 
|  | 1654         return innerEquiv; | 
|  | 1655 }()); | 
|  | 1656 | 
|  | 1657 // Based on jsDump by Ariel Flesler | 
|  | 1658 // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html | 
|  | 1659 QUnit.dump = (function() { | 
|  | 1660         function quote( str ) { | 
|  | 1661                 return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\""; | 
|  | 1662         } | 
|  | 1663         function literal( o ) { | 
|  | 1664                 return o + ""; | 
|  | 1665         } | 
|  | 1666         function join( pre, arr, post ) { | 
|  | 1667                 var s = dump.separator(), | 
|  | 1668                         base = dump.indent(), | 
|  | 1669                         inner = dump.indent( 1 ); | 
|  | 1670                 if ( arr.join ) { | 
|  | 1671                         arr = arr.join( "," + s + inner ); | 
|  | 1672                 } | 
|  | 1673                 if ( !arr ) { | 
|  | 1674                         return pre + post; | 
|  | 1675                 } | 
|  | 1676                 return [ pre, inner + arr, base + post ].join( s ); | 
|  | 1677         } | 
|  | 1678         function array( arr, stack ) { | 
|  | 1679                 var i = arr.length, | 
|  | 1680                         ret = new Array( i ); | 
|  | 1681 | 
|  | 1682                 if ( dump.maxDepth && dump.depth > dump.maxDepth ) { | 
|  | 1683                         return "[object Array]"; | 
|  | 1684                 } | 
|  | 1685 | 
|  | 1686                 this.up(); | 
|  | 1687                 while ( i-- ) { | 
|  | 1688                         ret[ i ] = this.parse( arr[ i ], undefined, stack ); | 
|  | 1689                 } | 
|  | 1690                 this.down(); | 
|  | 1691                 return join( "[", ret, "]" ); | 
|  | 1692         } | 
|  | 1693 | 
|  | 1694         var reName = /^function (\w+)/, | 
|  | 1695                 dump = { | 
|  | 1696 | 
|  | 1697                         // objType is used mostly internally, you can fix a (cus
      tom) type in advance | 
|  | 1698                         parse: function( obj, objType, stack ) { | 
|  | 1699                                 stack = stack || []; | 
|  | 1700                                 var res, parser, parserType, | 
|  | 1701                                         inStack = inArray( obj, stack ); | 
|  | 1702 | 
|  | 1703                                 if ( inStack !== -1 ) { | 
|  | 1704                                         return "recursion(" + ( inStack - stack.
      length ) + ")"; | 
|  | 1705                                 } | 
|  | 1706 | 
|  | 1707                                 objType = objType || this.typeOf( obj  ); | 
|  | 1708                                 parser = this.parsers[ objType ]; | 
|  | 1709                                 parserType = typeof parser; | 
|  | 1710 | 
|  | 1711                                 if ( parserType === "function" ) { | 
|  | 1712                                         stack.push( obj ); | 
|  | 1713                                         res = parser.call( this, obj, stack ); | 
|  | 1714                                         stack.pop(); | 
|  | 1715                                         return res; | 
|  | 1716                                 } | 
|  | 1717                                 return ( parserType === "string" ) ? parser : th
      is.parsers.error; | 
|  | 1718                         }, | 
|  | 1719                         typeOf: function( obj ) { | 
|  | 1720                                 var type; | 
|  | 1721                                 if ( obj === null ) { | 
|  | 1722                                         type = "null"; | 
|  | 1723                                 } else if ( typeof obj === "undefined" ) { | 
|  | 1724                                         type = "undefined"; | 
|  | 1725                                 } else if ( QUnit.is( "regexp", obj ) ) { | 
|  | 1726                                         type = "regexp"; | 
|  | 1727                                 } else if ( QUnit.is( "date", obj ) ) { | 
|  | 1728                                         type = "date"; | 
|  | 1729                                 } else if ( QUnit.is( "function", obj ) ) { | 
|  | 1730                                         type = "function"; | 
|  | 1731                                 } else if ( obj.setInterval !== undefined && | 
|  | 1732                                                 obj.document !== undefined && | 
|  | 1733                                                 obj.nodeType === undefined ) { | 
|  | 1734                                         type = "window"; | 
|  | 1735                                 } else if ( obj.nodeType === 9 ) { | 
|  | 1736                                         type = "document"; | 
|  | 1737                                 } else if ( obj.nodeType ) { | 
|  | 1738                                         type = "node"; | 
|  | 1739                                 } else if ( | 
|  | 1740 | 
|  | 1741                                         // native arrays | 
|  | 1742                                         toString.call( obj ) === "[object Array]
      " || | 
|  | 1743 | 
|  | 1744                                         // NodeList objects | 
|  | 1745                                         ( typeof obj.length === "number" && obj.
      item !== undefined && | 
|  | 1746                                         ( obj.length ? obj.item( 0 ) === obj[ 0 
      ] : ( obj.item( 0 ) === null && | 
|  | 1747                                         obj[ 0 ] === undefined ) ) ) | 
|  | 1748                                 ) { | 
|  | 1749                                         type = "array"; | 
|  | 1750                                 } else if ( obj.constructor === Error.prototype.
      constructor ) { | 
|  | 1751                                         type = "error"; | 
|  | 1752                                 } else { | 
|  | 1753                                         type = typeof obj; | 
|  | 1754                                 } | 
|  | 1755                                 return type; | 
|  | 1756                         }, | 
|  | 1757                         separator: function() { | 
|  | 1758                                 return this.multiline ? this.HTML ? "<br />" : "
      \n" : this.HTML ? " " : " "; | 
|  | 1759                         }, | 
|  | 1760                         // extra can be a number, shortcut for increasing-callin
      g-decreasing | 
|  | 1761                         indent: function( extra ) { | 
|  | 1762                                 if ( !this.multiline ) { | 
|  | 1763                                         return ""; | 
|  | 1764                                 } | 
|  | 1765                                 var chr = this.indentChar; | 
|  | 1766                                 if ( this.HTML ) { | 
|  | 1767                                         chr = chr.replace( /\t/g, "   " ).replac
      e( / /g, " " ); | 
|  | 1768                                 } | 
|  | 1769                                 return new Array( this.depth + ( extra || 0 ) ).
      join( chr ); | 
|  | 1770                         }, | 
|  | 1771                         up: function( a ) { | 
|  | 1772                                 this.depth += a || 1; | 
|  | 1773                         }, | 
|  | 1774                         down: function( a ) { | 
|  | 1775                                 this.depth -= a || 1; | 
|  | 1776                         }, | 
|  | 1777                         setParser: function( name, parser ) { | 
|  | 1778                                 this.parsers[ name ] = parser; | 
|  | 1779                         }, | 
|  | 1780                         // The next 3 are exposed so you can use them | 
|  | 1781                         quote: quote, | 
|  | 1782                         literal: literal, | 
|  | 1783                         join: join, | 
|  | 1784                         // | 
|  | 1785                         depth: 1, | 
|  | 1786                         maxDepth: 5, | 
|  | 1787 | 
|  | 1788                         // This is the list of parsers, to modify them, use dump
      .setParser | 
|  | 1789                         parsers: { | 
|  | 1790                                 window: "[Window]", | 
|  | 1791                                 document: "[Document]", | 
|  | 1792                                 error: function( error ) { | 
|  | 1793                                         return "Error(\"" + error.message + "\")
      "; | 
|  | 1794                                 }, | 
|  | 1795                                 unknown: "[Unknown]", | 
|  | 1796                                 "null": "null", | 
|  | 1797                                 "undefined": "undefined", | 
|  | 1798                                 "function": function( fn ) { | 
|  | 1799                                         var ret = "function", | 
|  | 1800 | 
|  | 1801                                                 // functions never have name in 
      IE | 
|  | 1802                                                 name = "name" in fn ? fn.name : 
      ( reName.exec( fn ) || [] )[ 1 ]; | 
|  | 1803 | 
|  | 1804                                         if ( name ) { | 
|  | 1805                                                 ret += " " + name; | 
|  | 1806                                         } | 
|  | 1807                                         ret += "( "; | 
|  | 1808 | 
|  | 1809                                         ret = [ ret, dump.parse( fn, "functionAr
      gs" ), "){" ].join( "" ); | 
|  | 1810                                         return join( ret, dump.parse( fn, "funct
      ionCode" ), "}" ); | 
|  | 1811                                 }, | 
|  | 1812                                 array: array, | 
|  | 1813                                 nodelist: array, | 
|  | 1814                                 "arguments": array, | 
|  | 1815                                 object: function( map, stack ) { | 
|  | 1816                                         var keys, key, val, i, nonEnumerableProp
      erties, | 
|  | 1817                                                 ret = []; | 
|  | 1818 | 
|  | 1819                                         if ( dump.maxDepth && dump.depth > dump.
      maxDepth ) { | 
|  | 1820                                                 return "[object Object]"; | 
|  | 1821                                         } | 
|  | 1822 | 
|  | 1823                                         dump.up(); | 
|  | 1824                                         keys = []; | 
|  | 1825                                         for ( key in map ) { | 
|  | 1826                                                 keys.push( key ); | 
|  | 1827                                         } | 
|  | 1828 | 
|  | 1829                                         // Some properties are not always enumer
      able on Error objects. | 
|  | 1830                                         nonEnumerableProperties = [ "message", "
      name" ]; | 
|  | 1831                                         for ( i in nonEnumerableProperties ) { | 
|  | 1832                                                 key = nonEnumerableProperties[ i
       ]; | 
|  | 1833                                                 if ( key in map && !( key in key
      s ) ) { | 
|  | 1834                                                         keys.push( key ); | 
|  | 1835                                                 } | 
|  | 1836                                         } | 
|  | 1837                                         keys.sort(); | 
|  | 1838                                         for ( i = 0; i < keys.length; i++ ) { | 
|  | 1839                                                 key = keys[ i ]; | 
|  | 1840                                                 val = map[ key ]; | 
|  | 1841                                                 ret.push( dump.parse( key, "key"
       ) + ": " + | 
|  | 1842                                                         dump.parse( val, undefin
      ed, stack ) ); | 
|  | 1843                                         } | 
|  | 1844                                         dump.down(); | 
|  | 1845                                         return join( "{", ret, "}" ); | 
|  | 1846                                 }, | 
|  | 1847                                 node: function( node ) { | 
|  | 1848                                         var len, i, val, | 
|  | 1849                                                 open = dump.HTML ? "<" : "<", | 
|  | 1850                                                 close = dump.HTML ? ">" : ">"
      , | 
|  | 1851                                                 tag = node.nodeName.toLowerCase(
      ), | 
|  | 1852                                                 ret = open + tag, | 
|  | 1853                                                 attrs = node.attributes; | 
|  | 1854 | 
|  | 1855                                         if ( attrs ) { | 
|  | 1856                                                 for ( i = 0, len = attrs.length;
       i < len; i++ ) { | 
|  | 1857                                                         val = attrs[ i ].nodeVal
      ue; | 
|  | 1858 | 
|  | 1859                                                         // IE6 includes all attr
      ibutes in .attributes, even ones not explicitly | 
|  | 1860                                                         // set. Those have value
      s like undefined, null, 0, false, "" or | 
|  | 1861                                                         // "inherit". | 
|  | 1862                                                         if ( val && val !== "inh
      erit" ) { | 
|  | 1863                                                                 ret += " " + att
      rs[ i ].nodeName + "=" + | 
|  | 1864                                                                         dump.par
      se( val, "attribute" ); | 
|  | 1865                                                         } | 
|  | 1866                                                 } | 
|  | 1867                                         } | 
|  | 1868                                         ret += close; | 
|  | 1869 | 
|  | 1870                                         // Show content of TextNode or CDATASect
      ion | 
|  | 1871                                         if ( node.nodeType === 3 || node.nodeTyp
      e === 4 ) { | 
|  | 1872                                                 ret += node.nodeValue; | 
|  | 1873                                         } | 
|  | 1874 | 
|  | 1875                                         return ret + open + "/" + tag + close; | 
|  | 1876                                 }, | 
|  | 1877 | 
|  | 1878                                 // function calls it internally, it's the argume
      nts part of the function | 
|  | 1879                                 functionArgs: function( fn ) { | 
|  | 1880                                         var args, | 
|  | 1881                                                 l = fn.length; | 
|  | 1882 | 
|  | 1883                                         if ( !l ) { | 
|  | 1884                                                 return ""; | 
|  | 1885                                         } | 
|  | 1886 | 
|  | 1887                                         args = new Array( l ); | 
|  | 1888                                         while ( l-- ) { | 
|  | 1889 | 
|  | 1890                                                 // 97 is 'a' | 
|  | 1891                                                 args[ l ] = String.fromCharCode(
       97 + l ); | 
|  | 1892                                         } | 
|  | 1893                                         return " " + args.join( ", " ) + " "; | 
|  | 1894                                 }, | 
|  | 1895                                 // object calls it internally, the key part of a
      n item in a map | 
|  | 1896                                 key: quote, | 
|  | 1897                                 // function calls it internally, it's the conten
      t of the function | 
|  | 1898                                 functionCode: "[code]", | 
|  | 1899                                 // node calls it internally, it's an html attrib
      ute value | 
|  | 1900                                 attribute: quote, | 
|  | 1901                                 string: quote, | 
|  | 1902                                 date: quote, | 
|  | 1903                                 regexp: literal, | 
|  | 1904                                 number: literal, | 
|  | 1905                                 "boolean": literal | 
|  | 1906                         }, | 
|  | 1907                         // if true, entities are escaped ( <, >, \t, space and \
      n ) | 
|  | 1908                         HTML: false, | 
|  | 1909                         // indentation unit | 
|  | 1910                         indentChar: "  ", | 
|  | 1911                         // if true, items in a collection, are separated by a \n
      , else just a space. | 
|  | 1912                         multiline: true | 
|  | 1913                 }; | 
|  | 1914 | 
|  | 1915         return dump; | 
|  | 1916 }()); | 
|  | 1917 | 
|  | 1918 // back compat | 
|  | 1919 QUnit.jsDump = QUnit.dump; | 
|  | 1920 | 
|  | 1921 // For browser, export only select globals | 
|  | 1922 if ( typeof window !== "undefined" ) { | 
|  | 1923 | 
|  | 1924         // Deprecated | 
|  | 1925         // Extend assert methods to QUnit and Global scope through Backwards com
      patibility | 
|  | 1926         (function() { | 
|  | 1927                 var i, | 
|  | 1928                         assertions = Assert.prototype; | 
|  | 1929 | 
|  | 1930                 function applyCurrent( current ) { | 
|  | 1931                         return function() { | 
|  | 1932                                 var assert = new Assert( QUnit.config.current ); | 
|  | 1933                                 current.apply( assert, arguments ); | 
|  | 1934                         }; | 
|  | 1935                 } | 
|  | 1936 | 
|  | 1937                 for ( i in assertions ) { | 
|  | 1938                         QUnit[ i ] = applyCurrent( assertions[ i ] ); | 
|  | 1939                 } | 
|  | 1940         })(); | 
|  | 1941 | 
|  | 1942         (function() { | 
|  | 1943                 var i, l, | 
|  | 1944                         keys = [ | 
|  | 1945                                 "test", | 
|  | 1946                                 "module", | 
|  | 1947                                 "expect", | 
|  | 1948                                 "asyncTest", | 
|  | 1949                                 "start", | 
|  | 1950                                 "stop", | 
|  | 1951                                 "ok", | 
|  | 1952                                 "equal", | 
|  | 1953                                 "notEqual", | 
|  | 1954                                 "propEqual", | 
|  | 1955                                 "notPropEqual", | 
|  | 1956                                 "deepEqual", | 
|  | 1957                                 "notDeepEqual", | 
|  | 1958                                 "strictEqual", | 
|  | 1959                                 "notStrictEqual", | 
|  | 1960                                 "throws" | 
|  | 1961                         ]; | 
|  | 1962 | 
|  | 1963                 for ( i = 0, l = keys.length; i < l; i++ ) { | 
|  | 1964                         window[ keys[ i ] ] = QUnit[ keys[ i ] ]; | 
|  | 1965                 } | 
|  | 1966         })(); | 
|  | 1967 | 
|  | 1968         window.QUnit = QUnit; | 
|  | 1969 } | 
|  | 1970 | 
|  | 1971 // For nodejs | 
|  | 1972 if ( typeof module !== "undefined" && module && module.exports ) { | 
|  | 1973         module.exports = QUnit; | 
|  | 1974 | 
|  | 1975         // For consistency with CommonJS environments' exports | 
|  | 1976         module.exports.QUnit = QUnit; | 
|  | 1977 } | 
|  | 1978 | 
|  | 1979 // For CommonJS with exports, but without module.exports, like Rhino | 
|  | 1980 if ( typeof exports !== "undefined" && exports ) { | 
|  | 1981         exports.QUnit = QUnit; | 
|  | 1982 } | 
|  | 1983 | 
|  | 1984 // Get a reference to the global object, like window in browsers | 
|  | 1985 }( (function() { | 
|  | 1986         return this; | 
|  | 1987 })() )); | 
|  | 1988 | 
|  | 1989 /*istanbul ignore next */ | 
|  | 1990 // jscs:disable maximumLineLength | 
|  | 1991 /* | 
|  | 1992  * Javascript Diff Algorithm | 
|  | 1993  *  By John Resig (http://ejohn.org/) | 
|  | 1994  *  Modified by Chu Alan "sprite" | 
|  | 1995  * | 
|  | 1996  * Released under the MIT license. | 
|  | 1997  * | 
|  | 1998  * More Info: | 
|  | 1999  *  http://ejohn.org/projects/javascript-diff-algorithm/ | 
|  | 2000  * | 
|  | 2001  * Usage: QUnit.diff(expected, actual) | 
|  | 2002  * | 
|  | 2003  * 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" | 
|  | 2004  */ | 
|  | 2005 QUnit.diff = (function() { | 
|  | 2006         var hasOwn = Object.prototype.hasOwnProperty; | 
|  | 2007 | 
|  | 2008         /*jshint eqeqeq:false, eqnull:true */ | 
|  | 2009         function diff( o, n ) { | 
|  | 2010                 var i, | 
|  | 2011                         ns = {}, | 
|  | 2012                         os = {}; | 
|  | 2013 | 
|  | 2014                 for ( i = 0; i < n.length; i++ ) { | 
|  | 2015                         if ( !hasOwn.call( ns, n[ i ] ) ) { | 
|  | 2016                                 ns[ n[ i ] ] = { | 
|  | 2017                                         rows: [], | 
|  | 2018                                         o: null | 
|  | 2019                                 }; | 
|  | 2020                         } | 
|  | 2021                         ns[ n[ i ] ].rows.push( i ); | 
|  | 2022                 } | 
|  | 2023 | 
|  | 2024                 for ( i = 0; i < o.length; i++ ) { | 
|  | 2025                         if ( !hasOwn.call( os, o[ i ] ) ) { | 
|  | 2026                                 os[ o[ i ] ] = { | 
|  | 2027                                         rows: [], | 
|  | 2028                                         n: null | 
|  | 2029                                 }; | 
|  | 2030                         } | 
|  | 2031                         os[ o[ i ] ].rows.push( i ); | 
|  | 2032                 } | 
|  | 2033 | 
|  | 2034                 for ( i in ns ) { | 
|  | 2035                         if ( hasOwn.call( ns, i ) ) { | 
|  | 2036                                 if ( ns[ i ].rows.length === 1 && hasOwn.call( o
      s, i ) && os[ i ].rows.length === 1 ) { | 
|  | 2037                                         n[ ns[ i ].rows[ 0 ] ] = { | 
|  | 2038                                                 text: n[ ns[ i ].rows[ 0 ] ], | 
|  | 2039                                                 row: os[ i ].rows[ 0 ] | 
|  | 2040                                         }; | 
|  | 2041                                         o[ os[ i ].rows[ 0 ] ] = { | 
|  | 2042                                                 text: o[ os[ i ].rows[ 0 ] ], | 
|  | 2043                                                 row: ns[ i ].rows[ 0 ] | 
|  | 2044                                         }; | 
|  | 2045                                 } | 
|  | 2046                         } | 
|  | 2047                 } | 
|  | 2048 | 
|  | 2049                 for ( i = 0; i < n.length - 1; i++ ) { | 
|  | 2050                         if ( n[ i ].text != null && n[ i + 1 ].text == null && n
      [ i ].row + 1 < o.length && o[ n[ i ].row + 1 ].text == null && | 
|  | 2051                                 n[ i + 1 ] == o[ n[ i ].row + 1 ] ) { | 
|  | 2052 | 
|  | 2053                                 n[ i + 1 ] = { | 
|  | 2054                                         text: n[ i + 1 ], | 
|  | 2055                                         row: n[ i ].row + 1 | 
|  | 2056                                 }; | 
|  | 2057                                 o[ n[ i ].row + 1 ] = { | 
|  | 2058                                         text: o[ n[ i ].row + 1 ], | 
|  | 2059                                         row: i + 1 | 
|  | 2060                                 }; | 
|  | 2061                         } | 
|  | 2062                 } | 
|  | 2063 | 
|  | 2064                 for ( i = n.length - 1; i > 0; i-- ) { | 
|  | 2065                         if ( n[ i ].text != null && n[ i - 1 ].text == null && n
      [ i ].row > 0 && o[ n[ i ].row - 1 ].text == null && | 
|  | 2066                                 n[ i - 1 ] == o[ n[ i ].row - 1 ] ) { | 
|  | 2067 | 
|  | 2068                                 n[ i - 1 ] = { | 
|  | 2069                                         text: n[ i - 1 ], | 
|  | 2070                                         row: n[ i ].row - 1 | 
|  | 2071                                 }; | 
|  | 2072                                 o[ n[ i ].row - 1 ] = { | 
|  | 2073                                         text: o[ n[ i ].row - 1 ], | 
|  | 2074                                         row: i - 1 | 
|  | 2075                                 }; | 
|  | 2076                         } | 
|  | 2077                 } | 
|  | 2078 | 
|  | 2079                 return { | 
|  | 2080                         o: o, | 
|  | 2081                         n: n | 
|  | 2082                 }; | 
|  | 2083         } | 
|  | 2084 | 
|  | 2085         return function( o, n ) { | 
|  | 2086                 o = o.replace( /\s+$/, "" ); | 
|  | 2087                 n = n.replace( /\s+$/, "" ); | 
|  | 2088 | 
|  | 2089                 var i, pre, | 
|  | 2090                         str = "", | 
|  | 2091                         out = diff( o === "" ? [] : o.split( /\s+/ ), n === "" ?
       [] : n.split( /\s+/ ) ), | 
|  | 2092                         oSpace = o.match( /\s+/g ), | 
|  | 2093                         nSpace = n.match( /\s+/g ); | 
|  | 2094 | 
|  | 2095                 if ( oSpace == null ) { | 
|  | 2096                         oSpace = [ " " ]; | 
|  | 2097                 } else { | 
|  | 2098                         oSpace.push( " " ); | 
|  | 2099                 } | 
|  | 2100 | 
|  | 2101                 if ( nSpace == null ) { | 
|  | 2102                         nSpace = [ " " ]; | 
|  | 2103                 } else { | 
|  | 2104                         nSpace.push( " " ); | 
|  | 2105                 } | 
|  | 2106 | 
|  | 2107                 if ( out.n.length === 0 ) { | 
|  | 2108                         for ( i = 0; i < out.o.length; i++ ) { | 
|  | 2109                                 str += "<del>" + out.o[ i ] + oSpace[ i ] + "</d
      el>"; | 
|  | 2110                         } | 
|  | 2111                 } else { | 
|  | 2112                         if ( out.n[ 0 ].text == null ) { | 
|  | 2113                                 for ( n = 0; n < out.o.length && out.o[ n ].text
       == null; n++ ) { | 
|  | 2114                                         str += "<del>" + out.o[ n ] + oSpace[ n 
      ] + "</del>"; | 
|  | 2115                                 } | 
|  | 2116                         } | 
|  | 2117 | 
|  | 2118                         for ( i = 0; i < out.n.length; i++ ) { | 
|  | 2119                                 if ( out.n[ i ].text == null ) { | 
|  | 2120                                         str += "<ins>" + out.n[ i ] + nSpace[ i 
      ] + "</ins>"; | 
|  | 2121                                 } else { | 
|  | 2122 | 
|  | 2123                                         // `pre` initialized at top of scope | 
|  | 2124                                         pre = ""; | 
|  | 2125 | 
|  | 2126                                         for ( n = out.n[ i ].row + 1; n < out.o.
      length && out.o[ n ].text == null; n++ ) { | 
|  | 2127                                                 pre += "<del>" + out.o[ n ] + oS
      pace[ n ] + "</del>"; | 
|  | 2128                                         } | 
|  | 2129                                         str += " " + out.n[ i ].text + nSpace[ i
       ] + pre; | 
|  | 2130                                 } | 
|  | 2131                         } | 
|  | 2132                 } | 
|  | 2133 | 
|  | 2134                 return str; | 
|  | 2135         }; | 
|  | 2136 }()); | 
|  | 2137 // jscs:enable | 
|  | 2138 | 
|  | 2139 (function() { | 
|  | 2140 | 
|  | 2141 // Deprecated QUnit.init - Ref #530 | 
|  | 2142 // Re-initialize the configuration options | 
|  | 2143 QUnit.init = function() { | 
|  | 2144         var tests, banner, result, qunit, | 
|  | 2145                 config = QUnit.config; | 
|  | 2146 | 
|  | 2147         config.stats = { all: 0, bad: 0 }; | 
|  | 2148         config.moduleStats = { all: 0, bad: 0 }; | 
|  | 2149         config.started = 0; | 
|  | 2150         config.updateRate = 1000; | 
|  | 2151         config.blocking = false; | 
|  | 2152         config.autostart = true; | 
|  | 2153         config.autorun = false; | 
|  | 2154         config.filter = ""; | 
|  | 2155         config.queue = []; | 
|  | 2156 | 
|  | 2157         // Return on non-browser environments | 
|  | 2158         // This is necessary to not break on node tests | 
|  | 2159         if ( typeof window === "undefined" ) { | 
|  | 2160                 return; | 
|  | 2161         } | 
|  | 2162 | 
|  | 2163         qunit = id( "qunit" ); | 
|  | 2164         if ( qunit ) { | 
|  | 2165                 qunit.innerHTML = | 
|  | 2166                         "<h1 id='qunit-header'>" + escapeText( document.title ) 
      + "</h1>" + | 
|  | 2167                         "<h2 id='qunit-banner'></h2>" + | 
|  | 2168                         "<div id='qunit-testrunner-toolbar'></div>" + | 
|  | 2169                         "<h2 id='qunit-userAgent'></h2>" + | 
|  | 2170                         "<ol id='qunit-tests'></ol>"; | 
|  | 2171         } | 
|  | 2172 | 
|  | 2173         tests = id( "qunit-tests" ); | 
|  | 2174         banner = id( "qunit-banner" ); | 
|  | 2175         result = id( "qunit-testresult" ); | 
|  | 2176 | 
|  | 2177         if ( tests ) { | 
|  | 2178                 tests.innerHTML = ""; | 
|  | 2179         } | 
|  | 2180 | 
|  | 2181         if ( banner ) { | 
|  | 2182                 banner.className = ""; | 
|  | 2183         } | 
|  | 2184 | 
|  | 2185         if ( result ) { | 
|  | 2186                 result.parentNode.removeChild( result ); | 
|  | 2187         } | 
|  | 2188 | 
|  | 2189         if ( tests ) { | 
|  | 2190                 result = document.createElement( "p" ); | 
|  | 2191                 result.id = "qunit-testresult"; | 
|  | 2192                 result.className = "result"; | 
|  | 2193                 tests.parentNode.insertBefore( result, tests ); | 
|  | 2194                 result.innerHTML = "Running...<br /> "; | 
|  | 2195         } | 
|  | 2196 }; | 
|  | 2197 | 
|  | 2198 // Don't load the HTML Reporter on non-Browser environments | 
|  | 2199 if ( typeof window === "undefined" ) { | 
|  | 2200         return; | 
|  | 2201 } | 
|  | 2202 | 
|  | 2203 var config = QUnit.config, | 
|  | 2204         hasOwn = Object.prototype.hasOwnProperty, | 
|  | 2205         defined = { | 
|  | 2206                 document: window.document !== undefined, | 
|  | 2207                 sessionStorage: (function() { | 
|  | 2208                         var x = "qunit-test-string"; | 
|  | 2209                         try { | 
|  | 2210                                 sessionStorage.setItem( x, x ); | 
|  | 2211                                 sessionStorage.removeItem( x ); | 
|  | 2212                                 return true; | 
|  | 2213                         } catch ( e ) { | 
|  | 2214                                 return false; | 
|  | 2215                         } | 
|  | 2216                 }()) | 
|  | 2217         }, | 
|  | 2218         modulesList = []; | 
|  | 2219 | 
|  | 2220 /** | 
|  | 2221 * Escape text for attribute or text content. | 
|  | 2222 */ | 
|  | 2223 function escapeText( s ) { | 
|  | 2224         if ( !s ) { | 
|  | 2225                 return ""; | 
|  | 2226         } | 
|  | 2227         s = s + ""; | 
|  | 2228 | 
|  | 2229         // Both single quotes and double quotes (for attributes) | 
|  | 2230         return s.replace( /['"<>&]/g, function( s ) { | 
|  | 2231                 switch ( s ) { | 
|  | 2232                 case "'": | 
|  | 2233                         return "'"; | 
|  | 2234                 case "\"": | 
|  | 2235                         return """; | 
|  | 2236                 case "<": | 
|  | 2237                         return "<"; | 
|  | 2238                 case ">": | 
|  | 2239                         return ">"; | 
|  | 2240                 case "&": | 
|  | 2241                         return "&"; | 
|  | 2242                 } | 
|  | 2243         }); | 
|  | 2244 } | 
|  | 2245 | 
|  | 2246 /** | 
|  | 2247  * @param {HTMLElement} elem | 
|  | 2248  * @param {string} type | 
|  | 2249  * @param {Function} fn | 
|  | 2250  */ | 
|  | 2251 function addEvent( elem, type, fn ) { | 
|  | 2252         if ( elem.addEventListener ) { | 
|  | 2253 | 
|  | 2254                 // Standards-based browsers | 
|  | 2255                 elem.addEventListener( type, fn, false ); | 
|  | 2256         } else if ( elem.attachEvent ) { | 
|  | 2257 | 
|  | 2258                 // support: IE <9 | 
|  | 2259                 elem.attachEvent( "on" + type, fn ); | 
|  | 2260         } | 
|  | 2261 } | 
|  | 2262 | 
|  | 2263 /** | 
|  | 2264  * @param {Array|NodeList} elems | 
|  | 2265  * @param {string} type | 
|  | 2266  * @param {Function} fn | 
|  | 2267  */ | 
|  | 2268 function addEvents( elems, type, fn ) { | 
|  | 2269         var i = elems.length; | 
|  | 2270         while ( i-- ) { | 
|  | 2271                 addEvent( elems[ i ], type, fn ); | 
|  | 2272         } | 
|  | 2273 } | 
|  | 2274 | 
|  | 2275 function hasClass( elem, name ) { | 
|  | 2276         return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0; | 
|  | 2277 } | 
|  | 2278 | 
|  | 2279 function addClass( elem, name ) { | 
|  | 2280         if ( !hasClass( elem, name ) ) { | 
|  | 2281                 elem.className += ( elem.className ? " " : "" ) + name; | 
|  | 2282         } | 
|  | 2283 } | 
|  | 2284 | 
|  | 2285 function toggleClass( elem, name ) { | 
|  | 2286         if ( hasClass( elem, name ) ) { | 
|  | 2287                 removeClass( elem, name ); | 
|  | 2288         } else { | 
|  | 2289                 addClass( elem, name ); | 
|  | 2290         } | 
|  | 2291 } | 
|  | 2292 | 
|  | 2293 function removeClass( elem, name ) { | 
|  | 2294         var set = " " + elem.className + " "; | 
|  | 2295 | 
|  | 2296         // Class name may appear multiple times | 
|  | 2297         while ( set.indexOf( " " + name + " " ) >= 0 ) { | 
|  | 2298                 set = set.replace( " " + name + " ", " " ); | 
|  | 2299         } | 
|  | 2300 | 
|  | 2301         // trim for prettiness | 
|  | 2302         elem.className = typeof set.trim === "function" ? set.trim() : set.repla
      ce( /^\s+|\s+$/g, "" ); | 
|  | 2303 } | 
|  | 2304 | 
|  | 2305 function id( name ) { | 
|  | 2306         return defined.document && document.getElementById && document.getElemen
      tById( name ); | 
|  | 2307 } | 
|  | 2308 | 
|  | 2309 function getUrlConfigHtml() { | 
|  | 2310         var i, j, val, | 
|  | 2311                 escaped, escapedTooltip, | 
|  | 2312                 selection = false, | 
|  | 2313                 len = config.urlConfig.length, | 
|  | 2314                 urlConfigHtml = ""; | 
|  | 2315 | 
|  | 2316         for ( i = 0; i < len; i++ ) { | 
|  | 2317                 val = config.urlConfig[ i ]; | 
|  | 2318                 if ( typeof val === "string" ) { | 
|  | 2319                         val = { | 
|  | 2320                                 id: val, | 
|  | 2321                                 label: val | 
|  | 2322                         }; | 
|  | 2323                 } | 
|  | 2324 | 
|  | 2325                 escaped = escapeText( val.id ); | 
|  | 2326                 escapedTooltip = escapeText( val.tooltip ); | 
|  | 2327 | 
|  | 2328                 if ( config[ val.id ] === undefined ) { | 
|  | 2329                         config[ val.id ] = QUnit.urlParams[ val.id ]; | 
|  | 2330                 } | 
|  | 2331 | 
|  | 2332                 if ( !val.value || typeof val.value === "string" ) { | 
|  | 2333                         urlConfigHtml += "<input id='qunit-urlconfig-" + escaped
       + | 
|  | 2334                                 "' name='" + escaped + "' type='checkbox'" + | 
|  | 2335                                 ( val.value ? " value='" + escapeText( val.value
       ) + "'" : "" ) + | 
|  | 2336                                 ( config[ val.id ] ? " checked='checked'" : "" )
       + | 
|  | 2337                                 " title='" + escapedTooltip + "' /><label for='q
      unit-urlconfig-" + escaped + | 
|  | 2338                                 "' title='" + escapedTooltip + "'>" + val.label 
      + "</label>"; | 
|  | 2339                 } else { | 
|  | 2340                         urlConfigHtml += "<label for='qunit-urlconfig-" + escape
      d + | 
|  | 2341                                 "' title='" + escapedTooltip + "'>" + val.label 
      + | 
|  | 2342                                 ": </label><select id='qunit-urlconfig-" + escap
      ed + | 
|  | 2343                                 "' name='" + escaped + "' title='" + escapedTool
      tip + "'><option></option>"; | 
|  | 2344 | 
|  | 2345                         if ( QUnit.is( "array", val.value ) ) { | 
|  | 2346                                 for ( j = 0; j < val.value.length; j++ ) { | 
|  | 2347                                         escaped = escapeText( val.value[ j ] ); | 
|  | 2348                                         urlConfigHtml += "<option value='" + esc
      aped + "'" + | 
|  | 2349                                                 ( config[ val.id ] === val.value
      [ j ] ? | 
|  | 2350                                                         ( selection = true ) && 
      " selected='selected'" : "" ) + | 
|  | 2351                                                 ">" + escaped + "</option>"; | 
|  | 2352                                 } | 
|  | 2353                         } else { | 
|  | 2354                                 for ( j in val.value ) { | 
|  | 2355                                         if ( hasOwn.call( val.value, j ) ) { | 
|  | 2356                                                 urlConfigHtml += "<option value=
      '" + escapeText( j ) + "'" + | 
|  | 2357                                                         ( config[ val.id ] === j
       ? | 
|  | 2358                                                                 ( selection = tr
      ue ) && " selected='selected'" : "" ) + | 
|  | 2359                                                         ">" + escapeText( val.va
      lue[ j ] ) + "</option>"; | 
|  | 2360                                         } | 
|  | 2361                                 } | 
|  | 2362                         } | 
|  | 2363                         if ( config[ val.id ] && !selection ) { | 
|  | 2364                                 escaped = escapeText( config[ val.id ] ); | 
|  | 2365                                 urlConfigHtml += "<option value='" + escaped + | 
|  | 2366                                         "' selected='selected' disabled='disable
      d'>" + escaped + "</option>"; | 
|  | 2367                         } | 
|  | 2368                         urlConfigHtml += "</select>"; | 
|  | 2369                 } | 
|  | 2370         } | 
|  | 2371 | 
|  | 2372         return urlConfigHtml; | 
|  | 2373 } | 
|  | 2374 | 
|  | 2375 // Handle "click" events on toolbar checkboxes and "change" for select menus. | 
|  | 2376 // Updates the URL with the new state of `config.urlConfig` values. | 
|  | 2377 function toolbarChanged() { | 
|  | 2378         var updatedUrl, value, | 
|  | 2379                 field = this, | 
|  | 2380                 params = {}; | 
|  | 2381 | 
|  | 2382         // Detect if field is a select menu or a checkbox | 
|  | 2383         if ( "selectedIndex" in field ) { | 
|  | 2384                 value = field.options[ field.selectedIndex ].value || undefined; | 
|  | 2385         } else { | 
|  | 2386                 value = field.checked ? ( field.defaultValue || true ) : undefin
      ed; | 
|  | 2387         } | 
|  | 2388 | 
|  | 2389         params[ field.name ] = value; | 
|  | 2390         updatedUrl = setUrl( params ); | 
|  | 2391 | 
|  | 2392         if ( "hidepassed" === field.name && "replaceState" in window.history ) { | 
|  | 2393                 config[ field.name ] = value || false; | 
|  | 2394                 if ( value ) { | 
|  | 2395                         addClass( id( "qunit-tests" ), "hidepass" ); | 
|  | 2396                 } else { | 
|  | 2397                         removeClass( id( "qunit-tests" ), "hidepass" ); | 
|  | 2398                 } | 
|  | 2399 | 
|  | 2400                 // It is not necessary to refresh the whole page | 
|  | 2401                 window.history.replaceState( null, "", updatedUrl ); | 
|  | 2402         } else { | 
|  | 2403                 window.location = updatedUrl; | 
|  | 2404         } | 
|  | 2405 } | 
|  | 2406 | 
|  | 2407 function setUrl( params ) { | 
|  | 2408         var key, | 
|  | 2409                 querystring = "?"; | 
|  | 2410 | 
|  | 2411         params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params ); | 
|  | 2412 | 
|  | 2413         for ( key in params ) { | 
|  | 2414                 if ( hasOwn.call( params, key ) ) { | 
|  | 2415                         if ( params[ key ] === undefined ) { | 
|  | 2416                                 continue; | 
|  | 2417                         } | 
|  | 2418                         querystring += encodeURIComponent( key ); | 
|  | 2419                         if ( params[ key ] !== true ) { | 
|  | 2420                                 querystring += "=" + encodeURIComponent( params[
       key ] ); | 
|  | 2421                         } | 
|  | 2422                         querystring += "&"; | 
|  | 2423                 } | 
|  | 2424         } | 
|  | 2425         return location.protocol + "//" + location.host + | 
|  | 2426                 location.pathname + querystring.slice( 0, -1 ); | 
|  | 2427 } | 
|  | 2428 | 
|  | 2429 function applyUrlParams() { | 
|  | 2430         var selectBox = id( "qunit-modulefilter" ), | 
|  | 2431                 selection = decodeURIComponent( selectBox.options[ selectBox.sel
      ectedIndex ].value ), | 
|  | 2432                 filter = id( "qunit-filter-input" ).value; | 
|  | 2433 | 
|  | 2434         window.location = setUrl({ | 
|  | 2435                 module: ( selection === "" ) ? undefined : selection, | 
|  | 2436                 filter: ( filter === "" ) ? undefined : filter, | 
|  | 2437 | 
|  | 2438                 // Remove testId filter | 
|  | 2439                 testId: undefined | 
|  | 2440         }); | 
|  | 2441 } | 
|  | 2442 | 
|  | 2443 function toolbarUrlConfigContainer() { | 
|  | 2444         var urlConfigContainer = document.createElement( "span" ); | 
|  | 2445 | 
|  | 2446         urlConfigContainer.innerHTML = getUrlConfigHtml(); | 
|  | 2447         addClass( urlConfigContainer, "qunit-url-config" ); | 
|  | 2448 | 
|  | 2449         // For oldIE support: | 
|  | 2450         // * Add handlers to the individual elements instead of the container | 
|  | 2451         // * Use "click" instead of "change" for checkboxes | 
|  | 2452         addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", 
      toolbarChanged ); | 
|  | 2453         addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change"
      , toolbarChanged ); | 
|  | 2454 | 
|  | 2455         return urlConfigContainer; | 
|  | 2456 } | 
|  | 2457 | 
|  | 2458 function toolbarLooseFilter() { | 
|  | 2459         var filter = document.createElement( "form" ), | 
|  | 2460                 label = document.createElement( "label" ), | 
|  | 2461                 input = document.createElement( "input" ), | 
|  | 2462                 button = document.createElement( "button" ); | 
|  | 2463 | 
|  | 2464         addClass( filter, "qunit-filter" ); | 
|  | 2465 | 
|  | 2466         label.innerHTML = "Filter: "; | 
|  | 2467 | 
|  | 2468         input.type = "text"; | 
|  | 2469         input.value = config.filter || ""; | 
|  | 2470         input.name = "filter"; | 
|  | 2471         input.id = "qunit-filter-input"; | 
|  | 2472 | 
|  | 2473         button.innerHTML = "Go"; | 
|  | 2474 | 
|  | 2475         label.appendChild( input ); | 
|  | 2476 | 
|  | 2477         filter.appendChild( label ); | 
|  | 2478         filter.appendChild( button ); | 
|  | 2479         addEvent( filter, "submit", function( ev ) { | 
|  | 2480                 applyUrlParams(); | 
|  | 2481 | 
|  | 2482                 if ( ev && ev.preventDefault ) { | 
|  | 2483                         ev.preventDefault(); | 
|  | 2484                 } | 
|  | 2485 | 
|  | 2486                 return false; | 
|  | 2487         }); | 
|  | 2488 | 
|  | 2489         return filter; | 
|  | 2490 } | 
|  | 2491 | 
|  | 2492 function toolbarModuleFilterHtml() { | 
|  | 2493         var i, | 
|  | 2494                 moduleFilterHtml = ""; | 
|  | 2495 | 
|  | 2496         if ( !modulesList.length ) { | 
|  | 2497                 return false; | 
|  | 2498         } | 
|  | 2499 | 
|  | 2500         modulesList.sort(function( a, b ) { | 
|  | 2501                 return a.localeCompare( b ); | 
|  | 2502         }); | 
|  | 2503 | 
|  | 2504         moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" + | 
|  | 2505                 "<select id='qunit-modulefilter' name='modulefilter'><option val
      ue='' " + | 
|  | 2506                 ( QUnit.urlParams.module === undefined ? "selected='selected'" :
       "" ) + | 
|  | 2507                 ">< All Modules ></option>"; | 
|  | 2508 | 
|  | 2509         for ( i = 0; i < modulesList.length; i++ ) { | 
|  | 2510                 moduleFilterHtml += "<option value='" + | 
|  | 2511                         escapeText( encodeURIComponent( modulesList[ i ] ) ) + "
      ' " + | 
|  | 2512                         ( QUnit.urlParams.module === modulesList[ i ] ? "selecte
      d='selected'" : "" ) + | 
|  | 2513                         ">" + escapeText( modulesList[ i ] ) + "</option>"; | 
|  | 2514         } | 
|  | 2515         moduleFilterHtml += "</select>"; | 
|  | 2516 | 
|  | 2517         return moduleFilterHtml; | 
|  | 2518 } | 
|  | 2519 | 
|  | 2520 function toolbarModuleFilter() { | 
|  | 2521         var toolbar = id( "qunit-testrunner-toolbar" ), | 
|  | 2522                 moduleFilter = document.createElement( "span" ), | 
|  | 2523                 moduleFilterHtml = toolbarModuleFilterHtml(); | 
|  | 2524 | 
|  | 2525         if ( !toolbar || !moduleFilterHtml ) { | 
|  | 2526                 return false; | 
|  | 2527         } | 
|  | 2528 | 
|  | 2529         moduleFilter.setAttribute( "id", "qunit-modulefilter-container" ); | 
|  | 2530         moduleFilter.innerHTML = moduleFilterHtml; | 
|  | 2531 | 
|  | 2532         addEvent( moduleFilter.lastChild, "change", applyUrlParams ); | 
|  | 2533 | 
|  | 2534         toolbar.appendChild( moduleFilter ); | 
|  | 2535 } | 
|  | 2536 | 
|  | 2537 function appendToolbar() { | 
|  | 2538         var toolbar = id( "qunit-testrunner-toolbar" ); | 
|  | 2539 | 
|  | 2540         if ( toolbar ) { | 
|  | 2541                 toolbar.appendChild( toolbarUrlConfigContainer() ); | 
|  | 2542                 toolbar.appendChild( toolbarLooseFilter() ); | 
|  | 2543         } | 
|  | 2544 } | 
|  | 2545 | 
|  | 2546 function appendHeader() { | 
|  | 2547         var header = id( "qunit-header" ); | 
|  | 2548 | 
|  | 2549         if ( header ) { | 
|  | 2550                 header.innerHTML = "<a href='" + | 
|  | 2551                         setUrl({ filter: undefined, module: undefined, testId: u
      ndefined }) + | 
|  | 2552                         "'>" + header.innerHTML + "</a> "; | 
|  | 2553         } | 
|  | 2554 } | 
|  | 2555 | 
|  | 2556 function appendBanner() { | 
|  | 2557         var banner = id( "qunit-banner" ); | 
|  | 2558 | 
|  | 2559         if ( banner ) { | 
|  | 2560                 banner.className = ""; | 
|  | 2561         } | 
|  | 2562 } | 
|  | 2563 | 
|  | 2564 function appendTestResults() { | 
|  | 2565         var tests = id( "qunit-tests" ), | 
|  | 2566                 result = id( "qunit-testresult" ); | 
|  | 2567 | 
|  | 2568         if ( result ) { | 
|  | 2569                 result.parentNode.removeChild( result ); | 
|  | 2570         } | 
|  | 2571 | 
|  | 2572         if ( tests ) { | 
|  | 2573                 tests.innerHTML = ""; | 
|  | 2574                 result = document.createElement( "p" ); | 
|  | 2575                 result.id = "qunit-testresult"; | 
|  | 2576                 result.className = "result"; | 
|  | 2577                 tests.parentNode.insertBefore( result, tests ); | 
|  | 2578                 result.innerHTML = "Running...<br /> "; | 
|  | 2579         } | 
|  | 2580 } | 
|  | 2581 | 
|  | 2582 function storeFixture() { | 
|  | 2583         var fixture = id( "qunit-fixture" ); | 
|  | 2584         if ( fixture ) { | 
|  | 2585                 config.fixture = fixture.innerHTML; | 
|  | 2586         } | 
|  | 2587 } | 
|  | 2588 | 
|  | 2589 function appendUserAgent() { | 
|  | 2590         var userAgent = id( "qunit-userAgent" ); | 
|  | 2591         if ( userAgent ) { | 
|  | 2592                 userAgent.innerHTML = ""; | 
|  | 2593                 userAgent.appendChild( document.createTextNode( navigator.userAg
      ent ) ); | 
|  | 2594         } | 
|  | 2595 } | 
|  | 2596 | 
|  | 2597 function appendTestsList( modules ) { | 
|  | 2598         var i, l, x, z, test, moduleObj; | 
|  | 2599 | 
|  | 2600         for ( i = 0, l = modules.length; i < l; i++ ) { | 
|  | 2601                 moduleObj = modules[ i ]; | 
|  | 2602 | 
|  | 2603                 if ( moduleObj.name ) { | 
|  | 2604                         modulesList.push( moduleObj.name ); | 
|  | 2605                 } | 
|  | 2606 | 
|  | 2607                 for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) { | 
|  | 2608                         test = moduleObj.tests[ x ]; | 
|  | 2609 | 
|  | 2610                         appendTest( test.name, test.testId, moduleObj.name ); | 
|  | 2611                 } | 
|  | 2612         } | 
|  | 2613 } | 
|  | 2614 | 
|  | 2615 function appendTest( name, testId, moduleName ) { | 
|  | 2616         var title, rerunTrigger, testBlock, assertList, | 
|  | 2617                 tests = id( "qunit-tests" ); | 
|  | 2618 | 
|  | 2619         if ( !tests ) { | 
|  | 2620                 return; | 
|  | 2621         } | 
|  | 2622 | 
|  | 2623         title = document.createElement( "strong" ); | 
|  | 2624         title.innerHTML = getNameHtml( name, moduleName ); | 
|  | 2625 | 
|  | 2626         rerunTrigger = document.createElement( "a" ); | 
|  | 2627         rerunTrigger.innerHTML = "Rerun"; | 
|  | 2628         rerunTrigger.href = setUrl({ testId: testId }); | 
|  | 2629 | 
|  | 2630         testBlock = document.createElement( "li" ); | 
|  | 2631         testBlock.appendChild( title ); | 
|  | 2632         testBlock.appendChild( rerunTrigger ); | 
|  | 2633         testBlock.id = "qunit-test-output-" + testId; | 
|  | 2634 | 
|  | 2635         assertList = document.createElement( "ol" ); | 
|  | 2636         assertList.className = "qunit-assert-list"; | 
|  | 2637 | 
|  | 2638         testBlock.appendChild( assertList ); | 
|  | 2639 | 
|  | 2640         tests.appendChild( testBlock ); | 
|  | 2641 } | 
|  | 2642 | 
|  | 2643 // HTML Reporter initialization and load | 
|  | 2644 QUnit.begin(function( details ) { | 
|  | 2645         var qunit = id( "qunit" ); | 
|  | 2646 | 
|  | 2647         // Fixture is the only one necessary to run without the #qunit element | 
|  | 2648         storeFixture(); | 
|  | 2649 | 
|  | 2650         if ( qunit ) { | 
|  | 2651                 qunit.innerHTML = | 
|  | 2652                         "<h1 id='qunit-header'>" + escapeText( document.title ) 
      + "</h1>" + | 
|  | 2653                         "<h2 id='qunit-banner'></h2>" + | 
|  | 2654                         "<div id='qunit-testrunner-toolbar'></div>" + | 
|  | 2655                         "<h2 id='qunit-userAgent'></h2>" + | 
|  | 2656                         "<ol id='qunit-tests'></ol>"; | 
|  | 2657         } | 
|  | 2658 | 
|  | 2659         appendHeader(); | 
|  | 2660         appendBanner(); | 
|  | 2661         appendTestResults(); | 
|  | 2662         appendUserAgent(); | 
|  | 2663         appendToolbar(); | 
|  | 2664         appendTestsList( details.modules ); | 
|  | 2665         toolbarModuleFilter(); | 
|  | 2666 | 
|  | 2667         if ( qunit && config.hidepassed ) { | 
|  | 2668                 addClass( qunit.lastChild, "hidepass" ); | 
|  | 2669         } | 
|  | 2670 }); | 
|  | 2671 | 
|  | 2672 QUnit.done(function( details ) { | 
|  | 2673         var i, key, | 
|  | 2674                 banner = id( "qunit-banner" ), | 
|  | 2675                 tests = id( "qunit-tests" ), | 
|  | 2676                 html = [ | 
|  | 2677                         "Tests completed in ", | 
|  | 2678                         details.runtime, | 
|  | 2679                         " milliseconds.<br />", | 
|  | 2680                         "<span class='passed'>", | 
|  | 2681                         details.passed, | 
|  | 2682                         "</span> assertions of <span class='total'>", | 
|  | 2683                         details.total, | 
|  | 2684                         "</span> passed, <span class='failed'>", | 
|  | 2685                         details.failed, | 
|  | 2686                         "</span> failed." | 
|  | 2687                 ].join( "" ); | 
|  | 2688 | 
|  | 2689         if ( banner ) { | 
|  | 2690                 banner.className = details.failed ? "qunit-fail" : "qunit-pass"; | 
|  | 2691         } | 
|  | 2692 | 
|  | 2693         if ( tests ) { | 
|  | 2694                 id( "qunit-testresult" ).innerHTML = html; | 
|  | 2695         } | 
|  | 2696 | 
|  | 2697         if ( config.altertitle && defined.document && document.title ) { | 
|  | 2698 | 
|  | 2699                 // show ✖ for good, ✔ for bad suite result in title | 
|  | 2700                 // use escape sequences in case file gets loaded with non-utf-8-
      charset | 
|  | 2701                 document.title = [ | 
|  | 2702                         ( details.failed ? "\u2716" : "\u2714" ), | 
|  | 2703                         document.title.replace( /^[\u2714\u2716] /i, "" ) | 
|  | 2704                 ].join( " " ); | 
|  | 2705         } | 
|  | 2706 | 
|  | 2707         // clear own sessionStorage items if all tests passed | 
|  | 2708         if ( config.reorder && defined.sessionStorage && details.failed === 0 ) 
      { | 
|  | 2709                 for ( i = 0; i < sessionStorage.length; i++ ) { | 
|  | 2710                         key = sessionStorage.key( i++ ); | 
|  | 2711                         if ( key.indexOf( "qunit-test-" ) === 0 ) { | 
|  | 2712                                 sessionStorage.removeItem( key ); | 
|  | 2713                         } | 
|  | 2714                 } | 
|  | 2715         } | 
|  | 2716 | 
|  | 2717         // scroll back to top to show results | 
|  | 2718         if ( config.scrolltop && window.scrollTo ) { | 
|  | 2719                 window.scrollTo( 0, 0 ); | 
|  | 2720         } | 
|  | 2721 }); | 
|  | 2722 | 
|  | 2723 function getNameHtml( name, module ) { | 
|  | 2724         var nameHtml = ""; | 
|  | 2725 | 
|  | 2726         if ( module ) { | 
|  | 2727                 nameHtml = "<span class='module-name'>" + escapeText( module ) +
       "</span>: "; | 
|  | 2728         } | 
|  | 2729 | 
|  | 2730         nameHtml += "<span class='test-name'>" + escapeText( name ) + "</span>"; | 
|  | 2731 | 
|  | 2732         return nameHtml; | 
|  | 2733 } | 
|  | 2734 | 
|  | 2735 QUnit.testStart(function( details ) { | 
|  | 2736         var running, testBlock; | 
|  | 2737 | 
|  | 2738         testBlock = id( "qunit-test-output-" + details.testId ); | 
|  | 2739         if ( testBlock ) { | 
|  | 2740                 testBlock.className = "running"; | 
|  | 2741         } else { | 
|  | 2742 | 
|  | 2743                 // Report later registered tests | 
|  | 2744                 appendTest( details.name, details.testId, details.module ); | 
|  | 2745         } | 
|  | 2746 | 
|  | 2747         running = id( "qunit-testresult" ); | 
|  | 2748         if ( running ) { | 
|  | 2749                 running.innerHTML = "Running: <br />" + getNameHtml( details.nam
      e, details.module ); | 
|  | 2750         } | 
|  | 2751 | 
|  | 2752 }); | 
|  | 2753 | 
|  | 2754 QUnit.log(function( details ) { | 
|  | 2755         var assertList, assertLi, | 
|  | 2756                 message, expected, actual, | 
|  | 2757                 testItem = id( "qunit-test-output-" + details.testId ); | 
|  | 2758 | 
|  | 2759         if ( !testItem ) { | 
|  | 2760                 return; | 
|  | 2761         } | 
|  | 2762 | 
|  | 2763         message = escapeText( details.message ) || ( details.result ? "okay" : "
      failed" ); | 
|  | 2764         message = "<span class='test-message'>" + message + "</span>"; | 
|  | 2765         message += "<span class='runtime'>@ " + details.runtime + " ms</span>"; | 
|  | 2766 | 
|  | 2767         // pushFailure doesn't provide details.expected | 
|  | 2768         // when it calls, it's implicit to also not show expected and diff stuff | 
|  | 2769         // Also, we need to check details.expected existence, as it can exist an
      d be undefined | 
|  | 2770         if ( !details.result && hasOwn.call( details, "expected" ) ) { | 
|  | 2771                 expected = escapeText( QUnit.dump.parse( details.expected ) ); | 
|  | 2772                 actual = escapeText( QUnit.dump.parse( details.actual ) ); | 
|  | 2773                 message += "<table><tr class='test-expected'><th>Expected: </th>
      <td><pre>" + | 
|  | 2774                         expected + | 
|  | 2775                         "</pre></td></tr>"; | 
|  | 2776 | 
|  | 2777                 if ( actual !== expected ) { | 
|  | 2778                         message += "<tr class='test-actual'><th>Result: </th><td
      ><pre>" + | 
|  | 2779                                 actual + "</pre></td></tr>" + | 
|  | 2780                                 "<tr class='test-diff'><th>Diff: </th><td><pre>"
       + | 
|  | 2781                                 QUnit.diff( expected, actual ) + "</pre></td></t
      r>"; | 
|  | 2782                 } | 
|  | 2783 | 
|  | 2784                 if ( details.source ) { | 
|  | 2785                         message += "<tr class='test-source'><th>Source: </th><td
      ><pre>" + | 
|  | 2786                                 escapeText( details.source ) + "</pre></td></tr>
      "; | 
|  | 2787                 } | 
|  | 2788 | 
|  | 2789                 message += "</table>"; | 
|  | 2790 | 
|  | 2791         // this occours when pushFailure is set and we have an extracted stack t
      race | 
|  | 2792         } else if ( !details.result && details.source ) { | 
|  | 2793                 message += "<table>" + | 
|  | 2794                         "<tr class='test-source'><th>Source: </th><td><pre>" + | 
|  | 2795                         escapeText( details.source ) + "</pre></td></tr>" + | 
|  | 2796                         "</table>"; | 
|  | 2797         } | 
|  | 2798 | 
|  | 2799         assertList = testItem.getElementsByTagName( "ol" )[ 0 ]; | 
|  | 2800 | 
|  | 2801         assertLi = document.createElement( "li" ); | 
|  | 2802         assertLi.className = details.result ? "pass" : "fail"; | 
|  | 2803         assertLi.innerHTML = message; | 
|  | 2804         assertList.appendChild( assertLi ); | 
|  | 2805 }); | 
|  | 2806 | 
|  | 2807 QUnit.testDone(function( details ) { | 
|  | 2808         var testTitle, time, testItem, assertList, | 
|  | 2809                 good, bad, testCounts, skipped, | 
|  | 2810                 tests = id( "qunit-tests" ); | 
|  | 2811 | 
|  | 2812         if ( !tests ) { | 
|  | 2813                 return; | 
|  | 2814         } | 
|  | 2815 | 
|  | 2816         testItem = id( "qunit-test-output-" + details.testId ); | 
|  | 2817 | 
|  | 2818         assertList = testItem.getElementsByTagName( "ol" )[ 0 ]; | 
|  | 2819 | 
|  | 2820         good = details.passed; | 
|  | 2821         bad = details.failed; | 
|  | 2822 | 
|  | 2823         // store result when possible | 
|  | 2824         if ( config.reorder && defined.sessionStorage ) { | 
|  | 2825                 if ( bad ) { | 
|  | 2826                         sessionStorage.setItem( "qunit-test-" + details.module +
       "-" + details.name, bad ); | 
|  | 2827                 } else { | 
|  | 2828                         sessionStorage.removeItem( "qunit-test-" + details.modul
      e + "-" + details.name ); | 
|  | 2829                 } | 
|  | 2830         } | 
|  | 2831 | 
|  | 2832         if ( bad === 0 ) { | 
|  | 2833                 addClass( assertList, "qunit-collapsed" ); | 
|  | 2834         } | 
|  | 2835 | 
|  | 2836         // testItem.firstChild is the test name | 
|  | 2837         testTitle = testItem.firstChild; | 
|  | 2838 | 
|  | 2839         testCounts = bad ? | 
|  | 2840                 "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + g
      ood + "</b>, " : | 
|  | 2841                 ""; | 
|  | 2842 | 
|  | 2843         testTitle.innerHTML += " <b class='counts'>(" + testCounts + | 
|  | 2844                 details.assertions.length + ")</b>"; | 
|  | 2845 | 
|  | 2846         if ( details.skipped ) { | 
|  | 2847                 testItem.className = "skipped"; | 
|  | 2848                 skipped = document.createElement( "em" ); | 
|  | 2849                 skipped.className = "qunit-skipped-label"; | 
|  | 2850                 skipped.innerHTML = "skipped"; | 
|  | 2851                 testItem.insertBefore( skipped, testTitle ); | 
|  | 2852         } else { | 
|  | 2853                 addEvent( testTitle, "click", function() { | 
|  | 2854                         toggleClass( assertList, "qunit-collapsed" ); | 
|  | 2855                 }); | 
|  | 2856 | 
|  | 2857                 testItem.className = bad ? "fail" : "pass"; | 
|  | 2858 | 
|  | 2859                 time = document.createElement( "span" ); | 
|  | 2860                 time.className = "runtime"; | 
|  | 2861                 time.innerHTML = details.runtime + " ms"; | 
|  | 2862                 testItem.insertBefore( time, assertList ); | 
|  | 2863         } | 
|  | 2864 }); | 
|  | 2865 | 
|  | 2866 if ( !defined.document || document.readyState === "complete" ) { | 
|  | 2867         config.pageLoaded = true; | 
|  | 2868         config.autorun = true; | 
|  | 2869 } | 
|  | 2870 | 
|  | 2871 if ( defined.document ) { | 
|  | 2872         addEvent( window, "load", QUnit.load ); | 
|  | 2873 } | 
|  | 2874 | 
|  | 2875 })(); | 
| LEFT | RIGHT | 
|---|