nodulator
Version:
Complete NodeJS Framework for Restfull APIs
1,629 lines (1,516 loc) • 113 kB
JavaScript
/*!
* Lo-Dash v0.9.2 <http://lodash.com>
* (c) 2012 John-David Dalton <http://allyoucanleet.com/>
* Based on Underscore.js 1.4.2 <http://underscorejs.org>
* (c) 2009-2012 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 `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 = [],
// avoid a Closure Compiler bug by creatively creating an object
objectRef = new function(){};
/** Used to generate unique IDs */
var idCounter = 0;
/** Used internally to indicate various things */
var indicatorObject = objectRef;
/** Used to restore the original `_` reference in `noConflict` */
var oldDash = window._;
/** Used to match HTML entities */
var reEscapedHtml = /&(?:amp|lt|gt|quot|#x27);/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 = /\$\{((?:(?=\\?)\\?[\s\S])*?)}/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,
propertyIsEnumerable = objectRef.propertyIsEnumerable,
slice = arrayRef.slice,
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 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 `Function#bind` exists and is inferred to be fast (all but V8) */
var isBindFast = nativeBind && /\n|Opera/.test(nativeBind + toString.call(window.opera));
/**
* Detect if sourceURL syntax is usable without erroring:
*
* The JS engine in Adobe products, like InDesign, will throw a syntax error
* when it encounters a single line comment beginning with the `@` symbol.
*
* The JS engine in Narwhal will generate the function `function anonymous(){//}`
* and throw a syntax error.
*
* Avoid comments beginning `@` symbols in IE because they are part of its
* non-standard conditional compilation support.
* http://msdn.microsoft.com/en-us/library/121hztk3(v=vs.94).aspx
*/
try {
var useSourceURL = (Function('//@')(), !window.attachEvent);
} catch(e) { }
/** Used to determine if values are of the language type Object */
var objectTypes = {
'boolean': false,
'function': true,
'object': true,
'number': false,
'string': false,
'undefined': false
};
/** Used to escape characters for inclusion in compiled string literals */
var stringEscapes = {
'\\': '\\',
"'": "'",
'\n': 'n',
'\r': 'r',
'\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
/*--------------------------------------------------------------------------*/
/**
* The `lodash` function.
*
* @name _
* @constructor
* @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
if (value && 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.
*
* @static
* @memberOf _.templateSettings
* @type RegExp
*/
'escape': /<%-([\s\S]+?)%>/g,
/**
* Used to detect code to be evaluated.
*
* @static
* @memberOf _.templateSettings
* @type RegExp
*/
'evaluate': /<%([\s\S]+?)%>/g,
/**
* Used to detect `data` property values to inject.
*
* @static
* @memberOf _.templateSettings
* @type RegExp
*/
'interpolate': reInterpolate,
/**
* Used to reference the data object in the template text.
*
* @static
* @memberOf _.templateSettings
* @type String
*/
'variable': ''
};
/*--------------------------------------------------------------------------*/
/**
* Reusable iterator options shared by `forEach`, `forIn`, and `forOwn`.
*/
var forEachIteratorOptions = {
'args': 'collection, callback, thisArg',
'top': 'callback = createCallback(callback, thisArg)',
'arrayLoop': 'if (callback(value, index, collection) === false) return result',
'objectLoop': 'if (callback(value, index, collection) === false) return result'
};
/** Reusable iterator options for `forIn` and `forOwn` */
var forOwnIteratorOptions = {
'arrayLoop': null
};
/*--------------------------------------------------------------------------*/
/**
* 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 || a === undefined) {
return 1;
}
if (a < b || 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 `partailArgs` 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.
* @returns {Function} Returns the new bound function.
*/
function createBound(func, thisArg, partialArgs) {
function bound() {
// `Function#bind` spec
// http://es5.github.com/#x15.3.4.5
var args = arguments,
thisBinding = thisArg;
if (partialArgs.length) {
args = args.length
? partialArgs.concat(slice.call(args))
: partialArgs;
}
if (this instanceof bound) {
// get `func` instance if `bound` is invoked in a `new` expression
noop.prototype = func.prototype;
thisBinding = new noop;
// mimic the constructor's `return` behavior
// http://es5.github.com/#x13.2.2
var result = func.apply(thisBinding, args);
return isObject(result)
? result
: thisBinding
}
return func.apply(thisBinding, args);
}
return bound;
}
/**
* Produces an iteration callback bound to an optional `thisArg`. If `func` is
* a property name, the callback will return the property value for a given element.
*
* @private
* @param {Function|String} [func=identity|property] The function called per
* iteration or property name to query.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Function} Returns a callback function.
*/
function createCallback(func, thisArg) {
if (!func) {
return identity;
}
if (typeof func != 'function') {
return function(object) {
return object[func];
};
}
if (thisArg !== undefined) {
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).
* 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.
* arrayLoop - A string of code to execute in the array loop.
* objectLoop - 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 = {
'arrayLoop': '',
'bottom': '',
'hasDontEnumBug': hasDontEnumBug,
'objectLoop': '',
'noArgsEnum': noArgsEnum,
'noCharByIndex': noCharByIndex,
'shadowed': shadowed,
'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, isString, objectTypes, ' +
'nativeKeys, propertyIsEnumerable',
'return function(' + args + ') {\n' + (data) + '\n}'
);
// return the compiled function
return factory(
createCallback, hasOwnProperty, isString, objectTypes,
nativeKeys, propertyIsEnumerable
);
}
/**
* Used by `template` to escape characters for inclusion in compiled
* string literals.
*
* @private
* @param {String} match The matched character to escape.
* @returns {String} Returns the escaped character.
*/
function escapeStringChar(match) {
return '\\' + stringEscapes[match];
}
/**
* Used by `escape` to convert characters to HTML entities.
*
* @private
* @param {String} match The matched character to escape.
* @returns {String} Returns the escaped character.
*/
function escapeHtmlChar(match) {
return htmlEscapes[match];
}
/**
* A no-operation function.
*
* @private
*/
function noop() {
// no operation performed
}
/**
* Used by `unescape` to convert HTML entities to characters.
*
* @private
* @param {String} match The matched character to unescape.
* @returns {String} Returns the unescaped character.
*/
function unescapeHtmlChar(match) {
return htmlUnescapes[match];
}
/*--------------------------------------------------------------------------*/
/**
* Checks if `value` is an `arguments` object.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is an `arguments` object, else `false`.
* @example
*
* (function() { return _.isArguments(arguments); })(1, 2, 3);
* // => true
*
* _.isArguments([1, 2, 3]);
* // => false
*/
lodash.isArguments = function(value) {
return toString.call(value) == argsClass;
}
// fallback for browsers that can't detect `arguments` objects by [[Class]]
if (!lodash.isArguments(arguments)) {
lodash.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 _
* @category Objects
* @param {Object} object The object to iterate over.
* @param {Function} callback The function called per iteration.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns `object`.
* @example
*
* function Dog(name) {
* this.name = name;
* }
*
* Dog.prototype.bark = function() {
* alert('Woof, woof!');
* };
*
* _.forIn(new Dog('Dagny'), function(value, key) {
* alert(key);
* });
* // => alerts 'name' and 'bark' (order is not guaranteed)
*/
var forIn = function (collection, callback) {
var index, value, iteratee = collection, result = collection;
if (!collection) return result;
callback = createCallback(callback);
for (index in iteratee) {
value = iteratee[index];
if (callback(value, index, collection) === indicatorObject) return result;
}
;
return result
};
/**
* Iterates over `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 _
* @category Objects
* @param {Object} object The object to iterate over.
* @param {Function} callback The function called per iteration.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns `object`.
* @example
*
* _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
* alert(key);
* });
* // => alerts '0', '1', and 'length' (order is not guaranteed)
*/
var forOwn = function (collection, callback) {
var index, value, iteratee = collection, result = collection;
if (!collection) return result;
callback = createCallback(callback);
for (index in iteratee) {
if (hasOwnProperty.call(iteratee, index)) {
value = iteratee[index];
if (callback(value, index, collection) === indicatorObject) return result;
}
}
;
return result
};
/**
* 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;
}
// IE < 9 presents DOM nodes as `Object` objects except they have `toString`
// methods that are `typeof` "string" and still can coerce nodes to strings.
// Also check that the constructor is `Object` (i.e. `Object instanceof Object`)
var ctor = value.constructor;
if (
(!isFunction(ctor) || ctor instanceof ctor)) {
// 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);
/*--------------------------------------------------------------------------*/
/**
* Creates a clone of `value`. If `deep` is `true`, all nested objects will
* also be cloned otherwise they will be assigned by reference. Functions, DOM
* nodes, `arguments` objects, and objects created by constructors other than
* `Object` are **not** cloned.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to clone.
* @param {Boolean} deep A flag to indicate a deep clone.
* @param- {Object} [guard] Internally used to allow this method to work with
* others like `_.map` without using their callback `index` argument for `deep`.
* @param- {Array} [stackA=[]] Internally used to track traversed source objects.
* @param- {Array} [stackB=[]] Internally used to associate clones with their
* source counterparts.
* @returns {Mixed} Returns the cloned `value`.
* @example
*
* var stooges = [
* { 'name': 'moe', 'age': 40 },
* { 'name': 'larry', 'age': 50 },
* { 'name': 'curly', 'age': 60 }
* ];
*
* _.clone({ 'name': 'moe' });
* // => { 'name': 'moe' }
*
* var shallow = _.clone(stooges);
* shallow[0] === stooges[0];
* // => true
*
* var deep = _.clone(stooges, true);
* shallow[0] === stooges[0];
* // => false
*/
function clone(value) {
return value && objectTypes[typeof value]
? (isArray(value) ? slice.call(value) : extend({}, value))
: value
}
/**
* Assigns enumerable properties of the default object(s) to the `destination`
* object for all `destination` properties that resolve to `null`/`undefined`.
* Once a property is set, additional defaults of the same property will be
* ignored.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The destination object.
* @param {Object} [default1, default2, ...] The default objects.
* @returns {Object} Returns the destination object.
* @example
*
* var iceCream = { 'flavor': 'chocolate' };
* _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' });
* // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' }
*/
var defaults = function (object) {
var index, value, iteratee = object, result = object;
if (!object) return result;
for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {
if (iteratee = arguments[argsIndex]) {;
for (index in iteratee) {
value = iteratee[index];
if (result[index] == null) result[index] = value;
}
}
};
return result
};
/**
* Assigns enumerable properties of the source object(s) to the `destination`
* object. Subsequent sources will overwrite propery assignments of previous
* sources.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The destination object.
* @param {Object} [source1, source2, ...] The source objects.
* @returns {Object} Returns the destination object.
* @example
*
* _.extend({ 'name': 'moe' }, { 'age': 40 });
* // => { 'name': 'moe', 'age': 40 }
*/
var extend = function (object) {
var index, value, iteratee = object, result = object;
if (!object) return result;
for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {
if (iteratee = arguments[argsIndex]) {;
for (index in iteratee) {
value = iteratee[index];
result[index] = value;
}
}
};
return result
};
/**
* Creates a sorted array of all enumerable properties, own and inherited,
* of `object` that have function values.
*
* @static
* @memberOf _
* @alias methods
* @category Objects
* @param {Object} object The object to inspect.
* @returns {Array} Returns a new array of property names that have function values.
* @example
*
* _.functions(_);
* // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
*/
function functions(object) {
var result = [];
forIn(object, function(value, key) {
if (isFunction(value)) {
result.push(key);
}
});
return result.sort();
}
/**
* Checks if the specified object `property` exists and is a direct property,
* instead of an inherited property.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to check.
* @param {String} property The property to check for.
* @returns {Boolean} Returns `true` if key is a direct property, else `false`.
* @example
*
* _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b');
* // => true
*/
function has(object, property) {
return object ? hasOwnProperty.call(object, property) : false;
}
/**
* Creates an object composed of the inverted keys and values of the given `object`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to invert.
* @returns {Object} Returns the created inverted object.
* @example
*
* _.invert({ 'first': 'Moe', 'second': 'Larry', 'third': 'Curly' });
* // => { 'Moe': 'first', 'Larry': 'second', 'Curly': 'third' } (order is not guaranteed)
*/
function invert(object) {
var result = {};
forOwn(object, function(value, key) {
result[value] = key;
});
return result;
}
/**
* Checks if `value` is an array.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is an array, else `false`.
* @example
*
* (function() { return _.isArray(arguments); })();
* // => false
*
* _.isArray([1, 2, 3]);
* // => true
*/
var isArray = nativeIsArray || function(value) {
return toString.call(value) == arrayClass;
};
/**
* Checks if `value` is a boolean (`true` or `false`) value.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is a boolean value, else `false`.
* @example
*
* _.isBoolean(null);
* // => false
*/
function isBoolean(value) {
return value === true || value === false || toString.call(value) == boolClass;
}
/**
* Checks if `value` is a date.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is a date, else `false`.
* @example
*
* _.isDate(new Date);
* // => true
*/
function isDate(value) {
return toString.call(value) == dateClass;
}
/**
* Checks if `value` is a DOM element.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is a DOM element, else `false`.
* @example
*
* _.isElement(document.body);
* // => true
*/
function isElement(value) {
return value ? value.nodeType === 1 : 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) {
if (!value) {
return true;
}
if (isArray(value) || isString(value)) {
return !value.length;
}
for (var key in value) {
if (hasOwnProperty.call(value, key)) {
return false;
}
}
return true;
}
/**
* Performs a deep comparison between two values to determine if they are
* equivalent to each other.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} a The value to compare.
* @param {Mixed} b The other value to compare.
* @param- {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', 'luckyNumbers': [13, 27, 34] };
* var clone = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] };
*
* moe == clone;
* // => false
*
* _.isEqual(moe, clone);
* // => true
*/
function isEqual(a, b, stackA, stackB) {
// exit early for identical values
if (a === b) {
// treat `+0` vs. `-0` as not equal
return a !== 0 || (1 / a == 1 / b);
}
// a strict comparison is necessary because `null == undefined`
if (a == null || b == null) {
return a === b;
}
// compare [[Class]] names
var className = toString.call(a);
if (className != toString.call(b)) {
return false;
}
switch (className) {
case boolClass:
case dateClass:
// coerce dates and booleans to numbers, dates to milliseconds and booleans
// to `1` or `0`, treating invalid dates coerced to `NaN` as not equal
return +a == +b;
case numberClass:
// treat `NaN` vs. `NaN` as equal
return a != +a
? b != +b
// but treat `+0` vs. `-0` as not equal
: (a == 0 ? (1 / a == 1 / b) : a == +b);
case regexpClass:
case stringClass:
// coerce regexes to strings (http://es5.github.com/#x15.10.6.4)
// treat string primitives and their corresponding object instances as equal
return a == b + '';
}
// exit early, in older browsers, if `a` is array-like but not `b`
var isArr = className == arrayClass;
if (!isArr) {
// unwrap any `lodash` wrapped values
if (a.__wrapped__ || b.__wrapped__) {
return isEqual(a.__wrapped__ || a, b.__wrapped__ || b);
}
// exit for functions and DOM nodes
if (className != objectClass) {
return false;
}
var ctorA = a.constructor,
ctorB = b.constructor;
// non `Object` object instances with different constructors are not equal
if (ctorA != ctorB && !(
isFunction(ctorA) && ctorA instanceof ctorA &&
isFunction(ctorB) && ctorB instanceof ctorB
)) {
return false;
}
}
// 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 length = stackA.length;
while (length--) {
if (stackA[length] == a) {
return stackB[length] == b;
}
}
var index = -1,
result = true,
size = 0;
// add `a` and `b` to the stack of traversed objects
stackA.push(a);
stackB.push(b);
// recursively compare objects and arrays (susceptible to call stack limits)
if (isArr) {
// compare lengths to determine if a deep comparison is necessary
size = a.length;
result = size == b.length;
if (result) {
// deep compare the contents, ignoring non-numeric properties
while (size--) {
if (!(result = isEqual(a[size], b[size], stackA, stackB))) {
break;
}
}
}
return result;
}
// deep compare objects
for (var key in a) {
if (hasOwnProperty.call(a, key)) {
// count the number of properties.
size++;
// deep compare each property value.
if (!(hasOwnProperty.call(b, key) && isEqual(a[key], b[key], stackA, stackB))) {
return false;
}
}
}
// ensure both objects have the same number of properties
for (key in b) {
// The JS engine in Adobe products, like InDesign, has a bug that causes
// `!size--` to throw an error so it must be wrapped in parentheses.
// https://github.com/documentcloud/underscore/issues/355
if (hasOwnProperty.call(b, key) && !(size--)) {
// `size` will be `-1` if `b` has more properties than `a`
return false;
}
}
return true;
}
/**
* Checks if `value` is, or can be coerced to, a finite number.
*
* Note: This is not the same as native `isFinite`, which will return true for
* booleans and empty strings. See http://es5.github.com/#x15.1.2.5.
*
* @deprecated
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is a finite number, else `false`.
* @example
*
* _.isFinite(-101);
* // => true
*
* _.isFinite('10');
* // => true
*
* _.isFinite(true);
* // => false
*
* _.isFinite('');
* // => false
*
* _.isFinite(Infinity);
* // => false
*/
function isFinite(value) {
return nativeIsFinite(value) && toString.call(value) == numberClass;
}
/**
* Checks if `value` is a function.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is a function, else `false`.
* @example
*
* _.isFunction(_);
* // => true
*/
function isFunction(value) {
return typeof value == 'function';
}
// fallback for older versions of Chrome and Safari
if (isFunction(/x/)) {
isFunction = function(value) {
return toString.call(value) == funcClass;
};
}
/**
* Checks if `value` is the language type of Object.
* (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is an object, else `false`.
* @example
*
* _.isObject({});
* // => true
*
* _.isObject([1, 2, 3]);
* // => true
*
* _.isObject(1);
* // => false
*/
function isObject(value) {
// check if the value is the ECMAScript language type of Object
// http://es5.github.com/#x8
// and avoid a V8 bug
// http://code.google.com/p/v8/issues/detail?id=2291
return value ? objectTypes[typeof value] : false;
}
/**
* Checks if `value` is `NaN`.
*
* Note: This is not the same as native `isNaN`, which will return true for
* `undefined` and other values. See http://es5.github.com/#x15.1.2.4.
*
* @deprecated
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is `NaN`, else `false`.
* @example
*
* _.isNaN(NaN);
* // => true
*
* _.isNaN(new Number(NaN));
* // => true
*
* isNaN(undefined);
* // => true
*
* _.isNaN(undefined);
* // => false
*/
function isNaN(value) {
// `NaN` as a primitive is the only value that is not equal to itself
// (perform the [[Class]] check first to avoid errors with some host objects in IE)
return toString.call(value) == numberClass && value != +value
}
/**
* Checks if `value` is `null`.
*
* @deprecated
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is `null`, else `false`.
* @example
*
* _.isNull(null);
* // => true
*
* _.isNull(undefined);
* // => false
*/
function isNull(value) {
return value === null;
}
/**
* Checks if `value` is a number.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is a number, else `false`.
* @example
*
* _.isNumber(8.4 * 5);
* // => true
*/
function isNumber(value) {
return toString.call(value) == numberClass;
}
/**
* Checks if `value` is a regular expression.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is a regular expression, else `false`.
* @example
*
* _.isRegExp(/moe/);
* // => true
*/
function isRegExp(value) {
return toString.call(value) == regexpClass;
}
/**
* Checks if `value` is a string.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is a string, else `false`.
* @example
*
* _.isString('moe');
* // => true
*/
function isString(value) {
return toString.call(value) == stringClass;
}
/**
* Checks if `value` is `undefined`.
*
* @deprecated
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is `undefined`, else `false`.
* @example
*
* _.isUndefined(void 0);
* // => true
*/
function isUndefined(value) {
return value === undefined;
}
/**
* Creates an array composed of the own enumerable property names of `object`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to inspect.
* @returns {Array} Returns a new array of property names.
* @example
*
* _.keys({ 'one': 1, 'two': 2, 'three': 3 });
* // => ['one', 'two', 'three'] (order is not guaranteed)
*/
var keys = !nativeKeys ? shimKeys : function(object) {
return (isObject(object) ? nativeKeys(object) : []);
};
/**
* Creates a shallow clone of `object` excluding the specified properties.
* Property names may be specified as individual arguments or as arrays of
* property names. If `callback` is passed, it will be executed for each property
* in the `object`, omitting the properties `callback` returns truthy for. The
* `callback` is bound to `thisArg` and invoked with three arguments; (value, key, object).
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The source object.
* @param {Function|String} callback|[prop1, prop2, ...] The properties to omit
* or the function called per iteration.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns an object without the omitted properties.
* @example
*
* _.omit({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'userid');
* // => { 'name': 'moe', 'age': 40 }
*
* _.omit({ 'name': 'moe', '_hint': 'knucklehead', '_seed': '96c4eb' }, function(value, key) {
* return key.charAt(0) == '_';
* });
* // => { 'name': 'moe' }
*/
function omit(object) {
var props = concat.apply(arrayRef, arguments),
result = {};
forIn(object, function(value, key) {
if (indexOf(props, key, 1) < 0) {
result[key] = value;
}
});
return result;
}
/**
* Creates a two dimensional array of the given object's key-value pairs,
* i.e. `[[key1, value1], [key2, value2]]`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to inspect.
* @returns {Array} Returns new array of key-value pairs.
* @example
*
* _.pairs({ 'moe': 30, 'larry': 40, 'curly': 50 });
* // => [['moe', 30], ['larry', 40], ['curly', 50]] (order is not guaranteed)
*/
function pairs(object) {
var result = [];
forOwn(object, function(value, key) {
result.push([key, value]);
});
return result;
}
/**
* Creates a shallow clone of `object` composed of the specified properties.
* Property names may be specified as individual arguments or as arrays of
* property names. If `callback` is passed, it will be executed for each property
* in the `object`, picking the properties `callback` returns truthy for. The
* `callback` is bound to `thisArg` and invoked with three arguments; (value, key, object).
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The source object.
* @param {Function|String} callback|[prop1, prop2, ...] The properties to pick
* or the function called per iteration.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns an object composed of the picked properties.
* @example
*
* _.pick({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'name', 'age');
* // => { 'name': 'moe', 'age': 40 }
*
* _.pick({ 'name': 'moe', '_hint': 'knucklehead', '_seed': '96c4eb' }, function(value, key) {
* return key.charAt(0) != '_';
* });
* // => { 'name': 'moe' }
*/
function pick(object) {
var index = 0,
props = concat.apply(arrayRef, arguments),
length = props.length,
result = {};
while (++index < length) {
var prop = props[index];
if (prop in object) {
result[prop] = object[prop];
}
}
return result;
}
/**
* Creates an array composed of the own enumerable property values of `object`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to inspect.
* @returns {Array} Returns a new array of property values.
* @example
*
* _.values({ 'one': 1, 'two': 2, 'three': 3 });
* // => [1, 2, 3]
*/
function values(object) {
var result = [];
forOwn(object, function(value) {
result.push(value);
});
return result;
}
/*--------------------------------------------------------------------------*/
/**
* Checks if a given `target` element is present in a `collection` using strict
* equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used
* as the offset from the end of the collection.
*
* @static
* @memberOf _
* @alias include
* @category Collections
* @param {Array|Object|String} collection The collection to iterate over.
* @param {Mixed} target The value to check for.
* @param {Number} [fromIndex=0] The index to search from.
* @returns {Boolean} Returns `true` if the `target` element is found, else `false`.
* @example
*
* _.contains([1, 2, 3], 1);
* // => true
*
* _.contains([1, 2, 3], 1, 2);
* // => false
*
* _.contains({ 'name': 'moe', 'age': 40 }, 'moe');
* // => true
*
* _.contains('curly', 'ur');
* // => true
*/
function contains(collection, target) {
var length = collection ? collection.length : 0;
return typeof length == 'number'
? indexOf(collection, target) > -1
: some(collection, function(value) { return value === target; });
}
/**
* Creates an object composed of keys returned from running each element of
* `collection` through a `callback`. The corresponding value of each key is
* the number of times the key was returned by `callback`. The `callback` is
* bound to `thisArg` and invoked with three arguments; (value, index|key, collection).
* The `callback` argument may also be the name of a property to count by (e.g. 'length').
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object|String} collection The collection to iterate over.
* @param {Function|String} callback|property The function called per iteration
* or property name to count by.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns the composed aggregate object.
* @example
*
* _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); });
* // => { '4': 1, '6': 2 }
*
* _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
* // => { '4': 1, '6': 2 }
*
* _.countBy(['one', 'two', 'three'], 'length');
* // => { '3': 2, '5': 1 }
*/
function countBy(collection, callback, thisArg) {
var result = {};
callback = createCallback(callback, thisArg);
forEach(collection, function(value, key, collection) {
key = callback(value, key, collection);
(hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1);
});
return result;
}
/**
* Checks if the `callback` returns a truthy value for **all** elements of a
* `collection`. The `callback` is bound to `thisArg` and invoked with three
* arguments; (value, index|key, collection).
*
* @static
* @memberOf _
* @alias all
* @category Collections
* @param {Array|Object|String} collection The collection to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Boolean} Returns `true` if all elements pass the callback check,
* else `false`.
* @example
*
* _.every([true, 1, null, 'yes'], Boolean);
* // => false
*/
function every(collection, callback, thisArg) {
var result = true;
callback = createCallback(callback, thisArg);
if (isArray(collection)) {
var index = -1,
length = collection.length;
while (++index < length) {
if (!(result = !!callback(collection[index], index, collection))) {
break;
}
}
} else {
forEach(collection, function(value, index, collection) {
return !(result = !!callback(value, index, collection)) && indicatorObject;
});
}
return result;
}
/**
* Examines each element in a `collection`, returning an array of all elements
* the `callback` returns truthy for. The `callback` is bound to `thisArg` and
* invoked with three arguments; (value, index|key, collection).
*
* @static
* @memberOf _
* @alias select
* @category Collections
* @param {Array|Object|String} collection The collection to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Array} Returns a new array of elements that passed the callback check.
* @example
*
* var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
* // => [2, 4, 6]
*/
function filter(collection, callback, thisArg) {
var result = [];
callback = createCallback(callback, thisArg);
forEach(collection, function(value, index, collection) {
if (callback(value, index, collection)) {
result.push(value);
}
});
return result;
}
/**
* Examines each element in a `collection`, returning the first one the `callback`
* returns truthy for. The function returns as soon as it finds an acceptable
* element, and does not iterate over the entire `collection`. The `callback` is
* bound to `thisArg` and invoked with three arguments; (value, index|key, collection).
*
* @static
* @memberOf _
* @alias detect
* @category Collections
* @param {Array|Object|String} collection The collection to iterate over.
* @param {Function} callback The function called per iteration.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Mixed} Returns the element that passed the callback check,
* else `undefined`.
* @example
*
* var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
* // => 2
*/
function find(collection, callback, thisArg) {
var result;
callback = createCallback(callback, thisArg);
forEach(collection, function(value, index, collection) {
if (callback(value, index, collection)) {
result = value;
return false;
}
});
return result;
}
/**
* Iterates over a `collection`, executing the `callback` for each element in
* the `collection`. The `callback` is bound to `thisArg` and invoked with three
* arguments; (value, index|key, collection). Callbacks may exit iteration early
* by explicitly returning `false`.
*
* @static
* @memberOf _
* @alias each
* @category Collections
* @param {Array|Object|String} collection The collection to iterate over.
* @param {Function} callback The function called per iteration.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Array|Object|String} Returns `collection`.
* @example
*
* _([1, 2, 3]).forEach(alert).join(',');
* // => alerts each number and returns '1,2,3'
*
* _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert);
* // => alerts each number (order is not guaranteed)
*/
var forEach = function (collection, callback, thisArg) {
var index, value, iteratee = collection, result = collection;
if (!collection) return result;
callback = createCallback(callback, thisArg);
var length = iteratee.length; index = -1;
if (typeof length == 'number') {
while (++index < length) {
value = iteratee[index];
if (callback(value, index, collection) === indicatorObject) return result
}
}
else {
for (index in iteratee) {
if (hasOwnProperty.call(iteratee, index)) {
value = iteratee[index];
if (callback(value, index, collection) === indicatorObject) return result;
}
}
}
;
};
/**
* Creates an object composed of keys returned from running each element of
* `collection` through a `callback`. The corresponding value of each key is an
* array of elements passed to `callback` that returned the key. The `callback`
* is bound to `thisArg` and invoked with three arguments; (value, index|key, collection).
* The `callback` argument may also be the name of a property to group by (e.g. 'length').
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object|String} collection The collection to iterate over.
* @param {Function|String} callback|property The function called per iteration
* or property name to group by.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns the composed aggregate object.
* @example
*
* _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); });
* // => { '4': [4.2], '6': [6.1, 6.4] }
*
* _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
* // => { '4': [4.2], '6': [6.1, 6.4] }
*
* _.groupBy(['one', 'two', 'three'