lodash
Version:
A utility library delivering consistency, customization, performance, and extras.
1,530 lines (1,404 loc) • 166 kB
JavaScript
/**
* @license
* Lo-Dash 1.0.2 (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>
*/
;(function(window, undefined) {
/** Detect free variable `exports` */
var freeExports = typeof exports == 'object' && exports;
/** Detect free variable `module` */
var freeModule = typeof module == 'object' && module && module.exports == freeExports && module;
/** Detect free variable `global` and use it as `window` */
var freeGlobal = typeof global == 'object' && global;
if (freeGlobal.global === freeGlobal) {
window = freeGlobal;
}
/** Used for array and object method references */
var arrayRef = [],
objectRef = {};
/** Used to generate unique IDs */
var idCounter = 0;
/** Used internally to indicate various things */
var indicatorObject = objectRef;
/** 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 match HTML entities */
var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/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 detect if a method is native */
var reNative = RegExp('^' +
(objectRef.valueOf + '')
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
.replace(/valueOf|for [^\]]+/g, '.+?') + '$'
);
/**
* Used to match ES6 template delimiters
* http://people.mozilla.org/~jorendorff/es6-draft.html#sec-7.8.6
*/
var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
/** Used to match "interpolate" template delimiters */
var reInterpolate = /<%=([\s\S]+?)%>/g;
/** Used to ensure capturing order of template delimiters */
var reNoMatch = /($^)/;
/** 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;
/** Native method shortcuts */
var ceil = Math.ceil,
concat = arrayRef.concat,
floor = Math.floor,
getPrototypeOf = reNative.test(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf,
hasOwnProperty = objectRef.hasOwnProperty,
push = arrayRef.push,
toString = objectRef.toString;
/* Native method shortcuts for methods with the same name as other `lodash` methods */
var nativeBind = reNative.test(nativeBind = slice.bind) && nativeBind,
nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray,
nativeIsFinite = window.isFinite,
nativeIsNaN = window.isNaN,
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]';
/** Detect various environments */
var isIeOpera = !!window.attachEvent,
isV8 = nativeBind && !/\n|true/.test(nativeBind + isIeOpera);
/* Detect if `Function#bind` exists and is inferred to be fast (all but V8) */
var isBindFast = nativeBind && !isV8;
/* Detect if `Object.keys` exists and is inferred to be fast (IE, Opera, V8) */
var isKeysFast = nativeKeys && (isIeOpera || isV8);
/**
* 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 a `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`.
*/
var hasEnumPrototype;
/** Detect if own properties are iterated after inherited properties (IE < 9) */
var iteratesOwnLast;
/**
* 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 = (hasObjectSpliceBug = { '0': 1, 'length': 1 },
arrayRef.splice.call(hasObjectSpliceBug, 0, 1), hasObjectSpliceBug[0]);
/** Detect if `arguments` object indexes are non-enumerable (Firefox < 4, IE < 9, PhantomJS, Safari < 5.1) */
var nonEnumArgs = true;
(function() {
var 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) { nonEnumArgs = !prop; }
hasDontEnumBug = !/valueOf/.test(props);
hasEnumPrototype = ctor.propertyIsEnumerable('prototype');
iteratesOwnLast = props[0] != 'x';
}(1));
/** Detect if `arguments` objects are `Object` objects (all but Opera < 10.5) */
var argsAreObjects = arguments.constructor == Object;
/** Detect if `arguments` objects [[Class]] is unresolvable (Firefox < 4, IE < 9) */
var noArgsClass = !isArguments(arguments);
/**
* 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 DOM 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` function.
*/
try {
var noNodeClass = toString.call(document) == objectClass && !({ 'toString': 0 } + '');
} catch(e) { }
/** Used to identify object classifications that `_.clone` supports */
var cloneableClasses = {};
cloneableClasses[funcClass] = false;
cloneableClasses[argsClass] = cloneableClasses[arrayClass] =
cloneableClasses[boolClass] = cloneableClasses[dateClass] =
cloneableClasses[numberClass] = cloneableClasses[objectClass] =
cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true;
/** Used to lookup a built-in constructor by [[Class]] */
var ctorByClass = {};
ctorByClass[arrayClass] = Array;
ctorByClass[boolClass] = Boolean;
ctorByClass[dateClass] = Date;
ctorByClass[objectClass] = Object;
ctorByClass[numberClass] = Number;
ctorByClass[regexpClass] = RegExp;
ctorByClass[stringClass] = String;
/** Used to determine if values are of the language type Object */
var objectTypes = {
'boolean': false,
'function': true,
'object': true,
'number': false,
'string': false,
'undefined': false
};
/** Used to escape characters for inclusion in compiled string literals */
var stringEscapes = {
'\\': '\\',
"'": "'",
'\n': 'n',
'\r': 'r',
'\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
/*--------------------------------------------------------------------------*/
/**
* Creates a `lodash` object, that wraps the given `value`, to enable method
* chaining.
*
* In addition to Lo-Dash methods, wrappers also have the following `Array` methods:
* `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`,
* and `unshift`
*
* The chainable wrapper functions are:
* `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, `compose`,
* `concat`, `countBy`, `debounce`, `defaults`, `defer`, `delay`, `difference`,
* `filter`, `flatten`, `forEach`, `forIn`, `forOwn`, `functions`, `groupBy`,
* `initial`, `intersection`, `invert`, `invoke`, `keys`, `map`, `max`, `memoize`,
* `merge`, `min`, `object`, `omit`, `once`, `pairs`, `partial`, `partialRight`,
* `pick`, `pluck`, `push`, `range`, `reject`, `rest`, `reverse`, `shuffle`,
* `slice`, `sort`, `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`,
* `union`, `uniq`, `unshift`, `values`, `where`, `without`, `wrap`, and `zip`
*
* The non-chainable wrapper functions are:
* `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `has`, `identity`,
* `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, `isEmpty`,
* `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`, `isObject`,
* `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`, `lastIndexOf`,
* `mixin`, `noConflict`, `pop`, `random`, `reduce`, `reduceRight`, `result`,
* `shift`, `size`, `some`, `sortedIndex`, `template`, `unescape`, and `uniqueId`
*
* The wrapper functions `first` and `last` return wrapped values when `n` is
* passed, otherwise they return unwrapped values.
*
* @name _
* @constructor
* @category Chaining
* @param {Mixed} value The value to wrap in a `lodash` instance.
* @returns {Object} Returns a `lodash` instance.
*/
function lodash(value) {
// exit early if already wrapped, even if wrapped by a different `lodash` constructor
if (value && typeof value == 'object' && value.__wrapped__) {
return value;
}
// allow invoking `lodash` without the `new` operator
if (!(this instanceof lodash)) {
return new lodash(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.
*
* @memberOf _.templateSettings
* @type RegExp
*/
'escape': /<%-([\s\S]+?)%>/g,
/**
* Used to detect code to be evaluated.
*
* @memberOf _.templateSettings
* @type RegExp
*/
'evaluate': /<%([\s\S]+?)%>/g,
/**
* Used to detect `data` property values to inject.
*
* @memberOf _.templateSettings
* @type RegExp
*/
'interpolate': reInterpolate,
/**
* Used to reference the data object in the template text.
*
* @memberOf _.templateSettings
* @type String
*/
'variable': '',
/**
* Used to import variables into the compiled template.
*
* @memberOf _.templateSettings
* @type Object
*/
'imports': {
/**
* A reference to the `lodash` function.
*
* @memberOf _.templateSettings.imports
* @type Function
*/
'_': lodash
}
};
/*--------------------------------------------------------------------------*/
/**
* 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 = function(obj) {
var __p = 'var index, iterable = ' +
(obj.firstArg ) +
', result = iterable;\nif (!iterable) return result;\n' +
(obj.top ) +
';\n';
if (obj.arrays) {
__p += 'var length = iterable.length; index = -1;\nif (' +
(obj.arrays ) +
') { ';
if (obj.noCharByIndex) {
__p += '\n if (isString(iterable)) {\n iterable = iterable.split(\'\')\n } ';
} ;
__p += '\n while (++index < length) {\n ' +
(obj.loop ) +
'\n }\n}\nelse { ';
} else if (obj.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 { ';
} ;
if (obj.hasEnumPrototype) {
__p += '\n var skipProto = typeof iterable == \'function\';\n ';
} ;
if (obj.isKeysFast && obj.useHas) {
__p += '\n var ownIndex = -1,\n ownProps = objectTypes[typeof iterable] ? nativeKeys(iterable) : [],\n length = ownProps.length;\n\n while (++ownIndex < length) {\n index = ownProps[ownIndex];\n ';
if (obj.hasEnumPrototype) {
__p += 'if (!(skipProto && index == \'prototype\')) {\n ';
} ;
__p +=
(obj.loop ) +
'';
if (obj.hasEnumPrototype) {
__p += '}\n';
} ;
__p += ' } ';
} else {
__p += '\n for (index in iterable) {';
if (obj.hasEnumPrototype || obj.useHas) {
__p += '\n if (';
if (obj.hasEnumPrototype) {
__p += '!(skipProto && index == \'prototype\')';
} if (obj.hasEnumPrototype && obj.useHas) {
__p += ' && ';
} if (obj.useHas) {
__p += 'hasOwnProperty.call(iterable, index)';
} ;
__p += ') { ';
} ;
__p +=
(obj.loop ) +
'; ';
if (obj.hasEnumPrototype || obj.useHas) {
__p += '\n }';
} ;
__p += '\n } ';
} ;
if (obj.hasDontEnumBug) {
__p += '\n\n var ctor = iterable.constructor;\n ';
for (var k = 0; k < 7; k++) {
__p += '\n index = \'' +
(obj.shadowed[k] ) +
'\';\n if (';
if (obj.shadowed[k] == 'constructor') {
__p += '!(ctor && ctor.prototype === iterable) && ';
} ;
__p += 'hasOwnProperty.call(iterable, index)) {\n ' +
(obj.loop ) +
'\n } ';
} ;
} ;
if (obj.arrays || obj.nonEnumArgs) {
__p += '\n}';
} ;
__p +=
(obj.bottom ) +
';\nreturn result';
return __p
};
/** Reusable iterator options for `assign` and `defaults` */
var defaultsIteratorOptions = {
'args': 'object, source, guard',
'top':
'var args = arguments,\n' +
' argsIndex = 0,\n' +
" argsLength = typeof guard == 'number' ? 2 : args.length;\n" +
'while (++argsIndex < argsLength) {\n' +
' iterable = args[argsIndex];\n' +
' if (iterable && objectTypes[typeof iterable]) {',
'loop': "if (typeof result[index] == 'undefined') result[index] = iterable[index]",
'bottom': ' }\n}'
};
/** Reusable iterator options shared by `each`, `forIn`, and `forOwn` */
var eachIteratorOptions = {
'args': 'collection, callback, thisArg',
'top': "callback = callback && typeof thisArg == 'undefined' ? callback : createCallback(callback, thisArg)",
'arrays': "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
};
/*--------------------------------------------------------------------------*/
/**
* 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.
* @param {Number} [fromIndex=0] The index to search 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);
if (isLarge) {
var cache = {},
index = fromIndex - 1;
while (++index < length) {
// manually coerce `value` to a string because `hasOwnProperty`, in some
// older versions of Firefox, coerces objects incorrectly
var 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(array, value, fromIndex) > -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
* @param {Function|String} func The function to bind or the method name.
* @param {Mixed} [thisArg] The `this` binding of `func`.
* @param {Array} partialArgs An array of arguments to be partially applied.
* @param {Object} [rightIndicator] Used to indicate partially applying arguments from the right.
* @returns {Function} Returns the new bound function.
*/
function createBound(func, thisArg, partialArgs, rightIndicator) {
var isFunc = isFunction(func),
isPartial = !partialArgs,
key = thisArg;
// juggle arguments
if (isPartial) {
partialArgs = thisArg;
}
if (!isFunc) {
thisArg = func;
}
function bound() {
// `Function#bind` spec
// http://es5.github.com/#x15.3.4.5
var args = arguments,
thisBinding = isPartial ? this : thisArg;
if (!isFunc) {
func = thisArg[key];
}
if (partialArgs.length) {
args = args.length
? (args = slice(args), rightIndicator ? args.concat(partialArgs) : partialArgs.concat(args))
: partialArgs;
}
if (this instanceof bound) {
// ensure `new bound` is an instance of `bound` and `func`
noop.prototype = func.prototype;
thisBinding = new noop;
noop.prototype = null;
// mimic the constructor's `return` behavior
// http://es5.github.com/#x13.2.2
var result = func.apply(thisBinding, args);
return isObject(result) ? result : thisBinding;
}
return func.apply(thisBinding, args);
}
return bound;
}
/**
* Produces a callback bound to an optional `thisArg`. If `func` is a property
* name, the created callback will return the property value for a given element.
* If `func` is an object, the created callback will return `true` for elements
* that contain the equivalent object properties, otherwise it will return `false`.
*
* @private
* @param {Mixed} [func=identity] The value to convert to a callback.
* @param {Mixed} [thisArg] The `this` binding of the created callback.
* @param {Number} [argCount=3] The number of arguments the callback accepts.
* @returns {Function} Returns a callback function.
*/
function createCallback(func, thisArg, argCount) {
if (func == null) {
return identity;
}
var type = typeof func;
if (type != 'function') {
if (type != 'object') {
return function(object) {
return object[func];
};
}
var props = keys(func);
return function(object) {
var length = props.length,
result = false;
while (length--) {
if (!(result = isEqual(object[props[length]], func[props[length]], indicatorObject))) {
break;
}
}
return result;
};
}
if (typeof thisArg != 'undefined') {
if (argCount === 1) {
return function(value) {
return func.call(thisArg, value);
};
}
if (argCount === 2) {
return function(a, b) {
return func.call(thisArg, a, b);
};
}
if (argCount === 4) {
return function(accumulator, value, index, object) {
return func.call(thisArg, accumulator, value, index, object);
};
}
return function(value, index, object) {
return func.call(thisArg, value, index, object);
};
}
return func;
}
/**
* 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.
* useHas - A boolean to specify using `hasOwnProperty` checks in the object loop.
* 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 = {
// support properties
'hasDontEnumBug': hasDontEnumBug,
'hasEnumPrototype': hasEnumPrototype,
'isKeysFast': isKeysFast,
'nonEnumArgs': nonEnumArgs,
'noCharByIndex': noCharByIndex,
'shadowed': shadowed,
// iterator options
'arrays': 'isArray(iterable)',
'bottom': '',
'loop': '',
'top': '',
'useHas': true
};
// 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];
}
}
var args = data.args;
data.firstArg = /^[^,]+/.exec(args)[0];
// create the function factory
var factory = Function(
'createCallback, hasOwnProperty, isArguments, isArray, isString, ' +
'objectTypes, nativeKeys',
'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}'
);
// return the compiled function
return factory(
createCallback, hasOwnProperty, isArguments, isArray, isString,
objectTypes, nativeKeys
);
}
/**
* A function compiled to iterate `arguments` objects, arrays, objects, and
* strings consistenly across environments, executing the `callback` for each
* element in the `collection`. The `callback` is bound to `thisArg` and invoked
* with three arguments; (value, index|key, collection). Callbacks may exit
* iteration early by explicitly returning `false`.
*
* @private
* @type Function
* @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);
/**
* 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];
}
/**
* 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
}
/**
* 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.
*/
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') : false;
};
}
/**
* Iterates over `object`'s own and inherited enumerable properties, executing
* the `callback` for each property. The `callback` is bound to `thisArg` and
* invoked with three arguments; (value, key, object). Callbacks may exit iteration
* early by explicitly returning `false`.
*
* @static
* @memberOf _
* @type Function
* @category Objects
* @param {Object} object The object to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns `object`.
* @example
*
* function Dog(name) {
* this.name = name;
* }
*
* Dog.prototype.bark = function() {
* alert('Woof, woof!');
* };
*
* _.forIn(new Dog('Dagny'), function(value, key) {
* alert(key);
* });
* // => alerts 'name' and 'bark' (order is not guaranteed)
*/
var forIn = createIterator(eachIteratorOptions, forOwnIteratorOptions, {
'useHas': false
});
/**
* Iterates over an object's own enumerable properties, executing the `callback`
* for each property. The `callback` is bound to `thisArg` and invoked with three
* arguments; (value, key, object). Callbacks may exit iteration early by explicitly
* returning `false`.
*
* @static
* @memberOf _
* @type Function
* @category Objects
* @param {Object} object The object to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns `object`.
* @example
*
* _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
* alert(key);
* });
* // => alerts '0', '1', and 'length' (order is not guaranteed)
*/
var forOwn = createIterator(eachIteratorOptions, forOwnIteratorOptions);
/**
* Checks if `value` is an array.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true`, if the `value` is an array, else `false`.
* @example
*
* (function() { return _.isArray(arguments); })();
* // => false
*
* _.isArray([1, 2, 3]);
* // => true
*/
var isArray = nativeIsArray || function(value) {
// `instanceof` may cause a memory leak in IE 7 if `value` is a host object
// http://ajaxian.com/archives/working-aroung-the-instanceof-memory-leak
return (argsAreObjects && value instanceof Array) || toString.call(value) == arrayClass;
};
/**
* Creates an array composed of the own enumerable property names of `object`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to inspect.
* @returns {Array} Returns a new array of property names.
* @example
*
* _.keys({ 'one': 1, 'two': 2, 'three': 3 });
* // => ['one', 'two', 'three'] (order is not guaranteed)
*/
var keys = !nativeKeys ? shimKeys : function(object) {
if (!isObject(object)) {
return [];
}
if ((hasEnumPrototype && typeof object == 'function') ||
(nonEnumArgs && object.length && isArguments(object))) {
return shimKeys(object);
}
return nativeKeys(object);
};
/**
* 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.
* @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 && typeof value == 'object') || isArguments(value)) {
return result;
}
// check that the constructor is `Object` (i.e. `Object instanceof Object`)
var ctor = value.constructor;
if ((!isFunction(ctor) && (!noNodeClass || !isNode(value))) || 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;
}
/**
* A fallback 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.
*/
function shimKeys(object) {
var result = [];
forOwn(object, function(value, key) {
result.push(key);
});
return result;
}
/**
* Used to convert characters to HTML entities:
*
* Though the `>` character is escaped for symmetry, characters like `>` and `/`
* don't require escaping in HTML and have no special meaning unless they're part
* of a tag or an unquoted attribute value.
* http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact")
*/
var htmlEscapes = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
/** Used to convert HTML entities to characters */
var htmlUnescapes = invert(htmlEscapes);
/*--------------------------------------------------------------------------*/
/**
* Assigns own enumerable properties of source object(s) to the destination
* object. Subsequent sources will overwrite propery assignments of previous
* sources. If a `callback` function is passed, it will be executed to produce
* the assigned values. The `callback` is bound to `thisArg` and invoked with
* two arguments; (objectValue, sourceValue).
*
* @static
* @memberOf _
* @type Function
* @alias extend
* @category Objects
* @param {Object} object The destination object.
* @param {Object} [source1, source2, ...] The source objects.
* @param {Function} [callback] The function to customize assigning values.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns the destination object.
* @example
*
* _.assign({ 'name': 'moe' }, { 'age': 40 });
* // => { 'name': 'moe', 'age': 40 }
*
* var defaults = _.partialRight(_.assign, function(a, b) {
* return typeof a == 'undefined' ? b : a;
* });
*
* var food = { 'name': 'apple' };
* defaults(food, { 'name': 'banana', 'type': 'fruit' });
* // => { 'name': 'apple', 'type': 'fruit' }
*/
var assign = createIterator(defaultsIteratorOptions, {
'top':
defaultsIteratorOptions.top.replace(';',
';\n' +
"if (argsLength > 3 && typeof args[argsLength - 2] == 'function') {\n" +
' var callback = createCallback(args[--argsLength - 1], args[argsLength--], 2);\n' +
"} else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {\n" +
' callback = args[--argsLength];\n' +
'}'
),
'loop': 'result[index] = callback ? callback(result[index], iterable[index]) : iterable[index]'
});
/**
* Creates a clone of `value`. If `deep` is `true`, nested objects will also
* be cloned, otherwise they will be assigned by reference. If a `callback`
* function is passed, it will be executed to produce the cloned values. If
* `callback` returns `undefined`, cloning will be handled by the method instead.
* The `callback` is bound to `thisArg` and invoked with one argument; (value).
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to clone.
* @param {Boolean} [deep=false] A flag to indicate a deep clone.
* @param {Function} [callback] The function to customize cloning values.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @param- {Array} [stackA=[]] Internally used to track traversed source objects.
* @param- {Array} [stackB=[]] Internally used to associate clones with source counterparts.
* @returns {Mixed} Returns the cloned `value`.
* @example
*
* var stooges = [
* { 'name': 'moe', 'age': 40 },
* { 'name': 'larry', 'age': 50 }
* ];
*
* var shallow = _.clone(stooges);
* shallow[0] === stooges[0];
* // => true
*
* var deep = _.clone(stooges, true);
* deep[0] === stooges[0];
* // => false
*
* _.mixin({
* 'clone': _.partialRight(_.clone, function(value) {
* return _.isElement(value) ? value.cloneNode(false) : undefined;
* })
* });
*
* var clone = _.clone(document.body);
* clone.childNodes.length;
* // => 0
*/
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') {
thisArg = callback;
callback = deep;
deep = false;
}
if (typeof callback == 'function') {
callback = typeof thisArg == 'undefined' ? callback : createCallback(callback, thisArg, 1);
result = callback(result);
var done = typeof result != 'undefined';
if (!done) {
result = value;
}
}
// inspect [[Class]]
var isObj = isObject(result);
if (isObj) {
var className = toString.call(result);
if (!cloneableClasses[className] || (noNodeClass && isNode(result))) {
return result;
}
var isArr = isArray(result);
}
// shallow clone
if (!isObj || !deep) {
return isObj && !done
? (isArr ? slice(result) : assign({}, result))
: result;
}
var ctor = ctorByClass[className];
switch (className) {
case boolClass:
case dateClass:
return done ? result : new ctor(+result);
case numberClass:
case stringClass:
return done ? result : new ctor(result);
case regexpClass:
return done ? result : ctor(result.source, reFlags.exec(result));
}
// check for circular references and return corresponding clone
stackA || (stackA = []);
stackB || (stackB = []);
var length = stackA.length;
while (length--) {
if (stackA[length] == value) {
return stackB[length];
}
}
// init cloned object
if (!done) {
result = isArr ? ctor(result.length) : {};
// add array properties assigned by `RegExp#exec`
if (isArr) {
if (hasOwnProperty.call(value, 'index')) {
result.index = value.index;
}
if (hasOwnProperty.call(value, 'input')) {
result.input = value.input;
}
}
}
// add the source value to the stack of traversed objects
// and associate it with its clone
stackA.push(value);
stackB.push(result);
// recursively populate clone (susceptible to call stack limits)
(isArr ? forEach : forOwn)(done ? result : value, function(objValue, key) {
result[key] = clone(objValue, deep, callback, undefined, stackA, 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 the value it
* was passed, 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
* 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
* @memberOf _
* @category Objects
* @param {Mixed} value The value to deep clone.
* @param {Function} [callback] The function to customize cloning values.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Mixed} Returns the deep cloned `value`.
* @example
*
* var stooges = [
* { 'name': 'moe', 'age': 40 },
* { 'name': 'larry', 'age': 50 }
* ];
*
* var deep = _.cloneDeep(stooges);
* deep[0] === stooges[0];
* // => false
*
* var view = {
* 'label': 'docs',
* 'node': element
* };
*
* var clone = _.cloneDeep(view, function(value) {
* return _.isElement(value) ? value.cloneNode(true) : value;
* });
*
* clone.node == view.node;
* // => false
*/
function cloneDeep(value, callback, thisArg) {
return clone(value, true, callback, thisArg);
}
/**
* Assigns own enumerable properties of source object(s) to the destination
* object for all destination properties that resolve to `undefined`. Once a
* property is set, additional defaults of the same property will be ignored.
*
* @static
* @memberOf _
* @type Function
* @category Objects
* @param {Object} object The destination object.
* @param {Object} [source1, source2, ...] The source objects.
* @param- {Object} [guard] Internally used to allow working with `_.reduce`
* without using its callback's `key` and `object` arguments as sources.
* @returns {Object} Returns the destination object.
* @example
*
* var food = { 'name': 'apple' };
* _.defaults(food, { 'name': 'banana', 'type': 'fruit' });
* // => { 'name': 'apple', 'type': 'fruit' }
*/
var defaults = createIterator(defaultsIteratorOptions);
/**
* Creates a sorted array of all enumerable properties, own and inherited,
* of `object` that have function values.
*
* @static
* @memberOf _
* @alias methods
* @category Objects
* @param {Object} object The object to inspect.
* @returns {Array} Returns a new array of property names that have function values.
* @example
*
* _.functions(_);
* // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
*/
function functions(object) {
var result = [];
forIn(object, function(value, key) {
if (isFunction(value)) {
result.push(key);
}
});
return result.sort();
}
/**
* Checks if the specified object `property` exists and is a direct property,
* instead of an inherited property.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to check.
* @param {String} property The property to check for.
* @returns {Boolean} Returns `true` if key is a direct property, else `false`.
* @example
*
* _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b');
* // => true
*/
function has(object, property) {
return object ? hasOwnProperty.call(object, property) : false;
}
/**
* Creates an object composed of the inverted keys and values of the given `object`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to invert.
* @returns {Object} Returns the created inverted object.
* @example
*
* _.invert({ 'first': 'moe', 'second': 'larry' });
* // => { 'moe': 'first', 'larry': 'second' } (order is not guaranteed)
*/
function invert(object) {
var index = -1,
props = keys(object),
length = props.length,
result = {};
while (++index < length) {
var key = props[index];
result[object[key]] = key;
}
return result;
}
/**
* Checks if `value` is a boolean value.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true`, if the `value` is a boolean value, else `false`.
* @example
*
* _.isBoolean(null);
* // => false
*/
function isBoolean(value) {
return value === true || value === false || toString.call(value) == boolClass;
}
/**
* Checks if `value` is a date.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true`, if the `value` is a date, else `false`.
* @example
*
* _.isDate(new Date);
* // => true
*/
function isDate(value) {
return value instanceof Date || toString.call(value) == dateClass;
}
/**
* 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
*/
function isEmpty(value) {
var result = true;
if (!value) {
return result;
}
var className = toString.call(value),
length = value.length;
if ((className == arrayClass || className == stringClass ||
className == argsClass || (noArgsClass && isArguments(value))) ||
(className == objectClass && typeof length == 'number' && isFunction(value.splice))) {
return !length;
}
forOwn(value, function() {
return (result = false);
});
return result;
}
/**
* Performs a deep comparison between two values to determine if they are
* equivalent to each other. If `callback` is passed, it will be executed to
* compare values. If `callback` returns `undefined`, comparisons will be handled
* by the method instead. The `callback` is bound to `thisArg` and invoked with
* two arguments; (a, b).
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} a The value to compare.
* @param {Mixed} b The other value to compare.
* @param {Function} [callback] The function to customize comparing values.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @param- {Object} [stackA=[]] Internally used track traversed `a` objects.
* @param- {Object} [stackB=[]] Internally used track traversed `b` objects.
* @returns {Boolean} Returns `true`, if the values are equvalent, else `false`.
* @example
*
* var moe = { 'name': 'moe', 'age': 40 };
* var copy = { 'name': 'moe', 'age': 40 };
*
* moe == copy;
* // => false
*
* _.isEqual(moe, copy);
* // => true
*
* var words = ['hello', 'goodbye'];
* var otherWords = ['hi', 'goodbye'];
*
* _.isEqual(words, otherWords, function(a, b) {
* var reGreet = /^(?:hello|hi)$/i,
* aGreet = _.isString(a) && reGreet.test(a),
* bGreet = _.isString(b) && reGreet.test(b);
*
* return (aGreet || bGreet) ? (aGreet == bGreet) : undefined;
* });
* // => true
*/
function isEqual(a, b, callback, thisArg, stackA, stackB) {
// used to indicate that when comparing objects, `a` has at least the properties of `b`
var whereIndicator = callback === indicatorObject;
if (callback && !whereIndicator) {
callback = typeof thisArg == 'undefined' ? callback : createCallback(callback, thisArg, 2);
var result = callback(a, b);
if (typeof result != 'undefined') {
return !!result;
}
}
// exit early for identical values
if (a === b) {
// treat `+0` vs. `-0` as not equal
return a !== 0 || (1 / a == 1 / b);
}
var type = typeof a,
otherType = typeof b;
// exit early for unlike primitive values
if (a === a &&
(!a || (type != 'function' && type != 'object')) &&
(!b || (otherType != 'function' && otherType != 'object'))) {
return false;
}
// exit early for `null` and `undefined`, avoiding ES3's Function#call behavior
// http://es5.github.com/#x15.3.4.4
if (a == null || b == null) {
return a === b;
}
// compare [[Class]] names
var className = toString.call(a),
otherClass = toString.call(b);
if (className == argsClass) {
className = objectClass;
}
if (otherClass == argsClass) {
otherClass = objectClass;
}
if (className != otherClass) {
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 + '';
}
var isArr = className == arrayClass;
if (!isArr) {
// unwrap any `lodash` wrapped values
if (a.__wrapped__ || b.__wrapped__) {
return isEqual(a.__wrapped__ || a, b.__wrapped__ || b, callback, thisArg, stackA, stackB);
}
// exit for f