diff --git a/lodash.js b/lodash.js index 02cac1c28..673d90749 100644 --- a/lodash.js +++ b/lodash.js @@ -3,7 +3,7 @@ * Lo-Dash 2.4.1 * Copyright 2012-2014 The Dojo Foundation * Based on Underscore.js 1.6.0 - * Copyright 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors * Available under MIT license */ ;(function() { @@ -25,15 +25,9 @@ /** Used as the property name for wrapper metadata */ var expando = '__lodash@' + version + '__'; - /** Used as the TypeError message for "Functions" methods */ - var funcErrorText = 'Expected a function'; - /** Used to generate unique IDs */ var idCounter = 0; - /** Used to detect words composed of all capital letters */ - var reAllCaps = /^[A-Z]+$/; - /** Used to match empty string literals in compiled template source */ var reEmptyStringLeading = /\b__p \+= '';/g, reEmptyStringMiddle = /\b(__p \+=) '' \+/g, @@ -49,13 +43,12 @@ reInterpolate = /<%=([\s\S]+?)%>/g; /** - * Used to match ES6 template delimiters. - * See the [ES6 spec](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-template-literal-lexical-components) - * for more details. + * Used to match ES6 template delimiters + * http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals */ var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; - /** Used to match `RegExp` flags from their coerced string values */ + /** Used to match regexp flags from their coerced string values */ var reFlags = /\w*$/; /** Used to detected named functions */ @@ -64,27 +57,14 @@ /** Used to detect hexadecimal string values */ var reHexPrefix = /^0[xX]/; - /** Used to match latin-1 supplement letters */ - var reLatin1 = /[\xC0-\xFF]/g; - /** Used to ensure capturing order of template delimiters */ var reNoMatch = /($^)/; - /** - * Used to match `RegExp` special characters. - * See this [article on `RegExp` characters](http://www.regular-expressions.info/characters.html#special) - * for more details. - */ - var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g; - /** Used to detect functions containing a `this` reference */ var reThis = /\bthis\b/; /** Used to match unescaped characters in compiled string literals */ - var reUnescapedString = /['\n\r\u2028\u2029\\]/g; - - /** Used to match words to create compound words */ - var reWords = /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[a-z]+|[0-9]+/g; + var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; /** Used to detect and test whitespace */ var whitespace = ( @@ -105,13 +85,13 @@ 'parseInt', 'setTimeout', 'TypeError', 'window', 'WinRTError' ]; - /** Used to fix the JScript `[[DontEnum]]` bug */ + /** Used to fix the JScript [[DontEnum]] bug */ var shadowedProps = [ 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf' ]; - /** Used to make template `sourceURL`s easier to identify */ + /** Used to make template sourceURLs easier to identify */ var templateCounter = 0; /** `Object#toString` result shortcuts */ @@ -134,7 +114,7 @@ cloneableClasses[numberClass] = cloneableClasses[objectClass] = cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true; - /** Used as an internal `_.debounce` options object by `_.throttle` */ + /** Used as an internal `_.debounce` options object */ var debounceOptions = { 'leading': false, 'maxWait': 0, @@ -175,32 +155,7 @@ ''': "'" }; - /** - * Used to convert latin-1 supplement letters to basic latin (ASCII) letters. - * See [Wikipedia](http://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) - * for more details. - */ - var deburredLetters = { - '\xC0': 'A', '\xC1': 'A', '\xC2': 'A', '\xC3': 'A', '\xC4': 'A', '\xC5': 'A', - '\xE0': 'a', '\xE1': 'a', '\xE2': 'a', '\xE3': 'a', '\xE4': 'a', '\xE5': 'a', - '\xC7': 'C', '\xE7': 'c', - '\xD0': 'D', '\xF0': 'd', - '\xC8': 'E', '\xC9': 'E', '\xCA': 'E', '\xCB': 'E', - '\xE8': 'e', '\xE9': 'e', '\xEA': 'e', '\xEB': 'e', - '\xCC': 'I', '\xCD': 'I', '\xCE': 'I', '\xCF': 'I', - '\xEC': 'i', '\xED': 'i', '\xEE': 'i', '\xEF': 'i', - '\xD1': 'N', '\xF1': 'n', - '\xD2': 'O', '\xD3': 'O', '\xD4': 'O', '\xD5': 'O', '\xD6': 'O', '\xD8': 'O', - '\xF2': 'o', '\xF3': 'o', '\xF4': 'o', '\xF5': 'o', '\xF6': 'o', '\xF8': 'o', - '\xD9': 'U', '\xDA': 'U', '\xDB': 'U', '\xDC': 'U', - '\xF9': 'u', '\xFA': 'u', '\xFB': 'u', '\xFC': 'u', - '\xDD': 'Y', '\xFD': 'y', '\xFF': 'y', - '\xC6': 'AE', '\xE6': 'ae', - '\xDE': 'Th', '\xFE': 'th', - '\xDF': 'ss', '\xD7': ' ', '\xF7': ' ' - }; - - /** Used to determine if values are of the language type `Object` */ + /** Used to determine if values are of the language type Object */ var objectTypes = { 'function': true, 'object': true @@ -212,6 +167,7 @@ "'": "'", '\n': 'n', '\r': 'r', + '\t': 't', '\u2028': 'u2028', '\u2029': 'u2029' }; @@ -236,33 +192,21 @@ /*--------------------------------------------------------------------------*/ - /** - * Used by `_.defaults` to customize its `_.assign` use. - * - * @private - * @param {*} objectValue The destination object property value. - * @param {*} sourceValue The source object property value. - * @returns {*} Returns the value to assign to the destination object. - */ - function assignDefaults(objectValue, sourceValue) { - return typeof objectValue == 'undefined' ? sourceValue : objectValue; - } - /** * The base implementation of `compareAscending` used to compare values and * sort them in ascending order without guaranteeing a stable sort. * * @private - * @param {*} value The value to compare to `other`. - * @param {*} other The value to compare to `value`. - * @returns {number} Returns the sort order indicator for `value`. + * @param {*} a The value to compare to `b`. + * @param {*} b The value to compare to `a`. + * @returns {number} Returns the sort order indicator for `a`. */ - function baseCompareAscending(value, other) { - if (value !== other) { - if (value > other || typeof value == 'undefined') { + function baseCompareAscending(a, b) { + if (a !== b) { + if (a > b || typeof a == 'undefined') { return 1; } - if (value < other || typeof other == 'undefined') { + if (a < b || typeof b == 'undefined') { return -1; } } @@ -276,7 +220,7 @@ * @param {Array} array The array to search. * @param {*} value The value to search for. * @param {number} [fromIndex=0] The index to search from. - * @returns {number} Returns the index of the matched value, else `-1`. + * @returns {number} Returns the index of the matched value or `-1`. */ function baseIndexOf(array, value, fromIndex) { var index = (fromIndex || 0) - 1, @@ -304,24 +248,22 @@ } /** - * Used by `_.max` and `_.min` as the default callback when a given collection - * is a string value. + * Used by `_.max` and `_.min` as the default callback when a given + * collection is a string value. * * @private - * @param {string} string The string to inspect. - * @returns {number} Returns the code unit of the first character of the string. + * @param {string} value The character to inspect. + * @returns {number} Returns the code unit of given character. */ - function charAtCallback(string) { - return string.charCodeAt(0); + function charAtCallback(value) { + return value.charCodeAt(0); } /** - * Used by `_.trim` and `_.trimLeft` to get the index of the first character - * of `string` that is not found in `chars`. + * Gets the index of the first character of `string` that is not found in `chars`. * * @private * @param {string} string The string to inspect. - * @param {string} chars The characters to find. * @returns {number} Returns the index of the first character not found in `chars`. */ function charsLeftIndex(string, chars) { @@ -337,12 +279,10 @@ } /** - * Used by `_.trim` and `_.trimRight` to get the index of the last character - * of `string` that is not found in `chars`. + * Gets the index of the last character of `string` that is not found in `chars`. * * @private * @param {string} string The string to inspect. - * @param {string} chars The characters to find. * @returns {number} Returns the index of the last character not found in `chars`. */ function charsRightIndex(string, chars) { @@ -356,103 +296,69 @@ } /** - * Used by `_.sortBy` to compare transformed elements of a collection and stable + * Used by `sortBy` to compare transformed elements of a collection and stable * sort them in ascending order. * * @private - * @param {Object} object The object to compare to `other`. - * @param {Object} other The object to compare to `object`. - * @returns {number} Returns the sort order indicator for `object`. + * @param {Object} a The object to compare to `b`. + * @param {Object} b The object to compare to `a`. + * @returns {number} Returns the sort order indicator for `a`. */ - function compareAscending(object, other) { - return baseCompareAscending(object.criteria, other.criteria) || object.index - other.index; + function compareAscending(a, b) { + return baseCompareAscending(a.criteria, b.criteria) || a.index - b.index; } /** - * Used by `_.sortBy` to compare multiple properties of each element in a + * Used by `sortBy` to compare multiple properties of each element in a * collection and stable sort them in ascending order. * * @private - * @param {Object} object The object to compare to `other`. - * @param {Object} other The object to compare to `object`. - * @returns {number} Returns the sort order indicator for `object`. + * @param {Object} a The object to compare to `b`. + * @param {Object} b The object to compare to `a`. + * @returns {number} Returns the sort order indicator for `a`. */ - function compareMultipleAscending(object, other) { - var index = -1, - objCriteria = object.criteria, - othCriteria = other.criteria, - length = objCriteria.length; + function compareMultipleAscending(a, b) { + var ac = a.criteria, + bc = b.criteria, + index = -1, + length = ac.length; while (++index < length) { - var result = baseCompareAscending(objCriteria[index], othCriteria[index]); + var result = baseCompareAscending(ac[index], bc[index]); if (result) { return result; } } // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications - // that causes it, under certain circumstances, to provide the same value - // for `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 + // that causes it, under certain circumstances, to provided the same value + // for `a` and `b`. See https://github.com/jashkenas/underscore/pull/1247 // // This also ensures a stable sort in V8 and other engines. // See https://code.google.com/p/v8/issues/detail?id=90 - return object.index - other.index; + return a.index - b.index; } /** - * Creates a function that produces compound words out of the words in a - * given string. + * Used by `escape` to convert characters to HTML entities. * * @private - * @param {Function} callback The function called to combine each word. - * @returns {Function} Returns the new compounder function. - */ - function createCompounder(callback) { - return function(string) { - var index = -1, - words = string != null && String(string).replace(reLatin1, deburrLetter).match(reWords), - length = words ? words.length : 0, - result = ''; - - while (++index < length) { - result = callback(result, words[index], index, words); - } - return result; - }; - } - - /** - * Used by `createCompounder` to convert latin-1 supplement letters to basic - * latin (ASCII) letters. - * - * @private - * @param {string} letter The matched letter to deburr. - * @returns {string} Returns the deburred letter. - */ - function deburrLetter(letter) { - return deburredLetters[letter]; - } - - /** - * Used by `_.escape` to convert characters to HTML entities. - * - * @private - * @param {string} chr The matched character to escape. + * @param {string} match The matched character to escape. * @returns {string} Returns the escaped character. */ - function escapeHtmlChar(chr) { - return htmlEscapes[chr]; + function escapeHtmlChar(match) { + return htmlEscapes[match]; } /** - * Used by `_.template` to escape characters for inclusion in compiled + * Used by `template` to escape characters for inclusion in compiled * string literals. * * @private - * @param {string} chr The matched character to escape. + * @param {string} match The matched character to escape. * @returns {string} Returns the escaped character. */ - function escapeStringChar(chr) { - return '\\' + stringEscapes[chr]; + function escapeStringChar(match) { + return '\\' + stringEscapes[match]; } /** @@ -460,7 +366,7 @@ * * @private * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a DOM node, else `false`. + * @returns {boolean} Returns `true` if the `value` is a DOM node, else `false`. */ function isNode(value) { // IE < 9 presents DOM nodes as `Object` objects except they have `toString` @@ -469,8 +375,70 @@ } /** - * Used by `_.trim` and `_.trimLeft` to get the index of the first non-whitespace - * character of `string`. + * A fallback implementation of `trim` to remove leading and trailing + * whitespace or specified characters from `string`. + * + * @private + * @param {string} string The string to trim. + * @param {string} [chars=whitespace] The characters to trim. + * @returns {string} Returns the trimmed string. + */ + function shimTrim(string, chars) { + string = string == null ? '' : String(string); + if (!string) { + return string; + } + if (chars == null) { + return string.slice(trimmedLeftIndex(string), trimmedRightIndex(string) + 1); + } + chars = String(chars); + return string.slice(charsLeftIndex(string, chars), charsRightIndex(string, chars) + 1); + } + + /** + * A fallback implementation of `trimLeft` to remove leading whitespace or + * specified characters from `string`. + * + * @private + * @param {string} string The string to trim. + * @param {string} [chars=whitespace] The characters to trim. + * @returns {string} Returns the trimmed string. + */ + function shimTrimLeft(string, chars) { + string = string == null ? '' : String(string); + if (!string) { + return string; + } + if (chars == null) { + return string.slice(trimmedLeftIndex(string)) + } + chars = String(chars); + return string.slice(charsLeftIndex(string, chars)); + } + + /** + * A fallback implementation of `trimRight` to remove trailing whitespace or + * specified characters from `string`. + * + * @private + * @param {string} string The string to trim. + * @param {string} [chars=whitespace] The characters to trim. + * @returns {string} Returns the trimmed string. + */ + function shimTrimRight(string, chars) { + string = string == null ? '' : String(string); + if (!string) { + return string; + } + if (chars == null) { + return string.slice(0, trimmedRightIndex(string) + 1) + } + chars = String(chars); + return string.slice(0, charsRightIndex(string, chars) + 1); + } + + /** + * Gets the index of the first non-whitespace character of `string`. * * @private * @param {string} string The string to inspect. @@ -491,8 +459,7 @@ } /** - * Used by `_.trim` and `_.trimRight` to get the index of the last non-whitespace - * character of `string`. + * Gets the index of the last non-whitespace character of `string`. * * @private * @param {string} string The string to inspect. @@ -511,20 +478,20 @@ } /** - * Used by `_.unescape` to convert HTML entities to characters. + * Used by `unescape` to convert HTML entities to characters. * * @private - * @param {string} chr The matched character to unescape. + * @param {string} match The matched character to unescape. * @returns {string} Returns the unescaped character. */ - function unescapeHtmlChar(chr) { - return htmlUnescapes[chr]; + function unescapeHtmlChar(match) { + return htmlUnescapes[match]; } /*--------------------------------------------------------------------------*/ /** - * Create a new `lodash` function using the given `context` object. + * Create a new `lodash` function using the given context object. * * @static * @memberOf _ @@ -561,23 +528,17 @@ /** Used to detect DOM support */ var document = (document = context.window) && document.document; - /** Used to restore the original `_` reference in `_.noConflict` */ + /** Used to restore the original `_` reference in `noConflict` */ var oldDash = context._; - /** - * Used as the maximum length of an array-like object. - * See the [ES6 spec](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength) - * for more details. - */ - var maxSafeInteger = Math.pow(2, 53) - 1; - - /** Used to resolve the internal `[[Class]]` of values */ + /** Used to resolve the internal [[Class]] of values */ var toString = objectProto.toString; /** Used to detect if a method is native */ var reNative = RegExp('^' + - escapeRegExp(toString) - .replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' + String(toString) + .replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + .replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' ); /** Native method shortcuts */ @@ -594,7 +555,7 @@ splice = arrayRef.splice, unshift = arrayRef.unshift; - /** Used to set metadata on functions */ + /** Used to set meta data on functions */ var defineProperty = (function() { // IE 8 only accepts DOM elements try { @@ -616,9 +577,12 @@ nativeMin = Math.min, nativeNow = isNative(nativeNow = Date.now) && nativeNow, nativeParseInt = context.parseInt, - nativeRandom = Math.random; + nativeRandom = Math.random, + nativeTrim = isNative(nativeTrim = stringProto.trim) && !nativeTrim.call(whitespace) && nativeTrim, + nativeTrimLeft = isNative(nativeTrimLeft = stringProto.trimLeft) && !nativeTrimLeft.call(whitespace) && nativeTrimLeft, + nativeTrimRight = isNative(nativeTrimRight = stringProto.trimRight) && !nativeTrimRight.call(whitespace) && nativeTrimRight; - /** Used to lookup built-in constructors by `[[Class]]` */ + /** Used to lookup a built-in constructor by [[Class]] */ var ctorByClass = {}; ctorByClass[arrayClass] = Array; ctorByClass[boolClass] = Boolean; @@ -629,7 +593,7 @@ ctorByClass[regexpClass] = RegExp; ctorByClass[stringClass] = String; - /** Used to avoid iterating over non-enumerable properties in IE < 9 */ + /** Used to avoid iterating non-enumerable properties in IE < 9 */ var nonEnumProps = {}; nonEnumProps[arrayClass] = nonEnumProps[dateClass] = nonEnumProps[numberClass] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true }; nonEnumProps[boolClass] = nonEnumProps[stringClass] = { 'constructor': true, 'toString': true, 'valueOf': true }; @@ -662,18 +626,18 @@ * implicitly or explicitly included in the build. * * The chainable wrapper functions are: - * `after`, `assign`, `at`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, + * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, * `compose`, `concat`, `constant`, `countBy`, `create`, `createCallback`, * `curry`, `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, * `flatten`, `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, * `forOwnRight`, `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, * `invert`, `invoke`, `keys`, `map`, `mapValues`, `matches`, `max`, `memoize`, - * `merge`, `min`, `mixin`, `noop`, `object`, `omit`, `once`, `pairs`, - * `partial`, `partialRight`, `pick`, `pluck`, `property`, `pull`, `push`, - * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, - * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`, - * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`, - * `xor`, and `zip` + * `merge`, `min`, `noop`, `object`, `omit`, `once`, `pairs`, `partial`, + * `partialRight`, `pick`, `pluck`, `property`, `pull`, `push`, `range`, + * `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, `sortBy`, + * `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`, `union`, + * `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`, `xor`, + * and `zip` * * The non-chainable wrapper functions are: * `capitalize`, `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, @@ -681,10 +645,10 @@ * `identity`, `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, * `isElement`, `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, * `isNull`, `isNumber`, `isObject`, `isPlainObject`, `isRegExp`, `isString`, - * `isUndefined`, `join`, `lastIndexOf`, `noConflict`, `now`, `parseInt`, - * `pop`, `random`, `reduce`, `reduceRight`, `result`, `shift`, `size`, `some`, - * `sortedIndex`, `runInContext`, `template`, `trim`, `trimLeft`, `trimRight`, - * `unescape`, `uniqueId`, and `value` + * `isUndefined`, `join`, `lastIndexOf`, `mixin`, `noConflict`, `now`, + * `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, `result`, `shift`, + * `size`, `some`, `sortedIndex`, `runInContext`, `template`, `trim`, + * `trimLeft`, `trimRight`, `unescape`, `uniqueId`, and `value` * * The wrapper functions `first`, `last`, and `sample` return wrapped values * when `n` is provided, otherwise they return unwrapped values. @@ -729,7 +693,7 @@ * * @private * @param {*} value The value to wrap in a `lodash` instance. - * @param {boolean} [chainAll=false] A flag to enable chaining for all methods. + * @param {boolean} [chainAll=false] A flag to enable chaining for all methods * @returns {Object} Returns a `lodash` instance. */ function lodashWrapper(value, chainAll) { @@ -748,19 +712,17 @@ */ var support = lodash.support = {}; - (function(x) { + (function() { var ctor = function() { this.x = 1; }, object = { '0': 1, 'length': 1 }, props = []; ctor.prototype = { 'valueOf': 1, 'y': 1 }; for (var key in new ctor) { props.push(key); } - for (var argsKey in arguments) { } - for (var strKey in 'x') { } + for (key in arguments) { } /** - * Detect if the `[[Class]]` of `arguments` objects is resolvable - * (all but Firefox < 4, IE < 9). + * Detect if an `arguments` object's [[Class]] is resolvable (all but Firefox < 4, IE < 9). * * @memberOf _.support * @type boolean @@ -768,8 +730,7 @@ support.argsClass = toString.call(arguments) == argsClass; /** - * Detect if `arguments` objects are `Object` objects - * (all but Narwhal and Opera < 10.5). + * Detect if `arguments` objects are `Object` objects (all but Narwhal and Opera < 10.5). * * @memberOf _.support * @type boolean @@ -778,21 +739,20 @@ /** * Detect if `name` or `message` properties of `Error.prototype` are - * enumerable by default (IE < 9, Safari < 5.1). + * enumerable by default. (IE < 9, Safari < 5.1) * * @memberOf _.support * @type boolean */ - support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') || - propertyIsEnumerable.call(errorProto, 'name'); + support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') || propertyIsEnumerable.call(errorProto, 'name'); /** * Detect if `prototype` properties are enumerable by default. * * Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 * (if the prototype or a property on the prototype has been set) - * incorrectly sets the `[[Enumerable]]` value of a function's `prototype` - * property to `true`. + * incorrectly sets a function's `prototype` property [[Enumerable]] + * value to `true`. * * @memberOf _.support * @type boolean @@ -801,8 +761,7 @@ /** * Detect if functions can be decompiled by `Function#toString` - * (all but Firefox OS certified apps, older Opera mobile browsers, and - * the PlayStation 3; forced `false` for Windows 8 apps). + * (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps). * * @memberOf _.support * @type boolean @@ -818,20 +777,19 @@ support.funcNames = typeof Function.name == 'string'; /** - * Detect if string indexes are non-enumerable - * (IE < 9, RingoJS, Rhino, Narwhal). + * Detect if `arguments` object indexes are non-enumerable + * (Firefox < 4, IE < 9, PhantomJS, Safari < 5.1). * * @memberOf _.support * @type boolean */ - support.nonEnumStrings = strKey != '0'; + support.nonEnumArgs = key != 0; /** - * Detect if properties shadowing those on `Object.prototype` are - * non-enumerable. + * Detect if properties shadowing those on `Object.prototype` are non-enumerable. * - * In IE < 9 an object's own properties, shadowing non-enumerable ones, - * are made non-enumerable as well (a.k.a the JScript `[[DontEnum]]` bug). + * In IE < 9 an objects own properties, shadowing non-enumerable ones, are + * made non-enumerable as well (a.k.a the JScript [[DontEnum]] bug). * * @memberOf _.support * @type boolean @@ -839,8 +797,7 @@ support.nonEnumShadows = !/valueOf/.test(props); /** - * Detect if own properties are iterated after inherited properties - * (all but IE < 9). + * Detect if own properties are iterated after inherited properties (all but IE < 9). * * @memberOf _.support * @type boolean @@ -848,15 +805,13 @@ support.ownLast = props[0] != 'x'; /** - * Detect if `Array#shift` and `Array#splice` augment array-like objects - * correctly. + * Detect if `Array#shift` and `Array#splice` augment array-like objects correctly. * * Firefox < 10, IE compatibility mode, and IE < 9 have buggy Array `shift()` * and `splice()` functions that fail to remove the last element, `value[0]`, * of array-like objects even though the `length` property is set to `0`. * The `shift()` method is buggy in IE 8 compatibility mode, while `splice()` - * is buggy regardless of mode in IE < 9 and buggy in compatibility mode - * in IE 9. + * is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9. * * @memberOf _.support * @type boolean @@ -866,8 +821,8 @@ /** * Detect lack of support for accessing string characters by index. * - * IE < 8 can't access characters by index. IE 8 can only access characters - * by index on string literals, not string objects. + * IE < 8 can't access characters by index and IE 8 can only access + * characters by index on string literals. * * @memberOf _.support * @type boolean @@ -887,9 +842,9 @@ } /** - * Detect if the `[[Class]]` of DOM nodes is resolvable (all but IE < 9) - * and that the JS engine errors when attempting to coerce an object to a - * string without a `toString` function. + * Detect if a DOM node's [[Class]] is resolvable (all but IE < 9) + * and that the JS engine errors when attempting to coerce an object to + * a string without a `toString` function. * * @memberOf _.support * @type boolean @@ -899,31 +854,12 @@ } catch(e) { support.nodeClass = true; } - - /** - * Detect if `arguments` object indexes are non-enumerable. - * - * In Firefox < 4, IE < 9, PhantomJS, and Safari < 5.1 `arguments` object - * indexes are non-enumerable. Chrome < 25 and Node.js < 0.11.0 treat - * `arguments` object indexes as non-enumerable and fail `hasOwnProperty` - * checks for indexes that exceed their function's formal parameters with - * associated values of `0`. - * - * @memberOf _.support - * @type boolean - */ - try { - support.nonEnumArgs = !(argsKey == '1' && hasOwnProperty.call(arguments, argsKey) && - propertyIsEnumerable.call(arguments, argsKey)); - } catch(e) { - support.nonEnumArgs = true; - } - }(0, 0)); + }(1)); /** - * By default, the template delimiters used by Lo-Dash are similar to those - * in embedded Ruby (ERB). Change the following template settings to use - * alternative delimiters. + * By default, the template delimiters used by Lo-Dash are similar to those in + * embedded Ruby (ERB). Change the following template settings to use alternative + * delimiters. * * @static * @memberOf _ @@ -984,68 +920,88 @@ /*--------------------------------------------------------------------------*/ /** - * A specialized version of `_.forEach` for arrays without support for - * callback shorthands or `this` binding. + * The template used to create iterator functions. * * @private - * @param {Array} array The array to iterate over. - * @param {Function} callback The function called per iteration. - * @returns {Array} Returns `array`. + * @param {Object} data The data object used to populate the text. + * @returns {string} Returns the interpolated text. */ - function arrayEach(array, callback) { - var index = -1, - length = array ? array.length : 0; + var iteratorTemplate = template( + // assign the `result` variable an initial value + 'var result = <%= init %>;\n' + - while (++index < length) { - if (callback(array[index], index, array) === false) { - break; - } - } - return array; - } + // exit early if the first argument is not an object + "if (!isObject(object)) {\n" + + ' return result;\n' + + '}' + - /** - * A specialized version of `_.forEachRight` for arrays without support for - * callback shorthands or `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} callback The function called per iteration. - * @returns {Array} Returns `array`. - */ - function arrayEachRight(array, callback) { - var length = array ? array.length : 0; - while (length--) { - if (callback(array[length], length, array) === false) { - break; - } - } - return array; - } + // add support for iterating over `arguments` objects if needed + '<% if (support.nonEnumArgs) { %>\n' + + 'var length = object.length;\n' + + 'if (length && isArguments(object)) {\n' + + ' key = -1;\n' + + ' while (++key < length) {\n' + + " key += '';\n" + + ' <%= loop %>;\n' + + ' }\n' + + ' return result;\n' + + '}' + + '<% } %>' + - /** - * A specialized version of `_.map` for arrays without support for callback - * shorthands or `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} callback The function called per iteration. - * @returns {Array} Returns the new mapped array. - */ - function arrayMap(array, callback) { - var index = -1, - length = array ? array.length >>> 0 : 0, - result = Array(length); + // avoid iterating over `prototype` properties in older Firefox, Opera, and Safari + '<% if (support.enumPrototypes) { %>\n' + + "var skipProto = typeof object == 'function';\n" + + '<% } %>' + - while (++index < length) { - result[index] = callback(array[index], index, array); - } - return result; - } + // avoid iterating over `Error.prototype` properties in older IE and Safari + '<% if (support.enumErrorProps) { %>\n' + + 'var skipErrorProps = object === errorProto || object instanceof Error;\n' + + '<% } %>' + + + // define conditions used in the loop + '<%' + + 'var conditions = [];\n' + + "if (support.enumPrototypes) { conditions.push('!(skipProto && key == \\'prototype\\')'); }\n" + + "if (support.enumErrorProps) { conditions.push('!(skipErrorProps && (key == \\'message\\' || key == \\'name\\'))'); }" + + '%>\n' + + + // iterate over the object + 'for (var key in object) {\n<%' + + " if (useHas) { conditions.push('hasOwnProperty.call(object, key)'); }\n" + + " if (conditions.length) { %> if (<%= conditions.join(' && ') %>) {\n <% } %>" + + ' <%= loop %>;' + + ' <% if (conditions.length) { %>\n }<% } %>\n' + + '}\n' + + + // Lo-Dash skips the `constructor` property when it infers it's iterating + // over a `prototype` object because IE < 9 can't set the `[[Enumerable]]` + // attribute of an existing property and the `constructor` property of a + // prototype defaults to non-enumerable. + '<% if (support.nonEnumShadows) { %>\n' + + 'if (object !== objectProto) {\n' + + " var ctor = object.constructor,\n" + + ' isProto = object === (ctor && ctor.prototype),\n' + + ' className = object === stringProto ? stringClass : object === errorProto ? errorClass : toString.call(object),\n' + + ' nonEnum = nonEnumProps[className];\n' + + ' <% for (var index = 0; index < 7; index++) { %>\n' + + " key = '<%= shadowedProps[index] %>';\n" + + ' if ((!(isProto && nonEnum[key]) && hasOwnProperty.call(object, key))<%' + + ' if (!useHas) { %> || (!nonEnum[key] && object[key] !== objectProto[key])<% }' + + ' %>) {\n' + + ' <%= loop %>;\n' + + ' }' + + ' <% } %>\n' + + '}' + + '<% } %>\n' + + + 'return result;' + ); + + /*--------------------------------------------------------------------------*/ /** * The base implementation of `_.bind` that creates the bound function and - * sets its metadata. + * sets its meta data. * * @private * @param {Array} data The metadata array. @@ -1064,8 +1020,8 @@ // avoid `arguments` object use disqualifying optimizations by // converting it to an array before passing it to `composeArgs` var index = -1, - length = arguments.length, - args = Array(length); + length = arguments.length, + args = Array(length); while (++index < length) { args[index] = arguments[index]; @@ -1089,7 +1045,7 @@ /** * The base implementation of `_.clone` without argument juggling or support - * for `this` binding. + * for `thisArg` binding. * * @private * @param {*} value The value to clone. @@ -1106,6 +1062,7 @@ return result; } } + // inspect [[Class]] var isObj = isObject(value); if (isObj) { var className = toString.call(value); @@ -1133,6 +1090,7 @@ var isArr = isArray(value); if (isDeep) { // check for circular references and return corresponding clone + var initedStack = !stackA; stackA || (stackA = []); stackB || (stackB = []); @@ -1166,8 +1124,8 @@ stackB.push(result); // recursively populate clone (susceptible to call stack limits) - (isArr ? arrayEach : baseForOwn)(value, function(valValue, key) { - result[key] = baseClone(valValue, isDeep, callback, stackA, stackB); + (isArr ? baseEach : baseForOwn)(value, function(objValue, key) { + result[key] = baseClone(objValue, isDeep, callback, stackA, stackB); }); return result; @@ -1207,7 +1165,7 @@ * @param {*} [func=identity] The value to convert to a callback. * @param {*} [thisArg] The `this` binding of the created callback. * @param {number} [argCount] The number of arguments the callback accepts. - * @returns {Function} Returns the new function. + * @returns {Function} Returns a callback function. */ function baseCreateCallback(func, thisArg, argCount) { if (typeof func != 'function') { @@ -1243,8 +1201,8 @@ case 1: return function(value) { return func.call(thisArg, value); }; - case 2: return function(value, other) { - return func.call(thisArg, value, other); + case 2: return function(a, b) { + return func.call(thisArg, a, b); }; case 3: return function(value, index, collection) { return func.call(thisArg, value, index, collection); @@ -1258,7 +1216,7 @@ /** * The base implementation of `createWrapper` that creates the wrapper and - * sets its metadata. + * sets its meta data. * * @private * @param {Array} data The metadata array. @@ -1294,19 +1252,14 @@ if (partialRightArgs) { args = composeArgsRight(partialRightArgs, partialRightHolders, args); } - if (isCurry) { - var newPartialHolders = getHolders(args); - length -= newPartialHolders.length; - - if (length < arity) { - bitmask |= PARTIAL_FLAG; - bitmask &= ~PARTIAL_RIGHT_FLAG - if (!isCurryBound) { - bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG); - } - var newArity = nativeMax(arity - length, 0); - return baseCreateWrapper([func, bitmask, newArity, thisArg, args, null, newPartialHolders]); + if (isCurry && length < arity) { + bitmask |= PARTIAL_FLAG; + bitmask &= ~PARTIAL_RIGHT_FLAG + if (!isCurryBound) { + bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG); } + var newArity = nativeMax(0, arity - length); + return baseCreateWrapper([func, bitmask, newArity, thisArg, args, null, []]); } var thisBinding = isBind ? thisArg : this; if (isBindKey) { @@ -1330,7 +1283,7 @@ * @private * @param {Array} array The array to process. * @param {Array} [values] The array of values to exclude. - * @returns {Array} Returns the new array of filtered values. + * @returns {Array} Returns a new array of filtered values. */ function baseDifference(array, values) { var length = array ? array.length : 0; @@ -1371,7 +1324,7 @@ /** * The base implementation of `_.forEach` without support for callback - * shorthands or `this` binding. + * shorthands or `thisArg` binding. * * @private * @param {Array|Object|string} collection The collection to iterate over. @@ -1383,7 +1336,7 @@ iterable = collection, length = collection ? collection.length : 0; - if (typeof length == 'number' && length > -1 && length <= maxSafeInteger) { + if (typeof length == 'number') { if (support.unindexedChars && isString(iterable)) { iterable = iterable.split(''); } @@ -1400,7 +1353,7 @@ /** * The base implementation of `_.forEachRight` without support for callback - * shorthands or `this` binding. + * shorthands or `thisArg` binding. * * @private * @param {Array|Object|string} collection The collection to iterate over. @@ -1411,7 +1364,7 @@ var iterable = collection, length = collection ? collection.length : 0; - if (typeof length == 'number' && length > -1 && length <= maxSafeInteger) { + if (typeof length == 'number') { if (support.unindexedChars && isString(iterable)) { iterable = iterable.split(''); } @@ -1426,41 +1379,16 @@ return collection; } - /** - * The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey` - * without support for callback shorthands or `this` binding which iterates - * over `collection` using the provided `eachFunc`. - * - * @private - * @param {Array|Object|string} collection The collection to search. - * @param {Function} predicate The function called per iteration. - * @param {Function} eachFunc The function to iterate over the collection. - * @param {boolean} [retKey=false] A flag to indicate returning the key of - * the found element instead of the element itself. - * @returns {*} Returns the found element or its key, else `undefined`. - */ - function baseFind(collection, predicate, eachFunc, retKey) { - var result; - - eachFunc(collection, function(value, key, collection) { - if (predicate(value, key, collection)) { - result = retKey ? key : value; - return false; - } - }); - return result; - } - /** * The base implementation of `_.flatten` without support for callback - * shorthands or `this` binding. + * shorthands or `thisArg` binding. * * @private * @param {Array} array The array to flatten. * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level. * @param {boolean} [isStrict=false] A flag to restrict flattening to arrays and `arguments` objects. * @param {number} [fromIndex=0] The index to start from. - * @returns {Array} Returns the new flattened array. + * @returns {Array} Returns a new flattened array. */ function baseFlatten(array, isShallow, isStrict, fromIndex) { var index = (fromIndex || 0) - 1, @@ -1492,20 +1420,17 @@ } /** - * The base implementation of `baseForIn` and `baseForOwn` which iterates - * over `object` properties returned by `keysFunc` executing the callback - * for each property. Callbacks may exit iteration early by explicitly - * returning `false`. + * The base implementation of `_.forOwn` without support for callback + * shorthands or `thisArg` binding. * * @private * @param {Object} object The object to iterate over. * @param {Function} callback The function called per iteration. - * @param {Function} keysFunc The function to get the keys of `object`. * @returns {Object} Returns `object`. */ - function baseFor(object, callback, keysFunc) { + function baseForOwn(object, callback) { var index = -1, - props = keysFunc(object), + props = keys(object), length = props.length; while (++index < length) { @@ -1518,17 +1443,16 @@ } /** - * This function is like `baseFor` except that it iterates over properties - * in the opposite order. + * The base implementation of `_.forOwnRight` without support for callback + * shorthands or `thisArg` binding. * * @private * @param {Object} object The object to iterate over. * @param {Function} callback The function called per iteration. - * @param {Function} keysFunc The function to get the keys of `object`. * @returns {Object} Returns `object`. */ - function baseForRight(object, callback, keysFunc) { - var props = keysFunc(object), + function baseForOwnRight(object, callback) { + var props = keys(object), length = props.length; while (length--) { @@ -1541,190 +1465,137 @@ } /** - * The base implementation of `_.forIn` without support for callback - * shorthands or `this` binding. + * The base implementation of `_.isEqual`, without support for `thisArg` binding, + * that allows partial "_.where" style comparisons. * * @private - * @param {Object} object The object to iterate over. - * @param {Function} callback The function called per iteration. - * @returns {Object} Returns `object`. - */ - function baseForIn(object, callback) { - return baseFor(object, callback, keysIn); - } - - /** - * The base implementation of `_.forOwn` without support for callback - * shorthands or `this` binding. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} callback The function called per iteration. - * @returns {Object} Returns `object`. - */ - function baseForOwn(object, callback) { - return baseFor(object, callback, keys); - } - - /** - * The base implementation of `_.forOwnRight` without support for callback - * shorthands or `this` binding. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} callback The function called per iteration. - * @returns {Object} Returns `object`. - */ - function baseForOwnRight(object, callback) { - return baseForRight(object, callback, keys); - } - - /** - * The base implementation of `_.isEqual`, without support for `thisArg` - * binding, that allows partial "_.where" style comparisons. - * - * @private - * @param {*} value The value to compare to `other`. - * @param {*} other The value to compare to `value`. + * @param {*} a The value to compare. + * @param {*} b The other value to compare. * @param {Function} [callback] The function to customize comparing values. * @param {Function} [isWhere=false] A flag to indicate performing partial comparisons. - * @param {Array} [stackA=[]] Tracks traversed `value` objects. - * @param {Array} [stackB=[]] Tracks traversed `other` objects. + * @param {Array} [stackA=[]] Tracks traversed `a` objects. + * @param {Array} [stackB=[]] Tracks traversed `b` objects. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. */ - function baseIsEqual(value, other, callback, isWhere, stackA, stackB) { + function baseIsEqual(a, b, callback, isWhere, stackA, stackB) { if (callback) { - var result = callback(value, other); + var result = callback(a, b); if (typeof result != 'undefined') { return !!result; } } // exit early for identical values - if (value === other) { + if (a === b) { // treat `+0` vs. `-0` as not equal - return value !== 0 || (1 / value == 1 / other); + return a !== 0 || (1 / a == 1 / b); } - var valType = typeof value, - othType = typeof other; + var type = typeof a, + otherType = typeof b; // exit early for unlike primitive values - if (value === value && (value == null || other == null || - (valType != 'function' && valType != 'object' && othType != 'function' && othType != 'object'))) { + if (a === a && (a == null || b == null || + (type != 'function' && type != 'object' && otherType != 'function' && otherType != 'object'))) { return false; } - var valClass = toString.call(value), - othClass = toString.call(other), - valIsArg = valClass == argsClass, - othIsArg = othClass == argsClass; + // compare [[Class]] names + var className = toString.call(a), + otherClass = toString.call(b); - if (valIsArg) { - valClass = objectClass; + if (className == argsClass) { + className = objectClass; } - if (othIsArg) { - othClass = objectClass; + if (otherClass == argsClass) { + otherClass = objectClass; } - if (valClass != othClass) { + if (className != otherClass) { return false; } - switch (valClass) { + switch (className) { case boolClass: case dateClass: // coerce dates and booleans to numbers, dates to milliseconds and booleans // to `1` or `0` treating invalid dates coerced to `NaN` as not equal - return +value == +other; + return +a == +b; case numberClass: // treat `NaN` vs. `NaN` as equal - return (value != +value) - ? other != +other + return (a != +a) + ? b != +b // but treat `-0` vs. `+0` as not equal - : (value == 0 ? (1 / value == 1 / other) : value == +other); + : (a == 0 ? (1 / a == 1 / b) : a == +b); case regexpClass: case stringClass: // coerce regexes to strings (http://es5.github.io/#x15.10.6.4) // treat string primitives and their corresponding object instances as equal - return value == String(other); + return a == String(b); } - var isArr = valClass == arrayClass; + var isArr = className == arrayClass; if (!isArr) { - // exit for functions and DOM nodes - if (valClass != objectClass || (!support.nodeClass && (isNode(value) || isNode(other)))) { - return false; - } // unwrap any `lodash` wrapped values - var valWrapped = hasOwnProperty.call(value, '__wrapped__'), - othWrapped = hasOwnProperty.call(other, '__wrapped__'); + var aWrapped = hasOwnProperty.call(a, '__wrapped__'), + bWrapped = hasOwnProperty.call(b, '__wrapped__'); - if (valWrapped || othWrapped) { - return baseIsEqual(valWrapped ? value.__wrapped__ : value, othWrapped ? other.__wrapped__ : other, callback, isWhere, stackA, stackB); + if (aWrapped || bWrapped) { + return baseIsEqual(aWrapped ? a.__wrapped__ : a, bWrapped ? b.__wrapped__ : b, callback, isWhere, stackA, stackB); } - if (!support.argsObject) { - valIsArg = isArguments(value); - othIsArg = isArguments(other); - } - var hasValCtor = !valIsArg && hasOwnProperty.call(value, 'constructor'), - hasOthCtor = !othIsArg && hasOwnProperty.call(other, 'constructor'); - - if (hasValCtor != hasOthCtor) { + // exit for functions and DOM nodes + if (className != objectClass || (!support.nodeClass && (isNode(a) || isNode(b)))) { return false; } - if (!hasValCtor) { - // in older versions of Opera, `arguments` objects have `Array` constructors - var valCtor = valIsArg ? Object : value.constructor, - othCtor = othIsArg ? Object : other.constructor; + // in older versions of Opera, `arguments` objects have `Array` constructors + var ctorA = !support.argsObject && isArguments(a) ? Object : a.constructor, + ctorB = !support.argsObject && isArguments(b) ? Object : b.constructor; - // non `Object` object instances with different constructors are not equal - if (valCtor != othCtor && - !(isFunction(valCtor) && valCtor instanceof valCtor && isFunction(othCtor) && othCtor instanceof othCtor) && - ('constructor' in value && 'constructor' in other) - ) { - return false; - } + // non `Object` object instances with different constructors are not equal + if (ctorA != ctorB && + !(hasOwnProperty.call(a, 'constructor') && hasOwnProperty.call(b, 'constructor')) && + !(isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB) && + ('constructor' in a && 'constructor' in b) + ) { + return false; } } // assume cyclic structures are equal // the algorithm for detecting cyclic structures is adapted from ES 5.1 // section 15.12.3, abstract operation `JO` (http://es5.github.io/#x15.12.3) + var initedStack = !stackA; stackA || (stackA = []); stackB || (stackB = []); var length = stackA.length; while (length--) { - if (stackA[length] == value) { - return stackB[length] == other; + if (stackA[length] == a) { + return stackB[length] == b; } } var size = 0; result = true; - // add `value` and `other` to the stack of traversed objects - stackA.push(value); - stackB.push(other); + // add `a` and `b` to the stack of traversed objects + stackA.push(a); + stackB.push(b); // recursively compare objects and arrays (susceptible to call stack limits) if (isArr) { // compare lengths to determine if a deep comparison is necessary - length = value.length; - size = other.length; + length = a.length; + size = b.length; result = size == length; if (result || isWhere) { // deep compare the contents, ignoring non-numeric properties while (size--) { var index = length, - othValue = other[size]; + value = b[size]; if (isWhere) { while (index--) { - if ((result = baseIsEqual(value[index], othValue, callback, isWhere, stackA, stackB))) { + if ((result = baseIsEqual(a[index], value, callback, isWhere, stackA, stackB))) { break; } } - if (!result) { - break; - } - } else if (!(result = baseIsEqual(value[size], othValue, callback, isWhere, stackA, stackB))) { + } else if (!(result = baseIsEqual(a[size], value, callback, isWhere, stackA, stackB))) { break; } } @@ -1733,20 +1604,20 @@ else { // deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys` // which, in this case, is more costly - baseForIn(other, function(othValue, key, other) { - if (hasOwnProperty.call(other, key)) { + baseForIn(b, function(value, key, b) { + if (hasOwnProperty.call(b, key)) { // count the number of properties. size++; // deep compare each property value. - return (result = hasOwnProperty.call(value, key) && baseIsEqual(value[key], othValue, callback, isWhere, stackA, stackB)); + return (result = hasOwnProperty.call(a, key) && baseIsEqual(a[key], value, callback, isWhere, stackA, stackB)); } }); if (result && !isWhere) { // ensure both objects have the same number of properties - baseForIn(value, function(valValue, key, value) { - if (hasOwnProperty.call(value, key)) { - // `size` will be `-1` if `value` has more properties than `other` + baseForIn(a, function(value, key, a) { + if (hasOwnProperty.call(a, key)) { + // `size` will be `-1` if `a` has more properties than `b` return (result = --size > -1); } }); @@ -1760,7 +1631,7 @@ /** * The base implementation of `_.merge` without argument juggling or support - * for `this` binding. + * for `thisArg` binding. * * @private * @param {Object} object The destination object. @@ -1770,7 +1641,7 @@ * @param {Array} [stackB=[]] Associates values with source counterparts. */ function baseMerge(object, source, callback, stackA, stackB) { - (isArray(source) ? arrayEach : baseForOwn)(source, function(source, key) { + (isArray(source) ? baseEach : baseForOwn)(source, function(source, key) { var found, isArr, result = source, @@ -1830,7 +1701,7 @@ * @private * @param {number} min The minimum possible value. * @param {number} max The maximum possible value. - * @returns {number} Returns the random number. + * @returns {number} Returns a random number. */ function baseRandom(min, max) { return min + floor(nativeRandom() * (max - min + 1)); @@ -1838,13 +1709,13 @@ /** * The base implementation of `_.uniq` without support for callback shorthands - * or `this` binding. + * or `thisArg` binding. * * @private * @param {Array} array The array to process. * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted. * @param {Function} [callback] The function called per iteration. - * @returns {Array} Returns the new duplicate-value-free array. + * @returns {Array} Returns a duplicate-value-free array. */ function baseUniq(array, isSorted, callback) { var length = array ? array.length : 0; @@ -1897,37 +1768,15 @@ return result; } - /** - * The base implementation of `_.values` and `_.valuesIn` which creates an - * array of `object` property values corresponding to the property names - * returned by `keysFunc`. - * - * @private - * @param {Object} object The object to inspect. - * @param {Function} keysFunc The function to get the keys of `object`. - * @returns {Object} Returns the array of property values. - */ - function baseValues(object, keysFunc) { - var index = -1, - props = keysFunc(object), - length = props.length, - result = Array(length); - - while (++index < length) { - result[index] = object[props[index]]; - } - return result; - } - /** * Creates an array that is the composition of partially applied arguments, * placeholders, and provided arguments into a single array of arguments. * * @private - * @param {Array} partialArgs An array of arguments to prepend to those provided. + * @param {Array} partialArg An array of arguments to prepend to those provided. * @param {Array} partialHolders An array of `partialArgs` placeholder indexes. * @param {Array|Object} args The provided arguments. - * @returns {Array} Returns the new array of composed arguments. + * @returns {Array} Returns a new array of composed arguments. */ function composeArgs(partialArgs, partialHolders, args) { var holdersLength = partialHolders.length, @@ -1954,10 +1803,10 @@ * is tailored for `_.partialRight`. * * @private - * @param {Array} partialRightArgs An array of arguments to append to those provided. + * @param {Array} partialRightArg An array of arguments to append to those provided. * @param {Array} partialHolders An array of `partialRightArgs` placeholder indexes. * @param {Array|Object} args The provided arguments. - * @returns {Array} Returns the new array of composed arguments. + * @returns {Array} Returns a new array of composed arguments. */ function composeArgsRight(partialRightArgs, partialRightHolders, args) { var holdersIndex = -1, @@ -1982,22 +1831,22 @@ } /** - * Creates a function that aggregates a collection, creating an accumulator - * object composed from the results of running each element in the collection - * through a callback. The given setter function sets the keys and values of - * the accumulator object. If `initializer` is provided will be used to - * initialize the accumulator object. + * Creates a function that aggregates a collection, creating an object or + * array composed from the results of running each element of the collection + * through a callback. The given `setter` function sets the keys and values + * of the composed object or array. * * @private - * @param {Function} setter The function to set keys and values of the accumulator object. - * @param {Function} [initializer] The function to initialize the accumulator object. + * @param {Function} setter The setter function. + * @param {boolean} [retArray=false] A flag to indicate that the aggregator + * function should return an array. * @returns {Function} Returns the new aggregator function. */ - function createAggregator(setter, initializer) { + function createAggregator(setter, retArray) { return function(collection, callback, thisArg) { - var result = initializer ? initializer() : {}; - callback = lodash.createCallback(callback, thisArg, 3); + var result = retArray ? [[], []] : {}; + callback = lodash.createCallback(callback, thisArg, 3); if (isArray(collection)) { var index = -1, length = collection.length; @@ -2020,7 +1869,7 @@ * * @private * @param {Array} [array=[]] The array to search. - * @returns {Object} Returns the new cache object. + * @returns {Object} Returns the cache object. */ var createCache = Set && function(array) { var cache = new Set, @@ -2034,31 +1883,8 @@ }; /** - * Creates the pad required for `string` based on the given padding length. - * The `chars` string may be truncated if the number of padding characters - * exceeds the padding length. - * - * @private - * @param {string} string The string to create padding for. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the pad for `string`. - */ - function createPad(string, length, chars) { - var strLength = string.length; - length = +length; - - if (strLength >= length || !nativeIsFinite(length)) { - return ''; - } - var padLength = length - strLength; - chars = chars == null ? ' ' : String(chars); - return repeat(chars, ceil(padLength / chars.length)).slice(0, padLength); - } - - /** - * Creates a function that either curries or invokes `func` with an optional - * `this` binding and partially applied arguments. + * Creates a function that, when called, either curries or invokes `func` + * with an optional `this` binding and partially applied arguments. * * @private * @param {Function|string} func The function or method name to reference. @@ -2076,16 +1902,18 @@ * provided to the new function. * @param {Array} [partialRightArgs] An array of arguments to append to those * provided to the new function. + * @param {Array} [partialHolders] An array of `partialArgs` placeholder indexes. + * @param {Array} [partialRightHolders] An array of `partialRightArgs` placeholder indexes. * @returns {Function} Returns the new function. */ - function createWrapper(func, bitmask, arity, thisArg, partialArgs, partialRightArgs) { + function createWrapper(func, bitmask, arity, thisArg, partialArgs, partialRightArgs, partialHolders, partialRightHolders) { var isBind = bitmask & BIND_FLAG, isBindKey = bitmask & BIND_KEY_FLAG, isPartial = bitmask & PARTIAL_FLAG, isPartialRight = bitmask & PARTIAL_RIGHT_FLAG; if (!isBindKey && !isFunction(func)) { - throw new TypeError(funcErrorText); + throw new TypeError; } if (isPartial && !partialArgs.length) { bitmask &= ~PARTIAL_FLAG; @@ -2141,17 +1969,17 @@ data[1] |= bitmask; return createWrapper.apply(null, data); } - if (isPartial) { - var partialHolders = getHolders(partialArgs); - } - if (isPartialRight) { - var partialRightHolders = getHolders(partialRightArgs); - } if (arity == null) { arity = isBindKey ? 0 : func.length; + } else if (arity < 0) { + arity = 0; + } + if (isPartial) { + partialHolders = getHolders(partialArgs); + } + if (isPartialRight) { + partialRightHolders = getHolders(partialRightArgs); } - arity = nativeMax(arity, 0); - // fast path for `_.bind` data = [func, bitmask, arity, thisArg, partialArgs, partialRightArgs, partialHolders, partialRightHolders]; return (bitmask == BIND_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) @@ -2160,11 +1988,40 @@ } /** - * Finds the indexes of all placeholder elements in `array`. + * Creates compiled iteration functions. + * + * @private + * @param {Object} [options] The compile options object. + * @param {string} [options.args] A comma separated string of iteration function arguments. + * @param {string} [options.init] The string representation of the initial `result` value. + * @param {string} [options.loop] Code to execute in the object loop. + * @param {boolean} [options.useHas] Specify using `hasOwnProperty` checks in the object loop. + * @returns {Function} Returns the compiled function. + */ + function createIterator(options) { + options.shadowedProps = shadowedProps; + options.support = support; + + // create the function factory + var factory = Function( + 'errorClass, errorProto, hasOwnProperty, isArguments, isObject, objectProto, ' + + 'nonEnumProps, stringClass, stringProto, toString', + 'return function(' + options.args + ') {\n' + iteratorTemplate(options) + '\n}' + ); + + // return the compiled function + return factory( + errorClass, errorProto, hasOwnProperty, isArguments, isObject, objectProto, + nonEnumProps, stringClass, stringProto, toString + ); + } + + /** + * Finds the indexes of all placeholder elements in a given array. * * @private * @param {Array} array The array to inspect. - * @returns {Array} Returns the new array of placeholder indexes. + * @returns {Array} Returns a new array of placeholder indexes. */ function getHolders(array) { var index = -1, @@ -2181,15 +2038,15 @@ /** * Gets the appropriate "indexOf" function. If the `_.indexOf` method is - * customized this function returns the custom method, otherwise it returns + * customized this method returns the custom method, otherwise it returns * the `baseIndexOf` function. * * @private * @returns {Function} Returns the "indexOf" function. */ function getIndexOf() { - var result = lodash.indexOf || indexOf; - return result === indexOf ? baseIndexOf : result; + var result = (result = lodash.indexOf) === indexOf ? baseIndexOf : result; + return result; } /** @@ -2197,7 +2054,7 @@ * * @private * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a native function, else `false`. + * @returns {boolean} Returns `true` if the `value` is a native function, else `false`. */ function isNative(value) { return typeof value == 'function' && reNative.test(fnToString.call(value)); @@ -2216,9 +2073,10 @@ }; /** - * A fallback implementation of `_.isPlainObject` which checks if `value` - * is an object created by the `Object` constructor or has a `[[Prototype]]` - * of `null`. + * A fallback implementation of `isPlainObject` which checks if a given value + * is an object created by the `Object` constructor, assuming objects created + * by the `Object` constructor have no inherited enumerable properties and that + * there are no `Object.prototype` extensions. * * @private * @param {*} value The value to check. @@ -2228,7 +2086,7 @@ var ctor, result; - // avoid non `Object` objects, `arguments` objects, and DOM elements + // avoid non Object objects, `arguments` objects, and DOM elements if (!(value && toString.call(value) == objectClass) || (!hasOwnProperty.call(value, 'constructor') && (ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor))) || @@ -2255,36 +2113,67 @@ return typeof result == 'undefined' || hasOwnProperty.call(value, result); } + /*--------------------------------------------------------------------------*/ + /** - * A fallback implementation of `Object.keys` which creates an array of the - * own enumerable property names of `object`. + * Checks if `value` is an `arguments` object. + * + * @static + * @memberOf _ + * @category Objects + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is an `arguments` object, else `false`. + * @example + * + * (function() { return _.isArguments(arguments); })(1, 2, 3); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + function isArguments(value) { + return value && typeof value == 'object' && typeof value.length == 'number' && + toString.call(value) == argsClass || false; + } + // fallback for environments that can't detect `arguments` objects by [[Class]] + if (!support.argsClass) { + isArguments = function(value) { + return value && typeof value == 'object' && typeof value.length == 'number' && + hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee') || false; + }; + } + + /** + * The base implementation of `_.forIn` without support for callback + * shorthands or `thisArg` binding. * * @private - * @param {Object} object The object to inspect. - * @returns {Array} Returns the array of property names. + * @param {Object} object The object to iterate over. + * @param {Function} callback The function called per iteration. + * @returns {Object} Returns `object`. */ - function shimKeys(object) { - var keyIndex, - index = -1, - props = keysIn(object), - length = props.length, - objLength = length && object.length, - maxIndex = objLength - 1, - result = []; + var baseForIn = createIterator({ + 'args': 'object, callback', + 'init': 'object', + 'loop': 'if (callback(object[key], key, object) === false) {\n return result;\n }', + 'useHas': false + }); - var allowIndexes = typeof objLength == 'number' && objLength > 0 && - (isArray(object) || (support.nonEnumArgs && isArguments(object)) || - (support.nonEnumStrings && isString(object))); - - while (++index < length) { - var key = props[index]; - if ((allowIndexes && (keyIndex = +key, keyIndex > -1 && keyIndex <= maxIndex && keyIndex % 1 == 0)) || - hasOwnProperty.call(object, key)) { - result.push(key); - } - } - return result; - } + /** + * A fallback implementation of `Object.keys` which produces an array of the + * given object's own enumerable property names. + * + * @private + * @type Function + * @param {Object} object The object to inspect. + * @returns {Array} Returns an array of property names. + */ + var shimKeys = createIterator({ + 'args': 'object', + 'init': '[]', + 'loop': 'result.push(key)', + 'useHas': true + }); /*--------------------------------------------------------------------------*/ @@ -2296,7 +2185,7 @@ * @memberOf _ * @category Arrays * @param {Array} array The array to compact. - * @returns {Array} Returns the new array of filtered values. + * @returns {Array} Returns a new array of filtered values. * @example * * _.compact([0, 1, false, 2, '', 3]); @@ -2326,171 +2215,24 @@ * @category Arrays * @param {Array} array The array to process. * @param {...Array} [values] The arrays of values to exclude. - * @returns {Array} Returns the new array of filtered values. + * @returns {Array} Returns a new array of filtered values. * @example * * _.difference([1, 2, 3], [5, 2, 10]); * // => [1, 3] */ - function difference() { - var index = -1, - length = arguments.length; - - while (++index < length) { - var value = arguments[index]; - if (isArray(value) || isArguments(value)) { - break; - } - } - return baseDifference(arguments[index], baseFlatten(arguments, true, true, ++index)); + function difference(array) { + return baseDifference(array, baseFlatten(arguments, true, true, 1)); } - /** - * Creates a slice of `array` with `n` elements dropped from the beginning. - * - * @static - * @memberOf _ - * @type Function - * @category Arrays - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to drop. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.drop([1, 2, 3], 1); - * // => [2, 3] - * - * _.drop([1, 2, 3], 2); - * // => [3] - * - * _.drop([1, 2, 3], 5); - * // => [] - * - * _.drop([1, 2, 3], 0); - * // => [1, 2, 3] - */ - var drop = rest; - - /** - * Creates a slice of `array` with `n` elements dropped from the end. - * - * @static - * @memberOf _ - * @type Function - * @category Arrays - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to drop. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.dropRight([1, 2, 3], 1); - * // => [1, 2] - * - * _.dropRight([1, 2, 3], 2); - * // => [1] - * - * _.dropRight([1, 2, 3], 5); - * // => [] - * - * _.dropRight([1, 2, 3], 0); - * // => [1, 2, 3] - */ - var dropRight = initial; - - /** - * Creates a slice of `array` excluding elements dropped from the end. - * Elements will be dropped until the predicate returns falsey. The predicate - * is bound to `thisArg` and invoked with three arguments; (value, index, array). - * - * If a property name is provided for `predicate` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `predicate` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @type Function - * @category Arrays - * @param {Array} array The array to query. - * @param {Function|Object|string} [predicate=identity] The function called - * per element. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.dropRightWhile([1, 2, 3], function(num) { - * return num > 1; - * }); - * // => [1] - * - * var characters = [ - * { 'name': 'barney', 'employer': 'slate' }, - * { 'name': 'fred', 'employer': 'slate', 'blocked': true }, - * { 'name': 'pebbles', 'employer': 'na', 'blocked': true } - * ]; - * - * // using "_.pluck" callback shorthand - * _.pluck(_.dropRightWhile(characters, 'blocked'), 'name'); - * // => ['barney'] - * - * // using "_.where" callback shorthand - * _.pluck(_.dropRightWhile(characters, { 'employer': 'na' }), 'name'); - * // => ['barney', 'fred'] - */ - var dropRightWhile = initial; - - /** - * Creates a slice of `array` excluding elements dropped from the beginning. - * Elements will be dropped until the predicate returns falsey. The predicate - * is bound to `thisArg` and invoked with three arguments; (value, index, array). - * - * If a property name is provided for `predicate` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `predicate` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @type Function - * @category Arrays - * @param {Array} array The array to query. - * @param {Function|Object|string} [predicate=identity] The function called - * per element. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.dropWhile([1, 2, 3], function(num) { - * return num < 3; - * }); - * // => [3] - * - * var characters = [ - * { 'name': 'barney', 'employer': 'slate', 'blocked': true }, - * { 'name': 'fred', 'employer': 'slate' }, - * { 'name': 'pebbles', 'employer': 'na', 'blocked': true } - * ]; - * - * // using "_.pluck" callback shorthand - * _.pluck(_.dropWhile(characters, 'blocked'), 'name'); - * // => ['fred', 'pebbles'] - * - * // using "_.where" callback shorthand - * _.pluck(_.dropWhile(characters, { 'employer': 'slate' }), 'name'); - * // => ['pebbles'] - */ - var dropWhile = rest; - /** * This method is like `_.find` except that it returns the index of the first - * element the predicate returns truthy for, instead of the element itself. + * element that passes the callback check, instead of the element itself. * - * If a property name is provided for `predicate` the created "_.pluck" style + * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * - * If an object is provided for `predicate` the created "_.where" style callback + * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * @@ -2498,10 +2240,10 @@ * @memberOf _ * @category Arrays * @param {Array} array The array to search. - * @param {Function|Object|string} [predicate=identity] The function called + * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback respectively. - * @param {*} [thisArg] The `this` binding of `predicate`. + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. * @returns {number} Returns the index of the found element, else `-1`. * @example * @@ -2524,13 +2266,13 @@ * _.findIndex(characters, 'blocked'); * // => 1 */ - function findIndex(array, predicate, thisArg) { + function findIndex(array, callback, thisArg) { var index = -1, length = array ? array.length : 0; - predicate = lodash.createCallback(predicate, thisArg, 3); + callback = lodash.createCallback(callback, thisArg, 3); while (++index < length) { - if (predicate(array[index], index, array)) { + if (callback(array[index], index, array)) { return index; } } @@ -2539,12 +2281,12 @@ /** * This method is like `_.findIndex` except that it iterates over elements - * of a collection from right to left. + * of a `collection` from right to left. * - * If a property name is provided for `predicate` the created "_.pluck" style + * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * - * If an object is provided for `predicate` the created "_.where" style callback + * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * @@ -2552,10 +2294,10 @@ * @memberOf _ * @category Arrays * @param {Array} array The array to search. - * @param {Function|Object|string} [predicate=identity] The function called + * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback respectively. - * @param {*} [thisArg] The `this` binding of `predicate`. + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. * @returns {number} Returns the index of the found element, else `-1`. * @example * @@ -2578,12 +2320,12 @@ * _.findLastIndex(characters, 'blocked'); * // => 2 */ - function findLastIndex(array, predicate, thisArg) { + function findLastIndex(array, callback, thisArg) { var length = array ? array.length : 0; - predicate = lodash.createCallback(predicate, thisArg, 3); + callback = lodash.createCallback(callback, thisArg, 3); while (length--) { - if (predicate(array[length], length, array)) { + if (callback(array[length], length, array)) { return length; } } @@ -2591,47 +2333,80 @@ } /** - * Gets the first element of `array`. + * Gets the first element or first `n` elements of an array. If a callback + * is provided elements at the beginning of the array are returned as long + * as the callback returns truey. The callback is bound to `thisArg` and + * invoked with three arguments; (value, index, array). * - * Note: The `n` and `predicate` arguments are deprecated; replace with - * `_.take` and `_.takeWhile` respectively. + * If a property name is provided for `callback` the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is provided for `callback` the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false`. * * @static * @memberOf _ - * @alias head + * @alias head, take * @category Arrays * @param {Array} array The array to query. - * @returns {*} Returns the first element of `array`. + * @param {Function|Object|number|string} [callback] The function called + * per element or the number of elements to return. If a property name or + * object is provided it will be used to create a "_.pluck" or "_.where" + * style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {*} Returns the first element(s) of `array`. * @example * * _.first([1, 2, 3]); * // => 1 * - * _.first([]); - * // => undefined + * // returns the first two elements + * _.first([1, 2, 3], 2); + * // => [1, 2] + * + * // returns elements from the beginning until the callback result is falsey + * _.first([1, 2, 3], function(num) { + * return num < 3; + * }); + * // => [1, 2] + * + * var characters = [ + * { 'name': 'barney', 'employer': 'slate', 'blocked': true }, + * { 'name': 'fred', 'employer': 'slate' }, + * { 'name': 'pebbles', 'employer': 'na', 'blocked': true } + * ]; + * + * // using "_.pluck" callback shorthand + * _.first(characters, 'blocked'); + * // => [{ 'name': 'barney', 'employer': 'slate', 'blocked': true }] + * + * // using "_.where" callback shorthand + * _.pluck(_.first(characters, { 'employer': 'slate' }), 'name'); + * // => ['barney', 'fred'] */ - function first(array, predicate, thisArg) { - if (typeof predicate != 'number' && predicate != null) { + function first(array, callback, thisArg) { + if (typeof callback != 'number' && callback != null) { var index = -1, length = array ? array.length : 0, n = 0; - predicate = lodash.createCallback(predicate, thisArg, 3); - while (++index < length && predicate(array[index], index, array)) { + callback = lodash.createCallback(callback, thisArg, 3); + while (++index < length && callback(array[index], index, array)) { n++; } } else { - n = predicate; + n = callback; if (n == null || thisArg) { return array ? array[0] : undefined; } } - return slice(array, 0, n < 0 ? 0 : n); + return slice(array, 0, n > 0 ? n : 0); } /** * Flattens a nested array (the nesting can be to any depth). If `isShallow` - * is truthy, the array will only be flattened a single level. If a callback + * is truey, the array will only be flattened a single level. If a callback * is provided each element of the array is passed through the callback before * flattening. The callback is bound to `thisArg` and invoked with three * arguments; (value, index, array). @@ -2648,11 +2423,11 @@ * @category Arrays * @param {Array} array The array to flatten. * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level. - * @param {Function|Object|string} [callback] The function called per iteration. - * If a property name or object is provided it will be used to create a "_.pluck" - * or "_.where" style callback respectively. + * @param {Function|Object|string} [callback=identity] The function called + * per iteration. If a property name or object is provided it will be used + * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Array} Returns the new flattened array. + * @returns {Array} Returns a new flattened array. * @example * * _.flatten([1, [2], [3, [[4]]]]); @@ -2706,7 +2481,7 @@ * @param {*} value The value to search for. * @param {boolean|number} [fromIndex=0] The index to search from or `true` * to perform a binary search on a sorted array. - * @returns {number} Returns the index of the matched value, else `-1`. + * @returns {number} Returns the index of the matched value or `-1`. * @example * * _.indexOf([1, 2, 3, 1, 2, 3], 2); @@ -2723,7 +2498,7 @@ function indexOf(array, value, fromIndex) { var length = array ? array.length : 0; if (typeof fromIndex == 'number') { - fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0); + fromIndex = fromIndex < 0 ? nativeMax(0, length + fromIndex) : (fromIndex || 0); } else if (fromIndex) { var index = sortedIndex(array, value); return (length && array[index] === value) ? index : -1; @@ -2732,37 +2507,73 @@ } /** - * Gets all but the last element of `array`. + * Gets all but the last element or last `n` elements of an array. If a + * callback is provided elements at the end of the array are excluded from + * the result as long as the callback returns truey. The callback is bound + * to `thisArg` and invoked with three arguments; (value, index, array). * - * Note: The `n` and `predicate` arguments are deprecated; replace with - * `_.dropRight` and `_.dropRightWhile` respectively. + * If a property name is provided for `callback` the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is provided for `callback` the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false`. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to query. - * @returns {Array} Returns the slice of `array`. + * @param {Function|Object|number|string} [callback=1] The function called + * per element or the number of elements to exclude. If a property name or + * object is provided it will be used to create a "_.pluck" or "_.where" + * style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Array} Returns a slice of `array`. * @example * * _.initial([1, 2, 3]); * // => [1, 2] + * + * // excludes the last two elements + * _.initial([1, 2, 3], 2); + * // => [1] + * + * // excludes elements from the end until the callback fails + * _.initial([1, 2, 3], function(num) { + * return num > 1; + * }); + * // => [1] + * + * var characters = [ + * { 'name': 'barney', 'employer': 'slate' }, + * { 'name': 'fred', 'employer': 'slate', 'blocked': true }, + * { 'name': 'pebbles', 'employer': 'na', 'blocked': true } + * ]; + * + * // using "_.pluck" callback shorthand + * _.initial(characters, 'blocked'); + * // => [{ 'name': 'barney', 'blocked': false, 'employer': 'slate' }] + * + * // using "_.where" callback shorthand + * _.pluck(_.initial(characters, { 'employer': 'na' }), 'name'); + * // => ['barney', 'fred'] */ - function initial(array, predicate, thisArg) { + function initial(array, callback, thisArg) { var length = array ? array.length : 0; - if (typeof predicate != 'number' && predicate != null) { + if (typeof callback != 'number' && callback != null) { var index = length, n = 0; - predicate = lodash.createCallback(predicate, thisArg, 3); - while (index-- && predicate(array[index], index, array)) { + callback = lodash.createCallback(callback, thisArg, 3); + while (index-- && callback(array[index], index, array)) { n++; } } else { - n = (predicate == null || thisArg) ? 1 : predicate; + n = (callback == null || thisArg) ? 1 : callback; } - n = length - (n || 0); - return slice(array, 0, n < 0 ? 0 : n); + n = length - n; + return slice(array, 0, n > 0 ? n : 0); } /** @@ -2772,8 +2583,8 @@ * @static * @memberOf _ * @category Arrays - * @param {...Array} [arrays] The arrays to inspect. - * @returns {Array} Returns the new array of shared values. + * @param {...Array} [array] The arrays to inspect. + * @returns {Array} Returns an array of shared values. * @example * * _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]); @@ -2785,37 +2596,36 @@ argsLength = arguments.length, caches = [], indexOf = getIndexOf(), - prereq = createCache && indexOf === baseIndexOf; + prereq = createCache && indexOf === baseIndexOf, + seen = []; while (++argsIndex < argsLength) { var value = arguments[argsIndex]; if (isArray(value) || isArguments(value)) { args.push(value); caches.push(prereq && value.length >= 120 && - createCache(argsIndex && value)); + createCache(argsIndex ? args[argsIndex] : seen)); } } - argsLength = args.length; var array = args[0], index = -1, length = array ? array.length : 0, - result = [], - seen = caches[0]; + result = []; outer: while (++index < length) { + var cache = caches[0]; value = array[index]; - if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value)) < 0) { + + if ((cache ? cacheIndexOf(cache, value) : indexOf(seen, value)) < 0) { argsIndex = argsLength; + (cache || seen).push(value); while (--argsIndex) { - var cache = caches[argsIndex]; + cache = caches[argsIndex]; if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) { continue outer; } } - if (seen) { - seen.push(value); - } result.push(value); } } @@ -2823,46 +2633,89 @@ } /** - * Gets the last element of `array`. + * Gets the last element or last `n` elements of an array. If a callback is + * provided elements at the end of the array are returned as long as the + * callback returns truey. The callback is bound to `thisArg` and invoked + * with three arguments; (value, index, array). * - * Note: The `n` and `predicate` arguments are deprecated; replace with - * `_.takeRight` and `_.takeRightWhile` respectively. + * If a property name is provided for `callback` the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is provided for `callback` the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false`. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to query. - * @returns {*} Returns the last element of `array`. + * @param {Function|Object|number|string} [callback] The function called + * per element or the number of elements to return. If a property name or + * object is provided it will be used to create a "_.pluck" or "_.where" + * style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {*} Returns the last element(s) of `array`. * @example * * _.last([1, 2, 3]); * // => 3 + * + * // returns the last two elements + * _.last([1, 2, 3], 2); + * // => [2, 3] + * + * // returns elements from the end until the callback fails + * _.last([1, 2, 3], function(num) { + * return num > 1; + * }); + * // => [2, 3] + * + * var characters = [ + * { 'name': 'barney', 'employer': 'slate' }, + * { 'name': 'fred', 'employer': 'slate', 'blocked': true }, + * { 'name': 'pebbles', 'employer': 'na', 'blocked': true } + * ]; + * + * // using "_.pluck" callback shorthand + * _.pluck(_.last(characters, 'blocked'), 'name'); + * // => ['fred', 'pebbles'] + * + * // using "_.where" callback shorthand + * _.last(characters, { 'employer': 'na' }); + * // => [{ 'name': 'pebbles', 'employer': 'na', 'blocked': true }] */ - function last(array, predicate, thisArg) { + function last(array, callback, thisArg) { var length = array ? array.length : 0; - if (typeof predicate != 'number' && predicate != null) { + if (typeof callback != 'number' && callback != null) { var index = length, n = 0; - predicate = lodash.createCallback(predicate, thisArg, 3); - while (index-- && predicate(array[index], index, array)) { + callback = lodash.createCallback(callback, thisArg, 3); + while (index-- && callback(array[index], index, array)) { n++; } } else { - n = predicate; + n = callback; if (n == null || thisArg) { return array ? array[length - 1] : undefined; } } - n = length - (n || 0); - return slice(array, n < 0 ? 0 : n); + n = length - n; + return slice(array, n > 0 ? n : 0); } /** - * Gets the index at which the last occurrence of `value` is found using - * strict equality for comparisons, i.e. `===`. If `fromIndex` is negative, - * it is used as the offset from the end of the collection. + * Gets the index at which the last occurrence of `value` is found using strict + * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used + * as the offset from the end of the collection. + * + * If a property name is provided for `callback` the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is provided for `callback` the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false`. * * @static * @memberOf _ @@ -2870,7 +2723,7 @@ * @param {Array} array The array to search. * @param {*} value The value to search for. * @param {number} [fromIndex=array.length-1] The index to search from. - * @returns {number} Returns the index of the matched value, else `-1`. + * @returns {number} Returns the index of the matched value or `-1`. * @example * * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2); @@ -2883,7 +2736,7 @@ function lastIndexOf(array, value, fromIndex) { var index = array ? array.length : 0; if (typeof fromIndex == 'number') { - index = (fromIndex < 0 ? nativeMax(index + fromIndex, 0) : nativeMin(fromIndex || 0, index - 1)) + 1; + index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1; } while (index--) { if (array[index] === value) { @@ -2897,13 +2750,11 @@ * Removes all provided values from `array` using strict equality for * comparisons, i.e. `===`. * - * Note: Unlike `_.without`, this method mutates `array`. - * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to modify. - * @param {...*} [values] The values to remove. + * @param {...*} [value] The values to remove. * @returns {Array} Returns `array`. * @example * @@ -2932,28 +2783,79 @@ } /** - * Removes all elements from `array` that the predicate returns truthy for - * and returns an array of removed elements. The predicate is bound to `thisArg` + * Creates an array of numbers (positive and/or negative) progressing from + * `start` up to but not including `end`. If `start` is less than `stop` a + * zero-length range is created unless a negative `step` is specified. + * + * @static + * @memberOf _ + * @category Arrays + * @param {number} [start=0] The start of the range. + * @param {number} end The end of the range. + * @param {number} [step=1] The value to increment or decrement by. + * @returns {Array} Returns a new range array. + * @example + * + * _.range(4); + * // => [0, 1, 2, 3] + * + * _.range(1, 5); + * // => [1, 2, 3, 4] + * + * _.range(0, 20, 5); + * // => [0, 5, 10, 15] + * + * _.range(0, -4, -1); + * // => [0, -1, -2, -3] + * + * _.range(1, 4, 0); + * // => [1, 1, 1] + * + * _.range(0); + * // => [] + */ + function range(start, end, step) { + start = +start || 0; + step = typeof step == 'number' ? step : (+step || 1); + + if (end == null) { + end = start; + start = 0; + } + // use `Array(length)` so engines like Chakra and V8 avoid slower modes + // http://youtu.be/XAqIpGU8ZZk#t=17m25s + var index = -1, + length = nativeMax(0, ceil((end - start) / (step || 1))), + result = Array(length); + + while (++index < length) { + result[index] = start; + start += step; + } + return result; + } + + /** + * Removes all elements from an array that the callback returns truey for + * and returns an array of removed elements. The callback is bound to `thisArg` * and invoked with three arguments; (value, index, array). * - * If a property name is provided for `predicate` the created "_.pluck" style + * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * - * If an object is provided for `predicate` the created "_.where" style callback + * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * - * Note: Unlike `_.filter`, this method mutates `array`. - * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to modify. - * @param {Function|Object|string} [predicate=identity] The function called + * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback respectively. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the array of removed elements. + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Array} Returns a new array of removed elements. * @example * * var array = [1, 2, 3, 4, 5, 6]; @@ -2965,15 +2867,15 @@ * console.log(evens); * // => [2, 4, 6] */ - function remove(array, predicate, thisArg) { + function remove(array, callback, thisArg) { var index = -1, length = array ? array.length : 0, result = []; - predicate = lodash.createCallback(predicate, thisArg, 3); + callback = lodash.createCallback(callback, thisArg, 3); while (++index < length) { var value = array[index]; - if (predicate(value, index, array)) { + if (callback(value, index, array)) { result.push(value); splice.call(array, index--, 1); length--; @@ -2983,36 +2885,124 @@ } /** - * Gets all but the first element of `array`. - * - * Note: The `n` and `predicate` arguments are deprecated; replace with - * `_.drop` and `_.dropWhile` respectively. + * Removes elements located at the given indexes and returns an array of + * removed elements. A hybrid of "_.remove" and "_.at". Indexes may be + * specified as an array of indexes or as individual arguments. * * @static * @memberOf _ - * @alias tail + * @category Arrays + * @param {Array} array The array to modify. + * @param {...(number|number[])} [index] The indexes of `array` to remove + * @returns {Array} Returns a new array of removed elements. + * @example + * + * var array = [5, 10, 15, 20, 25, 30]; + * var evens = _.removeAt(array, [1, 3, 5]); + * + * console.log(array); + * // => [5, 15, 25] + * + * console.log(evens); + * // => [10, 20, 30] + * + * var greeting = ('good morning').split(''); + * var vowels = _.removeAt(greeting, 1, 2, 6, 9); + * + * console.log(greeting.join('')); + * // => 'gd mrnng' + * + * console.log(vowels.join('')); + * // => 'oooi' + */ + function removeAt(array, guard) { + var args = arguments, + index = -1, + removals = baseFlatten(args, true, false, 1), + length = removals.length; + + // enables use as a callback for functions like `_.map` + if (typeof guard == 'number' && args[2] && args[2][guard] === array) { + length = 1; + } else { + removals.sort(baseCompareAscending); + } + var result = Array(length); + while(++index < length) { + result[index] = splice.call(array, removals[index] - index, 1)[0]; + } + return result; + } + + + /** + * The opposite of `_.initial`; this method gets all but the first element or + * first `n` elements of an array. If a callback function is provided elements + * at the beginning of the array are excluded from the result as long as the + * callback returns truey. The callback is bound to `thisArg` and invoked + * with three arguments; (value, index, array). + * + * If a property name is provided for `callback` the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is provided for `callback` the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false`. + * + * @static + * @memberOf _ + * @alias drop, tail * @category Arrays * @param {Array} array The array to query. - * @returns {Array} Returns the slice of `array`. + * @param {Function|Object|number|string} [callback=1] The function called + * per element or the number of elements to exclude. If a property name or + * object is provided it will be used to create a "_.pluck" or "_.where" + * style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Array} Returns a slice of `array`. * @example * * _.rest([1, 2, 3]); * // => [2, 3] + * + * // excludes the first two elements + * _.rest([1, 2, 3], 2); + * // => [3] + * + * // excludes elements from the beginning until the callback fails + * _.rest([1, 2, 3], function(num) { + * return num < 3; + * }); + * // => [3] + * + * var characters = [ + * { 'name': 'barney', 'employer': 'slate', 'blocked': true }, + * { 'name': 'fred', 'employer': 'slate' }, + * { 'name': 'pebbles', 'employer': 'na', 'blocked': true } + * ]; + * + * // using "_.pluck" callback shorthand + * _.pluck(_.rest(characters, 'blocked'), 'name'); + * // => ['fred', 'pebbles'] + * + * // using "_.where" callback shorthand + * _.rest(characters, { 'employer': 'slate' }); + * // => [{ 'name': 'pebbles', 'employer': 'na', 'blocked': true }] */ - function rest(array, predicate, thisArg) { - if (typeof predicate != 'number' && predicate != null) { + function rest(array, callback, thisArg) { + if (typeof callback != 'number' && callback != null) { var index = -1, length = array ? array.length : 0, n = 0; - predicate = lodash.createCallback(predicate, thisArg, 3); - while (++index < length && predicate(array[index], index, array)) { + callback = lodash.createCallback(callback, thisArg, 3); + while (++index < length && callback(array[index], index, array)) { n++; } - } else if (predicate == null || thisArg) { + } else if (callback == null || thisArg) { n = 1; } else { - n = predicate < 0 ? 0 : predicate; + n = callback > 0 ? callback : 0; } return slice(array, n); } @@ -3029,25 +3019,27 @@ * @param {Array} array The array to slice. * @param {number} [start=0] The start index. * @param {number} [end=array.length] The end index. - * @returns {Array} Returns the slice of `array`. + * @returns {Array} Returns the new array. */ function slice(array, start, end) { var index = -1, length = array ? array.length : 0; - start = typeof start == 'undefined' ? 0 : (+start || 0); - if (start < 0) { + if (typeof start == 'undefined') { + start = 0; + } else if (start < 0) { start = nativeMax(length + start, 0); } else if (start > length) { start = length; } - end = typeof end == 'undefined' ? length : (+end || 0); - if (end < 0) { + if (typeof end == 'undefined') { + end = length; + } else if (end < 0) { end = nativeMax(length + end, 0); } else if (end > length) { end = length; } - length = start > end ? 0 : (end - start); + length = end - start || 0; var result = Array(length); while (++index < length) { @@ -3077,7 +3069,7 @@ * @param {*} value The value to evaluate. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback respectively. + * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {number} Returns the index at which `value` should be inserted * into `array`. @@ -3087,17 +3079,17 @@ * // => 2 * * var dict = { - * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'forty': 40, 'fifty': 50 } + * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 } * }; * * // using `callback` - * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'forty', function(word) { + * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) { * return dict.wordToNumber[word]; * }); * // => 2 * * // using `callback` with `thisArg` - * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'forty', function(word) { + * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) { * return this.wordToNumber[word]; * }, dict); * // => 2 @@ -3123,144 +3115,6 @@ return low; } - /** - * Creates a slice of `array` with `n` elements taken from the beginning. - * - * @static - * @memberOf _ - * @type Function - * @category Arrays - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to take. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.take([1, 2, 3], 1); - * // => [1] - * - * _.take([1, 2, 3], 2); - * // => [1, 2] - * - * _.take([1, 2, 3], 5); - * // => [1, 2, 3] - * - * _.take([1, 2, 3], 0); - * // => [] - */ - var take = first; - - /** - * Creates a slice of `array` with `n` elements taken from the end. - * - * @static - * @memberOf _ - * @type Function - * @category Arrays - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to take. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.takeRight([1, 2, 3], 1); - * // => [3] - * - * _.takeRight([1, 2, 3], 2); - * // => [2, 3] - * - * _.takeRight([1, 2, 3], 5); - * // => [1, 2, 3] - * - * _.takeRight([1, 2, 3], 0); - * // => [] - */ - var takeRight = last; - - /** - * Creates a slice of `array` with elements taken from the end. Elements will - * be taken until the predicate returns falsey. The predicate is bound to - * `thisArg` and invoked with three arguments; (value, index, array). - * - * If a property name is provided for `predicate` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `predicate` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @type Function - * @category Arrays - * @param {Array} array The array to query. - * @param {Function|Object|string} [predicate=identity] The function called - * per element. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.takeRightWhile([1, 2, 3], function(num) { - * return num > 1; - * }); - * // => [2, 3] - * - * var characters = [ - * { 'name': 'barney', 'employer': 'slate' }, - * { 'name': 'fred', 'employer': 'slate', 'blocked': true }, - * { 'name': 'pebbles', 'employer': 'na', 'blocked': true } - * ]; - * - * // using "_.pluck" callback shorthand - * _.pluck(_.takeRightWhile(characters, 'blocked'), 'name'); - * // => ['fred', 'pebbles'] - * - * // using "_.where" callback shorthand - * _.pluck(_.takeRightWhile(characters, { 'employer': 'na' }), 'name'); - * // => ['pebbles'] - */ - var takeRightWhile = last; - - /** - * Creates a slice of `array` with elements taken from the beginning. Elements - * will be taken until the predicate returns falsey. The predicate is bound - * to `thisArg` and invoked with three arguments; (value, index, array). - * - * If a property name is provided for `predicate` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `predicate` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @type Function - * @category Arrays - * @param {Array} array The array to query. - * @param {Function|Object|string} [predicate=identity] The function called - * per element. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.takeWhile([1, 2, 3], function(num) { - * return num < 3; - * }); - * // => [1, 2] - * - * var characters = [ - * { 'name': 'barney', 'employer': 'slate', 'blocked': true }, - * { 'name': 'fred', 'employer': 'slate' }, - * { 'name': 'pebbles', 'employer': 'na', 'blocked': true } - * ]; - * - * // using "_.pluck" callback shorthand - * _.pluck(_.takeWhile(characters, 'blocked'), 'name'); - * // => ['barney'] - * - * // using "_.where" callback shorthand - * _.pluck(_.takeWhile(characters, { 'employer': 'slate' }), 'name'); - * // => ['barney', 'fred'] - */ - var takeWhile = first; - /** * Creates an array of unique values, in order, of the provided arrays using * strict equality for comparisons, i.e. `===`. @@ -3268,8 +3122,8 @@ * @static * @memberOf _ * @category Arrays - * @param {...Array} [arrays] The arrays to inspect. - * @returns {Array} Returns the new array of combined values. + * @param {...Array} [array] The arrays to inspect. + * @returns {Array} Returns an array of combined values. * @example * * _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]); @@ -3281,11 +3135,11 @@ /** * Creates a duplicate-value-free version of an array using strict equality - * for comparisons, i.e. `===`. If the array is sorted, providing `true` for - * `isSorted` will use a faster algorithm. If a callback is provided it will - * be executed for each value in the array to generate the criterion by which - * uniqueness is computed. The callback is bound to `thisArg` and invoked with - * three arguments; (value, index, array). + * for comparisons, i.e. `===`. If the array is sorted, providing + * `true` for `isSorted` will use a faster algorithm. If a callback is provided + * each element of `array` is passed through the callback before uniqueness + * is computed. The callback is bound to `thisArg` and invoked with three + * arguments; (value, index, array). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. @@ -3300,11 +3154,11 @@ * @category Arrays * @param {Array} array The array to process. * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted. - * @param {Function|Object|string} [callback] The function called per iteration. - * If a property name or object is provided it will be used to create a "_.pluck" - * or "_.where" style callback respectively. + * @param {Function|Object|string} [callback=identity] The function called + * per iteration. If a property name or object is provided it will be used + * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Array} Returns the new duplicate-value-free array. + * @returns {Array} Returns a duplicate-value-free array. * @example * * _.uniq([1, 2, 1, 3, 1]); @@ -3357,27 +3211,26 @@ * @memberOf _ * @category Arrays * @param {Array} array The array to filter. - * @param {...*} [values] The values to exclude. - * @returns {Array} Returns the new array of filtered values. + * @param {...*} [value] The values to exclude. + * @returns {Array} Returns a new array of filtered values. * @example * * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); * // => [2, 3, 4] */ - function without() { - return baseDifference(arguments[0], slice(arguments, 1)); + function without(array) { + return baseDifference(array, slice(arguments, 1)); } /** * Creates an array that is the symmetric difference of the provided arrays. - * See [Wikipedia](http://en.wikipedia.org/wiki/Symmetric_difference) for - * more details. + * See [Wikipedia](http://en.wikipedia.org/wiki/Symmetric_difference) for more details. * * @static * @memberOf _ * @category Arrays - * @param {...Array} [arrays] The arrays to inspect. - * @returns {Array} Returns the new array of values. + * @param {...Array} [array] The arrays to inspect. + * @returns {Array} Returns an array of values. * @example * * _.xor([1, 2, 3], [5, 2, 1, 4]); @@ -3411,8 +3264,8 @@ * @memberOf _ * @alias unzip * @category Arrays - * @param {...Array} [arrays] The arrays to process. - * @returns {Array} Returns the array of grouped elements. + * @param {...Array} [array] The arrays to process. + * @returns {Array} Returns a new array of grouped elements. * @example * * _.zip(['fred', 'barney'], [30, 40], [true, false]); @@ -3444,7 +3297,8 @@ * @category Arrays * @param {Array} keys The array of keys. * @param {Array} [values=[]] The array of values. - * @returns {Object} Returns the new object. + * @returns {Object} Returns an object composed of the given keys and + * corresponding values. * @example * * _.zipObject(['fred', 'barney'], [30, 40]); @@ -3479,7 +3333,7 @@ * @memberOf _ * @category Chaining * @param {*} value The value to wrap. - * @returns {Object} Returns the new wrapper object. + * @returns {Object} Returns the wrapper object. * @example * * var characters = [ @@ -3496,7 +3350,9 @@ * // => 'pebbles is 1' */ function chain(value) { - return new lodashWrapper(value, true); + value = new lodashWrapper(value); + value.__chain__ = true; + return value; } /** @@ -3556,12 +3412,12 @@ } /** - * Produces the result of coercing the wrapped value to a string. + * Produces the `toString` result of the wrapped value. * * @name toString * @memberOf _ * @category Chaining - * @returns {string} Returns the coerced string value. + * @returns {string} Returns the string result. * @example * * _([1, 2, 3]).toString(); @@ -3576,7 +3432,7 @@ * * @name valueOf * @memberOf _ - * @alias toJSON, value + * @alias value * @category Chaining * @returns {*} Returns the wrapped value. * @example @@ -3590,6 +3446,7 @@ /*--------------------------------------------------------------------------*/ + /** * Creates an array of elements from the specified indexes, or keys, of the * `collection`. Indexes may be specified as individual arguments or as arrays @@ -3599,9 +3456,10 @@ * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to iterate over. - * @param {...(number|number[]|string|string[])} [keys] The keys of elements - * to pick, specified as individual keys or arrays of keys. - * @returns {Array} Returns the array of picked elements. + * @param {...(number|number[]|string|string[])} [index] The indexes of `collection` + * to retrieve, specified as individual indexes or arrays of indexes. + * @returns {Array} Returns a new array of elements corresponding to the + * provided indexes. * @example * * _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]); @@ -3610,11 +3468,17 @@ * _.at(['fred', 'barney', 'pebbles'], 0, 2); * // => ['fred', 'pebbles'] */ - function at(collection) { - var index = -1, - props = baseFlatten(arguments, true, false, 1), - length = props.length; + function at(collection, guard) { + var args = arguments, + index = -1, + props = baseFlatten(args, true, false, 1), + length = props.length, + type = typeof guard; + // enables use as a callback for functions like `_.map` + if ((type == 'number' || type == 'string') && args[2] && args[2][guard] === collection) { + length = 1; + } if (support.unindexedChars && isString(collection)) { collection = collection.split(''); } @@ -3634,10 +3498,10 @@ * @memberOf _ * @alias include * @category Collections - * @param {Array|Object|string} collection The collection to search. + * @param {Array|Object|string} collection The collection to iterate over. * @param {*} target The value to check for. * @param {number} [fromIndex=0] The index to search from. - * @returns {boolean} Returns `true` if a matching element is found, else `false`. + * @returns {boolean} Returns `true` if the `target` element is found, else `false`. * @example * * _.contains([1, 2, 3], 1); @@ -3654,25 +3518,31 @@ */ function contains(collection, target, fromIndex) { var length = collection ? collection.length : 0; - if (!(typeof length == 'number' && length > -1 && length <= maxSafeInteger)) { - collection = values(collection); - length = collection.length; - } - if (typeof fromIndex == 'number') { - fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0); - } else { - fromIndex = 0; - } - if (typeof collection == 'string' || !isArray(collection) && isString(collection)) { + fromIndex = typeof fromIndex == 'number' ? fromIndex : 0; + + if (typeof length == 'number') { if (fromIndex >= length) { return false; } - return nativeContains - ? nativeContains.call(collection, target, fromIndex) - : collection.indexOf(target, fromIndex) > -1; + if (typeof collection == 'string' || !isArray(collection) && isString(collection)) { + return nativeContains + ? nativeContains.call(collection, target, fromIndex) + : collection.indexOf(target, fromIndex) > -1; + } + var indexOf = getIndexOf(); + fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0; + return indexOf(collection, target, fromIndex) > -1; } - var indexOf = getIndexOf(); - return indexOf(collection, target, fromIndex) > -1; + var index = -1, + result = false; + + baseEach(collection, function(value) { + if (++index >= fromIndex) { + return !(result = value === target); + } + }); + + return result; } /** @@ -3695,7 +3565,7 @@ * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback respectively. + * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns the composed aggregate object. * @example @@ -3714,14 +3584,14 @@ }); /** - * Checks if the predicate returns truthy for **all** elements of a collection. - * The predicate is bound to `thisArg` and invoked with three arguments; - * (value, index|key, collection). + * Checks if the callback returns truey value for **all** elements of a + * collection. The callback is bound to `thisArg` and invoked with three + * arguments; (value, index|key, collection). * - * If a property name is provided for `predicate` the created "_.pluck" style + * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * - * If an object is provided for `predicate` the created "_.where" style callback + * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * @@ -3730,11 +3600,11 @@ * @alias all * @category Collections * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [predicate=identity] The function called + * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback respectively. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {boolean} Returns `true` if all elements passed the predicate check, + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {boolean} Returns `true` if all elements passed the callback check, * else `false`. * @example * @@ -3754,36 +3624,36 @@ * _.every(characters, { 'age': 36 }); * // => false */ - function every(collection, predicate, thisArg) { + function every(collection, callback, thisArg) { var result = true; - predicate = lodash.createCallback(predicate, thisArg, 3); + callback = lodash.createCallback(callback, thisArg, 3); if (isArray(collection)) { var index = -1, length = collection.length; while (++index < length) { - if (!predicate(collection[index], index, collection)) { + if (!callback(collection[index], index, collection)) { return false; } } } else { baseEach(collection, function(value, index, collection) { - return (result = !!predicate(value, index, collection)); + return (result = !!callback(value, index, collection)); }); } return result; } /** - * Iterates over elements of a collection returning an array of all elements - * the predicate returns truthy for. The predicate is bound to `thisArg` and + * Iterates over elements of a collection, returning an array of all elements + * the callback returns truey for. The callback is bound to `thisArg` and * invoked with three arguments; (value, index|key, collection). * - * If a property name is provided for `predicate` the created "_.pluck" style + * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * - * If an object is provided for `predicate` the created "_.where" style callback + * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * @@ -3792,11 +3662,11 @@ * @alias select * @category Collections * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [predicate=identity] The function called + * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback respectively. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the new filtered array. + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Array} Returns a new array of elements that passed the callback check. * @example * * var evens = _.filter([1, 2, 3, 4], function(num) { return num % 2 == 0; }); @@ -3815,23 +3685,23 @@ * _.filter(characters, { 'age': 36 }); * // => [{ 'name': 'barney', 'age': 36 }] */ - function filter(collection, predicate, thisArg) { + function filter(collection, callback, thisArg) { var result = []; - predicate = lodash.createCallback(predicate, thisArg, 3); + callback = lodash.createCallback(callback, thisArg, 3); if (isArray(collection)) { var index = -1, length = collection.length; while (++index < length) { var value = collection[index]; - if (predicate(value, index, collection)) { + if (callback(value, index, collection)) { result.push(value); } } } else { baseEach(collection, function(value, index, collection) { - if (predicate(value, index, collection)) { + if (callback(value, index, collection)) { result.push(value); } }); @@ -3841,26 +3711,26 @@ /** * Iterates over elements of a collection, returning the first element that - * the predicate returns truthy for. The predicate is bound to `thisArg` and + * the callback returns truey for. The callback is bound to `thisArg` and * invoked with three arguments; (value, index|key, collection). * - * If a property name is provided for `predicate` the created "_.pluck" style + * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * - * If an object is provided for `predicate` the created "_.where" style callback + * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ - * @alias detect + * @alias detect, findWhere * @category Collections - * @param {Array|Object|string} collection The collection to search. - * @param {Function|Object|string} [predicate=identity] The function called + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback respectively. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {*} Returns the matched element, else `undefined`. + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {*} Returns the found element, else `undefined`. * @example * * var characters = [ @@ -3882,28 +3752,43 @@ * _.find(characters, 'blocked'); * // => { 'name': 'fred', 'age': 40, 'blocked': true } */ - function find(collection, predicate, thisArg) { + function find(collection, callback, thisArg) { + callback = lodash.createCallback(callback, thisArg, 3); if (isArray(collection)) { - var index = findIndex(collection, predicate, thisArg); - return index > -1 ? collection[index] : undefined; + var index = -1, + length = collection.length; + + while (++index < length) { + var value = collection[index]; + if (callback(value, index, collection)) { + return value; + } + } + } else { + var result; + baseEach(collection, function(value, index, collection) { + if (callback(value, index, collection)) { + result = value; + return false; + } + }); + return result; } - predicate = lodash.createCallback(predicate, thisArg, 3); - return baseFind(collection, predicate, baseEach); } /** - * This method is like `_.find` except that it iterates over elements of a - * collection from right to left. + * This method is like `_.find` except that it iterates over elements + * of a `collection` from right to left. * * @static * @memberOf _ * @category Collections - * @param {Array|Object|string} collection The collection to search. - * @param {Function|Object|string} [predicate=identity] The function called + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback respectively. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {*} Returns the matched element, else `undefined`. + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {*} Returns the found element, else `undefined`. * @example * * _.findLast([1, 2, 3, 4], function(num) { @@ -3911,41 +3796,21 @@ * }); * // => 3 */ - function findLast(collection, predicate, thisArg) { - predicate = lodash.createCallback(predicate, thisArg, 3); - return baseFind(collection, predicate, baseEachRight); + function findLast(collection, callback, thisArg) { + var result; + + callback = lodash.createCallback(callback, thisArg, 3); + baseEachRight(collection, function(value, index, collection) { + if (callback(value, index, collection)) { + result = value; + return false; + } + }); + return result; } /** - * Performs a deep comparison between each element in `collection` and the - * source object, returning the first element that has equivalent property - * values. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|string} collection The collection to search. - * @param {Object} source The object of property values to match. - * @returns {*} Returns the matched element, else `undefined`. - * @example - * - * var characters = [ - * { 'name': 'barney', 'age': 36, 'employer': 'slate' }, - * { 'name': 'fred', 'age': 40, 'employer': 'slate' } - * ]; - * - * _.findWhere(characters, { 'employer': 'slate' }); - * // => { 'name': 'barney', 'age': 36, 'employer': 'slate' } - * - * _.findWhere(characters, { 'age': 40 }); - * // => { 'name': 'fred', 'age': 40, 'employer': 'slate' } - */ - function findWhere(collection, source) { - return find(collection, matches(source)); - } - - /** - * Iterates over elements of a collection executing the callback for each + * Iterates over elements of a collection, executing the callback for each * element. The callback is bound to `thisArg` and invoked with three arguments; * (value, index|key, collection). Callbacks may exit iteration early by * explicitly returning `false`. @@ -3971,14 +3836,24 @@ * // => logs each number and returns the object (property order is not guaranteed across environments) */ function forEach(collection, callback, thisArg) { - return (callback && typeof thisArg == 'undefined' && isArray(collection)) - ? arrayEach(collection, callback) - : baseEach(collection, baseCreateCallback(callback, thisArg, 3)); + if (callback && typeof thisArg == 'undefined' && isArray(collection)) { + var index = -1, + length = collection.length; + + while (++index < length) { + if (callback(collection[index], index, collection) === false) { + break; + } + } + } else { + baseEach(collection, baseCreateCallback(callback, thisArg, 3)); + } + return collection; } /** - * This method is like `_.forEach` except that it iterates over elements of - * a collection from right to left. + * This method is like `_.forEach` except that it iterates over elements + * of a `collection` from right to left. * * @static * @memberOf _ @@ -3994,9 +3869,17 @@ * // => logs each number from right to left and returns '3,2,1' */ function forEachRight(collection, callback, thisArg) { - return (callback && typeof thisArg == 'undefined' && isArray(collection)) - ? arrayEachRight(collection, callback) - : baseEachRight(collection, baseCreateCallback(callback, thisArg, 3)); + if (callback && typeof thisArg == 'undefined' && isArray(collection)) { + var length = collection.length; + while (length--) { + if (callback(collection[length], length, collection) === false) { + break; + } + } + } else { + baseEachRight(collection, baseCreateCallback(callback, thisArg, 3)); + } + return collection; } /** @@ -4019,7 +3902,7 @@ * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback respectively. + * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns the composed aggregate object. * @example @@ -4062,23 +3945,23 @@ * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback respectively. + * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns the composed aggregate object. * @example * - * var keyData = [ + * var keys = [ * { 'dir': 'left', 'code': 97 }, * { 'dir': 'right', 'code': 100 } * ]; * - * _.indexBy(keyData, 'dir'); + * _.indexBy(keys, 'dir'); * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } * - * _.indexBy(keyData, function(object) { return String.fromCharCode(object.code); }); + * _.indexBy(keys, function(key) { return String.fromCharCode(key.code); }); * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } * - * _.indexBy(keyData, function(object) { return this.fromCharCode(object.code); }, String); + * _.indexBy(keys, function(key) { return this.fromCharCode(key.code); }, String); * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } */ var indexBy = createAggregator(function(result, value, key) { @@ -4086,10 +3969,10 @@ }); /** - * Invokes the method named by `methodName` on each element in the collection + * Invokes the method named by `methodName` on each element in the `collection` * returning an array of the results of each invoked method. Additional arguments * will be provided to each invoked method. If `methodName` is a function it - * will be invoked for, and `this` bound to, each element in the collection. + * will be invoked for, and `this` bound to, each element in the `collection`. * * @static * @memberOf _ @@ -4098,7 +3981,7 @@ * @param {Function|string} methodName The name of the method to invoke or * the function invoked per iteration. * @param {...*} [args] Arguments to invoke the method with. - * @returns {Array} Returns the array of results. + * @returns {Array} Returns a new array of the results of each invoked method. * @example * * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); @@ -4108,16 +3991,22 @@ * // => [['1', '2', '3'], ['4', '5', '6']] */ function invoke(collection, methodName) { - var args = slice(arguments, 2), - index = -1, + var index = -1, isFunc = typeof methodName == 'function', - length = collection && collection.length, - result = Array(length < 0 ? 0 : length >>> 0); + length = collection ? collection.length : 0, + result = Array(typeof length == 'number' ? length : 0); - baseEach(collection, function(value) { - var func = isFunc ? methodName : (value != null && value[methodName]); - result[++index] = func ? func.apply(value, args) : undefined; - }); + if (arguments.length < 3 && isArray(collection)) { + while (++index < length) { + var value = collection[index]; + result[index] = isFunc ? methodName.call(value) : value[methodName](); + } + } else { + var args = slice(arguments, 2); + baseEach(collection, function(value) { + result[++index] = (isFunc ? methodName : value[methodName]).apply(value, args); + }); + } return result; } @@ -4140,9 +4029,9 @@ * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback respectively. + * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Array} Returns the new mapped array. + * @returns {Array} Returns a new array of the results of each `callback` execution. * @example * * _.map([1, 2, 3], function(num) { return num * 3; }); @@ -4161,17 +4050,20 @@ * // => ['barney', 'fred'] */ function map(collection, callback, thisArg) { - callback = lodash.createCallback(callback, thisArg, 3); - - if (isArray(collection)) { - return arrayMap(collection, callback, thisArg); - } var index = -1, - result = []; + length = collection ? collection.length : 0, + result = Array(typeof length == 'number' ? length : 0); - baseEach(collection, function(value, key, collection) { - result[++index] = callback(value, key, collection); - }); + callback = lodash.createCallback(callback, thisArg, 3); + if (isArray(collection)) { + while (++index < length) { + result[index] = callback(collection[index], index, collection); + } + } else { + baseEach(collection, function(value, key, collection) { + result[++index] = callback(value, key, collection); + }); + } return result; } @@ -4193,9 +4085,9 @@ * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [callback] The function called per iteration. - * If a property name or object is provided it will be used to create a "_.pluck" - * or "_.where" style callback respectively. + * @param {Function|Object|string} [callback=identity] The function called + * per iteration. If a property name or object is provided it will be used + * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {*} Returns the maximum value. * @example @@ -4203,9 +4095,6 @@ * _.max([4, 2, 8, 6]); * // => 8 * - * _.max([]); - * // => -Infinity - * * var characters = [ * { 'name': 'barney', 'age': 36 }, * { 'name': 'fred', 'age': 40 } @@ -4244,7 +4133,7 @@ baseEach(collection, function(value, index, collection) { var current = callback(value, index, collection); - if (current > computed || (current === -Infinity && current === result)) { + if (current > computed) { computed = current; result = value; } @@ -4271,9 +4160,9 @@ * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [callback] The function called per iteration. - * If a property name or object is provided it will be used to create a "_.pluck" - * or "_.where" style callback respectively. + * @param {Function|Object|string} [callback=identity] The function called + * per iteration. If a property name or object is provided it will be used + * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {*} Returns the minimum value. * @example @@ -4281,9 +4170,6 @@ * _.min([4, 2, 8, 6]); * // => 2 * - * _.min([]); - * // => Infinity - * * var characters = [ * { 'name': 'barney', 'age': 36 }, * { 'name': 'fred', 'age': 40 } @@ -4322,7 +4208,7 @@ baseEach(collection, function(value, index, collection) { var current = callback(value, index, collection); - if (current < computed || (current === Infinity && current === result)) { + if (current < computed) { computed = current; result = value; } @@ -4333,14 +4219,14 @@ /** * Creates an array of elements split into two groups, the first of which - * contains elements the predicate returns truthy for, while the second of which - * contains elements the predicate returns falsey for. The predicate is bound + * contains elements the callback returns truey for, while the second of which + * contains elements the callback returns falsey for. The callback is bound * to `thisArg` and invoked with three arguments; (value, index|key, collection). * - * If a property name is provided for `predicate` the created "_.pluck" style + * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * - * If an object is provided for `predicate` the created "_.where" style callback + * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * @@ -4348,11 +4234,11 @@ * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [predicate=identity] The function called + * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback respectively. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the array of grouped elements. + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Array} Returns a new array of grouped elements. * @example * * _.partition([1, 2, 3], function(num) { return num % 2; }); @@ -4377,17 +4263,18 @@ */ var partition = createAggregator(function(result, value, key) { result[key ? 0 : 1].push(value); - }, function() { return [[], []]; }); + }, true); /** * Retrieves the value of a specified property from all elements in the collection. * * @static * @memberOf _ + * @type Function * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {string} key The name of the property to pluck. - * @returns {Array} Returns the property values. + * @returns {Array} Returns a new array of property values. * @example * * var characters = [ @@ -4398,9 +4285,7 @@ * _.pluck(characters, 'name'); * // => ['barney', 'fred'] */ - function pluck(collection, key) { - return map(collection, property(key)); - } + var pluck = map; /** * Reduces a collection to a value which is the accumulated result of running @@ -4434,8 +4319,8 @@ */ function reduce(collection, callback, accumulator, thisArg) { var noaccum = arguments.length < 3; - callback = lodash.createCallback(callback, thisArg, 4); + callback = lodash.createCallback(callback, thisArg, 4); if (isArray(collection)) { var index = -1, length = collection.length; @@ -4457,8 +4342,8 @@ } /** - * This method is like `_.reduce` except that it iterates over elements of a - * collection from right to left. + * This method is like `_.reduce` except that it iterates over elements + * of a `collection` from right to left. * * @static * @memberOf _ @@ -4471,14 +4356,14 @@ * @returns {*} Returns the accumulated value. * @example * - * var array = [[0, 1], [2, 3], [4, 5]]; - * _.reduceRight(array, function(flattened, other) { return flattened.concat(other); }, []); + * var list = [[0, 1], [2, 3], [4, 5]]; + * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []); * // => [4, 5, 2, 3, 0, 1] */ function reduceRight(collection, callback, accumulator, thisArg) { var noaccum = arguments.length < 3; - callback = lodash.createCallback(callback, thisArg, 4); + callback = lodash.createCallback(callback, thisArg, 4); baseEachRight(collection, function(value, index, collection) { accumulator = noaccum ? (noaccum = false, value) @@ -4488,13 +4373,13 @@ } /** - * The opposite of `_.filter`; this method returns the elements of a collection - * the predicate does **not** return truthy for. + * The opposite of `_.filter`; this method returns the elements of a + * collection that the callback does **not** return truey for. * - * If a property name is provided for `predicate` the created "_.pluck" style + * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * - * If an object is provided for `predicate` the created "_.where" style callback + * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * @@ -4502,11 +4387,11 @@ * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [predicate=identity] The function called + * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback respectively. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the new filtered array. + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Array} Returns a new array of elements that failed the callback check. * @example * * var odds = _.reject([1, 2, 3, 4], function(num) { return num % 2 == 0; }); @@ -4525,9 +4410,11 @@ * _.reject(characters, { 'age': 36 }); * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }] */ - function reject(collection, predicate, thisArg) { - predicate = lodash.createCallback(predicate, thisArg, 3); - return filter(collection, negate(predicate)); + function reject(collection, callback, thisArg) { + callback = lodash.createCallback(callback, thisArg, 3); + return filter(collection, function(value, index, collection) { + return !callback(value, index, collection); + }); } /** @@ -4539,7 +4426,7 @@ * @param {Array|Object|string} collection The collection to sample. * @param {number} [n] The number of elements to sample. * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {*} Returns the random sample(s). + * @returns {*} Returns the random sample(s) of `collection`. * @example * * _.sample([1, 2, 3, 4]); @@ -4555,24 +4442,22 @@ collection = collection.split(''); } if (n == null || guard) { - var length = collection ? collection.length : 0; - return length > 0 ? collection[baseRandom(0, length - 1)] : undefined; + return collection ? collection[baseRandom(0, collection.length - 1)] : undefined; } var result = shuffle(collection); - result.length = nativeMin(n < 0 ? 0 : (+n || 0), result.length); + result.length = nativeMin(nativeMax(0, n), result.length); return result; } /** * Creates an array of shuffled values, using a version of the Fisher-Yates - * shuffle. See [Wikipedia](http://en.wikipedia.org/wiki/Fisher-Yates_shuffle) - * for more details. + * shuffle. See [Wikipedia](http://en.wikipedia.org/wiki/Fisher-Yates_shuffle) for more details. * * @static * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to shuffle. - * @returns {Array} Returns the new shuffled array. + * @returns {Array} Returns a new shuffled collection. * @example * * _.shuffle([1, 2, 3, 4]); @@ -4580,19 +4465,20 @@ */ function shuffle(collection) { var index = -1, - length = collection && collection.length, - result = Array(length < 0 ? 0 : length >>> 0); + length = collection ? collection.length : 0, + result = Array(typeof length == 'number' ? length : 0); baseEach(collection, function(value) { var rand = baseRandom(0, ++index); result[index] = result[rand]; result[rand] = value; }); + return result; } /** - * Gets the size of the collection by returning `collection.length` for arrays + * Gets the size of the `collection` by returning `collection.length` for arrays * and array-like objects or the number of own enumerable properties for objects. * * @static @@ -4613,21 +4499,19 @@ */ function size(collection) { var length = collection ? collection.length : 0; - return (typeof length == 'number' && length > -1 && length <= maxSafeInteger) - ? length - : keys(collection).length; + return typeof length == 'number' ? length : keys(collection).length; } /** - * Checks if the predicate returns truthy for **any** element of a collection. - * The function returns as soon as it finds a passing value and does not iterate - * over the entire collection. The predicate is bound to `thisArg` and invoked - * with three arguments; (value, index|key, collection). + * Checks if the callback returns a truey value for **any** element of a + * collection. The function returns as soon as it finds a passing value and + * does not iterate over the entire collection. The callback is bound to + * `thisArg` and invoked with three arguments; (value, index|key, collection). * - * If a property name is provided for `predicate` the created "_.pluck" style + * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * - * If an object is provided for `predicate` the created "_.where" style callback + * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * @@ -4636,11 +4520,11 @@ * @alias any * @category Collections * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [predicate=identity] The function called + * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback respectively. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {boolean} Returns `true` if any element passed the predicate check, + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {boolean} Returns `true` if any element passed the callback check, * else `false`. * @example * @@ -4660,22 +4544,22 @@ * _.some(characters, { 'age': 1 }); * // => false */ - function some(collection, predicate, thisArg) { + function some(collection, callback, thisArg) { var result; - predicate = lodash.createCallback(predicate, thisArg, 3); + callback = lodash.createCallback(callback, thisArg, 3); if (isArray(collection)) { var index = -1, length = collection.length; while (++index < length) { - if (predicate(collection[index], index, collection)) { + if (callback(collection[index], index, collection)) { return true; } } } else { baseEach(collection, function(value, index, collection) { - return !(result = predicate(value, index, collection)); + return !(result = callback(value, index, collection)); }); } return !!result; @@ -4702,11 +4586,11 @@ * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to iterate over. - * @param {Array|Function|Object|string} [callback=identity] The function - * called per iteration. If a property name or object is provided it will - * be used to create a "_.pluck" or "_.where" style callback respectively. + * @param {Array|Function|Object|string} [callback=identity] The function called + * per iteration. If a property name or object is provided it will be used + * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Array} Returns the new sorted array. + * @returns {Array} Returns a new array of sorted elements. * @example * * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); @@ -4732,9 +4616,9 @@ */ function sortBy(collection, callback, thisArg) { var index = -1, - length = collection && collection.length, multi = callback && isArray(callback), - result = Array(length < 0 ? 0 : length >>> 0); + length = collection ? collection.length : 0, + result = Array(typeof length == 'number' ? length : 0); if (!multi) { callback = lodash.createCallback(callback, thisArg, 3); @@ -4762,7 +4646,7 @@ } /** - * Converts `collection` to an array. + * Converts the `collection` to an array. * * @static * @memberOf _ @@ -4775,8 +4659,7 @@ * // => [2, 3, 4] */ function toArray(collection) { - var length = collection && collection.length; - if (typeof length == 'number' && length > -1 && length <= maxSafeInteger) { + if (collection && typeof collection.length == 'number') { return (support.unindexedChars && isString(collection)) ? collection.split('') : slice(collection); @@ -4786,39 +4669,35 @@ /** * Performs a deep comparison between each element in `collection` and the - * source object, returning an array of all elements that have equivalent + * `source` object, returning an array of all elements that have equivalent * property values. * * @static * @memberOf _ + * @type Function * @category Collections - * @param {Array|Object|string} collection The collection to search. - * @param {Object} source The object of property values to match. - * @returns {Array} Returns the new filtered array. + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Object} source The object of property values to filter by. + * @returns {Array} Returns a new array of elements that have the given properties. * @example * * var characters = [ - * { 'name': 'barney', 'age': 36, 'employer': 'slate', 'pets': ['hoppy'] }, - * { 'name': 'fred', 'age': 40, 'employer': 'slate', 'pets': ['baby puss', 'dino'] } + * { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }, + * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] } * ]; * - * _.pluck(_.where(characters, { 'age': 36 }), 'name'); - * // => ['barney'] + * _.where(characters, { 'age': 36 }); + * // => [{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }] * - * _.pluck(_.where(characters, { 'pets': ['dino'] }), 'name'); - * // => ['fred'] - * - * _.pluck(_.where(characters, { 'employer': 'slate' }), 'name'); - * // => ['barney', 'fred'] + * _.where(characters, { 'pets': ['dino'] }); + * // => [{ 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }] */ - function where(collection, source) { - return filter(collection, matches(source)); - } + var where = filter; /*--------------------------------------------------------------------------*/ /** - * Creates a function that executes `func`, with the `this` binding and + * Creates a function that executes `func`, with the `this` binding and * arguments of the created function, only after being called `n` times. * * @static @@ -4843,9 +4722,8 @@ */ function after(n, func) { if (!isFunction(func)) { - throw new TypeError(funcErrorText); + throw new TypeError; } - n = nativeIsFinite(n = +n) ? n : 0; return function() { if (--n < 1) { return func.apply(this, arguments); @@ -4854,9 +4732,9 @@ } /** - * Creates a function that invokes `func` with the `this` binding of `thisArg` - * and prepends any additional `bind` arguments to those provided to the bound - * function. + * Creates a function that, when called, invokes `func` with the `this` + * binding of `thisArg` and prepends any additional `bind` arguments to those + * provided to the bound function. * * Note: Unlike native `Function#bind` this method does not set the `length` * property of bound functions. @@ -4903,8 +4781,8 @@ * @memberOf _ * @category Functions * @param {Object} object The object to bind and assign the bound methods to. - * @param {...string} [methodNames] The object method names to bind, specified - * as individual method names or arrays of method names. + * @param {...string} [methodName] The object method names to + * bind, specified as individual method names or arrays of method names. * @returns {Object} Returns `object`. * @example * @@ -4930,10 +4808,10 @@ } /** - * Creates a function that invokes the method at `object[key]` and prepends - * any additional `bindKey` arguments to those provided to the bound function. - * This method differs from `_.bind` by allowing bound functions to reference - * methods that will be redefined or don't yet exist. + * Creates a function that, when called, invokes the method at `object[key]` + * and prepends any additional `bindKey` arguments to those provided to the bound + * function. This method differs from `_.bind` by allowing bound functions to + * reference methods that will be redefined or don't yet exist. * See [Peter Michaux's article](http://michaux.ca/articles/lazy-function-definition-pattern) * for more details. * @@ -4979,7 +4857,7 @@ * @static * @memberOf _ * @category Functions - * @param {...Function} [funcs] Functions to compose. + * @param {...Function} [func] Functions to compose. * @returns {Function} Returns the new composed function. * @example * @@ -5007,7 +4885,7 @@ while (length--) { if (!isFunction(funcs[length])) { - throw new TypeError(funcErrorText); + throw new TypeError; } } return function() { @@ -5065,7 +4943,7 @@ * the leading and/or trailing edge of the `wait` timeout. Subsequent calls * to the debounced function will return the result of the last `func` call. * - * Note: If `leading` and `trailing` options are `true`, `func` will be called + * Note: If `leading` and `trailing` options are `true` `func` will be called * on the trailing edge of the timeout only if the the debounced function is * invoked more than once during the `wait` timeout. * @@ -5110,15 +4988,15 @@ trailing = true; if (!isFunction(func)) { - throw new TypeError(funcErrorText); + throw new TypeError; } - wait = wait < 0 ? 0 : wait; + wait = nativeMax(0, wait) || 0; if (options === true) { var leading = true; trailing = false; } else if (isObject(options)) { leading = options.leading; - maxWait = 'maxWait' in options && nativeMax(wait, +options.maxWait || 0); + maxWait = 'maxWait' in options && (nativeMax(wait, options.maxWait) || 0); trailing = 'trailing' in options ? options.trailing : trailing; } var delayed = function() { @@ -5215,7 +5093,7 @@ */ function defer(func) { if (!isFunction(func)) { - throw new TypeError(funcErrorText); + throw new TypeError; } var args = slice(arguments, 1); return setTimeout(function() { func.apply(undefined, args); }, 1); @@ -5239,7 +5117,7 @@ */ function delay(func, wait) { if (!isFunction(func)) { - throw new TypeError(funcErrorText); + throw new TypeError; } var args = slice(arguments, 2); return setTimeout(function() { func.apply(undefined, args); }, wait); @@ -5257,7 +5135,7 @@ * @memberOf _ * @category Functions * @param {Function} func The function to have its output memoized. - * @param {Function} [resolver] The function to resolve the cache key. + * @param {Function} [resolver] A function used to resolve the cache key. * @returns {Function} Returns the new memoizing function. * @example * @@ -5283,8 +5161,8 @@ * // => { 'name': 'penelope', 'age': 1 } */ function memoize(func, resolver) { - if (!isFunction(func) || (resolver && !isFunction(resolver))) { - throw new TypeError(funcErrorText); + if (!isFunction(func)) { + throw new TypeError; } var memoized = function() { var cache = memoized.cache, @@ -5298,34 +5176,6 @@ return memoized; } - /** - * Creates a function that negates the result of the predicate `func`. The - * `func` function is executed with the `this` binding and arguments of the - * created function. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} predicate The predicate to negate. - * @returns {Function} Returns the new function. - * @example - * - * function isEven(num) { - * return num % 2 == 0; - * } - * - * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven)); - * // => [1, 3, 5] - */ - function negate(predicate) { - if (!isFunction(predicate)) { - throw new TypeError(funcErrorText); - } - return function() { - return !predicate.apply(this, arguments); - }; - } - /** * Creates a function that is restricted to execute `func` once. Repeat calls to * the function will return the value of the first call. The `func` is executed @@ -5348,7 +5198,7 @@ result; if (!isFunction(func)) { - throw new TypeError(funcErrorText); + throw new TypeError; } return function() { if (ran) { @@ -5364,9 +5214,9 @@ } /** - * Creates a function that invokes `func` with any additional `partial` arguments - * prepended to those provided to the new function. This method is similar to - * `_.bind` except it does **not** alter the `this` binding. + * Creates a function that, when called, invokes `func` with any additional + * `partial` arguments prepended to those provided to the new function. This + * method is similar to `_.bind` except it does **not** alter the `this` binding. * * Note: This method does not set the `length` property of partially applied * functions. @@ -5409,15 +5259,20 @@ * @returns {Function} Returns the new partially applied function. * @example * - * var defaultsDeep = _.partialRight(_.merge, function deep(value, other) { - * return _.merge(value, other, deep); - * }); + * var defaultsDeep = _.partialRight(_.merge, _.defaults); * - * var object = { 'a': { 'b': { 'c': 1 } } }, - * source = { 'a': { 'b': { 'c': 2, 'd': 2 } } }; + * var options = { + * 'variable': 'data', + * 'imports': { 'jq': $ } + * }; * - * defaultsDeep(object, source); - * // => { 'a': { 'b': { 'c': 1, 'd': 2 } } } + * defaultsDeep(options, _.templateSettings); + * + * options.variable + * // => 'data' + * + * options.imports + * // => { '_': _, 'jq': $ } */ function partialRight(func) { if (func) { @@ -5436,7 +5291,7 @@ * of the `wait` timeout. Subsequent calls to the throttled function will * return the result of the last `func` call. * - * Note: If `leading` and `trailing` options are `true`, `func` will be called + * Note: If `leading` and `trailing` options are `true` `func` will be called * on the trailing edge of the timeout only if the the throttled function is * invoked more than once during the `wait` timeout. * @@ -5465,16 +5320,16 @@ trailing = true; if (!isFunction(func)) { - throw new TypeError(funcErrorText); + throw new TypeError; } if (options === false) { leading = false; } else if (isObject(options)) { - leading = 'leading' in options ? !!options.leading : leading; - trailing = 'trailing' in options ? !!options.trailing : trailing; + leading = 'leading' in options ? options.leading : leading; + trailing = 'trailing' in options ? options.trailing : trailing; } debounceOptions.leading = leading; - debounceOptions.maxWait = +wait; + debounceOptions.maxWait = wait; debounceOptions.trailing = trailing; return debounce(func, wait, debounceOptions); @@ -5507,6 +5362,7 @@ /*--------------------------------------------------------------------------*/ + /** * Assigns own enumerable properties of source object(s) to the destination * object. Subsequent sources will overwrite property assignments of previous @@ -5519,7 +5375,7 @@ * @alias extend * @category Objects * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. + * @param {...Object} [source] The source objects. * @param {Function} [callback] The function to customize assigning values. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns the destination object. @@ -5528,19 +5384,16 @@ * _.assign({ 'name': 'fred' }, { 'employer': 'slate' }); * // => { 'name': 'fred', 'employer': 'slate' } * - * var defaults = _.partialRight(_.assign, function(value, other) { - * return typeof value == 'undefined' ? other : value; + * var defaults = _.partialRight(_.assign, function(a, b) { + * return typeof a == 'undefined' ? b : a; * }); * * defaults({ 'name': 'barney' }, { 'name': 'fred', 'employer': 'slate' }); * // => { 'name': 'barney', 'employer': 'slate' } */ function assign(object, source, guard) { - var args = arguments; - if (!object || args.length < 2) { - return object; - } - var argsIndex = 0, + var args = arguments, + argsIndex = 0, argsLength = args.length, type = typeof guard; @@ -5556,13 +5409,15 @@ } while (++argsIndex < argsLength) { source = args[argsIndex]; - var index = -1, - props = keys(source), - length = props.length; + if (isObject(source)) { + var index = -1, + props = keys(source), + length = props.length; - while (++index < length) { - var key = props[index]; - object[key] = callback ? callback(object[key], source[key]) : source[key]; + while (++index < length) { + var key = props[index]; + object[key] = callback ? callback(object[key], source[key]) : source[key]; + } } } return object; @@ -5720,14 +5575,11 @@ * object for all destination properties that resolve to `undefined`. Once a * property is set, additional defaults of the same property will be ignored. * - * Note: See the [documentation example of `_.partialRight`](http://lodash.com/docs#partialRight) - * for a deep version of this method. - * * @static * @memberOf _ * @category Objects * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. + * @param {...Object} [source] The source objects. * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`. * @returns {Object} Returns the destination object. * @example @@ -5735,23 +5587,42 @@ * _.defaults({ 'name': 'barney' }, { 'name': 'fred', 'employer': 'slate' }); * // => { 'name': 'barney', 'employer': 'slate' } */ - function defaults(object) { - if (!object) { - return object; + function defaults(object, source, guard) { + var args = arguments, + argsIndex = 0, + argsLength = args.length, + type = typeof guard; + + // enables use as a callback for functions like `_.reduce` + if ((type == 'number' || type == 'string') && args[3] && args[3][guard] === source) { + argsLength = 2; } - var args = slice(arguments); - args.push(assignDefaults); - return assign.apply(null, args); + while (++argsIndex < argsLength) { + source = args[argsIndex]; + if (isObject(source)) { + var index = -1, + props = keys(source), + length = props.length; + + while (++index < length) { + var key = props[index]; + if (typeof object[key] == 'undefined') { + object[key] = source[key]; + } + } + } + } + return object; } /** * This method is like `_.findIndex` except that it returns the key of the - * first element the predicate returns truthy for, instead of the element itself. + * first element that passes the callback check, instead of the element itself. * - * If a property name is provided for `predicate` the created "_.pluck" style + * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * - * If an object is provided for `predicate` the created "_.where" style callback + * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * @@ -5759,11 +5630,11 @@ * @memberOf _ * @category Objects * @param {Object} object The object to search. - * @param {Function|Object|string} [predicate=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback respectively. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {string|undefined} Returns the key of the matched element, else `undefined`. + * @param {Function|Object|string} [callback=identity] The function called per + * iteration. If a property name or object is provided it will be used to + * create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {string|undefined} Returns the key of the found element, else `undefined`. * @example * * var characters = { @@ -5785,19 +5656,27 @@ * _.findKey(characters, 'blocked'); * // => 'fred' */ - function findKey(object, predicate, thisArg) { - predicate = lodash.createCallback(predicate, thisArg, 3); - return baseFind(object, predicate, baseForOwn, true); + function findKey(object, callback, thisArg) { + var result; + + callback = lodash.createCallback(callback, thisArg, 3); + baseForOwn(object, function(value, key, object) { + if (callback(value, key, object)) { + result = key; + return false; + } + }); + return result; } /** - * This method is like `_.findKey` except that it iterates over elements of - * a collection in the opposite order. + * This method is like `_.findKey` except that it iterates over elements + * of a `collection` in the opposite order. * - * If a property name is provided for `predicate` the created "_.pluck" style + * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * - * If an object is provided for `predicate` the created "_.where" style callback + * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * @@ -5805,11 +5684,11 @@ * @memberOf _ * @category Objects * @param {Object} object The object to search. - * @param {Function|Object|string} [predicate=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback respectively. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {string|undefined} Returns the key of the matched element, else `undefined`. + * @param {Function|Object|string} [callback=identity] The function called per + * iteration. If a property name or object is provided it will be used to + * create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {string|undefined} Returns the key of the found element, else `undefined`. * @example * * var characters = { @@ -5831,19 +5710,28 @@ * _.findLastKey(characters, 'blocked'); * // => 'pebbles' */ - function findLastKey(object, predicate, thisArg) { - predicate = lodash.createCallback(predicate, thisArg, 3); - return baseFind(object, predicate, baseForOwnRight, true); + function findLastKey(object, callback, thisArg) { + var result; + + callback = lodash.createCallback(callback, thisArg, 3); + baseForOwnRight(object, function(value, key, object) { + if (callback(value, key, object)) { + result = key; + return false; + } + }); + return result; } /** - * Iterates over own and inherited enumerable properties of an object executing - * the callback for each property. The callback is bound to `thisArg` and invoked - * with three arguments; (value, key, object). Callbacks may exit iteration - * early by explicitly returning `false`. + * Iterates over own and inherited enumerable properties of an object, + * executing the callback for each property. The callback is bound to `thisArg` + * and invoked with three arguments; (value, key, object). Callbacks may exit + * iteration early by explicitly returning `false`. * * @static * @memberOf _ + * @type Function * @category Objects * @param {Object} object The object to iterate over. * @param {Function} [callback=identity] The function called per iteration. @@ -5856,21 +5744,24 @@ * this.y = 0; * } * - * Shape.prototype.z = 0; + * Shape.prototype.move = function(x, y) { + * this.x += x; + * this.y += y; + * }; * * _.forIn(new Shape, function(value, key) { * console.log(key); * }); - * // => logs 'x', 'y', and 'z' (property order is not guaranteed across environments) + * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments) */ function forIn(object, callback, thisArg) { callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3); - return baseFor(object, callback, keysIn); + return baseForIn(object, callback); } /** - * This method is like `_.forIn` except that it iterates over elements of a - * collection in the opposite order. + * This method is like `_.forIn` except that it iterates over elements + * of a `collection` in the opposite order. * * @static * @memberOf _ @@ -5886,20 +5777,34 @@ * this.y = 0; * } * - * Shape.prototype.z = 0; + * Shape.prototype.move = function(x, y) { + * this.x += x; + * this.y += y; + * }; * * _.forInRight(new Shape, function(value, key) { * console.log(key); * }); - * // => logs 'z', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'z' + * // => logs 'move', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'move' */ function forInRight(object, callback, thisArg) { + var pairs = []; + baseForIn(object, function(value, key) { + pairs.push(key, value); + }); + + var length = pairs.length; callback = baseCreateCallback(callback, thisArg, 3); - return baseForRight(object, callback, keysIn); + while (length--) { + if (callback(pairs[length--], pairs[length], object) === false) { + break; + } + } + return object; } /** - * Iterates over own enumerable properties of an object executing the callback + * Iterates over own enumerable properties of an object, executing the callback * for each property. The callback is bound to `thisArg` and invoked with three * arguments; (value, key, object). Callbacks may exit iteration early by * explicitly returning `false`. @@ -5924,8 +5829,8 @@ } /** - * This method is like `_.forOwn` except that it iterates over elements of a - * collection in the opposite order. + * This method is like `_.forOwn` except that it iterates over elements + * of a `collection` in the opposite order. * * @static * @memberOf _ @@ -5942,8 +5847,17 @@ * // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length' */ function forOwnRight(object, callback, thisArg) { + var props = keys(object), + length = props.length; + callback = baseCreateCallback(callback, thisArg, 3); - return baseForRight(object, callback, keys); + while (length--) { + var key = props[length]; + if (callback(object[key], key, object) === false) { + break; + } + } + return object; } /** @@ -5955,7 +5869,7 @@ * @alias methods * @category Objects * @param {Object} object The object to inspect. - * @returns {Array} Returns the new sorted array of property names. + * @returns {Array} Returns an array of property names that have function values. * @example * * _.functions(_); @@ -5963,7 +5877,6 @@ */ function functions(object) { var result = []; - baseForIn(object, function(value, key) { if (isFunction(value)) { result.push(key); @@ -6002,7 +5915,7 @@ * @category Objects * @param {Object} object The object to invert. * @param {boolean} [multiValue=false] Allow multiple values per key. - * @returns {Object} Returns the new inverted object. + * @returns {Object} Returns the created inverted object. * @example * * _.invert({ 'first': 'fred', 'second': 'barney' }); @@ -6040,53 +5953,26 @@ return result; } - /** - * Checks if `value` is an `arguments` object. - * - * @static - * @memberOf _ - * @category Objects - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an `arguments` object, else `false`. - * @example - * - * (function() { return _.isArguments(arguments); })(); - * // => true - * - * _.isArguments([1, 2, 3]); - * // => false - */ - function isArguments(value) { - return (value && typeof value == 'object' && typeof value.length == 'number' && - toString.call(value) == argsClass) || false; - } - // fallback for environments without a `[[Class]]` for `arguments` objects - if (!support.argsClass) { - isArguments = function(value) { - return (value && typeof value == 'object' && typeof value.length == 'number' && - hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee')) || false; - }; - } - /** * Checks if `value` is an array. * * @static * @memberOf _ + * @type Function * @category Objects * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an array, else `false`. + * @returns {boolean} Returns `true` if the `value` is an array, else `false`. * @example * - * _.isArray([1, 2, 3]); - * // => true - * * (function() { return _.isArray(arguments); })(); * // => false + * + * _.isArray([1, 2, 3]); + * // => true */ var isArray = nativeIsArray || function(value) { - return (value && typeof value == 'object' && typeof value.length == 'number' && - toString.call(value) == arrayClass) || false; + return value && typeof value == 'object' && typeof value.length == 'number' && + toString.call(value) == arrayClass || false; }; /** @@ -6096,38 +5982,32 @@ * @memberOf _ * @category Objects * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a boolean value, else `false`. + * @returns {boolean} Returns `true` if the `value` is a boolean value, else `false`. * @example * - * _.isBoolean(false); - * // => true - * * _.isBoolean(null); * // => false */ function isBoolean(value) { - return (value === true || value === false || - value && typeof value == 'object' && toString.call(value) == boolClass) || false; + return value === true || value === false || + value && typeof value == 'object' && toString.call(value) == boolClass || false; } /** - * Checks if `value` is a `Date` object. + * Checks if `value` is a date. * * @static * @memberOf _ * @category Objects * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a date object, else `false`. + * @returns {boolean} Returns `true` if the `value` is a date, else `false`. * @example * * _.isDate(new Date); * // => true - * - * _.isDate('Mon April 23 2012'); - * // => false */ function isDate(value) { - return (value && typeof value == 'object' && toString.call(value) == dateClass) || false; + return value && typeof value == 'object' && toString.call(value) == dateClass || false; } /** @@ -6137,63 +6017,56 @@ * @memberOf _ * @category Objects * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. + * @returns {boolean} Returns `true` if the `value` is a DOM element, else `false`. * @example * * _.isElement(document.body); * // => true - * - * _.isElement(''); - * // => false */ function isElement(value) { - return (value && typeof value == 'object' && value.nodeType === 1 && - (support.nodeClass ? toString.call(value).indexOf('Element') > -1 : isNode(value))) || false; + return value && typeof value == 'object' && value.nodeType === 1 && + (support.nodeClass ? toString.call(value).indexOf('Element') > -1 : isNode(value)) || false; } // fallback for environments without DOM support if (!support.dom) { isElement = function(value) { - return (value && typeof value == 'object' && value.nodeType === 1 && - !isPlainObject(value)) || false; + return value && typeof value == 'object' && value.nodeType === 1 && + !isPlainObject(value) || false; }; } /** - * Checks if a collection is empty. A value is considered empty unless it is - * an array, array-like object, or string with a length greater than `0` or - * an object with own properties. + * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a + * length of `0` and objects with no own enumerable properties are considered + * "empty". * * @static * @memberOf _ * @category Objects * @param {Array|Object|string} value The value to inspect. - * @returns {boolean} Returns `true` if `value` is empty, else `false`. + * @returns {boolean} Returns `true` if the `value` is empty, else `false`. * @example * - * _.isEmpty(null); - * // => true - * - * _.isEmpty(true); - * // => true - * - * _.isEmpty(1); - * // => true - * * _.isEmpty([1, 2, 3]); * // => false * - * _.isEmpty({ 'a': 1 }); - * // => false + * _.isEmpty({}); + * // => true + * + * _.isEmpty(''); + * // => true */ function isEmpty(value) { var result = true; if (!value) { return result; } - var length = value.length; - if ((length > -1 && length <= maxSafeInteger) && - (isArray(value) || isString(value) || isArguments(value) || - (typeof value == 'object' && isFunction(value.splice)))) { + var className = toString.call(value), + length = value.length; + + if ((className == arrayClass || className == stringClass || + (support.argsClass ? className == argsClass : isArguments(value))) || + (className == objectClass && typeof length == 'number' && isFunction(value.splice))) { return !length; } baseForOwn(value, function() { @@ -6204,83 +6077,61 @@ /** * Performs a deep comparison between two values to determine if they are - * equivalent. If a callback is provided it will be executed to compare - * values. If the callback returns `undefined` comparisons will be handled - * by the method instead. The callback is bound to `thisArg` and invoked - * with two arguments; (value, other). - * - * Note: This method supports comparing arrays, booleans, `Date` objects, - * numbers, `Object` objects, regexes, and strings. Functions and DOM nodes - * are **not** supported. A callback may be used to extend support for - * comparing other values. + * equivalent to each other. If a callback is provided it will be executed + * to compare values. If the callback returns `undefined` comparisons will + * be handled by the method instead. The callback is bound to `thisArg` and + * invoked with two arguments; (a, b). * * @static * @memberOf _ * @category Objects - * @param {*} value The value to compare to `other`. - * @param {*} other The value to compare to `value`. + * @param {*} a The value to compare. + * @param {*} b The other value to compare. * @param {Function} [callback] The function to customize comparing values. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. * @example * * var object = { 'name': 'fred' }; - * var other = { 'name': 'fred' }; + * var copy = { 'name': 'fred' }; * - * object == other; + * object == copy; * // => false * - * _.isEqual(object, other); + * _.isEqual(object, copy); * // => true * * var words = ['hello', 'goodbye']; * var otherWords = ['hi', 'goodbye']; * - * _.isEqual(words, otherWords, function() { - * return _.every(arguments, _.bind(RegExp.prototype.test, /^h(?:i|ello)$/)) || undefined; + * _.isEqual(words, otherWords, function(a, b) { + * var reGreet = /^(?:hello|hi)$/i, + * aGreet = _.isString(a) && reGreet.test(a), + * bGreet = _.isString(b) && reGreet.test(b); + * + * return (aGreet || bGreet) ? (aGreet == bGreet) : undefined; * }); * // => true */ - function isEqual(value, other, callback, thisArg) { + function isEqual(a, b, callback, thisArg) { callback = typeof callback == 'function' && baseCreateCallback(callback, thisArg, 2); if (!callback) { // exit early for identical values - if (value === other) { + if (a === b) { // treat `-0` vs. `+0` as not equal - return value !== 0 || (1 / value == 1 / other); + return a !== 0 || (1 / a == 1 / b); } - var valType = typeof value, - othType = typeof other; + var type = typeof a, + otherType = typeof b; // exit early for unlike primitive values - if (value === value && (value == null || other == null || - (valType != 'function' && valType != 'object' && othType != 'function' && othType != 'object'))) { + if (a === a && (a == null || b == null || + (type != 'function' && type != 'object' && otherType != 'function' && otherType != 'object'))) { return false; } } - return baseIsEqual(value, other, callback); - } - - /** - * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, - * `SyntaxError`, `TypeError`, or `URIError` object. - * - * @static - * @memberOf _ - * @category Objects - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an error object, else `false`. - * @example - * - * _.isError(new Error); - * // => true - * - * _.isError(Error); - * // => false - */ - function isError(value) { - return (value && typeof value == 'object' && toString.call(value) == errorClass) || false; + return baseIsEqual(a, b, callback); } /** @@ -6294,7 +6145,7 @@ * @memberOf _ * @category Objects * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is finite, else `false`. + * @returns {boolean} Returns `true` if the `value` is finite, else `false`. * @example * * _.isFinite(-101); @@ -6323,14 +6174,11 @@ * @memberOf _ * @category Objects * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a function, else `false`. + * @returns {boolean} Returns `true` if the `value` is a function, else `false`. * @example * * _.isFunction(_); * // => true - * - * _.isFunction(/abc/); - * // => false */ function isFunction(value) { return typeof value == 'function'; @@ -6343,14 +6191,14 @@ } /** - * Checks if `value` is the language type of `Object`. + * Checks if `value` is the language type of Object. * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * * @static * @memberOf _ * @category Objects * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @returns {boolean} Returns `true` if the `value` is an object, else `false`. * @example * * _.isObject({}); @@ -6363,12 +6211,12 @@ * // => false */ function isObject(value) { - // check if the value is the ECMAScript language type of `Object` + // check if the value is the ECMAScript language type of Object // http://es5.github.io/#x8 // and avoid a V8 bug // https://code.google.com/p/v8/issues/detail?id=2291 var type = typeof value; - return type == 'function' || (value && type == 'object') || false; + return value && (type == 'function' || type == 'object') || false; } /** @@ -6382,7 +6230,7 @@ * @memberOf _ * @category Objects * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. + * @returns {boolean} Returns `true` if the `value` is `NaN`, else `false`. * @example * * _.isNaN(NaN); @@ -6399,7 +6247,7 @@ */ function isNaN(value) { // `NaN` as a primitive is the only value that is not equal to itself - // (perform the `[[Class]]` check first to avoid errors with some host objects in IE) + // (perform the [[Class]] check first to avoid errors with some host objects in IE) return isNumber(value) && value != +value; } @@ -6410,13 +6258,13 @@ * @memberOf _ * @category Objects * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `null`, else `false`. + * @returns {boolean} Returns `true` if the `value` is `null`, else `false`. * @example * * _.isNull(null); * // => true * - * _.isNull(void 0); + * _.isNull(undefined); * // => false */ function isNull(value) { @@ -6424,7 +6272,7 @@ } /** - * Checks if `value` is a `Number` primitive or object. + * Checks if `value` is a number. * * Note: `NaN` is considered a number. See the [ES5 spec](http://es5.github.io/#x8.5) * for more details. @@ -6433,30 +6281,20 @@ * @memberOf _ * @category Objects * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a number, else `false`. + * @returns {boolean} Returns `true` if the `value` is a number, else `false`. * @example * - * _.isNumber(8.4); + * _.isNumber(8.4 * 5); * // => true - * - * _.isNumber(NaN); - * // => true - * - * _.isNumber('8.4'); - * // => false */ function isNumber(value) { var type = typeof value; return type == 'number' || - (value && type == 'object' && toString.call(value) == numberClass) || false; + value && type == 'object' && toString.call(value) == numberClass || false; } /** - * Checks if `value` is an object created by the `Object` constructor or has - * a `[[Prototype]]` of `null`. - * - * Note: This method assumes objects created by the `Object` constructor - * have no inherited enumerable properties. + * Checks if `value` is an object created by the `Object` constructor. * * @static * @memberOf _ @@ -6478,9 +6316,6 @@ * * _.isPlainObject({ 'x': 0, 'y': 0 }); * // => true - * - * _.isPlainObject(Object.create(null)); - * // => true */ var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) { if (!(value && toString.call(value) == objectClass) || (!support.argsClass && isArguments(value))) { @@ -6495,44 +6330,40 @@ }; /** - * Checks if `value` is a `RegExp` object. + * Checks if `value` is a regular expression. * * @static * @memberOf _ * @category Objects * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a regexp object, else `false`. + * @returns {boolean} Returns `true` if the `value` is a regular expression, else `false`. * @example * - * _.isRegExp(/abc/); + * _.isRegExp(/fred/); * // => true - * - * _.isRegExp('/abc/'); - * // => false */ function isRegExp(value) { - return (isObject(value) && toString.call(value) == regexpClass) || false; + var type = typeof value; + return value && (type == 'function' || type == 'object') && + toString.call(value) == regexpClass || false; } /** - * Checks if `value` is a `String` primitive or object. + * Checks if `value` is a string. * * @static * @memberOf _ * @category Objects * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a string, else `false`. + * @returns {boolean} Returns `true` if the `value` is a string, else `false`. * @example * - * _.isString('abc'); + * _.isString('fred'); * // => true - * - * _.isString(1); - * // => false */ function isString(value) { return typeof value == 'string' || - (value && typeof value == 'object' && toString.call(value) == stringClass) || false; + value && typeof value == 'object' && toString.call(value) == stringClass || false; } /** @@ -6542,121 +6373,39 @@ * @memberOf _ * @category Objects * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. + * @returns {boolean} Returns `true` if the `value` is `undefined`, else `false`. * @example * * _.isUndefined(void 0); * // => true - * - * _.isUndefined(null); - * // => false */ function isUndefined(value) { return typeof value == 'undefined'; } /** - * Creates an array of the own enumerable property names of `object`. + * Creates an array composed of the own enumerable property names of an object. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to inspect. - * @returns {Array} Returns the array of property names. + * @returns {Array} Returns an array of property names. * @example * - * function Shape() { - * this.x = 0; - * this.y = 0; - * } - * - * Shape.prototype.z = 0; - * - * _.keys(new Shape); - * // => ['x', 'y'] (property order is not guaranteed across environments) + * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); + * // => ['one', 'two', 'three'] (property order is not guaranteed across environments) */ var keys = !nativeKeys ? shimKeys : function(object) { - var ctor = object && object.constructor, - length = object ? object.length : 0; - - if ((typeof length == 'number' && length > 0) || - (ctor && object === ctor.prototype)) { - return shimKeys(object); - } - return isObject(object) ? nativeKeys(object) : []; - }; - - /** - * Creates an array of the own and inherited enumerable property names of `object`. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to inspect. - * @returns {Array} Returns the array of property names. - * @example - * - * function Shape() { - * this.x = 0; - * this.y = 0; - * } - * - * Shape.prototype.z = 0; - * - * _.keysIn(new Shape); - * // => ['x', 'y', 'z'] (property order is not guaranteed across environments) - */ - function keysIn(object) { if (!isObject(object)) { return []; } - var length = object.length; - length = (typeof length == 'number' && length > 0 && - (isArray(object) || (support.nonEnumStrings && isString(object)) || - (support.nonEnumArgs && isArguments(object))) && length) >>> 0; - - var keyIndex, - ctor = object.constructor, - index = -1, - isProto = ctor && object === ctor.prototype, - maxIndex = length - 1, - result = Array(length), - skipIndexes = length > 0, - skipErrorProps = support.enumErrorProps && (object === errorProto || object instanceof Error), - skipProto = support.enumPrototypes && typeof object == 'function'; - - while (++index < length) { - result[index] = String(index); + if ((support.enumPrototypes && typeof object == 'function') || + (support.nonEnumArgs && object.length && isArguments(object))) { + return shimKeys(object); } - for (var key in object) { - if (!(isProto && key == 'constructor') && - !(skipProto && key == 'prototype') && - !(skipErrorProps && (key == 'message' || key == 'name')) && - !(skipIndexes && (keyIndex = +key, keyIndex > -1 && keyIndex <= maxIndex && keyIndex % 1 == 0))) { - result.push(key); - } - } - // Lo-Dash skips the `constructor` property when it infers it's iterating - // over a `prototype` object because IE < 9 can't set the `[[Enumerable]]` - // attribute of an existing property and the `constructor` property of a - // prototype defaults to non-enumerable. - if (support.nonEnumShadows && object !== objectProto) { - index = -1; - length = shadowedProps.length; - - if (isProto) { - var className = object === stringProto ? stringClass : object === errorProto ? errorClass : toString.call(object), - nonEnum = nonEnumProps[className]; - } - while (++index < length) { - key = shadowedProps[index]; - if (!(nonEnum && nonEnum[key]) && hasOwnProperty.call(object, key)) { - result.push(key); - } - } - } - return result; - } + return nativeKeys(object); + }; /** * Creates an object with the same keys as `object` and values generated by @@ -6677,9 +6426,9 @@ * @param {Object} object The object to iterate over. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback respectively. + * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Object} Returns the new mapped object. + * @returns {Object} Returns a new object with values of the results of each `callback` execution. * @example * * _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; }); @@ -6696,8 +6445,8 @@ */ function mapValues(object, callback, thisArg) { var result = {}; - callback = lodash.createCallback(callback, thisArg, 3); + callback = lodash.createCallback(callback, thisArg, 3); baseForOwn(object, function(value, key, object) { result[key] = callback(value, key, object); }); @@ -6717,7 +6466,7 @@ * @memberOf _ * @category Objects * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. + * @param {...Object} [source] The source objects. * @param {Function} [callback] The function to customize merging properties. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns the destination object. @@ -6756,13 +6505,13 @@ * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] } */ function merge(object, source, guard) { + if (!isObject(object)) { + return object; + } var args = arguments, length = args.length, type = typeof guard; - if (!object || length < 2) { - return object; - } // enables use as a callback for functions like `_.reduce` if ((type == 'number' || type == 'string') && args[3] && args[3][guard] === source) { length = 2; @@ -6773,7 +6522,7 @@ } else if (length > 2 && typeof args[length - 1] == 'function') { callback = args[--length]; } - var sources = slice(args, 1, length), + var sources = slice(arguments, 1, length), index = -1, stackA = [], stackB = []; @@ -6787,20 +6536,20 @@ /** * Creates a shallow clone of `object` excluding the specified properties. * Property names may be specified as individual arguments or as arrays of - * property names. If a predicate is provided it will be executed for each - * property of `object` omitting the properties the predicate returns truthy - * for. The predicate is bound to `thisArg` and invoked with three arguments; + * property names. If a callback is provided it will be executed for each + * property of `object` omitting the properties the callback returns truey + * for. The callback is bound to `thisArg` and invoked with three arguments; * (value, key, object). * * @static * @memberOf _ * @category Objects * @param {Object} object The source object. - * @param {Function|...string|string[]} [predicate] The function called per + * @param {Function|...string|string[]} [callback] The function called per * iteration or property names to omit, specified as individual property * names or arrays of property names. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Object} Returns the new object. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns an object without the omitted properties. * @example * * _.omit({ 'name': 'fred', 'age': 40 }, 'age'); @@ -6811,29 +6560,49 @@ * }); * // => { 'name': 'fred' } */ - function omit(object, predicate, thisArg) { - if (typeof predicate == 'function') { - predicate = lodash.createCallback(predicate, thisArg, 3); - return pick(object, negate(predicate)); - } - var omitProps = baseFlatten(arguments, true, false, 1), - length = omitProps.length; + function omit(object, callback, thisArg) { + var result = {}; - while (length--) { - omitProps[length] = String(omitProps[length]); + if (typeof callback != 'function') { + var omitProps = baseFlatten(arguments, true, false, 1), + length = omitProps.length; + + while (length--) { + omitProps[length] = String(omitProps[length]); + } + var props = []; + baseForIn(object, function(value, key) { + props.push(key); + }); + + var index = -1; + props = baseDifference(props, omitProps); + length = props.length; + + while (++index < length) { + var key = props[index]; + result[key] = object[key]; + } + } else { + callback = lodash.createCallback(callback, thisArg, 3); + baseForIn(object, function(value, key, object) { + if (!callback(value, key, object)) { + result[key] = value; + } + }); } - return pick(object, baseDifference(keysIn(object), omitProps)); + return result; } /** - * Creates a two dimensional array of a given object's key-value pairs, + * Creates a two dimensional array of an object's key-value pairs, * i.e. `[[key1, value1], [key2, value2]]`. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to inspect. - * @returns {Array} Returns the new array of key-value pairs. + * @returns {Array} Returns new array of key-value pairs. * @example * * _.pairs({ 'barney': 36, 'fred': 40 }); @@ -6855,20 +6624,20 @@ /** * Creates a shallow clone of `object` composed of the specified properties. * Property names may be specified as individual arguments or as arrays of - * property names. If a predicate is provided it will be executed for each - * property of `object` picking the properties the predicate returns truthy - * for. The predicate is bound to `thisArg` and invoked with three arguments; + * property names. If a callback is provided it will be executed for each + * property of `object` picking the properties the callback returns truey + * for. The callback is bound to `thisArg` and invoked with three arguments; * (value, key, object). * * @static * @memberOf _ * @category Objects * @param {Object} object The source object. - * @param {Function|...string|string[]} [predicate] The function called per + * @param {Function|...string|string[]} [callback] The function called per * iteration or property names to pick, specified as individual property * names or arrays of property names. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Object} Returns the new object. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns an object composed of the picked properties. * @example * * _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name'); @@ -6879,10 +6648,10 @@ * }); * // => { 'name': 'fred' } */ - function pick(object, predicate, thisArg) { + function pick(object, callback, thisArg) { var result = {}; - if (typeof predicate != 'function') { + if (typeof callback != 'function') { var index = -1, props = baseFlatten(arguments, true, false, 1), length = isObject(object) ? props.length : 0; @@ -6894,9 +6663,9 @@ } } } else { - predicate = lodash.createCallback(predicate, thisArg, 3); + callback = lodash.createCallback(callback, thisArg, 3); baseForIn(object, function(value, key, object) { - if (predicate(value, key, object)) { + if (callback(value, key, object)) { result[key] = value; } }); @@ -6941,16 +6710,15 @@ if (isArr) { accumulator = []; } else { - if (isObject(object)) { - var ctor = object.constructor, - proto = ctor && ctor.prototype; - } + var ctor = object && object.constructor, + proto = ctor && ctor.prototype; + accumulator = baseCreate(proto); } } if (callback) { callback = lodash.createCallback(callback, thisArg, 4); - (isArr ? arrayEach : baseForOwn)(object, function(value, index, object) { + (isArr ? baseEach : baseForOwn)(object, function(value, index, object) { return callback(accumulator, value, index, object); }); } @@ -6958,90 +6726,39 @@ } /** - * Creates an array of the own enumerable property values of `object`. + * Creates an array composed of the own enumerable property values of `object`. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to inspect. - * @returns {Array} Returns the array of property values. + * @returns {Array} Returns an array of property values. * @example * - * function Shape(x, y) { - * this.x = x; - * this.y = y; - * } - * - * Shape.prototype.z = 0; - * - * _.values(new Shape(2, 1)); - * // => [2, 1] (property order is not guaranteed across environments) + * _.values({ 'one': 1, 'two': 2, 'three': 3 }); + * // => [1, 2, 3] (property order is not guaranteed across environments) */ function values(object) { - return baseValues(object, keys); - } + var index = -1, + props = keys(object), + length = props.length, + result = Array(length); - /** - * Creates an array of the own and inherited enumerable property values - * of `object`. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to inspect. - * @returns {Array} Returns the array of property values. - * @example - * - * function Shape(x, y) { - * this.x = x; - * this.y = y; - * } - * - * Shape.prototype.z = 0; - * - * _.valuesIn(new Shape(2, 1)); - * // => [2, 1, 0] (property order is not guaranteed across environments) - */ - function valuesIn(object) { - return baseValues(object, keysIn); + while (++index < length) { + result[index] = object[props[index]]; + } + return result; } /*--------------------------------------------------------------------------*/ /** - * Converts `string` to camel case. - * See [Wikipedia](http://en.wikipedia.org/wiki/CamelCase) for more details. + * Converts the first character of `string` to upper case. * * @static * @memberOf _ * @category Strings - * @param {string} [string=''] The string to camel case. - * @returns {string} Returns the camel cased string. - * @example - * - * _.camelCase('Hello world'); - * // => 'helloWorld' - * - * _.camelCase('--hello-world'); - * // => 'helloWorld' - * - * _.camelCase('__hello_world__'); - * // => 'helloWorld' - */ - var camelCase = createCompounder(function(result, word, index) { - if (!index && reAllCaps.test(word)) { - return result + word.toLowerCase(); - } - return result + (word.charAt(0)[index ? 'toUpperCase' : 'toLowerCase']() + word.slice(1)); - }); - - /** - * Capitalizes the first character of `string`. - * - * @static - * @memberOf _ - * @category Strings - * @param {string} [string=''] The string to capitalize. + * @param {string} string The string to capitalize. * @returns {string} Returns the capitalized string. * @example * @@ -7056,37 +6773,6 @@ return string.charAt(0).toUpperCase() + string.slice(1); } - /** - * Checks if `string` ends with a given target string. - * - * @static - * @memberOf _ - * @category Strings - * @param {string} [string=''] The string to search. - * @param {string} [target] The string to search for. - * @param {number} [position=string.length] The position to search from. - * @returns {boolean} Returns `true` if the given string ends with the - * target string, else `false`. - * @example - * - * _.endsWith('abc', 'c'); - * // => true - * - * _.endsWith('abc', 'b'); - * // => false - * - * _.endsWith('abc', 'b', 2); - * // => true - */ - function endsWith(string, target, position) { - string = string == null ? '' : String(string); - target = String(target); - - var length = string.length; - position = (typeof position == 'undefined' ? length : nativeMin(position < 0 ? 0 : (+position || 0), length)) - target.length; - return position >= 0 && string.indexOf(target, position) == position; - } - /** * Converts the characters "&", "<", ">", '"', and "'" in `string` to * their corresponding HTML entities. @@ -7101,7 +6787,7 @@ * @static * @memberOf _ * @category Strings - * @param {string} [string=''] The string to escape. + * @param {string} string The string to escape. * @returns {string} Returns the escaped string. * @example * @@ -7112,234 +6798,6 @@ return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar); } - /** - * Escapes the `RegExp` special characters "\", "^", "$", ".", "|", "?", "*", - * "+", "(", ")", "[", "]", "{" and "}" in `string`. - * - * @static - * @memberOf _ - * @category Strings - * @param {string} [string=''] The string to escape. - * @returns {string} Returns the escaped string. - * @example - * - * _.escapeRegExp('[lodash](http://lodash.com)'); - * // => '\[lodash\]\(http://lodash\.com\)' - */ - function escapeRegExp(string) { - return string == null ? '' : String(string).replace(reRegExpChars, '\\$&'); - } - - /** - * Converts `string` to kebab case (a.k.a. spinal case). - * See [Wikipedia](http://en.wikipedia.org/wiki/Letter_case#Computers) for - * more details. - * - * @static - * @memberOf _ - * @category Strings - * @param {string} [string=''] The string to kebab case. - * @returns {string} Returns the kebab cased string. - * @example - * - * _.kebabCase('Hello world'); - * // => 'hello-world' - * - * _.kebabCase('helloWorld'); - * // => 'hello-world' - * - * _.kebabCase('__hello_world__'); - * // => 'hello-world' - */ - var kebabCase = createCompounder(function(result, word, index) { - return result + (index ? '-' : '') + word.toLowerCase(); - }); - - /** - * Pads `string` on the left and right sides if it is shorter then the given - * padding length. The `chars` string may be truncated if the number of padding - * characters can't be evenly divided by the padding length. - * - * @static - * @memberOf _ - * @category Strings - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * _.pad('abc', 8); - * // => ' abc ' - * - * _.pad('abc', 8, '_-'); - * // => '_-abc_-_' - * - * _.pad('abc', 3); - * // => 'abc' - */ - function pad(string, length, chars) { - string = string == null ? '' : String(string); - length = +length; - - var strLength = string.length; - if (strLength >= length || !nativeIsFinite(length)) { - return string; - } - var mid = (length - strLength) / 2, - leftLength = floor(mid), - rightLength = ceil(mid); - - chars = createPad('', rightLength, chars); - return chars.slice(0, leftLength) + string + chars; - } - - /** - * Pads `string` on the left side if it is shorter then the given padding - * length. The `chars` string may be truncated if the number of padding - * characters exceeds the padding length. - * - * @static - * @memberOf _ - * @category Strings - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * _.padLeft('abc', 6); - * // => ' abc' - * - * _.padLeft('abc', 6, '_-'); - * // => '_-_abc' - * - * _.padLeft('abc', 3); - * // => 'abc' - */ - function padLeft(string, length, chars) { - string = string == null ? '' : String(string); - return createPad(string, length, chars) + string; - } - - /** - * Pads `string` on the right side if it is shorter then the given padding - * length. The `chars` string may be truncated if the number of padding - * characters exceeds the padding length. - * - * @static - * @memberOf _ - * @category Strings - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * _.padRight('abc', 6); - * // => 'abc ' - * - * _.padRight('abc', 6, '_-'); - * // => 'abc_-_' - * - * _.padRight('abc', 3); - * // => 'abc' - */ - function padRight(string, length, chars) { - string = string == null ? '' : String(string); - return string + createPad(string, length, chars); - } - - /** - * Repeats the given string `n` times. - * - * @static - * @memberOf _ - * @category Strings - * @param {string} [string=''] The string to repeat. - * @param {number} [n=0] The number of times to repeat the string. - * @returns {string} Returns the repeated string. - * @example - * - * _.repeat('*', 3); - * // => '***' - * - * _.repeat('abc', 2); - * // => 'abcabc' - * - * _.repeat('abc', 0); - * // => '' - */ - function repeat(string, n) { - var result = ''; - n = +n; - - if (n < 1 || string == null || !nativeIsFinite(n)) { - return result; - } - string = String(string); - do { - if (n % 2) { - result += string; - } - n = floor(n / 2); - string += string; - } while (n); - return result; - } - - /** - * Converts `string` to snake case. - * See [Wikipedia](http://en.wikipedia.org/wiki/Snake_case) for more details. - * - * @static - * @memberOf _ - * @category Strings - * @param {string} [string=''] The string to snake case. - * @returns {string} Returns the snake cased string. - * @example - * - * _.snakeCase('Hello world'); - * // => 'hello_world' - * - * _.snakeCase('--hello-world'); - * // => 'hello_world' - * - * _.snakeCase('helloWorld'); - * // => 'hello_world' - */ - var snakeCase = createCompounder(function(result, word, index) { - return result + (index ? '_' : '') + word.toLowerCase(); - }); - - /** - * Checks if `string` starts with a given target string. - * - * @static - * @memberOf _ - * @category Strings - * @param {string} [string=''] The string to search. - * @param {string} [target] The string to search for. - * @param {number} [position=0] The position to search from. - * @returns {boolean} Returns `true` if the given string starts with the - * target string, else `false`. - * @example - * - * _.startsWith('abc', 'a'); - * // => true - * - * _.startsWith('abc', 'b'); - * // => false - * - * _.startsWith('abc', 'b', 1); - * // => true - */ - function startsWith(string, target, position) { - string = string == null ? '' : String(string); - position = typeof position == 'undefined' ? 0 : nativeMin(position < 0 ? 0 : (+position || 0), string.length); - return string.lastIndexOf(target, position) == position; - } - /** * Creates a compiled template function that can interpolate data properties * in "interpolate" delimiters, HTML-escaped interpolated data properties in @@ -7349,8 +6807,8 @@ * settings object is provided it will override `_.templateSettings` for the * template. * - * Note: In the development build, `_.template` utilizes `sourceURL`s for easier debugging. - * See the [HTML5 Rocks article on sourcemaps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) + * Note: In the development build, `_.template` utilizes sourceURLs for easier + * debugging. See [HTML5 Rocks' article on sourcemaps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) * for more details. * * For more information on precompiling templates see @@ -7362,17 +6820,17 @@ * @static * @memberOf _ * @category Strings - * @param {string} [string=''] The template string. - * @param {Object} [data] The data object used to populate the template string. + * @param {string} text The template text. + * @param {Object} [data] The data object used to populate the text. * @param {Object} [options] The options object. * @param {RegExp} [options.escape] The HTML "escape" delimiter. * @param {RegExp} [options.evaluate] The "evaluate" delimiter. * @param {Object} [options.imports] An object to import into the template as local variables. * @param {RegExp} [options.interpolate] The "interpolate" delimiter. - * @param {string} [options.sourceURL] The `sourceURL` of the template's compiled source. + * @param {string} [options.sourceURL] The sourceURL of the template's compiled source. * @param {string} [options.variable] The data object variable name. * @returns {Function|string} Returns the interpolated string if a data object - * is provided, else the compiled template function. + * is provided, else it returns a template function. * @example * * // using the "interpolate" delimiter to create a compiled template @@ -7407,7 +6865,7 @@ * _.template(list, { 'people': ['fred', 'barney'] }, { 'imports': { 'jq': jQuery } }); * // => '
  • fred
  • barney
  • ' * - * // using the `sourceURL` option to specify a custom `sourceURL` for the template + * // using the `sourceURL` option to specify a custom sourceURL for the template * var compiled = _.template('hello <%= name %>', null, { 'sourceURL': '/basic/greeting.jst' }); * compiled(data); * // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector @@ -7429,18 +6887,20 @@ * };\ * '); */ - function template(string, data, options) { + function template(text, data, options) { // based on John Resig's `tmpl` implementation // http://ejohn.org/blog/javascript-micro-templating/ // and Laura Doktorova's doT.js // https://github.com/olado/doT var settings = lodash.templateSettings; - options = defaults({}, options, settings); - string = String(string == null ? '' : string); + text = String(text || ''); - var imports = defaults({}, options.imports, settings.imports), - importsKeys = keys(imports), - importsValues = values(imports); + // avoid missing dependencies when `iteratorTemplate` is not defined + options = iteratorTemplate ? defaults({}, options, settings) : settings; + + var imports = iteratorTemplate && defaults({}, options.imports, settings.imports), + importsKeys = iteratorTemplate ? keys(imports) : ['_'], + importsValues = iteratorTemplate ? values(imports) : [lodash]; var isEscaping, isEvaluating, @@ -7456,11 +6916,11 @@ (options.evaluate || reNoMatch).source + '|$' , 'g'); - string.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) { + text.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) { interpolateValue || (interpolateValue = esTemplateValue); // escape characters that cannot be included in string literals - source += string.slice(index, offset).replace(reUnescapedString, escapeStringChar); + source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar); // replace delimiters with snippets if (escapeValue) { @@ -7513,7 +6973,7 @@ source + 'return __p\n}'; - // Use a `sourceURL` for easier debugging. + // Use a sourceURL for easier debugging. // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl var sourceURL = '\n/*\n//# sourceURL=' + (options.sourceURL || '/lodash/template/source[' + (templateCounter++) + ']') + '\n*/'; @@ -7539,7 +6999,7 @@ * @static * @memberOf _ * @category Strings - * @param {string} [string=''] The string to trim. + * @param {string} string The string to trim. * @param {string} [chars=whitespace] The characters to trim. * @returns {string} Returns the trimmed string. * @example @@ -7550,17 +7010,12 @@ * _.trim('-_-fred-_-', '_-'); * // => 'fred' */ - function trim(string, chars) { - string = string == null ? '' : String(string); - if (!string) { - return string; + var trim = !nativeTrim ? shimTrim : function(string, chars) { + if (string == null) { + return ''; } - if (chars == null) { - return string.slice(trimmedLeftIndex(string), trimmedRightIndex(string) + 1); - } - chars = String(chars); - return string.slice(charsLeftIndex(string, chars), charsRightIndex(string, chars) + 1); - } + return chars == null ? nativeTrim.call(string) : shimTrim(string, chars); + }; /** * Removes leading whitespace or specified characters from `string`. @@ -7568,7 +7023,7 @@ * @static * @memberOf _ * @category Strings - * @param {string} [string=''] The string to trim. + * @param {string} string The string to trim. * @param {string} [chars=whitespace] The characters to trim. * @returns {string} Returns the trimmed string. * @example @@ -7579,17 +7034,12 @@ * _.trimLeft('-_-fred-_-', '_-'); * // => 'fred-_-' */ - function trimLeft(string, chars) { - string = string == null ? '' : String(string); - if (!string) { - return string; + var trimLeft = !nativeTrimLeft ? shimTrimLeft : function(string, chars) { + if (string == null) { + return ''; } - if (chars == null) { - return string.slice(trimmedLeftIndex(string)) - } - chars = String(chars); - return string.slice(charsLeftIndex(string, chars)); - } + return chars == null ? nativeTrimLeft.call(string) : shimTrimLeft(string, chars); + }; /** * Removes trailing whitespace or specified characters from `string`. @@ -7597,7 +7047,7 @@ * @static * @memberOf _ * @category Strings - * @param {string} [string=''] The string to trim. + * @param {string} string The string to trim. * @param {string} [chars=whitespace] The characters to trim. * @returns {string} Returns the trimmed string. * @example @@ -7608,96 +7058,12 @@ * _.trimRight('-_-fred-_-', '_-'); * // => '-_-fred' */ - function trimRight(string, chars) { - string = string == null ? '' : String(string); - if (!string) { - return string; + var trimRight = !nativeTrimRight ? shimTrimRight : function(string, chars) { + if (string == null) { + return ''; } - if (chars == null) { - return string.slice(0, trimmedRightIndex(string) + 1) - } - chars = String(chars); - return string.slice(0, charsRightIndex(string, chars) + 1); - } - - /** - * Truncates `string` if it is longer than the given maximum string length. - * The last characters of the truncated string will be replaced with the - * omission string which defaults to "...". - * - * @static - * @memberOf _ - * @category Strings - * @param {string} [string=''] The string to truncate. - * @param {Object|number} [options] The options object or maximum string length. - * @param {number} [options.length=30] The maximum string length. - * @param {string} [options.omission='...'] The string used to indicate text is omitted. - * @param {RegExp|string} [options.separator] The separator pattern to truncate to. - * @returns {string} Returns the truncated string. - * @example - * - * _.truncate('hi-diddly-ho there, neighborino'); - * // => 'hi-diddly-ho there, neighbo...' - * - * _.truncate('hi-diddly-ho there, neighborino', 24); - * // => 'hi-diddly-ho there, n...' - * - * _.truncate('hi-diddly-ho there, neighborino', { 'length': 24, 'separator': ' ' }); - * // => 'hi-diddly-ho there,...' - * - * _.truncate('hi-diddly-ho there, neighborino', { 'length': 24, 'separator': /,? +/ }); - * //=> 'hi-diddly-ho there...' - * - * _.truncate('hi-diddly-ho there, neighborino', { 'omission': ' [...]' }); - * // => 'hi-diddly-ho there, neig [...]' - */ - function truncate(string, options) { - var length = 30, - omission = '...'; - - if (options && isObject(options)) { - var separator = 'separator' in options ? options.separator : separator; - length = 'length' in options ? +options.length || 0 : length; - omission = 'omission' in options ? String(options.omission) : omission; - } - else if (options != null) { - length = +options || 0; - } - string = string == null ? '' : String(string); - if (length >= string.length) { - return string; - } - var end = length - omission.length; - if (end < 1) { - return omission; - } - var result = string.slice(0, end); - if (separator == null) { - return result + omission; - } - if (isRegExp(separator)) { - if (string.slice(end).search(separator)) { - var match, - newEnd, - substring = string.slice(0, end); - - if (!separator.global) { - separator = RegExp(separator.source, (reFlags.exec(separator) || '') + 'g'); - } - separator.lastIndex = 0; - while ((match = separator.exec(substring))) { - newEnd = match.index; - } - result = result.slice(0, newEnd == null ? end : newEnd); - } - } else if (string.indexOf(separator, end) != end) { - var index = result.lastIndexOf(separator); - if (index > -1) { - result = result.slice(0, index); - } - } - return result + omission; - } + return chars == null ? nativeTrimRight.call(string) : shimTrimRight(string, chars); + }; /** * The inverse of `_.escape`; this method converts the HTML entities @@ -7710,7 +7076,7 @@ * @static * @memberOf _ * @category Strings - * @param {string} [string=''] The string to unescape. + * @param {string} string The string to unescape. * @returns {string} Returns the unescaped string. * @example * @@ -7749,7 +7115,7 @@ } /** - * Creates a function bound to an optional `thisArg`. If `func` is a property + * Produces a callback bound to an optional `thisArg`. If `func` is a property * name the created callback will return the property value for a given element. * If `func` is an object the created callback will return `true` for elements * that contain the equivalent object properties, otherwise it will return `false`. @@ -7761,7 +7127,7 @@ * @param {*} [func=identity] The value to convert to a callback. * @param {*} [thisArg] The `this` binding of the created callback. * @param {number} [argCount] The number of arguments the callback accepts. - * @returns {Function} Returns the new function. + * @returns {Function} Returns a callback function. * @example * * var characters = [ @@ -7783,11 +7149,11 @@ function createCallback(func, thisArg, argCount) { var type = typeof func; if (type == 'function' || func == null) { - return (typeof thisArg == 'undefined' || !(func && 'prototype' in func)) && + return (typeof thisArg == 'undefined' || !('prototype' in func)) && func || baseCreateCallback(func, thisArg, argCount); } // handle "_.pluck" and "_.where" style callback shorthands - return type == 'object' ? matches(func) : property(func); + return type != 'object' ? property(func) : matches(func); } /** @@ -7809,9 +7175,9 @@ } /** - * Creates a "_.where" style predicate function which performs a deep comparison - * between a given object and the `source` object, returning `true` if the given - * object has equivalent property values, else `false`. + * Creates a "_.where" style function, which performs a deep comparison + * between a given object and the `source` object, returning `true` if the + * given object has equivalent property values, else `false`. * * @static * @memberOf _ @@ -7834,33 +7200,32 @@ * // => { 'name': 'barney', 'age': 36 } */ function matches(source) { + source || (source = {}); + var props = keys(source), - propsLength = props.length, key = props[0], - value = propsLength && source[key]; + a = source[key]; // fast path the common case of providing an object with a single // property containing a primitive value - if (propsLength == 1 && value === value && !isObject(value)) { + if (props.length == 1 && a === a && !isObject(a)) { return function(object) { - if (!(object && hasOwnProperty.call(object, key))) { + if (!hasOwnProperty.call(object, key)) { return false; } // treat `-0` vs. `+0` as not equal - var other = object[key]; - return value === other && (value !== 0 || (1 / value == 1 / other)); + var b = object[key]; + return a === b && (a !== 0 || (1 / a == 1 / b)); }; } return function(object) { - var length = propsLength; - if (length && !object) { - return false; - } - var result = true; + var length = props.length, + result = false; + while (length--) { var key = props[length]; if (!(result = hasOwnProperty.call(object, key) && - baseIsEqual(object[key], source[key], null, true))) { + baseIsEqual(object[key], source[key], null, true))) { break; } } @@ -7875,12 +7240,10 @@ * @static * @memberOf _ * @category Utilities - * @param {Function|Object} [object=this] object The destination object. + * @param {Function|Object} [object=lodash] object The destination object. * @param {Object} source The object of functions to add. * @param {Object} [options] The options object. - * @param {boolean} [options.chain=true] Specify whether the functions added - * are chainable. - * @returns {Function|Object} Returns `object`. + * @param {boolean} [options.chain=true] Specify whether the functions added are chainable. * @example * * function vowels(string) { @@ -7909,7 +7272,7 @@ options = source; } source = object; - object = this; + object = lodash; methodNames = functions(source); } if (options === false) { @@ -7946,11 +7309,10 @@ }(func)); } } - return object; } /** - * Reverts the `_` variable to its previous value and returns a reference to + * Reverts the '_' variable to its previous value and returns a reference to * the `lodash` function. * * @static @@ -8001,8 +7363,8 @@ /** * Converts `value` to an integer of the specified radix. If `radix` is - * `undefined` or `0`, a `radix` of `10` is used unless `value` is a hexadecimal, - * in which case a `radix` of `16` is used. + * `undefined` or `0` a `radix` of `10` is used unless the `value` is a + * hexadecimal, in which case a `radix` of `16` is used. * * Note: This method avoids differences in native ES3 and ES5 `parseInt` * implementations. See the [ES5 spec](http://es5.github.io/#E) @@ -8013,7 +7375,7 @@ * @category Utilities * @param {string} value The value to parse. * @param {number} [radix] The radix used to interpret the value to parse. - * @returns {number} Returns the converted integer. + * @returns {number} Returns the new integer value. * @example * * _.parseInt('08'); @@ -8028,7 +7390,7 @@ }; /** - * Creates a "_.pluck" style function which returns the `key` value of a + * Creates a "_.pluck" style function, which returns the `key` value of a * given object. * * @static @@ -8053,14 +7415,14 @@ */ function property(key) { return function(object) { - return object == null ? undefined : object[key]; + return object[key]; }; } /** * Produces a random number between `min` and `max` (inclusive). If only one * argument is provided a number between `0` and the given number will be - * returned. If `floating` is truthy or either `min` or `max` are floats a + * returned. If `floating` is truey or either `min` or `max` are floats a * floating-point number will be returned instead of an integer. * * @static @@ -8069,7 +7431,7 @@ * @param {number} [min=0] The minimum possible value. * @param {number} [max=1] The maximum possible value. * @param {boolean} [floating=false] Specify returning a floating-point number. - * @returns {number} Returns the random number. + * @returns {number} Returns a random number. * @example * * _.random(0, 5); @@ -8116,61 +7478,6 @@ return baseRandom(min, max); } - /** - * Creates an array of numbers (positive and/or negative) progressing from - * `start` up to but not including `end`. If `start` is less than `stop` a - * zero-length range is created unless a negative `step` is specified. - * - * @static - * @memberOf _ - * @category Utilities - * @param {number} [start=0] The start of the range. - * @param {number} end The end of the range. - * @param {number} [step=1] The value to increment or decrement by. - * @returns {Array} Returns the new array of numbers. - * @example - * - * _.range(4); - * // => [0, 1, 2, 3] - * - * _.range(1, 5); - * // => [1, 2, 3, 4] - * - * _.range(0, 20, 5); - * // => [0, 5, 10, 15] - * - * _.range(0, -4, -1); - * // => [0, -1, -2, -3] - * - * _.range(1, 4, 0); - * // => [1, 1, 1] - * - * _.range(0); - * // => [] - */ - function range(start, end, step) { - start = +start || 0; - step = step == null ? 1 : (+step || 0); - - if (end == null) { - end = start; - start = 0; - } else { - end = +end || 0; - } - // use `Array(length)` so engines like Chakra and V8 avoid slower modes - // http://youtu.be/XAqIpGU8ZZk#t=17m25s - var index = -1, - length = nativeMax(ceil((end - start) / (step || 1)), 0), - result = Array(length); - - while (++index < length) { - result[index] = start; - start += step; - } - return result; - } - /** * Resolves the value of property `key` on `object`. If `key` is a function * it will be invoked with the `this` binding of `object` and its result @@ -8221,9 +7528,9 @@ * @memberOf _ * @category Utilities * @param {number} n The number of times to execute the callback. - * @param {Function} [callback=identity] The function called per iteration. + * @param {Function} callback The function called per iteration. * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Array} Returns the array of results. + * @returns {Array} Returns an array of the results of each `callback` execution. * @example * * var diceRolls = _.times(3, _.partial(_.random, 1, 6)); @@ -8236,12 +7543,11 @@ * // => also calls `mage.castSpell(n)` three times */ function times(n, callback, thisArg) { - n = n < 0 ? 0 : n >>> 0; - callback = baseCreateCallback(callback, thisArg, 1); - + n = (n = +n) > -1 ? n : 0; var index = -1, result = Array(n); + callback = baseCreateCallback(callback, thisArg, 1); while (++index < n) { result[index] = callback(index); } @@ -8291,10 +7597,6 @@ lodash.defer = defer; lodash.delay = delay; lodash.difference = difference; - lodash.drop = drop; - lodash.dropRight = dropRight; - lodash.dropRightWhile = dropRightWhile; - lodash.dropWhile = dropWhile; lodash.filter = filter; lodash.flatten = flatten; lodash.forEach = forEach; @@ -8311,7 +7613,6 @@ lodash.invert = invert; lodash.invoke = invoke; lodash.keys = keys; - lodash.keysIn = keysIn; lodash.map = map; lodash.mapValues = mapValues; lodash.matches = matches; @@ -8319,8 +7620,6 @@ lodash.memoize = memoize; lodash.merge = merge; lodash.min = min; - lodash.mixin = mixin; - lodash.negate = negate; lodash.omit = omit; lodash.once = once; lodash.pairs = pairs; @@ -8334,6 +7633,7 @@ lodash.range = range; lodash.reject = reject; lodash.remove = remove; + lodash.removeAt = removeAt; lodash.rest = rest; lodash.shuffle = shuffle; lodash.slice = slice; @@ -8346,7 +7646,6 @@ lodash.union = union; lodash.uniq = uniq; lodash.values = values; - lodash.valuesIn = valuesIn; lodash.where = where; lodash.without = without; lodash.wrap = wrap; @@ -8357,6 +7656,7 @@ // add aliases lodash.callback = createCallback; lodash.collect = map; + lodash.drop = rest; lodash.each = forEach; lodash.eachRight = forEachRight; lodash.extend = assign; @@ -8368,19 +7668,16 @@ lodash.unzip = zip; // add functions to `lodash.prototype` - mixin(lodash, assign({}, lodash)); + mixin(assign({}, lodash)); /*--------------------------------------------------------------------------*/ // add functions that return unwrapped values when chaining - lodash.camelCase = camelCase; lodash.capitalize = capitalize; lodash.clone = clone; lodash.cloneDeep = cloneDeep; lodash.contains = contains; - lodash.endsWith = endsWith; lodash.escape = escape; - lodash.escapeRegExp = escapeRegExp; lodash.every = every; lodash.find = find; lodash.findIndex = findIndex; @@ -8388,7 +7685,6 @@ lodash.findLast = findLast; lodash.findLastIndex = findLastIndex; lodash.findLastKey = findLastKey; - lodash.findWhere = findWhere; lodash.has = has; lodash.identity = identity; lodash.indexOf = indexOf; @@ -8399,7 +7695,6 @@ lodash.isElement = isElement; lodash.isEmpty = isEmpty; lodash.isEqual = isEqual; - lodash.isError = isError; lodash.isFinite = isFinite; lodash.isFunction = isFunction; lodash.isNaN = isNaN; @@ -8410,31 +7705,24 @@ lodash.isRegExp = isRegExp; lodash.isString = isString; lodash.isUndefined = isUndefined; - lodash.kebabCase = kebabCase; lodash.lastIndexOf = lastIndexOf; + lodash.mixin = mixin; lodash.noConflict = noConflict; lodash.noop = noop; lodash.now = now; - lodash.pad = pad; - lodash.padLeft = padLeft; - lodash.padRight = padRight; lodash.parseInt = parseInt; lodash.random = random; lodash.reduce = reduce; lodash.reduceRight = reduceRight; - lodash.repeat = repeat; lodash.result = result; lodash.runInContext = runInContext; lodash.size = size; lodash.some = some; lodash.sortedIndex = sortedIndex; - lodash.snakeCase = snakeCase; - lodash.startsWith = startsWith; lodash.template = template; lodash.trim = trim; lodash.trimLeft = trimLeft; lodash.trimRight = trimRight; - lodash.truncate = truncate; lodash.unescape = unescape; lodash.uniqueId = uniqueId; @@ -8442,12 +7730,13 @@ lodash.all = every; lodash.any = some; lodash.detect = find; + lodash.findWhere = find; lodash.foldl = reduce; lodash.foldr = reduceRight; lodash.include = contains; lodash.inject = reduce; - mixin(lodash, function() { + mixin(function() { var source = {} baseForOwn(lodash, function(func, methodName) { if (!lodash.prototype[methodName]) { @@ -8463,12 +7752,9 @@ lodash.first = first; lodash.last = last; lodash.sample = sample; - lodash.take = take; - lodash.takeRight = takeRight; - lodash.takeRightWhile = takeRightWhile; - lodash.takeWhile = takeWhile; // add aliases + lodash.take = first; lodash.head = first; baseForOwn(lodash, function(func, methodName) { @@ -8498,13 +7784,12 @@ // add "Chaining" functions to the wrapper lodash.prototype.chain = wrapperChain; - lodash.prototype.toJSON = wrapperValueOf; lodash.prototype.toString = wrapperToString; lodash.prototype.value = wrapperValueOf; lodash.prototype.valueOf = wrapperValueOf; // add `Array` functions that return unwrapped values - arrayEach(['join', 'pop', 'shift'], function(methodName) { + baseEach(['join', 'pop', 'shift'], function(methodName) { var func = arrayRef[methodName]; lodash.prototype[methodName] = function() { var chainAll = this.__chain__, @@ -8517,7 +7802,7 @@ }); // add `Array` functions that return the existing wrapped value - arrayEach(['push', 'reverse', 'sort', 'unshift'], function(methodName) { + baseEach(['push', 'reverse', 'sort', 'unshift'], function(methodName) { var func = arrayRef[methodName]; lodash.prototype[methodName] = function() { func.apply(this.__wrapped__, arguments); @@ -8526,7 +7811,7 @@ }); // add `Array` functions that return new wrapped values - arrayEach(['concat', 'splice'], function(methodName) { + baseEach(['concat', 'splice'], function(methodName) { var func = arrayRef[methodName]; lodash.prototype[methodName] = function() { return new lodashWrapper(func.apply(this.__wrapped__, arguments), this.__chain__); @@ -8536,7 +7821,7 @@ // avoid array-like object bugs with `Array#shift` and `Array#splice` // in IE < 9, Firefox < 10, Narwhal, and RingoJS if (!support.spliceObjects) { - arrayEach(['pop', 'shift', 'splice'], function(methodName) { + baseEach(['pop', 'shift', 'splice'], function(methodName) { var func = arrayRef[methodName], isSplice = methodName == 'splice'; @@ -8554,6 +7839,12 @@ }; }); } + + // add pseudo private property to be used and removed during the build process + lodash._baseForIn = baseForIn; + lodash._iteratorTemplate = iteratorTemplate; + lodash._shimKeys = shimKeys; + return lodash; } diff --git a/test/test.js b/test/test.js index 68b4f3a26..1212c6759 100644 --- a/test/test.js +++ b/test/test.js @@ -4,10 +4,7 @@ var undefined; /** Used as the size to cover large array optimizations */ - var largeArraySize = 200; - - /** Used as the maximum length an array-like object */ - var maxSafeInteger = Math.pow(2, 53) - 1; + var LARGE_ARRAY_SIZE = 200; /** Used as a reference to the global object */ var root = typeof global == 'object' && global || this; @@ -28,9 +25,7 @@ push = Array.prototype.push, slice = Array.prototype.slice, system = root.system, - toString = Object.prototype.toString; - - var JSON = root.JSON, + toString = Object.prototype.toString, Worker = document && root.Worker; /** The file path of the Lo-Dash file to test */ @@ -83,8 +78,8 @@ /** Detect if testing `npm` modules */ var isNpm = isModularize && /\bnpm\b/.test([ui.buildPath, ui.urlParams.build]); - /** Detects if running in PhantomJS */ - var isPhantom = phantom || typeof callPhantom == 'function'; + /** Detects if running in a PhantomJS web page */ + var isPhantomPage = typeof callPhantom == 'function'; /** Detect if running in Rhino */ var isRhino = isJava && typeof global == 'function' && global().Array === root.Array; @@ -185,7 +180,7 @@ return result; }()); - /** Used to check problem JScript properties (a.k.a. the `[[DontEnum]]` bug) */ + /** Used to check problem JScript properties (a.k.a. the [[DontEnum]] bug) */ var shadowedProps = [ 'constructor', 'hasOwnProperty', @@ -267,7 +262,6 @@ "'_array': [1, 2, 3],", "'_boolean': new Boolean(false),", "'_date': new Date,", - "'_errors': [new Error, new EvalError, new RangeError, new ReferenceError, new SyntaxError, new TypeError, new URIError],", "'_function': function() {},", "'_nan': NaN,", "'_null': null,", @@ -275,7 +269,7 @@ "'_object': { 'a': 1, 'b': 2, 'c': 3 },", "'_regexp': /x/,", "'_string': new String('a'),", - "'_undefined': undefined", + "'_undefined': undefined,", '})' ].join('\n'))); } @@ -294,12 +288,15 @@ } // allow bypassing native checks var _fnToString = Function.prototype.toString; - setProperty(Function.prototype, 'toString', function wrapper() { - setProperty(Function.prototype, 'toString', _fnToString); - var result = this === Set ? this.toString() : _fnToString.call(this); - setProperty(Function.prototype, 'toString', wrapper); - return result; - }); + setProperty(Function.prototype, 'toString', (function() { + function fnToString() { + setProperty(Function.prototype, 'toString', _fnToString); + var result = this === Set ? this.toString() : _fnToString.call(this); + setProperty(Function.prototype, 'toString', fnToString); + return result; + } + return fnToString; + }())); // fake DOM setProperty(global, 'window', {}); @@ -321,7 +318,7 @@ var _now = Date.now; setProperty(Date, 'now', function() {}); - var _create = create; + var _create = Object.create; setProperty(Object, 'create', function() {}); var _defineProperty = Object.defineProperty; @@ -333,17 +330,18 @@ var _keys = Object.keys; setProperty(Object, 'keys', function() {}); - var _hasOwnProperty = Object.prototype.hasOwnProperty; - setProperty(Object.prototype, 'hasOwnProperty', function(key) { - if (key == '1' && _.isArguments(this) && _.isEqual(_.values(this), [0, 0])) { - throw new Error; - } - return _hasOwnProperty.call(this, key); - }); - var _contains = String.prototype.contains; setProperty(String.prototype, 'contains', _contains ? function() {} : Boolean); + var _trim = String.prototype.trim; + setProperty(String.prototype, 'trim', _trim ? function() {} : String); + + var _trimLeft = String.prototype.trimLeft; + setProperty(String.prototype, 'trimLeft', _trimLeft ? function() {} : String); + + var _trimRight = String.prototype.trimRight; + setProperty(String.prototype, 'trimRight', _trimRight ? function() {} : String); + // clear cache so Lo-Dash can be reloaded emptyObject(require.cache); @@ -357,14 +355,22 @@ setProperty(Object, 'defineProperty', _defineProperty); setProperty(Object, 'getPrototypeOf', _getPrototypeOf); setProperty(Object, 'keys', _keys); - setProperty(Object.prototype, 'hasOwnProperty', _hasOwnProperty); setProperty(Function.prototype, 'toString', _fnToString); - if (_contains) { - setProperty(String.prototype, 'contains', _contains); - } else { - delete String.prototype.contains; - } + _.forOwn({ + 'contains': _contains, + 'trim': _trim, + 'trimLeft': _trimLeft, + 'trimRight': _trimRight + }, + function(func, key) { + if (func) { + setProperty(String.prototype, key, func); + } else { + delete String.prototype[key]; + } + }); + delete global.window; delete global.WinRTError; delete Function.prototype._method; @@ -387,7 +393,6 @@ 'parent._._boolean = new Boolean(false);', 'parent._._date = new Date;', "parent._._element = document.createElement('div');", - 'parent._._errors = [new Error, new EvalError, new RangeError, new ReferenceError, new SyntaxError, new TypeError, new URIError];', 'parent._._function = function() {};', 'parent._._nan = NaN;', 'parent._._null = null;', @@ -423,7 +428,7 @@ (function() { test('supports loading ' + basename + ' as the "lodash" module', 1, function() { if (amd) { - strictEqual((lodashModule || {}).moduleName, 'lodash'); + equal((lodashModule || {}).moduleName, 'lodash'); } else { skipTest(); @@ -432,7 +437,7 @@ test('supports loading ' + basename + ' with the Require.js "shim" configuration option', 1, function() { if (amd && /requirejs/.test(ui.loaderPath)) { - strictEqual((shimmedModule || {}).moduleName, 'shimmed'); + equal((shimmedModule || {}).moduleName, 'shimmed'); } else { skipTest(); } @@ -440,7 +445,7 @@ test('supports loading ' + basename + ' as the "underscore" module', 1, function() { if (amd && !/dojo/.test(ui.loaderPath)) { - strictEqual((underscoreModule || {}).moduleName, 'underscore'); + equal((underscoreModule || {}).moduleName, 'underscore'); } else { skipTest(); @@ -458,7 +463,7 @@ setTimeout(attempt, 16); return; } - strictEqual(actual, _.VERSION); + equal(actual, _.VERSION); QUnit.start(); }; @@ -472,27 +477,28 @@ test('should not add `Function.prototype` extensions to lodash', 1, function() { if (lodashBizarro) { - ok(!('_method' in lodashBizarro)); + equal('_method' in lodashBizarro, false); } else { skipTest(); } }); - test('should avoid overwritten native methods', 9, function() { + test('should avoid overwritten native methods', 12, function() { function Foo() {} function message(methodName) { return '`_.' + methodName + '` should avoid overwritten native methods'; } + var object = { 'a': true }; - var object = { 'a': 1 }, - otherObject = { 'b': 2 }, - largeArray = _.times(largeArraySize, _.constant(object)); + var largeArray = _.times(LARGE_ARRAY_SIZE, function() { + return object; + }); if (lodashBizarro) { try { - var actual = [lodashBizarro.isArray([]), lodashBizarro.isArray({ 'length': 0 })]; + actual = [lodashBizarro.isArray([]), lodashBizarro.isArray({ 'length': 0 })]; } catch(e) { actual = null; } @@ -514,11 +520,11 @@ deepEqual(actual[1], {}, message('Object.create')); try { - actual = lodashBizarro.bind(function() { return this.a; }, object); + var actual = lodashBizarro.bind(function() { return this.a; }, object); } catch(e) { actual = null; } - ok(!(expando in actual), message('Object.defineProperty')); + equal(expando in actual, false, message('Object.defineProperty')); try { actual = [lodashBizarro.isPlainObject({}), lodashBizarro.isPlainObject([])]; @@ -536,14 +542,14 @@ try { actual = [ - lodashBizarro.difference([object, otherObject], largeArray), + lodashBizarro.difference([object], largeArray), lodashBizarro.intersection(largeArray, [object]), lodashBizarro.uniq(largeArray) ]; } catch(e) { actual = null; } - deepEqual(actual, [[otherObject], [object], [object]], message('Set')); + deepEqual(actual, [[], [object], [object]], message('Set')); try { actual = lodashBizarro.contains('abc', 'c'); @@ -551,9 +557,23 @@ actual = null; } strictEqual(actual, true, message('String#contains')); + + _.forEach(['trim', 'trimLeft', 'trimRight'], function(methodName) { + try { + var actual = [ + lodashBizarro[methodName](whitespace + 'a b c' + whitespace), + lodashBizarro[methodName](''), + lodashBizarro[methodName]('-_-a-b-c-_-', '_-'), + lodashBizarro[methodName]('', '_-') + ]; + } catch(e) { + actual = null; + } + ok(_.every(actual, _.isString), message('String#' + methodName)); + }); } else { - skipTest(9); + skipTest(12); } }); }()); @@ -569,7 +589,7 @@ test('should return provided `lodash` instances', 1,function() { var wrapped = _(false); - strictEqual(_(wrapped), wrapped); + equal(_(wrapped), wrapped); }); }()); @@ -578,23 +598,18 @@ QUnit.module('lodash.after'); (function() { - function after(n, times) { - var count = 0; - _.times(times, _.after(n, function() { count++; })); - return count; - } test('should create a function that executes `func` after `n` calls', 4, function() { + function after(n, times) { + var count = 0; + _.times(times, _.after(n, function() { count++; })); + return count; + } + strictEqual(after(5, 5), 1, 'after(n) should execute `func` after being called `n` times'); strictEqual(after(5, 4), 0, 'after(n) should not execute `func` unless called `n` times'); strictEqual(after(0, 0), 0, 'after(0) should not execute `func` immediately'); strictEqual(after(0, 1), 1, 'after(0) should execute `func` when called once'); }); - - test('should coerce non-finite `n` values to `0`', 3, function() { - _.each([-Infinity, NaN, Infinity], function(n) { - strictEqual(after(n, 1), 1); - }); - }); }()); /*--------------------------------------------------------------------------*/ @@ -611,6 +626,7 @@ this.a = 1; this.c = 3; } + Foo.prototype.b = 2; deepEqual(_.assign({}, new Foo), { 'a': 1, 'c': 3 }); }); @@ -631,6 +647,14 @@ deepEqual(_.assign({ 'a': 1, 'b': 2 }, expected), expected); }); + test('should not error on `null` or `undefined` sources (test in IE < 9)', 1, function() { + try { + deepEqual(_.assign({}, null, undefined, { 'a': 1 }), { 'a': 1 }); + } catch(e) { + ok(false); + } + }); + test('should work with a callback', 1, function() { var actual = _.assign({ 'a': 1, 'b': 2 }, { 'a': 3, 'c': 3 }, function(a, b) { return typeof a == 'undefined' ? b : a; @@ -693,7 +717,14 @@ deepEqual(actual, [1, 3]); }); - _.each({ + test('should work when used as a callback for `_.map`', 1, function() { + var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], + actual = _.map(array, _.at); + + deepEqual(actual, [[1], [5], [9]]); + }); + + _.forEach({ 'literal': 'abc', 'object': Object('abc') }, @@ -726,7 +757,7 @@ var values = _.reject(falsey.slice(1), function(value) { return value == null; }), expected = _.map(values, function(value) { return [value]; }); - var actual = _.map(values, function(value) { + var actual = _.map(values, function(value, index) { try { var bound = _.bind(fn, value); return bound(); @@ -743,14 +774,14 @@ actual = bound('a'); ok(actual[0] === null || actual[0] && actual[0].Array); - strictEqual(actual[1], 'a'); + equal(actual[1], 'a'); _.times(2, function(index) { bound = index ? _.bind(fn, undefined) : _.bind(fn); actual = bound('b'); ok(actual[0] === undefined || actual[0] && actual[0].Array); - strictEqual(actual[1], 'b'); + equal(actual[1], 'b'); }); }); @@ -769,7 +800,7 @@ }); test('should support placeholders', 4, function() { - if (!isModularize) { + if (_._iteratorTemplate) { var object = {}, bound = _.bind(fn, object, _, 'b', _); @@ -784,7 +815,7 @@ }); test('should create a function with a `length` of `0`', 2, function() { - var fn = function(a, b, c) {}, + var func = function(a, b, c) {}, bound = _.bind(fn, {}); strictEqual(bound.length, 0); @@ -797,7 +828,6 @@ function Foo() { return this; } - var bound = _.bind(Foo, { 'a': 1 }), newBound = new bound; @@ -810,7 +840,6 @@ function Foo(value) { return value && object; } - var bound = _.bind(Foo), object = {}; @@ -825,6 +854,10 @@ deepEqual(bound(['b'], 'c'), [object, 'a', ['b'], 'c']); }); + test('should throw a TypeError if `func` is not a function', 1, function() { + raises(function() { _.bind(); }, TypeError); + }); + test('should return a wrapped value when chaining', 2, function() { if (!isNpm) { var object = {}, @@ -868,6 +901,7 @@ this._b = 2; this.a = function() { return this._a; }; } + Foo.prototype.b = function() { return this._b; }; var object = new Foo; @@ -951,122 +985,17 @@ var object = { 'name': 'fred', 'greet': function(greeting) { - return this.name + ' says: ' + greeting; + return greeting + ' ' + this.name; } }; - var bound = _.bindKey(object, 'greet', 'hi'); - strictEqual(bound(), 'fred says: hi'); + var func = _.bindKey(object, 'greet', 'hi'); + equal(func(), 'hi fred'); object.greet = function(greeting) { - return this.name + ' says: ' + greeting + '!'; + return greeting + ' ' + this.name + '!'; }; - strictEqual(bound(), 'fred says: hi!'); - }); - - test('should support placeholders', 4, function() { - var object = { - 'fn': function fn(a, b, c, d) { - return slice.call(arguments); - } - }; - - if (!isModularize) { - var bound = _.bindKey(object, 'fn', _, 'b', _); - deepEqual(bound('a', 'c'), ['a', 'b', 'c']); - deepEqual(bound('a'), ['a', 'b', undefined]); - deepEqual(bound('a', 'c', 'd'), ['a', 'b', 'c', 'd']); - deepEqual(bound(), [undefined, 'b', undefined]); - } - else { - skipTest(4); - } - }); - }()); - - /*--------------------------------------------------------------------------*/ - - QUnit.module('case methods'); - - _.each(['camel', 'kebab', 'snake'], function(caseName) { - var methodName = caseName + 'Case', - func = _[methodName]; - - var expected = (function() { - switch (caseName) { - case 'camel': return 'helloWorld'; - case 'kebab': return 'hello-world'; - case 'snake': return 'hello_world'; - } - }()); - - var burredLetters = [ - '\xC0', '\xC1', '\xC2', '\xC3', '\xC4', '\xC5', '\xC6', '\xC7', '\xC8', '\xC9', '\xCA', '\xCB', '\xCC', '\xCD', '\xCE', '\xCF', - '\xD0', '\xD1', '\xD2', '\xD3', '\xD4', '\xD5', '\xD6', '\xD7', '\xD8', '\xD9', '\xDA', '\xDB', '\xDC', '\xDD', '\xDE', '\xDF', - '\xE0', '\xE1', '\xE2', '\xE3', '\xE4', '\xE5', '\xE6', '\xE7', '\xE8', '\xE9', '\xEA', '\xEB', '\xEC', '\xED', '\xEE', '\xEF', - '\xF0', '\xF1', '\xF2', '\xF3', '\xF4', '\xF5', '\xF6', '\xF7', '\xF8', '\xF9', '\xFA', '\xFB', '\xFC', '\xFD', '\xFE', '\xFF' - ]; - - var deburredLetters = [ - 'A', 'A', 'A', 'A', 'A', 'A', 'AE', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', - 'D', 'N', 'O', 'O', 'O', 'O', 'O', '', 'O', 'U', 'U', 'U', 'U', 'Y', 'Th', 'ss', - 'a', 'a', 'a', 'a', 'a', 'a', 'ae', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', - 'd', 'n', 'o', 'o', 'o', 'o', 'o', '', 'o', 'u', 'u', 'u', 'u', 'y', 'th', 'y' - ]; - - test('`_.' + methodName + '` should convert `string` to ' + caseName + ' case', 4, function() { - _.each(['Hello world', 'helloWorld', '--hello-world', '__hello_world__'], function(string) { - strictEqual(func(string), expected); - }); - }); - - test('`_.' + methodName + '` should handle double-converting strings', 4, function() { - _.each(['Hello world', 'helloWorld', '--hello-world', '__hello_world__'], function(string) { - strictEqual(func(func(string)), expected); - }); - }); - - test('`_.' + methodName + '` should deburr letters', 1, function() { - var actual = _.map(burredLetters, function(burred, index) { - var isCamel = caseName == 'camel', - deburrLetter = deburredLetters[index]; - - var string = isCamel - ? func('z' + burred) - : func(burred); - - var deburredString = isCamel - ? 'z' + deburrLetter - : deburrLetter.toLowerCase(); - - return string == deburredString; - }); - - ok(_.every(actual, _.identity)); - }); - - test('`_.' + methodName + '` should coerce `string` to a string', 2, function() { - var string = 'Hello world'; - strictEqual(func(Object(string)), expected); - strictEqual(func({ 'toString': _.constant(string) }), expected); - }); - }); - - /*--------------------------------------------------------------------------*/ - - QUnit.module('lodash.camelCase'); - - (function() { - test('should work with numbers', 3, function() { - strictEqual(_.camelCase('too legit 2 quit'), 'tooLegit2Quit'); - strictEqual(_.camelCase('walk 500 miles'), 'walk500Miles'); - strictEqual(_.camelCase('xhr2 request'), 'xhr2Request'); - }); - - test('should handle acronyms', 3, function() { - strictEqual(_.camelCase('safe HTML'), 'safeHTML'); - strictEqual(_.camelCase('escape HTML entities'), 'escapeHTMLEntities'); - strictEqual(_.camelCase('XMLHttpRequest'), 'xmlHttpRequest'); + equal(func(), 'hi fred!'); }); }()); @@ -1076,9 +1005,15 @@ (function() { test('should capitalize the first character of a string', 3, function() { - strictEqual(_.capitalize('fred'), 'Fred'); - strictEqual(_.capitalize('Fred'), 'Fred'); - strictEqual(_.capitalize(' fred'), ' fred'); + equal(_.capitalize('fred'), 'Fred'); + equal(_.capitalize('Fred'), 'Fred'); + equal(_.capitalize(' fred'), ' fred'); + }); + + test('should return an empty string when provided `null`, `undefined`, or empty strings', 3, function() { + strictEqual(_.capitalize(null), ''); + strictEqual(_.capitalize(undefined), ''); + strictEqual(_.capitalize(''), ''); }); }()); @@ -1100,7 +1035,7 @@ test('should return the existing wrapper when chaining', 1, function() { if (!isNpm) { var wrapper = _({ 'a': 0 }); - strictEqual(wrapper.chain(), wrapper); + equal(wrapper.chain(), wrapper); } else { skipTest(); @@ -1184,7 +1119,7 @@ Klass.prototype = { 'b': 1 }; var nonCloneable = { - 'a DOM element': body, + 'an element': body, 'a function': Klass }; @@ -1214,14 +1149,14 @@ actual = _.clone(expected); deepEqual(actual, expected); - ok(actual !== expected && actual[0] === expected[0]); + ok(actual != expected && actual[0] === expected[0]); }); test('`_.clone` should perform a shallow clone when used as a callback for `_.map`', 1, function() { var expected = [{ 'a': [0] }, { 'b': [1] }], actual = _.map(expected, _.clone); - ok(actual[0] !== expected[0] && actual[0].a === expected[0].a && actual[1].b === expected[1].b); + ok(actual[0] != expected[0] && actual[0].a === expected[0].a && actual[1].b === expected[1].b); }); test('`_.cloneDeep` should deep clone objects with circular references', 1, function() { @@ -1237,7 +1172,7 @@ ok(clone.bar.b === clone.foo.b && clone === clone.foo.b.foo.c && clone !== object); }); - _.each([ + _.forEach([ 'clone', 'cloneDeep' ], @@ -1284,7 +1219,7 @@ return this[value]; }, { 'a': 'A' }); - strictEqual(actual, 'A'); + equal(actual, 'A'); }); test('`_.' + methodName + '` should handle cloning if `callback` returns `undefined`', 1, function() { @@ -1297,7 +1232,7 @@ actual = func(array); strictEqual(actual.index, 2); - strictEqual(actual.input, 'vwxyz'); + equal(actual.input, 'vwxyz'); }); test('`_.' + methodName + '` should deep clone `lastIndex` regexp property', 1, function() { @@ -1306,21 +1241,7 @@ regexp.exec('vwxyz'); var actual = func(regexp); - strictEqual(actual.lastIndex, 3); - }); - - test('`_.' + methodName + '` should not error on DOM elements', 1, function() { - if (document) { - var element = document.createElement('div'); - try { - strictEqual(func(element), element); - } catch(e) { - ok(false); - } - } - else { - skipTest(); - } + equal(actual.lastIndex, 3); }); }); }(1, 2, 3)); @@ -1356,7 +1277,7 @@ }; var welcome = _.compose(greet, format); - strictEqual(welcome('pebbles'), 'Hiya Penelope!'); + equal(welcome('pebbles'), 'Hiya Penelope!'); }); test('should return a new function', 1, function() { @@ -1406,59 +1327,40 @@ QUnit.module('lodash.contains'); (function() { - _.each({ + _.forEach({ 'an `arguments` object': arguments, - 'an array': [1, 2, 3, 4], - 'an object': { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }, - 'a string': '1234' + 'an array': [1, 2, 3, 1, 2, 3], + 'an object': { 'a': 1, 'b': 2, 'c': 3, 'd': 1, 'e': 2, 'f': 3 }, + 'a string': '123123' }, function(collection, key) { - var values = _.toArray(collection); - test('should work with ' + key + ' and return `true` for matched values', 1, function() { strictEqual(_.contains(collection, 3), true); }); test('should work with ' + key + ' and return `false` for unmatched values', 1, function() { - strictEqual(_.contains(collection, 5), false); + strictEqual(_.contains(collection, 4), false); }); - test('should work with ' + key + ' and a positive `fromIndex`', 2, function() { - strictEqual(_.contains(collection, values[2], 2), true); - strictEqual(_.contains(collection, values[1], 2), false); + test('should work with ' + key + ' and a positive `fromIndex`', 1, function() { + strictEqual(_.contains(collection, 1, 2), true); }); - test('should work with ' + key + ' and a `fromIndex` >= `collection.length`', 12, function() { - _.each([6, 8, Math.pow(2, 32), Infinity], function(fromIndex) { + test('should work with ' + key + ' and a `fromIndex` >= `collection.length`', 6, function() { + _.forEach([6, 8], function(fromIndex) { strictEqual(_.contains(collection, 1, fromIndex), false); strictEqual(_.contains(collection, undefined, fromIndex), false); strictEqual(_.contains(collection, '', fromIndex), false); }); }); - test('should work with ' + key + ' and treat falsey `fromIndex` values as `0`', 1, function() { - var expected = _.map(falsey, _.constant(true)); - - var actual = _.map(falsey, function(fromIndex) { - return _.contains(collection, values[0], fromIndex); - }); - - deepEqual(actual, expected); + test('should work with ' + key + ' and a negative `fromIndex`', 1, function() { + strictEqual(_.contains(collection, 2, -3), true); }); - test('should work with ' + key + ' and treat non-number `fromIndex` values as `0`', 1, function() { - strictEqual(_.contains(collection, values[0], '1'), true); - }); - - test('should work with ' + key + ' and a negative `fromIndex`', 2, function() { - strictEqual(_.contains(collection, values[2], -2), true); - strictEqual(_.contains(collection, values[1], -2), false); - }); - - test('should work with ' + key + ' and a negative `fromIndex` <= negative `collection.length`', 3, function() { - _.each([-4, -6, -Infinity], function(fromIndex) { - strictEqual(_.contains(collection, values[0], fromIndex), true); - }); + test('should work with ' + key + ' and a negative `fromIndex` <= negative `collection.length`', 2, function() { + strictEqual(_.contains(collection, 1, -6), true); + strictEqual(_.contains(collection, 2, -8), true); }); test('should work with ' + key + ' and return an unwrapped value when chaining', 1, function() { @@ -1471,7 +1373,7 @@ }); }); - _.each({ + _.forEach({ 'literal': 'abc', 'object': Object('abc') }, @@ -1489,7 +1391,7 @@ test('should be aliased', 1, function() { strictEqual(_.include, _.contains); }); - }(1, 2, 3, 4)); + }(1, 2, 3, 1, 2, 3)); /*--------------------------------------------------------------------------*/ @@ -1620,7 +1522,7 @@ test('should ignore primitive `prototype` arguments and use an empty object instead', 1, function() { var primitives = [true, null, 1, 'a', undefined], - expected = _.map(primitives, _.constant(true)); + expected = _.map(primitives, function() { return true; }); var actual = _.map(primitives, function(value, index) { return _.isPlainObject(index ? _.create(value) : _.create()); @@ -1635,60 +1537,28 @@ QUnit.module('lodash.callback'); (function() { - test('should create a callback with a falsey `thisArg`', 1, function() { - var values = _.map(falsey, function(value) { - return Object(value == null ? root : value); - }); + test('should work with functions created by `_.partial` and `_.partialRight`', 2, function() { + var fn = function() { + var result = [this.a]; + push.apply(result, arguments); + return result; + }; - var actual = _.map(values, function(value) { - var callback = _.callback(function() { return this; }, value); - return callback(); - }); + var expected = [1, 2, 3], + object = { 'a': 1 }, + callback = _.createCallback(_.partial(fn, 2), object); - deepEqual(actual, values); - }); + deepEqual(callback(3), expected); - test('should return `_.identity` when `func` is nullish', 2, function() { - var object = {}; - _.each([null, undefined], function(value) { - var callback = _.callback(value); - strictEqual(callback(object), object); - }); - }); - - test('should not error when `func` is nullish and a `thisArg` is provided', 2, function() { - var object = {}; - _.each([null, undefined], function(value) { - try { - var callback = _.callback(value, {}); - strictEqual(callback(object), object); - } catch(e) { - ok(false); - } - }); - }); - - test('should return a callback created by `_.matches` when `func` is an object', 2, function() { - var callback = _.callback({ 'a': 1 }); - strictEqual(callback({ 'a': 1, 'b': 2 }), true); - strictEqual(callback({}), false); - }); - - test('should return a callback created by `_.property` when `func` is a number or string', 2, function() { - var array = ['a'], - callback = _.callback(0); - - strictEqual(callback(array), 'a'); - - callback = _.callback('0'); - strictEqual(callback(array), 'a'); + callback = _.createCallback(_.partialRight(fn, 3), object); + deepEqual(callback(2), expected); }); test('should work without an `argCount`', 1, function() { var args, expected = ['a', 'b', 'c', 'd', 'e']; - var callback = _.callback(function() { + var callback = _.createCallback(function() { args = slice.call(arguments); }); @@ -1696,23 +1566,6 @@ deepEqual(args, expected); }); - test('should work with functions created by `_.partial` and `_.partialRight`', 2, function() { - function fn() { - var result = [this.a]; - push.apply(result, arguments); - return result; - } - - var expected = [1, 2, 3], - object = { 'a': 1 }, - callback = _.callback(_.partial(fn, 2), object); - - deepEqual(callback(3), expected); - - callback = _.callback(_.partialRight(fn, 3), object); - deepEqual(callback(2), expected); - }); - test('should return the function provided if already bound with `Function#bind`', 1, function() { function a() {} @@ -1721,7 +1574,7 @@ if (bound && !('prototype' in bound)) { var bound = a.bind(object); - strictEqual(_.callback(bound, object), bound); + strictEqual(_.createCallback(bound, object), bound); } else { skipTest(); @@ -1735,8 +1588,8 @@ var object = {}; if (_.support.funcDecomp) { - strictEqual(_.callback(a, object), a); - notStrictEqual(_.callback(b, object), b); + strictEqual(_.createCallback(a, object), a); + notStrictEqual(_.createCallback(b, object), b); } else { skipTest(2); @@ -1745,21 +1598,21 @@ test('should only write metadata to named functions', 3, function() { function a() {}; - var b = function() {}; function c() {}; - var object = {}; + var b = function() {}, + object = {}; if (defineProperty && _.support.funcDecomp) { - _.callback(a, object); + _.createCallback(a, object); ok(expando in a); - _.callback(b, object); - ok(!(expando in b)); + _.createCallback(b, object); + equal(expando in b, false); if (_.support.funcNames) { _.support.funcNames = false; - _.callback(c, object); + _.createCallback(c, object); ok(expando in c); _.support.funcNames = true; @@ -1777,8 +1630,8 @@ function a() {}; if (defineProperty && lodashBizarro) { - lodashBizarro.callback(a, {}); - ok(!(expando in a)); + lodashBizarro.createCallback(a, {}); + equal(expando in a, false); } else { skipTest(); @@ -1792,42 +1645,25 @@ (function() { function fn(a, b, c, d) { - return slice.call(arguments); + return a + b + c + d; } test('should curry based on the number of arguments provided', 3, function() { - var curried = _.curry(fn), - expected = [1, 2, 3, 4]; - - deepEqual(curried(1)(2)(3)(4), expected); - deepEqual(curried(1, 2)(3, 4), expected); - deepEqual(curried(1, 2, 3, 4), expected); + var curried = _.curry(fn); + equal(curried(1)(2)(3)(4), 10); + equal(curried(1, 2)(3, 4), 10); + equal(curried(1, 2, 3, 4), 10); }); test('should work with partialed methods', 2, function() { var curried = _.curry(fn), - expected = [1, 2, 3, 4]; - - var a = _.partial(curried, 1), + a = _.partial(curried, 1), b = _.bind(a, null, 2), c = _.partialRight(b, 4), d = _.partialRight(b(3), 4); - deepEqual(c(3), expected); - deepEqual(d(), expected); - }); - - test('should support placeholders', 4, function() { - if (!isModularize) { - var curried = _.curry(fn); - deepEqual(curried(1)(_, 3)(_, 4)(2), [1, 2, 3, 4]); - deepEqual(curried(_, 2)(1)(_, 4)(3), [1, 2, 3, 4]); - deepEqual(curried(_, _, 3)(_, 2)(_, 4)(1), [1, 2, 3, 4]); - deepEqual(curried(_, _, _, 4)(_, _, 3)(_, 2)(1), [1, 2, 3, 4]); - } - else { - skipTest(4); - } + equal(c(3), 10); + equal(d(), 10); }); test('should return a function with a `length` of `0`', 6, function() { @@ -1843,7 +1679,6 @@ function Foo(value) { return value && object; } - var curried = _.curry(Foo), object = {}; @@ -1852,26 +1687,24 @@ }); test('should not alter the `this` binding', 9, function() { - function fn(a, b, c) { + var fn = function(a, b, c) { var value = this || {}; - return [value[a], value[b], value[c]]; - } + return value[a] + value[b] + value[c]; + }; - var object = { 'a': 1, 'b': 2, 'c': 3 }, - expected = [1, 2, 3]; + var object = { 'a': 1, 'b': 2, 'c': 3 }; + equal(_.curry(_.bind(fn, object), 3)('a')('b')('c'), 6); + equal(_.curry(_.bind(fn, object), 3)('a', 'b')('c'), 6); + equal(_.curry(_.bind(fn, object), 3)('a', 'b', 'c'), 6); - deepEqual(_.curry(_.bind(fn, object), 3)('a')('b')('c'), expected); - deepEqual(_.curry(_.bind(fn, object), 3)('a', 'b')('c'), expected); - deepEqual(_.curry(_.bind(fn, object), 3)('a', 'b', 'c'), expected); - - deepEqual(_.bind(_.curry(fn), object)('a')('b')('c'), Array(3)); - deepEqual(_.bind(_.curry(fn), object)('a', 'b')('c'), Array(3)); - deepEqual(_.bind(_.curry(fn), object)('a', 'b', 'c'), expected); + ok(_.isEqual(_.bind(_.curry(fn), object)('a')('b')('c'), NaN)); + ok(_.isEqual(_.bind(_.curry(fn), object)('a', 'b')('c'), NaN)); + equal(_.bind(_.curry(fn), object)('a', 'b', 'c'), 6); object.curried = _.curry(fn); - deepEqual(object.curried('a')('b')('c'), Array(3)); - deepEqual(object.curried('a', 'b')('c'), Array(3)); - deepEqual(object.curried('a', 'b', 'c'), expected); + ok(_.isEqual(object.curried('a')('b')('c'), NaN)); + ok(_.isEqual(object.curried('a', 'b')('c'), NaN)); + equal(object.curried('a', 'b', 'c'), 6); }); }()); @@ -1889,10 +1722,10 @@ debounced(); debounced(); - strictEqual(count, 0); + equal(count, 0); setTimeout(function() { - strictEqual(count, 1); + equal(count, 1); QUnit.start(); }, 96); } @@ -1963,12 +1796,12 @@ } }); - asyncTest('should support a `leading` option', 7, function() { + asyncTest('should work with `leading` option', 7, function() { if (!(isRhino && isModularize)) { var withLeading, counts = [0, 0, 0]; - _.each([true, { 'leading': true }], function(options, index) { + _.forEach([true, { 'leading': true }], function(options, index) { var debounced = _.debounce(function(value) { counts[index]++; return value; @@ -1977,10 +1810,10 @@ if (index == 1) { withLeading = debounced; } - strictEqual(debounced('x'), 'x'); + equal(debounced('x'), 'x'); }); - _.each([false, { 'leading': false }], function(options) { + _.forEach([false, { 'leading': false }], function(options) { var withoutLeading = _.debounce(_.identity, 32, options); strictEqual(withoutLeading('x'), undefined); }); @@ -1998,7 +1831,7 @@ deepEqual(counts, [1, 1, 2]); withLeading('x'); - strictEqual(counts[1], 2); + equal(counts[1], 2); QUnit.start(); }, 64); @@ -2009,7 +1842,7 @@ } }); - asyncTest('should support a `trailing` option', 4, function() { + asyncTest('should work with `trailing` option', 4, function() { if (!(isRhino && isModularize)) { var withCount = 0, withoutCount = 0; @@ -2039,9 +1872,9 @@ } }); - test('should support a `maxWait` option', 2, function() { + test('should work with `maxWait` option', 2, function() { if (!(isRhino && isModularize)) { - var limit = (argv || isPhantom) ? 1000 : 256, + var limit = (argv || isPhantomPage) ? 1000 : 256, withCount = 0, withoutCount = 0; @@ -2105,7 +1938,7 @@ } } setTimeout(function() { - strictEqual(count, 2); + equal(count, 2); deepEqual(args, [object, 'a']); QUnit.start(); }, 64); @@ -2131,6 +1964,7 @@ this.a = 1; this.c = 3; } + Foo.prototype.b = 2; deepEqual(_.defaults({ 'c': 2 }, new Foo), { 'a': 1, 'c': 2 }); }); @@ -2150,6 +1984,14 @@ var actual = _.defaults({ 'a': undefined }, { 'a': 1 }); strictEqual(actual.a, 1); }); + + test('should not error on `null` or `undefined` sources (test in IE < 9)', 1, function() { + try { + deepEqual(_.defaults({ 'a': 1 }, null, undefined, { 'a': 2, 'b': 2 }), { 'a': 1, 'b': 2 }); + } catch(e) { + ok(false); + } + }); }()); /*--------------------------------------------------------------------------*/ @@ -2285,8 +2127,6 @@ QUnit.module('lodash.difference'); (function() { - var args = arguments; - test('should return the difference of the given arrays', 2, function() { var actual = _.difference([1, 2, 3, 4, 5], [5, 2, 10]); deepEqual(actual, [1, 3, 4]); @@ -2296,8 +2136,8 @@ }); test('should work with large arrays', 1, function() { - var array1 = _.range(largeArraySize + 1), - array2 = _.range(largeArraySize), + var array1 = _.range(LARGE_ARRAY_SIZE), + array2 = array1.slice(), a = {}, b = {}, c = {}; @@ -2305,73 +2145,22 @@ array1.push(a, b, c); array2.push(b, c, a); - deepEqual(_.difference(array1, array2), [largeArraySize]); + deepEqual(_.difference(array1, array2), []); }); test('should work with large arrays of objects', 1, function() { - var object1 = {}, - object2 = {}, - largeArray = _.times(largeArraySize, _.constant(object1)); + var object = {}; - deepEqual(_.difference([object1, object2], largeArray), [object2]); - }); - - test('should ignore values that are not arrays or `arguments` objects', 3, function() { - var array = [0, 1, null, 3]; - deepEqual(_.difference(array, 3, null, { '0': 1 }), array); - deepEqual(_.difference(null, array, null, [2, 1]), [0, null, 3]); - deepEqual(_.difference(null, array, null, args), [0, null]); - }); - }(1, 2, 3)); - - /*--------------------------------------------------------------------------*/ - - QUnit.module('lodash.endsWith'); - - (function() { - var string = 'abc'; - - test('should return `true` if a string ends with `target`', 1, function() { - strictEqual(_.endsWith(string, 'c'), true); - }); - - test('should return `false` if a string does not end with `target`', 1, function() { - strictEqual(_.endsWith(string, 'b'), false); - }); - - test('should work with a `position` argument', 1, function() { - strictEqual(_.endsWith(string, 'b', 2), true); - }); - - test('should work with `position` >= `string.length`', 4, function() { - _.each([3, 5, maxSafeInteger, Infinity], function(position) { - strictEqual(_.endsWith(string, 'c', position), true); - }); - }); - - test('should treat falsey `position` values, except `undefined`, as `0`', 1, function() { - var expected = _.map(falsey, _.constant(true)); - - var actual = _.map(falsey, function(position) { - return _.endsWith(string, position === undefined ? 'c' : '', position); + var largeArray = _.times(LARGE_ARRAY_SIZE, function() { + return object; }); - deepEqual(actual, expected); + deepEqual(_.difference(largeArray, [object]), []); }); - test('should treat a negative `position` as `0`', 6, function() { - _.each([-1, -3, -Infinity], function(position) { - ok(_.every(string, function(chr) { - return _.endsWith(string, chr, position) === false; - })); - strictEqual(_.endsWith(string, '', position), true); - }); - }); - - test('should always return `true` when `target` is an empty string regardless of `position`', 1, function() { - ok(_.every([-Infinity, NaN, -3, -1, 0, 1, 2, 3, 5, maxSafeInteger, Infinity], function(position) { - return _.endsWith(string, '', position, true); - })); + test('should ignore individual secondary values', 1, function() { + var array = [1, null, 3]; + deepEqual(_.difference(array, null, 3), array); }); }()); @@ -2384,36 +2173,21 @@ unescaped = '&<>"\'\/'; test('should escape values', 1, function() { - strictEqual(_.escape(unescaped), escaped); + equal(_.escape(unescaped), escaped); }); test('should not escape the "/" character', 1, function() { - strictEqual(_.escape('/'), '/'); + equal(_.escape('/'), '/'); }); test('should handle strings with nothing to escape', 1, function() { - strictEqual(_.escape('abc'), 'abc'); + equal(_.escape('abc'), 'abc'); }); - test('should escape the same characters unescaped by `_.unescape`', 1, function() { - strictEqual(_.escape(_.unescape(escaped)), escaped); - }); - }()); - - /*--------------------------------------------------------------------------*/ - - QUnit.module('lodash.escapeRegExp'); - - (function() { - test('should escape values', 1, function() { - var escaped = '\\.\\*\\+\\?\\^\\$\\{\\}\\(\\)\\|\\[\\]\\/\\\\', - unescaped = '.*+?^${}()|[\]\/\\'; - - strictEqual(_.escapeRegExp(unescaped), escaped); - }); - - test('should handle strings with nothing to escape', 1, function() { - strictEqual(_.escapeRegExp('abc'), 'abc'); + test('should return an empty string when provided `null`, `undefined`, or empty strings', 3, function() { + strictEqual(_.escape(null), ''); + strictEqual(_.escape(undefined), ''); + strictEqual(_.escape(''), ''); }); }()); @@ -2423,7 +2197,7 @@ (function() { test('should return `true` for empty or falsey collections', 1, function() { - var expected = _.map(empties, _.constant(true)); + var expected = _.map(empties, function() { return true; }); var actual = _.map(empties, function(value) { try { @@ -2434,7 +2208,7 @@ deepEqual(actual, expected); }); - test('should return `true` if the callback returns truthy for all elements in the collection', 1, function() { + test('should return `true` if the callback returns truey for all elements in the collection', 1, function() { strictEqual(_.every([true, 1, 'x'], _.identity), true); }); @@ -2460,13 +2234,12 @@ QUnit.module('source property checks'); - _.each(['assign', 'defaults', 'merge'], function(methodName) { + _.forEach(['assign', 'defaults', 'merge'], function(methodName) { var func = _[methodName]; test('`_.' + methodName + '` should not assign inherited `source` properties', 1, function() { function Foo() {} Foo.prototype = { 'a': 1 }; - deepEqual(func({}, new Foo), {}); }); @@ -2488,7 +2261,7 @@ expected[1] = undefined; - ok('1' in actual); + ok(1 in actual); deepEqual(actual, expected); }); } @@ -2498,7 +2271,7 @@ QUnit.module('strict mode checks'); - _.each(['assign', 'bindAll', 'defaults'], function(methodName) { + _.forEach(['assign', 'bindAll', 'defaults'], function(methodName) { var func = _[methodName]; test('`_.' + methodName + '` should not throw strict mode errors', 1, function() { @@ -2529,7 +2302,7 @@ QUnit.module('lodash.filter'); (function() { - test('should return elements the `callback` returns truthy for', 1, function() { + test('should return elements the `callback` returns truey for', 1, function() { var actual = _.filter([1, 2, 3], function(num) { return num % 2; }); @@ -2565,26 +2338,25 @@ /*--------------------------------------------------------------------------*/ - _.each(['find', 'findLast', 'findIndex', 'findLastIndex', 'findKey', 'findLastKey'], function(methodName) { - QUnit.module('lodash.' + methodName); + (function() { + var objects = [ + { 'a': 0, 'b': 0 }, + { 'a': 1, 'b': 1 }, + { 'a': 2, 'b': 2 } + ]; - var func = _[methodName]; + _.forEach({ + 'find': [objects[1], undefined, objects[2], objects[1]], + 'findLast': [objects[2], undefined, objects[2], objects[2]], + 'findIndex': [1, -1, 2, 1], + 'findLastIndex': [2, -1, 2, 2], + 'findKey': ['1', undefined, '2', '1'], + 'findLastKey': ['2', undefined, '2', '2'] + }, + function(expected, methodName) { + QUnit.module('lodash.' + methodName); - (function() { - var objects = [ - { 'a': 0, 'b': 0 }, - { 'a': 1, 'b': 1 }, - { 'a': 2, 'b': 2 } - ]; - - var expected = ({ - 'find': [objects[1], undefined, objects[2], objects[1]], - 'findLast': [objects[2], undefined, objects[2], objects[2]], - 'findIndex': [1, -1, 2, 1], - 'findLastIndex': [2, -1, 2, 2], - 'findKey': ['1', undefined, '2', '1'], - 'findLastKey': ['2', undefined, '2', '2'] - })[methodName]; + var func = _[methodName]; test('should return the correct value', 1, function() { strictEqual(func(objects, function(object) { return object.a; }), expected[0]); @@ -2595,7 +2367,15 @@ }); test('should return `' + expected[1] + '` if value is not found', 1, function() { - strictEqual(func(objects, function(object) { return object.a === 3; }), expected[1]); + strictEqual(func(objects, function(object) { return object.a == 3; }), expected[1]); + }); + + test('should work with an object for `collection`', 1, function() { + var actual = _.find({ 'a': 1, 'b': 2, 'c': 3 }, function(num) { + return num > 2; + }); + + equal(actual, 3); }); test('should work with an object for `callback`', 1, function() { @@ -2611,7 +2391,7 @@ emptyValues = /Index/.test(methodName) ? _.reject(empties, _.isPlainObject) : empties, expecting = _.map(emptyValues, function() { return expected[1]; }); - _.each(emptyValues, function(value) { + _.forEach(emptyValues, function(value) { try { actual.push(func(value, { 'a': 3 })); } catch(e) { } @@ -2619,89 +2399,13 @@ deepEqual(actual, expecting); }); - }()); - (function() { - var expected = ({ - 'find': 1, - 'findLast': 2, - 'findKey': 'a', - 'findLastKey': 'b' - })[methodName]; - - if (expected != null) { - test('should work with an object for `collection`', 1, function() { - var actual = func({ 'a': 1, 'b': 2, 'c': 3 }, function(num) { - return num < 3; - }); - - strictEqual(actual, expected); - }); - } - }()); - - (function() { - var expected = ({ - 'find': 'a', - 'findLast': 'b', - 'findIndex': 0, - 'findLastIndex': 1 - })[methodName]; - - if (expected != null) { - test('should work with a string for `collection`', 1, function() { - var actual = func('abc', function(chr, index) { - return index < 2; - }); - - strictEqual(actual, expected); - }); - } if (methodName == 'find') { - test('should be aliased', 1, function() { + test('should be aliased', 2, function() { strictEqual(_.detect, func); + strictEqual(_.findWhere, func); }); } - }()); - }); - - /*--------------------------------------------------------------------------*/ - - QUnit.module('lodash.findWhere'); - - (function() { - var objects = [ - { 'a': 1 }, - { 'a': 1 }, - { 'a': 1, 'b': 2 }, - { 'a': 2, 'b': 2 }, - { 'a': 3 } - ]; - - test('should filter by `source` properties', 6, function() { - strictEqual(_.findWhere(objects, { 'a': 1 }), objects[0]); - strictEqual(_.findWhere(objects, { 'a': 2 }), objects[3]); - strictEqual(_.findWhere(objects, { 'a': 3 }), objects[4]); - strictEqual(_.findWhere(objects, { 'b': 1 }), undefined); - strictEqual(_.findWhere(objects, { 'b': 2 }), objects[2]); - strictEqual(_.findWhere(objects, { 'a': 1, 'b': 2 }), objects[2]); - }); - - test('should work with a function for `source`', 1, function() { - function source() {} - source.a = 2; - - strictEqual(_.findWhere(objects, source), objects[3]); - }); - - test('should match all elements when provided an empty `source`', 1, function() { - var expected = _.map(empties, _.constant(true)); - - var actual = _.map(empties, function(value) { - return _.findWhere(objects, value) === objects[0]; - }); - - deepEqual(actual, expected); }); }()); @@ -2726,26 +2430,14 @@ deepEqual(_.first(array, 2), [1, 2]); }); - test('should treat falsey `n` values, except nullish, as `0`', 1, function() { - var expected = _.map(falsey, function(value) { - return value == null ? 1 : []; - }); - - var actual = _.map(falsey, function(n) { - return _.first(array, n); - }); - - deepEqual(actual, expected); - }); - test('should return an empty array when `n` < `1`', 3, function() { - _.each([0, -1, -Infinity], function(n) { + _.forEach([0, -1, -2], function(n) { deepEqual(_.first(array, n), []); }); }); - test('should return all elements when `n` >= `array.length`', 4, function() { - _.each([3, 4, Math.pow(2, 32), Infinity], function(n) { + test('should return all elements when `n` >= `array.length`', 2, function() { + _.forEach([3, 4], function(n) { deepEqual(_.first(array, n), array); }); }); @@ -2901,22 +2593,26 @@ expected.push(undefined, undefined, undefined); deepEqual(actual1, expected); - ok('4' in actual1); + ok(4 in actual1); deepEqual(actual2, expected); - ok('4' in actual2); + ok(4 in actual2); }); test('should work with extremely large arrays', 1, function() { - // test in modern browsers + var expected = Array(5e5), + pass = true; + if (freeze) { try { - var expected = Array(5e5), - actual = _.flatten([expected]); - - deepEqual(actual, expected) + var actual = _.flatten([expected]); } catch(e) { - ok(false); + pass = false; + } + if (pass) { + deepEqual(actual, expected); + } else { + ok(pass); } } else { skipTest(); @@ -2959,11 +2655,10 @@ QUnit.module('forEach methods'); - _.each(['forEach', 'forEachRight'], function(methodName) { - var func = _[methodName], - isForEach = methodName == 'forEach'; + _.forEach(['forEach', 'forEachRight'], function(methodName) { + var func = _[methodName]; - _.each({ + _.forEach({ 'literal': 'abc', 'object': Object('abc') }, @@ -2977,7 +2672,7 @@ values.push(value); }); - if (isForEach) { + if (methodName == 'forEach') { deepEqual(args, ['a', 0, collection]); deepEqual(values, ['a', 'b', 'c']); } else { @@ -2988,7 +2683,7 @@ }); test('`_.' + methodName + '` should be aliased', 1, function() { - if (isForEach) { + if (methodName == 'forEach') { strictEqual(_.each, _.forEach); } else { strictEqual(_.eachRight, _.forEachRight); @@ -3000,7 +2695,7 @@ QUnit.module('forIn methods'); - _.each(['forIn', 'forInRight'], function(methodName) { + _.forEach(['forIn', 'forInRight'], function(methodName) { var func = _[methodName]; test('`_.' + methodName + '` iterates over inherited properties', 1, function() { @@ -3017,7 +2712,7 @@ QUnit.module('forOwn methods'); - _.each(['forOwn', 'forOwnRight'], function(methodName) { + _.forEach(['forOwn', 'forOwnRight'], function(methodName) { var func = _[methodName]; test('iterates over the `length` property', 1, function() { @@ -3035,20 +2730,15 @@ (function() { var methods = [ - 'countBy', 'every', 'filter', + 'forEach', 'forEachRight', 'forIn', 'forInRight', 'forOwn', 'forOwnRight', - 'groupBy', - 'indexBy', 'map', - 'max', - 'min', - 'partition', 'reject', 'some' ]; @@ -3058,26 +2748,6 @@ 'some' ]; - var collectionMethods = [ - 'countBy', - 'every', - 'filter', - 'find', - 'findLast', - 'forEach', - 'forEachRight', - 'groupBy', - 'indexBy', - 'map', - 'max', - 'min', - 'partition', - 'reduce', - 'reduceRight', - 'reject', - 'some' - ]; - var forInMethods = [ 'forIn', 'forInRight' @@ -3105,7 +2775,7 @@ 'forOwnRight' ]; - _.each(methods, function(methodName) { + _.forEach(methods, function(methodName) { var array = [1, 2, 3], func = _[methodName]; @@ -3133,21 +2803,22 @@ function callback(num, index) { actual = this[index]; } + func([1], callback, [2]); - strictEqual(actual, 2); + equal(actual, 2); func({ 'a': 1 }, callback, { 'a': 2 }); - strictEqual(actual, 2); + equal(actual, 2); }); }); - _.each(_.difference(methods, boolMethods), function(methodName) { + _.forEach(_.difference(methods, boolMethods), function(methodName) { var array = [1, 2, 3], func = _[methodName]; test('`_.' + methodName + '` should return a wrapped value when chaining', 1, function() { if (!isNpm) { - var actual = _(array)[methodName](_.noop); + var actual = _(array)[methodName](noop); ok(actual instanceof _); } else { @@ -3156,7 +2827,7 @@ }); }); - _.each(_.difference(methods, forInMethods), function(methodName) { + _.forEach(_.difference(methods, forInMethods), function(methodName) { var array = [1, 2, 3], func = _[methodName]; @@ -3170,56 +2841,34 @@ }); }); - _.each(iterationMethods, function(methodName) { + _.forEach(iterationMethods, function(methodName) { var array = [1, 2, 3], func = _[methodName]; test('`_.' + methodName + '` should return the collection', 1, function() { - strictEqual(func(array, Boolean), array); + equal(func(array, Boolean), array); }); test('`_.' + methodName + '` should return the existing wrapper when chaining', 1, function() { if (!isNpm) { var wrapper = _(array); - strictEqual(wrapper[methodName](_.noop), wrapper); + equal(wrapper[methodName](noop), wrapper); } else { skipTest(); } }); }); - - _.each(collectionMethods, function(methodName) { - var func = _[methodName]; - - test('`_.' + methodName + '` should treat objects with lengths of `0` as array-like', 1, function() { - var pass = true; - func({ 'length': 0 }, function() { pass = false; }, 0); - ok(pass); - }); - - test('`_.' + methodName + '` should not treat objects with negative lengths as array-like', 1, function() { - var pass = false; - func({ 'length': -1 }, function() { pass = true; }, 0); - ok(pass); - }); - - test('`_.' + methodName + '` should not treat objects with non-number lengths as array-like', 1, function() { - var pass = false; - func({ 'length': '0' }, function() { pass = true; }, 0); - ok(pass); - }); - }); }()); /*--------------------------------------------------------------------------*/ QUnit.module('collection iteration bugs'); - _.each(['forEach', 'forEachRight', 'forIn', 'forInRight', 'forOwn', 'forOwnRight'], function(methodName) { + _.forEach(['forEach', 'forEachRight', 'forIn', 'forInRight', 'forOwn', 'forOwnRight'], function(methodName) { var func = _[methodName]; - test('`_.' + methodName + '` fixes the JScript `[[DontEnum]]` bug (test in IE < 9)', 1, function() { + test('`_.' + methodName + '` fixes the JScript [[DontEnum]] bug (test in IE < 9)', 1, function() { var props = []; func(shadowedObject, function(value, prop) { props.push(prop); }); deepEqual(props.sort(), shadowedProps); @@ -3276,17 +2925,13 @@ QUnit.module('object assignments'); - _.each(['assign', 'defaults', 'merge'], function(methodName) { + _.forEach(['assign', 'defaults', 'merge'], function(methodName) { var func = _[methodName]; - test('`_.' + methodName + '` should return `undefined` when no destination object is provided', 1, function() { - strictEqual(func(), undefined); - }); - test('`_.' + methodName + '` should return the existing wrapper when chaining', 1, function() { if (!isNpm) { var wrapper = _({ 'a': 1 }); - strictEqual(wrapper[methodName]({ 'b': 2 }), wrapper); + equal(wrapper[methodName]({ 'b': 2 }), wrapper); } else { skipTest(); @@ -3316,9 +2961,10 @@ test('`_.' + methodName + '` skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', 2, function() { function Foo() {} + Foo.prototype.c = 3; + Foo.a = 1; Foo.b = 2; - Foo.prototype.c = 3; var expected = { 'a': 1, 'b': 2 }; deepEqual(func({}, Foo), expected); @@ -3331,31 +2977,9 @@ var array = [{ 'b': 2 }, { 'c': 3 }]; deepEqual(_.reduce(array, func, { 'a': 1}), { 'a': 1, 'b': 2, 'c': 3 }); }); - - test('`_.' + methodName + '` should not error on nullish sources (test in IE < 9)', 1, function() { - try { - deepEqual(func({ 'a': 1 }, undefined, { 'b': 2 }, null), { 'a': 1, 'b': 2 }); - } catch(e) { - ok(false); - } - }); - - test('`_.' + methodName + '` should not error when `object` is nullish and source objects are provided', 1, function() { - var expected = _.times(2, _.constant(true)); - - var actual = _.map([null, undefined], function(value) { - try { - return _.isEqual(func(value, { 'a': 1 }), value); - } catch(e) { - return false; - } - }); - - deepEqual(actual, expected); - }); }); - _.each(['assign', 'merge'], function(methodName) { + _.forEach(['assign', 'merge'], function(methodName) { var func = _[methodName]; test('`_.' + methodName + '` should pass the correct `callback` arguments', 2, function() { @@ -3402,7 +3026,7 @@ QUnit.module('exit early'); - _.each(['_baseEach', 'forEach', 'forEachRight', 'forIn', 'forInRight', 'forOwn', 'forOwnRight'], function(methodName) { + _.forEach(['_baseEach', 'forEach', 'forEachRight', 'forIn', 'forInRight', 'forOwn', 'forOwnRight'], function(methodName) { var func = _[methodName]; if (!func) { return; @@ -3420,7 +3044,7 @@ values = []; func(object, function(value) { values.push(value); return false; }); - strictEqual(values.length, 1); + equal(values.length, 1); }); }); @@ -3434,7 +3058,7 @@ stringObject = Object(stringLiteral), expected = [stringLiteral, stringObject]; - var largeArray = _.times(largeArraySize, function(count) { + var largeArray = _.times(LARGE_ARRAY_SIZE, function(count) { return count % 2 ? stringObject : stringLiteral; }); @@ -3444,7 +3068,7 @@ deepEqual(_.without.apply(_, [largeArray].concat(largeArray)), []); }); - test('lodash.memoize should support values that resolve to the `__proto__` key', 1, function() { + test('lodash.memoize should memoize values resolved to the `__proto__` key', 1, function() { var count = 0, memoized = _.memoize(function() { return ++count; }); @@ -3460,7 +3084,7 @@ (function() { test('should return the function names of an object', 1, function() { - var object = { 'a': 'a', 'b': _.identity, 'c': /x/, 'd': _.each }; + var object = { 'a': 'a', 'b': _.identity, 'c': /x/, 'd': _.forEach }; deepEqual(_.functions(object), ['b', 'd']); }); @@ -3469,7 +3093,8 @@ this.a = _.identity; this.b = 'b' } - Foo.prototype.c = _.noop; + + Foo.prototype.c = noop; deepEqual(_.functions(new Foo), ['a', 'c']); }); @@ -3571,7 +3196,7 @@ test('should return `false` for primitives', 1, function() { var values = falsey.concat(1, 'a'), - expected = _.map(values, _.constant(false)); + expected = _.map(values, function() { return false; }); var actual = _.map(values, function(value) { return _.has(value, 'valueOf'); @@ -3647,52 +3272,41 @@ var array = [1, 2, 3, 1, 2, 3]; test('should return the index of the first matched value', 1, function() { - strictEqual(_.indexOf(array, 3), 2); + equal(_.indexOf(array, 3), 2); }); test('should return `-1` for an unmatched value', 4, function() { - strictEqual(_.indexOf(array, 4), -1); - strictEqual(_.indexOf(array, 4, true), -1); + equal(_.indexOf(array, 4), -1); + equal(_.indexOf(array, 4, true), -1); var empty = []; - strictEqual(_.indexOf(empty, undefined), -1); - strictEqual(_.indexOf(empty, undefined, true), -1); + equal(_.indexOf(empty, undefined), -1); + equal(_.indexOf(empty, undefined, true), -1); }); test('should work with a positive `fromIndex`', 1, function() { - strictEqual(_.indexOf(array, 1, 2), 3); + equal(_.indexOf(array, 1, 2), 3); }); - test('should work with `fromIndex` >= `array.length`', 12, function() { - _.each([6, 8, Math.pow(2, 32), Infinity], function(fromIndex) { - strictEqual(_.indexOf(array, 1, fromIndex), -1); - strictEqual(_.indexOf(array, undefined, fromIndex), -1); - strictEqual(_.indexOf(array, '', fromIndex), -1); + test('should work with `fromIndex` >= `array.length`', 6, function() { + _.forEach([6, 8], function(fromIndex) { + equal(_.indexOf(array, 1, fromIndex), -1); + equal(_.indexOf(array, undefined, fromIndex), -1); + equal(_.indexOf(array, '', fromIndex), -1); }); }); - test('should treat falsey `fromIndex` values as `0`', 1, function() { - var expected = _.map(falsey, _.constant(0)); - - var actual = _.map(falsey, function(fromIndex) { - return _.indexOf(array, 1, fromIndex); - }); - - deepEqual(actual, expected); - }); - - test('should treat non-number `fromIndex` values as `0`', 1, function() { - strictEqual(_.indexOf([1, 2, 3], 1, '1'), 0); - }); - test('should work with a negative `fromIndex`', 1, function() { - strictEqual(_.indexOf(array, 2, -3), 4); + equal(_.indexOf(array, 2, -3), 4); }); - test('should work with a negative `fromIndex` <= `-array.length`', 3, function() { - _.each([-6, -8, -Infinity], function(fromIndex) { - strictEqual(_.indexOf(array, 1, fromIndex), 0); - }); + test('should work with a negative `fromIndex` <= `-array.length`', 2, function() { + strictEqual(_.indexOf(array, 1, -6), 0); + strictEqual(_.indexOf(array, 2, -8), 1); + }); + + test('should ignore non-number `fromIndex` values', 1, function() { + strictEqual(_.indexOf([1, 2, 3], 1, '1'), 0); }); test('should work with `isSorted`', 1, function() { @@ -3705,8 +3319,6 @@ QUnit.module('custom `_.indexOf` methods'); (function() { - function Foo() {} - function custom(array, value, fromIndex) { var index = (fromIndex || 0) - 1, length = array.length; @@ -3720,22 +3332,23 @@ return -1; } + function Foo() {} + var array = [1, new Foo, 3, new Foo], indexOf = _.indexOf; - var largeArray = _.times(largeArraySize, function() { + var largeArray = _.times(LARGE_ARRAY_SIZE, function() { return new Foo; }); - test('`_.contains` should work with a custom `_.indexOf` method', 2, function() { + test('`_.contains` should work with a custom `_.indexOf` method', 1, function() { if (!isModularize) { _.indexOf = custom; ok(_.contains(array, new Foo)); - ok(_.contains({ 'a': 1, 'b': new Foo, 'c': 3 }, new Foo)); _.indexOf = indexOf; } else { - skipTest(2); + skipTest(); } }); @@ -3790,7 +3403,7 @@ ]; test('should accept a falsey `array` argument', 1, function() { - var expected = _.map(falsey, _.constant([])); + var expected = _.map(falsey, function() { return []; }); var actual = _.map(falsey, function(value, index) { try { @@ -3813,26 +3426,14 @@ deepEqual(_.initial([]), []); }); - test('should treat falsey `n` values, except nullish, as `0`', 1, function() { - var expected = _.map(falsey, function(value) { - return value == null ? [1, 2] : array; - }); - - var actual = _.map(falsey, function(n) { - return _.initial(array, n); - }); - - deepEqual(actual, expected); - }); - test('should return all elements when `n` < `1`', 3, function() { - _.each([0, -1, -Infinity], function(n) { + _.forEach([0, -1, -2], function(n) { deepEqual(_.initial(array, n), array); }); }); - test('should return an empty array when `n` >= `array.length`', 4, function() { - _.each([3, 4, Math.pow(2, 32), Infinity], function(n) { + test('should return an empty array when `n` >= `array.length`', 2, function() { + _.forEach([3, 4], function(n) { deepEqual(_.initial(array, n), []); }); }); @@ -3884,32 +3485,25 @@ QUnit.module('lodash.intersection'); (function() { - var args = arguments; - test('should return the intersection of the given arrays', 1, function() { var actual = _.intersection([1, 3, 2], [5, 2, 1, 4], [2, 1]); deepEqual(actual, [1, 2]); }); - test('should return an array of unique values', 2, function() { - var array = [1, 1, 3, 2, 2]; - deepEqual(_.intersection(array, [5, 2, 2, 1, 4], [2, 1, 1]), [1, 2]); - deepEqual(_.intersection(array), [1, 3, 2]); + test('should return an array of unique values', 1, function() { + var actual = _.intersection([1, 1, 3, 2, 2], [5, 2, 2, 1, 4], [2, 1, 1]); + deepEqual(actual, [1, 2]); }); test('should work with large arrays of objects', 1, function() { var object = {}, - largeArray = _.times(largeArraySize, _.constant(object)); + expected = [object]; - deepEqual(_.intersection([object], largeArray), [object]); - }); + var largeArray = _.times(LARGE_ARRAY_SIZE, function() { + return object; + }); - test('should work with large arrays of objects', 2, function() { - var object = {}, - largeArray = _.times(largeArraySize, _.constant(object)); - - deepEqual(_.intersection([object], largeArray), [object]); - deepEqual(_.intersection(_.range(largeArraySize), null, [1]), [1]); + deepEqual(_.intersection(expected, largeArray), expected); }); test('should return a wrapped value when chaining', 2, function() { @@ -3923,13 +3517,10 @@ } }); - test('should ignore values that are not arrays or `arguments` objects', 3, function() { - var array = [0, 1, null, 3]; - deepEqual(_.intersection(array, 3, null, { '0': 1 }), array); - deepEqual(_.intersection(null, array, null, [2, 1]), [1]); - deepEqual(_.intersection(null, array, null, args), [1, 3]); + test('should ignore individual secondary values', 1, function() { + deepEqual(_.intersection([1, null, 3], 3, null), []); }); - }(1, 2, 3)); + }()); /*--------------------------------------------------------------------------*/ @@ -3967,8 +3558,8 @@ (function() { test('should invoke a methods on each element of a collection', 1, function() { - var array = ['a', 'b', 'c']; - deepEqual( _.invoke(array, 'toUpperCase'), ['A', 'B', 'C']); + var actual = _.invoke(['a', 'b', 'c'], 'toUpperCase'); + deepEqual(actual, ['A', 'B', 'C']); }); test('should work with a function `methodName` argument', 1, function() { @@ -3983,15 +3574,6 @@ var object = { 'a': 1, 'b': 2, 'c': 3 }; deepEqual(_.invoke(object, 'toFixed', 1), ['1.0', '2.0', '3.0']); }); - - test('should treat number values for `collection` as empty', 1, function() { - deepEqual(_.invoke(1), []); - }); - - test('should work with nullish elements', 1, function() { - var array = ['a', null, undefined, 'd']; - deepEqual(_.invoke(array, 'toUpperCase'), ['A', undefined, undefined, 'D']); - }); }()); /*--------------------------------------------------------------------------*/ @@ -4005,8 +3587,8 @@ strictEqual(_.isArguments(args), true); }); - test('should return `false` for non `arguments` objects', 10, function() { - var expected = _.map(falsey, _.constant(false)); + test('should return `false` for non `arguments` objects', 9, function() { + var expected = _.map(falsey, function() { return false; }); var actual = _.map(falsey, function(value, index) { return index ? _.isArguments(value) : _.isArguments(); @@ -4015,10 +3597,9 @@ strictEqual(_.isArguments([1, 2, 3]), false); strictEqual(_.isArguments(true), false); strictEqual(_.isArguments(new Date), false); - strictEqual(_.isArguments(new Error), false); strictEqual(_.isArguments(_), false); - strictEqual(_.isArguments({ '0': 1, 'callee': _.noop, 'length': 1 }), false); - strictEqual(_.isArguments(1), false); + strictEqual(_.isArguments({ '0': 1, 'callee': noop, 'length': 1 }), false); + strictEqual(_.isArguments(0), false); strictEqual(_.isArguments(/x/), false); strictEqual(_.isArguments('a'), false); @@ -4046,8 +3627,8 @@ strictEqual(_.isArray([1, 2, 3]), true); }); - test('should return `false` for non arrays', 10, function() { - var expected = _.map(falsey, _.constant(false)); + test('should return `false` for non arrays', 9, function() { + var expected = _.map(falsey, function() { return false; }); var actual = _.map(falsey, function(value, index) { return index ? _.isArray(value) : _.isArray(); @@ -4056,10 +3637,9 @@ strictEqual(_.isArray(args), false); strictEqual(_.isArray(true), false); strictEqual(_.isArray(new Date), false); - strictEqual(_.isArray(new Error), false); strictEqual(_.isArray(_), false); strictEqual(_.isArray({ '0': 1, 'length': 1 }), false); - strictEqual(_.isArray(1), false); + strictEqual(_.isArray(0), false); strictEqual(_.isArray(/x/), false); strictEqual(_.isArray('a'), false); @@ -4090,7 +3670,7 @@ strictEqual(_.isBoolean(new Boolean(false)), true); }); - test('should return `false` for non booleans', 10, function() { + test('should return `false` for non booleans', 9, function() { var expected = _.map(falsey, function(value) { return value === false; }); var actual = _.map(falsey, function(value, index) { @@ -4100,10 +3680,9 @@ strictEqual(_.isBoolean(args), false); strictEqual(_.isBoolean([1, 2, 3]), false); strictEqual(_.isBoolean(new Date), false); - strictEqual(_.isBoolean(new Error), false); strictEqual(_.isBoolean(_), false); strictEqual(_.isBoolean({ 'a': 1 }), false); - strictEqual(_.isBoolean(1), false); + strictEqual(_.isBoolean(0), false); strictEqual(_.isBoolean(/x/), false); strictEqual(_.isBoolean('a'), false); @@ -4131,8 +3710,8 @@ strictEqual(_.isDate(new Date), true); }); - test('should return `false` for non dates', 10, function() { - var expected = _.map(falsey, _.constant(false)); + test('should return `false` for non dates', 9, function() { + var expected = _.map(falsey, function() { return false; }); var actual = _.map(falsey, function(value, index) { return index ? _.isDate(value) : _.isDate(); @@ -4141,10 +3720,9 @@ strictEqual(_.isDate(args), false); strictEqual(_.isDate([1, 2, 3]), false); strictEqual(_.isDate(true), false); - strictEqual(_.isDate(new Error), false); strictEqual(_.isDate(_), false); strictEqual(_.isDate({ 'a': 1 }), false); - strictEqual(_.isDate(1), false); + strictEqual(_.isDate(0), false); strictEqual(_.isDate(/x/), false); strictEqual(_.isDate('a'), false); @@ -4166,8 +3744,6 @@ QUnit.module('lodash.isElement'); (function() { - var args = arguments; - function Element() { this.nodeType = 1; } @@ -4196,28 +3772,7 @@ } }); - test('should return `false` for non DOM elements', 11, function() { - var expected = _.map(falsey, _.constant(false)); - - var actual = _.map(falsey, function(value, index) { - return index ? _.isElement(value) : _.isElement(); - }); - - strictEqual(_.isElement(args), false); - strictEqual(_.isElement([1, 2, 3]), false); - strictEqual(_.isElement(true), false); - strictEqual(_.isElement(new Date), false); - strictEqual(_.isElement(new Error), false); - strictEqual(_.isElement(_), false); - strictEqual(_.isElement({ 'a': 1 }), false); - strictEqual(_.isElement(1), false); - strictEqual(_.isElement(/x/), false); - strictEqual(_.isElement('a'), false); - - deepEqual(actual, expected); - }); - - test('should work with DOM elements from another realm', 1, function() { + test('should work with elements from another realm', 1, function() { if (_._element) { strictEqual(_.isElement(_._element), true); } @@ -4225,7 +3780,7 @@ skipTest(); } }); - }(1, 2, 3)); + }()); /*--------------------------------------------------------------------------*/ @@ -4235,7 +3790,7 @@ var args = arguments; test('should return `true` for empty or falsey values', 3, function() { - var expected = _.map(empties, _.constant(true)); + var expected = _.map(empties, function() { return true; }); var actual = _.map(empties, function(value) { return _.isEmpty(value); @@ -4252,12 +3807,21 @@ strictEqual(_.isEmpty('a'), false); }); - test('should work with an object that has a `length` property', 1, function() { - strictEqual(_.isEmpty({ 'length': 0 }), false); + test('fixes the JScript [[DontEnum]] bug (test in IE < 9)', 1, function() { + equal(_.isEmpty(shadowedObject), false); }); - test('should work with `arguments` objects (test in IE < 9)', 1, function() { - strictEqual(_.isEmpty(args), false); + test('skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', 2, function() { + function Foo() {} + Foo.prototype.a = 1; + strictEqual(_.isEmpty(Foo), true); + + Foo.prototype = { 'a': 1 }; + strictEqual(_.isEmpty(Foo), true); + }); + + test('should work with an object that has a `length` property', 1, function() { + strictEqual(_.isEmpty({ 'length': 0 }), false); }); test('should work with jQuery/MooTools DOM query collections', 1, function() { @@ -4267,36 +3831,12 @@ strictEqual(_.isEmpty(new Foo([])), true); }); - test('should not treat objects with negative lengths as array-like', 1, function() { - function Foo() {} - Foo.prototype.length = -1; - - strictEqual(_.isEmpty(new Foo), true); - }); - - test('should not treat objects with lengths larger than `maxSafeInteger` as array-like', 1, function() { - function Foo() {} - Foo.prototype.length = maxSafeInteger + 1; - - strictEqual(_.isEmpty(new Foo), true); - }); - - test('should not treat objects with non-number lengths as array-like', 1, function() { - strictEqual(_.isEmpty({ 'length': '0' }), false); - }); - - test('fixes the JScript `[[DontEnum]]` bug (test in IE < 9)', 1, function() { - strictEqual(_.isEmpty(shadowedObject), false); - }); - - test('skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', 2, function() { - function Foo() {} - Foo.prototype.a = 1; - - strictEqual(_.isEmpty(Foo), true); - - Foo.prototype = { 'a': 1 }; - strictEqual(_.isEmpty(Foo), true); + test('should work with `arguments` objects (test in IE < 9)', 1, function() { + if (!isPhantomPage) { + strictEqual(_.isEmpty(args), false); + } else { + skipTest(); + } }); test('should return an unwrapped value when intuitively chaining', 1, function() { @@ -4350,14 +3890,14 @@ var primitive, object = { 'toString': function() { return primitive; } }, values = [true, null, 1, 'a', undefined], - expected = _.map(values, _.constant(false)); + expected = _.map(values, function() { return false; }); var actual = _.map(values, function(value) { primitive = value; return _.isEqual(object, value); }); - deepEqual(actual, expected); + ok(actual, expected); }); test('should perform comparisons between arrays', 6, function() { @@ -4471,7 +4011,7 @@ 'f': ['a', new String('b'), 'c'], 'g': new Boolean(false), 'h': new Date(2012, 4, 23), - 'i': _.noop, + 'i': noop, 'j': 'a' } }; @@ -4485,7 +4025,7 @@ 'f': ['a', 'b', 'c'], 'g': false, 'h': new Date(2012, 4, 23), - 'i': _.noop, + 'i': noop, 'j': 'a' } }; @@ -4494,7 +4034,9 @@ }); test('should perform comparisons between object instances', 4, function() { - function Foo() { this.value = 1; } + function Foo() { + this.value = 1; + } Foo.prototype.value = 1; function Bar() { @@ -4540,7 +4082,7 @@ strictEqual(_.isEqual(args1, args2), true); - if (!isPhantom) { + if (!isPhantomPage) { strictEqual(_.isEqual(args1, args3), false); } else { @@ -4548,24 +4090,7 @@ } }); - test('should treat `arguments` objects like `Object` objects', 2, function() { - var args = (function() { return arguments; }(1, 2, 3)), - object = { '0': 1, '1': 2, '2': 3, 'length': 3 }; - - function Foo() {} - Foo.prototype = object; - - strictEqual(_.isEqual(args, object), true); - - if (!isPhantom) { - strictEqual(_.isEqual(args, new Foo), false); - } - else { - skipTest(); - } - }); - - test('fixes the JScript `[[DontEnum]]` bug (test in IE < 9)', 1, function() { + test('fixes the JScript [[DontEnum]] bug (test in IE < 9)', 1, function() { strictEqual(_.isEqual(shadowedObject, {}), false); }); @@ -4711,11 +4236,11 @@ var actual = _.isEqual('a', 'a', function() { return 'a'; }); strictEqual(actual, true); - var expected = _.map(falsey, _.constant(false)); + var expected = _.map(falsey, function() { return false; }); actual = []; - _.each(falsey, function(value) { - actual.push(_.isEqual('a', 'b', _.constant(value))); + _.forEach(falsey, function(value) { + actual.push(_.isEqual('a', 'b', function() { return value; })); }); deepEqual(actual, expected); @@ -4738,13 +4263,13 @@ function Foo() { this.a = 1; } Foo.prototype.constructor = null; - var otherObject = { 'a': 1 }; - strictEqual(_.isEqual(new Foo, otherObject), false); + var other = { 'a': 1 }; + strictEqual(_.isEqual(new Foo, other), false); if (create) { - var object = create(null); + var object = Object.create(null); object.a = 1; - strictEqual(_.isEqual(object, otherObject), true); + strictEqual(_.isEqual(object, other), true); } else { skipTest(); @@ -4819,80 +4344,10 @@ skipTest(); } }); - - test('should not error on DOM elements', 1, function() { - if (document) { - var element1 = document.createElement('div'), - element2 = element1.cloneNode(true); - - try { - strictEqual(_.isEqual(element1, element2), false); - } catch(e) { - ok(false); - } - } - else { - skipTest(); - } - }); }()); /*--------------------------------------------------------------------------*/ - QUnit.module('lodash.isError'); - - (function() { - var args = arguments; - - test('should return `true` for error objects', 1, function() { - var errors = [new Error, new EvalError, new RangeError, new ReferenceError, new SyntaxError, new TypeError, new URIError], - expected = _.map(errors, _.constant(true)); - - var actual = _.map(errors, function(error) { - return _.isError(error) === true; - }); - - deepEqual(actual, expected); - }); - - test('should return `false` for non-error objects', 10, function() { - var expected = _.map(falsey, _.constant(false)); - - var actual = _.map(falsey, function(value, index) { - return index ? _.isError(value) : _.isError(); - }); - - strictEqual(_.isError(args), false); - strictEqual(_.isError([1, 2, 3]), false); - strictEqual(_.isError(true), false); - strictEqual(_.isError(new Date), false); - strictEqual(_.isError(_), false); - strictEqual(_.isError({ 'a': 1 }), false); - strictEqual(_.isError(1), false); - strictEqual(_.isError(/x/), false); - strictEqual(_.isError('a'), false); - - deepEqual(actual, expected); - }); - - test('should work with an error object from another realm', 1, function() { - if (_._object) { - var expected = _.map(_._errors, _.constant(true)); - - var actual = _.map(_._errors, function(error) { - return _.isError(error) === true; - }); - - deepEqual(actual, expected); - } - else { - skipTest(); - } - }); - }(1, 2, 3)); - - /*--------------------------------------------------------------------------*/ - QUnit.module('lodash.isFinite'); (function() { @@ -4910,13 +4365,12 @@ strictEqual(_.isFinite(-Infinity), false); }); - test('should return `false` for non-numeric values', 9, function() { + test('should return `false` for non-numeric values', 8, function() { strictEqual(_.isFinite(null), false); strictEqual(_.isFinite(undefined), false); strictEqual(_.isFinite([]), false); strictEqual(_.isFinite(true), false); strictEqual(_.isFinite(new Date), false); - strictEqual(_.isFinite(new Error), false); strictEqual(_.isFinite(''), false); strictEqual(_.isFinite(' '), false); strictEqual(_.isFinite('2px'), false); @@ -4949,8 +4403,8 @@ strictEqual(_.isFunction(_), true); }); - test('should return `false` for non functions', 10, function() { - var expected = _.map(falsey, _.constant(false)); + test('should return `false` for non functions', 9, function() { + var expected = _.map(falsey, function() { return false; }); var actual = _.map(falsey, function(value, index) { return index ? _.isFunction(value) : _.isFunction(); @@ -4960,9 +4414,8 @@ strictEqual(_.isFunction([1, 2, 3]), false); strictEqual(_.isFunction(true), false); strictEqual(_.isFunction(new Date), false); - strictEqual(_.isFunction(new Error), false); strictEqual(_.isFunction({ 'a': 1 }), false); - strictEqual(_.isFunction(1), false); + strictEqual(_.isFunction(0), false); strictEqual(_.isFunction(/x/), false); strictEqual(_.isFunction('a'), false); @@ -4991,7 +4444,7 @@ strictEqual(_.isNaN(new Number(NaN)), true); }); - test('should return `false` for non NaNs', 11, function() { + test('should return `false` for non NaNs', 10, function() { var expected = _.map(falsey, function(value) { return value !== value; }); var actual = _.map(falsey, function(value, index) { @@ -5002,10 +4455,9 @@ strictEqual(_.isNaN([1, 2, 3]), false); strictEqual(_.isNaN(true), false); strictEqual(_.isNaN(new Date), false); - strictEqual(_.isNaN(new Error), false); strictEqual(_.isNaN(_), false); strictEqual(_.isNaN({ 'a': 1 }), false); - strictEqual(_.isNaN(1), false); + strictEqual(_.isNaN(0), false); strictEqual(_.isNaN(/x/), false); strictEqual(_.isNaN('a'), false); @@ -5033,7 +4485,7 @@ strictEqual(_.isNull(null), true); }); - test('should return `false` for non nulls', 11, function() { + test('should return `false` for non nulls', 10, function() { var expected = _.map(falsey, function(value) { return value === null; }); var actual = _.map(falsey, function(value, index) { @@ -5044,10 +4496,9 @@ strictEqual(_.isNull([1, 2, 3]), false); strictEqual(_.isNull(true), false); strictEqual(_.isNull(new Date), false); - strictEqual(_.isNull(new Error), false); strictEqual(_.isNull(_), false); strictEqual(_.isNull({ 'a': 1 }), false); - strictEqual(_.isNull(1), false); + strictEqual(_.isNull(0), false); strictEqual(_.isNull(/x/), false); strictEqual(_.isNull('a'), false); @@ -5076,7 +4527,7 @@ strictEqual(_.isNumber(new Number(0)), true); }); - test('should return `false` for non numbers', 10, function() { + test('should return `false` for non numbers', 9, function() { var expected = _.map(falsey, function(value) { return typeof value == 'number'; }); var actual = _.map(falsey, function(value, index) { @@ -5087,7 +4538,6 @@ strictEqual(_.isNumber([1, 2, 3]), false); strictEqual(_.isNumber(true), false); strictEqual(_.isNumber(new Date), false); - strictEqual(_.isNumber(new Error), false); strictEqual(_.isNumber(_), false); strictEqual(_.isNumber({ 'a': 1 }), false); strictEqual(_.isNumber(/x/), false); @@ -5117,12 +4567,11 @@ (function() { var args = arguments; - test('should return `true` for objects', 11, function() { + test('should return `true` for objects', 10, function() { strictEqual(_.isObject(args), true); strictEqual(_.isObject([1, 2, 3]), true); strictEqual(_.isObject(new Boolean(false)), true); strictEqual(_.isObject(new Date), true); - strictEqual(_.isObject(new Error), true); strictEqual(_.isObject(_), true); strictEqual(_.isObject({ 'a': 1 }), true); strictEqual(_.isObject(new Number(0)), true); @@ -5138,7 +4587,7 @@ test('should return `false` for non objects', 1, function() { var values = falsey.concat('a', true), - expected = _.map(values, _.constant(false)); + expected = _.map(values, function() { return false; }); var actual = _.map(values, function(value, index) { return index ? _.isObject(value) : _.isObject(); @@ -5179,7 +4628,7 @@ // 2: Initial check with object, this is the other half of the trigger _.isObject(obj); - strictEqual(_.isObject(str), false); + equal(_.isObject(str), false); }); }(1, 2, 3)); @@ -5202,7 +4651,7 @@ strictEqual(_.isPlainObject(new Foo(1)), false); }); - test('should return `true` for objects with a `[[Prototype]]` of `null`', 1, function() { + test('should return `true` for objects a [[Prototype]] of `null`', 1, function() { if (create) { strictEqual(_.isPlainObject(create(null)), true); } else { @@ -5232,14 +4681,14 @@ } }); - test('should return `false` for Object objects without a `[[Class]]` of "Object"', 3, function() { + test('should return `false` for Object objects without a [[Class]] of "Object"', 3, function() { strictEqual(_.isPlainObject(arguments), false); strictEqual(_.isPlainObject(Error), false); strictEqual(_.isPlainObject(Math), false); }); test('should return `false` for non objects', 3, function() { - var expected = _.map(falsey, _.constant(false)); + var expected = _.map(falsey, function() { return false; }); var actual = _.map(falsey, function(value, index) { return index ? _.isPlainObject(value) : _.isPlainObject(); @@ -5272,8 +4721,8 @@ strictEqual(_.isRegExp(RegExp('x')), true); }); - test('should return `false` for non regexes', 10, function() { - var expected = _.map(falsey, _.constant(false)); + test('should return `false` for non regexes', 9, function() { + var expected = _.map(falsey, function(value) { return false; }); var actual = _.map(falsey, function(value, index) { return index ? _.isRegExp(value) : _.isRegExp(); @@ -5283,10 +4732,9 @@ strictEqual(_.isRegExp([1, 2, 3]), false); strictEqual(_.isRegExp(true), false); strictEqual(_.isRegExp(new Date), false); - strictEqual(_.isRegExp(new Error), false); strictEqual(_.isRegExp(_), false); strictEqual(_.isRegExp({ 'a': 1 }), false); - strictEqual(_.isRegExp(1), false); + strictEqual(_.isRegExp(0), false); strictEqual(_.isRegExp('a'), false); deepEqual(actual, expected); @@ -5314,7 +4762,7 @@ strictEqual(_.isString(new String('a')), true); }); - test('should return `false` for non strings', 10, function() { + test('should return `false` for non strings', 9, function() { var expected = _.map(falsey, function(value) { return value === ''; }); var actual = _.map(falsey, function(value, index) { @@ -5325,10 +4773,9 @@ strictEqual(_.isString([1, 2, 3]), false); strictEqual(_.isString(true), false); strictEqual(_.isString(new Date), false); - strictEqual(_.isString(new Error), false); strictEqual(_.isString(_), false); strictEqual(_.isString({ '0': 1, 'length': 1 }), false); - strictEqual(_.isString(1), false); + strictEqual(_.isString(0), false); strictEqual(_.isString(/x/), false); deepEqual(actual, expected); @@ -5356,21 +4803,20 @@ strictEqual(_.isUndefined(undefined), true); }); - test('should return `false` for non `undefined` values', 11, function() { + test('should return `false` for non `undefined` values', 10, function() { var expected = _.map(falsey, function(value) { return value === undefined; }); var actual = _.map(falsey, function(value, index) { - return index ? _.isUndefined(value) : _.isUndefined(); + return _.isUndefined(value); }); strictEqual(_.isUndefined(args), false); strictEqual(_.isUndefined([1, 2, 3]), false); strictEqual(_.isUndefined(true), false); strictEqual(_.isUndefined(new Date), false); - strictEqual(_.isUndefined(new Error), false); strictEqual(_.isUndefined(_), false); strictEqual(_.isUndefined({ 'a': 1 }), false); - strictEqual(_.isUndefined(1), false); + strictEqual(_.isUndefined(0), false); strictEqual(_.isUndefined(/x/), false); strictEqual(_.isUndefined('a'), false); @@ -5392,13 +4838,13 @@ QUnit.module('isType checks'); (function() { - test('should return `false` for subclassed values', 8, function() { + test('should return `false` for subclassed values', 7, function() { var funcs = [ - 'isArray', 'isBoolean', 'isDate', 'isError', - 'isFunction', 'isNumber', 'isRegExp', 'isString' + 'isArray', 'isBoolean', 'isDate', 'isFunction', + 'isNumber', 'isRegExp', 'isString' ]; - _.each(funcs, function(methodName) { + _.forEach(funcs, function(methodName) { function Foo() {} Foo.prototype = root[methodName.slice(2)].prototype; @@ -5422,7 +4868,7 @@ 'isObject', 'isNull', 'isNumber', 'isRegExp', 'isString', 'isUndefined' ]; - _.each(funcs, function(methodName) { + _.forEach(funcs, function(methodName) { var pass = true; try { _[methodName](xml); @@ -5440,146 +4886,52 @@ /*--------------------------------------------------------------------------*/ - QUnit.module('keys methods'); + QUnit.module('lodash.keys'); - _.each(['keys', 'keysIn'], function(methodName) { - var args = arguments, - func = _[methodName], - isKeys = methodName == 'keys'; + (function() { + var args = arguments; - test('`_.' + methodName + '` should return the keys of an object', 1, function() { - var object = { 'a': 1, 'b': 1 }, - actual = func(object); - - deepEqual(actual.sort(), ['a', 'b']); + test('should return the keys of an object', 1, function() { + var object = { 'a': 1, 'b': 1 }; + deepEqual(_.keys(object), ['a', 'b']); }); - test('`_.' + methodName + '` should treat sparse arrays as dense', 1, function() { + test('should work with sparse arrays', 1, function() { var array = [1]; array[2] = 3; - - var actual = func(array); - deepEqual(actual.sort(), ['0', '1', '2']); + deepEqual(_.keys(array), ['0', '2']); }); - test('`_.' + methodName + '` should custom properties on arrays', 1, function() { - var array = [1]; - array.a = 1; - - var actual = func(array); - deepEqual(actual.sort(), ['0', 'a']); - }); - - test('`_.' + methodName + '` should ' + (isKeys ? 'not' : '') + ' include inherited properties of arrays', 1, function() { - Array.prototype.a = 1; - var expected = isKeys ? ['0'] : ['0', 'a'], - actual = func([1]); - - deepEqual(actual.sort(), expected); - delete Array.prototype.a; - }); - - test('`_.' + methodName + '` should work with `arguments` objects (test in IE < 9)', 1, function() { - if (!isPhantom) { - var actual = func(args); - deepEqual(actual.sort(), ['0', '1', '2']); + test('should work with `arguments` objects (test in IE < 9)', 1, function() { + if (!isPhantomPage) { + deepEqual(_.keys(args), ['0', '1', '2']); } else { skipTest(); } }); - test('`_.' + methodName + '` should custom properties on `arguments` objects', 1, function() { - if (!isPhantom) { - args.a = 1; - var actual = func(args); - - deepEqual(actual.sort(), ['0', '1', '2', 'a']); - delete args.a; - } else { - skipTest(); - } - }); - - test('`_.' + methodName + '` should ' + (isKeys ? 'not' : '') + ' include inherited properties of `arguments` objects', 1, function() { - if (!isPhantom) { - Object.prototype.a = 1; - var expected = isKeys ? ['0', '1', '2'] : ['0', '1', '2', 'a'], - actual = func(args); - - deepEqual(actual.sort(), expected); - delete Object.prototype.a; - } else { - skipTest(); - } - }); - - test('`_.' + methodName + '` should work with string objects (test in IE < 9)', 1, function() { - var actual = func(Object('abc')); - deepEqual(actual.sort(), ['0', '1', '2']); - }); - - test('`_.' + methodName + '` should custom properties on string objects', 1, function() { - var object = Object('a'); - object.a = 1; - - var actual = func(object); - deepEqual(actual.sort(), ['0', 'a']); - }); - - test('`_.' + methodName + '` should ' + (isKeys ? 'not' : '') + ' include inherited properties of string objects', 1, function() { - String.prototype.a = 1; - var expected = isKeys ? ['0'] : ['0', 'a'], - actual = func(Object('a')); - - deepEqual(actual.sort(), expected); - delete String.prototype.a; - }); - - test('`_.' + methodName + '` fixes the JScript `[[DontEnum]]` bug (test in IE < 9)', 1, function() { - var actual = func(shadowedObject); - deepEqual(actual.sort(), shadowedProps); - }); - - test('`_.' + methodName + '` skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', 2, function() { - function Foo() {} - Foo.a = 1; - Foo.b = 2; - Foo.prototype.c = 3; - - var expected = ['a', 'b'], - actual = func(Foo); - - deepEqual(actual.sort(), expected); - - Foo.prototype = { 'c': 3 }; - actual = func(Foo); - deepEqual(actual.sort(), expected); - }); - - test('`_.' + methodName + '` skips the `constructor` property on prototype objects', 2, function() { + test('fixes the JScript [[DontEnum]] bug (test in IE < 9)', 2, function() { function Foo() {} Foo.prototype.a = 1; - var expected = ['a']; - deepEqual(func(Foo.prototype), ['a']); - - Foo.prototype = { 'constructor': Foo, 'a': 1 }; - deepEqual(func(Foo.prototype), ['a']); + deepEqual(_.keys(Foo.prototype), ['a']); + deepEqual(_.keys(shadowedObject).sort(), shadowedProps); }); - test('`_.' + methodName + '` should ' + (isKeys ? 'not' : '') + ' include inherited properties', 1, function() { - function Foo() { - this.a = 1; - this.b = 2; - } + test('skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', 2, function() { + function Foo() {} Foo.prototype.c = 3; - var expected = isKeys ? ['a', 'b'] : ['a', 'b', 'c'], - actual = func(new Foo); + Foo.a = 1; + Foo.b = 2; - deepEqual(actual.sort(), expected); + var expected = ['a', 'b']; + deepEqual(_.keys(Foo), expected); + + Foo.prototype = { 'c': 3 }; + deepEqual(_.keys(Foo), expected); }); - }); + }(1, 2, 3)); /*--------------------------------------------------------------------------*/ @@ -5595,33 +4947,21 @@ ]; test('should return the last element', 1, function() { - strictEqual(_.last(array), 3); + equal(_.last(array), 3); }); test('should return the last two elements', 1, function() { deepEqual(_.last(array, 2), [2, 3]); }); - test('should treat falsey `n` values, except nullish, as `0`', 1, function() { - var expected = _.map(falsey, function(value) { - return value == null ? 3 : []; - }); - - var actual = _.map(falsey, function(n) { - return _.last(array, n); - }); - - deepEqual(actual, expected); - }); - test('should return an empty array when `n` < `1`', 3, function() { - _.each([0, -1, -Infinity], function(n) { + _.forEach([0, -1, -2], function(n) { deepEqual(_.last(array, n), []); }); }); - test('should return all elements when `n` >= `array.length`', 4, function() { - _.each([3, 4, Math.pow(2, 32), Infinity], function(n) { + test('should return all elements when `n` >= `array.length`', 2, function() { + _.forEach([3, 4], function(n) { deepEqual(_.last(array, n), array); }); }); @@ -5689,7 +5029,7 @@ test('should not chain when arguments are not provided', 1, function() { if (!isNpm) { var actual = _(array).last(); - strictEqual(actual, 3); + equal(actual, 3); } else { skipTest(); @@ -5713,50 +5053,37 @@ var array = [1, 2, 3, 1, 2, 3]; test('should return the index of the last matched value', 1, function() { - strictEqual(_.lastIndexOf(array, 3), 5); + equal(_.lastIndexOf(array, 3), 5); }); test('should return `-1` for an unmatched value', 1, function() { - strictEqual(_.lastIndexOf(array, 4), -1); + equal(_.lastIndexOf(array, 4), -1); }); test('should work with a positive `fromIndex`', 1, function() { strictEqual(_.lastIndexOf(array, 1, 2), 0); }); - test('should work with `fromIndex` >= `array.length`', 12, function() { - _.each([6, 8, Math.pow(2, 32), Infinity], function(fromIndex) { - strictEqual(_.lastIndexOf(array, undefined, fromIndex), -1); - strictEqual(_.lastIndexOf(array, 1, fromIndex), 3); - strictEqual(_.lastIndexOf(array, '', fromIndex), -1); + test('should work with `fromIndex` >= `array.length`', 6, function() { + _.forEach([6, 8], function(fromIndex) { + equal(_.lastIndexOf(array, undefined, fromIndex), -1); + equal(_.lastIndexOf(array, 1, fromIndex), 3); + equal(_.lastIndexOf(array, '', fromIndex), -1); }); }); - test('should treat falsey `fromIndex` values, except `0` and `NaN`, as `array.length`', 1, function() { - var expected = _.map(falsey, function(value) { - return typeof value == 'number' ? -1 : 5; - }); - - var actual = _.map(falsey, function(fromIndex) { - return _.lastIndexOf(array, 3, fromIndex); - }); - - deepEqual(actual, expected); - }); - - test('should treat non-number `fromIndex` values as `array.length`', 2, function() { - strictEqual(_.lastIndexOf(array, 3, '1'), 5); - strictEqual(_.lastIndexOf(array, 3, true), 5); - }); - test('should work with a negative `fromIndex`', 1, function() { strictEqual(_.lastIndexOf(array, 2, -3), 1); }); - test('should work with a negative `fromIndex` <= `-array.length`', 3, function() { - _.each([-6, -8, -Infinity], function(fromIndex) { - strictEqual(_.lastIndexOf(array, 1, fromIndex), 0); - }); + test('should work with a negative `fromIndex` <= `-array.length`', 2, function() { + strictEqual(_.lastIndexOf(array, 1, -6), 0); + equal(_.lastIndexOf(array, 2, -8), -1); + }); + + test('should ignore non-number `fromIndex` values', 2, function() { + equal(_.lastIndexOf([1, 2, 3], 3, '1'), 2); + equal(_.lastIndexOf([1, 2, 3], 3, true), 2); }); }()); @@ -5765,11 +5092,11 @@ QUnit.module('indexOf methods'); (function() { - _.each(['indexOf', 'lastIndexOf'], function(methodName) { + _.forEach(['indexOf', 'lastIndexOf'], function(methodName) { var func = _[methodName]; test('`_.' + methodName + '` should accept a falsey `array` argument', 1, function() { - var expected = _.map(falsey, _.constant(-1)); + var expected = _.map(falsey, function() { return -1; }); var actual = _.map(falsey, function(value, index) { try { @@ -5836,7 +5163,7 @@ test('should return a wrapped value when chaining', 1, function() { if (!isNpm) { - ok(_(array).map(_.noop) instanceof _); + ok(_(array).map(noop) instanceof _); } else { skipTest(); @@ -5856,8 +5183,8 @@ } }); - test('should accept a falsey `collection` argument', 1, function() { - var expected = _.map(falsey, _.constant([])); + test('should accept a falsey `array` argument', 1, function() { + var expected = _.map(falsey, function() { return []; }); var actual = _.map(falsey, function(value, index) { try { @@ -5868,10 +5195,6 @@ deepEqual(actual, expected); }); - test('should treat number values for `collection` as empty', 1, function() { - deepEqual(_.map(1), []); - }); - test('should be aliased', 1, function() { strictEqual(_.collect, _.map); }); @@ -5921,7 +5244,7 @@ test('should return a wrapped value when chaining', 1, function() { if (!isNpm) { - ok(_(object).mapValues(_.noop) instanceof _); + ok(_(object).mapValues(noop) instanceof _); } else { skipTest(); @@ -5929,7 +5252,7 @@ }); test('should accept a falsey `object` argument', 1, function() { - var expected = _.map(falsey, _.constant({})); + var expected = _.map(falsey, function() { return {}; }); var actual = _.map(falsey, function(value, index) { try { @@ -5946,60 +5269,21 @@ QUnit.module('lodash.matches'); (function() { - var object = { 'a': 1, 'b': 2, 'c': 3 }, - sources = [{ 'a': 1 }, { 'a': 1, 'c': 3 }]; + var object = { 'a': 1, 'b': 2 }; - test('should create a function that performs a deep comparison between a given object and the `source` object', 6, function() { - _.each(sources, function(source, index) { - var matches = _.matches(source); - strictEqual(matches.length, 1); - strictEqual(matches(object), true); + test('should create a function that performs a deep comparison between a given object and the `source` object', 3, function() { + var matches = _.matches({ 'a': 1 }); - matches = _.matches(index ? { 'c': 3, 'd': 4 } : { 'b': 1 }); - strictEqual(matches(object), false); - }); + equal(matches.length, 1); + strictEqual(matches(object), true); + + matches = _.matches({ 'b': 1 }); + strictEqual(matches(object), false); }); - test('should return `true` when comparing an empty `source`', 1, function() { - var expected = _.map(empties, _.constant(true)); - - var actual = _.map(empties, function(value) { - var matches = _.matches(value); - return matches(object) === true; - }); - - deepEqual(actual, expected); - }); - - test('should not error error for falsey `object` values', 2, function() { - var expected = _.map(falsey, _.constant(true)); - - _.each(sources, function(source) { - var matches = _.matches(source); - - var actual = _.map(falsey, function(value, index) { - try { - var result = index ? matches(value) : matches(); - return result === false; - } catch(e) { } - }); - - deepEqual(actual, expected); - }); - }); - - test('should return `true` when comparing an empty `source` to a falsey `object`', 1, function() { - var expected = _.map(falsey, _.constant(true)), - matches = _.matches({}); - - var actual = _.map(falsey, function(value, index) { - try { - var result = index ? matches(value) : matches(); - return result === true; - } catch(e) { } - }); - - deepEqual(actual, expected); + test('should return `false` when comparing an empty `source`', 1, function() { + var matches = _.matches({}); + strictEqual(matches(object), false); }); }()); @@ -6009,7 +5293,7 @@ (function() { test('should return the largest value from a collection', 1, function() { - strictEqual(3, _.max([1, 2, 3])); + equal(3, _.max([1, 2, 3])); }); test('should return `-Infinity` for empty collections', 1, function() { @@ -6048,16 +5332,16 @@ return a + b + c; }); - strictEqual(memoized(1, 2, 3), 6); - strictEqual(memoized(1, 3, 5), 6); + equal(memoized(1, 2, 3), 6); + equal(memoized(1, 3, 5), 6); }); test('should support a `resolver` argument', 2, function() { var fn = function(a, b, c) { return a + b + c; }, memoized = _.memoize(fn, fn); - strictEqual(memoized(1, 2, 3), 6); - strictEqual(memoized(1, 3, 5), 9); + equal(memoized(1, 2, 3), 6); + equal(memoized(1, 3, 5), 9); }); test('should not set a `this` binding', 2, function() { @@ -6066,31 +5350,15 @@ }); var object = { 'b': 2, 'c': 3, 'memoized': memoized }; - strictEqual(object.memoized(1), 6); - strictEqual(object.memoized(2), 7); - }); - - test('should throw a TypeError if `resolve` is truthy and not a function', function() { - raises(function() { _.memoize(_.noop, {}); }, TypeError); - }); - - test('should not throw a TypeError if `resolve` is falsey', function() { - var expected = _.map(falsey, _.constant(true)); - - var actual = _.map(falsey, function(value, index) { - try { - return _.isFunction(index ? _.memoize(_.noop, value) : _.memoize(_.noop)); - } catch(e) { } - }); - - deepEqual(actual, expected); + equal(object.memoized(1), 6); + equal(object.memoized(2), 7); }); test('should check cache for own properties', 1, function() { var actual = [], memoized = _.memoize(_.identity); - _.each(shadowedProps, function(value) { + _.forEach(shadowedProps, function(value) { actual.push(memoized(value)); }); @@ -6098,14 +5366,14 @@ }); test('should expose a `cache` object on the `memoized` function', 4, function() { - _.each(['_a', 'a'], function(key, index) { + _.forEach(['_a', 'a'], function(key, index) { var memoized = _.memoize(_.identity, index && _.identity); memoized('a'); - strictEqual(memoized.cache[key], 'a'); + equal(memoized.cache[key], 'a'); memoized.cache[key] = 'b'; - strictEqual(memoized('a'), 'b'); + equal(memoized('a'), 'b'); }); }); }()); @@ -6177,7 +5445,7 @@ }; var actual = _.merge(object, source); - strictEqual(_.isArguments(actual.args), false); + equal(_.isArguments(actual.args), false); }); test('should work with four arguments', 1, function() { @@ -6226,7 +5494,7 @@ (function() { test('should return the smallest value from a collection', 1, function() { - strictEqual(1, _.min([1, 2, 3])); + equal(1, _.min([1, 2, 3])); }); test('should return `Infinity` for empty collections', 1, function() { @@ -6259,16 +5527,15 @@ QUnit.module('lodash.max and lodash.min'); - _.each(['max', 'min'], function(methodName) { + _.forEach(['max', 'min'], function(methodName) { var array = [1, 2, 3], - func = _[methodName], - isMax = methodName == 'max'; + func = _[methodName]; test('`_.' + methodName + '` should work with Date objects', 1, function() { var now = new Date, past = new Date(0); - strictEqual(func([now, past]), isMax ? now : past); + equal(func([now, past]), methodName == 'max' ? now : past); }); test('`_.' + methodName + '` should work with a callback argument', 1, function() { @@ -6276,7 +5543,7 @@ return -num; }); - strictEqual(actual, isMax ? 1 : 3); + equal(actual, methodName == 'max' ? 1 : 3); }); test('`_.' + methodName + '` should pass the correct `callback` arguments when iterating an array', 1, function() { @@ -6310,39 +5577,29 @@ return -this[index]; }, array); - strictEqual(actual, isMax ? 1 : 3); + equal(actual, methodName == 'max' ? 1 : 3); }); test('`_.' + methodName + '` should work when used as a callback for `_.map`', 1, function() { var array = [[2, 3, 1], [5, 6, 4], [8, 9, 7]], actual = _.map(array, func); - deepEqual(actual, isMax ? [3, 6, 9] : [1, 4, 7]); + deepEqual(actual, methodName == 'max' ? [3, 6, 9] : [1, 4, 7]); }); test('`_.' + methodName + '` should iterate an object', 1, function() { var actual = func({ 'a': 1, 'b': 2, 'c': 3 }); - strictEqual(actual, isMax ? 3 : 1); + equal(actual, methodName == 'max' ? 3 : 1); }); test('`_.' + methodName + '` should iterate a string', 2, function() { - _.each(['abc', Object('abc')], function(value) { + _.forEach(['abc', Object('abc')], function(value) { var actual = func(value); - strictEqual(actual, isMax ? 'c' : 'a'); + equal(actual, methodName == 'max' ? 'c' : 'a'); }); }); - test('`_.' + methodName + '` should work when `callback` returns +/-Infinity', 1, function() { - var object = { 'a': (isMax ? -Infinity : Infinity) }; - - var actual = func([object, { 'a': object.a }], function(object) { - return object.a; - }); - - strictEqual(actual, object); - }); - - test('`_.' + methodName + '` should work when chaining on an array with only one value', 1, function() { + test('`_.' + methodName + '` should resolve the correct value when provided an array containing only one value', 1, function() { if (!isNpm) { var actual = _([40])[methodName]().value(); strictEqual(actual, 40); @@ -6354,7 +5611,7 @@ test('`_.' + methodName + '` should work with extremely large arrays', 1, function() { var array = _.range(0, 5e5); - strictEqual(func(array), isMax ? 499999 : 0); + equal(func(array), methodName == 'max' ? 499999 : 0); }); }); @@ -6371,36 +5628,15 @@ } var value = ['a'], - source = { 'a': function(array) { return array[0]; }, 'b': 'B' }; - - test('should use `this` as the default `object` value', 3, function() { - var object = _.create(_); - object.mixin(source); - - strictEqual(object.a(value), 'a'); - - ok(!('a' in _)); - ok(!('a' in _.prototype)); - - delete wrapper.a; - delete wrapper.prototype.a; - delete wrapper.b; - delete wrapper.prototype.b; - }); + source = { 'a': function(array) { return array[0]; } }; test('should accept an `object` argument', 1, function() { - var object = {}; - _.mixin(object, source); - strictEqual(object.a(value), 'a'); + var lodash = {}; + _.mixin(lodash, source); + strictEqual(lodash.a(value), 'a'); }); - test('should return `object`', 2, function() { - var object = {}; - strictEqual(_.mixin(object, source), object); - strictEqual(_.mixin(), _); - }); - - test('should work with a function for `object`', 2, function() { + test('should accept a function `object` argument', 2, function() { _.mixin(wrapper, source); var wrapped = wrapper(value), @@ -6411,24 +5647,30 @@ delete wrapper.a; delete wrapper.prototype.a; - delete wrapper.b; - delete wrapper.prototype.b; }); test('should mixin `source` methods into lodash', 4, function() { - _.mixin(source); + if (!isNpm) { + _.mixin({ + 'a': 'a', + 'A': function(string) { return string.toUpperCase(); } + }); - strictEqual(_.a(value), 'a'); - strictEqual(_(value).a().__wrapped__, 'a'); + equal('a' in _, false); + equal('a' in _.prototype, false); - delete _.a; - delete _.prototype.a; + delete _.a; + delete _.prototype.a; - ok(!('b' in _)); - ok(!('b' in _.prototype)); + equal(_.A('a'), 'A'); + equal(_('a').A().value(), 'A'); - delete _.b; - delete _.prototype.b; + delete _.A; + delete _.prototype.A; + } + else { + skipTest(4); + } }); test('should accept an `options` argument', 16, function() { @@ -6436,8 +5678,8 @@ return (func === _ ? 'lodash' : 'provided') + ' function should ' + (chain ? '' : 'not ') + 'chain'; } - _.each([_, wrapper], function(func) { - _.each([false, true, { 'chain': false }, { 'chain': true }], function(options) { + _.forEach([_, wrapper], function(func) { + _.forEach([false, true, { 'chain': false }, { 'chain': true }], function(options) { if (func === _) { _.mixin(source, options); } else { @@ -6446,17 +5688,15 @@ var wrapped = func(value), actual = wrapped.a(); - if (options === true || (options && options.chain)) { + if (options && (options === true || options.chain)) { strictEqual(actual.__wrapped__, 'a', message(func, true)); ok(actual instanceof func, message(func, true)); } else { strictEqual(actual, 'a', message(func, false)); - ok(!(actual instanceof func), message(func, false)); + equal(actual instanceof func, false, message(func, false)); } delete func.a; delete func.prototype.a; - delete func.b; - delete func.prototype.b; }); }); }); @@ -6480,36 +5720,9 @@ } delete _.a; delete _.prototype.a; - delete _.b; - delete _.prototype.b; ok(pass); }); - - test('should return the existing wrapper when chaining', 2, function() { - if (!isNpm) { - _.each([_, wrapper], function(func) { - if (func === _) { - var wrapper = _(source), - actual = wrapper.mixin(); - - strictEqual(actual.value(), _); - } - else { - wrapper = _(func); - actual = wrapper.mixin(source); - strictEqual(actual, wrapper); - } - delete func.a; - delete func.prototype.a; - delete func.b; - delete func.prototype.b; - }); - } - else { - skipTest(2); - } - }); }()); /*--------------------------------------------------------------------------*/ @@ -6519,7 +5732,7 @@ (function() { test('should always return `undefined`', 1, function() { var values = falsey.concat([], true, new Date, _, {}, /x/, 'a'), - expected = _.map(values, _.constant()); + expected = _.map(values, function() { return undefined; }); var actual = _.map(values, function(value, index) { return index ? _.noop(value) : _.noop(); @@ -6634,16 +5847,16 @@ (function() { test('should execute `func` once', 1, function() { var count = 0, - once = _.once(function() { count++; }); + func = _.once(function() { count++; }); - once(); - once(); + func(); + func(); strictEqual(count, 1); }); test('should not set a `this` binding', 1, function() { - var once = _.once(function() { this.count++; }), - object = { 'count': 0, 'once': once }; + var func = _.once(function() { this.count++; }), + object = { 'count': 0, 'once': func }; object.once(); object.once(); @@ -6653,26 +5866,26 @@ test('should ignore recursive calls', 1, function() { var count = 0; - var once = _.once(function() { + var func = _.once(function() { count++; - once(); + func(); }); - once(); + func(); strictEqual(count, 1); }); test('should not throw more than once', 2, function() { - var once = _.once(function() { + var pass = true; + + var func = _.once(function() { throw new Error; }); - raises(function() { once(); }, Error); - - var pass = true; + raises(function() { func(); }, Error); try { - once(); + func(); } catch(e) { pass = false; } @@ -6682,104 +5895,6 @@ /*--------------------------------------------------------------------------*/ - QUnit.module('lodash.pad'); - - (function() { - test('should pad a string to a given length', 1, function() { - strictEqual(_.pad('abc', 9), ' abc '); - }); - - test('should truncate pad characters to fit the pad length', 2, function() { - strictEqual(_.pad('abc', 8), ' abc '); - strictEqual(_.pad('abc', 8, '_-'), '_-abc_-_'); - }); - - test('should coerce `string` to a string', 2, function() { - strictEqual(_.pad(Object('abc'), 4), 'abc '); - strictEqual(_.pad({ 'toString': _.constant('abc') }, 5), ' abc '); - }); - }()); - - /*--------------------------------------------------------------------------*/ - - QUnit.module('lodash.padLeft'); - - (function() { - test('should pad a string to a given length', 1, function() { - strictEqual(_.padLeft('abc', 6), ' abc'); - }); - - test('should truncate pad characters to fit the pad length', 1, function() { - strictEqual(_.padLeft('abc', 6, '_-'), '_-_abc'); - }); - - test('should coerce `string` to a string', 2, function() { - strictEqual(_.padLeft(Object('abc'), 4), ' abc'); - strictEqual(_.padLeft({ 'toString': _.constant('abc') }, 5), ' abc'); - }); - }()); - - /*--------------------------------------------------------------------------*/ - - QUnit.module('lodash.padRight'); - - (function() { - test('should pad a string to a given length', 1, function() { - strictEqual(_.padRight('abc', 6), 'abc '); - }); - - test('should truncate pad characters to fit the pad length', 1, function() { - strictEqual(_.padRight('abc', 6, '_-'), 'abc_-_'); - }); - - test('should coerce `string` to a string', 2, function() { - strictEqual(_.padRight(Object('abc'), 4), 'abc '); - strictEqual(_.padRight({ 'toString': _.constant('abc') }, 5), 'abc '); - }); - }()); - - /*--------------------------------------------------------------------------*/ - - QUnit.module('pad methods'); - - _.each(['pad', 'padLeft', 'padRight'], function(methodName, index) { - var func = _[methodName]; - - test('`_.' + methodName + '` should not pad is string is >= `length`', 2, function() { - strictEqual(func('abc', 2), 'abc'); - strictEqual(func('abc', 3), 'abc'); - }); - - test('`_.' + methodName + '` should treat negative `length` as `0`', 2, function() { - _.each([0, -2], function(length) { - strictEqual(func('abc', length), 'abc'); - }); - }); - - test('`_.' + methodName + '` should coerce `length` to a number', 2, function() { - _.each(['', '4'], function(length) { - var actual = length ? (index == 1 ? ' abc' : 'abc ') : 'abc'; - strictEqual(func('abc', length), actual); - }); - }); - - test('`_.' + methodName + '` should return an empty string when provided `null`, `undefined`, or empty string and `chars`', 6, function() { - _.each([null, '_-'], function(chars) { - strictEqual(func(null, 0, chars), ''); - strictEqual(func(undefined, 0, chars), ''); - strictEqual(func('', 0, chars), ''); - }); - }); - - test('`_.' + methodName + '` should work with `null`, `undefined`, or empty string for `chars`', 3, function() { - notStrictEqual(func('abc', 6, null), 'abc'); - notStrictEqual(func('abc', 6, undefined), 'abc'); - strictEqual(func('abc', 6, ''), 'abc'); - }); - }); - - /*--------------------------------------------------------------------------*/ - QUnit.module('lodash.pairs'); (function() { @@ -6817,7 +5932,7 @@ }); test('should use a radix of `16`, for hexadecimals, if `radix` is `undefined` or `0`', 8, function() { - _.each(['0x20', '0X20'], function(string) { + _.forEach(['0x20', '0X20'], function(string) { strictEqual(_.parseInt(string), 32); strictEqual(_.parseInt(string, 0), 32); strictEqual(_.parseInt(string, 16), 32); @@ -6837,7 +5952,7 @@ strictEqual(_.parseInt(whitespace + '08'), 8); strictEqual(_.parseInt(whitespace + '08', 10), 8); - _.each(['0x20', '0X20'], function(string) { + _.forEach(['0x20', '0X20'], function(string) { strictEqual(_.parseInt(whitespace + string), 32); strictEqual(_.parseInt(whitespace + string, 16), 32); }); @@ -6854,13 +5969,15 @@ QUnit.module('partial methods'); - _.each(['partial', 'partialRight'], function(methodName) { + _.forEach(['partial', 'partialRight'], function(methodName) { var func = _[methodName], isPartial = methodName == 'partial'; test('`_.' + methodName + '` partially applies arguments', 1, function() { - var par = func(_.identity, 'a'); - strictEqual(par(), 'a'); + var fn = function(a) { return a; }, + par = func(fn, 'a'); + + equal(par(), 'a'); }); test('`_.' + methodName + '` creates a function that can be invoked with additional arguments', 1, function() { @@ -6879,29 +5996,10 @@ }); test('`_.' + methodName + '` works when there are no partially applied arguments and the created function is invoked with additional arguments', 1, function() { - var par = func(_.identity); - strictEqual(par('a'), 'a'); - }); + var fn = function(a) { return a; }, + par = func(fn); - test('`_.' + methodName + '` should support placeholders', 4, function() { - if (!isModularize) { - var fn = function() { return slice.call(arguments); }, - par = func(fn, _, 'b', _); - - deepEqual(par('a', 'c'), ['a', 'b', 'c']); - deepEqual(par('a'), ['a', 'b', undefined]); - deepEqual(par(), [undefined, 'b', undefined]); - - if (isPartial) { - deepEqual(par('a', 'c', 'd'), ['a', 'b', 'c', 'd']); - } else { - par = func(fn, _, 'c', _); - deepEqual(par('a', 'b', 'd'), ['a', 'b', 'c', 'd']); - } - } - else { - skipTest(4); - } + equal(par('a'), 'a'); }); test('`_.' + methodName + '` should not alter the `this` binding', 3, function() { @@ -6929,14 +6027,36 @@ function Foo(value) { return value && object; } - - var object = {}, - par = func(Foo); + var par = func(Foo), + object = {}; ok(new par instanceof Foo); strictEqual(new par(true), object); }); + test('`_.' + methodName + '` should support placeholders', 4, function() { + if (_._iteratorTemplate) { + var fn = function() { + return slice.call(arguments); + }; + + var par = func(fn, _, 'b', _); + deepEqual(par('a', 'c'), ['a', 'b', 'c']); + deepEqual(par('a'), ['a', 'b', undefined]); + deepEqual(par(), [undefined, 'b', undefined]); + + if (isPartial) { + deepEqual(par('a', 'c', 'd'), ['a', 'b', 'c', 'd']); + } else { + par = func(fn, _, 'c', _); + deepEqual(par('a', 'b', 'd'), ['a', 'b', 'c', 'd']); + } + } + else { + skipTest(4); + } + }); + test('`_.' + methodName + '` should clone metadata for created functions', 3, function() { var greet = function(greeting, name) { return greeting + ' ' + name; @@ -6946,17 +6066,17 @@ par2 = func(par1, 'barney'), par3 = func(par1, 'pebbles'); - strictEqual(par1('fred'), isPartial ? 'hi fred' : 'fred hi') - strictEqual(par2(), isPartial ? 'hi barney' : 'barney hi'); - strictEqual(par3(), isPartial ? 'hi pebbles' : 'pebbles hi'); + equal(par1('fred'), isPartial ? 'hi fred' : 'fred hi') + equal(par2(), isPartial ? 'hi barney' : 'barney hi'); + equal(par3(), isPartial ? 'hi pebbles' : 'pebbles hi'); }); test('`_.' + methodName + '` should work with curried methods', 2, function() { var fn = function(a, b, c) { return a + b + c; }, curried = _.curry(func(fn, 1), 2); - strictEqual(curried(2, 3), 6); - strictEqual(curried(2)(3), 6); + equal(curried(2, 3), 6); + equal(curried(2)(3), 6); }); }); @@ -6981,9 +6101,9 @@ (function() { test('combinations of partial functions should work', 1, function() { - function fn() { + var fn = function() { return slice.call(arguments); - } + }; var a = _.partial(fn), b = _.partialRight(a, 3), @@ -6993,11 +6113,11 @@ }); test('combinations of bound and partial functions should work', 3, function() { - function fn() { + var fn = function() { var result = [this.a]; push.apply(result, arguments); return result; - } + }; var expected = [1, 2, 3, 4], object = { 'a': 1, 'fn': fn }; @@ -7022,9 +6142,9 @@ }); test('recursively bound functions should work', 1, function() { - function fn() { + var fn = function() { return this.a; - } + }; var a = _.bind(fn, { 'a': 1 }), b = _.bind(a, { 'a': 2 }), @@ -7042,9 +6162,9 @@ var array = [1, 0, 1]; test('should always return two groups of elements', 3, function() { - deepEqual(_.partition([], _.identity), [[], []]); - deepEqual(_.partition(array, _.constant(true)), [array, []]); - deepEqual(_.partition(array, _.constant(false)), [[], array]); + deepEqual(_.partition([], function(value) { return value; }), [[], []]); + deepEqual(_.partition(array, function(value) { return true; }), [array, []]); + deepEqual(_.partition(array, function(value) { return false; }), [[], array]); }); test('should use `_.identity` when no `callback` is provided', 1, function() { @@ -7187,25 +6307,6 @@ var object = { 'a': [1], 'b': [1, 2], 'c': [1, 2, 3] }; deepEqual(_.pluck(object, 'length'), [1, 2, 3]); }); - - test('should work with nullish elements', 1, function() { - var objects = [{ 'a': 1 }, null, undefined, { 'a': 4 }]; - deepEqual(_.pluck(objects, 'a'), [1, undefined, undefined, 4]); - }); - - test('should coerce `key` to a string', 1, function() { - function fn() {} - fn.toString = _.constant('fn'); - - var objects = [{ 'null': 1 }, { 'undefined': 2 }, { 'fn': 3 }, { '[object Object]': 4 }], - values = [null, undefined, fn, {}] - - var actual = _.map(objects, function(object, index) { - return _.pluck([object], values[index]); - }); - - deepEqual(actual, [[1], [2], [3], [4]]); - }); }()); /*--------------------------------------------------------------------------*/ @@ -7217,10 +6318,10 @@ var object = { 'a': 1, 'b': 2 }, property = _.property('a'); - strictEqual(property.length, 1); + equal(property.length, 1); strictEqual(property(object), 1); - property = _.property('b'); + property = _.property('b'); strictEqual(property(object), 2); }); @@ -7228,7 +6329,7 @@ var array = [1, 2, 3], property = _.property(1); - strictEqual(property(array), 2); + equal(property(array), 2); }); }()); @@ -7251,8 +6352,8 @@ delete array[3]; _.pull(array, 1); - ok(!('0' in array)); - ok(!('2' in array)); + equal(0 in array, false); + equal(2 in array, false); }); test('should treat holes as `undefined`', 1, function() { @@ -7281,7 +6382,7 @@ test('supports not passing a `max` argument', 1, function() { ok(_.some(array, function() { - return _.random(5) !== 5; + return _.random(5) != 5; })); }); @@ -7328,6 +6429,8 @@ QUnit.module('lodash.range'); (function() { + var func = _.range; + test('should work when passing a single `end` argument', 1, function() { deepEqual(_.range(4), [0, 1, 2, 3]); }); @@ -7354,7 +6457,7 @@ }); test('should treat falsey `start` arguments as `0`', 13, function() { - _.each(falsey, function(value, index) { + _.forEach(falsey, function(value, index) { if (index) { deepEqual(_.range(value), []); deepEqual(_.range(value, 1), [0]); @@ -7364,9 +6467,9 @@ }); }); - test('should coerce arguments to finite numbers', 1, function() { - var actual = [_.range('0', 1), _.range('1'), _.range(0, 1, '1'), _.range(NaN), _.range(NaN, NaN)]; - deepEqual(actual, [[0], [0], [0], [], []]); + test('should coerce arguments to numbers', 1, function() { + var actual = [func('0',1), func('1'), func(0, 1, '1')]; + deepEqual(actual, [[0], [0], [0]]); }); }()); @@ -7425,7 +6528,7 @@ deepEqual(args, expected); }); - _.each({ + _.forEach({ 'literal': 'abc', 'object': Object('abc') }, @@ -7439,7 +6542,7 @@ }); deepEqual(args, ['a', 'b', 1, collection]); - strictEqual(actual, 'abc'); + equal(actual, 'abc'); }); }); @@ -7457,7 +6560,7 @@ var array = [1, 2, 3]; test('should use the last element of a collection as the default `accumulator`', 1, function() { - strictEqual(_.reduceRight(array), 3); + equal(_.reduceRight(array), 3); }); test('should pass the correct `callback` arguments when iterating an array', 2, function() { @@ -7504,7 +6607,7 @@ deepEqual(args, expected); }); - _.each({ + _.forEach({ 'literal': 'abc', 'object': Object('abc') }, @@ -7518,7 +6621,7 @@ }); deepEqual(args, ['c', 'b', 1, collection]); - strictEqual(actual, 'cba'); + equal(actual, 'cba'); }); }); @@ -7531,7 +6634,7 @@ QUnit.module('reduce methods'); - _.each(['reduce', 'reduceRight'], function(methodName) { + _.forEach(['reduce', 'reduceRight'], function(methodName) { var array = [1, 2, 3], func = _[methodName]; @@ -7540,7 +6643,7 @@ return accumulator + value; }, ''); - strictEqual(actual, methodName == 'reduce' ? 'abc' : 'cba'); + equal(actual, methodName == 'reduce' ? 'abc' : 'cba'); }); test('`_.' + methodName + '` should support the `thisArg` argument', 1, function() { @@ -7557,7 +6660,7 @@ return sum + num; }); - strictEqual(actual, 6); + equal(actual, 6); } else { skipTest(); @@ -7566,11 +6669,11 @@ test('`_.' + methodName + '` should support empty or falsey collections without an initial `accumulator` value', 1, function() { var actual = [], - expected = _.map(empties, _.constant()); + expected = _.map(empties, function() { return undefined; }); - _.each(empties, function(value) { + _.forEach(empties, function(value) { try { - actual.push(func(value, _.noop)); + actual.push(func(value, noop)); } catch(e) { } }); @@ -7578,11 +6681,11 @@ }); test('`_.' + methodName + '` should support empty or falsey collections with an initial `accumulator` value', 1, function() { - var expected = _.map(empties, _.constant('x')); + var expected = _.map(empties, function() { return 'x'; }); var actual = _.map(empties, function(value) { try { - return func(value, _.noop, 'x'); + return func(value, noop, 'x'); } catch(e) { } }); @@ -7590,7 +6693,7 @@ }); test('`_.' + methodName + '` should handle an initial `accumulator` value of `undefined`', 1, function() { - var actual = func([], _.noop, undefined); + var actual = func([], noop, undefined); strictEqual(actual, undefined); }); @@ -7600,12 +6703,12 @@ if ('__proto__' in array) { array.__proto__ = object; - strictEqual(_.reduce(array, _.noop), undefined); + strictEqual(_.reduce(array, noop), undefined); } else { skipTest(); } - strictEqual(_.reduce(object, _.noop), undefined); + strictEqual(_.reduce(object, noop), undefined); }); }); @@ -7627,7 +6730,7 @@ QUnit.module('filter methods'); - _.each(['filter', 'reject'], function(methodNames) { + _.forEach(['filter', 'reject'], function(methodNames) { var func = _[methodNames]; test('`_.' + methodNames + '` should not modify the resulting value from within `callback`', 1, function() { @@ -7683,8 +6786,8 @@ delete array[3]; _.remove(array, function(num) { return num === 1; }); - ok(!('0' in array)); - ok(!('2' in array)); + equal(0 in array, false); + equal(2 in array, false); }); test('should treat holes as `undefined`', 1, function() { @@ -7698,28 +6801,55 @@ /*--------------------------------------------------------------------------*/ - QUnit.module('lodash.repeat'); + QUnit.module('lodash.removeAt'); (function() { - test('should repeat a string `n` times', 2, function() { - strictEqual(_.repeat('*', 3), '***'); - strictEqual(_.repeat('abc', 2), 'abcabc'); + test('should modify the array and return removed elements', 2, function() { + var array = [1, 2, 3]; + var actual = _.removeAt(array, [0, 1]); + + deepEqual(array, [3]); + deepEqual(actual, [1, 2]); }); - test('should return an empty string for negative `n` or `n` of `0`', 2, function() { - strictEqual(_.repeat('abc', 0), ''); - strictEqual(_.repeat('abc', -2), ''); + test('should work with unsorted indexes', 2, function() { + var array = [1, 2, 3, 4, 5]; + var actual = _.removeAt(array, [4, 1, 0, 3]); + + deepEqual(array, [3]); + deepEqual(actual, [1, 2, 4, 5]); }); - test('should coerce `n` to a number', 3, function() { - strictEqual(_.repeat('abc'), ''); - strictEqual(_.repeat('abc', '2'), 'abcabc'); - strictEqual(_.repeat('*', { 'valueOf': _.constant(3) }), '***'); + test('should return `undefined` for nonexistent keys', 2, function() { + var array = ['a', 'b', 'c']; + var actual = _.removeAt(array, [0, 2, 4]); + + deepEqual(array, ['b']); + deepEqual(actual, ['a', 'c', undefined]); }); - test('should coerce `string` to a string', 2, function() { - strictEqual(_.repeat(Object('abc'), 2), 'abcabc'); - strictEqual(_.repeat({ 'toString': _.constant('*') }, 3), '***'); + test('should return an empty array when no keys are provided', 2, function() { + var array = ['a', 'b', 'c']; + var actual = _.removeAt(array); + + deepEqual(array, ['a', 'b', 'c']); + deepEqual(actual, []); + }); + + test('should accept multiple index arguments', 2, function() { + var array = ['a', 'b', 'c', 'd']; + var actual = _.removeAt(array, 0, 2, 3); + + deepEqual(array, ['b']); + deepEqual(actual, ['a', 'c', 'd']); + }); + + test('should work when used as a callback for `_.map`', 2, function() { + var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; + var actual = _.map(array, _.removeAt); + + deepEqual(array, [[2, 3], [4, 6], [7, 8]]); + deepEqual(actual, [[1], [5], [9]]); }); }()); @@ -7741,13 +6871,13 @@ strictEqual(_.result(object, 'd'), undefined); }); - test('should return `undefined` when `object` is nullish', 2, function() { + test('should return `undefined` when `object` is `null` or `undefined`', 2, function() { strictEqual(_.result(null, 'a'), undefined); strictEqual(_.result(undefined, 'a'), undefined); }); test('should return the specified default value for undefined properties', 1, function() { - var values = falsey.concat(1, _.constant(1)); + var values = falsey.concat(1, function() { return 1; }); var expected = _.transform(values, function(result, value) { result.push(value, value); @@ -7778,7 +6908,7 @@ ]; test('should accept a falsey `array` argument', 1, function() { - var expected = _.map(falsey, _.constant([])); + var expected = _.map(falsey, function() { return []; }); var actual = _.map(falsey, function(value, index) { try { @@ -7797,26 +6927,14 @@ deepEqual(_.rest(array, 2), [3]); }); - test('should treat falsey `n` values, except nullish, as `0`', 1, function() { - var expected = _.map(falsey, function(value) { - return value == null ? [2, 3] : array; - }); - - var actual = _.map(falsey, function(n) { - return _.rest(array, n); - }); - - deepEqual(actual, expected); - }); - test('should return all elements when `n` < `1`', 3, function() { - _.each([0, -1, -Infinity], function(n) { - deepEqual(_.rest(array, n), array); + _.forEach([0, -1, -2], function(n) { + deepEqual(_.rest(array, n), [1, 2, 3]); }); }); - test('should return an empty array when `n` >= `array.length`', 4, function() { - _.each([3, 4, Math.pow(2, 32), Infinity], function(n) { + test('should return an empty array when `n` >= `array.length`', 2, function() { + _.forEach([3, 4], function(n) { deepEqual(_.rest(array, n), []); }); }); @@ -7850,7 +6968,7 @@ deepEqual(args, [1, 0, array]); }); - test('should support the `thisArg` argument', 1, function() { + test('supports the `thisArg` argument', 1, function() { var actual = _.rest(array, function(num, index) { return this[index] < 3; }, array); @@ -7917,26 +7035,14 @@ deepEqual(actual.sort(), array); }); - test('should treat falsey `n` values, except nullish, as `0`', 1, function() { - var expected = _.map(falsey, function(value) { - return value == null ? 1 : []; - }); - - var actual = _.map(falsey, function(n) { - return _.sample([1], n); - }); - - deepEqual(actual, expected); - }); - - test('should return an empty array when `n` < `1` or `NaN`', 3, function() { - _.each([0, -1, -Infinity], function(n) { + test('should return an empty array when `n` < `1`', 3, function() { + _.forEach([0, -1, -2], function(n) { deepEqual(_.sample(array, n), []); }); }); - test('should return all elements when `n` >= `array.length`', 4, function() { - _.each([3, 4, Math.pow(2, 32), Infinity], function(n) { + test('should return all elements when `n` >= `array.length`', 2, function() { + _.forEach([3, 4], function(n) { deepEqual(_.sample(array, n).sort(), array); }); }); @@ -7952,7 +7058,7 @@ result.push([], []); }); - _.each(empties, function(value) { + _.forEach(empties, function(value) { try { actual.push(_.shuffle(value), _.shuffle(value, 1)); } catch(e) { } @@ -8000,7 +7106,7 @@ } }); - _.each({ + _.forEach({ 'literal': 'abc', 'object': Object('abc') }, @@ -8037,11 +7143,7 @@ deepEqual(actual.sort(), array); }); - test('should treat number values for `collection` as empty', 1, function() { - deepEqual(_.shuffle(1), []); - }); - - _.each({ + _.forEach({ 'literal': 'abc', 'object': Object('abc') }, @@ -8062,15 +7164,15 @@ array = [1, 2, 3]; test('should return the number of own enumerable properties of an object', 1, function() { - strictEqual(_.size({ 'one': 1, 'two': 2, 'three': 3 }), 3); + equal(_.size({ 'one': 1, 'two': 2, 'three': 3 }), 3); }); test('should return the length of an array', 1, function() { - strictEqual(_.size(array), 3); + equal(_.size(array), 3); }); test('should accept a falsey `object` argument', 1, function() { - var expected = _.map(falsey, _.constant(0)); + var expected = _.map(falsey, function() { return 0; }); var actual = _.map(falsey, function(value, index) { try { @@ -8081,34 +7183,26 @@ deepEqual(actual, expected); }); - test('should work with `arguments` objects (test in IE < 9)', 1, function() { - strictEqual(_.size(args), 3); - }); - test('should work with jQuery/MooTools DOM query collections', 1, function() { function Foo(elements) { push.apply(this, elements); } Foo.prototype = { 'length': 0, 'splice': Array.prototype.splice }; - strictEqual(_.size(new Foo(array)), 3); + equal(_.size(new Foo(array)), 3); }); - test('should not treat objects with negative lengths as array-like', 1, function() { - strictEqual(_.size({ 'length': -1 }), 1); + test('should work with `arguments` objects (test in IE < 9)', 1, function() { + if (!isPhantomPage) { + equal(_.size(args), 3); + } else { + skipTest(); + } }); - test('should not treat objects with lengths larger than `maxSafeInteger` as array-like', 1, function() { - strictEqual(_.size({ 'length': maxSafeInteger + 1 }), 1); + test('fixes the JScript [[DontEnum]] bug (test in IE < 9)', 1, function() { + equal(_.size(shadowedObject), 7); }); - test('should not treat objects with non-number lengths as array-like', 1, function() { - strictEqual(_.size({ 'length': '0' }), 1); - }); - - test('fixes the JScript `[[DontEnum]]` bug (test in IE < 9)', 1, function() { - strictEqual(_.size(shadowedObject), 7); - }); - - _.each({ + _.forEach({ 'literal': 'abc', 'object': Object('abc') }, @@ -8130,28 +7224,18 @@ deepEqual(_.slice(array, 1), [2, 3]); }); - test('should work with a `start` >= `array.length`', 4, function() { - _.each([3, 4, Math.pow(2, 32), Infinity], function(start) { + test('should work with a `start` >= `array.length`', 2, function() { + _.forEach([3, 4], function(start) { deepEqual(_.slice(array, start), []); }); }); - test('should treat falsey `start` values as `0`', 1, function() { - var expected = _.map(falsey, _.constant(array)); - - var actual = _.map(falsey, function(start) { - return _.slice(array, start); - }); - - deepEqual(actual, expected); - }); - test('should work with a negative `start`', 1, function() { deepEqual(_.slice(array, -1), [3]); }); - test('should work with a negative `start` <= negative `array.length`', 3, function() { - _.each([-3, -4, -Infinity], function(start) { + test('should work with a negative `start` <= negative `array.length`', 2, function() { + _.forEach([-3, -4], function(start) { deepEqual(_.slice(array, start), [1, 2, 3]); }); }); @@ -8160,38 +7244,21 @@ deepEqual(_.slice(array, 0, 1), [1]); }); - test('should work with a `end` >= `array.length`', 4, function() { - _.each([3, 4, Math.pow(2, 32), Infinity], function(end) { + test('should work with a `end` >= `array.length`', 2, function() { + _.forEach([3, 4], function(end) { deepEqual(_.slice(array, 0, end), [1, 2, 3]); }); }); - test('should treat falsey `end` values, except `undefined`, as `0`', 1, function() { - var expected = _.map(falsey, function(value) { - return value === undefined ? array : []; - }); - - var actual = _.map(falsey, function(end) { - return _.slice(array, 0, end); - }); - - deepEqual(actual, expected); - }); - test('should work with a negative `end`', 1, function() { deepEqual(_.slice(array, 0, -1), [1, 2]); }); - test('should work with a negative `end` <= negative `array.length`', 3, function() { - _.each([-3, -4, -Infinity], function(end) { + test('should work with a negative `end` <= negative `array.length`', 2, function() { + _.forEach([-3, -4], function(end) { deepEqual(_.slice(array, 0, end), []); }); }); - - test('should coerce `start` and `end` to finite numbers', 1, function() { - var actual = [_.slice(array, '0', 1), _.slice(array, 0, '1'), _.slice(array, '1'), _.slice(array, NaN, 1), _.slice(array, 1, NaN)]; - deepEqual(actual, [[1], [1], [2, 3], [1], []]); - }); }()); /*--------------------------------------------------------------------------*/ @@ -8200,7 +7267,7 @@ (function() { test('should return `false` for empty or falsey collections', 1, function() { - var expected = _.map(empties, _.constant(false)); + var expected = _.map(empties, function() { return false; }); var actual = _.map(empties, function(value) { try { @@ -8211,7 +7278,7 @@ deepEqual(actual, expected); }); - test('should return `true` if the callback returns truthy for any element in the collection', 2, function() { + test('should return `true` if the callback returns truey for any element in the collection', 2, function() { strictEqual(_.some([false, 1, ''], _.identity), true); strictEqual(_.some([null, 'x', 0], _.identity), true); }); @@ -8221,7 +7288,7 @@ strictEqual(_.some([null, 0, ''], _.identity), false); }); - test('should return `true` as soon as the `callback` result is truthy', 1, function() { + test('should return `true` as soon as the `callback` result is truey', 1, function() { strictEqual(_.some([null, true, null], _.identity), true); }); @@ -8323,10 +7390,6 @@ deepEqual(actual, [3, 1, 2]); }); - test('should treat number values for `collection` as empty', 1, function() { - deepEqual(_.sortBy(1), []); - }); - test('should support sorting by an array of properties', 1, function() { var actual = _.sortBy(objects, ['a', 'b']); deepEqual(actual, [objects[2], objects[0], objects[3], objects[1]]); @@ -8357,8 +7420,8 @@ objects = [{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }]; test('should return the insert index of a given value', 2, function() { - strictEqual(_.sortedIndex(array, 40), 2); - strictEqual(_.sortedIndex(array, 30), 1); + equal(_.sortedIndex(array, 40), 2); + equal(_.sortedIndex(array, 30), 1); }); test('should pass the correct `callback` arguments', 1, function() { @@ -8381,7 +7444,7 @@ test('should work with a string for `callback`', 1, function() { var actual = _.sortedIndex(objects, { 'x': 40 }, 'x'); - strictEqual(actual, 2); + equal(actual, 2); }); test('supports arrays with lengths larger than `Math.pow(2, 31) - 1`', 1, function() { @@ -8393,7 +7456,7 @@ if (array.length == length) { array[index] = index; _.sortedIndex(array, index, function() { steps++; }); - strictEqual(steps, 33); + equal(steps, 33); } else { skipTest(); @@ -8407,9 +7470,7 @@ (function() { test('should contain properties with boolean values', 1, function() { - ok(_.every(_.values(_.support), function(value) { - return value === true || value === false; - })); + ok(_.every(_.values(_.support), _.isBoolean)); }); test('should not contain minified properties (test production builds)', 1, function() { @@ -8425,94 +7486,17 @@ 'nodeClass', 'nonEnumArgs', 'nonEnumShadows', - 'nonEnumStrings', 'ownLast', 'spliceObjects', 'unindexedChars' ]; - ok(_.isEmpty(_.difference(_.keys(_.support), props))); + ok(!_.size(_.difference(_.keys(_.support), props))); }); }()); /*--------------------------------------------------------------------------*/ - QUnit.module('lodash.startsWith'); - - (function() { - var string = 'abc'; - - test('should return `true` if a string starts with `target`', 1, function() { - strictEqual(_.startsWith(string, 'a'), true); - }); - - test('should return `false` if a string does not start with `target`', 1, function() { - strictEqual(_.startsWith(string, 'b'), false); - }); - - test('should work with a `position` argument', 1, function() { - strictEqual(_.startsWith(string, 'b', 1), true); - }); - - test('should work with `position` >= `string.length`', 4, function() { - _.each([3, 5, maxSafeInteger, Infinity], function(position) { - strictEqual(_.startsWith(string, 'a', position), false); - }); - }); - - test('should treat falsey `position` values as `0`', 1, function() { - var expected = _.map(falsey, _.constant(true)); - - var actual = _.map(falsey, function(position) { - return _.startsWith(string, 'a', position); - }); - - deepEqual(actual, expected); - }); - - test('should treat a negative `position` as `0`', 6, function() { - _.each([-1, -3, -Infinity], function(position) { - strictEqual(_.startsWith(string, 'a', position), true); - strictEqual(_.startsWith(string, 'b', position), false); - }); - }); - - test('should always return `true` when `target` is an empty string regardless of `position`', 1, function() { - ok(_.every([-Infinity, NaN, -3, -1, 0, 1, 2, 3, 5, maxSafeInteger, Infinity], function(position) { - return _.startsWith(string, '', position, true); - })); - }); - }()); - - /*--------------------------------------------------------------------------*/ - - QUnit.module('lodash.startsWith and lodash.endsWith'); - - _.each(['startsWith', 'endsWith'], function(methodName) { - var func = _[methodName], - isEndsWith = methodName == 'endsWith', - chr = isEndsWith ? 'c' : 'a', - string = 'abc'; - - test('`_.' + methodName + '` should coerce `string` to a string', 2, function() { - strictEqual(func(Object(string), chr), true); - strictEqual(func({ 'toString': _.constant(string) }, chr), true); - }); - - test('`_.' + methodName + '` should coerce `target` to a string', 2, function() { - strictEqual(func(string, Object(chr)), true); - strictEqual(func(string, { 'toString': _.constant(chr) }), true); - }); - - test('`_.' + methodName + '` should coerce `position` to a number', 2, function() { - var position = isEndsWith ? 2 : 1; - strictEqual(func(string, 'b', Object(position)), true); - strictEqual(func(string, 'b', { 'toString': _.constant(String(position)) }), true); - }); - }); - - /*--------------------------------------------------------------------------*/ - QUnit.module('lodash.tap'); (function() { @@ -8577,7 +7561,7 @@ unescaped = '&<>"\'\/'; var compiled = _.template('

    <%- value %>

    '); - strictEqual(compiled({ 'value': unescaped }), escaped); + equal(compiled({ 'value': unescaped }), escaped); }); test('should evaluate JavaScript in "evaluate" delimiters', 1, function() { @@ -8589,24 +7573,24 @@ ); var actual = compiled({ 'collection': { 'a': 'A', 'b': 'B' } }); - strictEqual(actual, '
    • A
    • B
    '); + equal(actual, '
    • A
    • B
    '); }); test('should interpolate data object properties', 1, function() { var compiled = _.template('<%= a %>BC'); - strictEqual(compiled({ 'a': 'A' }), 'ABC'); + equal(compiled({ 'a': 'A' }), 'ABC'); }); test('should support escaped values in "interpolation" delimiters', 1, function() { var compiled = _.template('<%= a ? "a=\\"A\\"" : "" %>'); - strictEqual(compiled({ 'a': true }), 'a="A"'); + equal(compiled({ 'a': true }), 'a="A"'); }); test('should work with "interpolate" delimiters containing ternary operators', 1, function() { var compiled = _.template('<%= value ? value : "b" %>'), data = { 'value': 'a' }; - strictEqual(compiled(data), 'a'); + equal(compiled(data), 'a'); }); test('should work with "interpolate" delimiters containing global values', 1, function() { @@ -8616,11 +7600,11 @@ var actual = compiled(); } catch(e) { } - strictEqual(actual, 'function'); + equal(actual, 'function'); }); test('should work with complex "interpolate" delimiters', 22, function() { - _.each({ + _.forEach({ '<%= a + b %>': '3', '<%= b - a %>': '1', '<%= a = b %>': '2', @@ -8648,19 +7632,19 @@ var compiled = _.template(key), data = { 'a': 1, 'b': 2 }; - strictEqual(compiled(data), value, key); + equal(compiled(data), value, key); }); }); test('should parse ES6 template delimiters', 2, function() { var data = { 'value': 2 }; strictEqual(_.template('1${value}3', data), '123'); - strictEqual(_.template('${"{" + value + "\\}"}', data), '{2}'); + equal(_.template('${"{" + value + "\\}"}', data), '{2}'); }); test('should not reference `_.escape` when "escape" delimiters are not used', 1, function() { var compiled = _.template('<%= typeof __e %>'); - strictEqual(compiled({}), 'undefined'); + equal(compiled({}), 'undefined'); }); test('should allow referencing variables declared in "evaluate" delimiters from other delimiters', 1, function() { @@ -8672,7 +7656,7 @@ test('should support single line comments in "evaluate" delimiters (test production builds)', 1, function() { var compiled = _.template('<% // comment %><% if (value) { %>yap<% } else { %>nope<% } %>'); - strictEqual(compiled({ 'value': true }), 'yap'); + equal(compiled({ 'value': true }), 'yap'); }); test('should work with custom `_.templateSettings` delimiters', 1, function() { @@ -8684,10 +7668,10 @@ 'interpolate': /\{\{=([\s\S]+?)\}\}/g }); - var compiled = _.template('
      {{ _.each(collection, function(value, index) { }}
    • {{= index }}: {{- value }}
    • {{ }); }}
    '), + var compiled = _.template('
      {{ _.forEach(collection, function(value, index) { }}
    • {{= index }}: {{- value }}
    • {{ }); }}
    '), expected = '
    • 0: a & A
    • 1: b & B
    '; - strictEqual(compiled({ 'collection': ['a & A', 'b & B'] }), expected); + equal(compiled({ 'collection': ['a & A', 'b & B'] }), expected); _.assign(_.templateSettings, settings); }); @@ -8700,16 +7684,16 @@ 'interpolate': /<\?=([\s\S]+?)\?>/g }); - var compiled = _.template('
    • :
    '), + var compiled = _.template('
    • :
    '), expected = '
    • 0: a & A
    • 1: b & B
    '; - strictEqual(compiled({ 'collection': ['a & A', 'b & B'] }), expected); + equal(compiled({ 'collection': ['a & A', 'b & B'] }), expected); _.assign(_.templateSettings, settings); }); test('should work with no delimiters', 1, function() { var expected = 'abc'; - strictEqual(_.template(expected, {}), expected); + equal(_.template(expected, {}), expected); }); test('should support the "imports" option', 1, function() { @@ -8721,7 +7705,7 @@ test('should support the "variable" options', 1, function() { var compiled = _.template( - '<% _.each( data.a, function( value ) { %>' + + '<% _.forEach( data.a, function( value ) { %>' + '<%= value.valueOf() %>' + '<% }) %>', null, { 'variable': 'data' } ); @@ -8735,32 +7719,32 @@ }); test('should use a `with` statement by default', 1, function() { - var compiled = _.template('<%= index %><%= collection[index] %><% _.each(collection, function(value, index) { %><%= index %><% }); %>'), + var compiled = _.template('<%= index %><%= collection[index] %><% _.forEach(collection, function(value, index) { %><%= index %><% }); %>'), actual = compiled({ 'index': 1, 'collection': ['a', 'b', 'c'] }); - strictEqual(actual, '1b012'); + equal(actual, '1b012'); }); test('should work correctly with `this` references', 2, function() { var compiled = _.template('a<%= this.String("b") %>c'); - strictEqual(compiled(), 'abc'); + equal(compiled(), 'abc'); var object = { 'b': 'B' }; object.compiled = _.template('A<%= this.b %>C', null, { 'variable': 'obj' }); - strictEqual(object.compiled(), 'ABC'); + equal(object.compiled(), 'ABC'); }); test('should work with backslashes', 1, function() { var compiled = _.template('<%= a %> \\b'); - strictEqual(compiled({ 'a': 'A' }), 'A \\b'); + equal(compiled({ 'a': 'A' }), 'A \\b'); }); test('should work with escaped characters in string literals', 2, function() { var compiled = _.template('<% print("\'\\n\\r\\t\\u2028\\u2029\\\\") %>'); - strictEqual(compiled(), "'\n\r\t\u2028\u2029\\"); + equal(compiled(), "'\n\r\t\u2028\u2029\\"); compiled = _.template('\'\n\r\t<%= a %>\u2028\u2029\\"'); - strictEqual(compiled({ 'a': 'A' }), '\'\n\r\tA\u2028\u2029\\"'); + equal(compiled({ 'a': 'A' }), '\'\n\r\tA\u2028\u2029\\"'); }); test('should handle \\u2028 & \\u2029 characters', 1, function() { @@ -8775,7 +7759,7 @@ } %>" ); - strictEqual(compiled({ 'a': 'A' }), "'a',\"A\""); + equal(compiled({ 'a': 'A' }), "'a',\"A\""); }); test('should work with templates containing newlines and comments', 1, function() { @@ -8785,7 +7769,7 @@ %>

    <%= value %>

    ' ); - strictEqual(compiled({ 'value': 3 }), '

    6

    '); + equal(compiled({ 'value': 3 }), '

    6

    '); }); test('should not error with IE conditional comments enabled (test with development build)', 1, function() { @@ -8805,7 +7789,7 @@ var compiled = _.template(''), data = { 'type': 1 }; - strictEqual(compiled(data), ''); + equal(compiled(data), ''); }); test('should evaluate delimiters once', 1, function() { @@ -8818,10 +7802,10 @@ test('should match delimiters before escaping text', 1, function() { var compiled = _.template('<<\n a \n>>', null, { 'evaluate': /<<(.*?)>>/g }); - strictEqual(compiled(), '<<\n a \n>>'); + equal(compiled(), '<<\n a \n>>'); }); - test('should resolve `null` and `undefined` values to an empty string', 4, function() { + test('should resolve `null` and `undefined` values to empty strings', 4, function() { var compiled = _.template('<%= a %><%- a %>'); strictEqual(compiled({ 'a': null }), ''); strictEqual(compiled({ 'a': undefined }), ''); @@ -8836,14 +7820,14 @@ compiled = _.template(expected, null, { 'evaluate': /<<(.+?)>>/g }), data = { 'value': true }; - strictEqual(compiled(data), expected); + equal(compiled(data), expected); }); test('should support recursive calls', 1, function() { var compiled = _.template('<%= a %><% a = _.template(c, obj) %><%= a %>'), data = { 'a': 'A', 'b': 'B', 'c': '<%= b %>' }; - strictEqual(compiled(data), 'AB'); + equal(compiled(data), 'AB'); }); test('should coerce `text` argument to a string', 1, function() { @@ -8860,10 +7844,10 @@ }); test('should not modify `_.templateSettings` when `options` are provided', 2, function() { - ok(!('a' in _.templateSettings)); + equal('a' in _.templateSettings, false); _.template('', {}, { 'a': 1 }); - ok(!('a' in _.templateSettings)); + equal('a' in _.templateSettings, false); delete _.templateSettings.a; }); @@ -8900,60 +7884,6 @@ /*--------------------------------------------------------------------------*/ - QUnit.module('lodash.truncate'); - - (function() { - var string = 'hi-diddly-ho there, neighborino'; - - test('should truncate to a length of `30` by default', 1, function() { - strictEqual(_.truncate(string), 'hi-diddly-ho there, neighbo...'); - }); - - test('should not truncate if `string` is <= `length`', 2, function() { - strictEqual(_.truncate(string, string.length), string); - strictEqual(_.truncate(string, string.length + 2), string); - }); - - test('should truncate string the given length', 1, function() { - strictEqual(_.truncate(string, 24), 'hi-diddly-ho there, n...'); - }); - - test('should support a `omission` option', 1, function() { - strictEqual(_.truncate(string, { 'omission': ' [...]' }), 'hi-diddly-ho there, neig [...]'); - }); - - test('should support a `length` option', 1, function() { - strictEqual(_.truncate(string, { 'length': 4 }), 'h...'); - }); - - test('should support a `separator` option', 2, function() { - strictEqual(_.truncate(string, { 'length': 24, 'separator': ' ' }), 'hi-diddly-ho there,...'); - strictEqual(_.truncate(string, { 'length': 24, 'separator': /,? +/ }), 'hi-diddly-ho there...'); - }); - - test('should treat negative `length` as `0`', 4, function() { - _.each([0, -2], function(length) { - strictEqual(_.truncate(string, length), '...'); - strictEqual(_.truncate(string, { 'length': length }), '...'); - }); - }); - - test('should coerce `length` to a number', 4, function() { - _.each(['', '4'], function(length, index) { - var actual = index ? 'h...' : '...'; - strictEqual(_.truncate(string, length), actual); - strictEqual(_.truncate(string, { 'length': { 'valueOf': _.constant(length) } }), actual); - }); - }); - - test('should coerce `string` to a string', 2, function() { - strictEqual(_.truncate(Object(string), 4), 'h...'); - strictEqual(_.truncate({ 'toString': _.constant(string) }, 5), 'hi...'); - }); - }()); - - /*--------------------------------------------------------------------------*/ - QUnit.module('lodash.throttle'); (function() { @@ -8982,7 +7912,7 @@ asyncTest('subsequent calls should return the result of the first call', 5, function() { if (!(isRhino && isModularize)) { - var throttled = _.throttle(_.identity, 32), + var throttled = _.throttle(function(value) { return value; }, 32), result = [throttled('a'), throttled('b')]; deepEqual(result, ['a', 'a']); @@ -9027,7 +7957,7 @@ throttled(); setTimeout(function() { - strictEqual(callCount, 2); + equal(callCount, 2); QUnit.start(); }, 64); } @@ -9053,7 +7983,7 @@ }, 32); throttled.call(object, 'a'); - strictEqual(count, 1); + equal(count, 1); setTimeout(function() { ok(count < 3); @@ -9073,10 +8003,10 @@ throttled = _.throttle(function() { count++; }, 32); throttled(); - strictEqual(count, 1); + equal(count, 1); setTimeout(function() { - strictEqual(count, 1); + equal(count, 1); QUnit.start(); }, 64); } @@ -9118,8 +8048,8 @@ return value; }, 32, {}); - strictEqual(throttled('a'), 'a'); - strictEqual(throttled('b'), 'a'); + equal(throttled('a'), 'a'); + equal(throttled('b'), 'a'); setTimeout(function() { strictEqual(count, 2); @@ -9132,14 +8062,14 @@ } }); - test('should support a `leading` option', 4, function() { + test('should work with `leading` option', 4, function() { if (!(isRhino && isModularize)) { - _.each([true, { 'leading': true }], function(options) { + _.forEach([true, { 'leading': true }], function(options) { var withLeading = _.throttle(_.identity, 32, options); - strictEqual(withLeading('a'), 'a'); + equal(withLeading('a'), 'a'); }); - _.each([false, { 'leading': false }], function(options) { + _.forEach([false, { 'leading': false }], function(options) { var withoutLeading = _.throttle(_.identity, 32, options); strictEqual(withoutLeading('a'), undefined); }); @@ -9149,7 +8079,7 @@ } }); - asyncTest('should support a `trailing` option', 6, function() { + asyncTest('should work with `trailing` option', 6, function() { if (!(isRhino && isModularize)) { var withCount = 0, withoutCount = 0; @@ -9164,14 +8094,14 @@ return value; }, 64, { 'trailing': false }); - strictEqual(withTrailing('a'), 'a'); - strictEqual(withTrailing('b'), 'a'); + equal(withTrailing('a'), 'a'); + equal(withTrailing('b'), 'a'); - strictEqual(withoutTrailing('a'), 'a'); - strictEqual(withoutTrailing('b'), 'a'); + equal(withoutTrailing('a'), 'a'); + equal(withoutTrailing('b'), 'a'); setTimeout(function() { - strictEqual(withCount, 2); + equal(withCount, 2); strictEqual(withoutCount, 1); QUnit.start(); }, 256); @@ -9214,15 +8144,14 @@ QUnit.module('lodash.debounce and lodash.throttle'); - _.each(['debounce', 'throttle'], function(methodName) { - var func = _[methodName], - isThrottle = methodName == 'throttle'; + _.forEach(['debounce', 'throttle'], function(methodName) { + var func = _[methodName]; test('_.' + methodName + ' should not error for non-object `options` values', 1, function() { var pass = true; try { - func(_.noop, 32, 1); + func(noop, 32, 1); } catch(e) { pass = false; } @@ -9238,11 +8167,11 @@ }; object.funced(); - if (isThrottle) { + if (methodName == 'throttle') { object.funced(); } setTimeout(function() { - deepEqual(actual, isThrottle ? [object, object] : [object]); + deepEqual(actual, methodName == 'throttle' ? [object, object] : [object]); QUnit.start(); }, 64); } @@ -9275,7 +8204,7 @@ setTimeout(function() { funced(); - strictEqual(callCount, isThrottle ? 2 : 1); + equal(callCount, methodName == 'throttle' ? 2 : 1); QUnit.start(); }, 64); } @@ -9308,7 +8237,7 @@ QUnit.module('lodash.slice and lodash.toArray'); - _.each(['slice', 'toArray'], function(methodName) { + _.forEach(['slice', 'toArray'], function(methodName) { var args = (function() { return arguments; }(1, 2, 3)), array = [1, 2, 3], func = _[methodName]; @@ -9319,8 +8248,8 @@ var actual = func(sparse); - ok('0' in actual); - ok('2' in actual); + ok(0 in actual); + ok(2 in actual); deepEqual(actual, sparse); }); @@ -9354,17 +8283,6 @@ QUnit.module('lodash.times'); (function() { - test('should rollover large `n` values', 1, function() { - var actual = _.times(Math.pow(2, 32) + 1); - deepEqual(actual, [0]); - }); - - test('should coerce non-finite `n` values to `0`', 3, function() { - _.each([-Infinity, NaN, Infinity], function(n) { - deepEqual(_.times(n), []); - }); - }); - test('should pass the correct `callback` arguments', 1, function() { var args; @@ -9396,7 +8314,7 @@ test('should return an empty array for falsey and negative `n` arguments', 1, function() { var values = falsey.concat(-1, -Infinity), - expected = _.map(values, _.constant([])); + expected = _.map(values, function() { return []; }); var actual = _.map(values, function(value, index) { return index ? _.times(value) : _.times(); @@ -9450,11 +8368,7 @@ ok(_.transform(new Foo) instanceof Foo); }); - test('should check that `object` is an object before using it as the `accumulator` `[[Prototype]]', 1, function() { - ok(!(_.transform(1) instanceof Number)); - }); - - _.each({ + _.forEach({ 'array': [1, 2, 3], 'object': { 'a': 1, 'b': 2, 'c': 3 } }, @@ -9468,10 +8382,10 @@ var first = args[0]; if (key == 'array') { - ok(first !== object && _.isArray(first)); + ok(first != object && _.isArray(first)); deepEqual(args, [first, 1, 0, object]); } else { - ok(first !== object && _.isPlainObject(first)); + ok(first != object && _.isPlainObject(first)); deepEqual(args, [first, 1, 'a', object]); } }); @@ -9491,7 +8405,7 @@ QUnit.module('trim methods'); - _.each(['trim', 'trimLeft', 'trimRight'], function(methodName, index) { + _.forEach(['trim', 'trimLeft', 'trimRight'], function(methodName, index) { var func = _[methodName]; var parts = []; @@ -9508,13 +8422,6 @@ strictEqual(func(string), (index == 2 ? whitespace : '') + 'a b c' + (index == 1 ? whitespace : '')); }); - test('`_.' + methodName + '` should not remove non-whitespace characters', 1, function() { - var problemChars = '\x85\u200b\ufffe', - string = problemChars + 'a b c' + problemChars; - - strictEqual(func(string), string); - }); - test('`_.' + methodName + '` should coerce `string` to a string', 1, function() { var object = { 'toString': function() { return whitespace + 'a b c' + whitespace; } }; strictEqual(func(object), (index == 2 ? whitespace : '') + 'a b c' + (index == 1 ? whitespace : '')); @@ -9532,22 +8439,13 @@ strictEqual(func(string, object), (index == 2 ? '-_-' : '') + 'a-b-c' + (index == 1 ? '-_-' : '')); }); - test('`_.' + methodName + '` should return an empty string when provided `null`, `undefined`, or empty string and `chars`', 6, function() { - _.each([null, '_-'], function(chars) { - strictEqual(func(null, chars), ''); - strictEqual(func(undefined, chars), ''); - strictEqual(func('', chars), ''); + test('`_.' + methodName + '` should return an empty string when provided `null`, `undefined`, or empty strings', 6, function() { + _.forEach([null, '_-'], function(arg) { + strictEqual(func.call(_, null, arg), ''); + strictEqual(func.call(_, undefined, arg), ''); + strictEqual(func.call(_, '', arg), ''); }); }); - - test('`_.' + methodName + '` should work with `null`, `undefined`, or empty string for `chars`', 3, function() { - var string = whitespace + 'a b c' + whitespace, - expected = (index == 2 ? whitespace : '') + 'a b c' + (index == 1 ? whitespace : ''); - - strictEqual(func(string, null), expected); - strictEqual(func(string, undefined), expected); - strictEqual(func(string, ''), string); - }); }); /*--------------------------------------------------------------------------*/ @@ -9559,23 +8457,29 @@ unescaped = '&<>"\'\/'; test('should unescape entities in the correct order', 1, function() { - strictEqual(_.unescape('&lt;'), '<'); + equal(_.unescape('&lt;'), '<'); }); test('should unescape the proper entities', 1, function() { - strictEqual(_.unescape(escaped), unescaped); + equal(_.unescape(escaped), unescaped); }); test('should not unescape the "/" entity', 1, function() { - strictEqual(_.unescape('/'), '/'); + equal(_.unescape('/'), '/'); }); test('should handle strings with nothing to unescape', 1, function() { - strictEqual(_.unescape('abc'), 'abc'); + equal(_.unescape('abc'), 'abc'); }); test('should unescape the same characters escaped by `_.escape`', 1, function() { - strictEqual(_.unescape(_.escape(unescaped)), unescaped); + equal(_.unescape(_.escape(unescaped)), unescaped); + }); + + test('should return an empty string when provided `null`, `undefined`, or empty strings', 3, function() { + strictEqual(_.unescape(null), ''); + strictEqual(_.unescape(undefined), ''); + strictEqual(_.unescape(''), ''); }); }()); @@ -9584,25 +8488,28 @@ QUnit.module('lodash.union'); (function() { - var args = arguments; - test('should return the union of the given arrays', 1, function() { - var actual = _.union([1, 3, 2], [5, 2, 1, 4], [2, 1]); - deepEqual(actual, [1, 3, 2, 5, 4]); + var actual = _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]); + deepEqual(actual, [1, 2, 3, 5, 4]); }); test('should not flatten nested arrays', 1, function() { - var actual = _.union([1, 3, 2], [1, [5]], [2, [4]]); - deepEqual(actual, [1, 3, 2, [5], [4]]); + var actual = _.union([1, 2, 3], [1, [5]], [2, [4]]); + deepEqual(actual, [1, 2, 3, [5], [4]]); }); - test('should ignore values that are not arrays or `arguments` objects', 3, function() { - var array = [0]; - deepEqual(_.union(array, 3, null, { '0': 1 }), array); - deepEqual(_.union(null, array, null, [2, 1]), [0, 2, 1]); - deepEqual(_.union(null, array, null, args), [0, 1, 2, 3]); + test('should produce correct results when provided a falsey `array` argument', 1, function() { + var expected = [1, 2, 3], + actual = _.union(null, expected); + + deepEqual(actual, expected); }); - }(1, 2, 3)); + + test('should ignore individual secondary values', 1, function() { + var array = [1]; + deepEqual(_.union(array, 1, 2, 3), array); + }); + }()); /*--------------------------------------------------------------------------*/ @@ -9660,7 +8567,7 @@ test('should work with large arrays', 1, function() { var object = {}; - var largeArray = _.times(largeArraySize, function(index) { + var largeArray = _.times(LARGE_ARRAY_SIZE, function(index) { switch (index % 3) { case 0: return 0; case 1: return 'a'; @@ -9674,19 +8581,18 @@ test('should work with large arrays of boolean, `null`, and `undefined` values', 1, function() { var array = [], expected = [true, false, null, undefined], - count = Math.ceil(largeArraySize / expected.length); + count = Math.ceil(LARGE_ARRAY_SIZE / expected.length); _.times(count, function() { push.apply(array, expected); }); - deepEqual(_.uniq(array), expected); }); test('should distinguish between numbers and numeric strings', 1, function() { var array = [], expected = ['2', 2, Object('2'), Object(2)], - count = Math.ceil(largeArraySize / expected.length); + count = Math.ceil(LARGE_ARRAY_SIZE / expected.length); _.times(count, function() { push.apply(array, expected); @@ -9695,7 +8601,7 @@ deepEqual(_.uniq(array), expected); }); - _.each({ + _.forEach({ 'an object': ['a'], 'a number': 0, 'a string': '0' @@ -9723,11 +8629,11 @@ actual.push(_.uniqueId()); }); - strictEqual(_.uniq(actual).length, actual.length); + equal(_.uniq(actual).length, actual.length); }); test('should return a string value when not passing a prefix argument', 1, function() { - strictEqual(typeof _.uniqueId(), 'string'); + equal(typeof _.uniqueId(), 'string'); }); test('should coerce the prefix argument to a string', 1, function() { @@ -9757,7 +8663,7 @@ QUnit.module('lodash.where'); (function() { - var objects = [ + var array = [ { 'a': 1 }, { 'a': 1 }, { 'a': 1, 'b': 2 }, @@ -9765,27 +8671,22 @@ { 'a': 3 } ]; - test('should filter by `source` properties', 6, function() { - deepEqual(_.where(objects, { 'a': 1 }), [{ 'a': 1 }, { 'a': 1 }, { 'a': 1, 'b': 2 }]); - deepEqual(_.where(objects, { 'a': 2 }), [{ 'a': 2, 'b': 2 }]); - deepEqual(_.where(objects, { 'a': 3 }), [{ 'a': 3 }]); - deepEqual(_.where(objects, { 'b': 1 }), []); - deepEqual(_.where(objects, { 'b': 2 }), [{ 'a': 1, 'b': 2 }, { 'a': 2, 'b': 2 }]); - deepEqual(_.where(objects, { 'a': 1, 'b': 2 }), [{ 'a': 1, 'b': 2 }]); + test('should filter by properties', 6, function() { + deepEqual(_.where(array, { 'a': 1 }), [{ 'a': 1 }, { 'a': 1 }, { 'a': 1, 'b': 2 }]); + deepEqual(_.where(array, { 'a': 2 }), [{ 'a': 2, 'b': 2 }]); + deepEqual(_.where(array, { 'a': 3 }), [{ 'a': 3 }]); + deepEqual(_.where(array, { 'b': 1 }), []); + deepEqual(_.where(array, { 'b': 2 }), [{ 'a': 1, 'b': 2 }, { 'a': 2, 'b': 2 }]); + deepEqual(_.where(array, { 'a': 1, 'b': 2 }), [{ 'a': 1, 'b': 2 }]); }); - test('should not filter by inherited `source` properties', 2, function() { + test('should not filter by inherited properties', 1, function() { function Foo() {} Foo.prototype = { 'a': 2 }; - var source = new Foo; - source.b = 2; - - var expected = [objects[2], objects[3]], - actual = _.where(objects, source); - - deepEqual(actual, expected); - ok(_.isEmpty(_.difference(actual, objects))); + var properties = new Foo; + properties.b = 2; + deepEqual(_.where(array, properties), [{ 'a': 1, 'b': 2 }, { 'a': 2, 'b': 2 }]); }); test('should filter by problem JScript properties (test in IE < 9)', 1, function() { @@ -9793,83 +8694,47 @@ deepEqual(_.where(collection, shadowedObject), [shadowedObject]); }); - test('should work with an object for `collection`', 2, function() { + test('should work with an object for `collection`', 1, function() { var collection = { 'x': { 'a': 1 }, 'y': { 'a': 3 }, 'z': { 'a': 1, 'b': 2 } }; - var expected = [collection.x, collection.z], - actual = _.where(collection, { 'a': 1 }); - - deepEqual(actual, expected); - ok(_.isEmpty(_.difference(actual, _.values(collection)))); + deepEqual(_.where(collection, { 'a': 1 }), [{ 'a': 1 }, { 'a': 1, 'b': 2 }]); }); - test('should work with a function for `source`', 1, function() { - function source() {} - source.a = 2; - - deepEqual(_.where(objects, source), [{ 'a': 2, 'b': 2 }]); + test('should return an empty array when provided an empty `properties` object', 1, function() { + deepEqual(_.where(array, {}), []); }); - test('should match all elements when provided an empty `source`', 1, function() { - var expected = _.map(empties, _.constant(objects)); - - var actual = _.map(empties, function(value) { - var result = _.where(objects, value); - return result !== objects && result; - }); - - deepEqual(actual, expected); - }); - - test('should perform a deep partial comparison of `source`', 2, function() { + test('should deep compare `properties` values', 1, function() { var collection = [{ 'a': { 'b': { 'c': 1, 'd': 2 }, 'e': 3 }, 'f': 4 }], - expected = collection.slice(), - actual = _.where(collection, { 'a': { 'b': { 'c': 1 } } }); + expected = _.cloneDeep(collection); - deepEqual(actual, expected); - ok(_.isEmpty(_.difference(actual, collection))); + deepEqual(_.where(collection, { 'a': { 'b': { 'c': 1 } } }), expected); }); test('should search of arrays for values', 2, function() { var collection = [{ 'a': [1, 2] }], - expected = collection.slice(); + expected = _.cloneDeep(collection); deepEqual(_.where(collection, { 'a': [] }), []); deepEqual(_.where(collection, { 'a': [2] }), expected); }); - test('should perform a partial comparison of *all* objects within arrays of `source`', 2, function() { - var collection = [ - { 'a': [{ 'b': 1, 'c': 2, 'd': 3 }, { 'b': 4, 'c': 5, 'd': 6 }] }, - { 'a': [{ 'b': 1, 'c': 2, 'd': 3 }, { 'b': 4, 'c': 6, 'd': 7 }] } - ]; - - var actual = _.where(collection, { 'a': [{ 'b': 1, 'c': 2 }, { 'b': 4, 'c': 5 }] }); - deepEqual(actual, [collection[0]]); - ok(_.isEmpty(_.difference(actual, collection))); - }); - - test('should handle a `source` with `undefined` values', 4, function() { - var source = { 'b': undefined }, - actual = _.where([{ 'a': 1 }, { 'a': 1, 'b': 1 }], source); - - deepEqual(actual, []); + test('should handle `properties` with `undefined` values', 4, function() { + var properties = { 'b': undefined }; + deepEqual(_.where([{ 'a': 1 }, { 'a': 1, 'b': 1 }], properties), []); var object = { 'a': 1, 'b': undefined }; - actual = _.where([object], source); - deepEqual(actual, [object]); + deepEqual(_.where([object], properties), [object]); - source = { 'a': { 'c': undefined } }; - actual = _.where([{ 'a': { 'b': 1 } }, { 'a':{ 'b':1 , 'c': 1 } }], source); - deepEqual(actual, []); + properties = { 'a': { 'c': undefined } }; + deepEqual(_.where([{ 'a': { 'b': 1 } }, { 'a':{ 'b':1 , 'c': 1 } }], properties), []); object = { 'a': { 'b': 1, 'c': undefined } }; - actual = _.where([object], source); - deepEqual(actual, [object]); + deepEqual(_.where([object], properties), [object]); }); }()); @@ -9903,18 +8768,18 @@ return '

    ' + func(text) + '

    '; }); - strictEqual(p('fred, barney, & pebbles'), '

    fred, barney, & pebbles

    '); + equal(p('fred, barney, & pebbles'), '

    fred, barney, & pebbles

    '); }); test('should pass the correct `wrapper` arguments', 1, function() { var args; - var wrapped = _.wrap(_.noop, function() { + var wrapped = _.wrap(noop, function() { args || (args = slice.call(arguments)); }); wrapped(1, 2, 3); - deepEqual(args, [_.noop, 1, 2, 3]); + deepEqual(args, [noop, 1, 2, 3]); }); test('should not set a `this` binding', 1, function() { @@ -9923,7 +8788,7 @@ }); var object = { 'p': p, 'text': 'fred, barney, & pebbles' }; - strictEqual(object.p(), '

    fred, barney, & pebbles

    '); + equal(object.p(), '

    fred, barney, & pebbles

    '); }); }()); @@ -9932,8 +8797,6 @@ QUnit.module('lodash.xor'); (function() { - var args = arguments; - test('should return the symmetric difference of the given arrays', 1, function() { var actual = _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]); deepEqual(actual, [1, 4, 5]); @@ -9963,18 +8826,11 @@ } }); - test('should ignore individual secondary arguments', 1, function() { - var array = [0]; - deepEqual(_.xor(array, 3, null, { '0': 1 }), array); + test('should ignore individual secondary values', 1, function() { + var array = [1, null, 3]; + deepEqual(_.xor(array, 3, null), array); }); - - test('should ignore values that are not arrays or `arguments` objects', 3, function() { - var array = [1, 2]; - deepEqual(_.xor(array, 3, null, { '0': 1 }), array); - deepEqual(_.xor(null, array, null, [2, 3]), [1, 3]); - deepEqual(_.xor(null, array, null, args), [3]); - }); - }(1, 2, 3)); + }()); /*--------------------------------------------------------------------------*/ @@ -10015,11 +8871,11 @@ ]; var actual = _.zip(pair[0]); - ok('0' in actual[2]); + ok(0 in actual[2]); deepEqual(actual, pair[1]); actual = _.zip.apply(_, actual); - ok('2' in actual[0]); + ok(2 in actual[0]); deepEqual(actual, [['barney', 36, undefined], ['fred', 40, false]]); }); @@ -10066,7 +8922,7 @@ }); test('should accept a falsey `array` argument', 1, function() { - var expected = _.map(falsey, _.constant({})); + var expected = _.map(falsey, function() { return {}; }); var actual = _.map(falsey, function(value, index) { try { @@ -10097,7 +8953,7 @@ wrapped.shift(); deepEqual(wrapped.keys().value(), ['length']); - strictEqual(wrapped.first(), undefined); + equal(wrapped.first(), undefined); } else { skipTest(2); @@ -10116,7 +8972,7 @@ wrapped.splice(0, 1); deepEqual(wrapped.keys().value(), ['length']); - strictEqual(wrapped.first(), undefined); + equal(wrapped.first(), undefined); } else { skipTest(2); @@ -10132,7 +8988,7 @@ test('should return the `toString` result of the wrapped value', 1, function() { if (!isNpm) { var wrapped = _([1, 2, 3]); - strictEqual(String(wrapped), '1,2,3'); + equal(String(wrapped), '1,2,3'); } else { skipTest(); @@ -10148,33 +9004,12 @@ test('should return the `valueOf` result of the wrapped value', 1, function() { if (!isNpm) { var wrapped = _(123); - strictEqual(Number(wrapped), 123); + equal(Number(wrapped), 123); } else { skipTest(); } }); - - test('should stringify the wrapped value when passed to `JSON.stringify`', 1, function() { - if (!isNpm && JSON) { - var wrapped = _([1, 2, 3]); - strictEqual(JSON.stringify(wrapped), '[1,2,3]'); - } - else { - skipTest(); - } - }); - - test('should be aliased', 2, function() { - if (!isNpm) { - var expected = _.prototype.valueOf; - strictEqual(_.prototype.toJSON, expected); - strictEqual(_.prototype.value, expected); - } - else { - skipTest(2); - } - }); }()); /*--------------------------------------------------------------------------*/ @@ -10192,7 +9027,7 @@ 'unshift' ]; - _.each(funcs, function(methodName) { + _.forEach(funcs, function(methodName) { test('`_(...).' + methodName + '` should return the existing wrapped value', 1, function() { if (!isNpm) { strictEqual(wrapped[methodName](), wrapped); @@ -10218,7 +9053,7 @@ 'splice' ]; - _.each(funcs, function(methodName) { + _.forEach(funcs, function(methodName) { test('`_(...).' + methodName + '` should return a new wrapped value', 1, function() { if (!isNpm) { ok(wrapped[methodName]() instanceof _); @@ -10271,14 +9106,14 @@ 'some' ]; - _.each(funcs, function(methodName) { + _.forEach(funcs, function(methodName) { test('`_(...).' + methodName + '` should return an unwrapped value', 1, function() { if (!isNpm) { var actual = methodName == 'reduceRight' ? wrapped[methodName](_.identity) : wrapped[methodName](); - ok(!(actual instanceof _)); + equal(actual instanceof _, false); } else { skipTest(); @@ -10301,10 +9136,10 @@ 'sample' ]; - _.each(funcs, function(methodName) { + _.forEach(funcs, function(methodName) { test('`_(...).' + methodName + '` called without an `n` argument should return an unwrapped value', 1, function() { if (!isNpm) { - strictEqual(typeof wrapped[methodName](), 'number'); + equal(typeof wrapped[methodName](), 'number'); } else { skipTest(); @@ -10323,10 +9158,10 @@ test('`_.' + methodName + '` should return `undefined` when querying falsey arguments without an `n` argument', 1, function() { if (!isNpm) { var actual = [], - expected = _.map(falsey, _.constant()), + expected = _.map(falsey, function() { return undefined; }), func = _[methodName]; - _.each(falsey, function(value, index) { + _.forEach(falsey, function(value, index) { try { actual.push(index ? func(value) : func()); } catch(e) { } @@ -10341,7 +9176,7 @@ test('`_.' + methodName + '` should return an empty array when querying falsey arguments with an `n` argument', 1, function() { if (!isNpm) { - var expected = _.map(falsey, _.constant([])), + var expected = _.map(falsey, function() { return []; }), func = _[methodName]; var actual = _.map(falsey, function(value, index) { @@ -10402,22 +9237,21 @@ deepEqual([args[0], args[1], args[2]], [1, [3], 5], message('pull')); _.remove(args, function(value) { return typeof value == 'number'; }); - ok(args.length === 1 && _.isEqual(args[0], [3]), message('remove')); + ok(args.length == 1 && _.isEqual(args[0], [3]), message('remove')); } else { skipTest(2) } }); - test('should accept falsey primary arguments', 4, function() { + test('should accept falsey primary arguments', 3, function() { function message(methodName) { return '`_.' + methodName + '` should accept falsey primary arguments'; } - deepEqual(_.difference(null, array), array, message('difference')); - deepEqual(_.intersection(null, array), array, message('intersection')); + deepEqual(_.difference(null, array), [], message('difference')); + deepEqual(_.intersection(null, array), [], message('intersection')); deepEqual(_.union(null, array), array, message('union')); - deepEqual(_.xor(null, array), array, message('xor')); }); test('should accept falsey secondary arguments', 3, function() { @@ -10426,49 +9260,13 @@ } deepEqual(_.difference(array, null), array, message('difference')); - deepEqual(_.intersection(array, null), array, message('intersection')); + deepEqual(_.intersection(array, null), [], message('intersection')); deepEqual(_.union(array, null), array, message('union')); }); }(1, null, [3], null, 5)); /*--------------------------------------------------------------------------*/ - /*--------------------------------------------------------------------------*/ - - QUnit.module('"Strings" category methods'); - - (function() { - var stringMethods = [ - 'camelCase', - 'capitalize', - 'escape', - 'escapeRegExp', - 'kebabCase', - 'pad', - 'padLeft', - 'padRight', - 'repeat', - 'snakeCase', - 'trim', - 'trimLeft', - 'trimRight', - 'truncate', - 'unescape' - ]; - - _.each(stringMethods, function(methodName) { - var func = _[methodName]; - - test('`_.' + methodName + '` should return an empty string when provided `null`, `undefined`, or empty string', 3, function() { - strictEqual(func(null), ''); - strictEqual(func(undefined), ''); - strictEqual(func(''), ''); - }); - }); - }()); - - /*--------------------------------------------------------------------------*/ - QUnit.module('lodash methods'); (function() { @@ -10496,6 +9294,7 @@ 'range', 'reject', 'remove', + 'removeAt', 'rest', 'sample', 'shuffle', @@ -10520,7 +9319,6 @@ 'defer', 'delay', 'memoize', - 'negate', 'once', 'partial', 'partialRight', @@ -10531,12 +9329,12 @@ var acceptFalsey = _.difference(allMethods, rejectFalsey); - test('should accept falsey arguments', 185, function() { - var emptyArrays = _.map(falsey, _.constant([])), + test('should accept falsey arguments', 167, function() { + var emptyArrays = _.map(falsey, function() { return []; }), isExposed = '_' in root, oldDash = root._; - _.each(acceptFalsey, function(methodName) { + _.forEach(acceptFalsey, function(methodName) { var expected = emptyArrays, func = _[methodName], pass = true; @@ -10567,17 +9365,17 @@ }); // skip tests for missing methods of modularized builds - _.each(['noConflict', 'runInContext', 'tap'], function(methodName) { + _.forEach(['noConflict', 'runInContext', 'tap'], function(methodName) { if (!_[methodName]) { skipTest(); } }); }); - test('should return an array', 64, function() { + test('should return an array', 66, function() { var array = [1, 2, 3]; - _.each(returnArrays, function(methodName) { + _.forEach(returnArrays, function(methodName) { var actual, func = _[methodName]; @@ -10596,13 +9394,13 @@ ok(_.isArray(actual), '_.' + methodName + ' returns an array'); var isPull = methodName == 'pull'; - strictEqual(actual === array, isPull, '_.' + methodName + ' should ' + (isPull ? '' : 'not ') + 'return the provided array'); + equal(actual === array, isPull, '_.' + methodName + ' should ' + (isPull ? '' : 'not ') + 'return the provided array'); }); }); - test('should throw a TypeError for falsey arguments', 15, function() { - _.each(rejectFalsey, function(methodName) { - var expected = _.map(falsey, _.constant(true)), + test('should reject falsey arguments', 14, function() { + _.forEach(rejectFalsey, function(methodName) { + var expected = _.map(falsey, function() { return true; }), func = _[methodName]; var actual = _.map(falsey, function(value, index) { @@ -10619,18 +9417,14 @@ }); }); - test('should handle `null` `thisArg` arguments', 44, function() { - var expected = (function() { return this; }).call(null); + test('should handle `null` `thisArg` arguments', 30, function() { + var thisArg, + callback = function() { thisArg = this; }, + expected = (function() { return this; }).call(null); var funcs = [ - 'assign', - 'clone', - 'cloneDeep', 'countBy', - 'dropWhile', - 'dropRightWhile', 'every', - 'flatten', 'filter', 'find', 'findIndex', @@ -10645,14 +9439,10 @@ 'forOwn', 'forOwnRight', 'groupBy', - 'isEqual', 'map', - 'mapValues', 'max', - 'merge', 'min', 'omit', - 'partition', 'pick', 'reduce', 'reduceRight', @@ -10661,46 +9451,38 @@ 'some', 'sortBy', 'sortedIndex', - 'takeWhile', - 'takeRightWhile', - 'tap', 'times', - 'transform', 'uniq' ]; - _.each(funcs, function(methodName) { - var actual, - array = ['a'], + _.forEach(funcs, function(methodName) { + var array = ['a'], func = _[methodName], message = '`_.' + methodName + '` handles `null` `thisArg` arguments'; - function callback() { - actual = this; + thisArg = undefined; + + if (/^reduce/.test(methodName)) { + func(array, callback, 0, null); + } else if (methodName == 'sortedIndex') { + func(array, 'a', callback, null); + } else if (methodName == 'times') { + func(1, callback, null); + } else { + func(array, callback, null); } - if (func) { - if (/^reduce/.test(methodName) || methodName == 'transform') { - func(array, callback, 0, null); - } else if (_.contains(['assign', 'merge'], methodName)) { - func(array, array, callback, null); - } else if (_.contains(['isEqual', 'sortedIndex'], methodName)) { - func(array, 'a', callback, null); - } else if (methodName == 'times') { - func(1, callback, null); - } else { - func(array, callback, null); - } - strictEqual(actual, expected, message); - } - else { - skipTest(); + + if (expected === null) { + strictEqual(thisArg, null, message); + } else { + equal(thisArg, expected, message); } }); }); test('should not contain minified method names (test production builds)', 1, function() { ok(_.every(_.functions(_), function(methodName) { - return methodName.length > 2 || methodName === 'at'; + return methodName.length > 2 || methodName == 'at'; })); }); }());