vendor/assets/javascripts/lodash.compat.js in lodash-rails-1.2.1 vs vendor/assets/javascripts/lodash.compat.js in lodash-rails-1.3.1

- old
+ new

@@ -1,8 +1,8 @@ /** * @license - * Lo-Dash 1.2.1 (Custom Build) <http://lodash.com/> + * Lo-Dash 1.3.1 (Custom Build) <http://lodash.com/> * Build: `lodash -o ./dist/lodash.compat.js` * Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/> * Based on Underscore.js 1.4.4 <http://underscorejs.org/> * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. * Available under MIT license <http://lodash.com/license> @@ -10,34 +10,29 @@ ;(function(window) { /** Used as a safe reference for `undefined` in pre ES5 environments */ var undefined; - /** Detect free variable `exports` */ - var freeExports = typeof exports == 'object' && exports; + /** Used to pool arrays and objects used internally */ + var arrayPool = [], + objectPool = []; - /** Detect free variable `module` */ - var freeModule = typeof module == 'object' && module && module.exports == freeExports && module; - - /** Detect free variable `global`, from Node.js or Browserified code, and use it as `window` */ - var freeGlobal = typeof global == 'object' && global; - if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { - window = freeGlobal; - } - /** Used to generate unique IDs */ var idCounter = 0; /** Used internally to indicate various things */ var indicatorObject = {}; /** Used to prefix keys to avoid issues with `__proto__` and properties on `Object.prototype` */ var keyPrefix = +new Date + ''; /** Used as the size when optimizations are enabled for large arrays */ - var largeArraySize = 200; + var largeArraySize = 75; + /** Used as the max size of the `arrayPool` and `objectPool` */ + var maxPoolSize = 40; + /** 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; @@ -54,10 +49,13 @@ var reFlags = /\w*$/; /** Used to match "interpolate" template delimiters */ var reInterpolate = /<%=([\s\S]+?)%>/g; + /** Used to detect functions containing a `this` reference */ + var reThis = (reThis = /\bthis\b/) && reThis.test(runInContext) && reThis; + /** Used to detect and test whitespace */ var whitespace = ( // whitespace ' \t\x0B\f\xA0\ufeff' + @@ -80,13 +78,13 @@ /** Used to match unescaped characters in compiled string literals */ var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; /** Used to assign default `context` object properties */ var contextProps = [ - 'Array', 'Boolean', 'Date', 'Function', 'Math', 'Number', 'Object', 'RegExp', - 'String', '_', 'attachEvent', 'clearTimeout', 'isFinite', 'isNaN', 'parseInt', - 'setImmediate', 'setTimeout' + 'Array', 'Boolean', 'Date', 'Error', 'Function', 'Math', 'Number', 'Object', + 'RegExp', 'String', '_', 'attachEvent', 'clearTimeout', 'isFinite', 'isNaN', + 'parseInt', 'setImmediate', 'setTimeout' ]; /** Used to fix the JScript [[DontEnum]] bug */ var shadowedProps = [ 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', @@ -99,10 +97,11 @@ /** `Object#toString` result shortcuts */ var argsClass = '[object Arguments]', arrayClass = '[object Array]', boolClass = '[object Boolean]', dateClass = '[object Date]', + errorClass = '[object Error]', funcClass = '[object Function]', numberClass = '[object Number]', objectClass = '[object Object]', regexpClass = '[object RegExp]', stringClass = '[object String]'; @@ -134,13 +133,311 @@ '\t': 't', '\u2028': 'u2028', '\u2029': 'u2029' }; + /** Detect free variable `exports` */ + var freeExports = objectTypes[typeof exports] && exports; + + /** Detect free variable `module` */ + var freeModule = objectTypes[typeof module] && module && module.exports == freeExports && module; + + /** Detect free variable `global`, from Node.js or Browserified code, and use it as `window` */ + var freeGlobal = objectTypes[typeof global] && global; + if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) { + window = freeGlobal; + } + /*--------------------------------------------------------------------------*/ /** + * A basic implementation of `_.indexOf` without support for binary searches + * or `fromIndex` constraints. + * + * @private + * @param {Array} array The array to search. + * @param {Mixed} value The value to search for. + * @param {Number} [fromIndex=0] The index to search from. + * @returns {Number} Returns the index of the matched value or `-1`. + */ + function basicIndexOf(array, value, fromIndex) { + var index = (fromIndex || 0) - 1, + length = array.length; + + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * An implementation of `_.contains` for cache objects that mimics the return + * signature of `_.indexOf` by returning `0` if the value is found, else `-1`. + * + * @private + * @param {Object} cache The cache object to inspect. + * @param {Mixed} value The value to search for. + * @returns {Number} Returns `0` if `value` is found, else `-1`. + */ + function cacheIndexOf(cache, value) { + var type = typeof value; + cache = cache.cache; + + if (type == 'boolean' || value == null) { + return cache[value]; + } + if (type != 'number' && type != 'string') { + type = 'object'; + } + var key = type == 'number' ? value : keyPrefix + value; + cache = cache[type] || (cache[type] = {}); + + return type == 'object' + ? (cache[key] && basicIndexOf(cache[key], value) > -1 ? 0 : -1) + : (cache[key] ? 0 : -1); + } + + /** + * Adds a given `value` to the corresponding cache object. + * + * @private + * @param {Mixed} value The value to add to the cache. + */ + function cachePush(value) { + var cache = this.cache, + type = typeof value; + + if (type == 'boolean' || value == null) { + cache[value] = true; + } else { + if (type != 'number' && type != 'string') { + type = 'object'; + } + var key = type == 'number' ? value : keyPrefix + value, + typeCache = cache[type] || (cache[type] = {}); + + if (type == 'object') { + if ((typeCache[key] || (typeCache[key] = [])).push(value) == this.array.length) { + cache[type] = false; + } + } else { + typeCache[key] = true; + } + } + } + + /** + * Used by `_.max` and `_.min` as the default `callback` when a given + * `collection` is a string value. + * + * @private + * @param {String} value The character to inspect. + * @returns {Number} Returns the code unit of given character. + */ + function charAtCallback(value) { + return value.charCodeAt(0); + } + + /** + * 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 || typeof a == 'undefined') { + return 1; + } + if (a < b || typeof b == 'undefined') { + return -1; + } + } + return ai < bi ? -1 : 1; + } + + /** + * Creates a cache object to optimize linear searches of large arrays. + * + * @private + * @param {Array} [array=[]] The array to search. + * @returns {Null|Object} Returns the cache object or `null` if caching should not be used. + */ + function createCache(array) { + var index = -1, + length = array.length; + + var cache = getObject(); + cache['false'] = cache['null'] = cache['true'] = cache['undefined'] = false; + + var result = getObject(); + result.array = array; + result.cache = cache; + result.push = cachePush; + + while (++index < length) { + result.push(array[index]); + } + return cache.object === false + ? (releaseObject(result), null) + : result; + } + + /** + * 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]; + } + + /** + * Gets an array from the array pool or creates a new one if the pool is empty. + * + * @private + * @returns {Array} The array from the pool. + */ + function getArray() { + return arrayPool.pop() || []; + } + + /** + * Gets an object from the object pool or creates a new one if the pool is empty. + * + * @private + * @returns {Object} The object from the pool. + */ + function getObject() { + return objectPool.pop() || { + 'args': '', + 'array': null, + 'bottom': '', + 'cache': null, + 'criteria': null, + 'false': false, + 'firstArg': '', + 'index': 0, + 'init': '', + 'leading': false, + 'loop': '', + 'maxWait': 0, + 'null': false, + 'number': null, + 'object': null, + 'push': null, + 'shadowedProps': null, + 'string': null, + 'top': '', + 'trailing': false, + 'true': false, + 'undefined': false, + 'useHas': false, + 'useKeys': false, + 'value': null + }; + } + + /** + * Checks if `value` is a DOM node in IE < 9. + * + * @private + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a DOM node, else `false`. + */ + function isNode(value) { + // IE < 9 presents DOM nodes as `Object` objects except they have `toString` + // methods that are `typeof` "string" and still can coerce nodes to strings + return typeof value.toString != 'function' && typeof (value + '') == 'string'; + } + + /** + * A no-operation function. + * + * @private + */ + function noop() { + // no operation performed + } + + /** + * Releases the given `array` back to the array pool. + * + * @private + * @param {Array} [array] The array to release. + */ + function releaseArray(array) { + array.length = 0; + if (arrayPool.length < maxPoolSize) { + arrayPool.push(array); + } + } + + /** + * Releases the given `object` back to the object pool. + * + * @private + * @param {Object} [object] The object to release. + */ + function releaseObject(object) { + var cache = object.cache; + if (cache) { + releaseObject(cache); + } + object.array = object.cache = object.criteria = object.object = object.number = object.string = object.value = null; + if (objectPool.length < maxPoolSize) { + objectPool.push(object); + } + } + + /** + * Slices the `collection` from the `start` index up to, but not including, + * the `end` index. + * + * Note: This function is used, instead of `Array#slice`, to support node lists + * in IE < 9 and to ensure dense arrays are returned. + * + * @private + * @param {Array|Object|String} collection The collection to slice. + * @param {Number} start The start index. + * @param {Number} end The end index. + * @returns {Array} Returns the new array. + */ + function slice(array, start, end) { + start || (start = 0); + if (typeof end == 'undefined') { + end = array ? array.length : 0; + } + var index = -1, + length = end - start || 0, + result = Array(length < 0 ? 0 : length); + + while (++index < length) { + result[index] = array[start + index]; + } + return result; + } + + /*--------------------------------------------------------------------------*/ + + /** * Create a new `lodash` function using the given `context` object. * * @static * @memberOf _ * @category Utilities @@ -156,46 +453,59 @@ /** Native constructor references */ var Array = context.Array, Boolean = context.Boolean, Date = context.Date, + Error = context.Error, Function = context.Function, Math = context.Math, Number = context.Number, Object = context.Object, RegExp = context.RegExp, String = context.String, TypeError = context.TypeError; - /** Used for `Array` and `Object` method references */ - var arrayRef = Array(), - objectRef = Object(); + /** + * Used for `Array` method references. + * + * Normally `Array.prototype` would suffice, however, using an array literal + * avoids issues in Narwhal. + */ + var arrayRef = []; + /** Used for native method references */ + var errorProto = Error.prototype, + objectProto = Object.prototype, + stringProto = String.prototype; + /** Used to restore the original `_` reference in `noConflict` */ var oldDash = context._; /** Used to detect if a method is native */ var reNative = RegExp('^' + - String(objectRef.valueOf) + String(objectProto.valueOf) .replace(/[.*+?^${}()|[\]\\]/g, '\\$&') .replace(/valueOf|for [^\]]+/g, '.+?') + '$' ); /** Native method shortcuts */ var ceil = Math.ceil, clearTimeout = context.clearTimeout, concat = arrayRef.concat, floor = Math.floor, + fnToString = Function.prototype.toString, getPrototypeOf = reNative.test(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf, - hasOwnProperty = objectRef.hasOwnProperty, + hasOwnProperty = objectProto.hasOwnProperty, push = arrayRef.push, + propertyIsEnumerable = objectProto.propertyIsEnumerable, setImmediate = context.setImmediate, setTimeout = context.setTimeout, - toString = objectRef.toString; + toString = objectProto.toString; /* Native method shortcuts for methods with the same name as other `lodash` methods */ var nativeBind = reNative.test(nativeBind = toString.bind) && nativeBind, + nativeCreate = reNative.test(nativeCreate = Object.create) && nativeCreate, nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray, nativeIsFinite = context.isFinite, nativeIsNaN = context.isNaN, nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys, nativeMax = Math.max, @@ -211,15 +521,35 @@ /** Used to lookup a built-in constructor by [[Class]] */ var ctorByClass = {}; ctorByClass[arrayClass] = Array; ctorByClass[boolClass] = Boolean; ctorByClass[dateClass] = Date; + ctorByClass[funcClass] = Function; ctorByClass[objectClass] = Object; ctorByClass[numberClass] = Number; ctorByClass[regexpClass] = RegExp; ctorByClass[stringClass] = String; + /** Used to avoid iterating non-enumerable properties in IE < 9 */ + var nonEnumProps = {}; + nonEnumProps[arrayClass] = nonEnumProps[dateClass] = nonEnumProps[numberClass] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true }; + nonEnumProps[boolClass] = nonEnumProps[stringClass] = { 'constructor': true, 'toString': true, 'valueOf': true }; + nonEnumProps[errorClass] = nonEnumProps[funcClass] = nonEnumProps[regexpClass] = { 'constructor': true, 'toString': true }; + nonEnumProps[objectClass] = { 'constructor': true }; + + (function() { + var length = shadowedProps.length; + while (length--) { + var prop = shadowedProps[length]; + for (var className in nonEnumProps) { + if (hasOwnProperty.call(nonEnumProps, className) && !hasOwnProperty.call(nonEnumProps[className], prop)) { + nonEnumProps[className][prop] = false; + } + } + } + }()); + /*--------------------------------------------------------------------------*/ /** * Creates a `lodash` object, which wraps the given `value`, to enable method * chaining. @@ -237,12 +567,12 @@ * `defer`, `delay`, `difference`, `filter`, `flatten`, `forEach`, `forIn`, * `forOwn`, `functions`, `groupBy`, `initial`, `intersection`, `invert`, * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`, * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `push`, `range`, * `reject`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, `sortBy`, `splice`, - * `tap`, `throttle`, `times`, `toArray`, `union`, `uniq`, `unshift`, `unzip`, - * `values`, `where`, `without`, `wrap`, and `zip` + * `tap`, `throttle`, `times`, `toArray`, `transform`, `union`, `uniq`, `unshift`, + * `unzip`, `values`, `where`, `without`, `wrap`, and `zip` * * The non-chainable wrapper functions are: * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `has`, * `identity`, `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, * `isElement`, `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, @@ -254,10 +584,11 @@ * The wrapper functions `first` and `last` return wrapped values when `n` is * passed, otherwise they return unwrapped values. * * @name _ * @constructor + * @alias chain * @category Chaining * @param {Mixed} value The value to wrap in a `lodash` instance. * @returns {Object} Returns a `lodash` instance. * @example * @@ -286,10 +617,23 @@ ? value : new lodashWrapper(value); } /** + * A fast path for creating `lodash` wrapper objects. + * + * @private + * @param {Mixed} value The value to wrap in a `lodash` instance. + * @returns {Object} Returns a `lodash` instance. + */ + function lodashWrapper(value) { + this.__wrapped__ = value; + } + // ensure `new lodashWrapper` is an instance of `lodash` + lodashWrapper.prototype = lodash.prototype; + + /** * An object used to flag environments features. * * @static * @memberOf _ * @type Object @@ -320,21 +664,30 @@ * @type Boolean */ support.argsClass = isArguments(arguments); /** + * Detect if `name` or `message` properties of `Error.prototype` are + * enumerable by default. (IE < 9, Safari < 5.1) + * + * @memberOf _.support + * @type Boolean + */ + support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') || propertyIsEnumerable.call(errorProto, 'name'); + + /** * Detect if `prototype` properties are enumerable by default. * * Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 * (if the prototype or a property on the prototype has been set) * incorrectly sets a function's `prototype` property [[Enumerable]] * value to `true`. * * @memberOf _.support * @type Boolean */ - support.enumPrototypes = ctor.propertyIsEnumerable('prototype'); + support.enumPrototypes = propertyIsEnumerable.call(ctor, 'prototype'); /** * Detect if `Function#bind` exists and is inferred to be fast (all but V8). * * @memberOf _.support @@ -486,81 +839,84 @@ (obj.firstArg) + ', result = ' + (obj.init) + ';\nif (!iterable) return result;\n' + (obj.top) + - ';\n'; - if (obj.arrays) { - __p += 'var length = iterable.length; index = -1;\nif (' + - (obj.arrays) + + ';'; + if (obj.array) { + __p += '\nvar length = iterable.length; index = -1;\nif (' + + (obj.array) + ') { '; if (support.unindexedChars) { __p += '\n if (isString(iterable)) {\n iterable = iterable.split(\'\')\n } '; } __p += '\n while (++index < length) {\n ' + (obj.loop) + - '\n }\n}\nelse { '; - } else if (support.nonEnumArgs) { + ';\n }\n}\nelse { '; + } else if (support.nonEnumArgs) { __p += '\n var length = iterable.length; index = -1;\n if (length && isArguments(iterable)) {\n while (++index < length) {\n index += \'\';\n ' + (obj.loop) + - '\n }\n } else { '; + ';\n }\n } else { '; } if (support.enumPrototypes) { __p += '\n var skipProto = typeof iterable == \'function\';\n '; } + if (support.enumErrorProps) { + __p += '\n var skipErrorProps = iterable === errorProto || iterable instanceof Error;\n '; + } + + var conditions = []; if (support.enumPrototypes) { conditions.push('!(skipProto && index == "prototype")'); } if (support.enumErrorProps) { conditions.push('!(skipErrorProps && (index == "message" || index == "name"))'); } + if (obj.useHas && obj.useKeys) { - __p += '\n var ownIndex = -1,\n ownProps = objectTypes[typeof iterable] ? keys(iterable) : [],\n length = ownProps.length;\n\n while (++ownIndex < length) {\n index = ownProps[ownIndex];\n '; - if (support.enumPrototypes) { - __p += 'if (!(skipProto && index == \'prototype\')) {\n '; + __p += '\n var ownIndex = -1,\n ownProps = objectTypes[typeof iterable] && keys(iterable),\n length = ownProps ? ownProps.length : 0;\n\n while (++ownIndex < length) {\n index = ownProps[ownIndex];\n'; + if (conditions.length) { + __p += ' if (' + + (conditions.join(' && ')) + + ') {\n '; } __p += - (obj.loop); - if (support.enumPrototypes) { - __p += '}\n'; + (obj.loop) + + '; '; + if (conditions.length) { + __p += '\n }'; } - __p += ' } '; + __p += '\n } '; } else { - __p += '\n for (index in iterable) {'; - if (support.enumPrototypes || obj.useHas) { - __p += '\n if ('; - if (support.enumPrototypes) { - __p += '!(skipProto && index == \'prototype\')'; - } if (support.enumPrototypes && obj.useHas) { - __p += ' && '; - } if (obj.useHas) { - __p += 'hasOwnProperty.call(iterable, index)'; + __p += '\n for (index in iterable) {\n'; + if (obj.useHas) { conditions.push("hasOwnProperty.call(iterable, index)"); } if (conditions.length) { + __p += ' if (' + + (conditions.join(' && ')) + + ') {\n '; } - __p += ') { '; - } __p += (obj.loop) + '; '; - if (support.enumPrototypes || obj.useHas) { + if (conditions.length) { __p += '\n }'; } __p += '\n } '; if (support.nonEnumShadows) { - __p += '\n\n var ctor = iterable.constructor;\n '; - for (var k = 0; k < 7; k++) { - __p += '\n index = \'' + + __p += '\n\n if (iterable !== objectProto) {\n var ctor = iterable.constructor,\n isProto = iterable === (ctor && ctor.prototype),\n className = iterable === stringProto ? stringClass : iterable === errorProto ? errorClass : toString.call(iterable),\n nonEnum = nonEnumProps[className];\n '; + for (k = 0; k < 7; k++) { + __p += '\n index = \'' + (obj.shadowedProps[k]) + - '\';\n if ('; - if (obj.shadowedProps[k] == 'constructor') { - __p += '!(ctor && ctor.prototype === iterable) && '; - } - __p += 'hasOwnProperty.call(iterable, index)) {\n ' + + '\';\n if ((!(isProto && nonEnum[index]) && hasOwnProperty.call(iterable, index))'; + if (!obj.useHas) { + __p += ' || (!nonEnum[index] && iterable[index] !== objectProto[index])'; + } + __p += ') {\n ' + (obj.loop) + - '\n } '; + ';\n } '; } - + __p += '\n } '; } } - if (obj.arrays || support.nonEnumArgs) { + if (obj.array || support.nonEnumArgs) { __p += '\n}'; } __p += (obj.bottom) + ';\nreturn result'; @@ -584,95 +940,23 @@ /** Reusable iterator options shared by `each`, `forIn`, and `forOwn` */ var eachIteratorOptions = { 'args': 'collection, callback, thisArg', 'top': "callback = callback && typeof thisArg == 'undefined' ? callback : lodash.createCallback(callback, thisArg)", - 'arrays': "typeof length == 'number'", + 'array': "typeof length == 'number'", 'loop': 'if (callback(iterable[index], index, collection) === false) return result' }; /** Reusable iterator options for `forIn` and `forOwn` */ var forOwnIteratorOptions = { 'top': 'if (!objectTypes[typeof iterable]) return result;\n' + eachIteratorOptions.top, - 'arrays': false + 'array': false }; /*--------------------------------------------------------------------------*/ /** - * Creates a function optimized to search 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. - * @returns {Boolean} Returns `true`, if `value` is found, else `false`. - */ - function cachedContains(array) { - var length = array.length, - isLarge = length >= largeArraySize; - - if (isLarge) { - var cache = {}, - index = -1; - - while (++index < length) { - var key = keyPrefix + array[index]; - (cache[key] || (cache[key] = [])).push(array[index]); - } - } - return function(value) { - if (isLarge) { - var key = keyPrefix + value; - return cache[key] && indexOf(cache[key], value) > -1; - } - return indexOf(array, value) > -1; - } - } - - /** - * Used by `_.max` and `_.min` as the default `callback` when a given - * `collection` is a string value. - * - * @private - * @param {String} value The character to inspect. - * @returns {Number} Returns the code unit of given character. - */ - function charAtCallback(value) { - return value.charCodeAt(0); - } - - /** - * 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 || typeof a == 'undefined') { - return 1; - } - if (a < b || typeof b == 'undefined') { - return -1; - } - } - return ai < bi ? -1 : 1; - } - - /** * Creates a function that, when called, invokes `func` with the `this` binding * of `thisArg` and prepends any `partialArgs` to the arguments passed to the * bound function. * * @private @@ -714,13 +998,11 @@ ? (args = nativeSlice.call(args), rightIndicator ? args.concat(partialArgs) : partialArgs.concat(args)) : partialArgs; } if (this instanceof bound) { // ensure `new bound` is an instance of `func` - noop.prototype = func.prototype; - thisBinding = new noop; - noop.prototype = null; + thisBinding = createObject(func.prototype); // mimic the constructor's `return` behavior // http://es5.github.com/#x13.2.2 var result = func.apply(thisBinding, args); return isObject(result) ? result : thisBinding; @@ -733,33 +1015,30 @@ /** * Creates compiled iteration functions. * * @private * @param {Object} [options1, options2, ...] The compile options object(s). - * arrays - A string of code to determine if the iterable is an array or array-like. + * array - A string of code to determine if the iterable is an array or array-like. * useHas - A boolean to specify using `hasOwnProperty` checks in the object loop. * useKeys - A boolean to specify using `_.keys` for own property iteration. * args - A string of comma separated arguments the iteration function will accept. * top - A string of code to execute before the iteration branches. * loop - A string of code to execute in the object loop. * bottom - A string of code to execute after the iteration branches. * @returns {Function} Returns the compiled function. */ function createIterator() { - var data = { - // data properties - 'shadowedProps': shadowedProps, - // iterator options - 'arrays': 'isArray(iterable)', - 'bottom': '', - 'init': 'iterable', - 'loop': '', - 'top': '', - 'useHas': true, - 'useKeys': !!keys - }; + var data = getObject(); + // data properties + data.shadowedProps = shadowedProps; + // iterator options + data.array = data.bottom = data.loop = data.top = ''; + data.init = 'iterable'; + data.useHas = true; + data.useKeys = !!keys; + // merge options into a template data object for (var object, index = 0; object = arguments[index]; index++) { for (var key in object) { data[key] = object[key]; } @@ -767,32 +1046,47 @@ var args = data.args; data.firstArg = /^[^,]+/.exec(args)[0]; // create the function factory var factory = Function( - 'hasOwnProperty, isArguments, isArray, isString, keys, ' + - 'lodash, objectTypes', + 'errorClass, errorProto, hasOwnProperty, isArguments, isArray, ' + + 'isString, keys, lodash, objectProto, objectTypes, nonEnumProps, ' + + 'stringClass, stringProto, toString', 'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}' ); + + releaseObject(data); + // return the compiled function return factory( - hasOwnProperty, isArguments, isArray, isString, keys, - lodash, objectTypes + errorClass, errorProto, hasOwnProperty, isArguments, isArray, + isString, keys, lodash, objectProto, objectTypes, nonEnumProps, + stringClass, stringProto, toString ); } /** - * Used by `template` to escape characters for inclusion in compiled - * string literals. + * Creates a new object with the specified `prototype`. * * @private - * @param {String} match The matched character to escape. - * @returns {String} Returns the escaped character. + * @param {Object} prototype The prototype object. + * @returns {Object} Returns the new object. */ - function escapeStringChar(match) { - return '\\' + stringEscapes[match]; + function createObject(prototype) { + return isObject(prototype) ? nativeCreate(prototype) : {}; } + // fallback for browsers without `Object.create` + if (!nativeCreate) { + var createObject = function(prototype) { + if (isObject(prototype)) { + noop.prototype = prototype; + var result = new noop; + noop.prototype = null; + } + return result || {}; + }; + } /** * Used by `escape` to convert characters to HTML entities. * * @private @@ -802,114 +1096,86 @@ function escapeHtmlChar(match) { return htmlEscapes[match]; } /** - * Checks if `value` is a DOM node in IE < 9. + * Gets the appropriate "indexOf" function. If the `_.indexOf` method is + * customized, this method returns the custom method, otherwise it returns + * the `basicIndexOf` function. * * @private - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a DOM node, else `false`. + * @returns {Function} Returns the "indexOf" function. */ - function isNode(value) { - // IE < 9 presents DOM nodes as `Object` objects except they have `toString` - // methods that are `typeof` "string" and still can coerce nodes to strings - return typeof value.toString != 'function' && typeof (value + '') == 'string'; + function getIndexOf(array, value, fromIndex) { + var result = (result = lodash.indexOf) === indexOf ? basicIndexOf : result; + return result; } /** - * A fast path for creating `lodash` wrapper objects. + * Creates a function that juggles arguments, allowing argument overloading + * for `_.flatten` and `_.uniq`, before passing them to the given `func`. * * @private - * @param {Mixed} value The value to wrap in a `lodash` instance. - * @returns {Object} Returns a `lodash` instance. + * @param {Function} func The function to wrap. + * @returns {Function} Returns the new function. */ - function lodashWrapper(value) { - this.__wrapped__ = value; + function overloadWrapper(func) { + return function(array, flag, callback, thisArg) { + // juggle arguments + if (typeof flag != 'boolean' && flag != null) { + thisArg = callback; + callback = !(thisArg && thisArg[flag] === array) ? flag : undefined; + flag = false; + } + if (callback != null) { + callback = lodash.createCallback(callback, thisArg); + } + return func(array, flag, callback, thisArg); + }; } - // ensure `new lodashWrapper` is an instance of `lodash` - lodashWrapper.prototype = lodash.prototype; /** - * A no-operation function. - * - * @private - */ - function noop() { - // no operation performed - } - - /** * A fallback implementation of `isPlainObject` which checks if a given `value` * is an object created by the `Object` constructor, assuming objects created * by the `Object` constructor have no inherited enumerable properties and that * there are no `Object.prototype` extensions. * * @private * @param {Mixed} value The value to check. * @returns {Boolean} Returns `true`, if `value` is a plain object, else `false`. */ function shimIsPlainObject(value) { - // avoid non-objects and false positives for `arguments` objects - var result = false; - if (!(value && toString.call(value) == objectClass) || (!support.argsClass && isArguments(value))) { - return result; - } - // check that the constructor is `Object` (i.e. `Object instanceof Object`) - var ctor = value.constructor; + var ctor, + result; - if (isFunction(ctor) ? ctor instanceof ctor : (support.nodeClass || !isNode(value))) { - // 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 (support.ownLast) { - forIn(value, function(value, key, object) { - result = hasOwnProperty.call(object, key); - return false; - }); - return result === true; - } - // 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; + // avoid non Object objects, `arguments` objects, and DOM elements + if (!(value && toString.call(value) == objectClass) || + (ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor)) || + (!support.argsClass && isArguments(value)) || + (!support.nodeClass && isNode(value))) { + return false; + } + // 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 (support.ownLast) { + forIn(value, function(value, key, object) { + result = hasOwnProperty.call(object, key); + return false; }); - return result === false || hasOwnProperty.call(value, result); + return result !== false; } - return result; + // 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 === undefined || hasOwnProperty.call(value, result); } /** - * Slices the `collection` from the `start` index up to, but not including, - * the `end` index. - * - * Note: This function is used, instead of `Array#slice`, to support node lists - * in IE < 9 and to ensure dense arrays are returned. - * - * @private - * @param {Array|Object|String} collection The collection to slice. - * @param {Number} start The start index. - * @param {Number} end The end index. - * @returns {Array} Returns the new array. - */ - function slice(array, start, end) { - start || (start = 0); - if (typeof end == 'undefined') { - end = array ? array.length : 0; - } - var index = -1, - length = end - start || 0, - result = Array(length < 0 ? 0 : length); - - while (++index < length) { - result[index] = array[start + index]; - } - return result; - } - - /** * Used by `unescape` to convert HTML entities to characters. * * @private * @param {String} match The matched character to unescape. * @returns {String} Returns the unescaped character. @@ -977,12 +1243,11 @@ */ var shimKeys = createIterator({ 'args': 'object', 'init': '[]', 'top': 'if (!(objectTypes[typeof object])) return result', - 'loop': 'result.push(index)', - 'arrays': false + 'loop': 'result.push(index)' }); /** * Creates an array composed of the own enumerable property names of `object`. * @@ -1019,11 +1284,11 @@ * @param {Array|Object|String} collection The collection to iterate over. * @param {Function} [callback=identity] The function called per iteration. * @param {Mixed} [thisArg] The `this` binding of `callback`. * @returns {Array|Object|String} Returns `collection`. */ - var each = createIterator(eachIteratorOptions); + var basicEach = createIterator(eachIteratorOptions); /** * Used to convert characters to HTML entities: * * Though the `>` character is escaped for symmetry, characters like `>` and `/` @@ -1132,11 +1397,11 @@ function clone(value, deep, callback, thisArg, stackA, stackB) { var result = value; // allows working with "Collections" methods without using their `callback` // argument, `index|key`, for this method's `callback` - if (typeof deep == 'function') { + if (typeof deep != 'boolean' && deep != null) { thisArg = callback; callback = deep; deep = false; } if (typeof callback == 'function') { @@ -1177,12 +1442,13 @@ case regexpClass: return ctor(result.source, reFlags.exec(result)); } // check for circular references and return corresponding clone - stackA || (stackA = []); - stackB || (stackB = []); + var initedStack = !stackA; + stackA || (stackA = getArray()); + stackB || (stackB = getArray()); var length = stackA.length; while (length--) { if (stackA[length] == value) { return stackB[length]; @@ -1204,24 +1470,28 @@ // and associate it with its clone stackA.push(value); stackB.push(result); // recursively populate clone (susceptible to call stack limits) - (isArr ? forEach : forOwn)(value, function(objValue, key) { + (isArr ? basicEach : forOwn)(value, function(objValue, key) { result[key] = clone(objValue, deep, callback, undefined, stackA, stackB); }); + if (initedStack) { + releaseArray(stackA); + releaseArray(stackB); + } return result; } /** * Creates a deep clone of `value`. If a `callback` function is passed, * it will be executed to produce the cloned values. If `callback` returns * `undefined`, cloning will be handled by the method instead. The `callback` * is bound to `thisArg` and invoked with one argument; (value). * - * Note: This function is loosely based on the structured clone algorithm. Functions + * Note: This method is loosely based on the structured clone algorithm. Functions * and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and * objects created by constructors other than `Object` are cloned to plain `Object` objects. * See http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm. * * @static @@ -1654,12 +1924,13 @@ } } // 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) - stackA || (stackA = []); - stackB || (stackB = []); + var initedStack = !stackA; + stackA || (stackA = getArray()); + stackB || (stackB = getArray()); var length = stackA.length; while (length--) { if (stackA[length] == a) { return stackB[length] == b; @@ -1717,10 +1988,14 @@ // `size` will be `-1` if `a` has more properties than `b` return (result = --size > -1); } }); } + if (initedStack) { + releaseArray(stackA); + releaseArray(stackB); + } return result; } /** * Checks if `value` is, or can be coerced to, a finite number. @@ -1800,11 +2075,11 @@ 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; + return !!(value && objectTypes[typeof value]); } /** * Checks if `value` is `NaN`. * @@ -1921,11 +2196,11 @@ * * _.isRegExp(/moe/); * // => true */ function isRegExp(value) { - return value ? (objectTypes[typeof value] && toString.call(value) == regexpClass) : false; + return !!(value && objectTypes[typeof value]) && toString.call(value) == regexpClass; } /** * Checks if `value` is a string. * @@ -2026,12 +2301,13 @@ if (deepIndicator === indicatorObject) { var callback = args[3], stackA = args[4], stackB = args[5]; } else { - stackA = []; - stackB = []; + var initedStack = true; + stackA = getArray(); + stackB = getArray(); // allows working with `_.reduce` and `_.reduceRight` without // using their `callback` arguments, `index|key` and `collection` if (typeof deepIndicator != 'number') { length = args.length; @@ -2093,10 +2369,15 @@ } } object[key] = value; }); } + + if (initedStack) { + releaseArray(stackA); + releaseArray(stackB); + } return object; } /** * Creates a shallow clone of `object` excluding the specified properties. @@ -2123,11 +2404,12 @@ * return typeof value == 'number'; * }); * // => { 'name': 'moe' } */ function omit(object, callback, thisArg) { - var isFunc = typeof callback == 'function', + var indexOf = getIndexOf(), + isFunc = typeof callback == 'function', result = {}; if (isFunc) { callback = lodash.createCallback(callback, thisArg); } else { @@ -2219,10 +2501,61 @@ } return result; } /** + * An alternative to `_.reduce`, this method transforms an `object` to a new + * `accumulator` object which is the result of running each of its elements + * through the `callback`, with each `callback` execution potentially mutating + * the `accumulator` object. The `callback` is bound to `thisArg` and invoked + * with four arguments; (accumulator, value, key, object). Callbacks may exit + * iteration early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @category Objects + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [accumulator] The custom accumulator value. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Mixed} Returns the accumulated value. + * @example + * + * var squares = _.transform([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(result, num) { + * num *= num; + * if (num % 2) { + * return result.push(num) < 3; + * } + * }); + * // => [1, 9, 25] + * + * var mapped = _.transform({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) { + * result[key] = num * 3; + * }); + * // => { 'a': 3, 'b': 6, 'c': 9 } + */ + function transform(object, callback, accumulator, thisArg) { + var isArr = isArray(object); + callback = lodash.createCallback(callback, thisArg, 4); + + if (accumulator == null) { + if (isArr) { + accumulator = []; + } else { + var ctor = object && object.constructor, + proto = ctor && ctor.prototype; + + accumulator = createObject(proto); + } + } + (isArr ? basicEach : forOwn)(object, function(value, index, object) { + return callback(accumulator, value, index, object); + }); + return accumulator; + } + + /** * Creates an array composed of the own enumerable property values of `object`. * * @static * @memberOf _ * @category Objects @@ -2310,21 +2643,22 @@ * _.contains('curly', 'ur'); * // => true */ function contains(collection, target, fromIndex) { var index = -1, + indexOf = getIndexOf(), length = collection ? collection.length : 0, result = false; fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0; - if (typeof length == 'number') { + if (length && typeof length == 'number') { result = (isString(collection) ? collection.indexOf(target, fromIndex) : indexOf(collection, target, fromIndex) ) > -1; } else { - each(collection, function(value) { + basicEach(collection, function(value) { if (++index >= fromIndex) { return !(result = value === target); } }); } @@ -2428,11 +2762,11 @@ if (!(result = !!callback(collection[index], index, collection))) { break; } } } else { - each(collection, function(value, index, collection) { + basicEach(collection, function(value, index, collection) { return (result = !!callback(value, index, collection)); }); } return result; } @@ -2490,11 +2824,11 @@ if (callback(value, index, collection)) { result.push(value); } } } else { - each(collection, function(value, index, collection) { + basicEach(collection, function(value, index, collection) { if (callback(value, index, collection)) { result.push(value); } }); } @@ -2513,11 +2847,11 @@ * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ - * @alias detect + * @alias detect, findWhere * @category Collections * @param {Array|Object|String} collection The collection to iterate over. * @param {Function|Object|String} [callback=identity] The function called per * iteration. If a property name or object is passed, it will be used to create * a "_.pluck" or "_.where" style callback, respectively. @@ -2557,11 +2891,11 @@ return value; } } } else { var result; - each(collection, function(value, index, collection) { + basicEach(collection, function(value, index, collection) { if (callback(value, index, collection)) { result = value; return false; } }); @@ -2600,11 +2934,11 @@ if (callback(collection[index], index, collection) === false) { break; } } } else { - each(collection, callback, thisArg); + basicEach(collection, callback, thisArg); } return collection; } /** @@ -2735,11 +3069,11 @@ if (isArray(collection)) { while (++index < length) { result[index] = callback(collection[index], index, collection); } } else { - each(collection, function(value, key, collection) { + basicEach(collection, function(value, key, collection) { result[++index] = callback(value, key, collection); }); } return result; } @@ -2800,11 +3134,11 @@ } else { callback = (!callback && isString(collection)) ? charAtCallback : lodash.createCallback(callback, thisArg); - each(collection, function(value, index, collection) { + basicEach(collection, function(value, index, collection) { var current = callback(value, index, collection); if (current > computed) { computed = current; result = value; } @@ -2869,11 +3203,11 @@ } else { callback = (!callback && isString(collection)) ? charAtCallback : lodash.createCallback(callback, thisArg); - each(collection, function(value, index, collection) { + basicEach(collection, function(value, index, collection) { var current = callback(value, index, collection); if (current < computed) { computed = current; result = value; } @@ -2947,11 +3281,11 @@ } while (++index < length) { accumulator = callback(accumulator, collection[index], index, collection); } } else { - each(collection, function(value, index, collection) { + basicEach(collection, function(value, index, collection) { accumulator = noaccum ? (noaccum = false, value) : callback(accumulator, value, index, collection) }); } @@ -3150,11 +3484,11 @@ if ((result = callback(collection[index], index, collection))) { break; } } } else { - each(collection, function(value, index, collection) { + basicEach(collection, function(value, index, collection) { return !(result = callback(value, index, collection)); }); } return !!result; } @@ -3199,21 +3533,22 @@ length = collection ? collection.length : 0, result = Array(typeof length == 'number' ? length : 0); callback = lodash.createCallback(callback, thisArg); forEach(collection, function(value, key, collection) { - result[++index] = { - 'criteria': callback(value, key, collection), - 'index': index, - 'value': value - }; + var object = result[++index] = getObject(); + object.criteria = callback(value, key, collection); + object.index = index; + object.value = value; }); length = result.length; result.sort(compareAscending); while (length--) { - result[length] = result[length].value; + var object = result[length]; + result[length] = object.value; + releaseObject(object); } return result; } /** @@ -3309,21 +3644,35 @@ * _.difference([1, 2, 3, 4, 5], [5, 2, 10]); * // => [1, 3, 4] */ function difference(array) { var index = -1, + indexOf = getIndexOf(), length = array ? array.length : 0, - flattened = concat.apply(arrayRef, nativeSlice.call(arguments, 1)), - contains = cachedContains(flattened), + seen = concat.apply(arrayRef, nativeSlice.call(arguments, 1)), result = []; + var isLarge = length >= largeArraySize && indexOf === basicIndexOf; + + if (isLarge) { + var cache = createCache(seen); + if (cache) { + indexOf = cacheIndexOf; + seen = cache; + } else { + isLarge = false; + } + } while (++index < length) { var value = array[index]; - if (!contains(value)) { + if (indexOf(seen, value) < 0) { result.push(value); } } + if (isLarge) { + releaseObject(seen); + } return result; } /** * This method is similar to `_.find`, except that it returns the index of @@ -3475,24 +3824,15 @@ * * // using "_.pluck" callback shorthand * _.flatten(stooges, 'quotes'); * // => ['Oh, a wise guy, eh?', 'Poifect!', 'Spread out!', 'You knucklehead!'] */ - function flatten(array, isShallow, callback, thisArg) { + var flatten = overloadWrapper(function flatten(array, isShallow, callback) { var index = -1, length = array ? array.length : 0, result = []; - // juggle arguments - if (typeof isShallow != 'boolean' && isShallow != null) { - thisArg = callback; - callback = isShallow; - isShallow = false; - } - if (callback != null) { - callback = lodash.createCallback(callback, thisArg); - } while (++index < length) { var value = array[index]; if (callback) { value = callback(value, index, array); } @@ -3502,11 +3842,11 @@ } 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 `fromIndex` will run a faster binary search. @@ -3529,25 +3869,18 @@ * * _.indexOf([1, 1, 2, 2, 3, 3], 2, true); * // => 2 */ function indexOf(array, value, fromIndex) { - var index = -1, - length = array ? array.length : 0; - if (typeof fromIndex == 'number') { - index = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0) - 1; + var length = array ? array.length : 0; + fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0); } else if (fromIndex) { - index = sortedIndex(array, value); + var index = sortedIndex(array, value); return array[index] === value ? index : -1; } - while (++index < length) { - if (array[index] === value) { - return index; - } - } - return -1; + return array ? basicIndexOf(array, value, fromIndex) : -1; } /** * Gets all but the last element of `array`. If a number `n` is passed, the * last `n` elements are excluded from the result. If a `callback` function @@ -3639,39 +3972,49 @@ * // => [1, 2] */ function intersection(array) { var args = arguments, argsLength = args.length, - cache = { '0': {} }, + argsIndex = -1, + caches = getArray(), index = -1, + indexOf = getIndexOf(), length = array ? array.length : 0, - isLarge = length >= largeArraySize, result = [], - seen = result; + seen = getArray(); + while (++argsIndex < argsLength) { + var value = args[argsIndex]; + caches[argsIndex] = indexOf === basicIndexOf && + (value ? value.length : 0) >= largeArraySize && + createCache(argsIndex ? args[argsIndex] : seen); + } outer: while (++index < length) { - var value = array[index]; - if (isLarge) { - var key = keyPrefix + value; - var inited = cache[0][key] - ? !(seen = cache[0][key]) - : (seen = cache[0][key] = []); - } - if (inited || indexOf(seen, value) < 0) { - if (isLarge) { - seen.push(value); - } - var argsIndex = argsLength; + var cache = caches[0]; + value = array[index]; + + if ((cache ? cacheIndexOf(cache, value) : indexOf(seen, value)) < 0) { + argsIndex = argsLength; + (cache || seen).push(value); while (--argsIndex) { - if (!(cache[argsIndex] || (cache[argsIndex] = cachedContains(args[argsIndex])))(value)) { + cache = caches[argsIndex]; + if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) { continue outer; } } result.push(value); } } + while (argsLength--) { + cache = caches[argsLength]; + if (cache) { + releaseObject(cache); + } + } + releaseArray(caches); + releaseArray(seen); return result; } /** * Gets the last element of the `array`. If a number `n` is passed, the @@ -3996,11 +4339,11 @@ /** * 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. + * element of `array` is passed through the `callback` before uniqueness is computed. * The `callback` is bound to `thisArg` and invoked with three arguments; (value, index, array). * * If a property name is passed for `callback`, the created "_.pluck" style * callback will return the property value of the given element. * @@ -4025,63 +4368,61 @@ * // => [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(['A', 'b', 'C', 'a', 'B', 'c'], function(letter) { return letter.toLowerCase(); }); + * // => ['A', 'b', 'C'] * - * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math); - * // => [1, 2, 3] + * _.uniq([1, 2.5, 3, 1.5, 2, 3.5], function(num) { return this.floor(num); }, Math); + * // => [1, 2.5, 3] * * // using "_.pluck" callback shorthand * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); * // => [{ 'x': 1 }, { 'x': 2 }] */ - function uniq(array, isSorted, callback, thisArg) { + var uniq = overloadWrapper(function(array, isSorted, callback) { var index = -1, + indexOf = getIndexOf(), length = array ? array.length : 0, - result = [], - seen = result; + result = []; - // juggle arguments - if (typeof isSorted != 'boolean' && isSorted != null) { - thisArg = callback; - callback = isSorted; - isSorted = false; - } - // init value cache for large arrays - var isLarge = !isSorted && length >= largeArraySize; + var isLarge = !isSorted && length >= largeArraySize && indexOf === basicIndexOf, + seen = (callback || isLarge) ? getArray() : result; + if (isLarge) { - var cache = {}; + var cache = createCache(seen); + if (cache) { + indexOf = cacheIndexOf; + seen = cache; + } else { + isLarge = false; + seen = callback ? seen : (releaseArray(seen), result); + } } - if (callback != null) { - seen = []; - callback = lodash.createCallback(callback, thisArg); - } while (++index < length) { var value = array[index], computed = callback ? callback(value, index, array) : value; - if (isLarge) { - var key = keyPrefix + computed; - var inited = cache[key] - ? !(seen = cache[key]) - : (seen = cache[key] = []); - } if (isSorted ? !index || seen[seen.length - 1] !== computed - : inited || indexOf(seen, computed) < 0 + : indexOf(seen, computed) < 0 ) { if (callback || isLarge) { seen.push(computed); } result.push(value); } } + if (isLarge) { + releaseArray(seen.array); + releaseObject(seen); + } else if (callback) { + releaseArray(seen); + } return result; - } + }); /** * The inverse of `_.zip`, this method splits groups of elements into arrays * composed of elements from each group at their corresponding indexes. * @@ -4095,21 +4436,15 @@ * _.unzip([['moe', 30, true], ['larry', 40, false]]); * // => [['moe', 'larry'], [30, 40], [true, false]]; */ function unzip(array) { var index = -1, - length = array ? array.length : 0, - tupleLength = length ? max(pluck(array, 'length')) : 0, - result = Array(tupleLength); + length = array ? max(pluck(array, 'length')) : 0, + result = Array(length < 0 ? 0 : length); while (++index < length) { - var tupleIndex = -1, - tuple = array[index]; - - while (++tupleIndex < tupleLength) { - (result[tupleIndex] || (result[tupleIndex] = Array(length)))[index] = tuple[tupleIndex]; - } + result[index] = pluck(array, index); } return result; } /** @@ -4146,18 +4481,11 @@ * * _.zip(['moe', 'larry'], [30, 40], [true, false]); * // => [['moe', 30, true], ['larry', 40, false]] */ function zip(array) { - var index = -1, - length = array ? max(pluck(arguments, 'length')) : 0, - result = Array(length); - - while (++index < length) { - result[index] = pluck(arguments, index); - } - return result; + return array ? unzip(arguments) : []; } /** * Creates an object composed from arrays of `keys` and `values`. Pass either * a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`, or @@ -4429,31 +4757,31 @@ } } return result; }; } - if (typeof thisArg != 'undefined') { - if (argCount === 1) { - return function(value) { - return func.call(thisArg, value); - }; - } - if (argCount === 2) { - return function(a, b) { - return func.call(thisArg, a, b); - }; - } - if (argCount === 4) { - return function(accumulator, value, index, collection) { - return func.call(thisArg, accumulator, value, index, collection); - }; - } - return function(value, index, collection) { - return func.call(thisArg, value, index, collection); + if (typeof thisArg == 'undefined' || (reThis && !reThis.test(fnToString.call(func)))) { + return func; + } + if (argCount === 1) { + return function(value) { + return func.call(thisArg, value); }; } - return func; + if (argCount === 2) { + return function(a, b) { + return func.call(thisArg, a, b); + }; + } + if (argCount === 4) { + return function(accumulator, value, index, collection) { + return func.call(thisArg, accumulator, value, index, collection); + }; + } + return function(value, index, collection) { + return func.call(thisArg, value, index, collection); + }; } /** * Creates a function that will delay the execution of `func` until after * `wait` milliseconds have elapsed since the last time it was invoked. Pass @@ -4470,10 +4798,11 @@ * @category Functions * @param {Function} func The function to debounce. * @param {Number} wait The number of milliseconds to delay. * @param {Object} options The options object. * [leading=false] A boolean to specify execution on the leading edge of the timeout. + * [maxWait] The maximum time `func` is allowed to be delayed before it's called. * [trailing=true] A boolean to specify execution on the trailing edge of the timeout. * @returns {Function} Returns the new debounced function. * @example * * var lazyLayout = _.debounce(calculateLayout, 300); @@ -4484,38 +4813,84 @@ * 'trailing': false * }); */ function debounce(func, wait, options) { var args, - inited, result, thisArg, - timeoutId, + callCount = 0, + lastCalled = 0, + maxWait = false, + maxTimeoutId = null, + timeoutId = null, trailing = true; + function clear() { + clearTimeout(maxTimeoutId); + clearTimeout(timeoutId); + callCount = 0; + maxTimeoutId = timeoutId = null; + } + function delayed() { - inited = timeoutId = null; - if (trailing) { + var isCalled = trailing && (!leading || callCount > 1); + clear(); + if (isCalled) { + if (maxWait !== false) { + lastCalled = new Date; + } result = func.apply(thisArg, args); } } + + function maxDelayed() { + clear(); + if (trailing || (maxWait !== wait)) { + lastCalled = new Date; + result = func.apply(thisArg, args); + } + } + + wait = nativeMax(0, wait || 0); if (options === true) { var leading = true; trailing = false; - } else if (options && objectTypes[typeof options]) { + } else if (isObject(options)) { leading = options.leading; + maxWait = 'maxWait' in options && nativeMax(wait, options.maxWait || 0); trailing = 'trailing' in options ? options.trailing : trailing; } return function() { args = arguments; thisArg = this; + callCount++; + + // avoid issues with Titanium and `undefined` timeout ids + // https://github.com/appcelerator/titanium_mobile/blob/3_1_0_GA/android/titanium/src/java/ti/modules/titanium/TitaniumModule.java#L185-L192 clearTimeout(timeoutId); - if (!inited && leading) { - inited = true; - result = func.apply(thisArg, args); + if (maxWait === false) { + if (leading && callCount < 2) { + result = func.apply(thisArg, args); + } } else { + var now = new Date; + if (!maxTimeoutId && !leading) { + lastCalled = now; + } + var remaining = maxWait - (now - lastCalled); + if (remaining <= 0) { + clearTimeout(maxTimeoutId); + maxTimeoutId = null; + lastCalled = now; + result = func.apply(thisArg, args); + } + else if (!maxTimeoutId) { + maxTimeoutId = setTimeout(maxDelayed, remaining); + } + } + if (wait !== maxWait) { timeoutId = setTimeout(delayed, wait); } return result; }; } @@ -4569,11 +4944,12 @@ /** * Creates a function that memoizes the result of `func`. If `resolver` is * passed, it will be used to determine the cache key for storing the result * based on the arguments passed to the memoized function. By default, the first * argument passed to the memoized function is used as the cache key. The `func` - * is executed with the `this` binding of the memoized function. + * is executed with the `this` binding of the memoized function. The result + * cache is exposed as the `cache` property on the memoized function. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to have its output memoized. @@ -4584,17 +4960,20 @@ * var fibonacci = _.memoize(function(n) { * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); * }); */ function memoize(func, resolver) { - var cache = {}; - return function() { - var key = keyPrefix + (resolver ? resolver.apply(this, arguments) : arguments[0]); + function memoized() { + var cache = memoized.cache, + key = keyPrefix + (resolver ? resolver.apply(this, arguments) : arguments[0]); + return hasOwnProperty.call(cache, key) ? cache[key] : (cache[key] = func.apply(this, arguments)); - }; + } + memoized.cache = {}; + return memoized; } /** * Creates a function that is restricted to execute `func` once. Repeat calls to * the function will return the value of the first call. The `func` is executed @@ -4710,51 +5089,27 @@ * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, { * 'trailing': false * })); */ function throttle(func, wait, options) { - var args, - result, - thisArg, - timeoutId, - lastCalled = 0, - leading = true, + var leading = true, trailing = true; - function trailingCall() { - timeoutId = null; - if (trailing) { - lastCalled = new Date; - result = func.apply(thisArg, args); - } - } if (options === false) { leading = false; - } else if (options && objectTypes[typeof options]) { + } else if (isObject(options)) { leading = 'leading' in options ? options.leading : leading; trailing = 'trailing' in options ? options.trailing : trailing; } - return function() { - var now = new Date; - if (!timeoutId && !leading) { - lastCalled = now; - } - var remaining = wait - (now - lastCalled); - args = arguments; - thisArg = this; + options = getObject(); + options.leading = leading; + options.maxWait = wait; + options.trailing = trailing; - if (remaining <= 0) { - clearTimeout(timeoutId); - timeoutId = null; - lastCalled = now; - result = func.apply(thisArg, args); - } - else if (!timeoutId) { - timeoutId = setTimeout(trailingCall, remaining); - } - return result; - }; + var result = debounce(func, wait, options); + releaseObject(options); + return result; } /** * Creates a function that passes `value` to the `wrapper` function as its * first argument. Additional arguments passed to the function are appended @@ -4803,11 +5158,11 @@ function escape(string) { return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar); } /** - * This function returns the first argument passed to it. + * This method returns the first argument passed to it. * * @static * @memberOf _ * @category Utilities * @param {Mixed} value Any value. @@ -4852,11 +5207,11 @@ var value = this.__wrapped__, args = [value]; push.apply(args, arguments); var result = func.apply(lodash, args); - return (value && typeof value == 'object' && value == result) + return (value && typeof value == 'object' && value === result) ? this : new lodashWrapper(result); }; }); } @@ -4926,12 +5281,17 @@ } min = +min || 0; if (max == null) { max = min; min = 0; + } else { + max = +max || 0; } - return min + floor(nativeRandom() * ((+max || 0) - min + 1)); + var rand = nativeRandom(); + return (min % 1 || max % 1) + ? min + nativeMin(rand * (max - min + parseFloat('1e-' + ((rand +'').length - 1))), max) + : min + floor(rand * (max - min + 1)); } /** * Resolves the value of `property` on `object`. If `property` is a function, * it will be invoked with the `this` binding of `object` and its result returned, @@ -5330,10 +5690,11 @@ lodash.sortBy = sortBy; lodash.tap = tap; lodash.throttle = throttle; lodash.times = times; lodash.toArray = toArray; + lodash.transform = transform; lodash.union = union; lodash.uniq = uniq; lodash.unzip = unzip; lodash.values = values; lodash.where = where; @@ -5354,10 +5715,14 @@ lodash.unique = uniq; // add functions to `lodash.prototype` mixin(lodash); + // add Underscore compat + lodash.chain = lodash; + lodash.prototype.chain = function() { return this; }; + /*--------------------------------------------------------------------------*/ // add functions that return unwrapped values when chaining lodash.clone = clone; lodash.cloneDeep = cloneDeep; @@ -5405,10 +5770,11 @@ // add aliases lodash.all = every; lodash.any = some; lodash.detect = find; + lodash.findWhere = find; lodash.foldl = reduce; lodash.foldr = reduceRight; lodash.include = contains; lodash.inject = reduce; @@ -5450,45 +5816,45 @@ * * @static * @memberOf _ * @type String */ - lodash.VERSION = '1.2.1'; + lodash.VERSION = '1.3.1'; // add "Chaining" functions to the wrapper lodash.prototype.toString = wrapperToString; lodash.prototype.value = wrapperValueOf; lodash.prototype.valueOf = wrapperValueOf; // add `Array` functions that return unwrapped values - each(['join', 'pop', 'shift'], function(methodName) { + basicEach(['join', 'pop', 'shift'], function(methodName) { var func = arrayRef[methodName]; lodash.prototype[methodName] = function() { return func.apply(this.__wrapped__, arguments); }; }); // add `Array` functions that return the wrapped value - each(['push', 'reverse', 'sort', 'unshift'], function(methodName) { + basicEach(['push', 'reverse', 'sort', 'unshift'], function(methodName) { var func = arrayRef[methodName]; lodash.prototype[methodName] = function() { func.apply(this.__wrapped__, arguments); return this; }; }); // add `Array` functions that return new wrapped values - each(['concat', 'slice', 'splice'], function(methodName) { + basicEach(['concat', 'slice', 'splice'], function(methodName) { var func = arrayRef[methodName]; lodash.prototype[methodName] = function() { return new lodashWrapper(func.apply(this.__wrapped__, arguments)); }; }); // avoid array-like object bugs with `Array#shift` and `Array#splice` // in Firefox < 10 and IE < 9 if (!support.spliceObjects) { - each(['pop', 'shift', 'splice'], function(methodName) { + basicEach(['pop', 'shift', 'splice'], function(methodName) { var func = arrayRef[methodName], isSplice = methodName == 'splice'; lodash.prototype[methodName] = function() { var value = this.__wrapped__,