From 6c18935dc379ca5e0599c63b341b7994820ac659 Mon Sep 17 00:00:00 2001 From: Graeme Yeates Date: Thu, 2 Apr 2015 11:55:59 -0400 Subject: [PATCH 001/263] Implement partion via group --- underscore.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/underscore.js b/underscore.js index b29332f94..8809ecf78 100644 --- a/underscore.js +++ b/underscore.js @@ -391,9 +391,9 @@ }; // An internal function used for aggregate "group by" operations. - var group = function(behavior) { + var group = function(behavior, initializer) { return function(obj, iteratee, context) { - var result = {}; + var result = initializer ? initializer() : {}; iteratee = cb(iteratee, context); _.each(obj, function(value, index) { var key = iteratee(value, index, obj); @@ -438,14 +438,9 @@ // Split a collection into two arrays: one whose elements all satisfy the given // predicate, and one whose elements all do not satisfy the predicate. - _.partition = function(obj, predicate, context) { - predicate = cb(predicate, context); - var pass = [], fail = []; - _.each(obj, function(value, key, obj) { - (predicate(value, key, obj) ? pass : fail).push(value); - }); - return [pass, fail]; - }; + _.partition = group(function(result, value, pass) { + result[pass ? 0 : 1].push(value); + }, function() { return [[], []]; }); // Array Functions // --------------- From c545b48d16ce920cb91d5e690736635022671562 Mon Sep 17 00:00:00 2001 From: Graeme Yeates Date: Thu, 2 Apr 2015 12:20:17 -0400 Subject: [PATCH 002/263] Avoid initializer use --- underscore.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/underscore.js b/underscore.js index 8809ecf78..aac4d19d7 100644 --- a/underscore.js +++ b/underscore.js @@ -391,9 +391,9 @@ }; // An internal function used for aggregate "group by" operations. - var group = function(behavior, initializer) { + var group = function(behavior, partition) { return function(obj, iteratee, context) { - var result = initializer ? initializer() : {}; + var result = partition ? [[], []] : {}; iteratee = cb(iteratee, context); _.each(obj, function(value, index) { var key = iteratee(value, index, obj); @@ -440,7 +440,7 @@ // predicate, and one whose elements all do not satisfy the predicate. _.partition = group(function(result, value, pass) { result[pass ? 0 : 1].push(value); - }, function() { return [[], []]; }); + }, true); // Array Functions // --------------- From 6b29e986b99fc18dc3fded842b5e2e424a7557b5 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Thu, 2 Apr 2015 16:11:54 -0400 Subject: [PATCH 003/263] Some simple code cleanups - Moves createAssigner to just above where it's needed. - Use function expressions, not declarations. --- underscore.js | 56 +++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/underscore.js b/underscore.js index aac4d19d7..b38b1192b 100644 --- a/underscore.js +++ b/underscore.js @@ -94,24 +94,6 @@ return cb(value, context, Infinity); }; - // An internal function for creating assigner functions. - var createAssigner = function(keysFunc, undefinedOnly) { - return function(obj) { - var length = arguments.length; - if (length < 2 || obj == null) return obj; - for (var index = 1; index < length; index++) { - var source = arguments[index], - keys = keysFunc(source), - l = keys.length; - for (var i = 0; i < l; i++) { - var key = keys[i]; - if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key]; - } - } - return obj; - }; - }; - // An internal function for creating a new object that inherits from another. var baseCreate = function(prototype) { if (!_.isObject(prototype)) return {}; @@ -175,16 +157,16 @@ }; // Create a reducing function iterating left or right. - function createReduce(dir) { + var createReduce = function(dir) { // Optimized iterator function as using arguments.length // in the main function will deoptimize the, see #1991. - function iterator(obj, iteratee, memo, keys, index, length) { + var iterator = function(obj, iteratee, memo, keys, index, length) { for (; index >= 0 && index < length; index += dir) { var currentKey = keys ? keys[index] : index; memo = iteratee(memo, obj[currentKey], currentKey, obj); } return memo; - } + }; return function(obj, iteratee, memo, context) { iteratee = optimizeCb(iteratee, context, 4); @@ -198,7 +180,7 @@ } return iterator(obj, iteratee, memo, keys, index, length); }; - } + }; // **Reduce** builds up a single result from a list of values, aka `inject`, // or `foldl`. @@ -606,7 +588,7 @@ }; // Generator function to create the findIndex and findLastIndex functions - function createPredicateIndexFinder(dir) { + var createPredicateIndexFinder = function(dir) { return function(array, predicate, context) { predicate = cb(predicate, context); var length = getLength(array); @@ -616,7 +598,7 @@ } return -1; }; - } + }; // Returns the first index on an array-like that passes a predicate test _.findIndex = createPredicateIndexFinder(1); @@ -636,7 +618,7 @@ }; // Generator function to create the indexOf and lastIndexOf functions - function createIndexFinder(dir, predicateFind, sortedIndex) { + var createIndexFinder = function(dir, predicateFind, sortedIndex) { return function(array, item, idx) { var i = 0, length = getLength(array); if (typeof idx == 'number') { @@ -658,7 +640,7 @@ } return -1; }; - } + }; // Return the position of the first occurrence of an item in an array, // or -1 if the item is not included in the array. @@ -901,7 +883,7 @@ var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString', 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString']; - function collectNonEnumProps(obj, keys) { + var collectNonEnumProps = function(obj, keys) { var nonEnumIdx = nonEnumerableProps.length; var constructor = obj.constructor; var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto; @@ -916,7 +898,7 @@ keys.push(prop); } } - } + }; // Retrieve the names of an object's own properties. // Delegates to **ECMAScript 5**'s native `Object.keys` @@ -997,6 +979,24 @@ return names.sort(); }; + // An internal function for creating assigner functions. + var createAssigner = function(keysFunc, undefinedOnly) { + return function(obj) { + var length = arguments.length; + if (length < 2 || obj == null) return obj; + for (var index = 1; index < length; index++) { + var source = arguments[index], + keys = keysFunc(source), + l = keys.length; + for (var i = 0; i < l; i++) { + var key = keys[i]; + if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key]; + } + } + return obj; + }; + }; + // Extend a given object with all the properties in passed-in object(s). _.extend = createAssigner(_.allKeys); From f934a6734ec7810dc544925a2f1f88caad61c610 Mon Sep 17 00:00:00 2001 From: Adam Krebs Date: Tue, 17 Jun 2014 17:20:25 -0400 Subject: [PATCH 004/263] add _.partial.placeholder. closes #1688 --- test/functions.js | 5 +++++ underscore.js | 10 +++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/test/functions.js b/test/functions.js index 0fded5c40..22408e1b5 100644 --- a/test/functions.js +++ b/test/functions.js @@ -77,6 +77,11 @@ ok(widget instanceof MyWidget, 'Can partially bind a constructor'); equal(widget.get(), 'foo', 'keeps prototype'); deepEqual(widget.options, {a: 1}); + + _.partial.placeholder = obj; + func = _.partial(function() { return arguments.length; }, obj, 'b', obj, 'd'); + equal(func('a'), 4, 'allows the placeholder to be swapped out'); + _.partial.placeholder = _; }); test('bindAll', function() { diff --git a/underscore.js b/underscore.js index b38b1192b..7974881c6 100644 --- a/underscore.js +++ b/underscore.js @@ -697,14 +697,16 @@ // Partially apply a function by creating a version that has had some of its // arguments pre-filled, without changing its dynamic `this` context. _ acts - // as a placeholder, allowing any combination of arguments to be pre-filled. + // as a placeholder by default, allowing any combination of arguments to be + // pre-filled. Set `_.partial.placeholder` for a custom placeholder argument. _.partial = function(func) { - var boundArgs = slice.call(arguments, 1); + var boundArgs = slice.call(arguments, 1), + placeholder = _.partial.placeholder; var bound = function() { var position = 0, length = boundArgs.length; var args = Array(length); for (var i = 0; i < length; i++) { - args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i]; + args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i]; } while (position < arguments.length) args.push(arguments[position++]); return executeBound(func, bound, this, this, args); @@ -712,6 +714,8 @@ return bound; }; + _.partial.placeholder = _; + // Bind a number of an object's methods to that object. Remaining arguments // are the method names to be bound. Useful for ensuring that all callbacks // defined on an object belong to it. From e848714848299a997766f7ce633c4abd700b4cba Mon Sep 17 00:00:00 2001 From: Adam Krebs Date: Wed, 18 Jun 2014 14:48:25 -0400 Subject: [PATCH 005/263] swapping the placeholder preserves previously bound args --- test/functions.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/functions.js b/test/functions.js index 22408e1b5..108b44613 100644 --- a/test/functions.js +++ b/test/functions.js @@ -77,10 +77,15 @@ ok(widget instanceof MyWidget, 'Can partially bind a constructor'); equal(widget.get(), 'foo', 'keeps prototype'); deepEqual(widget.options, {a: 1}); - + _.partial.placeholder = obj; func = _.partial(function() { return arguments.length; }, obj, 'b', obj, 'd'); equal(func('a'), 4, 'allows the placeholder to be swapped out'); + + _.partial.placeholder = {}; + func = _.partial(function() { return arguments.length; }, obj, 'b', obj, 'd'); + equal(func('a'), 5, 'swapping the placeholder preserves previously bound arguments'); + _.partial.placeholder = _; }); @@ -580,7 +585,7 @@ deepEqual(_.toArray(cb(1, 2, 3)), _.range(1, 4)); deepEqual(_.toArray(cb(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)), _.range(1, 11)); }); - + }); }()); From 0a9d9d86f21b29bc4ed582fcad734aadf8c7dabb Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Wed, 8 Apr 2015 12:46:56 -0400 Subject: [PATCH 006/263] param/arg fight --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 40bb45e5a..977413db1 100644 --- a/index.html +++ b/index.html @@ -1232,7 +1232,7 @@

Function (uh, ahem) Functions

- Pass true for the immediate parameter to cause + Pass true for the immediate argument to cause debounce to trigger the function on the leading instead of the trailing edge of the wait interval. Useful in circumstances like preventing accidental double-clicks on a "submit" button from firing a From f087449a74418ef26deed0f3315cc1256d8143ae Mon Sep 17 00:00:00 2001 From: Graeme Date: Wed, 18 Feb 2015 13:29:23 -0500 Subject: [PATCH 007/263] Attempt at restParams? ping @jridgewell @buzzdecafe @davidchambers @jdalton Implementation proves to be uglier than I had hoped :z going to write some benchmarks after we head for lunch --- underscore.js | 62 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/underscore.js b/underscore.js index 7974881c6..162e85160 100644 --- a/underscore.js +++ b/underscore.js @@ -94,6 +94,26 @@ return cb(value, context, Infinity); }; + // Similar to ES6's rest params (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html) + // This accumulates the arguments passed into an array, after a given index. + _.restParams = function(func, startIndex) { + startIndex = startIndex != null ? +startIndex : 1; + return function() { + var len = arguments.length; + var args = Array(len > startIndex ? len - startIndex : 0); + for (var index = startIndex; index < len; index++) { + args[index - startIndex] = arguments[index]; + } + switch (startIndex) { + case 0: return func.call(this, args); + case 1: return func.call(this, arguments[0], args); + case 2: return func.call(this, arguments[0], arguments[1], args); + case 3: return func.call(this, arguments[0], arguments[1], arguments[2], args); + } + return func.apply(this, _.take(arguments, startIndex).concat([args])); + }; + }; + // An internal function for creating a new object that inherits from another. var baseCreate = function(prototype) { if (!_.isObject(prototype)) return {}; @@ -251,14 +271,13 @@ }; // Invoke a method (with arguments) on every item in a collection. - _.invoke = function(obj, method) { - var args = slice.call(arguments, 2); + _.invoke = _.restParams(function(obj, method, args) { var isFunc = _.isFunction(method); return _.map(obj, function(value) { var func = isFunc ? method : value[method]; return func == null ? func : func.apply(value, args); }); - }; + }, 2); // Convenience version of a common use case of `map`: fetching a property. _.pluck = function(obj, key) { @@ -489,9 +508,9 @@ }; // Return a version of the array that does not contain the specified value(s). - _.without = function(array) { - return _.difference(array, slice.call(arguments, 1)); - }; + _.without = _.restParams(function(array, otherArrays) { + return _.difference(array, otherArrays); + }); // Produce a duplicate-free version of the array. If the array has already // been sorted, you have the option of using a faster algorithm. @@ -689,9 +708,9 @@ if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function'); var args = slice.call(arguments, 2); - var bound = function() { - return executeBound(func, bound, context, this, args.concat(slice.call(arguments))); - }; + var bound = _.restParams(function(callArgs) { + return executeBound(func, bound, context, this, args.concat(callArgs)); + }, 0); return bound; }; @@ -699,9 +718,8 @@ // arguments pre-filled, without changing its dynamic `this` context. _ acts // as a placeholder by default, allowing any combination of arguments to be // pre-filled. Set `_.partial.placeholder` for a custom placeholder argument. - _.partial = function(func) { - var boundArgs = slice.call(arguments, 1), - placeholder = _.partial.placeholder; + _.partial = _.restParams(function(func, boundArgs) { + var placeholder = _.partial.placeholder; var bound = function() { var position = 0, length = boundArgs.length; var args = Array(length); @@ -712,22 +730,19 @@ return executeBound(func, bound, this, this, args); }; return bound; - }; + }); _.partial.placeholder = _; // Bind a number of an object's methods to that object. Remaining arguments // are the method names to be bound. Useful for ensuring that all callbacks // defined on an object belong to it. - _.bindAll = function(obj) { - var i, length = arguments.length, key; - if (length <= 1) throw new Error('bindAll must be passed function names'); - for (i = 1; i < length; i++) { - key = arguments[i]; + _.bindAll = _.restParams(function(obj, keys) { + if (keys.length < 1) throw new Error('bindAll must be passed function names'); + return _.each(keys, function(key) { obj[key] = _.bind(obj[key], obj); - } - return obj; - }; + }); + }); // Memoize an expensive function by storing its results. _.memoize = function(func, hasher) { @@ -743,12 +758,11 @@ // Delays a function for the given number of milliseconds, and then calls // it with the arguments supplied. - _.delay = function(func, wait) { - var args = slice.call(arguments, 2); + _.delay = _.restParams(function(func, wait, args) { return setTimeout(function(){ return func.apply(null, args); }, wait); - }; + }, 2); // Defers a function, scheduling it to run after the current call stack has // cleared. From 925f1bac6086f998ab0d81ca052224201eabda02 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Wed, 25 Feb 2015 16:12:16 -0500 Subject: [PATCH 008/263] Speed up _.restParams Fix error This is the last time I write things in Github editor Tricky slice Allow startIndex param really, really fix it this time Don't redeclare --- underscore.js | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/underscore.js b/underscore.js index 162e85160..3e7829706 100644 --- a/underscore.js +++ b/underscore.js @@ -97,20 +97,25 @@ // Similar to ES6's rest params (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html) // This accumulates the arguments passed into an array, after a given index. _.restParams = function(func, startIndex) { - startIndex = startIndex != null ? +startIndex : 1; + startIndex = startIndex == null ? func.length - 1 : +startIndex; return function() { - var len = arguments.length; - var args = Array(len > startIndex ? len - startIndex : 0); - for (var index = startIndex; index < len; index++) { - args[index - startIndex] = arguments[index]; + var length = arguments.length > startIndex ? arguments.length - startIndex : 0; + var rest = Array(length); + for (var index = 0; index < length; index++) { + rest[index] = arguments[index + startIndex]; } switch (startIndex) { - case 0: return func.call(this, args); - case 1: return func.call(this, arguments[0], args); - case 2: return func.call(this, arguments[0], arguments[1], args); - case 3: return func.call(this, arguments[0], arguments[1], arguments[2], args); + case 0: return func.call(this, rest); + case 1: return func.call(this, arguments[0], rest); + case 2: return func.call(this, arguments[0], arguments[1], rest); + case 3: return func.call(this, arguments[0], arguments[1], arguments[2], rest); + } + var args = Array(startIndex + 1); + for (index = 0; index < startIndex; index++) { + args[index] = arguments[index]; } - return func.apply(this, _.take(arguments, startIndex).concat([args])); + args[index] = rest; + return func.apply(this, args); }; }; @@ -277,7 +282,7 @@ var func = isFunc ? method : value[method]; return func == null ? func : func.apply(value, args); }); - }, 2); + }); // Convenience version of a common use case of `map`: fetching a property. _.pluck = function(obj, key) { @@ -710,7 +715,7 @@ var args = slice.call(arguments, 2); var bound = _.restParams(function(callArgs) { return executeBound(func, bound, context, this, args.concat(callArgs)); - }, 0); + }); return bound; }; @@ -762,7 +767,7 @@ return setTimeout(function(){ return func.apply(null, args); }, wait); - }, 2); + }); // Defers a function, scheduling it to run after the current call stack has // cleared. From add363c12dd8be0a6125c36e4c3b774d7e077cc7 Mon Sep 17 00:00:00 2001 From: Graeme Yeates Date: Thu, 2 Apr 2015 12:55:38 -0400 Subject: [PATCH 009/263] Use in chaining and add rename to restParam --- underscore.js | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/underscore.js b/underscore.js index 3e7829706..2df85a5a5 100644 --- a/underscore.js +++ b/underscore.js @@ -19,7 +19,6 @@ // Create quick reference variables for speed access to core prototypes. var - push = ArrayProto.push, slice = ArrayProto.slice, toString = ObjProto.toString, hasOwnProperty = ObjProto.hasOwnProperty; @@ -96,7 +95,7 @@ // Similar to ES6's rest params (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html) // This accumulates the arguments passed into an array, after a given index. - _.restParams = function(func, startIndex) { + _.restParam = function(func, startIndex) { startIndex = startIndex == null ? func.length - 1 : +startIndex; return function() { var length = arguments.length > startIndex ? arguments.length - startIndex : 0; @@ -108,7 +107,6 @@ case 0: return func.call(this, rest); case 1: return func.call(this, arguments[0], rest); case 2: return func.call(this, arguments[0], arguments[1], rest); - case 3: return func.call(this, arguments[0], arguments[1], arguments[2], rest); } var args = Array(startIndex + 1); for (index = 0; index < startIndex; index++) { @@ -276,7 +274,7 @@ }; // Invoke a method (with arguments) on every item in a collection. - _.invoke = _.restParams(function(obj, method, args) { + _.invoke = _.restParam(function(obj, method, args) { var isFunc = _.isFunction(method); return _.map(obj, function(value) { var func = isFunc ? method : value[method]; @@ -513,7 +511,7 @@ }; // Return a version of the array that does not contain the specified value(s). - _.without = _.restParams(function(array, otherArrays) { + _.without = _.restParam(function(array, otherArrays) { return _.difference(array, otherArrays); }); @@ -713,7 +711,7 @@ if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function'); var args = slice.call(arguments, 2); - var bound = _.restParams(function(callArgs) { + var bound = _.restParam(function(callArgs) { return executeBound(func, bound, context, this, args.concat(callArgs)); }); return bound; @@ -723,7 +721,7 @@ // arguments pre-filled, without changing its dynamic `this` context. _ acts // as a placeholder by default, allowing any combination of arguments to be // pre-filled. Set `_.partial.placeholder` for a custom placeholder argument. - _.partial = _.restParams(function(func, boundArgs) { + _.partial = _.restParam(function(func, boundArgs) { var placeholder = _.partial.placeholder; var bound = function() { var position = 0, length = boundArgs.length; @@ -742,7 +740,7 @@ // Bind a number of an object's methods to that object. Remaining arguments // are the method names to be bound. Useful for ensuring that all callbacks // defined on an object belong to it. - _.bindAll = _.restParams(function(obj, keys) { + _.bindAll = _.restParam(function(obj, keys) { if (keys.length < 1) throw new Error('bindAll must be passed function names'); return _.each(keys, function(key) { obj[key] = _.bind(obj[key], obj); @@ -763,7 +761,7 @@ // Delays a function for the given number of milliseconds, and then calls // it with the arguments supplied. - _.delay = _.restParams(function(func, wait, args) { + _.delay = _.restParam(function(func, wait, args) { return setTimeout(function(){ return func.apply(null, args); }, wait); @@ -1508,11 +1506,10 @@ _.mixin = function(obj) { _.each(_.functions(obj), function(name) { var func = _[name] = obj[name]; - _.prototype[name] = function() { - var args = [this._wrapped]; - push.apply(args, arguments); + _.prototype[name] = _.restParam(function(args) { + args.unshift(this._wrapped); return result(this, func.apply(_, args)); - }; + }); }); }; From 33f6489cb4913abade920b98a6c5ce46b9d188b7 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Thu, 2 Apr 2015 17:35:15 -0400 Subject: [PATCH 010/263] Assign _.restParam in Functions section --- underscore.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/underscore.js b/underscore.js index 2df85a5a5..3003c9429 100644 --- a/underscore.js +++ b/underscore.js @@ -95,7 +95,7 @@ // Similar to ES6's rest params (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html) // This accumulates the arguments passed into an array, after a given index. - _.restParam = function(func, startIndex) { + var restParam = function(func, startIndex) { startIndex = startIndex == null ? func.length - 1 : +startIndex; return function() { var length = arguments.length > startIndex ? arguments.length - startIndex : 0; @@ -112,7 +112,7 @@ for (index = 0; index < startIndex; index++) { args[index] = arguments[index]; } - args[index] = rest; + args[startIndex] = rest; return func.apply(this, args); }; }; @@ -274,7 +274,7 @@ }; // Invoke a method (with arguments) on every item in a collection. - _.invoke = _.restParam(function(obj, method, args) { + _.invoke = restParam(function(obj, method, args) { var isFunc = _.isFunction(method); return _.map(obj, function(value) { var func = isFunc ? method : value[method]; @@ -511,7 +511,7 @@ }; // Return a version of the array that does not contain the specified value(s). - _.without = _.restParam(function(array, otherArrays) { + _.without = restParam(function(array, otherArrays) { return _.difference(array, otherArrays); }); @@ -711,7 +711,7 @@ if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function'); var args = slice.call(arguments, 2); - var bound = _.restParam(function(callArgs) { + var bound = restParam(function(callArgs) { return executeBound(func, bound, context, this, args.concat(callArgs)); }); return bound; @@ -721,7 +721,7 @@ // arguments pre-filled, without changing its dynamic `this` context. _ acts // as a placeholder by default, allowing any combination of arguments to be // pre-filled. Set `_.partial.placeholder` for a custom placeholder argument. - _.partial = _.restParam(function(func, boundArgs) { + _.partial = restParam(function(func, boundArgs) { var placeholder = _.partial.placeholder; var bound = function() { var position = 0, length = boundArgs.length; @@ -740,7 +740,7 @@ // Bind a number of an object's methods to that object. Remaining arguments // are the method names to be bound. Useful for ensuring that all callbacks // defined on an object belong to it. - _.bindAll = _.restParam(function(obj, keys) { + _.bindAll = restParam(function(obj, keys) { if (keys.length < 1) throw new Error('bindAll must be passed function names'); return _.each(keys, function(key) { obj[key] = _.bind(obj[key], obj); @@ -761,7 +761,7 @@ // Delays a function for the given number of milliseconds, and then calls // it with the arguments supplied. - _.delay = _.restParam(function(func, wait, args) { + _.delay = restParam(function(func, wait, args) { return setTimeout(function(){ return func.apply(null, args); }, wait); @@ -896,6 +896,8 @@ // often you call it. Useful for lazy initialization. _.once = _.partial(_.before, 2); + _.restParam = restParam; + // Object Functions // ---------------- @@ -1506,7 +1508,7 @@ _.mixin = function(obj) { _.each(_.functions(obj), function(name) { var func = _[name] = obj[name]; - _.prototype[name] = _.restParam(function(args) { + _.prototype[name] = restParam(function(args) { args.unshift(this._wrapped); return result(this, func.apply(_, args)); }); From d9a6f915714be5cbab0ad8cd9a6dcf700bcdf1a8 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Thu, 2 Apr 2015 17:35:28 -0400 Subject: [PATCH 011/263] Use Math.max to simplify --- underscore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/underscore.js b/underscore.js index 3003c9429..7dee1f5f6 100644 --- a/underscore.js +++ b/underscore.js @@ -98,7 +98,7 @@ var restParam = function(func, startIndex) { startIndex = startIndex == null ? func.length - 1 : +startIndex; return function() { - var length = arguments.length > startIndex ? arguments.length - startIndex : 0; + var length = Math.max(arguments.length - startIndex, 0); var rest = Array(length); for (var index = 0; index < length; index++) { rest[index] = arguments[index + startIndex]; From 4afa5391155be985b022f286144a7b9790e7e3dc Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Thu, 2 Apr 2015 17:35:38 -0400 Subject: [PATCH 012/263] Test _.restParam --- test/functions.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/functions.js b/test/functions.js index 108b44613..92be24faa 100644 --- a/test/functions.js +++ b/test/functions.js @@ -588,4 +588,32 @@ }); + test('restParam', 10, function() { + _.restParam(function(a, args) { + strictEqual(a, 1); + deepEqual(args, [2, 3], 'collects rest arguments into an array'); + })(1, 2, 3); + + _.restParam(function(a, args) { + strictEqual(a, undefined); + deepEqual(args, [], 'passes empty array if there are not enough arguments'); + })(); + + _.restParam(function(a, b, c, args) { + strictEqual(arguments.length, 4); + deepEqual(args, [4, 5], 'works on functions with many named parameters'); + })(1, 2, 3, 4, 5); + + var obj = {}; + _.restParam(function() { + strictEqual(this, obj, 'invokes function with this context'); + }).call(obj); + + _.restParam(function(array, iteratee, context) { + deepEqual(array, [1, 2, 3, 4], 'startIndex can be used manually specify index of rest parameter'); + strictEqual(iteratee, undefined); + strictEqual(context, undefined); + }, 0)(1, 2, 3, 4); + }); + }()); From 3caa2b453fa48bf9448f39fcbb2410e0bf368f90 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Thu, 9 Apr 2015 11:46:03 -0400 Subject: [PATCH 013/263] Rename to restArgs --- test/functions.js | 12 ++++++------ underscore.js | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/test/functions.js b/test/functions.js index 92be24faa..e54ae945a 100644 --- a/test/functions.js +++ b/test/functions.js @@ -588,28 +588,28 @@ }); - test('restParam', 10, function() { - _.restParam(function(a, args) { + test('restArgs', 10, function() { + _.restArgs(function(a, args) { strictEqual(a, 1); deepEqual(args, [2, 3], 'collects rest arguments into an array'); })(1, 2, 3); - _.restParam(function(a, args) { + _.restArgs(function(a, args) { strictEqual(a, undefined); deepEqual(args, [], 'passes empty array if there are not enough arguments'); })(); - _.restParam(function(a, b, c, args) { + _.restArgs(function(a, b, c, args) { strictEqual(arguments.length, 4); deepEqual(args, [4, 5], 'works on functions with many named parameters'); })(1, 2, 3, 4, 5); var obj = {}; - _.restParam(function() { + _.restArgs(function() { strictEqual(this, obj, 'invokes function with this context'); }).call(obj); - _.restParam(function(array, iteratee, context) { + _.restArgs(function(array, iteratee, context) { deepEqual(array, [1, 2, 3, 4], 'startIndex can be used manually specify index of rest parameter'); strictEqual(iteratee, undefined); strictEqual(context, undefined); diff --git a/underscore.js b/underscore.js index 7dee1f5f6..7ecb85825 100644 --- a/underscore.js +++ b/underscore.js @@ -93,9 +93,9 @@ return cb(value, context, Infinity); }; - // Similar to ES6's rest params (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html) + // Similar to ES6's rest param (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html) // This accumulates the arguments passed into an array, after a given index. - var restParam = function(func, startIndex) { + var restArgs = function(func, startIndex) { startIndex = startIndex == null ? func.length - 1 : +startIndex; return function() { var length = Math.max(arguments.length - startIndex, 0); @@ -274,7 +274,7 @@ }; // Invoke a method (with arguments) on every item in a collection. - _.invoke = restParam(function(obj, method, args) { + _.invoke = restArgs(function(obj, method, args) { var isFunc = _.isFunction(method); return _.map(obj, function(value) { var func = isFunc ? method : value[method]; @@ -511,7 +511,7 @@ }; // Return a version of the array that does not contain the specified value(s). - _.without = restParam(function(array, otherArrays) { + _.without = restArgs(function(array, otherArrays) { return _.difference(array, otherArrays); }); @@ -711,7 +711,7 @@ if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function'); var args = slice.call(arguments, 2); - var bound = restParam(function(callArgs) { + var bound = restArgs(function(callArgs) { return executeBound(func, bound, context, this, args.concat(callArgs)); }); return bound; @@ -721,7 +721,7 @@ // arguments pre-filled, without changing its dynamic `this` context. _ acts // as a placeholder by default, allowing any combination of arguments to be // pre-filled. Set `_.partial.placeholder` for a custom placeholder argument. - _.partial = restParam(function(func, boundArgs) { + _.partial = restArgs(function(func, boundArgs) { var placeholder = _.partial.placeholder; var bound = function() { var position = 0, length = boundArgs.length; @@ -740,7 +740,7 @@ // Bind a number of an object's methods to that object. Remaining arguments // are the method names to be bound. Useful for ensuring that all callbacks // defined on an object belong to it. - _.bindAll = restParam(function(obj, keys) { + _.bindAll = restArgs(function(obj, keys) { if (keys.length < 1) throw new Error('bindAll must be passed function names'); return _.each(keys, function(key) { obj[key] = _.bind(obj[key], obj); @@ -761,7 +761,7 @@ // Delays a function for the given number of milliseconds, and then calls // it with the arguments supplied. - _.delay = restParam(function(func, wait, args) { + _.delay = restArgs(function(func, wait, args) { return setTimeout(function(){ return func.apply(null, args); }, wait); @@ -896,7 +896,7 @@ // often you call it. Useful for lazy initialization. _.once = _.partial(_.before, 2); - _.restParam = restParam; + _.restArgs = restArgs; // Object Functions // ---------------- @@ -1508,7 +1508,7 @@ _.mixin = function(obj) { _.each(_.functions(obj), function(name) { var func = _[name] = obj[name]; - _.prototype[name] = restParam(function(args) { + _.prototype[name] = restArgs(function(args) { args.unshift(this._wrapped); return result(this, func.apply(_, args)); }); From 1417938fbb12c068375645f0d10ba0b7bc86276b Mon Sep 17 00:00:00 2001 From: Carl Xiong Date: Wed, 22 Apr 2015 17:06:28 +0800 Subject: [PATCH 014/263] Fix array typo in home page _.unzip accepts a single array of arrays. --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 977413db1..cfb866ad4 100644 --- a/index.html +++ b/index.html @@ -975,7 +975,7 @@

Array Functions

and so on. Use with apply to pass in an array of arrays.

-_.unzip(["moe", 30, true], ["larry", 40, false], ["curly", 50, false]);
+_.unzip([["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]);
 => [['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]]
 
From ed828f43f49ee96784cf3f1ef4d7bf9b0e499971 Mon Sep 17 00:00:00 2001 From: Carl Xiong Date: Thu, 23 Apr 2015 18:32:15 +0800 Subject: [PATCH 015/263] Fix documents for zip and unzip. --- index.html | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/index.html b/index.html index cfb866ad4..78eedd16a 100644 --- a/index.html +++ b/index.html @@ -957,8 +957,9 @@

Array Functions

Merges together the values of each of the arrays with the values at the corresponding position. Useful when you have separate data sources that are coordinated through matching array indexes. - If you're working with a matrix of nested arrays, _.zip.apply - can transpose the matrix in a similar fashion. + Use with apply to pass in an array of arrays. + If you're working with a matrix of nested arrays, this can be used to + transpose the matrix.

 _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
@@ -967,12 +968,12 @@ 

Array Functions

- unzip_.unzip(*arrays) + unzip_.unzip(array)
- The opposite of zip. Given a number of arrays, returns a + The opposite of zip. Given an array of arrays, returns a series of new arrays, the first of which contains all of the first elements in the input arrays, the second of which contains all of the second elements, - and so on. Use with apply to pass in an array of arrays. + and so on.

 _.unzip([["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]);

From b10b2e6d72aebbff7961c65a9b9b1838812f9c73 Mon Sep 17 00:00:00 2001
From: Justin Ridgewell 
Date: Mon, 27 Apr 2015 13:01:39 -0400
Subject: [PATCH 016/263] Cleanup _.mapObject

---
 underscore.js | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/underscore.js b/underscore.js
index 7ecb85825..f54cffa47 100644
--- a/underscore.js
+++ b/underscore.js
@@ -961,14 +961,13 @@
   _.mapObject = function(obj, iteratee, context) {
     iteratee = cb(iteratee, context);
     var keys =  _.keys(obj),
-          length = keys.length,
-          results = {},
-          currentKey;
-      for (var index = 0; index < length; index++) {
-        currentKey = keys[index];
-        results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
-      }
-      return results;
+      length = keys.length,
+      results = {};
+    for (var index = 0; index < length; index++) {
+      var currentKey = keys[index];
+      results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
+    }
+    return results;
   };
 
   // Convert an object into a list of `[key, value]` pairs.

From 9244d619ccfe277c98600efcf5aaa2f8026bb53e Mon Sep 17 00:00:00 2001
From: Justin Ridgewell 
Date: Tue, 28 Apr 2015 10:22:32 -0400
Subject: [PATCH 017/263] Merge pull request #2161 from
 smelnikov/result-doc-fix

described correct behavior of _.result in docs
---
 index.html | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/index.html b/index.html
index 78eedd16a..cb64aa297 100644
--- a/index.html
+++ b/index.html
@@ -1931,8 +1931,8 @@ 

Utility Functions


If the value of the named property is a function then invoke it with the object as context; otherwise, return it. If a default value - is provided and the property doesn't exist than the default will be returned. - If defaultValue is a function its result will be returned. + is provided and the property doesn't exist or is undefined than the default + will be returned. If defaultValue is a function its result will be returned.

 var object = {cheese: 'crumpets', stuff: function(){ return 'nonsense'; }};

From 4c534babce217abb7e13bd47efcb399e47ef9cca Mon Sep 17 00:00:00 2001
From: Justin Ridgewell 
Date: Tue, 28 Apr 2015 10:25:24 -0400
Subject: [PATCH 018/263] Fix grammar

---
 index.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.html b/index.html
index cb64aa297..952b9e285 100644
--- a/index.html
+++ b/index.html
@@ -1931,7 +1931,7 @@ 

Utility Functions


If the value of the named property is a function then invoke it with the object as context; otherwise, return it. If a default value - is provided and the property doesn't exist or is undefined than the default + is provided and the property doesn't exist or is undefined then the default will be returned. If defaultValue is a function its result will be returned.


From 42b763e42873f630d1d0facd285ca7e9ad985110 Mon Sep 17 00:00:00 2001
From: Marek 
Date: Tue, 21 Apr 2015 10:53:08 +0100
Subject: [PATCH 019/263] Make _.sample more efficient by exiting early, make 
 _.shuffle call through to _.sample

* Improves efficiency of _.sample for when n is significantly smaller than obj.length
* Improves tests to make sure _.sample and _.shuffle actually change the order
* Replace shuffle with call to _.sample to avoid duplicated logic
---
 test/.eslintrc      |  1 +
 test/collections.js | 13 +++++++++----
 underscore.js       | 29 ++++++++++++++++-------------
 3 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/test/.eslintrc b/test/.eslintrc
index c48aeed6d..052484beb 100644
--- a/test/.eslintrc
+++ b/test/.eslintrc
@@ -12,6 +12,7 @@
     "strictEqual": false,
     "notStrictEqual": false,
     "notEqual": false,
+    "notDeepEqual": false,
     "throws": false,
     "asyncTest": false,
     "start": false,
diff --git a/test/collections.js b/test/collections.js
index c95fb0e22..3b94d21a4 100644
--- a/test/collections.js
+++ b/test/collections.js
@@ -737,12 +737,12 @@
   });
 
   test('shuffle', function() {
-    var numbers = _.range(10);
+    deepEqual(_.shuffle([1]), [1], 'behaves correctly on size 1 arrays');
+    var numbers = _.range(20);
     var shuffled = _.shuffle(numbers);
+    notDeepEqual(numbers, shuffled, 'does change the order'); // Chance of false negative: 1 in ~2.4*10^18
     notStrictEqual(numbers, shuffled, 'original object is unmodified');
-    ok(_.every(_.range(10), function() { //appears consistent?
-      return _.every(numbers, _.partial(_.contains, numbers));
-    }), 'contains the same members before and after shuffle');
+    deepEqual(numbers, _.sortBy(shuffled), 'contains the same members before and after shuffle');
 
     shuffled = _.shuffle({a: 1, b: 2, c: 3, d: 4});
     equal(shuffled.length, 4);
@@ -750,6 +750,8 @@
   });
 
   test('sample', function() {
+    strictEqual(_.sample([1]), 1, 'behaves correctly when no second parameter is given');
+    deepEqual(_.sample([1, 2, 3], -2), [], 'behaves correctly on negative n');
     var numbers = _.range(10);
     var allSampled = _.sample(numbers, 10).sort();
     deepEqual(allSampled, numbers, 'contains the same members before and after sample');
@@ -761,6 +763,9 @@
     notStrictEqual(_.sample([1, 2, 3], 0), [], 'sampling an array with 0 picks returns an empty array');
     deepEqual(_.sample([1, 2], -1), [], 'sampling a negative number of picks returns an empty array');
     ok(_.contains([1, 2, 3], _.sample({a: 1, b: 2, c: 3})), 'sample one value from an object');
+    var partialSample = _.sample(_.range(1000), 10);
+    var partialSampleSorted = partialSample.sort();
+    notDeepEqual(partialSampleSorted, _.range(10), 'samples from the whole array, not just the beginning');
   });
 
   test('toArray', function() {
diff --git a/underscore.js b/underscore.js
index 7ecb85825..e21f76460 100644
--- a/underscore.js
+++ b/underscore.js
@@ -349,21 +349,13 @@
     return result;
   };
 
-  // Shuffle a collection, using the modern version of the
-  // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
+  // Shuffle a collection.
   _.shuffle = function(obj) {
-    var set = isArrayLike(obj) ? obj : _.values(obj);
-    var length = set.length;
-    var shuffled = Array(length);
-    for (var index = 0, rand; index < length; index++) {
-      rand = _.random(0, index);
-      if (rand !== index) shuffled[index] = shuffled[rand];
-      shuffled[rand] = set[index];
-    }
-    return shuffled;
+    return _.sample(obj, Infinity);
   };
 
-  // Sample **n** random values from a collection.
+  // Sample **n** random values from a collection using the modern version of the
+  // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
   // If **n** is not specified, returns a single random element.
   // The internal `guard` argument allows it to work with `map`.
   _.sample = function(obj, n, guard) {
@@ -371,7 +363,18 @@
       if (!isArrayLike(obj)) obj = _.values(obj);
       return obj[_.random(obj.length - 1)];
     }
-    return _.shuffle(obj).slice(0, Math.max(0, n));
+
+    var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj);
+    var length = getLength(sample);
+    n = Math.max(Math.min(n, length), 0);
+    var rand, temp;
+    for (var index = length - 1; index > length - n - 1; index--) {
+      rand = _.random(0, index);
+      temp = sample[index];
+      sample[index] = sample[rand];
+      sample[rand] = temp;
+    }
+    return sample.slice(length - n);
   };
 
   // Sort the object's values by a criterion produced by an iteratee.

From 770bb76be323acbd829c519e6fc92922c1872e2c Mon Sep 17 00:00:00 2001
From: Andres Suarez 
Date: Thu, 7 May 2015 18:04:44 -0400
Subject: [PATCH 020/263] Reword comment to not use "require"

---
 underscore.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/underscore.js b/underscore.js
index f54cffa47..038e2561d 100644
--- a/underscore.js
+++ b/underscore.js
@@ -42,7 +42,7 @@
   };
 
   // Export the Underscore object for **Node.js**, with
-  // backwards-compatibility for the old `require()` API. If we're in
+  // backwards-compatibility for their old module API. If we're in
   // the browser, add `_` as a global object.
   if (typeof exports !== 'undefined') {
     if (typeof module !== 'undefined' && module.exports) {

From 450c21df094896703052556c715018f0501019ee Mon Sep 17 00:00:00 2001
From: Justin Ridgewell 
Date: Fri, 8 May 2015 11:17:50 -0400
Subject: [PATCH 021/263] _.restArgs cleanup

Two points:
- Use restArgs to cleanup `_.zip` method
- Undo a performance regression @akre54 [mentioned](https://github.com/jashkenas/underscore/pull/2140/files#r28144086)
---
 underscore.js | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/underscore.js b/underscore.js
index 038e2561d..adec86a10 100644
--- a/underscore.js
+++ b/underscore.js
@@ -19,6 +19,7 @@
 
   // Create quick reference variables for speed access to core prototypes.
   var
+    push             = ArrayProto.push,
     slice            = ArrayProto.slice,
     toString         = ObjProto.toString,
     hasOwnProperty   = ObjProto.hasOwnProperty;
@@ -576,12 +577,6 @@
     });
   };
 
-  // Zip together multiple lists into a single array -- elements that share
-  // an index go together.
-  _.zip = function() {
-    return _.unzip(arguments);
-  };
-
   // Complement of _.zip. Unzip accepts an array of arrays and groups
   // each array's elements on shared indices
   _.unzip = function(array) {
@@ -594,6 +589,10 @@
     return result;
   };
 
+  // Zip together multiple lists into a single array -- elements that share
+  // an index go together.
+  _.zip = restArgs(_.unzip);
+
   // Converts lists into objects. Pass either a single array of `[key, value]`
   // pairs, or two parallel arrays of the same length -- one of keys, and one of
   // the corresponding values.
@@ -1507,10 +1506,11 @@
   _.mixin = function(obj) {
     _.each(_.functions(obj), function(name) {
       var func = _[name] = obj[name];
-      _.prototype[name] = restArgs(function(args) {
-        args.unshift(this._wrapped);
+      _.prototype[name] = function() {
+        var args = [this._wrapped];
+        push.apply(args, arguments);
         return result(this, func.apply(_, args));
-      });
+      };
     });
   };
 

From a7f4623dc280cbb252cc892671b0a34228ac904f Mon Sep 17 00:00:00 2001
From: Justin Ridgewell 
Date: Tue, 28 Apr 2015 00:44:35 -0400
Subject: [PATCH 022/263] Simplify reduce wrapper to just determine initial

Well, and optimize `iteratee`, cause we don't really need context
anywhere else.
---
 underscore.js | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/underscore.js b/underscore.js
index 038e2561d..c427429ae 100644
--- a/underscore.js
+++ b/underscore.js
@@ -183,7 +183,14 @@
   var createReduce = function(dir) {
     // Optimized iterator function as using arguments.length
     // in the main function will deoptimize the, see #1991.
-    var iterator = function(obj, iteratee, memo, keys, index, length) {
+    var reducer = function(obj, iteratee, memo, initial) {
+      var keys = !isArrayLike(obj) && _.keys(obj),
+          length = (keys || obj).length,
+          index = dir > 0 ? 0 : length - 1;
+      if (!initial) {
+        memo = obj[keys ? keys[index] : index];
+        index += dir;
+      }
       for (; index >= 0 && index < length; index += dir) {
         var currentKey = keys ? keys[index] : index;
         memo = iteratee(memo, obj[currentKey], currentKey, obj);
@@ -192,16 +199,8 @@
     };
 
     return function(obj, iteratee, memo, context) {
-      iteratee = optimizeCb(iteratee, context, 4);
-      var keys = !isArrayLike(obj) && _.keys(obj),
-          length = (keys || obj).length,
-          index = dir > 0 ? 0 : length - 1;
-      // Determine the initial value if none is provided.
-      if (arguments.length < 3) {
-        memo = obj[keys ? keys[index] : index];
-        index += dir;
-      }
-      return iterator(obj, iteratee, memo, keys, index, length);
+      var initial = arguments.length >= 3;
+      return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
     };
   };
 

From 68bb747a1349b215752fdf8b1b2ad1667f1f8a0d Mon Sep 17 00:00:00 2001
From: Graeme Yeates 
Date: Tue, 14 Apr 2015 12:00:04 -0400
Subject: [PATCH 023/263] Detect global in strict mode and WebWorkers; fixes
 #2152

---
 underscore.js | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/underscore.js b/underscore.js
index 7ecb85825..2a03f3012 100644
--- a/underscore.js
+++ b/underscore.js
@@ -8,8 +8,10 @@
   // Baseline setup
   // --------------
 
-  // Establish the root object, `window` in the browser, or `exports` on the server.
-  var root = this;
+  // Establish the root object, `window` (`self`) in the browser, or `global` on the server.
+  // We use `self` instead of `window` for `WebWorker` support.
+  var root = (typeof self == 'object' && self.self == self && self) ||
+            (typeof global == 'object' && global.global == global && global);
 
   // Save the previous value of the `_` variable.
   var previousUnderscore = root._;
@@ -1562,4 +1564,4 @@
       return _;
     });
   }
-}.call(this));
+}());

From 481bff7ac433ef15bc63c93dad66a5787525fb3a Mon Sep 17 00:00:00 2001
From: Justin Ridgewell 
Date: Fri, 15 May 2015 01:26:37 -0400
Subject: [PATCH 024/263] Use restArgs in flattening functions

---
 underscore.js | 32 +++++++++++++++++---------------
 1 file changed, 17 insertions(+), 15 deletions(-)

diff --git a/underscore.js b/underscore.js
index 1efa346b5..a4d4b84a3 100644
--- a/underscore.js
+++ b/underscore.js
@@ -549,9 +549,9 @@
 
   // Produce an array that contains the union: each distinct element from all of
   // the passed-in arrays.
-  _.union = function() {
-    return _.uniq(flatten(arguments, true, true));
-  };
+  _.union = restArgs(function(arrays) {
+    return _.uniq(flatten(arrays, true, true));
+  });
 
   // Produce an array that contains every item shared between all the
   // passed-in arrays.
@@ -571,12 +571,12 @@
 
   // Take the difference between one array and a number of other arrays.
   // Only the elements present in just the first array will remain.
-  _.difference = function(array) {
-    var rest = flatten(arguments, true, true, 1);
+  _.difference = restArgs(function(array, rest) {
+    rest = flatten(rest, true, true);
     return _.filter(array, function(value){
       return !_.contains(rest, value);
     });
-  };
+  });
 
   // Complement of _.zip. Unzip accepts an array of arrays and groups
   // each array's elements on shared indices
@@ -1037,15 +1037,15 @@
   };
 
   // Return a copy of the object only containing the whitelisted properties.
-  _.pick = function(object, oiteratee, context) {
-    var result = {}, obj = object, iteratee, keys;
+  _.pick = restArgs(function(obj, keys) {
+    var result = {}, iteratee = keys[0];
     if (obj == null) return result;
-    if (_.isFunction(oiteratee)) {
+    if (_.isFunction(iteratee)) {
+      if (keys.length > 1) iteratee = optimizeCb(iteratee, keys[1]);
       keys = _.allKeys(obj);
-      iteratee = optimizeCb(oiteratee, context);
     } else {
-      keys = flatten(arguments, false, false, 1);
       iteratee = function(value, key, obj) { return key in obj; };
+      keys = flatten(keys, false, false);
       obj = Object(obj);
     }
     for (var i = 0, length = keys.length; i < length; i++) {
@@ -1054,20 +1054,22 @@
       if (iteratee(value, key, obj)) result[key] = value;
     }
     return result;
-  };
+  });
 
    // Return a copy of the object without the blacklisted properties.
-  _.omit = function(obj, iteratee, context) {
+  _.omit = restArgs(function(obj, keys) {
+    var iteratee = keys[0], context;
     if (_.isFunction(iteratee)) {
       iteratee = _.negate(iteratee);
+      if (keys.length > 1) context = keys[1];
     } else {
-      var keys = _.map(flatten(arguments, false, false, 1), String);
+      keys = _.map(flatten(keys, false, false), String);
       iteratee = function(value, key) {
         return !_.contains(keys, key);
       };
     }
     return _.pick(obj, iteratee, context);
-  };
+  });
 
   // Fill in a given object with default properties.
   _.defaults = createAssigner(_.allKeys, true);

From 8a7e8e68bdf98d32a6fcfe2ce8e0daf566d03c07 Mon Sep 17 00:00:00 2001
From: Justin Ridgewell 
Date: Fri, 15 May 2015 01:31:43 -0400
Subject: [PATCH 025/263] Remove unused param `startIndex` from flatten()

---
 underscore.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/underscore.js b/underscore.js
index a4d4b84a3..4ef811af2 100644
--- a/underscore.js
+++ b/underscore.js
@@ -488,9 +488,9 @@
   };
 
   // Internal implementation of a recursive `flatten` function.
-  var flatten = function(input, shallow, strict, startIndex) {
+  var flatten = function(input, shallow, strict) {
     var output = [], idx = 0;
-    for (var i = startIndex || 0, length = getLength(input); i < length; i++) {
+    for (var i = 0, length = getLength(input); i < length; i++) {
       var value = input[i];
       if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
         //flatten current level of array or arguments object

From 99fd23e9a35d64a0307352573793d8dd91e874fc Mon Sep 17 00:00:00 2001
From: Adam Krebs 
Date: Fri, 15 May 2015 10:57:19 -0400
Subject: [PATCH 026/263] document _.matches as alias of _.matcher

---
 index.html | 23 ++++++++++++-----------
 1 file changed, 12 insertions(+), 11 deletions(-)

diff --git a/index.html b/index.html
index 952b9e285..f0624544c 100644
--- a/index.html
+++ b/index.html
@@ -623,7 +623,7 @@ 

Collection Functions (Arrays or Objects)

Alias: includes
Returns true if the value is present in the list. - Uses indexOf internally, if list is an Array. + Uses indexOf internally, if list is an Array. Use fromIndex to start your search at a given index.

@@ -1409,7 +1409,7 @@ 

Object Functions

create_.create(prototype, props)
- Creates a new object with the given prototype, optionally attaching + Creates a new object with the given prototype, optionally attaching props as own properties. Basically, Object.create, but without all of the property descriptor jazz.

@@ -1454,7 +1454,7 @@

Object Functions

extendOwn_.extendOwn(destination, *sources) Alias: assign
- Like extend, but only copies own properties over to the + Like extend, but only copies own properties over to the destination object.

@@ -1505,7 +1505,7 @@

Object Functions

clone_.clone(object)
- Create a shallow-copied clone of the provided plain object. + Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.

@@ -1567,6 +1567,7 @@ 

Object Functions

matcher_.matcher(attrs) + Alias: matches
Returns a predicate function that will tell you if a passed in object contains all of the key/value properties present in attrs. @@ -2210,7 +2211,7 @@

Change Log

1.8.3April 2, 2015DiffDocs
  • - Adds an _.create method, as a slimmed down version of + Adds an _.create method, as a slimmed down version of Object.create.
  • @@ -2224,7 +2225,7 @@

    Change Log

    1.8.2Feb. 22, 2015DiffDocs
    • - Restores the previous old-Internet-Explorer edge cases changed in + Restores the previous old-Internet-Explorer edge cases changed in 1.8.1.
    • @@ -2237,7 +2238,7 @@

      Change Log

      1.8.1Feb. 19, 2015DiffDocs
      • - Fixes/changes some old-Internet Explorer and related edge case + Fixes/changes some old-Internet Explorer and related edge case behavior. Test your app with Underscore 1.8.1 in an old IE and let us know how it's doing...
      • @@ -2256,7 +2257,7 @@

        Change Log

        names on an object.
      • - Reverted a 1.7.0 change where _.extend only copied "own" + Reverted a 1.7.0 change where _.extend only copied "own" properties. Hopefully this will un-break you — if it breaks you again, I apologize.
      • @@ -2270,7 +2271,7 @@

        Change Log

      • Added an _.isMatch predicate function that tells you if an - object matches key-value properties. A kissing cousin of + object matches key-value properties. A kissing cousin of _.isEqual and _.matcher.
      • @@ -2285,7 +2286,7 @@

        Change Log

        that provides the fallback value).
      • - Added the _.propertyOf function generator as a mirror-world + Added the _.propertyOf function generator as a mirror-world version of _.property.
      • @@ -2293,7 +2294,7 @@

        Change Log

        name — _.matcher.
      • - Various and diverse code simplifications, changes for improved + Various and diverse code simplifications, changes for improved cross-platform compatibility, and edge case bug fixes.
      From 4e2d94d63ae8074da234af2258c1fae5106996ae Mon Sep 17 00:00:00 2001 From: Graeme Yeates Date: Fri, 15 May 2015 15:52:12 -0400 Subject: [PATCH 027/263] Test tricky object comparisions --- test/objects.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/objects.js b/test/objects.js index 9b4577408..0bc7cce02 100644 --- a/test/objects.js +++ b/test/objects.js @@ -554,6 +554,13 @@ var other = {a: 1}; strictEqual(_.isEqual(new Foo, other), false, 'Objects from different constructors are not equal'); + + + // Tricky object cases val comparisions + equal(_.isEqual([0], [-0]), false); + equal(_.isEqual({a: 0}, {a: -0}), false); + equal(_.isEqual([NaN], [NaN]), true); + equal(_.isEqual({a: NaN}, {a: NaN}), true); }); test('isEmpty', function() { From 20f1cc4750c1288a9784ed02c74b6f139c3da723 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Fri, 22 May 2015 14:22:20 -0400 Subject: [PATCH 028/263] style --- underscore.js | 1 - 1 file changed, 1 deletion(-) diff --git a/underscore.js b/underscore.js index 23bd1a408..5df26e053 100644 --- a/underscore.js +++ b/underscore.js @@ -365,7 +365,6 @@ if (!isArrayLike(obj)) obj = _.values(obj); return obj[_.random(obj.length - 1)]; } - var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj); var length = getLength(sample); n = Math.max(Math.min(n, length), 0); From 1673423ed988294e95df0af7da99928bf545590e Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sat, 16 May 2015 20:14:04 -0400 Subject: [PATCH 029/263] Optimize _.isEqual for primatives --- underscore.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/underscore.js b/underscore.js index 1efa346b5..2a0cc9c0e 100644 --- a/underscore.js +++ b/underscore.js @@ -1115,6 +1115,15 @@ if (a === b) return a !== 0 || 1 / a === 1 / b; // A strict comparison is necessary because `null == undefined`. if (a == null || b == null) return a === b; + // `NaN`s are equivalent, but non-reflexive. + if (a !== a) return b !== b; + // Exhaust primitive checks + var type = typeof a; + if (type !== 'function' && type !== 'object' && typeof b !== 'object') return false; + return deepEq(a, b, aStack, bStack); + }; + + var deepEq = function(a, b, aStack, bStack) { // Unwrap any wrapped objects. if (a instanceof _) a = a._wrapped; if (b instanceof _) b = b._wrapped; From 3cd04870adff10304da1a5cbd19c9aa310f1ee2c Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Fri, 3 Apr 2015 11:22:25 -0400 Subject: [PATCH 030/263] Allow _.bindAll to take arrays Closes #1996, supercedes #2005. --- test/functions.js | 4 ++++ underscore.js | 9 ++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/test/functions.js b/test/functions.js index e54ae945a..67d22d414 100644 --- a/test/functions.js +++ b/test/functions.js @@ -119,6 +119,10 @@ var sayLast = moe.sayLast; equal(sayLast(1, 2, 3, 4, 5, 6, 7, 'Tom'), 'hi: moe', 'createCallback works with any number of arguments'); + + _.bindAll(moe, ['getName']); + var getName = moe.getName; + equal(getName(), 'name: moe', 'flattens arguments into a single list'); }); test('memoize', function() { diff --git a/underscore.js b/underscore.js index 037b7e0b2..5168ba1c4 100644 --- a/underscore.js +++ b/underscore.js @@ -743,10 +743,13 @@ // are the method names to be bound. Useful for ensuring that all callbacks // defined on an object belong to it. _.bindAll = restArgs(function(obj, keys) { - if (keys.length < 1) throw new Error('bindAll must be passed function names'); - return _.each(keys, function(key) { + keys = flatten(keys, false, false); + var index = keys.length; + if (index < 1) throw new Error('bindAll must be passed function names'); + while (index--) { + var key = keys[index]; obj[key] = _.bind(obj[key], obj); - }); + }; }); // Memoize an expensive function by storing its results. From d55724239a87bf0b76d29810b43742595ba81dc3 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sun, 24 May 2015 15:22:39 -0400 Subject: [PATCH 031/263] Fix eslint errors --- underscore.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/underscore.js b/underscore.js index 5168ba1c4..5d9e52bf2 100644 --- a/underscore.js +++ b/underscore.js @@ -10,8 +10,8 @@ // Establish the root object, `window` (`self`) in the browser, or `global` on the server. // We use `self` instead of `window` for `WebWorker` support. - var root = (typeof self == 'object' && self.self == self && self) || - (typeof global == 'object' && global.global == global && global); + var root = (typeof self === 'object' && self.self === self && self) || + (typeof global === 'object' && global.global === global && global); // Save the previous value of the `_` variable. var previousUnderscore = root._; @@ -749,7 +749,7 @@ while (index--) { var key = keys[index]; obj[key] = _.bind(obj[key], obj); - }; + } }); // Memoize an expensive function by storing its results. @@ -1041,6 +1041,11 @@ } }; + // Internal pick helper function to determine if `obj` has key `key. + var keyInObj = function(value, key, obj) { + return key in obj; + }; + // Return a copy of the object only containing the whitelisted properties. _.pick = restArgs(function(obj, keys) { var result = {}, iteratee = keys[0]; @@ -1049,7 +1054,7 @@ if (keys.length > 1) iteratee = optimizeCb(iteratee, keys[1]); keys = _.allKeys(obj); } else { - iteratee = function(value, key, obj) { return key in obj; }; + iteratee = keyInObj; keys = flatten(keys, false, false); obj = Object(obj); } @@ -1116,7 +1121,8 @@ // Internal recursive comparison function for `isEqual`. - var eq = function(a, b, aStack, bStack) { + var eq, deepEq; + eq = function(a, b, aStack, bStack) { // Identical objects are equal. `0 === -0`, but they aren't identical. // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). if (a === b) return a !== 0 || 1 / a === 1 / b; @@ -1130,7 +1136,8 @@ return deepEq(a, b, aStack, bStack); }; - var deepEq = function(a, b, aStack, bStack) { + // Internal recursive comparison function for `isEqual`. + deepEq = function(a, b, aStack, bStack) { // Unwrap any wrapped objects. if (a instanceof _) a = a._wrapped; if (b instanceof _) b = b._wrapped; From a848bb5461b11fdbc89123c0b6c7d36583e3b5b4 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sun, 24 May 2015 19:04:48 -0400 Subject: [PATCH 032/263] Sample from left to right --- underscore.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/underscore.js b/underscore.js index 5d9e52bf2..dffee52f8 100644 --- a/underscore.js +++ b/underscore.js @@ -368,14 +368,14 @@ var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj); var length = getLength(sample); n = Math.max(Math.min(n, length), 0); - var rand, temp; - for (var index = length - 1; index > length - n - 1; index--) { - rand = _.random(0, index); - temp = sample[index]; + var last = length - 1; + for (var index = 0; index < n; index++) { + var rand = _.random(index, last); + var temp = sample[index]; sample[index] = sample[rand]; sample[rand] = temp; } - return sample.slice(length - n); + return sample.slice(0, n); }; // Sort the object's values by a criterion produced by an iteratee. From 669330f654f38ffeae9662facb7076f8b15bf259 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sun, 24 May 2015 23:36:59 -0400 Subject: [PATCH 033/263] Fix comment --- underscore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/underscore.js b/underscore.js index 5d9e52bf2..e7b382dbf 100644 --- a/underscore.js +++ b/underscore.js @@ -1041,7 +1041,7 @@ } }; - // Internal pick helper function to determine if `obj` has key `key. + // Internal pick helper function to determine if `obj` has key `key`. var keyInObj = function(value, key, obj) { return key in obj; }; From 0e326fdd1ca5e7981955af17dc65b09968648e2b Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Tue, 26 May 2015 02:15:30 -0700 Subject: [PATCH 034/263] upgrade eslint version and fix all current warnings/errors --- .eslintrc | 76 ++++++++++-- package.json | 4 +- test/arrays.js | 42 +++---- test/chaining.js | 3 +- test/collections.js | 124 ++++++++++---------- test/cross-document.js | 2 +- test/functions.js | 62 +++++----- test/objects.js | 260 ++++++++++++++++++++--------------------- test/utility.js | 93 +++++++-------- underscore.js | 85 +++++++------- 10 files changed, 406 insertions(+), 345 deletions(-) diff --git a/.eslintrc b/.eslintrc index 2c46d63de..e48ae1426 100644 --- a/.eslintrc +++ b/.eslintrc @@ -6,30 +6,86 @@ }, "rules": { + "block-scoped-var": 1, "brace-style": [1, "1tbs"], - "curly": [0, "multi"], - "eqeqeq": [1, "smart"], + "camelcase": 2, + "comma-dangle": [2, "never"], + "comma-spacing": 2, + "consistent-return": 1, + "dot-notation": [2, { "allowKeywords": false }], + "eol-last": 2, + "eqeqeq": [2, "smart"], + "indent": [2, 2, {"indentSwitchCase": true}], + "key-spacing": [1, { "align": "colon" }], + "linebreak-style": 2, "max-depth": [1, 4], "max-params": [1, 5], "new-cap": 2, - "new-parens": 0, - "no-constant-condition": 0, + "no-alert": 2, + "no-caller": 2, + "no-catch-shadow": 2, + "no-console": 2, + "no-debugger": 2, + "no-delete-var": 2, "no-div-regex": 1, + "no-dupe-args": 2, + "no-dupe-keys": 2, + "no-duplicate-case": 2, "no-else-return": 1, + "no-empty-class": 2, + "no-empty-label": 2, + "no-eval": 2, + "no-ex-assign": 2, + "no-extend-native": 2, + "no-extra-boolean-cast": 2, "no-extra-parens": 1, + "no-extra-semi": 2, + "no-fallthrough": 2, "no-floating-decimal": 2, + "no-func-assign": 2, + "no-implied-eval": 2, "no-inner-declarations": 2, - "no-lonely-if": 1, + "no-irregular-whitespace": 2, + "no-label-var": 2, + "no-lone-blocks": 2, + "no-lonely-if": 2, + "no-multi-spaces": 1, + "no-multi-str": 2, + "no-native-reassign": 2, + "no-negated-in-lhs": 1, "no-nested-ternary": 2, - "no-new-object": 0, - "no-new-func": 0, - "no-underscore-dangle": 0, + "no-new-object": 2, + "no-new-wrappers": 2, + "no-obj-calls": 2, + "no-octal": 2, + "no-octal-escape": 2, + "no-proto": 2, + "no-redeclare": 2, + "no-reserved-keys": 2, + "no-shadow": 2, + "no-spaced-func": 2, + "no-throw-literal": 2, + "no-trailing-spaces": 2, + "no-undef-init": 2, + "no-undefined": 2, + "no-unneeded-ternary": 2, + "no-unreachable": 2, + "no-unused-expressions": 2, + "no-unused-vars": 2, + "no-use-before-define": [2, "nofunc"], + "no-with": 2, + "quote-props": [1, "as-needed"], "quotes": [2, "single", "avoid-escape"], "radix": 2, + "semi": 2, "space-after-keywords": [2, "always"], + "space-before-function-paren": [2, {"anonymous": "never", "named": "never"}], "space-in-brackets": [2, "never"], - "space-unary-word-ops": 2, - "strict": 0, + "space-infix-ops": 2, + "space-return-throw-case": 2, + "space-unary-ops": [2, { "words": true, "nonwords": false }], + "use-isnan": 2, + "valid-typeof": 2, "wrap-iife": 2 } } diff --git a/package.json b/package.json index 0a9126bf0..f1ad720da 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "version": "1.8.3", "devDependencies": { "docco": "*", - "eslint": "0.6.x", + "eslint": "0.21.x", "karma": "~0.12.31", "karma-qunit": "~0.1.4", "qunit-cli": "~0.2.0", @@ -26,7 +26,7 @@ }, "scripts": { "test": "npm run test-node && npm run lint", - "lint": "eslint underscore.js test/*.js", + "lint": "eslint --reset underscore.js test/*.js", "test-node": "qunit-cli test/*.js", "test-browser": "npm i karma-phantomjs-launcher && ./node_modules/karma/bin/karma start", "build": "uglifyjs underscore.js -c \"evaluate=false\" --comments \"/ .*/\" -m --source-map underscore-min.map -o underscore-min.js", diff --git a/test/arrays.js b/test/arrays.js index 3f39ed90a..b8903d3ea 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -16,7 +16,7 @@ result = (function() { return _.first([1, 2, 3], 2); }()); deepEqual(result, [1, 2]); - equal(_.first(null), undefined, 'handles nulls'); + equal(_.first(null), void 0, 'handles nulls'); strictEqual(_.first([1, 2, 3], -1).length, 0); }); @@ -69,7 +69,7 @@ result = _.map([[1, 2, 3], [1, 2, 3]], _.last); deepEqual(result, [3, 3], 'works well with _.map'); - equal(_.last(null), undefined, 'handles nulls'); + equal(_.last(null), void 0, 'handles nulls'); strictEqual(_.last([1, 2, 3], -1).length, 0); }); @@ -106,8 +106,8 @@ var result = (function(){ return _.without(arguments, 0, 1); }(1, 2, 1, 0, 3, 1, 4)); deepEqual(result, [2, 3, 4], 'works on an arguments object'); - list = [{one : 1}, {two : 2}]; - equal(_.without(list, {one : 1}).length, 2, 'uses real object identity for comparisons.'); + list = [{one: 1}, {two: 2}]; + equal(_.without(list, {one: 1}).length, 2, 'uses real object identity for comparisons.'); equal(_.without(list, list[0]).length, 1, 'ditto.'); }); @@ -242,8 +242,8 @@ var names = ['moe', 'larry', 'curly'], ages = [30, 40, 50], leaders = [true]; deepEqual(_.zip(names, ages, leaders), [ ['moe', 30, true], - ['larry', 40, undefined], - ['curly', 50, undefined] + ['larry', 40, void 0], + ['curly', 50, void 0] ], 'zipped together arrays of different lengths'); var stooges = _.zip(['moe', 30, 'stooge 1'], ['larry', 40, 'stooge 2'], ['curly', 50, 'stooge 3']); @@ -252,7 +252,7 @@ // In the case of difference lengths of the tuples undefineds // should be used as placeholder stooges = _.zip(['moe', 30], ['larry', 40], ['curly', 50, 'extra data']); - deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], [undefined, undefined, 'extra data']], 'zipped pairs with empties'); + deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], [void 0, void 0, 'extra data']], 'zipped pairs with empties'); var empty = _.zip([]); deepEqual(empty, [], 'unzipped empty'); @@ -324,7 +324,7 @@ index = _.indexOf(numbers, 2, 5); equal(index, 7, 'supports the fromIndex argument'); - index = _.indexOf([,,,], undefined); + index = _.indexOf([,,, 0], void 0); equal(index, 0, 'treats sparse arrays as if they were dense'); var array = [1, 2, 3, 1, 2, 3]; @@ -336,7 +336,7 @@ }); strictEqual(_.indexOf([1, 2, 3], 1, true), 0); - index = _.indexOf([], undefined, true); + index = _.indexOf([], void 0, true); equal(index, -1, 'empty array with truthy `isSorted` returns -1'); }); @@ -361,7 +361,7 @@ test('lastIndexOf', function() { var numbers = [1, 0, 1]; - var falsey = [void 0, '', 0, false, NaN, null, undefined]; + var falsey = [void 0, '', 0, false, NaN, null, void 0]; equal(_.lastIndexOf(numbers, 1), 2); numbers = [1, 0, 1, 0, 0, 1, 0, 0, 0]; @@ -392,7 +392,7 @@ strictEqual(_.lastIndexOf(array, 1, 2), 0, 'should work with a positive `fromIndex`'); _.each([6, 8, Math.pow(2, 32), Infinity], function(fromIndex) { - strictEqual(_.lastIndexOf(array, undefined, fromIndex), -1); + strictEqual(_.lastIndexOf(array, void 0, fromIndex), -1); strictEqual(_.lastIndexOf(array, 1, fromIndex), 3); strictEqual(_.lastIndexOf(array, '', fromIndex), -1); }); @@ -439,10 +439,10 @@ test('findIndex', function() { var objects = [ - {'a': 0, 'b': 0}, - {'a': 1, 'b': 1}, - {'a': 2, 'b': 2}, - {'a': 0, 'b': 0} + {a: 0, b: 0}, + {a: 1, b: 1}, + {a: 2, b: 2}, + {a: 0, b: 0} ]; equal(_.findIndex(objects, function(obj) { @@ -470,7 +470,7 @@ }, objects); var sparse = []; - sparse[20] = {'a': 2, 'b': 2}; + sparse[20] = {a: 2, b: 2}; equal(_.findIndex(sparse, function(obj) { return obj && obj.b * obj.a === 4; }), 20, 'Works with sparse arrays'); @@ -482,10 +482,10 @@ test('findLastIndex', function() { var objects = [ - {'a': 0, 'b': 0}, - {'a': 1, 'b': 1}, - {'a': 2, 'b': 2}, - {'a': 0, 'b': 0} + {a: 0, b: 0}, + {a: 1, b: 1}, + {a: 2, b: 2}, + {a: 0, b: 0} ]; equal(_.findLastIndex(objects, function(obj) { @@ -513,7 +513,7 @@ }, objects); var sparse = []; - sparse[20] = {'a': 2, 'b': 2}; + sparse[20] = {a: 2, b: 2}; equal(_.findLastIndex(sparse, function(obj) { return obj && obj.b * obj.a === 4; }), 20, 'Works with sparse arrays'); diff --git a/test/chaining.js b/test/chaining.js index 34fd73c36..c5830f36f 100644 --- a/test/chaining.js +++ b/test/chaining.js @@ -17,7 +17,8 @@ hash[l] = hash[l] || 0; hash[l]++; return hash; - }, {}).value(); + }, {}) + .value(); equal(counts.a, 16, 'counted all the letters in the song'); equal(counts.e, 10, 'counted all the letters in the song'); }); diff --git a/test/collections.js b/test/collections.js index 3b94d21a4..2faccbafc 100644 --- a/test/collections.js +++ b/test/collections.js @@ -9,7 +9,7 @@ }); var answers = []; - _.each([1, 2, 3], function(num){ answers.push(num * this.multiplier);}, {multiplier : 5}); + _.each([1, 2, 3], function(num){ answers.push(num * this.multiplier); }, {multiplier: 5}); deepEqual(answers, [5, 10, 15], 'context object property accessed'); answers = []; @@ -17,7 +17,7 @@ deepEqual(answers, [1, 2, 3], 'aliased as "forEach"'); answers = []; - var obj = {one : 1, two : 2, three : 3}; + var obj = {one: 1, two: 2, three: 3}; obj.constructor.prototype.four = 4; _.each(obj, function(value, key){ answers.push(key); }); deepEqual(answers, ['one', 'two', 'three'], 'iterating over objects works, and ignores the object prototype.'); @@ -26,8 +26,8 @@ // ensure the each function is JITed _(1000).times(function() { _.each([], function(){}); }); var count = 0; - obj = {1 : 'foo', 2 : 'bar', 3 : 'baz'}; - _.each(obj, function(value, key){ count++; }); + obj = {1: 'foo', 2: 'bar', 3: 'baz'}; + _.each(obj, function(){ count++; }); equal(count, 3, 'the fun should be called only 3 times'); var answer = null; @@ -149,7 +149,7 @@ var doubled = _.map([1, 2, 3], function(num){ return num * 2; }); deepEqual(doubled, [2, 4, 6], 'doubled numbers'); - var tripled = _.map([1, 2, 3], function(num){ return num * this.multiplier; }, {multiplier : 3}); + var tripled = _.map([1, 2, 3], function(num){ return num * this.multiplier; }, {multiplier: 3}); deepEqual(tripled, [3, 6, 9], 'tripled numbers with context'); doubled = _([1, 2, 3]).map(function(num){ return num * 2; }); @@ -167,7 +167,7 @@ }, [5]), [1], 'called with context'); // Passing a property name like _.pluck. - var people = [{name : 'moe', age : 30}, {name : 'curly', age : 50}]; + var people = [{name: 'moe', age: 30}, {name: 'curly', age: 50}]; deepEqual(_.map(people, 'name'), ['moe', 'curly'], 'predicate string map to object properties'); }); @@ -176,29 +176,29 @@ }); test('reduce', function() { - var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; }, 0); + var sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0); equal(sum, 6, 'can sum up an array'); - var context = {multiplier : 3}; - sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num * this.multiplier; }, 0, context); + var context = {multiplier: 3}; + sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num * this.multiplier; }, 0, context); equal(sum, 18, 'can reduce with a context object'); - sum = _.inject([1, 2, 3], function(sum, num){ return sum + num; }, 0); + sum = _.inject([1, 2, 3], function(memo, num){ return memo + num; }, 0); equal(sum, 6, 'aliased as "inject"'); - sum = _([1, 2, 3]).reduce(function(sum, num){ return sum + num; }, 0); + sum = _([1, 2, 3]).reduce(function(memo, num){ return memo + num; }, 0); equal(sum, 6, 'OO-style reduce'); - sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; }); + sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }); equal(sum, 6, 'default initial value'); - var prod = _.reduce([1, 2, 3, 4], function(prod, num){ return prod * num; }); + var prod = _.reduce([1, 2, 3, 4], function(memo, num){ return memo * num; }); equal(prod, 24, 'can reduce via multiplication'); ok(_.reduce(null, _.noop, 138) === 138, 'handles a null (with initial value) properly'); - equal(_.reduce([], _.noop, undefined), undefined, 'undefined can be passed as a special case'); + equal(_.reduce([], _.noop, void 0), void 0, 'undefined can be passed as a special case'); equal(_.reduce([_], _.noop), _, 'collection of length one with no initial value returns the first item'); - equal(_.reduce([], _.noop), undefined, 'returns undefined when collection is empty and no initial value'); + equal(_.reduce([], _.noop), void 0, 'returns undefined when collection is empty and no initial value'); }); test('foldl', function() { @@ -212,45 +212,45 @@ list = _.reduceRight(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; }); equal(list, 'bazbarfoo', 'default initial value'); - var sum = _.reduceRight({a: 1, b: 2, c: 3}, function(sum, num){ return sum + num; }); + var sum = _.reduceRight({a: 1, b: 2, c: 3}, function(memo, num){ return memo + num; }); equal(sum, 6, 'default initial value on object'); ok(_.reduceRight(null, _.noop, 138) === 138, 'handles a null (with initial value) properly'); equal(_.reduceRight([_], _.noop), _, 'collection of length one with no initial value returns the first item'); - equal(_.reduceRight([], _.noop, undefined), undefined, 'undefined can be passed as a special case'); - equal(_.reduceRight([], _.noop), undefined, 'returns undefined when collection is empty and no initial value'); + equal(_.reduceRight([], _.noop, void 0), void 0, 'undefined can be passed as a special case'); + equal(_.reduceRight([], _.noop), void 0, 'returns undefined when collection is empty and no initial value'); // Assert that the correct arguments are being passed. var args, - memo = {}, + init = {}, object = {a: 1, b: 2}, lastKey = _.keys(object).pop(); var expected = lastKey === 'a' - ? [memo, 1, 'a', object] - : [memo, 2, 'b', object]; + ? [init, 1, 'a', object] + : [init, 2, 'b', object]; _.reduceRight(object, function() { if (!args) args = _.toArray(arguments); - }, memo); + }, init); deepEqual(args, expected); // And again, with numeric keys. - object = {'2': 'a', '1': 'b'}; + object = {2: 'a', 1: 'b'}; lastKey = _.keys(object).pop(); args = null; expected = lastKey === '2' - ? [memo, 'a', '2', object] - : [memo, 'b', '1', object]; + ? [init, 'a', '2', object] + : [init, 'b', '1', object]; _.reduceRight(object, function() { if (!args) args = _.toArray(arguments); - }, memo); + }, init); deepEqual(args, expected); }); @@ -290,9 +290,9 @@ return x.x === 4; }), {x: 4, z: 1}); - _.findIndex([{a: 1}], function(a, key, obj) { + _.findIndex([{a: 1}], function(a, key, o) { equal(key, 0); - deepEqual(obj, [{a: 1}]); + deepEqual(o, [{a: 1}]); strictEqual(this, _, 'called with context'); }, _); }); @@ -356,7 +356,7 @@ ok(!_.every([0, 11, 28], function(num){ return num % 2 === 0; }), 'an odd number'); ok(_.every([1], _.identity) === true, 'cast to boolean - true'); ok(_.every([0], _.identity) === false, 'cast to boolean - false'); - ok(!_.every([undefined, undefined, undefined], _.identity), 'works with arrays of undefined'); + ok(!_.every([void 0, void 0, void 0], _.identity), 'works with arrays of undefined'); var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; ok(!_.every(list, {a: 1, b: 2}), 'Can be called with object'); @@ -493,13 +493,13 @@ deepEqual(result[0], [1, 5, 7], 'first array sorted'); deepEqual(result[1], [1, 2, 3], 'second array sorted'); delete String.prototype.call; - equal(s.call, undefined, 'call function removed'); + equal(s.call, void 0, 'call function removed'); }); test('pluck', function() { var people = [{name: 'moe', age: 30}, {name: 'curly', age: 50}]; deepEqual(_.pluck(people, 'name'), ['moe', 'curly'], 'pulls names out of objects'); - deepEqual(_.pluck(people, 'address'), [undefined, undefined], 'missing properties are returned as undefined'); + deepEqual(_.pluck(people, 'address'), [void 0, void 0], 'missing properties are returned as undefined'); //compat: most flexible handling of edge cases deepEqual(_.pluck([{'[object Object]': 1}], {}), [1]); }); @@ -547,7 +547,7 @@ test('max', function() { equal(-Infinity, _.max(null), 'can handle null/undefined'); - equal(-Infinity, _.max(undefined), 'can handle null/undefined'); + equal(-Infinity, _.max(void 0), 'can handle null/undefined'); equal(-Infinity, _.max(null, _.identity), 'can handle null/undefined'); equal(3, _.max([1, 2, 3]), 'can perform a regular Math.max'); @@ -557,7 +557,7 @@ equal(-Infinity, _.max({}), 'Maximum value of an empty object'); equal(-Infinity, _.max([]), 'Maximum value of an empty array'); - equal(_.max({'a': 'a'}), -Infinity, 'Maximum value of a non-numeric collection'); + equal(_.max({a: 'a'}), -Infinity, 'Maximum value of a non-numeric collection'); equal(299999, _.max(_.range(1, 300000)), 'Maximum value of a too-big array'); @@ -569,16 +569,16 @@ var iterator = function(o){ return o.x; }; equal(_.max([a, b], iterator), a, 'Respects iterator return value of -Infinity'); - deepEqual(_.max([{'a': 1}, {'a': 0, 'b': 3}, {'a': 4}, {'a': 2}], 'a'), {'a': 4}, 'String keys use property iterator'); + deepEqual(_.max([{a: 1}, {a: 0, b: 3}, {a: 4}, {a: 2}], 'a'), {a: 4}, 'String keys use property iterator'); - deepEqual(_.max([0, 2], function(a){ return a * this.x; }, {x: 1}), 2, 'Iterator context'); + deepEqual(_.max([0, 2], function(c){ return c * this.x; }, {x: 1}), 2, 'Iterator context'); deepEqual(_.max([[1], [2, 3], [-1, 4], [5]], 0), [5], 'Lookup falsy iterator'); deepEqual(_.max([{0: 1}, {0: 2}, {0: -1}, {a: 1}], 0), {0: 2}, 'Lookup falsy iterator'); }); test('min', function() { equal(Infinity, _.min(null), 'can handle null/undefined'); - equal(Infinity, _.min(undefined), 'can handle null/undefined'); + equal(Infinity, _.min(void 0), 'can handle null/undefined'); equal(Infinity, _.min(null, _.identity), 'can handle null/undefined'); equal(1, _.min([1, 2, 3]), 'can perform a regular Math.min'); @@ -588,7 +588,7 @@ equal(Infinity, _.min({}), 'Minimum value of an empty object'); equal(Infinity, _.min([]), 'Minimum value of an empty array'); - equal(_.min({'a': 'a'}), Infinity, 'Minimum value of a non-numeric collection'); + equal(_.min({a: 'a'}), Infinity, 'Minimum value of a non-numeric collection'); var now = new Date(9999999999); var then = new Date(0); @@ -604,20 +604,20 @@ var iterator = function(o){ return o.x; }; equal(_.min([a, b], iterator), a, 'Respects iterator return value of Infinity'); - deepEqual(_.min([{'a': 1}, {'a': 0, 'b': 3}, {'a': 4}, {'a': 2}], 'a'), {'a': 0, 'b': 3}, 'String keys use property iterator'); + deepEqual(_.min([{a: 1}, {a: 0, b: 3}, {a: 4}, {a: 2}], 'a'), {a: 0, b: 3}, 'String keys use property iterator'); - deepEqual(_.min([0, 2], function(a){ return a * this.x; }, {x: -1}), 2, 'Iterator context'); + deepEqual(_.min([0, 2], function(c){ return c * this.x; }, {x: -1}), 2, 'Iterator context'); deepEqual(_.min([[1], [2, 3], [-1, 4], [5]], 0), [-1, 4], 'Lookup falsy iterator'); deepEqual(_.min([{0: 1}, {0: 2}, {0: -1}, {a: 1}], 0), {0: -1}, 'Lookup falsy iterator'); }); test('sortBy', function() { - var people = [{name : 'curly', age : 50}, {name : 'moe', age : 30}]; + var people = [{name: 'curly', age: 50}, {name: 'moe', age: 30}]; people = _.sortBy(people, function(person){ return person.age; }); deepEqual(_.pluck(people, 'name'), ['moe', 'curly'], 'stooges sorted by age'); - var list = [undefined, 4, 1, undefined, 3, 2]; - deepEqual(_.sortBy(list, _.identity), [1, 2, 3, 4, undefined, undefined], 'sortBy with undefined values'); + var list = [void 0, 4, 1, void 0, 3, 2]; + deepEqual(_.sortBy(list, _.identity), [1, 2, 3, 4, void 0, void 0], 'sortBy with undefined values'); list = ['one', 'two', 'three', 'four', 'five']; var sorted = _.sortBy(list, 'length'); @@ -635,9 +635,9 @@ new Pair(2, 1), new Pair(2, 2), new Pair(2, 3), new Pair(2, 4), new Pair(2, 5), new Pair(2, 6), - new Pair(undefined, 1), new Pair(undefined, 2), - new Pair(undefined, 3), new Pair(undefined, 4), - new Pair(undefined, 5), new Pair(undefined, 6) + new Pair(void 0, 1), new Pair(void 0, 2), + new Pair(void 0, 3), new Pair(void 0, 4), + new Pair(void 0, 5), new Pair(void 0, 6) ]; var actual = _.sortBy(collection, function(pair) { @@ -758,7 +758,7 @@ allSampled = _.sample(numbers, 20).sort(); deepEqual(allSampled, numbers, 'also works when sampling more objects than are present'); ok(_.contains(numbers, _.sample(numbers)), 'sampling a single element returns something from the array'); - strictEqual(_.sample([]), undefined, 'sampling empty array with no number returns undefined'); + strictEqual(_.sample([]), void 0, 'sampling empty array with no number returns undefined'); notStrictEqual(_.sample([], 5), [], 'sampling empty array with a number returns an empty array'); notStrictEqual(_.sample([1, 2, 3], 0), [], 'sampling an array with 0 picks returns an empty array'); deepEqual(_.sample([1, 2], -1), [], 'sampling a negative number of picks returns an empty array'); @@ -775,7 +775,7 @@ ok(_.toArray(a) !== a, 'array is cloned'); deepEqual(_.toArray(a), [1, 2, 3], 'cloned array contains same elements'); - var numbers = _.toArray({one : 1, two : 2, three : 3}); + var numbers = _.toArray({one: 1, two: 2, three: 3}); deepEqual(numbers, [1, 2, 3], 'object flattened into array'); if (typeof document != 'undefined') { @@ -783,13 +783,13 @@ var actual; try { actual = _.toArray(document.childNodes); - } catch(ex) { } + } catch(e) { /* ignored */ } deepEqual(actual, _.map(document.childNodes, _.identity), 'works on NodeList'); } }); test('size', function() { - equal(_.size({one : 1, two : 2, three : 3}), 3, 'can compute the size of an object'); + equal(_.size({one: 1, two: 2, three: 3}), 3, 'can compute the size of an object'); equal(_.size([1, 2, 3]), 3, 'can compute the size of an array'); equal(_.size({length: 3, 0: 0, 1: 0, 2: 0}), 3, 'can compute the size of Array-likes'); @@ -839,23 +839,23 @@ if (typeof document != 'undefined') { test('Can use various collection methods on NodeLists', function() { - var parent = document.createElement('div'); - parent.innerHTML = 'textnode'; + var parent = document.createElement('div'); + parent.innerHTML = 'textnode'; - var elementChildren = _.filter(parent.childNodes, _.isElement); - equal(elementChildren.length, 2); + var elementChildren = _.filter(parent.childNodes, _.isElement); + equal(elementChildren.length, 2); - deepEqual(_.map(elementChildren, 'id'), ['id1', 'id2']); - deepEqual(_.map(parent.childNodes, 'nodeType'), [1, 3, 1]); + deepEqual(_.map(elementChildren, 'id'), ['id1', 'id2']); + deepEqual(_.map(parent.childNodes, 'nodeType'), [1, 3, 1]); - ok(!_.every(parent.childNodes, _.isElement)); - ok(_.some(parent.childNodes, _.isElement)); + ok(!_.every(parent.childNodes, _.isElement)); + ok(_.some(parent.childNodes, _.isElement)); - function compareNode(node) { - return _.isElement(node) ? node.id.charAt(2) : void 0; - } - equal(_.max(parent.childNodes, compareNode), _.last(parent.childNodes)); - equal(_.min(parent.childNodes, compareNode), _.first(parent.childNodes)); + function compareNode(node) { + return _.isElement(node) ? node.id.charAt(2) : void 0; + } + equal(_.max(parent.childNodes, compareNode), _.last(parent.childNodes)); + equal(_.min(parent.childNodes, compareNode), _.first(parent.childNodes)); }); } diff --git a/test/cross-document.js b/test/cross-document.js index 735a8d7ec..215d78e85 100644 --- a/test/cross-document.js +++ b/test/cross-document.js @@ -138,4 +138,4 @@ }); } -}()); \ No newline at end of file +}()); diff --git a/test/functions.js b/test/functions.js index 67d22d414..8c4541110 100644 --- a/test/functions.js +++ b/test/functions.js @@ -5,7 +5,7 @@ QUnit.config.asyncRetries = 3; test('bind', function() { - var context = {name : 'moe'}; + var context = {name: 'moe'}; var func = function(arg) { return 'name: ' + (this.name || arg); }; var bound = _.bind(func, context); equal(bound(), 'name: moe', 'can bind a function to a context'); @@ -29,18 +29,18 @@ func = _.bind(func, this, 'hello', 'moe', 'curly'); equal(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments'); - func = function(context, message) { equal(this, context, message); }; + func = function(ctx, message) { equal(this, ctx, message); }; _.bind(func, 0, 0, 'can bind a function to `0`')(); _.bind(func, '', '', 'can bind a function to an empty string')(); _.bind(func, false, false, 'can bind a function to `false`')(); // These tests are only meaningful when using a browser without a native bind function // To test this with a modern browser, set underscore's nativeBind to undefined - var F = function () { return this; }; + var F = function() { return this; }; var boundf = _.bind(F, {hello: 'moe curly'}); var Boundf = boundf; // make eslint happy. var newBoundf = new Boundf(); - equal(newBoundf.hello, undefined, 'function should not be bound to the context, to comply with ECMAScript 5'); + equal(newBoundf.hello, void 0, 'function should not be bound to the context, to comply with ECMAScript 5'); equal(boundf().hello, 'moe curly', "When called without the new operator, it's OK to be bound to the context"); ok(newBoundf instanceof F, 'a bound instance is an instance of the original function'); @@ -90,10 +90,10 @@ }); test('bindAll', function() { - var curly = {name : 'curly'}, moe = { - name : 'moe', - getName : function() { return 'name: ' + this.name; }, - sayHi : function() { return 'hi: ' + this.name; } + var curly = {name: 'curly'}, moe = { + name : 'moe', + getName: function() { return 'name: ' + this.name; }, + sayHi : function() { return 'hi: ' + this.name; } }; curly.getName = moe.getName; _.bindAll(moe, 'getName', 'sayHi'); @@ -101,12 +101,12 @@ equal(curly.getName(), 'name: curly', 'unbound function is bound to current object'); equal(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object'); - curly = {name : 'curly'}; + curly = {name: 'curly'}; moe = { - name : 'moe', - getName : function() { return 'name: ' + this.name; }, - sayHi : function() { return 'hi: ' + this.name; }, - sayLast : function() { return this.sayHi(_.last(arguments)); } + name : 'moe', + getName: function() { return 'name: ' + this.name; }, + sayHi : function() { return 'hi: ' + this.name; }, + sayLast: function() { return this.sayHi(_.last(arguments)); } }; throws(function() { _.bindAll(moe); }, Error, 'throws an error for bindAll with no functions named'); @@ -159,7 +159,7 @@ return key.toUpperCase(); }); hashed('yep'); - deepEqual(hashed.cache, {'YEP': 'yep'}, 'takes a hasher'); + deepEqual(hashed.cache, {YEP: 'yep'}, 'takes a hasher'); // Test that the hash function can be used to swizzle the key. var objCacher = _.memoize(function(value, key) { @@ -169,7 +169,7 @@ }); var myObj = objCacher('a', 'alpha'); var myObjAlias = objCacher('b', 'alpha'); - notStrictEqual(myObj, undefined, 'object is created if second argument used as key'); + notStrictEqual(myObj, void 0, 'object is created if second argument used as key'); strictEqual(myObj, myObjAlias, 'object is cached if second argument used as key'); strictEqual(myObj.value, 'a', 'object is not modified if second argument used as key'); }); @@ -360,7 +360,7 @@ throttledIncr(); equal(counter, 1); - _.now = function () { + _.now = function() { return new Date(2013, 0, 1, 1, 1, 1); }; @@ -441,7 +441,7 @@ debouncedIncr(); equal(counter, 1, 'incr was called immediately'); - _.now = function () { + _.now = function() { return new Date(2013, 0, 1, 1, 1, 1); }; @@ -499,13 +499,13 @@ equal(backwards('moe'), 'hi: moe eom', 'wrapped the salutation function'); var inner = function(){ return 'Hello '; }; - var obj = {name : 'Moe'}; - obj.hi = _.wrap(inner, function(fn){ return fn() + this.name; }); + var obj = {name: 'Moe'}; + obj.hi = _.wrap(inner, function(fn){ return fn() + this.name; }); equal(obj.hi(), 'Hello Moe'); - var noop = function(){}; + var noop = function(){}; var wrapped = _.wrap(noop, function(){ return Array.prototype.slice.call(arguments, 0); }); - var ret = wrapped(['whats', 'your'], 'vector', 'victor'); + var ret = wrapped(['whats', 'your'], 'vector', 'victor'); deepEqual(ret, [noop, ['whats', 'your'], 'vector', 'victor']); }); @@ -594,29 +594,29 @@ test('restArgs', 10, function() { _.restArgs(function(a, args) { - strictEqual(a, 1); - deepEqual(args, [2, 3], 'collects rest arguments into an array'); + strictEqual(a, 1); + deepEqual(args, [2, 3], 'collects rest arguments into an array'); })(1, 2, 3); _.restArgs(function(a, args) { - strictEqual(a, undefined); - deepEqual(args, [], 'passes empty array if there are not enough arguments'); + strictEqual(a, void 0); + deepEqual(args, [], 'passes empty array if there are not enough arguments'); })(); _.restArgs(function(a, b, c, args) { - strictEqual(arguments.length, 4); - deepEqual(args, [4, 5], 'works on functions with many named parameters'); + strictEqual(arguments.length, 4); + deepEqual(args, [4, 5], 'works on functions with many named parameters'); })(1, 2, 3, 4, 5); var obj = {}; _.restArgs(function() { - strictEqual(this, obj, 'invokes function with this context'); + strictEqual(this, obj, 'invokes function with this context'); }).call(obj); _.restArgs(function(array, iteratee, context) { - deepEqual(array, [1, 2, 3, 4], 'startIndex can be used manually specify index of rest parameter'); - strictEqual(iteratee, undefined); - strictEqual(context, undefined); + deepEqual(array, [1, 2, 3, 4], 'startIndex can be used manually specify index of rest parameter'); + strictEqual(iteratee, void 0); + strictEqual(context, void 0); }, 0)(1, 2, 3, 4); }); diff --git a/test/objects.js b/test/objects.js index 0bc7cce02..af8a1f1ea 100644 --- a/test/objects.js +++ b/test/objects.js @@ -6,7 +6,7 @@ var testElement = typeof document === 'object' ? document.createElement('div') : void 0; test('keys', function() { - deepEqual(_.keys({one : 1, two : 2}), ['one', 'two'], 'can extract the keys from an object'); + deepEqual(_.keys({one: 1, two: 2}), ['one', 'two'], 'can extract the keys from an object'); // the test above is not safe because it relies on for-in enumeration order var a = []; a[1] = 0; deepEqual(_.keys(a), ['1'], 'is not fooled by sparse arrays; see issue #95'); @@ -18,17 +18,17 @@ // keys that may be missed if the implementation isn't careful var trouble = { - 'constructor': Object, - 'valueOf': _.noop, - 'hasOwnProperty': null, - 'toString': 5, - 'toLocaleString': undefined, - 'propertyIsEnumerable': /a/, - 'isPrototypeOf': this, - '__defineGetter__': Boolean, - '__defineSetter__': {}, - '__lookupSetter__': false, - '__lookupGetter__': [] + constructor : Object, + valueOf : _.noop, + hasOwnProperty : null, + toString : 5, + toLocaleString : void 0, + propertyIsEnumerable: /a/, + isPrototypeOf : this, + __defineGetter__ : Boolean, + __defineSetter__ : {}, + __lookupSetter__ : false, + __lookupGetter__ : [] }; var troubleKeys = ['constructor', 'valueOf', 'hasOwnProperty', 'toString', 'toLocaleString', 'propertyIsEnumerable', 'isPrototypeOf', '__defineGetter__', '__defineSetter__', '__lookupSetter__', '__lookupGetter__'].sort(); @@ -36,7 +36,7 @@ }); test('allKeys', function() { - deepEqual(_.allKeys({one : 1, two : 2}), ['one', 'two'], 'can extract the allKeys from an object'); + deepEqual(_.allKeys({one: 1, two: 2}), ['one', 'two'], 'can extract the allKeys from an object'); // the test above is not safe because it relies on for-in enumeration order var a = []; a[1] = 0; deepEqual(_.allKeys(a), ['1'], 'is not fooled by sparse arrays; see issue #95'); @@ -50,13 +50,13 @@ // allKeys that may be missed if the implementation isn't careful var trouble = { - constructor: Object, - valueOf: _.noop, - hasOwnProperty: null, - toString: 5, - toLocaleString: undefined, + constructor : Object, + valueOf : _.noop, + hasOwnProperty : null, + toString : 5, + toLocaleString : void 0, propertyIsEnumerable: /a/, - isPrototypeOf: this + isPrototypeOf : this }; var troubleKeys = ['constructor', 'valueOf', 'hasOwnProperty', 'toString', 'toLocaleString', 'propertyIsEnumerable', 'isPrototypeOf'].sort(); @@ -93,7 +93,7 @@ }); test('functions', function() { - var obj = {a : 'dash', b : _.map, c : /yo/, d : _.reduce}; + var obj = {a: 'dash', b: _.map, c: /yo/, d: _.reduce}; deepEqual(['b', 'd'], _.functions(obj), 'can grab the function names of any passed-in object'); var Animal = function(){}; @@ -127,13 +127,13 @@ try { result = {}; - _.extend(result, null, undefined, {a: 1}); - } catch(ex) {} + _.extend(result, null, void 0, {a: 1}); + } catch(e) { /* ignored */ } equal(result.a, 1, 'should not error on `null` or `undefined` sources'); strictEqual(_.extend(null, {a: 1}), null, 'extending null results in null'); - strictEqual(_.extend(undefined, {a: 1}), undefined, 'extending undefined results in undefined'); + strictEqual(_.extend(void 0, {a: 1}), void 0, 'extending undefined results in undefined'); }); test('extendOwn', function() { @@ -154,13 +154,13 @@ deepEqual(_.extendOwn({}, subObj), {c: 'd'}, 'assign copies own properties from source'); result = {}; - deepEqual(_.assign(result, null, undefined, {a: 1}), {a: 1}, 'should not error on `null` or `undefined` sources'); + deepEqual(_.assign(result, null, void 0, {a: 1}), {a: 1}, 'should not error on `null` or `undefined` sources'); _.each(['a', 5, null, false], function(val) { strictEqual(_.assign(val, {a: 1}), val, 'assigning non-objects results in returning the non-object value'); }); - strictEqual(_.extendOwn(undefined, {a: 1}), undefined, 'assigning undefined results in undefined'); + strictEqual(_.extendOwn(void 0, {a: 1}), void 0, 'assigning undefined results in undefined'); result = _.extendOwn({a: 1, 0: 2, 1: '5', length: 6}, {0: 1, 1: 2, length: 2}); deepEqual(result, {a: 1, 0: 1, 1: 2, length: 2}, 'assign should treat array-like objects like normal objects'); @@ -219,7 +219,7 @@ deepEqual(result, {1: 'b'}, 'can omit numeric properties'); deepEqual(_.omit(null, 'a', 'b'), {}, 'non objects return empty object'); - deepEqual(_.omit(undefined, 'toString'), {}, 'null/undefined return empty object'); + deepEqual(_.omit(void 0, 'toString'), {}, 'null/undefined return empty object'); deepEqual(_.omit(5, 'toString', 'b'), {}, 'returns empty object for primitives'); var data = {a: 1, b: 2, c: 3}; @@ -257,17 +257,17 @@ try { options = {}; - _.defaults(options, null, undefined, {a: 1}); - } catch(ex) {} + _.defaults(options, null, void 0, {a: 1}); + } catch(e) { /* ignored */ } equal(options.a, 1, 'should not error on `null` or `undefined` sources'); strictEqual(_.defaults(null, {a: 1}), null, 'result is null if destination is null'); - strictEqual(_.defaults(undefined, {a: 1}), undefined, 'result is undefined if destination is undefined'); + strictEqual(_.defaults(void 0, {a: 1}), void 0, 'result is undefined if destination is undefined'); }); test('clone', function() { - var moe = {name : 'moe', lucky : [13, 27, 34]}; + var moe = {name: 'moe', lucky: [13, 27, 34]}; var clone = _.clone(moe); equal(clone.name, 'moe', 'the clone as the attributes of the original'); @@ -277,7 +277,7 @@ clone.lucky.push(101); equal(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original'); - equal(_.clone(undefined), void 0, 'non objects should not be changed by clone'); + equal(_.clone(void 0), void 0, 'non objects should not be changed by clone'); equal(_.clone(1), 1, 'non objects should not be changed by clone'); equal(_.clone(null), null, 'non objects should not be changed by clone'); }); @@ -286,7 +286,7 @@ var Parent = function() {}; Parent.prototype = {foo: function() {}, bar: 2}; - _.each(['foo', null, undefined, 1], function(val) { + _.each(['foo', null, void 0, 1], function(val) { deepEqual(_.create(val), {}, 'should return empty object when a non-object is provided'); }); @@ -324,8 +324,8 @@ ok(!_.isEqual(0, -0), '`0` is not equal to `-0`'); ok(!_.isEqual(-0, 0), 'Commutative equality is implemented for `0` and `-0`'); - ok(!_.isEqual(null, undefined), '`null` is not equal to `undefined`'); - ok(!_.isEqual(undefined, null), 'Commutative equality is implemented for `null` and `undefined`'); + ok(!_.isEqual(null, void 0), '`null` is not equal to `undefined`'); + ok(!_.isEqual(void 0, null), 'Commutative equality is implemented for `null` and `undefined`'); // String object and primitive comparisons. ok(_.isEqual('Curly', 'Curly'), 'Identical string primitives are equal'); @@ -350,7 +350,7 @@ // Comparisons involving `NaN`. ok(_.isEqual(NaN, NaN), '`NaN` is equal to `NaN`'); - ok(_.isEqual(new Object(NaN), NaN), 'Object(`NaN`) is equal to `NaN`'); + ok(_.isEqual(new Number(NaN), NaN), 'Object(`NaN`) is equal to `NaN`'); ok(!_.isEqual(61, NaN), 'A number primitive is not equal to `NaN`'); ok(!_.isEqual(new Number(79), NaN), 'A number object is not equal to `NaN`'); ok(!_.isEqual(Infinity, NaN), '`Infinity` is not equal to `NaN`'); @@ -431,7 +431,7 @@ var sparse = []; sparse[1] = 5; - ok(_.isEqual(sparse, [undefined, 5]), 'Handles sparse arrays as dense'); + ok(_.isEqual(sparse, [void 0, 5]), 'Handles sparse arrays as dense'); // Simple objects. ok(_.isEqual({a: 'Curly', b: 1, c: true}, {a: 'Curly', b: 1, c: true}), 'Objects containing identical primitives are equal'); @@ -440,18 +440,18 @@ ok(!_.isEqual({a: 63, b: 75}, {a: 61, c: 55}), 'Objects of identical sizes with different property names are not equal'); ok(!_.isEqual({a: 1, b: 2}, {a: 1}), 'Objects of different sizes are not equal'); ok(!_.isEqual({a: 1}, {a: 1, b: 2}), 'Commutative equality is implemented for objects'); - ok(!_.isEqual({x: 1, y: undefined}, {x: 1, z: 2}), 'Objects with identical keys and different values are not equivalent'); + ok(!_.isEqual({x: 1, y: void 0}, {x: 1, z: 2}), 'Objects with identical keys and different values are not equivalent'); // `A` contains nested objects and arrays. a = { - name: new String('Moe Howard'), - age: new Number(77), - stooge: true, + name : new String('Moe Howard'), + age : new Number(77), + stooge : true, hobbies: ['acting'], - film: { - name: 'Sing a Song of Six Pants', + film : { + name : 'Sing a Song of Six Pants', release: new Date(1947, 9, 30), - stars: [new String('Larry Fine'), 'Shemp Howard'], + stars : [new String('Larry Fine'), 'Shemp Howard'], minutes: new Number(16), seconds: 54 } @@ -459,14 +459,14 @@ // `B` contains equivalent nested objects and arrays. b = { - name: new String('Moe Howard'), - age: new Number(77), - stooge: true, + name : new String('Moe Howard'), + age : new Number(77), + stooge : true, hobbies: ['acting'], - film: { - name: 'Sing a Song of Six Pants', + film : { + name : 'Sing a Song of Six Pants', release: new Date(1947, 9, 30), - stars: [new String('Larry Fine'), 'Shemp Howard'], + stars : [new String('Larry Fine'), 'Shemp Howard'], minutes: new Number(16), seconds: 54 } @@ -536,7 +536,7 @@ ok(_.isEqual(a, b), 'Cyclic structures with nested and identically-named properties are equal'); // Chaining. - ok(!_.isEqual(_({x: 1, y: undefined}).chain(), _({x: 1, z: 2}).chain()), 'Chained objects containing different values are not equal'); + ok(!_.isEqual(_({x: 1, y: void 0}).chain(), _({x: 1, z: 2}).chain()), 'Chained objects containing different values are not equal'); a = _({x: 1, y: 2}).chain(); b = _({x: 1, y: 2}).chain(); @@ -544,9 +544,9 @@ // Objects without a `constructor` property if (Object.create) { - a = Object.create(null, {x: {value: 1, enumerable: true}}); - b = {x: 1}; - ok(_.isEqual(a, b), 'Handles objects without a constructor (e.g. from Object.create'); + a = Object.create(null, {x: {value: 1, enumerable: true}}); + b = {x: 1}; + ok(_.isEqual(a, b), 'Handles objects without a constructor (e.g. from Object.create'); } function Foo() { this.a = 1; } @@ -566,7 +566,7 @@ test('isEmpty', function() { ok(!_([1]).isEmpty(), '[1] is not empty'); ok(_.isEmpty([]), '[] is empty'); - ok(!_.isEmpty({one : 1}), '{one : 1} is not empty'); + ok(!_.isEmpty({one: 1}), '{one: 1} is not empty'); ok(_.isEmpty({}), '{} is empty'); ok(_.isEmpty(new RegExp('')), 'objects with prototype properties are empty'); ok(_.isEmpty(null), 'null is empty'); @@ -574,7 +574,7 @@ ok(_.isEmpty(''), 'the empty string is empty'); ok(!_.isEmpty('moe'), 'but other strings are not'); - var obj = {one : 1}; + var obj = {one: 1}; delete obj.one; ok(_.isEmpty(obj), 'deleting all the keys from an object empties it'); @@ -583,7 +583,7 @@ ok(!_.isEmpty(args('')), 'non-empty arguments object is not empty'); // covers collecting non-enumerable properties in IE < 9 - var nonEnumProp = {'toString': 5}; + var nonEnumProp = {toString: 5}; ok(!_.isEmpty(nonEnumProp), 'non-enumerable property is not empty'); }); @@ -609,9 +609,9 @@ if (testElement) { ok(_.isObject(testElement), 'and DOM element'); } - ok(_.isObject(function () {}), 'and functions'); + ok(_.isObject(function() {}), 'and functions'); ok(!_.isObject(null), 'but not null'); - ok(!_.isObject(undefined), 'and not undefined'); + ok(!_.isObject(void 0), 'and not undefined'); ok(!_.isObject('string'), 'and not string'); ok(!_.isObject(12), 'and not number'); ok(!_.isObject(true), 'and not boolean'); @@ -619,7 +619,7 @@ }); test('isArray', function() { - ok(!_.isArray(undefined), 'undefined vars are not arrays'); + ok(!_.isArray(void 0), 'undefined vars are not arrays'); ok(!_.isArray(arguments), 'the arguments object is not an array'); ok(_.isArray([1, 2, 3]), 'but arrays are'); }); @@ -638,7 +638,7 @@ test('isNumber', function() { ok(!_.isNumber('string'), 'a string is not a number'); ok(!_.isNumber(arguments), 'the arguments object is not a number'); - ok(!_.isNumber(undefined), 'undefined is not a number'); + ok(!_.isNumber(void 0), 'undefined is not a number'); ok(_.isNumber(3 * 4 - 7 / 10), 'but numbers are'); ok(_.isNumber(NaN), 'NaN *is* a number'); ok(_.isNumber(Infinity), 'Infinity is a number'); @@ -651,7 +651,7 @@ ok(!_.isBoolean('false'), 'the string "false" is not a boolean'); ok(!_.isBoolean('true'), 'the string "true" is not a boolean'); ok(!_.isBoolean(arguments), 'the arguments object is not a boolean'); - ok(!_.isBoolean(undefined), 'undefined is not a boolean'); + ok(!_.isBoolean(void 0), 'undefined is not a boolean'); ok(!_.isBoolean(NaN), 'NaN is not a boolean'); ok(!_.isBoolean(null), 'null is not a boolean'); ok(_.isBoolean(true), 'but true is'); @@ -659,7 +659,7 @@ }); test('isFunction', function() { - ok(!_.isFunction(undefined), 'undefined vars are not functions'); + ok(!_.isFunction(void 0), 'undefined vars are not functions'); ok(!_.isFunction([1, 2, 3]), 'arrays are not functions'); ok(!_.isFunction('moe'), 'strings are not functions'); ok(_.isFunction(_.isFunction), 'but functions are'); @@ -676,9 +676,9 @@ .map(_.propertyOf(typeof GLOBAL != 'undefined' ? GLOBAL : window)) .compact() .each(function(TypedArray) { - // PhantomJS reports `typeof UInt8Array == 'object'` and doesn't report toString TypeArray - // as a function - strictEqual(_.isFunction(TypedArray), Object.prototype.toString.call(TypedArray) === '[object Function]'); + // PhantomJS reports `typeof UInt8Array == 'object'` and doesn't report toString TypeArray + // as a function + strictEqual(_.isFunction(TypedArray), Object.prototype.toString.call(TypedArray) === '[object Function]'); }); }); } @@ -695,7 +695,7 @@ }); test('isFinite', function() { - ok(!_.isFinite(undefined), 'undefined is not finite'); + ok(!_.isFinite(void 0), 'undefined is not finite'); ok(!_.isFinite(null), 'null is not finite'); ok(!_.isFinite(NaN), 'NaN is not finite'); ok(!_.isFinite(Infinity), 'Infinity is not finite'); @@ -711,7 +711,7 @@ }); test('isNaN', function() { - ok(!_.isNaN(undefined), 'undefined is not NaN'); + ok(!_.isNaN(void 0), 'undefined is not NaN'); ok(!_.isNaN(null), 'null is not NaN'); ok(!_.isNaN(0), '0 is not NaN'); ok(_.isNaN(NaN), 'but NaN is'); @@ -719,7 +719,7 @@ }); test('isNull', function() { - ok(!_.isNull(undefined), 'undefined is not null'); + ok(!_.isNull(void 0), 'undefined is not null'); ok(!_.isNull(NaN), 'NaN is not null'); ok(_.isNull(null), 'but null is'); }); @@ -730,7 +730,7 @@ ok(!_.isUndefined(false), 'false is defined'); ok(!_.isUndefined(NaN), 'NaN is defined'); ok(_.isUndefined(), 'nothing is undefined'); - ok(_.isUndefined(undefined), 'undefined is undefined'); + ok(_.isUndefined(void 0), 'undefined is undefined'); }); test('isError', function() { @@ -762,7 +762,7 @@ equal(intercepted, returned, 'can use tapped objects in a chain'); }); - test('has', function () { + test('has', function() { var obj = {foo: 'bar', func: function(){}}; ok(_.has(obj, 'foo'), 'has() checks that the object has a property.'); ok(!_.has(obj, 'baz'), "has() returns false if the object doesn't have the property."); @@ -773,7 +773,7 @@ child.prototype = obj; ok(!_.has(child, 'foo'), 'has() does not check the prototype chain for a property.'); strictEqual(_.has(null, 'foo'), false, 'has() returns false for null'); - strictEqual(_.has(undefined, 'foo'), false, 'has() returns false for undefined'); + strictEqual(_.has(void 0, 'foo'), false, 'has() returns false for undefined'); }); test('isMatch', function() { @@ -783,17 +783,17 @@ equal(_.isMatch(moe, {hair: true}), true, 'Returns a boolean'); equal(_.isMatch(curly, {hair: true}), false, 'Returns a boolean'); - equal(_.isMatch(5, {__x__: undefined}), false, 'can match undefined props on primitives'); - equal(_.isMatch({__x__: undefined}, {__x__: undefined}), true, 'can match undefined props'); + equal(_.isMatch(5, {__x__: void 0}), false, 'can match undefined props on primitives'); + equal(_.isMatch({__x__: void 0}, {__x__: void 0}), true, 'can match undefined props'); equal(_.isMatch(null, {}), true, 'Empty spec called with null object returns true'); equal(_.isMatch(null, {a: 1}), false, 'Non-empty spec called with null object returns false'); - _.each([null, undefined], function(item) { strictEqual(_.isMatch(item, null), true, 'null matches null'); }); - _.each([null, undefined], function(item) { strictEqual(_.isMatch(item, null), true, 'null matches {}'); }); - strictEqual(_.isMatch({b: 1}, {a: undefined}), false, 'handles undefined values (1683)'); + _.each([null, void 0], function(item) { strictEqual(_.isMatch(item, null), true, 'null matches null'); }); + _.each([null, void 0], function(item) { strictEqual(_.isMatch(item, null), true, 'null matches {}'); }); + strictEqual(_.isMatch({b: 1}, {a: void 0}), false, 'handles undefined values (1683)'); - _.each([true, 5, NaN, null, undefined], function(item) { + _.each([true, 5, NaN, null, void 0], function(item) { strictEqual(_.isMatch({a: 1}, item), true, 'treats primitives as empty'); }); @@ -812,8 +812,8 @@ ok(_.isMatch({x: 5, y: 1}, Prototest), 'spec can be a function'); //null edge cases - var oCon = {'constructor': Object}; - deepEqual(_.map([null, undefined, 5, {}], _.partial(_.isMatch, _, oCon)), [false, false, false, true], 'doesnt falsey match constructor on undefined/null'); + var oCon = {constructor: Object}; + deepEqual(_.map([null, void 0, 5, {}], _.partial(_.isMatch, _, oCon)), [false, false, false, true], 'doesnt falsey match constructor on undefined/null'); }); test('matcher', function() { @@ -824,21 +824,21 @@ equal(_.matcher({hair: true})(moe), true, 'Returns a boolean'); equal(_.matcher({hair: true})(curly), false, 'Returns a boolean'); - equal(_.matcher({__x__: undefined})(5), false, 'can match undefined props on primitives'); - equal(_.matcher({__x__: undefined})({__x__: undefined}), true, 'can match undefined props'); + equal(_.matcher({__x__: void 0})(5), false, 'can match undefined props on primitives'); + equal(_.matcher({__x__: void 0})({__x__: void 0}), true, 'can match undefined props'); equal(_.matcher({})(null), true, 'Empty spec called with null object returns true'); equal(_.matcher({a: 1})(null), false, 'Non-empty spec called with null object returns false'); ok(_.find(stooges, _.matcher({hair: false})) === curly, 'returns a predicate that can be used by finding functions.'); ok(_.find(stooges, _.matcher(moe)) === moe, 'can be used to locate an object exists in a collection.'); - deepEqual(_.where([null, undefined], {a: 1}), [], 'Do not throw on null values.'); + deepEqual(_.where([null, void 0], {a: 1}), [], 'Do not throw on null values.'); - deepEqual(_.where([null, undefined], null), [null, undefined], 'null matches null'); - deepEqual(_.where([null, undefined], {}), [null, undefined], 'null matches {}'); - deepEqual(_.where([{b: 1}], {a: undefined}), [], 'handles undefined values (1683)'); + deepEqual(_.where([null, void 0], null), [null, void 0], 'null matches null'); + deepEqual(_.where([null, void 0], {}), [null, void 0], 'null matches {}'); + deepEqual(_.where([{b: 1}], {a: void 0}), [], 'handles undefined values (1683)'); - _.each([true, 5, NaN, null, undefined], function(item) { + _.each([true, 5, NaN, null, void 0], function(item) { deepEqual(_.where([{a: 1}], item), [{a: 1}], 'treats primitives as empty'); }); @@ -859,18 +859,18 @@ ok(_.matcher(Prototest)({x: 5, y: 1}), 'spec can be a function'); // #1729 - var o = {'b': 1}; + var o = {b: 1}; var m = _.matcher(o); - equal(m({'b': 1}), true); + equal(m({b: 1}), true); o.b = 2; o.a = 1; - equal(m({'b': 1}), true, 'changing spec object doesnt change matches result'); + equal(m({b: 1}), true, 'changing spec object doesnt change matches result'); //null edge cases - var oCon = _.matcher({'constructor': Object}); - deepEqual(_.map([null, undefined, 5, {}], oCon), [false, false, false, true], 'doesnt falsey match constructor on undefined/null'); + var oCon = _.matcher({constructor: Object}); + deepEqual(_.map([null, void 0, 5, {}], oCon), [false, false, false, true], 'doesnt falsey match constructor on undefined/null'); }); test('matcher', function() { @@ -881,21 +881,21 @@ equal(_.matcher({hair: true})(moe), true, 'Returns a boolean'); equal(_.matcher({hair: true})(curly), false, 'Returns a boolean'); - equal(_.matcher({__x__: undefined})(5), false, 'can match undefined props on primitives'); - equal(_.matcher({__x__: undefined})({__x__: undefined}), true, 'can match undefined props'); + equal(_.matcher({__x__: void 0})(5), false, 'can match undefined props on primitives'); + equal(_.matcher({__x__: void 0})({__x__: void 0}), true, 'can match undefined props'); equal(_.matcher({})(null), true, 'Empty spec called with null object returns true'); equal(_.matcher({a: 1})(null), false, 'Non-empty spec called with null object returns false'); ok(_.find(stooges, _.matcher({hair: false})) === curly, 'returns a predicate that can be used by finding functions.'); ok(_.find(stooges, _.matcher(moe)) === moe, 'can be used to locate an object exists in a collection.'); - deepEqual(_.where([null, undefined], {a: 1}), [], 'Do not throw on null values.'); + deepEqual(_.where([null, void 0], {a: 1}), [], 'Do not throw on null values.'); - deepEqual(_.where([null, undefined], null), [null, undefined], 'null matches null'); - deepEqual(_.where([null, undefined], {}), [null, undefined], 'null matches {}'); - deepEqual(_.where([{b: 1}], {a: undefined}), [], 'handles undefined values (1683)'); + deepEqual(_.where([null, void 0], null), [null, void 0], 'null matches null'); + deepEqual(_.where([null, void 0], {}), [null, void 0], 'null matches {}'); + deepEqual(_.where([{b: 1}], {a: void 0}), [], 'handles undefined values (1683)'); - _.each([true, 5, NaN, null, undefined], function(item) { + _.each([true, 5, NaN, null, void 0], function(item) { deepEqual(_.where([{a: 1}], item), [{a: 1}], 'treats primitives as empty'); }); @@ -916,25 +916,25 @@ ok(_.matcher(Prototest)({x: 5, y: 1}), 'spec can be a function'); // #1729 - var o = {'b': 1}; + var o = {b: 1}; var m = _.matcher(o); - equal(m({'b': 1}), true); + equal(m({b: 1}), true); o.b = 2; o.a = 1; - equal(m({'b': 1}), true, 'changing spec object doesnt change matches result'); + equal(m({b: 1}), true, 'changing spec object doesnt change matches result'); //null edge cases - var oCon = _.matcher({'constructor': Object}); - deepEqual(_.map([null, undefined, 5, {}], oCon), [false, false, false, true], 'doesnt falsey match constructor on undefined/null'); + var oCon = _.matcher({constructor: Object}); + deepEqual(_.map([null, void 0, 5, {}], oCon), [false, false, false, true], 'doesnt falsey match constructor on undefined/null'); }); test('findKey', function() { var objects = { - a: {'a': 0, 'b': 0}, - b: {'a': 1, 'b': 1}, - c: {'a': 2, 'b': 2} + a: {a: 0, b: 0}, + b: {a: 1, b: 1}, + c: {a: 2, b: 2} }; equal(_.findKey(objects, function(obj) { @@ -949,7 +949,7 @@ equal(_.findKey(objects, function(obj) { return obj.b * obj.a === 5; - }), undefined); + }), void 0); strictEqual(_.findKey([1, 2, 3, 4, 5, 6], function(obj) { return obj === 3; @@ -957,7 +957,7 @@ strictEqual(_.findKey(objects, function(a) { return a.foo === null; - }), undefined); + }), void 0); _.findKey({a: {a: 1}}, function(a, key, obj) { equal(key, 'a'); @@ -972,53 +972,53 @@ test('mapObject', function() { - var obj = {'a': 1, 'b': 2}; - var objects = { - a: {'a': 0, 'b': 0}, - b: {'a': 1, 'b': 1}, - c: {'a': 2, 'b': 2} + var obj = {a: 1, b: 2}; + var objects = { + a: {a: 0, b: 0}, + b: {a: 1, b: 1}, + c: {a: 2, b: 2} }; deepEqual(_.mapObject(obj, function(val) { return val * 2; - }), {'a': 2, 'b': 4}, 'simple objects'); + }), {a: 2, b: 4}, 'simple objects'); deepEqual(_.mapObject(objects, function(val) { - return _.reduce(val, function(memo,v){ - return memo + v; - },0); - }), {'a': 0, 'b': 2, 'c': 4}, 'nested objects'); + return _.reduce(val, function(memo, v){ + return memo + v; + }, 0); + }), {a: 0, b: 2, c: 4}, 'nested objects'); - deepEqual(_.mapObject(obj, function(val,key,obj) { - return obj[key] * 2; - }), {'a': 2, 'b': 4}, 'correct keys'); + deepEqual(_.mapObject(obj, function(val, key, o) { + return o[key] * 2; + }), {a: 2, b: 4}, 'correct keys'); - deepEqual(_.mapObject([1,2], function(val) { + deepEqual(_.mapObject([1, 2], function(val) { return val * 2; - }), {'0': 2, '1': 4}, 'check behavior for arrays'); + }), {0: 2, 1: 4}, 'check behavior for arrays'); deepEqual(_.mapObject(obj, function(val) { return val * this.multiplier; - }, {multiplier : 3}), {'a': 3, 'b': 6}, 'keep context'); + }, {multiplier: 3}), {a: 3, b: 6}, 'keep context'); deepEqual(_.mapObject({a: 1}, function() { return this.length; - }, [1,2]), {'a': 2}, 'called with context'); + }, [1, 2]), {a: 2}, 'called with context'); var ids = _.mapObject({length: 2, 0: {id: '1'}, 1: {id: '2'}}, function(n){ return n.id; }); - deepEqual(ids, {'length': undefined, '0': '1', '1': '2'}, 'Check with array-like objects'); + deepEqual(ids, {length: void 0, 0: '1', 1: '2'}, 'Check with array-like objects'); // Passing a property name like _.pluck. - var people = {'a': {name : 'moe', age : 30}, 'b': {name : 'curly', age : 50}}; - deepEqual(_.mapObject(people, 'name'), {'a': 'moe', 'b': 'curly'}, 'predicate string map to object properties'); + var people = {a: {name: 'moe', age: 30}, b: {name: 'curly', age: 50}}; + deepEqual(_.mapObject(people, 'name'), {a: 'moe', b: 'curly'}, 'predicate string map to object properties'); - _.each([null, void 0, 1, 'abc', [], {}, undefined], function(val){ + _.each([null, void 0, 1, 'abc', [], {}, void 0], function(val){ deepEqual(_.mapObject(val, _.identity), {}, 'mapValue identity'); }); - var Proto = function(){this.a = 1;}; + var Proto = function(){ this.a = 1; }; Proto.prototype.b = 1; var protoObj = new Proto(); deepEqual(_.mapObject(protoObj, _.identity), {a: 1}, 'ignore inherited values from prototypes'); diff --git a/test/utility.js b/test/utility.js index 83b76a0e3..31746cc08 100644 --- a/test/utility.js +++ b/test/utility.js @@ -21,42 +21,42 @@ }); test('identity', function() { - var stooge = {name : 'moe'}; + var stooge = {name: 'moe'}; equal(_.identity(stooge), stooge, 'stooge is the same as his identity'); }); test('constant', function() { - var stooge = {name : 'moe'}; + var stooge = {name: 'moe'}; equal(_.constant(stooge)(), stooge, 'should create a function that returns stooge'); }); test('noop', function() { - strictEqual(_.noop('curly', 'larry', 'moe'), undefined, 'should always return undefined'); + strictEqual(_.noop('curly', 'larry', 'moe'), void 0, 'should always return undefined'); }); test('property', function() { - var stooge = {name : 'moe'}; + var stooge = {name: 'moe'}; equal(_.property('name')(stooge), 'moe', 'should return the property with the given name'); - equal(_.property('name')(null), undefined, 'should return undefined for null values'); - equal(_.property('name')(undefined), undefined, 'should return undefined for undefined values'); + equal(_.property('name')(null), void 0, 'should return undefined for null values'); + equal(_.property('name')(void 0), void 0, 'should return undefined for undefined values'); }); - + test('propertyOf', function() { var stoogeRanks = _.propertyOf({curly: 2, moe: 1, larry: 3}); equal(stoogeRanks('curly'), 2, 'should return the property with the given name'); - equal(stoogeRanks(null), undefined, 'should return undefined for null values'); - equal(stoogeRanks(undefined), undefined, 'should return undefined for undefined values'); - + equal(stoogeRanks(null), void 0, 'should return undefined for null values'); + equal(stoogeRanks(void 0), void 0, 'should return undefined for undefined values'); + function MoreStooges() { this.shemp = 87; } MoreStooges.prototype = {curly: 2, moe: 1, larry: 3}; var moreStoogeRanks = _.propertyOf(new MoreStooges()); equal(moreStoogeRanks('curly'), 2, 'should return properties from further up the prototype chain'); - + var nullPropertyOf = _.propertyOf(null); - equal(nullPropertyOf('curly'), undefined, 'should return undefined when obj is null'); - - var undefPropertyOf = _.propertyOf(undefined); - equal(undefPropertyOf('curly'), undefined, 'should return undefined when obj is undefined'); + equal(nullPropertyOf('curly'), void 0, 'should return undefined when obj is null'); + + var undefPropertyOf = _.propertyOf(void 0); + equal(undefPropertyOf('curly'), void 0, 'should return undefined when obj is undefined'); }); test('random', function() { @@ -86,7 +86,7 @@ test('times', function() { var vals = []; - _.times(3, function (i) { vals.push(i); }); + _.times(3, function(i) { vals.push(i); }); deepEqual(vals, [0, 1, 2], 'is 0 indexed'); // vals = []; @@ -127,16 +127,16 @@ var escapeCharacters = ['<', '>', '"', '\'', '`']; _.each(escapeCharacters, function(escapeChar) { - var str = 'a ' + escapeChar + ' string escaped'; - var escaped = _.escape(str); - notEqual(str, escaped, escapeChar + ' is escaped'); - equal(str, _.unescape(escaped), escapeChar + ' can be unescaped'); + var s = 'a ' + escapeChar + ' string escaped'; + var e = _.escape(s); + notEqual(s, e, escapeChar + ' is escaped'); + equal(s, _.unescape(e), escapeChar + ' can be unescaped'); - str = 'a ' + escapeChar + escapeChar + escapeChar + 'some more string' + escapeChar; - escaped = _.escape(str); + s = 'a ' + escapeChar + escapeChar + escapeChar + 'some more string' + escapeChar; + e = _.escape(s); - equal(escaped.indexOf(escapeChar), -1, 'can escape multiple occurances of ' + escapeChar); - equal(_.unescape(escaped), str, 'multiple occurrences of ' + escapeChar + ' can be unescaped'); + equal(e.indexOf(escapeChar), -1, 'can escape multiple occurances of ' + escapeChar); + equal(_.unescape(e), s, 'multiple occurrences of ' + escapeChar + ' can be unescaped'); }); // handles multiple escape characters at once @@ -158,7 +158,7 @@ test('template', function() { var basicTemplate = _.template("<%= thing %> is gettin' on my noives!"); - var result = basicTemplate({thing : 'This'}); + var result = basicTemplate({thing: 'This'}); equal(result, "This is gettin' on my noives!", 'can do basic attribute interpolation'); var sansSemicolonTemplate = _.template('A <% this %> B'); @@ -173,7 +173,7 @@ var fancyTemplate = _.template('
        <% ' + ' for (var key in people) { ' + '%>
      • <%= people[key] %>
      • <% } %>
      '); - result = fancyTemplate({people : {moe : 'Moe', larry : 'Larry', curly : 'Curly'}}); + result = fancyTemplate({people: {moe: 'Moe', larry: 'Larry', curly: 'Curly'}}); equal(result, '
      • Moe
      • Larry
      • Curly
      ', 'can run arbitrary javascript in templates'); var escapedCharsInJavascriptTemplate = _.template('
        <% _.each(numbers.split("\\n"), function(item) { %>
      • <%= item %>
      • <% }) %>
      '); @@ -182,7 +182,7 @@ var namespaceCollisionTemplate = _.template('<%= pageCount %> <%= thumbnails[pageCount] %> <% _.each(thumbnails, function(p) { %>
      <% }); %>'); result = namespaceCollisionTemplate({ - pageCount: 3, + pageCount : 3, thumbnails: { 1: 'p1-thumbnail.gif', 2: 'p2-thumbnail.gif', @@ -211,7 +211,7 @@ equal(result, '<script>'); var stooge = { - name: 'Moe', + name : 'Moe', template: _.template("I'm <%= this.name %>") }; equal(stooge.template(), "I'm Moe"); @@ -222,15 +222,15 @@ ' if (data) { data += 12345; }; %>\n ' + '
    • <%= data %>
    • \n ' ); - equal(template({data : 12345}).replace(/\s/g, ''), '
    • 24690
    • '); + equal(template({data: 12345}).replace(/\s/g, ''), '
    • 24690
    • '); _.templateSettings = { - evaluate : /\{\{([\s\S]+?)\}\}/g, - interpolate : /\{\{=([\s\S]+?)\}\}/g + evaluate : /\{\{([\s\S]+?)\}\}/g, + interpolate: /\{\{=([\s\S]+?)\}\}/g }; var custom = _.template('
        {{ for (var key in people) { }}
      • {{= people[key] }}
      • {{ } }}
      '); - result = custom({people : {moe : 'Moe', larry : 'Larry', curly : 'Curly'}}); + result = custom({people: {moe: 'Moe', larry: 'Larry', curly: 'Curly'}}); equal(result, '
      • Moe
      • Larry
      • Curly
      ', 'can run arbitrary javascript in templates'); var customQuote = _.template("It's its, not it's"); @@ -240,12 +240,12 @@ equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'."); _.templateSettings = { - evaluate : /<\?([\s\S]+?)\?>/g, - interpolate : /<\?=([\s\S]+?)\?>/g + evaluate : /<\?([\s\S]+?)\?>/g, + interpolate: /<\?=([\s\S]+?)\?>/g }; var customWithSpecialChars = _.template('
      '); - result = customWithSpecialChars({people : {moe : 'Moe', larry : 'Larry', curly : 'Curly'}}); + result = customWithSpecialChars({people: {moe: 'Moe', larry: 'Larry', curly: 'Curly'}}); equal(result, '
      • Moe
      • Larry
      • Curly
      ', 'can run arbitrary javascript in templates'); var customWithSpecialCharsQuote = _.template("It's its, not it's"); @@ -255,21 +255,22 @@ equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'."); _.templateSettings = { - interpolate : /\{\{(.+?)\}\}/g + interpolate: /\{\{(.+?)\}\}/g }; var mustache = _.template('Hello {{planet}}!'); - equal(mustache({planet : 'World'}), 'Hello World!', 'can mimic mustache.js'); + equal(mustache({planet: 'World'}), 'Hello World!', 'can mimic mustache.js'); var templateWithNull = _.template('a null undefined {{planet}}'); - equal(templateWithNull({planet : 'world'}), 'a null undefined world', 'can handle missing escape and evaluate settings'); + equal(templateWithNull({planet: 'world'}), 'a null undefined world', 'can handle missing escape and evaluate settings'); }); test('_.template provides the generated function source, when a SyntaxError occurs', function() { + var source; try { _.template('<%= if x %>'); } catch (ex) { - var source = ex.source; + source = ex.source; } ok(/__p/.test(source)); }); @@ -284,13 +285,13 @@ strictEqual(_.result(obj, 'w'), ''); strictEqual(_.result(obj, 'x'), 'x'); strictEqual(_.result(obj, 'y'), 'x'); - strictEqual(_.result(obj, 'z'), undefined); - strictEqual(_.result(null, 'x'), undefined); + strictEqual(_.result(obj, 'z'), void 0); + strictEqual(_.result(null, 'x'), void 0); }); test('result returns a default value if object is null or undefined', function() { strictEqual(_.result(null, 'b', 'default'), 'default'); - strictEqual(_.result(undefined, 'c', 'default'), 'default'); + strictEqual(_.result(void 0, 'c', 'default'), 'default'); strictEqual(_.result(''.match('missing'), 1, 'default'), 'default'); }); @@ -301,7 +302,7 @@ test('result only returns the default value if the object does not have the property or is undefined', function() { strictEqual(_.result({}, 'b', 'default'), 'default'); - strictEqual(_.result({d: undefined}, 'd', 'default'), 'default'); + strictEqual(_.result({d: void 0}, 'd', 'default'), 'default'); }); test('result does not return the default if the property of an object is found in the prototype', function() { @@ -312,7 +313,7 @@ test('result does use the fallback when the result of invoking the property is undefined', function() { var obj = {a: function() {}}; - strictEqual(_.result(obj, 'a', 'failed'), undefined); + strictEqual(_.result(obj, 'a', 'failed'), void 0); }); test('result fallback can use a function', function() { @@ -341,11 +342,11 @@ test('#556 - undefined template variables.', function() { var template = _.template('<%=x%>'); strictEqual(template({x: null}), ''); - strictEqual(template({x: undefined}), ''); + strictEqual(template({x: void 0}), ''); var templateEscaped = _.template('<%-x%>'); strictEqual(templateEscaped({x: null}), ''); - strictEqual(templateEscaped({x: undefined}), ''); + strictEqual(templateEscaped({x: void 0}), ''); var templateWithProperty = _.template('<%=x.foo%>'); strictEqual(templateWithProperty({x: {}}), ''); diff --git a/underscore.js b/underscore.js index e7b382dbf..08b3ed589 100644 --- a/underscore.js +++ b/underscore.js @@ -10,8 +10,8 @@ // Establish the root object, `window` (`self`) in the browser, or `global` on the server. // We use `self` instead of `window` for `WebWorker` support. - var root = (typeof self === 'object' && self.self === self && self) || - (typeof global === 'object' && global.global === global && global); + var root = typeof self === 'object' && self.self === self && self || + typeof global === 'object' && global.global === global && global; // Save the previous value of the `_` variable. var previousUnderscore = root._; @@ -21,18 +21,18 @@ // Create quick reference variables for speed access to core prototypes. var - push = ArrayProto.push, - slice = ArrayProto.slice, - toString = ObjProto.toString, - hasOwnProperty = ObjProto.hasOwnProperty; + push = ArrayProto.push, + slice = ArrayProto.slice, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; // All **ECMAScript 5** native function implementations that we hope to use // are declared here. var - nativeIsArray = Array.isArray, - nativeKeys = Object.keys, - nativeBind = FuncProto.bind, - nativeCreate = Object.create; + nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeBind = FuncProto.bind, + nativeCreate = Object.create; // Naked function reference for surrogate-prototype-swapping. var Ctor = function(){}; @@ -103,7 +103,8 @@ return function() { var length = Math.max(arguments.length - startIndex, 0); var rest = Array(length); - for (var index = 0; index < length; index++) { + var index; + for (index = 0; index < length; index++) { rest[index] = arguments[index + startIndex]; } switch (startIndex) { @@ -315,10 +316,10 @@ } } else { iteratee = cb(iteratee, context); - _.each(obj, function(value, index, list) { - computed = iteratee(value, index, list); + _.each(obj, function(v, index, list) { + computed = iteratee(v, index, list); if (computed > lastComputed || computed === -Infinity && result === -Infinity) { - result = value; + result = v; lastComputed = computed; } }); @@ -340,10 +341,10 @@ } } else { iteratee = cb(iteratee, context); - _.each(obj, function(value, index, list) { - computed = iteratee(value, index, list); + _.each(obj, function(v, index, list) { + computed = iteratee(v, index, list); if (computed < lastComputed || computed === Infinity && result === Infinity) { - result = value; + result = v; lastComputed = computed; } }); @@ -383,8 +384,8 @@ iteratee = cb(iteratee, context); return _.pluck(_.map(obj, function(value, index, list) { return { - value: value, - index: index, + value : value, + index : index, criteria: iteratee(value, index, list) }; }).sort(function(left, right) { @@ -563,7 +564,8 @@ for (var i = 0, length = getLength(array); i < length; i++) { var item = array[i]; if (_.contains(result, item)) continue; - for (var j = 1; j < argsLength; j++) { + var j; + for (j = 1; j < argsLength; j++) { if (!_.contains(arguments[j], item)) break; } if (j === argsLength) result.push(item); @@ -647,9 +649,9 @@ var i = 0, length = getLength(array); if (typeof idx == 'number') { if (dir > 0) { - i = idx >= 0 ? idx : Math.max(idx + length, i); + i = idx >= 0 ? idx : Math.max(idx + length, i); } else { - length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1; + length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1; } } else if (sortedIndex && idx && length) { idx = sortedIndex(array, item); @@ -914,7 +916,7 @@ var collectNonEnumProps = function(obj, keys) { var nonEnumIdx = nonEnumerableProps.length; var constructor = obj.constructor; - var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto; + var proto = _.isFunction(constructor) && constructor.prototype || ObjProto; // Constructor is a special case. var prop = 'constructor'; @@ -965,7 +967,7 @@ // In contrast to _.map it returns an object _.mapObject = function(obj, iteratee, context) { iteratee = cb(iteratee, context); - var keys = _.keys(obj), + var keys = _.keys(obj), length = keys.length, results = {}; for (var index = 0; index < length; index++) { @@ -1043,7 +1045,7 @@ // Internal pick helper function to determine if `obj` has key `key`. var keyInObj = function(value, key, obj) { - return key in obj; + return key in obj; }; // Return a copy of the object only containing the whitelisted properties. @@ -1402,8 +1404,8 @@ // If the value of the named `property` is a function then invoke it with the // `object` as context; otherwise, return it. - _.result = function(object, property, fallback) { - var value = object == null ? void 0 : object[property]; + _.result = function(object, prop, fallback) { + var value = object == null ? void 0 : object[prop]; if (value === void 0) { value = fallback; } @@ -1421,9 +1423,9 @@ // By default, Underscore uses ERB-style template delimiters, change the // following template settings to use alternative delimiters. _.templateSettings = { - evaluate : /<%([\s\S]+?)%>/g, - interpolate : /<%=([\s\S]+?)%>/g, - escape : /<%-([\s\S]+?)%>/g + evaluate : /<%([\s\S]+?)%>/g, + interpolate: /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g }; // When customizing `templateSettings`, if you don't want to define an @@ -1434,15 +1436,15 @@ // Certain characters need to be escaped so that they can be put into a // string literal. var escapes = { - "'": "'", - '\\': '\\', - '\r': 'r', - '\n': 'n', + "'" : "'", + '\\' : '\\', + '\r' : 'r', + '\n' : 'n', '\u2028': 'u2028', '\u2029': 'u2029' }; - var escaper = /\\|'|\r|\n|\u2028|\u2029/g; + var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g; var escapeChar = function(match) { return '\\' + escapes[match]; @@ -1467,7 +1469,7 @@ var index = 0; var source = "__p+='"; text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { - source += text.slice(index, offset).replace(escaper, escapeChar); + source += text.slice(index, offset).replace(escapeRegExp, escapeChar); index = offset + match.length; if (escape) { @@ -1490,8 +1492,9 @@ "print=function(){__p+=__j.call(arguments,'');};\n" + source + 'return __p;\n'; + var render; try { - var render = new Function(settings.variable || 'obj', '_', source); + render = new Function(settings.variable || 'obj', '_', source); } catch (e) { e.source = source; throw e; @@ -1522,7 +1525,7 @@ // underscore functions. Wrapped objects may be chained. // Helper function to continue chaining intermediate results. - var result = function(instance, obj) { + var chainResult = function(instance, obj) { return instance._chain ? _(obj).chain() : obj; }; @@ -1533,7 +1536,7 @@ _.prototype[name] = function() { var args = [this._wrapped]; push.apply(args, arguments); - return result(this, func.apply(_, args)); + return chainResult(this, func.apply(_, args)); }; }); }; @@ -1548,7 +1551,7 @@ var obj = this._wrapped; method.apply(obj, arguments); if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; - return result(this, obj); + return chainResult(this, obj); }; }); @@ -1556,7 +1559,7 @@ _.each(['concat', 'join', 'slice'], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { - return result(this, method.apply(this._wrapped, arguments)); + return chainResult(this, method.apply(this._wrapped, arguments)); }; }); From de320a16237f0a2cdd55a04a5ca2cf1efb62c8b3 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Tue, 26 May 2015 11:31:30 -0700 Subject: [PATCH 035/263] change key-spacing linting rule to not require colon alignment --- .eslintrc | 2 +- test/functions.js | 8 +++---- test/objects.js | 56 +++++++++++++++++++++++------------------------ test/utility.js | 8 +++---- underscore.js | 16 +++++++------- 5 files changed, 45 insertions(+), 45 deletions(-) diff --git a/.eslintrc b/.eslintrc index e48ae1426..0e88b37eb 100644 --- a/.eslintrc +++ b/.eslintrc @@ -16,7 +16,7 @@ "eol-last": 2, "eqeqeq": [2, "smart"], "indent": [2, 2, {"indentSwitchCase": true}], - "key-spacing": [1, { "align": "colon" }], + "key-spacing": 1, "linebreak-style": 2, "max-depth": [1, 4], "max-params": [1, 5], diff --git a/test/functions.js b/test/functions.js index 8c4541110..7ceb49dfe 100644 --- a/test/functions.js +++ b/test/functions.js @@ -91,9 +91,9 @@ test('bindAll', function() { var curly = {name: 'curly'}, moe = { - name : 'moe', + name: 'moe', getName: function() { return 'name: ' + this.name; }, - sayHi : function() { return 'hi: ' + this.name; } + sayHi: function() { return 'hi: ' + this.name; } }; curly.getName = moe.getName; _.bindAll(moe, 'getName', 'sayHi'); @@ -103,9 +103,9 @@ curly = {name: 'curly'}; moe = { - name : 'moe', + name: 'moe', getName: function() { return 'name: ' + this.name; }, - sayHi : function() { return 'hi: ' + this.name; }, + sayHi: function() { return 'hi: ' + this.name; }, sayLast: function() { return this.sayHi(_.last(arguments)); } }; diff --git a/test/objects.js b/test/objects.js index af8a1f1ea..2aa19db76 100644 --- a/test/objects.js +++ b/test/objects.js @@ -18,17 +18,17 @@ // keys that may be missed if the implementation isn't careful var trouble = { - constructor : Object, - valueOf : _.noop, - hasOwnProperty : null, - toString : 5, - toLocaleString : void 0, + constructor: Object, + valueOf: _.noop, + hasOwnProperty: null, + toString: 5, + toLocaleString: void 0, propertyIsEnumerable: /a/, - isPrototypeOf : this, - __defineGetter__ : Boolean, - __defineSetter__ : {}, - __lookupSetter__ : false, - __lookupGetter__ : [] + isPrototypeOf: this, + __defineGetter__: Boolean, + __defineSetter__: {}, + __lookupSetter__: false, + __lookupGetter__: [] }; var troubleKeys = ['constructor', 'valueOf', 'hasOwnProperty', 'toString', 'toLocaleString', 'propertyIsEnumerable', 'isPrototypeOf', '__defineGetter__', '__defineSetter__', '__lookupSetter__', '__lookupGetter__'].sort(); @@ -50,13 +50,13 @@ // allKeys that may be missed if the implementation isn't careful var trouble = { - constructor : Object, - valueOf : _.noop, - hasOwnProperty : null, - toString : 5, - toLocaleString : void 0, + constructor: Object, + valueOf: _.noop, + hasOwnProperty: null, + toString: 5, + toLocaleString: void 0, propertyIsEnumerable: /a/, - isPrototypeOf : this + isPrototypeOf: this }; var troubleKeys = ['constructor', 'valueOf', 'hasOwnProperty', 'toString', 'toLocaleString', 'propertyIsEnumerable', 'isPrototypeOf'].sort(); @@ -444,14 +444,14 @@ // `A` contains nested objects and arrays. a = { - name : new String('Moe Howard'), - age : new Number(77), - stooge : true, + name: new String('Moe Howard'), + age: new Number(77), + stooge: true, hobbies: ['acting'], - film : { - name : 'Sing a Song of Six Pants', + film: { + name: 'Sing a Song of Six Pants', release: new Date(1947, 9, 30), - stars : [new String('Larry Fine'), 'Shemp Howard'], + stars: [new String('Larry Fine'), 'Shemp Howard'], minutes: new Number(16), seconds: 54 } @@ -459,14 +459,14 @@ // `B` contains equivalent nested objects and arrays. b = { - name : new String('Moe Howard'), - age : new Number(77), - stooge : true, + name: new String('Moe Howard'), + age: new Number(77), + stooge: true, hobbies: ['acting'], - film : { - name : 'Sing a Song of Six Pants', + film: { + name: 'Sing a Song of Six Pants', release: new Date(1947, 9, 30), - stars : [new String('Larry Fine'), 'Shemp Howard'], + stars: [new String('Larry Fine'), 'Shemp Howard'], minutes: new Number(16), seconds: 54 } diff --git a/test/utility.js b/test/utility.js index 31746cc08..c20f33a24 100644 --- a/test/utility.js +++ b/test/utility.js @@ -182,7 +182,7 @@ var namespaceCollisionTemplate = _.template('<%= pageCount %> <%= thumbnails[pageCount] %> <% _.each(thumbnails, function(p) { %>
      <% }); %>'); result = namespaceCollisionTemplate({ - pageCount : 3, + pageCount: 3, thumbnails: { 1: 'p1-thumbnail.gif', 2: 'p2-thumbnail.gif', @@ -211,7 +211,7 @@ equal(result, '<script>'); var stooge = { - name : 'Moe', + name: 'Moe', template: _.template("I'm <%= this.name %>") }; equal(stooge.template(), "I'm Moe"); @@ -225,7 +225,7 @@ equal(template({data: 12345}).replace(/\s/g, ''), '
    • 24690
    • '); _.templateSettings = { - evaluate : /\{\{([\s\S]+?)\}\}/g, + evaluate: /\{\{([\s\S]+?)\}\}/g, interpolate: /\{\{=([\s\S]+?)\}\}/g }; @@ -240,7 +240,7 @@ equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'."); _.templateSettings = { - evaluate : /<\?([\s\S]+?)\?>/g, + evaluate: /<\?([\s\S]+?)\?>/g, interpolate: /<\?=([\s\S]+?)\?>/g }; diff --git a/underscore.js b/underscore.js index cbf33c45f..30365ab02 100644 --- a/underscore.js +++ b/underscore.js @@ -384,8 +384,8 @@ iteratee = cb(iteratee, context); return _.pluck(_.map(obj, function(value, index, list) { return { - value : value, - index : index, + value: value, + index: index, criteria: iteratee(value, index, list) }; }).sort(function(left, right) { @@ -1423,9 +1423,9 @@ // By default, Underscore uses ERB-style template delimiters, change the // following template settings to use alternative delimiters. _.templateSettings = { - evaluate : /<%([\s\S]+?)%>/g, + evaluate: /<%([\s\S]+?)%>/g, interpolate: /<%=([\s\S]+?)%>/g, - escape : /<%-([\s\S]+?)%>/g + escape: /<%-([\s\S]+?)%>/g }; // When customizing `templateSettings`, if you don't want to define an @@ -1436,10 +1436,10 @@ // Certain characters need to be escaped so that they can be put into a // string literal. var escapes = { - "'" : "'", - '\\' : '\\', - '\r' : 'r', - '\n' : 'n', + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', '\u2028': 'u2028', '\u2029': 'u2029' }; From d36d0da4988c5125aa5c293775951ee3490a7e1e Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Wed, 27 May 2015 13:10:06 -0400 Subject: [PATCH 036/263] Remove nativeBind usage We cover almost all of nativeBind's cases: - Binding function to context - Binding function to context with args - Binding constructor with args The only downside is supported environments will no longer get the correct `length` property of the bound method. Usually, you subtract the number of bound args from `func`'s length. But, now Underscore is consistent between all environments. --- underscore.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/underscore.js b/underscore.js index 30365ab02..0f680e47b 100644 --- a/underscore.js +++ b/underscore.js @@ -17,7 +17,7 @@ var previousUnderscore = root._; // Save bytes in the minified (but not gzipped) version: - var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + var ArrayProto = Array.prototype, ObjProto = Object.prototype; // Create quick reference variables for speed access to core prototypes. var @@ -31,7 +31,6 @@ var nativeIsArray = Array.isArray, nativeKeys = Object.keys, - nativeBind = FuncProto.bind, nativeCreate = Object.create; // Naked function reference for surrogate-prototype-swapping. @@ -711,15 +710,13 @@ // Create a function bound to a given object (assigning `this`, and arguments, // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if // available. - _.bind = function(func, context) { - if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + _.bind = restArgs(function(func, context, args) { if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function'); - var args = slice.call(arguments, 2); var bound = restArgs(function(callArgs) { return executeBound(func, bound, context, this, args.concat(callArgs)); }); return bound; - }; + }); // Partially apply a function by creating a version that has had some of its // arguments pre-filled, without changing its dynamic `this` context. _ acts From 66d61e8a5b82a5daa61f07fb3da240b0f194abc8 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Fri, 29 May 2015 16:56:02 -0400 Subject: [PATCH 037/263] Remove native #reduceRight reference --- index.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/index.html b/index.html index f0624544c..b3b80bd2c 100644 --- a/index.html +++ b/index.html @@ -514,8 +514,7 @@

      Collection Functions (Arrays or Objects)

      reduceRight_.reduceRight(list, iteratee, memo, [context]) Alias: foldr
      - The right-associative version of reduce. Delegates to the - JavaScript 1.8 version of reduceRight, if it exists. Foldr + The right-associative version of reduce. Foldr is not as useful in JavaScript as it would be in a language with lazy evaluation.

      From 427fa965f8f322b43433079fb3578f30c7f7305e Mon Sep 17 00:00:00 2001 From: Erik Bernhardsson Date: Fri, 29 May 2015 10:29:47 -0400 Subject: [PATCH 038/263] Avoid array creation in recursive flatten fixes #2202 If you have a n=1000 deeply nested array with k=1000 elements in the leaf node then it would be copied n times leading to O(n*k). After this fix, it will take O(n+k) --- test/arrays.js | 5 +++++ underscore.js | 16 +++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index b8903d3ea..d41a019e3 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -98,6 +98,11 @@ equal(_.flatten([_.range(10), _.range(10), 5, 1, 3]).length, 23); equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3]).length, 1056003, 'Flatten can handle massive collections'); equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3], true).length, 1056003, 'Flatten can handle massive collections'); + + var x = _.range(100000); + for (var i = 0; i < 1000; i++) x = [x]; + deepEqual(_.flatten(x), _.range(100000), 'Flatten can handle very deep arrays'); + deepEqual(_.flatten(x, true), x[0], 'Flatten can handle very deep arrays with shallow'); }); test('without', function() { diff --git a/underscore.js b/underscore.js index 0f680e47b..c8d534b10 100644 --- a/underscore.js +++ b/underscore.js @@ -490,17 +490,19 @@ }; // Internal implementation of a recursive `flatten` function. - var flatten = function(input, shallow, strict) { - var output = [], idx = 0; + var flatten = function(input, shallow, strict, output) { + output = output || []; + var idx = output.length; for (var i = 0, length = getLength(input); i < length; i++) { var value = input[i]; if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) { //flatten current level of array or arguments object - if (!shallow) value = flatten(value, shallow, strict); - var j = 0, len = value.length; - output.length += len; - while (j < len) { - output[idx++] = value[j++]; + if (shallow) { + var j = 0, len = value.length; + while (j < len) output[idx++] = value[j++]; + } else { + flatten(value, shallow, strict, output); + idx = output.length; } } else if (!strict) { output[idx++] = value; From d04b7e9cf10c6909ee5e74eaef8cc1177c80d1c3 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sun, 24 May 2015 23:42:51 -0700 Subject: [PATCH 039/263] Addes coverage with nyc and coveralls Closes #2191 --- .gitignore | 2 ++ .travis.yml | 3 ++- package.json | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2c7f8be2f..ab5b5c583 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ node_modules *.log *.idea *.swp +nyc_output +coverage diff --git a/.travis.yml b/.travis.yml index 07d33cc8a..d19cf6e8a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,4 +29,5 @@ env: global: - secure: bDZSBQfqr21hCayjcZ20IxrV6+XGhxQPFIfwWqEKLrF93Gu8LLVjZRxXE/mE8I8N4Z5WtDNb4ZHrm/TTzmcPa5MuHgIxEdknQCncobH8oimwc83SHwEPk6okeNKl39VlCjvvnmoe/V/KpnknuYn3Rqghtl/Uv9KLpCwskwjTtcw= - secure: SRECgXuwcZTcD3GVxTS2bYNgRyye4vq6BLrV2PH9FyNenowsKQR2EwlC/dppc1Q8NWMgv79J/R96q9JOFh+mEH9L5dlBb2yhnGH8amVeM/ChAJHT/F8YktKM453uVpz5fR00QcCQDDUOx6Pvx374ID0OKNpWKAkQBWA9mPTsLnE= - matrix: BROWSER=false \ No newline at end of file + matrix: BROWSER=false +after_success: npm run coveralls diff --git a/package.json b/package.json index f1ad720da..9bcfb0fc0 100644 --- a/package.json +++ b/package.json @@ -17,15 +17,19 @@ "main": "underscore.js", "version": "1.8.3", "devDependencies": { + "coveralls": "^2.11.2", "docco": "*", "eslint": "0.21.x", "karma": "~0.12.31", "karma-qunit": "~0.1.4", + "nyc": "^2.1.3", "qunit-cli": "~0.2.0", "uglify-js": "2.4.x" }, "scripts": { "test": "npm run test-node && npm run lint", + "coverage": "nyc npm run test-node && nyc report", + "coveralls": "nyc npm run test-node && nyc report --reporter=text-lcov | coveralls", "lint": "eslint --reset underscore.js test/*.js", "test-node": "qunit-cli test/*.js", "test-browser": "npm i karma-phantomjs-launcher && ./node_modules/karma/bin/karma start", From 4a4a94a4f9b85dd384e339af0c5f2741147882e2 Mon Sep 17 00:00:00 2001 From: Kevin Kirsche Date: Wed, 10 Jun 2015 18:25:49 -0400 Subject: [PATCH 040/263] Remove moot `version` property from bower.json Per bower/bower.json-spec@a325da3 --- bower.json | 1 - 1 file changed, 1 deletion(-) diff --git a/bower.json b/bower.json index c7b885334..951eabb18 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,5 @@ { "name": "underscore", - "version": "1.8.3", "main": "underscore.js", "keywords": ["util", "functional", "server", "client", "browser"], "ignore" : ["docs", "test", "*.yml", "CNAME", "index.html", "favicon.ico", "CONTRIBUTING.md", ".*", "component.json", "package.json", "karma.*"] From 1f19b943d3c868a7996b72e4c6f5cd80f3bc6dd6 Mon Sep 17 00:00:00 2001 From: Tom Martin Date: Fri, 12 Jun 2015 00:04:47 +0100 Subject: [PATCH 041/263] Extend max, min to return max/min of multple arrays --- test/collections.js | 4 ++++ underscore.js | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/test/collections.js b/test/collections.js index 2faccbafc..d25e9d0fa 100644 --- a/test/collections.js +++ b/test/collections.js @@ -564,6 +564,8 @@ equal(3, _.max([1, 2, 3, 'test']), 'Finds correct max in array starting with num and containing a NaN'); equal(3, _.max(['test', 1, 2, 3]), 'Finds correct max in array starting with NaN'); + deepEqual([3, 6], _.map([[1, 2, 3], [4, 5, 6]], _.max), 'Finds correct max in array when mapping through multiple arrays'); + var a = {x: -Infinity}; var b = {x: -Infinity}; var iterator = function(o){ return o.x; }; @@ -590,6 +592,8 @@ equal(Infinity, _.min([]), 'Minimum value of an empty array'); equal(_.min({a: 'a'}), Infinity, 'Minimum value of a non-numeric collection'); + deepEqual([1, 4], _.map([[1, 2, 3], [4, 5, 6]], _.min), 'Finds correct min in array when mapping through multiple arrays'); + var now = new Date(9999999999); var then = new Date(0); equal(_.min([now, then]), then); diff --git a/underscore.js b/underscore.js index c8d534b10..1f5f47050 100644 --- a/underscore.js +++ b/underscore.js @@ -305,7 +305,7 @@ _.max = function(obj, iteratee, context) { var result = -Infinity, lastComputed = -Infinity, value, computed; - if (iteratee == null && obj != null) { + if (iteratee == null || (typeof iteratee == 'number' && typeof obj[0] != 'object') && obj != null) { obj = isArrayLike(obj) ? obj : _.values(obj); for (var i = 0, length = obj.length; i < length; i++) { value = obj[i]; @@ -330,7 +330,7 @@ _.min = function(obj, iteratee, context) { var result = Infinity, lastComputed = Infinity, value, computed; - if (iteratee == null && obj != null) { + if (iteratee == null || (typeof iteratee == 'number' && typeof obj[0] != 'object') && obj != null) { obj = isArrayLike(obj) ? obj : _.values(obj); for (var i = 0, length = obj.length; i < length; i++) { value = obj[i]; From 094edb7ce5192306a90f24dbb74dd4d165aa586a Mon Sep 17 00:00:00 2001 From: Jeff Long Date: Fri, 26 Jun 2015 14:47:23 -0500 Subject: [PATCH 042/263] Fix typo in comment Noticed a small typo while checking something in the source. s/offest/offset --- underscore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/underscore.js b/underscore.js index c8d534b10..5d96f46b8 100644 --- a/underscore.js +++ b/underscore.js @@ -1479,7 +1479,7 @@ source += "';\n" + evaluate + "\n__p+='"; } - // Adobe VMs need the match returned to produce the correct offest. + // Adobe VMs need the match returned to produce the correct offset. return match; }); source += "';\n"; From 6567011ea1f3d7c0bf26571744020e39d4072a60 Mon Sep 17 00:00:00 2001 From: Graeme Yeates Date: Wed, 1 Jul 2015 11:47:13 -0400 Subject: [PATCH 043/263] Minor restArgs style tweak --- underscore.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/underscore.js b/underscore.js index 5d96f46b8..695153aa6 100644 --- a/underscore.js +++ b/underscore.js @@ -102,8 +102,7 @@ return function() { var length = Math.max(arguments.length - startIndex, 0); var rest = Array(length); - var index; - for (index = 0; index < length; index++) { + for (var index = 0; index < length; index++) { rest[index] = arguments[index + startIndex]; } switch (startIndex) { From 7fc15e5816dc02371517c4cb16a28e35f5b9a857 Mon Sep 17 00:00:00 2001 From: Graeme Yeates Date: Wed, 24 Jun 2015 11:59:22 -0400 Subject: [PATCH 044/263] Ensure underscore can be run in a Node VM --- test/utility.js | 31 +++++++++++++++++++++++++++++++ underscore.js | 8 +++++--- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/test/utility.js b/test/utility.js index c20f33a24..61c0347a4 100644 --- a/test/utility.js +++ b/test/utility.js @@ -14,6 +14,37 @@ }); + if (typeof this == 'object') { + test('noConflict', function() { + var underscore = _.noConflict(); + equal(underscore.identity(1), 1); + if (typeof require != 'function') { + equal(this._, void 0, 'global underscore is removed'); + this._ = underscore; + } + }); + } + + if (typeof require == 'function') { + asyncTest('noConflict (node vm)', 2, function() { + var fs = require('fs'); + var vm = require('vm'); + var filename = __dirname + '/../underscore.js'; + fs.readFile(filename, function(err, content){ + var sandbox = vm.createScript( + content + 'this.underscore = this._.noConflict();', + filename + ); + var context = {_: 'oldvalue'}; + sandbox.runInNewContext(context); + equal(context._, 'oldvalue'); + equal(context.underscore.VERSION, _.VERSION); + + start(); + }); + }); + } + test('#750 - Return _ instance.', 2, function() { var instance = _([]); ok(_(instance) === instance); diff --git a/underscore.js b/underscore.js index c8d534b10..387b196c6 100644 --- a/underscore.js +++ b/underscore.js @@ -8,10 +8,12 @@ // Baseline setup // -------------- - // Establish the root object, `window` (`self`) in the browser, or `global` on the server. - // We use `self` instead of `window` for `WebWorker` support. + // Establish the root object, `window` (`self`) in the browser, `global` + // on the server, or `this` in some virtual machines. We use `self` + // instead of `window` for `WebWorker` support. var root = typeof self === 'object' && self.self === self && self || - typeof global === 'object' && global.global === global && global; + typeof global === 'object' && global.global === global && global || + this; // Save the previous value of the `_` variable. var previousUnderscore = root._; From 95068507c3930f53cf828477c53fd8a3469f3307 Mon Sep 17 00:00:00 2001 From: Graeme Yeates Date: Sun, 5 Jul 2015 20:56:10 -0400 Subject: [PATCH 045/263] Remove unused optimizeCb case --- underscore.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/underscore.js b/underscore.js index 1413d1421..bd237ce3c 100644 --- a/underscore.js +++ b/underscore.js @@ -69,9 +69,6 @@ case 1: return function(value) { return func.call(context, value); }; - case 2: return function(value, other) { - return func.call(context, value, other); - }; case 3: return function(value, index, collection) { return func.call(context, value, index, collection); }; From acf826fa42ba65d42c7e5cccd78ea3a7876c8032 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Mon, 6 Jul 2015 07:58:55 -0700 Subject: [PATCH 046/263] add comment about 2-parameter case omission in optimizeCb function --- underscore.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/underscore.js b/underscore.js index bd237ce3c..7eefdb340 100644 --- a/underscore.js +++ b/underscore.js @@ -69,6 +69,8 @@ case 1: return function(value) { return func.call(context, value); }; + // The 2-parameter case has been omitted only because no current consumers + // made use of it. case 3: return function(value, index, collection) { return func.call(context, value, index, collection); }; From 1d65f77a8beff2eb8ae673bd9da47768acf2b796 Mon Sep 17 00:00:00 2001 From: Andrew Lewis Date: Thu, 9 Jul 2015 20:18:34 -0400 Subject: [PATCH 047/263] _.defaults() does not initialize for undefined/null --- test/objects.js | 4 ++-- underscore.js | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test/objects.js b/test/objects.js index 2aa19db76..92e66df80 100644 --- a/test/objects.js +++ b/test/objects.js @@ -262,8 +262,8 @@ equal(options.a, 1, 'should not error on `null` or `undefined` sources'); - strictEqual(_.defaults(null, {a: 1}), null, 'result is null if destination is null'); - strictEqual(_.defaults(void 0, {a: 1}), void 0, 'result is undefined if destination is undefined'); + deepEqual(_.defaults(null, {a: 1}), {a: 1}, 'defaults skips nulls'); + deepEqual(_.defaults(void 0, {a: 1}), {a: 1}, 'defaults skips undefined'); }); test('clone', function() { diff --git a/underscore.js b/underscore.js index bea6d0ac5..188f20968 100644 --- a/underscore.js +++ b/underscore.js @@ -1008,9 +1008,10 @@ }; // An internal function for creating assigner functions. - var createAssigner = function(keysFunc, undefinedOnly) { + var createAssigner = function(keysFunc, defaults) { return function(obj) { var length = arguments.length; + if (defaults) obj = Object(obj); if (length < 2 || obj == null) return obj; for (var index = 1; index < length; index++) { var source = arguments[index], @@ -1018,7 +1019,7 @@ l = keys.length; for (var i = 0; i < l; i++) { var key = keys[i]; - if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key]; + if (!defaults || obj[key] === void 0) obj[key] = source[key]; } } return obj; From 10026ebb7eb71db62c89cd76eb5054ce56240958 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sun, 12 Jul 2015 20:37:32 -0400 Subject: [PATCH 048/263] Fix isFunction for PhantomJS's NodeLists https://github.com/jashkenas/underscore/pull/2233#issuecomment-120677098 --- test/objects.js | 5 +++++ underscore.js | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/test/objects.js b/test/objects.js index 2aa19db76..f7f71cbbe 100644 --- a/test/objects.js +++ b/test/objects.js @@ -668,6 +668,11 @@ if (testElement) { ok(!_.isFunction(testElement), 'elements are not functions'); } + + var nodelist = typeof document != 'undefined' && document.childNodes; + if (nodelist) { + ok(!_.isFunction(nodelist)); + } }); if (typeof Int8Array !== 'undefined') { diff --git a/underscore.js b/underscore.js index bea6d0ac5..35d2e05d8 100644 --- a/underscore.js +++ b/underscore.js @@ -1271,8 +1271,9 @@ } // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8, - // IE 11 (#1621), and in Safari 8 (#1929). - if (typeof /./ != 'function' && typeof Int8Array != 'object') { + // IE 11 (#1621), Safari 8 (#1929), and PhantomJS (#2236). + var nodelist = root.document && root.document.childNodes; + if (typeof /./ != 'function' && typeof Int8Array != 'object' && typeof nodelist != 'function') { _.isFunction = function(obj) { return typeof obj == 'function' || false; }; From 30bc324a272197e92c5f47e68adade55f9739914 Mon Sep 17 00:00:00 2001 From: Adam Krebs Date: Wed, 15 Jul 2015 16:41:20 -0400 Subject: [PATCH 049/263] Fix _.unzip example. Closes #2238 --- index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index b3b80bd2c..08ee8e7b9 100644 --- a/index.html +++ b/index.html @@ -975,8 +975,8 @@

      Array Functions

      and so on.

      -_.unzip([["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]);
      -=> [['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]]
      +_.unzip([['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]])
      +=> ["moe", 30, true], ["larry", 40, false], ["curly", 50, false]
       

      From 533ecbca5b9250c6b37f8b7b098cb4dff549e9bc Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Wed, 22 Jul 2015 11:06:44 -0700 Subject: [PATCH 050/263] Fix #2242; add qunit dependency. --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 9bcfb0fc0..b2bc6eda1 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "karma-qunit": "~0.1.4", "nyc": "^2.1.3", "qunit-cli": "~0.2.0", + "qunitjs": "^1.18.0", "uglify-js": "2.4.x" }, "scripts": { From 90a49f3407487fde518ce18eb6986bd20eab5fdf Mon Sep 17 00:00:00 2001 From: Xue Hu Date: Fri, 24 Jul 2015 16:25:11 -0700 Subject: [PATCH 051/263] Clarify _.every documentation --- index.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index 08ee8e7b9..cd1725aa0 100644 --- a/index.html +++ b/index.html @@ -597,7 +597,8 @@

      Collection Functions (Arrays or Objects)

      Alias: all
      Returns true if all of the values in the list pass the - predicate truth test. + predicate truth test. Short-circuits and stops traversing the list + if a false element is found.

       _.every([true, 1, null, 'yes'], _.identity);
      
      From 4d9582350a7490db3fef5fa6bc0c889d6c9da7b3 Mon Sep 17 00:00:00 2001
      From: Petr Archakov 
      Date: Wed, 5 Nov 2014 11:48:08 +0100
      Subject: [PATCH 052/263] Add _.chunk to Array functions.
      
      fix _.chunk function
      
      fix _.chunk function
      
      Do not return original array if chunkin in more then array size parts
      
      Do not return original array if chunkin in more then array size parts
      
      spaces clean up
      ---
       test/arrays.js | 16 +++++++++++++++-
       underscore.js  | 14 ++++++++++++++
       2 files changed, 29 insertions(+), 1 deletion(-)
      
      diff --git a/test/arrays.js b/test/arrays.js
      index d41a019e3..01b6f9a6d 100644
      --- a/test/arrays.js
      +++ b/test/arrays.js
      @@ -538,5 +538,19 @@
           deepEqual(_.range(12, 7, -2), [12, 10, 8], 'range with three arguments a & b & c, a > b, c < 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b');
           deepEqual(_.range(0, -10, -1), [0, -1, -2, -3, -4, -5, -6, -7, -8, -9], 'final example in the Python docs');
         });
      -
      +  
      +  test('chunk', function() {
      +    deepEqual(_.chunk([], 2), [], 'chunk for empty array returns an empty array');
      +    deepEqual(_.chunk(null, 2), [], 'chunk for null returns an empty array');
      +    
      +    deepEqual(_.chunk([1, 2, 3], 0), [], 'chunk into parts of 0 elements returns an empty array');
      +    deepEqual(_.chunk([1, 2, 3], -1), [], 'chunk into parts of negative amount of elements returns an empty array');
      +    
      +    deepEqual(_.chunk([1, 2, 3], 3), [[1, 2, 3]], 'chunk into parts of current array length elements returns the original array');
      +    deepEqual(_.chunk([1, 2, 3], 5), [[1, 2, 3]], 'chunk into parts of more then current array length elements returns the original array');
      +    
      +    deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 2), [[10, 20], [30, 40], [50, 60], [70]], 'chunk into parts of less then current array length elements');
      +    deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 3), [[10, 20, 30], [40, 50, 60], [70]], 'chunk into parts of less then current array length elements');
      +  });
      +  
       }());
      diff --git a/underscore.js b/underscore.js
      index 35d2e05d8..03a0c7037 100644
      --- a/underscore.js
      +++ b/underscore.js
      @@ -696,6 +696,20 @@
           return range;
         };
       
      +  // Split an **array** into several arrays containing **count** or less elements
      +  // of initial array
      +  _.chunk = function(array, count) {
      +    var result = [];
      +
      +    if (array == null || count <= 0) return [];
      +
      +    for (var i = 0, length = array.length; i < length;) {
      +      result.push(array.slice(i, i += count));
      +    }
      +
      +    return result;
      +  };
      +
         // Function (ahem) Functions
         // ------------------
       
      
      From b221fe734e18a26163726c9f3bc27340e1e8f864 Mon Sep 17 00:00:00 2001
      From: Petr Archakov 
      Date: Mon, 13 Apr 2015 11:36:26 +0200
      Subject: [PATCH 053/263] chunk(): set default parts to 1
      
      ---
       test/arrays.js | 18 ++++++++++--------
       underscore.js  | 11 ++++++-----
       2 files changed, 16 insertions(+), 13 deletions(-)
      
      diff --git a/test/arrays.js b/test/arrays.js
      index 01b6f9a6d..95190daf5 100644
      --- a/test/arrays.js
      +++ b/test/arrays.js
      @@ -538,19 +538,21 @@
           deepEqual(_.range(12, 7, -2), [12, 10, 8], 'range with three arguments a & b & c, a > b, c < 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b');
           deepEqual(_.range(0, -10, -1), [0, -1, -2, -3, -4, -5, -6, -7, -8, -9], 'final example in the Python docs');
         });
      -  
      +
         test('chunk', function() {
           deepEqual(_.chunk([], 2), [], 'chunk for empty array returns an empty array');
      -    deepEqual(_.chunk(null, 2), [], 'chunk for null returns an empty array');
      -    
      -    deepEqual(_.chunk([1, 2, 3], 0), [], 'chunk into parts of 0 elements returns an empty array');
      -    deepEqual(_.chunk([1, 2, 3], -1), [], 'chunk into parts of negative amount of elements returns an empty array');
      -    
      +
      +    deepEqual(_.chunk([1, 2, 3], 0), [1, 2, 3], 'chunk into parts of 0 elements returns original array');
      +
      +    deepEqual(_.chunk([1, 2, 3], 1), [1, 2, 3], 'chunk into parts of 1 elements returns original array');
      +    deepEqual(_.chunk([1, 2, 3]), [1, 2, 3], 'chunk into parts of 1 elements is default value and returns original array');
      +
      +    deepEqual(_.chunk([1, 2, 3], -1), [1, 2, 3], 'chunk into parts of negative amount of elements returns an empty array');
      +
           deepEqual(_.chunk([1, 2, 3], 3), [[1, 2, 3]], 'chunk into parts of current array length elements returns the original array');
           deepEqual(_.chunk([1, 2, 3], 5), [[1, 2, 3]], 'chunk into parts of more then current array length elements returns the original array');
      -    
      +
           deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 2), [[10, 20], [30, 40], [50, 60], [70]], 'chunk into parts of less then current array length elements');
           deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 3), [[10, 20, 30], [40, 50, 60], [70]], 'chunk into parts of less then current array length elements');
         });
      -  
       }());
      diff --git a/underscore.js b/underscore.js
      index 03a0c7037..2cf5ceef8 100644
      --- a/underscore.js
      +++ b/underscore.js
      @@ -699,12 +699,13 @@
         // Split an **array** into several arrays containing **count** or less elements
         // of initial array
         _.chunk = function(array, count) {
      -    var result = [];
      -
      -    if (array == null || count <= 0) return [];
      +    count = +count || 1;
      +    if (count <= 1) return slice.call(array);
       
      -    for (var i = 0, length = array.length; i < length;) {
      -      result.push(array.slice(i, i += count));
      +    var result = [];
      +    var i = 0, length = array.length;
      +    while (i < length) {
      +      result.push(slice.call(array, i, i += count));
           }
       
           return result;
      
      From 4415d62d6b0fa34af1f3da0b0ebd708da8f93977 Mon Sep 17 00:00:00 2001
      From: John-David Dalton 
      Date: Tue, 28 Jul 2015 08:16:13 -0700
      Subject: [PATCH 054/263] Ensure `_.sortBy` stable sorts when iterating an
       object.
      
      ---
       test/collections.js | 15 +++++++++++----
       underscore.js       |  7 ++++---
       2 files changed, 15 insertions(+), 7 deletions(-)
      
      diff --git a/test/collections.js b/test/collections.js
      index d25e9d0fa..485eb0ce8 100644
      --- a/test/collections.js
      +++ b/test/collections.js
      @@ -632,7 +632,7 @@
             this.y = y;
           }
       
      -    var collection = [
      +    var stableArray = [
             new Pair(1, 1), new Pair(1, 2),
             new Pair(1, 3), new Pair(1, 4),
             new Pair(1, 5), new Pair(1, 6),
      @@ -644,13 +644,20 @@
             new Pair(void 0, 5), new Pair(void 0, 6)
           ];
       
      -    var actual = _.sortBy(collection, function(pair) {
      +    var stableObject = _.object('abcdefghijklmnopqr'.split(''), stableArray);
      +
      +    var actual = _.sortBy(stableArray, function(pair) {
             return pair.x;
           });
       
      -    deepEqual(actual, collection, 'sortBy should be stable');
      +    deepEqual(actual, stableArray, 'sortBy should be stable for arrays');
      +    deepEqual(_.sortBy(stableArray, 'x'), stableArray, 'sortBy accepts property string');
      +
      +    actual = _.sortBy(stableObject, function(pair) {
      +      return pair.x;
      +    });
       
      -    deepEqual(_.sortBy(collection, 'x'), collection, 'sortBy accepts property string');
      +    deepEqual(actual, stableArray, 'sortBy should be stable for objects');
       
           list = ['q', 'w', 'e', 'r', 't', 'y'];
           deepEqual(_.sortBy(list), ['e', 'q', 'r', 't', 'w', 'y'], 'uses _.identity if iterator is not specified');
      diff --git a/underscore.js b/underscore.js
      index 35d2e05d8..8ef56d251 100644
      --- a/underscore.js
      +++ b/underscore.js
      @@ -380,12 +380,13 @@
       
         // Sort the object's values by a criterion produced by an iteratee.
         _.sortBy = function(obj, iteratee, context) {
      +    var index = 0;
           iteratee = cb(iteratee, context);
      -    return _.pluck(_.map(obj, function(value, index, list) {
      +    return _.pluck(_.map(obj, function(value, key, list) {
             return {
               value: value,
      -        index: index,
      -        criteria: iteratee(value, index, list)
      +        index: index++,
      +        criteria: iteratee(value, key, list)
             };
           }).sort(function(left, right) {
             var a = left.criteria;
      
      From 97dee1323b34ba6a0a1aa672241e13603458a2bb Mon Sep 17 00:00:00 2001
      From: Graeme Yeates 
      Date: Tue, 28 Jul 2015 12:21:15 -0400
      Subject: [PATCH 055/263] Enforce 1 being the minimal chunk size #1919
      
      ---
       test/arrays.js | 9 ++++-----
       underscore.js  | 4 +---
       2 files changed, 5 insertions(+), 8 deletions(-)
      
      diff --git a/test/arrays.js b/test/arrays.js
      index 95190daf5..5b52ad433 100644
      --- a/test/arrays.js
      +++ b/test/arrays.js
      @@ -542,12 +542,11 @@
         test('chunk', function() {
           deepEqual(_.chunk([], 2), [], 'chunk for empty array returns an empty array');
       
      -    deepEqual(_.chunk([1, 2, 3], 0), [1, 2, 3], 'chunk into parts of 0 elements returns original array');
      +    deepEqual(_.chunk([1, 2, 3], 0), [], 'chunk into parts of 0 elements returns empty array');
      +    deepEqual(_.chunk([1, 2, 3], -1), [], 'chunk into parts of negative amount of elements returns an empty array');
      +    deepEqual(_.chunk([1, 2, 3]), [], 'defaults to empty array (chunk size 0)');
       
      -    deepEqual(_.chunk([1, 2, 3], 1), [1, 2, 3], 'chunk into parts of 1 elements returns original array');
      -    deepEqual(_.chunk([1, 2, 3]), [1, 2, 3], 'chunk into parts of 1 elements is default value and returns original array');
      -
      -    deepEqual(_.chunk([1, 2, 3], -1), [1, 2, 3], 'chunk into parts of negative amount of elements returns an empty array');
      +    deepEqual(_.chunk([1, 2, 3], 1), [[1], [2], [3]], 'chunk into parts of 1 elements returns original array');
       
           deepEqual(_.chunk([1, 2, 3], 3), [[1, 2, 3]], 'chunk into parts of current array length elements returns the original array');
           deepEqual(_.chunk([1, 2, 3], 5), [[1, 2, 3]], 'chunk into parts of more then current array length elements returns the original array');
      diff --git a/underscore.js b/underscore.js
      index 2cf5ceef8..4ff6a4811 100644
      --- a/underscore.js
      +++ b/underscore.js
      @@ -699,15 +699,13 @@
         // Split an **array** into several arrays containing **count** or less elements
         // of initial array
         _.chunk = function(array, count) {
      -    count = +count || 1;
      -    if (count <= 1) return slice.call(array);
      +    if (count == null || count < 1) return [];
       
           var result = [];
           var i = 0, length = array.length;
           while (i < length) {
             result.push(slice.call(array, i, i += count));
           }
      -
           return result;
         };
       
      
      From 72caece9eef35149b1dee0478198703307bfd881 Mon Sep 17 00:00:00 2001
      From: Damon Hunt 
      Date: Mon, 3 Aug 2015 17:28:24 +0800
      Subject: [PATCH 056/263] Fixes _.isNaN for wrapped numbers
      
      ---
       test/objects.js | 1 +
       underscore.js   | 2 +-
       2 files changed, 2 insertions(+), 1 deletion(-)
      
      diff --git a/test/objects.js b/test/objects.js
      index 1788ae4e1..850b328cf 100644
      --- a/test/objects.js
      +++ b/test/objects.js
      @@ -719,6 +719,7 @@
           ok(!_.isNaN(void 0), 'undefined is not NaN');
           ok(!_.isNaN(null), 'null is not NaN');
           ok(!_.isNaN(0), '0 is not NaN');
      +    ok(!_.isNaN(new Number(0)), 'wrapped 0 is not NaN');
           ok(_.isNaN(NaN), 'but NaN is');
           ok(_.isNaN(new Number(NaN)), 'wrapped NaN is still NaN');
         });
      diff --git a/underscore.js b/underscore.js
      index e9cdbde23..2db4b6179 100644
      --- a/underscore.js
      +++ b/underscore.js
      @@ -1301,7 +1301,7 @@
       
         // Is the given value `NaN`? (NaN is the only number which does not equal itself).
         _.isNaN = function(obj) {
      -    return _.isNumber(obj) && obj !== +obj;
      +    return _.isNumber(obj) && obj != +obj;
         };
       
         // Is a given value a boolean?
      
      From de6f16527f5f85e864b53f013e1cd0ddb13e263e Mon Sep 17 00:00:00 2001
      From: Damon Hunt 
      Date: Mon, 3 Aug 2015 20:52:23 +0800
      Subject: [PATCH 057/263] Fixes _.isNaN for wrapped numbers with isNaN
      
      ---
       underscore.js | 2 +-
       1 file changed, 1 insertion(+), 1 deletion(-)
      
      diff --git a/underscore.js b/underscore.js
      index 2db4b6179..92debf96d 100644
      --- a/underscore.js
      +++ b/underscore.js
      @@ -1301,7 +1301,7 @@
       
         // Is the given value `NaN`? (NaN is the only number which does not equal itself).
         _.isNaN = function(obj) {
      -    return _.isNumber(obj) && obj != +obj;
      +    return _.isNumber(obj) && isNaN(obj);
         };
       
         // Is a given value a boolean?
      
      From 0baa80709370e507d2c3d8a8724864788cec96d3 Mon Sep 17 00:00:00 2001
      From: Jon Abrams 
      Date: Mon, 3 Aug 2015 22:41:52 -0700
      Subject: [PATCH 058/263] Fixes out of date comment on _.isNaN
      
      ---
       underscore.js | 2 +-
       1 file changed, 1 insertion(+), 1 deletion(-)
      
      diff --git a/underscore.js b/underscore.js
      index 92debf96d..d513b47b6 100644
      --- a/underscore.js
      +++ b/underscore.js
      @@ -1299,7 +1299,7 @@
           return isFinite(obj) && !isNaN(parseFloat(obj));
         };
       
      -  // Is the given value `NaN`? (NaN is the only number which does not equal itself).
      +  // Is the given value `NaN`?
         _.isNaN = function(obj) {
           return _.isNumber(obj) && isNaN(obj);
         };
      
      From 7ee7064eb27b7cd7b247345e20031db53d71be3c Mon Sep 17 00:00:00 2001
      From: Matthew Hasbach 
      Date: Thu, 6 Aug 2015 06:06:30 -0400
      Subject: [PATCH 059/263] Remove LICENSE from package.json files array. NPM
       includes LICENSE automatically.
       https://docs.npmjs.com/files/package.json#files
      
      ---
       package.json | 3 +--
       1 file changed, 1 insertion(+), 2 deletions(-)
      
      diff --git a/package.json b/package.json
      index b2bc6eda1..2c39f4339 100644
      --- a/package.json
      +++ b/package.json
      @@ -41,7 +41,6 @@
         "files": [
           "underscore.js",
           "underscore-min.js",
      -    "underscore-min.map",
      -    "LICENSE"
      +    "underscore-min.map"
         ]
       }
      
      From 5edc6b288eaedf9a40d32044de55014304fcd188 Mon Sep 17 00:00:00 2001
      From: Dennis Lin 
      Date: Fri, 7 Aug 2015 13:27:19 -0700
      Subject: [PATCH 060/263] Add a navigational link to the sidebar for _.isError
      
      `_.isError` is documented but not in the sidebar.
      ---
       index.html | 1 +
       1 file changed, 1 insertion(+)
      
      diff --git a/index.html b/index.html
      index cd1725aa0..a657cab36 100644
      --- a/index.html
      +++ b/index.html
      @@ -307,6 +307,7 @@
             
    • - isBoolean
    • - isDate
    • - isRegExp
    • +
    • - isError
    • - isNaN
    • - isNull
    • - isUndefined
    • From 327e9812a592b5f5dcad7ffbee455c126d15aa84 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Sat, 8 Aug 2015 14:45:58 -0700 Subject: [PATCH 061/263] Remove duplicated test block. --- test/objects.js | 57 ------------------------------------------------- 1 file changed, 57 deletions(-) diff --git a/test/objects.js b/test/objects.js index 850b328cf..066264c2b 100644 --- a/test/objects.js +++ b/test/objects.js @@ -879,63 +879,6 @@ deepEqual(_.map([null, void 0, 5, {}], oCon), [false, false, false, true], 'doesnt falsey match constructor on undefined/null'); }); - test('matcher', function() { - var moe = {name: 'Moe Howard', hair: true}; - var curly = {name: 'Curly Howard', hair: false}; - var stooges = [moe, curly]; - - equal(_.matcher({hair: true})(moe), true, 'Returns a boolean'); - equal(_.matcher({hair: true})(curly), false, 'Returns a boolean'); - - equal(_.matcher({__x__: void 0})(5), false, 'can match undefined props on primitives'); - equal(_.matcher({__x__: void 0})({__x__: void 0}), true, 'can match undefined props'); - - equal(_.matcher({})(null), true, 'Empty spec called with null object returns true'); - equal(_.matcher({a: 1})(null), false, 'Non-empty spec called with null object returns false'); - - ok(_.find(stooges, _.matcher({hair: false})) === curly, 'returns a predicate that can be used by finding functions.'); - ok(_.find(stooges, _.matcher(moe)) === moe, 'can be used to locate an object exists in a collection.'); - deepEqual(_.where([null, void 0], {a: 1}), [], 'Do not throw on null values.'); - - deepEqual(_.where([null, void 0], null), [null, void 0], 'null matches null'); - deepEqual(_.where([null, void 0], {}), [null, void 0], 'null matches {}'); - deepEqual(_.where([{b: 1}], {a: void 0}), [], 'handles undefined values (1683)'); - - _.each([true, 5, NaN, null, void 0], function(item) { - deepEqual(_.where([{a: 1}], item), [{a: 1}], 'treats primitives as empty'); - }); - - function Prototest() {} - Prototest.prototype.x = 1; - var specObj = new Prototest; - var protospec = _.matcher(specObj); - equal(protospec({x: 2}), true, 'spec is restricted to own properties'); - - specObj.y = 5; - protospec = _.matcher(specObj); - equal(protospec({x: 1, y: 5}), true); - equal(protospec({x: 1, y: 4}), false); - - ok(_.matcher({x: 1, y: 5})(specObj), 'inherited and own properties are checked on the test object'); - - Prototest.x = 5; - ok(_.matcher(Prototest)({x: 5, y: 1}), 'spec can be a function'); - - // #1729 - var o = {b: 1}; - var m = _.matcher(o); - - equal(m({b: 1}), true); - o.b = 2; - o.a = 1; - equal(m({b: 1}), true, 'changing spec object doesnt change matches result'); - - - //null edge cases - var oCon = _.matcher({constructor: Object}); - deepEqual(_.map([null, void 0, 5, {}], oCon), [false, false, false, true], 'doesnt falsey match constructor on undefined/null'); - }); - test('findKey', function() { var objects = { a: {a: 0, b: 0}, From cabbb384728f3590167fdca199c6e177d0bb4b34 Mon Sep 17 00:00:00 2001 From: Benjamin Delespierre Date: Fri, 19 Jun 2015 16:43:18 +0200 Subject: [PATCH 062/263] Adds paragraph for bdelespierre/underscore.php lib in #links --- index.html | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index a657cab36..43469e5cc 100644 --- a/index.html +++ b/index.html @@ -2145,10 +2145,9 @@

      - Underscore.php, - a PHP port of the functions that are applicable in both languages. - Includes OOP-wrapping and chaining. - (source) + Underscore.php, + a PHP port of the functions that are applicable in both languages. Tailored for PHP 5.4 and made with data-type tolerance in mind. + (source)

      From b352144a0fa2d59a27dde6b3a47f989989f0e6c6 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Tue, 18 Aug 2015 13:15:25 -0700 Subject: [PATCH 063/263] Remove source map url in Uglify options of the package.json [closes #2145] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2c39f4339..2cb4cc47c 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "lint": "eslint --reset underscore.js test/*.js", "test-node": "qunit-cli test/*.js", "test-browser": "npm i karma-phantomjs-launcher && ./node_modules/karma/bin/karma start", - "build": "uglifyjs underscore.js -c \"evaluate=false\" --comments \"/ .*/\" -m --source-map underscore-min.map -o underscore-min.js", + "build": "uglifyjs underscore.js -c \"evaluate=false\" --comments \"/ .*/\" -m --source-map underscore-min.map --source-map-url \" \" -o underscore-min.js", "doc": "docco underscore.js" }, "license": "MIT", From b10c0203e445685133c24f9bca6609455f08da71 Mon Sep 17 00:00:00 2001 From: Adam Krebs Date: Tue, 18 Aug 2015 22:39:38 -0400 Subject: [PATCH 064/263] Mark _.identity as code using backticks. Closes #2278 --- underscore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/underscore.js b/underscore.js index d513b47b6..e00c04273 100644 --- a/underscore.js +++ b/underscore.js @@ -85,7 +85,7 @@ // A mostly-internal function to generate callbacks that can be applied // to each element in a collection, returning the desired result — either - // identity, an arbitrary callback, a property matcher, or a property accessor. + // `identity`, an arbitrary callback, a property matcher, or a property accessor. var cb = function(value, context, argCount) { if (value == null) return _.identity; if (_.isFunction(value)) return optimizeCb(value, context, argCount); From 37f62d39c2880ebabb05bcd8d90151cf289e5d88 Mon Sep 17 00:00:00 2001 From: Adam Krebs Date: Tue, 18 Aug 2015 22:41:21 -0400 Subject: [PATCH 065/263] grammar tweaks --- underscore.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/underscore.js b/underscore.js index e00c04273..b5ac00fd3 100644 --- a/underscore.js +++ b/underscore.js @@ -92,6 +92,7 @@ if (_.isObject(value)) return _.matcher(value); return _.property(value); }; + _.iteratee = function(value, context) { return cb(value, context, Infinity); }; @@ -137,7 +138,7 @@ }; // Helper for collection methods to determine whether a collection - // should be iterated as an array or as an object + // should be iterated as an array or as an object. // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094 var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; From 8b0c68267395132fa1133fa1aef1291e2a121438 Mon Sep 17 00:00:00 2001 From: wgpsutherland Date: Wed, 26 Aug 2015 17:26:55 +0100 Subject: [PATCH 066/263] Change equality comparisons of definite strings and numbers --- underscore.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/underscore.js b/underscore.js index b5ac00fd3..a8079d9e3 100644 --- a/underscore.js +++ b/underscore.js @@ -11,8 +11,8 @@ // Establish the root object, `window` (`self`) in the browser, `global` // on the server, or `this` in some virtual machines. We use `self` // instead of `window` for `WebWorker` support. - var root = typeof self === 'object' && self.self === self && self || - typeof global === 'object' && global.global === global && global || + var root = typeof self == 'object' && self.self === self && self || + typeof global == 'object' && global.global === global && global || this; // Save the previous value of the `_` variable. @@ -571,7 +571,7 @@ for (j = 1; j < argsLength; j++) { if (!_.contains(arguments[j], item)) break; } - if (j === argsLength) result.push(item); + if (j == argsLength) result.push(item); } return result; }; @@ -1149,7 +1149,7 @@ if (a !== a) return b !== b; // Exhaust primitive checks var type = typeof a; - if (type !== 'function' && type !== 'object' && typeof b !== 'object') return false; + if (type != 'function' && type != 'object' && typeof b != 'object') return false; return deepEq(a, b, aStack, bStack); }; @@ -1183,7 +1183,7 @@ return +a === +b; } - var areArrays = className === '[object Array]'; + var areArrays = className == '[object Array]'; if (!areArrays) { if (typeof a != 'object' || typeof b != 'object') return false; @@ -1251,7 +1251,7 @@ _.isEmpty = function(obj) { if (obj == null) return true; if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0; - return _.keys(obj).length === 0; + return _.keys(obj).length == 0; }; // Is a given value a DOM element? @@ -1262,19 +1262,19 @@ // Is a given value an array? // Delegates to ECMA5's native Array.isArray _.isArray = nativeIsArray || function(obj) { - return toString.call(obj) === '[object Array]'; + return toString.call(obj) == '[object Array]'; }; // Is a given variable an object? _.isObject = function(obj) { var type = typeof obj; - return type === 'function' || type === 'object' && !!obj; + return type == 'function' || type == 'object' && !!obj; }; // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError. _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) { _['is' + name] = function(obj) { - return toString.call(obj) === '[object ' + name + ']'; + return toString.call(obj) == '[object ' + name + ']'; }; }); @@ -1307,7 +1307,7 @@ // Is a given value a boolean? _.isBoolean = function(obj) { - return obj === true || obj === false || toString.call(obj) === '[object Boolean]'; + return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; }; // Is a given value equal to null? @@ -1566,7 +1566,7 @@ _.prototype[name] = function() { var obj = this._wrapped; method.apply(obj, arguments); - if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; + if ((name == 'shift' || name == 'splice') && obj.length == 0) delete obj[0]; return chainResult(this, obj); }; }); @@ -1599,7 +1599,7 @@ // popular enough to be bundled in a third party lib, but not be part of // an AMD load request. Those cases could generate an error when an // anonymous define() is called outside of a loader request. - if (typeof define === 'function' && define.amd) { + if (typeof define == 'function' && define.amd) { define('underscore', [], function() { return _; }); From 97c3f0f90590cfe257df4d844990d5c36947803f Mon Sep 17 00:00:00 2001 From: wgpsutherland Date: Wed, 26 Aug 2015 17:29:51 +0100 Subject: [PATCH 067/263] Change inequality comparisons of numbers and strings --- underscore.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/underscore.js b/underscore.js index a8079d9e3..bca2c1055 100644 --- a/underscore.js +++ b/underscore.js @@ -48,8 +48,8 @@ // Export the Underscore object for **Node.js**, with // backwards-compatibility for their old module API. If we're in // the browser, add `_` as a global object. - if (typeof exports !== 'undefined') { - if (typeof module !== 'undefined' && module.exports) { + if (typeof exports != 'undefined') { + if (typeof module != 'undefined' && module.exports) { exports = module.exports = _; } exports._ = _; @@ -1160,7 +1160,7 @@ if (b instanceof _) b = b._wrapped; // Compare `[[Class]]` names. var className = toString.call(a); - if (className !== toString.call(b)) return false; + if (className != toString.call(b)) return false; switch (className) { // Strings, numbers, regular expressions, dates, and booleans are compared by value. case '[object RegExp]': @@ -1218,7 +1218,7 @@ if (areArrays) { // Compare array lengths to determine if a deep comparison is necessary. length = a.length; - if (length !== b.length) return false; + if (length != b.length) return false; // Deep compare the contents, ignoring non-numeric properties. while (length--) { if (!eq(a[length], b[length], aStack, bStack)) return false; @@ -1228,7 +1228,7 @@ var keys = _.keys(a), key; length = keys.length; // Ensure that both objects contain the same number of properties before comparing deep equality. - if (_.keys(b).length !== length) return false; + if (_.keys(b).length != length) return false; while (length--) { // Deep compare each member key = keys[length]; From 660938d320f6c20a460316e3da40eafdb1a17df6 Mon Sep 17 00:00:00 2001 From: wgpsutherland Date: Wed, 26 Aug 2015 17:50:28 +0100 Subject: [PATCH 068/263] Change back equality comparisons that were failing in the linter --- underscore.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/underscore.js b/underscore.js index bca2c1055..7926dc293 100644 --- a/underscore.js +++ b/underscore.js @@ -571,7 +571,7 @@ for (j = 1; j < argsLength; j++) { if (!_.contains(arguments[j], item)) break; } - if (j == argsLength) result.push(item); + if (j === argsLength) result.push(item); } return result; }; @@ -1149,7 +1149,7 @@ if (a !== a) return b !== b; // Exhaust primitive checks var type = typeof a; - if (type != 'function' && type != 'object' && typeof b != 'object') return false; + if (type !== 'function' && type !== 'object' && typeof b != 'object') return false; return deepEq(a, b, aStack, bStack); }; @@ -1160,7 +1160,7 @@ if (b instanceof _) b = b._wrapped; // Compare `[[Class]]` names. var className = toString.call(a); - if (className != toString.call(b)) return false; + if (className !== toString.call(b)) return false; switch (className) { // Strings, numbers, regular expressions, dates, and booleans are compared by value. case '[object RegExp]': @@ -1183,7 +1183,7 @@ return +a === +b; } - var areArrays = className == '[object Array]'; + var areArrays = className === '[object Array]'; if (!areArrays) { if (typeof a != 'object' || typeof b != 'object') return false; @@ -1218,7 +1218,7 @@ if (areArrays) { // Compare array lengths to determine if a deep comparison is necessary. length = a.length; - if (length != b.length) return false; + if (length !== b.length) return false; // Deep compare the contents, ignoring non-numeric properties. while (length--) { if (!eq(a[length], b[length], aStack, bStack)) return false; @@ -1228,7 +1228,7 @@ var keys = _.keys(a), key; length = keys.length; // Ensure that both objects contain the same number of properties before comparing deep equality. - if (_.keys(b).length != length) return false; + if (_.keys(b).length !== length) return false; while (length--) { // Deep compare each member key = keys[length]; @@ -1251,7 +1251,7 @@ _.isEmpty = function(obj) { if (obj == null) return true; if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0; - return _.keys(obj).length == 0; + return _.keys(obj).length === 0; }; // Is a given value a DOM element? @@ -1262,19 +1262,19 @@ // Is a given value an array? // Delegates to ECMA5's native Array.isArray _.isArray = nativeIsArray || function(obj) { - return toString.call(obj) == '[object Array]'; + return toString.call(obj) === '[object Array]'; }; // Is a given variable an object? _.isObject = function(obj) { var type = typeof obj; - return type == 'function' || type == 'object' && !!obj; + return type === 'function' || type === 'object' && !!obj; }; // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError. _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) { _['is' + name] = function(obj) { - return toString.call(obj) == '[object ' + name + ']'; + return toString.call(obj) === '[object ' + name + ']'; }; }); @@ -1307,7 +1307,7 @@ // Is a given value a boolean? _.isBoolean = function(obj) { - return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; + return obj === true || obj === false || toString.call(obj) === '[object Boolean]'; }; // Is a given value equal to null? @@ -1566,7 +1566,7 @@ _.prototype[name] = function() { var obj = this._wrapped; method.apply(obj, arguments); - if ((name == 'shift' || name == 'splice') && obj.length == 0) delete obj[0]; + if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; return chainResult(this, obj); }; }); From ac5f82639c8ee082468433eff226315e5d19f02c Mon Sep 17 00:00:00 2001 From: Kevin Gibbons Date: Tue, 8 Sep 2015 16:37:58 -0700 Subject: [PATCH 069/263] fix tests for qunit 1.19.0 --- test/arrays.js | 462 +++++++++++----------- test/chaining.js | 62 +-- test/collections.js | 676 ++++++++++++++++---------------- test/cross-document.js | 102 ++--- test/functions.js | 322 ++++++++-------- test/objects.js | 846 ++++++++++++++++++++--------------------- test/utility.js | 260 ++++++------- 7 files changed, 1365 insertions(+), 1365 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index 5b52ad433..0de58e07e 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -3,135 +3,135 @@ QUnit.module('Arrays'); - test('first', function() { - equal(_.first([1, 2, 3]), 1, 'can pull out the first element of an array'); - equal(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"'); - deepEqual(_.first([1, 2, 3], 0), [], 'can pass an index to first'); - deepEqual(_.first([1, 2, 3], 2), [1, 2], 'can pass an index to first'); - deepEqual(_.first([1, 2, 3], 5), [1, 2, 3], 'can pass an index to first'); + test('first', function(assert) { + assert.equal(_.first([1, 2, 3]), 1, 'can pull out the first element of an array'); + assert.equal(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"'); + assert.deepEqual(_.first([1, 2, 3], 0), [], 'can pass an index to first'); + assert.deepEqual(_.first([1, 2, 3], 2), [1, 2], 'can pass an index to first'); + assert.deepEqual(_.first([1, 2, 3], 5), [1, 2, 3], 'can pass an index to first'); var result = (function(){ return _.first(arguments); }(4, 3, 2, 1)); - equal(result, 4, 'works on an arguments object.'); + assert.equal(result, 4, 'works on an arguments object.'); result = _.map([[1, 2, 3], [1, 2, 3]], _.first); - deepEqual(result, [1, 1], 'works well with _.map'); + assert.deepEqual(result, [1, 1], 'works well with _.map'); result = (function() { return _.first([1, 2, 3], 2); }()); - deepEqual(result, [1, 2]); + assert.deepEqual(result, [1, 2]); - equal(_.first(null), void 0, 'handles nulls'); - strictEqual(_.first([1, 2, 3], -1).length, 0); + assert.equal(_.first(null), void 0, 'handles nulls'); + assert.strictEqual(_.first([1, 2, 3], -1).length, 0); }); - test('head', function() { - strictEqual(_.first, _.head, 'alias for first'); + test('head', function(assert) { + assert.strictEqual(_.first, _.head, 'alias for first'); }); - test('take', function() { - strictEqual(_.first, _.take, 'alias for first'); + test('take', function(assert) { + assert.strictEqual(_.first, _.take, 'alias for first'); }); - test('rest', function() { + test('rest', function(assert) { var numbers = [1, 2, 3, 4]; - deepEqual(_.rest(numbers), [2, 3, 4], 'working rest()'); - deepEqual(_.rest(numbers, 0), [1, 2, 3, 4], 'working rest(0)'); - deepEqual(_.rest(numbers, 2), [3, 4], 'rest can take an index'); + assert.deepEqual(_.rest(numbers), [2, 3, 4], 'working rest()'); + assert.deepEqual(_.rest(numbers, 0), [1, 2, 3, 4], 'working rest(0)'); + assert.deepEqual(_.rest(numbers, 2), [3, 4], 'rest can take an index'); var result = (function(){ return _(arguments).rest(); }(1, 2, 3, 4)); - deepEqual(result, [2, 3, 4], 'works on arguments object'); + assert.deepEqual(result, [2, 3, 4], 'works on arguments object'); result = _.map([[1, 2, 3], [1, 2, 3]], _.rest); - deepEqual(_.flatten(result), [2, 3, 2, 3], 'works well with _.map'); + assert.deepEqual(_.flatten(result), [2, 3, 2, 3], 'works well with _.map'); result = (function(){ return _(arguments).rest(); }(1, 2, 3, 4)); - deepEqual(result, [2, 3, 4], 'works on arguments object'); + assert.deepEqual(result, [2, 3, 4], 'works on arguments object'); }); - test('tail', function() { - strictEqual(_.rest, _.tail, 'alias for rest'); + test('tail', function(assert) { + assert.strictEqual(_.rest, _.tail, 'alias for rest'); }); - test('drop', function() { - strictEqual(_.rest, _.drop, 'alias for rest'); + test('drop', function(assert) { + assert.strictEqual(_.rest, _.drop, 'alias for rest'); }); - test('initial', function() { - deepEqual(_.initial([1, 2, 3, 4, 5]), [1, 2, 3, 4], 'working initial()'); - deepEqual(_.initial([1, 2, 3, 4], 2), [1, 2], 'initial can take an index'); - deepEqual(_.initial([1, 2, 3, 4], 6), [], 'initial can take a large index'); + test('initial', function(assert) { + assert.deepEqual(_.initial([1, 2, 3, 4, 5]), [1, 2, 3, 4], 'working initial()'); + assert.deepEqual(_.initial([1, 2, 3, 4], 2), [1, 2], 'initial can take an index'); + assert.deepEqual(_.initial([1, 2, 3, 4], 6), [], 'initial can take a large index'); var result = (function(){ return _(arguments).initial(); }(1, 2, 3, 4)); - deepEqual(result, [1, 2, 3], 'initial works on arguments object'); + assert.deepEqual(result, [1, 2, 3], 'initial works on arguments object'); result = _.map([[1, 2, 3], [1, 2, 3]], _.initial); - deepEqual(_.flatten(result), [1, 2, 1, 2], 'initial works with _.map'); + assert.deepEqual(_.flatten(result), [1, 2, 1, 2], 'initial works with _.map'); }); - test('last', function() { - equal(_.last([1, 2, 3]), 3, 'can pull out the last element of an array'); - deepEqual(_.last([1, 2, 3], 0), [], 'can pass an index to last'); - deepEqual(_.last([1, 2, 3], 2), [2, 3], 'can pass an index to last'); - deepEqual(_.last([1, 2, 3], 5), [1, 2, 3], 'can pass an index to last'); + test('last', function(assert) { + assert.equal(_.last([1, 2, 3]), 3, 'can pull out the last element of an array'); + assert.deepEqual(_.last([1, 2, 3], 0), [], 'can pass an index to last'); + assert.deepEqual(_.last([1, 2, 3], 2), [2, 3], 'can pass an index to last'); + assert.deepEqual(_.last([1, 2, 3], 5), [1, 2, 3], 'can pass an index to last'); var result = (function(){ return _(arguments).last(); }(1, 2, 3, 4)); - equal(result, 4, 'works on an arguments object'); + assert.equal(result, 4, 'works on an arguments object'); result = _.map([[1, 2, 3], [1, 2, 3]], _.last); - deepEqual(result, [3, 3], 'works well with _.map'); + assert.deepEqual(result, [3, 3], 'works well with _.map'); - equal(_.last(null), void 0, 'handles nulls'); - strictEqual(_.last([1, 2, 3], -1).length, 0); + assert.equal(_.last(null), void 0, 'handles nulls'); + assert.strictEqual(_.last([1, 2, 3], -1).length, 0); }); - test('compact', function() { - equal(_.compact([0, 1, false, 2, false, 3]).length, 3, 'can trim out all falsy values'); + test('compact', function(assert) { + assert.equal(_.compact([0, 1, false, 2, false, 3]).length, 3, 'can trim out all falsy values'); var result = (function(){ return _.compact(arguments).length; }(0, 1, false, 2, false, 3)); - equal(result, 3, 'works on an arguments object'); + assert.equal(result, 3, 'works on an arguments object'); }); - test('flatten', function() { - deepEqual(_.flatten(null), [], 'Flattens supports null'); - deepEqual(_.flatten(void 0), [], 'Flattens supports undefined'); + test('flatten', function(assert) { + assert.deepEqual(_.flatten(null), [], 'Flattens supports null'); + assert.deepEqual(_.flatten(void 0), [], 'Flattens supports undefined'); - deepEqual(_.flatten([[], [[]], []]), [], 'Flattens empty arrays'); - deepEqual(_.flatten([[], [[]], []], true), [[]], 'Flattens empty arrays'); + assert.deepEqual(_.flatten([[], [[]], []]), [], 'Flattens empty arrays'); + assert.deepEqual(_.flatten([[], [[]], []], true), [[]], 'Flattens empty arrays'); var list = [1, [2], [3, [[[4]]]]]; - deepEqual(_.flatten(list), [1, 2, 3, 4], 'can flatten nested arrays'); - deepEqual(_.flatten(list, true), [1, 2, 3, [[[4]]]], 'can shallowly flatten nested arrays'); + assert.deepEqual(_.flatten(list), [1, 2, 3, 4], 'can flatten nested arrays'); + assert.deepEqual(_.flatten(list, true), [1, 2, 3, [[[4]]]], 'can shallowly flatten nested arrays'); var result = (function(){ return _.flatten(arguments); }(1, [2], [3, [[[4]]]])); - deepEqual(result, [1, 2, 3, 4], 'works on an arguments object'); + assert.deepEqual(result, [1, 2, 3, 4], 'works on an arguments object'); list = [[1], [2], [3], [[4]]]; - deepEqual(_.flatten(list, true), [1, 2, 3, [4]], 'can shallowly flatten arrays containing only other arrays'); + assert.deepEqual(_.flatten(list, true), [1, 2, 3, [4]], 'can shallowly flatten arrays containing only other arrays'); - equal(_.flatten([_.range(10), _.range(10), 5, 1, 3], true).length, 23); - equal(_.flatten([_.range(10), _.range(10), 5, 1, 3]).length, 23); - equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3]).length, 1056003, 'Flatten can handle massive collections'); - equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3], true).length, 1056003, 'Flatten can handle massive collections'); + assert.equal(_.flatten([_.range(10), _.range(10), 5, 1, 3], true).length, 23); + assert.equal(_.flatten([_.range(10), _.range(10), 5, 1, 3]).length, 23); + assert.equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3]).length, 1056003, 'Flatten can handle massive collections'); + assert.equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3], true).length, 1056003, 'Flatten can handle massive collections'); var x = _.range(100000); for (var i = 0; i < 1000; i++) x = [x]; - deepEqual(_.flatten(x), _.range(100000), 'Flatten can handle very deep arrays'); - deepEqual(_.flatten(x, true), x[0], 'Flatten can handle very deep arrays with shallow'); + assert.deepEqual(_.flatten(x), _.range(100000), 'Flatten can handle very deep arrays'); + assert.deepEqual(_.flatten(x, true), x[0], 'Flatten can handle very deep arrays with shallow'); }); - test('without', function() { + test('without', function(assert) { var list = [1, 2, 1, 0, 3, 1, 4]; - deepEqual(_.without(list, 0, 1), [2, 3, 4], 'can remove all instances of an object'); + assert.deepEqual(_.without(list, 0, 1), [2, 3, 4], 'can remove all instances of an object'); var result = (function(){ return _.without(arguments, 0, 1); }(1, 2, 1, 0, 3, 1, 4)); - deepEqual(result, [2, 3, 4], 'works on an arguments object'); + assert.deepEqual(result, [2, 3, 4], 'works on an arguments object'); list = [{one: 1}, {two: 2}]; - equal(_.without(list, {one: 1}).length, 2, 'uses real object identity for comparisons.'); - equal(_.without(list, list[0]).length, 1, 'ditto.'); + assert.equal(_.without(list, {one: 1}).length, 2, 'uses real object identity for comparisons.'); + assert.equal(_.without(list, list[0]).length, 1, 'ditto.'); }); - test('sortedIndex', function() { + test('sortedIndex', function(assert) { var numbers = [10, 20, 30, 40, 50], num = 35; var indexForNum = _.sortedIndex(numbers, num); - equal(indexForNum, 3, '35 should be inserted at index 3'); + assert.equal(indexForNum, 3, '35 should be inserted at index 3'); var indexFor30 = _.sortedIndex(numbers, 30); - equal(indexFor30, 2, '30 should be inserted at index 2'); + assert.equal(indexFor30, 2, '30 should be inserted at index 2'); var objects = [{x: 10}, {x: 20}, {x: 30}, {x: 40}]; var iterator = function(obj){ return obj.x; }; - strictEqual(_.sortedIndex(objects, {x: 25}, iterator), 2); - strictEqual(_.sortedIndex(objects, {x: 35}, 'x'), 3); + assert.strictEqual(_.sortedIndex(objects, {x: 25}, iterator), 2); + assert.strictEqual(_.sortedIndex(objects, {x: 35}, 'x'), 3); var context = {1: 2, 2: 3, 3: 4}; iterator = function(obj){ return this[obj]; }; - strictEqual(_.sortedIndex([1, 3], 2, iterator, context), 1); + assert.strictEqual(_.sortedIndex([1, 3], 2, iterator, context), 1); var values = [0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535, 131071, 262143, 524287, 1048575, 2097151, 4194303, 8388607, 16777215, 33554431, 67108863, 134217727, 268435455, 536870911, 1073741823, 2147483647]; var array = Array(Math.pow(2, 32) - 1); @@ -139,25 +139,25 @@ while (length--) { array[values[length]] = values[length]; } - equal(_.sortedIndex(array, 2147483648), 2147483648, 'should work with large indexes'); + assert.equal(_.sortedIndex(array, 2147483648), 2147483648, 'should work with large indexes'); }); - test('uniq', function() { + test('uniq', function(assert) { var list = [1, 2, 1, 3, 1, 4]; - deepEqual(_.uniq(list), [1, 2, 3, 4], 'can find the unique values of an unsorted array'); + assert.deepEqual(_.uniq(list), [1, 2, 3, 4], 'can find the unique values of an unsorted array'); list = [1, 1, 1, 2, 2, 3]; - deepEqual(_.uniq(list, true), [1, 2, 3], 'can find the unique values of a sorted array faster'); + assert.deepEqual(_.uniq(list, true), [1, 2, 3], 'can find the unique values of a sorted array faster'); list = [{name: 'moe'}, {name: 'curly'}, {name: 'larry'}, {name: 'curly'}]; var iterator = function(value) { return value.name; }; - deepEqual(_.map(_.uniq(list, false, iterator), iterator), ['moe', 'curly', 'larry'], 'can find the unique values of an array using a custom iterator'); + assert.deepEqual(_.map(_.uniq(list, false, iterator), iterator), ['moe', 'curly', 'larry'], 'can find the unique values of an array using a custom iterator'); - deepEqual(_.map(_.uniq(list, iterator), iterator), ['moe', 'curly', 'larry'], 'can find the unique values of an array using a custom iterator without specifying whether array is sorted'); + assert.deepEqual(_.map(_.uniq(list, iterator), iterator), ['moe', 'curly', 'larry'], 'can find the unique values of an array using a custom iterator without specifying whether array is sorted'); iterator = function(value) { return value + 1; }; list = [1, 2, 2, 3, 4, 4]; - deepEqual(_.uniq(list, true, iterator), [1, 2, 3, 4], 'iterator works with sorted array'); + assert.deepEqual(_.uniq(list, true, iterator), [1, 2, 3, 4], 'iterator works with sorted array'); var kittens = [ {kitten: 'Celery', cuteness: 8}, @@ -170,236 +170,236 @@ {kitten: 'Juniper', cuteness: 10} ]; - deepEqual(_.uniq(kittens, true, 'cuteness'), expected, 'string iterator works with sorted array'); + assert.deepEqual(_.uniq(kittens, true, 'cuteness'), expected, 'string iterator works with sorted array'); var result = (function(){ return _.uniq(arguments); }(1, 2, 1, 3, 1, 4)); - deepEqual(result, [1, 2, 3, 4], 'works on an arguments object'); + assert.deepEqual(result, [1, 2, 3, 4], 'works on an arguments object'); var a = {}, b = {}, c = {}; - deepEqual(_.uniq([a, b, a, b, c]), [a, b, c], 'works on values that can be tested for equivalency but not ordered'); + assert.deepEqual(_.uniq([a, b, a, b, c]), [a, b, c], 'works on values that can be tested for equivalency but not ordered'); - deepEqual(_.uniq(null), []); + assert.deepEqual(_.uniq(null), []); var context = {}; list = [3]; _.uniq(list, function(value, index, array) { - strictEqual(this, context); - strictEqual(value, 3); - strictEqual(index, 0); - strictEqual(array, list); + assert.strictEqual(this, context); + assert.strictEqual(value, 3); + assert.strictEqual(index, 0); + assert.strictEqual(array, list); }, context); - deepEqual(_.uniq([{a: 1, b: 1}, {a: 1, b: 2}, {a: 1, b: 3}, {a: 2, b: 1}], 'a'), [{a: 1, b: 1}, {a: 2, b: 1}], 'can use pluck like iterator'); - deepEqual(_.uniq([{0: 1, b: 1}, {0: 1, b: 2}, {0: 1, b: 3}, {0: 2, b: 1}], 0), [{0: 1, b: 1}, {0: 2, b: 1}], 'can use falsey pluck like iterator'); + assert.deepEqual(_.uniq([{a: 1, b: 1}, {a: 1, b: 2}, {a: 1, b: 3}, {a: 2, b: 1}], 'a'), [{a: 1, b: 1}, {a: 2, b: 1}], 'can use pluck like iterator'); + assert.deepEqual(_.uniq([{0: 1, b: 1}, {0: 1, b: 2}, {0: 1, b: 3}, {0: 2, b: 1}], 0), [{0: 1, b: 1}, {0: 2, b: 1}], 'can use falsey pluck like iterator'); }); - test('unique', function() { - strictEqual(_.uniq, _.unique, 'alias for uniq'); + test('unique', function(assert) { + assert.strictEqual(_.uniq, _.unique, 'alias for uniq'); }); - test('intersection', function() { + test('intersection', function(assert) { var stooges = ['moe', 'curly', 'larry'], leaders = ['moe', 'groucho']; - deepEqual(_.intersection(stooges, leaders), ['moe'], 'can take the set intersection of two arrays'); - deepEqual(_(stooges).intersection(leaders), ['moe'], 'can perform an OO-style intersection'); + assert.deepEqual(_.intersection(stooges, leaders), ['moe'], 'can take the set intersection of two arrays'); + assert.deepEqual(_(stooges).intersection(leaders), ['moe'], 'can perform an OO-style intersection'); var result = (function(){ return _.intersection(arguments, leaders); }('moe', 'curly', 'larry')); - deepEqual(result, ['moe'], 'works on an arguments object'); + assert.deepEqual(result, ['moe'], 'works on an arguments object'); var theSixStooges = ['moe', 'moe', 'curly', 'curly', 'larry', 'larry']; - deepEqual(_.intersection(theSixStooges, leaders), ['moe'], 'returns a duplicate-free array'); + assert.deepEqual(_.intersection(theSixStooges, leaders), ['moe'], 'returns a duplicate-free array'); result = _.intersection([2, 4, 3, 1], [1, 2, 3]); - deepEqual(result, [2, 3, 1], 'preserves order of first array'); + assert.deepEqual(result, [2, 3, 1], 'preserves order of first array'); result = _.intersection(null, [1, 2, 3]); - equal(Object.prototype.toString.call(result), '[object Array]', 'returns an empty array when passed null as first argument'); - equal(result.length, 0, 'returns an empty array when passed null as first argument'); + assert.equal(Object.prototype.toString.call(result), '[object Array]', 'returns an empty array when passed null as first argument'); + assert.equal(result.length, 0, 'returns an empty array when passed null as first argument'); result = _.intersection([1, 2, 3], null); - equal(Object.prototype.toString.call(result), '[object Array]', 'returns an empty array when passed null as argument beyond the first'); - equal(result.length, 0, 'returns an empty array when passed null as argument beyond the first'); + assert.equal(Object.prototype.toString.call(result), '[object Array]', 'returns an empty array when passed null as argument beyond the first'); + assert.equal(result.length, 0, 'returns an empty array when passed null as argument beyond the first'); }); - test('union', function() { + test('union', function(assert) { var result = _.union([1, 2, 3], [2, 30, 1], [1, 40]); - deepEqual(result, [1, 2, 3, 30, 40], 'takes the union of a list of arrays'); + assert.deepEqual(result, [1, 2, 3, 30, 40], 'takes the union of a list of arrays'); result = _.union([1, 2, 3], [2, 30, 1], [1, 40, [1]]); - deepEqual(result, [1, 2, 3, 30, 40, [1]], 'takes the union of a list of nested arrays'); + assert.deepEqual(result, [1, 2, 3, 30, 40, [1]], 'takes the union of a list of nested arrays'); var args = null; (function(){ args = arguments; }(1, 2, 3)); result = _.union(args, [2, 30, 1], [1, 40]); - deepEqual(result, [1, 2, 3, 30, 40], 'takes the union of a list of arrays'); + assert.deepEqual(result, [1, 2, 3, 30, 40], 'takes the union of a list of arrays'); result = _.union([1, 2, 3], 4); - deepEqual(result, [1, 2, 3], 'restrict the union to arrays only'); + assert.deepEqual(result, [1, 2, 3], 'restrict the union to arrays only'); }); - test('difference', function() { + test('difference', function(assert) { var result = _.difference([1, 2, 3], [2, 30, 40]); - deepEqual(result, [1, 3], 'takes the difference of two arrays'); + assert.deepEqual(result, [1, 3], 'takes the difference of two arrays'); result = _.difference([1, 2, 3, 4], [2, 30, 40], [1, 11, 111]); - deepEqual(result, [3, 4], 'takes the difference of three arrays'); + assert.deepEqual(result, [3, 4], 'takes the difference of three arrays'); result = _.difference([1, 2, 3], 1); - deepEqual(result, [1, 2, 3], 'restrict the difference to arrays only'); + assert.deepEqual(result, [1, 2, 3], 'restrict the difference to arrays only'); }); - test('zip', function() { + test('zip', function(assert) { var names = ['moe', 'larry', 'curly'], ages = [30, 40, 50], leaders = [true]; - deepEqual(_.zip(names, ages, leaders), [ + assert.deepEqual(_.zip(names, ages, leaders), [ ['moe', 30, true], ['larry', 40, void 0], ['curly', 50, void 0] ], 'zipped together arrays of different lengths'); var stooges = _.zip(['moe', 30, 'stooge 1'], ['larry', 40, 'stooge 2'], ['curly', 50, 'stooge 3']); - deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], ['stooge 1', 'stooge 2', 'stooge 3']], 'zipped pairs'); + assert.deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], ['stooge 1', 'stooge 2', 'stooge 3']], 'zipped pairs'); // In the case of difference lengths of the tuples undefineds // should be used as placeholder stooges = _.zip(['moe', 30], ['larry', 40], ['curly', 50, 'extra data']); - deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], [void 0, void 0, 'extra data']], 'zipped pairs with empties'); + assert.deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], [void 0, void 0, 'extra data']], 'zipped pairs with empties'); var empty = _.zip([]); - deepEqual(empty, [], 'unzipped empty'); + assert.deepEqual(empty, [], 'unzipped empty'); - deepEqual(_.zip(null), [], 'handles null'); - deepEqual(_.zip(), [], '_.zip() returns []'); + assert.deepEqual(_.zip(null), [], 'handles null'); + assert.deepEqual(_.zip(), [], '_.zip() returns []'); }); - test('unzip', function() { - deepEqual(_.unzip(null), [], 'handles null'); + test('unzip', function(assert) { + assert.deepEqual(_.unzip(null), [], 'handles null'); - deepEqual(_.unzip([['a', 'b'], [1, 2]]), [['a', 1], ['b', 2]]); + assert.deepEqual(_.unzip([['a', 'b'], [1, 2]]), [['a', 1], ['b', 2]]); // complements zip var zipped = _.zip(['fred', 'barney'], [30, 40], [true, false]); - deepEqual(_.unzip(zipped), [['fred', 'barney'], [30, 40], [true, false]]); + assert.deepEqual(_.unzip(zipped), [['fred', 'barney'], [30, 40], [true, false]]); zipped = _.zip(['moe', 30], ['larry', 40], ['curly', 50, 'extra data']); - deepEqual(_.unzip(zipped), [['moe', 30, void 0], ['larry', 40, void 0], ['curly', 50, 'extra data']], 'Uses length of largest array'); + assert.deepEqual(_.unzip(zipped), [['moe', 30, void 0], ['larry', 40, void 0], ['curly', 50, 'extra data']], 'Uses length of largest array'); }); - test('object', function() { + test('object', function(assert) { var result = _.object(['moe', 'larry', 'curly'], [30, 40, 50]); var shouldBe = {moe: 30, larry: 40, curly: 50}; - deepEqual(result, shouldBe, 'two arrays zipped together into an object'); + assert.deepEqual(result, shouldBe, 'two arrays zipped together into an object'); result = _.object([['one', 1], ['two', 2], ['three', 3]]); shouldBe = {one: 1, two: 2, three: 3}; - deepEqual(result, shouldBe, 'an array of pairs zipped together into an object'); + assert.deepEqual(result, shouldBe, 'an array of pairs zipped together into an object'); var stooges = {moe: 30, larry: 40, curly: 50}; - deepEqual(_.object(_.pairs(stooges)), stooges, 'an object converted to pairs and back to an object'); + assert.deepEqual(_.object(_.pairs(stooges)), stooges, 'an object converted to pairs and back to an object'); - deepEqual(_.object(null), {}, 'handles nulls'); + assert.deepEqual(_.object(null), {}, 'handles nulls'); }); - test('indexOf', function() { + test('indexOf', function(assert) { var numbers = [1, 2, 3]; - equal(_.indexOf(numbers, 2), 1, 'can compute indexOf'); + assert.equal(_.indexOf(numbers, 2), 1, 'can compute indexOf'); var result = (function(){ return _.indexOf(arguments, 2); }(1, 2, 3)); - equal(result, 1, 'works on an arguments object'); + assert.equal(result, 1, 'works on an arguments object'); _.each([null, void 0, [], false], function(val) { var msg = 'Handles: ' + (_.isArray(val) ? '[]' : val); - equal(_.indexOf(val, 2), -1, msg); - equal(_.indexOf(val, 2, -1), -1, msg); - equal(_.indexOf(val, 2, -20), -1, msg); - equal(_.indexOf(val, 2, 15), -1, msg); + assert.equal(_.indexOf(val, 2), -1, msg); + assert.equal(_.indexOf(val, 2, -1), -1, msg); + assert.equal(_.indexOf(val, 2, -20), -1, msg); + assert.equal(_.indexOf(val, 2, 15), -1, msg); }); var num = 35; numbers = [10, 20, 30, 40, 50]; var index = _.indexOf(numbers, num, true); - equal(index, -1, '35 is not in the list'); + assert.equal(index, -1, '35 is not in the list'); numbers = [10, 20, 30, 40, 50]; num = 40; index = _.indexOf(numbers, num, true); - equal(index, 3, '40 is in the list'); + assert.equal(index, 3, '40 is in the list'); numbers = [1, 40, 40, 40, 40, 40, 40, 40, 50, 60, 70]; num = 40; - equal(_.indexOf(numbers, num, true), 1, '40 is in the list'); - equal(_.indexOf(numbers, 6, true), -1, '6 isnt in the list'); - equal(_.indexOf([1, 2, 5, 4, 6, 7], 5, true), -1, 'sorted indexOf doesn\'t uses binary search'); - ok(_.every(['1', [], {}, null], function() { + assert.equal(_.indexOf(numbers, num, true), 1, '40 is in the list'); + assert.equal(_.indexOf(numbers, 6, true), -1, '6 isnt in the list'); + assert.equal(_.indexOf([1, 2, 5, 4, 6, 7], 5, true), -1, 'sorted indexOf doesn\'t uses binary search'); + assert.ok(_.every(['1', [], {}, null], function() { return _.indexOf(numbers, num, {}) === 1; }), 'non-nums as fromIndex make indexOf assume sorted'); numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; index = _.indexOf(numbers, 2, 5); - equal(index, 7, 'supports the fromIndex argument'); + assert.equal(index, 7, 'supports the fromIndex argument'); index = _.indexOf([,,, 0], void 0); - equal(index, 0, 'treats sparse arrays as if they were dense'); + assert.equal(index, 0, 'treats sparse arrays as if they were dense'); var array = [1, 2, 3, 1, 2, 3]; - strictEqual(_.indexOf(array, 1, -3), 3, 'neg `fromIndex` starts at the right index'); - strictEqual(_.indexOf(array, 1, -2), -1, 'neg `fromIndex` starts at the right index'); - strictEqual(_.indexOf(array, 2, -3), 4); + assert.strictEqual(_.indexOf(array, 1, -3), 3, 'neg `fromIndex` starts at the right index'); + assert.strictEqual(_.indexOf(array, 1, -2), -1, 'neg `fromIndex` starts at the right index'); + assert.strictEqual(_.indexOf(array, 2, -3), 4); _.each([-6, -8, -Infinity], function(fromIndex) { - strictEqual(_.indexOf(array, 1, fromIndex), 0); + assert.strictEqual(_.indexOf(array, 1, fromIndex), 0); }); - strictEqual(_.indexOf([1, 2, 3], 1, true), 0); + assert.strictEqual(_.indexOf([1, 2, 3], 1, true), 0); index = _.indexOf([], void 0, true); - equal(index, -1, 'empty array with truthy `isSorted` returns -1'); + assert.equal(index, -1, 'empty array with truthy `isSorted` returns -1'); }); - test('indexOf with NaN', function() { - strictEqual(_.indexOf([1, 2, NaN, NaN], NaN), 2, 'Expected [1, 2, NaN] to contain NaN'); - strictEqual(_.indexOf([1, 2, Infinity], NaN), -1, 'Expected [1, 2, NaN] to contain NaN'); + test('indexOf with NaN', function(assert) { + assert.strictEqual(_.indexOf([1, 2, NaN, NaN], NaN), 2, 'Expected [1, 2, NaN] to contain NaN'); + assert.strictEqual(_.indexOf([1, 2, Infinity], NaN), -1, 'Expected [1, 2, NaN] to contain NaN'); - strictEqual(_.indexOf([1, 2, NaN, NaN], NaN, 1), 2, 'startIndex does not affect result'); - strictEqual(_.indexOf([1, 2, NaN, NaN], NaN, -2), 2, 'startIndex does not affect result'); + assert.strictEqual(_.indexOf([1, 2, NaN, NaN], NaN, 1), 2, 'startIndex does not affect result'); + assert.strictEqual(_.indexOf([1, 2, NaN, NaN], NaN, -2), 2, 'startIndex does not affect result'); (function() { - strictEqual(_.indexOf(arguments, NaN), 2, 'Expected arguments [1, 2, NaN] to contain NaN'); + assert.strictEqual(_.indexOf(arguments, NaN), 2, 'Expected arguments [1, 2, NaN] to contain NaN'); }(1, 2, NaN, NaN)); }); - test('indexOf with +- 0', function() { + test('indexOf with +- 0', function(assert) { _.each([-0, +0], function(val) { - strictEqual(_.indexOf([1, 2, val, val], val), 2); - strictEqual(_.indexOf([1, 2, val, val], -val), 2); + assert.strictEqual(_.indexOf([1, 2, val, val], val), 2); + assert.strictEqual(_.indexOf([1, 2, val, val], -val), 2); }); }); - test('lastIndexOf', function() { + test('lastIndexOf', function(assert) { var numbers = [1, 0, 1]; var falsey = [void 0, '', 0, false, NaN, null, void 0]; - equal(_.lastIndexOf(numbers, 1), 2); + assert.equal(_.lastIndexOf(numbers, 1), 2); numbers = [1, 0, 1, 0, 0, 1, 0, 0, 0]; numbers.lastIndexOf = null; - equal(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function'); - equal(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element'); + assert.equal(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function'); + assert.equal(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element'); var result = (function(){ return _.lastIndexOf(arguments, 1); }(1, 0, 1, 0, 0, 1, 0, 0, 0)); - equal(result, 5, 'works on an arguments object'); + assert.equal(result, 5, 'works on an arguments object'); _.each([null, void 0, [], false], function(val) { var msg = 'Handles: ' + (_.isArray(val) ? '[]' : val); - equal(_.lastIndexOf(val, 2), -1, msg); - equal(_.lastIndexOf(val, 2, -1), -1, msg); - equal(_.lastIndexOf(val, 2, -20), -1, msg); - equal(_.lastIndexOf(val, 2, 15), -1, msg); + assert.equal(_.lastIndexOf(val, 2), -1, msg); + assert.equal(_.lastIndexOf(val, 2, -1), -1, msg); + assert.equal(_.lastIndexOf(val, 2, -20), -1, msg); + assert.equal(_.lastIndexOf(val, 2, 15), -1, msg); }); numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; var index = _.lastIndexOf(numbers, 2, 2); - equal(index, 1, 'supports the fromIndex argument'); + assert.equal(index, 1, 'supports the fromIndex argument'); var array = [1, 2, 3, 1, 2, 3]; - strictEqual(_.lastIndexOf(array, 1, 0), 0, 'starts at the correct from idx'); - strictEqual(_.lastIndexOf(array, 3), 5, 'should return the index of the last matched value'); - strictEqual(_.lastIndexOf(array, 4), -1, 'should return `-1` for an unmatched value'); + assert.strictEqual(_.lastIndexOf(array, 1, 0), 0, 'starts at the correct from idx'); + assert.strictEqual(_.lastIndexOf(array, 3), 5, 'should return the index of the last matched value'); + assert.strictEqual(_.lastIndexOf(array, 4), -1, 'should return `-1` for an unmatched value'); - strictEqual(_.lastIndexOf(array, 1, 2), 0, 'should work with a positive `fromIndex`'); + assert.strictEqual(_.lastIndexOf(array, 1, 2), 0, 'should work with a positive `fromIndex`'); _.each([6, 8, Math.pow(2, 32), Infinity], function(fromIndex) { - strictEqual(_.lastIndexOf(array, void 0, fromIndex), -1); - strictEqual(_.lastIndexOf(array, 1, fromIndex), 3); - strictEqual(_.lastIndexOf(array, '', fromIndex), -1); + assert.strictEqual(_.lastIndexOf(array, void 0, fromIndex), -1); + assert.strictEqual(_.lastIndexOf(array, 1, fromIndex), 3); + assert.strictEqual(_.lastIndexOf(array, '', fromIndex), -1); }); var expected = _.map(falsey, function(value) { @@ -410,39 +410,39 @@ return _.lastIndexOf(array, 3, fromIndex); }); - deepEqual(actual, expected, 'should treat falsey `fromIndex` values, except `0` and `NaN`, as `array.length`'); - strictEqual(_.lastIndexOf(array, 3, '1'), 5, 'should treat non-number `fromIndex` values as `array.length`'); - strictEqual(_.lastIndexOf(array, 3, true), 5, 'should treat non-number `fromIndex` values as `array.length`'); + assert.deepEqual(actual, expected, 'should treat falsey `fromIndex` values, except `0` and `NaN`, as `array.length`'); + assert.strictEqual(_.lastIndexOf(array, 3, '1'), 5, 'should treat non-number `fromIndex` values as `array.length`'); + assert.strictEqual(_.lastIndexOf(array, 3, true), 5, 'should treat non-number `fromIndex` values as `array.length`'); - strictEqual(_.lastIndexOf(array, 2, -3), 1, 'should work with a negative `fromIndex`'); - strictEqual(_.lastIndexOf(array, 1, -3), 3, 'neg `fromIndex` starts at the right index'); + assert.strictEqual(_.lastIndexOf(array, 2, -3), 1, 'should work with a negative `fromIndex`'); + assert.strictEqual(_.lastIndexOf(array, 1, -3), 3, 'neg `fromIndex` starts at the right index'); - deepEqual(_.map([-6, -8, -Infinity], function(fromIndex) { + assert.deepEqual(_.map([-6, -8, -Infinity], function(fromIndex) { return _.lastIndexOf(array, 1, fromIndex); }), [0, -1, -1]); }); - test('lastIndexOf with NaN', function() { - strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN), 3, 'Expected [1, 2, NaN] to contain NaN'); - strictEqual(_.lastIndexOf([1, 2, Infinity], NaN), -1, 'Expected [1, 2, NaN] to contain NaN'); + test('lastIndexOf with NaN', function(assert) { + assert.strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN), 3, 'Expected [1, 2, NaN] to contain NaN'); + assert.strictEqual(_.lastIndexOf([1, 2, Infinity], NaN), -1, 'Expected [1, 2, NaN] to contain NaN'); - strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN, 2), 2, 'fromIndex does not affect result'); - strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN, -2), 2, 'fromIndex does not affect result'); + assert.strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN, 2), 2, 'fromIndex does not affect result'); + assert.strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN, -2), 2, 'fromIndex does not affect result'); (function() { - strictEqual(_.lastIndexOf(arguments, NaN), 3, 'Expected arguments [1, 2, NaN] to contain NaN'); + assert.strictEqual(_.lastIndexOf(arguments, NaN), 3, 'Expected arguments [1, 2, NaN] to contain NaN'); }(1, 2, NaN, NaN)); }); - test('lastIndexOf with +- 0', function() { + test('lastIndexOf with +- 0', function(assert) { _.each([-0, +0], function(val) { - strictEqual(_.lastIndexOf([1, 2, val, val], val), 3); - strictEqual(_.lastIndexOf([1, 2, val, val], -val), 3); - strictEqual(_.lastIndexOf([-1, 1, 2], -val), -1); + assert.strictEqual(_.lastIndexOf([1, 2, val, val], val), 3); + assert.strictEqual(_.lastIndexOf([1, 2, val, val], -val), 3); + assert.strictEqual(_.lastIndexOf([-1, 1, 2], -val), -1); }); }); - test('findIndex', function() { + test('findIndex', function(assert) { var objects = [ {a: 0, b: 0}, {a: 1, b: 1}, @@ -450,42 +450,42 @@ {a: 0, b: 0} ]; - equal(_.findIndex(objects, function(obj) { + assert.equal(_.findIndex(objects, function(obj) { return obj.a === 0; }), 0); - equal(_.findIndex(objects, function(obj) { + assert.equal(_.findIndex(objects, function(obj) { return obj.b * obj.a === 4; }), 2); - equal(_.findIndex(objects, 'a'), 1, 'Uses lookupIterator'); + assert.equal(_.findIndex(objects, 'a'), 1, 'Uses lookupIterator'); - equal(_.findIndex(objects, function(obj) { + assert.equal(_.findIndex(objects, function(obj) { return obj.b * obj.a === 5; }), -1); - equal(_.findIndex(null, _.noop), -1); - strictEqual(_.findIndex(objects, function(a) { + assert.equal(_.findIndex(null, _.noop), -1); + assert.strictEqual(_.findIndex(objects, function(a) { return a.foo === null; }), -1); _.findIndex([{a: 1}], function(a, key, obj) { - equal(key, 0); - deepEqual(obj, [{a: 1}]); - strictEqual(this, objects, 'called with context'); + assert.equal(key, 0); + assert.deepEqual(obj, [{a: 1}]); + assert.strictEqual(this, objects, 'called with context'); }, objects); var sparse = []; sparse[20] = {a: 2, b: 2}; - equal(_.findIndex(sparse, function(obj) { + assert.equal(_.findIndex(sparse, function(obj) { return obj && obj.b * obj.a === 4; }), 20, 'Works with sparse arrays'); var array = [1, 2, 3, 4]; array.match = 55; - strictEqual(_.findIndex(array, function(x) { return x === 55; }), -1, 'doesn\'t match array-likes keys'); + assert.strictEqual(_.findIndex(array, function(x) { return x === 55; }), -1, 'doesn\'t match array-likes keys'); }); - test('findLastIndex', function() { + test('findLastIndex', function(assert) { var objects = [ {a: 0, b: 0}, {a: 1, b: 1}, @@ -493,65 +493,65 @@ {a: 0, b: 0} ]; - equal(_.findLastIndex(objects, function(obj) { + assert.equal(_.findLastIndex(objects, function(obj) { return obj.a === 0; }), 3); - equal(_.findLastIndex(objects, function(obj) { + assert.equal(_.findLastIndex(objects, function(obj) { return obj.b * obj.a === 4; }), 2); - equal(_.findLastIndex(objects, 'a'), 2, 'Uses lookupIterator'); + assert.equal(_.findLastIndex(objects, 'a'), 2, 'Uses lookupIterator'); - equal(_.findLastIndex(objects, function(obj) { + assert.equal(_.findLastIndex(objects, function(obj) { return obj.b * obj.a === 5; }), -1); - equal(_.findLastIndex(null, _.noop), -1); - strictEqual(_.findLastIndex(objects, function(a) { + assert.equal(_.findLastIndex(null, _.noop), -1); + assert.strictEqual(_.findLastIndex(objects, function(a) { return a.foo === null; }), -1); _.findLastIndex([{a: 1}], function(a, key, obj) { - equal(key, 0); - deepEqual(obj, [{a: 1}]); - strictEqual(this, objects, 'called with context'); + assert.equal(key, 0); + assert.deepEqual(obj, [{a: 1}]); + assert.strictEqual(this, objects, 'called with context'); }, objects); var sparse = []; sparse[20] = {a: 2, b: 2}; - equal(_.findLastIndex(sparse, function(obj) { + assert.equal(_.findLastIndex(sparse, function(obj) { return obj && obj.b * obj.a === 4; }), 20, 'Works with sparse arrays'); var array = [1, 2, 3, 4]; array.match = 55; - strictEqual(_.findLastIndex(array, function(x) { return x === 55; }), -1, 'doesn\'t match array-likes keys'); + assert.strictEqual(_.findLastIndex(array, function(x) { return x === 55; }), -1, 'doesn\'t match array-likes keys'); }); - test('range', function() { - deepEqual(_.range(0), [], 'range with 0 as a first argument generates an empty array'); - deepEqual(_.range(4), [0, 1, 2, 3], 'range with a single positive argument generates an array of elements 0,1,2,...,n-1'); - deepEqual(_.range(5, 8), [5, 6, 7], 'range with two arguments a & b, a<b generates an array of elements a,a+1,a+2,...,b-2,b-1'); - deepEqual(_.range(8, 5), [], 'range with two arguments a & b, b<a generates an empty array'); - deepEqual(_.range(3, 10, 3), [3, 6, 9], 'range with three arguments a & b & c, c < b-a, a < b generates an array of elements a,a+c,a+2c,...,b - (multiplier of a) < c'); - deepEqual(_.range(3, 10, 15), [3], 'range with three arguments a & b & c, c > b-a, a < b generates an array with a single element, equal to a'); - deepEqual(_.range(12, 7, -2), [12, 10, 8], 'range with three arguments a & b & c, a > b, c < 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b'); - deepEqual(_.range(0, -10, -1), [0, -1, -2, -3, -4, -5, -6, -7, -8, -9], 'final example in the Python docs'); + test('range', function(assert) { + assert.deepEqual(_.range(0), [], 'range with 0 as a first argument generates an empty array'); + assert.deepEqual(_.range(4), [0, 1, 2, 3], 'range with a single positive argument generates an array of elements 0,1,2,...,n-1'); + assert.deepEqual(_.range(5, 8), [5, 6, 7], 'range with two arguments a & b, a<b generates an array of elements a,a+1,a+2,...,b-2,b-1'); + assert.deepEqual(_.range(8, 5), [], 'range with two arguments a & b, b<a generates an empty array'); + assert.deepEqual(_.range(3, 10, 3), [3, 6, 9], 'range with three arguments a & b & c, c < b-a, a < b generates an array of elements a,a+c,a+2c,...,b - (multiplier of a) < c'); + assert.deepEqual(_.range(3, 10, 15), [3], 'range with three arguments a & b & c, c > b-a, a < b generates an array with a single element, equal to a'); + assert.deepEqual(_.range(12, 7, -2), [12, 10, 8], 'range with three arguments a & b & c, a > b, c < 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b'); + assert.deepEqual(_.range(0, -10, -1), [0, -1, -2, -3, -4, -5, -6, -7, -8, -9], 'final example in the Python docs'); }); - test('chunk', function() { - deepEqual(_.chunk([], 2), [], 'chunk for empty array returns an empty array'); + test('chunk', function(assert) { + assert.deepEqual(_.chunk([], 2), [], 'chunk for empty array returns an empty array'); - deepEqual(_.chunk([1, 2, 3], 0), [], 'chunk into parts of 0 elements returns empty array'); - deepEqual(_.chunk([1, 2, 3], -1), [], 'chunk into parts of negative amount of elements returns an empty array'); - deepEqual(_.chunk([1, 2, 3]), [], 'defaults to empty array (chunk size 0)'); + assert.deepEqual(_.chunk([1, 2, 3], 0), [], 'chunk into parts of 0 elements returns empty array'); + assert.deepEqual(_.chunk([1, 2, 3], -1), [], 'chunk into parts of negative amount of elements returns an empty array'); + assert.deepEqual(_.chunk([1, 2, 3]), [], 'defaults to empty array (chunk size 0)'); - deepEqual(_.chunk([1, 2, 3], 1), [[1], [2], [3]], 'chunk into parts of 1 elements returns original array'); + assert.deepEqual(_.chunk([1, 2, 3], 1), [[1], [2], [3]], 'chunk into parts of 1 elements returns original array'); - deepEqual(_.chunk([1, 2, 3], 3), [[1, 2, 3]], 'chunk into parts of current array length elements returns the original array'); - deepEqual(_.chunk([1, 2, 3], 5), [[1, 2, 3]], 'chunk into parts of more then current array length elements returns the original array'); + assert.deepEqual(_.chunk([1, 2, 3], 3), [[1, 2, 3]], 'chunk into parts of current array length elements returns the original array'); + assert.deepEqual(_.chunk([1, 2, 3], 5), [[1, 2, 3]], 'chunk into parts of more then current array length elements returns the original array'); - deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 2), [[10, 20], [30, 40], [50, 60], [70]], 'chunk into parts of less then current array length elements'); - deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 3), [[10, 20, 30], [40, 50, 60], [70]], 'chunk into parts of less then current array length elements'); + assert.deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 2), [[10, 20], [30, 40], [50, 60], [70]], 'chunk into parts of less then current array length elements'); + assert.deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 3), [[10, 20, 30], [40, 50, 60], [70]], 'chunk into parts of less then current array length elements'); }); }()); diff --git a/test/chaining.js b/test/chaining.js index c5830f36f..1e708b90b 100644 --- a/test/chaining.js +++ b/test/chaining.js @@ -3,7 +3,7 @@ QUnit.module('Chaining'); - test('map/flatten/reduce', function() { + test('map/flatten/reduce', function(assert) { var lyrics = [ 'I\'m a lumberjack and I\'m okay', 'I sleep all night and I work all day', @@ -19,11 +19,11 @@ return hash; }, {}) .value(); - equal(counts.a, 16, 'counted all the letters in the song'); - equal(counts.e, 10, 'counted all the letters in the song'); + assert.equal(counts.a, 16, 'counted all the letters in the song'); + assert.equal(counts.e, 10, 'counted all the letters in the song'); }); - test('select/reject/sortBy', function() { + test('select/reject/sortBy', function(assert) { var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; numbers = _(numbers).chain().select(function(n) { return n % 2 === 0; @@ -32,10 +32,10 @@ }).sortBy(function(n) { return -n; }).value(); - deepEqual(numbers, [10, 6, 2], 'filtered and reversed the numbers'); + assert.deepEqual(numbers, [10, 6, 2], 'filtered and reversed the numbers'); }); - test('select/reject/sortBy in functional style', function() { + test('select/reject/sortBy in functional style', function(assert) { var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; numbers = _.chain(numbers).select(function(n) { return n % 2 === 0; @@ -44,10 +44,10 @@ }).sortBy(function(n) { return -n; }).value(); - deepEqual(numbers, [10, 6, 2], 'filtered and reversed the numbers'); + assert.deepEqual(numbers, [10, 6, 2], 'filtered and reversed the numbers'); }); - test('reverse/concat/unshift/pop/map', function() { + test('reverse/concat/unshift/pop/map', function(assert) { var numbers = [1, 2, 3, 4, 5]; numbers = _(numbers).chain() .reverse() @@ -56,44 +56,44 @@ .pop() .map(function(n){ return n * 2; }) .value(); - deepEqual(numbers, [34, 10, 8, 6, 4, 2, 10, 10], 'can chain together array functions.'); + assert.deepEqual(numbers, [34, 10, 8, 6, 4, 2, 10, 10], 'can chain together array functions.'); }); - test('splice', function() { + test('splice', function(assert) { var instance = _([1, 2, 3, 4, 5]).chain(); - deepEqual(instance.splice(1, 3).value(), [1, 5]); - deepEqual(instance.splice(1, 0).value(), [1, 5]); - deepEqual(instance.splice(1, 1).value(), [1]); - deepEqual(instance.splice(0, 1).value(), [], '#397 Can create empty array'); + assert.deepEqual(instance.splice(1, 3).value(), [1, 5]); + assert.deepEqual(instance.splice(1, 0).value(), [1, 5]); + assert.deepEqual(instance.splice(1, 1).value(), [1]); + assert.deepEqual(instance.splice(0, 1).value(), [], '#397 Can create empty array'); }); - test('shift', function() { + test('shift', function(assert) { var instance = _([1, 2, 3]).chain(); - deepEqual(instance.shift().value(), [2, 3]); - deepEqual(instance.shift().value(), [3]); - deepEqual(instance.shift().value(), [], '#397 Can create empty array'); + assert.deepEqual(instance.shift().value(), [2, 3]); + assert.deepEqual(instance.shift().value(), [3]); + assert.deepEqual(instance.shift().value(), [], '#397 Can create empty array'); }); - test('pop', function() { + test('pop', function(assert) { var instance = _([1, 2, 3]).chain(); - deepEqual(instance.pop().value(), [1, 2]); - deepEqual(instance.pop().value(), [1]); - deepEqual(instance.pop().value(), [], '#397 Can create empty array'); + assert.deepEqual(instance.pop().value(), [1, 2]); + assert.deepEqual(instance.pop().value(), [1]); + assert.deepEqual(instance.pop().value(), [], '#397 Can create empty array'); }); - test('chaining works in small stages', function() { + test('chaining works in small stages', function(assert) { var o = _([1, 2, 3, 4]).chain(); - deepEqual(o.filter(function(i) { return i < 3; }).value(), [1, 2]); - deepEqual(o.filter(function(i) { return i > 2; }).value(), [3, 4]); + assert.deepEqual(o.filter(function(i) { return i < 3; }).value(), [1, 2]); + assert.deepEqual(o.filter(function(i) { return i > 2; }).value(), [3, 4]); }); - test('#1562: Engine proxies for chained functions', function() { + test('#1562: Engine proxies for chained functions', function(assert) { var wrapped = _(512); - strictEqual(wrapped.toJSON(), 512); - strictEqual(wrapped.valueOf(), 512); - strictEqual(+wrapped, 512); - strictEqual(wrapped.toString(), '512'); - strictEqual('' + wrapped, '512'); + assert.strictEqual(wrapped.toJSON(), 512); + assert.strictEqual(wrapped.valueOf(), 512); + assert.strictEqual(+wrapped, 512); + assert.strictEqual(wrapped.toString(), '512'); + assert.strictEqual('' + wrapped, '512'); }); }()); diff --git a/test/collections.js b/test/collections.js index 485eb0ce8..c718bab80 100644 --- a/test/collections.js +++ b/test/collections.js @@ -3,24 +3,24 @@ QUnit.module('Collections'); - test('each', function() { + test('each', function(assert) { _.each([1, 2, 3], function(num, i) { - equal(num, i + 1, 'each iterators provide value and iteration count'); + assert.equal(num, i + 1, 'each iterators provide value and iteration count'); }); var answers = []; _.each([1, 2, 3], function(num){ answers.push(num * this.multiplier); }, {multiplier: 5}); - deepEqual(answers, [5, 10, 15], 'context object property accessed'); + assert.deepEqual(answers, [5, 10, 15], 'context object property accessed'); answers = []; _.each([1, 2, 3], function(num){ answers.push(num); }); - deepEqual(answers, [1, 2, 3], 'aliased as "forEach"'); + assert.deepEqual(answers, [1, 2, 3], 'aliased as "forEach"'); answers = []; var obj = {one: 1, two: 2, three: 3}; obj.constructor.prototype.four = 4; _.each(obj, function(value, key){ answers.push(key); }); - deepEqual(answers, ['one', 'two', 'three'], 'iterating over objects works, and ignores the object prototype.'); + assert.deepEqual(answers, ['one', 'two', 'three'], 'iterating over objects works, and ignores the object prototype.'); delete obj.constructor.prototype.four; // ensure the each function is JITed @@ -28,36 +28,36 @@ var count = 0; obj = {1: 'foo', 2: 'bar', 3: 'baz'}; _.each(obj, function(){ count++; }); - equal(count, 3, 'the fun should be called only 3 times'); + assert.equal(count, 3, 'the fun should be called only 3 times'); var answer = null; _.each([1, 2, 3], function(num, index, arr){ if (_.include(arr, num)) answer = true; }); - ok(answer, 'can reference the original collection from inside the iterator'); + assert.ok(answer, 'can reference the original collection from inside the iterator'); answers = 0; _.each(null, function(){ ++answers; }); - equal(answers, 0, 'handles a null properly'); + assert.equal(answers, 0, 'handles a null properly'); _.each(false, function(){}); var a = [1, 2, 3]; - strictEqual(_.each(a, function(){}), a); - strictEqual(_.each(null, function(){}), null); + assert.strictEqual(_.each(a, function(){}), a); + assert.strictEqual(_.each(null, function(){}), null); }); - test('forEach', function() { - strictEqual(_.each, _.forEach, 'alias for each'); + test('forEach', function(assert) { + assert.strictEqual(_.each, _.forEach, 'alias for each'); }); - test('lookupIterator with contexts', function() { + test('lookupIterator with contexts', function(assert) { _.each([true, false, 'yes', '', 0, 1, {}], function(context) { _.each([1], function() { - equal(this, context); + assert.equal(this, context); }, context); }); }); - test('Iterating objects with sketchy length properties', function() { + test('Iterating objects with sketchy length properties', function(assert) { var functions = [ 'each', 'map', 'filter', 'find', 'some', 'every', 'max', 'min', @@ -79,29 +79,29 @@ {length: new Number(15)} ]; - expect(tricks.length * (functions.length + reducers.length + 4)); + assert.expect(tricks.length * (functions.length + reducers.length + 4)); _.each(tricks, function(trick) { var length = trick.length; - strictEqual(_.size(trick), 1, 'size on obj with length: ' + length); - deepEqual(_.toArray(trick), [length], 'toArray on obj with length: ' + length); - deepEqual(_.shuffle(trick), [length], 'shuffle on obj with length: ' + length); - deepEqual(_.sample(trick), length, 'sample on obj with length: ' + length); + assert.strictEqual(_.size(trick), 1, 'size on obj with length: ' + length); + assert.deepEqual(_.toArray(trick), [length], 'toArray on obj with length: ' + length); + assert.deepEqual(_.shuffle(trick), [length], 'shuffle on obj with length: ' + length); + assert.deepEqual(_.sample(trick), length, 'sample on obj with length: ' + length); _.each(functions, function(method) { _[method](trick, function(val, key) { - strictEqual(key, 'length', method + ': ran with length = ' + val); + assert.strictEqual(key, 'length', method + ': ran with length = ' + val); }); }); _.each(reducers, function(method) { - strictEqual(_[method](trick), trick.length, method); + assert.strictEqual(_[method](trick), trick.length, method); }); }); }); - test('Resistant to collection length and properties changing while iterating', function() { + test('Resistant to collection length and properties changing while iterating', function(assert) { var collection = [ 'each', 'map', 'filter', 'find', @@ -124,14 +124,14 @@ ++answers; return method === 'every' ? true : null; }, {}); - equal(answers, 100, method + ' enumerates [0, length)'); + assert.equal(answers, 100, method + ' enumerates [0, length)'); var growingCollection = [1, 2, 3], count = 0; _[method](growingCollection, function() { if (count < 10) growingCollection.push(count++); return method === 'every' ? true : null; }, {}); - equal(count, 3, method + ' is resistant to length changes'); + assert.equal(count, 3, method + ' is resistant to length changes'); }); _.each(collection.concat(object), function(method) { @@ -141,85 +141,85 @@ return method === 'every' ? true : null; }, {}); - equal(count, 2, method + ' is resistant to property changes'); + assert.equal(count, 2, method + ' is resistant to property changes'); }); }); - test('map', function() { + test('map', function(assert) { var doubled = _.map([1, 2, 3], function(num){ return num * 2; }); - deepEqual(doubled, [2, 4, 6], 'doubled numbers'); + assert.deepEqual(doubled, [2, 4, 6], 'doubled numbers'); var tripled = _.map([1, 2, 3], function(num){ return num * this.multiplier; }, {multiplier: 3}); - deepEqual(tripled, [3, 6, 9], 'tripled numbers with context'); + assert.deepEqual(tripled, [3, 6, 9], 'tripled numbers with context'); doubled = _([1, 2, 3]).map(function(num){ return num * 2; }); - deepEqual(doubled, [2, 4, 6], 'OO-style doubled numbers'); + assert.deepEqual(doubled, [2, 4, 6], 'OO-style doubled numbers'); var ids = _.map({length: 2, 0: {id: '1'}, 1: {id: '2'}}, function(n){ return n.id; }); - deepEqual(ids, ['1', '2'], 'Can use collection methods on Array-likes.'); + assert.deepEqual(ids, ['1', '2'], 'Can use collection methods on Array-likes.'); - deepEqual(_.map(null, _.noop), [], 'handles a null properly'); + assert.deepEqual(_.map(null, _.noop), [], 'handles a null properly'); - deepEqual(_.map([1], function() { + assert.deepEqual(_.map([1], function() { return this.length; }, [5]), [1], 'called with context'); // Passing a property name like _.pluck. var people = [{name: 'moe', age: 30}, {name: 'curly', age: 50}]; - deepEqual(_.map(people, 'name'), ['moe', 'curly'], 'predicate string map to object properties'); + assert.deepEqual(_.map(people, 'name'), ['moe', 'curly'], 'predicate string map to object properties'); }); - test('collect', function() { - strictEqual(_.map, _.collect, 'alias for map'); + test('collect', function(assert) { + assert.strictEqual(_.map, _.collect, 'alias for map'); }); - test('reduce', function() { + test('reduce', function(assert) { var sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0); - equal(sum, 6, 'can sum up an array'); + assert.equal(sum, 6, 'can sum up an array'); var context = {multiplier: 3}; sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num * this.multiplier; }, 0, context); - equal(sum, 18, 'can reduce with a context object'); + assert.equal(sum, 18, 'can reduce with a context object'); sum = _.inject([1, 2, 3], function(memo, num){ return memo + num; }, 0); - equal(sum, 6, 'aliased as "inject"'); + assert.equal(sum, 6, 'aliased as "inject"'); sum = _([1, 2, 3]).reduce(function(memo, num){ return memo + num; }, 0); - equal(sum, 6, 'OO-style reduce'); + assert.equal(sum, 6, 'OO-style reduce'); sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }); - equal(sum, 6, 'default initial value'); + assert.equal(sum, 6, 'default initial value'); var prod = _.reduce([1, 2, 3, 4], function(memo, num){ return memo * num; }); - equal(prod, 24, 'can reduce via multiplication'); + assert.equal(prod, 24, 'can reduce via multiplication'); - ok(_.reduce(null, _.noop, 138) === 138, 'handles a null (with initial value) properly'); - equal(_.reduce([], _.noop, void 0), void 0, 'undefined can be passed as a special case'); - equal(_.reduce([_], _.noop), _, 'collection of length one with no initial value returns the first item'); - equal(_.reduce([], _.noop), void 0, 'returns undefined when collection is empty and no initial value'); + assert.ok(_.reduce(null, _.noop, 138) === 138, 'handles a null (with initial value) properly'); + assert.equal(_.reduce([], _.noop, void 0), void 0, 'undefined can be passed as a special case'); + assert.equal(_.reduce([_], _.noop), _, 'collection of length one with no initial value returns the first item'); + assert.equal(_.reduce([], _.noop), void 0, 'returns undefined when collection is empty and no initial value'); }); - test('foldl', function() { - strictEqual(_.reduce, _.foldl, 'alias for reduce'); + test('foldl', function(assert) { + assert.strictEqual(_.reduce, _.foldl, 'alias for reduce'); }); - test('reduceRight', function() { + test('reduceRight', function(assert) { var list = _.reduceRight(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; }, ''); - equal(list, 'bazbarfoo', 'can perform right folds'); + assert.equal(list, 'bazbarfoo', 'can perform right folds'); list = _.reduceRight(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; }); - equal(list, 'bazbarfoo', 'default initial value'); + assert.equal(list, 'bazbarfoo', 'default initial value'); var sum = _.reduceRight({a: 1, b: 2, c: 3}, function(memo, num){ return memo + num; }); - equal(sum, 6, 'default initial value on object'); + assert.equal(sum, 6, 'default initial value on object'); - ok(_.reduceRight(null, _.noop, 138) === 138, 'handles a null (with initial value) properly'); - equal(_.reduceRight([_], _.noop), _, 'collection of length one with no initial value returns the first item'); + assert.ok(_.reduceRight(null, _.noop, 138) === 138, 'handles a null (with initial value) properly'); + assert.equal(_.reduceRight([_], _.noop), _, 'collection of length one with no initial value returns the first item'); - equal(_.reduceRight([], _.noop, void 0), void 0, 'undefined can be passed as a special case'); - equal(_.reduceRight([], _.noop), void 0, 'returns undefined when collection is empty and no initial value'); + assert.equal(_.reduceRight([], _.noop, void 0), void 0, 'undefined can be passed as a special case'); + assert.equal(_.reduceRight([], _.noop), void 0, 'returns undefined when collection is empty and no initial value'); // Assert that the correct arguments are being passed. @@ -236,7 +236,7 @@ if (!args) args = _.toArray(arguments); }, init); - deepEqual(args, expected); + assert.deepEqual(args, expected); // And again, with numeric keys. @@ -252,30 +252,30 @@ if (!args) args = _.toArray(arguments); }, init); - deepEqual(args, expected); + assert.deepEqual(args, expected); }); - test('foldr', function() { - strictEqual(_.reduceRight, _.foldr, 'alias for reduceRight'); + test('foldr', function(assert) { + assert.strictEqual(_.reduceRight, _.foldr, 'alias for reduceRight'); }); - test('find', function() { + test('find', function(assert) { var array = [1, 2, 3, 4]; - strictEqual(_.find(array, function(n) { return n > 2; }), 3, 'should return first found `value`'); - strictEqual(_.find(array, function() { return false; }), void 0, 'should return `undefined` if `value` is not found'); + assert.strictEqual(_.find(array, function(n) { return n > 2; }), 3, 'should return first found `value`'); + assert.strictEqual(_.find(array, function() { return false; }), void 0, 'should return `undefined` if `value` is not found'); array.dontmatch = 55; - strictEqual(_.find(array, function(x) { return x === 55; }), void 0, 'iterates array-likes correctly'); + assert.strictEqual(_.find(array, function(x) { return x === 55; }), void 0, 'iterates array-likes correctly'); // Matching an object like _.findWhere. var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}, {a: 2, b: 4}]; - deepEqual(_.find(list, {a: 1}), {a: 1, b: 2}, 'can be used as findWhere'); - deepEqual(_.find(list, {b: 4}), {a: 1, b: 4}); - ok(!_.find(list, {c: 1}), 'undefined when not found'); - ok(!_.find([], {c: 1}), 'undefined when searching empty list'); + assert.deepEqual(_.find(list, {a: 1}), {a: 1, b: 2}, 'can be used as findWhere'); + assert.deepEqual(_.find(list, {b: 4}), {a: 1, b: 4}); + assert.ok(!_.find(list, {c: 1}), 'undefined when not found'); + assert.ok(!_.find([], {c: 1}), 'undefined when searching empty list'); var result = _.find([1, 2, 3], function(num){ return num * 2 === 4; }); - equal(result, 2, 'found the first "2" and broke the loop'); + assert.equal(result, 2, 'found the first "2" and broke the loop'); var obj = { a: {x: 1, z: 3}, @@ -284,348 +284,348 @@ d: {x: 4, z: 1} }; - deepEqual(_.find(obj, {x: 2}), {x: 2, z: 2}, 'works on objects'); - deepEqual(_.find(obj, {x: 2, z: 1}), void 0); - deepEqual(_.find(obj, function(x) { + assert.deepEqual(_.find(obj, {x: 2}), {x: 2, z: 2}, 'works on objects'); + assert.deepEqual(_.find(obj, {x: 2, z: 1}), void 0); + assert.deepEqual(_.find(obj, function(x) { return x.x === 4; }), {x: 4, z: 1}); _.findIndex([{a: 1}], function(a, key, o) { - equal(key, 0); - deepEqual(o, [{a: 1}]); - strictEqual(this, _, 'called with context'); + assert.equal(key, 0); + assert.deepEqual(o, [{a: 1}]); + assert.strictEqual(this, _, 'called with context'); }, _); }); - test('detect', function() { - strictEqual(_.detect, _.find, 'alias for detect'); + test('detect', function(assert) { + assert.strictEqual(_.detect, _.find, 'alias for detect'); }); - test('filter', function() { + test('filter', function(assert) { var evenArray = [1, 2, 3, 4, 5, 6]; var evenObject = {one: 1, two: 2, three: 3}; var isEven = function(num){ return num % 2 === 0; }; - deepEqual(_.filter(evenArray, isEven), [2, 4, 6]); - deepEqual(_.filter(evenObject, isEven), [2], 'can filter objects'); - deepEqual(_.filter([{}, evenObject, []], 'two'), [evenObject], 'predicate string map to object properties'); + assert.deepEqual(_.filter(evenArray, isEven), [2, 4, 6]); + assert.deepEqual(_.filter(evenObject, isEven), [2], 'can filter objects'); + assert.deepEqual(_.filter([{}, evenObject, []], 'two'), [evenObject], 'predicate string map to object properties'); _.filter([1], function() { - equal(this, evenObject, 'given context'); + assert.equal(this, evenObject, 'given context'); }, evenObject); // Can be used like _.where. var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; - deepEqual(_.filter(list, {a: 1}), [{a: 1, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]); - deepEqual(_.filter(list, {b: 2}), [{a: 1, b: 2}, {a: 2, b: 2}]); - deepEqual(_.filter(list, {}), list, 'Empty object accepts all items'); - deepEqual(_(list).filter({}), list, 'OO-filter'); + assert.deepEqual(_.filter(list, {a: 1}), [{a: 1, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]); + assert.deepEqual(_.filter(list, {b: 2}), [{a: 1, b: 2}, {a: 2, b: 2}]); + assert.deepEqual(_.filter(list, {}), list, 'Empty object accepts all items'); + assert.deepEqual(_(list).filter({}), list, 'OO-filter'); }); - test('select', function() { - strictEqual(_.filter, _.select, 'alias for filter'); + test('select', function(assert) { + assert.strictEqual(_.filter, _.select, 'alias for filter'); }); - test('reject', function() { + test('reject', function(assert) { var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 === 0; }); - deepEqual(odds, [1, 3, 5], 'rejected each even number'); + assert.deepEqual(odds, [1, 3, 5], 'rejected each even number'); var context = 'obj'; var evens = _.reject([1, 2, 3, 4, 5, 6], function(num){ - equal(context, 'obj'); + assert.equal(context, 'obj'); return num % 2 !== 0; }, context); - deepEqual(evens, [2, 4, 6], 'rejected each odd number'); + assert.deepEqual(evens, [2, 4, 6], 'rejected each odd number'); - deepEqual(_.reject([odds, {one: 1, two: 2, three: 3}], 'two'), [odds], 'predicate string map to object properties'); + assert.deepEqual(_.reject([odds, {one: 1, two: 2, three: 3}], 'two'), [odds], 'predicate string map to object properties'); // Can be used like _.where. var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; - deepEqual(_.reject(list, {a: 1}), [{a: 2, b: 2}]); - deepEqual(_.reject(list, {b: 2}), [{a: 1, b: 3}, {a: 1, b: 4}]); - deepEqual(_.reject(list, {}), [], 'Returns empty list given empty object'); - deepEqual(_.reject(list, []), [], 'Returns empty list given empty array'); - }); - - test('every', function() { - ok(_.every([], _.identity), 'the empty set'); - ok(_.every([true, true, true], _.identity), 'every true values'); - ok(!_.every([true, false, true], _.identity), 'one false value'); - ok(_.every([0, 10, 28], function(num){ return num % 2 === 0; }), 'even numbers'); - ok(!_.every([0, 11, 28], function(num){ return num % 2 === 0; }), 'an odd number'); - ok(_.every([1], _.identity) === true, 'cast to boolean - true'); - ok(_.every([0], _.identity) === false, 'cast to boolean - false'); - ok(!_.every([void 0, void 0, void 0], _.identity), 'works with arrays of undefined'); + assert.deepEqual(_.reject(list, {a: 1}), [{a: 2, b: 2}]); + assert.deepEqual(_.reject(list, {b: 2}), [{a: 1, b: 3}, {a: 1, b: 4}]); + assert.deepEqual(_.reject(list, {}), [], 'Returns empty list given empty object'); + assert.deepEqual(_.reject(list, []), [], 'Returns empty list given empty array'); + }); + + test('every', function(assert) { + assert.ok(_.every([], _.identity), 'the empty set'); + assert.ok(_.every([true, true, true], _.identity), 'every true values'); + assert.ok(!_.every([true, false, true], _.identity), 'one false value'); + assert.ok(_.every([0, 10, 28], function(num){ return num % 2 === 0; }), 'even numbers'); + assert.ok(!_.every([0, 11, 28], function(num){ return num % 2 === 0; }), 'an odd number'); + assert.ok(_.every([1], _.identity) === true, 'cast to boolean - true'); + assert.ok(_.every([0], _.identity) === false, 'cast to boolean - false'); + assert.ok(!_.every([void 0, void 0, void 0], _.identity), 'works with arrays of undefined'); var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; - ok(!_.every(list, {a: 1, b: 2}), 'Can be called with object'); - ok(_.every(list, 'a'), 'String mapped to object property'); + assert.ok(!_.every(list, {a: 1, b: 2}), 'Can be called with object'); + assert.ok(_.every(list, 'a'), 'String mapped to object property'); list = [{a: 1, b: 2}, {a: 2, b: 2, c: true}]; - ok(_.every(list, {b: 2}), 'Can be called with object'); - ok(!_.every(list, 'c'), 'String mapped to object property'); + assert.ok(_.every(list, {b: 2}), 'Can be called with object'); + assert.ok(!_.every(list, 'c'), 'String mapped to object property'); - ok(_.every({a: 1, b: 2, c: 3, d: 4}, _.isNumber), 'takes objects'); - ok(!_.every({a: 1, b: 2, c: 3, d: 4}, _.isObject), 'takes objects'); - ok(_.every(['a', 'b', 'c', 'd'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); - ok(!_.every(['a', 'b', 'c', 'd', 'f'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); + assert.ok(_.every({a: 1, b: 2, c: 3, d: 4}, _.isNumber), 'takes objects'); + assert.ok(!_.every({a: 1, b: 2, c: 3, d: 4}, _.isObject), 'takes objects'); + assert.ok(_.every(['a', 'b', 'c', 'd'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); + assert.ok(!_.every(['a', 'b', 'c', 'd', 'f'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); }); - test('all', function() { - strictEqual(_.all, _.every, 'alias for all'); + test('all', function(assert) { + assert.strictEqual(_.all, _.every, 'alias for all'); }); - test('some', function() { - ok(!_.some([]), 'the empty set'); - ok(!_.some([false, false, false]), 'all false values'); - ok(_.some([false, false, true]), 'one true value'); - ok(_.some([null, 0, 'yes', false]), 'a string'); - ok(!_.some([null, 0, '', false]), 'falsy values'); - ok(!_.some([1, 11, 29], function(num){ return num % 2 === 0; }), 'all odd numbers'); - ok(_.some([1, 10, 29], function(num){ return num % 2 === 0; }), 'an even number'); - ok(_.some([1], _.identity) === true, 'cast to boolean - true'); - ok(_.some([0], _.identity) === false, 'cast to boolean - false'); - ok(_.some([false, false, true])); + test('some', function(assert) { + assert.ok(!_.some([]), 'the empty set'); + assert.ok(!_.some([false, false, false]), 'all false values'); + assert.ok(_.some([false, false, true]), 'one true value'); + assert.ok(_.some([null, 0, 'yes', false]), 'a string'); + assert.ok(!_.some([null, 0, '', false]), 'falsy values'); + assert.ok(!_.some([1, 11, 29], function(num){ return num % 2 === 0; }), 'all odd numbers'); + assert.ok(_.some([1, 10, 29], function(num){ return num % 2 === 0; }), 'an even number'); + assert.ok(_.some([1], _.identity) === true, 'cast to boolean - true'); + assert.ok(_.some([0], _.identity) === false, 'cast to boolean - false'); + assert.ok(_.some([false, false, true])); var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; - ok(!_.some(list, {a: 5, b: 2}), 'Can be called with object'); - ok(_.some(list, 'a'), 'String mapped to object property'); + assert.ok(!_.some(list, {a: 5, b: 2}), 'Can be called with object'); + assert.ok(_.some(list, 'a'), 'String mapped to object property'); list = [{a: 1, b: 2}, {a: 2, b: 2, c: true}]; - ok(_.some(list, {b: 2}), 'Can be called with object'); - ok(!_.some(list, 'd'), 'String mapped to object property'); + assert.ok(_.some(list, {b: 2}), 'Can be called with object'); + assert.ok(!_.some(list, 'd'), 'String mapped to object property'); - ok(_.some({a: '1', b: '2', c: '3', d: '4', e: 6}, _.isNumber), 'takes objects'); - ok(!_.some({a: 1, b: 2, c: 3, d: 4}, _.isObject), 'takes objects'); - ok(_.some(['a', 'b', 'c', 'd'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); - ok(!_.some(['x', 'y', 'z'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); + assert.ok(_.some({a: '1', b: '2', c: '3', d: '4', e: 6}, _.isNumber), 'takes objects'); + assert.ok(!_.some({a: 1, b: 2, c: 3, d: 4}, _.isObject), 'takes objects'); + assert.ok(_.some(['a', 'b', 'c', 'd'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); + assert.ok(!_.some(['x', 'y', 'z'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); }); - test('any', function() { - strictEqual(_.any, _.some, 'alias for any'); + test('any', function(assert) { + assert.strictEqual(_.any, _.some, 'alias for any'); }); - test('includes', function() { + test('includes', function(assert) { _.each([null, void 0, 0, 1, NaN, {}, []], function(val) { - strictEqual(_.includes(val, 'hasOwnProperty'), false); + assert.strictEqual(_.includes(val, 'hasOwnProperty'), false); }); - strictEqual(_.includes([1, 2, 3], 2), true, 'two is in the array'); - ok(!_.includes([1, 3, 9], 2), 'two is not in the array'); + assert.strictEqual(_.includes([1, 2, 3], 2), true, 'two is in the array'); + assert.ok(!_.includes([1, 3, 9], 2), 'two is not in the array'); - strictEqual(_.includes([5, 4, 3, 2, 1], 5, true), true, 'doesn\'t delegate to binary search'); + assert.strictEqual(_.includes([5, 4, 3, 2, 1], 5, true), true, 'doesn\'t delegate to binary search'); - ok(_.includes({moe: 1, larry: 3, curly: 9}, 3) === true, '_.includes on objects checks their values'); - ok(_([1, 2, 3]).includes(2), 'OO-style includes'); + assert.ok(_.includes({moe: 1, larry: 3, curly: 9}, 3) === true, '_.includes on objects checks their values'); + assert.ok(_([1, 2, 3]).includes(2), 'OO-style includes'); }); - test('include', function() { - strictEqual(_.includes, _.include, 'alias for includes'); + test('include', function(assert) { + assert.strictEqual(_.includes, _.include, 'alias for includes'); }); - test('contains', function() { - strictEqual(_.includes, _.contains, 'alias for includes'); + test('contains', function(assert) { + assert.strictEqual(_.includes, _.contains, 'alias for includes'); var numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; - strictEqual(_.includes(numbers, 1, 1), true, 'contains takes a fromIndex'); - strictEqual(_.includes(numbers, 1, -1), false, 'contains takes a fromIndex'); - strictEqual(_.includes(numbers, 1, -2), false, 'contains takes a fromIndex'); - strictEqual(_.includes(numbers, 1, -3), true, 'contains takes a fromIndex'); - strictEqual(_.includes(numbers, 1, 6), true, 'contains takes a fromIndex'); - strictEqual(_.includes(numbers, 1, 7), false, 'contains takes a fromIndex'); + assert.strictEqual(_.includes(numbers, 1, 1), true, 'contains takes a fromIndex'); + assert.strictEqual(_.includes(numbers, 1, -1), false, 'contains takes a fromIndex'); + assert.strictEqual(_.includes(numbers, 1, -2), false, 'contains takes a fromIndex'); + assert.strictEqual(_.includes(numbers, 1, -3), true, 'contains takes a fromIndex'); + assert.strictEqual(_.includes(numbers, 1, 6), true, 'contains takes a fromIndex'); + assert.strictEqual(_.includes(numbers, 1, 7), false, 'contains takes a fromIndex'); - ok(_.every([1, 2, 3], _.partial(_.contains, numbers)), 'fromIndex is guarded'); + assert.ok(_.every([1, 2, 3], _.partial(_.contains, numbers)), 'fromIndex is guarded'); }); - test('includes with NaN', function() { - strictEqual(_.includes([1, 2, NaN, NaN], NaN), true, 'Expected [1, 2, NaN] to contain NaN'); - strictEqual(_.includes([1, 2, Infinity], NaN), false, 'Expected [1, 2, NaN] to contain NaN'); + test('includes with NaN', function(assert) { + assert.strictEqual(_.includes([1, 2, NaN, NaN], NaN), true, 'Expected [1, 2, NaN] to contain NaN'); + assert.strictEqual(_.includes([1, 2, Infinity], NaN), false, 'Expected [1, 2, NaN] to contain NaN'); }); - test('includes with +- 0', function() { + test('includes with +- 0', function(assert) { _.each([-0, +0], function(val) { - strictEqual(_.includes([1, 2, val, val], val), true); - strictEqual(_.includes([1, 2, val, val], -val), true); - strictEqual(_.includes([-1, 1, 2], -val), false); + assert.strictEqual(_.includes([1, 2, val, val], val), true); + assert.strictEqual(_.includes([1, 2, val, val], -val), true); + assert.strictEqual(_.includes([-1, 1, 2], -val), false); }); }); - test('invoke', 5, function() { + test('invoke', 5, function(assert) { var list = [[5, 1, 7], [3, 2, 1]]; var result = _.invoke(list, 'sort'); - deepEqual(result[0], [1, 5, 7], 'first array sorted'); - deepEqual(result[1], [1, 2, 3], 'second array sorted'); + assert.deepEqual(result[0], [1, 5, 7], 'first array sorted'); + assert.deepEqual(result[1], [1, 2, 3], 'second array sorted'); _.invoke([{ method: function() { - deepEqual(_.toArray(arguments), [1, 2, 3], 'called with arguments'); + assert.deepEqual(_.toArray(arguments), [1, 2, 3], 'called with arguments'); } }], 'method', 1, 2, 3); - deepEqual(_.invoke([{a: null}, {}, {a: _.constant(1)}], 'a'), [null, void 0, 1], 'handles null & undefined'); + assert.deepEqual(_.invoke([{a: null}, {}, {a: _.constant(1)}], 'a'), [null, void 0, 1], 'handles null & undefined'); - throws(function() { + assert.throws(function() { _.invoke([{a: 1}], 'a'); }, TypeError, 'throws for non-functions'); }); - test('invoke w/ function reference', function() { + test('invoke w/ function reference', function(assert) { var list = [[5, 1, 7], [3, 2, 1]]; var result = _.invoke(list, Array.prototype.sort); - deepEqual(result[0], [1, 5, 7], 'first array sorted'); - deepEqual(result[1], [1, 2, 3], 'second array sorted'); + assert.deepEqual(result[0], [1, 5, 7], 'first array sorted'); + assert.deepEqual(result[1], [1, 2, 3], 'second array sorted'); - deepEqual(_.invoke([1, 2, 3], function(a) { + assert.deepEqual(_.invoke([1, 2, 3], function(a) { return a + this; }, 5), [6, 7, 8], 'receives params from invoke'); }); // Relevant when using ClojureScript - test('invoke when strings have a call method', function() { + test('invoke when strings have a call method', function(assert) { String.prototype.call = function() { return 42; }; var list = [[5, 1, 7], [3, 2, 1]]; var s = 'foo'; - equal(s.call(), 42, 'call function exists'); + assert.equal(s.call(), 42, 'call function exists'); var result = _.invoke(list, 'sort'); - deepEqual(result[0], [1, 5, 7], 'first array sorted'); - deepEqual(result[1], [1, 2, 3], 'second array sorted'); + assert.deepEqual(result[0], [1, 5, 7], 'first array sorted'); + assert.deepEqual(result[1], [1, 2, 3], 'second array sorted'); delete String.prototype.call; - equal(s.call, void 0, 'call function removed'); + assert.equal(s.call, void 0, 'call function removed'); }); - test('pluck', function() { + test('pluck', function(assert) { var people = [{name: 'moe', age: 30}, {name: 'curly', age: 50}]; - deepEqual(_.pluck(people, 'name'), ['moe', 'curly'], 'pulls names out of objects'); - deepEqual(_.pluck(people, 'address'), [void 0, void 0], 'missing properties are returned as undefined'); + assert.deepEqual(_.pluck(people, 'name'), ['moe', 'curly'], 'pulls names out of objects'); + assert.deepEqual(_.pluck(people, 'address'), [void 0, void 0], 'missing properties are returned as undefined'); //compat: most flexible handling of edge cases - deepEqual(_.pluck([{'[object Object]': 1}], {}), [1]); + assert.deepEqual(_.pluck([{'[object Object]': 1}], {}), [1]); }); - test('where', function() { + test('where', function(assert) { var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; var result = _.where(list, {a: 1}); - equal(result.length, 3); - equal(result[result.length - 1].b, 4); + assert.equal(result.length, 3); + assert.equal(result[result.length - 1].b, 4); result = _.where(list, {b: 2}); - equal(result.length, 2); - equal(result[0].a, 1); + assert.equal(result.length, 2); + assert.equal(result[0].a, 1); result = _.where(list, {}); - equal(result.length, list.length); + assert.equal(result.length, list.length); function test() {} test.map = _.map; - deepEqual(_.where([_, {a: 1, b: 2}, _], test), [_, _], 'checks properties given function'); + assert.deepEqual(_.where([_, {a: 1, b: 2}, _], test), [_, _], 'checks properties given function'); }); - test('findWhere', function() { + test('findWhere', function(assert) { var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}, {a: 2, b: 4}]; var result = _.findWhere(list, {a: 1}); - deepEqual(result, {a: 1, b: 2}); + assert.deepEqual(result, {a: 1, b: 2}); result = _.findWhere(list, {b: 4}); - deepEqual(result, {a: 1, b: 4}); + assert.deepEqual(result, {a: 1, b: 4}); result = _.findWhere(list, {c: 1}); - ok(_.isUndefined(result), 'undefined when not found'); + assert.ok(_.isUndefined(result), 'undefined when not found'); result = _.findWhere([], {c: 1}); - ok(_.isUndefined(result), 'undefined when searching empty list'); + assert.ok(_.isUndefined(result), 'undefined when searching empty list'); function test() {} test.map = _.map; - equal(_.findWhere([_, {a: 1, b: 2}, _], test), _, 'checks properties given function'); + assert.equal(_.findWhere([_, {a: 1, b: 2}, _], test), _, 'checks properties given function'); function TestClass() { this.y = 5; this.x = 'foo'; } var expect = {c: 1, x: 'foo', y: 5}; - deepEqual(_.findWhere([{y: 5, b: 6}, expect], new TestClass()), expect, 'uses class instance properties'); + assert.deepEqual(_.findWhere([{y: 5, b: 6}, expect], new TestClass()), expect, 'uses class instance properties'); }); - test('max', function() { - equal(-Infinity, _.max(null), 'can handle null/undefined'); - equal(-Infinity, _.max(void 0), 'can handle null/undefined'); - equal(-Infinity, _.max(null, _.identity), 'can handle null/undefined'); + test('max', function(assert) { + assert.equal(-Infinity, _.max(null), 'can handle null/undefined'); + assert.equal(-Infinity, _.max(void 0), 'can handle null/undefined'); + assert.equal(-Infinity, _.max(null, _.identity), 'can handle null/undefined'); - equal(3, _.max([1, 2, 3]), 'can perform a regular Math.max'); + assert.equal(3, _.max([1, 2, 3]), 'can perform a regular Math.max'); var neg = _.max([1, 2, 3], function(num){ return -num; }); - equal(neg, 1, 'can perform a computation-based max'); + assert.equal(neg, 1, 'can perform a computation-based max'); - equal(-Infinity, _.max({}), 'Maximum value of an empty object'); - equal(-Infinity, _.max([]), 'Maximum value of an empty array'); - equal(_.max({a: 'a'}), -Infinity, 'Maximum value of a non-numeric collection'); + assert.equal(-Infinity, _.max({}), 'Maximum value of an empty object'); + assert.equal(-Infinity, _.max([]), 'Maximum value of an empty array'); + assert.equal(_.max({a: 'a'}), -Infinity, 'Maximum value of a non-numeric collection'); - equal(299999, _.max(_.range(1, 300000)), 'Maximum value of a too-big array'); + assert.equal(299999, _.max(_.range(1, 300000)), 'Maximum value of a too-big array'); - equal(3, _.max([1, 2, 3, 'test']), 'Finds correct max in array starting with num and containing a NaN'); - equal(3, _.max(['test', 1, 2, 3]), 'Finds correct max in array starting with NaN'); + assert.equal(3, _.max([1, 2, 3, 'test']), 'Finds correct max in array starting with num and containing a NaN'); + assert.equal(3, _.max(['test', 1, 2, 3]), 'Finds correct max in array starting with NaN'); - deepEqual([3, 6], _.map([[1, 2, 3], [4, 5, 6]], _.max), 'Finds correct max in array when mapping through multiple arrays'); + assert.deepEqual([3, 6], _.map([[1, 2, 3], [4, 5, 6]], _.max), 'Finds correct max in array when mapping through multiple arrays'); var a = {x: -Infinity}; var b = {x: -Infinity}; var iterator = function(o){ return o.x; }; - equal(_.max([a, b], iterator), a, 'Respects iterator return value of -Infinity'); + assert.equal(_.max([a, b], iterator), a, 'Respects iterator return value of -Infinity'); - deepEqual(_.max([{a: 1}, {a: 0, b: 3}, {a: 4}, {a: 2}], 'a'), {a: 4}, 'String keys use property iterator'); + assert.deepEqual(_.max([{a: 1}, {a: 0, b: 3}, {a: 4}, {a: 2}], 'a'), {a: 4}, 'String keys use property iterator'); - deepEqual(_.max([0, 2], function(c){ return c * this.x; }, {x: 1}), 2, 'Iterator context'); - deepEqual(_.max([[1], [2, 3], [-1, 4], [5]], 0), [5], 'Lookup falsy iterator'); - deepEqual(_.max([{0: 1}, {0: 2}, {0: -1}, {a: 1}], 0), {0: 2}, 'Lookup falsy iterator'); + assert.deepEqual(_.max([0, 2], function(c){ return c * this.x; }, {x: 1}), 2, 'Iterator context'); + assert.deepEqual(_.max([[1], [2, 3], [-1, 4], [5]], 0), [5], 'Lookup falsy iterator'); + assert.deepEqual(_.max([{0: 1}, {0: 2}, {0: -1}, {a: 1}], 0), {0: 2}, 'Lookup falsy iterator'); }); - test('min', function() { - equal(Infinity, _.min(null), 'can handle null/undefined'); - equal(Infinity, _.min(void 0), 'can handle null/undefined'); - equal(Infinity, _.min(null, _.identity), 'can handle null/undefined'); + test('min', function(assert) { + assert.equal(Infinity, _.min(null), 'can handle null/undefined'); + assert.equal(Infinity, _.min(void 0), 'can handle null/undefined'); + assert.equal(Infinity, _.min(null, _.identity), 'can handle null/undefined'); - equal(1, _.min([1, 2, 3]), 'can perform a regular Math.min'); + assert.equal(1, _.min([1, 2, 3]), 'can perform a regular Math.min'); var neg = _.min([1, 2, 3], function(num){ return -num; }); - equal(neg, 3, 'can perform a computation-based min'); + assert.equal(neg, 3, 'can perform a computation-based min'); - equal(Infinity, _.min({}), 'Minimum value of an empty object'); - equal(Infinity, _.min([]), 'Minimum value of an empty array'); - equal(_.min({a: 'a'}), Infinity, 'Minimum value of a non-numeric collection'); + assert.equal(Infinity, _.min({}), 'Minimum value of an empty object'); + assert.equal(Infinity, _.min([]), 'Minimum value of an empty array'); + assert.equal(_.min({a: 'a'}), Infinity, 'Minimum value of a non-numeric collection'); - deepEqual([1, 4], _.map([[1, 2, 3], [4, 5, 6]], _.min), 'Finds correct min in array when mapping through multiple arrays'); + assert.deepEqual([1, 4], _.map([[1, 2, 3], [4, 5, 6]], _.min), 'Finds correct min in array when mapping through multiple arrays'); var now = new Date(9999999999); var then = new Date(0); - equal(_.min([now, then]), then); + assert.equal(_.min([now, then]), then); - equal(1, _.min(_.range(1, 300000)), 'Minimum value of a too-big array'); + assert.equal(1, _.min(_.range(1, 300000)), 'Minimum value of a too-big array'); - equal(1, _.min([1, 2, 3, 'test']), 'Finds correct min in array starting with num and containing a NaN'); - equal(1, _.min(['test', 1, 2, 3]), 'Finds correct min in array starting with NaN'); + assert.equal(1, _.min([1, 2, 3, 'test']), 'Finds correct min in array starting with num and containing a NaN'); + assert.equal(1, _.min(['test', 1, 2, 3]), 'Finds correct min in array starting with NaN'); var a = {x: Infinity}; var b = {x: Infinity}; var iterator = function(o){ return o.x; }; - equal(_.min([a, b], iterator), a, 'Respects iterator return value of Infinity'); + assert.equal(_.min([a, b], iterator), a, 'Respects iterator return value of Infinity'); - deepEqual(_.min([{a: 1}, {a: 0, b: 3}, {a: 4}, {a: 2}], 'a'), {a: 0, b: 3}, 'String keys use property iterator'); + assert.deepEqual(_.min([{a: 1}, {a: 0, b: 3}, {a: 4}, {a: 2}], 'a'), {a: 0, b: 3}, 'String keys use property iterator'); - deepEqual(_.min([0, 2], function(c){ return c * this.x; }, {x: -1}), 2, 'Iterator context'); - deepEqual(_.min([[1], [2, 3], [-1, 4], [5]], 0), [-1, 4], 'Lookup falsy iterator'); - deepEqual(_.min([{0: 1}, {0: 2}, {0: -1}, {a: 1}], 0), {0: -1}, 'Lookup falsy iterator'); + assert.deepEqual(_.min([0, 2], function(c){ return c * this.x; }, {x: -1}), 2, 'Iterator context'); + assert.deepEqual(_.min([[1], [2, 3], [-1, 4], [5]], 0), [-1, 4], 'Lookup falsy iterator'); + assert.deepEqual(_.min([{0: 1}, {0: 2}, {0: -1}, {a: 1}], 0), {0: -1}, 'Lookup falsy iterator'); }); - test('sortBy', function() { + test('sortBy', function(assert) { var people = [{name: 'curly', age: 50}, {name: 'moe', age: 30}]; people = _.sortBy(people, function(person){ return person.age; }); - deepEqual(_.pluck(people, 'name'), ['moe', 'curly'], 'stooges sorted by age'); + assert.deepEqual(_.pluck(people, 'name'), ['moe', 'curly'], 'stooges sorted by age'); var list = [void 0, 4, 1, void 0, 3, 2]; - deepEqual(_.sortBy(list, _.identity), [1, 2, 3, 4, void 0, void 0], 'sortBy with undefined values'); + assert.deepEqual(_.sortBy(list, _.identity), [1, 2, 3, 4, void 0, void 0], 'sortBy with undefined values'); list = ['one', 'two', 'three', 'four', 'five']; var sorted = _.sortBy(list, 'length'); - deepEqual(sorted, ['one', 'two', 'four', 'five', 'three'], 'sorted by length'); + assert.deepEqual(sorted, ['one', 'two', 'four', 'five', 'three'], 'sorted by length'); function Pair(x, y) { this.x = x; @@ -650,144 +650,144 @@ return pair.x; }); - deepEqual(actual, stableArray, 'sortBy should be stable for arrays'); - deepEqual(_.sortBy(stableArray, 'x'), stableArray, 'sortBy accepts property string'); + assert.deepEqual(actual, stableArray, 'sortBy should be stable for arrays'); + assert.deepEqual(_.sortBy(stableArray, 'x'), stableArray, 'sortBy accepts property string'); actual = _.sortBy(stableObject, function(pair) { return pair.x; }); - deepEqual(actual, stableArray, 'sortBy should be stable for objects'); + assert.deepEqual(actual, stableArray, 'sortBy should be stable for objects'); list = ['q', 'w', 'e', 'r', 't', 'y']; - deepEqual(_.sortBy(list), ['e', 'q', 'r', 't', 'w', 'y'], 'uses _.identity if iterator is not specified'); + assert.deepEqual(_.sortBy(list), ['e', 'q', 'r', 't', 'w', 'y'], 'uses _.identity if iterator is not specified'); }); - test('groupBy', function() { + test('groupBy', function(assert) { var parity = _.groupBy([1, 2, 3, 4, 5, 6], function(num){ return num % 2; }); - ok('0' in parity && '1' in parity, 'created a group for each value'); - deepEqual(parity[0], [2, 4, 6], 'put each even number in the right group'); + assert.ok('0' in parity && '1' in parity, 'created a group for each value'); + assert.deepEqual(parity[0], [2, 4, 6], 'put each even number in the right group'); var list = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten']; var grouped = _.groupBy(list, 'length'); - deepEqual(grouped['3'], ['one', 'two', 'six', 'ten']); - deepEqual(grouped['4'], ['four', 'five', 'nine']); - deepEqual(grouped['5'], ['three', 'seven', 'eight']); + assert.deepEqual(grouped['3'], ['one', 'two', 'six', 'ten']); + assert.deepEqual(grouped['4'], ['four', 'five', 'nine']); + assert.deepEqual(grouped['5'], ['three', 'seven', 'eight']); var context = {}; - _.groupBy([{}], function(){ ok(this === context); }, context); + _.groupBy([{}], function(){ assert.ok(this === context); }, context); grouped = _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor'; }); - equal(grouped.constructor.length, 1); - equal(grouped.hasOwnProperty.length, 2); + assert.equal(grouped.constructor.length, 1); + assert.equal(grouped.hasOwnProperty.length, 2); var array = [{}]; - _.groupBy(array, function(value, index, obj){ ok(obj === array); }); + _.groupBy(array, function(value, index, obj){ assert.ok(obj === array); }); array = [1, 2, 1, 2, 3]; grouped = _.groupBy(array); - equal(grouped['1'].length, 2); - equal(grouped['3'].length, 1); + assert.equal(grouped['1'].length, 2); + assert.equal(grouped['3'].length, 1); var matrix = [ [1, 2], [1, 3], [2, 3] ]; - deepEqual(_.groupBy(matrix, 0), {1: [[1, 2], [1, 3]], 2: [[2, 3]]}); - deepEqual(_.groupBy(matrix, 1), {2: [[1, 2]], 3: [[1, 3], [2, 3]]}); + assert.deepEqual(_.groupBy(matrix, 0), {1: [[1, 2], [1, 3]], 2: [[2, 3]]}); + assert.deepEqual(_.groupBy(matrix, 1), {2: [[1, 2]], 3: [[1, 3], [2, 3]]}); }); - test('indexBy', function() { + test('indexBy', function(assert) { var parity = _.indexBy([1, 2, 3, 4, 5], function(num){ return num % 2 === 0; }); - equal(parity['true'], 4); - equal(parity['false'], 5); + assert.equal(parity['true'], 4); + assert.equal(parity['false'], 5); var list = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten']; var grouped = _.indexBy(list, 'length'); - equal(grouped['3'], 'ten'); - equal(grouped['4'], 'nine'); - equal(grouped['5'], 'eight'); + assert.equal(grouped['3'], 'ten'); + assert.equal(grouped['4'], 'nine'); + assert.equal(grouped['5'], 'eight'); var array = [1, 2, 1, 2, 3]; grouped = _.indexBy(array); - equal(grouped['1'], 1); - equal(grouped['2'], 2); - equal(grouped['3'], 3); + assert.equal(grouped['1'], 1); + assert.equal(grouped['2'], 2); + assert.equal(grouped['3'], 3); }); - test('countBy', function() { + test('countBy', function(assert) { var parity = _.countBy([1, 2, 3, 4, 5], function(num){ return num % 2 === 0; }); - equal(parity['true'], 2); - equal(parity['false'], 3); + assert.equal(parity['true'], 2); + assert.equal(parity['false'], 3); var list = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten']; var grouped = _.countBy(list, 'length'); - equal(grouped['3'], 4); - equal(grouped['4'], 3); - equal(grouped['5'], 3); + assert.equal(grouped['3'], 4); + assert.equal(grouped['4'], 3); + assert.equal(grouped['5'], 3); var context = {}; - _.countBy([{}], function(){ ok(this === context); }, context); + _.countBy([{}], function(){ assert.ok(this === context); }, context); grouped = _.countBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor'; }); - equal(grouped.constructor, 1); - equal(grouped.hasOwnProperty, 2); + assert.equal(grouped.constructor, 1); + assert.equal(grouped.hasOwnProperty, 2); var array = [{}]; - _.countBy(array, function(value, index, obj){ ok(obj === array); }); + _.countBy(array, function(value, index, obj){ assert.ok(obj === array); }); array = [1, 2, 1, 2, 3]; grouped = _.countBy(array); - equal(grouped['1'], 2); - equal(grouped['3'], 1); + assert.equal(grouped['1'], 2); + assert.equal(grouped['3'], 1); }); - test('shuffle', function() { - deepEqual(_.shuffle([1]), [1], 'behaves correctly on size 1 arrays'); + test('shuffle', function(assert) { + assert.deepEqual(_.shuffle([1]), [1], 'behaves correctly on size 1 arrays'); var numbers = _.range(20); var shuffled = _.shuffle(numbers); - notDeepEqual(numbers, shuffled, 'does change the order'); // Chance of false negative: 1 in ~2.4*10^18 - notStrictEqual(numbers, shuffled, 'original object is unmodified'); - deepEqual(numbers, _.sortBy(shuffled), 'contains the same members before and after shuffle'); + assert.notDeepEqual(numbers, shuffled, 'does change the order'); // Chance of false negative: 1 in ~2.4*10^18 + assert.notStrictEqual(numbers, shuffled, 'original object is unmodified'); + assert.deepEqual(numbers, _.sortBy(shuffled), 'contains the same members before and after shuffle'); shuffled = _.shuffle({a: 1, b: 2, c: 3, d: 4}); - equal(shuffled.length, 4); - deepEqual(shuffled.sort(), [1, 2, 3, 4], 'works on objects'); + assert.equal(shuffled.length, 4); + assert.deepEqual(shuffled.sort(), [1, 2, 3, 4], 'works on objects'); }); - test('sample', function() { - strictEqual(_.sample([1]), 1, 'behaves correctly when no second parameter is given'); - deepEqual(_.sample([1, 2, 3], -2), [], 'behaves correctly on negative n'); + test('sample', function(assert) { + assert.strictEqual(_.sample([1]), 1, 'behaves correctly when no second parameter is given'); + assert.deepEqual(_.sample([1, 2, 3], -2), [], 'behaves correctly on negative n'); var numbers = _.range(10); var allSampled = _.sample(numbers, 10).sort(); - deepEqual(allSampled, numbers, 'contains the same members before and after sample'); + assert.deepEqual(allSampled, numbers, 'contains the same members before and after sample'); allSampled = _.sample(numbers, 20).sort(); - deepEqual(allSampled, numbers, 'also works when sampling more objects than are present'); - ok(_.contains(numbers, _.sample(numbers)), 'sampling a single element returns something from the array'); - strictEqual(_.sample([]), void 0, 'sampling empty array with no number returns undefined'); - notStrictEqual(_.sample([], 5), [], 'sampling empty array with a number returns an empty array'); - notStrictEqual(_.sample([1, 2, 3], 0), [], 'sampling an array with 0 picks returns an empty array'); - deepEqual(_.sample([1, 2], -1), [], 'sampling a negative number of picks returns an empty array'); - ok(_.contains([1, 2, 3], _.sample({a: 1, b: 2, c: 3})), 'sample one value from an object'); + assert.deepEqual(allSampled, numbers, 'also works when sampling more objects than are present'); + assert.ok(_.contains(numbers, _.sample(numbers)), 'sampling a single element returns something from the array'); + assert.strictEqual(_.sample([]), void 0, 'sampling empty array with no number returns undefined'); + assert.notStrictEqual(_.sample([], 5), [], 'sampling empty array with a number returns an empty array'); + assert.notStrictEqual(_.sample([1, 2, 3], 0), [], 'sampling an array with 0 picks returns an empty array'); + assert.deepEqual(_.sample([1, 2], -1), [], 'sampling a negative number of picks returns an empty array'); + assert.ok(_.contains([1, 2, 3], _.sample({a: 1, b: 2, c: 3})), 'sample one value from an object'); var partialSample = _.sample(_.range(1000), 10); var partialSampleSorted = partialSample.sort(); - notDeepEqual(partialSampleSorted, _.range(10), 'samples from the whole array, not just the beginning'); + assert.notDeepEqual(partialSampleSorted, _.range(10), 'samples from the whole array, not just the beginning'); }); - test('toArray', function() { - ok(!_.isArray(arguments), 'arguments object is not an array'); - ok(_.isArray(_.toArray(arguments)), 'arguments object converted into array'); + test('toArray', function(assert) { + assert.ok(!_.isArray(arguments), 'arguments object is not an array'); + assert.ok(_.isArray(_.toArray(arguments)), 'arguments object converted into array'); var a = [1, 2, 3]; - ok(_.toArray(a) !== a, 'array is cloned'); - deepEqual(_.toArray(a), [1, 2, 3], 'cloned array contains same elements'); + assert.ok(_.toArray(a) !== a, 'array is cloned'); + assert.deepEqual(_.toArray(a), [1, 2, 3], 'cloned array contains same elements'); var numbers = _.toArray({one: 1, two: 2, three: 3}); - deepEqual(numbers, [1, 2, 3], 'object flattened into array'); + assert.deepEqual(numbers, [1, 2, 3], 'object flattened into array'); if (typeof document != 'undefined') { // test in IE < 9 @@ -795,78 +795,78 @@ try { actual = _.toArray(document.childNodes); } catch(e) { /* ignored */ } - deepEqual(actual, _.map(document.childNodes, _.identity), 'works on NodeList'); + assert.deepEqual(actual, _.map(document.childNodes, _.identity), 'works on NodeList'); } }); - test('size', function() { - equal(_.size({one: 1, two: 2, three: 3}), 3, 'can compute the size of an object'); - equal(_.size([1, 2, 3]), 3, 'can compute the size of an array'); - equal(_.size({length: 3, 0: 0, 1: 0, 2: 0}), 3, 'can compute the size of Array-likes'); + test('size', function(assert) { + assert.equal(_.size({one: 1, two: 2, three: 3}), 3, 'can compute the size of an object'); + assert.equal(_.size([1, 2, 3]), 3, 'can compute the size of an array'); + assert.equal(_.size({length: 3, 0: 0, 1: 0, 2: 0}), 3, 'can compute the size of Array-likes'); var func = function() { return _.size(arguments); }; - equal(func(1, 2, 3, 4), 4, 'can test the size of the arguments object'); + assert.equal(func(1, 2, 3, 4), 4, 'can test the size of the arguments object'); - equal(_.size('hello'), 5, 'can compute the size of a string literal'); - equal(_.size(new String('hello')), 5, 'can compute the size of string object'); + assert.equal(_.size('hello'), 5, 'can compute the size of a string literal'); + assert.equal(_.size(new String('hello')), 5, 'can compute the size of string object'); - equal(_.size(null), 0, 'handles nulls'); - equal(_.size(0), 0, 'handles numbers'); + assert.equal(_.size(null), 0, 'handles nulls'); + assert.equal(_.size(0), 0, 'handles numbers'); }); - test('partition', function() { + test('partition', function(assert) { var list = [0, 1, 2, 3, 4, 5]; - deepEqual(_.partition(list, function(x) { return x < 4; }), [[0, 1, 2, 3], [4, 5]], 'handles bool return values'); - deepEqual(_.partition(list, function(x) { return x & 1; }), [[1, 3, 5], [0, 2, 4]], 'handles 0 and 1 return values'); - deepEqual(_.partition(list, function(x) { return x - 3; }), [[0, 1, 2, 4, 5], [3]], 'handles other numeric return values'); - deepEqual(_.partition(list, function(x) { return x > 1 ? null : true; }), [[0, 1], [2, 3, 4, 5]], 'handles null return values'); - deepEqual(_.partition(list, function(x) { if (x < 2) return true; }), [[0, 1], [2, 3, 4, 5]], 'handles undefined return values'); - deepEqual(_.partition({a: 1, b: 2, c: 3}, function(x) { return x > 1; }), [[2, 3], [1]], 'handles objects'); + assert.deepEqual(_.partition(list, function(x) { return x < 4; }), [[0, 1, 2, 3], [4, 5]], 'handles bool return values'); + assert.deepEqual(_.partition(list, function(x) { return x & 1; }), [[1, 3, 5], [0, 2, 4]], 'handles 0 and 1 return values'); + assert.deepEqual(_.partition(list, function(x) { return x - 3; }), [[0, 1, 2, 4, 5], [3]], 'handles other numeric return values'); + assert.deepEqual(_.partition(list, function(x) { return x > 1 ? null : true; }), [[0, 1], [2, 3, 4, 5]], 'handles null return values'); + assert.deepEqual(_.partition(list, function(x) { if (x < 2) return true; }), [[0, 1], [2, 3, 4, 5]], 'handles undefined return values'); + assert.deepEqual(_.partition({a: 1, b: 2, c: 3}, function(x) { return x > 1; }), [[2, 3], [1]], 'handles objects'); - deepEqual(_.partition(list, function(x, index) { return index % 2; }), [[1, 3, 5], [0, 2, 4]], 'can reference the array index'); - deepEqual(_.partition(list, function(x, index, arr) { return x === arr.length - 1; }), [[5], [0, 1, 2, 3, 4]], 'can reference the collection'); + assert.deepEqual(_.partition(list, function(x, index) { return index % 2; }), [[1, 3, 5], [0, 2, 4]], 'can reference the array index'); + assert.deepEqual(_.partition(list, function(x, index, arr) { return x === arr.length - 1; }), [[5], [0, 1, 2, 3, 4]], 'can reference the collection'); // Default iterator - deepEqual(_.partition([1, false, true, '']), [[1, true], [false, '']], 'Default iterator'); - deepEqual(_.partition([{x: 1}, {x: 0}, {x: 1}], 'x'), [[{x: 1}, {x: 1}], [{x: 0}]], 'Takes a string'); + assert.deepEqual(_.partition([1, false, true, '']), [[1, true], [false, '']], 'Default iterator'); + assert.deepEqual(_.partition([{x: 1}, {x: 0}, {x: 1}], 'x'), [[{x: 1}, {x: 1}], [{x: 0}]], 'Takes a string'); // Context var predicate = function(x){ return x === this.x; }; - deepEqual(_.partition([1, 2, 3], predicate, {x: 2}), [[2], [1, 3]], 'partition takes a context argument'); + assert.deepEqual(_.partition([1, 2, 3], predicate, {x: 2}), [[2], [1, 3]], 'partition takes a context argument'); - deepEqual(_.partition([{a: 1}, {b: 2}, {a: 1, b: 2}], {a: 1}), [[{a: 1}, {a: 1, b: 2}], [{b: 2}]], 'predicate can be object'); + assert.deepEqual(_.partition([{a: 1}, {b: 2}, {a: 1, b: 2}], {a: 1}), [[{a: 1}, {a: 1, b: 2}], [{b: 2}]], 'predicate can be object'); var object = {a: 1}; _.partition(object, function(val, key, obj) { - equal(val, 1); - equal(key, 'a'); - equal(obj, object); - equal(this, predicate); + assert.equal(val, 1); + assert.equal(key, 'a'); + assert.equal(obj, object); + assert.equal(this, predicate); }, predicate); }); if (typeof document != 'undefined') { - test('Can use various collection methods on NodeLists', function() { + test('Can use various collection methods on NodeLists', function(assert) { var parent = document.createElement('div'); parent.innerHTML = 'textnode'; var elementChildren = _.filter(parent.childNodes, _.isElement); - equal(elementChildren.length, 2); + assert.equal(elementChildren.length, 2); - deepEqual(_.map(elementChildren, 'id'), ['id1', 'id2']); - deepEqual(_.map(parent.childNodes, 'nodeType'), [1, 3, 1]); + assert.deepEqual(_.map(elementChildren, 'id'), ['id1', 'id2']); + assert.deepEqual(_.map(parent.childNodes, 'nodeType'), [1, 3, 1]); - ok(!_.every(parent.childNodes, _.isElement)); - ok(_.some(parent.childNodes, _.isElement)); + assert.ok(!_.every(parent.childNodes, _.isElement)); + assert.ok(_.some(parent.childNodes, _.isElement)); function compareNode(node) { return _.isElement(node) ? node.id.charAt(2) : void 0; } - equal(_.max(parent.childNodes, compareNode), _.last(parent.childNodes)); - equal(_.min(parent.childNodes, compareNode), _.first(parent.childNodes)); + assert.equal(_.max(parent.childNodes, compareNode), _.last(parent.childNodes)); + assert.equal(_.min(parent.childNodes, compareNode), _.first(parent.childNodes)); }); } diff --git a/test/cross-document.js b/test/cross-document.js index 215d78e85..fff3a07d4 100644 --- a/test/cross-document.js +++ b/test/cross-document.js @@ -33,95 +33,95 @@ ); iDoc.close(); - test('isEqual', function() { + test('isEqual', function(assert) { - ok(!_.isEqual(iNumber, 101)); - ok(_.isEqual(iNumber, 100)); + assert.ok(!_.isEqual(iNumber, 101)); + assert.ok(_.isEqual(iNumber, 100)); // Objects from another frame. - ok(_.isEqual({}, iObject), 'Objects with equivalent members created in different documents are equal'); + assert.ok(_.isEqual({}, iObject), 'Objects with equivalent members created in different documents are equal'); // Array from another frame. - ok(_.isEqual([1, 2, 3], iArray), 'Arrays with equivalent elements created in different documents are equal'); + assert.ok(_.isEqual([1, 2, 3], iArray), 'Arrays with equivalent elements created in different documents are equal'); }); - test('isEmpty', function() { - ok(!_([iNumber]).isEmpty(), '[1] is not empty'); - ok(!_.isEmpty(iArray), '[] is empty'); - ok(_.isEmpty(iObject), '{} is empty'); + test('isEmpty', function(assert) { + assert.ok(!_([iNumber]).isEmpty(), '[1] is not empty'); + assert.ok(!_.isEmpty(iArray), '[] is empty'); + assert.ok(_.isEmpty(iObject), '{} is empty'); }); - test('isElement', function() { - ok(!_.isElement('div'), 'strings are not dom elements'); - ok(_.isElement(document.body), 'the body tag is a DOM element'); - ok(_.isElement(iElement), 'even from another frame'); + test('isElement', function(assert) { + assert.ok(!_.isElement('div'), 'strings are not dom elements'); + assert.ok(_.isElement(document.body), 'the body tag is a DOM element'); + assert.ok(_.isElement(iElement), 'even from another frame'); }); - test('isArguments', function() { - ok(_.isArguments(iArguments), 'even from another frame'); + test('isArguments', function(assert) { + assert.ok(_.isArguments(iArguments), 'even from another frame'); }); - test('isObject', function() { - ok(_.isObject(iElement), 'even from another frame'); - ok(_.isObject(iFunction), 'even from another frame'); + test('isObject', function(assert) { + assert.ok(_.isObject(iElement), 'even from another frame'); + assert.ok(_.isObject(iFunction), 'even from another frame'); }); - test('isArray', function() { - ok(_.isArray(iArray), 'even from another frame'); + test('isArray', function(assert) { + assert.ok(_.isArray(iArray), 'even from another frame'); }); - test('isString', function() { - ok(_.isString(iString), 'even from another frame'); + test('isString', function(assert) { + assert.ok(_.isString(iString), 'even from another frame'); }); - test('isNumber', function() { - ok(_.isNumber(iNumber), 'even from another frame'); + test('isNumber', function(assert) { + assert.ok(_.isNumber(iNumber), 'even from another frame'); }); - test('isBoolean', function() { - ok(_.isBoolean(iBoolean), 'even from another frame'); + test('isBoolean', function(assert) { + assert.ok(_.isBoolean(iBoolean), 'even from another frame'); }); - test('isFunction', function() { - ok(_.isFunction(iFunction), 'even from another frame'); + test('isFunction', function(assert) { + assert.ok(_.isFunction(iFunction), 'even from another frame'); }); - test('isDate', function() { - ok(_.isDate(iDate), 'even from another frame'); + test('isDate', function(assert) { + assert.ok(_.isDate(iDate), 'even from another frame'); }); - test('isRegExp', function() { - ok(_.isRegExp(iRegExp), 'even from another frame'); + test('isRegExp', function(assert) { + assert.ok(_.isRegExp(iRegExp), 'even from another frame'); }); - test('isNaN', function() { - ok(_.isNaN(iNaN), 'even from another frame'); + test('isNaN', function(assert) { + assert.ok(_.isNaN(iNaN), 'even from another frame'); }); - test('isNull', function() { - ok(_.isNull(iNull), 'even from another frame'); + test('isNull', function(assert) { + assert.ok(_.isNull(iNull), 'even from another frame'); }); - test('isUndefined', function() { - ok(_.isUndefined(iUndefined), 'even from another frame'); + test('isUndefined', function(assert) { + assert.ok(_.isUndefined(iUndefined), 'even from another frame'); }); - test('isError', function() { - ok(_.isError(iError), 'even from another frame'); + test('isError', function(assert) { + assert.ok(_.isError(iError), 'even from another frame'); }); if (typeof ActiveXObject != 'undefined') { - test('IE host objects', function() { + test('IE host objects', function(assert) { var xml = new ActiveXObject('Msxml2.DOMDocument.3.0'); - ok(!_.isNumber(xml)); - ok(!_.isBoolean(xml)); - ok(!_.isNaN(xml)); - ok(!_.isFunction(xml)); - ok(!_.isNull(xml)); - ok(!_.isUndefined(xml)); + assert.ok(!_.isNumber(xml)); + assert.ok(!_.isBoolean(xml)); + assert.ok(!_.isNaN(xml)); + assert.ok(!_.isFunction(xml)); + assert.ok(!_.isNull(xml)); + assert.ok(!_.isUndefined(xml)); }); - test('#1621 IE 11 compat mode DOM elements are not functions', function() { + test('#1621 IE 11 compat mode DOM elements are not functions', function(assert) { var fn = function() {}; var xml = new ActiveXObject('Msxml2.DOMDocument.3.0'); var div = document.createElement('div'); @@ -132,9 +132,9 @@ _.isFunction(fn); } - equal(_.isFunction(xml), false); - equal(_.isFunction(div), false); - equal(_.isFunction(fn), true); + assert.equal(_.isFunction(xml), false); + assert.equal(_.isFunction(div), false); + assert.equal(_.isFunction(fn), true); }); } diff --git a/test/functions.js b/test/functions.js index 7ceb49dfe..926681c9d 100644 --- a/test/functions.js +++ b/test/functions.js @@ -4,32 +4,32 @@ QUnit.module('Functions'); QUnit.config.asyncRetries = 3; - test('bind', function() { + test('bind', function(assert) { var context = {name: 'moe'}; var func = function(arg) { return 'name: ' + (this.name || arg); }; var bound = _.bind(func, context); - equal(bound(), 'name: moe', 'can bind a function to a context'); + assert.equal(bound(), 'name: moe', 'can bind a function to a context'); bound = _(func).bind(context); - equal(bound(), 'name: moe', 'can do OO-style binding'); + assert.equal(bound(), 'name: moe', 'can do OO-style binding'); bound = _.bind(func, null, 'curly'); var result = bound(); // Work around a PhantomJS bug when applying a function with null|undefined. - ok(result === 'name: curly' || result === 'name: ' + window.name, 'can bind without specifying a context'); + assert.ok(result === 'name: curly' || result === 'name: ' + window.name, 'can bind without specifying a context'); func = function(salutation, name) { return salutation + ': ' + name; }; func = _.bind(func, this, 'hello'); - equal(func('moe'), 'hello: moe', 'the function was partially applied in advance'); + assert.equal(func('moe'), 'hello: moe', 'the function was partially applied in advance'); func = _.bind(func, this, 'curly'); - equal(func(), 'hello: curly', 'the function was completely applied in advance'); + assert.equal(func(), 'hello: curly', 'the function was completely applied in advance'); func = function(salutation, firstname, lastname) { return salutation + ': ' + firstname + ' ' + lastname; }; func = _.bind(func, this, 'hello', 'moe', 'curly'); - equal(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments'); + assert.equal(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments'); - func = function(ctx, message) { equal(this, ctx, message); }; + func = function(ctx, message) { assert.equal(this, ctx, message); }; _.bind(func, 0, 0, 'can bind a function to `0`')(); _.bind(func, '', '', 'can bind a function to an empty string')(); _.bind(func, false, false, 'can bind a function to `false`')(); @@ -40,29 +40,29 @@ var boundf = _.bind(F, {hello: 'moe curly'}); var Boundf = boundf; // make eslint happy. var newBoundf = new Boundf(); - equal(newBoundf.hello, void 0, 'function should not be bound to the context, to comply with ECMAScript 5'); - equal(boundf().hello, 'moe curly', "When called without the new operator, it's OK to be bound to the context"); - ok(newBoundf instanceof F, 'a bound instance is an instance of the original function'); + assert.equal(newBoundf.hello, void 0, 'function should not be bound to the context, to comply with ECMAScript 5'); + assert.equal(boundf().hello, 'moe curly', "When called without the new operator, it's OK to be bound to the context"); + assert.ok(newBoundf instanceof F, 'a bound instance is an instance of the original function'); - throws(function() { _.bind('notafunction'); }, TypeError, 'throws an error when binding to a non-function'); + assert.throws(function() { _.bind('notafunction'); }, TypeError, 'throws an error when binding to a non-function'); }); - test('partial', function() { + test('partial', function(assert) { var obj = {name: 'moe'}; var func = function() { return this.name + ' ' + _.toArray(arguments).join(' '); }; obj.func = _.partial(func, 'a', 'b'); - equal(obj.func('c', 'd'), 'moe a b c d', 'can partially apply'); + assert.equal(obj.func('c', 'd'), 'moe a b c d', 'can partially apply'); obj.func = _.partial(func, _, 'b', _, 'd'); - equal(obj.func('a', 'c'), 'moe a b c d', 'can partially apply with placeholders'); + assert.equal(obj.func('a', 'c'), 'moe a b c d', 'can partially apply with placeholders'); func = _.partial(function() { return arguments.length; }, _, 'b', _, 'd'); - equal(func('a', 'c', 'e'), 5, 'accepts more arguments than the number of placeholders'); - equal(func('a'), 4, 'accepts fewer arguments than the number of placeholders'); + assert.equal(func('a', 'c', 'e'), 5, 'accepts more arguments than the number of placeholders'); + assert.equal(func('a'), 4, 'accepts fewer arguments than the number of placeholders'); func = _.partial(function() { return typeof arguments[2]; }, _, 'b', _, 'd'); - equal(func('a'), 'undefined', 'unfilled placeholders are undefined'); + assert.equal(func('a'), 'undefined', 'unfilled placeholders are undefined'); // passes context function MyWidget(name, options) { @@ -74,22 +74,22 @@ }; var MyWidgetWithCoolOpts = _.partial(MyWidget, _, {a: 1}); var widget = new MyWidgetWithCoolOpts('foo'); - ok(widget instanceof MyWidget, 'Can partially bind a constructor'); - equal(widget.get(), 'foo', 'keeps prototype'); - deepEqual(widget.options, {a: 1}); + assert.ok(widget instanceof MyWidget, 'Can partially bind a constructor'); + assert.equal(widget.get(), 'foo', 'keeps prototype'); + assert.deepEqual(widget.options, {a: 1}); _.partial.placeholder = obj; func = _.partial(function() { return arguments.length; }, obj, 'b', obj, 'd'); - equal(func('a'), 4, 'allows the placeholder to be swapped out'); + assert.equal(func('a'), 4, 'allows the placeholder to be swapped out'); _.partial.placeholder = {}; func = _.partial(function() { return arguments.length; }, obj, 'b', obj, 'd'); - equal(func('a'), 5, 'swapping the placeholder preserves previously bound arguments'); + assert.equal(func('a'), 5, 'swapping the placeholder preserves previously bound arguments'); _.partial.placeholder = _; }); - test('bindAll', function() { + test('bindAll', function(assert) { var curly = {name: 'curly'}, moe = { name: 'moe', getName: function() { return 'name: ' + this.name; }, @@ -98,8 +98,8 @@ curly.getName = moe.getName; _.bindAll(moe, 'getName', 'sayHi'); curly.sayHi = moe.sayHi; - equal(curly.getName(), 'name: curly', 'unbound function is bound to current object'); - equal(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object'); + assert.equal(curly.getName(), 'name: curly', 'unbound function is bound to current object'); + assert.equal(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object'); curly = {name: 'curly'}; moe = { @@ -109,57 +109,57 @@ sayLast: function() { return this.sayHi(_.last(arguments)); } }; - throws(function() { _.bindAll(moe); }, Error, 'throws an error for bindAll with no functions named'); - throws(function() { _.bindAll(moe, 'sayBye'); }, TypeError, 'throws an error for bindAll if the given key is undefined'); - throws(function() { _.bindAll(moe, 'name'); }, TypeError, 'throws an error for bindAll if the given key is not a function'); + assert.throws(function() { _.bindAll(moe); }, Error, 'throws an error for bindAll with no functions named'); + assert.throws(function() { _.bindAll(moe, 'sayBye'); }, TypeError, 'throws an error for bindAll if the given key is undefined'); + assert.throws(function() { _.bindAll(moe, 'name'); }, TypeError, 'throws an error for bindAll if the given key is not a function'); _.bindAll(moe, 'sayHi', 'sayLast'); curly.sayHi = moe.sayHi; - equal(curly.sayHi(), 'hi: moe'); + assert.equal(curly.sayHi(), 'hi: moe'); var sayLast = moe.sayLast; - equal(sayLast(1, 2, 3, 4, 5, 6, 7, 'Tom'), 'hi: moe', 'createCallback works with any number of arguments'); + assert.equal(sayLast(1, 2, 3, 4, 5, 6, 7, 'Tom'), 'hi: moe', 'createCallback works with any number of arguments'); _.bindAll(moe, ['getName']); var getName = moe.getName; - equal(getName(), 'name: moe', 'flattens arguments into a single list'); + assert.equal(getName(), 'name: moe', 'flattens arguments into a single list'); }); - test('memoize', function() { + test('memoize', function(assert) { var fib = function(n) { return n < 2 ? n : fib(n - 1) + fib(n - 2); }; - equal(fib(10), 55, 'a memoized version of fibonacci produces identical results'); + assert.equal(fib(10), 55, 'a memoized version of fibonacci produces identical results'); fib = _.memoize(fib); // Redefine `fib` for memoization - equal(fib(10), 55, 'a memoized version of fibonacci produces identical results'); + assert.equal(fib(10), 55, 'a memoized version of fibonacci produces identical results'); var o = function(str) { return str; }; var fastO = _.memoize(o); - equal(o('toString'), 'toString', 'checks hasOwnProperty'); - equal(fastO('toString'), 'toString', 'checks hasOwnProperty'); + assert.equal(o('toString'), 'toString', 'checks hasOwnProperty'); + assert.equal(fastO('toString'), 'toString', 'checks hasOwnProperty'); // Expose the cache. var upper = _.memoize(function(s) { return s.toUpperCase(); }); - equal(upper('foo'), 'FOO'); - equal(upper('bar'), 'BAR'); - deepEqual(upper.cache, {foo: 'FOO', bar: 'BAR'}); + assert.equal(upper('foo'), 'FOO'); + assert.equal(upper('bar'), 'BAR'); + assert.deepEqual(upper.cache, {foo: 'FOO', bar: 'BAR'}); upper.cache = {foo: 'BAR', bar: 'FOO'}; - equal(upper('foo'), 'BAR'); - equal(upper('bar'), 'FOO'); + assert.equal(upper('foo'), 'BAR'); + assert.equal(upper('bar'), 'FOO'); var hashed = _.memoize(function(key) { //https://github.com/jashkenas/underscore/pull/1679#discussion_r13736209 - ok(/[a-z]+/.test(key), 'hasher doesn\'t change keys'); + assert.ok(/[a-z]+/.test(key), 'hasher doesn\'t change keys'); return key; }, function(key) { return key.toUpperCase(); }); hashed('yep'); - deepEqual(hashed.cache, {YEP: 'yep'}, 'takes a hasher'); + assert.deepEqual(hashed.cache, {YEP: 'yep'}, 'takes a hasher'); // Test that the hash function can be used to swizzle the key. var objCacher = _.memoize(function(value, key) { @@ -169,78 +169,78 @@ }); var myObj = objCacher('a', 'alpha'); var myObjAlias = objCacher('b', 'alpha'); - notStrictEqual(myObj, void 0, 'object is created if second argument used as key'); - strictEqual(myObj, myObjAlias, 'object is cached if second argument used as key'); - strictEqual(myObj.value, 'a', 'object is not modified if second argument used as key'); + assert.notStrictEqual(myObj, void 0, 'object is created if second argument used as key'); + assert.strictEqual(myObj, myObjAlias, 'object is cached if second argument used as key'); + assert.strictEqual(myObj.value, 'a', 'object is not modified if second argument used as key'); }); - asyncTest('delay', 2, function() { + asyncTest('delay', 2, function(assert) { var delayed = false; _.delay(function(){ delayed = true; }, 100); - setTimeout(function(){ ok(!delayed, "didn't delay the function quite yet"); }, 50); - setTimeout(function(){ ok(delayed, 'delayed the function'); start(); }, 150); + setTimeout(function(){ assert.ok(!delayed, "didn't delay the function quite yet"); }, 50); + setTimeout(function(){ assert.ok(delayed, 'delayed the function'); start(); }, 150); }); - asyncTest('defer', 1, function() { + asyncTest('defer', 1, function(assert) { var deferred = false; _.defer(function(bool){ deferred = bool; }, true); - _.delay(function(){ ok(deferred, 'deferred the function'); start(); }, 50); + _.delay(function(){ assert.ok(deferred, 'deferred the function'); start(); }, 50); }); - asyncTest('throttle', 2, function() { + asyncTest('throttle', 2, function(assert) { var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 32); throttledIncr(); throttledIncr(); - equal(counter, 1, 'incr was called immediately'); - _.delay(function(){ equal(counter, 2, 'incr was throttled'); start(); }, 64); + assert.equal(counter, 1, 'incr was called immediately'); + _.delay(function(){ assert.equal(counter, 2, 'incr was throttled'); start(); }, 64); }); - asyncTest('throttle arguments', 2, function() { + asyncTest('throttle arguments', 2, function(assert) { var value = 0; var update = function(val){ value = val; }; var throttledUpdate = _.throttle(update, 32); throttledUpdate(1); throttledUpdate(2); _.delay(function(){ throttledUpdate(3); }, 64); - equal(value, 1, 'updated to latest value'); - _.delay(function(){ equal(value, 3, 'updated to latest value'); start(); }, 96); + assert.equal(value, 1, 'updated to latest value'); + _.delay(function(){ assert.equal(value, 3, 'updated to latest value'); start(); }, 96); }); - asyncTest('throttle once', 2, function() { + asyncTest('throttle once', 2, function(assert) { var counter = 0; var incr = function(){ return ++counter; }; var throttledIncr = _.throttle(incr, 32); var result = throttledIncr(); _.delay(function(){ - equal(result, 1, 'throttled functions return their value'); - equal(counter, 1, 'incr was called once'); start(); + assert.equal(result, 1, 'throttled functions return their value'); + assert.equal(counter, 1, 'incr was called once'); start(); }, 64); }); - asyncTest('throttle twice', 1, function() { + asyncTest('throttle twice', 1, function(assert) { var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 32); throttledIncr(); throttledIncr(); - _.delay(function(){ equal(counter, 2, 'incr was called twice'); start(); }, 64); + _.delay(function(){ assert.equal(counter, 2, 'incr was called twice'); start(); }, 64); }); - asyncTest('more throttling', 3, function() { + asyncTest('more throttling', 3, function(assert) { var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 30); throttledIncr(); throttledIncr(); - equal(counter, 1); + assert.equal(counter, 1); _.delay(function(){ - equal(counter, 2); + assert.equal(counter, 2); throttledIncr(); - equal(counter, 3); + assert.equal(counter, 3); start(); }, 85); }); - asyncTest('throttle repeatedly with results', 6, function() { + asyncTest('throttle repeatedly with results', 6, function(assert) { var counter = 0; var incr = function(){ return ++counter; }; var throttledIncr = _.throttle(incr, 100); @@ -252,17 +252,17 @@ _.delay(saveResult, 160); _.delay(saveResult, 230); _.delay(function() { - equal(results[0], 1, 'incr was called once'); - equal(results[1], 1, 'incr was throttled'); - equal(results[2], 1, 'incr was throttled'); - equal(results[3], 2, 'incr was called twice'); - equal(results[4], 2, 'incr was throttled'); - equal(results[5], 3, 'incr was called trailing'); + assert.equal(results[0], 1, 'incr was called once'); + assert.equal(results[1], 1, 'incr was throttled'); + assert.equal(results[2], 1, 'incr was throttled'); + assert.equal(results[3], 2, 'incr was called twice'); + assert.equal(results[4], 2, 'incr was throttled'); + assert.equal(results[5], 3, 'incr was called trailing'); start(); }, 300); }); - asyncTest('throttle triggers trailing call when invoked repeatedly', 2, function() { + asyncTest('throttle triggers trailing call when invoked repeatedly', 2, function(assert) { var counter = 0; var limit = 48; var incr = function(){ counter++; }; @@ -273,29 +273,29 @@ throttledIncr(); } var lastCount = counter; - ok(counter > 1); + assert.ok(counter > 1); _.delay(function() { - ok(counter > lastCount); + assert.ok(counter > lastCount); start(); }, 96); }); - asyncTest('throttle does not trigger leading call when leading is set to false', 2, function() { + asyncTest('throttle does not trigger leading call when leading is set to false', 2, function(assert) { var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 60, {leading: false}); throttledIncr(); throttledIncr(); - equal(counter, 0); + assert.equal(counter, 0); _.delay(function() { - equal(counter, 1); + assert.equal(counter, 1); start(); }, 96); }); - asyncTest('more throttle does not trigger leading call when leading is set to false', 3, function() { + asyncTest('more throttle does not trigger leading call when leading is set to false', 3, function(assert) { var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 100, {leading: false}); @@ -304,75 +304,75 @@ _.delay(throttledIncr, 50); _.delay(throttledIncr, 60); _.delay(throttledIncr, 200); - equal(counter, 0); + assert.equal(counter, 0); _.delay(function() { - equal(counter, 1); + assert.equal(counter, 1); }, 250); _.delay(function() { - equal(counter, 2); + assert.equal(counter, 2); start(); }, 350); }); - asyncTest('one more throttle with leading: false test', 2, function() { + asyncTest('one more throttle with leading: false test', 2, function(assert) { var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 100, {leading: false}); var time = new Date; while (new Date - time < 350) throttledIncr(); - ok(counter <= 3); + assert.ok(counter <= 3); _.delay(function() { - ok(counter <= 4); + assert.ok(counter <= 4); start(); }, 200); }); - asyncTest('throttle does not trigger trailing call when trailing is set to false', 4, function() { + asyncTest('throttle does not trigger trailing call when trailing is set to false', 4, function(assert) { var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 60, {trailing: false}); throttledIncr(); throttledIncr(); throttledIncr(); - equal(counter, 1); + assert.equal(counter, 1); _.delay(function() { - equal(counter, 1); + assert.equal(counter, 1); throttledIncr(); throttledIncr(); - equal(counter, 2); + assert.equal(counter, 2); _.delay(function() { - equal(counter, 2); + assert.equal(counter, 2); start(); }, 96); }, 96); }); - asyncTest('throttle continues to function after system time is set backwards', 2, function() { + asyncTest('throttle continues to function after system time is set backwards', 2, function(assert) { var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 100); var origNowFunc = _.now; throttledIncr(); - equal(counter, 1); + assert.equal(counter, 1); _.now = function() { return new Date(2013, 0, 1, 1, 1, 1); }; _.delay(function() { throttledIncr(); - equal(counter, 2); + assert.equal(counter, 2); start(); _.now = origNowFunc; }, 200); }); - asyncTest('throttle re-entrant', 2, function() { + asyncTest('throttle re-entrant', 2, function(assert) { var sequence = [ ['b1', 'b2'], ['c1', 'c2'] @@ -388,50 +388,50 @@ }; throttledAppend = _.throttle(append, 32); throttledAppend.call('a1', 'a2'); - equal(value, 'a1a2'); + assert.equal(value, 'a1a2'); _.delay(function(){ - equal(value, 'a1a2c1c2b1b2', 'append was throttled successfully'); + assert.equal(value, 'a1a2c1c2b1b2', 'append was throttled successfully'); start(); }, 100); }); - asyncTest('debounce', 1, function() { + asyncTest('debounce', 1, function(assert) { var counter = 0; var incr = function(){ counter++; }; var debouncedIncr = _.debounce(incr, 32); debouncedIncr(); debouncedIncr(); _.delay(debouncedIncr, 16); - _.delay(function(){ equal(counter, 1, 'incr was debounced'); start(); }, 96); + _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); start(); }, 96); }); - asyncTest('debounce asap', 4, function() { + asyncTest('debounce asap', 4, function(assert) { var a, b; var counter = 0; var incr = function(){ return ++counter; }; var debouncedIncr = _.debounce(incr, 64, true); a = debouncedIncr(); b = debouncedIncr(); - equal(a, 1); - equal(b, 1); - equal(counter, 1, 'incr was called immediately'); + assert.equal(a, 1); + assert.equal(b, 1); + assert.equal(counter, 1, 'incr was called immediately'); _.delay(debouncedIncr, 16); _.delay(debouncedIncr, 32); _.delay(debouncedIncr, 48); - _.delay(function(){ equal(counter, 1, 'incr was debounced'); start(); }, 128); + _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); start(); }, 128); }); - asyncTest('debounce asap recursively', 2, function() { + asyncTest('debounce asap recursively', 2, function(assert) { var counter = 0; var debouncedIncr = _.debounce(function(){ counter++; if (counter < 10) debouncedIncr(); }, 32, true); debouncedIncr(); - equal(counter, 1, 'incr was called immediately'); - _.delay(function(){ equal(counter, 1, 'incr was debounced'); start(); }, 96); + assert.equal(counter, 1, 'incr was called immediately'); + _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); start(); }, 96); }); - asyncTest('debounce after system time is set backwards', 2, function() { + asyncTest('debounce after system time is set backwards', 2, function(assert) { var counter = 0; var origNowFunc = _.now; var debouncedIncr = _.debounce(function(){ @@ -439,7 +439,7 @@ }, 100, true); debouncedIncr(); - equal(counter, 1, 'incr was called immediately'); + assert.equal(counter, 1, 'incr was called immediately'); _.now = function() { return new Date(2013, 0, 1, 1, 1, 1); @@ -447,13 +447,13 @@ _.delay(function() { debouncedIncr(); - equal(counter, 2, 'incr was debounced successfully'); + assert.equal(counter, 2, 'incr was debounced successfully'); start(); _.now = origNowFunc; }, 200); }); - asyncTest('debounce re-entrant', 2, function() { + asyncTest('debounce re-entrant', 2, function(assert) { var sequence = [ ['b1', 'b2'] ]; @@ -468,80 +468,80 @@ }; debouncedAppend = _.debounce(append, 32); debouncedAppend.call('a1', 'a2'); - equal(value, ''); + assert.equal(value, ''); _.delay(function(){ - equal(value, 'a1a2b1b2', 'append was debounced successfully'); + assert.equal(value, 'a1a2b1b2', 'append was debounced successfully'); start(); }, 100); }); - test('once', function() { + test('once', function(assert) { var num = 0; var increment = _.once(function(){ return ++num; }); increment(); increment(); - equal(num, 1); + assert.equal(num, 1); - equal(increment(), 1, 'stores a memo to the last value'); + assert.equal(increment(), 1, 'stores a memo to the last value'); }); - test('Recursive onced function.', 1, function() { + test('Recursive onced function.', 1, function(assert) { var f = _.once(function(){ - ok(true); + assert.ok(true); f(); }); f(); }); - test('wrap', function() { + test('wrap', function(assert) { var greet = function(name){ return 'hi: ' + name; }; var backwards = _.wrap(greet, function(func, name){ return func(name) + ' ' + name.split('').reverse().join(''); }); - equal(backwards('moe'), 'hi: moe eom', 'wrapped the salutation function'); + assert.equal(backwards('moe'), 'hi: moe eom', 'wrapped the salutation function'); var inner = function(){ return 'Hello '; }; var obj = {name: 'Moe'}; obj.hi = _.wrap(inner, function(fn){ return fn() + this.name; }); - equal(obj.hi(), 'Hello Moe'); + assert.equal(obj.hi(), 'Hello Moe'); var noop = function(){}; var wrapped = _.wrap(noop, function(){ return Array.prototype.slice.call(arguments, 0); }); var ret = wrapped(['whats', 'your'], 'vector', 'victor'); - deepEqual(ret, [noop, ['whats', 'your'], 'vector', 'victor']); + assert.deepEqual(ret, [noop, ['whats', 'your'], 'vector', 'victor']); }); - test('negate', function() { + test('negate', function(assert) { var isOdd = function(n){ return n & 1; }; - equal(_.negate(isOdd)(2), true, 'should return the complement of the given function'); - equal(_.negate(isOdd)(3), false, 'should return the complement of the given function'); + assert.equal(_.negate(isOdd)(2), true, 'should return the complement of the given function'); + assert.equal(_.negate(isOdd)(3), false, 'should return the complement of the given function'); }); - test('compose', function() { + test('compose', function(assert) { var greet = function(name){ return 'hi: ' + name; }; var exclaim = function(sentence){ return sentence + '!'; }; var composed = _.compose(exclaim, greet); - equal(composed('moe'), 'hi: moe!', 'can compose a function that takes another'); + assert.equal(composed('moe'), 'hi: moe!', 'can compose a function that takes another'); composed = _.compose(greet, exclaim); - equal(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative'); + assert.equal(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative'); // f(g(h(x, y, z))) function h(x, y, z) { - equal(arguments.length, 3, 'First function called with multiple args'); + assert.equal(arguments.length, 3, 'First function called with multiple args'); return z * y; } function g(x) { - equal(arguments.length, 1, 'Composed function is called with 1 argument'); + assert.equal(arguments.length, 1, 'Composed function is called with 1 argument'); return x; } function f(x) { - equal(arguments.length, 1, 'Composed function is called with 1 argument'); + assert.equal(arguments.length, 1, 'Composed function is called with 1 argument'); return x * 2; } composed = _.compose(f, g, h); - equal(composed(1, 2, 3), 12); + assert.equal(composed(1, 2, 3), 12); }); - test('after', function() { + test('after', function(assert) { var testAfter = function(afterAmount, timesCalled) { var afterCalled = 0; var after = _.after(afterAmount, function() { @@ -551,13 +551,13 @@ return afterCalled; }; - equal(testAfter(5, 5), 1, 'after(N) should fire after being called N times'); - equal(testAfter(5, 4), 0, 'after(N) should not fire unless called N times'); - equal(testAfter(0, 0), 0, 'after(0) should not fire immediately'); - equal(testAfter(0, 1), 1, 'after(0) should fire when first invoked'); + assert.equal(testAfter(5, 5), 1, 'after(N) should fire after being called N times'); + assert.equal(testAfter(5, 4), 0, 'after(N) should not fire unless called N times'); + assert.equal(testAfter(0, 0), 0, 'after(0) should not fire immediately'); + assert.equal(testAfter(0, 1), 1, 'after(0) should fire when first invoked'); }); - test('before', function() { + test('before', function(assert) { var testBefore = function(beforeAmount, timesCalled) { var beforeCalled = 0; var before = _.before(beforeAmount, function() { beforeCalled++; }); @@ -565,58 +565,58 @@ return beforeCalled; }; - equal(testBefore(5, 5), 4, 'before(N) should not fire after being called N times'); - equal(testBefore(5, 4), 4, 'before(N) should fire before being called N times'); - equal(testBefore(0, 0), 0, 'before(0) should not fire immediately'); - equal(testBefore(0, 1), 0, 'before(0) should not fire when first invoked'); + assert.equal(testBefore(5, 5), 4, 'before(N) should not fire after being called N times'); + assert.equal(testBefore(5, 4), 4, 'before(N) should fire before being called N times'); + assert.equal(testBefore(0, 0), 0, 'before(0) should not fire immediately'); + assert.equal(testBefore(0, 1), 0, 'before(0) should not fire when first invoked'); var context = {num: 0}; var increment = _.before(3, function(){ return ++this.num; }); _.times(10, increment, context); - equal(increment(), 2, 'stores a memo to the last value'); - equal(context.num, 2, 'provides context'); + assert.equal(increment(), 2, 'stores a memo to the last value'); + assert.equal(context.num, 2, 'provides context'); }); - test('iteratee', function() { + test('iteratee', function(assert) { var identity = _.iteratee(); - equal(identity, _.identity, '_.iteratee is exposed as an external function.'); + assert.equal(identity, _.identity, '_.iteratee is exposed as an external function.'); function fn() { return arguments; } _.each([_.iteratee(fn), _.iteratee(fn, {})], function(cb) { - equal(cb().length, 0); - deepEqual(_.toArray(cb(1, 2, 3)), _.range(1, 4)); - deepEqual(_.toArray(cb(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)), _.range(1, 11)); + assert.equal(cb().length, 0); + assert.deepEqual(_.toArray(cb(1, 2, 3)), _.range(1, 4)); + assert.deepEqual(_.toArray(cb(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)), _.range(1, 11)); }); }); - test('restArgs', 10, function() { + test('restArgs', 10, function(assert) { _.restArgs(function(a, args) { - strictEqual(a, 1); - deepEqual(args, [2, 3], 'collects rest arguments into an array'); + assert.strictEqual(a, 1); + assert.deepEqual(args, [2, 3], 'collects rest arguments into an array'); })(1, 2, 3); _.restArgs(function(a, args) { - strictEqual(a, void 0); - deepEqual(args, [], 'passes empty array if there are not enough arguments'); + assert.strictEqual(a, void 0); + assert.deepEqual(args, [], 'passes empty array if there are not enough arguments'); })(); _.restArgs(function(a, b, c, args) { - strictEqual(arguments.length, 4); - deepEqual(args, [4, 5], 'works on functions with many named parameters'); + assert.strictEqual(arguments.length, 4); + assert.deepEqual(args, [4, 5], 'works on functions with many named parameters'); })(1, 2, 3, 4, 5); var obj = {}; _.restArgs(function() { - strictEqual(this, obj, 'invokes function with this context'); + assert.strictEqual(this, obj, 'invokes function with this context'); }).call(obj); _.restArgs(function(array, iteratee, context) { - deepEqual(array, [1, 2, 3, 4], 'startIndex can be used manually specify index of rest parameter'); - strictEqual(iteratee, void 0); - strictEqual(context, void 0); + assert.deepEqual(array, [1, 2, 3, 4], 'startIndex can be used manually specify index of rest parameter'); + assert.strictEqual(iteratee, void 0); + assert.strictEqual(context, void 0); }, 0)(1, 2, 3, 4); }); diff --git a/test/objects.js b/test/objects.js index 066264c2b..29bbd9281 100644 --- a/test/objects.js +++ b/test/objects.js @@ -5,16 +5,16 @@ var testElement = typeof document === 'object' ? document.createElement('div') : void 0; - test('keys', function() { - deepEqual(_.keys({one: 1, two: 2}), ['one', 'two'], 'can extract the keys from an object'); + test('keys', function(assert) { + assert.deepEqual(_.keys({one: 1, two: 2}), ['one', 'two'], 'can extract the keys from an object'); // the test above is not safe because it relies on for-in enumeration order var a = []; a[1] = 0; - deepEqual(_.keys(a), ['1'], 'is not fooled by sparse arrays; see issue #95'); - deepEqual(_.keys(null), []); - deepEqual(_.keys(void 0), []); - deepEqual(_.keys(1), []); - deepEqual(_.keys('a'), []); - deepEqual(_.keys(true), []); + assert.deepEqual(_.keys(a), ['1'], 'is not fooled by sparse arrays; see issue #95'); + assert.deepEqual(_.keys(null), []); + assert.deepEqual(_.keys(void 0), []); + assert.deepEqual(_.keys(1), []); + assert.deepEqual(_.keys('a'), []); + assert.deepEqual(_.keys(true), []); // keys that may be missed if the implementation isn't careful var trouble = { @@ -32,20 +32,20 @@ }; var troubleKeys = ['constructor', 'valueOf', 'hasOwnProperty', 'toString', 'toLocaleString', 'propertyIsEnumerable', 'isPrototypeOf', '__defineGetter__', '__defineSetter__', '__lookupSetter__', '__lookupGetter__'].sort(); - deepEqual(_.keys(trouble).sort(), troubleKeys, 'matches non-enumerable properties'); + assert.deepEqual(_.keys(trouble).sort(), troubleKeys, 'matches non-enumerable properties'); }); - test('allKeys', function() { - deepEqual(_.allKeys({one: 1, two: 2}), ['one', 'two'], 'can extract the allKeys from an object'); + test('allKeys', function(assert) { + assert.deepEqual(_.allKeys({one: 1, two: 2}), ['one', 'two'], 'can extract the allKeys from an object'); // the test above is not safe because it relies on for-in enumeration order var a = []; a[1] = 0; - deepEqual(_.allKeys(a), ['1'], 'is not fooled by sparse arrays; see issue #95'); + assert.deepEqual(_.allKeys(a), ['1'], 'is not fooled by sparse arrays; see issue #95'); a.a = a; - deepEqual(_.allKeys(a), ['1', 'a'], 'is not fooled by sparse arrays with additional properties'); + assert.deepEqual(_.allKeys(a), ['1', 'a'], 'is not fooled by sparse arrays with additional properties'); _.each([null, void 0, 1, 'a', true, NaN, {}, [], new Number(5), new Date(0)], function(val) { - deepEqual(_.allKeys(val), []); + assert.deepEqual(_.allKeys(val), []); }); // allKeys that may be missed if the implementation isn't careful @@ -60,255 +60,255 @@ }; var troubleKeys = ['constructor', 'valueOf', 'hasOwnProperty', 'toString', 'toLocaleString', 'propertyIsEnumerable', 'isPrototypeOf'].sort(); - deepEqual(_.allKeys(trouble).sort(), troubleKeys, 'matches non-enumerable properties'); + assert.deepEqual(_.allKeys(trouble).sort(), troubleKeys, 'matches non-enumerable properties'); function A() {} A.prototype.foo = 'foo'; var b = new A(); b.bar = 'bar'; - deepEqual(_.allKeys(b).sort(), ['bar', 'foo'], 'should include inherited keys'); + assert.deepEqual(_.allKeys(b).sort(), ['bar', 'foo'], 'should include inherited keys'); function y() {} y.x = 'z'; - deepEqual(_.allKeys(y), ['x'], 'should get keys from constructor'); + assert.deepEqual(_.allKeys(y), ['x'], 'should get keys from constructor'); }); - test('values', function() { - deepEqual(_.values({one: 1, two: 2}), [1, 2], 'can extract the values from an object'); - deepEqual(_.values({one: 1, two: 2, length: 3}), [1, 2, 3], '... even when one of them is "length"'); + test('values', function(assert) { + assert.deepEqual(_.values({one: 1, two: 2}), [1, 2], 'can extract the values from an object'); + assert.deepEqual(_.values({one: 1, two: 2, length: 3}), [1, 2, 3], '... even when one of them is "length"'); }); - test('pairs', function() { - deepEqual(_.pairs({one: 1, two: 2}), [['one', 1], ['two', 2]], 'can convert an object into pairs'); - deepEqual(_.pairs({one: 1, two: 2, length: 3}), [['one', 1], ['two', 2], ['length', 3]], '... even when one of them is "length"'); + test('pairs', function(assert) { + assert.deepEqual(_.pairs({one: 1, two: 2}), [['one', 1], ['two', 2]], 'can convert an object into pairs'); + assert.deepEqual(_.pairs({one: 1, two: 2, length: 3}), [['one', 1], ['two', 2], ['length', 3]], '... even when one of them is "length"'); }); - test('invert', function() { + test('invert', function(assert) { var obj = {first: 'Moe', second: 'Larry', third: 'Curly'}; - deepEqual(_.keys(_.invert(obj)), ['Moe', 'Larry', 'Curly'], 'can invert an object'); - deepEqual(_.invert(_.invert(obj)), obj, 'two inverts gets you back where you started'); + assert.deepEqual(_.keys(_.invert(obj)), ['Moe', 'Larry', 'Curly'], 'can invert an object'); + assert.deepEqual(_.invert(_.invert(obj)), obj, 'two inverts gets you back where you started'); obj = {length: 3}; - equal(_.invert(obj)['3'], 'length', 'can invert an object with "length"'); + assert.equal(_.invert(obj)['3'], 'length', 'can invert an object with "length"'); }); - test('functions', function() { + test('functions', function(assert) { var obj = {a: 'dash', b: _.map, c: /yo/, d: _.reduce}; - deepEqual(['b', 'd'], _.functions(obj), 'can grab the function names of any passed-in object'); + assert.deepEqual(['b', 'd'], _.functions(obj), 'can grab the function names of any passed-in object'); var Animal = function(){}; Animal.prototype.run = function(){}; - deepEqual(_.functions(new Animal), ['run'], 'also looks up functions on the prototype'); + assert.deepEqual(_.functions(new Animal), ['run'], 'also looks up functions on the prototype'); }); - test('methods', function() { - strictEqual(_.functions, _.methods, 'alias for functions'); + test('methods', function(assert) { + assert.strictEqual(_.functions, _.methods, 'alias for functions'); }); - test('extend', function() { + test('extend', function(assert) { var result; - equal(_.extend({}, {a: 'b'}).a, 'b', 'can extend an object with the attributes of another'); - equal(_.extend({a: 'x'}, {a: 'b'}).a, 'b', 'properties in source override destination'); - equal(_.extend({x: 'x'}, {a: 'b'}).x, 'x', "properties not in source don't get overriden"); + assert.equal(_.extend({}, {a: 'b'}).a, 'b', 'can extend an object with the attributes of another'); + assert.equal(_.extend({a: 'x'}, {a: 'b'}).a, 'b', 'properties in source override destination'); + assert.equal(_.extend({x: 'x'}, {a: 'b'}).x, 'x', "properties not in source don't get overriden"); result = _.extend({x: 'x'}, {a: 'a'}, {b: 'b'}); - deepEqual(result, {x: 'x', a: 'a', b: 'b'}, 'can extend from multiple source objects'); + assert.deepEqual(result, {x: 'x', a: 'a', b: 'b'}, 'can extend from multiple source objects'); result = _.extend({x: 'x'}, {a: 'a', x: 2}, {a: 'b'}); - deepEqual(result, {x: 2, a: 'b'}, 'extending from multiple source objects last property trumps'); + assert.deepEqual(result, {x: 2, a: 'b'}, 'extending from multiple source objects last property trumps'); result = _.extend({}, {a: void 0, b: null}); - deepEqual(_.keys(result), ['a', 'b'], 'extend copies undefined values'); + assert.deepEqual(_.keys(result), ['a', 'b'], 'extend copies undefined values'); var F = function() {}; F.prototype = {a: 'b'}; var subObj = new F(); subObj.c = 'd'; - deepEqual(_.extend({}, subObj), {a: 'b', c: 'd'}, 'extend copies all properties from source'); + assert.deepEqual(_.extend({}, subObj), {a: 'b', c: 'd'}, 'extend copies all properties from source'); _.extend(subObj, {}); - ok(!subObj.hasOwnProperty('a'), "extend does not convert destination object's 'in' properties to 'own' properties"); + assert.ok(!subObj.hasOwnProperty('a'), "extend does not convert destination object's 'in' properties to 'own' properties"); try { result = {}; _.extend(result, null, void 0, {a: 1}); } catch(e) { /* ignored */ } - equal(result.a, 1, 'should not error on `null` or `undefined` sources'); + assert.equal(result.a, 1, 'should not error on `null` or `undefined` sources'); - strictEqual(_.extend(null, {a: 1}), null, 'extending null results in null'); - strictEqual(_.extend(void 0, {a: 1}), void 0, 'extending undefined results in undefined'); + assert.strictEqual(_.extend(null, {a: 1}), null, 'extending null results in null'); + assert.strictEqual(_.extend(void 0, {a: 1}), void 0, 'extending undefined results in undefined'); }); - test('extendOwn', function() { + test('extendOwn', function(assert) { var result; - equal(_.extendOwn({}, {a: 'b'}).a, 'b', 'can assign an object with the attributes of another'); - equal(_.extendOwn({a: 'x'}, {a: 'b'}).a, 'b', 'properties in source override destination'); - equal(_.extendOwn({x: 'x'}, {a: 'b'}).x, 'x', "properties not in source don't get overriden"); + assert.equal(_.extendOwn({}, {a: 'b'}).a, 'b', 'can assign an object with the attributes of another'); + assert.equal(_.extendOwn({a: 'x'}, {a: 'b'}).a, 'b', 'properties in source override destination'); + assert.equal(_.extendOwn({x: 'x'}, {a: 'b'}).x, 'x', "properties not in source don't get overriden"); result = _.extendOwn({x: 'x'}, {a: 'a'}, {b: 'b'}); - deepEqual(result, {x: 'x', a: 'a', b: 'b'}, 'can assign from multiple source objects'); + assert.deepEqual(result, {x: 'x', a: 'a', b: 'b'}, 'can assign from multiple source objects'); result = _.assign({x: 'x'}, {a: 'a', x: 2}, {a: 'b'}); - deepEqual(result, {x: 2, a: 'b'}, 'assigning from multiple source objects last property trumps'); - deepEqual(_.extendOwn({}, {a: void 0, b: null}), {a: void 0, b: null}, 'assign copies undefined values'); + assert.deepEqual(result, {x: 2, a: 'b'}, 'assigning from multiple source objects last property trumps'); + assert.deepEqual(_.extendOwn({}, {a: void 0, b: null}), {a: void 0, b: null}, 'assign copies undefined values'); var F = function() {}; F.prototype = {a: 'b'}; var subObj = new F(); subObj.c = 'd'; - deepEqual(_.extendOwn({}, subObj), {c: 'd'}, 'assign copies own properties from source'); + assert.deepEqual(_.extendOwn({}, subObj), {c: 'd'}, 'assign copies own properties from source'); result = {}; - deepEqual(_.assign(result, null, void 0, {a: 1}), {a: 1}, 'should not error on `null` or `undefined` sources'); + assert.deepEqual(_.assign(result, null, void 0, {a: 1}), {a: 1}, 'should not error on `null` or `undefined` sources'); _.each(['a', 5, null, false], function(val) { - strictEqual(_.assign(val, {a: 1}), val, 'assigning non-objects results in returning the non-object value'); + assert.strictEqual(_.assign(val, {a: 1}), val, 'assigning non-objects results in returning the non-object value'); }); - strictEqual(_.extendOwn(void 0, {a: 1}), void 0, 'assigning undefined results in undefined'); + assert.strictEqual(_.extendOwn(void 0, {a: 1}), void 0, 'assigning undefined results in undefined'); result = _.extendOwn({a: 1, 0: 2, 1: '5', length: 6}, {0: 1, 1: 2, length: 2}); - deepEqual(result, {a: 1, 0: 1, 1: 2, length: 2}, 'assign should treat array-like objects like normal objects'); + assert.deepEqual(result, {a: 1, 0: 1, 1: 2, length: 2}, 'assign should treat array-like objects like normal objects'); }); - test('pick', function() { + test('pick', function(assert) { var result; result = _.pick({a: 1, b: 2, c: 3}, 'a', 'c'); - deepEqual(result, {a: 1, c: 3}, 'can restrict properties to those named'); + assert.deepEqual(result, {a: 1, c: 3}, 'can restrict properties to those named'); result = _.pick({a: 1, b: 2, c: 3}, ['b', 'c']); - deepEqual(result, {b: 2, c: 3}, 'can restrict properties to those named in an array'); + assert.deepEqual(result, {b: 2, c: 3}, 'can restrict properties to those named in an array'); result = _.pick({a: 1, b: 2, c: 3}, ['a'], 'b'); - deepEqual(result, {a: 1, b: 2}, 'can restrict properties to those named in mixed args'); + assert.deepEqual(result, {a: 1, b: 2}, 'can restrict properties to those named in mixed args'); result = _.pick(['a', 'b'], 1); - deepEqual(result, {1: 'b'}, 'can pick numeric properties'); + assert.deepEqual(result, {1: 'b'}, 'can pick numeric properties'); _.each([null, void 0], function(val) { - deepEqual(_.pick(val, 'hasOwnProperty'), {}, 'Called with null/undefined'); - deepEqual(_.pick(val, _.constant(true)), {}); + assert.deepEqual(_.pick(val, 'hasOwnProperty'), {}, 'Called with null/undefined'); + assert.deepEqual(_.pick(val, _.constant(true)), {}); }); - deepEqual(_.pick(5, 'toString', 'b'), {toString: Number.prototype.toString}, 'can iterate primitives'); + assert.deepEqual(_.pick(5, 'toString', 'b'), {toString: Number.prototype.toString}, 'can iterate primitives'); var data = {a: 1, b: 2, c: 3}; var callback = function(value, key, object) { - strictEqual(key, {1: 'a', 2: 'b', 3: 'c'}[value]); - strictEqual(object, data); + assert.strictEqual(key, {1: 'a', 2: 'b', 3: 'c'}[value]); + assert.strictEqual(object, data); return value !== this.value; }; result = _.pick(data, callback, {value: 2}); - deepEqual(result, {a: 1, c: 3}, 'can accept a predicate and context'); + assert.deepEqual(result, {a: 1, c: 3}, 'can accept a predicate and context'); var Obj = function(){}; Obj.prototype = {a: 1, b: 2, c: 3}; var instance = new Obj(); - deepEqual(_.pick(instance, 'a', 'c'), {a: 1, c: 3}, 'include prototype props'); + assert.deepEqual(_.pick(instance, 'a', 'c'), {a: 1, c: 3}, 'include prototype props'); - deepEqual(_.pick(data, function(val, key) { + assert.deepEqual(_.pick(data, function(val, key) { return this[key] === 3 && this === instance; }, instance), {c: 3}, 'function is given context'); - ok(!_.has(_.pick({}, 'foo'), 'foo'), 'does not set own property if property not in object'); + assert.ok(!_.has(_.pick({}, 'foo'), 'foo'), 'does not set own property if property not in object'); _.pick(data, function(value, key, obj) { - equal(obj, data, 'passes same object as third parameter of iteratee'); + assert.equal(obj, data, 'passes same object as third parameter of iteratee'); }); }); - test('omit', function() { + test('omit', function(assert) { var result; result = _.omit({a: 1, b: 2, c: 3}, 'b'); - deepEqual(result, {a: 1, c: 3}, 'can omit a single named property'); + assert.deepEqual(result, {a: 1, c: 3}, 'can omit a single named property'); result = _.omit({a: 1, b: 2, c: 3}, 'a', 'c'); - deepEqual(result, {b: 2}, 'can omit several named properties'); + assert.deepEqual(result, {b: 2}, 'can omit several named properties'); result = _.omit({a: 1, b: 2, c: 3}, ['b', 'c']); - deepEqual(result, {a: 1}, 'can omit properties named in an array'); + assert.deepEqual(result, {a: 1}, 'can omit properties named in an array'); result = _.omit(['a', 'b'], 0); - deepEqual(result, {1: 'b'}, 'can omit numeric properties'); + assert.deepEqual(result, {1: 'b'}, 'can omit numeric properties'); - deepEqual(_.omit(null, 'a', 'b'), {}, 'non objects return empty object'); - deepEqual(_.omit(void 0, 'toString'), {}, 'null/undefined return empty object'); - deepEqual(_.omit(5, 'toString', 'b'), {}, 'returns empty object for primitives'); + assert.deepEqual(_.omit(null, 'a', 'b'), {}, 'non objects return empty object'); + assert.deepEqual(_.omit(void 0, 'toString'), {}, 'null/undefined return empty object'); + assert.deepEqual(_.omit(5, 'toString', 'b'), {}, 'returns empty object for primitives'); var data = {a: 1, b: 2, c: 3}; var callback = function(value, key, object) { - strictEqual(key, {1: 'a', 2: 'b', 3: 'c'}[value]); - strictEqual(object, data); + assert.strictEqual(key, {1: 'a', 2: 'b', 3: 'c'}[value]); + assert.strictEqual(object, data); return value !== this.value; }; result = _.omit(data, callback, {value: 2}); - deepEqual(result, {b: 2}, 'can accept a predicate'); + assert.deepEqual(result, {b: 2}, 'can accept a predicate'); var Obj = function(){}; Obj.prototype = {a: 1, b: 2, c: 3}; var instance = new Obj(); - deepEqual(_.omit(instance, 'b'), {a: 1, c: 3}, 'include prototype props'); + assert.deepEqual(_.omit(instance, 'b'), {a: 1, c: 3}, 'include prototype props'); - deepEqual(_.omit(data, function(val, key) { + assert.deepEqual(_.omit(data, function(val, key) { return this[key] === 3 && this === instance; }, instance), {a: 1, b: 2}, 'function is given context'); }); - test('defaults', function() { + test('defaults', function(assert) { var options = {zero: 0, one: 1, empty: '', nan: NaN, nothing: null}; _.defaults(options, {zero: 1, one: 10, twenty: 20, nothing: 'str'}); - equal(options.zero, 0, 'value exists'); - equal(options.one, 1, 'value exists'); - equal(options.twenty, 20, 'default applied'); - equal(options.nothing, null, "null isn't overridden"); + assert.equal(options.zero, 0, 'value exists'); + assert.equal(options.one, 1, 'value exists'); + assert.equal(options.twenty, 20, 'default applied'); + assert.equal(options.nothing, null, "null isn't overridden"); _.defaults(options, {empty: 'full'}, {nan: 'nan'}, {word: 'word'}, {word: 'dog'}); - equal(options.empty, '', 'value exists'); - ok(_.isNaN(options.nan), "NaN isn't overridden"); - equal(options.word, 'word', 'new value is added, first one wins'); + assert.equal(options.empty, '', 'value exists'); + assert.ok(_.isNaN(options.nan), "NaN isn't overridden"); + assert.equal(options.word, 'word', 'new value is added, first one wins'); try { options = {}; _.defaults(options, null, void 0, {a: 1}); } catch(e) { /* ignored */ } - equal(options.a, 1, 'should not error on `null` or `undefined` sources'); + assert.equal(options.a, 1, 'should not error on `null` or `undefined` sources'); - deepEqual(_.defaults(null, {a: 1}), {a: 1}, 'defaults skips nulls'); - deepEqual(_.defaults(void 0, {a: 1}), {a: 1}, 'defaults skips undefined'); + assert.deepEqual(_.defaults(null, {a: 1}), {a: 1}, 'defaults skips nulls'); + assert.deepEqual(_.defaults(void 0, {a: 1}), {a: 1}, 'defaults skips undefined'); }); - test('clone', function() { + test('clone', function(assert) { var moe = {name: 'moe', lucky: [13, 27, 34]}; var clone = _.clone(moe); - equal(clone.name, 'moe', 'the clone as the attributes of the original'); + assert.equal(clone.name, 'moe', 'the clone as the attributes of the original'); clone.name = 'curly'; - ok(clone.name === 'curly' && moe.name === 'moe', 'clones can change shallow attributes without affecting the original'); + assert.ok(clone.name === 'curly' && moe.name === 'moe', 'clones can change shallow attributes without affecting the original'); clone.lucky.push(101); - equal(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original'); + assert.equal(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original'); - equal(_.clone(void 0), void 0, 'non objects should not be changed by clone'); - equal(_.clone(1), 1, 'non objects should not be changed by clone'); - equal(_.clone(null), null, 'non objects should not be changed by clone'); + assert.equal(_.clone(void 0), void 0, 'non objects should not be changed by clone'); + assert.equal(_.clone(1), 1, 'non objects should not be changed by clone'); + assert.equal(_.clone(null), null, 'non objects should not be changed by clone'); }); - test('create', function() { + test('create', function(assert) { var Parent = function() {}; Parent.prototype = {foo: function() {}, bar: 2}; _.each(['foo', null, void 0, 1], function(val) { - deepEqual(_.create(val), {}, 'should return empty object when a non-object is provided'); + assert.deepEqual(_.create(val), {}, 'should return empty object when a non-object is provided'); }); - ok(_.create([]) instanceof Array, 'should return new instance of array when array is provided'); + assert.ok(_.create([]) instanceof Array, 'should return new instance of array when array is provided'); var Child = function() {}; Child.prototype = _.create(Parent.prototype); - ok(new Child instanceof Parent, 'object should inherit prototype'); + assert.ok(new Child instanceof Parent, 'object should inherit prototype'); var func = function() {}; Child.prototype = _.create(Parent.prototype, {func: func}); - strictEqual(Child.prototype.func, func, 'properties should be added to object'); + assert.strictEqual(Child.prototype.func, func, 'properties should be added to object'); Child.prototype = _.create(Parent.prototype, {constructor: Child}); - strictEqual(Child.prototype.constructor, Child); + assert.strictEqual(Child.prototype.constructor, Child); Child.prototype.foo = 'foo'; var created = _.create(Child.prototype, new Child); - ok(!created.hasOwnProperty('foo'), 'should only add own properties'); + assert.ok(!created.hasOwnProperty('foo'), 'should only add own properties'); }); - test('isEqual', function() { + test('isEqual', function(assert) { function First() { this.value = 1; } @@ -319,128 +319,128 @@ Second.prototype.value = 2; // Basic equality and identity comparisons. - ok(_.isEqual(null, null), '`null` is equal to `null`'); - ok(_.isEqual(), '`undefined` is equal to `undefined`'); + assert.ok(_.isEqual(null, null), '`null` is equal to `null`'); + assert.ok(_.isEqual(), '`undefined` is equal to `undefined`'); - ok(!_.isEqual(0, -0), '`0` is not equal to `-0`'); - ok(!_.isEqual(-0, 0), 'Commutative equality is implemented for `0` and `-0`'); - ok(!_.isEqual(null, void 0), '`null` is not equal to `undefined`'); - ok(!_.isEqual(void 0, null), 'Commutative equality is implemented for `null` and `undefined`'); + assert.ok(!_.isEqual(0, -0), '`0` is not equal to `-0`'); + assert.ok(!_.isEqual(-0, 0), 'Commutative equality is implemented for `0` and `-0`'); + assert.ok(!_.isEqual(null, void 0), '`null` is not equal to `undefined`'); + assert.ok(!_.isEqual(void 0, null), 'Commutative equality is implemented for `null` and `undefined`'); // String object and primitive comparisons. - ok(_.isEqual('Curly', 'Curly'), 'Identical string primitives are equal'); - ok(_.isEqual(new String('Curly'), new String('Curly')), 'String objects with identical primitive values are equal'); - ok(_.isEqual(new String('Curly'), 'Curly'), 'String primitives and their corresponding object wrappers are equal'); - ok(_.isEqual('Curly', new String('Curly')), 'Commutative equality is implemented for string objects and primitives'); + assert.ok(_.isEqual('Curly', 'Curly'), 'Identical string primitives are equal'); + assert.ok(_.isEqual(new String('Curly'), new String('Curly')), 'String objects with identical primitive values are equal'); + assert.ok(_.isEqual(new String('Curly'), 'Curly'), 'String primitives and their corresponding object wrappers are equal'); + assert.ok(_.isEqual('Curly', new String('Curly')), 'Commutative equality is implemented for string objects and primitives'); - ok(!_.isEqual('Curly', 'Larry'), 'String primitives with different values are not equal'); - ok(!_.isEqual(new String('Curly'), new String('Larry')), 'String objects with different primitive values are not equal'); - ok(!_.isEqual(new String('Curly'), {toString: function(){ return 'Curly'; }}), 'String objects and objects with a custom `toString` method are not equal'); + assert.ok(!_.isEqual('Curly', 'Larry'), 'String primitives with different values are not equal'); + assert.ok(!_.isEqual(new String('Curly'), new String('Larry')), 'String objects with different primitive values are not equal'); + assert.ok(!_.isEqual(new String('Curly'), {toString: function(){ return 'Curly'; }}), 'String objects and objects with a custom `toString` method are not equal'); // Number object and primitive comparisons. - ok(_.isEqual(75, 75), 'Identical number primitives are equal'); - ok(_.isEqual(new Number(75), new Number(75)), 'Number objects with identical primitive values are equal'); - ok(_.isEqual(75, new Number(75)), 'Number primitives and their corresponding object wrappers are equal'); - ok(_.isEqual(new Number(75), 75), 'Commutative equality is implemented for number objects and primitives'); - ok(!_.isEqual(new Number(0), -0), '`new Number(0)` and `-0` are not equal'); - ok(!_.isEqual(0, new Number(-0)), 'Commutative equality is implemented for `new Number(0)` and `-0`'); + assert.ok(_.isEqual(75, 75), 'Identical number primitives are equal'); + assert.ok(_.isEqual(new Number(75), new Number(75)), 'Number objects with identical primitive values are equal'); + assert.ok(_.isEqual(75, new Number(75)), 'Number primitives and their corresponding object wrappers are equal'); + assert.ok(_.isEqual(new Number(75), 75), 'Commutative equality is implemented for number objects and primitives'); + assert.ok(!_.isEqual(new Number(0), -0), '`new Number(0)` and `-0` are not equal'); + assert.ok(!_.isEqual(0, new Number(-0)), 'Commutative equality is implemented for `new Number(0)` and `-0`'); - ok(!_.isEqual(new Number(75), new Number(63)), 'Number objects with different primitive values are not equal'); - ok(!_.isEqual(new Number(63), {valueOf: function(){ return 63; }}), 'Number objects and objects with a `valueOf` method are not equal'); + assert.ok(!_.isEqual(new Number(75), new Number(63)), 'Number objects with different primitive values are not equal'); + assert.ok(!_.isEqual(new Number(63), {valueOf: function(){ return 63; }}), 'Number objects and objects with a `valueOf` method are not equal'); // Comparisons involving `NaN`. - ok(_.isEqual(NaN, NaN), '`NaN` is equal to `NaN`'); - ok(_.isEqual(new Number(NaN), NaN), 'Object(`NaN`) is equal to `NaN`'); - ok(!_.isEqual(61, NaN), 'A number primitive is not equal to `NaN`'); - ok(!_.isEqual(new Number(79), NaN), 'A number object is not equal to `NaN`'); - ok(!_.isEqual(Infinity, NaN), '`Infinity` is not equal to `NaN`'); + assert.ok(_.isEqual(NaN, NaN), '`NaN` is equal to `NaN`'); + assert.ok(_.isEqual(new Number(NaN), NaN), 'Object(`NaN`) is equal to `NaN`'); + assert.ok(!_.isEqual(61, NaN), 'A number primitive is not equal to `NaN`'); + assert.ok(!_.isEqual(new Number(79), NaN), 'A number object is not equal to `NaN`'); + assert.ok(!_.isEqual(Infinity, NaN), '`Infinity` is not equal to `NaN`'); // Boolean object and primitive comparisons. - ok(_.isEqual(true, true), 'Identical boolean primitives are equal'); - ok(_.isEqual(new Boolean, new Boolean), 'Boolean objects with identical primitive values are equal'); - ok(_.isEqual(true, new Boolean(true)), 'Boolean primitives and their corresponding object wrappers are equal'); - ok(_.isEqual(new Boolean(true), true), 'Commutative equality is implemented for booleans'); - ok(!_.isEqual(new Boolean(true), new Boolean), 'Boolean objects with different primitive values are not equal'); + assert.ok(_.isEqual(true, true), 'Identical boolean primitives are equal'); + assert.ok(_.isEqual(new Boolean, new Boolean), 'Boolean objects with identical primitive values are equal'); + assert.ok(_.isEqual(true, new Boolean(true)), 'Boolean primitives and their corresponding object wrappers are equal'); + assert.ok(_.isEqual(new Boolean(true), true), 'Commutative equality is implemented for booleans'); + assert.ok(!_.isEqual(new Boolean(true), new Boolean), 'Boolean objects with different primitive values are not equal'); // Common type coercions. - ok(!_.isEqual(new Boolean(false), true), '`new Boolean(false)` is not equal to `true`'); - ok(!_.isEqual('75', 75), 'String and number primitives with like values are not equal'); - ok(!_.isEqual(new Number(63), new String(63)), 'String and number objects with like values are not equal'); - ok(!_.isEqual(75, '75'), 'Commutative equality is implemented for like string and number values'); - ok(!_.isEqual(0, ''), 'Number and string primitives with like values are not equal'); - ok(!_.isEqual(1, true), 'Number and boolean primitives with like values are not equal'); - ok(!_.isEqual(new Boolean(false), new Number(0)), 'Boolean and number objects with like values are not equal'); - ok(!_.isEqual(false, new String('')), 'Boolean primitives and string objects with like values are not equal'); - ok(!_.isEqual(12564504e5, new Date(2009, 9, 25)), 'Dates and their corresponding numeric primitive values are not equal'); + assert.ok(!_.isEqual(new Boolean(false), true), '`new Boolean(false)` is not equal to `true`'); + assert.ok(!_.isEqual('75', 75), 'String and number primitives with like values are not equal'); + assert.ok(!_.isEqual(new Number(63), new String(63)), 'String and number objects with like values are not equal'); + assert.ok(!_.isEqual(75, '75'), 'Commutative equality is implemented for like string and number values'); + assert.ok(!_.isEqual(0, ''), 'Number and string primitives with like values are not equal'); + assert.ok(!_.isEqual(1, true), 'Number and boolean primitives with like values are not equal'); + assert.ok(!_.isEqual(new Boolean(false), new Number(0)), 'Boolean and number objects with like values are not equal'); + assert.ok(!_.isEqual(false, new String('')), 'Boolean primitives and string objects with like values are not equal'); + assert.ok(!_.isEqual(12564504e5, new Date(2009, 9, 25)), 'Dates and their corresponding numeric primitive values are not equal'); // Dates. - ok(_.isEqual(new Date(2009, 9, 25), new Date(2009, 9, 25)), 'Date objects referencing identical times are equal'); - ok(!_.isEqual(new Date(2009, 9, 25), new Date(2009, 11, 13)), 'Date objects referencing different times are not equal'); - ok(!_.isEqual(new Date(2009, 11, 13), { + assert.ok(_.isEqual(new Date(2009, 9, 25), new Date(2009, 9, 25)), 'Date objects referencing identical times are equal'); + assert.ok(!_.isEqual(new Date(2009, 9, 25), new Date(2009, 11, 13)), 'Date objects referencing different times are not equal'); + assert.ok(!_.isEqual(new Date(2009, 11, 13), { getTime: function(){ return 12606876e5; } }), 'Date objects and objects with a `getTime` method are not equal'); - ok(!_.isEqual(new Date('Curly'), new Date('Curly')), 'Invalid dates are not equal'); + assert.ok(!_.isEqual(new Date('Curly'), new Date('Curly')), 'Invalid dates are not equal'); // Functions. - ok(!_.isEqual(First, Second), 'Different functions with identical bodies and source code representations are not equal'); + assert.ok(!_.isEqual(First, Second), 'Different functions with identical bodies and source code representations are not equal'); // RegExps. - ok(_.isEqual(/(?:)/gim, /(?:)/gim), 'RegExps with equivalent patterns and flags are equal'); - ok(_.isEqual(/(?:)/gi, /(?:)/ig), 'Flag order is not significant'); - ok(!_.isEqual(/(?:)/g, /(?:)/gi), 'RegExps with equivalent patterns and different flags are not equal'); - ok(!_.isEqual(/Moe/gim, /Curly/gim), 'RegExps with different patterns and equivalent flags are not equal'); - ok(!_.isEqual(/(?:)/gi, /(?:)/g), 'Commutative equality is implemented for RegExps'); - ok(!_.isEqual(/Curly/g, {source: 'Larry', global: true, ignoreCase: false, multiline: false}), 'RegExps and RegExp-like objects are not equal'); + assert.ok(_.isEqual(/(?:)/gim, /(?:)/gim), 'RegExps with equivalent patterns and flags are equal'); + assert.ok(_.isEqual(/(?:)/gi, /(?:)/ig), 'Flag order is not significant'); + assert.ok(!_.isEqual(/(?:)/g, /(?:)/gi), 'RegExps with equivalent patterns and different flags are not equal'); + assert.ok(!_.isEqual(/Moe/gim, /Curly/gim), 'RegExps with different patterns and equivalent flags are not equal'); + assert.ok(!_.isEqual(/(?:)/gi, /(?:)/g), 'Commutative equality is implemented for RegExps'); + assert.ok(!_.isEqual(/Curly/g, {source: 'Larry', global: true, ignoreCase: false, multiline: false}), 'RegExps and RegExp-like objects are not equal'); // Empty arrays, array-like objects, and object literals. - ok(_.isEqual({}, {}), 'Empty object literals are equal'); - ok(_.isEqual([], []), 'Empty array literals are equal'); - ok(_.isEqual([{}], [{}]), 'Empty nested arrays and objects are equal'); - ok(!_.isEqual({length: 0}, []), 'Array-like objects and arrays are not equal.'); - ok(!_.isEqual([], {length: 0}), 'Commutative equality is implemented for array-like objects'); + assert.ok(_.isEqual({}, {}), 'Empty object literals are equal'); + assert.ok(_.isEqual([], []), 'Empty array literals are equal'); + assert.ok(_.isEqual([{}], [{}]), 'Empty nested arrays and objects are equal'); + assert.ok(!_.isEqual({length: 0}, []), 'Array-like objects and arrays are not equal.'); + assert.ok(!_.isEqual([], {length: 0}), 'Commutative equality is implemented for array-like objects'); - ok(!_.isEqual({}, []), 'Object literals and array literals are not equal'); - ok(!_.isEqual([], {}), 'Commutative equality is implemented for objects and arrays'); + assert.ok(!_.isEqual({}, []), 'Object literals and array literals are not equal'); + assert.ok(!_.isEqual([], {}), 'Commutative equality is implemented for objects and arrays'); // Arrays with primitive and object values. - ok(_.isEqual([1, 'Larry', true], [1, 'Larry', true]), 'Arrays containing identical primitives are equal'); - ok(_.isEqual([/Moe/g, new Date(2009, 9, 25)], [/Moe/g, new Date(2009, 9, 25)]), 'Arrays containing equivalent elements are equal'); + assert.ok(_.isEqual([1, 'Larry', true], [1, 'Larry', true]), 'Arrays containing identical primitives are equal'); + assert.ok(_.isEqual([/Moe/g, new Date(2009, 9, 25)], [/Moe/g, new Date(2009, 9, 25)]), 'Arrays containing equivalent elements are equal'); // Multi-dimensional arrays. var a = [new Number(47), false, 'Larry', /Moe/, new Date(2009, 11, 13), ['running', 'biking', new String('programming')], {a: 47}]; var b = [new Number(47), false, 'Larry', /Moe/, new Date(2009, 11, 13), ['running', 'biking', new String('programming')], {a: 47}]; - ok(_.isEqual(a, b), 'Arrays containing nested arrays and objects are recursively compared'); + assert.ok(_.isEqual(a, b), 'Arrays containing nested arrays and objects are recursively compared'); // Overwrite the methods defined in ES 5.1 section 15.4.4. a.forEach = a.map = a.filter = a.every = a.indexOf = a.lastIndexOf = a.some = a.reduce = a.reduceRight = null; b.join = b.pop = b.reverse = b.shift = b.slice = b.splice = b.concat = b.sort = b.unshift = null; // Array elements and properties. - ok(_.isEqual(a, b), 'Arrays containing equivalent elements and different non-numeric properties are equal'); + assert.ok(_.isEqual(a, b), 'Arrays containing equivalent elements and different non-numeric properties are equal'); a.push('White Rocks'); - ok(!_.isEqual(a, b), 'Arrays of different lengths are not equal'); + assert.ok(!_.isEqual(a, b), 'Arrays of different lengths are not equal'); a.push('East Boulder'); b.push('Gunbarrel Ranch', 'Teller Farm'); - ok(!_.isEqual(a, b), 'Arrays of identical lengths containing different elements are not equal'); + assert.ok(!_.isEqual(a, b), 'Arrays of identical lengths containing different elements are not equal'); // Sparse arrays. - ok(_.isEqual(Array(3), Array(3)), 'Sparse arrays of identical lengths are equal'); - ok(!_.isEqual(Array(3), Array(6)), 'Sparse arrays of different lengths are not equal when both are empty'); + assert.ok(_.isEqual(Array(3), Array(3)), 'Sparse arrays of identical lengths are equal'); + assert.ok(!_.isEqual(Array(3), Array(6)), 'Sparse arrays of different lengths are not equal when both are empty'); var sparse = []; sparse[1] = 5; - ok(_.isEqual(sparse, [void 0, 5]), 'Handles sparse arrays as dense'); + assert.ok(_.isEqual(sparse, [void 0, 5]), 'Handles sparse arrays as dense'); // Simple objects. - ok(_.isEqual({a: 'Curly', b: 1, c: true}, {a: 'Curly', b: 1, c: true}), 'Objects containing identical primitives are equal'); - ok(_.isEqual({a: /Curly/g, b: new Date(2009, 11, 13)}, {a: /Curly/g, b: new Date(2009, 11, 13)}), 'Objects containing equivalent members are equal'); - ok(!_.isEqual({a: 63, b: 75}, {a: 61, b: 55}), 'Objects of identical sizes with different values are not equal'); - ok(!_.isEqual({a: 63, b: 75}, {a: 61, c: 55}), 'Objects of identical sizes with different property names are not equal'); - ok(!_.isEqual({a: 1, b: 2}, {a: 1}), 'Objects of different sizes are not equal'); - ok(!_.isEqual({a: 1}, {a: 1, b: 2}), 'Commutative equality is implemented for objects'); - ok(!_.isEqual({x: 1, y: void 0}, {x: 1, z: 2}), 'Objects with identical keys and different values are not equivalent'); + assert.ok(_.isEqual({a: 'Curly', b: 1, c: true}, {a: 'Curly', b: 1, c: true}), 'Objects containing identical primitives are equal'); + assert.ok(_.isEqual({a: /Curly/g, b: new Date(2009, 11, 13)}, {a: /Curly/g, b: new Date(2009, 11, 13)}), 'Objects containing equivalent members are equal'); + assert.ok(!_.isEqual({a: 63, b: 75}, {a: 61, b: 55}), 'Objects of identical sizes with different values are not equal'); + assert.ok(!_.isEqual({a: 63, b: 75}, {a: 61, c: 55}), 'Objects of identical sizes with different property names are not equal'); + assert.ok(!_.isEqual({a: 1, b: 2}, {a: 1}), 'Objects of different sizes are not equal'); + assert.ok(!_.isEqual({a: 1}, {a: 1, b: 2}), 'Commutative equality is implemented for objects'); + assert.ok(!_.isEqual({x: 1, y: void 0}, {x: 1, z: 2}), 'Objects with identical keys and different values are not equivalent'); // `A` contains nested objects and arrays. a = { @@ -471,456 +471,456 @@ seconds: 54 } }; - ok(_.isEqual(a, b), 'Objects with nested equivalent members are recursively compared'); + assert.ok(_.isEqual(a, b), 'Objects with nested equivalent members are recursively compared'); // Instances. - ok(_.isEqual(new First, new First), 'Object instances are equal'); - ok(!_.isEqual(new First, new Second), 'Objects with different constructors and identical own properties are not equal'); - ok(!_.isEqual({value: 1}, new First), 'Object instances and objects sharing equivalent properties are not equal'); - ok(!_.isEqual({value: 2}, new Second), 'The prototype chain of objects should not be examined'); + assert.ok(_.isEqual(new First, new First), 'Object instances are equal'); + assert.ok(!_.isEqual(new First, new Second), 'Objects with different constructors and identical own properties are not equal'); + assert.ok(!_.isEqual({value: 1}, new First), 'Object instances and objects sharing equivalent properties are not equal'); + assert.ok(!_.isEqual({value: 2}, new Second), 'The prototype chain of objects should not be examined'); // Circular Arrays. (a = []).push(a); (b = []).push(b); - ok(_.isEqual(a, b), 'Arrays containing circular references are equal'); + assert.ok(_.isEqual(a, b), 'Arrays containing circular references are equal'); a.push(new String('Larry')); b.push(new String('Larry')); - ok(_.isEqual(a, b), 'Arrays containing circular references and equivalent properties are equal'); + assert.ok(_.isEqual(a, b), 'Arrays containing circular references and equivalent properties are equal'); a.push('Shemp'); b.push('Curly'); - ok(!_.isEqual(a, b), 'Arrays containing circular references and different properties are not equal'); + assert.ok(!_.isEqual(a, b), 'Arrays containing circular references and different properties are not equal'); // More circular arrays #767. a = ['everything is checked but', 'this', 'is not']; a[1] = a; b = ['everything is checked but', ['this', 'array'], 'is not']; - ok(!_.isEqual(a, b), 'Comparison of circular references with non-circular references are not equal'); + assert.ok(!_.isEqual(a, b), 'Comparison of circular references with non-circular references are not equal'); // Circular Objects. a = {abc: null}; b = {abc: null}; a.abc = a; b.abc = b; - ok(_.isEqual(a, b), 'Objects containing circular references are equal'); + assert.ok(_.isEqual(a, b), 'Objects containing circular references are equal'); a.def = 75; b.def = 75; - ok(_.isEqual(a, b), 'Objects containing circular references and equivalent properties are equal'); + assert.ok(_.isEqual(a, b), 'Objects containing circular references and equivalent properties are equal'); a.def = new Number(75); b.def = new Number(63); - ok(!_.isEqual(a, b), 'Objects containing circular references and different properties are not equal'); + assert.ok(!_.isEqual(a, b), 'Objects containing circular references and different properties are not equal'); // More circular objects #767. a = {everything: 'is checked', but: 'this', is: 'not'}; a.but = a; b = {everything: 'is checked', but: {that: 'object'}, is: 'not'}; - ok(!_.isEqual(a, b), 'Comparison of circular references with non-circular object references are not equal'); + assert.ok(!_.isEqual(a, b), 'Comparison of circular references with non-circular object references are not equal'); // Cyclic Structures. a = [{abc: null}]; b = [{abc: null}]; (a[0].abc = a).push(a); (b[0].abc = b).push(b); - ok(_.isEqual(a, b), 'Cyclic structures are equal'); + assert.ok(_.isEqual(a, b), 'Cyclic structures are equal'); a[0].def = 'Larry'; b[0].def = 'Larry'; - ok(_.isEqual(a, b), 'Cyclic structures containing equivalent properties are equal'); + assert.ok(_.isEqual(a, b), 'Cyclic structures containing equivalent properties are equal'); a[0].def = new String('Larry'); b[0].def = new String('Curly'); - ok(!_.isEqual(a, b), 'Cyclic structures containing different properties are not equal'); + assert.ok(!_.isEqual(a, b), 'Cyclic structures containing different properties are not equal'); // Complex Circular References. a = {foo: {b: {foo: {c: {foo: null}}}}}; b = {foo: {b: {foo: {c: {foo: null}}}}}; a.foo.b.foo.c.foo = a; b.foo.b.foo.c.foo = b; - ok(_.isEqual(a, b), 'Cyclic structures with nested and identically-named properties are equal'); + assert.ok(_.isEqual(a, b), 'Cyclic structures with nested and identically-named properties are equal'); // Chaining. - ok(!_.isEqual(_({x: 1, y: void 0}).chain(), _({x: 1, z: 2}).chain()), 'Chained objects containing different values are not equal'); + assert.ok(!_.isEqual(_({x: 1, y: void 0}).chain(), _({x: 1, z: 2}).chain()), 'Chained objects containing different values are not equal'); a = _({x: 1, y: 2}).chain(); b = _({x: 1, y: 2}).chain(); - equal(_.isEqual(a.isEqual(b), _(true)), true, '`isEqual` can be chained'); + assert.equal(_.isEqual(a.isEqual(b), _(true)), true, '`isEqual` can be chained'); // Objects without a `constructor` property if (Object.create) { a = Object.create(null, {x: {value: 1, enumerable: true}}); b = {x: 1}; - ok(_.isEqual(a, b), 'Handles objects without a constructor (e.g. from Object.create'); + assert.ok(_.isEqual(a, b), 'Handles objects without a constructor (e.g. from Object.create'); } function Foo() { this.a = 1; } Foo.prototype.constructor = null; var other = {a: 1}; - strictEqual(_.isEqual(new Foo, other), false, 'Objects from different constructors are not equal'); + assert.strictEqual(_.isEqual(new Foo, other), false, 'Objects from different constructors are not equal'); // Tricky object cases val comparisions - equal(_.isEqual([0], [-0]), false); - equal(_.isEqual({a: 0}, {a: -0}), false); - equal(_.isEqual([NaN], [NaN]), true); - equal(_.isEqual({a: NaN}, {a: NaN}), true); - }); - - test('isEmpty', function() { - ok(!_([1]).isEmpty(), '[1] is not empty'); - ok(_.isEmpty([]), '[] is empty'); - ok(!_.isEmpty({one: 1}), '{one: 1} is not empty'); - ok(_.isEmpty({}), '{} is empty'); - ok(_.isEmpty(new RegExp('')), 'objects with prototype properties are empty'); - ok(_.isEmpty(null), 'null is empty'); - ok(_.isEmpty(), 'undefined is empty'); - ok(_.isEmpty(''), 'the empty string is empty'); - ok(!_.isEmpty('moe'), 'but other strings are not'); + assert.equal(_.isEqual([0], [-0]), false); + assert.equal(_.isEqual({a: 0}, {a: -0}), false); + assert.equal(_.isEqual([NaN], [NaN]), true); + assert.equal(_.isEqual({a: NaN}, {a: NaN}), true); + }); + + test('isEmpty', function(assert) { + assert.ok(!_([1]).isEmpty(), '[1] is not empty'); + assert.ok(_.isEmpty([]), '[] is empty'); + assert.ok(!_.isEmpty({one: 1}), '{one: 1} is not empty'); + assert.ok(_.isEmpty({}), '{} is empty'); + assert.ok(_.isEmpty(new RegExp('')), 'objects with prototype properties are empty'); + assert.ok(_.isEmpty(null), 'null is empty'); + assert.ok(_.isEmpty(), 'undefined is empty'); + assert.ok(_.isEmpty(''), 'the empty string is empty'); + assert.ok(!_.isEmpty('moe'), 'but other strings are not'); var obj = {one: 1}; delete obj.one; - ok(_.isEmpty(obj), 'deleting all the keys from an object empties it'); + assert.ok(_.isEmpty(obj), 'deleting all the keys from an object empties it'); var args = function(){ return arguments; }; - ok(_.isEmpty(args()), 'empty arguments object is empty'); - ok(!_.isEmpty(args('')), 'non-empty arguments object is not empty'); + assert.ok(_.isEmpty(args()), 'empty arguments object is empty'); + assert.ok(!_.isEmpty(args('')), 'non-empty arguments object is not empty'); // covers collecting non-enumerable properties in IE < 9 var nonEnumProp = {toString: 5}; - ok(!_.isEmpty(nonEnumProp), 'non-enumerable property is not empty'); + assert.ok(!_.isEmpty(nonEnumProp), 'non-enumerable property is not empty'); }); if (typeof document === 'object') { - test('isElement', function() { - ok(!_.isElement('div'), 'strings are not dom elements'); - ok(_.isElement(testElement), 'an element is a DOM element'); + test('isElement', function(assert) { + assert.ok(!_.isElement('div'), 'strings are not dom elements'); + assert.ok(_.isElement(testElement), 'an element is a DOM element'); }); } - test('isArguments', function() { + test('isArguments', function(assert) { var args = (function(){ return arguments; }(1, 2, 3)); - ok(!_.isArguments('string'), 'a string is not an arguments object'); - ok(!_.isArguments(_.isArguments), 'a function is not an arguments object'); - ok(_.isArguments(args), 'but the arguments object is an arguments object'); - ok(!_.isArguments(_.toArray(args)), 'but not when it\'s converted into an array'); - ok(!_.isArguments([1, 2, 3]), 'and not vanilla arrays.'); + assert.ok(!_.isArguments('string'), 'a string is not an arguments object'); + assert.ok(!_.isArguments(_.isArguments), 'a function is not an arguments object'); + assert.ok(_.isArguments(args), 'but the arguments object is an arguments object'); + assert.ok(!_.isArguments(_.toArray(args)), 'but not when it\'s converted into an array'); + assert.ok(!_.isArguments([1, 2, 3]), 'and not vanilla arrays.'); }); - test('isObject', function() { - ok(_.isObject(arguments), 'the arguments object is object'); - ok(_.isObject([1, 2, 3]), 'and arrays'); + test('isObject', function(assert) { + assert.ok(_.isObject(arguments), 'the arguments object is object'); + assert.ok(_.isObject([1, 2, 3]), 'and arrays'); if (testElement) { - ok(_.isObject(testElement), 'and DOM element'); + assert.ok(_.isObject(testElement), 'and DOM element'); } - ok(_.isObject(function() {}), 'and functions'); - ok(!_.isObject(null), 'but not null'); - ok(!_.isObject(void 0), 'and not undefined'); - ok(!_.isObject('string'), 'and not string'); - ok(!_.isObject(12), 'and not number'); - ok(!_.isObject(true), 'and not boolean'); - ok(_.isObject(new String('string')), 'but new String()'); + assert.ok(_.isObject(function() {}), 'and functions'); + assert.ok(!_.isObject(null), 'but not null'); + assert.ok(!_.isObject(void 0), 'and not undefined'); + assert.ok(!_.isObject('string'), 'and not string'); + assert.ok(!_.isObject(12), 'and not number'); + assert.ok(!_.isObject(true), 'and not boolean'); + assert.ok(_.isObject(new String('string')), 'but new String()'); }); - test('isArray', function() { - ok(!_.isArray(void 0), 'undefined vars are not arrays'); - ok(!_.isArray(arguments), 'the arguments object is not an array'); - ok(_.isArray([1, 2, 3]), 'but arrays are'); + test('isArray', function(assert) { + assert.ok(!_.isArray(void 0), 'undefined vars are not arrays'); + assert.ok(!_.isArray(arguments), 'the arguments object is not an array'); + assert.ok(_.isArray([1, 2, 3]), 'but arrays are'); }); - test('isString', function() { + test('isString', function(assert) { var obj = new String('I am a string object'); if (testElement) { - ok(!_.isString(testElement), 'an element is not a string'); + assert.ok(!_.isString(testElement), 'an element is not a string'); } - ok(_.isString([1, 2, 3].join(', ')), 'but strings are'); - strictEqual(_.isString('I am a string literal'), true, 'string literals are'); - ok(_.isString(obj), 'so are String objects'); - strictEqual(_.isString(1), false); - }); - - test('isNumber', function() { - ok(!_.isNumber('string'), 'a string is not a number'); - ok(!_.isNumber(arguments), 'the arguments object is not a number'); - ok(!_.isNumber(void 0), 'undefined is not a number'); - ok(_.isNumber(3 * 4 - 7 / 10), 'but numbers are'); - ok(_.isNumber(NaN), 'NaN *is* a number'); - ok(_.isNumber(Infinity), 'Infinity is a number'); - ok(!_.isNumber('1'), 'numeric strings are not numbers'); - }); - - test('isBoolean', function() { - ok(!_.isBoolean(2), 'a number is not a boolean'); - ok(!_.isBoolean('string'), 'a string is not a boolean'); - ok(!_.isBoolean('false'), 'the string "false" is not a boolean'); - ok(!_.isBoolean('true'), 'the string "true" is not a boolean'); - ok(!_.isBoolean(arguments), 'the arguments object is not a boolean'); - ok(!_.isBoolean(void 0), 'undefined is not a boolean'); - ok(!_.isBoolean(NaN), 'NaN is not a boolean'); - ok(!_.isBoolean(null), 'null is not a boolean'); - ok(_.isBoolean(true), 'but true is'); - ok(_.isBoolean(false), 'and so is false'); - }); - - test('isFunction', function() { - ok(!_.isFunction(void 0), 'undefined vars are not functions'); - ok(!_.isFunction([1, 2, 3]), 'arrays are not functions'); - ok(!_.isFunction('moe'), 'strings are not functions'); - ok(_.isFunction(_.isFunction), 'but functions are'); - ok(_.isFunction(function(){}), 'even anonymous ones'); + assert.ok(_.isString([1, 2, 3].join(', ')), 'but strings are'); + assert.strictEqual(_.isString('I am a string literal'), true, 'string literals are'); + assert.ok(_.isString(obj), 'so are String objects'); + assert.strictEqual(_.isString(1), false); + }); + + test('isNumber', function(assert) { + assert.ok(!_.isNumber('string'), 'a string is not a number'); + assert.ok(!_.isNumber(arguments), 'the arguments object is not a number'); + assert.ok(!_.isNumber(void 0), 'undefined is not a number'); + assert.ok(_.isNumber(3 * 4 - 7 / 10), 'but numbers are'); + assert.ok(_.isNumber(NaN), 'NaN *is* a number'); + assert.ok(_.isNumber(Infinity), 'Infinity is a number'); + assert.ok(!_.isNumber('1'), 'numeric strings are not numbers'); + }); + + test('isBoolean', function(assert) { + assert.ok(!_.isBoolean(2), 'a number is not a boolean'); + assert.ok(!_.isBoolean('string'), 'a string is not a boolean'); + assert.ok(!_.isBoolean('false'), 'the string "false" is not a boolean'); + assert.ok(!_.isBoolean('true'), 'the string "true" is not a boolean'); + assert.ok(!_.isBoolean(arguments), 'the arguments object is not a boolean'); + assert.ok(!_.isBoolean(void 0), 'undefined is not a boolean'); + assert.ok(!_.isBoolean(NaN), 'NaN is not a boolean'); + assert.ok(!_.isBoolean(null), 'null is not a boolean'); + assert.ok(_.isBoolean(true), 'but true is'); + assert.ok(_.isBoolean(false), 'and so is false'); + }); + + test('isFunction', function(assert) { + assert.ok(!_.isFunction(void 0), 'undefined vars are not functions'); + assert.ok(!_.isFunction([1, 2, 3]), 'arrays are not functions'); + assert.ok(!_.isFunction('moe'), 'strings are not functions'); + assert.ok(_.isFunction(_.isFunction), 'but functions are'); + assert.ok(_.isFunction(function(){}), 'even anonymous ones'); if (testElement) { - ok(!_.isFunction(testElement), 'elements are not functions'); + assert.ok(!_.isFunction(testElement), 'elements are not functions'); } var nodelist = typeof document != 'undefined' && document.childNodes; if (nodelist) { - ok(!_.isFunction(nodelist)); + assert.ok(!_.isFunction(nodelist)); } }); if (typeof Int8Array !== 'undefined') { - test('#1929 Typed Array constructors are functions', function() { + test('#1929 Typed Array constructors are functions', function(assert) { _.chain(['Float32Array', 'Float64Array', 'Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array']) .map(_.propertyOf(typeof GLOBAL != 'undefined' ? GLOBAL : window)) .compact() .each(function(TypedArray) { // PhantomJS reports `typeof UInt8Array == 'object'` and doesn't report toString TypeArray // as a function - strictEqual(_.isFunction(TypedArray), Object.prototype.toString.call(TypedArray) === '[object Function]'); + assert.strictEqual(_.isFunction(TypedArray), Object.prototype.toString.call(TypedArray) === '[object Function]'); }); }); } - test('isDate', function() { - ok(!_.isDate(100), 'numbers are not dates'); - ok(!_.isDate({}), 'objects are not dates'); - ok(_.isDate(new Date()), 'but dates are'); + test('isDate', function(assert) { + assert.ok(!_.isDate(100), 'numbers are not dates'); + assert.ok(!_.isDate({}), 'objects are not dates'); + assert.ok(_.isDate(new Date()), 'but dates are'); }); - test('isRegExp', function() { - ok(!_.isRegExp(_.identity), 'functions are not RegExps'); - ok(_.isRegExp(/identity/), 'but RegExps are'); + test('isRegExp', function(assert) { + assert.ok(!_.isRegExp(_.identity), 'functions are not RegExps'); + assert.ok(_.isRegExp(/identity/), 'but RegExps are'); }); - test('isFinite', function() { - ok(!_.isFinite(void 0), 'undefined is not finite'); - ok(!_.isFinite(null), 'null is not finite'); - ok(!_.isFinite(NaN), 'NaN is not finite'); - ok(!_.isFinite(Infinity), 'Infinity is not finite'); - ok(!_.isFinite(-Infinity), '-Infinity is not finite'); - ok(_.isFinite('12'), 'Numeric strings are numbers'); - ok(!_.isFinite('1a'), 'Non numeric strings are not numbers'); - ok(!_.isFinite(''), 'Empty strings are not numbers'); + test('isFinite', function(assert) { + assert.ok(!_.isFinite(void 0), 'undefined is not finite'); + assert.ok(!_.isFinite(null), 'null is not finite'); + assert.ok(!_.isFinite(NaN), 'NaN is not finite'); + assert.ok(!_.isFinite(Infinity), 'Infinity is not finite'); + assert.ok(!_.isFinite(-Infinity), '-Infinity is not finite'); + assert.ok(_.isFinite('12'), 'Numeric strings are numbers'); + assert.ok(!_.isFinite('1a'), 'Non numeric strings are not numbers'); + assert.ok(!_.isFinite(''), 'Empty strings are not numbers'); var obj = new Number(5); - ok(_.isFinite(obj), 'Number instances can be finite'); - ok(_.isFinite(0), '0 is finite'); - ok(_.isFinite(123), 'Ints are finite'); - ok(_.isFinite(-12.44), 'Floats are finite'); - }); - - test('isNaN', function() { - ok(!_.isNaN(void 0), 'undefined is not NaN'); - ok(!_.isNaN(null), 'null is not NaN'); - ok(!_.isNaN(0), '0 is not NaN'); - ok(!_.isNaN(new Number(0)), 'wrapped 0 is not NaN'); - ok(_.isNaN(NaN), 'but NaN is'); - ok(_.isNaN(new Number(NaN)), 'wrapped NaN is still NaN'); - }); - - test('isNull', function() { - ok(!_.isNull(void 0), 'undefined is not null'); - ok(!_.isNull(NaN), 'NaN is not null'); - ok(_.isNull(null), 'but null is'); - }); - - test('isUndefined', function() { - ok(!_.isUndefined(1), 'numbers are defined'); - ok(!_.isUndefined(null), 'null is defined'); - ok(!_.isUndefined(false), 'false is defined'); - ok(!_.isUndefined(NaN), 'NaN is defined'); - ok(_.isUndefined(), 'nothing is undefined'); - ok(_.isUndefined(void 0), 'undefined is undefined'); - }); - - test('isError', function() { - ok(!_.isError(1), 'numbers are not Errors'); - ok(!_.isError(null), 'null is not an Error'); - ok(!_.isError(Error), 'functions are not Errors'); - ok(_.isError(new Error()), 'Errors are Errors'); - ok(_.isError(new EvalError()), 'EvalErrors are Errors'); - ok(_.isError(new RangeError()), 'RangeErrors are Errors'); - ok(_.isError(new ReferenceError()), 'ReferenceErrors are Errors'); - ok(_.isError(new SyntaxError()), 'SyntaxErrors are Errors'); - ok(_.isError(new TypeError()), 'TypeErrors are Errors'); - ok(_.isError(new URIError()), 'URIErrors are Errors'); - }); - - test('tap', function() { + assert.ok(_.isFinite(obj), 'Number instances can be finite'); + assert.ok(_.isFinite(0), '0 is finite'); + assert.ok(_.isFinite(123), 'Ints are finite'); + assert.ok(_.isFinite(-12.44), 'Floats are finite'); + }); + + test('isNaN', function(assert) { + assert.ok(!_.isNaN(void 0), 'undefined is not NaN'); + assert.ok(!_.isNaN(null), 'null is not NaN'); + assert.ok(!_.isNaN(0), '0 is not NaN'); + assert.ok(!_.isNaN(new Number(0)), 'wrapped 0 is not NaN'); + assert.ok(_.isNaN(NaN), 'but NaN is'); + assert.ok(_.isNaN(new Number(NaN)), 'wrapped NaN is still NaN'); + }); + + test('isNull', function(assert) { + assert.ok(!_.isNull(void 0), 'undefined is not null'); + assert.ok(!_.isNull(NaN), 'NaN is not null'); + assert.ok(_.isNull(null), 'but null is'); + }); + + test('isUndefined', function(assert) { + assert.ok(!_.isUndefined(1), 'numbers are defined'); + assert.ok(!_.isUndefined(null), 'null is defined'); + assert.ok(!_.isUndefined(false), 'false is defined'); + assert.ok(!_.isUndefined(NaN), 'NaN is defined'); + assert.ok(_.isUndefined(), 'nothing is undefined'); + assert.ok(_.isUndefined(void 0), 'undefined is undefined'); + }); + + test('isError', function(assert) { + assert.ok(!_.isError(1), 'numbers are not Errors'); + assert.ok(!_.isError(null), 'null is not an Error'); + assert.ok(!_.isError(Error), 'functions are not Errors'); + assert.ok(_.isError(new Error()), 'Errors are Errors'); + assert.ok(_.isError(new EvalError()), 'EvalErrors are Errors'); + assert.ok(_.isError(new RangeError()), 'RangeErrors are Errors'); + assert.ok(_.isError(new ReferenceError()), 'ReferenceErrors are Errors'); + assert.ok(_.isError(new SyntaxError()), 'SyntaxErrors are Errors'); + assert.ok(_.isError(new TypeError()), 'TypeErrors are Errors'); + assert.ok(_.isError(new URIError()), 'URIErrors are Errors'); + }); + + test('tap', function(assert) { var intercepted = null; var interceptor = function(obj) { intercepted = obj; }; var returned = _.tap(1, interceptor); - equal(intercepted, 1, 'passes tapped object to interceptor'); - equal(returned, 1, 'returns tapped object'); + assert.equal(intercepted, 1, 'passes tapped object to interceptor'); + assert.equal(returned, 1, 'returns tapped object'); returned = _([1, 2, 3]).chain(). map(function(n){ return n * 2; }). max(). tap(interceptor). value(); - equal(returned, 6, 'can use tapped objects in a chain'); - equal(intercepted, returned, 'can use tapped objects in a chain'); + assert.equal(returned, 6, 'can use tapped objects in a chain'); + assert.equal(intercepted, returned, 'can use tapped objects in a chain'); }); - test('has', function() { + test('has', function(assert) { var obj = {foo: 'bar', func: function(){}}; - ok(_.has(obj, 'foo'), 'has() checks that the object has a property.'); - ok(!_.has(obj, 'baz'), "has() returns false if the object doesn't have the property."); - ok(_.has(obj, 'func'), 'has() works for functions too.'); + assert.ok(_.has(obj, 'foo'), 'has() checks that the object has a property.'); + assert.ok(!_.has(obj, 'baz'), "has() returns false if the object doesn't have the property."); + assert.ok(_.has(obj, 'func'), 'has() works for functions too.'); obj.hasOwnProperty = null; - ok(_.has(obj, 'foo'), 'has() works even when the hasOwnProperty method is deleted.'); + assert.ok(_.has(obj, 'foo'), 'has() works even when the hasOwnProperty method is deleted.'); var child = {}; child.prototype = obj; - ok(!_.has(child, 'foo'), 'has() does not check the prototype chain for a property.'); - strictEqual(_.has(null, 'foo'), false, 'has() returns false for null'); - strictEqual(_.has(void 0, 'foo'), false, 'has() returns false for undefined'); + assert.ok(!_.has(child, 'foo'), 'has() does not check the prototype chain for a property.'); + assert.strictEqual(_.has(null, 'foo'), false, 'has() returns false for null'); + assert.strictEqual(_.has(void 0, 'foo'), false, 'has() returns false for undefined'); }); - test('isMatch', function() { + test('isMatch', function(assert) { var moe = {name: 'Moe Howard', hair: true}; var curly = {name: 'Curly Howard', hair: false}; - equal(_.isMatch(moe, {hair: true}), true, 'Returns a boolean'); - equal(_.isMatch(curly, {hair: true}), false, 'Returns a boolean'); + assert.equal(_.isMatch(moe, {hair: true}), true, 'Returns a boolean'); + assert.equal(_.isMatch(curly, {hair: true}), false, 'Returns a boolean'); - equal(_.isMatch(5, {__x__: void 0}), false, 'can match undefined props on primitives'); - equal(_.isMatch({__x__: void 0}, {__x__: void 0}), true, 'can match undefined props'); + assert.equal(_.isMatch(5, {__x__: void 0}), false, 'can match undefined props on primitives'); + assert.equal(_.isMatch({__x__: void 0}, {__x__: void 0}), true, 'can match undefined props'); - equal(_.isMatch(null, {}), true, 'Empty spec called with null object returns true'); - equal(_.isMatch(null, {a: 1}), false, 'Non-empty spec called with null object returns false'); + assert.equal(_.isMatch(null, {}), true, 'Empty spec called with null object returns true'); + assert.equal(_.isMatch(null, {a: 1}), false, 'Non-empty spec called with null object returns false'); - _.each([null, void 0], function(item) { strictEqual(_.isMatch(item, null), true, 'null matches null'); }); - _.each([null, void 0], function(item) { strictEqual(_.isMatch(item, null), true, 'null matches {}'); }); - strictEqual(_.isMatch({b: 1}, {a: void 0}), false, 'handles undefined values (1683)'); + _.each([null, void 0], function(item) { assert.strictEqual(_.isMatch(item, null), true, 'null matches null'); }); + _.each([null, void 0], function(item) { assert.strictEqual(_.isMatch(item, null), true, 'null matches {}'); }); + assert.strictEqual(_.isMatch({b: 1}, {a: void 0}), false, 'handles undefined values (1683)'); _.each([true, 5, NaN, null, void 0], function(item) { - strictEqual(_.isMatch({a: 1}, item), true, 'treats primitives as empty'); + assert.strictEqual(_.isMatch({a: 1}, item), true, 'treats primitives as empty'); }); function Prototest() {} Prototest.prototype.x = 1; var specObj = new Prototest; - equal(_.isMatch({x: 2}, specObj), true, 'spec is restricted to own properties'); + assert.equal(_.isMatch({x: 2}, specObj), true, 'spec is restricted to own properties'); specObj.y = 5; - equal(_.isMatch({x: 1, y: 5}, specObj), true); - equal(_.isMatch({x: 1, y: 4}, specObj), false); + assert.equal(_.isMatch({x: 1, y: 5}, specObj), true); + assert.equal(_.isMatch({x: 1, y: 4}, specObj), false); - ok(_.isMatch(specObj, {x: 1, y: 5}), 'inherited and own properties are checked on the test object'); + assert.ok(_.isMatch(specObj, {x: 1, y: 5}), 'inherited and own properties are checked on the test object'); Prototest.x = 5; - ok(_.isMatch({x: 5, y: 1}, Prototest), 'spec can be a function'); + assert.ok(_.isMatch({x: 5, y: 1}, Prototest), 'spec can be a function'); //null edge cases var oCon = {constructor: Object}; - deepEqual(_.map([null, void 0, 5, {}], _.partial(_.isMatch, _, oCon)), [false, false, false, true], 'doesnt falsey match constructor on undefined/null'); + assert.deepEqual(_.map([null, void 0, 5, {}], _.partial(_.isMatch, _, oCon)), [false, false, false, true], 'doesnt falsey match constructor on undefined/null'); }); - test('matcher', function() { + test('matcher', function(assert) { var moe = {name: 'Moe Howard', hair: true}; var curly = {name: 'Curly Howard', hair: false}; var stooges = [moe, curly]; - equal(_.matcher({hair: true})(moe), true, 'Returns a boolean'); - equal(_.matcher({hair: true})(curly), false, 'Returns a boolean'); + assert.equal(_.matcher({hair: true})(moe), true, 'Returns a boolean'); + assert.equal(_.matcher({hair: true})(curly), false, 'Returns a boolean'); - equal(_.matcher({__x__: void 0})(5), false, 'can match undefined props on primitives'); - equal(_.matcher({__x__: void 0})({__x__: void 0}), true, 'can match undefined props'); + assert.equal(_.matcher({__x__: void 0})(5), false, 'can match undefined props on primitives'); + assert.equal(_.matcher({__x__: void 0})({__x__: void 0}), true, 'can match undefined props'); - equal(_.matcher({})(null), true, 'Empty spec called with null object returns true'); - equal(_.matcher({a: 1})(null), false, 'Non-empty spec called with null object returns false'); + assert.equal(_.matcher({})(null), true, 'Empty spec called with null object returns true'); + assert.equal(_.matcher({a: 1})(null), false, 'Non-empty spec called with null object returns false'); - ok(_.find(stooges, _.matcher({hair: false})) === curly, 'returns a predicate that can be used by finding functions.'); - ok(_.find(stooges, _.matcher(moe)) === moe, 'can be used to locate an object exists in a collection.'); - deepEqual(_.where([null, void 0], {a: 1}), [], 'Do not throw on null values.'); + assert.ok(_.find(stooges, _.matcher({hair: false})) === curly, 'returns a predicate that can be used by finding functions.'); + assert.ok(_.find(stooges, _.matcher(moe)) === moe, 'can be used to locate an object exists in a collection.'); + assert.deepEqual(_.where([null, void 0], {a: 1}), [], 'Do not throw on null values.'); - deepEqual(_.where([null, void 0], null), [null, void 0], 'null matches null'); - deepEqual(_.where([null, void 0], {}), [null, void 0], 'null matches {}'); - deepEqual(_.where([{b: 1}], {a: void 0}), [], 'handles undefined values (1683)'); + assert.deepEqual(_.where([null, void 0], null), [null, void 0], 'null matches null'); + assert.deepEqual(_.where([null, void 0], {}), [null, void 0], 'null matches {}'); + assert.deepEqual(_.where([{b: 1}], {a: void 0}), [], 'handles undefined values (1683)'); _.each([true, 5, NaN, null, void 0], function(item) { - deepEqual(_.where([{a: 1}], item), [{a: 1}], 'treats primitives as empty'); + assert.deepEqual(_.where([{a: 1}], item), [{a: 1}], 'treats primitives as empty'); }); function Prototest() {} Prototest.prototype.x = 1; var specObj = new Prototest; var protospec = _.matcher(specObj); - equal(protospec({x: 2}), true, 'spec is restricted to own properties'); + assert.equal(protospec({x: 2}), true, 'spec is restricted to own properties'); specObj.y = 5; protospec = _.matcher(specObj); - equal(protospec({x: 1, y: 5}), true); - equal(protospec({x: 1, y: 4}), false); + assert.equal(protospec({x: 1, y: 5}), true); + assert.equal(protospec({x: 1, y: 4}), false); - ok(_.matcher({x: 1, y: 5})(specObj), 'inherited and own properties are checked on the test object'); + assert.ok(_.matcher({x: 1, y: 5})(specObj), 'inherited and own properties are checked on the test object'); Prototest.x = 5; - ok(_.matcher(Prototest)({x: 5, y: 1}), 'spec can be a function'); + assert.ok(_.matcher(Prototest)({x: 5, y: 1}), 'spec can be a function'); // #1729 var o = {b: 1}; var m = _.matcher(o); - equal(m({b: 1}), true); + assert.equal(m({b: 1}), true); o.b = 2; o.a = 1; - equal(m({b: 1}), true, 'changing spec object doesnt change matches result'); + assert.equal(m({b: 1}), true, 'changing spec object doesnt change matches result'); //null edge cases var oCon = _.matcher({constructor: Object}); - deepEqual(_.map([null, void 0, 5, {}], oCon), [false, false, false, true], 'doesnt falsey match constructor on undefined/null'); + assert.deepEqual(_.map([null, void 0, 5, {}], oCon), [false, false, false, true], 'doesnt falsey match constructor on undefined/null'); }); - test('findKey', function() { + test('findKey', function(assert) { var objects = { a: {a: 0, b: 0}, b: {a: 1, b: 1}, c: {a: 2, b: 2} }; - equal(_.findKey(objects, function(obj) { + assert.equal(_.findKey(objects, function(obj) { return obj.a === 0; }), 'a'); - equal(_.findKey(objects, function(obj) { + assert.equal(_.findKey(objects, function(obj) { return obj.b * obj.a === 4; }), 'c'); - equal(_.findKey(objects, 'a'), 'b', 'Uses lookupIterator'); + assert.equal(_.findKey(objects, 'a'), 'b', 'Uses lookupIterator'); - equal(_.findKey(objects, function(obj) { + assert.equal(_.findKey(objects, function(obj) { return obj.b * obj.a === 5; }), void 0); - strictEqual(_.findKey([1, 2, 3, 4, 5, 6], function(obj) { + assert.strictEqual(_.findKey([1, 2, 3, 4, 5, 6], function(obj) { return obj === 3; }), '2', 'Keys are strings'); - strictEqual(_.findKey(objects, function(a) { + assert.strictEqual(_.findKey(objects, function(a) { return a.foo === null; }), void 0); _.findKey({a: {a: 1}}, function(a, key, obj) { - equal(key, 'a'); - deepEqual(obj, {a: {a: 1}}); - strictEqual(this, objects, 'called with context'); + assert.equal(key, 'a'); + assert.deepEqual(obj, {a: {a: 1}}); + assert.strictEqual(this, objects, 'called with context'); }, objects); var array = [1, 2, 3, 4]; array.match = 55; - strictEqual(_.findKey(array, function(x) { return x === 55; }), 'match', 'matches array-likes keys'); + assert.strictEqual(_.findKey(array, function(x) { return x === 55; }), 'match', 'matches array-likes keys'); }); - test('mapObject', function() { + test('mapObject', function(assert) { var obj = {a: 1, b: 2}; var objects = { a: {a: 0, b: 0}, @@ -928,49 +928,49 @@ c: {a: 2, b: 2} }; - deepEqual(_.mapObject(obj, function(val) { + assert.deepEqual(_.mapObject(obj, function(val) { return val * 2; }), {a: 2, b: 4}, 'simple objects'); - deepEqual(_.mapObject(objects, function(val) { + assert.deepEqual(_.mapObject(objects, function(val) { return _.reduce(val, function(memo, v){ return memo + v; }, 0); }), {a: 0, b: 2, c: 4}, 'nested objects'); - deepEqual(_.mapObject(obj, function(val, key, o) { + assert.deepEqual(_.mapObject(obj, function(val, key, o) { return o[key] * 2; }), {a: 2, b: 4}, 'correct keys'); - deepEqual(_.mapObject([1, 2], function(val) { + assert.deepEqual(_.mapObject([1, 2], function(val) { return val * 2; }), {0: 2, 1: 4}, 'check behavior for arrays'); - deepEqual(_.mapObject(obj, function(val) { + assert.deepEqual(_.mapObject(obj, function(val) { return val * this.multiplier; }, {multiplier: 3}), {a: 3, b: 6}, 'keep context'); - deepEqual(_.mapObject({a: 1}, function() { + assert.deepEqual(_.mapObject({a: 1}, function() { return this.length; }, [1, 2]), {a: 2}, 'called with context'); var ids = _.mapObject({length: 2, 0: {id: '1'}, 1: {id: '2'}}, function(n){ return n.id; }); - deepEqual(ids, {length: void 0, 0: '1', 1: '2'}, 'Check with array-like objects'); + assert.deepEqual(ids, {length: void 0, 0: '1', 1: '2'}, 'Check with array-like objects'); // Passing a property name like _.pluck. var people = {a: {name: 'moe', age: 30}, b: {name: 'curly', age: 50}}; - deepEqual(_.mapObject(people, 'name'), {a: 'moe', b: 'curly'}, 'predicate string map to object properties'); + assert.deepEqual(_.mapObject(people, 'name'), {a: 'moe', b: 'curly'}, 'predicate string map to object properties'); _.each([null, void 0, 1, 'abc', [], {}, void 0], function(val){ - deepEqual(_.mapObject(val, _.identity), {}, 'mapValue identity'); + assert.deepEqual(_.mapObject(val, _.identity), {}, 'mapValue identity'); }); var Proto = function(){ this.a = 1; }; Proto.prototype.b = 1; var protoObj = new Proto(); - deepEqual(_.mapObject(protoObj, _.identity), {a: 1}, 'ignore inherited values from prototypes'); + assert.deepEqual(_.mapObject(protoObj, _.identity), {a: 1}, 'ignore inherited values from prototypes'); }); }()); diff --git a/test/utility.js b/test/utility.js index 61c0347a4..bb58ae8a3 100644 --- a/test/utility.js +++ b/test/utility.js @@ -15,18 +15,18 @@ }); if (typeof this == 'object') { - test('noConflict', function() { + test('noConflict', function(assert) { var underscore = _.noConflict(); - equal(underscore.identity(1), 1); + assert.equal(underscore.identity(1), 1); if (typeof require != 'function') { - equal(this._, void 0, 'global underscore is removed'); + assert.equal(this._, void 0, 'global underscore is removed'); this._ = underscore; } }); } if (typeof require == 'function') { - asyncTest('noConflict (node vm)', 2, function() { + asyncTest('noConflict (node vm)', 2, function(assert) { var fs = require('fs'); var vm = require('vm'); var filename = __dirname + '/../underscore.js'; @@ -37,179 +37,179 @@ ); var context = {_: 'oldvalue'}; sandbox.runInNewContext(context); - equal(context._, 'oldvalue'); - equal(context.underscore.VERSION, _.VERSION); + assert.equal(context._, 'oldvalue'); + assert.equal(context.underscore.VERSION, _.VERSION); start(); }); }); } - test('#750 - Return _ instance.', 2, function() { + test('#750 - Return _ instance.', 2, function(assert) { var instance = _([]); - ok(_(instance) === instance); - ok(new _(instance) === instance); + assert.ok(_(instance) === instance); + assert.ok(new _(instance) === instance); }); - test('identity', function() { + test('identity', function(assert) { var stooge = {name: 'moe'}; - equal(_.identity(stooge), stooge, 'stooge is the same as his identity'); + assert.equal(_.identity(stooge), stooge, 'stooge is the same as his identity'); }); - test('constant', function() { + test('constant', function(assert) { var stooge = {name: 'moe'}; - equal(_.constant(stooge)(), stooge, 'should create a function that returns stooge'); + assert.equal(_.constant(stooge)(), stooge, 'should create a function that returns stooge'); }); - test('noop', function() { - strictEqual(_.noop('curly', 'larry', 'moe'), void 0, 'should always return undefined'); + test('noop', function(assert) { + assert.strictEqual(_.noop('curly', 'larry', 'moe'), void 0, 'should always return undefined'); }); - test('property', function() { + test('property', function(assert) { var stooge = {name: 'moe'}; - equal(_.property('name')(stooge), 'moe', 'should return the property with the given name'); - equal(_.property('name')(null), void 0, 'should return undefined for null values'); - equal(_.property('name')(void 0), void 0, 'should return undefined for undefined values'); + assert.equal(_.property('name')(stooge), 'moe', 'should return the property with the given name'); + assert.equal(_.property('name')(null), void 0, 'should return undefined for null values'); + assert.equal(_.property('name')(void 0), void 0, 'should return undefined for undefined values'); }); - test('propertyOf', function() { + test('propertyOf', function(assert) { var stoogeRanks = _.propertyOf({curly: 2, moe: 1, larry: 3}); - equal(stoogeRanks('curly'), 2, 'should return the property with the given name'); - equal(stoogeRanks(null), void 0, 'should return undefined for null values'); - equal(stoogeRanks(void 0), void 0, 'should return undefined for undefined values'); + assert.equal(stoogeRanks('curly'), 2, 'should return the property with the given name'); + assert.equal(stoogeRanks(null), void 0, 'should return undefined for null values'); + assert.equal(stoogeRanks(void 0), void 0, 'should return undefined for undefined values'); function MoreStooges() { this.shemp = 87; } MoreStooges.prototype = {curly: 2, moe: 1, larry: 3}; var moreStoogeRanks = _.propertyOf(new MoreStooges()); - equal(moreStoogeRanks('curly'), 2, 'should return properties from further up the prototype chain'); + assert.equal(moreStoogeRanks('curly'), 2, 'should return properties from further up the prototype chain'); var nullPropertyOf = _.propertyOf(null); - equal(nullPropertyOf('curly'), void 0, 'should return undefined when obj is null'); + assert.equal(nullPropertyOf('curly'), void 0, 'should return undefined when obj is null'); var undefPropertyOf = _.propertyOf(void 0); - equal(undefPropertyOf('curly'), void 0, 'should return undefined when obj is undefined'); + assert.equal(undefPropertyOf('curly'), void 0, 'should return undefined when obj is undefined'); }); - test('random', function() { + test('random', function(assert) { var array = _.range(1000); var min = Math.pow(2, 31); var max = Math.pow(2, 62); - ok(_.every(array, function() { + assert.ok(_.every(array, function() { return _.random(min, max) >= min; }), 'should produce a random number greater than or equal to the minimum number'); - ok(_.some(array, function() { + assert.ok(_.some(array, function() { return _.random(Number.MAX_VALUE) > 0; }), 'should produce a random number when passed `Number.MAX_VALUE`'); }); - test('now', function() { + test('now', function(assert) { var diff = _.now() - new Date().getTime(); - ok(diff <= 0 && diff > -5, 'Produces the correct time in milliseconds');//within 5ms + assert.ok(diff <= 0 && diff > -5, 'Produces the correct time in milliseconds');//within 5ms }); - test('uniqueId', function() { + test('uniqueId', function(assert) { var ids = [], i = 0; while (i++ < 100) ids.push(_.uniqueId()); - equal(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids'); + assert.equal(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids'); }); - test('times', function() { + test('times', function(assert) { var vals = []; _.times(3, function(i) { vals.push(i); }); - deepEqual(vals, [0, 1, 2], 'is 0 indexed'); + assert.deepEqual(vals, [0, 1, 2], 'is 0 indexed'); // vals = []; _(3).times(function(i) { vals.push(i); }); - deepEqual(vals, [0, 1, 2], 'works as a wrapper'); + assert.deepEqual(vals, [0, 1, 2], 'works as a wrapper'); // collects return values - deepEqual([0, 1, 2], _.times(3, function(i) { return i; }), 'collects return values'); + assert.deepEqual([0, 1, 2], _.times(3, function(i) { return i; }), 'collects return values'); - deepEqual(_.times(0, _.identity), []); - deepEqual(_.times(-1, _.identity), []); - deepEqual(_.times(parseFloat('-Infinity'), _.identity), []); + assert.deepEqual(_.times(0, _.identity), []); + assert.deepEqual(_.times(-1, _.identity), []); + assert.deepEqual(_.times(parseFloat('-Infinity'), _.identity), []); }); - test('mixin', function() { + test('mixin', function(assert) { _.mixin({ myReverse: function(string) { return string.split('').reverse().join(''); } }); - equal(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _'); - equal(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper'); + assert.equal(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _'); + assert.equal(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper'); }); - test('_.escape', function() { - equal(_.escape(null), ''); + test('_.escape', function(assert) { + assert.equal(_.escape(null), ''); }); - test('_.unescape', function() { + test('_.unescape', function(assert) { var string = 'Curly & Moe'; - equal(_.unescape(null), ''); - equal(_.unescape(_.escape(string)), string); - equal(_.unescape(string), string, 'don\'t unescape unnecessarily'); + assert.equal(_.unescape(null), ''); + assert.equal(_.unescape(_.escape(string)), string); + assert.equal(_.unescape(string), string, 'don\'t unescape unnecessarily'); }); // Don't care what they escape them to just that they're escaped and can be unescaped - test('_.escape & unescape', function() { + test('_.escape & unescape', function(assert) { // test & (&) seperately obviously var escapeCharacters = ['<', '>', '"', '\'', '`']; _.each(escapeCharacters, function(escapeChar) { var s = 'a ' + escapeChar + ' string escaped'; var e = _.escape(s); - notEqual(s, e, escapeChar + ' is escaped'); - equal(s, _.unescape(e), escapeChar + ' can be unescaped'); + assert.notEqual(s, e, escapeChar + ' is escaped'); + assert.equal(s, _.unescape(e), escapeChar + ' can be unescaped'); s = 'a ' + escapeChar + escapeChar + escapeChar + 'some more string' + escapeChar; e = _.escape(s); - equal(e.indexOf(escapeChar), -1, 'can escape multiple occurances of ' + escapeChar); - equal(_.unescape(e), s, 'multiple occurrences of ' + escapeChar + ' can be unescaped'); + assert.equal(e.indexOf(escapeChar), -1, 'can escape multiple occurances of ' + escapeChar); + assert.equal(_.unescape(e), s, 'multiple occurrences of ' + escapeChar + ' can be unescaped'); }); // handles multiple escape characters at once var joiner = ' other stuff '; var allEscaped = escapeCharacters.join(joiner); allEscaped += allEscaped; - ok(_.every(escapeCharacters, function(escapeChar) { + assert.ok(_.every(escapeCharacters, function(escapeChar) { return allEscaped.indexOf(escapeChar) !== -1; }), 'handles multiple characters'); - ok(allEscaped.indexOf(joiner) >= 0, 'can escape multiple escape characters at the same time'); + assert.ok(allEscaped.indexOf(joiner) >= 0, 'can escape multiple escape characters at the same time'); // test & -> & var str = 'some string & another string & yet another'; var escaped = _.escape(str); - ok(escaped.indexOf('&') !== -1, 'handles & aka &'); - equal(_.unescape(str), str, 'can unescape &'); + assert.ok(escaped.indexOf('&') !== -1, 'handles & aka &'); + assert.equal(_.unescape(str), str, 'can unescape &'); }); - test('template', function() { + test('template', function(assert) { var basicTemplate = _.template("<%= thing %> is gettin' on my noives!"); var result = basicTemplate({thing: 'This'}); - equal(result, "This is gettin' on my noives!", 'can do basic attribute interpolation'); + assert.equal(result, "This is gettin' on my noives!", 'can do basic attribute interpolation'); var sansSemicolonTemplate = _.template('A <% this %> B'); - equal(sansSemicolonTemplate(), 'A B'); + assert.equal(sansSemicolonTemplate(), 'A B'); var backslashTemplate = _.template('<%= thing %> is \\ridanculous'); - equal(backslashTemplate({thing: 'This'}), 'This is \\ridanculous'); + assert.equal(backslashTemplate({thing: 'This'}), 'This is \\ridanculous'); var escapeTemplate = _.template('<%= a ? "checked=\\"checked\\"" : "" %>'); - equal(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.'); + assert.equal(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.'); var fancyTemplate = _.template('

        <% ' + ' for (var key in people) { ' + '%>
      • <%= people[key] %>
      • <% } %>
      '); result = fancyTemplate({people: {moe: 'Moe', larry: 'Larry', curly: 'Curly'}}); - equal(result, '
      • Moe
      • Larry
      • Curly
      ', 'can run arbitrary javascript in templates'); + assert.equal(result, '
      • Moe
      • Larry
      • Curly
      ', 'can run arbitrary javascript in templates'); var escapedCharsInJavascriptTemplate = _.template('
        <% _.each(numbers.split("\\n"), function(item) { %>
      • <%= item %>
      • <% }) %>
      '); result = escapedCharsInJavascriptTemplate({numbers: 'one\ntwo\nthree\nfour'}); - equal(result, '
      • one
      • two
      • three
      • four
      ', 'Can use escaped characters (e.g. \\n) in JavaScript'); + assert.equal(result, '
      • one
      • two
      • three
      • four
      ', 'Can use escaped characters (e.g. \\n) in JavaScript'); var namespaceCollisionTemplate = _.template('<%= pageCount %> <%= thumbnails[pageCount] %> <% _.each(thumbnails, function(p) { %>
      <% }); %>'); result = namespaceCollisionTemplate({ @@ -220,32 +220,32 @@ 3: 'p3-thumbnail.gif' } }); - equal(result, '3 p3-thumbnail.gif
      '); + assert.equal(result, '3 p3-thumbnail.gif
      '); var noInterpolateTemplate = _.template('

      Just some text. Hey, I know this is silly but it aids consistency.

      '); result = noInterpolateTemplate(); - equal(result, '

      Just some text. Hey, I know this is silly but it aids consistency.

      '); + assert.equal(result, '

      Just some text. Hey, I know this is silly but it aids consistency.

      '); var quoteTemplate = _.template("It's its, not it's"); - equal(quoteTemplate({}), "It's its, not it's"); + assert.equal(quoteTemplate({}), "It's its, not it's"); var quoteInStatementAndBody = _.template('<% ' + " if(foo == 'bar'){ " + "%>Statement quotes and 'quotes'.<% } %>"); - equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'."); + assert.equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'."); var withNewlinesAndTabs = _.template('This\n\t\tis: <%= x %>.\n\tok.\nend.'); - equal(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.'); + assert.equal(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.'); var template = _.template('<%- value %>'); result = template({value: '
      + diff --git a/underscore.js b/underscore.js index ba5f828d8..7b78f2adb 100644 --- a/underscore.js +++ b/underscore.js @@ -48,8 +48,10 @@ // Export the Underscore object for **Node.js**, with // backwards-compatibility for their old module API. If we're in // the browser, add `_` as a global object. - if (typeof exports != 'undefined') { - if (typeof module != 'undefined' && module.exports) { + // (`nodeType` is checked to ensure that `module` + // and `exports` are not HTML elements.) + if (typeof exports != 'undefined' && !exports.nodeType) { + if (typeof module != 'undefined' && !module.nodeType && module.exports) { exports = module.exports = _; } exports._ = _; From 66d28bfb43a666cb508846676d367394c06b03e1 Mon Sep 17 00:00:00 2001 From: Graeme Yeates Date: Thu, 22 Oct 2015 09:56:42 -0400 Subject: [PATCH 079/263] Use karma concurrency for initiating parallel sauce tests Conform matrix to backbone's --- .travis.yml | 7 +------ karma.conf-sauce.js | 26 ++++++++++++++++---------- package.json | 2 +- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index c4d7577f5..d5c846134 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,12 +18,7 @@ before_script: script: - npm test - "[ $BROWSER == false ] || npm run test-browser" - # Karma sauce is limited to running about 5-7 browsers (or it will tiemout) at a time so we just run vendor by vendor here - - "[ $BROWSER == false ] || karma start karma.conf-sauce.js --browsers FIREFOX_V4,FIREFOX_V11,FIREFOX_V20,FIREFOX_V30,FIREFOX_V35" - - "[ $BROWSER == false ] || karma start karma.conf-sauce.js --browsers CHROME_V28,CHROME_V35,CHROME_V40,ANDROID_V4.0,ANDROID_V4.3" - - "[ $BROWSER == false ] || karma start karma.conf-sauce.js --browsers INTERNET_EXPLORER_V9,INTERNET_EXPLORER_V10,INTERNET_EXPLORER_V11,MICROSOFTEDGE_V20.10240" - - "[ $BROWSER == false ] || karma start karma.conf-sauce.js --browsers SAFARI_V5,SAFARI_V6,SAFARI_V7" - - "[ $BROWSER == false ] || karma start karma.conf-sauce.js --browsers OPERA_V11,OPERA_V12" + - "[ $BROWSER == false ] || karma start karma.conf-sauce.js" notifications: email: false env: diff --git a/karma.conf-sauce.js b/karma.conf-sauce.js index 1938fdd7e..3a3c460c6 100644 --- a/karma.conf-sauce.js +++ b/karma.conf-sauce.js @@ -4,32 +4,34 @@ var _ = require('./'); var sauceBrowsers = _.reduce([ ['firefox', '35'], ['firefox', '30'], - ['firefox', '20'], + ['firefox', '21'], ['firefox', '11'], ['firefox', '4'], ['chrome', '40'], - ['chrome', '35'], - ['chrome', '28'], + ['chrome', '39'], + ['chrome', '31'], + ['chrome', '26'], - ['microsoftedge', '20.10240', 'Windows 10'], + ['microsoftedge', '20', 'Windows 10'], ['internet explorer', '11', 'Windows 10'], ['internet explorer', '10', 'Windows 8'], ['internet explorer', '9', 'Windows 7'], - // Currently do not work with Karma. - // ['internet explorer', '8', 'Windows 7'], - // ['internet explorer', '7', 'Windows XP'], + ['internet explorer', '8'], + ['internet explorer', '7', 'Windows XP'], // ['internet explorer', '6', 'Windows XP'], ['opera', '12'], ['opera', '11'], - ['android', '4.3'], + ['android', '5'], + ['android', '4.4'], + ['android', '4.3'], ['android', '4.0'], - ['safari', '8'], - ['safari', '6'], + ['safari', '8.0', 'OS X 10.10'], ['safari', '7'], + ['safari', '6'], ['safari', '5'] ], function(memo, platform) { var label = (platform[0] + '_v' + platform[1]).replace(' ', '_').toUpperCase(); @@ -59,6 +61,10 @@ module.exports = function(config) { 'underscore.js', 'test/*.js' ], + + // Number of sauce tests to start in parallel + concurrency: 2, + // test results reporter to use reporters: ['dots', 'saucelabs'], port: 9876, diff --git a/package.json b/package.json index 2cb4cc47c..26e00c2aa 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "coveralls": "^2.11.2", "docco": "*", "eslint": "0.21.x", - "karma": "~0.12.31", + "karma": "^0.13.13", "karma-qunit": "~0.1.4", "nyc": "^2.1.3", "qunit-cli": "~0.2.0", From 77149bc2d7e248598f142d86307bf25e2e49789a Mon Sep 17 00:00:00 2001 From: Graeme Yeates Date: Mon, 26 Oct 2015 12:00:57 -0400 Subject: [PATCH 080/263] Fix karma sauce init for default browser case --- karma.conf-sauce.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/karma.conf-sauce.js b/karma.conf-sauce.js index 3a3c460c6..24fe7bd4b 100644 --- a/karma.conf-sauce.js +++ b/karma.conf-sauce.js @@ -13,20 +13,21 @@ var sauceBrowsers = _.reduce([ ['chrome', '31'], ['chrome', '26'], - ['microsoftedge', '20', 'Windows 10'], + ['microsoftedge', '20.10240', 'Windows 10'], ['internet explorer', '11', 'Windows 10'], ['internet explorer', '10', 'Windows 8'], ['internet explorer', '9', 'Windows 7'], ['internet explorer', '8'], - ['internet explorer', '7', 'Windows XP'], - // ['internet explorer', '6', 'Windows XP'], + // Currently karma-sauce has issues with sockets and these browsers + // ['internet explorer', '7'], + // ['internet explorer', '6'], ['opera', '12'], ['opera', '11'], ['android', '5'], ['android', '4.4'], - ['android', '4.3'], + ['android', '4.3'], ['android', '4.0'], ['safari', '8.0', 'OS X 10.10'], @@ -34,7 +35,12 @@ var sauceBrowsers = _.reduce([ ['safari', '6'], ['safari', '5'] ], function(memo, platform) { - var label = (platform[0] + '_v' + platform[1]).replace(' ', '_').toUpperCase(); + // internet explorer -> ie + var label = platform[0].split(' '); + if (label.length > 1) { + label = _.invoke(label, 'charAt', 0) + } + label = (label.join("") + '_v' + platform[1]).replace(' ', '_').toUpperCase(); memo[label] = _.pick({ 'base': 'SauceLabs', 'browserName': platform[0], @@ -63,7 +69,7 @@ module.exports = function(config) { ], // Number of sauce tests to start in parallel - concurrency: 2, + concurrency: 9, // test results reporter to use reporters: ['dots', 'saucelabs'], @@ -76,14 +82,11 @@ module.exports = function(config) { tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER }, - // TODO(vojta): remove once SauceLabs supports websockets. - // This speeds up the capturing a bit, as browsers don't even try to use websocket. - transports: ['xhr-polling'], captureTimeout: 120000, - customLaunchers: sauceBrowsers + customLaunchers: sauceBrowsers, // Browsers to launch, commented out to prevent karma from starting // too many concurrent browsers and timing sauce out. - // browsers: _.keys(sauceBrowsers) + browsers: _.keys(sauceBrowsers) }); }; From cd54225d3cc41389dd1ae562e3ddc78962e5f2cb Mon Sep 17 00:00:00 2001 From: Graeme Yeates Date: Mon, 26 Oct 2015 12:12:56 -0400 Subject: [PATCH 081/263] Temp disable some old ie due to external disconnect issues --- karma.conf-sauce.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/karma.conf-sauce.js b/karma.conf-sauce.js index 24fe7bd4b..1b2f622ed 100644 --- a/karma.conf-sauce.js +++ b/karma.conf-sauce.js @@ -17,8 +17,8 @@ var sauceBrowsers = _.reduce([ ['internet explorer', '11', 'Windows 10'], ['internet explorer', '10', 'Windows 8'], ['internet explorer', '9', 'Windows 7'], - ['internet explorer', '8'], - // Currently karma-sauce has issues with sockets and these browsers + // Currently disabled due to karma-sauce issues + // ['internet explorer', '8'], // ['internet explorer', '7'], // ['internet explorer', '6'], From 1b5b1eeb9ab4a0507bb764087aeaf5701df14487 Mon Sep 17 00:00:00 2001 From: Graeme Yeates Date: Mon, 26 Oct 2015 12:21:41 -0400 Subject: [PATCH 082/263] Add node v4.0 to the test matrix --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d5c846134..cb9a86882 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,11 +2,12 @@ language: node_js sudo: false node_js: - "0.8" + - "0.10" - "0.12" - "io.js" matrix: include: - - node_js: "0.10" + - node_js: "4.0" env: BROWSER=true before_install: - npm install -g npm@2.6 From 3521ab29ec9c66b1962abede2d9b4d68634bfd40 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Mon, 26 Oct 2015 14:41:37 -0700 Subject: [PATCH 083/263] Drop io.js from travis.yml. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index cb9a86882..172af86c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ node_js: - "0.8" - "0.10" - "0.12" - - "io.js" matrix: include: - node_js: "4.0" From 37d00493a4cadb5b35dbe17e3fce5347f41040d3 Mon Sep 17 00:00:00 2001 From: Ricardo Bin Date: Thu, 29 Oct 2015 22:28:26 -0200 Subject: [PATCH 084/263] Adding preserve -0 test to _.range --- test/arrays.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/arrays.js b/test/arrays.js index 0de58e07e..550f0bc4e 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -537,6 +537,7 @@ assert.deepEqual(_.range(3, 10, 15), [3], 'range with three arguments a & b & c, c > b-a, a < b generates an array with a single element, equal to a'); assert.deepEqual(_.range(12, 7, -2), [12, 10, 8], 'range with three arguments a & b & c, a > b, c < 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b'); assert.deepEqual(_.range(0, -10, -1), [0, -1, -2, -3, -4, -5, -6, -7, -8, -9], 'final example in the Python docs'); + assert.strictEqual(1 / _.range(-0, 1)[0], -Infinity, 'should preserve -0'); }); test('chunk', function(assert) { From 2fa0ac6e25d900100a4e14f32f4551e34daef61b Mon Sep 17 00:00:00 2001 From: guiled Date: Wed, 26 Mar 2014 23:17:02 +0100 Subject: [PATCH 085/263] New feature : reset the debounce It seems a little crappy to add a function into a function, but it provides a feature that was asked in #952 without breaking backward compatibility. --- underscore.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/underscore.js b/underscore.js index 7b78f2adb..0480b08df 100644 --- a/underscore.js +++ b/underscore.js @@ -857,7 +857,7 @@ } }; - return function() { + var func = function() { context = this; args = arguments; timestamp = _.now(); @@ -870,6 +870,12 @@ return result; }; + func.reset = function () { + clearTimeout(timeout); + timeout = null; + }; + + return func; }; // Returns the first function passed as an argument to the second, From c7bb6359a4fd57113b5a6f4ac9e89012c179e87a Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Thu, 5 Nov 2015 11:02:03 -0500 Subject: [PATCH 086/263] Clear debounced function Supersedes https://github.com/jashkenas/underscore/pull/1542. --- test/functions.js | 26 ++++++++++++++++++++++++++ underscore.js | 11 ++++++----- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/test/functions.js b/test/functions.js index 926681c9d..18ab66e26 100644 --- a/test/functions.js +++ b/test/functions.js @@ -404,6 +404,15 @@ _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); start(); }, 96); }); + asyncTest('debounce cleared', 1, function(assert) { + var counter = 0; + var incr = function(){ counter++; }; + var debouncedIncr = _.debounce(incr, 32); + debouncedIncr(); + debouncedIncr.clear(); + _.delay(function(){ assert.equal(counter, 0, 'incr was not called'); start(); }, 96); + }); + asyncTest('debounce asap', 4, function(assert) { var a, b; var counter = 0; @@ -420,6 +429,23 @@ _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); start(); }, 128); }); + asyncTest('debounce asap cleared', 4, function(assert) { + var a, b; + var counter = 0; + var incr = function(){ return ++counter; }; + var debouncedIncr = _.debounce(incr, 64, true); + a = debouncedIncr(); + debouncedIncr.clear(); + b = debouncedIncr(); + assert.equal(a, 1); + assert.equal(b, 2); + assert.equal(counter, 2, 'incr was called immediately'); + _.delay(debouncedIncr, 16); + _.delay(debouncedIncr, 32); + _.delay(debouncedIncr, 48); + _.delay(function(){ assert.equal(counter, 2, 'incr was debounced'); start(); }, 128); + }); + asyncTest('debounce asap recursively', 2, function(assert) { var counter = 0; var debouncedIncr = _.debounce(function(){ diff --git a/underscore.js b/underscore.js index 0480b08df..ead4987ae 100644 --- a/underscore.js +++ b/underscore.js @@ -857,7 +857,7 @@ } }; - var func = function() { + var debounced = function() { context = this; args = arguments; timestamp = _.now(); @@ -870,12 +870,13 @@ return result; }; - func.reset = function () { + + debounced.clear = function() { clearTimeout(timeout); - timeout = null; + timeout = context = args = null; }; - - return func; + + return debounced; }; // Returns the first function passed as an argument to the second, From 8a70f347e32650f9ca114b99ee09c61e711ecc08 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Thu, 5 Nov 2015 14:50:20 -0500 Subject: [PATCH 087/263] Clear the throttle --- test/functions.js | 24 ++++++++++++++++++++++++ underscore.js | 15 ++++++++++++--- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/test/functions.js b/test/functions.js index 18ab66e26..0e02b3531 100644 --- a/test/functions.js +++ b/test/functions.js @@ -395,6 +395,30 @@ }, 100); }); + asyncTest('throttle cleared', function(assert) { + var counter = 0; + var incr = function(){ counter++; }; + var throttledIncr = _.throttle(incr, 32); + throttledIncr(); + throttledIncr.clear(); + throttledIncr(); + throttledIncr(); + + assert.equal(counter, 2, 'incr was called immediately'); + _.delay(function(){ assert.equal(counter, 3, 'incr was throttled'); start(); }, 64); + }); + + asyncTest('throttle cleared with leading: false', function(assert) { + var counter = 0; + var incr = function(){ counter++; }; + var throttledIncr = _.throttle(incr, 32, {leading: false}); + throttledIncr(); + throttledIncr.clear(); + + assert.equal(counter, 0, 'incr was throttled'); + _.delay(function(){ assert.equal(counter, 0, 'incr was throttled'); start(); }, 64); + }); + asyncTest('debounce', 1, function(assert) { var counter = 0; var incr = function(){ counter++; }; diff --git a/underscore.js b/underscore.js index ead4987ae..6a8f254c8 100644 --- a/underscore.js +++ b/underscore.js @@ -805,17 +805,18 @@ // but if you'd like to disable the execution on the leading edge, pass // `{leading: false}`. To disable execution on the trailing edge, ditto. _.throttle = function(func, wait, options) { - var context, args, result; - var timeout = null; + var timeout, context, args, result; var previous = 0; if (!options) options = {}; + var later = function() { previous = options.leading === false ? 0 : _.now(); timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; - return function() { + + var throttled = function() { var now = _.now(); if (!previous && options.leading === false) previous = now; var remaining = wait - (now - previous); @@ -834,6 +835,14 @@ } return result; }; + + throttled.clear = function() { + clearTimeout(timeout); + previous = 0; + timeout = context = args = null; + }; + + return throttled; }; // Returns a function, that, as long as it continues to be invoked, will not From f812ba7b00e97a332d5d7374f8a6afaf2576ca50 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Thu, 5 Nov 2015 14:56:28 -0500 Subject: [PATCH 088/263] Cleanup the debounce function --- underscore.js | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/underscore.js b/underscore.js index ead4987ae..4d0aa1b5d 100644 --- a/underscore.js +++ b/underscore.js @@ -790,7 +790,7 @@ // Delays a function for the given number of milliseconds, and then calls // it with the arguments supplied. _.delay = restArgs(function(func, wait, args) { - return setTimeout(function(){ + return setTimeout(function() { return func.apply(null, args); }, wait); }); @@ -841,35 +841,25 @@ // N milliseconds. If `immediate` is passed, trigger the function on the // leading edge, instead of the trailing. _.debounce = function(func, wait, immediate) { - var timeout, args, context, timestamp, result; + var timeout, result; - var later = function() { - var last = _.now() - timestamp; - - if (last < wait && last >= 0) { - timeout = setTimeout(later, wait - last); - } else { - timeout = null; - if (!immediate) { - result = func.apply(context, args); - if (!timeout) context = args = null; - } - } + var later = function(context, args) { + timeout = null; + if (args) result = func.apply(context, args); }; - var debounced = function() { - context = this; - args = arguments; - timestamp = _.now(); + var debounced = restArgs(function(args) { var callNow = immediate && !timeout; - if (!timeout) timeout = setTimeout(later, wait); + if (timeout) clearTimeout(timeout); if (callNow) { - result = func.apply(context, args); - context = args = null; + timeout = setTimeout(later, wait); + result = func.apply(this, args); + } else if (!immediate) { + timeout = _.delay(later, wait, this, args); } return result; - }; + }); debounced.clear = function() { clearTimeout(timeout); From e7221cb4d03667b83a6a99a4d37ae465169550ed Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Thu, 5 Nov 2015 17:25:58 -0500 Subject: [PATCH 089/263] Fix debounce clear error Just spotted this, re: #2340. --- underscore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/underscore.js b/underscore.js index 262765672..77c8ecd12 100644 --- a/underscore.js +++ b/underscore.js @@ -872,7 +872,7 @@ debounced.clear = function() { clearTimeout(timeout); - timeout = context = args = null; + timeout = null; }; return debounced; From fb700690bdd72cd19182c85978ed69e28a10350d Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Tue, 10 Nov 2015 17:54:04 -0800 Subject: [PATCH 090/263] enable eslint no-undef rule; ref #2341 --- .eslintrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.eslintrc b/.eslintrc index 0e88b37eb..216f4a0c3 100644 --- a/.eslintrc +++ b/.eslintrc @@ -66,6 +66,7 @@ "no-spaced-func": 2, "no-throw-literal": 2, "no-trailing-spaces": 2, + "no-undef": 2, "no-undef-init": 2, "no-undefined": 2, "no-unneeded-ternary": 2, From e2e1d65147d5007c0c6288591bc8ed145e1958fa Mon Sep 17 00:00:00 2001 From: Adam Krebs Date: Thu, 12 Nov 2015 14:07:14 -0500 Subject: [PATCH 091/263] enable QUnit.config.noglobals in tests --- karma.conf-sauce.js | 1 + karma.conf.js | 1 + test/index.html | 2 +- test/qunit-setup.js | 3 +++ test/utility.js | 2 ++ 5 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 test/qunit-setup.js diff --git a/karma.conf-sauce.js b/karma.conf-sauce.js index 1b2f622ed..7cf946ecd 100644 --- a/karma.conf-sauce.js +++ b/karma.conf-sauce.js @@ -64,6 +64,7 @@ module.exports = function(config) { // list of files / patterns to load in the browser files: [ 'test/vendor/qunit-extras.js', + 'test/qunit-setup.js', 'underscore.js', 'test/*.js' ], diff --git a/karma.conf.js b/karma.conf.js index fb0fa376b..d01f24ee0 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -15,6 +15,7 @@ module.exports = function(config) { // list of files / patterns to load in the browser files: [ 'test/vendor/qunit-extras.js', + 'test/qunit-setup.js', 'underscore.js', 'test/*.js' ], diff --git a/test/index.html b/test/index.html index 4b4e03072..ab523afe4 100644 --- a/test/index.html +++ b/test/index.html @@ -11,7 +11,7 @@ - + diff --git a/test/qunit-setup.js b/test/qunit-setup.js new file mode 100644 index 000000000..ad1e8cd8c --- /dev/null +++ b/test/qunit-setup.js @@ -0,0 +1,3 @@ +(function() { + QUnit.config.noglobals = true; +}()); diff --git a/test/utility.js b/test/utility.js index bb58ae8a3..09cd54d62 100644 --- a/test/utility.js +++ b/test/utility.js @@ -21,6 +21,8 @@ if (typeof require != 'function') { assert.equal(this._, void 0, 'global underscore is removed'); this._ = underscore; + } else if (typeof global !== 'undefined') { + delete global._; } }); } From 749861bcf7c8b190d69b9cfbdba82c0609a48af8 Mon Sep 17 00:00:00 2001 From: Chris Martin Date: Mon, 16 Nov 2015 20:22:59 -0800 Subject: [PATCH 092/263] debounce doc: which invocation's args are used --- index.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/index.html b/index.html index e1fc14051..8094957db 100644 --- a/index.html +++ b/index.html @@ -1233,6 +1233,12 @@

      Function (uh, ahem) Functions

      has stopped being resized, and so on.

      +

      + At the end of the wait interval, the function will be called + with the arguments that were passed most recently to the + debounced function. +

      +

      Pass true for the immediate argument to cause debounce to trigger the function on the leading instead of the From a7f8ae120bc35e45e950a3745800d76db0d736c1 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Tue, 17 Nov 2015 20:57:48 -0800 Subject: [PATCH 093/263] Remove redundant empty string handing in _.toArray() As part of the review for pull request #2298, @jdalton requested that we add this check to handle the empty string case. However, I'm guessing he didn't realize that the first line of the function, `if (!obj) return [];`, already handles that case. As part of that pull request, @JonAbrams wisely added a test for the empty string case, so as long as tests are passing this change should be safe. See: https://github.com/jashkenas/underscore/pull/2298#discussion_r39480698 --- underscore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/underscore.js b/underscore.js index 77c8ecd12..682cdf647 100644 --- a/underscore.js +++ b/underscore.js @@ -441,7 +441,7 @@ if (_.isArray(obj)) return slice.call(obj); if (_.isString(obj)) { // Keep surrogate pair characters together - return obj ? obj.match(reStrSymbol) : []; + return obj.match(reStrSymbol); } if (isArrayLike(obj)) return _.map(obj, _.identity); return _.values(obj); From a6fcdd3a50358c2d7824521a85515526da5ecdb7 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Mon, 23 Nov 2015 19:08:26 -0800 Subject: [PATCH 094/263] Clean up assertion descriptions for _.flatten() --- test/arrays.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index 550f0bc4e..8dc134cd9 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -80,11 +80,11 @@ }); test('flatten', function(assert) { - assert.deepEqual(_.flatten(null), [], 'Flattens supports null'); - assert.deepEqual(_.flatten(void 0), [], 'Flattens supports undefined'); + assert.deepEqual(_.flatten(null), [], 'supports null'); + assert.deepEqual(_.flatten(void 0), [], 'supports undefined'); - assert.deepEqual(_.flatten([[], [[]], []]), [], 'Flattens empty arrays'); - assert.deepEqual(_.flatten([[], [[]], []], true), [[]], 'Flattens empty arrays'); + assert.deepEqual(_.flatten([[], [[]], []]), [], 'supports empty arrays'); + assert.deepEqual(_.flatten([[], [[]], []], true), [[]], 'can shallowly flatten empty arrays'); var list = [1, [2], [3, [[[4]]]]]; assert.deepEqual(_.flatten(list), [1, 2, 3, 4], 'can flatten nested arrays'); @@ -94,15 +94,15 @@ list = [[1], [2], [3], [[4]]]; assert.deepEqual(_.flatten(list, true), [1, 2, 3, [4]], 'can shallowly flatten arrays containing only other arrays'); - assert.equal(_.flatten([_.range(10), _.range(10), 5, 1, 3], true).length, 23); - assert.equal(_.flatten([_.range(10), _.range(10), 5, 1, 3]).length, 23); - assert.equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3]).length, 1056003, 'Flatten can handle massive collections'); - assert.equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3], true).length, 1056003, 'Flatten can handle massive collections'); + assert.equal(_.flatten([_.range(10), _.range(10), 5, 1, 3], true).length, 23, 'can flatten medium length arrays'); + assert.equal(_.flatten([_.range(10), _.range(10), 5, 1, 3]).length, 23, 'can shallowly flatten medium length arrays'); + assert.equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3]).length, 1056003, 'can handle massive arrays'); + assert.equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3], true).length, 1056003, 'can handle massive arrays in shallow mode'); var x = _.range(100000); for (var i = 0; i < 1000; i++) x = [x]; - assert.deepEqual(_.flatten(x), _.range(100000), 'Flatten can handle very deep arrays'); - assert.deepEqual(_.flatten(x, true), x[0], 'Flatten can handle very deep arrays with shallow'); + assert.deepEqual(_.flatten(x), _.range(100000), 'can handle very deep arrays'); + assert.deepEqual(_.flatten(x, true), x[0], 'can handle very deep arrays in shallow mode'); }); test('without', function(assert) { From db30872e27aae757cf3110e096915ec28b150dbe Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Tue, 24 Nov 2015 09:00:26 -0800 Subject: [PATCH 095/263] Remove redundant _.first() test This test was originally added in pull request #500 as a way to affirm that the `_.take()` alias was working. In pull request #1663 we moved to testing aliases explicitly, but left this test an additional `_.first()` test. It is redundant because it is functionally identical to the test on line 10. --- test/arrays.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index 8dc134cd9..44fd1553c 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -13,8 +13,6 @@ assert.equal(result, 4, 'works on an arguments object.'); result = _.map([[1, 2, 3], [1, 2, 3]], _.first); assert.deepEqual(result, [1, 1], 'works well with _.map'); - result = (function() { return _.first([1, 2, 3], 2); }()); - assert.deepEqual(result, [1, 2]); assert.equal(_.first(null), void 0, 'handles nulls'); assert.strictEqual(_.first([1, 2, 3], -1).length, 0); From b99f2f7d5d80d892ede0a0c4fbc06c455d6d5bfe Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Tue, 24 Nov 2015 09:19:11 -0800 Subject: [PATCH 096/263] Clean up assertions for _.first() Makes the assertion descriptions more consistent. In the case of the `-n` test, I opted to rewrite it to be more explicit. Asserting that it actually returns an empty array is ever so slightly more meaningful than testing that its return value has a length of 0. --- test/arrays.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index 44fd1553c..2ee9bd6c4 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -6,16 +6,15 @@ test('first', function(assert) { assert.equal(_.first([1, 2, 3]), 1, 'can pull out the first element of an array'); assert.equal(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"'); - assert.deepEqual(_.first([1, 2, 3], 0), [], 'can pass an index to first'); - assert.deepEqual(_.first([1, 2, 3], 2), [1, 2], 'can pass an index to first'); - assert.deepEqual(_.first([1, 2, 3], 5), [1, 2, 3], 'can pass an index to first'); + assert.deepEqual(_.first([1, 2, 3], 0), [], 'can fetch the first 0 elements'); + assert.deepEqual(_.first([1, 2, 3], 2), [1, 2], 'can fetch the first n elements'); + assert.deepEqual(_.first([1, 2, 3], 5), [1, 2, 3], 'returns the whole array if n > length'); var result = (function(){ return _.first(arguments); }(4, 3, 2, 1)); - assert.equal(result, 4, 'works on an arguments object.'); + assert.equal(result, 4, 'works on an arguments object'); result = _.map([[1, 2, 3], [1, 2, 3]], _.first); assert.deepEqual(result, [1, 1], 'works well with _.map'); - - assert.equal(_.first(null), void 0, 'handles nulls'); - assert.strictEqual(_.first([1, 2, 3], -1).length, 0); + assert.equal(_.first(null), void 0, 'returns undefined when called on null'); + assert.deepEqual(_.first([1, 2, 3], -1), [], 'returns an empty array when asked for -n elements'); }); test('head', function(assert) { From e2c67d1f6aa44b2dc93390e05aac73c00dca002e Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Tue, 24 Nov 2015 10:56:09 -0800 Subject: [PATCH 097/263] Clean up testing of aliases * Ensure all tests are run against the canonical function name * Ensure all aliases mentioned in the documentation are tested * Remove redundant tests which were originally meant to test aliases * Make alias assertion descriptions consistent readable sentences * Use `(actual, expected)` argument order in alias assertions --- test/arrays.js | 10 +++++----- test/collections.js | 48 +++++++++++++++++++++++---------------------- test/objects.js | 30 +++++++++++++++++----------- 3 files changed, 49 insertions(+), 39 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index 2ee9bd6c4..596fb87ca 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -18,11 +18,11 @@ }); test('head', function(assert) { - assert.strictEqual(_.first, _.head, 'alias for first'); + assert.strictEqual(_.head, _.first, 'is an alias for first'); }); test('take', function(assert) { - assert.strictEqual(_.first, _.take, 'alias for first'); + assert.strictEqual(_.take, _.first, 'is an alias for first'); }); test('rest', function(assert) { @@ -39,11 +39,11 @@ }); test('tail', function(assert) { - assert.strictEqual(_.rest, _.tail, 'alias for rest'); + assert.strictEqual(_.tail, _.rest, 'is an alias for rest'); }); test('drop', function(assert) { - assert.strictEqual(_.rest, _.drop, 'alias for rest'); + assert.strictEqual(_.drop, _.rest, 'is an alias for rest'); }); test('initial', function(assert) { @@ -192,7 +192,7 @@ }); test('unique', function(assert) { - assert.strictEqual(_.uniq, _.unique, 'alias for uniq'); + assert.strictEqual(_.unique, _.uniq, 'is an alias for uniq'); }); test('intersection', function(assert) { diff --git a/test/collections.js b/test/collections.js index d900f88ae..721f4acb7 100644 --- a/test/collections.js +++ b/test/collections.js @@ -14,7 +14,7 @@ answers = []; _.each([1, 2, 3], function(num){ answers.push(num); }); - assert.deepEqual(answers, [1, 2, 3], 'aliased as "forEach"'); + assert.deepEqual(answers, [1, 2, 3], 'can iterate a simple array'); answers = []; var obj = {one: 1, two: 2, three: 3}; @@ -46,7 +46,7 @@ }); test('forEach', function(assert) { - assert.strictEqual(_.each, _.forEach, 'alias for each'); + assert.strictEqual(_.forEach, _.each, 'is an alias for each'); }); test('lookupIterator with contexts', function(assert) { @@ -172,7 +172,7 @@ }); test('collect', function(assert) { - assert.strictEqual(_.map, _.collect, 'alias for map'); + assert.strictEqual(_.collect, _.map, 'is an alias for map'); }); test('reduce', function(assert) { @@ -183,9 +183,6 @@ sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num * this.multiplier; }, 0, context); assert.equal(sum, 18, 'can reduce with a context object'); - sum = _.inject([1, 2, 3], function(memo, num){ return memo + num; }, 0); - assert.equal(sum, 6, 'aliased as "inject"'); - sum = _([1, 2, 3]).reduce(function(memo, num){ return memo + num; }, 0); assert.equal(sum, 6, 'OO-style reduce'); @@ -202,7 +199,11 @@ }); test('foldl', function(assert) { - assert.strictEqual(_.reduce, _.foldl, 'alias for reduce'); + assert.strictEqual(_.foldl, _.reduce, 'is an alias for reduce'); + }); + + test('inject', function(assert) { + assert.strictEqual(_.inject, _.reduce, 'is an alias for reduce'); }); test('reduceRight', function(assert) { @@ -256,7 +257,7 @@ }); test('foldr', function(assert) { - assert.strictEqual(_.reduceRight, _.foldr, 'alias for reduceRight'); + assert.strictEqual(_.foldr, _.reduceRight, 'is an alias for reduceRight'); }); test('find', function(assert) { @@ -298,7 +299,7 @@ }); test('detect', function(assert) { - assert.strictEqual(_.detect, _.find, 'alias for detect'); + assert.strictEqual(_.detect, _.find, 'is an alias for find'); }); test('filter', function(assert) { @@ -323,7 +324,7 @@ }); test('select', function(assert) { - assert.strictEqual(_.filter, _.select, 'alias for filter'); + assert.strictEqual(_.select, _.filter, 'is an alias for filter'); }); test('reject', function(assert) { @@ -373,7 +374,7 @@ }); test('all', function(assert) { - assert.strictEqual(_.all, _.every, 'alias for all'); + assert.strictEqual(_.all, _.every, 'is an alias for every'); }); test('some', function(assert) { @@ -403,7 +404,7 @@ }); test('any', function(assert) { - assert.strictEqual(_.any, _.some, 'alias for any'); + assert.strictEqual(_.any, _.some, 'is an alias for some'); }); test('includes', function(assert) { @@ -417,24 +418,25 @@ assert.ok(_.includes({moe: 1, larry: 3, curly: 9}, 3) === true, '_.includes on objects checks their values'); assert.ok(_([1, 2, 3]).includes(2), 'OO-style includes'); + + var numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; + assert.strictEqual(_.includes(numbers, 1, 1), true, 'takes a fromIndex'); + assert.strictEqual(_.includes(numbers, 1, -1), false, 'takes a fromIndex'); + assert.strictEqual(_.includes(numbers, 1, -2), false, 'takes a fromIndex'); + assert.strictEqual(_.includes(numbers, 1, -3), true, 'takes a fromIndex'); + assert.strictEqual(_.includes(numbers, 1, 6), true, 'takes a fromIndex'); + assert.strictEqual(_.includes(numbers, 1, 7), false, 'takes a fromIndex'); + + assert.ok(_.every([1, 2, 3], _.partial(_.includes, numbers)), 'fromIndex is guarded'); }); test('include', function(assert) { - assert.strictEqual(_.includes, _.include, 'alias for includes'); + assert.strictEqual(_.include, _.includes, 'is an alias for includes'); }); test('contains', function(assert) { - assert.strictEqual(_.includes, _.contains, 'alias for includes'); + assert.strictEqual(_.contains, _.includes, 'is an alias for includes'); - var numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; - assert.strictEqual(_.includes(numbers, 1, 1), true, 'contains takes a fromIndex'); - assert.strictEqual(_.includes(numbers, 1, -1), false, 'contains takes a fromIndex'); - assert.strictEqual(_.includes(numbers, 1, -2), false, 'contains takes a fromIndex'); - assert.strictEqual(_.includes(numbers, 1, -3), true, 'contains takes a fromIndex'); - assert.strictEqual(_.includes(numbers, 1, 6), true, 'contains takes a fromIndex'); - assert.strictEqual(_.includes(numbers, 1, 7), false, 'contains takes a fromIndex'); - - assert.ok(_.every([1, 2, 3], _.partial(_.contains, numbers)), 'fromIndex is guarded'); }); test('includes with NaN', function(assert) { diff --git a/test/objects.js b/test/objects.js index 29bbd9281..bc3075839 100644 --- a/test/objects.js +++ b/test/objects.js @@ -102,7 +102,7 @@ }); test('methods', function(assert) { - assert.strictEqual(_.functions, _.methods, 'alias for functions'); + assert.strictEqual(_.methods, _.functions, 'is an alias for functions'); }); test('extend', function(assert) { @@ -138,32 +138,36 @@ test('extendOwn', function(assert) { var result; - assert.equal(_.extendOwn({}, {a: 'b'}).a, 'b', 'can assign an object with the attributes of another'); + assert.equal(_.extendOwn({}, {a: 'b'}).a, 'b', 'can extend an object with the attributes of another'); assert.equal(_.extendOwn({a: 'x'}, {a: 'b'}).a, 'b', 'properties in source override destination'); assert.equal(_.extendOwn({x: 'x'}, {a: 'b'}).x, 'x', "properties not in source don't get overriden"); result = _.extendOwn({x: 'x'}, {a: 'a'}, {b: 'b'}); - assert.deepEqual(result, {x: 'x', a: 'a', b: 'b'}, 'can assign from multiple source objects'); - result = _.assign({x: 'x'}, {a: 'a', x: 2}, {a: 'b'}); - assert.deepEqual(result, {x: 2, a: 'b'}, 'assigning from multiple source objects last property trumps'); - assert.deepEqual(_.extendOwn({}, {a: void 0, b: null}), {a: void 0, b: null}, 'assign copies undefined values'); + assert.deepEqual(result, {x: 'x', a: 'a', b: 'b'}, 'can extend from multiple source objects'); + result = _.extendOwn({x: 'x'}, {a: 'a', x: 2}, {a: 'b'}); + assert.deepEqual(result, {x: 2, a: 'b'}, 'extending from multiple source objects last property trumps'); + assert.deepEqual(_.extendOwn({}, {a: void 0, b: null}), {a: void 0, b: null}, 'copies undefined values'); var F = function() {}; F.prototype = {a: 'b'}; var subObj = new F(); subObj.c = 'd'; - assert.deepEqual(_.extendOwn({}, subObj), {c: 'd'}, 'assign copies own properties from source'); + assert.deepEqual(_.extendOwn({}, subObj), {c: 'd'}, 'copies own properties from source'); result = {}; - assert.deepEqual(_.assign(result, null, void 0, {a: 1}), {a: 1}, 'should not error on `null` or `undefined` sources'); + assert.deepEqual(_.extendOwn(result, null, void 0, {a: 1}), {a: 1}, 'should not error on `null` or `undefined` sources'); _.each(['a', 5, null, false], function(val) { - assert.strictEqual(_.assign(val, {a: 1}), val, 'assigning non-objects results in returning the non-object value'); + assert.strictEqual(_.extendOwn(val, {a: 1}), val, 'extending non-objects results in returning the non-object value'); }); - assert.strictEqual(_.extendOwn(void 0, {a: 1}), void 0, 'assigning undefined results in undefined'); + assert.strictEqual(_.extendOwn(void 0, {a: 1}), void 0, 'extending undefined results in undefined'); result = _.extendOwn({a: 1, 0: 2, 1: '5', length: 6}, {0: 1, 1: 2, length: 2}); - assert.deepEqual(result, {a: 1, 0: 1, 1: 2, length: 2}, 'assign should treat array-like objects like normal objects'); + assert.deepEqual(result, {a: 1, 0: 1, 1: 2, length: 2}, 'should treat array-like objects like normal objects'); + }); + + test('assign', function(assert) { + assert.strictEqual(_.assign, _.extendOwn, 'is an alias for extendOwn'); }); test('pick', function(assert) { @@ -879,6 +883,10 @@ assert.deepEqual(_.map([null, void 0, 5, {}], oCon), [false, false, false, true], 'doesnt falsey match constructor on undefined/null'); }); + test('matches', function(assert) { + assert.strictEqual(_.matches, _.matcher, 'is an alias for matcher'); + }); + test('findKey', function(assert) { var objects = { a: {a: 0, b: 0}, From e38a9debcc73f03bca4179f147159aab6fcaf1c4 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Tue, 24 Nov 2015 12:02:58 -0800 Subject: [PATCH 098/263] Clean up assertions for _.rest() Clean up assertion descriptions, and remove a duplicate assertion. The duplicate assertion is a remnant from when that assertion was used to test the `_.drop` alias. It was made obsolete in pull request #1663. --- test/arrays.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index 596fb87ca..6e5e6b87f 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -27,15 +27,13 @@ test('rest', function(assert) { var numbers = [1, 2, 3, 4]; - assert.deepEqual(_.rest(numbers), [2, 3, 4], 'working rest()'); - assert.deepEqual(_.rest(numbers, 0), [1, 2, 3, 4], 'working rest(0)'); - assert.deepEqual(_.rest(numbers, 2), [3, 4], 'rest can take an index'); + assert.deepEqual(_.rest(numbers), [2, 3, 4], 'fetches all but the first element'); + assert.deepEqual(_.rest(numbers, 0), [1, 2, 3, 4], 'returns the whole array when n is 0'); + assert.deepEqual(_.rest(numbers, 2), [3, 4], 'returns all but the first n elements'); var result = (function(){ return _(arguments).rest(); }(1, 2, 3, 4)); - assert.deepEqual(result, [2, 3, 4], 'works on arguments object'); + assert.deepEqual(result, [2, 3, 4], 'works on an arguments object'); result = _.map([[1, 2, 3], [1, 2, 3]], _.rest); assert.deepEqual(_.flatten(result), [2, 3, 2, 3], 'works well with _.map'); - result = (function(){ return _(arguments).rest(); }(1, 2, 3, 4)); - assert.deepEqual(result, [2, 3, 4], 'works on arguments object'); }); test('tail', function(assert) { From c74610d089f0010bbdb48902b255a78b029467f8 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Tue, 24 Nov 2015 14:23:16 -0800 Subject: [PATCH 099/263] Cleanup _.rest() assertion descriptions again Make the language better match the documentation --- test/arrays.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index 6e5e6b87f..f0f250b7b 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -28,8 +28,8 @@ test('rest', function(assert) { var numbers = [1, 2, 3, 4]; assert.deepEqual(_.rest(numbers), [2, 3, 4], 'fetches all but the first element'); - assert.deepEqual(_.rest(numbers, 0), [1, 2, 3, 4], 'returns the whole array when n is 0'); - assert.deepEqual(_.rest(numbers, 2), [3, 4], 'returns all but the first n elements'); + assert.deepEqual(_.rest(numbers, 0), [1, 2, 3, 4], 'returns the whole array when index is 0'); + assert.deepEqual(_.rest(numbers, 2), [3, 4], 'returns elements starting at the given index'); var result = (function(){ return _(arguments).rest(); }(1, 2, 3, 4)); assert.deepEqual(result, [2, 3, 4], 'works on an arguments object'); result = _.map([[1, 2, 3], [1, 2, 3]], _.rest); From c6e0658e1979f2f301a28622016012ff79c008d2 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Tue, 24 Nov 2015 14:28:20 -0800 Subject: [PATCH 100/263] Cleanup assertion descriptions for _.initial() --- test/arrays.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index 6e5e6b87f..ad9100c66 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -45,13 +45,13 @@ }); test('initial', function(assert) { - assert.deepEqual(_.initial([1, 2, 3, 4, 5]), [1, 2, 3, 4], 'working initial()'); - assert.deepEqual(_.initial([1, 2, 3, 4], 2), [1, 2], 'initial can take an index'); - assert.deepEqual(_.initial([1, 2, 3, 4], 6), [], 'initial can take a large index'); + assert.deepEqual(_.initial([1, 2, 3, 4, 5]), [1, 2, 3, 4], 'returns all but the last element'); + assert.deepEqual(_.initial([1, 2, 3, 4], 2), [1, 2], 'returns all but the last n elements'); + assert.deepEqual(_.initial([1, 2, 3, 4], 6), [], 'returns an empty array when n > length'); var result = (function(){ return _(arguments).initial(); }(1, 2, 3, 4)); - assert.deepEqual(result, [1, 2, 3], 'initial works on arguments object'); + assert.deepEqual(result, [1, 2, 3], 'works on an arguments object'); result = _.map([[1, 2, 3], [1, 2, 3]], _.initial); - assert.deepEqual(_.flatten(result), [1, 2, 1, 2], 'initial works with _.map'); + assert.deepEqual(_.flatten(result), [1, 2, 1, 2], 'works well with _.map'); }); test('last', function(assert) { From 1fb7d5cb94de4728f246c63cf54b9979f05a3185 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Tue, 24 Nov 2015 14:46:50 -0800 Subject: [PATCH 101/263] Clean up assertions for _.last() Improves messages and ordering. Also makes the `-n` assertion explicitly check for an empty array. --- test/arrays.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index 6e5e6b87f..92dc1d6dc 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -56,16 +56,16 @@ test('last', function(assert) { assert.equal(_.last([1, 2, 3]), 3, 'can pull out the last element of an array'); - assert.deepEqual(_.last([1, 2, 3], 0), [], 'can pass an index to last'); - assert.deepEqual(_.last([1, 2, 3], 2), [2, 3], 'can pass an index to last'); - assert.deepEqual(_.last([1, 2, 3], 5), [1, 2, 3], 'can pass an index to last'); + assert.equal(_([1, 2, 3]).last(), 3, 'can perform OO-style "last()"'); + assert.deepEqual(_.last([1, 2, 3], 0), [], 'returns an empty array when n <= 0 (0 case)'); + assert.deepEqual(_.last([1, 2, 3], -1), [], 'returns an empty array when n <= 0 (negative case)'); + assert.deepEqual(_.last([1, 2, 3], 2), [2, 3], 'can fetch the last n elements'); + assert.deepEqual(_.last([1, 2, 3], 5), [1, 2, 3], 'returns the whole array if n > length'); var result = (function(){ return _(arguments).last(); }(1, 2, 3, 4)); assert.equal(result, 4, 'works on an arguments object'); result = _.map([[1, 2, 3], [1, 2, 3]], _.last); assert.deepEqual(result, [3, 3], 'works well with _.map'); - - assert.equal(_.last(null), void 0, 'handles nulls'); - assert.strictEqual(_.last([1, 2, 3], -1).length, 0); + assert.equal(_.last(null), void 0, 'returns undefined when called on null'); }); test('compact', function(assert) { From 276ab6cae0423710fe70108b499e7a1a5b509a8d Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Tue, 24 Nov 2015 15:36:27 -0800 Subject: [PATCH 102/263] Improve assertions for _.compact() Explicitly test all falsy values, and assert it works with map. --- test/arrays.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index 6e5e6b87f..6bc7d3cf5 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -69,9 +69,11 @@ }); test('compact', function(assert) { - assert.equal(_.compact([0, 1, false, 2, false, 3]).length, 3, 'can trim out all falsy values'); - var result = (function(){ return _.compact(arguments).length; }(0, 1, false, 2, false, 3)); - assert.equal(result, 3, 'works on an arguments object'); + assert.deepEqual(_.compact([1, false, null, 0, '', void 0, NaN, 2]), [1, 2], 'removes all falsy values'); + var result = (function(){ return _.compact(arguments); }(0, 1, false, 2, false, 3)); + assert.deepEqual(result, [1, 2, 3], 'works on an arguments object'); + result = _.map([[1, false, false], [false, false, 3]], _.compact); + assert.deepEqual(result, [[1], [3]], 'works well with _.map'); }); test('flatten', function(assert) { From 0380082865ac2ed5136bdb8fb6d8478ee296c68a Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Wed, 25 Nov 2015 16:56:56 -0800 Subject: [PATCH 103/263] Make _.first() assertions consistant with _.last()'s In pull request #2360 @michaelficarra suggested an improvement to the assertion messages for `_.last()`'s two `n <= 0` cases. This change brings `_.first()` assertions in line with this improved format. --- test/arrays.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index d53ec8584..fb1a328b5 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -6,7 +6,8 @@ test('first', function(assert) { assert.equal(_.first([1, 2, 3]), 1, 'can pull out the first element of an array'); assert.equal(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"'); - assert.deepEqual(_.first([1, 2, 3], 0), [], 'can fetch the first 0 elements'); + assert.deepEqual(_.first([1, 2, 3], 0), [], 'returns an empty array when n <= 0 (0 case)'); + assert.deepEqual(_.first([1, 2, 3], -1), [], 'returns an empty array when n <= 0 (negative case)'); assert.deepEqual(_.first([1, 2, 3], 2), [1, 2], 'can fetch the first n elements'); assert.deepEqual(_.first([1, 2, 3], 5), [1, 2, 3], 'returns the whole array if n > length'); var result = (function(){ return _.first(arguments); }(4, 3, 2, 1)); @@ -14,7 +15,6 @@ result = _.map([[1, 2, 3], [1, 2, 3]], _.first); assert.deepEqual(result, [1, 1], 'works well with _.map'); assert.equal(_.first(null), void 0, 'returns undefined when called on null'); - assert.deepEqual(_.first([1, 2, 3], -1), [], 'returns an empty array when asked for -n elements'); }); test('head', function(assert) { From ad304d32000194622d3f2671876e2735f821c829 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sat, 28 Nov 2015 16:10:26 -0800 Subject: [PATCH 104/263] Improve assertion messages for _.without() --- test/arrays.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index d53ec8584..c20db97d3 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -102,13 +102,13 @@ test('without', function(assert) { var list = [1, 2, 1, 0, 3, 1, 4]; - assert.deepEqual(_.without(list, 0, 1), [2, 3, 4], 'can remove all instances of an object'); + assert.deepEqual(_.without(list, 0, 1), [2, 3, 4], 'removes all instances of the given values'); var result = (function(){ return _.without(arguments, 0, 1); }(1, 2, 1, 0, 3, 1, 4)); assert.deepEqual(result, [2, 3, 4], 'works on an arguments object'); list = [{one: 1}, {two: 2}]; - assert.equal(_.without(list, {one: 1}).length, 2, 'uses real object identity for comparisons.'); - assert.equal(_.without(list, list[0]).length, 1, 'ditto.'); + assert.deepEqual(_.without(list, {one: 1}), list, 'compares objects by reference (value case)'); + assert.deepEqual(_.without(list, list[0]), [{two: 2}], 'compares objects by reference (reference case)'); }); test('sortedIndex', function(assert) { From a595fe10aa14f382efcac03a793270ef779c5b37 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Mon, 30 Nov 2015 22:59:42 -0500 Subject: [PATCH 105/263] Negative Ranges Fixes https://github.com/jashkenas/underscore/issues/2364. --- test/arrays.js | 3 ++- underscore.js | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index 41d14e7cf..0e0e87dec 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -529,12 +529,13 @@ assert.deepEqual(_.range(0), [], 'range with 0 as a first argument generates an empty array'); assert.deepEqual(_.range(4), [0, 1, 2, 3], 'range with a single positive argument generates an array of elements 0,1,2,...,n-1'); assert.deepEqual(_.range(5, 8), [5, 6, 7], 'range with two arguments a & b, a<b generates an array of elements a,a+1,a+2,...,b-2,b-1'); - assert.deepEqual(_.range(8, 5), [], 'range with two arguments a & b, b<a generates an empty array'); assert.deepEqual(_.range(3, 10, 3), [3, 6, 9], 'range with three arguments a & b & c, c < b-a, a < b generates an array of elements a,a+c,a+2c,...,b - (multiplier of a) < c'); assert.deepEqual(_.range(3, 10, 15), [3], 'range with three arguments a & b & c, c > b-a, a < b generates an array with a single element, equal to a'); assert.deepEqual(_.range(12, 7, -2), [12, 10, 8], 'range with three arguments a & b & c, a > b, c < 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b'); assert.deepEqual(_.range(0, -10, -1), [0, -1, -2, -3, -4, -5, -6, -7, -8, -9], 'final example in the Python docs'); assert.strictEqual(1 / _.range(-0, 1)[0], -Infinity, 'should preserve -0'); + assert.deepEqual(_.range(8, 5), [8, 7, 6], 'negative range generates descending array'); + assert.deepEqual(_.range(-3), [0, -1, -2], 'negative range generates descending array'); }); test('chunk', function(assert) { diff --git a/underscore.js b/underscore.js index 682cdf647..719473471 100644 --- a/underscore.js +++ b/underscore.js @@ -693,7 +693,9 @@ stop = start || 0; start = 0; } - step = step || 1; + if (!step) { + step = stop < start ? -1 : 1; + } var length = Math.max(Math.ceil((stop - start) / step), 0); var range = Array(length); From ac3e7ff98a9d7f9bb12d5daf17765afc01eb213a Mon Sep 17 00:00:00 2001 From: Henry Wong Date: Sat, 5 Dec 2015 00:58:42 -0800 Subject: [PATCH 106/263] chore(arrays.js): clarify comment Made the comment clear by fixing the grammar. Took @michaelficarra suggestion to make comment change even better. --- test/arrays.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index 0e0e87dec..154764d0f 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -251,8 +251,7 @@ var stooges = _.zip(['moe', 30, 'stooge 1'], ['larry', 40, 'stooge 2'], ['curly', 50, 'stooge 3']); assert.deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], ['stooge 1', 'stooge 2', 'stooge 3']], 'zipped pairs'); - // In the case of difference lengths of the tuples undefineds - // should be used as placeholder + //In the case of different lengths of the tuples, undefined values stooges = _.zip(['moe', 30], ['larry', 40], ['curly', 50, 'extra data']); assert.deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], [void 0, void 0, 'extra data']], 'zipped pairs with empties'); From 188c60c2a9f5b6dd3cacbf64f02c57f0f189070f Mon Sep 17 00:00:00 2001 From: Henry Wong Date: Sat, 5 Dec 2015 15:53:55 -0800 Subject: [PATCH 107/263] chore(arrays.js): clarify comment, added line 255 back. accidentally delete line 255. --- test/arrays.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/arrays.js b/test/arrays.js index 154764d0f..f22dc6184 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -252,6 +252,7 @@ assert.deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], ['stooge 1', 'stooge 2', 'stooge 3']], 'zipped pairs'); //In the case of different lengths of the tuples, undefined values + // should be used as placeholder stooges = _.zip(['moe', 30], ['larry', 40], ['curly', 50, 'extra data']); assert.deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], [void 0, void 0, 'extra data']], 'zipped pairs with empties'); From 5b0d22aeadfd98679588844339a93f05600b5aaa Mon Sep 17 00:00:00 2001 From: Henry Wong Date: Sat, 5 Dec 2015 22:03:34 -0800 Subject: [PATCH 108/263] chore(arrays.js): added space on line 254. Added space on the comment on line 254 so it is consistent with other comments. --- test/arrays.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/arrays.js b/test/arrays.js index f22dc6184..acefbe8c0 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -251,7 +251,7 @@ var stooges = _.zip(['moe', 30, 'stooge 1'], ['larry', 40, 'stooge 2'], ['curly', 50, 'stooge 3']); assert.deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], ['stooge 1', 'stooge 2', 'stooge 3']], 'zipped pairs'); - //In the case of different lengths of the tuples, undefined values + // In the case of different lengths of the tuples, undefined values // should be used as placeholder stooges = _.zip(['moe', 30], ['larry', 40], ['curly', 50, 'extra data']); assert.deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], [void 0, void 0, 'extra data']], 'zipped pairs with empties'); From ed8c4e06f58e8dc34b7e5170e101bc7835a3a28f Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sun, 6 Dec 2015 17:37:55 -0800 Subject: [PATCH 109/263] Clean up comments on cb() and _.iteratee() In commit 44a47fb03de1d9dbf49ea96f265fd3c3f5f5ffd4, `_.iteratee()` was converted into the fully internal function `cb()`. However, the comment was not not updated to reflect its new internal status. --- underscore.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/underscore.js b/underscore.js index 719473471..7eddd5644 100644 --- a/underscore.js +++ b/underscore.js @@ -85,9 +85,9 @@ }; }; - // A mostly-internal function to generate callbacks that can be applied - // to each element in a collection, returning the desired result — either - // `identity`, an arbitrary callback, a property matcher, or a property accessor. + // An internal function to generate callbacks that can be applied to each + // element in a collection, returning the desired result — either `identity`, + // an arbitrary callback, a property matcher, or a property accessor. var cb = function(value, context, argCount) { if (value == null) return _.identity; if (_.isFunction(value)) return optimizeCb(value, context, argCount); @@ -95,6 +95,7 @@ return _.property(value); }; + // An external wrapper for the internal callback generator _.iteratee = function(value, context) { return cb(value, context, Infinity); }; From 58377aec6867d8d09e7703c7110f0ad4e1881d37 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sun, 6 Dec 2015 20:58:30 -0800 Subject: [PATCH 110/263] Cleanup assertions for _.sortedIndex Makes assertion messages more consistent and readable. The rational for the second assertion, "finds the smallest index...", was outlined in pull request #563, but not clearly documented. I've attempted to remedy that with the updated message. --- test/arrays.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index acefbe8c0..d159b37a8 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -114,29 +114,30 @@ }); test('sortedIndex', function(assert) { - var numbers = [10, 20, 30, 40, 50], num = 35; - var indexForNum = _.sortedIndex(numbers, num); - assert.equal(indexForNum, 3, '35 should be inserted at index 3'); - + var numbers = [10, 20, 30, 40, 50]; + var indexFor35 = _.sortedIndex(numbers, 35); + assert.equal(indexFor35, 3, 'finds the index at which a value should be inserted to retain order'); var indexFor30 = _.sortedIndex(numbers, 30); - assert.equal(indexFor30, 2, '30 should be inserted at index 2'); + assert.equal(indexFor30, 2, 'finds the smallest index at which a value could be inserted to retain order'); var objects = [{x: 10}, {x: 20}, {x: 30}, {x: 40}]; var iterator = function(obj){ return obj.x; }; - assert.strictEqual(_.sortedIndex(objects, {x: 25}, iterator), 2); - assert.strictEqual(_.sortedIndex(objects, {x: 35}, 'x'), 3); + assert.strictEqual(_.sortedIndex(objects, {x: 25}, iterator), 2, 'uses the result of `iterator` for order comparisons'); + assert.strictEqual(_.sortedIndex(objects, {x: 35}, 'x'), 3, 'when `iterator` is a string, uses that key for order comparisons'); var context = {1: 2, 2: 3, 3: 4}; iterator = function(obj){ return this[obj]; }; - assert.strictEqual(_.sortedIndex([1, 3], 2, iterator, context), 1); + assert.strictEqual(_.sortedIndex([1, 3], 2, iterator, context), 1, 'can execute its iterator in the given context'); - var values = [0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535, 131071, 262143, 524287, 1048575, 2097151, 4194303, 8388607, 16777215, 33554431, 67108863, 134217727, 268435455, 536870911, 1073741823, 2147483647]; - var array = Array(Math.pow(2, 32) - 1); + var values = [0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535, 131071, 262143, 524287, + 1048575, 2097151, 4194303, 8388607, 16777215, 33554431, 67108863, 134217727, 268435455, 536870911, 1073741823, 2147483647]; + var largeArray = Array(Math.pow(2, 32) - 1); var length = values.length; + // Sparsely populate `array` while (length--) { - array[values[length]] = values[length]; + largeArray[values[length]] = values[length]; } - assert.equal(_.sortedIndex(array, 2147483648), 2147483648, 'should work with large indexes'); + assert.equal(_.sortedIndex(largeArray, 2147483648), 2147483648, 'works with large indexes'); }); test('uniq', function(assert) { From 21f24118267d2383a9069044bda9287fac5dbf49 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Wed, 9 Dec 2015 14:25:58 -0800 Subject: [PATCH 111/263] Improve comment in createReduce() This incomplete comment was introduced in pull request #1991. I believe I have captured the intended intent. --- underscore.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/underscore.js b/underscore.js index 719473471..42f004789 100644 --- a/underscore.js +++ b/underscore.js @@ -187,8 +187,8 @@ // Create a reducing function iterating left or right. var createReduce = function(dir) { - // Optimized iterator function as using arguments.length - // in the main function will deoptimize the, see #1991. + // Wrap code that reassigns argument variables in a separate function than + // the one that accesses `arguments.length` to avoid a perf hit. (#1191) var reducer = function(obj, iteratee, memo, initial) { var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length, From e8c252b205813fbb4070cf87118f6502dc1099be Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Thu, 10 Dec 2015 11:14:02 -0500 Subject: [PATCH 112/263] Rename throttled and debounced cancel function https://github.com/jashkenas/underscore/commit/8a70f347e32650f9ca114b99ee09c61e711ecc08#commitcomment-14893333 --- test/functions.js | 16 ++++++++-------- underscore.js | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/functions.js b/test/functions.js index 0e02b3531..09512feb7 100644 --- a/test/functions.js +++ b/test/functions.js @@ -395,12 +395,12 @@ }, 100); }); - asyncTest('throttle cleared', function(assert) { + asyncTest('throttle cancel', function(assert) { var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 32); throttledIncr(); - throttledIncr.clear(); + throttledIncr.cancel(); throttledIncr(); throttledIncr(); @@ -408,12 +408,12 @@ _.delay(function(){ assert.equal(counter, 3, 'incr was throttled'); start(); }, 64); }); - asyncTest('throttle cleared with leading: false', function(assert) { + asyncTest('throttle cancel with leading: false', function(assert) { var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 32, {leading: false}); throttledIncr(); - throttledIncr.clear(); + throttledIncr.cancel(); assert.equal(counter, 0, 'incr was throttled'); _.delay(function(){ assert.equal(counter, 0, 'incr was throttled'); start(); }, 64); @@ -428,12 +428,12 @@ _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); start(); }, 96); }); - asyncTest('debounce cleared', 1, function(assert) { + asyncTest('debounce cancel', 1, function(assert) { var counter = 0; var incr = function(){ counter++; }; var debouncedIncr = _.debounce(incr, 32); debouncedIncr(); - debouncedIncr.clear(); + debouncedIncr.cancel(); _.delay(function(){ assert.equal(counter, 0, 'incr was not called'); start(); }, 96); }); @@ -453,13 +453,13 @@ _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); start(); }, 128); }); - asyncTest('debounce asap cleared', 4, function(assert) { + asyncTest('debounce asap cancel', 4, function(assert) { var a, b; var counter = 0; var incr = function(){ return ++counter; }; var debouncedIncr = _.debounce(incr, 64, true); a = debouncedIncr(); - debouncedIncr.clear(); + debouncedIncr.cancel(); b = debouncedIncr(); assert.equal(a, 1); assert.equal(b, 2); diff --git a/underscore.js b/underscore.js index a02b58cd8..288886743 100644 --- a/underscore.js +++ b/underscore.js @@ -839,7 +839,7 @@ return result; }; - throttled.clear = function() { + throttled.cancel = function() { clearTimeout(timeout); previous = 0; timeout = context = args = null; @@ -873,7 +873,7 @@ return result; }); - debounced.clear = function() { + debounced.cancel = function() { clearTimeout(timeout); timeout = null; }; From f361f8df8b2c0c640b2339aec3667aca72b3b237 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Thu, 10 Dec 2015 10:43:39 -0800 Subject: [PATCH 113/263] Correct pull requst number in createReduce comment I mistyped the pull request number in my previous commit. --- underscore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/underscore.js b/underscore.js index 288886743..6fdfb3caf 100644 --- a/underscore.js +++ b/underscore.js @@ -189,7 +189,7 @@ // Create a reducing function iterating left or right. var createReduce = function(dir) { // Wrap code that reassigns argument variables in a separate function than - // the one that accesses `arguments.length` to avoid a perf hit. (#1191) + // the one that accesses `arguments.length` to avoid a perf hit. (#1991) var reducer = function(obj, iteratee, memo, initial) { var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length, From 13e93b41b89dc61f95cbfdfe055e9806bc579d3d Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Tue, 8 Dec 2015 20:26:14 -0800 Subject: [PATCH 114/263] Clean up assertions for _.uniq() * Improve assertion grouping * Improve assertion messages * Remove unused properties from test objects * Reuse existing sorted "score" list for sorted tests * Remove clever/confusing usage of _.map for asserting list equality * Simplify test data to be more terse and readable (yes I feel guilty for removing the cute kittens) --- test/arrays.js | 46 +++++++++++++++++----------------------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index d159b37a8..f6b581596 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -143,33 +143,23 @@ test('uniq', function(assert) { var list = [1, 2, 1, 3, 1, 4]; assert.deepEqual(_.uniq(list), [1, 2, 3, 4], 'can find the unique values of an unsorted array'); - list = [1, 1, 1, 2, 2, 3]; assert.deepEqual(_.uniq(list, true), [1, 2, 3], 'can find the unique values of a sorted array faster'); - list = [{name: 'moe'}, {name: 'curly'}, {name: 'larry'}, {name: 'curly'}]; - var iterator = function(value) { return value.name; }; - assert.deepEqual(_.map(_.uniq(list, false, iterator), iterator), ['moe', 'curly', 'larry'], 'can find the unique values of an array using a custom iterator'); - - assert.deepEqual(_.map(_.uniq(list, iterator), iterator), ['moe', 'curly', 'larry'], 'can find the unique values of an array using a custom iterator without specifying whether array is sorted'); - - iterator = function(value) { return value + 1; }; - list = [1, 2, 2, 3, 4, 4]; - assert.deepEqual(_.uniq(list, true, iterator), [1, 2, 3, 4], 'iterator works with sorted array'); - - var kittens = [ - {kitten: 'Celery', cuteness: 8}, - {kitten: 'Juniper', cuteness: 10}, - {kitten: 'Spottis', cuteness: 10} - ]; - - var expected = [ - {kitten: 'Celery', cuteness: 8}, - {kitten: 'Juniper', cuteness: 10} - ]; + list = [{name: 'Moe'}, {name: 'Curly'}, {name: 'Larry'}, {name: 'Curly'}]; + var expected = [{name: 'Moe'}, {name: 'Curly'}, {name: 'Larry'}]; + var iterator = function(stooge) { return stooge.name; }; + assert.deepEqual(_.uniq(list, false, iterator), expected, 'uses the result of `iterator` for uniqueness comparisons (unsorted case)'); + assert.deepEqual(_.uniq(list, iterator), expected, '`sorted` argument defaults to false when omitted'); + assert.deepEqual(_.uniq(list, 'name'), expected, 'when `iterator` is a string, uses that key for comparisons (unsorted case)'); - assert.deepEqual(_.uniq(kittens, true, 'cuteness'), expected, 'string iterator works with sorted array'); + list = [{score: 8}, {score: 10}, {score: 10}]; + expected = [{score: 8}, {score: 10}]; + iterator = function(item) { return item.score; }; + assert.deepEqual(_.uniq(list, true, iterator), expected, 'uses the result of `iterator` for uniqueness comparisons (sorted case)'); + assert.deepEqual(_.uniq(list, true, 'score'), expected, 'when `iterator` is a string, uses that key for comparisons (sorted case)'); + assert.deepEqual(_.uniq([{0: 1}, {0: 1}, {0: 1}, {0: 2}], 0), [{0: 1}, {0: 2}], 'can use falsey pluck like iterator'); var result = (function(){ return _.uniq(arguments); }(1, 2, 1, 3, 1, 4)); assert.deepEqual(result, [1, 2, 3, 4], 'works on an arguments object'); @@ -177,19 +167,17 @@ var a = {}, b = {}, c = {}; assert.deepEqual(_.uniq([a, b, a, b, c]), [a, b, c], 'works on values that can be tested for equivalency but not ordered'); - assert.deepEqual(_.uniq(null), []); + assert.deepEqual(_.uniq(null), [], 'returns an empty array when `array` is not iterable'); var context = {}; list = [3]; _.uniq(list, function(value, index, array) { - assert.strictEqual(this, context); - assert.strictEqual(value, 3); - assert.strictEqual(index, 0); - assert.strictEqual(array, list); + assert.strictEqual(this, context, 'executes its iterator in the given context'); + assert.strictEqual(value, 3, 'passes its iterator the value'); + assert.strictEqual(index, 0, 'passes its iterator the index'); + assert.strictEqual(array, list, 'passes its iterator the entire array'); }, context); - assert.deepEqual(_.uniq([{a: 1, b: 1}, {a: 1, b: 2}, {a: 1, b: 3}, {a: 2, b: 1}], 'a'), [{a: 1, b: 1}, {a: 2, b: 1}], 'can use pluck like iterator'); - assert.deepEqual(_.uniq([{0: 1, b: 1}, {0: 1, b: 2}, {0: 1, b: 3}, {0: 2, b: 1}], 0), [{0: 1, b: 1}, {0: 2, b: 1}], 'can use falsey pluck like iterator'); }); test('unique', function(assert) { From b6ebffbc2a935c9456c83cac804ece79b37bb4f4 Mon Sep 17 00:00:00 2001 From: Craig Martin Date: Fri, 11 Dec 2015 16:02:55 -0500 Subject: [PATCH 115/263] trust npm to set up $PATH --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 26e00c2aa..860a76819 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "coveralls": "nyc npm run test-node && nyc report --reporter=text-lcov | coveralls", "lint": "eslint --reset underscore.js test/*.js", "test-node": "qunit-cli test/*.js", - "test-browser": "npm i karma-phantomjs-launcher && ./node_modules/karma/bin/karma start", + "test-browser": "npm i karma-phantomjs-launcher && karma start", "build": "uglifyjs underscore.js -c \"evaluate=false\" --comments \"/ .*/\" -m --source-map underscore-min.map --source-map-url \" \" -o underscore-min.js", "doc": "docco underscore.js" }, From 8684a63e2a1918ea81eee22f459660e901513c00 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sun, 13 Dec 2015 21:23:08 -0800 Subject: [PATCH 116/263] Don't let eslint ignore global _ in tests As of pull request #2033 (commit 2646f2eaaa3a1b6511051b5e0c461bcaf238e778) we explicitly define `_` in our test files, so we no longer need this exception. --- test/.eslintrc | 1 - 1 file changed, 1 deletion(-) diff --git a/test/.eslintrc b/test/.eslintrc index 052484beb..0fa854fd3 100644 --- a/test/.eslintrc +++ b/test/.eslintrc @@ -3,7 +3,6 @@ "browser": true }, "globals": { - "_": false, "test": false, "ok": false, "is": false, From 6da9a2b6f68707a6239c4395389ad349683003d4 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sun, 13 Dec 2015 22:01:57 -0800 Subject: [PATCH 117/263] Use assert.raises instead of .throws In some environments `.throws()` is a reserved word, so QUnit offers an alias `.raises()`. Using `.raises` seems safer, and also allows us to remove an eslint exception. --- test/.eslintrc | 1 - test/collections.js | 2 +- test/functions.js | 8 ++++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/test/.eslintrc b/test/.eslintrc index 052484beb..e86eb4f6e 100644 --- a/test/.eslintrc +++ b/test/.eslintrc @@ -21,7 +21,6 @@ }, "rules": { "brace-style": 0, - "dot-notation": 1, "no-new-wrappers": 0, "no-sparse-arrays": 0, "no-extend-native": 0 diff --git a/test/collections.js b/test/collections.js index 721f4acb7..864b04f6f 100644 --- a/test/collections.js +++ b/test/collections.js @@ -467,7 +467,7 @@ assert.deepEqual(_.invoke([{a: null}, {}, {a: _.constant(1)}], 'a'), [null, void 0, 1], 'handles null & undefined'); - assert.throws(function() { + assert.raises(function() { _.invoke([{a: 1}], 'a'); }, TypeError, 'throws for non-functions'); }); diff --git a/test/functions.js b/test/functions.js index 09512feb7..ec15c272d 100644 --- a/test/functions.js +++ b/test/functions.js @@ -44,7 +44,7 @@ assert.equal(boundf().hello, 'moe curly', "When called without the new operator, it's OK to be bound to the context"); assert.ok(newBoundf instanceof F, 'a bound instance is an instance of the original function'); - assert.throws(function() { _.bind('notafunction'); }, TypeError, 'throws an error when binding to a non-function'); + assert.raises(function() { _.bind('notafunction'); }, TypeError, 'throws an error when binding to a non-function'); }); test('partial', function(assert) { @@ -109,9 +109,9 @@ sayLast: function() { return this.sayHi(_.last(arguments)); } }; - assert.throws(function() { _.bindAll(moe); }, Error, 'throws an error for bindAll with no functions named'); - assert.throws(function() { _.bindAll(moe, 'sayBye'); }, TypeError, 'throws an error for bindAll if the given key is undefined'); - assert.throws(function() { _.bindAll(moe, 'name'); }, TypeError, 'throws an error for bindAll if the given key is not a function'); + assert.raises(function() { _.bindAll(moe); }, Error, 'throws an error for bindAll with no functions named'); + assert.raises(function() { _.bindAll(moe, 'sayBye'); }, TypeError, 'throws an error for bindAll if the given key is undefined'); + assert.raises(function() { _.bindAll(moe, 'name'); }, TypeError, 'throws an error for bindAll if the given key is not a function'); _.bindAll(moe, 'sayHi', 'sayLast'); curly.sayHi = moe.sayHi; From ca693cf5942ff1292a90f1a620dbaded4b956423 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sun, 13 Dec 2015 23:50:47 -0800 Subject: [PATCH 118/263] Use same version of QUnit in browser as CLI In our `package.json` we list `"qunitjs": "^1.18.0"`, but the version in `tests/vendor` was only 1.17.1. This brings our browser test runner up to date. --- test/vendor/qunit.css | 19 +- test/vendor/qunit.js | 1395 ++++++++++++++++++++++++++++++++++------- 2 files changed, 1189 insertions(+), 225 deletions(-) mode change 100644 => 100755 test/vendor/qunit.css mode change 100644 => 100755 test/vendor/qunit.js diff --git a/test/vendor/qunit.css b/test/vendor/qunit.css old mode 100644 new mode 100755 index 447caa3d5..f1dcd4e1c --- a/test/vendor/qunit.css +++ b/test/vendor/qunit.css @@ -1,12 +1,12 @@ /*! - * QUnit 1.17.1 + * QUnit 1.18.0 * http://qunitjs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * http://jquery.org/license * - * Date: 2015-01-20T19:39Z + * Date: 2015-04-03T10:23Z */ /** Font Family and Sizes */ @@ -116,7 +116,13 @@ #qunit-tests.hidepass li.running, #qunit-tests.hidepass li.pass { - display: none; + visibility: hidden; + position: absolute; + width: 0px; + height: 0px; + padding: 0; + border: 0; + margin: 0; } #qunit-tests li strong { @@ -132,6 +138,11 @@ color: #C2CCD1; text-decoration: none; } + +#qunit-tests li p a { + padding: 0.25em; + color: #6B6464; +} #qunit-tests li a:hover, #qunit-tests li a:focus { color: #000; @@ -277,4 +288,4 @@ left: -10000px; width: 1000px; height: 1000px; -} \ No newline at end of file +} diff --git a/test/vendor/qunit.js b/test/vendor/qunit.js old mode 100644 new mode 100755 index f03c3c58d..f3542ca9d --- a/test/vendor/qunit.js +++ b/test/vendor/qunit.js @@ -1,12 +1,12 @@ /*! - * QUnit 1.17.1 + * QUnit 1.18.0 * http://qunitjs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * http://jquery.org/license * - * Date: 2015-01-20T19:39Z + * Date: 2015-04-03T10:23Z */ (function( window ) { @@ -116,6 +116,9 @@ config = { // when enabled, all tests must call expect() requireExpects: false, + // depth up-to which object will be dumped + maxDepth: 5, + // add checkboxes that are persisted in the query-string // when enabled, the id is set to `true` as a `QUnit.config` property urlConfig: [ @@ -185,11 +188,17 @@ config.modules.push( config.currentModule ); // String search anywhere in moduleName+testName config.filter = urlParams.filter; + if ( urlParams.maxDepth ) { + config.maxDepth = parseInt( urlParams.maxDepth, 10 ) === -1 ? + Number.POSITIVE_INFINITY : + urlParams.maxDepth; + } + config.testId = []; if ( urlParams.testId ) { // Ensure that urlParams.testId is an array - urlParams.testId = [].concat( urlParams.testId ); + urlParams.testId = decodeURIComponent( urlParams.testId ).split( "," ); for ( i = 0; i < urlParams.testId.length; i++ ) { config.testId.push( urlParams.testId[ i ] ); } @@ -197,6 +206,9 @@ config.modules.push( config.currentModule ); // Figure out if we're running the tests from a server or not QUnit.isLocal = location.protocol === "file:"; + + // Expose the current QUnit version + QUnit.version = "1.18.0"; }()); // Root QUnit object. @@ -484,20 +496,14 @@ function done() { }); } -// Doesn't support IE6 to IE9 +// Doesn't support IE6 to IE9, it will return undefined on these browsers // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack function extractStacktrace( e, offset ) { offset = offset === undefined ? 4 : offset; var stack, include, i; - if ( e.stacktrace ) { - - // Opera 12.x - return e.stacktrace.split( "\n" )[ offset + 3 ]; - } else if ( e.stack ) { - - // Firefox, Chrome, Safari 6+, IE10+, PhantomJS and Node + if ( e.stack ) { stack = e.stack.split( "\n" ); if ( /^error$/i.test( stack[ 0 ] ) ) { stack.shift(); @@ -515,9 +521,10 @@ function extractStacktrace( e, offset ) { } } return stack[ offset ]; + + // Support: Safari <=6 only } else if ( e.sourceURL ) { - // Safari < 6 // exclude useless self-reference for generated Error objects if ( /qunit.js$/.test( e.sourceURL ) ) { return; @@ -529,16 +536,19 @@ function extractStacktrace( e, offset ) { } function sourceFromStacktrace( offset ) { - var e = new Error(); - if ( !e.stack ) { + var error = new Error(); + + // Support: Safari <=7 only, IE <=10 - 11 only + // Not all browsers generate the `stack` property for `new Error()`, see also #636 + if ( !error.stack ) { try { - throw e; + throw error; } catch ( err ) { - // This should already be true in most browsers - e = err; + error = err; } } - return extractStacktrace( e, offset ); + + return extractStacktrace( error, offset ); } function synchronize( callback, last ) { @@ -1123,7 +1133,7 @@ Test.prototype = { valid: function() { var include, - filter = config.filter, + filter = config.filter && config.filter.toLowerCase(), module = QUnit.urlParams.module && QUnit.urlParams.module.toLowerCase(), fullName = ( this.module.name + ": " + this.testName ).toLowerCase(); @@ -1146,7 +1156,7 @@ Test.prototype = { include = filter.charAt( 0 ) !== "!"; if ( !include ) { - filter = filter.toLowerCase().slice( 1 ); + filter = filter.slice( 1 ); } // If the filter matches, we need to honour include @@ -1284,87 +1294,52 @@ QUnit.assert = Assert.prototype = { return assert.test.push.apply( assert.test, arguments ); }, - /** - * Asserts rough true-ish result. - * @name ok - * @function - * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); - */ ok: function( result, message ) { message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " + QUnit.dump.parse( result ) ); this.push( !!result, result, true, message ); }, - /** - * Assert that the first two arguments are equal, with an optional message. - * Prints out both actual and expected values. - * @name equal - * @function - * @example equal( format( "{0} bytes.", 2), "2 bytes.", "replaces {0} with next argument" ); - */ + notOk: function( result, message ) { + message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " + + QUnit.dump.parse( result ) ); + this.push( !result, result, false, message ); + }, + equal: function( actual, expected, message ) { /*jshint eqeqeq:false */ this.push( expected == actual, actual, expected, message ); }, - /** - * @name notEqual - * @function - */ notEqual: function( actual, expected, message ) { /*jshint eqeqeq:false */ this.push( expected != actual, actual, expected, message ); }, - /** - * @name propEqual - * @function - */ propEqual: function( actual, expected, message ) { actual = objectValues( actual ); expected = objectValues( expected ); this.push( QUnit.equiv( actual, expected ), actual, expected, message ); }, - /** - * @name notPropEqual - * @function - */ notPropEqual: function( actual, expected, message ) { actual = objectValues( actual ); expected = objectValues( expected ); this.push( !QUnit.equiv( actual, expected ), actual, expected, message ); }, - /** - * @name deepEqual - * @function - */ deepEqual: function( actual, expected, message ) { this.push( QUnit.equiv( actual, expected ), actual, expected, message ); }, - /** - * @name notDeepEqual - * @function - */ notDeepEqual: function( actual, expected, message ) { this.push( !QUnit.equiv( actual, expected ), actual, expected, message ); }, - /** - * @name strictEqual - * @function - */ strictEqual: function( actual, expected, message ) { this.push( expected === actual, actual, expected, message ); }, - /** - * @name notStrictEqual - * @function - */ notStrictEqual: function( actual, expected, message ) { this.push( expected !== actual, actual, expected, message ); }, @@ -1372,7 +1347,8 @@ QUnit.assert = Assert.prototype = { "throws": function( block, expected, message ) { var actual, expectedType, expectedOutput = expected, - ok = false; + ok = false, + currentTest = ( this instanceof Assert && this.test ) || QUnit.config.current; // 'expected' is optional unless doing string comparison if ( message == null && typeof expected === "string" ) { @@ -1380,13 +1356,13 @@ QUnit.assert = Assert.prototype = { expected = null; } - this.test.ignoreGlobalErrors = true; + currentTest.ignoreGlobalErrors = true; try { - block.call( this.test.testEnvironment ); + block.call( currentTest.testEnvironment ); } catch (e) { actual = e; } - this.test.ignoreGlobalErrors = false; + currentTest.ignoreGlobalErrors = false; if ( actual ) { expectedType = QUnit.objectType( expected ); @@ -1419,11 +1395,9 @@ QUnit.assert = Assert.prototype = { expectedOutput = null; ok = true; } - - this.push( ok, actual, expectedOutput, message ); - } else { - this.test.pushFailure( message, null, "No exception was thrown." ); } + + currentTest.assert.push( ok, actual, expectedOutput, message ); } }; @@ -1435,7 +1409,7 @@ QUnit.assert = Assert.prototype = { }()); // Test for equality any JavaScript type. -// Author: Philippe Rathé +// Author: Philippe Rathé QUnit.equiv = (function() { // Call the o related callback with the given arguments. @@ -1783,7 +1757,7 @@ QUnit.dump = (function() { join: join, // depth: 1, - maxDepth: 5, + maxDepth: QUnit.config.maxDepth, // This is the list of parsers, to modify them, use dump.setParser parsers: { @@ -1830,7 +1804,7 @@ QUnit.dump = (function() { nonEnumerableProperties = [ "message", "name" ]; for ( i in nonEnumerableProperties ) { key = nonEnumerableProperties[ i ]; - if ( key in map && !( key in keys ) ) { + if ( key in map && inArray( key, keys ) < 0 ) { keys.push( key ); } } @@ -1949,6 +1923,7 @@ if ( typeof window !== "undefined" ) { "start", "stop", "ok", + "notOk", "equal", "notEqual", "propEqual", @@ -1981,6 +1956,13 @@ if ( typeof exports !== "undefined" && exports ) { exports.QUnit = QUnit; } +if ( typeof define === "function" && define.amd ) { + define( function() { + return QUnit; + } ); + QUnit.config.autostart = false; +} + // Get a reference to the global object, like window in browsers }( (function() { return this; @@ -1989,150 +1971,1088 @@ if ( typeof exports !== "undefined" && exports ) { /*istanbul ignore next */ // jscs:disable maximumLineLength /* - * Javascript Diff Algorithm - * By John Resig (http://ejohn.org/) - * Modified by Chu Alan "sprite" + * This file is a modified version of google-diff-match-patch's JavaScript implementation + * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js), + * modifications are licensed as more fully set forth in LICENSE.txt. + * + * The original source of google-diff-match-patch is attributable and licensed as follows: + * + * Copyright 2006 Google Inc. + * http://code.google.com/p/google-diff-match-patch/ * - * Released under the MIT license. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * More Info: - * http://ejohn.org/projects/javascript-diff-algorithm/ + * https://code.google.com/p/google-diff-match-patch/ * * Usage: QUnit.diff(expected, actual) * - * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick brown fox jumped jumps over" + * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) === "the quick brown fox jumpsed 0; i-- ) { - if ( n[ i ].text != null && n[ i - 1 ].text == null && n[ i ].row > 0 && o[ n[ i ].row - 1 ].text == null && - n[ i - 1 ] == o[ n[ i ].row - 1 ] ) { - - n[ i - 1 ] = { - text: n[ i - 1 ], - row: n[ i ].row - 1 - }; - o[ n[ i ].row - 1 ] = { - text: o[ n[ i ].row - 1 ], - row: i - 1 - }; - } - } - - return { - o: o, - n: n - }; - } - - return function( o, n ) { - o = o.replace( /\s+$/, "" ); - n = n.replace( /\s+$/, "" ); - - var i, pre, - str = "", - out = diff( o === "" ? [] : o.split( /\s+/ ), n === "" ? [] : n.split( /\s+/ ) ), - oSpace = o.match( /\s+/g ), - nSpace = n.match( /\s+/g ); - - if ( oSpace == null ) { - oSpace = [ " " ]; - } else { - oSpace.push( " " ); - } - - if ( nSpace == null ) { - nSpace = [ " " ]; - } else { - nSpace.push( " " ); - } - - if ( out.n.length === 0 ) { - for ( i = 0; i < out.o.length; i++ ) { - str += "" + out.o[ i ] + oSpace[ i ] + ""; - } - } else { - if ( out.n[ 0 ].text == null ) { - for ( n = 0; n < out.o.length && out.o[ n ].text == null; n++ ) { - str += "" + out.o[ n ] + oSpace[ n ] + ""; - } - } - - for ( i = 0; i < out.n.length; i++ ) { - if ( out.n[ i ].text == null ) { - str += "" + out.n[ i ] + nSpace[ i ] + ""; - } else { - - // `pre` initialized at top of scope - pre = ""; - - for ( n = out.n[ i ].row + 1; n < out.o.length && out.o[ n ].text == null; n++ ) { - pre += "" + out.o[ n ] + oSpace[ n ] + ""; - } - str += " " + out.n[ i ].text + nSpace[ i ] + pre; - } - } - } - - return str; - }; + function DiffMatchPatch() { + + // Defaults. + // Redefine these in your program to override the defaults. + + // Number of seconds to map a diff before giving up (0 for infinity). + this.DiffTimeout = 1.0; + // Cost of an empty edit operation in terms of edit characters. + this.DiffEditCost = 4; + } + + // DIFF FUNCTIONS + + /** + * The data structure representing a diff is an array of tuples: + * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] + * which means: delete 'Hello', add 'Goodbye' and keep ' world.' + */ + var DIFF_DELETE = -1, + DIFF_INSERT = 1, + DIFF_EQUAL = 0; + + /** + * Find the differences between two texts. Simplifies the problem by stripping + * any common prefix or suffix off the texts before diffing. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean=} optChecklines Optional speedup flag. If present and false, + * then don't run a line-level diff first to identify the changed areas. + * Defaults to true, which does a faster, slightly less optimal diff. + * @param {number} optDeadline Optional time when the diff should be complete + * by. Used internally for recursive calls. Users should set DiffTimeout + * instead. + * @return {!Array.} Array of diff tuples. + */ + DiffMatchPatch.prototype.DiffMain = function( text1, text2, optChecklines, optDeadline ) { + var deadline, checklines, commonlength, + commonprefix, commonsuffix, diffs; + // Set a deadline by which time the diff must be complete. + if ( typeof optDeadline === "undefined" ) { + if ( this.DiffTimeout <= 0 ) { + optDeadline = Number.MAX_VALUE; + } else { + optDeadline = ( new Date() ).getTime() + this.DiffTimeout * 1000; + } + } + deadline = optDeadline; + + // Check for null inputs. + if ( text1 === null || text2 === null ) { + throw new Error( "Null input. (DiffMain)" ); + } + + // Check for equality (speedup). + if ( text1 === text2 ) { + if ( text1 ) { + return [ + [ DIFF_EQUAL, text1 ] + ]; + } + return []; + } + + if ( typeof optChecklines === "undefined" ) { + optChecklines = true; + } + + checklines = optChecklines; + + // Trim off common prefix (speedup). + commonlength = this.diffCommonPrefix( text1, text2 ); + commonprefix = text1.substring( 0, commonlength ); + text1 = text1.substring( commonlength ); + text2 = text2.substring( commonlength ); + + // Trim off common suffix (speedup). + ///////// + commonlength = this.diffCommonSuffix( text1, text2 ); + commonsuffix = text1.substring( text1.length - commonlength ); + text1 = text1.substring( 0, text1.length - commonlength ); + text2 = text2.substring( 0, text2.length - commonlength ); + + // Compute the diff on the middle block. + diffs = this.diffCompute( text1, text2, checklines, deadline ); + + // Restore the prefix and suffix. + if ( commonprefix ) { + diffs.unshift( [ DIFF_EQUAL, commonprefix ] ); + } + if ( commonsuffix ) { + diffs.push( [ DIFF_EQUAL, commonsuffix ] ); + } + this.diffCleanupMerge( diffs ); + return diffs; + }; + + /** + * Reduce the number of edits by eliminating operationally trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupEfficiency = function( diffs ) { + var changes, equalities, equalitiesLength, lastequality, + pointer, preIns, preDel, postIns, postDel; + changes = false; + equalities = []; // Stack of indices where equalities are found. + equalitiesLength = 0; // Keeping our own length var is faster in JS. + /** @type {?string} */ + lastequality = null; + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + pointer = 0; // Index of current position. + // Is there an insertion operation before the last equality. + preIns = false; + // Is there a deletion operation before the last equality. + preDel = false; + // Is there an insertion operation after the last equality. + postIns = false; + // Is there a deletion operation after the last equality. + postDel = false; + while ( pointer < diffs.length ) { + if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { // Equality found. + if ( diffs[ pointer ][ 1 ].length < this.DiffEditCost && ( postIns || postDel ) ) { + // Candidate found. + equalities[ equalitiesLength++ ] = pointer; + preIns = postIns; + preDel = postDel; + lastequality = diffs[ pointer ][ 1 ]; + } else { + // Not a candidate, and can never become one. + equalitiesLength = 0; + lastequality = null; + } + postIns = postDel = false; + } else { // An insertion or deletion. + if ( diffs[ pointer ][ 0 ] === DIFF_DELETE ) { + postDel = true; + } else { + postIns = true; + } + /* + * Five types to be split: + * ABXYCD + * AXCD + * ABXC + * AXCD + * ABXC + */ + if ( lastequality && ( ( preIns && preDel && postIns && postDel ) || + ( ( lastequality.length < this.DiffEditCost / 2 ) && + ( preIns + preDel + postIns + postDel ) === 3 ) ) ) { + // Duplicate record. + diffs.splice( equalities[equalitiesLength - 1], 0, [ DIFF_DELETE, lastequality ] ); + // Change second copy to insert. + diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT; + equalitiesLength--; // Throw away the equality we just deleted; + lastequality = null; + if (preIns && preDel) { + // No changes made which could affect previous entry, keep going. + postIns = postDel = true; + equalitiesLength = 0; + } else { + equalitiesLength--; // Throw away the previous equality. + pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1; + postIns = postDel = false; + } + changes = true; + } + } + pointer++; + } + + if ( changes ) { + this.diffCleanupMerge( diffs ); + } + }; + + /** + * Convert a diff array into a pretty HTML report. + * @param {!Array.} diffs Array of diff tuples. + * @param {integer} string to be beautified. + * @return {string} HTML representation. + */ + DiffMatchPatch.prototype.diffPrettyHtml = function( diffs ) { + var op, data, x, html = []; + for ( x = 0; x < diffs.length; x++ ) { + op = diffs[x][0]; // Operation (insert, delete, equal) + data = diffs[x][1]; // Text of change. + switch ( op ) { + case DIFF_INSERT: + html[x] = "" + data + ""; + break; + case DIFF_DELETE: + html[x] = "" + data + ""; + break; + case DIFF_EQUAL: + html[x] = "" + data + ""; + break; + } + } + return html.join(""); + }; + + /** + * Determine the common prefix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the start of each + * string. + */ + DiffMatchPatch.prototype.diffCommonPrefix = function( text1, text2 ) { + var pointermid, pointermax, pointermin, pointerstart; + // Quick check for common null cases. + if ( !text1 || !text2 || text1.charAt(0) !== text2.charAt(0) ) { + return 0; + } + // Binary search. + // Performance analysis: http://neil.fraser.name/news/2007/10/09/ + pointermin = 0; + pointermax = Math.min( text1.length, text2.length ); + pointermid = pointermax; + pointerstart = 0; + while ( pointermin < pointermid ) { + if ( text1.substring( pointerstart, pointermid ) === text2.substring( pointerstart, pointermid ) ) { + pointermin = pointermid; + pointerstart = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin ); + } + return pointermid; + }; + + /** + * Determine the common suffix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of each string. + */ + DiffMatchPatch.prototype.diffCommonSuffix = function( text1, text2 ) { + var pointermid, pointermax, pointermin, pointerend; + // Quick check for common null cases. + if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) { + return 0; + } + // Binary search. + // Performance analysis: http://neil.fraser.name/news/2007/10/09/ + pointermin = 0; + pointermax = Math.min(text1.length, text2.length); + pointermid = pointermax; + pointerend = 0; + while ( pointermin < pointermid ) { + if (text1.substring( text1.length - pointermid, text1.length - pointerend ) === + text2.substring( text2.length - pointermid, text2.length - pointerend ) ) { + pointermin = pointermid; + pointerend = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin ); + } + return pointermid; + }; + + /** + * Find the differences between two texts. Assumes that the texts do not + * have any common prefix or suffix. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean} checklines Speedup flag. If false, then don't run a + * line-level diff first to identify the changed areas. + * If true, then run a faster, slightly less optimal diff. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffCompute = function( text1, text2, checklines, deadline ) { + var diffs, longtext, shorttext, i, hm, + text1A, text2A, text1B, text2B, + midCommon, diffsA, diffsB; + + if ( !text1 ) { + // Just add some text (speedup). + return [ + [ DIFF_INSERT, text2 ] + ]; + } + + if (!text2) { + // Just delete some text (speedup). + return [ + [ DIFF_DELETE, text1 ] + ]; + } + + longtext = text1.length > text2.length ? text1 : text2; + shorttext = text1.length > text2.length ? text2 : text1; + i = longtext.indexOf( shorttext ); + if ( i !== -1 ) { + // Shorter text is inside the longer text (speedup). + diffs = [ + [ DIFF_INSERT, longtext.substring( 0, i ) ], + [ DIFF_EQUAL, shorttext ], + [ DIFF_INSERT, longtext.substring( i + shorttext.length ) ] + ]; + // Swap insertions for deletions if diff is reversed. + if ( text1.length > text2.length ) { + diffs[0][0] = diffs[2][0] = DIFF_DELETE; + } + return diffs; + } + + if ( shorttext.length === 1 ) { + // Single character string. + // After the previous speedup, the character can't be an equality. + return [ + [ DIFF_DELETE, text1 ], + [ DIFF_INSERT, text2 ] + ]; + } + + // Check to see if the problem can be split in two. + hm = this.diffHalfMatch(text1, text2); + if (hm) { + // A half-match was found, sort out the return data. + text1A = hm[0]; + text1B = hm[1]; + text2A = hm[2]; + text2B = hm[3]; + midCommon = hm[4]; + // Send both pairs off for separate processing. + diffsA = this.DiffMain(text1A, text2A, checklines, deadline); + diffsB = this.DiffMain(text1B, text2B, checklines, deadline); + // Merge the results. + return diffsA.concat([ + [ DIFF_EQUAL, midCommon ] + ], diffsB); + } + + if (checklines && text1.length > 100 && text2.length > 100) { + return this.diffLineMode(text1, text2, deadline); + } + + return this.diffBisect(text1, text2, deadline); + }; + + /** + * Do the two texts share a substring which is at least half the length of the + * longer text? + * This speedup can produce non-minimal diffs. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {Array.} Five element Array, containing the prefix of + * text1, the suffix of text1, the prefix of text2, the suffix of + * text2 and the common middle. Or null if there was no match. + * @private + */ + DiffMatchPatch.prototype.diffHalfMatch = function(text1, text2) { + var longtext, shorttext, dmp, + text1A, text2B, text2A, text1B, midCommon, + hm1, hm2, hm; + if (this.DiffTimeout <= 0) { + // Don't risk returning a non-optimal diff if we have unlimited time. + return null; + } + longtext = text1.length > text2.length ? text1 : text2; + shorttext = text1.length > text2.length ? text2 : text1; + if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { + return null; // Pointless. + } + dmp = this; // 'this' becomes 'window' in a closure. + + /** + * Does a substring of shorttext exist within longtext such that the substring + * is at least half the length of longtext? + * Closure, but does not reference any external variables. + * @param {string} longtext Longer string. + * @param {string} shorttext Shorter string. + * @param {number} i Start index of quarter length substring within longtext. + * @return {Array.} Five element Array, containing the prefix of + * longtext, the suffix of longtext, the prefix of shorttext, the suffix + * of shorttext and the common middle. Or null if there was no match. + * @private + */ + function diffHalfMatchI(longtext, shorttext, i) { + var seed, j, bestCommon, prefixLength, suffixLength, + bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB; + // Start with a 1/4 length substring at position i as a seed. + seed = longtext.substring(i, i + Math.floor(longtext.length / 4)); + j = -1; + bestCommon = ""; + while ((j = shorttext.indexOf(seed, j + 1)) !== -1) { + prefixLength = dmp.diffCommonPrefix(longtext.substring(i), + shorttext.substring(j)); + suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), + shorttext.substring(0, j)); + if (bestCommon.length < suffixLength + prefixLength) { + bestCommon = shorttext.substring(j - suffixLength, j) + + shorttext.substring(j, j + prefixLength); + bestLongtextA = longtext.substring(0, i - suffixLength); + bestLongtextB = longtext.substring(i + prefixLength); + bestShorttextA = shorttext.substring(0, j - suffixLength); + bestShorttextB = shorttext.substring(j + prefixLength); + } + } + if (bestCommon.length * 2 >= longtext.length) { + return [ bestLongtextA, bestLongtextB, + bestShorttextA, bestShorttextB, bestCommon + ]; + } else { + return null; + } + } + + // First check if the second quarter is the seed for a half-match. + hm1 = diffHalfMatchI(longtext, shorttext, + Math.ceil(longtext.length / 4)); + // Check again based on the third quarter. + hm2 = diffHalfMatchI(longtext, shorttext, + Math.ceil(longtext.length / 2)); + if (!hm1 && !hm2) { + return null; + } else if (!hm2) { + hm = hm1; + } else if (!hm1) { + hm = hm2; + } else { + // Both matched. Select the longest. + hm = hm1[4].length > hm2[4].length ? hm1 : hm2; + } + + // A half-match was found, sort out the return data. + text1A, text1B, text2A, text2B; + if (text1.length > text2.length) { + text1A = hm[0]; + text1B = hm[1]; + text2A = hm[2]; + text2B = hm[3]; + } else { + text2A = hm[0]; + text2B = hm[1]; + text1A = hm[2]; + text1B = hm[3]; + } + midCommon = hm[4]; + return [ text1A, text1B, text2A, text2B, midCommon ]; + }; + + /** + * Do a quick line-level diff on both strings, then rediff the parts for + * greater accuracy. + * This speedup can produce non-minimal diffs. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffLineMode = function(text1, text2, deadline) { + var a, diffs, linearray, pointer, countInsert, + countDelete, textInsert, textDelete, j; + // Scan the text on a line-by-line basis first. + a = this.diffLinesToChars(text1, text2); + text1 = a.chars1; + text2 = a.chars2; + linearray = a.lineArray; + + diffs = this.DiffMain(text1, text2, false, deadline); + + // Convert the diff back to original text. + this.diffCharsToLines(diffs, linearray); + // Eliminate freak matches (e.g. blank lines) + this.diffCleanupSemantic(diffs); + + // Rediff any replacement blocks, this time character-by-character. + // Add a dummy entry at the end. + diffs.push( [ DIFF_EQUAL, "" ] ); + pointer = 0; + countDelete = 0; + countInsert = 0; + textDelete = ""; + textInsert = ""; + while (pointer < diffs.length) { + switch ( diffs[pointer][0] ) { + case DIFF_INSERT: + countInsert++; + textInsert += diffs[pointer][1]; + break; + case DIFF_DELETE: + countDelete++; + textDelete += diffs[pointer][1]; + break; + case DIFF_EQUAL: + // Upon reaching an equality, check for prior redundancies. + if (countDelete >= 1 && countInsert >= 1) { + // Delete the offending records and add the merged ones. + diffs.splice(pointer - countDelete - countInsert, + countDelete + countInsert); + pointer = pointer - countDelete - countInsert; + a = this.DiffMain(textDelete, textInsert, false, deadline); + for (j = a.length - 1; j >= 0; j--) { + diffs.splice( pointer, 0, a[j] ); + } + pointer = pointer + a.length; + } + countInsert = 0; + countDelete = 0; + textDelete = ""; + textInsert = ""; + break; + } + pointer++; + } + diffs.pop(); // Remove the dummy entry at the end. + + return diffs; + }; + + /** + * Find the 'middle snake' of a diff, split the problem in two + * and return the recursively constructed diff. + * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffBisect = function(text1, text2, deadline) { + var text1Length, text2Length, maxD, vOffset, vLength, + v1, v2, x, delta, front, k1start, k1end, k2start, + k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2; + // Cache the text lengths to prevent multiple calls. + text1Length = text1.length; + text2Length = text2.length; + maxD = Math.ceil((text1Length + text2Length) / 2); + vOffset = maxD; + vLength = 2 * maxD; + v1 = new Array(vLength); + v2 = new Array(vLength); + // Setting all elements to -1 is faster in Chrome & Firefox than mixing + // integers and undefined. + for (x = 0; x < vLength; x++) { + v1[x] = -1; + v2[x] = -1; + } + v1[vOffset + 1] = 0; + v2[vOffset + 1] = 0; + delta = text1Length - text2Length; + // If the total number of characters is odd, then the front path will collide + // with the reverse path. + front = (delta % 2 !== 0); + // Offsets for start and end of k loop. + // Prevents mapping of space beyond the grid. + k1start = 0; + k1end = 0; + k2start = 0; + k2end = 0; + for (d = 0; d < maxD; d++) { + // Bail out if deadline is reached. + if ((new Date()).getTime() > deadline) { + break; + } + + // Walk the front path one step. + for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { + k1Offset = vOffset + k1; + if ( k1 === -d || ( k1 !== d && v1[ k1Offset - 1 ] < v1[ k1Offset + 1 ] ) ) { + x1 = v1[k1Offset + 1]; + } else { + x1 = v1[k1Offset - 1] + 1; + } + y1 = x1 - k1; + while (x1 < text1Length && y1 < text2Length && + text1.charAt(x1) === text2.charAt(y1)) { + x1++; + y1++; + } + v1[k1Offset] = x1; + if (x1 > text1Length) { + // Ran off the right of the graph. + k1end += 2; + } else if (y1 > text2Length) { + // Ran off the bottom of the graph. + k1start += 2; + } else if (front) { + k2Offset = vOffset + delta - k1; + if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) { + // Mirror x2 onto top-left coordinate system. + x2 = text1Length - v2[k2Offset]; + if (x1 >= x2) { + // Overlap detected. + return this.diffBisectSplit(text1, text2, x1, y1, deadline); + } + } + } + } + + // Walk the reverse path one step. + for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { + k2Offset = vOffset + k2; + if ( k2 === -d || (k2 !== d && v2[ k2Offset - 1 ] < v2[ k2Offset + 1 ] ) ) { + x2 = v2[k2Offset + 1]; + } else { + x2 = v2[k2Offset - 1] + 1; + } + y2 = x2 - k2; + while (x2 < text1Length && y2 < text2Length && + text1.charAt(text1Length - x2 - 1) === + text2.charAt(text2Length - y2 - 1)) { + x2++; + y2++; + } + v2[k2Offset] = x2; + if (x2 > text1Length) { + // Ran off the left of the graph. + k2end += 2; + } else if (y2 > text2Length) { + // Ran off the top of the graph. + k2start += 2; + } else if (!front) { + k1Offset = vOffset + delta - k2; + if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) { + x1 = v1[k1Offset]; + y1 = vOffset + x1 - k1Offset; + // Mirror x2 onto top-left coordinate system. + x2 = text1Length - x2; + if (x1 >= x2) { + // Overlap detected. + return this.diffBisectSplit(text1, text2, x1, y1, deadline); + } + } + } + } + } + // Diff took too long and hit the deadline or + // number of diffs equals number of characters, no commonality at all. + return [ + [ DIFF_DELETE, text1 ], + [ DIFF_INSERT, text2 ] + ]; + }; + + /** + * Given the location of the 'middle snake', split the diff in two parts + * and recurse. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} x Index of split point in text1. + * @param {number} y Index of split point in text2. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffBisectSplit = function( text1, text2, x, y, deadline ) { + var text1a, text1b, text2a, text2b, diffs, diffsb; + text1a = text1.substring(0, x); + text2a = text2.substring(0, y); + text1b = text1.substring(x); + text2b = text2.substring(y); + + // Compute both diffs serially. + diffs = this.DiffMain(text1a, text2a, false, deadline); + diffsb = this.DiffMain(text1b, text2b, false, deadline); + + return diffs.concat(diffsb); + }; + + /** + * Reduce the number of edits by eliminating semantically trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupSemantic = function(diffs) { + var changes, equalities, equalitiesLength, lastequality, + pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, + lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2; + changes = false; + equalities = []; // Stack of indices where equalities are found. + equalitiesLength = 0; // Keeping our own length var is faster in JS. + /** @type {?string} */ + lastequality = null; + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + pointer = 0; // Index of current position. + // Number of characters that changed prior to the equality. + lengthInsertions1 = 0; + lengthDeletions1 = 0; + // Number of characters that changed after the equality. + lengthInsertions2 = 0; + lengthDeletions2 = 0; + while (pointer < diffs.length) { + if (diffs[pointer][0] === DIFF_EQUAL) { // Equality found. + equalities[equalitiesLength++] = pointer; + lengthInsertions1 = lengthInsertions2; + lengthDeletions1 = lengthDeletions2; + lengthInsertions2 = 0; + lengthDeletions2 = 0; + lastequality = diffs[pointer][1]; + } else { // An insertion or deletion. + if (diffs[pointer][0] === DIFF_INSERT) { + lengthInsertions2 += diffs[pointer][1].length; + } else { + lengthDeletions2 += diffs[pointer][1].length; + } + // Eliminate an equality that is smaller or equal to the edits on both + // sides of it. + if (lastequality && (lastequality.length <= + Math.max(lengthInsertions1, lengthDeletions1)) && + (lastequality.length <= Math.max(lengthInsertions2, + lengthDeletions2))) { + // Duplicate record. + diffs.splice( equalities[ equalitiesLength - 1 ], 0, [ DIFF_DELETE, lastequality ] ); + // Change second copy to insert. + diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; + // Throw away the equality we just deleted. + equalitiesLength--; + // Throw away the previous equality (it needs to be reevaluated). + equalitiesLength--; + pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; + lengthInsertions1 = 0; // Reset the counters. + lengthDeletions1 = 0; + lengthInsertions2 = 0; + lengthDeletions2 = 0; + lastequality = null; + changes = true; + } + } + pointer++; + } + + // Normalize the diff. + if (changes) { + this.diffCleanupMerge(diffs); + } + + // Find any overlaps between deletions and insertions. + // e.g: abcxxxxxxdef + // -> abcxxxdef + // e.g: xxxabcdefxxx + // -> defxxxabc + // Only extract an overlap if it is as big as the edit ahead or behind it. + pointer = 1; + while (pointer < diffs.length) { + if (diffs[pointer - 1][0] === DIFF_DELETE && + diffs[pointer][0] === DIFF_INSERT) { + deletion = diffs[pointer - 1][1]; + insertion = diffs[pointer][1]; + overlapLength1 = this.diffCommonOverlap(deletion, insertion); + overlapLength2 = this.diffCommonOverlap(insertion, deletion); + if (overlapLength1 >= overlapLength2) { + if (overlapLength1 >= deletion.length / 2 || + overlapLength1 >= insertion.length / 2) { + // Overlap found. Insert an equality and trim the surrounding edits. + diffs.splice( pointer, 0, [ DIFF_EQUAL, insertion.substring( 0, overlapLength1 ) ] ); + diffs[pointer - 1][1] = + deletion.substring(0, deletion.length - overlapLength1); + diffs[pointer + 1][1] = insertion.substring(overlapLength1); + pointer++; + } + } else { + if (overlapLength2 >= deletion.length / 2 || + overlapLength2 >= insertion.length / 2) { + // Reverse overlap found. + // Insert an equality and swap and trim the surrounding edits. + diffs.splice( pointer, 0, [ DIFF_EQUAL, deletion.substring( 0, overlapLength2 ) ] ); + diffs[pointer - 1][0] = DIFF_INSERT; + diffs[pointer - 1][1] = + insertion.substring(0, insertion.length - overlapLength2); + diffs[pointer + 1][0] = DIFF_DELETE; + diffs[pointer + 1][1] = + deletion.substring(overlapLength2); + pointer++; + } + } + pointer++; + } + pointer++; + } + }; + + /** + * Determine if the suffix of one string is the prefix of another. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of the first + * string and the start of the second string. + * @private + */ + DiffMatchPatch.prototype.diffCommonOverlap = function(text1, text2) { + var text1Length, text2Length, textLength, + best, length, pattern, found; + // Cache the text lengths to prevent multiple calls. + text1Length = text1.length; + text2Length = text2.length; + // Eliminate the null case. + if (text1Length === 0 || text2Length === 0) { + return 0; + } + // Truncate the longer string. + if (text1Length > text2Length) { + text1 = text1.substring(text1Length - text2Length); + } else if (text1Length < text2Length) { + text2 = text2.substring(0, text1Length); + } + textLength = Math.min(text1Length, text2Length); + // Quick check for the worst case. + if (text1 === text2) { + return textLength; + } + + // Start by looking for a single character match + // and increase length until no match is found. + // Performance analysis: http://neil.fraser.name/news/2010/11/04/ + best = 0; + length = 1; + while (true) { + pattern = text1.substring(textLength - length); + found = text2.indexOf(pattern); + if (found === -1) { + return best; + } + length += found; + if (found === 0 || text1.substring(textLength - length) === + text2.substring(0, length)) { + best = length; + length++; + } + } + }; + + /** + * Split two texts into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {{chars1: string, chars2: string, lineArray: !Array.}} + * An object containing the encoded text1, the encoded text2 and + * the array of unique strings. + * The zeroth element of the array of unique strings is intentionally blank. + * @private + */ + DiffMatchPatch.prototype.diffLinesToChars = function(text1, text2) { + var lineArray, lineHash, chars1, chars2; + lineArray = []; // e.g. lineArray[4] === 'Hello\n' + lineHash = {}; // e.g. lineHash['Hello\n'] === 4 + + // '\x00' is a valid character, but various debuggers don't like it. + // So we'll insert a junk entry to avoid generating a null character. + lineArray[0] = ""; + + /** + * Split a text into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * Modifies linearray and linehash through being a closure. + * @param {string} text String to encode. + * @return {string} Encoded string. + * @private + */ + function diffLinesToCharsMunge(text) { + var chars, lineStart, lineEnd, lineArrayLength, line; + chars = ""; + // Walk the text, pulling out a substring for each line. + // text.split('\n') would would temporarily double our memory footprint. + // Modifying text would create many large strings to garbage collect. + lineStart = 0; + lineEnd = -1; + // Keeping our own length variable is faster than looking it up. + lineArrayLength = lineArray.length; + while (lineEnd < text.length - 1) { + lineEnd = text.indexOf("\n", lineStart); + if (lineEnd === -1) { + lineEnd = text.length - 1; + } + line = text.substring(lineStart, lineEnd + 1); + lineStart = lineEnd + 1; + + if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : + (lineHash[line] !== undefined)) { + chars += String.fromCharCode( lineHash[ line ] ); + } else { + chars += String.fromCharCode(lineArrayLength); + lineHash[line] = lineArrayLength; + lineArray[lineArrayLength++] = line; + } + } + return chars; + } + + chars1 = diffLinesToCharsMunge(text1); + chars2 = diffLinesToCharsMunge(text2); + return { + chars1: chars1, + chars2: chars2, + lineArray: lineArray + }; + }; + + /** + * Rehydrate the text in a diff from a string of line hashes to real lines of + * text. + * @param {!Array.} diffs Array of diff tuples. + * @param {!Array.} lineArray Array of unique strings. + * @private + */ + DiffMatchPatch.prototype.diffCharsToLines = function( diffs, lineArray ) { + var x, chars, text, y; + for ( x = 0; x < diffs.length; x++ ) { + chars = diffs[x][1]; + text = []; + for ( y = 0; y < chars.length; y++ ) { + text[y] = lineArray[chars.charCodeAt(y)]; + } + diffs[x][1] = text.join(""); + } + }; + + /** + * Reorder and merge like edit sections. Merge equalities. + * Any edit section can move as long as it doesn't cross an equality. + * @param {!Array.} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupMerge = function(diffs) { + var pointer, countDelete, countInsert, textInsert, textDelete, + commonlength, changes; + diffs.push( [ DIFF_EQUAL, "" ] ); // Add a dummy entry at the end. + pointer = 0; + countDelete = 0; + countInsert = 0; + textDelete = ""; + textInsert = ""; + commonlength; + while (pointer < diffs.length) { + switch ( diffs[ pointer ][ 0 ] ) { + case DIFF_INSERT: + countInsert++; + textInsert += diffs[pointer][1]; + pointer++; + break; + case DIFF_DELETE: + countDelete++; + textDelete += diffs[pointer][1]; + pointer++; + break; + case DIFF_EQUAL: + // Upon reaching an equality, check for prior redundancies. + if (countDelete + countInsert > 1) { + if (countDelete !== 0 && countInsert !== 0) { + // Factor out any common prefixies. + commonlength = this.diffCommonPrefix(textInsert, textDelete); + if (commonlength !== 0) { + if ((pointer - countDelete - countInsert) > 0 && + diffs[pointer - countDelete - countInsert - 1][0] === + DIFF_EQUAL) { + diffs[pointer - countDelete - countInsert - 1][1] += + textInsert.substring(0, commonlength); + } else { + diffs.splice( 0, 0, [ DIFF_EQUAL, + textInsert.substring( 0, commonlength ) + ] ); + pointer++; + } + textInsert = textInsert.substring(commonlength); + textDelete = textDelete.substring(commonlength); + } + // Factor out any common suffixies. + commonlength = this.diffCommonSuffix(textInsert, textDelete); + if (commonlength !== 0) { + diffs[pointer][1] = textInsert.substring(textInsert.length - + commonlength) + diffs[pointer][1]; + textInsert = textInsert.substring(0, textInsert.length - + commonlength); + textDelete = textDelete.substring(0, textDelete.length - + commonlength); + } + } + // Delete the offending records and add the merged ones. + if (countDelete === 0) { + diffs.splice( pointer - countInsert, + countDelete + countInsert, [ DIFF_INSERT, textInsert ] ); + } else if (countInsert === 0) { + diffs.splice( pointer - countDelete, + countDelete + countInsert, [ DIFF_DELETE, textDelete ] ); + } else { + diffs.splice( pointer - countDelete - countInsert, + countDelete + countInsert, [ DIFF_DELETE, textDelete ], [ DIFF_INSERT, textInsert ] ); + } + pointer = pointer - countDelete - countInsert + + (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1; + } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) { + // Merge this equality with the previous one. + diffs[pointer - 1][1] += diffs[pointer][1]; + diffs.splice(pointer, 1); + } else { + pointer++; + } + countInsert = 0; + countDelete = 0; + textDelete = ""; + textInsert = ""; + break; + } + } + if (diffs[diffs.length - 1][1] === "") { + diffs.pop(); // Remove the dummy entry at the end. + } + + // Second pass: look for single edits surrounded on both sides by equalities + // which can be shifted sideways to eliminate an equality. + // e.g: ABAC -> ABAC + changes = false; + pointer = 1; + // Intentionally ignore the first and last element (don't need checking). + while (pointer < diffs.length - 1) { + if (diffs[pointer - 1][0] === DIFF_EQUAL && + diffs[pointer + 1][0] === DIFF_EQUAL) { + // This is a single edit surrounded by equalities. + if ( diffs[ pointer ][ 1 ].substring( diffs[ pointer ][ 1 ].length - + diffs[ pointer - 1 ][ 1 ].length ) === diffs[ pointer - 1 ][ 1 ] ) { + // Shift the edit over the previous equality. + diffs[pointer][1] = diffs[pointer - 1][1] + + diffs[pointer][1].substring(0, diffs[pointer][1].length - + diffs[pointer - 1][1].length); + diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; + diffs.splice(pointer - 1, 1); + changes = true; + } else if ( diffs[ pointer ][ 1 ].substring( 0, diffs[ pointer + 1 ][ 1 ].length ) === + diffs[ pointer + 1 ][ 1 ] ) { + // Shift the edit over the next equality. + diffs[pointer - 1][1] += diffs[pointer + 1][1]; + diffs[pointer][1] = + diffs[pointer][1].substring(diffs[pointer + 1][1].length) + + diffs[pointer + 1][1]; + diffs.splice(pointer + 1, 1); + changes = true; + } + } + pointer++; + } + // If shifts were made, the diff needs reordering and another shift sweep. + if (changes) { + this.diffCleanupMerge(diffs); + } + }; + + return function(o, n) { + var diff, output, text; + diff = new DiffMatchPatch(); + output = diff.DiffMain(o, n); + //console.log(output); + diff.diffCleanupEfficiency(output); + text = diff.diffPrettyHtml(output); + + return text; + }; }()); // jscs:enable @@ -2256,7 +3176,14 @@ function addEvent( elem, type, fn ) { } else if ( elem.attachEvent ) { // support: IE <9 - elem.attachEvent( "on" + type, fn ); + elem.attachEvent( "on" + type, function() { + var event = window.event; + if ( !event.target ) { + event.target = event.srcElement || document; + } + + fn.call( elem, event ); + }); } } @@ -2427,12 +3354,16 @@ function setUrl( params ) { } function applyUrlParams() { - var selectBox = id( "qunit-modulefilter" ), - selection = decodeURIComponent( selectBox.options[ selectBox.selectedIndex ].value ), + var selectedModule, + modulesList = id( "qunit-modulefilter" ), filter = id( "qunit-filter-input" ).value; + selectedModule = modulesList ? + decodeURIComponent( modulesList.options[ modulesList.selectedIndex ].value ) : + undefined; + window.location = setUrl({ - module: ( selection === "" ) ? undefined : selection, + module: ( selectedModule === "" ) ? undefined : selectedModule, filter: ( filter === "" ) ? undefined : filter, // Remove testId filter @@ -2588,9 +3519,14 @@ function storeFixture() { function appendUserAgent() { var userAgent = id( "qunit-userAgent" ); + if ( userAgent ) { userAgent.innerHTML = ""; - userAgent.appendChild( document.createTextNode( navigator.userAgent ) ); + userAgent.appendChild( + document.createTextNode( + "QUnit " + QUnit.version + "; " + navigator.userAgent + ) + ); } } @@ -2696,7 +3632,7 @@ QUnit.done(function( details ) { if ( config.altertitle && defined.document && document.title ) { - // show ✖ for good, ✔ for bad suite result in title + // show ✖ for good, ✔ for bad suite result in title // use escape sequences in case file gets loaded with non-utf-8-charset document.title = [ ( details.failed ? "\u2716" : "\u2714" ), @@ -2733,7 +3669,7 @@ function getNameHtml( name, module ) { } QUnit.testStart(function( details ) { - var running, testBlock; + var running, testBlock, bad; testBlock = id( "qunit-test-output-" + details.testId ); if ( testBlock ) { @@ -2746,7 +3682,13 @@ QUnit.testStart(function( details ) { running = id( "qunit-testresult" ); if ( running ) { - running.innerHTML = "Running:
      " + getNameHtml( details.name, details.module ); + bad = QUnit.config.reorder && defined.sessionStorage && + +sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name ); + + running.innerHTML = ( bad ? + "Rerunning previously failed test:
      " : + "Running:
      " ) + + getNameHtml( details.name, details.module ); } }); @@ -2779,6 +3721,15 @@ QUnit.log(function( details ) { actual + "

      " + "Diff:
      " +
       				QUnit.diff( expected, actual ) + "
      "; + } else { + if ( expected.indexOf( "[object Array]" ) !== -1 || + expected.indexOf( "[object Object]" ) !== -1 ) { + message += "Message: " + + "Diff suppressed as the depth of object is more than current max depth (" + + QUnit.config.maxDepth + ").

      Hint: Use QUnit.dump.maxDepth to " + + " run with a higher max depth or " + + "Rerun without max depth.

      "; + } } if ( details.source ) { @@ -2863,13 +3814,15 @@ QUnit.testDone(function( details ) { } }); -if ( !defined.document || document.readyState === "complete" ) { +if ( defined.document ) { + if ( document.readyState === "complete" ) { + QUnit.load(); + } else { + addEvent( window, "load", QUnit.load ); + } +} else { config.pageLoaded = true; config.autorun = true; } -if ( defined.document ) { - addEvent( window, "load", QUnit.load ); -} - -})(); \ No newline at end of file +})(); From fce4a78514830f9f2f4d666f9a252b4415db954d Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sun, 13 Dec 2015 20:35:30 -0800 Subject: [PATCH 119/263] Replace global test() with QUnit.test Helps avoid implicit globals, and starts us on the path toward compatibility with QUnit 2.0. --- test/.eslintrc | 1 - test/arrays.js | 60 ++++++++++++++-------------- test/chaining.js | 18 ++++----- test/collections.js | 88 +++++++++++++++++++++--------------------- test/cross-document.js | 36 ++++++++--------- test/functions.js | 26 ++++++------- test/objects.js | 80 +++++++++++++++++++------------------- test/utility.js | 62 ++++++++++++++--------------- 8 files changed, 185 insertions(+), 186 deletions(-) diff --git a/test/.eslintrc b/test/.eslintrc index 8ced41115..b7af19c2e 100644 --- a/test/.eslintrc +++ b/test/.eslintrc @@ -3,7 +3,6 @@ "browser": true }, "globals": { - "test": false, "ok": false, "is": false, "equal": false, diff --git a/test/arrays.js b/test/arrays.js index d159b37a8..953116a40 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -3,7 +3,7 @@ QUnit.module('Arrays'); - test('first', function(assert) { + QUnit.test('first', function(assert) { assert.equal(_.first([1, 2, 3]), 1, 'can pull out the first element of an array'); assert.equal(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"'); assert.deepEqual(_.first([1, 2, 3], 0), [], 'returns an empty array when n <= 0 (0 case)'); @@ -17,15 +17,15 @@ assert.equal(_.first(null), void 0, 'returns undefined when called on null'); }); - test('head', function(assert) { + QUnit.test('head', function(assert) { assert.strictEqual(_.head, _.first, 'is an alias for first'); }); - test('take', function(assert) { + QUnit.test('take', function(assert) { assert.strictEqual(_.take, _.first, 'is an alias for first'); }); - test('rest', function(assert) { + QUnit.test('rest', function(assert) { var numbers = [1, 2, 3, 4]; assert.deepEqual(_.rest(numbers), [2, 3, 4], 'fetches all but the first element'); assert.deepEqual(_.rest(numbers, 0), [1, 2, 3, 4], 'returns the whole array when index is 0'); @@ -36,15 +36,15 @@ assert.deepEqual(_.flatten(result), [2, 3, 2, 3], 'works well with _.map'); }); - test('tail', function(assert) { + QUnit.test('tail', function(assert) { assert.strictEqual(_.tail, _.rest, 'is an alias for rest'); }); - test('drop', function(assert) { + QUnit.test('drop', function(assert) { assert.strictEqual(_.drop, _.rest, 'is an alias for rest'); }); - test('initial', function(assert) { + QUnit.test('initial', function(assert) { assert.deepEqual(_.initial([1, 2, 3, 4, 5]), [1, 2, 3, 4], 'returns all but the last element'); assert.deepEqual(_.initial([1, 2, 3, 4], 2), [1, 2], 'returns all but the last n elements'); assert.deepEqual(_.initial([1, 2, 3, 4], 6), [], 'returns an empty array when n > length'); @@ -54,7 +54,7 @@ assert.deepEqual(_.flatten(result), [1, 2, 1, 2], 'works well with _.map'); }); - test('last', function(assert) { + QUnit.test('last', function(assert) { assert.equal(_.last([1, 2, 3]), 3, 'can pull out the last element of an array'); assert.equal(_([1, 2, 3]).last(), 3, 'can perform OO-style "last()"'); assert.deepEqual(_.last([1, 2, 3], 0), [], 'returns an empty array when n <= 0 (0 case)'); @@ -68,7 +68,7 @@ assert.equal(_.last(null), void 0, 'returns undefined when called on null'); }); - test('compact', function(assert) { + QUnit.test('compact', function(assert) { assert.deepEqual(_.compact([1, false, null, 0, '', void 0, NaN, 2]), [1, 2], 'removes all falsy values'); var result = (function(){ return _.compact(arguments); }(0, 1, false, 2, false, 3)); assert.deepEqual(result, [1, 2, 3], 'works on an arguments object'); @@ -76,7 +76,7 @@ assert.deepEqual(result, [[1], [3]], 'works well with _.map'); }); - test('flatten', function(assert) { + QUnit.test('flatten', function(assert) { assert.deepEqual(_.flatten(null), [], 'supports null'); assert.deepEqual(_.flatten(void 0), [], 'supports undefined'); @@ -102,7 +102,7 @@ assert.deepEqual(_.flatten(x, true), x[0], 'can handle very deep arrays in shallow mode'); }); - test('without', function(assert) { + QUnit.test('without', function(assert) { var list = [1, 2, 1, 0, 3, 1, 4]; assert.deepEqual(_.without(list, 0, 1), [2, 3, 4], 'removes all instances of the given values'); var result = (function(){ return _.without(arguments, 0, 1); }(1, 2, 1, 0, 3, 1, 4)); @@ -113,7 +113,7 @@ assert.deepEqual(_.without(list, list[0]), [{two: 2}], 'compares objects by reference (reference case)'); }); - test('sortedIndex', function(assert) { + QUnit.test('sortedIndex', function(assert) { var numbers = [10, 20, 30, 40, 50]; var indexFor35 = _.sortedIndex(numbers, 35); assert.equal(indexFor35, 3, 'finds the index at which a value should be inserted to retain order'); @@ -140,7 +140,7 @@ assert.equal(_.sortedIndex(largeArray, 2147483648), 2147483648, 'works with large indexes'); }); - test('uniq', function(assert) { + QUnit.test('uniq', function(assert) { var list = [1, 2, 1, 3, 1, 4]; assert.deepEqual(_.uniq(list), [1, 2, 3, 4], 'can find the unique values of an unsorted array'); @@ -192,11 +192,11 @@ assert.deepEqual(_.uniq([{0: 1, b: 1}, {0: 1, b: 2}, {0: 1, b: 3}, {0: 2, b: 1}], 0), [{0: 1, b: 1}, {0: 2, b: 1}], 'can use falsey pluck like iterator'); }); - test('unique', function(assert) { + QUnit.test('unique', function(assert) { assert.strictEqual(_.unique, _.uniq, 'is an alias for uniq'); }); - test('intersection', function(assert) { + QUnit.test('intersection', function(assert) { var stooges = ['moe', 'curly', 'larry'], leaders = ['moe', 'groucho']; assert.deepEqual(_.intersection(stooges, leaders), ['moe'], 'can take the set intersection of two arrays'); assert.deepEqual(_(stooges).intersection(leaders), ['moe'], 'can perform an OO-style intersection'); @@ -214,7 +214,7 @@ assert.equal(result.length, 0, 'returns an empty array when passed null as argument beyond the first'); }); - test('union', function(assert) { + QUnit.test('union', function(assert) { var result = _.union([1, 2, 3], [2, 30, 1], [1, 40]); assert.deepEqual(result, [1, 2, 3, 30, 40], 'takes the union of a list of arrays'); @@ -230,7 +230,7 @@ assert.deepEqual(result, [1, 2, 3], 'restrict the union to arrays only'); }); - test('difference', function(assert) { + QUnit.test('difference', function(assert) { var result = _.difference([1, 2, 3], [2, 30, 40]); assert.deepEqual(result, [1, 3], 'takes the difference of two arrays'); @@ -241,7 +241,7 @@ assert.deepEqual(result, [1, 2, 3], 'restrict the difference to arrays only'); }); - test('zip', function(assert) { + QUnit.test('zip', function(assert) { var names = ['moe', 'larry', 'curly'], ages = [30, 40, 50], leaders = [true]; assert.deepEqual(_.zip(names, ages, leaders), [ ['moe', 30, true], @@ -264,7 +264,7 @@ assert.deepEqual(_.zip(), [], '_.zip() returns []'); }); - test('unzip', function(assert) { + QUnit.test('unzip', function(assert) { assert.deepEqual(_.unzip(null), [], 'handles null'); assert.deepEqual(_.unzip([['a', 'b'], [1, 2]]), [['a', 1], ['b', 2]]); @@ -277,7 +277,7 @@ assert.deepEqual(_.unzip(zipped), [['moe', 30, void 0], ['larry', 40, void 0], ['curly', 50, 'extra data']], 'Uses length of largest array'); }); - test('object', function(assert) { + QUnit.test('object', function(assert) { var result = _.object(['moe', 'larry', 'curly'], [30, 40, 50]); var shouldBe = {moe: 30, larry: 40, curly: 50}; assert.deepEqual(result, shouldBe, 'two arrays zipped together into an object'); @@ -292,7 +292,7 @@ assert.deepEqual(_.object(null), {}, 'handles nulls'); }); - test('indexOf', function(assert) { + QUnit.test('indexOf', function(assert) { var numbers = [1, 2, 3]; assert.equal(_.indexOf(numbers, 2), 1, 'can compute indexOf'); var result = (function(){ return _.indexOf(arguments, 2); }(1, 2, 3)); @@ -343,7 +343,7 @@ assert.equal(index, -1, 'empty array with truthy `isSorted` returns -1'); }); - test('indexOf with NaN', function(assert) { + QUnit.test('indexOf with NaN', function(assert) { assert.strictEqual(_.indexOf([1, 2, NaN, NaN], NaN), 2, 'Expected [1, 2, NaN] to contain NaN'); assert.strictEqual(_.indexOf([1, 2, Infinity], NaN), -1, 'Expected [1, 2, NaN] to contain NaN'); @@ -355,14 +355,14 @@ }(1, 2, NaN, NaN)); }); - test('indexOf with +- 0', function(assert) { + QUnit.test('indexOf with +- 0', function(assert) { _.each([-0, +0], function(val) { assert.strictEqual(_.indexOf([1, 2, val, val], val), 2); assert.strictEqual(_.indexOf([1, 2, val, val], -val), 2); }); }); - test('lastIndexOf', function(assert) { + QUnit.test('lastIndexOf', function(assert) { var numbers = [1, 0, 1]; var falsey = [void 0, '', 0, false, NaN, null, void 0]; assert.equal(_.lastIndexOf(numbers, 1), 2); @@ -420,7 +420,7 @@ }), [0, -1, -1]); }); - test('lastIndexOf with NaN', function(assert) { + QUnit.test('lastIndexOf with NaN', function(assert) { assert.strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN), 3, 'Expected [1, 2, NaN] to contain NaN'); assert.strictEqual(_.lastIndexOf([1, 2, Infinity], NaN), -1, 'Expected [1, 2, NaN] to contain NaN'); @@ -432,7 +432,7 @@ }(1, 2, NaN, NaN)); }); - test('lastIndexOf with +- 0', function(assert) { + QUnit.test('lastIndexOf with +- 0', function(assert) { _.each([-0, +0], function(val) { assert.strictEqual(_.lastIndexOf([1, 2, val, val], val), 3); assert.strictEqual(_.lastIndexOf([1, 2, val, val], -val), 3); @@ -440,7 +440,7 @@ }); }); - test('findIndex', function(assert) { + QUnit.test('findIndex', function(assert) { var objects = [ {a: 0, b: 0}, {a: 1, b: 1}, @@ -483,7 +483,7 @@ assert.strictEqual(_.findIndex(array, function(x) { return x === 55; }), -1, 'doesn\'t match array-likes keys'); }); - test('findLastIndex', function(assert) { + QUnit.test('findLastIndex', function(assert) { var objects = [ {a: 0, b: 0}, {a: 1, b: 1}, @@ -526,7 +526,7 @@ assert.strictEqual(_.findLastIndex(array, function(x) { return x === 55; }), -1, 'doesn\'t match array-likes keys'); }); - test('range', function(assert) { + QUnit.test('range', function(assert) { assert.deepEqual(_.range(0), [], 'range with 0 as a first argument generates an empty array'); assert.deepEqual(_.range(4), [0, 1, 2, 3], 'range with a single positive argument generates an array of elements 0,1,2,...,n-1'); assert.deepEqual(_.range(5, 8), [5, 6, 7], 'range with two arguments a & b, a<b generates an array of elements a,a+1,a+2,...,b-2,b-1'); @@ -539,7 +539,7 @@ assert.deepEqual(_.range(-3), [0, -1, -2], 'negative range generates descending array'); }); - test('chunk', function(assert) { + QUnit.test('chunk', function(assert) { assert.deepEqual(_.chunk([], 2), [], 'chunk for empty array returns an empty array'); assert.deepEqual(_.chunk([1, 2, 3], 0), [], 'chunk into parts of 0 elements returns empty array'); diff --git a/test/chaining.js b/test/chaining.js index 1e708b90b..6ad21dcf6 100644 --- a/test/chaining.js +++ b/test/chaining.js @@ -3,7 +3,7 @@ QUnit.module('Chaining'); - test('map/flatten/reduce', function(assert) { + QUnit.test('map/flatten/reduce', function(assert) { var lyrics = [ 'I\'m a lumberjack and I\'m okay', 'I sleep all night and I work all day', @@ -23,7 +23,7 @@ assert.equal(counts.e, 10, 'counted all the letters in the song'); }); - test('select/reject/sortBy', function(assert) { + QUnit.test('select/reject/sortBy', function(assert) { var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; numbers = _(numbers).chain().select(function(n) { return n % 2 === 0; @@ -35,7 +35,7 @@ assert.deepEqual(numbers, [10, 6, 2], 'filtered and reversed the numbers'); }); - test('select/reject/sortBy in functional style', function(assert) { + QUnit.test('select/reject/sortBy in functional style', function(assert) { var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; numbers = _.chain(numbers).select(function(n) { return n % 2 === 0; @@ -47,7 +47,7 @@ assert.deepEqual(numbers, [10, 6, 2], 'filtered and reversed the numbers'); }); - test('reverse/concat/unshift/pop/map', function(assert) { + QUnit.test('reverse/concat/unshift/pop/map', function(assert) { var numbers = [1, 2, 3, 4, 5]; numbers = _(numbers).chain() .reverse() @@ -59,7 +59,7 @@ assert.deepEqual(numbers, [34, 10, 8, 6, 4, 2, 10, 10], 'can chain together array functions.'); }); - test('splice', function(assert) { + QUnit.test('splice', function(assert) { var instance = _([1, 2, 3, 4, 5]).chain(); assert.deepEqual(instance.splice(1, 3).value(), [1, 5]); assert.deepEqual(instance.splice(1, 0).value(), [1, 5]); @@ -67,27 +67,27 @@ assert.deepEqual(instance.splice(0, 1).value(), [], '#397 Can create empty array'); }); - test('shift', function(assert) { + QUnit.test('shift', function(assert) { var instance = _([1, 2, 3]).chain(); assert.deepEqual(instance.shift().value(), [2, 3]); assert.deepEqual(instance.shift().value(), [3]); assert.deepEqual(instance.shift().value(), [], '#397 Can create empty array'); }); - test('pop', function(assert) { + QUnit.test('pop', function(assert) { var instance = _([1, 2, 3]).chain(); assert.deepEqual(instance.pop().value(), [1, 2]); assert.deepEqual(instance.pop().value(), [1]); assert.deepEqual(instance.pop().value(), [], '#397 Can create empty array'); }); - test('chaining works in small stages', function(assert) { + QUnit.test('chaining works in small stages', function(assert) { var o = _([1, 2, 3, 4]).chain(); assert.deepEqual(o.filter(function(i) { return i < 3; }).value(), [1, 2]); assert.deepEqual(o.filter(function(i) { return i > 2; }).value(), [3, 4]); }); - test('#1562: Engine proxies for chained functions', function(assert) { + QUnit.test('#1562: Engine proxies for chained functions', function(assert) { var wrapped = _(512); assert.strictEqual(wrapped.toJSON(), 512); assert.strictEqual(wrapped.valueOf(), 512); diff --git a/test/collections.js b/test/collections.js index 864b04f6f..1bc14790e 100644 --- a/test/collections.js +++ b/test/collections.js @@ -3,7 +3,7 @@ QUnit.module('Collections'); - test('each', function(assert) { + QUnit.test('each', function(assert) { _.each([1, 2, 3], function(num, i) { assert.equal(num, i + 1, 'each iterators provide value and iteration count'); }); @@ -45,11 +45,11 @@ assert.strictEqual(_.each(null, function(){}), null); }); - test('forEach', function(assert) { + QUnit.test('forEach', function(assert) { assert.strictEqual(_.forEach, _.each, 'is an alias for each'); }); - test('lookupIterator with contexts', function(assert) { + QUnit.test('lookupIterator with contexts', function(assert) { _.each([true, false, 'yes', '', 0, 1, {}], function(context) { _.each([1], function() { assert.equal(this, context); @@ -57,7 +57,7 @@ }); }); - test('Iterating objects with sketchy length properties', function(assert) { + QUnit.test('Iterating objects with sketchy length properties', function(assert) { var functions = [ 'each', 'map', 'filter', 'find', 'some', 'every', 'max', 'min', @@ -101,7 +101,7 @@ }); }); - test('Resistant to collection length and properties changing while iterating', function(assert) { + QUnit.test('Resistant to collection length and properties changing while iterating', function(assert) { var collection = [ 'each', 'map', 'filter', 'find', @@ -145,7 +145,7 @@ }); }); - test('map', function(assert) { + QUnit.test('map', function(assert) { var doubled = _.map([1, 2, 3], function(num){ return num * 2; }); assert.deepEqual(doubled, [2, 4, 6], 'doubled numbers'); @@ -171,11 +171,11 @@ assert.deepEqual(_.map(people, 'name'), ['moe', 'curly'], 'predicate string map to object properties'); }); - test('collect', function(assert) { + QUnit.test('collect', function(assert) { assert.strictEqual(_.collect, _.map, 'is an alias for map'); }); - test('reduce', function(assert) { + QUnit.test('reduce', function(assert) { var sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0); assert.equal(sum, 6, 'can sum up an array'); @@ -198,15 +198,15 @@ assert.equal(_.reduce([], _.noop), void 0, 'returns undefined when collection is empty and no initial value'); }); - test('foldl', function(assert) { + QUnit.test('foldl', function(assert) { assert.strictEqual(_.foldl, _.reduce, 'is an alias for reduce'); }); - test('inject', function(assert) { + QUnit.test('inject', function(assert) { assert.strictEqual(_.inject, _.reduce, 'is an alias for reduce'); }); - test('reduceRight', function(assert) { + QUnit.test('reduceRight', function(assert) { var list = _.reduceRight(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; }, ''); assert.equal(list, 'bazbarfoo', 'can perform right folds'); @@ -256,11 +256,11 @@ assert.deepEqual(args, expected); }); - test('foldr', function(assert) { + QUnit.test('foldr', function(assert) { assert.strictEqual(_.foldr, _.reduceRight, 'is an alias for reduceRight'); }); - test('find', function(assert) { + QUnit.test('find', function(assert) { var array = [1, 2, 3, 4]; assert.strictEqual(_.find(array, function(n) { return n > 2; }), 3, 'should return first found `value`'); assert.strictEqual(_.find(array, function() { return false; }), void 0, 'should return `undefined` if `value` is not found'); @@ -298,11 +298,11 @@ }, _); }); - test('detect', function(assert) { + QUnit.test('detect', function(assert) { assert.strictEqual(_.detect, _.find, 'is an alias for find'); }); - test('filter', function(assert) { + QUnit.test('filter', function(assert) { var evenArray = [1, 2, 3, 4, 5, 6]; var evenObject = {one: 1, two: 2, three: 3}; var isEven = function(num){ return num % 2 === 0; }; @@ -323,11 +323,11 @@ assert.deepEqual(_(list).filter({}), list, 'OO-filter'); }); - test('select', function(assert) { + QUnit.test('select', function(assert) { assert.strictEqual(_.select, _.filter, 'is an alias for filter'); }); - test('reject', function(assert) { + QUnit.test('reject', function(assert) { var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 === 0; }); assert.deepEqual(odds, [1, 3, 5], 'rejected each even number'); @@ -349,7 +349,7 @@ assert.deepEqual(_.reject(list, []), [], 'Returns empty list given empty array'); }); - test('every', function(assert) { + QUnit.test('every', function(assert) { assert.ok(_.every([], _.identity), 'the empty set'); assert.ok(_.every([true, true, true], _.identity), 'every true values'); assert.ok(!_.every([true, false, true], _.identity), 'one false value'); @@ -373,11 +373,11 @@ assert.ok(!_.every(['a', 'b', 'c', 'd', 'f'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); }); - test('all', function(assert) { + QUnit.test('all', function(assert) { assert.strictEqual(_.all, _.every, 'is an alias for every'); }); - test('some', function(assert) { + QUnit.test('some', function(assert) { assert.ok(!_.some([]), 'the empty set'); assert.ok(!_.some([false, false, false]), 'all false values'); assert.ok(_.some([false, false, true]), 'one true value'); @@ -403,11 +403,11 @@ assert.ok(!_.some(['x', 'y', 'z'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); }); - test('any', function(assert) { + QUnit.test('any', function(assert) { assert.strictEqual(_.any, _.some, 'is an alias for some'); }); - test('includes', function(assert) { + QUnit.test('includes', function(assert) { _.each([null, void 0, 0, 1, NaN, {}, []], function(val) { assert.strictEqual(_.includes(val, 'hasOwnProperty'), false); }); @@ -430,21 +430,21 @@ assert.ok(_.every([1, 2, 3], _.partial(_.includes, numbers)), 'fromIndex is guarded'); }); - test('include', function(assert) { + QUnit.test('include', function(assert) { assert.strictEqual(_.include, _.includes, 'is an alias for includes'); }); - test('contains', function(assert) { + QUnit.test('contains', function(assert) { assert.strictEqual(_.contains, _.includes, 'is an alias for includes'); }); - test('includes with NaN', function(assert) { + QUnit.test('includes with NaN', function(assert) { assert.strictEqual(_.includes([1, 2, NaN, NaN], NaN), true, 'Expected [1, 2, NaN] to contain NaN'); assert.strictEqual(_.includes([1, 2, Infinity], NaN), false, 'Expected [1, 2, NaN] to contain NaN'); }); - test('includes with +- 0', function(assert) { + QUnit.test('includes with +- 0', function(assert) { _.each([-0, +0], function(val) { assert.strictEqual(_.includes([1, 2, val, val], val), true); assert.strictEqual(_.includes([1, 2, val, val], -val), true); @@ -453,7 +453,7 @@ }); - test('invoke', 5, function(assert) { + QUnit.test('invoke', 5, function(assert) { var list = [[5, 1, 7], [3, 2, 1]]; var result = _.invoke(list, 'sort'); assert.deepEqual(result[0], [1, 5, 7], 'first array sorted'); @@ -472,7 +472,7 @@ }, TypeError, 'throws for non-functions'); }); - test('invoke w/ function reference', function(assert) { + QUnit.test('invoke w/ function reference', function(assert) { var list = [[5, 1, 7], [3, 2, 1]]; var result = _.invoke(list, Array.prototype.sort); assert.deepEqual(result[0], [1, 5, 7], 'first array sorted'); @@ -484,7 +484,7 @@ }); // Relevant when using ClojureScript - test('invoke when strings have a call method', function(assert) { + QUnit.test('invoke when strings have a call method', function(assert) { String.prototype.call = function() { return 42; }; @@ -498,7 +498,7 @@ assert.equal(s.call, void 0, 'call function removed'); }); - test('pluck', function(assert) { + QUnit.test('pluck', function(assert) { var people = [{name: 'moe', age: 30}, {name: 'curly', age: 50}]; assert.deepEqual(_.pluck(people, 'name'), ['moe', 'curly'], 'pulls names out of objects'); assert.deepEqual(_.pluck(people, 'address'), [void 0, void 0], 'missing properties are returned as undefined'); @@ -506,7 +506,7 @@ assert.deepEqual(_.pluck([{'[object Object]': 1}], {}), [1]); }); - test('where', function(assert) { + QUnit.test('where', function(assert) { var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; var result = _.where(list, {a: 1}); assert.equal(result.length, 3); @@ -522,7 +522,7 @@ assert.deepEqual(_.where([_, {a: 1, b: 2}, _], test), [_, _], 'checks properties given function'); }); - test('findWhere', function(assert) { + QUnit.test('findWhere', function(assert) { var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}, {a: 2, b: 4}]; var result = _.findWhere(list, {a: 1}); assert.deepEqual(result, {a: 1, b: 2}); @@ -547,7 +547,7 @@ assert.deepEqual(_.findWhere([{y: 5, b: 6}, expect], new TestClass()), expect, 'uses class instance properties'); }); - test('max', function(assert) { + QUnit.test('max', function(assert) { assert.equal(-Infinity, _.max(null), 'can handle null/undefined'); assert.equal(-Infinity, _.max(void 0), 'can handle null/undefined'); assert.equal(-Infinity, _.max(null, _.identity), 'can handle null/undefined'); @@ -592,7 +592,7 @@ assert.deepEqual(_.max([{0: 1}, {0: 2}, {0: -1}, {a: 1}], 0), {0: 2}, 'Lookup falsy iterator'); }); - test('min', function(assert) { + QUnit.test('min', function(assert) { assert.equal(Infinity, _.min(null), 'can handle null/undefined'); assert.equal(Infinity, _.min(void 0), 'can handle null/undefined'); assert.equal(Infinity, _.min(null, _.identity), 'can handle null/undefined'); @@ -635,7 +635,7 @@ assert.deepEqual(_.min([{0: 1}, {0: 2}, {0: -1}, {a: 1}], 0), {0: -1}, 'Lookup falsy iterator'); }); - test('sortBy', function(assert) { + QUnit.test('sortBy', function(assert) { var people = [{name: 'curly', age: 50}, {name: 'moe', age: 30}]; people = _.sortBy(people, function(person){ return person.age; }); assert.deepEqual(_.pluck(people, 'name'), ['moe', 'curly'], 'stooges sorted by age'); @@ -683,7 +683,7 @@ assert.deepEqual(_.sortBy(list), ['e', 'q', 'r', 't', 'w', 'y'], 'uses _.identity if iterator is not specified'); }); - test('groupBy', function(assert) { + QUnit.test('groupBy', function(assert) { var parity = _.groupBy([1, 2, 3, 4, 5, 6], function(num){ return num % 2; }); assert.ok('0' in parity && '1' in parity, 'created a group for each value'); assert.deepEqual(parity[0], [2, 4, 6], 'put each even number in the right group'); @@ -720,7 +720,7 @@ assert.deepEqual(_.groupBy(matrix, 1), {2: [[1, 2]], 3: [[1, 3], [2, 3]]}); }); - test('indexBy', function(assert) { + QUnit.test('indexBy', function(assert) { var parity = _.indexBy([1, 2, 3, 4, 5], function(num){ return num % 2 === 0; }); assert.equal(parity['true'], 4); assert.equal(parity['false'], 5); @@ -738,7 +738,7 @@ assert.equal(grouped['3'], 3); }); - test('countBy', function(assert) { + QUnit.test('countBy', function(assert) { var parity = _.countBy([1, 2, 3, 4, 5], function(num){ return num % 2 === 0; }); assert.equal(parity['true'], 2); assert.equal(parity['false'], 3); @@ -767,7 +767,7 @@ assert.equal(grouped['3'], 1); }); - test('shuffle', function(assert) { + QUnit.test('shuffle', function(assert) { assert.deepEqual(_.shuffle([1]), [1], 'behaves correctly on size 1 arrays'); var numbers = _.range(20); var shuffled = _.shuffle(numbers); @@ -780,7 +780,7 @@ assert.deepEqual(shuffled.sort(), [1, 2, 3, 4], 'works on objects'); }); - test('sample', function(assert) { + QUnit.test('sample', function(assert) { assert.strictEqual(_.sample([1]), 1, 'behaves correctly when no second parameter is given'); assert.deepEqual(_.sample([1, 2, 3], -2), [], 'behaves correctly on negative n'); var numbers = _.range(10); @@ -799,7 +799,7 @@ assert.notDeepEqual(partialSampleSorted, _.range(10), 'samples from the whole array, not just the beginning'); }); - test('toArray', function(assert) { + QUnit.test('toArray', function(assert) { assert.ok(!_.isArray(arguments), 'arguments object is not an array'); assert.ok(_.isArray(_.toArray(arguments)), 'arguments object converted into array'); var a = [1, 2, 3]; @@ -825,7 +825,7 @@ } }); - test('size', function(assert) { + QUnit.test('size', function(assert) { assert.equal(_.size({one: 1, two: 2, three: 3}), 3, 'can compute the size of an object'); assert.equal(_.size([1, 2, 3]), 3, 'can compute the size of an array'); assert.equal(_.size({length: 3, 0: 0, 1: 0, 2: 0}), 3, 'can compute the size of Array-likes'); @@ -843,7 +843,7 @@ assert.equal(_.size(0), 0, 'handles numbers'); }); - test('partition', function(assert) { + QUnit.test('partition', function(assert) { var list = [0, 1, 2, 3, 4, 5]; assert.deepEqual(_.partition(list, function(x) { return x < 4; }), [[0, 1, 2, 3], [4, 5]], 'handles bool return values'); assert.deepEqual(_.partition(list, function(x) { return x & 1; }), [[1, 3, 5], [0, 2, 4]], 'handles 0 and 1 return values'); @@ -875,7 +875,7 @@ }); if (typeof document != 'undefined') { - test('Can use various collection methods on NodeLists', function(assert) { + QUnit.test('Can use various collection methods on NodeLists', function(assert) { var parent = document.createElement('div'); parent.innerHTML = 'textnode'; diff --git a/test/cross-document.js b/test/cross-document.js index fff3a07d4..cb68a3d9b 100644 --- a/test/cross-document.js +++ b/test/cross-document.js @@ -33,7 +33,7 @@ ); iDoc.close(); - test('isEqual', function(assert) { + QUnit.test('isEqual', function(assert) { assert.ok(!_.isEqual(iNumber, 101)); assert.ok(_.isEqual(iNumber, 100)); @@ -45,73 +45,73 @@ assert.ok(_.isEqual([1, 2, 3], iArray), 'Arrays with equivalent elements created in different documents are equal'); }); - test('isEmpty', function(assert) { + QUnit.test('isEmpty', function(assert) { assert.ok(!_([iNumber]).isEmpty(), '[1] is not empty'); assert.ok(!_.isEmpty(iArray), '[] is empty'); assert.ok(_.isEmpty(iObject), '{} is empty'); }); - test('isElement', function(assert) { + QUnit.test('isElement', function(assert) { assert.ok(!_.isElement('div'), 'strings are not dom elements'); assert.ok(_.isElement(document.body), 'the body tag is a DOM element'); assert.ok(_.isElement(iElement), 'even from another frame'); }); - test('isArguments', function(assert) { + QUnit.test('isArguments', function(assert) { assert.ok(_.isArguments(iArguments), 'even from another frame'); }); - test('isObject', function(assert) { + QUnit.test('isObject', function(assert) { assert.ok(_.isObject(iElement), 'even from another frame'); assert.ok(_.isObject(iFunction), 'even from another frame'); }); - test('isArray', function(assert) { + QUnit.test('isArray', function(assert) { assert.ok(_.isArray(iArray), 'even from another frame'); }); - test('isString', function(assert) { + QUnit.test('isString', function(assert) { assert.ok(_.isString(iString), 'even from another frame'); }); - test('isNumber', function(assert) { + QUnit.test('isNumber', function(assert) { assert.ok(_.isNumber(iNumber), 'even from another frame'); }); - test('isBoolean', function(assert) { + QUnit.test('isBoolean', function(assert) { assert.ok(_.isBoolean(iBoolean), 'even from another frame'); }); - test('isFunction', function(assert) { + QUnit.test('isFunction', function(assert) { assert.ok(_.isFunction(iFunction), 'even from another frame'); }); - test('isDate', function(assert) { + QUnit.test('isDate', function(assert) { assert.ok(_.isDate(iDate), 'even from another frame'); }); - test('isRegExp', function(assert) { + QUnit.test('isRegExp', function(assert) { assert.ok(_.isRegExp(iRegExp), 'even from another frame'); }); - test('isNaN', function(assert) { + QUnit.test('isNaN', function(assert) { assert.ok(_.isNaN(iNaN), 'even from another frame'); }); - test('isNull', function(assert) { + QUnit.test('isNull', function(assert) { assert.ok(_.isNull(iNull), 'even from another frame'); }); - test('isUndefined', function(assert) { + QUnit.test('isUndefined', function(assert) { assert.ok(_.isUndefined(iUndefined), 'even from another frame'); }); - test('isError', function(assert) { + QUnit.test('isError', function(assert) { assert.ok(_.isError(iError), 'even from another frame'); }); if (typeof ActiveXObject != 'undefined') { - test('IE host objects', function(assert) { + QUnit.test('IE host objects', function(assert) { var xml = new ActiveXObject('Msxml2.DOMDocument.3.0'); assert.ok(!_.isNumber(xml)); assert.ok(!_.isBoolean(xml)); @@ -121,7 +121,7 @@ assert.ok(!_.isUndefined(xml)); }); - test('#1621 IE 11 compat mode DOM elements are not functions', function(assert) { + QUnit.test('#1621 IE 11 compat mode DOM elements are not functions', function(assert) { var fn = function() {}; var xml = new ActiveXObject('Msxml2.DOMDocument.3.0'); var div = document.createElement('div'); diff --git a/test/functions.js b/test/functions.js index ec15c272d..c35a17395 100644 --- a/test/functions.js +++ b/test/functions.js @@ -4,7 +4,7 @@ QUnit.module('Functions'); QUnit.config.asyncRetries = 3; - test('bind', function(assert) { + QUnit.test('bind', function(assert) { var context = {name: 'moe'}; var func = function(arg) { return 'name: ' + (this.name || arg); }; var bound = _.bind(func, context); @@ -47,7 +47,7 @@ assert.raises(function() { _.bind('notafunction'); }, TypeError, 'throws an error when binding to a non-function'); }); - test('partial', function(assert) { + QUnit.test('partial', function(assert) { var obj = {name: 'moe'}; var func = function() { return this.name + ' ' + _.toArray(arguments).join(' '); }; @@ -89,7 +89,7 @@ _.partial.placeholder = _; }); - test('bindAll', function(assert) { + QUnit.test('bindAll', function(assert) { var curly = {name: 'curly'}, moe = { name: 'moe', getName: function() { return 'name: ' + this.name; }, @@ -125,7 +125,7 @@ assert.equal(getName(), 'name: moe', 'flattens arguments into a single list'); }); - test('memoize', function(assert) { + QUnit.test('memoize', function(assert) { var fib = function(n) { return n < 2 ? n : fib(n - 1) + fib(n - 2); }; @@ -525,7 +525,7 @@ }, 100); }); - test('once', function(assert) { + QUnit.test('once', function(assert) { var num = 0; var increment = _.once(function(){ return ++num; }); increment(); @@ -535,7 +535,7 @@ assert.equal(increment(), 1, 'stores a memo to the last value'); }); - test('Recursive onced function.', 1, function(assert) { + QUnit.test('Recursive onced function.', 1, function(assert) { var f = _.once(function(){ assert.ok(true); f(); @@ -543,7 +543,7 @@ f(); }); - test('wrap', function(assert) { + QUnit.test('wrap', function(assert) { var greet = function(name){ return 'hi: ' + name; }; var backwards = _.wrap(greet, function(func, name){ return func(name) + ' ' + name.split('').reverse().join(''); }); assert.equal(backwards('moe'), 'hi: moe eom', 'wrapped the salutation function'); @@ -559,13 +559,13 @@ assert.deepEqual(ret, [noop, ['whats', 'your'], 'vector', 'victor']); }); - test('negate', function(assert) { + QUnit.test('negate', function(assert) { var isOdd = function(n){ return n & 1; }; assert.equal(_.negate(isOdd)(2), true, 'should return the complement of the given function'); assert.equal(_.negate(isOdd)(3), false, 'should return the complement of the given function'); }); - test('compose', function(assert) { + QUnit.test('compose', function(assert) { var greet = function(name){ return 'hi: ' + name; }; var exclaim = function(sentence){ return sentence + '!'; }; var composed = _.compose(exclaim, greet); @@ -591,7 +591,7 @@ assert.equal(composed(1, 2, 3), 12); }); - test('after', function(assert) { + QUnit.test('after', function(assert) { var testAfter = function(afterAmount, timesCalled) { var afterCalled = 0; var after = _.after(afterAmount, function() { @@ -607,7 +607,7 @@ assert.equal(testAfter(0, 1), 1, 'after(0) should fire when first invoked'); }); - test('before', function(assert) { + QUnit.test('before', function(assert) { var testBefore = function(beforeAmount, timesCalled) { var beforeCalled = 0; var before = _.before(beforeAmount, function() { beforeCalled++; }); @@ -627,7 +627,7 @@ assert.equal(context.num, 2, 'provides context'); }); - test('iteratee', function(assert) { + QUnit.test('iteratee', function(assert) { var identity = _.iteratee(); assert.equal(identity, _.identity, '_.iteratee is exposed as an external function.'); @@ -642,7 +642,7 @@ }); - test('restArgs', 10, function(assert) { + QUnit.test('restArgs', 10, function(assert) { _.restArgs(function(a, args) { assert.strictEqual(a, 1); assert.deepEqual(args, [2, 3], 'collects rest arguments into an array'); diff --git a/test/objects.js b/test/objects.js index bc3075839..fff8ea962 100644 --- a/test/objects.js +++ b/test/objects.js @@ -5,7 +5,7 @@ var testElement = typeof document === 'object' ? document.createElement('div') : void 0; - test('keys', function(assert) { + QUnit.test('keys', function(assert) { assert.deepEqual(_.keys({one: 1, two: 2}), ['one', 'two'], 'can extract the keys from an object'); // the test above is not safe because it relies on for-in enumeration order var a = []; a[1] = 0; @@ -35,7 +35,7 @@ assert.deepEqual(_.keys(trouble).sort(), troubleKeys, 'matches non-enumerable properties'); }); - test('allKeys', function(assert) { + QUnit.test('allKeys', function(assert) { assert.deepEqual(_.allKeys({one: 1, two: 2}), ['one', 'two'], 'can extract the allKeys from an object'); // the test above is not safe because it relies on for-in enumeration order var a = []; a[1] = 0; @@ -73,17 +73,17 @@ assert.deepEqual(_.allKeys(y), ['x'], 'should get keys from constructor'); }); - test('values', function(assert) { + QUnit.test('values', function(assert) { assert.deepEqual(_.values({one: 1, two: 2}), [1, 2], 'can extract the values from an object'); assert.deepEqual(_.values({one: 1, two: 2, length: 3}), [1, 2, 3], '... even when one of them is "length"'); }); - test('pairs', function(assert) { + QUnit.test('pairs', function(assert) { assert.deepEqual(_.pairs({one: 1, two: 2}), [['one', 1], ['two', 2]], 'can convert an object into pairs'); assert.deepEqual(_.pairs({one: 1, two: 2, length: 3}), [['one', 1], ['two', 2], ['length', 3]], '... even when one of them is "length"'); }); - test('invert', function(assert) { + QUnit.test('invert', function(assert) { var obj = {first: 'Moe', second: 'Larry', third: 'Curly'}; assert.deepEqual(_.keys(_.invert(obj)), ['Moe', 'Larry', 'Curly'], 'can invert an object'); assert.deepEqual(_.invert(_.invert(obj)), obj, 'two inverts gets you back where you started'); @@ -92,7 +92,7 @@ assert.equal(_.invert(obj)['3'], 'length', 'can invert an object with "length"'); }); - test('functions', function(assert) { + QUnit.test('functions', function(assert) { var obj = {a: 'dash', b: _.map, c: /yo/, d: _.reduce}; assert.deepEqual(['b', 'd'], _.functions(obj), 'can grab the function names of any passed-in object'); @@ -101,11 +101,11 @@ assert.deepEqual(_.functions(new Animal), ['run'], 'also looks up functions on the prototype'); }); - test('methods', function(assert) { + QUnit.test('methods', function(assert) { assert.strictEqual(_.methods, _.functions, 'is an alias for functions'); }); - test('extend', function(assert) { + QUnit.test('extend', function(assert) { var result; assert.equal(_.extend({}, {a: 'b'}).a, 'b', 'can extend an object with the attributes of another'); assert.equal(_.extend({a: 'x'}, {a: 'b'}).a, 'b', 'properties in source override destination'); @@ -136,7 +136,7 @@ assert.strictEqual(_.extend(void 0, {a: 1}), void 0, 'extending undefined results in undefined'); }); - test('extendOwn', function(assert) { + QUnit.test('extendOwn', function(assert) { var result; assert.equal(_.extendOwn({}, {a: 'b'}).a, 'b', 'can extend an object with the attributes of another'); assert.equal(_.extendOwn({a: 'x'}, {a: 'b'}).a, 'b', 'properties in source override destination'); @@ -166,11 +166,11 @@ assert.deepEqual(result, {a: 1, 0: 1, 1: 2, length: 2}, 'should treat array-like objects like normal objects'); }); - test('assign', function(assert) { + QUnit.test('assign', function(assert) { assert.strictEqual(_.assign, _.extendOwn, 'is an alias for extendOwn'); }); - test('pick', function(assert) { + QUnit.test('pick', function(assert) { var result; result = _.pick({a: 1, b: 2, c: 3}, 'a', 'c'); assert.deepEqual(result, {a: 1, c: 3}, 'can restrict properties to those named'); @@ -211,7 +211,7 @@ }); }); - test('omit', function(assert) { + QUnit.test('omit', function(assert) { var result; result = _.omit({a: 1, b: 2, c: 3}, 'b'); assert.deepEqual(result, {a: 1, c: 3}, 'can omit a single named property'); @@ -245,7 +245,7 @@ }, instance), {a: 1, b: 2}, 'function is given context'); }); - test('defaults', function(assert) { + QUnit.test('defaults', function(assert) { var options = {zero: 0, one: 1, empty: '', nan: NaN, nothing: null}; _.defaults(options, {zero: 1, one: 10, twenty: 20, nothing: 'str'}); @@ -270,7 +270,7 @@ assert.deepEqual(_.defaults(void 0, {a: 1}), {a: 1}, 'defaults skips undefined'); }); - test('clone', function(assert) { + QUnit.test('clone', function(assert) { var moe = {name: 'moe', lucky: [13, 27, 34]}; var clone = _.clone(moe); assert.equal(clone.name, 'moe', 'the clone as the attributes of the original'); @@ -286,7 +286,7 @@ assert.equal(_.clone(null), null, 'non objects should not be changed by clone'); }); - test('create', function(assert) { + QUnit.test('create', function(assert) { var Parent = function() {}; Parent.prototype = {foo: function() {}, bar: 2}; @@ -312,7 +312,7 @@ assert.ok(!created.hasOwnProperty('foo'), 'should only add own properties'); }); - test('isEqual', function(assert) { + QUnit.test('isEqual', function(assert) { function First() { this.value = 1; } @@ -567,7 +567,7 @@ assert.equal(_.isEqual({a: NaN}, {a: NaN}), true); }); - test('isEmpty', function(assert) { + QUnit.test('isEmpty', function(assert) { assert.ok(!_([1]).isEmpty(), '[1] is not empty'); assert.ok(_.isEmpty([]), '[] is empty'); assert.ok(!_.isEmpty({one: 1}), '{one: 1} is not empty'); @@ -592,13 +592,13 @@ }); if (typeof document === 'object') { - test('isElement', function(assert) { + QUnit.test('isElement', function(assert) { assert.ok(!_.isElement('div'), 'strings are not dom elements'); assert.ok(_.isElement(testElement), 'an element is a DOM element'); }); } - test('isArguments', function(assert) { + QUnit.test('isArguments', function(assert) { var args = (function(){ return arguments; }(1, 2, 3)); assert.ok(!_.isArguments('string'), 'a string is not an arguments object'); assert.ok(!_.isArguments(_.isArguments), 'a function is not an arguments object'); @@ -607,7 +607,7 @@ assert.ok(!_.isArguments([1, 2, 3]), 'and not vanilla arrays.'); }); - test('isObject', function(assert) { + QUnit.test('isObject', function(assert) { assert.ok(_.isObject(arguments), 'the arguments object is object'); assert.ok(_.isObject([1, 2, 3]), 'and arrays'); if (testElement) { @@ -622,13 +622,13 @@ assert.ok(_.isObject(new String('string')), 'but new String()'); }); - test('isArray', function(assert) { + QUnit.test('isArray', function(assert) { assert.ok(!_.isArray(void 0), 'undefined vars are not arrays'); assert.ok(!_.isArray(arguments), 'the arguments object is not an array'); assert.ok(_.isArray([1, 2, 3]), 'but arrays are'); }); - test('isString', function(assert) { + QUnit.test('isString', function(assert) { var obj = new String('I am a string object'); if (testElement) { assert.ok(!_.isString(testElement), 'an element is not a string'); @@ -639,7 +639,7 @@ assert.strictEqual(_.isString(1), false); }); - test('isNumber', function(assert) { + QUnit.test('isNumber', function(assert) { assert.ok(!_.isNumber('string'), 'a string is not a number'); assert.ok(!_.isNumber(arguments), 'the arguments object is not a number'); assert.ok(!_.isNumber(void 0), 'undefined is not a number'); @@ -649,7 +649,7 @@ assert.ok(!_.isNumber('1'), 'numeric strings are not numbers'); }); - test('isBoolean', function(assert) { + QUnit.test('isBoolean', function(assert) { assert.ok(!_.isBoolean(2), 'a number is not a boolean'); assert.ok(!_.isBoolean('string'), 'a string is not a boolean'); assert.ok(!_.isBoolean('false'), 'the string "false" is not a boolean'); @@ -662,7 +662,7 @@ assert.ok(_.isBoolean(false), 'and so is false'); }); - test('isFunction', function(assert) { + QUnit.test('isFunction', function(assert) { assert.ok(!_.isFunction(void 0), 'undefined vars are not functions'); assert.ok(!_.isFunction([1, 2, 3]), 'arrays are not functions'); assert.ok(!_.isFunction('moe'), 'strings are not functions'); @@ -680,7 +680,7 @@ }); if (typeof Int8Array !== 'undefined') { - test('#1929 Typed Array constructors are functions', function(assert) { + QUnit.test('#1929 Typed Array constructors are functions', function(assert) { _.chain(['Float32Array', 'Float64Array', 'Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array']) .map(_.propertyOf(typeof GLOBAL != 'undefined' ? GLOBAL : window)) .compact() @@ -692,18 +692,18 @@ }); } - test('isDate', function(assert) { + QUnit.test('isDate', function(assert) { assert.ok(!_.isDate(100), 'numbers are not dates'); assert.ok(!_.isDate({}), 'objects are not dates'); assert.ok(_.isDate(new Date()), 'but dates are'); }); - test('isRegExp', function(assert) { + QUnit.test('isRegExp', function(assert) { assert.ok(!_.isRegExp(_.identity), 'functions are not RegExps'); assert.ok(_.isRegExp(/identity/), 'but RegExps are'); }); - test('isFinite', function(assert) { + QUnit.test('isFinite', function(assert) { assert.ok(!_.isFinite(void 0), 'undefined is not finite'); assert.ok(!_.isFinite(null), 'null is not finite'); assert.ok(!_.isFinite(NaN), 'NaN is not finite'); @@ -719,7 +719,7 @@ assert.ok(_.isFinite(-12.44), 'Floats are finite'); }); - test('isNaN', function(assert) { + QUnit.test('isNaN', function(assert) { assert.ok(!_.isNaN(void 0), 'undefined is not NaN'); assert.ok(!_.isNaN(null), 'null is not NaN'); assert.ok(!_.isNaN(0), '0 is not NaN'); @@ -728,13 +728,13 @@ assert.ok(_.isNaN(new Number(NaN)), 'wrapped NaN is still NaN'); }); - test('isNull', function(assert) { + QUnit.test('isNull', function(assert) { assert.ok(!_.isNull(void 0), 'undefined is not null'); assert.ok(!_.isNull(NaN), 'NaN is not null'); assert.ok(_.isNull(null), 'but null is'); }); - test('isUndefined', function(assert) { + QUnit.test('isUndefined', function(assert) { assert.ok(!_.isUndefined(1), 'numbers are defined'); assert.ok(!_.isUndefined(null), 'null is defined'); assert.ok(!_.isUndefined(false), 'false is defined'); @@ -743,7 +743,7 @@ assert.ok(_.isUndefined(void 0), 'undefined is undefined'); }); - test('isError', function(assert) { + QUnit.test('isError', function(assert) { assert.ok(!_.isError(1), 'numbers are not Errors'); assert.ok(!_.isError(null), 'null is not an Error'); assert.ok(!_.isError(Error), 'functions are not Errors'); @@ -756,7 +756,7 @@ assert.ok(_.isError(new URIError()), 'URIErrors are Errors'); }); - test('tap', function(assert) { + QUnit.test('tap', function(assert) { var intercepted = null; var interceptor = function(obj) { intercepted = obj; }; var returned = _.tap(1, interceptor); @@ -772,7 +772,7 @@ assert.equal(intercepted, returned, 'can use tapped objects in a chain'); }); - test('has', function(assert) { + QUnit.test('has', function(assert) { var obj = {foo: 'bar', func: function(){}}; assert.ok(_.has(obj, 'foo'), 'has() checks that the object has a property.'); assert.ok(!_.has(obj, 'baz'), "has() returns false if the object doesn't have the property."); @@ -786,7 +786,7 @@ assert.strictEqual(_.has(void 0, 'foo'), false, 'has() returns false for undefined'); }); - test('isMatch', function(assert) { + QUnit.test('isMatch', function(assert) { var moe = {name: 'Moe Howard', hair: true}; var curly = {name: 'Curly Howard', hair: false}; @@ -826,7 +826,7 @@ assert.deepEqual(_.map([null, void 0, 5, {}], _.partial(_.isMatch, _, oCon)), [false, false, false, true], 'doesnt falsey match constructor on undefined/null'); }); - test('matcher', function(assert) { + QUnit.test('matcher', function(assert) { var moe = {name: 'Moe Howard', hair: true}; var curly = {name: 'Curly Howard', hair: false}; var stooges = [moe, curly]; @@ -883,11 +883,11 @@ assert.deepEqual(_.map([null, void 0, 5, {}], oCon), [false, false, false, true], 'doesnt falsey match constructor on undefined/null'); }); - test('matches', function(assert) { + QUnit.test('matches', function(assert) { assert.strictEqual(_.matches, _.matcher, 'is an alias for matcher'); }); - test('findKey', function(assert) { + QUnit.test('findKey', function(assert) { var objects = { a: {a: 0, b: 0}, b: {a: 1, b: 1}, @@ -928,7 +928,7 @@ }); - test('mapObject', function(assert) { + QUnit.test('mapObject', function(assert) { var obj = {a: 1, b: 2}; var objects = { a: {a: 0, b: 0}, diff --git a/test/utility.js b/test/utility.js index 09cd54d62..674b99cbc 100644 --- a/test/utility.js +++ b/test/utility.js @@ -15,7 +15,7 @@ }); if (typeof this == 'object') { - test('noConflict', function(assert) { + QUnit.test('noConflict', function(assert) { var underscore = _.noConflict(); assert.equal(underscore.identity(1), 1); if (typeof require != 'function') { @@ -47,34 +47,34 @@ }); } - test('#750 - Return _ instance.', 2, function(assert) { + QUnit.test('#750 - Return _ instance.', 2, function(assert) { var instance = _([]); assert.ok(_(instance) === instance); assert.ok(new _(instance) === instance); }); - test('identity', function(assert) { + QUnit.test('identity', function(assert) { var stooge = {name: 'moe'}; assert.equal(_.identity(stooge), stooge, 'stooge is the same as his identity'); }); - test('constant', function(assert) { + QUnit.test('constant', function(assert) { var stooge = {name: 'moe'}; assert.equal(_.constant(stooge)(), stooge, 'should create a function that returns stooge'); }); - test('noop', function(assert) { + QUnit.test('noop', function(assert) { assert.strictEqual(_.noop('curly', 'larry', 'moe'), void 0, 'should always return undefined'); }); - test('property', function(assert) { + QUnit.test('property', function(assert) { var stooge = {name: 'moe'}; assert.equal(_.property('name')(stooge), 'moe', 'should return the property with the given name'); assert.equal(_.property('name')(null), void 0, 'should return undefined for null values'); assert.equal(_.property('name')(void 0), void 0, 'should return undefined for undefined values'); }); - test('propertyOf', function(assert) { + QUnit.test('propertyOf', function(assert) { var stoogeRanks = _.propertyOf({curly: 2, moe: 1, larry: 3}); assert.equal(stoogeRanks('curly'), 2, 'should return the property with the given name'); assert.equal(stoogeRanks(null), void 0, 'should return undefined for null values'); @@ -92,7 +92,7 @@ assert.equal(undefPropertyOf('curly'), void 0, 'should return undefined when obj is undefined'); }); - test('random', function(assert) { + QUnit.test('random', function(assert) { var array = _.range(1000); var min = Math.pow(2, 31); var max = Math.pow(2, 62); @@ -106,18 +106,18 @@ }), 'should produce a random number when passed `Number.MAX_VALUE`'); }); - test('now', function(assert) { + QUnit.test('now', function(assert) { var diff = _.now() - new Date().getTime(); assert.ok(diff <= 0 && diff > -5, 'Produces the correct time in milliseconds');//within 5ms }); - test('uniqueId', function(assert) { + QUnit.test('uniqueId', function(assert) { var ids = [], i = 0; while (i++ < 100) ids.push(_.uniqueId()); assert.equal(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids'); }); - test('times', function(assert) { + QUnit.test('times', function(assert) { var vals = []; _.times(3, function(i) { vals.push(i); }); assert.deepEqual(vals, [0, 1, 2], 'is 0 indexed'); @@ -133,7 +133,7 @@ assert.deepEqual(_.times(parseFloat('-Infinity'), _.identity), []); }); - test('mixin', function(assert) { + QUnit.test('mixin', function(assert) { _.mixin({ myReverse: function(string) { return string.split('').reverse().join(''); @@ -143,11 +143,11 @@ assert.equal(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper'); }); - test('_.escape', function(assert) { + QUnit.test('_.escape', function(assert) { assert.equal(_.escape(null), ''); }); - test('_.unescape', function(assert) { + QUnit.test('_.unescape', function(assert) { var string = 'Curly & Moe'; assert.equal(_.unescape(null), ''); assert.equal(_.unescape(_.escape(string)), string); @@ -155,7 +155,7 @@ }); // Don't care what they escape them to just that they're escaped and can be unescaped - test('_.escape & unescape', function(assert) { + QUnit.test('_.escape & unescape', function(assert) { // test & (&) seperately obviously var escapeCharacters = ['<', '>', '"', '\'', '`']; @@ -189,7 +189,7 @@ assert.equal(_.unescape(str), str, 'can unescape &'); }); - test('template', function(assert) { + QUnit.test('template', function(assert) { var basicTemplate = _.template("<%= thing %> is gettin' on my noives!"); var result = basicTemplate({thing: 'This'}); assert.equal(result, "This is gettin' on my noives!", 'can do basic attribute interpolation'); @@ -298,7 +298,7 @@ assert.equal(templateWithNull({planet: 'world'}), 'a null undefined world', 'can handle missing escape and evaluate settings'); }); - test('_.template provides the generated function source, when a SyntaxError occurs', function(assert) { + QUnit.test('_.template provides the generated function source, when a SyntaxError occurs', function(assert) { var source; try { _.template('<%= if x %>'); @@ -308,12 +308,12 @@ assert.ok(/__p/.test(source)); }); - test('_.template handles \\u2028 & \\u2029', function(assert) { + QUnit.test('_.template handles \\u2028 & \\u2029', function(assert) { var tmpl = _.template('

      \u2028<%= "\\u2028\\u2029" %>\u2029

      '); assert.strictEqual(tmpl(), '

      \u2028\u2028\u2029\u2029

      '); }); - test('result calls functions and returns primitives', function(assert) { + QUnit.test('result calls functions and returns primitives', function(assert) { var obj = {w: '', x: 'x', y: function(){ return this.x; }}; assert.strictEqual(_.result(obj, 'w'), ''); assert.strictEqual(_.result(obj, 'x'), 'x'); @@ -322,34 +322,34 @@ assert.strictEqual(_.result(null, 'x'), void 0); }); - test('result returns a default value if object is null or undefined', function(assert) { + QUnit.test('result returns a default value if object is null or undefined', function(assert) { assert.strictEqual(_.result(null, 'b', 'default'), 'default'); assert.strictEqual(_.result(void 0, 'c', 'default'), 'default'); assert.strictEqual(_.result(''.match('missing'), 1, 'default'), 'default'); }); - test('result returns a default value if property of object is missing', function(assert) { + QUnit.test('result returns a default value if property of object is missing', function(assert) { assert.strictEqual(_.result({d: null}, 'd', 'default'), null); assert.strictEqual(_.result({e: false}, 'e', 'default'), false); }); - test('result only returns the default value if the object does not have the property or is undefined', function(assert) { + QUnit.test('result only returns the default value if the object does not have the property or is undefined', function(assert) { assert.strictEqual(_.result({}, 'b', 'default'), 'default'); assert.strictEqual(_.result({d: void 0}, 'd', 'default'), 'default'); }); - test('result does not return the default if the property of an object is found in the prototype', function(assert) { + QUnit.test('result does not return the default if the property of an object is found in the prototype', function(assert) { var Foo = function(){}; Foo.prototype.bar = 1; assert.strictEqual(_.result(new Foo, 'bar', 2), 1); }); - test('result does use the fallback when the result of invoking the property is undefined', function(assert) { + QUnit.test('result does use the fallback when the result of invoking the property is undefined', function(assert) { var obj = {a: function() {}}; assert.strictEqual(_.result(obj, 'a', 'failed'), void 0); }); - test('result fallback can use a function', function(assert) { + QUnit.test('result fallback can use a function', function(assert) { var obj = {a: [1, 2, 3]}; assert.strictEqual(_.result(obj, 'b', _.constant(5)), 5); assert.strictEqual(_.result(obj, 'b', function() { @@ -357,7 +357,7 @@ }), obj.a, 'called with context'); }); - test('_.templateSettings.variable', function(assert) { + QUnit.test('_.templateSettings.variable', function(assert) { var s = '<%=data.x%>'; var data = {x: 'x'}; var tmp = _.template(s, {variable: 'data'}); @@ -366,13 +366,13 @@ assert.strictEqual(_.template(s)(data), 'x'); }); - test('#547 - _.templateSettings is unchanged by custom settings.', function(assert) { + QUnit.test('#547 - _.templateSettings is unchanged by custom settings.', function(assert) { assert.ok(!_.templateSettings.variable); _.template('', {}, {variable: 'x'}); assert.ok(!_.templateSettings.variable); }); - test('#556 - undefined template variables.', function(assert) { + QUnit.test('#556 - undefined template variables.', function(assert) { var template = _.template('<%=x%>'); assert.strictEqual(template({x: null}), ''); assert.strictEqual(template({x: void 0}), ''); @@ -390,7 +390,7 @@ assert.strictEqual(templateWithPropertyEscaped({x: {}}), ''); }); - test('interpolate evaluates code only once.', 2, function(assert) { + QUnit.test('interpolate evaluates code only once.', 2, function(assert) { var count = 0; var template = _.template('<%= f() %>'); template({f: function(){ assert.ok(!count++); }}); @@ -400,13 +400,13 @@ templateEscaped({f: function(){ assert.ok(!countEscaped++); }}); }); - test('#746 - _.template settings are not modified.', 1, function(assert) { + QUnit.test('#746 - _.template settings are not modified.', 1, function(assert) { var settings = {}; _.template('', null, settings); assert.deepEqual(settings, {}); }); - test('#779 - delimeters are applied to unescaped text.', 1, function(assert) { + QUnit.test('#779 - delimeters are applied to unescaped text.', 1, function(assert) { var template = _.template('<<\nx\n>>', null, {evaluate: /<<(.*?)>>/g}); assert.strictEqual(template(), '<<\nx\n>>'); }); From 97963986d909f03c1feea11c14c277043915f9c7 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sun, 13 Dec 2015 20:42:19 -0800 Subject: [PATCH 120/263] Don't allow the global expect() in tests We are already using QUnit.expect in all cases, and QUnit version 2 will not support the global `expect` function. --- test/.eslintrc | 1 - 1 file changed, 1 deletion(-) diff --git a/test/.eslintrc b/test/.eslintrc index b7af19c2e..3d2d9faf6 100644 --- a/test/.eslintrc +++ b/test/.eslintrc @@ -14,7 +14,6 @@ "throws": false, "asyncTest": false, "start": false, - "expect": false, "QUnit": false }, "rules": { From 2fc589e41abfb857616668c4167145ef10c27c1e Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sun, 13 Dec 2015 20:47:26 -0800 Subject: [PATCH 121/263] Don't permit global QUnit assertion syntax We aren't doing it currently, and as of QUnit2, it will no longer be supported. --- test/.eslintrc | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/.eslintrc b/test/.eslintrc index 3d2d9faf6..545fc53af 100644 --- a/test/.eslintrc +++ b/test/.eslintrc @@ -3,15 +3,6 @@ "browser": true }, "globals": { - "ok": false, - "is": false, - "equal": false, - "deepEqual": false, - "strictEqual": false, - "notStrictEqual": false, - "notEqual": false, - "notDeepEqual": false, - "throws": false, "asyncTest": false, "start": false, "QUnit": false From 151de4d905a1a6689ae27f1043f8a8739a714ac3 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sun, 13 Dec 2015 20:50:06 -0800 Subject: [PATCH 122/263] Use QUnit2 syntax for setup/teardown QUnit 2 will require us to use this new syntax. --- test/utility.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/utility.js b/test/utility.js index 674b99cbc..ff8363530 100644 --- a/test/utility.js +++ b/test/utility.js @@ -4,11 +4,11 @@ QUnit.module('Utility', { - setup: function() { + beforeEach: function() { templateSettings = _.clone(_.templateSettings); }, - teardown: function() { + afterEach: function() { _.templateSettings = templateSettings; } From 2f5b91f43d84944fd1504c4bae17251fdf8b4796 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sun, 13 Dec 2015 21:08:25 -0800 Subject: [PATCH 123/263] Use QUnit 2 async testing style --- test/.eslintrc | 2 - test/functions.js | 120 +++++++++++++++++++++++++++------------------- test/utility.js | 5 +- 3 files changed, 75 insertions(+), 52 deletions(-) diff --git a/test/.eslintrc b/test/.eslintrc index 545fc53af..867008dfe 100644 --- a/test/.eslintrc +++ b/test/.eslintrc @@ -3,8 +3,6 @@ "browser": true }, "globals": { - "asyncTest": false, - "start": false, "QUnit": false }, "rules": { diff --git a/test/functions.js b/test/functions.js index c35a17395..3b4549129 100644 --- a/test/functions.js +++ b/test/functions.js @@ -174,59 +174,66 @@ assert.strictEqual(myObj.value, 'a', 'object is not modified if second argument used as key'); }); - asyncTest('delay', 2, function(assert) { + QUnit.test('delay', 2, function(assert) { + var done = assert.async(); var delayed = false; _.delay(function(){ delayed = true; }, 100); setTimeout(function(){ assert.ok(!delayed, "didn't delay the function quite yet"); }, 50); - setTimeout(function(){ assert.ok(delayed, 'delayed the function'); start(); }, 150); + setTimeout(function(){ assert.ok(delayed, 'delayed the function'); done(); }, 150); }); - asyncTest('defer', 1, function(assert) { + QUnit.test('defer', 1, function(assert) { + var done = assert.async(); var deferred = false; _.defer(function(bool){ deferred = bool; }, true); - _.delay(function(){ assert.ok(deferred, 'deferred the function'); start(); }, 50); + _.delay(function(){ assert.ok(deferred, 'deferred the function'); done(); }, 50); }); - asyncTest('throttle', 2, function(assert) { + QUnit.test('throttle', 2, function(assert) { + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 32); throttledIncr(); throttledIncr(); assert.equal(counter, 1, 'incr was called immediately'); - _.delay(function(){ assert.equal(counter, 2, 'incr was throttled'); start(); }, 64); + _.delay(function(){ assert.equal(counter, 2, 'incr was throttled'); done(); }, 64); }); - asyncTest('throttle arguments', 2, function(assert) { + QUnit.test('throttle arguments', 2, function(assert) { + var done = assert.async(); var value = 0; var update = function(val){ value = val; }; var throttledUpdate = _.throttle(update, 32); throttledUpdate(1); throttledUpdate(2); _.delay(function(){ throttledUpdate(3); }, 64); assert.equal(value, 1, 'updated to latest value'); - _.delay(function(){ assert.equal(value, 3, 'updated to latest value'); start(); }, 96); + _.delay(function(){ assert.equal(value, 3, 'updated to latest value'); done(); }, 96); }); - asyncTest('throttle once', 2, function(assert) { + QUnit.test('throttle once', 2, function(assert) { + var done = assert.async(); var counter = 0; var incr = function(){ return ++counter; }; var throttledIncr = _.throttle(incr, 32); var result = throttledIncr(); _.delay(function(){ assert.equal(result, 1, 'throttled functions return their value'); - assert.equal(counter, 1, 'incr was called once'); start(); + assert.equal(counter, 1, 'incr was called once'); done(); }, 64); }); - asyncTest('throttle twice', 1, function(assert) { + QUnit.test('throttle twice', 1, function(assert) { + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 32); throttledIncr(); throttledIncr(); - _.delay(function(){ assert.equal(counter, 2, 'incr was called twice'); start(); }, 64); + _.delay(function(){ assert.equal(counter, 2, 'incr was called twice'); done(); }, 64); }); - asyncTest('more throttling', 3, function(assert) { + QUnit.test('more throttling', 3, function(assert) { + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 30); @@ -236,11 +243,12 @@ assert.equal(counter, 2); throttledIncr(); assert.equal(counter, 3); - start(); + done(); }, 85); }); - asyncTest('throttle repeatedly with results', 6, function(assert) { + QUnit.test('throttle repeatedly with results', 6, function(assert) { + var done = assert.async(); var counter = 0; var incr = function(){ return ++counter; }; var throttledIncr = _.throttle(incr, 100); @@ -258,11 +266,12 @@ assert.equal(results[3], 2, 'incr was called twice'); assert.equal(results[4], 2, 'incr was throttled'); assert.equal(results[5], 3, 'incr was called trailing'); - start(); + done(); }, 300); }); - asyncTest('throttle triggers trailing call when invoked repeatedly', 2, function(assert) { + QUnit.test('throttle triggers trailing call when invoked repeatedly', 2, function(assert) { + var done = assert.async(); var counter = 0; var limit = 48; var incr = function(){ counter++; }; @@ -277,11 +286,12 @@ _.delay(function() { assert.ok(counter > lastCount); - start(); + done(); }, 96); }); - asyncTest('throttle does not trigger leading call when leading is set to false', 2, function(assert) { + QUnit.test('throttle does not trigger leading call when leading is set to false', 2, function(assert) { + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 60, {leading: false}); @@ -291,11 +301,12 @@ _.delay(function() { assert.equal(counter, 1); - start(); + done(); }, 96); }); - asyncTest('more throttle does not trigger leading call when leading is set to false', 3, function(assert) { + QUnit.test('more throttle does not trigger leading call when leading is set to false', 3, function(assert) { + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 100, {leading: false}); @@ -312,11 +323,12 @@ _.delay(function() { assert.equal(counter, 2); - start(); + done(); }, 350); }); - asyncTest('one more throttle with leading: false test', 2, function(assert) { + QUnit.test('one more throttle with leading: false test', 2, function(assert) { + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 100, {leading: false}); @@ -327,11 +339,12 @@ _.delay(function() { assert.ok(counter <= 4); - start(); + done(); }, 200); }); - asyncTest('throttle does not trigger trailing call when trailing is set to false', 4, function(assert) { + QUnit.test('throttle does not trigger trailing call when trailing is set to false', 4, function(assert) { + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 60, {trailing: false}); @@ -347,12 +360,13 @@ _.delay(function() { assert.equal(counter, 2); - start(); + done(); }, 96); }, 96); }); - asyncTest('throttle continues to function after system time is set backwards', 2, function(assert) { + QUnit.test('throttle continues to function after system time is set backwards', 2, function(assert) { + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 100); @@ -367,12 +381,13 @@ _.delay(function() { throttledIncr(); assert.equal(counter, 2); - start(); + done(); _.now = origNowFunc; }, 200); }); - asyncTest('throttle re-entrant', 2, function(assert) { + QUnit.test('throttle re-entrant', 2, function(assert) { + var done = assert.async(); var sequence = [ ['b1', 'b2'], ['c1', 'c2'] @@ -391,11 +406,12 @@ assert.equal(value, 'a1a2'); _.delay(function(){ assert.equal(value, 'a1a2c1c2b1b2', 'append was throttled successfully'); - start(); + done(); }, 100); }); - asyncTest('throttle cancel', function(assert) { + QUnit.test('throttle cancel', function(assert) { + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 32); @@ -405,10 +421,11 @@ throttledIncr(); assert.equal(counter, 2, 'incr was called immediately'); - _.delay(function(){ assert.equal(counter, 3, 'incr was throttled'); start(); }, 64); + _.delay(function(){ assert.equal(counter, 3, 'incr was throttled'); done(); }, 64); }); - asyncTest('throttle cancel with leading: false', function(assert) { + QUnit.test('throttle cancel with leading: false', function(assert) { + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 32, {leading: false}); @@ -416,28 +433,31 @@ throttledIncr.cancel(); assert.equal(counter, 0, 'incr was throttled'); - _.delay(function(){ assert.equal(counter, 0, 'incr was throttled'); start(); }, 64); + _.delay(function(){ assert.equal(counter, 0, 'incr was throttled'); done(); }, 64); }); - asyncTest('debounce', 1, function(assert) { + QUnit.test('debounce', 1, function(assert) { + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var debouncedIncr = _.debounce(incr, 32); debouncedIncr(); debouncedIncr(); _.delay(debouncedIncr, 16); - _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); start(); }, 96); + _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); done(); }, 96); }); - asyncTest('debounce cancel', 1, function(assert) { + QUnit.test('debounce cancel', 1, function(assert) { + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var debouncedIncr = _.debounce(incr, 32); debouncedIncr(); debouncedIncr.cancel(); - _.delay(function(){ assert.equal(counter, 0, 'incr was not called'); start(); }, 96); + _.delay(function(){ assert.equal(counter, 0, 'incr was not called'); done(); }, 96); }); - asyncTest('debounce asap', 4, function(assert) { + QUnit.test('debounce asap', 4, function(assert) { + var done = assert.async(); var a, b; var counter = 0; var incr = function(){ return ++counter; }; @@ -450,10 +470,11 @@ _.delay(debouncedIncr, 16); _.delay(debouncedIncr, 32); _.delay(debouncedIncr, 48); - _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); start(); }, 128); + _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); done(); }, 128); }); - asyncTest('debounce asap cancel', 4, function(assert) { + QUnit.test('debounce asap cancel', 4, function(assert) { + var done = assert.async(); var a, b; var counter = 0; var incr = function(){ return ++counter; }; @@ -467,10 +488,11 @@ _.delay(debouncedIncr, 16); _.delay(debouncedIncr, 32); _.delay(debouncedIncr, 48); - _.delay(function(){ assert.equal(counter, 2, 'incr was debounced'); start(); }, 128); + _.delay(function(){ assert.equal(counter, 2, 'incr was debounced'); done(); }, 128); }); - asyncTest('debounce asap recursively', 2, function(assert) { + QUnit.test('debounce asap recursively', 2, function(assert) { + var done = assert.async(); var counter = 0; var debouncedIncr = _.debounce(function(){ counter++; @@ -478,10 +500,11 @@ }, 32, true); debouncedIncr(); assert.equal(counter, 1, 'incr was called immediately'); - _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); start(); }, 96); + _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); done(); }, 96); }); - asyncTest('debounce after system time is set backwards', 2, function(assert) { + QUnit.test('debounce after system time is set backwards', 2, function(assert) { + var done = assert.async(); var counter = 0; var origNowFunc = _.now; var debouncedIncr = _.debounce(function(){ @@ -498,12 +521,13 @@ _.delay(function() { debouncedIncr(); assert.equal(counter, 2, 'incr was debounced successfully'); - start(); + done(); _.now = origNowFunc; }, 200); }); - asyncTest('debounce re-entrant', 2, function(assert) { + QUnit.test('debounce re-entrant', 2, function(assert) { + var done = assert.async(); var sequence = [ ['b1', 'b2'] ]; @@ -521,7 +545,7 @@ assert.equal(value, ''); _.delay(function(){ assert.equal(value, 'a1a2b1b2', 'append was debounced successfully'); - start(); + done(); }, 100); }); diff --git a/test/utility.js b/test/utility.js index ff8363530..6e1b33b2c 100644 --- a/test/utility.js +++ b/test/utility.js @@ -28,7 +28,8 @@ } if (typeof require == 'function') { - asyncTest('noConflict (node vm)', 2, function(assert) { + QUnit.test('noConflict (node vm)', 2, function(assert) { + var done = assert.async(); var fs = require('fs'); var vm = require('vm'); var filename = __dirname + '/../underscore.js'; @@ -42,7 +43,7 @@ assert.equal(context._, 'oldvalue'); assert.equal(context.underscore.VERSION, _.VERSION); - start(); + done(); }); }); } From 38ac797c34631a3b6a26cd9ef927b363a40da9ae Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sun, 13 Dec 2015 21:51:55 -0800 Subject: [PATCH 124/263] Use QUnit 2 expect() syntax --- test/collections.js | 3 +- test/functions.js | 72 ++++++++++++++++++++++++++++++--------------- test/utility.js | 15 ++++++---- 3 files changed, 60 insertions(+), 30 deletions(-) diff --git a/test/collections.js b/test/collections.js index 1bc14790e..c66239f12 100644 --- a/test/collections.js +++ b/test/collections.js @@ -453,7 +453,8 @@ }); - QUnit.test('invoke', 5, function(assert) { + QUnit.test('invoke', function(assert) { + assert.expect(5); var list = [[5, 1, 7], [3, 2, 1]]; var result = _.invoke(list, 'sort'); assert.deepEqual(result[0], [1, 5, 7], 'first array sorted'); diff --git a/test/functions.js b/test/functions.js index 3b4549129..db627ccc1 100644 --- a/test/functions.js +++ b/test/functions.js @@ -174,7 +174,8 @@ assert.strictEqual(myObj.value, 'a', 'object is not modified if second argument used as key'); }); - QUnit.test('delay', 2, function(assert) { + QUnit.test('delay', function(assert) { + assert.expect(2); var done = assert.async(); var delayed = false; _.delay(function(){ delayed = true; }, 100); @@ -182,14 +183,16 @@ setTimeout(function(){ assert.ok(delayed, 'delayed the function'); done(); }, 150); }); - QUnit.test('defer', 1, function(assert) { + QUnit.test('defer', function(assert) { + assert.expect(1); var done = assert.async(); var deferred = false; _.defer(function(bool){ deferred = bool; }, true); _.delay(function(){ assert.ok(deferred, 'deferred the function'); done(); }, 50); }); - QUnit.test('throttle', 2, function(assert) { + QUnit.test('throttle', function(assert) { + assert.expect(2); var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; @@ -200,7 +203,8 @@ _.delay(function(){ assert.equal(counter, 2, 'incr was throttled'); done(); }, 64); }); - QUnit.test('throttle arguments', 2, function(assert) { + QUnit.test('throttle arguments', function(assert) { + assert.expect(2); var done = assert.async(); var value = 0; var update = function(val){ value = val; }; @@ -211,7 +215,8 @@ _.delay(function(){ assert.equal(value, 3, 'updated to latest value'); done(); }, 96); }); - QUnit.test('throttle once', 2, function(assert) { + QUnit.test('throttle once', function(assert) { + assert.expect(2); var done = assert.async(); var counter = 0; var incr = function(){ return ++counter; }; @@ -223,7 +228,8 @@ }, 64); }); - QUnit.test('throttle twice', 1, function(assert) { + QUnit.test('throttle twice', function(assert) { + assert.expect(1); var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; @@ -232,7 +238,8 @@ _.delay(function(){ assert.equal(counter, 2, 'incr was called twice'); done(); }, 64); }); - QUnit.test('more throttling', 3, function(assert) { + QUnit.test('more throttling', function(assert) { + assert.expect(3); var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; @@ -247,7 +254,8 @@ }, 85); }); - QUnit.test('throttle repeatedly with results', 6, function(assert) { + QUnit.test('throttle repeatedly with results', function(assert) { + assert.expect(6); var done = assert.async(); var counter = 0; var incr = function(){ return ++counter; }; @@ -270,7 +278,8 @@ }, 300); }); - QUnit.test('throttle triggers trailing call when invoked repeatedly', 2, function(assert) { + QUnit.test('throttle triggers trailing call when invoked repeatedly', function(assert) { + assert.expect(2); var done = assert.async(); var counter = 0; var limit = 48; @@ -290,7 +299,8 @@ }, 96); }); - QUnit.test('throttle does not trigger leading call when leading is set to false', 2, function(assert) { + QUnit.test('throttle does not trigger leading call when leading is set to false', function(assert) { + assert.expect(2); var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; @@ -305,7 +315,8 @@ }, 96); }); - QUnit.test('more throttle does not trigger leading call when leading is set to false', 3, function(assert) { + QUnit.test('more throttle does not trigger leading call when leading is set to false', function(assert) { + assert.expect(3); var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; @@ -327,7 +338,8 @@ }, 350); }); - QUnit.test('one more throttle with leading: false test', 2, function(assert) { + QUnit.test('one more throttle with leading: false test', function(assert) { + assert.expect(2); var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; @@ -343,7 +355,8 @@ }, 200); }); - QUnit.test('throttle does not trigger trailing call when trailing is set to false', 4, function(assert) { + QUnit.test('throttle does not trigger trailing call when trailing is set to false', function(assert) { + assert.expect(4); var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; @@ -365,7 +378,8 @@ }, 96); }); - QUnit.test('throttle continues to function after system time is set backwards', 2, function(assert) { + QUnit.test('throttle continues to function after system time is set backwards', function(assert) { + assert.expect(2); var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; @@ -386,7 +400,8 @@ }, 200); }); - QUnit.test('throttle re-entrant', 2, function(assert) { + QUnit.test('throttle re-entrant', function(assert) { + assert.expect(2); var done = assert.async(); var sequence = [ ['b1', 'b2'], @@ -436,7 +451,8 @@ _.delay(function(){ assert.equal(counter, 0, 'incr was throttled'); done(); }, 64); }); - QUnit.test('debounce', 1, function(assert) { + QUnit.test('debounce', function(assert) { + assert.expect(1); var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; @@ -446,7 +462,8 @@ _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); done(); }, 96); }); - QUnit.test('debounce cancel', 1, function(assert) { + QUnit.test('debounce cancel', function(assert) { + assert.expect(1); var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; @@ -456,7 +473,8 @@ _.delay(function(){ assert.equal(counter, 0, 'incr was not called'); done(); }, 96); }); - QUnit.test('debounce asap', 4, function(assert) { + QUnit.test('debounce asap', function(assert) { + assert.expect(4); var done = assert.async(); var a, b; var counter = 0; @@ -473,7 +491,8 @@ _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); done(); }, 128); }); - QUnit.test('debounce asap cancel', 4, function(assert) { + QUnit.test('debounce asap cancel', function(assert) { + assert.expect(4); var done = assert.async(); var a, b; var counter = 0; @@ -491,7 +510,8 @@ _.delay(function(){ assert.equal(counter, 2, 'incr was debounced'); done(); }, 128); }); - QUnit.test('debounce asap recursively', 2, function(assert) { + QUnit.test('debounce asap recursively', function(assert) { + assert.expect(2); var done = assert.async(); var counter = 0; var debouncedIncr = _.debounce(function(){ @@ -503,7 +523,8 @@ _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); done(); }, 96); }); - QUnit.test('debounce after system time is set backwards', 2, function(assert) { + QUnit.test('debounce after system time is set backwards', function(assert) { + assert.expect(2); var done = assert.async(); var counter = 0; var origNowFunc = _.now; @@ -526,7 +547,8 @@ }, 200); }); - QUnit.test('debounce re-entrant', 2, function(assert) { + QUnit.test('debounce re-entrant', function(assert) { + assert.expect(2); var done = assert.async(); var sequence = [ ['b1', 'b2'] @@ -559,7 +581,8 @@ assert.equal(increment(), 1, 'stores a memo to the last value'); }); - QUnit.test('Recursive onced function.', 1, function(assert) { + QUnit.test('Recursive onced function.', function(assert) { + assert.expect(1); var f = _.once(function(){ assert.ok(true); f(); @@ -666,7 +689,8 @@ }); - QUnit.test('restArgs', 10, function(assert) { + QUnit.test('restArgs', function(assert) { + assert.expect(10); _.restArgs(function(a, args) { assert.strictEqual(a, 1); assert.deepEqual(args, [2, 3], 'collects rest arguments into an array'); diff --git a/test/utility.js b/test/utility.js index 6e1b33b2c..fbd54df31 100644 --- a/test/utility.js +++ b/test/utility.js @@ -28,7 +28,8 @@ } if (typeof require == 'function') { - QUnit.test('noConflict (node vm)', 2, function(assert) { + QUnit.test('noConflict (node vm)', function(assert) { + assert.expect(2); var done = assert.async(); var fs = require('fs'); var vm = require('vm'); @@ -48,7 +49,8 @@ }); } - QUnit.test('#750 - Return _ instance.', 2, function(assert) { + QUnit.test('#750 - Return _ instance.', function(assert) { + assert.expect(2); var instance = _([]); assert.ok(_(instance) === instance); assert.ok(new _(instance) === instance); @@ -391,7 +393,8 @@ assert.strictEqual(templateWithPropertyEscaped({x: {}}), ''); }); - QUnit.test('interpolate evaluates code only once.', 2, function(assert) { + QUnit.test('interpolate evaluates code only once.', function(assert) { + assert.expect(2); var count = 0; var template = _.template('<%= f() %>'); template({f: function(){ assert.ok(!count++); }}); @@ -401,13 +404,15 @@ templateEscaped({f: function(){ assert.ok(!countEscaped++); }}); }); - QUnit.test('#746 - _.template settings are not modified.', 1, function(assert) { + QUnit.test('#746 - _.template settings are not modified.', function(assert) { + assert.expect(1); var settings = {}; _.template('', null, settings); assert.deepEqual(settings, {}); }); - QUnit.test('#779 - delimeters are applied to unescaped text.', 1, function(assert) { + QUnit.test('#779 - delimeters are applied to unescaped text.', function(assert) { + assert.expect(1); var template = _.template('<<\nx\n>>', null, {evaluate: /<<(.*?)>>/g}); assert.strictEqual(template(), '<<\nx\n>>'); }); From bd91acc072c405ba315a7ffb703eb14e2c3508ca Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Tue, 15 Dec 2015 09:26:09 -0800 Subject: [PATCH 125/263] Upgrade ESLint The new version of ESLint has all rules off by default, which matches our existing usage of the tool. Some rules have been renamed, or their behavior has changed slightly. Most obviously, the `indent` rule is no-longer as lax about the indention of variable declarations. I think upgrading the tool is a net win, and the changes (while arguably trivial to the point of annoyance) do at least represent an increase in consistency. --- .eslintrc | 10 ++++++---- package.json | 4 ++-- test/collections.js | 14 +++++--------- test/functions.js | 3 ++- test/objects.js | 4 ++-- underscore.js | 20 +++++++++----------- 6 files changed, 26 insertions(+), 29 deletions(-) diff --git a/.eslintrc b/.eslintrc index 216f4a0c3..89bbb2f49 100644 --- a/.eslintrc +++ b/.eslintrc @@ -6,16 +6,18 @@ }, "rules": { + "array-bracket-spacing": [2], "block-scoped-var": 1, "brace-style": [1, "1tbs"], "camelcase": 2, "comma-dangle": [2, "never"], "comma-spacing": 2, + "computed-property-spacing": [2, "never"], "consistent-return": 1, "dot-notation": [2, { "allowKeywords": false }], "eol-last": 2, "eqeqeq": [2, "smart"], - "indent": [2, 2, {"indentSwitchCase": true}], + "indent": [2, 2, {"SwitchCase": 1, "VariableDeclarator": 2}], "key-spacing": 1, "linebreak-style": 2, "max-depth": [1, 4], @@ -32,7 +34,7 @@ "no-dupe-keys": 2, "no-duplicate-case": 2, "no-else-return": 1, - "no-empty-class": 2, + "no-empty-character-class": 2, "no-empty-label": 2, "no-eval": 2, "no-ex-assign": 2, @@ -61,7 +63,6 @@ "no-octal-escape": 2, "no-proto": 2, "no-redeclare": 2, - "no-reserved-keys": 2, "no-shadow": 2, "no-spaced-func": 2, "no-throw-literal": 2, @@ -75,13 +76,14 @@ "no-unused-vars": 2, "no-use-before-define": [2, "nofunc"], "no-with": 2, + "object-curly-spacing": [2, "never"], + "quote-props": 2, "quote-props": [1, "as-needed"], "quotes": [2, "single", "avoid-escape"], "radix": 2, "semi": 2, "space-after-keywords": [2, "always"], "space-before-function-paren": [2, {"anonymous": "never", "named": "never"}], - "space-in-brackets": [2, "never"], "space-infix-ops": 2, "space-return-throw-case": 2, "space-unary-ops": [2, { "words": true, "nonwords": false }], diff --git a/package.json b/package.json index 860a76819..b56d80770 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "devDependencies": { "coveralls": "^2.11.2", "docco": "*", - "eslint": "0.21.x", + "eslint": "1.10.x", "karma": "^0.13.13", "karma-qunit": "~0.1.4", "nyc": "^2.1.3", @@ -31,7 +31,7 @@ "test": "npm run test-node && npm run lint", "coverage": "nyc npm run test-node && nyc report", "coveralls": "nyc npm run test-node && nyc report --reporter=text-lcov | coveralls", - "lint": "eslint --reset underscore.js test/*.js", + "lint": "eslint underscore.js test/*.js", "test-node": "qunit-cli test/*.js", "test-browser": "npm i karma-phantomjs-launcher && karma start", "build": "uglifyjs underscore.js -c \"evaluate=false\" --comments \"/ .*/\" -m --source-map underscore-min.map --source-map-url \" \" -o underscore-min.js", diff --git a/test/collections.js b/test/collections.js index c66239f12..182f7a218 100644 --- a/test/collections.js +++ b/test/collections.js @@ -59,19 +59,15 @@ QUnit.test('Iterating objects with sketchy length properties', function(assert) { var functions = [ - 'each', 'map', 'filter', 'find', - 'some', 'every', 'max', 'min', - 'groupBy', 'countBy', 'partition', 'indexBy' + 'each', 'map', 'filter', 'find', + 'some', 'every', 'max', 'min', + 'groupBy', 'countBy', 'partition', 'indexBy' ]; var reducers = ['reduce', 'reduceRight']; var tricks = [ {length: '5'}, - { - length: { - valueOf: _.constant(5) - } - }, + {length: {valueOf: _.constant(5)}}, {length: Math.pow(2, 53) + 1}, {length: Math.pow(2, 53)}, {length: null}, @@ -821,7 +817,7 @@ var actual; try { actual = _.toArray(document.childNodes); - } catch(e) { /* ignored */ } + } catch (e) { /* ignored */ } assert.deepEqual(actual, _.map(document.childNodes, _.identity), 'works on NodeList'); } }); diff --git a/test/functions.js b/test/functions.js index db627ccc1..8d2b4dbb3 100644 --- a/test/functions.js +++ b/test/functions.js @@ -90,7 +90,8 @@ }); QUnit.test('bindAll', function(assert) { - var curly = {name: 'curly'}, moe = { + var curly = {name: 'curly'}; + var moe = { name: 'moe', getName: function() { return 'name: ' + this.name; }, sayHi: function() { return 'hi: ' + this.name; } diff --git a/test/objects.js b/test/objects.js index fff8ea962..ea3711370 100644 --- a/test/objects.js +++ b/test/objects.js @@ -128,7 +128,7 @@ try { result = {}; _.extend(result, null, void 0, {a: 1}); - } catch(e) { /* ignored */ } + } catch (e) { /* ignored */ } assert.equal(result.a, 1, 'should not error on `null` or `undefined` sources'); @@ -262,7 +262,7 @@ try { options = {}; _.defaults(options, null, void 0, {a: 1}); - } catch(e) { /* ignored */ } + } catch (e) { /* ignored */ } assert.equal(options.a, 1, 'should not error on `null` or `undefined` sources'); diff --git a/underscore.js b/underscore.js index 6fdfb3caf..19b3f7c9c 100644 --- a/underscore.js +++ b/underscore.js @@ -22,18 +22,16 @@ var ArrayProto = Array.prototype, ObjProto = Object.prototype; // Create quick reference variables for speed access to core prototypes. - var - push = ArrayProto.push, - slice = ArrayProto.slice, - toString = ObjProto.toString, - hasOwnProperty = ObjProto.hasOwnProperty; + var push = ArrayProto.push, + slice = ArrayProto.slice, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; // All **ECMAScript 5** native function implementations that we hope to use // are declared here. - var - nativeIsArray = Array.isArray, - nativeKeys = Object.keys, - nativeCreate = Object.create; + var nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeCreate = Object.create; // Naked function reference for surrogate-prototype-swapping. var Ctor = function(){}; @@ -998,8 +996,8 @@ _.mapObject = function(obj, iteratee, context) { iteratee = cb(iteratee, context); var keys = _.keys(obj), - length = keys.length, - results = {}; + length = keys.length, + results = {}; for (var index = 0; index < length; index++) { var currentKey = keys[index]; results[currentKey] = iteratee(obj[currentKey], currentKey, obj); From 1e9f5d65528f5c73d4ae0f6a6f20b4b0725c85a3 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Tue, 15 Dec 2015 14:54:28 -0800 Subject: [PATCH 126/263] Remove duplicate eslint rule definition Accidentally added this rule a second time in pull request #2390. --- .eslintrc | 1 - 1 file changed, 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index 89bbb2f49..73a97e193 100644 --- a/.eslintrc +++ b/.eslintrc @@ -77,7 +77,6 @@ "no-use-before-define": [2, "nofunc"], "no-with": 2, "object-curly-spacing": [2, "never"], - "quote-props": 2, "quote-props": [1, "as-needed"], "quotes": [2, "single", "avoid-escape"], "radix": 2, From e3ab2305cb315eeb5771469702cfc28263b8bc04 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Wed, 16 Dec 2015 19:30:31 -0800 Subject: [PATCH 127/263] Improve example for _.every() Using `_.identity` (the default predicate) as an example predicate is confusing. It implies that `predicate` is actually a required argument and if you want to evaluate the values untransformed, you must pass `_.identity` explicitly. --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 8094957db..be25e6f1a 100644 --- a/index.html +++ b/index.html @@ -602,7 +602,7 @@

      Collection Functions (Arrays or Objects)

      if a false element is found.

      -_.every([true, 1, null, 'yes'], _.identity);
      +_.every([2, 4, 5], function(num) { return num % 2 == 0; });
       => false
       
      From 2bce5b0b74a563607a93d24d7c477b522f1c39e1 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Fri, 18 Dec 2015 08:44:00 -0800 Subject: [PATCH 128/263] Remove _.where from _.matcher tests Avoids making assumptions about _.where's implementation. --- test/objects.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/objects.js b/test/objects.js index ea3711370..027e42b6c 100644 --- a/test/objects.js +++ b/test/objects.js @@ -842,14 +842,14 @@ assert.ok(_.find(stooges, _.matcher({hair: false})) === curly, 'returns a predicate that can be used by finding functions.'); assert.ok(_.find(stooges, _.matcher(moe)) === moe, 'can be used to locate an object exists in a collection.'); - assert.deepEqual(_.where([null, void 0], {a: 1}), [], 'Do not throw on null values.'); + assert.deepEqual(_.filter([null, void 0], _.matcher({a: 1})), [], 'Do not throw on null values.'); - assert.deepEqual(_.where([null, void 0], null), [null, void 0], 'null matches null'); - assert.deepEqual(_.where([null, void 0], {}), [null, void 0], 'null matches {}'); - assert.deepEqual(_.where([{b: 1}], {a: void 0}), [], 'handles undefined values (1683)'); + assert.deepEqual(_.filter([null, void 0], _.matcher(null)), [null, void 0], 'null matches null'); + assert.deepEqual(_.filter([null, void 0], _.matcher({})), [null, void 0], 'null matches {}'); + assert.deepEqual(_.filter([{b: 1}], _.matcher({a: void 0})), [], 'handles undefined values (1683)'); _.each([true, 5, NaN, null, void 0], function(item) { - assert.deepEqual(_.where([{a: 1}], item), [{a: 1}], 'treats primitives as empty'); + assert.equal(_.matcher(item)({a: 1}), true, 'treats primitives as empty'); }); function Prototest() {} From 296063a017537dd51ae456cda7504e9abcbdb089 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Sun, 3 Jan 2016 07:48:51 -0800 Subject: [PATCH 129/263] add isSymbol function --- index.html | 11 +++++++++++ test/objects.js | 11 +++++++++++ underscore.js | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index be25e6f1a..5e12e45b6 100644 --- a/index.html +++ b/index.html @@ -308,6 +308,7 @@
    • - isDate
    • - isRegExp
    • - isError
    • +
    • - isSymbol
    • - isNaN
    • - isNull
    • - isUndefined
    • @@ -1756,6 +1757,16 @@

      Object Functions

      _.isError(o_O) } => true +
+ +

+ isSymbol_.isSymbol(object) +
+ Returns true if object is a Symbol. +

+
+_.isSymbol(Symbol());
+=> true
 

diff --git a/test/objects.js b/test/objects.js index 027e42b6c..6ca0a106f 100644 --- a/test/objects.js +++ b/test/objects.js @@ -639,6 +639,17 @@ assert.strictEqual(_.isString(1), false); }); + QUnit.test('isSymbol', function(assert) { + assert.ok(!_.isSymbol(0), 'numbers are not symbols'); + assert.ok(!_.isSymbol(''), 'strings are not symbols'); + assert.ok(!_.isSymbol(_.isSymbol), 'functions are not symbols'); + if (typeof Symbol === 'function') { + assert.ok(_.isSymbol(Symbol()), 'symbols are symbols'); + assert.ok(_.isSymbol(Symbol('description')), 'described symbols are symbols'); + assert.ok(_.isSymbol(Object(Symbol())), 'boxed symbols are symbols'); + } + }); + QUnit.test('isNumber', function(assert) { assert.ok(!_.isNumber('string'), 'a string is not a number'); assert.ok(!_.isNumber(arguments), 'the arguments object is not a number'); diff --git a/underscore.js b/underscore.js index 19b3f7c9c..d8c741b22 100644 --- a/underscore.js +++ b/underscore.js @@ -1286,7 +1286,7 @@ }; // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError. - _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) { + _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol'], function(name) { _['is' + name] = function(obj) { return toString.call(obj) === '[object ' + name + ']'; }; From dd19d54709dbe4abe1641623722bfe80d8564748 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Sun, 3 Jan 2016 07:54:35 -0800 Subject: [PATCH 130/263] add missing semicolon --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index be25e6f1a..478cce5c0 100644 --- a/index.html +++ b/index.html @@ -1753,7 +1753,7 @@

Object Functions

try { throw new TypeError("Example"); } catch (o_O) { - _.isError(o_O) + _.isError(o_O); } => true
From 8f3d0507e885f64a05424fdc09a07cf77e2f676b Mon Sep 17 00:00:00 2001 From: Paul Falgout Date: Sun, 3 Jan 2016 22:48:05 -0600 Subject: [PATCH 131/263] Remove deprecated component.json Same as https://github.com/jashkenas/backbone/pull/3917 component.json has been [deprecated since June](https://github.com/componentjs/component#this-project-is-deprecated) So there's no reason to keep maintaining it. And as it is, @jashkenas [does](https://github.com/jashkenas/underscore/pull/884#issuecomment-10999128) [not](https://github.com/jashkenas/backbone/pull/2622#issuecomment-19669694) [like](https://github.com/jashkenas/backbone/pull/2811#issuecomment-26351438) [it](https://github.com/jashkenas/backbone/commit/28d345a231cfae6b4fcc6a0e394a3534269cd168#commitcomment-4557129). --- bower.json | 2 +- component.json | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) delete mode 100644 component.json diff --git a/bower.json b/bower.json index 951eabb18..d5e1d7852 100644 --- a/bower.json +++ b/bower.json @@ -2,5 +2,5 @@ "name": "underscore", "main": "underscore.js", "keywords": ["util", "functional", "server", "client", "browser"], - "ignore" : ["docs", "test", "*.yml", "CNAME", "index.html", "favicon.ico", "CONTRIBUTING.md", ".*", "component.json", "package.json", "karma.*"] + "ignore" : ["docs", "test", "*.yml", "CNAME", "index.html", "favicon.ico", "CONTRIBUTING.md", ".*", "package.json", "karma.*"] } diff --git a/component.json b/component.json deleted file mode 100644 index 8c033f640..000000000 --- a/component.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name" : "underscore", - "description" : "JavaScript's functional programming helper library.", - "keywords" : ["util", "functional", "server", "client", "browser"], - "repo" : "jashkenas/underscore", - "main" : "underscore.js", - "scripts" : ["underscore.js"], - "version" : "1.8.3", - "license" : "MIT" -} From f796632ddc0de111236fecc84aa640664de1ee5c Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Sun, 3 Jan 2016 07:23:59 -0800 Subject: [PATCH 132/263] fixes #2402: symbol support for _.isFinite --- test/objects.js | 5 +++++ underscore.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/test/objects.js b/test/objects.js index 6ca0a106f..614d1cd8f 100644 --- a/test/objects.js +++ b/test/objects.js @@ -728,6 +728,11 @@ assert.ok(_.isFinite(0), '0 is finite'); assert.ok(_.isFinite(123), 'Ints are finite'); assert.ok(_.isFinite(-12.44), 'Floats are finite'); + if (typeof Symbol === 'function') { + assert.ok(!_.isFinite(Symbol()), 'symbols are not numbers'); + assert.ok(!_.isFinite(Symbol('description')), 'described symbols are not numbers'); + assert.ok(!_.isFinite(Object(Symbol())), 'boxed symbols are not numbers'); + } }); QUnit.test('isNaN', function(assert) { diff --git a/underscore.js b/underscore.js index d8c741b22..8c2874eda 100644 --- a/underscore.js +++ b/underscore.js @@ -1311,7 +1311,7 @@ // Is a given object a finite number? _.isFinite = function(obj) { - return isFinite(obj) && !isNaN(parseFloat(obj)); + return !_.isSymbol(obj) && isFinite(obj) && !isNaN(parseFloat(obj)); }; // Is the given value `NaN`? From b887a4d0ef6d0dbe0b774d1dbb6583d481d6ad53 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sat, 9 Jan 2016 08:59:50 -0800 Subject: [PATCH 133/263] Use canonical name for `_.uniq` in documentation --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index b7b91a3cb..a5d9ec206 100644 --- a/index.html +++ b/index.html @@ -1905,7 +1905,7 @@

Utility Functions

through _.iteratee is map, find, filter, reject, every, some, max, min, sortBy, groupBy, indexBy, - countBy, sortedIndex, partition, and unique. + countBy, sortedIndex, partition, and uniq.

 var stooges = [{name: 'curly', age: 25}, {name: 'moe', age: 21}, {name: 'larry', age: 23}];

From 9de10a86878598f8d99cec2865e4a55a62051595 Mon Sep 17 00:00:00 2001
From: Nuno Arruda 
Date: Sun, 10 Jan 2016 12:48:09 +0700
Subject: [PATCH 134/263] update copyright year range

---
 LICENSE | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/LICENSE b/LICENSE
index ad0e71bc4..447239f3d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative
+Copyright (c) 2009-2016 Jeremy Ashkenas, DocumentCloud and Investigative
 Reporters & Editors
 
 Permission is hereby granted, free of charge, to any person

From 0e6d1e545451cba9838ad8883384f6872980a8be Mon Sep 17 00:00:00 2001
From: Adam Krebs 
Date: Mon, 11 Jan 2016 13:42:54 -0500
Subject: [PATCH 135/263] update copyright year

---
 underscore.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/underscore.js b/underscore.js
index 8c2874eda..9db5560d7 100644
--- a/underscore.js
+++ b/underscore.js
@@ -1,6 +1,6 @@
 //     Underscore.js 1.8.3
 //     http://underscorejs.org
-//     (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+//     (c) 2009-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
 //     Underscore may be freely distributed under the MIT license.
 
 (function() {

From 0eb701c71d058ace66953feb630cfd3d74cbbeed Mon Sep 17 00:00:00 2001
From: Jordan Eldredge 
Date: Thu, 14 Jan 2016 17:48:34 -0800
Subject: [PATCH 136/263] Add missing methods to _.iteratee docs

The following methods are transformed through `cb()` but are not listed in the
`_.itereatee` documentation:

* findIndex
* findKey
* findLastIndex
* mapObject

I've added them here, and alphabetized the list.
---
 index.html | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/index.html b/index.html
index a5d9ec206..f9f4271e9 100644
--- a/index.html
+++ b/index.html
@@ -1902,10 +1902,11 @@ 

Utility Functions

identity, an arbitrary callback, a property matcher, or a property accessor.
The full list of Underscore methods that transform predicates - through _.iteratee is map, find, - filter, reject, every, some, max, - min, sortBy, groupBy, indexBy, - countBy, sortedIndex, partition, and uniq. + through _.iteratee is countBy, every, filter, + find, findIndex, findKey, findLastIndex, + groupBy, indexBy, map, mapObject, max, + min, partition, reject, some, sortBy, + sortedIndex, and uniq

 var stooges = [{name: 'curly', age: 25}, {name: 'moe', age: 21}, {name: 'larry', age: 23}];

From 1f607d47c207acef34030eb3e7bfe69d3f16c92d Mon Sep 17 00:00:00 2001
From: Jordan Eldredge 
Date: Tue, 19 Jan 2016 18:16:47 -0800
Subject: [PATCH 137/263] Clean up assertions for _.intersection()

---
 test/arrays.js | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/test/arrays.js b/test/arrays.js
index 6f9cd67b6..41931c64a 100644
--- a/test/arrays.js
+++ b/test/arrays.js
@@ -186,20 +186,18 @@
 
   QUnit.test('intersection', function(assert) {
     var stooges = ['moe', 'curly', 'larry'], leaders = ['moe', 'groucho'];
-    assert.deepEqual(_.intersection(stooges, leaders), ['moe'], 'can take the set intersection of two arrays');
+    assert.deepEqual(_.intersection(stooges, leaders), ['moe'], 'can find the set intersection of two arrays');
     assert.deepEqual(_(stooges).intersection(leaders), ['moe'], 'can perform an OO-style intersection');
     var result = (function(){ return _.intersection(arguments, leaders); }('moe', 'curly', 'larry'));
     assert.deepEqual(result, ['moe'], 'works on an arguments object');
     var theSixStooges = ['moe', 'moe', 'curly', 'curly', 'larry', 'larry'];
     assert.deepEqual(_.intersection(theSixStooges, leaders), ['moe'], 'returns a duplicate-free array');
     result = _.intersection([2, 4, 3, 1], [1, 2, 3]);
-    assert.deepEqual(result, [2, 3, 1], 'preserves order of first array');
+    assert.deepEqual(result, [2, 3, 1], 'preserves the order of the first array');
     result = _.intersection(null, [1, 2, 3]);
-    assert.equal(Object.prototype.toString.call(result), '[object Array]', 'returns an empty array when passed null as first argument');
-    assert.equal(result.length, 0, 'returns an empty array when passed null as first argument');
+    assert.deepEqual(result, [], 'returns an empty array when passed null as the first argument');
     result = _.intersection([1, 2, 3], null);
-    assert.equal(Object.prototype.toString.call(result), '[object Array]', 'returns an empty array when passed null as argument beyond the first');
-    assert.equal(result.length, 0, 'returns an empty array when passed null as argument beyond the first');
+    assert.deepEqual(result, [], 'returns an empty array when passed null as an argument beyond the first');
   });
 
   QUnit.test('union', function(assert) {

From 2e3cc40d06ad3c49a9996f13e481fca67b369c06 Mon Sep 17 00:00:00 2001
From: Jordan Eldredge 
Date: Wed, 20 Jan 2016 09:16:30 -0800
Subject: [PATCH 138/263] Clean up assertions for _.union()

---
 test/arrays.js | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/test/arrays.js b/test/arrays.js
index 6f9cd67b6..ee4c92e2b 100644
--- a/test/arrays.js
+++ b/test/arrays.js
@@ -204,18 +204,21 @@
 
   QUnit.test('union', function(assert) {
     var result = _.union([1, 2, 3], [2, 30, 1], [1, 40]);
-    assert.deepEqual(result, [1, 2, 3, 30, 40], 'takes the union of a list of arrays');
+    assert.deepEqual(result, [1, 2, 3, 30, 40], 'can find the union of a list of arrays');
+
+    result = _([1, 2, 3]).union([2, 30, 1], [1, 40]);
+    assert.deepEqual(result, [1, 2, 3, 30, 40], 'can perform an OO-style union');
 
     result = _.union([1, 2, 3], [2, 30, 1], [1, 40, [1]]);
-    assert.deepEqual(result, [1, 2, 3, 30, 40, [1]], 'takes the union of a list of nested arrays');
+    assert.deepEqual(result, [1, 2, 3, 30, 40, [1]], 'can find the union of a list of nested arrays');
+
+    result = _.union([10, 20], [1, 30, 10], [0, 40]);
+    assert.deepEqual(result, [10, 20, 1, 30, 0, 40], 'orders values by their first encounter');
 
-    var args = null;
-    (function(){ args = arguments; }(1, 2, 3));
-    result = _.union(args, [2, 30, 1], [1, 40]);
-    assert.deepEqual(result, [1, 2, 3, 30, 40], 'takes the union of a list of arrays');
+    result = (function(){ return _.union(arguments, [2, 30, 1], [1, 40]); }(1, 2, 3));
+    assert.deepEqual(result, [1, 2, 3, 30, 40], 'works on an arguments object');
 
-    result = _.union([1, 2, 3], 4);
-    assert.deepEqual(result, [1, 2, 3], 'restrict the union to arrays only');
+    assert.deepEqual(_.union([1, 2, 3], 4), [1, 2, 3], 'restricts the union to arrays only');
   });
 
   QUnit.test('difference', function(assert) {

From 1e69d38a5a8f24c1330c29e748fa6174436c122b Mon Sep 17 00:00:00 2001
From: Jordan Eldredge 
Date: Wed, 20 Jan 2016 09:58:48 -0800
Subject: [PATCH 139/263] Clean up assertions for _.difference()

---
 test/arrays.js | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/test/arrays.js b/test/arrays.js
index 90e2a4b91..748edea4f 100644
--- a/test/arrays.js
+++ b/test/arrays.js
@@ -221,10 +221,19 @@
 
   QUnit.test('difference', function(assert) {
     var result = _.difference([1, 2, 3], [2, 30, 40]);
-    assert.deepEqual(result, [1, 3], 'takes the difference of two arrays');
+    assert.deepEqual(result, [1, 3], 'can find the difference of two arrays');
+
+    result = _([1, 2, 3]).difference([2, 30, 40]);
+    assert.deepEqual(result, [1, 3], 'can perform an OO-style difference');
 
     result = _.difference([1, 2, 3, 4], [2, 30, 40], [1, 11, 111]);
-    assert.deepEqual(result, [3, 4], 'takes the difference of three arrays');
+    assert.deepEqual(result, [3, 4], 'can find the difference of three arrays');
+
+    result = _.difference([8, 9, 3, 1], [3, 8]);
+    assert.deepEqual(result, [9, 1], 'preserves the order of the first array');
+
+    result = (function(){ return _.difference(arguments, [2, 30, 40]); }(1, 2, 3));
+    assert.deepEqual(result, [1, 3], 'works on an arguments object');
 
     result = _.difference([1, 2, 3], 1);
     assert.deepEqual(result, [1, 2, 3], 'restrict the difference to arrays only');

From 34b45d8b6110a4ea18ed2b4629877fb4a2002333 Mon Sep 17 00:00:00 2001
From: Craig Martin 
Date: Thu, 21 Jan 2016 08:58:28 -0500
Subject: [PATCH 140/263] add gitter and stackoverflow links to readme

---
 README.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/README.md b/README.md
index c2ba2590c..f29a03c51 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,10 @@ without extending any core JavaScript objects.
 For Docs, License, Tests, and pre-packed downloads, see:
 http://underscorejs.org
 
+For support and questions, please use
+[the gitter channel](https://gitter.im/jashkenas/underscore)
+or [stackoverflow](http://stackoverflow.com/search?q=underscore.js)
+
 Underscore is an open-sourced component of DocumentCloud:
 https://github.com/documentcloud
 

From b7925e0819557abe4f113df5ee2e19ff64e77776 Mon Sep 17 00:00:00 2001
From: Craig Martin 
Date: Wed, 27 Jan 2016 09:38:50 -0500
Subject: [PATCH 141/263] disable npm's progress bar in travis ci for quicker
 install

---
 .travis.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.travis.yml b/.travis.yml
index 172af86c3..f0e6ad148 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -23,6 +23,7 @@ notifications:
   email: false
 env:
   global:
+  - NPM_CONFIG_PROGRESS="false"
   - secure: bDZSBQfqr21hCayjcZ20IxrV6+XGhxQPFIfwWqEKLrF93Gu8LLVjZRxXE/mE8I8N4Z5WtDNb4ZHrm/TTzmcPa5MuHgIxEdknQCncobH8oimwc83SHwEPk6okeNKl39VlCjvvnmoe/V/KpnknuYn3Rqghtl/Uv9KLpCwskwjTtcw=
   - secure: SRECgXuwcZTcD3GVxTS2bYNgRyye4vq6BLrV2PH9FyNenowsKQR2EwlC/dppc1Q8NWMgv79J/R96q9JOFh+mEH9L5dlBb2yhnGH8amVeM/ChAJHT/F8YktKM453uVpz5fR00QcCQDDUOx6Pvx374ID0OKNpWKAkQBWA9mPTsLnE=
   matrix: BROWSER=false

From 65b5398d432083727e3f8f0461f781e6c5fe3001 Mon Sep 17 00:00:00 2001
From: Jordan Eldredge 
Date: Wed, 27 Jan 2016 08:05:06 -0800
Subject: [PATCH 142/263] Fix closing  tags in _.iteratee docs

I forgot to close these tags in the #2414.
---
 index.html | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/index.html b/index.html
index f9f4271e9..6897cbeaf 100644
--- a/index.html
+++ b/index.html
@@ -1902,11 +1902,11 @@ 

Utility Functions

identity, an arbitrary callback, a property matcher, or a property accessor.
The full list of Underscore methods that transform predicates - through _.iteratee is countBy, every, filter, - find, findIndex, findKey, findLastIndex, - groupBy, indexBy, map, mapObject, max, - min, partition, reject, some, sortBy, - sortedIndex, and uniq + through _.iteratee is countBy, every, filter, + find, findIndex, findKey, findLastIndex, + groupBy, indexBy, map, mapObject, max, + min, partition, reject, some, sortBy, + sortedIndex, and uniq

 var stooges = [{name: 'curly', age: 25}, {name: 'moe', age: 21}, {name: 'larry', age: 23}];

From ed96600c23798168f998eb873bde629f4bbbcc67 Mon Sep 17 00:00:00 2001
From: Jordan Eldredge 
Date: Wed, 27 Jan 2016 08:18:10 -0800
Subject: [PATCH 143/263] Separate object-oriented style and chaining in docs

I think the fact that the object-oriented style and chaining share
implementation details has lead us to conflate them in the docs. In practice,
the two features have almost nothing to do with each other.

The existing documentation could easily lead one to believe that they could
start a chain using the OOP style.

This confusion is compounded by a few factors:

1. Lodash supports chains starting with the OOP wrapper.
2. The fact that we share names with native Array methods can lead to manual
   tests that appear to work.

For example:

    _([1,2,3]).reverse().map(function(n){ return n * 2; });

This appears to be an example of successfully starting a chain with the OOP
style. The astute observer will note that the lack of a `.value()` call, proves
this is not Underscore chaining, but a new user attempting to clarify the
behavior for herself may not catch that detail.
---
 index.html | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/index.html b/index.html
index 6897cbeaf..60c290e5a 100644
--- a/index.html
+++ b/index.html
@@ -334,6 +334,9 @@
       
  • - template
  • + + OOP Style + Chaining @@ -1861,7 +1864,7 @@

    Utility Functions

    iteratee is called with an index argument. Produces an array of the returned values.
    - Note: this example uses the chaining syntax. + Note: this example uses the object-oriented syntax.

     _(3).times(function(n){ genie.grantWishNumber(n); });
    @@ -2055,7 +2058,7 @@

    Utility Functions

    </script>
    -

    Chaining

    +

    Object-Oriented Style

    You can use Underscore in either an object-oriented or a functional style, @@ -2067,6 +2070,8 @@

    Chaining

    _.map([1, 2, 3], function(n){ return n * 2; }); _([1, 2, 3]).map(function(n){ return n * 2; });
    +

    Chaining

    +

    Calling chain will cause all future method calls to return wrapped objects. When you've finished the computation, call @@ -2119,13 +2124,13 @@

    Chaining

    - value_(obj).value() + value_.chain(obj).value()
    Extracts the value of a wrapped object.

    -_([1, 2, 3]).value();
    -=> [1, 2, 3]
    +_.chain([1, 2, 3]).reverse().value();
    +=> [3, 2, 1]
     
    From a95b3b13959cc4fe038ebab8c031ba4ccd2e0f3a Mon Sep 17 00:00:00 2001 From: Mark Sanghoon Kim Date: Wed, 27 Jan 2016 23:34:46 -0800 Subject: [PATCH 144/263] Added punctuation to end of comments to match style --- underscore.js | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/underscore.js b/underscore.js index 9db5560d7..78c709bd3 100644 --- a/underscore.js +++ b/underscore.js @@ -93,7 +93,7 @@ return _.property(value); }; - // An external wrapper for the internal callback generator + // An external wrapper for the internal callback generator. _.iteratee = function(value, context) { return cb(value, context, Infinity); }; @@ -504,7 +504,7 @@ for (var i = 0, length = getLength(input); i < length; i++) { var value = input[i]; if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) { - // Flatten current level of array or arguments object + // Flatten current level of array or arguments object. if (shallow) { var j = 0, len = value.length; while (j < len) output[idx++] = value[j++]; @@ -592,7 +592,7 @@ }); // Complement of _.zip. Unzip accepts an array of arrays and groups - // each array's elements on shared indices + // each array's elements on shared indices. _.unzip = function(array) { var length = array && _.max(array, getLength).length || 0; var result = Array(length); @@ -622,7 +622,7 @@ return result; }; - // Generator function to create the findIndex and findLastIndex functions + // Generator function to create the findIndex and findLastIndex functions. var createPredicateIndexFinder = function(dir) { return function(array, predicate, context) { predicate = cb(predicate, context); @@ -635,7 +635,7 @@ }; }; - // Returns the first index on an array-like that passes a predicate test + // Returns the first index on an array-like that passes a predicate test. _.findIndex = createPredicateIndexFinder(1); _.findLastIndex = createPredicateIndexFinder(-1); @@ -652,7 +652,7 @@ return low; }; - // Generator function to create the indexOf and lastIndexOf functions + // Generator function to create the indexOf and lastIndexOf functions. var createIndexFinder = function(dir, predicateFind, sortedIndex) { return function(array, item, idx) { var i = 0, length = getLength(array); @@ -707,7 +707,7 @@ }; // Split an **array** into several arrays containing **count** or less elements - // of initial array + // of initial array. _.chunk = function(array, count) { if (count == null || count < 1) return []; @@ -723,7 +723,7 @@ // ------------------ // Determines whether to execute a function as a constructor - // or a normal function with the provided arguments + // or a normal function with the provided arguments. var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) { if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args); var self = baseCreate(sourceFunc.prototype); @@ -959,7 +959,7 @@ }; // Retrieve the names of an object's own properties. - // Delegates to **ECMAScript 5**'s native `Object.keys` + // Delegates to **ECMAScript 5**'s native `Object.keys`. _.keys = function(obj) { if (!_.isObject(obj)) return []; if (nativeKeys) return nativeKeys(obj); @@ -991,8 +991,8 @@ return values; }; - // Returns the results of applying the iteratee to each element of the object - // In contrast to _.map it returns an object + // Returns the results of applying the iteratee to each element of the object. + // In contrast to _.map it returns an object. _.mapObject = function(obj, iteratee, context) { iteratee = cb(iteratee, context); var keys = _.keys(obj), @@ -1027,7 +1027,7 @@ }; // Return a sorted list of the function names available on the object. - // Aliased as `methods` + // Aliased as `methods`. _.functions = _.methods = function(obj) { var names = []; for (var key in obj) { @@ -1058,11 +1058,11 @@ // Extend a given object with all the properties in passed-in object(s). _.extend = createAssigner(_.allKeys); - // Assigns a given object with all the own properties in the passed-in object(s) + // Assigns a given object with all the own properties in the passed-in object(s). // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) _.extendOwn = _.assign = createAssigner(_.keys); - // Returns the first key on an object that passes a predicate test + // Returns the first key on an object that passes a predicate test. _.findKey = function(obj, predicate, context) { predicate = cb(predicate, context); var keys = _.keys(obj), key; @@ -1185,7 +1185,7 @@ return '' + a === '' + b; case '[object Number]': // `NaN`s are equivalent, but non-reflexive. - // Object(NaN) is equivalent to NaN + // Object(NaN) is equivalent to NaN. if (+a !== +a) return +b !== +b; // An `egal` comparison is performed for other numeric values. return +a === 0 ? 1 / +a === 1 / b : +a === +b; @@ -1420,7 +1420,7 @@ var escaper = function(match) { return map[match]; }; - // Regexes for identifying a key that needs to be escaped + // Regexes for identifying a key that needs to be escaped. var source = '(?:' + _.keys(map).join('|') + ')'; var testRegexp = RegExp(source); var replaceRegexp = RegExp(source, 'g'); From 7ba2fb01755eec7a6a33878ed6466ae948d9140e Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sat, 13 Feb 2016 08:59:39 -0800 Subject: [PATCH 145/263] Remove superfulous _.identitys By removing these we avoid unnecessarily transforming out predicate through `optimizeCb()`. --- underscore.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/underscore.js b/underscore.js index 9db5560d7..4970e5b3e 100644 --- a/underscore.js +++ b/underscore.js @@ -442,7 +442,7 @@ // Keep surrogate pair characters together return obj.match(reStrSymbol); } - if (isArrayLike(obj)) return _.map(obj, _.identity); + if (isArrayLike(obj)) return _.map(obj); return _.values(obj); }; @@ -494,7 +494,7 @@ // Trim out all falsy values from an array. _.compact = function(array) { - return _.filter(array, _.identity); + return _.filter(array); }; // Internal implementation of a recursive `flatten` function. From 79626f63612999965fc54bdd069dda8c3421705f Mon Sep 17 00:00:00 2001 From: vaverix Date: Wed, 17 Feb 2016 18:59:30 +0100 Subject: [PATCH 146/263] Docs copy-paste mistake with _.unzip function There is a misinformation about "unzip" function in the docs. Seems like a example was copied from "zip" description and someone forgot to change this. --- index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 6897cbeaf..7571f7b60 100644 --- a/index.html +++ b/index.html @@ -978,8 +978,8 @@

    Array Functions

    and so on.

    -_.unzip([['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]])
    -=> ["moe", 30, true], ["larry", 40, false], ["curly", 50, false]
    +_.unzip([["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]);
    +=> [['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]]
     

    From 723aaff63df45520eeea4daabf35623427c712d1 Mon Sep 17 00:00:00 2001 From: Adam Krebs Date: Mon, 29 Feb 2016 10:39:49 -0500 Subject: [PATCH 147/263] Two symbols are equal by valueOf(). Closes #2450 --- test/objects.js | 8 ++++++++ underscore.js | 3 +++ 2 files changed, 11 insertions(+) diff --git a/test/objects.js b/test/objects.js index 614d1cd8f..4226f5cbb 100644 --- a/test/objects.js +++ b/test/objects.js @@ -565,6 +565,14 @@ assert.equal(_.isEqual({a: 0}, {a: -0}), false); assert.equal(_.isEqual([NaN], [NaN]), true); assert.equal(_.isEqual({a: NaN}, {a: NaN}), true); + + if (typeof Symbol !== 'undefined') { + var symbol = Symbol('x'); + assert.strictEqual(_.isEqual(symbol, symbol), true, 'A symbol is equal to itself'); + assert.strictEqual(_.isEqual(symbol, Object(symbol)), true, 'Even when wrapped in Object()'); + assert.strictEqual(_.isEqual(symbol, null), false, 'Different types are not equal'); + } + }); QUnit.test('isEmpty', function(assert) { diff --git a/underscore.js b/underscore.js index 3b3b123ca..fc9036322 100644 --- a/underscore.js +++ b/underscore.js @@ -20,6 +20,7 @@ // Save bytes in the minified (but not gzipped) version: var ArrayProto = Array.prototype, ObjProto = Object.prototype; + var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null; // Create quick reference variables for speed access to core prototypes. var push = ArrayProto.push, @@ -1195,6 +1196,8 @@ // millisecond representations. Note that invalid dates with millisecond representations // of `NaN` are not equivalent. return +a === +b; + case '[object Symbol]': + return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b); } var areArrays = className === '[object Array]'; From 46dd86ee1f24152d3d6fc90f1e6781bc2472f116 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Mon, 29 Feb 2016 14:16:49 -0800 Subject: [PATCH 148/263] Add a code of conduct doc. [ci skip] --- CODE_OF_CONDUCT.md | 74 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..8ef5923b1 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at jashkenas@gmail.com. The project team +will review and investigate all complaints, and will respond in a way that it deems +appropriate to the circumstances. The project team is obligated to maintain +confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ From 72fab7c2eea52e8416db3c017e290ec90a78357a Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sat, 5 Mar 2016 10:17:34 -0800 Subject: [PATCH 149/263] Clean up text wrapping --- index.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index 1aa67a27f..4b23e2bcd 100644 --- a/index.html +++ b/index.html @@ -2164,7 +2164,8 @@

    Underscore.php, - a PHP port of the functions that are applicable in both languages. Tailored for PHP 5.4 and made with data-type tolerance in mind. + a PHP port of the functions that are applicable in both languages. + Tailored for PHP 5.4 and made with data-type tolerance in mind. (source)

    From febdde9c1f16a9e8d5753c9c17310544a4e03265 Mon Sep 17 00:00:00 2001 From: Paul Falgout Date: Tue, 8 Mar 2016 00:22:55 -0600 Subject: [PATCH 150/263] Add links to COC --- CONTRIBUTING.md | 2 ++ README.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8b22557d1..7e9a579e4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,7 @@ ## How to contribute to Underscore.js +* This project adheres to a [code of conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. + * Before you open a ticket or send a pull request, [search](https://github.com/jashkenas/underscore/issues) for previous discussions about the same feature or issue. Add to the earlier ticket if you find one. * If you're proposing a new feature, make sure it isn't already implemented in [Underscore-Contrib](https://github.com/documentcloud/underscore-contrib). diff --git a/README.md b/README.md index f29a03c51..a99db1895 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ Underscore.js is a utility-belt library for JavaScript that provides support for the usual functional suspects (each, map, reduce, filter...) without extending any core JavaScript objects. +This project adheres to a [code of conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. + For Docs, License, Tests, and pre-packed downloads, see: http://underscorejs.org From b10e9759ba2c995fe7e724ce1bc9d8797ac1a9d9 Mon Sep 17 00:00:00 2001 From: gwpmad Date: Wed, 9 Mar 2016 15:29:01 +0000 Subject: [PATCH 151/263] isMap feature and associated tests --- test/objects.js | 16 ++++++++++++++++ underscore.js | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/test/objects.js b/test/objects.js index 4226f5cbb..45ec2b53d 100644 --- a/test/objects.js +++ b/test/objects.js @@ -681,6 +681,22 @@ assert.ok(_.isBoolean(false), 'and so is false'); }); + QUnit.test('isMap', function(assert) { + var keyString = 'a string', keyObj = {}, keyFunc = function() {}; + var obj = new Map([[keyString, 'value one'], [keyObj, 'value two'], + [keyFunc, 'value three']]); + + assert.ok(!_.isMap('string'), 'a string is not a map'); + assert.ok(!_.isMap(2), 'a number is not a map'); + assert.ok(!_.isMap({}), 'an object is not a map'); + assert.ok(!_.isMap(false), 'a boolean is not a map'); + assert.ok(!_.isMap(void 0), 'undefined is not a map'); + assert.ok(!_.isMap([1, 2, 3]), 'an array is not a map'); + assert.ok(!_.isMap(new Set([['key', 'value']])), 'a set is not a map'); + assert.ok(!_.isMap(new WeakMap([[{x: 1}, 'value']])), 'a weak map is not a map'); + assert.ok(_.isMap(obj), 'but a map is'); + }); + QUnit.test('isFunction', function(assert) { assert.ok(!_.isFunction(void 0), 'undefined vars are not functions'); assert.ok(!_.isFunction([1, 2, 3]), 'arrays are not functions'); diff --git a/underscore.js b/underscore.js index fc9036322..cc7ddaa19 100644 --- a/underscore.js +++ b/underscore.js @@ -1289,7 +1289,7 @@ }; // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError. - _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol'], function(name) { + _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map'], function(name) { _['is' + name] = function(obj) { return toString.call(obj) === '[object ' + name + ']'; }; From 17da693ef9961fd911201551029cab2819629609 Mon Sep 17 00:00:00 2001 From: mahmudh Date: Wed, 9 Mar 2016 15:43:16 +0000 Subject: [PATCH 152/263] isWeakMap feature and associated tests --- test/objects.js | 17 ++++++++++++++++- underscore.js | 4 ++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/test/objects.js b/test/objects.js index 45ec2b53d..56970c2a3 100644 --- a/test/objects.js +++ b/test/objects.js @@ -693,10 +693,25 @@ assert.ok(!_.isMap(void 0), 'undefined is not a map'); assert.ok(!_.isMap([1, 2, 3]), 'an array is not a map'); assert.ok(!_.isMap(new Set([['key', 'value']])), 'a set is not a map'); - assert.ok(!_.isMap(new WeakMap([[{x: 1}, 'value']])), 'a weak map is not a map'); + assert.ok(!_.isMap(new WeakMap([[{x: 1}, 'value']])), 'a weakmap is not a map'); assert.ok(_.isMap(obj), 'but a map is'); }); + QUnit.test('isWeakMap', function(assert) { + var keyObj = {}; + var obj = new WeakMap([[keyObj, 'value']]); + + assert.ok(!_.isWeakMap('string'), 'a string is not a weakmap'); + assert.ok(!_.isWeakMap(2), 'a number is not a weakmap'); + assert.ok(!_.isWeakMap({}), 'an object is not a weakmap'); + assert.ok(!_.isWeakMap(false), 'a boolean is not a weakmap'); + assert.ok(!_.isWeakMap(void 0), 'undefined is not a weakmap'); + assert.ok(!_.isWeakMap([1, 2, 3]), 'an array is not a weakmap'); + assert.ok(!_.isWeakMap(new Set([['key', 'value']])), 'a set is not a weakmap'); + assert.ok(!_.isWeakMap(new Map([[{x: 1}, 'value']])), 'a map is not a weakmap'); + assert.ok(_.isWeakMap(obj), 'but a weakmap is'); + }); + QUnit.test('isFunction', function(assert) { assert.ok(!_.isFunction(void 0), 'undefined vars are not functions'); assert.ok(!_.isFunction([1, 2, 3]), 'arrays are not functions'); diff --git a/underscore.js b/underscore.js index cc7ddaa19..1471d1a25 100644 --- a/underscore.js +++ b/underscore.js @@ -1288,8 +1288,8 @@ return type === 'function' || type === 'object' && !!obj; }; - // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError. - _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map'], function(name) { + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError, isMap, isWeakMap. + _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap'], function(name) { _['is' + name] = function(obj) { return toString.call(obj) === '[object ' + name + ']'; }; From c18ff6e474be5dbc74db40df755f4644d52fe0b0 Mon Sep 17 00:00:00 2001 From: gwpmad Date: Wed, 9 Mar 2016 16:04:43 +0000 Subject: [PATCH 153/263] isSet feature and associated tests --- test/objects.js | 15 +++++++++++++++ underscore.js | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/test/objects.js b/test/objects.js index 56970c2a3..419619a73 100644 --- a/test/objects.js +++ b/test/objects.js @@ -712,6 +712,21 @@ assert.ok(_.isWeakMap(obj), 'but a weakmap is'); }); + QUnit.test('isSet', function(assert) { + var obj = new Set([1, 'string', false, {}]); + + assert.ok(!_.isSet('string'), 'a string is not a set'); + assert.ok(!_.isSet(2), 'a number is not a set'); + assert.ok(!_.isSet({}), 'an object is not a set'); + assert.ok(!_.isSet(false), 'a boolean is not a set'); + assert.ok(!_.isSet(void 0), 'undefined is not a set'); + assert.ok(!_.isSet([1, 2, 3]), 'an array is not a set'); + assert.ok(!_.isSet(new Map([['key', 'value']])), 'a map is not a set'); + assert.ok(!_.isSet(new WeakMap([[{x: 1}, 'value']])), 'a weakmap is not a set'); + assert.ok(!_.isSet(new WeakSet([{x: 1}, {y: 2}])), 'a weakset is not a set'); + assert.ok(_.isSet(obj), 'but a set is'); + }); + QUnit.test('isFunction', function(assert) { assert.ok(!_.isFunction(void 0), 'undefined vars are not functions'); assert.ok(!_.isFunction([1, 2, 3]), 'arrays are not functions'); diff --git a/underscore.js b/underscore.js index 1471d1a25..ad4de20da 100644 --- a/underscore.js +++ b/underscore.js @@ -1288,8 +1288,8 @@ return type === 'function' || type === 'object' && !!obj; }; - // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError, isMap, isWeakMap. - _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap'], function(name) { + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError, isMap, isWeakMap, isSet. + _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set'], function(name) { _['is' + name] = function(obj) { return toString.call(obj) === '[object ' + name + ']'; }; From 5c5e38e587289a5dda8863ef80a1f99abe35edb9 Mon Sep 17 00:00:00 2001 From: mahmudh Date: Wed, 9 Mar 2016 16:14:02 +0000 Subject: [PATCH 154/263] isWeakSet feature and associated tests --- test/objects.js | 15 +++++++++++++++ underscore.js | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/test/objects.js b/test/objects.js index 419619a73..ceaf12a1d 100644 --- a/test/objects.js +++ b/test/objects.js @@ -727,6 +727,21 @@ assert.ok(_.isSet(obj), 'but a set is'); }); + QUnit.test('isWeakSet', function(assert) { + var obj = new WeakSet([{x: 1}, {y: 'string'}, {z: [1, 2, 3]}]); + + assert.ok(!_.isWeakSet('string'), 'a string is not a weakset'); + assert.ok(!_.isWeakSet(2), 'a number is not a weakset'); + assert.ok(!_.isWeakSet({}), 'an object is not a weakset'); + assert.ok(!_.isWeakSet(false), 'a boolean is not a weakset'); + assert.ok(!_.isWeakSet(void 0), 'undefined is not a weakset'); + assert.ok(!_.isWeakSet([1, 2, 3]), 'an array is not a weakset'); + assert.ok(!_.isWeakSet(new Map([['key', 'value']])), 'a map is not a weakset'); + assert.ok(!_.isWeakSet(new WeakMap([[{x: 1}, 'value']])), 'a weakmap is not a weakset'); + assert.ok(!_.isWeakSet(new Set([1, 'string', false])), 'a set is not a weakset'); + assert.ok(_.isWeakSet(obj), 'but a weakset is'); + }); + QUnit.test('isFunction', function(assert) { assert.ok(!_.isFunction(void 0), 'undefined vars are not functions'); assert.ok(!_.isFunction([1, 2, 3]), 'arrays are not functions'); diff --git a/underscore.js b/underscore.js index ad4de20da..1bd886d4e 100644 --- a/underscore.js +++ b/underscore.js @@ -1288,8 +1288,8 @@ return type === 'function' || type === 'object' && !!obj; }; - // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError, isMap, isWeakMap, isSet. - _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set'], function(name) { + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError, isMap, isWeakMap, isSet, isWeakSet. + _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'], function(name) { _['is' + name] = function(obj) { return toString.call(obj) === '[object ' + name + ']'; }; From d7eee49d7d7239c132c7ef839d6c280730cfa9a4 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Tue, 8 Mar 2016 06:14:31 -0800 Subject: [PATCH 155/263] add moderation sections to code of conduct --- CODE_OF_CONDUCT.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 8ef5923b1..6b777fb6d 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -59,12 +59,31 @@ reported by contacting the project team at jashkenas@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. +## Moderation + +Edits of another user's comment must be clearly marked with "**edit**", the +moderator's username, and a timestamp for each occurrence. The only acceptable +reasons for editing another user's comment are: + +1. to edit out violations of Our Pledge. These edits must include a rationale. +2. to direct future readers to a relevant point later in the conversation + (usually the resolution). These edits must be append-only. + +Deletion of another user's comment is only acceptable when the comment includes +no original value, such as "+1", ":+1:", or "me too". + +## Self-Moderation + +Edits of your own comment after someone has responded must be append-only and +clearly marked with "**edit**". Typographical and formatting fixes to your own +comment which do not affect its meaning are exempt from this requirement. +Deletion of your own comment is only acceptable before any later comments have +been posted. + ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, From 00a2b715b6ddb56b3d1ff3d6d9f019a3d80da603 Mon Sep 17 00:00:00 2001 From: gwpmad Date: Wed, 9 Mar 2016 17:47:02 +0000 Subject: [PATCH 156/263] fix tests for browsers that do not support Map, Set, WeakMap or WeakSet --- test/objects.js | 81 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 24 deletions(-) diff --git a/test/objects.js b/test/objects.js index ceaf12a1d..fa1d9e3e3 100644 --- a/test/objects.js +++ b/test/objects.js @@ -682,53 +682,76 @@ }); QUnit.test('isMap', function(assert) { - var keyString = 'a string', keyObj = {}, keyFunc = function() {}; - var obj = new Map([[keyString, 'value one'], [keyObj, 'value two'], - [keyFunc, 'value three']]); - assert.ok(!_.isMap('string'), 'a string is not a map'); assert.ok(!_.isMap(2), 'a number is not a map'); assert.ok(!_.isMap({}), 'an object is not a map'); assert.ok(!_.isMap(false), 'a boolean is not a map'); assert.ok(!_.isMap(void 0), 'undefined is not a map'); assert.ok(!_.isMap([1, 2, 3]), 'an array is not a map'); - assert.ok(!_.isMap(new Set([['key', 'value']])), 'a set is not a map'); - assert.ok(!_.isMap(new WeakMap([[{x: 1}, 'value']])), 'a weakmap is not a map'); - assert.ok(_.isMap(obj), 'but a map is'); + if (typeof Set === 'function') { + assert.ok(!_.isMap(new Set()), 'a set is not a map'); + } + if (typeof WeakSet === 'function') { + assert.ok(!_.isMap(new WeakSet()), 'a weakset is not a map'); + } + if (typeof WeakMap === 'function') { + assert.ok(!_.isMap(new WeakMap()), 'a weakmap is not a map'); + } + if (typeof Map === 'function') { + var keyString = 'a string'; + var obj = new Map(); + obj.set(keyString, 'value'); + assert.ok(_.isMap(obj), 'but a map is'); + } }); QUnit.test('isWeakMap', function(assert) { - var keyObj = {}; - var obj = new WeakMap([[keyObj, 'value']]); - assert.ok(!_.isWeakMap('string'), 'a string is not a weakmap'); assert.ok(!_.isWeakMap(2), 'a number is not a weakmap'); assert.ok(!_.isWeakMap({}), 'an object is not a weakmap'); assert.ok(!_.isWeakMap(false), 'a boolean is not a weakmap'); assert.ok(!_.isWeakMap(void 0), 'undefined is not a weakmap'); assert.ok(!_.isWeakMap([1, 2, 3]), 'an array is not a weakmap'); - assert.ok(!_.isWeakMap(new Set([['key', 'value']])), 'a set is not a weakmap'); - assert.ok(!_.isWeakMap(new Map([[{x: 1}, 'value']])), 'a map is not a weakmap'); - assert.ok(_.isWeakMap(obj), 'but a weakmap is'); + if (typeof Set === 'function') { + assert.ok(!_.isWeakMap(new Set()), 'a set is not a weakmap'); + } + if (typeof WeakSet === 'function') { + assert.ok(!_.isWeakMap(new WeakSet()), 'a weakset is not a weakmap'); + } + if (typeof Map === 'function') { + assert.ok(!_.isWeakMap(new Map()), 'a map is not a weakmap'); + } + if (typeof WeakMap === 'function') { + var keyObj = {}, obj = new WeakMap(); + obj.set(keyObj, 'value'); + assert.ok(_.isWeakMap(obj), 'but a weakmap is'); + } }); QUnit.test('isSet', function(assert) { - var obj = new Set([1, 'string', false, {}]); - assert.ok(!_.isSet('string'), 'a string is not a set'); assert.ok(!_.isSet(2), 'a number is not a set'); assert.ok(!_.isSet({}), 'an object is not a set'); assert.ok(!_.isSet(false), 'a boolean is not a set'); assert.ok(!_.isSet(void 0), 'undefined is not a set'); assert.ok(!_.isSet([1, 2, 3]), 'an array is not a set'); - assert.ok(!_.isSet(new Map([['key', 'value']])), 'a map is not a set'); - assert.ok(!_.isSet(new WeakMap([[{x: 1}, 'value']])), 'a weakmap is not a set'); - assert.ok(!_.isSet(new WeakSet([{x: 1}, {y: 2}])), 'a weakset is not a set'); - assert.ok(_.isSet(obj), 'but a set is'); + if (typeof Map === 'function') { + assert.ok(!_.isSet(new Map()), 'a map is not a set'); + } + if (typeof WeakMap === 'function') { + assert.ok(!_.isSet(new WeakMap()), 'a weakmap is not a set'); + } + if (typeof WeakSet === 'function') { + assert.ok(!_.isSet(new WeakSet()), 'a weakset is not a set'); + } + if (typeof Set === 'function') { + var obj = new Set(); + obj.add(1).add('string').add(false).add({}); + assert.ok(_.isSet(obj), 'but a set is'); + } }); QUnit.test('isWeakSet', function(assert) { - var obj = new WeakSet([{x: 1}, {y: 'string'}, {z: [1, 2, 3]}]); assert.ok(!_.isWeakSet('string'), 'a string is not a weakset'); assert.ok(!_.isWeakSet(2), 'a number is not a weakset'); @@ -736,10 +759,20 @@ assert.ok(!_.isWeakSet(false), 'a boolean is not a weakset'); assert.ok(!_.isWeakSet(void 0), 'undefined is not a weakset'); assert.ok(!_.isWeakSet([1, 2, 3]), 'an array is not a weakset'); - assert.ok(!_.isWeakSet(new Map([['key', 'value']])), 'a map is not a weakset'); - assert.ok(!_.isWeakSet(new WeakMap([[{x: 1}, 'value']])), 'a weakmap is not a weakset'); - assert.ok(!_.isWeakSet(new Set([1, 'string', false])), 'a set is not a weakset'); - assert.ok(_.isWeakSet(obj), 'but a weakset is'); + if (typeof Map === 'function') { + assert.ok(!_.isWeakSet(new Map()), 'a map is not a weakset'); + } + if (typeof WeakMap === 'function') { + assert.ok(!_.isWeakSet(new WeakMap()), 'a weakmap is not a weakset'); + } + if (typeof Set === 'function') { + assert.ok(!_.isWeakSet(new Set()), 'a set is not a weakset'); + } + if (typeof WeakSet === 'function') { + var obj = new WeakSet(); + obj.add({x: 1}, {y: 'string'}).add({y: 'string'}).add({z: [1, 2, 3]}); + assert.ok(_.isWeakSet(obj), 'but a weakset is'); + } }); QUnit.test('isFunction', function(assert) { From 6a2c2ee882dd20dd9411275f7e6a1c9831318621 Mon Sep 17 00:00:00 2001 From: javadev Date: Thu, 14 May 2015 06:55:34 +0300 Subject: [PATCH 157/263] Add link to the underscore-java project. Underscore-java is a java port of underscore.js. In addition to porting Underscore's functionality, Underscore-java includes matching unit tests. --- index.html | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/index.html b/index.html index 459db77c1..45857b07f 100644 --- a/index.html +++ b/index.html @@ -2194,6 +2194,13 @@ reverse, sprintf, and more.

    +

    + Underscore-java, + a java port of the functions that are applicable in both languages. + Includes OOP-wrapping and chaining. + (source) +

    +

    Ruby's Enumerable module.

    From 15f10acf2b9a2b2df3a30551f87e67c095202908 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Wed, 23 Mar 2016 16:05:03 -0400 Subject: [PATCH 158/263] Make `_.iteratee` docs more explicit `_.iteratee` contains a huge amount of power/magic, and based upon my [personal experience](https://jordaneldredge.com/blog/youre-underusing-underscore/), it is one of the most underused aspects of Underscore. The issue of `_.iteratee`'s functionality being hard to discover has been raised before (#2052). Hopefully this more explicit documentation can help. Additionally it lays groundwork for documenting `_.iteratee` as user definable if #2480 ends up getting merged. --- index.html | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/index.html b/index.html index 45857b07f..17ee2e2af 100644 --- a/index.html +++ b/index.html @@ -1900,22 +1900,37 @@

    Utility Functions

    iteratee_.iteratee(value, [context])
    - A mostly-internal function to generate callbacks that can be applied - to each element in a collection, returning the desired result — either - identity, an arbitrary callback, a property matcher, or a property accessor. -
    - The full list of Underscore methods that transform predicates - through _.iteratee is countBy, every, filter, - find, findIndex, findKey, findLastIndex, - groupBy, indexBy, map, mapObject, max, - min, partition, reject, some, sortBy, - sortedIndex, and uniq + Generates a callback that can be applied to each element in + a collection. _.iteratee supports a number of shorthand + syntaxes for common callback use cases. Depending value's type + _.iteratee will return:

    -var stooges = [{name: 'curly', age: 25}, {name: 'moe', age: 21}, {name: 'larry', age: 23}];
    -_.map(stooges, _.iteratee('age'));
    -=> [25, 21, 23];
    -
    +// No value +_.iteratee(); +=> _.identity() + +// Function +_.iteratee(function(n) { return n * 2; }); +=> function(n) { return n * 2; } + +// Object +_.iteratee({firstName: 'Chelsea'}); +=> _.matcher({firstName: 'Chelsea'}); + +// Anything else +_.iteratee('firstName'); +=> _.property('firstName');
    + +

    + The following Underscore methods transform their predicates through + _.iteratee: countBy, every, + filter, find, findIndex, findKey, + findLastIndex, groupBy, indexBy, + map, mapObject, max, min, + partition, reject, some, sortBy, + sortedIndex, and uniq +

    uniqueId_.uniqueId([prefix]) From b51863962a5d9246bb054106d3bf94f2d5e75929 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Wed, 23 Mar 2016 12:09:46 -0400 Subject: [PATCH 159/263] Allow users to customize _.iteratee This fix was originally proposed by @jridgewell in #1965 but it was unable to gain consensus since his approach added an additional layer of abstraction to which @akre54 objected. I've refactored @jridgewell's approach to avoid that abstraction. This PR was motivated by a renewed interest in customizable shorthand syntaxes such as the RegEx syntax proposed in #2475. --- test/collections.js | 8 ++++++++ underscore.js | 13 +++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/test/collections.js b/test/collections.js index 182f7a218..f96222859 100644 --- a/test/collections.js +++ b/test/collections.js @@ -165,6 +165,14 @@ // Passing a property name like _.pluck. var people = [{name: 'moe', age: 30}, {name: 'curly', age: 50}]; assert.deepEqual(_.map(people, 'name'), ['moe', 'curly'], 'predicate string map to object properties'); + + var iteratee = _.iteratee; + _.iteratee = function(n) { return function(value) { return value * n; }; }; + + assert.deepEqual(_.map([1, 2, 3], 2), [2, 4, 6], 'will use a user-defined _.iteratee function'); + + // Replace the built-in iteratee so as to not break other tests + _.iteratee = iteratee; }); QUnit.test('collect', function(assert) { diff --git a/underscore.js b/underscore.js index 1bd886d4e..db457f62c 100644 --- a/underscore.js +++ b/underscore.js @@ -84,18 +84,23 @@ }; }; + var builtinIteratee; + // An internal function to generate callbacks that can be applied to each // element in a collection, returning the desired result — either `identity`, // an arbitrary callback, a property matcher, or a property accessor. var cb = function(value, context, argCount) { + if (_.iteratee !== builtinIteratee) return _.iteratee(value, context); if (value == null) return _.identity; if (_.isFunction(value)) return optimizeCb(value, context, argCount); if (_.isObject(value)) return _.matcher(value); return _.property(value); }; - // An external wrapper for the internal callback generator. - _.iteratee = function(value, context) { + // External wrapper for our callback generator. Users may customize + // `_.iteratee` if they want additional predicate/iteratee shorthand styles. + // This abstraction hides the internal-only argCount argument. + _.iteratee = builtinIteratee = function(value, context) { return cb(value, context, Infinity); }; @@ -443,7 +448,7 @@ // Keep surrogate pair characters together return obj.match(reStrSymbol); } - if (isArrayLike(obj)) return _.map(obj); + if (isArrayLike(obj)) return _.map(obj, _.identity); return _.values(obj); }; @@ -495,7 +500,7 @@ // Trim out all falsy values from an array. _.compact = function(array) { - return _.filter(array); + return _.filter(array, Boolean); }; // Internal implementation of a recursive `flatten` function. From 6f2ad2f959a5e42a71ec6256ff9f52432395c9b1 Mon Sep 17 00:00:00 2001 From: Afnan Fahim Date: Thu, 24 Mar 2016 10:08:25 -0700 Subject: [PATCH 160/263] Make no-use-before-define eslint rule a warning. --- .eslintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index 73a97e193..5ab39e461 100644 --- a/.eslintrc +++ b/.eslintrc @@ -74,7 +74,7 @@ "no-unreachable": 2, "no-unused-expressions": 2, "no-unused-vars": 2, - "no-use-before-define": [2, "nofunc"], + "no-use-before-define": [1, "nofunc"], "no-with": 2, "object-curly-spacing": [2, "never"], "quote-props": [1, "as-needed"], From 96719954d086f8f77c5521698ac138f595c4cb9b Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Wed, 23 Mar 2016 23:48:16 -0400 Subject: [PATCH 161/263] Fix debounce immediate Fixes an issue with `_.debounce` when `{ immediate: true }`. When called twice within `wait` ms, we cleared the `timeout`. Because it is truthy on the second run, it was cleared but `timeout` remained truthy (on any later run, `timeout` remains truthy). Because `timeout` was cleared, the `later` function is never run to null out `timeout`. Anyways, we should always be setting a fresh timeout function when `{ immediate: true }`, to prevent further calls to the debounced function until the `wait` ms after the last call. Fixes #2478, supersedes #2479. Thanks for the bug report @hanzichi. --- test/functions.js | 12 +++++++++--- underscore.js | 8 ++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/test/functions.js b/test/functions.js index 8d2b4dbb3..f696bd648 100644 --- a/test/functions.js +++ b/test/functions.js @@ -475,9 +475,9 @@ }); QUnit.test('debounce asap', function(assert) { - assert.expect(4); + assert.expect(6); var done = assert.async(); - var a, b; + var a, b, c; var counter = 0; var incr = function(){ return ++counter; }; var debouncedIncr = _.debounce(incr, 64, true); @@ -489,7 +489,13 @@ _.delay(debouncedIncr, 16); _.delay(debouncedIncr, 32); _.delay(debouncedIncr, 48); - _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); done(); }, 128); + _.delay(function(){ + assert.equal(counter, 1, 'incr was debounced'); + c = debouncedIncr(); + assert.equal(c, 2); + assert.equal(counter, 2, 'incr was called again'); + done(); + }, 128); }); QUnit.test('debounce asap cancel', function(assert) { diff --git a/underscore.js b/underscore.js index 1bd886d4e..effe9a20d 100644 --- a/underscore.js +++ b/underscore.js @@ -860,12 +860,12 @@ }; var debounced = restArgs(function(args) { - var callNow = immediate && !timeout; if (timeout) clearTimeout(timeout); - if (callNow) { + if (immediate) { + var callNow = !timeout; timeout = setTimeout(later, wait); - result = func.apply(this, args); - } else if (!immediate) { + if (callNow) result = func.apply(this, args); + } else { timeout = _.delay(later, wait, this, args); } From 0bff13f62e582044b2603a57ed0bb730f8aa9586 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Thu, 24 Mar 2016 11:58:01 -0400 Subject: [PATCH 162/263] Golf _.find Saves 4 bytes gzipped, and I don't find it any harder to read. One minuscule step toward #2060 --- underscore.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/underscore.js b/underscore.js index 1bd886d4e..8a65f5162 100644 --- a/underscore.js +++ b/underscore.js @@ -219,12 +219,8 @@ // Return the first value which passes a truth test. Aliased as `detect`. _.find = _.detect = function(obj, predicate, context) { - var key; - if (isArrayLike(obj)) { - key = _.findIndex(obj, predicate, context); - } else { - key = _.findKey(obj, predicate, context); - } + var keyFinder = isArrayLike(obj) ? _.findIndex : _.findKey; + var key = keyFinder(obj, predicate, context); if (key !== void 0 && key !== -1) return obj[key]; }; From 280512b843a1a243f624486a6bdc43e069652957 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Fri, 25 Mar 2016 09:20:16 -0400 Subject: [PATCH 163/263] Add `npm run weight` tool The way a code change maps to a change in minified/gzipped size is not always intuitive (#2383). To help folks in our effort to slim down (#2060), they can now issue the command: npm run weight And get output like: 6.27 kB Which should give a fairly good estimate of the "over-the-wire" size of their `underscore.js`. Caveat: The weight reported does not include the source-map comment at the end of the file. --- package.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b56d80770..6e6ad7c21 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,11 @@ "coveralls": "^2.11.2", "docco": "*", "eslint": "1.10.x", + "gzip-size-cli": "^1.0.0", "karma": "^0.13.13", "karma-qunit": "~0.1.4", "nyc": "^2.1.3", + "pretty-bytes-cli": "^1.0.0", "qunit-cli": "~0.2.0", "qunitjs": "^1.18.0", "uglify-js": "2.4.x" @@ -34,8 +36,10 @@ "lint": "eslint underscore.js test/*.js", "test-node": "qunit-cli test/*.js", "test-browser": "npm i karma-phantomjs-launcher && karma start", - "build": "uglifyjs underscore.js -c \"evaluate=false\" --comments \"/ .*/\" -m --source-map underscore-min.map --source-map-url \" \" -o underscore-min.js", - "doc": "docco underscore.js" + "minify": "uglifyjs underscore.js -c \"evaluate=false\" --comments \"/ .*/\" -m", + "build": "npm run minify -- --source-map underscore-min.map --source-map-url \" \" -o underscore-min.js", + "doc": "docco underscore.js", + "weight": "npm run minify | gzip-size | pretty-bytes" }, "license": "MIT", "files": [ From f04536898b2692039f71dbf41b9c7dc0d89e4401 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Thu, 24 Mar 2016 11:50:38 -0400 Subject: [PATCH 164/263] Improve tests for using custom `_.iteratee` --- test/collections.js | 8 -------- test/functions.js | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/test/collections.js b/test/collections.js index f96222859..182f7a218 100644 --- a/test/collections.js +++ b/test/collections.js @@ -165,14 +165,6 @@ // Passing a property name like _.pluck. var people = [{name: 'moe', age: 30}, {name: 'curly', age: 50}]; assert.deepEqual(_.map(people, 'name'), ['moe', 'curly'], 'predicate string map to object properties'); - - var iteratee = _.iteratee; - _.iteratee = function(n) { return function(value) { return value * n; }; }; - - assert.deepEqual(_.map([1, 2, 3], 2), [2, 4, 6], 'will use a user-defined _.iteratee function'); - - // Replace the built-in iteratee so as to not break other tests - _.iteratee = iteratee; }); QUnit.test('collect', function(assert) { diff --git a/test/functions.js b/test/functions.js index 8d2b4dbb3..74c9a0c23 100644 --- a/test/functions.js +++ b/test/functions.js @@ -688,6 +688,43 @@ assert.deepEqual(_.toArray(cb(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)), _.range(1, 11)); }); + // Test custom iteratee + var builtinIteratee = _.iteratee; + _.iteratee = function(value) { + // RegEx values return a function that returns the number of matches + if (_.isRegExp(value)) return function(obj) { + return (obj.match(value) || []).length; + }; + return value; + }; + + var collection = ['foo', 'bar', 'bbiz']; + + // Test all methods that claim to be transformed through `_.iteratee` + assert.deepEqual(_.countBy(collection, /b/g), {0: 1, 1: 1, 2: 1}); + assert.equal(_.every(collection, /b/g), false); + assert.deepEqual(_.filter(collection, /b/g), ['bar', 'bbiz']); + assert.equal(_.find(collection, /b/g), 'bar'); + assert.equal(_.findIndex(collection, /b/g), 1); + assert.equal(_.findKey(collection, /b/g), 1); + assert.equal(_.findLastIndex(collection, /b/g), 2); + assert.deepEqual(_.groupBy(collection, /b/g), {0: ['foo'], 1: ['bar'], 2: ['bbiz']}); + assert.deepEqual(_.indexBy(collection, /b/g), {0: 'foo', 1: 'bar', 2: 'bbiz'}); + assert.deepEqual(_.map(collection, /b/g), [0, 1, 2]); + assert.equal(_.max(collection, /b/g), 'bbiz'); + assert.equal(_.min(collection, /b/g), 'foo'); + assert.deepEqual(_.partition(collection, /b/g), [['bar', 'bbiz'], ['foo']]); + assert.deepEqual(_.reject(collection, /b/g), ['foo']); + assert.equal(_.some(collection, /b/g), true); + assert.deepEqual(_.sortBy(collection, /b/g), ['foo', 'bar', 'bbiz']); + assert.equal(_.sortedIndex(collection, 'blah', /b/g), 1); + assert.deepEqual(_.uniq(collection, /b/g), ['foo', 'bar', 'bbiz']); + + var objCollection = {a: 'foo', b: 'bar', c: 'bbiz'}; + assert.deepEqual(_.mapObject(objCollection, /b/g), {a: 0, b: 1, c: 2}); + + // Restore the builtin iteratee + _.iteratee = builtinIteratee; }); QUnit.test('restArgs', function(assert) { From 29fd55c8d378a582d4f2e49d8edcfe44b95fcdc9 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Wed, 30 Mar 2016 08:57:42 -0700 Subject: [PATCH 165/263] Fix typo in `_.iteratee` docs --- index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 17ee2e2af..48ea5f8aa 100644 --- a/index.html +++ b/index.html @@ -1902,8 +1902,8 @@

    Utility Functions


    Generates a callback that can be applied to each element in a collection. _.iteratee supports a number of shorthand - syntaxes for common callback use cases. Depending value's type - _.iteratee will return: + syntaxes for common callback use cases. Depending upon value's + type, _.iteratee will return:

     // No value
    
    From f32dfc6fde825976a3126b12cc81dad4e42cfcc5 Mon Sep 17 00:00:00 2001
    From: Jordan Eldredge 
    Date: Wed, 30 Mar 2016 08:53:01 -0700
    Subject: [PATCH 166/263] Document customizing `_.iteratee`
    
    ---
     index.html | 11 +++++++++++
     1 file changed, 11 insertions(+)
    
    diff --git a/index.html b/index.html
    index 17ee2e2af..04f129aaf 100644
    --- a/index.html
    +++ b/index.html
    @@ -1932,6 +1932,17 @@ 

    Utility Functions

    sortedIndex, and uniq

    +

    + You may overwrite _.iteratee with your own custom function, + if you want additional or different shorthand syntaxes: +

    +
    +// Support `RegExp` predicate shorthand.
    +var builtinIteratee = _.iteratee;
    +_.iteratee = function(value, context) {
    +  if (_.isRegExp(value)) return function(obj) { return value.test(obj) };
    +  return builtinIteratee(value, context);
    +};

    uniqueId_.uniqueId([prefix])
    From 012fe2b77e3d206d763c0c078d3b97a63639fc24 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sun, 3 Apr 2016 18:45:44 -0700 Subject: [PATCH 167/263] Cleanup comment indentation --- underscore.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/underscore.js b/underscore.js index f2f04dc24..db5d62845 100644 --- a/underscore.js +++ b/underscore.js @@ -1099,7 +1099,7 @@ return result; }); - // Return a copy of the object without the blacklisted properties. + // Return a copy of the object without the blacklisted properties. _.omit = restArgs(function(obj, keys) { var iteratee = keys[0], context; if (_.isFunction(iteratee)) { @@ -1408,7 +1408,7 @@ return new Date().getTime(); }; - // List of HTML entities for escaping. + // List of HTML entities for escaping. var escapeMap = { '&': '&', '<': '<', From 0c10935623cd378d15ec3b04f82c6a5359274a7e Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sun, 3 Apr 2016 22:48:48 -0700 Subject: [PATCH 168/263] Fix the argument order of some assertions Error messages assume the order `actual, expected`. Using https://github.com/platinumazure/eslint-plugin-qunit/blob/master/docs/rules/literal-compare-order.md I was able to automatically detect some of our tests which don't follow this convention. I'm sure there are others. --- test/collections.js | 54 ++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/test/collections.js b/test/collections.js index 182f7a218..b3e4f8623 100644 --- a/test/collections.js +++ b/test/collections.js @@ -549,7 +549,7 @@ assert.equal(-Infinity, _.max(void 0), 'can handle null/undefined'); assert.equal(-Infinity, _.max(null, _.identity), 'can handle null/undefined'); - assert.equal(3, _.max([1, 2, 3]), 'can perform a regular Math.max'); + assert.equal(_.max([1, 2, 3]), 3, 'can perform a regular Math.max'); var neg = _.max([1, 2, 3], function(num){ return -num; }); assert.equal(neg, 1, 'can perform a computation-based max'); @@ -558,24 +558,24 @@ assert.equal(-Infinity, _.max([]), 'Maximum value of an empty array'); assert.equal(_.max({a: 'a'}), -Infinity, 'Maximum value of a non-numeric collection'); - assert.equal(299999, _.max(_.range(1, 300000)), 'Maximum value of a too-big array'); + assert.equal(_.max(_.range(1, 300000)), 299999, 'Maximum value of a too-big array'); - assert.equal(3, _.max([1, 2, 3, 'test']), 'Finds correct max in array starting with num and containing a NaN'); - assert.equal(3, _.max(['test', 1, 2, 3]), 'Finds correct max in array starting with NaN'); + assert.equal(_.max([1, 2, 3, 'test']), 3, 'Finds correct max in array starting with num and containing a NaN'); + assert.equal(_.max(['test', 1, 2, 3]), 3, 'Finds correct max in array starting with NaN'); - assert.equal(3, _.max([1, 2, 3, null]), 'Finds correct max in array starting with num and containing a `null`'); - assert.equal(3, _.max([null, 1, 2, 3]), 'Finds correct max in array starting with a `null`'); + assert.equal(_.max([1, 2, 3, null]), 3, 'Finds correct max in array starting with num and containing a `null`'); + assert.equal(_.max([null, 1, 2, 3]), 3, 'Finds correct max in array starting with a `null`'); - assert.equal(3, _.max([1, 2, 3, '']), 'Finds correct max in array starting with num and containing an empty string'); - assert.equal(3, _.max(['', 1, 2, 3]), 'Finds correct max in array starting with an empty string'); + assert.equal(_.max([1, 2, 3, '']), 3, 'Finds correct max in array starting with num and containing an empty string'); + assert.equal(_.max(['', 1, 2, 3]), 3, 'Finds correct max in array starting with an empty string'); - assert.equal(3, _.max([1, 2, 3, false]), 'Finds correct max in array starting with num and containing a false'); - assert.equal(3, _.max([false, 1, 2, 3]), 'Finds correct max in array starting with a false'); + assert.equal(_.max([1, 2, 3, false]), 3, 'Finds correct max in array starting with num and containing a false'); + assert.equal(_.max([false, 1, 2, 3]), 3, 'Finds correct max in array starting with a false'); - assert.equal(4, _.max([0, 1, 2, 3, 4]), 'Finds correct max in array containing a zero'); - assert.equal(0, _.max([-3, -2, -1, 0]), 'Finds correct max in array containing negative numbers'); + assert.equal(_.max([0, 1, 2, 3, 4]), 4, 'Finds correct max in array containing a zero'); + assert.equal(_.max([-3, -2, -1, 0]), 0, 'Finds correct max in array containing negative numbers'); - assert.deepEqual([3, 6], _.map([[1, 2, 3], [4, 5, 6]], _.max), 'Finds correct max in array when mapping through multiple arrays'); + assert.deepEqual(_.map([[1, 2, 3], [4, 5, 6]], _.max), [3, 6], 'Finds correct max in array when mapping through multiple arrays'); var a = {x: -Infinity}; var b = {x: -Infinity}; @@ -590,35 +590,35 @@ }); QUnit.test('min', function(assert) { - assert.equal(Infinity, _.min(null), 'can handle null/undefined'); - assert.equal(Infinity, _.min(void 0), 'can handle null/undefined'); - assert.equal(Infinity, _.min(null, _.identity), 'can handle null/undefined'); + assert.equal(_.min(null), Infinity, 'can handle null/undefined'); + assert.equal(_.min(void 0), Infinity, 'can handle null/undefined'); + assert.equal(_.min(null, _.identity), Infinity, 'can handle null/undefined'); - assert.equal(1, _.min([1, 2, 3]), 'can perform a regular Math.min'); + assert.equal(_.min([1, 2, 3]), 1, 'can perform a regular Math.min'); var neg = _.min([1, 2, 3], function(num){ return -num; }); assert.equal(neg, 3, 'can perform a computation-based min'); - assert.equal(Infinity, _.min({}), 'Minimum value of an empty object'); - assert.equal(Infinity, _.min([]), 'Minimum value of an empty array'); + assert.equal(_.min({}), Infinity, 'Minimum value of an empty object'); + assert.equal(_.min([]), Infinity, 'Minimum value of an empty array'); assert.equal(_.min({a: 'a'}), Infinity, 'Minimum value of a non-numeric collection'); - assert.deepEqual([1, 4], _.map([[1, 2, 3], [4, 5, 6]], _.min), 'Finds correct min in array when mapping through multiple arrays'); + assert.deepEqual(_.map([[1, 2, 3], [4, 5, 6]], _.min), [1, 4], 'Finds correct min in array when mapping through multiple arrays'); var now = new Date(9999999999); var then = new Date(0); assert.equal(_.min([now, then]), then); - assert.equal(1, _.min(_.range(1, 300000)), 'Minimum value of a too-big array'); + assert.equal(_.min(_.range(1, 300000)), 1, 'Minimum value of a too-big array'); - assert.equal(1, _.min([1, 2, 3, 'test']), 'Finds correct min in array starting with num and containing a NaN'); - assert.equal(1, _.min(['test', 1, 2, 3]), 'Finds correct min in array starting with NaN'); + assert.equal(_.min([1, 2, 3, 'test']), 1, 'Finds correct min in array starting with num and containing a NaN'); + assert.equal(_.min(['test', 1, 2, 3]), 1, 'Finds correct min in array starting with NaN'); - assert.equal(1, _.min([1, 2, 3, null]), 'Finds correct min in array starting with num and containing a `null`'); - assert.equal(1, _.min([null, 1, 2, 3]), 'Finds correct min in array starting with a `null`'); + assert.equal(_.min([1, 2, 3, null]), 1, 'Finds correct min in array starting with num and containing a `null`'); + assert.equal(_.min([null, 1, 2, 3]), 1, 'Finds correct min in array starting with a `null`'); - assert.equal(0, _.min([0, 1, 2, 3, 4]), 'Finds correct min in array containing a zero'); - assert.equal(-3, _.min([-3, -2, -1, 0]), 'Finds correct min in array containing negative numbers'); + assert.equal(_.min([0, 1, 2, 3, 4]), 0, 'Finds correct min in array containing a zero'); + assert.equal(_.min([-3, -2, -1, 0]), -3, 'Finds correct min in array containing negative numbers'); var a = {x: Infinity}; var b = {x: Infinity}; From 7943530568a94af59f36804cf82c9aaff7afbc89 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sun, 3 Apr 2016 22:34:54 -0700 Subject: [PATCH 169/263] Use descriptive assertions Using descriptive assertions can help give better failure messages. These were found using @platinumazure's [`eslint-plugin-qunit`](https://github.com/platinumazure/eslint-plugin-qunit). Once it reaches version 1.0, we could consider including it as part of our linting. --- test/collections.js | 24 ++++++++++++------------ test/objects.js | 4 ++-- test/utility.js | 6 +++--- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/test/collections.js b/test/collections.js index 182f7a218..00f1a8a22 100644 --- a/test/collections.js +++ b/test/collections.js @@ -188,7 +188,7 @@ var prod = _.reduce([1, 2, 3, 4], function(memo, num){ return memo * num; }); assert.equal(prod, 24, 'can reduce via multiplication'); - assert.ok(_.reduce(null, _.noop, 138) === 138, 'handles a null (with initial value) properly'); + assert.strictEqual(_.reduce(null, _.noop, 138), 138, 'handles a null (with initial value) properly'); assert.equal(_.reduce([], _.noop, void 0), void 0, 'undefined can be passed as a special case'); assert.equal(_.reduce([_], _.noop), _, 'collection of length one with no initial value returns the first item'); assert.equal(_.reduce([], _.noop), void 0, 'returns undefined when collection is empty and no initial value'); @@ -212,7 +212,7 @@ var sum = _.reduceRight({a: 1, b: 2, c: 3}, function(memo, num){ return memo + num; }); assert.equal(sum, 6, 'default initial value on object'); - assert.ok(_.reduceRight(null, _.noop, 138) === 138, 'handles a null (with initial value) properly'); + assert.strictEqual(_.reduceRight(null, _.noop, 138), 138, 'handles a null (with initial value) properly'); assert.equal(_.reduceRight([_], _.noop), _, 'collection of length one with no initial value returns the first item'); assert.equal(_.reduceRight([], _.noop, void 0), void 0, 'undefined can be passed as a special case'); @@ -351,8 +351,8 @@ assert.ok(!_.every([true, false, true], _.identity), 'one false value'); assert.ok(_.every([0, 10, 28], function(num){ return num % 2 === 0; }), 'even numbers'); assert.ok(!_.every([0, 11, 28], function(num){ return num % 2 === 0; }), 'an odd number'); - assert.ok(_.every([1], _.identity) === true, 'cast to boolean - true'); - assert.ok(_.every([0], _.identity) === false, 'cast to boolean - false'); + assert.strictEqual(_.every([1], _.identity), true, 'cast to boolean - true'); + assert.strictEqual(_.every([0], _.identity), false, 'cast to boolean - false'); assert.ok(!_.every([void 0, void 0, void 0], _.identity), 'works with arrays of undefined'); var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; @@ -381,8 +381,8 @@ assert.ok(!_.some([null, 0, '', false]), 'falsy values'); assert.ok(!_.some([1, 11, 29], function(num){ return num % 2 === 0; }), 'all odd numbers'); assert.ok(_.some([1, 10, 29], function(num){ return num % 2 === 0; }), 'an even number'); - assert.ok(_.some([1], _.identity) === true, 'cast to boolean - true'); - assert.ok(_.some([0], _.identity) === false, 'cast to boolean - false'); + assert.strictEqual(_.some([1], _.identity), true, 'cast to boolean - true'); + assert.strictEqual(_.some([0], _.identity), false, 'cast to boolean - false'); assert.ok(_.some([false, false, true])); var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; @@ -412,7 +412,7 @@ assert.strictEqual(_.includes([5, 4, 3, 2, 1], 5, true), true, 'doesn\'t delegate to binary search'); - assert.ok(_.includes({moe: 1, larry: 3, curly: 9}, 3) === true, '_.includes on objects checks their values'); + assert.strictEqual(_.includes({moe: 1, larry: 3, curly: 9}, 3), true, '_.includes on objects checks their values'); assert.ok(_([1, 2, 3]).includes(2), 'OO-style includes'); var numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; @@ -692,7 +692,7 @@ assert.deepEqual(grouped['5'], ['three', 'seven', 'eight']); var context = {}; - _.groupBy([{}], function(){ assert.ok(this === context); }, context); + _.groupBy([{}], function(){ assert.strictEqual(this, context); }, context); grouped = _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor'; @@ -701,7 +701,7 @@ assert.equal(grouped.hasOwnProperty.length, 2); var array = [{}]; - _.groupBy(array, function(value, index, obj){ assert.ok(obj === array); }); + _.groupBy(array, function(value, index, obj){ assert.strictEqual(obj, array); }); array = [1, 2, 1, 2, 3]; grouped = _.groupBy(array); @@ -747,7 +747,7 @@ assert.equal(grouped['5'], 3); var context = {}; - _.countBy([{}], function(){ assert.ok(this === context); }, context); + _.countBy([{}], function(){ assert.strictEqual(this, context); }, context); grouped = _.countBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor'; @@ -756,7 +756,7 @@ assert.equal(grouped.hasOwnProperty, 2); var array = [{}]; - _.countBy(array, function(value, index, obj){ assert.ok(obj === array); }); + _.countBy(array, function(value, index, obj){ assert.strictEqual(obj, array); }); array = [1, 2, 1, 2, 3]; grouped = _.countBy(array); @@ -800,7 +800,7 @@ assert.ok(!_.isArray(arguments), 'arguments object is not an array'); assert.ok(_.isArray(_.toArray(arguments)), 'arguments object converted into array'); var a = [1, 2, 3]; - assert.ok(_.toArray(a) !== a, 'array is cloned'); + assert.notStrictEqual(_.toArray(a), a, 'array is cloned'); assert.deepEqual(_.toArray(a), [1, 2, 3], 'cloned array contains same elements'); var numbers = _.toArray({one: 1, two: 2, three: 3}); diff --git a/test/objects.js b/test/objects.js index fa1d9e3e3..059a9b28c 100644 --- a/test/objects.js +++ b/test/objects.js @@ -958,8 +958,8 @@ assert.equal(_.matcher({})(null), true, 'Empty spec called with null object returns true'); assert.equal(_.matcher({a: 1})(null), false, 'Non-empty spec called with null object returns false'); - assert.ok(_.find(stooges, _.matcher({hair: false})) === curly, 'returns a predicate that can be used by finding functions.'); - assert.ok(_.find(stooges, _.matcher(moe)) === moe, 'can be used to locate an object exists in a collection.'); + assert.strictEqual(_.find(stooges, _.matcher({hair: false})), curly, 'returns a predicate that can be used by finding functions.'); + assert.strictEqual(_.find(stooges, _.matcher(moe)), moe, 'can be used to locate an object exists in a collection.'); assert.deepEqual(_.filter([null, void 0], _.matcher({a: 1})), [], 'Do not throw on null values.'); assert.deepEqual(_.filter([null, void 0], _.matcher(null)), [null, void 0], 'null matches null'); diff --git a/test/utility.js b/test/utility.js index fbd54df31..60d387597 100644 --- a/test/utility.js +++ b/test/utility.js @@ -52,8 +52,8 @@ QUnit.test('#750 - Return _ instance.', function(assert) { assert.expect(2); var instance = _([]); - assert.ok(_(instance) === instance); - assert.ok(new _(instance) === instance); + assert.strictEqual(_(instance), instance); + assert.strictEqual(new _(instance), instance); }); QUnit.test('identity', function(assert) { @@ -188,7 +188,7 @@ var str = 'some string & another string & yet another'; var escaped = _.escape(str); - assert.ok(escaped.indexOf('&') !== -1, 'handles & aka &'); + assert.notStrictEqual(escaped.indexOf('&'), -1, 'handles & aka &'); assert.equal(_.unescape(str), str, 'can unescape &'); }); From cc1cb526bc8d6d9d0e9ef6c6b2b3b0905cffe04e Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sun, 3 Apr 2016 21:58:52 -0700 Subject: [PATCH 170/263] Prefer `assert.notOk()` to `assert.ok(!)` In #2389 we upgraded our browser tests to QUnit 1.18.0 which means we can now use the `assert.notOk()` assertion. Using this gives a less confusing error message when the assertion fails. Assuming my `shouldBeFalse()` function is failing: assert.ok(!shouldBeFalse()); => failed, expected argument to be truthy, was: false assert.notOk(shouldBeFalse()); => failed, expected argument to be falsy, was: true --- test/collections.js | 40 +++--- test/cross-document.js | 20 +-- test/functions.js | 2 +- test/objects.js | 316 ++++++++++++++++++++--------------------- test/utility.js | 8 +- 5 files changed, 193 insertions(+), 193 deletions(-) diff --git a/test/collections.js b/test/collections.js index 320f323d0..76ed98c94 100644 --- a/test/collections.js +++ b/test/collections.js @@ -268,8 +268,8 @@ var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}, {a: 2, b: 4}]; assert.deepEqual(_.find(list, {a: 1}), {a: 1, b: 2}, 'can be used as findWhere'); assert.deepEqual(_.find(list, {b: 4}), {a: 1, b: 4}); - assert.ok(!_.find(list, {c: 1}), 'undefined when not found'); - assert.ok(!_.find([], {c: 1}), 'undefined when searching empty list'); + assert.notOk(_.find(list, {c: 1}), 'undefined when not found'); + assert.notOk(_.find([], {c: 1}), 'undefined when searching empty list'); var result = _.find([1, 2, 3], function(num){ return num * 2 === 4; }); assert.equal(result, 2, 'found the first "2" and broke the loop'); @@ -348,25 +348,25 @@ QUnit.test('every', function(assert) { assert.ok(_.every([], _.identity), 'the empty set'); assert.ok(_.every([true, true, true], _.identity), 'every true values'); - assert.ok(!_.every([true, false, true], _.identity), 'one false value'); + assert.notOk(_.every([true, false, true], _.identity), 'one false value'); assert.ok(_.every([0, 10, 28], function(num){ return num % 2 === 0; }), 'even numbers'); - assert.ok(!_.every([0, 11, 28], function(num){ return num % 2 === 0; }), 'an odd number'); + assert.notOk(_.every([0, 11, 28], function(num){ return num % 2 === 0; }), 'an odd number'); assert.strictEqual(_.every([1], _.identity), true, 'cast to boolean - true'); assert.strictEqual(_.every([0], _.identity), false, 'cast to boolean - false'); - assert.ok(!_.every([void 0, void 0, void 0], _.identity), 'works with arrays of undefined'); + assert.notOk(_.every([void 0, void 0, void 0], _.identity), 'works with arrays of undefined'); var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; - assert.ok(!_.every(list, {a: 1, b: 2}), 'Can be called with object'); + assert.notOk(_.every(list, {a: 1, b: 2}), 'Can be called with object'); assert.ok(_.every(list, 'a'), 'String mapped to object property'); list = [{a: 1, b: 2}, {a: 2, b: 2, c: true}]; assert.ok(_.every(list, {b: 2}), 'Can be called with object'); - assert.ok(!_.every(list, 'c'), 'String mapped to object property'); + assert.notOk(_.every(list, 'c'), 'String mapped to object property'); assert.ok(_.every({a: 1, b: 2, c: 3, d: 4}, _.isNumber), 'takes objects'); - assert.ok(!_.every({a: 1, b: 2, c: 3, d: 4}, _.isObject), 'takes objects'); + assert.notOk(_.every({a: 1, b: 2, c: 3, d: 4}, _.isObject), 'takes objects'); assert.ok(_.every(['a', 'b', 'c', 'd'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); - assert.ok(!_.every(['a', 'b', 'c', 'd', 'f'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); + assert.notOk(_.every(['a', 'b', 'c', 'd', 'f'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); }); QUnit.test('all', function(assert) { @@ -374,29 +374,29 @@ }); QUnit.test('some', function(assert) { - assert.ok(!_.some([]), 'the empty set'); - assert.ok(!_.some([false, false, false]), 'all false values'); + assert.notOk(_.some([]), 'the empty set'); + assert.notOk(_.some([false, false, false]), 'all false values'); assert.ok(_.some([false, false, true]), 'one true value'); assert.ok(_.some([null, 0, 'yes', false]), 'a string'); - assert.ok(!_.some([null, 0, '', false]), 'falsy values'); - assert.ok(!_.some([1, 11, 29], function(num){ return num % 2 === 0; }), 'all odd numbers'); + assert.notOk(_.some([null, 0, '', false]), 'falsy values'); + assert.notOk(_.some([1, 11, 29], function(num){ return num % 2 === 0; }), 'all odd numbers'); assert.ok(_.some([1, 10, 29], function(num){ return num % 2 === 0; }), 'an even number'); assert.strictEqual(_.some([1], _.identity), true, 'cast to boolean - true'); assert.strictEqual(_.some([0], _.identity), false, 'cast to boolean - false'); assert.ok(_.some([false, false, true])); var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; - assert.ok(!_.some(list, {a: 5, b: 2}), 'Can be called with object'); + assert.notOk(_.some(list, {a: 5, b: 2}), 'Can be called with object'); assert.ok(_.some(list, 'a'), 'String mapped to object property'); list = [{a: 1, b: 2}, {a: 2, b: 2, c: true}]; assert.ok(_.some(list, {b: 2}), 'Can be called with object'); - assert.ok(!_.some(list, 'd'), 'String mapped to object property'); + assert.notOk(_.some(list, 'd'), 'String mapped to object property'); assert.ok(_.some({a: '1', b: '2', c: '3', d: '4', e: 6}, _.isNumber), 'takes objects'); - assert.ok(!_.some({a: 1, b: 2, c: 3, d: 4}, _.isObject), 'takes objects'); + assert.notOk(_.some({a: 1, b: 2, c: 3, d: 4}, _.isObject), 'takes objects'); assert.ok(_.some(['a', 'b', 'c', 'd'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); - assert.ok(!_.some(['x', 'y', 'z'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); + assert.notOk(_.some(['x', 'y', 'z'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); }); QUnit.test('any', function(assert) { @@ -408,7 +408,7 @@ assert.strictEqual(_.includes(val, 'hasOwnProperty'), false); }); assert.strictEqual(_.includes([1, 2, 3], 2), true, 'two is in the array'); - assert.ok(!_.includes([1, 3, 9], 2), 'two is not in the array'); + assert.notOk(_.includes([1, 3, 9], 2), 'two is not in the array'); assert.strictEqual(_.includes([5, 4, 3, 2, 1], 5, true), true, 'doesn\'t delegate to binary search'); @@ -797,7 +797,7 @@ }); QUnit.test('toArray', function(assert) { - assert.ok(!_.isArray(arguments), 'arguments object is not an array'); + assert.notOk(_.isArray(arguments), 'arguments object is not an array'); assert.ok(_.isArray(_.toArray(arguments)), 'arguments object converted into array'); var a = [1, 2, 3]; assert.notStrictEqual(_.toArray(a), a, 'array is cloned'); @@ -882,7 +882,7 @@ assert.deepEqual(_.map(elementChildren, 'id'), ['id1', 'id2']); assert.deepEqual(_.map(parent.childNodes, 'nodeType'), [1, 3, 1]); - assert.ok(!_.every(parent.childNodes, _.isElement)); + assert.notOk(_.every(parent.childNodes, _.isElement)); assert.ok(_.some(parent.childNodes, _.isElement)); function compareNode(node) { diff --git a/test/cross-document.js b/test/cross-document.js index cb68a3d9b..bc3ab77f1 100644 --- a/test/cross-document.js +++ b/test/cross-document.js @@ -35,7 +35,7 @@ QUnit.test('isEqual', function(assert) { - assert.ok(!_.isEqual(iNumber, 101)); + assert.notOk(_.isEqual(iNumber, 101)); assert.ok(_.isEqual(iNumber, 100)); // Objects from another frame. @@ -46,13 +46,13 @@ }); QUnit.test('isEmpty', function(assert) { - assert.ok(!_([iNumber]).isEmpty(), '[1] is not empty'); - assert.ok(!_.isEmpty(iArray), '[] is empty'); + assert.notOk(_([iNumber]).isEmpty(), '[1] is not empty'); + assert.notOk(_.isEmpty(iArray), '[] is empty'); assert.ok(_.isEmpty(iObject), '{} is empty'); }); QUnit.test('isElement', function(assert) { - assert.ok(!_.isElement('div'), 'strings are not dom elements'); + assert.notOk(_.isElement('div'), 'strings are not dom elements'); assert.ok(_.isElement(document.body), 'the body tag is a DOM element'); assert.ok(_.isElement(iElement), 'even from another frame'); }); @@ -113,12 +113,12 @@ if (typeof ActiveXObject != 'undefined') { QUnit.test('IE host objects', function(assert) { var xml = new ActiveXObject('Msxml2.DOMDocument.3.0'); - assert.ok(!_.isNumber(xml)); - assert.ok(!_.isBoolean(xml)); - assert.ok(!_.isNaN(xml)); - assert.ok(!_.isFunction(xml)); - assert.ok(!_.isNull(xml)); - assert.ok(!_.isUndefined(xml)); + assert.notOk(_.isNumber(xml)); + assert.notOk(_.isBoolean(xml)); + assert.notOk(_.isNaN(xml)); + assert.notOk(_.isFunction(xml)); + assert.notOk(_.isNull(xml)); + assert.notOk(_.isUndefined(xml)); }); QUnit.test('#1621 IE 11 compat mode DOM elements are not functions', function(assert) { diff --git a/test/functions.js b/test/functions.js index e31d1d355..f73f5d382 100644 --- a/test/functions.js +++ b/test/functions.js @@ -180,7 +180,7 @@ var done = assert.async(); var delayed = false; _.delay(function(){ delayed = true; }, 100); - setTimeout(function(){ assert.ok(!delayed, "didn't delay the function quite yet"); }, 50); + setTimeout(function(){ assert.notOk(delayed, "didn't delay the function quite yet"); }, 50); setTimeout(function(){ assert.ok(delayed, 'delayed the function'); done(); }, 150); }); diff --git a/test/objects.js b/test/objects.js index 059a9b28c..91c49a4cc 100644 --- a/test/objects.js +++ b/test/objects.js @@ -123,7 +123,7 @@ subObj.c = 'd'; assert.deepEqual(_.extend({}, subObj), {a: 'b', c: 'd'}, 'extend copies all properties from source'); _.extend(subObj, {}); - assert.ok(!subObj.hasOwnProperty('a'), "extend does not convert destination object's 'in' properties to 'own' properties"); + assert.notOk(subObj.hasOwnProperty('a'), "extend does not convert destination object's 'in' properties to 'own' properties"); try { result = {}; @@ -205,7 +205,7 @@ return this[key] === 3 && this === instance; }, instance), {c: 3}, 'function is given context'); - assert.ok(!_.has(_.pick({}, 'foo'), 'foo'), 'does not set own property if property not in object'); + assert.notOk(_.has(_.pick({}, 'foo'), 'foo'), 'does not set own property if property not in object'); _.pick(data, function(value, key, obj) { assert.equal(obj, data, 'passes same object as third parameter of iteratee'); }); @@ -309,7 +309,7 @@ Child.prototype.foo = 'foo'; var created = _.create(Child.prototype, new Child); - assert.ok(!created.hasOwnProperty('foo'), 'should only add own properties'); + assert.notOk(created.hasOwnProperty('foo'), 'should only add own properties'); }); QUnit.test('isEqual', function(assert) { @@ -326,10 +326,10 @@ assert.ok(_.isEqual(null, null), '`null` is equal to `null`'); assert.ok(_.isEqual(), '`undefined` is equal to `undefined`'); - assert.ok(!_.isEqual(0, -0), '`0` is not equal to `-0`'); - assert.ok(!_.isEqual(-0, 0), 'Commutative equality is implemented for `0` and `-0`'); - assert.ok(!_.isEqual(null, void 0), '`null` is not equal to `undefined`'); - assert.ok(!_.isEqual(void 0, null), 'Commutative equality is implemented for `null` and `undefined`'); + assert.notOk(_.isEqual(0, -0), '`0` is not equal to `-0`'); + assert.notOk(_.isEqual(-0, 0), 'Commutative equality is implemented for `0` and `-0`'); + assert.notOk(_.isEqual(null, void 0), '`null` is not equal to `undefined`'); + assert.notOk(_.isEqual(void 0, null), 'Commutative equality is implemented for `null` and `undefined`'); // String object and primitive comparisons. assert.ok(_.isEqual('Curly', 'Curly'), 'Identical string primitives are equal'); @@ -337,76 +337,76 @@ assert.ok(_.isEqual(new String('Curly'), 'Curly'), 'String primitives and their corresponding object wrappers are equal'); assert.ok(_.isEqual('Curly', new String('Curly')), 'Commutative equality is implemented for string objects and primitives'); - assert.ok(!_.isEqual('Curly', 'Larry'), 'String primitives with different values are not equal'); - assert.ok(!_.isEqual(new String('Curly'), new String('Larry')), 'String objects with different primitive values are not equal'); - assert.ok(!_.isEqual(new String('Curly'), {toString: function(){ return 'Curly'; }}), 'String objects and objects with a custom `toString` method are not equal'); + assert.notOk(_.isEqual('Curly', 'Larry'), 'String primitives with different values are not equal'); + assert.notOk(_.isEqual(new String('Curly'), new String('Larry')), 'String objects with different primitive values are not equal'); + assert.notOk(_.isEqual(new String('Curly'), {toString: function(){ return 'Curly'; }}), 'String objects and objects with a custom `toString` method are not equal'); // Number object and primitive comparisons. assert.ok(_.isEqual(75, 75), 'Identical number primitives are equal'); assert.ok(_.isEqual(new Number(75), new Number(75)), 'Number objects with identical primitive values are equal'); assert.ok(_.isEqual(75, new Number(75)), 'Number primitives and their corresponding object wrappers are equal'); assert.ok(_.isEqual(new Number(75), 75), 'Commutative equality is implemented for number objects and primitives'); - assert.ok(!_.isEqual(new Number(0), -0), '`new Number(0)` and `-0` are not equal'); - assert.ok(!_.isEqual(0, new Number(-0)), 'Commutative equality is implemented for `new Number(0)` and `-0`'); + assert.notOk(_.isEqual(new Number(0), -0), '`new Number(0)` and `-0` are not equal'); + assert.notOk(_.isEqual(0, new Number(-0)), 'Commutative equality is implemented for `new Number(0)` and `-0`'); - assert.ok(!_.isEqual(new Number(75), new Number(63)), 'Number objects with different primitive values are not equal'); - assert.ok(!_.isEqual(new Number(63), {valueOf: function(){ return 63; }}), 'Number objects and objects with a `valueOf` method are not equal'); + assert.notOk(_.isEqual(new Number(75), new Number(63)), 'Number objects with different primitive values are not equal'); + assert.notOk(_.isEqual(new Number(63), {valueOf: function(){ return 63; }}), 'Number objects and objects with a `valueOf` method are not equal'); // Comparisons involving `NaN`. assert.ok(_.isEqual(NaN, NaN), '`NaN` is equal to `NaN`'); assert.ok(_.isEqual(new Number(NaN), NaN), 'Object(`NaN`) is equal to `NaN`'); - assert.ok(!_.isEqual(61, NaN), 'A number primitive is not equal to `NaN`'); - assert.ok(!_.isEqual(new Number(79), NaN), 'A number object is not equal to `NaN`'); - assert.ok(!_.isEqual(Infinity, NaN), '`Infinity` is not equal to `NaN`'); + assert.notOk(_.isEqual(61, NaN), 'A number primitive is not equal to `NaN`'); + assert.notOk(_.isEqual(new Number(79), NaN), 'A number object is not equal to `NaN`'); + assert.notOk(_.isEqual(Infinity, NaN), '`Infinity` is not equal to `NaN`'); // Boolean object and primitive comparisons. assert.ok(_.isEqual(true, true), 'Identical boolean primitives are equal'); assert.ok(_.isEqual(new Boolean, new Boolean), 'Boolean objects with identical primitive values are equal'); assert.ok(_.isEqual(true, new Boolean(true)), 'Boolean primitives and their corresponding object wrappers are equal'); assert.ok(_.isEqual(new Boolean(true), true), 'Commutative equality is implemented for booleans'); - assert.ok(!_.isEqual(new Boolean(true), new Boolean), 'Boolean objects with different primitive values are not equal'); + assert.notOk(_.isEqual(new Boolean(true), new Boolean), 'Boolean objects with different primitive values are not equal'); // Common type coercions. - assert.ok(!_.isEqual(new Boolean(false), true), '`new Boolean(false)` is not equal to `true`'); - assert.ok(!_.isEqual('75', 75), 'String and number primitives with like values are not equal'); - assert.ok(!_.isEqual(new Number(63), new String(63)), 'String and number objects with like values are not equal'); - assert.ok(!_.isEqual(75, '75'), 'Commutative equality is implemented for like string and number values'); - assert.ok(!_.isEqual(0, ''), 'Number and string primitives with like values are not equal'); - assert.ok(!_.isEqual(1, true), 'Number and boolean primitives with like values are not equal'); - assert.ok(!_.isEqual(new Boolean(false), new Number(0)), 'Boolean and number objects with like values are not equal'); - assert.ok(!_.isEqual(false, new String('')), 'Boolean primitives and string objects with like values are not equal'); - assert.ok(!_.isEqual(12564504e5, new Date(2009, 9, 25)), 'Dates and their corresponding numeric primitive values are not equal'); + assert.notOk(_.isEqual(new Boolean(false), true), '`new Boolean(false)` is not equal to `true`'); + assert.notOk(_.isEqual('75', 75), 'String and number primitives with like values are not equal'); + assert.notOk(_.isEqual(new Number(63), new String(63)), 'String and number objects with like values are not equal'); + assert.notOk(_.isEqual(75, '75'), 'Commutative equality is implemented for like string and number values'); + assert.notOk(_.isEqual(0, ''), 'Number and string primitives with like values are not equal'); + assert.notOk(_.isEqual(1, true), 'Number and boolean primitives with like values are not equal'); + assert.notOk(_.isEqual(new Boolean(false), new Number(0)), 'Boolean and number objects with like values are not equal'); + assert.notOk(_.isEqual(false, new String('')), 'Boolean primitives and string objects with like values are not equal'); + assert.notOk(_.isEqual(12564504e5, new Date(2009, 9, 25)), 'Dates and their corresponding numeric primitive values are not equal'); // Dates. assert.ok(_.isEqual(new Date(2009, 9, 25), new Date(2009, 9, 25)), 'Date objects referencing identical times are equal'); - assert.ok(!_.isEqual(new Date(2009, 9, 25), new Date(2009, 11, 13)), 'Date objects referencing different times are not equal'); - assert.ok(!_.isEqual(new Date(2009, 11, 13), { + assert.notOk(_.isEqual(new Date(2009, 9, 25), new Date(2009, 11, 13)), 'Date objects referencing different times are not equal'); + assert.notOk(_.isEqual(new Date(2009, 11, 13), { getTime: function(){ return 12606876e5; } }), 'Date objects and objects with a `getTime` method are not equal'); - assert.ok(!_.isEqual(new Date('Curly'), new Date('Curly')), 'Invalid dates are not equal'); + assert.notOk(_.isEqual(new Date('Curly'), new Date('Curly')), 'Invalid dates are not equal'); // Functions. - assert.ok(!_.isEqual(First, Second), 'Different functions with identical bodies and source code representations are not equal'); + assert.notOk(_.isEqual(First, Second), 'Different functions with identical bodies and source code representations are not equal'); // RegExps. assert.ok(_.isEqual(/(?:)/gim, /(?:)/gim), 'RegExps with equivalent patterns and flags are equal'); assert.ok(_.isEqual(/(?:)/gi, /(?:)/ig), 'Flag order is not significant'); - assert.ok(!_.isEqual(/(?:)/g, /(?:)/gi), 'RegExps with equivalent patterns and different flags are not equal'); - assert.ok(!_.isEqual(/Moe/gim, /Curly/gim), 'RegExps with different patterns and equivalent flags are not equal'); - assert.ok(!_.isEqual(/(?:)/gi, /(?:)/g), 'Commutative equality is implemented for RegExps'); - assert.ok(!_.isEqual(/Curly/g, {source: 'Larry', global: true, ignoreCase: false, multiline: false}), 'RegExps and RegExp-like objects are not equal'); + assert.notOk(_.isEqual(/(?:)/g, /(?:)/gi), 'RegExps with equivalent patterns and different flags are not equal'); + assert.notOk(_.isEqual(/Moe/gim, /Curly/gim), 'RegExps with different patterns and equivalent flags are not equal'); + assert.notOk(_.isEqual(/(?:)/gi, /(?:)/g), 'Commutative equality is implemented for RegExps'); + assert.notOk(_.isEqual(/Curly/g, {source: 'Larry', global: true, ignoreCase: false, multiline: false}), 'RegExps and RegExp-like objects are not equal'); // Empty arrays, array-like objects, and object literals. assert.ok(_.isEqual({}, {}), 'Empty object literals are equal'); assert.ok(_.isEqual([], []), 'Empty array literals are equal'); assert.ok(_.isEqual([{}], [{}]), 'Empty nested arrays and objects are equal'); - assert.ok(!_.isEqual({length: 0}, []), 'Array-like objects and arrays are not equal.'); - assert.ok(!_.isEqual([], {length: 0}), 'Commutative equality is implemented for array-like objects'); + assert.notOk(_.isEqual({length: 0}, []), 'Array-like objects and arrays are not equal.'); + assert.notOk(_.isEqual([], {length: 0}), 'Commutative equality is implemented for array-like objects'); - assert.ok(!_.isEqual({}, []), 'Object literals and array literals are not equal'); - assert.ok(!_.isEqual([], {}), 'Commutative equality is implemented for objects and arrays'); + assert.notOk(_.isEqual({}, []), 'Object literals and array literals are not equal'); + assert.notOk(_.isEqual([], {}), 'Commutative equality is implemented for objects and arrays'); // Arrays with primitive and object values. assert.ok(_.isEqual([1, 'Larry', true], [1, 'Larry', true]), 'Arrays containing identical primitives are equal'); @@ -424,14 +424,14 @@ // Array elements and properties. assert.ok(_.isEqual(a, b), 'Arrays containing equivalent elements and different non-numeric properties are equal'); a.push('White Rocks'); - assert.ok(!_.isEqual(a, b), 'Arrays of different lengths are not equal'); + assert.notOk(_.isEqual(a, b), 'Arrays of different lengths are not equal'); a.push('East Boulder'); b.push('Gunbarrel Ranch', 'Teller Farm'); - assert.ok(!_.isEqual(a, b), 'Arrays of identical lengths containing different elements are not equal'); + assert.notOk(_.isEqual(a, b), 'Arrays of identical lengths containing different elements are not equal'); // Sparse arrays. assert.ok(_.isEqual(Array(3), Array(3)), 'Sparse arrays of identical lengths are equal'); - assert.ok(!_.isEqual(Array(3), Array(6)), 'Sparse arrays of different lengths are not equal when both are empty'); + assert.notOk(_.isEqual(Array(3), Array(6)), 'Sparse arrays of different lengths are not equal when both are empty'); var sparse = []; sparse[1] = 5; @@ -440,11 +440,11 @@ // Simple objects. assert.ok(_.isEqual({a: 'Curly', b: 1, c: true}, {a: 'Curly', b: 1, c: true}), 'Objects containing identical primitives are equal'); assert.ok(_.isEqual({a: /Curly/g, b: new Date(2009, 11, 13)}, {a: /Curly/g, b: new Date(2009, 11, 13)}), 'Objects containing equivalent members are equal'); - assert.ok(!_.isEqual({a: 63, b: 75}, {a: 61, b: 55}), 'Objects of identical sizes with different values are not equal'); - assert.ok(!_.isEqual({a: 63, b: 75}, {a: 61, c: 55}), 'Objects of identical sizes with different property names are not equal'); - assert.ok(!_.isEqual({a: 1, b: 2}, {a: 1}), 'Objects of different sizes are not equal'); - assert.ok(!_.isEqual({a: 1}, {a: 1, b: 2}), 'Commutative equality is implemented for objects'); - assert.ok(!_.isEqual({x: 1, y: void 0}, {x: 1, z: 2}), 'Objects with identical keys and different values are not equivalent'); + assert.notOk(_.isEqual({a: 63, b: 75}, {a: 61, b: 55}), 'Objects of identical sizes with different values are not equal'); + assert.notOk(_.isEqual({a: 63, b: 75}, {a: 61, c: 55}), 'Objects of identical sizes with different property names are not equal'); + assert.notOk(_.isEqual({a: 1, b: 2}, {a: 1}), 'Objects of different sizes are not equal'); + assert.notOk(_.isEqual({a: 1}, {a: 1, b: 2}), 'Commutative equality is implemented for objects'); + assert.notOk(_.isEqual({x: 1, y: void 0}, {x: 1, z: 2}), 'Objects with identical keys and different values are not equivalent'); // `A` contains nested objects and arrays. a = { @@ -479,9 +479,9 @@ // Instances. assert.ok(_.isEqual(new First, new First), 'Object instances are equal'); - assert.ok(!_.isEqual(new First, new Second), 'Objects with different constructors and identical own properties are not equal'); - assert.ok(!_.isEqual({value: 1}, new First), 'Object instances and objects sharing equivalent properties are not equal'); - assert.ok(!_.isEqual({value: 2}, new Second), 'The prototype chain of objects should not be examined'); + assert.notOk(_.isEqual(new First, new Second), 'Objects with different constructors and identical own properties are not equal'); + assert.notOk(_.isEqual({value: 1}, new First), 'Object instances and objects sharing equivalent properties are not equal'); + assert.notOk(_.isEqual({value: 2}, new Second), 'The prototype chain of objects should not be examined'); // Circular Arrays. (a = []).push(a); @@ -492,13 +492,13 @@ assert.ok(_.isEqual(a, b), 'Arrays containing circular references and equivalent properties are equal'); a.push('Shemp'); b.push('Curly'); - assert.ok(!_.isEqual(a, b), 'Arrays containing circular references and different properties are not equal'); + assert.notOk(_.isEqual(a, b), 'Arrays containing circular references and different properties are not equal'); // More circular arrays #767. a = ['everything is checked but', 'this', 'is not']; a[1] = a; b = ['everything is checked but', ['this', 'array'], 'is not']; - assert.ok(!_.isEqual(a, b), 'Comparison of circular references with non-circular references are not equal'); + assert.notOk(_.isEqual(a, b), 'Comparison of circular references with non-circular references are not equal'); // Circular Objects. a = {abc: null}; @@ -511,13 +511,13 @@ assert.ok(_.isEqual(a, b), 'Objects containing circular references and equivalent properties are equal'); a.def = new Number(75); b.def = new Number(63); - assert.ok(!_.isEqual(a, b), 'Objects containing circular references and different properties are not equal'); + assert.notOk(_.isEqual(a, b), 'Objects containing circular references and different properties are not equal'); // More circular objects #767. a = {everything: 'is checked', but: 'this', is: 'not'}; a.but = a; b = {everything: 'is checked', but: {that: 'object'}, is: 'not'}; - assert.ok(!_.isEqual(a, b), 'Comparison of circular references with non-circular object references are not equal'); + assert.notOk(_.isEqual(a, b), 'Comparison of circular references with non-circular object references are not equal'); // Cyclic Structures. a = [{abc: null}]; @@ -530,7 +530,7 @@ assert.ok(_.isEqual(a, b), 'Cyclic structures containing equivalent properties are equal'); a[0].def = new String('Larry'); b[0].def = new String('Curly'); - assert.ok(!_.isEqual(a, b), 'Cyclic structures containing different properties are not equal'); + assert.notOk(_.isEqual(a, b), 'Cyclic structures containing different properties are not equal'); // Complex Circular References. a = {foo: {b: {foo: {c: {foo: null}}}}}; @@ -540,7 +540,7 @@ assert.ok(_.isEqual(a, b), 'Cyclic structures with nested and identically-named properties are equal'); // Chaining. - assert.ok(!_.isEqual(_({x: 1, y: void 0}).chain(), _({x: 1, z: 2}).chain()), 'Chained objects containing different values are not equal'); + assert.notOk(_.isEqual(_({x: 1, y: void 0}).chain(), _({x: 1, z: 2}).chain()), 'Chained objects containing different values are not equal'); a = _({x: 1, y: 2}).chain(); b = _({x: 1, y: 2}).chain(); @@ -576,15 +576,15 @@ }); QUnit.test('isEmpty', function(assert) { - assert.ok(!_([1]).isEmpty(), '[1] is not empty'); + assert.notOk(_([1]).isEmpty(), '[1] is not empty'); assert.ok(_.isEmpty([]), '[] is empty'); - assert.ok(!_.isEmpty({one: 1}), '{one: 1} is not empty'); + assert.notOk(_.isEmpty({one: 1}), '{one: 1} is not empty'); assert.ok(_.isEmpty({}), '{} is empty'); assert.ok(_.isEmpty(new RegExp('')), 'objects with prototype properties are empty'); assert.ok(_.isEmpty(null), 'null is empty'); assert.ok(_.isEmpty(), 'undefined is empty'); assert.ok(_.isEmpty(''), 'the empty string is empty'); - assert.ok(!_.isEmpty('moe'), 'but other strings are not'); + assert.notOk(_.isEmpty('moe'), 'but other strings are not'); var obj = {one: 1}; delete obj.one; @@ -592,27 +592,27 @@ var args = function(){ return arguments; }; assert.ok(_.isEmpty(args()), 'empty arguments object is empty'); - assert.ok(!_.isEmpty(args('')), 'non-empty arguments object is not empty'); + assert.notOk(_.isEmpty(args('')), 'non-empty arguments object is not empty'); // covers collecting non-enumerable properties in IE < 9 var nonEnumProp = {toString: 5}; - assert.ok(!_.isEmpty(nonEnumProp), 'non-enumerable property is not empty'); + assert.notOk(_.isEmpty(nonEnumProp), 'non-enumerable property is not empty'); }); if (typeof document === 'object') { QUnit.test('isElement', function(assert) { - assert.ok(!_.isElement('div'), 'strings are not dom elements'); + assert.notOk(_.isElement('div'), 'strings are not dom elements'); assert.ok(_.isElement(testElement), 'an element is a DOM element'); }); } QUnit.test('isArguments', function(assert) { var args = (function(){ return arguments; }(1, 2, 3)); - assert.ok(!_.isArguments('string'), 'a string is not an arguments object'); - assert.ok(!_.isArguments(_.isArguments), 'a function is not an arguments object'); + assert.notOk(_.isArguments('string'), 'a string is not an arguments object'); + assert.notOk(_.isArguments(_.isArguments), 'a function is not an arguments object'); assert.ok(_.isArguments(args), 'but the arguments object is an arguments object'); - assert.ok(!_.isArguments(_.toArray(args)), 'but not when it\'s converted into an array'); - assert.ok(!_.isArguments([1, 2, 3]), 'and not vanilla arrays.'); + assert.notOk(_.isArguments(_.toArray(args)), 'but not when it\'s converted into an array'); + assert.notOk(_.isArguments([1, 2, 3]), 'and not vanilla arrays.'); }); QUnit.test('isObject', function(assert) { @@ -622,24 +622,24 @@ assert.ok(_.isObject(testElement), 'and DOM element'); } assert.ok(_.isObject(function() {}), 'and functions'); - assert.ok(!_.isObject(null), 'but not null'); - assert.ok(!_.isObject(void 0), 'and not undefined'); - assert.ok(!_.isObject('string'), 'and not string'); - assert.ok(!_.isObject(12), 'and not number'); - assert.ok(!_.isObject(true), 'and not boolean'); + assert.notOk(_.isObject(null), 'but not null'); + assert.notOk(_.isObject(void 0), 'and not undefined'); + assert.notOk(_.isObject('string'), 'and not string'); + assert.notOk(_.isObject(12), 'and not number'); + assert.notOk(_.isObject(true), 'and not boolean'); assert.ok(_.isObject(new String('string')), 'but new String()'); }); QUnit.test('isArray', function(assert) { - assert.ok(!_.isArray(void 0), 'undefined vars are not arrays'); - assert.ok(!_.isArray(arguments), 'the arguments object is not an array'); + assert.notOk(_.isArray(void 0), 'undefined vars are not arrays'); + assert.notOk(_.isArray(arguments), 'the arguments object is not an array'); assert.ok(_.isArray([1, 2, 3]), 'but arrays are'); }); QUnit.test('isString', function(assert) { var obj = new String('I am a string object'); if (testElement) { - assert.ok(!_.isString(testElement), 'an element is not a string'); + assert.notOk(_.isString(testElement), 'an element is not a string'); } assert.ok(_.isString([1, 2, 3].join(', ')), 'but strings are'); assert.strictEqual(_.isString('I am a string literal'), true, 'string literals are'); @@ -648,9 +648,9 @@ }); QUnit.test('isSymbol', function(assert) { - assert.ok(!_.isSymbol(0), 'numbers are not symbols'); - assert.ok(!_.isSymbol(''), 'strings are not symbols'); - assert.ok(!_.isSymbol(_.isSymbol), 'functions are not symbols'); + assert.notOk(_.isSymbol(0), 'numbers are not symbols'); + assert.notOk(_.isSymbol(''), 'strings are not symbols'); + assert.notOk(_.isSymbol(_.isSymbol), 'functions are not symbols'); if (typeof Symbol === 'function') { assert.ok(_.isSymbol(Symbol()), 'symbols are symbols'); assert.ok(_.isSymbol(Symbol('description')), 'described symbols are symbols'); @@ -659,43 +659,43 @@ }); QUnit.test('isNumber', function(assert) { - assert.ok(!_.isNumber('string'), 'a string is not a number'); - assert.ok(!_.isNumber(arguments), 'the arguments object is not a number'); - assert.ok(!_.isNumber(void 0), 'undefined is not a number'); + assert.notOk(_.isNumber('string'), 'a string is not a number'); + assert.notOk(_.isNumber(arguments), 'the arguments object is not a number'); + assert.notOk(_.isNumber(void 0), 'undefined is not a number'); assert.ok(_.isNumber(3 * 4 - 7 / 10), 'but numbers are'); assert.ok(_.isNumber(NaN), 'NaN *is* a number'); assert.ok(_.isNumber(Infinity), 'Infinity is a number'); - assert.ok(!_.isNumber('1'), 'numeric strings are not numbers'); + assert.notOk(_.isNumber('1'), 'numeric strings are not numbers'); }); QUnit.test('isBoolean', function(assert) { - assert.ok(!_.isBoolean(2), 'a number is not a boolean'); - assert.ok(!_.isBoolean('string'), 'a string is not a boolean'); - assert.ok(!_.isBoolean('false'), 'the string "false" is not a boolean'); - assert.ok(!_.isBoolean('true'), 'the string "true" is not a boolean'); - assert.ok(!_.isBoolean(arguments), 'the arguments object is not a boolean'); - assert.ok(!_.isBoolean(void 0), 'undefined is not a boolean'); - assert.ok(!_.isBoolean(NaN), 'NaN is not a boolean'); - assert.ok(!_.isBoolean(null), 'null is not a boolean'); + assert.notOk(_.isBoolean(2), 'a number is not a boolean'); + assert.notOk(_.isBoolean('string'), 'a string is not a boolean'); + assert.notOk(_.isBoolean('false'), 'the string "false" is not a boolean'); + assert.notOk(_.isBoolean('true'), 'the string "true" is not a boolean'); + assert.notOk(_.isBoolean(arguments), 'the arguments object is not a boolean'); + assert.notOk(_.isBoolean(void 0), 'undefined is not a boolean'); + assert.notOk(_.isBoolean(NaN), 'NaN is not a boolean'); + assert.notOk(_.isBoolean(null), 'null is not a boolean'); assert.ok(_.isBoolean(true), 'but true is'); assert.ok(_.isBoolean(false), 'and so is false'); }); QUnit.test('isMap', function(assert) { - assert.ok(!_.isMap('string'), 'a string is not a map'); - assert.ok(!_.isMap(2), 'a number is not a map'); - assert.ok(!_.isMap({}), 'an object is not a map'); - assert.ok(!_.isMap(false), 'a boolean is not a map'); - assert.ok(!_.isMap(void 0), 'undefined is not a map'); - assert.ok(!_.isMap([1, 2, 3]), 'an array is not a map'); + assert.notOk(_.isMap('string'), 'a string is not a map'); + assert.notOk(_.isMap(2), 'a number is not a map'); + assert.notOk(_.isMap({}), 'an object is not a map'); + assert.notOk(_.isMap(false), 'a boolean is not a map'); + assert.notOk(_.isMap(void 0), 'undefined is not a map'); + assert.notOk(_.isMap([1, 2, 3]), 'an array is not a map'); if (typeof Set === 'function') { - assert.ok(!_.isMap(new Set()), 'a set is not a map'); + assert.notOk(_.isMap(new Set()), 'a set is not a map'); } if (typeof WeakSet === 'function') { - assert.ok(!_.isMap(new WeakSet()), 'a weakset is not a map'); + assert.notOk(_.isMap(new WeakSet()), 'a weakset is not a map'); } if (typeof WeakMap === 'function') { - assert.ok(!_.isMap(new WeakMap()), 'a weakmap is not a map'); + assert.notOk(_.isMap(new WeakMap()), 'a weakmap is not a map'); } if (typeof Map === 'function') { var keyString = 'a string'; @@ -706,20 +706,20 @@ }); QUnit.test('isWeakMap', function(assert) { - assert.ok(!_.isWeakMap('string'), 'a string is not a weakmap'); - assert.ok(!_.isWeakMap(2), 'a number is not a weakmap'); - assert.ok(!_.isWeakMap({}), 'an object is not a weakmap'); - assert.ok(!_.isWeakMap(false), 'a boolean is not a weakmap'); - assert.ok(!_.isWeakMap(void 0), 'undefined is not a weakmap'); - assert.ok(!_.isWeakMap([1, 2, 3]), 'an array is not a weakmap'); + assert.notOk(_.isWeakMap('string'), 'a string is not a weakmap'); + assert.notOk(_.isWeakMap(2), 'a number is not a weakmap'); + assert.notOk(_.isWeakMap({}), 'an object is not a weakmap'); + assert.notOk(_.isWeakMap(false), 'a boolean is not a weakmap'); + assert.notOk(_.isWeakMap(void 0), 'undefined is not a weakmap'); + assert.notOk(_.isWeakMap([1, 2, 3]), 'an array is not a weakmap'); if (typeof Set === 'function') { - assert.ok(!_.isWeakMap(new Set()), 'a set is not a weakmap'); + assert.notOk(_.isWeakMap(new Set()), 'a set is not a weakmap'); } if (typeof WeakSet === 'function') { - assert.ok(!_.isWeakMap(new WeakSet()), 'a weakset is not a weakmap'); + assert.notOk(_.isWeakMap(new WeakSet()), 'a weakset is not a weakmap'); } if (typeof Map === 'function') { - assert.ok(!_.isWeakMap(new Map()), 'a map is not a weakmap'); + assert.notOk(_.isWeakMap(new Map()), 'a map is not a weakmap'); } if (typeof WeakMap === 'function') { var keyObj = {}, obj = new WeakMap(); @@ -729,20 +729,20 @@ }); QUnit.test('isSet', function(assert) { - assert.ok(!_.isSet('string'), 'a string is not a set'); - assert.ok(!_.isSet(2), 'a number is not a set'); - assert.ok(!_.isSet({}), 'an object is not a set'); - assert.ok(!_.isSet(false), 'a boolean is not a set'); - assert.ok(!_.isSet(void 0), 'undefined is not a set'); - assert.ok(!_.isSet([1, 2, 3]), 'an array is not a set'); + assert.notOk(_.isSet('string'), 'a string is not a set'); + assert.notOk(_.isSet(2), 'a number is not a set'); + assert.notOk(_.isSet({}), 'an object is not a set'); + assert.notOk(_.isSet(false), 'a boolean is not a set'); + assert.notOk(_.isSet(void 0), 'undefined is not a set'); + assert.notOk(_.isSet([1, 2, 3]), 'an array is not a set'); if (typeof Map === 'function') { - assert.ok(!_.isSet(new Map()), 'a map is not a set'); + assert.notOk(_.isSet(new Map()), 'a map is not a set'); } if (typeof WeakMap === 'function') { - assert.ok(!_.isSet(new WeakMap()), 'a weakmap is not a set'); + assert.notOk(_.isSet(new WeakMap()), 'a weakmap is not a set'); } if (typeof WeakSet === 'function') { - assert.ok(!_.isSet(new WeakSet()), 'a weakset is not a set'); + assert.notOk(_.isSet(new WeakSet()), 'a weakset is not a set'); } if (typeof Set === 'function') { var obj = new Set(); @@ -753,20 +753,20 @@ QUnit.test('isWeakSet', function(assert) { - assert.ok(!_.isWeakSet('string'), 'a string is not a weakset'); - assert.ok(!_.isWeakSet(2), 'a number is not a weakset'); - assert.ok(!_.isWeakSet({}), 'an object is not a weakset'); - assert.ok(!_.isWeakSet(false), 'a boolean is not a weakset'); - assert.ok(!_.isWeakSet(void 0), 'undefined is not a weakset'); - assert.ok(!_.isWeakSet([1, 2, 3]), 'an array is not a weakset'); + assert.notOk(_.isWeakSet('string'), 'a string is not a weakset'); + assert.notOk(_.isWeakSet(2), 'a number is not a weakset'); + assert.notOk(_.isWeakSet({}), 'an object is not a weakset'); + assert.notOk(_.isWeakSet(false), 'a boolean is not a weakset'); + assert.notOk(_.isWeakSet(void 0), 'undefined is not a weakset'); + assert.notOk(_.isWeakSet([1, 2, 3]), 'an array is not a weakset'); if (typeof Map === 'function') { - assert.ok(!_.isWeakSet(new Map()), 'a map is not a weakset'); + assert.notOk(_.isWeakSet(new Map()), 'a map is not a weakset'); } if (typeof WeakMap === 'function') { - assert.ok(!_.isWeakSet(new WeakMap()), 'a weakmap is not a weakset'); + assert.notOk(_.isWeakSet(new WeakMap()), 'a weakmap is not a weakset'); } if (typeof Set === 'function') { - assert.ok(!_.isWeakSet(new Set()), 'a set is not a weakset'); + assert.notOk(_.isWeakSet(new Set()), 'a set is not a weakset'); } if (typeof WeakSet === 'function') { var obj = new WeakSet(); @@ -776,19 +776,19 @@ }); QUnit.test('isFunction', function(assert) { - assert.ok(!_.isFunction(void 0), 'undefined vars are not functions'); - assert.ok(!_.isFunction([1, 2, 3]), 'arrays are not functions'); - assert.ok(!_.isFunction('moe'), 'strings are not functions'); + assert.notOk(_.isFunction(void 0), 'undefined vars are not functions'); + assert.notOk(_.isFunction([1, 2, 3]), 'arrays are not functions'); + assert.notOk(_.isFunction('moe'), 'strings are not functions'); assert.ok(_.isFunction(_.isFunction), 'but functions are'); assert.ok(_.isFunction(function(){}), 'even anonymous ones'); if (testElement) { - assert.ok(!_.isFunction(testElement), 'elements are not functions'); + assert.notOk(_.isFunction(testElement), 'elements are not functions'); } var nodelist = typeof document != 'undefined' && document.childNodes; if (nodelist) { - assert.ok(!_.isFunction(nodelist)); + assert.notOk(_.isFunction(nodelist)); } }); @@ -806,65 +806,65 @@ } QUnit.test('isDate', function(assert) { - assert.ok(!_.isDate(100), 'numbers are not dates'); - assert.ok(!_.isDate({}), 'objects are not dates'); + assert.notOk(_.isDate(100), 'numbers are not dates'); + assert.notOk(_.isDate({}), 'objects are not dates'); assert.ok(_.isDate(new Date()), 'but dates are'); }); QUnit.test('isRegExp', function(assert) { - assert.ok(!_.isRegExp(_.identity), 'functions are not RegExps'); + assert.notOk(_.isRegExp(_.identity), 'functions are not RegExps'); assert.ok(_.isRegExp(/identity/), 'but RegExps are'); }); QUnit.test('isFinite', function(assert) { - assert.ok(!_.isFinite(void 0), 'undefined is not finite'); - assert.ok(!_.isFinite(null), 'null is not finite'); - assert.ok(!_.isFinite(NaN), 'NaN is not finite'); - assert.ok(!_.isFinite(Infinity), 'Infinity is not finite'); - assert.ok(!_.isFinite(-Infinity), '-Infinity is not finite'); + assert.notOk(_.isFinite(void 0), 'undefined is not finite'); + assert.notOk(_.isFinite(null), 'null is not finite'); + assert.notOk(_.isFinite(NaN), 'NaN is not finite'); + assert.notOk(_.isFinite(Infinity), 'Infinity is not finite'); + assert.notOk(_.isFinite(-Infinity), '-Infinity is not finite'); assert.ok(_.isFinite('12'), 'Numeric strings are numbers'); - assert.ok(!_.isFinite('1a'), 'Non numeric strings are not numbers'); - assert.ok(!_.isFinite(''), 'Empty strings are not numbers'); + assert.notOk(_.isFinite('1a'), 'Non numeric strings are not numbers'); + assert.notOk(_.isFinite(''), 'Empty strings are not numbers'); var obj = new Number(5); assert.ok(_.isFinite(obj), 'Number instances can be finite'); assert.ok(_.isFinite(0), '0 is finite'); assert.ok(_.isFinite(123), 'Ints are finite'); assert.ok(_.isFinite(-12.44), 'Floats are finite'); if (typeof Symbol === 'function') { - assert.ok(!_.isFinite(Symbol()), 'symbols are not numbers'); - assert.ok(!_.isFinite(Symbol('description')), 'described symbols are not numbers'); - assert.ok(!_.isFinite(Object(Symbol())), 'boxed symbols are not numbers'); + assert.notOk(_.isFinite(Symbol()), 'symbols are not numbers'); + assert.notOk(_.isFinite(Symbol('description')), 'described symbols are not numbers'); + assert.notOk(_.isFinite(Object(Symbol())), 'boxed symbols are not numbers'); } }); QUnit.test('isNaN', function(assert) { - assert.ok(!_.isNaN(void 0), 'undefined is not NaN'); - assert.ok(!_.isNaN(null), 'null is not NaN'); - assert.ok(!_.isNaN(0), '0 is not NaN'); - assert.ok(!_.isNaN(new Number(0)), 'wrapped 0 is not NaN'); + assert.notOk(_.isNaN(void 0), 'undefined is not NaN'); + assert.notOk(_.isNaN(null), 'null is not NaN'); + assert.notOk(_.isNaN(0), '0 is not NaN'); + assert.notOk(_.isNaN(new Number(0)), 'wrapped 0 is not NaN'); assert.ok(_.isNaN(NaN), 'but NaN is'); assert.ok(_.isNaN(new Number(NaN)), 'wrapped NaN is still NaN'); }); QUnit.test('isNull', function(assert) { - assert.ok(!_.isNull(void 0), 'undefined is not null'); - assert.ok(!_.isNull(NaN), 'NaN is not null'); + assert.notOk(_.isNull(void 0), 'undefined is not null'); + assert.notOk(_.isNull(NaN), 'NaN is not null'); assert.ok(_.isNull(null), 'but null is'); }); QUnit.test('isUndefined', function(assert) { - assert.ok(!_.isUndefined(1), 'numbers are defined'); - assert.ok(!_.isUndefined(null), 'null is defined'); - assert.ok(!_.isUndefined(false), 'false is defined'); - assert.ok(!_.isUndefined(NaN), 'NaN is defined'); + assert.notOk(_.isUndefined(1), 'numbers are defined'); + assert.notOk(_.isUndefined(null), 'null is defined'); + assert.notOk(_.isUndefined(false), 'false is defined'); + assert.notOk(_.isUndefined(NaN), 'NaN is defined'); assert.ok(_.isUndefined(), 'nothing is undefined'); assert.ok(_.isUndefined(void 0), 'undefined is undefined'); }); QUnit.test('isError', function(assert) { - assert.ok(!_.isError(1), 'numbers are not Errors'); - assert.ok(!_.isError(null), 'null is not an Error'); - assert.ok(!_.isError(Error), 'functions are not Errors'); + assert.notOk(_.isError(1), 'numbers are not Errors'); + assert.notOk(_.isError(null), 'null is not an Error'); + assert.notOk(_.isError(Error), 'functions are not Errors'); assert.ok(_.isError(new Error()), 'Errors are Errors'); assert.ok(_.isError(new EvalError()), 'EvalErrors are Errors'); assert.ok(_.isError(new RangeError()), 'RangeErrors are Errors'); @@ -893,13 +893,13 @@ QUnit.test('has', function(assert) { var obj = {foo: 'bar', func: function(){}}; assert.ok(_.has(obj, 'foo'), 'has() checks that the object has a property.'); - assert.ok(!_.has(obj, 'baz'), "has() returns false if the object doesn't have the property."); + assert.notOk(_.has(obj, 'baz'), "has() returns false if the object doesn't have the property."); assert.ok(_.has(obj, 'func'), 'has() works for functions too.'); obj.hasOwnProperty = null; assert.ok(_.has(obj, 'foo'), 'has() works even when the hasOwnProperty method is deleted.'); var child = {}; child.prototype = obj; - assert.ok(!_.has(child, 'foo'), 'has() does not check the prototype chain for a property.'); + assert.notOk(_.has(child, 'foo'), 'has() does not check the prototype chain for a property.'); assert.strictEqual(_.has(null, 'foo'), false, 'has() returns false for null'); assert.strictEqual(_.has(void 0, 'foo'), false, 'has() returns false for undefined'); }); diff --git a/test/utility.js b/test/utility.js index 60d387597..b7ded62b1 100644 --- a/test/utility.js +++ b/test/utility.js @@ -370,9 +370,9 @@ }); QUnit.test('#547 - _.templateSettings is unchanged by custom settings.', function(assert) { - assert.ok(!_.templateSettings.variable); + assert.notOk(_.templateSettings.variable); _.template('', {}, {variable: 'x'}); - assert.ok(!_.templateSettings.variable); + assert.notOk(_.templateSettings.variable); }); QUnit.test('#556 - undefined template variables.', function(assert) { @@ -397,11 +397,11 @@ assert.expect(2); var count = 0; var template = _.template('<%= f() %>'); - template({f: function(){ assert.ok(!count++); }}); + template({f: function(){ assert.notOk(count++); }}); var countEscaped = 0; var templateEscaped = _.template('<%- f() %>'); - templateEscaped({f: function(){ assert.ok(!countEscaped++); }}); + templateEscaped({f: function(){ assert.notOk(countEscaped++); }}); }); QUnit.test('#746 - _.template settings are not modified.', function(assert) { From d988667b708592eecdc6fb3348c0f9848535c8d7 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Tue, 5 Apr 2016 10:37:00 -0700 Subject: [PATCH 171/263] Point users to Gitter instead of IRC --- index.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 68634baa0..366108f1a 100644 --- a/index.html +++ b/index.html @@ -396,8 +396,9 @@ The project is hosted on GitHub. You can report bugs and discuss features on the - issues page, or - on Freenode in the #documentcloud channel. + issues page, + on Freenode in the #documentcloud channel, or in our Gitter + channel.

    From 10d2d64dc92e8939e64ce38039051870139956ce Mon Sep 17 00:00:00 2001 From: marija Date: Wed, 13 Apr 2016 15:09:28 +0200 Subject: [PATCH 172/263] optimization of _.isNaN --- underscore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/underscore.js b/underscore.js index 78c709bd3..b39f6733b 100644 --- a/underscore.js +++ b/underscore.js @@ -1316,7 +1316,7 @@ // Is the given value `NaN`? _.isNaN = function(obj) { - return _.isNumber(obj) && isNaN(obj); + return isNaN(obj) && _.isNumber(obj); }; // Is a given value a boolean? From 929fc74b880a87e669229597b9c5cf6cdd2dbf14 Mon Sep 17 00:00:00 2001 From: Amit Evron Date: Sat, 16 Apr 2016 00:03:17 +0300 Subject: [PATCH 173/263] Return self after using mixin() will allow user to chain mixin(), and most importantly use mixin() in the dependency section in the file. --- underscore.js | 1 + 1 file changed, 1 insertion(+) diff --git a/underscore.js b/underscore.js index ad065442b..1630b7286 100644 --- a/underscore.js +++ b/underscore.js @@ -1573,6 +1573,7 @@ return chainResult(this, func.apply(_, args)); }; }); + return _; }; // Add all of the Underscore functions to the wrapper object. From 8707c00bae6a264d58f90de15d67ba5b1aca737d Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Mon, 18 Apr 2016 20:22:11 -0700 Subject: [PATCH 174/263] Add tests and docs for `_.mixin` chaining This change was introduced in #2502 without tests or docs. This commit adds those two missing pieces. --- index.html | 4 ++-- test/utility.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 366108f1a..429f0a2d5 100644 --- a/index.html +++ b/index.html @@ -1886,8 +1886,8 @@

    Utility Functions


    Allows you to extend Underscore with your own utility functions. Pass a hash of {name: function} definitions to have your functions - added to the Underscore object, as well as the OOP wrapper. -

    + added to the Underscore object, as well as the OOP wrapper. Returns the + Underscore object to facilitate chaining.

     _.mixin({
       capitalize: function(string) {
    diff --git a/test/utility.js b/test/utility.js
    index b7ded62b1..6a81e8735 100644
    --- a/test/utility.js
    +++ b/test/utility.js
    @@ -137,11 +137,12 @@
       });
     
       QUnit.test('mixin', function(assert) {
    -    _.mixin({
    +    var ret = _.mixin({
           myReverse: function(string) {
             return string.split('').reverse().join('');
           }
         });
    +    assert.equal(ret, _, 'returns the _ object to facilitate chaining');
         assert.equal(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _');
         assert.equal(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper');
       });
    
    From e90055343e86122782cbcc7623dab0770e48c132 Mon Sep 17 00:00:00 2001
    From: Jordan Eldredge 
    Date: Sat, 23 Apr 2016 10:51:44 -0700
    Subject: [PATCH 175/263] Cleanup docs for methods with multiple aliases
    
    Our formatting was a bit inconsistent.
    ---
     index.html | 6 +++---
     1 file changed, 3 insertions(+), 3 deletions(-)
    
    diff --git a/index.html b/index.html
    index 366108f1a..69a53d139 100644
    --- a/index.html
    +++ b/index.html
    @@ -499,7 +499,7 @@ 

    Collection Functions (Arrays or Objects)

    reduce_.reduce(list, iteratee, [memo], [context]) - Aliases: inject, foldl + Aliases: inject, foldl
    Also known as inject and foldl, reduce boils down a list of values into a single value. Memo is the initial state of the reduction, and each successive step of it should be returned by @@ -826,7 +826,7 @@

    Array Functions

    first_.first(array, [n]) - Alias: head, take + Aliases: head, take
    Returns the first element of an array. Passing n will return the first n elements of the array. @@ -861,7 +861,7 @@

    Array Functions

    rest_.rest(array, [index]) - Alias: tail, drop + Aliases: tail, drop
    Returns the rest of the elements in an array. Pass an index to return the values of the array from that index onward. From 507b858e0ba99d158b1ad61d97f260e7d7ec04f9 Mon Sep 17 00:00:00 2001 From: codefalling Date: Sun, 24 Apr 2016 18:59:51 +0800 Subject: [PATCH 176/263] check length in _.first and _.last, fix #2495 --- test/arrays.js | 8 ++++++++ underscore.js | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index 748edea4f..319994aa1 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -15,6 +15,10 @@ result = _.map([[1, 2, 3], [1, 2, 3]], _.first); assert.deepEqual(result, [1, 1], 'works well with _.map'); assert.equal(_.first(null), void 0, 'returns undefined when called on null'); + + Array.prototype[0] = 'boo'; + assert.equal(_.first([]), void 0, 'return undefined when called on a empty array'); + delete Array.prototype[0]; }); QUnit.test('head', function(assert) { @@ -66,6 +70,10 @@ result = _.map([[1, 2, 3], [1, 2, 3]], _.last); assert.deepEqual(result, [3, 3], 'works well with _.map'); assert.equal(_.last(null), void 0, 'returns undefined when called on null'); + + var arr = []; + arr[-1] = 'boo'; + assert.equal(_.last(arr), void 0, 'return undefined when called on a empty array'); }); QUnit.test('compact', function(assert) { diff --git a/underscore.js b/underscore.js index 1630b7286..84713e688 100644 --- a/underscore.js +++ b/underscore.js @@ -467,7 +467,7 @@ // values in the array. Aliased as `head` and `take`. The **guard** check // allows it to work with `_.map`. _.first = _.head = _.take = function(array, n, guard) { - if (array == null) return void 0; + if (array == null || array.length < 1) return void 0; if (n == null || guard) return array[0]; return _.initial(array, array.length - n); }; @@ -482,7 +482,7 @@ // Get the last element of an array. Passing **n** will return the last N // values in the array. _.last = function(array, n, guard) { - if (array == null) return void 0; + if (array == null || array.length < 1) return void 0; if (n == null || guard) return array[array.length - 1]; return _.rest(array, Math.max(0, array.length - n)); }; From d484f8ff4486c6894684af2bbbb0834a38d69674 Mon Sep 17 00:00:00 2001 From: codefalling Date: Mon, 25 Apr 2016 17:19:25 +0800 Subject: [PATCH 177/263] safer _#toString, fix #2498 --- underscore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/underscore.js b/underscore.js index 84713e688..ab2f75f36 100644 --- a/underscore.js +++ b/underscore.js @@ -1608,7 +1608,7 @@ _.prototype.valueOf = _.prototype.toJSON = _.prototype.value; _.prototype.toString = function() { - return '' + this._wrapped; + return String(this._wrapped); }; // AMD registration happens at the end for compatibility with AMD loaders From ef9ee4b7f54ecf4387f681d54dbca36ad0cb0e85 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sat, 23 Apr 2016 10:49:42 -0700 Subject: [PATCH 178/263] Make functions in docs filterable With a utility library like Underscore, it is very common to open the docs with the aim of locating the documentation for a specific function. Currently I use my browser's search functionality, but since many functions reference other functions, I frequently have to cycle through multiple matches before I arrive at the actual function's description. This patch aims to improve this common use case by offering a "filter" input above the list of functions. As a user types, the list of functions (and section titles) is reduced, leaving only the matching functions. If the user presses enter, they are jumped to the documentation for the first matching function. This functionality (and implementation) is greatly inspired by the implementation that is included in the [Ramda docs](http://ramdajs.com/0.21.0/docs/). You can read their source code [here](https://github.com/ramda/ramda.github.io/blob/master/main.js) --- docs/.eslintrc | 5 + docs/main.js | 58 +++++++++ index.html | 339 ++++++++++++++++++++++++++----------------------- 3 files changed, 246 insertions(+), 156 deletions(-) create mode 100644 docs/.eslintrc create mode 100644 docs/main.js diff --git a/docs/.eslintrc b/docs/.eslintrc new file mode 100644 index 000000000..aac42a91c --- /dev/null +++ b/docs/.eslintrc @@ -0,0 +1,5 @@ +{ + "globals": { + "_": true + } +} diff --git a/docs/main.js b/docs/main.js new file mode 100644 index 000000000..b46891fca --- /dev/null +++ b/docs/main.js @@ -0,0 +1,58 @@ +(function() { + var functions = document.querySelectorAll('[data-name]'); + var sections = document.querySelectorAll('.searchable_section'); + var searchInput = document.getElementById('function_filter'); + + function strIn(a, b) { + a = a.toLowerCase(); + b = b.toLowerCase(); + return b.indexOf(a) >= 0; + } + + function doesMatch(element) { + var name = element.getAttribute('data-name'); + var aliases = element.getAttribute('data-aliases') || ''; + return strIn(searchInput.value, name) || strIn(searchInput.value, aliases); + } + + function filterElement(element) { + element.style.display = doesMatch(element) ? '' : 'none'; + } + + function filterToc() { + _.each(functions, filterElement); + + var emptySearch = searchInput.value === ''; + + // Hide the titles of empty sections + _.each(sections, function(section) { + var sectionFunctions = section.querySelectorAll('[data-name]'); + var showSection = emptySearch || _.some(sectionFunctions, doesMatch); + section.style.display = showSection ? '' : 'none'; + }); + } + + function gotoFirst() { + var firstFunction = _.find(functions, doesMatch); + if(firstFunction) { + window.location.hash = firstFunction.getAttribute('data-name'); + searchInput.focus(); + } + } + + searchInput.addEventListener('input', filterToc, false); + + // Press "Enter" to jump to the first matching function + searchInput.addEventListener('keypress', function(e) { + if (e.which === 13) { + gotoFirst(); + } + }); + + // Press "/" to search + document.body.addEventListener('keyup', function(event) { + if (191 === event.which) { + searchInput.focus(); + } + }); +}()); diff --git a/index.html b/index.html index 366108f1a..79b72363f 100644 --- a/index.html +++ b/index.html @@ -64,6 +64,9 @@ .toc_section li a:hover { text-decoration: underline; } + input#function_filter { + width: 80%; + } div.container { width: 550px; margin: 40px 0 50px 260px; @@ -188,170 +191,193 @@

  • » Underscore-contrib
  • - - Introduction - + - - Collections - - + - - Arrays - - +
    + + Collections + + +
    - - Functions - - +
    + + Arrays + + +
    - - Objects - - +
    + + Functions + + +
    - - Utility - - +
    + + Objects + + +
    - - OOP Style - - - Chaining - - +
    + + Utility + + +
    - - Links - + - - Change Log - +
    + + Chaining + + +
    + + + + @@ -3223,6 +3249,7 @@

    Change Log

    + From e1741aba46db0a24c45a8aec8f05414fa3c2c73d Mon Sep 17 00:00:00 2001 From: codefalling Date: Thu, 28 Apr 2016 20:53:16 +0800 Subject: [PATCH 179/263] fix #2503-_.isNaN errors on symbols --- test/objects.js | 3 +++ underscore.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/test/objects.js b/test/objects.js index 91c49a4cc..aaa1db94d 100644 --- a/test/objects.js +++ b/test/objects.js @@ -844,6 +844,9 @@ assert.notOk(_.isNaN(new Number(0)), 'wrapped 0 is not NaN'); assert.ok(_.isNaN(NaN), 'but NaN is'); assert.ok(_.isNaN(new Number(NaN)), 'wrapped NaN is still NaN'); + if (typeof Symbol !== 'undefined'){ + assert.notOk(_.isNaN(Symbol()), 'symbol is not NaN'); + } }); QUnit.test('isNull', function(assert) { diff --git a/underscore.js b/underscore.js index ab2f75f36..4a0cf6e73 100644 --- a/underscore.js +++ b/underscore.js @@ -1320,7 +1320,7 @@ // Is the given value `NaN`? _.isNaN = function(obj) { - return isNaN(obj) && _.isNumber(obj); + return _.isNumber(obj) && isNaN(obj); }; // Is a given value a boolean? From f0b9f4fc925663f10283c43bd2944a00fb21ef0d Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Fri, 29 Apr 2016 09:36:55 -0700 Subject: [PATCH 180/263] Use the irregular id when jumping to `functions` Because the "Functions" section heading already uses `#functions` the individual method uses an irregular id. I missed this in #2514. --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 3b22dc140..656ec44c8 100644 --- a/index.html +++ b/index.html @@ -294,7 +294,7 @@
  • - pairs
  • - invert
  • - create
  • -
  • - functions
  • +
  • - functions
  • - findKey
  • - extend
  • - extendOwn
  • From 9bf5d8ff6840cfb4cb9ebba7daca307bbb829368 Mon Sep 17 00:00:00 2001 From: sibo Date: Tue, 31 May 2016 14:07:48 +0800 Subject: [PATCH 181/263] Remove Simplified Chinese link It's not available now. --- index.html | 5 ----- 1 file changed, 5 deletions(-) diff --git a/index.html b/index.html index 656ec44c8..a768c3685 100644 --- a/index.html +++ b/index.html @@ -2188,11 +2188,6 @@

    Chaining

    -

    - The Underscore documentation is also available in - Simplified Chinese. -

    -

    Underscore.lua, a Lua port of the functions that are applicable in both languages. From b98304c81e6555a20645eefa775961ea7af54244 Mon Sep 17 00:00:00 2001 From: ikasumi_wt Date: Sat, 25 Jun 2016 16:43:29 +0900 Subject: [PATCH 182/263] matcher move to propertyOf in index.html --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index a768c3685..e897d604b 100644 --- a/index.html +++ b/index.html @@ -304,9 +304,9 @@

  • - clone
  • - tap
  • - has
  • -
  • - matcher
  • - property
  • - propertyOf
  • +
  • - matcher
  • - isEqual
  • - isMatch
  • - isEmpty
  • From 4051c4af940825a77528c2562cfdde50d120ec3b Mon Sep 17 00:00:00 2001 From: tnga Date: Sat, 25 Jun 2016 19:15:50 +0100 Subject: [PATCH 183/263] fix (eslint) warning "index" used outside of binding context --- underscore.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/underscore.js b/underscore.js index 4a0cf6e73..fe8f45d15 100644 --- a/underscore.js +++ b/underscore.js @@ -109,9 +109,10 @@ var restArgs = function(func, startIndex) { startIndex = startIndex == null ? func.length - 1 : +startIndex; return function() { - var length = Math.max(arguments.length - startIndex, 0); - var rest = Array(length); - for (var index = 0; index < length; index++) { + var length = Math.max(arguments.length - startIndex, 0), + rest = Array(length), + index = 0; + for (index = 0; index < length; index++) { rest[index] = arguments[index + startIndex]; } switch (startIndex) { From 563755108a9705ba9231c0d894fd12455fbe4a04 Mon Sep 17 00:00:00 2001 From: Janelle Delbello Date: Sat, 9 Jul 2016 00:06:30 -0600 Subject: [PATCH 184/263] Note in documentation that non-numerical values don't work for max/min --- index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index e897d604b..3609c26e2 100644 --- a/index.html +++ b/index.html @@ -694,7 +694,7 @@

    Collection Functions (Arrays or Objects)

    function is provided, it will be used on each value to generate the criterion by which the value is ranked. -Infinity is returned if list is empty, so an isEmpty guard - may be required. + may be required. Non-numerical values in list will be ignored.

     var stooges = [{name: 'moe', age: 40}, {name: 'larry', age: 50}, {name: 'curly', age: 60}];
    @@ -709,7 +709,7 @@ 

    Collection Functions (Arrays or Objects)

    function is provided, it will be used on each value to generate the criterion by which the value is ranked. Infinity is returned if list is empty, so an isEmpty guard - may be required. + may be required. Non-numerical values in list will be ignored.

     var numbers = [10, 5, 100, 2, 1000];
    
    From 05da85ab497ff3599adaf5cd865b156541229b34 Mon Sep 17 00:00:00 2001
    From: tnga 
    Date: Wed, 13 Jul 2016 15:35:02 +0100
    Subject: [PATCH 185/263] adjust fix for (eslint) warning index used outside...
    
    ---
     underscore.js | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/underscore.js b/underscore.js
    index fe8f45d15..e0afacf07 100644
    --- a/underscore.js
    +++ b/underscore.js
    @@ -112,7 +112,7 @@
           var length = Math.max(arguments.length - startIndex, 0),
               rest = Array(length),
               index = 0;
    -      for (index = 0; index < length; index++) {
    +      for (; index < length; index++) {
             rest[index] = arguments[index + startIndex];
           }
           switch (startIndex) {
    
    From 27e08b29c6bd34958ad33331af253237e2be51ed Mon Sep 17 00:00:00 2001
    From: Ben Joostens 
    Date: Thu, 14 Jul 2016 23:14:09 +0200
    Subject: [PATCH 186/263] Improve readability of underscorejs.org TOC
    
    ---
     index.html | 5 ++---
     1 file changed, 2 insertions(+), 3 deletions(-)
    
    diff --git a/index.html b/index.html
    index 3609c26e2..a6d45fa0f 100644
    --- a/index.html
    +++ b/index.html
    @@ -46,12 +46,11 @@
             font-weight: normal;
           }
         ul.toc_section {
    -      font-size: 11px;
    -      line-height: 14px;
    +      font-size: 14px;
    +      line-height: 16px;
           margin: 5px 0 0 0;
           padding-left: 0px;
           list-style-type: none;
    -      font-family: Lucida Grande;
         }
           .toc_section li {
             cursor: pointer;
    
    From 2f4494a351ebdc04faff1852ac8a901d8a64ec62 Mon Sep 17 00:00:00 2001
    From: Sakthipriyan Vairamani 
    Date: Thu, 21 Jul 2016 12:46:48 +0530
    Subject: [PATCH 187/263] minor documentation improvement
    
    ---
     index.html | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/index.html b/index.html
    index 3609c26e2..b0c915890 100644
    --- a/index.html
    +++ b/index.html
    @@ -1583,7 +1583,7 @@ 

    Object Functions

    property_.property(key)
    - Returns a function that will itself return the key + Returns a function that will return the key property of any passed-in object.

    
    From 5084b51dcd263e0576a078b65b95197b748214c5 Mon Sep 17 00:00:00 2001
    From: Janelle Delbello 
    Date: Fri, 29 Jul 2016 23:08:28 -0600
    Subject: [PATCH 188/263] [Documentation] Swap findWhere and where in
     documentation
    
    ---
     index.html | 24 ++++++++++++------------
     1 file changed, 12 insertions(+), 12 deletions(-)
    
    diff --git a/index.html b/index.html
    index f6d02061c..1f640af30 100644
    --- a/index.html
    +++ b/index.html
    @@ -582,18 +582,6 @@ 

    Collection Functions (Arrays or Objects)

    => [2, 4, 6]
    -

    - where_.where(list, properties) -
    - Looks through each value in the list, returning an array of all - the values that contain all of the key-value pairs listed in properties. -

    -
    -_.where(listOfPlays, {author: "Shakespeare", year: 1611});
    -=> [{title: "Cymbeline", author: "Shakespeare", year: 1611},
    -    {title: "The Tempest", author: "Shakespeare", year: 1611}]
    -
    -

    findWhere_.findWhere(list, properties)
    @@ -612,6 +600,18 @@

    Collection Functions (Arrays or Objects)

    conduct of the war."}
    +

    + where_.where(list, properties) +
    + Looks through each value in the list, returning an array of all + the values that contain all of the key-value pairs listed in properties. +

    +
    +_.where(listOfPlays, {author: "Shakespeare", year: 1611});
    +=> [{title: "Cymbeline", author: "Shakespeare", year: 1611},
    +    {title: "The Tempest", author: "Shakespeare", year: 1611}]
    +
    +

    reject_.reject(list, predicate, [context])
    From a9432276b90bd23d3022deb89ac1ba6b10ee7495 Mon Sep 17 00:00:00 2001 From: Adam Krebs Date: Mon, 29 Aug 2016 16:42:34 -0400 Subject: [PATCH 189/263] document that _.extend is shallow. Fixes #2579 --- index.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index 1f640af30..1a4f903d7 100644 --- a/index.html +++ b/index.html @@ -1477,8 +1477,9 @@

    Object Functions

    extend_.extend(destination, *sources)
    - Copy all of the properties in the source objects over to the + Shallowly copy all of the properties in the source objects over to the destination object, and return the destination object. + Any nested objects or arrays will be copied by reference, not duplicated. It's in-order, so the last source will override properties of the same name in previous arguments.

    From 7c08ea8cb939c8376eadcb5655314b08dda4dc81 Mon Sep 17 00:00:00 2001 From: Steve Webster Date: Wed, 31 Aug 2016 01:43:10 -0400 Subject: [PATCH 190/263] Update _.object and _.pairs docs to show relation Long time user, first time contributor. Reading through this well-documented source code, I noticed the link between _.pairs and _.object for the first time. As presented, these 2 related functions are far away from each other (with good reason), so I suggest this highlighting the connection in the annotations. --- index.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 1a4f903d7..670f431b3 100644 --- a/index.html +++ b/index.html @@ -1015,7 +1015,8 @@

    Array Functions

    object_.object(list, [values])
    Converts arrays into objects. Pass either a single list of - [key, value] pairs, or a list of keys, and a list of values. + [key, value] pairs, or a list of keys, and a list of values. The former + usage is invertible through _.pairs. If duplicate keys exist, the last value wins.

    @@ -1424,7 +1425,8 @@ 

    Object Functions

    pairs_.pairs(object)
    - Convert an object into a list of [key, value] pairs. + Convert an object into a list of [key, value] pairs. Inverse of first + usage of _.object.

     _.pairs({one: 1, two: 2, three: 3});
    
    From ca478f0d3b3c1c27188d881c9bf87a8777e20e2a Mon Sep 17 00:00:00 2001
    From: Jordan Eldredge 
    Date: Sat, 1 Oct 2016 19:46:13 -0700
    Subject: [PATCH 191/263] Simplify `_.bind` tests
    
    Pull the assertions out to the top level rather than having them called
    by the function being tested.
    
    While clever, the previous implementation was harder to understand, and
    more error prone. If for some reason `_.bind` does not run the bound
    function at all, the assertion will never get run and tests will
    erroneously appear to pass.
    ---
     test/functions.js | 8 ++++----
     1 file changed, 4 insertions(+), 4 deletions(-)
    
    diff --git a/test/functions.js b/test/functions.js
    index f73f5d382..015ab7baf 100644
    --- a/test/functions.js
    +++ b/test/functions.js
    @@ -29,10 +29,10 @@
         func = _.bind(func, this, 'hello', 'moe', 'curly');
         assert.equal(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments');
     
    -    func = function(ctx, message) { assert.equal(this, ctx, message); };
    -    _.bind(func, 0, 0, 'can bind a function to `0`')();
    -    _.bind(func, '', '', 'can bind a function to an empty string')();
    -    _.bind(func, false, false, 'can bind a function to `false`')();
    +    func = function() { return this; };
    +    assert.equal(_.bind(func, 0)(), 0, 'can bind a function to `0`');
    +    assert.equal(_.bind(func, '')(), '', 'can bind a function to an empty string');
    +    assert.equal(_.bind(func, false)(), false, 'can bind a function to `false`');
     
         // These tests are only meaningful when using a browser without a native bind function
         // To test this with a modern browser, set underscore's nativeBind to undefined
    
    From bfa74a2487ab0e5d5d98d3550012f33b3f8059ca Mon Sep 17 00:00:00 2001
    From: Jordan Eldredge 
    Date: Sat, 1 Oct 2016 19:55:39 -0700
    Subject: [PATCH 192/263] `_.bind`ing to a primitive returns a wrapped obj
    
    Enhance out tests to more clearly document the behavior of binding
    a primitive as the context of a function.
    ---
     test/functions.js | 8 +++++---
     1 file changed, 5 insertions(+), 3 deletions(-)
    
    diff --git a/test/functions.js b/test/functions.js
    index 015ab7baf..31bca207e 100644
    --- a/test/functions.js
    +++ b/test/functions.js
    @@ -30,9 +30,11 @@
         assert.equal(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments');
     
         func = function() { return this; };
    -    assert.equal(_.bind(func, 0)(), 0, 'can bind a function to `0`');
    -    assert.equal(_.bind(func, '')(), '', 'can bind a function to an empty string');
    -    assert.equal(_.bind(func, false)(), false, 'can bind a function to `false`');
    +    assert.strictEqual(typeof _.bind(func, 0)(), 'object', 'binding a primitive to `this` returns a wrapped primitive');
    +
    +    assert.strictEqual(_.bind(func, 0)().valueOf(), 0, 'can bind a function to `0`');
    +    assert.strictEqual(_.bind(func, '')().valueOf(), '', 'can bind a function to an empty string');
    +    assert.strictEqual(_.bind(func, false)().valueOf(), false, 'can bind a function to `false`');
     
         // These tests are only meaningful when using a browser without a native bind function
         // To test this with a modern browser, set underscore's nativeBind to undefined
    
    From 12cf8040694d21f237dee9b20ea0dbce41e25a2e Mon Sep 17 00:00:00 2001
    From: Jordan Eldredge 
    Date: Sat, 1 Oct 2016 20:34:56 -0700
    Subject: [PATCH 193/263] Clarify in tests that obj key is a string
    
    The existing test uses `assert.equal` rather than `assert.strictEqual`
    which accidentally hid a type coercion. This change makes the type
    explicit.
    ---
     test/functions.js | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/test/functions.js b/test/functions.js
    index f73f5d382..660304dd3 100644
    --- a/test/functions.js
    +++ b/test/functions.js
    @@ -712,7 +712,7 @@
         assert.deepEqual(_.filter(collection, /b/g), ['bar', 'bbiz']);
         assert.equal(_.find(collection, /b/g), 'bar');
         assert.equal(_.findIndex(collection, /b/g), 1);
    -    assert.equal(_.findKey(collection, /b/g), 1);
    +    assert.strictEqual(_.findKey(collection, /b/g), '1');
         assert.equal(_.findLastIndex(collection, /b/g), 2);
         assert.deepEqual(_.groupBy(collection, /b/g), {0: ['foo'], 1: ['bar'], 2: ['bbiz']});
         assert.deepEqual(_.indexBy(collection, /b/g), {0: 'foo', 1: 'bar', 2: 'bbiz'});
    
    From 1a2bdf98bef1d4c95bed429b83c43a5b1c65383c Mon Sep 17 00:00:00 2001
    From: Jordan Eldredge 
    Date: Sat, 1 Oct 2016 20:46:55 -0700
    Subject: [PATCH 194/263] Make iterator context tests explicit about type
    
    By using a coercive assertion, the tests were not being explicit about
    the fact that when specifying a primitive as the context for `_.each`,
    the `this` value will actually be a wrapped version of the given
    primitive.
    
    This change simply makes the assertions more explicit.
    ---
     test/collections.js | 4 +++-
     1 file changed, 3 insertions(+), 1 deletion(-)
    
    diff --git a/test/collections.js b/test/collections.js
    index 76ed98c94..a74394073 100644
    --- a/test/collections.js
    +++ b/test/collections.js
    @@ -52,7 +52,9 @@
       QUnit.test('lookupIterator with contexts', function(assert) {
         _.each([true, false, 'yes', '', 0, 1, {}], function(context) {
           _.each([1], function() {
    -        assert.equal(this, context);
    +        assert.strictEqual(typeof this, 'object', 'context is a wrapped primitive');
    +        assert.strictEqual(this.valueOf(), context, 'the unwrapped context is the specified primitive');
    +        assert.equal(this, context, 'context can be coerced to the specified primitive');
           }, context);
         });
       });
    
    From 9b2c36dd2e954e33b4318f615760c2485c18a3d6 Mon Sep 17 00:00:00 2001
    From: Jordan Eldredge 
    Date: Sat, 1 Oct 2016 20:34:03 -0700
    Subject: [PATCH 195/263] Always use strictEqual in tests
    
    By switching all assertions to use `strictEqual` rather than `equal`
    I was able to discover three worthwhile improvements to our tests:
    
    * https://github.com/jashkenas/underscore/pull/2597
    * https://github.com/jashkenas/underscore/pull/2596
    * https://github.com/jashkenas/underscore/pull/2595
    
    I think it's a good idea to have our tests generally prefer the more
    explicit: `strictEqual`. Not only could it help avoid some subtle bug
    going unnoticed, but it also gives the reader of the test suite a more
    concrete description of the expected behavior.
    ---
     test/arrays.js         | 108 ++++++++++----------
     test/chaining.js       |   4 +-
     test/collections.js    | 212 +++++++++++++++++++--------------------
     test/cross-document.js |   6 +-
     test/functions.js      | 222 ++++++++++++++++++++---------------------
     test/objects.js        | 112 ++++++++++-----------
     test/utility.js        |  98 +++++++++---------
     7 files changed, 381 insertions(+), 381 deletions(-)
    
    diff --git a/test/arrays.js b/test/arrays.js
    index 319994aa1..023734601 100644
    --- a/test/arrays.js
    +++ b/test/arrays.js
    @@ -4,20 +4,20 @@
       QUnit.module('Arrays');
     
       QUnit.test('first', function(assert) {
    -    assert.equal(_.first([1, 2, 3]), 1, 'can pull out the first element of an array');
    -    assert.equal(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"');
    +    assert.strictEqual(_.first([1, 2, 3]), 1, 'can pull out the first element of an array');
    +    assert.strictEqual(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"');
         assert.deepEqual(_.first([1, 2, 3], 0), [], 'returns an empty array when n <= 0 (0 case)');
         assert.deepEqual(_.first([1, 2, 3], -1), [], 'returns an empty array when n <= 0 (negative case)');
         assert.deepEqual(_.first([1, 2, 3], 2), [1, 2], 'can fetch the first n elements');
         assert.deepEqual(_.first([1, 2, 3], 5), [1, 2, 3], 'returns the whole array if n > length');
         var result = (function(){ return _.first(arguments); }(4, 3, 2, 1));
    -    assert.equal(result, 4, 'works on an arguments object');
    +    assert.strictEqual(result, 4, 'works on an arguments object');
         result = _.map([[1, 2, 3], [1, 2, 3]], _.first);
         assert.deepEqual(result, [1, 1], 'works well with _.map');
    -    assert.equal(_.first(null), void 0, 'returns undefined when called on null');
    +    assert.strictEqual(_.first(null), void 0, 'returns undefined when called on null');
     
         Array.prototype[0] = 'boo';
    -    assert.equal(_.first([]), void 0, 'return undefined when called on a empty array');
    +    assert.strictEqual(_.first([]), void 0, 'return undefined when called on a empty array');
         delete Array.prototype[0];
       });
     
    @@ -59,21 +59,21 @@
       });
     
       QUnit.test('last', function(assert) {
    -    assert.equal(_.last([1, 2, 3]), 3, 'can pull out the last element of an array');
    -    assert.equal(_([1, 2, 3]).last(), 3, 'can perform OO-style "last()"');
    +    assert.strictEqual(_.last([1, 2, 3]), 3, 'can pull out the last element of an array');
    +    assert.strictEqual(_([1, 2, 3]).last(), 3, 'can perform OO-style "last()"');
         assert.deepEqual(_.last([1, 2, 3], 0), [], 'returns an empty array when n <= 0 (0 case)');
         assert.deepEqual(_.last([1, 2, 3], -1), [], 'returns an empty array when n <= 0 (negative case)');
         assert.deepEqual(_.last([1, 2, 3], 2), [2, 3], 'can fetch the last n elements');
         assert.deepEqual(_.last([1, 2, 3], 5), [1, 2, 3], 'returns the whole array if n > length');
         var result = (function(){ return _(arguments).last(); }(1, 2, 3, 4));
    -    assert.equal(result, 4, 'works on an arguments object');
    +    assert.strictEqual(result, 4, 'works on an arguments object');
         result = _.map([[1, 2, 3], [1, 2, 3]], _.last);
         assert.deepEqual(result, [3, 3], 'works well with _.map');
    -    assert.equal(_.last(null), void 0, 'returns undefined when called on null');
    +    assert.strictEqual(_.last(null), void 0, 'returns undefined when called on null');
     
         var arr = [];
         arr[-1] = 'boo';
    -    assert.equal(_.last(arr), void 0, 'return undefined when called on a empty array');
    +    assert.strictEqual(_.last(arr), void 0, 'return undefined when called on a empty array');
       });
     
       QUnit.test('compact', function(assert) {
    @@ -99,10 +99,10 @@
         list = [[1], [2], [3], [[4]]];
         assert.deepEqual(_.flatten(list, true), [1, 2, 3, [4]], 'can shallowly flatten arrays containing only other arrays');
     
    -    assert.equal(_.flatten([_.range(10), _.range(10), 5, 1, 3], true).length, 23, 'can flatten medium length arrays');
    -    assert.equal(_.flatten([_.range(10), _.range(10), 5, 1, 3]).length, 23, 'can shallowly flatten medium length arrays');
    -    assert.equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3]).length, 1056003, 'can handle massive arrays');
    -    assert.equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3], true).length, 1056003, 'can handle massive arrays in shallow mode');
    +    assert.strictEqual(_.flatten([_.range(10), _.range(10), 5, 1, 3], true).length, 23, 'can flatten medium length arrays');
    +    assert.strictEqual(_.flatten([_.range(10), _.range(10), 5, 1, 3]).length, 23, 'can shallowly flatten medium length arrays');
    +    assert.strictEqual(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3]).length, 1056003, 'can handle massive arrays');
    +    assert.strictEqual(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3], true).length, 1056003, 'can handle massive arrays in shallow mode');
     
         var x = _.range(100000);
         for (var i = 0; i < 1000; i++) x = [x];
    @@ -124,9 +124,9 @@
       QUnit.test('sortedIndex', function(assert) {
         var numbers = [10, 20, 30, 40, 50];
         var indexFor35 = _.sortedIndex(numbers, 35);
    -    assert.equal(indexFor35, 3, 'finds the index at which a value should be inserted to retain order');
    +    assert.strictEqual(indexFor35, 3, 'finds the index at which a value should be inserted to retain order');
         var indexFor30 = _.sortedIndex(numbers, 30);
    -    assert.equal(indexFor30, 2, 'finds the smallest index at which a value could be inserted to retain order');
    +    assert.strictEqual(indexFor30, 2, 'finds the smallest index at which a value could be inserted to retain order');
     
         var objects = [{x: 10}, {x: 20}, {x: 30}, {x: 40}];
         var iterator = function(obj){ return obj.x; };
    @@ -145,7 +145,7 @@
         while (length--) {
           largeArray[values[length]] = values[length];
         }
    -    assert.equal(_.sortedIndex(largeArray, 2147483648), 2147483648, 'works with large indexes');
    +    assert.strictEqual(_.sortedIndex(largeArray, 2147483648), 2147483648, 'works with large indexes');
       });
     
       QUnit.test('uniq', function(assert) {
    @@ -300,41 +300,41 @@
     
       QUnit.test('indexOf', function(assert) {
         var numbers = [1, 2, 3];
    -    assert.equal(_.indexOf(numbers, 2), 1, 'can compute indexOf');
    +    assert.strictEqual(_.indexOf(numbers, 2), 1, 'can compute indexOf');
         var result = (function(){ return _.indexOf(arguments, 2); }(1, 2, 3));
    -    assert.equal(result, 1, 'works on an arguments object');
    +    assert.strictEqual(result, 1, 'works on an arguments object');
     
         _.each([null, void 0, [], false], function(val) {
           var msg = 'Handles: ' + (_.isArray(val) ? '[]' : val);
    -      assert.equal(_.indexOf(val, 2), -1, msg);
    -      assert.equal(_.indexOf(val, 2, -1), -1, msg);
    -      assert.equal(_.indexOf(val, 2, -20), -1, msg);
    -      assert.equal(_.indexOf(val, 2, 15), -1, msg);
    +      assert.strictEqual(_.indexOf(val, 2), -1, msg);
    +      assert.strictEqual(_.indexOf(val, 2, -1), -1, msg);
    +      assert.strictEqual(_.indexOf(val, 2, -20), -1, msg);
    +      assert.strictEqual(_.indexOf(val, 2, 15), -1, msg);
         });
     
         var num = 35;
         numbers = [10, 20, 30, 40, 50];
         var index = _.indexOf(numbers, num, true);
    -    assert.equal(index, -1, '35 is not in the list');
    +    assert.strictEqual(index, -1, '35 is not in the list');
     
         numbers = [10, 20, 30, 40, 50]; num = 40;
         index = _.indexOf(numbers, num, true);
    -    assert.equal(index, 3, '40 is in the list');
    +    assert.strictEqual(index, 3, '40 is in the list');
     
         numbers = [1, 40, 40, 40, 40, 40, 40, 40, 50, 60, 70]; num = 40;
    -    assert.equal(_.indexOf(numbers, num, true), 1, '40 is in the list');
    -    assert.equal(_.indexOf(numbers, 6, true), -1, '6 isnt in the list');
    -    assert.equal(_.indexOf([1, 2, 5, 4, 6, 7], 5, true), -1, 'sorted indexOf doesn\'t uses binary search');
    +    assert.strictEqual(_.indexOf(numbers, num, true), 1, '40 is in the list');
    +    assert.strictEqual(_.indexOf(numbers, 6, true), -1, '6 isnt in the list');
    +    assert.strictEqual(_.indexOf([1, 2, 5, 4, 6, 7], 5, true), -1, 'sorted indexOf doesn\'t uses binary search');
         assert.ok(_.every(['1', [], {}, null], function() {
           return _.indexOf(numbers, num, {}) === 1;
         }), 'non-nums as fromIndex make indexOf assume sorted');
     
         numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3];
         index = _.indexOf(numbers, 2, 5);
    -    assert.equal(index, 7, 'supports the fromIndex argument');
    +    assert.strictEqual(index, 7, 'supports the fromIndex argument');
     
         index = _.indexOf([,,, 0], void 0);
    -    assert.equal(index, 0, 'treats sparse arrays as if they were dense');
    +    assert.strictEqual(index, 0, 'treats sparse arrays as if they were dense');
     
         var array = [1, 2, 3, 1, 2, 3];
         assert.strictEqual(_.indexOf(array, 1, -3), 3, 'neg `fromIndex` starts at the right index');
    @@ -346,7 +346,7 @@
         assert.strictEqual(_.indexOf([1, 2, 3], 1, true), 0);
     
         index = _.indexOf([], void 0, true);
    -    assert.equal(index, -1, 'empty array with truthy `isSorted` returns -1');
    +    assert.strictEqual(index, -1, 'empty array with truthy `isSorted` returns -1');
       });
     
       QUnit.test('indexOf with NaN', function(assert) {
    @@ -371,26 +371,26 @@
       QUnit.test('lastIndexOf', function(assert) {
         var numbers = [1, 0, 1];
         var falsey = [void 0, '', 0, false, NaN, null, void 0];
    -    assert.equal(_.lastIndexOf(numbers, 1), 2);
    +    assert.strictEqual(_.lastIndexOf(numbers, 1), 2);
     
         numbers = [1, 0, 1, 0, 0, 1, 0, 0, 0];
         numbers.lastIndexOf = null;
    -    assert.equal(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function');
    -    assert.equal(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element');
    +    assert.strictEqual(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function');
    +    assert.strictEqual(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element');
         var result = (function(){ return _.lastIndexOf(arguments, 1); }(1, 0, 1, 0, 0, 1, 0, 0, 0));
    -    assert.equal(result, 5, 'works on an arguments object');
    +    assert.strictEqual(result, 5, 'works on an arguments object');
     
         _.each([null, void 0, [], false], function(val) {
           var msg = 'Handles: ' + (_.isArray(val) ? '[]' : val);
    -      assert.equal(_.lastIndexOf(val, 2), -1, msg);
    -      assert.equal(_.lastIndexOf(val, 2, -1), -1, msg);
    -      assert.equal(_.lastIndexOf(val, 2, -20), -1, msg);
    -      assert.equal(_.lastIndexOf(val, 2, 15), -1, msg);
    +      assert.strictEqual(_.lastIndexOf(val, 2), -1, msg);
    +      assert.strictEqual(_.lastIndexOf(val, 2, -1), -1, msg);
    +      assert.strictEqual(_.lastIndexOf(val, 2, -20), -1, msg);
    +      assert.strictEqual(_.lastIndexOf(val, 2, 15), -1, msg);
         });
     
         numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3];
         var index = _.lastIndexOf(numbers, 2, 2);
    -    assert.equal(index, 1, 'supports the fromIndex argument');
    +    assert.strictEqual(index, 1, 'supports the fromIndex argument');
     
         var array = [1, 2, 3, 1, 2, 3];
     
    @@ -454,33 +454,33 @@
           {a: 0, b: 0}
         ];
     
    -    assert.equal(_.findIndex(objects, function(obj) {
    +    assert.strictEqual(_.findIndex(objects, function(obj) {
           return obj.a === 0;
         }), 0);
     
    -    assert.equal(_.findIndex(objects, function(obj) {
    +    assert.strictEqual(_.findIndex(objects, function(obj) {
           return obj.b * obj.a === 4;
         }), 2);
     
    -    assert.equal(_.findIndex(objects, 'a'), 1, 'Uses lookupIterator');
    +    assert.strictEqual(_.findIndex(objects, 'a'), 1, 'Uses lookupIterator');
     
    -    assert.equal(_.findIndex(objects, function(obj) {
    +    assert.strictEqual(_.findIndex(objects, function(obj) {
           return obj.b * obj.a === 5;
         }), -1);
     
    -    assert.equal(_.findIndex(null, _.noop), -1);
    +    assert.strictEqual(_.findIndex(null, _.noop), -1);
         assert.strictEqual(_.findIndex(objects, function(a) {
           return a.foo === null;
         }), -1);
         _.findIndex([{a: 1}], function(a, key, obj) {
    -      assert.equal(key, 0);
    +      assert.strictEqual(key, 0);
           assert.deepEqual(obj, [{a: 1}]);
           assert.strictEqual(this, objects, 'called with context');
         }, objects);
     
         var sparse = [];
         sparse[20] = {a: 2, b: 2};
    -    assert.equal(_.findIndex(sparse, function(obj) {
    +    assert.strictEqual(_.findIndex(sparse, function(obj) {
           return obj && obj.b * obj.a === 4;
         }), 20, 'Works with sparse arrays');
     
    @@ -497,33 +497,33 @@
           {a: 0, b: 0}
         ];
     
    -    assert.equal(_.findLastIndex(objects, function(obj) {
    +    assert.strictEqual(_.findLastIndex(objects, function(obj) {
           return obj.a === 0;
         }), 3);
     
    -    assert.equal(_.findLastIndex(objects, function(obj) {
    +    assert.strictEqual(_.findLastIndex(objects, function(obj) {
           return obj.b * obj.a === 4;
         }), 2);
     
    -    assert.equal(_.findLastIndex(objects, 'a'), 2, 'Uses lookupIterator');
    +    assert.strictEqual(_.findLastIndex(objects, 'a'), 2, 'Uses lookupIterator');
     
    -    assert.equal(_.findLastIndex(objects, function(obj) {
    +    assert.strictEqual(_.findLastIndex(objects, function(obj) {
           return obj.b * obj.a === 5;
         }), -1);
     
    -    assert.equal(_.findLastIndex(null, _.noop), -1);
    +    assert.strictEqual(_.findLastIndex(null, _.noop), -1);
         assert.strictEqual(_.findLastIndex(objects, function(a) {
           return a.foo === null;
         }), -1);
         _.findLastIndex([{a: 1}], function(a, key, obj) {
    -      assert.equal(key, 0);
    +      assert.strictEqual(key, 0);
           assert.deepEqual(obj, [{a: 1}]);
           assert.strictEqual(this, objects, 'called with context');
         }, objects);
     
         var sparse = [];
         sparse[20] = {a: 2, b: 2};
    -    assert.equal(_.findLastIndex(sparse, function(obj) {
    +    assert.strictEqual(_.findLastIndex(sparse, function(obj) {
           return obj && obj.b * obj.a === 4;
         }), 20, 'Works with sparse arrays');
     
    diff --git a/test/chaining.js b/test/chaining.js
    index 6ad21dcf6..5d7595cdc 100644
    --- a/test/chaining.js
    +++ b/test/chaining.js
    @@ -19,8 +19,8 @@
             return hash;
           }, {})
           .value();
    -    assert.equal(counts.a, 16, 'counted all the letters in the song');
    -    assert.equal(counts.e, 10, 'counted all the letters in the song');
    +    assert.strictEqual(counts.a, 16, 'counted all the letters in the song');
    +    assert.strictEqual(counts.e, 10, 'counted all the letters in the song');
       });
     
       QUnit.test('select/reject/sortBy', function(assert) {
    diff --git a/test/collections.js b/test/collections.js
    index a74394073..8bc92f112 100644
    --- a/test/collections.js
    +++ b/test/collections.js
    @@ -5,7 +5,7 @@
     
       QUnit.test('each', function(assert) {
         _.each([1, 2, 3], function(num, i) {
    -      assert.equal(num, i + 1, 'each iterators provide value and iteration count');
    +      assert.strictEqual(num, i + 1, 'each iterators provide value and iteration count');
         });
     
         var answers = [];
    @@ -28,7 +28,7 @@
         var count = 0;
         obj = {1: 'foo', 2: 'bar', 3: 'baz'};
         _.each(obj, function(){ count++; });
    -    assert.equal(count, 3, 'the fun should be called only 3 times');
    +    assert.strictEqual(count, 3, 'the fun should be called only 3 times');
     
         var answer = null;
         _.each([1, 2, 3], function(num, index, arr){ if (_.include(arr, num)) answer = true; });
    @@ -36,7 +36,7 @@
     
         answers = 0;
         _.each(null, function(){ ++answers; });
    -    assert.equal(answers, 0, 'handles a null properly');
    +    assert.strictEqual(answers, 0, 'handles a null properly');
     
         _.each(false, function(){});
     
    @@ -122,14 +122,14 @@
             ++answers;
             return method === 'every' ? true : null;
           }, {});
    -      assert.equal(answers, 100, method + ' enumerates [0, length)');
    +      assert.strictEqual(answers, 100, method + ' enumerates [0, length)');
     
           var growingCollection = [1, 2, 3], count = 0;
           _[method](growingCollection, function() {
             if (count < 10) growingCollection.push(count++);
             return method === 'every' ? true : null;
           }, {});
    -      assert.equal(count, 3, method + ' is resistant to length changes');
    +      assert.strictEqual(count, 3, method + ' is resistant to length changes');
         });
     
         _.each(collection.concat(object), function(method) {
    @@ -139,7 +139,7 @@
             return method === 'every' ? true : null;
           }, {});
     
    -      assert.equal(count, 2, method + ' is resistant to property changes');
    +      assert.strictEqual(count, 2, method + ' is resistant to property changes');
         });
       });
     
    @@ -175,25 +175,25 @@
     
       QUnit.test('reduce', function(assert) {
         var sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0);
    -    assert.equal(sum, 6, 'can sum up an array');
    +    assert.strictEqual(sum, 6, 'can sum up an array');
     
         var context = {multiplier: 3};
         sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num * this.multiplier; }, 0, context);
    -    assert.equal(sum, 18, 'can reduce with a context object');
    +    assert.strictEqual(sum, 18, 'can reduce with a context object');
     
         sum = _([1, 2, 3]).reduce(function(memo, num){ return memo + num; }, 0);
    -    assert.equal(sum, 6, 'OO-style reduce');
    +    assert.strictEqual(sum, 6, 'OO-style reduce');
     
         sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; });
    -    assert.equal(sum, 6, 'default initial value');
    +    assert.strictEqual(sum, 6, 'default initial value');
     
         var prod = _.reduce([1, 2, 3, 4], function(memo, num){ return memo * num; });
    -    assert.equal(prod, 24, 'can reduce via multiplication');
    +    assert.strictEqual(prod, 24, 'can reduce via multiplication');
     
         assert.strictEqual(_.reduce(null, _.noop, 138), 138, 'handles a null (with initial value) properly');
    -    assert.equal(_.reduce([], _.noop, void 0), void 0, 'undefined can be passed as a special case');
    -    assert.equal(_.reduce([_], _.noop), _, 'collection of length one with no initial value returns the first item');
    -    assert.equal(_.reduce([], _.noop), void 0, 'returns undefined when collection is empty and no initial value');
    +    assert.strictEqual(_.reduce([], _.noop, void 0), void 0, 'undefined can be passed as a special case');
    +    assert.strictEqual(_.reduce([_], _.noop), _, 'collection of length one with no initial value returns the first item');
    +    assert.strictEqual(_.reduce([], _.noop), void 0, 'returns undefined when collection is empty and no initial value');
       });
     
       QUnit.test('foldl', function(assert) {
    @@ -206,19 +206,19 @@
     
       QUnit.test('reduceRight', function(assert) {
         var list = _.reduceRight(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; }, '');
    -    assert.equal(list, 'bazbarfoo', 'can perform right folds');
    +    assert.strictEqual(list, 'bazbarfoo', 'can perform right folds');
     
         list = _.reduceRight(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; });
    -    assert.equal(list, 'bazbarfoo', 'default initial value');
    +    assert.strictEqual(list, 'bazbarfoo', 'default initial value');
     
         var sum = _.reduceRight({a: 1, b: 2, c: 3}, function(memo, num){ return memo + num; });
    -    assert.equal(sum, 6, 'default initial value on object');
    +    assert.strictEqual(sum, 6, 'default initial value on object');
     
         assert.strictEqual(_.reduceRight(null, _.noop, 138), 138, 'handles a null (with initial value) properly');
    -    assert.equal(_.reduceRight([_], _.noop), _, 'collection of length one with no initial value returns the first item');
    +    assert.strictEqual(_.reduceRight([_], _.noop), _, 'collection of length one with no initial value returns the first item');
     
    -    assert.equal(_.reduceRight([], _.noop, void 0), void 0, 'undefined can be passed as a special case');
    -    assert.equal(_.reduceRight([], _.noop), void 0, 'returns undefined when collection is empty and no initial value');
    +    assert.strictEqual(_.reduceRight([], _.noop, void 0), void 0, 'undefined can be passed as a special case');
    +    assert.strictEqual(_.reduceRight([], _.noop), void 0, 'returns undefined when collection is empty and no initial value');
     
         // Assert that the correct arguments are being passed.
     
    @@ -274,7 +274,7 @@
         assert.notOk(_.find([], {c: 1}), 'undefined when searching empty list');
     
         var result = _.find([1, 2, 3], function(num){ return num * 2 === 4; });
    -    assert.equal(result, 2, 'found the first "2" and broke the loop');
    +    assert.strictEqual(result, 2, 'found the first "2" and broke the loop');
     
         var obj = {
           a: {x: 1, z: 3},
    @@ -290,7 +290,7 @@
         }), {x: 4, z: 1});
     
         _.findIndex([{a: 1}], function(a, key, o) {
    -      assert.equal(key, 0);
    +      assert.strictEqual(key, 0);
           assert.deepEqual(o, [{a: 1}]);
           assert.strictEqual(this, _, 'called with context');
         }, _);
    @@ -310,7 +310,7 @@
         assert.deepEqual(_.filter([{}, evenObject, []], 'two'), [evenObject], 'predicate string map to object properties');
     
         _.filter([1], function() {
    -      assert.equal(this, evenObject, 'given context');
    +      assert.strictEqual(this, evenObject, 'given context');
         }, evenObject);
     
         // Can be used like _.where.
    @@ -332,7 +332,7 @@
         var context = 'obj';
     
         var evens = _.reject([1, 2, 3, 4, 5, 6], function(num){
    -      assert.equal(context, 'obj');
    +      assert.strictEqual(context, 'obj');
           return num % 2 !== 0;
         }, context);
         assert.deepEqual(evens, [2, 4, 6], 'rejected each odd number');
    @@ -489,12 +489,12 @@
         };
         var list = [[5, 1, 7], [3, 2, 1]];
         var s = 'foo';
    -    assert.equal(s.call(), 42, 'call function exists');
    +    assert.strictEqual(s.call(), 42, 'call function exists');
         var result = _.invoke(list, 'sort');
         assert.deepEqual(result[0], [1, 5, 7], 'first array sorted');
         assert.deepEqual(result[1], [1, 2, 3], 'second array sorted');
         delete String.prototype.call;
    -    assert.equal(s.call, void 0, 'call function removed');
    +    assert.strictEqual(s.call, void 0, 'call function removed');
       });
     
       QUnit.test('pluck', function(assert) {
    @@ -508,13 +508,13 @@
       QUnit.test('where', function(assert) {
         var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}];
         var result = _.where(list, {a: 1});
    -    assert.equal(result.length, 3);
    -    assert.equal(result[result.length - 1].b, 4);
    +    assert.strictEqual(result.length, 3);
    +    assert.strictEqual(result[result.length - 1].b, 4);
         result = _.where(list, {b: 2});
    -    assert.equal(result.length, 2);
    -    assert.equal(result[0].a, 1);
    +    assert.strictEqual(result.length, 2);
    +    assert.strictEqual(result[0].a, 1);
         result = _.where(list, {});
    -    assert.equal(result.length, list.length);
    +    assert.strictEqual(result.length, list.length);
     
         function test() {}
         test.map = _.map;
    @@ -536,7 +536,7 @@
     
         function test() {}
         test.map = _.map;
    -    assert.equal(_.findWhere([_, {a: 1, b: 2}, _], test), _, 'checks properties given function');
    +    assert.strictEqual(_.findWhere([_, {a: 1, b: 2}, _], test), _, 'checks properties given function');
     
         function TestClass() {
           this.y = 5;
    @@ -547,42 +547,42 @@
       });
     
       QUnit.test('max', function(assert) {
    -    assert.equal(-Infinity, _.max(null), 'can handle null/undefined');
    -    assert.equal(-Infinity, _.max(void 0), 'can handle null/undefined');
    -    assert.equal(-Infinity, _.max(null, _.identity), 'can handle null/undefined');
    +    assert.strictEqual(-Infinity, _.max(null), 'can handle null/undefined');
    +    assert.strictEqual(-Infinity, _.max(void 0), 'can handle null/undefined');
    +    assert.strictEqual(-Infinity, _.max(null, _.identity), 'can handle null/undefined');
     
    -    assert.equal(_.max([1, 2, 3]), 3, 'can perform a regular Math.max');
    +    assert.strictEqual(_.max([1, 2, 3]), 3, 'can perform a regular Math.max');
     
         var neg = _.max([1, 2, 3], function(num){ return -num; });
    -    assert.equal(neg, 1, 'can perform a computation-based max');
    +    assert.strictEqual(neg, 1, 'can perform a computation-based max');
     
    -    assert.equal(-Infinity, _.max({}), 'Maximum value of an empty object');
    -    assert.equal(-Infinity, _.max([]), 'Maximum value of an empty array');
    -    assert.equal(_.max({a: 'a'}), -Infinity, 'Maximum value of a non-numeric collection');
    +    assert.strictEqual(-Infinity, _.max({}), 'Maximum value of an empty object');
    +    assert.strictEqual(-Infinity, _.max([]), 'Maximum value of an empty array');
    +    assert.strictEqual(_.max({a: 'a'}), -Infinity, 'Maximum value of a non-numeric collection');
     
    -    assert.equal(_.max(_.range(1, 300000)), 299999, 'Maximum value of a too-big array');
    +    assert.strictEqual(_.max(_.range(1, 300000)), 299999, 'Maximum value of a too-big array');
     
    -    assert.equal(_.max([1, 2, 3, 'test']), 3, 'Finds correct max in array starting with num and containing a NaN');
    -    assert.equal(_.max(['test', 1, 2, 3]), 3, 'Finds correct max in array starting with NaN');
    +    assert.strictEqual(_.max([1, 2, 3, 'test']), 3, 'Finds correct max in array starting with num and containing a NaN');
    +    assert.strictEqual(_.max(['test', 1, 2, 3]), 3, 'Finds correct max in array starting with NaN');
     
    -    assert.equal(_.max([1, 2, 3, null]), 3, 'Finds correct max in array starting with num and containing a `null`');
    -    assert.equal(_.max([null, 1, 2, 3]), 3, 'Finds correct max in array starting with a `null`');
    +    assert.strictEqual(_.max([1, 2, 3, null]), 3, 'Finds correct max in array starting with num and containing a `null`');
    +    assert.strictEqual(_.max([null, 1, 2, 3]), 3, 'Finds correct max in array starting with a `null`');
     
    -    assert.equal(_.max([1, 2, 3, '']), 3, 'Finds correct max in array starting with num and containing an empty string');
    -    assert.equal(_.max(['', 1, 2, 3]), 3, 'Finds correct max in array starting with an empty string');
    +    assert.strictEqual(_.max([1, 2, 3, '']), 3, 'Finds correct max in array starting with num and containing an empty string');
    +    assert.strictEqual(_.max(['', 1, 2, 3]), 3, 'Finds correct max in array starting with an empty string');
     
    -    assert.equal(_.max([1, 2, 3, false]), 3, 'Finds correct max in array starting with num and containing a false');
    -    assert.equal(_.max([false, 1, 2, 3]), 3, 'Finds correct max in array starting with a false');
    +    assert.strictEqual(_.max([1, 2, 3, false]), 3, 'Finds correct max in array starting with num and containing a false');
    +    assert.strictEqual(_.max([false, 1, 2, 3]), 3, 'Finds correct max in array starting with a false');
     
    -    assert.equal(_.max([0, 1, 2, 3, 4]), 4, 'Finds correct max in array containing a zero');
    -    assert.equal(_.max([-3, -2, -1, 0]), 0, 'Finds correct max in array containing negative numbers');
    +    assert.strictEqual(_.max([0, 1, 2, 3, 4]), 4, 'Finds correct max in array containing a zero');
    +    assert.strictEqual(_.max([-3, -2, -1, 0]), 0, 'Finds correct max in array containing negative numbers');
     
         assert.deepEqual(_.map([[1, 2, 3], [4, 5, 6]], _.max), [3, 6], 'Finds correct max in array when mapping through multiple arrays');
     
         var a = {x: -Infinity};
         var b = {x: -Infinity};
         var iterator = function(o){ return o.x; };
    -    assert.equal(_.max([a, b], iterator), a, 'Respects iterator return value of -Infinity');
    +    assert.strictEqual(_.max([a, b], iterator), a, 'Respects iterator return value of -Infinity');
     
         assert.deepEqual(_.max([{a: 1}, {a: 0, b: 3}, {a: 4}, {a: 2}], 'a'), {a: 4}, 'String keys use property iterator');
     
    @@ -592,40 +592,40 @@
       });
     
       QUnit.test('min', function(assert) {
    -    assert.equal(_.min(null), Infinity, 'can handle null/undefined');
    -    assert.equal(_.min(void 0), Infinity, 'can handle null/undefined');
    -    assert.equal(_.min(null, _.identity), Infinity, 'can handle null/undefined');
    +    assert.strictEqual(_.min(null), Infinity, 'can handle null/undefined');
    +    assert.strictEqual(_.min(void 0), Infinity, 'can handle null/undefined');
    +    assert.strictEqual(_.min(null, _.identity), Infinity, 'can handle null/undefined');
     
    -    assert.equal(_.min([1, 2, 3]), 1, 'can perform a regular Math.min');
    +    assert.strictEqual(_.min([1, 2, 3]), 1, 'can perform a regular Math.min');
     
         var neg = _.min([1, 2, 3], function(num){ return -num; });
    -    assert.equal(neg, 3, 'can perform a computation-based min');
    +    assert.strictEqual(neg, 3, 'can perform a computation-based min');
     
    -    assert.equal(_.min({}), Infinity, 'Minimum value of an empty object');
    -    assert.equal(_.min([]), Infinity, 'Minimum value of an empty array');
    -    assert.equal(_.min({a: 'a'}), Infinity, 'Minimum value of a non-numeric collection');
    +    assert.strictEqual(_.min({}), Infinity, 'Minimum value of an empty object');
    +    assert.strictEqual(_.min([]), Infinity, 'Minimum value of an empty array');
    +    assert.strictEqual(_.min({a: 'a'}), Infinity, 'Minimum value of a non-numeric collection');
     
         assert.deepEqual(_.map([[1, 2, 3], [4, 5, 6]], _.min), [1, 4], 'Finds correct min in array when mapping through multiple arrays');
     
         var now = new Date(9999999999);
         var then = new Date(0);
    -    assert.equal(_.min([now, then]), then);
    +    assert.strictEqual(_.min([now, then]), then);
     
    -    assert.equal(_.min(_.range(1, 300000)), 1, 'Minimum value of a too-big array');
    +    assert.strictEqual(_.min(_.range(1, 300000)), 1, 'Minimum value of a too-big array');
     
    -    assert.equal(_.min([1, 2, 3, 'test']), 1, 'Finds correct min in array starting with num and containing a NaN');
    -    assert.equal(_.min(['test', 1, 2, 3]), 1, 'Finds correct min in array starting with NaN');
    +    assert.strictEqual(_.min([1, 2, 3, 'test']), 1, 'Finds correct min in array starting with num and containing a NaN');
    +    assert.strictEqual(_.min(['test', 1, 2, 3]), 1, 'Finds correct min in array starting with NaN');
     
    -    assert.equal(_.min([1, 2, 3, null]), 1, 'Finds correct min in array starting with num and containing a `null`');
    -    assert.equal(_.min([null, 1, 2, 3]), 1, 'Finds correct min in array starting with a `null`');
    +    assert.strictEqual(_.min([1, 2, 3, null]), 1, 'Finds correct min in array starting with num and containing a `null`');
    +    assert.strictEqual(_.min([null, 1, 2, 3]), 1, 'Finds correct min in array starting with a `null`');
     
    -    assert.equal(_.min([0, 1, 2, 3, 4]), 0, 'Finds correct min in array containing a zero');
    -    assert.equal(_.min([-3, -2, -1, 0]), -3, 'Finds correct min in array containing negative numbers');
    +    assert.strictEqual(_.min([0, 1, 2, 3, 4]), 0, 'Finds correct min in array containing a zero');
    +    assert.strictEqual(_.min([-3, -2, -1, 0]), -3, 'Finds correct min in array containing negative numbers');
     
         var a = {x: Infinity};
         var b = {x: Infinity};
         var iterator = function(o){ return o.x; };
    -    assert.equal(_.min([a, b], iterator), a, 'Respects iterator return value of Infinity');
    +    assert.strictEqual(_.min([a, b], iterator), a, 'Respects iterator return value of Infinity');
     
         assert.deepEqual(_.min([{a: 1}, {a: 0, b: 3}, {a: 4}, {a: 2}], 'a'), {a: 0, b: 3}, 'String keys use property iterator');
     
    @@ -699,16 +699,16 @@
         grouped = _.groupBy([4.2, 6.1, 6.4], function(num) {
           return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor';
         });
    -    assert.equal(grouped.constructor.length, 1);
    -    assert.equal(grouped.hasOwnProperty.length, 2);
    +    assert.strictEqual(grouped.constructor.length, 1);
    +    assert.strictEqual(grouped.hasOwnProperty.length, 2);
     
         var array = [{}];
         _.groupBy(array, function(value, index, obj){ assert.strictEqual(obj, array); });
     
         array = [1, 2, 1, 2, 3];
         grouped = _.groupBy(array);
    -    assert.equal(grouped['1'].length, 2);
    -    assert.equal(grouped['3'].length, 1);
    +    assert.strictEqual(grouped['1'].length, 2);
    +    assert.strictEqual(grouped['3'].length, 1);
     
         var matrix = [
           [1, 2],
    @@ -721,32 +721,32 @@
     
       QUnit.test('indexBy', function(assert) {
         var parity = _.indexBy([1, 2, 3, 4, 5], function(num){ return num % 2 === 0; });
    -    assert.equal(parity['true'], 4);
    -    assert.equal(parity['false'], 5);
    +    assert.strictEqual(parity['true'], 4);
    +    assert.strictEqual(parity['false'], 5);
     
         var list = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'];
         var grouped = _.indexBy(list, 'length');
    -    assert.equal(grouped['3'], 'ten');
    -    assert.equal(grouped['4'], 'nine');
    -    assert.equal(grouped['5'], 'eight');
    +    assert.strictEqual(grouped['3'], 'ten');
    +    assert.strictEqual(grouped['4'], 'nine');
    +    assert.strictEqual(grouped['5'], 'eight');
     
         var array = [1, 2, 1, 2, 3];
         grouped = _.indexBy(array);
    -    assert.equal(grouped['1'], 1);
    -    assert.equal(grouped['2'], 2);
    -    assert.equal(grouped['3'], 3);
    +    assert.strictEqual(grouped['1'], 1);
    +    assert.strictEqual(grouped['2'], 2);
    +    assert.strictEqual(grouped['3'], 3);
       });
     
       QUnit.test('countBy', function(assert) {
         var parity = _.countBy([1, 2, 3, 4, 5], function(num){ return num % 2 === 0; });
    -    assert.equal(parity['true'], 2);
    -    assert.equal(parity['false'], 3);
    +    assert.strictEqual(parity['true'], 2);
    +    assert.strictEqual(parity['false'], 3);
     
         var list = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'];
         var grouped = _.countBy(list, 'length');
    -    assert.equal(grouped['3'], 4);
    -    assert.equal(grouped['4'], 3);
    -    assert.equal(grouped['5'], 3);
    +    assert.strictEqual(grouped['3'], 4);
    +    assert.strictEqual(grouped['4'], 3);
    +    assert.strictEqual(grouped['5'], 3);
     
         var context = {};
         _.countBy([{}], function(){ assert.strictEqual(this, context); }, context);
    @@ -754,16 +754,16 @@
         grouped = _.countBy([4.2, 6.1, 6.4], function(num) {
           return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor';
         });
    -    assert.equal(grouped.constructor, 1);
    -    assert.equal(grouped.hasOwnProperty, 2);
    +    assert.strictEqual(grouped.constructor, 1);
    +    assert.strictEqual(grouped.hasOwnProperty, 2);
     
         var array = [{}];
         _.countBy(array, function(value, index, obj){ assert.strictEqual(obj, array); });
     
         array = [1, 2, 1, 2, 3];
         grouped = _.countBy(array);
    -    assert.equal(grouped['1'], 2);
    -    assert.equal(grouped['3'], 1);
    +    assert.strictEqual(grouped['1'], 2);
    +    assert.strictEqual(grouped['3'], 1);
       });
     
       QUnit.test('shuffle', function(assert) {
    @@ -775,7 +775,7 @@
         assert.deepEqual(numbers, _.sortBy(shuffled), 'contains the same members before and after shuffle');
     
         shuffled = _.shuffle({a: 1, b: 2, c: 3, d: 4});
    -    assert.equal(shuffled.length, 4);
    +    assert.strictEqual(shuffled.length, 4);
         assert.deepEqual(shuffled.sort(), [1, 2, 3, 4], 'works on objects');
       });
     
    @@ -825,21 +825,21 @@
       });
     
       QUnit.test('size', function(assert) {
    -    assert.equal(_.size({one: 1, two: 2, three: 3}), 3, 'can compute the size of an object');
    -    assert.equal(_.size([1, 2, 3]), 3, 'can compute the size of an array');
    -    assert.equal(_.size({length: 3, 0: 0, 1: 0, 2: 0}), 3, 'can compute the size of Array-likes');
    +    assert.strictEqual(_.size({one: 1, two: 2, three: 3}), 3, 'can compute the size of an object');
    +    assert.strictEqual(_.size([1, 2, 3]), 3, 'can compute the size of an array');
    +    assert.strictEqual(_.size({length: 3, 0: 0, 1: 0, 2: 0}), 3, 'can compute the size of Array-likes');
     
         var func = function() {
           return _.size(arguments);
         };
     
    -    assert.equal(func(1, 2, 3, 4), 4, 'can test the size of the arguments object');
    +    assert.strictEqual(func(1, 2, 3, 4), 4, 'can test the size of the arguments object');
     
    -    assert.equal(_.size('hello'), 5, 'can compute the size of a string literal');
    -    assert.equal(_.size(new String('hello')), 5, 'can compute the size of string object');
    +    assert.strictEqual(_.size('hello'), 5, 'can compute the size of a string literal');
    +    assert.strictEqual(_.size(new String('hello')), 5, 'can compute the size of string object');
     
    -    assert.equal(_.size(null), 0, 'handles nulls');
    -    assert.equal(_.size(0), 0, 'handles numbers');
    +    assert.strictEqual(_.size(null), 0, 'handles nulls');
    +    assert.strictEqual(_.size(0), 0, 'handles numbers');
       });
     
       QUnit.test('partition', function(assert) {
    @@ -866,10 +866,10 @@
     
         var object = {a: 1};
         _.partition(object, function(val, key, obj) {
    -      assert.equal(val, 1);
    -      assert.equal(key, 'a');
    -      assert.equal(obj, object);
    -      assert.equal(this, predicate);
    +      assert.strictEqual(val, 1);
    +      assert.strictEqual(key, 'a');
    +      assert.strictEqual(obj, object);
    +      assert.strictEqual(this, predicate);
         }, predicate);
       });
     
    @@ -879,7 +879,7 @@
           parent.innerHTML = 'textnode';
     
           var elementChildren = _.filter(parent.childNodes, _.isElement);
    -      assert.equal(elementChildren.length, 2);
    +      assert.strictEqual(elementChildren.length, 2);
     
           assert.deepEqual(_.map(elementChildren, 'id'), ['id1', 'id2']);
           assert.deepEqual(_.map(parent.childNodes, 'nodeType'), [1, 3, 1]);
    @@ -890,8 +890,8 @@
           function compareNode(node) {
             return _.isElement(node) ? node.id.charAt(2) : void 0;
           }
    -      assert.equal(_.max(parent.childNodes, compareNode), _.last(parent.childNodes));
    -      assert.equal(_.min(parent.childNodes, compareNode), _.first(parent.childNodes));
    +      assert.strictEqual(_.max(parent.childNodes, compareNode), _.last(parent.childNodes));
    +      assert.strictEqual(_.min(parent.childNodes, compareNode), _.first(parent.childNodes));
         });
       }
     
    diff --git a/test/cross-document.js b/test/cross-document.js
    index bc3ab77f1..09ef2a1cb 100644
    --- a/test/cross-document.js
    +++ b/test/cross-document.js
    @@ -132,9 +132,9 @@
             _.isFunction(fn);
           }
     
    -      assert.equal(_.isFunction(xml), false);
    -      assert.equal(_.isFunction(div), false);
    -      assert.equal(_.isFunction(fn), true);
    +      assert.strictEqual(_.isFunction(xml), false);
    +      assert.strictEqual(_.isFunction(div), false);
    +      assert.strictEqual(_.isFunction(fn), true);
         });
       }
     
    diff --git a/test/functions.js b/test/functions.js
    index 0f595316d..d271c04eb 100644
    --- a/test/functions.js
    +++ b/test/functions.js
    @@ -8,10 +8,10 @@
         var context = {name: 'moe'};
         var func = function(arg) { return 'name: ' + (this.name || arg); };
         var bound = _.bind(func, context);
    -    assert.equal(bound(), 'name: moe', 'can bind a function to a context');
    +    assert.strictEqual(bound(), 'name: moe', 'can bind a function to a context');
     
         bound = _(func).bind(context);
    -    assert.equal(bound(), 'name: moe', 'can do OO-style binding');
    +    assert.strictEqual(bound(), 'name: moe', 'can do OO-style binding');
     
         bound = _.bind(func, null, 'curly');
         var result = bound();
    @@ -20,14 +20,14 @@
     
         func = function(salutation, name) { return salutation + ': ' + name; };
         func = _.bind(func, this, 'hello');
    -    assert.equal(func('moe'), 'hello: moe', 'the function was partially applied in advance');
    +    assert.strictEqual(func('moe'), 'hello: moe', 'the function was partially applied in advance');
     
         func = _.bind(func, this, 'curly');
    -    assert.equal(func(), 'hello: curly', 'the function was completely applied in advance');
    +    assert.strictEqual(func(), 'hello: curly', 'the function was completely applied in advance');
     
         func = function(salutation, firstname, lastname) { return salutation + ': ' + firstname + ' ' + lastname; };
         func = _.bind(func, this, 'hello', 'moe', 'curly');
    -    assert.equal(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments');
    +    assert.strictEqual(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments');
     
         func = function() { return this; };
         assert.strictEqual(typeof _.bind(func, 0)(), 'object', 'binding a primitive to `this` returns a wrapped primitive');
    @@ -42,8 +42,8 @@
         var boundf = _.bind(F, {hello: 'moe curly'});
         var Boundf = boundf; // make eslint happy.
         var newBoundf = new Boundf();
    -    assert.equal(newBoundf.hello, void 0, 'function should not be bound to the context, to comply with ECMAScript 5');
    -    assert.equal(boundf().hello, 'moe curly', "When called without the new operator, it's OK to be bound to the context");
    +    assert.strictEqual(newBoundf.hello, void 0, 'function should not be bound to the context, to comply with ECMAScript 5');
    +    assert.strictEqual(boundf().hello, 'moe curly', "When called without the new operator, it's OK to be bound to the context");
         assert.ok(newBoundf instanceof F, 'a bound instance is an instance of the original function');
     
         assert.raises(function() { _.bind('notafunction'); }, TypeError, 'throws an error when binding to a non-function');
    @@ -54,17 +54,17 @@
         var func = function() { return this.name + ' ' + _.toArray(arguments).join(' '); };
     
         obj.func = _.partial(func, 'a', 'b');
    -    assert.equal(obj.func('c', 'd'), 'moe a b c d', 'can partially apply');
    +    assert.strictEqual(obj.func('c', 'd'), 'moe a b c d', 'can partially apply');
     
         obj.func = _.partial(func, _, 'b', _, 'd');
    -    assert.equal(obj.func('a', 'c'), 'moe a b c d', 'can partially apply with placeholders');
    +    assert.strictEqual(obj.func('a', 'c'), 'moe a b c d', 'can partially apply with placeholders');
     
         func = _.partial(function() { return arguments.length; }, _, 'b', _, 'd');
    -    assert.equal(func('a', 'c', 'e'), 5, 'accepts more arguments than the number of placeholders');
    -    assert.equal(func('a'), 4, 'accepts fewer arguments than the number of placeholders');
    +    assert.strictEqual(func('a', 'c', 'e'), 5, 'accepts more arguments than the number of placeholders');
    +    assert.strictEqual(func('a'), 4, 'accepts fewer arguments than the number of placeholders');
     
         func = _.partial(function() { return typeof arguments[2]; }, _, 'b', _, 'd');
    -    assert.equal(func('a'), 'undefined', 'unfilled placeholders are undefined');
    +    assert.strictEqual(func('a'), 'undefined', 'unfilled placeholders are undefined');
     
         // passes context
         function MyWidget(name, options) {
    @@ -77,16 +77,16 @@
         var MyWidgetWithCoolOpts = _.partial(MyWidget, _, {a: 1});
         var widget = new MyWidgetWithCoolOpts('foo');
         assert.ok(widget instanceof MyWidget, 'Can partially bind a constructor');
    -    assert.equal(widget.get(), 'foo', 'keeps prototype');
    +    assert.strictEqual(widget.get(), 'foo', 'keeps prototype');
         assert.deepEqual(widget.options, {a: 1});
     
         _.partial.placeholder = obj;
         func = _.partial(function() { return arguments.length; }, obj, 'b', obj, 'd');
    -    assert.equal(func('a'), 4, 'allows the placeholder to be swapped out');
    +    assert.strictEqual(func('a'), 4, 'allows the placeholder to be swapped out');
     
         _.partial.placeholder = {};
         func = _.partial(function() { return arguments.length; }, obj, 'b', obj, 'd');
    -    assert.equal(func('a'), 5, 'swapping the placeholder preserves previously bound arguments');
    +    assert.strictEqual(func('a'), 5, 'swapping the placeholder preserves previously bound arguments');
     
         _.partial.placeholder = _;
       });
    @@ -101,8 +101,8 @@
         curly.getName = moe.getName;
         _.bindAll(moe, 'getName', 'sayHi');
         curly.sayHi = moe.sayHi;
    -    assert.equal(curly.getName(), 'name: curly', 'unbound function is bound to current object');
    -    assert.equal(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object');
    +    assert.strictEqual(curly.getName(), 'name: curly', 'unbound function is bound to current object');
    +    assert.strictEqual(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object');
     
         curly = {name: 'curly'};
         moe = {
    @@ -118,41 +118,41 @@
     
         _.bindAll(moe, 'sayHi', 'sayLast');
         curly.sayHi = moe.sayHi;
    -    assert.equal(curly.sayHi(), 'hi: moe');
    +    assert.strictEqual(curly.sayHi(), 'hi: moe');
     
         var sayLast = moe.sayLast;
    -    assert.equal(sayLast(1, 2, 3, 4, 5, 6, 7, 'Tom'), 'hi: moe', 'createCallback works with any number of arguments');
    +    assert.strictEqual(sayLast(1, 2, 3, 4, 5, 6, 7, 'Tom'), 'hi: moe', 'createCallback works with any number of arguments');
     
         _.bindAll(moe, ['getName']);
         var getName = moe.getName;
    -    assert.equal(getName(), 'name: moe', 'flattens arguments into a single list');
    +    assert.strictEqual(getName(), 'name: moe', 'flattens arguments into a single list');
       });
     
       QUnit.test('memoize', function(assert) {
         var fib = function(n) {
           return n < 2 ? n : fib(n - 1) + fib(n - 2);
         };
    -    assert.equal(fib(10), 55, 'a memoized version of fibonacci produces identical results');
    +    assert.strictEqual(fib(10), 55, 'a memoized version of fibonacci produces identical results');
         fib = _.memoize(fib); // Redefine `fib` for memoization
    -    assert.equal(fib(10), 55, 'a memoized version of fibonacci produces identical results');
    +    assert.strictEqual(fib(10), 55, 'a memoized version of fibonacci produces identical results');
     
         var o = function(str) {
           return str;
         };
         var fastO = _.memoize(o);
    -    assert.equal(o('toString'), 'toString', 'checks hasOwnProperty');
    -    assert.equal(fastO('toString'), 'toString', 'checks hasOwnProperty');
    +    assert.strictEqual(o('toString'), 'toString', 'checks hasOwnProperty');
    +    assert.strictEqual(fastO('toString'), 'toString', 'checks hasOwnProperty');
     
         // Expose the cache.
         var upper = _.memoize(function(s) {
           return s.toUpperCase();
         });
    -    assert.equal(upper('foo'), 'FOO');
    -    assert.equal(upper('bar'), 'BAR');
    +    assert.strictEqual(upper('foo'), 'FOO');
    +    assert.strictEqual(upper('bar'), 'BAR');
         assert.deepEqual(upper.cache, {foo: 'FOO', bar: 'BAR'});
         upper.cache = {foo: 'BAR', bar: 'FOO'};
    -    assert.equal(upper('foo'), 'BAR');
    -    assert.equal(upper('bar'), 'FOO');
    +    assert.strictEqual(upper('foo'), 'BAR');
    +    assert.strictEqual(upper('bar'), 'FOO');
     
         var hashed = _.memoize(function(key) {
           //https://github.com/jashkenas/underscore/pull/1679#discussion_r13736209
    @@ -202,8 +202,8 @@
         var throttledIncr = _.throttle(incr, 32);
         throttledIncr(); throttledIncr();
     
    -    assert.equal(counter, 1, 'incr was called immediately');
    -    _.delay(function(){ assert.equal(counter, 2, 'incr was throttled'); done(); }, 64);
    +    assert.strictEqual(counter, 1, 'incr was called immediately');
    +    _.delay(function(){ assert.strictEqual(counter, 2, 'incr was throttled'); done(); }, 64);
       });
     
       QUnit.test('throttle arguments', function(assert) {
    @@ -214,8 +214,8 @@
         var throttledUpdate = _.throttle(update, 32);
         throttledUpdate(1); throttledUpdate(2);
         _.delay(function(){ throttledUpdate(3); }, 64);
    -    assert.equal(value, 1, 'updated to latest value');
    -    _.delay(function(){ assert.equal(value, 3, 'updated to latest value'); done(); }, 96);
    +    assert.strictEqual(value, 1, 'updated to latest value');
    +    _.delay(function(){ assert.strictEqual(value, 3, 'updated to latest value'); done(); }, 96);
       });
     
       QUnit.test('throttle once', function(assert) {
    @@ -226,8 +226,8 @@
         var throttledIncr = _.throttle(incr, 32);
         var result = throttledIncr();
         _.delay(function(){
    -      assert.equal(result, 1, 'throttled functions return their value');
    -      assert.equal(counter, 1, 'incr was called once'); done();
    +      assert.strictEqual(result, 1, 'throttled functions return their value');
    +      assert.strictEqual(counter, 1, 'incr was called once'); done();
         }, 64);
       });
     
    @@ -238,7 +238,7 @@
         var incr = function(){ counter++; };
         var throttledIncr = _.throttle(incr, 32);
         throttledIncr(); throttledIncr();
    -    _.delay(function(){ assert.equal(counter, 2, 'incr was called twice'); done(); }, 64);
    +    _.delay(function(){ assert.strictEqual(counter, 2, 'incr was called twice'); done(); }, 64);
       });
     
       QUnit.test('more throttling', function(assert) {
    @@ -248,11 +248,11 @@
         var incr = function(){ counter++; };
         var throttledIncr = _.throttle(incr, 30);
         throttledIncr(); throttledIncr();
    -    assert.equal(counter, 1);
    +    assert.strictEqual(counter, 1);
         _.delay(function(){
    -      assert.equal(counter, 2);
    +      assert.strictEqual(counter, 2);
           throttledIncr();
    -      assert.equal(counter, 3);
    +      assert.strictEqual(counter, 3);
           done();
         }, 85);
       });
    @@ -271,12 +271,12 @@
         _.delay(saveResult, 160);
         _.delay(saveResult, 230);
         _.delay(function() {
    -      assert.equal(results[0], 1, 'incr was called once');
    -      assert.equal(results[1], 1, 'incr was throttled');
    -      assert.equal(results[2], 1, 'incr was throttled');
    -      assert.equal(results[3], 2, 'incr was called twice');
    -      assert.equal(results[4], 2, 'incr was throttled');
    -      assert.equal(results[5], 3, 'incr was called trailing');
    +      assert.strictEqual(results[0], 1, 'incr was called once');
    +      assert.strictEqual(results[1], 1, 'incr was throttled');
    +      assert.strictEqual(results[2], 1, 'incr was throttled');
    +      assert.strictEqual(results[3], 2, 'incr was called twice');
    +      assert.strictEqual(results[4], 2, 'incr was throttled');
    +      assert.strictEqual(results[5], 3, 'incr was called trailing');
           done();
         }, 300);
       });
    @@ -310,10 +310,10 @@
         var throttledIncr = _.throttle(incr, 60, {leading: false});
     
         throttledIncr(); throttledIncr();
    -    assert.equal(counter, 0);
    +    assert.strictEqual(counter, 0);
     
         _.delay(function() {
    -      assert.equal(counter, 1);
    +      assert.strictEqual(counter, 1);
           done();
         }, 96);
       });
    @@ -329,14 +329,14 @@
         _.delay(throttledIncr, 50);
         _.delay(throttledIncr, 60);
         _.delay(throttledIncr, 200);
    -    assert.equal(counter, 0);
    +    assert.strictEqual(counter, 0);
     
         _.delay(function() {
    -      assert.equal(counter, 1);
    +      assert.strictEqual(counter, 1);
         }, 250);
     
         _.delay(function() {
    -      assert.equal(counter, 2);
    +      assert.strictEqual(counter, 2);
           done();
         }, 350);
       });
    @@ -366,16 +366,16 @@
         var throttledIncr = _.throttle(incr, 60, {trailing: false});
     
         throttledIncr(); throttledIncr(); throttledIncr();
    -    assert.equal(counter, 1);
    +    assert.strictEqual(counter, 1);
     
         _.delay(function() {
    -      assert.equal(counter, 1);
    +      assert.strictEqual(counter, 1);
     
           throttledIncr(); throttledIncr();
    -      assert.equal(counter, 2);
    +      assert.strictEqual(counter, 2);
     
           _.delay(function() {
    -        assert.equal(counter, 2);
    +        assert.strictEqual(counter, 2);
             done();
           }, 96);
         }, 96);
    @@ -390,14 +390,14 @@
         var origNowFunc = _.now;
     
         throttledIncr();
    -    assert.equal(counter, 1);
    +    assert.strictEqual(counter, 1);
         _.now = function() {
           return new Date(2013, 0, 1, 1, 1, 1);
         };
     
         _.delay(function() {
           throttledIncr();
    -      assert.equal(counter, 2);
    +      assert.strictEqual(counter, 2);
           done();
           _.now = origNowFunc;
         }, 200);
    @@ -421,9 +421,9 @@
         };
         throttledAppend = _.throttle(append, 32);
         throttledAppend.call('a1', 'a2');
    -    assert.equal(value, 'a1a2');
    +    assert.strictEqual(value, 'a1a2');
         _.delay(function(){
    -      assert.equal(value, 'a1a2c1c2b1b2', 'append was throttled successfully');
    +      assert.strictEqual(value, 'a1a2c1c2b1b2', 'append was throttled successfully');
           done();
         }, 100);
       });
    @@ -438,8 +438,8 @@
         throttledIncr();
         throttledIncr();
     
    -    assert.equal(counter, 2, 'incr was called immediately');
    -    _.delay(function(){ assert.equal(counter, 3, 'incr was throttled'); done(); }, 64);
    +    assert.strictEqual(counter, 2, 'incr was called immediately');
    +    _.delay(function(){ assert.strictEqual(counter, 3, 'incr was throttled'); done(); }, 64);
       });
     
       QUnit.test('throttle cancel with leading: false', function(assert) {
    @@ -450,8 +450,8 @@
         throttledIncr();
         throttledIncr.cancel();
     
    -    assert.equal(counter, 0, 'incr was throttled');
    -    _.delay(function(){ assert.equal(counter, 0, 'incr was throttled'); done(); }, 64);
    +    assert.strictEqual(counter, 0, 'incr was throttled');
    +    _.delay(function(){ assert.strictEqual(counter, 0, 'incr was throttled'); done(); }, 64);
       });
     
       QUnit.test('debounce', function(assert) {
    @@ -462,7 +462,7 @@
         var debouncedIncr = _.debounce(incr, 32);
         debouncedIncr(); debouncedIncr();
         _.delay(debouncedIncr, 16);
    -    _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); done(); }, 96);
    +    _.delay(function(){ assert.strictEqual(counter, 1, 'incr was debounced'); done(); }, 96);
       });
     
       QUnit.test('debounce cancel', function(assert) {
    @@ -473,7 +473,7 @@
         var debouncedIncr = _.debounce(incr, 32);
         debouncedIncr();
         debouncedIncr.cancel();
    -    _.delay(function(){ assert.equal(counter, 0, 'incr was not called'); done(); }, 96);
    +    _.delay(function(){ assert.strictEqual(counter, 0, 'incr was not called'); done(); }, 96);
       });
     
       QUnit.test('debounce asap', function(assert) {
    @@ -485,17 +485,17 @@
         var debouncedIncr = _.debounce(incr, 64, true);
         a = debouncedIncr();
         b = debouncedIncr();
    -    assert.equal(a, 1);
    -    assert.equal(b, 1);
    -    assert.equal(counter, 1, 'incr was called immediately');
    +    assert.strictEqual(a, 1);
    +    assert.strictEqual(b, 1);
    +    assert.strictEqual(counter, 1, 'incr was called immediately');
         _.delay(debouncedIncr, 16);
         _.delay(debouncedIncr, 32);
         _.delay(debouncedIncr, 48);
         _.delay(function(){
    -      assert.equal(counter, 1, 'incr was debounced');
    +      assert.strictEqual(counter, 1, 'incr was debounced');
           c = debouncedIncr();
    -      assert.equal(c, 2);
    -      assert.equal(counter, 2, 'incr was called again');
    +      assert.strictEqual(c, 2);
    +      assert.strictEqual(counter, 2, 'incr was called again');
           done();
         }, 128);
       });
    @@ -510,13 +510,13 @@
         a = debouncedIncr();
         debouncedIncr.cancel();
         b = debouncedIncr();
    -    assert.equal(a, 1);
    -    assert.equal(b, 2);
    -    assert.equal(counter, 2, 'incr was called immediately');
    +    assert.strictEqual(a, 1);
    +    assert.strictEqual(b, 2);
    +    assert.strictEqual(counter, 2, 'incr was called immediately');
         _.delay(debouncedIncr, 16);
         _.delay(debouncedIncr, 32);
         _.delay(debouncedIncr, 48);
    -    _.delay(function(){ assert.equal(counter, 2, 'incr was debounced'); done(); }, 128);
    +    _.delay(function(){ assert.strictEqual(counter, 2, 'incr was debounced'); done(); }, 128);
       });
     
       QUnit.test('debounce asap recursively', function(assert) {
    @@ -528,8 +528,8 @@
           if (counter < 10) debouncedIncr();
         }, 32, true);
         debouncedIncr();
    -    assert.equal(counter, 1, 'incr was called immediately');
    -    _.delay(function(){ assert.equal(counter, 1, 'incr was debounced'); done(); }, 96);
    +    assert.strictEqual(counter, 1, 'incr was called immediately');
    +    _.delay(function(){ assert.strictEqual(counter, 1, 'incr was debounced'); done(); }, 96);
       });
     
       QUnit.test('debounce after system time is set backwards', function(assert) {
    @@ -542,7 +542,7 @@
         }, 100, true);
     
         debouncedIncr();
    -    assert.equal(counter, 1, 'incr was called immediately');
    +    assert.strictEqual(counter, 1, 'incr was called immediately');
     
         _.now = function() {
           return new Date(2013, 0, 1, 1, 1, 1);
    @@ -550,7 +550,7 @@
     
         _.delay(function() {
           debouncedIncr();
    -      assert.equal(counter, 2, 'incr was debounced successfully');
    +      assert.strictEqual(counter, 2, 'incr was debounced successfully');
           done();
           _.now = origNowFunc;
         }, 200);
    @@ -573,9 +573,9 @@
         };
         debouncedAppend = _.debounce(append, 32);
         debouncedAppend.call('a1', 'a2');
    -    assert.equal(value, '');
    +    assert.strictEqual(value, '');
         _.delay(function(){
    -      assert.equal(value, 'a1a2b1b2', 'append was debounced successfully');
    +      assert.strictEqual(value, 'a1a2b1b2', 'append was debounced successfully');
           done();
         }, 100);
       });
    @@ -585,9 +585,9 @@
         var increment = _.once(function(){ return ++num; });
         increment();
         increment();
    -    assert.equal(num, 1);
    +    assert.strictEqual(num, 1);
     
    -    assert.equal(increment(), 1, 'stores a memo to the last value');
    +    assert.strictEqual(increment(), 1, 'stores a memo to the last value');
       });
     
       QUnit.test('Recursive onced function.', function(assert) {
    @@ -602,12 +602,12 @@
       QUnit.test('wrap', function(assert) {
         var greet = function(name){ return 'hi: ' + name; };
         var backwards = _.wrap(greet, function(func, name){ return func(name) + ' ' + name.split('').reverse().join(''); });
    -    assert.equal(backwards('moe'), 'hi: moe eom', 'wrapped the salutation function');
    +    assert.strictEqual(backwards('moe'), 'hi: moe eom', 'wrapped the salutation function');
     
         var inner = function(){ return 'Hello '; };
         var obj = {name: 'Moe'};
         obj.hi = _.wrap(inner, function(fn){ return fn() + this.name; });
    -    assert.equal(obj.hi(), 'Hello Moe');
    +    assert.strictEqual(obj.hi(), 'Hello Moe');
     
         var noop = function(){};
         var wrapped = _.wrap(noop, function(){ return Array.prototype.slice.call(arguments, 0); });
    @@ -617,34 +617,34 @@
     
       QUnit.test('negate', function(assert) {
         var isOdd = function(n){ return n & 1; };
    -    assert.equal(_.negate(isOdd)(2), true, 'should return the complement of the given function');
    -    assert.equal(_.negate(isOdd)(3), false, 'should return the complement of the given function');
    +    assert.strictEqual(_.negate(isOdd)(2), true, 'should return the complement of the given function');
    +    assert.strictEqual(_.negate(isOdd)(3), false, 'should return the complement of the given function');
       });
     
       QUnit.test('compose', function(assert) {
         var greet = function(name){ return 'hi: ' + name; };
         var exclaim = function(sentence){ return sentence + '!'; };
         var composed = _.compose(exclaim, greet);
    -    assert.equal(composed('moe'), 'hi: moe!', 'can compose a function that takes another');
    +    assert.strictEqual(composed('moe'), 'hi: moe!', 'can compose a function that takes another');
     
         composed = _.compose(greet, exclaim);
    -    assert.equal(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative');
    +    assert.strictEqual(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative');
     
         // f(g(h(x, y, z)))
         function h(x, y, z) {
    -      assert.equal(arguments.length, 3, 'First function called with multiple args');
    +      assert.strictEqual(arguments.length, 3, 'First function called with multiple args');
           return z * y;
         }
         function g(x) {
    -      assert.equal(arguments.length, 1, 'Composed function is called with 1 argument');
    +      assert.strictEqual(arguments.length, 1, 'Composed function is called with 1 argument');
           return x;
         }
         function f(x) {
    -      assert.equal(arguments.length, 1, 'Composed function is called with 1 argument');
    +      assert.strictEqual(arguments.length, 1, 'Composed function is called with 1 argument');
           return x * 2;
         }
         composed = _.compose(f, g, h);
    -    assert.equal(composed(1, 2, 3), 12);
    +    assert.strictEqual(composed(1, 2, 3), 12);
       });
     
       QUnit.test('after', function(assert) {
    @@ -657,10 +657,10 @@
           return afterCalled;
         };
     
    -    assert.equal(testAfter(5, 5), 1, 'after(N) should fire after being called N times');
    -    assert.equal(testAfter(5, 4), 0, 'after(N) should not fire unless called N times');
    -    assert.equal(testAfter(0, 0), 0, 'after(0) should not fire immediately');
    -    assert.equal(testAfter(0, 1), 1, 'after(0) should fire when first invoked');
    +    assert.strictEqual(testAfter(5, 5), 1, 'after(N) should fire after being called N times');
    +    assert.strictEqual(testAfter(5, 4), 0, 'after(N) should not fire unless called N times');
    +    assert.strictEqual(testAfter(0, 0), 0, 'after(0) should not fire immediately');
    +    assert.strictEqual(testAfter(0, 1), 1, 'after(0) should fire when first invoked');
       });
     
       QUnit.test('before', function(assert) {
    @@ -671,27 +671,27 @@
           return beforeCalled;
         };
     
    -    assert.equal(testBefore(5, 5), 4, 'before(N) should not fire after being called N times');
    -    assert.equal(testBefore(5, 4), 4, 'before(N) should fire before being called N times');
    -    assert.equal(testBefore(0, 0), 0, 'before(0) should not fire immediately');
    -    assert.equal(testBefore(0, 1), 0, 'before(0) should not fire when first invoked');
    +    assert.strictEqual(testBefore(5, 5), 4, 'before(N) should not fire after being called N times');
    +    assert.strictEqual(testBefore(5, 4), 4, 'before(N) should fire before being called N times');
    +    assert.strictEqual(testBefore(0, 0), 0, 'before(0) should not fire immediately');
    +    assert.strictEqual(testBefore(0, 1), 0, 'before(0) should not fire when first invoked');
     
         var context = {num: 0};
         var increment = _.before(3, function(){ return ++this.num; });
         _.times(10, increment, context);
    -    assert.equal(increment(), 2, 'stores a memo to the last value');
    -    assert.equal(context.num, 2, 'provides context');
    +    assert.strictEqual(increment(), 2, 'stores a memo to the last value');
    +    assert.strictEqual(context.num, 2, 'provides context');
       });
     
       QUnit.test('iteratee', function(assert) {
         var identity = _.iteratee();
    -    assert.equal(identity, _.identity, '_.iteratee is exposed as an external function.');
    +    assert.strictEqual(identity, _.identity, '_.iteratee is exposed as an external function.');
     
         function fn() {
           return arguments;
         }
         _.each([_.iteratee(fn), _.iteratee(fn, {})], function(cb) {
    -      assert.equal(cb().length, 0);
    +      assert.strictEqual(cb().length, 0);
           assert.deepEqual(_.toArray(cb(1, 2, 3)), _.range(1, 4));
           assert.deepEqual(_.toArray(cb(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)), _.range(1, 11));
         });
    @@ -710,22 +710,22 @@
     
         // Test all methods that claim to be transformed through `_.iteratee`
         assert.deepEqual(_.countBy(collection, /b/g), {0: 1, 1: 1, 2: 1});
    -    assert.equal(_.every(collection, /b/g), false);
    +    assert.strictEqual(_.every(collection, /b/g), false);
         assert.deepEqual(_.filter(collection, /b/g), ['bar', 'bbiz']);
    -    assert.equal(_.find(collection, /b/g), 'bar');
    -    assert.equal(_.findIndex(collection, /b/g), 1);
    +    assert.strictEqual(_.find(collection, /b/g), 'bar');
    +    assert.strictEqual(_.findIndex(collection, /b/g), 1);
         assert.strictEqual(_.findKey(collection, /b/g), '1');
    -    assert.equal(_.findLastIndex(collection, /b/g), 2);
    +    assert.strictEqual(_.findLastIndex(collection, /b/g), 2);
         assert.deepEqual(_.groupBy(collection, /b/g), {0: ['foo'], 1: ['bar'], 2: ['bbiz']});
         assert.deepEqual(_.indexBy(collection, /b/g), {0: 'foo', 1: 'bar', 2: 'bbiz'});
         assert.deepEqual(_.map(collection, /b/g), [0, 1, 2]);
    -    assert.equal(_.max(collection, /b/g), 'bbiz');
    -    assert.equal(_.min(collection, /b/g), 'foo');
    +    assert.strictEqual(_.max(collection, /b/g), 'bbiz');
    +    assert.strictEqual(_.min(collection, /b/g), 'foo');
         assert.deepEqual(_.partition(collection, /b/g), [['bar', 'bbiz'], ['foo']]);
         assert.deepEqual(_.reject(collection, /b/g), ['foo']);
    -    assert.equal(_.some(collection, /b/g), true);
    +    assert.strictEqual(_.some(collection, /b/g), true);
         assert.deepEqual(_.sortBy(collection, /b/g), ['foo', 'bar', 'bbiz']);
    -    assert.equal(_.sortedIndex(collection, 'blah', /b/g), 1);
    +    assert.strictEqual(_.sortedIndex(collection, 'blah', /b/g), 1);
         assert.deepEqual(_.uniq(collection, /b/g), ['foo', 'bar', 'bbiz']);
     
         var objCollection = {a: 'foo', b: 'bar', c: 'bbiz'};
    diff --git a/test/objects.js b/test/objects.js
    index aaa1db94d..9d7da87a4 100644
    --- a/test/objects.js
    +++ b/test/objects.js
    @@ -89,7 +89,7 @@
         assert.deepEqual(_.invert(_.invert(obj)), obj, 'two inverts gets you back where you started');
     
         obj = {length: 3};
    -    assert.equal(_.invert(obj)['3'], 'length', 'can invert an object with "length"');
    +    assert.strictEqual(_.invert(obj)['3'], 'length', 'can invert an object with "length"');
       });
     
       QUnit.test('functions', function(assert) {
    @@ -107,9 +107,9 @@
     
       QUnit.test('extend', function(assert) {
         var result;
    -    assert.equal(_.extend({}, {a: 'b'}).a, 'b', 'can extend an object with the attributes of another');
    -    assert.equal(_.extend({a: 'x'}, {a: 'b'}).a, 'b', 'properties in source override destination');
    -    assert.equal(_.extend({x: 'x'}, {a: 'b'}).x, 'x', "properties not in source don't get overriden");
    +    assert.strictEqual(_.extend({}, {a: 'b'}).a, 'b', 'can extend an object with the attributes of another');
    +    assert.strictEqual(_.extend({a: 'x'}, {a: 'b'}).a, 'b', 'properties in source override destination');
    +    assert.strictEqual(_.extend({x: 'x'}, {a: 'b'}).x, 'x', "properties not in source don't get overriden");
         result = _.extend({x: 'x'}, {a: 'a'}, {b: 'b'});
         assert.deepEqual(result, {x: 'x', a: 'a', b: 'b'}, 'can extend from multiple source objects');
         result = _.extend({x: 'x'}, {a: 'a', x: 2}, {a: 'b'});
    @@ -130,7 +130,7 @@
           _.extend(result, null, void 0, {a: 1});
         } catch (e) { /* ignored */ }
     
    -    assert.equal(result.a, 1, 'should not error on `null` or `undefined` sources');
    +    assert.strictEqual(result.a, 1, 'should not error on `null` or `undefined` sources');
     
         assert.strictEqual(_.extend(null, {a: 1}), null, 'extending null results in null');
         assert.strictEqual(_.extend(void 0, {a: 1}), void 0, 'extending undefined results in undefined');
    @@ -138,9 +138,9 @@
     
       QUnit.test('extendOwn', function(assert) {
         var result;
    -    assert.equal(_.extendOwn({}, {a: 'b'}).a, 'b', 'can extend an object with the attributes of another');
    -    assert.equal(_.extendOwn({a: 'x'}, {a: 'b'}).a, 'b', 'properties in source override destination');
    -    assert.equal(_.extendOwn({x: 'x'}, {a: 'b'}).x, 'x', "properties not in source don't get overriden");
    +    assert.strictEqual(_.extendOwn({}, {a: 'b'}).a, 'b', 'can extend an object with the attributes of another');
    +    assert.strictEqual(_.extendOwn({a: 'x'}, {a: 'b'}).a, 'b', 'properties in source override destination');
    +    assert.strictEqual(_.extendOwn({x: 'x'}, {a: 'b'}).x, 'x', "properties not in source don't get overriden");
         result = _.extendOwn({x: 'x'}, {a: 'a'}, {b: 'b'});
         assert.deepEqual(result, {x: 'x', a: 'a', b: 'b'}, 'can extend from multiple source objects');
         result = _.extendOwn({x: 'x'}, {a: 'a', x: 2}, {a: 'b'});
    @@ -207,7 +207,7 @@
     
         assert.notOk(_.has(_.pick({}, 'foo'), 'foo'), 'does not set own property if property not in object');
         _.pick(data, function(value, key, obj) {
    -      assert.equal(obj, data, 'passes same object as third parameter of iteratee');
    +      assert.strictEqual(obj, data, 'passes same object as third parameter of iteratee');
         });
       });
     
    @@ -249,22 +249,22 @@
         var options = {zero: 0, one: 1, empty: '', nan: NaN, nothing: null};
     
         _.defaults(options, {zero: 1, one: 10, twenty: 20, nothing: 'str'});
    -    assert.equal(options.zero, 0, 'value exists');
    -    assert.equal(options.one, 1, 'value exists');
    -    assert.equal(options.twenty, 20, 'default applied');
    -    assert.equal(options.nothing, null, "null isn't overridden");
    +    assert.strictEqual(options.zero, 0, 'value exists');
    +    assert.strictEqual(options.one, 1, 'value exists');
    +    assert.strictEqual(options.twenty, 20, 'default applied');
    +    assert.strictEqual(options.nothing, null, "null isn't overridden");
     
         _.defaults(options, {empty: 'full'}, {nan: 'nan'}, {word: 'word'}, {word: 'dog'});
    -    assert.equal(options.empty, '', 'value exists');
    +    assert.strictEqual(options.empty, '', 'value exists');
         assert.ok(_.isNaN(options.nan), "NaN isn't overridden");
    -    assert.equal(options.word, 'word', 'new value is added, first one wins');
    +    assert.strictEqual(options.word, 'word', 'new value is added, first one wins');
     
         try {
           options = {};
           _.defaults(options, null, void 0, {a: 1});
         } catch (e) { /* ignored */ }
     
    -    assert.equal(options.a, 1, 'should not error on `null` or `undefined` sources');
    +    assert.strictEqual(options.a, 1, 'should not error on `null` or `undefined` sources');
     
         assert.deepEqual(_.defaults(null, {a: 1}), {a: 1}, 'defaults skips nulls');
         assert.deepEqual(_.defaults(void 0, {a: 1}), {a: 1}, 'defaults skips undefined');
    @@ -273,17 +273,17 @@
       QUnit.test('clone', function(assert) {
         var moe = {name: 'moe', lucky: [13, 27, 34]};
         var clone = _.clone(moe);
    -    assert.equal(clone.name, 'moe', 'the clone as the attributes of the original');
    +    assert.strictEqual(clone.name, 'moe', 'the clone as the attributes of the original');
     
         clone.name = 'curly';
         assert.ok(clone.name === 'curly' && moe.name === 'moe', 'clones can change shallow attributes without affecting the original');
     
         clone.lucky.push(101);
    -    assert.equal(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original');
    +    assert.strictEqual(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original');
     
    -    assert.equal(_.clone(void 0), void 0, 'non objects should not be changed by clone');
    -    assert.equal(_.clone(1), 1, 'non objects should not be changed by clone');
    -    assert.equal(_.clone(null), null, 'non objects should not be changed by clone');
    +    assert.strictEqual(_.clone(void 0), void 0, 'non objects should not be changed by clone');
    +    assert.strictEqual(_.clone(1), 1, 'non objects should not be changed by clone');
    +    assert.strictEqual(_.clone(null), null, 'non objects should not be changed by clone');
       });
     
       QUnit.test('create', function(assert) {
    @@ -544,7 +544,7 @@
     
         a = _({x: 1, y: 2}).chain();
         b = _({x: 1, y: 2}).chain();
    -    assert.equal(_.isEqual(a.isEqual(b), _(true)), true, '`isEqual` can be chained');
    +    assert.strictEqual(_.isEqual(a.isEqual(b), _(true)), true, '`isEqual` can be chained');
     
         // Objects without a `constructor` property
         if (Object.create) {
    @@ -561,10 +561,10 @@
     
     
         // Tricky object cases val comparisions
    -    assert.equal(_.isEqual([0], [-0]), false);
    -    assert.equal(_.isEqual({a: 0}, {a: -0}), false);
    -    assert.equal(_.isEqual([NaN], [NaN]), true);
    -    assert.equal(_.isEqual({a: NaN}, {a: NaN}), true);
    +    assert.strictEqual(_.isEqual([0], [-0]), false);
    +    assert.strictEqual(_.isEqual({a: 0}, {a: -0}), false);
    +    assert.strictEqual(_.isEqual([NaN], [NaN]), true);
    +    assert.strictEqual(_.isEqual({a: NaN}, {a: NaN}), true);
     
         if (typeof Symbol !== 'undefined') {
           var symbol = Symbol('x');
    @@ -881,16 +881,16 @@
         var intercepted = null;
         var interceptor = function(obj) { intercepted = obj; };
         var returned = _.tap(1, interceptor);
    -    assert.equal(intercepted, 1, 'passes tapped object to interceptor');
    -    assert.equal(returned, 1, 'returns tapped object');
    +    assert.strictEqual(intercepted, 1, 'passes tapped object to interceptor');
    +    assert.strictEqual(returned, 1, 'returns tapped object');
     
         returned = _([1, 2, 3]).chain().
           map(function(n){ return n * 2; }).
           max().
           tap(interceptor).
           value();
    -    assert.equal(returned, 6, 'can use tapped objects in a chain');
    -    assert.equal(intercepted, returned, 'can use tapped objects in a chain');
    +    assert.strictEqual(returned, 6, 'can use tapped objects in a chain');
    +    assert.strictEqual(intercepted, returned, 'can use tapped objects in a chain');
       });
     
       QUnit.test('has', function(assert) {
    @@ -911,14 +911,14 @@
         var moe = {name: 'Moe Howard', hair: true};
         var curly = {name: 'Curly Howard', hair: false};
     
    -    assert.equal(_.isMatch(moe, {hair: true}), true, 'Returns a boolean');
    -    assert.equal(_.isMatch(curly, {hair: true}), false, 'Returns a boolean');
    +    assert.strictEqual(_.isMatch(moe, {hair: true}), true, 'Returns a boolean');
    +    assert.strictEqual(_.isMatch(curly, {hair: true}), false, 'Returns a boolean');
     
    -    assert.equal(_.isMatch(5, {__x__: void 0}), false, 'can match undefined props on primitives');
    -    assert.equal(_.isMatch({__x__: void 0}, {__x__: void 0}), true, 'can match undefined props');
    +    assert.strictEqual(_.isMatch(5, {__x__: void 0}), false, 'can match undefined props on primitives');
    +    assert.strictEqual(_.isMatch({__x__: void 0}, {__x__: void 0}), true, 'can match undefined props');
     
    -    assert.equal(_.isMatch(null, {}), true, 'Empty spec called with null object returns true');
    -    assert.equal(_.isMatch(null, {a: 1}), false, 'Non-empty spec called with null object returns false');
    +    assert.strictEqual(_.isMatch(null, {}), true, 'Empty spec called with null object returns true');
    +    assert.strictEqual(_.isMatch(null, {a: 1}), false, 'Non-empty spec called with null object returns false');
     
         _.each([null, void 0], function(item) { assert.strictEqual(_.isMatch(item, null), true, 'null matches null'); });
         _.each([null, void 0], function(item) { assert.strictEqual(_.isMatch(item, null), true, 'null matches {}'); });
    @@ -931,11 +931,11 @@
         function Prototest() {}
         Prototest.prototype.x = 1;
         var specObj = new Prototest;
    -    assert.equal(_.isMatch({x: 2}, specObj), true, 'spec is restricted to own properties');
    +    assert.strictEqual(_.isMatch({x: 2}, specObj), true, 'spec is restricted to own properties');
     
         specObj.y = 5;
    -    assert.equal(_.isMatch({x: 1, y: 5}, specObj), true);
    -    assert.equal(_.isMatch({x: 1, y: 4}, specObj), false);
    +    assert.strictEqual(_.isMatch({x: 1, y: 5}, specObj), true);
    +    assert.strictEqual(_.isMatch({x: 1, y: 4}, specObj), false);
     
         assert.ok(_.isMatch(specObj, {x: 1, y: 5}), 'inherited and own properties are checked on the test object');
     
    @@ -952,14 +952,14 @@
         var curly = {name: 'Curly Howard', hair: false};
         var stooges = [moe, curly];
     
    -    assert.equal(_.matcher({hair: true})(moe), true, 'Returns a boolean');
    -    assert.equal(_.matcher({hair: true})(curly), false, 'Returns a boolean');
    +    assert.strictEqual(_.matcher({hair: true})(moe), true, 'Returns a boolean');
    +    assert.strictEqual(_.matcher({hair: true})(curly), false, 'Returns a boolean');
     
    -    assert.equal(_.matcher({__x__: void 0})(5), false, 'can match undefined props on primitives');
    -    assert.equal(_.matcher({__x__: void 0})({__x__: void 0}), true, 'can match undefined props');
    +    assert.strictEqual(_.matcher({__x__: void 0})(5), false, 'can match undefined props on primitives');
    +    assert.strictEqual(_.matcher({__x__: void 0})({__x__: void 0}), true, 'can match undefined props');
     
    -    assert.equal(_.matcher({})(null), true, 'Empty spec called with null object returns true');
    -    assert.equal(_.matcher({a: 1})(null), false, 'Non-empty spec called with null object returns false');
    +    assert.strictEqual(_.matcher({})(null), true, 'Empty spec called with null object returns true');
    +    assert.strictEqual(_.matcher({a: 1})(null), false, 'Non-empty spec called with null object returns false');
     
         assert.strictEqual(_.find(stooges, _.matcher({hair: false})), curly, 'returns a predicate that can be used by finding functions.');
         assert.strictEqual(_.find(stooges, _.matcher(moe)), moe, 'can be used to locate an object exists in a collection.');
    @@ -970,19 +970,19 @@
         assert.deepEqual(_.filter([{b: 1}], _.matcher({a: void 0})), [], 'handles undefined values (1683)');
     
         _.each([true, 5, NaN, null, void 0], function(item) {
    -      assert.equal(_.matcher(item)({a: 1}), true, 'treats primitives as empty');
    +      assert.strictEqual(_.matcher(item)({a: 1}), true, 'treats primitives as empty');
         });
     
         function Prototest() {}
         Prototest.prototype.x = 1;
         var specObj = new Prototest;
         var protospec = _.matcher(specObj);
    -    assert.equal(protospec({x: 2}), true, 'spec is restricted to own properties');
    +    assert.strictEqual(protospec({x: 2}), true, 'spec is restricted to own properties');
     
         specObj.y = 5;
         protospec = _.matcher(specObj);
    -    assert.equal(protospec({x: 1, y: 5}), true);
    -    assert.equal(protospec({x: 1, y: 4}), false);
    +    assert.strictEqual(protospec({x: 1, y: 5}), true);
    +    assert.strictEqual(protospec({x: 1, y: 4}), false);
     
         assert.ok(_.matcher({x: 1, y: 5})(specObj), 'inherited and own properties are checked on the test object');
     
    @@ -993,10 +993,10 @@
         var o = {b: 1};
         var m = _.matcher(o);
     
    -    assert.equal(m({b: 1}), true);
    +    assert.strictEqual(m({b: 1}), true);
         o.b = 2;
         o.a = 1;
    -    assert.equal(m({b: 1}), true, 'changing spec object doesnt change matches result');
    +    assert.strictEqual(m({b: 1}), true, 'changing spec object doesnt change matches result');
     
     
         //null edge cases
    @@ -1015,17 +1015,17 @@
           c: {a: 2, b: 2}
         };
     
    -    assert.equal(_.findKey(objects, function(obj) {
    +    assert.strictEqual(_.findKey(objects, function(obj) {
           return obj.a === 0;
         }), 'a');
     
    -    assert.equal(_.findKey(objects, function(obj) {
    +    assert.strictEqual(_.findKey(objects, function(obj) {
           return obj.b * obj.a === 4;
         }), 'c');
     
    -    assert.equal(_.findKey(objects, 'a'), 'b', 'Uses lookupIterator');
    +    assert.strictEqual(_.findKey(objects, 'a'), 'b', 'Uses lookupIterator');
     
    -    assert.equal(_.findKey(objects, function(obj) {
    +    assert.strictEqual(_.findKey(objects, function(obj) {
           return obj.b * obj.a === 5;
         }), void 0);
     
    @@ -1038,7 +1038,7 @@
         }), void 0);
     
         _.findKey({a: {a: 1}}, function(a, key, obj) {
    -      assert.equal(key, 'a');
    +      assert.strictEqual(key, 'a');
           assert.deepEqual(obj, {a: {a: 1}});
           assert.strictEqual(this, objects, 'called with context');
         }, objects);
    diff --git a/test/utility.js b/test/utility.js
    index 6a81e8735..26d1c1122 100644
    --- a/test/utility.js
    +++ b/test/utility.js
    @@ -17,9 +17,9 @@
       if (typeof this == 'object') {
         QUnit.test('noConflict', function(assert) {
           var underscore = _.noConflict();
    -      assert.equal(underscore.identity(1), 1);
    +      assert.strictEqual(underscore.identity(1), 1);
           if (typeof require != 'function') {
    -        assert.equal(this._, void 0, 'global underscore is removed');
    +        assert.strictEqual(this._, void 0, 'global underscore is removed');
             this._ = underscore;
           } else if (typeof global !== 'undefined') {
             delete global._;
    @@ -41,8 +41,8 @@
             );
             var context = {_: 'oldvalue'};
             sandbox.runInNewContext(context);
    -        assert.equal(context._, 'oldvalue');
    -        assert.equal(context.underscore.VERSION, _.VERSION);
    +        assert.strictEqual(context._, 'oldvalue');
    +        assert.strictEqual(context.underscore.VERSION, _.VERSION);
     
             done();
           });
    @@ -58,12 +58,12 @@
     
       QUnit.test('identity', function(assert) {
         var stooge = {name: 'moe'};
    -    assert.equal(_.identity(stooge), stooge, 'stooge is the same as his identity');
    +    assert.strictEqual(_.identity(stooge), stooge, 'stooge is the same as his identity');
       });
     
       QUnit.test('constant', function(assert) {
         var stooge = {name: 'moe'};
    -    assert.equal(_.constant(stooge)(), stooge, 'should create a function that returns stooge');
    +    assert.strictEqual(_.constant(stooge)(), stooge, 'should create a function that returns stooge');
       });
     
       QUnit.test('noop', function(assert) {
    @@ -72,27 +72,27 @@
     
       QUnit.test('property', function(assert) {
         var stooge = {name: 'moe'};
    -    assert.equal(_.property('name')(stooge), 'moe', 'should return the property with the given name');
    -    assert.equal(_.property('name')(null), void 0, 'should return undefined for null values');
    -    assert.equal(_.property('name')(void 0), void 0, 'should return undefined for undefined values');
    +    assert.strictEqual(_.property('name')(stooge), 'moe', 'should return the property with the given name');
    +    assert.strictEqual(_.property('name')(null), void 0, 'should return undefined for null values');
    +    assert.strictEqual(_.property('name')(void 0), void 0, 'should return undefined for undefined values');
       });
     
       QUnit.test('propertyOf', function(assert) {
         var stoogeRanks = _.propertyOf({curly: 2, moe: 1, larry: 3});
    -    assert.equal(stoogeRanks('curly'), 2, 'should return the property with the given name');
    -    assert.equal(stoogeRanks(null), void 0, 'should return undefined for null values');
    -    assert.equal(stoogeRanks(void 0), void 0, 'should return undefined for undefined values');
    +    assert.strictEqual(stoogeRanks('curly'), 2, 'should return the property with the given name');
    +    assert.strictEqual(stoogeRanks(null), void 0, 'should return undefined for null values');
    +    assert.strictEqual(stoogeRanks(void 0), void 0, 'should return undefined for undefined values');
     
         function MoreStooges() { this.shemp = 87; }
         MoreStooges.prototype = {curly: 2, moe: 1, larry: 3};
         var moreStoogeRanks = _.propertyOf(new MoreStooges());
    -    assert.equal(moreStoogeRanks('curly'), 2, 'should return properties from further up the prototype chain');
    +    assert.strictEqual(moreStoogeRanks('curly'), 2, 'should return properties from further up the prototype chain');
     
         var nullPropertyOf = _.propertyOf(null);
    -    assert.equal(nullPropertyOf('curly'), void 0, 'should return undefined when obj is null');
    +    assert.strictEqual(nullPropertyOf('curly'), void 0, 'should return undefined when obj is null');
     
         var undefPropertyOf = _.propertyOf(void 0);
    -    assert.equal(undefPropertyOf('curly'), void 0, 'should return undefined when obj is undefined');
    +    assert.strictEqual(undefPropertyOf('curly'), void 0, 'should return undefined when obj is undefined');
       });
     
       QUnit.test('random', function(assert) {
    @@ -117,7 +117,7 @@
       QUnit.test('uniqueId', function(assert) {
         var ids = [], i = 0;
         while (i++ < 100) ids.push(_.uniqueId());
    -    assert.equal(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids');
    +    assert.strictEqual(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids');
       });
     
       QUnit.test('times', function(assert) {
    @@ -142,20 +142,20 @@
             return string.split('').reverse().join('');
           }
         });
    -    assert.equal(ret, _, 'returns the _ object to facilitate chaining');
    -    assert.equal(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _');
    -    assert.equal(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper');
    +    assert.strictEqual(ret, _, 'returns the _ object to facilitate chaining');
    +    assert.strictEqual(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _');
    +    assert.strictEqual(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper');
       });
     
       QUnit.test('_.escape', function(assert) {
    -    assert.equal(_.escape(null), '');
    +    assert.strictEqual(_.escape(null), '');
       });
     
       QUnit.test('_.unescape', function(assert) {
         var string = 'Curly & Moe';
    -    assert.equal(_.unescape(null), '');
    -    assert.equal(_.unescape(_.escape(string)), string);
    -    assert.equal(_.unescape(string), string, 'don\'t unescape unnecessarily');
    +    assert.strictEqual(_.unescape(null), '');
    +    assert.strictEqual(_.unescape(_.escape(string)), string);
    +    assert.strictEqual(_.unescape(string), string, 'don\'t unescape unnecessarily');
       });
     
       // Don't care what they escape them to just that they're escaped and can be unescaped
    @@ -167,13 +167,13 @@
           var s = 'a ' + escapeChar + ' string escaped';
           var e = _.escape(s);
           assert.notEqual(s, e, escapeChar + ' is escaped');
    -      assert.equal(s, _.unescape(e), escapeChar + ' can be unescaped');
    +      assert.strictEqual(s, _.unescape(e), escapeChar + ' can be unescaped');
     
           s = 'a ' + escapeChar + escapeChar + escapeChar + 'some more string' + escapeChar;
           e = _.escape(s);
     
    -      assert.equal(e.indexOf(escapeChar), -1, 'can escape multiple occurances of ' + escapeChar);
    -      assert.equal(_.unescape(e), s, 'multiple occurrences of ' + escapeChar + ' can be unescaped');
    +      assert.strictEqual(e.indexOf(escapeChar), -1, 'can escape multiple occurances of ' + escapeChar);
    +      assert.strictEqual(_.unescape(e), s, 'multiple occurrences of ' + escapeChar + ' can be unescaped');
         });
     
         // handles multiple escape characters at once
    @@ -190,32 +190,32 @@
         var escaped = _.escape(str);
     
         assert.notStrictEqual(escaped.indexOf('&'), -1, 'handles & aka &');
    -    assert.equal(_.unescape(str), str, 'can unescape &');
    +    assert.strictEqual(_.unescape(str), str, 'can unescape &');
       });
     
       QUnit.test('template', function(assert) {
         var basicTemplate = _.template("<%= thing %> is gettin' on my noives!");
         var result = basicTemplate({thing: 'This'});
    -    assert.equal(result, "This is gettin' on my noives!", 'can do basic attribute interpolation');
    +    assert.strictEqual(result, "This is gettin' on my noives!", 'can do basic attribute interpolation');
     
         var sansSemicolonTemplate = _.template('A <% this %> B');
    -    assert.equal(sansSemicolonTemplate(), 'A  B');
    +    assert.strictEqual(sansSemicolonTemplate(), 'A  B');
     
         var backslashTemplate = _.template('<%= thing %> is \\ridanculous');
    -    assert.equal(backslashTemplate({thing: 'This'}), 'This is \\ridanculous');
    +    assert.strictEqual(backslashTemplate({thing: 'This'}), 'This is \\ridanculous');
     
         var escapeTemplate = _.template('<%= a ? "checked=\\"checked\\"" : "" %>');
    -    assert.equal(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.');
    +    assert.strictEqual(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.');
     
         var fancyTemplate = _.template('
      <% ' + ' for (var key in people) { ' + '%>
    • <%= people[key] %>
    • <% } %>
    '); result = fancyTemplate({people: {moe: 'Moe', larry: 'Larry', curly: 'Curly'}}); - assert.equal(result, '
    • Moe
    • Larry
    • Curly
    ', 'can run arbitrary javascript in templates'); + assert.strictEqual(result, '
    • Moe
    • Larry
    • Curly
    ', 'can run arbitrary javascript in templates'); var escapedCharsInJavascriptTemplate = _.template('
      <% _.each(numbers.split("\\n"), function(item) { %>
    • <%= item %>
    • <% }) %>
    '); result = escapedCharsInJavascriptTemplate({numbers: 'one\ntwo\nthree\nfour'}); - assert.equal(result, '
    • one
    • two
    • three
    • four
    ', 'Can use escaped characters (e.g. \\n) in JavaScript'); + assert.strictEqual(result, '
    • one
    • two
    • three
    • four
    ', 'Can use escaped characters (e.g. \\n) in JavaScript'); var namespaceCollisionTemplate = _.template('<%= pageCount %> <%= thumbnails[pageCount] %> <% _.each(thumbnails, function(p) { %>
    <% }); %>'); result = namespaceCollisionTemplate({ @@ -226,32 +226,32 @@ 3: 'p3-thumbnail.gif' } }); - assert.equal(result, '3 p3-thumbnail.gif
    '); + assert.strictEqual(result, '3 p3-thumbnail.gif
    '); var noInterpolateTemplate = _.template('

    Just some text. Hey, I know this is silly but it aids consistency.

    '); result = noInterpolateTemplate(); - assert.equal(result, '

    Just some text. Hey, I know this is silly but it aids consistency.

    '); + assert.strictEqual(result, '

    Just some text. Hey, I know this is silly but it aids consistency.

    '); var quoteTemplate = _.template("It's its, not it's"); - assert.equal(quoteTemplate({}), "It's its, not it's"); + assert.strictEqual(quoteTemplate({}), "It's its, not it's"); var quoteInStatementAndBody = _.template('<% ' + " if(foo == 'bar'){ " + "%>Statement quotes and 'quotes'.<% } %>"); - assert.equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'."); + assert.strictEqual(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'."); var withNewlinesAndTabs = _.template('This\n\t\tis: <%= x %>.\n\tok.\nend.'); - assert.equal(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.'); + assert.strictEqual(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.'); var template = _.template('<%- value %>'); result = template({value: ' - diff --git a/test/objects.js b/test/objects.js index 811ddc97d..1042eec50 100644 --- a/test/objects.js +++ b/test/objects.js @@ -123,7 +123,7 @@ subObj.c = 'd'; assert.deepEqual(_.extend({}, subObj), {a: 'b', c: 'd'}, 'extend copies all properties from source'); _.extend(subObj, {}); - assert.notOk(subObj.hasOwnProperty('a'), "extend does not convert destination object's 'in' properties to 'own' properties"); + assert.ok(!subObj.hasOwnProperty('a'), "extend does not convert destination object's 'in' properties to 'own' properties"); try { result = {}; @@ -205,7 +205,7 @@ return this[key] === 3 && this === instance; }, instance), {c: 3}, 'function is given context'); - assert.notOk(_.has(_.pick({}, 'foo'), 'foo'), 'does not set own property if property not in object'); + assert.ok(!_.has(_.pick({}, 'foo'), 'foo'), 'does not set own property if property not in object'); _.pick(data, function(value, key, obj) { assert.strictEqual(obj, data, 'passes same object as third parameter of iteratee'); }); @@ -309,7 +309,7 @@ Child.prototype.foo = 'foo'; var created = _.create(Child.prototype, new Child); - assert.notOk(created.hasOwnProperty('foo'), 'should only add own properties'); + assert.ok(!created.hasOwnProperty('foo'), 'should only add own properties'); }); QUnit.test('isEqual', function(assert) { @@ -326,10 +326,10 @@ assert.ok(_.isEqual(null, null), '`null` is equal to `null`'); assert.ok(_.isEqual(), '`undefined` is equal to `undefined`'); - assert.notOk(_.isEqual(0, -0), '`0` is not equal to `-0`'); - assert.notOk(_.isEqual(-0, 0), 'Commutative equality is implemented for `0` and `-0`'); - assert.notOk(_.isEqual(null, void 0), '`null` is not equal to `undefined`'); - assert.notOk(_.isEqual(void 0, null), 'Commutative equality is implemented for `null` and `undefined`'); + assert.ok(!_.isEqual(0, -0), '`0` is not equal to `-0`'); + assert.ok(!_.isEqual(-0, 0), 'Commutative equality is implemented for `0` and `-0`'); + assert.ok(!_.isEqual(null, void 0), '`null` is not equal to `undefined`'); + assert.ok(!_.isEqual(void 0, null), 'Commutative equality is implemented for `null` and `undefined`'); // String object and primitive comparisons. assert.ok(_.isEqual('Curly', 'Curly'), 'Identical string primitives are equal'); @@ -337,76 +337,76 @@ assert.ok(_.isEqual(new String('Curly'), 'Curly'), 'String primitives and their corresponding object wrappers are equal'); assert.ok(_.isEqual('Curly', new String('Curly')), 'Commutative equality is implemented for string objects and primitives'); - assert.notOk(_.isEqual('Curly', 'Larry'), 'String primitives with different values are not equal'); - assert.notOk(_.isEqual(new String('Curly'), new String('Larry')), 'String objects with different primitive values are not equal'); - assert.notOk(_.isEqual(new String('Curly'), {toString: function(){ return 'Curly'; }}), 'String objects and objects with a custom `toString` method are not equal'); + assert.ok(!_.isEqual('Curly', 'Larry'), 'String primitives with different values are not equal'); + assert.ok(!_.isEqual(new String('Curly'), new String('Larry')), 'String objects with different primitive values are not equal'); + assert.ok(!_.isEqual(new String('Curly'), {toString: function(){ return 'Curly'; }}), 'String objects and objects with a custom `toString` method are not equal'); // Number object and primitive comparisons. assert.ok(_.isEqual(75, 75), 'Identical number primitives are equal'); assert.ok(_.isEqual(new Number(75), new Number(75)), 'Number objects with identical primitive values are equal'); assert.ok(_.isEqual(75, new Number(75)), 'Number primitives and their corresponding object wrappers are equal'); assert.ok(_.isEqual(new Number(75), 75), 'Commutative equality is implemented for number objects and primitives'); - assert.notOk(_.isEqual(new Number(0), -0), '`new Number(0)` and `-0` are not equal'); - assert.notOk(_.isEqual(0, new Number(-0)), 'Commutative equality is implemented for `new Number(0)` and `-0`'); + assert.ok(!_.isEqual(new Number(0), -0), '`new Number(0)` and `-0` are not equal'); + assert.ok(!_.isEqual(0, new Number(-0)), 'Commutative equality is implemented for `new Number(0)` and `-0`'); - assert.notOk(_.isEqual(new Number(75), new Number(63)), 'Number objects with different primitive values are not equal'); - assert.notOk(_.isEqual(new Number(63), {valueOf: function(){ return 63; }}), 'Number objects and objects with a `valueOf` method are not equal'); + assert.ok(!_.isEqual(new Number(75), new Number(63)), 'Number objects with different primitive values are not equal'); + assert.ok(!_.isEqual(new Number(63), {valueOf: function(){ return 63; }}), 'Number objects and objects with a `valueOf` method are not equal'); // Comparisons involving `NaN`. assert.ok(_.isEqual(NaN, NaN), '`NaN` is equal to `NaN`'); assert.ok(_.isEqual(new Number(NaN), NaN), 'Object(`NaN`) is equal to `NaN`'); - assert.notOk(_.isEqual(61, NaN), 'A number primitive is not equal to `NaN`'); - assert.notOk(_.isEqual(new Number(79), NaN), 'A number object is not equal to `NaN`'); - assert.notOk(_.isEqual(Infinity, NaN), '`Infinity` is not equal to `NaN`'); + assert.ok(!_.isEqual(61, NaN), 'A number primitive is not equal to `NaN`'); + assert.ok(!_.isEqual(new Number(79), NaN), 'A number object is not equal to `NaN`'); + assert.ok(!_.isEqual(Infinity, NaN), '`Infinity` is not equal to `NaN`'); // Boolean object and primitive comparisons. assert.ok(_.isEqual(true, true), 'Identical boolean primitives are equal'); assert.ok(_.isEqual(new Boolean, new Boolean), 'Boolean objects with identical primitive values are equal'); assert.ok(_.isEqual(true, new Boolean(true)), 'Boolean primitives and their corresponding object wrappers are equal'); assert.ok(_.isEqual(new Boolean(true), true), 'Commutative equality is implemented for booleans'); - assert.notOk(_.isEqual(new Boolean(true), new Boolean), 'Boolean objects with different primitive values are not equal'); + assert.ok(!_.isEqual(new Boolean(true), new Boolean), 'Boolean objects with different primitive values are not equal'); // Common type coercions. - assert.notOk(_.isEqual(new Boolean(false), true), '`new Boolean(false)` is not equal to `true`'); - assert.notOk(_.isEqual('75', 75), 'String and number primitives with like values are not equal'); - assert.notOk(_.isEqual(new Number(63), new String(63)), 'String and number objects with like values are not equal'); - assert.notOk(_.isEqual(75, '75'), 'Commutative equality is implemented for like string and number values'); - assert.notOk(_.isEqual(0, ''), 'Number and string primitives with like values are not equal'); - assert.notOk(_.isEqual(1, true), 'Number and boolean primitives with like values are not equal'); - assert.notOk(_.isEqual(new Boolean(false), new Number(0)), 'Boolean and number objects with like values are not equal'); - assert.notOk(_.isEqual(false, new String('')), 'Boolean primitives and string objects with like values are not equal'); - assert.notOk(_.isEqual(12564504e5, new Date(2009, 9, 25)), 'Dates and their corresponding numeric primitive values are not equal'); + assert.ok(!_.isEqual(new Boolean(false), true), '`new Boolean(false)` is not equal to `true`'); + assert.ok(!_.isEqual('75', 75), 'String and number primitives with like values are not equal'); + assert.ok(!_.isEqual(new Number(63), new String(63)), 'String and number objects with like values are not equal'); + assert.ok(!_.isEqual(75, '75'), 'Commutative equality is implemented for like string and number values'); + assert.ok(!_.isEqual(0, ''), 'Number and string primitives with like values are not equal'); + assert.ok(!_.isEqual(1, true), 'Number and boolean primitives with like values are not equal'); + assert.ok(!_.isEqual(new Boolean(false), new Number(0)), 'Boolean and number objects with like values are not equal'); + assert.ok(!_.isEqual(false, new String('')), 'Boolean primitives and string objects with like values are not equal'); + assert.ok(!_.isEqual(12564504e5, new Date(2009, 9, 25)), 'Dates and their corresponding numeric primitive values are not equal'); // Dates. assert.ok(_.isEqual(new Date(2009, 9, 25), new Date(2009, 9, 25)), 'Date objects referencing identical times are equal'); - assert.notOk(_.isEqual(new Date(2009, 9, 25), new Date(2009, 11, 13)), 'Date objects referencing different times are not equal'); - assert.notOk(_.isEqual(new Date(2009, 11, 13), { + assert.ok(!_.isEqual(new Date(2009, 9, 25), new Date(2009, 11, 13)), 'Date objects referencing different times are not equal'); + assert.ok(!_.isEqual(new Date(2009, 11, 13), { getTime: function(){ return 12606876e5; } }), 'Date objects and objects with a `getTime` method are not equal'); - assert.notOk(_.isEqual(new Date('Curly'), new Date('Curly')), 'Invalid dates are not equal'); + assert.ok(!_.isEqual(new Date('Curly'), new Date('Curly')), 'Invalid dates are not equal'); // Functions. - assert.notOk(_.isEqual(First, Second), 'Different functions with identical bodies and source code representations are not equal'); + assert.ok(!_.isEqual(First, Second), 'Different functions with identical bodies and source code representations are not equal'); // RegExps. assert.ok(_.isEqual(/(?:)/gim, /(?:)/gim), 'RegExps with equivalent patterns and flags are equal'); assert.ok(_.isEqual(/(?:)/gi, /(?:)/ig), 'Flag order is not significant'); - assert.notOk(_.isEqual(/(?:)/g, /(?:)/gi), 'RegExps with equivalent patterns and different flags are not equal'); - assert.notOk(_.isEqual(/Moe/gim, /Curly/gim), 'RegExps with different patterns and equivalent flags are not equal'); - assert.notOk(_.isEqual(/(?:)/gi, /(?:)/g), 'Commutative equality is implemented for RegExps'); - assert.notOk(_.isEqual(/Curly/g, {source: 'Larry', global: true, ignoreCase: false, multiline: false}), 'RegExps and RegExp-like objects are not equal'); + assert.ok(!_.isEqual(/(?:)/g, /(?:)/gi), 'RegExps with equivalent patterns and different flags are not equal'); + assert.ok(!_.isEqual(/Moe/gim, /Curly/gim), 'RegExps with different patterns and equivalent flags are not equal'); + assert.ok(!_.isEqual(/(?:)/gi, /(?:)/g), 'Commutative equality is implemented for RegExps'); + assert.ok(!_.isEqual(/Curly/g, {source: 'Larry', global: true, ignoreCase: false, multiline: false}), 'RegExps and RegExp-like objects are not equal'); // Empty arrays, array-like objects, and object literals. assert.ok(_.isEqual({}, {}), 'Empty object literals are equal'); assert.ok(_.isEqual([], []), 'Empty array literals are equal'); assert.ok(_.isEqual([{}], [{}]), 'Empty nested arrays and objects are equal'); - assert.notOk(_.isEqual({length: 0}, []), 'Array-like objects and arrays are not equal.'); - assert.notOk(_.isEqual([], {length: 0}), 'Commutative equality is implemented for array-like objects'); + assert.ok(!_.isEqual({length: 0}, []), 'Array-like objects and arrays are not equal.'); + assert.ok(!_.isEqual([], {length: 0}), 'Commutative equality is implemented for array-like objects'); - assert.notOk(_.isEqual({}, []), 'Object literals and array literals are not equal'); - assert.notOk(_.isEqual([], {}), 'Commutative equality is implemented for objects and arrays'); + assert.ok(!_.isEqual({}, []), 'Object literals and array literals are not equal'); + assert.ok(!_.isEqual([], {}), 'Commutative equality is implemented for objects and arrays'); // Arrays with primitive and object values. assert.ok(_.isEqual([1, 'Larry', true], [1, 'Larry', true]), 'Arrays containing identical primitives are equal'); @@ -424,14 +424,14 @@ // Array elements and properties. assert.ok(_.isEqual(a, b), 'Arrays containing equivalent elements and different non-numeric properties are equal'); a.push('White Rocks'); - assert.notOk(_.isEqual(a, b), 'Arrays of different lengths are not equal'); + assert.ok(!_.isEqual(a, b), 'Arrays of different lengths are not equal'); a.push('East Boulder'); b.push('Gunbarrel Ranch', 'Teller Farm'); - assert.notOk(_.isEqual(a, b), 'Arrays of identical lengths containing different elements are not equal'); + assert.ok(!_.isEqual(a, b), 'Arrays of identical lengths containing different elements are not equal'); // Sparse arrays. assert.ok(_.isEqual(Array(3), Array(3)), 'Sparse arrays of identical lengths are equal'); - assert.notOk(_.isEqual(Array(3), Array(6)), 'Sparse arrays of different lengths are not equal when both are empty'); + assert.ok(!_.isEqual(Array(3), Array(6)), 'Sparse arrays of different lengths are not equal when both are empty'); var sparse = []; sparse[1] = 5; @@ -440,11 +440,11 @@ // Simple objects. assert.ok(_.isEqual({a: 'Curly', b: 1, c: true}, {a: 'Curly', b: 1, c: true}), 'Objects containing identical primitives are equal'); assert.ok(_.isEqual({a: /Curly/g, b: new Date(2009, 11, 13)}, {a: /Curly/g, b: new Date(2009, 11, 13)}), 'Objects containing equivalent members are equal'); - assert.notOk(_.isEqual({a: 63, b: 75}, {a: 61, b: 55}), 'Objects of identical sizes with different values are not equal'); - assert.notOk(_.isEqual({a: 63, b: 75}, {a: 61, c: 55}), 'Objects of identical sizes with different property names are not equal'); - assert.notOk(_.isEqual({a: 1, b: 2}, {a: 1}), 'Objects of different sizes are not equal'); - assert.notOk(_.isEqual({a: 1}, {a: 1, b: 2}), 'Commutative equality is implemented for objects'); - assert.notOk(_.isEqual({x: 1, y: void 0}, {x: 1, z: 2}), 'Objects with identical keys and different values are not equivalent'); + assert.ok(!_.isEqual({a: 63, b: 75}, {a: 61, b: 55}), 'Objects of identical sizes with different values are not equal'); + assert.ok(!_.isEqual({a: 63, b: 75}, {a: 61, c: 55}), 'Objects of identical sizes with different property names are not equal'); + assert.ok(!_.isEqual({a: 1, b: 2}, {a: 1}), 'Objects of different sizes are not equal'); + assert.ok(!_.isEqual({a: 1}, {a: 1, b: 2}), 'Commutative equality is implemented for objects'); + assert.ok(!_.isEqual({x: 1, y: void 0}, {x: 1, z: 2}), 'Objects with identical keys and different values are not equivalent'); // `A` contains nested objects and arrays. a = { @@ -479,9 +479,9 @@ // Instances. assert.ok(_.isEqual(new First, new First), 'Object instances are equal'); - assert.notOk(_.isEqual(new First, new Second), 'Objects with different constructors and identical own properties are not equal'); - assert.notOk(_.isEqual({value: 1}, new First), 'Object instances and objects sharing equivalent properties are not equal'); - assert.notOk(_.isEqual({value: 2}, new Second), 'The prototype chain of objects should not be examined'); + assert.ok(!_.isEqual(new First, new Second), 'Objects with different constructors and identical own properties are not equal'); + assert.ok(!_.isEqual({value: 1}, new First), 'Object instances and objects sharing equivalent properties are not equal'); + assert.ok(!_.isEqual({value: 2}, new Second), 'The prototype chain of objects should not be examined'); // Circular Arrays. (a = []).push(a); @@ -492,13 +492,13 @@ assert.ok(_.isEqual(a, b), 'Arrays containing circular references and equivalent properties are equal'); a.push('Shemp'); b.push('Curly'); - assert.notOk(_.isEqual(a, b), 'Arrays containing circular references and different properties are not equal'); + assert.ok(!_.isEqual(a, b), 'Arrays containing circular references and different properties are not equal'); // More circular arrays #767. a = ['everything is checked but', 'this', 'is not']; a[1] = a; b = ['everything is checked but', ['this', 'array'], 'is not']; - assert.notOk(_.isEqual(a, b), 'Comparison of circular references with non-circular references are not equal'); + assert.ok(!_.isEqual(a, b), 'Comparison of circular references with non-circular references are not equal'); // Circular Objects. a = {abc: null}; @@ -511,13 +511,13 @@ assert.ok(_.isEqual(a, b), 'Objects containing circular references and equivalent properties are equal'); a.def = new Number(75); b.def = new Number(63); - assert.notOk(_.isEqual(a, b), 'Objects containing circular references and different properties are not equal'); + assert.ok(!_.isEqual(a, b), 'Objects containing circular references and different properties are not equal'); // More circular objects #767. a = {everything: 'is checked', but: 'this', is: 'not'}; a.but = a; b = {everything: 'is checked', but: {that: 'object'}, is: 'not'}; - assert.notOk(_.isEqual(a, b), 'Comparison of circular references with non-circular object references are not equal'); + assert.ok(!_.isEqual(a, b), 'Comparison of circular references with non-circular object references are not equal'); // Cyclic Structures. a = [{abc: null}]; @@ -530,7 +530,7 @@ assert.ok(_.isEqual(a, b), 'Cyclic structures containing equivalent properties are equal'); a[0].def = new String('Larry'); b[0].def = new String('Curly'); - assert.notOk(_.isEqual(a, b), 'Cyclic structures containing different properties are not equal'); + assert.ok(!_.isEqual(a, b), 'Cyclic structures containing different properties are not equal'); // Complex Circular References. a = {foo: {b: {foo: {c: {foo: null}}}}}; @@ -540,7 +540,7 @@ assert.ok(_.isEqual(a, b), 'Cyclic structures with nested and identically-named properties are equal'); // Chaining. - assert.notOk(_.isEqual(_({x: 1, y: void 0}).chain(), _({x: 1, z: 2}).chain()), 'Chained objects containing different values are not equal'); + assert.ok(!_.isEqual(_({x: 1, y: void 0}).chain(), _({x: 1, z: 2}).chain()), 'Chained objects containing different values are not equal'); a = _({x: 1, y: 2}).chain(); b = _({x: 1, y: 2}).chain(); @@ -581,15 +581,15 @@ }); QUnit.test('isEmpty', function(assert) { - assert.notOk(_([1]).isEmpty(), '[1] is not empty'); + assert.ok(!_([1]).isEmpty(), '[1] is not empty'); assert.ok(_.isEmpty([]), '[] is empty'); - assert.notOk(_.isEmpty({one: 1}), '{one: 1} is not empty'); + assert.ok(!_.isEmpty({one: 1}), '{one: 1} is not empty'); assert.ok(_.isEmpty({}), '{} is empty'); assert.ok(_.isEmpty(new RegExp('')), 'objects with prototype properties are empty'); assert.ok(_.isEmpty(null), 'null is empty'); assert.ok(_.isEmpty(), 'undefined is empty'); assert.ok(_.isEmpty(''), 'the empty string is empty'); - assert.notOk(_.isEmpty('moe'), 'but other strings are not'); + assert.ok(!_.isEmpty('moe'), 'but other strings are not'); var obj = {one: 1}; delete obj.one; @@ -597,27 +597,27 @@ var args = function(){ return arguments; }; assert.ok(_.isEmpty(args()), 'empty arguments object is empty'); - assert.notOk(_.isEmpty(args('')), 'non-empty arguments object is not empty'); + assert.ok(!_.isEmpty(args('')), 'non-empty arguments object is not empty'); // covers collecting non-enumerable properties in IE < 9 var nonEnumProp = {toString: 5}; - assert.notOk(_.isEmpty(nonEnumProp), 'non-enumerable property is not empty'); + assert.ok(!_.isEmpty(nonEnumProp), 'non-enumerable property is not empty'); }); if (typeof document === 'object') { QUnit.test('isElement', function(assert) { - assert.notOk(_.isElement('div'), 'strings are not dom elements'); + assert.ok(!_.isElement('div'), 'strings are not dom elements'); assert.ok(_.isElement(testElement), 'an element is a DOM element'); }); } QUnit.test('isArguments', function(assert) { var args = (function(){ return arguments; }(1, 2, 3)); - assert.notOk(_.isArguments('string'), 'a string is not an arguments object'); - assert.notOk(_.isArguments(_.isArguments), 'a function is not an arguments object'); + assert.ok(!_.isArguments('string'), 'a string is not an arguments object'); + assert.ok(!_.isArguments(_.isArguments), 'a function is not an arguments object'); assert.ok(_.isArguments(args), 'but the arguments object is an arguments object'); - assert.notOk(_.isArguments(_.toArray(args)), 'but not when it\'s converted into an array'); - assert.notOk(_.isArguments([1, 2, 3]), 'and not vanilla arrays.'); + assert.ok(!_.isArguments(_.toArray(args)), 'but not when it\'s converted into an array'); + assert.ok(!_.isArguments([1, 2, 3]), 'and not vanilla arrays.'); }); QUnit.test('isObject', function(assert) { @@ -627,24 +627,24 @@ assert.ok(_.isObject(testElement), 'and DOM element'); } assert.ok(_.isObject(function() {}), 'and functions'); - assert.notOk(_.isObject(null), 'but not null'); - assert.notOk(_.isObject(void 0), 'and not undefined'); - assert.notOk(_.isObject('string'), 'and not string'); - assert.notOk(_.isObject(12), 'and not number'); - assert.notOk(_.isObject(true), 'and not boolean'); + assert.ok(!_.isObject(null), 'but not null'); + assert.ok(!_.isObject(void 0), 'and not undefined'); + assert.ok(!_.isObject('string'), 'and not string'); + assert.ok(!_.isObject(12), 'and not number'); + assert.ok(!_.isObject(true), 'and not boolean'); assert.ok(_.isObject(new String('string')), 'but new String()'); }); QUnit.test('isArray', function(assert) { - assert.notOk(_.isArray(void 0), 'undefined vars are not arrays'); - assert.notOk(_.isArray(arguments), 'the arguments object is not an array'); + assert.ok(!_.isArray(void 0), 'undefined vars are not arrays'); + assert.ok(!_.isArray(arguments), 'the arguments object is not an array'); assert.ok(_.isArray([1, 2, 3]), 'but arrays are'); }); QUnit.test('isString', function(assert) { var obj = new String('I am a string object'); if (testElement) { - assert.notOk(_.isString(testElement), 'an element is not a string'); + assert.ok(!_.isString(testElement), 'an element is not a string'); } assert.ok(_.isString([1, 2, 3].join(', ')), 'but strings are'); assert.strictEqual(_.isString('I am a string literal'), true, 'string literals are'); @@ -653,9 +653,9 @@ }); QUnit.test('isSymbol', function(assert) { - assert.notOk(_.isSymbol(0), 'numbers are not symbols'); - assert.notOk(_.isSymbol(''), 'strings are not symbols'); - assert.notOk(_.isSymbol(_.isSymbol), 'functions are not symbols'); + assert.ok(!_.isSymbol(0), 'numbers are not symbols'); + assert.ok(!_.isSymbol(''), 'strings are not symbols'); + assert.ok(!_.isSymbol(_.isSymbol), 'functions are not symbols'); if (typeof Symbol === 'function') { assert.ok(_.isSymbol(Symbol()), 'symbols are symbols'); assert.ok(_.isSymbol(Symbol('description')), 'described symbols are symbols'); @@ -664,43 +664,43 @@ }); QUnit.test('isNumber', function(assert) { - assert.notOk(_.isNumber('string'), 'a string is not a number'); - assert.notOk(_.isNumber(arguments), 'the arguments object is not a number'); - assert.notOk(_.isNumber(void 0), 'undefined is not a number'); + assert.ok(!_.isNumber('string'), 'a string is not a number'); + assert.ok(!_.isNumber(arguments), 'the arguments object is not a number'); + assert.ok(!_.isNumber(void 0), 'undefined is not a number'); assert.ok(_.isNumber(3 * 4 - 7 / 10), 'but numbers are'); assert.ok(_.isNumber(NaN), 'NaN *is* a number'); assert.ok(_.isNumber(Infinity), 'Infinity is a number'); - assert.notOk(_.isNumber('1'), 'numeric strings are not numbers'); + assert.ok(!_.isNumber('1'), 'numeric strings are not numbers'); }); QUnit.test('isBoolean', function(assert) { - assert.notOk(_.isBoolean(2), 'a number is not a boolean'); - assert.notOk(_.isBoolean('string'), 'a string is not a boolean'); - assert.notOk(_.isBoolean('false'), 'the string "false" is not a boolean'); - assert.notOk(_.isBoolean('true'), 'the string "true" is not a boolean'); - assert.notOk(_.isBoolean(arguments), 'the arguments object is not a boolean'); - assert.notOk(_.isBoolean(void 0), 'undefined is not a boolean'); - assert.notOk(_.isBoolean(NaN), 'NaN is not a boolean'); - assert.notOk(_.isBoolean(null), 'null is not a boolean'); + assert.ok(!_.isBoolean(2), 'a number is not a boolean'); + assert.ok(!_.isBoolean('string'), 'a string is not a boolean'); + assert.ok(!_.isBoolean('false'), 'the string "false" is not a boolean'); + assert.ok(!_.isBoolean('true'), 'the string "true" is not a boolean'); + assert.ok(!_.isBoolean(arguments), 'the arguments object is not a boolean'); + assert.ok(!_.isBoolean(void 0), 'undefined is not a boolean'); + assert.ok(!_.isBoolean(NaN), 'NaN is not a boolean'); + assert.ok(!_.isBoolean(null), 'null is not a boolean'); assert.ok(_.isBoolean(true), 'but true is'); assert.ok(_.isBoolean(false), 'and so is false'); }); QUnit.test('isMap', function(assert) { - assert.notOk(_.isMap('string'), 'a string is not a map'); - assert.notOk(_.isMap(2), 'a number is not a map'); - assert.notOk(_.isMap({}), 'an object is not a map'); - assert.notOk(_.isMap(false), 'a boolean is not a map'); - assert.notOk(_.isMap(void 0), 'undefined is not a map'); - assert.notOk(_.isMap([1, 2, 3]), 'an array is not a map'); + assert.ok(!_.isMap('string'), 'a string is not a map'); + assert.ok(!_.isMap(2), 'a number is not a map'); + assert.ok(!_.isMap({}), 'an object is not a map'); + assert.ok(!_.isMap(false), 'a boolean is not a map'); + assert.ok(!_.isMap(void 0), 'undefined is not a map'); + assert.ok(!_.isMap([1, 2, 3]), 'an array is not a map'); if (typeof Set === 'function') { - assert.notOk(_.isMap(new Set()), 'a set is not a map'); + assert.ok(!_.isMap(new Set()), 'a set is not a map'); } if (typeof WeakSet === 'function') { - assert.notOk(_.isMap(new WeakSet()), 'a weakset is not a map'); + assert.ok(!_.isMap(new WeakSet()), 'a weakset is not a map'); } if (typeof WeakMap === 'function') { - assert.notOk(_.isMap(new WeakMap()), 'a weakmap is not a map'); + assert.ok(!_.isMap(new WeakMap()), 'a weakmap is not a map'); } if (typeof Map === 'function') { var keyString = 'a string'; @@ -711,20 +711,20 @@ }); QUnit.test('isWeakMap', function(assert) { - assert.notOk(_.isWeakMap('string'), 'a string is not a weakmap'); - assert.notOk(_.isWeakMap(2), 'a number is not a weakmap'); - assert.notOk(_.isWeakMap({}), 'an object is not a weakmap'); - assert.notOk(_.isWeakMap(false), 'a boolean is not a weakmap'); - assert.notOk(_.isWeakMap(void 0), 'undefined is not a weakmap'); - assert.notOk(_.isWeakMap([1, 2, 3]), 'an array is not a weakmap'); + assert.ok(!_.isWeakMap('string'), 'a string is not a weakmap'); + assert.ok(!_.isWeakMap(2), 'a number is not a weakmap'); + assert.ok(!_.isWeakMap({}), 'an object is not a weakmap'); + assert.ok(!_.isWeakMap(false), 'a boolean is not a weakmap'); + assert.ok(!_.isWeakMap(void 0), 'undefined is not a weakmap'); + assert.ok(!_.isWeakMap([1, 2, 3]), 'an array is not a weakmap'); if (typeof Set === 'function') { - assert.notOk(_.isWeakMap(new Set()), 'a set is not a weakmap'); + assert.ok(!_.isWeakMap(new Set()), 'a set is not a weakmap'); } if (typeof WeakSet === 'function') { - assert.notOk(_.isWeakMap(new WeakSet()), 'a weakset is not a weakmap'); + assert.ok(!_.isWeakMap(new WeakSet()), 'a weakset is not a weakmap'); } if (typeof Map === 'function') { - assert.notOk(_.isWeakMap(new Map()), 'a map is not a weakmap'); + assert.ok(!_.isWeakMap(new Map()), 'a map is not a weakmap'); } if (typeof WeakMap === 'function') { var keyObj = {}, obj = new WeakMap(); @@ -734,20 +734,20 @@ }); QUnit.test('isSet', function(assert) { - assert.notOk(_.isSet('string'), 'a string is not a set'); - assert.notOk(_.isSet(2), 'a number is not a set'); - assert.notOk(_.isSet({}), 'an object is not a set'); - assert.notOk(_.isSet(false), 'a boolean is not a set'); - assert.notOk(_.isSet(void 0), 'undefined is not a set'); - assert.notOk(_.isSet([1, 2, 3]), 'an array is not a set'); + assert.ok(!_.isSet('string'), 'a string is not a set'); + assert.ok(!_.isSet(2), 'a number is not a set'); + assert.ok(!_.isSet({}), 'an object is not a set'); + assert.ok(!_.isSet(false), 'a boolean is not a set'); + assert.ok(!_.isSet(void 0), 'undefined is not a set'); + assert.ok(!_.isSet([1, 2, 3]), 'an array is not a set'); if (typeof Map === 'function') { - assert.notOk(_.isSet(new Map()), 'a map is not a set'); + assert.ok(!_.isSet(new Map()), 'a map is not a set'); } if (typeof WeakMap === 'function') { - assert.notOk(_.isSet(new WeakMap()), 'a weakmap is not a set'); + assert.ok(!_.isSet(new WeakMap()), 'a weakmap is not a set'); } if (typeof WeakSet === 'function') { - assert.notOk(_.isSet(new WeakSet()), 'a weakset is not a set'); + assert.ok(!_.isSet(new WeakSet()), 'a weakset is not a set'); } if (typeof Set === 'function') { var obj = new Set(); @@ -758,20 +758,20 @@ QUnit.test('isWeakSet', function(assert) { - assert.notOk(_.isWeakSet('string'), 'a string is not a weakset'); - assert.notOk(_.isWeakSet(2), 'a number is not a weakset'); - assert.notOk(_.isWeakSet({}), 'an object is not a weakset'); - assert.notOk(_.isWeakSet(false), 'a boolean is not a weakset'); - assert.notOk(_.isWeakSet(void 0), 'undefined is not a weakset'); - assert.notOk(_.isWeakSet([1, 2, 3]), 'an array is not a weakset'); + assert.ok(!_.isWeakSet('string'), 'a string is not a weakset'); + assert.ok(!_.isWeakSet(2), 'a number is not a weakset'); + assert.ok(!_.isWeakSet({}), 'an object is not a weakset'); + assert.ok(!_.isWeakSet(false), 'a boolean is not a weakset'); + assert.ok(!_.isWeakSet(void 0), 'undefined is not a weakset'); + assert.ok(!_.isWeakSet([1, 2, 3]), 'an array is not a weakset'); if (typeof Map === 'function') { - assert.notOk(_.isWeakSet(new Map()), 'a map is not a weakset'); + assert.ok(!_.isWeakSet(new Map()), 'a map is not a weakset'); } if (typeof WeakMap === 'function') { - assert.notOk(_.isWeakSet(new WeakMap()), 'a weakmap is not a weakset'); + assert.ok(!_.isWeakSet(new WeakMap()), 'a weakmap is not a weakset'); } if (typeof Set === 'function') { - assert.notOk(_.isWeakSet(new Set()), 'a set is not a weakset'); + assert.ok(!_.isWeakSet(new Set()), 'a set is not a weakset'); } if (typeof WeakSet === 'function') { var obj = new WeakSet(); @@ -781,19 +781,19 @@ }); QUnit.test('isFunction', function(assert) { - assert.notOk(_.isFunction(void 0), 'undefined vars are not functions'); - assert.notOk(_.isFunction([1, 2, 3]), 'arrays are not functions'); - assert.notOk(_.isFunction('moe'), 'strings are not functions'); + assert.ok(!_.isFunction(void 0), 'undefined vars are not functions'); + assert.ok(!_.isFunction([1, 2, 3]), 'arrays are not functions'); + assert.ok(!_.isFunction('moe'), 'strings are not functions'); assert.ok(_.isFunction(_.isFunction), 'but functions are'); assert.ok(_.isFunction(function(){}), 'even anonymous ones'); if (testElement) { - assert.notOk(_.isFunction(testElement), 'elements are not functions'); + assert.ok(!_.isFunction(testElement), 'elements are not functions'); } var nodelist = typeof document != 'undefined' && document.childNodes; if (nodelist) { - assert.notOk(_.isFunction(nodelist)); + assert.ok(!_.isFunction(nodelist)); } }); @@ -811,68 +811,68 @@ } QUnit.test('isDate', function(assert) { - assert.notOk(_.isDate(100), 'numbers are not dates'); - assert.notOk(_.isDate({}), 'objects are not dates'); + assert.ok(!_.isDate(100), 'numbers are not dates'); + assert.ok(!_.isDate({}), 'objects are not dates'); assert.ok(_.isDate(new Date()), 'but dates are'); }); QUnit.test('isRegExp', function(assert) { - assert.notOk(_.isRegExp(_.identity), 'functions are not RegExps'); + assert.ok(!_.isRegExp(_.identity), 'functions are not RegExps'); assert.ok(_.isRegExp(/identity/), 'but RegExps are'); }); QUnit.test('isFinite', function(assert) { - assert.notOk(_.isFinite(void 0), 'undefined is not finite'); - assert.notOk(_.isFinite(null), 'null is not finite'); - assert.notOk(_.isFinite(NaN), 'NaN is not finite'); - assert.notOk(_.isFinite(Infinity), 'Infinity is not finite'); - assert.notOk(_.isFinite(-Infinity), '-Infinity is not finite'); + assert.ok(!_.isFinite(void 0), 'undefined is not finite'); + assert.ok(!_.isFinite(null), 'null is not finite'); + assert.ok(!_.isFinite(NaN), 'NaN is not finite'); + assert.ok(!_.isFinite(Infinity), 'Infinity is not finite'); + assert.ok(!_.isFinite(-Infinity), '-Infinity is not finite'); assert.ok(_.isFinite('12'), 'Numeric strings are numbers'); - assert.notOk(_.isFinite('1a'), 'Non numeric strings are not numbers'); - assert.notOk(_.isFinite(''), 'Empty strings are not numbers'); + assert.ok(!_.isFinite('1a'), 'Non numeric strings are not numbers'); + assert.ok(!_.isFinite(''), 'Empty strings are not numbers'); var obj = new Number(5); assert.ok(_.isFinite(obj), 'Number instances can be finite'); assert.ok(_.isFinite(0), '0 is finite'); assert.ok(_.isFinite(123), 'Ints are finite'); assert.ok(_.isFinite(-12.44), 'Floats are finite'); if (typeof Symbol === 'function') { - assert.notOk(_.isFinite(Symbol()), 'symbols are not numbers'); - assert.notOk(_.isFinite(Symbol('description')), 'described symbols are not numbers'); - assert.notOk(_.isFinite(Object(Symbol())), 'boxed symbols are not numbers'); + assert.ok(!_.isFinite(Symbol()), 'symbols are not numbers'); + assert.ok(!_.isFinite(Symbol('description')), 'described symbols are not numbers'); + assert.ok(!_.isFinite(Object(Symbol())), 'boxed symbols are not numbers'); } }); QUnit.test('isNaN', function(assert) { - assert.notOk(_.isNaN(void 0), 'undefined is not NaN'); - assert.notOk(_.isNaN(null), 'null is not NaN'); - assert.notOk(_.isNaN(0), '0 is not NaN'); - assert.notOk(_.isNaN(new Number(0)), 'wrapped 0 is not NaN'); + assert.ok(!_.isNaN(void 0), 'undefined is not NaN'); + assert.ok(!_.isNaN(null), 'null is not NaN'); + assert.ok(!_.isNaN(0), '0 is not NaN'); + assert.ok(!_.isNaN(new Number(0)), 'wrapped 0 is not NaN'); assert.ok(_.isNaN(NaN), 'but NaN is'); assert.ok(_.isNaN(new Number(NaN)), 'wrapped NaN is still NaN'); if (typeof Symbol !== 'undefined'){ - assert.notOk(_.isNaN(Symbol()), 'symbol is not NaN'); + assert.ok(!_.isNaN(Symbol()), 'symbol is not NaN'); } }); QUnit.test('isNull', function(assert) { - assert.notOk(_.isNull(void 0), 'undefined is not null'); - assert.notOk(_.isNull(NaN), 'NaN is not null'); + assert.ok(!_.isNull(void 0), 'undefined is not null'); + assert.ok(!_.isNull(NaN), 'NaN is not null'); assert.ok(_.isNull(null), 'but null is'); }); QUnit.test('isUndefined', function(assert) { - assert.notOk(_.isUndefined(1), 'numbers are defined'); - assert.notOk(_.isUndefined(null), 'null is defined'); - assert.notOk(_.isUndefined(false), 'false is defined'); - assert.notOk(_.isUndefined(NaN), 'NaN is defined'); + assert.ok(!_.isUndefined(1), 'numbers are defined'); + assert.ok(!_.isUndefined(null), 'null is defined'); + assert.ok(!_.isUndefined(false), 'false is defined'); + assert.ok(!_.isUndefined(NaN), 'NaN is defined'); assert.ok(_.isUndefined(), 'nothing is undefined'); assert.ok(_.isUndefined(void 0), 'undefined is undefined'); }); QUnit.test('isError', function(assert) { - assert.notOk(_.isError(1), 'numbers are not Errors'); - assert.notOk(_.isError(null), 'null is not an Error'); - assert.notOk(_.isError(Error), 'functions are not Errors'); + assert.ok(!_.isError(1), 'numbers are not Errors'); + assert.ok(!_.isError(null), 'null is not an Error'); + assert.ok(!_.isError(Error), 'functions are not Errors'); assert.ok(_.isError(new Error()), 'Errors are Errors'); assert.ok(_.isError(new EvalError()), 'EvalErrors are Errors'); assert.ok(_.isError(new RangeError()), 'RangeErrors are Errors'); @@ -901,19 +901,19 @@ QUnit.test('has', function(assert) { var obj = {foo: 'bar', func: function(){}}; assert.ok(_.has(obj, 'foo'), 'checks that the object has a property.'); - assert.notOk(_.has(obj, 'baz'), "returns false if the object doesn't have the property."); + assert.ok(!_.has(obj, 'baz'), "returns false if the object doesn't have the property."); assert.ok(_.has(obj, 'func'), 'works for functions too.'); obj.hasOwnProperty = null; assert.ok(_.has(obj, 'foo'), 'works even when the hasOwnProperty method is deleted.'); function Child() {} Child.prototype = obj; var child = new Child(); - assert.notOk(_.has(child, 'foo'), 'does not check the prototype chain for a property.'); + assert.ok(!_.has(child, 'foo'), 'does not check the prototype chain for a property.'); assert.strictEqual(_.has(null, 'foo'), false, 'returns false for null'); assert.strictEqual(_.has(void 0, 'foo'), false, 'returns false for undefined'); assert.ok(_.has({a: {b: 'foo'}}, ['a', 'b']), 'can check for nested properties.'); - assert.notOk(_.has({a: child}, ['a', 'foo']), 'does not check the prototype of nested props.'); + assert.ok(!_.has({a: child}, ['a', 'foo']), 'does not check the prototype of nested props.'); }); QUnit.test('property', function(assert) { diff --git a/test/utility.js b/test/utility.js index 23289afd9..b4030c198 100644 --- a/test/utility.js +++ b/test/utility.js @@ -402,9 +402,9 @@ }); QUnit.test('#547 - _.templateSettings is unchanged by custom settings.', function(assert) { - assert.notOk(_.templateSettings.variable); + assert.ok(!_.templateSettings.variable); _.template('', {}, {variable: 'x'}); - assert.notOk(_.templateSettings.variable); + assert.ok(!_.templateSettings.variable); }); QUnit.test('#556 - undefined template variables.', function(assert) { @@ -429,11 +429,11 @@ assert.expect(2); var count = 0; var template = _.template('<%= f() %>'); - template({f: function(){ assert.notOk(count++); }}); + template({f: function(){ assert.ok(!count++); }}); var countEscaped = 0; var templateEscaped = _.template('<%- f() %>'); - templateEscaped({f: function(){ assert.notOk(countEscaped++); }}); + templateEscaped({f: function(){ assert.ok(!countEscaped++); }}); }); QUnit.test('#746 - _.template settings are not modified.', function(assert) { diff --git a/test/vendor/qunit-extras.js b/test/vendor/qunit-extras.js deleted file mode 100644 index 663d32489..000000000 --- a/test/vendor/qunit-extras.js +++ /dev/null @@ -1,776 +0,0 @@ -/*! - * QUnit Extras v1.4.1 - * Copyright 2011-2015 John-David Dalton - * Based on a gist by Jörn Zaefferer - * Available under MIT license - */ -;(function() { - - /** Used as a safe reference for `undefined` in pre ES5 environments. */ - var undefined; - - /** Used as a horizontal rule in console output. */ - var hr = '----------------------------------------'; - - /** Used for native method references. */ - var arrayProto = Array.prototype; - - /** Native method shortcut. */ - var push = arrayProto.push, - unshift = arrayProto.unshift; - - /** Used to match HTML entities. */ - var reEscapedHtml = /(&|<|>|"|')/g; - - /** Used to match parts of the assert message. */ - var reDied = /^Died on test #\d+/, - reMessage = /^([\s\S]*?)<\/span>/; - - /** Used to associate color names with their corresponding codes. */ - var ansiCodes = { - 'bold': 1, - 'green': 32, - 'magenta': 35, - 'red': 31 - }; - - /** Used to convert HTML entities to characters. */ - var htmlUnescapes = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - ''': "'" - }; - - /** Used to determine if values are of the language type Object. */ - var objectTypes = { - 'function': true, - 'object': true - }; - - /** Used as a reference to the global object. */ - var root = (objectTypes[typeof window] && window) || this; - - /** Detect free variable `exports`. */ - var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; - - /** Detect free variable `module`. */ - var freeModule = objectTypes[typeof module] && module && !module.nodeType && module; - - /** Detect free variable `global` from Node.js or Browserified code and use it as `root`. */ - var freeGlobal = freeExports && freeModule && typeof global == 'object' && global; - if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal || freeGlobal.self === freeGlobal)) { - root = freeGlobal; - } - - /*--------------------------------------------------------------------------*/ - - /** - * Checks if a given value is present in an array using strict equality - * for comparisons, i.e. `===`. - * - * @private - * @param {Array} array The array to iterate over. - * @param {*} value The value to check for. - * @returns {boolean} Returns `true` if the `value` is found, else `false`. - */ - function contains(array, value) { - var index = -1, - length = array ? array.length : 0; - - while (++index < length) { - if (array[index] === value) { - return true; - } - } - return false; - } - - /** - * Checks if `value` is the language type of `Object`. - * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an object, else `false`. - */ - function isObject(value) { - var type = typeof value; - return type == 'function' || (value && type == 'object') || false; - } - - /** - * Creates a string with `text` repeated `n` number of times. - * - * @private - * @param {string} text The text to repeat. - * @param {number} n The number of times to repeat `text`. - * @returns {string} The created string. - */ - function repeat(text, n) { - return Array(n + 1).join(text); - } - - /** - * Resolves the value of property `key` on `object`. - * - * @private - * @param {Object} object The object to inspect. - * @param {string} key The name of the property to resolve. - * @returns {*} Returns the resolved value. - */ - function result(object, key) { - return object == null ? undefined : object[key]; - } - - /** - * Converts the HTML entities `&`, `<`, `>`, `"`, and `'` - * in `string` to their corresponding characters. - * - * @private - * @param {string} string The string to unescape. - * @returns {string} Returns the unescaped string. - */ - function unescape(string) { - return string == null ? '' : String(string).replace(reEscapedHtml, unescapeHtmlChar); - } - - /** - * Used by `unescape` to convert HTML entities to characters. - * - * @private - * @param {string} match The matched character to unescape. - * @returns {string} Returns the unescaped character. - */ - function unescapeHtmlChar(match) { - return htmlUnescapes[match]; - } - - /** - * Creates a function that provides `value` to the wrapper function as its - * first argument. Additional arguments provided to the function are appended - * to those provided to the wrapper function. The wrapper is executed with - * the `this` binding of the created function. - * - * @private - * @param {*} value The value to wrap. - * @param {Function} wrapper The wrapper function. - * @returns {Function} Returns the new function. - */ - function wrap(value, wrapper) { - return function() { - var args = [value]; - push.apply(args, arguments); - return wrapper.apply(this, args); - }; - } - - /*--------------------------------------------------------------------------*/ - - /** - * Installs the QUnit additions on the given `context` object. - * - * @memberOf exports - * @param {Object} context The context object. - */ - function runInContext(context) { - - /** Object references. */ - var phantom = context.phantom, - define = context.define, - document = !phantom && context.document, - process = phantom || context.process, - amd = define && define.amd, - console = context.console, - java = !document && context.java, - print = context.print, - require = context.require; - - /** Detects if running on Node.js. */ - var isNode = isObject(process) && typeof process.on == 'function'; - - /** Detects if running in a PhantomJS web page. */ - var isPhantomPage = typeof context.callPhantom == 'function'; - - /** Detects if QUnit Extras should log to the console. */ - var isSilent = document && !isPhantomPage; - - /** Used to indicate if running in Windows. */ - var isWindows = isNode && process.platform == 'win32'; - - /** Used to indicate if ANSI escape codes are supported. */ - var isAnsiSupported = (function() { - if (isNode && process.stdout && !process.stdout.isTTY) { - return false; - } - if (isWindows || getEnv('COLORTERM')) { - return true; - } - return /^(?:ansi|cygwin|linux|screen|xterm|vt100)$|color/i.test(getEnv('TERM')); - }()); - - /** Used to display the wait throbber. */ - var throbberDelay = 500, - waitCount = -1; - - /** Shorten `context.QUnit.QUnit` to `context.QUnit`. */ - var QUnit = context.QUnit = context.QUnit.QUnit || context.QUnit; - - /*------------------------------------------------------------------------*/ - - /** - * Schedules timer-based callbacks. - * - * @private - * @param {Function|string} fn The function to call. - * @param {number} delay The number of milliseconds to delay the `fn` call. - * @param {Array} args Arguments to invoke `fn` with. - * @param {boolean} repeated A flag to specify whether `fn` is called repeatedly. - * @returns {number} The ID of the timeout. - */ - function schedule(fn, delay, args, repeated) { - // Rhino 1.7RC4 will error assigning `task` below. - // See https://bugzilla.mozilla.org/show_bug.cgi?id=775566. - var task = ids[++counter] = new JavaAdapter(java.util.TimerTask, { - 'run': function() { - fn.apply(context, args); - } - }); - // Support non-functions. - if (typeof fn != 'function') { - fn = (function(code) { - code = String(code); - return function() { eval(code); }; - }(fn)); - } - // Used by `setInterval`. - if (repeated) { - timer.schedule(task, delay, delay); - } - // Used by `setTimeout`. - else { - timer.schedule(task, delay); - } - return counter; - } - - /** - * Clears the delay set by `setInterval` or `setTimeout`. - * - * @memberOf context - * @param {number} id The ID of the timeout to be cleared. - */ - function clearTimer(id) { - if (ids[id]) { - ids[id].cancel(); - timer.purge(); - delete ids[id]; - } - } - - /** - * Executes a code snippet or function repeatedly, with a delay between each call. - * - * @memberOf context - * @param {Function|string} fn The function to call or string to evaluate. - * @param {number} delay The number of milliseconds to delay each `fn` call. - * @param {...*} [args] Arguments to invoke `fn` with. - * @returns {number} The ID of the timeout. - */ - function setInterval(fn, delay) { - return schedule(fn, delay, slice.call(arguments, 2), true); - } - - /** - * Executes a code snippet or a function after specified delay. - * - * @memberOf context - * @param {Function|string} fn The function to call or string to evaluate. - * @param {number} delay The number of milliseconds to delay the `fn` call. - * @param {...*} [args] Arguments to invoke `fn` with. - * @returns {number} The ID of the timeout. - */ - function setTimeout(fn, delay) { - return schedule(fn, delay, slice.call(arguments, 2)); - } - - /*------------------------------------------------------------------------*/ - - /** - * Gets the environment variable value by a given name. - * - * @private - * @param {string} name The name of the environment variable to get. - * @returns {*} Returns the environment variable value. - */ - function getEnv(name) { - if (isNode) { - return process.env[name]; - } - if (java) { - return java.lang.System.getenv(name); - } - if (!amd && typeof require == 'function') { - try { - return require('system').env[name]; - } catch(e) {} - } - } - - /** - * Adds text color to the terminal output of `string`. - * - * @private - * @param {string} colorName The name of the color to add. - * @param {string} string The string to add colors to. - * @returns {string} Returns the colored string. - */ - function color(colorName, string) { - return isAnsiSupported - ? ('\x1B[' + ansiCodes[colorName] + 'm' + string + '\x1B[0m') - : string; - } - - /** - * Writes an inline message to standard output. - * - * @private - * @param {string} [text=''] The text to log. - */ - var logInline = (function() { - if (!isNode || isWindows) { - return function() {}; - } - // Cleanup any inline logs when exited via `ctrl+c`. - process.on('SIGINT', function() { - logInline(); - process.exit(); - }); - - var prevLine = ''; - return function(text) { - var blankLine = repeat(' ', prevLine.length); - if (text == null) { - text = ''; - } - if (text.length > hr.length) { - text = text.slice(0, hr.length - 3) + '...'; - } - prevLine = text; - process.stdout.write(text + blankLine.slice(text.length) + '\r'); - } - }()); - - /** - * Writes the wait throbber to standard output. - * - * @private - */ - function logThrobber() { - logInline('Please wait' + repeat('.', (++waitCount % 3) + 1)); - } - - /*------------------------------------------------------------------------*/ - - /** - * The number of retries async tests have to succeed. - * - * @memberOf QUnit.config - * @type number - */ - QUnit.config.asyncRetries = 0; - - /** - * An object of excused tests and assertions. - * - * @memberOf QUnit.config - * @type Object - */ - QUnit.config.excused = {}; - - /** - * An object used to hold "extras" information about the current running test. - * - * @memberOf QUnit.config - * @type Object - */ - QUnit.config.extrasData = { - - /** - * The data object for the active test module. - * - * @memberOf QUnit.config.extrasData - * @type Object - */ - 'module': {}, - - /** - * The data object for Sauce Labs. - * - * @memberOf QUnit.config.extrasData - * @type Object - */ - 'sauce': { - - /** - * An array of failed test details. - * - * @memberOf QUnit.config.extrasData.sauce - * @type Array - */ - 'tests': [] - } - }; - - /** - * Converts an object into a string representation. - * - * @memberOf QUnit - * @type Function - * @param {Object} object The object to stringify. - * @returns {string} The result string. - */ - QUnit.jsDump.parsers.object = (function() { - var func = QUnit.jsDump.parsers.object; - if (isSilent) { - return func; - } - return function(object) { - if (typeof object.rhinoException != 'object') { - return func(object); - } - return object.name + - ' { message: "' + object.message + - '", fileName: "' + object.fileName + - '", lineNumber: ' + object.lineNumber + ' }'; - }; - }()); - - /*------------------------------------------------------------------------*/ - - // Add a callback to be triggered after every assertion. - QUnit.log(function(details) { - QUnit.config.extrasData.module.logs.push(details); - }); - - // Add a callback to be triggered at the start of every test module. - QUnit.moduleStart(function(details) { - var module = QUnit.config.extrasData.module; - module.name = details.name; - module.logs = []; - module.printed = false; - }); - - // Wrap old API to intercept `expected` and `message`. - if (QUnit.push) { - QUnit.push = wrap(QUnit.push, function(push, result, actual, expected, message) { - push.call(this, result, actual, expected, message); - - var asserts = QUnit.config.current.assertions, - item = asserts[asserts.length - 1]; - - item.expected = QUnit.jsDump.parse(expected); - item.text = message; - }); - } - // Wrap old API to intercept `message`. - if (QUnit.pushFailure) { - QUnit.pushFailure = wrap(QUnit.pushFailure, function(pushFailure, message, source, actual) { - pushFailure.call(this, message, source, actual); - - var asserts = QUnit.config.current.assertions, - item = asserts[asserts.length - 1]; - - item.expected = ''; - item.text = message; - }); - } - // Wrap to flag tests using `assert.async`. - if (QUnit.assert.async) { - QUnit.assert.async = wrap(QUnit.assert.async, function(async) { - this.test.usesAsync = true; - return async.call(this); - }); - } - // Add a callback to be triggered at the start of every test. - QUnit.testStart(function(details) { - var config = QUnit.config, - test = config.current; - - var excused = config.excused || {}, - excusedTests = excused[details.module], - excusedAsserts = excusedTests && excusedTests[details.name]; - - // Allow async tests to retry. - if (!test.retries) { - test.retries = 0; - test.finish = wrap(test.finish, function(finish) { - if (this.async || this.usesAsync) { - var asserts = this.assertions, - config = QUnit.config, - index = -1, - length = asserts.length, - logs = config.extrasData.module.logs, - queue = config.queue; - - while (++index < length) { - var assert = asserts[index]; - if (!assert.result && this.retries < config.asyncRetries) { - var oldLength = queue.length; - logs.length -= asserts.length; - asserts.length = 0; - - this.retries++; - this.queue(); - - unshift.apply(queue, queue.splice(oldLength, queue.length - oldLength)); - return; - } - } - } - finish.call(this); - }); - } - // Exit early when there is nothing to excuse. - if (!excusedAsserts) { - return; - } - // Excuse the entire test. - if (excusedAsserts === true) { - test.async = test.usesAsync = false; - test.callback = function() {}; - test.expected = 0; - return; - } - // Wrap to intercept `expected` and `message`. - if (test.push) { - test.push = wrap(test.push, function(push, result, actual, expected, message) { - push.call(this, result, actual, expected, message); - - var item = this.assertions[this.assertions.length - 1]; - item.expected = QUnit.jsDump.parse(expected); - item.text = message; - }); - } - // Wrap to intercept `message`. - if (test.pushFailure) { - test.pushFailure = wrap(test.pushFailure, function(pushFailure, message, source, actual) { - pushFailure.call(this, message, source, actual); - - var item = this.assertions[this.assertions.length - 1]; - item.expected = ''; - item.text = message; - }); - } - // Wrap to excuse specific assertions. - test.finish = wrap(test.finish, function(finish) { - var asserts = this.assertions, - config = QUnit.config, - expected = this.expected, - items = asserts.slice(), - length = items.length; - - if (expected == null) { - if (config.requireExpects) { - expected = length; - items.push('Expected number of assertions to be defined, but expect() was not called.'); - } else if (!length) { - expected = 1; - items.push('Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.'); - } - } else if (expected != length) { - items.push('Expected ' + expected + ' assertions, but ' + length + ' were run'); - } - var index = -1; - length = items.length; - - while (++index < length) { - var assert = items[index], - isStr = typeof assert == 'string'; - - var assertMessage = isStr ? assert : assert.text || unescape(result(reMessage.exec(assert.message), 1)), - assertValue = isStr ? assert : assert.expected, - assertDied = result(reDied.exec(assertMessage), 0); - - if ((assertMessage && contains(excusedAsserts, assertMessage)) || - (assertDied && contains(excusedAsserts, assertDied)) || - (assertValue && ( - contains(excusedAsserts, assertValue) || - contains(excusedAsserts, assertValue.replace(/\s+/g, '')) - ))) { - if (isStr) { - while (asserts.length < expected) { - asserts.push({ 'result': true }); - } - asserts.length = expected; - } - else { - assert.result = true; - } - } - } - finish.call(this); - }); - }); - - // Add a callback to be triggered after a test is completed. - QUnit.testDone(function(details) { - var config = QUnit.config, - data = config.extrasData, - failures = details.failed, - hidepassed = config.hidepassed, - module = data.module, - moduleLogs = module.logs, - sauceTests = data.sauce.tests; - - if (hidepassed && !failures) { - return; - } - if (!isSilent) { - logInline(); - if (!module.printed) { - module.printed = true; - console.log(hr); - console.log(color('bold', module.name)); - console.log(hr); - } - console.log(' ' + (failures ? color('red', 'FAIL') : color('green', 'PASS')) + ' - ' + details.name); - } - if (!failures) { - return; - } - var index = -1, - length = moduleLogs.length; - - while(++index < length) { - var entry = moduleLogs[index]; - if (hidepassed && entry.result) { - continue; - } - var expected = entry.expected, - result = entry.result, - type = typeof expected != 'undefined' ? 'EQ' : 'OK'; - - var message = [ - result ? color('green', 'PASS') : color('red', 'FAIL'), - type, - entry.message || 'ok' - ]; - - if (!result && type == 'EQ') { - message.push(color('magenta', 'Expected: ' + expected + ', Actual: ' + entry.actual)); - } - if (!isSilent) { - console.log(' ' + message.join(' | ')); - } - if (!entry.result) { - sauceTests.push(entry); - } - } - }); - - // Add a callback to be triggered when all testing has completed. - QUnit.done(function(details) { - var failures = details.failed, - statusColor = failures ? 'magenta' : 'green'; - - if (!isSilent) { - logInline(); - console.log(hr); - console.log(color(statusColor, ' PASS: ' + details.passed + ' FAIL: ' + failures + ' TOTAL: ' + details.total)); - console.log(color(statusColor, ' Finished in ' + details.runtime + ' milliseconds.')); - console.log(hr); - } - // Exit out of Node.js or PhantomJS. - try { - if (failures) { - process.exit(1); - } else { - process.exit(0); - } - } catch(e) {} - - // Exit out of Narwhal, Rhino, or RingoJS. - try { - if (failures) { - java.lang.System.exit(1); - } else { - quit(); - } - } catch(e) {} - - // Assign results to `global_test_results` for Sauce Labs. - details.tests = QUnit.config.extrasData.sauce.tests; - context.global_test_results = details; - }); - - /*------------------------------------------------------------------------*/ - - // Replace poisoned `raises` method. - context.raises = QUnit.raises = QUnit['throws'] || QUnit.raises; - - // Add CLI extras. - if (!document) { - // Timeout fallbacks based on the work of Andrea Giammarchi and Weston C. - // See https://github.com/WebReflection/wru/blob/master/src/rhinoTimers.js - // and http://stackoverflow.com/questions/2261705/how-to-run-a-javascript-function-asynchronously-without-using-settimeout. - try { - var counter = 0, - ids = {}, - slice = Array.prototype.slice, - timer = new java.util.Timer; - - (function() { - var getDescriptor = Object.getOwnPropertyDescriptor || function() { - return { 'writable': true }; - }; - - var descriptor; - if ((!context.clearInterval || ((descriptor = getDescriptor(context, 'clearInterval')) && (descriptor.writable || descriptor.set))) && - (!context.setInterval || ((descriptor = getDescriptor(context, 'setInterval')) && (descriptor.writable || descriptor.set)))) { - context.clearInterval = clearTimer; - context.setInterval = setInterval; - } - if ((!context.clearTimeout || ((descriptor = getDescriptor(context, 'clearTimeout')) && (descriptor.writable || descriptor.set))) && - (!context.setTimeout || ((descriptor = getDescriptor(context, 'setTimeout')) && (descriptor.writable || descriptor.set)))) { - context.clearTimeout = clearTimer; - context.setTimeout = setTimeout; - } - }()); - } catch(e) {} - - // Expose QUnit API on `context`. - // Exclude `module` because some environments have it as a built-in object. - ('asyncTest deepEqual equal equals expect notDeepEqual notEqual notStrictEqual ' + - 'ok raises same start stop strictEqual test throws').replace(/\S+/g, function(methodName) { - context[methodName] = QUnit[methodName]; - }); - - // Add `console.log` support to Narwhal, Rhino, and RingoJS. - if (!console) { - console = context.console = { 'log': function() {} }; - } - // RingoJS removes ANSI escape codes in `console.log`, but not in `print`. - if (java && typeof print == 'function') { - console.log = print; - } - // Start log throbber. - if (!isSilent) { - context.setInterval(logThrobber, throbberDelay); - } - // Must call `QUnit.start` in the test file if not loaded in a browser. - QUnit.config.autostart = false; - QUnit.init(); - } - } - - /*--------------------------------------------------------------------------*/ - - // Export QUnit Extras. - if (freeExports) { - freeExports.runInContext = runInContext; - } else { - runInContext(root); - } -}.call(this)); \ No newline at end of file diff --git a/test/vendor/qunit.js b/test/vendor/qunit.js index f3542ca9d..d7b22ddf8 100755 --- a/test/vendor/qunit.js +++ b/test/vendor/qunit.js @@ -1,3828 +1,5264 @@ /*! - * QUnit 1.18.0 - * http://qunitjs.com/ + * QUnit 2.6.0 + * https://qunitjs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license - * http://jquery.org/license + * https://jquery.org/license * - * Date: 2015-04-03T10:23Z + * Date: 2018-03-27T02:18Z */ +(function (global$1) { + 'use strict'; -(function( window ) { - -var QUnit, - config, - onErrorFnPrev, - loggingCallbacks = {}, - fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ), - toString = Object.prototype.toString, - hasOwn = Object.prototype.hasOwnProperty, - // Keep a local reference to Date (GH-283) - Date = window.Date, - now = Date.now || function() { - return new Date().getTime(); - }, - globalStartCalled = false, - runStarted = false, - setTimeout = window.setTimeout, - clearTimeout = window.clearTimeout, - defined = { - document: window.document !== undefined, - setTimeout: window.setTimeout !== undefined, - sessionStorage: (function() { - var x = "qunit-test-string"; - try { - sessionStorage.setItem( x, x ); - sessionStorage.removeItem( x ); - return true; - } catch ( e ) { - return false; - } - }()) - }, - /** - * Provides a normalized error string, correcting an issue - * with IE 7 (and prior) where Error.prototype.toString is - * not properly implemented - * - * Based on http://es5.github.com/#x15.11.4.4 - * - * @param {String|Error} error - * @return {String} error message - */ - errorString = function( error ) { - var name, message, - errorString = error.toString(); - if ( errorString.substring( 0, 7 ) === "[object" ) { - name = error.name ? error.name.toString() : "Error"; - message = error.message ? error.message.toString() : ""; - if ( name && message ) { - return name + ": " + message; - } else if ( name ) { - return name; - } else if ( message ) { - return message; - } else { - return "Error"; - } - } else { - return errorString; - } - }, - /** - * Makes a clone of an object using only Array or Object as base, - * and copies over the own enumerable properties. - * - * @param {Object} obj - * @return {Object} New object with only the own properties (recursively). - */ - objectValues = function( obj ) { - var key, val, - vals = QUnit.is( "array", obj ) ? [] : {}; - for ( key in obj ) { - if ( hasOwn.call( obj, key ) ) { - val = obj[ key ]; - vals[ key ] = val === Object( val ) ? objectValues( val ) : val; - } - } - return vals; - }; - -QUnit = {}; - -/** - * Config object: Maintain internal state - * Later exposed as QUnit.config - * `config` initialized at top of scope - */ -config = { - // The queue of tests to run - queue: [], - - // block until document ready - blocking: true, - - // by default, run previously failed tests first - // very useful in combination with "Hide passed tests" checked - reorder: true, - - // by default, modify document.title when suite is done - altertitle: true, - - // by default, scroll to top of the page when suite is done - scrolltop: true, - - // when enabled, all tests must call expect() - requireExpects: false, - - // depth up-to which object will be dumped - maxDepth: 5, - - // add checkboxes that are persisted in the query-string - // when enabled, the id is set to `true` as a `QUnit.config` property - urlConfig: [ - { - id: "hidepassed", - label: "Hide passed tests", - tooltip: "Only show tests and assertions that fail. Stored as query-strings." - }, - { - id: "noglobals", - label: "Check for Globals", - tooltip: "Enabling this will test if any test introduces new properties on the " + - "`window` object. Stored as query-strings." - }, - { - id: "notrycatch", - label: "No try-catch", - tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + - "exceptions in IE reasonable. Stored as query-strings." - } - ], - - // Set of all modules. - modules: [], - - // The first unnamed module - currentModule: { - name: "", - tests: [] - }, - - callbacks: {} -}; - -// Push a loose unnamed module to the modules collection -config.modules.push( config.currentModule ); - -// Initialize more QUnit.config and QUnit.urlParams -(function() { - var i, current, - location = window.location || { search: "", protocol: "file:" }, - params = location.search.slice( 1 ).split( "&" ), - length = params.length, - urlParams = {}; - - if ( params[ 0 ] ) { - for ( i = 0; i < length; i++ ) { - current = params[ i ].split( "=" ); - current[ 0 ] = decodeURIComponent( current[ 0 ] ); - - // allow just a key to turn on a flag, e.g., test.html?noglobals - current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; - if ( urlParams[ current[ 0 ] ] ) { - urlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] ); - } else { - urlParams[ current[ 0 ] ] = current[ 1 ]; - } - } - } - - if ( urlParams.filter === true ) { - delete urlParams.filter; - } - - QUnit.urlParams = urlParams; - - // String search anywhere in moduleName+testName - config.filter = urlParams.filter; - - if ( urlParams.maxDepth ) { - config.maxDepth = parseInt( urlParams.maxDepth, 10 ) === -1 ? - Number.POSITIVE_INFINITY : - urlParams.maxDepth; - } - - config.testId = []; - if ( urlParams.testId ) { - - // Ensure that urlParams.testId is an array - urlParams.testId = decodeURIComponent( urlParams.testId ).split( "," ); - for ( i = 0; i < urlParams.testId.length; i++ ) { - config.testId.push( urlParams.testId[ i ] ); - } - } - - // Figure out if we're running the tests from a server or not - QUnit.isLocal = location.protocol === "file:"; - - // Expose the current QUnit version - QUnit.version = "1.18.0"; -}()); - -// Root QUnit object. -// `QUnit` initialized at top of scope -extend( QUnit, { - - // call on start of module test to prepend name to all tests - module: function( name, testEnvironment ) { - var currentModule = { - name: name, - testEnvironment: testEnvironment, - tests: [] - }; - - // DEPRECATED: handles setup/teardown functions, - // beforeEach and afterEach should be used instead - if ( testEnvironment && testEnvironment.setup ) { - testEnvironment.beforeEach = testEnvironment.setup; - delete testEnvironment.setup; - } - if ( testEnvironment && testEnvironment.teardown ) { - testEnvironment.afterEach = testEnvironment.teardown; - delete testEnvironment.teardown; - } - - config.modules.push( currentModule ); - config.currentModule = currentModule; - }, - - // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0. - asyncTest: function( testName, expected, callback ) { - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - - QUnit.test( testName, expected, callback, true ); - }, - - test: function( testName, expected, callback, async ) { - var test; - - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - - test = new Test({ - testName: testName, - expected: expected, - async: async, - callback: callback - }); - - test.queue(); - }, - - skip: function( testName ) { - var test = new Test({ - testName: testName, - skip: true - }); - - test.queue(); - }, - - // DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0. - // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior. - start: function( count ) { - var globalStartAlreadyCalled = globalStartCalled; - - if ( !config.current ) { - globalStartCalled = true; - - if ( runStarted ) { - throw new Error( "Called start() outside of a test context while already started" ); - } else if ( globalStartAlreadyCalled || count > 1 ) { - throw new Error( "Called start() outside of a test context too many times" ); - } else if ( config.autostart ) { - throw new Error( "Called start() outside of a test context when " + - "QUnit.config.autostart was true" ); - } else if ( !config.pageLoaded ) { - - // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it - config.autostart = true; - return; - } - } else { - - // If a test is running, adjust its semaphore - config.current.semaphore -= count || 1; - - // Don't start until equal number of stop-calls - if ( config.current.semaphore > 0 ) { - return; - } - - // throw an Error if start is called more often than stop - if ( config.current.semaphore < 0 ) { - config.current.semaphore = 0; - - QUnit.pushFailure( - "Called start() while already started (test's semaphore was 0 already)", - sourceFromStacktrace( 2 ) - ); - return; - } - } - - resumeProcessing(); - }, - - // DEPRECATED: QUnit.stop() will be removed in QUnit 2.0. - stop: function( count ) { - - // If there isn't a test running, don't allow QUnit.stop() to be called - if ( !config.current ) { - throw new Error( "Called stop() outside of a test context" ); - } - - // If a test is running, adjust its semaphore - config.current.semaphore += count || 1; - - pauseProcessing(); - }, - - config: config, - - // Safe object type checking - is: function( type, obj ) { - return QUnit.objectType( obj ) === type; - }, - - objectType: function( obj ) { - if ( typeof obj === "undefined" ) { - return "undefined"; - } - - // Consider: typeof null === object - if ( obj === null ) { - return "null"; - } - - var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ), - type = match && match[ 1 ] || ""; - - switch ( type ) { - case "Number": - if ( isNaN( obj ) ) { - return "nan"; - } - return "number"; - case "String": - case "Boolean": - case "Array": - case "Date": - case "RegExp": - case "Function": - return type.toLowerCase(); - } - if ( typeof obj === "object" ) { - return "object"; - } - return undefined; - }, - - extend: extend, - - load: function() { - config.pageLoaded = true; - - // Initialize the configuration options - extend( config, { - stats: { all: 0, bad: 0 }, - moduleStats: { all: 0, bad: 0 }, - started: 0, - updateRate: 1000, - autostart: true, - filter: "" - }, true ); - - config.blocking = false; - - if ( config.autostart ) { - resumeProcessing(); - } - } -}); - -// Register logging callbacks -(function() { - var i, l, key, - callbacks = [ "begin", "done", "log", "testStart", "testDone", - "moduleStart", "moduleDone" ]; - - function registerLoggingCallback( key ) { - var loggingCallback = function( callback ) { - if ( QUnit.objectType( callback ) !== "function" ) { - throw new Error( - "QUnit logging methods require a callback function as their first parameters." - ); - } - - config.callbacks[ key ].push( callback ); - }; - - // DEPRECATED: This will be removed on QUnit 2.0.0+ - // Stores the registered functions allowing restoring - // at verifyLoggingCallbacks() if modified - loggingCallbacks[ key ] = loggingCallback; - - return loggingCallback; - } - - for ( i = 0, l = callbacks.length; i < l; i++ ) { - key = callbacks[ i ]; - - // Initialize key collection of logging callback - if ( QUnit.objectType( config.callbacks[ key ] ) === "undefined" ) { - config.callbacks[ key ] = []; - } - - QUnit[ key ] = registerLoggingCallback( key ); - } -})(); - -// `onErrorFnPrev` initialized at top of scope -// Preserve other handlers -onErrorFnPrev = window.onerror; - -// Cover uncaught exceptions -// Returning true will suppress the default browser handler, -// returning false will let it run. -window.onerror = function( error, filePath, linerNr ) { - var ret = false; - if ( onErrorFnPrev ) { - ret = onErrorFnPrev( error, filePath, linerNr ); - } - - // Treat return value as window.onerror itself does, - // Only do our handling if not suppressed. - if ( ret !== true ) { - if ( QUnit.config.current ) { - if ( QUnit.config.current.ignoreGlobalErrors ) { - return true; - } - QUnit.pushFailure( error, filePath + ":" + linerNr ); - } else { - QUnit.test( "global failure", extend(function() { - QUnit.pushFailure( error, filePath + ":" + linerNr ); - }, { validTest: true } ) ); - } - return false; - } - - return ret; -}; - -function done() { - var runtime, passed; - - config.autorun = true; - - // Log the last module results - if ( config.previousModule ) { - runLoggingCallbacks( "moduleDone", { - name: config.previousModule.name, - tests: config.previousModule.tests, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all, - runtime: now() - config.moduleStats.started - }); - } - delete config.previousModule; - - runtime = now() - config.started; - passed = config.stats.all - config.stats.bad; - - runLoggingCallbacks( "done", { - failed: config.stats.bad, - passed: passed, - total: config.stats.all, - runtime: runtime - }); -} - -// Doesn't support IE6 to IE9, it will return undefined on these browsers -// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack -function extractStacktrace( e, offset ) { - offset = offset === undefined ? 4 : offset; - - var stack, include, i; - - if ( e.stack ) { - stack = e.stack.split( "\n" ); - if ( /^error$/i.test( stack[ 0 ] ) ) { - stack.shift(); - } - if ( fileName ) { - include = []; - for ( i = offset; i < stack.length; i++ ) { - if ( stack[ i ].indexOf( fileName ) !== -1 ) { - break; - } - include.push( stack[ i ] ); - } - if ( include.length ) { - return include.join( "\n" ); - } - } - return stack[ offset ]; - - // Support: Safari <=6 only - } else if ( e.sourceURL ) { - - // exclude useless self-reference for generated Error objects - if ( /qunit.js$/.test( e.sourceURL ) ) { - return; - } - - // for actual exceptions, this is useful - return e.sourceURL + ":" + e.line; - } -} - -function sourceFromStacktrace( offset ) { - var error = new Error(); - - // Support: Safari <=7 only, IE <=10 - 11 only - // Not all browsers generate the `stack` property for `new Error()`, see also #636 - if ( !error.stack ) { - try { - throw error; - } catch ( err ) { - error = err; - } - } - - return extractStacktrace( error, offset ); -} - -function synchronize( callback, last ) { - if ( QUnit.objectType( callback ) === "array" ) { - while ( callback.length ) { - synchronize( callback.shift() ); - } - return; - } - config.queue.push( callback ); - - if ( config.autorun && !config.blocking ) { - process( last ); - } -} - -function process( last ) { - function next() { - process( last ); - } - var start = now(); - config.depth = ( config.depth || 0 ) + 1; - - while ( config.queue.length && !config.blocking ) { - if ( !defined.setTimeout || config.updateRate <= 0 || - ( ( now() - start ) < config.updateRate ) ) { - if ( config.current ) { - - // Reset async tracking for each phase of the Test lifecycle - config.current.usedAsync = false; - } - config.queue.shift()(); - } else { - setTimeout( next, 13 ); - break; - } - } - config.depth--; - if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { - done(); - } -} - -function begin() { - var i, l, - modulesLog = []; - - // If the test run hasn't officially begun yet - if ( !config.started ) { - - // Record the time of the test run's beginning - config.started = now(); - - verifyLoggingCallbacks(); - - // Delete the loose unnamed module if unused. - if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) { - config.modules.shift(); - } - - // Avoid unnecessary information by not logging modules' test environments - for ( i = 0, l = config.modules.length; i < l; i++ ) { - modulesLog.push({ - name: config.modules[ i ].name, - tests: config.modules[ i ].tests - }); - } - - // The test run is officially beginning now - runLoggingCallbacks( "begin", { - totalTests: Test.count, - modules: modulesLog - }); - } - - config.blocking = false; - process( true ); -} - -function resumeProcessing() { - runStarted = true; - - // A slight delay to allow this iteration of the event loop to finish (more assertions, etc.) - if ( defined.setTimeout ) { - setTimeout(function() { - if ( config.current && config.current.semaphore > 0 ) { - return; - } - if ( config.timeout ) { - clearTimeout( config.timeout ); - } - - begin(); - }, 13 ); - } else { - begin(); - } -} - -function pauseProcessing() { - config.blocking = true; - - if ( config.testTimeout && defined.setTimeout ) { - clearTimeout( config.timeout ); - config.timeout = setTimeout(function() { - if ( config.current ) { - config.current.semaphore = 0; - QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) ); - } else { - throw new Error( "Test timed out" ); - } - resumeProcessing(); - }, config.testTimeout ); - } -} - -function saveGlobal() { - config.pollution = []; - - if ( config.noglobals ) { - for ( var key in window ) { - if ( hasOwn.call( window, key ) ) { - // in Opera sometimes DOM element ids show up here, ignore them - if ( /^qunit-test-output/.test( key ) ) { - continue; - } - config.pollution.push( key ); - } - } - } -} - -function checkPollution() { - var newGlobals, - deletedGlobals, - old = config.pollution; - - saveGlobal(); - - newGlobals = diff( config.pollution, old ); - if ( newGlobals.length > 0 ) { - QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) ); - } - - deletedGlobals = diff( old, config.pollution ); - if ( deletedGlobals.length > 0 ) { - QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) ); - } -} - -// returns a new Array with the elements that are in a but not in b -function diff( a, b ) { - var i, j, - result = a.slice(); - - for ( i = 0; i < result.length; i++ ) { - for ( j = 0; j < b.length; j++ ) { - if ( result[ i ] === b[ j ] ) { - result.splice( i, 1 ); - i--; - break; - } - } - } - return result; -} - -function extend( a, b, undefOnly ) { - for ( var prop in b ) { - if ( hasOwn.call( b, prop ) ) { - - // Avoid "Member not found" error in IE8 caused by messing with window.constructor - if ( !( prop === "constructor" && a === window ) ) { - if ( b[ prop ] === undefined ) { - delete a[ prop ]; - } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) { - a[ prop ] = b[ prop ]; - } - } - } - } - - return a; -} - -function runLoggingCallbacks( key, args ) { - var i, l, callbacks; - - callbacks = config.callbacks[ key ]; - for ( i = 0, l = callbacks.length; i < l; i++ ) { - callbacks[ i ]( args ); - } -} - -// DEPRECATED: This will be removed on 2.0.0+ -// This function verifies if the loggingCallbacks were modified by the user -// If so, it will restore it, assign the given callback and print a console warning -function verifyLoggingCallbacks() { - var loggingCallback, userCallback; - - for ( loggingCallback in loggingCallbacks ) { - if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) { - - userCallback = QUnit[ loggingCallback ]; - - // Restore the callback function - QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ]; - - // Assign the deprecated given callback - QUnit[ loggingCallback ]( userCallback ); - - if ( window.console && window.console.warn ) { - window.console.warn( - "QUnit." + loggingCallback + " was replaced with a new value.\n" + - "Please, check out the documentation on how to apply logging callbacks.\n" + - "Reference: http://api.qunitjs.com/category/callbacks/" - ); - } - } - } -} - -// from jquery.js -function inArray( elem, array ) { - if ( array.indexOf ) { - return array.indexOf( elem ); - } - - for ( var i = 0, length = array.length; i < length; i++ ) { - if ( array[ i ] === elem ) { - return i; - } - } - - return -1; -} - -function Test( settings ) { - var i, l; - - ++Test.count; - - extend( this, settings ); - this.assertions = []; - this.semaphore = 0; - this.usedAsync = false; - this.module = config.currentModule; - this.stack = sourceFromStacktrace( 3 ); - - // Register unique strings - for ( i = 0, l = this.module.tests; i < l.length; i++ ) { - if ( this.module.tests[ i ].name === this.testName ) { - this.testName += " "; - } - } - - this.testId = generateHash( this.module.name, this.testName ); - - this.module.tests.push({ - name: this.testName, - testId: this.testId - }); - - if ( settings.skip ) { - - // Skipped tests will fully ignore any sent callback - this.callback = function() {}; - this.async = false; - this.expected = 0; - } else { - this.assert = new Assert( this ); - } -} - -Test.count = 0; - -Test.prototype = { - before: function() { - if ( - - // Emit moduleStart when we're switching from one module to another - this.module !== config.previousModule || - - // They could be equal (both undefined) but if the previousModule property doesn't - // yet exist it means this is the first test in a suite that isn't wrapped in a - // module, in which case we'll just emit a moduleStart event for 'undefined'. - // Without this, reporters can get testStart before moduleStart which is a problem. - !hasOwn.call( config, "previousModule" ) - ) { - if ( hasOwn.call( config, "previousModule" ) ) { - runLoggingCallbacks( "moduleDone", { - name: config.previousModule.name, - tests: config.previousModule.tests, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all, - runtime: now() - config.moduleStats.started - }); - } - config.previousModule = this.module; - config.moduleStats = { all: 0, bad: 0, started: now() }; - runLoggingCallbacks( "moduleStart", { - name: this.module.name, - tests: this.module.tests - }); - } - - config.current = this; - - this.testEnvironment = extend( {}, this.module.testEnvironment ); - delete this.testEnvironment.beforeEach; - delete this.testEnvironment.afterEach; - - this.started = now(); - runLoggingCallbacks( "testStart", { - name: this.testName, - module: this.module.name, - testId: this.testId - }); - - if ( !config.pollution ) { - saveGlobal(); - } - }, - - run: function() { - var promise; - - config.current = this; - - if ( this.async ) { - QUnit.stop(); - } - - this.callbackStarted = now(); - - if ( config.notrycatch ) { - promise = this.callback.call( this.testEnvironment, this.assert ); - this.resolvePromise( promise ); - return; - } - - try { - promise = this.callback.call( this.testEnvironment, this.assert ); - this.resolvePromise( promise ); - } catch ( e ) { - this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " + - this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) ); - - // else next test will carry the responsibility - saveGlobal(); - - // Restart the tests if they're blocking - if ( config.blocking ) { - QUnit.start(); - } - } - }, - - after: function() { - checkPollution(); - }, - - queueHook: function( hook, hookName ) { - var promise, - test = this; - return function runHook() { - config.current = test; - if ( config.notrycatch ) { - promise = hook.call( test.testEnvironment, test.assert ); - test.resolvePromise( promise, hookName ); - return; - } - try { - promise = hook.call( test.testEnvironment, test.assert ); - test.resolvePromise( promise, hookName ); - } catch ( error ) { - test.pushFailure( hookName + " failed on " + test.testName + ": " + - ( error.message || error ), extractStacktrace( error, 0 ) ); - } - }; - }, - - // Currently only used for module level hooks, can be used to add global level ones - hooks: function( handler ) { - var hooks = []; - - // Hooks are ignored on skipped tests - if ( this.skip ) { - return hooks; - } - - if ( this.module.testEnvironment && - QUnit.objectType( this.module.testEnvironment[ handler ] ) === "function" ) { - hooks.push( this.queueHook( this.module.testEnvironment[ handler ], handler ) ); - } - - return hooks; - }, - - finish: function() { - config.current = this; - if ( config.requireExpects && this.expected === null ) { - this.pushFailure( "Expected number of assertions to be defined, but expect() was " + - "not called.", this.stack ); - } else if ( this.expected !== null && this.expected !== this.assertions.length ) { - this.pushFailure( "Expected " + this.expected + " assertions, but " + - this.assertions.length + " were run", this.stack ); - } else if ( this.expected === null && !this.assertions.length ) { - this.pushFailure( "Expected at least one assertion, but none were run - call " + - "expect(0) to accept zero assertions.", this.stack ); - } - - var i, - bad = 0; - - this.runtime = now() - this.started; - config.stats.all += this.assertions.length; - config.moduleStats.all += this.assertions.length; - - for ( i = 0; i < this.assertions.length; i++ ) { - if ( !this.assertions[ i ].result ) { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - - runLoggingCallbacks( "testDone", { - name: this.testName, - module: this.module.name, - skipped: !!this.skip, - failed: bad, - passed: this.assertions.length - bad, - total: this.assertions.length, - runtime: this.runtime, - - // HTML Reporter use - assertions: this.assertions, - testId: this.testId, - - // DEPRECATED: this property will be removed in 2.0.0, use runtime instead - duration: this.runtime - }); - - // QUnit.reset() is deprecated and will be replaced for a new - // fixture reset function on QUnit 2.0/2.1. - // It's still called here for backwards compatibility handling - QUnit.reset(); - - config.current = undefined; - }, - - queue: function() { - var bad, - test = this; - - if ( !this.valid() ) { - return; - } - - function run() { - - // each of these can by async - synchronize([ - function() { - test.before(); - }, - - test.hooks( "beforeEach" ), - - function() { - test.run(); - }, - - test.hooks( "afterEach" ).reverse(), - - function() { - test.after(); - }, - function() { - test.finish(); - } - ]); - } - - // `bad` initialized at top of scope - // defer when previous test run passed, if storage is available - bad = QUnit.config.reorder && defined.sessionStorage && - +sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName ); - - if ( bad ) { - run(); - } else { - synchronize( run, true ); - } - }, - - push: function( result, actual, expected, message ) { - var source, - details = { - module: this.module.name, - name: this.testName, - result: result, - message: message, - actual: actual, - expected: expected, - testId: this.testId, - runtime: now() - this.started - }; - - if ( !result ) { - source = sourceFromStacktrace(); - - if ( source ) { - details.source = source; - } - } - - runLoggingCallbacks( "log", details ); - - this.assertions.push({ - result: !!result, - message: message - }); - }, - - pushFailure: function( message, source, actual ) { - if ( !this instanceof Test ) { - throw new Error( "pushFailure() assertion outside test context, was " + - sourceFromStacktrace( 2 ) ); - } - - var details = { - module: this.module.name, - name: this.testName, - result: false, - message: message || "error", - actual: actual || null, - testId: this.testId, - runtime: now() - this.started - }; - - if ( source ) { - details.source = source; - } - - runLoggingCallbacks( "log", details ); - - this.assertions.push({ - result: false, - message: message - }); - }, - - resolvePromise: function( promise, phase ) { - var then, message, - test = this; - if ( promise != null ) { - then = promise.then; - if ( QUnit.objectType( then ) === "function" ) { - QUnit.stop(); - then.call( - promise, - QUnit.start, - function( error ) { - message = "Promise rejected " + - ( !phase ? "during" : phase.replace( /Each$/, "" ) ) + - " " + test.testName + ": " + ( error.message || error ); - test.pushFailure( message, extractStacktrace( error, 0 ) ); - - // else next test will carry the responsibility - saveGlobal(); - - // Unblock - QUnit.start(); - } - ); - } - } - }, - - valid: function() { - var include, - filter = config.filter && config.filter.toLowerCase(), - module = QUnit.urlParams.module && QUnit.urlParams.module.toLowerCase(), - fullName = ( this.module.name + ": " + this.testName ).toLowerCase(); - - // Internally-generated tests are always valid - if ( this.callback && this.callback.validTest ) { - return true; - } - - if ( config.testId.length > 0 && inArray( this.testId, config.testId ) < 0 ) { - return false; - } - - if ( module && ( !this.module.name || this.module.name.toLowerCase() !== module ) ) { - return false; - } - - if ( !filter ) { - return true; - } - - include = filter.charAt( 0 ) !== "!"; - if ( !include ) { - filter = filter.slice( 1 ); - } - - // If the filter matches, we need to honour include - if ( fullName.indexOf( filter ) !== -1 ) { - return include; - } - - // Otherwise, do the opposite - return !include; - } - -}; - -// Resets the test setup. Useful for tests that modify the DOM. -/* -DEPRECATED: Use multiple tests instead of resetting inside a test. -Use testStart or testDone for custom cleanup. -This method will throw an error in 2.0, and will be removed in 2.1 -*/ -QUnit.reset = function() { - - // Return on non-browser environments - // This is necessary to not break on node tests - if ( typeof window === "undefined" ) { - return; - } - - var fixture = defined.document && document.getElementById && - document.getElementById( "qunit-fixture" ); - - if ( fixture ) { - fixture.innerHTML = config.fixture; - } -}; - -QUnit.pushFailure = function() { - if ( !QUnit.config.current ) { - throw new Error( "pushFailure() assertion outside test context, in " + - sourceFromStacktrace( 2 ) ); - } - - // Gets current test obj - var currentTest = QUnit.config.current; - - return currentTest.pushFailure.apply( currentTest, arguments ); -}; - -// Based on Java's String.hashCode, a simple but not -// rigorously collision resistant hashing function -function generateHash( module, testName ) { - var hex, - i = 0, - hash = 0, - str = module + "\x1C" + testName, - len = str.length; - - for ( ; i < len; i++ ) { - hash = ( ( hash << 5 ) - hash ) + str.charCodeAt( i ); - hash |= 0; - } - - // Convert the possibly negative integer hash code into an 8 character hex string, which isn't - // strictly necessary but increases user understanding that the id is a SHA-like hash - hex = ( 0x100000000 + hash ).toString( 16 ); - if ( hex.length < 8 ) { - hex = "0000000" + hex; - } - - return hex.slice( -8 ); -} - -function Assert( testContext ) { - this.test = testContext; -} - -// Assert helpers -QUnit.assert = Assert.prototype = { - - // Specify the number of expected assertions to guarantee that failed test - // (no assertions are run at all) don't slip through. - expect: function( asserts ) { - if ( arguments.length === 1 ) { - this.test.expected = asserts; - } else { - return this.test.expected; - } - }, - - // Increment this Test's semaphore counter, then return a single-use function that - // decrements that counter a maximum of once. - async: function() { - var test = this.test, - popped = false; - - test.semaphore += 1; - test.usedAsync = true; - pauseProcessing(); - - return function done() { - if ( !popped ) { - test.semaphore -= 1; - popped = true; - resumeProcessing(); - } else { - test.pushFailure( "Called the callback returned from `assert.async` more than once", - sourceFromStacktrace( 2 ) ); - } - }; - }, - - // Exports test.push() to the user API - push: function( /* result, actual, expected, message */ ) { - var assert = this, - currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current; - - // Backwards compatibility fix. - // Allows the direct use of global exported assertions and QUnit.assert.* - // Although, it's use is not recommended as it can leak assertions - // to other tests from async tests, because we only get a reference to the current test, - // not exactly the test where assertion were intended to be called. - if ( !currentTest ) { - throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) ); - } - - if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) { - currentTest.pushFailure( "Assertion after the final `assert.async` was resolved", - sourceFromStacktrace( 2 ) ); - - // Allow this assertion to continue running anyway... - } - - if ( !( assert instanceof Assert ) ) { - assert = currentTest.assert; - } - return assert.test.push.apply( assert.test, arguments ); - }, - - ok: function( result, message ) { - message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " + - QUnit.dump.parse( result ) ); - this.push( !!result, result, true, message ); - }, - - notOk: function( result, message ) { - message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " + - QUnit.dump.parse( result ) ); - this.push( !result, result, false, message ); - }, - - equal: function( actual, expected, message ) { - /*jshint eqeqeq:false */ - this.push( expected == actual, actual, expected, message ); - }, - - notEqual: function( actual, expected, message ) { - /*jshint eqeqeq:false */ - this.push( expected != actual, actual, expected, message ); - }, - - propEqual: function( actual, expected, message ) { - actual = objectValues( actual ); - expected = objectValues( expected ); - this.push( QUnit.equiv( actual, expected ), actual, expected, message ); - }, - - notPropEqual: function( actual, expected, message ) { - actual = objectValues( actual ); - expected = objectValues( expected ); - this.push( !QUnit.equiv( actual, expected ), actual, expected, message ); - }, - - deepEqual: function( actual, expected, message ) { - this.push( QUnit.equiv( actual, expected ), actual, expected, message ); - }, - - notDeepEqual: function( actual, expected, message ) { - this.push( !QUnit.equiv( actual, expected ), actual, expected, message ); - }, - - strictEqual: function( actual, expected, message ) { - this.push( expected === actual, actual, expected, message ); - }, - - notStrictEqual: function( actual, expected, message ) { - this.push( expected !== actual, actual, expected, message ); - }, - - "throws": function( block, expected, message ) { - var actual, expectedType, - expectedOutput = expected, - ok = false, - currentTest = ( this instanceof Assert && this.test ) || QUnit.config.current; - - // 'expected' is optional unless doing string comparison - if ( message == null && typeof expected === "string" ) { - message = expected; - expected = null; - } - - currentTest.ignoreGlobalErrors = true; - try { - block.call( currentTest.testEnvironment ); - } catch (e) { - actual = e; - } - currentTest.ignoreGlobalErrors = false; - - if ( actual ) { - expectedType = QUnit.objectType( expected ); - - // we don't want to validate thrown error - if ( !expected ) { - ok = true; - expectedOutput = null; - - // expected is a regexp - } else if ( expectedType === "regexp" ) { - ok = expected.test( errorString( actual ) ); - - // expected is a string - } else if ( expectedType === "string" ) { - ok = expected === errorString( actual ); - - // expected is a constructor, maybe an Error constructor - } else if ( expectedType === "function" && actual instanceof expected ) { - ok = true; - - // expected is an Error object - } else if ( expectedType === "object" ) { - ok = actual instanceof expected.constructor && - actual.name === expected.name && - actual.message === expected.message; - - // expected is a validation function which returns true if validation passed - } else if ( expectedType === "function" && expected.call( {}, actual ) === true ) { - expectedOutput = null; - ok = true; - } - } - - currentTest.assert.push( ok, actual, expectedOutput, message ); - } -}; - -// Provide an alternative to assert.throws(), for enviroments that consider throws a reserved word -// Known to us are: Closure Compiler, Narwhal -(function() { - /*jshint sub:true */ - Assert.prototype.raises = Assert.prototype[ "throws" ]; -}()); - -// Test for equality any JavaScript type. -// Author: Philippe Rathé -QUnit.equiv = (function() { - - // Call the o related callback with the given arguments. - function bindCallbacks( o, callbacks, args ) { - var prop = QUnit.objectType( o ); - if ( prop ) { - if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) { - return callbacks[ prop ].apply( callbacks, args ); - } else { - return callbacks[ prop ]; // or undefined - } - } - } - - // the real equiv function - var innerEquiv, - - // stack to decide between skip/abort functions - callers = [], - - // stack to avoiding loops from circular referencing - parents = [], - parentsB = [], - - getProto = Object.getPrototypeOf || function( obj ) { - /* jshint camelcase: false, proto: true */ - return obj.__proto__; - }, - callbacks = (function() { - - // for string, boolean, number and null - function useStrictEquality( b, a ) { - - /*jshint eqeqeq:false */ - if ( b instanceof a.constructor || a instanceof b.constructor ) { - - // to catch short annotation VS 'new' annotation of a - // declaration - // e.g. var i = 1; - // var j = new Number(1); - return a == b; - } else { - return a === b; - } - } - - return { - "string": useStrictEquality, - "boolean": useStrictEquality, - "number": useStrictEquality, - "null": useStrictEquality, - "undefined": useStrictEquality, - - "nan": function( b ) { - return isNaN( b ); - }, - - "date": function( b, a ) { - return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf(); - }, - - "regexp": function( b, a ) { - return QUnit.objectType( b ) === "regexp" && - - // the regex itself - a.source === b.source && - - // and its modifiers - a.global === b.global && - - // (gmi) ... - a.ignoreCase === b.ignoreCase && - a.multiline === b.multiline && - a.sticky === b.sticky; - }, - - // - skip when the property is a method of an instance (OOP) - // - abort otherwise, - // initial === would have catch identical references anyway - "function": function() { - var caller = callers[ callers.length - 1 ]; - return caller !== Object && typeof caller !== "undefined"; - }, - - "array": function( b, a ) { - var i, j, len, loop, aCircular, bCircular; - - // b could be an object literal here - if ( QUnit.objectType( b ) !== "array" ) { - return false; - } - - len = a.length; - if ( len !== b.length ) { - // safe and faster - return false; - } - - // track reference to avoid circular references - parents.push( a ); - parentsB.push( b ); - for ( i = 0; i < len; i++ ) { - loop = false; - for ( j = 0; j < parents.length; j++ ) { - aCircular = parents[ j ] === a[ i ]; - bCircular = parentsB[ j ] === b[ i ]; - if ( aCircular || bCircular ) { - if ( a[ i ] === b[ i ] || aCircular && bCircular ) { - loop = true; - } else { - parents.pop(); - parentsB.pop(); - return false; - } - } - } - if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { - parents.pop(); - parentsB.pop(); - return false; - } - } - parents.pop(); - parentsB.pop(); - return true; - }, - - "object": function( b, a ) { - - /*jshint forin:false */ - var i, j, loop, aCircular, bCircular, - // Default to true - eq = true, - aProperties = [], - bProperties = []; - - // comparing constructors is more strict than using - // instanceof - if ( a.constructor !== b.constructor ) { - - // Allow objects with no prototype to be equivalent to - // objects with Object as their constructor. - if ( !( ( getProto( a ) === null && getProto( b ) === Object.prototype ) || - ( getProto( b ) === null && getProto( a ) === Object.prototype ) ) ) { - return false; - } - } - - // stack constructor before traversing properties - callers.push( a.constructor ); - - // track reference to avoid circular references - parents.push( a ); - parentsB.push( b ); - - // be strict: don't ensure hasOwnProperty and go deep - for ( i in a ) { - loop = false; - for ( j = 0; j < parents.length; j++ ) { - aCircular = parents[ j ] === a[ i ]; - bCircular = parentsB[ j ] === b[ i ]; - if ( aCircular || bCircular ) { - if ( a[ i ] === b[ i ] || aCircular && bCircular ) { - loop = true; - } else { - eq = false; - break; - } - } - } - aProperties.push( i ); - if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { - eq = false; - break; - } - } - - parents.pop(); - parentsB.pop(); - callers.pop(); // unstack, we are done - - for ( i in b ) { - bProperties.push( i ); // collect b's properties - } - - // Ensures identical properties name - return eq && innerEquiv( aProperties.sort(), bProperties.sort() ); - } - }; - }()); - - innerEquiv = function() { // can take multiple arguments - var args = [].slice.apply( arguments ); - if ( args.length < 2 ) { - return true; // end transition - } - - return ( (function( a, b ) { - if ( a === b ) { - return true; // catch the most you can - } else if ( a === null || b === null || typeof a === "undefined" || - typeof b === "undefined" || - QUnit.objectType( a ) !== QUnit.objectType( b ) ) { - - // don't lose time with error prone cases - return false; - } else { - return bindCallbacks( a, callbacks, [ b, a ] ); - } - - // apply transition with (1..n) arguments - }( args[ 0 ], args[ 1 ] ) ) && - innerEquiv.apply( this, args.splice( 1, args.length - 1 ) ) ); - }; - - return innerEquiv; -}()); - -// Based on jsDump by Ariel Flesler -// http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html -QUnit.dump = (function() { - function quote( str ) { - return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\""; - } - function literal( o ) { - return o + ""; - } - function join( pre, arr, post ) { - var s = dump.separator(), - base = dump.indent(), - inner = dump.indent( 1 ); - if ( arr.join ) { - arr = arr.join( "," + s + inner ); - } - if ( !arr ) { - return pre + post; - } - return [ pre, inner + arr, base + post ].join( s ); - } - function array( arr, stack ) { - var i = arr.length, - ret = new Array( i ); - - if ( dump.maxDepth && dump.depth > dump.maxDepth ) { - return "[object Array]"; - } - - this.up(); - while ( i-- ) { - ret[ i ] = this.parse( arr[ i ], undefined, stack ); - } - this.down(); - return join( "[", ret, "]" ); - } - - var reName = /^function (\w+)/, - dump = { - - // objType is used mostly internally, you can fix a (custom) type in advance - parse: function( obj, objType, stack ) { - stack = stack || []; - var res, parser, parserType, - inStack = inArray( obj, stack ); - - if ( inStack !== -1 ) { - return "recursion(" + ( inStack - stack.length ) + ")"; - } - - objType = objType || this.typeOf( obj ); - parser = this.parsers[ objType ]; - parserType = typeof parser; - - if ( parserType === "function" ) { - stack.push( obj ); - res = parser.call( this, obj, stack ); - stack.pop(); - return res; - } - return ( parserType === "string" ) ? parser : this.parsers.error; - }, - typeOf: function( obj ) { - var type; - if ( obj === null ) { - type = "null"; - } else if ( typeof obj === "undefined" ) { - type = "undefined"; - } else if ( QUnit.is( "regexp", obj ) ) { - type = "regexp"; - } else if ( QUnit.is( "date", obj ) ) { - type = "date"; - } else if ( QUnit.is( "function", obj ) ) { - type = "function"; - } else if ( obj.setInterval !== undefined && - obj.document !== undefined && - obj.nodeType === undefined ) { - type = "window"; - } else if ( obj.nodeType === 9 ) { - type = "document"; - } else if ( obj.nodeType ) { - type = "node"; - } else if ( - - // native arrays - toString.call( obj ) === "[object Array]" || - - // NodeList objects - ( typeof obj.length === "number" && obj.item !== undefined && - ( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null && - obj[ 0 ] === undefined ) ) ) - ) { - type = "array"; - } else if ( obj.constructor === Error.prototype.constructor ) { - type = "error"; - } else { - type = typeof obj; - } - return type; - }, - separator: function() { - return this.multiline ? this.HTML ? "
    " : "\n" : this.HTML ? " " : " "; - }, - // extra can be a number, shortcut for increasing-calling-decreasing - indent: function( extra ) { - if ( !this.multiline ) { - return ""; - } - var chr = this.indentChar; - if ( this.HTML ) { - chr = chr.replace( /\t/g, " " ).replace( / /g, " " ); - } - return new Array( this.depth + ( extra || 0 ) ).join( chr ); - }, - up: function( a ) { - this.depth += a || 1; - }, - down: function( a ) { - this.depth -= a || 1; - }, - setParser: function( name, parser ) { - this.parsers[ name ] = parser; - }, - // The next 3 are exposed so you can use them - quote: quote, - literal: literal, - join: join, - // - depth: 1, - maxDepth: QUnit.config.maxDepth, - - // This is the list of parsers, to modify them, use dump.setParser - parsers: { - window: "[Window]", - document: "[Document]", - error: function( error ) { - return "Error(\"" + error.message + "\")"; - }, - unknown: "[Unknown]", - "null": "null", - "undefined": "undefined", - "function": function( fn ) { - var ret = "function", - - // functions never have name in IE - name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ]; - - if ( name ) { - ret += " " + name; - } - ret += "( "; - - ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" ); - return join( ret, dump.parse( fn, "functionCode" ), "}" ); - }, - array: array, - nodelist: array, - "arguments": array, - object: function( map, stack ) { - var keys, key, val, i, nonEnumerableProperties, - ret = []; - - if ( dump.maxDepth && dump.depth > dump.maxDepth ) { - return "[object Object]"; - } - - dump.up(); - keys = []; - for ( key in map ) { - keys.push( key ); - } - - // Some properties are not always enumerable on Error objects. - nonEnumerableProperties = [ "message", "name" ]; - for ( i in nonEnumerableProperties ) { - key = nonEnumerableProperties[ i ]; - if ( key in map && inArray( key, keys ) < 0 ) { - keys.push( key ); - } - } - keys.sort(); - for ( i = 0; i < keys.length; i++ ) { - key = keys[ i ]; - val = map[ key ]; - ret.push( dump.parse( key, "key" ) + ": " + - dump.parse( val, undefined, stack ) ); - } - dump.down(); - return join( "{", ret, "}" ); - }, - node: function( node ) { - var len, i, val, - open = dump.HTML ? "<" : "<", - close = dump.HTML ? ">" : ">", - tag = node.nodeName.toLowerCase(), - ret = open + tag, - attrs = node.attributes; - - if ( attrs ) { - for ( i = 0, len = attrs.length; i < len; i++ ) { - val = attrs[ i ].nodeValue; - - // IE6 includes all attributes in .attributes, even ones not explicitly - // set. Those have values like undefined, null, 0, false, "" or - // "inherit". - if ( val && val !== "inherit" ) { - ret += " " + attrs[ i ].nodeName + "=" + - dump.parse( val, "attribute" ); - } - } - } - ret += close; - - // Show content of TextNode or CDATASection - if ( node.nodeType === 3 || node.nodeType === 4 ) { - ret += node.nodeValue; - } - - return ret + open + "/" + tag + close; - }, - - // function calls it internally, it's the arguments part of the function - functionArgs: function( fn ) { - var args, - l = fn.length; - - if ( !l ) { - return ""; - } - - args = new Array( l ); - while ( l-- ) { - - // 97 is 'a' - args[ l ] = String.fromCharCode( 97 + l ); - } - return " " + args.join( ", " ) + " "; - }, - // object calls it internally, the key part of an item in a map - key: quote, - // function calls it internally, it's the content of the function - functionCode: "[code]", - // node calls it internally, it's an html attribute value - attribute: quote, - string: quote, - date: quote, - regexp: literal, - number: literal, - "boolean": literal - }, - // if true, entities are escaped ( <, >, \t, space and \n ) - HTML: false, - // indentation unit - indentChar: " ", - // if true, items in a collection, are separated by a \n, else just a space. - multiline: true - }; - - return dump; -}()); - -// back compat -QUnit.jsDump = QUnit.dump; - -// For browser, export only select globals -if ( typeof window !== "undefined" ) { - - // Deprecated - // Extend assert methods to QUnit and Global scope through Backwards compatibility - (function() { - var i, - assertions = Assert.prototype; - - function applyCurrent( current ) { - return function() { - var assert = new Assert( QUnit.config.current ); - current.apply( assert, arguments ); - }; - } - - for ( i in assertions ) { - QUnit[ i ] = applyCurrent( assertions[ i ] ); - } - })(); - - (function() { - var i, l, - keys = [ - "test", - "module", - "expect", - "asyncTest", - "start", - "stop", - "ok", - "notOk", - "equal", - "notEqual", - "propEqual", - "notPropEqual", - "deepEqual", - "notDeepEqual", - "strictEqual", - "notStrictEqual", - "throws" - ]; - - for ( i = 0, l = keys.length; i < l; i++ ) { - window[ keys[ i ] ] = QUnit[ keys[ i ] ]; - } - })(); - - window.QUnit = QUnit; -} - -// For nodejs -if ( typeof module !== "undefined" && module && module.exports ) { - module.exports = QUnit; - - // For consistency with CommonJS environments' exports - module.exports.QUnit = QUnit; -} - -// For CommonJS with exports, but without module.exports, like Rhino -if ( typeof exports !== "undefined" && exports ) { - exports.QUnit = QUnit; -} - -if ( typeof define === "function" && define.amd ) { - define( function() { - return QUnit; - } ); - QUnit.config.autostart = false; -} - -// Get a reference to the global object, like window in browsers -}( (function() { - return this; -})() )); - -/*istanbul ignore next */ -// jscs:disable maximumLineLength -/* - * This file is a modified version of google-diff-match-patch's JavaScript implementation - * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js), - * modifications are licensed as more fully set forth in LICENSE.txt. - * - * The original source of google-diff-match-patch is attributable and licensed as follows: - * - * Copyright 2006 Google Inc. - * http://code.google.com/p/google-diff-match-patch/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * More Info: - * https://code.google.com/p/google-diff-match-patch/ - * - * Usage: QUnit.diff(expected, actual) - * - * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) === "the quick brown fox jumpsed} Array of diff tuples. - */ - DiffMatchPatch.prototype.DiffMain = function( text1, text2, optChecklines, optDeadline ) { - var deadline, checklines, commonlength, - commonprefix, commonsuffix, diffs; - // Set a deadline by which time the diff must be complete. - if ( typeof optDeadline === "undefined" ) { - if ( this.DiffTimeout <= 0 ) { - optDeadline = Number.MAX_VALUE; - } else { - optDeadline = ( new Date() ).getTime() + this.DiffTimeout * 1000; - } - } - deadline = optDeadline; - - // Check for null inputs. - if ( text1 === null || text2 === null ) { - throw new Error( "Null input. (DiffMain)" ); - } - - // Check for equality (speedup). - if ( text1 === text2 ) { - if ( text1 ) { - return [ - [ DIFF_EQUAL, text1 ] - ]; - } - return []; - } - - if ( typeof optChecklines === "undefined" ) { - optChecklines = true; - } - - checklines = optChecklines; - - // Trim off common prefix (speedup). - commonlength = this.diffCommonPrefix( text1, text2 ); - commonprefix = text1.substring( 0, commonlength ); - text1 = text1.substring( commonlength ); - text2 = text2.substring( commonlength ); - - // Trim off common suffix (speedup). - ///////// - commonlength = this.diffCommonSuffix( text1, text2 ); - commonsuffix = text1.substring( text1.length - commonlength ); - text1 = text1.substring( 0, text1.length - commonlength ); - text2 = text2.substring( 0, text2.length - commonlength ); - - // Compute the diff on the middle block. - diffs = this.diffCompute( text1, text2, checklines, deadline ); - - // Restore the prefix and suffix. - if ( commonprefix ) { - diffs.unshift( [ DIFF_EQUAL, commonprefix ] ); - } - if ( commonsuffix ) { - diffs.push( [ DIFF_EQUAL, commonsuffix ] ); - } - this.diffCleanupMerge( diffs ); - return diffs; - }; - /** - * Reduce the number of edits by eliminating operationally trivial equalities. - * @param {!Array.} diffs Array of diff tuples. - */ - DiffMatchPatch.prototype.diffCleanupEfficiency = function( diffs ) { - var changes, equalities, equalitiesLength, lastequality, - pointer, preIns, preDel, postIns, postDel; - changes = false; - equalities = []; // Stack of indices where equalities are found. - equalitiesLength = 0; // Keeping our own length var is faster in JS. - /** @type {?string} */ - lastequality = null; - // Always equal to diffs[equalities[equalitiesLength - 1]][1] - pointer = 0; // Index of current position. - // Is there an insertion operation before the last equality. - preIns = false; - // Is there a deletion operation before the last equality. - preDel = false; - // Is there an insertion operation after the last equality. - postIns = false; - // Is there a deletion operation after the last equality. - postDel = false; - while ( pointer < diffs.length ) { - if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { // Equality found. - if ( diffs[ pointer ][ 1 ].length < this.DiffEditCost && ( postIns || postDel ) ) { - // Candidate found. - equalities[ equalitiesLength++ ] = pointer; - preIns = postIns; - preDel = postDel; - lastequality = diffs[ pointer ][ 1 ]; - } else { - // Not a candidate, and can never become one. - equalitiesLength = 0; - lastequality = null; - } - postIns = postDel = false; - } else { // An insertion or deletion. - if ( diffs[ pointer ][ 0 ] === DIFF_DELETE ) { - postDel = true; - } else { - postIns = true; - } - /* - * Five types to be split: - * ABXYCD - * AXCD - * ABXC - * AXCD - * ABXC - */ - if ( lastequality && ( ( preIns && preDel && postIns && postDel ) || - ( ( lastequality.length < this.DiffEditCost / 2 ) && - ( preIns + preDel + postIns + postDel ) === 3 ) ) ) { - // Duplicate record. - diffs.splice( equalities[equalitiesLength - 1], 0, [ DIFF_DELETE, lastequality ] ); - // Change second copy to insert. - diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT; - equalitiesLength--; // Throw away the equality we just deleted; - lastequality = null; - if (preIns && preDel) { - // No changes made which could affect previous entry, keep going. - postIns = postDel = true; - equalitiesLength = 0; - } else { - equalitiesLength--; // Throw away the previous equality. - pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1; - postIns = postDel = false; - } - changes = true; - } - } - pointer++; - } - - if ( changes ) { - this.diffCleanupMerge( diffs ); - } - }; - /** - * Convert a diff array into a pretty HTML report. - * @param {!Array.} diffs Array of diff tuples. - * @param {integer} string to be beautified. - * @return {string} HTML representation. - */ - DiffMatchPatch.prototype.diffPrettyHtml = function( diffs ) { - var op, data, x, html = []; - for ( x = 0; x < diffs.length; x++ ) { - op = diffs[x][0]; // Operation (insert, delete, equal) - data = diffs[x][1]; // Text of change. - switch ( op ) { - case DIFF_INSERT: - html[x] = "" + data + ""; - break; - case DIFF_DELETE: - html[x] = "" + data + ""; - break; - case DIFF_EQUAL: - html[x] = "" + data + ""; - break; - } - } - return html.join(""); - }; - /** - * Determine the common prefix of two strings. - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {number} The number of characters common to the start of each - * string. - */ - DiffMatchPatch.prototype.diffCommonPrefix = function( text1, text2 ) { - var pointermid, pointermax, pointermin, pointerstart; - // Quick check for common null cases. - if ( !text1 || !text2 || text1.charAt(0) !== text2.charAt(0) ) { - return 0; - } - // Binary search. - // Performance analysis: http://neil.fraser.name/news/2007/10/09/ - pointermin = 0; - pointermax = Math.min( text1.length, text2.length ); - pointermid = pointermax; - pointerstart = 0; - while ( pointermin < pointermid ) { - if ( text1.substring( pointerstart, pointermid ) === text2.substring( pointerstart, pointermid ) ) { - pointermin = pointermid; - pointerstart = pointermin; - } else { - pointermax = pointermid; - } - pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin ); - } - return pointermid; - }; - /** - * Determine the common suffix of two strings. - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {number} The number of characters common to the end of each string. - */ - DiffMatchPatch.prototype.diffCommonSuffix = function( text1, text2 ) { - var pointermid, pointermax, pointermin, pointerend; - // Quick check for common null cases. - if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) { - return 0; - } - // Binary search. - // Performance analysis: http://neil.fraser.name/news/2007/10/09/ - pointermin = 0; - pointermax = Math.min(text1.length, text2.length); - pointermid = pointermax; - pointerend = 0; - while ( pointermin < pointermid ) { - if (text1.substring( text1.length - pointermid, text1.length - pointerend ) === - text2.substring( text2.length - pointermid, text2.length - pointerend ) ) { - pointermin = pointermid; - pointerend = pointermin; - } else { - pointermax = pointermid; - } - pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin ); - } - return pointermid; - }; - /** - * Find the differences between two texts. Assumes that the texts do not - * have any common prefix or suffix. - * @param {string} text1 Old string to be diffed. - * @param {string} text2 New string to be diffed. - * @param {boolean} checklines Speedup flag. If false, then don't run a - * line-level diff first to identify the changed areas. - * If true, then run a faster, slightly less optimal diff. - * @param {number} deadline Time when the diff should be complete by. - * @return {!Array.} Array of diff tuples. - * @private - */ - DiffMatchPatch.prototype.diffCompute = function( text1, text2, checklines, deadline ) { - var diffs, longtext, shorttext, i, hm, - text1A, text2A, text1B, text2B, - midCommon, diffsA, diffsB; - - if ( !text1 ) { - // Just add some text (speedup). - return [ - [ DIFF_INSERT, text2 ] - ]; - } - - if (!text2) { - // Just delete some text (speedup). - return [ - [ DIFF_DELETE, text1 ] - ]; - } - - longtext = text1.length > text2.length ? text1 : text2; - shorttext = text1.length > text2.length ? text2 : text1; - i = longtext.indexOf( shorttext ); - if ( i !== -1 ) { - // Shorter text is inside the longer text (speedup). - diffs = [ - [ DIFF_INSERT, longtext.substring( 0, i ) ], - [ DIFF_EQUAL, shorttext ], - [ DIFF_INSERT, longtext.substring( i + shorttext.length ) ] - ]; - // Swap insertions for deletions if diff is reversed. - if ( text1.length > text2.length ) { - diffs[0][0] = diffs[2][0] = DIFF_DELETE; - } - return diffs; - } - - if ( shorttext.length === 1 ) { - // Single character string. - // After the previous speedup, the character can't be an equality. - return [ - [ DIFF_DELETE, text1 ], - [ DIFF_INSERT, text2 ] - ]; - } - - // Check to see if the problem can be split in two. - hm = this.diffHalfMatch(text1, text2); - if (hm) { - // A half-match was found, sort out the return data. - text1A = hm[0]; - text1B = hm[1]; - text2A = hm[2]; - text2B = hm[3]; - midCommon = hm[4]; - // Send both pairs off for separate processing. - diffsA = this.DiffMain(text1A, text2A, checklines, deadline); - diffsB = this.DiffMain(text1B, text2B, checklines, deadline); - // Merge the results. - return diffsA.concat([ - [ DIFF_EQUAL, midCommon ] - ], diffsB); - } - - if (checklines && text1.length > 100 && text2.length > 100) { - return this.diffLineMode(text1, text2, deadline); - } - - return this.diffBisect(text1, text2, deadline); - }; - /** - * Do the two texts share a substring which is at least half the length of the - * longer text? - * This speedup can produce non-minimal diffs. - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {Array.} Five element Array, containing the prefix of - * text1, the suffix of text1, the prefix of text2, the suffix of - * text2 and the common middle. Or null if there was no match. - * @private - */ - DiffMatchPatch.prototype.diffHalfMatch = function(text1, text2) { - var longtext, shorttext, dmp, - text1A, text2B, text2A, text1B, midCommon, - hm1, hm2, hm; - if (this.DiffTimeout <= 0) { - // Don't risk returning a non-optimal diff if we have unlimited time. - return null; - } - longtext = text1.length > text2.length ? text1 : text2; - shorttext = text1.length > text2.length ? text2 : text1; - if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { - return null; // Pointless. - } - dmp = this; // 'this' becomes 'window' in a closure. - - /** - * Does a substring of shorttext exist within longtext such that the substring - * is at least half the length of longtext? - * Closure, but does not reference any external variables. - * @param {string} longtext Longer string. - * @param {string} shorttext Shorter string. - * @param {number} i Start index of quarter length substring within longtext. - * @return {Array.} Five element Array, containing the prefix of - * longtext, the suffix of longtext, the prefix of shorttext, the suffix - * of shorttext and the common middle. Or null if there was no match. - * @private - */ - function diffHalfMatchI(longtext, shorttext, i) { - var seed, j, bestCommon, prefixLength, suffixLength, - bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB; - // Start with a 1/4 length substring at position i as a seed. - seed = longtext.substring(i, i + Math.floor(longtext.length / 4)); - j = -1; - bestCommon = ""; - while ((j = shorttext.indexOf(seed, j + 1)) !== -1) { - prefixLength = dmp.diffCommonPrefix(longtext.substring(i), - shorttext.substring(j)); - suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), - shorttext.substring(0, j)); - if (bestCommon.length < suffixLength + prefixLength) { - bestCommon = shorttext.substring(j - suffixLength, j) + - shorttext.substring(j, j + prefixLength); - bestLongtextA = longtext.substring(0, i - suffixLength); - bestLongtextB = longtext.substring(i + prefixLength); - bestShorttextA = shorttext.substring(0, j - suffixLength); - bestShorttextB = shorttext.substring(j + prefixLength); - } - } - if (bestCommon.length * 2 >= longtext.length) { - return [ bestLongtextA, bestLongtextB, - bestShorttextA, bestShorttextB, bestCommon - ]; - } else { - return null; - } - } - - // First check if the second quarter is the seed for a half-match. - hm1 = diffHalfMatchI(longtext, shorttext, - Math.ceil(longtext.length / 4)); - // Check again based on the third quarter. - hm2 = diffHalfMatchI(longtext, shorttext, - Math.ceil(longtext.length / 2)); - if (!hm1 && !hm2) { - return null; - } else if (!hm2) { - hm = hm1; - } else if (!hm1) { - hm = hm2; - } else { - // Both matched. Select the longest. - hm = hm1[4].length > hm2[4].length ? hm1 : hm2; - } - - // A half-match was found, sort out the return data. - text1A, text1B, text2A, text2B; - if (text1.length > text2.length) { - text1A = hm[0]; - text1B = hm[1]; - text2A = hm[2]; - text2B = hm[3]; - } else { - text2A = hm[0]; - text2B = hm[1]; - text1A = hm[2]; - text1B = hm[3]; - } - midCommon = hm[4]; - return [ text1A, text1B, text2A, text2B, midCommon ]; - }; - /** - * Do a quick line-level diff on both strings, then rediff the parts for - * greater accuracy. - * This speedup can produce non-minimal diffs. - * @param {string} text1 Old string to be diffed. - * @param {string} text2 New string to be diffed. - * @param {number} deadline Time when the diff should be complete by. - * @return {!Array.} Array of diff tuples. - * @private - */ - DiffMatchPatch.prototype.diffLineMode = function(text1, text2, deadline) { - var a, diffs, linearray, pointer, countInsert, - countDelete, textInsert, textDelete, j; - // Scan the text on a line-by-line basis first. - a = this.diffLinesToChars(text1, text2); - text1 = a.chars1; - text2 = a.chars2; - linearray = a.lineArray; - - diffs = this.DiffMain(text1, text2, false, deadline); - - // Convert the diff back to original text. - this.diffCharsToLines(diffs, linearray); - // Eliminate freak matches (e.g. blank lines) - this.diffCleanupSemantic(diffs); - - // Rediff any replacement blocks, this time character-by-character. - // Add a dummy entry at the end. - diffs.push( [ DIFF_EQUAL, "" ] ); - pointer = 0; - countDelete = 0; - countInsert = 0; - textDelete = ""; - textInsert = ""; - while (pointer < diffs.length) { - switch ( diffs[pointer][0] ) { - case DIFF_INSERT: - countInsert++; - textInsert += diffs[pointer][1]; - break; - case DIFF_DELETE: - countDelete++; - textDelete += diffs[pointer][1]; - break; - case DIFF_EQUAL: - // Upon reaching an equality, check for prior redundancies. - if (countDelete >= 1 && countInsert >= 1) { - // Delete the offending records and add the merged ones. - diffs.splice(pointer - countDelete - countInsert, - countDelete + countInsert); - pointer = pointer - countDelete - countInsert; - a = this.DiffMain(textDelete, textInsert, false, deadline); - for (j = a.length - 1; j >= 0; j--) { - diffs.splice( pointer, 0, a[j] ); - } - pointer = pointer + a.length; - } - countInsert = 0; - countDelete = 0; - textDelete = ""; - textInsert = ""; - break; - } - pointer++; - } - diffs.pop(); // Remove the dummy entry at the end. - - return diffs; - }; - /** - * Find the 'middle snake' of a diff, split the problem in two - * and return the recursively constructed diff. - * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. - * @param {string} text1 Old string to be diffed. - * @param {string} text2 New string to be diffed. - * @param {number} deadline Time at which to bail if not yet complete. - * @return {!Array.} Array of diff tuples. - * @private - */ - DiffMatchPatch.prototype.diffBisect = function(text1, text2, deadline) { - var text1Length, text2Length, maxD, vOffset, vLength, - v1, v2, x, delta, front, k1start, k1end, k2start, - k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2; - // Cache the text lengths to prevent multiple calls. - text1Length = text1.length; - text2Length = text2.length; - maxD = Math.ceil((text1Length + text2Length) / 2); - vOffset = maxD; - vLength = 2 * maxD; - v1 = new Array(vLength); - v2 = new Array(vLength); - // Setting all elements to -1 is faster in Chrome & Firefox than mixing - // integers and undefined. - for (x = 0; x < vLength; x++) { - v1[x] = -1; - v2[x] = -1; - } - v1[vOffset + 1] = 0; - v2[vOffset + 1] = 0; - delta = text1Length - text2Length; - // If the total number of characters is odd, then the front path will collide - // with the reverse path. - front = (delta % 2 !== 0); - // Offsets for start and end of k loop. - // Prevents mapping of space beyond the grid. - k1start = 0; - k1end = 0; - k2start = 0; - k2end = 0; - for (d = 0; d < maxD; d++) { - // Bail out if deadline is reached. - if ((new Date()).getTime() > deadline) { - break; - } - - // Walk the front path one step. - for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { - k1Offset = vOffset + k1; - if ( k1 === -d || ( k1 !== d && v1[ k1Offset - 1 ] < v1[ k1Offset + 1 ] ) ) { - x1 = v1[k1Offset + 1]; - } else { - x1 = v1[k1Offset - 1] + 1; - } - y1 = x1 - k1; - while (x1 < text1Length && y1 < text2Length && - text1.charAt(x1) === text2.charAt(y1)) { - x1++; - y1++; - } - v1[k1Offset] = x1; - if (x1 > text1Length) { - // Ran off the right of the graph. - k1end += 2; - } else if (y1 > text2Length) { - // Ran off the bottom of the graph. - k1start += 2; - } else if (front) { - k2Offset = vOffset + delta - k1; - if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) { - // Mirror x2 onto top-left coordinate system. - x2 = text1Length - v2[k2Offset]; - if (x1 >= x2) { - // Overlap detected. - return this.diffBisectSplit(text1, text2, x1, y1, deadline); - } - } - } - } - - // Walk the reverse path one step. - for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { - k2Offset = vOffset + k2; - if ( k2 === -d || (k2 !== d && v2[ k2Offset - 1 ] < v2[ k2Offset + 1 ] ) ) { - x2 = v2[k2Offset + 1]; - } else { - x2 = v2[k2Offset - 1] + 1; - } - y2 = x2 - k2; - while (x2 < text1Length && y2 < text2Length && - text1.charAt(text1Length - x2 - 1) === - text2.charAt(text2Length - y2 - 1)) { - x2++; - y2++; - } - v2[k2Offset] = x2; - if (x2 > text1Length) { - // Ran off the left of the graph. - k2end += 2; - } else if (y2 > text2Length) { - // Ran off the top of the graph. - k2start += 2; - } else if (!front) { - k1Offset = vOffset + delta - k2; - if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) { - x1 = v1[k1Offset]; - y1 = vOffset + x1 - k1Offset; - // Mirror x2 onto top-left coordinate system. - x2 = text1Length - x2; - if (x1 >= x2) { - // Overlap detected. - return this.diffBisectSplit(text1, text2, x1, y1, deadline); - } - } - } - } - } - // Diff took too long and hit the deadline or - // number of diffs equals number of characters, no commonality at all. - return [ - [ DIFF_DELETE, text1 ], - [ DIFF_INSERT, text2 ] - ]; - }; - /** - * Given the location of the 'middle snake', split the diff in two parts - * and recurse. - * @param {string} text1 Old string to be diffed. - * @param {string} text2 New string to be diffed. - * @param {number} x Index of split point in text1. - * @param {number} y Index of split point in text2. - * @param {number} deadline Time at which to bail if not yet complete. - * @return {!Array.} Array of diff tuples. - * @private - */ - DiffMatchPatch.prototype.diffBisectSplit = function( text1, text2, x, y, deadline ) { - var text1a, text1b, text2a, text2b, diffs, diffsb; - text1a = text1.substring(0, x); - text2a = text2.substring(0, y); - text1b = text1.substring(x); - text2b = text2.substring(y); - - // Compute both diffs serially. - diffs = this.DiffMain(text1a, text2a, false, deadline); - diffsb = this.DiffMain(text1b, text2b, false, deadline); - - return diffs.concat(diffsb); - }; - /** - * Reduce the number of edits by eliminating semantically trivial equalities. - * @param {!Array.} diffs Array of diff tuples. - */ - DiffMatchPatch.prototype.diffCleanupSemantic = function(diffs) { - var changes, equalities, equalitiesLength, lastequality, - pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, - lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2; - changes = false; - equalities = []; // Stack of indices where equalities are found. - equalitiesLength = 0; // Keeping our own length var is faster in JS. - /** @type {?string} */ - lastequality = null; - // Always equal to diffs[equalities[equalitiesLength - 1]][1] - pointer = 0; // Index of current position. - // Number of characters that changed prior to the equality. - lengthInsertions1 = 0; - lengthDeletions1 = 0; - // Number of characters that changed after the equality. - lengthInsertions2 = 0; - lengthDeletions2 = 0; - while (pointer < diffs.length) { - if (diffs[pointer][0] === DIFF_EQUAL) { // Equality found. - equalities[equalitiesLength++] = pointer; - lengthInsertions1 = lengthInsertions2; - lengthDeletions1 = lengthDeletions2; - lengthInsertions2 = 0; - lengthDeletions2 = 0; - lastequality = diffs[pointer][1]; - } else { // An insertion or deletion. - if (diffs[pointer][0] === DIFF_INSERT) { - lengthInsertions2 += diffs[pointer][1].length; - } else { - lengthDeletions2 += diffs[pointer][1].length; - } - // Eliminate an equality that is smaller or equal to the edits on both - // sides of it. - if (lastequality && (lastequality.length <= - Math.max(lengthInsertions1, lengthDeletions1)) && - (lastequality.length <= Math.max(lengthInsertions2, - lengthDeletions2))) { - // Duplicate record. - diffs.splice( equalities[ equalitiesLength - 1 ], 0, [ DIFF_DELETE, lastequality ] ); - // Change second copy to insert. - diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; - // Throw away the equality we just deleted. - equalitiesLength--; - // Throw away the previous equality (it needs to be reevaluated). - equalitiesLength--; - pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; - lengthInsertions1 = 0; // Reset the counters. - lengthDeletions1 = 0; - lengthInsertions2 = 0; - lengthDeletions2 = 0; - lastequality = null; - changes = true; - } - } - pointer++; - } - - // Normalize the diff. - if (changes) { - this.diffCleanupMerge(diffs); - } - - // Find any overlaps between deletions and insertions. - // e.g: abcxxxxxxdef - // -> abcxxxdef - // e.g: xxxabcdefxxx - // -> defxxxabc - // Only extract an overlap if it is as big as the edit ahead or behind it. - pointer = 1; - while (pointer < diffs.length) { - if (diffs[pointer - 1][0] === DIFF_DELETE && - diffs[pointer][0] === DIFF_INSERT) { - deletion = diffs[pointer - 1][1]; - insertion = diffs[pointer][1]; - overlapLength1 = this.diffCommonOverlap(deletion, insertion); - overlapLength2 = this.diffCommonOverlap(insertion, deletion); - if (overlapLength1 >= overlapLength2) { - if (overlapLength1 >= deletion.length / 2 || - overlapLength1 >= insertion.length / 2) { - // Overlap found. Insert an equality and trim the surrounding edits. - diffs.splice( pointer, 0, [ DIFF_EQUAL, insertion.substring( 0, overlapLength1 ) ] ); - diffs[pointer - 1][1] = - deletion.substring(0, deletion.length - overlapLength1); - diffs[pointer + 1][1] = insertion.substring(overlapLength1); - pointer++; - } - } else { - if (overlapLength2 >= deletion.length / 2 || - overlapLength2 >= insertion.length / 2) { - // Reverse overlap found. - // Insert an equality and swap and trim the surrounding edits. - diffs.splice( pointer, 0, [ DIFF_EQUAL, deletion.substring( 0, overlapLength2 ) ] ); - diffs[pointer - 1][0] = DIFF_INSERT; - diffs[pointer - 1][1] = - insertion.substring(0, insertion.length - overlapLength2); - diffs[pointer + 1][0] = DIFF_DELETE; - diffs[pointer + 1][1] = - deletion.substring(overlapLength2); - pointer++; - } - } - pointer++; - } - pointer++; - } - }; + var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + }; + + var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } - /** - * Determine if the suffix of one string is the prefix of another. - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {number} The number of characters common to the end of the first - * string and the start of the second string. - * @private - */ - DiffMatchPatch.prototype.diffCommonOverlap = function(text1, text2) { - var text1Length, text2Length, textLength, - best, length, pattern, found; - // Cache the text lengths to prevent multiple calls. - text1Length = text1.length; - text2Length = text2.length; - // Eliminate the null case. - if (text1Length === 0 || text2Length === 0) { - return 0; - } - // Truncate the longer string. - if (text1Length > text2Length) { - text1 = text1.substring(text1Length - text2Length); - } else if (text1Length < text2Length) { - text2 = text2.substring(0, text1Length); - } - textLength = Math.min(text1Length, text2Length); - // Quick check for the worst case. - if (text1 === text2) { - return textLength; - } - - // Start by looking for a single character match - // and increase length until no match is found. - // Performance analysis: http://neil.fraser.name/news/2010/11/04/ - best = 0; - length = 1; - while (true) { - pattern = text1.substring(textLength - length); - found = text2.indexOf(pattern); - if (found === -1) { - return best; - } - length += found; - if (found === 0 || text1.substring(textLength - length) === - text2.substring(0, length)) { - best = length; - length++; - } - } + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; }; + }(); - /** - * Split two texts into an array of strings. Reduce the texts to a string of - * hashes where each Unicode character represents one line. - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {{chars1: string, chars2: string, lineArray: !Array.}} - * An object containing the encoded text1, the encoded text2 and - * the array of unique strings. - * The zeroth element of the array of unique strings is intentionally blank. - * @private - */ - DiffMatchPatch.prototype.diffLinesToChars = function(text1, text2) { - var lineArray, lineHash, chars1, chars2; - lineArray = []; // e.g. lineArray[4] === 'Hello\n' - lineHash = {}; // e.g. lineHash['Hello\n'] === 4 - - // '\x00' is a valid character, but various debuggers don't like it. - // So we'll insert a junk entry to avoid generating a null character. - lineArray[0] = ""; - - /** - * Split a text into an array of strings. Reduce the texts to a string of - * hashes where each Unicode character represents one line. - * Modifies linearray and linehash through being a closure. - * @param {string} text String to encode. - * @return {string} Encoded string. - * @private - */ - function diffLinesToCharsMunge(text) { - var chars, lineStart, lineEnd, lineArrayLength, line; - chars = ""; - // Walk the text, pulling out a substring for each line. - // text.split('\n') would would temporarily double our memory footprint. - // Modifying text would create many large strings to garbage collect. - lineStart = 0; - lineEnd = -1; - // Keeping our own length variable is faster than looking it up. - lineArrayLength = lineArray.length; - while (lineEnd < text.length - 1) { - lineEnd = text.indexOf("\n", lineStart); - if (lineEnd === -1) { - lineEnd = text.length - 1; - } - line = text.substring(lineStart, lineEnd + 1); - lineStart = lineEnd + 1; - - if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : - (lineHash[line] !== undefined)) { - chars += String.fromCharCode( lineHash[ line ] ); - } else { - chars += String.fromCharCode(lineArrayLength); - lineHash[line] = lineArrayLength; - lineArray[lineArrayLength++] = line; - } - } - return chars; - } - - chars1 = diffLinesToCharsMunge(text1); - chars2 = diffLinesToCharsMunge(text2); - return { - chars1: chars1, - chars2: chars2, - lineArray: lineArray - }; - }; - /** - * Rehydrate the text in a diff from a string of line hashes to real lines of - * text. - * @param {!Array.} diffs Array of diff tuples. - * @param {!Array.} lineArray Array of unique strings. - * @private - */ - DiffMatchPatch.prototype.diffCharsToLines = function( diffs, lineArray ) { - var x, chars, text, y; - for ( x = 0; x < diffs.length; x++ ) { - chars = diffs[x][1]; - text = []; - for ( y = 0; y < chars.length; y++ ) { - text[y] = lineArray[chars.charCodeAt(y)]; - } - diffs[x][1] = text.join(""); - } - }; - /** - * Reorder and merge like edit sections. Merge equalities. - * Any edit section can move as long as it doesn't cross an equality. - * @param {!Array.} diffs Array of diff tuples. - */ - DiffMatchPatch.prototype.diffCleanupMerge = function(diffs) { - var pointer, countDelete, countInsert, textInsert, textDelete, - commonlength, changes; - diffs.push( [ DIFF_EQUAL, "" ] ); // Add a dummy entry at the end. - pointer = 0; - countDelete = 0; - countInsert = 0; - textDelete = ""; - textInsert = ""; - commonlength; - while (pointer < diffs.length) { - switch ( diffs[ pointer ][ 0 ] ) { - case DIFF_INSERT: - countInsert++; - textInsert += diffs[pointer][1]; - pointer++; - break; - case DIFF_DELETE: - countDelete++; - textDelete += diffs[pointer][1]; - pointer++; - break; - case DIFF_EQUAL: - // Upon reaching an equality, check for prior redundancies. - if (countDelete + countInsert > 1) { - if (countDelete !== 0 && countInsert !== 0) { - // Factor out any common prefixies. - commonlength = this.diffCommonPrefix(textInsert, textDelete); - if (commonlength !== 0) { - if ((pointer - countDelete - countInsert) > 0 && - diffs[pointer - countDelete - countInsert - 1][0] === - DIFF_EQUAL) { - diffs[pointer - countDelete - countInsert - 1][1] += - textInsert.substring(0, commonlength); - } else { - diffs.splice( 0, 0, [ DIFF_EQUAL, - textInsert.substring( 0, commonlength ) - ] ); - pointer++; - } - textInsert = textInsert.substring(commonlength); - textDelete = textDelete.substring(commonlength); - } - // Factor out any common suffixies. - commonlength = this.diffCommonSuffix(textInsert, textDelete); - if (commonlength !== 0) { - diffs[pointer][1] = textInsert.substring(textInsert.length - - commonlength) + diffs[pointer][1]; - textInsert = textInsert.substring(0, textInsert.length - - commonlength); - textDelete = textDelete.substring(0, textDelete.length - - commonlength); - } - } - // Delete the offending records and add the merged ones. - if (countDelete === 0) { - diffs.splice( pointer - countInsert, - countDelete + countInsert, [ DIFF_INSERT, textInsert ] ); - } else if (countInsert === 0) { - diffs.splice( pointer - countDelete, - countDelete + countInsert, [ DIFF_DELETE, textDelete ] ); - } else { - diffs.splice( pointer - countDelete - countInsert, - countDelete + countInsert, [ DIFF_DELETE, textDelete ], [ DIFF_INSERT, textInsert ] ); - } - pointer = pointer - countDelete - countInsert + - (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1; - } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) { - // Merge this equality with the previous one. - diffs[pointer - 1][1] += diffs[pointer][1]; - diffs.splice(pointer, 1); - } else { - pointer++; - } - countInsert = 0; - countDelete = 0; - textDelete = ""; - textInsert = ""; - break; - } - } - if (diffs[diffs.length - 1][1] === "") { - diffs.pop(); // Remove the dummy entry at the end. - } - - // Second pass: look for single edits surrounded on both sides by equalities - // which can be shifted sideways to eliminate an equality. - // e.g: ABAC -> ABAC - changes = false; - pointer = 1; - // Intentionally ignore the first and last element (don't need checking). - while (pointer < diffs.length - 1) { - if (diffs[pointer - 1][0] === DIFF_EQUAL && - diffs[pointer + 1][0] === DIFF_EQUAL) { - // This is a single edit surrounded by equalities. - if ( diffs[ pointer ][ 1 ].substring( diffs[ pointer ][ 1 ].length - - diffs[ pointer - 1 ][ 1 ].length ) === diffs[ pointer - 1 ][ 1 ] ) { - // Shift the edit over the previous equality. - diffs[pointer][1] = diffs[pointer - 1][1] + - diffs[pointer][1].substring(0, diffs[pointer][1].length - - diffs[pointer - 1][1].length); - diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; - diffs.splice(pointer - 1, 1); - changes = true; - } else if ( diffs[ pointer ][ 1 ].substring( 0, diffs[ pointer + 1 ][ 1 ].length ) === - diffs[ pointer + 1 ][ 1 ] ) { - // Shift the edit over the next equality. - diffs[pointer - 1][1] += diffs[pointer + 1][1]; - diffs[pointer][1] = - diffs[pointer][1].substring(diffs[pointer + 1][1].length) + - diffs[pointer + 1][1]; - diffs.splice(pointer + 1, 1); - changes = true; - } - } - pointer++; - } - // If shifts were made, the diff needs reordering and another shift sweep. - if (changes) { - this.diffCleanupMerge(diffs); - } - }; - return function(o, n) { - var diff, output, text; - diff = new DiffMatchPatch(); - output = diff.DiffMain(o, n); - //console.log(output); - diff.diffCleanupEfficiency(output); - text = diff.diffPrettyHtml(output); - return text; - }; -}()); -// jscs:enable - -(function() { - -// Deprecated QUnit.init - Ref #530 -// Re-initialize the configuration options -QUnit.init = function() { - var tests, banner, result, qunit, - config = QUnit.config; - - config.stats = { all: 0, bad: 0 }; - config.moduleStats = { all: 0, bad: 0 }; - config.started = 0; - config.updateRate = 1000; - config.blocking = false; - config.autostart = true; - config.autorun = false; - config.filter = ""; - config.queue = []; - - // Return on non-browser environments - // This is necessary to not break on node tests - if ( typeof window === "undefined" ) { - return; - } - - qunit = id( "qunit" ); - if ( qunit ) { - qunit.innerHTML = - "

    " + escapeText( document.title ) + "

    " + - "

    " + - "
    " + - "

    " + - "
      "; - } - - tests = id( "qunit-tests" ); - banner = id( "qunit-banner" ); - result = id( "qunit-testresult" ); - - if ( tests ) { - tests.innerHTML = ""; - } - - if ( banner ) { - banner.className = ""; - } - - if ( result ) { - result.parentNode.removeChild( result ); - } - - if ( tests ) { - result = document.createElement( "p" ); - result.id = "qunit-testresult"; - result.className = "result"; - tests.parentNode.insertBefore( result, tests ); - result.innerHTML = "Running...
       "; - } -}; - -// Don't load the HTML Reporter on non-Browser environments -if ( typeof window === "undefined" ) { - return; -} - -var config = QUnit.config, - hasOwn = Object.prototype.hasOwnProperty, - defined = { - document: window.document !== undefined, - sessionStorage: (function() { - var x = "qunit-test-string"; - try { - sessionStorage.setItem( x, x ); - sessionStorage.removeItem( x ); - return true; - } catch ( e ) { - return false; - } - }()) - }, - modulesList = []; - -/** -* Escape text for attribute or text content. -*/ -function escapeText( s ) { - if ( !s ) { - return ""; - } - s = s + ""; - - // Both single quotes and double quotes (for attributes) - return s.replace( /['"<>&]/g, function( s ) { - switch ( s ) { - case "'": - return "'"; - case "\"": - return """; - case "<": - return "<"; - case ">": - return ">"; - case "&": - return "&"; - } - }); -} - -/** - * @param {HTMLElement} elem - * @param {string} type - * @param {Function} fn - */ -function addEvent( elem, type, fn ) { - if ( elem.addEventListener ) { - - // Standards-based browsers - elem.addEventListener( type, fn, false ); - } else if ( elem.attachEvent ) { - - // support: IE <9 - elem.attachEvent( "on" + type, function() { - var event = window.event; - if ( !event.target ) { - event.target = event.srcElement || document; - } - - fn.call( elem, event ); - }); - } -} - -/** - * @param {Array|NodeList} elems - * @param {string} type - * @param {Function} fn - */ -function addEvents( elems, type, fn ) { - var i = elems.length; - while ( i-- ) { - addEvent( elems[ i ], type, fn ); - } -} - -function hasClass( elem, name ) { - return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0; -} - -function addClass( elem, name ) { - if ( !hasClass( elem, name ) ) { - elem.className += ( elem.className ? " " : "" ) + name; - } -} - -function toggleClass( elem, name ) { - if ( hasClass( elem, name ) ) { - removeClass( elem, name ); - } else { - addClass( elem, name ); - } -} - -function removeClass( elem, name ) { - var set = " " + elem.className + " "; - - // Class name may appear multiple times - while ( set.indexOf( " " + name + " " ) >= 0 ) { - set = set.replace( " " + name + " ", " " ); - } - - // trim for prettiness - elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" ); -} - -function id( name ) { - return defined.document && document.getElementById && document.getElementById( name ); -} - -function getUrlConfigHtml() { - var i, j, val, - escaped, escapedTooltip, - selection = false, - len = config.urlConfig.length, - urlConfigHtml = ""; - - for ( i = 0; i < len; i++ ) { - val = config.urlConfig[ i ]; - if ( typeof val === "string" ) { - val = { - id: val, - label: val - }; - } - - escaped = escapeText( val.id ); - escapedTooltip = escapeText( val.tooltip ); - - if ( config[ val.id ] === undefined ) { - config[ val.id ] = QUnit.urlParams[ val.id ]; - } - - if ( !val.value || typeof val.value === "string" ) { - urlConfigHtml += ""; - } else { - urlConfigHtml += ""; - } - } - - return urlConfigHtml; -} - -// Handle "click" events on toolbar checkboxes and "change" for select menus. -// Updates the URL with the new state of `config.urlConfig` values. -function toolbarChanged() { - var updatedUrl, value, - field = this, - params = {}; - - // Detect if field is a select menu or a checkbox - if ( "selectedIndex" in field ) { - value = field.options[ field.selectedIndex ].value || undefined; - } else { - value = field.checked ? ( field.defaultValue || true ) : undefined; - } - - params[ field.name ] = value; - updatedUrl = setUrl( params ); - - if ( "hidepassed" === field.name && "replaceState" in window.history ) { - config[ field.name ] = value || false; - if ( value ) { - addClass( id( "qunit-tests" ), "hidepass" ); - } else { - removeClass( id( "qunit-tests" ), "hidepass" ); - } - - // It is not necessary to refresh the whole page - window.history.replaceState( null, "", updatedUrl ); - } else { - window.location = updatedUrl; - } -} - -function setUrl( params ) { - var key, - querystring = "?"; - - params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params ); - - for ( key in params ) { - if ( hasOwn.call( params, key ) ) { - if ( params[ key ] === undefined ) { - continue; - } - querystring += encodeURIComponent( key ); - if ( params[ key ] !== true ) { - querystring += "=" + encodeURIComponent( params[ key ] ); - } - querystring += "&"; - } - } - return location.protocol + "//" + location.host + - location.pathname + querystring.slice( 0, -1 ); -} - -function applyUrlParams() { - var selectedModule, - modulesList = id( "qunit-modulefilter" ), - filter = id( "qunit-filter-input" ).value; - - selectedModule = modulesList ? - decodeURIComponent( modulesList.options[ modulesList.selectedIndex ].value ) : - undefined; - - window.location = setUrl({ - module: ( selectedModule === "" ) ? undefined : selectedModule, - filter: ( filter === "" ) ? undefined : filter, - - // Remove testId filter - testId: undefined - }); -} - -function toolbarUrlConfigContainer() { - var urlConfigContainer = document.createElement( "span" ); - - urlConfigContainer.innerHTML = getUrlConfigHtml(); - addClass( urlConfigContainer, "qunit-url-config" ); - - // For oldIE support: - // * Add handlers to the individual elements instead of the container - // * Use "click" instead of "change" for checkboxes - addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged ); - addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged ); - - return urlConfigContainer; -} - -function toolbarLooseFilter() { - var filter = document.createElement( "form" ), - label = document.createElement( "label" ), - input = document.createElement( "input" ), - button = document.createElement( "button" ); - - addClass( filter, "qunit-filter" ); - - label.innerHTML = "Filter: "; - - input.type = "text"; - input.value = config.filter || ""; - input.name = "filter"; - input.id = "qunit-filter-input"; - - button.innerHTML = "Go"; - - label.appendChild( input ); - - filter.appendChild( label ); - filter.appendChild( button ); - addEvent( filter, "submit", function( ev ) { - applyUrlParams(); - - if ( ev && ev.preventDefault ) { - ev.preventDefault(); - } - - return false; - }); - - return filter; -} - -function toolbarModuleFilterHtml() { - var i, - moduleFilterHtml = ""; - - if ( !modulesList.length ) { - return false; - } - - modulesList.sort(function( a, b ) { - return a.localeCompare( b ); - }); - - moduleFilterHtml += "" + - ""; - - return moduleFilterHtml; -} - -function toolbarModuleFilter() { - var toolbar = id( "qunit-testrunner-toolbar" ), - moduleFilter = document.createElement( "span" ), - moduleFilterHtml = toolbarModuleFilterHtml(); - - if ( !toolbar || !moduleFilterHtml ) { - return false; - } - - moduleFilter.setAttribute( "id", "qunit-modulefilter-container" ); - moduleFilter.innerHTML = moduleFilterHtml; - - addEvent( moduleFilter.lastChild, "change", applyUrlParams ); - - toolbar.appendChild( moduleFilter ); -} - -function appendToolbar() { - var toolbar = id( "qunit-testrunner-toolbar" ); - - if ( toolbar ) { - toolbar.appendChild( toolbarUrlConfigContainer() ); - toolbar.appendChild( toolbarLooseFilter() ); - } -} - -function appendHeader() { - var header = id( "qunit-header" ); - - if ( header ) { - header.innerHTML = "" + header.innerHTML + " "; - } -} - -function appendBanner() { - var banner = id( "qunit-banner" ); - - if ( banner ) { - banner.className = ""; - } -} - -function appendTestResults() { - var tests = id( "qunit-tests" ), - result = id( "qunit-testresult" ); - - if ( result ) { - result.parentNode.removeChild( result ); - } - - if ( tests ) { - tests.innerHTML = ""; - result = document.createElement( "p" ); - result.id = "qunit-testresult"; - result.className = "result"; - tests.parentNode.insertBefore( result, tests ); - result.innerHTML = "Running...
       "; - } -} - -function storeFixture() { - var fixture = id( "qunit-fixture" ); - if ( fixture ) { - config.fixture = fixture.innerHTML; - } -} - -function appendUserAgent() { - var userAgent = id( "qunit-userAgent" ); - - if ( userAgent ) { - userAgent.innerHTML = ""; - userAgent.appendChild( - document.createTextNode( - "QUnit " + QUnit.version + "; " + navigator.userAgent - ) - ); - } -} - -function appendTestsList( modules ) { - var i, l, x, z, test, moduleObj; - - for ( i = 0, l = modules.length; i < l; i++ ) { - moduleObj = modules[ i ]; - - if ( moduleObj.name ) { - modulesList.push( moduleObj.name ); - } - - for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) { - test = moduleObj.tests[ x ]; - - appendTest( test.name, test.testId, moduleObj.name ); - } - } -} - -function appendTest( name, testId, moduleName ) { - var title, rerunTrigger, testBlock, assertList, - tests = id( "qunit-tests" ); - - if ( !tests ) { - return; - } - - title = document.createElement( "strong" ); - title.innerHTML = getNameHtml( name, moduleName ); - - rerunTrigger = document.createElement( "a" ); - rerunTrigger.innerHTML = "Rerun"; - rerunTrigger.href = setUrl({ testId: testId }); - - testBlock = document.createElement( "li" ); - testBlock.appendChild( title ); - testBlock.appendChild( rerunTrigger ); - testBlock.id = "qunit-test-output-" + testId; - - assertList = document.createElement( "ol" ); - assertList.className = "qunit-assert-list"; - - testBlock.appendChild( assertList ); - - tests.appendChild( testBlock ); -} - -// HTML Reporter initialization and load -QUnit.begin(function( details ) { - var qunit = id( "qunit" ); - - // Fixture is the only one necessary to run without the #qunit element - storeFixture(); - - if ( qunit ) { - qunit.innerHTML = - "

      " + escapeText( document.title ) + "

      " + - "

      " + - "
      " + - "

      " + - "
        "; - } - - appendHeader(); - appendBanner(); - appendTestResults(); - appendUserAgent(); - appendToolbar(); - appendTestsList( details.modules ); - toolbarModuleFilter(); - - if ( qunit && config.hidepassed ) { - addClass( qunit.lastChild, "hidepass" ); - } -}); - -QUnit.done(function( details ) { - var i, key, - banner = id( "qunit-banner" ), - tests = id( "qunit-tests" ), - html = [ - "Tests completed in ", - details.runtime, - " milliseconds.
        ", - "", - details.passed, - " assertions of ", - details.total, - " passed, ", - details.failed, - " failed." - ].join( "" ); - - if ( banner ) { - banner.className = details.failed ? "qunit-fail" : "qunit-pass"; - } - - if ( tests ) { - id( "qunit-testresult" ).innerHTML = html; - } - - if ( config.altertitle && defined.document && document.title ) { - - // show ✖ for good, ✔ for bad suite result in title - // use escape sequences in case file gets loaded with non-utf-8-charset - document.title = [ - ( details.failed ? "\u2716" : "\u2714" ), - document.title.replace( /^[\u2714\u2716] /i, "" ) - ].join( " " ); - } - - // clear own sessionStorage items if all tests passed - if ( config.reorder && defined.sessionStorage && details.failed === 0 ) { - for ( i = 0; i < sessionStorage.length; i++ ) { - key = sessionStorage.key( i++ ); - if ( key.indexOf( "qunit-test-" ) === 0 ) { - sessionStorage.removeItem( key ); - } - } - } - - // scroll back to top to show results - if ( config.scrolltop && window.scrollTo ) { - window.scrollTo( 0, 0 ); - } -}); - -function getNameHtml( name, module ) { - var nameHtml = ""; - - if ( module ) { - nameHtml = "" + escapeText( module ) + ": "; - } - - nameHtml += "" + escapeText( name ) + ""; - - return nameHtml; -} - -QUnit.testStart(function( details ) { - var running, testBlock, bad; - - testBlock = id( "qunit-test-output-" + details.testId ); - if ( testBlock ) { - testBlock.className = "running"; - } else { - - // Report later registered tests - appendTest( details.name, details.testId, details.module ); - } - - running = id( "qunit-testresult" ); - if ( running ) { - bad = QUnit.config.reorder && defined.sessionStorage && - +sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name ); - - running.innerHTML = ( bad ? - "Rerunning previously failed test:
        " : - "Running:
        " ) + - getNameHtml( details.name, details.module ); - } - -}); - -QUnit.log(function( details ) { - var assertList, assertLi, - message, expected, actual, - testItem = id( "qunit-test-output-" + details.testId ); - - if ( !testItem ) { - return; - } - - message = escapeText( details.message ) || ( details.result ? "okay" : "failed" ); - message = "" + message + ""; - message += "@ " + details.runtime + " ms"; - - // pushFailure doesn't provide details.expected - // when it calls, it's implicit to also not show expected and diff stuff - // Also, we need to check details.expected existence, as it can exist and be undefined - if ( !details.result && hasOwn.call( details, "expected" ) ) { - expected = escapeText( QUnit.dump.parse( details.expected ) ); - actual = escapeText( QUnit.dump.parse( details.actual ) ); - message += ""; - - if ( actual !== expected ) { - message += "" + - ""; - } else { - if ( expected.indexOf( "[object Array]" ) !== -1 || - expected.indexOf( "[object Object]" ) !== -1 ) { - message += ""; - } - } - - if ( details.source ) { - message += ""; - } - - message += "
        Expected:
        " +
        -			expected +
        -			"
        Result:
        " +
        -				actual + "
        Diff:
        " +
        -				QUnit.diff( expected, actual ) + "
        Message: " + - "Diff suppressed as the depth of object is more than current max depth (" + - QUnit.config.maxDepth + ").

        Hint: Use QUnit.dump.maxDepth to " + - " run with a higher max depth or " + - "Rerun without max depth.

        Source:
        " +
        -				escapeText( details.source ) + "
        "; - - // this occours when pushFailure is set and we have an extracted stack trace - } else if ( !details.result && details.source ) { - message += "" + - "" + - "
        Source:
        " +
        -			escapeText( details.source ) + "
        "; - } - - assertList = testItem.getElementsByTagName( "ol" )[ 0 ]; - - assertLi = document.createElement( "li" ); - assertLi.className = details.result ? "pass" : "fail"; - assertLi.innerHTML = message; - assertList.appendChild( assertLi ); -}); - -QUnit.testDone(function( details ) { - var testTitle, time, testItem, assertList, - good, bad, testCounts, skipped, - tests = id( "qunit-tests" ); - - if ( !tests ) { - return; - } - - testItem = id( "qunit-test-output-" + details.testId ); - - assertList = testItem.getElementsByTagName( "ol" )[ 0 ]; - - good = details.passed; - bad = details.failed; - - // store result when possible - if ( config.reorder && defined.sessionStorage ) { - if ( bad ) { - sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad ); - } else { - sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name ); - } - } - - if ( bad === 0 ) { - addClass( assertList, "qunit-collapsed" ); - } - - // testItem.firstChild is the test name - testTitle = testItem.firstChild; - - testCounts = bad ? - "" + bad + ", " + "" + good + ", " : - ""; - - testTitle.innerHTML += " (" + testCounts + - details.assertions.length + ")"; - - if ( details.skipped ) { - testItem.className = "skipped"; - skipped = document.createElement( "em" ); - skipped.className = "qunit-skipped-label"; - skipped.innerHTML = "skipped"; - testItem.insertBefore( skipped, testTitle ); - } else { - addEvent( testTitle, "click", function() { - toggleClass( assertList, "qunit-collapsed" ); - }); - - testItem.className = bad ? "fail" : "pass"; - - time = document.createElement( "span" ); - time.className = "runtime"; - time.innerHTML = details.runtime + " ms"; - testItem.insertBefore( time, assertList ); - } -}); - -if ( defined.document ) { - if ( document.readyState === "complete" ) { - QUnit.load(); - } else { - addEvent( window, "load", QUnit.load ); - } -} else { - config.pageLoaded = true; - config.autorun = true; -} - -})(); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + var toConsumableArray = function (arr) { + if (Array.isArray(arr)) { + for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; + + return arr2; + } else { + return Array.from(arr); + } + }; + + var toString = Object.prototype.toString; + var hasOwn = Object.prototype.hasOwnProperty; + var now = Date.now || function () { + return new Date().getTime(); + }; + + var defined = { + document: window && window.document !== undefined, + setTimeout: setTimeout !== undefined + }; + + // Returns a new Array with the elements that are in a but not in b + function diff(a, b) { + var i, + j, + result = a.slice(); + + for (i = 0; i < result.length; i++) { + for (j = 0; j < b.length; j++) { + if (result[i] === b[j]) { + result.splice(i, 1); + i--; + break; + } + } + } + return result; + } + + /** + * Determines whether an element exists in a given array or not. + * + * @method inArray + * @param {Any} elem + * @param {Array} array + * @return {Boolean} + */ + function inArray(elem, array) { + return array.indexOf(elem) !== -1; + } + + /** + * Makes a clone of an object using only Array or Object as base, + * and copies over the own enumerable properties. + * + * @param {Object} obj + * @return {Object} New object with only the own properties (recursively). + */ + function objectValues(obj) { + var key, + val, + vals = is("array", obj) ? [] : {}; + for (key in obj) { + if (hasOwn.call(obj, key)) { + val = obj[key]; + vals[key] = val === Object(val) ? objectValues(val) : val; + } + } + return vals; + } + + function extend(a, b, undefOnly) { + for (var prop in b) { + if (hasOwn.call(b, prop)) { + if (b[prop] === undefined) { + delete a[prop]; + } else if (!(undefOnly && typeof a[prop] !== "undefined")) { + a[prop] = b[prop]; + } + } + } + + return a; + } + + function objectType(obj) { + if (typeof obj === "undefined") { + return "undefined"; + } + + // Consider: typeof null === object + if (obj === null) { + return "null"; + } + + var match = toString.call(obj).match(/^\[object\s(.*)\]$/), + type = match && match[1]; + + switch (type) { + case "Number": + if (isNaN(obj)) { + return "nan"; + } + return "number"; + case "String": + case "Boolean": + case "Array": + case "Set": + case "Map": + case "Date": + case "RegExp": + case "Function": + case "Symbol": + return type.toLowerCase(); + default: + return typeof obj === "undefined" ? "undefined" : _typeof(obj); + } + } + + // Safe object type checking + function is(type, obj) { + return objectType(obj) === type; + } + + // Based on Java's String.hashCode, a simple but not + // rigorously collision resistant hashing function + function generateHash(module, testName) { + var str = module + "\x1C" + testName; + var hash = 0; + + for (var i = 0; i < str.length; i++) { + hash = (hash << 5) - hash + str.charCodeAt(i); + hash |= 0; + } + + // Convert the possibly negative integer hash code into an 8 character hex string, which isn't + // strictly necessary but increases user understanding that the id is a SHA-like hash + var hex = (0x100000000 + hash).toString(16); + if (hex.length < 8) { + hex = "0000000" + hex; + } + + return hex.slice(-8); + } + + // Test for equality any JavaScript type. + // Authors: Philippe Rathé , David Chan + var equiv = (function () { + + // Value pairs queued for comparison. Used for breadth-first processing order, recursion + // detection and avoiding repeated comparison (see below for details). + // Elements are { a: val, b: val }. + var pairs = []; + + var getProto = Object.getPrototypeOf || function (obj) { + return obj.__proto__; + }; + + function useStrictEquality(a, b) { + + // This only gets called if a and b are not strict equal, and is used to compare on + // the primitive values inside object wrappers. For example: + // `var i = 1;` + // `var j = new Number(1);` + // Neither a nor b can be null, as a !== b and they have the same type. + if ((typeof a === "undefined" ? "undefined" : _typeof(a)) === "object") { + a = a.valueOf(); + } + if ((typeof b === "undefined" ? "undefined" : _typeof(b)) === "object") { + b = b.valueOf(); + } + + return a === b; + } + + function compareConstructors(a, b) { + var protoA = getProto(a); + var protoB = getProto(b); + + // Comparing constructors is more strict than using `instanceof` + if (a.constructor === b.constructor) { + return true; + } + + // Ref #851 + // If the obj prototype descends from a null constructor, treat it + // as a null prototype. + if (protoA && protoA.constructor === null) { + protoA = null; + } + if (protoB && protoB.constructor === null) { + protoB = null; + } + + // Allow objects with no prototype to be equivalent to + // objects with Object as their constructor. + if (protoA === null && protoB === Object.prototype || protoB === null && protoA === Object.prototype) { + return true; + } + + return false; + } + + function getRegExpFlags(regexp) { + return "flags" in regexp ? regexp.flags : regexp.toString().match(/[gimuy]*$/)[0]; + } + + function isContainer(val) { + return ["object", "array", "map", "set"].indexOf(objectType(val)) !== -1; + } + + function breadthFirstCompareChild(a, b) { + + // If a is a container not reference-equal to b, postpone the comparison to the + // end of the pairs queue -- unless (a, b) has been seen before, in which case skip + // over the pair. + if (a === b) { + return true; + } + if (!isContainer(a)) { + return typeEquiv(a, b); + } + if (pairs.every(function (pair) { + return pair.a !== a || pair.b !== b; + })) { + + // Not yet started comparing this pair + pairs.push({ a: a, b: b }); + } + return true; + } + + var callbacks = { + "string": useStrictEquality, + "boolean": useStrictEquality, + "number": useStrictEquality, + "null": useStrictEquality, + "undefined": useStrictEquality, + "symbol": useStrictEquality, + "date": useStrictEquality, + + "nan": function nan() { + return true; + }, + + "regexp": function regexp(a, b) { + return a.source === b.source && + + // Include flags in the comparison + getRegExpFlags(a) === getRegExpFlags(b); + }, + + // abort (identical references / instance methods were skipped earlier) + "function": function _function() { + return false; + }, + + "array": function array(a, b) { + var i, len; + + len = a.length; + if (len !== b.length) { + + // Safe and faster + return false; + } + + for (i = 0; i < len; i++) { + + // Compare non-containers; queue non-reference-equal containers + if (!breadthFirstCompareChild(a[i], b[i])) { + return false; + } + } + return true; + }, + + // Define sets a and b to be equivalent if for each element aVal in a, there + // is some element bVal in b such that aVal and bVal are equivalent. Element + // repetitions are not counted, so these are equivalent: + // a = new Set( [ {}, [], [] ] ); + // b = new Set( [ {}, {}, [] ] ); + "set": function set$$1(a, b) { + var innerEq, + outerEq = true; + + if (a.size !== b.size) { + + // This optimization has certain quirks because of the lack of + // repetition counting. For instance, adding the same + // (reference-identical) element to two equivalent sets can + // make them non-equivalent. + return false; + } + + a.forEach(function (aVal) { + + // Short-circuit if the result is already known. (Using for...of + // with a break clause would be cleaner here, but it would cause + // a syntax error on older Javascript implementations even if + // Set is unused) + if (!outerEq) { + return; + } + + innerEq = false; + + b.forEach(function (bVal) { + var parentPairs; + + // Likewise, short-circuit if the result is already known + if (innerEq) { + return; + } + + // Swap out the global pairs list, as the nested call to + // innerEquiv will clobber its contents + parentPairs = pairs; + if (innerEquiv(bVal, aVal)) { + innerEq = true; + } + + // Replace the global pairs list + pairs = parentPairs; + }); + + if (!innerEq) { + outerEq = false; + } + }); + + return outerEq; + }, + + // Define maps a and b to be equivalent if for each key-value pair (aKey, aVal) + // in a, there is some key-value pair (bKey, bVal) in b such that + // [ aKey, aVal ] and [ bKey, bVal ] are equivalent. Key repetitions are not + // counted, so these are equivalent: + // a = new Map( [ [ {}, 1 ], [ {}, 1 ], [ [], 1 ] ] ); + // b = new Map( [ [ {}, 1 ], [ [], 1 ], [ [], 1 ] ] ); + "map": function map(a, b) { + var innerEq, + outerEq = true; + + if (a.size !== b.size) { + + // This optimization has certain quirks because of the lack of + // repetition counting. For instance, adding the same + // (reference-identical) key-value pair to two equivalent maps + // can make them non-equivalent. + return false; + } + + a.forEach(function (aVal, aKey) { + + // Short-circuit if the result is already known. (Using for...of + // with a break clause would be cleaner here, but it would cause + // a syntax error on older Javascript implementations even if + // Map is unused) + if (!outerEq) { + return; + } + + innerEq = false; + + b.forEach(function (bVal, bKey) { + var parentPairs; + + // Likewise, short-circuit if the result is already known + if (innerEq) { + return; + } + + // Swap out the global pairs list, as the nested call to + // innerEquiv will clobber its contents + parentPairs = pairs; + if (innerEquiv([bVal, bKey], [aVal, aKey])) { + innerEq = true; + } + + // Replace the global pairs list + pairs = parentPairs; + }); + + if (!innerEq) { + outerEq = false; + } + }); + + return outerEq; + }, + + "object": function object(a, b) { + var i, + aProperties = [], + bProperties = []; + + if (compareConstructors(a, b) === false) { + return false; + } + + // Be strict: don't ensure hasOwnProperty and go deep + for (i in a) { + + // Collect a's properties + aProperties.push(i); + + // Skip OOP methods that look the same + if (a.constructor !== Object && typeof a.constructor !== "undefined" && typeof a[i] === "function" && typeof b[i] === "function" && a[i].toString() === b[i].toString()) { + continue; + } + + // Compare non-containers; queue non-reference-equal containers + if (!breadthFirstCompareChild(a[i], b[i])) { + return false; + } + } + + for (i in b) { + + // Collect b's properties + bProperties.push(i); + } + + // Ensures identical properties name + return typeEquiv(aProperties.sort(), bProperties.sort()); + } + }; + + function typeEquiv(a, b) { + var type = objectType(a); + + // Callbacks for containers will append to the pairs queue to achieve breadth-first + // search order. The pairs queue is also used to avoid reprocessing any pair of + // containers that are reference-equal to a previously visited pair (a special case + // this being recursion detection). + // + // Because of this approach, once typeEquiv returns a false value, it should not be + // called again without clearing the pair queue else it may wrongly report a visited + // pair as being equivalent. + return objectType(b) === type && callbacks[type](a, b); + } + + function innerEquiv(a, b) { + var i, pair; + + // We're done when there's nothing more to compare + if (arguments.length < 2) { + return true; + } + + // Clear the global pair queue and add the top-level values being compared + pairs = [{ a: a, b: b }]; + + for (i = 0; i < pairs.length; i++) { + pair = pairs[i]; + + // Perform type-specific comparison on any pairs that are not strictly + // equal. For container types, that comparison will postpone comparison + // of any sub-container pair to the end of the pair queue. This gives + // breadth-first search order. It also avoids the reprocessing of + // reference-equal siblings, cousins etc, which can have a significant speed + // impact when comparing a container of small objects each of which has a + // reference to the same (singleton) large object. + if (pair.a !== pair.b && !typeEquiv(pair.a, pair.b)) { + return false; + } + } + + // ...across all consecutive argument pairs + return arguments.length === 2 || innerEquiv.apply(this, [].slice.call(arguments, 1)); + } + + return function () { + var result = innerEquiv.apply(undefined, arguments); + + // Release any retained objects + pairs.length = 0; + return result; + }; + })(); + + /** + * Config object: Maintain internal state + * Later exposed as QUnit.config + * `config` initialized at top of scope + */ + var config = { + + // The queue of tests to run + queue: [], + + // Block until document ready + blocking: true, + + // By default, run previously failed tests first + // very useful in combination with "Hide passed tests" checked + reorder: true, + + // By default, modify document.title when suite is done + altertitle: true, + + // HTML Reporter: collapse every test except the first failing test + // If false, all failing tests will be expanded + collapse: true, + + // By default, scroll to top of the page when suite is done + scrolltop: true, + + // Depth up-to which object will be dumped + maxDepth: 5, + + // When enabled, all tests must call expect() + requireExpects: false, + + // Placeholder for user-configurable form-exposed URL parameters + urlConfig: [], + + // Set of all modules. + modules: [], + + // The first unnamed module + currentModule: { + name: "", + tests: [], + childModules: [], + testsRun: 0, + unskippedTestsRun: 0, + hooks: { + before: [], + beforeEach: [], + afterEach: [], + after: [] + } + }, + + callbacks: {}, + + // The storage module to use for reordering tests + storage: localSessionStorage + }; + + // take a predefined QUnit.config and extend the defaults + var globalConfig = window && window.QUnit && window.QUnit.config; + + // only extend the global config if there is no QUnit overload + if (window && window.QUnit && !window.QUnit.version) { + extend(config, globalConfig); + } + + // Push a loose unnamed module to the modules collection + config.modules.push(config.currentModule); + + // Based on jsDump by Ariel Flesler + // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html + var dump = (function () { + function quote(str) { + return "\"" + str.toString().replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\""; + } + function literal(o) { + return o + ""; + } + function join(pre, arr, post) { + var s = dump.separator(), + base = dump.indent(), + inner = dump.indent(1); + if (arr.join) { + arr = arr.join("," + s + inner); + } + if (!arr) { + return pre + post; + } + return [pre, inner + arr, base + post].join(s); + } + function array(arr, stack) { + var i = arr.length, + ret = new Array(i); + + if (dump.maxDepth && dump.depth > dump.maxDepth) { + return "[object Array]"; + } + + this.up(); + while (i--) { + ret[i] = this.parse(arr[i], undefined, stack); + } + this.down(); + return join("[", ret, "]"); + } + + function isArray(obj) { + return ( + + //Native Arrays + toString.call(obj) === "[object Array]" || + + // NodeList objects + typeof obj.length === "number" && obj.item !== undefined && (obj.length ? obj.item(0) === obj[0] : obj.item(0) === null && obj[0] === undefined) + ); + } + + var reName = /^function (\w+)/, + dump = { + + // The objType is used mostly internally, you can fix a (custom) type in advance + parse: function parse(obj, objType, stack) { + stack = stack || []; + var res, + parser, + parserType, + objIndex = stack.indexOf(obj); + + if (objIndex !== -1) { + return "recursion(" + (objIndex - stack.length) + ")"; + } + + objType = objType || this.typeOf(obj); + parser = this.parsers[objType]; + parserType = typeof parser === "undefined" ? "undefined" : _typeof(parser); + + if (parserType === "function") { + stack.push(obj); + res = parser.call(this, obj, stack); + stack.pop(); + return res; + } + return parserType === "string" ? parser : this.parsers.error; + }, + typeOf: function typeOf(obj) { + var type; + + if (obj === null) { + type = "null"; + } else if (typeof obj === "undefined") { + type = "undefined"; + } else if (is("regexp", obj)) { + type = "regexp"; + } else if (is("date", obj)) { + type = "date"; + } else if (is("function", obj)) { + type = "function"; + } else if (obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined) { + type = "window"; + } else if (obj.nodeType === 9) { + type = "document"; + } else if (obj.nodeType) { + type = "node"; + } else if (isArray(obj)) { + type = "array"; + } else if (obj.constructor === Error.prototype.constructor) { + type = "error"; + } else { + type = typeof obj === "undefined" ? "undefined" : _typeof(obj); + } + return type; + }, + + separator: function separator() { + if (this.multiline) { + return this.HTML ? "
        " : "\n"; + } else { + return this.HTML ? " " : " "; + } + }, + + // Extra can be a number, shortcut for increasing-calling-decreasing + indent: function indent(extra) { + if (!this.multiline) { + return ""; + } + var chr = this.indentChar; + if (this.HTML) { + chr = chr.replace(/\t/g, " ").replace(/ /g, " "); + } + return new Array(this.depth + (extra || 0)).join(chr); + }, + up: function up(a) { + this.depth += a || 1; + }, + down: function down(a) { + this.depth -= a || 1; + }, + setParser: function setParser(name, parser) { + this.parsers[name] = parser; + }, + + // The next 3 are exposed so you can use them + quote: quote, + literal: literal, + join: join, + depth: 1, + maxDepth: config.maxDepth, + + // This is the list of parsers, to modify them, use dump.setParser + parsers: { + window: "[Window]", + document: "[Document]", + error: function error(_error) { + return "Error(\"" + _error.message + "\")"; + }, + unknown: "[Unknown]", + "null": "null", + "undefined": "undefined", + "function": function _function(fn) { + var ret = "function", + + + // Functions never have name in IE + name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1]; + + if (name) { + ret += " " + name; + } + ret += "("; + + ret = [ret, dump.parse(fn, "functionArgs"), "){"].join(""); + return join(ret, dump.parse(fn, "functionCode"), "}"); + }, + array: array, + nodelist: array, + "arguments": array, + object: function object(map, stack) { + var keys, + key, + val, + i, + nonEnumerableProperties, + ret = []; + + if (dump.maxDepth && dump.depth > dump.maxDepth) { + return "[object Object]"; + } + + dump.up(); + keys = []; + for (key in map) { + keys.push(key); + } + + // Some properties are not always enumerable on Error objects. + nonEnumerableProperties = ["message", "name"]; + for (i in nonEnumerableProperties) { + key = nonEnumerableProperties[i]; + if (key in map && !inArray(key, keys)) { + keys.push(key); + } + } + keys.sort(); + for (i = 0; i < keys.length; i++) { + key = keys[i]; + val = map[key]; + ret.push(dump.parse(key, "key") + ": " + dump.parse(val, undefined, stack)); + } + dump.down(); + return join("{", ret, "}"); + }, + node: function node(_node) { + var len, + i, + val, + open = dump.HTML ? "<" : "<", + close = dump.HTML ? ">" : ">", + tag = _node.nodeName.toLowerCase(), + ret = open + tag, + attrs = _node.attributes; + + if (attrs) { + for (i = 0, len = attrs.length; i < len; i++) { + val = attrs[i].nodeValue; + + // IE6 includes all attributes in .attributes, even ones not explicitly + // set. Those have values like undefined, null, 0, false, "" or + // "inherit". + if (val && val !== "inherit") { + ret += " " + attrs[i].nodeName + "=" + dump.parse(val, "attribute"); + } + } + } + ret += close; + + // Show content of TextNode or CDATASection + if (_node.nodeType === 3 || _node.nodeType === 4) { + ret += _node.nodeValue; + } + + return ret + open + "/" + tag + close; + }, + + // Function calls it internally, it's the arguments part of the function + functionArgs: function functionArgs(fn) { + var args, + l = fn.length; + + if (!l) { + return ""; + } + + args = new Array(l); + while (l--) { + + // 97 is 'a' + args[l] = String.fromCharCode(97 + l); + } + return " " + args.join(", ") + " "; + }, + + // Object calls it internally, the key part of an item in a map + key: quote, + + // Function calls it internally, it's the content of the function + functionCode: "[code]", + + // Node calls it internally, it's a html attribute value + attribute: quote, + string: quote, + date: quote, + regexp: literal, + number: literal, + "boolean": literal, + symbol: function symbol(sym) { + return sym.toString(); + } + }, + + // If true, entities are escaped ( <, >, \t, space and \n ) + HTML: false, + + // Indentation unit + indentChar: " ", + + // If true, items in a collection, are separated by a \n, else just a space. + multiline: true + }; + + return dump; + })(); + + var LISTENERS = Object.create(null); + var SUPPORTED_EVENTS = ["runStart", "suiteStart", "testStart", "assertion", "testEnd", "suiteEnd", "runEnd"]; + + /** + * Emits an event with the specified data to all currently registered listeners. + * Callbacks will fire in the order in which they are registered (FIFO). This + * function is not exposed publicly; it is used by QUnit internals to emit + * logging events. + * + * @private + * @method emit + * @param {String} eventName + * @param {Object} data + * @return {Void} + */ + function emit(eventName, data) { + if (objectType(eventName) !== "string") { + throw new TypeError("eventName must be a string when emitting an event"); + } + + // Clone the callbacks in case one of them registers a new callback + var originalCallbacks = LISTENERS[eventName]; + var callbacks = originalCallbacks ? [].concat(toConsumableArray(originalCallbacks)) : []; + + for (var i = 0; i < callbacks.length; i++) { + callbacks[i](data); + } + } + + /** + * Registers a callback as a listener to the specified event. + * + * @public + * @method on + * @param {String} eventName + * @param {Function} callback + * @return {Void} + */ + function on(eventName, callback) { + if (objectType(eventName) !== "string") { + throw new TypeError("eventName must be a string when registering a listener"); + } else if (!inArray(eventName, SUPPORTED_EVENTS)) { + var events = SUPPORTED_EVENTS.join(", "); + throw new Error("\"" + eventName + "\" is not a valid event; must be one of: " + events + "."); + } else if (objectType(callback) !== "function") { + throw new TypeError("callback must be a function when registering a listener"); + } + + if (!LISTENERS[eventName]) { + LISTENERS[eventName] = []; + } + + // Don't register the same callback more than once + if (!inArray(callback, LISTENERS[eventName])) { + LISTENERS[eventName].push(callback); + } + } + + // Register logging callbacks + function registerLoggingCallbacks(obj) { + var i, + l, + key, + callbackNames = ["begin", "done", "log", "testStart", "testDone", "moduleStart", "moduleDone"]; + + function registerLoggingCallback(key) { + var loggingCallback = function loggingCallback(callback) { + if (objectType(callback) !== "function") { + throw new Error("QUnit logging methods require a callback function as their first parameters."); + } + + config.callbacks[key].push(callback); + }; + + return loggingCallback; + } + + for (i = 0, l = callbackNames.length; i < l; i++) { + key = callbackNames[i]; + + // Initialize key collection of logging callback + if (objectType(config.callbacks[key]) === "undefined") { + config.callbacks[key] = []; + } + + obj[key] = registerLoggingCallback(key); + } + } + + function runLoggingCallbacks(key, args) { + var i, l, callbacks; + + callbacks = config.callbacks[key]; + for (i = 0, l = callbacks.length; i < l; i++) { + callbacks[i](args); + } + } + + // Doesn't support IE9, it will return undefined on these browsers + // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack + var fileName = (sourceFromStacktrace(0) || "").replace(/(:\d+)+\)?/, "").replace(/.+\//, ""); + + function extractStacktrace(e, offset) { + offset = offset === undefined ? 4 : offset; + + var stack, include, i; + + if (e && e.stack) { + stack = e.stack.split("\n"); + if (/^error$/i.test(stack[0])) { + stack.shift(); + } + if (fileName) { + include = []; + for (i = offset; i < stack.length; i++) { + if (stack[i].indexOf(fileName) !== -1) { + break; + } + include.push(stack[i]); + } + if (include.length) { + return include.join("\n"); + } + } + return stack[offset]; + } + } + + function sourceFromStacktrace(offset) { + var error = new Error(); + + // Support: Safari <=7 only, IE <=10 - 11 only + // Not all browsers generate the `stack` property for `new Error()`, see also #636 + if (!error.stack) { + try { + throw error; + } catch (err) { + error = err; + } + } + + return extractStacktrace(error, offset); + } + + var priorityCount = 0; + var unitSampler = void 0; + + // This is a queue of functions that are tasks within a single test. + // After tests are dequeued from config.queue they are expanded into + // a set of tasks in this queue. + var taskQueue = []; + + /** + * Advances the taskQueue to the next task. If the taskQueue is empty, + * process the testQueue + */ + function advance() { + advanceTaskQueue(); + + if (!taskQueue.length) { + advanceTestQueue(); + } + } + + /** + * Advances the taskQueue to the next task if it is ready and not empty. + */ + function advanceTaskQueue() { + var start = now(); + config.depth = (config.depth || 0) + 1; + + while (taskQueue.length && !config.blocking) { + var elapsedTime = now() - start; + + if (!defined.setTimeout || config.updateRate <= 0 || elapsedTime < config.updateRate) { + var task = taskQueue.shift(); + task(); + } else { + setTimeout(advance); + break; + } + } + + config.depth--; + } + + /** + * Advance the testQueue to the next test to process. Call done() if testQueue completes. + */ + function advanceTestQueue() { + if (!config.blocking && !config.queue.length && config.depth === 0) { + done(); + return; + } + + var testTasks = config.queue.shift(); + addToTaskQueue(testTasks()); + + if (priorityCount > 0) { + priorityCount--; + } + + advance(); + } + + /** + * Enqueue the tasks for a test into the task queue. + * @param {Array} tasksArray + */ + function addToTaskQueue(tasksArray) { + taskQueue.push.apply(taskQueue, toConsumableArray(tasksArray)); + } + + /** + * Return the number of tasks remaining in the task queue to be processed. + * @return {Number} + */ + function taskQueueLength() { + return taskQueue.length; + } + + /** + * Adds a test to the TestQueue for execution. + * @param {Function} testTasksFunc + * @param {Boolean} prioritize + * @param {String} seed + */ + function addToTestQueue(testTasksFunc, prioritize, seed) { + if (prioritize) { + config.queue.splice(priorityCount++, 0, testTasksFunc); + } else if (seed) { + if (!unitSampler) { + unitSampler = unitSamplerGenerator(seed); + } + + // Insert into a random position after all prioritized items + var index = Math.floor(unitSampler() * (config.queue.length - priorityCount + 1)); + config.queue.splice(priorityCount + index, 0, testTasksFunc); + } else { + config.queue.push(testTasksFunc); + } + } + + /** + * Creates a seeded "sample" generator which is used for randomizing tests. + */ + function unitSamplerGenerator(seed) { + + // 32-bit xorshift, requires only a nonzero seed + // http://excamera.com/sphinx/article-xorshift.html + var sample = parseInt(generateHash(seed), 16) || -1; + return function () { + sample ^= sample << 13; + sample ^= sample >>> 17; + sample ^= sample << 5; + + // ECMAScript has no unsigned number type + if (sample < 0) { + sample += 0x100000000; + } + + return sample / 0x100000000; + }; + } + + /** + * This function is called when the ProcessingQueue is done processing all + * items. It handles emitting the final run events. + */ + function done() { + var storage = config.storage; + + ProcessingQueue.finished = true; + + var runtime = now() - config.started; + var passed = config.stats.all - config.stats.bad; + + if (config.stats.all === 0) { + + if (config.filter && config.filter.length) { + throw new Error("No tests matched the filter \"" + config.filter + "\"."); + } + + if (config.module && config.module.length) { + throw new Error("No tests matched the module \"" + config.module + "\"."); + } + + if (config.moduleId && config.moduleId.length) { + throw new Error("No tests matched the moduleId \"" + config.moduleId + "\"."); + } + + if (config.testId && config.testId.length) { + throw new Error("No tests matched the testId \"" + config.testId + "\"."); + } + + throw new Error("No tests were run."); + } + + emit("runEnd", globalSuite.end(true)); + runLoggingCallbacks("done", { + passed: passed, + failed: config.stats.bad, + total: config.stats.all, + runtime: runtime + }); + + // Clear own storage items if all tests passed + if (storage && config.stats.bad === 0) { + for (var i = storage.length - 1; i >= 0; i--) { + var key = storage.key(i); + + if (key.indexOf("qunit-test-") === 0) { + storage.removeItem(key); + } + } + } + } + + var ProcessingQueue = { + finished: false, + add: addToTestQueue, + advance: advance, + taskCount: taskQueueLength + }; + + var TestReport = function () { + function TestReport(name, suite, options) { + classCallCheck(this, TestReport); + + this.name = name; + this.suiteName = suite.name; + this.fullName = suite.fullName.concat(name); + this.runtime = 0; + this.assertions = []; + + this.skipped = !!options.skip; + this.todo = !!options.todo; + + this.valid = options.valid; + + this._startTime = 0; + this._endTime = 0; + + suite.pushTest(this); + } + + createClass(TestReport, [{ + key: "start", + value: function start(recordTime) { + if (recordTime) { + this._startTime = Date.now(); + } + + return { + name: this.name, + suiteName: this.suiteName, + fullName: this.fullName.slice() + }; + } + }, { + key: "end", + value: function end(recordTime) { + if (recordTime) { + this._endTime = Date.now(); + } + + return extend(this.start(), { + runtime: this.getRuntime(), + status: this.getStatus(), + errors: this.getFailedAssertions(), + assertions: this.getAssertions() + }); + } + }, { + key: "pushAssertion", + value: function pushAssertion(assertion) { + this.assertions.push(assertion); + } + }, { + key: "getRuntime", + value: function getRuntime() { + return this._endTime - this._startTime; + } + }, { + key: "getStatus", + value: function getStatus() { + if (this.skipped) { + return "skipped"; + } + + var testPassed = this.getFailedAssertions().length > 0 ? this.todo : !this.todo; + + if (!testPassed) { + return "failed"; + } else if (this.todo) { + return "todo"; + } else { + return "passed"; + } + } + }, { + key: "getFailedAssertions", + value: function getFailedAssertions() { + return this.assertions.filter(function (assertion) { + return !assertion.passed; + }); + } + }, { + key: "getAssertions", + value: function getAssertions() { + return this.assertions.slice(); + } + + // Remove actual and expected values from assertions. This is to prevent + // leaking memory throughout a test suite. + + }, { + key: "slimAssertions", + value: function slimAssertions() { + this.assertions = this.assertions.map(function (assertion) { + delete assertion.actual; + delete assertion.expected; + return assertion; + }); + } + }]); + return TestReport; + }(); + + var focused$1 = false; + + function Test(settings) { + var i, l; + + ++Test.count; + + this.expected = null; + this.assertions = []; + this.semaphore = 0; + this.module = config.currentModule; + this.stack = sourceFromStacktrace(3); + this.steps = []; + this.timeout = undefined; + + // If a module is skipped, all its tests and the tests of the child suites + // should be treated as skipped even if they are defined as `only` or `todo`. + // As for `todo` module, all its tests will be treated as `todo` except for + // tests defined as `skip` which will be left intact. + // + // So, if a test is defined as `todo` and is inside a skipped module, we should + // then treat that test as if was defined as `skip`. + if (this.module.skip) { + settings.skip = true; + settings.todo = false; + + // Skipped tests should be left intact + } else if (this.module.todo && !settings.skip) { + settings.todo = true; + } + + extend(this, settings); + + this.testReport = new TestReport(settings.testName, this.module.suiteReport, { + todo: settings.todo, + skip: settings.skip, + valid: this.valid() + }); + + // Register unique strings + for (i = 0, l = this.module.tests; i < l.length; i++) { + if (this.module.tests[i].name === this.testName) { + this.testName += " "; + } + } + + this.testId = generateHash(this.module.name, this.testName); + + this.module.tests.push({ + name: this.testName, + testId: this.testId, + skip: !!settings.skip + }); + + if (settings.skip) { + + // Skipped tests will fully ignore any sent callback + this.callback = function () {}; + this.async = false; + this.expected = 0; + } else { + if (typeof this.callback !== "function") { + var method = this.todo ? "todo" : "test"; + + // eslint-disable-next-line max-len + throw new TypeError("You must provide a function as a test callback to QUnit." + method + "(\"" + settings.testName + "\")"); + } + + this.assert = new Assert(this); + } + } + + Test.count = 0; + + function getNotStartedModules(startModule) { + var module = startModule, + modules = []; + + while (module && module.testsRun === 0) { + modules.push(module); + module = module.parentModule; + } + + return modules; + } + + Test.prototype = { + before: function before() { + var i, + startModule, + module = this.module, + notStartedModules = getNotStartedModules(module); + + for (i = notStartedModules.length - 1; i >= 0; i--) { + startModule = notStartedModules[i]; + startModule.stats = { all: 0, bad: 0, started: now() }; + emit("suiteStart", startModule.suiteReport.start(true)); + runLoggingCallbacks("moduleStart", { + name: startModule.name, + tests: startModule.tests + }); + } + + config.current = this; + + this.testEnvironment = extend({}, module.testEnvironment); + + this.started = now(); + emit("testStart", this.testReport.start(true)); + runLoggingCallbacks("testStart", { + name: this.testName, + module: module.name, + testId: this.testId, + previousFailure: this.previousFailure + }); + + if (!config.pollution) { + saveGlobal(); + } + }, + + run: function run() { + var promise; + + config.current = this; + + this.callbackStarted = now(); + + if (config.notrycatch) { + runTest(this); + return; + } + + try { + runTest(this); + } catch (e) { + this.pushFailure("Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + (e.message || e), extractStacktrace(e, 0)); + + // Else next test will carry the responsibility + saveGlobal(); + + // Restart the tests if they're blocking + if (config.blocking) { + internalRecover(this); + } + } + + function runTest(test) { + promise = test.callback.call(test.testEnvironment, test.assert); + test.resolvePromise(promise); + + // If the test has a "lock" on it, but the timeout is 0, then we push a + // failure as the test should be synchronous. + if (test.timeout === 0 && test.semaphore !== 0) { + pushFailure("Test did not finish synchronously even though assert.timeout( 0 ) was used.", sourceFromStacktrace(2)); + } + } + }, + + after: function after() { + checkPollution(); + }, + + queueHook: function queueHook(hook, hookName, hookOwner) { + var _this = this; + + var callHook = function callHook() { + var promise = hook.call(_this.testEnvironment, _this.assert); + _this.resolvePromise(promise, hookName); + }; + + var runHook = function runHook() { + if (hookName === "before") { + if (hookOwner.unskippedTestsRun !== 0) { + return; + } + + _this.preserveEnvironment = true; + } + + // The 'after' hook should only execute when there are not tests left and + // when the 'after' and 'finish' tasks are the only tasks left to process + if (hookName === "after" && hookOwner.unskippedTestsRun !== numberOfUnskippedTests(hookOwner) - 1 && (config.queue.length > 0 || ProcessingQueue.taskCount() > 2)) { + return; + } + + config.current = _this; + if (config.notrycatch) { + callHook(); + return; + } + try { + callHook(); + } catch (error) { + _this.pushFailure(hookName + " failed on " + _this.testName + ": " + (error.message || error), extractStacktrace(error, 0)); + } + }; + + return runHook; + }, + + + // Currently only used for module level hooks, can be used to add global level ones + hooks: function hooks(handler) { + var hooks = []; + + function processHooks(test, module) { + if (module.parentModule) { + processHooks(test, module.parentModule); + } + + if (module.hooks[handler].length) { + for (var i = 0; i < module.hooks[handler].length; i++) { + hooks.push(test.queueHook(module.hooks[handler][i], handler, module)); + } + } + } + + // Hooks are ignored on skipped tests + if (!this.skip) { + processHooks(this, this.module); + } + + return hooks; + }, + + + finish: function finish() { + config.current = this; + + if (this.steps.length) { + var stepsList = this.steps.join(", "); + this.pushFailure("Expected assert.verifySteps() to be called before end of test " + ("after using assert.step(). Unverified steps: " + stepsList), this.stack); + } + + if (config.requireExpects && this.expected === null) { + this.pushFailure("Expected number of assertions to be defined, but expect() was " + "not called.", this.stack); + } else if (this.expected !== null && this.expected !== this.assertions.length) { + this.pushFailure("Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack); + } else if (this.expected === null && !this.assertions.length) { + this.pushFailure("Expected at least one assertion, but none were run - call " + "expect(0) to accept zero assertions.", this.stack); + } + + var i, + module = this.module, + moduleName = module.name, + testName = this.testName, + skipped = !!this.skip, + todo = !!this.todo, + bad = 0, + storage = config.storage; + + this.runtime = now() - this.started; + + config.stats.all += this.assertions.length; + module.stats.all += this.assertions.length; + + for (i = 0; i < this.assertions.length; i++) { + if (!this.assertions[i].result) { + bad++; + config.stats.bad++; + module.stats.bad++; + } + } + + notifyTestsRan(module, skipped); + + // Store result when possible + if (storage) { + if (bad) { + storage.setItem("qunit-test-" + moduleName + "-" + testName, bad); + } else { + storage.removeItem("qunit-test-" + moduleName + "-" + testName); + } + } + + // After emitting the js-reporters event we cleanup the assertion data to + // avoid leaking it. It is not used by the legacy testDone callbacks. + emit("testEnd", this.testReport.end(true)); + this.testReport.slimAssertions(); + + runLoggingCallbacks("testDone", { + name: testName, + module: moduleName, + skipped: skipped, + todo: todo, + failed: bad, + passed: this.assertions.length - bad, + total: this.assertions.length, + runtime: skipped ? 0 : this.runtime, + + // HTML Reporter use + assertions: this.assertions, + testId: this.testId, + + // Source of Test + source: this.stack + }); + + if (module.testsRun === numberOfTests(module)) { + logSuiteEnd(module); + + // Check if the parent modules, iteratively, are done. If that the case, + // we emit the `suiteEnd` event and trigger `moduleDone` callback. + var parent = module.parentModule; + while (parent && parent.testsRun === numberOfTests(parent)) { + logSuiteEnd(parent); + parent = parent.parentModule; + } + } + + config.current = undefined; + + function logSuiteEnd(module) { + emit("suiteEnd", module.suiteReport.end(true)); + runLoggingCallbacks("moduleDone", { + name: module.name, + tests: module.tests, + failed: module.stats.bad, + passed: module.stats.all - module.stats.bad, + total: module.stats.all, + runtime: now() - module.stats.started + }); + } + }, + + preserveTestEnvironment: function preserveTestEnvironment() { + if (this.preserveEnvironment) { + this.module.testEnvironment = this.testEnvironment; + this.testEnvironment = extend({}, this.module.testEnvironment); + } + }, + + queue: function queue() { + var test = this; + + if (!this.valid()) { + return; + } + + function runTest() { + return [function () { + test.before(); + }].concat(toConsumableArray(test.hooks("before")), [function () { + test.preserveTestEnvironment(); + }], toConsumableArray(test.hooks("beforeEach")), [function () { + test.run(); + }], toConsumableArray(test.hooks("afterEach").reverse()), toConsumableArray(test.hooks("after").reverse()), [function () { + test.after(); + }, function () { + test.finish(); + }]); + } + + var previousFailCount = config.storage && +config.storage.getItem("qunit-test-" + this.module.name + "-" + this.testName); + + // Prioritize previously failed tests, detected from storage + var prioritize = config.reorder && !!previousFailCount; + + this.previousFailure = !!previousFailCount; + + ProcessingQueue.add(runTest, prioritize, config.seed); + + // If the queue has already finished, we manually process the new test + if (ProcessingQueue.finished) { + ProcessingQueue.advance(); + } + }, + + + pushResult: function pushResult(resultInfo) { + if (this !== config.current) { + throw new Error("Assertion occurred after test had finished."); + } + + // Destructure of resultInfo = { result, actual, expected, message, negative } + var source, + details = { + module: this.module.name, + name: this.testName, + result: resultInfo.result, + message: resultInfo.message, + actual: resultInfo.actual, + testId: this.testId, + negative: resultInfo.negative || false, + runtime: now() - this.started, + todo: !!this.todo + }; + + if (hasOwn.call(resultInfo, "expected")) { + details.expected = resultInfo.expected; + } + + if (!resultInfo.result) { + source = resultInfo.source || sourceFromStacktrace(); + + if (source) { + details.source = source; + } + } + + this.logAssertion(details); + + this.assertions.push({ + result: !!resultInfo.result, + message: resultInfo.message + }); + }, + + pushFailure: function pushFailure(message, source, actual) { + if (!(this instanceof Test)) { + throw new Error("pushFailure() assertion outside test context, was " + sourceFromStacktrace(2)); + } + + this.pushResult({ + result: false, + message: message || "error", + actual: actual || null, + source: source + }); + }, + + /** + * Log assertion details using both the old QUnit.log interface and + * QUnit.on( "assertion" ) interface. + * + * @private + */ + logAssertion: function logAssertion(details) { + runLoggingCallbacks("log", details); + + var assertion = { + passed: details.result, + actual: details.actual, + expected: details.expected, + message: details.message, + stack: details.source, + todo: details.todo + }; + this.testReport.pushAssertion(assertion); + emit("assertion", assertion); + }, + + + resolvePromise: function resolvePromise(promise, phase) { + var then, + resume, + message, + test = this; + if (promise != null) { + then = promise.then; + if (objectType(then) === "function") { + resume = internalStop(test); + if (config.notrycatch) { + then.call(promise, function () { + resume(); + }); + } else { + then.call(promise, function () { + resume(); + }, function (error) { + message = "Promise rejected " + (!phase ? "during" : phase.replace(/Each$/, "")) + " \"" + test.testName + "\": " + (error && error.message || error); + test.pushFailure(message, extractStacktrace(error, 0)); + + // Else next test will carry the responsibility + saveGlobal(); + + // Unblock + internalRecover(test); + }); + } + } + } + }, + + valid: function valid() { + var filter = config.filter, + regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec(filter), + module = config.module && config.module.toLowerCase(), + fullName = this.module.name + ": " + this.testName; + + function moduleChainNameMatch(testModule) { + var testModuleName = testModule.name ? testModule.name.toLowerCase() : null; + if (testModuleName === module) { + return true; + } else if (testModule.parentModule) { + return moduleChainNameMatch(testModule.parentModule); + } else { + return false; + } + } + + function moduleChainIdMatch(testModule) { + return inArray(testModule.moduleId, config.moduleId) || testModule.parentModule && moduleChainIdMatch(testModule.parentModule); + } + + // Internally-generated tests are always valid + if (this.callback && this.callback.validTest) { + return true; + } + + if (config.moduleId && config.moduleId.length > 0 && !moduleChainIdMatch(this.module)) { + + return false; + } + + if (config.testId && config.testId.length > 0 && !inArray(this.testId, config.testId)) { + + return false; + } + + if (module && !moduleChainNameMatch(this.module)) { + return false; + } + + if (!filter) { + return true; + } + + return regexFilter ? this.regexFilter(!!regexFilter[1], regexFilter[2], regexFilter[3], fullName) : this.stringFilter(filter, fullName); + }, + + regexFilter: function regexFilter(exclude, pattern, flags, fullName) { + var regex = new RegExp(pattern, flags); + var match = regex.test(fullName); + + return match !== exclude; + }, + + stringFilter: function stringFilter(filter, fullName) { + filter = filter.toLowerCase(); + fullName = fullName.toLowerCase(); + + var include = filter.charAt(0) !== "!"; + if (!include) { + filter = filter.slice(1); + } + + // If the filter matches, we need to honour include + if (fullName.indexOf(filter) !== -1) { + return include; + } + + // Otherwise, do the opposite + return !include; + } + }; + + function pushFailure() { + if (!config.current) { + throw new Error("pushFailure() assertion outside test context, in " + sourceFromStacktrace(2)); + } + + // Gets current test obj + var currentTest = config.current; + + return currentTest.pushFailure.apply(currentTest, arguments); + } + + function saveGlobal() { + config.pollution = []; + + if (config.noglobals) { + for (var key in global$1) { + if (hasOwn.call(global$1, key)) { + + // In Opera sometimes DOM element ids show up here, ignore them + if (/^qunit-test-output/.test(key)) { + continue; + } + config.pollution.push(key); + } + } + } + } + + function checkPollution() { + var newGlobals, + deletedGlobals, + old = config.pollution; + + saveGlobal(); + + newGlobals = diff(config.pollution, old); + if (newGlobals.length > 0) { + pushFailure("Introduced global variable(s): " + newGlobals.join(", ")); + } + + deletedGlobals = diff(old, config.pollution); + if (deletedGlobals.length > 0) { + pushFailure("Deleted global variable(s): " + deletedGlobals.join(", ")); + } + } + + // Will be exposed as QUnit.test + function test(testName, callback) { + if (focused$1) { + return; + } + + var newTest = new Test({ + testName: testName, + callback: callback + }); + + newTest.queue(); + } + + function todo(testName, callback) { + if (focused$1) { + return; + } + + var newTest = new Test({ + testName: testName, + callback: callback, + todo: true + }); + + newTest.queue(); + } + + // Will be exposed as QUnit.skip + function skip(testName) { + if (focused$1) { + return; + } + + var test = new Test({ + testName: testName, + skip: true + }); + + test.queue(); + } + + // Will be exposed as QUnit.only + function only(testName, callback) { + if (focused$1) { + return; + } + + config.queue.length = 0; + focused$1 = true; + + var newTest = new Test({ + testName: testName, + callback: callback + }); + + newTest.queue(); + } + + // Put a hold on processing and return a function that will release it. + function internalStop(test) { + test.semaphore += 1; + config.blocking = true; + + // Set a recovery timeout, if so configured. + if (defined.setTimeout) { + var timeoutDuration = void 0; + + if (typeof test.timeout === "number") { + timeoutDuration = test.timeout; + } else if (typeof config.testTimeout === "number") { + timeoutDuration = config.testTimeout; + } + + if (typeof timeoutDuration === "number" && timeoutDuration > 0) { + clearTimeout(config.timeout); + config.timeout = setTimeout(function () { + pushFailure("Test took longer than " + timeoutDuration + "ms; test timed out.", sourceFromStacktrace(2)); + internalRecover(test); + }, timeoutDuration); + } + } + + var released = false; + return function resume() { + if (released) { + return; + } + + released = true; + test.semaphore -= 1; + internalStart(test); + }; + } + + // Forcefully release all processing holds. + function internalRecover(test) { + test.semaphore = 0; + internalStart(test); + } + + // Release a processing hold, scheduling a resumption attempt if no holds remain. + function internalStart(test) { + + // If semaphore is non-numeric, throw error + if (isNaN(test.semaphore)) { + test.semaphore = 0; + + pushFailure("Invalid value on test.semaphore", sourceFromStacktrace(2)); + return; + } + + // Don't start until equal number of stop-calls + if (test.semaphore > 0) { + return; + } + + // Throw an Error if start is called more often than stop + if (test.semaphore < 0) { + test.semaphore = 0; + + pushFailure("Tried to restart test while already started (test's semaphore was 0 already)", sourceFromStacktrace(2)); + return; + } + + // Add a slight delay to allow more assertions etc. + if (defined.setTimeout) { + if (config.timeout) { + clearTimeout(config.timeout); + } + config.timeout = setTimeout(function () { + if (test.semaphore > 0) { + return; + } + + if (config.timeout) { + clearTimeout(config.timeout); + } + + begin(); + }); + } else { + begin(); + } + } + + function collectTests(module) { + var tests = [].concat(module.tests); + var modules = [].concat(toConsumableArray(module.childModules)); + + // Do a breadth-first traversal of the child modules + while (modules.length) { + var nextModule = modules.shift(); + tests.push.apply(tests, nextModule.tests); + modules.push.apply(modules, toConsumableArray(nextModule.childModules)); + } + + return tests; + } + + function numberOfTests(module) { + return collectTests(module).length; + } + + function numberOfUnskippedTests(module) { + return collectTests(module).filter(function (test) { + return !test.skip; + }).length; + } + + function notifyTestsRan(module, skipped) { + module.testsRun++; + if (!skipped) { + module.unskippedTestsRun++; + } + while (module = module.parentModule) { + module.testsRun++; + if (!skipped) { + module.unskippedTestsRun++; + } + } + } + + /** + * Returns a function that proxies to the given method name on the globals + * console object. The proxy will also detect if the console doesn't exist and + * will appropriately no-op. This allows support for IE9, which doesn't have a + * console if the developer tools are not open. + */ + function consoleProxy(method) { + return function () { + if (console) { + console[method].apply(console, arguments); + } + }; + } + + var Logger = { + warn: consoleProxy("warn") + }; + + var Assert = function () { + function Assert(testContext) { + classCallCheck(this, Assert); + + this.test = testContext; + } + + // Assert helpers + + createClass(Assert, [{ + key: "timeout", + value: function timeout(duration) { + if (typeof duration !== "number") { + throw new Error("You must pass a number as the duration to assert.timeout"); + } + + this.test.timeout = duration; + } + + // Documents a "step", which is a string value, in a test as a passing assertion + + }, { + key: "step", + value: function step(message) { + var assertionMessage = message; + var result = !!message; + + this.test.steps.push(message); + + if (objectType(message) === "undefined" || message === "") { + assertionMessage = "You must provide a message to assert.step"; + } else if (objectType(message) !== "string") { + assertionMessage = "You must provide a string value to assert.step"; + result = false; + } + + return this.pushResult({ + result: result, + message: assertionMessage + }); + } + + // Verifies the steps in a test match a given array of string values + + }, { + key: "verifySteps", + value: function verifySteps(steps, message) { + + // Since the steps array is just string values, we can clone with slice + var actualStepsClone = this.test.steps.slice(); + this.deepEqual(actualStepsClone, steps, message); + this.test.steps.length = 0; + } + + // Specify the number of expected assertions to guarantee that failed test + // (no assertions are run at all) don't slip through. + + }, { + key: "expect", + value: function expect(asserts) { + if (arguments.length === 1) { + this.test.expected = asserts; + } else { + return this.test.expected; + } + } + + // Put a hold on processing and return a function that will release it a maximum of once. + + }, { + key: "async", + value: function async(count) { + var test$$1 = this.test; + + var popped = false, + acceptCallCount = count; + + if (typeof acceptCallCount === "undefined") { + acceptCallCount = 1; + } + + var resume = internalStop(test$$1); + + return function done() { + if (config.current !== test$$1) { + throw Error("assert.async callback called after test finished."); + } + + if (popped) { + test$$1.pushFailure("Too many calls to the `assert.async` callback", sourceFromStacktrace(2)); + return; + } + + acceptCallCount -= 1; + if (acceptCallCount > 0) { + return; + } + + popped = true; + resume(); + }; + } + + // Exports test.push() to the user API + // Alias of pushResult. + + }, { + key: "push", + value: function push(result, actual, expected, message, negative) { + 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)."); + + var currentAssert = this instanceof Assert ? this : config.current.assert; + return currentAssert.pushResult({ + result: result, + actual: actual, + expected: expected, + message: message, + negative: negative + }); + } + }, { + key: "pushResult", + value: function pushResult(resultInfo) { + + // Destructure of resultInfo = { result, actual, expected, message, negative } + var assert = this; + var currentTest = assert instanceof Assert && assert.test || config.current; + + // Backwards compatibility fix. + // Allows the direct use of global exported assertions and QUnit.assert.* + // Although, it's use is not recommended as it can leak assertions + // to other tests from async tests, because we only get a reference to the current test, + // not exactly the test where assertion were intended to be called. + if (!currentTest) { + throw new Error("assertion outside test context, in " + sourceFromStacktrace(2)); + } + + if (!(assert instanceof Assert)) { + assert = currentTest.assert; + } + + return assert.test.pushResult(resultInfo); + } + }, { + key: "ok", + value: function ok(result, message) { + if (!message) { + message = result ? "okay" : "failed, expected argument to be truthy, was: " + dump.parse(result); + } + + this.pushResult({ + result: !!result, + actual: result, + expected: true, + message: message + }); + } + }, { + key: "notOk", + value: function notOk(result, message) { + if (!message) { + message = !result ? "okay" : "failed, expected argument to be falsy, was: " + dump.parse(result); + } + + this.pushResult({ + result: !result, + actual: result, + expected: false, + message: message + }); + } + }, { + key: "equal", + value: function equal(actual, expected, message) { + + // eslint-disable-next-line eqeqeq + var result = expected == actual; + + this.pushResult({ + result: result, + actual: actual, + expected: expected, + message: message + }); + } + }, { + key: "notEqual", + value: function notEqual(actual, expected, message) { + + // eslint-disable-next-line eqeqeq + var result = expected != actual; + + this.pushResult({ + result: result, + actual: actual, + expected: expected, + message: message, + negative: true + }); + } + }, { + key: "propEqual", + value: function propEqual(actual, expected, message) { + actual = objectValues(actual); + expected = objectValues(expected); + + this.pushResult({ + result: equiv(actual, expected), + actual: actual, + expected: expected, + message: message + }); + } + }, { + key: "notPropEqual", + value: function notPropEqual(actual, expected, message) { + actual = objectValues(actual); + expected = objectValues(expected); + + this.pushResult({ + result: !equiv(actual, expected), + actual: actual, + expected: expected, + message: message, + negative: true + }); + } + }, { + key: "deepEqual", + value: function deepEqual(actual, expected, message) { + this.pushResult({ + result: equiv(actual, expected), + actual: actual, + expected: expected, + message: message + }); + } + }, { + key: "notDeepEqual", + value: function notDeepEqual(actual, expected, message) { + this.pushResult({ + result: !equiv(actual, expected), + actual: actual, + expected: expected, + message: message, + negative: true + }); + } + }, { + key: "strictEqual", + value: function strictEqual(actual, expected, message) { + this.pushResult({ + result: expected === actual, + actual: actual, + expected: expected, + message: message + }); + } + }, { + key: "notStrictEqual", + value: function notStrictEqual(actual, expected, message) { + this.pushResult({ + result: expected !== actual, + actual: actual, + expected: expected, + message: message, + negative: true + }); + } + }, { + key: "throws", + value: function throws(block, expected, message) { + var actual = void 0, + result = false; + + var currentTest = this instanceof Assert && this.test || config.current; + + // 'expected' is optional unless doing string comparison + if (objectType(expected) === "string") { + if (message == null) { + message = expected; + expected = null; + } else { + 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."); + } + } + + currentTest.ignoreGlobalErrors = true; + try { + block.call(currentTest.testEnvironment); + } catch (e) { + actual = e; + } + currentTest.ignoreGlobalErrors = false; + + if (actual) { + var expectedType = objectType(expected); + + // We don't want to validate thrown error + if (!expected) { + result = true; + expected = null; + + // Expected is a regexp + } else if (expectedType === "regexp") { + result = expected.test(errorString(actual)); + + // Expected is a constructor, maybe an Error constructor + } else if (expectedType === "function" && actual instanceof expected) { + result = true; + + // Expected is an Error object + } else if (expectedType === "object") { + result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message; + + // Expected is a validation function which returns true if validation passed + } else if (expectedType === "function" && expected.call({}, actual) === true) { + expected = null; + result = true; + } + } + + currentTest.assert.pushResult({ + result: result, + actual: actual, + expected: expected, + message: message + }); + } + }, { + key: "rejects", + value: function rejects(promise, expected, message) { + var result = false; + + var currentTest = this instanceof Assert && this.test || config.current; + + // 'expected' is optional unless doing string comparison + if (objectType(expected) === "string") { + if (message === undefined) { + message = expected; + expected = undefined; + } else { + 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."; + + currentTest.assert.pushResult({ + result: false, + message: message + }); + + return; + } + } + + var then = promise && promise.then; + if (objectType(then) !== "function") { + var _message = "The value provided to `assert.rejects` in " + "\"" + currentTest.testName + "\" was not a promise."; + + currentTest.assert.pushResult({ + result: false, + message: _message, + actual: promise + }); + + return; + } + + var done = this.async(); + + return then.call(promise, function handleFulfillment() { + var message = "The promise returned by the `assert.rejects` callback in " + "\"" + currentTest.testName + "\" did not reject."; + + currentTest.assert.pushResult({ + result: false, + message: message, + actual: promise + }); + + done(); + }, function handleRejection(actual) { + var expectedType = objectType(expected); + + // We don't want to validate + if (expected === undefined) { + result = true; + expected = actual; + + // Expected is a regexp + } else if (expectedType === "regexp") { + result = expected.test(errorString(actual)); + + // Expected is a constructor, maybe an Error constructor + } else if (expectedType === "function" && actual instanceof expected) { + result = true; + + // Expected is an Error object + } else if (expectedType === "object") { + result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message; + + // Expected is a validation function which returns true if validation passed + } else { + if (expectedType === "function") { + result = expected.call({}, actual) === true; + expected = null; + + // Expected is some other invalid type + } else { + result = false; + message = "invalid expected value provided to `assert.rejects` " + "callback in \"" + currentTest.testName + "\": " + expectedType + "."; + } + } + + currentTest.assert.pushResult({ + result: result, + actual: actual, + expected: expected, + message: message + }); + + done(); + }); + } + }]); + return Assert; + }(); + + // Provide an alternative to assert.throws(), for environments that consider throws a reserved word + // Known to us are: Closure Compiler, Narwhal + // eslint-disable-next-line dot-notation + + + Assert.prototype.raises = Assert.prototype["throws"]; + + /** + * Converts an error into a simple string for comparisons. + * + * @param {Error} error + * @return {String} + */ + function errorString(error) { + var resultErrorString = error.toString(); + + if (resultErrorString.substring(0, 7) === "[object") { + var name = error.name ? error.name.toString() : "Error"; + var message = error.message ? error.message.toString() : ""; + + if (name && message) { + return name + ": " + message; + } else if (name) { + return name; + } else if (message) { + return message; + } else { + return "Error"; + } + } else { + return resultErrorString; + } + } + + /* global module, exports, define */ + function exportQUnit(QUnit) { + + if (defined.document) { + + // QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined. + if (window.QUnit && window.QUnit.version) { + throw new Error("QUnit has already been defined."); + } + + window.QUnit = QUnit; + } + + // For nodejs + if (typeof module !== "undefined" && module && module.exports) { + module.exports = QUnit; + + // For consistency with CommonJS environments' exports + module.exports.QUnit = QUnit; + } + + // For CommonJS with exports, but without module.exports, like Rhino + if (typeof exports !== "undefined" && exports) { + exports.QUnit = QUnit; + } + + if (typeof define === "function" && define.amd) { + define(function () { + return QUnit; + }); + QUnit.config.autostart = false; + } + + // For Web/Service Workers + if (self$1 && self$1.WorkerGlobalScope && self$1 instanceof self$1.WorkerGlobalScope) { + self$1.QUnit = QUnit; + } + } + + var SuiteReport = function () { + function SuiteReport(name, parentSuite) { + classCallCheck(this, SuiteReport); + + this.name = name; + this.fullName = parentSuite ? parentSuite.fullName.concat(name) : []; + + this.tests = []; + this.childSuites = []; + + if (parentSuite) { + parentSuite.pushChildSuite(this); + } + } + + createClass(SuiteReport, [{ + key: "start", + value: function start(recordTime) { + if (recordTime) { + this._startTime = Date.now(); + } + + return { + name: this.name, + fullName: this.fullName.slice(), + tests: this.tests.map(function (test) { + return test.start(); + }), + childSuites: this.childSuites.map(function (suite) { + return suite.start(); + }), + testCounts: { + total: this.getTestCounts().total + } + }; + } + }, { + key: "end", + value: function end(recordTime) { + if (recordTime) { + this._endTime = Date.now(); + } + + return { + name: this.name, + fullName: this.fullName.slice(), + tests: this.tests.map(function (test) { + return test.end(); + }), + childSuites: this.childSuites.map(function (suite) { + return suite.end(); + }), + testCounts: this.getTestCounts(), + runtime: this.getRuntime(), + status: this.getStatus() + }; + } + }, { + key: "pushChildSuite", + value: function pushChildSuite(suite) { + this.childSuites.push(suite); + } + }, { + key: "pushTest", + value: function pushTest(test) { + this.tests.push(test); + } + }, { + key: "getRuntime", + value: function getRuntime() { + return this._endTime - this._startTime; + } + }, { + key: "getTestCounts", + value: function getTestCounts() { + var counts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { passed: 0, failed: 0, skipped: 0, todo: 0, total: 0 }; + + counts = this.tests.reduce(function (counts, test) { + if (test.valid) { + counts[test.getStatus()]++; + counts.total++; + } + + return counts; + }, counts); + + return this.childSuites.reduce(function (counts, suite) { + return suite.getTestCounts(counts); + }, counts); + } + }, { + key: "getStatus", + value: function getStatus() { + var _getTestCounts = this.getTestCounts(), + total = _getTestCounts.total, + failed = _getTestCounts.failed, + skipped = _getTestCounts.skipped, + todo = _getTestCounts.todo; + + if (failed) { + return "failed"; + } else { + if (skipped === total) { + return "skipped"; + } else if (todo === total) { + return "todo"; + } else { + return "passed"; + } + } + } + }]); + return SuiteReport; + }(); + + // Handle an unhandled exception. By convention, returns true if further + // error handling should be suppressed and false otherwise. + // In this case, we will only suppress further error handling if the + // "ignoreGlobalErrors" configuration option is enabled. + function onError(error) { + for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + if (config.current) { + if (config.current.ignoreGlobalErrors) { + return true; + } + pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args)); + } else { + test("global failure", extend(function () { + pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args)); + }, { validTest: true })); + } + + return false; + } + + // Handle an unhandled rejection + function onUnhandledRejection(reason) { + var resultInfo = { + result: false, + message: reason.message || "error", + actual: reason, + source: reason.stack || sourceFromStacktrace(3) + }; + + var currentTest = config.current; + if (currentTest) { + currentTest.assert.pushResult(resultInfo); + } else { + test("global failure", extend(function (assert) { + assert.pushResult(resultInfo); + }, { validTest: true })); + } + } + + var focused = false; + var QUnit = {}; + var globalSuite = new SuiteReport(); + + // The initial "currentModule" represents the global (or top-level) module that + // is not explicitly defined by the user, therefore we add the "globalSuite" to + // it since each module has a suiteReport associated with it. + config.currentModule.suiteReport = globalSuite; + + var moduleStack = []; + var globalStartCalled = false; + var runStarted = false; + + // Figure out if we're running the tests from a server or not + QUnit.isLocal = !(defined.document && window.location.protocol !== "file:"); + + // Expose the current QUnit version + QUnit.version = "2.6.0"; + + function createModule(name, testEnvironment, modifiers) { + var parentModule = moduleStack.length ? moduleStack.slice(-1)[0] : null; + var moduleName = parentModule !== null ? [parentModule.name, name].join(" > ") : name; + var parentSuite = parentModule ? parentModule.suiteReport : globalSuite; + + var skip$$1 = parentModule !== null && parentModule.skip || modifiers.skip; + var todo$$1 = parentModule !== null && parentModule.todo || modifiers.todo; + + var module = { + name: moduleName, + parentModule: parentModule, + tests: [], + moduleId: generateHash(moduleName), + testsRun: 0, + unskippedTestsRun: 0, + childModules: [], + suiteReport: new SuiteReport(name, parentSuite), + + // Pass along `skip` and `todo` properties from parent module, in case + // there is one, to childs. And use own otherwise. + // This property will be used to mark own tests and tests of child suites + // as either `skipped` or `todo`. + skip: skip$$1, + todo: skip$$1 ? false : todo$$1 + }; + + var env = {}; + if (parentModule) { + parentModule.childModules.push(module); + extend(env, parentModule.testEnvironment); + } + extend(env, testEnvironment); + module.testEnvironment = env; + + config.modules.push(module); + return module; + } + + function processModule(name, options, executeNow) { + var modifiers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; + + var module = createModule(name, options, modifiers); + + // Move any hooks to a 'hooks' object + var testEnvironment = module.testEnvironment; + var hooks = module.hooks = {}; + + setHookFromEnvironment(hooks, testEnvironment, "before"); + setHookFromEnvironment(hooks, testEnvironment, "beforeEach"); + setHookFromEnvironment(hooks, testEnvironment, "afterEach"); + setHookFromEnvironment(hooks, testEnvironment, "after"); + + function setHookFromEnvironment(hooks, environment, name) { + var potentialHook = environment[name]; + hooks[name] = typeof potentialHook === "function" ? [potentialHook] : []; + delete environment[name]; + } + + var moduleFns = { + before: setHookFunction(module, "before"), + beforeEach: setHookFunction(module, "beforeEach"), + afterEach: setHookFunction(module, "afterEach"), + after: setHookFunction(module, "after") + }; + + var currentModule = config.currentModule; + if (objectType(executeNow) === "function") { + moduleStack.push(module); + config.currentModule = module; + executeNow.call(module.testEnvironment, moduleFns); + moduleStack.pop(); + module = module.parentModule || currentModule; + } + + config.currentModule = module; + } + + // TODO: extract this to a new file alongside its related functions + function module$1(name, options, executeNow) { + if (focused) { + return; + } + + if (arguments.length === 2) { + if (objectType(options) === "function") { + executeNow = options; + options = undefined; + } + } + + processModule(name, options, executeNow); + } + + module$1.only = function () { + if (focused) { + return; + } + + config.modules.length = 0; + config.queue.length = 0; + + module$1.apply(undefined, arguments); + + focused = true; + }; + + module$1.skip = function (name, options, executeNow) { + if (focused) { + return; + } + + if (arguments.length === 2) { + if (objectType(options) === "function") { + executeNow = options; + options = undefined; + } + } + + processModule(name, options, executeNow, { skip: true }); + }; + + module$1.todo = function (name, options, executeNow) { + if (focused) { + return; + } + + if (arguments.length === 2) { + if (objectType(options) === "function") { + executeNow = options; + options = undefined; + } + } + + processModule(name, options, executeNow, { todo: true }); + }; + + extend(QUnit, { + on: on, + + module: module$1, + + test: test, + + todo: todo, + + skip: skip, + + only: only, + + start: function start(count) { + var globalStartAlreadyCalled = globalStartCalled; + + if (!config.current) { + globalStartCalled = true; + + if (runStarted) { + throw new Error("Called start() while test already started running"); + } else if (globalStartAlreadyCalled || count > 1) { + throw new Error("Called start() outside of a test context too many times"); + } else if (config.autostart) { + throw new Error("Called start() outside of a test context when " + "QUnit.config.autostart was true"); + } else if (!config.pageLoaded) { + + // The page isn't completely loaded yet, so we set autostart and then + // load if we're in Node or wait for the browser's load event. + config.autostart = true; + + // Starts from Node even if .load was not previously called. We still return + // early otherwise we'll wind up "beginning" twice. + if (!defined.document) { + QUnit.load(); + } + + return; + } + } else { + throw new Error("QUnit.start cannot be called inside a test context."); + } + + scheduleBegin(); + }, + + config: config, + + is: is, + + objectType: objectType, + + extend: extend, + + load: function load() { + config.pageLoaded = true; + + // Initialize the configuration options + extend(config, { + stats: { all: 0, bad: 0 }, + started: 0, + updateRate: 1000, + autostart: true, + filter: "" + }, true); + + if (!runStarted) { + config.blocking = false; + + if (config.autostart) { + scheduleBegin(); + } + } + }, + + stack: function stack(offset) { + offset = (offset || 0) + 2; + return sourceFromStacktrace(offset); + }, + + onError: onError, + + onUnhandledRejection: onUnhandledRejection + }); + + QUnit.pushFailure = pushFailure; + QUnit.assert = Assert.prototype; + QUnit.equiv = equiv; + QUnit.dump = dump; + + registerLoggingCallbacks(QUnit); + + function scheduleBegin() { + + runStarted = true; + + // Add a slight delay to allow definition of more modules and tests. + if (defined.setTimeout) { + setTimeout(function () { + begin(); + }); + } else { + begin(); + } + } + + function begin() { + var i, + l, + modulesLog = []; + + // If the test run hasn't officially begun yet + if (!config.started) { + + // Record the time of the test run's beginning + config.started = now(); + + // Delete the loose unnamed module if unused. + if (config.modules[0].name === "" && config.modules[0].tests.length === 0) { + config.modules.shift(); + } + + // Avoid unnecessary information by not logging modules' test environments + for (i = 0, l = config.modules.length; i < l; i++) { + modulesLog.push({ + name: config.modules[i].name, + tests: config.modules[i].tests + }); + } + + // The test run is officially beginning now + emit("runStart", globalSuite.start(true)); + runLoggingCallbacks("begin", { + totalTests: Test.count, + modules: modulesLog + }); + } + + config.blocking = false; + ProcessingQueue.advance(); + } + + function setHookFunction(module, hookName) { + return function setHook(callback) { + module.hooks[hookName].push(callback); + }; + } + + exportQUnit(QUnit); + + (function () { + + if (typeof window === "undefined" || typeof document === "undefined") { + return; + } + + var config = QUnit.config, + hasOwn = Object.prototype.hasOwnProperty; + + // Stores fixture HTML for resetting later + function storeFixture() { + + // Avoid overwriting user-defined values + if (hasOwn.call(config, "fixture")) { + return; + } + + var fixture = document.getElementById("qunit-fixture"); + if (fixture) { + config.fixture = fixture.cloneNode(true); + } + } + + QUnit.begin(storeFixture); + + // Resets the fixture DOM element if available. + function resetFixture() { + if (config.fixture == null) { + return; + } + + var fixture = document.getElementById("qunit-fixture"); + var resetFixtureType = _typeof(config.fixture); + if (resetFixtureType === "string") { + + // support user defined values for `config.fixture` + var newFixture = document.createElement("div"); + newFixture.setAttribute("id", "qunit-fixture"); + newFixture.innerHTML = config.fixture; + fixture.parentNode.replaceChild(newFixture, fixture); + } else { + var clonedFixture = config.fixture.cloneNode(true); + fixture.parentNode.replaceChild(clonedFixture, fixture); + } + } + + QUnit.testStart(resetFixture); + })(); + + (function () { + + // Only interact with URLs via window.location + var location = typeof window !== "undefined" && window.location; + if (!location) { + return; + } + + var urlParams = getUrlParams(); + + QUnit.urlParams = urlParams; + + // Match module/test by inclusion in an array + QUnit.config.moduleId = [].concat(urlParams.moduleId || []); + QUnit.config.testId = [].concat(urlParams.testId || []); + + // Exact case-insensitive match of the module name + QUnit.config.module = urlParams.module; + + // Regular expression or case-insenstive substring match against "moduleName: testName" + QUnit.config.filter = urlParams.filter; + + // Test order randomization + if (urlParams.seed === true) { + + // Generate a random seed if the option is specified without a value + QUnit.config.seed = Math.random().toString(36).slice(2); + } else if (urlParams.seed) { + QUnit.config.seed = urlParams.seed; + } + + // Add URL-parameter-mapped config values with UI form rendering data + QUnit.config.urlConfig.push({ + id: "hidepassed", + label: "Hide passed tests", + tooltip: "Only show tests and assertions that fail. Stored as query-strings." + }, { + id: "noglobals", + label: "Check for Globals", + tooltip: "Enabling this will test if any test introduces new properties on the " + "global object (`window` in Browsers). Stored as query-strings." + }, { + id: "notrycatch", + label: "No try-catch", + tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + "exceptions in IE reasonable. Stored as query-strings." + }); + + QUnit.begin(function () { + var i, + option, + urlConfig = QUnit.config.urlConfig; + + for (i = 0; i < urlConfig.length; i++) { + + // Options can be either strings or objects with nonempty "id" properties + option = QUnit.config.urlConfig[i]; + if (typeof option !== "string") { + option = option.id; + } + + if (QUnit.config[option] === undefined) { + QUnit.config[option] = urlParams[option]; + } + } + }); + + function getUrlParams() { + var i, param, name, value; + var urlParams = Object.create(null); + var params = location.search.slice(1).split("&"); + var length = params.length; + + for (i = 0; i < length; i++) { + if (params[i]) { + param = params[i].split("="); + name = decodeQueryParam(param[0]); + + // Allow just a key to turn on a flag, e.g., test.html?noglobals + value = param.length === 1 || decodeQueryParam(param.slice(1).join("=")); + if (name in urlParams) { + urlParams[name] = [].concat(urlParams[name], value); + } else { + urlParams[name] = value; + } + } + } + + return urlParams; + } + + function decodeQueryParam(param) { + return decodeURIComponent(param.replace(/\+/g, "%20")); + } + })(); + + var stats = { + passedTests: 0, + failedTests: 0, + skippedTests: 0, + todoTests: 0 + }; + + // Escape text for attribute or text content. + function escapeText(s) { + if (!s) { + return ""; + } + s = s + ""; + + // Both single quotes and double quotes (for attributes) + return s.replace(/['"<>&]/g, function (s) { + switch (s) { + case "'": + return "'"; + case "\"": + return """; + case "<": + return "<"; + case ">": + return ">"; + case "&": + return "&"; + } + }); + } + + (function () { + + // Don't load the HTML Reporter on non-browser environments + if (typeof window === "undefined" || !window.document) { + return; + } + + var config = QUnit.config, + document$$1 = window.document, + collapseNext = false, + hasOwn = Object.prototype.hasOwnProperty, + unfilteredUrl = setUrl({ filter: undefined, module: undefined, + moduleId: undefined, testId: undefined }), + modulesList = []; + + function addEvent(elem, type, fn) { + elem.addEventListener(type, fn, false); + } + + function removeEvent(elem, type, fn) { + elem.removeEventListener(type, fn, false); + } + + function addEvents(elems, type, fn) { + var i = elems.length; + while (i--) { + addEvent(elems[i], type, fn); + } + } + + function hasClass(elem, name) { + return (" " + elem.className + " ").indexOf(" " + name + " ") >= 0; + } + + function addClass(elem, name) { + if (!hasClass(elem, name)) { + elem.className += (elem.className ? " " : "") + name; + } + } + + function toggleClass(elem, name, force) { + if (force || typeof force === "undefined" && !hasClass(elem, name)) { + addClass(elem, name); + } else { + removeClass(elem, name); + } + } + + function removeClass(elem, name) { + var set = " " + elem.className + " "; + + // Class name may appear multiple times + while (set.indexOf(" " + name + " ") >= 0) { + set = set.replace(" " + name + " ", " "); + } + + // Trim for prettiness + elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, ""); + } + + function id(name) { + return document$$1.getElementById && document$$1.getElementById(name); + } + + function abortTests() { + var abortButton = id("qunit-abort-tests-button"); + if (abortButton) { + abortButton.disabled = true; + abortButton.innerHTML = "Aborting..."; + } + QUnit.config.queue.length = 0; + return false; + } + + function interceptNavigation(ev) { + applyUrlParams(); + + if (ev && ev.preventDefault) { + ev.preventDefault(); + } + + return false; + } + + function getUrlConfigHtml() { + var i, + j, + val, + escaped, + escapedTooltip, + selection = false, + urlConfig = config.urlConfig, + urlConfigHtml = ""; + + for (i = 0; i < urlConfig.length; i++) { + + // Options can be either strings or objects with nonempty "id" properties + val = config.urlConfig[i]; + if (typeof val === "string") { + val = { + id: val, + label: val + }; + } + + escaped = escapeText(val.id); + escapedTooltip = escapeText(val.tooltip); + + if (!val.value || typeof val.value === "string") { + urlConfigHtml += ""; + } else { + urlConfigHtml += ""; + } + } + + return urlConfigHtml; + } + + // Handle "click" events on toolbar checkboxes and "change" for select menus. + // Updates the URL with the new state of `config.urlConfig` values. + function toolbarChanged() { + var updatedUrl, + value, + tests, + field = this, + params = {}; + + // Detect if field is a select menu or a checkbox + if ("selectedIndex" in field) { + value = field.options[field.selectedIndex].value || undefined; + } else { + value = field.checked ? field.defaultValue || true : undefined; + } + + params[field.name] = value; + updatedUrl = setUrl(params); + + // Check if we can apply the change without a page refresh + if ("hidepassed" === field.name && "replaceState" in window.history) { + QUnit.urlParams[field.name] = value; + config[field.name] = value || false; + tests = id("qunit-tests"); + if (tests) { + toggleClass(tests, "hidepass", value || false); + } + window.history.replaceState(null, "", updatedUrl); + } else { + window.location = updatedUrl; + } + } + + function setUrl(params) { + var key, + arrValue, + i, + querystring = "?", + location = window.location; + + params = QUnit.extend(QUnit.extend({}, QUnit.urlParams), params); + + for (key in params) { + + // Skip inherited or undefined properties + if (hasOwn.call(params, key) && params[key] !== undefined) { + + // Output a parameter for each value of this key + // (but usually just one) + arrValue = [].concat(params[key]); + for (i = 0; i < arrValue.length; i++) { + querystring += encodeURIComponent(key); + if (arrValue[i] !== true) { + querystring += "=" + encodeURIComponent(arrValue[i]); + } + querystring += "&"; + } + } + } + return location.protocol + "//" + location.host + location.pathname + querystring.slice(0, -1); + } + + function applyUrlParams() { + var i, + selectedModules = [], + modulesList = id("qunit-modulefilter-dropdown-list").getElementsByTagName("input"), + filter = id("qunit-filter-input").value; + + for (i = 0; i < modulesList.length; i++) { + if (modulesList[i].checked) { + selectedModules.push(modulesList[i].value); + } + } + + window.location = setUrl({ + filter: filter === "" ? undefined : filter, + moduleId: selectedModules.length === 0 ? undefined : selectedModules, + + // Remove module and testId filter + module: undefined, + testId: undefined + }); + } + + function toolbarUrlConfigContainer() { + var urlConfigContainer = document$$1.createElement("span"); + + urlConfigContainer.innerHTML = getUrlConfigHtml(); + addClass(urlConfigContainer, "qunit-url-config"); + + addEvents(urlConfigContainer.getElementsByTagName("input"), "change", toolbarChanged); + addEvents(urlConfigContainer.getElementsByTagName("select"), "change", toolbarChanged); + + return urlConfigContainer; + } + + function abortTestsButton() { + var button = document$$1.createElement("button"); + button.id = "qunit-abort-tests-button"; + button.innerHTML = "Abort"; + addEvent(button, "click", abortTests); + return button; + } + + function toolbarLooseFilter() { + var filter = document$$1.createElement("form"), + label = document$$1.createElement("label"), + input = document$$1.createElement("input"), + button = document$$1.createElement("button"); + + addClass(filter, "qunit-filter"); + + label.innerHTML = "Filter: "; + + input.type = "text"; + input.value = config.filter || ""; + input.name = "filter"; + input.id = "qunit-filter-input"; + + button.innerHTML = "Go"; + + label.appendChild(input); + + filter.appendChild(label); + filter.appendChild(document$$1.createTextNode(" ")); + filter.appendChild(button); + addEvent(filter, "submit", interceptNavigation); + + return filter; + } + + function moduleListHtml() { + var i, + checked, + html = ""; + + for (i = 0; i < config.modules.length; i++) { + if (config.modules[i].name !== "") { + checked = config.moduleId.indexOf(config.modules[i].moduleId) > -1; + html += "
      1. "; + } + } + + return html; + } + + function toolbarModuleFilter() { + var allCheckbox, + commit, + reset, + moduleFilter = document$$1.createElement("form"), + label = document$$1.createElement("label"), + moduleSearch = document$$1.createElement("input"), + dropDown = document$$1.createElement("div"), + actions = document$$1.createElement("span"), + dropDownList = document$$1.createElement("ul"), + dirty = false; + + moduleSearch.id = "qunit-modulefilter-search"; + addEvent(moduleSearch, "input", searchInput); + addEvent(moduleSearch, "input", searchFocus); + addEvent(moduleSearch, "focus", searchFocus); + addEvent(moduleSearch, "click", searchFocus); + + label.id = "qunit-modulefilter-search-container"; + label.innerHTML = "Module: "; + label.appendChild(moduleSearch); + + actions.id = "qunit-modulefilter-actions"; + actions.innerHTML = "" + "" + ""; + allCheckbox = actions.lastChild.firstChild; + commit = actions.firstChild; + reset = commit.nextSibling; + addEvent(commit, "click", applyUrlParams); + + dropDownList.id = "qunit-modulefilter-dropdown-list"; + dropDownList.innerHTML = moduleListHtml(); + + dropDown.id = "qunit-modulefilter-dropdown"; + dropDown.style.display = "none"; + dropDown.appendChild(actions); + dropDown.appendChild(dropDownList); + addEvent(dropDown, "change", selectionChange); + selectionChange(); + + moduleFilter.id = "qunit-modulefilter"; + moduleFilter.appendChild(label); + moduleFilter.appendChild(dropDown); + addEvent(moduleFilter, "submit", interceptNavigation); + addEvent(moduleFilter, "reset", function () { + + // Let the reset happen, then update styles + window.setTimeout(selectionChange); + }); + + // Enables show/hide for the dropdown + function searchFocus() { + if (dropDown.style.display !== "none") { + return; + } + + dropDown.style.display = "block"; + addEvent(document$$1, "click", hideHandler); + addEvent(document$$1, "keydown", hideHandler); + + // Hide on Escape keydown or outside-container click + function hideHandler(e) { + var inContainer = moduleFilter.contains(e.target); + + if (e.keyCode === 27 || !inContainer) { + if (e.keyCode === 27 && inContainer) { + moduleSearch.focus(); + } + dropDown.style.display = "none"; + removeEvent(document$$1, "click", hideHandler); + removeEvent(document$$1, "keydown", hideHandler); + moduleSearch.value = ""; + searchInput(); + } + } + } + + // Processes module search box input + function searchInput() { + var i, + item, + searchText = moduleSearch.value.toLowerCase(), + listItems = dropDownList.children; + + for (i = 0; i < listItems.length; i++) { + item = listItems[i]; + if (!searchText || item.textContent.toLowerCase().indexOf(searchText) > -1) { + item.style.display = ""; + } else { + item.style.display = "none"; + } + } + } + + // Processes selection changes + function selectionChange(evt) { + var i, + item, + checkbox = evt && evt.target || allCheckbox, + modulesList = dropDownList.getElementsByTagName("input"), + selectedNames = []; + + toggleClass(checkbox.parentNode, "checked", checkbox.checked); + + dirty = false; + if (checkbox.checked && checkbox !== allCheckbox) { + allCheckbox.checked = false; + removeClass(allCheckbox.parentNode, "checked"); + } + for (i = 0; i < modulesList.length; i++) { + item = modulesList[i]; + if (!evt) { + toggleClass(item.parentNode, "checked", item.checked); + } else if (checkbox === allCheckbox && checkbox.checked) { + item.checked = false; + removeClass(item.parentNode, "checked"); + } + dirty = dirty || item.checked !== item.defaultChecked; + if (item.checked) { + selectedNames.push(item.parentNode.textContent); + } + } + + commit.style.display = reset.style.display = dirty ? "" : "none"; + moduleSearch.placeholder = selectedNames.join(", ") || allCheckbox.parentNode.textContent; + moduleSearch.title = "Type to filter list. Current selection:\n" + (selectedNames.join("\n") || allCheckbox.parentNode.textContent); + } + + return moduleFilter; + } + + function appendToolbar() { + var toolbar = id("qunit-testrunner-toolbar"); + + if (toolbar) { + toolbar.appendChild(toolbarUrlConfigContainer()); + toolbar.appendChild(toolbarModuleFilter()); + toolbar.appendChild(toolbarLooseFilter()); + toolbar.appendChild(document$$1.createElement("div")).className = "clearfix"; + } + } + + function appendHeader() { + var header = id("qunit-header"); + + if (header) { + header.innerHTML = "" + header.innerHTML + " "; + } + } + + function appendBanner() { + var banner = id("qunit-banner"); + + if (banner) { + banner.className = ""; + } + } + + function appendTestResults() { + var tests = id("qunit-tests"), + result = id("qunit-testresult"), + controls; + + if (result) { + result.parentNode.removeChild(result); + } + + if (tests) { + tests.innerHTML = ""; + result = document$$1.createElement("p"); + result.id = "qunit-testresult"; + result.className = "result"; + tests.parentNode.insertBefore(result, tests); + result.innerHTML = "
        Running...
         
        " + "
        " + "
        "; + controls = id("qunit-testresult-controls"); + } + + if (controls) { + controls.appendChild(abortTestsButton()); + } + } + + function appendFilteredTest() { + var testId = QUnit.config.testId; + if (!testId || testId.length <= 0) { + return ""; + } + return "
        Rerunning selected tests: " + escapeText(testId.join(", ")) + " Run all tests
        "; + } + + function appendUserAgent() { + var userAgent = id("qunit-userAgent"); + + if (userAgent) { + userAgent.innerHTML = ""; + userAgent.appendChild(document$$1.createTextNode("QUnit " + QUnit.version + "; " + navigator.userAgent)); + } + } + + function appendInterface() { + var qunit = id("qunit"); + + if (qunit) { + qunit.innerHTML = "

        " + escapeText(document$$1.title) + "

        " + "

        " + "
        " + appendFilteredTest() + "

        " + "
          "; + } + + appendHeader(); + appendBanner(); + appendTestResults(); + appendUserAgent(); + appendToolbar(); + } + + function appendTestsList(modules) { + var i, l, x, z, test, moduleObj; + + for (i = 0, l = modules.length; i < l; i++) { + moduleObj = modules[i]; + + for (x = 0, z = moduleObj.tests.length; x < z; x++) { + test = moduleObj.tests[x]; + + appendTest(test.name, test.testId, moduleObj.name); + } + } + } + + function appendTest(name, testId, moduleName) { + var title, + rerunTrigger, + testBlock, + assertList, + tests = id("qunit-tests"); + + if (!tests) { + return; + } + + title = document$$1.createElement("strong"); + title.innerHTML = getNameHtml(name, moduleName); + + rerunTrigger = document$$1.createElement("a"); + rerunTrigger.innerHTML = "Rerun"; + rerunTrigger.href = setUrl({ testId: testId }); + + testBlock = document$$1.createElement("li"); + testBlock.appendChild(title); + testBlock.appendChild(rerunTrigger); + testBlock.id = "qunit-test-output-" + testId; + + assertList = document$$1.createElement("ol"); + assertList.className = "qunit-assert-list"; + + testBlock.appendChild(assertList); + + tests.appendChild(testBlock); + } + + // HTML Reporter initialization and load + QUnit.begin(function (details) { + var i, moduleObj, tests; + + // Sort modules by name for the picker + for (i = 0; i < details.modules.length; i++) { + moduleObj = details.modules[i]; + if (moduleObj.name) { + modulesList.push(moduleObj.name); + } + } + modulesList.sort(function (a, b) { + return a.localeCompare(b); + }); + + // Initialize QUnit elements + appendInterface(); + appendTestsList(details.modules); + tests = id("qunit-tests"); + if (tests && config.hidepassed) { + addClass(tests, "hidepass"); + } + }); + + QUnit.done(function (details) { + var banner = id("qunit-banner"), + tests = id("qunit-tests"), + abortButton = id("qunit-abort-tests-button"), + totalTests = stats.passedTests + stats.skippedTests + stats.todoTests + stats.failedTests, + html = [totalTests, " tests completed in ", details.runtime, " milliseconds, with ", stats.failedTests, " failed, ", stats.skippedTests, " skipped, and ", stats.todoTests, " todo.
          ", "", details.passed, " assertions of ", details.total, " passed, ", details.failed, " failed."].join(""), + test, + assertLi, + assertList; + + // Update remaing tests to aborted + if (abortButton && abortButton.disabled) { + html = "Tests aborted after " + details.runtime + " milliseconds."; + + for (var i = 0; i < tests.children.length; i++) { + test = tests.children[i]; + if (test.className === "" || test.className === "running") { + test.className = "aborted"; + assertList = test.getElementsByTagName("ol")[0]; + assertLi = document$$1.createElement("li"); + assertLi.className = "fail"; + assertLi.innerHTML = "Test aborted."; + assertList.appendChild(assertLi); + } + } + } + + if (banner && (!abortButton || abortButton.disabled === false)) { + banner.className = stats.failedTests ? "qunit-fail" : "qunit-pass"; + } + + if (abortButton) { + abortButton.parentNode.removeChild(abortButton); + } + + if (tests) { + id("qunit-testresult-display").innerHTML = html; + } + + if (config.altertitle && document$$1.title) { + + // Show ✖ for good, ✔ for bad suite result in title + // use escape sequences in case file gets loaded with non-utf-8 + // charset + document$$1.title = [stats.failedTests ? "\u2716" : "\u2714", document$$1.title.replace(/^[\u2714\u2716] /i, "")].join(" "); + } + + // Scroll back to top to show results + if (config.scrolltop && window.scrollTo) { + window.scrollTo(0, 0); + } + }); + + function getNameHtml(name, module) { + var nameHtml = ""; + + if (module) { + nameHtml = "" + escapeText(module) + ": "; + } + + nameHtml += "" + escapeText(name) + ""; + + return nameHtml; + } + + QUnit.testStart(function (details) { + var running, testBlock, bad; + + testBlock = id("qunit-test-output-" + details.testId); + if (testBlock) { + testBlock.className = "running"; + } else { + + // Report later registered tests + appendTest(details.name, details.testId, details.module); + } + + running = id("qunit-testresult-display"); + if (running) { + bad = QUnit.config.reorder && details.previousFailure; + + running.innerHTML = [bad ? "Rerunning previously failed test:
          " : "Running:
          ", getNameHtml(details.name, details.module)].join(""); + } + }); + + function stripHtml(string) { + + // Strip tags, html entity and whitespaces + return string.replace(/<\/?[^>]+(>|$)/g, "").replace(/\"/g, "").replace(/\s+/g, ""); + } + + QUnit.log(function (details) { + var assertList, + assertLi, + message, + expected, + actual, + diff, + showDiff = false, + testItem = id("qunit-test-output-" + details.testId); + + if (!testItem) { + return; + } + + message = escapeText(details.message) || (details.result ? "okay" : "failed"); + message = "" + message + ""; + message += "@ " + details.runtime + " ms"; + + // The pushFailure doesn't provide details.expected + // when it calls, it's implicit to also not show expected and diff stuff + // Also, we need to check details.expected existence, as it can exist and be undefined + if (!details.result && hasOwn.call(details, "expected")) { + if (details.negative) { + expected = "NOT " + QUnit.dump.parse(details.expected); + } else { + expected = QUnit.dump.parse(details.expected); + } + + actual = QUnit.dump.parse(details.actual); + message += ""; + + if (actual !== expected) { + + message += ""; + + if (typeof details.actual === "number" && typeof details.expected === "number") { + if (!isNaN(details.actual) && !isNaN(details.expected)) { + showDiff = true; + diff = details.actual - details.expected; + diff = (diff > 0 ? "+" : "") + diff; + } + } else if (typeof details.actual !== "boolean" && typeof details.expected !== "boolean") { + diff = QUnit.diff(expected, actual); + + // don't show diff if there is zero overlap + showDiff = stripHtml(diff).length !== stripHtml(expected).length + stripHtml(actual).length; + } + + if (showDiff) { + message += ""; + } + } else if (expected.indexOf("[object Array]") !== -1 || expected.indexOf("[object Object]") !== -1) { + message += ""; + } else { + message += ""; + } + + if (details.source) { + message += ""; + } + + message += "
          Expected:
          " + escapeText(expected) + "
          Result:
          " + escapeText(actual) + "
          Diff:
          " + diff + "
          Message: " + "Diff suppressed as the depth of object is more than current max depth (" + QUnit.config.maxDepth + ").

          Hint: Use QUnit.dump.maxDepth to " + " run with a higher max depth or " + "Rerun without max depth.

          Message: " + "Diff suppressed as the expected and actual results have an equivalent" + " serialization
          Source:
          " + escapeText(details.source) + "
          "; + + // This occurs when pushFailure is set and we have an extracted stack trace + } else if (!details.result && details.source) { + message += "" + "" + "
          Source:
          " + escapeText(details.source) + "
          "; + } + + assertList = testItem.getElementsByTagName("ol")[0]; + + assertLi = document$$1.createElement("li"); + assertLi.className = details.result ? "pass" : "fail"; + assertLi.innerHTML = message; + assertList.appendChild(assertLi); + }); + + QUnit.testDone(function (details) { + var testTitle, + time, + testItem, + assertList, + good, + bad, + testCounts, + skipped, + sourceName, + tests = id("qunit-tests"); + + if (!tests) { + return; + } + + testItem = id("qunit-test-output-" + details.testId); + + assertList = testItem.getElementsByTagName("ol")[0]; + + good = details.passed; + bad = details.failed; + + // This test passed if it has no unexpected failed assertions + var testPassed = details.failed > 0 ? details.todo : !details.todo; + + if (testPassed) { + + // Collapse the passing tests + addClass(assertList, "qunit-collapsed"); + } else if (config.collapse) { + if (!collapseNext) { + + // Skip collapsing the first failing test + collapseNext = true; + } else { + + // Collapse remaining tests + addClass(assertList, "qunit-collapsed"); + } + } + + // The testItem.firstChild is the test name + testTitle = testItem.firstChild; + + testCounts = bad ? "" + bad + ", " + "" + good + ", " : ""; + + testTitle.innerHTML += " (" + testCounts + details.assertions.length + ")"; + + if (details.skipped) { + stats.skippedTests++; + + testItem.className = "skipped"; + skipped = document$$1.createElement("em"); + skipped.className = "qunit-skipped-label"; + skipped.innerHTML = "skipped"; + testItem.insertBefore(skipped, testTitle); + } else { + addEvent(testTitle, "click", function () { + toggleClass(assertList, "qunit-collapsed"); + }); + + testItem.className = testPassed ? "pass" : "fail"; + + if (details.todo) { + var todoLabel = document$$1.createElement("em"); + todoLabel.className = "qunit-todo-label"; + todoLabel.innerHTML = "todo"; + testItem.className += " todo"; + testItem.insertBefore(todoLabel, testTitle); + } + + time = document$$1.createElement("span"); + time.className = "runtime"; + time.innerHTML = details.runtime + " ms"; + testItem.insertBefore(time, assertList); + + if (!testPassed) { + stats.failedTests++; + } else if (details.todo) { + stats.todoTests++; + } else { + stats.passedTests++; + } + } + + // Show the source of the test when showing assertions + if (details.source) { + sourceName = document$$1.createElement("p"); + sourceName.innerHTML = "Source: " + details.source; + addClass(sourceName, "qunit-source"); + if (testPassed) { + addClass(sourceName, "qunit-collapsed"); + } + addEvent(testTitle, "click", function () { + toggleClass(sourceName, "qunit-collapsed"); + }); + testItem.appendChild(sourceName); + } + }); + + // Avoid readyState issue with phantomjs + // Ref: #818 + var notPhantom = function (p) { + return !(p && p.version && p.version.major > 0); + }(window.phantom); + + if (notPhantom && document$$1.readyState === "complete") { + QUnit.load(); + } else { + addEvent(window, "load", QUnit.load); + } + + // Wrap window.onerror. We will call the original window.onerror to see if + // the existing handler fully handles the error; if not, we will call the + // QUnit.onError function. + var originalWindowOnError = window.onerror; + + // Cover uncaught exceptions + // Returning true will suppress the default browser handler, + // returning false will let it run. + window.onerror = function (message, fileName, lineNumber) { + var ret = false; + if (originalWindowOnError) { + for (var _len = arguments.length, args = Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) { + args[_key - 3] = arguments[_key]; + } + + ret = originalWindowOnError.call.apply(originalWindowOnError, [this, message, fileName, lineNumber].concat(args)); + } + + // Treat return value as window.onerror itself does, + // Only do our handling if not suppressed. + if (ret !== true) { + var error = { + message: message, + fileName: fileName, + lineNumber: lineNumber + }; + + ret = QUnit.onError(error); + } + + return ret; + }; + + // Listen for unhandled rejections, and call QUnit.onUnhandledRejection + window.addEventListener("unhandledrejection", function (event) { + QUnit.onUnhandledRejection(event.reason); + }); + })(); + + /* + * This file is a modified version of google-diff-match-patch's JavaScript implementation + * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js), + * modifications are licensed as more fully set forth in LICENSE.txt. + * + * The original source of google-diff-match-patch is attributable and licensed as follows: + * + * Copyright 2006 Google Inc. + * https://code.google.com/p/google-diff-match-patch/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * More Info: + * https://code.google.com/p/google-diff-match-patch/ + * + * Usage: QUnit.diff(expected, actual) + * + */ + QUnit.diff = function () { + function DiffMatchPatch() {} + + // DIFF FUNCTIONS + + /** + * The data structure representing a diff is an array of tuples: + * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] + * which means: delete 'Hello', add 'Goodbye' and keep ' world.' + */ + var DIFF_DELETE = -1, + DIFF_INSERT = 1, + DIFF_EQUAL = 0; + + /** + * Find the differences between two texts. Simplifies the problem by stripping + * any common prefix or suffix off the texts before diffing. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean=} optChecklines Optional speedup flag. If present and false, + * then don't run a line-level diff first to identify the changed areas. + * Defaults to true, which does a faster, slightly less optimal diff. + * @return {!Array.} Array of diff tuples. + */ + DiffMatchPatch.prototype.DiffMain = function (text1, text2, optChecklines) { + var deadline, checklines, commonlength, commonprefix, commonsuffix, diffs; + + // The diff must be complete in up to 1 second. + deadline = new Date().getTime() + 1000; + + // Check for null inputs. + if (text1 === null || text2 === null) { + throw new Error("Null input. (DiffMain)"); + } + + // Check for equality (speedup). + if (text1 === text2) { + if (text1) { + return [[DIFF_EQUAL, text1]]; + } + return []; + } + + if (typeof optChecklines === "undefined") { + optChecklines = true; + } + + checklines = optChecklines; + + // Trim off common prefix (speedup). + commonlength = this.diffCommonPrefix(text1, text2); + commonprefix = text1.substring(0, commonlength); + text1 = text1.substring(commonlength); + text2 = text2.substring(commonlength); + + // Trim off common suffix (speedup). + commonlength = this.diffCommonSuffix(text1, text2); + commonsuffix = text1.substring(text1.length - commonlength); + text1 = text1.substring(0, text1.length - commonlength); + text2 = text2.substring(0, text2.length - commonlength); + + // Compute the diff on the middle block. + diffs = this.diffCompute(text1, text2, checklines, deadline); + + // Restore the prefix and suffix. + if (commonprefix) { + diffs.unshift([DIFF_EQUAL, commonprefix]); + } + if (commonsuffix) { + diffs.push([DIFF_EQUAL, commonsuffix]); + } + this.diffCleanupMerge(diffs); + return diffs; + }; + + /** + * Reduce the number of edits by eliminating operationally trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupEfficiency = function (diffs) { + var changes, equalities, equalitiesLength, lastequality, pointer, preIns, preDel, postIns, postDel; + changes = false; + equalities = []; // Stack of indices where equalities are found. + equalitiesLength = 0; // Keeping our own length var is faster in JS. + /** @type {?string} */ + lastequality = null; + + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + pointer = 0; // Index of current position. + + // Is there an insertion operation before the last equality. + preIns = false; + + // Is there a deletion operation before the last equality. + preDel = false; + + // Is there an insertion operation after the last equality. + postIns = false; + + // Is there a deletion operation after the last equality. + postDel = false; + while (pointer < diffs.length) { + + // Equality found. + if (diffs[pointer][0] === DIFF_EQUAL) { + if (diffs[pointer][1].length < 4 && (postIns || postDel)) { + + // Candidate found. + equalities[equalitiesLength++] = pointer; + preIns = postIns; + preDel = postDel; + lastequality = diffs[pointer][1]; + } else { + + // Not a candidate, and can never become one. + equalitiesLength = 0; + lastequality = null; + } + postIns = postDel = false; + + // An insertion or deletion. + } else { + + if (diffs[pointer][0] === DIFF_DELETE) { + postDel = true; + } else { + postIns = true; + } + + /* + * Five types to be split: + * ABXYCD + * AXCD + * ABXC + * AXCD + * ABXC + */ + if (lastequality && (preIns && preDel && postIns && postDel || lastequality.length < 2 && preIns + preDel + postIns + postDel === 3)) { + + // Duplicate record. + diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]); + + // Change second copy to insert. + diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; + equalitiesLength--; // Throw away the equality we just deleted; + lastequality = null; + if (preIns && preDel) { + + // No changes made which could affect previous entry, keep going. + postIns = postDel = true; + equalitiesLength = 0; + } else { + equalitiesLength--; // Throw away the previous equality. + pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; + postIns = postDel = false; + } + changes = true; + } + } + pointer++; + } + + if (changes) { + this.diffCleanupMerge(diffs); + } + }; + + /** + * Convert a diff array into a pretty HTML report. + * @param {!Array.} diffs Array of diff tuples. + * @param {integer} string to be beautified. + * @return {string} HTML representation. + */ + DiffMatchPatch.prototype.diffPrettyHtml = function (diffs) { + var op, + data, + x, + html = []; + for (x = 0; x < diffs.length; x++) { + op = diffs[x][0]; // Operation (insert, delete, equal) + data = diffs[x][1]; // Text of change. + switch (op) { + case DIFF_INSERT: + html[x] = "" + escapeText(data) + ""; + break; + case DIFF_DELETE: + html[x] = "" + escapeText(data) + ""; + break; + case DIFF_EQUAL: + html[x] = "" + escapeText(data) + ""; + break; + } + } + return html.join(""); + }; + + /** + * Determine the common prefix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the start of each + * string. + */ + DiffMatchPatch.prototype.diffCommonPrefix = function (text1, text2) { + var pointermid, pointermax, pointermin, pointerstart; + + // Quick check for common null cases. + if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0)) { + return 0; + } + + // Binary search. + // Performance analysis: https://neil.fraser.name/news/2007/10/09/ + pointermin = 0; + pointermax = Math.min(text1.length, text2.length); + pointermid = pointermax; + pointerstart = 0; + while (pointermin < pointermid) { + if (text1.substring(pointerstart, pointermid) === text2.substring(pointerstart, pointermid)) { + pointermin = pointermid; + pointerstart = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + return pointermid; + }; + + /** + * Determine the common suffix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of each string. + */ + DiffMatchPatch.prototype.diffCommonSuffix = function (text1, text2) { + var pointermid, pointermax, pointermin, pointerend; + + // Quick check for common null cases. + if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) { + return 0; + } + + // Binary search. + // Performance analysis: https://neil.fraser.name/news/2007/10/09/ + pointermin = 0; + pointermax = Math.min(text1.length, text2.length); + pointermid = pointermax; + pointerend = 0; + while (pointermin < pointermid) { + if (text1.substring(text1.length - pointermid, text1.length - pointerend) === text2.substring(text2.length - pointermid, text2.length - pointerend)) { + pointermin = pointermid; + pointerend = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + return pointermid; + }; + + /** + * Find the differences between two texts. Assumes that the texts do not + * have any common prefix or suffix. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean} checklines Speedup flag. If false, then don't run a + * line-level diff first to identify the changed areas. + * If true, then run a faster, slightly less optimal diff. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffCompute = function (text1, text2, checklines, deadline) { + var diffs, longtext, shorttext, i, hm, text1A, text2A, text1B, text2B, midCommon, diffsA, diffsB; + + if (!text1) { + + // Just add some text (speedup). + return [[DIFF_INSERT, text2]]; + } + + if (!text2) { + + // Just delete some text (speedup). + return [[DIFF_DELETE, text1]]; + } + + longtext = text1.length > text2.length ? text1 : text2; + shorttext = text1.length > text2.length ? text2 : text1; + i = longtext.indexOf(shorttext); + if (i !== -1) { + + // Shorter text is inside the longer text (speedup). + diffs = [[DIFF_INSERT, longtext.substring(0, i)], [DIFF_EQUAL, shorttext], [DIFF_INSERT, longtext.substring(i + shorttext.length)]]; + + // Swap insertions for deletions if diff is reversed. + if (text1.length > text2.length) { + diffs[0][0] = diffs[2][0] = DIFF_DELETE; + } + return diffs; + } + + if (shorttext.length === 1) { + + // Single character string. + // After the previous speedup, the character can't be an equality. + return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; + } + + // Check to see if the problem can be split in two. + hm = this.diffHalfMatch(text1, text2); + if (hm) { + + // A half-match was found, sort out the return data. + text1A = hm[0]; + text1B = hm[1]; + text2A = hm[2]; + text2B = hm[3]; + midCommon = hm[4]; + + // Send both pairs off for separate processing. + diffsA = this.DiffMain(text1A, text2A, checklines, deadline); + diffsB = this.DiffMain(text1B, text2B, checklines, deadline); + + // Merge the results. + return diffsA.concat([[DIFF_EQUAL, midCommon]], diffsB); + } + + if (checklines && text1.length > 100 && text2.length > 100) { + return this.diffLineMode(text1, text2, deadline); + } + + return this.diffBisect(text1, text2, deadline); + }; + + /** + * Do the two texts share a substring which is at least half the length of the + * longer text? + * This speedup can produce non-minimal diffs. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {Array.} Five element Array, containing the prefix of + * text1, the suffix of text1, the prefix of text2, the suffix of + * text2 and the common middle. Or null if there was no match. + * @private + */ + DiffMatchPatch.prototype.diffHalfMatch = function (text1, text2) { + var longtext, shorttext, dmp, text1A, text2B, text2A, text1B, midCommon, hm1, hm2, hm; + + longtext = text1.length > text2.length ? text1 : text2; + shorttext = text1.length > text2.length ? text2 : text1; + if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { + return null; // Pointless. + } + dmp = this; // 'this' becomes 'window' in a closure. + + /** + * Does a substring of shorttext exist within longtext such that the substring + * is at least half the length of longtext? + * Closure, but does not reference any external variables. + * @param {string} longtext Longer string. + * @param {string} shorttext Shorter string. + * @param {number} i Start index of quarter length substring within longtext. + * @return {Array.} Five element Array, containing the prefix of + * longtext, the suffix of longtext, the prefix of shorttext, the suffix + * of shorttext and the common middle. Or null if there was no match. + * @private + */ + function diffHalfMatchI(longtext, shorttext, i) { + var seed, j, bestCommon, prefixLength, suffixLength, bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB; + + // Start with a 1/4 length substring at position i as a seed. + seed = longtext.substring(i, i + Math.floor(longtext.length / 4)); + j = -1; + bestCommon = ""; + while ((j = shorttext.indexOf(seed, j + 1)) !== -1) { + prefixLength = dmp.diffCommonPrefix(longtext.substring(i), shorttext.substring(j)); + suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), shorttext.substring(0, j)); + if (bestCommon.length < suffixLength + prefixLength) { + bestCommon = shorttext.substring(j - suffixLength, j) + shorttext.substring(j, j + prefixLength); + bestLongtextA = longtext.substring(0, i - suffixLength); + bestLongtextB = longtext.substring(i + prefixLength); + bestShorttextA = shorttext.substring(0, j - suffixLength); + bestShorttextB = shorttext.substring(j + prefixLength); + } + } + if (bestCommon.length * 2 >= longtext.length) { + return [bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB, bestCommon]; + } else { + return null; + } + } + + // First check if the second quarter is the seed for a half-match. + hm1 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 4)); + + // Check again based on the third quarter. + hm2 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 2)); + if (!hm1 && !hm2) { + return null; + } else if (!hm2) { + hm = hm1; + } else if (!hm1) { + hm = hm2; + } else { + + // Both matched. Select the longest. + hm = hm1[4].length > hm2[4].length ? hm1 : hm2; + } + + // A half-match was found, sort out the return data. + if (text1.length > text2.length) { + text1A = hm[0]; + text1B = hm[1]; + text2A = hm[2]; + text2B = hm[3]; + } else { + text2A = hm[0]; + text2B = hm[1]; + text1A = hm[2]; + text1B = hm[3]; + } + midCommon = hm[4]; + return [text1A, text1B, text2A, text2B, midCommon]; + }; + + /** + * Do a quick line-level diff on both strings, then rediff the parts for + * greater accuracy. + * This speedup can produce non-minimal diffs. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffLineMode = function (text1, text2, deadline) { + var a, diffs, linearray, pointer, countInsert, countDelete, textInsert, textDelete, j; + + // Scan the text on a line-by-line basis first. + a = this.diffLinesToChars(text1, text2); + text1 = a.chars1; + text2 = a.chars2; + linearray = a.lineArray; + + diffs = this.DiffMain(text1, text2, false, deadline); + + // Convert the diff back to original text. + this.diffCharsToLines(diffs, linearray); + + // Eliminate freak matches (e.g. blank lines) + this.diffCleanupSemantic(diffs); + + // Rediff any replacement blocks, this time character-by-character. + // Add a dummy entry at the end. + diffs.push([DIFF_EQUAL, ""]); + pointer = 0; + countDelete = 0; + countInsert = 0; + textDelete = ""; + textInsert = ""; + while (pointer < diffs.length) { + switch (diffs[pointer][0]) { + case DIFF_INSERT: + countInsert++; + textInsert += diffs[pointer][1]; + break; + case DIFF_DELETE: + countDelete++; + textDelete += diffs[pointer][1]; + break; + case DIFF_EQUAL: + + // Upon reaching an equality, check for prior redundancies. + if (countDelete >= 1 && countInsert >= 1) { + + // Delete the offending records and add the merged ones. + diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert); + pointer = pointer - countDelete - countInsert; + a = this.DiffMain(textDelete, textInsert, false, deadline); + for (j = a.length - 1; j >= 0; j--) { + diffs.splice(pointer, 0, a[j]); + } + pointer = pointer + a.length; + } + countInsert = 0; + countDelete = 0; + textDelete = ""; + textInsert = ""; + break; + } + pointer++; + } + diffs.pop(); // Remove the dummy entry at the end. + + return diffs; + }; + + /** + * Find the 'middle snake' of a diff, split the problem in two + * and return the recursively constructed diff. + * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffBisect = function (text1, text2, deadline) { + var text1Length, text2Length, maxD, vOffset, vLength, v1, v2, x, delta, front, k1start, k1end, k2start, k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2; + + // Cache the text lengths to prevent multiple calls. + text1Length = text1.length; + text2Length = text2.length; + maxD = Math.ceil((text1Length + text2Length) / 2); + vOffset = maxD; + vLength = 2 * maxD; + v1 = new Array(vLength); + v2 = new Array(vLength); + + // Setting all elements to -1 is faster in Chrome & Firefox than mixing + // integers and undefined. + for (x = 0; x < vLength; x++) { + v1[x] = -1; + v2[x] = -1; + } + v1[vOffset + 1] = 0; + v2[vOffset + 1] = 0; + delta = text1Length - text2Length; + + // If the total number of characters is odd, then the front path will collide + // with the reverse path. + front = delta % 2 !== 0; + + // Offsets for start and end of k loop. + // Prevents mapping of space beyond the grid. + k1start = 0; + k1end = 0; + k2start = 0; + k2end = 0; + for (d = 0; d < maxD; d++) { + + // Bail out if deadline is reached. + if (new Date().getTime() > deadline) { + break; + } + + // Walk the front path one step. + for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { + k1Offset = vOffset + k1; + if (k1 === -d || k1 !== d && v1[k1Offset - 1] < v1[k1Offset + 1]) { + x1 = v1[k1Offset + 1]; + } else { + x1 = v1[k1Offset - 1] + 1; + } + y1 = x1 - k1; + while (x1 < text1Length && y1 < text2Length && text1.charAt(x1) === text2.charAt(y1)) { + x1++; + y1++; + } + v1[k1Offset] = x1; + if (x1 > text1Length) { + + // Ran off the right of the graph. + k1end += 2; + } else if (y1 > text2Length) { + + // Ran off the bottom of the graph. + k1start += 2; + } else if (front) { + k2Offset = vOffset + delta - k1; + if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) { + + // Mirror x2 onto top-left coordinate system. + x2 = text1Length - v2[k2Offset]; + if (x1 >= x2) { + + // Overlap detected. + return this.diffBisectSplit(text1, text2, x1, y1, deadline); + } + } + } + } + + // Walk the reverse path one step. + for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { + k2Offset = vOffset + k2; + if (k2 === -d || k2 !== d && v2[k2Offset - 1] < v2[k2Offset + 1]) { + x2 = v2[k2Offset + 1]; + } else { + x2 = v2[k2Offset - 1] + 1; + } + y2 = x2 - k2; + while (x2 < text1Length && y2 < text2Length && text1.charAt(text1Length - x2 - 1) === text2.charAt(text2Length - y2 - 1)) { + x2++; + y2++; + } + v2[k2Offset] = x2; + if (x2 > text1Length) { + + // Ran off the left of the graph. + k2end += 2; + } else if (y2 > text2Length) { + + // Ran off the top of the graph. + k2start += 2; + } else if (!front) { + k1Offset = vOffset + delta - k2; + if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) { + x1 = v1[k1Offset]; + y1 = vOffset + x1 - k1Offset; + + // Mirror x2 onto top-left coordinate system. + x2 = text1Length - x2; + if (x1 >= x2) { + + // Overlap detected. + return this.diffBisectSplit(text1, text2, x1, y1, deadline); + } + } + } + } + } + + // Diff took too long and hit the deadline or + // number of diffs equals number of characters, no commonality at all. + return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; + }; + + /** + * Given the location of the 'middle snake', split the diff in two parts + * and recurse. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} x Index of split point in text1. + * @param {number} y Index of split point in text2. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffBisectSplit = function (text1, text2, x, y, deadline) { + var text1a, text1b, text2a, text2b, diffs, diffsb; + text1a = text1.substring(0, x); + text2a = text2.substring(0, y); + text1b = text1.substring(x); + text2b = text2.substring(y); + + // Compute both diffs serially. + diffs = this.DiffMain(text1a, text2a, false, deadline); + diffsb = this.DiffMain(text1b, text2b, false, deadline); + + return diffs.concat(diffsb); + }; + + /** + * Reduce the number of edits by eliminating semantically trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupSemantic = function (diffs) { + var changes, equalities, equalitiesLength, lastequality, pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2; + changes = false; + equalities = []; // Stack of indices where equalities are found. + equalitiesLength = 0; // Keeping our own length var is faster in JS. + /** @type {?string} */ + lastequality = null; + + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + pointer = 0; // Index of current position. + + // Number of characters that changed prior to the equality. + lengthInsertions1 = 0; + lengthDeletions1 = 0; + + // Number of characters that changed after the equality. + lengthInsertions2 = 0; + lengthDeletions2 = 0; + while (pointer < diffs.length) { + if (diffs[pointer][0] === DIFF_EQUAL) { + // Equality found. + equalities[equalitiesLength++] = pointer; + lengthInsertions1 = lengthInsertions2; + lengthDeletions1 = lengthDeletions2; + lengthInsertions2 = 0; + lengthDeletions2 = 0; + lastequality = diffs[pointer][1]; + } else { + // An insertion or deletion. + if (diffs[pointer][0] === DIFF_INSERT) { + lengthInsertions2 += diffs[pointer][1].length; + } else { + lengthDeletions2 += diffs[pointer][1].length; + } + + // Eliminate an equality that is smaller or equal to the edits on both + // sides of it. + if (lastequality && lastequality.length <= Math.max(lengthInsertions1, lengthDeletions1) && lastequality.length <= Math.max(lengthInsertions2, lengthDeletions2)) { + + // Duplicate record. + diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]); + + // Change second copy to insert. + diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; + + // Throw away the equality we just deleted. + equalitiesLength--; + + // Throw away the previous equality (it needs to be reevaluated). + equalitiesLength--; + pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; + + // Reset the counters. + lengthInsertions1 = 0; + lengthDeletions1 = 0; + lengthInsertions2 = 0; + lengthDeletions2 = 0; + lastequality = null; + changes = true; + } + } + pointer++; + } + + // Normalize the diff. + if (changes) { + this.diffCleanupMerge(diffs); + } + + // Find any overlaps between deletions and insertions. + // e.g: abcxxxxxxdef + // -> abcxxxdef + // e.g: xxxabcdefxxx + // -> defxxxabc + // Only extract an overlap if it is as big as the edit ahead or behind it. + pointer = 1; + while (pointer < diffs.length) { + if (diffs[pointer - 1][0] === DIFF_DELETE && diffs[pointer][0] === DIFF_INSERT) { + deletion = diffs[pointer - 1][1]; + insertion = diffs[pointer][1]; + overlapLength1 = this.diffCommonOverlap(deletion, insertion); + overlapLength2 = this.diffCommonOverlap(insertion, deletion); + if (overlapLength1 >= overlapLength2) { + if (overlapLength1 >= deletion.length / 2 || overlapLength1 >= insertion.length / 2) { + + // Overlap found. Insert an equality and trim the surrounding edits. + diffs.splice(pointer, 0, [DIFF_EQUAL, insertion.substring(0, overlapLength1)]); + diffs[pointer - 1][1] = deletion.substring(0, deletion.length - overlapLength1); + diffs[pointer + 1][1] = insertion.substring(overlapLength1); + pointer++; + } + } else { + if (overlapLength2 >= deletion.length / 2 || overlapLength2 >= insertion.length / 2) { + + // Reverse overlap found. + // Insert an equality and swap and trim the surrounding edits. + diffs.splice(pointer, 0, [DIFF_EQUAL, deletion.substring(0, overlapLength2)]); + + diffs[pointer - 1][0] = DIFF_INSERT; + diffs[pointer - 1][1] = insertion.substring(0, insertion.length - overlapLength2); + diffs[pointer + 1][0] = DIFF_DELETE; + diffs[pointer + 1][1] = deletion.substring(overlapLength2); + pointer++; + } + } + pointer++; + } + pointer++; + } + }; + + /** + * Determine if the suffix of one string is the prefix of another. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of the first + * string and the start of the second string. + * @private + */ + DiffMatchPatch.prototype.diffCommonOverlap = function (text1, text2) { + var text1Length, text2Length, textLength, best, length, pattern, found; + + // Cache the text lengths to prevent multiple calls. + text1Length = text1.length; + text2Length = text2.length; + + // Eliminate the null case. + if (text1Length === 0 || text2Length === 0) { + return 0; + } + + // Truncate the longer string. + if (text1Length > text2Length) { + text1 = text1.substring(text1Length - text2Length); + } else if (text1Length < text2Length) { + text2 = text2.substring(0, text1Length); + } + textLength = Math.min(text1Length, text2Length); + + // Quick check for the worst case. + if (text1 === text2) { + return textLength; + } + + // Start by looking for a single character match + // and increase length until no match is found. + // Performance analysis: https://neil.fraser.name/news/2010/11/04/ + best = 0; + length = 1; + while (true) { + pattern = text1.substring(textLength - length); + found = text2.indexOf(pattern); + if (found === -1) { + return best; + } + length += found; + if (found === 0 || text1.substring(textLength - length) === text2.substring(0, length)) { + best = length; + length++; + } + } + }; + + /** + * Split two texts into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {{chars1: string, chars2: string, lineArray: !Array.}} + * An object containing the encoded text1, the encoded text2 and + * the array of unique strings. + * The zeroth element of the array of unique strings is intentionally blank. + * @private + */ + DiffMatchPatch.prototype.diffLinesToChars = function (text1, text2) { + var lineArray, lineHash, chars1, chars2; + lineArray = []; // E.g. lineArray[4] === 'Hello\n' + lineHash = {}; // E.g. lineHash['Hello\n'] === 4 + + // '\x00' is a valid character, but various debuggers don't like it. + // So we'll insert a junk entry to avoid generating a null character. + lineArray[0] = ""; + + /** + * Split a text into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * Modifies linearray and linehash through being a closure. + * @param {string} text String to encode. + * @return {string} Encoded string. + * @private + */ + function diffLinesToCharsMunge(text) { + var chars, lineStart, lineEnd, lineArrayLength, line; + chars = ""; + + // Walk the text, pulling out a substring for each line. + // text.split('\n') would would temporarily double our memory footprint. + // Modifying text would create many large strings to garbage collect. + lineStart = 0; + lineEnd = -1; + + // Keeping our own length variable is faster than looking it up. + lineArrayLength = lineArray.length; + while (lineEnd < text.length - 1) { + lineEnd = text.indexOf("\n", lineStart); + if (lineEnd === -1) { + lineEnd = text.length - 1; + } + line = text.substring(lineStart, lineEnd + 1); + lineStart = lineEnd + 1; + + var lineHashExists = lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : lineHash[line] !== undefined; + + if (lineHashExists) { + chars += String.fromCharCode(lineHash[line]); + } else { + chars += String.fromCharCode(lineArrayLength); + lineHash[line] = lineArrayLength; + lineArray[lineArrayLength++] = line; + } + } + return chars; + } + + chars1 = diffLinesToCharsMunge(text1); + chars2 = diffLinesToCharsMunge(text2); + return { + chars1: chars1, + chars2: chars2, + lineArray: lineArray + }; + }; + + /** + * Rehydrate the text in a diff from a string of line hashes to real lines of + * text. + * @param {!Array.} diffs Array of diff tuples. + * @param {!Array.} lineArray Array of unique strings. + * @private + */ + DiffMatchPatch.prototype.diffCharsToLines = function (diffs, lineArray) { + var x, chars, text, y; + for (x = 0; x < diffs.length; x++) { + chars = diffs[x][1]; + text = []; + for (y = 0; y < chars.length; y++) { + text[y] = lineArray[chars.charCodeAt(y)]; + } + diffs[x][1] = text.join(""); + } + }; + + /** + * Reorder and merge like edit sections. Merge equalities. + * Any edit section can move as long as it doesn't cross an equality. + * @param {!Array.} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupMerge = function (diffs) { + var pointer, countDelete, countInsert, textInsert, textDelete, commonlength, changes, diffPointer, position; + diffs.push([DIFF_EQUAL, ""]); // Add a dummy entry at the end. + pointer = 0; + countDelete = 0; + countInsert = 0; + textDelete = ""; + textInsert = ""; + + while (pointer < diffs.length) { + switch (diffs[pointer][0]) { + case DIFF_INSERT: + countInsert++; + textInsert += diffs[pointer][1]; + pointer++; + break; + case DIFF_DELETE: + countDelete++; + textDelete += diffs[pointer][1]; + pointer++; + break; + case DIFF_EQUAL: + + // Upon reaching an equality, check for prior redundancies. + if (countDelete + countInsert > 1) { + if (countDelete !== 0 && countInsert !== 0) { + + // Factor out any common prefixes. + commonlength = this.diffCommonPrefix(textInsert, textDelete); + if (commonlength !== 0) { + if (pointer - countDelete - countInsert > 0 && diffs[pointer - countDelete - countInsert - 1][0] === DIFF_EQUAL) { + diffs[pointer - countDelete - countInsert - 1][1] += textInsert.substring(0, commonlength); + } else { + diffs.splice(0, 0, [DIFF_EQUAL, textInsert.substring(0, commonlength)]); + pointer++; + } + textInsert = textInsert.substring(commonlength); + textDelete = textDelete.substring(commonlength); + } + + // Factor out any common suffixies. + commonlength = this.diffCommonSuffix(textInsert, textDelete); + if (commonlength !== 0) { + diffs[pointer][1] = textInsert.substring(textInsert.length - commonlength) + diffs[pointer][1]; + textInsert = textInsert.substring(0, textInsert.length - commonlength); + textDelete = textDelete.substring(0, textDelete.length - commonlength); + } + } + + // Delete the offending records and add the merged ones. + if (countDelete === 0) { + diffs.splice(pointer - countInsert, countDelete + countInsert, [DIFF_INSERT, textInsert]); + } else if (countInsert === 0) { + diffs.splice(pointer - countDelete, countDelete + countInsert, [DIFF_DELETE, textDelete]); + } else { + diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert, [DIFF_DELETE, textDelete], [DIFF_INSERT, textInsert]); + } + pointer = pointer - countDelete - countInsert + (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1; + } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) { + + // Merge this equality with the previous one. + diffs[pointer - 1][1] += diffs[pointer][1]; + diffs.splice(pointer, 1); + } else { + pointer++; + } + countInsert = 0; + countDelete = 0; + textDelete = ""; + textInsert = ""; + break; + } + } + if (diffs[diffs.length - 1][1] === "") { + diffs.pop(); // Remove the dummy entry at the end. + } + + // Second pass: look for single edits surrounded on both sides by equalities + // which can be shifted sideways to eliminate an equality. + // e.g: ABAC -> ABAC + changes = false; + pointer = 1; + + // Intentionally ignore the first and last element (don't need checking). + while (pointer < diffs.length - 1) { + if (diffs[pointer - 1][0] === DIFF_EQUAL && diffs[pointer + 1][0] === DIFF_EQUAL) { + + diffPointer = diffs[pointer][1]; + position = diffPointer.substring(diffPointer.length - diffs[pointer - 1][1].length); + + // This is a single edit surrounded by equalities. + if (position === diffs[pointer - 1][1]) { + + // Shift the edit over the previous equality. + diffs[pointer][1] = diffs[pointer - 1][1] + diffs[pointer][1].substring(0, diffs[pointer][1].length - diffs[pointer - 1][1].length); + diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; + diffs.splice(pointer - 1, 1); + changes = true; + } else if (diffPointer.substring(0, diffs[pointer + 1][1].length) === diffs[pointer + 1][1]) { + + // Shift the edit over the next equality. + diffs[pointer - 1][1] += diffs[pointer + 1][1]; + diffs[pointer][1] = diffs[pointer][1].substring(diffs[pointer + 1][1].length) + diffs[pointer + 1][1]; + diffs.splice(pointer + 1, 1); + changes = true; + } + } + pointer++; + } + + // If shifts were made, the diff needs reordering and another shift sweep. + if (changes) { + this.diffCleanupMerge(diffs); + } + }; + + return function (o, n) { + var diff, output, text; + diff = new DiffMatchPatch(); + output = diff.DiffMain(o, n); + diff.diffCleanupEfficiency(output); + text = diff.diffPrettyHtml(output); + + return text; + }; + }(); + +}((function() { return this; }()))); From f5792e8a486de6d4bb9e9d8f9bd9092696efe402 Mon Sep 17 00:00:00 2001 From: jashkenas Date: Wed, 18 Apr 2018 10:35:17 -0700 Subject: [PATCH 259/263] Upgrading Uglify minifier --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 4f531c0b8..c318e7893 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "pretty-bytes-cli": "^1.0.0", "qunit-cli": "~0.2.0", "qunit": "^2.6.0", - "uglify-js": "2.4.x" + "uglify-js": "3.3.21" }, "scripts": { "test": "npm run test-node && npm run lint", @@ -37,7 +37,7 @@ "test-node": "qunit-cli test/*.js", "test-browser": "npm i karma-phantomjs-launcher && karma start", "minify": "uglifyjs underscore.js -c \"evaluate=false\" --comments \"/ .*/\" -m", - "build": "npm run minify -- --source-map underscore-min.map --source-map-url \" \" -o underscore-min.js", + "build": "npm run minify -- --source-map \"filename='underscore-min.map'\" --source-map-url \" \" -o underscore-min.js", "doc": "docco underscore.js", "weight": "npm run minify | gzip-size | pretty-bytes" }, From a1ea916a4c0616dfc3b546d684f9148641d8071f Mon Sep 17 00:00:00 2001 From: jashkenas Date: Wed, 18 Apr 2018 10:36:39 -0700 Subject: [PATCH 260/263] COC as last word in the README. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a99db1895..69e8b56c4 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,6 @@ Underscore.js is a utility-belt library for JavaScript that provides support for the usual functional suspects (each, map, reduce, filter...) without extending any core JavaScript objects. -This project adheres to a [code of conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. - For Docs, License, Tests, and pre-packed downloads, see: http://underscorejs.org @@ -26,3 +24,5 @@ https://github.com/documentcloud Many thanks to our contributors: https://github.com/jashkenas/underscore/contributors + +This project adheres to a [code of conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. From 7e43b56ad4a63ea109c6e50b27b961a6bd84e1f9 Mon Sep 17 00:00:00 2001 From: jashkenas Date: Wed, 18 Apr 2018 10:40:05 -0700 Subject: [PATCH 261/263] s/param/argument --- underscore.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/underscore.js b/underscore.js index be0cef830..c196962a1 100644 --- a/underscore.js +++ b/underscore.js @@ -71,8 +71,7 @@ case 1: return function(value) { return func.call(context, value); }; - // The 2-parameter case has been omitted only because no current consumers - // made use of it. + // The 2-argument case is omitted because we’re not using it. case 3: return function(value, index, collection) { return func.call(context, value, index, collection); }; From ac689556db21a5336e94aaff0203451e9f0776a7 Mon Sep 17 00:00:00 2001 From: jashkenas Date: Wed, 18 Apr 2018 11:23:06 -0700 Subject: [PATCH 262/263] Removing old ESLint rules --- .eslintrc | 86 ------------------------------------------------------- 1 file changed, 86 deletions(-) diff --git a/.eslintrc b/.eslintrc index 5ab39e461..6aa275a7d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -3,91 +3,5 @@ "browser": true, "node": true, "amd": true - }, - - "rules": { - "array-bracket-spacing": [2], - "block-scoped-var": 1, - "brace-style": [1, "1tbs"], - "camelcase": 2, - "comma-dangle": [2, "never"], - "comma-spacing": 2, - "computed-property-spacing": [2, "never"], - "consistent-return": 1, - "dot-notation": [2, { "allowKeywords": false }], - "eol-last": 2, - "eqeqeq": [2, "smart"], - "indent": [2, 2, {"SwitchCase": 1, "VariableDeclarator": 2}], - "key-spacing": 1, - "linebreak-style": 2, - "max-depth": [1, 4], - "max-params": [1, 5], - "new-cap": 2, - "no-alert": 2, - "no-caller": 2, - "no-catch-shadow": 2, - "no-console": 2, - "no-debugger": 2, - "no-delete-var": 2, - "no-div-regex": 1, - "no-dupe-args": 2, - "no-dupe-keys": 2, - "no-duplicate-case": 2, - "no-else-return": 1, - "no-empty-character-class": 2, - "no-empty-label": 2, - "no-eval": 2, - "no-ex-assign": 2, - "no-extend-native": 2, - "no-extra-boolean-cast": 2, - "no-extra-parens": 1, - "no-extra-semi": 2, - "no-fallthrough": 2, - "no-floating-decimal": 2, - "no-func-assign": 2, - "no-implied-eval": 2, - "no-inner-declarations": 2, - "no-irregular-whitespace": 2, - "no-label-var": 2, - "no-lone-blocks": 2, - "no-lonely-if": 2, - "no-multi-spaces": 1, - "no-multi-str": 2, - "no-native-reassign": 2, - "no-negated-in-lhs": 1, - "no-nested-ternary": 2, - "no-new-object": 2, - "no-new-wrappers": 2, - "no-obj-calls": 2, - "no-octal": 2, - "no-octal-escape": 2, - "no-proto": 2, - "no-redeclare": 2, - "no-shadow": 2, - "no-spaced-func": 2, - "no-throw-literal": 2, - "no-trailing-spaces": 2, - "no-undef": 2, - "no-undef-init": 2, - "no-undefined": 2, - "no-unneeded-ternary": 2, - "no-unreachable": 2, - "no-unused-expressions": 2, - "no-unused-vars": 2, - "no-use-before-define": [1, "nofunc"], - "no-with": 2, - "object-curly-spacing": [2, "never"], - "quote-props": [1, "as-needed"], - "quotes": [2, "single", "avoid-escape"], - "radix": 2, - "semi": 2, - "space-after-keywords": [2, "always"], - "space-before-function-paren": [2, {"anonymous": "never", "named": "never"}], - "space-infix-ops": 2, - "space-return-throw-case": 2, - "space-unary-ops": [2, { "words": true, "nonwords": false }], - "use-isnan": 2, - "valid-typeof": 2, - "wrap-iife": 2 } } From cbecaf0377a3fde1e25fdb3d3138b27466bc4eb7 Mon Sep 17 00:00:00 2001 From: jashkenas Date: Wed, 18 Apr 2018 11:39:30 -0700 Subject: [PATCH 263/263] Underscore.js 1.9.0 --- docs/underscore.html | 1829 +++++++++++++++++++++++------------------ index.html | 166 +++- package.json | 6 +- underscore-min.js | 7 +- underscore-min.js.map | 1 + underscore-min.map | 1 - underscore.js | 13 +- 7 files changed, 1202 insertions(+), 821 deletions(-) create mode 100644 underscore-min.js.map delete mode 100644 underscore-min.map diff --git a/docs/underscore.html b/docs/underscore.html index 2170598b4..77e841493 100644 --- a/docs/underscore.html +++ b/docs/underscore.html @@ -27,15 +27,15 @@

          underscore.js

          -
          Underscore.js 1.8.3
          +              
          Underscore.js 1.9.0
           http://underscorejs.org
          -(c) 2009-2017 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
          +(c) 2009-2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
           Underscore may be freely distributed under the MIT license.
           
          -(function() {
          +(function() {
          @@ -71,11 +71,16 @@

          Baseline setup

          -

          Establish the root object, window in the browser, or exports on the server.

          +

          Establish the root object, window (self) in the browser, global +on the server, or this in some virtual machines. We use self +instead of window for WebWorker support.

          -
            var root = this;
          +
            var root = typeof self == 'object' && self.self === self && self ||
          +            typeof global == 'object' && global.global === global && global ||
          +            this ||
          +            {};
          @@ -105,7 +110,8 @@

          Baseline setup

          -
            var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
          +
            var ArrayProto = Array.prototype, ObjProto = Object.prototype;
          +  var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null;
          @@ -120,11 +126,10 @@

          Baseline setup

          -
            var
          -    push             = ArrayProto.push,
          -    slice            = ArrayProto.slice,
          -    toString         = ObjProto.toString,
          -    hasOwnProperty   = ObjProto.hasOwnProperty;
          +
            var push = ArrayProto.push,
          +      slice = ArrayProto.slice,
          +      toString = ObjProto.toString,
          +      hasOwnProperty = ObjProto.hasOwnProperty;
          @@ -140,11 +145,9 @@

          Baseline setup

          -
            var
          -    nativeIsArray      = Array.isArray,
          -    nativeKeys         = Object.keys,
          -    nativeBind         = FuncProto.bind,
          -    nativeCreate       = Object.create;
          +
            var nativeIsArray = Array.isArray,
          +      nativeKeys = Object.keys,
          +      nativeCreate = Object.create;
          @@ -159,7 +162,7 @@

          Baseline setup

          -
            var Ctor = function(){};
          +
            var Ctor = function(){};
          @@ -174,7 +177,7 @@

          Baseline setup

          -
            var _ = function(obj) {
          +            
            var _ = function(obj) {
               if (obj instanceof _) return obj;
               if (!(this instanceof _)) return new _(obj);
               this._wrapped = obj;
          @@ -190,13 +193,15 @@ 

          Baseline setup

          Export the Underscore object for Node.js, with -backwards-compatibility for the old require() API. If we’re in -the browser, add _ as a global object.

          +backwards-compatibility for their old module API. If we’re in +the browser, add _ as a global object. +(nodeType is checked to ensure that module +and exports are not HTML elements.)

          -
            if (typeof exports !== 'undefined') {
          -    if (typeof module !== 'undefined' && module.exports) {
          +            
            if (typeof exports != 'undefined' && !exports.nodeType) {
          +    if (typeof module != 'undefined' && !module.nodeType && module.exports) {
                 exports = module.exports = _;
               }
               exports._ = _;
          @@ -217,7 +222,7 @@ 

          Baseline setup

          -
            _.VERSION = '1.8.3';
          +
            _.VERSION = '1.9.0';
          @@ -234,96 +239,136 @@

          Baseline setup

          -
            var optimizeCb = function(func, context, argCount) {
          +            
            var optimizeCb = function(func, context, argCount) {
               if (context === void 0) return func;
               switch (argCount == null ? 3 : argCount) {
          -      case 1: return function(value) {
          +      case 1: return function(value) {
                   return func.call(context, value);
          -      };
          -      case 2: return function(value, other) {
          -        return func.call(context, value, other);
          -      };
          -      case 3: return function(value, index, collection) {
          +      };
          + + + + +
        1. +
          + +
          + +
          +

          The 2-argument case is omitted because we’re not using it.

          + +
          + +
                case 3: return function(value, index, collection) {
                   return func.call(context, value, index, collection);
                 };
          -      case 4: return function(accumulator, value, index, collection) {
          +      case 4: return function(accumulator, value, index, collection) {
                   return func.call(context, accumulator, value, index, collection);
                 };
               }
          -    return function() {
          +    return function() {
                 return func.apply(context, arguments);
               };
          -  };
          + }; + + var builtinIteratee;
        2. -
        3. +
        4. - +
          -

          A mostly-internal function to generate callbacks that can be applied -to each element in a collection, returning the desired result — either -identity, an arbitrary callback, a property matcher, or a property accessor.

          +

          An internal function to generate callbacks that can be applied to each +element in a collection, returning the desired result — either identity, +an arbitrary callback, a property matcher, or a property accessor.

          -
            var cb = function(value, context, argCount) {
          +            
            var cb = function(value, context, argCount) {
          +    if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);
               if (value == null) return _.identity;
               if (_.isFunction(value)) return optimizeCb(value, context, argCount);
          -    if (_.isObject(value)) return _.matcher(value);
          +    if (_.isObject(value) && !_.isArray(value)) return _.matcher(value);
               return _.property(value);
          -  };
          -  _.iteratee = function(value, context) {
          +  };
          + +
        5. + + +
        6. +
          + +
          + +
          +

          External wrapper for our callback generator. Users may customize +_.iteratee if they want additional predicate/iteratee shorthand styles. +This abstraction hides the internal-only argCount argument.

          + +
          + +
            _.iteratee = builtinIteratee = function(value, context) {
               return cb(value, context, Infinity);
             };
        7. -
        8. +
        9. - +
          -

          An internal function for creating assigner functions.

          +

          Some functions take a variable number of arguments, or a few expected +arguments at the beginning and then a variable number of values to operate +on. This helper accumulates all remaining arguments past the function’s +argument length (or an explicit startIndex), into an array that becomes +the last argument. Similar to ES6’s “rest parameter”.

          -
            var createAssigner = function(keysFunc, undefinedOnly) {
          -    return function(obj) {
          -      var length = arguments.length;
          -      if (length < 2 || obj == null) return obj;
          -      for (var index = 1; index < length; index++) {
          -        var source = arguments[index],
          -            keys = keysFunc(source),
          -            l = keys.length;
          -        for (var i = 0; i < l; i++) {
          -          var key = keys[i];
          -          if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
          -        }
          +            
            var restArguments = function(func, startIndex) {
          +    startIndex = startIndex == null ? func.length - 1 : +startIndex;
          +    return function() {
          +      var length = Math.max(arguments.length - startIndex, 0),
          +          rest = Array(length),
          +          index = 0;
          +      for (; index < length; index++) {
          +        rest[index] = arguments[index + startIndex];
                 }
          -      return obj;
          +      switch (startIndex) {
          +        case 0: return func.call(this, rest);
          +        case 1: return func.call(this, arguments[0], rest);
          +        case 2: return func.call(this, arguments[0], arguments[1], rest);
          +      }
          +      var args = Array(startIndex + 1);
          +      for (index = 0; index < startIndex; index++) {
          +        args[index] = arguments[index];
          +      }
          +      args[startIndex] = rest;
          +      return func.apply(this, args);
               };
             };
        10. -
        11. +
        12. - +

          An internal function for creating a new object that inherits from another.

          -
            var baseCreate = function(prototype) {
          +            
            var baseCreate = function(prototype) {
               if (!_.isObject(prototype)) return {};
               if (nativeCreate) return nativeCreate(prototype);
               Ctor.prototype = prototype;
          @@ -332,31 +377,40 @@ 

          Baseline setup

          return result; }; - var property = function(key) { - return function(obj) { + var shallowProperty = function(key) { + return function(obj) { return obj == null ? void 0 : obj[key]; }; + }; + + var deepGet = function(obj, path) { + var length = path.length; + for (var i = 0; i < length; i++) { + if (obj == null) return void 0; + obj = obj[path[i]]; + } + return length ? obj : void 0; };
        13. -
        14. +
        15. - +

          Helper for collection methods to determine whether a collection -should be iterated as an array or as an object +should be iterated as an array or as an object. Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094

            var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
          -  var getLength = property('length');
          -  var isArrayLike = function(collection) {
          +  var getLength = shallowProperty('length');
          +  var isArrayLike = function(collection) {
               var length = getLength(collection);
               return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
             };
          @@ -364,11 +418,11 @@

          Baseline setup

        16. -
        17. +
        18. - +

          Collection Functions

          @@ -377,11 +431,11 @@

          Collection Functions

        19. -
        20. +
        21. - +
          @@ -389,11 +443,11 @@

          Collection Functions

        22. -
        23. +
        24. - +

          The cornerstone, an each implementation, aka forEach. Handles raw objects in addition to array-likes. Treats all @@ -401,7 +455,7 @@

          Collection Functions

          -
            _.each = _.forEach = function(obj, iteratee, context) {
          +            
            _.each = _.forEach = function(obj, iteratee, context) {
               iteratee = optimizeCb(iteratee, context);
               var i, length;
               if (isArrayLike(obj)) {
          @@ -420,17 +474,17 @@ 

          Collection Functions

        25. -
        26. +
        27. - +

          Return the results of applying the iteratee to each element.

          -
            _.map = _.collect = function(obj, iteratee, context) {
          +            
            _.map = _.collect = function(obj, iteratee, context) {
               iteratee = cb(iteratee, context);
               var keys = !isArrayLike(obj) && _.keys(obj),
                   length = (keys || obj).length,
          @@ -445,75 +499,61 @@ 

          Collection Functions

        28. -
        29. +
        30. - +

          Create a reducing function iterating left or right.

          -
            function createReduce(dir) {
          +
            var createReduce = function(dir) {
        31. -
        32. +
        33. - +
          -

          Optimized iterator function as using arguments.length -in the main function will deoptimize the, see #1991.

          +

          Wrap code that reassigns argument variables in a separate function than +the one that accesses arguments.length to avoid a perf hit. (#1991)

          -
              function iterator(obj, iteratee, memo, keys, index, length) {
          +            
              var reducer = function(obj, iteratee, memo, initial) {
          +      var keys = !isArrayLike(obj) && _.keys(obj),
          +          length = (keys || obj).length,
          +          index = dir > 0 ? 0 : length - 1;
          +      if (!initial) {
          +        memo = obj[keys ? keys[index] : index];
          +        index += dir;
          +      }
                 for (; index >= 0 && index < length; index += dir) {
                   var currentKey = keys ? keys[index] : index;
                   memo = iteratee(memo, obj[currentKey], currentKey, obj);
                 }
                 return memo;
          -    }
          -
          -    return function(obj, iteratee, memo, context) {
          -      iteratee = optimizeCb(iteratee, context, 4);
          -      var keys = !isArrayLike(obj) && _.keys(obj),
          -          length = (keys || obj).length,
          -          index = dir > 0 ? 0 : length - 1;
          - -
        34. - - -
        35. -
          - -
          - -
          -

          Determine the initial value if none is provided.

          + }; -
          - -
                if (arguments.length < 3) {
          -        memo = obj[keys ? keys[index] : index];
          -        index += dir;
          -      }
          -      return iterator(obj, iteratee, memo, keys, index, length);
          +    return function(obj, iteratee, memo, context) {
          +      var initial = arguments.length >= 3;
          +      return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
               };
          -  }
          + };
        36. -
        37. +
        38. - +

          Reduce builds up a single result from a list of values, aka inject, or foldl.

          @@ -525,59 +565,55 @@

          Collection Functions

        39. -
        40. +
        41. - +

          The right-associative version of reduce, also known as foldr.

          -
            _.reduceRight = _.foldr = createReduce(-1);
          +
            _.reduceRight = _.foldr = createReduce(-1);
        42. -
        43. +
        44. - +

          Return the first value which passes a truth test. Aliased as detect.

          -
            _.find = _.detect = function(obj, predicate, context) {
          -    var key;
          -    if (isArrayLike(obj)) {
          -      key = _.findIndex(obj, predicate, context);
          -    } else {
          -      key = _.findKey(obj, predicate, context);
          -    }
          -    if (key !== void 0 && key !== -1) return obj[key];
          +            
            _.find = _.detect = function(obj, predicate, context) {
          +    var keyFinder = isArrayLike(obj) ? _.findIndex : _.findKey;
          +    var key = keyFinder(obj, predicate, context);
          +    if (key !== void 0 && key !== -1) return obj[key];
             };
        45. -
        46. +
        47. - +

          Return all the elements that pass a truth test. Aliased as select.

          -
            _.filter = _.select = function(obj, predicate, context) {
          +            
            _.filter = _.select = function(obj, predicate, context) {
               var results = [];
               predicate = cb(predicate, context);
          -    _.each(obj, function(value, index, list) {
          +    _.each(obj, function(value, index, list) {
                 if (predicate(value, index, list)) results.push(value);
               });
               return results;
          @@ -586,35 +622,35 @@ 

          Collection Functions

        48. -
        49. +
        50. - +

          Return all the elements for which a truth test fails.

          -
            _.reject = function(obj, predicate, context) {
          +            
            _.reject = function(obj, predicate, context) {
               return _.filter(obj, _.negate(cb(predicate)), context);
             };
        51. -
        52. +
        53. - +

          Determine whether all of the elements match a truth test. Aliased as all.

          -
            _.every = _.all = function(obj, predicate, context) {
          +            
            _.every = _.all = function(obj, predicate, context) {
               predicate = cb(predicate, context);
               var keys = !isArrayLike(obj) && _.keys(obj),
                   length = (keys || obj).length;
          @@ -628,18 +664,18 @@ 

          Collection Functions

        54. -
        55. +
        56. - +

          Determine if at least one element in the object matches a truth test. Aliased as any.

          -
            _.some = _.any = function(obj, predicate, context) {
          +            
            _.some = _.any = function(obj, predicate, context) {
               predicate = cb(predicate, context);
               var keys = !isArrayLike(obj) && _.keys(obj),
                   length = (keys || obj).length;
          @@ -653,18 +689,18 @@ 

          Collection Functions

        57. -
        58. +
        59. - +

          Determine if the array or object contains a given item (using ===). Aliased as includes and include.

          -
            _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
          +            
            _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
               if (!isArrayLike(obj)) obj = _.values(obj);
               if (typeof fromIndex != 'number' || guard) fromIndex = 0;
               return _.indexOf(obj, item, fromIndex) >= 0;
          @@ -673,108 +709,120 @@ 

          Collection Functions

        60. -
        61. +
        62. - +

          Invoke a method (with arguments) on every item in a collection.

          -
            _.invoke = function(obj, method) {
          -    var args = slice.call(arguments, 2);
          -    var isFunc = _.isFunction(method);
          -    return _.map(obj, function(value) {
          -      var func = isFunc ? method : value[method];
          -      return func == null ? func : func.apply(value, args);
          +            
            _.invoke = restArguments(function(obj, path, args) {
          +    var contextPath, func;
          +    if (_.isFunction(path)) {
          +      func = path;
          +    } else if (_.isArray(path)) {
          +      contextPath = path.slice(0, -1);
          +      path = path[path.length - 1];
          +    }
          +    return _.map(obj, function(context) {
          +      var method = func;
          +      if (!method) {
          +        if (contextPath && contextPath.length) {
          +          context = deepGet(context, contextPath);
          +        }
          +        if (context == null) return void 0;
          +        method = context[path];
          +      }
          +      return method == null ? method : method.apply(context, args);
               });
          -  };
          + });
        63. -
        64. +
        65. - +

          Convenience version of a common use case of map: fetching a property.

          -
            _.pluck = function(obj, key) {
          +            
            _.pluck = function(obj, key) {
               return _.map(obj, _.property(key));
             };
        66. -
        67. +
        68. - +

          Convenience version of a common use case of filter: selecting only objects containing specific key:value pairs.

          -
            _.where = function(obj, attrs) {
          +            
            _.where = function(obj, attrs) {
               return _.filter(obj, _.matcher(attrs));
             };
        69. -
        70. +
        71. - +

          Convenience version of a common use case of find: getting the first object containing specific key:value pairs.

          -
            _.findWhere = function(obj, attrs) {
          +            
            _.findWhere = function(obj, attrs) {
               return _.find(obj, _.matcher(attrs));
             };
        72. -
        73. +
        74. - +

          Return the maximum element (or element-based computation).

          -
            _.max = function(obj, iteratee, context) {
          +            
            _.max = function(obj, iteratee, context) {
               var result = -Infinity, lastComputed = -Infinity,
                   value, computed;
          -    if (iteratee == null && obj != null) {
          +    if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) {
                 obj = isArrayLike(obj) ? obj : _.values(obj);
                 for (var i = 0, length = obj.length; i < length; i++) {
                   value = obj[i];
          -        if (value > result) {
          +        if (value != null && value > result) {
                     result = value;
                   }
                 }
               } else {
                 iteratee = cb(iteratee, context);
          -      _.each(obj, function(value, index, list) {
          -        computed = iteratee(value, index, list);
          +      _.each(obj, function(v, index, list) {
          +        computed = iteratee(v, index, list);
                   if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
          -          result = value;
          +          result = v;
                     lastComputed = computed;
                   }
                 });
          @@ -785,33 +833,33 @@ 

          Collection Functions

        75. -
        76. +
        77. - +

          Return the minimum element (or element-based computation).

          -
            _.min = function(obj, iteratee, context) {
          +            
            _.min = function(obj, iteratee, context) {
               var result = Infinity, lastComputed = Infinity,
                   value, computed;
          -    if (iteratee == null && obj != null) {
          +    if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) {
                 obj = isArrayLike(obj) ? obj : _.values(obj);
                 for (var i = 0, length = obj.length; i < length; i++) {
                   value = obj[i];
          -        if (value < result) {
          +        if (value != null && value < result) {
                     result = value;
                   }
                 }
               } else {
                 iteratee = cb(iteratee, context);
          -      _.each(obj, function(value, index, list) {
          -        computed = iteratee(value, index, list);
          +      _.each(obj, function(v, index, list) {
          +        computed = iteratee(v, index, list);
                   if (computed < lastComputed || computed === Infinity && result === Infinity) {
          -          result = value;
          +          result = v;
                     lastComputed = computed;
                   }
                 });
          @@ -822,79 +870,82 @@ 

          Collection Functions

        78. -
        79. +
        80. - +
          -

          Shuffle a collection, using the modern version of the -Fisher-Yates shuffle.

          +

          Shuffle a collection.

          -
            _.shuffle = function(obj) {
          -    var set = isArrayLike(obj) ? obj : _.values(obj);
          -    var length = set.length;
          -    var shuffled = Array(length);
          -    for (var index = 0, rand; index < length; index++) {
          -      rand = _.random(0, index);
          -      if (rand !== index) shuffled[index] = shuffled[rand];
          -      shuffled[rand] = set[index];
          -    }
          -    return shuffled;
          +            
            _.shuffle = function(obj) {
          +    return _.sample(obj, Infinity);
             };
        81. -
        82. +
        83. - +
          -

          Sample n random values from a collection. +

          Sample n random values from a collection using the modern version of the +Fisher-Yates shuffle. If n is not specified, returns a single random element. The internal guard argument allows it to work with map.

          -
            _.sample = function(obj, n, guard) {
          +            
            _.sample = function(obj, n, guard) {
               if (n == null || guard) {
                 if (!isArrayLike(obj)) obj = _.values(obj);
                 return obj[_.random(obj.length - 1)];
               }
          -    return _.shuffle(obj).slice(0, Math.max(0, n));
          +    var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj);
          +    var length = getLength(sample);
          +    n = Math.max(Math.min(n, length), 0);
          +    var last = length - 1;
          +    for (var index = 0; index < n; index++) {
          +      var rand = _.random(index, last);
          +      var temp = sample[index];
          +      sample[index] = sample[rand];
          +      sample[rand] = temp;
          +    }
          +    return sample.slice(0, n);
             };
        84. -
        85. +
        86. - +

          Sort the object’s values by a criterion produced by an iteratee.

          -
            _.sortBy = function(obj, iteratee, context) {
          +            
            _.sortBy = function(obj, iteratee, context) {
          +    var index = 0;
               iteratee = cb(iteratee, context);
          -    return _.pluck(_.map(obj, function(value, index, list) {
          +    return _.pluck(_.map(obj, function(value, key, list) {
                 return {
          -        value: value,
          -        index: index,
          -        criteria: iteratee(value, index, list)
          +        value: value,
          +        index: index++,
          +        criteria: iteratee(value, key, list)
                 };
          -    }).sort(function(left, right) {
          +    }).sort(function(left, right) {
                 var a = left.criteria;
                 var b = right.criteria;
                 if (a !== b) {
                   if (a > b || a === void 0) return 1;
          -        if (a < b || b === void 0) return -1;
          +        if (a < b || b === void 0) return -1;
                 }
                 return left.index - right.index;
               }), 'value');
          @@ -903,21 +954,21 @@ 

          Collection Functions

        87. -
        88. +
        89. - +

          An internal function used for aggregate “group by” operations.

          -
            var group = function(behavior) {
          -    return function(obj, iteratee, context) {
          -      var result = {};
          +            
            var group = function(behavior, partition) {
          +    return function(obj, iteratee, context) {
          +      var result = partition ? [[], []] : {};
                 iteratee = cb(iteratee, context);
          -      _.each(obj, function(value, index) {
          +      _.each(obj, function(value, index) {
                   var key = iteratee(value, index, obj);
                   behavior(result, value, key);
                 });
          @@ -928,47 +979,47 @@ 

          Collection Functions

        90. -
        91. +
        92. - +

          Groups the object’s values by a criterion. Pass either a string attribute to group by, or a function that returns the criterion.

          -
            _.groupBy = group(function(result, value, key) {
          +            
            _.groupBy = group(function(result, value, key) {
               if (_.has(result, key)) result[key].push(value); else result[key] = [value];
             });
        93. -
        94. +
        95. - +

          Indexes the object’s values by a criterion, similar to groupBy, but for when you know that your index values will be unique.

          -
            _.indexBy = group(function(result, value, key) {
          +            
            _.indexBy = group(function(result, value, key) {
               result[key] = value;
             });
        96. -
        97. +
        98. - +

          Counts instances of an object that group by a certain criterion. Pass either a string attribute to count by, or a function that returns the @@ -976,97 +1027,93 @@

          Collection Functions

          -
            _.countBy = group(function(result, value, key) {
          +            
            _.countBy = group(function(result, value, key) {
               if (_.has(result, key)) result[key]++; else result[key] = 1;
          -  });
          + }); + + var reStrSymbol = /[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;
        99. -
        100. +
        101. - +

          Safely create a real, live array from anything iterable.

          -
            _.toArray = function(obj) {
          +            
            _.toArray = function(obj) {
               if (!obj) return [];
               if (_.isArray(obj)) return slice.call(obj);
          -    if (isArrayLike(obj)) return _.map(obj, _.identity);
          -    return _.values(obj);
          -  };
          + if (_.isString(obj)) {
        102. -
        103. +
        104. - +
          -

          Return the number of elements in an object.

          +

          Keep surrogate pair characters together

          -
            _.size = function(obj) {
          -    if (obj == null) return 0;
          -    return isArrayLike(obj) ? obj.length : _.keys(obj).length;
          +            
                return obj.match(reStrSymbol);
          +    }
          +    if (isArrayLike(obj)) return _.map(obj, _.identity);
          +    return _.values(obj);
             };
        105. -
        106. +
        107. - +
          -

          Split a collection into two arrays: one whose elements all satisfy the given -predicate, and one whose elements all do not satisfy the predicate.

          +

          Return the number of elements in an object.

          -
            _.partition = function(obj, predicate, context) {
          -    predicate = cb(predicate, context);
          -    var pass = [], fail = [];
          -    _.each(obj, function(value, key, obj) {
          -      (predicate(value, key, obj) ? pass : fail).push(value);
          -    });
          -    return [pass, fail];
          +            
            _.size = function(obj) {
          +    if (obj == null) return 0;
          +    return isArrayLike(obj) ? obj.length : _.keys(obj).length;
             };
        108. - - -
        109. + + +
        110. - +
          -

          Trim out all falsy values from an object.

          +

          Split a collection into two arrays: one whose elements all satisfy the given +predicate, and one whose elements all do not satisfy the predicate.

          -
            _.compact = function(obj) {
          -    return _.filter(obj, _.identity);
          -  };
          +
            _.partition = group(function(result, value, pass) {
          +    result[pass ? 0 : 1].push(value);
          +  }, true);
        111. - -
        112. +
        113. - +

          Array Functions

          @@ -1075,11 +1122,11 @@

          Array Functions

        114. -
        115. +
        116. - +
          @@ -1087,11 +1134,11 @@

          Array Functions

        117. -
        118. +
        119. - +

          Get the first element of an array. Passing n will return the first N values in the array. Aliased as head and take. The guard check @@ -1099,8 +1146,8 @@

          Array Functions

          -
            _.first = _.head = _.take = function(array, n, guard) {
          -    if (array == null) return void 0;
          +            
            _.first = _.head = _.take = function(array, n, guard) {
          +    if (array == null || array.length < 1) return void 0;
               if (n == null || guard) return array[0];
               return _.initial(array, array.length - n);
             };
          @@ -1108,11 +1155,11 @@

          Array Functions

        120. -
        121. +
        122. - +

          Returns everything but the last entry of the array. Especially useful on the arguments object. Passing n will return all the values in @@ -1120,26 +1167,26 @@

          Array Functions

          -
            _.initial = function(array, n, guard) {
          +            
            _.initial = function(array, n, guard) {
               return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
             };
        123. -
        124. +
        125. - +

          Get the last element of an array. Passing n will return the last N values in the array.

          -
            _.last = function(array, n, guard) {
          -    if (array == null) return void 0;
          +            
            _.last = function(array, n, guard) {
          +    if (array == null || array.length < 1) return void 0;
               if (n == null || guard) return array[array.length - 1];
               return _.rest(array, Math.max(0, array.length - n));
             };
          @@ -1147,11 +1194,11 @@

          Array Functions

        126. -
        127. +
        128. - +

          Returns everything but the first entry of the array. Aliased as tail and drop. Especially useful on the arguments object. Passing an n will return @@ -1159,47 +1206,66 @@

          Array Functions

          -
            _.rest = _.tail = _.drop = function(array, n, guard) {
          +            
            _.rest = _.tail = _.drop = function(array, n, guard) {
               return slice.call(array, n == null || guard ? 1 : n);
             };
          -
        129. + -
        130. +
        131. - + +
          +

          Trim out all falsy values from an array.

          + +
          + +
            _.compact = function(array) {
          +    return _.filter(array, Boolean);
          +  };
          + +
        132. + + +
        133. +
          + +
          +

          Internal implementation of a recursive flatten function.

          -
            var flatten = function(input, shallow, strict, startIndex) {
          -    var output = [], idx = 0;
          -    for (var i = startIndex || 0, length = getLength(input); i < length; i++) {
          +            
            var flatten = function(input, shallow, strict, output) {
          +    output = output || [];
          +    var idx = output.length;
          +    for (var i = 0, length = getLength(input); i < length; i++) {
                 var value = input[i];
                 if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
        134. -
        135. +
        136. - +
          -

          flatten current level of array or arguments object

          +

          Flatten current level of array or arguments object.

          -
                  if (!shallow) value = flatten(value, shallow, strict);
          -        var j = 0, len = value.length;
          -        output.length += len;
          -        while (j < len) {
          -          output[idx++] = value[j++];
          +            
                  if (shallow) {
          +          var j = 0, len = value.length;
          +          while (j < len) output[idx++] = value[j++];
          +        } else {
          +          flatten(value, shallow, strict, output);
          +          idx = output.length;
                   }
                 } else if (!strict) {
                   output[idx++] = value;
          @@ -1211,53 +1277,56 @@ 

          Array Functions

        137. -
        138. +
        139. - +

          Flatten out an array, either recursively (by default), or just one level.

          -
            _.flatten = function(array, shallow) {
          +            
            _.flatten = function(array, shallow) {
               return flatten(array, shallow, false);
             };
        140. -
        141. +
        142. - +

          Return a version of the array that does not contain the specified value(s).

          -
            _.without = function(array) {
          -    return _.difference(array, slice.call(arguments, 1));
          -  };
          +
            _.without = restArguments(function(array, otherArrays) {
          +    return _.difference(array, otherArrays);
          +  });
        143. -
        144. +
        145. - +

          Produce a duplicate-free version of the array. If the array has already been sorted, you have the option of using a faster algorithm. +The faster algorithm will not work with an iteratee if the iteratee +is not a one-to-one function, so providing an iteratee will disable +the faster algorithm. Aliased as unique.

          -
            _.uniq = _.unique = function(array, isSorted, iteratee, context) {
          +            
            _.uniq = _.unique = function(array, isSorted, iteratee, context) {
               if (!_.isBoolean(isSorted)) {
                 context = iteratee;
                 iteratee = isSorted;
          @@ -1269,7 +1338,7 @@ 

          Array Functions

          for (var i = 0, length = getLength(array); i < length; i++) { var value = array[i], computed = iteratee ? iteratee(value, i, array) : value; - if (isSorted) { + if (isSorted && !iteratee) { if (!i || seen !== computed) result.push(value); seen = computed; } else if (iteratee) { @@ -1287,42 +1356,43 @@

          Array Functions

        146. -
        147. +
        148. - +

          Produce an array that contains the union: each distinct element from all of the passed-in arrays.

          -
            _.union = function() {
          -    return _.uniq(flatten(arguments, true, true));
          -  };
          +
            _.union = restArguments(function(arrays) {
          +    return _.uniq(flatten(arrays, true, true));
          +  });
        149. -
        150. +
        151. - +

          Produce an array that contains every item shared between all the passed-in arrays.

          -
            _.intersection = function(array) {
          +            
            _.intersection = function(array) {
               var result = [];
               var argsLength = arguments.length;
               for (var i = 0, length = getLength(array); i < length; i++) {
                 var item = array[i];
                 if (_.contains(result, item)) continue;
          -      for (var j = 1; j < argsLength; j++) {
          +      var j;
          +      for (j = 1; j < argsLength; j++) {
                   if (!_.contains(arguments[j], item)) break;
                 }
                 if (j === argsLength) result.push(item);
          @@ -1333,82 +1403,80 @@ 

          Array Functions

        152. -
        153. +
        154. - +

          Take the difference between one array and a number of other arrays. Only the elements present in just the first array will remain.

          -
            _.difference = function(array) {
          -    var rest = flatten(arguments, true, true, 1);
          -    return _.filter(array, function(value){
          +            
            _.difference = restArguments(function(array, rest) {
          +    rest = flatten(rest, true, true);
          +    return _.filter(array, function(value){
                 return !_.contains(rest, value);
               });
          -  };
          + });
        155. -
        156. +
        157. - +
          -

          Zip together multiple lists into a single array — elements that share -an index go together.

          +

          Complement of _.zip. Unzip accepts an array of arrays and groups +each array’s elements on shared indices.

          -
            _.zip = function() {
          -    return _.unzip(arguments);
          +            
            _.unzip = function(array) {
          +    var length = array && _.max(array, getLength).length || 0;
          +    var result = Array(length);
          +
          +    for (var index = 0; index < length; index++) {
          +      result[index] = _.pluck(array, index);
          +    }
          +    return result;
             };
        158. -
        159. +
        160. - +
          -

          Complement of _.zip. Unzip accepts an array of arrays and groups -each array’s elements on shared indices

          +

          Zip together multiple lists into a single array – elements that share +an index go together.

          -
            _.unzip = function(array) {
          -    var length = array && _.max(array, getLength).length || 0;
          -    var result = Array(length);
          -
          -    for (var index = 0; index < length; index++) {
          -      result[index] = _.pluck(array, index);
          -    }
          -    return result;
          -  };
          +
            _.zip = restArguments(_.unzip);
        161. -
        162. +
        163. - +

          Converts lists into objects. Pass either a single array of [key, value] -pairs, or two parallel arrays of the same length — one of keys, and one of -the corresponding values.

          +pairs, or two parallel arrays of the same length – one of keys, and one of +the corresponding values. Passing by pairs is the reverse of _.pairs.

          -
            _.object = function(list, values) {
          +            
            _.object = function(list, values) {
               var result = {};
               for (var i = 0, length = getLength(list); i < length; i++) {
                 if (values) {
          @@ -1423,59 +1491,59 @@ 

          Array Functions

        164. -
        165. +
        166. - +
          -

          Generator function to create the findIndex and findLastIndex functions

          +

          Generator function to create the findIndex and findLastIndex functions.

          -
            function createPredicateIndexFinder(dir) {
          -    return function(array, predicate, context) {
          +            
            var createPredicateIndexFinder = function(dir) {
          +    return function(array, predicate, context) {
                 predicate = cb(predicate, context);
                 var length = getLength(array);
                 var index = dir > 0 ? 0 : length - 1;
                 for (; index >= 0 && index < length; index += dir) {
                   if (predicate(array[index], index, array)) return index;
                 }
          -      return -1;
          +      return -1;
               };
          -  }
          + };
        167. -
        168. +
        169. - +
          -

          Returns the first index on an array-like that passes a predicate test

          +

          Returns the first index on an array-like that passes a predicate test.

            _.findIndex = createPredicateIndexFinder(1);
          -  _.findLastIndex = createPredicateIndexFinder(-1);
          + _.findLastIndex = createPredicateIndexFinder(-1);
        170. -
        171. +
        172. - +

          Use a comparator function to figure out the smallest index at which an object should be inserted so as to maintain order. Uses binary search.

          -
            _.sortedIndex = function(array, obj, iteratee, context) {
          +            
            _.sortedIndex = function(array, obj, iteratee, context) {
               iteratee = cb(iteratee, context, 1);
               var value = iteratee(obj);
               var low = 0, high = getLength(array);
          @@ -1489,48 +1557,48 @@ 

          Array Functions

        173. -
        174. +
        175. - +
          -

          Generator function to create the indexOf and lastIndexOf functions

          +

          Generator function to create the indexOf and lastIndexOf functions.

          -
            function createIndexFinder(dir, predicateFind, sortedIndex) {
          -    return function(array, item, idx) {
          +            
            var createIndexFinder = function(dir, predicateFind, sortedIndex) {
          +    return function(array, item, idx) {
                 var i = 0, length = getLength(array);
                 if (typeof idx == 'number') {
                   if (dir > 0) {
          -            i = idx >= 0 ? idx : Math.max(idx + length, i);
          +          i = idx >= 0 ? idx : Math.max(idx + length, i);
                   } else {
          -            length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
          +          length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
                   }
                 } else if (sortedIndex && idx && length) {
                   idx = sortedIndex(array, item);
          -        return array[idx] === item ? idx : -1;
          +        return array[idx] === item ? idx : -1;
                 }
                 if (item !== item) {
                   idx = predicateFind(slice.call(array, i, length), _.isNaN);
          -        return idx >= 0 ? idx + i : -1;
          +        return idx >= 0 ? idx + i : -1;
                 }
                 for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
                   if (array[idx] === item) return idx;
                 }
          -      return -1;
          +      return -1;
               };
          -  }
          + };
        176. -
        177. +
        178. - +

          Return the position of the first occurrence of an item in an array, or -1 if the item is not included in the array. @@ -1540,16 +1608,16 @@

          Array Functions

            _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
          -  _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
          + _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
        179. -
        180. +
        181. - +

          Generate an integer Array containing an arithmetic progression. A port of the native Python range() function. See @@ -1557,12 +1625,14 @@

          Array Functions

          -
            _.range = function(start, stop, step) {
          +            
            _.range = function(start, stop, step) {
               if (stop == null) {
                 stop = start || 0;
                 start = 0;
               }
          -    step = step || 1;
          +    if (!step) {
          +      step = stop < start ? -1 : 1;
          +    }
           
               var length = Math.max(Math.ceil((stop - start) / step), 0);
               var range = Array(length);
          @@ -1577,11 +1647,35 @@ 

          Array Functions

        182. -
        183. +
        184. - + +
          +

          Chunk a single array into multiple arrays, each containing count or fewer +items.

          + +
          + +
            _.chunk = function(array, count) {
          +    if (count == null || count < 1) return [];
          +    var result = [];
          +    var i = 0, length = array.length;
          +    while (i < length) {
          +      result.push(slice.call(array, i, i += count));
          +    }
          +    return result;
          +  };
          + +
        185. + + +
        186. +
          + +
          +

          Function (ahem) Functions

          @@ -1590,11 +1684,11 @@

          Function (ahem) Functions

        187. -
        188. +
        189. - +
          @@ -1602,18 +1696,18 @@

          Function (ahem) Functions

        190. -
        191. +
        192. - +

          Determines whether to execute a function as a constructor -or a normal function with the provided arguments

          +or a normal function with the provided arguments.

          -
            var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
          +            
            var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
               if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
               var self = baseCreate(sourceFunc.prototype);
               var result = sourceFunc.apply(self, args);
          @@ -1624,11 +1718,11 @@ 

          Function (ahem) Functions

        193. -
        194. +
        195. - +

          Create a function bound to a given object (assigning this, and arguments, optionally). Delegates to ECMAScript 5‘s native Function.bind if @@ -1636,53 +1730,54 @@

          Function (ahem) Functions

          -
            _.bind = function(func, context) {
          -    if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
          +            
            _.bind = restArguments(function(func, context, args) {
               if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
          -    var args = slice.call(arguments, 2);
          -    var bound = function() {
          -      return executeBound(func, bound, context, this, args.concat(slice.call(arguments)));
          -    };
          +    var bound = restArguments(function(callArgs) {
          +      return executeBound(func, bound, context, this, args.concat(callArgs));
          +    });
               return bound;
          -  };
          + });
        196. -
        197. +
        198. - +

          Partially apply a function by creating a version that has had some of its arguments pre-filled, without changing its dynamic this context. _ acts -as a placeholder, allowing any combination of arguments to be pre-filled.

          +as a placeholder by default, allowing any combination of arguments to be +pre-filled. Set _.partial.placeholder for a custom placeholder argument.

          -
            _.partial = function(func) {
          -    var boundArgs = slice.call(arguments, 1);
          -    var bound = function() {
          +            
            _.partial = restArguments(function(func, boundArgs) {
          +    var placeholder = _.partial.placeholder;
          +    var bound = function() {
                 var position = 0, length = boundArgs.length;
                 var args = Array(length);
                 for (var i = 0; i < length; i++) {
          -        args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i];
          +        args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i];
                 }
                 while (position < arguments.length) args.push(arguments[position++]);
                 return executeBound(func, bound, this, this, args);
               };
               return bound;
          -  };
          + }); + + _.partial.placeholder = _;
        199. -
        200. +
        201. - +

          Bind a number of an object’s methods to that object. Remaining arguments are the method names to be bound. Useful for ensuring that all callbacks @@ -1690,31 +1785,31 @@

          Function (ahem) Functions

          -
            _.bindAll = function(obj) {
          -    var i, length = arguments.length, key;
          -    if (length <= 1) throw new Error('bindAll must be passed function names');
          -    for (i = 1; i < length; i++) {
          -      key = arguments[i];
          +            
            _.bindAll = restArguments(function(obj, keys) {
          +    keys = flatten(keys, false, false);
          +    var index = keys.length;
          +    if (index < 1) throw new Error('bindAll must be passed function names');
          +    while (index--) {
          +      var key = keys[index];
                 obj[key] = _.bind(obj[key], obj);
               }
          -    return obj;
          -  };
          + });
        202. -
        203. +
        204. - +

          Memoize an expensive function by storing its results.

          -
            _.memoize = function(func, hasher) {
          -    var memoize = function(key) {
          +            
            _.memoize = function(func, hasher) {
          +    var memoize = function(key) {
                 var cache = memoize.cache;
                 var address = '' + (hasher ? hasher.apply(this, arguments) : key);
                 if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
          @@ -1727,32 +1822,31 @@ 

          Function (ahem) Functions

        205. -
        206. +
        207. - +

          Delays a function for the given number of milliseconds, and then calls it with the arguments supplied.

          -
            _.delay = function(func, wait) {
          -    var args = slice.call(arguments, 2);
          -    return setTimeout(function(){
          +            
            _.delay = restArguments(function(func, wait, args) {
          +    return setTimeout(function() {
                 return func.apply(null, args);
               }, wait);
          -  };
          + });
        208. -
        209. +
        210. - +

          Defers a function, scheduling it to run after the current call stack has cleared.

          @@ -1764,11 +1858,11 @@

          Function (ahem) Functions

        211. -
        212. +
        213. - +

          Returns a function, that, when invoked, will only be triggered at most once during a given window of time. Normally, the throttled function will run @@ -1778,18 +1872,19 @@

          Function (ahem) Functions

          -
            _.throttle = function(func, wait, options) {
          -    var context, args, result;
          -    var timeout = null;
          +            
            _.throttle = function(func, wait, options) {
          +    var timeout, context, args, result;
               var previous = 0;
               if (!options) options = {};
          -    var later = function() {
          +
          +    var later = function() {
                 previous = options.leading === false ? 0 : _.now();
                 timeout = null;
                 result = func.apply(context, args);
                 if (!timeout) context = args = null;
               };
          -    return function() {
          +
          +    var throttled = function() {
                 var now = _.now();
                 if (!previous && options.leading === false) previous = now;
                 var remaining = wait - (now - previous);
          @@ -1808,16 +1903,24 @@ 

          Function (ahem) Functions

          } return result; }; + + throttled.cancel = function() { + clearTimeout(timeout); + previous = 0; + timeout = context = args = null; + }; + + return throttled; };
        214. -
        215. +
        216. - +

          Returns a function, that, as long as it continues to be invoked, will not be triggered. The function will be called after it stops being called for @@ -1826,46 +1929,43 @@

          Function (ahem) Functions

          -
            _.debounce = function(func, wait, immediate) {
          -    var timeout, args, context, timestamp, result;
          -
          -    var later = function() {
          -      var last = _.now() - timestamp;
          +            
            _.debounce = function(func, wait, immediate) {
          +    var timeout, result;
           
          -      if (last < wait && last >= 0) {
          -        timeout = setTimeout(later, wait - last);
          -      } else {
          -        timeout = null;
          -        if (!immediate) {
          -          result = func.apply(context, args);
          -          if (!timeout) context = args = null;
          -        }
          -      }
          -    };
          -
          -    return function() {
          -      context = this;
          -      args = arguments;
          -      timestamp = _.now();
          -      var callNow = immediate && !timeout;
          -      if (!timeout) timeout = setTimeout(later, wait);
          -      if (callNow) {
          -        result = func.apply(context, args);
          -        context = args = null;
          +    var later = function(context, args) {
          +      timeout = null;
          +      if (args) result = func.apply(context, args);
          +    };
          +
          +    var debounced = restArguments(function(args) {
          +      if (timeout) clearTimeout(timeout);
          +      if (immediate) {
          +        var callNow = !timeout;
          +        timeout = setTimeout(later, wait);
          +        if (callNow) result = func.apply(this, args);
          +      } else {
          +        timeout = _.delay(later, wait, this, args);
                 }
           
                 return result;
          +    });
          +
          +    debounced.cancel = function() {
          +      clearTimeout(timeout);
          +      timeout = null;
               };
          +
          +    return debounced;
             };
        217. -
        218. +
        219. - +

          Returns the first function passed as an argument to the second, allowing you to adjust arguments, run code before and after, and @@ -1873,25 +1973,25 @@

          Function (ahem) Functions

          -
            _.wrap = function(func, wrapper) {
          +            
            _.wrap = function(func, wrapper) {
               return _.partial(wrapper, func);
             };
        220. -
        221. +
        222. - +

          Returns a negated version of the passed-in predicate.

          -
            _.negate = function(predicate) {
          -    return function() {
          +            
            _.negate = function(predicate) {
          +    return function() {
                 return !predicate.apply(this, arguments);
               };
             };
          @@ -1899,21 +1999,21 @@

          Function (ahem) Functions

        223. -
        224. +
        225. - +

          Returns a function that is the composition of a list of functions, each consuming the return value of the function that follows.

          -
            _.compose = function() {
          +            
            _.compose = function() {
               var args = arguments;
               var start = args.length - 1;
          -    return function() {
          +    return function() {
                 var i = start;
                 var result = args[start].apply(this, arguments);
                 while (i--) result = args[i].call(this, result);
          @@ -1924,18 +2024,18 @@ 

          Function (ahem) Functions

        226. -
        227. +
        228. - +

          Returns a function that will only be executed on and after the Nth call.

          -
            _.after = function(times, func) {
          -    return function() {
          +            
            _.after = function(times, func) {
          +    return function() {
                 if (--times < 1) {
                   return func.apply(this, arguments);
                 }
          @@ -1945,19 +2045,19 @@ 

          Function (ahem) Functions

        229. -
        230. +
        231. - +

          Returns a function that will only be executed up to (but not including) the Nth call.

          -
            _.before = function(times, func) {
          +            
            _.before = function(times, func) {
               var memo;
          -    return function() {
          +    return function() {
                 if (--times > 0) {
                   memo = func.apply(this, arguments);
                 }
          @@ -1969,27 +2069,29 @@ 

          Function (ahem) Functions

        232. -
        233. +
        234. - +

          Returns a function that will be executed at most one time, no matter how often you call it. Useful for lazy initialization.

          -
            _.once = _.partial(_.before, 2);
          +
            _.once = _.partial(_.before, 2);
          +
          +  _.restArguments = restArguments;
        235. -
        236. +
        237. - +

          Object Functions

          @@ -1998,11 +2100,11 @@

          Object Functions

        238. -
        239. +
        240. - +
          @@ -2010,33 +2112,33 @@

          Object Functions

        241. -
        242. +
        243. - +

          Keys in IE < 9 that won’t be iterated by for key in ... and thus missed.

          -
            var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
          +            
            var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
             var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
          -                      'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
          +    'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
           
          -  function collectNonEnumProps(obj, keys) {
          +  var collectNonEnumProps = function(obj, keys) {
               var nonEnumIdx = nonEnumerableProps.length;
          -    var constructor = obj.constructor;
          -    var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto;
          + var constructor = obj.constructor; + var proto = _.isFunction(constructor) && constructor.prototype || ObjProto;
        244. -
        245. +
        246. - +

          Constructor is a special case.

          @@ -2051,23 +2153,23 @@

          Object Functions

          keys.push(prop); } } - }
        247. + };
          -
        248. +
        249. - +

          Retrieve the names of an object’s own properties. -Delegates to ECMAScript 5‘s native Object.keys

          +Delegates to ECMAScript 5‘s native Object.keys.

          -
            _.keys = function(obj) {
          +            
            _.keys = function(obj) {
               if (!_.isObject(obj)) return [];
               if (nativeKeys) return nativeKeys(obj);
               var keys = [];
          @@ -2076,11 +2178,11 @@ 

          Object Functions

        250. -
        251. +
        252. - +

          Ahem, IE < 9.

          @@ -2093,17 +2195,17 @@

          Object Functions

        253. -
        254. +
        255. - +

          Retrieve all the property names of an object.

          -
            _.allKeys = function(obj) {
          +            
            _.allKeys = function(obj) {
               if (!_.isObject(obj)) return [];
               var keys = [];
               for (var key in obj) keys.push(key);
          @@ -2111,11 +2213,11 @@

          Object Functions

        256. -
        257. +
        258. - +

          Ahem, IE < 9.

          @@ -2128,17 +2230,17 @@

          Object Functions

        259. -
        260. +
        261. - +

          Retrieve the values of an object’s properties.

          -
            _.values = function(obj) {
          +            
            _.values = function(obj) {
               var keys = _.keys(obj);
               var length = keys.length;
               var values = Array(length);
          @@ -2151,44 +2253,44 @@ 

          Object Functions

        262. -
        263. +
        264. - +
          -

          Returns the results of applying the iteratee to each element of the object -In contrast to _.map it returns an object

          +

          Returns the results of applying the iteratee to each element of the object. +In contrast to _.map it returns an object.

          -
            _.mapObject = function(obj, iteratee, context) {
          +            
            _.mapObject = function(obj, iteratee, context) {
               iteratee = cb(iteratee, context);
          -    var keys =  _.keys(obj),
          -          length = keys.length,
          -          results = {},
          -          currentKey;
          -      for (var index = 0; index < length; index++) {
          -        currentKey = keys[index];
          -        results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
          -      }
          -      return results;
          +    var keys = _.keys(obj),
          +        length = keys.length,
          +        results = {};
          +    for (var index = 0; index < length; index++) {
          +      var currentKey = keys[index];
          +      results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
          +    }
          +    return results;
             };
        265. -
        266. +
        267. - +
          -

          Convert an object into a list of [key, value] pairs.

          +

          Convert an object into a list of [key, value] pairs. +The opposite of _.object.

          -
            _.pairs = function(obj) {
          +            
            _.pairs = function(obj) {
               var keys = _.keys(obj);
               var length = keys.length;
               var pairs = Array(length);
          @@ -2201,17 +2303,17 @@ 

          Object Functions

        268. -
        269. +
        270. - +

          Invert the keys and values of an object. The values must be serializable.

          -
            _.invert = function(obj) {
          +            
            _.invert = function(obj) {
               var result = {};
               var keys = _.keys(obj);
               for (var i = 0, length = keys.length; i < length; i++) {
          @@ -2223,18 +2325,18 @@ 

          Object Functions

        271. -
        272. +
        273. - +

          Return a sorted list of the function names available on the object. -Aliased as methods

          +Aliased as methods.

          -
            _.functions = _.methods = function(obj) {
          +            
            _.functions = _.methods = function(obj) {
               var names = [];
               for (var key in obj) {
                 if (_.isFunction(obj[key])) names.push(key);
          @@ -2245,11 +2347,42 @@ 

          Object Functions

        274. -
        275. +
        276. - + +
          +

          An internal function for creating assigner functions.

          + +
          + +
            var createAssigner = function(keysFunc, defaults) {
          +    return function(obj) {
          +      var length = arguments.length;
          +      if (defaults) obj = Object(obj);
          +      if (length < 2 || obj == null) return obj;
          +      for (var index = 1; index < length; index++) {
          +        var source = arguments[index],
          +            keys = keysFunc(source),
          +            l = keys.length;
          +        for (var i = 0; i < l; i++) {
          +          var key = keys[i];
          +          if (!defaults || obj[key] === void 0) obj[key] = source[key];
          +        }
          +      }
          +      return obj;
          +    };
          +  };
          + +
        277. + + +
        278. +
          + +
          +

          Extend a given object with all the properties in passed-in object(s).

          @@ -2260,13 +2393,13 @@

          Object Functions

        279. -
        280. +
        281. - +
          -

          Assigns a given object with all the own properties in the passed-in object(s) +

          Assigns a given object with all the own properties in the passed-in object(s). (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)

          @@ -2276,17 +2409,17 @@

          Object Functions

        282. -
        283. +
        284. - +
          -

          Returns the first key on an object that passes a predicate test

          +

          Returns the first key on an object that passes a predicate test.

          -
            _.findKey = function(obj, predicate, context) {
          +            
            _.findKey = function(obj, predicate, context) {
               predicate = cb(predicate, context);
               var keys = _.keys(obj), key;
               for (var i = 0, length = keys.length; i < length; i++) {
          @@ -2298,25 +2431,42 @@ 

          Object Functions

        285. -
        286. +
        287. - + +
          +

          Internal pick helper function to determine if obj has key key.

          + +
          + +
            var keyInObj = function(value, key, obj) {
          +    return key in obj;
          +  };
          + +
        288. + + +
        289. +
          + +
          +

          Return a copy of the object only containing the whitelisted properties.

          -
            _.pick = function(object, oiteratee, context) {
          -    var result = {}, obj = object, iteratee, keys;
          +            
            _.pick = restArguments(function(obj, keys) {
          +    var result = {}, iteratee = keys[0];
               if (obj == null) return result;
          -    if (_.isFunction(oiteratee)) {
          +    if (_.isFunction(iteratee)) {
          +      if (keys.length > 1) iteratee = optimizeCb(iteratee, keys[1]);
                 keys = _.allKeys(obj);
          -      iteratee = optimizeCb(oiteratee, context);
               } else {
          -      keys = flatten(arguments, false, false, 1);
          -      iteratee = function(value, key, obj) { return key in obj; };
          +      iteratee = keyInObj;
          +      keys = flatten(keys, false, false);
                 obj = Object(obj);
               }
               for (var i = 0, length = keys.length; i < length; i++) {
          @@ -2325,41 +2475,43 @@ 

          Object Functions

          if (iteratee(value, key, obj)) result[key] = value; } return result; - };
          + });
        290. -
        291. +
        292. - +

          Return a copy of the object without the blacklisted properties.

          -
            _.omit = function(obj, iteratee, context) {
          +            
            _.omit = restArguments(function(obj, keys) {
          +    var iteratee = keys[0], context;
               if (_.isFunction(iteratee)) {
                 iteratee = _.negate(iteratee);
          +      if (keys.length > 1) context = keys[1];
               } else {
          -      var keys = _.map(flatten(arguments, false, false, 1), String);
          -      iteratee = function(value, key) {
          +      keys = _.map(flatten(keys, false, false), String);
          +      iteratee = function(value, key) {
                   return !_.contains(keys, key);
                 };
               }
               return _.pick(obj, iteratee, context);
          -  };
          + });
        293. -
        294. +
        295. - +

          Fill in a given object with default properties.

          @@ -2370,11 +2522,11 @@

          Object Functions

        296. -
        297. +
        298. - +

          Creates an object that inherits from the given prototype object. If additional properties are provided then they will be added to the @@ -2382,7 +2534,7 @@

          Object Functions

          -
            _.create = function(prototype, props) {
          +            
            _.create = function(prototype, props) {
               var result = baseCreate(prototype);
               if (props) _.extendOwn(result, props);
               return result;
          @@ -2391,17 +2543,17 @@ 

          Object Functions

        299. -
        300. +
        301. - +

          Create a (shallow-cloned) duplicate of an object.

          -
            _.clone = function(obj) {
          +            
            _.clone = function(obj) {
               if (!_.isObject(obj)) return obj;
               return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
             };
          @@ -2409,11 +2561,11 @@

          Object Functions

        302. -
        303. +
        304. - +

          Invokes interceptor with the obj, and then returns obj. The primary purpose of this method is to “tap into” a method chain, in @@ -2421,7 +2573,7 @@

          Object Functions

          -
            _.tap = function(obj, interceptor) {
          +            
            _.tap = function(obj, interceptor) {
               interceptor(obj);
               return obj;
             };
          @@ -2429,17 +2581,17 @@

          Object Functions

        305. -
        306. +
        307. - +

          Returns whether an object has a given set of key:value pairs.

          -
            _.isMatch = function(object, attrs) {
          +            
            _.isMatch = function(object, attrs) {
               var keys = _.keys(attrs), length = keys.length;
               if (object == null) return !length;
               var obj = Object(object);
          @@ -2453,26 +2605,27 @@ 

          Object Functions

        308. -
        309. +
        310. - +

          Internal recursive comparison function for isEqual.

          -
            var eq = function(a, b, aStack, bStack) {
          +
            var eq, deepEq;
          +  eq = function(a, b, aStack, bStack) {
        311. -
        312. +
        313. - +

          Identical objects are equal. 0 === -0, but they aren’t identical. See the Harmony egal proposal.

          @@ -2484,26 +2637,74 @@

          Object Functions

        314. -
        315. +
        316. - +
          -

          A strict comparison is necessary because null == undefined.

          +

          null or undefined only equal to itself (strict comparison).

          -
              if (a == null || b == null) return a === b;
          +
              if (a == null || b == null) return false;
        317. -
        318. +
        319. - + +
          +

          NaNs are equivalent, but non-reflexive.

          + +
          + +
              if (a !== a) return b !== b;
          + +
        320. + + +
        321. +
          + +
          + +
          +

          Exhaust primitive checks

          + +
          + +
              var type = typeof a;
          +    if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;
          +    return deepEq(a, b, aStack, bStack);
          +  };
          + +
        322. + + +
        323. +
          + +
          + +
          +

          Internal recursive comparison function for isEqual.

          + +
          + +
            deepEq = function(a, b, aStack, bStack) {
          + +
        324. + + +
        325. +
          + +
          +

          Unwrap any wrapped objects.

          @@ -2515,11 +2716,11 @@

          Object Functions

        326. -
        327. +
        328. - +

          Compare [[Class]] names.

          @@ -2532,11 +2733,11 @@

          Object Functions

        329. -
        330. +
        331. - +

          Strings, numbers, regular expressions, dates, and booleans are compared by value.

          @@ -2547,11 +2748,11 @@

          Object Functions

        332. -
        333. +
        334. - +

          RegExps are coerced to strings for comparison (Note: ‘’ + /a/i === ‘/a/i’)

          @@ -2562,11 +2763,11 @@

          Object Functions

        335. -
        336. +
        337. - +

          Primitives and their corresponding object wrappers are equivalent; thus, "5" is equivalent to new String("5").

          @@ -2579,14 +2780,14 @@

          Object Functions

        338. -
        339. +
        340. - +

          NaNs are equivalent, but non-reflexive. -Object(NaN) is equivalent to NaN

          +Object(NaN) is equivalent to NaN.

          @@ -2595,11 +2796,11 @@

          Object Functions

        341. -
        342. +
        343. - +

          An egal comparison is performed for other numeric values.

          @@ -2612,11 +2813,11 @@

          Object Functions

        344. -
        345. +
        346. - +

          Coerce dates and booleans to numeric primitive values. Dates are compared by their millisecond representations. Note that invalid dates with millisecond representations @@ -2625,6 +2826,8 @@

          Object Functions

                  return +a === +b;
          +      case '[object Symbol]':
          +        return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b);
               }
           
               var areArrays = className === '[object Array]';
          @@ -2634,11 +2837,11 @@ 

          Object Functions

        347. -
        348. +
        349. - +

          Objects with different constructors are not equivalent, but Objects or Arrays from different frames are.

          @@ -2656,11 +2859,11 @@

          Object Functions

        350. -
        351. +
        352. - +

          Assume equality for cyclic structures. The algorithm for detecting cyclic structures is adapted from ES 5.1 section 15.12.3, abstract operation JO.

          @@ -2670,11 +2873,11 @@

          Object Functions

        353. -
        354. +
        355. - +

          Initializing stack of traversed objects. It’s done here since we only need them for objects and arrays comparison.

          @@ -2689,11 +2892,11 @@

          Object Functions

        356. -
        357. +
        358. - +

          Linear search. Performance is inversely proportional to the number of unique nested structures.

          @@ -2706,11 +2909,11 @@

          Object Functions

        359. -
        360. +
        361. - +

          Add the first object to the stack of traversed objects.

          @@ -2722,11 +2925,11 @@

          Object Functions

        362. -
        363. +
        364. - +

          Recursively compare objects and arrays.

          @@ -2737,11 +2940,11 @@

          Object Functions

        365. -
        366. +
        367. - +

          Compare array lengths to determine if a deep comparison is necessary.

          @@ -2753,11 +2956,11 @@

          Object Functions

        368. -
        369. +
        370. - +

          Deep compare the contents, ignoring non-numeric properties.

          @@ -2771,11 +2974,11 @@

          Object Functions

        371. -
        372. +
        373. - +

          Deep compare objects.

          @@ -2787,11 +2990,11 @@

          Object Functions

        374. -
        375. +
        376. - +

          Ensure that both objects contain the same number of properties before comparing deep equality.

          @@ -2803,11 +3006,11 @@

          Object Functions

        377. -
        378. +
        379. - +

          Deep compare each member

          @@ -2821,11 +3024,11 @@

          Object Functions

        380. -
        381. +
        382. - +

          Remove the first object from the stack of traversed objects.

          @@ -2839,35 +3042,35 @@

          Object Functions

        383. -
        384. +
        385. - +

          Perform a deep comparison to check if two objects are equal.

          -
            _.isEqual = function(a, b) {
          +            
            _.isEqual = function(a, b) {
               return eq(a, b);
             };
        386. -
        387. +
        388. - +

          Is a given array, string, or object empty? An “empty” object has no enumerable own-properties.

          -
            _.isEmpty = function(obj) {
          +            
            _.isEmpty = function(obj) {
               if (obj == null) return true;
               if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
               return _.keys(obj).length === 0;
          @@ -2876,52 +3079,52 @@ 

          Object Functions

        389. -
        390. +
        391. - +

          Is a given value a DOM element?

          -
            _.isElement = function(obj) {
          +            
            _.isElement = function(obj) {
               return !!(obj && obj.nodeType === 1);
             };
        392. -
        393. +
        394. - +

          Is a given value an array? Delegates to ECMA5’s native Array.isArray

          -
            _.isArray = nativeIsArray || function(obj) {
          +            
            _.isArray = nativeIsArray || function(obj) {
               return toString.call(obj) === '[object Array]';
             };
        395. -
        396. +
        397. - +

          Is a given variable an object?

          -
            _.isObject = function(obj) {
          +            
            _.isObject = function(obj) {
               var type = typeof obj;
               return type === 'function' || type === 'object' && !!obj;
             };
          @@ -2929,18 +3132,18 @@

          Object Functions

        398. -
        399. +
        400. - +
          -

          Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError.

          +

          Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError, isMap, isWeakMap, isSet, isWeakSet.

          -
            _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) {
          -    _['is' + name] = function(obj) {
          +            
            _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'], function(name) {
          +    _['is' + name] = function(obj) {
                 return toString.call(obj) === '[object ' + name + ']';
               };
             });
          @@ -2948,11 +3151,11 @@

          Object Functions

        401. -
        402. +
        403. - +

          Define a fallback version of the method in browsers (ahem, IE < 9), where there isn’t any inspectable “Arguments” type.

          @@ -2960,7 +3163,7 @@

          Object Functions

            if (!_.isArguments(arguments)) {
          -    _.isArguments = function(obj) {
          +    _.isArguments = function(obj) {
                 return _.has(obj, 'callee');
               };
             }
          @@ -2968,19 +3171,20 @@

          Object Functions

        404. -
        405. +
        406. - +

          Optimize isFunction if appropriate. Work around some typeof bugs in old v8, -IE 11 (#1621), and in Safari 8 (#1929).

          +IE 11 (#1621), Safari 8 (#1929), and PhantomJS (#2236).

          -
            if (typeof /./ != 'function' && typeof Int8Array != 'object') {
          -    _.isFunction = function(obj) {
          +            
            var nodelist = root.document && root.document.childNodes;
          +  if (typeof /./ != 'function' && typeof Int8Array != 'object' && typeof nodelist != 'function') {
          +    _.isFunction = function(obj) {
                 return typeof obj == 'function' || false;
               };
             }
          @@ -2988,114 +3192,125 @@

          Object Functions

        407. -
        408. +
        409. - +

          Is a given object a finite number?

          -
            _.isFinite = function(obj) {
          -    return isFinite(obj) && !isNaN(parseFloat(obj));
          +            
            _.isFinite = function(obj) {
          +    return !_.isSymbol(obj) && isFinite(obj) && !isNaN(parseFloat(obj));
             };
        410. -
        411. +
        412. - +
          -

          Is the given value NaN? (NaN is the only number which does not equal itself).

          +

          Is the given value NaN?

          -
            _.isNaN = function(obj) {
          -    return _.isNumber(obj) && obj !== +obj;
          +            
            _.isNaN = function(obj) {
          +    return _.isNumber(obj) && isNaN(obj);
             };
        413. -
        414. +
        415. - +

          Is a given value a boolean?

          -
            _.isBoolean = function(obj) {
          +            
            _.isBoolean = function(obj) {
               return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
             };
        416. -
        417. +
        418. - +

          Is a given value equal to null?

          -
            _.isNull = function(obj) {
          +            
            _.isNull = function(obj) {
               return obj === null;
             };
        419. -
        420. +
        421. - +

          Is a given variable undefined?

          -
            _.isUndefined = function(obj) {
          +            
            _.isUndefined = function(obj) {
               return obj === void 0;
             };
        422. -
        423. +
        424. - +

          Shortcut function for checking if an object has a given property directly on itself (in other words, not on a prototype).

          -
            _.has = function(obj, key) {
          -    return obj != null && hasOwnProperty.call(obj, key);
          +            
            _.has = function(obj, path) {
          +    if (!_.isArray(path)) {
          +      return obj != null && hasOwnProperty.call(obj, path);
          +    }
          +    var length = path.length;
          +    for (var i = 0; i < length; i++) {
          +      var key = path[i];
          +      if (obj == null || !hasOwnProperty.call(obj, key)) {
          +        return false;
          +      }
          +      obj = obj[key];
          +    }
          +    return !!length;
             };
        425. -
        426. +
        427. - +

          Utility Functions

          @@ -3104,11 +3319,11 @@

          Utility Functions

        428. -
        429. +
        430. - +
          @@ -3116,18 +3331,18 @@

          Utility Functions

        431. -
        432. +
        433. - +

          Run Underscore.js in noConflict mode, returning the _ variable to its previous owner. Returns a reference to the Underscore object.

          -
            _.noConflict = function() {
          +            
            _.noConflict = function() {
               root._ = previousUnderscore;
               return this;
             };
          @@ -3135,79 +3350,103 @@

          Utility Functions

        434. -
        435. +
        436. - +

          Keep the identity function around for default iteratees.

          -
            _.identity = function(value) {
          +            
            _.identity = function(value) {
               return value;
             };
        437. -
        438. +
        439. - +

          Predicate-generating functions. Often useful outside of Underscore.

          -
            _.constant = function(value) {
          -    return function() {
          +            
            _.constant = function(value) {
          +    return function() {
                 return value;
               };
             };
           
          -  _.noop = function(){};
          +  _.noop = function(){};
          + +
        440. + + +
        441. +
          + +
          + +
          +

          Creates a function that, when passed an object, will traverse that object’s +properties down the given path, specified as an array of keys or indexes.

          - _.property = property;
        442. + + +
            _.property = function(path) {
          +    if (!_.isArray(path)) {
          +      return shallowProperty(path);
          +    }
          +    return function(obj) {
          +      return deepGet(obj, path);
          +    };
          +  };
          -
        443. +
        444. - +

          Generates a function for a given object that returns a given property.

          -
            _.propertyOf = function(obj) {
          -    return obj == null ? function(){} : function(key) {
          -      return obj[key];
          +            
            _.propertyOf = function(obj) {
          +    if (obj == null) {
          +      return function(){};
          +    }
          +    return function(path) {
          +      return !_.isArray(path) ? obj[path] : deepGet(obj, path);
               };
             };
        445. -
        446. +
        447. - +

          Returns a predicate for checking whether an object has a given set of key:value pairs.

          -
            _.matcher = _.matches = function(attrs) {
          +            
            _.matcher = _.matches = function(attrs) {
               attrs = _.extendOwn({}, attrs);
          -    return function(obj) {
          +    return function(obj) {
                 return _.isMatch(obj, attrs);
               };
             };
          @@ -3215,17 +3454,17 @@

          Utility Functions

        448. -
        449. +
        450. - +

          Run a function n times.

          -
            _.times = function(n, iteratee, context) {
          +            
            _.times = function(n, iteratee, context) {
               var accum = Array(Math.max(0, n));
               iteratee = optimizeCb(iteratee, context, 1);
               for (var i = 0; i < n; i++) accum[i] = iteratee(i);
          @@ -3235,17 +3474,17 @@ 

          Utility Functions

        451. -
        452. +
        453. - +

          Return a random integer between min and max (inclusive).

          -
            _.random = function(min, max) {
          +            
            _.random = function(min, max) {
               if (max == null) {
                 max = min;
                 min = 0;
          @@ -3256,28 +3495,28 @@ 

          Utility Functions

        454. -
        455. +
        456. - +

          A (possibly faster) way to get the current timestamp as an integer.

          -
            _.now = Date.now || function() {
          +            
            _.now = Date.now || function() {
               return new Date().getTime();
             };
        457. -
        458. +
        459. - +

          List of HTML entities for escaping.

          @@ -3296,38 +3535,38 @@

          Utility Functions

        460. -
        461. +
        462. - +

          Functions for escaping and unescaping strings to/from HTML interpolation.

          -
            var createEscaper = function(map) {
          -    var escaper = function(match) {
          +            
            var createEscaper = function(map) {
          +    var escaper = function(match) {
                 return map[match];
               };
        463. -
        464. +
        465. - +
          -

          Regexes for identifying a key that needs to be escaped

          +

          Regexes for identifying a key that needs to be escaped.

              var source = '(?:' + _.keys(map).join('|') + ')';
               var testRegexp = RegExp(source);
               var replaceRegexp = RegExp(source, 'g');
          -    return function(string) {
          +    return function(string) {
                 string = string == null ? '' : '' + string;
                 return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
               };
          @@ -3338,33 +3577,43 @@ 

          Utility Functions

        466. -
        467. +
        468. - +
          -

          If the value of the named property is a function then invoke it with the -object as context; otherwise, return it.

          +

          Traverses the children of obj along path. If a child is a function, it +is invoked with its parent as context. Returns the value of the final +child, or fallback if any child is undefined.

          -
            _.result = function(object, property, fallback) {
          -    var value = object == null ? void 0 : object[property];
          -    if (value === void 0) {
          -      value = fallback;
          +            
            _.result = function(obj, path, fallback) {
          +    if (!_.isArray(path)) path = [path];
          +    var length = path.length;
          +    if (!length) {
          +      return _.isFunction(fallback) ? fallback.call(obj) : fallback;
          +    }
          +    for (var i = 0; i < length; i++) {
          +      var prop = obj == null ? void 0 : obj[path[i]];
          +      if (prop === void 0) {
          +        prop = fallback;
          +        i = length; // Ensure we don't continue iterating.
          +      }
          +      obj = _.isFunction(prop) ? prop.call(obj) : prop;
               }
          -    return _.isFunction(value) ? value.call(object) : value;
          +    return obj;
             };
        469. -
        470. +
        471. - +

          Generate a unique integer id (unique within the entire client session). Useful for temporary DOM ids.

          @@ -3372,7 +3621,7 @@

          Utility Functions

            var idCounter = 0;
          -  _.uniqueId = function(prefix) {
          +  _.uniqueId = function(prefix) {
               var id = ++idCounter + '';
               return prefix ? prefix + id : id;
             };
          @@ -3380,11 +3629,11 @@

          Utility Functions

        472. -
        473. +
        474. - +

          By default, Underscore uses ERB-style template delimiters, change the following template settings to use alternative delimiters.

          @@ -3392,19 +3641,19 @@

          Utility Functions

            _.templateSettings = {
          -    evaluate    : /<%([\s\S]+?)%>/g,
          -    interpolate : /<%=([\s\S]+?)%>/g,
          -    escape      : /<%-([\s\S]+?)%>/g
          +    evaluate: /<%([\s\S]+?)%>/g,
          +    interpolate: /<%=([\s\S]+?)%>/g,
          +    escape: /<%-([\s\S]+?)%>/g
             };
        475. -
        476. +
        477. - +

          When customizing templateSettings, if you don’t want to define an interpolation, evaluation or escaping regex, we need one that is @@ -3417,11 +3666,11 @@

          Utility Functions

        478. -
        479. +
        480. - +

          Certain characters need to be escaped so that they can be put into a string literal.

          @@ -3429,28 +3678,28 @@

          Utility Functions

            var escapes = {
          -    "'":      "'",
          -    '\\':     '\\',
          -    '\r':     'r',
          -    '\n':     'n',
          +    "'": "'",
          +    '\\': '\\',
          +    '\r': 'r',
          +    '\n': 'n',
               '\u2028': 'u2028',
               '\u2029': 'u2029'
             };
           
          -  var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
          +  var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g;
           
          -  var escapeChar = function(match) {
          +  var escapeChar = function(match) {
               return '\\' + escapes[match];
             };
        481. -
        482. +
        483. - +

          JavaScript micro-templating, similar to John Resig’s implementation. Underscore templating handles arbitrary delimiters, preserves whitespace, @@ -3459,18 +3708,18 @@

          Utility Functions

          -
            _.template = function(text, settings, oldSettings) {
          +            
            _.template = function(text, settings, oldSettings) {
               if (!settings && oldSettings) settings = oldSettings;
               settings = _.defaults({}, settings, _.templateSettings);
        484. -
        485. +
        486. - +

          Combine delimiters into one regular expression via alternation.

          @@ -3485,11 +3734,11 @@

          Utility Functions

        487. -
        488. +
        489. - +

          Compile the template source, escaping string literals appropriately.

          @@ -3497,8 +3746,8 @@

          Utility Functions

              var index = 0;
               var source = "__p+='";
          -    text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
          -      source += text.slice(index, offset).replace(escaper, escapeChar);
          +    text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
          +      source += text.slice(index, offset).replace(escapeRegExp, escapeChar);
                 index = offset + match.length;
           
                 if (escape) {
          @@ -3512,11 +3761,11 @@ 

          Utility Functions

        490. -
        491. +
        492. - +

          Adobe VMs need the match returned to produce the correct offset.

          @@ -3529,11 +3778,11 @@

          Utility Functions

        493. -
        494. +
        495. - +

          If a variable is not specified, place data values in local scope.

          @@ -3545,25 +3794,26 @@

          Utility Functions

          "print=function(){__p+=__j.call(arguments,'');};\n" + source + 'return __p;\n'; + var render; try { - var render = new Function(settings.variable || 'obj', '_', source); + render = new Function(settings.variable || 'obj', '_', source); } catch (e) { e.source = source; throw e; } - var template = function(data) { + var template = function(data) { return render.call(this, data, _); };
        496. -
        497. +
        498. - +

          Provide the compiled source as a convenience for precompilation.

          @@ -3578,17 +3828,17 @@

          Utility Functions

        499. -
        500. +
        501. - +

          Add a “chain” function. Start chaining a wrapped Underscore object.

          -
            _.chain = function(obj) {
          +            
            _.chain = function(obj) {
               var instance = _(obj);
               instance._chain = true;
               return instance;
          @@ -3597,11 +3847,11 @@ 

          Utility Functions

        502. -
        503. +
        504. - +

          OOP

          @@ -3610,11 +3860,11 @@

          OOP

        505. -
        506. +
        507. - +

          If Underscore is called as a function, it returns a wrapped object that can be used OO-style. This wrapper holds altered versions of all the @@ -3625,52 +3875,53 @@

          OOP

        508. -
        509. +
        510. - +

          Helper function to continue chaining intermediate results.

          -
            var result = function(instance, obj) {
          +            
            var chainResult = function(instance, obj) {
               return instance._chain ? _(obj).chain() : obj;
             };
        511. -
        512. +
        513. - +

          Add your own custom functions to the Underscore object.

          -
            _.mixin = function(obj) {
          -    _.each(_.functions(obj), function(name) {
          +            
            _.mixin = function(obj) {
          +    _.each(_.functions(obj), function(name) {
                 var func = _[name] = obj[name];
          -      _.prototype[name] = function() {
          +      _.prototype[name] = function() {
                   var args = [this._wrapped];
                   push.apply(args, arguments);
          -        return result(this, func.apply(_, args));
          +        return chainResult(this, func.apply(_, args));
                 };
               });
          +    return _;
             };
        514. -
        515. +
        516. - +

          Add all of the Underscore functions to the wrapper object.

          @@ -3681,71 +3932,71 @@

          OOP

        517. -
        518. +
        519. - +

          Add all mutator Array functions to the wrapper.

          -
            _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
          +            
            _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
               var method = ArrayProto[name];
          -    _.prototype[name] = function() {
          +    _.prototype[name] = function() {
                 var obj = this._wrapped;
                 method.apply(obj, arguments);
                 if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
          -      return result(this, obj);
          +      return chainResult(this, obj);
               };
             });
        520. -
        521. +
        522. - +

          Add all accessor Array functions to the wrapper.

          -
            _.each(['concat', 'join', 'slice'], function(name) {
          +            
            _.each(['concat', 'join', 'slice'], function(name) {
               var method = ArrayProto[name];
          -    _.prototype[name] = function() {
          -      return result(this, method.apply(this._wrapped, arguments));
          +    _.prototype[name] = function() {
          +      return chainResult(this, method.apply(this._wrapped, arguments));
               };
             });
        523. -
        524. +
        525. - +

          Extracts the result from a wrapped and chained object.

          -
            _.prototype.value = function() {
          +            
            _.prototype.value = function() {
               return this._wrapped;
             };
        526. -
        527. +
        528. - +

          Provide unwrapping proxy for some methods used in engine operations such as arithmetic and JSON stringification.

          @@ -3754,18 +4005,18 @@

          OOP

            _.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
           
          -  _.prototype.toString = function() {
          -    return '' + this._wrapped;
          +  _.prototype.toString = function() {
          +    return String(this._wrapped);
             };
        529. -
        530. +
        531. - +

          AMD registration happens at the end for compatibility with AMD loaders that may not enforce next-turn semantics on modules. Even though general @@ -3777,12 +4028,12 @@

          OOP

          -
            if (typeof define === 'function' && define.amd) {
          -    define('underscore', [], function() {
          +            
            if (typeof define == 'function' && define.amd) {
          +    define('underscore', [], function() {
                 return _;
               });
             }
          -}.call(this));
          +}());
        532. diff --git a/index.html b/index.html index 9d7d837b0..1b660928d 100644 --- a/index.html +++ b/index.html @@ -46,8 +46,8 @@ font-weight: normal; } ul.toc_section { - font-size: 14px; - line-height: 16px; + font-size: 11px; + line-height: 14px; margin: 5px 0 0 0; padding-left: 0px; list-style-type: none; @@ -182,7 +182,7 @@ @@ -322,6 +324,10 @@
        533. - isRegExp
        534. - isError
        535. - isSymbol
        536. +
        537. - isMap
        538. +
        539. - isWeakMap
        540. +
        541. - isSet
        542. +
        543. - isWeakSet
        544. - isNaN
        545. - isNull
        546. - isUndefined
        547. @@ -434,14 +440,14 @@

          Downloads (Right-click, and u - - + + - + @@ -449,7 +455,7 @@

          Downloads (Right-click, and u

          - +
          Development Version (1.8.3)52kb, Uncompressed with Plentiful CommentsDevelopment Version (1.9.0)60kb, Uncompressed with Plentiful Comments
          Production Version (1.8.3)Production Version (1.9.0) - 5.7kb, Minified and Gzipped -  (Source Map) + 6.5kb, Minified and Gzipped +  (Source Map)
          Edge VersionUnreleased, current master, use at your own riskUnreleased, current master, use by your own judgement and at your own risk
          @@ -1038,6 +1044,17 @@

          Array Functions

          _.object([['moe', 30], ['larry', 40], ['curly', 50]]); => {moe: 30, larry: 40, curly: 50} +

          + +

          + chunk_.chunk(array, length) +
          + Chunks an array into multiple arrays, each containing length + or fewer items. +

          +
          +var partners = _.chunk(_.shuffle(kindergarten), 2);
          +=> [["Tyrone", "Elie"], ["Aidan", "Sam"], ["Katrina", "Billie"], ["Little Timmy"]]
           

          @@ -1264,6 +1281,10 @@

          Function (uh, ahem) Functions

          var throttled = _.throttle(updatePosition, 100); $(window).scroll(throttled);
          +

          + If you need to cancel a scheduled throttle, you can call .cancel() + on the throttled function. +

          debounce_.debounce(function, wait, [immediate]) @@ -1276,13 +1297,11 @@

          Function (uh, ahem) Functions

          preview of a Markdown comment, recalculating a layout after the window has stopped being resized, and so on.

          -

          At the end of the wait interval, the function will be called with the arguments that were passed most recently to the debounced function.

          -

          Pass true for the immediate argument to cause debounce to trigger the function on the leading instead of the @@ -1290,11 +1309,14 @@

          Function (uh, ahem) Functions

          preventing accidental double-clicks on a "submit" button from firing a second time.

          -
           var lazyLayout = _.debounce(calculateLayout, 300);
           $(window).resize(lazyLayout);
           
          +

          + If you need to cancel a scheduled debounce, you can call .cancel() + on the debounced function. +

          once_.once(function) @@ -1384,6 +1406,24 @@

          Function (uh, ahem) Functions

          var welcome = _.compose(greet, exclaim); welcome('moe'); => 'hi: MOE!' + + +

          + restArguments_.restArguments(function, [startIndex]) +
          + Returns a version of the function that, when called, receives all + arguments from and beyond startIndex collected into a single array. + If you don’t pass an explicit startIndex, it will be determined by + looking at the number of arguments to the function itself. Similar + to ES6’s rest + parameters syntax. +

          +
          +var raceResults = _.restArguments(function(gold, silver, bronze, everyoneElse) {
          +  _.each(everyoneElse, sendConsolations);
          +});
          +
          +raceResults("Dopey", "Grumpy", "Happy", "Sneezy", "Bashful", "Sleepy", "Doc");
           

          Object Functions

          @@ -1590,7 +1630,7 @@

          Object Functions

          Does the object contain the given key? Identical to object.hasOwnProperty(key), but uses a safe reference to the hasOwnProperty function, in case it's been - + overridden accidentally.

          @@ -1599,15 +1639,21 @@ 

          Object Functions

          - property_.property(key) + property_.property(path)
          - Returns a function that will return the key - property of any passed-in object. + Returns a function that will return the specified property of any + passed-in object. path may be specified as a simple key, or + as an array of object keys or array indexes, for deep property fetching.

           var stooge = {name: 'moe'};
           'moe' === _.property('name')(stooge);
           => true
          +
          +var stooges = {moe: {fears: {worst: 'Spiders'}}, curly: {fears: {worst: 'Moe'}}};
          +var curlysWorstFear = _.property(['curly', 'fears', 'worst']);
          +curlysWorstFear(stooges);
          +=> 'Moe'
           

          @@ -1810,11 +1856,51 @@

          Object Functions

          isSymbol_.isSymbol(object)
          - Returns true if object is a Symbol. + Returns true if object is a Symbol.

           _.isSymbol(Symbol());
           => true
          +
          + +

          + isMap_.isMap(object) +
          + Returns true if object is a Map. +

          +
          +_.isMap(new Map());
          +=> true
          +
          + +

          + isWeakMap_.isWeakMap(object) +
          + Returns true if object is a WeakMap. +

          +
          +_.isWeakMap(new WeakMap());
          +=> true
          +
          + +

          + isSet_.isSet(object) +
          + Returns true if object is a Set. +

          +
          +_.isSet(new Set());
          +=> true
          +
          + +

          + isWeakSet_.isWeakSet(object) +
          + Returns true if object is a WeakSet. +

          +
          +_.isWeakSet(WeakSet());
          +=> true
           

          @@ -2304,6 +2390,50 @@

          Change Log

          +

          + 1.9.0April 18, 2018DiffDocs
          +

            +
          • + Adds the _.restArguments function for variadic function + handling. +
          • +
          • + Adds the _.chunk function for chunking up an array. +
          • +
          • + Adds a _.isSymbol, _.isMap, _.isWeakMap, + _.isSet and _.isWeakSet functions. +
          • +
          • + _.throttle and _.debounce return functions that now + have a .cancel() method, which can be used to cancel any + scheduled calls. +
          • +
          • + _.property now accepts arrays of keys and indexes as path + specifiers, for looking up a deep properties of a value. +
          • +
          • + _.range now accepts negative ranges to generate descending + arrays. +
          • +
          • + Adds support for several environments including: WebWorkers, + browserify and ES6 imports. +
          • +
          • + The placeholder used for partial is now configurable by setting + _.partial.placeholder. +
          • +
          • + _.bindAll now accepts arrays or arguments for keys. +
          • +
          • + Three years of performance improvements. +
          • +
          +

          +

          1.8.3April 2, 2015DiffDocs

            diff --git a/package.json b/package.json index c318e7893..3c5c2349e 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "url": "git://github.com/jashkenas/underscore.git" }, "main": "underscore.js", - "version": "1.8.3", + "version": "1.9.0", "devDependencies": { "coveralls": "^2.11.2", "docco": "*", @@ -37,7 +37,7 @@ "test-node": "qunit-cli test/*.js", "test-browser": "npm i karma-phantomjs-launcher && karma start", "minify": "uglifyjs underscore.js -c \"evaluate=false\" --comments \"/ .*/\" -m", - "build": "npm run minify -- --source-map \"filename='underscore-min.map'\" --source-map-url \" \" -o underscore-min.js", + "build": "npm run minify -- --source-map --source-map-url \" \" -o underscore-min.js", "doc": "docco underscore.js", "weight": "npm run minify | gzip-size | pretty-bytes" }, @@ -45,6 +45,6 @@ "files": [ "underscore.js", "underscore-min.js", - "underscore-min.map" + "underscore-min.js.map" ] } diff --git a/underscore-min.js b/underscore-min.js index f01025b7b..87b8d2e19 100644 --- a/underscore-min.js +++ b/underscore-min.js @@ -1,6 +1,5 @@ -// Underscore.js 1.8.3 +// Underscore.js 1.9.0 // http://underscorejs.org -// (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// (c) 2009-2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // Underscore may be freely distributed under the MIT license. -(function(){function n(n){function t(t,r,e,u,i,o){for(;i>=0&&o>i;i+=n){var a=u?u[i]:i;e=r(e,t[a],a,t)}return e}return function(r,e,u,i){e=b(e,i,4);var o=!k(r)&&m.keys(r),a=(o||r).length,c=n>0?0:a-1;return arguments.length<3&&(u=r[o?o[c]:c],c+=n),t(r,e,u,o,c,a)}}function t(n){return function(t,r,e){r=x(r,e);for(var u=O(t),i=n>0?0:u-1;i>=0&&u>i;i+=n)if(r(t[i],i,t))return i;return-1}}function r(n,t,r){return function(e,u,i){var o=0,a=O(e);if("number"==typeof i)n>0?o=i>=0?i:Math.max(i+a,o):a=i>=0?Math.min(i+1,a):i+a+1;else if(r&&i&&a)return i=r(e,u),e[i]===u?i:-1;if(u!==u)return i=t(l.call(e,o,a),m.isNaN),i>=0?i+o:-1;for(i=n>0?o:a-1;i>=0&&a>i;i+=n)if(e[i]===u)return i;return-1}}function e(n,t){var r=I.length,e=n.constructor,u=m.isFunction(e)&&e.prototype||a,i="constructor";for(m.has(n,i)&&!m.contains(t,i)&&t.push(i);r--;)i=I[r],i in n&&n[i]!==u[i]&&!m.contains(t,i)&&t.push(i)}var u=this,i=u._,o=Array.prototype,a=Object.prototype,c=Function.prototype,f=o.push,l=o.slice,s=a.toString,p=a.hasOwnProperty,h=Array.isArray,v=Object.keys,g=c.bind,y=Object.create,d=function(){},m=function(n){return n instanceof m?n:this instanceof m?void(this._wrapped=n):new m(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=m),exports._=m):u._=m,m.VERSION="1.8.3";var b=function(n,t,r){if(t===void 0)return n;switch(null==r?3:r){case 1:return function(r){return n.call(t,r)};case 2:return function(r,e){return n.call(t,r,e)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,i){return n.call(t,r,e,u,i)}}return function(){return n.apply(t,arguments)}},x=function(n,t,r){return null==n?m.identity:m.isFunction(n)?b(n,t,r):m.isObject(n)?m.matcher(n):m.property(n)};m.iteratee=function(n,t){return x(n,t,1/0)};var _=function(n,t){return function(r){var e=arguments.length;if(2>e||null==r)return r;for(var u=1;e>u;u++)for(var i=arguments[u],o=n(i),a=o.length,c=0;a>c;c++){var f=o[c];t&&r[f]!==void 0||(r[f]=i[f])}return r}},j=function(n){if(!m.isObject(n))return{};if(y)return y(n);d.prototype=n;var t=new d;return d.prototype=null,t},w=function(n){return function(t){return null==t?void 0:t[n]}},A=Math.pow(2,53)-1,O=w("length"),k=function(n){var t=O(n);return"number"==typeof t&&t>=0&&A>=t};m.each=m.forEach=function(n,t,r){t=b(t,r);var e,u;if(k(n))for(e=0,u=n.length;u>e;e++)t(n[e],e,n);else{var i=m.keys(n);for(e=0,u=i.length;u>e;e++)t(n[i[e]],i[e],n)}return n},m.map=m.collect=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=Array(u),o=0;u>o;o++){var a=e?e[o]:o;i[o]=t(n[a],a,n)}return i},m.reduce=m.foldl=m.inject=n(1),m.reduceRight=m.foldr=n(-1),m.find=m.detect=function(n,t,r){var e;return e=k(n)?m.findIndex(n,t,r):m.findKey(n,t,r),e!==void 0&&e!==-1?n[e]:void 0},m.filter=m.select=function(n,t,r){var e=[];return t=x(t,r),m.each(n,function(n,r,u){t(n,r,u)&&e.push(n)}),e},m.reject=function(n,t,r){return m.filter(n,m.negate(x(t)),r)},m.every=m.all=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(!t(n[o],o,n))return!1}return!0},m.some=m.any=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(t(n[o],o,n))return!0}return!1},m.contains=m.includes=m.include=function(n,t,r,e){return k(n)||(n=m.values(n)),("number"!=typeof r||e)&&(r=0),m.indexOf(n,t,r)>=0},m.invoke=function(n,t){var r=l.call(arguments,2),e=m.isFunction(t);return m.map(n,function(n){var u=e?t:n[t];return null==u?u:u.apply(n,r)})},m.pluck=function(n,t){return m.map(n,m.property(t))},m.where=function(n,t){return m.filter(n,m.matcher(t))},m.findWhere=function(n,t){return m.find(n,m.matcher(t))},m.max=function(n,t,r){var e,u,i=-1/0,o=-1/0;if(null==t&&null!=n){n=k(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],e>i&&(i=e)}else t=x(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(u>o||u===-1/0&&i===-1/0)&&(i=n,o=u)});return i},m.min=function(n,t,r){var e,u,i=1/0,o=1/0;if(null==t&&null!=n){n=k(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],i>e&&(i=e)}else t=x(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(o>u||1/0===u&&1/0===i)&&(i=n,o=u)});return i},m.shuffle=function(n){for(var t,r=k(n)?n:m.values(n),e=r.length,u=Array(e),i=0;e>i;i++)t=m.random(0,i),t!==i&&(u[i]=u[t]),u[t]=r[i];return u},m.sample=function(n,t,r){return null==t||r?(k(n)||(n=m.values(n)),n[m.random(n.length-1)]):m.shuffle(n).slice(0,Math.max(0,t))},m.sortBy=function(n,t,r){return t=x(t,r),m.pluck(m.map(n,function(n,r,e){return{value:n,index:r,criteria:t(n,r,e)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={};return r=x(r,e),m.each(t,function(e,i){var o=r(e,i,t);n(u,e,o)}),u}};m.groupBy=F(function(n,t,r){m.has(n,r)?n[r].push(t):n[r]=[t]}),m.indexBy=F(function(n,t,r){n[r]=t}),m.countBy=F(function(n,t,r){m.has(n,r)?n[r]++:n[r]=1}),m.toArray=function(n){return n?m.isArray(n)?l.call(n):k(n)?m.map(n,m.identity):m.values(n):[]},m.size=function(n){return null==n?0:k(n)?n.length:m.keys(n).length},m.partition=function(n,t,r){t=x(t,r);var e=[],u=[];return m.each(n,function(n,r,i){(t(n,r,i)?e:u).push(n)}),[e,u]},m.first=m.head=m.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:m.initial(n,n.length-t)},m.initial=function(n,t,r){return l.call(n,0,Math.max(0,n.length-(null==t||r?1:t)))},m.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:m.rest(n,Math.max(0,n.length-t))},m.rest=m.tail=m.drop=function(n,t,r){return l.call(n,null==t||r?1:t)},m.compact=function(n){return m.filter(n,m.identity)};var S=function(n,t,r,e){for(var u=[],i=0,o=e||0,a=O(n);a>o;o++){var c=n[o];if(k(c)&&(m.isArray(c)||m.isArguments(c))){t||(c=S(c,t,r));var f=0,l=c.length;for(u.length+=l;l>f;)u[i++]=c[f++]}else r||(u[i++]=c)}return u};m.flatten=function(n,t){return S(n,t,!1)},m.without=function(n){return m.difference(n,l.call(arguments,1))},m.uniq=m.unique=function(n,t,r,e){m.isBoolean(t)||(e=r,r=t,t=!1),null!=r&&(r=x(r,e));for(var u=[],i=[],o=0,a=O(n);a>o;o++){var c=n[o],f=r?r(c,o,n):c;t?(o&&i===f||u.push(c),i=f):r?m.contains(i,f)||(i.push(f),u.push(c)):m.contains(u,c)||u.push(c)}return u},m.union=function(){return m.uniq(S(arguments,!0,!0))},m.intersection=function(n){for(var t=[],r=arguments.length,e=0,u=O(n);u>e;e++){var i=n[e];if(!m.contains(t,i)){for(var o=1;r>o&&m.contains(arguments[o],i);o++);o===r&&t.push(i)}}return t},m.difference=function(n){var t=S(arguments,!0,!0,1);return m.filter(n,function(n){return!m.contains(t,n)})},m.zip=function(){return m.unzip(arguments)},m.unzip=function(n){for(var t=n&&m.max(n,O).length||0,r=Array(t),e=0;t>e;e++)r[e]=m.pluck(n,e);return r},m.object=function(n,t){for(var r={},e=0,u=O(n);u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},m.findIndex=t(1),m.findLastIndex=t(-1),m.sortedIndex=function(n,t,r,e){r=x(r,e,1);for(var u=r(t),i=0,o=O(n);o>i;){var a=Math.floor((i+o)/2);r(n[a])i;i++,n+=r)u[i]=n;return u};var E=function(n,t,r,e,u){if(!(e instanceof t))return n.apply(r,u);var i=j(n.prototype),o=n.apply(i,u);return m.isObject(o)?o:i};m.bind=function(n,t){if(g&&n.bind===g)return g.apply(n,l.call(arguments,1));if(!m.isFunction(n))throw new TypeError("Bind must be called on a function");var r=l.call(arguments,2),e=function(){return E(n,e,t,this,r.concat(l.call(arguments)))};return e},m.partial=function(n){var t=l.call(arguments,1),r=function(){for(var e=0,u=t.length,i=Array(u),o=0;u>o;o++)i[o]=t[o]===m?arguments[e++]:t[o];for(;e=e)throw new Error("bindAll must be passed function names");for(t=1;e>t;t++)r=arguments[t],n[r]=m.bind(n[r],n);return n},m.memoize=function(n,t){var r=function(e){var u=r.cache,i=""+(t?t.apply(this,arguments):e);return m.has(u,i)||(u[i]=n.apply(this,arguments)),u[i]};return r.cache={},r},m.delay=function(n,t){var r=l.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},m.defer=m.partial(m.delay,m,1),m.throttle=function(n,t,r){var e,u,i,o=null,a=0;r||(r={});var c=function(){a=r.leading===!1?0:m.now(),o=null,i=n.apply(e,u),o||(e=u=null)};return function(){var f=m.now();a||r.leading!==!1||(a=f);var l=t-(f-a);return e=this,u=arguments,0>=l||l>t?(o&&(clearTimeout(o),o=null),a=f,i=n.apply(e,u),o||(e=u=null)):o||r.trailing===!1||(o=setTimeout(c,l)),i}},m.debounce=function(n,t,r){var e,u,i,o,a,c=function(){var f=m.now()-o;t>f&&f>=0?e=setTimeout(c,t-f):(e=null,r||(a=n.apply(i,u),e||(i=u=null)))};return function(){i=this,u=arguments,o=m.now();var f=r&&!e;return e||(e=setTimeout(c,t)),f&&(a=n.apply(i,u),i=u=null),a}},m.wrap=function(n,t){return m.partial(t,n)},m.negate=function(n){return function(){return!n.apply(this,arguments)}},m.compose=function(){var n=arguments,t=n.length-1;return function(){for(var r=t,e=n[t].apply(this,arguments);r--;)e=n[r].call(this,e);return e}},m.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},m.before=function(n,t){var r;return function(){return--n>0&&(r=t.apply(this,arguments)),1>=n&&(t=null),r}},m.once=m.partial(m.before,2);var M=!{toString:null}.propertyIsEnumerable("toString"),I=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"];m.keys=function(n){if(!m.isObject(n))return[];if(v)return v(n);var t=[];for(var r in n)m.has(n,r)&&t.push(r);return M&&e(n,t),t},m.allKeys=function(n){if(!m.isObject(n))return[];var t=[];for(var r in n)t.push(r);return M&&e(n,t),t},m.values=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},m.mapObject=function(n,t,r){t=x(t,r);for(var e,u=m.keys(n),i=u.length,o={},a=0;i>a;a++)e=u[a],o[e]=t(n[e],e,n);return o},m.pairs=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},m.invert=function(n){for(var t={},r=m.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},m.functions=m.methods=function(n){var t=[];for(var r in n)m.isFunction(n[r])&&t.push(r);return t.sort()},m.extend=_(m.allKeys),m.extendOwn=m.assign=_(m.keys),m.findKey=function(n,t,r){t=x(t,r);for(var e,u=m.keys(n),i=0,o=u.length;o>i;i++)if(e=u[i],t(n[e],e,n))return e},m.pick=function(n,t,r){var e,u,i={},o=n;if(null==o)return i;m.isFunction(t)?(u=m.allKeys(o),e=b(t,r)):(u=S(arguments,!1,!1,1),e=function(n,t,r){return t in r},o=Object(o));for(var a=0,c=u.length;c>a;a++){var f=u[a],l=o[f];e(l,f,o)&&(i[f]=l)}return i},m.omit=function(n,t,r){if(m.isFunction(t))t=m.negate(t);else{var e=m.map(S(arguments,!1,!1,1),String);t=function(n,t){return!m.contains(e,t)}}return m.pick(n,t,r)},m.defaults=_(m.allKeys,!0),m.create=function(n,t){var r=j(n);return t&&m.extendOwn(r,t),r},m.clone=function(n){return m.isObject(n)?m.isArray(n)?n.slice():m.extend({},n):n},m.tap=function(n,t){return t(n),n},m.isMatch=function(n,t){var r=m.keys(t),e=r.length;if(null==n)return!e;for(var u=Object(n),i=0;e>i;i++){var o=r[i];if(t[o]!==u[o]||!(o in u))return!1}return!0};var N=function(n,t,r,e){if(n===t)return 0!==n||1/n===1/t;if(null==n||null==t)return n===t;n instanceof m&&(n=n._wrapped),t instanceof m&&(t=t._wrapped);var u=s.call(n);if(u!==s.call(t))return!1;switch(u){case"[object RegExp]":case"[object String]":return""+n==""+t;case"[object Number]":return+n!==+n?+t!==+t:0===+n?1/+n===1/t:+n===+t;case"[object Date]":case"[object Boolean]":return+n===+t}var i="[object Array]"===u;if(!i){if("object"!=typeof n||"object"!=typeof t)return!1;var o=n.constructor,a=t.constructor;if(o!==a&&!(m.isFunction(o)&&o instanceof o&&m.isFunction(a)&&a instanceof a)&&"constructor"in n&&"constructor"in t)return!1}r=r||[],e=e||[];for(var c=r.length;c--;)if(r[c]===n)return e[c]===t;if(r.push(n),e.push(t),i){if(c=n.length,c!==t.length)return!1;for(;c--;)if(!N(n[c],t[c],r,e))return!1}else{var f,l=m.keys(n);if(c=l.length,m.keys(t).length!==c)return!1;for(;c--;)if(f=l[c],!m.has(t,f)||!N(n[f],t[f],r,e))return!1}return r.pop(),e.pop(),!0};m.isEqual=function(n,t){return N(n,t)},m.isEmpty=function(n){return null==n?!0:k(n)&&(m.isArray(n)||m.isString(n)||m.isArguments(n))?0===n.length:0===m.keys(n).length},m.isElement=function(n){return!(!n||1!==n.nodeType)},m.isArray=h||function(n){return"[object Array]"===s.call(n)},m.isObject=function(n){var t=typeof n;return"function"===t||"object"===t&&!!n},m.each(["Arguments","Function","String","Number","Date","RegExp","Error"],function(n){m["is"+n]=function(t){return s.call(t)==="[object "+n+"]"}}),m.isArguments(arguments)||(m.isArguments=function(n){return m.has(n,"callee")}),"function"!=typeof/./&&"object"!=typeof Int8Array&&(m.isFunction=function(n){return"function"==typeof n||!1}),m.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},m.isNaN=function(n){return m.isNumber(n)&&n!==+n},m.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"===s.call(n)},m.isNull=function(n){return null===n},m.isUndefined=function(n){return n===void 0},m.has=function(n,t){return null!=n&&p.call(n,t)},m.noConflict=function(){return u._=i,this},m.identity=function(n){return n},m.constant=function(n){return function(){return n}},m.noop=function(){},m.property=w,m.propertyOf=function(n){return null==n?function(){}:function(t){return n[t]}},m.matcher=m.matches=function(n){return n=m.extendOwn({},n),function(t){return m.isMatch(t,n)}},m.times=function(n,t,r){var e=Array(Math.max(0,n));t=b(t,r,1);for(var u=0;n>u;u++)e[u]=t(u);return e},m.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},m.now=Date.now||function(){return(new Date).getTime()};var B={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},T=m.invert(B),R=function(n){var t=function(t){return n[t]},r="(?:"+m.keys(n).join("|")+")",e=RegExp(r),u=RegExp(r,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,t):n}};m.escape=R(B),m.unescape=R(T),m.result=function(n,t,r){var e=null==n?void 0:n[t];return e===void 0&&(e=r),m.isFunction(e)?e.call(n):e};var q=0;m.uniqueId=function(n){var t=++q+"";return n?n+t:t},m.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var K=/(.)^/,z={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\u2028|\u2029/g,L=function(n){return"\\"+z[n]};m.template=function(n,t,r){!t&&r&&(t=r),t=m.defaults({},t,m.templateSettings);var e=RegExp([(t.escape||K).source,(t.interpolate||K).source,(t.evaluate||K).source].join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,o,a){return i+=n.slice(u,a).replace(D,L),u=a+t.length,r?i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":e?i+="'+\n((__t=("+e+"))==null?'':__t)+\n'":o&&(i+="';\n"+o+"\n__p+='"),t}),i+="';\n",t.variable||(i="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var o=new Function(t.variable||"obj","_",i)}catch(a){throw a.source=i,a}var c=function(n){return o.call(this,n,m)},f=t.variable||"obj";return c.source="function("+f+"){\n"+i+"}",c},m.chain=function(n){var t=m(n);return t._chain=!0,t};var P=function(n,t){return n._chain?m(t).chain():t};m.mixin=function(n){m.each(m.functions(n),function(t){var r=m[t]=n[t];m.prototype[t]=function(){var n=[this._wrapped];return f.apply(n,arguments),P(this,r.apply(m,n))}})},m.mixin(m),m.each(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=o[n];m.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!==n&&"splice"!==n||0!==r.length||delete r[0],P(this,r)}}),m.each(["concat","join","slice"],function(n){var t=o[n];m.prototype[n]=function(){return P(this,t.apply(this._wrapped,arguments))}}),m.prototype.value=function(){return this._wrapped},m.prototype.valueOf=m.prototype.toJSON=m.prototype.value,m.prototype.toString=function(){return""+this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return m})}).call(this); -//# sourceMappingURL=underscore-min.map \ No newline at end of file +!function(){var n="object"==typeof self&&self.self===self&&self||"object"==typeof global&&global.global===global&&global||this||{},r=n._,e=Array.prototype,o=Object.prototype,s="undefined"!=typeof Symbol?Symbol.prototype:null,u=e.push,c=e.slice,p=o.toString,i=o.hasOwnProperty,t=Array.isArray,a=Object.keys,l=Object.create,f=function(){},h=function(n){return n instanceof h?n:this instanceof h?void(this._wrapped=n):new h(n)};"undefined"==typeof exports||exports.nodeType?n._=h:("undefined"!=typeof module&&!module.nodeType&&module.exports&&(exports=module.exports=h),exports._=h),h.VERSION="1.9.0";var v,y=function(u,i,n){if(void 0===i)return u;switch(null==n?3:n){case 1:return function(n){return u.call(i,n)};case 3:return function(n,r,t){return u.call(i,n,r,t)};case 4:return function(n,r,t,e){return u.call(i,n,r,t,e)}}return function(){return u.apply(i,arguments)}},d=function(n,r,t){return h.iteratee!==v?h.iteratee(n,r):null==n?h.identity:h.isFunction(n)?y(n,r,t):h.isObject(n)&&!h.isArray(n)?h.matcher(n):h.property(n)};h.iteratee=v=function(n,r){return d(n,r,1/0)};var g=function(u,i){return i=null==i?u.length-1:+i,function(){for(var n=Math.max(arguments.length-i,0),r=Array(n),t=0;t":">",'"':""","'":"'","`":"`"},L=h.invert(D),P=function(r){var t=function(n){return r[n]},n="(?:"+h.keys(r).join("|")+")",e=RegExp(n),u=RegExp(n,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,t):n}};h.escape=P(D),h.unescape=P(L),h.result=function(n,r,t){h.isArray(r)||(r=[r]);var e=r.length;if(!e)return h.isFunction(t)?t.call(n):t;for(var u=0;u/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var C=/(.)^/,J={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},U=/\\|'|\r|\n|\u2028|\u2029/g,V=function(n){return"\\"+J[n]};h.template=function(i,n,r){!n&&r&&(n=r),n=h.defaults({},n,h.templateSettings);var t,e=RegExp([(n.escape||C).source,(n.interpolate||C).source,(n.evaluate||C).source].join("|")+"|$","g"),o=0,a="__p+='";i.replace(e,function(n,r,t,e,u){return a+=i.slice(o,u).replace(U,V),o=u+n.length,r?a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":t?a+="'+\n((__t=("+t+"))==null?'':__t)+\n'":e&&(a+="';\n"+e+"\n__p+='"),n}),a+="';\n",n.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{t=new Function(n.variable||"obj","_",a)}catch(n){throw n.source=a,n}var u=function(n){return t.call(this,n,h)},c=n.variable||"obj";return u.source="function("+c+"){\n"+a+"}",u},h.chain=function(n){var r=h(n);return r._chain=!0,r};var $=function(n,r){return n._chain?h(r).chain():r};h.mixin=function(t){return h.each(h.functions(t),function(n){var r=h[n]=t[n];h.prototype[n]=function(){var n=[this._wrapped];return u.apply(n,arguments),$(this,r.apply(h,n))}}),h},h.mixin(h),h.each(["pop","push","reverse","shift","sort","splice","unshift"],function(r){var t=e[r];h.prototype[r]=function(){var n=this._wrapped;return t.apply(n,arguments),"shift"!==r&&"splice"!==r||0!==n.length||delete n[0],$(this,n)}}),h.each(["concat","join","slice"],function(n){var r=e[n];h.prototype[n]=function(){return $(this,r.apply(this._wrapped,arguments))}}),h.prototype.value=function(){return this._wrapped},h.prototype.valueOf=h.prototype.toJSON=h.prototype.value,h.prototype.toString=function(){return String(this._wrapped)},"function"==typeof define&&define.amd&&define("underscore",[],function(){return h})}(); \ No newline at end of file diff --git a/underscore-min.js.map b/underscore-min.js.map new file mode 100644 index 000000000..2b3d0d22c --- /dev/null +++ b/underscore-min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["underscore.js"],"names":["root","self","global","this","previousUnderscore","_","ArrayProto","Array","prototype","ObjProto","Object","SymbolProto","Symbol","push","slice","toString","hasOwnProperty","nativeIsArray","isArray","nativeKeys","keys","nativeCreate","create","Ctor","obj","_wrapped","exports","nodeType","module","VERSION","builtinIteratee","optimizeCb","func","context","argCount","value","call","index","collection","accumulator","apply","arguments","cb","iteratee","identity","isFunction","isObject","matcher","property","Infinity","restArguments","startIndex","length","Math","max","rest","args","baseCreate","result","shallowProperty","key","deepGet","path","i","MAX_ARRAY_INDEX","pow","getLength","isArrayLike","each","forEach","map","collect","results","currentKey","createReduce","dir","memo","initial","reducer","reduce","foldl","inject","reduceRight","foldr","find","detect","predicate","findIndex","findKey","filter","select","list","reject","negate","every","all","some","any","contains","includes","include","item","fromIndex","guard","values","indexOf","invoke","contextPath","method","pluck","where","attrs","findWhere","computed","lastComputed","v","min","shuffle","sample","n","random","clone","last","rand","temp","sortBy","criteria","sort","left","right","a","b","group","behavior","partition","groupBy","has","indexBy","countBy","reStrSymbol","toArray","isString","match","size","pass","first","head","take","array","tail","drop","compact","Boolean","flatten","input","shallow","strict","output","idx","isArguments","j","len","without","otherArrays","difference","uniq","unique","isSorted","isBoolean","seen","union","arrays","intersection","argsLength","unzip","zip","object","createPredicateIndexFinder","findLastIndex","sortedIndex","low","high","mid","floor","createIndexFinder","predicateFind","isNaN","lastIndexOf","range","start","stop","step","ceil","chunk","count","executeBound","sourceFunc","boundFunc","callingContext","bind","TypeError","bound","callArgs","concat","partial","boundArgs","placeholder","position","bindAll","Error","memoize","hasher","cache","address","delay","wait","setTimeout","defer","throttle","options","timeout","previous","later","leading","now","throttled","remaining","clearTimeout","trailing","cancel","debounce","immediate","debounced","callNow","wrap","wrapper","compose","after","times","before","once","hasEnumBug","propertyIsEnumerable","nonEnumerableProps","collectNonEnumProps","nonEnumIdx","constructor","proto","prop","allKeys","mapObject","pairs","invert","functions","methods","names","createAssigner","keysFunc","defaults","source","l","extend","extendOwn","assign","eq","deepEq","keyInObj","pick","omit","String","props","tap","interceptor","isMatch","aStack","bStack","type","className","valueOf","areArrays","aCtor","bCtor","pop","isEqual","isEmpty","isElement","name","nodelist","document","childNodes","Int8Array","isFinite","isSymbol","parseFloat","isNumber","isNull","isUndefined","noConflict","constant","noop","propertyOf","matches","accum","Date","getTime","escapeMap","&","<",">","\"","'","`","unescapeMap","createEscaper","escaper","join","testRegexp","RegExp","replaceRegexp","string","test","replace","escape","unescape","fallback","idCounter","uniqueId","prefix","id","templateSettings","evaluate","interpolate","noMatch","escapes","\\","\r","\n","
","
","escapeRegExp","escapeChar","template","text","settings","oldSettings","render","offset","variable","Function","e","data","argument","chain","instance","_chain","chainResult","mixin","toJSON","define","amd"],"mappings":";;;;CAKC,WAQC,IAAIA,EAAsB,iBAARC,MAAoBA,KAAKA,OAASA,MAAQA,MACjC,iBAAVC,QAAsBA,OAAOA,SAAWA,QAAUA,QACzDC,MACA,GAGNC,EAAqBJ,EAAKK,EAG1BC,EAAaC,MAAMC,UAAWC,EAAWC,OAAOF,UAChDG,EAAgC,oBAAXC,OAAyBA,OAAOJ,UAAY,KAGjEK,EAAOP,EAAWO,KAClBC,EAAQR,EAAWQ,MACnBC,EAAWN,EAASM,SACpBC,EAAiBP,EAASO,eAI1BC,EAAgBV,MAAMW,QACtBC,EAAaT,OAAOU,KACpBC,EAAeX,OAAOY,OAGtBC,EAAO,aAGPlB,EAAI,SAASmB,GACf,OAAIA,aAAenB,EAAUmB,EACvBrB,gBAAgBE,OACtBF,KAAKsB,SAAWD,GADiB,IAAInB,EAAEmB,IASnB,oBAAXE,SAA2BA,QAAQC,SAM5C3B,EAAKK,EAAIA,GALY,oBAAVuB,SAA0BA,OAAOD,UAAYC,OAAOF,UAC7DA,QAAUE,OAAOF,QAAUrB,GAE7BqB,QAAQrB,EAAIA,GAMdA,EAAEwB,QAAU,QAKZ,IAmBIC,EAnBAC,EAAa,SAASC,EAAMC,EAASC,GACvC,QAAgB,IAAZD,EAAoB,OAAOD,EAC/B,OAAoB,MAAZE,EAAmB,EAAIA,GAC7B,KAAK,EAAG,OAAO,SAASC,GACtB,OAAOH,EAAKI,KAAKH,EAASE,IAG5B,KAAK,EAAG,OAAO,SAASA,EAAOE,EAAOC,GACpC,OAAON,EAAKI,KAAKH,EAASE,EAAOE,EAAOC,IAE1C,KAAK,EAAG,OAAO,SAASC,EAAaJ,EAAOE,EAAOC,GACjD,OAAON,EAAKI,KAAKH,EAASM,EAAaJ,EAAOE,EAAOC,IAGzD,OAAO,WACL,OAAON,EAAKQ,MAAMP,EAASQ,aAS3BC,EAAK,SAASP,EAAOF,EAASC,GAChC,OAAI7B,EAAEsC,WAAab,EAAwBzB,EAAEsC,SAASR,EAAOF,GAChD,MAATE,EAAsB9B,EAAEuC,SACxBvC,EAAEwC,WAAWV,GAAeJ,EAAWI,EAAOF,EAASC,GACvD7B,EAAEyC,SAASX,KAAW9B,EAAEa,QAAQiB,GAAe9B,EAAE0C,QAAQZ,GACtD9B,EAAE2C,SAASb,IAMpB9B,EAAEsC,SAAWb,EAAkB,SAASK,EAAOF,GAC7C,OAAOS,EAAGP,EAAOF,EAASgB,EAAAA,IAQ5B,IAAIC,EAAgB,SAASlB,EAAMmB,GAEjC,OADAA,EAA2B,MAAdA,EAAqBnB,EAAKoB,OAAS,GAAKD,EAC9C,WAIL,IAHA,IAAIC,EAASC,KAAKC,IAAIb,UAAUW,OAASD,EAAY,GACjDI,EAAOhD,MAAM6C,GACbf,EAAQ,EACLA,EAAQe,EAAQf,IACrBkB,EAAKlB,GAASI,UAAUJ,EAAQc,GAElC,OAAQA,GACN,KAAK,EAAG,OAAOnB,EAAKI,KAAKjC,KAAMoD,GAC/B,KAAK,EAAG,OAAOvB,EAAKI,KAAKjC,KAAMsC,UAAU,GAAIc,GAC7C,KAAK,EAAG,OAAOvB,EAAKI,KAAKjC,KAAMsC,UAAU,GAAIA,UAAU,GAAIc,GAE7D,IAAIC,EAAOjD,MAAM4C,EAAa,GAC9B,IAAKd,EAAQ,EAAGA,EAAQc,EAAYd,IAClCmB,EAAKnB,GAASI,UAAUJ,GAG1B,OADAmB,EAAKL,GAAcI,EACZvB,EAAKQ,MAAMrC,KAAMqD,KAKxBC,EAAa,SAASjD,GACxB,IAAKH,EAAEyC,SAAStC,GAAY,MAAO,GACnC,GAAIa,EAAc,OAAOA,EAAab,GACtCe,EAAKf,UAAYA,EACjB,IAAIkD,EAAS,IAAInC,EAEjB,OADAA,EAAKf,UAAY,KACVkD,GAGLC,EAAkB,SAASC,GAC7B,OAAO,SAASpC,GACd,OAAc,MAAPA,OAAc,EAASA,EAAIoC,KAIlCC,EAAU,SAASrC,EAAKsC,GAE1B,IADA,IAAIV,EAASU,EAAKV,OACTW,EAAI,EAAGA,EAAIX,EAAQW,IAAK,CAC/B,GAAW,MAAPvC,EAAa,OACjBA,EAAMA,EAAIsC,EAAKC,IAEjB,OAAOX,EAAS5B,OAAM,GAOpBwC,EAAkBX,KAAKY,IAAI,EAAG,IAAM,EACpCC,EAAYP,EAAgB,UAC5BQ,EAAc,SAAS7B,GACzB,IAAIc,EAASc,EAAU5B,GACvB,MAAwB,iBAAVc,GAAgC,GAAVA,GAAeA,GAAUY,GAS/D3D,EAAE+D,KAAO/D,EAAEgE,QAAU,SAAS7C,EAAKmB,EAAUV,GAE3C,IAAI8B,EAAGX,EACP,GAFAT,EAAWZ,EAAWY,EAAUV,GAE5BkC,EAAY3C,GACd,IAAKuC,EAAI,EAAGX,EAAS5B,EAAI4B,OAAQW,EAAIX,EAAQW,IAC3CpB,EAASnB,EAAIuC,GAAIA,EAAGvC,OAEjB,CACL,IAAIJ,EAAOf,EAAEe,KAAKI,GAClB,IAAKuC,EAAI,EAAGX,EAAShC,EAAKgC,OAAQW,EAAIX,EAAQW,IAC5CpB,EAASnB,EAAIJ,EAAK2C,IAAK3C,EAAK2C,GAAIvC,GAGpC,OAAOA,GAITnB,EAAEiE,IAAMjE,EAAEkE,QAAU,SAAS/C,EAAKmB,EAAUV,GAC1CU,EAAWD,EAAGC,EAAUV,GAIxB,IAHA,IAAIb,GAAQ+C,EAAY3C,IAAQnB,EAAEe,KAAKI,GACnC4B,GAAUhC,GAAQI,GAAK4B,OACvBoB,EAAUjE,MAAM6C,GACXf,EAAQ,EAAGA,EAAQe,EAAQf,IAAS,CAC3C,IAAIoC,EAAarD,EAAOA,EAAKiB,GAASA,EACtCmC,EAAQnC,GAASM,EAASnB,EAAIiD,GAAaA,EAAYjD,GAEzD,OAAOgD,GAIT,IAAIE,EAAe,SAASC,GAkB1B,OAAO,SAASnD,EAAKmB,EAAUiC,EAAM3C,GACnC,IAAI4C,EAA8B,GAApBpC,UAAUW,OACxB,OAjBY,SAAS5B,EAAKmB,EAAUiC,EAAMC,GAC1C,IAAIzD,GAAQ+C,EAAY3C,IAAQnB,EAAEe,KAAKI,GACnC4B,GAAUhC,GAAQI,GAAK4B,OACvBf,EAAc,EAANsC,EAAU,EAAIvB,EAAS,EAKnC,IAJKyB,IACHD,EAAOpD,EAAIJ,EAAOA,EAAKiB,GAASA,GAChCA,GAASsC,GAEK,GAATtC,GAAcA,EAAQe,EAAQf,GAASsC,EAAK,CACjD,IAAIF,EAAarD,EAAOA,EAAKiB,GAASA,EACtCuC,EAAOjC,EAASiC,EAAMpD,EAAIiD,GAAaA,EAAYjD,GAErD,OAAOoD,EAKAE,CAAQtD,EAAKO,EAAWY,EAAUV,EAAS,GAAI2C,EAAMC,KAMhExE,EAAE0E,OAAS1E,EAAE2E,MAAQ3E,EAAE4E,OAASP,EAAa,GAG7CrE,EAAE6E,YAAc7E,EAAE8E,MAAQT,GAAc,GAGxCrE,EAAE+E,KAAO/E,EAAEgF,OAAS,SAAS7D,EAAK8D,EAAWrD,GAC3C,IACI2B,GADYO,EAAY3C,GAAOnB,EAAEkF,UAAYlF,EAAEmF,SAC/BhE,EAAK8D,EAAWrD,GACpC,QAAY,IAAR2B,IAA2B,IAATA,EAAY,OAAOpC,EAAIoC,IAK/CvD,EAAEoF,OAASpF,EAAEqF,OAAS,SAASlE,EAAK8D,EAAWrD,GAC7C,IAAIuC,EAAU,GAKd,OAJAc,EAAY5C,EAAG4C,EAAWrD,GAC1B5B,EAAE+D,KAAK5C,EAAK,SAASW,EAAOE,EAAOsD,GAC7BL,EAAUnD,EAAOE,EAAOsD,IAAOnB,EAAQ3D,KAAKsB,KAE3CqC,GAITnE,EAAEuF,OAAS,SAASpE,EAAK8D,EAAWrD,GAClC,OAAO5B,EAAEoF,OAAOjE,EAAKnB,EAAEwF,OAAOnD,EAAG4C,IAAarD,IAKhD5B,EAAEyF,MAAQzF,EAAE0F,IAAM,SAASvE,EAAK8D,EAAWrD,GACzCqD,EAAY5C,EAAG4C,EAAWrD,GAG1B,IAFA,IAAIb,GAAQ+C,EAAY3C,IAAQnB,EAAEe,KAAKI,GACnC4B,GAAUhC,GAAQI,GAAK4B,OAClBf,EAAQ,EAAGA,EAAQe,EAAQf,IAAS,CAC3C,IAAIoC,EAAarD,EAAOA,EAAKiB,GAASA,EACtC,IAAKiD,EAAU9D,EAAIiD,GAAaA,EAAYjD,GAAM,OAAO,EAE3D,OAAO,GAKTnB,EAAE2F,KAAO3F,EAAE4F,IAAM,SAASzE,EAAK8D,EAAWrD,GACxCqD,EAAY5C,EAAG4C,EAAWrD,GAG1B,IAFA,IAAIb,GAAQ+C,EAAY3C,IAAQnB,EAAEe,KAAKI,GACnC4B,GAAUhC,GAAQI,GAAK4B,OAClBf,EAAQ,EAAGA,EAAQe,EAAQf,IAAS,CAC3C,IAAIoC,EAAarD,EAAOA,EAAKiB,GAASA,EACtC,GAAIiD,EAAU9D,EAAIiD,GAAaA,EAAYjD,GAAM,OAAO,EAE1D,OAAO,GAKTnB,EAAE6F,SAAW7F,EAAE8F,SAAW9F,EAAE+F,QAAU,SAAS5E,EAAK6E,EAAMC,EAAWC,GAGnE,OAFKpC,EAAY3C,KAAMA,EAAMnB,EAAEmG,OAAOhF,KACd,iBAAb8E,GAAyBC,KAAOD,EAAY,GACb,GAAnCjG,EAAEoG,QAAQjF,EAAK6E,EAAMC,IAI9BjG,EAAEqG,OAASxD,EAAc,SAAS1B,EAAKsC,EAAMN,GAC3C,IAAImD,EAAa3E,EAOjB,OANI3B,EAAEwC,WAAWiB,GACf9B,EAAO8B,EACEzD,EAAEa,QAAQ4C,KACnB6C,EAAc7C,EAAKhD,MAAM,GAAI,GAC7BgD,EAAOA,EAAKA,EAAKV,OAAS,IAErB/C,EAAEiE,IAAI9C,EAAK,SAASS,GACzB,IAAI2E,EAAS5E,EACb,IAAK4E,EAAQ,CAIX,GAHID,GAAeA,EAAYvD,SAC7BnB,EAAU4B,EAAQ5B,EAAS0E,IAEd,MAAX1E,EAAiB,OACrB2E,EAAS3E,EAAQ6B,GAEnB,OAAiB,MAAV8C,EAAiBA,EAASA,EAAOpE,MAAMP,EAASuB,OAK3DnD,EAAEwG,MAAQ,SAASrF,EAAKoC,GACtB,OAAOvD,EAAEiE,IAAI9C,EAAKnB,EAAE2C,SAASY,KAK/BvD,EAAEyG,MAAQ,SAAStF,EAAKuF,GACtB,OAAO1G,EAAEoF,OAAOjE,EAAKnB,EAAE0C,QAAQgE,KAKjC1G,EAAE2G,UAAY,SAASxF,EAAKuF,GAC1B,OAAO1G,EAAE+E,KAAK5D,EAAKnB,EAAE0C,QAAQgE,KAI/B1G,EAAEiD,IAAM,SAAS9B,EAAKmB,EAAUV,GAC9B,IACIE,EAAO8E,EADPvD,GAAUT,EAAAA,EAAUiE,GAAgBjE,EAAAA,EAExC,GAAgB,MAAZN,GAAuC,iBAAZA,GAAyC,iBAAVnB,EAAI,IAAyB,MAAPA,EAElF,IAAK,IAAIuC,EAAI,EAAGX,GADhB5B,EAAM2C,EAAY3C,GAAOA,EAAMnB,EAAEmG,OAAOhF,IACX4B,OAAQW,EAAIX,EAAQW,IAElC,OADb5B,EAAQX,EAAIuC,KACiBL,EAARvB,IACnBuB,EAASvB,QAIbQ,EAAWD,EAAGC,EAAUV,GACxB5B,EAAE+D,KAAK5C,EAAK,SAAS2F,EAAG9E,EAAOsD,GAC7BsB,EAAWtE,EAASwE,EAAG9E,EAAOsD,IACfuB,EAAXD,GAA2BA,KAAchE,EAAAA,GAAYS,KAAYT,EAAAA,KACnES,EAASyD,EACTD,EAAeD,KAIrB,OAAOvD,GAITrD,EAAE+G,IAAM,SAAS5F,EAAKmB,EAAUV,GAC9B,IACIE,EAAO8E,EADPvD,EAAST,EAAAA,EAAUiE,EAAejE,EAAAA,EAEtC,GAAgB,MAAZN,GAAuC,iBAAZA,GAAyC,iBAAVnB,EAAI,IAAyB,MAAPA,EAElF,IAAK,IAAIuC,EAAI,EAAGX,GADhB5B,EAAM2C,EAAY3C,GAAOA,EAAMnB,EAAEmG,OAAOhF,IACX4B,OAAQW,EAAIX,EAAQW,IAElC,OADb5B,EAAQX,EAAIuC,KACS5B,EAAQuB,IAC3BA,EAASvB,QAIbQ,EAAWD,EAAGC,EAAUV,GACxB5B,EAAE+D,KAAK5C,EAAK,SAAS2F,EAAG9E,EAAOsD,KAC7BsB,EAAWtE,EAASwE,EAAG9E,EAAOsD,IACfuB,GAAgBD,IAAahE,EAAAA,GAAYS,IAAWT,EAAAA,KACjES,EAASyD,EACTD,EAAeD,KAIrB,OAAOvD,GAITrD,EAAEgH,QAAU,SAAS7F,GACnB,OAAOnB,EAAEiH,OAAO9F,EAAKyB,EAAAA,IAOvB5C,EAAEiH,OAAS,SAAS9F,EAAK+F,EAAGhB,GAC1B,GAAS,MAALgB,GAAahB,EAEf,OADKpC,EAAY3C,KAAMA,EAAMnB,EAAEmG,OAAOhF,IAC/BA,EAAInB,EAAEmH,OAAOhG,EAAI4B,OAAS,IAEnC,IAAIkE,EAASnD,EAAY3C,GAAOnB,EAAEoH,MAAMjG,GAAOnB,EAAEmG,OAAOhF,GACpD4B,EAASc,EAAUoD,GACvBC,EAAIlE,KAAKC,IAAID,KAAK+D,IAAIG,EAAGnE,GAAS,GAElC,IADA,IAAIsE,EAAOtE,EAAS,EACXf,EAAQ,EAAGA,EAAQkF,EAAGlF,IAAS,CACtC,IAAIsF,EAAOtH,EAAEmH,OAAOnF,EAAOqF,GACvBE,EAAON,EAAOjF,GAClBiF,EAAOjF,GAASiF,EAAOK,GACvBL,EAAOK,GAAQC,EAEjB,OAAON,EAAOxG,MAAM,EAAGyG,IAIzBlH,EAAEwH,OAAS,SAASrG,EAAKmB,EAAUV,GACjC,IAAII,EAAQ,EAEZ,OADAM,EAAWD,EAAGC,EAAUV,GACjB5B,EAAEwG,MAAMxG,EAAEiE,IAAI9C,EAAK,SAASW,EAAOyB,EAAK+B,GAC7C,MAAO,CACLxD,MAAOA,EACPE,MAAOA,IACPyF,SAAUnF,EAASR,EAAOyB,EAAK+B,MAEhCoC,KAAK,SAASC,EAAMC,GACrB,IAAIC,EAAIF,EAAKF,SACTK,EAAIF,EAAMH,SACd,GAAII,IAAMC,EAAG,CACX,GAAQA,EAAJD,QAAe,IAANA,EAAc,OAAO,EAClC,GAAIA,EAAIC,QAAW,IAANA,EAAc,OAAQ,EAErC,OAAOH,EAAK3F,MAAQ4F,EAAM5F,QACxB,UAIN,IAAI+F,EAAQ,SAASC,EAAUC,GAC7B,OAAO,SAAS9G,EAAKmB,EAAUV,GAC7B,IAAIyB,EAAS4E,EAAY,CAAC,GAAI,IAAM,GAMpC,OALA3F,EAAWD,EAAGC,EAAUV,GACxB5B,EAAE+D,KAAK5C,EAAK,SAASW,EAAOE,GAC1B,IAAIuB,EAAMjB,EAASR,EAAOE,EAAOb,GACjC6G,EAAS3E,EAAQvB,EAAOyB,KAEnBF,IAMXrD,EAAEkI,QAAUH,EAAM,SAAS1E,EAAQvB,EAAOyB,GACpCvD,EAAEmI,IAAI9E,EAAQE,GAAMF,EAAOE,GAAK/C,KAAKsB,GAAauB,EAAOE,GAAO,CAACzB,KAKvE9B,EAAEoI,QAAUL,EAAM,SAAS1E,EAAQvB,EAAOyB,GACxCF,EAAOE,GAAOzB,IAMhB9B,EAAEqI,QAAUN,EAAM,SAAS1E,EAAQvB,EAAOyB,GACpCvD,EAAEmI,IAAI9E,EAAQE,GAAMF,EAAOE,KAAaF,EAAOE,GAAO,IAG5D,IAAI+E,EAAc,mEAElBtI,EAAEuI,QAAU,SAASpH,GACnB,OAAKA,EACDnB,EAAEa,QAAQM,GAAaV,EAAMsB,KAAKZ,GAClCnB,EAAEwI,SAASrH,GAENA,EAAIsH,MAAMH,GAEfxE,EAAY3C,GAAanB,EAAEiE,IAAI9C,EAAKnB,EAAEuC,UACnCvC,EAAEmG,OAAOhF,GAPC,IAWnBnB,EAAE0I,KAAO,SAASvH,GAChB,OAAW,MAAPA,EAAoB,EACjB2C,EAAY3C,GAAOA,EAAI4B,OAAS/C,EAAEe,KAAKI,GAAK4B,QAKrD/C,EAAEiI,UAAYF,EAAM,SAAS1E,EAAQvB,EAAO6G,GAC1CtF,EAAOsF,EAAO,EAAI,GAAGnI,KAAKsB,KACzB,GAQH9B,EAAE4I,MAAQ5I,EAAE6I,KAAO7I,EAAE8I,KAAO,SAASC,EAAO7B,EAAGhB,GAC7C,KAAa,MAAT6C,GAAiBA,EAAMhG,OAAS,GACpC,OAAS,MAALmE,GAAahB,EAAc6C,EAAM,GAC9B/I,EAAEwE,QAAQuE,EAAOA,EAAMhG,OAASmE,IAMzClH,EAAEwE,QAAU,SAASuE,EAAO7B,EAAGhB,GAC7B,OAAOzF,EAAMsB,KAAKgH,EAAO,EAAG/F,KAAKC,IAAI,EAAG8F,EAAMhG,QAAe,MAALmE,GAAahB,EAAQ,EAAIgB,MAKnFlH,EAAEqH,KAAO,SAAS0B,EAAO7B,EAAGhB,GAC1B,KAAa,MAAT6C,GAAiBA,EAAMhG,OAAS,GACpC,OAAS,MAALmE,GAAahB,EAAc6C,EAAMA,EAAMhG,OAAS,GAC7C/C,EAAEkD,KAAK6F,EAAO/F,KAAKC,IAAI,EAAG8F,EAAMhG,OAASmE,KAMlDlH,EAAEkD,KAAOlD,EAAEgJ,KAAOhJ,EAAEiJ,KAAO,SAASF,EAAO7B,EAAGhB,GAC5C,OAAOzF,EAAMsB,KAAKgH,EAAY,MAAL7B,GAAahB,EAAQ,EAAIgB,IAIpDlH,EAAEkJ,QAAU,SAASH,GACnB,OAAO/I,EAAEoF,OAAO2D,EAAOI,UAIzB,IAAIC,EAAU,SAASC,EAAOC,EAASC,EAAQC,GAG7C,IADA,IAAIC,GADJD,EAASA,GAAU,IACFzG,OACRW,EAAI,EAAGX,EAASc,EAAUwF,GAAQ3F,EAAIX,EAAQW,IAAK,CAC1D,IAAI5B,EAAQuH,EAAM3F,GAClB,GAAII,EAAYhC,KAAW9B,EAAEa,QAAQiB,IAAU9B,EAAE0J,YAAY5H,IAE3D,GAAIwH,EAEF,IADA,IAAIK,EAAI,EAAGC,EAAM9H,EAAMiB,OAChB4G,EAAIC,GAAKJ,EAAOC,KAAS3H,EAAM6H,UAEtCP,EAAQtH,EAAOwH,EAASC,EAAQC,GAChCC,EAAMD,EAAOzG,YAELwG,IACVC,EAAOC,KAAS3H,GAGpB,OAAO0H,GAITxJ,EAAEoJ,QAAU,SAASL,EAAOO,GAC1B,OAAOF,EAAQL,EAAOO,GAAS,IAIjCtJ,EAAE6J,QAAUhH,EAAc,SAASkG,EAAOe,GACxC,OAAO9J,EAAE+J,WAAWhB,EAAOe,KAS7B9J,EAAEgK,KAAOhK,EAAEiK,OAAS,SAASlB,EAAOmB,EAAU5H,EAAUV,GACjD5B,EAAEmK,UAAUD,KACftI,EAAUU,EACVA,EAAW4H,EACXA,GAAW,GAEG,MAAZ5H,IAAkBA,EAAWD,EAAGC,EAAUV,IAG9C,IAFA,IAAIyB,EAAS,GACT+G,EAAO,GACF1G,EAAI,EAAGX,EAASc,EAAUkF,GAAQrF,EAAIX,EAAQW,IAAK,CAC1D,IAAI5B,EAAQiH,EAAMrF,GACdkD,EAAWtE,EAAWA,EAASR,EAAO4B,EAAGqF,GAASjH,EAClDoI,IAAa5H,GACVoB,GAAK0G,IAASxD,GAAUvD,EAAO7C,KAAKsB,GACzCsI,EAAOxD,GACEtE,EACJtC,EAAE6F,SAASuE,EAAMxD,KACpBwD,EAAK5J,KAAKoG,GACVvD,EAAO7C,KAAKsB,IAEJ9B,EAAE6F,SAASxC,EAAQvB,IAC7BuB,EAAO7C,KAAKsB,GAGhB,OAAOuB,GAKTrD,EAAEqK,MAAQxH,EAAc,SAASyH,GAC/B,OAAOtK,EAAEgK,KAAKZ,EAAQkB,GAAQ,GAAM,MAKtCtK,EAAEuK,aAAe,SAASxB,GAGxB,IAFA,IAAI1F,EAAS,GACTmH,EAAapI,UAAUW,OAClBW,EAAI,EAAGX,EAASc,EAAUkF,GAAQrF,EAAIX,EAAQW,IAAK,CAC1D,IAAIsC,EAAO+C,EAAMrF,GACjB,IAAI1D,EAAE6F,SAASxC,EAAQ2C,GAAvB,CACA,IAAI2D,EACJ,IAAKA,EAAI,EAAGA,EAAIa,GACTxK,EAAE6F,SAASzD,UAAUuH,GAAI3D,GADJ2D,KAGxBA,IAAMa,GAAYnH,EAAO7C,KAAKwF,IAEpC,OAAO3C,GAKTrD,EAAE+J,WAAalH,EAAc,SAASkG,EAAO7F,GAE3C,OADAA,EAAOkG,EAAQlG,GAAM,GAAM,GACpBlD,EAAEoF,OAAO2D,EAAO,SAASjH,GAC9B,OAAQ9B,EAAE6F,SAAS3C,EAAMpB,OAM7B9B,EAAEyK,MAAQ,SAAS1B,GAIjB,IAHA,IAAIhG,EAASgG,GAAS/I,EAAEiD,IAAI8F,EAAOlF,GAAWd,QAAU,EACpDM,EAASnD,MAAM6C,GAEVf,EAAQ,EAAGA,EAAQe,EAAQf,IAClCqB,EAAOrB,GAAShC,EAAEwG,MAAMuC,EAAO/G,GAEjC,OAAOqB,GAKTrD,EAAE0K,IAAM7H,EAAc7C,EAAEyK,OAKxBzK,EAAE2K,OAAS,SAASrF,EAAMa,GAExB,IADA,IAAI9C,EAAS,GACJK,EAAI,EAAGX,EAASc,EAAUyB,GAAO5B,EAAIX,EAAQW,IAChDyC,EACF9C,EAAOiC,EAAK5B,IAAMyC,EAAOzC,GAEzBL,EAAOiC,EAAK5B,GAAG,IAAM4B,EAAK5B,GAAG,GAGjC,OAAOL,GAIT,IAAIuH,EAA6B,SAAStG,GACxC,OAAO,SAASyE,EAAO9D,EAAWrD,GAChCqD,EAAY5C,EAAG4C,EAAWrD,GAG1B,IAFA,IAAImB,EAASc,EAAUkF,GACnB/G,EAAc,EAANsC,EAAU,EAAIvB,EAAS,EACnB,GAATf,GAAcA,EAAQe,EAAQf,GAASsC,EAC5C,GAAIW,EAAU8D,EAAM/G,GAAQA,EAAO+G,GAAQ,OAAO/G,EAEpD,OAAQ,IAKZhC,EAAEkF,UAAY0F,EAA2B,GACzC5K,EAAE6K,cAAgBD,GAA4B,GAI9C5K,EAAE8K,YAAc,SAAS/B,EAAO5H,EAAKmB,EAAUV,GAI7C,IAFA,IAAIE,GADJQ,EAAWD,EAAGC,EAAUV,EAAS,IACZT,GACjB4J,EAAM,EAAGC,EAAOnH,EAAUkF,GACvBgC,EAAMC,GAAM,CACjB,IAAIC,EAAMjI,KAAKkI,OAAOH,EAAMC,GAAQ,GAChC1I,EAASyG,EAAMkC,IAAQnJ,EAAOiJ,EAAME,EAAM,EAAQD,EAAOC,EAE/D,OAAOF,GAIT,IAAII,EAAoB,SAAS7G,EAAK8G,EAAeN,GACnD,OAAO,SAAS/B,EAAO/C,EAAMyD,GAC3B,IAAI/F,EAAI,EAAGX,EAASc,EAAUkF,GAC9B,GAAkB,iBAAPU,EACC,EAANnF,EACFZ,EAAW,GAAP+F,EAAWA,EAAMzG,KAAKC,IAAIwG,EAAM1G,EAAQW,GAE5CX,EAAgB,GAAP0G,EAAWzG,KAAK+D,IAAI0C,EAAM,EAAG1G,GAAU0G,EAAM1G,EAAS,OAE5D,GAAI+H,GAAerB,GAAO1G,EAE/B,OAAOgG,EADPU,EAAMqB,EAAY/B,EAAO/C,MACHA,EAAOyD,GAAO,EAEtC,GAAIzD,GAASA,EAEX,OAAc,IADdyD,EAAM2B,EAAc3K,EAAMsB,KAAKgH,EAAOrF,EAAGX,GAAS/C,EAAEqL,QAClC5B,EAAM/F,GAAK,EAE/B,IAAK+F,EAAY,EAANnF,EAAUZ,EAAIX,EAAS,EAAU,GAAP0G,GAAYA,EAAM1G,EAAQ0G,GAAOnF,EACpE,GAAIyE,EAAMU,KAASzD,EAAM,OAAOyD,EAElC,OAAQ,IAQZzJ,EAAEoG,QAAU+E,EAAkB,EAAGnL,EAAEkF,UAAWlF,EAAE8K,aAChD9K,EAAEsL,YAAcH,GAAmB,EAAGnL,EAAE6K,eAKxC7K,EAAEuL,MAAQ,SAASC,EAAOC,EAAMC,GAClB,MAARD,IACFA,EAAOD,GAAS,EAChBA,EAAQ,GAELE,IACHA,EAAOD,EAAOD,GAAS,EAAI,GAM7B,IAHA,IAAIzI,EAASC,KAAKC,IAAID,KAAK2I,MAAMF,EAAOD,GAASE,GAAO,GACpDH,EAAQrL,MAAM6C,GAET0G,EAAM,EAAGA,EAAM1G,EAAQ0G,IAAO+B,GAASE,EAC9CH,EAAM9B,GAAO+B,EAGf,OAAOD,GAKTvL,EAAE4L,MAAQ,SAAS7C,EAAO8C,GACxB,GAAa,MAATA,GAAiBA,EAAQ,EAAG,MAAO,GAGvC,IAFA,IAAIxI,EAAS,GACTK,EAAI,EAAGX,EAASgG,EAAMhG,OACnBW,EAAIX,GACTM,EAAO7C,KAAKC,EAAMsB,KAAKgH,EAAOrF,EAAGA,GAAKmI,IAExC,OAAOxI,GAQT,IAAIyI,EAAe,SAASC,EAAYC,EAAWpK,EAASqK,EAAgB9I,GAC1E,KAAM8I,aAA0BD,GAAY,OAAOD,EAAW5J,MAAMP,EAASuB,GAC7E,IAAIvD,EAAOwD,EAAW2I,EAAW5L,WAC7BkD,EAAS0I,EAAW5J,MAAMvC,EAAMuD,GACpC,OAAInD,EAAEyC,SAASY,GAAgBA,EACxBzD,GAMTI,EAAEkM,KAAOrJ,EAAc,SAASlB,EAAMC,EAASuB,GAC7C,IAAKnD,EAAEwC,WAAWb,GAAO,MAAM,IAAIwK,UAAU,qCAC7C,IAAIC,EAAQvJ,EAAc,SAASwJ,GACjC,OAAOP,EAAanK,EAAMyK,EAAOxK,EAAS9B,KAAMqD,EAAKmJ,OAAOD,MAE9D,OAAOD,IAOTpM,EAAEuM,QAAU1J,EAAc,SAASlB,EAAM6K,GACvC,IAAIC,EAAczM,EAAEuM,QAAQE,YACxBL,EAAQ,WAGV,IAFA,IAAIM,EAAW,EAAG3J,EAASyJ,EAAUzJ,OACjCI,EAAOjD,MAAM6C,GACRW,EAAI,EAAGA,EAAIX,EAAQW,IAC1BP,EAAKO,GAAK8I,EAAU9I,KAAO+I,EAAcrK,UAAUsK,KAAcF,EAAU9I,GAE7E,KAAOgJ,EAAWtK,UAAUW,QAAQI,EAAK3C,KAAK4B,UAAUsK,MACxD,OAAOZ,EAAanK,EAAMyK,EAAOtM,KAAMA,KAAMqD,IAE/C,OAAOiJ,KAGTpM,EAAEuM,QAAQE,YAAczM,GAKtB2M,QAAU9J,EAAc,SAAS1B,EAAKJ,GAEtC,IAAIiB,GADJjB,EAAOqI,EAAQrI,GAAM,GAAO,IACXgC,OACjB,GAAIf,EAAQ,EAAG,MAAM,IAAI4K,MAAM,yCAC/B,KAAO5K,KAAS,CACd,IAAIuB,EAAMxC,EAAKiB,GACfb,EAAIoC,GAAOvD,EAAEkM,KAAK/K,EAAIoC,GAAMpC,MAKhCnB,EAAE6M,QAAU,SAASlL,EAAMmL,GACzB,IAAID,EAAU,SAAStJ,GACrB,IAAIwJ,EAAQF,EAAQE,MAChBC,EAAU,IAAMF,EAASA,EAAO3K,MAAMrC,KAAMsC,WAAamB,GAE7D,OADKvD,EAAEmI,IAAI4E,EAAOC,KAAUD,EAAMC,GAAWrL,EAAKQ,MAAMrC,KAAMsC,YACvD2K,EAAMC,IAGf,OADAH,EAAQE,MAAQ,GACTF,GAKT7M,EAAEiN,MAAQpK,EAAc,SAASlB,EAAMuL,EAAM/J,GAC3C,OAAOgK,WAAW,WAChB,OAAOxL,EAAKQ,MAAM,KAAMgB,IACvB+J,KAKLlN,EAAEoN,MAAQpN,EAAEuM,QAAQvM,EAAEiN,MAAOjN,EAAG,GAOhCA,EAAEqN,SAAW,SAAS1L,EAAMuL,EAAMI,GAChC,IAAIC,EAAS3L,EAASuB,EAAME,EACxBmK,EAAW,EACVF,IAASA,EAAU,IAExB,IAAIG,EAAQ,WACVD,GAA+B,IAApBF,EAAQI,QAAoB,EAAI1N,EAAE2N,MAC7CJ,EAAU,KACVlK,EAAS1B,EAAKQ,MAAMP,EAASuB,GACxBoK,IAAS3L,EAAUuB,EAAO,OAG7ByK,EAAY,WACd,IAAID,EAAM3N,EAAE2N,MACPH,IAAgC,IAApBF,EAAQI,UAAmBF,EAAWG,GACvD,IAAIE,EAAYX,GAAQS,EAAMH,GAc9B,OAbA5L,EAAU9B,KACVqD,EAAOf,UACHyL,GAAa,GAAiBX,EAAZW,GAChBN,IACFO,aAAaP,GACbA,EAAU,MAEZC,EAAWG,EACXtK,EAAS1B,EAAKQ,MAAMP,EAASuB,GACxBoK,IAAS3L,EAAUuB,EAAO,OACrBoK,IAAgC,IAArBD,EAAQS,WAC7BR,EAAUJ,WAAWM,EAAOI,IAEvBxK,GAST,OANAuK,EAAUI,OAAS,WACjBF,aAAaP,GACbC,EAAW,EACXD,EAAU3L,EAAUuB,EAAO,MAGtByK,GAOT5N,EAAEiO,SAAW,SAAStM,EAAMuL,EAAMgB,GAChC,IAAIX,EAASlK,EAEToK,EAAQ,SAAS7L,EAASuB,GAC5BoK,EAAU,KACNpK,IAAME,EAAS1B,EAAKQ,MAAMP,EAASuB,KAGrCgL,EAAYtL,EAAc,SAASM,GAErC,GADIoK,GAASO,aAAaP,GACtBW,EAAW,CACb,IAAIE,GAAWb,EACfA,EAAUJ,WAAWM,EAAOP,GACxBkB,IAAS/K,EAAS1B,EAAKQ,MAAMrC,KAAMqD,SAEvCoK,EAAUvN,EAAEiN,MAAMQ,EAAOP,EAAMpN,KAAMqD,GAGvC,OAAOE,IAQT,OALA8K,EAAUH,OAAS,WACjBF,aAAaP,GACbA,EAAU,MAGLY,GAMTnO,EAAEqO,KAAO,SAAS1M,EAAM2M,GACtB,OAAOtO,EAAEuM,QAAQ+B,EAAS3M,IAI5B3B,EAAEwF,OAAS,SAASP,GAClB,OAAO,WACL,OAAQA,EAAU9C,MAAMrC,KAAMsC,aAMlCpC,EAAEuO,QAAU,WACV,IAAIpL,EAAOf,UACPoJ,EAAQrI,EAAKJ,OAAS,EAC1B,OAAO,WAGL,IAFA,IAAIW,EAAI8H,EACJnI,EAASF,EAAKqI,GAAOrJ,MAAMrC,KAAMsC,WAC9BsB,KAAKL,EAASF,EAAKO,GAAG3B,KAAKjC,KAAMuD,GACxC,OAAOA,IAKXrD,EAAEwO,MAAQ,SAASC,EAAO9M,GACxB,OAAO,WACL,KAAM8M,EAAQ,EACZ,OAAO9M,EAAKQ,MAAMrC,KAAMsC,aAM9BpC,EAAE0O,OAAS,SAASD,EAAO9M,GACzB,IAAI4C,EACJ,OAAO,WAKL,OAJc,IAARkK,IACJlK,EAAO5C,EAAKQ,MAAMrC,KAAMsC,YAEtBqM,GAAS,IAAG9M,EAAO,MAChB4C,IAMXvE,EAAE2O,KAAO3O,EAAEuM,QAAQvM,EAAE0O,OAAQ,GAE7B1O,EAAE6C,cAAgBA,EAMlB,IAAI+L,GAAc,CAAClO,SAAU,MAAMmO,qBAAqB,YACpDC,EAAqB,CAAC,UAAW,gBAAiB,WACpD,uBAAwB,iBAAkB,kBAExCC,EAAsB,SAAS5N,EAAKJ,GACtC,IAAIiO,EAAaF,EAAmB/L,OAChCkM,EAAc9N,EAAI8N,YAClBC,EAAQlP,EAAEwC,WAAWyM,IAAgBA,EAAY9O,WAAaC,EAG9D+O,EAAO,cAGX,IAFInP,EAAEmI,IAAIhH,EAAKgO,KAAUnP,EAAE6F,SAAS9E,EAAMoO,IAAOpO,EAAKP,KAAK2O,GAEpDH,MACLG,EAAOL,EAAmBE,MACd7N,GAAOA,EAAIgO,KAAUD,EAAMC,KAAUnP,EAAE6F,SAAS9E,EAAMoO,IAChEpO,EAAKP,KAAK2O,IAOhBnP,EAAEe,KAAO,SAASI,GAChB,IAAKnB,EAAEyC,SAAStB,GAAM,MAAO,GAC7B,GAAIL,EAAY,OAAOA,EAAWK,GAClC,IAAIJ,EAAO,GACX,IAAK,IAAIwC,KAAOpC,EAASnB,EAAEmI,IAAIhH,EAAKoC,IAAMxC,EAAKP,KAAK+C,GAGpD,OADIqL,GAAYG,EAAoB5N,EAAKJ,GAClCA,GAITf,EAAEoP,QAAU,SAASjO,GACnB,IAAKnB,EAAEyC,SAAStB,GAAM,MAAO,GAC7B,IAAIJ,EAAO,GACX,IAAK,IAAIwC,KAAOpC,EAAKJ,EAAKP,KAAK+C,GAG/B,OADIqL,GAAYG,EAAoB5N,EAAKJ,GAClCA,GAITf,EAAEmG,OAAS,SAAShF,GAIlB,IAHA,IAAIJ,EAAOf,EAAEe,KAAKI,GACd4B,EAAShC,EAAKgC,OACdoD,EAASjG,MAAM6C,GACVW,EAAI,EAAGA,EAAIX,EAAQW,IAC1ByC,EAAOzC,GAAKvC,EAAIJ,EAAK2C,IAEvB,OAAOyC,GAKTnG,EAAEqP,UAAY,SAASlO,EAAKmB,EAAUV,GACpCU,EAAWD,EAAGC,EAAUV,GAIxB,IAHA,IAAIb,EAAOf,EAAEe,KAAKI,GACd4B,EAAShC,EAAKgC,OACdoB,EAAU,GACLnC,EAAQ,EAAGA,EAAQe,EAAQf,IAAS,CAC3C,IAAIoC,EAAarD,EAAKiB,GACtBmC,EAAQC,GAAc9B,EAASnB,EAAIiD,GAAaA,EAAYjD,GAE9D,OAAOgD,GAKTnE,EAAEsP,MAAQ,SAASnO,GAIjB,IAHA,IAAIJ,EAAOf,EAAEe,KAAKI,GACd4B,EAAShC,EAAKgC,OACduM,EAAQpP,MAAM6C,GACTW,EAAI,EAAGA,EAAIX,EAAQW,IAC1B4L,EAAM5L,GAAK,CAAC3C,EAAK2C,GAAIvC,EAAIJ,EAAK2C,KAEhC,OAAO4L,GAITtP,EAAEuP,OAAS,SAASpO,GAGlB,IAFA,IAAIkC,EAAS,GACTtC,EAAOf,EAAEe,KAAKI,GACTuC,EAAI,EAAGX,EAAShC,EAAKgC,OAAQW,EAAIX,EAAQW,IAChDL,EAAOlC,EAAIJ,EAAK2C,KAAO3C,EAAK2C,GAE9B,OAAOL,GAKTrD,EAAEwP,UAAYxP,EAAEyP,QAAU,SAAStO,GACjC,IAAIuO,EAAQ,GACZ,IAAK,IAAInM,KAAOpC,EACVnB,EAAEwC,WAAWrB,EAAIoC,KAAOmM,EAAMlP,KAAK+C,GAEzC,OAAOmM,EAAMhI,QAIf,IAAIiI,EAAiB,SAASC,EAAUC,GACtC,OAAO,SAAS1O,GACd,IAAI4B,EAASX,UAAUW,OAEvB,GADI8M,IAAU1O,EAAMd,OAAOc,IACvB4B,EAAS,GAAY,MAAP5B,EAAa,OAAOA,EACtC,IAAK,IAAIa,EAAQ,EAAGA,EAAQe,EAAQf,IAIlC,IAHA,IAAI8N,EAAS1N,UAAUJ,GACnBjB,EAAO6O,EAASE,GAChBC,EAAIhP,EAAKgC,OACJW,EAAI,EAAGA,EAAIqM,EAAGrM,IAAK,CAC1B,IAAIH,EAAMxC,EAAK2C,GACVmM,QAAyB,IAAb1O,EAAIoC,KAAiBpC,EAAIoC,GAAOuM,EAAOvM,IAG5D,OAAOpC,IAKXnB,EAAEgQ,OAASL,EAAe3P,EAAEoP,SAI5BpP,EAAEiQ,UAAYjQ,EAAEkQ,OAASP,EAAe3P,EAAEe,MAG1Cf,EAAEmF,QAAU,SAAShE,EAAK8D,EAAWrD,GACnCqD,EAAY5C,EAAG4C,EAAWrD,GAE1B,IADA,IAAwB2B,EAApBxC,EAAOf,EAAEe,KAAKI,GACTuC,EAAI,EAAGX,EAAShC,EAAKgC,OAAQW,EAAIX,EAAQW,IAEhD,GAAIuB,EAAU9D,EADdoC,EAAMxC,EAAK2C,IACaH,EAAKpC,GAAM,OAAOoC,GAK9C,IA+EI4M,EAAIC,EA/EJC,EAAW,SAASvO,EAAOyB,EAAKpC,GAClC,OAAOoC,KAAOpC,GAIhBnB,EAAEsQ,KAAOzN,EAAc,SAAS1B,EAAKJ,GACnC,IAAIsC,EAAS,GAAIf,EAAWvB,EAAK,GACjC,GAAW,MAAPI,EAAa,OAAOkC,EACpBrD,EAAEwC,WAAWF,IACG,EAAdvB,EAAKgC,SAAYT,EAAWZ,EAAWY,EAAUvB,EAAK,KAC1DA,EAAOf,EAAEoP,QAAQjO,KAEjBmB,EAAW+N,EACXtP,EAAOqI,EAAQrI,GAAM,GAAO,GAC5BI,EAAMd,OAAOc,IAEf,IAAK,IAAIuC,EAAI,EAAGX,EAAShC,EAAKgC,OAAQW,EAAIX,EAAQW,IAAK,CACrD,IAAIH,EAAMxC,EAAK2C,GACX5B,EAAQX,EAAIoC,GACZjB,EAASR,EAAOyB,EAAKpC,KAAMkC,EAAOE,GAAOzB,GAE/C,OAAOuB,IAITrD,EAAEuQ,KAAO1N,EAAc,SAAS1B,EAAKJ,GACnC,IAAwBa,EAApBU,EAAWvB,EAAK,GAUpB,OATIf,EAAEwC,WAAWF,IACfA,EAAWtC,EAAEwF,OAAOlD,GACF,EAAdvB,EAAKgC,SAAYnB,EAAUb,EAAK,MAEpCA,EAAOf,EAAEiE,IAAImF,EAAQrI,GAAM,GAAO,GAAQyP,QAC1ClO,EAAW,SAASR,EAAOyB,GACzB,OAAQvD,EAAE6F,SAAS9E,EAAMwC,KAGtBvD,EAAEsQ,KAAKnP,EAAKmB,EAAUV,KAI/B5B,EAAE6P,SAAWF,EAAe3P,EAAEoP,SAAS,GAKvCpP,EAAEiB,OAAS,SAASd,EAAWsQ,GAC7B,IAAIpN,EAASD,EAAWjD,GAExB,OADIsQ,GAAOzQ,EAAEiQ,UAAU5M,EAAQoN,GACxBpN,GAITrD,EAAEoH,MAAQ,SAASjG,GACjB,OAAKnB,EAAEyC,SAAStB,GACTnB,EAAEa,QAAQM,GAAOA,EAAIV,QAAUT,EAAEgQ,OAAO,GAAI7O,GADtBA,GAO/BnB,EAAE0Q,IAAM,SAASvP,EAAKwP,GAEpB,OADAA,EAAYxP,GACLA,GAITnB,EAAE4Q,QAAU,SAASjG,EAAQjE,GAC3B,IAAI3F,EAAOf,EAAEe,KAAK2F,GAAQ3D,EAAShC,EAAKgC,OACxC,GAAc,MAAV4H,EAAgB,OAAQ5H,EAE5B,IADA,IAAI5B,EAAMd,OAAOsK,GACRjH,EAAI,EAAGA,EAAIX,EAAQW,IAAK,CAC/B,IAAIH,EAAMxC,EAAK2C,GACf,GAAIgD,EAAMnD,KAASpC,EAAIoC,MAAUA,KAAOpC,GAAM,OAAO,EAEvD,OAAO,GAMTgP,EAAK,SAAStI,EAAGC,EAAG+I,EAAQC,GAG1B,GAAIjJ,IAAMC,EAAG,OAAa,IAAND,GAAW,EAAIA,GAAM,EAAIC,EAE7C,GAAS,MAALD,GAAkB,MAALC,EAAW,OAAO,EAEnC,GAAID,GAAMA,EAAG,OAAOC,GAAMA,EAE1B,IAAIiJ,SAAclJ,EAClB,OAAa,aAATkJ,GAAgC,WAATA,GAAiC,iBAALjJ,IAChDsI,EAAOvI,EAAGC,EAAG+I,EAAQC,IAI9BV,EAAS,SAASvI,EAAGC,EAAG+I,EAAQC,GAE1BjJ,aAAa7H,IAAG6H,EAAIA,EAAEzG,UACtB0G,aAAa9H,IAAG8H,EAAIA,EAAE1G,UAE1B,IAAI4P,EAAYtQ,EAASqB,KAAK8F,GAC9B,GAAImJ,IAActQ,EAASqB,KAAK+F,GAAI,OAAO,EAC3C,OAAQkJ,GAEN,IAAK,kBAEL,IAAK,kBAGH,MAAO,GAAKnJ,GAAM,GAAKC,EACzB,IAAK,kBAGH,OAAKD,IAAOA,GAAWC,IAAOA,EAEhB,IAAND,EAAU,GAAKA,GAAM,EAAIC,GAAKD,IAAOC,EAC/C,IAAK,gBACL,IAAK,mBAIH,OAAQD,IAAOC,EACjB,IAAK,kBACH,OAAOxH,EAAY2Q,QAAQlP,KAAK8F,KAAOvH,EAAY2Q,QAAQlP,KAAK+F,GAGpE,IAAIoJ,EAA0B,mBAAdF,EAChB,IAAKE,EAAW,CACd,GAAgB,iBAALrJ,GAA6B,iBAALC,EAAe,OAAO,EAIzD,IAAIqJ,EAAQtJ,EAAEoH,YAAamC,EAAQtJ,EAAEmH,YACrC,GAAIkC,IAAUC,KAAWpR,EAAEwC,WAAW2O,IAAUA,aAAiBA,GACxCnR,EAAEwC,WAAW4O,IAAUA,aAAiBA,IACzC,gBAAiBvJ,GAAK,gBAAiBC,EAC7D,OAAO,EASXgJ,EAASA,GAAU,GAEnB,IADA,IAAI/N,GAFJ8N,EAASA,GAAU,IAEC9N,OACbA,KAGL,GAAI8N,EAAO9N,KAAY8E,EAAG,OAAOiJ,EAAO/N,KAAY+E,EAQtD,GAJA+I,EAAOrQ,KAAKqH,GACZiJ,EAAOtQ,KAAKsH,GAGRoJ,EAAW,CAGb,IADAnO,EAAS8E,EAAE9E,UACI+E,EAAE/E,OAAQ,OAAO,EAEhC,KAAOA,KACL,IAAKoN,EAAGtI,EAAE9E,GAAS+E,EAAE/E,GAAS8N,EAAQC,GAAS,OAAO,MAEnD,CAEL,IAAsBvN,EAAlBxC,EAAOf,EAAEe,KAAK8G,GAGlB,GAFA9E,EAAShC,EAAKgC,OAEV/C,EAAEe,KAAK+G,GAAG/E,SAAWA,EAAQ,OAAO,EACxC,KAAOA,KAGL,GADAQ,EAAMxC,EAAKgC,IACL/C,EAAEmI,IAAIL,EAAGvE,KAAQ4M,EAAGtI,EAAEtE,GAAMuE,EAAEvE,GAAMsN,EAAQC,GAAU,OAAO,EAMvE,OAFAD,EAAOQ,MACPP,EAAOO,OACA,GAITrR,EAAEsR,QAAU,SAASzJ,EAAGC,GACtB,OAAOqI,EAAGtI,EAAGC,IAKf9H,EAAEuR,QAAU,SAASpQ,GACnB,OAAW,MAAPA,IACA2C,EAAY3C,KAASnB,EAAEa,QAAQM,IAAQnB,EAAEwI,SAASrH,IAAQnB,EAAE0J,YAAYvI,IAA6B,IAAfA,EAAI4B,OAChE,IAAvB/C,EAAEe,KAAKI,GAAK4B,SAIrB/C,EAAEwR,UAAY,SAASrQ,GACrB,SAAUA,GAAwB,IAAjBA,EAAIG,WAKvBtB,EAAEa,QAAUD,GAAiB,SAASO,GACpC,MAA8B,mBAAvBT,EAASqB,KAAKZ,IAIvBnB,EAAEyC,SAAW,SAAStB,GACpB,IAAI4P,SAAc5P,EAClB,MAAgB,aAAT4P,GAAgC,WAATA,KAAuB5P,GAIvDnB,EAAE+D,KAAK,CAAC,YAAa,WAAY,SAAU,SAAU,OAAQ,SAAU,QAAS,SAAU,MAAO,UAAW,MAAO,WAAY,SAAS0N,GACtIzR,EAAE,KAAOyR,GAAQ,SAAStQ,GACxB,OAAOT,EAASqB,KAAKZ,KAAS,WAAasQ,EAAO,OAMjDzR,EAAE0J,YAAYtH,aACjBpC,EAAE0J,YAAc,SAASvI,GACvB,OAAOnB,EAAEmI,IAAIhH,EAAK,YAMtB,IAAIuQ,EAAW/R,EAAKgS,UAAYhS,EAAKgS,SAASC,WAC5B,kBAAP,KAAyC,iBAAbC,WAA4C,mBAAZH,IACrE1R,EAAEwC,WAAa,SAASrB,GACtB,MAAqB,mBAAPA,IAAqB,IAKvCnB,EAAE8R,SAAW,SAAS3Q,GACpB,OAAQnB,EAAE+R,SAAS5Q,IAAQ2Q,SAAS3Q,KAASkK,MAAM2G,WAAW7Q,KAIhEnB,EAAEqL,MAAQ,SAASlK,GACjB,OAAOnB,EAAEiS,SAAS9Q,IAAQkK,MAAMlK,IAIlCnB,EAAEmK,UAAY,SAAShJ,GACrB,OAAe,IAARA,IAAwB,IAARA,GAAwC,qBAAvBT,EAASqB,KAAKZ,IAIxDnB,EAAEkS,OAAS,SAAS/Q,GAClB,OAAe,OAARA,GAITnB,EAAEmS,YAAc,SAAShR,GACvB,YAAe,IAARA,GAKTnB,EAAEmI,IAAM,SAAShH,EAAKsC,GACpB,IAAKzD,EAAEa,QAAQ4C,GACb,OAAc,MAAPtC,GAAeR,EAAeoB,KAAKZ,EAAKsC,GAGjD,IADA,IAAIV,EAASU,EAAKV,OACTW,EAAI,EAAGA,EAAIX,EAAQW,IAAK,CAC/B,IAAIH,EAAME,EAAKC,GACf,GAAW,MAAPvC,IAAgBR,EAAeoB,KAAKZ,EAAKoC,GAC3C,OAAO,EAETpC,EAAMA,EAAIoC,GAEZ,QAASR,GAQX/C,EAAEoS,WAAa,WAEb,OADAzS,EAAKK,EAAID,EACFD,MAITE,EAAEuC,SAAW,SAAST,GACpB,OAAOA,GAIT9B,EAAEqS,SAAW,SAASvQ,GACpB,OAAO,WACL,OAAOA,IAIX9B,EAAEsS,KAAO,aAITtS,EAAE2C,SAAW,SAASc,GACpB,OAAKzD,EAAEa,QAAQ4C,GAGR,SAAStC,GACd,OAAOqC,EAAQrC,EAAKsC,IAHbH,EAAgBG,IAQ3BzD,EAAEuS,WAAa,SAASpR,GACtB,OAAW,MAAPA,EACK,aAEF,SAASsC,GACd,OAAQzD,EAAEa,QAAQ4C,GAAoBD,EAAQrC,EAAKsC,GAAzBtC,EAAIsC,KAMlCzD,EAAE0C,QAAU1C,EAAEwS,QAAU,SAAS9L,GAE/B,OADAA,EAAQ1G,EAAEiQ,UAAU,GAAIvJ,GACjB,SAASvF,GACd,OAAOnB,EAAE4Q,QAAQzP,EAAKuF,KAK1B1G,EAAEyO,MAAQ,SAASvH,EAAG5E,EAAUV,GAC9B,IAAI6Q,EAAQvS,MAAM8C,KAAKC,IAAI,EAAGiE,IAC9B5E,EAAWZ,EAAWY,EAAUV,EAAS,GACzC,IAAK,IAAI8B,EAAI,EAAGA,EAAIwD,EAAGxD,IAAK+O,EAAM/O,GAAKpB,EAASoB,GAChD,OAAO+O,GAITzS,EAAEmH,OAAS,SAASJ,EAAK9D,GAKvB,OAJW,MAAPA,IACFA,EAAM8D,EACNA,EAAM,GAEDA,EAAM/D,KAAKkI,MAAMlI,KAAKmE,UAAYlE,EAAM8D,EAAM,KAIvD/G,EAAE2N,IAAM+E,KAAK/E,KAAO,WAClB,OAAO,IAAI+E,MAAOC,WAIpB,IAAIC,EAAY,CACdC,IAAK,QACLC,IAAK,OACLC,IAAK,OACLC,IAAK,SACLC,IAAK,SACLC,IAAK,UAEHC,EAAcnT,EAAEuP,OAAOqD,GAGvBQ,EAAgB,SAASnP,GAC3B,IAAIoP,EAAU,SAAS5K,GACrB,OAAOxE,EAAIwE,IAGTqH,EAAS,MAAQ9P,EAAEe,KAAKkD,GAAKqP,KAAK,KAAO,IACzCC,EAAaC,OAAO1D,GACpB2D,EAAgBD,OAAO1D,EAAQ,KACnC,OAAO,SAAS4D,GAEd,OADAA,EAAmB,MAAVA,EAAiB,GAAK,GAAKA,EAC7BH,EAAWI,KAAKD,GAAUA,EAAOE,QAAQH,EAAeJ,GAAWK,IAG9E1T,EAAE6T,OAAST,EAAcR,GACzB5S,EAAE8T,SAAWV,EAAcD,GAK3BnT,EAAEqD,OAAS,SAASlC,EAAKsC,EAAMsQ,GACxB/T,EAAEa,QAAQ4C,KAAOA,EAAO,CAACA,IAC9B,IAAIV,EAASU,EAAKV,OAClB,IAAKA,EACH,OAAO/C,EAAEwC,WAAWuR,GAAYA,EAAShS,KAAKZ,GAAO4S,EAEvD,IAAK,IAAIrQ,EAAI,EAAGA,EAAIX,EAAQW,IAAK,CAC/B,IAAIyL,EAAc,MAAPhO,OAAc,EAASA,EAAIsC,EAAKC,SAC9B,IAATyL,IACFA,EAAO4E,EACPrQ,EAAIX,GAEN5B,EAAMnB,EAAEwC,WAAW2M,GAAQA,EAAKpN,KAAKZ,GAAOgO,EAE9C,OAAOhO,GAKT,IAAI6S,EAAY,EAChBhU,EAAEiU,SAAW,SAASC,GACpB,IAAIC,IAAOH,EAAY,GACvB,OAAOE,EAASA,EAASC,EAAKA,GAKhCnU,EAAEoU,iBAAmB,CACnBC,SAAU,kBACVC,YAAa,mBACbT,OAAQ,oBAMV,IAAIU,EAAU,OAIVC,EAAU,CACZvB,IAAK,IACLwB,KAAM,KACNC,KAAM,IACNC,KAAM,IACNC,SAAU,QACVC,SAAU,SAGRC,EAAe,4BAEfC,EAAa,SAAStM,GACxB,MAAO,KAAO+L,EAAQ/L,IAOxBzI,EAAEgV,SAAW,SAASC,EAAMC,EAAUC,IAC/BD,GAAYC,IAAaD,EAAWC,GACzCD,EAAWlV,EAAE6P,SAAS,GAAIqF,EAAUlV,EAAEoU,kBAGtC,IAiCIgB,EAjCA1S,EAAU8Q,OAAO,EAClB0B,EAASrB,QAAUU,GAASzE,QAC5BoF,EAASZ,aAAeC,GAASzE,QACjCoF,EAASb,UAAYE,GAASzE,QAC/BwD,KAAK,KAAO,KAAM,KAGhBtR,EAAQ,EACR8N,EAAS,SACbmF,EAAKrB,QAAQlR,EAAS,SAAS+F,EAAOoL,EAAQS,EAAaD,EAAUgB,GAanE,OAZAvF,GAAUmF,EAAKxU,MAAMuB,EAAOqT,GAAQzB,QAAQkB,EAAcC,GAC1D/S,EAAQqT,EAAS5M,EAAM1F,OAEnB8Q,EACF/D,GAAU,cAAgB+D,EAAS,iCAC1BS,EACTxE,GAAU,cAAgBwE,EAAc,uBAC/BD,IACTvE,GAAU,OAASuE,EAAW,YAIzB5L,IAETqH,GAAU,OAGLoF,EAASI,WAAUxF,EAAS,mBAAqBA,EAAS,OAE/DA,EAAS,2CACP,oDACAA,EAAS,gBAGX,IACEsF,EAAS,IAAIG,SAASL,EAASI,UAAY,MAAO,IAAKxF,GACvD,MAAO0F,GAEP,MADAA,EAAE1F,OAASA,EACL0F,EAGR,IAAIR,EAAW,SAASS,GACtB,OAAOL,EAAOrT,KAAKjC,KAAM2V,EAAMzV,IAI7B0V,EAAWR,EAASI,UAAY,MAGpC,OAFAN,EAASlF,OAAS,YAAc4F,EAAW,OAAS5F,EAAS,IAEtDkF,GAIThV,EAAE2V,MAAQ,SAASxU,GACjB,IAAIyU,EAAW5V,EAAEmB,GAEjB,OADAyU,EAASC,QAAS,EACXD,GAUT,IAAIE,EAAc,SAASF,EAAUzU,GACnC,OAAOyU,EAASC,OAAS7V,EAAEmB,GAAKwU,QAAUxU,GAI5CnB,EAAE+V,MAAQ,SAAS5U,GASjB,OARAnB,EAAE+D,KAAK/D,EAAEwP,UAAUrO,GAAM,SAASsQ,GAChC,IAAI9P,EAAO3B,EAAEyR,GAAQtQ,EAAIsQ,GACzBzR,EAAEG,UAAUsR,GAAQ,WAClB,IAAItO,EAAO,CAACrD,KAAKsB,UAEjB,OADAZ,EAAK2B,MAAMgB,EAAMf,WACV0T,EAAYhW,KAAM6B,EAAKQ,MAAMnC,EAAGmD,OAGpCnD,GAITA,EAAE+V,MAAM/V,GAGRA,EAAE+D,KAAK,CAAC,MAAO,OAAQ,UAAW,QAAS,OAAQ,SAAU,WAAY,SAAS0N,GAChF,IAAIlL,EAAStG,EAAWwR,GACxBzR,EAAEG,UAAUsR,GAAQ,WAClB,IAAItQ,EAAMrB,KAAKsB,SAGf,OAFAmF,EAAOpE,MAAMhB,EAAKiB,WACJ,UAATqP,GAA6B,WAATA,GAAqC,IAAftQ,EAAI4B,eAAqB5B,EAAI,GACrE2U,EAAYhW,KAAMqB,MAK7BnB,EAAE+D,KAAK,CAAC,SAAU,OAAQ,SAAU,SAAS0N,GAC3C,IAAIlL,EAAStG,EAAWwR,GACxBzR,EAAEG,UAAUsR,GAAQ,WAClB,OAAOqE,EAAYhW,KAAMyG,EAAOpE,MAAMrC,KAAKsB,SAAUgB,eAKzDpC,EAAEG,UAAU2B,MAAQ,WAClB,OAAOhC,KAAKsB,UAKdpB,EAAEG,UAAU8Q,QAAUjR,EAAEG,UAAU6V,OAAShW,EAAEG,UAAU2B,MAEvD9B,EAAEG,UAAUO,SAAW,WACrB,OAAO8P,OAAO1Q,KAAKsB,WAUA,mBAAV6U,QAAwBA,OAAOC,KACxCD,OAAO,aAAc,GAAI,WACvB,OAAOjW,IA/oDb"} \ No newline at end of file diff --git a/underscore-min.map b/underscore-min.map deleted file mode 100644 index cf356bf9a..000000000 --- a/underscore-min.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"underscore-min.js","sources":["underscore.js"],"names":["createReduce","dir","iterator","obj","iteratee","memo","keys","index","length","currentKey","context","optimizeCb","isArrayLike","_","arguments","createPredicateIndexFinder","array","predicate","cb","getLength","createIndexFinder","predicateFind","sortedIndex","item","idx","i","Math","max","min","slice","call","isNaN","collectNonEnumProps","nonEnumIdx","nonEnumerableProps","constructor","proto","isFunction","prototype","ObjProto","prop","has","contains","push","root","this","previousUnderscore","ArrayProto","Array","Object","FuncProto","Function","toString","hasOwnProperty","nativeIsArray","isArray","nativeKeys","nativeBind","bind","nativeCreate","create","Ctor","_wrapped","exports","module","VERSION","func","argCount","value","other","collection","accumulator","apply","identity","isObject","matcher","property","Infinity","createAssigner","keysFunc","undefinedOnly","source","l","key","baseCreate","result","MAX_ARRAY_INDEX","pow","each","forEach","map","collect","results","reduce","foldl","inject","reduceRight","foldr","find","detect","findIndex","findKey","filter","select","list","reject","negate","every","all","some","any","includes","include","fromIndex","guard","values","indexOf","invoke","method","args","isFunc","pluck","where","attrs","findWhere","computed","lastComputed","shuffle","rand","set","shuffled","random","sample","n","sortBy","criteria","sort","left","right","a","b","group","behavior","groupBy","indexBy","countBy","toArray","size","partition","pass","fail","first","head","take","initial","last","rest","tail","drop","compact","flatten","input","shallow","strict","startIndex","output","isArguments","j","len","without","difference","uniq","unique","isSorted","isBoolean","seen","union","intersection","argsLength","zip","unzip","object","findLastIndex","low","high","mid","floor","lastIndexOf","range","start","stop","step","ceil","executeBound","sourceFunc","boundFunc","callingContext","self","TypeError","bound","concat","partial","boundArgs","position","bindAll","Error","memoize","hasher","cache","address","delay","wait","setTimeout","defer","throttle","options","timeout","previous","later","leading","now","remaining","clearTimeout","trailing","debounce","immediate","timestamp","callNow","wrap","wrapper","compose","after","times","before","once","hasEnumBug","propertyIsEnumerable","allKeys","mapObject","pairs","invert","functions","methods","names","extend","extendOwn","assign","pick","oiteratee","omit","String","defaults","props","clone","tap","interceptor","isMatch","eq","aStack","bStack","className","areArrays","aCtor","bCtor","pop","isEqual","isEmpty","isString","isElement","nodeType","type","name","Int8Array","isFinite","parseFloat","isNumber","isNull","isUndefined","noConflict","constant","noop","propertyOf","matches","accum","Date","getTime","escapeMap","&","<",">","\"","'","`","unescapeMap","createEscaper","escaper","match","join","testRegexp","RegExp","replaceRegexp","string","test","replace","escape","unescape","fallback","idCounter","uniqueId","prefix","id","templateSettings","evaluate","interpolate","noMatch","escapes","\\","\r","\n","
","
","escapeChar","template","text","settings","oldSettings","offset","variable","render","e","data","argument","chain","instance","_chain","mixin","valueOf","toJSON","define","amd"],"mappings":";;;;CAKC,WA4KC,QAASA,GAAaC,GAGpB,QAASC,GAASC,EAAKC,EAAUC,EAAMC,EAAMC,EAAOC,GAClD,KAAOD,GAAS,GAAaC,EAARD,EAAgBA,GAASN,EAAK,CACjD,GAAIQ,GAAaH,EAAOA,EAAKC,GAASA,CACtCF,GAAOD,EAASC,EAAMF,EAAIM,GAAaA,EAAYN,GAErD,MAAOE,GAGT,MAAO,UAASF,EAAKC,EAAUC,EAAMK,GACnCN,EAAWO,EAAWP,EAAUM,EAAS,EACzC,IAAIJ,IAAQM,EAAYT,IAAQU,EAAEP,KAAKH,GACnCK,GAAUF,GAAQH,GAAKK,OACvBD,EAAQN,EAAM,EAAI,EAAIO,EAAS,CAMnC,OAJIM,WAAUN,OAAS,IACrBH,EAAOF,EAAIG,EAAOA,EAAKC,GAASA,GAChCA,GAASN,GAEJC,EAASC,EAAKC,EAAUC,EAAMC,EAAMC,EAAOC,IA+ZtD,QAASO,GAA2Bd,GAClC,MAAO,UAASe,EAAOC,EAAWP,GAChCO,EAAYC,EAAGD,EAAWP,EAG1B,KAFA,GAAIF,GAASW,EAAUH,GACnBT,EAAQN,EAAM,EAAI,EAAIO,EAAS,EAC5BD,GAAS,GAAaC,EAARD,EAAgBA,GAASN,EAC5C,GAAIgB,EAAUD,EAAMT,GAAQA,EAAOS,GAAQ,MAAOT,EAEpD,QAAQ,GAsBZ,QAASa,GAAkBnB,EAAKoB,EAAeC,GAC7C,MAAO,UAASN,EAAOO,EAAMC,GAC3B,GAAIC,GAAI,EAAGjB,EAASW,EAAUH,EAC9B,IAAkB,gBAAPQ,GACLvB,EAAM,EACNwB,EAAID,GAAO,EAAIA,EAAME,KAAKC,IAAIH,EAAMhB,EAAQiB,GAE5CjB,EAASgB,GAAO,EAAIE,KAAKE,IAAIJ,EAAM,EAAGhB,GAAUgB,EAAMhB,EAAS,MAE9D,IAAIc,GAAeE,GAAOhB,EAE/B,MADAgB,GAAMF,EAAYN,EAAOO,GAClBP,EAAMQ,KAASD,EAAOC,GAAO,CAEtC,IAAID,IAASA,EAEX,MADAC,GAAMH,EAAcQ,EAAMC,KAAKd,EAAOS,EAAGjB,GAASK,EAAEkB,OAC7CP,GAAO,EAAIA,EAAMC,GAAK,CAE/B,KAAKD,EAAMvB,EAAM,EAAIwB,EAAIjB,EAAS,EAAGgB,GAAO,GAAWhB,EAANgB,EAAcA,GAAOvB,EACpE,GAAIe,EAAMQ,KAASD,EAAM,MAAOC,EAElC,QAAQ,GAqPZ,QAASQ,GAAoB7B,EAAKG,GAChC,GAAI2B,GAAaC,EAAmB1B,OAChC2B,EAAchC,EAAIgC,YAClBC,EAASvB,EAAEwB,WAAWF,IAAgBA,EAAYG,WAAcC,EAGhEC,EAAO,aAGX,KAFI3B,EAAE4B,IAAItC,EAAKqC,KAAU3B,EAAE6B,SAASpC,EAAMkC,IAAOlC,EAAKqC,KAAKH,GAEpDP,KACLO,EAAON,EAAmBD,GACtBO,IAAQrC,IAAOA,EAAIqC,KAAUJ,EAAMI,KAAU3B,EAAE6B,SAASpC,EAAMkC,IAChElC,EAAKqC,KAAKH,GA74BhB,GAAII,GAAOC,KAGPC,EAAqBF,EAAK/B,EAG1BkC,EAAaC,MAAMV,UAAWC,EAAWU,OAAOX,UAAWY,EAAYC,SAASb,UAIlFK,EAAmBI,EAAWJ,KAC9Bd,EAAmBkB,EAAWlB,MAC9BuB,EAAmBb,EAASa,SAC5BC,EAAmBd,EAASc,eAK5BC,EAAqBN,MAAMO,QAC3BC,EAAqBP,OAAO3C,KAC5BmD,EAAqBP,EAAUQ,KAC/BC,EAAqBV,OAAOW,OAG1BC,EAAO,aAGPhD,EAAI,SAASV,GACf,MAAIA,aAAeU,GAAUV,EACvB0C,eAAgBhC,QACtBgC,KAAKiB,SAAW3D,GADiB,GAAIU,GAAEV,GAOlB,oBAAZ4D,UACa,mBAAXC,SAA0BA,OAAOD,UAC1CA,QAAUC,OAAOD,QAAUlD,GAE7BkD,QAAQlD,EAAIA,GAEZ+B,EAAK/B,EAAIA,EAIXA,EAAEoD,QAAU,OAKZ,IAAItD,GAAa,SAASuD,EAAMxD,EAASyD,GACvC,GAAIzD,QAAiB,GAAG,MAAOwD,EAC/B,QAAoB,MAAZC,EAAmB,EAAIA,GAC7B,IAAK,GAAG,MAAO,UAASC,GACtB,MAAOF,GAAKpC,KAAKpB,EAAS0D,GAE5B,KAAK,GAAG,MAAO,UAASA,EAAOC,GAC7B,MAAOH,GAAKpC,KAAKpB,EAAS0D,EAAOC,GAEnC,KAAK,GAAG,MAAO,UAASD,EAAO7D,EAAO+D,GACpC,MAAOJ,GAAKpC,KAAKpB,EAAS0D,EAAO7D,EAAO+D,GAE1C,KAAK,GAAG,MAAO,UAASC,EAAaH,EAAO7D,EAAO+D,GACjD,MAAOJ,GAAKpC,KAAKpB,EAAS6D,EAAaH,EAAO7D,EAAO+D,IAGzD,MAAO,YACL,MAAOJ,GAAKM,MAAM9D,EAASI,aAO3BI,EAAK,SAASkD,EAAO1D,EAASyD,GAChC,MAAa,OAATC,EAAsBvD,EAAE4D,SACxB5D,EAAEwB,WAAW+B,GAAezD,EAAWyD,EAAO1D,EAASyD,GACvDtD,EAAE6D,SAASN,GAAevD,EAAE8D,QAAQP,GACjCvD,EAAE+D,SAASR,GAEpBvD,GAAET,SAAW,SAASgE,EAAO1D,GAC3B,MAAOQ,GAAGkD,EAAO1D,EAASmE,KAI5B,IAAIC,GAAiB,SAASC,EAAUC,GACtC,MAAO,UAAS7E,GACd,GAAIK,GAASM,UAAUN,MACvB,IAAa,EAATA,GAAqB,MAAPL,EAAa,MAAOA,EACtC,KAAK,GAAII,GAAQ,EAAWC,EAARD,EAAgBA,IAIlC,IAAK,GAHD0E,GAASnE,UAAUP,GACnBD,EAAOyE,EAASE,GAChBC,EAAI5E,EAAKE,OACJiB,EAAI,EAAOyD,EAAJzD,EAAOA,IAAK,CAC1B,GAAI0D,GAAM7E,EAAKmB,EACVuD,IAAiB7E,EAAIgF,SAAc,KAAGhF,EAAIgF,GAAOF,EAAOE,IAGjE,MAAOhF,KAKPiF,EAAa,SAAS9C,GACxB,IAAKzB,EAAE6D,SAASpC,GAAY,QAC5B,IAAIqB,EAAc,MAAOA,GAAarB,EACtCuB,GAAKvB,UAAYA,CACjB,IAAI+C,GAAS,GAAIxB,EAEjB,OADAA,GAAKvB,UAAY,KACV+C,GAGLT,EAAW,SAASO,GACtB,MAAO,UAAShF,GACd,MAAc,OAAPA,MAAmB,GAAIA,EAAIgF,KAQlCG,EAAkB5D,KAAK6D,IAAI,EAAG,IAAM,EACpCpE,EAAYyD,EAAS,UACrBhE,EAAc,SAAS0D,GACzB,GAAI9D,GAASW,EAAUmD,EACvB,OAAwB,gBAAV9D,IAAsBA,GAAU,GAAe8E,GAAV9E,EASrDK,GAAE2E,KAAO3E,EAAE4E,QAAU,SAAStF,EAAKC,EAAUM,GAC3CN,EAAWO,EAAWP,EAAUM,EAChC,IAAIe,GAAGjB,CACP,IAAII,EAAYT,GACd,IAAKsB,EAAI,EAAGjB,EAASL,EAAIK,OAAYA,EAAJiB,EAAYA,IAC3CrB,EAASD,EAAIsB,GAAIA,EAAGtB,OAEjB,CACL,GAAIG,GAAOO,EAAEP,KAAKH,EAClB,KAAKsB,EAAI,EAAGjB,EAASF,EAAKE,OAAYA,EAAJiB,EAAYA,IAC5CrB,EAASD,EAAIG,EAAKmB,IAAKnB,EAAKmB,GAAItB,GAGpC,MAAOA,IAITU,EAAE6E,IAAM7E,EAAE8E,QAAU,SAASxF,EAAKC,EAAUM,GAC1CN,EAAWc,EAAGd,EAAUM,EAIxB,KAAK,GAHDJ,IAAQM,EAAYT,IAAQU,EAAEP,KAAKH,GACnCK,GAAUF,GAAQH,GAAKK,OACvBoF,EAAU5C,MAAMxC,GACXD,EAAQ,EAAWC,EAARD,EAAgBA,IAAS,CAC3C,GAAIE,GAAaH,EAAOA,EAAKC,GAASA,CACtCqF,GAAQrF,GAASH,EAASD,EAAIM,GAAaA,EAAYN,GAEzD,MAAOyF,IA+BT/E,EAAEgF,OAAShF,EAAEiF,MAAQjF,EAAEkF,OAAS/F,EAAa,GAG7Ca,EAAEmF,YAAcnF,EAAEoF,MAAQjG,GAAc,GAGxCa,EAAEqF,KAAOrF,EAAEsF,OAAS,SAAShG,EAAKc,EAAWP,GAC3C,GAAIyE,EAMJ,OAJEA,GADEvE,EAAYT,GACRU,EAAEuF,UAAUjG,EAAKc,EAAWP,GAE5BG,EAAEwF,QAAQlG,EAAKc,EAAWP,GAE9ByE,QAAa,IAAKA,KAAS,EAAUhF,EAAIgF,GAA7C,QAKFtE,EAAEyF,OAASzF,EAAE0F,OAAS,SAASpG,EAAKc,EAAWP,GAC7C,GAAIkF,KAKJ,OAJA3E,GAAYC,EAAGD,EAAWP,GAC1BG,EAAE2E,KAAKrF,EAAK,SAASiE,EAAO7D,EAAOiG,GAC7BvF,EAAUmD,EAAO7D,EAAOiG,IAAOZ,EAAQjD,KAAKyB,KAE3CwB,GAIT/E,EAAE4F,OAAS,SAAStG,EAAKc,EAAWP,GAClC,MAAOG,GAAEyF,OAAOnG,EAAKU,EAAE6F,OAAOxF,EAAGD,IAAaP,IAKhDG,EAAE8F,MAAQ9F,EAAE+F,IAAM,SAASzG,EAAKc,EAAWP,GACzCO,EAAYC,EAAGD,EAAWP,EAG1B,KAAK,GAFDJ,IAAQM,EAAYT,IAAQU,EAAEP,KAAKH,GACnCK,GAAUF,GAAQH,GAAKK,OAClBD,EAAQ,EAAWC,EAARD,EAAgBA,IAAS,CAC3C,GAAIE,GAAaH,EAAOA,EAAKC,GAASA,CACtC,KAAKU,EAAUd,EAAIM,GAAaA,EAAYN,GAAM,OAAO,EAE3D,OAAO,GAKTU,EAAEgG,KAAOhG,EAAEiG,IAAM,SAAS3G,EAAKc,EAAWP,GACxCO,EAAYC,EAAGD,EAAWP,EAG1B,KAAK,GAFDJ,IAAQM,EAAYT,IAAQU,EAAEP,KAAKH,GACnCK,GAAUF,GAAQH,GAAKK,OAClBD,EAAQ,EAAWC,EAARD,EAAgBA,IAAS,CAC3C,GAAIE,GAAaH,EAAOA,EAAKC,GAASA,CACtC,IAAIU,EAAUd,EAAIM,GAAaA,EAAYN,GAAM,OAAO,EAE1D,OAAO,GAKTU,EAAE6B,SAAW7B,EAAEkG,SAAWlG,EAAEmG,QAAU,SAAS7G,EAAKoB,EAAM0F,EAAWC,GAGnE,MAFKtG,GAAYT,KAAMA,EAAMU,EAAEsG,OAAOhH,KACd,gBAAb8G,IAAyBC,KAAOD,EAAY,GAChDpG,EAAEuG,QAAQjH,EAAKoB,EAAM0F,IAAc,GAI5CpG,EAAEwG,OAAS,SAASlH,EAAKmH,GACvB,GAAIC,GAAO1F,EAAMC,KAAKhB,UAAW,GAC7B0G,EAAS3G,EAAEwB,WAAWiF,EAC1B,OAAOzG,GAAE6E,IAAIvF,EAAK,SAASiE,GACzB,GAAIF,GAAOsD,EAASF,EAASlD,EAAMkD,EACnC,OAAe,OAARpD,EAAeA,EAAOA,EAAKM,MAAMJ,EAAOmD,MAKnD1G,EAAE4G,MAAQ,SAAStH,EAAKgF,GACtB,MAAOtE,GAAE6E,IAAIvF,EAAKU,EAAE+D,SAASO,KAK/BtE,EAAE6G,MAAQ,SAASvH,EAAKwH,GACtB,MAAO9G,GAAEyF,OAAOnG,EAAKU,EAAE8D,QAAQgD,KAKjC9G,EAAE+G,UAAY,SAASzH,EAAKwH,GAC1B,MAAO9G,GAAEqF,KAAK/F,EAAKU,EAAE8D,QAAQgD,KAI/B9G,EAAEc,IAAM,SAASxB,EAAKC,EAAUM,GAC9B,GACI0D,GAAOyD,EADPxC,GAAUR,IAAUiD,GAAgBjD,GAExC,IAAgB,MAAZzE,GAA2B,MAAPD,EAAa,CACnCA,EAAMS,EAAYT,GAAOA,EAAMU,EAAEsG,OAAOhH,EACxC,KAAK,GAAIsB,GAAI,EAAGjB,EAASL,EAAIK,OAAYA,EAAJiB,EAAYA,IAC/C2C,EAAQjE,EAAIsB,GACR2C,EAAQiB,IACVA,EAASjB,OAIbhE,GAAWc,EAAGd,EAAUM,GACxBG,EAAE2E,KAAKrF,EAAK,SAASiE,EAAO7D,EAAOiG,GACjCqB,EAAWzH,EAASgE,EAAO7D,EAAOiG,IAC9BqB,EAAWC,GAAgBD,KAAchD,KAAYQ,KAAYR,OACnEQ,EAASjB,EACT0D,EAAeD,IAIrB,OAAOxC,IAITxE,EAAEe,IAAM,SAASzB,EAAKC,EAAUM,GAC9B,GACI0D,GAAOyD,EADPxC,EAASR,IAAUiD,EAAejD,GAEtC,IAAgB,MAAZzE,GAA2B,MAAPD,EAAa,CACnCA,EAAMS,EAAYT,GAAOA,EAAMU,EAAEsG,OAAOhH,EACxC,KAAK,GAAIsB,GAAI,EAAGjB,EAASL,EAAIK,OAAYA,EAAJiB,EAAYA,IAC/C2C,EAAQjE,EAAIsB,GACA4D,EAARjB,IACFiB,EAASjB,OAIbhE,GAAWc,EAAGd,EAAUM,GACxBG,EAAE2E,KAAKrF,EAAK,SAASiE,EAAO7D,EAAOiG,GACjCqB,EAAWzH,EAASgE,EAAO7D,EAAOiG,IACnBsB,EAAXD,GAAwChD,MAAbgD,GAAoChD,MAAXQ,KACtDA,EAASjB,EACT0D,EAAeD,IAIrB,OAAOxC,IAKTxE,EAAEkH,QAAU,SAAS5H,GAInB,IAAK,GAAe6H,GAHhBC,EAAMrH,EAAYT,GAAOA,EAAMU,EAAEsG,OAAOhH,GACxCK,EAASyH,EAAIzH,OACb0H,EAAWlF,MAAMxC,GACZD,EAAQ,EAAiBC,EAARD,EAAgBA,IACxCyH,EAAOnH,EAAEsH,OAAO,EAAG5H,GACfyH,IAASzH,IAAO2H,EAAS3H,GAAS2H,EAASF,IAC/CE,EAASF,GAAQC,EAAI1H,EAEvB,OAAO2H,IAMTrH,EAAEuH,OAAS,SAASjI,EAAKkI,EAAGnB,GAC1B,MAAS,OAALmB,GAAanB,GACVtG,EAAYT,KAAMA,EAAMU,EAAEsG,OAAOhH,IAC/BA,EAAIU,EAAEsH,OAAOhI,EAAIK,OAAS,KAE5BK,EAAEkH,QAAQ5H,GAAK0B,MAAM,EAAGH,KAAKC,IAAI,EAAG0G,KAI7CxH,EAAEyH,OAAS,SAASnI,EAAKC,EAAUM,GAEjC,MADAN,GAAWc,EAAGd,EAAUM,GACjBG,EAAE4G,MAAM5G,EAAE6E,IAAIvF,EAAK,SAASiE,EAAO7D,EAAOiG,GAC/C,OACEpC,MAAOA,EACP7D,MAAOA,EACPgI,SAAUnI,EAASgE,EAAO7D,EAAOiG,MAElCgC,KAAK,SAASC,EAAMC,GACrB,GAAIC,GAAIF,EAAKF,SACTK,EAAIF,EAAMH,QACd,IAAII,IAAMC,EAAG,CACX,GAAID,EAAIC,GAAKD,QAAW,GAAG,MAAO,EAClC,IAAQC,EAAJD,GAASC,QAAW,GAAG,OAAQ,EAErC,MAAOH,GAAKlI,MAAQmI,EAAMnI,QACxB,SAIN,IAAIsI,GAAQ,SAASC,GACnB,MAAO,UAAS3I,EAAKC,EAAUM,GAC7B,GAAI2E,KAMJ,OALAjF,GAAWc,EAAGd,EAAUM,GACxBG,EAAE2E,KAAKrF,EAAK,SAASiE,EAAO7D,GAC1B,GAAI4E,GAAM/E,EAASgE,EAAO7D,EAAOJ,EACjC2I,GAASzD,EAAQjB,EAAOe,KAEnBE,GAMXxE,GAAEkI,QAAUF,EAAM,SAASxD,EAAQjB,EAAOe,GACpCtE,EAAE4B,IAAI4C,EAAQF,GAAME,EAAOF,GAAKxC,KAAKyB,GAAaiB,EAAOF,IAAQf,KAKvEvD,EAAEmI,QAAUH,EAAM,SAASxD,EAAQjB,EAAOe,GACxCE,EAAOF,GAAOf,IAMhBvD,EAAEoI,QAAUJ,EAAM,SAASxD,EAAQjB,EAAOe,GACpCtE,EAAE4B,IAAI4C,EAAQF,GAAME,EAAOF,KAAaE,EAAOF,GAAO,IAI5DtE,EAAEqI,QAAU,SAAS/I,GACnB,MAAKA,GACDU,EAAE0C,QAAQpD,GAAa0B,EAAMC,KAAK3B,GAClCS,EAAYT,GAAaU,EAAE6E,IAAIvF,EAAKU,EAAE4D,UACnC5D,EAAEsG,OAAOhH,OAIlBU,EAAEsI,KAAO,SAAShJ,GAChB,MAAW,OAAPA,EAAoB,EACjBS,EAAYT,GAAOA,EAAIK,OAASK,EAAEP,KAAKH,GAAKK,QAKrDK,EAAEuI,UAAY,SAASjJ,EAAKc,EAAWP,GACrCO,EAAYC,EAAGD,EAAWP,EAC1B,IAAI2I,MAAWC,IAIf,OAHAzI,GAAE2E,KAAKrF,EAAK,SAASiE,EAAOe,EAAKhF,IAC9Bc,EAAUmD,EAAOe,EAAKhF,GAAOkJ,EAAOC,GAAM3G,KAAKyB,MAE1CiF,EAAMC,IAShBzI,EAAE0I,MAAQ1I,EAAE2I,KAAO3I,EAAE4I,KAAO,SAASzI,EAAOqH,EAAGnB,GAC7C,MAAa,OAATlG,MAA2B,GACtB,MAALqH,GAAanB,EAAclG,EAAM,GAC9BH,EAAE6I,QAAQ1I,EAAOA,EAAMR,OAAS6H,IAMzCxH,EAAE6I,QAAU,SAAS1I,EAAOqH,EAAGnB,GAC7B,MAAOrF,GAAMC,KAAKd,EAAO,EAAGU,KAAKC,IAAI,EAAGX,EAAMR,QAAe,MAAL6H,GAAanB,EAAQ,EAAImB,MAKnFxH,EAAE8I,KAAO,SAAS3I,EAAOqH,EAAGnB,GAC1B,MAAa,OAATlG,MAA2B,GACtB,MAALqH,GAAanB,EAAclG,EAAMA,EAAMR,OAAS,GAC7CK,EAAE+I,KAAK5I,EAAOU,KAAKC,IAAI,EAAGX,EAAMR,OAAS6H,KAMlDxH,EAAE+I,KAAO/I,EAAEgJ,KAAOhJ,EAAEiJ,KAAO,SAAS9I,EAAOqH,EAAGnB,GAC5C,MAAOrF,GAAMC,KAAKd,EAAY,MAALqH,GAAanB,EAAQ,EAAImB,IAIpDxH,EAAEkJ,QAAU,SAAS/I,GACnB,MAAOH,GAAEyF,OAAOtF,EAAOH,EAAE4D,UAI3B,IAAIuF,GAAU,SAASC,EAAOC,EAASC,EAAQC,GAE7C,IAAK,GADDC,MAAa7I,EAAM,EACdC,EAAI2I,GAAc,EAAG5J,EAASW,EAAU8I,GAAYzJ,EAAJiB,EAAYA,IAAK,CACxE,GAAI2C,GAAQ6F,EAAMxI,EAClB,IAAIb,EAAYwD,KAAWvD,EAAE0C,QAAQa,IAAUvD,EAAEyJ,YAAYlG,IAAS,CAE/D8F,IAAS9F,EAAQ4F,EAAQ5F,EAAO8F,EAASC,GAC9C,IAAII,GAAI,EAAGC,EAAMpG,EAAM5D,MAEvB,KADA6J,EAAO7J,QAAUgK,EACNA,EAAJD,GACLF,EAAO7I,KAAS4C,EAAMmG,SAEdJ,KACVE,EAAO7I,KAAS4C,GAGpB,MAAOiG,GAITxJ,GAAEmJ,QAAU,SAAShJ,EAAOkJ,GAC1B,MAAOF,GAAQhJ,EAAOkJ,GAAS,IAIjCrJ,EAAE4J,QAAU,SAASzJ,GACnB,MAAOH,GAAE6J,WAAW1J,EAAOa,EAAMC,KAAKhB,UAAW,KAMnDD,EAAE8J,KAAO9J,EAAE+J,OAAS,SAAS5J,EAAO6J,EAAUzK,EAAUM,GACjDG,EAAEiK,UAAUD,KACfnK,EAAUN,EACVA,EAAWyK,EACXA,GAAW,GAEG,MAAZzK,IAAkBA,EAAWc,EAAGd,EAAUM,GAG9C,KAAK,GAFD2E,MACA0F,KACKtJ,EAAI,EAAGjB,EAASW,EAAUH,GAAYR,EAAJiB,EAAYA,IAAK,CAC1D,GAAI2C,GAAQpD,EAAMS,GACdoG,EAAWzH,EAAWA,EAASgE,EAAO3C,EAAGT,GAASoD,CAClDyG,IACGpJ,GAAKsJ,IAASlD,GAAUxC,EAAO1C,KAAKyB,GACzC2G,EAAOlD,GACEzH,EACJS,EAAE6B,SAASqI,EAAMlD,KACpBkD,EAAKpI,KAAKkF,GACVxC,EAAO1C,KAAKyB,IAEJvD,EAAE6B,SAAS2C,EAAQjB,IAC7BiB,EAAO1C,KAAKyB,GAGhB,MAAOiB,IAKTxE,EAAEmK,MAAQ,WACR,MAAOnK,GAAE8J,KAAKX,EAAQlJ,WAAW,GAAM,KAKzCD,EAAEoK,aAAe,SAASjK,GAGxB,IAAK,GAFDqE,MACA6F,EAAapK,UAAUN,OAClBiB,EAAI,EAAGjB,EAASW,EAAUH,GAAYR,EAAJiB,EAAYA,IAAK,CAC1D,GAAIF,GAAOP,EAAMS,EACjB,KAAIZ,EAAE6B,SAAS2C,EAAQ9D,GAAvB,CACA,IAAK,GAAIgJ,GAAI,EAAOW,EAAJX,GACT1J,EAAE6B,SAAS5B,UAAUyJ,GAAIhJ,GADAgJ,KAG5BA,IAAMW,GAAY7F,EAAO1C,KAAKpB,IAEpC,MAAO8D,IAKTxE,EAAE6J,WAAa,SAAS1J,GACtB,GAAI4I,GAAOI,EAAQlJ,WAAW,GAAM,EAAM,EAC1C,OAAOD,GAAEyF,OAAOtF,EAAO,SAASoD,GAC9B,OAAQvD,EAAE6B,SAASkH,EAAMxF,MAM7BvD,EAAEsK,IAAM,WACN,MAAOtK,GAAEuK,MAAMtK,YAKjBD,EAAEuK,MAAQ,SAASpK,GAIjB,IAAK,GAHDR,GAASQ,GAASH,EAAEc,IAAIX,EAAOG,GAAWX,QAAU,EACpD6E,EAASrC,MAAMxC,GAEVD,EAAQ,EAAWC,EAARD,EAAgBA,IAClC8E,EAAO9E,GAASM,EAAE4G,MAAMzG,EAAOT,EAEjC,OAAO8E,IAMTxE,EAAEwK,OAAS,SAAS7E,EAAMW,GAExB,IAAK,GADD9B,MACK5D,EAAI,EAAGjB,EAASW,EAAUqF,GAAWhG,EAAJiB,EAAYA,IAChD0F,EACF9B,EAAOmB,EAAK/E,IAAM0F,EAAO1F,GAEzB4D,EAAOmB,EAAK/E,GAAG,IAAM+E,EAAK/E,GAAG,EAGjC,OAAO4D,IAiBTxE,EAAEuF,UAAYrF,EAA2B,GACzCF,EAAEyK,cAAgBvK,GAA4B,GAI9CF,EAAES,YAAc,SAASN,EAAOb,EAAKC,EAAUM,GAC7CN,EAAWc,EAAGd,EAAUM,EAAS,EAGjC,KAFA,GAAI0D,GAAQhE,EAASD,GACjBoL,EAAM,EAAGC,EAAOrK,EAAUH,GACjBwK,EAAND,GAAY,CACjB,GAAIE,GAAM/J,KAAKgK,OAAOH,EAAMC,GAAQ,EAChCpL,GAASY,EAAMyK,IAAQrH,EAAOmH,EAAME,EAAM,EAAQD,EAAOC,EAE/D,MAAOF,IAgCT1K,EAAEuG,QAAUhG,EAAkB,EAAGP,EAAEuF,UAAWvF,EAAES,aAChDT,EAAE8K,YAAcvK,GAAmB,EAAGP,EAAEyK,eAKxCzK,EAAE+K,MAAQ,SAASC,EAAOC,EAAMC,GAClB,MAARD,IACFA,EAAOD,GAAS,EAChBA,EAAQ,GAEVE,EAAOA,GAAQ,CAKf,KAAK,GAHDvL,GAASkB,KAAKC,IAAID,KAAKsK,MAAMF,EAAOD,GAASE,GAAO,GACpDH,EAAQ5I,MAAMxC,GAETgB,EAAM,EAAShB,EAANgB,EAAcA,IAAOqK,GAASE,EAC9CH,EAAMpK,GAAOqK,CAGf,OAAOD,GAQT,IAAIK,GAAe,SAASC,EAAYC,EAAWzL,EAAS0L,EAAgB7E,GAC1E,KAAM6E,YAA0BD,IAAY,MAAOD,GAAW1H,MAAM9D,EAAS6G,EAC7E,IAAI8E,GAAOjH,EAAW8G,EAAW5J,WAC7B+C,EAAS6G,EAAW1H,MAAM6H,EAAM9E,EACpC,OAAI1G,GAAE6D,SAASW,GAAgBA,EACxBgH,EAMTxL,GAAE6C,KAAO,SAASQ,EAAMxD,GACtB,GAAI+C,GAAcS,EAAKR,OAASD,EAAY,MAAOA,GAAWe,MAAMN,EAAMrC,EAAMC,KAAKhB,UAAW,GAChG,KAAKD,EAAEwB,WAAW6B,GAAO,KAAM,IAAIoI,WAAU,oCAC7C,IAAI/E,GAAO1F,EAAMC,KAAKhB,UAAW,GAC7ByL,EAAQ,WACV,MAAON,GAAa/H,EAAMqI,EAAO7L,EAASmC,KAAM0E,EAAKiF,OAAO3K,EAAMC,KAAKhB,aAEzE,OAAOyL,IAMT1L,EAAE4L,QAAU,SAASvI,GACnB,GAAIwI,GAAY7K,EAAMC,KAAKhB,UAAW,GAClCyL,EAAQ,WAGV,IAAK,GAFDI,GAAW,EAAGnM,EAASkM,EAAUlM,OACjC+G,EAAOvE,MAAMxC,GACRiB,EAAI,EAAOjB,EAAJiB,EAAYA,IAC1B8F,EAAK9F,GAAKiL,EAAUjL,KAAOZ,EAAIC,UAAU6L,KAAcD,EAAUjL,EAEnE,MAAOkL,EAAW7L,UAAUN,QAAQ+G,EAAK5E,KAAK7B,UAAU6L,KACxD,OAAOV,GAAa/H,EAAMqI,EAAO1J,KAAMA,KAAM0E,GAE/C,OAAOgF,IAMT1L,EAAE+L,QAAU,SAASzM,GACnB,GAAIsB,GAA8B0D,EAA3B3E,EAASM,UAAUN,MAC1B,IAAc,GAAVA,EAAa,KAAM,IAAIqM,OAAM,wCACjC,KAAKpL,EAAI,EAAOjB,EAAJiB,EAAYA,IACtB0D,EAAMrE,UAAUW,GAChBtB,EAAIgF,GAAOtE,EAAE6C,KAAKvD,EAAIgF,GAAMhF,EAE9B,OAAOA,IAITU,EAAEiM,QAAU,SAAS5I,EAAM6I,GACzB,GAAID,GAAU,SAAS3H,GACrB,GAAI6H,GAAQF,EAAQE,MAChBC,EAAU,IAAMF,EAASA,EAAOvI,MAAM3B,KAAM/B,WAAaqE,EAE7D,OADKtE,GAAE4B,IAAIuK,EAAOC,KAAUD,EAAMC,GAAW/I,EAAKM,MAAM3B,KAAM/B,YACvDkM,EAAMC,GAGf,OADAH,GAAQE,SACDF,GAKTjM,EAAEqM,MAAQ,SAAShJ,EAAMiJ,GACvB,GAAI5F,GAAO1F,EAAMC,KAAKhB,UAAW,EACjC,OAAOsM,YAAW,WAChB,MAAOlJ,GAAKM,MAAM,KAAM+C,IACvB4F,IAKLtM,EAAEwM,MAAQxM,EAAE4L,QAAQ5L,EAAEqM,MAAOrM,EAAG,GAOhCA,EAAEyM,SAAW,SAASpJ,EAAMiJ,EAAMI,GAChC,GAAI7M,GAAS6G,EAAMlC,EACfmI,EAAU,KACVC,EAAW,CACVF,KAASA,KACd,IAAIG,GAAQ,WACVD,EAAWF,EAAQI,WAAY,EAAQ,EAAI9M,EAAE+M,MAC7CJ,EAAU,KACVnI,EAASnB,EAAKM,MAAM9D,EAAS6G,GACxBiG,IAAS9M,EAAU6G,EAAO,MAEjC,OAAO,YACL,GAAIqG,GAAM/M,EAAE+M,KACPH,IAAYF,EAAQI,WAAY,IAAOF,EAAWG,EACvD,IAAIC,GAAYV,GAAQS,EAAMH,EAc9B,OAbA/M,GAAUmC,KACV0E,EAAOzG,UACU,GAAb+M,GAAkBA,EAAYV,GAC5BK,IACFM,aAAaN,GACbA,EAAU,MAEZC,EAAWG,EACXvI,EAASnB,EAAKM,MAAM9D,EAAS6G,GACxBiG,IAAS9M,EAAU6G,EAAO,OACrBiG,GAAWD,EAAQQ,YAAa,IAC1CP,EAAUJ,WAAWM,EAAOG,IAEvBxI,IAQXxE,EAAEmN,SAAW,SAAS9J,EAAMiJ,EAAMc,GAChC,GAAIT,GAASjG,EAAM7G,EAASwN,EAAW7I,EAEnCqI,EAAQ,WACV,GAAI/D,GAAO9I,EAAE+M,MAAQM,CAEVf,GAAPxD,GAAeA,GAAQ,EACzB6D,EAAUJ,WAAWM,EAAOP,EAAOxD,IAEnC6D,EAAU,KACLS,IACH5I,EAASnB,EAAKM,MAAM9D,EAAS6G,GACxBiG,IAAS9M,EAAU6G,EAAO,QAKrC,OAAO,YACL7G,EAAUmC,KACV0E,EAAOzG,UACPoN,EAAYrN,EAAE+M,KACd,IAAIO,GAAUF,IAAcT,CAO5B,OANKA,KAASA,EAAUJ,WAAWM,EAAOP,IACtCgB,IACF9I,EAASnB,EAAKM,MAAM9D,EAAS6G,GAC7B7G,EAAU6G,EAAO,MAGZlC,IAOXxE,EAAEuN,KAAO,SAASlK,EAAMmK,GACtB,MAAOxN,GAAE4L,QAAQ4B,EAASnK,IAI5BrD,EAAE6F,OAAS,SAASzF,GAClB,MAAO,YACL,OAAQA,EAAUuD,MAAM3B,KAAM/B,aAMlCD,EAAEyN,QAAU,WACV,GAAI/G,GAAOzG,UACP+K,EAAQtE,EAAK/G,OAAS,CAC1B,OAAO,YAGL,IAFA,GAAIiB,GAAIoK,EACJxG,EAASkC,EAAKsE,GAAOrH,MAAM3B,KAAM/B,WAC9BW,KAAK4D,EAASkC,EAAK9F,GAAGK,KAAKe,KAAMwC,EACxC,OAAOA,KAKXxE,EAAE0N,MAAQ,SAASC,EAAOtK,GACxB,MAAO,YACL,QAAMsK,EAAQ,EACLtK,EAAKM,MAAM3B,KAAM/B,WAD1B,SAOJD,EAAE4N,OAAS,SAASD,EAAOtK,GACzB,GAAI7D,EACJ,OAAO,YAKL,QAJMmO,EAAQ,IACZnO,EAAO6D,EAAKM,MAAM3B,KAAM/B,YAEb,GAAT0N,IAAYtK,EAAO,MAChB7D,IAMXQ,EAAE6N,KAAO7N,EAAE4L,QAAQ5L,EAAE4N,OAAQ,EAM7B,IAAIE,KAAevL,SAAU,MAAMwL,qBAAqB,YACpD1M,GAAsB,UAAW,gBAAiB,WAClC,uBAAwB,iBAAkB,iBAqB9DrB,GAAEP,KAAO,SAASH,GAChB,IAAKU,EAAE6D,SAASvE,GAAM,QACtB,IAAIqD,EAAY,MAAOA,GAAWrD,EAClC,IAAIG,KACJ,KAAK,GAAI6E,KAAOhF,GAASU,EAAE4B,IAAItC,EAAKgF,IAAM7E,EAAKqC,KAAKwC,EAGpD,OADIwJ,IAAY3M,EAAoB7B,EAAKG,GAClCA,GAITO,EAAEgO,QAAU,SAAS1O,GACnB,IAAKU,EAAE6D,SAASvE,GAAM,QACtB,IAAIG,KACJ,KAAK,GAAI6E,KAAOhF,GAAKG,EAAKqC,KAAKwC,EAG/B,OADIwJ,IAAY3M,EAAoB7B,EAAKG,GAClCA,GAITO,EAAEsG,OAAS,SAAShH,GAIlB,IAAK,GAHDG,GAAOO,EAAEP,KAAKH,GACdK,EAASF,EAAKE,OACd2G,EAASnE,MAAMxC,GACViB,EAAI,EAAOjB,EAAJiB,EAAYA,IAC1B0F,EAAO1F,GAAKtB,EAAIG,EAAKmB,GAEvB,OAAO0F,IAKTtG,EAAEiO,UAAY,SAAS3O,EAAKC,EAAUM,GACpCN,EAAWc,EAAGd,EAAUM,EAKtB,KAAK,GADDD,GAHFH,EAAQO,EAAEP,KAAKH,GACbK,EAASF,EAAKE,OACdoF,KAEKrF,EAAQ,EAAWC,EAARD,EAAgBA,IAClCE,EAAaH,EAAKC,GAClBqF,EAAQnF,GAAcL,EAASD,EAAIM,GAAaA,EAAYN,EAE9D,OAAOyF,IAIX/E,EAAEkO,MAAQ,SAAS5O,GAIjB,IAAK,GAHDG,GAAOO,EAAEP,KAAKH,GACdK,EAASF,EAAKE,OACduO,EAAQ/L,MAAMxC,GACTiB,EAAI,EAAOjB,EAAJiB,EAAYA,IAC1BsN,EAAMtN,IAAMnB,EAAKmB,GAAItB,EAAIG,EAAKmB,IAEhC,OAAOsN,IAITlO,EAAEmO,OAAS,SAAS7O,GAGlB,IAAK,GAFDkF,MACA/E,EAAOO,EAAEP,KAAKH,GACTsB,EAAI,EAAGjB,EAASF,EAAKE,OAAYA,EAAJiB,EAAYA,IAChD4D,EAAOlF,EAAIG,EAAKmB,KAAOnB,EAAKmB,EAE9B,OAAO4D,IAKTxE,EAAEoO,UAAYpO,EAAEqO,QAAU,SAAS/O,GACjC,GAAIgP,KACJ,KAAK,GAAIhK,KAAOhF,GACVU,EAAEwB,WAAWlC,EAAIgF,KAAOgK,EAAMxM,KAAKwC,EAEzC,OAAOgK,GAAM3G,QAIf3H,EAAEuO,OAAStK,EAAejE,EAAEgO,SAI5BhO,EAAEwO,UAAYxO,EAAEyO,OAASxK,EAAejE,EAAEP,MAG1CO,EAAEwF,QAAU,SAASlG,EAAKc,EAAWP,GACnCO,EAAYC,EAAGD,EAAWP,EAE1B,KAAK,GADmByE,GAApB7E,EAAOO,EAAEP,KAAKH,GACTsB,EAAI,EAAGjB,EAASF,EAAKE,OAAYA,EAAJiB,EAAYA,IAEhD,GADA0D,EAAM7E,EAAKmB,GACPR,EAAUd,EAAIgF,GAAMA,EAAKhF,GAAM,MAAOgF,IAK9CtE,EAAE0O,KAAO,SAASlE,EAAQmE,EAAW9O,GACnC,GAA+BN,GAAUE,EAArC+E,KAAalF,EAAMkL,CACvB,IAAW,MAAPlL,EAAa,MAAOkF,EACpBxE,GAAEwB,WAAWmN,IACflP,EAAOO,EAAEgO,QAAQ1O,GACjBC,EAAWO,EAAW6O,EAAW9O,KAEjCJ,EAAO0J,EAAQlJ,WAAW,GAAO,EAAO,GACxCV,EAAW,SAASgE,EAAOe,EAAKhF,GAAO,MAAOgF,KAAOhF,IACrDA,EAAM8C,OAAO9C,GAEf,KAAK,GAAIsB,GAAI,EAAGjB,EAASF,EAAKE,OAAYA,EAAJiB,EAAYA,IAAK,CACrD,GAAI0D,GAAM7E,EAAKmB,GACX2C,EAAQjE,EAAIgF,EACZ/E,GAASgE,EAAOe,EAAKhF,KAAMkF,EAAOF,GAAOf,GAE/C,MAAOiB,IAITxE,EAAE4O,KAAO,SAAStP,EAAKC,EAAUM,GAC/B,GAAIG,EAAEwB,WAAWjC,GACfA,EAAWS,EAAE6F,OAAOtG,OACf,CACL,GAAIE,GAAOO,EAAE6E,IAAIsE,EAAQlJ,WAAW,GAAO,EAAO,GAAI4O,OACtDtP,GAAW,SAASgE,EAAOe,GACzB,OAAQtE,EAAE6B,SAASpC,EAAM6E,IAG7B,MAAOtE,GAAE0O,KAAKpP,EAAKC,EAAUM,IAI/BG,EAAE8O,SAAW7K,EAAejE,EAAEgO,SAAS,GAKvChO,EAAE+C,OAAS,SAAStB,EAAWsN,GAC7B,GAAIvK,GAASD,EAAW9C,EAExB,OADIsN,IAAO/O,EAAEwO,UAAUhK,EAAQuK,GACxBvK,GAITxE,EAAEgP,MAAQ,SAAS1P,GACjB,MAAKU,GAAE6D,SAASvE,GACTU,EAAE0C,QAAQpD,GAAOA,EAAI0B,QAAUhB,EAAEuO,UAAWjP,GADtBA,GAO/BU,EAAEiP,IAAM,SAAS3P,EAAK4P,GAEpB,MADAA,GAAY5P,GACLA,GAITU,EAAEmP,QAAU,SAAS3E,EAAQ1D,GAC3B,GAAIrH,GAAOO,EAAEP,KAAKqH,GAAQnH,EAASF,EAAKE,MACxC,IAAc,MAAV6K,EAAgB,OAAQ7K,CAE5B,KAAK,GADDL,GAAM8C,OAAOoI,GACR5J,EAAI,EAAOjB,EAAJiB,EAAYA,IAAK,CAC/B,GAAI0D,GAAM7E,EAAKmB,EACf,IAAIkG,EAAMxC,KAAShF,EAAIgF,MAAUA,IAAOhF,IAAM,OAAO,EAEvD,OAAO,EAKT,IAAI8P,GAAK,SAAStH,EAAGC,EAAGsH,EAAQC,GAG9B,GAAIxH,IAAMC,EAAG,MAAa,KAAND,GAAW,EAAIA,IAAM,EAAIC,CAE7C,IAAS,MAALD,GAAkB,MAALC,EAAW,MAAOD,KAAMC,CAErCD,aAAa9H,KAAG8H,EAAIA,EAAE7E,UACtB8E,YAAa/H,KAAG+H,EAAIA,EAAE9E,SAE1B,IAAIsM,GAAYhN,EAAStB,KAAK6G,EAC9B,IAAIyH,IAAchN,EAAStB,KAAK8G,GAAI,OAAO,CAC3C,QAAQwH,GAEN,IAAK,kBAEL,IAAK,kBAGH,MAAO,GAAKzH,GAAM,GAAKC,CACzB,KAAK,kBAGH,OAAKD,KAAOA,GAAWC,KAAOA,EAEhB,KAAND,EAAU,GAAKA,IAAM,EAAIC,GAAKD,KAAOC,CAC/C,KAAK,gBACL,IAAK,mBAIH,OAAQD,KAAOC,EAGnB,GAAIyH,GAA0B,mBAAdD,CAChB,KAAKC,EAAW,CACd,GAAgB,gBAAL1H,IAA6B,gBAALC,GAAe,OAAO,CAIzD,IAAI0H,GAAQ3H,EAAExG,YAAaoO,EAAQ3H,EAAEzG,WACrC,IAAImO,IAAUC,KAAW1P,EAAEwB,WAAWiO,IAAUA,YAAiBA,IACxCzP,EAAEwB,WAAWkO,IAAUA,YAAiBA,KACzC,eAAiB5H,IAAK,eAAiBC,GAC7D,OAAO,EAQXsH,EAASA,MACTC,EAASA,KAET,KADA,GAAI3P,GAAS0P,EAAO1P,OACbA,KAGL,GAAI0P,EAAO1P,KAAYmI,EAAG,MAAOwH,GAAO3P,KAAYoI,CAQtD,IAJAsH,EAAOvN,KAAKgG,GACZwH,EAAOxN,KAAKiG,GAGRyH,EAAW,CAGb,GADA7P,EAASmI,EAAEnI,OACPA,IAAWoI,EAAEpI,OAAQ,OAAO,CAEhC,MAAOA,KACL,IAAKyP,EAAGtH,EAAEnI,GAASoI,EAAEpI,GAAS0P,EAAQC,GAAS,OAAO,MAEnD,CAEL,GAAsBhL,GAAlB7E,EAAOO,EAAEP,KAAKqI,EAGlB,IAFAnI,EAASF,EAAKE,OAEVK,EAAEP,KAAKsI,GAAGpI,SAAWA,EAAQ,OAAO,CACxC,MAAOA,KAGL,GADA2E,EAAM7E,EAAKE,IACLK,EAAE4B,IAAImG,EAAGzD,KAAQ8K,EAAGtH,EAAExD,GAAMyD,EAAEzD,GAAM+K,EAAQC,GAAU,OAAO,EAMvE,MAFAD,GAAOM,MACPL,EAAOK,OACA,EAIT3P,GAAE4P,QAAU,SAAS9H,EAAGC,GACtB,MAAOqH,GAAGtH,EAAGC,IAKf/H,EAAE6P,QAAU,SAASvQ,GACnB,MAAW,OAAPA,GAAoB,EACpBS,EAAYT,KAASU,EAAE0C,QAAQpD,IAAQU,EAAE8P,SAASxQ,IAAQU,EAAEyJ,YAAYnK,IAA6B,IAAfA,EAAIK,OAChE,IAAvBK,EAAEP,KAAKH,GAAKK,QAIrBK,EAAE+P,UAAY,SAASzQ,GACrB,SAAUA,GAAwB,IAAjBA,EAAI0Q,WAKvBhQ,EAAE0C,QAAUD,GAAiB,SAASnD,GACpC,MAA8B,mBAAvBiD,EAAStB,KAAK3B,IAIvBU,EAAE6D,SAAW,SAASvE,GACpB,GAAI2Q,SAAc3Q,EAClB,OAAgB,aAAT2Q,GAAgC,WAATA,KAAuB3Q,GAIvDU,EAAE2E,MAAM,YAAa,WAAY,SAAU,SAAU,OAAQ,SAAU,SAAU,SAASuL,GACxFlQ,EAAE,KAAOkQ,GAAQ,SAAS5Q,GACxB,MAAOiD,GAAStB,KAAK3B,KAAS,WAAa4Q,EAAO,OAMjDlQ,EAAEyJ,YAAYxJ,aACjBD,EAAEyJ,YAAc,SAASnK,GACvB,MAAOU,GAAE4B,IAAItC,EAAK,YAMJ,kBAAP,KAAyC,gBAAb6Q,aACrCnQ,EAAEwB,WAAa,SAASlC,GACtB,MAAqB,kBAAPA,KAAqB,IAKvCU,EAAEoQ,SAAW,SAAS9Q,GACpB,MAAO8Q,UAAS9Q,KAAS4B,MAAMmP,WAAW/Q,KAI5CU,EAAEkB,MAAQ,SAAS5B,GACjB,MAAOU,GAAEsQ,SAAShR,IAAQA,KAASA,GAIrCU,EAAEiK,UAAY,SAAS3K,GACrB,MAAOA,MAAQ,GAAQA,KAAQ,GAAgC,qBAAvBiD,EAAStB,KAAK3B,IAIxDU,EAAEuQ,OAAS,SAASjR,GAClB,MAAe,QAARA,GAITU,EAAEwQ,YAAc,SAASlR,GACvB,MAAOA,SAAa,IAKtBU,EAAE4B,IAAM,SAAStC,EAAKgF,GACpB,MAAc,OAAPhF,GAAekD,EAAevB,KAAK3B,EAAKgF,IAQjDtE,EAAEyQ,WAAa,WAEb,MADA1O,GAAK/B,EAAIiC,EACFD,MAIThC,EAAE4D,SAAW,SAASL,GACpB,MAAOA,IAITvD,EAAE0Q,SAAW,SAASnN,GACpB,MAAO,YACL,MAAOA,KAIXvD,EAAE2Q,KAAO,aAET3Q,EAAE+D,SAAWA,EAGb/D,EAAE4Q,WAAa,SAAStR,GACtB,MAAc,OAAPA,EAAc,aAAe,SAASgF,GAC3C,MAAOhF,GAAIgF,KAMftE,EAAE8D,QAAU9D,EAAE6Q,QAAU,SAAS/J,GAE/B,MADAA,GAAQ9G,EAAEwO,aAAc1H,GACjB,SAASxH,GACd,MAAOU,GAAEmP,QAAQ7P,EAAKwH,KAK1B9G,EAAE2N,MAAQ,SAASnG,EAAGjI,EAAUM,GAC9B,GAAIiR,GAAQ3O,MAAMtB,KAAKC,IAAI,EAAG0G,GAC9BjI,GAAWO,EAAWP,EAAUM,EAAS,EACzC,KAAK,GAAIe,GAAI,EAAO4G,EAAJ5G,EAAOA,IAAKkQ,EAAMlQ,GAAKrB,EAASqB,EAChD,OAAOkQ,IAIT9Q,EAAEsH,OAAS,SAASvG,EAAKD,GAKvB,MAJW,OAAPA,IACFA,EAAMC,EACNA,EAAM,GAEDA,EAAMF,KAAKgK,MAAMhK,KAAKyG,UAAYxG,EAAMC,EAAM,KAIvDf,EAAE+M,IAAMgE,KAAKhE,KAAO,WAClB,OAAO,GAAIgE,OAAOC,UAIpB,IAAIC,IACFC,IAAK,QACLC,IAAK,OACLC,IAAK,OACLC,IAAK,SACLC,IAAK,SACLC,IAAK,UAEHC,EAAcxR,EAAEmO,OAAO8C,GAGvBQ,EAAgB,SAAS5M,GAC3B,GAAI6M,GAAU,SAASC,GACrB,MAAO9M,GAAI8M,IAGTvN,EAAS,MAAQpE,EAAEP,KAAKoF,GAAK+M,KAAK,KAAO,IACzCC,EAAaC,OAAO1N,GACpB2N,EAAgBD,OAAO1N,EAAQ,IACnC,OAAO,UAAS4N,GAEd,MADAA,GAAmB,MAAVA,EAAiB,GAAK,GAAKA,EAC7BH,EAAWI,KAAKD,GAAUA,EAAOE,QAAQH,EAAeL,GAAWM,GAG9EhS,GAAEmS,OAASV,EAAcR,GACzBjR,EAAEoS,SAAWX,EAAcD,GAI3BxR,EAAEwE,OAAS,SAASgG,EAAQzG,EAAUsO,GACpC,GAAI9O,GAAkB,MAAViH,MAAsB,GAAIA,EAAOzG,EAI7C,OAHIR,SAAe,KACjBA,EAAQ8O,GAEHrS,EAAEwB,WAAW+B,GAASA,EAAMtC,KAAKuJ,GAAUjH,EAKpD,IAAI+O,GAAY,CAChBtS,GAAEuS,SAAW,SAASC,GACpB,GAAIC,KAAOH,EAAY,EACvB,OAAOE,GAASA,EAASC,EAAKA,GAKhCzS,EAAE0S,kBACAC,SAAc,kBACdC,YAAc,mBACdT,OAAc,mBAMhB,IAAIU,GAAU,OAIVC,GACFxB,IAAU,IACVyB,KAAU,KACVC,KAAU,IACVC,KAAU,IACVC,SAAU,QACVC,SAAU,SAGRzB,EAAU,4BAEV0B,EAAa,SAASzB,GACxB,MAAO,KAAOmB,EAAQnB,GAOxB3R,GAAEqT,SAAW,SAASC,EAAMC,EAAUC,IAC/BD,GAAYC,IAAaD,EAAWC,GACzCD,EAAWvT,EAAE8O,YAAayE,EAAUvT,EAAE0S,iBAGtC,IAAI5O,GAAUgO,SACXyB,EAASpB,QAAUU,GAASzO,QAC5BmP,EAASX,aAAeC,GAASzO,QACjCmP,EAASZ,UAAYE,GAASzO,QAC/BwN,KAAK,KAAO,KAAM,KAGhBlS,EAAQ,EACR0E,EAAS,QACbkP,GAAKpB,QAAQpO,EAAS,SAAS6N,EAAOQ,EAAQS,EAAaD,EAAUc,GAanE,MAZArP,IAAUkP,EAAKtS,MAAMtB,EAAO+T,GAAQvB,QAAQR,EAAS0B,GACrD1T,EAAQ+T,EAAS9B,EAAMhS,OAEnBwS,EACF/N,GAAU,cAAgB+N,EAAS,iCAC1BS,EACTxO,GAAU,cAAgBwO,EAAc,uBAC/BD,IACTvO,GAAU,OAASuO,EAAW,YAIzBhB,IAETvN,GAAU,OAGLmP,EAASG,WAAUtP,EAAS,mBAAqBA,EAAS,OAE/DA,EAAS,2CACP,oDACAA,EAAS,eAEX,KACE,GAAIuP,GAAS,GAAIrR,UAASiR,EAASG,UAAY,MAAO,IAAKtP,GAC3D,MAAOwP,GAEP,KADAA,GAAExP,OAASA,EACLwP,EAGR,GAAIP,GAAW,SAASQ,GACtB,MAAOF,GAAO1S,KAAKe,KAAM6R,EAAM7T,IAI7B8T,EAAWP,EAASG,UAAY,KAGpC,OAFAL,GAASjP,OAAS,YAAc0P,EAAW,OAAS1P,EAAS,IAEtDiP,GAITrT,EAAE+T,MAAQ,SAASzU,GACjB,GAAI0U,GAAWhU,EAAEV,EAEjB,OADA0U,GAASC,QAAS,EACXD,EAUT,IAAIxP,GAAS,SAASwP,EAAU1U,GAC9B,MAAO0U,GAASC,OAASjU,EAAEV,GAAKyU,QAAUzU,EAI5CU,GAAEkU,MAAQ,SAAS5U,GACjBU,EAAE2E,KAAK3E,EAAEoO,UAAU9O,GAAM,SAAS4Q,GAChC,GAAI7M,GAAOrD,EAAEkQ,GAAQ5Q,EAAI4Q,EACzBlQ,GAAEyB,UAAUyO,GAAQ,WAClB,GAAIxJ,IAAQ1E,KAAKiB,SAEjB,OADAnB,GAAK6B,MAAM+C,EAAMzG,WACVuE,EAAOxC,KAAMqB,EAAKM,MAAM3D,EAAG0G,QAMxC1G,EAAEkU,MAAMlU,GAGRA,EAAE2E,MAAM,MAAO,OAAQ,UAAW,QAAS,OAAQ,SAAU,WAAY,SAASuL,GAChF,GAAIzJ,GAASvE,EAAWgO,EACxBlQ,GAAEyB,UAAUyO,GAAQ,WAClB,GAAI5Q,GAAM0C,KAAKiB,QAGf,OAFAwD,GAAO9C,MAAMrE,EAAKW,WACJ,UAATiQ,GAA6B,WAATA,GAAqC,IAAf5Q,EAAIK,cAAqBL,GAAI,GACrEkF,EAAOxC,KAAM1C,MAKxBU,EAAE2E,MAAM,SAAU,OAAQ,SAAU,SAASuL,GAC3C,GAAIzJ,GAASvE,EAAWgO,EACxBlQ,GAAEyB,UAAUyO,GAAQ,WAClB,MAAO1L,GAAOxC,KAAMyE,EAAO9C,MAAM3B,KAAKiB,SAAUhD,eAKpDD,EAAEyB,UAAU8B,MAAQ,WAClB,MAAOvB,MAAKiB,UAKdjD,EAAEyB,UAAU0S,QAAUnU,EAAEyB,UAAU2S,OAASpU,EAAEyB,UAAU8B,MAEvDvD,EAAEyB,UAAUc,SAAW,WACrB,MAAO,GAAKP,KAAKiB,UAUG,kBAAXoR,SAAyBA,OAAOC,KACzCD,OAAO,gBAAkB,WACvB,MAAOrU,OAGXiB,KAAKe"} \ No newline at end of file diff --git a/underscore.js b/underscore.js index c196962a1..e4959b461 100644 --- a/underscore.js +++ b/underscore.js @@ -1,6 +1,6 @@ -// Underscore.js 1.8.3 +// Underscore.js 1.9.0 // http://underscorejs.org -// (c) 2009-2017 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// (c) 2009-2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // Underscore may be freely distributed under the MIT license. (function() { @@ -60,7 +60,7 @@ } // Current version. - _.VERSION = '1.8.3'; + _.VERSION = '1.9.0'; // Internal function that returns an efficient (for current engines) version // of the passed-in callback, to be repeatedly applied in other Underscore @@ -737,11 +737,10 @@ return range; }; - // Split an **array** into several arrays containing **count** or less elements - // of initial array. + // Chunk a single array into multiple arrays, each containing `count` or fewer + // items. _.chunk = function(array, count) { if (count == null || count < 1) return []; - var result = []; var i = 0, length = array.length; while (i < length) { @@ -1409,6 +1408,8 @@ _.noop = function(){}; + // Creates a function that, when passed an object, will traverse that object’s + // properties down the given `path`, specified as an array of keys or indexes. _.property = function(path) { if (!_.isArray(path)) { return shallowProperty(path);