Merge "Don't check for LIBXML_PARSEHUGE presence"
[lhc/web/wiklou.git] / resources / lib / qunitjs / qunit.js
1 /*!
2 * QUnit 2.9.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: 2019-01-07T16:37Z
10 */
11 (function (global$1) {
12 'use strict';
13
14 global$1 = global$1 && global$1.hasOwnProperty('default') ? global$1['default'] : global$1;
15
16 var window$1 = global$1.window;
17 var self$1 = global$1.self;
18 var console = global$1.console;
19 var setTimeout$1 = global$1.setTimeout;
20 var clearTimeout = global$1.clearTimeout;
21
22 var document$1 = window$1 && window$1.document;
23 var navigator = window$1 && window$1.navigator;
24
25 var localSessionStorage = function () {
26 var x = "qunit-test-string";
27 try {
28 global$1.sessionStorage.setItem(x, x);
29 global$1.sessionStorage.removeItem(x);
30 return global$1.sessionStorage;
31 } catch (e) {
32 return undefined;
33 }
34 }();
35
36 /**
37 * Returns a function that proxies to the given method name on the globals
38 * console object. The proxy will also detect if the console doesn't exist and
39 * will appropriately no-op. This allows support for IE9, which doesn't have a
40 * console if the developer tools are not open.
41 */
42 function consoleProxy(method) {
43 return function () {
44 if (console) {
45 console[method].apply(console, arguments);
46 }
47 };
48 }
49
50 var Logger = {
51 warn: consoleProxy("warn")
52 };
53
54 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
55 return typeof obj;
56 } : function (obj) {
57 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
58 };
59
60
61
62
63
64
65
66
67
68
69
70 var classCallCheck = function (instance, Constructor) {
71 if (!(instance instanceof Constructor)) {
72 throw new TypeError("Cannot call a class as a function");
73 }
74 };
75
76 var createClass = function () {
77 function defineProperties(target, props) {
78 for (var i = 0; i < props.length; i++) {
79 var descriptor = props[i];
80 descriptor.enumerable = descriptor.enumerable || false;
81 descriptor.configurable = true;
82 if ("value" in descriptor) descriptor.writable = true;
83 Object.defineProperty(target, descriptor.key, descriptor);
84 }
85 }
86
87 return function (Constructor, protoProps, staticProps) {
88 if (protoProps) defineProperties(Constructor.prototype, protoProps);
89 if (staticProps) defineProperties(Constructor, staticProps);
90 return Constructor;
91 };
92 }();
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 var toConsumableArray = function (arr) {
135 if (Array.isArray(arr)) {
136 for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
137
138 return arr2;
139 } else {
140 return Array.from(arr);
141 }
142 };
143
144 var toString = Object.prototype.toString;
145 var hasOwn = Object.prototype.hasOwnProperty;
146 var now = Date.now || function () {
147 return new Date().getTime();
148 };
149
150 var hasPerformanceApi = detectPerformanceApi();
151 var performance = hasPerformanceApi ? window$1.performance : undefined;
152 var performanceNow = hasPerformanceApi ? performance.now.bind(performance) : now;
153
154 function detectPerformanceApi() {
155 return window$1 && typeof window$1.performance !== "undefined" && typeof window$1.performance.mark === "function" && typeof window$1.performance.measure === "function";
156 }
157
158 function measure(comment, startMark, endMark) {
159
160 // `performance.measure` may fail if the mark could not be found.
161 // reasons a specific mark could not be found include: outside code invoking `performance.clearMarks()`
162 try {
163 performance.measure(comment, startMark, endMark);
164 } catch (ex) {
165 Logger.warn("performance.measure could not be executed because of ", ex.message);
166 }
167 }
168
169 var defined = {
170 document: window$1 && window$1.document !== undefined,
171 setTimeout: setTimeout$1 !== undefined
172 };
173
174 // Returns a new Array with the elements that are in a but not in b
175 function diff(a, b) {
176 var i,
177 j,
178 result = a.slice();
179
180 for (i = 0; i < result.length; i++) {
181 for (j = 0; j < b.length; j++) {
182 if (result[i] === b[j]) {
183 result.splice(i, 1);
184 i--;
185 break;
186 }
187 }
188 }
189 return result;
190 }
191
192 /**
193 * Determines whether an element exists in a given array or not.
194 *
195 * @method inArray
196 * @param {Any} elem
197 * @param {Array} array
198 * @return {Boolean}
199 */
200 function inArray(elem, array) {
201 return array.indexOf(elem) !== -1;
202 }
203
204 /**
205 * Makes a clone of an object using only Array or Object as base,
206 * and copies over the own enumerable properties.
207 *
208 * @param {Object} obj
209 * @return {Object} New object with only the own properties (recursively).
210 */
211 function objectValues(obj) {
212 var key,
213 val,
214 vals = is("array", obj) ? [] : {};
215 for (key in obj) {
216 if (hasOwn.call(obj, key)) {
217 val = obj[key];
218 vals[key] = val === Object(val) ? objectValues(val) : val;
219 }
220 }
221 return vals;
222 }
223
224 function extend(a, b, undefOnly) {
225 for (var prop in b) {
226 if (hasOwn.call(b, prop)) {
227 if (b[prop] === undefined) {
228 delete a[prop];
229 } else if (!(undefOnly && typeof a[prop] !== "undefined")) {
230 a[prop] = b[prop];
231 }
232 }
233 }
234
235 return a;
236 }
237
238 function objectType(obj) {
239 if (typeof obj === "undefined") {
240 return "undefined";
241 }
242
243 // Consider: typeof null === object
244 if (obj === null) {
245 return "null";
246 }
247
248 var match = toString.call(obj).match(/^\[object\s(.*)\]$/),
249 type = match && match[1];
250
251 switch (type) {
252 case "Number":
253 if (isNaN(obj)) {
254 return "nan";
255 }
256 return "number";
257 case "String":
258 case "Boolean":
259 case "Array":
260 case "Set":
261 case "Map":
262 case "Date":
263 case "RegExp":
264 case "Function":
265 case "Symbol":
266 return type.toLowerCase();
267 default:
268 return typeof obj === "undefined" ? "undefined" : _typeof(obj);
269 }
270 }
271
272 // Safe object type checking
273 function is(type, obj) {
274 return objectType(obj) === type;
275 }
276
277 // Based on Java's String.hashCode, a simple but not
278 // rigorously collision resistant hashing function
279 function generateHash(module, testName) {
280 var str = module + "\x1C" + testName;
281 var hash = 0;
282
283 for (var i = 0; i < str.length; i++) {
284 hash = (hash << 5) - hash + str.charCodeAt(i);
285 hash |= 0;
286 }
287
288 // Convert the possibly negative integer hash code into an 8 character hex string, which isn't
289 // strictly necessary but increases user understanding that the id is a SHA-like hash
290 var hex = (0x100000000 + hash).toString(16);
291 if (hex.length < 8) {
292 hex = "0000000" + hex;
293 }
294
295 return hex.slice(-8);
296 }
297
298 // Test for equality any JavaScript type.
299 // Authors: Philippe Rathé <prathe@gmail.com>, David Chan <david@troi.org>
300 var equiv = (function () {
301
302 // Value pairs queued for comparison. Used for breadth-first processing order, recursion
303 // detection and avoiding repeated comparison (see below for details).
304 // Elements are { a: val, b: val }.
305 var pairs = [];
306
307 var getProto = Object.getPrototypeOf || function (obj) {
308 return obj.__proto__;
309 };
310
311 function useStrictEquality(a, b) {
312
313 // This only gets called if a and b are not strict equal, and is used to compare on
314 // the primitive values inside object wrappers. For example:
315 // `var i = 1;`
316 // `var j = new Number(1);`
317 // Neither a nor b can be null, as a !== b and they have the same type.
318 if ((typeof a === "undefined" ? "undefined" : _typeof(a)) === "object") {
319 a = a.valueOf();
320 }
321 if ((typeof b === "undefined" ? "undefined" : _typeof(b)) === "object") {
322 b = b.valueOf();
323 }
324
325 return a === b;
326 }
327
328 function compareConstructors(a, b) {
329 var protoA = getProto(a);
330 var protoB = getProto(b);
331
332 // Comparing constructors is more strict than using `instanceof`
333 if (a.constructor === b.constructor) {
334 return true;
335 }
336
337 // Ref #851
338 // If the obj prototype descends from a null constructor, treat it
339 // as a null prototype.
340 if (protoA && protoA.constructor === null) {
341 protoA = null;
342 }
343 if (protoB && protoB.constructor === null) {
344 protoB = null;
345 }
346
347 // Allow objects with no prototype to be equivalent to
348 // objects with Object as their constructor.
349 if (protoA === null && protoB === Object.prototype || protoB === null && protoA === Object.prototype) {
350 return true;
351 }
352
353 return false;
354 }
355
356 function getRegExpFlags(regexp) {
357 return "flags" in regexp ? regexp.flags : regexp.toString().match(/[gimuy]*$/)[0];
358 }
359
360 function isContainer(val) {
361 return ["object", "array", "map", "set"].indexOf(objectType(val)) !== -1;
362 }
363
364 function breadthFirstCompareChild(a, b) {
365
366 // If a is a container not reference-equal to b, postpone the comparison to the
367 // end of the pairs queue -- unless (a, b) has been seen before, in which case skip
368 // over the pair.
369 if (a === b) {
370 return true;
371 }
372 if (!isContainer(a)) {
373 return typeEquiv(a, b);
374 }
375 if (pairs.every(function (pair) {
376 return pair.a !== a || pair.b !== b;
377 })) {
378
379 // Not yet started comparing this pair
380 pairs.push({ a: a, b: b });
381 }
382 return true;
383 }
384
385 var callbacks = {
386 "string": useStrictEquality,
387 "boolean": useStrictEquality,
388 "number": useStrictEquality,
389 "null": useStrictEquality,
390 "undefined": useStrictEquality,
391 "symbol": useStrictEquality,
392 "date": useStrictEquality,
393
394 "nan": function nan() {
395 return true;
396 },
397
398 "regexp": function regexp(a, b) {
399 return a.source === b.source &&
400
401 // Include flags in the comparison
402 getRegExpFlags(a) === getRegExpFlags(b);
403 },
404
405 // abort (identical references / instance methods were skipped earlier)
406 "function": function _function() {
407 return false;
408 },
409
410 "array": function array(a, b) {
411 var i, len;
412
413 len = a.length;
414 if (len !== b.length) {
415
416 // Safe and faster
417 return false;
418 }
419
420 for (i = 0; i < len; i++) {
421
422 // Compare non-containers; queue non-reference-equal containers
423 if (!breadthFirstCompareChild(a[i], b[i])) {
424 return false;
425 }
426 }
427 return true;
428 },
429
430 // Define sets a and b to be equivalent if for each element aVal in a, there
431 // is some element bVal in b such that aVal and bVal are equivalent. Element
432 // repetitions are not counted, so these are equivalent:
433 // a = new Set( [ {}, [], [] ] );
434 // b = new Set( [ {}, {}, [] ] );
435 "set": function set$$1(a, b) {
436 var innerEq,
437 outerEq = true;
438
439 if (a.size !== b.size) {
440
441 // This optimization has certain quirks because of the lack of
442 // repetition counting. For instance, adding the same
443 // (reference-identical) element to two equivalent sets can
444 // make them non-equivalent.
445 return false;
446 }
447
448 a.forEach(function (aVal) {
449
450 // Short-circuit if the result is already known. (Using for...of
451 // with a break clause would be cleaner here, but it would cause
452 // a syntax error on older Javascript implementations even if
453 // Set is unused)
454 if (!outerEq) {
455 return;
456 }
457
458 innerEq = false;
459
460 b.forEach(function (bVal) {
461 var parentPairs;
462
463 // Likewise, short-circuit if the result is already known
464 if (innerEq) {
465 return;
466 }
467
468 // Swap out the global pairs list, as the nested call to
469 // innerEquiv will clobber its contents
470 parentPairs = pairs;
471 if (innerEquiv(bVal, aVal)) {
472 innerEq = true;
473 }
474
475 // Replace the global pairs list
476 pairs = parentPairs;
477 });
478
479 if (!innerEq) {
480 outerEq = false;
481 }
482 });
483
484 return outerEq;
485 },
486
487 // Define maps a and b to be equivalent if for each key-value pair (aKey, aVal)
488 // in a, there is some key-value pair (bKey, bVal) in b such that
489 // [ aKey, aVal ] and [ bKey, bVal ] are equivalent. Key repetitions are not
490 // counted, so these are equivalent:
491 // a = new Map( [ [ {}, 1 ], [ {}, 1 ], [ [], 1 ] ] );
492 // b = new Map( [ [ {}, 1 ], [ [], 1 ], [ [], 1 ] ] );
493 "map": function map(a, b) {
494 var innerEq,
495 outerEq = true;
496
497 if (a.size !== b.size) {
498
499 // This optimization has certain quirks because of the lack of
500 // repetition counting. For instance, adding the same
501 // (reference-identical) key-value pair to two equivalent maps
502 // can make them non-equivalent.
503 return false;
504 }
505
506 a.forEach(function (aVal, aKey) {
507
508 // Short-circuit if the result is already known. (Using for...of
509 // with a break clause would be cleaner here, but it would cause
510 // a syntax error on older Javascript implementations even if
511 // Map is unused)
512 if (!outerEq) {
513 return;
514 }
515
516 innerEq = false;
517
518 b.forEach(function (bVal, bKey) {
519 var parentPairs;
520
521 // Likewise, short-circuit if the result is already known
522 if (innerEq) {
523 return;
524 }
525
526 // Swap out the global pairs list, as the nested call to
527 // innerEquiv will clobber its contents
528 parentPairs = pairs;
529 if (innerEquiv([bVal, bKey], [aVal, aKey])) {
530 innerEq = true;
531 }
532
533 // Replace the global pairs list
534 pairs = parentPairs;
535 });
536
537 if (!innerEq) {
538 outerEq = false;
539 }
540 });
541
542 return outerEq;
543 },
544
545 "object": function object(a, b) {
546 var i,
547 aProperties = [],
548 bProperties = [];
549
550 if (compareConstructors(a, b) === false) {
551 return false;
552 }
553
554 // Be strict: don't ensure hasOwnProperty and go deep
555 for (i in a) {
556
557 // Collect a's properties
558 aProperties.push(i);
559
560 // Skip OOP methods that look the same
561 if (a.constructor !== Object && typeof a.constructor !== "undefined" && typeof a[i] === "function" && typeof b[i] === "function" && a[i].toString() === b[i].toString()) {
562 continue;
563 }
564
565 // Compare non-containers; queue non-reference-equal containers
566 if (!breadthFirstCompareChild(a[i], b[i])) {
567 return false;
568 }
569 }
570
571 for (i in b) {
572
573 // Collect b's properties
574 bProperties.push(i);
575 }
576
577 // Ensures identical properties name
578 return typeEquiv(aProperties.sort(), bProperties.sort());
579 }
580 };
581
582 function typeEquiv(a, b) {
583 var type = objectType(a);
584
585 // Callbacks for containers will append to the pairs queue to achieve breadth-first
586 // search order. The pairs queue is also used to avoid reprocessing any pair of
587 // containers that are reference-equal to a previously visited pair (a special case
588 // this being recursion detection).
589 //
590 // Because of this approach, once typeEquiv returns a false value, it should not be
591 // called again without clearing the pair queue else it may wrongly report a visited
592 // pair as being equivalent.
593 return objectType(b) === type && callbacks[type](a, b);
594 }
595
596 function innerEquiv(a, b) {
597 var i, pair;
598
599 // We're done when there's nothing more to compare
600 if (arguments.length < 2) {
601 return true;
602 }
603
604 // Clear the global pair queue and add the top-level values being compared
605 pairs = [{ a: a, b: b }];
606
607 for (i = 0; i < pairs.length; i++) {
608 pair = pairs[i];
609
610 // Perform type-specific comparison on any pairs that are not strictly
611 // equal. For container types, that comparison will postpone comparison
612 // of any sub-container pair to the end of the pair queue. This gives
613 // breadth-first search order. It also avoids the reprocessing of
614 // reference-equal siblings, cousins etc, which can have a significant speed
615 // impact when comparing a container of small objects each of which has a
616 // reference to the same (singleton) large object.
617 if (pair.a !== pair.b && !typeEquiv(pair.a, pair.b)) {
618 return false;
619 }
620 }
621
622 // ...across all consecutive argument pairs
623 return arguments.length === 2 || innerEquiv.apply(this, [].slice.call(arguments, 1));
624 }
625
626 return function () {
627 var result = innerEquiv.apply(undefined, arguments);
628
629 // Release any retained objects
630 pairs.length = 0;
631 return result;
632 };
633 })();
634
635 /**
636 * Config object: Maintain internal state
637 * Later exposed as QUnit.config
638 * `config` initialized at top of scope
639 */
640 var config = {
641
642 // The queue of tests to run
643 queue: [],
644
645 // Block until document ready
646 blocking: true,
647
648 // By default, run previously failed tests first
649 // very useful in combination with "Hide passed tests" checked
650 reorder: true,
651
652 // By default, modify document.title when suite is done
653 altertitle: true,
654
655 // HTML Reporter: collapse every test except the first failing test
656 // If false, all failing tests will be expanded
657 collapse: true,
658
659 // By default, scroll to top of the page when suite is done
660 scrolltop: true,
661
662 // Depth up-to which object will be dumped
663 maxDepth: 5,
664
665 // When enabled, all tests must call expect()
666 requireExpects: false,
667
668 // Placeholder for user-configurable form-exposed URL parameters
669 urlConfig: [],
670
671 // Set of all modules.
672 modules: [],
673
674 // The first unnamed module
675 currentModule: {
676 name: "",
677 tests: [],
678 childModules: [],
679 testsRun: 0,
680 unskippedTestsRun: 0,
681 hooks: {
682 before: [],
683 beforeEach: [],
684 afterEach: [],
685 after: []
686 }
687 },
688
689 callbacks: {},
690
691 // The storage module to use for reordering tests
692 storage: localSessionStorage
693 };
694
695 // take a predefined QUnit.config and extend the defaults
696 var globalConfig = window$1 && window$1.QUnit && window$1.QUnit.config;
697
698 // only extend the global config if there is no QUnit overload
699 if (window$1 && window$1.QUnit && !window$1.QUnit.version) {
700 extend(config, globalConfig);
701 }
702
703 // Push a loose unnamed module to the modules collection
704 config.modules.push(config.currentModule);
705
706 // Based on jsDump by Ariel Flesler
707 // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
708 var dump = (function () {
709 function quote(str) {
710 return "\"" + str.toString().replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"";
711 }
712 function literal(o) {
713 return o + "";
714 }
715 function join(pre, arr, post) {
716 var s = dump.separator(),
717 base = dump.indent(),
718 inner = dump.indent(1);
719 if (arr.join) {
720 arr = arr.join("," + s + inner);
721 }
722 if (!arr) {
723 return pre + post;
724 }
725 return [pre, inner + arr, base + post].join(s);
726 }
727 function array(arr, stack) {
728 var i = arr.length,
729 ret = new Array(i);
730
731 if (dump.maxDepth && dump.depth > dump.maxDepth) {
732 return "[object Array]";
733 }
734
735 this.up();
736 while (i--) {
737 ret[i] = this.parse(arr[i], undefined, stack);
738 }
739 this.down();
740 return join("[", ret, "]");
741 }
742
743 function isArray(obj) {
744 return (
745
746 //Native Arrays
747 toString.call(obj) === "[object Array]" ||
748
749 // NodeList objects
750 typeof obj.length === "number" && obj.item !== undefined && (obj.length ? obj.item(0) === obj[0] : obj.item(0) === null && obj[0] === undefined)
751 );
752 }
753
754 var reName = /^function (\w+)/,
755 dump = {
756
757 // The objType is used mostly internally, you can fix a (custom) type in advance
758 parse: function parse(obj, objType, stack) {
759 stack = stack || [];
760 var res,
761 parser,
762 parserType,
763 objIndex = stack.indexOf(obj);
764
765 if (objIndex !== -1) {
766 return "recursion(" + (objIndex - stack.length) + ")";
767 }
768
769 objType = objType || this.typeOf(obj);
770 parser = this.parsers[objType];
771 parserType = typeof parser === "undefined" ? "undefined" : _typeof(parser);
772
773 if (parserType === "function") {
774 stack.push(obj);
775 res = parser.call(this, obj, stack);
776 stack.pop();
777 return res;
778 }
779 return parserType === "string" ? parser : this.parsers.error;
780 },
781 typeOf: function typeOf(obj) {
782 var type;
783
784 if (obj === null) {
785 type = "null";
786 } else if (typeof obj === "undefined") {
787 type = "undefined";
788 } else if (is("regexp", obj)) {
789 type = "regexp";
790 } else if (is("date", obj)) {
791 type = "date";
792 } else if (is("function", obj)) {
793 type = "function";
794 } else if (obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined) {
795 type = "window";
796 } else if (obj.nodeType === 9) {
797 type = "document";
798 } else if (obj.nodeType) {
799 type = "node";
800 } else if (isArray(obj)) {
801 type = "array";
802 } else if (obj.constructor === Error.prototype.constructor) {
803 type = "error";
804 } else {
805 type = typeof obj === "undefined" ? "undefined" : _typeof(obj);
806 }
807 return type;
808 },
809
810 separator: function separator() {
811 if (this.multiline) {
812 return this.HTML ? "<br />" : "\n";
813 } else {
814 return this.HTML ? "&#160;" : " ";
815 }
816 },
817
818 // Extra can be a number, shortcut for increasing-calling-decreasing
819 indent: function indent(extra) {
820 if (!this.multiline) {
821 return "";
822 }
823 var chr = this.indentChar;
824 if (this.HTML) {
825 chr = chr.replace(/\t/g, " ").replace(/ /g, "&#160;");
826 }
827 return new Array(this.depth + (extra || 0)).join(chr);
828 },
829 up: function up(a) {
830 this.depth += a || 1;
831 },
832 down: function down(a) {
833 this.depth -= a || 1;
834 },
835 setParser: function setParser(name, parser) {
836 this.parsers[name] = parser;
837 },
838
839 // The next 3 are exposed so you can use them
840 quote: quote,
841 literal: literal,
842 join: join,
843 depth: 1,
844 maxDepth: config.maxDepth,
845
846 // This is the list of parsers, to modify them, use dump.setParser
847 parsers: {
848 window: "[Window]",
849 document: "[Document]",
850 error: function error(_error) {
851 return "Error(\"" + _error.message + "\")";
852 },
853 unknown: "[Unknown]",
854 "null": "null",
855 "undefined": "undefined",
856 "function": function _function(fn) {
857 var ret = "function",
858
859
860 // Functions never have name in IE
861 name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
862
863 if (name) {
864 ret += " " + name;
865 }
866 ret += "(";
867
868 ret = [ret, dump.parse(fn, "functionArgs"), "){"].join("");
869 return join(ret, dump.parse(fn, "functionCode"), "}");
870 },
871 array: array,
872 nodelist: array,
873 "arguments": array,
874 object: function object(map, stack) {
875 var keys,
876 key,
877 val,
878 i,
879 nonEnumerableProperties,
880 ret = [];
881
882 if (dump.maxDepth && dump.depth > dump.maxDepth) {
883 return "[object Object]";
884 }
885
886 dump.up();
887 keys = [];
888 for (key in map) {
889 keys.push(key);
890 }
891
892 // Some properties are not always enumerable on Error objects.
893 nonEnumerableProperties = ["message", "name"];
894 for (i in nonEnumerableProperties) {
895 key = nonEnumerableProperties[i];
896 if (key in map && !inArray(key, keys)) {
897 keys.push(key);
898 }
899 }
900 keys.sort();
901 for (i = 0; i < keys.length; i++) {
902 key = keys[i];
903 val = map[key];
904 ret.push(dump.parse(key, "key") + ": " + dump.parse(val, undefined, stack));
905 }
906 dump.down();
907 return join("{", ret, "}");
908 },
909 node: function node(_node) {
910 var len,
911 i,
912 val,
913 open = dump.HTML ? "&lt;" : "<",
914 close = dump.HTML ? "&gt;" : ">",
915 tag = _node.nodeName.toLowerCase(),
916 ret = open + tag,
917 attrs = _node.attributes;
918
919 if (attrs) {
920 for (i = 0, len = attrs.length; i < len; i++) {
921 val = attrs[i].nodeValue;
922
923 // IE6 includes all attributes in .attributes, even ones not explicitly
924 // set. Those have values like undefined, null, 0, false, "" or
925 // "inherit".
926 if (val && val !== "inherit") {
927 ret += " " + attrs[i].nodeName + "=" + dump.parse(val, "attribute");
928 }
929 }
930 }
931 ret += close;
932
933 // Show content of TextNode or CDATASection
934 if (_node.nodeType === 3 || _node.nodeType === 4) {
935 ret += _node.nodeValue;
936 }
937
938 return ret + open + "/" + tag + close;
939 },
940
941 // Function calls it internally, it's the arguments part of the function
942 functionArgs: function functionArgs(fn) {
943 var args,
944 l = fn.length;
945
946 if (!l) {
947 return "";
948 }
949
950 args = new Array(l);
951 while (l--) {
952
953 // 97 is 'a'
954 args[l] = String.fromCharCode(97 + l);
955 }
956 return " " + args.join(", ") + " ";
957 },
958
959 // Object calls it internally, the key part of an item in a map
960 key: quote,
961
962 // Function calls it internally, it's the content of the function
963 functionCode: "[code]",
964
965 // Node calls it internally, it's a html attribute value
966 attribute: quote,
967 string: quote,
968 date: quote,
969 regexp: literal,
970 number: literal,
971 "boolean": literal,
972 symbol: function symbol(sym) {
973 return sym.toString();
974 }
975 },
976
977 // If true, entities are escaped ( <, >, \t, space and \n )
978 HTML: false,
979
980 // Indentation unit
981 indentChar: " ",
982
983 // If true, items in a collection, are separated by a \n, else just a space.
984 multiline: true
985 };
986
987 return dump;
988 })();
989
990 var SuiteReport = function () {
991 function SuiteReport(name, parentSuite) {
992 classCallCheck(this, SuiteReport);
993
994 this.name = name;
995 this.fullName = parentSuite ? parentSuite.fullName.concat(name) : [];
996
997 this.tests = [];
998 this.childSuites = [];
999
1000 if (parentSuite) {
1001 parentSuite.pushChildSuite(this);
1002 }
1003 }
1004
1005 createClass(SuiteReport, [{
1006 key: "start",
1007 value: function start(recordTime) {
1008 if (recordTime) {
1009 this._startTime = performanceNow();
1010
1011 if (performance) {
1012 var suiteLevel = this.fullName.length;
1013 performance.mark("qunit_suite_" + suiteLevel + "_start");
1014 }
1015 }
1016
1017 return {
1018 name: this.name,
1019 fullName: this.fullName.slice(),
1020 tests: this.tests.map(function (test) {
1021 return test.start();
1022 }),
1023 childSuites: this.childSuites.map(function (suite) {
1024 return suite.start();
1025 }),
1026 testCounts: {
1027 total: this.getTestCounts().total
1028 }
1029 };
1030 }
1031 }, {
1032 key: "end",
1033 value: function end(recordTime) {
1034 if (recordTime) {
1035 this._endTime = performanceNow();
1036
1037 if (performance) {
1038 var suiteLevel = this.fullName.length;
1039 performance.mark("qunit_suite_" + suiteLevel + "_end");
1040
1041 var suiteName = this.fullName.join(" – ");
1042
1043 measure(suiteLevel === 0 ? "QUnit Test Run" : "QUnit Test Suite: " + suiteName, "qunit_suite_" + suiteLevel + "_start", "qunit_suite_" + suiteLevel + "_end");
1044 }
1045 }
1046
1047 return {
1048 name: this.name,
1049 fullName: this.fullName.slice(),
1050 tests: this.tests.map(function (test) {
1051 return test.end();
1052 }),
1053 childSuites: this.childSuites.map(function (suite) {
1054 return suite.end();
1055 }),
1056 testCounts: this.getTestCounts(),
1057 runtime: this.getRuntime(),
1058 status: this.getStatus()
1059 };
1060 }
1061 }, {
1062 key: "pushChildSuite",
1063 value: function pushChildSuite(suite) {
1064 this.childSuites.push(suite);
1065 }
1066 }, {
1067 key: "pushTest",
1068 value: function pushTest(test) {
1069 this.tests.push(test);
1070 }
1071 }, {
1072 key: "getRuntime",
1073 value: function getRuntime() {
1074 return this._endTime - this._startTime;
1075 }
1076 }, {
1077 key: "getTestCounts",
1078 value: function getTestCounts() {
1079 var counts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { passed: 0, failed: 0, skipped: 0, todo: 0, total: 0 };
1080
1081 counts = this.tests.reduce(function (counts, test) {
1082 if (test.valid) {
1083 counts[test.getStatus()]++;
1084 counts.total++;
1085 }
1086
1087 return counts;
1088 }, counts);
1089
1090 return this.childSuites.reduce(function (counts, suite) {
1091 return suite.getTestCounts(counts);
1092 }, counts);
1093 }
1094 }, {
1095 key: "getStatus",
1096 value: function getStatus() {
1097 var _getTestCounts = this.getTestCounts(),
1098 total = _getTestCounts.total,
1099 failed = _getTestCounts.failed,
1100 skipped = _getTestCounts.skipped,
1101 todo = _getTestCounts.todo;
1102
1103 if (failed) {
1104 return "failed";
1105 } else {
1106 if (skipped === total) {
1107 return "skipped";
1108 } else if (todo === total) {
1109 return "todo";
1110 } else {
1111 return "passed";
1112 }
1113 }
1114 }
1115 }]);
1116 return SuiteReport;
1117 }();
1118
1119 var focused = false;
1120
1121 var moduleStack = [];
1122
1123 function createModule(name, testEnvironment, modifiers) {
1124 var parentModule = moduleStack.length ? moduleStack.slice(-1)[0] : null;
1125 var moduleName = parentModule !== null ? [parentModule.name, name].join(" > ") : name;
1126 var parentSuite = parentModule ? parentModule.suiteReport : globalSuite;
1127
1128 var skip = parentModule !== null && parentModule.skip || modifiers.skip;
1129 var todo = parentModule !== null && parentModule.todo || modifiers.todo;
1130
1131 var module = {
1132 name: moduleName,
1133 parentModule: parentModule,
1134 tests: [],
1135 moduleId: generateHash(moduleName),
1136 testsRun: 0,
1137 unskippedTestsRun: 0,
1138 childModules: [],
1139 suiteReport: new SuiteReport(name, parentSuite),
1140
1141 // Pass along `skip` and `todo` properties from parent module, in case
1142 // there is one, to childs. And use own otherwise.
1143 // This property will be used to mark own tests and tests of child suites
1144 // as either `skipped` or `todo`.
1145 skip: skip,
1146 todo: skip ? false : todo
1147 };
1148
1149 var env = {};
1150 if (parentModule) {
1151 parentModule.childModules.push(module);
1152 extend(env, parentModule.testEnvironment);
1153 }
1154 extend(env, testEnvironment);
1155 module.testEnvironment = env;
1156
1157 config.modules.push(module);
1158 return module;
1159 }
1160
1161 function processModule(name, options, executeNow) {
1162 var modifiers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
1163
1164 if (objectType(options) === "function") {
1165 executeNow = options;
1166 options = undefined;
1167 }
1168
1169 var module = createModule(name, options, modifiers);
1170
1171 // Move any hooks to a 'hooks' object
1172 var testEnvironment = module.testEnvironment;
1173 var hooks = module.hooks = {};
1174
1175 setHookFromEnvironment(hooks, testEnvironment, "before");
1176 setHookFromEnvironment(hooks, testEnvironment, "beforeEach");
1177 setHookFromEnvironment(hooks, testEnvironment, "afterEach");
1178 setHookFromEnvironment(hooks, testEnvironment, "after");
1179
1180 var moduleFns = {
1181 before: setHookFunction(module, "before"),
1182 beforeEach: setHookFunction(module, "beforeEach"),
1183 afterEach: setHookFunction(module, "afterEach"),
1184 after: setHookFunction(module, "after")
1185 };
1186
1187 var currentModule = config.currentModule;
1188 if (objectType(executeNow) === "function") {
1189 moduleStack.push(module);
1190 config.currentModule = module;
1191 executeNow.call(module.testEnvironment, moduleFns);
1192 moduleStack.pop();
1193 module = module.parentModule || currentModule;
1194 }
1195
1196 config.currentModule = module;
1197
1198 function setHookFromEnvironment(hooks, environment, name) {
1199 var potentialHook = environment[name];
1200 hooks[name] = typeof potentialHook === "function" ? [potentialHook] : [];
1201 delete environment[name];
1202 }
1203
1204 function setHookFunction(module, hookName) {
1205 return function setHook(callback) {
1206 module.hooks[hookName].push(callback);
1207 };
1208 }
1209 }
1210
1211 function module$1(name, options, executeNow) {
1212 if (focused) {
1213 return;
1214 }
1215
1216 processModule(name, options, executeNow);
1217 }
1218
1219 module$1.only = function () {
1220 if (focused) {
1221 return;
1222 }
1223
1224 config.modules.length = 0;
1225 config.queue.length = 0;
1226
1227 module$1.apply(undefined, arguments);
1228
1229 focused = true;
1230 };
1231
1232 module$1.skip = function (name, options, executeNow) {
1233 if (focused) {
1234 return;
1235 }
1236
1237 processModule(name, options, executeNow, { skip: true });
1238 };
1239
1240 module$1.todo = function (name, options, executeNow) {
1241 if (focused) {
1242 return;
1243 }
1244
1245 processModule(name, options, executeNow, { todo: true });
1246 };
1247
1248 var LISTENERS = Object.create(null);
1249 var SUPPORTED_EVENTS = ["runStart", "suiteStart", "testStart", "assertion", "testEnd", "suiteEnd", "runEnd"];
1250
1251 /**
1252 * Emits an event with the specified data to all currently registered listeners.
1253 * Callbacks will fire in the order in which they are registered (FIFO). This
1254 * function is not exposed publicly; it is used by QUnit internals to emit
1255 * logging events.
1256 *
1257 * @private
1258 * @method emit
1259 * @param {String} eventName
1260 * @param {Object} data
1261 * @return {Void}
1262 */
1263 function emit(eventName, data) {
1264 if (objectType(eventName) !== "string") {
1265 throw new TypeError("eventName must be a string when emitting an event");
1266 }
1267
1268 // Clone the callbacks in case one of them registers a new callback
1269 var originalCallbacks = LISTENERS[eventName];
1270 var callbacks = originalCallbacks ? [].concat(toConsumableArray(originalCallbacks)) : [];
1271
1272 for (var i = 0; i < callbacks.length; i++) {
1273 callbacks[i](data);
1274 }
1275 }
1276
1277 /**
1278 * Registers a callback as a listener to the specified event.
1279 *
1280 * @public
1281 * @method on
1282 * @param {String} eventName
1283 * @param {Function} callback
1284 * @return {Void}
1285 */
1286 function on(eventName, callback) {
1287 if (objectType(eventName) !== "string") {
1288 throw new TypeError("eventName must be a string when registering a listener");
1289 } else if (!inArray(eventName, SUPPORTED_EVENTS)) {
1290 var events = SUPPORTED_EVENTS.join(", ");
1291 throw new Error("\"" + eventName + "\" is not a valid event; must be one of: " + events + ".");
1292 } else if (objectType(callback) !== "function") {
1293 throw new TypeError("callback must be a function when registering a listener");
1294 }
1295
1296 if (!LISTENERS[eventName]) {
1297 LISTENERS[eventName] = [];
1298 }
1299
1300 // Don't register the same callback more than once
1301 if (!inArray(callback, LISTENERS[eventName])) {
1302 LISTENERS[eventName].push(callback);
1303 }
1304 }
1305
1306 function objectOrFunction(x) {
1307 var type = typeof x === 'undefined' ? 'undefined' : _typeof(x);
1308 return x !== null && (type === 'object' || type === 'function');
1309 }
1310
1311 function isFunction(x) {
1312 return typeof x === 'function';
1313 }
1314
1315
1316
1317 var _isArray = void 0;
1318 if (Array.isArray) {
1319 _isArray = Array.isArray;
1320 } else {
1321 _isArray = function _isArray(x) {
1322 return Object.prototype.toString.call(x) === '[object Array]';
1323 };
1324 }
1325
1326 var isArray = _isArray;
1327
1328 var len = 0;
1329 var vertxNext = void 0;
1330 var customSchedulerFn = void 0;
1331
1332 var asap = function asap(callback, arg) {
1333 queue[len] = callback;
1334 queue[len + 1] = arg;
1335 len += 2;
1336 if (len === 2) {
1337 // If len is 2, that means that we need to schedule an async flush.
1338 // If additional callbacks are queued before the queue is flushed, they
1339 // will be processed by this flush that we are scheduling.
1340 if (customSchedulerFn) {
1341 customSchedulerFn(flush);
1342 } else {
1343 scheduleFlush();
1344 }
1345 }
1346 };
1347
1348 function setScheduler(scheduleFn) {
1349 customSchedulerFn = scheduleFn;
1350 }
1351
1352 function setAsap(asapFn) {
1353 asap = asapFn;
1354 }
1355
1356 var browserWindow = typeof window !== 'undefined' ? window : undefined;
1357 var browserGlobal = browserWindow || {};
1358 var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
1359 var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';
1360
1361 // test for web worker but not in IE10
1362 var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined';
1363
1364 // node
1365 function useNextTick() {
1366 // node version 0.10.x displays a deprecation warning when nextTick is used recursively
1367 // see https://github.com/cujojs/when/issues/410 for details
1368 return function () {
1369 return process.nextTick(flush);
1370 };
1371 }
1372
1373 // vertx
1374 function useVertxTimer() {
1375 if (typeof vertxNext !== 'undefined') {
1376 return function () {
1377 vertxNext(flush);
1378 };
1379 }
1380
1381 return useSetTimeout();
1382 }
1383
1384 function useMutationObserver() {
1385 var iterations = 0;
1386 var observer = new BrowserMutationObserver(flush);
1387 var node = document.createTextNode('');
1388 observer.observe(node, { characterData: true });
1389
1390 return function () {
1391 node.data = iterations = ++iterations % 2;
1392 };
1393 }
1394
1395 // web worker
1396 function useMessageChannel() {
1397 var channel = new MessageChannel();
1398 channel.port1.onmessage = flush;
1399 return function () {
1400 return channel.port2.postMessage(0);
1401 };
1402 }
1403
1404 function useSetTimeout() {
1405 // Store setTimeout reference so es6-promise will be unaffected by
1406 // other code modifying setTimeout (like sinon.useFakeTimers())
1407 var globalSetTimeout = setTimeout;
1408 return function () {
1409 return globalSetTimeout(flush, 1);
1410 };
1411 }
1412
1413 var queue = new Array(1000);
1414 function flush() {
1415 for (var i = 0; i < len; i += 2) {
1416 var callback = queue[i];
1417 var arg = queue[i + 1];
1418
1419 callback(arg);
1420
1421 queue[i] = undefined;
1422 queue[i + 1] = undefined;
1423 }
1424
1425 len = 0;
1426 }
1427
1428 function attemptVertx() {
1429 try {
1430 var vertx = Function('return this')().require('vertx');
1431 vertxNext = vertx.runOnLoop || vertx.runOnContext;
1432 return useVertxTimer();
1433 } catch (e) {
1434 return useSetTimeout();
1435 }
1436 }
1437
1438 var scheduleFlush = void 0;
1439 // Decide what async method to use to triggering processing of queued callbacks:
1440 if (isNode) {
1441 scheduleFlush = useNextTick();
1442 } else if (BrowserMutationObserver) {
1443 scheduleFlush = useMutationObserver();
1444 } else if (isWorker) {
1445 scheduleFlush = useMessageChannel();
1446 } else if (browserWindow === undefined && typeof require === 'function') {
1447 scheduleFlush = attemptVertx();
1448 } else {
1449 scheduleFlush = useSetTimeout();
1450 }
1451
1452 function then(onFulfillment, onRejection) {
1453 var parent = this;
1454
1455 var child = new this.constructor(noop);
1456
1457 if (child[PROMISE_ID] === undefined) {
1458 makePromise(child);
1459 }
1460
1461 var _state = parent._state;
1462
1463
1464 if (_state) {
1465 var callback = arguments[_state - 1];
1466 asap(function () {
1467 return invokeCallback(_state, child, callback, parent._result);
1468 });
1469 } else {
1470 subscribe(parent, child, onFulfillment, onRejection);
1471 }
1472
1473 return child;
1474 }
1475
1476 /**
1477 `Promise.resolve` returns a promise that will become resolved with the
1478 passed `value`. It is shorthand for the following:
1479
1480 ```javascript
1481 let promise = new Promise(function(resolve, reject){
1482 resolve(1);
1483 });
1484
1485 promise.then(function(value){
1486 // value === 1
1487 });
1488 ```
1489
1490 Instead of writing the above, your code now simply becomes the following:
1491
1492 ```javascript
1493 let promise = Promise.resolve(1);
1494
1495 promise.then(function(value){
1496 // value === 1
1497 });
1498 ```
1499
1500 @method resolve
1501 @static
1502 @param {Any} value value that the returned promise will be resolved with
1503 Useful for tooling.
1504 @return {Promise} a promise that will become fulfilled with the given
1505 `value`
1506 */
1507 function resolve$1(object) {
1508 /*jshint validthis:true */
1509 var Constructor = this;
1510
1511 if (object && (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' && object.constructor === Constructor) {
1512 return object;
1513 }
1514
1515 var promise = new Constructor(noop);
1516 resolve(promise, object);
1517 return promise;
1518 }
1519
1520 var PROMISE_ID = Math.random().toString(36).substring(2);
1521
1522 function noop() {}
1523
1524 var PENDING = void 0;
1525 var FULFILLED = 1;
1526 var REJECTED = 2;
1527
1528 var TRY_CATCH_ERROR = { error: null };
1529
1530 function selfFulfillment() {
1531 return new TypeError("You cannot resolve a promise with itself");
1532 }
1533
1534 function cannotReturnOwn() {
1535 return new TypeError('A promises callback cannot return that same promise.');
1536 }
1537
1538 function getThen(promise) {
1539 try {
1540 return promise.then;
1541 } catch (error) {
1542 TRY_CATCH_ERROR.error = error;
1543 return TRY_CATCH_ERROR;
1544 }
1545 }
1546
1547 function tryThen(then$$1, value, fulfillmentHandler, rejectionHandler) {
1548 try {
1549 then$$1.call(value, fulfillmentHandler, rejectionHandler);
1550 } catch (e) {
1551 return e;
1552 }
1553 }
1554
1555 function handleForeignThenable(promise, thenable, then$$1) {
1556 asap(function (promise) {
1557 var sealed = false;
1558 var error = tryThen(then$$1, thenable, function (value) {
1559 if (sealed) {
1560 return;
1561 }
1562 sealed = true;
1563 if (thenable !== value) {
1564 resolve(promise, value);
1565 } else {
1566 fulfill(promise, value);
1567 }
1568 }, function (reason) {
1569 if (sealed) {
1570 return;
1571 }
1572 sealed = true;
1573
1574 reject(promise, reason);
1575 }, 'Settle: ' + (promise._label || ' unknown promise'));
1576
1577 if (!sealed && error) {
1578 sealed = true;
1579 reject(promise, error);
1580 }
1581 }, promise);
1582 }
1583
1584 function handleOwnThenable(promise, thenable) {
1585 if (thenable._state === FULFILLED) {
1586 fulfill(promise, thenable._result);
1587 } else if (thenable._state === REJECTED) {
1588 reject(promise, thenable._result);
1589 } else {
1590 subscribe(thenable, undefined, function (value) {
1591 return resolve(promise, value);
1592 }, function (reason) {
1593 return reject(promise, reason);
1594 });
1595 }
1596 }
1597
1598 function handleMaybeThenable(promise, maybeThenable, then$$1) {
1599 if (maybeThenable.constructor === promise.constructor && then$$1 === then && maybeThenable.constructor.resolve === resolve$1) {
1600 handleOwnThenable(promise, maybeThenable);
1601 } else {
1602 if (then$$1 === TRY_CATCH_ERROR) {
1603 reject(promise, TRY_CATCH_ERROR.error);
1604 TRY_CATCH_ERROR.error = null;
1605 } else if (then$$1 === undefined) {
1606 fulfill(promise, maybeThenable);
1607 } else if (isFunction(then$$1)) {
1608 handleForeignThenable(promise, maybeThenable, then$$1);
1609 } else {
1610 fulfill(promise, maybeThenable);
1611 }
1612 }
1613 }
1614
1615 function resolve(promise, value) {
1616 if (promise === value) {
1617 reject(promise, selfFulfillment());
1618 } else if (objectOrFunction(value)) {
1619 handleMaybeThenable(promise, value, getThen(value));
1620 } else {
1621 fulfill(promise, value);
1622 }
1623 }
1624
1625 function publishRejection(promise) {
1626 if (promise._onerror) {
1627 promise._onerror(promise._result);
1628 }
1629
1630 publish(promise);
1631 }
1632
1633 function fulfill(promise, value) {
1634 if (promise._state !== PENDING) {
1635 return;
1636 }
1637
1638 promise._result = value;
1639 promise._state = FULFILLED;
1640
1641 if (promise._subscribers.length !== 0) {
1642 asap(publish, promise);
1643 }
1644 }
1645
1646 function reject(promise, reason) {
1647 if (promise._state !== PENDING) {
1648 return;
1649 }
1650 promise._state = REJECTED;
1651 promise._result = reason;
1652
1653 asap(publishRejection, promise);
1654 }
1655
1656 function subscribe(parent, child, onFulfillment, onRejection) {
1657 var _subscribers = parent._subscribers;
1658 var length = _subscribers.length;
1659
1660
1661 parent._onerror = null;
1662
1663 _subscribers[length] = child;
1664 _subscribers[length + FULFILLED] = onFulfillment;
1665 _subscribers[length + REJECTED] = onRejection;
1666
1667 if (length === 0 && parent._state) {
1668 asap(publish, parent);
1669 }
1670 }
1671
1672 function publish(promise) {
1673 var subscribers = promise._subscribers;
1674 var settled = promise._state;
1675
1676 if (subscribers.length === 0) {
1677 return;
1678 }
1679
1680 var child = void 0,
1681 callback = void 0,
1682 detail = promise._result;
1683
1684 for (var i = 0; i < subscribers.length; i += 3) {
1685 child = subscribers[i];
1686 callback = subscribers[i + settled];
1687
1688 if (child) {
1689 invokeCallback(settled, child, callback, detail);
1690 } else {
1691 callback(detail);
1692 }
1693 }
1694
1695 promise._subscribers.length = 0;
1696 }
1697
1698 function tryCatch(callback, detail) {
1699 try {
1700 return callback(detail);
1701 } catch (e) {
1702 TRY_CATCH_ERROR.error = e;
1703 return TRY_CATCH_ERROR;
1704 }
1705 }
1706
1707 function invokeCallback(settled, promise, callback, detail) {
1708 var hasCallback = isFunction(callback),
1709 value = void 0,
1710 error = void 0,
1711 succeeded = void 0,
1712 failed = void 0;
1713
1714 if (hasCallback) {
1715 value = tryCatch(callback, detail);
1716
1717 if (value === TRY_CATCH_ERROR) {
1718 failed = true;
1719 error = value.error;
1720 value.error = null;
1721 } else {
1722 succeeded = true;
1723 }
1724
1725 if (promise === value) {
1726 reject(promise, cannotReturnOwn());
1727 return;
1728 }
1729 } else {
1730 value = detail;
1731 succeeded = true;
1732 }
1733
1734 if (promise._state !== PENDING) {
1735 // noop
1736 } else if (hasCallback && succeeded) {
1737 resolve(promise, value);
1738 } else if (failed) {
1739 reject(promise, error);
1740 } else if (settled === FULFILLED) {
1741 fulfill(promise, value);
1742 } else if (settled === REJECTED) {
1743 reject(promise, value);
1744 }
1745 }
1746
1747 function initializePromise(promise, resolver) {
1748 try {
1749 resolver(function resolvePromise(value) {
1750 resolve(promise, value);
1751 }, function rejectPromise(reason) {
1752 reject(promise, reason);
1753 });
1754 } catch (e) {
1755 reject(promise, e);
1756 }
1757 }
1758
1759 var id = 0;
1760 function nextId() {
1761 return id++;
1762 }
1763
1764 function makePromise(promise) {
1765 promise[PROMISE_ID] = id++;
1766 promise._state = undefined;
1767 promise._result = undefined;
1768 promise._subscribers = [];
1769 }
1770
1771 function validationError() {
1772 return new Error('Array Methods must be provided an Array');
1773 }
1774
1775 var Enumerator = function () {
1776 function Enumerator(Constructor, input) {
1777 classCallCheck(this, Enumerator);
1778
1779 this._instanceConstructor = Constructor;
1780 this.promise = new Constructor(noop);
1781
1782 if (!this.promise[PROMISE_ID]) {
1783 makePromise(this.promise);
1784 }
1785
1786 if (isArray(input)) {
1787 this.length = input.length;
1788 this._remaining = input.length;
1789
1790 this._result = new Array(this.length);
1791
1792 if (this.length === 0) {
1793 fulfill(this.promise, this._result);
1794 } else {
1795 this.length = this.length || 0;
1796 this._enumerate(input);
1797 if (this._remaining === 0) {
1798 fulfill(this.promise, this._result);
1799 }
1800 }
1801 } else {
1802 reject(this.promise, validationError());
1803 }
1804 }
1805
1806 createClass(Enumerator, [{
1807 key: '_enumerate',
1808 value: function _enumerate(input) {
1809 for (var i = 0; this._state === PENDING && i < input.length; i++) {
1810 this._eachEntry(input[i], i);
1811 }
1812 }
1813 }, {
1814 key: '_eachEntry',
1815 value: function _eachEntry(entry, i) {
1816 var c = this._instanceConstructor;
1817 var resolve$$1 = c.resolve;
1818
1819
1820 if (resolve$$1 === resolve$1) {
1821 var _then = getThen(entry);
1822
1823 if (_then === then && entry._state !== PENDING) {
1824 this._settledAt(entry._state, i, entry._result);
1825 } else if (typeof _then !== 'function') {
1826 this._remaining--;
1827 this._result[i] = entry;
1828 } else if (c === Promise$2) {
1829 var promise = new c(noop);
1830 handleMaybeThenable(promise, entry, _then);
1831 this._willSettleAt(promise, i);
1832 } else {
1833 this._willSettleAt(new c(function (resolve$$1) {
1834 return resolve$$1(entry);
1835 }), i);
1836 }
1837 } else {
1838 this._willSettleAt(resolve$$1(entry), i);
1839 }
1840 }
1841 }, {
1842 key: '_settledAt',
1843 value: function _settledAt(state, i, value) {
1844 var promise = this.promise;
1845
1846
1847 if (promise._state === PENDING) {
1848 this._remaining--;
1849
1850 if (state === REJECTED) {
1851 reject(promise, value);
1852 } else {
1853 this._result[i] = value;
1854 }
1855 }
1856
1857 if (this._remaining === 0) {
1858 fulfill(promise, this._result);
1859 }
1860 }
1861 }, {
1862 key: '_willSettleAt',
1863 value: function _willSettleAt(promise, i) {
1864 var enumerator = this;
1865
1866 subscribe(promise, undefined, function (value) {
1867 return enumerator._settledAt(FULFILLED, i, value);
1868 }, function (reason) {
1869 return enumerator._settledAt(REJECTED, i, reason);
1870 });
1871 }
1872 }]);
1873 return Enumerator;
1874 }();
1875
1876 /**
1877 `Promise.all` accepts an array of promises, and returns a new promise which
1878 is fulfilled with an array of fulfillment values for the passed promises, or
1879 rejected with the reason of the first passed promise to be rejected. It casts all
1880 elements of the passed iterable to promises as it runs this algorithm.
1881
1882 Example:
1883
1884 ```javascript
1885 let promise1 = resolve(1);
1886 let promise2 = resolve(2);
1887 let promise3 = resolve(3);
1888 let promises = [ promise1, promise2, promise3 ];
1889
1890 Promise.all(promises).then(function(array){
1891 // The array here would be [ 1, 2, 3 ];
1892 });
1893 ```
1894
1895 If any of the `promises` given to `all` are rejected, the first promise
1896 that is rejected will be given as an argument to the returned promises's
1897 rejection handler. For example:
1898
1899 Example:
1900
1901 ```javascript
1902 let promise1 = resolve(1);
1903 let promise2 = reject(new Error("2"));
1904 let promise3 = reject(new Error("3"));
1905 let promises = [ promise1, promise2, promise3 ];
1906
1907 Promise.all(promises).then(function(array){
1908 // Code here never runs because there are rejected promises!
1909 }, function(error) {
1910 // error.message === "2"
1911 });
1912 ```
1913
1914 @method all
1915 @static
1916 @param {Array} entries array of promises
1917 @param {String} label optional string for labeling the promise.
1918 Useful for tooling.
1919 @return {Promise} promise that is fulfilled when all `promises` have been
1920 fulfilled, or rejected if any of them become rejected.
1921 @static
1922 */
1923 function all(entries) {
1924 return new Enumerator(this, entries).promise;
1925 }
1926
1927 /**
1928 `Promise.race` returns a new promise which is settled in the same way as the
1929 first passed promise to settle.
1930
1931 Example:
1932
1933 ```javascript
1934 let promise1 = new Promise(function(resolve, reject){
1935 setTimeout(function(){
1936 resolve('promise 1');
1937 }, 200);
1938 });
1939
1940 let promise2 = new Promise(function(resolve, reject){
1941 setTimeout(function(){
1942 resolve('promise 2');
1943 }, 100);
1944 });
1945
1946 Promise.race([promise1, promise2]).then(function(result){
1947 // result === 'promise 2' because it was resolved before promise1
1948 // was resolved.
1949 });
1950 ```
1951
1952 `Promise.race` is deterministic in that only the state of the first
1953 settled promise matters. For example, even if other promises given to the
1954 `promises` array argument are resolved, but the first settled promise has
1955 become rejected before the other promises became fulfilled, the returned
1956 promise will become rejected:
1957
1958 ```javascript
1959 let promise1 = new Promise(function(resolve, reject){
1960 setTimeout(function(){
1961 resolve('promise 1');
1962 }, 200);
1963 });
1964
1965 let promise2 = new Promise(function(resolve, reject){
1966 setTimeout(function(){
1967 reject(new Error('promise 2'));
1968 }, 100);
1969 });
1970
1971 Promise.race([promise1, promise2]).then(function(result){
1972 // Code here never runs
1973 }, function(reason){
1974 // reason.message === 'promise 2' because promise 2 became rejected before
1975 // promise 1 became fulfilled
1976 });
1977 ```
1978
1979 An example real-world use case is implementing timeouts:
1980
1981 ```javascript
1982 Promise.race([ajax('foo.json'), timeout(5000)])
1983 ```
1984
1985 @method race
1986 @static
1987 @param {Array} promises array of promises to observe
1988 Useful for tooling.
1989 @return {Promise} a promise which settles in the same way as the first passed
1990 promise to settle.
1991 */
1992 function race(entries) {
1993 /*jshint validthis:true */
1994 var Constructor = this;
1995
1996 if (!isArray(entries)) {
1997 return new Constructor(function (_, reject) {
1998 return reject(new TypeError('You must pass an array to race.'));
1999 });
2000 } else {
2001 return new Constructor(function (resolve, reject) {
2002 var length = entries.length;
2003 for (var i = 0; i < length; i++) {
2004 Constructor.resolve(entries[i]).then(resolve, reject);
2005 }
2006 });
2007 }
2008 }
2009
2010 /**
2011 `Promise.reject` returns a promise rejected with the passed `reason`.
2012 It is shorthand for the following:
2013
2014 ```javascript
2015 let promise = new Promise(function(resolve, reject){
2016 reject(new Error('WHOOPS'));
2017 });
2018
2019 promise.then(function(value){
2020 // Code here doesn't run because the promise is rejected!
2021 }, function(reason){
2022 // reason.message === 'WHOOPS'
2023 });
2024 ```
2025
2026 Instead of writing the above, your code now simply becomes the following:
2027
2028 ```javascript
2029 let promise = Promise.reject(new Error('WHOOPS'));
2030
2031 promise.then(function(value){
2032 // Code here doesn't run because the promise is rejected!
2033 }, function(reason){
2034 // reason.message === 'WHOOPS'
2035 });
2036 ```
2037
2038 @method reject
2039 @static
2040 @param {Any} reason value that the returned promise will be rejected with.
2041 Useful for tooling.
2042 @return {Promise} a promise rejected with the given `reason`.
2043 */
2044 function reject$1(reason) {
2045 /*jshint validthis:true */
2046 var Constructor = this;
2047 var promise = new Constructor(noop);
2048 reject(promise, reason);
2049 return promise;
2050 }
2051
2052 function needsResolver() {
2053 throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
2054 }
2055
2056 function needsNew() {
2057 throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
2058 }
2059
2060 /**
2061 Promise objects represent the eventual result of an asynchronous operation. The
2062 primary way of interacting with a promise is through its `then` method, which
2063 registers callbacks to receive either a promise's eventual value or the reason
2064 why the promise cannot be fulfilled.
2065
2066 Terminology
2067 -----------
2068
2069 - `promise` is an object or function with a `then` method whose behavior conforms to this specification.
2070 - `thenable` is an object or function that defines a `then` method.
2071 - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
2072 - `exception` is a value that is thrown using the throw statement.
2073 - `reason` is a value that indicates why a promise was rejected.
2074 - `settled` the final resting state of a promise, fulfilled or rejected.
2075
2076 A promise can be in one of three states: pending, fulfilled, or rejected.
2077
2078 Promises that are fulfilled have a fulfillment value and are in the fulfilled
2079 state. Promises that are rejected have a rejection reason and are in the
2080 rejected state. A fulfillment value is never a thenable.
2081
2082 Promises can also be said to *resolve* a value. If this value is also a
2083 promise, then the original promise's settled state will match the value's
2084 settled state. So a promise that *resolves* a promise that rejects will
2085 itself reject, and a promise that *resolves* a promise that fulfills will
2086 itself fulfill.
2087
2088
2089 Basic Usage:
2090 ------------
2091
2092 ```js
2093 let promise = new Promise(function(resolve, reject) {
2094 // on success
2095 resolve(value);
2096
2097 // on failure
2098 reject(reason);
2099 });
2100
2101 promise.then(function(value) {
2102 // on fulfillment
2103 }, function(reason) {
2104 // on rejection
2105 });
2106 ```
2107
2108 Advanced Usage:
2109 ---------------
2110
2111 Promises shine when abstracting away asynchronous interactions such as
2112 `XMLHttpRequest`s.
2113
2114 ```js
2115 function getJSON(url) {
2116 return new Promise(function(resolve, reject){
2117 let xhr = new XMLHttpRequest();
2118
2119 xhr.open('GET', url);
2120 xhr.onreadystatechange = handler;
2121 xhr.responseType = 'json';
2122 xhr.setRequestHeader('Accept', 'application/json');
2123 xhr.send();
2124
2125 function handler() {
2126 if (this.readyState === this.DONE) {
2127 if (this.status === 200) {
2128 resolve(this.response);
2129 } else {
2130 reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
2131 }
2132 }
2133 };
2134 });
2135 }
2136
2137 getJSON('/posts.json').then(function(json) {
2138 // on fulfillment
2139 }, function(reason) {
2140 // on rejection
2141 });
2142 ```
2143
2144 Unlike callbacks, promises are great composable primitives.
2145
2146 ```js
2147 Promise.all([
2148 getJSON('/posts'),
2149 getJSON('/comments')
2150 ]).then(function(values){
2151 values[0] // => postsJSON
2152 values[1] // => commentsJSON
2153
2154 return values;
2155 });
2156 ```
2157
2158 @class Promise
2159 @param {Function} resolver
2160 Useful for tooling.
2161 @constructor
2162 */
2163
2164 var Promise$2 = function () {
2165 function Promise(resolver) {
2166 classCallCheck(this, Promise);
2167
2168 this[PROMISE_ID] = nextId();
2169 this._result = this._state = undefined;
2170 this._subscribers = [];
2171
2172 if (noop !== resolver) {
2173 typeof resolver !== 'function' && needsResolver();
2174 this instanceof Promise ? initializePromise(this, resolver) : needsNew();
2175 }
2176 }
2177
2178 /**
2179 The primary way of interacting with a promise is through its `then` method,
2180 which registers callbacks to receive either a promise's eventual value or the
2181 reason why the promise cannot be fulfilled.
2182 ```js
2183 findUser().then(function(user){
2184 // user is available
2185 }, function(reason){
2186 // user is unavailable, and you are given the reason why
2187 });
2188 ```
2189 Chaining
2190 --------
2191 The return value of `then` is itself a promise. This second, 'downstream'
2192 promise is resolved with the return value of the first promise's fulfillment
2193 or rejection handler, or rejected if the handler throws an exception.
2194 ```js
2195 findUser().then(function (user) {
2196 return user.name;
2197 }, function (reason) {
2198 return 'default name';
2199 }).then(function (userName) {
2200 // If `findUser` fulfilled, `userName` will be the user's name, otherwise it
2201 // will be `'default name'`
2202 });
2203 findUser().then(function (user) {
2204 throw new Error('Found user, but still unhappy');
2205 }, function (reason) {
2206 throw new Error('`findUser` rejected and we're unhappy');
2207 }).then(function (value) {
2208 // never reached
2209 }, function (reason) {
2210 // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
2211 // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
2212 });
2213 ```
2214 If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
2215 ```js
2216 findUser().then(function (user) {
2217 throw new PedagogicalException('Upstream error');
2218 }).then(function (value) {
2219 // never reached
2220 }).then(function (value) {
2221 // never reached
2222 }, function (reason) {
2223 // The `PedgagocialException` is propagated all the way down to here
2224 });
2225 ```
2226 Assimilation
2227 ------------
2228 Sometimes the value you want to propagate to a downstream promise can only be
2229 retrieved asynchronously. This can be achieved by returning a promise in the
2230 fulfillment or rejection handler. The downstream promise will then be pending
2231 until the returned promise is settled. This is called *assimilation*.
2232 ```js
2233 findUser().then(function (user) {
2234 return findCommentsByAuthor(user);
2235 }).then(function (comments) {
2236 // The user's comments are now available
2237 });
2238 ```
2239 If the assimliated promise rejects, then the downstream promise will also reject.
2240 ```js
2241 findUser().then(function (user) {
2242 return findCommentsByAuthor(user);
2243 }).then(function (comments) {
2244 // If `findCommentsByAuthor` fulfills, we'll have the value here
2245 }, function (reason) {
2246 // If `findCommentsByAuthor` rejects, we'll have the reason here
2247 });
2248 ```
2249 Simple Example
2250 --------------
2251 Synchronous Example
2252 ```javascript
2253 let result;
2254 try {
2255 result = findResult();
2256 // success
2257 } catch(reason) {
2258 // failure
2259 }
2260 ```
2261 Errback Example
2262 ```js
2263 findResult(function(result, err){
2264 if (err) {
2265 // failure
2266 } else {
2267 // success
2268 }
2269 });
2270 ```
2271 Promise Example;
2272 ```javascript
2273 findResult().then(function(result){
2274 // success
2275 }, function(reason){
2276 // failure
2277 });
2278 ```
2279 Advanced Example
2280 --------------
2281 Synchronous Example
2282 ```javascript
2283 let author, books;
2284 try {
2285 author = findAuthor();
2286 books = findBooksByAuthor(author);
2287 // success
2288 } catch(reason) {
2289 // failure
2290 }
2291 ```
2292 Errback Example
2293 ```js
2294 function foundBooks(books) {
2295 }
2296 function failure(reason) {
2297 }
2298 findAuthor(function(author, err){
2299 if (err) {
2300 failure(err);
2301 // failure
2302 } else {
2303 try {
2304 findBoooksByAuthor(author, function(books, err) {
2305 if (err) {
2306 failure(err);
2307 } else {
2308 try {
2309 foundBooks(books);
2310 } catch(reason) {
2311 failure(reason);
2312 }
2313 }
2314 });
2315 } catch(error) {
2316 failure(err);
2317 }
2318 // success
2319 }
2320 });
2321 ```
2322 Promise Example;
2323 ```javascript
2324 findAuthor().
2325 then(findBooksByAuthor).
2326 then(function(books){
2327 // found books
2328 }).catch(function(reason){
2329 // something went wrong
2330 });
2331 ```
2332 @method then
2333 @param {Function} onFulfilled
2334 @param {Function} onRejected
2335 Useful for tooling.
2336 @return {Promise}
2337 */
2338
2339 /**
2340 `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
2341 as the catch block of a try/catch statement.
2342 ```js
2343 function findAuthor(){
2344 throw new Error('couldn't find that author');
2345 }
2346 // synchronous
2347 try {
2348 findAuthor();
2349 } catch(reason) {
2350 // something went wrong
2351 }
2352 // async with promises
2353 findAuthor().catch(function(reason){
2354 // something went wrong
2355 });
2356 ```
2357 @method catch
2358 @param {Function} onRejection
2359 Useful for tooling.
2360 @return {Promise}
2361 */
2362
2363
2364 createClass(Promise, [{
2365 key: 'catch',
2366 value: function _catch(onRejection) {
2367 return this.then(null, onRejection);
2368 }
2369
2370 /**
2371 `finally` will be invoked regardless of the promise's fate just as native
2372 try/catch/finally behaves
2373
2374 Synchronous example:
2375
2376 ```js
2377 findAuthor() {
2378 if (Math.random() > 0.5) {
2379 throw new Error();
2380 }
2381 return new Author();
2382 }
2383
2384 try {
2385 return findAuthor(); // succeed or fail
2386 } catch(error) {
2387 return findOtherAuther();
2388 } finally {
2389 // always runs
2390 // doesn't affect the return value
2391 }
2392 ```
2393
2394 Asynchronous example:
2395
2396 ```js
2397 findAuthor().catch(function(reason){
2398 return findOtherAuther();
2399 }).finally(function(){
2400 // author was either found, or not
2401 });
2402 ```
2403
2404 @method finally
2405 @param {Function} callback
2406 @return {Promise}
2407 */
2408
2409 }, {
2410 key: 'finally',
2411 value: function _finally(callback) {
2412 var promise = this;
2413 var constructor = promise.constructor;
2414
2415 if (isFunction(callback)) {
2416 return promise.then(function (value) {
2417 return constructor.resolve(callback()).then(function () {
2418 return value;
2419 });
2420 }, function (reason) {
2421 return constructor.resolve(callback()).then(function () {
2422 throw reason;
2423 });
2424 });
2425 }
2426
2427 return promise.then(callback, callback);
2428 }
2429 }]);
2430 return Promise;
2431 }();
2432
2433 Promise$2.prototype.then = then;
2434 Promise$2.all = all;
2435 Promise$2.race = race;
2436 Promise$2.resolve = resolve$1;
2437 Promise$2.reject = reject$1;
2438 Promise$2._setScheduler = setScheduler;
2439 Promise$2._setAsap = setAsap;
2440 Promise$2._asap = asap;
2441
2442 /*global self*/
2443 function polyfill() {
2444 var local = void 0;
2445
2446 if (typeof global !== 'undefined') {
2447 local = global;
2448 } else if (typeof self !== 'undefined') {
2449 local = self;
2450 } else {
2451 try {
2452 local = Function('return this')();
2453 } catch (e) {
2454 throw new Error('polyfill failed because global object is unavailable in this environment');
2455 }
2456 }
2457
2458 var P = local.Promise;
2459
2460 if (P) {
2461 var promiseToString = null;
2462 try {
2463 promiseToString = Object.prototype.toString.call(P.resolve());
2464 } catch (e) {
2465 // silently ignored
2466 }
2467
2468 if (promiseToString === '[object Promise]' && !P.cast) {
2469 return;
2470 }
2471 }
2472
2473 local.Promise = Promise$2;
2474 }
2475
2476 // Strange compat..
2477 Promise$2.polyfill = polyfill;
2478 Promise$2.Promise = Promise$2;
2479
2480 var Promise$1 = typeof Promise !== "undefined" ? Promise : Promise$2;
2481
2482 // Register logging callbacks
2483 function registerLoggingCallbacks(obj) {
2484 var i,
2485 l,
2486 key,
2487 callbackNames = ["begin", "done", "log", "testStart", "testDone", "moduleStart", "moduleDone"];
2488
2489 function registerLoggingCallback(key) {
2490 var loggingCallback = function loggingCallback(callback) {
2491 if (objectType(callback) !== "function") {
2492 throw new Error("QUnit logging methods require a callback function as their first parameters.");
2493 }
2494
2495 config.callbacks[key].push(callback);
2496 };
2497
2498 return loggingCallback;
2499 }
2500
2501 for (i = 0, l = callbackNames.length; i < l; i++) {
2502 key = callbackNames[i];
2503
2504 // Initialize key collection of logging callback
2505 if (objectType(config.callbacks[key]) === "undefined") {
2506 config.callbacks[key] = [];
2507 }
2508
2509 obj[key] = registerLoggingCallback(key);
2510 }
2511 }
2512
2513 function runLoggingCallbacks(key, args) {
2514 var callbacks = config.callbacks[key];
2515
2516 // Handling 'log' callbacks separately. Unlike the other callbacks,
2517 // the log callback is not controlled by the processing queue,
2518 // but rather used by asserts. Hence to promisfy the 'log' callback
2519 // would mean promisfying each step of a test
2520 if (key === "log") {
2521 callbacks.map(function (callback) {
2522 return callback(args);
2523 });
2524 return;
2525 }
2526
2527 // ensure that each callback is executed serially
2528 return callbacks.reduce(function (promiseChain, callback) {
2529 return promiseChain.then(function () {
2530 return Promise$1.resolve(callback(args));
2531 });
2532 }, Promise$1.resolve([]));
2533 }
2534
2535 // Doesn't support IE9, it will return undefined on these browsers
2536 // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
2537 var fileName = (sourceFromStacktrace(0) || "").replace(/(:\d+)+\)?/, "").replace(/.+\//, "");
2538
2539 function extractStacktrace(e, offset) {
2540 offset = offset === undefined ? 4 : offset;
2541
2542 var stack, include, i;
2543
2544 if (e && e.stack) {
2545 stack = e.stack.split("\n");
2546 if (/^error$/i.test(stack[0])) {
2547 stack.shift();
2548 }
2549 if (fileName) {
2550 include = [];
2551 for (i = offset; i < stack.length; i++) {
2552 if (stack[i].indexOf(fileName) !== -1) {
2553 break;
2554 }
2555 include.push(stack[i]);
2556 }
2557 if (include.length) {
2558 return include.join("\n");
2559 }
2560 }
2561 return stack[offset];
2562 }
2563 }
2564
2565 function sourceFromStacktrace(offset) {
2566 var error = new Error();
2567
2568 // Support: Safari <=7 only, IE <=10 - 11 only
2569 // Not all browsers generate the `stack` property for `new Error()`, see also #636
2570 if (!error.stack) {
2571 try {
2572 throw error;
2573 } catch (err) {
2574 error = err;
2575 }
2576 }
2577
2578 return extractStacktrace(error, offset);
2579 }
2580
2581 var priorityCount = 0;
2582 var unitSampler = void 0;
2583
2584 // This is a queue of functions that are tasks within a single test.
2585 // After tests are dequeued from config.queue they are expanded into
2586 // a set of tasks in this queue.
2587 var taskQueue = [];
2588
2589 /**
2590 * Advances the taskQueue to the next task. If the taskQueue is empty,
2591 * process the testQueue
2592 */
2593 function advance() {
2594 advanceTaskQueue();
2595
2596 if (!taskQueue.length && !config.blocking && !config.current) {
2597 advanceTestQueue();
2598 }
2599 }
2600
2601 /**
2602 * Advances the taskQueue with an increased depth
2603 */
2604 function advanceTaskQueue() {
2605 var start = now();
2606 config.depth = (config.depth || 0) + 1;
2607
2608 processTaskQueue(start);
2609
2610 config.depth--;
2611 }
2612
2613 /**
2614 * Process the first task on the taskQueue as a promise.
2615 * Each task is a function returned by https://github.com/qunitjs/qunit/blob/master/src/test.js#L381
2616 */
2617 function processTaskQueue(start) {
2618 if (taskQueue.length && !config.blocking) {
2619 var elapsedTime = now() - start;
2620
2621 if (!defined.setTimeout || config.updateRate <= 0 || elapsedTime < config.updateRate) {
2622 var task = taskQueue.shift();
2623 Promise$1.resolve(task()).then(function () {
2624 if (!taskQueue.length) {
2625 advance();
2626 } else {
2627 processTaskQueue(start);
2628 }
2629 });
2630 } else {
2631 setTimeout$1(advance);
2632 }
2633 }
2634 }
2635
2636 /**
2637 * Advance the testQueue to the next test to process. Call done() if testQueue completes.
2638 */
2639 function advanceTestQueue() {
2640 if (!config.blocking && !config.queue.length && config.depth === 0) {
2641 done();
2642 return;
2643 }
2644
2645 var testTasks = config.queue.shift();
2646 addToTaskQueue(testTasks());
2647
2648 if (priorityCount > 0) {
2649 priorityCount--;
2650 }
2651
2652 advance();
2653 }
2654
2655 /**
2656 * Enqueue the tasks for a test into the task queue.
2657 * @param {Array} tasksArray
2658 */
2659 function addToTaskQueue(tasksArray) {
2660 taskQueue.push.apply(taskQueue, toConsumableArray(tasksArray));
2661 }
2662
2663 /**
2664 * Return the number of tasks remaining in the task queue to be processed.
2665 * @return {Number}
2666 */
2667 function taskQueueLength() {
2668 return taskQueue.length;
2669 }
2670
2671 /**
2672 * Adds a test to the TestQueue for execution.
2673 * @param {Function} testTasksFunc
2674 * @param {Boolean} prioritize
2675 * @param {String} seed
2676 */
2677 function addToTestQueue(testTasksFunc, prioritize, seed) {
2678 if (prioritize) {
2679 config.queue.splice(priorityCount++, 0, testTasksFunc);
2680 } else if (seed) {
2681 if (!unitSampler) {
2682 unitSampler = unitSamplerGenerator(seed);
2683 }
2684
2685 // Insert into a random position after all prioritized items
2686 var index = Math.floor(unitSampler() * (config.queue.length - priorityCount + 1));
2687 config.queue.splice(priorityCount + index, 0, testTasksFunc);
2688 } else {
2689 config.queue.push(testTasksFunc);
2690 }
2691 }
2692
2693 /**
2694 * Creates a seeded "sample" generator which is used for randomizing tests.
2695 */
2696 function unitSamplerGenerator(seed) {
2697
2698 // 32-bit xorshift, requires only a nonzero seed
2699 // http://excamera.com/sphinx/article-xorshift.html
2700 var sample = parseInt(generateHash(seed), 16) || -1;
2701 return function () {
2702 sample ^= sample << 13;
2703 sample ^= sample >>> 17;
2704 sample ^= sample << 5;
2705
2706 // ECMAScript has no unsigned number type
2707 if (sample < 0) {
2708 sample += 0x100000000;
2709 }
2710
2711 return sample / 0x100000000;
2712 };
2713 }
2714
2715 /**
2716 * This function is called when the ProcessingQueue is done processing all
2717 * items. It handles emitting the final run events.
2718 */
2719 function done() {
2720 var storage = config.storage;
2721
2722 ProcessingQueue.finished = true;
2723
2724 var runtime = now() - config.started;
2725 var passed = config.stats.all - config.stats.bad;
2726
2727 if (config.stats.all === 0) {
2728
2729 if (config.filter && config.filter.length) {
2730 throw new Error("No tests matched the filter \"" + config.filter + "\".");
2731 }
2732
2733 if (config.module && config.module.length) {
2734 throw new Error("No tests matched the module \"" + config.module + "\".");
2735 }
2736
2737 if (config.moduleId && config.moduleId.length) {
2738 throw new Error("No tests matched the moduleId \"" + config.moduleId + "\".");
2739 }
2740
2741 if (config.testId && config.testId.length) {
2742 throw new Error("No tests matched the testId \"" + config.testId + "\".");
2743 }
2744
2745 throw new Error("No tests were run.");
2746 }
2747
2748 emit("runEnd", globalSuite.end(true));
2749 runLoggingCallbacks("done", {
2750 passed: passed,
2751 failed: config.stats.bad,
2752 total: config.stats.all,
2753 runtime: runtime
2754 }).then(function () {
2755
2756 // Clear own storage items if all tests passed
2757 if (storage && config.stats.bad === 0) {
2758 for (var i = storage.length - 1; i >= 0; i--) {
2759 var key = storage.key(i);
2760
2761 if (key.indexOf("qunit-test-") === 0) {
2762 storage.removeItem(key);
2763 }
2764 }
2765 }
2766 });
2767 }
2768
2769 var ProcessingQueue = {
2770 finished: false,
2771 add: addToTestQueue,
2772 advance: advance,
2773 taskCount: taskQueueLength
2774 };
2775
2776 var TestReport = function () {
2777 function TestReport(name, suite, options) {
2778 classCallCheck(this, TestReport);
2779
2780 this.name = name;
2781 this.suiteName = suite.name;
2782 this.fullName = suite.fullName.concat(name);
2783 this.runtime = 0;
2784 this.assertions = [];
2785
2786 this.skipped = !!options.skip;
2787 this.todo = !!options.todo;
2788
2789 this.valid = options.valid;
2790
2791 this._startTime = 0;
2792 this._endTime = 0;
2793
2794 suite.pushTest(this);
2795 }
2796
2797 createClass(TestReport, [{
2798 key: "start",
2799 value: function start(recordTime) {
2800 if (recordTime) {
2801 this._startTime = performanceNow();
2802 if (performance) {
2803 performance.mark("qunit_test_start");
2804 }
2805 }
2806
2807 return {
2808 name: this.name,
2809 suiteName: this.suiteName,
2810 fullName: this.fullName.slice()
2811 };
2812 }
2813 }, {
2814 key: "end",
2815 value: function end(recordTime) {
2816 if (recordTime) {
2817 this._endTime = performanceNow();
2818 if (performance) {
2819 performance.mark("qunit_test_end");
2820
2821 var testName = this.fullName.join(" – ");
2822
2823 measure("QUnit Test: " + testName, "qunit_test_start", "qunit_test_end");
2824 }
2825 }
2826
2827 return extend(this.start(), {
2828 runtime: this.getRuntime(),
2829 status: this.getStatus(),
2830 errors: this.getFailedAssertions(),
2831 assertions: this.getAssertions()
2832 });
2833 }
2834 }, {
2835 key: "pushAssertion",
2836 value: function pushAssertion(assertion) {
2837 this.assertions.push(assertion);
2838 }
2839 }, {
2840 key: "getRuntime",
2841 value: function getRuntime() {
2842 return this._endTime - this._startTime;
2843 }
2844 }, {
2845 key: "getStatus",
2846 value: function getStatus() {
2847 if (this.skipped) {
2848 return "skipped";
2849 }
2850
2851 var testPassed = this.getFailedAssertions().length > 0 ? this.todo : !this.todo;
2852
2853 if (!testPassed) {
2854 return "failed";
2855 } else if (this.todo) {
2856 return "todo";
2857 } else {
2858 return "passed";
2859 }
2860 }
2861 }, {
2862 key: "getFailedAssertions",
2863 value: function getFailedAssertions() {
2864 return this.assertions.filter(function (assertion) {
2865 return !assertion.passed;
2866 });
2867 }
2868 }, {
2869 key: "getAssertions",
2870 value: function getAssertions() {
2871 return this.assertions.slice();
2872 }
2873
2874 // Remove actual and expected values from assertions. This is to prevent
2875 // leaking memory throughout a test suite.
2876
2877 }, {
2878 key: "slimAssertions",
2879 value: function slimAssertions() {
2880 this.assertions = this.assertions.map(function (assertion) {
2881 delete assertion.actual;
2882 delete assertion.expected;
2883 return assertion;
2884 });
2885 }
2886 }]);
2887 return TestReport;
2888 }();
2889
2890 var focused$1 = false;
2891
2892 function Test(settings) {
2893 var i, l;
2894
2895 ++Test.count;
2896
2897 this.expected = null;
2898 this.assertions = [];
2899 this.semaphore = 0;
2900 this.module = config.currentModule;
2901 this.stack = sourceFromStacktrace(3);
2902 this.steps = [];
2903 this.timeout = undefined;
2904
2905 // If a module is skipped, all its tests and the tests of the child suites
2906 // should be treated as skipped even if they are defined as `only` or `todo`.
2907 // As for `todo` module, all its tests will be treated as `todo` except for
2908 // tests defined as `skip` which will be left intact.
2909 //
2910 // So, if a test is defined as `todo` and is inside a skipped module, we should
2911 // then treat that test as if was defined as `skip`.
2912 if (this.module.skip) {
2913 settings.skip = true;
2914 settings.todo = false;
2915
2916 // Skipped tests should be left intact
2917 } else if (this.module.todo && !settings.skip) {
2918 settings.todo = true;
2919 }
2920
2921 extend(this, settings);
2922
2923 this.testReport = new TestReport(settings.testName, this.module.suiteReport, {
2924 todo: settings.todo,
2925 skip: settings.skip,
2926 valid: this.valid()
2927 });
2928
2929 // Register unique strings
2930 for (i = 0, l = this.module.tests; i < l.length; i++) {
2931 if (this.module.tests[i].name === this.testName) {
2932 this.testName += " ";
2933 }
2934 }
2935
2936 this.testId = generateHash(this.module.name, this.testName);
2937
2938 this.module.tests.push({
2939 name: this.testName,
2940 testId: this.testId,
2941 skip: !!settings.skip
2942 });
2943
2944 if (settings.skip) {
2945
2946 // Skipped tests will fully ignore any sent callback
2947 this.callback = function () {};
2948 this.async = false;
2949 this.expected = 0;
2950 } else {
2951 if (typeof this.callback !== "function") {
2952 var method = this.todo ? "todo" : "test";
2953
2954 // eslint-disable-next-line max-len
2955 throw new TypeError("You must provide a function as a test callback to QUnit." + method + "(\"" + settings.testName + "\")");
2956 }
2957
2958 this.assert = new Assert(this);
2959 }
2960 }
2961
2962 Test.count = 0;
2963
2964 function getNotStartedModules(startModule) {
2965 var module = startModule,
2966 modules = [];
2967
2968 while (module && module.testsRun === 0) {
2969 modules.push(module);
2970 module = module.parentModule;
2971 }
2972
2973 // The above push modules from the child to the parent
2974 // return a reversed order with the top being the top most parent module
2975 return modules.reverse();
2976 }
2977
2978 Test.prototype = {
2979 before: function before() {
2980 var _this = this;
2981
2982 var module = this.module,
2983 notStartedModules = getNotStartedModules(module);
2984
2985 // ensure the callbacks are executed serially for each module
2986 var callbackPromises = notStartedModules.reduce(function (promiseChain, startModule) {
2987 return promiseChain.then(function () {
2988 startModule.stats = { all: 0, bad: 0, started: now() };
2989 emit("suiteStart", startModule.suiteReport.start(true));
2990 return runLoggingCallbacks("moduleStart", {
2991 name: startModule.name,
2992 tests: startModule.tests
2993 });
2994 });
2995 }, Promise$1.resolve([]));
2996
2997 return callbackPromises.then(function () {
2998 config.current = _this;
2999
3000 _this.testEnvironment = extend({}, module.testEnvironment);
3001
3002 _this.started = now();
3003 emit("testStart", _this.testReport.start(true));
3004 return runLoggingCallbacks("testStart", {
3005 name: _this.testName,
3006 module: module.name,
3007 testId: _this.testId,
3008 previousFailure: _this.previousFailure
3009 }).then(function () {
3010 if (!config.pollution) {
3011 saveGlobal();
3012 }
3013 });
3014 });
3015 },
3016
3017 run: function run() {
3018 var promise;
3019
3020 config.current = this;
3021
3022 this.callbackStarted = now();
3023
3024 if (config.notrycatch) {
3025 runTest(this);
3026 return;
3027 }
3028
3029 try {
3030 runTest(this);
3031 } catch (e) {
3032 this.pushFailure("Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + (e.message || e), extractStacktrace(e, 0));
3033
3034 // Else next test will carry the responsibility
3035 saveGlobal();
3036
3037 // Restart the tests if they're blocking
3038 if (config.blocking) {
3039 internalRecover(this);
3040 }
3041 }
3042
3043 function runTest(test) {
3044 promise = test.callback.call(test.testEnvironment, test.assert);
3045 test.resolvePromise(promise);
3046
3047 // If the test has a "lock" on it, but the timeout is 0, then we push a
3048 // failure as the test should be synchronous.
3049 if (test.timeout === 0 && test.semaphore !== 0) {
3050 pushFailure("Test did not finish synchronously even though assert.timeout( 0 ) was used.", sourceFromStacktrace(2));
3051 }
3052 }
3053 },
3054
3055 after: function after() {
3056 checkPollution();
3057 },
3058
3059 queueHook: function queueHook(hook, hookName, hookOwner) {
3060 var _this2 = this;
3061
3062 var callHook = function callHook() {
3063 var promise = hook.call(_this2.testEnvironment, _this2.assert);
3064 _this2.resolvePromise(promise, hookName);
3065 };
3066
3067 var runHook = function runHook() {
3068 if (hookName === "before") {
3069 if (hookOwner.unskippedTestsRun !== 0) {
3070 return;
3071 }
3072
3073 _this2.preserveEnvironment = true;
3074 }
3075
3076 // The 'after' hook should only execute when there are not tests left and
3077 // when the 'after' and 'finish' tasks are the only tasks left to process
3078 if (hookName === "after" && hookOwner.unskippedTestsRun !== numberOfUnskippedTests(hookOwner) - 1 && (config.queue.length > 0 || ProcessingQueue.taskCount() > 2)) {
3079 return;
3080 }
3081
3082 config.current = _this2;
3083 if (config.notrycatch) {
3084 callHook();
3085 return;
3086 }
3087 try {
3088 callHook();
3089 } catch (error) {
3090 _this2.pushFailure(hookName + " failed on " + _this2.testName + ": " + (error.message || error), extractStacktrace(error, 0));
3091 }
3092 };
3093
3094 return runHook;
3095 },
3096
3097
3098 // Currently only used for module level hooks, can be used to add global level ones
3099 hooks: function hooks(handler) {
3100 var hooks = [];
3101
3102 function processHooks(test, module) {
3103 if (module.parentModule) {
3104 processHooks(test, module.parentModule);
3105 }
3106
3107 if (module.hooks[handler].length) {
3108 for (var i = 0; i < module.hooks[handler].length; i++) {
3109 hooks.push(test.queueHook(module.hooks[handler][i], handler, module));
3110 }
3111 }
3112 }
3113
3114 // Hooks are ignored on skipped tests
3115 if (!this.skip) {
3116 processHooks(this, this.module);
3117 }
3118
3119 return hooks;
3120 },
3121
3122
3123 finish: function finish() {
3124 config.current = this;
3125
3126 // Release the test callback to ensure that anything referenced has been
3127 // released to be garbage collected.
3128 this.callback = undefined;
3129
3130 if (this.steps.length) {
3131 var stepsList = this.steps.join(", ");
3132 this.pushFailure("Expected assert.verifySteps() to be called before end of test " + ("after using assert.step(). Unverified steps: " + stepsList), this.stack);
3133 }
3134
3135 if (config.requireExpects && this.expected === null) {
3136 this.pushFailure("Expected number of assertions to be defined, but expect() was " + "not called.", this.stack);
3137 } else if (this.expected !== null && this.expected !== this.assertions.length) {
3138 this.pushFailure("Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack);
3139 } else if (this.expected === null && !this.assertions.length) {
3140 this.pushFailure("Expected at least one assertion, but none were run - call " + "expect(0) to accept zero assertions.", this.stack);
3141 }
3142
3143 var i,
3144 module = this.module,
3145 moduleName = module.name,
3146 testName = this.testName,
3147 skipped = !!this.skip,
3148 todo = !!this.todo,
3149 bad = 0,
3150 storage = config.storage;
3151
3152 this.runtime = now() - this.started;
3153
3154 config.stats.all += this.assertions.length;
3155 module.stats.all += this.assertions.length;
3156
3157 for (i = 0; i < this.assertions.length; i++) {
3158 if (!this.assertions[i].result) {
3159 bad++;
3160 config.stats.bad++;
3161 module.stats.bad++;
3162 }
3163 }
3164
3165 notifyTestsRan(module, skipped);
3166
3167 // Store result when possible
3168 if (storage) {
3169 if (bad) {
3170 storage.setItem("qunit-test-" + moduleName + "-" + testName, bad);
3171 } else {
3172 storage.removeItem("qunit-test-" + moduleName + "-" + testName);
3173 }
3174 }
3175
3176 // After emitting the js-reporters event we cleanup the assertion data to
3177 // avoid leaking it. It is not used by the legacy testDone callbacks.
3178 emit("testEnd", this.testReport.end(true));
3179 this.testReport.slimAssertions();
3180
3181 return runLoggingCallbacks("testDone", {
3182 name: testName,
3183 module: moduleName,
3184 skipped: skipped,
3185 todo: todo,
3186 failed: bad,
3187 passed: this.assertions.length - bad,
3188 total: this.assertions.length,
3189 runtime: skipped ? 0 : this.runtime,
3190
3191 // HTML Reporter use
3192 assertions: this.assertions,
3193 testId: this.testId,
3194
3195 // Source of Test
3196 source: this.stack
3197 }).then(function () {
3198 if (module.testsRun === numberOfTests(module)) {
3199 var completedModules = [module];
3200
3201 // Check if the parent modules, iteratively, are done. If that the case,
3202 // we emit the `suiteEnd` event and trigger `moduleDone` callback.
3203 var parent = module.parentModule;
3204 while (parent && parent.testsRun === numberOfTests(parent)) {
3205 completedModules.push(parent);
3206 parent = parent.parentModule;
3207 }
3208
3209 return completedModules.reduce(function (promiseChain, completedModule) {
3210 return promiseChain.then(function () {
3211 return logSuiteEnd(completedModule);
3212 });
3213 }, Promise$1.resolve([]));
3214 }
3215 }).then(function () {
3216 config.current = undefined;
3217 });
3218
3219 function logSuiteEnd(module) {
3220
3221 // Reset `module.hooks` to ensure that anything referenced in these hooks
3222 // has been released to be garbage collected.
3223 module.hooks = {};
3224
3225 emit("suiteEnd", module.suiteReport.end(true));
3226 return runLoggingCallbacks("moduleDone", {
3227 name: module.name,
3228 tests: module.tests,
3229 failed: module.stats.bad,
3230 passed: module.stats.all - module.stats.bad,
3231 total: module.stats.all,
3232 runtime: now() - module.stats.started
3233 });
3234 }
3235 },
3236
3237 preserveTestEnvironment: function preserveTestEnvironment() {
3238 if (this.preserveEnvironment) {
3239 this.module.testEnvironment = this.testEnvironment;
3240 this.testEnvironment = extend({}, this.module.testEnvironment);
3241 }
3242 },
3243
3244 queue: function queue() {
3245 var test = this;
3246
3247 if (!this.valid()) {
3248 return;
3249 }
3250
3251 function runTest() {
3252 return [function () {
3253 return test.before();
3254 }].concat(toConsumableArray(test.hooks("before")), [function () {
3255 test.preserveTestEnvironment();
3256 }], toConsumableArray(test.hooks("beforeEach")), [function () {
3257 test.run();
3258 }], toConsumableArray(test.hooks("afterEach").reverse()), toConsumableArray(test.hooks("after").reverse()), [function () {
3259 test.after();
3260 }, function () {
3261 return test.finish();
3262 }]);
3263 }
3264
3265 var previousFailCount = config.storage && +config.storage.getItem("qunit-test-" + this.module.name + "-" + this.testName);
3266
3267 // Prioritize previously failed tests, detected from storage
3268 var prioritize = config.reorder && !!previousFailCount;
3269
3270 this.previousFailure = !!previousFailCount;
3271
3272 ProcessingQueue.add(runTest, prioritize, config.seed);
3273
3274 // If the queue has already finished, we manually process the new test
3275 if (ProcessingQueue.finished) {
3276 ProcessingQueue.advance();
3277 }
3278 },
3279
3280
3281 pushResult: function pushResult(resultInfo) {
3282 if (this !== config.current) {
3283 throw new Error("Assertion occurred after test had finished.");
3284 }
3285
3286 // Destructure of resultInfo = { result, actual, expected, message, negative }
3287 var source,
3288 details = {
3289 module: this.module.name,
3290 name: this.testName,
3291 result: resultInfo.result,
3292 message: resultInfo.message,
3293 actual: resultInfo.actual,
3294 testId: this.testId,
3295 negative: resultInfo.negative || false,
3296 runtime: now() - this.started,
3297 todo: !!this.todo
3298 };
3299
3300 if (hasOwn.call(resultInfo, "expected")) {
3301 details.expected = resultInfo.expected;
3302 }
3303
3304 if (!resultInfo.result) {
3305 source = resultInfo.source || sourceFromStacktrace();
3306
3307 if (source) {
3308 details.source = source;
3309 }
3310 }
3311
3312 this.logAssertion(details);
3313
3314 this.assertions.push({
3315 result: !!resultInfo.result,
3316 message: resultInfo.message
3317 });
3318 },
3319
3320 pushFailure: function pushFailure(message, source, actual) {
3321 if (!(this instanceof Test)) {
3322 throw new Error("pushFailure() assertion outside test context, was " + sourceFromStacktrace(2));
3323 }
3324
3325 this.pushResult({
3326 result: false,
3327 message: message || "error",
3328 actual: actual || null,
3329 source: source
3330 });
3331 },
3332
3333 /**
3334 * Log assertion details using both the old QUnit.log interface and
3335 * QUnit.on( "assertion" ) interface.
3336 *
3337 * @private
3338 */
3339 logAssertion: function logAssertion(details) {
3340 runLoggingCallbacks("log", details);
3341
3342 var assertion = {
3343 passed: details.result,
3344 actual: details.actual,
3345 expected: details.expected,
3346 message: details.message,
3347 stack: details.source,
3348 todo: details.todo
3349 };
3350 this.testReport.pushAssertion(assertion);
3351 emit("assertion", assertion);
3352 },
3353
3354
3355 resolvePromise: function resolvePromise(promise, phase) {
3356 var then,
3357 resume,
3358 message,
3359 test = this;
3360 if (promise != null) {
3361 then = promise.then;
3362 if (objectType(then) === "function") {
3363 resume = internalStop(test);
3364 if (config.notrycatch) {
3365 then.call(promise, function () {
3366 resume();
3367 });
3368 } else {
3369 then.call(promise, function () {
3370 resume();
3371 }, function (error) {
3372 message = "Promise rejected " + (!phase ? "during" : phase.replace(/Each$/, "")) + " \"" + test.testName + "\": " + (error && error.message || error);
3373 test.pushFailure(message, extractStacktrace(error, 0));
3374
3375 // Else next test will carry the responsibility
3376 saveGlobal();
3377
3378 // Unblock
3379 internalRecover(test);
3380 });
3381 }
3382 }
3383 }
3384 },
3385
3386 valid: function valid() {
3387 var filter = config.filter,
3388 regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec(filter),
3389 module = config.module && config.module.toLowerCase(),
3390 fullName = this.module.name + ": " + this.testName;
3391
3392 function moduleChainNameMatch(testModule) {
3393 var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;
3394 if (testModuleName === module) {
3395 return true;
3396 } else if (testModule.parentModule) {
3397 return moduleChainNameMatch(testModule.parentModule);
3398 } else {
3399 return false;
3400 }
3401 }
3402
3403 function moduleChainIdMatch(testModule) {
3404 return inArray(testModule.moduleId, config.moduleId) || testModule.parentModule && moduleChainIdMatch(testModule.parentModule);
3405 }
3406
3407 // Internally-generated tests are always valid
3408 if (this.callback && this.callback.validTest) {
3409 return true;
3410 }
3411
3412 if (config.moduleId && config.moduleId.length > 0 && !moduleChainIdMatch(this.module)) {
3413
3414 return false;
3415 }
3416
3417 if (config.testId && config.testId.length > 0 && !inArray(this.testId, config.testId)) {
3418
3419 return false;
3420 }
3421
3422 if (module && !moduleChainNameMatch(this.module)) {
3423 return false;
3424 }
3425
3426 if (!filter) {
3427 return true;
3428 }
3429
3430 return regexFilter ? this.regexFilter(!!regexFilter[1], regexFilter[2], regexFilter[3], fullName) : this.stringFilter(filter, fullName);
3431 },
3432
3433 regexFilter: function regexFilter(exclude, pattern, flags, fullName) {
3434 var regex = new RegExp(pattern, flags);
3435 var match = regex.test(fullName);
3436
3437 return match !== exclude;
3438 },
3439
3440 stringFilter: function stringFilter(filter, fullName) {
3441 filter = filter.toLowerCase();
3442 fullName = fullName.toLowerCase();
3443
3444 var include = filter.charAt(0) !== "!";
3445 if (!include) {
3446 filter = filter.slice(1);
3447 }
3448
3449 // If the filter matches, we need to honour include
3450 if (fullName.indexOf(filter) !== -1) {
3451 return include;
3452 }
3453
3454 // Otherwise, do the opposite
3455 return !include;
3456 }
3457 };
3458
3459 function pushFailure() {
3460 if (!config.current) {
3461 throw new Error("pushFailure() assertion outside test context, in " + sourceFromStacktrace(2));
3462 }
3463
3464 // Gets current test obj
3465 var currentTest = config.current;
3466
3467 return currentTest.pushFailure.apply(currentTest, arguments);
3468 }
3469
3470 function saveGlobal() {
3471 config.pollution = [];
3472
3473 if (config.noglobals) {
3474 for (var key in global$1) {
3475 if (hasOwn.call(global$1, key)) {
3476
3477 // In Opera sometimes DOM element ids show up here, ignore them
3478 if (/^qunit-test-output/.test(key)) {
3479 continue;
3480 }
3481 config.pollution.push(key);
3482 }
3483 }
3484 }
3485 }
3486
3487 function checkPollution() {
3488 var newGlobals,
3489 deletedGlobals,
3490 old = config.pollution;
3491
3492 saveGlobal();
3493
3494 newGlobals = diff(config.pollution, old);
3495 if (newGlobals.length > 0) {
3496 pushFailure("Introduced global variable(s): " + newGlobals.join(", "));
3497 }
3498
3499 deletedGlobals = diff(old, config.pollution);
3500 if (deletedGlobals.length > 0) {
3501 pushFailure("Deleted global variable(s): " + deletedGlobals.join(", "));
3502 }
3503 }
3504
3505 // Will be exposed as QUnit.test
3506 function test(testName, callback) {
3507 if (focused$1) {
3508 return;
3509 }
3510
3511 var newTest = new Test({
3512 testName: testName,
3513 callback: callback
3514 });
3515
3516 newTest.queue();
3517 }
3518
3519 function todo(testName, callback) {
3520 if (focused$1) {
3521 return;
3522 }
3523
3524 var newTest = new Test({
3525 testName: testName,
3526 callback: callback,
3527 todo: true
3528 });
3529
3530 newTest.queue();
3531 }
3532
3533 // Will be exposed as QUnit.skip
3534 function skip(testName) {
3535 if (focused$1) {
3536 return;
3537 }
3538
3539 var test = new Test({
3540 testName: testName,
3541 skip: true
3542 });
3543
3544 test.queue();
3545 }
3546
3547 // Will be exposed as QUnit.only
3548 function only(testName, callback) {
3549 if (focused$1) {
3550 return;
3551 }
3552
3553 config.queue.length = 0;
3554 focused$1 = true;
3555
3556 var newTest = new Test({
3557 testName: testName,
3558 callback: callback
3559 });
3560
3561 newTest.queue();
3562 }
3563
3564 // Put a hold on processing and return a function that will release it.
3565 function internalStop(test) {
3566 test.semaphore += 1;
3567 config.blocking = true;
3568
3569 // Set a recovery timeout, if so configured.
3570 if (defined.setTimeout) {
3571 var timeoutDuration = void 0;
3572
3573 if (typeof test.timeout === "number") {
3574 timeoutDuration = test.timeout;
3575 } else if (typeof config.testTimeout === "number") {
3576 timeoutDuration = config.testTimeout;
3577 }
3578
3579 if (typeof timeoutDuration === "number" && timeoutDuration > 0) {
3580 clearTimeout(config.timeout);
3581 config.timeout = setTimeout$1(function () {
3582 pushFailure("Test took longer than " + timeoutDuration + "ms; test timed out.", sourceFromStacktrace(2));
3583 internalRecover(test);
3584 }, timeoutDuration);
3585 }
3586 }
3587
3588 var released = false;
3589 return function resume() {
3590 if (released) {
3591 return;
3592 }
3593
3594 released = true;
3595 test.semaphore -= 1;
3596 internalStart(test);
3597 };
3598 }
3599
3600 // Forcefully release all processing holds.
3601 function internalRecover(test) {
3602 test.semaphore = 0;
3603 internalStart(test);
3604 }
3605
3606 // Release a processing hold, scheduling a resumption attempt if no holds remain.
3607 function internalStart(test) {
3608
3609 // If semaphore is non-numeric, throw error
3610 if (isNaN(test.semaphore)) {
3611 test.semaphore = 0;
3612
3613 pushFailure("Invalid value on test.semaphore", sourceFromStacktrace(2));
3614 return;
3615 }
3616
3617 // Don't start until equal number of stop-calls
3618 if (test.semaphore > 0) {
3619 return;
3620 }
3621
3622 // Throw an Error if start is called more often than stop
3623 if (test.semaphore < 0) {
3624 test.semaphore = 0;
3625
3626 pushFailure("Tried to restart test while already started (test's semaphore was 0 already)", sourceFromStacktrace(2));
3627 return;
3628 }
3629
3630 // Add a slight delay to allow more assertions etc.
3631 if (defined.setTimeout) {
3632 if (config.timeout) {
3633 clearTimeout(config.timeout);
3634 }
3635 config.timeout = setTimeout$1(function () {
3636 if (test.semaphore > 0) {
3637 return;
3638 }
3639
3640 if (config.timeout) {
3641 clearTimeout(config.timeout);
3642 }
3643
3644 begin();
3645 });
3646 } else {
3647 begin();
3648 }
3649 }
3650
3651 function collectTests(module) {
3652 var tests = [].concat(module.tests);
3653 var modules = [].concat(toConsumableArray(module.childModules));
3654
3655 // Do a breadth-first traversal of the child modules
3656 while (modules.length) {
3657 var nextModule = modules.shift();
3658 tests.push.apply(tests, nextModule.tests);
3659 modules.push.apply(modules, toConsumableArray(nextModule.childModules));
3660 }
3661
3662 return tests;
3663 }
3664
3665 function numberOfTests(module) {
3666 return collectTests(module).length;
3667 }
3668
3669 function numberOfUnskippedTests(module) {
3670 return collectTests(module).filter(function (test) {
3671 return !test.skip;
3672 }).length;
3673 }
3674
3675 function notifyTestsRan(module, skipped) {
3676 module.testsRun++;
3677 if (!skipped) {
3678 module.unskippedTestsRun++;
3679 }
3680 while (module = module.parentModule) {
3681 module.testsRun++;
3682 if (!skipped) {
3683 module.unskippedTestsRun++;
3684 }
3685 }
3686 }
3687
3688 var Assert = function () {
3689 function Assert(testContext) {
3690 classCallCheck(this, Assert);
3691
3692 this.test = testContext;
3693 }
3694
3695 // Assert helpers
3696
3697 createClass(Assert, [{
3698 key: "timeout",
3699 value: function timeout(duration) {
3700 if (typeof duration !== "number") {
3701 throw new Error("You must pass a number as the duration to assert.timeout");
3702 }
3703
3704 this.test.timeout = duration;
3705 }
3706
3707 // Documents a "step", which is a string value, in a test as a passing assertion
3708
3709 }, {
3710 key: "step",
3711 value: function step(message) {
3712 var assertionMessage = message;
3713 var result = !!message;
3714
3715 this.test.steps.push(message);
3716
3717 if (objectType(message) === "undefined" || message === "") {
3718 assertionMessage = "You must provide a message to assert.step";
3719 } else if (objectType(message) !== "string") {
3720 assertionMessage = "You must provide a string value to assert.step";
3721 result = false;
3722 }
3723
3724 this.pushResult({
3725 result: result,
3726 message: assertionMessage
3727 });
3728 }
3729
3730 // Verifies the steps in a test match a given array of string values
3731
3732 }, {
3733 key: "verifySteps",
3734 value: function verifySteps(steps, message) {
3735
3736 // Since the steps array is just string values, we can clone with slice
3737 var actualStepsClone = this.test.steps.slice();
3738 this.deepEqual(actualStepsClone, steps, message);
3739 this.test.steps.length = 0;
3740 }
3741
3742 // Specify the number of expected assertions to guarantee that failed test
3743 // (no assertions are run at all) don't slip through.
3744
3745 }, {
3746 key: "expect",
3747 value: function expect(asserts) {
3748 if (arguments.length === 1) {
3749 this.test.expected = asserts;
3750 } else {
3751 return this.test.expected;
3752 }
3753 }
3754
3755 // Put a hold on processing and return a function that will release it a maximum of once.
3756
3757 }, {
3758 key: "async",
3759 value: function async(count) {
3760 var test$$1 = this.test;
3761
3762 var popped = false,
3763 acceptCallCount = count;
3764
3765 if (typeof acceptCallCount === "undefined") {
3766 acceptCallCount = 1;
3767 }
3768
3769 var resume = internalStop(test$$1);
3770
3771 return function done() {
3772 if (config.current !== test$$1) {
3773 throw Error("assert.async callback called after test finished.");
3774 }
3775
3776 if (popped) {
3777 test$$1.pushFailure("Too many calls to the `assert.async` callback", sourceFromStacktrace(2));
3778 return;
3779 }
3780
3781 acceptCallCount -= 1;
3782 if (acceptCallCount > 0) {
3783 return;
3784 }
3785
3786 popped = true;
3787 resume();
3788 };
3789 }
3790
3791 // Exports test.push() to the user API
3792 // Alias of pushResult.
3793
3794 }, {
3795 key: "push",
3796 value: function push(result, actual, expected, message, negative) {
3797 Logger.warn("assert.push is deprecated and will be removed in QUnit 3.0." + " Please use assert.pushResult instead (https://api.qunitjs.com/assert/pushResult).");
3798
3799 var currentAssert = this instanceof Assert ? this : config.current.assert;
3800 return currentAssert.pushResult({
3801 result: result,
3802 actual: actual,
3803 expected: expected,
3804 message: message,
3805 negative: negative
3806 });
3807 }
3808 }, {
3809 key: "pushResult",
3810 value: function pushResult(resultInfo) {
3811
3812 // Destructure of resultInfo = { result, actual, expected, message, negative }
3813 var assert = this;
3814 var currentTest = assert instanceof Assert && assert.test || config.current;
3815
3816 // Backwards compatibility fix.
3817 // Allows the direct use of global exported assertions and QUnit.assert.*
3818 // Although, it's use is not recommended as it can leak assertions
3819 // to other tests from async tests, because we only get a reference to the current test,
3820 // not exactly the test where assertion were intended to be called.
3821 if (!currentTest) {
3822 throw new Error("assertion outside test context, in " + sourceFromStacktrace(2));
3823 }
3824
3825 if (!(assert instanceof Assert)) {
3826 assert = currentTest.assert;
3827 }
3828
3829 return assert.test.pushResult(resultInfo);
3830 }
3831 }, {
3832 key: "ok",
3833 value: function ok(result, message) {
3834 if (!message) {
3835 message = result ? "okay" : "failed, expected argument to be truthy, was: " + dump.parse(result);
3836 }
3837
3838 this.pushResult({
3839 result: !!result,
3840 actual: result,
3841 expected: true,
3842 message: message
3843 });
3844 }
3845 }, {
3846 key: "notOk",
3847 value: function notOk(result, message) {
3848 if (!message) {
3849 message = !result ? "okay" : "failed, expected argument to be falsy, was: " + dump.parse(result);
3850 }
3851
3852 this.pushResult({
3853 result: !result,
3854 actual: result,
3855 expected: false,
3856 message: message
3857 });
3858 }
3859 }, {
3860 key: "equal",
3861 value: function equal(actual, expected, message) {
3862
3863 // eslint-disable-next-line eqeqeq
3864 var result = expected == actual;
3865
3866 this.pushResult({
3867 result: result,
3868 actual: actual,
3869 expected: expected,
3870 message: message
3871 });
3872 }
3873 }, {
3874 key: "notEqual",
3875 value: function notEqual(actual, expected, message) {
3876
3877 // eslint-disable-next-line eqeqeq
3878 var result = expected != actual;
3879
3880 this.pushResult({
3881 result: result,
3882 actual: actual,
3883 expected: expected,
3884 message: message,
3885 negative: true
3886 });
3887 }
3888 }, {
3889 key: "propEqual",
3890 value: function propEqual(actual, expected, message) {
3891 actual = objectValues(actual);
3892 expected = objectValues(expected);
3893
3894 this.pushResult({
3895 result: equiv(actual, expected),
3896 actual: actual,
3897 expected: expected,
3898 message: message
3899 });
3900 }
3901 }, {
3902 key: "notPropEqual",
3903 value: function notPropEqual(actual, expected, message) {
3904 actual = objectValues(actual);
3905 expected = objectValues(expected);
3906
3907 this.pushResult({
3908 result: !equiv(actual, expected),
3909 actual: actual,
3910 expected: expected,
3911 message: message,
3912 negative: true
3913 });
3914 }
3915 }, {
3916 key: "deepEqual",
3917 value: function deepEqual(actual, expected, message) {
3918 this.pushResult({
3919 result: equiv(actual, expected),
3920 actual: actual,
3921 expected: expected,
3922 message: message
3923 });
3924 }
3925 }, {
3926 key: "notDeepEqual",
3927 value: function notDeepEqual(actual, expected, message) {
3928 this.pushResult({
3929 result: !equiv(actual, expected),
3930 actual: actual,
3931 expected: expected,
3932 message: message,
3933 negative: true
3934 });
3935 }
3936 }, {
3937 key: "strictEqual",
3938 value: function strictEqual(actual, expected, message) {
3939 this.pushResult({
3940 result: expected === actual,
3941 actual: actual,
3942 expected: expected,
3943 message: message
3944 });
3945 }
3946 }, {
3947 key: "notStrictEqual",
3948 value: function notStrictEqual(actual, expected, message) {
3949 this.pushResult({
3950 result: expected !== actual,
3951 actual: actual,
3952 expected: expected,
3953 message: message,
3954 negative: true
3955 });
3956 }
3957 }, {
3958 key: "throws",
3959 value: function throws(block, expected, message) {
3960 var actual = void 0,
3961 result = false;
3962
3963 var currentTest = this instanceof Assert && this.test || config.current;
3964
3965 // 'expected' is optional unless doing string comparison
3966 if (objectType(expected) === "string") {
3967 if (message == null) {
3968 message = expected;
3969 expected = null;
3970 } else {
3971 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.");
3972 }
3973 }
3974
3975 currentTest.ignoreGlobalErrors = true;
3976 try {
3977 block.call(currentTest.testEnvironment);
3978 } catch (e) {
3979 actual = e;
3980 }
3981 currentTest.ignoreGlobalErrors = false;
3982
3983 if (actual) {
3984 var expectedType = objectType(expected);
3985
3986 // We don't want to validate thrown error
3987 if (!expected) {
3988 result = true;
3989
3990 // Expected is a regexp
3991 } else if (expectedType === "regexp") {
3992 result = expected.test(errorString(actual));
3993
3994 // Log the string form of the regexp
3995 expected = String(expected);
3996
3997 // Expected is a constructor, maybe an Error constructor
3998 } else if (expectedType === "function" && actual instanceof expected) {
3999 result = true;
4000
4001 // Expected is an Error object
4002 } else if (expectedType === "object") {
4003 result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
4004
4005 // Log the string form of the Error object
4006 expected = errorString(expected);
4007
4008 // Expected is a validation function which returns true if validation passed
4009 } else if (expectedType === "function" && expected.call({}, actual) === true) {
4010 expected = null;
4011 result = true;
4012 }
4013 }
4014
4015 currentTest.assert.pushResult({
4016 result: result,
4017
4018 // undefined if it didn't throw
4019 actual: actual && errorString(actual),
4020 expected: expected,
4021 message: message
4022 });
4023 }
4024 }, {
4025 key: "rejects",
4026 value: function rejects(promise, expected, message) {
4027 var result = false;
4028
4029 var currentTest = this instanceof Assert && this.test || config.current;
4030
4031 // 'expected' is optional unless doing string comparison
4032 if (objectType(expected) === "string") {
4033 if (message === undefined) {
4034 message = expected;
4035 expected = undefined;
4036 } else {
4037 message = "assert.rejects does not accept a string value for the expected " + "argument.\nUse a non-string object value (e.g. validator function) instead " + "if necessary.";
4038
4039 currentTest.assert.pushResult({
4040 result: false,
4041 message: message
4042 });
4043
4044 return;
4045 }
4046 }
4047
4048 var then = promise && promise.then;
4049 if (objectType(then) !== "function") {
4050 var _message = "The value provided to `assert.rejects` in " + "\"" + currentTest.testName + "\" was not a promise.";
4051
4052 currentTest.assert.pushResult({
4053 result: false,
4054 message: _message,
4055 actual: promise
4056 });
4057
4058 return;
4059 }
4060
4061 var done = this.async();
4062
4063 return then.call(promise, function handleFulfillment() {
4064 var message = "The promise returned by the `assert.rejects` callback in " + "\"" + currentTest.testName + "\" did not reject.";
4065
4066 currentTest.assert.pushResult({
4067 result: false,
4068 message: message,
4069 actual: promise
4070 });
4071
4072 done();
4073 }, function handleRejection(actual) {
4074 var expectedType = objectType(expected);
4075
4076 // We don't want to validate
4077 if (expected === undefined) {
4078 result = true;
4079
4080 // Expected is a regexp
4081 } else if (expectedType === "regexp") {
4082 result = expected.test(errorString(actual));
4083
4084 // Log the string form of the regexp
4085 expected = String(expected);
4086
4087 // Expected is a constructor, maybe an Error constructor
4088 } else if (expectedType === "function" && actual instanceof expected) {
4089 result = true;
4090
4091 // Expected is an Error object
4092 } else if (expectedType === "object") {
4093 result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
4094
4095 // Log the string form of the Error object
4096 expected = errorString(expected);
4097
4098 // Expected is a validation function which returns true if validation passed
4099 } else {
4100 if (expectedType === "function") {
4101 result = expected.call({}, actual) === true;
4102 expected = null;
4103
4104 // Expected is some other invalid type
4105 } else {
4106 result = false;
4107 message = "invalid expected value provided to `assert.rejects` " + "callback in \"" + currentTest.testName + "\": " + expectedType + ".";
4108 }
4109 }
4110
4111 currentTest.assert.pushResult({
4112 result: result,
4113
4114 // leave rejection value of undefined as-is
4115 actual: actual && errorString(actual),
4116 expected: expected,
4117 message: message
4118 });
4119
4120 done();
4121 });
4122 }
4123 }]);
4124 return Assert;
4125 }();
4126
4127 // Provide an alternative to assert.throws(), for environments that consider throws a reserved word
4128 // Known to us are: Closure Compiler, Narwhal
4129 // eslint-disable-next-line dot-notation
4130
4131
4132 Assert.prototype.raises = Assert.prototype["throws"];
4133
4134 /**
4135 * Converts an error into a simple string for comparisons.
4136 *
4137 * @param {Error|Object} error
4138 * @return {String}
4139 */
4140 function errorString(error) {
4141 var resultErrorString = error.toString();
4142
4143 // If the error wasn't a subclass of Error but something like
4144 // an object literal with name and message properties...
4145 if (resultErrorString.substring(0, 7) === "[object") {
4146 var name = error.name ? error.name.toString() : "Error";
4147 var message = error.message ? error.message.toString() : "";
4148
4149 if (name && message) {
4150 return name + ": " + message;
4151 } else if (name) {
4152 return name;
4153 } else if (message) {
4154 return message;
4155 } else {
4156 return "Error";
4157 }
4158 } else {
4159 return resultErrorString;
4160 }
4161 }
4162
4163 /* global module, exports, define */
4164 function exportQUnit(QUnit) {
4165
4166 if (defined.document) {
4167
4168 // QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined.
4169 if (window$1.QUnit && window$1.QUnit.version) {
4170 throw new Error("QUnit has already been defined.");
4171 }
4172
4173 window$1.QUnit = QUnit;
4174 }
4175
4176 // For nodejs
4177 if (typeof module !== "undefined" && module && module.exports) {
4178 module.exports = QUnit;
4179
4180 // For consistency with CommonJS environments' exports
4181 module.exports.QUnit = QUnit;
4182 }
4183
4184 // For CommonJS with exports, but without module.exports, like Rhino
4185 if (typeof exports !== "undefined" && exports) {
4186 exports.QUnit = QUnit;
4187 }
4188
4189 if (typeof define === "function" && define.amd) {
4190 define(function () {
4191 return QUnit;
4192 });
4193 QUnit.config.autostart = false;
4194 }
4195
4196 // For Web/Service Workers
4197 if (self$1 && self$1.WorkerGlobalScope && self$1 instanceof self$1.WorkerGlobalScope) {
4198 self$1.QUnit = QUnit;
4199 }
4200 }
4201
4202 // Handle an unhandled exception. By convention, returns true if further
4203 // error handling should be suppressed and false otherwise.
4204 // In this case, we will only suppress further error handling if the
4205 // "ignoreGlobalErrors" configuration option is enabled.
4206 function onError(error) {
4207 for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
4208 args[_key - 1] = arguments[_key];
4209 }
4210
4211 if (config.current) {
4212 if (config.current.ignoreGlobalErrors) {
4213 return true;
4214 }
4215 pushFailure.apply(undefined, [error.message, error.stacktrace || error.fileName + ":" + error.lineNumber].concat(args));
4216 } else {
4217 test("global failure", extend(function () {
4218 pushFailure.apply(undefined, [error.message, error.stacktrace || error.fileName + ":" + error.lineNumber].concat(args));
4219 }, { validTest: true }));
4220 }
4221
4222 return false;
4223 }
4224
4225 // Handle an unhandled rejection
4226 function onUnhandledRejection(reason) {
4227 var resultInfo = {
4228 result: false,
4229 message: reason.message || "error",
4230 actual: reason,
4231 source: reason.stack || sourceFromStacktrace(3)
4232 };
4233
4234 var currentTest = config.current;
4235 if (currentTest) {
4236 currentTest.assert.pushResult(resultInfo);
4237 } else {
4238 test("global failure", extend(function (assert) {
4239 assert.pushResult(resultInfo);
4240 }, { validTest: true }));
4241 }
4242 }
4243
4244 var QUnit = {};
4245 var globalSuite = new SuiteReport();
4246
4247 // The initial "currentModule" represents the global (or top-level) module that
4248 // is not explicitly defined by the user, therefore we add the "globalSuite" to
4249 // it since each module has a suiteReport associated with it.
4250 config.currentModule.suiteReport = globalSuite;
4251
4252 var globalStartCalled = false;
4253 var runStarted = false;
4254
4255 // Figure out if we're running the tests from a server or not
4256 QUnit.isLocal = !(defined.document && window$1.location.protocol !== "file:");
4257
4258 // Expose the current QUnit version
4259 QUnit.version = "2.9.1";
4260
4261 extend(QUnit, {
4262 on: on,
4263
4264 module: module$1,
4265
4266 test: test,
4267
4268 todo: todo,
4269
4270 skip: skip,
4271
4272 only: only,
4273
4274 start: function start(count) {
4275 var globalStartAlreadyCalled = globalStartCalled;
4276
4277 if (!config.current) {
4278 globalStartCalled = true;
4279
4280 if (runStarted) {
4281 throw new Error("Called start() while test already started running");
4282 } else if (globalStartAlreadyCalled || count > 1) {
4283 throw new Error("Called start() outside of a test context too many times");
4284 } else if (config.autostart) {
4285 throw new Error("Called start() outside of a test context when " + "QUnit.config.autostart was true");
4286 } else if (!config.pageLoaded) {
4287
4288 // The page isn't completely loaded yet, so we set autostart and then
4289 // load if we're in Node or wait for the browser's load event.
4290 config.autostart = true;
4291
4292 // Starts from Node even if .load was not previously called. We still return
4293 // early otherwise we'll wind up "beginning" twice.
4294 if (!defined.document) {
4295 QUnit.load();
4296 }
4297
4298 return;
4299 }
4300 } else {
4301 throw new Error("QUnit.start cannot be called inside a test context.");
4302 }
4303
4304 scheduleBegin();
4305 },
4306
4307 config: config,
4308
4309 is: is,
4310
4311 objectType: objectType,
4312
4313 extend: extend,
4314
4315 load: function load() {
4316 config.pageLoaded = true;
4317
4318 // Initialize the configuration options
4319 extend(config, {
4320 stats: { all: 0, bad: 0 },
4321 started: 0,
4322 updateRate: 1000,
4323 autostart: true,
4324 filter: ""
4325 }, true);
4326
4327 if (!runStarted) {
4328 config.blocking = false;
4329
4330 if (config.autostart) {
4331 scheduleBegin();
4332 }
4333 }
4334 },
4335
4336 stack: function stack(offset) {
4337 offset = (offset || 0) + 2;
4338 return sourceFromStacktrace(offset);
4339 },
4340
4341 onError: onError,
4342
4343 onUnhandledRejection: onUnhandledRejection
4344 });
4345
4346 QUnit.pushFailure = pushFailure;
4347 QUnit.assert = Assert.prototype;
4348 QUnit.equiv = equiv;
4349 QUnit.dump = dump;
4350
4351 registerLoggingCallbacks(QUnit);
4352
4353 function scheduleBegin() {
4354
4355 runStarted = true;
4356
4357 // Add a slight delay to allow definition of more modules and tests.
4358 if (defined.setTimeout) {
4359 setTimeout$1(function () {
4360 begin();
4361 });
4362 } else {
4363 begin();
4364 }
4365 }
4366
4367 function unblockAndAdvanceQueue() {
4368 config.blocking = false;
4369 ProcessingQueue.advance();
4370 }
4371
4372 function begin() {
4373 var i,
4374 l,
4375 modulesLog = [];
4376
4377 // If the test run hasn't officially begun yet
4378 if (!config.started) {
4379
4380 // Record the time of the test run's beginning
4381 config.started = now();
4382
4383 // Delete the loose unnamed module if unused.
4384 if (config.modules[0].name === "" && config.modules[0].tests.length === 0) {
4385 config.modules.shift();
4386 }
4387
4388 // Avoid unnecessary information by not logging modules' test environments
4389 for (i = 0, l = config.modules.length; i < l; i++) {
4390 modulesLog.push({
4391 name: config.modules[i].name,
4392 tests: config.modules[i].tests
4393 });
4394 }
4395
4396 // The test run is officially beginning now
4397 emit("runStart", globalSuite.start(true));
4398 runLoggingCallbacks("begin", {
4399 totalTests: Test.count,
4400 modules: modulesLog
4401 }).then(unblockAndAdvanceQueue);
4402 } else {
4403 unblockAndAdvanceQueue();
4404 }
4405 }
4406
4407 exportQUnit(QUnit);
4408
4409 (function () {
4410
4411 if (typeof window$1 === "undefined" || typeof document$1 === "undefined") {
4412 return;
4413 }
4414
4415 var config = QUnit.config,
4416 hasOwn = Object.prototype.hasOwnProperty;
4417
4418 // Stores fixture HTML for resetting later
4419 function storeFixture() {
4420
4421 // Avoid overwriting user-defined values
4422 if (hasOwn.call(config, "fixture")) {
4423 return;
4424 }
4425
4426 var fixture = document$1.getElementById("qunit-fixture");
4427 if (fixture) {
4428 config.fixture = fixture.cloneNode(true);
4429 }
4430 }
4431
4432 QUnit.begin(storeFixture);
4433
4434 // Resets the fixture DOM element if available.
4435 function resetFixture() {
4436 if (config.fixture == null) {
4437 return;
4438 }
4439
4440 var fixture = document$1.getElementById("qunit-fixture");
4441 var resetFixtureType = _typeof(config.fixture);
4442 if (resetFixtureType === "string") {
4443
4444 // support user defined values for `config.fixture`
4445 var newFixture = document$1.createElement("div");
4446 newFixture.setAttribute("id", "qunit-fixture");
4447 newFixture.innerHTML = config.fixture;
4448 fixture.parentNode.replaceChild(newFixture, fixture);
4449 } else {
4450 var clonedFixture = config.fixture.cloneNode(true);
4451 fixture.parentNode.replaceChild(clonedFixture, fixture);
4452 }
4453 }
4454
4455 QUnit.testStart(resetFixture);
4456 })();
4457
4458 (function () {
4459
4460 // Only interact with URLs via window.location
4461 var location = typeof window$1 !== "undefined" && window$1.location;
4462 if (!location) {
4463 return;
4464 }
4465
4466 var urlParams = getUrlParams();
4467
4468 QUnit.urlParams = urlParams;
4469
4470 // Match module/test by inclusion in an array
4471 QUnit.config.moduleId = [].concat(urlParams.moduleId || []);
4472 QUnit.config.testId = [].concat(urlParams.testId || []);
4473
4474 // Exact case-insensitive match of the module name
4475 QUnit.config.module = urlParams.module;
4476
4477 // Regular expression or case-insenstive substring match against "moduleName: testName"
4478 QUnit.config.filter = urlParams.filter;
4479
4480 // Test order randomization
4481 if (urlParams.seed === true) {
4482
4483 // Generate a random seed if the option is specified without a value
4484 QUnit.config.seed = Math.random().toString(36).slice(2);
4485 } else if (urlParams.seed) {
4486 QUnit.config.seed = urlParams.seed;
4487 }
4488
4489 // Add URL-parameter-mapped config values with UI form rendering data
4490 QUnit.config.urlConfig.push({
4491 id: "hidepassed",
4492 label: "Hide passed tests",
4493 tooltip: "Only show tests and assertions that fail. Stored as query-strings."
4494 }, {
4495 id: "noglobals",
4496 label: "Check for Globals",
4497 tooltip: "Enabling this will test if any test introduces new properties on the " + "global object (`window` in Browsers). Stored as query-strings."
4498 }, {
4499 id: "notrycatch",
4500 label: "No try-catch",
4501 tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + "exceptions in IE reasonable. Stored as query-strings."
4502 });
4503
4504 QUnit.begin(function () {
4505 var i,
4506 option,
4507 urlConfig = QUnit.config.urlConfig;
4508
4509 for (i = 0; i < urlConfig.length; i++) {
4510
4511 // Options can be either strings or objects with nonempty "id" properties
4512 option = QUnit.config.urlConfig[i];
4513 if (typeof option !== "string") {
4514 option = option.id;
4515 }
4516
4517 if (QUnit.config[option] === undefined) {
4518 QUnit.config[option] = urlParams[option];
4519 }
4520 }
4521 });
4522
4523 function getUrlParams() {
4524 var i, param, name, value;
4525 var urlParams = Object.create(null);
4526 var params = location.search.slice(1).split("&");
4527 var length = params.length;
4528
4529 for (i = 0; i < length; i++) {
4530 if (params[i]) {
4531 param = params[i].split("=");
4532 name = decodeQueryParam(param[0]);
4533
4534 // Allow just a key to turn on a flag, e.g., test.html?noglobals
4535 value = param.length === 1 || decodeQueryParam(param.slice(1).join("="));
4536 if (name in urlParams) {
4537 urlParams[name] = [].concat(urlParams[name], value);
4538 } else {
4539 urlParams[name] = value;
4540 }
4541 }
4542 }
4543
4544 return urlParams;
4545 }
4546
4547 function decodeQueryParam(param) {
4548 return decodeURIComponent(param.replace(/\+/g, "%20"));
4549 }
4550 })();
4551
4552 var stats = {
4553 passedTests: 0,
4554 failedTests: 0,
4555 skippedTests: 0,
4556 todoTests: 0
4557 };
4558
4559 // Escape text for attribute or text content.
4560 function escapeText(s) {
4561 if (!s) {
4562 return "";
4563 }
4564 s = s + "";
4565
4566 // Both single quotes and double quotes (for attributes)
4567 return s.replace(/['"<>&]/g, function (s) {
4568 switch (s) {
4569 case "'":
4570 return "&#039;";
4571 case "\"":
4572 return "&quot;";
4573 case "<":
4574 return "&lt;";
4575 case ">":
4576 return "&gt;";
4577 case "&":
4578 return "&amp;";
4579 }
4580 });
4581 }
4582
4583 (function () {
4584
4585 // Don't load the HTML Reporter on non-browser environments
4586 if (typeof window$1 === "undefined" || !window$1.document) {
4587 return;
4588 }
4589
4590 var config = QUnit.config,
4591 hiddenTests = [],
4592 document = window$1.document,
4593 collapseNext = false,
4594 hasOwn = Object.prototype.hasOwnProperty,
4595 unfilteredUrl = setUrl({ filter: undefined, module: undefined,
4596 moduleId: undefined, testId: undefined }),
4597 modulesList = [];
4598
4599 function addEvent(elem, type, fn) {
4600 elem.addEventListener(type, fn, false);
4601 }
4602
4603 function removeEvent(elem, type, fn) {
4604 elem.removeEventListener(type, fn, false);
4605 }
4606
4607 function addEvents(elems, type, fn) {
4608 var i = elems.length;
4609 while (i--) {
4610 addEvent(elems[i], type, fn);
4611 }
4612 }
4613
4614 function hasClass(elem, name) {
4615 return (" " + elem.className + " ").indexOf(" " + name + " ") >= 0;
4616 }
4617
4618 function addClass(elem, name) {
4619 if (!hasClass(elem, name)) {
4620 elem.className += (elem.className ? " " : "") + name;
4621 }
4622 }
4623
4624 function toggleClass(elem, name, force) {
4625 if (force || typeof force === "undefined" && !hasClass(elem, name)) {
4626 addClass(elem, name);
4627 } else {
4628 removeClass(elem, name);
4629 }
4630 }
4631
4632 function removeClass(elem, name) {
4633 var set = " " + elem.className + " ";
4634
4635 // Class name may appear multiple times
4636 while (set.indexOf(" " + name + " ") >= 0) {
4637 set = set.replace(" " + name + " ", " ");
4638 }
4639
4640 // Trim for prettiness
4641 elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
4642 }
4643
4644 function id(name) {
4645 return document.getElementById && document.getElementById(name);
4646 }
4647
4648 function abortTests() {
4649 var abortButton = id("qunit-abort-tests-button");
4650 if (abortButton) {
4651 abortButton.disabled = true;
4652 abortButton.innerHTML = "Aborting...";
4653 }
4654 QUnit.config.queue.length = 0;
4655 return false;
4656 }
4657
4658 function interceptNavigation(ev) {
4659 applyUrlParams();
4660
4661 if (ev && ev.preventDefault) {
4662 ev.preventDefault();
4663 }
4664
4665 return false;
4666 }
4667
4668 function getUrlConfigHtml() {
4669 var i,
4670 j,
4671 val,
4672 escaped,
4673 escapedTooltip,
4674 selection = false,
4675 urlConfig = config.urlConfig,
4676 urlConfigHtml = "";
4677
4678 for (i = 0; i < urlConfig.length; i++) {
4679
4680 // Options can be either strings or objects with nonempty "id" properties
4681 val = config.urlConfig[i];
4682 if (typeof val === "string") {
4683 val = {
4684 id: val,
4685 label: val
4686 };
4687 }
4688
4689 escaped = escapeText(val.id);
4690 escapedTooltip = escapeText(val.tooltip);
4691
4692 if (!val.value || typeof val.value === "string") {
4693 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>";
4694 } else {
4695 urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'>" + val.label + ": </label><select id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
4696
4697 if (QUnit.is("array", val.value)) {
4698 for (j = 0; j < val.value.length; j++) {
4699 escaped = escapeText(val.value[j]);
4700 urlConfigHtml += "<option value='" + escaped + "'" + (config[val.id] === val.value[j] ? (selection = true) && " selected='selected'" : "") + ">" + escaped + "</option>";
4701 }
4702 } else {
4703 for (j in val.value) {
4704 if (hasOwn.call(val.value, j)) {
4705 urlConfigHtml += "<option value='" + escapeText(j) + "'" + (config[val.id] === j ? (selection = true) && " selected='selected'" : "") + ">" + escapeText(val.value[j]) + "</option>";
4706 }
4707 }
4708 }
4709 if (config[val.id] && !selection) {
4710 escaped = escapeText(config[val.id]);
4711 urlConfigHtml += "<option value='" + escaped + "' selected='selected' disabled='disabled'>" + escaped + "</option>";
4712 }
4713 urlConfigHtml += "</select>";
4714 }
4715 }
4716
4717 return urlConfigHtml;
4718 }
4719
4720 // Handle "click" events on toolbar checkboxes and "change" for select menus.
4721 // Updates the URL with the new state of `config.urlConfig` values.
4722 function toolbarChanged() {
4723 var updatedUrl,
4724 value,
4725 tests,
4726 field = this,
4727 params = {};
4728
4729 // Detect if field is a select menu or a checkbox
4730 if ("selectedIndex" in field) {
4731 value = field.options[field.selectedIndex].value || undefined;
4732 } else {
4733 value = field.checked ? field.defaultValue || true : undefined;
4734 }
4735
4736 params[field.name] = value;
4737 updatedUrl = setUrl(params);
4738
4739 // Check if we can apply the change without a page refresh
4740 if ("hidepassed" === field.name && "replaceState" in window$1.history) {
4741 QUnit.urlParams[field.name] = value;
4742 config[field.name] = value || false;
4743 tests = id("qunit-tests");
4744 if (tests) {
4745 var length = tests.children.length;
4746 var children = tests.children;
4747
4748 if (field.checked) {
4749 for (var i = 0; i < length; i++) {
4750 var test = children[i];
4751
4752 if (test && test.className.indexOf("pass") > -1) {
4753 hiddenTests.push(test);
4754 }
4755 }
4756
4757 var _iteratorNormalCompletion = true;
4758 var _didIteratorError = false;
4759 var _iteratorError = undefined;
4760
4761 try {
4762 for (var _iterator = hiddenTests[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
4763 var hiddenTest = _step.value;
4764
4765 tests.removeChild(hiddenTest);
4766 }
4767 } catch (err) {
4768 _didIteratorError = true;
4769 _iteratorError = err;
4770 } finally {
4771 try {
4772 if (!_iteratorNormalCompletion && _iterator.return) {
4773 _iterator.return();
4774 }
4775 } finally {
4776 if (_didIteratorError) {
4777 throw _iteratorError;
4778 }
4779 }
4780 }
4781 } else {
4782 while ((test = hiddenTests.pop()) != null) {
4783 tests.appendChild(test);
4784 }
4785 }
4786 }
4787 window$1.history.replaceState(null, "", updatedUrl);
4788 } else {
4789 window$1.location = updatedUrl;
4790 }
4791 }
4792
4793 function setUrl(params) {
4794 var key,
4795 arrValue,
4796 i,
4797 querystring = "?",
4798 location = window$1.location;
4799
4800 params = QUnit.extend(QUnit.extend({}, QUnit.urlParams), params);
4801
4802 for (key in params) {
4803
4804 // Skip inherited or undefined properties
4805 if (hasOwn.call(params, key) && params[key] !== undefined) {
4806
4807 // Output a parameter for each value of this key
4808 // (but usually just one)
4809 arrValue = [].concat(params[key]);
4810 for (i = 0; i < arrValue.length; i++) {
4811 querystring += encodeURIComponent(key);
4812 if (arrValue[i] !== true) {
4813 querystring += "=" + encodeURIComponent(arrValue[i]);
4814 }
4815 querystring += "&";
4816 }
4817 }
4818 }
4819 return location.protocol + "//" + location.host + location.pathname + querystring.slice(0, -1);
4820 }
4821
4822 function applyUrlParams() {
4823 var i,
4824 selectedModules = [],
4825 modulesList = id("qunit-modulefilter-dropdown-list").getElementsByTagName("input"),
4826 filter = id("qunit-filter-input").value;
4827
4828 for (i = 0; i < modulesList.length; i++) {
4829 if (modulesList[i].checked) {
4830 selectedModules.push(modulesList[i].value);
4831 }
4832 }
4833
4834 window$1.location = setUrl({
4835 filter: filter === "" ? undefined : filter,
4836 moduleId: selectedModules.length === 0 ? undefined : selectedModules,
4837
4838 // Remove module and testId filter
4839 module: undefined,
4840 testId: undefined
4841 });
4842 }
4843
4844 function toolbarUrlConfigContainer() {
4845 var urlConfigContainer = document.createElement("span");
4846
4847 urlConfigContainer.innerHTML = getUrlConfigHtml();
4848 addClass(urlConfigContainer, "qunit-url-config");
4849
4850 addEvents(urlConfigContainer.getElementsByTagName("input"), "change", toolbarChanged);
4851 addEvents(urlConfigContainer.getElementsByTagName("select"), "change", toolbarChanged);
4852
4853 return urlConfigContainer;
4854 }
4855
4856 function abortTestsButton() {
4857 var button = document.createElement("button");
4858 button.id = "qunit-abort-tests-button";
4859 button.innerHTML = "Abort";
4860 addEvent(button, "click", abortTests);
4861 return button;
4862 }
4863
4864 function toolbarLooseFilter() {
4865 var filter = document.createElement("form"),
4866 label = document.createElement("label"),
4867 input = document.createElement("input"),
4868 button = document.createElement("button");
4869
4870 addClass(filter, "qunit-filter");
4871
4872 label.innerHTML = "Filter: ";
4873
4874 input.type = "text";
4875 input.value = config.filter || "";
4876 input.name = "filter";
4877 input.id = "qunit-filter-input";
4878
4879 button.innerHTML = "Go";
4880
4881 label.appendChild(input);
4882
4883 filter.appendChild(label);
4884 filter.appendChild(document.createTextNode(" "));
4885 filter.appendChild(button);
4886 addEvent(filter, "submit", interceptNavigation);
4887
4888 return filter;
4889 }
4890
4891 function moduleListHtml() {
4892 var i,
4893 checked,
4894 html = "";
4895
4896 for (i = 0; i < config.modules.length; i++) {
4897 if (config.modules[i].name !== "") {
4898 checked = config.moduleId.indexOf(config.modules[i].moduleId) > -1;
4899 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>";
4900 }
4901 }
4902
4903 return html;
4904 }
4905
4906 function toolbarModuleFilter() {
4907 var allCheckbox,
4908 commit,
4909 reset,
4910 moduleFilter = document.createElement("form"),
4911 label = document.createElement("label"),
4912 moduleSearch = document.createElement("input"),
4913 dropDown = document.createElement("div"),
4914 actions = document.createElement("span"),
4915 dropDownList = document.createElement("ul"),
4916 dirty = false;
4917
4918 moduleSearch.id = "qunit-modulefilter-search";
4919 moduleSearch.autocomplete = "off";
4920 addEvent(moduleSearch, "input", searchInput);
4921 addEvent(moduleSearch, "input", searchFocus);
4922 addEvent(moduleSearch, "focus", searchFocus);
4923 addEvent(moduleSearch, "click", searchFocus);
4924
4925 label.id = "qunit-modulefilter-search-container";
4926 label.innerHTML = "Module: ";
4927 label.appendChild(moduleSearch);
4928
4929 actions.id = "qunit-modulefilter-actions";
4930 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>";
4931 allCheckbox = actions.lastChild.firstChild;
4932 commit = actions.firstChild;
4933 reset = commit.nextSibling;
4934 addEvent(commit, "click", applyUrlParams);
4935
4936 dropDownList.id = "qunit-modulefilter-dropdown-list";
4937 dropDownList.innerHTML = moduleListHtml();
4938
4939 dropDown.id = "qunit-modulefilter-dropdown";
4940 dropDown.style.display = "none";
4941 dropDown.appendChild(actions);
4942 dropDown.appendChild(dropDownList);
4943 addEvent(dropDown, "change", selectionChange);
4944 selectionChange();
4945
4946 moduleFilter.id = "qunit-modulefilter";
4947 moduleFilter.appendChild(label);
4948 moduleFilter.appendChild(dropDown);
4949 addEvent(moduleFilter, "submit", interceptNavigation);
4950 addEvent(moduleFilter, "reset", function () {
4951
4952 // Let the reset happen, then update styles
4953 window$1.setTimeout(selectionChange);
4954 });
4955
4956 // Enables show/hide for the dropdown
4957 function searchFocus() {
4958 if (dropDown.style.display !== "none") {
4959 return;
4960 }
4961
4962 dropDown.style.display = "block";
4963 addEvent(document, "click", hideHandler);
4964 addEvent(document, "keydown", hideHandler);
4965
4966 // Hide on Escape keydown or outside-container click
4967 function hideHandler(e) {
4968 var inContainer = moduleFilter.contains(e.target);
4969
4970 if (e.keyCode === 27 || !inContainer) {
4971 if (e.keyCode === 27 && inContainer) {
4972 moduleSearch.focus();
4973 }
4974 dropDown.style.display = "none";
4975 removeEvent(document, "click", hideHandler);
4976 removeEvent(document, "keydown", hideHandler);
4977 moduleSearch.value = "";
4978 searchInput();
4979 }
4980 }
4981 }
4982
4983 // Processes module search box input
4984 function searchInput() {
4985 var i,
4986 item,
4987 searchText = moduleSearch.value.toLowerCase(),
4988 listItems = dropDownList.children;
4989
4990 for (i = 0; i < listItems.length; i++) {
4991 item = listItems[i];
4992 if (!searchText || item.textContent.toLowerCase().indexOf(searchText) > -1) {
4993 item.style.display = "";
4994 } else {
4995 item.style.display = "none";
4996 }
4997 }
4998 }
4999
5000 // Processes selection changes
5001 function selectionChange(evt) {
5002 var i,
5003 item,
5004 checkbox = evt && evt.target || allCheckbox,
5005 modulesList = dropDownList.getElementsByTagName("input"),
5006 selectedNames = [];
5007
5008 toggleClass(checkbox.parentNode, "checked", checkbox.checked);
5009
5010 dirty = false;
5011 if (checkbox.checked && checkbox !== allCheckbox) {
5012 allCheckbox.checked = false;
5013 removeClass(allCheckbox.parentNode, "checked");
5014 }
5015 for (i = 0; i < modulesList.length; i++) {
5016 item = modulesList[i];
5017 if (!evt) {
5018 toggleClass(item.parentNode, "checked", item.checked);
5019 } else if (checkbox === allCheckbox && checkbox.checked) {
5020 item.checked = false;
5021 removeClass(item.parentNode, "checked");
5022 }
5023 dirty = dirty || item.checked !== item.defaultChecked;
5024 if (item.checked) {
5025 selectedNames.push(item.parentNode.textContent);
5026 }
5027 }
5028
5029 commit.style.display = reset.style.display = dirty ? "" : "none";
5030 moduleSearch.placeholder = selectedNames.join(", ") || allCheckbox.parentNode.textContent;
5031 moduleSearch.title = "Type to filter list. Current selection:\n" + (selectedNames.join("\n") || allCheckbox.parentNode.textContent);
5032 }
5033
5034 return moduleFilter;
5035 }
5036
5037 function appendToolbar() {
5038 var toolbar = id("qunit-testrunner-toolbar");
5039
5040 if (toolbar) {
5041 toolbar.appendChild(toolbarUrlConfigContainer());
5042 toolbar.appendChild(toolbarModuleFilter());
5043 toolbar.appendChild(toolbarLooseFilter());
5044 toolbar.appendChild(document.createElement("div")).className = "clearfix";
5045 }
5046 }
5047
5048 function appendHeader() {
5049 var header = id("qunit-header");
5050
5051 if (header) {
5052 header.innerHTML = "<a href='" + escapeText(unfilteredUrl) + "'>" + header.innerHTML + "</a> ";
5053 }
5054 }
5055
5056 function appendBanner() {
5057 var banner = id("qunit-banner");
5058
5059 if (banner) {
5060 banner.className = "";
5061 }
5062 }
5063
5064 function appendTestResults() {
5065 var tests = id("qunit-tests"),
5066 result = id("qunit-testresult"),
5067 controls;
5068
5069 if (result) {
5070 result.parentNode.removeChild(result);
5071 }
5072
5073 if (tests) {
5074 tests.innerHTML = "";
5075 result = document.createElement("p");
5076 result.id = "qunit-testresult";
5077 result.className = "result";
5078 tests.parentNode.insertBefore(result, tests);
5079 result.innerHTML = "<div id=\"qunit-testresult-display\">Running...<br />&#160;</div>" + "<div id=\"qunit-testresult-controls\"></div>" + "<div class=\"clearfix\"></div>";
5080 controls = id("qunit-testresult-controls");
5081 }
5082
5083 if (controls) {
5084 controls.appendChild(abortTestsButton());
5085 }
5086 }
5087
5088 function appendFilteredTest() {
5089 var testId = QUnit.config.testId;
5090 if (!testId || testId.length <= 0) {
5091 return "";
5092 }
5093 return "<div id='qunit-filteredTest'>Rerunning selected tests: " + escapeText(testId.join(", ")) + " <a id='qunit-clearFilter' href='" + escapeText(unfilteredUrl) + "'>Run all tests</a></div>";
5094 }
5095
5096 function appendUserAgent() {
5097 var userAgent = id("qunit-userAgent");
5098
5099 if (userAgent) {
5100 userAgent.innerHTML = "";
5101 userAgent.appendChild(document.createTextNode("QUnit " + QUnit.version + "; " + navigator.userAgent));
5102 }
5103 }
5104
5105 function appendInterface() {
5106 var qunit = id("qunit");
5107
5108 if (qunit) {
5109 qunit.innerHTML = "<h1 id='qunit-header'>" + escapeText(document.title) + "</h1>" + "<h2 id='qunit-banner'></h2>" + "<div id='qunit-testrunner-toolbar'></div>" + appendFilteredTest() + "<h2 id='qunit-userAgent'></h2>" + "<ol id='qunit-tests'></ol>";
5110 }
5111
5112 appendHeader();
5113 appendBanner();
5114 appendTestResults();
5115 appendUserAgent();
5116 appendToolbar();
5117 }
5118
5119 function appendTest(name, testId, moduleName) {
5120 var title,
5121 rerunTrigger,
5122 testBlock,
5123 assertList,
5124 tests = id("qunit-tests");
5125
5126 if (!tests) {
5127 return;
5128 }
5129
5130 title = document.createElement("strong");
5131 title.innerHTML = getNameHtml(name, moduleName);
5132
5133 rerunTrigger = document.createElement("a");
5134 rerunTrigger.innerHTML = "Rerun";
5135 rerunTrigger.href = setUrl({ testId: testId });
5136
5137 testBlock = document.createElement("li");
5138 testBlock.appendChild(title);
5139 testBlock.appendChild(rerunTrigger);
5140 testBlock.id = "qunit-test-output-" + testId;
5141
5142 assertList = document.createElement("ol");
5143 assertList.className = "qunit-assert-list";
5144
5145 testBlock.appendChild(assertList);
5146
5147 tests.appendChild(testBlock);
5148 }
5149
5150 // HTML Reporter initialization and load
5151 QUnit.begin(function (details) {
5152 var i, moduleObj;
5153
5154 // Sort modules by name for the picker
5155 for (i = 0; i < details.modules.length; i++) {
5156 moduleObj = details.modules[i];
5157 if (moduleObj.name) {
5158 modulesList.push(moduleObj.name);
5159 }
5160 }
5161 modulesList.sort(function (a, b) {
5162 return a.localeCompare(b);
5163 });
5164
5165 // Initialize QUnit elements
5166 appendInterface();
5167 });
5168
5169 QUnit.done(function (details) {
5170 var banner = id("qunit-banner"),
5171 tests = id("qunit-tests"),
5172 abortButton = id("qunit-abort-tests-button"),
5173 totalTests = stats.passedTests + stats.skippedTests + stats.todoTests + stats.failedTests,
5174 html = [totalTests, " tests completed in ", details.runtime, " milliseconds, with ", stats.failedTests, " failed, ", stats.skippedTests, " skipped, and ", stats.todoTests, " todo.<br />", "<span class='passed'>", details.passed, "</span> assertions of <span class='total'>", details.total, "</span> passed, <span class='failed'>", details.failed, "</span> failed."].join(""),
5175 test,
5176 assertLi,
5177 assertList;
5178
5179 // Update remaing tests to aborted
5180 if (abortButton && abortButton.disabled) {
5181 html = "Tests aborted after " + details.runtime + " milliseconds.";
5182
5183 for (var i = 0; i < tests.children.length; i++) {
5184 test = tests.children[i];
5185 if (test.className === "" || test.className === "running") {
5186 test.className = "aborted";
5187 assertList = test.getElementsByTagName("ol")[0];
5188 assertLi = document.createElement("li");
5189 assertLi.className = "fail";
5190 assertLi.innerHTML = "Test aborted.";
5191 assertList.appendChild(assertLi);
5192 }
5193 }
5194 }
5195
5196 if (banner && (!abortButton || abortButton.disabled === false)) {
5197 banner.className = stats.failedTests ? "qunit-fail" : "qunit-pass";
5198 }
5199
5200 if (abortButton) {
5201 abortButton.parentNode.removeChild(abortButton);
5202 }
5203
5204 if (tests) {
5205 id("qunit-testresult-display").innerHTML = html;
5206 }
5207
5208 if (config.altertitle && document.title) {
5209
5210 // Show ✖ for good, ✔ for bad suite result in title
5211 // use escape sequences in case file gets loaded with non-utf-8
5212 // charset
5213 document.title = [stats.failedTests ? "\u2716" : "\u2714", document.title.replace(/^[\u2714\u2716] /i, "")].join(" ");
5214 }
5215
5216 // Scroll back to top to show results
5217 if (config.scrolltop && window$1.scrollTo) {
5218 window$1.scrollTo(0, 0);
5219 }
5220 });
5221
5222 function getNameHtml(name, module) {
5223 var nameHtml = "";
5224
5225 if (module) {
5226 nameHtml = "<span class='module-name'>" + escapeText(module) + "</span>: ";
5227 }
5228
5229 nameHtml += "<span class='test-name'>" + escapeText(name) + "</span>";
5230
5231 return nameHtml;
5232 }
5233
5234 QUnit.testStart(function (details) {
5235 var running, bad;
5236
5237 appendTest(details.name, details.testId, details.module);
5238
5239 running = id("qunit-testresult-display");
5240
5241 if (running) {
5242 addClass(running, "running");
5243
5244 bad = QUnit.config.reorder && details.previousFailure;
5245
5246 running.innerHTML = [bad ? "Rerunning previously failed test: <br />" : "Running: <br />", getNameHtml(details.name, details.module)].join("");
5247 }
5248 });
5249
5250 function stripHtml(string) {
5251
5252 // Strip tags, html entity and whitespaces
5253 return string.replace(/<\/?[^>]+(>|$)/g, "").replace(/&quot;/g, "").replace(/\s+/g, "");
5254 }
5255
5256 QUnit.log(function (details) {
5257 var assertList,
5258 assertLi,
5259 message,
5260 expected,
5261 actual,
5262 diff,
5263 showDiff = false,
5264 testItem = id("qunit-test-output-" + details.testId);
5265
5266 if (!testItem) {
5267 return;
5268 }
5269
5270 message = escapeText(details.message) || (details.result ? "okay" : "failed");
5271 message = "<span class='test-message'>" + message + "</span>";
5272 message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
5273
5274 // The pushFailure doesn't provide details.expected
5275 // when it calls, it's implicit to also not show expected and diff stuff
5276 // Also, we need to check details.expected existence, as it can exist and be undefined
5277 if (!details.result && hasOwn.call(details, "expected")) {
5278 if (details.negative) {
5279 expected = "NOT " + QUnit.dump.parse(details.expected);
5280 } else {
5281 expected = QUnit.dump.parse(details.expected);
5282 }
5283
5284 actual = QUnit.dump.parse(details.actual);
5285 message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + escapeText(expected) + "</pre></td></tr>";
5286
5287 if (actual !== expected) {
5288
5289 message += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText(actual) + "</pre></td></tr>";
5290
5291 if (typeof details.actual === "number" && typeof details.expected === "number") {
5292 if (!isNaN(details.actual) && !isNaN(details.expected)) {
5293 showDiff = true;
5294 diff = details.actual - details.expected;
5295 diff = (diff > 0 ? "+" : "") + diff;
5296 }
5297 } else if (typeof details.actual !== "boolean" && typeof details.expected !== "boolean") {
5298 diff = QUnit.diff(expected, actual);
5299
5300 // don't show diff if there is zero overlap
5301 showDiff = stripHtml(diff).length !== stripHtml(expected).length + stripHtml(actual).length;
5302 }
5303
5304 if (showDiff) {
5305 message += "<tr class='test-diff'><th>Diff: </th><td><pre>" + diff + "</pre></td></tr>";
5306 }
5307 } else if (expected.indexOf("[object Array]") !== -1 || expected.indexOf("[object Object]") !== -1) {
5308 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>";
5309 } else {
5310 message += "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the expected and actual results have an equivalent" + " serialization</td></tr>";
5311 }
5312
5313 if (details.source) {
5314 message += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>";
5315 }
5316
5317 message += "</table>";
5318
5319 // This occurs when pushFailure is set and we have an extracted stack trace
5320 } else if (!details.result && details.source) {
5321 message += "<table>" + "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>" + "</table>";
5322 }
5323
5324 assertList = testItem.getElementsByTagName("ol")[0];
5325
5326 assertLi = document.createElement("li");
5327 assertLi.className = details.result ? "pass" : "fail";
5328 assertLi.innerHTML = message;
5329 assertList.appendChild(assertLi);
5330 });
5331
5332 QUnit.testDone(function (details) {
5333 var testTitle,
5334 time,
5335 testItem,
5336 assertList,
5337 status,
5338 good,
5339 bad,
5340 testCounts,
5341 skipped,
5342 sourceName,
5343 tests = id("qunit-tests");
5344
5345 if (!tests) {
5346 return;
5347 }
5348
5349 testItem = id("qunit-test-output-" + details.testId);
5350
5351 removeClass(testItem, "running");
5352
5353 if (details.failed > 0) {
5354 status = "failed";
5355 } else if (details.todo) {
5356 status = "todo";
5357 } else {
5358 status = details.skipped ? "skipped" : "passed";
5359 }
5360
5361 assertList = testItem.getElementsByTagName("ol")[0];
5362
5363 good = details.passed;
5364 bad = details.failed;
5365
5366 // This test passed if it has no unexpected failed assertions
5367 var testPassed = details.failed > 0 ? details.todo : !details.todo;
5368
5369 if (testPassed) {
5370
5371 // Collapse the passing tests
5372 addClass(assertList, "qunit-collapsed");
5373 } else if (config.collapse) {
5374 if (!collapseNext) {
5375
5376 // Skip collapsing the first failing test
5377 collapseNext = true;
5378 } else {
5379
5380 // Collapse remaining tests
5381 addClass(assertList, "qunit-collapsed");
5382 }
5383 }
5384
5385 // The testItem.firstChild is the test name
5386 testTitle = testItem.firstChild;
5387
5388 testCounts = bad ? "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " : "";
5389
5390 testTitle.innerHTML += " <b class='counts'>(" + testCounts + details.assertions.length + ")</b>";
5391
5392 if (details.skipped) {
5393 stats.skippedTests++;
5394
5395 testItem.className = "skipped";
5396 skipped = document.createElement("em");
5397 skipped.className = "qunit-skipped-label";
5398 skipped.innerHTML = "skipped";
5399 testItem.insertBefore(skipped, testTitle);
5400 } else {
5401 addEvent(testTitle, "click", function () {
5402 toggleClass(assertList, "qunit-collapsed");
5403 });
5404
5405 testItem.className = testPassed ? "pass" : "fail";
5406
5407 if (details.todo) {
5408 var todoLabel = document.createElement("em");
5409 todoLabel.className = "qunit-todo-label";
5410 todoLabel.innerHTML = "todo";
5411 testItem.className += " todo";
5412 testItem.insertBefore(todoLabel, testTitle);
5413 }
5414
5415 time = document.createElement("span");
5416 time.className = "runtime";
5417 time.innerHTML = details.runtime + " ms";
5418 testItem.insertBefore(time, assertList);
5419
5420 if (!testPassed) {
5421 stats.failedTests++;
5422 } else if (details.todo) {
5423 stats.todoTests++;
5424 } else {
5425 stats.passedTests++;
5426 }
5427 }
5428
5429 // Show the source of the test when showing assertions
5430 if (details.source) {
5431 sourceName = document.createElement("p");
5432 sourceName.innerHTML = "<strong>Source: </strong>" + escapeText(details.source);
5433 addClass(sourceName, "qunit-source");
5434 if (testPassed) {
5435 addClass(sourceName, "qunit-collapsed");
5436 }
5437 addEvent(testTitle, "click", function () {
5438 toggleClass(sourceName, "qunit-collapsed");
5439 });
5440 testItem.appendChild(sourceName);
5441 }
5442
5443 if (config.hidepassed && status === "passed") {
5444
5445 // use removeChild instead of remove because of support
5446 hiddenTests.push(testItem);
5447
5448 tests.removeChild(testItem);
5449 }
5450 });
5451
5452 // Avoid readyState issue with phantomjs
5453 // Ref: #818
5454 var notPhantom = function (p) {
5455 return !(p && p.version && p.version.major > 0);
5456 }(window$1.phantom);
5457
5458 if (notPhantom && document.readyState === "complete") {
5459 QUnit.load();
5460 } else {
5461 addEvent(window$1, "load", QUnit.load);
5462 }
5463
5464 // Wrap window.onerror. We will call the original window.onerror to see if
5465 // the existing handler fully handles the error; if not, we will call the
5466 // QUnit.onError function.
5467 var originalWindowOnError = window$1.onerror;
5468
5469 // Cover uncaught exceptions
5470 // Returning true will suppress the default browser handler,
5471 // returning false will let it run.
5472 window$1.onerror = function (message, fileName, lineNumber, columnNumber, errorObj) {
5473 var ret = false;
5474 if (originalWindowOnError) {
5475 for (var _len = arguments.length, args = Array(_len > 5 ? _len - 5 : 0), _key = 5; _key < _len; _key++) {
5476 args[_key - 5] = arguments[_key];
5477 }
5478
5479 ret = originalWindowOnError.call.apply(originalWindowOnError, [this, message, fileName, lineNumber, columnNumber, errorObj].concat(args));
5480 }
5481
5482 // Treat return value as window.onerror itself does,
5483 // Only do our handling if not suppressed.
5484 if (ret !== true) {
5485 var error = {
5486 message: message,
5487 fileName: fileName,
5488 lineNumber: lineNumber
5489 };
5490
5491 // According to
5492 // https://blog.sentry.io/2016/01/04/client-javascript-reporting-window-onerror,
5493 // most modern browsers support an errorObj argument; use that to
5494 // get a full stack trace if it's available.
5495 if (errorObj && errorObj.stack) {
5496 error.stacktrace = extractStacktrace(errorObj, 0);
5497 }
5498
5499 ret = QUnit.onError(error);
5500 }
5501
5502 return ret;
5503 };
5504
5505 // Listen for unhandled rejections, and call QUnit.onUnhandledRejection
5506 window$1.addEventListener("unhandledrejection", function (event) {
5507 QUnit.onUnhandledRejection(event.reason);
5508 });
5509 })();
5510
5511 /*
5512 * This file is a modified version of google-diff-match-patch's JavaScript implementation
5513 * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
5514 * modifications are licensed as more fully set forth in LICENSE.txt.
5515 *
5516 * The original source of google-diff-match-patch is attributable and licensed as follows:
5517 *
5518 * Copyright 2006 Google Inc.
5519 * https://code.google.com/p/google-diff-match-patch/
5520 *
5521 * Licensed under the Apache License, Version 2.0 (the "License");
5522 * you may not use this file except in compliance with the License.
5523 * You may obtain a copy of the License at
5524 *
5525 * https://www.apache.org/licenses/LICENSE-2.0
5526 *
5527 * Unless required by applicable law or agreed to in writing, software
5528 * distributed under the License is distributed on an "AS IS" BASIS,
5529 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5530 * See the License for the specific language governing permissions and
5531 * limitations under the License.
5532 *
5533 * More Info:
5534 * https://code.google.com/p/google-diff-match-patch/
5535 *
5536 * Usage: QUnit.diff(expected, actual)
5537 *
5538 */
5539 QUnit.diff = function () {
5540 function DiffMatchPatch() {}
5541
5542 // DIFF FUNCTIONS
5543
5544 /**
5545 * The data structure representing a diff is an array of tuples:
5546 * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
5547 * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
5548 */
5549 var DIFF_DELETE = -1,
5550 DIFF_INSERT = 1,
5551 DIFF_EQUAL = 0;
5552
5553 /**
5554 * Find the differences between two texts. Simplifies the problem by stripping
5555 * any common prefix or suffix off the texts before diffing.
5556 * @param {string} text1 Old string to be diffed.
5557 * @param {string} text2 New string to be diffed.
5558 * @param {boolean=} optChecklines Optional speedup flag. If present and false,
5559 * then don't run a line-level diff first to identify the changed areas.
5560 * Defaults to true, which does a faster, slightly less optimal diff.
5561 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
5562 */
5563 DiffMatchPatch.prototype.DiffMain = function (text1, text2, optChecklines) {
5564 var deadline, checklines, commonlength, commonprefix, commonsuffix, diffs;
5565
5566 // The diff must be complete in up to 1 second.
5567 deadline = new Date().getTime() + 1000;
5568
5569 // Check for null inputs.
5570 if (text1 === null || text2 === null) {
5571 throw new Error("Null input. (DiffMain)");
5572 }
5573
5574 // Check for equality (speedup).
5575 if (text1 === text2) {
5576 if (text1) {
5577 return [[DIFF_EQUAL, text1]];
5578 }
5579 return [];
5580 }
5581
5582 if (typeof optChecklines === "undefined") {
5583 optChecklines = true;
5584 }
5585
5586 checklines = optChecklines;
5587
5588 // Trim off common prefix (speedup).
5589 commonlength = this.diffCommonPrefix(text1, text2);
5590 commonprefix = text1.substring(0, commonlength);
5591 text1 = text1.substring(commonlength);
5592 text2 = text2.substring(commonlength);
5593
5594 // Trim off common suffix (speedup).
5595 commonlength = this.diffCommonSuffix(text1, text2);
5596 commonsuffix = text1.substring(text1.length - commonlength);
5597 text1 = text1.substring(0, text1.length - commonlength);
5598 text2 = text2.substring(0, text2.length - commonlength);
5599
5600 // Compute the diff on the middle block.
5601 diffs = this.diffCompute(text1, text2, checklines, deadline);
5602
5603 // Restore the prefix and suffix.
5604 if (commonprefix) {
5605 diffs.unshift([DIFF_EQUAL, commonprefix]);
5606 }
5607 if (commonsuffix) {
5608 diffs.push([DIFF_EQUAL, commonsuffix]);
5609 }
5610 this.diffCleanupMerge(diffs);
5611 return diffs;
5612 };
5613
5614 /**
5615 * Reduce the number of edits by eliminating operationally trivial equalities.
5616 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
5617 */
5618 DiffMatchPatch.prototype.diffCleanupEfficiency = function (diffs) {
5619 var changes, equalities, equalitiesLength, lastequality, pointer, preIns, preDel, postIns, postDel;
5620 changes = false;
5621 equalities = []; // Stack of indices where equalities are found.
5622 equalitiesLength = 0; // Keeping our own length var is faster in JS.
5623 /** @type {?string} */
5624 lastequality = null;
5625
5626 // Always equal to diffs[equalities[equalitiesLength - 1]][1]
5627 pointer = 0; // Index of current position.
5628
5629 // Is there an insertion operation before the last equality.
5630 preIns = false;
5631
5632 // Is there a deletion operation before the last equality.
5633 preDel = false;
5634
5635 // Is there an insertion operation after the last equality.
5636 postIns = false;
5637
5638 // Is there a deletion operation after the last equality.
5639 postDel = false;
5640 while (pointer < diffs.length) {
5641
5642 // Equality found.
5643 if (diffs[pointer][0] === DIFF_EQUAL) {
5644 if (diffs[pointer][1].length < 4 && (postIns || postDel)) {
5645
5646 // Candidate found.
5647 equalities[equalitiesLength++] = pointer;
5648 preIns = postIns;
5649 preDel = postDel;
5650 lastequality = diffs[pointer][1];
5651 } else {
5652
5653 // Not a candidate, and can never become one.
5654 equalitiesLength = 0;
5655 lastequality = null;
5656 }
5657 postIns = postDel = false;
5658
5659 // An insertion or deletion.
5660 } else {
5661
5662 if (diffs[pointer][0] === DIFF_DELETE) {
5663 postDel = true;
5664 } else {
5665 postIns = true;
5666 }
5667
5668 /*
5669 * Five types to be split:
5670 * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
5671 * <ins>A</ins>X<ins>C</ins><del>D</del>
5672 * <ins>A</ins><del>B</del>X<ins>C</ins>
5673 * <ins>A</del>X<ins>C</ins><del>D</del>
5674 * <ins>A</ins><del>B</del>X<del>C</del>
5675 */
5676 if (lastequality && (preIns && preDel && postIns && postDel || lastequality.length < 2 && preIns + preDel + postIns + postDel === 3)) {
5677
5678 // Duplicate record.
5679 diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
5680
5681 // Change second copy to insert.
5682 diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
5683 equalitiesLength--; // Throw away the equality we just deleted;
5684 lastequality = null;
5685 if (preIns && preDel) {
5686
5687 // No changes made which could affect previous entry, keep going.
5688 postIns = postDel = true;
5689 equalitiesLength = 0;
5690 } else {
5691 equalitiesLength--; // Throw away the previous equality.
5692 pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
5693 postIns = postDel = false;
5694 }
5695 changes = true;
5696 }
5697 }
5698 pointer++;
5699 }
5700
5701 if (changes) {
5702 this.diffCleanupMerge(diffs);
5703 }
5704 };
5705
5706 /**
5707 * Convert a diff array into a pretty HTML report.
5708 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
5709 * @param {integer} string to be beautified.
5710 * @return {string} HTML representation.
5711 */
5712 DiffMatchPatch.prototype.diffPrettyHtml = function (diffs) {
5713 var op,
5714 data,
5715 x,
5716 html = [];
5717 for (x = 0; x < diffs.length; x++) {
5718 op = diffs[x][0]; // Operation (insert, delete, equal)
5719 data = diffs[x][1]; // Text of change.
5720 switch (op) {
5721 case DIFF_INSERT:
5722 html[x] = "<ins>" + escapeText(data) + "</ins>";
5723 break;
5724 case DIFF_DELETE:
5725 html[x] = "<del>" + escapeText(data) + "</del>";
5726 break;
5727 case DIFF_EQUAL:
5728 html[x] = "<span>" + escapeText(data) + "</span>";
5729 break;
5730 }
5731 }
5732 return html.join("");
5733 };
5734
5735 /**
5736 * Determine the common prefix of two strings.
5737 * @param {string} text1 First string.
5738 * @param {string} text2 Second string.
5739 * @return {number} The number of characters common to the start of each
5740 * string.
5741 */
5742 DiffMatchPatch.prototype.diffCommonPrefix = function (text1, text2) {
5743 var pointermid, pointermax, pointermin, pointerstart;
5744
5745 // Quick check for common null cases.
5746 if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0)) {
5747 return 0;
5748 }
5749
5750 // Binary search.
5751 // Performance analysis: https://neil.fraser.name/news/2007/10/09/
5752 pointermin = 0;
5753 pointermax = Math.min(text1.length, text2.length);
5754 pointermid = pointermax;
5755 pointerstart = 0;
5756 while (pointermin < pointermid) {
5757 if (text1.substring(pointerstart, pointermid) === text2.substring(pointerstart, pointermid)) {
5758 pointermin = pointermid;
5759 pointerstart = pointermin;
5760 } else {
5761 pointermax = pointermid;
5762 }
5763 pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
5764 }
5765 return pointermid;
5766 };
5767
5768 /**
5769 * Determine the common suffix of two strings.
5770 * @param {string} text1 First string.
5771 * @param {string} text2 Second string.
5772 * @return {number} The number of characters common to the end of each string.
5773 */
5774 DiffMatchPatch.prototype.diffCommonSuffix = function (text1, text2) {
5775 var pointermid, pointermax, pointermin, pointerend;
5776
5777 // Quick check for common null cases.
5778 if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) {
5779 return 0;
5780 }
5781
5782 // Binary search.
5783 // Performance analysis: https://neil.fraser.name/news/2007/10/09/
5784 pointermin = 0;
5785 pointermax = Math.min(text1.length, text2.length);
5786 pointermid = pointermax;
5787 pointerend = 0;
5788 while (pointermin < pointermid) {
5789 if (text1.substring(text1.length - pointermid, text1.length - pointerend) === text2.substring(text2.length - pointermid, text2.length - pointerend)) {
5790 pointermin = pointermid;
5791 pointerend = pointermin;
5792 } else {
5793 pointermax = pointermid;
5794 }
5795 pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
5796 }
5797 return pointermid;
5798 };
5799
5800 /**
5801 * Find the differences between two texts. Assumes that the texts do not
5802 * have any common prefix or suffix.
5803 * @param {string} text1 Old string to be diffed.
5804 * @param {string} text2 New string to be diffed.
5805 * @param {boolean} checklines Speedup flag. If false, then don't run a
5806 * line-level diff first to identify the changed areas.
5807 * If true, then run a faster, slightly less optimal diff.
5808 * @param {number} deadline Time when the diff should be complete by.
5809 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
5810 * @private
5811 */
5812 DiffMatchPatch.prototype.diffCompute = function (text1, text2, checklines, deadline) {
5813 var diffs, longtext, shorttext, i, hm, text1A, text2A, text1B, text2B, midCommon, diffsA, diffsB;
5814
5815 if (!text1) {
5816
5817 // Just add some text (speedup).
5818 return [[DIFF_INSERT, text2]];
5819 }
5820
5821 if (!text2) {
5822
5823 // Just delete some text (speedup).
5824 return [[DIFF_DELETE, text1]];
5825 }
5826
5827 longtext = text1.length > text2.length ? text1 : text2;
5828 shorttext = text1.length > text2.length ? text2 : text1;
5829 i = longtext.indexOf(shorttext);
5830 if (i !== -1) {
5831
5832 // Shorter text is inside the longer text (speedup).
5833 diffs = [[DIFF_INSERT, longtext.substring(0, i)], [DIFF_EQUAL, shorttext], [DIFF_INSERT, longtext.substring(i + shorttext.length)]];
5834
5835 // Swap insertions for deletions if diff is reversed.
5836 if (text1.length > text2.length) {
5837 diffs[0][0] = diffs[2][0] = DIFF_DELETE;
5838 }
5839 return diffs;
5840 }
5841
5842 if (shorttext.length === 1) {
5843
5844 // Single character string.
5845 // After the previous speedup, the character can't be an equality.
5846 return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
5847 }
5848
5849 // Check to see if the problem can be split in two.
5850 hm = this.diffHalfMatch(text1, text2);
5851 if (hm) {
5852
5853 // A half-match was found, sort out the return data.
5854 text1A = hm[0];
5855 text1B = hm[1];
5856 text2A = hm[2];
5857 text2B = hm[3];
5858 midCommon = hm[4];
5859
5860 // Send both pairs off for separate processing.
5861 diffsA = this.DiffMain(text1A, text2A, checklines, deadline);
5862 diffsB = this.DiffMain(text1B, text2B, checklines, deadline);
5863
5864 // Merge the results.
5865 return diffsA.concat([[DIFF_EQUAL, midCommon]], diffsB);
5866 }
5867
5868 if (checklines && text1.length > 100 && text2.length > 100) {
5869 return this.diffLineMode(text1, text2, deadline);
5870 }
5871
5872 return this.diffBisect(text1, text2, deadline);
5873 };
5874
5875 /**
5876 * Do the two texts share a substring which is at least half the length of the
5877 * longer text?
5878 * This speedup can produce non-minimal diffs.
5879 * @param {string} text1 First string.
5880 * @param {string} text2 Second string.
5881 * @return {Array.<string>} Five element Array, containing the prefix of
5882 * text1, the suffix of text1, the prefix of text2, the suffix of
5883 * text2 and the common middle. Or null if there was no match.
5884 * @private
5885 */
5886 DiffMatchPatch.prototype.diffHalfMatch = function (text1, text2) {
5887 var longtext, shorttext, dmp, text1A, text2B, text2A, text1B, midCommon, hm1, hm2, hm;
5888
5889 longtext = text1.length > text2.length ? text1 : text2;
5890 shorttext = text1.length > text2.length ? text2 : text1;
5891 if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {
5892 return null; // Pointless.
5893 }
5894 dmp = this; // 'this' becomes 'window' in a closure.
5895
5896 /**
5897 * Does a substring of shorttext exist within longtext such that the substring
5898 * is at least half the length of longtext?
5899 * Closure, but does not reference any external variables.
5900 * @param {string} longtext Longer string.
5901 * @param {string} shorttext Shorter string.
5902 * @param {number} i Start index of quarter length substring within longtext.
5903 * @return {Array.<string>} Five element Array, containing the prefix of
5904 * longtext, the suffix of longtext, the prefix of shorttext, the suffix
5905 * of shorttext and the common middle. Or null if there was no match.
5906 * @private
5907 */
5908 function diffHalfMatchI(longtext, shorttext, i) {
5909 var seed, j, bestCommon, prefixLength, suffixLength, bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
5910
5911 // Start with a 1/4 length substring at position i as a seed.
5912 seed = longtext.substring(i, i + Math.floor(longtext.length / 4));
5913 j = -1;
5914 bestCommon = "";
5915 while ((j = shorttext.indexOf(seed, j + 1)) !== -1) {
5916 prefixLength = dmp.diffCommonPrefix(longtext.substring(i), shorttext.substring(j));
5917 suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), shorttext.substring(0, j));
5918 if (bestCommon.length < suffixLength + prefixLength) {
5919 bestCommon = shorttext.substring(j - suffixLength, j) + shorttext.substring(j, j + prefixLength);
5920 bestLongtextA = longtext.substring(0, i - suffixLength);
5921 bestLongtextB = longtext.substring(i + prefixLength);
5922 bestShorttextA = shorttext.substring(0, j - suffixLength);
5923 bestShorttextB = shorttext.substring(j + prefixLength);
5924 }
5925 }
5926 if (bestCommon.length * 2 >= longtext.length) {
5927 return [bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB, bestCommon];
5928 } else {
5929 return null;
5930 }
5931 }
5932
5933 // First check if the second quarter is the seed for a half-match.
5934 hm1 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 4));
5935
5936 // Check again based on the third quarter.
5937 hm2 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 2));
5938 if (!hm1 && !hm2) {
5939 return null;
5940 } else if (!hm2) {
5941 hm = hm1;
5942 } else if (!hm1) {
5943 hm = hm2;
5944 } else {
5945
5946 // Both matched. Select the longest.
5947 hm = hm1[4].length > hm2[4].length ? hm1 : hm2;
5948 }
5949
5950 // A half-match was found, sort out the return data.
5951 if (text1.length > text2.length) {
5952 text1A = hm[0];
5953 text1B = hm[1];
5954 text2A = hm[2];
5955 text2B = hm[3];
5956 } else {
5957 text2A = hm[0];
5958 text2B = hm[1];
5959 text1A = hm[2];
5960 text1B = hm[3];
5961 }
5962 midCommon = hm[4];
5963 return [text1A, text1B, text2A, text2B, midCommon];
5964 };
5965
5966 /**
5967 * Do a quick line-level diff on both strings, then rediff the parts for
5968 * greater accuracy.
5969 * This speedup can produce non-minimal diffs.
5970 * @param {string} text1 Old string to be diffed.
5971 * @param {string} text2 New string to be diffed.
5972 * @param {number} deadline Time when the diff should be complete by.
5973 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
5974 * @private
5975 */
5976 DiffMatchPatch.prototype.diffLineMode = function (text1, text2, deadline) {
5977 var a, diffs, linearray, pointer, countInsert, countDelete, textInsert, textDelete, j;
5978
5979 // Scan the text on a line-by-line basis first.
5980 a = this.diffLinesToChars(text1, text2);
5981 text1 = a.chars1;
5982 text2 = a.chars2;
5983 linearray = a.lineArray;
5984
5985 diffs = this.DiffMain(text1, text2, false, deadline);
5986
5987 // Convert the diff back to original text.
5988 this.diffCharsToLines(diffs, linearray);
5989
5990 // Eliminate freak matches (e.g. blank lines)
5991 this.diffCleanupSemantic(diffs);
5992
5993 // Rediff any replacement blocks, this time character-by-character.
5994 // Add a dummy entry at the end.
5995 diffs.push([DIFF_EQUAL, ""]);
5996 pointer = 0;
5997 countDelete = 0;
5998 countInsert = 0;
5999 textDelete = "";
6000 textInsert = "";
6001 while (pointer < diffs.length) {
6002 switch (diffs[pointer][0]) {
6003 case DIFF_INSERT:
6004 countInsert++;
6005 textInsert += diffs[pointer][1];
6006 break;
6007 case DIFF_DELETE:
6008 countDelete++;
6009 textDelete += diffs[pointer][1];
6010 break;
6011 case DIFF_EQUAL:
6012
6013 // Upon reaching an equality, check for prior redundancies.
6014 if (countDelete >= 1 && countInsert >= 1) {
6015
6016 // Delete the offending records and add the merged ones.
6017 diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert);
6018 pointer = pointer - countDelete - countInsert;
6019 a = this.DiffMain(textDelete, textInsert, false, deadline);
6020 for (j = a.length - 1; j >= 0; j--) {
6021 diffs.splice(pointer, 0, a[j]);
6022 }
6023 pointer = pointer + a.length;
6024 }
6025 countInsert = 0;
6026 countDelete = 0;
6027 textDelete = "";
6028 textInsert = "";
6029 break;
6030 }
6031 pointer++;
6032 }
6033 diffs.pop(); // Remove the dummy entry at the end.
6034
6035 return diffs;
6036 };
6037
6038 /**
6039 * Find the 'middle snake' of a diff, split the problem in two
6040 * and return the recursively constructed diff.
6041 * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
6042 * @param {string} text1 Old string to be diffed.
6043 * @param {string} text2 New string to be diffed.
6044 * @param {number} deadline Time at which to bail if not yet complete.
6045 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
6046 * @private
6047 */
6048 DiffMatchPatch.prototype.diffBisect = function (text1, text2, deadline) {
6049 var text1Length, text2Length, maxD, vOffset, vLength, v1, v2, x, delta, front, k1start, k1end, k2start, k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
6050
6051 // Cache the text lengths to prevent multiple calls.
6052 text1Length = text1.length;
6053 text2Length = text2.length;
6054 maxD = Math.ceil((text1Length + text2Length) / 2);
6055 vOffset = maxD;
6056 vLength = 2 * maxD;
6057 v1 = new Array(vLength);
6058 v2 = new Array(vLength);
6059
6060 // Setting all elements to -1 is faster in Chrome & Firefox than mixing
6061 // integers and undefined.
6062 for (x = 0; x < vLength; x++) {
6063 v1[x] = -1;
6064 v2[x] = -1;
6065 }
6066 v1[vOffset + 1] = 0;
6067 v2[vOffset + 1] = 0;
6068 delta = text1Length - text2Length;
6069
6070 // If the total number of characters is odd, then the front path will collide
6071 // with the reverse path.
6072 front = delta % 2 !== 0;
6073
6074 // Offsets for start and end of k loop.
6075 // Prevents mapping of space beyond the grid.
6076 k1start = 0;
6077 k1end = 0;
6078 k2start = 0;
6079 k2end = 0;
6080 for (d = 0; d < maxD; d++) {
6081
6082 // Bail out if deadline is reached.
6083 if (new Date().getTime() > deadline) {
6084 break;
6085 }
6086
6087 // Walk the front path one step.
6088 for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
6089 k1Offset = vOffset + k1;
6090 if (k1 === -d || k1 !== d && v1[k1Offset - 1] < v1[k1Offset + 1]) {
6091 x1 = v1[k1Offset + 1];
6092 } else {
6093 x1 = v1[k1Offset - 1] + 1;
6094 }
6095 y1 = x1 - k1;
6096 while (x1 < text1Length && y1 < text2Length && text1.charAt(x1) === text2.charAt(y1)) {
6097 x1++;
6098 y1++;
6099 }
6100 v1[k1Offset] = x1;
6101 if (x1 > text1Length) {
6102
6103 // Ran off the right of the graph.
6104 k1end += 2;
6105 } else if (y1 > text2Length) {
6106
6107 // Ran off the bottom of the graph.
6108 k1start += 2;
6109 } else if (front) {
6110 k2Offset = vOffset + delta - k1;
6111 if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) {
6112
6113 // Mirror x2 onto top-left coordinate system.
6114 x2 = text1Length - v2[k2Offset];
6115 if (x1 >= x2) {
6116
6117 // Overlap detected.
6118 return this.diffBisectSplit(text1, text2, x1, y1, deadline);
6119 }
6120 }
6121 }
6122 }
6123
6124 // Walk the reverse path one step.
6125 for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
6126 k2Offset = vOffset + k2;
6127 if (k2 === -d || k2 !== d && v2[k2Offset - 1] < v2[k2Offset + 1]) {
6128 x2 = v2[k2Offset + 1];
6129 } else {
6130 x2 = v2[k2Offset - 1] + 1;
6131 }
6132 y2 = x2 - k2;
6133 while (x2 < text1Length && y2 < text2Length && text1.charAt(text1Length - x2 - 1) === text2.charAt(text2Length - y2 - 1)) {
6134 x2++;
6135 y2++;
6136 }
6137 v2[k2Offset] = x2;
6138 if (x2 > text1Length) {
6139
6140 // Ran off the left of the graph.
6141 k2end += 2;
6142 } else if (y2 > text2Length) {
6143
6144 // Ran off the top of the graph.
6145 k2start += 2;
6146 } else if (!front) {
6147 k1Offset = vOffset + delta - k2;
6148 if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) {
6149 x1 = v1[k1Offset];
6150 y1 = vOffset + x1 - k1Offset;
6151
6152 // Mirror x2 onto top-left coordinate system.
6153 x2 = text1Length - x2;
6154 if (x1 >= x2) {
6155
6156 // Overlap detected.
6157 return this.diffBisectSplit(text1, text2, x1, y1, deadline);
6158 }
6159 }
6160 }
6161 }
6162 }
6163
6164 // Diff took too long and hit the deadline or
6165 // number of diffs equals number of characters, no commonality at all.
6166 return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
6167 };
6168
6169 /**
6170 * Given the location of the 'middle snake', split the diff in two parts
6171 * and recurse.
6172 * @param {string} text1 Old string to be diffed.
6173 * @param {string} text2 New string to be diffed.
6174 * @param {number} x Index of split point in text1.
6175 * @param {number} y Index of split point in text2.
6176 * @param {number} deadline Time at which to bail if not yet complete.
6177 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
6178 * @private
6179 */
6180 DiffMatchPatch.prototype.diffBisectSplit = function (text1, text2, x, y, deadline) {
6181 var text1a, text1b, text2a, text2b, diffs, diffsb;
6182 text1a = text1.substring(0, x);
6183 text2a = text2.substring(0, y);
6184 text1b = text1.substring(x);
6185 text2b = text2.substring(y);
6186
6187 // Compute both diffs serially.
6188 diffs = this.DiffMain(text1a, text2a, false, deadline);
6189 diffsb = this.DiffMain(text1b, text2b, false, deadline);
6190
6191 return diffs.concat(diffsb);
6192 };
6193
6194 /**
6195 * Reduce the number of edits by eliminating semantically trivial equalities.
6196 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
6197 */
6198 DiffMatchPatch.prototype.diffCleanupSemantic = function (diffs) {
6199 var changes, equalities, equalitiesLength, lastequality, pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2;
6200 changes = false;
6201 equalities = []; // Stack of indices where equalities are found.
6202 equalitiesLength = 0; // Keeping our own length var is faster in JS.
6203 /** @type {?string} */
6204 lastequality = null;
6205
6206 // Always equal to diffs[equalities[equalitiesLength - 1]][1]
6207 pointer = 0; // Index of current position.
6208
6209 // Number of characters that changed prior to the equality.
6210 lengthInsertions1 = 0;
6211 lengthDeletions1 = 0;
6212
6213 // Number of characters that changed after the equality.
6214 lengthInsertions2 = 0;
6215 lengthDeletions2 = 0;
6216 while (pointer < diffs.length) {
6217 if (diffs[pointer][0] === DIFF_EQUAL) {
6218 // Equality found.
6219 equalities[equalitiesLength++] = pointer;
6220 lengthInsertions1 = lengthInsertions2;
6221 lengthDeletions1 = lengthDeletions2;
6222 lengthInsertions2 = 0;
6223 lengthDeletions2 = 0;
6224 lastequality = diffs[pointer][1];
6225 } else {
6226 // An insertion or deletion.
6227 if (diffs[pointer][0] === DIFF_INSERT) {
6228 lengthInsertions2 += diffs[pointer][1].length;
6229 } else {
6230 lengthDeletions2 += diffs[pointer][1].length;
6231 }
6232
6233 // Eliminate an equality that is smaller or equal to the edits on both
6234 // sides of it.
6235 if (lastequality && lastequality.length <= Math.max(lengthInsertions1, lengthDeletions1) && lastequality.length <= Math.max(lengthInsertions2, lengthDeletions2)) {
6236
6237 // Duplicate record.
6238 diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
6239
6240 // Change second copy to insert.
6241 diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
6242
6243 // Throw away the equality we just deleted.
6244 equalitiesLength--;
6245
6246 // Throw away the previous equality (it needs to be reevaluated).
6247 equalitiesLength--;
6248 pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
6249
6250 // Reset the counters.
6251 lengthInsertions1 = 0;
6252 lengthDeletions1 = 0;
6253 lengthInsertions2 = 0;
6254 lengthDeletions2 = 0;
6255 lastequality = null;
6256 changes = true;
6257 }
6258 }
6259 pointer++;
6260 }
6261
6262 // Normalize the diff.
6263 if (changes) {
6264 this.diffCleanupMerge(diffs);
6265 }
6266
6267 // Find any overlaps between deletions and insertions.
6268 // e.g: <del>abcxxx</del><ins>xxxdef</ins>
6269 // -> <del>abc</del>xxx<ins>def</ins>
6270 // e.g: <del>xxxabc</del><ins>defxxx</ins>
6271 // -> <ins>def</ins>xxx<del>abc</del>
6272 // Only extract an overlap if it is as big as the edit ahead or behind it.
6273 pointer = 1;
6274 while (pointer < diffs.length) {
6275 if (diffs[pointer - 1][0] === DIFF_DELETE && diffs[pointer][0] === DIFF_INSERT) {
6276 deletion = diffs[pointer - 1][1];
6277 insertion = diffs[pointer][1];
6278 overlapLength1 = this.diffCommonOverlap(deletion, insertion);
6279 overlapLength2 = this.diffCommonOverlap(insertion, deletion);
6280 if (overlapLength1 >= overlapLength2) {
6281 if (overlapLength1 >= deletion.length / 2 || overlapLength1 >= insertion.length / 2) {
6282
6283 // Overlap found. Insert an equality and trim the surrounding edits.
6284 diffs.splice(pointer, 0, [DIFF_EQUAL, insertion.substring(0, overlapLength1)]);
6285 diffs[pointer - 1][1] = deletion.substring(0, deletion.length - overlapLength1);
6286 diffs[pointer + 1][1] = insertion.substring(overlapLength1);
6287 pointer++;
6288 }
6289 } else {
6290 if (overlapLength2 >= deletion.length / 2 || overlapLength2 >= insertion.length / 2) {
6291
6292 // Reverse overlap found.
6293 // Insert an equality and swap and trim the surrounding edits.
6294 diffs.splice(pointer, 0, [DIFF_EQUAL, deletion.substring(0, overlapLength2)]);
6295
6296 diffs[pointer - 1][0] = DIFF_INSERT;
6297 diffs[pointer - 1][1] = insertion.substring(0, insertion.length - overlapLength2);
6298 diffs[pointer + 1][0] = DIFF_DELETE;
6299 diffs[pointer + 1][1] = deletion.substring(overlapLength2);
6300 pointer++;
6301 }
6302 }
6303 pointer++;
6304 }
6305 pointer++;
6306 }
6307 };
6308
6309 /**
6310 * Determine if the suffix of one string is the prefix of another.
6311 * @param {string} text1 First string.
6312 * @param {string} text2 Second string.
6313 * @return {number} The number of characters common to the end of the first
6314 * string and the start of the second string.
6315 * @private
6316 */
6317 DiffMatchPatch.prototype.diffCommonOverlap = function (text1, text2) {
6318 var text1Length, text2Length, textLength, best, length, pattern, found;
6319
6320 // Cache the text lengths to prevent multiple calls.
6321 text1Length = text1.length;
6322 text2Length = text2.length;
6323
6324 // Eliminate the null case.
6325 if (text1Length === 0 || text2Length === 0) {
6326 return 0;
6327 }
6328
6329 // Truncate the longer string.
6330 if (text1Length > text2Length) {
6331 text1 = text1.substring(text1Length - text2Length);
6332 } else if (text1Length < text2Length) {
6333 text2 = text2.substring(0, text1Length);
6334 }
6335 textLength = Math.min(text1Length, text2Length);
6336
6337 // Quick check for the worst case.
6338 if (text1 === text2) {
6339 return textLength;
6340 }
6341
6342 // Start by looking for a single character match
6343 // and increase length until no match is found.
6344 // Performance analysis: https://neil.fraser.name/news/2010/11/04/
6345 best = 0;
6346 length = 1;
6347 while (true) {
6348 pattern = text1.substring(textLength - length);
6349 found = text2.indexOf(pattern);
6350 if (found === -1) {
6351 return best;
6352 }
6353 length += found;
6354 if (found === 0 || text1.substring(textLength - length) === text2.substring(0, length)) {
6355 best = length;
6356 length++;
6357 }
6358 }
6359 };
6360
6361 /**
6362 * Split two texts into an array of strings. Reduce the texts to a string of
6363 * hashes where each Unicode character represents one line.
6364 * @param {string} text1 First string.
6365 * @param {string} text2 Second string.
6366 * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
6367 * An object containing the encoded text1, the encoded text2 and
6368 * the array of unique strings.
6369 * The zeroth element of the array of unique strings is intentionally blank.
6370 * @private
6371 */
6372 DiffMatchPatch.prototype.diffLinesToChars = function (text1, text2) {
6373 var lineArray, lineHash, chars1, chars2;
6374 lineArray = []; // E.g. lineArray[4] === 'Hello\n'
6375 lineHash = {}; // E.g. lineHash['Hello\n'] === 4
6376
6377 // '\x00' is a valid character, but various debuggers don't like it.
6378 // So we'll insert a junk entry to avoid generating a null character.
6379 lineArray[0] = "";
6380
6381 /**
6382 * Split a text into an array of strings. Reduce the texts to a string of
6383 * hashes where each Unicode character represents one line.
6384 * Modifies linearray and linehash through being a closure.
6385 * @param {string} text String to encode.
6386 * @return {string} Encoded string.
6387 * @private
6388 */
6389 function diffLinesToCharsMunge(text) {
6390 var chars, lineStart, lineEnd, lineArrayLength, line;
6391 chars = "";
6392
6393 // Walk the text, pulling out a substring for each line.
6394 // text.split('\n') would would temporarily double our memory footprint.
6395 // Modifying text would create many large strings to garbage collect.
6396 lineStart = 0;
6397 lineEnd = -1;
6398
6399 // Keeping our own length variable is faster than looking it up.
6400 lineArrayLength = lineArray.length;
6401 while (lineEnd < text.length - 1) {
6402 lineEnd = text.indexOf("\n", lineStart);
6403 if (lineEnd === -1) {
6404 lineEnd = text.length - 1;
6405 }
6406 line = text.substring(lineStart, lineEnd + 1);
6407 lineStart = lineEnd + 1;
6408
6409 var lineHashExists = lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : lineHash[line] !== undefined;
6410
6411 if (lineHashExists) {
6412 chars += String.fromCharCode(lineHash[line]);
6413 } else {
6414 chars += String.fromCharCode(lineArrayLength);
6415 lineHash[line] = lineArrayLength;
6416 lineArray[lineArrayLength++] = line;
6417 }
6418 }
6419 return chars;
6420 }
6421
6422 chars1 = diffLinesToCharsMunge(text1);
6423 chars2 = diffLinesToCharsMunge(text2);
6424 return {
6425 chars1: chars1,
6426 chars2: chars2,
6427 lineArray: lineArray
6428 };
6429 };
6430
6431 /**
6432 * Rehydrate the text in a diff from a string of line hashes to real lines of
6433 * text.
6434 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
6435 * @param {!Array.<string>} lineArray Array of unique strings.
6436 * @private
6437 */
6438 DiffMatchPatch.prototype.diffCharsToLines = function (diffs, lineArray) {
6439 var x, chars, text, y;
6440 for (x = 0; x < diffs.length; x++) {
6441 chars = diffs[x][1];
6442 text = [];
6443 for (y = 0; y < chars.length; y++) {
6444 text[y] = lineArray[chars.charCodeAt(y)];
6445 }
6446 diffs[x][1] = text.join("");
6447 }
6448 };
6449
6450 /**
6451 * Reorder and merge like edit sections. Merge equalities.
6452 * Any edit section can move as long as it doesn't cross an equality.
6453 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
6454 */
6455 DiffMatchPatch.prototype.diffCleanupMerge = function (diffs) {
6456 var pointer, countDelete, countInsert, textInsert, textDelete, commonlength, changes, diffPointer, position;
6457 diffs.push([DIFF_EQUAL, ""]); // Add a dummy entry at the end.
6458 pointer = 0;
6459 countDelete = 0;
6460 countInsert = 0;
6461 textDelete = "";
6462 textInsert = "";
6463
6464 while (pointer < diffs.length) {
6465 switch (diffs[pointer][0]) {
6466 case DIFF_INSERT:
6467 countInsert++;
6468 textInsert += diffs[pointer][1];
6469 pointer++;
6470 break;
6471 case DIFF_DELETE:
6472 countDelete++;
6473 textDelete += diffs[pointer][1];
6474 pointer++;
6475 break;
6476 case DIFF_EQUAL:
6477
6478 // Upon reaching an equality, check for prior redundancies.
6479 if (countDelete + countInsert > 1) {
6480 if (countDelete !== 0 && countInsert !== 0) {
6481
6482 // Factor out any common prefixes.
6483 commonlength = this.diffCommonPrefix(textInsert, textDelete);
6484 if (commonlength !== 0) {
6485 if (pointer - countDelete - countInsert > 0 && diffs[pointer - countDelete - countInsert - 1][0] === DIFF_EQUAL) {
6486 diffs[pointer - countDelete - countInsert - 1][1] += textInsert.substring(0, commonlength);
6487 } else {
6488 diffs.splice(0, 0, [DIFF_EQUAL, textInsert.substring(0, commonlength)]);
6489 pointer++;
6490 }
6491 textInsert = textInsert.substring(commonlength);
6492 textDelete = textDelete.substring(commonlength);
6493 }
6494
6495 // Factor out any common suffixies.
6496 commonlength = this.diffCommonSuffix(textInsert, textDelete);
6497 if (commonlength !== 0) {
6498 diffs[pointer][1] = textInsert.substring(textInsert.length - commonlength) + diffs[pointer][1];
6499 textInsert = textInsert.substring(0, textInsert.length - commonlength);
6500 textDelete = textDelete.substring(0, textDelete.length - commonlength);
6501 }
6502 }
6503
6504 // Delete the offending records and add the merged ones.
6505 if (countDelete === 0) {
6506 diffs.splice(pointer - countInsert, countDelete + countInsert, [DIFF_INSERT, textInsert]);
6507 } else if (countInsert === 0) {
6508 diffs.splice(pointer - countDelete, countDelete + countInsert, [DIFF_DELETE, textDelete]);
6509 } else {
6510 diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert, [DIFF_DELETE, textDelete], [DIFF_INSERT, textInsert]);
6511 }
6512 pointer = pointer - countDelete - countInsert + (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1;
6513 } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) {
6514
6515 // Merge this equality with the previous one.
6516 diffs[pointer - 1][1] += diffs[pointer][1];
6517 diffs.splice(pointer, 1);
6518 } else {
6519 pointer++;
6520 }
6521 countInsert = 0;
6522 countDelete = 0;
6523 textDelete = "";
6524 textInsert = "";
6525 break;
6526 }
6527 }
6528 if (diffs[diffs.length - 1][1] === "") {
6529 diffs.pop(); // Remove the dummy entry at the end.
6530 }
6531
6532 // Second pass: look for single edits surrounded on both sides by equalities
6533 // which can be shifted sideways to eliminate an equality.
6534 // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
6535 changes = false;
6536 pointer = 1;
6537
6538 // Intentionally ignore the first and last element (don't need checking).
6539 while (pointer < diffs.length - 1) {
6540 if (diffs[pointer - 1][0] === DIFF_EQUAL && diffs[pointer + 1][0] === DIFF_EQUAL) {
6541
6542 diffPointer = diffs[pointer][1];
6543 position = diffPointer.substring(diffPointer.length - diffs[pointer - 1][1].length);
6544
6545 // This is a single edit surrounded by equalities.
6546 if (position === diffs[pointer - 1][1]) {
6547
6548 // Shift the edit over the previous equality.
6549 diffs[pointer][1] = diffs[pointer - 1][1] + diffs[pointer][1].substring(0, diffs[pointer][1].length - diffs[pointer - 1][1].length);
6550 diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
6551 diffs.splice(pointer - 1, 1);
6552 changes = true;
6553 } else if (diffPointer.substring(0, diffs[pointer + 1][1].length) === diffs[pointer + 1][1]) {
6554
6555 // Shift the edit over the next equality.
6556 diffs[pointer - 1][1] += diffs[pointer + 1][1];
6557 diffs[pointer][1] = diffs[pointer][1].substring(diffs[pointer + 1][1].length) + diffs[pointer + 1][1];
6558 diffs.splice(pointer + 1, 1);
6559 changes = true;
6560 }
6561 }
6562 pointer++;
6563 }
6564
6565 // If shifts were made, the diff needs reordering and another shift sweep.
6566 if (changes) {
6567 this.diffCleanupMerge(diffs);
6568 }
6569 };
6570
6571 return function (o, n) {
6572 var diff, output, text;
6573 diff = new DiffMatchPatch();
6574 output = diff.DiffMain(o, n);
6575 diff.diffCleanupEfficiency(output);
6576 text = diff.diffPrettyHtml(output);
6577
6578 return text;
6579 };
6580 }();
6581
6582 }((function() { return this; }())));