diff options
author | Simon Metson <metson@apache.org> | 2013-04-04 13:08:19 +0100 |
---|---|---|
committer | Simon Metson <metson@apache.org> | 2013-04-04 13:08:19 +0100 |
commit | a46e87d71a2821c79615dff9c0fefbdb5a140020 (patch) | |
tree | 875b99e22f506ca6a01b9ac71b2e36709e239801 | |
parent | cca2015397c065d59e0cf99d64ad19deae91313f (diff) | |
download | couchdb-a46e87d71a2821c79615dff9c0fefbdb5a140020.tar.gz |
update lodash
-rw-r--r-- | src/fauxton/assets/js/libs/lodash.js | 5223 |
1 files changed, 3012 insertions, 2211 deletions
diff --git a/src/fauxton/assets/js/libs/lodash.js b/src/fauxton/assets/js/libs/lodash.js index 43ddcb208..ccf632d5e 100644 --- a/src/fauxton/assets/js/libs/lodash.js +++ b/src/fauxton/assets/js/libs/lodash.js @@ -1,77 +1,77 @@ -/*! - * Lo-Dash v0.3.1 <http://lodash.com> - * Copyright 2012 John-David Dalton <http://allyoucanleet.com/> - * Based on Underscore.js 1.3.3, copyright 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. - * <http://documentcloud.github.com/underscore> +/** + * @license + * Lo-Dash 1.1.1 (Custom Build) <http://lodash.com/> + * Build: `lodash underscore exports="amd,commonjs,global,node" -o ./dist/lodash.underscore.js` + * Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/> + * Based on Underscore.js 1.4.4 <http://underscorejs.org/> + * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. * Available under MIT license <http://lodash.com/license> */ -;(function(window, undefined) { - 'use strict'; +;(function(window) { + + /** Used as a safe reference for `undefined` in pre ES5 environments */ + var undefined; /** Detect free variable `exports` */ - var freeExports = typeof exports == 'object' && exports && - (typeof global == 'object' && global && global == global.global && (window = global), exports); + var freeExports = typeof exports == 'object' && exports; - /** - * Detect the JScript [[DontEnum]] bug: - * In IE < 9 an objects own properties, shadowing non-enumerable ones, are - * made non-enumerable as well. - */ - var hasDontEnumBug = !{ 'valueOf': 0 }.propertyIsEnumerable('valueOf'); + /** Detect free variable `module` */ + var freeModule = typeof module == 'object' && module && module.exports == freeExports && module; + + /** Detect free variable `global` and use it as `window` */ + var freeGlobal = typeof global == 'object' && global; + if (freeGlobal.global === freeGlobal) { + window = freeGlobal; + } /** Used to generate unique IDs */ var idCounter = 0; - /** Used to restore the original `_` reference in `noConflict` */ - var oldDash = window._; + /** Used internally to indicate various things */ + var indicatorObject = {}; - /** Used to detect if a method is native */ - var reNative = RegExp('^' + ({}.valueOf + '') - .replace(/[.*+?^=!:${}()|[\]\/\\]/g, '\\$&') - .replace(/valueOf|for [^\]]+/g, '.+?') + '$'); + /** Used to match empty string literals in compiled template source */ + var reEmptyStringLeading = /\b__p \+= '';/g, + reEmptyStringMiddle = /\b(__p \+=) '' \+/g, + reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; - /** Used to match tokens in template text */ - var reToken = /__token__(\d+)/g; + /** Used to match HTML entities */ + var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g; - /** Used to match unescaped characters in HTML */ - var reUnescapedHtml = /[&<"']/g; + /** + * Used to match ES6 template delimiters + * http://people.mozilla.org/~jorendorff/es6-draft.html#sec-7.8.6 + */ + var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; - /** Used to match unescaped characters in compiled string literals */ - var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; + /** Used to match regexp flags from their coerced string values */ + var reFlags = /\w*$/; - /** Used to fix the JScript [[DontEnum]] bug */ - var shadowed = [ - 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', - 'toLocaleString', 'toString', 'valueOf' - ]; + /** Used to match "interpolate" template delimiters */ + var reInterpolate = /<%=([\s\S]+?)%>/g; - /** Used to make template sourceURLs easier to identify */ - var templateCounter = 0; + /** Used to ensure capturing order of template delimiters */ + var reNoMatch = /($^)/; - /** Used to replace template delimiters */ - var token = '__token__'; + /** Used to match HTML characters */ + var reUnescapedHtml = /[&<>"']/g; - /** Used to store tokenized template text snippets */ - var tokenized = []; + /** Used to match unescaped characters in compiled string literals */ + var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; - /** Detect if sourceURL syntax is usable without erroring */ - try { - // Adobe's and Narwhal's JS engines will error - var useSourceURL = (Function('//@')(), true); - } catch(e){ } + /** Used to make template sourceURLs easier to identify */ + var templateCounter = 0; - /** - * Used to escape characters for inclusion in HTML. - * The `>` and `/` characters don't require escaping in HTML and have no - * special meaning unless they're part of a tag or an unquoted attribute value - * http://mathiasbynens.be/notes/ambiguous-ampersands (semi-related fun fact) - */ - var htmlEscapes = { - '&': '&', - '<': '<', - '"': '"', - "'": ''' - }; + /** `Object#toString` result shortcuts */ + var argsClass = '[object Arguments]', + arrayClass = '[object Array]', + boolClass = '[object Boolean]', + dateClass = '[object Date]', + funcClass = '[object Function]', + numberClass = '[object Number]', + objectClass = '[object Object]', + regexpClass = '[object RegExp]', + stringClass = '[object String]'; /** Used to determine if values are of the language type Object */ var objectTypes = { @@ -94,72 +94,142 @@ '\u2029': 'u2029' }; - /** Object#toString result shortcuts */ - var arrayClass = '[object Array]', - boolClass = '[object Boolean]', - dateClass = '[object Date]', - funcClass = '[object Function]', - numberClass = '[object Number]', - regexpClass = '[object RegExp]', - stringClass = '[object String]'; + /*--------------------------------------------------------------------------*/ - /** Native prototype shortcuts */ - var ArrayProto = Array.prototype, - ObjectProto = Object.prototype; + /** Used for `Array` and `Object` method references */ + var arrayRef = Array(), + objectRef = Object(); - /** Native method shortcuts */ - var concat = ArrayProto.concat, - hasOwnProperty = ObjectProto.hasOwnProperty, - push = ArrayProto.push, - slice = ArrayProto.slice, - toString = ObjectProto.toString; + /** Used to restore the original `_` reference in `noConflict` */ + var oldDash = window._; + + /** Used to detect if a method is native */ + var reNative = RegExp('^' + + String(objectRef.valueOf) + .replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + .replace(/valueOf|for [^\]]+/g, '.+?') + '$' + ); - /* Used if `Function#bind` exists and is inferred to be fast (i.e. all but V8) */ - var nativeBind = reNative.test(nativeBind = slice.bind) && - /\n|Opera/.test(nativeBind + toString.call(window.opera)) && nativeBind; + /** Native method shortcuts */ + var ceil = Math.ceil, + clearTimeout = window.clearTimeout, + concat = arrayRef.concat, + floor = Math.floor, + hasOwnProperty = objectRef.hasOwnProperty, + push = arrayRef.push, + setTimeout = window.setTimeout, + slice = arrayRef.slice, + toString = objectRef.toString; /* Native method shortcuts for methods with the same name as other `lodash` methods */ - var nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray, + var nativeBind = reNative.test(nativeBind = slice.bind) && nativeBind, + nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray, nativeIsFinite = window.isFinite, - nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys; + nativeIsNaN = window.isNaN, + nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys, + nativeMax = Math.max, + nativeMin = Math.min, + nativeRandom = Math.random; - /** Timer shortcuts */ - var clearTimeout = window.clearTimeout, - setTimeout = window.setTimeout; + /** Detect various environments */ + var isIeOpera = reNative.test(window.attachEvent), + isV8 = nativeBind && !/\n|true/.test(nativeBind + isIeOpera); /*--------------------------------------------------------------------------*/ /** - * The `lodash` function. + * Creates a `lodash` object, that wraps the given `value`, to enable method + * chaining. + * + * In addition to Lo-Dash methods, wrappers also have the following `Array` methods: + * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`, + * and `unshift` + * + * Chaining is supported in custom builds as long as the `value` method is + * implicitly or explicitly included in the build. + * + * The chainable wrapper functions are: + * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, + * `compose`, `concat`, `countBy`, `createCallback`, `debounce`, `defaults`, + * `defer`, `delay`, `difference`, `filter`, `flatten`, `forEach`, `forIn`, + * `forOwn`, `functions`, `groupBy`, `initial`, `intersection`, `invert`, + * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`, + * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `push`, `range`, + * `reject`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, `sortBy`, `splice`, + * `tap`, `throttle`, `times`, `toArray`, `union`, `uniq`, `unshift`, `values`, + * `where`, `without`, `wrap`, and `zip` + * + * The non-chainable wrapper functions are: + * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `has`, + * `identity`, `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, + * `isElement`, `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, + * `isNull`, `isNumber`, `isObject`, `isPlainObject`, `isRegExp`, `isString`, + * `isUndefined`, `join`, `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, + * `pop`, `random`, `reduce`, `reduceRight`, `result`, `shift`, `size`, `some`, + * `sortedIndex`, `runInContext`, `template`, `unescape`, `uniqueId`, and `value` + * + * The wrapper functions `first` and `last` return wrapped values when `n` is + * passed, otherwise they return unwrapped values. * * @name _ * @constructor - * @param {Mixed} value The value to wrap in a `LoDash` instance. - * @returns {Object} Returns a `LoDash` instance. + * @category Chaining + * @param {Mixed} value The value to wrap in a `lodash` instance. + * @returns {Object} Returns a `lodash` instance. */ function lodash(value) { - // allow invoking `lodash` without the `new` operator - return new LoDash(value); + return (value instanceof lodash) + ? value + : new lodashWrapper(value); } /** - * Creates a `LoDash` instance that wraps a value to allow chaining. + * An object used to flag environments features. * - * @private - * @constructor - * @param {Mixed} value The value to wrap. + * @static + * @memberOf _ + * @type Object */ - function LoDash(value) { - // exit early if already wrapped - if (value && value._wrapped) { - return value; - } - this._wrapped = value; - } + var support = {}; + + (function() { + var object = { '0': 1, 'length': 1 }; + + /** + * Detect if `arguments` objects are `Object` objects (all but Opera < 10.5). + * + * @memberOf _.support + * @type Boolean + */ + support.argsObject = arguments.constructor == Object; + + /** + * Detect if `Function#bind` exists and is inferred to be fast (all but V8). + * + * @memberOf _.support + * @type Boolean + */ + support.fastBind = nativeBind && !isV8; + + /** + * 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. + * + * @memberOf _.support + * @type Boolean + */ + support.spliceObjects = (arrayRef.splice.call(object, 0, 1), !object[0]); + }(1)); /** - * By default, Lo-Dash uses ERB-style template delimiters, 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 _ @@ -170,7 +240,6 @@ /** * Used to detect `data` property values to be HTML-escaped. * - * @static * @memberOf _.templateSettings * @type RegExp */ @@ -179,7 +248,6 @@ /** * Used to detect code to be evaluated. * - * @static * @memberOf _.templateSettings * @type RegExp */ @@ -188,510 +256,1545 @@ /** * Used to detect `data` property values to inject. * - * @static * @memberOf _.templateSettings * @type RegExp */ - 'interpolate': /<%=([\s\S]+?)%>/g, + 'interpolate': reInterpolate, /** * Used to reference the data object in the template text. * - * @static * @memberOf _.templateSettings * @type String */ - 'variable': 'obj' + 'variable': '' }; /*--------------------------------------------------------------------------*/ /** - * The template used to create iterator functions. + * Used by `_.max` and `_.min` as the default `callback` when a given + * `collection` is a string value. * * @private - * @param {Obect} data The data object used to populate the text. - * @returns {String} Returns the interpolated text. - */ - var iteratorTemplate = template( - // assign the `result` variable an initial value - 'var index, result<% if (init) { %> = <%= init %><% } %>;\n' + - // add code to exit early or do so if the first argument is falsey - '<%= exit %>;\n' + - // add code after the exit snippet but before the iteration branches - '<%= top %>;\n' + - - // the following branch is for iterating arrays and array-like objects - '<% if (arrayBranch) { %>' + - 'var length = <%= firstArg %>.length; index = -1;' + - ' <% if (objectBranch) { %>\nif (length === length >>> 0) {<% } %>\n' + - ' <%= arrayBranch.beforeLoop %>;\n' + - ' while (<%= arrayBranch.loopExp %>) {\n' + - ' <%= arrayBranch.inLoop %>;\n' + - ' }' + - ' <% if (objectBranch) { %>\n}\n<% }' + - '}' + - - // the following branch is for iterating an object's own/inherited properties - 'if (objectBranch) {' + - ' if (arrayBranch) { %>else {\n<% }' + - ' if (!hasDontEnumBug) { %> var skipProto = typeof <%= iteratedObject %> == \'function\';\n<% } %>' + - ' <%= objectBranch.beforeLoop %>;\n' + - ' for (<%= objectBranch.loopExp %>) {' + - ' \n<%' + - ' if (hasDontEnumBug) {' + - ' if (useHas) { %> if (<%= hasExp %>) {\n <% } %>' + - ' <%= objectBranch.inLoop %>;<%' + - ' if (useHas) { %>\n }<% }' + - ' }' + - ' else {' + - ' %>' + - - // 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 a function's `prototype` property [[Enumerable]] - // value to `true`. Because of this Lo-Dash standardizes on skipping - // the the `prototype` property of functions regardless of its - // [[Enumerable]] value. - ' if (!(skipProto && index == \'prototype\')<% if (useHas) { %> && <%= hasExp %><% } %>) {\n' + - ' <%= objectBranch.inLoop %>;\n' + - ' }' + - ' <% } %>\n' + - ' }' + - - // Because IE < 9 can't set the `[[Enumerable]]` attribute of an - // existing property and the `constructor` property of a prototype - // defaults to non-enumerable, Lo-Dash skips the `constructor` - // property when it infers it's iterating over a `prototype` object. - ' <% if (hasDontEnumBug) { %>\n' + - ' var ctor = <%= iteratedObject %>.constructor;\n' + - ' <% for (var k = 0; k < 7; k++) { %>\n' + - ' index = \'<%= shadowed[k] %>\';\n' + - ' if (<%' + - ' if (shadowed[k] == \'constructor\') {' + - ' %>!(ctor && ctor.prototype === <%= iteratedObject %>) && <%' + - ' } %><%= hasExp %>) {\n' + - ' <%= objectBranch.inLoop %>;\n' + - ' }<%' + - ' }' + - ' }' + - ' if (arrayBranch) { %>\n}<% }' + - '} %>\n' + - - // add code to the bottom of the iteration function - '<%= bottom %>;\n' + - // finally, return the `result` - 'return result' - ); + * @param {String} value The character to inspect. + * @returns {Number} Returns the code unit of given character. + */ + function charAtCallback(value) { + return value.charCodeAt(0); + } /** - * Reusable iterator options shared by - * `every`, `filter`, `find`, `forEach`, `forIn`, `forOwn`, `map`, `reject`, and `some`. - */ - var baseIteratorOptions = { - 'args': 'collection, callback, thisArg', - 'init': 'collection', - 'top': - 'if (!callback) {\n' + - ' callback = identity\n' + - '}\n' + - 'else if (thisArg) {\n' + - ' callback = iteratorBind(callback, thisArg)\n' + - '}', - 'inLoop': 'callback(collection[index], index, collection)' - }; + * Used by `sortBy` to compare transformed `collection` values, stable sorting + * them in ascending order. + * + * @private + * @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 of `1` or `-1`. + */ + function compareAscending(a, b) { + var ai = a.index, + bi = b.index; - /** Reusable iterator options for `every` and `some` */ - var everyIteratorOptions = { - 'init': 'true', - 'inLoop': 'if (!callback(collection[index], index, collection)) return !result' - }; + a = a.criteria; + b = b.criteria; - /** Reusable iterator options for `defaults` and `extend` */ - var extendIteratorOptions = { - 'args': 'object', - 'init': 'object', - 'top': - 'for (var source, sourceIndex = 1, length = arguments.length; sourceIndex < length; sourceIndex++) {\n' + - ' source = arguments[sourceIndex];\n' + - (hasDontEnumBug ? ' if (source) {' : ''), - 'loopExp': 'index in source', - 'useHas': false, - 'inLoop': 'object[index] = source[index]', - 'bottom': (hasDontEnumBug ? ' }\n' : '') + '}' - }; + // ensure a stable sort in V8 and other engines + // http://code.google.com/p/v8/issues/detail?id=90 + if (a !== b) { + if (a > b || typeof a == 'undefined') { + return 1; + } + if (a < b || typeof b == 'undefined') { + return -1; + } + } + return ai < bi ? -1 : 1; + } + + /** + * Creates a function that, when called, invokes `func` with the `this` binding + * of `thisArg` and prepends any `partialArgs` to the arguments passed to the + * bound function. + * + * @private + * @param {Function|String} func The function to bind or the method name. + * @param {Mixed} [thisArg] The `this` binding of `func`. + * @param {Array} partialArgs An array of arguments to be partially applied. + * @param {Object} [idicator] Used to indicate binding by key or partially + * applying arguments from the right. + * @returns {Function} Returns the new bound function. + */ + function createBound(func, thisArg, partialArgs, indicator) { + var isFunc = isFunction(func), + isPartial = !partialArgs, + key = thisArg; + + // juggle arguments + if (isPartial) { + var rightIndicator = indicator; + partialArgs = thisArg; + } + else if (!isFunc) { + if (!indicator) { + throw new TypeError; + } + thisArg = func; + } + + function bound() { + // `Function#bind` spec + // http://es5.github.com/#x15.3.4.5 + var args = arguments, + thisBinding = isPartial ? this : thisArg; + + if (!isFunc) { + func = thisArg[key]; + } + if (partialArgs.length) { + args = args.length + ? (args = slice.call(args), rightIndicator ? args.concat(partialArgs) : partialArgs.concat(args)) + : partialArgs; + } + if (this instanceof bound) { + // ensure `new bound` is an instance of `func` + noop.prototype = func.prototype; + thisBinding = new noop; + noop.prototype = null; + + // mimic the constructor's `return` behavior + // http://es5.github.com/#x13.2.2 + var result = func.apply(thisBinding, args); + return isObject(result) ? result : thisBinding; + } + return func.apply(thisBinding, args); + } + return bound; + } + + /** + * Used by `template` to escape characters for inclusion in compiled + * string literals. + * + * @private + * @param {String} match The matched character to escape. + * @returns {String} Returns the escaped character. + */ + function escapeStringChar(match) { + return '\\' + stringEscapes[match]; + } + + /** + * Used by `escape` to convert characters to HTML entities. + * + * @private + * @param {String} match The matched character to escape. + * @returns {String} Returns the escaped character. + */ + function escapeHtmlChar(match) { + return htmlEscapes[match]; + } + + /** + * A fast path for creating `lodash` wrapper objects. + * + * @private + * @param {Mixed} value The value to wrap in a `lodash` instance. + * @returns {Object} Returns a `lodash` instance. + */ + function lodashWrapper(value) { + this.__wrapped__ = value; + } + // ensure `new lodashWrapper` is an instance of `lodash` + lodashWrapper.prototype = lodash.prototype; - /** Reusable iterator options for `filter` and `reject` */ - var filterIteratorOptions = { - 'init': '[]', - 'inLoop': 'callback(collection[index], index, collection) && result.push(collection[index])' + /** + * A no-operation function. + * + * @private + */ + function noop() { + // no operation performed + } + + /** + * Used by `unescape` to convert HTML entities to characters. + * + * @private + * @param {String} match The matched character to unescape. + * @returns {String} Returns the unescaped character. + */ + function unescapeHtmlChar(match) { + return htmlUnescapes[match]; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Checks if `value` is an `arguments` object. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} 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 toString.call(value) == argsClass; + } + // fallback for browsers that can't detect `arguments` objects by [[Class]] + if (!isArguments(arguments)) { + isArguments = function(value) { + return value ? hasOwnProperty.call(value, 'callee') : false; + }; + } + + /** + * Checks if `value` is an array. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true`, if the `value` is an array, else `false`. + * @example + * + * (function() { return _.isArray(arguments); })(); + * // => false + * + * _.isArray([1, 2, 3]); + * // => true + */ + var isArray = nativeIsArray || function(value) { + // `instanceof` may cause a memory leak in IE 7 if `value` is a host object + // http://ajaxian.com/archives/working-aroung-the-instanceof-memory-leak + return (support.argsObject && value instanceof Array) || toString.call(value) == arrayClass; }; - /** Reusable iterator options for `find`, `forEach`, `forIn`, and `forOwn` */ - var forEachIteratorOptions = { - 'top': 'if (thisArg) callback = iteratorBind(callback, thisArg)' + /** + * A fallback implementation of `Object.keys` that 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 a new array of property names. + */ + var shimKeys = function (object) { + var index, iterable = object, result = []; + if (!iterable) return result; + if (!(objectTypes[typeof object])) return result; + + for (index in iterable) { + if (hasOwnProperty.call(iterable, index)) { + result.push(index); + } + } + return result }; - /** Reusable iterator options for `forIn` and `forOwn` */ - var forOwnIteratorOptions = { - 'inLoop': { - 'object': baseIteratorOptions.inLoop + /** + * Creates an array composed of the own enumerable property names of `object`. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names. + * @example + * + * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); + * // => ['one', 'two', 'three'] (order is not guaranteed) + */ + var keys = !nativeKeys ? shimKeys : function(object) { + if (!isObject(object)) { + return []; } + return nativeKeys(object); }; + /** + * Used to convert characters to HTML entities: + * + * Though the `>` character is escaped for symmetry, characters like `>` and `/` + * don't require escaping in HTML and have no special meaning unless they're part + * of a tag or an unquoted attribute value. + * http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact") + */ + var htmlEscapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + + /** Used to convert HTML entities to characters */ + var htmlUnescapes = invert(htmlEscapes); + /*--------------------------------------------------------------------------*/ /** - * Creates compiled iteration functions. The iteration function will be created - * to iterate over only objects if the first argument of `options.args` is - * "object" or `options.inLoop.array` is falsey. + * Assigns own enumerable properties of source object(s) to the destination + * object. Subsequent sources will overwrite property assignments of previous + * sources. If a `callback` function is passed, it will be executed to produce + * the assigned values. The `callback` is bound to `thisArg` and invoked with + * two arguments; (objectValue, sourceValue). * - * @private - * @param {Object} [options1, options2, ...] The compile options objects. - * - * args - A string of comma separated arguments the iteration function will - * accept. + * @static + * @memberOf _ + * @type Function + * @alias extend + * @category Objects + * @param {Object} object The destination object. + * @param {Object} [source1, source2, ...] The source objects. + * @param {Function} [callback] The function to customize assigning values. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns the destination object. + * @example * - * init - A string to specify the initial value of the `result` variable. + * _.assign({ 'name': 'moe' }, { 'age': 40 }); + * // => { 'name': 'moe', 'age': 40 } * - * exit - A string of code to use in place of the default exit-early check - * of `if (!arguments[0]) return result`. + * var defaults = _.partialRight(_.assign, function(a, b) { + * return typeof a == 'undefined' ? b : a; + * }); * - * top - A string of code to execute after the exit-early check but before - * the iteration branches. + * var food = { 'name': 'apple' }; + * defaults(food, { 'name': 'banana', 'type': 'fruit' }); + * // => { 'name': 'apple', 'type': 'fruit' } + */ + function assign(object) { + if (!object) { + return object; + } + for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { + var iterable = arguments[argsIndex]; + if (iterable) { + for (var key in iterable) { + object[key] = iterable[key]; + } + } + } + return object; + } + + /** + * Creates a clone of `value`. If `deep` is `true`, nested objects will also + * be cloned, otherwise they will be assigned by reference. If a `callback` + * function is passed, it will be executed to produce the cloned values. If + * `callback` returns `undefined`, cloning will be handled by the method instead. + * The `callback` is bound to `thisArg` and invoked with one argument; (value). * - * beforeLoop - A string or object containing an "array" or "object" property - * of code to execute before the array or object loops. + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to clone. + * @param {Boolean} [deep=false] A flag to indicate a deep clone. + * @param {Function} [callback] The function to customize cloning values. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @param- {Array} [stackA=[]] Tracks traversed source objects. + * @param- {Array} [stackB=[]] Associates clones with source counterparts. + * @returns {Mixed} Returns the cloned `value`. + * @example * - * loopExp - A string or object containing an "array" or "object" property - * of code to execute as the array or object loop expression. + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 } + * ]; * - * useHas - A boolean to specify whether or not to use `hasOwnProperty` checks - * in the object loop. + * var shallow = _.clone(stooges); + * shallow[0] === stooges[0]; + * // => true * - * inLoop - A string or object containing an "array" or "object" property - * of code to execute in the array or object loops. + * var deep = _.clone(stooges, true); + * deep[0] === stooges[0]; + * // => false * - * bottom - A string of code to execute after the iteration branches but - * before the `result` is returned. + * _.mixin({ + * 'clone': _.partialRight(_.clone, function(value) { + * return _.isElement(value) ? value.cloneNode(false) : undefined; + * }) + * }); * - * @returns {Function} Returns the compiled function. + * var clone = _.clone(document.body); + * clone.childNodes.length; + * // => 0 */ - function createIterator() { - var object, - prop, - value, - index = -1, - length = arguments.length; - - // merge options into a template data object - var data = { - 'bottom': '', - 'exit': '', - 'init': '', - 'top': '', - 'arrayBranch': { 'beforeLoop': '', 'loopExp': '++index < length' }, - 'objectBranch': { 'beforeLoop': '' } - }; + function clone(value) { + return isObject(value) + ? (isArray(value) ? slice.call(value) : assign({}, value)) + : value; + } - while (++index < length) { - object = arguments[index]; - for (prop in object) { - value = (value = object[prop]) == null ? '' : value; - // keep this regexp explicit for the build pre-process - if (/beforeLoop|loopExp|inLoop/.test(prop)) { - if (typeof value == 'string') { - value = { 'array': value, 'object': value }; + /** + * Assigns own enumerable properties of source object(s) to the destination + * object for all destination properties that resolve to `undefined`. Once a + * property is set, additional defaults of the same property will be ignored. + * + * @static + * @memberOf _ + * @type Function + * @category Objects + * @param {Object} object The destination object. + * @param {Object} [source1, source2, ...] The source objects. + * @param- {Object} [guard] Allows working with `_.reduce` without using its + * callback's `key` and `object` arguments as sources. + * @returns {Object} Returns the destination object. + * @example + * + * var food = { 'name': 'apple' }; + * _.defaults(food, { 'name': 'banana', 'type': 'fruit' }); + * // => { 'name': 'apple', 'type': 'fruit' } + */ + function defaults(object) { + if (!object) { + return object; + } + for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { + var iterable = arguments[argsIndex]; + if (iterable) { + for (var key in iterable) { + if (object[key] == null) { + object[key] = iterable[key]; } - data.arrayBranch[prop] = value.array; - data.objectBranch[prop] = value.object; - } else { - data[prop] = value; } } } - // set additional template data values - var args = data.args, - arrayBranch = data.arrayBranch, - objectBranch = data.objectBranch, - firstArg = /^[^,]+/.exec(args)[0], - loopExp = objectBranch.loopExp, - iteratedObject = /\S+$/.exec(loopExp || firstArg)[0]; + return object; + } + + /** + * Iterates over `object`'s own and inherited enumerable properties, 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. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns `object`. + * @example + * + * function Dog(name) { + * this.name = name; + * } + * + * Dog.prototype.bark = function() { + * alert('Woof, woof!'); + * }; + * + * _.forIn(new Dog('Dagny'), function(value, key) { + * alert(key); + * }); + * // => alerts 'name' and 'bark' (order is not guaranteed) + */ + var forIn = function (collection, callback) { + var index, iterable = collection, result = iterable; + if (!iterable) return result; + if (!objectTypes[typeof iterable]) return result; + + for (index in iterable) { + if (callback(iterable[index], index, collection) === indicatorObject) return result; + } + return result + }; + + /** + * Iterates over an object's own enumerable properties, 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. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns `object`. + * @example + * + * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { + * alert(key); + * }); + * // => alerts '0', '1', and 'length' (order is not guaranteed) + */ + var forOwn = function (collection, callback) { + var index, iterable = collection, result = iterable; + if (!iterable) return result; + if (!objectTypes[typeof iterable]) return result; + + for (index in iterable) { + if (hasOwnProperty.call(iterable, index)) { + if (callback(iterable[index], index, collection) === indicatorObject) return result; + } + } + return result + }; + + /** + * Creates a sorted array of all enumerable properties, own and inherited, + * of `object` that have function values. + * + * @static + * @memberOf _ + * @alias methods + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names that have function values. + * @example + * + * _.functions(_); + * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] + */ + function functions(object) { + var result = []; + forIn(object, function(value, key) { + if (isFunction(value)) { + result.push(key); + } + }); + return result.sort(); + } + + /** + * Checks if the specified object `property` exists and is a direct property, + * instead of an inherited property. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to check. + * @param {String} property The property to check for. + * @returns {Boolean} Returns `true` if key is a direct property, else `false`. + * @example + * + * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); + * // => true + */ + function has(object, property) { + return object ? hasOwnProperty.call(object, property) : false; + } + + /** + * Creates an object composed of the inverted keys and values of the given `object`. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to invert. + * @returns {Object} Returns the created inverted object. + * @example + * + * _.invert({ 'first': 'moe', 'second': 'larry' }); + * // => { 'moe': 'first', 'larry': 'second' } + */ + function invert(object) { + var index = -1, + props = keys(object), + length = props.length, + result = {}; + + while (++index < length) { + var key = props[index]; + result[object[key]] = key; + } + return result; + } + + /** + * Checks if `value` is a boolean value. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true`, if the `value` is a boolean value, else `false`. + * @example + * + * _.isBoolean(null); + * // => false + */ + function isBoolean(value) { + return value === true || value === false || toString.call(value) == boolClass; + } + + /** + * Checks if `value` is a date. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true`, if the `value` is a date, else `false`. + * @example + * + * _.isDate(new Date); + * // => true + */ + function isDate(value) { + return value instanceof Date || toString.call(value) == dateClass; + } - data.firstArg = firstArg; - data.hasDontEnumBug = hasDontEnumBug; - data.hasExp = 'hasOwnProperty.call(' + iteratedObject + ', index)'; - data.iteratedObject = iteratedObject; - data.shadowed = shadowed; - data.useHas = data.useHas !== false; + /** + * Checks if `value` is a DOM element. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true`, if the `value` is a DOM element, else `false`. + * @example + * + * _.isElement(document.body); + * // => true + */ + function isElement(value) { + return value ? value.nodeType === 1 : false; + } - if (!data.exit) { - data.exit = 'if (!' + firstArg + ') return result'; + /** + * 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 the `value` is empty, else `false`. + * @example + * + * _.isEmpty([1, 2, 3]); + * // => false + * + * _.isEmpty({}); + * // => true + * + * _.isEmpty(''); + * // => true + */ + function isEmpty(value) { + if (!value) { + return true; } - if (firstArg == 'object' || !arrayBranch.inLoop) { - data.arrayBranch = null; + if (isArray(value) || isString(value)) { + return !value.length; } - if (!loopExp) { - objectBranch.loopExp = 'index in ' + iteratedObject; + for (var key in value) { + if (hasOwnProperty.call(value, key)) { + return false; + } } - // create the function factory - var factory = Function( - 'arrayClass, funcClass, hasOwnProperty, identity, iteratorBind, objectTypes, ' + - 'stringClass, toString, undefined', - '"use strict"; return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}' - ); - // return the compiled function - return factory( - arrayClass, funcClass, hasOwnProperty, identity, iteratorBind, objectTypes, - stringClass, toString - ); + return true; } /** - * Used by `template()` to replace tokens with their corresponding code snippets. + * Performs a deep comparison between two values to determine if they are + * equivalent to each other. If `callback` is passed, it will be executed to + * compare values. If `callback` returns `undefined`, comparisons will be handled + * by the method instead. The `callback` is bound to `thisArg` and invoked with + * two arguments; (a, b). * - * @private - * @param {String} match The matched token. - * @param {String} index The `tokenized` index of the code snippet. - * @returns {String} Returns the code snippet. + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} a The value to compare. + * @param {Mixed} b The other value to compare. + * @param {Function} [callback] The function to customize comparing values. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @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`. + * @example + * + * var moe = { 'name': 'moe', 'age': 40 }; + * var copy = { 'name': 'moe', 'age': 40 }; + * + * moe == copy; + * // => false + * + * _.isEqual(moe, copy); + * // => true + * + * var words = ['hello', 'goodbye']; + * var otherWords = ['hi', 'goodbye']; + * + * _.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 detokenize(match, index) { - return tokenized[index]; + function isEqual(a, b, stackA, stackB) { + if (a === b) { + return a !== 0 || (1 / a == 1 / b); + } + var type = typeof a, + otherType = typeof b; + + if (a === a && + (!a || (type != 'function' && type != 'object')) && + (!b || (otherType != 'function' && otherType != 'object'))) { + return false; + } + if (a == null || b == null) { + return a === b; + } + var className = toString.call(a), + otherClass = toString.call(b); + + if (className != otherClass) { + return false; + } + switch (className) { + case boolClass: + case dateClass: + return +a == +b; + + case numberClass: + return a != +a + ? b != +b + : (a == 0 ? (1 / a == 1 / b) : a == +b); + + case regexpClass: + case stringClass: + return a == String(b); + } + var isArr = className == arrayClass; + if (!isArr) { + if (a instanceof lodash || b instanceof lodash) { + return isEqual(a.__wrapped__ || a, b.__wrapped__ || b, stackA, stackB); + } + if (className != objectClass) { + return false; + } + var ctorA = a.constructor, + ctorB = b.constructor; + + if (ctorA != ctorB && !( + isFunction(ctorA) && ctorA instanceof ctorA && + isFunction(ctorB) && ctorB instanceof ctorB + )) { + return false; + } + } + stackA || (stackA = []); + stackB || (stackB = []); + + var length = stackA.length; + while (length--) { + if (stackA[length] == a) { + return stackB[length] == b; + } + } + var result = true, + size = 0; + + stackA.push(a); + stackB.push(b); + + if (isArr) { + size = b.length; + result = size == a.length; + + if (result) { + while (size--) { + if (!(result = isEqual(a[size], b[size], stackA, stackB))) { + break; + } + } + } + return result; + } + forIn(b, function(value, key, b) { + if (hasOwnProperty.call(b, key)) { + size++; + return !(result = hasOwnProperty.call(a, key) && isEqual(a[key], value, stackA, stackB)) && indicatorObject; + } + }); + + if (result) { + forIn(a, function(value, key, a) { + if (hasOwnProperty.call(a, key)) { + return !(result = --size > -1) && indicatorObject; + } + }); + } + return result; } /** - * Used by `template()` to escape characters for inclusion in compiled - * string literals. + * Checks if `value` is, or can be coerced to, a finite number. * - * @private - * @param {String} match The matched character to escape. - * @returns {String} Returns the escaped character. + * Note: This is not the same as native `isFinite`, which will return true for + * booleans and empty strings. See http://es5.github.com/#x15.1.2.5. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true`, if the `value` is finite, else `false`. + * @example + * + * _.isFinite(-101); + * // => true + * + * _.isFinite('10'); + * // => true + * + * _.isFinite(true); + * // => false + * + * _.isFinite(''); + * // => false + * + * _.isFinite(Infinity); + * // => false */ - function escapeStringChar(match) { - return '\\' + stringEscapes[match]; + function isFinite(value) { + return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value)); } /** - * Used by `escape()` to escape characters for inclusion in HTML. + * Checks if `value` is a function. * - * @private - * @param {String} match The matched character to escape. - * @returns {String} Returns the escaped character. + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true`, if the `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true */ - function escapeHtmlChar(match) { - return htmlEscapes[match]; + function isFunction(value) { + return typeof value == 'function'; + } + // fallback for older versions of Chrome and Safari + if (isFunction(/x/)) { + isFunction = function(value) { + return value instanceof Function || toString.call(value) == funcClass; + }; } /** - * Creates a new function that, when called, invokes `func` with the `this` - * binding of `thisArg` and the arguments (value, index, object). + * Checks if `value` is the language type of Object. + * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * - * @private - * @param {Function} func The function to bind. - * @param {Mixed} [thisArg] The `this` binding of `func`. - * @returns {Function} Returns the new bound function. + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true`, if the `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(1); + * // => false */ - function iteratorBind(func, thisArg) { - return function(value, index, object) { - return func.call(thisArg, value, index, object); - }; + function isObject(value) { + // check if the value is the ECMAScript language type of Object + // http://es5.github.com/#x8 + // and avoid a V8 bug + // http://code.google.com/p/v8/issues/detail?id=2291 + return value ? objectTypes[typeof value] : false; } /** - * A no-operation function. + * Checks if `value` is `NaN`. * - * @private + * Note: This is not the same as native `isNaN`, which will return `true` for + * `undefined` and other values. See http://es5.github.com/#x15.1.2.4. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true`, if the `value` is `NaN`, else `false`. + * @example + * + * _.isNaN(NaN); + * // => true + * + * _.isNaN(new Number(NaN)); + * // => true + * + * isNaN(undefined); + * // => true + * + * _.isNaN(undefined); + * // => false */ - function noop() { - // no operation performed + 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) + return isNumber(value) && value != +value } /** - * A shim implementation of `Object.keys` that produces an array of the given - * object's own enumerable property names. + * Checks if `value` is `null`. * - * @private - * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property names. + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true`, if the `value` is `null`, else `false`. + * @example + * + * _.isNull(null); + * // => true + * + * _.isNull(undefined); + * // => false */ - var shimKeys = createIterator({ - 'args': 'object', - 'exit': 'if (!objectTypes[typeof object] || object === null) throw TypeError()', - 'init': '[]', - 'inLoop': 'result.push(index)' - }); + function isNull(value) { + return value === null; + } /** - * Used by `template()` to replace "escape" template delimiters with tokens. + * Checks if `value` is a number. * - * @private - * @param {String} match The matched template delimiter. - * @param {String} value The delimiter value. - * @returns {String} Returns a token. + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true`, if the `value` is a number, else `false`. + * @example + * + * _.isNumber(8.4 * 5); + * // => true */ - function tokenizeEscape(match, value) { - var index = tokenized.length; - tokenized[index] = "'+\n_.escape(" + value + ") +\n'"; - return token + index; + function isNumber(value) { + return typeof value == 'number' || toString.call(value) == numberClass; } /** - * Used by `template()` to replace "interpolate" template delimiters with tokens. + * Checks if `value` is a regular expression. * - * @private - * @param {String} match The matched template delimiter. - * @param {String} value The delimiter value. - * @returns {String} Returns a token. + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true`, if the `value` is a regular expression, else `false`. + * @example + * + * _.isRegExp(/moe/); + * // => true */ - function tokenizeInterpolate(match, value) { - var index = tokenized.length; - tokenized[index] = "'+\n((__t = (" + value + ")) == null ? '' : __t) +\n'"; - return token + index; + function isRegExp(value) { + return value instanceof RegExp || toString.call(value) == regexpClass; } /** - * Used by `template()` to replace "evaluate" template delimiters with tokens. + * Checks if `value` is a string. * - * @private - * @param {String} match The matched template delimiter. - * @param {String} value The delimiter value. - * @returns {String} Returns a token. + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true`, if the `value` is a string, else `false`. + * @example + * + * _.isString('moe'); + * // => true + */ + function isString(value) { + return typeof value == 'string' || toString.call(value) == stringClass; + } + + /** + * Checks if `value` is `undefined`. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true`, if the `value` is `undefined`, else `false`. + * @example + * + * _.isUndefined(void 0); + * // => true + */ + function isUndefined(value) { + return typeof value == 'undefined'; + } + + /** + * 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 `callback` function is passed, it will be executed + * for each property in the `object`, omitting the properties `callback` + * returns truthy 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} callback|[prop1, prop2, ...] The properties to omit + * or the function called per iteration. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns an object without the omitted properties. + * @example + * + * _.omit({ 'name': 'moe', 'age': 40 }, 'age'); + * // => { 'name': 'moe' } + * + * _.omit({ 'name': 'moe', 'age': 40 }, function(value) { + * return typeof value == 'number'; + * }); + * // => { 'name': 'moe' } + */ + function omit(object) { + var props = concat.apply(arrayRef, arguments), + result = {}; + + forIn(object, function(value, key) { + if (indexOf(props, key, 1) < 0) { + result[key] = value; + } + }); + return result; + } + + /** + * Creates a two dimensional array of the given 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 new array of key-value pairs. + * @example + * + * _.pairs({ 'moe': 30, 'larry': 40 }); + * // => [['moe', 30], ['larry', 40]] (order is not guaranteed) + */ + function pairs(object) { + var index = -1, + props = keys(object), + length = props.length, + result = Array(length); + + while (++index < length) { + var key = props[index]; + result[index] = [key, object[key]]; + } + return result; + } + + /** + * 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 `callback` is passed, it will be executed for each property in the + * `object`, picking the properties `callback` returns truthy 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 {Array|Function|String} callback|[prop1, prop2, ...] The function called + * per iteration or properties to pick, either as individual arguments or arrays. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns an object composed of the picked properties. + * @example + * + * _.pick({ 'name': 'moe', '_userid': 'moe1' }, 'name'); + * // => { 'name': 'moe' } + * + * _.pick({ 'name': 'moe', '_userid': 'moe1' }, function(value, key) { + * return key.charAt(0) != '_'; + * }); + * // => { 'name': 'moe' } */ - function tokenizeEvaluate(match, value) { - var index = tokenized.length; - tokenized[index] = "';\n" + value + ";\n__p += '"; - return token + index; + function pick(object) { + var index = 0, + props = concat.apply(arrayRef, arguments), + length = props.length, + result = {}; + + while (++index < length) { + var prop = props[index]; + if (prop in object) { + result[prop] = object[prop]; + } + } + return result; + } + + /** + * 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 a new array of property values. + * @example + * + * _.values({ 'one': 1, 'two': 2, 'three': 3 }); + * // => [1, 2, 3] (order is not guaranteed) + */ + function values(object) { + var index = -1, + props = keys(object), + length = props.length, + result = Array(length); + + while (++index < length) { + result[index] = object[props[index]]; + } + return result; } /*--------------------------------------------------------------------------*/ /** - * Checks if a given `target` value is present in a `collection` using strict - * equality for comparisons, i.e. `===`. + * Checks if a given `target` element is present in a `collection` using strict + * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used + * as the offset from the end of the collection. * * @static * @memberOf _ * @alias include * @category Collections - * @param {Array|Object} collection The collection to iterate over. + * @param {Array|Object|String} collection The collection to iterate over. * @param {Mixed} target The value to check for. - * @returns {Boolean} Returns `true` if `target` value is found, else `false`. + * @param {Number} [fromIndex=0] The index to search from. + * @returns {Boolean} Returns `true` if the `target` element is found, else `false`. * @example * - * _.contains([1, 2, 3], 3); + * _.contains([1, 2, 3], 1); + * // => true + * + * _.contains([1, 2, 3], 1, 2); + * // => false + * + * _.contains({ 'name': 'moe', 'age': 40 }, 'moe'); + * // => true + * + * _.contains('curly', 'ur'); * // => true */ - var contains = createIterator({ - 'args': 'collection, target', - 'init': 'false', - 'inLoop': 'if (collection[index] === target) return true' - }); + function contains(collection, target) { + var length = collection ? collection.length : 0, + result = false; + if (typeof length == 'number') { + result = indexOf(collection, target) > -1; + } else { + forOwn(collection, function(value) { + return (result = value === target) && indicatorObject; + }); + } + return result; + } + + /** + * Creates an object composed of keys returned from running each element of the + * `collection` through the given `callback`. The corresponding value of each key + * is the number of times the key was returned by the `callback`. The `callback` + * is bound to `thisArg` and invoked with three arguments; (value, index|key, collection). + * + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is passed 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 Collections + * @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 passed, it will be used to create + * a "_.pluck" or "_.where" style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); }); + * // => { '4': 1, '6': 2 } + * + * _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math); + * // => { '4': 1, '6': 2 } + * + * _.countBy(['one', 'two', 'three'], 'length'); + * // => { '3': 2, '5': 1 } + */ + function countBy(collection, callback, thisArg) { + var result = {}; + callback = createCallback(callback, thisArg); + + forEach(collection, function(value, key, collection) { + key = String(callback(value, key, collection)); + (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1); + }); + return result; + } /** * Checks if the `callback` returns a truthy value for **all** elements of a - * `collection`. The `callback` is bound to `thisArg` and invoked with 3 - * arguments; for arrays they are (value, index, array) and for objects they - * are (value, key, object). + * `collection`. The `callback` is bound to `thisArg` and invoked with three + * arguments; (value, index|key, collection). + * + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is passed 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 all * @category Collections - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Boolean} Returns `true` if all values pass the callback check, else `false`. + * @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 passed, it will be used to create + * a "_.pluck" or "_.where" style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Boolean} Returns `true` if all elements pass the callback check, + * else `false`. * @example * * _.every([true, 1, null, 'yes'], Boolean); * // => false + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 } + * ]; + * + * // using "_.pluck" callback shorthand + * _.every(stooges, 'age'); + * // => true + * + * // using "_.where" callback shorthand + * _.every(stooges, { 'age': 50 }); + * // => false */ - var every = createIterator(baseIteratorOptions, everyIteratorOptions); + function every(collection, callback, thisArg) { + var result = true; + callback = createCallback(callback, thisArg); + + var index = -1, + length = collection ? collection.length : 0; + + if (typeof length == 'number') { + while (++index < length) { + if (!(result = !!callback(collection[index], index, collection))) { + break; + } + } + } else { + forOwn(collection, function(value, index, collection) { + return !(result = !!callback(value, index, collection)) && indicatorObject; + }); + } + return result; + } /** - * Examines each value in a `collection`, returning an array of all values the - * `callback` returns truthy for. The `callback` is bound to `thisArg` and - * invoked with 3 arguments; for arrays they are (value, index, array) and for - * objects they are (value, key, object). + * Examines each element in a `collection`, returning an array of all elements + * the `callback` returns truthy for. The `callback` is bound to `thisArg` and + * invoked with three arguments; (value, index|key, collection). + * + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is passed 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 select * @category Collections - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Array} Returns a new array of values that passed callback check. + * @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 passed, it will be used to create + * a "_.pluck" or "_.where" style callback, respectively. + * @param {Mixed} [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, 5, 6], function(num) { return num % 2 == 0; }); * // => [2, 4, 6] + * + * var food = [ + * { 'name': 'apple', 'organic': false, 'type': 'fruit' }, + * { 'name': 'carrot', 'organic': true, 'type': 'vegetable' } + * ]; + * + * // using "_.pluck" callback shorthand + * _.filter(food, 'organic'); + * // => [{ 'name': 'carrot', 'organic': true, 'type': 'vegetable' }] + * + * // using "_.where" callback shorthand + * _.filter(food, { 'type': 'fruit' }); + * // => [{ 'name': 'apple', 'organic': false, 'type': 'fruit' }] */ - var filter = createIterator(baseIteratorOptions, filterIteratorOptions); + function filter(collection, callback, thisArg) { + var result = []; + callback = createCallback(callback, thisArg); + + var index = -1, + length = collection ? collection.length : 0; + + if (typeof length == 'number') { + while (++index < length) { + var value = collection[index]; + if (callback(value, index, collection)) { + result.push(value); + } + } + } else { + forOwn(collection, function(value, index, collection) { + if (callback(value, index, collection)) { + result.push(value); + } + }); + } + return result; + } /** - * Examines each value in a `collection`, returning the first one the `callback` - * returns truthy for. The function returns as soon as it finds an acceptable - * value, and does not iterate over the entire `collection`. The `callback` is - * bound to `thisArg` and invoked with 3 arguments; for arrays they are - * (value, index, array) and for objects they are (value, key, object). + * Examines each element in a `collection`, returning the first that the `callback` + * returns truthy for. The `callback` is bound to `thisArg` and invoked with three + * arguments; (value, index|key, collection). + * + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is passed 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 * @category Collections - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} callback The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Mixed} Returns the value that passed the callback check, else `undefined`. + * @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 passed, it will be used to create + * a "_.pluck" or "_.where" style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Mixed} Returns the found element, else `undefined`. * @example * - * var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * _.find([1, 2, 3, 4], function(num) { return num % 2 == 0; }); * // => 2 + * + * var food = [ + * { 'name': 'apple', 'organic': false, 'type': 'fruit' }, + * { 'name': 'banana', 'organic': true, 'type': 'fruit' }, + * { 'name': 'beet', 'organic': false, 'type': 'vegetable' } + * ]; + * + * // using "_.where" callback shorthand + * _.find(food, { 'type': 'vegetable' }); + * // => { 'name': 'beet', 'organic': false, 'type': 'vegetable' } + * + * // using "_.pluck" callback shorthand + * _.find(food, 'organic'); + * // => { 'name': 'banana', 'organic': true, 'type': 'fruit' } */ - var find = createIterator(baseIteratorOptions, forEachIteratorOptions, { - 'init': '', - 'inLoop': 'if (callback(collection[index], index, collection)) return collection[index]' - }); + function find(collection, callback, thisArg) { + callback = createCallback(callback, thisArg); + + var index = -1, + length = collection ? collection.length : 0; + + if (typeof length == 'number') { + while (++index < length) { + var value = collection[index]; + if (callback(value, index, collection)) { + return value; + } + } + } else { + var result; + forOwn(collection, function(value, index, collection) { + if (callback(value, index, collection)) { + result = value; + return indicatorObject; + } + }); + return result; + } + } + + function findWhere(object, properties) { + return where(object, properties, true); + } /** - * Iterates over a `collection`, executing the `callback` for each value in the - * `collection`. The `callback` is bound to `thisArg` and invoked with 3 - * arguments; for arrays they are (value, index, array) and for objects they - * are (value, key, object). + * Iterates over a `collection`, executing the `callback` for each element in + * the `collection`. The `callback` is bound to `thisArg` and invoked with three + * arguments; (value, index|key, collection). Callbacks may exit iteration early + * by explicitly returning `false`. * * @static * @memberOf _ * @alias each * @category Collections - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} callback The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Array|Object} Returns the `collection`. + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Array|Object|String} Returns `collection`. * @example * * _([1, 2, 3]).forEach(alert).join(','); * // => alerts each number and returns '1,2,3' * * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); - * // => alerts each number (order is not guaranteed) + * // => alerts each number value (order is not guaranteed) */ - var forEach = createIterator(baseIteratorOptions, forEachIteratorOptions); + function forEach(collection, callback, thisArg) { + var index = -1, + length = collection ? collection.length : 0; + + callback = callback && typeof thisArg == 'undefined' ? callback : createCallback(callback, thisArg); + if (typeof length == 'number') { + while (++index < length) { + if (callback(collection[index], index, collection) === indicatorObject) { + break; + } + } + } else { + forOwn(collection, callback); + }; + } + + /** + * Creates an object composed of keys returned from running each element of the + * `collection` through the `callback`. The corresponding value of each key is + * an array of elements passed to `callback` that returned the key. The `callback` + * is bound to `thisArg` and invoked with three arguments; (value, index|key, collection). + * + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is passed 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 Collections + * @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 passed, it will be used to create + * a "_.pluck" or "_.where" style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); }); + * // => { '4': [4.2], '6': [6.1, 6.4] } + * + * _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math); + * // => { '4': [4.2], '6': [6.1, 6.4] } + * + * // using "_.pluck" callback shorthand + * _.groupBy(['one', 'two', 'three'], 'length'); + * // => { '3': ['one', 'two'], '5': ['three'] } + */ + function groupBy(collection, callback, thisArg) { + var result = {}; + callback = createCallback(callback, thisArg); + + forEach(collection, function(value, key, collection) { + key = String(callback(value, key, collection)); + (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value); + }); + return result; + } + + /** + * 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 passed to each invoked method. If `methodName` is a function, it will + * be invoked for, and `this` bound to, each element in the `collection`. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} methodName The name of the method to invoke or + * the function invoked per iteration. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with. + * @returns {Array} Returns a new array of the results of each invoked method. + * @example + * + * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); + * // => [[1, 5, 7], [1, 2, 3]] + * + * _.invoke([123, 456], String.prototype.split, ''); + * // => [['1', '2', '3'], ['4', '5', '6']] + */ + function invoke(collection, methodName) { + var args = slice.call(arguments, 2), + index = -1, + isFunc = typeof methodName == 'function', + length = collection ? collection.length : 0, + result = Array(typeof length == 'number' ? length : 0); + + forEach(collection, function(value) { + result[++index] = (isFunc ? methodName : value[methodName]).apply(value, args); + }); + return result; + } /** - * Produces a new array of values by mapping each value in the `collection` - * through a transformation `callback`. The `callback` is bound to `thisArg` - * and invoked with 3 arguments; for arrays they are (value, index, array) - * and for objects they are (value, key, object). + * Creates an array of values by running each element in the `collection` + * through the `callback`. The `callback` is bound to `thisArg` and invoked with + * three arguments; (value, index|key, collection). + * + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is passed 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 collect * @category Collections - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Array} Returns a new array of values returned by the callback. + * @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 passed, it will be used to create + * a "_.pluck" or "_.where" style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Array} Returns a new array of the results of each `callback` execution. * @example * * _.map([1, 2, 3], function(num) { return num * 3; }); @@ -699,71 +1802,269 @@ * * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); * // => [3, 6, 9] (order is not guaranteed) + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 } + * ]; + * + * // using "_.pluck" callback shorthand + * _.map(stooges, 'name'); + * // => ['moe', 'larry'] */ - var map = createIterator(baseIteratorOptions, { - 'init': '', - 'exit': 'if (!collection) return []', - 'beforeLoop': { - 'array': 'result = Array(length)', - 'object': 'result = []' - }, - 'inLoop': { - 'array': 'result[index] = callback(collection[index], index, collection)', - 'object': 'result.push(callback(collection[index], index, collection))' + function map(collection, callback, thisArg) { + var index = -1, + length = collection ? collection.length : 0; + + callback = createCallback(callback, thisArg); + if (typeof length == 'number') { + var result = Array(length); + while (++index < length) { + result[index] = callback(collection[index], index, collection); + } + } else { + result = []; + forOwn(collection, function(value, key, collection) { + result[++index] = callback(value, key, collection); + }); } - }); + return result; + } + + /** + * Retrieves the maximum value of an `array`. If `callback` is passed, + * it will be executed for each value in the `array` to generate the + * criterion by which the value is ranked. The `callback` is bound to + * `thisArg` and invoked with three arguments; (value, index, collection). + * + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is passed 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 Collections + * @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 passed, it will be used to create + * a "_.pluck" or "_.where" style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Mixed} Returns the maximum value. + * @example + * + * _.max([4, 2, 8, 6]); + * // => 8 + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 } + * ]; + * + * _.max(stooges, function(stooge) { return stooge.age; }); + * // => { 'name': 'larry', 'age': 50 }; + * + * // using "_.pluck" callback shorthand + * _.max(stooges, 'age'); + * // => { 'name': 'larry', 'age': 50 }; + */ + function max(collection, callback, thisArg) { + var computed = -Infinity, + result = computed; + + var index = -1, + length = collection ? collection.length : 0; + + if (!callback && typeof length == 'number') { + while (++index < length) { + var value = collection[index]; + if (value > result) { + result = value; + } + } + } else { + callback = createCallback(callback, thisArg); + + forEach(collection, function(value, index, collection) { + var current = callback(value, index, collection); + if (current > computed) { + computed = current; + result = value; + } + }); + } + return result; + } + + /** + * Retrieves the minimum value of an `array`. If `callback` is passed, + * it will be executed for each value in the `array` to generate the + * criterion by which the value is ranked. The `callback` is bound to `thisArg` + * and invoked with three arguments; (value, index, collection). + * + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is passed 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 Collections + * @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 passed, it will be used to create + * a "_.pluck" or "_.where" style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Mixed} Returns the minimum value. + * @example + * + * _.min([4, 2, 8, 6]); + * // => 2 + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 } + * ]; + * + * _.min(stooges, function(stooge) { return stooge.age; }); + * // => { 'name': 'moe', 'age': 40 }; + * + * // using "_.pluck" callback shorthand + * _.min(stooges, 'age'); + * // => { 'name': 'moe', 'age': 40 }; + */ + function min(collection, callback, thisArg) { + var computed = Infinity, + result = computed; + + var index = -1, + length = collection ? collection.length : 0; + + if (!callback && typeof length == 'number') { + while (++index < length) { + var value = collection[index]; + if (value < result) { + result = value; + } + } + } else { + callback = createCallback(callback, thisArg); + + forEach(collection, function(value, index, collection) { + var current = callback(value, index, collection); + if (current < computed) { + computed = current; + result = value; + } + }); + } + return result; + } + + /** + * 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} property The property to pluck. + * @returns {Array} Returns a new array of property values. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 } + * ]; + * + * _.pluck(stooges, 'name'); + * // => ['moe', 'larry'] + */ + function pluck(collection, property) { + var index = -1, + length = collection ? collection.length : 0; + + if (typeof length == 'number') { + var result = Array(length); + while (++index < length) { + result[index] = collection[index][property]; + } + } + return result || map(collection, property); + } /** - * Boils down a `collection` to a single value. The initial state of the - * reduction is `accumulator` and each successive step of it should be returned - * by the `callback`. The `callback` is bound to `thisArg` and invoked with 4 - * arguments; for arrays they are (accumulator, value, index, array) and for - * objects they are (accumulator, value, key, object). + * Reduces a `collection` to a value that is the accumulated result of running + * each element in the `collection` through the `callback`, where each successive + * `callback` execution consumes the return value of the previous execution. + * If `accumulator` is not passed, the first element of the `collection` will be + * used as the initial `accumulator` value. The `callback` is bound to `thisArg` + * and invoked with four arguments; (accumulator, value, index|key, collection). * * @static * @memberOf _ * @alias foldl, inject * @category Collections - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} callback The function called per iteration. + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. * @param {Mixed} [accumulator] Initial value of the accumulator. - * @param {Mixed} [thisArg] The `this` binding for the callback. + * @param {Mixed} [thisArg] The `this` binding of `callback`. * @returns {Mixed} Returns the accumulated value. * @example * - * var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; }); + * var sum = _.reduce([1, 2, 3], function(sum, num) { + * return sum + num; + * }); * // => 6 + * + * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) { + * result[key] = num * 3; + * return result; + * }, {}); + * // => { 'a': 3, 'b': 6, 'c': 9 } */ - var reduce = createIterator({ - 'args': 'collection, callback, accumulator, thisArg', - 'init': 'accumulator', - 'top': - 'var noaccum = arguments.length < 3;\n' + - 'if (thisArg) callback = iteratorBind(callback, thisArg)', - 'beforeLoop': { - 'array': 'if (noaccum) result = collection[++index]' - }, - 'inLoop': { - 'array': - 'result = callback(result, collection[index], index, collection)', - 'object': - 'result = noaccum\n' + - ' ? (noaccum = false, collection[index])\n' + - ' : callback(result, collection[index], index, collection)' + function reduce(collection, callback, accumulator, thisArg) { + if (!collection) return accumulator; + var noaccum = arguments.length < 3; + callback = createCallback(callback, thisArg, 4); + + var index = -1, + length = collection.length; + + if (typeof length == 'number') { + if (noaccum) { + accumulator = collection[++index]; + } + while (++index < length) { + accumulator = callback(accumulator, collection[index], index, collection); + } + } else { + forOwn(collection, function(value, index, collection) { + accumulator = noaccum + ? (noaccum = false, value) + : callback(accumulator, value, index, collection) + }); } - }); + return accumulator; + } /** - * The right-associative version of `_.reduce`. + * This method is similar to `_.reduce`, except that it iterates over a + * `collection` from right to left. * * @static * @memberOf _ * @alias foldr * @category Collections - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} callback The function called per iteration. + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. * @param {Mixed} [accumulator] Initial value of the accumulator. - * @param {Mixed} [thisArg] The `this` binding for the callback. + * @param {Mixed} [thisArg] The `this` binding of `callback`. * @returns {Mixed} Returns the accumulated value. * @example * @@ -772,93 +2073,249 @@ * // => [4, 5, 2, 3, 0, 1] */ function reduceRight(collection, callback, accumulator, thisArg) { - if (!collection) { - return accumulator; - } - - var length = collection.length, + var iterable = collection, + length = collection ? collection.length : 0, noaccum = arguments.length < 3; - if(thisArg) { - callback = iteratorBind(callback, thisArg); - } - if (length === length >>> 0) { - if (length && noaccum) { - accumulator = collection[--length]; - } - while (length--) { - accumulator = callback(accumulator, collection[length], length, collection); - } - return accumulator; - } - - var prop, - props = keys(collection); - - length = props.length; - if (length && noaccum) { - accumulator = collection[props[--length]]; - } - while (length--) { - prop = props[length]; - accumulator = callback(accumulator, collection[prop], prop, collection); + if (typeof length != 'number') { + var props = keys(collection); + length = props.length; } + callback = createCallback(callback, thisArg, 4); + forEach(collection, function(value, index, collection) { + index = props ? props[--length] : --length; + accumulator = noaccum + ? (noaccum = false, iterable[index]) + : callback(accumulator, iterable[index], index, collection); + }); return accumulator; } /** - * The opposite of `_.filter`, this method returns the values of a `collection` - * that `callback` does **not** return truthy for. + * The opposite of `_.filter`, this method returns the elements of a + * `collection` that `callback` does **not** return truthy for. + * + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is passed 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 Collections - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Array} Returns a new array of values that did **not** pass the callback check. + * @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 passed, it will be used to create + * a "_.pluck" or "_.where" style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Array} Returns a new array of elements that did **not** pass the + * callback check. * @example * * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); * // => [1, 3, 5] + * + * var food = [ + * { 'name': 'apple', 'organic': false, 'type': 'fruit' }, + * { 'name': 'carrot', 'organic': true, 'type': 'vegetable' } + * ]; + * + * // using "_.pluck" callback shorthand + * _.reject(food, 'organic'); + * // => [{ 'name': 'apple', 'organic': false, 'type': 'fruit' }] + * + * // using "_.where" callback shorthand + * _.reject(food, { 'type': 'fruit' }); + * // => [{ 'name': 'carrot', 'organic': true, 'type': 'vegetable' }] */ - var reject = createIterator(baseIteratorOptions, filterIteratorOptions, { - 'inLoop': '!' + filterIteratorOptions.inLoop - }); + function reject(collection, callback, thisArg) { + callback = createCallback(callback, thisArg); + return filter(collection, function(value, index, collection) { + return !callback(value, index, collection); + }); + } + + /** + * Creates an array of shuffled `array` values, using a version of the + * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to shuffle. + * @returns {Array} Returns a new shuffled collection. + * @example + * + * _.shuffle([1, 2, 3, 4, 5, 6]); + * // => [4, 1, 6, 3, 5, 2] + */ + function shuffle(collection) { + var index = -1, + length = collection ? collection.length : 0, + result = Array(typeof length == 'number' ? length : 0); + + forEach(collection, function(value) { + var rand = floor(nativeRandom() * (++index + 1)); + result[index] = result[rand]; + result[rand] = value; + }); + return result; + } + + /** + * 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 + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to inspect. + * @returns {Number} Returns `collection.length` or number of own enumerable properties. + * @example + * + * _.size([1, 2]); + * // => 2 + * + * _.size({ 'one': 1, 'two': 2, 'three': 3 }); + * // => 3 + * + * _.size('curly'); + * // => 5 + */ + function size(collection) { + var length = collection ? collection.length : 0; + return typeof length == 'number' ? length : keys(collection).length; + } /** * Checks if the `callback` returns a truthy value for **any** element of a * `collection`. The function returns as soon as it finds passing value, and * does not iterate over the entire `collection`. The `callback` is bound to - * `thisArg` and invoked with 3 arguments; for arrays they are - * (value, index, array) and for objects they are (value, key, object). + * `thisArg` and invoked with three arguments; (value, index|key, collection). + * + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is passed 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 any * @category Collections - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Boolean} Returns `true` if any value passes the callback check, else `false`. + * @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 passed, it will be used to create + * a "_.pluck" or "_.where" style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Boolean} Returns `true` if any element passes the callback check, + * else `false`. * @example * - * _.some([null, 0, 'yes', false]); + * _.some([null, 0, 'yes', false], Boolean); + * // => true + * + * var food = [ + * { 'name': 'apple', 'organic': false, 'type': 'fruit' }, + * { 'name': 'carrot', 'organic': true, 'type': 'vegetable' } + * ]; + * + * // using "_.pluck" callback shorthand + * _.some(food, 'organic'); * // => true + * + * // using "_.where" callback shorthand + * _.some(food, { 'type': 'meat' }); + * // => false */ - var some = createIterator(baseIteratorOptions, everyIteratorOptions, { - 'init': 'false', - 'inLoop': everyIteratorOptions.inLoop.replace('!', '') - }); + function some(collection, callback, thisArg) { + var result; + callback = createCallback(callback, thisArg); + + var index = -1, + length = collection ? collection.length : 0; + + if (typeof length == 'number') { + while (++index < length) { + if ((result = callback(collection[index], index, collection))) { + break; + } + } + } else { + forOwn(collection, function(value, index, collection) { + return (result = callback(value, index, collection)) && indicatorObject; + }); + } + return !!result; + } /** - * Converts the `collection`, into an array. Useful for converting the - * `arguments` object. + * Creates an array of elements, sorted in ascending order by the results of + * running each element in the `collection` through the `callback`. This method + * performs a stable sort, that is, it will preserve the original sort order of + * equal elements. The `callback` is bound to `thisArg` and invoked with three + * arguments; (value, index|key, collection). + * + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is passed 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 Collections - * @param {Array|Object} collection The collection to convert. + * @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 passed, it will be used to create + * a "_.pluck" or "_.where" style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Array} Returns a new array of sorted elements. + * @example + * + * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); + * // => [3, 1, 2] + * + * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math); + * // => [3, 1, 2] + * + * // using "_.pluck" callback shorthand + * _.sortBy(['banana', 'strawberry', 'apple'], 'length'); + * // => ['apple', 'banana', 'strawberry'] + */ + function sortBy(collection, callback, thisArg) { + var index = -1, + length = collection ? collection.length : 0, + result = Array(typeof length == 'number' ? length : 0); + + callback = createCallback(callback, thisArg); + forEach(collection, function(value, key, collection) { + result[++index] = { + 'criteria': callback(value, key, collection), + 'index': index, + 'value': value + }; + }); + + length = result.length; + result.sort(compareAscending); + while (length--) { + result[length] = result[length].value; + } + return result; + } + + /** + * Converts the `collection` to an array. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to convert. * @returns {Array} Returns the new converted array. * @example * @@ -866,23 +2323,48 @@ * // => [2, 3, 4] */ function toArray(collection) { - if (!collection) { - return []; - } - if (toString.call(collection.toArray) == funcClass) { - return collection.toArray(); - } - var length = collection.length; - if (length === length >>> 0) { + if (isArray(collection)) { return slice.call(collection); } + if (collection && typeof collection.length == 'number') { + return map(collection); + } return values(collection); } + /** + * Examines each element in a `collection`, returning an array of all elements + * that have the given `properties`. When checking `properties`, this method + * performs a deep comparison between values to determine if they are equivalent + * to each other. + * + * @static + * @memberOf _ + * @type Function + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Object} properties The object of property values to filter by. + * @returns {Array} Returns a new array of elements that have the given `properties`. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 } + * ]; + * + * _.where(stooges, { 'age': 40 }); + * // => [{ 'name': 'moe', 'age': 40 }] + */ + function where(collection, properties, first) { + return (first && isEmpty(properties)) + ? null + : (first ? find : filter)(collection, properties); + } + /*--------------------------------------------------------------------------*/ /** - * Produces a new array with all falsey values of `array` removed. The values + * Creates an array with all falsey values of `array` removed. The values * `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey. * * @static @@ -896,23 +2378,21 @@ * // => [1, 2, 3] */ function compact(array) { - var result = []; - if (!array) { - return result; - } var index = -1, - length = array.length; + length = array ? array.length : 0, + result = []; while (++index < length) { - if (array[index]) { - result.push(array[index]); + var value = array[index]; + if (value) { + result.push(value); } } return result; } /** - * Produces a new array of `array` values not present in the other arrays + * Creates an array of `array` elements not present in the other arrays * using strict equality for comparisons, i.e. `===`. * * @static @@ -920,7 +2400,7 @@ * @category Arrays * @param {Array} array The array to process. * @param {Array} [array1, array2, ...] Arrays to check. - * @returns {Array} Returns a new array of `array` values not present in the + * @returns {Array} Returns a new array of `array` elements not present in the * other arrays. * @example * @@ -928,56 +2408,121 @@ * // => [1, 3, 4] */ function difference(array) { - var result = []; - if (!array) { - return result; - } var index = -1, length = array.length, - flattened = concat.apply(result, slice.call(arguments, 1)); + flattened = concat.apply(arrayRef, arguments), + result = []; while (++index < length) { - if (indexOf(flattened, array[index]) < 0) { - result.push(array[index]); + var value = array[index]; + if (indexOf(flattened, value, length) < 0) { + result.push(value); } } return result; } /** - * Gets the first value of the `array`. Pass `n` to return the first `n` values - * of the `array`. + * Gets the first element of the `array`. If a number `n` is passed, the first + * `n` elements of the `array` are returned. If a `callback` function is passed, + * elements at the beginning of the array are returned as long as the `callback` + * returns truthy. The `callback` is bound to `thisArg` and invoked with three + * arguments; (value, index, array). + * + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is passed 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, take * @category Arrays * @param {Array} array The array to query. - * @param {Number} [n] The number of elements to return. - * @param {Object} [guard] Internally used to allow this method to work with - * others like `_.map` without using their callback `index` argument for `n`. - * @returns {Mixed} Returns the first value or an array of the first `n` values - * of `array`. + * @param {Function|Object|Number|String} [callback|n] The function called + * per element or the number of elements to return. If a property name or + * object is passed, it will be used to create a "_.pluck" or "_.where" + * style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Mixed} Returns the first element(s) of `array`. * @example * - * _.first([5, 4, 3, 2, 1]); - * // => 5 + * _.first([1, 2, 3]); + * // => 1 + * + * _.first([1, 2, 3], 2); + * // => [1, 2] + * + * _.first([1, 2, 3], function(num) { + * return num < 3; + * }); + * // => [1, 2] + * + * var food = [ + * { 'name': 'banana', 'organic': true }, + * { 'name': 'beet', 'organic': false }, + * ]; + * + * // using "_.pluck" callback shorthand + * _.first(food, 'organic'); + * // => [{ 'name': 'banana', 'organic': true }] + * + * var food = [ + * { 'name': 'apple', 'type': 'fruit' }, + * { 'name': 'banana', 'type': 'fruit' }, + * { 'name': 'beet', 'type': 'vegetable' } + * ]; + * + * // using "_.where" callback shorthand + * _.first(food, { 'type': 'fruit' }); + * // => [{ 'name': 'apple', 'type': 'fruit' }, { 'name': 'banana', 'type': 'fruit' }] */ - function first(array, n, guard) { + function first(array, callback, thisArg) { if (array) { - return (n == undefined || guard) ? array[0] : slice.call(array, 0, n); + var n = 0, + length = array.length; + + if (typeof callback != 'number' && callback != null) { + var index = -1; + callback = createCallback(callback, thisArg); + while (++index < length && callback(array[index], index, array)) { + n++; + } + } else { + n = callback; + if (n == null || thisArg) { + return array[0]; + } + } + return slice.call(array, 0, nativeMin(nativeMax(0, n), length)); } } /** - * Flattens a nested array (the nesting can be to any depth). If `shallow` is - * truthy, `array` will only be flattened a single level. + * Flattens a nested array (the nesting can be to any depth). If `isShallow` + * is truthy, `array` will only be flattened a single level. If `callback` + * is passed, each element of `array` is passed through a callback` before + * flattening. The `callback` is bound to `thisArg` and invoked with three + * arguments; (value, index, array). + * + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is passed 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 compact. - * @param {Boolean} shallow A flag to indicate only flattening a single level. + * @param {Boolean} [isShallow=false] A flag to indicate only flattening a single level. + * @param {Function|Object|String} [callback=identity] The function called per + * iteration. If a property name or object is passed, it will be used to create + * a "_.pluck" or "_.where" style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. * @returns {Array} Returns a new flattened array. * @example * @@ -986,20 +2531,25 @@ * * _.flatten([1, [2], [3, [[4]]]], true); * // => [1, 2, 3, [[4]]]; + * + * var stooges = [ + * { 'name': 'curly', 'quotes': ['Oh, a wise guy, eh?', 'Poifect!'] }, + * { 'name': 'moe', 'quotes': ['Spread out!', 'You knucklehead!'] } + * ]; + * + * // using "_.pluck" callback shorthand + * _.flatten(stooges, 'quotes'); + * // => ['Oh, a wise guy, eh?', 'Poifect!', 'Spread out!', 'You knucklehead!'] */ - function flatten(array, shallow) { - var result = []; - if (!array) { - return result; - } - var value, - index = -1, - length = array.length; + function flatten(array, isShallow) { + var index = -1, + length = array ? array.length : 0, + result = []; while (++index < length) { - value = array[index]; + var value = array[index]; if (isArray(value)) { - push.apply(result, shallow ? value : flatten(value)); + push.apply(result, isShallow ? value : flatten(value)); } else { result.push(value); } @@ -1008,64 +2558,17 @@ } /** - * Splits `array` into sets, grouped by the result of running each value - * through `callback`. The `callback` is bound to `thisArg` and invoked with 3 - * arguments; (value, index, array). The `callback` argument may also be the - * name of a property to group by. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to iterate over. - * @param {Function|String} callback The function called per iteration or - * property name to group by. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Object} Returns an object of grouped values. - * @example - * - * _.groupBy([1.3, 2.1, 2.4], function(num) { return Math.floor(num); }); - * // => { '1': [1.3], '2': [2.1, 2.4] } - * - * _.groupBy([1.3, 2.1, 2.4], function(num) { return this.floor(num); }, Math); - * // => { '1': [1.3], '2': [2.1, 2.4] } - * - * _.groupBy(['one', 'two', 'three'], 'length'); - * // => { '3': ['one', 'two'], '5': ['three'] } - */ - function groupBy(array, callback, thisArg) { - var result = {}; - if (!array) { - return result; - } - var prop, - value, - index = -1, - isFunc = typeof callback == 'function', - length = array.length; - - if (isFunc && thisArg) { - callback = iteratorBind(callback, thisArg); - } - while (++index < length) { - value = array[index]; - prop = isFunc ? callback(value, index, array) : value[callback]; - (hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(value); - } - return result; - } - - /** * Gets the index at which the first occurrence of `value` is found using * strict equality for comparisons, i.e. `===`. If the `array` is already - * sorted, passing `true` for `isSorted` will run a faster binary search. + * sorted, passing `true` for `fromIndex` will run a faster binary search. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to search. * @param {Mixed} value The value to search for. - * @param {Boolean|Number} [fromIndex=0] The index to start searching from or - * `true` to perform a binary search on a sorted `array`. + * @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 or `-1`. * @example * @@ -1079,19 +2582,14 @@ * // => 2 */ function indexOf(array, value, fromIndex) { - if (!array) { - return -1; - } var index = -1, - length = array.length; + length = array ? array.length : 0; - if (fromIndex) { - if (typeof fromIndex == 'number') { - index = (fromIndex < 0 ? Math.max(0, length + fromIndex) : fromIndex) - 1; - } else { - index = sortedIndex(array, value); - return array[index] === value ? index : -1; - } + if (typeof fromIndex == 'number') { + index = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0) - 1; + } else if (fromIndex) { + index = sortedIndex(array, value); + return array[index] === value ? index : -1; } while (++index < length) { if (array[index] === value) { @@ -1102,57 +2600,112 @@ } /** - * Gets all but the last value of `array`. Pass `n` to exclude the last `n` - * values from the result. + * Gets all but the last element of `array`. If a number `n` is passed, the + * last `n` elements are excluded from the result. If a `callback` function + * is passed, elements at the end of the array are excluded from the result + * as long as the `callback` returns truthy. The `callback` is bound to + * `thisArg` and invoked with three arguments; (value, index, array). + * + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is passed 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. - * @param {Number} [n] The number of elements to return. - * @param {Object} [guard] Internally used to allow this method to work with - * others like `_.map` without using their callback `index` argument for `n`. - * @returns {Array} Returns all but the last value or `n` values of `array`. + * @param {Function|Object|Number|String} [callback|n=1] The function called + * per element or the number of elements to exclude. If a property name or + * object is passed, it will be used to create a "_.pluck" or "_.where" + * style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Array} Returns a slice of `array`. * @example * - * _.initial([3, 2, 1]); - * // => [3, 2] + * _.initial([1, 2, 3]); + * // => [1, 2] + * + * _.initial([1, 2, 3], 2); + * // => [1] + * + * _.initial([1, 2, 3], function(num) { + * return num > 1; + * }); + * // => [1] + * + * var food = [ + * { 'name': 'beet', 'organic': false }, + * { 'name': 'carrot', 'organic': true } + * ]; + * + * // using "_.pluck" callback shorthand + * _.initial(food, 'organic'); + * // => [{ 'name': 'beet', 'organic': false }] + * + * var food = [ + * { 'name': 'banana', 'type': 'fruit' }, + * { 'name': 'beet', 'type': 'vegetable' }, + * { 'name': 'carrot', 'type': 'vegetable' } + * ]; + * + * // using "_.where" callback shorthand + * _.initial(food, { 'type': 'vegetable' }); + * // => [{ 'name': 'banana', 'type': 'fruit' }] */ - function initial(array, n, guard) { + function initial(array, callback, thisArg) { if (!array) { return []; } - return slice.call(array, 0, -((n == undefined || guard) ? 1 : n)); + var n = 0, + length = array.length; + + if (typeof callback != 'number' && callback != null) { + var index = length; + callback = createCallback(callback, thisArg); + while (index-- && callback(array[index], index, array)) { + n++; + } + } else { + n = (callback == null || thisArg) ? 1 : callback || n; + } + return slice.call(array, 0, nativeMin(nativeMax(0, length - n), length)); } /** - * Computes the intersection of all the passed-in arrays. + * Computes the intersection of all the passed-in arrays using strict equality + * for comparisons, i.e. `===`. * * @static * @memberOf _ * @category Arrays * @param {Array} [array1, array2, ...] Arrays to process. - * @returns {Array} Returns a new array of unique values, in order, that are - * present in **all** of the arrays. + * @returns {Array} Returns a new array of unique elements that are present + * in **all** of the arrays. * @example * * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); * // => [1, 2] */ function intersection(array) { - var result = []; - if (!array) { - return result; - } - var value, + var args = arguments, + argsLength = args.length, index = -1, - length = array.length, - others = slice.call(arguments, 1); + length = array ? array.length : 0, + result = []; + outer: while (++index < length) { - value = array[index]; - if (indexOf(result, value) < 0 && - every(others, function(other) { return indexOf(other, value) > -1; })) { + var value = array[index]; + if (indexOf(result, value) < 0) { + var argsIndex = argsLength; + while (--argsIndex) { + if (indexOf(args[argsIndex], value) < 0) { + continue outer; + } + } result.push(value); } } @@ -1160,78 +2713,94 @@ } /** - * Invokes the method named by `methodName` on each element of `array`. - * Additional arguments will be passed to each invoked method. If `methodName` - * is a function it will be invoked for, and `this` bound to, each element - * of `array`. + * Gets the last element of the `array`. If a number `n` is passed, the + * last `n` elements of the `array` are returned. If a `callback` function + * is passed, elements at the end of the array are returned as long as the + * `callback` returns truthy. The `callback` is bound to `thisArg` and + * invoked with three arguments;(value, index, array). * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to iterate over. - * @param {Function|String} methodName The name of the method to invoke or - * the function invoked per iteration. - * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with. - * @returns {Array} Returns a new array of values returned from each invoked method. - * @example * - * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); - * // => [[1, 5, 7], [1, 2, 3]] + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. * - * _.invoke([123, 456], String.prototype.split, ''); - * // => [['1', '2', '3'], ['4', '5', '6']] - */ - function invoke(array, methodName) { - var result = []; - if (!array) { - return result; - } - var args = slice.call(arguments, 2), - index = -1, - length = array.length, - isFunc = typeof methodName == 'function'; - - while (++index < length) { - result[index] = (isFunc ? methodName : array[index][methodName]).apply(array[index], args); - } - return result; - } - - /** - * Gets the last value of the `array`. Pass `n` to return the lasy `n` values - * of the `array`. + * If an object is passed 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. - * @param {Number} [n] The number of elements to return. - * @param {Object} [guard] Internally used to allow this method to work with - * others like `_.map` without using their callback `index` argument for `n`. - * @returns {Mixed} Returns the last value or an array of the last `n` values - * of `array`. + * @param {Function|Object|Number|String} [callback|n] The function called + * per element or the number of elements to return. If a property name or + * object is passed, it will be used to create a "_.pluck" or "_.where" + * style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Mixed} Returns the last element(s) of `array`. * @example * - * _.last([3, 2, 1]); - * // => 1 + * _.last([1, 2, 3]); + * // => 3 + * + * _.last([1, 2, 3], 2); + * // => [2, 3] + * + * _.last([1, 2, 3], function(num) { + * return num > 1; + * }); + * // => [2, 3] + * + * var food = [ + * { 'name': 'beet', 'organic': false }, + * { 'name': 'carrot', 'organic': true } + * ]; + * + * // using "_.pluck" callback shorthand + * _.last(food, 'organic'); + * // => [{ 'name': 'carrot', 'organic': true }] + * + * var food = [ + * { 'name': 'banana', 'type': 'fruit' }, + * { 'name': 'beet', 'type': 'vegetable' }, + * { 'name': 'carrot', 'type': 'vegetable' } + * ]; + * + * // using "_.where" callback shorthand + * _.last(food, { 'type': 'vegetable' }); + * // => [{ 'name': 'beet', 'type': 'vegetable' }, { 'name': 'carrot', 'type': 'vegetable' }] */ - function last(array, n, guard) { + function last(array, callback, thisArg) { if (array) { - var length = array.length; - return (n == undefined || guard) ? array[length - 1] : slice.call(array, -n || length); + var n = 0, + length = array.length; + + if (typeof callback != 'number' && callback != null) { + var index = length; + callback = createCallback(callback, thisArg); + while (index-- && callback(array[index], index, array)) { + n++; + } + } else { + n = callback; + if (n == null || thisArg) { + return array[length - 1]; + } + } + return slice.call(array, nativeMax(0, length - n)); } } /** - * Gets the index at which the last occurrence of `value` is found using - * strict equality for comparisons, i.e. `===`. + * 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. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to search. * @param {Mixed} value The value to search for. - * @param {Number} [fromIndex=array.length-1] The index to start searching from. + * @param {Number} [fromIndex=array.length-1] The index to search from. * @returns {Number} Returns the index of the matched value or `-1`. * @example * @@ -1242,12 +2811,9 @@ * // => 1 */ function lastIndexOf(array, value, fromIndex) { - if (!array) { - return -1; - } - var index = array.length; - if (fromIndex && typeof fromIndex == 'number') { - index = (fromIndex < 0 ? Math.max(0, index + fromIndex) : Math.min(fromIndex, index - 1)) + 1; + var index = array ? array.length : 0; + if (typeof fromIndex == 'number') { + index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1; } while (index--) { if (array[index] === value) { @@ -1258,156 +2824,15 @@ } /** - * Retrieves the maximum value of an `array`. If `callback` is passed, - * it will be executed for each value in the `array` to generate the - * criterion by which the value is ranked. The `callback` is bound to - * `thisArg` and invoked with 3 arguments; (value, index, array). - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to iterate over. - * @param {Function} [callback] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Mixed} Returns the maximum value. - * @example - * - * var stooges = [ - * { 'name': 'moe', 'age': 40 }, - * { 'name': 'larry', 'age': 50 }, - * { 'name': 'curly', 'age': 60 } - * ]; - * - * _.max(stooges, function(stooge) { return stooge.age; }); - * // => { 'name': 'curly', 'age': 60 }; - */ - function max(array, callback, thisArg) { - var computed = -Infinity, - result = computed; - - if (!array) { - return result; - } - var current, - index = -1, - length = array.length; - - if (!callback) { - while (++index < length) { - if (array[index] > result) { - result = array[index]; - } - } - return result; - } - if (thisArg) { - callback = iteratorBind(callback, thisArg); - } - while (++index < length) { - current = callback(array[index], index, array); - if (current > computed) { - computed = current; - result = array[index]; - } - } - return result; - } - - /** - * Retrieves the minimum value of an `array`. If `callback` is passed, - * it will be executed for each value in the `array` to generate the - * criterion by which the value is ranked. The `callback` is bound to `thisArg` - * and invoked with 3 arguments; (value, index, array). - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to iterate over. - * @param {Function} [callback] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Mixed} Returns the minimum value. - * @example - * - * _.min([10, 5, 100, 2, 1000]); - * // => 2 - */ - function min(array, callback, thisArg) { - var computed = Infinity, - result = computed; - - if (!array) { - return result; - } - var current, - index = -1, - length = array.length; - - if (!callback) { - while (++index < length) { - if (array[index] < result) { - result = array[index]; - } - } - return result; - } - if (thisArg) { - callback = iteratorBind(callback, thisArg); - } - while (++index < length) { - current = callback(array[index], index, array); - if (current < computed) { - computed = current; - result = array[index]; - } - } - return result; - } - - /** - * Retrieves the value of a specified property from all elements in `array`. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to iterate over. - * @param {String} property The property to pluck. - * @returns {Array} Returns a new array of property values. - * @example - * - * var stooges = [ - * { 'name': 'moe', 'age': 40 }, - * { 'name': 'larry', 'age': 50 }, - * { 'name': 'curly', 'age': 60 } - * ]; - * - * _.pluck(stooges, 'name'); - * // => ['moe', 'larry', 'curly'] - */ - function pluck(array, property) { - if (!array) { - return []; - } - var index = -1, - length = array.length, - result = Array(length); - - while (++index < length) { - result[index] = array[index][property]; - } - return result; - } - - /** * Creates an array of numbers (positive and/or negative) progressing from - * `start` up to but not including `stop`. This method is a port of Python's - * `range()` function. See http://docs.python.org/library/functions.html#range. + * `start` up to but not including `end`. * * @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 descrement by. + * @param {Number} [step=1] The value to increment or decrement by. * @returns {Array} Returns a new range array. * @example * @@ -1427,14 +2852,17 @@ * // => [] */ function range(start, end, step) { - step || (step = 1); - if (arguments.length < 2) { - end = start || 0; + start = +start || 0; + step = +step || 1; + + if (end == null) { + end = start; start = 0; } - + // use `Array(length)` so V8 will avoid the slower "dictionary" mode + // http://youtu.be/XAqIpGU8ZZk#t=17m25s var index = -1, - length = Math.max(Math.ceil((end - start) / step), 0), + length = nativeMax(0, ceil((end - start) / step)), result = Array(length); while (++index < length) { @@ -1446,122 +2874,76 @@ /** * The opposite of `_.initial`, this method gets all but the first value of - * `array`. Pass `n` to exclude the first `n` values from the result. + * `array`. If a number `n` is passed, the first `n` values are excluded from + * the result. If a `callback` function is passed, elements at the beginning + * of the array are excluded from the result as long as the `callback` returns + * truthy. The `callback` is bound to `thisArg` and invoked with three + * arguments; (value, index, array). + * + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is passed 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 tail + * @alias drop, tail * @category Arrays * @param {Array} array The array to query. - * @param {Number} [n] The number of elements to return. - * @param {Object} [guard] Internally used to allow this method to work with - * others like `_.map` without using their callback `index` argument for `n`. - * @returns {Array} Returns all but the first value or `n` values of `array`. + * @param {Function|Object|Number|String} [callback|n=1] The function called + * per element or the number of elements to exclude. If a property name or + * object is passed, it will be used to create a "_.pluck" or "_.where" + * style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Array} Returns a slice of `array`. * @example * - * _.rest([3, 2, 1]); - * // => [2, 1] - */ - function rest(array, n, guard) { - if (!array) { - return []; - } - return slice.call(array, (n == undefined || guard) ? 1 : n); - } - - /** - * Produces a new array of shuffled `array` values, using a version of the - * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. + * _.rest([1, 2, 3]); + * // => [2, 3] * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to shuffle. - * @returns {Array} Returns a new shuffled array. - * @example + * _.rest([1, 2, 3], 2); + * // => [3] * - * _.shuffle([1, 2, 3, 4, 5, 6]); - * // => [4, 1, 6, 3, 5, 2] - */ - function shuffle(array) { - if (!array) { - return []; - } - var rand, - index = -1, - length = array.length, - result = Array(length); - - while (++index < length) { - rand = Math.floor(Math.random() * (index + 1)); - result[index] = result[rand]; - result[rand] = array[index]; - } - return result; - } - - /** - * Produces a new sorted array, ranked in ascending order by the results of - * running each element of `array` through `callback`. The `callback` is - * bound to `thisArg` and invoked with 3 arguments; (value, index, array). The - * `callback` argument may also be the name of a property to sort by (e.g. 'length'). + * _.rest([1, 2, 3], function(num) { + * return num < 3; + * }); + * // => [3] * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to iterate over. - * @param {Function|String} callback The function called per iteration or - * property name to sort by. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Array} Returns a new array of sorted values. - * @example + * var food = [ + * { 'name': 'banana', 'organic': true }, + * { 'name': 'beet', 'organic': false }, + * ]; * - * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); - * // => [3, 1, 2] + * // using "_.pluck" callback shorthand + * _.rest(food, 'organic'); + * // => [{ 'name': 'beet', 'organic': false }] * - * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math); - * // => [3, 1, 2] + * var food = [ + * { 'name': 'apple', 'type': 'fruit' }, + * { 'name': 'banana', 'type': 'fruit' }, + * { 'name': 'beet', 'type': 'vegetable' } + * ]; * - * _.sortBy(['larry', 'brendan', 'moe'], 'length'); - * // => ['moe', 'larry', 'brendan'] + * // using "_.where" callback shorthand + * _.rest(food, { 'type': 'fruit' }); + * // => [{ 'name': 'beet', 'type': 'vegetable' }] */ - function sortBy(array, callback, thisArg) { - if (!array) { - return []; - } - if (typeof callback == 'string') { - var prop = callback; - callback = function(array) { return array[prop]; }; - } else if (thisArg) { - callback = iteratorBind(callback, thisArg); - } - var index = -1, - length = array.length, - result = Array(length); - - while (++index < length) { - result[index] = { - 'criteria': callback(array[index], index, array), - 'value': array[index] - }; - } - result.sort(function(left, right) { - var a = left.criteria, - b = right.criteria; - - if (a === undefined) { - return 1; - } - if (b === undefined) { - return -1; + function rest(array, callback, thisArg) { + if (typeof callback != 'number' && callback != null) { + var n = 0, + index = -1, + length = array ? array.length : 0; + + callback = createCallback(callback, thisArg); + while (++index < length && callback(array[index], index, array)) { + n++; } - return a < b ? -1 : a > b ? 1 : 0; - }); - - while (length--) { - result[length] = result[length].value; + } else { + n = (callback == null || thisArg) ? 1 : nativeMax(0, callback); } - return result; + return slice.call(array, n); } /** @@ -1569,61 +2951,69 @@ * should be inserted into `array` in order to maintain the sort order of the * sorted `array`. If `callback` is passed, it will be executed for `value` and * each element in `array` to compute their sort ranking. The `callback` is - * bound to `thisArg` and invoked with 1 argument; (value). + * bound to `thisArg` and invoked with one argument; (value). + * + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is passed 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 iterate over. * @param {Mixed} value The value to evaluate. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. + * @param {Function|Object|String} [callback=identity] The function called per + * iteration. If a property name or object is passed, it will be used to create + * a "_.pluck" or "_.where" style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. * @returns {Number} Returns the index at which the value should be inserted * into `array`. * @example * - * _.sortedIndex([20, 30, 40], 35); + * _.sortedIndex([20, 30, 50], 40); + * // => 2 + * + * // using "_.pluck" callback shorthand + * _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x'); * // => 2 * * var dict = { - * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'thirty-five': 35, 'fourty': 40 } + * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 } * }; * - * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { + * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) { * return dict.wordToNumber[word]; * }); * // => 2 * - * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { + * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) { * return this.wordToNumber[word]; * }, dict); * // => 2 */ function sortedIndex(array, value, callback, thisArg) { - if (!array) { - return 0; - } - var mid, - low = 0, - high = array.length; - - if (callback) { - value = callback.call(thisArg, value); - while (low < high) { - mid = (low + high) >>> 1; - callback.call(thisArg, array[mid]) < value ? low = mid + 1 : high = mid; - } - } else { - while (low < high) { - mid = (low + high) >>> 1; - array[mid] < value ? low = mid + 1 : high = mid; - } + var low = 0, + high = array ? array.length : low; + + // explicitly reference `identity` for better inlining in Firefox + callback = callback ? createCallback(callback, thisArg, 1) : identity; + value = callback(value); + + while (low < high) { + var mid = (low + high) >>> 1; + (callback(array[mid]) < value) + ? low = mid + 1 + : high = mid; } return low; } /** - * Computes the union of the passed-in arrays. + * Computes the union of the passed-in arrays using strict equality for + * comparisons, i.e. `===`. * * @static * @memberOf _ @@ -1637,26 +3027,22 @@ * // => [1, 2, 3, 101, 10] */ function union() { - var index = -1, - result = [], - flattened = concat.apply(result, arguments), - length = flattened.length; - - while (++index < length) { - if (indexOf(result, flattened[index]) < 0) { - result.push(flattened[index]); - } - } - return result; + return uniq(concat.apply(arrayRef, arguments)); } /** - * Produces a duplicate-value-free version of the `array` using strict equality + * Creates a duplicate-value-free version of the `array` using strict equality * for comparisons, i.e. `===`. If the `array` is already sorted, passing `true` - * for `isSorted` will run a faster algorithm. If `callback` is passed, - * each value of `array` is passed through a transformation `callback` before - * uniqueness is computed. The `callback` is bound to `thisArg` and invoked - * with 3 arguments; (value, index, array). + * for `isSorted` will run a faster algorithm. If `callback` is passed, each + * element of `array` is passed through a callback` before uniqueness is computed. + * The `callback` is bound to `thisArg` and invoked with three arguments; (value, index, array). + * + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is passed for `callback`, the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false`. * * @static * @memberOf _ @@ -1664,15 +3050,17 @@ * @category Arrays * @param {Array} array The array to process. * @param {Boolean} [isSorted=false] A flag to indicate that the `array` is already sorted. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. + * @param {Function|Object|String} [callback=identity] The function called per + * iteration. If a property name or object is passed, it will be used to create + * a "_.pluck" or "_.where" style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. * @returns {Array} Returns a duplicate-value-free array. * @example * * _.uniq([1, 2, 1, 3, 1]); * // => [1, 2, 3] * - * _.uiq([1, 1, 2, 2, 3], true); + * _.uniq([1, 1, 2, 2, 3], true); * // => [1, 2, 3] * * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return Math.floor(num); }); @@ -1680,43 +3068,45 @@ * * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math); * // => [1, 2, 3] + * + * // using "_.pluck" callback shorthand + * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }, { 'x': 2 }] */ function uniq(array, isSorted, callback, thisArg) { - var result = []; - if (!array) { - return result; - } - var computed, - index = -1, - length = array.length, - seen = []; + var index = -1, + length = array ? array.length : 0, + result = [], + seen = result; - // juggle arguments - if (typeof isSorted == 'function') { + if (typeof isSorted != 'boolean' && isSorted != null) { thisArg = callback; callback = isSorted; isSorted = false; } - if (!callback) { - callback = identity; - } else if (thisArg) { - callback = iteratorBind(callback, thisArg); + if (callback != null) { + seen = []; + callback = createCallback(callback, thisArg); } while (++index < length) { - computed = callback(array[index], index, array); + var value = array[index], + computed = callback ? callback(value, index, array) : value; + if (isSorted ? !index || seen[seen.length - 1] !== computed : indexOf(seen, computed) < 0 ) { - seen.push(computed); - result.push(array[index]); + if (callback) { + seen.push(computed); + } + result.push(value); } } return result; } /** - * Produces a new array with all occurrences of the passed values removed using + * Creates an array with all occurrences of the passed values removed using * strict equality for comparisons, i.e. `===`. * * @static @@ -1731,44 +3121,38 @@ * // => [2, 3, 4] */ function without(array) { - var result = []; - if (!array) { - return result; - } - var excluded = slice.call(arguments, 1), - index = -1, - length = array.length; + var index = -1, + length = array.length, + result = []; while (++index < length) { - if (indexOf(excluded, array[index]) < 0) { - result.push(array[index]); + var value = array[index]; + if (indexOf(arguments, value, 1) < 0) { + result.push(value); } } - return result; + return result } /** - * Merges together the values of each of the arrays with the value at the - * corresponding position. Useful for separate data sources that are coordinated - * through matching array indexes. For a matrix of nested arrays, `_.zip.apply(...)` - * can transpose the matrix in a similar fashion. + * Groups the elements of each array at their corresponding indexes. Useful for + * separate data sources that are coordinated through matching array indexes. + * For a matrix of nested arrays, `_.zip.apply(...)` can transpose the matrix + * in a similar fashion. * * @static * @memberOf _ * @category Arrays * @param {Array} [array1, array2, ...] Arrays to process. - * @returns {Array} Returns a new array of merged arrays. + * @returns {Array} Returns a new array of grouped elements. * @example * - * _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]); - * // => [['moe', 30, true], ['larry', 40, false], ['curly', 50, false]] + * _.zip(['moe', 'larry'], [30, 40], [true, false]); + * // => [['moe', 30, true], ['larry', 40, false]] */ function zip(array) { - if (!array) { - return []; - } var index = -1, - length = max(pluck(arguments, 'length')), + length = array ? max(pluck(arguments, 'length')) : 0, result = Array(length); while (++index < length) { @@ -1777,11 +3161,48 @@ return result; } + /** + * Creates an object composed from arrays of `keys` and `values`. Pass either + * a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`, or + * two arrays, one of `keys` and one of corresponding `values`. + * + * @static + * @memberOf _ + * @alias object + * @category Arrays + * @param {Array} keys The array of keys. + * @param {Array} [values=[]] The array of values. + * @returns {Object} Returns an object composed of the given keys and + * corresponding values. + * @example + * + * _.zipObject(['moe', 'larry'], [30, 40]); + * // => { 'moe': 30, 'larry': 40 } + */ + function zipObject(keys, values) { + var index = -1, + length = keys ? keys.length : 0, + result = {}; + + while (++index < length) { + var key = keys[index]; + if (values) { + result[key] = values[index]; + } else { + result[key[0]] = key[1]; + } + } + return result; + } + /*--------------------------------------------------------------------------*/ /** - * Creates a new function that is restricted to executing only after it is - * called `n` times. + * If `n` is greater than `0`, a function is created that is restricted to + * executing `func`, with the `this` binding and arguments of the created + * function, only after it is called `n` times. If `n` is less than `1`, + * `func` is executed immediately, without a `this` binding or additional + * arguments, and its result is returned. * * @static * @memberOf _ @@ -1810,99 +3231,39 @@ } /** - * Creates a new function that, when called, invokes `func` with the `this` + * Creates a function that, when called, invokes `func` with the `this` * binding of `thisArg` and prepends any additional `bind` arguments to those - * passed to the bound function. Lazy defined methods may be bound by passing - * the object they are bound to as `func` and the method name as `thisArg`. + * passed to the bound function. * * @static * @memberOf _ * @category Functions - * @param {Function|Object} func The function to bind or the object the method belongs to. - * @param {Mixed} [thisArg] The `this` binding of `func` or the method name. + * @param {Function} func The function to bind. + * @param {Mixed} [thisArg] The `this` binding of `func`. * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. * @returns {Function} Returns the new bound function. * @example * - * // basic bind * var func = function(greeting) { - * return greeting + ': ' + this.name; + * return greeting + ' ' + this.name; * }; * * func = _.bind(func, { 'name': 'moe' }, 'hi'); * func(); - * // => 'hi: moe' - * - * // lazy bind - * var object = { - * 'name': 'moe', - * 'greet': function(greeting) { - * return greeting + ': ' + this.name; - * } - * }; - * - * var func = _.bind(object, 'greet', 'hi'); - * func(); - * // => 'hi: moe' - * - * object.greet = function(greeting) { - * return greeting + ', ' + this.name + '!'; - * }; - * - * func(); - * // => 'hi, moe!' + * // => 'hi moe' */ function bind(func, thisArg) { - var methodName, - isFunc = toString.call(func) == funcClass; - - // juggle arguments - if (!isFunc) { - methodName = thisArg; - thisArg = func; - } - // use if `Function#bind` is faster - else if (nativeBind) { - return nativeBind.call.apply(nativeBind, arguments); - } - - var partialArgs = slice.call(arguments, 2); - - function bound() { - // `Function#bind` spec - // http://es5.github.com/#x15.3.4.5 - var args = arguments, - thisBinding = thisArg; - - if (!isFunc) { - func = thisArg[methodName]; - } - if (partialArgs.length) { - args = args.length - ? concat.apply(partialArgs, args) - : partialArgs; - } - if (this instanceof bound) { - // get `func` instance if `bound` is invoked in a `new` expression - noop.prototype = func.prototype; - thisBinding = new noop; - - // mimic the constructor's `return` behavior - // http://es5.github.com/#x13.2.2 - var result = func.apply(thisBinding, args); - return objectTypes[typeof result] && result !== null - ? result - : thisBinding - } - return func.apply(thisBinding, args); - } - - return bound; + // use `Function#bind` if it exists and is fast + // (in V8 `Function#bind` is slower except when partially applied) + return support.fastBind || (nativeBind && arguments.length > 2) + ? nativeBind.call.apply(nativeBind, arguments) + : createBound(func, thisArg, slice.call(arguments, 2)); } /** * Binds methods on `object` to `object`, overwriting the existing method. - * If no method names are provided, all the function properties of `object` + * Method names may be specified as individual arguments or as arrays of method + * names. If no method names are provided, all the function properties of `object` * will be bound. * * @static @@ -1910,37 +3271,35 @@ * @category Functions * @param {Object} object The object to bind and assign the bound methods to. * @param {String} [methodName1, methodName2, ...] Method names on the object to bind. - * @returns {Object} Returns the `object`. + * @returns {Object} Returns `object`. * @example * - * var buttonView = { - * 'label': 'lodash', - * 'onClick': function() { alert('clicked: ' + this.label); }, - * 'onHover': function() { console.log('hovering: ' + this.label); } + * var view = { + * 'label': 'docs', + * 'onClick': function() { alert('clicked ' + this.label); } * }; * - * _.bindAll(buttonView); - * jQuery('#lodash_button').on('click', buttonView.onClick); - * // => When the button is clicked, `this.label` will have the correct value + * _.bindAll(view); + * jQuery('#docs').on('click', view.onClick); + * // => alerts 'clicked docs', when the button is clicked */ function bindAll(object) { - var funcs = arguments, - index = 1; + var funcs = concat.apply(arrayRef, arguments), + index = funcs.length > 1 ? 0 : (funcs = functions(object), -1), + length = funcs.length; - if (funcs.length == 1) { - index = 0; - funcs = functions(object); - } - for (var length = funcs.length; index < length; index++) { - object[funcs[index]] = bind(object[funcs[index]], object); + while (++index < length) { + var key = funcs[index]; + object[key] = bind(object[key], object); } return object; } /** - * Creates a new function that is the composition of the passed functions, + * Creates a function that is the composition of the passed functions, * where each function consumes the return value of the function that follows. - * In math terms, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`. + * For example, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`. + * Each function is executed with the `this` binding of the composed function. * * @static * @memberOf _ @@ -1949,11 +3308,11 @@ * @returns {Function} Returns the new composed function. * @example * - * var greet = function(name) { return 'hi: ' + name; }; + * var greet = function(name) { return 'hi ' + name; }; * var exclaim = function(statement) { return statement + '!'; }; * var welcome = _.compose(exclaim, greet); * welcome('moe'); - * // => 'hi: moe!' + * // => 'hi moe!' */ function compose() { var funcs = arguments; @@ -1969,7 +3328,99 @@ } /** - * Creates a new function that will delay the execution of `func` until after + * 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`. + * + * Note: All Lo-Dash methods, that accept a `callback` argument, use `_.createCallback`. + * + * @static + * @memberOf _ + * @category Functions + * @param {Mixed} [func=identity] The value to convert to a callback. + * @param {Mixed} [thisArg] The `this` binding of the created callback. + * @param {Number} [argCount=3] The number of arguments the callback accepts. + * @returns {Function} Returns a callback function. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 } + * ]; + * + * // wrap to create custom callback shorthands + * _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) { + * var match = /^(.+?)__([gl]t)(.+)$/.exec(callback); + * return !match ? func(callback, thisArg) : function(object) { + * return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3]; + * }; + * }); + * + * _.filter(stooges, 'age__gt45'); + * // => [{ 'name': 'larry', 'age': 50 }] + * + * // create mixins with support for "_.pluck" and "_.where" callback shorthands + * _.mixin({ + * 'toLookup': function(collection, callback, thisArg) { + * callback = _.createCallback(callback, thisArg); + * return _.reduce(collection, function(result, value, index, collection) { + * return (result[callback(value, index, collection)] = value, result); + * }, {}); + * } + * }); + * + * _.toLookup(stooges, 'name'); + * // => { 'moe': { 'name': 'moe', 'age': 40 }, 'larry': { 'name': 'larry', 'age': 50 } } + */ + function createCallback(func, thisArg, argCount) { + if (func == null) { + return identity; + } + var type = typeof func; + if (type != 'function') { + if (type != 'object') { + return function(object) { + return object[func]; + }; + } + var props = keys(func); + return function(object) { + var length = props.length, + result = false; + while (length--) { + if (!(result = object[props[length]] === func[props[length]])) { + break; + } + } + return result; + }; + } + if (typeof thisArg != 'undefined') { + if (argCount === 1) { + return function(value) { + return func.call(thisArg, value); + }; + } + if (argCount === 2) { + return function(a, b) { + return func.call(thisArg, a, b); + }; + } + if (argCount === 4) { + return function(accumulator, value, index, collection) { + return func.call(thisArg, accumulator, value, index, collection); + }; + } + return function(value, index, collection) { + return func.call(thisArg, value, index, collection); + }; + } + return func; + } + + /** + * Creates a function that will delay the execution of `func` until after * `wait` milliseconds have elapsed since the last time it was invoked. Pass * `true` for `immediate` to cause debounce to invoke `func` on the leading, * instead of the trailing, edge of the `wait` timeout. Subsequent calls to @@ -1995,12 +3446,11 @@ timeoutId; function delayed() { - timeoutId = undefined; + timeoutId = null; if (!immediate) { - func.apply(thisArg, args); + result = func.apply(thisArg, args); } } - return function() { var isImmediate = immediate && !timeoutId; args = arguments; @@ -2017,52 +3467,53 @@ } /** - * Executes the `func` function after `wait` milliseconds. Additional arguments - * are passed to `func` when it is invoked. + * Defers executing the `func` function until the current call stack has cleared. + * Additional arguments will be passed to `func` when it is invoked. * * @static * @memberOf _ * @category Functions - * @param {Function} func The function to delay. - * @param {Number} wait The number of milliseconds to delay execution. + * @param {Function} func The function to defer. * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. - * @returns {Number} Returns the `setTimeout` timeout id. + * @returns {Number} Returns the timer id. * @example * - * var log = _.bind(console.log, console); - * _.delay(log, 1000, 'logged later'); - * // => 'logged later' (Appears after one second.) + * _.defer(function() { alert('deferred'); }); + * // returns from the function before `alert` is called */ - function delay(func, wait) { - var args = slice.call(arguments, 2); - return setTimeout(function() { return func.apply(undefined, args); }, wait); + function defer(func) { + var args = slice.call(arguments, 1); + return setTimeout(function() { func.apply(undefined, args); }, 1); } /** - * Defers executing the `func` function until the current call stack has cleared. - * Additional arguments are passed to `func` when it is invoked. + * Executes the `func` function after `wait` milliseconds. Additional arguments + * will be passed to `func` when it is invoked. * * @static * @memberOf _ * @category Functions - * @param {Function} func The function to defer. + * @param {Function} func The function to delay. + * @param {Number} wait The number of milliseconds to delay execution. * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. - * @returns {Number} Returns the `setTimeout` timeout id. + * @returns {Number} Returns the timer id. * @example * - * _.defer(function() { alert('deferred'); }); - * // returns from the function before `alert` is called + * var log = _.bind(console.log, console); + * _.delay(log, 1000, 'logged later'); + * // => 'logged later' (Appears after one second.) */ - function defer(func) { - var args = slice.call(arguments, 1); - return setTimeout(function() { return func.apply(undefined, args); }, 1); + function delay(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function() { func.apply(undefined, args); }, wait); } /** - * Creates a new function that memoizes the result of `func`. If `resolver` is + * Creates a function that memoizes the result of `func`. If `resolver` is * passed, it will be used to determine the cache key for storing the result * based on the arguments passed to the memoized function. By default, the first - * argument passed to the memoized function is used as the cache key. + * argument passed to the memoized function is used as the cache key. The `func` + * is executed with the `this` binding of the memoized function. * * @static * @memberOf _ @@ -2079,16 +3530,17 @@ function memoize(func, resolver) { var cache = {}; return function() { - var prop = resolver ? resolver.apply(this, arguments) : arguments[0]; - return hasOwnProperty.call(cache, prop) - ? cache[prop] - : (cache[prop] = func.apply(this, arguments)); + var key = String(resolver ? resolver.apply(this, arguments) : arguments[0]); + return hasOwnProperty.call(cache, key) + ? cache[key] + : (cache[key] = func.apply(this, arguments)); }; } /** - * Creates a new function that is restricted to one execution. Repeat calls to - * the function will return the value of the first call. + * 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 + * with the `this` binding of the created function. * * @static * @memberOf _ @@ -2100,11 +3552,11 @@ * var initialize = _.once(createApplication); * initialize(); * initialize(); - * // Application is only created once. + * // `initialize` executes `createApplication` once */ function once(func) { - var result, - ran = false; + var ran, + result; return function() { if (ran) { @@ -2112,15 +3564,17 @@ } ran = true; result = func.apply(this, arguments); + + // clear the `func` variable so the function may be garbage collected + func = null; return result; }; } /** - * Creates a new function that, when called, invokes `func` with any additional - * `partial` arguments prepended to those passed to the partially applied - * function. This method is similar `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 passed to the new function. This + * method is similar to `_.bind`, except it does **not** alter the `this` binding. * * @static * @memberOf _ @@ -2130,35 +3584,21 @@ * @returns {Function} Returns the new partially applied function. * @example * - * var greet = function(greeting, name) { return greeting + ': ' + name; }; + * var greet = function(greeting, name) { return greeting + ' ' + name; }; * var hi = _.partial(greet, 'hi'); * hi('moe'); - * // => 'hi: moe' + * // => 'hi moe' */ function partial(func) { - var args = slice.call(arguments, 1), - argsLength = args.length; - - return function() { - var result, - others = arguments; - - if (others.length) { - args.length = argsLength; - push.apply(args, others); - } - result = args.length == 1 ? func.call(this, args[0]) : func.apply(this, args); - args.length = argsLength; - return result; - }; + return createBound(func, slice.call(arguments, 1)); } /** - * Creates a new function that, when executed, will only call the `func` - * function at most once per every `wait` milliseconds. If the throttled function - * is invoked more than once, `func` will also be called on the trailing edge - * of the `wait` timeout. Subsequent calls to the throttled function will - * return the result of the last `func` call. + * Creates a function that, when executed, will only call the `func` + * function at most once per every `wait` milliseconds. If the throttled + * function is invoked more than once during the `wait` timeout, `func` will + * also be called on the trailing edge of the timeout. Subsequent calls to the + * throttled function will return the result of the last `func` call. * * @static * @memberOf _ @@ -2180,55 +3620,54 @@ function trailingCall() { lastCalled = new Date; - timeoutId = undefined; - func.apply(thisArg, args); + timeoutId = null; + result = func.apply(thisArg, args); } - return function() { var now = new Date, - remain = wait - (now - lastCalled); + remaining = wait - (now - lastCalled); args = arguments; thisArg = this; - if (remain <= 0) { + if (remaining <= 0) { + clearTimeout(timeoutId); + timeoutId = null; lastCalled = now; result = func.apply(thisArg, args); } else if (!timeoutId) { - timeoutId = setTimeout(trailingCall, remain); + timeoutId = setTimeout(trailingCall, remaining); } return result; }; } /** - * Create a new function that passes the `func` function to the `wrapper` - * function as its first argument. Additional arguments are appended to those - * passed to the `wrapper` function. + * Creates a function that passes `value` to the `wrapper` function as its + * first argument. Additional arguments passed to the function are appended + * to those passed to the `wrapper` function. The `wrapper` is executed with + * the `this` binding of the created function. * * @static * @memberOf _ * @category Functions - * @param {Function} func The function to wrap. + * @param {Mixed} value The value to wrap. * @param {Function} wrapper The wrapper function. - * @param {Mixed} [arg1, arg2, ...] Arguments to append to those passed to the wrapper. * @returns {Function} Returns the new function. * @example * - * var hello = function(name) { return 'hello: ' + name; }; + * var hello = function(name) { return 'hello ' + name; }; * hello = _.wrap(hello, function(func) { * return 'before, ' + func('moe') + ', after'; * }); * hello(); - * // => 'before, hello: moe, after' + * // => 'before, hello moe, after' */ - function wrap(func, wrapper) { + function wrap(value, wrapper) { return function() { - var args = [func]; - if (arguments.length) { - push.apply(args, arguments); - } + var args = [value]; + push.apply(args, arguments); return wrapper.apply(this, args); }; } @@ -2236,755 +3675,8 @@ /*--------------------------------------------------------------------------*/ /** - * Create a shallow clone of the `value`. Any nested objects or arrays will be - * assigned by reference and not cloned. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to clone. - * @returns {Mixed} Returns the cloned `value`. - * @example - * - * _.clone({ 'name': 'moe' }); - * // => { 'name': 'moe' }; - */ - function clone(value) { - return objectTypes[typeof value] && value !== null - ? (isArray(value) ? value.slice() : extend({}, value)) - : value; - } - - /** - * Assigns missing properties in `object` with default values from the defaults - * objects. As soon as a property is set, additional defaults of the same - * property will be ignored. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to populate. - * @param {Object} [defaults1, defaults2, ...] The defaults objects to apply to `object`. - * @returns {Object} Returns `object`. - * @example - * - * var iceCream = { 'flavor': 'chocolate' }; - * _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); - * // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' } - */ - var defaults = createIterator(extendIteratorOptions, { - 'inLoop': 'if (object[index] == undefined)' + extendIteratorOptions.inLoop - }); - - /** - * Copies enumerable properties from the source objects to the `destination` object. - * Subsequent sources will overwrite propery assignments of previous sources. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The destination object. - * @param {Object} [source1, source2, ...] The source objects. - * @returns {Object} Returns the destination object. - * @example - * - * _.extend({ 'name': 'moe' }, { 'age': 40 }); - * // => { 'name': 'moe', 'age': 40 } - */ - var extend = createIterator(extendIteratorOptions); - - /** - * Iterates over `object`'s own and inherited enumerable properties, executing - * the `callback` for each property. The `callback` is bound to `thisArg` and - * invoked with 3 arguments; (value, key, object). - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to iterate over. - * @param {Function} callback The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Object} Returns the `object`. - * @example - * - * function Dog(name) { - * this.name = name; - * } - * - * Dog.prototype.bark = function() { - * alert('Woof, woof!'); - * }; - * - * _.forIn(new Dog('Dagny'), function(value, key) { - * alert(key); - * }); - * // => alerts 'name' and 'bark' (order is not guaranteed) - */ - var forIn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions, { - 'useHas': false - }); - - /** - * Iterates over `object`'s own enumerable properties, executing the `callback` - * for each property. The `callback` is bound to `thisArg` and invoked with 3 - * arguments; (value, key, object). - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to iterate over. - * @param {Function} callback The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Object} Returns the `object`. - * @example - * - * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { - * alert(key); - * }); - * // => alerts '0', '1', and 'length' (order is not guaranteed) - */ - var forOwn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions); - - /** - * Produces a sorted array of the enumerable properties, own and inherited, - * of `object` that have function values. - * - * @static - * @memberOf _ - * @alias methods - * @category Objects - * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property names that have function values. - * @example - * - * _.functions(_); - * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] - */ - var functions = createIterator({ - 'args': 'object', - 'init': '[]', - 'useHas': false, - 'inLoop': 'if (toString.call(object[index]) == funcClass) result.push(index)', - 'bottom': 'result.sort()' - }); - - /** - * Checks if the specified object `property` exists and is a direct property, - * instead of an inherited property. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to check. - * @param {String} property The property to check for. - * @returns {Boolean} Returns `true` if key is a direct property, else `false`. - * @example - * - * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); - * // => true - */ - function has(object, property) { - return hasOwnProperty.call(object, property); - } - - /** - * Checks if a `value` is an `arguments` object. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} 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 - */ - var isArguments = function(value) { - return toString.call(value) == '[object Arguments]'; - }; - // fallback for browser like IE < 9 which detect `arguments` as `[object Object]` - if (!isArguments(arguments)) { - isArguments = function(value) { - return !!(value && hasOwnProperty.call(value, 'callee')); - }; - } - - /** - * Checks if a `value` is an array. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is an array, else `false`. - * @example - * - * (function() { return _.isArray(arguments); })(); - * // => false - * - * _.isArray([1, 2, 3]); - * // => true - */ - var isArray = nativeIsArray || function(value) { - return toString.call(value) == arrayClass; - }; - - /** - * Checks if a `value` is a boolean (`true` or `false`) value. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a boolean value, else `false`. - * @example - * - * _.isBoolean(null); - * // => false - */ - function isBoolean(value) { - return value === true || value === false || toString.call(value) == boolClass; - } - - /** - * Checks if a `value` is a date. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a date, else `false`. - * @example - * - * _.isDate(new Date); - * // => true - */ - function isDate(value) { - return toString.call(value) == dateClass; - } - - /** - * Checks if a `value` is a DOM element. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a DOM element, else `false`. - * @example - * - * _.isElement(document.body); - * // => true - */ - function isElement(value) { - return !!(value && value.nodeType == 1); - } - - /** - * Checks if a `value` is empty. Arrays or strings 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 the `value` is empty, else `false`. - * @example - * - * _.isEmpty([1, 2, 3]); - * // => false - * - * _.isEmpty({}); - * // => true - */ - var isEmpty = createIterator({ - 'args': 'value', - 'init': 'true', - 'top': - 'var className = toString.call(value);\n' + - 'if (className == arrayClass || className == stringClass) return !value.length', - 'inLoop': { - 'object': 'return false' - } - }); - - /** - * Performs a deep comparison between two values to determine if they are - * equivalent to each other. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} a The value to compare. - * @param {Mixed} b The other value to compare. - * @param {Array} [stack] Internally used to keep track of "seen" objects to - * avoid circular references. - * @returns {Boolean} Returns `true` if the values are equvalent, else `false`. - * @example - * - * var moe = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; - * var clone = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; - * - * moe == clone; - * // => false - * - * _.isEqual(moe, clone); - * // => true - */ - function isEqual(a, b, stack) { - stack || (stack = []); - - // exit early for identical values - if (a === b) { - // treat `+0` vs. `-0` as not equal - return a !== 0 || (1 / a == 1 / b); - } - // a strict comparison is necessary because `null == undefined` - if (a == undefined || b == undefined) { - return a === b; - } - // unwrap any wrapped objects - if (a._chain) { - a = a._wrapped; - } - if (b._chain) { - b = b._wrapped; - } - // invoke a custom `isEqual` method if one is provided - if (a.isEqual && toString.call(a.isEqual) == funcClass) { - return a.isEqual(b); - } - if (b.isEqual && toString.call(b.isEqual) == funcClass) { - return b.isEqual(a); - } - // compare [[Class]] names - var className = toString.call(a); - if (className != toString.call(b)) { - return false; - } - switch (className) { - // strings, numbers, dates, and booleans are compared by value - case stringClass: - // primitives and their corresponding object instances are equivalent; - // thus, `'5'` is quivalent to `new String('5')` - return a == String(b); - - case numberClass: - // treat `NaN` vs. `NaN` as equal - return a != +a - ? b != +b - // but treat `+0` vs. `-0` as not equal - : (a == 0 ? (1 / a == 1 / b) : a == +b); - - case boolClass: - case dateClass: - // coerce dates and booleans to numeric values, dates to milliseconds and booleans to 1 or 0; - // treat invalid dates coerced to `NaN` as not equal - return +a == +b; - - // regexps are compared by their source and flags - case regexpClass: - return a.source == b.source && - a.global == b.global && - a.multiline == b.multiline && - a.ignoreCase == b.ignoreCase; - } - if (typeof a != 'object' || typeof b != 'object') { - return false; - } - // Assume equality for cyclic structures. The algorithm for detecting cyclic - // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. - var length = stack.length; - while (length--) { - // Linear search. Performance is inversely proportional to the number of - // unique nested structures. - if (stack[length] == a) { - return true; - } - } - - var index = -1, - result = true, - size = 0; - - // add the first collection to the stack of traversed objects - stack.push(a); - - // recursively compare objects and arrays - if (className == arrayClass) { - // compare array lengths to determine if a deep comparison is necessary - size = a.length; - result = size == b.length; - - if (result) { - // deep compare the contents, ignoring non-numeric properties - while (size--) { - if (!(result = isEqual(a[size], b[size], stack))) { - break; - } - } - } - } else { - // objects with different constructors are not equivalent - if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) { - return false; - } - // deep compare objects. - for (var prop in a) { - if (hasOwnProperty.call(a, prop)) { - // count the number of properties. - size++; - // deep compare each property value. - if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) { - break; - } - } - } - // ensure both objects have the same number of properties - if (result) { - for (prop in b) { - // Adobe's JS engine, embedded in applications like InDesign, has a - // bug that causes `!size--` to throw an error so it must be wrapped - // in parentheses. - // https://github.com/documentcloud/underscore/issues/355 - if (hasOwnProperty.call(b, prop) && !(size--)) { - break; - } - } - result = !size; - } - // handle JScript [[DontEnum]] bug - if (result && hasDontEnumBug) { - while (++index < 7) { - prop = shadowed[index]; - if (hasOwnProperty.call(a, prop)) { - if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) { - break; - } - } - } - } - } - // remove the first collection from the stack of traversed objects - stack.pop(); - return result; - } - - /** - * Checks if a `value` is a finite number. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a finite number, else `false`. - * @example - * - * _.isFinite(-101); - * // => true - * - * _.isFinite('10'); - * // => false - * - * _.isFinite(Infinity); - * // => false - */ - function isFinite(value) { - return nativeIsFinite(value) && toString.call(value) == numberClass; - } - - /** - * Checks if a `value` is a function. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a function, else `false`. - * @example - * - * _.isFunction(''.concat); - * // => true - */ - function isFunction(value) { - return toString.call(value) == funcClass; - } - - /** - * Checks if a `value` is the language type of Object. - * (e.g. arrays, functions, objects, regexps, `new Number(0)`, and `new String('')`) - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is an object, else `false`. - * @example - * - * _.isObject({}); - * // => true - * - * _.isObject(1); - * // => false - */ - function isObject(value) { - // check if the value is the ECMAScript language type of Object - // http://es5.github.com/#x8 - return objectTypes[typeof value] && value !== null; - } - - /** - * Checks if a `value` is `NaN`. - * Note: This is not the same as native `isNaN`, which will return true for - * `undefined` and other values. See http://es5.github.com/#x15.1.2.4. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is `NaN`, else `false`. - * @example - * - * _.isNaN(NaN); - * // => true - * - * _.isNaN(new Number(NaN)); - * // => true - * - * isNaN(undefined); - * // => true - * - * _.isNaN(undefined); - * // => false - */ - 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) - return toString.call(value) == numberClass && value != +value - } - - /** - * Checks if a `value` is `null`. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is `null`, else `false`. - * @example - * - * _.isNull(null); - * // => true - * - * _.isNull(undefined); - * // => false - */ - function isNull(value) { - return value === null; - } - - /** - * Checks if a `value` is a number. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a number, else `false`. - * @example - * - * _.isNumber(8.4 * 5; - * // => true - */ - function isNumber(value) { - return toString.call(value) == numberClass; - } - - /** - * Checks if a `value` is a regular expression. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a regular expression, else `false`. - * @example - * - * _.isRegExp(/moe/); - * // => true - */ - function isRegExp(value) { - return toString.call(value) == regexpClass; - } - - /** - * Checks if a `value` is a string. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a string, else `false`. - * @example - * - * _.isString('moe'); - * // => true - */ - function isString(value) { - return toString.call(value) == stringClass; - } - - /** - * Checks if a `value` is `undefined`. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is `undefined`, else `false`. - * @example - * - * _.isUndefined(void 0); - * // => true - */ - function isUndefined(value) { - return value === undefined; - } - - /** - * Produces an array of object`'s own enumerable property names. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property names. - * @example - * - * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); - * // => ['one', 'two', 'three'] (order is not guaranteed) - */ - var keys = !nativeKeys ? shimKeys : function(object) { - // avoid iterating over the `prototype` property - return typeof object == 'function' - ? shimKeys(object) - : nativeKeys(object); - }; - - /** - * Creates an object composed of the specified properties. Property names may - * be specified as individual arguments or as arrays of property names. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to pluck. - * @param {Object} [prop1, prop2, ...] The properties to pick. - * @returns {Object} Returns an object composed of the picked properties. - * @example - * - * _.pick({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'name', 'age'); - * // => { 'name': 'moe', 'age': 40 } - */ - function pick(object) { - var prop, - index = 0, - props = concat.apply(ArrayProto, arguments), - length = props.length, - result = {}; - - // start `index` at `1` to skip `object` - while (++index < length) { - prop = props[index]; - if (prop in object) { - result[prop] = object[prop]; - } - } - return result; - } - - /** - * Gets the size of `value` by returning `value.length` if `value` is a string - * or array, or the number of own enumerable properties if `value` is an object. - * - * @static - * @memberOf _ - * @category Objects - * @param {Array|Object|String} value The value to inspect. - * @returns {Number} Returns `value.length` if `value` is a string or array, - * or the number of own enumerable properties if `value` is an object. - * @example - * - * _.size([1, 2]); - * // => 2 - * - * _.size({ 'one': 1, 'two': 2, 'three': 3 }); - * // => 3 - * - * _.size('curly'); - * // => 5 - */ - function size(value) { - var className = toString.call(value); - return className == arrayClass || className == stringClass - ? value.length - : keys(value).length; - } - - /** - * Invokes `interceptor` with the `value` as the first argument, and then returns - * `value`. The primary purpose of this method is to "tap into" a method chain, - * in order to performoperations on intermediate results within the chain. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to pass to `callback`. - * @param {Function} interceptor The function to invoke. - * @returns {Mixed} Returns `value`. - * @example - * - * _.chain([1,2,3,200]) - * .filter(function(num) { return num % 2 == 0; }) - * .tap(alert) - * .map(function(num) { return num * num }) - * .value(); - * // => // [2, 200] (alerted) - * // => [4, 40000] - */ - function tap(value, interceptor) { - interceptor(value); - return value; - } - - /** - * Produces an array of `object`'s own enumerable property values. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property values. - * @example - * - * _.values({ 'one': 1, 'two': 2, 'three': 3 }); - * // => [1, 2, 3] - */ - var values = createIterator({ - 'args': 'object', - 'init': '[]', - 'inLoop': 'result.push(object[index])' - }); - - /*--------------------------------------------------------------------------*/ - - /** - * Escapes a string for inclusion in HTML, replacing `&`, `<`, `"`, and `'` - * characters. + * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their + * corresponding HTML entities. * * @static * @memberOf _ @@ -2993,16 +3685,15 @@ * @returns {String} Returns the escaped string. * @example * - * _.escape('Curly, Larry & Moe'); - * // => "Curly, Larry & Moe" + * _.escape('Moe, Larry & Curly'); + * // => 'Moe, Larry & Curly' */ function escape(string) { - return string == null ? '' : (string + '').replace(reUnescapedHtml, escapeHtmlChar); + return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar); } /** * This function returns the first argument passed to it. - * Note: It is used throughout Lo-Dash as a default callback. * * @static * @memberOf _ @@ -3035,25 +3726,24 @@ * } * }); * - * _.capitalize('curly'); - * // => 'Curly' + * _.capitalize('moe'); + * // => 'Moe' * - * _('larry').capitalize(); - * // => 'Larry' + * _('moe').capitalize(); + * // => 'Moe' */ function mixin(object) { forEach(functions(object), function(methodName) { var func = lodash[methodName] = object[methodName]; - LoDash.prototype[methodName] = function() { - var args = [this._wrapped]; - if (arguments.length) { - push.apply(args, arguments); - } + lodash.prototype[methodName] = function() { + var args = [this.__wrapped__]; + push.apply(args, arguments); + var result = func.apply(lodash, args); - if (this._chain) { - result = new LoDash(result); - result._chain = true; + if (this.__chain__) { + result = new lodashWrapper(result); + result.__chain__ = true; } return result; }; @@ -3078,15 +3768,46 @@ } /** - * Resolves the value of `property` on `object`. If `property` is a function - * it will be invoked and its result returned, else the property value is - * returned. If `object` is falsey, then `null` is returned. + * Produces a random number between `min` and `max` (inclusive). If only one + * argument is passed, a number between `0` and the given number will be returned. + * + * @static + * @memberOf _ + * @category Utilities + * @param {Number} [min=0] The minimum possible value. + * @param {Number} [max=1] The maximum possible value. + * @returns {Number} Returns a random number. + * @example + * + * _.random(0, 5); + * // => a number between 0 and 5 + * + * _.random(5); + * // => also a number between 0 and 5 + */ + function random(min, max) { + if (min == null && max == null) { + max = 1; + } + min = +min || 0; + if (max == null) { + max = min; + min = 0; + } + return min + floor(nativeRandom() * ((+max || 0) - min + 1)); + } + + /** + * Resolves the value of `property` on `object`. If `property` is a function, + * it will be invoked with the `this` binding of `object` and its result returned, + * else the property value is returned. If `object` is falsey, then `undefined` + * is returned. * * @static * @memberOf _ * @category Utilities * @param {Object} object The object to inspect. - * @param {String} property The property to get the result of. + * @param {String} property The property to get the value of. * @returns {Mixed} Returns the resolved value. * @example * @@ -3104,19 +3825,25 @@ * // => 'nonsense' */ function result(object, property) { - // based on Backbone's private `getValue` function - // https://github.com/documentcloud/backbone/blob/0.9.2/backbone.js#L1419-1424 - if (!object) { - return null; - } - var value = object[property]; - return toString.call(value) == funcClass ? object[property]() : value; + var value = object ? object[property] : null; + return isFunction(value) ? object[property]() : value; } /** - * A micro-templating method, similar to John Resig's implementation. - * Lo-Dash templating handles arbitrary delimiters, preserves whitespace, and - * correctly escapes quotes within interpolated code. + * A micro-templating method that handles arbitrary delimiters, preserves + * whitespace, and correctly escapes quotes within interpolated code. + * + * Note: In the development build, `_.template` utilizes sourceURLs for easier + * debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl + * + * Note: Lo-Dash may be used in Chrome extensions by either creating a `lodash csp` + * build and using precompiled templates, or loading Lo-Dash in a sandbox. + * + * For more information on precompiling templates see: + * http://lodash.com/#custom-builds + * + * For more information on Chrome extension sandboxes see: + * http://developer.chrome.com/stable/extensions/sandboxingEval.html * * @static * @memberOf _ @@ -3124,177 +3851,202 @@ * @param {String} text The template text. * @param {Obect} data The data object used to populate the text. * @param {Object} options The options object. + * escape - The "escape" delimiter regexp. + * evaluate - The "evaluate" delimiter regexp. + * interpolate - The "interpolate" delimiter regexp. + * sourceURL - The sourceURL of the template's compiled source. + * variable - The data object variable name. * @returns {Function|String} Returns a compiled function when no `data` object * is given, else it returns the interpolated text. * @example * - * // using compiled template - * var compiled = _.template('hello: <%= name %>'); + * // using a compiled template + * var compiled = _.template('hello <%= name %>'); * compiled({ 'name': 'moe' }); - * // => 'hello: moe' + * // => 'hello moe' + * + * var list = '<% _.forEach(people, function(name) { %><li><%= name %></li><% }); %>'; + * _.template(list, { 'people': ['moe', 'larry'] }); + * // => '<li>moe</li><li>larry</li>' * - * var list = '<% _.forEach(people, function(name) { %> <li><%= name %></li> <% }); %>'; - * _.template(list, { 'people': ['moe', 'curly', 'larry'] }); - * // => '<li>moe</li><li>curly</li><li>larry</li>' + * // using the "escape" delimiter to escape HTML in data property values + * _.template('<b><%- value %></b>', { 'value': '<script>' }); + * // => '<b><script></b>' * - * var template = _.template('<b><%- value %></b>'); - * template({ 'value': '<script>' }); - * // => '<b><script></b>' + * // using the ES6 delimiter as an alternative to the default "interpolate" delimiter + * _.template('hello ${ name }', { 'name': 'curly' }); + * // => 'hello curly' * - * // using `print` - * var compiled = _.template('<% print("Hello " + epithet); %>'); - * compiled({ 'epithet': 'stooge' }); - * // => 'Hello stooge.' + * // using the internal `print` function in "evaluate" delimiters + * _.template('<% print("hello " + epithet); %>!', { 'epithet': 'stooge' }); + * // => 'hello stooge!' * - * // using custom template settings + * // using custom template delimiters * _.templateSettings = { - * 'interpolate': /\{\{(.+?)\}\}/g + * 'interpolate': /{{([\s\S]+?)}}/g * }; * - * var template = _.template('Hello {{ name }}!'); - * template({ 'name': 'Mustache' }); - * // => 'Hello Mustache!' - * - * // using the `variable` option - * _.template('<%= data.hasWith %>', { 'hasWith': 'no' }, { 'variable': 'data' }); - * // => 'no' + * _.template('hello {{ name }}!', { 'name': 'mustache' }); + * // => 'hello mustache!' + * + * // 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 + * + * // using the `variable` option to ensure a with-statement isn't used in the compiled template + * var compiled = _.template('hi <%= data.name %>!', null, { 'variable': 'data' }); + * compiled.source; + * // => function(data) { + * var __t, __p = '', __e = _.escape; + * __p += 'hi ' + ((__t = ( data.name )) == null ? '' : __t) + '!'; + * return __p; + * } * - * // using the `source` property - * <script> - * JST.project = <%= _.template(jstText).source %>; - * </script> + * // using the `source` property to inline compiled templates for meaningful + * // line numbers in error messages and a stack trace + * fs.writeFileSync(path.join(cwd, 'jst.js'), '\ + * var JST = {\ + * "main": ' + _.template(mainText).source + '\ + * };\ + * '); */ function template(text, data, options) { - options || (options = {}); + text || (text = ''); + options = defaults({}, options, lodash.templateSettings); - var result, - defaults = lodash.templateSettings, - escapeDelimiter = options.escape, - evaluateDelimiter = options.evaluate, - interpolateDelimiter = options.interpolate, + var index = 0, + source = "__p += '", variable = options.variable; - // use template defaults if no option is provided - if (escapeDelimiter == null) { - escapeDelimiter = defaults.escape; - } - if (evaluateDelimiter == null) { - evaluateDelimiter = defaults.evaluate; - } - if (interpolateDelimiter == null) { - interpolateDelimiter = defaults.interpolate; - } - - // tokenize delimiters to avoid escaping them - if (escapeDelimiter) { - text = text.replace(escapeDelimiter, tokenizeEscape); - } - if (interpolateDelimiter) { - text = text.replace(interpolateDelimiter, tokenizeInterpolate); - } - if (evaluateDelimiter) { - text = text.replace(evaluateDelimiter, tokenizeEvaluate); - } + var reDelimiters = RegExp( + (options.escape || reNoMatch).source + '|' + + (options.interpolate || reNoMatch).source + '|' + + (options.evaluate || reNoMatch).source + '|$' + , 'g'); - // escape characters that cannot be included in string literals and - // detokenize delimiter code snippets - text = "__p='" + text - .replace(reUnescapedString, escapeStringChar) - .replace(reToken, detokenize) + "';\n"; - - // clear stored code snippets - tokenized.length = 0; + text.replace(reDelimiters, function(match, escapeValue, interpolateValue, evaluateValue, offset) { + source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar); + if (escapeValue) { + source += "' +\n_.escape(" + escapeValue + ") +\n'"; + } + if (evaluateValue) { + source += "';\n" + evaluateValue + ";\n__p += '"; + } + if (interpolateValue) { + source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'"; + } + index = offset + match.length; + return match; + }); - // if `options.variable` is not specified, add `data` to the top of the scope chain + source += "';\n"; if (!variable) { - variable = defaults.variable; - text = 'with (' + variable + ' || {}) {\n' + text + '\n}\n'; + variable = 'obj'; + source = 'with (' + variable + ' || {}) {\n' + source + '\n}\n'; } - - text = 'function(' + variable + ') {\n' + - 'var __p, __t, __j = Array.prototype.join;\n' + - 'function print() { __p += __j.call(arguments, \'\') }\n' + - text + + source = 'function(' + variable + ') {\n' + + "var __t, __p = '', __j = Array.prototype.join;\n" + + "function print() { __p += __j.call(arguments, '') }\n" + + source + 'return __p\n}'; - // add a sourceURL for easier debugging - // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl - if (useSourceURL) { - text += '\n//@ sourceURL=/lodash/template/source[' + (templateCounter++) + ']'; + try { + var result = Function('_', 'return ' + source)(lodash); + } catch(e) { + e.source = source; + throw e; } - - result = Function('_', 'return ' + text)(lodash); - if (data) { return result(data); } - // provide the compiled function's source via its `toString()` method, in - // supported environments, or the `source` property as a convenience for - // build time precompilation - result.source = text; + result.source = source; return result; } /** - * Executes the `callback` function `n` times. The `callback` is bound to - * `thisArg` and invoked with 1 argument; (index). + * Executes the `callback` function `n` times, returning an array of the results + * of each `callback` execution. The `callback` is bound to `thisArg` and invoked + * with one argument; (index). * * @static * @memberOf _ * @category Utilities * @param {Number} n The number of times to execute the callback. * @param {Function} callback The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Array} Returns a new array of the results of each `callback` execution. * @example * - * _.times(3, function() { genie.grantWish(); }); - * // => calls `genie.grantWish()` 3 times + * var diceRolls = _.times(3, _.partial(_.random, 1, 6)); + * // => [3, 6, 4] + * + * _.times(3, function(n) { mage.castSpell(n); }); + * // => calls `mage.castSpell(n)` three times, passing `n` of `0`, `1`, and `2` respectively * - * _.times(3, function() { this.grantWish(); }, genie); - * // => also calls `genie.grantWish()` 3 times + * _.times(3, function(n) { this.cast(n); }, mage); + * // => also calls `mage.castSpell(n)` three times */ function times(n, callback, thisArg) { - var index = -1; - if (thisArg) { - while (++index < n) { - callback.call(thisArg, index); - } - } else { - while (++index < n) { - callback(index); - } + var index = -1, + result = Array(n > -1 ? n : 0); + + while (++index < n) { + result[index] = callback.call(thisArg, index); } + return result; } /** - * Generates a unique id. If `prefix` is passed, the id will be appended to it. + * The opposite of `_.escape`, this method converts the HTML entities + * `&`, `<`, `>`, `"`, and `'` in `string` to their + * corresponding characters. * * @static * @memberOf _ * @category Utilities - * @param {String} [prefix] The value to prefix the id with. - * @returns {Number|String} Returns a numeric id if no prefix is passed, else - * a string id may be returned. + * @param {String} string The string to unescape. + * @returns {String} Returns the unescaped string. + * @example + * + * _.unescape('Moe, Larry & Curly'); + * // => 'Moe, Larry & Curly' + */ + function unescape(string) { + return string == null ? '' : String(string).replace(reEscapedHtml, unescapeHtmlChar); + } + + /** + * Generates a unique ID. If `prefix` is passed, the ID will be appended to it. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} [prefix] The value to prefix the ID with. + * @returns {String} Returns the unique ID. * @example * * _.uniqueId('contact_'); * // => 'contact_104' + * + * _.uniqueId(); + * // => '105' */ function uniqueId(prefix) { - var id = idCounter++; + var id = ++idCounter + ''; return prefix ? prefix + id : id; } /*--------------------------------------------------------------------------*/ /** - * Wraps the value in a `lodash` chainable object. + * Creates a `lodash` object that wraps the given `value`. * * @static * @memberOf _ * @category Chaining * @param {Mixed} value The value to wrap. - * @returns {Object} Returns the `lodash` chainable object. + * @returns {Object} Returns the wrapper object. * @example * * var stooges = [ @@ -3306,92 +4058,168 @@ * var youngest = _.chain(stooges) * .sortBy(function(stooge) { return stooge.age; }) * .map(function(stooge) { return stooge.name + ' is ' + stooge.age; }) - * .first() - * .value(); + * .first(); * // => 'moe is 40' */ function chain(value) { - value = new LoDash(value); - value._chain = true; + value = new lodashWrapper(value); + value.__chain__ = true; return value; } /** - * Extracts the value from a wrapped chainable object. + * Invokes `interceptor` with the `value` as the first argument, and then + * returns `value`. The purpose of this method is to "tap into" a method chain, + * in order to perform operations on intermediate results within the chain. + * + * @static + * @memberOf _ + * @category Chaining + * @param {Mixed} value The value to pass to `interceptor`. + * @param {Function} interceptor The function to invoke. + * @returns {Mixed} Returns `value`. + * @example + * + * _([1, 2, 3, 4]) + * .filter(function(num) { return num % 2 == 0; }) + * .tap(alert) + * .map(function(num) { return num * num; }) + * .value(); + * // => // [2, 4] (alerted) + * // => [4, 16] + */ + function tap(value, interceptor) { + interceptor(value); + return value; + } + + /** + * Enables method chaining on the wrapper object. * * @name chain * @memberOf _ * @category Chaining - * @returns {Mixed} Returns the wrapped object. + * @returns {Mixed} Returns the wrapper object. * @example * - * _([1, 2, 3]).value(); - * // => [1, 2, 3] + * var sum = _([1, 2, 3]) + * .chain() + * .reduce(function(sum, num) { return sum + num; }) + * .value() + * // => 6` */ function wrapperChain() { - this._chain = true; + this.__chain__ = true; return this; } /** - * Extracts the value from a wrapped chainable object. + * Produces the `toString` result of the wrapped value. * - * @name value + * @name toString * @memberOf _ * @category Chaining - * @returns {Mixed} Returns the wrapped object. + * @returns {String} Returns the string result. * @example * - * _([1, 2, 3]).value(); - * // => [1, 2, 3] + * _([1, 2, 3]).toString(); + * // => '1,2,3' */ - function wrapperValue() { - return this._wrapped; + function wrapperToString() { + return String(this.__wrapped__); } - /*--------------------------------------------------------------------------*/ - /** - * The semantic version number. + * Extracts the wrapped value. * - * @static + * @name valueOf * @memberOf _ - * @type String + * @alias value + * @category Chaining + * @returns {Mixed} Returns the wrapped value. + * @example + * + * _([1, 2, 3]).valueOf(); + * // => [1, 2, 3] */ - lodash.VERSION = '0.3.1'; + function wrapperValueOf() { + return this.__wrapped__; + } + + /*--------------------------------------------------------------------------*/ - // assign static methods + // add functions that return wrapped values when chaining lodash.after = after; lodash.bind = bind; lodash.bindAll = bindAll; - lodash.chain = chain; - lodash.clone = clone; lodash.compact = compact; lodash.compose = compose; - lodash.contains = contains; + lodash.countBy = countBy; lodash.debounce = debounce; lodash.defaults = defaults; lodash.defer = defer; lodash.delay = delay; lodash.difference = difference; - lodash.escape = escape; - lodash.every = every; - lodash.extend = extend; lodash.filter = filter; - lodash.find = find; - lodash.first = first; lodash.flatten = flatten; lodash.forEach = forEach; - lodash.forIn = forIn; - lodash.forOwn = forOwn; lodash.functions = functions; lodash.groupBy = groupBy; - lodash.has = has; - lodash.identity = identity; - lodash.indexOf = indexOf; lodash.initial = initial; lodash.intersection = intersection; + lodash.invert = invert; lodash.invoke = invoke; + lodash.keys = keys; + lodash.map = map; + lodash.max = max; + lodash.memoize = memoize; + lodash.min = min; + lodash.omit = omit; + lodash.once = once; + lodash.pairs = pairs; + lodash.partial = partial; + lodash.pick = pick; + lodash.pluck = pluck; + lodash.range = range; + lodash.reject = reject; + lodash.rest = rest; + lodash.shuffle = shuffle; + lodash.sortBy = sortBy; + lodash.tap = tap; + lodash.throttle = throttle; + lodash.times = times; + lodash.toArray = toArray; + lodash.union = union; + lodash.uniq = uniq; + lodash.values = values; + lodash.where = where; + lodash.without = without; + lodash.wrap = wrap; + lodash.zip = zip; + + // add aliases + lodash.collect = map; + lodash.drop = rest; + lodash.each = forEach; + lodash.extend = assign; + lodash.methods = functions; + lodash.object = zipObject; + lodash.select = filter; + lodash.tail = rest; + lodash.unique = uniq; + + /*--------------------------------------------------------------------------*/ + + // add functions that return unwrapped values when chaining + lodash.clone = clone; + lodash.contains = contains; + lodash.escape = escape; + lodash.every = every; + lodash.find = find; + lodash.findWhere = findWhere; + lodash.has = has; + lodash.identity = identity; + lodash.indexOf = indexOf; lodash.isArguments = isArguments; lodash.isArray = isArray; lodash.isBoolean = isBoolean; @@ -3408,112 +4236,85 @@ lodash.isRegExp = isRegExp; lodash.isString = isString; lodash.isUndefined = isUndefined; - lodash.keys = keys; - lodash.last = last; lodash.lastIndexOf = lastIndexOf; - lodash.map = map; - lodash.max = max; - lodash.memoize = memoize; - lodash.min = min; lodash.mixin = mixin; lodash.noConflict = noConflict; - lodash.once = once; - lodash.partial = partial; - lodash.pick = pick; - lodash.pluck = pluck; - lodash.range = range; + lodash.random = random; lodash.reduce = reduce; lodash.reduceRight = reduceRight; - lodash.reject = reject; - lodash.rest = rest; lodash.result = result; - lodash.shuffle = shuffle; lodash.size = size; lodash.some = some; - lodash.sortBy = sortBy; lodash.sortedIndex = sortedIndex; - lodash.tap = tap; lodash.template = template; - lodash.throttle = throttle; - lodash.times = times; - lodash.toArray = toArray; - lodash.union = union; - lodash.uniq = uniq; + lodash.unescape = unescape; lodash.uniqueId = uniqueId; - lodash.values = values; - lodash.without = without; - lodash.wrap = wrap; - lodash.zip = zip; - // assign aliases + // add aliases lodash.all = every; lodash.any = some; - lodash.collect = map; lodash.detect = find; - lodash.each = forEach; lodash.foldl = reduce; lodash.foldr = reduceRight; - lodash.head = first; lodash.include = contains; lodash.inject = reduce; - lodash.methods = functions; - lodash.select = filter; - lodash.tail = rest; - lodash.take = first; - lodash.unique = uniq; - // add pseudo private properties used and removed during the build process - lodash._iteratorTemplate = iteratorTemplate; - lodash._shimKeys = shimKeys; + /*--------------------------------------------------------------------------*/ + + // add functions capable of returning wrapped and unwrapped values when chaining + lodash.first = first; + lodash.last = last; + + // add aliases + lodash.take = first; + lodash.head = first; /*--------------------------------------------------------------------------*/ - // assign private `LoDash` constructor's prototype - LoDash.prototype = lodash.prototype; + lodash.chain = chain; + + /** + * The semantic version number. + * + * @static + * @memberOf _ + * @type String + */ + lodash.VERSION = '1.1.1'; - // add all static functions to `LoDash.prototype` + // add functions to `lodash.prototype` mixin(lodash); - // add `LoDash.prototype.chain` after calling `mixin()` to avoid overwriting - // it with the wrapped `lodash.chain` - LoDash.prototype.chain = wrapperChain; - LoDash.prototype.value = wrapperValue; + // add "Chaining" functions to the wrapper + lodash.prototype.chain = wrapperChain; + lodash.prototype.value = wrapperValueOf; - // add all mutator Array functions to the wrapper. + // add `Array` mutator functions to the wrapper forEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) { - var func = ArrayProto[methodName]; - - LoDash.prototype[methodName] = function() { - var value = this._wrapped; + var func = arrayRef[methodName]; + lodash.prototype[methodName] = function() { + var value = this.__wrapped__; func.apply(value, arguments); - // 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. - if (value.length === 0) { + // avoid array-like object bugs with `Array#shift` and `Array#splice` + // in Firefox < 10 and IE < 9 + if (!support.spliceObjects && value.length === 0) { delete value[0]; } - if (this._chain) { - value = new LoDash(value); - value._chain = true; - } - return value; + return this; }; }); - // add all accessor Array functions to the wrapper. + // add `Array` accessor functions to the wrapper forEach(['concat', 'join', 'slice'], function(methodName) { - var func = ArrayProto[methodName]; - - LoDash.prototype[methodName] = function() { - var value = this._wrapped, + var func = arrayRef[methodName]; + lodash.prototype[methodName] = function() { + var value = this.__wrapped__, result = func.apply(value, arguments); - if (this._chain) { - result = new LoDash(result); - result._chain = true; + if (this.__chain__) { + result = new lodashWrapper(result); + result.__chain__ = true; } return result; }; @@ -3537,10 +4338,10 @@ }); } // check for `exports` after `define` in case a build optimizer adds an `exports` object - else if (freeExports) { + else if (freeExports && !freeExports.nodeType) { // in Node.js or RingoJS v0.8.0+ - if (typeof module == 'object' && module && module.exports == freeExports) { - (module.exports = lodash)._ = lodash; + if (freeModule) { + (freeModule.exports = lodash)._ = lodash; } // in Narwhal or RingoJS v0.7.0- else { |