]> source.dussan.org Git - jquery.git/commitdiff
Revert "Build: Bump qunit version"
authorOleg Gaidarenko <markelog@gmail.com>
Fri, 20 May 2016 10:38:12 +0000 (13:38 +0300)
committerOleg Gaidarenko <markelog@gmail.com>
Fri, 20 May 2016 10:38:12 +0000 (13:38 +0300)
This reverts commit afe2727c5610ba9cd5ffbec5c34f15d38538b05f.

external/qunit/LICENSE.txt
external/qunit/qunit.css
external/qunit/qunit.js
package.json

index a3a9b5120d1ae0c064863c2f1ce02c93b9c608c6..fb928a54325dd94ecc7aa87fb10ec29a8b92f7d1 100644 (file)
@@ -30,6 +30,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 ====
 
-All files located in the node_modules directory are externally maintained
-libraries used by this software which have their own licenses; we
-recommend you read them, as their terms may differ from the terms above.
+All files located in the node_modules and external directories are
+externally maintained libraries used by this software which have their
+own licenses; we recommend you read them, as their terms may differ from
+the terms above.
index ae68fc412eefac5e120b871f67949a4d2a95d91e..0eb0b0171daac2f53e38bb496e5b04d9ee4ffd19 100644 (file)
@@ -1,27 +1,27 @@
 /*!
- * QUnit 1.23.1
- * https://qunitjs.com/
+ * QUnit 1.17.1
+ * http://qunitjs.com/
  *
  * Copyright jQuery Foundation and other contributors
  * Released under the MIT license
- * https://jquery.org/license
+ * http://jquery.org/license
  *
- * Date: 2016-04-12T17:29Z
+ * Date: 2015-01-20T19:39Z
  */
 
 /** Font Family and Sizes */
 
-#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult {
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
        font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
 }
 
-#qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
+#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
 #qunit-tests { font-size: smaller; }
 
 
 /** Resets */
 
-#qunit-tests, #qunit-header, #qunit-banner, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
        margin: 0;
        padding: 0;
 }
        overflow: hidden;
 }
 
-#qunit-filteredTest {
-       padding: 0.5em 1em 0.5em 1em;
-       background-color: #F4FF77;
-       color: #366097;
-}
-
 #qunit-userAgent {
        padding: 0.5em 1em 0.5em 1em;
        background-color: #2B81AF;
        display: list-item;
 }
 
-#qunit-tests.hidepass {
-       position: relative;
-}
-
 #qunit-tests.hidepass li.running,
 #qunit-tests.hidepass li.pass {
-       visibility: hidden;
-       position: absolute;
-       width:   0;
-       height:  0;
-       padding: 0;
-       border:  0;
-       margin:  0;
+       display: none;
 }
 
 #qunit-tests li strong {
        color: #C2CCD1;
        text-decoration: none;
 }
-
-#qunit-tests li p a {
-       padding: 0.25em;
-       color: #6B6464;
-}
 #qunit-tests li a:hover,
 #qunit-tests li a:focus {
        color: #000;
        border-radius: 5px;
 }
 
-.qunit-source {
-       margin: 0.6em 0 0.3em;
-}
-
 .qunit-collapsed {
        display: none;
 }
index 5df0822ea428345513a05ff23e78c395c402d06f..006ca47474b21f11068f34fc9ab4cd1f290bcb97 100644 (file)
 /*!
- * QUnit 1.23.1
- * https://qunitjs.com/
+ * QUnit 1.17.1
+ * http://qunitjs.com/
  *
  * Copyright jQuery Foundation and other contributors
  * Released under the MIT license
- * https://jquery.org/license
+ * http://jquery.org/license
  *
- * Date: 2016-04-12T17:29Z
+ * Date: 2015-01-20T19:39Z
  */
 
-( function( global ) {
+(function( window ) {
 
-var QUnit = {};
-
-var Date = global.Date;
-var now = Date.now || function() {
-       return new Date().getTime();
-};
-
-var setTimeout = global.setTimeout;
-var clearTimeout = global.clearTimeout;
-
-// Store a local window from the global to allow direct references.
-var window = global.window;
-
-var defined = {
-       document: window && window.document !== undefined,
-       setTimeout: setTimeout !== undefined,
-       sessionStorage: ( function() {
-               var x = "qunit-test-string";
-               try {
-                       sessionStorage.setItem( x, x );
-                       sessionStorage.removeItem( x );
-                       return true;
-               } catch ( e ) {
-                       return false;
-               }
-       }() )
-};
-
-var fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" );
-var globalStartCalled = false;
-var runStarted = false;
-
-var toString = Object.prototype.toString,
-       hasOwn = Object.prototype.hasOwnProperty;
-
-// Returns a new Array with the elements that are in a but not in b
-function diff( a, b ) {
-       var i, j,
-               result = a.slice();
-
-       for ( i = 0; i < result.length; i++ ) {
-               for ( j = 0; j < b.length; j++ ) {
-                       if ( result[ i ] === b[ j ] ) {
-                               result.splice( i, 1 );
-                               i--;
-                               break;
-                       }
-               }
-       }
-       return result;
-}
-
-// From jquery.js
-function inArray( elem, array ) {
-       if ( array.indexOf ) {
-               return array.indexOf( elem );
-       }
-
-       for ( var i = 0, length = array.length; i < length; i++ ) {
-               if ( array[ i ] === elem ) {
-                       return i;
-               }
-       }
-
-       return -1;
-}
-
-/**
- * Makes a clone of an object using only Array or Object as base,
- * and copies over the own enumerable properties.
- *
- * @param {Object} obj
- * @return {Object} New object with only the own properties (recursively).
- */
-function objectValues ( obj ) {
-       var key, val,
-               vals = QUnit.is( "array", obj ) ? [] : {};
-       for ( key in obj ) {
-               if ( hasOwn.call( obj, key ) ) {
-                       val = obj[ key ];
-                       vals[ key ] = val === Object( val ) ? objectValues( val ) : val;
-               }
-       }
-       return vals;
-}
-
-function extend( a, b, undefOnly ) {
-       for ( var prop in b ) {
-               if ( hasOwn.call( b, prop ) ) {
-
-                       // Avoid "Member not found" error in IE8 caused by messing with window.constructor
-                       // This block runs on every environment, so `global` is being used instead of `window`
-                       // to avoid errors on node.
-                       if ( prop !== "constructor" || a !== global ) {
-                               if ( b[ prop ] === undefined ) {
-                                       delete a[ prop ];
-                               } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) {
-                                       a[ prop ] = b[ prop ];
-                               }
+var QUnit,
+       config,
+       onErrorFnPrev,
+       loggingCallbacks = {},
+       fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ),
+       toString = Object.prototype.toString,
+       hasOwn = Object.prototype.hasOwnProperty,
+       // Keep a local reference to Date (GH-283)
+       Date = window.Date,
+       now = Date.now || function() {
+               return new Date().getTime();
+       },
+       globalStartCalled = false,
+       runStarted = false,
+       setTimeout = window.setTimeout,
+       clearTimeout = window.clearTimeout,
+       defined = {
+               document: window.document !== undefined,
+               setTimeout: window.setTimeout !== undefined,
+               sessionStorage: (function() {
+                       var x = "qunit-test-string";
+                       try {
+                               sessionStorage.setItem( x, x );
+                               sessionStorage.removeItem( x );
+                               return true;
+                       } catch ( e ) {
+                               return false;
                        }
-               }
-       }
-
-       return a;
-}
-
-function objectType( obj ) {
-       if ( typeof obj === "undefined" ) {
-               return "undefined";
-       }
-
-       // Consider: typeof null === object
-       if ( obj === null ) {
-               return "null";
-       }
-
-       var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ),
-               type = match && match[ 1 ];
-
-       switch ( type ) {
-               case "Number":
-                       if ( isNaN( obj ) ) {
-                               return "nan";
+               }())
+       },
+       /**
+        * Provides a normalized error string, correcting an issue
+        * with IE 7 (and prior) where Error.prototype.toString is
+        * not properly implemented
+        *
+        * Based on http://es5.github.com/#x15.11.4.4
+        *
+        * @param {String|Error} error
+        * @return {String} error message
+        */
+       errorString = function( error ) {
+               var name, message,
+                       errorString = error.toString();
+               if ( errorString.substring( 0, 7 ) === "[object" ) {
+                       name = error.name ? error.name.toString() : "Error";
+                       message = error.message ? error.message.toString() : "";
+                       if ( name && message ) {
+                               return name + ": " + message;
+                       } else if ( name ) {
+                               return name;
+                       } else if ( message ) {
+                               return message;
+                       } else {
+                               return "Error";
                        }
-                       return "number";
-               case "String":
-               case "Boolean":
-               case "Array":
-               case "Set":
-               case "Map":
-               case "Date":
-               case "RegExp":
-               case "Function":
-               case "Symbol":
-                       return type.toLowerCase();
-       }
-       if ( typeof obj === "object" ) {
-               return "object";
-       }
-}
-
-// Safe object type checking
-function is( type, obj ) {
-       return QUnit.objectType( obj ) === type;
-}
-
-// Doesn't support IE6 to IE9, it will return undefined on these browsers
-// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
-function extractStacktrace( e, offset ) {
-       offset = offset === undefined ? 4 : offset;
-
-       var stack, include, i;
-
-       if ( e.stack ) {
-               stack = e.stack.split( "\n" );
-               if ( /^error$/i.test( stack[ 0 ] ) ) {
-                       stack.shift();
+               } else {
+                       return errorString;
                }
-               if ( fileName ) {
-                       include = [];
-                       for ( i = offset; i < stack.length; i++ ) {
-                               if ( stack[ i ].indexOf( fileName ) !== -1 ) {
-                                       break;
-                               }
-                               include.push( stack[ i ] );
-                       }
-                       if ( include.length ) {
-                               return include.join( "\n" );
+       },
+       /**
+        * Makes a clone of an object using only Array or Object as base,
+        * and copies over the own enumerable properties.
+        *
+        * @param {Object} obj
+        * @return {Object} New object with only the own properties (recursively).
+        */
+       objectValues = function( obj ) {
+               var key, val,
+                       vals = QUnit.is( "array", obj ) ? [] : {};
+               for ( key in obj ) {
+                       if ( hasOwn.call( obj, key ) ) {
+                               val = obj[ key ];
+                               vals[ key ] = val === Object( val ) ? objectValues( val ) : val;
                        }
                }
-               return stack[ offset ];
-
-       // Support: Safari <=6 only
-       } else if ( e.sourceURL ) {
-
-               // Exclude useless self-reference for generated Error objects
-               if ( /qunit.js$/.test( e.sourceURL ) ) {
-                       return;
-               }
-
-               // For actual exceptions, this is useful
-               return e.sourceURL + ":" + e.line;
-       }
-}
-
-function sourceFromStacktrace( offset ) {
-       var error = new Error();
-
-       // Support: Safari <=7 only, IE <=10 - 11 only
-       // Not all browsers generate the `stack` property for `new Error()`, see also #636
-       if ( !error.stack ) {
-               try {
-                       throw error;
-               } catch ( err ) {
-                       error = err;
-               }
-       }
+               return vals;
+       };
 
-       return extractStacktrace( error, offset );
-}
+QUnit = {};
 
 /**
  * Config object: Maintain internal state
  * Later exposed as QUnit.config
  * `config` initialized at top of scope
  */
-var config = {
-
+config = {
        // The queue of tests to run
        queue: [],
 
-       // Block until document ready
+       // block until document ready
        blocking: true,
 
-       // By default, run previously failed tests first
+       // by default, run previously failed tests first
        // very useful in combination with "Hide passed tests" checked
        reorder: true,
 
-       // By default, modify document.title when suite is done
+       // by default, modify document.title when suite is done
        altertitle: true,
 
-       // HTML Reporter: collapse every test except the first failing test
-       // If false, all failing tests will be expanded
-       collapse: true,
-
-       // By default, scroll to top of the page when suite is done
+       // by default, scroll to top of the page when suite is done
        scrolltop: true,
 
-       // Depth up-to which object will be dumped
-       maxDepth: 5,
-
-       // When enabled, all tests must call expect()
+       // when enabled, all tests must call expect()
        requireExpects: false,
 
-       // Placeholder for user-configurable form-exposed URL parameters
-       urlConfig: [],
+       // add checkboxes that are persisted in the query-string
+       // when enabled, the id is set to `true` as a `QUnit.config` property
+       urlConfig: [
+               {
+                       id: "hidepassed",
+                       label: "Hide passed tests",
+                       tooltip: "Only show tests and assertions that fail. Stored as query-strings."
+               },
+               {
+                       id: "noglobals",
+                       label: "Check for Globals",
+                       tooltip: "Enabling this will test if any test introduces new properties on the " +
+                               "`window` object. Stored as query-strings."
+               },
+               {
+                       id: "notrycatch",
+                       label: "No try-catch",
+                       tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " +
+                               "exceptions in IE reasonable. Stored as query-strings."
+               }
+       ],
 
        // Set of all modules.
        modules: [],
 
-       // Stack of nested modules
-       moduleStack: [],
-
        // The first unnamed module
        currentModule: {
                name: "",
@@ -266,139 +153,63 @@ var config = {
 // Push a loose unnamed module to the modules collection
 config.modules.push( config.currentModule );
 
-var loggingCallbacks = {};
-
-// Register logging callbacks
-function registerLoggingCallbacks( obj ) {
-       var i, l, key,
-               callbackNames = [ "begin", "done", "log", "testStart", "testDone",
-                       "moduleStart", "moduleDone" ];
-
-       function registerLoggingCallback( key ) {
-               var loggingCallback = function( callback ) {
-                       if ( objectType( callback ) !== "function" ) {
-                               throw new Error(
-                                       "QUnit logging methods require a callback function as their first parameters."
-                               );
+// Initialize more QUnit.config and QUnit.urlParams
+(function() {
+       var i, current,
+               location = window.location || { search: "", protocol: "file:" },
+               params = location.search.slice( 1 ).split( "&" ),
+               length = params.length,
+               urlParams = {};
+
+       if ( params[ 0 ] ) {
+               for ( i = 0; i < length; i++ ) {
+                       current = params[ i ].split( "=" );
+                       current[ 0 ] = decodeURIComponent( current[ 0 ] );
+
+                       // allow just a key to turn on a flag, e.g., test.html?noglobals
+                       current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
+                       if ( urlParams[ current[ 0 ] ] ) {
+                               urlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] );
+                       } else {
+                               urlParams[ current[ 0 ] ] = current[ 1 ];
                        }
-
-                       config.callbacks[ key ].push( callback );
-               };
-
-               // DEPRECATED: This will be removed on QUnit 2.0.0+
-               // Stores the registered functions allowing restoring
-               // at verifyLoggingCallbacks() if modified
-               loggingCallbacks[ key ] = loggingCallback;
-
-               return loggingCallback;
-       }
-
-       for ( i = 0, l = callbackNames.length; i < l; i++ ) {
-               key = callbackNames[ i ];
-
-               // Initialize key collection of logging callback
-               if ( objectType( config.callbacks[ key ] ) === "undefined" ) {
-                       config.callbacks[ key ] = [];
                }
-
-               obj[ key ] = registerLoggingCallback( key );
        }
-}
-
-function runLoggingCallbacks( key, args ) {
-       var i, l, callbacks;
 
-       callbacks = config.callbacks[ key ];
-       for ( i = 0, l = callbacks.length; i < l; i++ ) {
-               callbacks[ i ]( args );
+       if ( urlParams.filter === true ) {
+               delete urlParams.filter;
        }
-}
-
-// DEPRECATED: This will be removed on 2.0.0+
-// This function verifies if the loggingCallbacks were modified by the user
-// If so, it will restore it, assign the given callback and print a console warning
-function verifyLoggingCallbacks() {
-       var loggingCallback, userCallback;
-
-       for ( loggingCallback in loggingCallbacks ) {
-               if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) {
 
-                       userCallback = QUnit[ loggingCallback ];
+       QUnit.urlParams = urlParams;
 
-                       // Restore the callback function
-                       QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ];
+       // String search anywhere in moduleName+testName
+       config.filter = urlParams.filter;
 
-                       // Assign the deprecated given callback
-                       QUnit[ loggingCallback ]( userCallback );
+       config.testId = [];
+       if ( urlParams.testId ) {
 
-                       if ( global.console && global.console.warn ) {
-                               global.console.warn(
-                                       "QUnit." + loggingCallback + " was replaced with a new value.\n" +
-                                       "Please, check out the documentation on how to apply logging callbacks.\n" +
-                                       "Reference: https://api.qunitjs.com/category/callbacks/"
-                               );
-                       }
+               // Ensure that urlParams.testId is an array
+               urlParams.testId = [].concat( urlParams.testId );
+               for ( i = 0; i < urlParams.testId.length; i++ ) {
+                       config.testId.push( urlParams.testId[ i ] );
                }
        }
-}
-
-( function() {
-       if ( !defined.document ) {
-               return;
-       }
-
-       // `onErrorFnPrev` initialized at top of scope
-       // Preserve other handlers
-       var onErrorFnPrev = window.onerror;
-
-       // Cover uncaught exceptions
-       // Returning true will suppress the default browser handler,
-       // returning false will let it run.
-       window.onerror = function( error, filePath, linerNr ) {
-               var ret = false;
-               if ( onErrorFnPrev ) {
-                       ret = onErrorFnPrev( error, filePath, linerNr );
-               }
-
-               // Treat return value as window.onerror itself does,
-               // Only do our handling if not suppressed.
-               if ( ret !== true ) {
-                       if ( QUnit.config.current ) {
-                               if ( QUnit.config.current.ignoreGlobalErrors ) {
-                                       return true;
-                               }
-                               QUnit.pushFailure( error, filePath + ":" + linerNr );
-                       } else {
-                               QUnit.test( "global failure", extend( function() {
-                                       QUnit.pushFailure( error, filePath + ":" + linerNr );
-                               }, { validTest: true } ) );
-                       }
-                       return false;
-               }
-
-               return ret;
-       };
-}() );
-
-// Figure out if we're running the tests from a server or not
-QUnit.isLocal = !( defined.document && window.location.protocol !== "file:" );
 
-// Expose the current QUnit version
-QUnit.version = "1.23.1";
+       // Figure out if we're running the tests from a server or not
+       QUnit.isLocal = location.protocol === "file:";
+}());
 
+// Root QUnit object.
+// `QUnit` initialized at top of scope
 extend( QUnit, {
 
-       // Call on start of module test to prepend name to all tests
-       module: function( name, testEnvironment, executeNow ) {
-               var module, moduleFns;
-               var currentModule = config.currentModule;
-
-               if ( arguments.length === 2 ) {
-                       if ( objectType( testEnvironment ) === "function" ) {
-                               executeNow = testEnvironment;
-                               testEnvironment = undefined;
-                       }
-               }
+       // call on start of module test to prepend name to all tests
+       module: function( name, testEnvironment ) {
+               var currentModule = {
+                       name: name,
+                       testEnvironment: testEnvironment,
+                       tests: []
+               };
 
                // DEPRECATED: handles setup/teardown functions,
                // beforeEach and afterEach should be used instead
@@ -411,62 +222,46 @@ extend( QUnit, {
                        delete testEnvironment.teardown;
                }
 
-               module = createModule();
-
-               moduleFns = {
-                       beforeEach: setHook( module, "beforeEach" ),
-                       afterEach: setHook( module, "afterEach" )
-               };
+               config.modules.push( currentModule );
+               config.currentModule = currentModule;
+       },
 
-               if ( objectType( executeNow ) === "function" ) {
-                       config.moduleStack.push( module );
-                       setCurrentModule( module );
-                       executeNow.call( module.testEnvironment, moduleFns );
-                       config.moduleStack.pop();
-                       module = module.parentModule || currentModule;
+       // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0.
+       asyncTest: function( testName, expected, callback ) {
+               if ( arguments.length === 2 ) {
+                       callback = expected;
+                       expected = null;
                }
 
-               setCurrentModule( module );
-
-               function createModule() {
-                       var parentModule = config.moduleStack.length ?
-                               config.moduleStack.slice( -1 )[ 0 ] : null;
-                       var moduleName = parentModule !== null ?
-                               [ parentModule.name, name ].join( " > " ) : name;
-                       var module = {
-                               name: moduleName,
-                               parentModule: parentModule,
-                               tests: [],
-                               moduleId: generateHash( moduleName )
-                       };
+               QUnit.test( testName, expected, callback, true );
+       },
 
-                       var env = {};
-                       if ( parentModule ) {
-                               extend( env, parentModule.testEnvironment );
-                               delete env.beforeEach;
-                               delete env.afterEach;
-                       }
-                       extend( env, testEnvironment );
-                       module.testEnvironment = env;
+       test: function( testName, expected, callback, async ) {
+               var test;
 
-                       config.modules.push( module );
-                       return module;
+               if ( arguments.length === 2 ) {
+                       callback = expected;
+                       expected = null;
                }
 
-               function setCurrentModule( module ) {
-                       config.currentModule = module;
-               }
+               test = new Test({
+                       testName: testName,
+                       expected: expected,
+                       async: async,
+                       callback: callback
+               });
 
+               test.queue();
        },
 
-       // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0.
-       asyncTest: asyncTest,
-
-       test: test,
+       skip: function( testName ) {
+               var test = new Test({
+                       testName: testName,
+                       skip: true
+               });
 
-       skip: skip,
-
-       only: only,
+               test.queue();
+       },
 
        // DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0.
        // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior.
@@ -494,23 +289,12 @@ extend( QUnit, {
                        // If a test is running, adjust its semaphore
                        config.current.semaphore -= count || 1;
 
-                       // If semaphore is non-numeric, throw error
-                       if ( isNaN( config.current.semaphore ) ) {
-                               config.current.semaphore = 0;
-
-                               QUnit.pushFailure(
-                                       "Called start() with a non-numeric decrement.",
-                                       sourceFromStacktrace( 2 )
-                               );
-                               return;
-                       }
-
                        // Don't start until equal number of stop-calls
                        if ( config.current.semaphore > 0 ) {
                                return;
                        }
 
-                       // Throw an Error if start is called more often than stop
+                       // throw an Error if start is called more often than stop
                        if ( config.current.semaphore < 0 ) {
                                config.current.semaphore = 0;
 
@@ -541,9 +325,43 @@ extend( QUnit, {
 
        config: config,
 
-       is: is,
+       // Safe object type checking
+       is: function( type, obj ) {
+               return QUnit.objectType( obj ) === type;
+       },
+
+       objectType: function( obj ) {
+               if ( typeof obj === "undefined" ) {
+                       return "undefined";
+               }
+
+               // Consider: typeof null === object
+               if ( obj === null ) {
+                       return "null";
+               }
+
+               var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ),
+                       type = match && match[ 1 ] || "";
 
-       objectType: objectType,
+               switch ( type ) {
+                       case "Number":
+                               if ( isNaN( obj ) ) {
+                                       return "nan";
+                               }
+                               return "number";
+                       case "String":
+                       case "Boolean":
+                       case "Array":
+                       case "Date":
+                       case "RegExp":
+                       case "Function":
+                               return type.toLowerCase();
+               }
+               if ( typeof obj === "object" ) {
+                       return "object";
+               }
+               return undefined;
+       },
 
        extend: extend,
 
@@ -565,50 +383,176 @@ extend( QUnit, {
                if ( config.autostart ) {
                        resumeProcessing();
                }
-       },
-
-       stack: function( offset ) {
-               offset = ( offset || 0 ) + 2;
-               return sourceFromStacktrace( offset );
        }
-} );
+});
 
-registerLoggingCallbacks( QUnit );
+// Register logging callbacks
+(function() {
+       var i, l, key,
+               callbacks = [ "begin", "done", "log", "testStart", "testDone",
+                       "moduleStart", "moduleDone" ];
 
-function begin() {
-       var i, l,
-               modulesLog = [];
+       function registerLoggingCallback( key ) {
+               var loggingCallback = function( callback ) {
+                       if ( QUnit.objectType( callback ) !== "function" ) {
+                               throw new Error(
+                                       "QUnit logging methods require a callback function as their first parameters."
+                               );
+                       }
 
-       // If the test run hasn't officially begun yet
-       if ( !config.started ) {
+                       config.callbacks[ key ].push( callback );
+               };
 
-               // Record the time of the test run's beginning
-               config.started = now();
+               // DEPRECATED: This will be removed on QUnit 2.0.0+
+               // Stores the registered functions allowing restoring
+               // at verifyLoggingCallbacks() if modified
+               loggingCallbacks[ key ] = loggingCallback;
 
-               verifyLoggingCallbacks();
+               return loggingCallback;
+       }
 
-               // Delete the loose unnamed module if unused.
-               if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) {
-                       config.modules.shift();
+       for ( i = 0, l = callbacks.length; i < l; i++ ) {
+               key = callbacks[ i ];
+
+               // Initialize key collection of logging callback
+               if ( QUnit.objectType( config.callbacks[ key ] ) === "undefined" ) {
+                       config.callbacks[ key ] = [];
                }
 
-               // Avoid unnecessary information by not logging modules' test environments
-               for ( i = 0, l = config.modules.length; i < l; i++ ) {
-                       modulesLog.push( {
-                               name: config.modules[ i ].name,
-                               tests: config.modules[ i ].tests
-                       } );
+               QUnit[ key ] = registerLoggingCallback( key );
+       }
+})();
+
+// `onErrorFnPrev` initialized at top of scope
+// Preserve other handlers
+onErrorFnPrev = window.onerror;
+
+// Cover uncaught exceptions
+// Returning true will suppress the default browser handler,
+// returning false will let it run.
+window.onerror = function( error, filePath, linerNr ) {
+       var ret = false;
+       if ( onErrorFnPrev ) {
+               ret = onErrorFnPrev( error, filePath, linerNr );
+       }
+
+       // Treat return value as window.onerror itself does,
+       // Only do our handling if not suppressed.
+       if ( ret !== true ) {
+               if ( QUnit.config.current ) {
+                       if ( QUnit.config.current.ignoreGlobalErrors ) {
+                               return true;
+                       }
+                       QUnit.pushFailure( error, filePath + ":" + linerNr );
+               } else {
+                       QUnit.test( "global failure", extend(function() {
+                               QUnit.pushFailure( error, filePath + ":" + linerNr );
+                       }, { validTest: true } ) );
                }
+               return false;
+       }
 
-               // The test run is officially beginning now
-               runLoggingCallbacks( "begin", {
-                       totalTests: Test.count,
-                       modules: modulesLog
-               } );
+       return ret;
+};
+
+function done() {
+       var runtime, passed;
+
+       config.autorun = true;
+
+       // Log the last module results
+       if ( config.previousModule ) {
+               runLoggingCallbacks( "moduleDone", {
+                       name: config.previousModule.name,
+                       tests: config.previousModule.tests,
+                       failed: config.moduleStats.bad,
+                       passed: config.moduleStats.all - config.moduleStats.bad,
+                       total: config.moduleStats.all,
+                       runtime: now() - config.moduleStats.started
+               });
+       }
+       delete config.previousModule;
+
+       runtime = now() - config.started;
+       passed = config.stats.all - config.stats.bad;
+
+       runLoggingCallbacks( "done", {
+               failed: config.stats.bad,
+               passed: passed,
+               total: config.stats.all,
+               runtime: runtime
+       });
+}
+
+// Doesn't support IE6 to IE9
+// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
+function extractStacktrace( e, offset ) {
+       offset = offset === undefined ? 4 : offset;
+
+       var stack, include, i;
+
+       if ( e.stacktrace ) {
+
+               // Opera 12.x
+               return e.stacktrace.split( "\n" )[ offset + 3 ];
+       } else if ( e.stack ) {
+
+               // Firefox, Chrome, Safari 6+, IE10+, PhantomJS and Node
+               stack = e.stack.split( "\n" );
+               if ( /^error$/i.test( stack[ 0 ] ) ) {
+                       stack.shift();
+               }
+               if ( fileName ) {
+                       include = [];
+                       for ( i = offset; i < stack.length; i++ ) {
+                               if ( stack[ i ].indexOf( fileName ) !== -1 ) {
+                                       break;
+                               }
+                               include.push( stack[ i ] );
+                       }
+                       if ( include.length ) {
+                               return include.join( "\n" );
+                       }
+               }
+               return stack[ offset ];
+       } else if ( e.sourceURL ) {
+
+               // Safari < 6
+               // exclude useless self-reference for generated Error objects
+               if ( /qunit.js$/.test( e.sourceURL ) ) {
+                       return;
+               }
+
+               // for actual exceptions, this is useful
+               return e.sourceURL + ":" + e.line;
        }
+}
 
-       config.blocking = false;
-       process( true );
+function sourceFromStacktrace( offset ) {
+       var e = new Error();
+       if ( !e.stack ) {
+               try {
+                       throw e;
+               } catch ( err ) {
+                       // This should already be true in most browsers
+                       e = err;
+               }
+       }
+       return extractStacktrace( e, offset );
+}
+
+function synchronize( callback, last ) {
+       if ( QUnit.objectType( callback ) === "array" ) {
+               while ( callback.length ) {
+                       synchronize( callback.shift() );
+               }
+               return;
+       }
+       config.queue.push( callback );
+
+       if ( config.autorun && !config.blocking ) {
+               process( last );
+       }
 }
 
 function process( last ) {
@@ -638,21 +582,40 @@ function process( last ) {
        }
 }
 
-function pauseProcessing() {
-       config.blocking = true;
+function begin() {
+       var i, l,
+               modulesLog = [];
 
-       if ( config.testTimeout && defined.setTimeout ) {
-               clearTimeout( config.timeout );
-               config.timeout = setTimeout( function() {
-                       if ( config.current ) {
-                               config.current.semaphore = 0;
-                               QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) );
-                       } else {
-                               throw new Error( "Test timed out" );
-                       }
-                       resumeProcessing();
-               }, config.testTimeout );
+       // If the test run hasn't officially begun yet
+       if ( !config.started ) {
+
+               // Record the time of the test run's beginning
+               config.started = now();
+
+               verifyLoggingCallbacks();
+
+               // Delete the loose unnamed module if unused.
+               if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) {
+                       config.modules.shift();
+               }
+
+               // Avoid unnecessary information by not logging modules' test environments
+               for ( i = 0, l = config.modules.length; i < l; i++ ) {
+                       modulesLog.push({
+                               name: config.modules[ i ].name,
+                               tests: config.modules[ i ].tests
+                       });
+               }
+
+               // The test run is officially beginning now
+               runLoggingCallbacks( "begin", {
+                       totalTests: Test.count,
+                       modules: modulesLog
+               });
        }
+
+       config.blocking = false;
+       process( true );
 }
 
 function resumeProcessing() {
@@ -660,7 +623,7 @@ function resumeProcessing() {
 
        // A slight delay to allow this iteration of the event loop to finish (more assertions, etc.)
        if ( defined.setTimeout ) {
-               setTimeout( function() {
+               setTimeout(function() {
                        if ( config.current && config.current.semaphore > 0 ) {
                                return;
                        }
@@ -675,83 +638,178 @@ function resumeProcessing() {
        }
 }
 
-function done() {
-       var runtime, passed;
-
-       config.autorun = true;
-
-       // Log the last module results
-       if ( config.previousModule ) {
-               runLoggingCallbacks( "moduleDone", {
-                       name: config.previousModule.name,
-                       tests: config.previousModule.tests,
-                       failed: config.moduleStats.bad,
-                       passed: config.moduleStats.all - config.moduleStats.bad,
-                       total: config.moduleStats.all,
-                       runtime: now() - config.moduleStats.started
-               } );
-       }
-       delete config.previousModule;
-
-       runtime = now() - config.started;
-       passed = config.stats.all - config.stats.bad;
-
-       runLoggingCallbacks( "done", {
-               failed: config.stats.bad,
-               passed: passed,
-               total: config.stats.all,
-               runtime: runtime
-       } );
-}
+function pauseProcessing() {
+       config.blocking = true;
 
-function setHook( module, hookName ) {
-       if ( module.testEnvironment === undefined ) {
-               module.testEnvironment = {};
+       if ( config.testTimeout && defined.setTimeout ) {
+               clearTimeout( config.timeout );
+               config.timeout = setTimeout(function() {
+                       if ( config.current ) {
+                               config.current.semaphore = 0;
+                               QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) );
+                       } else {
+                               throw new Error( "Test timed out" );
+                       }
+                       resumeProcessing();
+               }, config.testTimeout );
        }
-
-       return function( callback ) {
-               module.testEnvironment[ hookName ] = callback;
-       };
 }
 
-var focused = false;
-var priorityCount = 0;
-var unitSampler;
-
-function Test( settings ) {
-       var i, l;
-
-       ++Test.count;
-
-       extend( this, settings );
-       this.assertions = [];
-       this.semaphore = 0;
-       this.usedAsync = false;
-       this.module = config.currentModule;
-       this.stack = sourceFromStacktrace( 3 );
+function saveGlobal() {
+       config.pollution = [];
 
-       // Register unique strings
-       for ( i = 0, l = this.module.tests; i < l.length; i++ ) {
-               if ( this.module.tests[ i ].name === this.testName ) {
-                       this.testName += " ";
+       if ( config.noglobals ) {
+               for ( var key in window ) {
+                       if ( hasOwn.call( window, key ) ) {
+                               // in Opera sometimes DOM element ids show up here, ignore them
+                               if ( /^qunit-test-output/.test( key ) ) {
+                                       continue;
+                               }
+                               config.pollution.push( key );
+                       }
                }
        }
+}
 
-       this.testId = generateHash( this.module.name, this.testName );
+function checkPollution() {
+       var newGlobals,
+               deletedGlobals,
+               old = config.pollution;
 
-       this.module.tests.push( {
-               name: this.testName,
-               testId: this.testId
-       } );
+       saveGlobal();
 
-       if ( settings.skip ) {
+       newGlobals = diff( config.pollution, old );
+       if ( newGlobals.length > 0 ) {
+               QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) );
+       }
 
-               // Skipped tests will fully ignore any sent callback
-               this.callback = function() {};
-               this.async = false;
-               this.expected = 0;
-       } else {
-               this.assert = new Assert( this );
+       deletedGlobals = diff( old, config.pollution );
+       if ( deletedGlobals.length > 0 ) {
+               QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) );
+       }
+}
+
+// returns a new Array with the elements that are in a but not in b
+function diff( a, b ) {
+       var i, j,
+               result = a.slice();
+
+       for ( i = 0; i < result.length; i++ ) {
+               for ( j = 0; j < b.length; j++ ) {
+                       if ( result[ i ] === b[ j ] ) {
+                               result.splice( i, 1 );
+                               i--;
+                               break;
+                       }
+               }
+       }
+       return result;
+}
+
+function extend( a, b, undefOnly ) {
+       for ( var prop in b ) {
+               if ( hasOwn.call( b, prop ) ) {
+
+                       // Avoid "Member not found" error in IE8 caused by messing with window.constructor
+                       if ( !( prop === "constructor" && a === window ) ) {
+                               if ( b[ prop ] === undefined ) {
+                                       delete a[ prop ];
+                               } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) {
+                                       a[ prop ] = b[ prop ];
+                               }
+                       }
+               }
+       }
+
+       return a;
+}
+
+function runLoggingCallbacks( key, args ) {
+       var i, l, callbacks;
+
+       callbacks = config.callbacks[ key ];
+       for ( i = 0, l = callbacks.length; i < l; i++ ) {
+               callbacks[ i ]( args );
+       }
+}
+
+// DEPRECATED: This will be removed on 2.0.0+
+// This function verifies if the loggingCallbacks were modified by the user
+// If so, it will restore it, assign the given callback and print a console warning
+function verifyLoggingCallbacks() {
+       var loggingCallback, userCallback;
+
+       for ( loggingCallback in loggingCallbacks ) {
+               if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) {
+
+                       userCallback = QUnit[ loggingCallback ];
+
+                       // Restore the callback function
+                       QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ];
+
+                       // Assign the deprecated given callback
+                       QUnit[ loggingCallback ]( userCallback );
+
+                       if ( window.console && window.console.warn ) {
+                               window.console.warn(
+                                       "QUnit." + loggingCallback + " was replaced with a new value.\n" +
+                                       "Please, check out the documentation on how to apply logging callbacks.\n" +
+                                       "Reference: http://api.qunitjs.com/category/callbacks/"
+                               );
+                       }
+               }
+       }
+}
+
+// from jquery.js
+function inArray( elem, array ) {
+       if ( array.indexOf ) {
+               return array.indexOf( elem );
+       }
+
+       for ( var i = 0, length = array.length; i < length; i++ ) {
+               if ( array[ i ] === elem ) {
+                       return i;
+               }
+       }
+
+       return -1;
+}
+
+function Test( settings ) {
+       var i, l;
+
+       ++Test.count;
+
+       extend( this, settings );
+       this.assertions = [];
+       this.semaphore = 0;
+       this.usedAsync = false;
+       this.module = config.currentModule;
+       this.stack = sourceFromStacktrace( 3 );
+
+       // Register unique strings
+       for ( i = 0, l = this.module.tests; i < l.length; i++ ) {
+               if ( this.module.tests[ i ].name === this.testName ) {
+                       this.testName += " ";
+               }
+       }
+
+       this.testId = generateHash( this.module.name, this.testName );
+
+       this.module.tests.push({
+               name: this.testName,
+               testId: this.testId
+       });
+
+       if ( settings.skip ) {
+
+               // Skipped tests will fully ignore any sent callback
+               this.callback = function() {};
+               this.async = false;
+               this.expected = 0;
+       } else {
+               this.assert = new Assert( this );
        }
 }
 
@@ -778,30 +836,28 @@ Test.prototype = {
                                        passed: config.moduleStats.all - config.moduleStats.bad,
                                        total: config.moduleStats.all,
                                        runtime: now() - config.moduleStats.started
-                               } );
+                               });
                        }
                        config.previousModule = this.module;
                        config.moduleStats = { all: 0, bad: 0, started: now() };
                        runLoggingCallbacks( "moduleStart", {
                                name: this.module.name,
                                tests: this.module.tests
-                       } );
+                       });
                }
 
                config.current = this;
 
-               if ( this.module.testEnvironment ) {
-                       delete this.module.testEnvironment.beforeEach;
-                       delete this.module.testEnvironment.afterEach;
-               }
                this.testEnvironment = extend( {}, this.module.testEnvironment );
+               delete this.testEnvironment.beforeEach;
+               delete this.testEnvironment.afterEach;
 
                this.started = now();
                runLoggingCallbacks( "testStart", {
                        name: this.testName,
                        module: this.module.name,
                        testId: this.testId
-               } );
+               });
 
                if ( !config.pollution ) {
                        saveGlobal();
@@ -820,17 +876,19 @@ Test.prototype = {
                this.callbackStarted = now();
 
                if ( config.notrycatch ) {
-                       runTest( this );
+                       promise = this.callback.call( this.testEnvironment, this.assert );
+                       this.resolvePromise( promise );
                        return;
                }
 
                try {
-                       runTest( this );
+                       promise = this.callback.call( this.testEnvironment, this.assert );
+                       this.resolvePromise( promise );
                } catch ( e ) {
                        this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " +
                                this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
 
-                       // Else next test will carry the responsibility
+                       // else next test will carry the responsibility
                        saveGlobal();
 
                        // Restart the tests if they're blocking
@@ -838,11 +896,6 @@ Test.prototype = {
                                QUnit.start();
                        }
                }
-
-               function runTest( test ) {
-                       promise = test.callback.call( test.testEnvironment, test.assert );
-                       test.resolvePromise( promise );
-               }
        },
 
        after: function() {
@@ -855,19 +908,16 @@ Test.prototype = {
                return function runHook() {
                        config.current = test;
                        if ( config.notrycatch ) {
-                               callHook();
+                               promise = hook.call( test.testEnvironment, test.assert );
+                               test.resolvePromise( promise, hookName );
                                return;
                        }
                        try {
-                               callHook();
-                       } catch ( error ) {
-                               test.pushFailure( hookName + " failed on " + test.testName + ": " +
-                               ( error.message || error ), extractStacktrace( error, 0 ) );
-                       }
-
-                       function callHook() {
                                promise = hook.call( test.testEnvironment, test.assert );
                                test.resolvePromise( promise, hookName );
+                       } catch ( error ) {
+                               test.pushFailure( hookName + " failed on " + test.testName + ": " +
+                                       ( error.message || error ), extractStacktrace( error, 0 ) );
                        }
                };
        },
@@ -876,20 +926,16 @@ Test.prototype = {
        hooks: function( handler ) {
                var hooks = [];
 
-               function processHooks( test, module ) {
-                       if ( module.parentModule ) {
-                               processHooks( test, module.parentModule );
-                       }
-                       if ( module.testEnvironment &&
-                               QUnit.objectType( module.testEnvironment[ handler ] ) === "function" ) {
-                               hooks.push( test.queueHook( module.testEnvironment[ handler ], handler ) );
-                       }
+               // Hooks are ignored on skipped tests
+               if ( this.skip ) {
+                       return hooks;
                }
 
-               // Hooks are ignored on skipped tests
-               if ( !this.skip ) {
-                       processHooks( this, this.module );
+               if ( this.module.testEnvironment &&
+                               QUnit.objectType( this.module.testEnvironment[ handler ] ) === "function" ) {
+                       hooks.push( this.queueHook( this.module.testEnvironment[ handler ], handler ) );
                }
+
                return hooks;
        },
 
@@ -934,12 +980,9 @@ Test.prototype = {
                        assertions: this.assertions,
                        testId: this.testId,
 
-                       // Source of Test
-                       source: this.stack,
-
                        // DEPRECATED: this property will be removed in 2.0.0, use runtime instead
                        duration: this.runtime
-               } );
+               });
 
                // QUnit.reset() is deprecated and will be replaced for a new
                // fixture reset function on QUnit 2.0/2.1.
@@ -950,7 +993,7 @@ Test.prototype = {
        },
 
        queue: function() {
-               var priority,
+               var bad,
                        test = this;
 
                if ( !this.valid() ) {
@@ -959,13 +1002,14 @@ Test.prototype = {
 
                function run() {
 
-                       // Each of these can by async
-                       synchronize( [
+                       // each of these can by async
+                       synchronize([
                                function() {
                                        test.before();
                                },
 
                                test.hooks( "beforeEach" ),
+
                                function() {
                                        test.run();
                                },
@@ -978,33 +1022,35 @@ Test.prototype = {
                                function() {
                                        test.finish();
                                }
-                       ] );
+                       ]);
                }
 
-               // Prioritize previously failed tests, detected from sessionStorage
-               priority = QUnit.config.reorder && defined.sessionStorage &&
+               // `bad` initialized at top of scope
+               // defer when previous test run passed, if storage is available
+               bad = QUnit.config.reorder && defined.sessionStorage &&
                                +sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName );
 
-               return synchronize( run, priority, config.seed );
+               if ( bad ) {
+                       run();
+               } else {
+                       synchronize( run, true );
+               }
        },
 
-       pushResult: function( resultInfo ) {
-
-               // Destructure of resultInfo = { result, actual, expected, message, negative }
+       push: function( result, actual, expected, message ) {
                var source,
                        details = {
                                module: this.module.name,
                                name: this.testName,
-                               result: resultInfo.result,
-                               message: resultInfo.message,
-                               actual: resultInfo.actual,
-                               expected: resultInfo.expected,
+                               result: result,
+                               message: message,
+                               actual: actual,
+                               expected: expected,
                                testId: this.testId,
-                               negative: resultInfo.negative || false,
                                runtime: now() - this.started
                        };
 
-               if ( !resultInfo.result ) {
+               if ( !result ) {
                        source = sourceFromStacktrace();
 
                        if ( source ) {
@@ -1014,14 +1060,14 @@ Test.prototype = {
 
                runLoggingCallbacks( "log", details );
 
-               this.assertions.push( {
-                       result: !!resultInfo.result,
-                       message: resultInfo.message
-               } );
+               this.assertions.push({
+                       result: !!result,
+                       message: message
+               });
        },
 
        pushFailure: function( message, source, actual ) {
-               if ( !( this instanceof Test ) ) {
+               if ( !this instanceof Test ) {
                        throw new Error( "pushFailure() assertion outside test context, was " +
                                sourceFromStacktrace( 2 ) );
                }
@@ -1042,10 +1088,10 @@ Test.prototype = {
 
                runLoggingCallbacks( "log", details );
 
-               this.assertions.push( {
+               this.assertions.push({
                        result: false,
                        message: message
-               } );
+               });
        },
 
        resolvePromise: function( promise, phase ) {
@@ -1057,14 +1103,14 @@ Test.prototype = {
                                QUnit.stop();
                                then.call(
                                        promise,
-                                       function() { QUnit.start(); },
+                                       QUnit.start,
                                        function( error ) {
                                                message = "Promise rejected " +
                                                        ( !phase ? "during" : phase.replace( /Each$/, "" ) ) +
                                                        " " + test.testName + ": " + ( error.message || error );
                                                test.pushFailure( message, extractStacktrace( error, 0 ) );
 
-                                               // Else next test will carry the responsibility
+                                               // else next test will carry the responsibility
                                                saveGlobal();
 
                                                // Unblock
@@ -1076,45 +1122,21 @@ Test.prototype = {
        },
 
        valid: function() {
-               var filter = config.filter,
-                       regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec( filter ),
-                       module = config.module && config.module.toLowerCase(),
-                       fullName = ( this.module.name + ": " + this.testName );
-
-               function moduleChainNameMatch( testModule ) {
-                       var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;
-                       if ( testModuleName === module ) {
-                               return true;
-                       } else if ( testModule.parentModule ) {
-                               return moduleChainNameMatch( testModule.parentModule );
-                       } else {
-                               return false;
-                       }
-               }
-
-               function moduleChainIdMatch( testModule ) {
-                       return inArray( testModule.moduleId, config.moduleId ) > -1 ||
-                               testModule.parentModule && moduleChainIdMatch( testModule.parentModule );
-               }
+               var include,
+                       filter = config.filter,
+                       module = QUnit.urlParams.module && QUnit.urlParams.module.toLowerCase(),
+                       fullName = ( this.module.name + ": " + this.testName ).toLowerCase();
 
                // Internally-generated tests are always valid
                if ( this.callback && this.callback.validTest ) {
                        return true;
                }
 
-               if ( config.moduleId && config.moduleId.length > 0 &&
-                       !moduleChainIdMatch( this.module ) ) {
-
-                       return false;
-               }
-
-               if ( config.testId && config.testId.length > 0 &&
-                       inArray( this.testId, config.testId ) < 0 ) {
-
+               if ( config.testId.length > 0 && inArray( this.testId, config.testId ) < 0 ) {
                        return false;
                }
 
-               if ( module && !moduleChainNameMatch( this.module ) ) {
+               if ( module && ( !this.module.name || this.module.name.toLowerCase() !== module ) ) {
                        return false;
                }
 
@@ -1122,25 +1144,9 @@ Test.prototype = {
                        return true;
                }
 
-               return regexFilter ?
-                       this.regexFilter( !!regexFilter[ 1 ], regexFilter[ 2 ], regexFilter[ 3 ], fullName ) :
-                       this.stringFilter( filter, fullName );
-       },
-
-       regexFilter: function( exclude, pattern, flags, fullName ) {
-               var regex = new RegExp( pattern, flags );
-               var match = regex.test( fullName );
-
-               return match !== exclude;
-       },
-
-       stringFilter: function( filter, fullName ) {
-               filter = filter.toLowerCase();
-               fullName = fullName.toLowerCase();
-
-               var include = filter.charAt( 0 ) !== "!";
+               include = filter.charAt( 0 ) !== "!";
                if ( !include ) {
-                       filter = filter.slice( 1 );
+                       filter = filter.toLowerCase().slice( 1 );
                }
 
                // If the filter matches, we need to honour include
@@ -1151,6 +1157,7 @@ Test.prototype = {
                // Otherwise, do the opposite
                return !include;
        }
+
 };
 
 // Resets the test setup. Useful for tests that modify the DOM.
@@ -1163,7 +1170,7 @@ QUnit.reset = function() {
 
        // Return on non-browser environments
        // This is necessary to not break on node tests
-       if ( !defined.document ) {
+       if ( typeof window === "undefined" ) {
                return;
        }
 
@@ -1211,157 +1218,6 @@ function generateHash( module, testName ) {
        return hex.slice( -8 );
 }
 
-function synchronize( callback, priority, seed ) {
-       var last = !priority,
-               index;
-
-       if ( QUnit.objectType( callback ) === "array" ) {
-               while ( callback.length ) {
-                       synchronize( callback.shift() );
-               }
-               return;
-       }
-
-       if ( priority ) {
-               config.queue.splice( priorityCount++, 0, callback );
-       } else if ( seed ) {
-               if ( !unitSampler ) {
-                       unitSampler = unitSamplerGenerator( seed );
-               }
-
-               // Insert into a random position after all priority items
-               index = Math.floor( unitSampler() * ( config.queue.length - priorityCount + 1 ) );
-               config.queue.splice( priorityCount + index, 0, callback );
-       } else {
-               config.queue.push( callback );
-       }
-
-       if ( config.autorun && !config.blocking ) {
-               process( last );
-       }
-}
-
-function unitSamplerGenerator( seed ) {
-
-       // 32-bit xorshift, requires only a nonzero seed
-       // http://excamera.com/sphinx/article-xorshift.html
-       var sample = parseInt( generateHash( seed ), 16 ) || -1;
-       return function() {
-               sample ^= sample << 13;
-               sample ^= sample >>> 17;
-               sample ^= sample << 5;
-
-               // ECMAScript has no unsigned number type
-               if ( sample < 0 ) {
-                       sample += 0x100000000;
-               }
-
-               return sample / 0x100000000;
-       };
-}
-
-function saveGlobal() {
-       config.pollution = [];
-
-       if ( config.noglobals ) {
-               for ( var key in global ) {
-                       if ( hasOwn.call( global, key ) ) {
-
-                               // In Opera sometimes DOM element ids show up here, ignore them
-                               if ( /^qunit-test-output/.test( key ) ) {
-                                       continue;
-                               }
-                               config.pollution.push( key );
-                       }
-               }
-       }
-}
-
-function checkPollution() {
-       var newGlobals,
-               deletedGlobals,
-               old = config.pollution;
-
-       saveGlobal();
-
-       newGlobals = diff( config.pollution, old );
-       if ( newGlobals.length > 0 ) {
-               QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) );
-       }
-
-       deletedGlobals = diff( old, config.pollution );
-       if ( deletedGlobals.length > 0 ) {
-               QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) );
-       }
-}
-
-// Will be exposed as QUnit.asyncTest
-function asyncTest( testName, expected, callback ) {
-       if ( arguments.length === 2 ) {
-               callback = expected;
-               expected = null;
-       }
-
-       QUnit.test( testName, expected, callback, true );
-}
-
-// Will be exposed as QUnit.test
-function test( testName, expected, callback, async ) {
-       if ( focused )  { return; }
-
-       var newTest;
-
-       if ( arguments.length === 2 ) {
-               callback = expected;
-               expected = null;
-       }
-
-       newTest = new Test( {
-               testName: testName,
-               expected: expected,
-               async: async,
-               callback: callback
-       } );
-
-       newTest.queue();
-}
-
-// Will be exposed as QUnit.skip
-function skip( testName ) {
-       if ( focused )  { return; }
-
-       var test = new Test( {
-               testName: testName,
-               skip: true
-       } );
-
-       test.queue();
-}
-
-// Will be exposed as QUnit.only
-function only( testName, expected, callback, async ) {
-       var newTest;
-
-       if ( focused )  { return; }
-
-       QUnit.config.queue.length = 0;
-       focused = true;
-
-       if ( arguments.length === 2 ) {
-               callback = expected;
-               expected = null;
-       }
-
-       newTest = new Test( {
-               testName: testName,
-               expected: expected,
-               async: async,
-               callback: callback
-       } );
-
-       newTest.queue();
-}
-
 function Assert( testContext ) {
        this.test = testContext;
 }
@@ -1379,55 +1235,30 @@ QUnit.assert = Assert.prototype = {
                }
        },
 
-       // Increment this Test's semaphore counter, then return a function that
+       // Increment this Test's semaphore counter, then return a single-use function that
        // decrements that counter a maximum of once.
-       async: function( count ) {
+       async: function() {
                var test = this.test,
-                       popped = false,
-                       acceptCallCount = count;
-
-               if ( typeof acceptCallCount === "undefined" ) {
-                       acceptCallCount = 1;
-               }
+                       popped = false;
 
                test.semaphore += 1;
                test.usedAsync = true;
                pauseProcessing();
 
                return function done() {
-
-                       if ( popped ) {
-                               test.pushFailure( "Too many calls to the `assert.async` callback",
+                       if ( !popped ) {
+                               test.semaphore -= 1;
+                               popped = true;
+                               resumeProcessing();
+                       } else {
+                               test.pushFailure( "Called the callback returned from `assert.async` more than once",
                                        sourceFromStacktrace( 2 ) );
-                               return;
-                       }
-                       acceptCallCount -= 1;
-                       if ( acceptCallCount > 0 ) {
-                               return;
                        }
-
-                       test.semaphore -= 1;
-                       popped = true;
-                       resumeProcessing();
                };
        },
 
        // Exports test.push() to the user API
-       // Alias of pushResult.
-       push: function( result, actual, expected, message, negative ) {
-               var currentAssert = this instanceof Assert ? this : QUnit.config.current.assert;
-               return currentAssert.pushResult( {
-                       result: result,
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: negative
-               } );
-       },
-
-       pushResult: function( resultInfo ) {
-
-               // Destructure of resultInfo = { result, actual, expected, message, negative }
+       push: function( /* result, actual, expected, message */ ) {
                var assert = this,
                        currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current;
 
@@ -1450,119 +1281,98 @@ QUnit.assert = Assert.prototype = {
                if ( !( assert instanceof Assert ) ) {
                        assert = currentTest.assert;
                }
-
-               return assert.test.pushResult( resultInfo );
+               return assert.test.push.apply( assert.test, arguments );
        },
 
+       /**
+        * Asserts rough true-ish result.
+        * @name ok
+        * @function
+        * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
+        */
        ok: function( result, message ) {
                message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " +
                        QUnit.dump.parse( result ) );
-               this.pushResult( {
-                       result: !!result,
-                       actual: result,
-                       expected: true,
-                       message: message
-               } );
-       },
-
-       notOk: function( result, message ) {
-               message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " +
-                       QUnit.dump.parse( result ) );
-               this.pushResult( {
-                       result: !result,
-                       actual: result,
-                       expected: false,
-                       message: message
-               } );
+               this.push( !!result, result, true, message );
        },
 
+       /**
+        * Assert that the first two arguments are equal, with an optional message.
+        * Prints out both actual and expected values.
+        * @name equal
+        * @function
+        * @example equal( format( "{0} bytes.", 2), "2 bytes.", "replaces {0} with next argument" );
+        */
        equal: function( actual, expected, message ) {
                /*jshint eqeqeq:false */
-               this.pushResult( {
-                       result: expected == actual,
-                       actual: actual,
-                       expected: expected,
-                       message: message
-               } );
+               this.push( expected == actual, actual, expected, message );
        },
 
+       /**
+        * @name notEqual
+        * @function
+        */
        notEqual: function( actual, expected, message ) {
                /*jshint eqeqeq:false */
-               this.pushResult( {
-                       result: expected != actual,
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: true
-               } );
+               this.push( expected != actual, actual, expected, message );
        },
 
+       /**
+        * @name propEqual
+        * @function
+        */
        propEqual: function( actual, expected, message ) {
                actual = objectValues( actual );
                expected = objectValues( expected );
-               this.pushResult( {
-                       result: QUnit.equiv( actual, expected ),
-                       actual: actual,
-                       expected: expected,
-                       message: message
-               } );
+               this.push( QUnit.equiv( actual, expected ), actual, expected, message );
        },
 
+       /**
+        * @name notPropEqual
+        * @function
+        */
        notPropEqual: function( actual, expected, message ) {
                actual = objectValues( actual );
                expected = objectValues( expected );
-               this.pushResult( {
-                       result: !QUnit.equiv( actual, expected ),
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: true
-               } );
+               this.push( !QUnit.equiv( actual, expected ), actual, expected, message );
        },
 
+       /**
+        * @name deepEqual
+        * @function
+        */
        deepEqual: function( actual, expected, message ) {
-               this.pushResult( {
-                       result: QUnit.equiv( actual, expected ),
-                       actual: actual,
-                       expected: expected,
-                       message: message
-               } );
+               this.push( QUnit.equiv( actual, expected ), actual, expected, message );
        },
 
+       /**
+        * @name notDeepEqual
+        * @function
+        */
        notDeepEqual: function( actual, expected, message ) {
-               this.pushResult( {
-                       result: !QUnit.equiv( actual, expected ),
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: true
-               } );
+               this.push( !QUnit.equiv( actual, expected ), actual, expected, message );
        },
 
+       /**
+        * @name strictEqual
+        * @function
+        */
        strictEqual: function( actual, expected, message ) {
-               this.pushResult( {
-                       result: expected === actual,
-                       actual: actual,
-                       expected: expected,
-                       message: message
-               } );
+               this.push( expected === actual, actual, expected, message );
        },
 
+       /**
+        * @name notStrictEqual
+        * @function
+        */
        notStrictEqual: function( actual, expected, message ) {
-               this.pushResult( {
-                       result: expected !== actual,
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: true
-               } );
+               this.push( expected !== actual, actual, expected, message );
        },
 
        "throws": function( block, expected, message ) {
                var actual, expectedType,
                        expectedOutput = expected,
-                       ok = false,
-                       currentTest = ( this instanceof Assert && this.test ) || QUnit.config.current;
+                       ok = false;
 
                // 'expected' is optional unless doing string comparison
                if ( message == null && typeof expected === "string" ) {
@@ -1570,351 +1380,285 @@ QUnit.assert = Assert.prototype = {
                        expected = null;
                }
 
-               currentTest.ignoreGlobalErrors = true;
+               this.test.ignoreGlobalErrors = true;
                try {
-                       block.call( currentTest.testEnvironment );
-               } catch ( e ) {
+                       block.call( this.test.testEnvironment );
+               } catch (e) {
                        actual = e;
                }
-               currentTest.ignoreGlobalErrors = false;
+               this.test.ignoreGlobalErrors = false;
 
                if ( actual ) {
                        expectedType = QUnit.objectType( expected );
 
-                       // We don't want to validate thrown error
+                       // we don't want to validate thrown error
                        if ( !expected ) {
                                ok = true;
                                expectedOutput = null;
 
-                       // Expected is a regexp
+                       // expected is a regexp
                        } else if ( expectedType === "regexp" ) {
                                ok = expected.test( errorString( actual ) );
 
-                       // Expected is a string
+                       // expected is a string
                        } else if ( expectedType === "string" ) {
                                ok = expected === errorString( actual );
 
-                       // Expected is a constructor, maybe an Error constructor
+                       // expected is a constructor, maybe an Error constructor
                        } else if ( expectedType === "function" && actual instanceof expected ) {
                                ok = true;
 
-                       // Expected is an Error object
+                       // expected is an Error object
                        } else if ( expectedType === "object" ) {
                                ok = actual instanceof expected.constructor &&
                                        actual.name === expected.name &&
                                        actual.message === expected.message;
 
-                       // Expected is a validation function which returns true if validation passed
+                       // expected is a validation function which returns true if validation passed
                        } else if ( expectedType === "function" && expected.call( {}, actual ) === true ) {
                                expectedOutput = null;
                                ok = true;
                        }
-               }
 
-               currentTest.assert.pushResult( {
-                       result: ok,
-                       actual: actual,
-                       expected: expectedOutput,
-                       message: message
-               } );
+                       this.push( ok, actual, expectedOutput, message );
+               } else {
+                       this.test.pushFailure( message, null, "No exception was thrown." );
+               }
        }
 };
 
-// Provide an alternative to assert.throws(), for environments that consider throws a reserved word
+// Provide an alternative to assert.throws(), for enviroments that consider throws a reserved word
 // Known to us are: Closure Compiler, Narwhal
-( function() {
+(function() {
        /*jshint sub:true */
-       Assert.prototype.raises = Assert.prototype [ "throws" ]; //jscs:ignore requireDotNotation
-}() );
-
-function errorString( error ) {
-       var name, message,
-               resultErrorString = error.toString();
-       if ( resultErrorString.substring( 0, 7 ) === "[object" ) {
-               name = error.name ? error.name.toString() : "Error";
-               message = error.message ? error.message.toString() : "";
-               if ( name && message ) {
-                       return name + ": " + message;
-               } else if ( name ) {
-                       return name;
-               } else if ( message ) {
-                       return message;
-               } else {
-                       return "Error";
-               }
-       } else {
-               return resultErrorString;
-       }
-}
+       Assert.prototype.raises = Assert.prototype[ "throws" ];
+}());
 
 // Test for equality any JavaScript type.
 // Author: Philippe Rathé <prathe@gmail.com>
-QUnit.equiv = ( function() {
-
-       // Stack to decide between skip/abort functions
-       var callers = [];
-
-       // Stack to avoiding loops from circular referencing
-       var parents = [];
-       var parentsB = [];
-
-       var getProto = Object.getPrototypeOf || function( obj ) {
+QUnit.equiv = (function() {
+
+       // Call the o related callback with the given arguments.
+       function bindCallbacks( o, callbacks, args ) {
+               var prop = QUnit.objectType( o );
+               if ( prop ) {
+                       if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
+                               return callbacks[ prop ].apply( callbacks, args );
+                       } else {
+                               return callbacks[ prop ]; // or undefined
+                       }
+               }
+       }
 
-               /*jshint proto: true */
-               return obj.__proto__;
-       };
+       // the real equiv function
+       var innerEquiv,
 
-       function useStrictEquality( b, a ) {
+               // stack to decide between skip/abort functions
+               callers = [],
 
-               // To catch short annotation VS 'new' annotation of a declaration. e.g.:
-               // `var i = 1;`
-               // `var j = new Number(1);`
-               if ( typeof a === "object" ) {
-                       a = a.valueOf();
-               }
-               if ( typeof b === "object" ) {
-                       b = b.valueOf();
-               }
+               // stack to avoiding loops from circular referencing
+               parents = [],
+               parentsB = [],
 
-               return a === b;
-       }
+               getProto = Object.getPrototypeOf || function( obj ) {
+                       /* jshint camelcase: false, proto: true */
+                       return obj.__proto__;
+               },
+               callbacks = (function() {
 
-       function compareConstructors( a, b ) {
-               var protoA = getProto( a );
-               var protoB = getProto( b );
+                       // for string, boolean, number and null
+                       function useStrictEquality( b, a ) {
 
-               // Comparing constructors is more strict than using `instanceof`
-               if ( a.constructor === b.constructor ) {
-                       return true;
-               }
+                               /*jshint eqeqeq:false */
+                               if ( b instanceof a.constructor || a instanceof b.constructor ) {
 
-               // Ref #851
-               // If the obj prototype descends from a null constructor, treat it
-               // as a null prototype.
-               if ( protoA && protoA.constructor === null ) {
-                       protoA = null;
-               }
-               if ( protoB && protoB.constructor === null ) {
-                       protoB = null;
-               }
+                                       // to catch short annotation VS 'new' annotation of a
+                                       // declaration
+                                       // e.g. var i = 1;
+                                       // var j = new Number(1);
+                                       return a == b;
+                               } else {
+                                       return a === b;
+                               }
+                       }
 
-               // Allow objects with no prototype to be equivalent to
-               // objects with Object as their constructor.
-               if ( ( protoA === null && protoB === Object.prototype ) ||
-                               ( protoB === null && protoA === Object.prototype ) ) {
-                       return true;
-               }
+                       return {
+                               "string": useStrictEquality,
+                               "boolean": useStrictEquality,
+                               "number": useStrictEquality,
+                               "null": useStrictEquality,
+                               "undefined": useStrictEquality,
 
-               return false;
-       }
+                               "nan": function( b ) {
+                                       return isNaN( b );
+                               },
 
-       function getRegExpFlags( regexp ) {
-               return "flags" in regexp ? regexp.flags : regexp.toString().match( /[gimuy]*$/ )[ 0 ];
-       }
+                               "date": function( b, a ) {
+                                       return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
+                               },
 
-       var callbacks = {
-               "string": useStrictEquality,
-               "boolean": useStrictEquality,
-               "number": useStrictEquality,
-               "null": useStrictEquality,
-               "undefined": useStrictEquality,
-               "symbol": useStrictEquality,
-               "date": useStrictEquality,
+                               "regexp": function( b, a ) {
+                                       return QUnit.objectType( b ) === "regexp" &&
 
-               "nan": function() {
-                       return true;
-               },
+                                               // the regex itself
+                                               a.source === b.source &&
 
-               "regexp": function( b, a ) {
-                       return a.source === b.source &&
+                                               // and its modifiers
+                                               a.global === b.global &&
 
-                               // Include flags in the comparison
-                               getRegExpFlags( a ) === getRegExpFlags( b );
-               },
+                                               // (gmi) ...
+                                               a.ignoreCase === b.ignoreCase &&
+                                               a.multiline === b.multiline &&
+                                               a.sticky === b.sticky;
+                               },
 
-               // - skip when the property is a method of an instance (OOP)
-               // - abort otherwise,
-               // initial === would have catch identical references anyway
-               "function": function() {
-                       var caller = callers[ callers.length - 1 ];
-                       return caller !== Object && typeof caller !== "undefined";
-               },
+                               // - skip when the property is a method of an instance (OOP)
+                               // - abort otherwise,
+                               // initial === would have catch identical references anyway
+                               "function": function() {
+                                       var caller = callers[ callers.length - 1 ];
+                                       return caller !== Object && typeof caller !== "undefined";
+                               },
 
-               "array": function( b, a ) {
-                       var i, j, len, loop, aCircular, bCircular;
+                               "array": function( b, a ) {
+                                       var i, j, len, loop, aCircular, bCircular;
 
-                       len = a.length;
-                       if ( len !== b.length ) {
+                                       // b could be an object literal here
+                                       if ( QUnit.objectType( b ) !== "array" ) {
+                                               return false;
+                                       }
 
-                               // Safe and faster
-                               return false;
-                       }
+                                       len = a.length;
+                                       if ( len !== b.length ) {
+                                               // safe and faster
+                                               return false;
+                                       }
 
-                       // Track reference to avoid circular references
-                       parents.push( a );
-                       parentsB.push( b );
-                       for ( i = 0; i < len; i++ ) {
-                               loop = false;
-                               for ( j = 0; j < parents.length; j++ ) {
-                                       aCircular = parents[ j ] === a[ i ];
-                                       bCircular = parentsB[ j ] === b[ i ];
-                                       if ( aCircular || bCircular ) {
-                                               if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
-                                                       loop = true;
-                                               } else {
+                                       // track reference to avoid circular references
+                                       parents.push( a );
+                                       parentsB.push( b );
+                                       for ( i = 0; i < len; i++ ) {
+                                               loop = false;
+                                               for ( j = 0; j < parents.length; j++ ) {
+                                                       aCircular = parents[ j ] === a[ i ];
+                                                       bCircular = parentsB[ j ] === b[ i ];
+                                                       if ( aCircular || bCircular ) {
+                                                               if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
+                                                                       loop = true;
+                                                               } else {
+                                                                       parents.pop();
+                                                                       parentsB.pop();
+                                                                       return false;
+                                                               }
+                                                       }
+                                               }
+                                               if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
                                                        parents.pop();
                                                        parentsB.pop();
                                                        return false;
                                                }
                                        }
-                               }
-                               if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
                                        parents.pop();
                                        parentsB.pop();
-                                       return false;
-                               }
-                       }
-                       parents.pop();
-                       parentsB.pop();
-                       return true;
-               },
-
-               "set": function( b, a ) {
-                       var innerEq,
-                               outerEq = true;
-
-                       if ( a.size !== b.size ) {
-                               return false;
-                       }
-
-                       a.forEach( function( aVal ) {
-                               innerEq = false;
-
-                               b.forEach( function( bVal ) {
-                                       if ( innerEquiv( bVal, aVal ) ) {
-                                               innerEq = true;
-                                       }
-                               } );
-
-                               if ( !innerEq ) {
-                                       outerEq = false;
-                               }
-                       } );
-
-                       return outerEq;
-               },
+                                       return true;
+                               },
 
-               "map": function( b, a ) {
-                       var innerEq,
-                               outerEq = true;
+                               "object": function( b, a ) {
 
-                       if ( a.size !== b.size ) {
-                               return false;
-                       }
+                                       /*jshint forin:false */
+                                       var i, j, loop, aCircular, bCircular,
+                                               // Default to true
+                                               eq = true,
+                                               aProperties = [],
+                                               bProperties = [];
 
-                       a.forEach( function( aVal, aKey ) {
-                               innerEq = false;
+                                       // comparing constructors is more strict than using
+                                       // instanceof
+                                       if ( a.constructor !== b.constructor ) {
 
-                               b.forEach( function( bVal, bKey ) {
-                                       if ( innerEquiv( [ bVal, bKey ], [ aVal, aKey ] ) ) {
-                                               innerEq = true;
+                                               // Allow objects with no prototype to be equivalent to
+                                               // objects with Object as their constructor.
+                                               if ( !( ( getProto( a ) === null && getProto( b ) === Object.prototype ) ||
+                                                       ( getProto( b ) === null && getProto( a ) === Object.prototype ) ) ) {
+                                                       return false;
+                                               }
                                        }
-                               } );
-
-                               if ( !innerEq ) {
-                                       outerEq = false;
-                               }
-                       } );
-
-                       return outerEq;
-               },
-
-               "object": function( b, a ) {
-                       var i, j, loop, aCircular, bCircular;
-
-                       // Default to true
-                       var eq = true;
-                       var aProperties = [];
-                       var bProperties = [];
-
-                       if ( compareConstructors( a, b ) === false ) {
-                               return false;
-                       }
 
-                       // Stack constructor before traversing properties
-                       callers.push( a.constructor );
-
-                       // Track reference to avoid circular references
-                       parents.push( a );
-                       parentsB.push( b );
-
-                       // Be strict: don't ensure hasOwnProperty and go deep
-                       for ( i in a ) {
-                               loop = false;
-                               for ( j = 0; j < parents.length; j++ ) {
-                                       aCircular = parents[ j ] === a[ i ];
-                                       bCircular = parentsB[ j ] === b[ i ];
-                                       if ( aCircular || bCircular ) {
-                                               if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
-                                                       loop = true;
-                                               } else {
+                                       // stack constructor before traversing properties
+                                       callers.push( a.constructor );
+
+                                       // track reference to avoid circular references
+                                       parents.push( a );
+                                       parentsB.push( b );
+
+                                       // be strict: don't ensure hasOwnProperty and go deep
+                                       for ( i in a ) {
+                                               loop = false;
+                                               for ( j = 0; j < parents.length; j++ ) {
+                                                       aCircular = parents[ j ] === a[ i ];
+                                                       bCircular = parentsB[ j ] === b[ i ];
+                                                       if ( aCircular || bCircular ) {
+                                                               if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
+                                                                       loop = true;
+                                                               } else {
+                                                                       eq = false;
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
+                                               aProperties.push( i );
+                                               if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
                                                        eq = false;
                                                        break;
                                                }
                                        }
-                               }
-                               aProperties.push( i );
-                               if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
-                                       eq = false;
-                                       break;
-                               }
-                       }
-
-                       parents.pop();
-                       parentsB.pop();
 
-                       // Unstack, we are done
-                       callers.pop();
+                                       parents.pop();
+                                       parentsB.pop();
+                                       callers.pop(); // unstack, we are done
 
-                       for ( i in b ) {
+                                       for ( i in b ) {
+                                               bProperties.push( i ); // collect b's properties
+                                       }
 
-                               // Collect b's properties
-                               bProperties.push( i );
-                       }
+                                       // Ensures identical properties name
+                                       return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
+                               }
+                       };
+               }());
 
-                       // Ensures identical properties name
-                       return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
+       innerEquiv = function() { // can take multiple arguments
+               var args = [].slice.apply( arguments );
+               if ( args.length < 2 ) {
+                       return true; // end transition
                }
-       };
-
-       function typeEquiv( a, b ) {
-               var type = QUnit.objectType( a );
-               return QUnit.objectType( b ) === type && callbacks[ type ]( b, a );
-       }
-
-       // The real equiv function
-       function innerEquiv( a, b ) {
 
-               // We're done when there's nothing more to compare
-               if ( arguments.length < 2 ) {
-                       return true;
-               }
+               return ( (function( a, b ) {
+                       if ( a === b ) {
+                               return true; // catch the most you can
+                       } else if ( a === null || b === null || typeof a === "undefined" ||
+                                       typeof b === "undefined" ||
+                                       QUnit.objectType( a ) !== QUnit.objectType( b ) ) {
 
-               // Require type-specific equality
-               return ( a === b || typeEquiv( a, b ) ) &&
+                               // don't lose time with error prone cases
+                               return false;
+                       } else {
+                               return bindCallbacks( a, callbacks, [ b, a ] );
+                       }
 
-                       // ...across all consecutive argument pairs
-                       ( arguments.length === 2 || innerEquiv.apply( this, [].slice.call( arguments, 1 ) ) );
-       }
+                       // apply transition with (1..n) arguments
+               }( args[ 0 ], args[ 1 ] ) ) &&
+                       innerEquiv.apply( this, args.splice( 1, args.length - 1 ) ) );
+       };
 
        return innerEquiv;
-}() );
+}());
 
 // Based on jsDump by Ariel Flesler
 // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
-QUnit.dump = ( function() {
+QUnit.dump = (function() {
        function quote( str ) {
-               return "\"" + str.toString().replace( /\\/g, "\\\\" ).replace( /"/g, "\\\"" ) + "\"";
+               return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";
        }
        function literal( o ) {
                return o + "";
@@ -1950,7 +1694,7 @@ QUnit.dump = ( function() {
        var reName = /^function (\w+)/,
                dump = {
 
-                       // The objType is used mostly internally, you can fix a (custom) type in advance
+                       // objType is used mostly internally, you can fix a (custom) type in advance
                        parse: function( obj, objType, stack ) {
                                stack = stack || [];
                                var res, parser, parserType,
@@ -1994,7 +1738,7 @@ QUnit.dump = ( function() {
                                        type = "node";
                                } else if (
 
-                                       // Native arrays
+                                       // native arrays
                                        toString.call( obj ) === "[object Array]" ||
 
                                        // NodeList objects
@@ -2010,12 +1754,10 @@ QUnit.dump = ( function() {
                                }
                                return type;
                        },
-
                        separator: function() {
                                return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&#160;" : " ";
                        },
-
-                       // Extra can be a number, shortcut for increasing-calling-decreasing
+                       // extra can be a number, shortcut for increasing-calling-decreasing
                        indent: function( extra ) {
                                if ( !this.multiline ) {
                                        return "";
@@ -2035,13 +1777,13 @@ QUnit.dump = ( function() {
                        setParser: function( name, parser ) {
                                this.parsers[ name ] = parser;
                        },
-
                        // The next 3 are exposed so you can use them
                        quote: quote,
                        literal: literal,
                        join: join,
+                       //
                        depth: 1,
-                       maxDepth: QUnit.config.maxDepth,
+                       maxDepth: 5,
 
                        // This is the list of parsers, to modify them, use dump.setParser
                        parsers: {
@@ -2056,13 +1798,13 @@ QUnit.dump = ( function() {
                                "function": function( fn ) {
                                        var ret = "function",
 
-                                               // Functions never have name in IE
+                                               // functions never have name in IE
                                                name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ];
 
                                        if ( name ) {
                                                ret += " " + name;
                                        }
-                                       ret += "(";
+                                       ret += "( ";
 
                                        ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" );
                                        return join( ret, dump.parse( fn, "functionCode" ), "}" );
@@ -2088,7 +1830,7 @@ QUnit.dump = ( function() {
                                        nonEnumerableProperties = [ "message", "name" ];
                                        for ( i in nonEnumerableProperties ) {
                                                key = nonEnumerableProperties[ i ];
-                                               if ( key in map && inArray( key, keys ) < 0 ) {
+                                               if ( key in map && !( key in keys ) ) {
                                                        keys.push( key );
                                                }
                                        }
@@ -2133,7 +1875,7 @@ QUnit.dump = ( function() {
                                        return ret + open + "/" + tag + close;
                                },
 
-                               // Function calls it internally, it's the arguments part of the function
+                               // function calls it internally, it's the arguments part of the function
                                functionArgs: function( fn ) {
                                        var args,
                                                l = fn.length;
@@ -2150,14 +1892,11 @@ QUnit.dump = ( function() {
                                        }
                                        return " " + args.join( ", " ) + " ";
                                },
-
-                               // Object calls it internally, the key part of an item in a map
+                               // object calls it internally, the key part of an item in a map
                                key: quote,
-
-                               // Function calls it internally, it's the content of the function
+                               // function calls it internally, it's the content of the function
                                functionCode: "[code]",
-
-                               // Node calls it internally, it's a html attribute value
+                               // node calls it internally, it's an html attribute value
                                attribute: quote,
                                string: quote,
                                date: quote,
@@ -2165,45 +1904,42 @@ QUnit.dump = ( function() {
                                number: literal,
                                "boolean": literal
                        },
-
-                       // If true, entities are escaped ( <, >, \t, space and \n )
+                       // if true, entities are escaped ( <, >, \t, space and \n )
                        HTML: false,
-
-                       // Indentation unit
+                       // indentation unit
                        indentChar: "  ",
-
-                       // If true, items in a collection, are separated by a \n, else just a space.
+                       // if true, items in a collection, are separated by a \n, else just a space.
                        multiline: true
                };
 
        return dump;
-}() );
+}());
 
-// Back compat
+// back compat
 QUnit.jsDump = QUnit.dump;
 
-// Deprecated
-// Extend assert methods to QUnit for Backwards compatibility
-( function() {
-       var i,
-               assertions = Assert.prototype;
+// For browser, export only select globals
+if ( typeof window !== "undefined" ) {
 
-       function applyCurrent( current ) {
-               return function() {
-                       var assert = new Assert( QUnit.config.current );
-                       current.apply( assert, arguments );
-               };
-       }
+       // Deprecated
+       // Extend assert methods to QUnit and Global scope through Backwards compatibility
+       (function() {
+               var i,
+                       assertions = Assert.prototype;
 
-       for ( i in assertions ) {
-               QUnit[ i ] = applyCurrent( assertions[ i ] );
-       }
-}() );
+               function applyCurrent( current ) {
+                       return function() {
+                               var assert = new Assert( QUnit.config.current );
+                               current.apply( assert, arguments );
+                       };
+               }
 
-// For browser, export only select globals
-if ( defined.document ) {
+               for ( i in assertions ) {
+                       QUnit[ i ] = applyCurrent( assertions[ i ] );
+               }
+       })();
 
-       ( function() {
+       (function() {
                var i, l,
                        keys = [
                                "test",
@@ -2213,7 +1949,6 @@ if ( defined.document ) {
                                "start",
                                "stop",
                                "ok",
-                               "notOk",
                                "equal",
                                "notEqual",
                                "propEqual",
@@ -2222,14 +1957,13 @@ if ( defined.document ) {
                                "notDeepEqual",
                                "strictEqual",
                                "notStrictEqual",
-                               "throws",
-                               "raises"
+                               "throws"
                        ];
 
                for ( i = 0, l = keys.length; i < l; i++ ) {
                        window[ keys[ i ] ] = QUnit[ keys[ i ] ];
                }
-       }() );
+       })();
 
        window.QUnit = QUnit;
 }
@@ -2247,122 +1981,168 @@ if ( typeof exports !== "undefined" && exports ) {
        exports.QUnit = QUnit;
 }
 
-if ( typeof define === "function" && define.amd ) {
-       define( function() {
-               return QUnit;
-       } );
-       QUnit.config.autostart = false;
-}
-
 // Get a reference to the global object, like window in browsers
-}( ( function() {
+}( (function() {
        return this;
-}() ) ) );
-
-( function() {
+})() ));
 
-// Only interact with URLs via window.location
-var location = typeof window !== "undefined" && window.location;
-if ( !location ) {
-       return;
-}
-
-var urlParams = getUrlParams();
-
-QUnit.urlParams = urlParams;
+/*istanbul ignore next */
+// jscs:disable maximumLineLength
+/*
+ * Javascript Diff Algorithm
+ *  By John Resig (http://ejohn.org/)
+ *  Modified by Chu Alan "sprite"
+ *
+ * Released under the MIT license.
+ *
+ * More Info:
+ *  http://ejohn.org/projects/javascript-diff-algorithm/
+ *
+ * Usage: QUnit.diff(expected, actual)
+ *
+ * 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"
+ */
+QUnit.diff = (function() {
+       var hasOwn = Object.prototype.hasOwnProperty;
 
-// Match module/test by inclusion in an array
-QUnit.config.moduleId = [].concat( urlParams.moduleId || [] );
-QUnit.config.testId = [].concat( urlParams.testId || [] );
+       /*jshint eqeqeq:false, eqnull:true */
+       function diff( o, n ) {
+               var i,
+                       ns = {},
+                       os = {};
+
+               for ( i = 0; i < n.length; i++ ) {
+                       if ( !hasOwn.call( ns, n[ i ] ) ) {
+                               ns[ n[ i ] ] = {
+                                       rows: [],
+                                       o: null
+                               };
+                       }
+                       ns[ n[ i ] ].rows.push( i );
+               }
+
+               for ( i = 0; i < o.length; i++ ) {
+                       if ( !hasOwn.call( os, o[ i ] ) ) {
+                               os[ o[ i ] ] = {
+                                       rows: [],
+                                       n: null
+                               };
+                       }
+                       os[ o[ i ] ].rows.push( i );
+               }
+
+               for ( i in ns ) {
+                       if ( hasOwn.call( ns, i ) ) {
+                               if ( ns[ i ].rows.length === 1 && hasOwn.call( os, i ) && os[ i ].rows.length === 1 ) {
+                                       n[ ns[ i ].rows[ 0 ] ] = {
+                                               text: n[ ns[ i ].rows[ 0 ] ],
+                                               row: os[ i ].rows[ 0 ]
+                                       };
+                                       o[ os[ i ].rows[ 0 ] ] = {
+                                               text: o[ os[ i ].rows[ 0 ] ],
+                                               row: ns[ i ].rows[ 0 ]
+                                       };
+                               }
+                       }
+               }
 
-// Exact case-insensitive match of the module name
-QUnit.config.module = urlParams.module;
+               for ( i = 0; i < n.length - 1; i++ ) {
+                       if ( n[ i ].text != null && n[ i + 1 ].text == null && n[ i ].row + 1 < o.length && o[ n[ i ].row + 1 ].text == null &&
+                               n[ i + 1 ] == o[ n[ i ].row + 1 ] ) {
 
-// Regular expression or case-insenstive substring match against "moduleName: testName"
-QUnit.config.filter = urlParams.filter;
+                               n[ i + 1 ] = {
+                                       text: n[ i + 1 ],
+                                       row: n[ i ].row + 1
+                               };
+                               o[ n[ i ].row + 1 ] = {
+                                       text: o[ n[ i ].row + 1 ],
+                                       row: i + 1
+                               };
+                       }
+               }
 
-// Test order randomization
-if ( urlParams.seed === true ) {
+               for ( i = n.length - 1; i > 0; i-- ) {
+                       if ( n[ i ].text != null && n[ i - 1 ].text == null && n[ i ].row > 0 && o[ n[ i ].row - 1 ].text == null &&
+                               n[ i - 1 ] == o[ n[ i ].row - 1 ] ) {
 
-       // Generate a random seed if the option is specified without a value
-       QUnit.config.seed = Math.random().toString( 36 ).slice( 2 );
-} else if ( urlParams.seed ) {
-       QUnit.config.seed = urlParams.seed;
-}
+                               n[ i - 1 ] = {
+                                       text: n[ i - 1 ],
+                                       row: n[ i ].row - 1
+                               };
+                               o[ n[ i ].row - 1 ] = {
+                                       text: o[ n[ i ].row - 1 ],
+                                       row: i - 1
+                               };
+                       }
+               }
 
-// Add URL-parameter-mapped config values with UI form rendering data
-QUnit.config.urlConfig.push(
-       {
-               id: "hidepassed",
-               label: "Hide passed tests",
-               tooltip: "Only show tests and assertions that fail. Stored as query-strings."
-       },
-       {
-               id: "noglobals",
-               label: "Check for Globals",
-               tooltip: "Enabling this will test if any test introduces new properties on the " +
-                       "global object (`window` in Browsers). Stored as query-strings."
-       },
-       {
-               id: "notrycatch",
-               label: "No try-catch",
-               tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " +
-                       "exceptions in IE reasonable. Stored as query-strings."
+               return {
+                       o: o,
+                       n: n
+               };
        }
-);
 
-QUnit.begin( function() {
-       var i, option,
-               urlConfig = QUnit.config.urlConfig;
+       return function( o, n ) {
+               o = o.replace( /\s+$/, "" );
+               n = n.replace( /\s+$/, "" );
 
-       for ( i = 0; i < urlConfig.length; i++ ) {
+               var i, pre,
+                       str = "",
+                       out = diff( o === "" ? [] : o.split( /\s+/ ), n === "" ? [] : n.split( /\s+/ ) ),
+                       oSpace = o.match( /\s+/g ),
+                       nSpace = n.match( /\s+/g );
 
-               // Options can be either strings or objects with nonempty "id" properties
-               option = QUnit.config.urlConfig[ i ];
-               if ( typeof option !== "string" ) {
-                       option = option.id;
+               if ( oSpace == null ) {
+                       oSpace = [ " " ];
+               } else {
+                       oSpace.push( " " );
                }
 
-               if ( QUnit.config[ option ] === undefined ) {
-                       QUnit.config[ option ] = urlParams[ option ];
+               if ( nSpace == null ) {
+                       nSpace = [ " " ];
+               } else {
+                       nSpace.push( " " );
                }
-       }
-} );
-
-function getUrlParams() {
-       var i, param, name, value;
-       var urlParams = {};
-       var params = location.search.slice( 1 ).split( "&" );
-       var length = params.length;
-
-       for ( i = 0; i < length; i++ ) {
-               if ( params[ i ] ) {
-                       param = params[ i ].split( "=" );
-                       name = decodeURIComponent( param[ 0 ] );
-
-                       // Allow just a key to turn on a flag, e.g., test.html?noglobals
-                       value = param.length === 1 ||
-                               decodeURIComponent( param.slice( 1 ).join( "=" ) ) ;
-                       if ( urlParams[ name ] ) {
-                               urlParams[ name ] = [].concat( urlParams[ name ], value );
-                       } else {
-                               urlParams[ name ] = value;
+
+               if ( out.n.length === 0 ) {
+                       for ( i = 0; i < out.o.length; i++ ) {
+                               str += "<del>" + out.o[ i ] + oSpace[ i ] + "</del>";
+                       }
+               } else {
+                       if ( out.n[ 0 ].text == null ) {
+                               for ( n = 0; n < out.o.length && out.o[ n ].text == null; n++ ) {
+                                       str += "<del>" + out.o[ n ] + oSpace[ n ] + "</del>";
+                               }
+                       }
+
+                       for ( i = 0; i < out.n.length; i++ ) {
+                               if ( out.n[ i ].text == null ) {
+                                       str += "<ins>" + out.n[ i ] + nSpace[ i ] + "</ins>";
+                               } else {
+
+                                       // `pre` initialized at top of scope
+                                       pre = "";
+
+                                       for ( n = out.n[ i ].row + 1; n < out.o.length && out.o[ n ].text == null; n++ ) {
+                                               pre += "<del>" + out.o[ n ] + oSpace[ n ] + "</del>";
+                                       }
+                                       str += " " + out.n[ i ].text + nSpace[ i ] + pre;
+                               }
                        }
                }
-       }
 
-       return urlParams;
-}
+               return str;
+       };
+}());
+// jscs:enable
 
-// Don't load the HTML Reporter on non-browser environments
-if ( typeof window === "undefined" || !window.document ) {
-       return;
-}
+(function() {
 
 // Deprecated QUnit.init - Ref #530
 // Re-initialize the configuration options
 QUnit.init = function() {
-       var config = QUnit.config;
+       var tests, banner, result, qunit,
+               config = QUnit.config;
 
        config.stats = { all: 0, bad: 0 };
        config.moduleStats = { all: 0, bad: 0 };
@@ -2374,17 +2154,57 @@ QUnit.init = function() {
        config.filter = "";
        config.queue = [];
 
-       appendInterface();
+       // Return on non-browser environments
+       // This is necessary to not break on node tests
+       if ( typeof window === "undefined" ) {
+               return;
+       }
+
+       qunit = id( "qunit" );
+       if ( qunit ) {
+               qunit.innerHTML =
+                       "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
+                       "<h2 id='qunit-banner'></h2>" +
+                       "<div id='qunit-testrunner-toolbar'></div>" +
+                       "<h2 id='qunit-userAgent'></h2>" +
+                       "<ol id='qunit-tests'></ol>";
+       }
+
+       tests = id( "qunit-tests" );
+       banner = id( "qunit-banner" );
+       result = id( "qunit-testresult" );
+
+       if ( tests ) {
+               tests.innerHTML = "";
+       }
+
+       if ( banner ) {
+               banner.className = "";
+       }
+
+       if ( result ) {
+               result.parentNode.removeChild( result );
+       }
+
+       if ( tests ) {
+               result = document.createElement( "p" );
+               result.id = "qunit-testresult";
+               result.className = "result";
+               tests.parentNode.insertBefore( result, tests );
+               result.innerHTML = "Running...<br />&#160;";
+       }
 };
 
+// Don't load the HTML Reporter on non-Browser environments
+if ( typeof window === "undefined" ) {
+       return;
+}
+
 var config = QUnit.config,
-       document = window.document,
-       collapseNext = false,
        hasOwn = Object.prototype.hasOwnProperty,
-       unfilteredUrl = setUrl( { filter: undefined, module: undefined,
-               moduleId: undefined, testId: undefined } ),
        defined = {
-               sessionStorage: ( function() {
+               document: window.document !== undefined,
+               sessionStorage: (function() {
                        var x = "qunit-test-string";
                        try {
                                sessionStorage.setItem( x, x );
@@ -2393,7 +2213,7 @@ var config = QUnit.config,
                        } catch ( e ) {
                                return false;
                        }
-               }() )
+               }())
        },
        modulesList = [];
 
@@ -2420,7 +2240,7 @@ function escapeText( s ) {
                case "&":
                        return "&amp;";
                }
-       } );
+       });
 }
 
 /**
@@ -2435,15 +2255,8 @@ function addEvent( elem, type, fn ) {
                elem.addEventListener( type, fn, false );
        } else if ( elem.attachEvent ) {
 
-               // Support: IE <9
-               elem.attachEvent( "on" + type, function() {
-                       var event = window.event;
-                       if ( !event.target ) {
-                               event.target = event.srcElement || document;
-                       }
-
-                       fn.call( elem, event );
-               } );
+               // support: IE <9
+               elem.attachEvent( "on" + type, fn );
        }
 }
 
@@ -2469,11 +2282,11 @@ function addClass( elem, name ) {
        }
 }
 
-function toggleClass( elem, name, force ) {
-       if ( force || typeof force === "undefined" && !hasClass( elem, name ) ) {
-               addClass( elem, name );
-       } else {
+function toggleClass( elem, name ) {
+       if ( hasClass( elem, name ) ) {
                removeClass( elem, name );
+       } else {
+               addClass( elem, name );
        }
 }
 
@@ -2485,24 +2298,22 @@ function removeClass( elem, name ) {
                set = set.replace( " " + name + " ", " " );
        }
 
-       // Trim for prettiness
+       // trim for prettiness
        elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" );
 }
 
 function id( name ) {
-       return document.getElementById && document.getElementById( name );
+       return defined.document && document.getElementById && document.getElementById( name );
 }
 
 function getUrlConfigHtml() {
        var i, j, val,
                escaped, escapedTooltip,
                selection = false,
-               urlConfig = config.urlConfig,
+               len = config.urlConfig.length,
                urlConfigHtml = "";
 
-       for ( i = 0; i < urlConfig.length; i++ ) {
-
-               // Options can be either strings or objects with nonempty "id" properties
+       for ( i = 0; i < len; i++ ) {
                val = config.urlConfig[ i ];
                if ( typeof val === "string" ) {
                        val = {
@@ -2514,6 +2325,10 @@ function getUrlConfigHtml() {
                escaped = escapeText( val.id );
                escapedTooltip = escapeText( val.tooltip );
 
+               if ( config[ val.id ] === undefined ) {
+                       config[ val.id ] = QUnit.urlParams[ val.id ];
+               }
+
                if ( !val.value || typeof val.value === "string" ) {
                        urlConfigHtml += "<input id='qunit-urlconfig-" + escaped +
                                "' name='" + escaped + "' type='checkbox'" +
@@ -2560,7 +2375,7 @@ function getUrlConfigHtml() {
 // Handle "click" events on toolbar checkboxes and "change" for select menus.
 // Updates the URL with the new state of `config.urlConfig` values.
 function toolbarChanged() {
-       var updatedUrl, value, tests,
+       var updatedUrl, value,
                field = this,
                params = {};
 
@@ -2574,14 +2389,15 @@ function toolbarChanged() {
        params[ field.name ] = value;
        updatedUrl = setUrl( params );
 
-       // Check if we can apply the change without a page refresh
        if ( "hidepassed" === field.name && "replaceState" in window.history ) {
-               QUnit.urlParams[ field.name ] = value;
                config[ field.name ] = value || false;
-               tests = id( "qunit-tests" );
-               if ( tests ) {
-                       toggleClass( tests, "hidepass", value || false );
+               if ( value ) {
+                       addClass( id( "qunit-tests" ), "hidepass" );
+               } else {
+                       removeClass( id( "qunit-tests" ), "hidepass" );
                }
+
+               // It is not necessary to refresh the whole page
                window.history.replaceState( null, "", updatedUrl );
        } else {
                window.location = updatedUrl;
@@ -2589,26 +2405,21 @@ function toolbarChanged() {
 }
 
 function setUrl( params ) {
-       var key, arrValue, i,
-               querystring = "?",
-               location = window.location;
+       var key,
+               querystring = "?";
 
        params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params );
 
        for ( key in params ) {
-
-               // Skip inherited or undefined properties
-               if ( hasOwn.call( params, key ) && params[ key ] !== undefined ) {
-
-                       // Output a parameter for each value of this key (but usually just one)
-                       arrValue = [].concat( params[ key ] );
-                       for ( i = 0; i < arrValue.length; i++ ) {
-                               querystring += encodeURIComponent( key );
-                               if ( arrValue[ i ] !== true ) {
-                                       querystring += "=" + encodeURIComponent( arrValue[ i ] );
-                               }
-                               querystring += "&";
+               if ( hasOwn.call( params, key ) ) {
+                       if ( params[ key ] === undefined ) {
+                               continue;
                        }
+                       querystring += encodeURIComponent( key );
+                       if ( params[ key ] !== true ) {
+                               querystring += "=" + encodeURIComponent( params[ key ] );
+                       }
+                       querystring += "&";
                }
        }
        return location.protocol + "//" + location.host +
@@ -2616,22 +2427,17 @@ function setUrl( params ) {
 }
 
 function applyUrlParams() {
-       var selectedModule,
-               modulesList = id( "qunit-modulefilter" ),
+       var selectBox = id( "qunit-modulefilter" ),
+               selection = decodeURIComponent( selectBox.options[ selectBox.selectedIndex ].value ),
                filter = id( "qunit-filter-input" ).value;
 
-       selectedModule = modulesList ?
-               decodeURIComponent( modulesList.options[ modulesList.selectedIndex ].value ) :
-               undefined;
-
-       window.location = setUrl( {
-               module: ( selectedModule === "" ) ? undefined : selectedModule,
+       window.location = setUrl({
+               module: ( selection === "" ) ? undefined : selection,
                filter: ( filter === "" ) ? undefined : filter,
 
-               // Remove moduleId and testId filters
-               moduleId: undefined,
+               // Remove testId filter
                testId: undefined
-       } );
+       });
 }
 
 function toolbarUrlConfigContainer() {
@@ -2678,7 +2484,7 @@ function toolbarLooseFilter() {
                }
 
                return false;
-       } );
+       });
 
        return filter;
 }
@@ -2691,6 +2497,10 @@ function toolbarModuleFilterHtml() {
                return false;
        }
 
+       modulesList.sort(function( a, b ) {
+               return a.localeCompare( b );
+       });
+
        moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" +
                "<select id='qunit-modulefilter' name='modulefilter'><option value='' " +
                ( QUnit.urlParams.module === undefined ? "selected='selected'" : "" ) +
@@ -2730,7 +2540,6 @@ function appendToolbar() {
        if ( toolbar ) {
                toolbar.appendChild( toolbarUrlConfigContainer() );
                toolbar.appendChild( toolbarLooseFilter() );
-               toolbarModuleFilter();
        }
 }
 
@@ -2738,8 +2547,9 @@ function appendHeader() {
        var header = id( "qunit-header" );
 
        if ( header ) {
-               header.innerHTML = "<a href='" + escapeText( unfilteredUrl ) + "'>" + header.innerHTML +
-                       "</a> ";
+               header.innerHTML = "<a href='" +
+                       setUrl({ filter: undefined, module: undefined, testId: undefined }) +
+                       "'>" + header.innerHTML + "</a> ";
        }
 }
 
@@ -2776,57 +2586,24 @@ function storeFixture() {
        }
 }
 
-function appendFilteredTest() {
-       var testId = QUnit.config.testId;
-       if ( !testId || testId.length <= 0 ) {
-               return "";
-       }
-       return "<div id='qunit-filteredTest'>Rerunning selected tests: " +
-               escapeText( testId.join( ", " ) ) +
-               " <a id='qunit-clearFilter' href='" +
-               escapeText( unfilteredUrl ) +
-               "'>Run all tests</a></div>";
-}
-
 function appendUserAgent() {
        var userAgent = id( "qunit-userAgent" );
-
        if ( userAgent ) {
                userAgent.innerHTML = "";
-               userAgent.appendChild(
-                       document.createTextNode(
-                               "QUnit " + QUnit.version + "; " + navigator.userAgent
-                       )
-               );
+               userAgent.appendChild( document.createTextNode( navigator.userAgent ) );
        }
 }
 
-function appendInterface() {
-       var qunit = id( "qunit" );
-
-       if ( qunit ) {
-               qunit.innerHTML =
-                       "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
-                       "<h2 id='qunit-banner'></h2>" +
-                       "<div id='qunit-testrunner-toolbar'></div>" +
-                       appendFilteredTest() +
-                       "<h2 id='qunit-userAgent'></h2>" +
-                       "<ol id='qunit-tests'></ol>";
-       }
-
-       appendHeader();
-       appendBanner();
-       appendTestResults();
-       appendUserAgent();
-       appendToolbar();
-}
-
 function appendTestsList( modules ) {
        var i, l, x, z, test, moduleObj;
 
        for ( i = 0, l = modules.length; i < l; i++ ) {
                moduleObj = modules[ i ];
 
+               if ( moduleObj.name ) {
+                       modulesList.push( moduleObj.name );
+               }
+
                for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) {
                        test = moduleObj.tests[ x ];
 
@@ -2848,7 +2625,7 @@ function appendTest( name, testId, moduleName ) {
 
        rerunTrigger = document.createElement( "a" );
        rerunTrigger.innerHTML = "Rerun";
-       rerunTrigger.href = setUrl( { testId: testId } );
+       rerunTrigger.href = setUrl({ testId: testId });
 
        testBlock = document.createElement( "li" );
        testBlock.appendChild( title );
@@ -2864,33 +2641,35 @@ function appendTest( name, testId, moduleName ) {
 }
 
 // HTML Reporter initialization and load
-QUnit.begin( function( details ) {
-       var i, moduleObj, tests;
-
-       // Sort modules by name for the picker
-       for ( i = 0; i < details.modules.length; i++ ) {
-               moduleObj = details.modules[ i ];
-               if ( moduleObj.name ) {
-                       modulesList.push( moduleObj.name );
-               }
-       }
-       modulesList.sort( function( a, b ) {
-               return a.localeCompare( b );
-       } );
+QUnit.begin(function( details ) {
+       var qunit = id( "qunit" );
 
-       // Capture fixture HTML from the page
+       // Fixture is the only one necessary to run without the #qunit element
        storeFixture();
 
-       // Initialize QUnit elements
-       appendInterface();
+       if ( qunit ) {
+               qunit.innerHTML =
+                       "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
+                       "<h2 id='qunit-banner'></h2>" +
+                       "<div id='qunit-testrunner-toolbar'></div>" +
+                       "<h2 id='qunit-userAgent'></h2>" +
+                       "<ol id='qunit-tests'></ol>";
+       }
+
+       appendHeader();
+       appendBanner();
+       appendTestResults();
+       appendUserAgent();
+       appendToolbar();
        appendTestsList( details.modules );
-       tests = id( "qunit-tests" );
-       if ( tests && config.hidepassed ) {
-               addClass( tests, "hidepass" );
+       toolbarModuleFilter();
+
+       if ( qunit && config.hidepassed ) {
+               addClass( qunit.lastChild, "hidepass" );
        }
-} );
+});
 
-QUnit.done( function( details ) {
+QUnit.done(function( details ) {
        var i, key,
                banner = id( "qunit-banner" ),
                tests = id( "qunit-tests" ),
@@ -2915,9 +2694,9 @@ QUnit.done( function( details ) {
                id( "qunit-testresult" ).innerHTML = html;
        }
 
-       if ( config.altertitle && document.title ) {
+       if ( config.altertitle && defined.document && document.title ) {
 
-               // Show âœ– for good, âœ” for bad suite result in title
+               // show âœ– for good, âœ” for bad suite result in title
                // use escape sequences in case file gets loaded with non-utf-8-charset
                document.title = [
                        ( details.failed ? "\u2716" : "\u2714" ),
@@ -2925,7 +2704,7 @@ QUnit.done( function( details ) {
                ].join( " " );
        }
 
-       // Clear own sessionStorage items if all tests passed
+       // clear own sessionStorage items if all tests passed
        if ( config.reorder && defined.sessionStorage && details.failed === 0 ) {
                for ( i = 0; i < sessionStorage.length; i++ ) {
                        key = sessionStorage.key( i++ );
@@ -2935,11 +2714,11 @@ QUnit.done( function( details ) {
                }
        }
 
-       // Scroll back to top to show results
+       // scroll back to top to show results
        if ( config.scrolltop && window.scrollTo ) {
                window.scrollTo( 0, 0 );
        }
-} );
+});
 
 function getNameHtml( name, module ) {
        var nameHtml = "";
@@ -2953,8 +2732,8 @@ function getNameHtml( name, module ) {
        return nameHtml;
 }
 
-QUnit.testStart( function( details ) {
-       var running, testBlock, bad;
+QUnit.testStart(function( details ) {
+       var running, testBlock;
 
        testBlock = id( "qunit-test-output-" + details.testId );
        if ( testBlock ) {
@@ -2967,27 +2746,14 @@ QUnit.testStart( function( details ) {
 
        running = id( "qunit-testresult" );
        if ( running ) {
-               bad = QUnit.config.reorder && defined.sessionStorage &&
-                       +sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name );
-
-               running.innerHTML = ( bad ?
-                       "Rerunning previously failed test: <br />" :
-                       "Running: <br />" ) +
-                       getNameHtml( details.name, details.module );
+               running.innerHTML = "Running: <br />" + getNameHtml( details.name, details.module );
        }
 
-} );
-
-function stripHtml( string ) {
+});
 
-       // Strip tags, html entity and whitespaces
-       return string.replace( /<\/?[^>]+(>|$)/g, "" ).replace( /\&quot;/g, "" ).replace( /\s+/g, "" );
-}
-
-QUnit.log( function( details ) {
+QUnit.log(function( details ) {
        var assertList, assertLi,
-               message, expected, actual, diff,
-               showDiff = false,
+               message, expected, actual,
                testItem = id( "qunit-test-output-" + details.testId );
 
        if ( !testItem ) {
@@ -2998,52 +2764,21 @@ QUnit.log( function( details ) {
        message = "<span class='test-message'>" + message + "</span>";
        message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
 
-       // The pushFailure doesn't provide details.expected
+       // pushFailure doesn't provide details.expected
        // when it calls, it's implicit to also not show expected and diff stuff
        // Also, we need to check details.expected existence, as it can exist and be undefined
        if ( !details.result && hasOwn.call( details, "expected" ) ) {
-               if ( details.negative ) {
-                       expected = "NOT " + QUnit.dump.parse( details.expected );
-               } else {
-                       expected = QUnit.dump.parse( details.expected );
-               }
-
-               actual = QUnit.dump.parse( details.actual );
+               expected = escapeText( QUnit.dump.parse( details.expected ) );
+               actual = escapeText( QUnit.dump.parse( details.actual ) );
                message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" +
-                       escapeText( expected ) +
+                       expected +
                        "</pre></td></tr>";
 
                if ( actual !== expected ) {
-
                        message += "<tr class='test-actual'><th>Result: </th><td><pre>" +
-                               escapeText( actual ) + "</pre></td></tr>";
-
-                       // Don't show diff if actual or expected are booleans
-                       if ( !( /^(true|false)$/.test( actual ) ) &&
-                                       !( /^(true|false)$/.test( expected ) ) ) {
-                               diff = QUnit.diff( expected, actual );
-                               showDiff = stripHtml( diff ).length !==
-                                       stripHtml( expected ).length +
-                                       stripHtml( actual ).length;
-                       }
-
-                       // Don't show diff if expected and actual are totally different
-                       if ( showDiff ) {
-                               message += "<tr class='test-diff'><th>Diff: </th><td><pre>" +
-                                       diff + "</pre></td></tr>";
-                       }
-               } else if ( expected.indexOf( "[object Array]" ) !== -1 ||
-                               expected.indexOf( "[object Object]" ) !== -1 ) {
-                       message += "<tr class='test-message'><th>Message: </th><td>" +
-                               "Diff suppressed as the depth of object is more than current max depth (" +
-                               QUnit.config.maxDepth + ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " +
-                               " run with a higher max depth or <a href='" +
-                               escapeText( setUrl( { maxDepth: -1 } ) ) + "'>" +
-                               "Rerun</a> without max depth.</p></td></tr>";
-               } else {
-                       message += "<tr class='test-message'><th>Message: </th><td>" +
-                               "Diff suppressed as the expected and actual results have an equivalent" +
-                               " serialization</td></tr>";
+                               actual + "</pre></td></tr>" +
+                               "<tr class='test-diff'><th>Diff: </th><td><pre>" +
+                               QUnit.diff( expected, actual ) + "</pre></td></tr>";
                }
 
                if ( details.source ) {
@@ -3053,7 +2788,7 @@ QUnit.log( function( details ) {
 
                message += "</table>";
 
-       // This occurs when pushFailure is set and we have an extracted stack trace
+       // this occours when pushFailure is set and we have an extracted stack trace
        } else if ( !details.result && details.source ) {
                message += "<table>" +
                        "<tr class='test-source'><th>Source: </th><td><pre>" +
@@ -3067,11 +2802,11 @@ QUnit.log( function( details ) {
        assertLi.className = details.result ? "pass" : "fail";
        assertLi.innerHTML = message;
        assertList.appendChild( assertLi );
-} );
+});
 
-QUnit.testDone( function( details ) {
+QUnit.testDone(function( details ) {
        var testTitle, time, testItem, assertList,
-               good, bad, testCounts, skipped, sourceName,
+               good, bad, testCounts, skipped,
                tests = id( "qunit-tests" );
 
        if ( !tests ) {
@@ -3085,7 +2820,7 @@ QUnit.testDone( function( details ) {
        good = details.passed;
        bad = details.failed;
 
-       // Store result when possible
+       // store result when possible
        if ( config.reorder && defined.sessionStorage ) {
                if ( bad ) {
                        sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad );
@@ -3095,20 +2830,10 @@ QUnit.testDone( function( details ) {
        }
 
        if ( bad === 0 ) {
-
-               // Collapse the passing tests
-               addClass( assertList, "qunit-collapsed" );
-       } else if ( bad && config.collapse && !collapseNext ) {
-
-               // Skip collapsing the first failing test
-               collapseNext = true;
-       } else {
-
-               // Collapse remaining tests
                addClass( assertList, "qunit-collapsed" );
        }
 
-       // The testItem.firstChild is the test name
+       // testItem.firstChild is the test name
        testTitle = testItem.firstChild;
 
        testCounts = bad ?
@@ -3127,7 +2852,7 @@ QUnit.testDone( function( details ) {
        } else {
                addEvent( testTitle, "click", function() {
                        toggleClass( assertList, "qunit-collapsed" );
-               } );
+               });
 
                testItem.className = bad ? "fail" : "pass";
 
@@ -3136,1199 +2861,15 @@ QUnit.testDone( function( details ) {
                time.innerHTML = details.runtime + " ms";
                testItem.insertBefore( time, assertList );
        }
+});
 
-       // Show the source of the test when showing assertions
-       if ( details.source ) {
-               sourceName = document.createElement( "p" );
-               sourceName.innerHTML = "<strong>Source: </strong>" + details.source;
-               addClass( sourceName, "qunit-source" );
-               if ( bad === 0 ) {
-                       addClass( sourceName, "qunit-collapsed" );
-               }
-               addEvent( testTitle, "click", function() {
-                       toggleClass( sourceName, "qunit-collapsed" );
-               } );
-               testItem.appendChild( sourceName );
-       }
-} );
-
-// Avoid readyState issue with phantomjs
-// Ref: #818
-var notPhantom = ( function( p ) {
-       return !( p && p.version && p.version.major > 0 );
-} )( window.phantom );
+if ( !defined.document || document.readyState === "complete" ) {
+       config.pageLoaded = true;
+       config.autorun = true;
+}
 
-if ( notPhantom && document.readyState === "complete" ) {
-       QUnit.load();
-} else {
+if ( defined.document ) {
        addEvent( window, "load", QUnit.load );
 }
 
-/*
- * This file is a modified version of google-diff-match-patch's JavaScript implementation
- * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
- * modifications are licensed as more fully set forth in LICENSE.txt.
- *
- * The original source of google-diff-match-patch is attributable and licensed as follows:
- *
- * Copyright 2006 Google Inc.
- * https://code.google.com/p/google-diff-match-patch/
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * More Info:
- *  https://code.google.com/p/google-diff-match-patch/
- *
- * Usage: QUnit.diff(expected, actual)
- *
- */
-QUnit.diff = ( function() {
-       function DiffMatchPatch() {
-       }
-
-       //  DIFF FUNCTIONS
-
-       /**
-        * The data structure representing a diff is an array of tuples:
-        * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
-        * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
-        */
-       var DIFF_DELETE = -1,
-               DIFF_INSERT = 1,
-               DIFF_EQUAL = 0;
-
-       /**
-        * Find the differences between two texts.  Simplifies the problem by stripping
-        * any common prefix or suffix off the texts before diffing.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {boolean=} optChecklines Optional speedup flag. If present and false,
-        *     then don't run a line-level diff first to identify the changed areas.
-        *     Defaults to true, which does a faster, slightly less optimal diff.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        */
-       DiffMatchPatch.prototype.DiffMain = function( text1, text2, optChecklines ) {
-               var deadline, checklines, commonlength,
-                       commonprefix, commonsuffix, diffs;
-
-               // The diff must be complete in up to 1 second.
-               deadline = ( new Date() ).getTime() + 1000;
-
-               // Check for null inputs.
-               if ( text1 === null || text2 === null ) {
-                       throw new Error( "Null input. (DiffMain)" );
-               }
-
-               // Check for equality (speedup).
-               if ( text1 === text2 ) {
-                       if ( text1 ) {
-                               return [
-                                       [ DIFF_EQUAL, text1 ]
-                               ];
-                       }
-                       return [];
-               }
-
-               if ( typeof optChecklines === "undefined" ) {
-                       optChecklines = true;
-               }
-
-               checklines = optChecklines;
-
-               // Trim off common prefix (speedup).
-               commonlength = this.diffCommonPrefix( text1, text2 );
-               commonprefix = text1.substring( 0, commonlength );
-               text1 = text1.substring( commonlength );
-               text2 = text2.substring( commonlength );
-
-               // Trim off common suffix (speedup).
-               commonlength = this.diffCommonSuffix( text1, text2 );
-               commonsuffix = text1.substring( text1.length - commonlength );
-               text1 = text1.substring( 0, text1.length - commonlength );
-               text2 = text2.substring( 0, text2.length - commonlength );
-
-               // Compute the diff on the middle block.
-               diffs = this.diffCompute( text1, text2, checklines, deadline );
-
-               // Restore the prefix and suffix.
-               if ( commonprefix ) {
-                       diffs.unshift( [ DIFF_EQUAL, commonprefix ] );
-               }
-               if ( commonsuffix ) {
-                       diffs.push( [ DIFF_EQUAL, commonsuffix ] );
-               }
-               this.diffCleanupMerge( diffs );
-               return diffs;
-       };
-
-       /**
-        * Reduce the number of edits by eliminating operationally trivial equalities.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        */
-       DiffMatchPatch.prototype.diffCleanupEfficiency = function( diffs ) {
-               var changes, equalities, equalitiesLength, lastequality,
-                       pointer, preIns, preDel, postIns, postDel;
-               changes = false;
-               equalities = []; // Stack of indices where equalities are found.
-               equalitiesLength = 0; // Keeping our own length var is faster in JS.
-               /** @type {?string} */
-               lastequality = null;
-
-               // Always equal to diffs[equalities[equalitiesLength - 1]][1]
-               pointer = 0; // Index of current position.
-
-               // Is there an insertion operation before the last equality.
-               preIns = false;
-
-               // Is there a deletion operation before the last equality.
-               preDel = false;
-
-               // Is there an insertion operation after the last equality.
-               postIns = false;
-
-               // Is there a deletion operation after the last equality.
-               postDel = false;
-               while ( pointer < diffs.length ) {
-
-                       // Equality found.
-                       if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) {
-                               if ( diffs[ pointer ][ 1 ].length < 4 && ( postIns || postDel ) ) {
-
-                                       // Candidate found.
-                                       equalities[ equalitiesLength++ ] = pointer;
-                                       preIns = postIns;
-                                       preDel = postDel;
-                                       lastequality = diffs[ pointer ][ 1 ];
-                               } else {
-
-                                       // Not a candidate, and can never become one.
-                                       equalitiesLength = 0;
-                                       lastequality = null;
-                               }
-                               postIns = postDel = false;
-
-                       // An insertion or deletion.
-                       } else {
-
-                               if ( diffs[ pointer ][ 0 ] === DIFF_DELETE ) {
-                                       postDel = true;
-                               } else {
-                                       postIns = true;
-                               }
-
-                               /*
-                                * Five types to be split:
-                                * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
-                                * <ins>A</ins>X<ins>C</ins><del>D</del>
-                                * <ins>A</ins><del>B</del>X<ins>C</ins>
-                                * <ins>A</del>X<ins>C</ins><del>D</del>
-                                * <ins>A</ins><del>B</del>X<del>C</del>
-                                */
-                               if ( lastequality && ( ( preIns && preDel && postIns && postDel ) ||
-                                               ( ( lastequality.length < 2 ) &&
-                                               ( preIns + preDel + postIns + postDel ) === 3 ) ) ) {
-
-                                       // Duplicate record.
-                                       diffs.splice(
-                                               equalities[ equalitiesLength - 1 ],
-                                               0,
-                                               [ DIFF_DELETE, lastequality ]
-                                       );
-
-                                       // Change second copy to insert.
-                                       diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT;
-                                       equalitiesLength--; // Throw away the equality we just deleted;
-                                       lastequality = null;
-                                       if ( preIns && preDel ) {
-
-                                               // No changes made which could affect previous entry, keep going.
-                                               postIns = postDel = true;
-                                               equalitiesLength = 0;
-                                       } else {
-                                               equalitiesLength--; // Throw away the previous equality.
-                                               pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1;
-                                               postIns = postDel = false;
-                                       }
-                                       changes = true;
-                               }
-                       }
-                       pointer++;
-               }
-
-               if ( changes ) {
-                       this.diffCleanupMerge( diffs );
-               }
-       };
-
-       /**
-        * Convert a diff array into a pretty HTML report.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        * @param {integer} string to be beautified.
-        * @return {string} HTML representation.
-        */
-       DiffMatchPatch.prototype.diffPrettyHtml = function( diffs ) {
-               var op, data, x,
-                       html = [];
-               for ( x = 0; x < diffs.length; x++ ) {
-                       op = diffs[ x ][ 0 ]; // Operation (insert, delete, equal)
-                       data = diffs[ x ][ 1 ]; // Text of change.
-                       switch ( op ) {
-                       case DIFF_INSERT:
-                               html[ x ] = "<ins>" + escapeText( data ) + "</ins>";
-                               break;
-                       case DIFF_DELETE:
-                               html[ x ] = "<del>" + escapeText( data ) + "</del>";
-                               break;
-                       case DIFF_EQUAL:
-                               html[ x ] = "<span>" + escapeText( data ) + "</span>";
-                               break;
-                       }
-               }
-               return html.join( "" );
-       };
-
-       /**
-        * Determine the common prefix of two strings.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {number} The number of characters common to the start of each
-        *     string.
-        */
-       DiffMatchPatch.prototype.diffCommonPrefix = function( text1, text2 ) {
-               var pointermid, pointermax, pointermin, pointerstart;
-
-               // Quick check for common null cases.
-               if ( !text1 || !text2 || text1.charAt( 0 ) !== text2.charAt( 0 ) ) {
-                       return 0;
-               }
-
-               // Binary search.
-               // Performance analysis: https://neil.fraser.name/news/2007/10/09/
-               pointermin = 0;
-               pointermax = Math.min( text1.length, text2.length );
-               pointermid = pointermax;
-               pointerstart = 0;
-               while ( pointermin < pointermid ) {
-                       if ( text1.substring( pointerstart, pointermid ) ===
-                                       text2.substring( pointerstart, pointermid ) ) {
-                               pointermin = pointermid;
-                               pointerstart = pointermin;
-                       } else {
-                               pointermax = pointermid;
-                       }
-                       pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin );
-               }
-               return pointermid;
-       };
-
-       /**
-        * Determine the common suffix of two strings.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {number} The number of characters common to the end of each string.
-        */
-       DiffMatchPatch.prototype.diffCommonSuffix = function( text1, text2 ) {
-               var pointermid, pointermax, pointermin, pointerend;
-
-               // Quick check for common null cases.
-               if ( !text1 ||
-                               !text2 ||
-                               text1.charAt( text1.length - 1 ) !== text2.charAt( text2.length - 1 ) ) {
-                       return 0;
-               }
-
-               // Binary search.
-               // Performance analysis: https://neil.fraser.name/news/2007/10/09/
-               pointermin = 0;
-               pointermax = Math.min( text1.length, text2.length );
-               pointermid = pointermax;
-               pointerend = 0;
-               while ( pointermin < pointermid ) {
-                       if ( text1.substring( text1.length - pointermid, text1.length - pointerend ) ===
-                                       text2.substring( text2.length - pointermid, text2.length - pointerend ) ) {
-                               pointermin = pointermid;
-                               pointerend = pointermin;
-                       } else {
-                               pointermax = pointermid;
-                       }
-                       pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin );
-               }
-               return pointermid;
-       };
-
-       /**
-        * Find the differences between two texts.  Assumes that the texts do not
-        * have any common prefix or suffix.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {boolean} checklines Speedup flag.  If false, then don't run a
-        *     line-level diff first to identify the changed areas.
-        *     If true, then run a faster, slightly less optimal diff.
-        * @param {number} deadline Time when the diff should be complete by.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffCompute = function( text1, text2, checklines, deadline ) {
-               var diffs, longtext, shorttext, i, hm,
-                       text1A, text2A, text1B, text2B,
-                       midCommon, diffsA, diffsB;
-
-               if ( !text1 ) {
-
-                       // Just add some text (speedup).
-                       return [
-                               [ DIFF_INSERT, text2 ]
-                       ];
-               }
-
-               if ( !text2 ) {
-
-                       // Just delete some text (speedup).
-                       return [
-                               [ DIFF_DELETE, text1 ]
-                       ];
-               }
-
-               longtext = text1.length > text2.length ? text1 : text2;
-               shorttext = text1.length > text2.length ? text2 : text1;
-               i = longtext.indexOf( shorttext );
-               if ( i !== -1 ) {
-
-                       // Shorter text is inside the longer text (speedup).
-                       diffs = [
-                               [ DIFF_INSERT, longtext.substring( 0, i ) ],
-                               [ DIFF_EQUAL, shorttext ],
-                               [ DIFF_INSERT, longtext.substring( i + shorttext.length ) ]
-                       ];
-
-                       // Swap insertions for deletions if diff is reversed.
-                       if ( text1.length > text2.length ) {
-                               diffs[ 0 ][ 0 ] = diffs[ 2 ][ 0 ] = DIFF_DELETE;
-                       }
-                       return diffs;
-               }
-
-               if ( shorttext.length === 1 ) {
-
-                       // Single character string.
-                       // After the previous speedup, the character can't be an equality.
-                       return [
-                               [ DIFF_DELETE, text1 ],
-                               [ DIFF_INSERT, text2 ]
-                       ];
-               }
-
-               // Check to see if the problem can be split in two.
-               hm = this.diffHalfMatch( text1, text2 );
-               if ( hm ) {
-
-                       // A half-match was found, sort out the return data.
-                       text1A = hm[ 0 ];
-                       text1B = hm[ 1 ];
-                       text2A = hm[ 2 ];
-                       text2B = hm[ 3 ];
-                       midCommon = hm[ 4 ];
-
-                       // Send both pairs off for separate processing.
-                       diffsA = this.DiffMain( text1A, text2A, checklines, deadline );
-                       diffsB = this.DiffMain( text1B, text2B, checklines, deadline );
-
-                       // Merge the results.
-                       return diffsA.concat( [
-                               [ DIFF_EQUAL, midCommon ]
-                       ], diffsB );
-               }
-
-               if ( checklines && text1.length > 100 && text2.length > 100 ) {
-                       return this.diffLineMode( text1, text2, deadline );
-               }
-
-               return this.diffBisect( text1, text2, deadline );
-       };
-
-       /**
-        * Do the two texts share a substring which is at least half the length of the
-        * longer text?
-        * This speedup can produce non-minimal diffs.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {Array.<string>} Five element Array, containing the prefix of
-        *     text1, the suffix of text1, the prefix of text2, the suffix of
-        *     text2 and the common middle.  Or null if there was no match.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffHalfMatch = function( text1, text2 ) {
-               var longtext, shorttext, dmp,
-                       text1A, text2B, text2A, text1B, midCommon,
-                       hm1, hm2, hm;
-
-               longtext = text1.length > text2.length ? text1 : text2;
-               shorttext = text1.length > text2.length ? text2 : text1;
-               if ( longtext.length < 4 || shorttext.length * 2 < longtext.length ) {
-                       return null; // Pointless.
-               }
-               dmp = this; // 'this' becomes 'window' in a closure.
-
-               /**
-                * Does a substring of shorttext exist within longtext such that the substring
-                * is at least half the length of longtext?
-                * Closure, but does not reference any external variables.
-                * @param {string} longtext Longer string.
-                * @param {string} shorttext Shorter string.
-                * @param {number} i Start index of quarter length substring within longtext.
-                * @return {Array.<string>} Five element Array, containing the prefix of
-                *     longtext, the suffix of longtext, the prefix of shorttext, the suffix
-                *     of shorttext and the common middle.  Or null if there was no match.
-                * @private
-                */
-               function diffHalfMatchI( longtext, shorttext, i ) {
-                       var seed, j, bestCommon, prefixLength, suffixLength,
-                               bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
-
-                       // Start with a 1/4 length substring at position i as a seed.
-                       seed = longtext.substring( i, i + Math.floor( longtext.length / 4 ) );
-                       j = -1;
-                       bestCommon = "";
-                       while ( ( j = shorttext.indexOf( seed, j + 1 ) ) !== -1 ) {
-                               prefixLength = dmp.diffCommonPrefix( longtext.substring( i ),
-                                       shorttext.substring( j ) );
-                               suffixLength = dmp.diffCommonSuffix( longtext.substring( 0, i ),
-                                       shorttext.substring( 0, j ) );
-                               if ( bestCommon.length < suffixLength + prefixLength ) {
-                                       bestCommon = shorttext.substring( j - suffixLength, j ) +
-                                               shorttext.substring( j, j + prefixLength );
-                                       bestLongtextA = longtext.substring( 0, i - suffixLength );
-                                       bestLongtextB = longtext.substring( i + prefixLength );
-                                       bestShorttextA = shorttext.substring( 0, j - suffixLength );
-                                       bestShorttextB = shorttext.substring( j + prefixLength );
-                               }
-                       }
-                       if ( bestCommon.length * 2 >= longtext.length ) {
-                               return [ bestLongtextA, bestLongtextB,
-                                       bestShorttextA, bestShorttextB, bestCommon
-                               ];
-                       } else {
-                               return null;
-                       }
-               }
-
-               // First check if the second quarter is the seed for a half-match.
-               hm1 = diffHalfMatchI( longtext, shorttext,
-                       Math.ceil( longtext.length / 4 ) );
-
-               // Check again based on the third quarter.
-               hm2 = diffHalfMatchI( longtext, shorttext,
-                       Math.ceil( longtext.length / 2 ) );
-               if ( !hm1 && !hm2 ) {
-                       return null;
-               } else if ( !hm2 ) {
-                       hm = hm1;
-               } else if ( !hm1 ) {
-                       hm = hm2;
-               } else {
-
-                       // Both matched.  Select the longest.
-                       hm = hm1[ 4 ].length > hm2[ 4 ].length ? hm1 : hm2;
-               }
-
-               // A half-match was found, sort out the return data.
-               text1A, text1B, text2A, text2B;
-               if ( text1.length > text2.length ) {
-                       text1A = hm[ 0 ];
-                       text1B = hm[ 1 ];
-                       text2A = hm[ 2 ];
-                       text2B = hm[ 3 ];
-               } else {
-                       text2A = hm[ 0 ];
-                       text2B = hm[ 1 ];
-                       text1A = hm[ 2 ];
-                       text1B = hm[ 3 ];
-               }
-               midCommon = hm[ 4 ];
-               return [ text1A, text1B, text2A, text2B, midCommon ];
-       };
-
-       /**
-        * Do a quick line-level diff on both strings, then rediff the parts for
-        * greater accuracy.
-        * This speedup can produce non-minimal diffs.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {number} deadline Time when the diff should be complete by.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffLineMode = function( text1, text2, deadline ) {
-               var a, diffs, linearray, pointer, countInsert,
-                       countDelete, textInsert, textDelete, j;
-
-               // Scan the text on a line-by-line basis first.
-               a = this.diffLinesToChars( text1, text2 );
-               text1 = a.chars1;
-               text2 = a.chars2;
-               linearray = a.lineArray;
-
-               diffs = this.DiffMain( text1, text2, false, deadline );
-
-               // Convert the diff back to original text.
-               this.diffCharsToLines( diffs, linearray );
-
-               // Eliminate freak matches (e.g. blank lines)
-               this.diffCleanupSemantic( diffs );
-
-               // Rediff any replacement blocks, this time character-by-character.
-               // Add a dummy entry at the end.
-               diffs.push( [ DIFF_EQUAL, "" ] );
-               pointer = 0;
-               countDelete = 0;
-               countInsert = 0;
-               textDelete = "";
-               textInsert = "";
-               while ( pointer < diffs.length ) {
-                       switch ( diffs[ pointer ][ 0 ] ) {
-                       case DIFF_INSERT:
-                               countInsert++;
-                               textInsert += diffs[ pointer ][ 1 ];
-                               break;
-                       case DIFF_DELETE:
-                               countDelete++;
-                               textDelete += diffs[ pointer ][ 1 ];
-                               break;
-                       case DIFF_EQUAL:
-
-                               // Upon reaching an equality, check for prior redundancies.
-                               if ( countDelete >= 1 && countInsert >= 1 ) {
-
-                                       // Delete the offending records and add the merged ones.
-                                       diffs.splice( pointer - countDelete - countInsert,
-                                               countDelete + countInsert );
-                                       pointer = pointer - countDelete - countInsert;
-                                       a = this.DiffMain( textDelete, textInsert, false, deadline );
-                                       for ( j = a.length - 1; j >= 0; j-- ) {
-                                               diffs.splice( pointer, 0, a[ j ] );
-                                       }
-                                       pointer = pointer + a.length;
-                               }
-                               countInsert = 0;
-                               countDelete = 0;
-                               textDelete = "";
-                               textInsert = "";
-                               break;
-                       }
-                       pointer++;
-               }
-               diffs.pop(); // Remove the dummy entry at the end.
-
-               return diffs;
-       };
-
-       /**
-        * Find the 'middle snake' of a diff, split the problem in two
-        * and return the recursively constructed diff.
-        * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {number} deadline Time at which to bail if not yet complete.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffBisect = function( text1, text2, deadline ) {
-               var text1Length, text2Length, maxD, vOffset, vLength,
-                       v1, v2, x, delta, front, k1start, k1end, k2start,
-                       k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
-
-               // Cache the text lengths to prevent multiple calls.
-               text1Length = text1.length;
-               text2Length = text2.length;
-               maxD = Math.ceil( ( text1Length + text2Length ) / 2 );
-               vOffset = maxD;
-               vLength = 2 * maxD;
-               v1 = new Array( vLength );
-               v2 = new Array( vLength );
-
-               // Setting all elements to -1 is faster in Chrome & Firefox than mixing
-               // integers and undefined.
-               for ( x = 0; x < vLength; x++ ) {
-                       v1[ x ] = -1;
-                       v2[ x ] = -1;
-               }
-               v1[ vOffset + 1 ] = 0;
-               v2[ vOffset + 1 ] = 0;
-               delta = text1Length - text2Length;
-
-               // If the total number of characters is odd, then the front path will collide
-               // with the reverse path.
-               front = ( delta % 2 !== 0 );
-
-               // Offsets for start and end of k loop.
-               // Prevents mapping of space beyond the grid.
-               k1start = 0;
-               k1end = 0;
-               k2start = 0;
-               k2end = 0;
-               for ( d = 0; d < maxD; d++ ) {
-
-                       // Bail out if deadline is reached.
-                       if ( ( new Date() ).getTime() > deadline ) {
-                               break;
-                       }
-
-                       // Walk the front path one step.
-                       for ( k1 = -d + k1start; k1 <= d - k1end; k1 += 2 ) {
-                               k1Offset = vOffset + k1;
-                               if ( k1 === -d || ( k1 !== d && v1[ k1Offset - 1 ] < v1[ k1Offset + 1 ] ) ) {
-                                       x1 = v1[ k1Offset + 1 ];
-                               } else {
-                                       x1 = v1[ k1Offset - 1 ] + 1;
-                               }
-                               y1 = x1 - k1;
-                               while ( x1 < text1Length && y1 < text2Length &&
-                                       text1.charAt( x1 ) === text2.charAt( y1 ) ) {
-                                       x1++;
-                                       y1++;
-                               }
-                               v1[ k1Offset ] = x1;
-                               if ( x1 > text1Length ) {
-
-                                       // Ran off the right of the graph.
-                                       k1end += 2;
-                               } else if ( y1 > text2Length ) {
-
-                                       // Ran off the bottom of the graph.
-                                       k1start += 2;
-                               } else if ( front ) {
-                                       k2Offset = vOffset + delta - k1;
-                                       if ( k2Offset >= 0 && k2Offset < vLength && v2[ k2Offset ] !== -1 ) {
-
-                                               // Mirror x2 onto top-left coordinate system.
-                                               x2 = text1Length - v2[ k2Offset ];
-                                               if ( x1 >= x2 ) {
-
-                                                       // Overlap detected.
-                                                       return this.diffBisectSplit( text1, text2, x1, y1, deadline );
-                                               }
-                                       }
-                               }
-                       }
-
-                       // Walk the reverse path one step.
-                       for ( k2 = -d + k2start; k2 <= d - k2end; k2 += 2 ) {
-                               k2Offset = vOffset + k2;
-                               if ( k2 === -d || ( k2 !== d && v2[ k2Offset - 1 ] < v2[ k2Offset + 1 ] ) ) {
-                                       x2 = v2[ k2Offset + 1 ];
-                               } else {
-                                       x2 = v2[ k2Offset - 1 ] + 1;
-                               }
-                               y2 = x2 - k2;
-                               while ( x2 < text1Length && y2 < text2Length &&
-                                       text1.charAt( text1Length - x2 - 1 ) ===
-                                       text2.charAt( text2Length - y2 - 1 ) ) {
-                                       x2++;
-                                       y2++;
-                               }
-                               v2[ k2Offset ] = x2;
-                               if ( x2 > text1Length ) {
-
-                                       // Ran off the left of the graph.
-                                       k2end += 2;
-                               } else if ( y2 > text2Length ) {
-
-                                       // Ran off the top of the graph.
-                                       k2start += 2;
-                               } else if ( !front ) {
-                                       k1Offset = vOffset + delta - k2;
-                                       if ( k1Offset >= 0 && k1Offset < vLength && v1[ k1Offset ] !== -1 ) {
-                                               x1 = v1[ k1Offset ];
-                                               y1 = vOffset + x1 - k1Offset;
-
-                                               // Mirror x2 onto top-left coordinate system.
-                                               x2 = text1Length - x2;
-                                               if ( x1 >= x2 ) {
-
-                                                       // Overlap detected.
-                                                       return this.diffBisectSplit( text1, text2, x1, y1, deadline );
-                                               }
-                                       }
-                               }
-                       }
-               }
-
-               // Diff took too long and hit the deadline or
-               // number of diffs equals number of characters, no commonality at all.
-               return [
-                       [ DIFF_DELETE, text1 ],
-                       [ DIFF_INSERT, text2 ]
-               ];
-       };
-
-       /**
-        * Given the location of the 'middle snake', split the diff in two parts
-        * and recurse.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {number} x Index of split point in text1.
-        * @param {number} y Index of split point in text2.
-        * @param {number} deadline Time at which to bail if not yet complete.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffBisectSplit = function( text1, text2, x, y, deadline ) {
-               var text1a, text1b, text2a, text2b, diffs, diffsb;
-               text1a = text1.substring( 0, x );
-               text2a = text2.substring( 0, y );
-               text1b = text1.substring( x );
-               text2b = text2.substring( y );
-
-               // Compute both diffs serially.
-               diffs = this.DiffMain( text1a, text2a, false, deadline );
-               diffsb = this.DiffMain( text1b, text2b, false, deadline );
-
-               return diffs.concat( diffsb );
-       };
-
-       /**
-        * Reduce the number of edits by eliminating semantically trivial equalities.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        */
-       DiffMatchPatch.prototype.diffCleanupSemantic = function( diffs ) {
-               var changes, equalities, equalitiesLength, lastequality,
-                       pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1,
-                       lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2;
-               changes = false;
-               equalities = []; // Stack of indices where equalities are found.
-               equalitiesLength = 0; // Keeping our own length var is faster in JS.
-               /** @type {?string} */
-               lastequality = null;
-
-               // Always equal to diffs[equalities[equalitiesLength - 1]][1]
-               pointer = 0; // Index of current position.
-
-               // Number of characters that changed prior to the equality.
-               lengthInsertions1 = 0;
-               lengthDeletions1 = 0;
-
-               // Number of characters that changed after the equality.
-               lengthInsertions2 = 0;
-               lengthDeletions2 = 0;
-               while ( pointer < diffs.length ) {
-                       if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { // Equality found.
-                               equalities[ equalitiesLength++ ] = pointer;
-                               lengthInsertions1 = lengthInsertions2;
-                               lengthDeletions1 = lengthDeletions2;
-                               lengthInsertions2 = 0;
-                               lengthDeletions2 = 0;
-                               lastequality = diffs[ pointer ][ 1 ];
-                       } else { // An insertion or deletion.
-                               if ( diffs[ pointer ][ 0 ] === DIFF_INSERT ) {
-                                       lengthInsertions2 += diffs[ pointer ][ 1 ].length;
-                               } else {
-                                       lengthDeletions2 += diffs[ pointer ][ 1 ].length;
-                               }
-
-                               // Eliminate an equality that is smaller or equal to the edits on both
-                               // sides of it.
-                               if ( lastequality && ( lastequality.length <=
-                                               Math.max( lengthInsertions1, lengthDeletions1 ) ) &&
-                                               ( lastequality.length <= Math.max( lengthInsertions2,
-                                                       lengthDeletions2 ) ) ) {
-
-                                       // Duplicate record.
-                                       diffs.splice(
-                                               equalities[ equalitiesLength - 1 ],
-                                               0,
-                                               [ DIFF_DELETE, lastequality ]
-                                       );
-
-                                       // Change second copy to insert.
-                                       diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT;
-
-                                       // Throw away the equality we just deleted.
-                                       equalitiesLength--;
-
-                                       // Throw away the previous equality (it needs to be reevaluated).
-                                       equalitiesLength--;
-                                       pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1;
-
-                                       // Reset the counters.
-                                       lengthInsertions1 = 0;
-                                       lengthDeletions1 = 0;
-                                       lengthInsertions2 = 0;
-                                       lengthDeletions2 = 0;
-                                       lastequality = null;
-                                       changes = true;
-                               }
-                       }
-                       pointer++;
-               }
-
-               // Normalize the diff.
-               if ( changes ) {
-                       this.diffCleanupMerge( diffs );
-               }
-
-               // Find any overlaps between deletions and insertions.
-               // e.g: <del>abcxxx</del><ins>xxxdef</ins>
-               //   -> <del>abc</del>xxx<ins>def</ins>
-               // e.g: <del>xxxabc</del><ins>defxxx</ins>
-               //   -> <ins>def</ins>xxx<del>abc</del>
-               // Only extract an overlap if it is as big as the edit ahead or behind it.
-               pointer = 1;
-               while ( pointer < diffs.length ) {
-                       if ( diffs[ pointer - 1 ][ 0 ] === DIFF_DELETE &&
-                                       diffs[ pointer ][ 0 ] === DIFF_INSERT ) {
-                               deletion = diffs[ pointer - 1 ][ 1 ];
-                               insertion = diffs[ pointer ][ 1 ];
-                               overlapLength1 = this.diffCommonOverlap( deletion, insertion );
-                               overlapLength2 = this.diffCommonOverlap( insertion, deletion );
-                               if ( overlapLength1 >= overlapLength2 ) {
-                                       if ( overlapLength1 >= deletion.length / 2 ||
-                                                       overlapLength1 >= insertion.length / 2 ) {
-
-                                               // Overlap found.  Insert an equality and trim the surrounding edits.
-                                               diffs.splice(
-                                                       pointer,
-                                                       0,
-                                                       [ DIFF_EQUAL, insertion.substring( 0, overlapLength1 ) ]
-                                               );
-                                               diffs[ pointer - 1 ][ 1 ] =
-                                                       deletion.substring( 0, deletion.length - overlapLength1 );
-                                               diffs[ pointer + 1 ][ 1 ] = insertion.substring( overlapLength1 );
-                                               pointer++;
-                                       }
-                               } else {
-                                       if ( overlapLength2 >= deletion.length / 2 ||
-                                                       overlapLength2 >= insertion.length / 2 ) {
-
-                                               // Reverse overlap found.
-                                               // Insert an equality and swap and trim the surrounding edits.
-                                               diffs.splice(
-                                                       pointer,
-                                                       0,
-                                                       [ DIFF_EQUAL, deletion.substring( 0, overlapLength2 ) ]
-                                               );
-
-                                               diffs[ pointer - 1 ][ 0 ] = DIFF_INSERT;
-                                               diffs[ pointer - 1 ][ 1 ] =
-                                                       insertion.substring( 0, insertion.length - overlapLength2 );
-                                               diffs[ pointer + 1 ][ 0 ] = DIFF_DELETE;
-                                               diffs[ pointer + 1 ][ 1 ] =
-                                                       deletion.substring( overlapLength2 );
-                                               pointer++;
-                                       }
-                               }
-                               pointer++;
-                       }
-                       pointer++;
-               }
-       };
-
-       /**
-        * Determine if the suffix of one string is the prefix of another.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {number} The number of characters common to the end of the first
-        *     string and the start of the second string.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffCommonOverlap = function( text1, text2 ) {
-               var text1Length, text2Length, textLength,
-                       best, length, pattern, found;
-
-               // Cache the text lengths to prevent multiple calls.
-               text1Length = text1.length;
-               text2Length = text2.length;
-
-               // Eliminate the null case.
-               if ( text1Length === 0 || text2Length === 0 ) {
-                       return 0;
-               }
-
-               // Truncate the longer string.
-               if ( text1Length > text2Length ) {
-                       text1 = text1.substring( text1Length - text2Length );
-               } else if ( text1Length < text2Length ) {
-                       text2 = text2.substring( 0, text1Length );
-               }
-               textLength = Math.min( text1Length, text2Length );
-
-               // Quick check for the worst case.
-               if ( text1 === text2 ) {
-                       return textLength;
-               }
-
-               // Start by looking for a single character match
-               // and increase length until no match is found.
-               // Performance analysis: https://neil.fraser.name/news/2010/11/04/
-               best = 0;
-               length = 1;
-               while ( true ) {
-                       pattern = text1.substring( textLength - length );
-                       found = text2.indexOf( pattern );
-                       if ( found === -1 ) {
-                               return best;
-                       }
-                       length += found;
-                       if ( found === 0 || text1.substring( textLength - length ) ===
-                                       text2.substring( 0, length ) ) {
-                               best = length;
-                               length++;
-                       }
-               }
-       };
-
-       /**
-        * Split two texts into an array of strings.  Reduce the texts to a string of
-        * hashes where each Unicode character represents one line.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
-        *     An object containing the encoded text1, the encoded text2 and
-        *     the array of unique strings.
-        *     The zeroth element of the array of unique strings is intentionally blank.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffLinesToChars = function( text1, text2 ) {
-               var lineArray, lineHash, chars1, chars2;
-               lineArray = []; // E.g. lineArray[4] === 'Hello\n'
-               lineHash = {};  // E.g. lineHash['Hello\n'] === 4
-
-               // '\x00' is a valid character, but various debuggers don't like it.
-               // So we'll insert a junk entry to avoid generating a null character.
-               lineArray[ 0 ] = "";
-
-               /**
-                * Split a text into an array of strings.  Reduce the texts to a string of
-                * hashes where each Unicode character represents one line.
-                * Modifies linearray and linehash through being a closure.
-                * @param {string} text String to encode.
-                * @return {string} Encoded string.
-                * @private
-                */
-               function diffLinesToCharsMunge( text ) {
-                       var chars, lineStart, lineEnd, lineArrayLength, line;
-                       chars = "";
-
-                       // Walk the text, pulling out a substring for each line.
-                       // text.split('\n') would would temporarily double our memory footprint.
-                       // Modifying text would create many large strings to garbage collect.
-                       lineStart = 0;
-                       lineEnd = -1;
-
-                       // Keeping our own length variable is faster than looking it up.
-                       lineArrayLength = lineArray.length;
-                       while ( lineEnd < text.length - 1 ) {
-                               lineEnd = text.indexOf( "\n", lineStart );
-                               if ( lineEnd === -1 ) {
-                                       lineEnd = text.length - 1;
-                               }
-                               line = text.substring( lineStart, lineEnd + 1 );
-                               lineStart = lineEnd + 1;
-
-                               if ( lineHash.hasOwnProperty ? lineHash.hasOwnProperty( line ) :
-                                                       ( lineHash[ line ] !== undefined ) ) {
-                                       chars += String.fromCharCode( lineHash[ line ] );
-                               } else {
-                                       chars += String.fromCharCode( lineArrayLength );
-                                       lineHash[ line ] = lineArrayLength;
-                                       lineArray[ lineArrayLength++ ] = line;
-                               }
-                       }
-                       return chars;
-               }
-
-               chars1 = diffLinesToCharsMunge( text1 );
-               chars2 = diffLinesToCharsMunge( text2 );
-               return {
-                       chars1: chars1,
-                       chars2: chars2,
-                       lineArray: lineArray
-               };
-       };
-
-       /**
-        * Rehydrate the text in a diff from a string of line hashes to real lines of
-        * text.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        * @param {!Array.<string>} lineArray Array of unique strings.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffCharsToLines = function( diffs, lineArray ) {
-               var x, chars, text, y;
-               for ( x = 0; x < diffs.length; x++ ) {
-                       chars = diffs[ x ][ 1 ];
-                       text = [];
-                       for ( y = 0; y < chars.length; y++ ) {
-                               text[ y ] = lineArray[ chars.charCodeAt( y ) ];
-                       }
-                       diffs[ x ][ 1 ] = text.join( "" );
-               }
-       };
-
-       /**
-        * Reorder and merge like edit sections.  Merge equalities.
-        * Any edit section can move as long as it doesn't cross an equality.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        */
-       DiffMatchPatch.prototype.diffCleanupMerge = function( diffs ) {
-               var pointer, countDelete, countInsert, textInsert, textDelete,
-                       commonlength, changes, diffPointer, position;
-               diffs.push( [ DIFF_EQUAL, "" ] ); // Add a dummy entry at the end.
-               pointer = 0;
-               countDelete = 0;
-               countInsert = 0;
-               textDelete = "";
-               textInsert = "";
-               commonlength;
-               while ( pointer < diffs.length ) {
-                       switch ( diffs[ pointer ][ 0 ] ) {
-                       case DIFF_INSERT:
-                               countInsert++;
-                               textInsert += diffs[ pointer ][ 1 ];
-                               pointer++;
-                               break;
-                       case DIFF_DELETE:
-                               countDelete++;
-                               textDelete += diffs[ pointer ][ 1 ];
-                               pointer++;
-                               break;
-                       case DIFF_EQUAL:
-
-                               // Upon reaching an equality, check for prior redundancies.
-                               if ( countDelete + countInsert > 1 ) {
-                                       if ( countDelete !== 0 && countInsert !== 0 ) {
-
-                                               // Factor out any common prefixes.
-                                               commonlength = this.diffCommonPrefix( textInsert, textDelete );
-                                               if ( commonlength !== 0 ) {
-                                                       if ( ( pointer - countDelete - countInsert ) > 0 &&
-                                                                       diffs[ pointer - countDelete - countInsert - 1 ][ 0 ] ===
-                                                                       DIFF_EQUAL ) {
-                                                               diffs[ pointer - countDelete - countInsert - 1 ][ 1 ] +=
-                                                                       textInsert.substring( 0, commonlength );
-                                                       } else {
-                                                               diffs.splice( 0, 0, [ DIFF_EQUAL,
-                                                                       textInsert.substring( 0, commonlength )
-                                                               ] );
-                                                               pointer++;
-                                                       }
-                                                       textInsert = textInsert.substring( commonlength );
-                                                       textDelete = textDelete.substring( commonlength );
-                                               }
-
-                                               // Factor out any common suffixies.
-                                               commonlength = this.diffCommonSuffix( textInsert, textDelete );
-                                               if ( commonlength !== 0 ) {
-                                                       diffs[ pointer ][ 1 ] = textInsert.substring( textInsert.length -
-                                                                       commonlength ) + diffs[ pointer ][ 1 ];
-                                                       textInsert = textInsert.substring( 0, textInsert.length -
-                                                               commonlength );
-                                                       textDelete = textDelete.substring( 0, textDelete.length -
-                                                               commonlength );
-                                               }
-                                       }
-
-                                       // Delete the offending records and add the merged ones.
-                                       if ( countDelete === 0 ) {
-                                               diffs.splice( pointer - countInsert,
-                                                       countDelete + countInsert, [ DIFF_INSERT, textInsert ] );
-                                       } else if ( countInsert === 0 ) {
-                                               diffs.splice( pointer - countDelete,
-                                                       countDelete + countInsert, [ DIFF_DELETE, textDelete ] );
-                                       } else {
-                                               diffs.splice(
-                                                       pointer - countDelete - countInsert,
-                                                       countDelete + countInsert,
-                                                       [ DIFF_DELETE, textDelete ], [ DIFF_INSERT, textInsert ]
-                                               );
-                                       }
-                                       pointer = pointer - countDelete - countInsert +
-                                               ( countDelete ? 1 : 0 ) + ( countInsert ? 1 : 0 ) + 1;
-                               } else if ( pointer !== 0 && diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL ) {
-
-                                       // Merge this equality with the previous one.
-                                       diffs[ pointer - 1 ][ 1 ] += diffs[ pointer ][ 1 ];
-                                       diffs.splice( pointer, 1 );
-                               } else {
-                                       pointer++;
-                               }
-                               countInsert = 0;
-                               countDelete = 0;
-                               textDelete = "";
-                               textInsert = "";
-                               break;
-                       }
-               }
-               if ( diffs[ diffs.length - 1 ][ 1 ] === "" ) {
-                       diffs.pop(); // Remove the dummy entry at the end.
-               }
-
-               // Second pass: look for single edits surrounded on both sides by equalities
-               // which can be shifted sideways to eliminate an equality.
-               // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
-               changes = false;
-               pointer = 1;
-
-               // Intentionally ignore the first and last element (don't need checking).
-               while ( pointer < diffs.length - 1 ) {
-                       if ( diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL &&
-                                       diffs[ pointer + 1 ][ 0 ] === DIFF_EQUAL ) {
-
-                               diffPointer = diffs[ pointer ][ 1 ];
-                               position = diffPointer.substring(
-                                       diffPointer.length - diffs[ pointer - 1 ][ 1 ].length
-                               );
-
-                               // This is a single edit surrounded by equalities.
-                               if ( position === diffs[ pointer - 1 ][ 1 ] ) {
-
-                                       // Shift the edit over the previous equality.
-                                       diffs[ pointer ][ 1 ] = diffs[ pointer - 1 ][ 1 ] +
-                                               diffs[ pointer ][ 1 ].substring( 0, diffs[ pointer ][ 1 ].length -
-                                                       diffs[ pointer - 1 ][ 1 ].length );
-                                       diffs[ pointer + 1 ][ 1 ] =
-                                               diffs[ pointer - 1 ][ 1 ] + diffs[ pointer + 1 ][ 1 ];
-                                       diffs.splice( pointer - 1, 1 );
-                                       changes = true;
-                               } else if ( diffPointer.substring( 0, diffs[ pointer + 1 ][ 1 ].length ) ===
-                                               diffs[ pointer + 1 ][ 1 ] ) {
-
-                                       // Shift the edit over the next equality.
-                                       diffs[ pointer - 1 ][ 1 ] += diffs[ pointer + 1 ][ 1 ];
-                                       diffs[ pointer ][ 1 ] =
-                                               diffs[ pointer ][ 1 ].substring( diffs[ pointer + 1 ][ 1 ].length ) +
-                                               diffs[ pointer + 1 ][ 1 ];
-                                       diffs.splice( pointer + 1, 1 );
-                                       changes = true;
-                               }
-                       }
-                       pointer++;
-               }
-
-               // If shifts were made, the diff needs reordering and another shift sweep.
-               if ( changes ) {
-                       this.diffCleanupMerge( diffs );
-               }
-       };
-
-       return function( o, n ) {
-               var diff, output, text;
-               diff = new DiffMatchPatch();
-               output = diff.DiffMain( o, n );
-               diff.diffCleanupEfficiency( output );
-               text = diff.diffPrettyHtml( output );
-
-               return text;
-       };
-}() );
-
-}() );
+})();
index a6e300ae940d0b8d73cccd5002e4b7a5df5d0719..6070c261310d4bfe0d1096e3ff93318d4aaaf464 100644 (file)
@@ -41,7 +41,7 @@
     "gzip-js": "0.3.2",
     "jsdom": "5.6.1",
     "load-grunt-tasks": "1.0.0",
-    "qunitjs": "1.23.1",
+    "qunitjs": "1.17.1",
     "qunit-assert-step": "1.0.3",
     "requirejs": "2.1.17",
     "sinon": "1.10.3",