/*! * Lo-Dash v0.4.2 * Copyright 2012 John-David Dalton * Based on Underscore.js 1.3.3, copyright 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. * * Available under MIT license */ ;(function(window, undefined) { 'use strict'; /** * Used to cache the last `_.templateSettings.evaluate` delimiter to avoid * unnecessarily assigning `reEvaluateDelimiter` a new generated regexp. * Assigned in `_.template`. */ var lastEvaluateDelimiter; /** * Used to cache the last template `options.variable` to avoid unnecessarily * assigning `reDoubleVariable` a new generated regexp. Assigned in `_.template`. */ var lastVariable; /** * Used to match potentially incorrect data object references, like `obj.obj`, * in compiled templates. Assigned in `_.template`. */ var reDoubleVariable; /** * Used to match "evaluate" delimiters, including internal delimiters, * in template text. Assigned in `_.template`. */ var reEvaluateDelimiter; /** Detect free variable `exports` */ var freeExports = typeof exports == 'object' && exports && (typeof global == 'object' && global && global == global.global && (window = global), exports); /** Native prototype shortcuts */ var ArrayProto = Array.prototype, ObjectProto = Object.prototype; /** Used to generate unique IDs */ var idCounter = 0; /** Used to restore the original `_` reference in `noConflict` */ var oldDash = window._; /** Used to detect delimiter values that should be processed by `tokenizeEvaluate` */ var reComplexDelimiter = /[-+=!~*%&^<>|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/; /** 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 insert the data object variable into compiled template source */ var reInsertVariable = /(?:__e|__t = )\(\s*(?![\d\s"']|this\.)/g; /** Used to detect if a method is native */ var reNative = RegExp('^' + (ObjectProto.valueOf + '') .replace(/[.*+?^=!:${}()|[\]\/\\]/g, '\\$&') .replace(/valueOf|for [^\]]+/g, '.+?') + '$' ); /** Used to match tokens in template text */ var reToken = /__token__(\d+)/g; /** Used to match unescaped characters in strings for inclusion in HTML */ var reUnescapedHtml = /[&<"']/g; /** Used to match unescaped characters in compiled string literals */ var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; /** Used to fix the JScript [[DontEnum]] bug */ var shadowed = [ 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf' ]; /** Used to make template sourceURLs easier to identify */ var templateCounter = 0; /** Used to replace template delimiters */ var token = '__token__'; /** Used to store tokenized template text snippets */ var tokenized = []; /** Native method shortcuts */ var concat = ArrayProto.concat, hasOwnProperty = ObjectProto.hasOwnProperty, push = ArrayProto.push, propertyIsEnumerable = ObjectProto.propertyIsEnumerable, slice = ArrayProto.slice, toString = ObjectProto.toString; /* Native method shortcuts for methods with the same name as other `lodash` methods */ 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; /** `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]'; /** Timer shortcuts */ var clearTimeout = window.clearTimeout, setTimeout = window.setTimeout; /** * Detect the JScript [[DontEnum]] bug: * In IE < 9 an objects own properties, shadowing non-enumerable ones, are * made non-enumerable as well. */ var hasDontEnumBug = !propertyIsEnumerable.call({ 'valueOf': 0 }, 'valueOf'); /** Detect if `Array#slice` cannot be used to convert strings to arrays (Opera < 10.52) */ var noArraySliceOnStrings = slice.call('x')[0] != 'x'; /** * Detect lack of support for accessing string characters by index: * IE < 8 can't access characters by index and IE 8 can only access * characters by index on string literals. */ var noCharByIndex = ('x'[0] + Object('x')[0]) != 'xx'; /* Detect if `Function#bind` exists and is inferred to be fast (all but V8) */ var isBindFast = nativeBind && /\n|Opera/.test(nativeBind + toString.call(window.opera)); /* Detect if `Object.keys` exists and is inferred to be fast (V8, Opera, IE) */ var isKeysFast = nativeKeys && /^.+$|true/.test(nativeKeys + !!window.attachEvent); /** 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 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 = { '&': '&', '<': '<', '"': '"', "'": ''' }; /** Used to determine if values are of the language type Object */ var objectTypes = { 'boolean': false, 'function': true, 'object': true, 'number': false, 'string': false, 'undefined': false }; /** Used to escape characters for inclusion in compiled string literals */ var stringEscapes = { '\\': '\\', "'": "'", '\n': 'n', '\r': 'r', '\t': 't', '\u2028': 'u2028', '\u2029': 'u2029' }; /*--------------------------------------------------------------------------*/ /** * The `lodash` function. * * @name _ * @constructor * @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); } /** * Creates a `LoDash` instance that wraps a value to allow chaining. * * @private * @constructor * @param {Mixed} value The value to wrap. */ function LoDash(value) { // exit early if already wrapped if (value && value._wrapped) { return value; } this._wrapped = value; } /** * By default, Lo-Dash uses embedded Ruby (ERB) style template delimiters, * change the following template settings to use alternative delimiters. * * @static * @memberOf _ * @type Object */ lodash.templateSettings = { /** * Used to detect `data` property values to be HTML-escaped. * * @static * @memberOf _.templateSettings * @type RegExp */ 'escape': /<%-([\s\S]+?)%>/g, /** * Used to detect code to be evaluated. * * @static * @memberOf _.templateSettings * @type RegExp */ 'evaluate': /<%([\s\S]+?)%>/g, /** * Used to detect `data` property values to inject. * * @static * @memberOf _.templateSettings * @type RegExp */ 'interpolate': /<%=([\s\S]+?)%>/g, /** * Used to reference the data object in the template text. * * @static * @memberOf _.templateSettings * @type String */ 'variable': 'obj' }; /*--------------------------------------------------------------------------*/ /** * The template used to create iterator functions. * * @private * @param {Obect} data The data object used to populate the text. * @returns {String} Returns the interpolated text. */ var iteratorTemplate = template( // conditional strict mode '<% if (useStrict) { %>\'use strict\';\n<% } %>' + // the `iteratee` may be reassigned by the `top` snippet 'var index, iteratee = <%= firstArg %>, ' + // assign the `result` variable an initial value '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 = iteratee.length; index = -1;' + ' <% if (objectBranch) { %>\nif (length === length >>> 0) {<% } %>' + // add support for accessing string characters by index if needed ' <% if (noCharByIndex) { %>\n' + ' if (toString.call(iteratee) == stringClass) {\n' + ' iteratee = iteratee.split(\'\')\n' + ' }' + ' <% } %>\n' + ' <%= arrayBranch.beforeLoop %>;\n' + ' while (++index < length) {\n' + ' <%= arrayBranch.inLoop %>\n' + ' }' + ' <% if (objectBranch) { %>\n}<% } %>' + '<% } %>' + // the following branch is for iterating an object's own/inherited properties '<% if (objectBranch) { %>' + ' <% if (arrayBranch) { %>\nelse {<% } %>' + ' <% if (!hasDontEnumBug) { %>\n' + ' var skipProto = typeof iteratee == \'function\' && \n' + ' propertyIsEnumerable.call(iteratee, \'prototype\');\n' + ' <% } %>' + // iterate own properties using `Object.keys` if it's fast ' <% if (isKeysFast && useHas) { %>\n' + ' var props = nativeKeys(iteratee),\n' + ' propIndex = -1,\n' + ' length = props.length;\n\n' + ' <%= objectBranch.beforeLoop %>;\n' + ' while (++propIndex < length) {\n' + ' index = props[propIndex];\n' + ' if (!(skipProto && index == \'prototype\')) {\n' + ' <%= objectBranch.inLoop %>\n' + ' }\n' + ' }' + // else using a for-in loop ' <% } else { %>\n' + ' <%= objectBranch.beforeLoop %>;\n' + ' for (index in iteratee) {' + ' <% if (hasDontEnumBug) { %>\n' + ' <% if (useHas) { %>if (hasOwnProperty.call(iteratee, index)) {\n <% } %>' + ' <%= objectBranch.inLoop %>;\n' + ' <% if (useHas) { %>}<% } %>' + ' <% } else { %>\n' + // 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) { %> &&\n' + ' hasOwnProperty.call(iteratee, index)<% } %>) {\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\n' + ' var ctor = iteratee.constructor;\n' + ' <% for (var k = 0; k < 7; k++) { %>\n' + ' index = \'<%= shadowed[k] %>\';\n' + ' if (<%' + ' if (shadowed[k] == \'constructor\') {' + ' %>!(ctor && ctor.prototype === iteratee) && <%' + ' } %>hasOwnProperty.call(iteratee, index)) {\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' ); /** * Reusable iterator options shared by * `every`, `filter`, `find`, `forEach`, `forIn`, `forOwn`, `groupBy`, `map`, * `reject`, `some`, and `sortBy`. */ 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(iteratee[index], index, collection)' }; /** Reusable iterator options for `every` and `some` */ var everyIteratorOptions = { 'init': 'true', 'inLoop': 'if (!callback(iteratee[index], index, collection)) return !result' }; /** Reusable iterator options for `defaults` and `extend` */ var extendIteratorOptions = { 'useHas': false, 'useStrict': false, 'args': 'object', 'init': 'object', 'top': 'for (var iterateeIndex = 1, length = arguments.length; iterateeIndex < length; iterateeIndex++) {\n' + ' iteratee = arguments[iterateeIndex];\n' + (hasDontEnumBug ? ' if (iteratee) {' : ''), 'inLoop': 'result[index] = iteratee[index]', 'bottom': (hasDontEnumBug ? ' }\n' : '') + '}' }; /** Reusable iterator options for `filter` and `reject` */ var filterIteratorOptions = { 'init': '[]', 'inLoop': 'callback(iteratee[index], index, collection) && result.push(iteratee[index])' }; /** Reusable iterator options for `find`, `forEach`, `forIn`, and `forOwn` */ var forEachIteratorOptions = { 'top': 'if (thisArg) callback = iteratorBind(callback, thisArg)' }; /** Reusable iterator options for `forIn` and `forOwn` */ var forOwnIteratorOptions = { 'inLoop': { 'object': baseIteratorOptions.inLoop } }; /** Reusable iterator options for `invoke`, `map`, `pluck`, and `sortBy` */ var mapIteratorOptions = { 'init': '', 'exit': 'if (!collection) return []', 'beforeLoop': { 'array': 'result = Array(length)', 'object': 'result = ' + (isKeysFast ? 'Array(length)' : '[]') }, 'inLoop': { 'array': 'result[index] = callback(iteratee[index], index, collection)', 'object': 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '(callback(iteratee[index], index, collection))' } }; /*--------------------------------------------------------------------------*/ /** * Creates a new function optimized for searching large arrays for a given `value`, * starting at `fromIndex`, using strict equality for comparisons, i.e. `===`. * * @private * @param {Array} array The array to search. * @param {Mixed} value The value to search for. * @param {Number} [fromIndex=0] The index to start searching from. * @param {Number} [largeSize=30] The length at which an array is considered large. * @returns {Boolean} Returns `true` if `value` is found, else `false`. */ function cachedContains(array, fromIndex, largeSize) { fromIndex || (fromIndex = 0); var length = array.length, isLarge = (length - fromIndex) >= (largeSize || 30), cache = isLarge ? {} : array; if (isLarge) { // init value cache var key, index = fromIndex - 1; while (++index < length) { // manually coerce `value` to string because `hasOwnProperty`, in some // older versions of Firefox, coerces objects incorrectly key = array[index] + ''; (hasOwnProperty.call(cache, key) ? cache[key] : (cache[key] = [])).push(array[index]); } } return function(value) { if (isLarge) { var key = value + ''; return hasOwnProperty.call(cache, key) && indexOf(cache[key], value) > -1; } return indexOf(cache, value, fromIndex) > -1; } } /** * 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. * * @private * @param {Object} [options1, options2, ...] The compile options objects. * * useHas - A boolean to specify whether or not to use `hasOwnProperty` checks * in the object loop. * * useStrict - A boolean to specify whether or not to include the ES5 * "use strict" directive. * * args - A string of comma separated arguments the iteration function will * accept. * * init - A string to specify the initial value of the `result` variable. * * exit - A string of code to use in place of the default exit-early check * of `if (!arguments[0]) return result`. * * top - A string of code to execute after the exit-early check but before * the iteration branches. * * beforeLoop - A string or object containing an "array" or "object" property * of code to execute before the array or object loops. * * inLoop - A string or object containing an "array" or "object" property * of code to execute in the array or object loops. * * bottom - A string of code to execute after the iteration branches but * before the `result` is returned. * * @returns {Function} Returns the compiled function. */ 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': '' }, 'objectBranch': { 'beforeLoop': '' } }; 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|inLoop/.test(prop)) { if (typeof value == 'string') { value = { 'array': value, 'object': value }; } data.arrayBranch[prop] = value.array; data.objectBranch[prop] = value.object; } else { data[prop] = value; } } } // set additional template `data` values var args = data.args, firstArg = /^[^,]+/.exec(args)[0]; data.firstArg = firstArg; data.hasDontEnumBug = hasDontEnumBug; data.isKeysFast = isKeysFast; data.shadowed = shadowed; data.useHas = data.useHas !== false; data.useStrict = data.useStrict !== false; if (!('noCharByIndex' in data)) { data.noCharByIndex = noCharByIndex; } if (!data.exit) { data.exit = 'if (!' + firstArg + ') return result'; } if (firstArg != 'collection' || !data.arrayBranch.inLoop) { data.arrayBranch = null; } // create the function factory var factory = Function( 'arrayClass, bind, compareAscending, funcClass, hasOwnProperty, identity, ' + 'iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, slice, ' + 'stringClass, toString', 'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}' ); // return the compiled function return factory( arrayClass, bind, compareAscending, funcClass, hasOwnProperty, identity, iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, slice, stringClass, toString ); } /** * Used by `sortBy` to compare transformed values of `collection`, 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 `-1` if `a` < `b`, `0` if `a` == `b`, or `1` if `a` > `b`. */ function compareAscending(a, b) { a = a.criteria; b = b.criteria; if (a === undefined) { return 1; } if (b === undefined) { return -1; } return a < b ? -1 : a > b ? 1 : 0; } /** * Used by `template` to replace tokens with their corresponding code snippets. * * @private * @param {String} match The matched token. * @param {String} index The `tokenized` index of the code snippet. * @returns {String} Returns the code snippet. */ function detokenize(match, index) { return tokenized[index]; } /** * 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 escape characters for inclusion in HTML. * * @private * @param {String} match The matched character to escape. * @returns {String} Returns the escaped character. */ function escapeHtmlChar(match) { return htmlEscapes[match]; } /** * Creates a new function that, when called, invokes `func` with the `this` * binding of `thisArg` and the arguments (value, index, object). * * @private * @param {Function} func The function to bind. * @param {Mixed} [thisArg] The `this` binding of `func`. * @returns {Function} Returns the new bound function. */ function iteratorBind(func, thisArg) { return function(value, index, object) { return func.call(thisArg, value, index, object); }; } /** * A no-operation function. * * @private */ function noop() { // no operation performed } /** * A shim implementation of `Object.keys` that produces an array of the given * object's own enumerable property names. * * @private * @param {Object} object The object to inspect. * @returns {Array} Returns a new array of property names. */ var shimKeys = createIterator({ 'args': 'object', 'exit': 'if (!(object && objectTypes[typeof object])) throw TypeError()', 'init': '[]', 'inLoop': 'result.push(index)' }); /** * Used by `template` to replace "escape" template delimiters with tokens. * * @private * @param {String} match The matched template delimiter. * @param {String} value The delimiter value. * @returns {String} Returns a token. */ function tokenizeEscape(match, value) { if (reComplexDelimiter.test(value)) { return ''; } var index = tokenized.length; tokenized[index] = "' +\n__e(" + value + ") +\n'"; return token + index; } /** * Used by `template` to replace "evaluate" template delimiters, or complex * "escape" and "interpolate" delimiters, with tokens. * * @private * @param {String} match The matched template delimiter. * @param {String} value The delimiter value. * @param {String} escapeValue The "escape" delimiter value. * @param {String} interpolateValue The "interpolate" delimiter value. * @returns {String} Returns a token. */ function tokenizeEvaluate(match, value, escapeValue, interpolateValue) { var index = tokenized.length; if (value) { tokenized[index] = "';\n" + value + ";\n__p += '" } else if (escapeValue) { tokenized[index] = "' +\n__e(" + escapeValue + ") +\n'"; } else if (interpolateValue) { tokenized[index] = "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'"; } return token + index; } /** * Used by `template` to replace "interpolate" template delimiters with tokens. * * @private * @param {String} match The matched template delimiter. * @param {String} value The delimiter value. * @returns {String} Returns a token. */ function tokenizeInterpolate(match, value) { if (reComplexDelimiter.test(value)) { return ''; } var index = tokenized.length; tokenized[index] = "' +\n((__t = (" + value + ")) == null ? '' : __t) +\n'"; return token + index; } /*--------------------------------------------------------------------------*/ /** * Checks if a given `target` value is present in a `collection` using strict * equality for comparisons, i.e. `===`. * * @static * @memberOf _ * @alias include * @category Collections * @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`. * @example * * _.contains([1, 2, 3], 3); * // => true * * _.contains({ 'name': 'moe', 'age': 40 }, 'moe'); * // => true * * _.contains('curly', 'ur'); * // => true */ var contains = createIterator({ 'args': 'collection, target', 'init': 'false', 'noCharByIndex': false, 'beforeLoop': { 'array': 'if (toString.call(iteratee) == stringClass) return collection.indexOf(target) > -1' }, 'inLoop': 'if (iteratee[index] === target) return true' }); /** * 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; (value, index|key, collection). * * @static * @memberOf _ * @alias all * @category Collections * @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 for the callback. * @returns {Boolean} Returns `true` if all values pass the callback check, else `false`. * @example * * _.every([true, 1, null, 'yes'], Boolean); * // => false */ var every = createIterator(baseIteratorOptions, everyIteratorOptions); /** * 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; (value, index|key, collection). * * @static * @memberOf _ * @alias select * @category Collections * @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 for the callback. * @returns {Array} Returns a new array of values that passed callback check. * @example * * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); * // => [2, 4, 6] */ var filter = createIterator(baseIteratorOptions, filterIteratorOptions); /** * 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; (value, index|key, collection). * * @static * @memberOf _ * @alias detect * @category Collections * @param {Array|Object|String} 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`. * @example * * var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); * // => 2 */ var find = createIterator(baseIteratorOptions, forEachIteratorOptions, { 'init': '', 'inLoop': 'if (callback(iteratee[index], index, collection)) return iteratee[index]' }); /** * Iterates over a `collection`, executing the `callback` for each value in the * `collection`. The `callback` is bound to `thisArg` and invoked with 3 * arguments; (value, index|key, collection). * * @static * @memberOf _ * @alias each * @category Collections * @param {Array|Object|String} 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`. * @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) */ var forEach = createIterator(baseIteratorOptions, forEachIteratorOptions); /** * Splits `collection` 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|key, collection). The `callback` argument may * also be the name of a property to group by. * * @static * @memberOf _ * @category Collections * @param {Array|Object|String} collection The collection 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'] } */ var groupBy = createIterator(baseIteratorOptions, { 'init': '{}', 'top': 'var prop, isFunc = typeof callback == \'function\';\n' + 'if (isFunc && thisArg) callback = iteratorBind(callback, thisArg)', 'inLoop': 'prop = isFunc\n' + ' ? callback(iteratee[index], index, collection)\n' + ' : iteratee[index][callback];\n' + '(hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(iteratee[index])' }); /** * Invokes the method named by `methodName` on each element in the `collection`. * 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 values returned from 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']] */ var invoke = createIterator(mapIteratorOptions, { 'args': 'collection, methodName', 'top': 'var args = slice.call(arguments, 2),\n' + ' isFunc = typeof methodName == \'function\'', 'inLoop': { 'array': 'result[index] = (isFunc ? methodName : iteratee[index][methodName])' + '.apply(iteratee[index], args)', 'object': 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '((isFunc ? methodName : iteratee[index][methodName]).apply(iteratee[index], args))' } }); /** * Produces a new array of values by mapping each element in the `collection` * through a transformation `callback`. The `callback` is bound to `thisArg` * and invoked with 3 arguments; (value, index|key, collection). * * @static * @memberOf _ * @alias collect * @category Collections * @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 for the callback. * @returns {Array} Returns a new array of values returned by the callback. * @example * * _.map([1, 2, 3], function(num) { return num * 3; }); * // => [3, 6, 9] * * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); * // => [3, 6, 9] (order is not guaranteed) */ var map = createIterator(baseIteratorOptions, mapIteratorOptions); /** * Retrieves the value of a specified property from all elements in * the `collection`. * * @static * @memberOf _ * @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 }, * { 'name': 'curly', 'age': 60 } * ]; * * _.pluck(stooges, 'name'); * // => ['moe', 'larry', 'curly'] */ var pluck = createIterator(mapIteratorOptions, { 'args': 'collection, property', 'inLoop': { 'array': 'result[index] = iteratee[index][property]', 'object': 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '(iteratee[index][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|key, collection). * * @static * @memberOf _ * @alias foldl, inject * @category Collections * @param {Array|Object|String} collection The collection to iterate over. * @param {Function} callback The function called per iteration. * @param {Mixed} [accumulator] Initial value of the accumulator. * @param {Mixed} [thisArg] The `this` binding for the callback. * @returns {Mixed} Returns the accumulated value. * @example * * var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; }); * // => 6 */ 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, iteratee[index], index, collection)', 'object': 'result = noaccum\n' + ' ? (noaccum = false, iteratee[index])\n' + ' : callback(result, iteratee[index], index, collection)' } }); /** * The right-associative version of `_.reduce`. * * @static * @memberOf _ * @alias foldr * @category Collections * @param {Array|Object|String} collection The collection to iterate over. * @param {Function} callback The function called per iteration. * @param {Mixed} [accumulator] Initial value of the accumulator. * @param {Mixed} [thisArg] The `this` binding for the callback. * @returns {Mixed} Returns the accumulated value. * @example * * var list = [[0, 1], [2, 3], [4, 5]]; * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []); * // => [4, 5, 2, 3, 0, 1] */ function reduceRight(collection, callback, accumulator, thisArg) { if (!collection) { return accumulator; } var length = collection.length, noaccum = arguments.length < 3; if(thisArg) { callback = iteratorBind(callback, thisArg); } if (length === length >>> 0) { var iteratee = noCharByIndex && toString.call(collection) == stringClass ? collection.split('') : collection; if (length && noaccum) { accumulator = iteratee[--length]; } while (length--) { accumulator = callback(accumulator, iteratee[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); } return accumulator; } /** * The opposite of `_.filter`, this method returns the values of a * `collection` that `callback` does **not** return truthy for. * * @static * @memberOf _ * @category Collections * @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 for the callback. * @returns {Array} Returns a new array of values 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 reject = createIterator(baseIteratorOptions, filterIteratorOptions, { 'inLoop': '!' + filterIteratorOptions.inLoop }); /** * 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; (value, index|key, collection). * * @static * @memberOf _ * @alias any * @category Collections * @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 for the callback. * @returns {Boolean} Returns `true` if any value passes the callback check, else `false`. * @example * * _.some([null, 0, 'yes', false]); * // => true */ var some = createIterator(baseIteratorOptions, everyIteratorOptions, { 'init': 'false', 'inLoop': everyIteratorOptions.inLoop.replace('!', '') }); /** * Produces a new sorted array, sorted in ascending order by the results of * running each element of `collection` through a transformation `callback`. * The `callback` is bound to `thisArg` and invoked with 3 arguments; * (value, index|key, collection). The `callback` argument may also be the * name of a property to sort by (e.g. 'length'). * * @static * @memberOf _ * @category Collections * @param {Array|Object|String} collection The collection 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 * * _.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] * * _.sortBy(['larry', 'brendan', 'moe'], 'length'); * // => ['moe', 'larry', 'brendan'] */ var sortBy = createIterator(baseIteratorOptions, mapIteratorOptions, { 'top': 'if (typeof callback == \'string\') {\n' + ' var prop = callback;\n' + ' callback = function(collection) { return collection[prop] }\n' + '}\n' + 'else if (thisArg) {\n' + ' callback = iteratorBind(callback, thisArg)\n' + '}', 'inLoop': { 'array': 'result[index] = {\n' + ' criteria: callback(iteratee[index], index, collection),\n' + ' value: iteratee[index]\n' + '}', 'object': 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '({\n' + ' criteria: callback(iteratee[index], index, collection),\n' + ' value: iteratee[index]\n' + '})' }, 'bottom': 'result.sort(compareAscending);\n' + 'length = result.length;\n' + 'while (length--) {\n' + ' result[length] = result[length].value\n' + '}' }); /** * Converts the `collection`, into an array. Useful for converting the * `arguments` object. * * @static * @memberOf _ * @category Collections * @param {Array|Object|String} collection The collection to convert. * @returns {Array} Returns the new converted array. * @example * * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4); * // => [2, 3, 4] */ function toArray(collection) { if (!collection) { return []; } if (collection.toArray && toString.call(collection.toArray) == funcClass) { return collection.toArray(); } var length = collection.length; if (length === length >>> 0) { return (noArraySliceOnStrings ? toString.call(collection) == stringClass : typeof collection == 'string') ? collection.split('') : slice.call(collection); } return values(collection); } /*--------------------------------------------------------------------------*/ /** * Produces a new array with all falsey values of `array` removed. The values * `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to compact. * @returns {Array} Returns a new filtered array. * @example * * _.compact([0, 1, false, 2, '', 3]); * // => [1, 2, 3] */ function compact(array) { var result = []; if (!array) { return result; } var index = -1, length = array.length; while (++index < length) { if (array[index]) { result.push(array[index]); } } return result; } /** * Produces a new array of `array` values not present in the other arrays * using strict equality for comparisons, i.e. `===`. * * @static * @memberOf _ * @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 * other arrays. * @example * * _.difference([1, 2, 3, 4, 5], [5, 2, 10]); * // => [1, 3, 4] */ function difference(array) { var result = []; if (!array) { return result; } var index = -1, length = array.length, flattened = concat.apply(result, arguments), contains = cachedContains(flattened, length); while (++index < length) { if (!contains(array[index])) { result.push(array[index]); } } return result; } /** * Gets the first value of the `array`. Pass `n` to return the first `n` values * of the `array`. * * @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`. * @example * * _.first([5, 4, 3, 2, 1]); * // => 5 */ function first(array, n, guard) { if (array) { return (n == null || guard) ? array[0] : slice.call(array, 0, n); } } /** * Flattens a nested array (the nesting can be to any depth). If `shallow` is * truthy, `array` will only be flattened a single level. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to compact. * @param {Boolean} shallow A flag to indicate only flattening a single level. * @returns {Array} Returns a new flattened array. * @example * * _.flatten([1, [2], [3, [[4]]]]); * // => [1, 2, 3, 4]; * * _.flatten([1, [2], [3, [[4]]]], true); * // => [1, 2, 3, [[4]]]; */ function flatten(array, shallow) { var result = []; if (!array) { return result; } var value, index = -1, length = array.length; while (++index < length) { value = array[index]; if (isArray(value)) { push.apply(result, shallow ? value : flatten(value)); } else { result.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. * * @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`. * @returns {Number} Returns the index of the matched value or `-1`. * @example * * _.indexOf([1, 2, 3, 1, 2, 3], 2); * // => 1 * * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3); * // => 4 * * _.indexOf([1, 1, 2, 2, 3, 3], 2, true); * // => 2 */ function indexOf(array, value, fromIndex) { if (!array) { return -1; } var index = -1, length = array.length; 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; } } while (++index < length) { if (array[index] === value) { return index; } } return -1; } /** * Gets all but the last value of `array`. Pass `n` to exclude the last `n` * values from the result. * * @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`. * @example * * _.initial([3, 2, 1]); * // => [3, 2] */ function initial(array, n, guard) { if (!array) { return []; } return slice.call(array, 0, -((n == null || guard) ? 1 : n)); } /** * Computes the intersection of all the passed-in arrays. * * @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. * @example * * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); * // => [1, 2] */ function intersection(array) { var result = []; if (!array) { return result; } var value, index = -1, length = array.length, others = slice.call(arguments, 1), cache = []; while (++index < length) { value = array[index]; if (indexOf(result, value) < 0 && every(others, function(other, index) { return (cache[index] || (cache[index] = cachedContains(other)))(value); })) { result.push(value); } } return result; } /** * Gets the last value of the `array`. Pass `n` to return the lasy `n` values * of the `array`. * * @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`. * @example * * _.last([3, 2, 1]); * // => 1 */ function last(array, n, guard) { if (array) { var length = array.length; return (n == null || guard) ? array[length - 1] : slice.call(array, -n || length); } } /** * Gets the index at which the last occurrence of `value` is found using * strict equality for comparisons, i.e. `===`. * * @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. * @returns {Number} Returns the index of the matched value or `-1`. * @example * * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2); * // => 4 * * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3); * // => 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; } while (index--) { if (array[index] === value) { return index; } } return -1; } /** * 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; } /** * 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. * * @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. * @returns {Array} Returns a new range array. * @example * * _.range(10); * // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] * * _.range(1, 11); * // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] * * _.range(0, 30, 5); * // => [0, 5, 10, 15, 20, 25] * * _.range(0, -10, -1); * // => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] * * _.range(0); * // => [] */ function range(start, end, step) { step || (step = 1); if (end == null) { end = start || 0; start = 0; } // use `Array(length)` so V8 will avoid the slower "dictionary" mode // http://www.youtube.com/watch?v=XAqIpGU8ZZk#t=16m27s var index = -1, length = Math.max(0, Math.ceil((end - start) / step)), result = Array(length); while (++index < length) { result[index] = start; start += step; } return result; } /** * 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. * * @static * @memberOf _ * @alias 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`. * @example * * _.rest([3, 2, 1]); * // => [2, 1] */ function rest(array, n, guard) { if (!array) { return []; } return slice.call(array, (n == null || 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. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to shuffle. * @returns {Array} Returns a new shuffled array. * @example * * _.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; } /** * Uses a binary search to determine the smallest index at which the `value` * 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). * * @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. * @returns {Number} Returns the index at which the value should be inserted * into `array`. * @example * * _.sortedIndex([20, 30, 40], 35); * // => 2 * * var dict = { * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'thirty-five': 35, 'fourty': 40 } * }; * * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { * return dict.wordToNumber[word]; * }); * // => 2 * * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', 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) { if (thisArg) { callback = bind(callback, thisArg); } value = callback(value); while (low < high) { mid = (low + high) >>> 1; callback(array[mid]) < value ? low = mid + 1 : high = mid; } } else { while (low < high) { mid = (low + high) >>> 1; array[mid] < value ? low = mid + 1 : high = mid; } } return low; } /** * Computes the union of the passed-in arrays. * * @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 one or more of the arrays. * @example * * _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); * // => [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; } /** * Produces 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). * * @static * @memberOf _ * @alias unique * @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. * @returns {Array} Returns a duplicate-value-free array. * @example * * _.uniq([1, 2, 1, 3, 1]); * // => [1, 2, 3] * * _.uniq([1, 1, 2, 2, 3], true); * // => [1, 2, 3] * * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return Math.floor(num); }); * // => [1, 2, 3] * * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math); * // => [1, 2, 3] */ function uniq(array, isSorted, callback, thisArg) { var result = []; if (!array) { return result; } var computed, index = -1, length = array.length, seen = []; // juggle arguments if (typeof isSorted == 'function') { thisArg = callback; callback = isSorted; isSorted = false; } if (!callback) { callback = identity; } else if (thisArg) { callback = iteratorBind(callback, thisArg); } while (++index < length) { computed = callback(array[index], index, array); if (isSorted ? !index || seen[seen.length - 1] !== computed : indexOf(seen, computed) < 0 ) { seen.push(computed); result.push(array[index]); } } return result; } /** * Produces a new array with all occurrences of the passed values removed using * strict equality for comparisons, i.e. `===`. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to filter. * @param {Mixed} [value1, value2, ...] Values to remove. * @returns {Array} Returns a new filtered array. * @example * * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); * // => [2, 3, 4] */ function without(array) { var result = []; if (!array) { return result; } var index = -1, length = array.length, contains = cachedContains(arguments, 1, 20); while (++index < length) { if (!contains(array[index])) { result.push(array[index]); } } return result; } /** * Merges 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. * @example * * _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]); * // => [['moe', 30, true], ['larry', 40, false], ['curly', 50, false]] */ function zip(array) { if (!array) { return []; } var index = -1, length = max(pluck(arguments, 'length')), result = Array(length); while (++index < length) { result[index] = pluck(arguments, index); } return result; } /** * Merges an array of `keys` and an array of `values` into a single object. * * @static * @memberOf _ * @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', 'curly'], [30, 40, 50]); * // => { 'moe': 30, 'larry': 40, 'curly': 50 } */ function zipObject(keys, values) { if (!keys) { return {}; } var index = -1, length = keys.length, result = {}; values || (values = []); while (++index < length) { result[keys[index]] = values[index]; } return result; } /*--------------------------------------------------------------------------*/ /** * Creates a new function that is restricted to executing only after it is * called `n` times. * * @static * @memberOf _ * @category Functions * @param {Number} n The number of times the function must be called before * it is executed. * @param {Function} func The function to restrict. * @returns {Function} Returns the new restricted function. * @example * * var renderNotes = _.after(notes.length, render); * _.forEach(notes, function(note) { * note.asyncSave({ 'success': renderNotes }); * }); * // `renderNotes` is run once, after all notes have saved */ function after(n, func) { if (n < 1) { return func(); } return function() { if (--n < 1) { return func.apply(this, arguments); } }; } /** * Creates a new 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`. * * @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 {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; * }; * * 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!' */ function bind(func, thisArg) { var methodName, isFunc = toString.call(func) == funcClass; // juggle arguments if (!isFunc) { methodName = thisArg; thisArg = func; } // use `Function#bind` if it exists and is fast // (in V8 `Function#bind` is slower except when partially applied) else if (isBindFast || (nativeBind && arguments.length > 2)) { 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 result && objectTypes[typeof result] ? result : thisBinding } return func.apply(thisBinding, args); } return bound; } /** * Binds methods on `object` to `object`, overwriting the existing method. * If no method names are provided, all the function properties of `object` * will be bound. * * @static * @memberOf _ * @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`. * @example * * var buttonView = { * 'label': 'lodash', * '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 */ var bindAll = createIterator({ 'useHas': false, 'useStrict': false, 'args': 'object', 'init': 'object', 'top': 'var funcs = arguments,\n' + ' length = funcs.length;\n' + 'if (length > 1) {\n' + ' for (var index = 1; index < length; index++)\n' + ' result[funcs[index]] = bind(result[funcs[index]], result);\n' + ' return result\n' + '}', 'inLoop': 'if (toString.call(result[index]) == funcClass)' + ' result[index] = bind(result[index], result)' }); /** * Creates a new 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()))`. * * @static * @memberOf _ * @category Functions * @param {Function} [func1, func2, ...] Functions to compose. * @returns {Function} Returns the new composed function. * @example * * var greet = function(name) { return 'hi: ' + name; }; * var exclaim = function(statement) { return statement + '!'; }; * var welcome = _.compose(exclaim, greet); * welcome('moe'); * // => 'hi: moe!' */ function compose() { var funcs = arguments; return function() { var args = arguments, length = funcs.length; while (length--) { args = [funcs[length].apply(this, args)]; } return args[0]; }; } /** * Creates a new 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 * the debounced function will return the result of the last `func` call. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to debounce. * @param {Number} wait The number of milliseconds to delay. * @param {Boolean} immediate A flag to indicate execution is on the leading * edge of the timeout. * @returns {Function} Returns the new debounced function. * @example * * var lazyLayout = _.debounce(calculateLayout, 300); * jQuery(window).on('resize', lazyLayout); */ function debounce(func, wait, immediate) { var args, result, thisArg, timeoutId; function delayed() { timeoutId = null; if (!immediate) { func.apply(thisArg, args); } } return function() { var isImmediate = immediate && !timeoutId; args = arguments; thisArg = this; clearTimeout(timeoutId); timeoutId = setTimeout(delayed, wait); if (isImmediate) { result = func.apply(thisArg, args); } return result; }; } /** * Executes the `func` function after `wait` milliseconds. Additional arguments * are 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 {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. * @returns {Number} Returns the `setTimeout` timeout id. * @example * * var log = _.bind(console.log, console); * _.delay(log, 1000, 'logged later'); * // => 'logged later' (Appears after one second.) */ function delay(func, wait) { var args = slice.call(arguments, 2); return setTimeout(function() { return func.apply(undefined, args); }, wait); } /** * Defers executing the `func` function until the current call stack has cleared. * Additional arguments are passed to `func` when it is invoked. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to defer. * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. * @returns {Number} Returns the `setTimeout` timeout id. * @example * * _.defer(function() { alert('deferred'); }); * // returns from the function before `alert` is called */ function defer(func) { var args = slice.call(arguments, 1); return setTimeout(function() { return func.apply(undefined, args); }, 1); } /** * Creates a new 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. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to have its output memoized. * @param {Function} [resolver] A function used to resolve the cache key. * @returns {Function} Returns the new memoizing function. * @example * * var fibonacci = _.memoize(function(n) { * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); * }); */ 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)); }; } /** * Creates a new function that is restricted to one execution. Repeat calls to * the function will return the value of the first call. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to restrict. * @returns {Function} Returns the new restricted function. * @example * * var initialize = _.once(createApplication); * initialize(); * initialize(); * // Application is only created once. */ function once(func) { var result, ran = false; return function() { if (ran) { return result; } ran = true; result = func.apply(this, arguments); 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. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to partially apply arguments to. * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. * @returns {Function} Returns the new partially applied function. * @example * * var greet = function(greeting, name) { return greeting + ': ' + name; }; * var hi = _.partial(greet, 'hi'); * 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; }; } /** * 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 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 _ * @category Functions * @param {Function} func The function to throttle. * @param {Number} wait The number of milliseconds to throttle executions to. * @returns {Function} Returns the new throttled function. * @example * * var throttled = _.throttle(updatePosition, 100); * jQuery(window).on('scroll', throttled); */ function throttle(func, wait) { var args, result, thisArg, timeoutId, lastCalled = 0; function trailingCall() { lastCalled = new Date; timeoutId = null; func.apply(thisArg, args); } return function() { var now = new Date, remain = wait - (now - lastCalled); args = arguments; thisArg = this; if (remain <= 0) { lastCalled = now; result = func.apply(thisArg, args); } else if (!timeoutId) { timeoutId = setTimeout(trailingCall, remain); } 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. * * @static * @memberOf _ * @category Functions * @param {Function} func The function 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; }; * hello = _.wrap(hello, function(func) { * return 'before, ' + func('moe') + ', after'; * }); * hello(); * // => 'before, hello: moe, after' */ function wrap(func, wrapper) { return function() { var args = [func]; if (arguments.length) { push.apply(args, arguments); } return wrapper.apply(this, args); }; } /*--------------------------------------------------------------------------*/ /** * 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 value && objectTypes[typeof value] ? (isArray(value) ? value.slice() : extend({}, value)) : value; } /** * Assigns missing properties on `object` with default values from the defaults * objects. Once 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 (result[index] == null) ' + 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({ 'useHas': false, 'args': 'object', 'init': '[]', 'inLoop': 'if (toString.call(iteratee[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 `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 Firefox < 4 and IE < 9 which detect // `arguments` as `[object Object]` if (!isArguments(arguments)) { isArguments = function(value) { return !!(value && hasOwnProperty.call(value, 'callee')); }; } /** * 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) { return toString.call(value) == arrayClass; }; /** * Checks if `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 `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 `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 `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 * * _.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 `undefined == null` if (a == null || b == null) { 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 `value` is a finite number. * Note: This is not the same as native `isFinite`, which will return true for * booleans and other values. See http://es5.github.com/#x15.1.2.5. * * @deprecated * @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 `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 `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 value && objectTypes[typeof value]; } /** * Checks if `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. * * @deprecated * @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 `value` is `null`. * * @deprecated * @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 `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 `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 `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 `value` is `undefined`. * * @deprecated * @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' && propertyIsEnumerable.call(object, 'prototype') ? 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. * * @deprecated * @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) { if (!value) { return 0; } var length = value.length; return length === length >>> 0 ? value.length : keys(value).length; } /** * 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(iteratee[index])' }); /*--------------------------------------------------------------------------*/ /** * Escapes a string for inclusion in HTML, replacing `&`, `<`, `"`, and `'` * characters. * * @static * @memberOf _ * @category Utilities * @param {String} string The string to escape. * @returns {String} Returns the escaped string. * @example * * _.escape('Curly, Larry & Moe'); * // => "Curly, Larry & Moe" */ function escape(string) { return string == null ? '' : (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 _ * @category Utilities * @param {Mixed} value Any value. * @returns {Mixed} Returns `value`. * @example * * var moe = { 'name': 'moe' }; * moe === _.identity(moe); * // => true */ function identity(value) { return value; } /** * Adds functions properties of `object` to the `lodash` function and chainable * wrapper. * * @static * @memberOf _ * @category Utilities * @param {Object} object The object of function properties to add to `lodash`. * @example * * _.mixin({ * 'capitalize': function(string) { * return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); * } * }); * * _.capitalize('curly'); * // => 'Curly' * * _('larry').capitalize(); * // => 'Larry' */ 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); } var result = func.apply(lodash, args); if (this._chain) { result = new LoDash(result); result._chain = true; } return result; }; }); } /** * Reverts the '_' variable to its previous value and returns a reference to * the `lodash` function. * * @static * @memberOf _ * @category Utilities * @returns {Function} Returns the `lodash` function. * @example * * var lodash = _.noConflict(); */ function noConflict() { window._ = oldDash; return this; } /** * 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. * * @deprecated * @static * @memberOf _ * @category Utilities * @param {Object} object The object to inspect. * @param {String} property The property to get the result of. * @returns {Mixed} Returns the resolved value. * @example * * var object = { * 'cheese': 'crumpets', * 'stuff': function() { * return 'nonsense'; * } * }; * * _.result(object, 'cheese'); * // => 'crumpets' * * _.result(object, 'stuff'); * // => '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; } /** * A micro-templating method that handles arbitrary delimiters, preserves * whitespace, and correctly escapes quotes within interpolated code. * * @static * @memberOf _ * @category Utilities * @param {String} text The template text. * @param {Obect} data The data object used to populate the text. * @param {Object} options The options object. * @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 %>'); * compiled({ 'name': 'moe' }); * // => 'hello: moe' * * var list = '<% _.forEach(people, function(name) { %>
  • <%= name %>
  • <% }); %>'; * _.template(list, { 'people': ['moe', 'curly', 'larry'] }); * // => '
  • moe
  • curly
  • larry
  • ' * * var template = _.template('<%- value %>'); * template({ 'value': ' */ function template(text, data, options) { // based on John Resig's `tmpl` implementation // http://ejohn.org/blog/javascript-micro-templating/ // and Laura Doktorova's doT.js // https://github.com/olado/doT options || (options = {}); var isEvaluating, result, escapeDelimiter = options.escape, evaluateDelimiter = options.evaluate, interpolateDelimiter = options.interpolate, settings = lodash.templateSettings, variable = options.variable; // use default settings if no options object is provided if (escapeDelimiter == null) { escapeDelimiter = settings.escape; } if (evaluateDelimiter == null) { evaluateDelimiter = settings.evaluate; } if (interpolateDelimiter == null) { interpolateDelimiter = settings.interpolate; } // tokenize delimiters to avoid escaping them if (escapeDelimiter) { text = text.replace(escapeDelimiter, tokenizeEscape); } if (interpolateDelimiter) { text = text.replace(interpolateDelimiter, tokenizeInterpolate); } if (evaluateDelimiter != lastEvaluateDelimiter) { // generate `reEvaluateDelimiter` to match `_.templateSettings.evaluate` // and internal ``, `` delimiters lastEvaluateDelimiter = evaluateDelimiter; reEvaluateDelimiter = RegExp( (evaluateDelimiter ? evaluateDelimiter.source : '($^)') + '||' , 'g'); } isEvaluating = tokenized.length; text = text.replace(reEvaluateDelimiter, tokenizeEvaluate); isEvaluating = isEvaluating != tokenized.length; // 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; // if `options.variable` is not specified and the template contains "evaluate" // delimiters, wrap a with-statement around the generated code to add the // data object to the top of the scope chain if (!variable) { variable = settings.variable || lastVariable || 'obj'; if (isEvaluating) { text = 'with (' + variable + ') {\n' + text + '\n}\n'; } else { if (variable != lastVariable) { // generate `reDoubleVariable` to match references like `obj.obj` inside // transformed "escape" and "interpolate" delimiters lastVariable = variable; reDoubleVariable = RegExp('(\\(\\s*)' + variable + '\\.' + variable + '\\b', 'g'); } // avoid a with-statement by prepending data object references to property names text = text .replace(reInsertVariable, '$&' + variable + '.') .replace(reDoubleVariable, '$1__d'); } } // cleanup code by stripping empty strings text = ( isEvaluating ? text.replace(reEmptyStringLeading, '') : text) .replace(reEmptyStringMiddle, '$1') .replace(reEmptyStringTrailing, '$1;'); // frame code as the function body text = 'function(' + variable + ') {\n' + variable + ' || (' + variable + ' = {});\n' + 'var __t, __p = \'\', __e = _.escape' + (isEvaluating ? ', __j = Array.prototype.join;\n' + 'function print() { __p += __j.call(arguments, \'\') }\n' : ', __d = ' + variable + '.' + variable + ' || ' + variable + ';\n' ) + text + '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 { result = Function('_', 'return ' + text)(lodash); } catch(e) { result = function() { throw e; }; } 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; return result; } /** * Executes the `callback` function `n` times. The `callback` is bound to * `thisArg` and invoked with 1 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. * @example * * _.times(3, function() { genie.grantWish(); }); * // => calls `genie.grantWish()` 3 times * * _.times(3, function() { this.grantWish(); }, genie); * // => also calls `genie.grantWish()` 3 times */ function times(n, callback, thisArg) { var index = -1; if (thisArg) { while (++index < n) { callback.call(thisArg, index); } } else { while (++index < n) { callback(index); } } } /** * 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 {Number|String} Returns a numeric id if no prefix is passed, else * a string id may be returned. * @example * * _.uniqueId('contact_'); * // => 'contact_104' */ function uniqueId(prefix) { var id = idCounter++; return prefix ? prefix + id : id; } /*--------------------------------------------------------------------------*/ /** * Wraps the value in a `lodash` wrapper object. * * @static * @memberOf _ * @category Chaining * @param {Mixed} value The value to wrap. * @returns {Object} Returns the wrapper object. * @example * * var stooges = [ * { 'name': 'moe', 'age': 40 }, * { 'name': 'larry', 'age': 50 }, * { 'name': 'curly', 'age': 60 } * ]; * * var youngest = _.chain(stooges) * .sortBy(function(stooge) { return stooge.age; }) * .map(function(stooge) { return stooge.name + ' is ' + stooge.age; }) * .first() * .value(); * // => 'moe is 40' */ function chain(value) { value = new LoDash(value); value._chain = true; return value; } /** * 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 `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; } /** * Enables method chaining on the wrapper object. * * @name chain * @deprecated * @memberOf _ * @category Chaining * @returns {Mixed} Returns the wrapper object. * @example * * _([1, 2, 3]).value(); * // => [1, 2, 3] */ function wrapperChain() { this._chain = true; return this; } /** * Extracts the wrapped value. * * @name value * @memberOf _ * @category Chaining * @returns {Mixed} Returns the wrapped value. * @example * * _([1, 2, 3]).value(); * // => [1, 2, 3] */ function wrapperValue() { return this._wrapped; } /*--------------------------------------------------------------------------*/ /** * The semantic version number. * * @static * @memberOf _ * @type String */ lodash.VERSION = '0.4.2'; // assign static methods 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.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.invoke = invoke; lodash.isArguments = isArguments; lodash.isArray = isArray; lodash.isBoolean = isBoolean; lodash.isDate = isDate; lodash.isElement = isElement; lodash.isEmpty = isEmpty; lodash.isEqual = isEqual; lodash.isFinite = isFinite; lodash.isFunction = isFunction; lodash.isNaN = isNaN; lodash.isNull = isNull; lodash.isNumber = isNumber; lodash.isObject = isObject; 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.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.uniqueId = uniqueId; lodash.values = values; lodash.without = without; lodash.wrap = wrap; lodash.zip = zip; lodash.zipObject = zipObject; // assign 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; /*--------------------------------------------------------------------------*/ // assign private `LoDash` constructor's prototype LoDash.prototype = lodash.prototype; // add all static 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 all mutator Array 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; func.apply(value, arguments); // 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. if (value.length === 0) { delete value[0]; } if (this._chain) { value = new LoDash(value); value._chain = true; } return value; }; }); // add all accessor Array functions to the wrapper. forEach(['concat', 'join', 'slice'], function(methodName) { var func = ArrayProto[methodName]; LoDash.prototype[methodName] = function() { var value = this._wrapped, result = func.apply(value, arguments); if (this._chain) { result = new LoDash(result); result._chain = true; } return result; }; }); /*--------------------------------------------------------------------------*/ // expose Lo-Dash // some AMD build optimizers, like r.js, check for specific condition patterns like the following: if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) { // Expose Lo-Dash to the global object even when an AMD loader is present in // case Lo-Dash was injected by a third-party script and not intended to be // loaded as a module. The global assignment can be reverted in the Lo-Dash // module via its `noConflict()` method. window._ = lodash; // define as an anonymous module so, through path mapping, it can be // referenced as the "underscore" module define(function() { return lodash; }); } // check for `exports` after `define` in case a build optimizer adds an `exports` object else if (freeExports) { // in Node.js or RingoJS v0.8.0+ if (typeof module == 'object' && module && module.exports == freeExports) { (module.exports = lodash)._ = lodash; } // in Narwhal or RingoJS v0.7.0- else { freeExports._ = lodash; } } else { // in a browser or Rhino window._ = lodash; } }(this));