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