github.com/mweagle/Sparta@v1.15.0/docs_source/static/presentations/reveal.js-3.9.2/test/qunit-2.5.0.js (about)

     1  /*!
     2   * QUnit 2.5.0
     3   * https://qunitjs.com/
     4   *
     5   * Copyright jQuery Foundation and other contributors
     6   * Released under the MIT license
     7   * https://jquery.org/license
     8   *
     9   * Date: 2018-01-10T02:56Z
    10   */
    11  (function (global$1) {
    12    'use strict';
    13  
    14    global$1 = global$1 && global$1.hasOwnProperty('default') ? global$1['default'] : global$1;
    15  
    16    var window = global$1.window;
    17    var self$1 = global$1.self;
    18    var console = global$1.console;
    19    var setTimeout = global$1.setTimeout;
    20    var clearTimeout = global$1.clearTimeout;
    21  
    22    var document = window && window.document;
    23    var navigator = window && window.navigator;
    24  
    25    var localSessionStorage = function () {
    26    	var x = "qunit-test-string";
    27    	try {
    28    		global$1.sessionStorage.setItem(x, x);
    29    		global$1.sessionStorage.removeItem(x);
    30    		return global$1.sessionStorage;
    31    	} catch (e) {
    32    		return undefined;
    33    	}
    34    }();
    35  
    36    var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
    37      return typeof obj;
    38    } : function (obj) {
    39      return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
    40    };
    41  
    42  
    43  
    44  
    45  
    46  
    47  
    48  
    49  
    50  
    51  
    52    var classCallCheck = function (instance, Constructor) {
    53      if (!(instance instanceof Constructor)) {
    54        throw new TypeError("Cannot call a class as a function");
    55      }
    56    };
    57  
    58    var createClass = function () {
    59      function defineProperties(target, props) {
    60        for (var i = 0; i < props.length; i++) {
    61          var descriptor = props[i];
    62          descriptor.enumerable = descriptor.enumerable || false;
    63          descriptor.configurable = true;
    64          if ("value" in descriptor) descriptor.writable = true;
    65          Object.defineProperty(target, descriptor.key, descriptor);
    66        }
    67      }
    68  
    69      return function (Constructor, protoProps, staticProps) {
    70        if (protoProps) defineProperties(Constructor.prototype, protoProps);
    71        if (staticProps) defineProperties(Constructor, staticProps);
    72        return Constructor;
    73      };
    74    }();
    75  
    76  
    77  
    78  
    79  
    80  
    81  
    82  
    83  
    84  
    85  
    86  
    87  
    88  
    89  
    90  
    91  
    92  
    93  
    94  
    95  
    96  
    97  
    98  
    99  
   100  
   101  
   102  
   103  
   104  
   105  
   106  
   107  
   108  
   109  
   110  
   111  
   112  
   113  
   114  
   115  
   116    var toConsumableArray = function (arr) {
   117      if (Array.isArray(arr)) {
   118        for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
   119  
   120        return arr2;
   121      } else {
   122        return Array.from(arr);
   123      }
   124    };
   125  
   126    var toString = Object.prototype.toString;
   127    var hasOwn = Object.prototype.hasOwnProperty;
   128    var now = Date.now || function () {
   129    	return new Date().getTime();
   130    };
   131  
   132    var defined = {
   133    	document: window && window.document !== undefined,
   134    	setTimeout: setTimeout !== undefined
   135    };
   136  
   137    // Returns a new Array with the elements that are in a but not in b
   138    function diff(a, b) {
   139    	var i,
   140    	    j,
   141    	    result = a.slice();
   142  
   143    	for (i = 0; i < result.length; i++) {
   144    		for (j = 0; j < b.length; j++) {
   145    			if (result[i] === b[j]) {
   146    				result.splice(i, 1);
   147    				i--;
   148    				break;
   149    			}
   150    		}
   151    	}
   152    	return result;
   153    }
   154  
   155    /**
   156     * Determines whether an element exists in a given array or not.
   157     *
   158     * @method inArray
   159     * @param {Any} elem
   160     * @param {Array} array
   161     * @return {Boolean}
   162     */
   163    function inArray(elem, array) {
   164    	return array.indexOf(elem) !== -1;
   165    }
   166  
   167    /**
   168     * Makes a clone of an object using only Array or Object as base,
   169     * and copies over the own enumerable properties.
   170     *
   171     * @param {Object} obj
   172     * @return {Object} New object with only the own properties (recursively).
   173     */
   174    function objectValues(obj) {
   175    	var key,
   176    	    val,
   177    	    vals = is("array", obj) ? [] : {};
   178    	for (key in obj) {
   179    		if (hasOwn.call(obj, key)) {
   180    			val = obj[key];
   181    			vals[key] = val === Object(val) ? objectValues(val) : val;
   182    		}
   183    	}
   184    	return vals;
   185    }
   186  
   187    function extend(a, b, undefOnly) {
   188    	for (var prop in b) {
   189    		if (hasOwn.call(b, prop)) {
   190    			if (b[prop] === undefined) {
   191    				delete a[prop];
   192    			} else if (!(undefOnly && typeof a[prop] !== "undefined")) {
   193    				a[prop] = b[prop];
   194    			}
   195    		}
   196    	}
   197  
   198    	return a;
   199    }
   200  
   201    function objectType(obj) {
   202    	if (typeof obj === "undefined") {
   203    		return "undefined";
   204    	}
   205  
   206    	// Consider: typeof null === object
   207    	if (obj === null) {
   208    		return "null";
   209    	}
   210  
   211    	var match = toString.call(obj).match(/^\[object\s(.*)\]$/),
   212    	    type = match && match[1];
   213  
   214    	switch (type) {
   215    		case "Number":
   216    			if (isNaN(obj)) {
   217    				return "nan";
   218    			}
   219    			return "number";
   220    		case "String":
   221    		case "Boolean":
   222    		case "Array":
   223    		case "Set":
   224    		case "Map":
   225    		case "Date":
   226    		case "RegExp":
   227    		case "Function":
   228    		case "Symbol":
   229    			return type.toLowerCase();
   230    		default:
   231    			return typeof obj === "undefined" ? "undefined" : _typeof(obj);
   232    	}
   233    }
   234  
   235    // Safe object type checking
   236    function is(type, obj) {
   237    	return objectType(obj) === type;
   238    }
   239  
   240    // Based on Java's String.hashCode, a simple but not
   241    // rigorously collision resistant hashing function
   242    function generateHash(module, testName) {
   243    	var str = module + "\x1C" + testName;
   244    	var hash = 0;
   245  
   246    	for (var i = 0; i < str.length; i++) {
   247    		hash = (hash << 5) - hash + str.charCodeAt(i);
   248    		hash |= 0;
   249    	}
   250  
   251    	// Convert the possibly negative integer hash code into an 8 character hex string, which isn't
   252    	// strictly necessary but increases user understanding that the id is a SHA-like hash
   253    	var hex = (0x100000000 + hash).toString(16);
   254    	if (hex.length < 8) {
   255    		hex = "0000000" + hex;
   256    	}
   257  
   258    	return hex.slice(-8);
   259    }
   260  
   261    // Test for equality any JavaScript type.
   262    // Authors: Philippe Rathé <prathe@gmail.com>, David Chan <david@troi.org>
   263    var equiv = (function () {
   264  
   265    	// Value pairs queued for comparison. Used for breadth-first processing order, recursion
   266    	// detection and avoiding repeated comparison (see below for details).
   267    	// Elements are { a: val, b: val }.
   268    	var pairs = [];
   269  
   270    	var getProto = Object.getPrototypeOf || function (obj) {
   271    		return obj.__proto__;
   272    	};
   273  
   274    	function useStrictEquality(a, b) {
   275  
   276    		// This only gets called if a and b are not strict equal, and is used to compare on
   277    		// the primitive values inside object wrappers. For example:
   278    		// `var i = 1;`
   279    		// `var j = new Number(1);`
   280    		// Neither a nor b can be null, as a !== b and they have the same type.
   281    		if ((typeof a === "undefined" ? "undefined" : _typeof(a)) === "object") {
   282    			a = a.valueOf();
   283    		}
   284    		if ((typeof b === "undefined" ? "undefined" : _typeof(b)) === "object") {
   285    			b = b.valueOf();
   286    		}
   287  
   288    		return a === b;
   289    	}
   290  
   291    	function compareConstructors(a, b) {
   292    		var protoA = getProto(a);
   293    		var protoB = getProto(b);
   294  
   295    		// Comparing constructors is more strict than using `instanceof`
   296    		if (a.constructor === b.constructor) {
   297    			return true;
   298    		}
   299  
   300    		// Ref #851
   301    		// If the obj prototype descends from a null constructor, treat it
   302    		// as a null prototype.
   303    		if (protoA && protoA.constructor === null) {
   304    			protoA = null;
   305    		}
   306    		if (protoB && protoB.constructor === null) {
   307    			protoB = null;
   308    		}
   309  
   310    		// Allow objects with no prototype to be equivalent to
   311    		// objects with Object as their constructor.
   312    		if (protoA === null && protoB === Object.prototype || protoB === null && protoA === Object.prototype) {
   313    			return true;
   314    		}
   315  
   316    		return false;
   317    	}
   318  
   319    	function getRegExpFlags(regexp) {
   320    		return "flags" in regexp ? regexp.flags : regexp.toString().match(/[gimuy]*$/)[0];
   321    	}
   322  
   323    	function isContainer(val) {
   324    		return ["object", "array", "map", "set"].indexOf(objectType(val)) !== -1;
   325    	}
   326  
   327    	function breadthFirstCompareChild(a, b) {
   328  
   329    		// If a is a container not reference-equal to b, postpone the comparison to the
   330    		// end of the pairs queue -- unless (a, b) has been seen before, in which case skip
   331    		// over the pair.
   332    		if (a === b) {
   333    			return true;
   334    		}
   335    		if (!isContainer(a)) {
   336    			return typeEquiv(a, b);
   337    		}
   338    		if (pairs.every(function (pair) {
   339    			return pair.a !== a || pair.b !== b;
   340    		})) {
   341  
   342    			// Not yet started comparing this pair
   343    			pairs.push({ a: a, b: b });
   344    		}
   345    		return true;
   346    	}
   347  
   348    	var callbacks = {
   349    		"string": useStrictEquality,
   350    		"boolean": useStrictEquality,
   351    		"number": useStrictEquality,
   352    		"null": useStrictEquality,
   353    		"undefined": useStrictEquality,
   354    		"symbol": useStrictEquality,
   355    		"date": useStrictEquality,
   356  
   357    		"nan": function nan() {
   358    			return true;
   359    		},
   360  
   361    		"regexp": function regexp(a, b) {
   362    			return a.source === b.source &&
   363  
   364    			// Include flags in the comparison
   365    			getRegExpFlags(a) === getRegExpFlags(b);
   366    		},
   367  
   368    		// abort (identical references / instance methods were skipped earlier)
   369    		"function": function _function() {
   370    			return false;
   371    		},
   372  
   373    		"array": function array(a, b) {
   374    			var i, len;
   375  
   376    			len = a.length;
   377    			if (len !== b.length) {
   378  
   379    				// Safe and faster
   380    				return false;
   381    			}
   382  
   383    			for (i = 0; i < len; i++) {
   384  
   385    				// Compare non-containers; queue non-reference-equal containers
   386    				if (!breadthFirstCompareChild(a[i], b[i])) {
   387    					return false;
   388    				}
   389    			}
   390    			return true;
   391    		},
   392  
   393    		// Define sets a and b to be equivalent if for each element aVal in a, there
   394    		// is some element bVal in b such that aVal and bVal are equivalent. Element
   395    		// repetitions are not counted, so these are equivalent:
   396    		// a = new Set( [ {}, [], [] ] );
   397    		// b = new Set( [ {}, {}, [] ] );
   398    		"set": function set$$1(a, b) {
   399    			var innerEq,
   400    			    outerEq = true;
   401  
   402    			if (a.size !== b.size) {
   403  
   404    				// This optimization has certain quirks because of the lack of
   405    				// repetition counting. For instance, adding the same
   406    				// (reference-identical) element to two equivalent sets can
   407    				// make them non-equivalent.
   408    				return false;
   409    			}
   410  
   411    			a.forEach(function (aVal) {
   412  
   413    				// Short-circuit if the result is already known. (Using for...of
   414    				// with a break clause would be cleaner here, but it would cause
   415    				// a syntax error on older Javascript implementations even if
   416    				// Set is unused)
   417    				if (!outerEq) {
   418    					return;
   419    				}
   420  
   421    				innerEq = false;
   422  
   423    				b.forEach(function (bVal) {
   424    					var parentPairs;
   425  
   426    					// Likewise, short-circuit if the result is already known
   427    					if (innerEq) {
   428    						return;
   429    					}
   430  
   431    					// Swap out the global pairs list, as the nested call to
   432    					// innerEquiv will clobber its contents
   433    					parentPairs = pairs;
   434    					if (innerEquiv(bVal, aVal)) {
   435    						innerEq = true;
   436    					}
   437  
   438    					// Replace the global pairs list
   439    					pairs = parentPairs;
   440    				});
   441  
   442    				if (!innerEq) {
   443    					outerEq = false;
   444    				}
   445    			});
   446  
   447    			return outerEq;
   448    		},
   449  
   450    		// Define maps a and b to be equivalent if for each key-value pair (aKey, aVal)
   451    		// in a, there is some key-value pair (bKey, bVal) in b such that
   452    		// [ aKey, aVal ] and [ bKey, bVal ] are equivalent. Key repetitions are not
   453    		// counted, so these are equivalent:
   454    		// a = new Map( [ [ {}, 1 ], [ {}, 1 ], [ [], 1 ] ] );
   455    		// b = new Map( [ [ {}, 1 ], [ [], 1 ], [ [], 1 ] ] );
   456    		"map": function map(a, b) {
   457    			var innerEq,
   458    			    outerEq = true;
   459  
   460    			if (a.size !== b.size) {
   461  
   462    				// This optimization has certain quirks because of the lack of
   463    				// repetition counting. For instance, adding the same
   464    				// (reference-identical) key-value pair to two equivalent maps
   465    				// can make them non-equivalent.
   466    				return false;
   467    			}
   468  
   469    			a.forEach(function (aVal, aKey) {
   470  
   471    				// Short-circuit if the result is already known. (Using for...of
   472    				// with a break clause would be cleaner here, but it would cause
   473    				// a syntax error on older Javascript implementations even if
   474    				// Map is unused)
   475    				if (!outerEq) {
   476    					return;
   477    				}
   478  
   479    				innerEq = false;
   480  
   481    				b.forEach(function (bVal, bKey) {
   482    					var parentPairs;
   483  
   484    					// Likewise, short-circuit if the result is already known
   485    					if (innerEq) {
   486    						return;
   487    					}
   488  
   489    					// Swap out the global pairs list, as the nested call to
   490    					// innerEquiv will clobber its contents
   491    					parentPairs = pairs;
   492    					if (innerEquiv([bVal, bKey], [aVal, aKey])) {
   493    						innerEq = true;
   494    					}
   495  
   496    					// Replace the global pairs list
   497    					pairs = parentPairs;
   498    				});
   499  
   500    				if (!innerEq) {
   501    					outerEq = false;
   502    				}
   503    			});
   504  
   505    			return outerEq;
   506    		},
   507  
   508    		"object": function object(a, b) {
   509    			var i,
   510    			    aProperties = [],
   511    			    bProperties = [];
   512  
   513    			if (compareConstructors(a, b) === false) {
   514    				return false;
   515    			}
   516  
   517    			// Be strict: don't ensure hasOwnProperty and go deep
   518    			for (i in a) {
   519  
   520    				// Collect a's properties
   521    				aProperties.push(i);
   522  
   523    				// Skip OOP methods that look the same
   524    				if (a.constructor !== Object && typeof a.constructor !== "undefined" && typeof a[i] === "function" && typeof b[i] === "function" && a[i].toString() === b[i].toString()) {
   525    					continue;
   526    				}
   527  
   528    				// Compare non-containers; queue non-reference-equal containers
   529    				if (!breadthFirstCompareChild(a[i], b[i])) {
   530    					return false;
   531    				}
   532    			}
   533  
   534    			for (i in b) {
   535  
   536    				// Collect b's properties
   537    				bProperties.push(i);
   538    			}
   539  
   540    			// Ensures identical properties name
   541    			return typeEquiv(aProperties.sort(), bProperties.sort());
   542    		}
   543    	};
   544  
   545    	function typeEquiv(a, b) {
   546    		var type = objectType(a);
   547  
   548    		// Callbacks for containers will append to the pairs queue to achieve breadth-first
   549    		// search order. The pairs queue is also used to avoid reprocessing any pair of
   550    		// containers that are reference-equal to a previously visited pair (a special case
   551    		// this being recursion detection).
   552    		//
   553    		// Because of this approach, once typeEquiv returns a false value, it should not be
   554    		// called again without clearing the pair queue else it may wrongly report a visited
   555    		// pair as being equivalent.
   556    		return objectType(b) === type && callbacks[type](a, b);
   557    	}
   558  
   559    	function innerEquiv(a, b) {
   560    		var i, pair;
   561  
   562    		// We're done when there's nothing more to compare
   563    		if (arguments.length < 2) {
   564    			return true;
   565    		}
   566  
   567    		// Clear the global pair queue and add the top-level values being compared
   568    		pairs = [{ a: a, b: b }];
   569  
   570    		for (i = 0; i < pairs.length; i++) {
   571    			pair = pairs[i];
   572  
   573    			// Perform type-specific comparison on any pairs that are not strictly
   574    			// equal. For container types, that comparison will postpone comparison
   575    			// of any sub-container pair to the end of the pair queue. This gives
   576    			// breadth-first search order. It also avoids the reprocessing of
   577    			// reference-equal siblings, cousins etc, which can have a significant speed
   578    			// impact when comparing a container of small objects each of which has a
   579    			// reference to the same (singleton) large object.
   580    			if (pair.a !== pair.b && !typeEquiv(pair.a, pair.b)) {
   581    				return false;
   582    			}
   583    		}
   584  
   585    		// ...across all consecutive argument pairs
   586    		return arguments.length === 2 || innerEquiv.apply(this, [].slice.call(arguments, 1));
   587    	}
   588  
   589    	return function () {
   590    		var result = innerEquiv.apply(undefined, arguments);
   591  
   592    		// Release any retained objects
   593    		pairs.length = 0;
   594    		return result;
   595    	};
   596    })();
   597  
   598    /**
   599     * Config object: Maintain internal state
   600     * Later exposed as QUnit.config
   601     * `config` initialized at top of scope
   602     */
   603    var config = {
   604  
   605    	// The queue of tests to run
   606    	queue: [],
   607  
   608    	// Block until document ready
   609    	blocking: true,
   610  
   611    	// By default, run previously failed tests first
   612    	// very useful in combination with "Hide passed tests" checked
   613    	reorder: true,
   614  
   615    	// By default, modify document.title when suite is done
   616    	altertitle: true,
   617  
   618    	// HTML Reporter: collapse every test except the first failing test
   619    	// If false, all failing tests will be expanded
   620    	collapse: true,
   621  
   622    	// By default, scroll to top of the page when suite is done
   623    	scrolltop: true,
   624  
   625    	// Depth up-to which object will be dumped
   626    	maxDepth: 5,
   627  
   628    	// When enabled, all tests must call expect()
   629    	requireExpects: false,
   630  
   631    	// Placeholder for user-configurable form-exposed URL parameters
   632    	urlConfig: [],
   633  
   634    	// Set of all modules.
   635    	modules: [],
   636  
   637    	// The first unnamed module
   638    	currentModule: {
   639    		name: "",
   640    		tests: [],
   641    		childModules: [],
   642    		testsRun: 0,
   643    		unskippedTestsRun: 0,
   644    		hooks: {
   645    			before: [],
   646    			beforeEach: [],
   647    			afterEach: [],
   648    			after: []
   649    		}
   650    	},
   651  
   652    	callbacks: {},
   653  
   654    	// The storage module to use for reordering tests
   655    	storage: localSessionStorage
   656    };
   657  
   658    // take a predefined QUnit.config and extend the defaults
   659    var globalConfig = window && window.QUnit && window.QUnit.config;
   660  
   661    // only extend the global config if there is no QUnit overload
   662    if (window && window.QUnit && !window.QUnit.version) {
   663    	extend(config, globalConfig);
   664    }
   665  
   666    // Push a loose unnamed module to the modules collection
   667    config.modules.push(config.currentModule);
   668  
   669    // Based on jsDump by Ariel Flesler
   670    // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
   671    var dump = (function () {
   672    	function quote(str) {
   673    		return "\"" + str.toString().replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"";
   674    	}
   675    	function literal(o) {
   676    		return o + "";
   677    	}
   678    	function join(pre, arr, post) {
   679    		var s = dump.separator(),
   680    		    base = dump.indent(),
   681    		    inner = dump.indent(1);
   682    		if (arr.join) {
   683    			arr = arr.join("," + s + inner);
   684    		}
   685    		if (!arr) {
   686    			return pre + post;
   687    		}
   688    		return [pre, inner + arr, base + post].join(s);
   689    	}
   690    	function array(arr, stack) {
   691    		var i = arr.length,
   692    		    ret = new Array(i);
   693  
   694    		if (dump.maxDepth && dump.depth > dump.maxDepth) {
   695    			return "[object Array]";
   696    		}
   697  
   698    		this.up();
   699    		while (i--) {
   700    			ret[i] = this.parse(arr[i], undefined, stack);
   701    		}
   702    		this.down();
   703    		return join("[", ret, "]");
   704    	}
   705  
   706    	function isArray(obj) {
   707    		return (
   708  
   709    			//Native Arrays
   710    			toString.call(obj) === "[object Array]" ||
   711  
   712    			// NodeList objects
   713    			typeof obj.length === "number" && obj.item !== undefined && (obj.length ? obj.item(0) === obj[0] : obj.item(0) === null && obj[0] === undefined)
   714    		);
   715    	}
   716  
   717    	var reName = /^function (\w+)/,
   718    	    dump = {
   719  
   720    		// The objType is used mostly internally, you can fix a (custom) type in advance
   721    		parse: function parse(obj, objType, stack) {
   722    			stack = stack || [];
   723    			var res,
   724    			    parser,
   725    			    parserType,
   726    			    objIndex = stack.indexOf(obj);
   727  
   728    			if (objIndex !== -1) {
   729    				return "recursion(" + (objIndex - stack.length) + ")";
   730    			}
   731  
   732    			objType = objType || this.typeOf(obj);
   733    			parser = this.parsers[objType];
   734    			parserType = typeof parser === "undefined" ? "undefined" : _typeof(parser);
   735  
   736    			if (parserType === "function") {
   737    				stack.push(obj);
   738    				res = parser.call(this, obj, stack);
   739    				stack.pop();
   740    				return res;
   741    			}
   742    			return parserType === "string" ? parser : this.parsers.error;
   743    		},
   744    		typeOf: function typeOf(obj) {
   745    			var type;
   746  
   747    			if (obj === null) {
   748    				type = "null";
   749    			} else if (typeof obj === "undefined") {
   750    				type = "undefined";
   751    			} else if (is("regexp", obj)) {
   752    				type = "regexp";
   753    			} else if (is("date", obj)) {
   754    				type = "date";
   755    			} else if (is("function", obj)) {
   756    				type = "function";
   757    			} else if (obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined) {
   758    				type = "window";
   759    			} else if (obj.nodeType === 9) {
   760    				type = "document";
   761    			} else if (obj.nodeType) {
   762    				type = "node";
   763    			} else if (isArray(obj)) {
   764    				type = "array";
   765    			} else if (obj.constructor === Error.prototype.constructor) {
   766    				type = "error";
   767    			} else {
   768    				type = typeof obj === "undefined" ? "undefined" : _typeof(obj);
   769    			}
   770    			return type;
   771    		},
   772  
   773    		separator: function separator() {
   774    			if (this.multiline) {
   775    				return this.HTML ? "<br />" : "\n";
   776    			} else {
   777    				return this.HTML ? "&#160;" : " ";
   778    			}
   779    		},
   780  
   781    		// Extra can be a number, shortcut for increasing-calling-decreasing
   782    		indent: function indent(extra) {
   783    			if (!this.multiline) {
   784    				return "";
   785    			}
   786    			var chr = this.indentChar;
   787    			if (this.HTML) {
   788    				chr = chr.replace(/\t/g, "   ").replace(/ /g, "&#160;");
   789    			}
   790    			return new Array(this.depth + (extra || 0)).join(chr);
   791    		},
   792    		up: function up(a) {
   793    			this.depth += a || 1;
   794    		},
   795    		down: function down(a) {
   796    			this.depth -= a || 1;
   797    		},
   798    		setParser: function setParser(name, parser) {
   799    			this.parsers[name] = parser;
   800    		},
   801  
   802    		// The next 3 are exposed so you can use them
   803    		quote: quote,
   804    		literal: literal,
   805    		join: join,
   806    		depth: 1,
   807    		maxDepth: config.maxDepth,
   808  
   809    		// This is the list of parsers, to modify them, use dump.setParser
   810    		parsers: {
   811    			window: "[Window]",
   812    			document: "[Document]",
   813    			error: function error(_error) {
   814    				return "Error(\"" + _error.message + "\")";
   815    			},
   816    			unknown: "[Unknown]",
   817    			"null": "null",
   818    			"undefined": "undefined",
   819    			"function": function _function(fn) {
   820    				var ret = "function",
   821  
   822  
   823    				// Functions never have name in IE
   824    				name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
   825  
   826    				if (name) {
   827    					ret += " " + name;
   828    				}
   829    				ret += "(";
   830  
   831    				ret = [ret, dump.parse(fn, "functionArgs"), "){"].join("");
   832    				return join(ret, dump.parse(fn, "functionCode"), "}");
   833    			},
   834    			array: array,
   835    			nodelist: array,
   836    			"arguments": array,
   837    			object: function object(map, stack) {
   838    				var keys,
   839    				    key,
   840    				    val,
   841    				    i,
   842    				    nonEnumerableProperties,
   843    				    ret = [];
   844  
   845    				if (dump.maxDepth && dump.depth > dump.maxDepth) {
   846    					return "[object Object]";
   847    				}
   848  
   849    				dump.up();
   850    				keys = [];
   851    				for (key in map) {
   852    					keys.push(key);
   853    				}
   854  
   855    				// Some properties are not always enumerable on Error objects.
   856    				nonEnumerableProperties = ["message", "name"];
   857    				for (i in nonEnumerableProperties) {
   858    					key = nonEnumerableProperties[i];
   859    					if (key in map && !inArray(key, keys)) {
   860    						keys.push(key);
   861    					}
   862    				}
   863    				keys.sort();
   864    				for (i = 0; i < keys.length; i++) {
   865    					key = keys[i];
   866    					val = map[key];
   867    					ret.push(dump.parse(key, "key") + ": " + dump.parse(val, undefined, stack));
   868    				}
   869    				dump.down();
   870    				return join("{", ret, "}");
   871    			},
   872    			node: function node(_node) {
   873    				var len,
   874    				    i,
   875    				    val,
   876    				    open = dump.HTML ? "&lt;" : "<",
   877    				    close = dump.HTML ? "&gt;" : ">",
   878    				    tag = _node.nodeName.toLowerCase(),
   879    				    ret = open + tag,
   880    				    attrs = _node.attributes;
   881  
   882    				if (attrs) {
   883    					for (i = 0, len = attrs.length; i < len; i++) {
   884    						val = attrs[i].nodeValue;
   885  
   886    						// IE6 includes all attributes in .attributes, even ones not explicitly
   887    						// set. Those have values like undefined, null, 0, false, "" or
   888    						// "inherit".
   889    						if (val && val !== "inherit") {
   890    							ret += " " + attrs[i].nodeName + "=" + dump.parse(val, "attribute");
   891    						}
   892    					}
   893    				}
   894    				ret += close;
   895  
   896    				// Show content of TextNode or CDATASection
   897    				if (_node.nodeType === 3 || _node.nodeType === 4) {
   898    					ret += _node.nodeValue;
   899    				}
   900  
   901    				return ret + open + "/" + tag + close;
   902    			},
   903  
   904    			// Function calls it internally, it's the arguments part of the function
   905    			functionArgs: function functionArgs(fn) {
   906    				var args,
   907    				    l = fn.length;
   908  
   909    				if (!l) {
   910    					return "";
   911    				}
   912  
   913    				args = new Array(l);
   914    				while (l--) {
   915  
   916    					// 97 is 'a'
   917    					args[l] = String.fromCharCode(97 + l);
   918    				}
   919    				return " " + args.join(", ") + " ";
   920    			},
   921  
   922    			// Object calls it internally, the key part of an item in a map
   923    			key: quote,
   924  
   925    			// Function calls it internally, it's the content of the function
   926    			functionCode: "[code]",
   927  
   928    			// Node calls it internally, it's a html attribute value
   929    			attribute: quote,
   930    			string: quote,
   931    			date: quote,
   932    			regexp: literal,
   933    			number: literal,
   934    			"boolean": literal,
   935    			symbol: function symbol(sym) {
   936    				return sym.toString();
   937    			}
   938    		},
   939  
   940    		// If true, entities are escaped ( <, >, \t, space and \n )
   941    		HTML: false,
   942  
   943    		// Indentation unit
   944    		indentChar: "  ",
   945  
   946    		// If true, items in a collection, are separated by a \n, else just a space.
   947    		multiline: true
   948    	};
   949  
   950    	return dump;
   951    })();
   952  
   953    var LISTENERS = Object.create(null);
   954    var SUPPORTED_EVENTS = ["runStart", "suiteStart", "testStart", "assertion", "testEnd", "suiteEnd", "runEnd"];
   955  
   956    /**
   957     * Emits an event with the specified data to all currently registered listeners.
   958     * Callbacks will fire in the order in which they are registered (FIFO). This
   959     * function is not exposed publicly; it is used by QUnit internals to emit
   960     * logging events.
   961     *
   962     * @private
   963     * @method emit
   964     * @param {String} eventName
   965     * @param {Object} data
   966     * @return {Void}
   967     */
   968    function emit(eventName, data) {
   969    	if (objectType(eventName) !== "string") {
   970    		throw new TypeError("eventName must be a string when emitting an event");
   971    	}
   972  
   973    	// Clone the callbacks in case one of them registers a new callback
   974    	var originalCallbacks = LISTENERS[eventName];
   975    	var callbacks = originalCallbacks ? [].concat(toConsumableArray(originalCallbacks)) : [];
   976  
   977    	for (var i = 0; i < callbacks.length; i++) {
   978    		callbacks[i](data);
   979    	}
   980    }
   981  
   982    /**
   983     * Registers a callback as a listener to the specified event.
   984     *
   985     * @public
   986     * @method on
   987     * @param {String} eventName
   988     * @param {Function} callback
   989     * @return {Void}
   990     */
   991    function on(eventName, callback) {
   992    	if (objectType(eventName) !== "string") {
   993    		throw new TypeError("eventName must be a string when registering a listener");
   994    	} else if (!inArray(eventName, SUPPORTED_EVENTS)) {
   995    		var events = SUPPORTED_EVENTS.join(", ");
   996    		throw new Error("\"" + eventName + "\" is not a valid event; must be one of: " + events + ".");
   997    	} else if (objectType(callback) !== "function") {
   998    		throw new TypeError("callback must be a function when registering a listener");
   999    	}
  1000  
  1001    	if (!LISTENERS[eventName]) {
  1002    		LISTENERS[eventName] = [];
  1003    	}
  1004  
  1005    	// Don't register the same callback more than once
  1006    	if (!inArray(callback, LISTENERS[eventName])) {
  1007    		LISTENERS[eventName].push(callback);
  1008    	}
  1009    }
  1010  
  1011    // Register logging callbacks
  1012    function registerLoggingCallbacks(obj) {
  1013    	var i,
  1014    	    l,
  1015    	    key,
  1016    	    callbackNames = ["begin", "done", "log", "testStart", "testDone", "moduleStart", "moduleDone"];
  1017  
  1018    	function registerLoggingCallback(key) {
  1019    		var loggingCallback = function loggingCallback(callback) {
  1020    			if (objectType(callback) !== "function") {
  1021    				throw new Error("QUnit logging methods require a callback function as their first parameters.");
  1022    			}
  1023  
  1024    			config.callbacks[key].push(callback);
  1025    		};
  1026  
  1027    		return loggingCallback;
  1028    	}
  1029  
  1030    	for (i = 0, l = callbackNames.length; i < l; i++) {
  1031    		key = callbackNames[i];
  1032  
  1033    		// Initialize key collection of logging callback
  1034    		if (objectType(config.callbacks[key]) === "undefined") {
  1035    			config.callbacks[key] = [];
  1036    		}
  1037  
  1038    		obj[key] = registerLoggingCallback(key);
  1039    	}
  1040    }
  1041  
  1042    function runLoggingCallbacks(key, args) {
  1043    	var i, l, callbacks;
  1044  
  1045    	callbacks = config.callbacks[key];
  1046    	for (i = 0, l = callbacks.length; i < l; i++) {
  1047    		callbacks[i](args);
  1048    	}
  1049    }
  1050  
  1051    // Doesn't support IE9, it will return undefined on these browsers
  1052    // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
  1053    var fileName = (sourceFromStacktrace(0) || "").replace(/(:\d+)+\)?/, "").replace(/.+\//, "");
  1054  
  1055    function extractStacktrace(e, offset) {
  1056    	offset = offset === undefined ? 4 : offset;
  1057  
  1058    	var stack, include, i;
  1059  
  1060    	if (e && e.stack) {
  1061    		stack = e.stack.split("\n");
  1062    		if (/^error$/i.test(stack[0])) {
  1063    			stack.shift();
  1064    		}
  1065    		if (fileName) {
  1066    			include = [];
  1067    			for (i = offset; i < stack.length; i++) {
  1068    				if (stack[i].indexOf(fileName) !== -1) {
  1069    					break;
  1070    				}
  1071    				include.push(stack[i]);
  1072    			}
  1073    			if (include.length) {
  1074    				return include.join("\n");
  1075    			}
  1076    		}
  1077    		return stack[offset];
  1078    	}
  1079    }
  1080  
  1081    function sourceFromStacktrace(offset) {
  1082    	var error = new Error();
  1083  
  1084    	// Support: Safari <=7 only, IE <=10 - 11 only
  1085    	// Not all browsers generate the `stack` property for `new Error()`, see also #636
  1086    	if (!error.stack) {
  1087    		try {
  1088    			throw error;
  1089    		} catch (err) {
  1090    			error = err;
  1091    		}
  1092    	}
  1093  
  1094    	return extractStacktrace(error, offset);
  1095    }
  1096  
  1097    var priorityCount = 0;
  1098    var unitSampler = void 0;
  1099  
  1100    /**
  1101     * Advances the ProcessingQueue to the next item if it is ready.
  1102     * @param {Boolean} last
  1103     */
  1104    function advance() {
  1105    	var start = now();
  1106    	config.depth = (config.depth || 0) + 1;
  1107  
  1108    	while (config.queue.length && !config.blocking) {
  1109    		var elapsedTime = now() - start;
  1110  
  1111    		if (!defined.setTimeout || config.updateRate <= 0 || elapsedTime < config.updateRate) {
  1112    			if (priorityCount > 0) {
  1113    				priorityCount--;
  1114    			}
  1115  
  1116    			config.queue.shift()();
  1117    		} else {
  1118    			setTimeout(advance);
  1119    			break;
  1120    		}
  1121    	}
  1122  
  1123    	config.depth--;
  1124  
  1125    	if (!config.blocking && !config.queue.length && config.depth === 0) {
  1126    		done();
  1127    	}
  1128    }
  1129  
  1130    function addToQueueImmediate(callback) {
  1131    	if (objectType(callback) === "array") {
  1132    		while (callback.length) {
  1133    			addToQueueImmediate(callback.pop());
  1134    		}
  1135  
  1136    		return;
  1137    	}
  1138  
  1139    	config.queue.unshift(callback);
  1140    	priorityCount++;
  1141    }
  1142  
  1143    /**
  1144     * Adds a function to the ProcessingQueue for execution.
  1145     * @param {Function|Array} callback
  1146     * @param {Boolean} priority
  1147     * @param {String} seed
  1148     */
  1149    function addToQueue(callback, prioritize, seed) {
  1150    	if (prioritize) {
  1151    		config.queue.splice(priorityCount++, 0, callback);
  1152    	} else if (seed) {
  1153    		if (!unitSampler) {
  1154    			unitSampler = unitSamplerGenerator(seed);
  1155    		}
  1156  
  1157    		// Insert into a random position after all prioritized items
  1158    		var index = Math.floor(unitSampler() * (config.queue.length - priorityCount + 1));
  1159    		config.queue.splice(priorityCount + index, 0, callback);
  1160    	} else {
  1161    		config.queue.push(callback);
  1162    	}
  1163    }
  1164  
  1165    /**
  1166     * Creates a seeded "sample" generator which is used for randomizing tests.
  1167     */
  1168    function unitSamplerGenerator(seed) {
  1169  
  1170    	// 32-bit xorshift, requires only a nonzero seed
  1171    	// http://excamera.com/sphinx/article-xorshift.html
  1172    	var sample = parseInt(generateHash(seed), 16) || -1;
  1173    	return function () {
  1174    		sample ^= sample << 13;
  1175    		sample ^= sample >>> 17;
  1176    		sample ^= sample << 5;
  1177  
  1178    		// ECMAScript has no unsigned number type
  1179    		if (sample < 0) {
  1180    			sample += 0x100000000;
  1181    		}
  1182  
  1183    		return sample / 0x100000000;
  1184    	};
  1185    }
  1186  
  1187    /**
  1188     * This function is called when the ProcessingQueue is done processing all
  1189     * items. It handles emitting the final run events.
  1190     */
  1191    function done() {
  1192    	var storage = config.storage;
  1193  
  1194    	ProcessingQueue.finished = true;
  1195  
  1196    	var runtime = now() - config.started;
  1197    	var passed = config.stats.all - config.stats.bad;
  1198  
  1199    	emit("runEnd", globalSuite.end(true));
  1200    	runLoggingCallbacks("done", {
  1201    		passed: passed,
  1202    		failed: config.stats.bad,
  1203    		total: config.stats.all,
  1204    		runtime: runtime
  1205    	});
  1206  
  1207    	// Clear own storage items if all tests passed
  1208    	if (storage && config.stats.bad === 0) {
  1209    		for (var i = storage.length - 1; i >= 0; i--) {
  1210    			var key = storage.key(i);
  1211  
  1212    			if (key.indexOf("qunit-test-") === 0) {
  1213    				storage.removeItem(key);
  1214    			}
  1215    		}
  1216    	}
  1217    }
  1218  
  1219    var ProcessingQueue = {
  1220    	finished: false,
  1221    	add: addToQueue,
  1222    	addImmediate: addToQueueImmediate,
  1223    	advance: advance
  1224    };
  1225  
  1226    var TestReport = function () {
  1227    	function TestReport(name, suite, options) {
  1228    		classCallCheck(this, TestReport);
  1229  
  1230    		this.name = name;
  1231    		this.suiteName = suite.name;
  1232    		this.fullName = suite.fullName.concat(name);
  1233    		this.runtime = 0;
  1234    		this.assertions = [];
  1235  
  1236    		this.skipped = !!options.skip;
  1237    		this.todo = !!options.todo;
  1238  
  1239    		this.valid = options.valid;
  1240  
  1241    		this._startTime = 0;
  1242    		this._endTime = 0;
  1243  
  1244    		suite.pushTest(this);
  1245    	}
  1246  
  1247    	createClass(TestReport, [{
  1248    		key: "start",
  1249    		value: function start(recordTime) {
  1250    			if (recordTime) {
  1251    				this._startTime = Date.now();
  1252    			}
  1253  
  1254    			return {
  1255    				name: this.name,
  1256    				suiteName: this.suiteName,
  1257    				fullName: this.fullName.slice()
  1258    			};
  1259    		}
  1260    	}, {
  1261    		key: "end",
  1262    		value: function end(recordTime) {
  1263    			if (recordTime) {
  1264    				this._endTime = Date.now();
  1265    			}
  1266  
  1267    			return extend(this.start(), {
  1268    				runtime: this.getRuntime(),
  1269    				status: this.getStatus(),
  1270    				errors: this.getFailedAssertions(),
  1271    				assertions: this.getAssertions()
  1272    			});
  1273    		}
  1274    	}, {
  1275    		key: "pushAssertion",
  1276    		value: function pushAssertion(assertion) {
  1277    			this.assertions.push(assertion);
  1278    		}
  1279    	}, {
  1280    		key: "getRuntime",
  1281    		value: function getRuntime() {
  1282    			return this._endTime - this._startTime;
  1283    		}
  1284    	}, {
  1285    		key: "getStatus",
  1286    		value: function getStatus() {
  1287    			if (this.skipped) {
  1288    				return "skipped";
  1289    			}
  1290  
  1291    			var testPassed = this.getFailedAssertions().length > 0 ? this.todo : !this.todo;
  1292  
  1293    			if (!testPassed) {
  1294    				return "failed";
  1295    			} else if (this.todo) {
  1296    				return "todo";
  1297    			} else {
  1298    				return "passed";
  1299    			}
  1300    		}
  1301    	}, {
  1302    		key: "getFailedAssertions",
  1303    		value: function getFailedAssertions() {
  1304    			return this.assertions.filter(function (assertion) {
  1305    				return !assertion.passed;
  1306    			});
  1307    		}
  1308    	}, {
  1309    		key: "getAssertions",
  1310    		value: function getAssertions() {
  1311    			return this.assertions.slice();
  1312    		}
  1313  
  1314    		// Remove actual and expected values from assertions. This is to prevent
  1315    		// leaking memory throughout a test suite.
  1316  
  1317    	}, {
  1318    		key: "slimAssertions",
  1319    		value: function slimAssertions() {
  1320    			this.assertions = this.assertions.map(function (assertion) {
  1321    				delete assertion.actual;
  1322    				delete assertion.expected;
  1323    				return assertion;
  1324    			});
  1325    		}
  1326    	}]);
  1327    	return TestReport;
  1328    }();
  1329  
  1330    var focused$1 = false;
  1331  
  1332    function Test(settings) {
  1333    	var i, l;
  1334  
  1335    	++Test.count;
  1336  
  1337    	this.expected = null;
  1338    	this.assertions = [];
  1339    	this.semaphore = 0;
  1340    	this.module = config.currentModule;
  1341    	this.stack = sourceFromStacktrace(3);
  1342    	this.steps = [];
  1343    	this.timeout = undefined;
  1344  
  1345    	// If a module is skipped, all its tests and the tests of the child suites
  1346    	// should be treated as skipped even if they are defined as `only` or `todo`.
  1347    	// As for `todo` module, all its tests will be treated as `todo` except for
  1348    	// tests defined as `skip` which will be left intact.
  1349    	//
  1350    	// So, if a test is defined as `todo` and is inside a skipped module, we should
  1351    	// then treat that test as if was defined as `skip`.
  1352    	if (this.module.skip) {
  1353    		settings.skip = true;
  1354    		settings.todo = false;
  1355  
  1356    		// Skipped tests should be left intact
  1357    	} else if (this.module.todo && !settings.skip) {
  1358    		settings.todo = true;
  1359    	}
  1360  
  1361    	extend(this, settings);
  1362  
  1363    	this.testReport = new TestReport(settings.testName, this.module.suiteReport, {
  1364    		todo: settings.todo,
  1365    		skip: settings.skip,
  1366    		valid: this.valid()
  1367    	});
  1368  
  1369    	// Register unique strings
  1370    	for (i = 0, l = this.module.tests; i < l.length; i++) {
  1371    		if (this.module.tests[i].name === this.testName) {
  1372    			this.testName += " ";
  1373    		}
  1374    	}
  1375  
  1376    	this.testId = generateHash(this.module.name, this.testName);
  1377  
  1378    	this.module.tests.push({
  1379    		name: this.testName,
  1380    		testId: this.testId,
  1381    		skip: !!settings.skip
  1382    	});
  1383  
  1384    	if (settings.skip) {
  1385  
  1386    		// Skipped tests will fully ignore any sent callback
  1387    		this.callback = function () {};
  1388    		this.async = false;
  1389    		this.expected = 0;
  1390    	} else {
  1391    		if (typeof this.callback !== "function") {
  1392    			var method = this.todo ? "todo" : "test";
  1393  
  1394    			// eslint-disable-next-line max-len
  1395    			throw new TypeError("You must provide a function as a test callback to QUnit." + method + "(\"" + settings.testName + "\")");
  1396    		}
  1397  
  1398    		this.assert = new Assert(this);
  1399    	}
  1400    }
  1401  
  1402    Test.count = 0;
  1403  
  1404    function getNotStartedModules(startModule) {
  1405    	var module = startModule,
  1406    	    modules = [];
  1407  
  1408    	while (module && module.testsRun === 0) {
  1409    		modules.push(module);
  1410    		module = module.parentModule;
  1411    	}
  1412  
  1413    	return modules;
  1414    }
  1415  
  1416    Test.prototype = {
  1417    	before: function before() {
  1418    		var i,
  1419    		    startModule,
  1420    		    module = this.module,
  1421    		    notStartedModules = getNotStartedModules(module);
  1422  
  1423    		for (i = notStartedModules.length - 1; i >= 0; i--) {
  1424    			startModule = notStartedModules[i];
  1425    			startModule.stats = { all: 0, bad: 0, started: now() };
  1426    			emit("suiteStart", startModule.suiteReport.start(true));
  1427    			runLoggingCallbacks("moduleStart", {
  1428    				name: startModule.name,
  1429    				tests: startModule.tests
  1430    			});
  1431    		}
  1432  
  1433    		config.current = this;
  1434  
  1435    		this.testEnvironment = extend({}, module.testEnvironment);
  1436  
  1437    		this.started = now();
  1438    		emit("testStart", this.testReport.start(true));
  1439    		runLoggingCallbacks("testStart", {
  1440    			name: this.testName,
  1441    			module: module.name,
  1442    			testId: this.testId,
  1443    			previousFailure: this.previousFailure
  1444    		});
  1445  
  1446    		if (!config.pollution) {
  1447    			saveGlobal();
  1448    		}
  1449    	},
  1450  
  1451    	run: function run() {
  1452    		var promise;
  1453  
  1454    		config.current = this;
  1455  
  1456    		this.callbackStarted = now();
  1457  
  1458    		if (config.notrycatch) {
  1459    			runTest(this);
  1460    			return;
  1461    		}
  1462  
  1463    		try {
  1464    			runTest(this);
  1465    		} catch (e) {
  1466    			this.pushFailure("Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + (e.message || e), extractStacktrace(e, 0));
  1467  
  1468    			// Else next test will carry the responsibility
  1469    			saveGlobal();
  1470  
  1471    			// Restart the tests if they're blocking
  1472    			if (config.blocking) {
  1473    				internalRecover(this);
  1474    			}
  1475    		}
  1476  
  1477    		function runTest(test) {
  1478    			promise = test.callback.call(test.testEnvironment, test.assert);
  1479    			test.resolvePromise(promise);
  1480  
  1481    			// If the test has a "lock" on it, but the timeout is 0, then we push a
  1482    			// failure as the test should be synchronous.
  1483    			if (test.timeout === 0 && test.semaphore !== 0) {
  1484    				pushFailure("Test did not finish synchronously even though assert.timeout( 0 ) was used.", sourceFromStacktrace(2));
  1485    			}
  1486    		}
  1487    	},
  1488  
  1489    	after: function after() {
  1490    		checkPollution();
  1491    	},
  1492  
  1493    	queueHook: function queueHook(hook, hookName, hookOwner) {
  1494    		var _this = this;
  1495  
  1496    		var callHook = function callHook() {
  1497    			var promise = hook.call(_this.testEnvironment, _this.assert);
  1498    			_this.resolvePromise(promise, hookName);
  1499    		};
  1500  
  1501    		var runHook = function runHook() {
  1502    			if (hookName === "before") {
  1503    				if (hookOwner.unskippedTestsRun !== 0) {
  1504    					return;
  1505    				}
  1506  
  1507    				_this.preserveEnvironment = true;
  1508    			}
  1509  
  1510    			if (hookName === "after" && hookOwner.unskippedTestsRun !== numberOfUnskippedTests(hookOwner) - 1 && config.queue.length > 2) {
  1511    				return;
  1512    			}
  1513  
  1514    			config.current = _this;
  1515    			if (config.notrycatch) {
  1516    				callHook();
  1517    				return;
  1518    			}
  1519    			try {
  1520    				callHook();
  1521    			} catch (error) {
  1522    				_this.pushFailure(hookName + " failed on " + _this.testName + ": " + (error.message || error), extractStacktrace(error, 0));
  1523    			}
  1524    		};
  1525  
  1526    		return runHook;
  1527    	},
  1528  
  1529  
  1530    	// Currently only used for module level hooks, can be used to add global level ones
  1531    	hooks: function hooks(handler) {
  1532    		var hooks = [];
  1533  
  1534    		function processHooks(test, module) {
  1535    			if (module.parentModule) {
  1536    				processHooks(test, module.parentModule);
  1537    			}
  1538  
  1539    			if (module.hooks[handler].length) {
  1540    				for (var i = 0; i < module.hooks[handler].length; i++) {
  1541    					hooks.push(test.queueHook(module.hooks[handler][i], handler, module));
  1542    				}
  1543    			}
  1544    		}
  1545  
  1546    		// Hooks are ignored on skipped tests
  1547    		if (!this.skip) {
  1548    			processHooks(this, this.module);
  1549    		}
  1550  
  1551    		return hooks;
  1552    	},
  1553  
  1554  
  1555    	finish: function finish() {
  1556    		config.current = this;
  1557    		if (config.requireExpects && this.expected === null) {
  1558    			this.pushFailure("Expected number of assertions to be defined, but expect() was " + "not called.", this.stack);
  1559    		} else if (this.expected !== null && this.expected !== this.assertions.length) {
  1560    			this.pushFailure("Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack);
  1561    		} else if (this.expected === null && !this.assertions.length) {
  1562    			this.pushFailure("Expected at least one assertion, but none were run - call " + "expect(0) to accept zero assertions.", this.stack);
  1563    		}
  1564  
  1565    		var i,
  1566    		    module = this.module,
  1567    		    moduleName = module.name,
  1568    		    testName = this.testName,
  1569    		    skipped = !!this.skip,
  1570    		    todo = !!this.todo,
  1571    		    bad = 0,
  1572    		    storage = config.storage;
  1573  
  1574    		this.runtime = now() - this.started;
  1575  
  1576    		config.stats.all += this.assertions.length;
  1577    		module.stats.all += this.assertions.length;
  1578  
  1579    		for (i = 0; i < this.assertions.length; i++) {
  1580    			if (!this.assertions[i].result) {
  1581    				bad++;
  1582    				config.stats.bad++;
  1583    				module.stats.bad++;
  1584    			}
  1585    		}
  1586  
  1587    		notifyTestsRan(module, skipped);
  1588  
  1589    		// Store result when possible
  1590    		if (storage) {
  1591    			if (bad) {
  1592    				storage.setItem("qunit-test-" + moduleName + "-" + testName, bad);
  1593    			} else {
  1594    				storage.removeItem("qunit-test-" + moduleName + "-" + testName);
  1595    			}
  1596    		}
  1597  
  1598    		// After emitting the js-reporters event we cleanup the assertion data to
  1599    		// avoid leaking it. It is not used by the legacy testDone callbacks.
  1600    		emit("testEnd", this.testReport.end(true));
  1601    		this.testReport.slimAssertions();
  1602  
  1603    		runLoggingCallbacks("testDone", {
  1604    			name: testName,
  1605    			module: moduleName,
  1606    			skipped: skipped,
  1607    			todo: todo,
  1608    			failed: bad,
  1609    			passed: this.assertions.length - bad,
  1610    			total: this.assertions.length,
  1611    			runtime: skipped ? 0 : this.runtime,
  1612  
  1613    			// HTML Reporter use
  1614    			assertions: this.assertions,
  1615    			testId: this.testId,
  1616  
  1617    			// Source of Test
  1618    			source: this.stack
  1619    		});
  1620  
  1621    		if (module.testsRun === numberOfTests(module)) {
  1622    			logSuiteEnd(module);
  1623  
  1624    			// Check if the parent modules, iteratively, are done. If that the case,
  1625    			// we emit the `suiteEnd` event and trigger `moduleDone` callback.
  1626    			var parent = module.parentModule;
  1627    			while (parent && parent.testsRun === numberOfTests(parent)) {
  1628    				logSuiteEnd(parent);
  1629    				parent = parent.parentModule;
  1630    			}
  1631    		}
  1632  
  1633    		config.current = undefined;
  1634  
  1635    		function logSuiteEnd(module) {
  1636    			emit("suiteEnd", module.suiteReport.end(true));
  1637    			runLoggingCallbacks("moduleDone", {
  1638    				name: module.name,
  1639    				tests: module.tests,
  1640    				failed: module.stats.bad,
  1641    				passed: module.stats.all - module.stats.bad,
  1642    				total: module.stats.all,
  1643    				runtime: now() - module.stats.started
  1644    			});
  1645    		}
  1646    	},
  1647  
  1648    	preserveTestEnvironment: function preserveTestEnvironment() {
  1649    		if (this.preserveEnvironment) {
  1650    			this.module.testEnvironment = this.testEnvironment;
  1651    			this.testEnvironment = extend({}, this.module.testEnvironment);
  1652    		}
  1653    	},
  1654  
  1655    	queue: function queue() {
  1656    		var test = this;
  1657  
  1658    		if (!this.valid()) {
  1659    			return;
  1660    		}
  1661  
  1662    		function runTest() {
  1663  
  1664    			// Each of these can by async
  1665    			ProcessingQueue.addImmediate([function () {
  1666    				test.before();
  1667    			}, test.hooks("before"), function () {
  1668    				test.preserveTestEnvironment();
  1669    			}, test.hooks("beforeEach"), function () {
  1670    				test.run();
  1671    			}, test.hooks("afterEach").reverse(), test.hooks("after").reverse(), function () {
  1672    				test.after();
  1673    			}, function () {
  1674    				test.finish();
  1675    			}]);
  1676    		}
  1677  
  1678    		var previousFailCount = config.storage && +config.storage.getItem("qunit-test-" + this.module.name + "-" + this.testName);
  1679  
  1680    		// Prioritize previously failed tests, detected from storage
  1681    		var prioritize = config.reorder && !!previousFailCount;
  1682  
  1683    		this.previousFailure = !!previousFailCount;
  1684  
  1685    		ProcessingQueue.add(runTest, prioritize, config.seed);
  1686  
  1687    		// If the queue has already finished, we manually process the new test
  1688    		if (ProcessingQueue.finished) {
  1689    			ProcessingQueue.advance();
  1690    		}
  1691    	},
  1692  
  1693  
  1694    	pushResult: function pushResult(resultInfo) {
  1695    		if (this !== config.current) {
  1696    			throw new Error("Assertion occurred after test had finished.");
  1697    		}
  1698  
  1699    		// Destructure of resultInfo = { result, actual, expected, message, negative }
  1700    		var source,
  1701    		    details = {
  1702    			module: this.module.name,
  1703    			name: this.testName,
  1704    			result: resultInfo.result,
  1705    			message: resultInfo.message,
  1706    			actual: resultInfo.actual,
  1707    			testId: this.testId,
  1708    			negative: resultInfo.negative || false,
  1709    			runtime: now() - this.started,
  1710    			todo: !!this.todo
  1711    		};
  1712  
  1713    		if (hasOwn.call(resultInfo, "expected")) {
  1714    			details.expected = resultInfo.expected;
  1715    		}
  1716  
  1717    		if (!resultInfo.result) {
  1718    			source = resultInfo.source || sourceFromStacktrace();
  1719  
  1720    			if (source) {
  1721    				details.source = source;
  1722    			}
  1723    		}
  1724  
  1725    		this.logAssertion(details);
  1726  
  1727    		this.assertions.push({
  1728    			result: !!resultInfo.result,
  1729    			message: resultInfo.message
  1730    		});
  1731    	},
  1732  
  1733    	pushFailure: function pushFailure(message, source, actual) {
  1734    		if (!(this instanceof Test)) {
  1735    			throw new Error("pushFailure() assertion outside test context, was " + sourceFromStacktrace(2));
  1736    		}
  1737  
  1738    		this.pushResult({
  1739    			result: false,
  1740    			message: message || "error",
  1741    			actual: actual || null,
  1742    			source: source
  1743    		});
  1744    	},
  1745  
  1746    	/**
  1747      * Log assertion details using both the old QUnit.log interface and
  1748      * QUnit.on( "assertion" ) interface.
  1749      *
  1750      * @private
  1751      */
  1752    	logAssertion: function logAssertion(details) {
  1753    		runLoggingCallbacks("log", details);
  1754  
  1755    		var assertion = {
  1756    			passed: details.result,
  1757    			actual: details.actual,
  1758    			expected: details.expected,
  1759    			message: details.message,
  1760    			stack: details.source,
  1761    			todo: details.todo
  1762    		};
  1763    		this.testReport.pushAssertion(assertion);
  1764    		emit("assertion", assertion);
  1765    	},
  1766  
  1767  
  1768    	resolvePromise: function resolvePromise(promise, phase) {
  1769    		var then,
  1770    		    resume,
  1771    		    message,
  1772    		    test = this;
  1773    		if (promise != null) {
  1774    			then = promise.then;
  1775    			if (objectType(then) === "function") {
  1776    				resume = internalStop(test);
  1777    				if (config.notrycatch) {
  1778    					then.call(promise, function () {
  1779    						resume();
  1780    					});
  1781    				} else {
  1782    					then.call(promise, function () {
  1783    						resume();
  1784    					}, function (error) {
  1785    						message = "Promise rejected " + (!phase ? "during" : phase.replace(/Each$/, "")) + " \"" + test.testName + "\": " + (error && error.message || error);
  1786    						test.pushFailure(message, extractStacktrace(error, 0));
  1787  
  1788    						// Else next test will carry the responsibility
  1789    						saveGlobal();
  1790  
  1791    						// Unblock
  1792    						resume();
  1793    					});
  1794    				}
  1795    			}
  1796    		}
  1797    	},
  1798  
  1799    	valid: function valid() {
  1800    		var filter = config.filter,
  1801    		    regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec(filter),
  1802    		    module = config.module && config.module.toLowerCase(),
  1803    		    fullName = this.module.name + ": " + this.testName;
  1804  
  1805    		function moduleChainNameMatch(testModule) {
  1806    			var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;
  1807    			if (testModuleName === module) {
  1808    				return true;
  1809    			} else if (testModule.parentModule) {
  1810    				return moduleChainNameMatch(testModule.parentModule);
  1811    			} else {
  1812    				return false;
  1813    			}
  1814    		}
  1815  
  1816    		function moduleChainIdMatch(testModule) {
  1817    			return inArray(testModule.moduleId, config.moduleId) || testModule.parentModule && moduleChainIdMatch(testModule.parentModule);
  1818    		}
  1819  
  1820    		// Internally-generated tests are always valid
  1821    		if (this.callback && this.callback.validTest) {
  1822    			return true;
  1823    		}
  1824  
  1825    		if (config.moduleId && config.moduleId.length > 0 && !moduleChainIdMatch(this.module)) {
  1826  
  1827    			return false;
  1828    		}
  1829  
  1830    		if (config.testId && config.testId.length > 0 && !inArray(this.testId, config.testId)) {
  1831  
  1832    			return false;
  1833    		}
  1834  
  1835    		if (module && !moduleChainNameMatch(this.module)) {
  1836    			return false;
  1837    		}
  1838  
  1839    		if (!filter) {
  1840    			return true;
  1841    		}
  1842  
  1843    		return regexFilter ? this.regexFilter(!!regexFilter[1], regexFilter[2], regexFilter[3], fullName) : this.stringFilter(filter, fullName);
  1844    	},
  1845  
  1846    	regexFilter: function regexFilter(exclude, pattern, flags, fullName) {
  1847    		var regex = new RegExp(pattern, flags);
  1848    		var match = regex.test(fullName);
  1849  
  1850    		return match !== exclude;
  1851    	},
  1852  
  1853    	stringFilter: function stringFilter(filter, fullName) {
  1854    		filter = filter.toLowerCase();
  1855    		fullName = fullName.toLowerCase();
  1856  
  1857    		var include = filter.charAt(0) !== "!";
  1858    		if (!include) {
  1859    			filter = filter.slice(1);
  1860    		}
  1861  
  1862    		// If the filter matches, we need to honour include
  1863    		if (fullName.indexOf(filter) !== -1) {
  1864    			return include;
  1865    		}
  1866  
  1867    		// Otherwise, do the opposite
  1868    		return !include;
  1869    	}
  1870    };
  1871  
  1872    function pushFailure() {
  1873    	if (!config.current) {
  1874    		throw new Error("pushFailure() assertion outside test context, in " + sourceFromStacktrace(2));
  1875    	}
  1876  
  1877    	// Gets current test obj
  1878    	var currentTest = config.current;
  1879  
  1880    	return currentTest.pushFailure.apply(currentTest, arguments);
  1881    }
  1882  
  1883    function saveGlobal() {
  1884    	config.pollution = [];
  1885  
  1886    	if (config.noglobals) {
  1887    		for (var key in global$1) {
  1888    			if (hasOwn.call(global$1, key)) {
  1889  
  1890    				// In Opera sometimes DOM element ids show up here, ignore them
  1891    				if (/^qunit-test-output/.test(key)) {
  1892    					continue;
  1893    				}
  1894    				config.pollution.push(key);
  1895    			}
  1896    		}
  1897    	}
  1898    }
  1899  
  1900    function checkPollution() {
  1901    	var newGlobals,
  1902    	    deletedGlobals,
  1903    	    old = config.pollution;
  1904  
  1905    	saveGlobal();
  1906  
  1907    	newGlobals = diff(config.pollution, old);
  1908    	if (newGlobals.length > 0) {
  1909    		pushFailure("Introduced global variable(s): " + newGlobals.join(", "));
  1910    	}
  1911  
  1912    	deletedGlobals = diff(old, config.pollution);
  1913    	if (deletedGlobals.length > 0) {
  1914    		pushFailure("Deleted global variable(s): " + deletedGlobals.join(", "));
  1915    	}
  1916    }
  1917  
  1918    // Will be exposed as QUnit.test
  1919    function test(testName, callback) {
  1920    	if (focused$1) {
  1921    		return;
  1922    	}
  1923  
  1924    	var newTest = new Test({
  1925    		testName: testName,
  1926    		callback: callback
  1927    	});
  1928  
  1929    	newTest.queue();
  1930    }
  1931  
  1932    function todo(testName, callback) {
  1933    	if (focused$1) {
  1934    		return;
  1935    	}
  1936  
  1937    	var newTest = new Test({
  1938    		testName: testName,
  1939    		callback: callback,
  1940    		todo: true
  1941    	});
  1942  
  1943    	newTest.queue();
  1944    }
  1945  
  1946    // Will be exposed as QUnit.skip
  1947    function skip(testName) {
  1948    	if (focused$1) {
  1949    		return;
  1950    	}
  1951  
  1952    	var test = new Test({
  1953    		testName: testName,
  1954    		skip: true
  1955    	});
  1956  
  1957    	test.queue();
  1958    }
  1959  
  1960    // Will be exposed as QUnit.only
  1961    function only(testName, callback) {
  1962    	if (focused$1) {
  1963    		return;
  1964    	}
  1965  
  1966    	config.queue.length = 0;
  1967    	focused$1 = true;
  1968  
  1969    	var newTest = new Test({
  1970    		testName: testName,
  1971    		callback: callback
  1972    	});
  1973  
  1974    	newTest.queue();
  1975    }
  1976  
  1977    // Put a hold on processing and return a function that will release it.
  1978    function internalStop(test) {
  1979    	test.semaphore += 1;
  1980    	config.blocking = true;
  1981  
  1982    	// Set a recovery timeout, if so configured.
  1983    	if (defined.setTimeout) {
  1984    		var timeoutDuration = void 0;
  1985  
  1986    		if (typeof test.timeout === "number") {
  1987    			timeoutDuration = test.timeout;
  1988    		} else if (typeof config.testTimeout === "number") {
  1989    			timeoutDuration = config.testTimeout;
  1990    		}
  1991  
  1992    		if (typeof timeoutDuration === "number" && timeoutDuration > 0) {
  1993    			clearTimeout(config.timeout);
  1994    			config.timeout = setTimeout(function () {
  1995    				pushFailure("Test took longer than " + timeoutDuration + "ms; test timed out.", sourceFromStacktrace(2));
  1996    				internalRecover(test);
  1997    			}, timeoutDuration);
  1998    		}
  1999    	}
  2000  
  2001    	var released = false;
  2002    	return function resume() {
  2003    		if (released) {
  2004    			return;
  2005    		}
  2006  
  2007    		released = true;
  2008    		test.semaphore -= 1;
  2009    		internalStart(test);
  2010    	};
  2011    }
  2012  
  2013    // Forcefully release all processing holds.
  2014    function internalRecover(test) {
  2015    	test.semaphore = 0;
  2016    	internalStart(test);
  2017    }
  2018  
  2019    // Release a processing hold, scheduling a resumption attempt if no holds remain.
  2020    function internalStart(test) {
  2021  
  2022    	// If semaphore is non-numeric, throw error
  2023    	if (isNaN(test.semaphore)) {
  2024    		test.semaphore = 0;
  2025  
  2026    		pushFailure("Invalid value on test.semaphore", sourceFromStacktrace(2));
  2027    		return;
  2028    	}
  2029  
  2030    	// Don't start until equal number of stop-calls
  2031    	if (test.semaphore > 0) {
  2032    		return;
  2033    	}
  2034  
  2035    	// Throw an Error if start is called more often than stop
  2036    	if (test.semaphore < 0) {
  2037    		test.semaphore = 0;
  2038  
  2039    		pushFailure("Tried to restart test while already started (test's semaphore was 0 already)", sourceFromStacktrace(2));
  2040    		return;
  2041    	}
  2042  
  2043    	// Add a slight delay to allow more assertions etc.
  2044    	if (defined.setTimeout) {
  2045    		if (config.timeout) {
  2046    			clearTimeout(config.timeout);
  2047    		}
  2048    		config.timeout = setTimeout(function () {
  2049    			if (test.semaphore > 0) {
  2050    				return;
  2051    			}
  2052  
  2053    			if (config.timeout) {
  2054    				clearTimeout(config.timeout);
  2055    			}
  2056  
  2057    			begin();
  2058    		});
  2059    	} else {
  2060    		begin();
  2061    	}
  2062    }
  2063  
  2064    function collectTests(module) {
  2065    	var tests = [].concat(module.tests);
  2066    	var modules = [].concat(toConsumableArray(module.childModules));
  2067  
  2068    	// Do a breadth-first traversal of the child modules
  2069    	while (modules.length) {
  2070    		var nextModule = modules.shift();
  2071    		tests.push.apply(tests, nextModule.tests);
  2072    		modules.push.apply(modules, toConsumableArray(nextModule.childModules));
  2073    	}
  2074  
  2075    	return tests;
  2076    }
  2077  
  2078    function numberOfTests(module) {
  2079    	return collectTests(module).length;
  2080    }
  2081  
  2082    function numberOfUnskippedTests(module) {
  2083    	return collectTests(module).filter(function (test) {
  2084    		return !test.skip;
  2085    	}).length;
  2086    }
  2087  
  2088    function notifyTestsRan(module, skipped) {
  2089    	module.testsRun++;
  2090    	if (!skipped) {
  2091    		module.unskippedTestsRun++;
  2092    	}
  2093    	while (module = module.parentModule) {
  2094    		module.testsRun++;
  2095    		if (!skipped) {
  2096    			module.unskippedTestsRun++;
  2097    		}
  2098    	}
  2099    }
  2100  
  2101    /**
  2102     * Returns a function that proxies to the given method name on the globals
  2103     * console object. The proxy will also detect if the console doesn't exist and
  2104     * will appropriately no-op. This allows support for IE9, which doesn't have a
  2105     * console if the developer tools are not open.
  2106     */
  2107    function consoleProxy(method) {
  2108    	return function () {
  2109    		if (console) {
  2110    			console[method].apply(console, arguments);
  2111    		}
  2112    	};
  2113    }
  2114  
  2115    var Logger = {
  2116    	warn: consoleProxy("warn")
  2117    };
  2118  
  2119    var Assert = function () {
  2120    	function Assert(testContext) {
  2121    		classCallCheck(this, Assert);
  2122  
  2123    		this.test = testContext;
  2124    	}
  2125  
  2126    	// Assert helpers
  2127  
  2128    	createClass(Assert, [{
  2129    		key: "timeout",
  2130    		value: function timeout(duration) {
  2131    			if (typeof duration !== "number") {
  2132    				throw new Error("You must pass a number as the duration to assert.timeout");
  2133    			}
  2134  
  2135    			this.test.timeout = duration;
  2136    		}
  2137  
  2138    		// Documents a "step", which is a string value, in a test as a passing assertion
  2139  
  2140    	}, {
  2141    		key: "step",
  2142    		value: function step(message) {
  2143    			var result = !!message;
  2144  
  2145    			this.test.steps.push(message);
  2146  
  2147    			return this.pushResult({
  2148    				result: result,
  2149    				message: message || "You must provide a message to assert.step"
  2150    			});
  2151    		}
  2152  
  2153    		// Verifies the steps in a test match a given array of string values
  2154  
  2155    	}, {
  2156    		key: "verifySteps",
  2157    		value: function verifySteps(steps, message) {
  2158    			this.deepEqual(this.test.steps, steps, message);
  2159    			this.test.steps.length = 0;
  2160    		}
  2161  
  2162    		// Specify the number of expected assertions to guarantee that failed test
  2163    		// (no assertions are run at all) don't slip through.
  2164  
  2165    	}, {
  2166    		key: "expect",
  2167    		value: function expect(asserts) {
  2168    			if (arguments.length === 1) {
  2169    				this.test.expected = asserts;
  2170    			} else {
  2171    				return this.test.expected;
  2172    			}
  2173    		}
  2174  
  2175    		// Put a hold on processing and return a function that will release it a maximum of once.
  2176  
  2177    	}, {
  2178    		key: "async",
  2179    		value: function async(count) {
  2180    			var test$$1 = this.test;
  2181  
  2182    			var popped = false,
  2183    			    acceptCallCount = count;
  2184  
  2185    			if (typeof acceptCallCount === "undefined") {
  2186    				acceptCallCount = 1;
  2187    			}
  2188  
  2189    			var resume = internalStop(test$$1);
  2190  
  2191    			return function done() {
  2192    				if (config.current !== test$$1) {
  2193    					throw Error("assert.async callback called after test finished.");
  2194    				}
  2195  
  2196    				if (popped) {
  2197    					test$$1.pushFailure("Too many calls to the `assert.async` callback", sourceFromStacktrace(2));
  2198    					return;
  2199    				}
  2200  
  2201    				acceptCallCount -= 1;
  2202    				if (acceptCallCount > 0) {
  2203    					return;
  2204    				}
  2205  
  2206    				popped = true;
  2207    				resume();
  2208    			};
  2209    		}
  2210  
  2211    		// Exports test.push() to the user API
  2212    		// Alias of pushResult.
  2213  
  2214    	}, {
  2215    		key: "push",
  2216    		value: function push(result, actual, expected, message, negative) {
  2217    			Logger.warn("assert.push is deprecated and will be removed in QUnit 3.0." + " Please use assert.pushResult instead (https://api.qunitjs.com/assert/pushResult).");
  2218  
  2219    			var currentAssert = this instanceof Assert ? this : config.current.assert;
  2220    			return currentAssert.pushResult({
  2221    				result: result,
  2222    				actual: actual,
  2223    				expected: expected,
  2224    				message: message,
  2225    				negative: negative
  2226    			});
  2227    		}
  2228    	}, {
  2229    		key: "pushResult",
  2230    		value: function pushResult(resultInfo) {
  2231  
  2232    			// Destructure of resultInfo = { result, actual, expected, message, negative }
  2233    			var assert = this;
  2234    			var currentTest = assert instanceof Assert && assert.test || config.current;
  2235  
  2236    			// Backwards compatibility fix.
  2237    			// Allows the direct use of global exported assertions and QUnit.assert.*
  2238    			// Although, it's use is not recommended as it can leak assertions
  2239    			// to other tests from async tests, because we only get a reference to the current test,
  2240    			// not exactly the test where assertion were intended to be called.
  2241    			if (!currentTest) {
  2242    				throw new Error("assertion outside test context, in " + sourceFromStacktrace(2));
  2243    			}
  2244  
  2245    			if (!(assert instanceof Assert)) {
  2246    				assert = currentTest.assert;
  2247    			}
  2248  
  2249    			return assert.test.pushResult(resultInfo);
  2250    		}
  2251    	}, {
  2252    		key: "ok",
  2253    		value: function ok(result, message) {
  2254    			if (!message) {
  2255    				message = result ? "okay" : "failed, expected argument to be truthy, was: " + dump.parse(result);
  2256    			}
  2257  
  2258    			this.pushResult({
  2259    				result: !!result,
  2260    				actual: result,
  2261    				expected: true,
  2262    				message: message
  2263    			});
  2264    		}
  2265    	}, {
  2266    		key: "notOk",
  2267    		value: function notOk(result, message) {
  2268    			if (!message) {
  2269    				message = !result ? "okay" : "failed, expected argument to be falsy, was: " + dump.parse(result);
  2270    			}
  2271  
  2272    			this.pushResult({
  2273    				result: !result,
  2274    				actual: result,
  2275    				expected: false,
  2276    				message: message
  2277    			});
  2278    		}
  2279    	}, {
  2280    		key: "equal",
  2281    		value: function equal(actual, expected, message) {
  2282  
  2283    			// eslint-disable-next-line eqeqeq
  2284    			var result = expected == actual;
  2285  
  2286    			this.pushResult({
  2287    				result: result,
  2288    				actual: actual,
  2289    				expected: expected,
  2290    				message: message
  2291    			});
  2292    		}
  2293    	}, {
  2294    		key: "notEqual",
  2295    		value: function notEqual(actual, expected, message) {
  2296  
  2297    			// eslint-disable-next-line eqeqeq
  2298    			var result = expected != actual;
  2299  
  2300    			this.pushResult({
  2301    				result: result,
  2302    				actual: actual,
  2303    				expected: expected,
  2304    				message: message,
  2305    				negative: true
  2306    			});
  2307    		}
  2308    	}, {
  2309    		key: "propEqual",
  2310    		value: function propEqual(actual, expected, message) {
  2311    			actual = objectValues(actual);
  2312    			expected = objectValues(expected);
  2313  
  2314    			this.pushResult({
  2315    				result: equiv(actual, expected),
  2316    				actual: actual,
  2317    				expected: expected,
  2318    				message: message
  2319    			});
  2320    		}
  2321    	}, {
  2322    		key: "notPropEqual",
  2323    		value: function notPropEqual(actual, expected, message) {
  2324    			actual = objectValues(actual);
  2325    			expected = objectValues(expected);
  2326  
  2327    			this.pushResult({
  2328    				result: !equiv(actual, expected),
  2329    				actual: actual,
  2330    				expected: expected,
  2331    				message: message,
  2332    				negative: true
  2333    			});
  2334    		}
  2335    	}, {
  2336    		key: "deepEqual",
  2337    		value: function deepEqual(actual, expected, message) {
  2338    			this.pushResult({
  2339    				result: equiv(actual, expected),
  2340    				actual: actual,
  2341    				expected: expected,
  2342    				message: message
  2343    			});
  2344    		}
  2345    	}, {
  2346    		key: "notDeepEqual",
  2347    		value: function notDeepEqual(actual, expected, message) {
  2348    			this.pushResult({
  2349    				result: !equiv(actual, expected),
  2350    				actual: actual,
  2351    				expected: expected,
  2352    				message: message,
  2353    				negative: true
  2354    			});
  2355    		}
  2356    	}, {
  2357    		key: "strictEqual",
  2358    		value: function strictEqual(actual, expected, message) {
  2359    			this.pushResult({
  2360    				result: expected === actual,
  2361    				actual: actual,
  2362    				expected: expected,
  2363    				message: message
  2364    			});
  2365    		}
  2366    	}, {
  2367    		key: "notStrictEqual",
  2368    		value: function notStrictEqual(actual, expected, message) {
  2369    			this.pushResult({
  2370    				result: expected !== actual,
  2371    				actual: actual,
  2372    				expected: expected,
  2373    				message: message,
  2374    				negative: true
  2375    			});
  2376    		}
  2377    	}, {
  2378    		key: "throws",
  2379    		value: function throws(block, expected, message) {
  2380    			var actual = void 0,
  2381    			    result = false;
  2382  
  2383    			var currentTest = this instanceof Assert && this.test || config.current;
  2384  
  2385    			// 'expected' is optional unless doing string comparison
  2386    			if (objectType(expected) === "string") {
  2387    				if (message == null) {
  2388    					message = expected;
  2389    					expected = null;
  2390    				} else {
  2391    					throw new Error("throws/raises does not accept a string value for the expected argument.\n" + "Use a non-string object value (e.g. regExp) instead if it's necessary.");
  2392    				}
  2393    			}
  2394  
  2395    			currentTest.ignoreGlobalErrors = true;
  2396    			try {
  2397    				block.call(currentTest.testEnvironment);
  2398    			} catch (e) {
  2399    				actual = e;
  2400    			}
  2401    			currentTest.ignoreGlobalErrors = false;
  2402  
  2403    			if (actual) {
  2404    				var expectedType = objectType(expected);
  2405  
  2406    				// We don't want to validate thrown error
  2407    				if (!expected) {
  2408    					result = true;
  2409    					expected = null;
  2410  
  2411    					// Expected is a regexp
  2412    				} else if (expectedType === "regexp") {
  2413    					result = expected.test(errorString(actual));
  2414  
  2415    					// Expected is a constructor, maybe an Error constructor
  2416    				} else if (expectedType === "function" && actual instanceof expected) {
  2417    					result = true;
  2418  
  2419    					// Expected is an Error object
  2420    				} else if (expectedType === "object") {
  2421    					result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
  2422  
  2423    					// Expected is a validation function which returns true if validation passed
  2424    				} else if (expectedType === "function" && expected.call({}, actual) === true) {
  2425    					expected = null;
  2426    					result = true;
  2427    				}
  2428    			}
  2429  
  2430    			currentTest.assert.pushResult({
  2431    				result: result,
  2432    				actual: actual,
  2433    				expected: expected,
  2434    				message: message
  2435    			});
  2436    		}
  2437    	}, {
  2438    		key: "rejects",
  2439    		value: function rejects(promise, expected, message) {
  2440    			var result = false;
  2441  
  2442    			var currentTest = this instanceof Assert && this.test || config.current;
  2443  
  2444    			// 'expected' is optional unless doing string comparison
  2445    			if (objectType(expected) === "string") {
  2446    				if (message === undefined) {
  2447    					message = expected;
  2448    					expected = undefined;
  2449    				} else {
  2450    					message = "assert.rejects does not accept a string value for the expected " + "argument.\nUse a non-string object value (e.g. validator function) instead " + "if necessary.";
  2451  
  2452    					currentTest.assert.pushResult({
  2453    						result: false,
  2454    						message: message
  2455    					});
  2456  
  2457    					return;
  2458    				}
  2459    			}
  2460  
  2461    			var then = promise && promise.then;
  2462    			if (objectType(then) !== "function") {
  2463    				var _message = "The value provided to `assert.rejects` in " + "\"" + currentTest.testName + "\" was not a promise.";
  2464  
  2465    				currentTest.assert.pushResult({
  2466    					result: false,
  2467    					message: _message,
  2468    					actual: promise
  2469    				});
  2470  
  2471    				return;
  2472    			}
  2473  
  2474    			var done = this.async();
  2475  
  2476    			return then.call(promise, function handleFulfillment() {
  2477    				var message = "The promise returned by the `assert.rejects` callback in " + "\"" + currentTest.testName + "\" did not reject.";
  2478  
  2479    				currentTest.assert.pushResult({
  2480    					result: false,
  2481    					message: message,
  2482    					actual: promise
  2483    				});
  2484  
  2485    				done();
  2486    			}, function handleRejection(actual) {
  2487    				if (actual) {
  2488    					var expectedType = objectType(expected);
  2489  
  2490    					// We don't want to validate
  2491    					if (expected === undefined) {
  2492    						result = true;
  2493    						expected = null;
  2494  
  2495    						// Expected is a regexp
  2496    					} else if (expectedType === "regexp") {
  2497    						result = expected.test(errorString(actual));
  2498  
  2499    						// Expected is a constructor, maybe an Error constructor
  2500    					} else if (expectedType === "function" && actual instanceof expected) {
  2501    						result = true;
  2502  
  2503    						// Expected is an Error object
  2504    					} else if (expectedType === "object") {
  2505    						result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
  2506  
  2507    						// Expected is a validation function which returns true if validation passed
  2508    					} else {
  2509    						if (expectedType === "function") {
  2510    							result = expected.call({}, actual) === true;
  2511    							expected = null;
  2512  
  2513    							// Expected is some other invalid type
  2514    						} else {
  2515    							result = false;
  2516    							message = "invalid expected value provided to `assert.rejects` " + "callback in \"" + currentTest.testName + "\": " + expectedType + ".";
  2517    						}
  2518    					}
  2519    				}
  2520  
  2521    				currentTest.assert.pushResult({
  2522    					result: result,
  2523    					actual: actual,
  2524    					expected: expected,
  2525    					message: message
  2526    				});
  2527  
  2528    				done();
  2529    			});
  2530    		}
  2531    	}]);
  2532    	return Assert;
  2533    }();
  2534  
  2535    // Provide an alternative to assert.throws(), for environments that consider throws a reserved word
  2536    // Known to us are: Closure Compiler, Narwhal
  2537    // eslint-disable-next-line dot-notation
  2538  
  2539  
  2540    Assert.prototype.raises = Assert.prototype["throws"];
  2541  
  2542    /**
  2543     * Converts an error into a simple string for comparisons.
  2544     *
  2545     * @param {Error} error
  2546     * @return {String}
  2547     */
  2548    function errorString(error) {
  2549    	var resultErrorString = error.toString();
  2550  
  2551    	if (resultErrorString.substring(0, 7) === "[object") {
  2552    		var name = error.name ? error.name.toString() : "Error";
  2553    		var message = error.message ? error.message.toString() : "";
  2554  
  2555    		if (name && message) {
  2556    			return name + ": " + message;
  2557    		} else if (name) {
  2558    			return name;
  2559    		} else if (message) {
  2560    			return message;
  2561    		} else {
  2562    			return "Error";
  2563    		}
  2564    	} else {
  2565    		return resultErrorString;
  2566    	}
  2567    }
  2568  
  2569    /* global module, exports, define */
  2570    function exportQUnit(QUnit) {
  2571  
  2572    	if (defined.document) {
  2573  
  2574    		// QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined.
  2575    		if (window.QUnit && window.QUnit.version) {
  2576    			throw new Error("QUnit has already been defined.");
  2577    		}
  2578  
  2579    		window.QUnit = QUnit;
  2580    	}
  2581  
  2582    	// For nodejs
  2583    	if (typeof module !== "undefined" && module && module.exports) {
  2584    		module.exports = QUnit;
  2585  
  2586    		// For consistency with CommonJS environments' exports
  2587    		module.exports.QUnit = QUnit;
  2588    	}
  2589  
  2590    	// For CommonJS with exports, but without module.exports, like Rhino
  2591    	if (typeof exports !== "undefined" && exports) {
  2592    		exports.QUnit = QUnit;
  2593    	}
  2594  
  2595    	if (typeof define === "function" && define.amd) {
  2596    		define(function () {
  2597    			return QUnit;
  2598    		});
  2599    		QUnit.config.autostart = false;
  2600    	}
  2601  
  2602    	// For Web/Service Workers
  2603    	if (self$1 && self$1.WorkerGlobalScope && self$1 instanceof self$1.WorkerGlobalScope) {
  2604    		self$1.QUnit = QUnit;
  2605    	}
  2606    }
  2607  
  2608    var SuiteReport = function () {
  2609    	function SuiteReport(name, parentSuite) {
  2610    		classCallCheck(this, SuiteReport);
  2611  
  2612    		this.name = name;
  2613    		this.fullName = parentSuite ? parentSuite.fullName.concat(name) : [];
  2614  
  2615    		this.tests = [];
  2616    		this.childSuites = [];
  2617  
  2618    		if (parentSuite) {
  2619    			parentSuite.pushChildSuite(this);
  2620    		}
  2621    	}
  2622  
  2623    	createClass(SuiteReport, [{
  2624    		key: "start",
  2625    		value: function start(recordTime) {
  2626    			if (recordTime) {
  2627    				this._startTime = Date.now();
  2628    			}
  2629  
  2630    			return {
  2631    				name: this.name,
  2632    				fullName: this.fullName.slice(),
  2633    				tests: this.tests.map(function (test) {
  2634    					return test.start();
  2635    				}),
  2636    				childSuites: this.childSuites.map(function (suite) {
  2637    					return suite.start();
  2638    				}),
  2639    				testCounts: {
  2640    					total: this.getTestCounts().total
  2641    				}
  2642    			};
  2643    		}
  2644    	}, {
  2645    		key: "end",
  2646    		value: function end(recordTime) {
  2647    			if (recordTime) {
  2648    				this._endTime = Date.now();
  2649    			}
  2650  
  2651    			return {
  2652    				name: this.name,
  2653    				fullName: this.fullName.slice(),
  2654    				tests: this.tests.map(function (test) {
  2655    					return test.end();
  2656    				}),
  2657    				childSuites: this.childSuites.map(function (suite) {
  2658    					return suite.end();
  2659    				}),
  2660    				testCounts: this.getTestCounts(),
  2661    				runtime: this.getRuntime(),
  2662    				status: this.getStatus()
  2663    			};
  2664    		}
  2665    	}, {
  2666    		key: "pushChildSuite",
  2667    		value: function pushChildSuite(suite) {
  2668    			this.childSuites.push(suite);
  2669    		}
  2670    	}, {
  2671    		key: "pushTest",
  2672    		value: function pushTest(test) {
  2673    			this.tests.push(test);
  2674    		}
  2675    	}, {
  2676    		key: "getRuntime",
  2677    		value: function getRuntime() {
  2678    			return this._endTime - this._startTime;
  2679    		}
  2680    	}, {
  2681    		key: "getTestCounts",
  2682    		value: function getTestCounts() {
  2683    			var counts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { passed: 0, failed: 0, skipped: 0, todo: 0, total: 0 };
  2684  
  2685    			counts = this.tests.reduce(function (counts, test) {
  2686    				if (test.valid) {
  2687    					counts[test.getStatus()]++;
  2688    					counts.total++;
  2689    				}
  2690  
  2691    				return counts;
  2692    			}, counts);
  2693  
  2694    			return this.childSuites.reduce(function (counts, suite) {
  2695    				return suite.getTestCounts(counts);
  2696    			}, counts);
  2697    		}
  2698    	}, {
  2699    		key: "getStatus",
  2700    		value: function getStatus() {
  2701    			var _getTestCounts = this.getTestCounts(),
  2702    			    total = _getTestCounts.total,
  2703    			    failed = _getTestCounts.failed,
  2704    			    skipped = _getTestCounts.skipped,
  2705    			    todo = _getTestCounts.todo;
  2706  
  2707    			if (failed) {
  2708    				return "failed";
  2709    			} else {
  2710    				if (skipped === total) {
  2711    					return "skipped";
  2712    				} else if (todo === total) {
  2713    					return "todo";
  2714    				} else {
  2715    					return "passed";
  2716    				}
  2717    			}
  2718    		}
  2719    	}]);
  2720    	return SuiteReport;
  2721    }();
  2722  
  2723    // Handle an unhandled exception. By convention, returns true if further
  2724    // error handling should be suppressed and false otherwise.
  2725    // In this case, we will only suppress further error handling if the
  2726    // "ignoreGlobalErrors" configuration option is enabled.
  2727    function onError(error) {
  2728    	for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  2729    		args[_key - 1] = arguments[_key];
  2730    	}
  2731  
  2732    	if (config.current) {
  2733    		if (config.current.ignoreGlobalErrors) {
  2734    			return true;
  2735    		}
  2736    		pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args));
  2737    	} else {
  2738    		test("global failure", extend(function () {
  2739    			pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args));
  2740    		}, { validTest: true }));
  2741    	}
  2742  
  2743    	return false;
  2744    }
  2745  
  2746    // Handle an unhandled rejection
  2747    function onUnhandledRejection(reason) {
  2748    	var resultInfo = {
  2749    		result: false,
  2750    		message: reason.message || "error",
  2751    		actual: reason,
  2752    		source: reason.stack || sourceFromStacktrace(3)
  2753    	};
  2754  
  2755    	var currentTest = config.current;
  2756    	if (currentTest) {
  2757    		currentTest.assert.pushResult(resultInfo);
  2758    	} else {
  2759    		test("global failure", extend(function (assert) {
  2760    			assert.pushResult(resultInfo);
  2761    		}, { validTest: true }));
  2762    	}
  2763    }
  2764  
  2765    var focused = false;
  2766    var QUnit = {};
  2767    var globalSuite = new SuiteReport();
  2768  
  2769    // The initial "currentModule" represents the global (or top-level) module that
  2770    // is not explicitly defined by the user, therefore we add the "globalSuite" to
  2771    // it since each module has a suiteReport associated with it.
  2772    config.currentModule.suiteReport = globalSuite;
  2773  
  2774    var moduleStack = [];
  2775    var globalStartCalled = false;
  2776    var runStarted = false;
  2777  
  2778    // Figure out if we're running the tests from a server or not
  2779    QUnit.isLocal = !(defined.document && window.location.protocol !== "file:");
  2780  
  2781    // Expose the current QUnit version
  2782    QUnit.version = "2.5.0";
  2783  
  2784    function createModule(name, testEnvironment, modifiers) {
  2785    	var parentModule = moduleStack.length ? moduleStack.slice(-1)[0] : null;
  2786    	var moduleName = parentModule !== null ? [parentModule.name, name].join(" > ") : name;
  2787    	var parentSuite = parentModule ? parentModule.suiteReport : globalSuite;
  2788  
  2789    	var skip$$1 = parentModule !== null && parentModule.skip || modifiers.skip;
  2790    	var todo$$1 = parentModule !== null && parentModule.todo || modifiers.todo;
  2791  
  2792    	var module = {
  2793    		name: moduleName,
  2794    		parentModule: parentModule,
  2795    		tests: [],
  2796    		moduleId: generateHash(moduleName),
  2797    		testsRun: 0,
  2798    		unskippedTestsRun: 0,
  2799    		childModules: [],
  2800    		suiteReport: new SuiteReport(name, parentSuite),
  2801  
  2802    		// Pass along `skip` and `todo` properties from parent module, in case
  2803    		// there is one, to childs. And use own otherwise.
  2804    		// This property will be used to mark own tests and tests of child suites
  2805    		// as either `skipped` or `todo`.
  2806    		skip: skip$$1,
  2807    		todo: skip$$1 ? false : todo$$1
  2808    	};
  2809  
  2810    	var env = {};
  2811    	if (parentModule) {
  2812    		parentModule.childModules.push(module);
  2813    		extend(env, parentModule.testEnvironment);
  2814    	}
  2815    	extend(env, testEnvironment);
  2816    	module.testEnvironment = env;
  2817  
  2818    	config.modules.push(module);
  2819    	return module;
  2820    }
  2821  
  2822    function processModule(name, options, executeNow) {
  2823    	var modifiers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
  2824  
  2825    	var module = createModule(name, options, modifiers);
  2826  
  2827    	// Move any hooks to a 'hooks' object
  2828    	var testEnvironment = module.testEnvironment;
  2829    	var hooks = module.hooks = {};
  2830  
  2831    	setHookFromEnvironment(hooks, testEnvironment, "before");
  2832    	setHookFromEnvironment(hooks, testEnvironment, "beforeEach");
  2833    	setHookFromEnvironment(hooks, testEnvironment, "afterEach");
  2834    	setHookFromEnvironment(hooks, testEnvironment, "after");
  2835  
  2836    	function setHookFromEnvironment(hooks, environment, name) {
  2837    		var potentialHook = environment[name];
  2838    		hooks[name] = typeof potentialHook === "function" ? [potentialHook] : [];
  2839    		delete environment[name];
  2840    	}
  2841  
  2842    	var moduleFns = {
  2843    		before: setHookFunction(module, "before"),
  2844    		beforeEach: setHookFunction(module, "beforeEach"),
  2845    		afterEach: setHookFunction(module, "afterEach"),
  2846    		after: setHookFunction(module, "after")
  2847    	};
  2848  
  2849    	var currentModule = config.currentModule;
  2850    	if (objectType(executeNow) === "function") {
  2851    		moduleStack.push(module);
  2852    		config.currentModule = module;
  2853    		executeNow.call(module.testEnvironment, moduleFns);
  2854    		moduleStack.pop();
  2855    		module = module.parentModule || currentModule;
  2856    	}
  2857  
  2858    	config.currentModule = module;
  2859    }
  2860  
  2861    // TODO: extract this to a new file alongside its related functions
  2862    function module$1(name, options, executeNow) {
  2863    	if (focused) {
  2864    		return;
  2865    	}
  2866  
  2867    	if (arguments.length === 2) {
  2868    		if (objectType(options) === "function") {
  2869    			executeNow = options;
  2870    			options = undefined;
  2871    		}
  2872    	}
  2873  
  2874    	processModule(name, options, executeNow);
  2875    }
  2876  
  2877    module$1.only = function () {
  2878    	if (focused) {
  2879    		return;
  2880    	}
  2881  
  2882    	config.modules.length = 0;
  2883    	config.queue.length = 0;
  2884  
  2885    	module$1.apply(undefined, arguments);
  2886  
  2887    	focused = true;
  2888    };
  2889  
  2890    module$1.skip = function (name, options, executeNow) {
  2891    	if (focused) {
  2892    		return;
  2893    	}
  2894  
  2895    	if (arguments.length === 2) {
  2896    		if (objectType(options) === "function") {
  2897    			executeNow = options;
  2898    			options = undefined;
  2899    		}
  2900    	}
  2901  
  2902    	processModule(name, options, executeNow, { skip: true });
  2903    };
  2904  
  2905    module$1.todo = function (name, options, executeNow) {
  2906    	if (focused) {
  2907    		return;
  2908    	}
  2909  
  2910    	if (arguments.length === 2) {
  2911    		if (objectType(options) === "function") {
  2912    			executeNow = options;
  2913    			options = undefined;
  2914    		}
  2915    	}
  2916  
  2917    	processModule(name, options, executeNow, { todo: true });
  2918    };
  2919  
  2920    extend(QUnit, {
  2921    	on: on,
  2922  
  2923    	module: module$1,
  2924  
  2925    	test: test,
  2926  
  2927    	todo: todo,
  2928  
  2929    	skip: skip,
  2930  
  2931    	only: only,
  2932  
  2933    	start: function start(count) {
  2934    		var globalStartAlreadyCalled = globalStartCalled;
  2935  
  2936    		if (!config.current) {
  2937    			globalStartCalled = true;
  2938  
  2939    			if (runStarted) {
  2940    				throw new Error("Called start() while test already started running");
  2941    			} else if (globalStartAlreadyCalled || count > 1) {
  2942    				throw new Error("Called start() outside of a test context too many times");
  2943    			} else if (config.autostart) {
  2944    				throw new Error("Called start() outside of a test context when " + "QUnit.config.autostart was true");
  2945    			} else if (!config.pageLoaded) {
  2946  
  2947    				// The page isn't completely loaded yet, so we set autostart and then
  2948    				// load if we're in Node or wait for the browser's load event.
  2949    				config.autostart = true;
  2950  
  2951    				// Starts from Node even if .load was not previously called. We still return
  2952    				// early otherwise we'll wind up "beginning" twice.
  2953    				if (!defined.document) {
  2954    					QUnit.load();
  2955    				}
  2956  
  2957    				return;
  2958    			}
  2959    		} else {
  2960    			throw new Error("QUnit.start cannot be called inside a test context.");
  2961    		}
  2962  
  2963    		scheduleBegin();
  2964    	},
  2965  
  2966    	config: config,
  2967  
  2968    	is: is,
  2969  
  2970    	objectType: objectType,
  2971  
  2972    	extend: extend,
  2973  
  2974    	load: function load() {
  2975    		config.pageLoaded = true;
  2976  
  2977    		// Initialize the configuration options
  2978    		extend(config, {
  2979    			stats: { all: 0, bad: 0 },
  2980    			started: 0,
  2981    			updateRate: 1000,
  2982    			autostart: true,
  2983    			filter: ""
  2984    		}, true);
  2985  
  2986    		if (!runStarted) {
  2987    			config.blocking = false;
  2988  
  2989    			if (config.autostart) {
  2990    				scheduleBegin();
  2991    			}
  2992    		}
  2993    	},
  2994  
  2995    	stack: function stack(offset) {
  2996    		offset = (offset || 0) + 2;
  2997    		return sourceFromStacktrace(offset);
  2998    	},
  2999  
  3000    	onError: onError,
  3001  
  3002    	onUnhandledRejection: onUnhandledRejection
  3003    });
  3004  
  3005    QUnit.pushFailure = pushFailure;
  3006    QUnit.assert = Assert.prototype;
  3007    QUnit.equiv = equiv;
  3008    QUnit.dump = dump;
  3009  
  3010    registerLoggingCallbacks(QUnit);
  3011  
  3012    function scheduleBegin() {
  3013  
  3014    	runStarted = true;
  3015  
  3016    	// Add a slight delay to allow definition of more modules and tests.
  3017    	if (defined.setTimeout) {
  3018    		setTimeout(function () {
  3019    			begin();
  3020    		});
  3021    	} else {
  3022    		begin();
  3023    	}
  3024    }
  3025  
  3026    function begin() {
  3027    	var i,
  3028    	    l,
  3029    	    modulesLog = [];
  3030  
  3031    	// If the test run hasn't officially begun yet
  3032    	if (!config.started) {
  3033  
  3034    		// Record the time of the test run's beginning
  3035    		config.started = now();
  3036  
  3037    		// Delete the loose unnamed module if unused.
  3038    		if (config.modules[0].name === "" && config.modules[0].tests.length === 0) {
  3039    			config.modules.shift();
  3040    		}
  3041  
  3042    		// Avoid unnecessary information by not logging modules' test environments
  3043    		for (i = 0, l = config.modules.length; i < l; i++) {
  3044    			modulesLog.push({
  3045    				name: config.modules[i].name,
  3046    				tests: config.modules[i].tests
  3047    			});
  3048    		}
  3049  
  3050    		// The test run is officially beginning now
  3051    		emit("runStart", globalSuite.start(true));
  3052    		runLoggingCallbacks("begin", {
  3053    			totalTests: Test.count,
  3054    			modules: modulesLog
  3055    		});
  3056    	}
  3057  
  3058    	config.blocking = false;
  3059    	ProcessingQueue.advance();
  3060    }
  3061  
  3062    function setHookFunction(module, hookName) {
  3063    	return function setHook(callback) {
  3064    		module.hooks[hookName].push(callback);
  3065    	};
  3066    }
  3067  
  3068    exportQUnit(QUnit);
  3069  
  3070    (function () {
  3071  
  3072    	if (typeof window === "undefined" || typeof document === "undefined") {
  3073    		return;
  3074    	}
  3075  
  3076    	var config = QUnit.config,
  3077    	    hasOwn = Object.prototype.hasOwnProperty;
  3078  
  3079    	// Stores fixture HTML for resetting later
  3080    	function storeFixture() {
  3081  
  3082    		// Avoid overwriting user-defined values
  3083    		if (hasOwn.call(config, "fixture")) {
  3084    			return;
  3085    		}
  3086  
  3087    		var fixture = document.getElementById("qunit-fixture");
  3088    		if (fixture) {
  3089    			config.fixture = fixture.innerHTML;
  3090    		}
  3091    	}
  3092  
  3093    	QUnit.begin(storeFixture);
  3094  
  3095    	// Resets the fixture DOM element if available.
  3096    	function resetFixture() {
  3097    		if (config.fixture == null) {
  3098    			return;
  3099    		}
  3100  
  3101    		var fixture = document.getElementById("qunit-fixture");
  3102    		if (fixture) {
  3103    			fixture.innerHTML = config.fixture;
  3104    		}
  3105    	}
  3106  
  3107    	QUnit.testStart(resetFixture);
  3108    })();
  3109  
  3110    (function () {
  3111  
  3112    	// Only interact with URLs via window.location
  3113    	var location = typeof window !== "undefined" && window.location;
  3114    	if (!location) {
  3115    		return;
  3116    	}
  3117  
  3118    	var urlParams = getUrlParams();
  3119  
  3120    	QUnit.urlParams = urlParams;
  3121  
  3122    	// Match module/test by inclusion in an array
  3123    	QUnit.config.moduleId = [].concat(urlParams.moduleId || []);
  3124    	QUnit.config.testId = [].concat(urlParams.testId || []);
  3125  
  3126    	// Exact case-insensitive match of the module name
  3127    	QUnit.config.module = urlParams.module;
  3128  
  3129    	// Regular expression or case-insenstive substring match against "moduleName: testName"
  3130    	QUnit.config.filter = urlParams.filter;
  3131  
  3132    	// Test order randomization
  3133    	if (urlParams.seed === true) {
  3134  
  3135    		// Generate a random seed if the option is specified without a value
  3136    		QUnit.config.seed = Math.random().toString(36).slice(2);
  3137    	} else if (urlParams.seed) {
  3138    		QUnit.config.seed = urlParams.seed;
  3139    	}
  3140  
  3141    	// Add URL-parameter-mapped config values with UI form rendering data
  3142    	QUnit.config.urlConfig.push({
  3143    		id: "hidepassed",
  3144    		label: "Hide passed tests",
  3145    		tooltip: "Only show tests and assertions that fail. Stored as query-strings."
  3146    	}, {
  3147    		id: "noglobals",
  3148    		label: "Check for Globals",
  3149    		tooltip: "Enabling this will test if any test introduces new properties on the " + "global object (`window` in Browsers). Stored as query-strings."
  3150    	}, {
  3151    		id: "notrycatch",
  3152    		label: "No try-catch",
  3153    		tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + "exceptions in IE reasonable. Stored as query-strings."
  3154    	});
  3155  
  3156    	QUnit.begin(function () {
  3157    		var i,
  3158    		    option,
  3159    		    urlConfig = QUnit.config.urlConfig;
  3160  
  3161    		for (i = 0; i < urlConfig.length; i++) {
  3162  
  3163    			// Options can be either strings or objects with nonempty "id" properties
  3164    			option = QUnit.config.urlConfig[i];
  3165    			if (typeof option !== "string") {
  3166    				option = option.id;
  3167    			}
  3168  
  3169    			if (QUnit.config[option] === undefined) {
  3170    				QUnit.config[option] = urlParams[option];
  3171    			}
  3172    		}
  3173    	});
  3174  
  3175    	function getUrlParams() {
  3176    		var i, param, name, value;
  3177    		var urlParams = Object.create(null);
  3178    		var params = location.search.slice(1).split("&");
  3179    		var length = params.length;
  3180  
  3181    		for (i = 0; i < length; i++) {
  3182    			if (params[i]) {
  3183    				param = params[i].split("=");
  3184    				name = decodeQueryParam(param[0]);
  3185  
  3186    				// Allow just a key to turn on a flag, e.g., test.html?noglobals
  3187    				value = param.length === 1 || decodeQueryParam(param.slice(1).join("="));
  3188    				if (name in urlParams) {
  3189    					urlParams[name] = [].concat(urlParams[name], value);
  3190    				} else {
  3191    					urlParams[name] = value;
  3192    				}
  3193    			}
  3194    		}
  3195  
  3196    		return urlParams;
  3197    	}
  3198  
  3199    	function decodeQueryParam(param) {
  3200    		return decodeURIComponent(param.replace(/\+/g, "%20"));
  3201    	}
  3202    })();
  3203  
  3204    var stats = {
  3205    	passedTests: 0,
  3206    	failedTests: 0,
  3207    	skippedTests: 0,
  3208    	todoTests: 0
  3209    };
  3210  
  3211    // Escape text for attribute or text content.
  3212    function escapeText(s) {
  3213    	if (!s) {
  3214    		return "";
  3215    	}
  3216    	s = s + "";
  3217  
  3218    	// Both single quotes and double quotes (for attributes)
  3219    	return s.replace(/['"<>&]/g, function (s) {
  3220    		switch (s) {
  3221    			case "'":
  3222    				return "&#039;";
  3223    			case "\"":
  3224    				return "&quot;";
  3225    			case "<":
  3226    				return "&lt;";
  3227    			case ">":
  3228    				return "&gt;";
  3229    			case "&":
  3230    				return "&amp;";
  3231    		}
  3232    	});
  3233    }
  3234  
  3235    (function () {
  3236  
  3237    	// Don't load the HTML Reporter on non-browser environments
  3238    	if (typeof window === "undefined" || !window.document) {
  3239    		return;
  3240    	}
  3241  
  3242    	var config = QUnit.config,
  3243    	    document$$1 = window.document,
  3244    	    collapseNext = false,
  3245    	    hasOwn = Object.prototype.hasOwnProperty,
  3246    	    unfilteredUrl = setUrl({ filter: undefined, module: undefined,
  3247    		moduleId: undefined, testId: undefined }),
  3248    	    modulesList = [];
  3249  
  3250    	function addEvent(elem, type, fn) {
  3251    		elem.addEventListener(type, fn, false);
  3252    	}
  3253  
  3254    	function removeEvent(elem, type, fn) {
  3255    		elem.removeEventListener(type, fn, false);
  3256    	}
  3257  
  3258    	function addEvents(elems, type, fn) {
  3259    		var i = elems.length;
  3260    		while (i--) {
  3261    			addEvent(elems[i], type, fn);
  3262    		}
  3263    	}
  3264  
  3265    	function hasClass(elem, name) {
  3266    		return (" " + elem.className + " ").indexOf(" " + name + " ") >= 0;
  3267    	}
  3268  
  3269    	function addClass(elem, name) {
  3270    		if (!hasClass(elem, name)) {
  3271    			elem.className += (elem.className ? " " : "") + name;
  3272    		}
  3273    	}
  3274  
  3275    	function toggleClass(elem, name, force) {
  3276    		if (force || typeof force === "undefined" && !hasClass(elem, name)) {
  3277    			addClass(elem, name);
  3278    		} else {
  3279    			removeClass(elem, name);
  3280    		}
  3281    	}
  3282  
  3283    	function removeClass(elem, name) {
  3284    		var set = " " + elem.className + " ";
  3285  
  3286    		// Class name may appear multiple times
  3287    		while (set.indexOf(" " + name + " ") >= 0) {
  3288    			set = set.replace(" " + name + " ", " ");
  3289    		}
  3290  
  3291    		// Trim for prettiness
  3292    		elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
  3293    	}
  3294  
  3295    	function id(name) {
  3296    		return document$$1.getElementById && document$$1.getElementById(name);
  3297    	}
  3298  
  3299    	function abortTests() {
  3300    		var abortButton = id("qunit-abort-tests-button");
  3301    		if (abortButton) {
  3302    			abortButton.disabled = true;
  3303    			abortButton.innerHTML = "Aborting...";
  3304    		}
  3305    		QUnit.config.queue.length = 0;
  3306    		return false;
  3307    	}
  3308  
  3309    	function interceptNavigation(ev) {
  3310    		applyUrlParams();
  3311  
  3312    		if (ev && ev.preventDefault) {
  3313    			ev.preventDefault();
  3314    		}
  3315  
  3316    		return false;
  3317    	}
  3318  
  3319    	function getUrlConfigHtml() {
  3320    		var i,
  3321    		    j,
  3322    		    val,
  3323    		    escaped,
  3324    		    escapedTooltip,
  3325    		    selection = false,
  3326    		    urlConfig = config.urlConfig,
  3327    		    urlConfigHtml = "";
  3328  
  3329    		for (i = 0; i < urlConfig.length; i++) {
  3330  
  3331    			// Options can be either strings or objects with nonempty "id" properties
  3332    			val = config.urlConfig[i];
  3333    			if (typeof val === "string") {
  3334    				val = {
  3335    					id: val,
  3336    					label: val
  3337    				};
  3338    			}
  3339  
  3340    			escaped = escapeText(val.id);
  3341    			escapedTooltip = escapeText(val.tooltip);
  3342  
  3343    			if (!val.value || typeof val.value === "string") {
  3344    				urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'><input id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' type='checkbox'" + (val.value ? " value='" + escapeText(val.value) + "'" : "") + (config[val.id] ? " checked='checked'" : "") + " title='" + escapedTooltip + "' />" + escapeText(val.label) + "</label>";
  3345    			} else {
  3346    				urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'>" + val.label + ": </label><select id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
  3347  
  3348    				if (QUnit.is("array", val.value)) {
  3349    					for (j = 0; j < val.value.length; j++) {
  3350    						escaped = escapeText(val.value[j]);
  3351    						urlConfigHtml += "<option value='" + escaped + "'" + (config[val.id] === val.value[j] ? (selection = true) && " selected='selected'" : "") + ">" + escaped + "</option>";
  3352    					}
  3353    				} else {
  3354    					for (j in val.value) {
  3355    						if (hasOwn.call(val.value, j)) {
  3356    							urlConfigHtml += "<option value='" + escapeText(j) + "'" + (config[val.id] === j ? (selection = true) && " selected='selected'" : "") + ">" + escapeText(val.value[j]) + "</option>";
  3357    						}
  3358    					}
  3359    				}
  3360    				if (config[val.id] && !selection) {
  3361    					escaped = escapeText(config[val.id]);
  3362    					urlConfigHtml += "<option value='" + escaped + "' selected='selected' disabled='disabled'>" + escaped + "</option>";
  3363    				}
  3364    				urlConfigHtml += "</select>";
  3365    			}
  3366    		}
  3367  
  3368    		return urlConfigHtml;
  3369    	}
  3370  
  3371    	// Handle "click" events on toolbar checkboxes and "change" for select menus.
  3372    	// Updates the URL with the new state of `config.urlConfig` values.
  3373    	function toolbarChanged() {
  3374    		var updatedUrl,
  3375    		    value,
  3376    		    tests,
  3377    		    field = this,
  3378    		    params = {};
  3379  
  3380    		// Detect if field is a select menu or a checkbox
  3381    		if ("selectedIndex" in field) {
  3382    			value = field.options[field.selectedIndex].value || undefined;
  3383    		} else {
  3384    			value = field.checked ? field.defaultValue || true : undefined;
  3385    		}
  3386  
  3387    		params[field.name] = value;
  3388    		updatedUrl = setUrl(params);
  3389  
  3390    		// Check if we can apply the change without a page refresh
  3391    		if ("hidepassed" === field.name && "replaceState" in window.history) {
  3392    			QUnit.urlParams[field.name] = value;
  3393    			config[field.name] = value || false;
  3394    			tests = id("qunit-tests");
  3395    			if (tests) {
  3396    				toggleClass(tests, "hidepass", value || false);
  3397    			}
  3398    			window.history.replaceState(null, "", updatedUrl);
  3399    		} else {
  3400    			window.location = updatedUrl;
  3401    		}
  3402    	}
  3403  
  3404    	function setUrl(params) {
  3405    		var key,
  3406    		    arrValue,
  3407    		    i,
  3408    		    querystring = "?",
  3409    		    location = window.location;
  3410  
  3411    		params = QUnit.extend(QUnit.extend({}, QUnit.urlParams), params);
  3412  
  3413    		for (key in params) {
  3414  
  3415    			// Skip inherited or undefined properties
  3416    			if (hasOwn.call(params, key) && params[key] !== undefined) {
  3417  
  3418    				// Output a parameter for each value of this key
  3419    				// (but usually just one)
  3420    				arrValue = [].concat(params[key]);
  3421    				for (i = 0; i < arrValue.length; i++) {
  3422    					querystring += encodeURIComponent(key);
  3423    					if (arrValue[i] !== true) {
  3424    						querystring += "=" + encodeURIComponent(arrValue[i]);
  3425    					}
  3426    					querystring += "&";
  3427    				}
  3428    			}
  3429    		}
  3430    		return location.protocol + "//" + location.host + location.pathname + querystring.slice(0, -1);
  3431    	}
  3432  
  3433    	function applyUrlParams() {
  3434    		var i,
  3435    		    selectedModules = [],
  3436    		    modulesList = id("qunit-modulefilter-dropdown-list").getElementsByTagName("input"),
  3437    		    filter = id("qunit-filter-input").value;
  3438  
  3439    		for (i = 0; i < modulesList.length; i++) {
  3440    			if (modulesList[i].checked) {
  3441    				selectedModules.push(modulesList[i].value);
  3442    			}
  3443    		}
  3444  
  3445    		window.location = setUrl({
  3446    			filter: filter === "" ? undefined : filter,
  3447    			moduleId: selectedModules.length === 0 ? undefined : selectedModules,
  3448  
  3449    			// Remove module and testId filter
  3450    			module: undefined,
  3451    			testId: undefined
  3452    		});
  3453    	}
  3454  
  3455    	function toolbarUrlConfigContainer() {
  3456    		var urlConfigContainer = document$$1.createElement("span");
  3457  
  3458    		urlConfigContainer.innerHTML = getUrlConfigHtml();
  3459    		addClass(urlConfigContainer, "qunit-url-config");
  3460  
  3461    		addEvents(urlConfigContainer.getElementsByTagName("input"), "change", toolbarChanged);
  3462    		addEvents(urlConfigContainer.getElementsByTagName("select"), "change", toolbarChanged);
  3463  
  3464    		return urlConfigContainer;
  3465    	}
  3466  
  3467    	function abortTestsButton() {
  3468    		var button = document$$1.createElement("button");
  3469    		button.id = "qunit-abort-tests-button";
  3470    		button.innerHTML = "Abort";
  3471    		addEvent(button, "click", abortTests);
  3472    		return button;
  3473    	}
  3474  
  3475    	function toolbarLooseFilter() {
  3476    		var filter = document$$1.createElement("form"),
  3477    		    label = document$$1.createElement("label"),
  3478    		    input = document$$1.createElement("input"),
  3479    		    button = document$$1.createElement("button");
  3480  
  3481    		addClass(filter, "qunit-filter");
  3482  
  3483    		label.innerHTML = "Filter: ";
  3484  
  3485    		input.type = "text";
  3486    		input.value = config.filter || "";
  3487    		input.name = "filter";
  3488    		input.id = "qunit-filter-input";
  3489  
  3490    		button.innerHTML = "Go";
  3491  
  3492    		label.appendChild(input);
  3493  
  3494    		filter.appendChild(label);
  3495    		filter.appendChild(document$$1.createTextNode(" "));
  3496    		filter.appendChild(button);
  3497    		addEvent(filter, "submit", interceptNavigation);
  3498  
  3499    		return filter;
  3500    	}
  3501  
  3502    	function moduleListHtml() {
  3503    		var i,
  3504    		    checked,
  3505    		    html = "";
  3506  
  3507    		for (i = 0; i < config.modules.length; i++) {
  3508    			if (config.modules[i].name !== "") {
  3509    				checked = config.moduleId.indexOf(config.modules[i].moduleId) > -1;
  3510    				html += "<li><label class='clickable" + (checked ? " checked" : "") + "'><input type='checkbox' " + "value='" + config.modules[i].moduleId + "'" + (checked ? " checked='checked'" : "") + " />" + escapeText(config.modules[i].name) + "</label></li>";
  3511    			}
  3512    		}
  3513  
  3514    		return html;
  3515    	}
  3516  
  3517    	function toolbarModuleFilter() {
  3518    		var allCheckbox,
  3519    		    commit,
  3520    		    reset,
  3521    		    moduleFilter = document$$1.createElement("form"),
  3522    		    label = document$$1.createElement("label"),
  3523    		    moduleSearch = document$$1.createElement("input"),
  3524    		    dropDown = document$$1.createElement("div"),
  3525    		    actions = document$$1.createElement("span"),
  3526    		    dropDownList = document$$1.createElement("ul"),
  3527    		    dirty = false;
  3528  
  3529    		moduleSearch.id = "qunit-modulefilter-search";
  3530    		addEvent(moduleSearch, "input", searchInput);
  3531    		addEvent(moduleSearch, "input", searchFocus);
  3532    		addEvent(moduleSearch, "focus", searchFocus);
  3533    		addEvent(moduleSearch, "click", searchFocus);
  3534  
  3535    		label.id = "qunit-modulefilter-search-container";
  3536    		label.innerHTML = "Module: ";
  3537    		label.appendChild(moduleSearch);
  3538  
  3539    		actions.id = "qunit-modulefilter-actions";
  3540    		actions.innerHTML = "<button style='display:none'>Apply</button>" + "<button type='reset' style='display:none'>Reset</button>" + "<label class='clickable" + (config.moduleId.length ? "" : " checked") + "'><input type='checkbox'" + (config.moduleId.length ? "" : " checked='checked'") + ">All modules</label>";
  3541    		allCheckbox = actions.lastChild.firstChild;
  3542    		commit = actions.firstChild;
  3543    		reset = commit.nextSibling;
  3544    		addEvent(commit, "click", applyUrlParams);
  3545  
  3546    		dropDownList.id = "qunit-modulefilter-dropdown-list";
  3547    		dropDownList.innerHTML = moduleListHtml();
  3548  
  3549    		dropDown.id = "qunit-modulefilter-dropdown";
  3550    		dropDown.style.display = "none";
  3551    		dropDown.appendChild(actions);
  3552    		dropDown.appendChild(dropDownList);
  3553    		addEvent(dropDown, "change", selectionChange);
  3554    		selectionChange();
  3555  
  3556    		moduleFilter.id = "qunit-modulefilter";
  3557    		moduleFilter.appendChild(label);
  3558    		moduleFilter.appendChild(dropDown);
  3559    		addEvent(moduleFilter, "submit", interceptNavigation);
  3560    		addEvent(moduleFilter, "reset", function () {
  3561  
  3562    			// Let the reset happen, then update styles
  3563    			window.setTimeout(selectionChange);
  3564    		});
  3565  
  3566    		// Enables show/hide for the dropdown
  3567    		function searchFocus() {
  3568    			if (dropDown.style.display !== "none") {
  3569    				return;
  3570    			}
  3571  
  3572    			dropDown.style.display = "block";
  3573    			addEvent(document$$1, "click", hideHandler);
  3574    			addEvent(document$$1, "keydown", hideHandler);
  3575  
  3576    			// Hide on Escape keydown or outside-container click
  3577    			function hideHandler(e) {
  3578    				var inContainer = moduleFilter.contains(e.target);
  3579  
  3580    				if (e.keyCode === 27 || !inContainer) {
  3581    					if (e.keyCode === 27 && inContainer) {
  3582    						moduleSearch.focus();
  3583    					}
  3584    					dropDown.style.display = "none";
  3585    					removeEvent(document$$1, "click", hideHandler);
  3586    					removeEvent(document$$1, "keydown", hideHandler);
  3587    					moduleSearch.value = "";
  3588    					searchInput();
  3589    				}
  3590    			}
  3591    		}
  3592  
  3593    		// Processes module search box input
  3594    		function searchInput() {
  3595    			var i,
  3596    			    item,
  3597    			    searchText = moduleSearch.value.toLowerCase(),
  3598    			    listItems = dropDownList.children;
  3599  
  3600    			for (i = 0; i < listItems.length; i++) {
  3601    				item = listItems[i];
  3602    				if (!searchText || item.textContent.toLowerCase().indexOf(searchText) > -1) {
  3603    					item.style.display = "";
  3604    				} else {
  3605    					item.style.display = "none";
  3606    				}
  3607    			}
  3608    		}
  3609  
  3610    		// Processes selection changes
  3611    		function selectionChange(evt) {
  3612    			var i,
  3613    			    item,
  3614    			    checkbox = evt && evt.target || allCheckbox,
  3615    			    modulesList = dropDownList.getElementsByTagName("input"),
  3616    			    selectedNames = [];
  3617  
  3618    			toggleClass(checkbox.parentNode, "checked", checkbox.checked);
  3619  
  3620    			dirty = false;
  3621    			if (checkbox.checked && checkbox !== allCheckbox) {
  3622    				allCheckbox.checked = false;
  3623    				removeClass(allCheckbox.parentNode, "checked");
  3624    			}
  3625    			for (i = 0; i < modulesList.length; i++) {
  3626    				item = modulesList[i];
  3627    				if (!evt) {
  3628    					toggleClass(item.parentNode, "checked", item.checked);
  3629    				} else if (checkbox === allCheckbox && checkbox.checked) {
  3630    					item.checked = false;
  3631    					removeClass(item.parentNode, "checked");
  3632    				}
  3633    				dirty = dirty || item.checked !== item.defaultChecked;
  3634    				if (item.checked) {
  3635    					selectedNames.push(item.parentNode.textContent);
  3636    				}
  3637    			}
  3638  
  3639    			commit.style.display = reset.style.display = dirty ? "" : "none";
  3640    			moduleSearch.placeholder = selectedNames.join(", ") || allCheckbox.parentNode.textContent;
  3641    			moduleSearch.title = "Type to filter list. Current selection:\n" + (selectedNames.join("\n") || allCheckbox.parentNode.textContent);
  3642    		}
  3643  
  3644    		return moduleFilter;
  3645    	}
  3646  
  3647    	function appendToolbar() {
  3648    		var toolbar = id("qunit-testrunner-toolbar");
  3649  
  3650    		if (toolbar) {
  3651    			toolbar.appendChild(toolbarUrlConfigContainer());
  3652    			toolbar.appendChild(toolbarModuleFilter());
  3653    			toolbar.appendChild(toolbarLooseFilter());
  3654    			toolbar.appendChild(document$$1.createElement("div")).className = "clearfix";
  3655    		}
  3656    	}
  3657  
  3658    	function appendHeader() {
  3659    		var header = id("qunit-header");
  3660  
  3661    		if (header) {
  3662    			header.innerHTML = "<a href='" + escapeText(unfilteredUrl) + "'>" + header.innerHTML + "</a> ";
  3663    		}
  3664    	}
  3665  
  3666    	function appendBanner() {
  3667    		var banner = id("qunit-banner");
  3668  
  3669    		if (banner) {
  3670    			banner.className = "";
  3671    		}
  3672    	}
  3673  
  3674    	function appendTestResults() {
  3675    		var tests = id("qunit-tests"),
  3676    		    result = id("qunit-testresult"),
  3677    		    controls;
  3678  
  3679    		if (result) {
  3680    			result.parentNode.removeChild(result);
  3681    		}
  3682  
  3683    		if (tests) {
  3684    			tests.innerHTML = "";
  3685    			result = document$$1.createElement("p");
  3686    			result.id = "qunit-testresult";
  3687    			result.className = "result";
  3688    			tests.parentNode.insertBefore(result, tests);
  3689    			result.innerHTML = "<div id=\"qunit-testresult-display\">Running...<br />&#160;</div>" + "<div id=\"qunit-testresult-controls\"></div>" + "<div class=\"clearfix\"></div>";
  3690    			controls = id("qunit-testresult-controls");
  3691    		}
  3692  
  3693    		if (controls) {
  3694    			controls.appendChild(abortTestsButton());
  3695    		}
  3696    	}
  3697  
  3698    	function appendFilteredTest() {
  3699    		var testId = QUnit.config.testId;
  3700    		if (!testId || testId.length <= 0) {
  3701    			return "";
  3702    		}
  3703    		return "<div id='qunit-filteredTest'>Rerunning selected tests: " + escapeText(testId.join(", ")) + " <a id='qunit-clearFilter' href='" + escapeText(unfilteredUrl) + "'>Run all tests</a></div>";
  3704    	}
  3705  
  3706    	function appendUserAgent() {
  3707    		var userAgent = id("qunit-userAgent");
  3708  
  3709    		if (userAgent) {
  3710    			userAgent.innerHTML = "";
  3711    			userAgent.appendChild(document$$1.createTextNode("QUnit " + QUnit.version + "; " + navigator.userAgent));
  3712    		}
  3713    	}
  3714  
  3715    	function appendInterface() {
  3716    		var qunit = id("qunit");
  3717  
  3718    		if (qunit) {
  3719    			qunit.innerHTML = "<h1 id='qunit-header'>" + escapeText(document$$1.title) + "</h1>" + "<h2 id='qunit-banner'></h2>" + "<div id='qunit-testrunner-toolbar'></div>" + appendFilteredTest() + "<h2 id='qunit-userAgent'></h2>" + "<ol id='qunit-tests'></ol>";
  3720    		}
  3721  
  3722    		appendHeader();
  3723    		appendBanner();
  3724    		appendTestResults();
  3725    		appendUserAgent();
  3726    		appendToolbar();
  3727    	}
  3728  
  3729    	function appendTestsList(modules) {
  3730    		var i, l, x, z, test, moduleObj;
  3731  
  3732    		for (i = 0, l = modules.length; i < l; i++) {
  3733    			moduleObj = modules[i];
  3734  
  3735    			for (x = 0, z = moduleObj.tests.length; x < z; x++) {
  3736    				test = moduleObj.tests[x];
  3737  
  3738    				appendTest(test.name, test.testId, moduleObj.name);
  3739    			}
  3740    		}
  3741    	}
  3742  
  3743    	function appendTest(name, testId, moduleName) {
  3744    		var title,
  3745    		    rerunTrigger,
  3746    		    testBlock,
  3747    		    assertList,
  3748    		    tests = id("qunit-tests");
  3749  
  3750    		if (!tests) {
  3751    			return;
  3752    		}
  3753  
  3754    		title = document$$1.createElement("strong");
  3755    		title.innerHTML = getNameHtml(name, moduleName);
  3756  
  3757    		rerunTrigger = document$$1.createElement("a");
  3758    		rerunTrigger.innerHTML = "Rerun";
  3759    		rerunTrigger.href = setUrl({ testId: testId });
  3760  
  3761    		testBlock = document$$1.createElement("li");
  3762    		testBlock.appendChild(title);
  3763    		testBlock.appendChild(rerunTrigger);
  3764    		testBlock.id = "qunit-test-output-" + testId;
  3765  
  3766    		assertList = document$$1.createElement("ol");
  3767    		assertList.className = "qunit-assert-list";
  3768  
  3769    		testBlock.appendChild(assertList);
  3770  
  3771    		tests.appendChild(testBlock);
  3772    	}
  3773  
  3774    	// HTML Reporter initialization and load
  3775    	QUnit.begin(function (details) {
  3776    		var i, moduleObj, tests;
  3777  
  3778    		// Sort modules by name for the picker
  3779    		for (i = 0; i < details.modules.length; i++) {
  3780    			moduleObj = details.modules[i];
  3781    			if (moduleObj.name) {
  3782    				modulesList.push(moduleObj.name);
  3783    			}
  3784    		}
  3785    		modulesList.sort(function (a, b) {
  3786    			return a.localeCompare(b);
  3787    		});
  3788  
  3789    		// Initialize QUnit elements
  3790    		appendInterface();
  3791    		appendTestsList(details.modules);
  3792    		tests = id("qunit-tests");
  3793    		if (tests && config.hidepassed) {
  3794    			addClass(tests, "hidepass");
  3795    		}
  3796    	});
  3797  
  3798    	QUnit.done(function (details) {
  3799    		var banner = id("qunit-banner"),
  3800    		    tests = id("qunit-tests"),
  3801    		    abortButton = id("qunit-abort-tests-button"),
  3802    		    totalTests = stats.passedTests + stats.skippedTests + stats.todoTests + stats.failedTests,
  3803    		    html = [totalTests, " tests completed in ", details.runtime, " milliseconds, with ", stats.failedTests, " failed, ", stats.skippedTests, " skipped, and ", stats.todoTests, " todo.<br />", "<span class='passed'>", details.passed, "</span> assertions of <span class='total'>", details.total, "</span> passed, <span class='failed'>", details.failed, "</span> failed."].join(""),
  3804    		    test,
  3805    		    assertLi,
  3806    		    assertList;
  3807  
  3808    		// Update remaing tests to aborted
  3809    		if (abortButton && abortButton.disabled) {
  3810    			html = "Tests aborted after " + details.runtime + " milliseconds.";
  3811  
  3812    			for (var i = 0; i < tests.children.length; i++) {
  3813    				test = tests.children[i];
  3814    				if (test.className === "" || test.className === "running") {
  3815    					test.className = "aborted";
  3816    					assertList = test.getElementsByTagName("ol")[0];
  3817    					assertLi = document$$1.createElement("li");
  3818    					assertLi.className = "fail";
  3819    					assertLi.innerHTML = "Test aborted.";
  3820    					assertList.appendChild(assertLi);
  3821    				}
  3822    			}
  3823    		}
  3824  
  3825    		if (banner && (!abortButton || abortButton.disabled === false)) {
  3826    			banner.className = stats.failedTests ? "qunit-fail" : "qunit-pass";
  3827    		}
  3828  
  3829    		if (abortButton) {
  3830    			abortButton.parentNode.removeChild(abortButton);
  3831    		}
  3832  
  3833    		if (tests) {
  3834    			id("qunit-testresult-display").innerHTML = html;
  3835    		}
  3836  
  3837    		if (config.altertitle && document$$1.title) {
  3838  
  3839    			// Show ✖ for good, ✔ for bad suite result in title
  3840    			// use escape sequences in case file gets loaded with non-utf-8
  3841    			// charset
  3842    			document$$1.title = [stats.failedTests ? "\u2716" : "\u2714", document$$1.title.replace(/^[\u2714\u2716] /i, "")].join(" ");
  3843    		}
  3844  
  3845    		// Scroll back to top to show results
  3846    		if (config.scrolltop && window.scrollTo) {
  3847    			window.scrollTo(0, 0);
  3848    		}
  3849    	});
  3850  
  3851    	function getNameHtml(name, module) {
  3852    		var nameHtml = "";
  3853  
  3854    		if (module) {
  3855    			nameHtml = "<span class='module-name'>" + escapeText(module) + "</span>: ";
  3856    		}
  3857  
  3858    		nameHtml += "<span class='test-name'>" + escapeText(name) + "</span>";
  3859  
  3860    		return nameHtml;
  3861    	}
  3862  
  3863    	QUnit.testStart(function (details) {
  3864    		var running, testBlock, bad;
  3865  
  3866    		testBlock = id("qunit-test-output-" + details.testId);
  3867    		if (testBlock) {
  3868    			testBlock.className = "running";
  3869    		} else {
  3870  
  3871    			// Report later registered tests
  3872    			appendTest(details.name, details.testId, details.module);
  3873    		}
  3874  
  3875    		running = id("qunit-testresult-display");
  3876    		if (running) {
  3877    			bad = QUnit.config.reorder && details.previousFailure;
  3878  
  3879    			running.innerHTML = [bad ? "Rerunning previously failed test: <br />" : "Running: <br />", getNameHtml(details.name, details.module)].join("");
  3880    		}
  3881    	});
  3882  
  3883    	function stripHtml(string) {
  3884  
  3885    		// Strip tags, html entity and whitespaces
  3886    		return string.replace(/<\/?[^>]+(>|$)/g, "").replace(/\&quot;/g, "").replace(/\s+/g, "");
  3887    	}
  3888  
  3889    	QUnit.log(function (details) {
  3890    		var assertList,
  3891    		    assertLi,
  3892    		    message,
  3893    		    expected,
  3894    		    actual,
  3895    		    diff,
  3896    		    showDiff = false,
  3897    		    testItem = id("qunit-test-output-" + details.testId);
  3898  
  3899    		if (!testItem) {
  3900    			return;
  3901    		}
  3902  
  3903    		message = escapeText(details.message) || (details.result ? "okay" : "failed");
  3904    		message = "<span class='test-message'>" + message + "</span>";
  3905    		message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
  3906  
  3907    		// The pushFailure doesn't provide details.expected
  3908    		// when it calls, it's implicit to also not show expected and diff stuff
  3909    		// Also, we need to check details.expected existence, as it can exist and be undefined
  3910    		if (!details.result && hasOwn.call(details, "expected")) {
  3911    			if (details.negative) {
  3912    				expected = "NOT " + QUnit.dump.parse(details.expected);
  3913    			} else {
  3914    				expected = QUnit.dump.parse(details.expected);
  3915    			}
  3916  
  3917    			actual = QUnit.dump.parse(details.actual);
  3918    			message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + escapeText(expected) + "</pre></td></tr>";
  3919  
  3920    			if (actual !== expected) {
  3921  
  3922    				message += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText(actual) + "</pre></td></tr>";
  3923  
  3924    				if (typeof details.actual === "number" && typeof details.expected === "number") {
  3925    					if (!isNaN(details.actual) && !isNaN(details.expected)) {
  3926    						showDiff = true;
  3927    						diff = details.actual - details.expected;
  3928    						diff = (diff > 0 ? "+" : "") + diff;
  3929    					}
  3930    				} else if (typeof details.actual !== "boolean" && typeof details.expected !== "boolean") {
  3931    					diff = QUnit.diff(expected, actual);
  3932  
  3933    					// don't show diff if there is zero overlap
  3934    					showDiff = stripHtml(diff).length !== stripHtml(expected).length + stripHtml(actual).length;
  3935    				}
  3936  
  3937    				if (showDiff) {
  3938    					message += "<tr class='test-diff'><th>Diff: </th><td><pre>" + diff + "</pre></td></tr>";
  3939    				}
  3940    			} else if (expected.indexOf("[object Array]") !== -1 || expected.indexOf("[object Object]") !== -1) {
  3941    				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>";
  3942    			} else {
  3943    				message += "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the expected and actual results have an equivalent" + " serialization</td></tr>";
  3944    			}
  3945  
  3946    			if (details.source) {
  3947    				message += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>";
  3948    			}
  3949  
  3950    			message += "</table>";
  3951  
  3952    			// This occurs when pushFailure is set and we have an extracted stack trace
  3953    		} else if (!details.result && details.source) {
  3954    			message += "<table>" + "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>" + "</table>";
  3955    		}
  3956  
  3957    		assertList = testItem.getElementsByTagName("ol")[0];
  3958  
  3959    		assertLi = document$$1.createElement("li");
  3960    		assertLi.className = details.result ? "pass" : "fail";
  3961    		assertLi.innerHTML = message;
  3962    		assertList.appendChild(assertLi);
  3963    	});
  3964  
  3965    	QUnit.testDone(function (details) {
  3966    		var testTitle,
  3967    		    time,
  3968    		    testItem,
  3969    		    assertList,
  3970    		    good,
  3971    		    bad,
  3972    		    testCounts,
  3973    		    skipped,
  3974    		    sourceName,
  3975    		    tests = id("qunit-tests");
  3976  
  3977    		if (!tests) {
  3978    			return;
  3979    		}
  3980  
  3981    		testItem = id("qunit-test-output-" + details.testId);
  3982  
  3983    		assertList = testItem.getElementsByTagName("ol")[0];
  3984  
  3985    		good = details.passed;
  3986    		bad = details.failed;
  3987  
  3988    		// This test passed if it has no unexpected failed assertions
  3989    		var testPassed = details.failed > 0 ? details.todo : !details.todo;
  3990  
  3991    		if (testPassed) {
  3992  
  3993    			// Collapse the passing tests
  3994    			addClass(assertList, "qunit-collapsed");
  3995    		} else if (config.collapse) {
  3996    			if (!collapseNext) {
  3997  
  3998    				// Skip collapsing the first failing test
  3999    				collapseNext = true;
  4000    			} else {
  4001  
  4002    				// Collapse remaining tests
  4003    				addClass(assertList, "qunit-collapsed");
  4004    			}
  4005    		}
  4006  
  4007    		// The testItem.firstChild is the test name
  4008    		testTitle = testItem.firstChild;
  4009  
  4010    		testCounts = bad ? "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " : "";
  4011  
  4012    		testTitle.innerHTML += " <b class='counts'>(" + testCounts + details.assertions.length + ")</b>";
  4013  
  4014    		if (details.skipped) {
  4015    			stats.skippedTests++;
  4016  
  4017    			testItem.className = "skipped";
  4018    			skipped = document$$1.createElement("em");
  4019    			skipped.className = "qunit-skipped-label";
  4020    			skipped.innerHTML = "skipped";
  4021    			testItem.insertBefore(skipped, testTitle);
  4022    		} else {
  4023    			addEvent(testTitle, "click", function () {
  4024    				toggleClass(assertList, "qunit-collapsed");
  4025    			});
  4026  
  4027    			testItem.className = testPassed ? "pass" : "fail";
  4028  
  4029    			if (details.todo) {
  4030    				var todoLabel = document$$1.createElement("em");
  4031    				todoLabel.className = "qunit-todo-label";
  4032    				todoLabel.innerHTML = "todo";
  4033    				testItem.className += " todo";
  4034    				testItem.insertBefore(todoLabel, testTitle);
  4035    			}
  4036  
  4037    			time = document$$1.createElement("span");
  4038    			time.className = "runtime";
  4039    			time.innerHTML = details.runtime + " ms";
  4040    			testItem.insertBefore(time, assertList);
  4041  
  4042    			if (!testPassed) {
  4043    				stats.failedTests++;
  4044    			} else if (details.todo) {
  4045    				stats.todoTests++;
  4046    			} else {
  4047    				stats.passedTests++;
  4048    			}
  4049    		}
  4050  
  4051    		// Show the source of the test when showing assertions
  4052    		if (details.source) {
  4053    			sourceName = document$$1.createElement("p");
  4054    			sourceName.innerHTML = "<strong>Source: </strong>" + details.source;
  4055    			addClass(sourceName, "qunit-source");
  4056    			if (testPassed) {
  4057    				addClass(sourceName, "qunit-collapsed");
  4058    			}
  4059    			addEvent(testTitle, "click", function () {
  4060    				toggleClass(sourceName, "qunit-collapsed");
  4061    			});
  4062    			testItem.appendChild(sourceName);
  4063    		}
  4064    	});
  4065  
  4066    	// Avoid readyState issue with phantomjs
  4067    	// Ref: #818
  4068    	var notPhantom = function (p) {
  4069    		return !(p && p.version && p.version.major > 0);
  4070    	}(window.phantom);
  4071  
  4072    	if (notPhantom && document$$1.readyState === "complete") {
  4073    		QUnit.load();
  4074    	} else {
  4075    		addEvent(window, "load", QUnit.load);
  4076    	}
  4077  
  4078    	// Wrap window.onerror. We will call the original window.onerror to see if
  4079    	// the existing handler fully handles the error; if not, we will call the
  4080    	// QUnit.onError function.
  4081    	var originalWindowOnError = window.onerror;
  4082  
  4083    	// Cover uncaught exceptions
  4084    	// Returning true will suppress the default browser handler,
  4085    	// returning false will let it run.
  4086    	window.onerror = function (message, fileName, lineNumber) {
  4087    		var ret = false;
  4088    		if (originalWindowOnError) {
  4089    			for (var _len = arguments.length, args = Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) {
  4090    				args[_key - 3] = arguments[_key];
  4091    			}
  4092  
  4093    			ret = originalWindowOnError.call.apply(originalWindowOnError, [this, message, fileName, lineNumber].concat(args));
  4094    		}
  4095  
  4096    		// Treat return value as window.onerror itself does,
  4097    		// Only do our handling if not suppressed.
  4098    		if (ret !== true) {
  4099    			var error = {
  4100    				message: message,
  4101    				fileName: fileName,
  4102    				lineNumber: lineNumber
  4103    			};
  4104  
  4105    			ret = QUnit.onError(error);
  4106    		}
  4107  
  4108    		return ret;
  4109    	};
  4110  
  4111    	// Listen for unhandled rejections, and call QUnit.onUnhandledRejection
  4112    	window.addEventListener("unhandledrejection", function (event) {
  4113    		QUnit.onUnhandledRejection(event.reason);
  4114    	});
  4115    })();
  4116  
  4117    /*
  4118     * This file is a modified version of google-diff-match-patch's JavaScript implementation
  4119     * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
  4120     * modifications are licensed as more fully set forth in LICENSE.txt.
  4121     *
  4122     * The original source of google-diff-match-patch is attributable and licensed as follows:
  4123     *
  4124     * Copyright 2006 Google Inc.
  4125     * https://code.google.com/p/google-diff-match-patch/
  4126     *
  4127     * Licensed under the Apache License, Version 2.0 (the "License");
  4128     * you may not use this file except in compliance with the License.
  4129     * You may obtain a copy of the License at
  4130     *
  4131     * https://www.apache.org/licenses/LICENSE-2.0
  4132     *
  4133     * Unless required by applicable law or agreed to in writing, software
  4134     * distributed under the License is distributed on an "AS IS" BASIS,
  4135     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  4136     * See the License for the specific language governing permissions and
  4137     * limitations under the License.
  4138     *
  4139     * More Info:
  4140     *  https://code.google.com/p/google-diff-match-patch/
  4141     *
  4142     * Usage: QUnit.diff(expected, actual)
  4143     *
  4144     */
  4145    QUnit.diff = function () {
  4146    	function DiffMatchPatch() {}
  4147  
  4148    	//  DIFF FUNCTIONS
  4149  
  4150    	/**
  4151      * The data structure representing a diff is an array of tuples:
  4152      * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
  4153      * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
  4154      */
  4155    	var DIFF_DELETE = -1,
  4156    	    DIFF_INSERT = 1,
  4157    	    DIFF_EQUAL = 0;
  4158  
  4159    	/**
  4160      * Find the differences between two texts.  Simplifies the problem by stripping
  4161      * any common prefix or suffix off the texts before diffing.
  4162      * @param {string} text1 Old string to be diffed.
  4163      * @param {string} text2 New string to be diffed.
  4164      * @param {boolean=} optChecklines Optional speedup flag. If present and false,
  4165      *     then don't run a line-level diff first to identify the changed areas.
  4166      *     Defaults to true, which does a faster, slightly less optimal diff.
  4167      * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
  4168      */
  4169    	DiffMatchPatch.prototype.DiffMain = function (text1, text2, optChecklines) {
  4170    		var deadline, checklines, commonlength, commonprefix, commonsuffix, diffs;
  4171  
  4172    		// The diff must be complete in up to 1 second.
  4173    		deadline = new Date().getTime() + 1000;
  4174  
  4175    		// Check for null inputs.
  4176    		if (text1 === null || text2 === null) {
  4177    			throw new Error("Null input. (DiffMain)");
  4178    		}
  4179  
  4180    		// Check for equality (speedup).
  4181    		if (text1 === text2) {
  4182    			if (text1) {
  4183    				return [[DIFF_EQUAL, text1]];
  4184    			}
  4185    			return [];
  4186    		}
  4187  
  4188    		if (typeof optChecklines === "undefined") {
  4189    			optChecklines = true;
  4190    		}
  4191  
  4192    		checklines = optChecklines;
  4193  
  4194    		// Trim off common prefix (speedup).
  4195    		commonlength = this.diffCommonPrefix(text1, text2);
  4196    		commonprefix = text1.substring(0, commonlength);
  4197    		text1 = text1.substring(commonlength);
  4198    		text2 = text2.substring(commonlength);
  4199  
  4200    		// Trim off common suffix (speedup).
  4201    		commonlength = this.diffCommonSuffix(text1, text2);
  4202    		commonsuffix = text1.substring(text1.length - commonlength);
  4203    		text1 = text1.substring(0, text1.length - commonlength);
  4204    		text2 = text2.substring(0, text2.length - commonlength);
  4205  
  4206    		// Compute the diff on the middle block.
  4207    		diffs = this.diffCompute(text1, text2, checklines, deadline);
  4208  
  4209    		// Restore the prefix and suffix.
  4210    		if (commonprefix) {
  4211    			diffs.unshift([DIFF_EQUAL, commonprefix]);
  4212    		}
  4213    		if (commonsuffix) {
  4214    			diffs.push([DIFF_EQUAL, commonsuffix]);
  4215    		}
  4216    		this.diffCleanupMerge(diffs);
  4217    		return diffs;
  4218    	};
  4219  
  4220    	/**
  4221      * Reduce the number of edits by eliminating operationally trivial equalities.
  4222      * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
  4223      */
  4224    	DiffMatchPatch.prototype.diffCleanupEfficiency = function (diffs) {
  4225    		var changes, equalities, equalitiesLength, lastequality, pointer, preIns, preDel, postIns, postDel;
  4226    		changes = false;
  4227    		equalities = []; // Stack of indices where equalities are found.
  4228    		equalitiesLength = 0; // Keeping our own length var is faster in JS.
  4229    		/** @type {?string} */
  4230    		lastequality = null;
  4231  
  4232    		// Always equal to diffs[equalities[equalitiesLength - 1]][1]
  4233    		pointer = 0; // Index of current position.
  4234  
  4235    		// Is there an insertion operation before the last equality.
  4236    		preIns = false;
  4237  
  4238    		// Is there a deletion operation before the last equality.
  4239    		preDel = false;
  4240  
  4241    		// Is there an insertion operation after the last equality.
  4242    		postIns = false;
  4243  
  4244    		// Is there a deletion operation after the last equality.
  4245    		postDel = false;
  4246    		while (pointer < diffs.length) {
  4247  
  4248    			// Equality found.
  4249    			if (diffs[pointer][0] === DIFF_EQUAL) {
  4250    				if (diffs[pointer][1].length < 4 && (postIns || postDel)) {
  4251  
  4252    					// Candidate found.
  4253    					equalities[equalitiesLength++] = pointer;
  4254    					preIns = postIns;
  4255    					preDel = postDel;
  4256    					lastequality = diffs[pointer][1];
  4257    				} else {
  4258  
  4259    					// Not a candidate, and can never become one.
  4260    					equalitiesLength = 0;
  4261    					lastequality = null;
  4262    				}
  4263    				postIns = postDel = false;
  4264  
  4265    				// An insertion or deletion.
  4266    			} else {
  4267  
  4268    				if (diffs[pointer][0] === DIFF_DELETE) {
  4269    					postDel = true;
  4270    				} else {
  4271    					postIns = true;
  4272    				}
  4273  
  4274    				/*
  4275         * Five types to be split:
  4276         * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
  4277         * <ins>A</ins>X<ins>C</ins><del>D</del>
  4278         * <ins>A</ins><del>B</del>X<ins>C</ins>
  4279         * <ins>A</del>X<ins>C</ins><del>D</del>
  4280         * <ins>A</ins><del>B</del>X<del>C</del>
  4281         */
  4282    				if (lastequality && (preIns && preDel && postIns && postDel || lastequality.length < 2 && preIns + preDel + postIns + postDel === 3)) {
  4283  
  4284    					// Duplicate record.
  4285    					diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
  4286  
  4287    					// Change second copy to insert.
  4288    					diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
  4289    					equalitiesLength--; // Throw away the equality we just deleted;
  4290    					lastequality = null;
  4291    					if (preIns && preDel) {
  4292  
  4293    						// No changes made which could affect previous entry, keep going.
  4294    						postIns = postDel = true;
  4295    						equalitiesLength = 0;
  4296    					} else {
  4297    						equalitiesLength--; // Throw away the previous equality.
  4298    						pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
  4299    						postIns = postDel = false;
  4300    					}
  4301    					changes = true;
  4302    				}
  4303    			}
  4304    			pointer++;
  4305    		}
  4306  
  4307    		if (changes) {
  4308    			this.diffCleanupMerge(diffs);
  4309    		}
  4310    	};
  4311  
  4312    	/**
  4313      * Convert a diff array into a pretty HTML report.
  4314      * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
  4315      * @param {integer} string to be beautified.
  4316      * @return {string} HTML representation.
  4317      */
  4318    	DiffMatchPatch.prototype.diffPrettyHtml = function (diffs) {
  4319    		var op,
  4320    		    data,
  4321    		    x,
  4322    		    html = [];
  4323    		for (x = 0; x < diffs.length; x++) {
  4324    			op = diffs[x][0]; // Operation (insert, delete, equal)
  4325    			data = diffs[x][1]; // Text of change.
  4326    			switch (op) {
  4327    				case DIFF_INSERT:
  4328    					html[x] = "<ins>" + escapeText(data) + "</ins>";
  4329    					break;
  4330    				case DIFF_DELETE:
  4331    					html[x] = "<del>" + escapeText(data) + "</del>";
  4332    					break;
  4333    				case DIFF_EQUAL:
  4334    					html[x] = "<span>" + escapeText(data) + "</span>";
  4335    					break;
  4336    			}
  4337    		}
  4338    		return html.join("");
  4339    	};
  4340  
  4341    	/**
  4342      * Determine the common prefix of two strings.
  4343      * @param {string} text1 First string.
  4344      * @param {string} text2 Second string.
  4345      * @return {number} The number of characters common to the start of each
  4346      *     string.
  4347      */
  4348    	DiffMatchPatch.prototype.diffCommonPrefix = function (text1, text2) {
  4349    		var pointermid, pointermax, pointermin, pointerstart;
  4350  
  4351    		// Quick check for common null cases.
  4352    		if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0)) {
  4353    			return 0;
  4354    		}
  4355  
  4356    		// Binary search.
  4357    		// Performance analysis: https://neil.fraser.name/news/2007/10/09/
  4358    		pointermin = 0;
  4359    		pointermax = Math.min(text1.length, text2.length);
  4360    		pointermid = pointermax;
  4361    		pointerstart = 0;
  4362    		while (pointermin < pointermid) {
  4363    			if (text1.substring(pointerstart, pointermid) === text2.substring(pointerstart, pointermid)) {
  4364    				pointermin = pointermid;
  4365    				pointerstart = pointermin;
  4366    			} else {
  4367    				pointermax = pointermid;
  4368    			}
  4369    			pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
  4370    		}
  4371    		return pointermid;
  4372    	};
  4373  
  4374    	/**
  4375      * Determine the common suffix of two strings.
  4376      * @param {string} text1 First string.
  4377      * @param {string} text2 Second string.
  4378      * @return {number} The number of characters common to the end of each string.
  4379      */
  4380    	DiffMatchPatch.prototype.diffCommonSuffix = function (text1, text2) {
  4381    		var pointermid, pointermax, pointermin, pointerend;
  4382  
  4383    		// Quick check for common null cases.
  4384    		if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) {
  4385    			return 0;
  4386    		}
  4387  
  4388    		// Binary search.
  4389    		// Performance analysis: https://neil.fraser.name/news/2007/10/09/
  4390    		pointermin = 0;
  4391    		pointermax = Math.min(text1.length, text2.length);
  4392    		pointermid = pointermax;
  4393    		pointerend = 0;
  4394    		while (pointermin < pointermid) {
  4395    			if (text1.substring(text1.length - pointermid, text1.length - pointerend) === text2.substring(text2.length - pointermid, text2.length - pointerend)) {
  4396    				pointermin = pointermid;
  4397    				pointerend = pointermin;
  4398    			} else {
  4399    				pointermax = pointermid;
  4400    			}
  4401    			pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
  4402    		}
  4403    		return pointermid;
  4404    	};
  4405  
  4406    	/**
  4407      * Find the differences between two texts.  Assumes that the texts do not
  4408      * have any common prefix or suffix.
  4409      * @param {string} text1 Old string to be diffed.
  4410      * @param {string} text2 New string to be diffed.
  4411      * @param {boolean} checklines Speedup flag.  If false, then don't run a
  4412      *     line-level diff first to identify the changed areas.
  4413      *     If true, then run a faster, slightly less optimal diff.
  4414      * @param {number} deadline Time when the diff should be complete by.
  4415      * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
  4416      * @private
  4417      */
  4418    	DiffMatchPatch.prototype.diffCompute = function (text1, text2, checklines, deadline) {
  4419    		var diffs, longtext, shorttext, i, hm, text1A, text2A, text1B, text2B, midCommon, diffsA, diffsB;
  4420  
  4421    		if (!text1) {
  4422  
  4423    			// Just add some text (speedup).
  4424    			return [[DIFF_INSERT, text2]];
  4425    		}
  4426  
  4427    		if (!text2) {
  4428  
  4429    			// Just delete some text (speedup).
  4430    			return [[DIFF_DELETE, text1]];
  4431    		}
  4432  
  4433    		longtext = text1.length > text2.length ? text1 : text2;
  4434    		shorttext = text1.length > text2.length ? text2 : text1;
  4435    		i = longtext.indexOf(shorttext);
  4436    		if (i !== -1) {
  4437  
  4438    			// Shorter text is inside the longer text (speedup).
  4439    			diffs = [[DIFF_INSERT, longtext.substring(0, i)], [DIFF_EQUAL, shorttext], [DIFF_INSERT, longtext.substring(i + shorttext.length)]];
  4440  
  4441    			// Swap insertions for deletions if diff is reversed.
  4442    			if (text1.length > text2.length) {
  4443    				diffs[0][0] = diffs[2][0] = DIFF_DELETE;
  4444    			}
  4445    			return diffs;
  4446    		}
  4447  
  4448    		if (shorttext.length === 1) {
  4449  
  4450    			// Single character string.
  4451    			// After the previous speedup, the character can't be an equality.
  4452    			return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
  4453    		}
  4454  
  4455    		// Check to see if the problem can be split in two.
  4456    		hm = this.diffHalfMatch(text1, text2);
  4457    		if (hm) {
  4458  
  4459    			// A half-match was found, sort out the return data.
  4460    			text1A = hm[0];
  4461    			text1B = hm[1];
  4462    			text2A = hm[2];
  4463    			text2B = hm[3];
  4464    			midCommon = hm[4];
  4465  
  4466    			// Send both pairs off for separate processing.
  4467    			diffsA = this.DiffMain(text1A, text2A, checklines, deadline);
  4468    			diffsB = this.DiffMain(text1B, text2B, checklines, deadline);
  4469  
  4470    			// Merge the results.
  4471    			return diffsA.concat([[DIFF_EQUAL, midCommon]], diffsB);
  4472    		}
  4473  
  4474    		if (checklines && text1.length > 100 && text2.length > 100) {
  4475    			return this.diffLineMode(text1, text2, deadline);
  4476    		}
  4477  
  4478    		return this.diffBisect(text1, text2, deadline);
  4479    	};
  4480  
  4481    	/**
  4482      * Do the two texts share a substring which is at least half the length of the
  4483      * longer text?
  4484      * This speedup can produce non-minimal diffs.
  4485      * @param {string} text1 First string.
  4486      * @param {string} text2 Second string.
  4487      * @return {Array.<string>} Five element Array, containing the prefix of
  4488      *     text1, the suffix of text1, the prefix of text2, the suffix of
  4489      *     text2 and the common middle.  Or null if there was no match.
  4490      * @private
  4491      */
  4492    	DiffMatchPatch.prototype.diffHalfMatch = function (text1, text2) {
  4493    		var longtext, shorttext, dmp, text1A, text2B, text2A, text1B, midCommon, hm1, hm2, hm;
  4494  
  4495    		longtext = text1.length > text2.length ? text1 : text2;
  4496    		shorttext = text1.length > text2.length ? text2 : text1;
  4497    		if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {
  4498    			return null; // Pointless.
  4499    		}
  4500    		dmp = this; // 'this' becomes 'window' in a closure.
  4501  
  4502    		/**
  4503       * Does a substring of shorttext exist within longtext such that the substring
  4504       * is at least half the length of longtext?
  4505       * Closure, but does not reference any external variables.
  4506       * @param {string} longtext Longer string.
  4507       * @param {string} shorttext Shorter string.
  4508       * @param {number} i Start index of quarter length substring within longtext.
  4509       * @return {Array.<string>} Five element Array, containing the prefix of
  4510       *     longtext, the suffix of longtext, the prefix of shorttext, the suffix
  4511       *     of shorttext and the common middle.  Or null if there was no match.
  4512       * @private
  4513       */
  4514    		function diffHalfMatchI(longtext, shorttext, i) {
  4515    			var seed, j, bestCommon, prefixLength, suffixLength, bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
  4516  
  4517    			// Start with a 1/4 length substring at position i as a seed.
  4518    			seed = longtext.substring(i, i + Math.floor(longtext.length / 4));
  4519    			j = -1;
  4520    			bestCommon = "";
  4521    			while ((j = shorttext.indexOf(seed, j + 1)) !== -1) {
  4522    				prefixLength = dmp.diffCommonPrefix(longtext.substring(i), shorttext.substring(j));
  4523    				suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), shorttext.substring(0, j));
  4524    				if (bestCommon.length < suffixLength + prefixLength) {
  4525    					bestCommon = shorttext.substring(j - suffixLength, j) + shorttext.substring(j, j + prefixLength);
  4526    					bestLongtextA = longtext.substring(0, i - suffixLength);
  4527    					bestLongtextB = longtext.substring(i + prefixLength);
  4528    					bestShorttextA = shorttext.substring(0, j - suffixLength);
  4529    					bestShorttextB = shorttext.substring(j + prefixLength);
  4530    				}
  4531    			}
  4532    			if (bestCommon.length * 2 >= longtext.length) {
  4533    				return [bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB, bestCommon];
  4534    			} else {
  4535    				return null;
  4536    			}
  4537    		}
  4538  
  4539    		// First check if the second quarter is the seed for a half-match.
  4540    		hm1 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 4));
  4541  
  4542    		// Check again based on the third quarter.
  4543    		hm2 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 2));
  4544    		if (!hm1 && !hm2) {
  4545    			return null;
  4546    		} else if (!hm2) {
  4547    			hm = hm1;
  4548    		} else if (!hm1) {
  4549    			hm = hm2;
  4550    		} else {
  4551  
  4552    			// Both matched.  Select the longest.
  4553    			hm = hm1[4].length > hm2[4].length ? hm1 : hm2;
  4554    		}
  4555  
  4556    		// A half-match was found, sort out the return data.
  4557    		if (text1.length > text2.length) {
  4558    			text1A = hm[0];
  4559    			text1B = hm[1];
  4560    			text2A = hm[2];
  4561    			text2B = hm[3];
  4562    		} else {
  4563    			text2A = hm[0];
  4564    			text2B = hm[1];
  4565    			text1A = hm[2];
  4566    			text1B = hm[3];
  4567    		}
  4568    		midCommon = hm[4];
  4569    		return [text1A, text1B, text2A, text2B, midCommon];
  4570    	};
  4571  
  4572    	/**
  4573      * Do a quick line-level diff on both strings, then rediff the parts for
  4574      * greater accuracy.
  4575      * This speedup can produce non-minimal diffs.
  4576      * @param {string} text1 Old string to be diffed.
  4577      * @param {string} text2 New string to be diffed.
  4578      * @param {number} deadline Time when the diff should be complete by.
  4579      * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
  4580      * @private
  4581      */
  4582    	DiffMatchPatch.prototype.diffLineMode = function (text1, text2, deadline) {
  4583    		var a, diffs, linearray, pointer, countInsert, countDelete, textInsert, textDelete, j;
  4584  
  4585    		// Scan the text on a line-by-line basis first.
  4586    		a = this.diffLinesToChars(text1, text2);
  4587    		text1 = a.chars1;
  4588    		text2 = a.chars2;
  4589    		linearray = a.lineArray;
  4590  
  4591    		diffs = this.DiffMain(text1, text2, false, deadline);
  4592  
  4593    		// Convert the diff back to original text.
  4594    		this.diffCharsToLines(diffs, linearray);
  4595  
  4596    		// Eliminate freak matches (e.g. blank lines)
  4597    		this.diffCleanupSemantic(diffs);
  4598  
  4599    		// Rediff any replacement blocks, this time character-by-character.
  4600    		// Add a dummy entry at the end.
  4601    		diffs.push([DIFF_EQUAL, ""]);
  4602    		pointer = 0;
  4603    		countDelete = 0;
  4604    		countInsert = 0;
  4605    		textDelete = "";
  4606    		textInsert = "";
  4607    		while (pointer < diffs.length) {
  4608    			switch (diffs[pointer][0]) {
  4609    				case DIFF_INSERT:
  4610    					countInsert++;
  4611    					textInsert += diffs[pointer][1];
  4612    					break;
  4613    				case DIFF_DELETE:
  4614    					countDelete++;
  4615    					textDelete += diffs[pointer][1];
  4616    					break;
  4617    				case DIFF_EQUAL:
  4618  
  4619    					// Upon reaching an equality, check for prior redundancies.
  4620    					if (countDelete >= 1 && countInsert >= 1) {
  4621  
  4622    						// Delete the offending records and add the merged ones.
  4623    						diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert);
  4624    						pointer = pointer - countDelete - countInsert;
  4625    						a = this.DiffMain(textDelete, textInsert, false, deadline);
  4626    						for (j = a.length - 1; j >= 0; j--) {
  4627    							diffs.splice(pointer, 0, a[j]);
  4628    						}
  4629    						pointer = pointer + a.length;
  4630    					}
  4631    					countInsert = 0;
  4632    					countDelete = 0;
  4633    					textDelete = "";
  4634    					textInsert = "";
  4635    					break;
  4636    			}
  4637    			pointer++;
  4638    		}
  4639    		diffs.pop(); // Remove the dummy entry at the end.
  4640  
  4641    		return diffs;
  4642    	};
  4643  
  4644    	/**
  4645      * Find the 'middle snake' of a diff, split the problem in two
  4646      * and return the recursively constructed diff.
  4647      * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
  4648      * @param {string} text1 Old string to be diffed.
  4649      * @param {string} text2 New string to be diffed.
  4650      * @param {number} deadline Time at which to bail if not yet complete.
  4651      * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
  4652      * @private
  4653      */
  4654    	DiffMatchPatch.prototype.diffBisect = function (text1, text2, deadline) {
  4655    		var text1Length, text2Length, maxD, vOffset, vLength, v1, v2, x, delta, front, k1start, k1end, k2start, k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
  4656  
  4657    		// Cache the text lengths to prevent multiple calls.
  4658    		text1Length = text1.length;
  4659    		text2Length = text2.length;
  4660    		maxD = Math.ceil((text1Length + text2Length) / 2);
  4661    		vOffset = maxD;
  4662    		vLength = 2 * maxD;
  4663    		v1 = new Array(vLength);
  4664    		v2 = new Array(vLength);
  4665  
  4666    		// Setting all elements to -1 is faster in Chrome & Firefox than mixing
  4667    		// integers and undefined.
  4668    		for (x = 0; x < vLength; x++) {
  4669    			v1[x] = -1;
  4670    			v2[x] = -1;
  4671    		}
  4672    		v1[vOffset + 1] = 0;
  4673    		v2[vOffset + 1] = 0;
  4674    		delta = text1Length - text2Length;
  4675  
  4676    		// If the total number of characters is odd, then the front path will collide
  4677    		// with the reverse path.
  4678    		front = delta % 2 !== 0;
  4679  
  4680    		// Offsets for start and end of k loop.
  4681    		// Prevents mapping of space beyond the grid.
  4682    		k1start = 0;
  4683    		k1end = 0;
  4684    		k2start = 0;
  4685    		k2end = 0;
  4686    		for (d = 0; d < maxD; d++) {
  4687  
  4688    			// Bail out if deadline is reached.
  4689    			if (new Date().getTime() > deadline) {
  4690    				break;
  4691    			}
  4692  
  4693    			// Walk the front path one step.
  4694    			for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
  4695    				k1Offset = vOffset + k1;
  4696    				if (k1 === -d || k1 !== d && v1[k1Offset - 1] < v1[k1Offset + 1]) {
  4697    					x1 = v1[k1Offset + 1];
  4698    				} else {
  4699    					x1 = v1[k1Offset - 1] + 1;
  4700    				}
  4701    				y1 = x1 - k1;
  4702    				while (x1 < text1Length && y1 < text2Length && text1.charAt(x1) === text2.charAt(y1)) {
  4703    					x1++;
  4704    					y1++;
  4705    				}
  4706    				v1[k1Offset] = x1;
  4707    				if (x1 > text1Length) {
  4708  
  4709    					// Ran off the right of the graph.
  4710    					k1end += 2;
  4711    				} else if (y1 > text2Length) {
  4712  
  4713    					// Ran off the bottom of the graph.
  4714    					k1start += 2;
  4715    				} else if (front) {
  4716    					k2Offset = vOffset + delta - k1;
  4717    					if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) {
  4718  
  4719    						// Mirror x2 onto top-left coordinate system.
  4720    						x2 = text1Length - v2[k2Offset];
  4721    						if (x1 >= x2) {
  4722  
  4723    							// Overlap detected.
  4724    							return this.diffBisectSplit(text1, text2, x1, y1, deadline);
  4725    						}
  4726    					}
  4727    				}
  4728    			}
  4729  
  4730    			// Walk the reverse path one step.
  4731    			for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
  4732    				k2Offset = vOffset + k2;
  4733    				if (k2 === -d || k2 !== d && v2[k2Offset - 1] < v2[k2Offset + 1]) {
  4734    					x2 = v2[k2Offset + 1];
  4735    				} else {
  4736    					x2 = v2[k2Offset - 1] + 1;
  4737    				}
  4738    				y2 = x2 - k2;
  4739    				while (x2 < text1Length && y2 < text2Length && text1.charAt(text1Length - x2 - 1) === text2.charAt(text2Length - y2 - 1)) {
  4740    					x2++;
  4741    					y2++;
  4742    				}
  4743    				v2[k2Offset] = x2;
  4744    				if (x2 > text1Length) {
  4745  
  4746    					// Ran off the left of the graph.
  4747    					k2end += 2;
  4748    				} else if (y2 > text2Length) {
  4749  
  4750    					// Ran off the top of the graph.
  4751    					k2start += 2;
  4752    				} else if (!front) {
  4753    					k1Offset = vOffset + delta - k2;
  4754    					if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) {
  4755    						x1 = v1[k1Offset];
  4756    						y1 = vOffset + x1 - k1Offset;
  4757  
  4758    						// Mirror x2 onto top-left coordinate system.
  4759    						x2 = text1Length - x2;
  4760    						if (x1 >= x2) {
  4761  
  4762    							// Overlap detected.
  4763    							return this.diffBisectSplit(text1, text2, x1, y1, deadline);
  4764    						}
  4765    					}
  4766    				}
  4767    			}
  4768    		}
  4769  
  4770    		// Diff took too long and hit the deadline or
  4771    		// number of diffs equals number of characters, no commonality at all.
  4772    		return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
  4773    	};
  4774  
  4775    	/**
  4776      * Given the location of the 'middle snake', split the diff in two parts
  4777      * and recurse.
  4778      * @param {string} text1 Old string to be diffed.
  4779      * @param {string} text2 New string to be diffed.
  4780      * @param {number} x Index of split point in text1.
  4781      * @param {number} y Index of split point in text2.
  4782      * @param {number} deadline Time at which to bail if not yet complete.
  4783      * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
  4784      * @private
  4785      */
  4786    	DiffMatchPatch.prototype.diffBisectSplit = function (text1, text2, x, y, deadline) {
  4787    		var text1a, text1b, text2a, text2b, diffs, diffsb;
  4788    		text1a = text1.substring(0, x);
  4789    		text2a = text2.substring(0, y);
  4790    		text1b = text1.substring(x);
  4791    		text2b = text2.substring(y);
  4792  
  4793    		// Compute both diffs serially.
  4794    		diffs = this.DiffMain(text1a, text2a, false, deadline);
  4795    		diffsb = this.DiffMain(text1b, text2b, false, deadline);
  4796  
  4797    		return diffs.concat(diffsb);
  4798    	};
  4799  
  4800    	/**
  4801      * Reduce the number of edits by eliminating semantically trivial equalities.
  4802      * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
  4803      */
  4804    	DiffMatchPatch.prototype.diffCleanupSemantic = function (diffs) {
  4805    		var changes, equalities, equalitiesLength, lastequality, pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2;
  4806    		changes = false;
  4807    		equalities = []; // Stack of indices where equalities are found.
  4808    		equalitiesLength = 0; // Keeping our own length var is faster in JS.
  4809    		/** @type {?string} */
  4810    		lastequality = null;
  4811  
  4812    		// Always equal to diffs[equalities[equalitiesLength - 1]][1]
  4813    		pointer = 0; // Index of current position.
  4814  
  4815    		// Number of characters that changed prior to the equality.
  4816    		lengthInsertions1 = 0;
  4817    		lengthDeletions1 = 0;
  4818  
  4819    		// Number of characters that changed after the equality.
  4820    		lengthInsertions2 = 0;
  4821    		lengthDeletions2 = 0;
  4822    		while (pointer < diffs.length) {
  4823    			if (diffs[pointer][0] === DIFF_EQUAL) {
  4824    				// Equality found.
  4825    				equalities[equalitiesLength++] = pointer;
  4826    				lengthInsertions1 = lengthInsertions2;
  4827    				lengthDeletions1 = lengthDeletions2;
  4828    				lengthInsertions2 = 0;
  4829    				lengthDeletions2 = 0;
  4830    				lastequality = diffs[pointer][1];
  4831    			} else {
  4832    				// An insertion or deletion.
  4833    				if (diffs[pointer][0] === DIFF_INSERT) {
  4834    					lengthInsertions2 += diffs[pointer][1].length;
  4835    				} else {
  4836    					lengthDeletions2 += diffs[pointer][1].length;
  4837    				}
  4838  
  4839    				// Eliminate an equality that is smaller or equal to the edits on both
  4840    				// sides of it.
  4841    				if (lastequality && lastequality.length <= Math.max(lengthInsertions1, lengthDeletions1) && lastequality.length <= Math.max(lengthInsertions2, lengthDeletions2)) {
  4842  
  4843    					// Duplicate record.
  4844    					diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
  4845  
  4846    					// Change second copy to insert.
  4847    					diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
  4848  
  4849    					// Throw away the equality we just deleted.
  4850    					equalitiesLength--;
  4851  
  4852    					// Throw away the previous equality (it needs to be reevaluated).
  4853    					equalitiesLength--;
  4854    					pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
  4855  
  4856    					// Reset the counters.
  4857    					lengthInsertions1 = 0;
  4858    					lengthDeletions1 = 0;
  4859    					lengthInsertions2 = 0;
  4860    					lengthDeletions2 = 0;
  4861    					lastequality = null;
  4862    					changes = true;
  4863    				}
  4864    			}
  4865    			pointer++;
  4866    		}
  4867  
  4868    		// Normalize the diff.
  4869    		if (changes) {
  4870    			this.diffCleanupMerge(diffs);
  4871    		}
  4872  
  4873    		// Find any overlaps between deletions and insertions.
  4874    		// e.g: <del>abcxxx</del><ins>xxxdef</ins>
  4875    		//   -> <del>abc</del>xxx<ins>def</ins>
  4876    		// e.g: <del>xxxabc</del><ins>defxxx</ins>
  4877    		//   -> <ins>def</ins>xxx<del>abc</del>
  4878    		// Only extract an overlap if it is as big as the edit ahead or behind it.
  4879    		pointer = 1;
  4880    		while (pointer < diffs.length) {
  4881    			if (diffs[pointer - 1][0] === DIFF_DELETE && diffs[pointer][0] === DIFF_INSERT) {
  4882    				deletion = diffs[pointer - 1][1];
  4883    				insertion = diffs[pointer][1];
  4884    				overlapLength1 = this.diffCommonOverlap(deletion, insertion);
  4885    				overlapLength2 = this.diffCommonOverlap(insertion, deletion);
  4886    				if (overlapLength1 >= overlapLength2) {
  4887    					if (overlapLength1 >= deletion.length / 2 || overlapLength1 >= insertion.length / 2) {
  4888  
  4889    						// Overlap found.  Insert an equality and trim the surrounding edits.
  4890    						diffs.splice(pointer, 0, [DIFF_EQUAL, insertion.substring(0, overlapLength1)]);
  4891    						diffs[pointer - 1][1] = deletion.substring(0, deletion.length - overlapLength1);
  4892    						diffs[pointer + 1][1] = insertion.substring(overlapLength1);
  4893    						pointer++;
  4894    					}
  4895    				} else {
  4896    					if (overlapLength2 >= deletion.length / 2 || overlapLength2 >= insertion.length / 2) {
  4897  
  4898    						// Reverse overlap found.
  4899    						// Insert an equality and swap and trim the surrounding edits.
  4900    						diffs.splice(pointer, 0, [DIFF_EQUAL, deletion.substring(0, overlapLength2)]);
  4901  
  4902    						diffs[pointer - 1][0] = DIFF_INSERT;
  4903    						diffs[pointer - 1][1] = insertion.substring(0, insertion.length - overlapLength2);
  4904    						diffs[pointer + 1][0] = DIFF_DELETE;
  4905    						diffs[pointer + 1][1] = deletion.substring(overlapLength2);
  4906    						pointer++;
  4907    					}
  4908    				}
  4909    				pointer++;
  4910    			}
  4911    			pointer++;
  4912    		}
  4913    	};
  4914  
  4915    	/**
  4916      * Determine if the suffix of one string is the prefix of another.
  4917      * @param {string} text1 First string.
  4918      * @param {string} text2 Second string.
  4919      * @return {number} The number of characters common to the end of the first
  4920      *     string and the start of the second string.
  4921      * @private
  4922      */
  4923    	DiffMatchPatch.prototype.diffCommonOverlap = function (text1, text2) {
  4924    		var text1Length, text2Length, textLength, best, length, pattern, found;
  4925  
  4926    		// Cache the text lengths to prevent multiple calls.
  4927    		text1Length = text1.length;
  4928    		text2Length = text2.length;
  4929  
  4930    		// Eliminate the null case.
  4931    		if (text1Length === 0 || text2Length === 0) {
  4932    			return 0;
  4933    		}
  4934  
  4935    		// Truncate the longer string.
  4936    		if (text1Length > text2Length) {
  4937    			text1 = text1.substring(text1Length - text2Length);
  4938    		} else if (text1Length < text2Length) {
  4939    			text2 = text2.substring(0, text1Length);
  4940    		}
  4941    		textLength = Math.min(text1Length, text2Length);
  4942  
  4943    		// Quick check for the worst case.
  4944    		if (text1 === text2) {
  4945    			return textLength;
  4946    		}
  4947  
  4948    		// Start by looking for a single character match
  4949    		// and increase length until no match is found.
  4950    		// Performance analysis: https://neil.fraser.name/news/2010/11/04/
  4951    		best = 0;
  4952    		length = 1;
  4953    		while (true) {
  4954    			pattern = text1.substring(textLength - length);
  4955    			found = text2.indexOf(pattern);
  4956    			if (found === -1) {
  4957    				return best;
  4958    			}
  4959    			length += found;
  4960    			if (found === 0 || text1.substring(textLength - length) === text2.substring(0, length)) {
  4961    				best = length;
  4962    				length++;
  4963    			}
  4964    		}
  4965    	};
  4966  
  4967    	/**
  4968      * Split two texts into an array of strings.  Reduce the texts to a string of
  4969      * hashes where each Unicode character represents one line.
  4970      * @param {string} text1 First string.
  4971      * @param {string} text2 Second string.
  4972      * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
  4973      *     An object containing the encoded text1, the encoded text2 and
  4974      *     the array of unique strings.
  4975      *     The zeroth element of the array of unique strings is intentionally blank.
  4976      * @private
  4977      */
  4978    	DiffMatchPatch.prototype.diffLinesToChars = function (text1, text2) {
  4979    		var lineArray, lineHash, chars1, chars2;
  4980    		lineArray = []; // E.g. lineArray[4] === 'Hello\n'
  4981    		lineHash = {}; // E.g. lineHash['Hello\n'] === 4
  4982  
  4983    		// '\x00' is a valid character, but various debuggers don't like it.
  4984    		// So we'll insert a junk entry to avoid generating a null character.
  4985    		lineArray[0] = "";
  4986  
  4987    		/**
  4988       * Split a text into an array of strings.  Reduce the texts to a string of
  4989       * hashes where each Unicode character represents one line.
  4990       * Modifies linearray and linehash through being a closure.
  4991       * @param {string} text String to encode.
  4992       * @return {string} Encoded string.
  4993       * @private
  4994       */
  4995    		function diffLinesToCharsMunge(text) {
  4996    			var chars, lineStart, lineEnd, lineArrayLength, line;
  4997    			chars = "";
  4998  
  4999    			// Walk the text, pulling out a substring for each line.
  5000    			// text.split('\n') would would temporarily double our memory footprint.
  5001    			// Modifying text would create many large strings to garbage collect.
  5002    			lineStart = 0;
  5003    			lineEnd = -1;
  5004  
  5005    			// Keeping our own length variable is faster than looking it up.
  5006    			lineArrayLength = lineArray.length;
  5007    			while (lineEnd < text.length - 1) {
  5008    				lineEnd = text.indexOf("\n", lineStart);
  5009    				if (lineEnd === -1) {
  5010    					lineEnd = text.length - 1;
  5011    				}
  5012    				line = text.substring(lineStart, lineEnd + 1);
  5013    				lineStart = lineEnd + 1;
  5014  
  5015    				var lineHashExists = lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : lineHash[line] !== undefined;
  5016  
  5017    				if (lineHashExists) {
  5018    					chars += String.fromCharCode(lineHash[line]);
  5019    				} else {
  5020    					chars += String.fromCharCode(lineArrayLength);
  5021    					lineHash[line] = lineArrayLength;
  5022    					lineArray[lineArrayLength++] = line;
  5023    				}
  5024    			}
  5025    			return chars;
  5026    		}
  5027  
  5028    		chars1 = diffLinesToCharsMunge(text1);
  5029    		chars2 = diffLinesToCharsMunge(text2);
  5030    		return {
  5031    			chars1: chars1,
  5032    			chars2: chars2,
  5033    			lineArray: lineArray
  5034    		};
  5035    	};
  5036  
  5037    	/**
  5038      * Rehydrate the text in a diff from a string of line hashes to real lines of
  5039      * text.
  5040      * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
  5041      * @param {!Array.<string>} lineArray Array of unique strings.
  5042      * @private
  5043      */
  5044    	DiffMatchPatch.prototype.diffCharsToLines = function (diffs, lineArray) {
  5045    		var x, chars, text, y;
  5046    		for (x = 0; x < diffs.length; x++) {
  5047    			chars = diffs[x][1];
  5048    			text = [];
  5049    			for (y = 0; y < chars.length; y++) {
  5050    				text[y] = lineArray[chars.charCodeAt(y)];
  5051    			}
  5052    			diffs[x][1] = text.join("");
  5053    		}
  5054    	};
  5055  
  5056    	/**
  5057      * Reorder and merge like edit sections.  Merge equalities.
  5058      * Any edit section can move as long as it doesn't cross an equality.
  5059      * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
  5060      */
  5061    	DiffMatchPatch.prototype.diffCleanupMerge = function (diffs) {
  5062    		var pointer, countDelete, countInsert, textInsert, textDelete, commonlength, changes, diffPointer, position;
  5063    		diffs.push([DIFF_EQUAL, ""]); // Add a dummy entry at the end.
  5064    		pointer = 0;
  5065    		countDelete = 0;
  5066    		countInsert = 0;
  5067    		textDelete = "";
  5068    		textInsert = "";
  5069  
  5070    		while (pointer < diffs.length) {
  5071    			switch (diffs[pointer][0]) {
  5072    				case DIFF_INSERT:
  5073    					countInsert++;
  5074    					textInsert += diffs[pointer][1];
  5075    					pointer++;
  5076    					break;
  5077    				case DIFF_DELETE:
  5078    					countDelete++;
  5079    					textDelete += diffs[pointer][1];
  5080    					pointer++;
  5081    					break;
  5082    				case DIFF_EQUAL:
  5083  
  5084    					// Upon reaching an equality, check for prior redundancies.
  5085    					if (countDelete + countInsert > 1) {
  5086    						if (countDelete !== 0 && countInsert !== 0) {
  5087  
  5088    							// Factor out any common prefixes.
  5089    							commonlength = this.diffCommonPrefix(textInsert, textDelete);
  5090    							if (commonlength !== 0) {
  5091    								if (pointer - countDelete - countInsert > 0 && diffs[pointer - countDelete - countInsert - 1][0] === DIFF_EQUAL) {
  5092    									diffs[pointer - countDelete - countInsert - 1][1] += textInsert.substring(0, commonlength);
  5093    								} else {
  5094    									diffs.splice(0, 0, [DIFF_EQUAL, textInsert.substring(0, commonlength)]);
  5095    									pointer++;
  5096    								}
  5097    								textInsert = textInsert.substring(commonlength);
  5098    								textDelete = textDelete.substring(commonlength);
  5099    							}
  5100  
  5101    							// Factor out any common suffixies.
  5102    							commonlength = this.diffCommonSuffix(textInsert, textDelete);
  5103    							if (commonlength !== 0) {
  5104    								diffs[pointer][1] = textInsert.substring(textInsert.length - commonlength) + diffs[pointer][1];
  5105    								textInsert = textInsert.substring(0, textInsert.length - commonlength);
  5106    								textDelete = textDelete.substring(0, textDelete.length - commonlength);
  5107    							}
  5108    						}
  5109  
  5110    						// Delete the offending records and add the merged ones.
  5111    						if (countDelete === 0) {
  5112    							diffs.splice(pointer - countInsert, countDelete + countInsert, [DIFF_INSERT, textInsert]);
  5113    						} else if (countInsert === 0) {
  5114    							diffs.splice(pointer - countDelete, countDelete + countInsert, [DIFF_DELETE, textDelete]);
  5115    						} else {
  5116    							diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert, [DIFF_DELETE, textDelete], [DIFF_INSERT, textInsert]);
  5117    						}
  5118    						pointer = pointer - countDelete - countInsert + (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1;
  5119    					} else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) {
  5120  
  5121    						// Merge this equality with the previous one.
  5122    						diffs[pointer - 1][1] += diffs[pointer][1];
  5123    						diffs.splice(pointer, 1);
  5124    					} else {
  5125    						pointer++;
  5126    					}
  5127    					countInsert = 0;
  5128    					countDelete = 0;
  5129    					textDelete = "";
  5130    					textInsert = "";
  5131    					break;
  5132    			}
  5133    		}
  5134    		if (diffs[diffs.length - 1][1] === "") {
  5135    			diffs.pop(); // Remove the dummy entry at the end.
  5136    		}
  5137  
  5138    		// Second pass: look for single edits surrounded on both sides by equalities
  5139    		// which can be shifted sideways to eliminate an equality.
  5140    		// e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
  5141    		changes = false;
  5142    		pointer = 1;
  5143  
  5144    		// Intentionally ignore the first and last element (don't need checking).
  5145    		while (pointer < diffs.length - 1) {
  5146    			if (diffs[pointer - 1][0] === DIFF_EQUAL && diffs[pointer + 1][0] === DIFF_EQUAL) {
  5147  
  5148    				diffPointer = diffs[pointer][1];
  5149    				position = diffPointer.substring(diffPointer.length - diffs[pointer - 1][1].length);
  5150  
  5151    				// This is a single edit surrounded by equalities.
  5152    				if (position === diffs[pointer - 1][1]) {
  5153  
  5154    					// Shift the edit over the previous equality.
  5155    					diffs[pointer][1] = diffs[pointer - 1][1] + diffs[pointer][1].substring(0, diffs[pointer][1].length - diffs[pointer - 1][1].length);
  5156    					diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
  5157    					diffs.splice(pointer - 1, 1);
  5158    					changes = true;
  5159    				} else if (diffPointer.substring(0, diffs[pointer + 1][1].length) === diffs[pointer + 1][1]) {
  5160  
  5161    					// Shift the edit over the next equality.
  5162    					diffs[pointer - 1][1] += diffs[pointer + 1][1];
  5163    					diffs[pointer][1] = diffs[pointer][1].substring(diffs[pointer + 1][1].length) + diffs[pointer + 1][1];
  5164    					diffs.splice(pointer + 1, 1);
  5165    					changes = true;
  5166    				}
  5167    			}
  5168    			pointer++;
  5169    		}
  5170  
  5171    		// If shifts were made, the diff needs reordering and another shift sweep.
  5172    		if (changes) {
  5173    			this.diffCleanupMerge(diffs);
  5174    		}
  5175    	};
  5176  
  5177    	return function (o, n) {
  5178    		var diff, output, text;
  5179    		diff = new DiffMatchPatch();
  5180    		output = diff.DiffMain(o, n);
  5181    		diff.diffCleanupEfficiency(output);
  5182    		text = diff.diffPrettyHtml(output);
  5183  
  5184    		return text;
  5185    	};
  5186    }();
  5187  
  5188  }((function() { return this; }())));