]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - resources/lib/qunitjs/qunit.js
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / resources / lib / qunitjs / qunit.js
1 /*!
2  * QUnit 2.4.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: 2017-07-08T15:20Z
10  */
11 (function (global$1) {
12   'use strict';
13
14   global$1 = global$1 && 'default' in global$1 ? 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, 13);
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                 this.assert = new Assert(this);
1392         }
1393   }
1394
1395   Test.count = 0;
1396
1397   function getNotStartedModules(startModule) {
1398         var module = startModule,
1399             modules = [];
1400
1401         while (module && module.testsRun === 0) {
1402                 modules.push(module);
1403                 module = module.parentModule;
1404         }
1405
1406         return modules;
1407   }
1408
1409   Test.prototype = {
1410         before: function before() {
1411                 var i,
1412                     startModule,
1413                     module = this.module,
1414                     notStartedModules = getNotStartedModules(module);
1415
1416                 for (i = notStartedModules.length - 1; i >= 0; i--) {
1417                         startModule = notStartedModules[i];
1418                         startModule.stats = { all: 0, bad: 0, started: now() };
1419                         emit("suiteStart", startModule.suiteReport.start(true));
1420                         runLoggingCallbacks("moduleStart", {
1421                                 name: startModule.name,
1422                                 tests: startModule.tests
1423                         });
1424                 }
1425
1426                 config.current = this;
1427
1428                 this.testEnvironment = extend({}, module.testEnvironment);
1429
1430                 this.started = now();
1431                 emit("testStart", this.testReport.start(true));
1432                 runLoggingCallbacks("testStart", {
1433                         name: this.testName,
1434                         module: module.name,
1435                         testId: this.testId,
1436                         previousFailure: this.previousFailure
1437                 });
1438
1439                 if (!config.pollution) {
1440                         saveGlobal();
1441                 }
1442         },
1443
1444         run: function run() {
1445                 var promise;
1446
1447                 config.current = this;
1448
1449                 this.callbackStarted = now();
1450
1451                 if (config.notrycatch) {
1452                         runTest(this);
1453                         return;
1454                 }
1455
1456                 try {
1457                         runTest(this);
1458                 } catch (e) {
1459                         this.pushFailure("Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + (e.message || e), extractStacktrace(e, 0));
1460
1461                         // Else next test will carry the responsibility
1462                         saveGlobal();
1463
1464                         // Restart the tests if they're blocking
1465                         if (config.blocking) {
1466                                 internalRecover(this);
1467                         }
1468                 }
1469
1470                 function runTest(test) {
1471                         promise = test.callback.call(test.testEnvironment, test.assert);
1472                         test.resolvePromise(promise);
1473
1474                         // If the test has a "lock" on it, but the timeout is 0, then we push a
1475                         // failure as the test should be synchronous.
1476                         if (test.timeout === 0 && test.semaphore !== 0) {
1477                                 pushFailure("Test did not finish synchronously even though assert.timeout( 0 ) was used.", sourceFromStacktrace(2));
1478                         }
1479                 }
1480         },
1481
1482         after: function after() {
1483                 checkPollution();
1484         },
1485
1486         queueHook: function queueHook(hook, hookName, hookOwner) {
1487                 var _this = this;
1488
1489                 var callHook = function callHook() {
1490                         var promise = hook.call(_this.testEnvironment, _this.assert);
1491                         _this.resolvePromise(promise, hookName);
1492                 };
1493
1494                 var runHook = function runHook() {
1495                         if (hookName === "before") {
1496                                 if (hookOwner.unskippedTestsRun !== 0) {
1497                                         return;
1498                                 }
1499
1500                                 _this.preserveEnvironment = true;
1501                         }
1502
1503                         if (hookName === "after" && hookOwner.unskippedTestsRun !== numberOfUnskippedTests(hookOwner) - 1 && config.queue.length > 2) {
1504                                 return;
1505                         }
1506
1507                         config.current = _this;
1508                         if (config.notrycatch) {
1509                                 callHook();
1510                                 return;
1511                         }
1512                         try {
1513                                 callHook();
1514                         } catch (error) {
1515                                 _this.pushFailure(hookName + " failed on " + _this.testName + ": " + (error.message || error), extractStacktrace(error, 0));
1516                         }
1517                 };
1518
1519                 return runHook;
1520         },
1521
1522
1523         // Currently only used for module level hooks, can be used to add global level ones
1524         hooks: function hooks(handler) {
1525                 var hooks = [];
1526
1527                 function processHooks(test, module) {
1528                         if (module.parentModule) {
1529                                 processHooks(test, module.parentModule);
1530                         }
1531
1532                         if (module.hooks[handler].length) {
1533                                 for (var i = 0; i < module.hooks[handler].length; i++) {
1534                                         hooks.push(test.queueHook(module.hooks[handler][i], handler, module));
1535                                 }
1536                         }
1537                 }
1538
1539                 // Hooks are ignored on skipped tests
1540                 if (!this.skip) {
1541                         processHooks(this, this.module);
1542                 }
1543
1544                 return hooks;
1545         },
1546
1547
1548         finish: function finish() {
1549                 config.current = this;
1550                 if (config.requireExpects && this.expected === null) {
1551                         this.pushFailure("Expected number of assertions to be defined, but expect() was " + "not called.", this.stack);
1552                 } else if (this.expected !== null && this.expected !== this.assertions.length) {
1553                         this.pushFailure("Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack);
1554                 } else if (this.expected === null && !this.assertions.length) {
1555                         this.pushFailure("Expected at least one assertion, but none were run - call " + "expect(0) to accept zero assertions.", this.stack);
1556                 }
1557
1558                 var i,
1559                     module = this.module,
1560                     moduleName = module.name,
1561                     testName = this.testName,
1562                     skipped = !!this.skip,
1563                     todo = !!this.todo,
1564                     bad = 0,
1565                     storage = config.storage;
1566
1567                 this.runtime = now() - this.started;
1568
1569                 config.stats.all += this.assertions.length;
1570                 module.stats.all += this.assertions.length;
1571
1572                 for (i = 0; i < this.assertions.length; i++) {
1573                         if (!this.assertions[i].result) {
1574                                 bad++;
1575                                 config.stats.bad++;
1576                                 module.stats.bad++;
1577                         }
1578                 }
1579
1580                 notifyTestsRan(module, skipped);
1581
1582                 // Store result when possible
1583                 if (storage) {
1584                         if (bad) {
1585                                 storage.setItem("qunit-test-" + moduleName + "-" + testName, bad);
1586                         } else {
1587                                 storage.removeItem("qunit-test-" + moduleName + "-" + testName);
1588                         }
1589                 }
1590
1591                 // After emitting the js-reporters event we cleanup the assertion data to
1592                 // avoid leaking it. It is not used by the legacy testDone callbacks.
1593                 emit("testEnd", this.testReport.end(true));
1594                 this.testReport.slimAssertions();
1595
1596                 runLoggingCallbacks("testDone", {
1597                         name: testName,
1598                         module: moduleName,
1599                         skipped: skipped,
1600                         todo: todo,
1601                         failed: bad,
1602                         passed: this.assertions.length - bad,
1603                         total: this.assertions.length,
1604                         runtime: skipped ? 0 : this.runtime,
1605
1606                         // HTML Reporter use
1607                         assertions: this.assertions,
1608                         testId: this.testId,
1609
1610                         // Source of Test
1611                         source: this.stack
1612                 });
1613
1614                 if (module.testsRun === numberOfTests(module)) {
1615                         logSuiteEnd(module);
1616
1617                         // Check if the parent modules, iteratively, are done. If that the case,
1618                         // we emit the `suiteEnd` event and trigger `moduleDone` callback.
1619                         var parent = module.parentModule;
1620                         while (parent && parent.testsRun === numberOfTests(parent)) {
1621                                 logSuiteEnd(parent);
1622                                 parent = parent.parentModule;
1623                         }
1624                 }
1625
1626                 config.current = undefined;
1627
1628                 function logSuiteEnd(module) {
1629                         emit("suiteEnd", module.suiteReport.end(true));
1630                         runLoggingCallbacks("moduleDone", {
1631                                 name: module.name,
1632                                 tests: module.tests,
1633                                 failed: module.stats.bad,
1634                                 passed: module.stats.all - module.stats.bad,
1635                                 total: module.stats.all,
1636                                 runtime: now() - module.stats.started
1637                         });
1638                 }
1639         },
1640
1641         preserveTestEnvironment: function preserveTestEnvironment() {
1642                 if (this.preserveEnvironment) {
1643                         this.module.testEnvironment = this.testEnvironment;
1644                         this.testEnvironment = extend({}, this.module.testEnvironment);
1645                 }
1646         },
1647
1648         queue: function queue() {
1649                 var test = this;
1650
1651                 if (!this.valid()) {
1652                         return;
1653                 }
1654
1655                 function runTest() {
1656
1657                         // Each of these can by async
1658                         ProcessingQueue.addImmediate([function () {
1659                                 test.before();
1660                         }, test.hooks("before"), function () {
1661                                 test.preserveTestEnvironment();
1662                         }, test.hooks("beforeEach"), function () {
1663                                 test.run();
1664                         }, test.hooks("afterEach").reverse(), test.hooks("after").reverse(), function () {
1665                                 test.after();
1666                         }, function () {
1667                                 test.finish();
1668                         }]);
1669                 }
1670
1671                 var previousFailCount = config.storage && +config.storage.getItem("qunit-test-" + this.module.name + "-" + this.testName);
1672
1673                 // Prioritize previously failed tests, detected from storage
1674                 var prioritize = config.reorder && !!previousFailCount;
1675
1676                 this.previousFailure = !!previousFailCount;
1677
1678                 ProcessingQueue.add(runTest, prioritize, config.seed);
1679
1680                 // If the queue has already finished, we manually process the new test
1681                 if (ProcessingQueue.finished) {
1682                         ProcessingQueue.advance();
1683                 }
1684         },
1685
1686
1687         pushResult: function pushResult(resultInfo) {
1688                 if (this !== config.current) {
1689                         throw new Error("Assertion occured after test had finished.");
1690                 }
1691
1692                 // Destructure of resultInfo = { result, actual, expected, message, negative }
1693                 var source,
1694                     details = {
1695                         module: this.module.name,
1696                         name: this.testName,
1697                         result: resultInfo.result,
1698                         message: resultInfo.message,
1699                         actual: resultInfo.actual,
1700                         expected: resultInfo.expected,
1701                         testId: this.testId,
1702                         negative: resultInfo.negative || false,
1703                         runtime: now() - this.started,
1704                         todo: !!this.todo
1705                 };
1706
1707                 if (!resultInfo.result) {
1708                         source = resultInfo.source || sourceFromStacktrace();
1709
1710                         if (source) {
1711                                 details.source = source;
1712                         }
1713                 }
1714
1715                 this.logAssertion(details);
1716
1717                 this.assertions.push({
1718                         result: !!resultInfo.result,
1719                         message: resultInfo.message
1720                 });
1721         },
1722
1723         pushFailure: function pushFailure(message, source, actual) {
1724                 if (!(this instanceof Test)) {
1725                         throw new Error("pushFailure() assertion outside test context, was " + sourceFromStacktrace(2));
1726                 }
1727
1728                 this.pushResult({
1729                         result: false,
1730                         message: message || "error",
1731                         actual: actual || null,
1732                         expected: null,
1733                         source: source
1734                 });
1735         },
1736
1737         /**
1738     * Log assertion details using both the old QUnit.log interface and
1739     * QUnit.on( "assertion" ) interface.
1740     *
1741     * @private
1742     */
1743         logAssertion: function logAssertion(details) {
1744                 runLoggingCallbacks("log", details);
1745
1746                 var assertion = {
1747                         passed: details.result,
1748                         actual: details.actual,
1749                         expected: details.expected,
1750                         message: details.message,
1751                         stack: details.source,
1752                         todo: details.todo
1753                 };
1754                 this.testReport.pushAssertion(assertion);
1755                 emit("assertion", assertion);
1756         },
1757
1758
1759         resolvePromise: function resolvePromise(promise, phase) {
1760                 var then,
1761                     resume,
1762                     message,
1763                     test = this;
1764                 if (promise != null) {
1765                         then = promise.then;
1766                         if (objectType(then) === "function") {
1767                                 resume = internalStop(test);
1768                                 then.call(promise, function () {
1769                                         resume();
1770                                 }, function (error) {
1771                                         message = "Promise rejected " + (!phase ? "during" : phase.replace(/Each$/, "")) + " \"" + test.testName + "\": " + (error && error.message || error);
1772                                         test.pushFailure(message, extractStacktrace(error, 0));
1773
1774                                         // Else next test will carry the responsibility
1775                                         saveGlobal();
1776
1777                                         // Unblock
1778                                         resume();
1779                                 });
1780                         }
1781                 }
1782         },
1783
1784         valid: function valid() {
1785                 var filter = config.filter,
1786                     regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec(filter),
1787                     module = config.module && config.module.toLowerCase(),
1788                     fullName = this.module.name + ": " + this.testName;
1789
1790                 function moduleChainNameMatch(testModule) {
1791                         var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;
1792                         if (testModuleName === module) {
1793                                 return true;
1794                         } else if (testModule.parentModule) {
1795                                 return moduleChainNameMatch(testModule.parentModule);
1796                         } else {
1797                                 return false;
1798                         }
1799                 }
1800
1801                 function moduleChainIdMatch(testModule) {
1802                         return inArray(testModule.moduleId, config.moduleId) || testModule.parentModule && moduleChainIdMatch(testModule.parentModule);
1803                 }
1804
1805                 // Internally-generated tests are always valid
1806                 if (this.callback && this.callback.validTest) {
1807                         return true;
1808                 }
1809
1810                 if (config.moduleId && config.moduleId.length > 0 && !moduleChainIdMatch(this.module)) {
1811
1812                         return false;
1813                 }
1814
1815                 if (config.testId && config.testId.length > 0 && !inArray(this.testId, config.testId)) {
1816
1817                         return false;
1818                 }
1819
1820                 if (module && !moduleChainNameMatch(this.module)) {
1821                         return false;
1822                 }
1823
1824                 if (!filter) {
1825                         return true;
1826                 }
1827
1828                 return regexFilter ? this.regexFilter(!!regexFilter[1], regexFilter[2], regexFilter[3], fullName) : this.stringFilter(filter, fullName);
1829         },
1830
1831         regexFilter: function regexFilter(exclude, pattern, flags, fullName) {
1832                 var regex = new RegExp(pattern, flags);
1833                 var match = regex.test(fullName);
1834
1835                 return match !== exclude;
1836         },
1837
1838         stringFilter: function stringFilter(filter, fullName) {
1839                 filter = filter.toLowerCase();
1840                 fullName = fullName.toLowerCase();
1841
1842                 var include = filter.charAt(0) !== "!";
1843                 if (!include) {
1844                         filter = filter.slice(1);
1845                 }
1846
1847                 // If the filter matches, we need to honour include
1848                 if (fullName.indexOf(filter) !== -1) {
1849                         return include;
1850                 }
1851
1852                 // Otherwise, do the opposite
1853                 return !include;
1854         }
1855   };
1856
1857   function pushFailure() {
1858         if (!config.current) {
1859                 throw new Error("pushFailure() assertion outside test context, in " + sourceFromStacktrace(2));
1860         }
1861
1862         // Gets current test obj
1863         var currentTest = config.current;
1864
1865         return currentTest.pushFailure.apply(currentTest, arguments);
1866   }
1867
1868   function saveGlobal() {
1869         config.pollution = [];
1870
1871         if (config.noglobals) {
1872                 for (var key in global$1) {
1873                         if (hasOwn.call(global$1, key)) {
1874
1875                                 // In Opera sometimes DOM element ids show up here, ignore them
1876                                 if (/^qunit-test-output/.test(key)) {
1877                                         continue;
1878                                 }
1879                                 config.pollution.push(key);
1880                         }
1881                 }
1882         }
1883   }
1884
1885   function checkPollution() {
1886         var newGlobals,
1887             deletedGlobals,
1888             old = config.pollution;
1889
1890         saveGlobal();
1891
1892         newGlobals = diff(config.pollution, old);
1893         if (newGlobals.length > 0) {
1894                 pushFailure("Introduced global variable(s): " + newGlobals.join(", "));
1895         }
1896
1897         deletedGlobals = diff(old, config.pollution);
1898         if (deletedGlobals.length > 0) {
1899                 pushFailure("Deleted global variable(s): " + deletedGlobals.join(", "));
1900         }
1901   }
1902
1903   // Will be exposed as QUnit.test
1904   function test(testName, callback) {
1905         if (focused$1) {
1906                 return;
1907         }
1908
1909         var newTest = new Test({
1910                 testName: testName,
1911                 callback: callback
1912         });
1913
1914         newTest.queue();
1915   }
1916
1917   function todo(testName, callback) {
1918         if (focused$1) {
1919                 return;
1920         }
1921
1922         var newTest = new Test({
1923                 testName: testName,
1924                 callback: callback,
1925                 todo: true
1926         });
1927
1928         newTest.queue();
1929   }
1930
1931   // Will be exposed as QUnit.skip
1932   function skip(testName) {
1933         if (focused$1) {
1934                 return;
1935         }
1936
1937         var test = new Test({
1938                 testName: testName,
1939                 skip: true
1940         });
1941
1942         test.queue();
1943   }
1944
1945   // Will be exposed as QUnit.only
1946   function only(testName, callback) {
1947         if (focused$1) {
1948                 return;
1949         }
1950
1951         config.queue.length = 0;
1952         focused$1 = true;
1953
1954         var newTest = new Test({
1955                 testName: testName,
1956                 callback: callback
1957         });
1958
1959         newTest.queue();
1960   }
1961
1962   // Put a hold on processing and return a function that will release it.
1963   function internalStop(test) {
1964         test.semaphore += 1;
1965         config.blocking = true;
1966
1967         // Set a recovery timeout, if so configured.
1968         if (defined.setTimeout) {
1969                 var timeoutDuration = void 0;
1970
1971                 if (typeof test.timeout === "number") {
1972                         timeoutDuration = test.timeout;
1973                 } else if (typeof config.testTimeout === "number") {
1974                         timeoutDuration = config.testTimeout;
1975                 }
1976
1977                 if (typeof timeoutDuration === "number" && timeoutDuration > 0) {
1978                         clearTimeout(config.timeout);
1979                         config.timeout = setTimeout(function () {
1980                                 pushFailure("Test took longer than " + timeoutDuration + "ms; test timed out.", sourceFromStacktrace(2));
1981                                 internalRecover(test);
1982                         }, timeoutDuration);
1983                 }
1984         }
1985
1986         var released = false;
1987         return function resume() {
1988                 if (released) {
1989                         return;
1990                 }
1991
1992                 released = true;
1993                 test.semaphore -= 1;
1994                 internalStart(test);
1995         };
1996   }
1997
1998   // Forcefully release all processing holds.
1999   function internalRecover(test) {
2000         test.semaphore = 0;
2001         internalStart(test);
2002   }
2003
2004   // Release a processing hold, scheduling a resumption attempt if no holds remain.
2005   function internalStart(test) {
2006
2007         // If semaphore is non-numeric, throw error
2008         if (isNaN(test.semaphore)) {
2009                 test.semaphore = 0;
2010
2011                 pushFailure("Invalid value on test.semaphore", sourceFromStacktrace(2));
2012                 return;
2013         }
2014
2015         // Don't start until equal number of stop-calls
2016         if (test.semaphore > 0) {
2017                 return;
2018         }
2019
2020         // Throw an Error if start is called more often than stop
2021         if (test.semaphore < 0) {
2022                 test.semaphore = 0;
2023
2024                 pushFailure("Tried to restart test while already started (test's semaphore was 0 already)", sourceFromStacktrace(2));
2025                 return;
2026         }
2027
2028         // Add a slight delay to allow more assertions etc.
2029         if (defined.setTimeout) {
2030                 if (config.timeout) {
2031                         clearTimeout(config.timeout);
2032                 }
2033                 config.timeout = setTimeout(function () {
2034                         if (test.semaphore > 0) {
2035                                 return;
2036                         }
2037
2038                         if (config.timeout) {
2039                                 clearTimeout(config.timeout);
2040                         }
2041
2042                         begin();
2043                 }, 13);
2044         } else {
2045                 begin();
2046         }
2047   }
2048
2049   function collectTests(module) {
2050         var tests = [].concat(module.tests);
2051         var modules = [].concat(toConsumableArray(module.childModules));
2052
2053         // Do a breadth-first traversal of the child modules
2054         while (modules.length) {
2055                 var nextModule = modules.shift();
2056                 tests.push.apply(tests, nextModule.tests);
2057                 modules.push.apply(modules, toConsumableArray(nextModule.childModules));
2058         }
2059
2060         return tests;
2061   }
2062
2063   function numberOfTests(module) {
2064         return collectTests(module).length;
2065   }
2066
2067   function numberOfUnskippedTests(module) {
2068         return collectTests(module).filter(function (test) {
2069                 return !test.skip;
2070         }).length;
2071   }
2072
2073   function notifyTestsRan(module, skipped) {
2074         module.testsRun++;
2075         if (!skipped) {
2076                 module.unskippedTestsRun++;
2077         }
2078         while (module = module.parentModule) {
2079                 module.testsRun++;
2080                 if (!skipped) {
2081                         module.unskippedTestsRun++;
2082                 }
2083         }
2084   }
2085
2086   /**
2087    * Returns a function that proxies to the given method name on the globals
2088    * console object. The proxy will also detect if the console doesn't exist and
2089    * will appropriately no-op. This allows support for IE9, which doesn't have a
2090    * console if the developer tools are not open.
2091    */
2092   function consoleProxy(method) {
2093         return function () {
2094                 if (console) {
2095                         console[method].apply(console, arguments);
2096                 }
2097         };
2098   }
2099
2100   var Logger = {
2101         warn: consoleProxy("warn")
2102   };
2103
2104   var Assert = function () {
2105         function Assert(testContext) {
2106                 classCallCheck(this, Assert);
2107
2108                 this.test = testContext;
2109         }
2110
2111         // Assert helpers
2112
2113         createClass(Assert, [{
2114                 key: "timeout",
2115                 value: function timeout(duration) {
2116                         if (typeof duration !== "number") {
2117                                 throw new Error("You must pass a number as the duration to assert.timeout");
2118                         }
2119
2120                         this.test.timeout = duration;
2121                 }
2122
2123                 // Documents a "step", which is a string value, in a test as a passing assertion
2124
2125         }, {
2126                 key: "step",
2127                 value: function step(message) {
2128                         var result = !!message;
2129
2130                         this.test.steps.push(message);
2131
2132                         return this.pushResult({
2133                                 result: result,
2134                                 message: message || "You must provide a message to assert.step"
2135                         });
2136                 }
2137
2138                 // Verifies the steps in a test match a given array of string values
2139
2140         }, {
2141                 key: "verifySteps",
2142                 value: function verifySteps(steps, message) {
2143                         this.deepEqual(this.test.steps, steps, message);
2144                 }
2145
2146                 // Specify the number of expected assertions to guarantee that failed test
2147                 // (no assertions are run at all) don't slip through.
2148
2149         }, {
2150                 key: "expect",
2151                 value: function expect(asserts) {
2152                         if (arguments.length === 1) {
2153                                 this.test.expected = asserts;
2154                         } else {
2155                                 return this.test.expected;
2156                         }
2157                 }
2158
2159                 // Put a hold on processing and return a function that will release it a maximum of once.
2160
2161         }, {
2162                 key: "async",
2163                 value: function async(count) {
2164                         var test$$1 = this.test;
2165
2166                         var popped = false,
2167                             acceptCallCount = count;
2168
2169                         if (typeof acceptCallCount === "undefined") {
2170                                 acceptCallCount = 1;
2171                         }
2172
2173                         var resume = internalStop(test$$1);
2174
2175                         return function done() {
2176                                 if (config.current !== test$$1) {
2177                                         throw Error("assert.async callback called after test finished.");
2178                                 }
2179
2180                                 if (popped) {
2181                                         test$$1.pushFailure("Too many calls to the `assert.async` callback", sourceFromStacktrace(2));
2182                                         return;
2183                                 }
2184
2185                                 acceptCallCount -= 1;
2186                                 if (acceptCallCount > 0) {
2187                                         return;
2188                                 }
2189
2190                                 popped = true;
2191                                 resume();
2192                         };
2193                 }
2194
2195                 // Exports test.push() to the user API
2196                 // Alias of pushResult.
2197
2198         }, {
2199                 key: "push",
2200                 value: function push(result, actual, expected, message, negative) {
2201                         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).");
2202
2203                         var currentAssert = this instanceof Assert ? this : config.current.assert;
2204                         return currentAssert.pushResult({
2205                                 result: result,
2206                                 actual: actual,
2207                                 expected: expected,
2208                                 message: message,
2209                                 negative: negative
2210                         });
2211                 }
2212         }, {
2213                 key: "pushResult",
2214                 value: function pushResult(resultInfo) {
2215
2216                         // Destructure of resultInfo = { result, actual, expected, message, negative }
2217                         var assert = this;
2218                         var currentTest = assert instanceof Assert && assert.test || config.current;
2219
2220                         // Backwards compatibility fix.
2221                         // Allows the direct use of global exported assertions and QUnit.assert.*
2222                         // Although, it's use is not recommended as it can leak assertions
2223                         // to other tests from async tests, because we only get a reference to the current test,
2224                         // not exactly the test where assertion were intended to be called.
2225                         if (!currentTest) {
2226                                 throw new Error("assertion outside test context, in " + sourceFromStacktrace(2));
2227                         }
2228
2229                         if (!(assert instanceof Assert)) {
2230                                 assert = currentTest.assert;
2231                         }
2232
2233                         return assert.test.pushResult(resultInfo);
2234                 }
2235         }, {
2236                 key: "ok",
2237                 value: function ok(result, message) {
2238                         if (!message) {
2239                                 message = result ? "okay" : "failed, expected argument to be truthy, was: " + dump.parse(result);
2240                         }
2241
2242                         this.pushResult({
2243                                 result: !!result,
2244                                 actual: result,
2245                                 expected: true,
2246                                 message: message
2247                         });
2248                 }
2249         }, {
2250                 key: "notOk",
2251                 value: function notOk(result, message) {
2252                         if (!message) {
2253                                 message = !result ? "okay" : "failed, expected argument to be falsy, was: " + dump.parse(result);
2254                         }
2255
2256                         this.pushResult({
2257                                 result: !result,
2258                                 actual: result,
2259                                 expected: false,
2260                                 message: message
2261                         });
2262                 }
2263         }, {
2264                 key: "equal",
2265                 value: function equal(actual, expected, message) {
2266
2267                         // eslint-disable-next-line eqeqeq
2268                         var result = expected == actual;
2269
2270                         this.pushResult({
2271                                 result: result,
2272                                 actual: actual,
2273                                 expected: expected,
2274                                 message: message
2275                         });
2276                 }
2277         }, {
2278                 key: "notEqual",
2279                 value: function notEqual(actual, expected, message) {
2280
2281                         // eslint-disable-next-line eqeqeq
2282                         var result = expected != actual;
2283
2284                         this.pushResult({
2285                                 result: result,
2286                                 actual: actual,
2287                                 expected: expected,
2288                                 message: message,
2289                                 negative: true
2290                         });
2291                 }
2292         }, {
2293                 key: "propEqual",
2294                 value: function propEqual(actual, expected, message) {
2295                         actual = objectValues(actual);
2296                         expected = objectValues(expected);
2297
2298                         this.pushResult({
2299                                 result: equiv(actual, expected),
2300                                 actual: actual,
2301                                 expected: expected,
2302                                 message: message
2303                         });
2304                 }
2305         }, {
2306                 key: "notPropEqual",
2307                 value: function notPropEqual(actual, expected, message) {
2308                         actual = objectValues(actual);
2309                         expected = objectValues(expected);
2310
2311                         this.pushResult({
2312                                 result: !equiv(actual, expected),
2313                                 actual: actual,
2314                                 expected: expected,
2315                                 message: message,
2316                                 negative: true
2317                         });
2318                 }
2319         }, {
2320                 key: "deepEqual",
2321                 value: function deepEqual(actual, expected, message) {
2322                         this.pushResult({
2323                                 result: equiv(actual, expected),
2324                                 actual: actual,
2325                                 expected: expected,
2326                                 message: message
2327                         });
2328                 }
2329         }, {
2330                 key: "notDeepEqual",
2331                 value: function notDeepEqual(actual, expected, message) {
2332                         this.pushResult({
2333                                 result: !equiv(actual, expected),
2334                                 actual: actual,
2335                                 expected: expected,
2336                                 message: message,
2337                                 negative: true
2338                         });
2339                 }
2340         }, {
2341                 key: "strictEqual",
2342                 value: function strictEqual(actual, expected, message) {
2343                         this.pushResult({
2344                                 result: expected === actual,
2345                                 actual: actual,
2346                                 expected: expected,
2347                                 message: message
2348                         });
2349                 }
2350         }, {
2351                 key: "notStrictEqual",
2352                 value: function notStrictEqual(actual, expected, message) {
2353                         this.pushResult({
2354                                 result: expected !== actual,
2355                                 actual: actual,
2356                                 expected: expected,
2357                                 message: message,
2358                                 negative: true
2359                         });
2360                 }
2361         }, {
2362                 key: "throws",
2363                 value: function throws(block, expected, message) {
2364                         var actual = void 0,
2365                             result = false;
2366
2367                         var currentTest = this instanceof Assert && this.test || config.current;
2368
2369                         // 'expected' is optional unless doing string comparison
2370                         if (objectType(expected) === "string") {
2371                                 if (message == null) {
2372                                         message = expected;
2373                                         expected = null;
2374                                 } else {
2375                                         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.");
2376                                 }
2377                         }
2378
2379                         currentTest.ignoreGlobalErrors = true;
2380                         try {
2381                                 block.call(currentTest.testEnvironment);
2382                         } catch (e) {
2383                                 actual = e;
2384                         }
2385                         currentTest.ignoreGlobalErrors = false;
2386
2387                         if (actual) {
2388                                 var expectedType = objectType(expected);
2389
2390                                 // We don't want to validate thrown error
2391                                 if (!expected) {
2392                                         result = true;
2393                                         expected = null;
2394
2395                                         // Expected is a regexp
2396                                 } else if (expectedType === "regexp") {
2397                                         result = expected.test(errorString(actual));
2398
2399                                         // Expected is a constructor, maybe an Error constructor
2400                                 } else if (expectedType === "function" && actual instanceof expected) {
2401                                         result = true;
2402
2403                                         // Expected is an Error object
2404                                 } else if (expectedType === "object") {
2405                                         result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
2406
2407                                         // Expected is a validation function which returns true if validation passed
2408                                 } else if (expectedType === "function" && expected.call({}, actual) === true) {
2409                                         expected = null;
2410                                         result = true;
2411                                 }
2412                         }
2413
2414                         currentTest.assert.pushResult({
2415                                 result: result,
2416                                 actual: actual,
2417                                 expected: expected,
2418                                 message: message
2419                         });
2420                 }
2421         }]);
2422         return Assert;
2423   }();
2424
2425   // Provide an alternative to assert.throws(), for environments that consider throws a reserved word
2426   // Known to us are: Closure Compiler, Narwhal
2427   // eslint-disable-next-line dot-notation
2428
2429
2430   Assert.prototype.raises = Assert.prototype["throws"];
2431
2432   /**
2433    * Converts an error into a simple string for comparisons.
2434    *
2435    * @param {Error} error
2436    * @return {String}
2437    */
2438   function errorString(error) {
2439         var resultErrorString = error.toString();
2440
2441         if (resultErrorString.substring(0, 7) === "[object") {
2442                 var name = error.name ? error.name.toString() : "Error";
2443                 var message = error.message ? error.message.toString() : "";
2444
2445                 if (name && message) {
2446                         return name + ": " + message;
2447                 } else if (name) {
2448                         return name;
2449                 } else if (message) {
2450                         return message;
2451                 } else {
2452                         return "Error";
2453                 }
2454         } else {
2455                 return resultErrorString;
2456         }
2457   }
2458
2459   /* global module, exports, define */
2460   function exportQUnit(QUnit) {
2461
2462         if (defined.document) {
2463
2464                 // QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined.
2465                 if (window.QUnit && window.QUnit.version) {
2466                         throw new Error("QUnit has already been defined.");
2467                 }
2468
2469                 window.QUnit = QUnit;
2470         }
2471
2472         // For nodejs
2473         if (typeof module !== "undefined" && module && module.exports) {
2474                 module.exports = QUnit;
2475
2476                 // For consistency with CommonJS environments' exports
2477                 module.exports.QUnit = QUnit;
2478         }
2479
2480         // For CommonJS with exports, but without module.exports, like Rhino
2481         if (typeof exports !== "undefined" && exports) {
2482                 exports.QUnit = QUnit;
2483         }
2484
2485         if (typeof define === "function" && define.amd) {
2486                 define(function () {
2487                         return QUnit;
2488                 });
2489                 QUnit.config.autostart = false;
2490         }
2491
2492         // For Web/Service Workers
2493         if (self$1 && self$1.WorkerGlobalScope && self$1 instanceof self$1.WorkerGlobalScope) {
2494                 self$1.QUnit = QUnit;
2495         }
2496   }
2497
2498   var SuiteReport = function () {
2499         function SuiteReport(name, parentSuite) {
2500                 classCallCheck(this, SuiteReport);
2501
2502                 this.name = name;
2503                 this.fullName = parentSuite ? parentSuite.fullName.concat(name) : [];
2504
2505                 this.tests = [];
2506                 this.childSuites = [];
2507
2508                 if (parentSuite) {
2509                         parentSuite.pushChildSuite(this);
2510                 }
2511         }
2512
2513         createClass(SuiteReport, [{
2514                 key: "start",
2515                 value: function start(recordTime) {
2516                         if (recordTime) {
2517                                 this._startTime = Date.now();
2518                         }
2519
2520                         return {
2521                                 name: this.name,
2522                                 fullName: this.fullName.slice(),
2523                                 tests: this.tests.map(function (test) {
2524                                         return test.start();
2525                                 }),
2526                                 childSuites: this.childSuites.map(function (suite) {
2527                                         return suite.start();
2528                                 }),
2529                                 testCounts: {
2530                                         total: this.getTestCounts().total
2531                                 }
2532                         };
2533                 }
2534         }, {
2535                 key: "end",
2536                 value: function end(recordTime) {
2537                         if (recordTime) {
2538                                 this._endTime = Date.now();
2539                         }
2540
2541                         return {
2542                                 name: this.name,
2543                                 fullName: this.fullName.slice(),
2544                                 tests: this.tests.map(function (test) {
2545                                         return test.end();
2546                                 }),
2547                                 childSuites: this.childSuites.map(function (suite) {
2548                                         return suite.end();
2549                                 }),
2550                                 testCounts: this.getTestCounts(),
2551                                 runtime: this.getRuntime(),
2552                                 status: this.getStatus()
2553                         };
2554                 }
2555         }, {
2556                 key: "pushChildSuite",
2557                 value: function pushChildSuite(suite) {
2558                         this.childSuites.push(suite);
2559                 }
2560         }, {
2561                 key: "pushTest",
2562                 value: function pushTest(test) {
2563                         this.tests.push(test);
2564                 }
2565         }, {
2566                 key: "getRuntime",
2567                 value: function getRuntime() {
2568                         return this._endTime - this._startTime;
2569                 }
2570         }, {
2571                 key: "getTestCounts",
2572                 value: function getTestCounts() {
2573                         var counts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { passed: 0, failed: 0, skipped: 0, todo: 0, total: 0 };
2574
2575                         counts = this.tests.reduce(function (counts, test) {
2576                                 if (test.valid) {
2577                                         counts[test.getStatus()]++;
2578                                         counts.total++;
2579                                 }
2580
2581                                 return counts;
2582                         }, counts);
2583
2584                         return this.childSuites.reduce(function (counts, suite) {
2585                                 return suite.getTestCounts(counts);
2586                         }, counts);
2587                 }
2588         }, {
2589                 key: "getStatus",
2590                 value: function getStatus() {
2591                         var _getTestCounts = this.getTestCounts(),
2592                             total = _getTestCounts.total,
2593                             failed = _getTestCounts.failed,
2594                             skipped = _getTestCounts.skipped,
2595                             todo = _getTestCounts.todo;
2596
2597                         if (failed) {
2598                                 return "failed";
2599                         } else {
2600                                 if (skipped === total) {
2601                                         return "skipped";
2602                                 } else if (todo === total) {
2603                                         return "todo";
2604                                 } else {
2605                                         return "passed";
2606                                 }
2607                         }
2608                 }
2609         }]);
2610         return SuiteReport;
2611   }();
2612
2613   // Handle an unhandled exception. By convention, returns true if further
2614   // error handling should be suppressed and false otherwise.
2615   // In this case, we will only suppress further error handling if the
2616   // "ignoreGlobalErrors" configuration option is enabled.
2617   function onError(error) {
2618         for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
2619                 args[_key - 1] = arguments[_key];
2620         }
2621
2622         if (config.current) {
2623                 if (config.current.ignoreGlobalErrors) {
2624                         return true;
2625                 }
2626                 pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args));
2627         } else {
2628                 test("global failure", extend(function () {
2629                         pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args));
2630                 }, { validTest: true }));
2631         }
2632
2633         return false;
2634   }
2635
2636   var focused = false;
2637   var QUnit = {};
2638   var globalSuite = new SuiteReport();
2639
2640   // The initial "currentModule" represents the global (or top-level) module that
2641   // is not explicitly defined by the user, therefore we add the "globalSuite" to
2642   // it since each module has a suiteReport associated with it.
2643   config.currentModule.suiteReport = globalSuite;
2644
2645   var moduleStack = [];
2646   var globalStartCalled = false;
2647   var runStarted = false;
2648
2649   // Figure out if we're running the tests from a server or not
2650   QUnit.isLocal = !(defined.document && window.location.protocol !== "file:");
2651
2652   // Expose the current QUnit version
2653   QUnit.version = "2.4.0";
2654
2655   function createModule(name, testEnvironment, modifiers) {
2656         var parentModule = moduleStack.length ? moduleStack.slice(-1)[0] : null;
2657         var moduleName = parentModule !== null ? [parentModule.name, name].join(" > ") : name;
2658         var parentSuite = parentModule ? parentModule.suiteReport : globalSuite;
2659
2660         var skip$$1 = parentModule !== null && parentModule.skip || modifiers.skip;
2661         var todo$$1 = parentModule !== null && parentModule.todo || modifiers.todo;
2662
2663         var module = {
2664                 name: moduleName,
2665                 parentModule: parentModule,
2666                 tests: [],
2667                 moduleId: generateHash(moduleName),
2668                 testsRun: 0,
2669                 unskippedTestsRun: 0,
2670                 childModules: [],
2671                 suiteReport: new SuiteReport(name, parentSuite),
2672
2673                 // Pass along `skip` and `todo` properties from parent module, in case
2674                 // there is one, to childs. And use own otherwise.
2675                 // This property will be used to mark own tests and tests of child suites
2676                 // as either `skipped` or `todo`.
2677                 skip: skip$$1,
2678                 todo: skip$$1 ? false : todo$$1
2679         };
2680
2681         var env = {};
2682         if (parentModule) {
2683                 parentModule.childModules.push(module);
2684                 extend(env, parentModule.testEnvironment);
2685         }
2686         extend(env, testEnvironment);
2687         module.testEnvironment = env;
2688
2689         config.modules.push(module);
2690         return module;
2691   }
2692
2693   function processModule(name, options, executeNow) {
2694         var modifiers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
2695
2696         var module = createModule(name, options, modifiers);
2697
2698         // Move any hooks to a 'hooks' object
2699         var testEnvironment = module.testEnvironment;
2700         var hooks = module.hooks = {};
2701
2702         setHookFromEnvironment(hooks, testEnvironment, "before");
2703         setHookFromEnvironment(hooks, testEnvironment, "beforeEach");
2704         setHookFromEnvironment(hooks, testEnvironment, "afterEach");
2705         setHookFromEnvironment(hooks, testEnvironment, "after");
2706
2707         function setHookFromEnvironment(hooks, environment, name) {
2708                 var potentialHook = environment[name];
2709                 hooks[name] = typeof potentialHook === "function" ? [potentialHook] : [];
2710                 delete environment[name];
2711         }
2712
2713         var moduleFns = {
2714                 before: setHookFunction(module, "before"),
2715                 beforeEach: setHookFunction(module, "beforeEach"),
2716                 afterEach: setHookFunction(module, "afterEach"),
2717                 after: setHookFunction(module, "after")
2718         };
2719
2720         var currentModule = config.currentModule;
2721         if (objectType(executeNow) === "function") {
2722                 moduleStack.push(module);
2723                 config.currentModule = module;
2724                 executeNow.call(module.testEnvironment, moduleFns);
2725                 moduleStack.pop();
2726                 module = module.parentModule || currentModule;
2727         }
2728
2729         config.currentModule = module;
2730   }
2731
2732   // TODO: extract this to a new file alongside its related functions
2733   function module$1(name, options, executeNow) {
2734         if (focused) {
2735                 return;
2736         }
2737
2738         if (arguments.length === 2) {
2739                 if (objectType(options) === "function") {
2740                         executeNow = options;
2741                         options = undefined;
2742                 }
2743         }
2744
2745         processModule(name, options, executeNow);
2746   }
2747
2748   module$1.only = function () {
2749         if (focused) {
2750                 return;
2751         }
2752
2753         config.modules.length = 0;
2754         config.queue.length = 0;
2755
2756         module$1.apply(undefined, arguments);
2757
2758         focused = true;
2759   };
2760
2761   module$1.skip = function (name, options, executeNow) {
2762         if (focused) {
2763                 return;
2764         }
2765
2766         if (arguments.length === 2) {
2767                 if (objectType(options) === "function") {
2768                         executeNow = options;
2769                         options = undefined;
2770                 }
2771         }
2772
2773         processModule(name, options, executeNow, { skip: true });
2774   };
2775
2776   module$1.todo = function (name, options, executeNow) {
2777         if (focused) {
2778                 return;
2779         }
2780
2781         if (arguments.length === 2) {
2782                 if (objectType(options) === "function") {
2783                         executeNow = options;
2784                         options = undefined;
2785                 }
2786         }
2787
2788         processModule(name, options, executeNow, { todo: true });
2789   };
2790
2791   extend(QUnit, {
2792         on: on,
2793
2794         module: module$1,
2795
2796         test: test,
2797
2798         todo: todo,
2799
2800         skip: skip,
2801
2802         only: only,
2803
2804         start: function start(count) {
2805                 var globalStartAlreadyCalled = globalStartCalled;
2806
2807                 if (!config.current) {
2808                         globalStartCalled = true;
2809
2810                         if (runStarted) {
2811                                 throw new Error("Called start() while test already started running");
2812                         } else if (globalStartAlreadyCalled || count > 1) {
2813                                 throw new Error("Called start() outside of a test context too many times");
2814                         } else if (config.autostart) {
2815                                 throw new Error("Called start() outside of a test context when " + "QUnit.config.autostart was true");
2816                         } else if (!config.pageLoaded) {
2817
2818                                 // The page isn't completely loaded yet, so we set autostart and then
2819                                 // load if we're in Node or wait for the browser's load event.
2820                                 config.autostart = true;
2821
2822                                 // Starts from Node even if .load was not previously called. We still return
2823                                 // early otherwise we'll wind up "beginning" twice.
2824                                 if (!defined.document) {
2825                                         QUnit.load();
2826                                 }
2827
2828                                 return;
2829                         }
2830                 } else {
2831                         throw new Error("QUnit.start cannot be called inside a test context.");
2832                 }
2833
2834                 scheduleBegin();
2835         },
2836
2837         config: config,
2838
2839         is: is,
2840
2841         objectType: objectType,
2842
2843         extend: extend,
2844
2845         load: function load() {
2846                 config.pageLoaded = true;
2847
2848                 // Initialize the configuration options
2849                 extend(config, {
2850                         stats: { all: 0, bad: 0 },
2851                         started: 0,
2852                         updateRate: 1000,
2853                         autostart: true,
2854                         filter: ""
2855                 }, true);
2856
2857                 if (!runStarted) {
2858                         config.blocking = false;
2859
2860                         if (config.autostart) {
2861                                 scheduleBegin();
2862                         }
2863                 }
2864         },
2865
2866         stack: function stack(offset) {
2867                 offset = (offset || 0) + 2;
2868                 return sourceFromStacktrace(offset);
2869         },
2870
2871         onError: onError
2872   });
2873
2874   QUnit.pushFailure = pushFailure;
2875   QUnit.assert = Assert.prototype;
2876   QUnit.equiv = equiv;
2877   QUnit.dump = dump;
2878
2879   registerLoggingCallbacks(QUnit);
2880
2881   function scheduleBegin() {
2882
2883         runStarted = true;
2884
2885         // Add a slight delay to allow definition of more modules and tests.
2886         if (defined.setTimeout) {
2887                 setTimeout(function () {
2888                         begin();
2889                 }, 13);
2890         } else {
2891                 begin();
2892         }
2893   }
2894
2895   function begin() {
2896         var i,
2897             l,
2898             modulesLog = [];
2899
2900         // If the test run hasn't officially begun yet
2901         if (!config.started) {
2902
2903                 // Record the time of the test run's beginning
2904                 config.started = now();
2905
2906                 // Delete the loose unnamed module if unused.
2907                 if (config.modules[0].name === "" && config.modules[0].tests.length === 0) {
2908                         config.modules.shift();
2909                 }
2910
2911                 // Avoid unnecessary information by not logging modules' test environments
2912                 for (i = 0, l = config.modules.length; i < l; i++) {
2913                         modulesLog.push({
2914                                 name: config.modules[i].name,
2915                                 tests: config.modules[i].tests
2916                         });
2917                 }
2918
2919                 // The test run is officially beginning now
2920                 emit("runStart", globalSuite.start(true));
2921                 runLoggingCallbacks("begin", {
2922                         totalTests: Test.count,
2923                         modules: modulesLog
2924                 });
2925         }
2926
2927         config.blocking = false;
2928         ProcessingQueue.advance();
2929   }
2930
2931   function setHookFunction(module, hookName) {
2932         return function setHook(callback) {
2933                 module.hooks[hookName].push(callback);
2934         };
2935   }
2936
2937   exportQUnit(QUnit);
2938
2939   (function () {
2940
2941         if (typeof window === "undefined" || typeof document === "undefined") {
2942                 return;
2943         }
2944
2945         var config = QUnit.config,
2946             hasOwn = Object.prototype.hasOwnProperty;
2947
2948         // Stores fixture HTML for resetting later
2949         function storeFixture() {
2950
2951                 // Avoid overwriting user-defined values
2952                 if (hasOwn.call(config, "fixture")) {
2953                         return;
2954                 }
2955
2956                 var fixture = document.getElementById("qunit-fixture");
2957                 if (fixture) {
2958                         config.fixture = fixture.innerHTML;
2959                 }
2960         }
2961
2962         QUnit.begin(storeFixture);
2963
2964         // Resets the fixture DOM element if available.
2965         function resetFixture() {
2966                 if (config.fixture == null) {
2967                         return;
2968                 }
2969
2970                 var fixture = document.getElementById("qunit-fixture");
2971                 if (fixture) {
2972                         fixture.innerHTML = config.fixture;
2973                 }
2974         }
2975
2976         QUnit.testStart(resetFixture);
2977   })();
2978
2979   (function () {
2980
2981         // Only interact with URLs via window.location
2982         var location = typeof window !== "undefined" && window.location;
2983         if (!location) {
2984                 return;
2985         }
2986
2987         var urlParams = getUrlParams();
2988
2989         QUnit.urlParams = urlParams;
2990
2991         // Match module/test by inclusion in an array
2992         QUnit.config.moduleId = [].concat(urlParams.moduleId || []);
2993         QUnit.config.testId = [].concat(urlParams.testId || []);
2994
2995         // Exact case-insensitive match of the module name
2996         QUnit.config.module = urlParams.module;
2997
2998         // Regular expression or case-insenstive substring match against "moduleName: testName"
2999         QUnit.config.filter = urlParams.filter;
3000
3001         // Test order randomization
3002         if (urlParams.seed === true) {
3003
3004                 // Generate a random seed if the option is specified without a value
3005                 QUnit.config.seed = Math.random().toString(36).slice(2);
3006         } else if (urlParams.seed) {
3007                 QUnit.config.seed = urlParams.seed;
3008         }
3009
3010         // Add URL-parameter-mapped config values with UI form rendering data
3011         QUnit.config.urlConfig.push({
3012                 id: "hidepassed",
3013                 label: "Hide passed tests",
3014                 tooltip: "Only show tests and assertions that fail. Stored as query-strings."
3015         }, {
3016                 id: "noglobals",
3017                 label: "Check for Globals",
3018                 tooltip: "Enabling this will test if any test introduces new properties on the " + "global object (`window` in Browsers). Stored as query-strings."
3019         }, {
3020                 id: "notrycatch",
3021                 label: "No try-catch",
3022                 tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + "exceptions in IE reasonable. Stored as query-strings."
3023         });
3024
3025         QUnit.begin(function () {
3026                 var i,
3027                     option,
3028                     urlConfig = QUnit.config.urlConfig;
3029
3030                 for (i = 0; i < urlConfig.length; i++) {
3031
3032                         // Options can be either strings or objects with nonempty "id" properties
3033                         option = QUnit.config.urlConfig[i];
3034                         if (typeof option !== "string") {
3035                                 option = option.id;
3036                         }
3037
3038                         if (QUnit.config[option] === undefined) {
3039                                 QUnit.config[option] = urlParams[option];
3040                         }
3041                 }
3042         });
3043
3044         function getUrlParams() {
3045                 var i, param, name, value;
3046                 var urlParams = Object.create(null);
3047                 var params = location.search.slice(1).split("&");
3048                 var length = params.length;
3049
3050                 for (i = 0; i < length; i++) {
3051                         if (params[i]) {
3052                                 param = params[i].split("=");
3053                                 name = decodeQueryParam(param[0]);
3054
3055                                 // Allow just a key to turn on a flag, e.g., test.html?noglobals
3056                                 value = param.length === 1 || decodeQueryParam(param.slice(1).join("="));
3057                                 if (name in urlParams) {
3058                                         urlParams[name] = [].concat(urlParams[name], value);
3059                                 } else {
3060                                         urlParams[name] = value;
3061                                 }
3062                         }
3063                 }
3064
3065                 return urlParams;
3066         }
3067
3068         function decodeQueryParam(param) {
3069                 return decodeURIComponent(param.replace(/\+/g, "%20"));
3070         }
3071   })();
3072
3073   var stats = {
3074         passedTests: 0,
3075         failedTests: 0,
3076         skippedTests: 0,
3077         todoTests: 0
3078   };
3079
3080   // Escape text for attribute or text content.
3081   function escapeText(s) {
3082         if (!s) {
3083                 return "";
3084         }
3085         s = s + "";
3086
3087         // Both single quotes and double quotes (for attributes)
3088         return s.replace(/['"<>&]/g, function (s) {
3089                 switch (s) {
3090                         case "'":
3091                                 return "&#039;";
3092                         case "\"":
3093                                 return "&quot;";
3094                         case "<":
3095                                 return "&lt;";
3096                         case ">":
3097                                 return "&gt;";
3098                         case "&":
3099                                 return "&amp;";
3100                 }
3101         });
3102   }
3103
3104   (function () {
3105
3106         // Don't load the HTML Reporter on non-browser environments
3107         if (typeof window === "undefined" || !window.document) {
3108                 return;
3109         }
3110
3111         var config = QUnit.config,
3112             document$$1 = window.document,
3113             collapseNext = false,
3114             hasOwn = Object.prototype.hasOwnProperty,
3115             unfilteredUrl = setUrl({ filter: undefined, module: undefined,
3116                 moduleId: undefined, testId: undefined }),
3117             modulesList = [];
3118
3119         function addEvent(elem, type, fn) {
3120                 elem.addEventListener(type, fn, false);
3121         }
3122
3123         function removeEvent(elem, type, fn) {
3124                 elem.removeEventListener(type, fn, false);
3125         }
3126
3127         function addEvents(elems, type, fn) {
3128                 var i = elems.length;
3129                 while (i--) {
3130                         addEvent(elems[i], type, fn);
3131                 }
3132         }
3133
3134         function hasClass(elem, name) {
3135                 return (" " + elem.className + " ").indexOf(" " + name + " ") >= 0;
3136         }
3137
3138         function addClass(elem, name) {
3139                 if (!hasClass(elem, name)) {
3140                         elem.className += (elem.className ? " " : "") + name;
3141                 }
3142         }
3143
3144         function toggleClass(elem, name, force) {
3145                 if (force || typeof force === "undefined" && !hasClass(elem, name)) {
3146                         addClass(elem, name);
3147                 } else {
3148                         removeClass(elem, name);
3149                 }
3150         }
3151
3152         function removeClass(elem, name) {
3153                 var set = " " + elem.className + " ";
3154
3155                 // Class name may appear multiple times
3156                 while (set.indexOf(" " + name + " ") >= 0) {
3157                         set = set.replace(" " + name + " ", " ");
3158                 }
3159
3160                 // Trim for prettiness
3161                 elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
3162         }
3163
3164         function id(name) {
3165                 return document$$1.getElementById && document$$1.getElementById(name);
3166         }
3167
3168         function abortTests() {
3169                 var abortButton = id("qunit-abort-tests-button");
3170                 if (abortButton) {
3171                         abortButton.disabled = true;
3172                         abortButton.innerHTML = "Aborting...";
3173                 }
3174                 QUnit.config.queue.length = 0;
3175                 return false;
3176         }
3177
3178         function interceptNavigation(ev) {
3179                 applyUrlParams();
3180
3181                 if (ev && ev.preventDefault) {
3182                         ev.preventDefault();
3183                 }
3184
3185                 return false;
3186         }
3187
3188         function getUrlConfigHtml() {
3189                 var i,
3190                     j,
3191                     val,
3192                     escaped,
3193                     escapedTooltip,
3194                     selection = false,
3195                     urlConfig = config.urlConfig,
3196                     urlConfigHtml = "";
3197
3198                 for (i = 0; i < urlConfig.length; i++) {
3199
3200                         // Options can be either strings or objects with nonempty "id" properties
3201                         val = config.urlConfig[i];
3202                         if (typeof val === "string") {
3203                                 val = {
3204                                         id: val,
3205                                         label: val
3206                                 };
3207                         }
3208
3209                         escaped = escapeText(val.id);
3210                         escapedTooltip = escapeText(val.tooltip);
3211
3212                         if (!val.value || typeof val.value === "string") {
3213                                 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>";
3214                         } else {
3215                                 urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'>" + val.label + ": </label><select id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
3216
3217                                 if (QUnit.is("array", val.value)) {
3218                                         for (j = 0; j < val.value.length; j++) {
3219                                                 escaped = escapeText(val.value[j]);
3220                                                 urlConfigHtml += "<option value='" + escaped + "'" + (config[val.id] === val.value[j] ? (selection = true) && " selected='selected'" : "") + ">" + escaped + "</option>";
3221                                         }
3222                                 } else {
3223                                         for (j in val.value) {
3224                                                 if (hasOwn.call(val.value, j)) {
3225                                                         urlConfigHtml += "<option value='" + escapeText(j) + "'" + (config[val.id] === j ? (selection = true) && " selected='selected'" : "") + ">" + escapeText(val.value[j]) + "</option>";
3226                                                 }
3227                                         }
3228                                 }
3229                                 if (config[val.id] && !selection) {
3230                                         escaped = escapeText(config[val.id]);
3231                                         urlConfigHtml += "<option value='" + escaped + "' selected='selected' disabled='disabled'>" + escaped + "</option>";
3232                                 }
3233                                 urlConfigHtml += "</select>";
3234                         }
3235                 }
3236
3237                 return urlConfigHtml;
3238         }
3239
3240         // Handle "click" events on toolbar checkboxes and "change" for select menus.
3241         // Updates the URL with the new state of `config.urlConfig` values.
3242         function toolbarChanged() {
3243                 var updatedUrl,
3244                     value,
3245                     tests,
3246                     field = this,
3247                     params = {};
3248
3249                 // Detect if field is a select menu or a checkbox
3250                 if ("selectedIndex" in field) {
3251                         value = field.options[field.selectedIndex].value || undefined;
3252                 } else {
3253                         value = field.checked ? field.defaultValue || true : undefined;
3254                 }
3255
3256                 params[field.name] = value;
3257                 updatedUrl = setUrl(params);
3258
3259                 // Check if we can apply the change without a page refresh
3260                 if ("hidepassed" === field.name && "replaceState" in window.history) {
3261                         QUnit.urlParams[field.name] = value;
3262                         config[field.name] = value || false;
3263                         tests = id("qunit-tests");
3264                         if (tests) {
3265                                 toggleClass(tests, "hidepass", value || false);
3266                         }
3267                         window.history.replaceState(null, "", updatedUrl);
3268                 } else {
3269                         window.location = updatedUrl;
3270                 }
3271         }
3272
3273         function setUrl(params) {
3274                 var key,
3275                     arrValue,
3276                     i,
3277                     querystring = "?",
3278                     location = window.location;
3279
3280                 params = QUnit.extend(QUnit.extend({}, QUnit.urlParams), params);
3281
3282                 for (key in params) {
3283
3284                         // Skip inherited or undefined properties
3285                         if (hasOwn.call(params, key) && params[key] !== undefined) {
3286
3287                                 // Output a parameter for each value of this key (but usually just one)
3288                                 arrValue = [].concat(params[key]);
3289                                 for (i = 0; i < arrValue.length; i++) {
3290                                         querystring += encodeURIComponent(key);
3291                                         if (arrValue[i] !== true) {
3292                                                 querystring += "=" + encodeURIComponent(arrValue[i]);
3293                                         }
3294                                         querystring += "&";
3295                                 }
3296                         }
3297                 }
3298                 return location.protocol + "//" + location.host + location.pathname + querystring.slice(0, -1);
3299         }
3300
3301         function applyUrlParams() {
3302                 var i,
3303                     selectedModules = [],
3304                     modulesList = id("qunit-modulefilter-dropdown-list").getElementsByTagName("input"),
3305                     filter = id("qunit-filter-input").value;
3306
3307                 for (i = 0; i < modulesList.length; i++) {
3308                         if (modulesList[i].checked) {
3309                                 selectedModules.push(modulesList[i].value);
3310                         }
3311                 }
3312
3313                 window.location = setUrl({
3314                         filter: filter === "" ? undefined : filter,
3315                         moduleId: selectedModules.length === 0 ? undefined : selectedModules,
3316
3317                         // Remove module and testId filter
3318                         module: undefined,
3319                         testId: undefined
3320                 });
3321         }
3322
3323         function toolbarUrlConfigContainer() {
3324                 var urlConfigContainer = document$$1.createElement("span");
3325
3326                 urlConfigContainer.innerHTML = getUrlConfigHtml();
3327                 addClass(urlConfigContainer, "qunit-url-config");
3328
3329                 addEvents(urlConfigContainer.getElementsByTagName("input"), "change", toolbarChanged);
3330                 addEvents(urlConfigContainer.getElementsByTagName("select"), "change", toolbarChanged);
3331
3332                 return urlConfigContainer;
3333         }
3334
3335         function abortTestsButton() {
3336                 var button = document$$1.createElement("button");
3337                 button.id = "qunit-abort-tests-button";
3338                 button.innerHTML = "Abort";
3339                 addEvent(button, "click", abortTests);
3340                 return button;
3341         }
3342
3343         function toolbarLooseFilter() {
3344                 var filter = document$$1.createElement("form"),
3345                     label = document$$1.createElement("label"),
3346                     input = document$$1.createElement("input"),
3347                     button = document$$1.createElement("button");
3348
3349                 addClass(filter, "qunit-filter");
3350
3351                 label.innerHTML = "Filter: ";
3352
3353                 input.type = "text";
3354                 input.value = config.filter || "";
3355                 input.name = "filter";
3356                 input.id = "qunit-filter-input";
3357
3358                 button.innerHTML = "Go";
3359
3360                 label.appendChild(input);
3361
3362                 filter.appendChild(label);
3363                 filter.appendChild(document$$1.createTextNode(" "));
3364                 filter.appendChild(button);
3365                 addEvent(filter, "submit", interceptNavigation);
3366
3367                 return filter;
3368         }
3369
3370         function moduleListHtml() {
3371                 var i,
3372                     checked,
3373                     html = "";
3374
3375                 for (i = 0; i < config.modules.length; i++) {
3376                         if (config.modules[i].name !== "") {
3377                                 checked = config.moduleId.indexOf(config.modules[i].moduleId) > -1;
3378                                 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>";
3379                         }
3380                 }
3381
3382                 return html;
3383         }
3384
3385         function toolbarModuleFilter() {
3386                 var allCheckbox,
3387                     commit,
3388                     reset,
3389                     moduleFilter = document$$1.createElement("form"),
3390                     label = document$$1.createElement("label"),
3391                     moduleSearch = document$$1.createElement("input"),
3392                     dropDown = document$$1.createElement("div"),
3393                     actions = document$$1.createElement("span"),
3394                     dropDownList = document$$1.createElement("ul"),
3395                     dirty = false;
3396
3397                 moduleSearch.id = "qunit-modulefilter-search";
3398                 addEvent(moduleSearch, "input", searchInput);
3399                 addEvent(moduleSearch, "input", searchFocus);
3400                 addEvent(moduleSearch, "focus", searchFocus);
3401                 addEvent(moduleSearch, "click", searchFocus);
3402
3403                 label.id = "qunit-modulefilter-search-container";
3404                 label.innerHTML = "Module: ";
3405                 label.appendChild(moduleSearch);
3406
3407                 actions.id = "qunit-modulefilter-actions";
3408                 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>";
3409                 allCheckbox = actions.lastChild.firstChild;
3410                 commit = actions.firstChild;
3411                 reset = commit.nextSibling;
3412                 addEvent(commit, "click", applyUrlParams);
3413
3414                 dropDownList.id = "qunit-modulefilter-dropdown-list";
3415                 dropDownList.innerHTML = moduleListHtml();
3416
3417                 dropDown.id = "qunit-modulefilter-dropdown";
3418                 dropDown.style.display = "none";
3419                 dropDown.appendChild(actions);
3420                 dropDown.appendChild(dropDownList);
3421                 addEvent(dropDown, "change", selectionChange);
3422                 selectionChange();
3423
3424                 moduleFilter.id = "qunit-modulefilter";
3425                 moduleFilter.appendChild(label);
3426                 moduleFilter.appendChild(dropDown);
3427                 addEvent(moduleFilter, "submit", interceptNavigation);
3428                 addEvent(moduleFilter, "reset", function () {
3429
3430                         // Let the reset happen, then update styles
3431                         window.setTimeout(selectionChange);
3432                 });
3433
3434                 // Enables show/hide for the dropdown
3435                 function searchFocus() {
3436                         if (dropDown.style.display !== "none") {
3437                                 return;
3438                         }
3439
3440                         dropDown.style.display = "block";
3441                         addEvent(document$$1, "click", hideHandler);
3442                         addEvent(document$$1, "keydown", hideHandler);
3443
3444                         // Hide on Escape keydown or outside-container click
3445                         function hideHandler(e) {
3446                                 var inContainer = moduleFilter.contains(e.target);
3447
3448                                 if (e.keyCode === 27 || !inContainer) {
3449                                         if (e.keyCode === 27 && inContainer) {
3450                                                 moduleSearch.focus();
3451                                         }
3452                                         dropDown.style.display = "none";
3453                                         removeEvent(document$$1, "click", hideHandler);
3454                                         removeEvent(document$$1, "keydown", hideHandler);
3455                                         moduleSearch.value = "";
3456                                         searchInput();
3457                                 }
3458                         }
3459                 }
3460
3461                 // Processes module search box input
3462                 function searchInput() {
3463                         var i,
3464                             item,
3465                             searchText = moduleSearch.value.toLowerCase(),
3466                             listItems = dropDownList.children;
3467
3468                         for (i = 0; i < listItems.length; i++) {
3469                                 item = listItems[i];
3470                                 if (!searchText || item.textContent.toLowerCase().indexOf(searchText) > -1) {
3471                                         item.style.display = "";
3472                                 } else {
3473                                         item.style.display = "none";
3474                                 }
3475                         }
3476                 }
3477
3478                 // Processes selection changes
3479                 function selectionChange(evt) {
3480                         var i,
3481                             item,
3482                             checkbox = evt && evt.target || allCheckbox,
3483                             modulesList = dropDownList.getElementsByTagName("input"),
3484                             selectedNames = [];
3485
3486                         toggleClass(checkbox.parentNode, "checked", checkbox.checked);
3487
3488                         dirty = false;
3489                         if (checkbox.checked && checkbox !== allCheckbox) {
3490                                 allCheckbox.checked = false;
3491                                 removeClass(allCheckbox.parentNode, "checked");
3492                         }
3493                         for (i = 0; i < modulesList.length; i++) {
3494                                 item = modulesList[i];
3495                                 if (!evt) {
3496                                         toggleClass(item.parentNode, "checked", item.checked);
3497                                 } else if (checkbox === allCheckbox && checkbox.checked) {
3498                                         item.checked = false;
3499                                         removeClass(item.parentNode, "checked");
3500                                 }
3501                                 dirty = dirty || item.checked !== item.defaultChecked;
3502                                 if (item.checked) {
3503                                         selectedNames.push(item.parentNode.textContent);
3504                                 }
3505                         }
3506
3507                         commit.style.display = reset.style.display = dirty ? "" : "none";
3508                         moduleSearch.placeholder = selectedNames.join(", ") || allCheckbox.parentNode.textContent;
3509                         moduleSearch.title = "Type to filter list. Current selection:\n" + (selectedNames.join("\n") || allCheckbox.parentNode.textContent);
3510                 }
3511
3512                 return moduleFilter;
3513         }
3514
3515         function appendToolbar() {
3516                 var toolbar = id("qunit-testrunner-toolbar");
3517
3518                 if (toolbar) {
3519                         toolbar.appendChild(toolbarUrlConfigContainer());
3520                         toolbar.appendChild(toolbarModuleFilter());
3521                         toolbar.appendChild(toolbarLooseFilter());
3522                         toolbar.appendChild(document$$1.createElement("div")).className = "clearfix";
3523                 }
3524         }
3525
3526         function appendHeader() {
3527                 var header = id("qunit-header");
3528
3529                 if (header) {
3530                         header.innerHTML = "<a href='" + escapeText(unfilteredUrl) + "'>" + header.innerHTML + "</a> ";
3531                 }
3532         }
3533
3534         function appendBanner() {
3535                 var banner = id("qunit-banner");
3536
3537                 if (banner) {
3538                         banner.className = "";
3539                 }
3540         }
3541
3542         function appendTestResults() {
3543                 var tests = id("qunit-tests"),
3544                     result = id("qunit-testresult"),
3545                     controls;
3546
3547                 if (result) {
3548                         result.parentNode.removeChild(result);
3549                 }
3550
3551                 if (tests) {
3552                         tests.innerHTML = "";
3553                         result = document$$1.createElement("p");
3554                         result.id = "qunit-testresult";
3555                         result.className = "result";
3556                         tests.parentNode.insertBefore(result, tests);
3557                         result.innerHTML = "<div id=\"qunit-testresult-display\">Running...<br />&#160;</div>" + "<div id=\"qunit-testresult-controls\"></div>" + "<div class=\"clearfix\"></div>";
3558                         controls = id("qunit-testresult-controls");
3559                 }
3560
3561                 if (controls) {
3562                         controls.appendChild(abortTestsButton());
3563                 }
3564         }
3565
3566         function appendFilteredTest() {
3567                 var testId = QUnit.config.testId;
3568                 if (!testId || testId.length <= 0) {
3569                         return "";
3570                 }
3571                 return "<div id='qunit-filteredTest'>Rerunning selected tests: " + escapeText(testId.join(", ")) + " <a id='qunit-clearFilter' href='" + escapeText(unfilteredUrl) + "'>Run all tests</a></div>";
3572         }
3573
3574         function appendUserAgent() {
3575                 var userAgent = id("qunit-userAgent");
3576
3577                 if (userAgent) {
3578                         userAgent.innerHTML = "";
3579                         userAgent.appendChild(document$$1.createTextNode("QUnit " + QUnit.version + "; " + navigator.userAgent));
3580                 }
3581         }
3582
3583         function appendInterface() {
3584                 var qunit = id("qunit");
3585
3586                 if (qunit) {
3587                         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>";
3588                 }
3589
3590                 appendHeader();
3591                 appendBanner();
3592                 appendTestResults();
3593                 appendUserAgent();
3594                 appendToolbar();
3595         }
3596
3597         function appendTestsList(modules) {
3598                 var i, l, x, z, test, moduleObj;
3599
3600                 for (i = 0, l = modules.length; i < l; i++) {
3601                         moduleObj = modules[i];
3602
3603                         for (x = 0, z = moduleObj.tests.length; x < z; x++) {
3604                                 test = moduleObj.tests[x];
3605
3606                                 appendTest(test.name, test.testId, moduleObj.name);
3607                         }
3608                 }
3609         }
3610
3611         function appendTest(name, testId, moduleName) {
3612                 var title,
3613                     rerunTrigger,
3614                     testBlock,
3615                     assertList,
3616                     tests = id("qunit-tests");
3617
3618                 if (!tests) {
3619                         return;
3620                 }
3621
3622                 title = document$$1.createElement("strong");
3623                 title.innerHTML = getNameHtml(name, moduleName);
3624
3625                 rerunTrigger = document$$1.createElement("a");
3626                 rerunTrigger.innerHTML = "Rerun";
3627                 rerunTrigger.href = setUrl({ testId: testId });
3628
3629                 testBlock = document$$1.createElement("li");
3630                 testBlock.appendChild(title);
3631                 testBlock.appendChild(rerunTrigger);
3632                 testBlock.id = "qunit-test-output-" + testId;
3633
3634                 assertList = document$$1.createElement("ol");
3635                 assertList.className = "qunit-assert-list";
3636
3637                 testBlock.appendChild(assertList);
3638
3639                 tests.appendChild(testBlock);
3640         }
3641
3642         // HTML Reporter initialization and load
3643         QUnit.begin(function (details) {
3644                 var i, moduleObj, tests;
3645
3646                 // Sort modules by name for the picker
3647                 for (i = 0; i < details.modules.length; i++) {
3648                         moduleObj = details.modules[i];
3649                         if (moduleObj.name) {
3650                                 modulesList.push(moduleObj.name);
3651                         }
3652                 }
3653                 modulesList.sort(function (a, b) {
3654                         return a.localeCompare(b);
3655                 });
3656
3657                 // Initialize QUnit elements
3658                 appendInterface();
3659                 appendTestsList(details.modules);
3660                 tests = id("qunit-tests");
3661                 if (tests && config.hidepassed) {
3662                         addClass(tests, "hidepass");
3663                 }
3664         });
3665
3666         QUnit.done(function (details) {
3667                 var banner = id("qunit-banner"),
3668                     tests = id("qunit-tests"),
3669                     abortButton = id("qunit-abort-tests-button"),
3670                     totalTests = stats.passedTests + stats.skippedTests + stats.todoTests + stats.failedTests,
3671                     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(""),
3672                     test,
3673                     assertLi,
3674                     assertList;
3675
3676                 // Update remaing tests to aborted
3677                 if (abortButton && abortButton.disabled) {
3678                         html = "Tests aborted after " + details.runtime + " milliseconds.";
3679
3680                         for (var i = 0; i < tests.children.length; i++) {
3681                                 test = tests.children[i];
3682                                 if (test.className === "" || test.className === "running") {
3683                                         test.className = "aborted";
3684                                         assertList = test.getElementsByTagName("ol")[0];
3685                                         assertLi = document$$1.createElement("li");
3686                                         assertLi.className = "fail";
3687                                         assertLi.innerHTML = "Test aborted.";
3688                                         assertList.appendChild(assertLi);
3689                                 }
3690                         }
3691                 }
3692
3693                 if (banner && (!abortButton || abortButton.disabled === false)) {
3694                         banner.className = stats.failedTests ? "qunit-fail" : "qunit-pass";
3695                 }
3696
3697                 if (abortButton) {
3698                         abortButton.parentNode.removeChild(abortButton);
3699                 }
3700
3701                 if (tests) {
3702                         id("qunit-testresult-display").innerHTML = html;
3703                 }
3704
3705                 if (config.altertitle && document$$1.title) {
3706
3707                         // Show âœ– for good, âœ” for bad suite result in title
3708                         // use escape sequences in case file gets loaded with non-utf-8-charset
3709                         document$$1.title = [stats.failedTests ? "\u2716" : "\u2714", document$$1.title.replace(/^[\u2714\u2716] /i, "")].join(" ");
3710                 }
3711
3712                 // Scroll back to top to show results
3713                 if (config.scrolltop && window.scrollTo) {
3714                         window.scrollTo(0, 0);
3715                 }
3716         });
3717
3718         function getNameHtml(name, module) {
3719                 var nameHtml = "";
3720
3721                 if (module) {
3722                         nameHtml = "<span class='module-name'>" + escapeText(module) + "</span>: ";
3723                 }
3724
3725                 nameHtml += "<span class='test-name'>" + escapeText(name) + "</span>";
3726
3727                 return nameHtml;
3728         }
3729
3730         QUnit.testStart(function (details) {
3731                 var running, testBlock, bad;
3732
3733                 testBlock = id("qunit-test-output-" + details.testId);
3734                 if (testBlock) {
3735                         testBlock.className = "running";
3736                 } else {
3737
3738                         // Report later registered tests
3739                         appendTest(details.name, details.testId, details.module);
3740                 }
3741
3742                 running = id("qunit-testresult-display");
3743                 if (running) {
3744                         bad = QUnit.config.reorder && details.previousFailure;
3745
3746                         running.innerHTML = (bad ? "Rerunning previously failed test: <br />" : "Running: <br />") + getNameHtml(details.name, details.module);
3747                 }
3748         });
3749
3750         function stripHtml(string) {
3751
3752                 // Strip tags, html entity and whitespaces
3753                 return string.replace(/<\/?[^>]+(>|$)/g, "").replace(/\&quot;/g, "").replace(/\s+/g, "");
3754         }
3755
3756         QUnit.log(function (details) {
3757                 var assertList,
3758                     assertLi,
3759                     message,
3760                     expected,
3761                     actual,
3762                     diff,
3763                     showDiff = false,
3764                     testItem = id("qunit-test-output-" + details.testId);
3765
3766                 if (!testItem) {
3767                         return;
3768                 }
3769
3770                 message = escapeText(details.message) || (details.result ? "okay" : "failed");
3771                 message = "<span class='test-message'>" + message + "</span>";
3772                 message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
3773
3774                 // The pushFailure doesn't provide details.expected
3775                 // when it calls, it's implicit to also not show expected and diff stuff
3776                 // Also, we need to check details.expected existence, as it can exist and be undefined
3777                 if (!details.result && hasOwn.call(details, "expected")) {
3778                         if (details.negative) {
3779                                 expected = "NOT " + QUnit.dump.parse(details.expected);
3780                         } else {
3781                                 expected = QUnit.dump.parse(details.expected);
3782                         }
3783
3784                         actual = QUnit.dump.parse(details.actual);
3785                         message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + escapeText(expected) + "</pre></td></tr>";
3786
3787                         if (actual !== expected) {
3788
3789                                 message += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText(actual) + "</pre></td></tr>";
3790
3791                                 if (typeof details.actual === "number" && typeof details.expected === "number") {
3792                                         if (!isNaN(details.actual) && !isNaN(details.expected)) {
3793                                                 showDiff = true;
3794                                                 diff = details.actual - details.expected;
3795                                                 diff = (diff > 0 ? "+" : "") + diff;
3796                                         }
3797                                 } else if (typeof details.actual !== "boolean" && typeof details.expected !== "boolean") {
3798                                         diff = QUnit.diff(expected, actual);
3799
3800                                         // don't show diff if there is zero overlap
3801                                         showDiff = stripHtml(diff).length !== stripHtml(expected).length + stripHtml(actual).length;
3802                                 }
3803
3804                                 if (showDiff) {
3805                                         message += "<tr class='test-diff'><th>Diff: </th><td><pre>" + diff + "</pre></td></tr>";
3806                                 }
3807                         } else if (expected.indexOf("[object Array]") !== -1 || expected.indexOf("[object Object]") !== -1) {
3808                                 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>";
3809                         } else {
3810                                 message += "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the expected and actual results have an equivalent" + " serialization</td></tr>";
3811                         }
3812
3813                         if (details.source) {
3814                                 message += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>";
3815                         }
3816
3817                         message += "</table>";
3818
3819                         // This occurs when pushFailure is set and we have an extracted stack trace
3820                 } else if (!details.result && details.source) {
3821                         message += "<table>" + "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>" + "</table>";
3822                 }
3823
3824                 assertList = testItem.getElementsByTagName("ol")[0];
3825
3826                 assertLi = document$$1.createElement("li");
3827                 assertLi.className = details.result ? "pass" : "fail";
3828                 assertLi.innerHTML = message;
3829                 assertList.appendChild(assertLi);
3830         });
3831
3832         QUnit.testDone(function (details) {
3833                 var testTitle,
3834                     time,
3835                     testItem,
3836                     assertList,
3837                     good,
3838                     bad,
3839                     testCounts,
3840                     skipped,
3841                     sourceName,
3842                     tests = id("qunit-tests");
3843
3844                 if (!tests) {
3845                         return;
3846                 }
3847
3848                 testItem = id("qunit-test-output-" + details.testId);
3849
3850                 assertList = testItem.getElementsByTagName("ol")[0];
3851
3852                 good = details.passed;
3853                 bad = details.failed;
3854
3855                 // This test passed if it has no unexpected failed assertions
3856                 var testPassed = details.failed > 0 ? details.todo : !details.todo;
3857
3858                 if (testPassed) {
3859
3860                         // Collapse the passing tests
3861                         addClass(assertList, "qunit-collapsed");
3862                 } else if (config.collapse) {
3863                         if (!collapseNext) {
3864
3865                                 // Skip collapsing the first failing test
3866                                 collapseNext = true;
3867                         } else {
3868
3869                                 // Collapse remaining tests
3870                                 addClass(assertList, "qunit-collapsed");
3871                         }
3872                 }
3873
3874                 // The testItem.firstChild is the test name
3875                 testTitle = testItem.firstChild;
3876
3877                 testCounts = bad ? "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " : "";
3878
3879                 testTitle.innerHTML += " <b class='counts'>(" + testCounts + details.assertions.length + ")</b>";
3880
3881                 if (details.skipped) {
3882                         stats.skippedTests++;
3883
3884                         testItem.className = "skipped";
3885                         skipped = document$$1.createElement("em");
3886                         skipped.className = "qunit-skipped-label";
3887                         skipped.innerHTML = "skipped";
3888                         testItem.insertBefore(skipped, testTitle);
3889                 } else {
3890                         addEvent(testTitle, "click", function () {
3891                                 toggleClass(assertList, "qunit-collapsed");
3892                         });
3893
3894                         testItem.className = testPassed ? "pass" : "fail";
3895
3896                         if (details.todo) {
3897                                 var todoLabel = document$$1.createElement("em");
3898                                 todoLabel.className = "qunit-todo-label";
3899                                 todoLabel.innerHTML = "todo";
3900                                 testItem.className += " todo";
3901                                 testItem.insertBefore(todoLabel, testTitle);
3902                         }
3903
3904                         time = document$$1.createElement("span");
3905                         time.className = "runtime";
3906                         time.innerHTML = details.runtime + " ms";
3907                         testItem.insertBefore(time, assertList);
3908
3909                         if (!testPassed) {
3910                                 stats.failedTests++;
3911                         } else if (details.todo) {
3912                                 stats.todoTests++;
3913                         } else {
3914                                 stats.passedTests++;
3915                         }
3916                 }
3917
3918                 // Show the source of the test when showing assertions
3919                 if (details.source) {
3920                         sourceName = document$$1.createElement("p");
3921                         sourceName.innerHTML = "<strong>Source: </strong>" + details.source;
3922                         addClass(sourceName, "qunit-source");
3923                         if (testPassed) {
3924                                 addClass(sourceName, "qunit-collapsed");
3925                         }
3926                         addEvent(testTitle, "click", function () {
3927                                 toggleClass(sourceName, "qunit-collapsed");
3928                         });
3929                         testItem.appendChild(sourceName);
3930                 }
3931         });
3932
3933         // Avoid readyState issue with phantomjs
3934         // Ref: #818
3935         var notPhantom = function (p) {
3936                 return !(p && p.version && p.version.major > 0);
3937         }(window.phantom);
3938
3939         if (notPhantom && document$$1.readyState === "complete") {
3940                 QUnit.load();
3941         } else {
3942                 addEvent(window, "load", QUnit.load);
3943         }
3944
3945         // Wrap window.onerror. We will call the original window.onerror to see if
3946         // the existing handler fully handles the error; if not, we will call the
3947         // QUnit.onError function.
3948         var originalWindowOnError = window.onerror;
3949
3950         // Cover uncaught exceptions
3951         // Returning true will suppress the default browser handler,
3952         // returning false will let it run.
3953         window.onerror = function (message, fileName, lineNumber) {
3954                 var ret = false;
3955                 if (originalWindowOnError) {
3956                         for (var _len = arguments.length, args = Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) {
3957                                 args[_key - 3] = arguments[_key];
3958                         }
3959
3960                         ret = originalWindowOnError.call.apply(originalWindowOnError, [this, message, fileName, lineNumber].concat(args));
3961                 }
3962
3963                 // Treat return value as window.onerror itself does,
3964                 // Only do our handling if not suppressed.
3965                 if (ret !== true) {
3966                         var error = {
3967                                 message: message,
3968                                 fileName: fileName,
3969                                 lineNumber: lineNumber
3970                         };
3971
3972                         ret = QUnit.onError(error);
3973                 }
3974
3975                 return ret;
3976         };
3977   })();
3978
3979   /*
3980    * This file is a modified version of google-diff-match-patch's JavaScript implementation
3981    * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
3982    * modifications are licensed as more fully set forth in LICENSE.txt.
3983    *
3984    * The original source of google-diff-match-patch is attributable and licensed as follows:
3985    *
3986    * Copyright 2006 Google Inc.
3987    * https://code.google.com/p/google-diff-match-patch/
3988    *
3989    * Licensed under the Apache License, Version 2.0 (the "License");
3990    * you may not use this file except in compliance with the License.
3991    * You may obtain a copy of the License at
3992    *
3993    * https://www.apache.org/licenses/LICENSE-2.0
3994    *
3995    * Unless required by applicable law or agreed to in writing, software
3996    * distributed under the License is distributed on an "AS IS" BASIS,
3997    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3998    * See the License for the specific language governing permissions and
3999    * limitations under the License.
4000    *
4001    * More Info:
4002    *  https://code.google.com/p/google-diff-match-patch/
4003    *
4004    * Usage: QUnit.diff(expected, actual)
4005    *
4006    */
4007   QUnit.diff = function () {
4008         function DiffMatchPatch() {}
4009
4010         //  DIFF FUNCTIONS
4011
4012         /**
4013     * The data structure representing a diff is an array of tuples:
4014     * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
4015     * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
4016     */
4017         var DIFF_DELETE = -1,
4018             DIFF_INSERT = 1,
4019             DIFF_EQUAL = 0;
4020
4021         /**
4022     * Find the differences between two texts.  Simplifies the problem by stripping
4023     * any common prefix or suffix off the texts before diffing.
4024     * @param {string} text1 Old string to be diffed.
4025     * @param {string} text2 New string to be diffed.
4026     * @param {boolean=} optChecklines Optional speedup flag. If present and false,
4027     *     then don't run a line-level diff first to identify the changed areas.
4028     *     Defaults to true, which does a faster, slightly less optimal diff.
4029     * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
4030     */
4031         DiffMatchPatch.prototype.DiffMain = function (text1, text2, optChecklines) {
4032                 var deadline, checklines, commonlength, commonprefix, commonsuffix, diffs;
4033
4034                 // The diff must be complete in up to 1 second.
4035                 deadline = new Date().getTime() + 1000;
4036
4037                 // Check for null inputs.
4038                 if (text1 === null || text2 === null) {
4039                         throw new Error("Null input. (DiffMain)");
4040                 }
4041
4042                 // Check for equality (speedup).
4043                 if (text1 === text2) {
4044                         if (text1) {
4045                                 return [[DIFF_EQUAL, text1]];
4046                         }
4047                         return [];
4048                 }
4049
4050                 if (typeof optChecklines === "undefined") {
4051                         optChecklines = true;
4052                 }
4053
4054                 checklines = optChecklines;
4055
4056                 // Trim off common prefix (speedup).
4057                 commonlength = this.diffCommonPrefix(text1, text2);
4058                 commonprefix = text1.substring(0, commonlength);
4059                 text1 = text1.substring(commonlength);
4060                 text2 = text2.substring(commonlength);
4061
4062                 // Trim off common suffix (speedup).
4063                 commonlength = this.diffCommonSuffix(text1, text2);
4064                 commonsuffix = text1.substring(text1.length - commonlength);
4065                 text1 = text1.substring(0, text1.length - commonlength);
4066                 text2 = text2.substring(0, text2.length - commonlength);
4067
4068                 // Compute the diff on the middle block.
4069                 diffs = this.diffCompute(text1, text2, checklines, deadline);
4070
4071                 // Restore the prefix and suffix.
4072                 if (commonprefix) {
4073                         diffs.unshift([DIFF_EQUAL, commonprefix]);
4074                 }
4075                 if (commonsuffix) {
4076                         diffs.push([DIFF_EQUAL, commonsuffix]);
4077                 }
4078                 this.diffCleanupMerge(diffs);
4079                 return diffs;
4080         };
4081
4082         /**
4083     * Reduce the number of edits by eliminating operationally trivial equalities.
4084     * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
4085     */
4086         DiffMatchPatch.prototype.diffCleanupEfficiency = function (diffs) {
4087                 var changes, equalities, equalitiesLength, lastequality, pointer, preIns, preDel, postIns, postDel;
4088                 changes = false;
4089                 equalities = []; // Stack of indices where equalities are found.
4090                 equalitiesLength = 0; // Keeping our own length var is faster in JS.
4091                 /** @type {?string} */
4092                 lastequality = null;
4093
4094                 // Always equal to diffs[equalities[equalitiesLength - 1]][1]
4095                 pointer = 0; // Index of current position.
4096
4097                 // Is there an insertion operation before the last equality.
4098                 preIns = false;
4099
4100                 // Is there a deletion operation before the last equality.
4101                 preDel = false;
4102
4103                 // Is there an insertion operation after the last equality.
4104                 postIns = false;
4105
4106                 // Is there a deletion operation after the last equality.
4107                 postDel = false;
4108                 while (pointer < diffs.length) {
4109
4110                         // Equality found.
4111                         if (diffs[pointer][0] === DIFF_EQUAL) {
4112                                 if (diffs[pointer][1].length < 4 && (postIns || postDel)) {
4113
4114                                         // Candidate found.
4115                                         equalities[equalitiesLength++] = pointer;
4116                                         preIns = postIns;
4117                                         preDel = postDel;
4118                                         lastequality = diffs[pointer][1];
4119                                 } else {
4120
4121                                         // Not a candidate, and can never become one.
4122                                         equalitiesLength = 0;
4123                                         lastequality = null;
4124                                 }
4125                                 postIns = postDel = false;
4126
4127                                 // An insertion or deletion.
4128                         } else {
4129
4130                                 if (diffs[pointer][0] === DIFF_DELETE) {
4131                                         postDel = true;
4132                                 } else {
4133                                         postIns = true;
4134                                 }
4135
4136                                 /*
4137        * Five types to be split:
4138        * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
4139        * <ins>A</ins>X<ins>C</ins><del>D</del>
4140        * <ins>A</ins><del>B</del>X<ins>C</ins>
4141        * <ins>A</del>X<ins>C</ins><del>D</del>
4142        * <ins>A</ins><del>B</del>X<del>C</del>
4143        */
4144                                 if (lastequality && (preIns && preDel && postIns && postDel || lastequality.length < 2 && preIns + preDel + postIns + postDel === 3)) {
4145
4146                                         // Duplicate record.
4147                                         diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
4148
4149                                         // Change second copy to insert.
4150                                         diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
4151                                         equalitiesLength--; // Throw away the equality we just deleted;
4152                                         lastequality = null;
4153                                         if (preIns && preDel) {
4154
4155                                                 // No changes made which could affect previous entry, keep going.
4156                                                 postIns = postDel = true;
4157                                                 equalitiesLength = 0;
4158                                         } else {
4159                                                 equalitiesLength--; // Throw away the previous equality.
4160                                                 pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
4161                                                 postIns = postDel = false;
4162                                         }
4163                                         changes = true;
4164                                 }
4165                         }
4166                         pointer++;
4167                 }
4168
4169                 if (changes) {
4170                         this.diffCleanupMerge(diffs);
4171                 }
4172         };
4173
4174         /**
4175     * Convert a diff array into a pretty HTML report.
4176     * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
4177     * @param {integer} string to be beautified.
4178     * @return {string} HTML representation.
4179     */
4180         DiffMatchPatch.prototype.diffPrettyHtml = function (diffs) {
4181                 var op,
4182                     data,
4183                     x,
4184                     html = [];
4185                 for (x = 0; x < diffs.length; x++) {
4186                         op = diffs[x][0]; // Operation (insert, delete, equal)
4187                         data = diffs[x][1]; // Text of change.
4188                         switch (op) {
4189                                 case DIFF_INSERT:
4190                                         html[x] = "<ins>" + escapeText(data) + "</ins>";
4191                                         break;
4192                                 case DIFF_DELETE:
4193                                         html[x] = "<del>" + escapeText(data) + "</del>";
4194                                         break;
4195                                 case DIFF_EQUAL:
4196                                         html[x] = "<span>" + escapeText(data) + "</span>";
4197                                         break;
4198                         }
4199                 }
4200                 return html.join("");
4201         };
4202
4203         /**
4204     * Determine the common prefix of two strings.
4205     * @param {string} text1 First string.
4206     * @param {string} text2 Second string.
4207     * @return {number} The number of characters common to the start of each
4208     *     string.
4209     */
4210         DiffMatchPatch.prototype.diffCommonPrefix = function (text1, text2) {
4211                 var pointermid, pointermax, pointermin, pointerstart;
4212
4213                 // Quick check for common null cases.
4214                 if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0)) {
4215                         return 0;
4216                 }
4217
4218                 // Binary search.
4219                 // Performance analysis: https://neil.fraser.name/news/2007/10/09/
4220                 pointermin = 0;
4221                 pointermax = Math.min(text1.length, text2.length);
4222                 pointermid = pointermax;
4223                 pointerstart = 0;
4224                 while (pointermin < pointermid) {
4225                         if (text1.substring(pointerstart, pointermid) === text2.substring(pointerstart, pointermid)) {
4226                                 pointermin = pointermid;
4227                                 pointerstart = pointermin;
4228                         } else {
4229                                 pointermax = pointermid;
4230                         }
4231                         pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
4232                 }
4233                 return pointermid;
4234         };
4235
4236         /**
4237     * Determine the common suffix of two strings.
4238     * @param {string} text1 First string.
4239     * @param {string} text2 Second string.
4240     * @return {number} The number of characters common to the end of each string.
4241     */
4242         DiffMatchPatch.prototype.diffCommonSuffix = function (text1, text2) {
4243                 var pointermid, pointermax, pointermin, pointerend;
4244
4245                 // Quick check for common null cases.
4246                 if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) {
4247                         return 0;
4248                 }
4249
4250                 // Binary search.
4251                 // Performance analysis: https://neil.fraser.name/news/2007/10/09/
4252                 pointermin = 0;
4253                 pointermax = Math.min(text1.length, text2.length);
4254                 pointermid = pointermax;
4255                 pointerend = 0;
4256                 while (pointermin < pointermid) {
4257                         if (text1.substring(text1.length - pointermid, text1.length - pointerend) === text2.substring(text2.length - pointermid, text2.length - pointerend)) {
4258                                 pointermin = pointermid;
4259                                 pointerend = pointermin;
4260                         } else {
4261                                 pointermax = pointermid;
4262                         }
4263                         pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
4264                 }
4265                 return pointermid;
4266         };
4267
4268         /**
4269     * Find the differences between two texts.  Assumes that the texts do not
4270     * have any common prefix or suffix.
4271     * @param {string} text1 Old string to be diffed.
4272     * @param {string} text2 New string to be diffed.
4273     * @param {boolean} checklines Speedup flag.  If false, then don't run a
4274     *     line-level diff first to identify the changed areas.
4275     *     If true, then run a faster, slightly less optimal diff.
4276     * @param {number} deadline Time when the diff should be complete by.
4277     * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
4278     * @private
4279     */
4280         DiffMatchPatch.prototype.diffCompute = function (text1, text2, checklines, deadline) {
4281                 var diffs, longtext, shorttext, i, hm, text1A, text2A, text1B, text2B, midCommon, diffsA, diffsB;
4282
4283                 if (!text1) {
4284
4285                         // Just add some text (speedup).
4286                         return [[DIFF_INSERT, text2]];
4287                 }
4288
4289                 if (!text2) {
4290
4291                         // Just delete some text (speedup).
4292                         return [[DIFF_DELETE, text1]];
4293                 }
4294
4295                 longtext = text1.length > text2.length ? text1 : text2;
4296                 shorttext = text1.length > text2.length ? text2 : text1;
4297                 i = longtext.indexOf(shorttext);
4298                 if (i !== -1) {
4299
4300                         // Shorter text is inside the longer text (speedup).
4301                         diffs = [[DIFF_INSERT, longtext.substring(0, i)], [DIFF_EQUAL, shorttext], [DIFF_INSERT, longtext.substring(i + shorttext.length)]];
4302
4303                         // Swap insertions for deletions if diff is reversed.
4304                         if (text1.length > text2.length) {
4305                                 diffs[0][0] = diffs[2][0] = DIFF_DELETE;
4306                         }
4307                         return diffs;
4308                 }
4309
4310                 if (shorttext.length === 1) {
4311
4312                         // Single character string.
4313                         // After the previous speedup, the character can't be an equality.
4314                         return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
4315                 }
4316
4317                 // Check to see if the problem can be split in two.
4318                 hm = this.diffHalfMatch(text1, text2);
4319                 if (hm) {
4320
4321                         // A half-match was found, sort out the return data.
4322                         text1A = hm[0];
4323                         text1B = hm[1];
4324                         text2A = hm[2];
4325                         text2B = hm[3];
4326                         midCommon = hm[4];
4327
4328                         // Send both pairs off for separate processing.
4329                         diffsA = this.DiffMain(text1A, text2A, checklines, deadline);
4330                         diffsB = this.DiffMain(text1B, text2B, checklines, deadline);
4331
4332                         // Merge the results.
4333                         return diffsA.concat([[DIFF_EQUAL, midCommon]], diffsB);
4334                 }
4335
4336                 if (checklines && text1.length > 100 && text2.length > 100) {
4337                         return this.diffLineMode(text1, text2, deadline);
4338                 }
4339
4340                 return this.diffBisect(text1, text2, deadline);
4341         };
4342
4343         /**
4344     * Do the two texts share a substring which is at least half the length of the
4345     * longer text?
4346     * This speedup can produce non-minimal diffs.
4347     * @param {string} text1 First string.
4348     * @param {string} text2 Second string.
4349     * @return {Array.<string>} Five element Array, containing the prefix of
4350     *     text1, the suffix of text1, the prefix of text2, the suffix of
4351     *     text2 and the common middle.  Or null if there was no match.
4352     * @private
4353     */
4354         DiffMatchPatch.prototype.diffHalfMatch = function (text1, text2) {
4355                 var longtext, shorttext, dmp, text1A, text2B, text2A, text1B, midCommon, hm1, hm2, hm;
4356
4357                 longtext = text1.length > text2.length ? text1 : text2;
4358                 shorttext = text1.length > text2.length ? text2 : text1;
4359                 if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {
4360                         return null; // Pointless.
4361                 }
4362                 dmp = this; // 'this' becomes 'window' in a closure.
4363
4364                 /**
4365      * Does a substring of shorttext exist within longtext such that the substring
4366      * is at least half the length of longtext?
4367      * Closure, but does not reference any external variables.
4368      * @param {string} longtext Longer string.
4369      * @param {string} shorttext Shorter string.
4370      * @param {number} i Start index of quarter length substring within longtext.
4371      * @return {Array.<string>} Five element Array, containing the prefix of
4372      *     longtext, the suffix of longtext, the prefix of shorttext, the suffix
4373      *     of shorttext and the common middle.  Or null if there was no match.
4374      * @private
4375      */
4376                 function diffHalfMatchI(longtext, shorttext, i) {
4377                         var seed, j, bestCommon, prefixLength, suffixLength, bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
4378
4379                         // Start with a 1/4 length substring at position i as a seed.
4380                         seed = longtext.substring(i, i + Math.floor(longtext.length / 4));
4381                         j = -1;
4382                         bestCommon = "";
4383                         while ((j = shorttext.indexOf(seed, j + 1)) !== -1) {
4384                                 prefixLength = dmp.diffCommonPrefix(longtext.substring(i), shorttext.substring(j));
4385                                 suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), shorttext.substring(0, j));
4386                                 if (bestCommon.length < suffixLength + prefixLength) {
4387                                         bestCommon = shorttext.substring(j - suffixLength, j) + shorttext.substring(j, j + prefixLength);
4388                                         bestLongtextA = longtext.substring(0, i - suffixLength);
4389                                         bestLongtextB = longtext.substring(i + prefixLength);
4390                                         bestShorttextA = shorttext.substring(0, j - suffixLength);
4391                                         bestShorttextB = shorttext.substring(j + prefixLength);
4392                                 }
4393                         }
4394                         if (bestCommon.length * 2 >= longtext.length) {
4395                                 return [bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB, bestCommon];
4396                         } else {
4397                                 return null;
4398                         }
4399                 }
4400
4401                 // First check if the second quarter is the seed for a half-match.
4402                 hm1 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 4));
4403
4404                 // Check again based on the third quarter.
4405                 hm2 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 2));
4406                 if (!hm1 && !hm2) {
4407                         return null;
4408                 } else if (!hm2) {
4409                         hm = hm1;
4410                 } else if (!hm1) {
4411                         hm = hm2;
4412                 } else {
4413
4414                         // Both matched.  Select the longest.
4415                         hm = hm1[4].length > hm2[4].length ? hm1 : hm2;
4416                 }
4417
4418                 // A half-match was found, sort out the return data.
4419                 if (text1.length > text2.length) {
4420                         text1A = hm[0];
4421                         text1B = hm[1];
4422                         text2A = hm[2];
4423                         text2B = hm[3];
4424                 } else {
4425                         text2A = hm[0];
4426                         text2B = hm[1];
4427                         text1A = hm[2];
4428                         text1B = hm[3];
4429                 }
4430                 midCommon = hm[4];
4431                 return [text1A, text1B, text2A, text2B, midCommon];
4432         };
4433
4434         /**
4435     * Do a quick line-level diff on both strings, then rediff the parts for
4436     * greater accuracy.
4437     * This speedup can produce non-minimal diffs.
4438     * @param {string} text1 Old string to be diffed.
4439     * @param {string} text2 New string to be diffed.
4440     * @param {number} deadline Time when the diff should be complete by.
4441     * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
4442     * @private
4443     */
4444         DiffMatchPatch.prototype.diffLineMode = function (text1, text2, deadline) {
4445                 var a, diffs, linearray, pointer, countInsert, countDelete, textInsert, textDelete, j;
4446
4447                 // Scan the text on a line-by-line basis first.
4448                 a = this.diffLinesToChars(text1, text2);
4449                 text1 = a.chars1;
4450                 text2 = a.chars2;
4451                 linearray = a.lineArray;
4452
4453                 diffs = this.DiffMain(text1, text2, false, deadline);
4454
4455                 // Convert the diff back to original text.
4456                 this.diffCharsToLines(diffs, linearray);
4457
4458                 // Eliminate freak matches (e.g. blank lines)
4459                 this.diffCleanupSemantic(diffs);
4460
4461                 // Rediff any replacement blocks, this time character-by-character.
4462                 // Add a dummy entry at the end.
4463                 diffs.push([DIFF_EQUAL, ""]);
4464                 pointer = 0;
4465                 countDelete = 0;
4466                 countInsert = 0;
4467                 textDelete = "";
4468                 textInsert = "";
4469                 while (pointer < diffs.length) {
4470                         switch (diffs[pointer][0]) {
4471                                 case DIFF_INSERT:
4472                                         countInsert++;
4473                                         textInsert += diffs[pointer][1];
4474                                         break;
4475                                 case DIFF_DELETE:
4476                                         countDelete++;
4477                                         textDelete += diffs[pointer][1];
4478                                         break;
4479                                 case DIFF_EQUAL:
4480
4481                                         // Upon reaching an equality, check for prior redundancies.
4482                                         if (countDelete >= 1 && countInsert >= 1) {
4483
4484                                                 // Delete the offending records and add the merged ones.
4485                                                 diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert);
4486                                                 pointer = pointer - countDelete - countInsert;
4487                                                 a = this.DiffMain(textDelete, textInsert, false, deadline);
4488                                                 for (j = a.length - 1; j >= 0; j--) {
4489                                                         diffs.splice(pointer, 0, a[j]);
4490                                                 }
4491                                                 pointer = pointer + a.length;
4492                                         }
4493                                         countInsert = 0;
4494                                         countDelete = 0;
4495                                         textDelete = "";
4496                                         textInsert = "";
4497                                         break;
4498                         }
4499                         pointer++;
4500                 }
4501                 diffs.pop(); // Remove the dummy entry at the end.
4502
4503                 return diffs;
4504         };
4505
4506         /**
4507     * Find the 'middle snake' of a diff, split the problem in two
4508     * and return the recursively constructed diff.
4509     * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
4510     * @param {string} text1 Old string to be diffed.
4511     * @param {string} text2 New string to be diffed.
4512     * @param {number} deadline Time at which to bail if not yet complete.
4513     * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
4514     * @private
4515     */
4516         DiffMatchPatch.prototype.diffBisect = function (text1, text2, deadline) {
4517                 var text1Length, text2Length, maxD, vOffset, vLength, v1, v2, x, delta, front, k1start, k1end, k2start, k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
4518
4519                 // Cache the text lengths to prevent multiple calls.
4520                 text1Length = text1.length;
4521                 text2Length = text2.length;
4522                 maxD = Math.ceil((text1Length + text2Length) / 2);
4523                 vOffset = maxD;
4524                 vLength = 2 * maxD;
4525                 v1 = new Array(vLength);
4526                 v2 = new Array(vLength);
4527
4528                 // Setting all elements to -1 is faster in Chrome & Firefox than mixing
4529                 // integers and undefined.
4530                 for (x = 0; x < vLength; x++) {
4531                         v1[x] = -1;
4532                         v2[x] = -1;
4533                 }
4534                 v1[vOffset + 1] = 0;
4535                 v2[vOffset + 1] = 0;
4536                 delta = text1Length - text2Length;
4537
4538                 // If the total number of characters is odd, then the front path will collide
4539                 // with the reverse path.
4540                 front = delta % 2 !== 0;
4541
4542                 // Offsets for start and end of k loop.
4543                 // Prevents mapping of space beyond the grid.
4544                 k1start = 0;
4545                 k1end = 0;
4546                 k2start = 0;
4547                 k2end = 0;
4548                 for (d = 0; d < maxD; d++) {
4549
4550                         // Bail out if deadline is reached.
4551                         if (new Date().getTime() > deadline) {
4552                                 break;
4553                         }
4554
4555                         // Walk the front path one step.
4556                         for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
4557                                 k1Offset = vOffset + k1;
4558                                 if (k1 === -d || k1 !== d && v1[k1Offset - 1] < v1[k1Offset + 1]) {
4559                                         x1 = v1[k1Offset + 1];
4560                                 } else {
4561                                         x1 = v1[k1Offset - 1] + 1;
4562                                 }
4563                                 y1 = x1 - k1;
4564                                 while (x1 < text1Length && y1 < text2Length && text1.charAt(x1) === text2.charAt(y1)) {
4565                                         x1++;
4566                                         y1++;
4567                                 }
4568                                 v1[k1Offset] = x1;
4569                                 if (x1 > text1Length) {
4570
4571                                         // Ran off the right of the graph.
4572                                         k1end += 2;
4573                                 } else if (y1 > text2Length) {
4574
4575                                         // Ran off the bottom of the graph.
4576                                         k1start += 2;
4577                                 } else if (front) {
4578                                         k2Offset = vOffset + delta - k1;
4579                                         if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) {
4580
4581                                                 // Mirror x2 onto top-left coordinate system.
4582                                                 x2 = text1Length - v2[k2Offset];
4583                                                 if (x1 >= x2) {
4584
4585                                                         // Overlap detected.
4586                                                         return this.diffBisectSplit(text1, text2, x1, y1, deadline);
4587                                                 }
4588                                         }
4589                                 }
4590                         }
4591
4592                         // Walk the reverse path one step.
4593                         for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
4594                                 k2Offset = vOffset + k2;
4595                                 if (k2 === -d || k2 !== d && v2[k2Offset - 1] < v2[k2Offset + 1]) {
4596                                         x2 = v2[k2Offset + 1];
4597                                 } else {
4598                                         x2 = v2[k2Offset - 1] + 1;
4599                                 }
4600                                 y2 = x2 - k2;
4601                                 while (x2 < text1Length && y2 < text2Length && text1.charAt(text1Length - x2 - 1) === text2.charAt(text2Length - y2 - 1)) {
4602                                         x2++;
4603                                         y2++;
4604                                 }
4605                                 v2[k2Offset] = x2;
4606                                 if (x2 > text1Length) {
4607
4608                                         // Ran off the left of the graph.
4609                                         k2end += 2;
4610                                 } else if (y2 > text2Length) {
4611
4612                                         // Ran off the top of the graph.
4613                                         k2start += 2;
4614                                 } else if (!front) {
4615                                         k1Offset = vOffset + delta - k2;
4616                                         if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) {
4617                                                 x1 = v1[k1Offset];
4618                                                 y1 = vOffset + x1 - k1Offset;
4619
4620                                                 // Mirror x2 onto top-left coordinate system.
4621                                                 x2 = text1Length - x2;
4622                                                 if (x1 >= x2) {
4623
4624                                                         // Overlap detected.
4625                                                         return this.diffBisectSplit(text1, text2, x1, y1, deadline);
4626                                                 }
4627                                         }
4628                                 }
4629                         }
4630                 }
4631
4632                 // Diff took too long and hit the deadline or
4633                 // number of diffs equals number of characters, no commonality at all.
4634                 return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
4635         };
4636
4637         /**
4638     * Given the location of the 'middle snake', split the diff in two parts
4639     * and recurse.
4640     * @param {string} text1 Old string to be diffed.
4641     * @param {string} text2 New string to be diffed.
4642     * @param {number} x Index of split point in text1.
4643     * @param {number} y Index of split point in text2.
4644     * @param {number} deadline Time at which to bail if not yet complete.
4645     * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
4646     * @private
4647     */
4648         DiffMatchPatch.prototype.diffBisectSplit = function (text1, text2, x, y, deadline) {
4649                 var text1a, text1b, text2a, text2b, diffs, diffsb;
4650                 text1a = text1.substring(0, x);
4651                 text2a = text2.substring(0, y);
4652                 text1b = text1.substring(x);
4653                 text2b = text2.substring(y);
4654
4655                 // Compute both diffs serially.
4656                 diffs = this.DiffMain(text1a, text2a, false, deadline);
4657                 diffsb = this.DiffMain(text1b, text2b, false, deadline);
4658
4659                 return diffs.concat(diffsb);
4660         };
4661
4662         /**
4663     * Reduce the number of edits by eliminating semantically trivial equalities.
4664     * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
4665     */
4666         DiffMatchPatch.prototype.diffCleanupSemantic = function (diffs) {
4667                 var changes, equalities, equalitiesLength, lastequality, pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2;
4668                 changes = false;
4669                 equalities = []; // Stack of indices where equalities are found.
4670                 equalitiesLength = 0; // Keeping our own length var is faster in JS.
4671                 /** @type {?string} */
4672                 lastequality = null;
4673
4674                 // Always equal to diffs[equalities[equalitiesLength - 1]][1]
4675                 pointer = 0; // Index of current position.
4676
4677                 // Number of characters that changed prior to the equality.
4678                 lengthInsertions1 = 0;
4679                 lengthDeletions1 = 0;
4680
4681                 // Number of characters that changed after the equality.
4682                 lengthInsertions2 = 0;
4683                 lengthDeletions2 = 0;
4684                 while (pointer < diffs.length) {
4685                         if (diffs[pointer][0] === DIFF_EQUAL) {
4686                                 // Equality found.
4687                                 equalities[equalitiesLength++] = pointer;
4688                                 lengthInsertions1 = lengthInsertions2;
4689                                 lengthDeletions1 = lengthDeletions2;
4690                                 lengthInsertions2 = 0;
4691                                 lengthDeletions2 = 0;
4692                                 lastequality = diffs[pointer][1];
4693                         } else {
4694                                 // An insertion or deletion.
4695                                 if (diffs[pointer][0] === DIFF_INSERT) {
4696                                         lengthInsertions2 += diffs[pointer][1].length;
4697                                 } else {
4698                                         lengthDeletions2 += diffs[pointer][1].length;
4699                                 }
4700
4701                                 // Eliminate an equality that is smaller or equal to the edits on both
4702                                 // sides of it.
4703                                 if (lastequality && lastequality.length <= Math.max(lengthInsertions1, lengthDeletions1) && lastequality.length <= Math.max(lengthInsertions2, lengthDeletions2)) {
4704
4705                                         // Duplicate record.
4706                                         diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
4707
4708                                         // Change second copy to insert.
4709                                         diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
4710
4711                                         // Throw away the equality we just deleted.
4712                                         equalitiesLength--;
4713
4714                                         // Throw away the previous equality (it needs to be reevaluated).
4715                                         equalitiesLength--;
4716                                         pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
4717
4718                                         // Reset the counters.
4719                                         lengthInsertions1 = 0;
4720                                         lengthDeletions1 = 0;
4721                                         lengthInsertions2 = 0;
4722                                         lengthDeletions2 = 0;
4723                                         lastequality = null;
4724                                         changes = true;
4725                                 }
4726                         }
4727                         pointer++;
4728                 }
4729
4730                 // Normalize the diff.
4731                 if (changes) {
4732                         this.diffCleanupMerge(diffs);
4733                 }
4734
4735                 // Find any overlaps between deletions and insertions.
4736                 // e.g: <del>abcxxx</del><ins>xxxdef</ins>
4737                 //   -> <del>abc</del>xxx<ins>def</ins>
4738                 // e.g: <del>xxxabc</del><ins>defxxx</ins>
4739                 //   -> <ins>def</ins>xxx<del>abc</del>
4740                 // Only extract an overlap if it is as big as the edit ahead or behind it.
4741                 pointer = 1;
4742                 while (pointer < diffs.length) {
4743                         if (diffs[pointer - 1][0] === DIFF_DELETE && diffs[pointer][0] === DIFF_INSERT) {
4744                                 deletion = diffs[pointer - 1][1];
4745                                 insertion = diffs[pointer][1];
4746                                 overlapLength1 = this.diffCommonOverlap(deletion, insertion);
4747                                 overlapLength2 = this.diffCommonOverlap(insertion, deletion);
4748                                 if (overlapLength1 >= overlapLength2) {
4749                                         if (overlapLength1 >= deletion.length / 2 || overlapLength1 >= insertion.length / 2) {
4750
4751                                                 // Overlap found.  Insert an equality and trim the surrounding edits.
4752                                                 diffs.splice(pointer, 0, [DIFF_EQUAL, insertion.substring(0, overlapLength1)]);
4753                                                 diffs[pointer - 1][1] = deletion.substring(0, deletion.length - overlapLength1);
4754                                                 diffs[pointer + 1][1] = insertion.substring(overlapLength1);
4755                                                 pointer++;
4756                                         }
4757                                 } else {
4758                                         if (overlapLength2 >= deletion.length / 2 || overlapLength2 >= insertion.length / 2) {
4759
4760                                                 // Reverse overlap found.
4761                                                 // Insert an equality and swap and trim the surrounding edits.
4762                                                 diffs.splice(pointer, 0, [DIFF_EQUAL, deletion.substring(0, overlapLength2)]);
4763
4764                                                 diffs[pointer - 1][0] = DIFF_INSERT;
4765                                                 diffs[pointer - 1][1] = insertion.substring(0, insertion.length - overlapLength2);
4766                                                 diffs[pointer + 1][0] = DIFF_DELETE;
4767                                                 diffs[pointer + 1][1] = deletion.substring(overlapLength2);
4768                                                 pointer++;
4769                                         }
4770                                 }
4771                                 pointer++;
4772                         }
4773                         pointer++;
4774                 }
4775         };
4776
4777         /**
4778     * Determine if the suffix of one string is the prefix of another.
4779     * @param {string} text1 First string.
4780     * @param {string} text2 Second string.
4781     * @return {number} The number of characters common to the end of the first
4782     *     string and the start of the second string.
4783     * @private
4784     */
4785         DiffMatchPatch.prototype.diffCommonOverlap = function (text1, text2) {
4786                 var text1Length, text2Length, textLength, best, length, pattern, found;
4787
4788                 // Cache the text lengths to prevent multiple calls.
4789                 text1Length = text1.length;
4790                 text2Length = text2.length;
4791
4792                 // Eliminate the null case.
4793                 if (text1Length === 0 || text2Length === 0) {
4794                         return 0;
4795                 }
4796
4797                 // Truncate the longer string.
4798                 if (text1Length > text2Length) {
4799                         text1 = text1.substring(text1Length - text2Length);
4800                 } else if (text1Length < text2Length) {
4801                         text2 = text2.substring(0, text1Length);
4802                 }
4803                 textLength = Math.min(text1Length, text2Length);
4804
4805                 // Quick check for the worst case.
4806                 if (text1 === text2) {
4807                         return textLength;
4808                 }
4809
4810                 // Start by looking for a single character match
4811                 // and increase length until no match is found.
4812                 // Performance analysis: https://neil.fraser.name/news/2010/11/04/
4813                 best = 0;
4814                 length = 1;
4815                 while (true) {
4816                         pattern = text1.substring(textLength - length);
4817                         found = text2.indexOf(pattern);
4818                         if (found === -1) {
4819                                 return best;
4820                         }
4821                         length += found;
4822                         if (found === 0 || text1.substring(textLength - length) === text2.substring(0, length)) {
4823                                 best = length;
4824                                 length++;
4825                         }
4826                 }
4827         };
4828
4829         /**
4830     * Split two texts into an array of strings.  Reduce the texts to a string of
4831     * hashes where each Unicode character represents one line.
4832     * @param {string} text1 First string.
4833     * @param {string} text2 Second string.
4834     * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
4835     *     An object containing the encoded text1, the encoded text2 and
4836     *     the array of unique strings.
4837     *     The zeroth element of the array of unique strings is intentionally blank.
4838     * @private
4839     */
4840         DiffMatchPatch.prototype.diffLinesToChars = function (text1, text2) {
4841                 var lineArray, lineHash, chars1, chars2;
4842                 lineArray = []; // E.g. lineArray[4] === 'Hello\n'
4843                 lineHash = {}; // E.g. lineHash['Hello\n'] === 4
4844
4845                 // '\x00' is a valid character, but various debuggers don't like it.
4846                 // So we'll insert a junk entry to avoid generating a null character.
4847                 lineArray[0] = "";
4848
4849                 /**
4850      * Split a text into an array of strings.  Reduce the texts to a string of
4851      * hashes where each Unicode character represents one line.
4852      * Modifies linearray and linehash through being a closure.
4853      * @param {string} text String to encode.
4854      * @return {string} Encoded string.
4855      * @private
4856      */
4857                 function diffLinesToCharsMunge(text) {
4858                         var chars, lineStart, lineEnd, lineArrayLength, line;
4859                         chars = "";
4860
4861                         // Walk the text, pulling out a substring for each line.
4862                         // text.split('\n') would would temporarily double our memory footprint.
4863                         // Modifying text would create many large strings to garbage collect.
4864                         lineStart = 0;
4865                         lineEnd = -1;
4866
4867                         // Keeping our own length variable is faster than looking it up.
4868                         lineArrayLength = lineArray.length;
4869                         while (lineEnd < text.length - 1) {
4870                                 lineEnd = text.indexOf("\n", lineStart);
4871                                 if (lineEnd === -1) {
4872                                         lineEnd = text.length - 1;
4873                                 }
4874                                 line = text.substring(lineStart, lineEnd + 1);
4875                                 lineStart = lineEnd + 1;
4876
4877                                 if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : lineHash[line] !== undefined) {
4878                                         chars += String.fromCharCode(lineHash[line]);
4879                                 } else {
4880                                         chars += String.fromCharCode(lineArrayLength);
4881                                         lineHash[line] = lineArrayLength;
4882                                         lineArray[lineArrayLength++] = line;
4883                                 }
4884                         }
4885                         return chars;
4886                 }
4887
4888                 chars1 = diffLinesToCharsMunge(text1);
4889                 chars2 = diffLinesToCharsMunge(text2);
4890                 return {
4891                         chars1: chars1,
4892                         chars2: chars2,
4893                         lineArray: lineArray
4894                 };
4895         };
4896
4897         /**
4898     * Rehydrate the text in a diff from a string of line hashes to real lines of
4899     * text.
4900     * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
4901     * @param {!Array.<string>} lineArray Array of unique strings.
4902     * @private
4903     */
4904         DiffMatchPatch.prototype.diffCharsToLines = function (diffs, lineArray) {
4905                 var x, chars, text, y;
4906                 for (x = 0; x < diffs.length; x++) {
4907                         chars = diffs[x][1];
4908                         text = [];
4909                         for (y = 0; y < chars.length; y++) {
4910                                 text[y] = lineArray[chars.charCodeAt(y)];
4911                         }
4912                         diffs[x][1] = text.join("");
4913                 }
4914         };
4915
4916         /**
4917     * Reorder and merge like edit sections.  Merge equalities.
4918     * Any edit section can move as long as it doesn't cross an equality.
4919     * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
4920     */
4921         DiffMatchPatch.prototype.diffCleanupMerge = function (diffs) {
4922                 var pointer, countDelete, countInsert, textInsert, textDelete, commonlength, changes, diffPointer, position;
4923                 diffs.push([DIFF_EQUAL, ""]); // Add a dummy entry at the end.
4924                 pointer = 0;
4925                 countDelete = 0;
4926                 countInsert = 0;
4927                 textDelete = "";
4928                 textInsert = "";
4929
4930                 while (pointer < diffs.length) {
4931                         switch (diffs[pointer][0]) {
4932                                 case DIFF_INSERT:
4933                                         countInsert++;
4934                                         textInsert += diffs[pointer][1];
4935                                         pointer++;
4936                                         break;
4937                                 case DIFF_DELETE:
4938                                         countDelete++;
4939                                         textDelete += diffs[pointer][1];
4940                                         pointer++;
4941                                         break;
4942                                 case DIFF_EQUAL:
4943
4944                                         // Upon reaching an equality, check for prior redundancies.
4945                                         if (countDelete + countInsert > 1) {
4946                                                 if (countDelete !== 0 && countInsert !== 0) {
4947
4948                                                         // Factor out any common prefixes.
4949                                                         commonlength = this.diffCommonPrefix(textInsert, textDelete);
4950                                                         if (commonlength !== 0) {
4951                                                                 if (pointer - countDelete - countInsert > 0 && diffs[pointer - countDelete - countInsert - 1][0] === DIFF_EQUAL) {
4952                                                                         diffs[pointer - countDelete - countInsert - 1][1] += textInsert.substring(0, commonlength);
4953                                                                 } else {
4954                                                                         diffs.splice(0, 0, [DIFF_EQUAL, textInsert.substring(0, commonlength)]);
4955                                                                         pointer++;
4956                                                                 }
4957                                                                 textInsert = textInsert.substring(commonlength);
4958                                                                 textDelete = textDelete.substring(commonlength);
4959                                                         }
4960
4961                                                         // Factor out any common suffixies.
4962                                                         commonlength = this.diffCommonSuffix(textInsert, textDelete);
4963                                                         if (commonlength !== 0) {
4964                                                                 diffs[pointer][1] = textInsert.substring(textInsert.length - commonlength) + diffs[pointer][1];
4965                                                                 textInsert = textInsert.substring(0, textInsert.length - commonlength);
4966                                                                 textDelete = textDelete.substring(0, textDelete.length - commonlength);
4967                                                         }
4968                                                 }
4969
4970                                                 // Delete the offending records and add the merged ones.
4971                                                 if (countDelete === 0) {
4972                                                         diffs.splice(pointer - countInsert, countDelete + countInsert, [DIFF_INSERT, textInsert]);
4973                                                 } else if (countInsert === 0) {
4974                                                         diffs.splice(pointer - countDelete, countDelete + countInsert, [DIFF_DELETE, textDelete]);
4975                                                 } else {
4976                                                         diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert, [DIFF_DELETE, textDelete], [DIFF_INSERT, textInsert]);
4977                                                 }
4978                                                 pointer = pointer - countDelete - countInsert + (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1;
4979                                         } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) {
4980
4981                                                 // Merge this equality with the previous one.
4982                                                 diffs[pointer - 1][1] += diffs[pointer][1];
4983                                                 diffs.splice(pointer, 1);
4984                                         } else {
4985                                                 pointer++;
4986                                         }
4987                                         countInsert = 0;
4988                                         countDelete = 0;
4989                                         textDelete = "";
4990                                         textInsert = "";
4991                                         break;
4992                         }
4993                 }
4994                 if (diffs[diffs.length - 1][1] === "") {
4995                         diffs.pop(); // Remove the dummy entry at the end.
4996                 }
4997
4998                 // Second pass: look for single edits surrounded on both sides by equalities
4999                 // which can be shifted sideways to eliminate an equality.
5000                 // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
5001                 changes = false;
5002                 pointer = 1;
5003
5004                 // Intentionally ignore the first and last element (don't need checking).
5005                 while (pointer < diffs.length - 1) {
5006                         if (diffs[pointer - 1][0] === DIFF_EQUAL && diffs[pointer + 1][0] === DIFF_EQUAL) {
5007
5008                                 diffPointer = diffs[pointer][1];
5009                                 position = diffPointer.substring(diffPointer.length - diffs[pointer - 1][1].length);
5010
5011                                 // This is a single edit surrounded by equalities.
5012                                 if (position === diffs[pointer - 1][1]) {
5013
5014                                         // Shift the edit over the previous equality.
5015                                         diffs[pointer][1] = diffs[pointer - 1][1] + diffs[pointer][1].substring(0, diffs[pointer][1].length - diffs[pointer - 1][1].length);
5016                                         diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
5017                                         diffs.splice(pointer - 1, 1);
5018                                         changes = true;
5019                                 } else if (diffPointer.substring(0, diffs[pointer + 1][1].length) === diffs[pointer + 1][1]) {
5020
5021                                         // Shift the edit over the next equality.
5022                                         diffs[pointer - 1][1] += diffs[pointer + 1][1];
5023                                         diffs[pointer][1] = diffs[pointer][1].substring(diffs[pointer + 1][1].length) + diffs[pointer + 1][1];
5024                                         diffs.splice(pointer + 1, 1);
5025                                         changes = true;
5026                                 }
5027                         }
5028                         pointer++;
5029                 }
5030
5031                 // If shifts were made, the diff needs reordering and another shift sweep.
5032                 if (changes) {
5033                         this.diffCleanupMerge(diffs);
5034                 }
5035         };
5036
5037         return function (o, n) {
5038                 var diff, output, text;
5039                 diff = new DiffMatchPatch();
5040                 output = diff.DiffMain(o, n);
5041                 diff.diffCleanupEfficiency(output);
5042                 text = diff.diffPrettyHtml(output);
5043
5044                 return text;
5045         };
5046   }();
5047
5048 }((function() { return this; }())));