/*! * Lo-Dash v0.7.0 * 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, BoolProto = Boolean.prototype, ObjectProto = Object.prototype, NumberProto = Number.prototype, StringProto = String.prototype; /** Used to generate unique IDs */ var idCounter = 0; /** Used by `cachedContains` as the default size when optimizations are enabled for large arrays */ var largeArraySize = 30; /** 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 HTML entities */ var reEscapedHtml = /&(?:amp|lt|gt|quot|#x27);/g; /** Used to match empty string literals in compiled template source */ var reEmptyStringLeading = /\b__p \+= '';/g, reEmptyStringMiddle = /\b(__p \+=) '' \+/g, reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; /** Used to match regexp flags from their coerced string values */ var reFlags = /\w*$/; /** 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 internally used tokens in template text */ var reToken = /__token(\d+)__/g; /** Used to match HTML characters */ 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 tokenHead = '__token', tokenFoot = '__'; /** 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, nativeFloor = Math.floor, nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray, nativeIsFinite = window.isFinite, nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys, nativeMax = Math.max, nativeMin = Math.min, nativeRandom = Math.random; /** `Object#toString` result shortcuts */ var argsClass = '[object Arguments]', arrayClass = '[object Array]', boolClass = '[object Boolean]', dateClass = '[object Date]', funcClass = '[object Function]', numberClass = '[object Number]', objectClass = '[object Object]', regexpClass = '[object RegExp]', stringClass = '[object String]'; /** 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; /** * Detect if `Array#shift` and `Array#splice` augment array-like objects * incorrectly: * * 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. */ var hasObjectSpliceBug; /** Detect if own properties are iterated after inherited properties (IE < 9) */ var iteratesOwnLast; /** Detect if an `arguments` object's indexes are non-enumerable (IE < 9) */ var noArgsEnum = true; (function() { var object = { '0': 1, 'length': 1 }, props = []; function ctor() { this.x = 1; } ctor.prototype = { 'valueOf': 1, 'y': 1 }; for (var prop in new ctor) { props.push(prop); } for (prop in arguments) { noArgsEnum = !prop; } hasDontEnumBug = (props + '').length < 4; iteratesOwnLast = props[0] != 'x'; hasObjectSpliceBug = (props.splice.call(object, 0, 1), object[0]); }(1)); /** Detect if an `arguments` object's [[Class]] is unresolvable (Firefox < 4, IE < 9) */ var noArgsClass = !isArguments(arguments); /** 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 a node's [[Class]] is unresolvable (IE < 9) * and that the JS engine won't error when attempting to coerce an object to * a string without a `toString` property value of `typeof` "function". */ try { var noNodeClass = ({ 'toString': 0 } + '', toString.call(window.document || 0) == objectClass); } catch(e) { } /* 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 (IE, Opera, V8) */ var isKeysFast = nativeKeys && /^.+$|true/.test(nativeKeys + !!window.attachEvent); /* Detect if strict mode, "use strict", is inferred to be fast (V8) */ var isStrictFast = !isBindFast; /** * Detect if sourceURL syntax is usable without erroring: * * The JS engine in Adobe products, like InDesign, will throw a syntax error * when it encounters a single line comment beginning with the `@` symbol. * * The JS engine in Narwhal will generate the function `function anonymous(){//}` * and throw a syntax error. * * Avoid comments beginning `@` symbols in IE because they are part of its * non-standard conditional compilation support. * http://msdn.microsoft.com/en-us/library/121hztk3(v=vs.94).aspx */ try { var useSourceURL = (Function('//@')(), !window.attachEvent); } catch(e) { } /** Used to identify object classifications that are array-like */ var arrayLikeClasses = {}; arrayLikeClasses[boolClass] = arrayLikeClasses[dateClass] = arrayLikeClasses[funcClass] = arrayLikeClasses[numberClass] = arrayLikeClasses[objectClass] = arrayLikeClasses[regexpClass] = false; arrayLikeClasses[argsClass] = arrayLikeClasses[arrayClass] = arrayLikeClasses[stringClass] = true; /** Used to identify object classifications that `_.clone` supports */ var cloneableClasses = {}; cloneableClasses[argsClass] = cloneableClasses[funcClass] = false; cloneableClasses[arrayClass] = cloneableClasses[boolClass] = cloneableClasses[dateClass] = cloneableClasses[numberClass] = cloneableClasses[objectClass] = cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true; /** * Used to convert characters to HTML entities: * * Though the `>` character is escaped for symmetry, characters like `>` and `/` * don't require escaping in HTML and have no special meaning unless they're part * of a tag or an unquoted attribute value. * http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact") */ var htmlEscapes = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; /** Used to convert HTML entities to characters */ var htmlUnescapes = { '&': '&', '<': '<', '>': '>', '"': '"', ''': "'" }; /** 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, 'unknown': true }; /** 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, the template delimiters used by Lo-Dash are similar to those in * embedded Ruby (ERB). Change the following template settings to use alternative * delimiters. * * @static * @memberOf _ * @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': '' }; /*--------------------------------------------------------------------------*/ /** * 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, value, 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 > -1 && 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' + ' value = iteratee[index];\n' + ' <%= arrayBranch.inLoop %>\n' + ' }' + ' <% if (objectBranch) { %>\n}<% } %>' + '<% } %>' + // the following branch is for iterating an object's own/inherited properties '<% if (objectBranch) { %>' + ' <% if (arrayBranch) { %>\nelse {' + // add support for iterating over `arguments` objects if needed ' <% } else if (noArgsEnum) { %>\n' + ' var length = iteratee.length; index = -1;\n' + ' if (length && isArguments(iteratee)) {\n' + ' while (++index < length) {\n' + ' value = iteratee[index += \'\'];\n' + ' <%= objectBranch.inLoop %>\n' + ' }\n' + ' } else {' + ' <% } %>' + // Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 // (if the prototype or a property on the prototype has been set) // incorrectly sets a function's `prototype` property [[Enumerable]] // value to `true`. Because of this Lo-Dash standardizes on skipping // the the `prototype` property of functions regardless of its // [[Enumerable]] value. ' <% if (!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 ownIndex = -1,\n' + ' ownProps = objectTypes[typeof iteratee] ? nativeKeys(iteratee) : [],\n' + ' length = ownProps.length;\n\n' + ' <%= objectBranch.beforeLoop %>;\n' + ' while (++ownIndex < length) {\n' + ' index = ownProps[ownIndex];\n' + ' <% if (!hasDontEnumBug) { %>if (!(skipProto && index == \'prototype\')) {\n <% } %>' + ' value = iteratee[index];\n' + ' <%= objectBranch.inLoop %>\n' + ' <% if (!hasDontEnumBug) { %>}\n<% } %>' + ' }' + // else using a for-in loop ' <% } else { %>\n' + ' <%= objectBranch.beforeLoop %>;\n' + ' for (index in iteratee) {<%' + ' if (!hasDontEnumBug || useHas) { %>\n if (<%' + ' if (!hasDontEnumBug) { %>!(skipProto && index == \'prototype\')<% }' + ' if (!hasDontEnumBug && useHas) { %> && <% }' + ' if (useHas) { %>hasOwnProperty.call(iteratee, index)<% }' + ' %>) {' + ' <% } %>\n' + ' value = iteratee[index];\n' + ' <%= objectBranch.inLoop %>;\n' + ' <% if (!hasDontEnumBug || useHas) { %>}\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' + ' value = iteratee[index];\n' + ' <%= objectBranch.inLoop %>\n' + ' }' + ' <% } %>' + ' <% } %>' + ' <% if (arrayBranch || noArgsEnum) { %>\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': 'if (callback(value, index, collection) === false) return result' }; /** Reusable iterator options for `countBy`, `groupBy`, and `sortBy` */ var countByIteratorOptions = { 'init': '{}', 'top': 'var prop;\n' + 'if (typeof callback != \'function\') {\n' + ' var valueProp = callback;\n' + ' callback = function(value) { return value[valueProp] }\n' + '}\n' + 'else if (thisArg) {\n' + ' callback = iteratorBind(callback, thisArg)\n' + '}', 'inLoop': 'prop = callback(value, index, collection);\n' + '(hasOwnProperty.call(result, prop) ? result[prop]++ : result[prop] = 1)' }; /** Reusable iterator options for `every` and `some` */ var everyIteratorOptions = { 'init': 'true', 'inLoop': 'if (!callback(value, index, collection)) return !result' }; /** Reusable iterator options for `defaults` and `extend` */ var extendIteratorOptions = { 'useHas': false, 'useStrict': false, 'args': 'object', 'init': 'object', 'top': 'for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {\n' + ' if (iteratee = arguments[argsIndex]) {', 'inLoop': 'result[index] = value', 'bottom': ' }\n}' }; /** Reusable iterator options for `filter`, `reject`, and `where` */ var filterIteratorOptions = { 'init': '[]', 'inLoop': 'callback(value, index, collection) && result.push(value)' }; /** 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(value, index, collection)', 'object': 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '(callback(value, index, collection))' } }; /** Reusable iterator options for `omit` and `pick` */ var omitIteratorOptions = { 'useHas': false, 'args': 'object, callback, thisArg', 'init': '{}', 'top': 'var isFunc = typeof callback == \'function\';\n' + 'if (!isFunc) {\n' + ' var props = concat.apply(ArrayProto, arguments)\n' + '} else if (thisArg) {\n' + ' callback = iteratorBind(callback, thisArg)\n' + '}', 'inLoop': 'if (isFunc\n' + ' ? !callback(value, index, object)\n' + ' : indexOf(props, index) < 0\n' + ') result[index] = value' }; /*--------------------------------------------------------------------------*/ /** * 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 || largeArraySize), 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], useStrict = data.useStrict; data.firstArg = firstArg; data.hasDontEnumBug = hasDontEnumBug; data.isKeysFast = isKeysFast; data.noArgsEnum = noArgsEnum; data.shadowed = shadowed; data.useHas = data.useHas !== false; data.useStrict = useStrict == null ? isStrictFast : useStrict; if (data.noCharByIndex == null) { 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( 'arrayLikeClasses, ArrayProto, bind, compareAscending, concat, forIn, ' + 'hasOwnProperty, identity, indexOf, isArguments, isArray, isFunction, ' + 'isPlainObject, iteratorBind, objectClass, objectTypes, nativeKeys, ' + 'propertyIsEnumerable, slice, stringClass, toString', 'var callee = function(' + args + ') {\n' + iteratorTemplate(data) + '\n};\n' + 'return callee' ); // return the compiled function return factory( arrayLikeClasses, ArrayProto, bind, compareAscending, concat, forIn, hasOwnProperty, identity, indexOf, isArguments, isArray, isFunction, isPlainObject, iteratorBind, objectClass, objectTypes, nativeKeys, propertyIsEnumerable, slice, stringClass, toString ); } /** * Used by `sortBy` to compare transformed `collection` values, stable sorting * them in ascending order. * * @private * @param {Object} a The object to compare to `b`. * @param {Object} b The object to compare to `a`. * @returns {Number} Returns the sort order indicator of `1` or `-1`. */ function compareAscending(a, b) { var ai = a.index, bi = b.index; a = a.criteria; b = b.criteria; // ensure a stable sort in V8 and other engines // http://code.google.com/p/v8/issues/detail?id=90 if (a !== b) { if (a > b || a === undefined) { return 1; } if (a < b || b === undefined) { return -1; } } return ai < bi ? -1 : 1; } /** * 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 convert characters to HTML entities. * * @private * @param {String} match The matched character to escape. * @returns {String} Returns the escaped character. */ function escapeHtmlChar(match) { return htmlEscapes[match]; } /** * 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 } /** * 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 (match && reComplexDelimiter.test(value)) { return ''; } var index = tokenized.length; tokenized[index] = "' +\n__e(" + value + ") +\n'"; return tokenHead + index + tokenFoot; } /** * 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} escapeValue The complex "escape" delimiter value. * @param {String} interpolateValue The complex "interpolate" delimiter value. * @param {String} [evaluateValue] The "evaluate" delimiter value. * @returns {String} Returns a token. */ function tokenizeEvaluate(match, escapeValue, interpolateValue, evaluateValue) { if (evaluateValue) { var index = tokenized.length; tokenized[index] = "';\n" + evaluateValue + ";\n__p += '"; return tokenHead + index + tokenFoot; } return escapeValue ? tokenizeEscape(null, escapeValue) : tokenizeInterpolate(null, interpolateValue); } /** * 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 (match && reComplexDelimiter.test(value)) { return ''; } var index = tokenized.length; tokenized[index] = "' +\n((__t = (" + value + ")) == null ? '' : __t) +\n'"; return tokenHead + index + tokenFoot; } /** * Used by `unescape` to convert HTML entities to characters. * * @private * @param {String} match The matched character to unescape. * @returns {String} Returns the unescaped character. */ function unescapeHtmlChar(match) { return htmlUnescapes[match]; } /*--------------------------------------------------------------------------*/ /** * Checks if `value` is an `arguments` object. * * @static * @memberOf _ * @category Objects * @param {Mixed} value The value to check. * @returns {Boolean} Returns `true` if the `value` is an `arguments` object, else `false`. * @example * * (function() { return _.isArguments(arguments); })(1, 2, 3); * // => true * * _.isArguments([1, 2, 3]); * // => false */ function isArguments(value) { return toString.call(value) == argsClass; } // fallback for browsers that can't detect `arguments` objects by [[Class]] if (noArgsClass) { 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 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 typeof value == 'function'; } // fallback for older versions of Chrome and Safari if (isFunction(/x/)) { isFunction = function(value) { return toString.call(value) == funcClass; }; } /** * A fallback implementation of `isPlainObject` that checks if a given `value` * is an object created by the `Object` constructor, assuming objects created * by the `Object` constructor have no inherited enumerable properties and that * there are no `Object.prototype` extensions. * * @private * @param {Mixed} value The value to check. * @param {Boolean} [skipArgsCheck=false] Internally used to skip checks for * `arguments` objects. * @returns {Boolean} Returns `true` if `value` is a plain object, else `false`. */ function isPlainFallback(value, skipArgsCheck) { // avoid non-objects and false positives for `arguments` objects var result = false; if (!(value && typeof value == 'object') || (!skipArgsCheck && isArguments(value))) { return result; } // IE < 9 presents DOM nodes as `Object` objects except they have `toString` // methods that are `typeof` "string" and still can coerce nodes to strings. // Also check that the constructor is `Object` (i.e. `Object instanceof Object`) var ctor = value.constructor; if ((!noNodeClass || !(typeof value.toString != 'function' && typeof (value + '') == 'string')) && (!isFunction(ctor) || ctor instanceof ctor)) { // IE < 9 iterates inherited properties before own properties. If the first // iterated property is an object's own property then there are no inherited // enumerable properties. if (iteratesOwnLast) { forIn(value, function(value, key, object) { result = !hasOwnProperty.call(object, key); return false; }); return result === false; } // In most environments an object's own properties are iterated before // its inherited properties. If the last iterated property is an object's // own property then there are no inherited enumerable properties. forIn(value, function(value, key) { result = key; }); return result === false || hasOwnProperty.call(value, result); } return result; } /** * Checks if a given `value` is an object created by the `Object` constructor. * * @private * @param {Mixed} value The value to check. * @param {Boolean} [skipArgsCheck=false] Internally used to skip checks for * `arguments` objects. * @returns {Boolean} Returns `true` if `value` is a plain object, else `false`. */ var isPlainObject = objectTypes.__proto__ != ObjectProto ? isPlainFallback : function(value, skipArgsCheck) { if (!value) { return false; } var valueOf = value.valueOf, objProto = typeof valueOf == 'function' && (objProto = valueOf.__proto__) && objProto.__proto__; return objProto ? value == objProto || (value.__proto__ == objProto && (skipArgsCheck || !isArguments(value))) : isPlainFallback(value); }; /** * 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', 'init': '[]', 'inLoop': 'result.push(index)' }); /*--------------------------------------------------------------------------*/ /** * Creates a clone of `value`. If `deep` is `true`, all nested objects will * also be cloned otherwise they will be assigned by reference. If a value has * a `clone` method it will be used to perform the clone. Functions, DOM nodes, * `arguments` objects, and objects created by constructors other than `Object` * are **not** cloned unless they have a custom `clone` method. * * @static * @memberOf _ * @category Objects * @param {Mixed} value The value to clone. * @param {Boolean} deep A flag to indicate a deep clone. * @param {Object} [guard] Internally used to allow this method to work with * others like `_.map` without using their callback `index` argument for `deep`. * @param {Object} [data={}] Internally used to track traversed objects to avoid * circular references and indicate whether to perform a more thorough clone * of non-object values. * @returns {Mixed} Returns the cloned `value`. * @example * * var stooges = [ * { 'name': 'moe', 'age': 40 }, * { 'name': 'larry', 'age': 50 }, * { 'name': 'curly', 'age': 60 } * ]; * * _.clone({ 'name': 'moe' }); * // => { 'name': 'moe' } * * var shallow = _.clone(stooges); * shallow[0] === stooges[0]; * // => true * * var deep = _.clone(stooges, true); * shallow[0] === stooges[0]; * // => false */ function clone(value, deep, guard, data) { if (value == null) { return value; } if (guard) { deep = false; } // init internal data data || (data = { 'thorough': null }); // avoid slower checks on primitives if (data.thorough == null) { // primitives passed from iframes use the primary document's native prototypes data.thorough = !!(BoolProto.clone || NumberProto.clone || StringProto.clone); } // use custom `clone` method if available var isObj = objectTypes[typeof value]; if ((isObj || data.thorough) && value.clone && isFunction(value.clone)) { data.thorough = null; return value.clone(deep); } // inspect [[Class]] if (isObj) { // don't clone `arguments` objects, functions, or non-object Objects var className = toString.call(value); if (!cloneableClasses[className] || (noArgsClass && isArguments(value))) { return value; } var isArr = className == arrayClass; isObj = isArr || (className == objectClass ? isPlainObject(value, true) : isObj); } // shallow clone if (!isObj || !deep) { // don't clone functions return isObj ? (isArr ? slice.call(value) : extend({}, value)) : value; } var ctor = value.constructor; switch (className) { case boolClass: return new ctor(value == true); case dateClass: return new ctor(+value); case numberClass: case stringClass: return new ctor(value); case regexpClass: return ctor(value.source, reFlags.exec(value)); } var clones = data.clones || (data.clones = []), sources = data.sources || (data.sources = []), length = clones.length; // check for circular references and return corresponding clone while (length--) { if (sources[length] == value) { return clones[length]; } } // init cloned object var result = isArr ? ctor(length = value.length) : {}; // add current clone and original source value to the stack of traversed objects clones.push(result); sources.push(value); // recursively populate clone (susceptible to call stack limits) if (isArr) { var index = -1; while (++index < length) { result[index] = clone(value[index], deep, null, data); } } else { forOwn(value, function(objValue, key) { result[key] = clone(objValue, deep, null, data); }); } return result; } /** * Assigns enumerable properties of the default object(s) to the `destination` * object for all `destination` properties that resolve to `null`/`undefined`. * Once a property is set, additional defaults of the same property will be * ignored. * * @static * @memberOf _ * @category Objects * @param {Object} object The destination object. * @param {Object} [default1, default2, ...] The default objects. * @returns {Object} Returns the destination 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 }); /** * Assigns enumerable properties of the source object(s) 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). Callbacks may exit iteration * early by explicitly returning `false`. * * @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 `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). Callbacks may exit iteration early by * explicitly returning `false`. * * @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 `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); /** * Creates a sorted array of all enumerable properties, own and inherited, * of `object` that have function values. * * @static * @memberOf _ * @alias methods * @category Objects * @param {Object} object The object to inspect. * @returns {Array} Returns a new array of property names that have function values. * @example * * _.functions(_); * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] */ var functions = createIterator({ 'useHas': false, 'args': 'object', 'init': '[]', 'inLoop': 'if (isFunction(value)) 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 object ? hasOwnProperty.call(object, property) : false; } /** * Creates an object composed of the inverted keys and values of the given `object`. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to invert. * @returns {Object} Returns the created inverted object. * @example * * _.invert({ 'first': 'Moe', 'second': 'Larry', 'third': 'Curly' }); * // => { 'Moe': 'first', 'Larry': 'second', 'Curly': 'third' } (order is not guaranteed) */ var invert = createIterator({ 'args': 'object', 'init': '{}', 'inLoop': 'result[value] = index' }); /** * 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 : false; } /** * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a * length of `0` and objects with no own enumerable properties are considered * "empty". * * @static * @memberOf _ * @category Objects * @param {Array|Object|String} value The value to inspect. * @returns {Boolean} Returns `true` if the `value` is empty, else `false`. * @example * * _.isEmpty([1, 2, 3]); * // => false * * _.isEmpty({}); * // => true * * _.isEmpty(''); * // => true */ var isEmpty = createIterator({ 'args': 'value', 'init': 'true', 'top': 'var className = toString.call(value),\n' + ' length = value.length;\n' + 'if (arrayLikeClasses[className]' + (noArgsClass ? ' || isArguments(value)' : '') + ' ||\n' + ' (className == objectClass && length > -1 && length === length >>> 0 &&\n' + ' isFunction(value.splice))' + ') return !length', 'inLoop': { 'object': 'return false' } }); /** * Performs a deep comparison between two values to determine if they are * equivalent to each other. If a value has an `isEqual` method it will be * used to perform the comparison. * * @static * @memberOf _ * @category Objects * @param {Mixed} a The value to compare. * @param {Mixed} b The other value to compare. * @param {Object} [data={}] Internally used track traversed objects to avoid * circular references and indicate whether to perform a more thorough comparison * of non-object values. * @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, data) { // a strict comparison is necessary because `null == undefined` if (a == null || b == null) { return a === b; } // init internal data data || (data = { 'thorough': null }); // avoid slower checks on non-objects if (data.thorough == null) { // primitives passed from iframes use the primary document's native prototypes data.thorough = !!(BoolProto.isEqual || NumberProto.isEqual || StringProto.isEqual); } if (objectTypes[typeof a] || objectTypes[typeof b] || data.thorough) { // unwrap any LoDash wrapped values a = a.__wrapped__ || a; b = b.__wrapped__ || b; // use custom `isEqual` method if available if (a.isEqual && isFunction(a.isEqual)) { data.thorough = null; return a.isEqual(b); } if (b.isEqual && isFunction(b.isEqual)) { data.thorough = null; return b.isEqual(a); } } // exit early for identical values if (a === b) { // treat `+0` vs. `-0` as not equal return a !== 0 || (1 / a == 1 / b); } // compare [[Class]] names var className = toString.call(a); if (className != toString.call(b)) { return false; } switch (className) { case boolClass: case dateClass: // coerce dates and booleans to numbers, dates to milliseconds and booleans // to `1` or `0`, treating invalid dates coerced to `NaN` as not equal return +a == +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 regexpClass: case stringClass: // coerce regexes to strings (http://es5.github.com/#x15.10.6.4) // treat string primitives and their corresponding object instances as equal return a == b + ''; } // exit early, in older browsers, if `a` is array-like but not `b` var isArr = arrayLikeClasses[className]; if (noArgsClass && !isArr && (isArr = isArguments(a)) && !isArguments(b)) { return false; } // exit for functions and DOM nodes if (!isArr && (className != objectClass || (noNodeClass && ( (typeof a.toString != 'function' && typeof (a + '') == 'string') || (typeof b.toString != 'function' && typeof (b + '') == 'string'))))) { return false; } // assume cyclic structures are equal // the algorithm for detecting cyclic structures is adapted from ES 5.1 // section 15.12.3, abstract operation `JO` (http://es5.github.com/#x15.12.3) var stackA = data.stackA || (data.stackA = []), stackB = data.stackB || (data.stackB = []), length = stackA.length; while (length--) { if (stackA[length] == a) { return stackB[length] == b; } } var index = -1, result = true, size = 0; // add `a` and `b` to the stack of traversed objects stackA.push(a); stackB.push(b); // recursively compare objects and arrays (susceptible to call stack limits) if (isArr) { // compare lengths to determine if a deep comparison is necessary 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], data))) { break; } } } return result; } var ctorA = a.constructor, ctorB = b.constructor; // non `Object` object instances with different constructors are not equal if (ctorA != ctorB && !( isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB )) { 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 (!(hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], data))) { return false; } } } // ensure both objects have the same number of properties for (prop in b) { // The JS engine in Adobe products, 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--)) { // `size` will be `-1` if `b` has more properties than `a` return false; } } // handle JScript [[DontEnum]] bug if (hasDontEnumBug) { while (++index < 7) { prop = shadowed[index]; if (hasOwnProperty.call(a, prop) && !(hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], data))) { return false; } } } return true; } /** * 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 the language type of Object. * (e.g. arrays, functions, objects, regexes, `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 // and avoid a V8 bug // http://code.google.com/p/v8/issues/detail?id=2291 return value ? objectTypes[typeof value] : false; } /** * 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; } /** * Creates an array composed of the own enumerable property names of `object`. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to inspect. * @returns {Array} Returns a new array of property names. * @example * * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); * // => ['one', 'two', 'three'] (order is not guaranteed) */ var keys = !nativeKeys ? shimKeys : function(object) { var type = typeof object; // avoid iterating over the `prototype` property if (type == 'function' && propertyIsEnumerable.call(object, 'prototype')) { return shimKeys(object); } return object && objectTypes[type] ? nativeKeys(object) : []; }; /** * Merges enumerable properties of the source object(s) into 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. * @param {Object} [indicator] Internally used to indicate that the `stack` * argument is an array of traversed objects instead of another source object. * @param {Object} [data={}] Internally used to track traversed objects to avoid * circular references. * @returns {Object} Returns the destination object. * @example * * var stooges = [ * { 'name': 'moe' }, * { 'name': 'larry' } * ]; * * var ages = [ * { 'age': 40 }, * { 'age': 50 } * ]; * * _.merge(stooges, ages); * // => [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }] */ var merge = createIterator(extendIteratorOptions, { 'args': 'object, source, indicator', 'top': 'var isArr, source, recursive = indicator == isPlainObject,\n' + ' data = recursive ? arguments[3] : { values: [], sources: [] };\n' + 'for (var argsIndex = 1, argsLength = recursive ? 2 : arguments.length; argsIndex < argsLength; argsIndex++) {\n' + ' if (iteratee = arguments[argsIndex]) {', 'inLoop': 'if ((source = value) && ((isArr = isArray(source)) || isPlainObject(source))) {\n' + ' var found = false, values = data.values, sources = data.sources, stackLength = sources.length;\n' + ' while (stackLength--) {\n' + ' if (found = sources[stackLength] == source) break\n' + ' }\n' + ' if (found) {\n' + ' result[index] = values[stackLength]\n' + ' } else {\n' + ' values.push(value = (value = result[index]) && isArr\n' + ' ? (isArray(value) ? value : [])\n' + ' : (isPlainObject(value) ? value : {})\n' + ' );\n' + ' sources.push(source);\n' + ' result[index] = callee(value, source, isPlainObject, data)\n' + ' }\n' + '} else if (source != null) {\n' + ' result[index] = source\n' + '}' }); /** * Creates a shallow clone of `object` excluding the specified properties. * Property names may be specified as individual arguments or as arrays of * property names. If `callback` is passed, it will be executed for each property * in the `object`, omitting the properties `callback` returns truthy for. The * `callback` is bound to `thisArg` and invoked with 3 arguments; (value, key, object). * * @static * @memberOf _ * @category Objects * @param {Object} object The source object. * @param {Function|String} callback|[prop1, prop2, ...] The properties to omit * or the function called per iteration. * @param {Mixed} [thisArg] The `this` binding for the callback. * @returns {Object} Returns an object without the omitted properties. * @example * * _.omit({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'userid'); * // => { 'name': 'moe', 'age': 40 } * * _.omit({ 'name': 'moe', '_hint': 'knucklehead', '_seed': '96c4eb' }, function(value, key) { * return key.charAt(0) == '_'; * }); * // => { 'name': 'moe' } */ var omit = createIterator(omitIteratorOptions); /** * Creates a two dimensional array of the given object's key-value pairs, * i.e. `[[key1, value1], [key2, value2]]`. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to inspect. * @returns {Array} Returns new array of key-value pairs. * @example * * _.pairs({ 'moe': 30, 'larry': 40, 'curly': 50 }); * // => [['moe', 30], ['larry', 40], ['curly', 50]] (order is not guaranteed) */ var pairs = createIterator({ 'args': 'object', 'init':'[]', 'inLoop': 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '([index, value])' }); /** * Creates a shallow clone of `object` composed of the specified properties. * Property names may be specified as individual arguments or as arrays of * property names. If `callback` is passed, it will be executed for each property * in the `object`, picking the properties `callback` returns truthy for. The * `callback` is bound to `thisArg` and invoked with 3 arguments; (value, key, object). * * @static * @memberOf _ * @category Objects * @param {Object} object The source object. * @param {Function|String} callback|[prop1, prop2, ...] The properties to pick * or the function called per iteration. * @param {Mixed} [thisArg] The `this` binding for the callback. * @returns {Object} Returns an object composed of the picked properties. * @example * * _.pick({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'name', 'age'); * // => { 'name': 'moe', 'age': 40 } * * _.pick({ 'name': 'moe', '_hint': 'knucklehead', '_seed': '96c4eb' }, function(value, key) { * return key.charAt(0) != '_'; * }); * // => { 'name': 'moe' } */ var pick = createIterator(omitIteratorOptions, { 'top': 'if (typeof callback != \'function\') {\n' + ' var prop,\n' + ' props = concat.apply(ArrayProto, arguments),\n' + ' length = props.length;\n' + ' for (index = 1; index < length; index++) {\n' + ' prop = props[index];\n' + ' if (prop in object) result[prop] = object[prop]\n' + ' }\n' + '} else {\n' + ' if (thisArg) callback = iteratorBind(callback, thisArg)', 'inLoop': 'if (callback(value, index, object)) result[index] = value', 'bottom': '}' }); /** * Creates an array composed of the own enumerable property values of `object`. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to inspect. * @returns {Array} Returns a new array of property values. * @example * * _.values({ 'one': 1, 'two': 2, 'three': 3 }); * // => [1, 2, 3] */ var values = createIterator({ 'args': 'object', 'init': '[]', 'inLoop': 'result.push(value)' }); /*--------------------------------------------------------------------------*/ /** * Checks if a given `target` element 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 the `target` element 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(collection) == stringClass) return collection.indexOf(target) > -1' }, 'inLoop': 'if (value === target) return true' }); /** * Creates an object composed of keys returned from running each element of * `collection` through a `callback`. The corresponding value of each key is * the number of times the key was returned by `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 count by (e.g. 'length'). * * @static * @memberOf _ * @category Collections * @param {Array|Object|String} collection The collection to iterate over. * @param {Function|String} callback|property The function called per iteration * or property name to count by. * @param {Mixed} [thisArg] The `this` binding for the callback. * @returns {Object} Returns the composed aggregate object. * @example * * _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); }); * // => { '4': 1, '6': 2 } * * _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math); * // => { '4': 1, '6': 2 } * * _.countBy(['one', 'two', 'three'], 'length'); * // => { '3': 2, '5': 1 } */ var countBy = createIterator(baseIteratorOptions, countByIteratorOptions); /** * 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 elements pass the callback check, else `false`. * @example * * _.every([true, 1, null, 'yes'], Boolean); * // => false */ var every = createIterator(baseIteratorOptions, everyIteratorOptions); /** * Examines each element in a `collection`, returning an array of all elements * the `callback` returns truthy for. The `callback` is bound to `thisArg` and * invoked with 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 elements 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 element in a `collection`, returning the first one the `callback` * returns truthy for. The function returns as soon as it finds an acceptable * element, 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 element 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(value, index, collection)) return value' }); /** * Iterates over a `collection`, executing the `callback` for each element in * the `collection`. The `callback` is bound to `thisArg` and invoked with 3 * arguments; (value, index|key, collection). Callbacks may exit iteration * early by explicitly returning `false`. * * @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 `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); /** * Creates an object composed of keys returned from running each element of * `collection` through a `callback`. The corresponding value of each key is an * array of elements passed to `callback` that returned the key. The `callback` * is bound to `thisArg` and invoked with 3 arguments; (value, index|key, collection). * The `callback` argument may also be the name of a property to count by (e.g. 'length'). * * @static * @memberOf _ * @category Collections * @param {Array|Object|String} collection The collection to iterate over. * @param {Function|String} callback|property The function called per iteration * or property name to group by. * @param {Mixed} [thisArg] The `this` binding for the callback. * @returns {Object} Returns the composed aggregate object. * @example * * _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); }); * // => { '4': [4.2], '6': [6.1, 6.4] } * * _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math); * // => { '4': [4.2], '6': [6.1, 6.4] } * * _.groupBy(['one', 'two', 'three'], 'length'); * // => { '3': ['one', 'two'], '5': ['three'] } */ var groupBy = createIterator(baseIteratorOptions, countByIteratorOptions, { 'inLoop': 'prop = callback(value, index, collection);\n' + '(hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(value)' }); /** * 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 : value[methodName]).apply(value, args)', 'object': 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '((isFunc ? methodName : value[methodName]).apply(value, args))' } }); /** * Creates a new array of values by running each element in the `collection` * through a `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 elements 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] = value[property]', 'object': 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '(value[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 = iteratee[++index]' }, 'inLoop': { 'array': 'result = callback(result, value, index, collection)', 'object': 'result = noaccum\n' + ' ? (noaccum = false, value)\n' + ' : callback(result, value, 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); } // Opera 10.53-10.60 JITted `length >>> 0` returns the wrong value for negative numbers if (length > -1 && 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 elements that did **not** pass the callback check. * @example * * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); * // => [1, 3, 5] */ var reject = createIterator(baseIteratorOptions, filterIteratorOptions, { 'inLoop': '!' + filterIteratorOptions.inLoop }); /** * Gets the size of the `collection` by returning `collection.length` for arrays * and array-like objects or the number of own enumerable properties for objects. * * @static * @memberOf _ * @category Collections * @param {Array|Object|String} collection The collection to inspect. * @returns {Number} Returns `collection.length` or number of own enumerable properties. * @example * * _.size([1, 2]); * // => 2 * * _.size({ 'one': 1, 'two': 2, 'three': 3 }); * // => 3 * * _.size('curly'); * // => 5 */ function size(collection) { if (!collection) { return 0; } var length = collection.length; return length > -1 && length === length >>> 0 ? length : keys(collection).length; } /** * Checks if the `callback` returns a truthy value for **any** element of a * `collection`. The function returns as soon as it finds passing value, and * does not iterate over the entire `collection`. The `callback` is bound to * `thisArg` and invoked with 3 arguments; (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 element passes the callback check, else `false`. * @example * * _.some([null, 0, 'yes', false]); * // => true */ var some = createIterator(baseIteratorOptions, everyIteratorOptions, { 'init': 'false', 'inLoop': everyIteratorOptions.inLoop.replace('!', '') }); /** * Creates a new array, stable sorted in ascending order by the results of * running each element of `collection` through a `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|property 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 elements. * @example * * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); * // => [3, 1, 2] * * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math); * // => [3, 1, 2] * * _.sortBy(['larry', 'brendan', 'moe'], 'length'); * // => ['moe', 'larry', 'brendan'] */ var sortBy = createIterator(baseIteratorOptions, countByIteratorOptions, mapIteratorOptions, { 'inLoop': { 'array': 'result[index] = {\n' + ' criteria: callback(value, index, collection),\n' + ' index: index,\n' + ' value: value\n' + '}', 'object': 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '({\n' + ' criteria: callback(value, index, collection),\n' + ' index: index,\n' + ' value: value\n' + '})' }, 'bottom': 'result.sort(compareAscending);\n' + 'length = result.length;\n' + 'while (length--) {\n' + ' result[length] = result[length].value\n' + '}' }); /** * Converts the `collection`, to 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 && isFunction(collection.toArray)) { return collection.toArray(); } var length = collection.length; if (length > -1 && length === length >>> 0) { return (noArraySliceOnStrings ? toString.call(collection) == stringClass : typeof collection == 'string') ? collection.split('') : slice.call(collection); } return values(collection); } /** * Examines each element in a `collection`, returning an array of all elements * that contain the given `properties`. * * @static * @memberOf _ * @category Collections * @param {Array|Object|String} collection The collection to iterate over. * @param {Object} properties The object of properties/values to filter by. * @returns {Array} Returns a new array of elements that contain the given `properties`. * @example * * var stooges = [ * { 'name': 'moe', 'age': 40 }, * { 'name': 'larry', 'age': 50 }, * { 'name': 'curly', 'age': 60 } * ]; * * _.where(stooges, { 'age': 40 }); * // => [{ 'name': 'moe', 'age': 40 }] */ var where = createIterator(filterIteratorOptions, { 'args': 'collection, properties', 'top': 'var props = [];\n' + 'forIn(properties, function(value, prop) { props.push(prop) });\n' + 'var propsLength = props.length', 'inLoop': 'for (var prop, pass = true, propIndex = 0; propIndex < propsLength; propIndex++) {\n' + ' prop = props[propIndex];\n' + ' if (!(pass = value[prop] === properties[prop])) break\n' + '}\n' + 'pass && result.push(value)' }); /*--------------------------------------------------------------------------*/ /** * Creates 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; } /** * Creates a new array of `array` elements 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` elements 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 element of the `array`. Pass `n` to return the first `n` * elements 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 element or an array of the first `n` * elements 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 ? nativeMax(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 element of `array`. Pass `n` to exclude the last `n` * elements 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 element or `n` elements 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 using strict equality * for comparisons, i.e. `===`. * * @static * @memberOf _ * @category Arrays * @param {Array} [array1, array2, ...] Arrays to process. * @returns {Array} Returns a new array of unique elements, 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, argsLength = arguments.length, cache = [], index = -1, length = array.length; array: while (++index < length) { value = array[index]; if (indexOf(result, value) < 0) { for (var argsIndex = 1; argsIndex < argsLength; argsIndex++) { if (!(cache[argsIndex] || (cache[argsIndex] = cachedContains(arguments[argsIndex])))(value)) { continue array; } } result.push(value); } } return result; } /** * Gets the last element of the `array`. Pass `n` to return the lasy `n` * elementsvof 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 element or an array of the last `n` * elements 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 ? nativeMax(0, index + fromIndex) : nativeMin(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 object composed from arrays of `keys` and `values`. Pass either * a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`, or * two arrays, one of `keys` and one of corresponding `values`. * * @static * @memberOf _ * @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 * * _.object(['moe', 'larry', 'curly'], [30, 40, 50]); * // => { 'moe': 30, 'larry': 40, 'curly': 50 } */ function object(keys, values) { if (!keys) { return {}; } var index = -1, length = keys.length, result = {}; while (++index < length) { if (values) { result[keys[index]] = values[index]; } else { result[keys[index][0]] = keys[index][1]; } } 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) { start = +start || 0; step = +step || 1; if (end == null) { end = start; start = 0; } // use `Array(length)` so V8 will avoid the slower "dictionary" mode // http://www.youtube.com/watch?v=XAqIpGU8ZZk#t=16m27s var index = -1, length = nativeMax(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 drop, tail * @category Arrays * @param {Array} array The array to query. * @param {Number} [n] The number of elements to return. * @param {Object} [guard] Internally used to allow this method to work with * others like `_.map` without using their callback `index` argument for `n`. * @returns {Array} Returns all but the first value or `n` values of `array`. * @example * * _.rest([3, 2, 1]); * // => [2, 1] */ function rest(array, n, guard) { if (!array) { return []; } return slice.call(array, (n == null || guard) ? 1 : n); } /** * Creates 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 = nativeFloor(nativeRandom() * (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 using strict equality for * comparisons, i.e. `===`. * * @static * @memberOf _ * @category Arrays * @param {Array} [array1, array2, ...] Arrays to process. * @returns {Array} Returns a new array of unique values, in order, that are * present in 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; } /** * Creates a duplicate-value-free version of the `array` using strict equality * for comparisons, i.e. `===`. If the `array` is already sorted, passing `true` * for `isSorted` will run a faster algorithm. If `callback` is passed, each * element of `array` is passed through a 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; } /** * Creates 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; } /** * Groups the elements of each array at their corresponding indexes. Useful for * separate data sources that are coordinated through matching array indexes. * For a matrix of nested arrays, `_.zip.apply(...)` can transpose the matrix * in a similar fashion. * * @static * @memberOf _ * @category Arrays * @param {Array} [array1, array2, ...] Arrays to process. * @returns {Array} Returns a new array of grouped elements. * @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; } /*--------------------------------------------------------------------------*/ /** * 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 = isFunction(func); // 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 ? partialArgs.concat(slice.call(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 `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' + ' }\n' + ' return result\n' + '}', 'inLoop': 'if (isFunction(result[index])) {\n' + ' result[index] = bind(result[index], result)\n' + '}' }); /** * 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) { result = 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 * will be passed to `func` when it is invoked. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to delay. * @param {Number} wait The number of milliseconds to delay execution. * @param {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 will be 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); // clear the `func` variable so the function may be garbage collected func = null; return result; }; } /** * Creates a new function that, when called, invokes `func` with any additional * `partial` arguments prepended to those passed to the new 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; result = 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; }; } /** * Creates a new function that passes `value` to the `wrapper` function as its * first argument. Additional arguments passed to the new function are appended * to those passed to the `wrapper` function. * * @static * @memberOf _ * @category Functions * @param {Mixed} value The value to wrap. * @param {Function} wrapper The wrapper function. * @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(value, wrapper) { return function() { var args = [value]; if (arguments.length) { push.apply(args, arguments); } return wrapper.apply(this, args); }; } /*--------------------------------------------------------------------------*/ /** * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their * corresponding HTML entities. * * @static * @memberOf _ * @category Utilities * @param {String} string The string to escape. * @returns {String} Returns the escaped string. * @example * * _.escape('Moe, Larry & Curly'); * // => "Moe, Larry & Curly" */ 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('larry'); * // => 'Larry' * * _('curly').capitalize(); * // => 'Curly' */ 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; } /** * Produces a random number between `min` and `max` (inclusive). If only one * argument is passed, a number between `0` and the given number will be returned. * If no arguments are passed `_.random` will act as `Math.random`. * * @static * @memberOf _ * @category Utilities * @param {Number} min The minimum possible value. * @param {Number} max The maximum possible value. * @returns {Number} Returns a random number. * @example * * _.random(0, 5); * // => a number between 1 and 5 * * _.random(5); * // => also a number between 1 and 5 * * _.random(); * // => an integer between 0 and less than 1 */ function random(min, max) { if (min == null && max == null) { return nativeRandom(); } min = +min || 0; if (max == null) { max = min; min = 0; } return min + nativeFloor(nativeRandom() * ((+max || 0) - min + 1)); } /** * 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 isFunction(value) ? object[property]() : value; } /** * A micro-templating method that handles arbitrary delimiters, preserves * whitespace, and correctly escapes quotes within interpolated code. * * Note: In the development build `_.template` utilizes sourceURLs for easier * debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl * * Note: Lo-Dash may be used in Chrome extensions by either creating a `lodash csp` * build and avoiding `_.template` use, or loading Lo-Dash in a sandboxed page. * See http://developer.chrome.com/trunk/extensions/sandboxingEval.html * * @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 a compiled template * var compiled = _.template('hello: <%= name %>'); * compiled({ 'name': 'moe' }); * // => 'hello: moe' * * var list = '<% _.forEach(people, function(name) { %>
  • <%= name %>
  • <% }); %>'; * _.template(list, { 'people': ['moe', 'larry', 'curly'] }); * // => '
  • moe
  • larry
  • curly
  • ' * * // using the "escape" delimiter to escape HTML in data property values * _.template('<%- value %>', { 'value': '