diff --git a/.npmignore b/.npmignore
index 08210e2d72..2529b8039b 100644
--- a/.npmignore
+++ b/.npmignore
@@ -18,6 +18,7 @@ vendor/docdown/
vendor/firebug-lite/
vendor/json3/
vendor/jquery/
+vendor/platform.js/
vendor/qunit/qunit/*.css
vendor/qunit/qunit/*-1.8.0.js
vendor/requirejs/
diff --git a/README.md b/README.md
index 6181c7790f..11aefd27ed 100644
--- a/README.md
+++ b/README.md
@@ -1,29 +1,29 @@
-# Lo-Dash v1.0.1
+# Lo-Dash v1.1.0
[](http://travis-ci.org/bestiejs/lodash)
-An alternative to Underscore.js, delivering consistency, [customization](https://github.com/bestiejs/lodash#custom-builds), [performance](http://lodash.com/benchmarks), and [extra features](https://github.com/bestiejs/lodash#features).
+A low-level utility library delivering consistency, [customization](https://github.com/bestiejs/lodash#custom-builds), [performance](http://lodash.com/benchmarks), and [extra features](https://github.com/bestiejs/lodash#features).
## Download
* Lo-Dash builds (for modern environments):
-[Development](https://raw.github.com/bestiejs/lodash/v1.0.1/dist/lodash.js) and
-[Production](https://raw.github.com/bestiejs/lodash/v1.0.1/dist/lodash.min.js)
+[Development](https://raw.github.com/bestiejs/lodash/v1.1.0/dist/lodash.js) and
+[Production](https://raw.github.com/bestiejs/lodash/v1.1.0/dist/lodash.min.js)
* Lo-Dash compatibility builds (for legacy and modern environments):
-[Development](https://raw.github.com/bestiejs/lodash/v1.0.1/dist/lodash.compat.js) and
-[Production](https://raw.github.com/bestiejs/lodash/v1.0.1/dist/lodash.compat.min.js)
+[Development](https://raw.github.com/bestiejs/lodash/v1.1.0/dist/lodash.compat.js) and
+[Production](https://raw.github.com/bestiejs/lodash/v1.1.0/dist/lodash.compat.min.js)
* Underscore compatibility builds:
-[Development](https://raw.github.com/bestiejs/lodash/v1.0.1/dist/lodash.underscore.js) and
-[Production](https://raw.github.com/bestiejs/lodash/v1.0.1/dist/lodash.underscore.min.js)
+[Development](https://raw.github.com/bestiejs/lodash/v1.1.0/dist/lodash.underscore.js) and
+[Production](https://raw.github.com/bestiejs/lodash/v1.1.0/dist/lodash.underscore.min.js)
-* CDN copies of ≤ v1.0.1’s builds are available on [cdnjs](http://cdnjs.com/) thanks to [CloudFlare](http://www.cloudflare.com/):
-[Lo-Dash dev](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.1/lodash.js),
-[Lo-Dash prod](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.1/lodash.min.js),
-[Lo-Dash compat-dev](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.1/lodash.compat.js),
-[Lo-Dash compat-prod](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.1/lodash.compat.min.js),
-[Underscore compat-dev](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.1/lodash.underscore.js), and
-[Underscore compat-prod](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.1/lodash.underscore.min.js)
+* CDN copies of ≤ v1.1.0’s builds are available on [cdnjs](http://cdnjs.com/) thanks to [CloudFlare](http://www.cloudflare.com/):
+[Lo-Dash dev](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.1.0/lodash.js),
+[Lo-Dash prod](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.1.0/lodash.min.js),
+[Lo-Dash compat-dev](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.1.0/lodash.compat.js),
+[Lo-Dash compat-prod](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.1.0/lodash.compat.min.js),
+[Underscore compat-dev](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.1.0/lodash.underscore.js), and
+[Underscore compat-prod](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.1.0/lodash.underscore.min.js)
* For optimal file size, [create a custom build](https://github.com/bestiejs/lodash#custom-builds) with only the features you need
@@ -56,12 +56,17 @@ For more information check out these articles, screencasts, and other videos ove
* [_.bindKey](http://lodash.com/docs#bindKey) for binding [*“lazy”* defined](http://michaux.ca/articles/lazy-function-definition-pattern) methods
* [_.cloneDeep](http://lodash.com/docs#cloneDeep) for deep cloning arrays and objects
* [_.contains](http://lodash.com/docs#contains) accepts a `fromIndex` argument
+ * [_.createCallback](http://lodash.com/docs#createCallback) to customize how callback arguments are handled and support callback shorthands in mixins
+ * [_.findIndex](http://lodash.com/docs#findIndex) and [_.findKey](http://lodash.com/docs#findKey) for finding indexes and keys of collections
* [_.forEach](http://lodash.com/docs#forEach) is chainable and supports exiting iteration early
* [_.forIn](http://lodash.com/docs#forIn) for iterating over an object’s own and inherited properties
* [_.forOwn](http://lodash.com/docs#forOwn) for iterating over an object’s own properties
* [_.isPlainObject](http://lodash.com/docs#isPlainObject) checks if values are created by the `Object` constructor
* [_.merge](http://lodash.com/docs#merge) for a deep [_.extend](http://lodash.com/docs#extend)
+ * [_.parseInt](http://lodash.com/docs#parseInt) for consistent cross-environment behavior
* [_.partial](http://lodash.com/docs#partial) and [_.partialRight](http://lodash.com/docs#partialRight) for partial application without `this` binding
+ * [_.runInContext](http://lodash.com/docs#runInContext) for easier mocking and extended environment support
+ * [_.support](http://lodash.com/docs#support) to flag environment features
* [_.template](http://lodash.com/docs#template) supports [*“imports”* options](http://lodash.com/docs#templateSettings_imports), [ES6 template delimiters](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-7.8.6), and [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl)
* [_.where](http://lodash.com/docs#where) supports deep object comparisons
* [_.clone](http://lodash.com/docs#clone), [_.omit](http://lodash.com/docs#omit), [_.pick](http://lodash.com/docs#pick),
@@ -73,7 +78,7 @@ For more information check out these articles, screencasts, and other videos ove
## Support
-Lo-Dash has been tested in at least Chrome 5~24, Firefox 1~18, IE 6-10, Opera 9.25-12, Safari 3-6, Node.js 0.4.8-0.8.20, Narwhal 0.3.2, PhantomJS 1.8.1, RingoJS 0.9, and Rhino 1.7RC5.
+Lo-Dash has been tested in at least Chrome 5~25, Firefox 2~19, IE 6-10, Opera 9.25-12, Safari 3-6, Node.js 0.4.8-0.10.1, Narwhal 0.3.2, PhantomJS 1.8.1, RingoJS 0.9, and Rhino 1.7RC5.
## Custom builds
@@ -86,7 +91,7 @@ lodash backbone
```
* CSP builds, supporting default [Content Security Policy](https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html) restrictions, may be created using the `csp` modifier argument.
- The `csp` modifier is an alais of the `mobile` modifier. Lo-Dash may be used in Chrome extensions by using either the `csp`, `mobile`, or `underscore` build and using precompiled templates, or loading Lo-Dash in a [sandbox](http://developer.chrome.com/stable/extensions/sandboxingEval.html).
+ The `csp` modifier is an alias of the `mobile` modifier. Lo-Dash may be used in Chrome extensions by using either the `csp`, `mobile`, or `underscore` build and using precompiled templates, or loading Lo-Dash in a [sandbox](http://developer.chrome.com/stable/extensions/sandboxingEval.html).
```bash
lodash csp
```
@@ -209,18 +214,18 @@ To avoid potential issues, update `npm` before installing Lo-Dash:
npm install npm -g
```
-In [Node.js](http://nodejs.org/) and [RingoJS v0.8.0+](http://ringojs.org/):
+In [Node.js](http://nodejs.org/) and [RingoJS ≥ v0.8.0](http://ringojs.org/):
```js
var _ = require('lodash');
// or as a drop-in replacement for Underscore
-var _ = require('lodash/lodash.underscore');
+var _ = require('lodash/dist/lodash.underscore');
```
**Note:** If Lo-Dash is installed globally, run [`npm link lodash`](http://blog.nodejs.org/2011/03/23/npm-1-0-global-vs-local-installation/) in your project’s root directory before requiring it.
-In [RingoJS v0.7.0-](http://ringojs.org/):
+In [RingoJS ≤ v0.7.0](http://ringojs.org/):
```js
var _ = require('lodash')._;
@@ -247,21 +252,25 @@ require({
## Release Notes
-### v1.0.1
+### v1.1.0
- * Add support for specifying source map URLs in `-p`/`--source-map` build options
- * Ensured the second argument passed to `_.assign` is not treated as a `callback`
- * Ensured `-p`/`--source-map` build options correctly set the `sourceMappingURL`
- * Made `-p`/`--source-map` build options set source map *“sources”* keys based on the builds performed
- * Made `_.defer` use `setImmediate`, in Node.js, when available
- * Made `_.where` search arrays for values regardless of their index position
- * Removed dead code from `_.template`
+ * Added `rhino -require` support
+ * Added `_.createCallback`, `_findIndex`, `_.findKey`, `_.parseInt`, `_.runInContext`, and `_.support`
+ * Added support for `callback` and `thisArg` arguments to `_.flatten`
+ * Added CommonJS/Node support to precompiled templates
+ * Ensured the `exports` object is not a DOM element
+ * Ensured `_.isPlainObject` returns `false` for objects without a `[[Class]]` of “Object”
+ * Made `_.cloneDeep`’s `callback` support more closely follow its documentation
+ * Made the template precompiler create nonexistent directories of `--output` paths
+ * Made `_.object` an alias of `_.zipObject`
+ * Optimized method chaining, object iteration, `_.find`, and `_.pluck` (an average of 18% overall better performance)
+ * Updated `backbone` build Lo-Dash method dependencies
The full changelog is available [here](https://github.com/bestiejs/lodash/wiki/Changelog).
## BestieJS
-Lo-Dash is part of the BestieJS *“Best in Class”* module collection. This means we promote solid browser/environment support, ES5 precedents, unit testing, and plenty of documentation.
+Lo-Dash is part of the BestieJS *“Best in Class”* module collection. This means we promote solid browser/environment support, ES5+ precedents, unit testing, and plenty of documentation.
## Author
diff --git a/build.js b/build.js
index 4e34c827da..b49647aadb 100755
--- a/build.js
+++ b/build.js
@@ -2,12 +2,18 @@
;(function() {
'use strict';
- /** Load modules */
+ /** Load Node.js modules */
var fs = require('fs'),
path = require('path'),
- vm = require('vm'),
+ vm = require('vm');
+
+ /** Load other modules */
+ var _ = require(path.join(__dirname, 'lodash.js')),
minify = require(path.join(__dirname, 'build', 'minify.js')),
- _ = require(path.join(__dirname, 'lodash.js'));
+ mkdirpSync = require(path.join(__dirname, 'build', 'mkdirp-sync.js'));
+
+ /** Add `path.sep` for older versions of Node.js */
+ path.sep || (path.sep = process.platform == 'win32' ? '\\' : '/');
/** The current working directory */
var cwd = process.cwd();
@@ -18,6 +24,9 @@
/** Shortcut used to push arrays of values to an array */
var push = arrayRef.push;
+ /** Used to detect the Node.js executable in command-line arguments */
+ var reNode = RegExp('(?:^|' + path.sep + ')node(?:\\.exe)?$');
+
/** Shortcut used to convert array-like objects to arrays */
var slice = arrayRef.slice;
@@ -39,6 +48,7 @@
'include': 'contains',
'inject': 'reduce',
'methods': 'functions',
+ 'object': 'zipObject',
'select': 'filter',
'tail': 'rest',
'take': 'first',
@@ -60,13 +70,14 @@
'reduceRight': ['foldr'],
'rest': ['drop', 'tail'],
'some': ['any'],
- 'uniq': ['unique']
+ 'uniq': ['unique'],
+ 'zipObject': ['object']
};
/** Used to track function dependencies */
var dependencyMap = {
'after': [],
- 'assign': ['isArray', 'forEach', 'forOwn'],
+ 'assign': ['isArray', 'keys'],
'at': ['isString'],
'bind': ['isFunction', 'isObject'],
'bindAll': ['bind', 'functions'],
@@ -76,23 +87,26 @@
'compact': [],
'compose': [],
'contains': ['indexOf', 'isString'],
- 'countBy': ['forEach', 'identity', 'isEqual', 'keys'],
+ 'countBy': ['createCallback', 'forEach'],
+ 'createCallback': ['identity', 'isEqual', 'keys'],
'debounce': [],
- 'defaults': ['isArray', 'forEach', 'forOwn'],
+ 'defaults': ['isArray', 'keys'],
'defer': ['bind'],
'delay': [],
'difference': ['indexOf'],
'escape': [],
- 'every': ['identity', 'isArray', 'isEqual', 'keys'],
- 'filter': ['identity', 'isArray', 'isEqual', 'keys'],
- 'find': ['forEach', 'identity', 'isEqual', 'keys'],
+ 'every': ['createCallback', 'isArray'],
+ 'filter': ['createCallback', 'isArray'],
+ 'find': ['createCallback', 'forEach', 'isArray'],
+ 'findIndex': ['createCallback'],
+ 'findKey': ['createCallback'],
'first': [],
- 'flatten': ['isArray'],
- 'forEach': ['identity', 'isArguments', 'isArray', 'isString'],
- 'forIn': ['identity', 'isArguments'],
- 'forOwn': ['identity', 'isArguments'],
+ 'flatten': ['createCallback', 'isArray'],
+ 'forEach': ['createCallback', 'isArguments', 'isArray', 'isString', 'keys'],
+ 'forIn': ['createCallback', 'isArguments'],
+ 'forOwn': ['createCallback', 'isArguments', 'keys'],
'functions': ['forIn', 'isFunction'],
- 'groupBy': ['forEach', 'identity', 'isEqual', 'keys'],
+ 'groupBy': ['createCallback', 'forEach'],
'has': [],
'identity': [],
'indexOf': ['sortedIndex'],
@@ -120,51 +134,53 @@
'keys': ['forOwn', 'isArguments', 'isObject'],
'last': [],
'lastIndexOf': [],
- 'map': ['identity', 'isArray', 'isEqual', 'keys'],
- 'max': ['isArray', 'isEqual', 'isString', 'keys'],
+ 'map': ['createCallback', 'isArray'],
+ 'max': ['createCallback', 'isArray', 'isString'],
'memoize': [],
'merge': ['forEach', 'forOwn', 'isArray', 'isObject', 'isPlainObject'],
- 'min': ['isArray', 'isEqual', 'isString', 'keys'],
- 'mixin': ['forEach', 'forOwn', 'functions'],
+ 'min': ['createCallback', 'isArray', 'isString'],
+ 'mixin': ['forEach', 'functions'],
'noConflict': [],
- 'object': [],
'omit': ['forIn', 'indexOf'],
'once': [],
'pairs': ['keys'],
+ 'parseInt': ['isString'],
'partial': ['isFunction', 'isObject'],
'partialRight': ['isFunction', 'isObject'],
'pick': ['forIn', 'isObject'],
'pluck': ['map'],
'random': [],
'range': [],
- 'reduce': ['identity', 'isArray', 'isEqual', 'keys'],
- 'reduceRight': ['forEach', 'identity', 'isEqual', 'isString', 'keys'],
- 'reject': ['filter', 'identity', 'isEqual', 'keys'],
+ 'reduce': ['createCallback', 'isArray'],
+ 'reduceRight': ['createCallback', 'forEach', 'isString', 'keys'],
+ 'reject': ['createCallback', 'filter'],
'rest': [],
'result': ['isFunction'],
+ 'runInContext': ['defaults', 'pick'],
'shuffle': ['forEach'],
'size': ['keys'],
- 'some': ['identity', 'isArray', 'isEqual', 'keys'],
- 'sortBy': ['forEach', 'identity', 'isEqual', 'keys'],
- 'sortedIndex': ['identity', 'isEqual', 'keys'],
- 'tap': ['mixin'],
+ 'some': ['createCallback', 'isArray'],
+ 'sortBy': ['createCallback', 'forEach'],
+ 'sortedIndex': ['createCallback', 'identity'],
+ 'tap': ['value'],
'template': ['defaults', 'escape', 'keys', 'values'],
'throttle': [],
- 'times': [],
+ 'times': ['createCallback'],
'toArray': ['isString', 'values'],
'unescape': [],
'union': ['uniq'],
- 'uniq': ['indexOf', 'isEqual', 'keys'],
+ 'uniq': ['createCallback', 'indexOf'],
'uniqueId': [],
- 'value': ['mixin'],
+ 'value': ['forOwn', 'isArray'],
'values': ['keys'],
'where': ['filter'],
'without': ['indexOf'],
'wrap': [],
'zip': ['max', 'pluck'],
+ 'zipObject': [],
// method used by the `backbone` and `underscore` builds
- 'chain': ['mixin'],
+ 'chain': ['value'],
'findWhere': ['where']
};
@@ -174,15 +190,12 @@
'arrays',
'bottom',
'firstArg',
- 'hasDontEnumBug',
- 'hasEnumPrototype',
- 'isKeysFast',
+ 'init',
'loop',
- 'nonEnumArgs',
- 'noCharByIndex',
- 'shadowed',
+ 'shadowedProps',
'top',
- 'useHas'
+ 'useHas',
+ 'useKeys'
];
/** List of all Lo-Dash methods */
@@ -208,6 +221,7 @@
'has',
'indexOf',
'initial',
+ 'invert',
'invoke',
'isArray',
'isEmpty',
@@ -223,7 +237,9 @@
'max',
'min',
'mixin',
+ 'omit',
'once',
+ 'pairs',
'pick',
'reduce',
'reduceRight',
@@ -238,6 +254,7 @@
'toArray',
'uniqueId',
'value',
+ 'values',
'without'
];
@@ -246,11 +263,16 @@
'at',
'bindKey',
'cloneDeep',
+ 'createCallback',
+ 'findIndex',
+ 'findKey',
'forIn',
'forOwn',
'isPlainObject',
'merge',
- 'partialRight'
+ 'parseInt',
+ 'partialRight',
+ 'runInContext'
]));
/** List of ways to export the `lodash` function */
@@ -261,6 +283,9 @@
'node'
];
+ /** Add `path.sep` for older versions of Node.js */
+ path.sep || (path.sep = process.platform == 'win32' ? '\\' : '/');
+
/*--------------------------------------------------------------------------*/
/**
@@ -273,66 +298,68 @@
function addChainMethods(source) {
// add `_.chain`
source = source.replace(matchFunction(source, 'tap'), function(match) {
- return [
+ var indent = getIndent(match);
+ return match && (indent + [
'',
- ' /**',
- ' * Creates a `lodash` object that wraps the given `value`.',
- ' *',
- ' * @static',
- ' * @memberOf _',
- ' * @category Chaining',
- ' * @param {Mixed} value The value to wrap.',
- ' * @returns {Object} Returns the wrapper object.',
- ' * @example',
- ' *',
- ' * var stooges = [',
- " * { 'name': 'moe', 'age': 40 },",
- " * { 'name': 'larry', 'age': 50 },",
- " * { 'name': 'curly', 'age': 60 }",
- ' * ];',
- ' *',
- ' * var youngest = _.chain(stooges)',
- ' * .sortBy(function(stooge) { return stooge.age; })',
- " * .map(function(stooge) { return stooge.name + ' is ' + stooge.age; })",
- ' * .first();',
- " * // => 'moe is 40'",
- ' */',
- ' function chain(value) {',
- ' value = new lodash(value);',
- ' value.__chain__ = true;',
- ' return value;',
- ' }',
+ '/**',
+ ' * Creates a `lodash` object that wraps the given `value`.',
+ ' *',
+ ' * @static',
+ ' * @memberOf _',
+ ' * @category Chaining',
+ ' * @param {Mixed} value The value to wrap.',
+ ' * @returns {Object} Returns the wrapper object.',
+ ' * @example',
+ ' *',
+ ' * var stooges = [',
+ " * { 'name': 'moe', 'age': 40 },",
+ " * { 'name': 'larry', 'age': 50 },",
+ " * { 'name': 'curly', 'age': 60 }",
+ ' * ];',
+ ' *',
+ ' * var youngest = _.chain(stooges)',
+ ' * .sortBy(function(stooge) { return stooge.age; })',
+ " * .map(function(stooge) { return stooge.name + ' is ' + stooge.age; })",
+ ' * .first();',
+ " * // => 'moe is 40'",
+ ' */',
+ 'function chain(value) {',
+ ' value = new lodashWrapper(value);',
+ ' value.__chain__ = true;',
+ ' return value;',
+ '}',
'',
match
- ].join('\n');
+ ].join('\n' + indent));
});
// add `wrapperChain`
source = source.replace(matchFunction(source, 'wrapperToString'), function(match) {
- return [
+ var indent = getIndent(match);
+ return match && (indent + [
'',
- ' /**',
- ' * Enables method chaining on the wrapper object.',
- ' *',
- ' * @name chain',
- ' * @memberOf _',
- ' * @category Chaining',
- ' * @returns {Mixed} Returns the wrapper object.',
- ' * @example',
- ' *',
- ' * var sum = _([1, 2, 3])',
- ' * .chain()',
- ' * .reduce(function(sum, num) { return sum + num; })',
- ' * .value()',
- ' * // => 6`',
- ' */',
- ' function wrapperChain() {',
- ' this.__chain__ = true;',
- ' return this;',
- ' }',
+ '/**',
+ ' * Enables method chaining on the wrapper object.',
+ ' *',
+ ' * @name chain',
+ ' * @memberOf _',
+ ' * @category Chaining',
+ ' * @returns {Mixed} Returns the wrapper object.',
+ ' * @example',
+ ' *',
+ ' * var sum = _([1, 2, 3])',
+ ' * .chain()',
+ ' * .reduce(function(sum, num) { return sum + num; })',
+ ' * .value()',
+ ' * // => 6`',
+ ' */',
+ 'function wrapperChain() {',
+ ' this.__chain__ = true;',
+ ' return this;',
+ '}',
'',
match
- ].join('\n');
+ ].join('\n' + indent));
});
// add `lodash.chain` assignment
@@ -350,66 +377,72 @@
source = source.replace(/(?:\s*\/\/.*)*\n( *)forOwn\(lodash, *function\(func, *methodName\)[\s\S]+?\n\1}.+/g, '');
// move `mixin(lodash)` to after the method assignments
- source = source.replace(/(?:\s*\/\/.*)*\s*mixin\(lodash\).+/, '');
+ source = source.replace(/(?:\s*\/\/.*)*\n( *)mixin\(lodash\).+/, '');
source = source.replace(getMethodAssignments(source), function(match) {
+ var indent = /^ *(?=lodash\.)/m.exec(match)[0];
return match + [
'',
'',
- ' // add functions to `lodash.prototype`',
- ' mixin(lodash);'
- ].join('\n');
+ '// add functions to `lodash.prototype`',
+ 'mixin(lodash);'
+ ].join('\n' + indent);
});
- // add `__chain__` checks to `_.mixin`
- source = source.replace(matchFunction(source, 'mixin'), function(match) {
- return match.replace(/^( *)return new lodash.+/m, function() {
- var indent = arguments[1];
- return indent + [
- '',
- 'var result = func.apply(lodash, args);',
- 'if (this.__chain__) {',
- ' result = new lodash(result);',
- ' result.__chain__ = true;',
- '}',
- 'return result;'
- ].join('\n' + indent);
- });
- });
+ // replace `_.mixin`
+ source = replaceFunction(source, 'mixin', [
+ 'function mixin(object) {',
+ ' forEach(functions(object), function(methodName) {',
+ ' var func = lodash[methodName] = object[methodName];',
+ '',
+ ' lodash.prototype[methodName] = function() {',
+ ' var args = [this.__wrapped__];',
+ ' push.apply(args, arguments);',
+ '',
+ ' var result = func.apply(lodash, args);',
+ ' if (this.__chain__) {',
+ ' result = new lodashWrapper(result);',
+ ' result.__chain__ = true;',
+ ' }',
+ ' return result;',
+ ' };',
+ ' });',
+ '}'
+ ].join('\n'));
// replace wrapper `Array` method assignments
- source = source.replace(/^(?: *\/\/.*\n)*( *)each\(\['[\s\S]+?\n\1}$/m, function() {
- return [
- ' // add `Array` mutator functions to the wrapper',
- " each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {",
- ' var func = arrayRef[methodName];',
- ' lodash.prototype[methodName] = function() {',
- ' var value = this.__wrapped__;',
- ' func.apply(value, arguments);',
+ source = source.replace(/^(?: *\/\/.*\n)*( *)each\(\['[\s\S]+?\n\1}$/m, function(match, indent) {
+ return indent + [
+ '// add `Array` mutator functions to the wrapper',
+ "each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {",
+ ' var func = arrayRef[methodName];',
+ ' lodash.prototype[methodName] = function() {',
+ ' var value = this.__wrapped__;',
+ ' func.apply(value, arguments);',
'',
- ' // avoid array-like object bugs with `Array#shift` and `Array#splice`',
- ' // in Firefox < 10 and IE < 9',
- ' if (hasObjectSpliceBug && value.length === 0) {',
- ' delete value[0];',
- ' }',
- ' return this;',
- ' };',
- ' });',
+ ' // avoid array-like object bugs with `Array#shift` and `Array#splice`',
+ ' // in Firefox < 10 and IE < 9',
+ ' if (!support.spliceObjects && value.length === 0) {',
+ ' delete value[0];',
+ ' }',
+ ' return this;',
+ ' };',
+ '});',
'',
- ' // add `Array` accessor functions to the wrapper',
- " each(['concat', 'join', 'slice'], function(methodName) {",
- ' var func = arrayRef[methodName];',
- ' lodash.prototype[methodName] = function() {',
- ' var value = this.__wrapped__,',
- ' result = func.apply(value, arguments);',
+ '// add `Array` accessor functions to the wrapper',
+ "each(['concat', 'join', 'slice'], function(methodName) {",
+ ' var func = arrayRef[methodName];',
+ ' lodash.prototype[methodName] = function() {',
+ ' var value = this.__wrapped__,',
+ ' result = func.apply(value, arguments);',
'',
- ' if (this.__chain__) {',
- ' result = new lodash(result);',
- ' result.__chain__ = true;',
- ' }',
- ' return result;',
- ' };',
- ' });'
- ].join('\n');
+ ' if (this.__chain__) {',
+ ' result = new lodashWrapper(result);',
+ ' result.__chain__ = true;',
+ ' }',
+ ' return result;',
+ ' };',
+ '});'
+ ].join('\n' + indent);
});
return source;
@@ -426,7 +459,7 @@
function addCommandsToHeader(source, commands) {
return source.replace(/(\/\**\n)( \*)( *@license[\s*]+)( *Lo-Dash [\w.-]+)(.*)/, function() {
// remove `node path/to/build.js` from `commands`
- if (commands[0] == 'node') {
+ if (reNode.test(commands[0])) {
commands.splice(0, 2);
}
// add quotes to commands with spaces or equals signs
@@ -472,7 +505,7 @@
var source = [
';(function(window) {',
- " var freeExports = typeof exports == 'object' && exports;",
+ " var freeExports = typeof exports == 'object' && typeof require == 'function' && exports;",
'',
" var freeModule = typeof module == 'object' && module && module.exports == freeExports && module;",
'',
@@ -510,7 +543,8 @@
' _ = lodash;',
' lodash.templates = lodash.extend(lodash.templates || {}, templates);',
' });',
- " } else if (freeExports) {",
+ " } else if (freeExports && !freeExports.nodeType) {",
+ " _ = require('" + options.moduleId + "');",
" if (freeModule) {",
' (freeModule.exports = templates).templates = templates;',
' } else {',
@@ -547,6 +581,8 @@
return source
// remove pseudo private properties
.replace(/(?:(?:\s*\/\/.*)*\s*lodash\._[^=]+=.+\n)+/g, '\n')
+ // remove extraneous whitespace
+ .replace(/^ *\n/gm, '\n')
// remove lines with just whitespace and semicolons
.replace(/^ *;\n/gm, '')
// consolidate multiple newlines
@@ -582,7 +618,7 @@
' lodash exports=... Comma separated names of ways to export the `lodash` function',
' (i.e. “amd”, “commonjs”, “global”, “node”, and “none”)',
' lodash iife=... Code to replace the immediately-invoked function expression that wraps Lo-Dash',
- ' (e.g. `lodash iife="!function(window,undefined){%output%}(this)"`)',
+ ' (e.g. `lodash iife="!function(window){%output%}(this)"`)',
'',
' lodash template=... File path pattern used to match template files to precompile',
' (e.g. `lodash template=./*.jst`)',
@@ -680,17 +716,23 @@
* @private
* @param {Array|String} methodName A single method name or array of
* dependencies to query.
+ * @param- {Object} [stackA=[]] Internally used track queried methods.
* @returns {Array} Returns an array of method dependencies.
*/
- function getDependencies(methodName) {
+ function getDependencies(methodName, stack) {
var dependencies = Array.isArray(methodName) ? methodName : dependencyMap[methodName];
if (!dependencies) {
return [];
}
+ stack || (stack = []);
+
// recursively accumulate the dependencies of the `methodName` function, and
// the dependencies of its dependencies, and so on
return _.uniq(dependencies.reduce(function(result, otherName) {
- result.push.apply(result, getDependencies(otherName).concat(otherName));
+ if (!_.contains(stack, otherName)) {
+ stack.push(otherName);
+ result.push.apply(result, getDependencies(otherName, stack).concat(otherName));
+ }
return result;
}, []));
}
@@ -700,20 +742,35 @@
*
* @private
* @param {Function} func The function to process.
+ * @param {String} indent The function indent.
* @returns {String} Returns the formatted source.
*/
- function getFunctionSource(func) {
+ function getFunctionSource(func, indent) {
var source = func.source || (func + '');
-
+ if (indent == null) {
+ indent = ' ';
+ }
// format leading whitespace
return source.replace(/\n(?:.*)/g, function(match, index) {
match = match.slice(1);
return (
- match == '}' && source.indexOf('}', index + 2) < 0 ? '\n ' : '\n '
+ '\n' + indent +
+ (match == '}' && !_.contains(source, '}', index + 2) ? '' : ' ')
) + match;
});
}
+ /**
+ * Gets the indent of the given function.
+ *
+ * @private
+ * @param {Function} func The function to process.
+ * @returns {String} Returns the indent.
+ */
+ function getIndent(func) {
+ return /^ *(?=\S)/m.exec(func.source || func)[0];
+ }
+
/**
* Gets the `_.isArguments` fallback from `source`.
*
@@ -722,7 +779,7 @@
* @returns {String} Returns the `isArguments` fallback.
*/
function getIsArgumentsFallback(source) {
- return (source.match(/(?:\s*\/\/.*)*\n( *)if *\((?:noArgsClass|!isArguments)[\s\S]+?};\n\1}/) || [''])[0];
+ return (source.match(/(?:\s*\/\/.*)*\n( *)if *\((?:!support\.argsClass|!isArguments)[\s\S]+?};\n\1}/) || [''])[0];
}
/**
@@ -793,7 +850,10 @@
*/
function isRemoved(source) {
return slice.call(arguments, 1).every(function(funcName) {
- return !matchFunction(source, funcName);
+ return !(
+ matchFunction(source, funcName) ||
+ RegExp('^ *lodash\\.prototype\\.' + funcName + ' *=.+', 'm').test(source)
+ );
});
}
@@ -808,8 +868,8 @@
*/
function matchFunction(source, funcName) {
var result = source.match(RegExp(
- // match multi-line comment block (could be on a single line)
- '(?:\\n +/\\*[^*]*\\*+(?:[^/][^*]*\\*+)*/\\n)?' +
+ // match multi-line comment block
+ '(?:\\n +/\\*[^*]*\\*+(?:[^/][^*]*\\*+)*/)?\\n' +
// begin non-capturing group
'( *)(?:' +
// match a function declaration
@@ -839,7 +899,10 @@
* @returns {Array} Returns the new converted array.
*/
function optionToArray(value) {
- return value.match(/\w+=(.*)$/)[1].split(/, */);
+ return _.compact(_.isArray(value)
+ ? value
+ : value.match(/\w+=(.*)$/)[1].split(/, */)
+ );
}
/**
@@ -854,11 +917,6 @@
function optionToMethodsArray(source, value) {
var methodNames = optionToArray(value);
- // convert categories to method names
- methodNames.forEach(function(category) {
- push.apply(methodNames, getMethodsByCategory(source, category));
- });
-
// convert aliases to real method names
methodNames = methodNames.map(getRealName);
@@ -867,53 +925,39 @@
}
/**
- * Removes all `argsAreObjects` references from `source`.
+ * Removes the all references to `varName` from `createIterator` in `source`.
*
* @private
* @param {String} source The source to process.
+ * @param {String} identifier The name of the variable or property to remove.
* @returns {String} Returns the modified source.
*/
- function removeArgsAreObjects(source) {
- source = removeVar(source, 'argsAreObjects');
+ function removeFromCreateIterator(source, identifier) {
+ var snippet = matchFunction(source, 'createIterator');
+ if (!snippet) {
+ return source;
+ }
+ // remove data object property assignment
+ var modified = snippet
+ .replace(RegExp("^(?: *\\/\\/.*\\n)* *'" + identifier + "':.+\\n+", 'm'), '')
+ .replace(/,(?=\s*})/, '');
- // remove `argsAreObjects` from `_.isArray`
- source = source.replace(matchFunction(source, 'isArray'), function(match) {
- return match.replace(/\(argsAreObjects && *([^)]+)\)/g, '$1');
+ source = source.replace(snippet, function() {
+ return modified;
});
- // remove `argsAreObjects` from `_.isEqual`
- source = source.replace(matchFunction(source, 'isEqual'), function(match) {
- return match.replace(/!argsAreObjects[^:]+:\s*/g, '');
- });
+ // clip at the `factory` assignment
+ snippet = modified.match(/Function\([\s\S]+$/)[0];
- return source;
- }
+ modified = snippet
+ .replace(RegExp('\\b' + identifier + '\\b,? *', 'g'), '')
+ .replace(/, *(?=',)/, '')
+ .replace(/,(?=\s*\))/, '')
+
+ source = source.replace(snippet, function() {
+ return modified;
+ });
- /**
- * Removes the all references to `varName` from `createIterator` in `source`.
- *
- * @private
- * @param {String} source The source to process.
- * @param {String} varName The name of the variable to remove.
- * @returns {String} Returns the modified source.
- */
- function removeFromCreateIterator(source, varName) {
- var snippet = matchFunction(source, 'createIterator');
- if ( snippet) {
- // remove data object property assignment
- var modified = snippet.replace(RegExp("^ *'" + varName + "': *" + varName + '.+\\n', 'm'), '');
- source = source.replace(snippet, modified);
-
- // clip at the `factory` assignment
- snippet = modified.match(/Function\([\s\S]+$/)[0];
-
- modified = snippet
- .replace(RegExp('\\b' + varName + '\\b,? *', 'g'), '')
- .replace(/, *',/, "',")
- .replace(/,\s*\)/, ')')
-
- source = source.replace(snippet, modified);
- }
return source;
}
@@ -927,278 +971,379 @@
* @returns {String} Returns the modified source.
*/
function removeFunction(source, funcName) {
+ var snippet;
+
// remove function
- var snippet = matchFunction(source, funcName);
- if (snippet) {
+ if (funcName == 'runInContext') {
+ source = removeRunInContext(source, funcName);
+ } else if (funcName != 'each' && (snippet = matchFunction(source, funcName))) {
source = source.replace(snippet, '');
}
// grab the method assignments snippet
snippet = getMethodAssignments(source);
+ // remove method assignment from `lodash.prototype`
+ source = source.replace(RegExp('^ *lodash\\.prototype\\.' + funcName + ' *=.+\\n', 'm'), '');
+
// remove assignment and aliases
var modified = getAliases(funcName).concat(funcName).reduce(function(result, otherName) {
- return result.replace(RegExp('(?:\\n *//.*\\s*)* *lodash\\.' + otherName + ' *= *.+\\n'), '');
+ return result.replace(RegExp('^(?: *//.*\\s*)* *lodash\\.' + otherName + ' *=.+\\n', 'm'), '');
}, snippet);
// replace with the modified snippet
- source = source.replace(snippet, modified);
+ source = source.replace(snippet, function() {
+ return modified;
+ });
return removeFromCreateIterator(source, funcName);
}
/**
- * Removes all `hasDontEnumBug` references from `source`.
+ * Removes the `_.isArguments` fallback from `source`.
*
* @private
* @param {String} source The source to process.
* @returns {String} Returns the modified source.
*/
- function removeHasDontEnumBug(source) {
- source = removeFromCreateIterator(source, 'hasDontEnumBug');
- source = removeFromCreateIterator(source, 'shadowed');
+ function removeIsArgumentsFallback(source) {
+ return source.replace(getIsArgumentsFallback(source), '');
+ }
- // remove `hasDontEnumBug` declaration and assignment
- source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var hasDontEnumBug\b.*|.+?hasDontEnumBug *=.+/g, '');
+ /**
+ * Removes the `_.isFunction` fallback from `source`.
+ *
+ * @private
+ * @param {String} source The source to process.
+ * @returns {String} Returns the modified source.
+ */
+ function removeIsFunctionFallback(source) {
+ return source.replace(getIsFunctionFallback(source), '');
+ }
- // remove `shadowed` variable
- source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var shadowed[\s\S]+?;\n/, '');
+ /**
+ * Removes the `Object.keys` object iteration optimization from `source`.
+ *
+ * @private
+ * @param {String} source The source to process.
+ * @returns {String} Returns the modified source.
+ */
+ function removeKeysOptimization(source) {
+ source = removeFromCreateIterator(source, 'useKeys');
- // remove `hasDontEnumBug` from `iteratorTemplate`
+ // remove optimized branch in `iteratorTemplate`
source = source.replace(getIteratorTemplate(source), function(match) {
- return match.replace(/(?: *\/\/.*\n)* *["']( *)<% *if *\(hasDontEnumBug[\s\S]+?["']\1<% *} *%>.+/, '');
+ return match.replace(/^(?: *\/\/.*\n)* *["']( *)<% *if *\(useHas *&& *useKeys[\s\S]+?["']\1<% *} *else *{ *%>.+\n([\s\S]+?) *["']\1<% *} *%>.+/m, "'\\n' +\n$2");
});
return source;
}
/**
- * Removes all `hasEnumPrototype` references from `source`.
+ * Removes all `lodashWrapper` references from `source`.
*
* @private
* @param {String} source The source to process.
* @returns {String} Returns the modified source.
*/
- function removeHasEnumPrototype(source) {
- source = removeFromCreateIterator(source, 'hasEnumPrototype');
+ function removeLodashWrapper(source) {
+ source = removeFunction(source, 'lodashWrapper');
- // remove `hasEnumPrototype` declaration and assignment
- source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var hasEnumPrototype\b.*|.+?hasEnumPrototype *=.+/g, '');
+ // remove `lodashWrapper.prototype` assignment
+ source = source.replace(/(?:\s*\/\/.*)*\n *lodashWrapper\.prototype *=.+/, '');
- // remove `hasEnumPrototype` from `_.keys`
- source = source.replace(matchFunction(source, 'keys'), function(match) {
- return match
- .replace(/\(hasEnumPrototype[^)]+\)(?:\s*\|\|\s*)?/, '')
- .replace(/\s*if *\(\s*\)[^}]+}/, '');
+ // replace `new lodashWrapper` with `new lodash`
+ source = source.replace(/\bnew lodashWrapper\b/g, 'new lodash');
+
+ return source;
+ }
+
+ /**
+ * Removes all `support.argsObject` references from `source`.
+ *
+ * @private
+ * @param {String} source The source to process.
+ * @returns {String} Returns the modified source.
+ */
+ function removeSupportArgsObject(source) {
+ source = removeSupportProp(source, 'argsObject');
+
+ // remove `argsAreObjects` from `_.isArray`
+ source = source.replace(matchFunction(source, 'isArray'), function(match) {
+ return match.replace(/\(support\.argsObject && *([^)]+)\)/g, '$1');
});
- // remove `hasEnumPrototype` from `iteratorTemplate`
- source = source.replace(getIteratorTemplate(source), function(match) {
- return match
- .replace(/(?: *\/\/.*\n)* *["'] *(?:<% *)?if *\(hasEnumPrototype *(?:&&|\))[\s\S]+?<% *} *(?:%>|["']).+/g, '')
- .replace(/hasEnumPrototype *\|\|\s*/g, '');
+ // remove `argsAreObjects` from `_.isEqual`
+ source = source.replace(matchFunction(source, 'isEqual'), function(match) {
+ return match.replace(/!support.\argsObject[^:]+:\s*/g, '');
});
return source;
}
/**
- * Removes the `_.isArguments` fallback from `source`.
+ * Removes all `support.argsClass` references from `source`.
*
* @private
* @param {String} source The source to process.
* @returns {String} Returns the modified source.
*/
- function removeIsArgumentsFallback(source) {
- return source.replace(getIsArgumentsFallback(source), '');
+ function removeSupportArgsClass(source) {
+ source = removeSupportProp(source, 'argsClass');
+
+ // replace `support.argsClass` in the `_.isArguments` fallback
+ source = source.replace(getIsArgumentsFallback(source), function(match) {
+ return match.replace(/!support\.argsClass/g, '!isArguments(arguments)');
+ });
+
+ // remove `support.argsClass` from `_.isEmpty`
+ source = source.replace(matchFunction(source, 'isEmpty'), function(match) {
+ return match.replace(/\s*\(support\.argsClass\s*\?([^:]+):.+?\)\)/g, '$1');
+ });
+
+ // remove `support.argsClass` from `_.isPlainObject`
+ _.each(['shimIsPlainObject', 'isPlainObject'], function(methodName) {
+ source = source.replace(matchFunction(source, methodName), function(match) {
+ return match.replace(/\s*\|\|\s*\(!support\.argsClass[\s\S]+?\)\)/, '');
+ });
+ });
+
+ return source;
}
/**
- * Removes the `_.isFunction` fallback from `source`.
+ * Removes all `support.enumPrototypes` references from `source`.
*
* @private
* @param {String} source The source to process.
* @returns {String} Returns the modified source.
*/
- function removeIsFunctionFallback(source) {
- return source.replace(getIsFunctionFallback(source), '');
+ function removeSupportEnumPrototypes(source) {
+ source = removeSupportProp(source, 'enumPrototypes');
+
+ // remove `support.enumPrototypes` from `_.keys`
+ source = source.replace(matchFunction(source, 'keys'), function(match) {
+ return match
+ .replace(/\(support\.enumPrototypes[^)]+\)(?:\s*\|\|\s*)?/, '')
+ .replace(/\s*if *\(\s*\)[^}]+}/, '');
+ });
+
+ // remove `support.enumPrototypes` from `iteratorTemplate`
+ source = source.replace(getIteratorTemplate(source), function(match) {
+ return match
+ .replace(/(?: *\/\/.*\n)* *["'] *(?:<% *)?if *\(support\.enumPrototypes *(?:&&|\))[\s\S]+?<% *} *(?:%>|["']).+/g, '')
+ .replace(/support\.enumPrototypes\s*\|\|\s*/g, '');
+ });
+
+ return source;
}
/**
- * Removes all `iteratesOwnLast` references from `source`.
+ * Removes all `support.nodeClass` references from `source`.
*
* @private
* @param {String} source The source to process.
* @returns {String} Returns the modified source.
*/
- function removeIteratesOwnLast(source) {
- // remove `iteratesOwnLast` declaration and assignment
- source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var iteratesOwnLast\b.*|.+?iteratesOwnLast *=.+/g, '');
+ function removeSupportNodeClass(source) {
+ source = removeFunction(source, 'isNode');
+ source = removeSupportProp(source, 'nodeClass');
- // remove `iteratesOwnLast` from `shimIsPlainObject`
+ // remove `support.nodeClass` from `shimIsPlainObject`
source = source.replace(matchFunction(source, 'shimIsPlainObject'), function(match) {
- return match.replace(/(?:\s*\/\/.*)*\n( *)if *\(iteratesOwnLast[\s\S]+?\n\1}/, '');
+ return match.replace(/ *&& *\(support\.nodeClass[\s\S]+?\)\)/, '');
+ });
+
+ // remove `support.nodeClass` from `_.clone`
+ source = source.replace(matchFunction(source, 'clone'), function(match) {
+ return match.replace(/\s*\|\|\s*\(!support\.nodeClass[\s\S]+?\)\)/, '');
+ });
+
+ // remove `support.nodeClass` from `_.isEqual`
+ source = source.replace(matchFunction(source, 'isEqual'), function(match) {
+ return match.replace(/\s*\|\|\s*\(!support\.nodeClass[\s\S]+?\)\)\)/, '');
});
return source;
}
/**
- * Removes the `Object.keys` object iteration optimization from `source`.
+ * Removes all `support.nonEnumArgs` references from `source`.
*
* @private
* @param {String} source The source to process.
* @returns {String} Returns the modified source.
*/
- function removeKeysOptimization(source) {
- source = removeVar(source, 'isKeysFast');
+ function removeSupportNonEnumArgs(source) {
+ source = removeSupportProp(source, 'nonEnumArgs');
- // remove optimized branch in `iteratorTemplate`
+ // remove `support.nonEnumArgs` from `_.keys`
+ source = source.replace(matchFunction(source, 'keys'), function(match) {
+ return match
+ .replace(/(?:\s*\|\|\s*)?\(support\.nonEnumArgs[^)]+\)\)/, '')
+ .replace(/\s*if *\(\s*\)[^}]+}/, '');
+ });
+
+ // remove `nonEnumArgs` from `iteratorTemplate`
source = source.replace(getIteratorTemplate(source), function(match) {
- return match.replace(/(?: *\/\/.*\n)* *["']( *)<% *if *\(isKeysFast[\s\S]+?["']\1<% *} *else *{ *%>.+\n([\s\S]+?) *["']\1<% *} *%>.+/, "'\\n' +\n$2");
+ return match
+ .replace(/(?: *\/\/.*\n)*( *["'] *)<% *} *else *if *\(support\.nonEnumArgs[\s\S]+?(\1<% *} *%>.+)/, '$2')
+ .replace(/\s*\|\|\s*support\.nonEnumArgs/, '');
});
return source;
}
/**
- * Removes all `noArgsClass` references from `source`.
+ * Removes all `support.nonEnumShadows` references from `source`.
*
* @private
* @param {String} source The source to process.
* @returns {String} Returns the modified source.
*/
- function removeNoArgsClass(source) {
- source = removeVar(source, 'noArgsClass');
-
- // replace `noArgsClass` in the `_.isArguments` fallback
- source = source.replace(getIsArgumentsFallback(source), function(match) {
- return match.replace(/noArgsClass/g, '!isArguments(arguments)');
- });
+ function removeSupportNonEnumShadows(source) {
+ source = removeSupportProp(source, 'nonEnumShadows');
+ source = removeVar(source, 'shadowedProps');
+ source = removeFromCreateIterator(source, 'shadowedProps');
- // remove `noArgsClass` from `_.isEmpty`
- source = source.replace(matchFunction(source, 'isEmpty'), function(match) {
- return match.replace(/ *\|\|\s*\(noArgsClass *&&[^)]+?\)\)/g, '');
+ // remove `support.nonEnumShadows` from `iteratorTemplate`
+ source = source.replace(getIteratorTemplate(source), function(match) {
+ return match.replace(/(?: *\/\/.*\n)* *["']( *)<% *if *\(support\.nonEnumShadows[\s\S]+?["']\1<% *} *%>.+/, '');
});
return source;
}
/**
- * Removes all `noCharByIndex` references from `source`.
+ * Removes all `support.ownLast` references from `source`.
*
* @private
* @param {String} source The source to process.
* @returns {String} Returns the modified source.
*/
- function removeNoCharByIndex(source) {
- source = removeVar(source, 'noCharByIndex');
+ function removeSupportOwnLast(source) {
+ source = removeSupportProp(source, 'ownLast');
- // remove `noCharByIndex` from `_.at`
- source = source.replace(matchFunction(source, 'at'), function(match) {
- return match.replace(/^ *if *\(noCharByIndex[^}]+}\n/m, '');
+ // remove `support.ownLast` from `shimIsPlainObject`
+ source = source.replace(matchFunction(source, 'shimIsPlainObject'), function(match) {
+ return match.replace(/(?:\s*\/\/.*)*\n( *)if *\(support\.ownLast[\s\S]+?\n\1}/, '');
});
- // remove `noCharByIndex` from `_.reduceRight`
- source = source.replace(matchFunction(source, 'reduceRight'), function(match) {
- return match.replace(/}\s*else if *\(noCharByIndex[^}]+/, '');
- });
+ return source;
+ }
- // remove `noCharByIndex` from `_.toArray`
- source = source.replace(matchFunction(source, 'toArray'), function(match) {
- return match.replace(/noCharByIndex[^:]+:/, '');
- });
+ /**
+ * Removes all `support.spliceObjects` references from `source`.
+ *
+ * @private
+ * @param {String} source The source to process.
+ * @returns {String} Returns the modified source.
+ */
+ function removeSupportSpliceObjects(source) {
+ source = removeSupportProp(source, 'spliceObjects');
- // `noCharByIndex` from `iteratorTemplate`
- source = source.replace(getIteratorTemplate(source), function(match) {
- return match
- .replace(/'if *\(<%= *arrays *%>[^']*/, '$&\\n')
- .replace(/(?: *\/\/.*\n)* *["']( *)<% *if *\(noCharByIndex[\s\S]+?["']\1<% *} *%>.+/, '');
- });
+ // remove `support.spliceObjects` fix from the `Array` function mixins
+ source = source.replace(/(?:\s*\/\/.*)*\n( *)if *\(!support\.spliceObjects[\s\S]+?(?:{\s*}|\n\1})/, '');
return source;
}
/**
- * Removes all `nonEnumArgs` references from `source`.
+ * Removes all `support.unindexedChars` references from `source`.
*
* @private
* @param {String} source The source to process.
* @returns {String} Returns the modified source.
*/
- function removeNonEnumArgs(source) {
- source = removeFromCreateIterator(source, 'nonEnumArgs');
+ function removeSupportUnindexedChars(source) {
+ source = removeSupportProp(source, 'unindexedChars');
- // remove `nonEnumArgs` declaration and assignment
- source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var nonEnumArgs\b.*|.+?nonEnumArgs *=.+/g, '');
+ // remove `support.unindexedChars` from `_.at`
+ source = source.replace(matchFunction(source, 'at'), function(match) {
+ return match.replace(/^ *if *\(support\.unindexedChars[^}]+}\n+/m, '');
+ });
- // remove `nonEnumArgs` from `_.keys`
- source = source.replace(matchFunction(source, 'keys'), function(match) {
- return match
- .replace(/(?:\s*\|\|\s*)?\(nonEnumArgs[^)]+\)\)/, '')
- .replace(/\s*if *\(\s*\)[^}]+}/, '');
+ // remove `support.unindexedChars` from `_.reduceRight`
+ source = source.replace(matchFunction(source, 'reduceRight'), function(match) {
+ return match.replace(/}\s*else if *\(support\.unindexedChars[^}]+/, '');
});
- // remove `nonEnumArgs` from `iteratorTemplate`
+ // remove `support.unindexedChars` from `_.toArray`
+ source = source.replace(matchFunction(source, 'toArray'), function(match) {
+ return match.replace(/(return\b).+?support\.unindexedChars[^:]+:\s*/, '$1 ');
+ });
+
+ // remove `support.unindexedChars` from `iteratorTemplate`
source = source.replace(getIteratorTemplate(source), function(match) {
return match
- .replace(/(?: *\/\/.*\n)*( *["'] *)<% *} *else *if *\(nonEnumArgs[\s\S]+?(\1<% *} *%>.+)/, '$2')
- .replace(/ *\|\|\s*nonEnumArgs/, '');
+ .replace(/'if *\(<%= *arrays *%>[^']*/, '$&\\n')
+ .replace(/(?: *\/\/.*\n)* *["']( *)<% *if *\(support\.unindexedChars[\s\S]+?["']\1<% *} *%>.+/, '');
});
return source;
}
/**
- * Removes all `noNodeClass` references from `source`.
+ * Removes all `runInContext` references from `source`.
*
* @private
* @param {String} source The source to process.
* @returns {String} Returns the modified source.
*/
- function removeNoNodeClass(source) {
- // remove `noNodeClass` assignment
- source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *try *{(?:\s*\/\/.*)*\n *var noNodeClass[\s\S]+?catch[^}]+}\n/, '');
+ function removeRunInContext(source) {
+ source = removeVar(source, 'contextProps');
- // remove `noNodeClass` from `shimIsPlainObject`
- source = source.replace(matchFunction(source, 'shimIsPlainObject'), function(match) {
- return match.replace(/ *&& *\(!noNodeClass[\s\S]+?\)\)/, '');
+ // remove function scaffolding, leaving most of its content
+ source = source.replace(matchFunction(source, 'runInContext'), function(match) {
+ return match
+ .replace(/^[\s\S]+?function runInContext[\s\S]+?context *= *context.+| *return lodash[\s\S]+$/g, '')
+ .replace(/^ {4}/gm, ' ');
});
- // remove `noNodeClass` from `_.clone`
- source = source.replace(matchFunction(source, 'clone'), function(match) {
- return match.replace(/ *\|\|\s*\(noNodeClass[\s\S]+?\)\)/, '');
- });
-
- // remove `noNodeClass` from `_.isEqual`
- source = source.replace(matchFunction(source, 'isEqual'), function(match) {
- return match.replace(/ *\|\|\s*\(noNodeClass[\s\S]+?\)\)\)/, '');
- });
+ // cleanup adjusted source
+ source = source
+ .replace(/\bcontext\b/g, 'window')
+ .replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var Array *=[\s\S]+?;\n/, '')
+ .replace(/(return *|= *)_([;)])/g, '$1lodash$2')
+ .replace(/^ *var _ *=.+\n+/m, '');
return source;
}
/**
- * Removes all `hasObjectSpliceByg` references from `source`.
+ * Removes all `setImmediate` references from `source`.
*
* @private
* @param {String} source The source to process.
* @returns {String} Returns the modified source.
*/
- function removeHasObjectSpliceBug(source) {
- return removeVar(source, 'hasObjectSpliceBug')
- // remove `hasObjectSpliceBug` fix from the `Array` function mixins
- .replace(/(?:\s*\/\/.*)*\n( *)if *\(hasObjectSpliceBug[\s\S]+?(?:{\s*}|\n\1})/, '');
+ function removeSetImmediate(source) {
+ source = removeVar(source, 'setImmediate');
+
+ // remove the `setImmediate` fork of `_.defer`.
+ source = source.replace(/(?:\s*\/\/.*)*\n( *)if *\(isV8 *&& *freeModule[\s\S]+?\n\1}/, '');
+
+ return source;
}
/**
- * Removes the `setImmediate` fork of `_.defer`.
+ * Removes a given property from the `support` object in `source`.
*
* @private
* @param {String} source The source to process.
+ * @param {String} varName The name of the `support` property to remove.
* @returns {String} Returns the modified source.
*/
- function removeSetImmediate(source) {
- return source.replace(/(?:\s*\/\/.*)*\n( *)if *\(isV8 *&& *freeModule[\s\S]+?\n\1}/, '');
+ function removeSupportProp(source, propName) {
+ return source.replace(RegExp(
+ // match multi-line comment block
+ '(?:\\n +/\\*[^*]*\\*+(?:[^/][^*]*\\*+)*/)?\\n' +
+ // match a `try` block
+ '(?: *try\\b.+\\n)?' +
+ // match the `support` property assignment
+ ' *support\\.' + propName + ' *=.+\\n' +
+ // match `catch` block
+ '(?:( *).+?catch\\b[\\s\\S]+?\\n\\1}\\n)?'
+ ), '');
}
/**
@@ -1210,17 +1355,21 @@
* @returns {String} Returns the modified source.
*/
function removeVar(source, varName) {
- // simplify `cloneableClasses`, `ctorByClass`, or `hasObjectSpliceBug`
- if (/^(?:cloneableClasses|ctorByClass|hasObjectSpliceBug)$/.test(varName)) {
+ // simplify complex variable assignments
+ if (/^(?:cloneableClasses|contextProps|ctorByClass|shadowedProps)$/.test(varName)) {
source = source.replace(RegExp('(var ' + varName + ' *=)[\\s\\S]+?\\n\\n'), '$1=null;\n\n');
}
+
+ source = removeFunction(source, varName);
+
source = source.replace(RegExp(
// match multi-line comment block
'(?:\\n +/\\*[^*]*\\*+(?:[^/][^*]*\\*+)*/)?\\n' +
// match a variable declaration that's not part of a declaration list
'( *)var ' + varName + ' *= *(?:.+?(?:;|&&\\n[^;]+;)|(?:\\w+\\(|{)[\\s\\S]+?\\n\\1.+?;)\\n|' +
// match a variable in a declaration list
- '\\n +' + varName + ' *=.+?,'
+ '^ *' + varName + ' *=.+?,\\n',
+ 'm'
), '');
// remove a varaible at the start of a variable declaration list
@@ -1229,29 +1378,56 @@
// remove a variable at the end of a variable declaration list
source = source.replace(RegExp(',\\s*' + varName + ' *=.+?;'), ';');
- return removeFromCreateIterator(source, varName);
+ return source;
}
/**
* Replaces the `funcName` function body in `source` with `funcValue`.
*
* @private
- * @param {String} source The source to inspect.
+ * @param {String} source The source to process.
* @param {String} varName The name of the function to replace.
* @returns {String} Returns the modified source.
*/
function replaceFunction(source, funcName, funcValue) {
- var match = matchFunction(source, funcName);
- if (match) {
- // clip snippet after the JSDoc comment block
- match = match.replace(/^\s*(?:\/\/.*|\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)\n/, '');
- source = source.replace(match, function() {
- return funcValue.trimRight() + '\n';
- });
+ var snippet = matchFunction(source, funcName);
+ if (!snippet) {
+ return source;
}
+ // clip snippet after the JSDoc comment block
+ snippet = snippet.replace(/^\s*(?:\/\/.*|\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)\n/, '');
+
+ source = source.replace(snippet, function() {
+ return funcValue
+ .replace(RegExp('^' + getIndent(funcValue), 'gm'), getIndent(snippet))
+ .trimRight() + '\n';
+ });
+
return source;
}
+
+ /**
+ * Replaces the `support` object `propName` property value in `source` with `propValue`.
+ *
+ * @private
+ * @param {String} source The source to process.
+ * @param {String} varName The name of the `support` property to replace.
+ * @returns {String} Returns the modified source.
+ */
+ function replaceSupportProp(source, propName, propValue) {
+ return source.replace(RegExp(
+ // match a `try` block
+ '(?: *try\\b.+\\n)?' +
+ // match the `support` property assignment
+ '( *support\\.' + propName + ' *=).+\\n' +
+ // match `catch` block
+ '(?:( *).+?catch\\b[\\s\\S]+?\\n\\2}\\n)?'
+ ), function(match, left) {
+ return left + ' ' + propValue + ';\n';
+ });
+ }
+
/**
* Replaces the `varName` variable declaration value in `source` with `varValue`.
*
@@ -1263,22 +1439,22 @@
function replaceVar(source, varName, varValue) {
// replace a variable that's not part of a declaration list
var result = source.replace(RegExp(
- '(( *)var ' + varName + ' *= *)' +
+ '(( *)var ' + varName + ' *=)' +
'(?:.+?;|(?:Function\\(.+?|.*?[^,])\\n[\\s\\S]+?\\n\\2.+?;)\\n'
- ), function(match, captured) {
- return captured + varValue + ';\n';
+ ), function(match, left) {
+ return left + ' ' + varValue + ';\n';
});
if (source == result) {
// replace a varaible at the start or middle of a declaration list
- result = source.replace(RegExp('((?:var|\\n) +' + varName + ' *=).+?,'), function(match, captured) {
- return captured + ' ' + varValue + ',';
+ result = source.replace(RegExp('((?:var|\\n) +' + varName + ' *=).+?,'), function(match, left) {
+ return left + ' ' + varValue + ',';
});
}
if (source == result) {
// replace a variable at the end of a variable declaration list
- result = source.replace(RegExp('(,\\s*' + varName + ' *=).+?;'), function(match, captured) {
- return captured + ' ' + varValue + ';';
+ result = source.replace(RegExp('(,\\s*' + varName + ' *=).+?;'), function(match, left) {
+ return left + ' ' + varValue + ';';
});
}
return result;
@@ -1325,12 +1501,12 @@
var sourceMapURL;
// used to report invalid command-line arguments
- var invalidArgs = _.reject(options.slice(options[0] == 'node' ? 2 : 0), function(value, index, options) {
+ var invalidArgs = _.reject(options.slice(reNode.test(options[0]) ? 2 : 0), function(value, index, options) {
if (/^(?:-o|--output)$/.test(options[index - 1]) ||
/^(?:category|exclude|exports|iife|include|moduleId|minus|plus|settings|template)=.*$/.test(value)) {
return true;
}
- var result = [
+ var result = _.contains([
'backbone',
'csp',
'legacy',
@@ -1347,7 +1523,7 @@
'-p', '--source-map',
'-s', '--silent',
'-V', '--version'
- ].indexOf(value) > -1;
+ ], value);
if (!result && /^(?:-p|--source-map)$/.test(options[index - 1])) {
result = true;
@@ -1398,52 +1574,47 @@
var filePath = path.join(__dirname, 'lodash.js');
// flag to specify a Backbone build
- var isBackbone = options.indexOf('backbone') > -1;
+ var isBackbone = _.contains(options, 'backbone');
// flag to specify a Content Security Policy build
- var isCSP = options.indexOf('csp') > -1 || options.indexOf('CSP') > -1;
+ var isCSP = _.contains(options, 'csp') || _.contains(options, 'CSP');
// flag to specify only creating the debug build
- var isDebug = options.indexOf('-d') > -1 || options.indexOf('--debug') > -1;
+ var isDebug = _.contains(options, '-d') || _.contains(options, '--debug');
// flag to indicate that a custom IIFE was specified
var isIIFE = typeof iife == 'string';
// flag to specify creating a source map for the minified source
- var isMapped = options.indexOf('-p') > -1 || options.indexOf('--source-map') > -1;
+ var isMapped = _.contains(options, '-p') || _.contains(options, '--source-map');
// flag to specify only creating the minified build
- var isMinify = options.indexOf('-m') > -1 || options.indexOf('--minify') > -1;
+ var isMinify = _.contains(options, '-m') || _.contains(options, '--minify');
// flag to specify a mobile build
- var isMobile = isCSP || options.indexOf('mobile') > -1;
+ var isMobile = isCSP || _.contains(options, 'mobile');
// flag to specify a modern build
- var isModern = isMobile || options.indexOf('modern') > -1;
+ var isModern = isMobile || _.contains(options, 'modern');
// flag to specify a modularize build
- var isModularize = options.indexOf('modularize') > -1;
+ var isModularize = _.contains(options, 'modularize');
// flag to specify writing output to standard output
- var isStdOut = options.indexOf('-c') > -1 || options.indexOf('--stdout') > -1;
+ var isStdOut = _.contains(options, '-c') || _.contains(options, '--stdout');
// flag to specify skipping status updates normally logged to the console
- var isSilent = isStdOut || options.indexOf('-s') > -1 || options.indexOf('--silent') > -1;
+ var isSilent = isStdOut || _.contains(options, '-s') || _.contains(options, '--silent');
// flag to specify `_.assign`, `_.bindAll`, and `_.defaults` are
// constructed using the "use strict" directive
- var isStrict = options.indexOf('strict') > -1;
+ var isStrict = _.contains(options, 'strict');
// flag to specify an Underscore build
- var isUnderscore = isBackbone || options.indexOf('underscore') > -1;
+ var isUnderscore = isBackbone || _.contains(options, 'underscore');
// flag to specify a legacy build
- var isLegacy = !(isModern || isUnderscore) && options.indexOf('legacy') > -1;
-
- // used to specify methods of specific categories
- var categories = options.reduce(function(result, value) {
- return /category/.test(value) ? optionToArray(value) : result;
- }, []);
+ var isLegacy = !(isModern || isUnderscore) && _.contains(options, 'legacy');
// used to specify the ways to export the `lodash` function
var exportsOptions = options.reduce(function(result, value) {
@@ -1463,7 +1634,9 @@
var outputPath = options.reduce(function(result, value, index) {
if (/-o|--output/.test(value)) {
result = options[index + 1];
- result = path.join(fs.realpathSync(path.dirname(result)), path.basename(result));
+ var dirname = path.dirname(result);
+ mkdirpSync(dirname);
+ result = path.join(fs.realpathSync(dirname), path.basename(result));
}
return result;
}, '');
@@ -1476,7 +1649,7 @@
: result;
}, '');
- // used when precompiling template files
+ // used as the template settings for precompiled templates
var templateSettings = options.reduce(function(result, value) {
var match = value.match(/settings=(.+)$/);
return match
@@ -1497,15 +1670,17 @@
// flags to specify exposing Lo-Dash methods in an Underscore build
var exposeAssign = !isUnderscore,
+ exposeCreateCallback = !isUnderscore,
exposeForIn = !isUnderscore,
exposeForOwn = !isUnderscore,
- exposeIsPlainObject = !isUnderscore;
+ exposeIsPlainObject = !isUnderscore,
+ exposeZipObject = !isUnderscore;
// flags to specify export options
- var isAMD = exportsOptions.indexOf('amd') > -1,
- isCommonJS = exportsOptions.indexOf('commonjs') > -1,
- isGlobal = exportsOptions.indexOf('global') > -1,
- isNode = exportsOptions.indexOf('node') > -1;
+ var isAMD = _.contains(exportsOptions, 'amd'),
+ isCommonJS = _.contains(exportsOptions, 'commonjs'),
+ isGlobal = _.contains(exportsOptions, 'global'),
+ isNode = _.contains(exportsOptions, 'node');
/*------------------------------------------------------------------------*/
@@ -1531,41 +1706,54 @@
: accumulator;
}, []);
+ var categories = options.reduce(function(accumulator, value) {
+ if (/category|exclude|include|minus|plus/.test(value)) {
+ var array = optionToArray(value);
+ accumulator = _.union(accumulator, /category/.test(value)
+ ? array
+ : array.filter(function(category) { return /^[A-Z]/.test(category); })
+ );
+ }
+ return accumulator;
+ }, []);
+
// set flags to include Lo-Dash's methods if explicitly requested
if (isUnderscore) {
var methods = _.without.apply(_, [_.union(includeMethods, plusMethods)].concat(minusMethods));
- exposeAssign = methods.indexOf('assign') > -1;
- exposeForIn = methods.indexOf('forIn') > -1;
- exposeForOwn = methods.indexOf('forOwn') > -1;
- exposeIsPlainObject = methods.indexOf('isPlainObject') > -1;
+ exposeAssign = _.contains(methods, 'assign');
+ exposeCreateCallback = _.contains(methods, 'createCallback');
+ exposeForIn = _.contains(methods, 'forIn');
+ exposeForOwn = _.contains(methods, 'forOwn');
+ exposeIsPlainObject = _.contains(methods, 'isPlainObject');
+ exposeZipObject = _.contains(methods, 'zipObject');
methods = _.without.apply(_, [plusMethods].concat(minusMethods));
- useUnderscoreClone = methods.indexOf('clone') < 0;
+ useUnderscoreClone = !_.contains(methods, 'clone') && !_.contains(methods, 'cloneDeep');
}
// update dependencies
if (isLegacy) {
dependencyMap.defer = _.without(dependencyMap.defer, 'bind');
}
+ if (isModern) {
+ dependencyMap.isEmpty = _.without(dependencyMap.isEmpty, 'isArguments');
+ dependencyMap.isEqual = _.without(dependencyMap.isEqual, 'isArguments');
+ dependencyMap.isPlainObject = _.without(dependencyMap.isPlainObject, 'isArguments');
+ dependencyMap.keys = _.without(dependencyMap.keys, 'isArguments');
+ dependencyMap.reduceRight = _.without(dependencyMap.reduceRight, 'isString');
+ }
if (isUnderscore) {
dependencyMap.contains = _.without(dependencyMap.contains, 'isString');
- dependencyMap.countBy = _.without(dependencyMap.countBy, 'isEqual', 'keys');
- dependencyMap.every = _.without(dependencyMap.every, 'isEqual', 'keys');
- dependencyMap.filter = _.without(dependencyMap.filter, 'isEqual');
- dependencyMap.find = _.without(dependencyMap.find, 'isEqual', 'keys');
- dependencyMap.groupBy = _.without(dependencyMap.groupBy, 'isEqual', 'keys');
- dependencyMap.isEqual = _.without(dependencyMap.isEqual, 'forIn', 'isArguments');
+ dependencyMap.createCallback = _.without(dependencyMap.createCallback, 'isEqual');
+ dependencyMap.flatten = _.without(dependencyMap.flatten, 'createCallback');
dependencyMap.isEmpty = ['isArray', 'isString'];
- dependencyMap.map = _.without(dependencyMap.map, 'isEqual', 'keys');
- dependencyMap.max = _.without(dependencyMap.max, 'isEqual', 'isString', 'keys');
- dependencyMap.min = _.without(dependencyMap.min, 'isEqual', 'isString', 'keys');
+ dependencyMap.isEqual = _.without(dependencyMap.isEqual, 'forIn', 'isArguments');
+ dependencyMap.max = _.without(dependencyMap.max, 'isArray', 'isString');
+ dependencyMap.min = _.without(dependencyMap.min, 'isArray', 'isString');
dependencyMap.pick = _.without(dependencyMap.pick, 'forIn', 'isObject');
- dependencyMap.reduce = _.without(dependencyMap.reduce, 'isEqual', 'keys');
- dependencyMap.reject = _.without(dependencyMap.reject, 'isEqual', 'keys');
- dependencyMap.some = _.without(dependencyMap.some, 'isEqual', 'keys');
- dependencyMap.sortBy = _.without(dependencyMap.sortBy, 'isEqual', 'keys');
- dependencyMap.sortedIndex = _.without(dependencyMap.sortedIndex, 'isEqual', 'keys');
+ dependencyMap.reduceRight = _.without(dependencyMap.reduceRight, 'isString');
dependencyMap.template = _.without(dependencyMap.template, 'keys', 'values');
- dependencyMap.uniq = _.without(dependencyMap.uniq, 'isEqual', 'keys');
+ dependencyMap.toArray.push('isArray', 'map');
+ dependencyMap.value = _.without(dependencyMap.value, 'isArray');
dependencyMap.where.push('find', 'isEmpty');
if (useUnderscoreClone) {
@@ -1573,7 +1761,22 @@
}
}
if (isModern || isUnderscore) {
- dependencyMap.reduceRight = _.without(dependencyMap.reduceRight, 'isEqual', 'isString');
+ dependencyMap.at = _.without(dependencyMap.at, 'isString');
+ dependencyMap.forEach = _.without(dependencyMap.forEach, 'isArguments', 'isString');
+ dependencyMap.forIn = _.without(dependencyMap.forIn, 'isArguments');
+ dependencyMap.forOwn = _.without(dependencyMap.forOwn, 'isArguments');
+ dependencyMap.toArray = _.without(dependencyMap.toArray, 'isString');
+
+ if (!isMobile) {
+ dependencyMap.every = _.without(dependencyMap.every, 'isArray');
+ dependencyMap.find = _.without(dependencyMap.find, 'isArray');
+ dependencyMap.filter = _.without(dependencyMap.filter, 'isArray');
+ dependencyMap.forEach = _.without(dependencyMap.forEach, 'isArray');
+ dependencyMap.map = _.without(dependencyMap.map, 'isArray');
+ dependencyMap.max.push('forEach');
+ dependencyMap.min.push('forEach');
+ dependencyMap.reduce = _.without(dependencyMap.reduce, 'isArray');
+ }
}
// add method names explicitly
@@ -1590,8 +1793,21 @@
// add method names by category
if (categories.length) {
result = _.union(result || [], getDependencies(categories.reduce(function(accumulator, category) {
- // resolve method names belonging to each category (case-insensitive)
- return accumulator.concat(getMethodsByCategory(source, capitalize(category)));
+ // get method names belonging to each category (case-insensitive)
+ var methodNames = getMethodsByCategory(source, capitalize(category));
+
+ // limit category methods to those available for specific builds
+ if (isBackbone) {
+ methodNames = methodNames.filter(function(methodName) {
+ return _.contains(backboneDependencies, methodName);
+ });
+ }
+ else if (isUnderscore) {
+ methodNames = methodNames.filter(function(methodName) {
+ return _.contains(underscoreMethods, methodName);
+ });
+ }
+ return accumulator.concat(methodNames);
}, [])));
}
if (!result) {
@@ -1619,437 +1835,587 @@
source = setUseStrictOption(source, isStrict);
if (isLegacy) {
- _.each(['getPrototypeOf', 'isBindFast', 'isKeysFast', 'nativeBind', 'nativeIsArray', 'nativeKeys'], function(varName) {
+ _.each(['getPrototypeOf', 'nativeBind', 'nativeIsArray', 'nativeKeys'], function(varName) {
source = replaceVar(source, varName, 'false');
});
- source = replaceVar(source, 'noArgsClass', 'true');
+ _.each(['argsClass', 'fastBind'], function(propName) {
+ source = replaceSupportProp(source, propName, 'false');
+ });
+
source = removeKeysOptimization(source);
}
if (isMobile || isUnderscore) {
source = removeKeysOptimization(source);
+ source = removeSetImmediate(source);
}
if (isModern || isUnderscore) {
- source = removeHasDontEnumBug(source);
- source = removeHasEnumPrototype(source);
- source = removeIteratesOwnLast(source);
- source = removeNoCharByIndex(source);
- source = removeNoNodeClass(source);
+ source = removeSupportNonEnumShadows(source);
+ source = removeSupportEnumPrototypes(source);
+ source = removeSupportOwnLast(source);
+ source = removeSupportUnindexedChars(source);
+ source = removeSupportNodeClass(source);
if (!isMobile) {
- source = removeNonEnumArgs(source);
+ source = removeSupportNonEnumArgs(source);
+
+ // replace `_.forEach`
+ source = replaceFunction(source, 'forEach', [
+ 'function forEach(collection, callback, thisArg) {',
+ ' var index = -1,',
+ ' length = collection ? collection.length : 0;',
+ '',
+ " if (typeof length == 'number') {",
+ " callback = callback && typeof thisArg == 'undefined' ? callback : lodash.createCallback(callback, thisArg);",
+ ' while (++index < length) {',
+ ' if (callback(collection[index], index, collection) === false) {',
+ ' break;',
+ ' }',
+ ' }',
+ ' } else {',
+ ' each(collection, callback, thisArg);',
+ ' }',
+ ' return collection;',
+ '}',
+ ].join('\n'));
+
+ // replace `_.map`
+ source = replaceFunction(source, 'map', [
+ 'function map(collection, callback, thisArg) {',
+ ' var index = -1,',
+ ' length = collection ? collection.length : 0;',
+ '',
+ ' callback = lodash.createCallback(callback, thisArg);',
+ " if (typeof length == 'number') {",
+ ' var result = Array(length);',
+ ' while (++index < length) {',
+ ' result[index] = callback(collection[index], index, collection);',
+ ' }',
+ ' } else {',
+ ' result = [];',
+ ' each(collection, function(value, key, collection) {',
+ ' result[++index] = callback(value, key, collection);',
+ ' });',
+ ' }',
+ ' return result;',
+ '}'
+ ].join('\n'));
+
+ // replace `_.pluck`
+ source = replaceFunction(source, 'pluck', [
+ 'function pluck(collection, property) {',
+ ' var index = -1,',
+ ' length = collection ? collection.length : 0;',
+ '',
+ " if (typeof length == 'number') {",
+ ' var result = Array(length);',
+ ' while (++index < length) {',
+ ' result[index] = collection[index][property];',
+ ' }',
+ ' }',
+ ' return result || map(collection, property);',
+ '}'
+ ].join('\n'));
+
+ // replace `isArray(collection)` checks in "Collections" methods with simpler type checks
+ _.each(['every', 'filter', 'find', 'max', 'min', 'reduce', 'some'], function(methodName) {
+ source = source.replace(matchFunction(source, methodName), function(match) {
+ if (methodName == 'reduce') {
+ match = match.replace(/^( *)var noaccum\b/m, '$1if (!collection) return accumulator;\n$&');
+ }
+ else if (/^(?:max|min)$/.test(methodName)) {
+ match = match.replace(/\beach\(/, 'forEach(');
+ if (!isUnderscore) {
+ return match;
+ }
+ }
+ return match.replace(/^(( *)if *\(.*?\bisArray\([^\)]+\).*?\) *{\n)(( *)var index[^;]+.+\n+)/m, function(snippet, statement, indent, vars) {
+ vars = vars
+ .replace(/\b(length *=)[^;]+/, '$1 collection' + (methodName == 'reduce' ? '.length' : ' ? collection.length : 0'))
+ .replace(RegExp('^ ' + indent, 'gm'), indent);
+
+ return vars + statement.replace(/\bisArray\([^\)]+\)/, "typeof length == 'number'");
+ });
+ });
+ });
+
+ // replace `arrays` property value of `eachIteratorOptions` with `false`
+ source = source.replace(/^( *)var eachIteratorOptions *= *[\s\S]+?\n\1};\n/m, function(match) {
+ return match.replace(/(^ *'arrays':)[^,]+/m, '$1 false');
+ });
}
}
if (isModern) {
// remove `_.isPlainObject` fallback
source = source.replace(matchFunction(source, 'isPlainObject'), function(match) {
- return match.replace(/!getPrototypeOf.+?: */, '');
+ return match.replace(/!getPrototypeOf[^:]+:\s*/, '');
});
+
+ if (!isMobile) {
+ source = removeIsFunctionFallback(source);
+ }
}
if (isUnderscore) {
// replace `_.assign`
source = replaceFunction(source, 'assign', [
- ' function assign(object) {',
- ' if (!object) {',
- ' return object;',
- ' }',
- ' for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {',
- ' var iterable = arguments[argsIndex];',
- ' if (iterable) {',
- ' for (var key in iterable) {',
- ' object[key] = iterable[key];',
- ' }',
+ 'function assign(object) {',
+ ' if (!object) {',
+ ' return object;',
+ ' }',
+ ' for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {',
+ ' var iterable = arguments[argsIndex];',
+ ' if (iterable) {',
+ ' for (var key in iterable) {',
+ ' object[key] = iterable[key];',
' }',
' }',
- ' return object;',
- ' }'
+ ' }',
+ ' return object;',
+ '}'
].join('\n'));
// replace `_.clone`
if (useUnderscoreClone) {
source = replaceFunction(source, 'clone', [
- ' function clone(value) {',
- ' return isObject(value)',
- ' ? (isArray(value) ? slice(value) : assign({}, value))',
- ' : value',
- ' }'
+ 'function clone(value) {',
+ ' return isObject(value)',
+ ' ? (isArray(value) ? slice(value) : assign({}, value))',
+ ' : value;',
+ '}'
].join('\n'));
}
// replace `_.contains`
source = replaceFunction(source, 'contains', [
- ' function contains(collection, target) {',
- ' var length = collection ? collection.length : 0,',
- ' result = false;',
- " if (typeof length == 'number') {",
- ' result = indexOf(collection, target) > -1;',
- ' } else {',
- ' each(collection, function(value) {',
- ' return (result = value === target) && indicatorObject;',
- ' });',
- ' }',
- ' return result;',
- ' }'
+ 'function contains(collection, target) {',
+ ' var length = collection ? collection.length : 0,',
+ ' result = false;',
+ " if (typeof length == 'number') {",
+ ' result = indexOf(collection, target) > -1;',
+ ' } else {',
+ ' each(collection, function(value) {',
+ ' return (result = value === target) && indicatorObject;',
+ ' });',
+ ' }',
+ ' return result;',
+ '}'
].join('\n'));
// replace `_.defaults`
source = replaceFunction(source, 'defaults', [
- ' function defaults(object) {',
- ' if (!object) {',
- ' return object;',
- ' }',
- ' for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {',
- ' var iterable = arguments[argsIndex];',
- ' if (iterable) {',
- ' for (var key in iterable) {',
- ' if (object[key] == null) {',
- ' object[key] = iterable[key];',
- ' }',
+ 'function defaults(object) {',
+ ' if (!object) {',
+ ' return object;',
+ ' }',
+ ' for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {',
+ ' var iterable = arguments[argsIndex];',
+ ' if (iterable) {',
+ ' for (var key in iterable) {',
+ ' if (object[key] == null) {',
+ ' object[key] = iterable[key];',
' }',
' }',
' }',
- ' return object;',
- ' }'
+ ' }',
+ ' return object;',
+ '}'
].join('\n'));
// replace `_.difference`
source = replaceFunction(source, 'difference', [
- ' function difference(array) {',
- ' var index = -1,',
- ' length = array.length,',
- ' flattened = concat.apply(arrayRef, arguments),',
- ' result = [];',
+ 'function difference(array) {',
+ ' var index = -1,',
+ ' length = array.length,',
+ ' flattened = concat.apply(arrayRef, arguments),',
+ ' result = [];',
'',
- ' while (++index < length) {',
- ' var value = array[index]',
- ' if (indexOf(flattened, value, length) < 0) {',
- ' result.push(value);',
- ' }',
+ ' while (++index < length) {',
+ ' var value = array[index];',
+ ' if (indexOf(flattened, value, length) < 0) {',
+ ' result.push(value);',
' }',
- ' return result',
- ' }'
+ ' }',
+ ' return result;',
+ '}'
+ ].join('\n'));
+
+ // replace `_.flatten`
+ source = replaceFunction(source, 'flatten', [
+ 'function flatten(array, isShallow) {',
+ ' var index = -1,',
+ ' length = array ? array.length : 0,',
+ ' result = [];',
+ '' ,
+ ' while (++index < length) {',
+ ' var value = array[index];',
+ ' if (isArray(value)) {',
+ ' push.apply(result, isShallow ? value : flatten(value));',
+ ' } else {',
+ ' result.push(value);',
+ ' }',
+ ' }',
+ ' return result;',
+ '}'
].join('\n'));
// replace `_.intersection`
source = replaceFunction(source, 'intersection', [
- ' function intersection(array) {',
- ' var args = arguments,',
- ' argsLength = args.length,',
- ' index = -1,',
- ' length = array ? array.length : 0,',
- ' result = [];',
+ 'function intersection(array) {',
+ ' var args = arguments,',
+ ' argsLength = args.length,',
+ ' index = -1,',
+ ' length = array ? array.length : 0,',
+ ' result = [];',
'',
- ' outer:',
- ' while (++index < length) {',
- ' var value = array[index];',
- ' if (indexOf(result, value) < 0) {',
- ' var argsIndex = argsLength;',
- ' while (--argsIndex) {',
- ' if (indexOf(args[argsIndex], value) < 0) {',
- ' continue outer;',
- ' }',
+ ' outer:',
+ ' while (++index < length) {',
+ ' var value = array[index];',
+ ' if (indexOf(result, value) < 0) {',
+ ' var argsIndex = argsLength;',
+ ' while (--argsIndex) {',
+ ' if (indexOf(args[argsIndex], value) < 0) {',
+ ' continue outer;',
' }',
- ' result.push(value);',
' }',
+ ' result.push(value);',
' }',
- ' return result;',
- ' }'
+ ' }',
+ ' return result;',
+ '}'
].join('\n'));
// replace `_.isEmpty`
source = replaceFunction(source, 'isEmpty', [
- ' 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;',
- ' }',
- ' }',
+ '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;',
+ '}'
].join('\n'));
// replace `_.isEqual`
source = replaceFunction(source, 'isEqual', [
- ' function isEqual(a, b, stackA, stackB) {',
- ' if (a === b) {',
- ' return a !== 0 || (1 / a == 1 / b);',
- ' }',
- ' var type = typeof a,',
- ' otherType = typeof b;',
+ 'function isEqual(a, b, stackA, stackB) {',
+ ' if (a === b) {',
+ ' return a !== 0 || (1 / a == 1 / b);',
+ ' }',
+ ' var type = typeof a,',
+ ' otherType = typeof b;',
'',
- ' if (a === a &&',
- " (!a || (type != 'function' && type != 'object')) &&",
- " (!b || (otherType != 'function' && otherType != 'object'))) {",
- ' return false;',
- ' }',
- ' if (a == null || b == null) {',
- ' return a === b;',
- ' }',
- ' var className = toString.call(a),',
- ' otherClass = toString.call(b);',
+ ' if (a === a &&',
+ " (!a || (type != 'function' && type != 'object')) &&",
+ " (!b || (otherType != 'function' && otherType != 'object'))) {",
+ ' return false;',
+ ' }',
+ ' if (a == null || b == null) {',
+ ' return a === b;',
+ ' }',
+ ' var className = toString.call(a),',
+ ' otherClass = toString.call(b);',
'',
- ' if (className != otherClass) {',
- ' return false;',
- ' }',
- ' switch (className) {',
- ' case boolClass:',
- ' case dateClass:',
- ' return +a == +b;',
+ ' if (className != otherClass) {',
+ ' return false;',
+ ' }',
+ ' switch (className) {',
+ ' case boolClass:',
+ ' case dateClass:',
+ ' return +a == +b;',
'',
- ' case numberClass:',
- ' return a != +a',
- ' ? b != +b',
- ' : (a == 0 ? (1 / a == 1 / b) : a == +b);',
+ ' case numberClass:',
+ ' return a != +a',
+ ' ? b != +b',
+ ' : (a == 0 ? (1 / a == 1 / b) : a == +b);',
'',
- ' case regexpClass:',
- ' case stringClass:',
- " return a == b + '';",
+ ' case regexpClass:',
+ ' case stringClass:',
+ ' return a == String(b);',
+ ' }',
+ ' var isArr = className == arrayClass;',
+ ' if (!isArr) {',
+ ' if (a instanceof lodash || b instanceof lodash) {',
+ ' return isEqual(a.__wrapped__ || a, b.__wrapped__ || b, stackA, stackB);',
' }',
- ' var isArr = className == arrayClass;',
- ' if (!isArr) {',
- ' if (a.__wrapped__ || b.__wrapped__) {',
- ' return isEqual(a.__wrapped__ || a, b.__wrapped__ || b, stackA, stackB);',
- ' }',
- ' if (className != objectClass) {',
- ' return false;',
- ' }',
- ' var ctorA = a.constructor,',
- ' ctorB = b.constructor;',
+ ' if (className != objectClass) {',
+ ' return false;',
+ ' }',
+ ' var ctorA = a.constructor,',
+ ' ctorB = b.constructor;',
'',
- ' if (ctorA != ctorB && !(',
- ' isFunction(ctorA) && ctorA instanceof ctorA &&',
- ' isFunction(ctorB) && ctorB instanceof ctorB',
- ' )) {',
- ' return false;',
- ' }',
+ ' if (ctorA != ctorB && !(',
+ ' isFunction(ctorA) && ctorA instanceof ctorA &&',
+ ' isFunction(ctorB) && ctorB instanceof ctorB',
+ ' )) {',
+ ' return false;',
' }',
- ' stackA || (stackA = []);',
- ' stackB || (stackB = []);',
+ ' }',
+ ' stackA || (stackA = []);',
+ ' stackB || (stackB = []);',
'',
- ' var length = stackA.length;',
- ' while (length--) {',
- ' if (stackA[length] == a) {',
- ' return stackB[length] == b;',
- ' }',
+ ' var length = stackA.length;',
+ ' while (length--) {',
+ ' if (stackA[length] == a) {',
+ ' return stackB[length] == b;',
' }',
- ' var result = true,',
- ' size = 0;',
+ ' }',
+ ' var result = true,',
+ ' size = 0;',
'',
- ' stackA.push(a);',
- ' stackB.push(b);',
+ ' stackA.push(a);',
+ ' stackB.push(b);',
'',
- ' if (isArr) {',
- ' size = b.length;',
- ' result = size == a.length;',
+ ' if (isArr) {',
+ ' size = b.length;',
+ ' result = size == a.length;',
'',
- ' if (result) {',
- ' while (size--) {',
- ' if (!(result = isEqual(a[size], b[size], stackA, stackB))) {',
- ' break;',
- ' }',
+ ' if (result) {',
+ ' while (size--) {',
+ ' if (!(result = isEqual(a[size], b[size], stackA, stackB))) {',
+ ' break;',
' }',
' }',
- ' return result;',
' }',
- ' forIn(b, function(value, key, b) {',
- ' if (hasOwnProperty.call(b, key)) {',
- ' size++;',
- ' return !(result = hasOwnProperty.call(a, key) && isEqual(a[key], value, stackA, stackB)) && indicatorObject;',
+ ' return result;',
+ ' }',
+ ' forIn(b, function(value, key, b) {',
+ ' if (hasOwnProperty.call(b, key)) {',
+ ' size++;',
+ ' return !(result = hasOwnProperty.call(a, key) && isEqual(a[key], value, stackA, stackB)) && indicatorObject;',
+ ' }',
+ ' });',
+ '',
+ ' if (result) {',
+ ' forIn(a, function(value, key, a) {',
+ ' if (hasOwnProperty.call(a, key)) {',
+ ' return !(result = --size > -1) && indicatorObject;',
' }',
' });',
- '',
- ' if (result) {',
- ' forIn(a, function(value, key, a) {',
- ' if (hasOwnProperty.call(a, key)) {',
- ' return !(result = --size > -1) && indicatorObject;',
- ' }',
- ' });',
- ' }',
- ' return result;',
- ' }'
+ ' }',
+ ' return result;',
+ '}'
+ ].join('\n'));
+
+ // replace `lodash`
+ source = replaceFunction(source, 'lodash', [
+ 'function lodash(value) {',
+ ' return (value instanceof lodash)',
+ ' ? value',
+ ' : new lodashWrapper(value);',
+ '}'
].join('\n'));
// replace `_.omit`
source = replaceFunction(source, 'omit', [
- ' function omit(object) {',
- ' var props = concat.apply(arrayRef, arguments),',
- ' result = {};',
+ '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;',
- ' }'
+ ' forIn(object, function(value, key) {',
+ ' if (indexOf(props, key, 1) < 0) {',
+ ' result[key] = value;',
+ ' }',
+ ' });',
+ ' return result;',
+ '}'
].join('\n'));
// replace `_.pick`
source = replaceFunction(source, 'pick', [
- ' function pick(object) {',
- ' var index = 0,',
- ' props = concat.apply(arrayRef, arguments),',
- ' length = props.length,',
- ' result = {};',
+ '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];',
- ' }',
+ ' while (++index < length) {',
+ ' var prop = props[index];',
+ ' if (prop in object) {',
+ ' result[prop] = object[prop];',
' }',
- ' return result;',
- ' }'
+ ' }',
+ ' return result;',
+ '}'
].join('\n'));
// replace `_.result`
source = replaceFunction(source, 'result', [
- ' function result(object, property) {',
- ' var value = object ? object[property] : null;',
- ' return isFunction(value) ? object[property]() : value;',
- ' }'
+ 'function result(object, property) {',
+ ' var value = object ? object[property] : null;',
+ ' return isFunction(value) ? object[property]() : value;',
+ '}'
].join('\n'));
// replace `_.template`
source = replaceFunction(source, 'template', [
- ' function template(text, data, options) {',
- " text || (text = '');",
- ' options = defaults({}, options, lodash.templateSettings);',
+ 'function template(text, data, options) {',
+ " text || (text = '');",
+ ' options = defaults({}, options, lodash.templateSettings);',
'',
- ' var index = 0,',
- ' source = "__p += \'",',
- ' variable = options.variable;',
+ ' var index = 0,',
+ ' source = "__p += \'",',
+ ' variable = options.variable;',
'',
- ' var reDelimiters = RegExp(',
- " (options.escape || reNoMatch).source + '|' +",
- " (options.interpolate || reNoMatch).source + '|' +",
- " (options.evaluate || reNoMatch).source + '|$'",
- " , 'g');",
+ ' var reDelimiters = RegExp(',
+ " (options.escape || reNoMatch).source + '|' +",
+ " (options.interpolate || reNoMatch).source + '|' +",
+ " (options.evaluate || reNoMatch).source + '|$'",
+ " , 'g');",
'',
- ' text.replace(reDelimiters, function(match, escapeValue, interpolateValue, evaluateValue, offset) {',
- ' source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar);',
- ' if (escapeValue) {',
- ' source += "\' +\\n_.escape(" + escapeValue + ") +\\n\'";',
- ' }',
- ' if (evaluateValue) {',
- ' source += "\';\\n" + evaluateValue + ";\\n__p += \'";',
- ' }',
- ' if (interpolateValue) {',
- ' source += "\' +\\n((__t = (" + interpolateValue + ")) == null ? \'\' : __t) +\\n\'";',
- ' }',
- ' index = offset + match.length;',
- ' return match;',
- ' });',
- '',
- ' source += "\';\\n";',
- ' if (!variable) {',
- " variable = 'obj';",
- " source = 'with (' + variable + ' || {}) {\\n' + source + '\\n}\\n';",
+ ' text.replace(reDelimiters, function(match, escapeValue, interpolateValue, evaluateValue, offset) {',
+ ' source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar);',
+ ' if (escapeValue) {',
+ ' source += "\' +\\n_.escape(" + escapeValue + ") +\\n\'";',
' }',
- " source = 'function(' + variable + ') {\\n' +",
- ' "var __t, __p = \'\', __j = Array.prototype.join;\\n" +',
- ' "function print() { __p += __j.call(arguments, \'\') }\\n" +',
- ' source +',
- " 'return __p\\n}';",
- '',
- ' try {',
- " var result = Function('_', 'return ' + source)(lodash);",
- ' } catch(e) {',
- ' e.source = source;',
- ' throw e;',
+ ' if (evaluateValue) {',
+ ' source += "\';\\n" + evaluateValue + ";\\n__p += \'";',
' }',
- ' if (data) {',
- ' return result(data);',
+ ' if (interpolateValue) {',
+ ' source += "\' +\\n((__t = (" + interpolateValue + ")) == null ? \'\' : __t) +\\n\'";',
' }',
- ' result.source = source;',
- ' return result;',
- ' }'
+ ' index = offset + match.length;',
+ ' return match;',
+ ' });',
+ '',
+ ' source += "\';\\n";',
+ ' if (!variable) {',
+ " variable = 'obj';",
+ " source = 'with (' + variable + ' || {}) {\\n' + source + '\\n}\\n';",
+ ' }',
+ " source = 'function(' + variable + ') {\\n' +",
+ ' "var __t, __p = \'\', __j = Array.prototype.join;\\n" +',
+ ' "function print() { __p += __j.call(arguments, \'\') }\\n" +',
+ ' source +',
+ " 'return __p\\n}';",
+ '',
+ ' try {',
+ " var result = Function('_', 'return ' + source)(lodash);",
+ ' } catch(e) {',
+ ' e.source = source;',
+ ' throw e;',
+ ' }',
+ ' if (data) {',
+ ' return result(data);',
+ ' }',
+ ' result.source = source;',
+ ' return result;',
+ '}'
].join('\n'));
+ // replace `_.times`
+ source = replaceFunction(source, 'times', [
+ 'function times(n, callback, thisArg) {',
+ ' var index = -1,',
+ ' result = Array(n > -1 ? n : 0);',
+ '',
+ ' while (++index < n) {',
+ ' result[index] = callback.call(thisArg, index);',
+ ' }',
+ ' return result;',
+ '}'
+ ].join('\n'));
+
+ // replace `_.toArray`
+ if (useUnderscoreClone) {
+ source = replaceFunction(source, 'toArray', [
+ 'function toArray(collection) {',
+ ' if (isArray(collection)) {',
+ ' return slice(collection);',
+ ' }',
+ " if (collection && typeof collection.length == 'number') {",
+ ' return map(collection);',
+ ' }',
+ ' return values(collection);',
+ '}'
+ ].join('\n'));
+ }
+
// replace `_.uniq`
source = replaceFunction(source, 'uniq', [
- ' function uniq(array, isSorted, callback, thisArg) {',
- ' var index = -1,',
- ' length = array ? array.length : 0,',
- ' result = [],',
- ' seen = result;',
+ 'function uniq(array, isSorted, callback, thisArg) {',
+ ' var index = -1,',
+ ' length = array ? array.length : 0,',
+ ' result = [],',
+ ' seen = result;',
'',
- " if (typeof isSorted == 'function') {",
- ' thisArg = callback;',
- ' callback = isSorted;',
- ' isSorted = false;',
- ' }',
- ' if (callback) {',
- ' seen = [];',
- ' callback = createCallback(callback, thisArg);',
- ' }',
- ' while (++index < length) {',
- ' var value = array[index],',
- ' computed = callback ? callback(value, index, array) : value;',
+ " if (typeof isSorted != 'boolean' && isSorted != null) {",
+ ' thisArg = callback;',
+ ' callback = isSorted;',
+ ' isSorted = false;',
+ ' }',
+ ' if (callback != null) {',
+ ' seen = [];',
+ ' callback = lodash.createCallback(callback, thisArg);',
+ ' }',
+ ' while (++index < length) {',
+ ' var value = array[index],',
+ ' computed = callback ? callback(value, index, array) : value;',
'',
- ' if (isSorted',
- ' ? !index || seen[seen.length - 1] !== computed',
- ' : indexOf(seen, computed) < 0',
- ' ) {',
- ' if (callback) {',
- ' seen.push(computed);',
- ' }',
- ' result.push(value);',
+ ' if (isSorted',
+ ' ? !index || seen[seen.length - 1] !== computed',
+ ' : indexOf(seen, computed) < 0',
+ ' ) {',
+ ' if (callback) {',
+ ' seen.push(computed);',
' }',
+ ' result.push(value);',
' }',
- ' return result;',
- ' }'
+ ' }',
+ ' return result;',
+ '}'
].join('\n'));
// replace `_.uniqueId`
source = replaceFunction(source, 'uniqueId', [
- ' function uniqueId(prefix) {',
- " var id = ++idCounter + '';",
- ' return prefix ? prefix + id : id;',
- ' }'
+ 'function uniqueId(prefix) {',
+ " var id = ++idCounter + '';",
+ ' return prefix ? prefix + id : id;',
+ '}'
].join('\n'));
// replace `_.where`
source = replaceFunction(source, 'where', [
- ' function where(collection, properties, first) {',
- ' return (first && isEmpty(properties))',
- ' ? null',
- ' : (first ? find : filter)(collection, properties);',
- ' }'
+ 'function where(collection, properties, first) {',
+ ' return (first && isEmpty(properties))',
+ ' ? null',
+ ' : (first ? find : filter)(collection, properties);',
+ '}'
].join('\n'));
// replace `_.without`
source = replaceFunction(source, 'without', [
- ' function without(array) {',
- ' var index = -1,',
- ' length = array.length,',
- ' result = [];',
+ 'function without(array) {',
+ ' var index = -1,',
+ ' length = array.length,',
+ ' result = [];',
'',
- ' while (++index < length) {',
- ' var value = array[index]',
- ' if (indexOf(arguments, value, 1) < 0) {',
- ' result.push(value);',
- ' }',
+ ' while (++index < length) {',
+ ' var value = array[index];',
+ ' if (indexOf(arguments, value, 1) < 0) {',
+ ' result.push(value);',
' }',
- ' return result',
- ' }'
+ ' }',
+ ' return result',
+ '}'
].join('\n'));
// add `_.findWhere`
- source = source.replace(matchFunction(source, 'find'), function (match) {
- return match + [
+ source = source.replace(matchFunction(source, 'find'), function(match) {
+ var indent = getIndent(match);
+ return match && (match + [
'',
- ' function findWhere(object, properties) {',
- ' return where(object, properties, true);',
- ' }',
+ 'function findWhere(object, properties) {',
+ ' return where(object, properties, true);',
+ '}',
''
- ].join('\n')
+ ].join('\n' + indent));
});
source = source.replace(getMethodAssignments(source), function(match) {
@@ -2064,36 +2430,65 @@
// remove large array optimizations
source = removeFunction(source, 'cachedContains');
- source = removeVar(source, 'largeArraySize');
// remove `_.isEqual` use from `createCallback`
source = source.replace(matchFunction(source, 'createCallback'), function(match) {
- return match.replace(/isEqual\(([^,]+), *([^,]+)[^)]+\)/, '$1 === $2');
+ return match.replace(/\bisEqual\(([^,]+), *([^,]+)[^)]+\)/, '$1 === $2');
});
// remove conditional `charCodeCallback` use from `_.max` and `_.min`
_.each(['max', 'min'], function(methodName) {
source = source.replace(matchFunction(source, methodName), function(match) {
- return match.replace(/!callback *&& *isString\(collection\)[\s\S]+?: */g, '');
+ return match.replace(/=.+?callback *&& *isString[^:]+:\s*/g, '= ');
});
});
+ // replace `slice` with `slice.call`
+ source = removeFunction(source, 'slice');
+ source = source.replace(/^(( *)setTimeout = context.setTimeout)([,;])/m, '$1,\n$2slice = arrayRef.slice$3');
+ source = source.replace(/([^.]\bslice)\(/g, '$1.call(');
+
+ // replace `lodash.createCallback` references with `createCallback`
+ if (!exposeCreateCallback) {
+ source = source.replace(/\blodash\.(createCallback\()\b/g, '$1');
+ }
// remove unneeded variables
if (useUnderscoreClone) {
source = removeVar(source, 'cloneableClasses');
source = removeVar(source, 'ctorByClass');
}
// remove unused features from `createBound`
- if (buildMethods.indexOf('partial') < 0 && buildMethods.indexOf('partialRight') < 0) {
+ if (_.every(['bindKey', 'partial', 'partialRight'], function(methodName) {
+ return !_.contains(buildMethods, methodName);
+ })) {
source = source.replace(matchFunction(source, 'createBound'), function(match) {
return match
- .replace(/, *right[^)]*/, '')
- .replace(/(function createBound\([^{]+{)[\s\S]+?(\n *function bound)/, '$1$2')
+ .replace(/, *indicator[^)]*/, '')
+ .replace(/(function createBound\([^{]+{)[\s\S]+?(\n *)(function bound)/, function(match, part1, indent, part2) {
+ return [
+ part1,
+ 'if (!isFunction(func)) {',
+ ' throw new TypeError;',
+ '}',
+ part2
+ ].join(indent);
+ })
.replace(/thisBinding *=[^}]+}/, 'thisBinding = thisArg;\n')
.replace(/\(args *=.+/, 'partialArgs.concat(slice(args))');
});
}
}
+ // replace `each` references with `forEach` and `forOwn`
+ if ((isUnderscore || (isModern && !isMobile)) &&
+ _.contains(buildMethods, 'forEach') &&
+ (_.contains(buildMethods, 'forOwn') || !exposeForOwn)
+ ) {
+ source = source
+ .replace(matchFunction(source, 'each'), '')
+ .replace(/^ *lodash\._each *=.+\n/gm, '')
+ .replace(/\beach(?=\(collection)/g, 'forOwn')
+ .replace(/\beach(?=\(\[)/g, 'forEach');
+ }
vm.runInContext(source, context);
return context._;
}());
@@ -2121,55 +2516,59 @@
source = source.replace(matchFunction(source, 'template'), function(match) {
return match
.replace(/iteratorTemplate *&& */g, '')
- .replace(/iteratorTemplate *\? *([^:]+?) *:[^,;]+/g, '$1');
+ .replace(/iteratorTemplate\s*\?\s*([^:]+?)\s*:[^,;]+/g, '$1');
});
/*----------------------------------------------------------------------*/
if (isLegacy) {
source = removeSetImmediate(source);
+ source = removeSupportProp(source, 'fastBind');
- _.each(['isBindFast', 'isV8', 'nativeBind', 'nativeIsArray', 'nativeKeys', 'reNative'], function(varName) {
+ _.each(['isIeOpera', 'isV8', 'nativeBind', 'nativeIsArray', 'nativeKeys', 'reNative'], function(varName) {
source = removeVar(source, varName);
});
// remove native `Function#bind` branch in `_.bind`
source = source.replace(matchFunction(source, 'bind'), function(match) {
- return match.replace(/(?:\s*\/\/.*)*\s*return isBindFast[^:]+:\s*/, 'return ');
+ return match.replace(/(?:\s*\/\/.*)*\s*return support\.fastBind[^:]+:\s*/, 'return ');
});
// remove native `Array.isArray` branch in `_.isArray`
source = source.replace(matchFunction(source, 'isArray'), function(match) {
- return match.replace(/nativeIsArray * \|\|\s*/, '');
+ return match.replace(/nativeIsArray\s*\|\|\s*/, '');
});
// replace `_.keys` with `shimKeys`
if (!isRemoved(source, 'keys')) {
source = source.replace(
matchFunction(source, 'keys').replace(/[\s\S]+?var keys *= */, ''),
- matchFunction(source, 'shimKeys').replace(/[\s\S]+?function shimKeys/, 'function').replace(/}\n$/, '};\n')
+ matchFunction(source, 'shimKeys').replace(/[\s\S]+?var shimKeys *= */, '')
);
source = removeFunction(source, 'shimKeys');
}
// replace `_.isArguments` with fallback
if (!isRemoved(source, 'isArguments')) {
- source = source.replace(
- matchFunction(source, 'isArguments').replace(/[\s\S]+?function isArguments/, ''),
- getIsArgumentsFallback(source).match(/isArguments *= *function([\s\S]+?) *};/)[1] + ' }\n'
- );
+ source = source.replace(matchFunction(source, 'isArguments').replace(/[\s\S]+?function isArguments/, ''), function() {
+ var fallback = getIsArgumentsFallback(source),
+ body = fallback.match(/isArguments *= *function([\s\S]+? *});/)[1],
+ indent = getIndent(fallback);
+
+ return body.replace(RegExp('^' + indent, 'gm'), indent.slice(0, -2)) + '\n';
+ });
source = removeIsArgumentsFallback(source);
}
}
if (isModern) {
- source = removeArgsAreObjects(source);
- source = removeHasObjectSpliceBug(source);
+ source = removeSupportArgsObject(source);
+ source = removeSupportSpliceObjects(source);
source = removeIsArgumentsFallback(source);
}
if (isModern || isUnderscore) {
- source = removeNoArgsClass(source);
- source = removeNoNodeClass(source);
+ source = removeSupportArgsClass(source);
+ source = removeSupportNodeClass(source);
}
if (isMobile || isUnderscore) {
source = removeVar(source, 'iteratorTemplate');
@@ -2177,36 +2576,48 @@
// inline all functions defined with `createIterator`
_.functions(lodash).forEach(function(methodName) {
// strip leading underscores to match pseudo private functions
- var reFunc = RegExp('(\\bvar ' + methodName.replace(/^_/, '') + ' *= *)createIterator\\(((?:{|[a-zA-Z])[\\s\\S]+?)\\);\\n');
+ var reFunc = RegExp('^( *)(var ' + methodName.replace(/^_/, '') + ' *= *)createIterator\\(((?:{|[a-zA-Z])[\\s\\S]+?)\\);\\n', 'm');
if (reFunc.test(source)) {
// extract, format, and inject the compiled function's source code
- source = source.replace(reFunc, function(match, captured) {
- return captured + getFunctionSource(lodash[methodName]) + ';\n';
+ source = source.replace(reFunc, function(match, indent, left) {
+ return (indent + left) +
+ getFunctionSource(lodash[methodName], indent) + ';\n';
});
}
});
}
if (isUnderscore) {
- // remove `_.assign`, `_.forIn`, `_.forOwn`, and `_.isPlainObject` assignments
+ // remove `_.assign`, `_.forIn`, `_.forOwn`, `_.isPlainObject`, and `_.zipObject` assignments
(function() {
var snippet = getMethodAssignments(source),
modified = snippet;
if (!exposeAssign) {
- modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.assign *= *.+\n/m, '');
+ modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.assign *=.+\n/m, '');
+ }
+ if (!exposeCreateCallback) {
+ modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.createCallback *=.+\n/m, '');
}
if (!exposeForIn) {
- modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.forIn *= *.+\n/m, '');
+ modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.forIn *=.+\n/m, '');
}
if (!exposeForOwn) {
- modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.forOwn *= *.+\n/m, '');
+ modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.forOwn *=.+\n/m, '');
}
if (!exposeIsPlainObject) {
- modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.isPlainObject *= *.+\n/m, '');
+ modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.isPlainObject *=.+\n/m, '');
}
- source = source.replace(snippet, modified);
+ if (!exposeZipObject) {
+ modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.zipObject *=.+\n/m, '');
+ }
+ source = source.replace(snippet, function() {
+ return modified;
+ });
}());
+ // unexpose `lodash.support`
+ source = source.replace(/lodash\.support *= */, '');
+
// remove `thisArg` from unexposed `forIn` and `forOwn`
_.each([
{ 'methodName': 'forIn', 'flag': exposeForIn },
@@ -2224,7 +2635,9 @@
// remove chainability from `each` and `_.forEach`
_.each(['each', 'forEach'], function(methodName) {
source = source.replace(matchFunction(source, methodName), function(match) {
- return match.replace(/\n *return .+?([};\s]+)$/, '$1');
+ return match
+ .replace(/\n *return .+?([};\s]+)$/, '$1')
+ .replace(/\b(return) +result\b/, '$1')
});
});
@@ -2251,9 +2664,12 @@
});
}
if (!(isMobile || isUnderscore)) {
+ source = removeFromCreateIterator(source, 'support');
+
// inline `iteratorTemplate` template
- source = source.replace(getIteratorTemplate(source), function() {
- var snippet = getFunctionSource(lodash._iteratorTemplate);
+ source = source.replace(getIteratorTemplate(source), function(match) {
+ var indent = getIndent(match),
+ snippet = getFunctionSource(lodash._iteratorTemplate, indent);
// prepend data object references to property names to avoid having to
// use a with-statement
@@ -2267,22 +2683,23 @@
.replace(/function print[^}]+}/, '')
.replace(/'(?:\\n|\s)+'/g, "''")
.replace(/__p *\+= *' *';/g, '')
+ .replace(/\s*\+\s*'';/g, ';')
.replace(/(__p *\+= *)' *' *\+/g, '$1')
- .replace(/({) *;|; *(})/g, '$1$2')
- .replace(/\(\(__t *= *\( *([^)]+) *\)\) *== *null *\? *'' *: *__t\)/g, '($1)');
+ .replace(/(?:; *)([{}])|([{}])(?: *;)/g, '$1$2')
+ .replace(/\(\(__t *= *\( *([^)]+?) *\)\) *== *null *\? *'' *: *__t\)/g, '($1)');
// remove the with-statement
snippet = snippet.replace(/ *with *\(.+?\) *{/, '\n').replace(/}([^}]*}[^}]*$)/, '$1');
// minor cleanup
snippet = snippet
- .replace(/obj *\|\|\s*\(obj *= *{}\);/, '')
+ .replace(/obj\s*\|\|\s*\(obj *= *{}\);/, '')
.replace(/var __p = '';\s*__p \+=/, 'var __p =');
// remove comments, including sourceURLs
snippet = snippet.replace(/\s*\/\/.*(?:\n|$)/g, '');
- return ' var iteratorTemplate = ' + snippet + ';\n';
+ return indent + 'var iteratorTemplate = ' + snippet + ';\n';
});
}
}
@@ -2310,19 +2727,19 @@
source = source.replace(/(?: *\/\/.*\n)*( *)if *\(typeof +define[\s\S]+?else /, '$1');
}
if (!isNode) {
- source = source.replace(/(?: *\/\/.*\n)*( *)if *\(freeModule[\s\S]+?else *{([\s\S]+?\n)\1}\n/, '$1$2');
+ source = source.replace(/(?: *\/\/.*\n)*( *)if *\(freeModule[\s\S]+?else *{([\s\S]+?\n)\1}\n+/, '$1$2');
}
if (!isCommonJS) {
- source = source.replace(/(?: *\/\/.*\n)*(?:( *)else *{)?\s*freeExports\.\w+ *=[\s\S]+?(?:\n\1})?\n/, '');
+ source = source.replace(/(?: *\/\/.*\n)*(?:( *)else *{)?\s*freeExports\.\w+ *=[\s\S]+?(?:\n\1})?\n+/, '');
}
if (!isGlobal) {
- source = source.replace(/(?:( *)(})? *else(?: *if *\(_\))? *{)?(?:\s*\/\/.*)*\s*(?:window\._|_\.templates) *=[\s\S]+?(?:\n\1})?\n/g, '$1$2\n');
+ source = source.replace(/(?:( *)(})? *else(?: *if *\(_\))? *{)?(?:\s*\/\/.*)*\s*(?:window\._|_\.templates) *=[\s\S]+?(?:\n\1})?\n+/g, '$1$2\n');
}
// remove `if (freeExports) {...}` if it's empty
if (isAMD && isGlobal) {
- source = source.replace(/(?: *\/\/.*\n)* *(?:else )?if *\(freeExports\) *{\s*}\n/, '');
+ source = source.replace(/(?: *\/\/.*\n)* *(?:else )?if *\(freeExports.*?\) *{\s*}\n+/, '');
} else {
- source = source.replace(/(?: *\/\/.*\n)* *(?:else )?if *\(freeExports\) *{\s*}(?:\s*else *{([\s\S]+?) *})?\n/, '$1\n');
+ source = source.replace(/(?: *\/\/.*\n)* *(?:else )?if *\(freeExports.*?\) *{\s*}(?:\s*else *{([\s\S]+?) *})?\n+/, '$1\n');
}
}());
@@ -2334,82 +2751,132 @@
source = replaceVar(source, 'htmlUnescapes', "{'&':'&','<':'<','>':'>','"':'\"',''':\"'\"}");
}
if (isRemoved(source, 'isArguments')) {
- source = replaceVar(source, 'noArgsClass', 'false');
+ source = replaceSupportProp(source, 'argsClass', 'true');
}
if (isRemoved(source, 'isFunction')) {
source = removeIsFunctionFallback(source);
}
if (isRemoved(source, 'mixin')) {
- source = removeHasObjectSpliceBug(source);
+ // inline `_.mixin` call to ensure proper chaining behavior
+ source = source.replace(/^( *)mixin\(lodash\).+/m, function(match, indent) {
+ return indent + [
+ 'forOwn(lodash, function(func, methodName) {',
+ ' lodash[methodName] = func;',
+ '',
+ ' lodash.prototype[methodName] = function() {',
+ ' var value = this.__wrapped__,',
+ ' args = [value];',
+ '',
+ ' push.apply(args, arguments);',
+ ' var result = func.apply(lodash, args);',
+ " return (value && typeof value == 'object' && value == result)",
+ ' ? this',
+ ' : new lodashWrapper(result);',
+ ' };',
+ '});'
+ ].join('\n' + indent);
+ });
+ }
+ if (isRemoved(source, 'value')) {
+ source = removeFunction(source, 'wrapperToString');
+ source = removeFunction(source, 'wrapperValueOf');
+ source = removeSupportSpliceObjects(source);
+ source = removeLodashWrapper(source);
// simplify the `lodash` function
source = replaceFunction(source, 'lodash', [
- ' function lodash() {',
- ' // no operation performed',
- ' }'
+ 'function lodash() {',
+ ' // no operation performed',
+ '}'
+ ].join('\n'));
+
+ // remove `lodash.prototype` method assignments from `_.mixin`
+ source = replaceFunction(source, 'mixin', [
+ 'function mixin(object) {',
+ ' forEach(functions(object), function(methodName) {',
+ ' lodash[methodName] = object[methodName];',
+ ' });',
+ '}'
].join('\n'));
// remove all `lodash.prototype` additions
source = source
.replace(/(?:\s*\/\/.*)*\n( *)forOwn\(lodash, *function\(func, *methodName\)[\s\S]+?\n\1}.+/g, '')
- .replace(/(?:\s*\/\/.*)*\n( *)each\(\['[\s\S]+?\n\1}.+/g, '')
- .replace(/(?:\s*\/\/.*)*\s*lodash\.prototype.+\n/g, '')
- .replace(/(?:\s*\/\/.*)*\s*mixin\(lodash\).+\n/, '');
+ .replace(/(?:\s*\/\/.*)*\n( *)(?:each|forEach)\(\['[\s\S]+?\n\1}.+/g, '')
+ .replace(/(?:\s*\/\/.*)*\n *lodash\.prototype.+/g, '');
}
// remove functions, variables, and snippets that the minifier may miss
if (isRemoved(source, 'clone')) {
source = removeVar(source, 'cloneableClasses');
source = removeVar(source, 'ctorByClass');
}
+ if (isRemoved(source, 'defer')) {
+ source = removeSetImmediate(source);
+ }
if (isRemoved(source, 'isArray')) {
source = removeVar(source, 'nativeIsArray');
}
if (isRemoved(source, 'isPlainObject')) {
+ source = removeFunction(source, 'shimIsPlainObject');
source = removeVar(source, 'getPrototypeOf');
- source = removeIteratesOwnLast(source);
+ source = removeSupportOwnLast(source);
}
if (isRemoved(source, 'keys')) {
source = removeFunction(source, 'shimKeys');
}
+ if (isRemoved(source, 'parseInt')) {
+ source = removeVar(source, 'nativeParseInt');
+ source = removeVar(source, 'reLeadingZeros');
+ }
if (isRemoved(source, 'template')) {
// remove `templateSettings` assignment
source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *lodash\.templateSettings[\s\S]+?};\n/, '');
}
if (isRemoved(source, 'isArguments', 'isEmpty')) {
- source = removeNoArgsClass(source);
+ source = removeSupportArgsClass(source);
}
if (isRemoved(source, 'clone', 'isEqual', 'isPlainObject')) {
- source = removeNoNodeClass(source);
+ source = removeSupportNodeClass(source);
+ }
+ if (!/\beach\(/.test(source)) {
+ source = source.replace(matchFunction(source, 'each'), '');
}
if ((source.match(/\bcreateIterator\b/g) || []).length < 2) {
source = removeFunction(source, 'createIterator');
source = removeVar(source, 'defaultsIteratorOptions');
source = removeVar(source, 'eachIteratorOptions');
source = removeVar(source, 'forOwnIteratorOptions');
+ source = removeVar(source, 'iteratorTemplate');
source = removeVar(source, 'templateIterator');
- source = removeHasDontEnumBug(source);
- source = removeHasEnumPrototype(source);
+ source = removeSupportNonEnumShadows(source);
+ source = removeSupportEnumPrototypes(source);
}
if (isRemoved(source, 'createIterator', 'bind', 'keys')) {
- source = removeSetImmediate(source);
- source = removeVar(source, 'isBindFast');
+ source = removeSupportProp(source, 'fastBind');
source = removeVar(source, 'isV8');
source = removeVar(source, 'nativeBind');
}
if (isRemoved(source, 'createIterator', 'keys')) {
source = removeVar(source, 'nativeKeys');
source = removeKeysOptimization(source);
- source = removeNonEnumArgs(source);
+ source = removeSupportNonEnumArgs(source);
}
- if (!source.match(/var (?:hasDontEnumBug|hasEnumPrototype|iteratesOwnLast|nonEnumArgs)\b/g)) {
- // remove IIFE used to assign `hasDontEnumBug`, `hasEnumPrototype`, `iteratesOwnLast`, and `nonEnumArgs`
- source = source.replace(/^ *\(function\(\) *{[\s\S]+?}\(1\)\);\n/m, '');
+ if (!/support\.(?:enumPrototypes|nonEnumShadows|ownLast)\b/.test(source)) {
+ // remove code used to resolve unneeded `support` properties
+ source = source.replace(/^ *\(function[\s\S]+?\n(( *)var ctor *= *function[\s\S]+?(?:\n *for.+)+\n)([\s\S]+?)}\(1\)\);\n/m, function(match, setup, indent, body) {
+ if (/support\.spliceObjects\b/.test(match)) {
+ return match.replace(setup, indent + "var object = { '0': 1, 'length': 1 };\n");
+ } else if (/support\.nonEnumArgs\b/.test(match)) {
+ return match.replace(setup, indent + 'for (var prop in arguments) { }\n');
+ }
+ return body.replace(RegExp('^' + indent, 'gm'), indent.slice(0, -2));
+ });
}
}
- if ((source.match(/\bfreeModule\b/g) || []).length < 2) {
+ if (_.size(source.match(/\bfreeModule\b/g)) < 2) {
source = removeVar(source, 'freeModule');
}
- if ((source.match(/\bfreeExports\b/g) || []).length < 2) {
+ if (_.size(source.match(/\bfreeExports\b/g)) < 2) {
source = removeVar(source, 'freeExports');
}
diff --git a/build/minify.js b/build/minify.js
index 2250669eeb..88acedb21d 100755
--- a/build/minify.js
+++ b/build/minify.js
@@ -2,24 +2,31 @@
;(function() {
'use strict';
- /** Load Node modules */
+ /** Load Node.js modules */
var fs = require('fs'),
https = require('https'),
path = require('path'),
spawn = require('child_process').spawn,
- zlib = require('zlib'),
- tar = require('../vendor/tar/tar.js'),
- _ = require('../lodash.js');
+ zlib = require('zlib');
/** Load other modules */
- var preprocess = require('./pre-compile.js'),
- postprocess = require('./post-compile.js');
+ var _ = require('../lodash.js'),
+ mkdirpSync = require('./mkdirp-sync.js'),
+ preprocess = require('./pre-compile.js'),
+ postprocess = require('./post-compile.js'),
+ tar = require('../vendor/tar/tar.js');
+
+ /** Add `fs.existsSync` for older versions of Node.js */
+ fs.existsSync || (fs.existsSync = path.existsSync);
+
+ /** Add `path.sep` for older versions of Node.js */
+ path.sep || (path.sep = process.platform == 'win32' ? '\\' : '/');
/** The Git object ID of `closure-compiler.tar.gz` */
- var closureId = '23cf67d0f0b979d97631fc108a2a43bb82225994';
+ var closureId = 'a95a8007892aa824ce90c6aa3d3abb0489bf0fff';
/** The Git object ID of `uglifyjs.tar.gz` */
- var uglifyId = 'a934fb18f8fa2768c6a68de44b6e035fe96a268b';
+ var uglifyId = '41308bd569db41a32d4f08af115875d0342e8bfb';
/** The path of the directory that is the base of the repository */
var basePath = fs.realpathSync(path.join(__dirname, '..'));
@@ -39,6 +46,9 @@
/** The media type for raw blob data */
var mediaType = 'application/vnd.github.v3.raw';
+ /** Used to detect the Node.js executable in command-line arguments */
+ var reNode = RegExp('(?:^|' + path.sep + ')node(?:\\.exe)?$');
+
/** Used to reference parts of the blob href */
var location = (function() {
var host = 'api.github.com',
@@ -59,9 +69,6 @@
'advanced': 'ADVANCED_OPTIMIZATIONS'
};
- /** Reassign `existsSync` for older versions of Node */
- fs.existsSync || (fs.existsSync = path.existsSync);
-
/*--------------------------------------------------------------------------*/
/**
@@ -96,7 +103,7 @@
options = source;
// used to report invalid command-line arguments
- var invalidArgs = _.reject(options.slice(options[0] == 'node' ? 2 : 0), function(value, index, options) {
+ var invalidArgs = _.reject(options.slice(reNode.test(options[0]) ? 2 : 0), function(value, index, options) {
if (/^(?:-o|--output)$/.test(options[index - 1]) ||
/^modes=.*$/.test(value)) {
return true;
@@ -125,9 +132,9 @@
return;
}
var filePath = options[options.length - 1],
- isMapped = options.indexOf('-p') > -1 || options.indexOf('--source-map') > -1,
- isSilent = options.indexOf('-s') > -1 || options.indexOf('--silent') > -1,
- isTemplate = options.indexOf('-t') > -1 || options.indexOf('--template') > -1,
+ isMapped = _.contains(options, '-p') || _.contains(options, '--source-map'),
+ isSilent = _.contains(options, '-s') || _.contains(options, '--silent'),
+ isTemplate = _.contains(options, '-t') || _.contains(options, '--template'),
outputPath = path.join(path.dirname(filePath), path.basename(filePath, '.js') + '.min.js');
modes = options.reduce(function(result, value) {
@@ -138,7 +145,9 @@
outputPath = options.reduce(function(result, value, index) {
if (/-o|--output/.test(value)) {
result = options[index + 1];
- result = path.join(fs.realpathSync(path.dirname(result)), path.basename(result));
+ var dirname = path.dirname(result);
+ mkdirpSync(dirname);
+ result = path.join(fs.realpathSync(dirname), path.basename(result));
}
return result;
}, outputPath);
@@ -238,9 +247,9 @@
};
// begin the minification process
- if (modes.indexOf('simple') > -1) {
+ if (_.contains(modes, 'simple')) {
closureCompile.call(this, source, 'simple', onClosureSimpleCompile.bind(this));
- } else if (modes.indexOf('advanced') > -1) {
+ } else if (_.contains(modes, 'advanced')) {
onClosureSimpleGzip.call(this);
} else {
onClosureAdvancedGzip.call(this);
@@ -353,7 +362,7 @@
source = source.replace(license, '');
}
- var hasIIFE = /^;?\(function[^{]+{\s*/.test(source),
+ var hasIIFE = /^;?\(function[^{]+{/.test(source),
isStrict = hasIIFE && /^;?\(function[^{]+{\s*["']use strict["']/.test(source);
// to avoid stripping the IIFE, convert it to a function call
@@ -446,6 +455,7 @@
toplevel.figure_out_scope();
toplevel = toplevel.transform(uglifyJS.Compressor({
'comparisons': false,
+ 'unsafe': true,
'unsafe_comps': true,
'warnings': false
}));
@@ -514,7 +524,7 @@
this.compiled.simple.gzip = result;
}
// compile the source using advanced optimizations
- if (this.modes.indexOf('advanced') > -1) {
+ if (_.contains(this.modes, 'advanced')) {
closureCompile.call(this, this.source, 'advanced', onClosureAdvancedCompile.bind(this));
} else {
onClosureAdvancedGzip.call(this);
@@ -601,10 +611,10 @@
}
// minify the already Closure Compiler simple optimized source using UglifyJS
var modes = this.modes;
- if (modes.indexOf('hybrid') > -1) {
- if (modes.indexOf('simple') > -1) {
+ if (_.contains(modes, 'hybrid')) {
+ if (_.contains(modes, 'simple')) {
uglify.call(this, this.compiled.simple.source, 'hybrid (simple)', onSimpleHybrid.bind(this));
- } else if (modes.indexOf('advanced') > -1) {
+ } else if (_.contains(modes, 'advanced')) {
onSimpleHybridGzip.call(this);
}
} else {
@@ -646,7 +656,7 @@
this.hybrid.simple.gzip = result;
}
// minify the already Closure Compiler advance optimized source using UglifyJS
- if (this.modes.indexOf('advanced') > -1) {
+ if (_.contains(this.modes, 'advanced')) {
uglify.call(this, this.compiled.advanced.source, 'hybrid (advanced)', onAdvancedHybrid.bind(this));
} else {
onComplete.call(this);
diff --git a/build/mkdirp-sync.js b/build/mkdirp-sync.js
new file mode 100755
index 0000000000..19e0f915de
--- /dev/null
+++ b/build/mkdirp-sync.js
@@ -0,0 +1,43 @@
+#!/usr/bin/env node
+;(function() {
+ 'use strict';
+
+ /** Load Node.js modules */
+ var fs = require('fs'),
+ path = require('path');
+
+ /** Add `path.sep` for older versions of Node.js */
+ path.sep || (path.sep = process.platform == 'win32' ? '\\' : '/');
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Makes the given `dirname` directory, without throwing errors for existing
+ * directories and making parent directories as needed.
+ *
+ * @param {String} dirname The path of the directory.
+ * @param {Number|String} [mode='0777'] The permission mode.
+ */
+ function mkdirpSync(dirname, mode) {
+ var sep = path.sep;
+
+ // ensure relative paths are prefixed with `./`
+ if (!RegExp('^\\.?' + sep).test(dirname)) {
+ dirname = '.' + sep + dirname;
+ }
+ dirname.split(sep).reduce(function(currPath, segment) {
+ currPath += sep + segment;
+ try {
+ currPath = fs.realpathSync(currPath);
+ } catch(e) {
+ fs.mkdirSync(currPath, mode);
+ }
+ return currPath;
+ });
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ // expose
+ module.exports = mkdirpSync;
+}());
diff --git a/build/post-compile.js b/build/post-compile.js
index db56866f83..1cd032b6bd 100644
--- a/build/post-compile.js
+++ b/build/post-compile.js
@@ -2,7 +2,7 @@
;(function() {
'use strict';
- /** The Node filesystem module */
+ /** The Node.js filesystem module */
var fs = require('fs');
/** The minimal license/copyright template */
@@ -30,7 +30,11 @@
// correct overly aggressive Closure Compiler advanced optimizations
source = source
.replace(/prototype\s*=\s*{\s*valueOf\s*:\s*1\s*}/, 'prototype={valueOf:1,y:1}')
- .replace(/(document[^&]+&&)\s*(?:\w+|!\d)/, '$1!({toString:0}+"")');
+ .replace(/(document[^&]+&&)\s*(?:\w+|!\d)/, '$1!({toString:0}+"")')
+
+ source = source.replace(/(\w+\.prototype\s*=\s*)\w+(?=\.prototype;)/, function(match, left) {
+ return left + /\w+(?=\.VERSION)/.exec(source);
+ });
// flip `typeof` expressions to help optimize Safari and
// correct the AMD module definition for AMD build optimizers
diff --git a/build/pre-compile.js b/build/pre-compile.js
index 94e1011b34..69ef77732b 100644
--- a/build/pre-compile.js
+++ b/build/pre-compile.js
@@ -2,7 +2,7 @@
;(function() {
'use strict';
- /** The Node filesystem module */
+ /** The Node.js filesystem module */
var fs = require('fs');
/** Used to minify variables embedded in compiled strings */
@@ -12,7 +12,6 @@
'argsLength',
'callback',
'collection',
- 'createCallback',
'ctor',
'guard',
'hasOwnProperty',
@@ -22,7 +21,8 @@
'isString',
'iterable',
'length',
- 'nativeKeys',
+ 'keys',
+ 'lodash',
'object',
'objectTypes',
'ownIndex',
@@ -33,21 +33,18 @@
'thisArg'
];
- /** Used to minify `compileIterator` option properties */
+ /** Used to minify `iteratorTemplate` data properties */
var iteratorOptions = [
'args',
'arrays',
'bottom',
'firstArg',
- 'hasDontEnumBug',
- 'hasEnumPrototype',
- 'isKeysFast',
+ 'init',
'loop',
- 'nonEnumArgs',
- 'noCharByIndex',
- 'shadowed',
+ 'shadowedProps',
'top',
- 'useHas'
+ 'useHas',
+ 'useKeys'
];
/** Used to minify variables and string values to a single character */
@@ -58,12 +55,25 @@
/** Used to protect the specified properties from getting minified */
var propWhitelist = [
+ 'Array',
+ 'Boolean',
+ 'Date',
+ 'Function',
+ 'Math',
+ 'Number',
+ 'Object',
+ 'RegExp',
+ 'String',
+ 'TypeError',
+ 'VERSION',
'_',
'__wrapped__',
'after',
'all',
'amd',
'any',
+ 'argsClass',
+ 'argsObject',
'assign',
'at',
'attachEvent',
@@ -78,6 +88,7 @@
'compose',
'contains',
'countBy',
+ 'createCallback',
'criteria',
'debounce',
'defaults',
@@ -87,14 +98,19 @@
'difference',
'drop',
'each',
+ 'enumPrototypes',
'environment',
'escape',
'evaluate',
'every',
'exports',
'extend',
+ 'fastBind',
+ 'fastKeys',
'filter',
'find',
+ 'findIndex',
+ 'findKey',
'first',
'flatten',
'foldl',
@@ -107,8 +123,8 @@
'groupBy',
'has',
'head',
- 'imports',
'identity',
+ 'imports',
'include',
'index',
'indexOf',
@@ -148,11 +164,15 @@
'min',
'mixin',
'noConflict',
+ 'nodeClass',
+ 'nonEnumArgs',
+ 'nonEnumShadows',
'object',
'omit',
'once',
- 'opera',
+ 'ownLast',
'pairs',
+ 'parseInt',
'partial',
'partialRight',
'pick',
@@ -164,6 +184,7 @@
'reject',
'rest',
'result',
+ 'runInContext',
'select',
'setImmediate',
'setTimeout',
@@ -173,6 +194,8 @@
'sortBy',
'sortedIndex',
'source',
+ 'spliceObjects',
+ 'support',
'tail',
'take',
'tap',
@@ -182,6 +205,7 @@
'times',
'toArray',
'unescape',
+ 'unindexedChars',
'union',
'uniq',
'unique',
@@ -189,11 +213,11 @@
'value',
'values',
'variable',
- 'VERSION',
'where',
'without',
'wrap',
'zip',
+ 'zipObject',
// properties used by the `backbone` and `underscore` builds
'__chain__',
@@ -226,7 +250,13 @@
return "['" + prop.replace(/['\n\r\t]/g, '\\$&') + "']";
});
- // remove brackets from `_.escape()` in `_.template`
+ // remove brackets from `lodash.createCallback` in `eachIteratorOptions`
+ source = source.replace('lodash[\'createCallback\'](callback, thisArg)"', 'lodash.createCallback(callback, thisArg)"');
+
+ // remove brackets from `lodash.createCallback` in `_.assign`
+ source = source.replace("' var callback = lodash['createCallback']", "'var callback=lodash.createCallback");
+
+ // remove brackets from `_.escape` in `_.template`
source = source.replace(/__e *= *_\['escape']/g, '__e=_.escape');
// remove brackets from `collection.indexOf` in `_.contains`
@@ -236,17 +266,17 @@
source = source.replace("result[length]['value']", 'result[length].value');
// remove whitespace from string literals
- source = source.replace(/^([ "'\w]+:)? *"[^"\\\n]*(?:\\.[^"\\\n]*)*"|'[^'\\\n]*(?:\\.[^'\\\n]*)*'/gm, function(string, captured) {
- // remove object literal property name
- if (/:$/.test(captured)) {
- string = string.slice(captured.length);
+ source = source.replace(/^((?:[ "'\w]+:)? *)"[^"\\\n]*(?:\\.[^"\\\n]*)*"|'[^'\\\n]*(?:\\.[^'\\\n]*)*'/gm, function(string, left) {
+ // clip after an object literal property name or leading spaces
+ if (left) {
+ string = string.slice(left.length);
}
// avoids removing the '\n' of the `stringEscapes` object
string = string.replace(/\[object |delete |else (?!{)|function | in |return\s+[\w"']|throw |typeof |use strict|var |@ |(["'])\\n\1|\\\\n|\\n|\s+/g, function(match) {
return match == false || match == '\\n' ? '' : match;
});
- // prepend object literal property name
- return (captured || '') + string;
+ // unclip
+ return (left || '') + string;
});
// remove whitespace from `_.template` related regexes
@@ -287,7 +317,9 @@
});
// replace with modified snippet
- source = source.replace(snippet, modified);
+ source = source.replace(snippet, function() {
+ return modified;
+ });
});
}());
@@ -297,7 +329,7 @@
// match the `iteratorTemplate`
'( +)var iteratorTemplate\\b[\\s\\S]+?\\n\\1}',
// match methods created by `createIterator` calls
- 'createIterator\\((?:{|[a-zA-Z]+)[\\s\\S]+?\\);\\n',
+ 'createIterator\\((?:{|[a-zA-Z]+)[\\s\\S]*?\\);\\n',
// match variables storing `createIterator` options
'( +)var [a-zA-Z]+IteratorOptions\\b[\\s\\S]+?\\n\\2}',
// match the the `createIterator` function
@@ -320,11 +352,6 @@
return "['" + prop.replace(/['\n\r\t]/g, '\\$&') + "']";
});
- if (isCreateIterator) {
- // clip before the `factory` call to avoid minifying its arguments
- source = source.replace(snippet, modified);
- snippet = modified = modified.replace(/return factory\([\s\S]+$/, '');
- }
// minify `createIterator` option property names
iteratorOptions.forEach(function(property, index) {
var minName = minNames[index];
@@ -336,20 +363,27 @@
});
// minify snippet variables / arguments
- compiledVars.forEach(function(variable, index) {
+ compiledVars.forEach(function(varName, index) {
var minName = minNames[index];
+ // minify variable names present in strings
+ if (isCreateIterator) {
+ modified = modified.replace(RegExp('(([\'"])[^\\n\\2]*?)\\b' + varName + '\\b(?=[^\\n\\2]*\\2[ ,+]+$)', 'gm'), '$1' + minName);
+ }
// ensure properties in compiled strings aren't minified
- modified = modified.replace(RegExp('([^.]\\b)' + variable + '\\b(?!\' *[\\]:])', 'g'), '$1' + minName);
-
+ else {
+ modified = modified.replace(RegExp('([^.])\\b' + varName + '\\b(?!\' *[\\]:])', 'g'), '$1' + minName);
+ }
// correct `typeof` values
- if (/^(?:boolean|function|object|number|string|undefined)$/.test(variable)) {
- modified = modified.replace(RegExp("(typeof [^']+')" + minName + "'", 'g'), '$1' + variable + "'");
+ if (/^(?:boolean|function|object|number|string|undefined)$/.test(varName)) {
+ modified = modified.replace(RegExp("(typeof [^']+')" + minName + "'", 'g'), '$1' + varName + "'");
}
});
// replace with modified snippet
- source = source.replace(snippet, modified);
+ source = source.replace(snippet, function() {
+ return modified;
+ });
});
return source;
diff --git a/dist/lodash.compat.js b/dist/lodash.compat.js
index 37ebc920be..eb0c9d966a 100644
--- a/dist/lodash.compat.js
+++ b/dist/lodash.compat.js
@@ -1,13 +1,16 @@
/**
* @license
- * Lo-Dash 1.0.1 (Custom Build)
+ * Lo-Dash 1.1.0 (Custom Build)
* Build: `lodash -o ./dist/lodash.compat.js`
* Copyright 2012-2013 The Dojo Foundation
* Based on Underscore.js 1.4.4
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
* Available under MIT license
*/
-;(function(window, undefined) {
+;(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;
@@ -21,39 +24,19 @@
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;
+ var indicatorObject = {};
/** 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 HTML entities */
+ var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g;
/**
* Used to match ES6 template delimiters
@@ -61,9 +44,15 @@
*/
var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
+ /** Used to match regexp flags from their coerced string values */
+ var reFlags = /\w*$/;
+
/** Used to match "interpolate" template delimiters */
var reInterpolate = /<%=([\s\S]+?)%>/g;
+ /** Used to match leading zeros to be removed */
+ var reLeadingZeros = /^0+(?=.$)/;
+
/** Used to ensure capturing order of template delimiters */
var reNoMatch = /($^)/;
@@ -73,8 +62,15 @@
/** 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'
+ ];
+
/** Used to fix the JScript [[DontEnum]] bug */
- var shadowed = [
+ var shadowedProps = [
'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
'toLocaleString', 'toString', 'valueOf'
];
@@ -82,25 +78,6 @@
/** 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]',
@@ -112,88 +89,6 @@
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;
@@ -202,16 +97,6 @@
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,
@@ -236,936 +121,1040 @@
/*--------------------------------------------------------------------------*/
/**
- * 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.
+ * Create a new `lodash` function using the given `context` object.
*
* @static
* @memberOf _
- * @type Object
+ * @category Utilities
+ * @param {Object} [context=window] The context object.
+ * @returns {Function} Returns the `lodash` function.
*/
- lodash.templateSettings = {
+ function runInContext(context) {
+ // Avoid issues with some ES3 environments that attempt to use values, named
+ // after built-in constructors like `Object`, for the creation of literals.
+ // ES5 clears this up by stating that literals must use built-in constructors.
+ // See http://es5.github.com/#x11.1.5.
+ context = context ? _.defaults(window.Object(), context, _.pick(window, contextProps)) : window;
+
+ /** Native constructor references */
+ var Array = context.Array,
+ Boolean = context.Boolean,
+ Date = context.Date,
+ 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 to restore the original `_` reference in `noConflict` */
+ var oldDash = context._;
+
+ /** Used to detect if a method is native */
+ var reNative = RegExp('^' +
+ String(objectRef.valueOf)
+ .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+ .replace(/valueOf|for [^\]]+/g, '.+?') + '$'
+ );
- /**
- * Used to detect `data` property values to be HTML-escaped.
- *
- * @memberOf _.templateSettings
- * @type RegExp
- */
- 'escape': /<%-([\s\S]+?)%>/g,
+ /** Native method shortcuts */
+ var ceil = Math.ceil,
+ clearTimeout = context.clearTimeout,
+ concat = arrayRef.concat,
+ floor = Math.floor,
+ getPrototypeOf = reNative.test(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf,
+ hasOwnProperty = objectRef.hasOwnProperty,
+ push = arrayRef.push,
+ setImmediate = context.setImmediate,
+ setTimeout = context.setTimeout,
+ 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 = context.isFinite,
+ nativeIsNaN = context.isNaN,
+ nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys,
+ nativeMax = Math.max,
+ nativeMin = Math.min,
+ nativeParseInt = context.parseInt,
+ nativeRandom = Math.random;
+
+ /** Detect various environments */
+ var isIeOpera = reNative.test(context.attachEvent),
+ isV8 = nativeBind && !/\n|true/.test(nativeBind + isIeOpera);
+
+ /** 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 detect code to be evaluated.
+ * Creates a `lodash` object, that wraps the given `value`, to enable method
+ * chaining.
*
- * @memberOf _.templateSettings
- * @type RegExp
- */
- 'evaluate': /<%([\s\S]+?)%>/g,
-
- /**
- * Used to detect `data` property values to inject.
+ * In addition to Lo-Dash methods, wrappers also have the following `Array` methods:
+ * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`,
+ * and `unshift`
+ *
+ * Chaining is supported in custom builds as long as the `value` method is
+ * implicitly or explicitly included in the build.
+ *
+ * The chainable wrapper functions are:
+ * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
+ * `compose`, `concat`, `countBy`, `createCallback`, `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`, `parseInt`,
+ * `pop`, `random`, `reduce`, `reduceRight`, `result`, `shift`, `size`, `some`,
+ * `sortedIndex`, `runInContext`, `template`, `unescape`, `uniqueId`, and `value`
*
- * @memberOf _.templateSettings
- * @type RegExp
+ * 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.
*/
- 'interpolate': reInterpolate,
+ function lodash(value) {
+ // don't wrap if already wrapped, even if wrapped by a different `lodash` constructor
+ return (value && typeof value == 'object' && !isArray(value) && hasOwnProperty.call(value, '__wrapped__'))
+ ? value
+ : new lodashWrapper(value);
+ }
/**
- * Used to reference the data object in the template text.
+ * An object used to flag environments features.
*
- * @memberOf _.templateSettings
- * @type String
+ * @static
+ * @memberOf _
+ * @type Object
*/
- 'variable': '',
+ var support = lodash.support = {};
+
+ (function() {
+ var ctor = function() { this.x = 1; },
+ object = { '0': 1, 'length': 1 },
+ props = [];
+
+ ctor.prototype = { 'valueOf': 1, 'y': 1 };
+ for (var prop in new ctor) { props.push(prop); }
+ for (prop in arguments) { }
+
+ /**
+ * Detect if `arguments` objects are `Object` objects (all but Opera < 10.5).
+ *
+ * @memberOf _.support
+ * @type Boolean
+ */
+ support.argsObject = arguments.constructor == Object;
+
+ /**
+ * Detect if an `arguments` object's [[Class]] is resolvable (all but Firefox < 4, IE < 9).
+ *
+ * @memberOf _.support
+ * @type Boolean
+ */
+ support.argsClass = isArguments(arguments);
+
+ /**
+ * 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');
+
+ /**
+ * Detect if `Function#bind` exists and is inferred to be fast (all but V8).
+ *
+ * @memberOf _.support
+ * @type Boolean
+ */
+ support.fastBind = nativeBind && !isV8;
+
+ /**
+ * Detect if own properties are iterated after inherited properties (all but IE < 9).
+ *
+ * @memberOf _.support
+ * @type Boolean
+ */
+ support.ownLast = props[0] != 'x';
+
+ /**
+ * Detect if `arguments` object indexes are non-enumerable
+ * (Firefox < 4, IE < 9, PhantomJS, Safari < 5.1).
+ *
+ * @memberOf _.support
+ * @type Boolean
+ */
+ support.nonEnumArgs = prop != 0;
+
+ /**
+ * Detect if properties shadowing those on `Object.prototype` are non-enumerable.
+ *
+ * In IE < 9 an objects own properties, shadowing non-enumerable ones, are
+ * made non-enumerable as well (a.k.a the JScript [[DontEnum]] bug).
+ *
+ * @memberOf _.support
+ * @type Boolean
+ */
+ support.nonEnumShadows = !/valueOf/.test(props);
+
+ /**
+ * Detect if `Array#shift` and `Array#splice` augment array-like objects correctly.
+ *
+ * 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.
+ *
+ * @memberOf _.support
+ * @type Boolean
+ */
+ support.spliceObjects = (arrayRef.splice.call(object, 0, 1), !object[0]);
+
+ /**
+ * 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.
+ *
+ * @memberOf _.support
+ * @type Boolean
+ */
+ support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx';
+
+ /**
+ * Detect if a DOM node's [[Class]] is resolvable (all but IE < 9)
+ * and that the JS engine errors when attempting to coerce an object to
+ * a string without a `toString` function.
+ *
+ * @memberOf _.support
+ * @type Boolean
+ */
+ try {
+ support.nodeClass = !(toString.call(document) == objectClass && !({ 'toString': 0 } + ''));
+ } catch(e) {
+ support.nodeClass = true;
+ }
+ }(1));
/**
- * Used to import variables into the compiled template.
+ * 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.
*
- * @memberOf _.templateSettings
+ * @static
+ * @memberOf _
* @type Object
*/
- 'imports': {
+ lodash.templateSettings = {
/**
- * A reference to the `lodash` function.
+ * Used to detect `data` property values to be HTML-escaped.
*
- * @memberOf _.templateSettings.imports
- * @type Function
+ * @memberOf _.templateSettings
+ * @type RegExp
*/
- '_': lodash
- }
- };
+ 'escape': /<%-([\s\S]+?)%>/g,
- /*--------------------------------------------------------------------------*/
+ /**
+ * Used to detect code to be evaluated.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'evaluate': /<%([\s\S]+?)%>/g,
- /**
- * 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
- };
+ /**
+ * Used to detect `data` property values to inject.
+ *
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'interpolate': reInterpolate,
- /** 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}'
- };
+ /**
+ * Used to reference the data object in the template text.
+ *
+ * @memberOf _.templateSettings
+ * @type String
+ */
+ 'variable': '',
- /** 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'
- };
+ /**
+ * 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
+ }
+ };
- /** Reusable iterator options for `forIn` and `forOwn` */
- var forOwnIteratorOptions = {
- 'top': 'if (!objectTypes[typeof iterable]) return result;\n' + eachIteratorOptions.top,
- 'arrays': false
- };
+ /*--------------------------------------------------------------------------*/
- /*--------------------------------------------------------------------------*/
+ /**
+ * 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 = ' +
+ (obj.init) +
+ ';\nif (!iterable) return result;\n' +
+ (obj.top) +
+ ';\n';
+ if (obj.arrays) {
+ __p += 'var length = iterable.length; index = -1;\nif (' +
+ (obj.arrays) +
+ ') { ';
+ 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) {
+ __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 (support.enumPrototypes) {
+ __p += '\n var skipProto = typeof iterable == \'function\';\n ';
+ }
+
+ 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 +=
+ (obj.loop);
+ if (support.enumPrototypes) {
+ __p += '}\n';
+ }
+ __p += ' } ';
+ } 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 += ') { ';
+ }
+ __p +=
+ (obj.loop) +
+ '; ';
+ if (support.enumPrototypes || obj.useHas) {
+ __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 = \'' +
+ (obj.shadowedProps[k]) +
+ '\';\n if (';
+ if (obj.shadowedProps[k] == 'constructor') {
+ __p += '!(ctor && ctor.prototype === iterable) && ';
+ }
+ __p += 'hasOwnProperty.call(iterable, index)) {\n ' +
+ (obj.loop) +
+ '\n } ';
+ }
- /**
- * 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;
+ if (obj.arrays || support.nonEnumArgs) {
+ __p += '\n}';
+ }
+ __p +=
+ (obj.bottom) +
+ ';\nreturn result';
- 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;
- }
- }
+ return __p
+ };
- /**
- * 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);
- }
+ /** 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}'
+ };
- /**
- * 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;
- }
+ /** 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'",
+ 'loop': 'if (callback(iterable[index], index, collection) === false) return result'
+ };
- /**
- * 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;
- }
+ /** Reusable iterator options for `forIn` and `forOwn` */
+ var forOwnIteratorOptions = {
+ 'top': 'if (!objectTypes[typeof iterable]) return result;\n' + eachIteratorOptions.top,
+ 'arrays': false
+ };
- 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;
+ /**
+ * 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 The index to search from.
+ * @param {Number} largeSize The length at which an array is considered large.
+ * @returns {Boolean} Returns `true`, if `value` is found, else `false`.
+ */
+ function cachedContains(array, fromIndex, largeSize) {
+ var length = array.length,
+ isLarge = (length - fromIndex) >= largeSize;
+
+ 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 = String(array[index]);
+ (hasOwnProperty.call(cache, key) ? cache[key] : (cache[key] = [])).push(array[index]);
+ }
}
- 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 function(value) {
+ if (isLarge) {
+ var key = String(value);
+ return hasOwnProperty.call(cache, key) && indexOf(cache[key], value) > -1;
+ }
+ return indexOf(array, value, fromIndex) > -1;
}
- 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;
+ /**
+ * 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);
}
- 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;
- }
+
+ /**
+ * 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;
}
- return result;
- };
+ if (a < b || typeof b == 'undefined') {
+ return -1;
+ }
+ }
+ return ai < bi ? -1 : 1;
}
- if (typeof thisArg != 'undefined') {
- if (argCount === 1) {
- return function(value) {
- return func.call(thisArg, value);
- };
+
+ /**
+ * 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} [idicator] Used to indicate binding by key or partially
+ * applying arguments from the right.
+ * @returns {Function} Returns the new bound function.
+ */
+ function createBound(func, thisArg, partialArgs, indicator) {
+ var isFunc = isFunction(func),
+ isPartial = !partialArgs,
+ key = thisArg;
+
+ // juggle arguments
+ if (isPartial) {
+ var rightIndicator = indicator;
+ partialArgs = thisArg;
}
- if (argCount === 2) {
- return function(a, b) {
- return func.call(thisArg, a, b);
- };
+ else if (!isFunc) {
+ if (!indicator) {
+ throw new TypeError;
+ }
+ thisArg = func;
}
- if (argCount === 4) {
- return function(accumulator, value, index, object) {
- return func.call(thisArg, accumulator, value, index, object);
- };
+
+ 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 `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 function(value, index, object) {
- return func.call(thisArg, value, index, object);
- };
+ return bound;
}
- 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
- };
+ /**
+ * 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 = {
+ // data properties
+ 'shadowedProps': shadowedProps,
+ // iterator options
+ 'arrays': 'isArray(iterable)',
+ 'bottom': '',
+ 'init': 'iterable',
+ 'loop': '',
+ 'top': '',
+ 'useHas': true,
+ '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];
+ // 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(
+ 'hasOwnProperty, isArguments, isArray, isString, keys, ' +
+ 'lodash, objectTypes',
+ 'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}'
+ );
+ // return the compiled function
+ return factory(
+ hasOwnProperty, isArguments, isArray, isString, keys,
+ lodash, objectTypes
+ );
}
- 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 `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];
+ }
- /**
- * 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;
+ /**
+ * 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';
}
- var index = -1,
- length = end - start || 0,
- result = Array(length < 0 ? 0 : length);
- while (++index < length) {
- result[index] = array[start + index];
+ /**
+ * 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;
}
- return result;
- }
+ // ensure `new lodashWrapper` is an instance of `lodash`
+ lodashWrapper.prototype = lodash.prototype;
- /**
- * 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];
- }
+ /**
+ * A no-operation function.
+ *
+ * @private
+ */
+ function noop() {
+ // no operation performed
+ }
- /*--------------------------------------------------------------------------*/
+ /**
+ * 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 && 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;
+
+ 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;
+ });
+ return result === false || hasOwnProperty.call(value, result);
+ }
+ return result;
+ }
- /**
- * 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;
- };
- }
+ /**
+ * 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);
- /**
- * 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
- });
+ while (++index < length) {
+ result[index] = array[start + index];
+ }
+ return result;
+ }
- /**
- * 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);
+ /**
+ * 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 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 [];
+ /**
+ * 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;
}
- if ((hasEnumPrototype && typeof object == 'function') ||
- (nonEnumArgs && object.length && isArguments(object))) {
- return shimKeys(object);
+ // fallback for browsers that can't detect `arguments` objects by [[Class]]
+ if (!support.argsClass) {
+ isArguments = function(value) {
+ return value ? hasOwnProperty.call(value, 'callee') : false;
+ };
}
- 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;
- }
+ /**
+ * 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 (support.argsObject && value instanceof Array) || toString.call(value) == arrayClass;
+ };
- /**
- * 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);
+ /**
+ * A fallback implementation of `Object.keys` that produces an array of the
+ * given object's own enumerable property names.
+ *
+ * @private
+ * @type Function
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns a new array of property names.
+ */
+ var shimKeys = createIterator({
+ 'args': 'object',
+ 'init': '[]',
+ 'top': 'if (!(objectTypes[typeof object])) return result',
+ 'loop': 'result.push(index)',
+ 'arrays': false
});
- 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 = {
- '&': '&',
- '<': '<',
- '>': '>',
- '"': '"',
- "'": '''
- };
+ /**
+ * 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 ((support.enumPrototypes && typeof object == 'function') ||
+ (support.nonEnumArgs && object.length && isArguments(object))) {
+ return shimKeys(object);
+ }
+ return nativeKeys(object);
+ };
- /** Used to convert HTML entities to characters */
- var htmlUnescapes = invert(htmlEscapes);
+ /**
+ * 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 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 = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": '''
+ };
- /**
- * 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]'
- });
+ /** Used to convert HTML entities to characters */
+ var htmlUnescapes = invert(htmlEscapes);
- /**
- * 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);
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Assigns own enumerable properties of source object(s) to the destination
+ * object. Subsequent sources will overwrite property 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 = lodash.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]'
+ });
- var done = typeof result != 'undefined';
- if (!done) {
+ /**
+ * 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=[]] Tracks traversed source objects.
+ * @param- {Array} [stackB=[]] Associates 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
+ : lodash.createCallback(callback, thisArg, 1);
+
+ result = callback(result);
+ if (typeof result != 'undefined') {
+ return result;
+ }
result = value;
}
- }
- // inspect [[Class]]
- var isObj = isObject(result);
- if (isObj) {
- var className = toString.call(result);
- if (!cloneableClasses[className] || (noNodeClass && isNode(result))) {
- return result;
+ // inspect [[Class]]
+ var isObj = isObject(result);
+ if (isObj) {
+ var className = toString.call(result);
+ if (!cloneableClasses[className] || (!support.nodeClass && isNode(result))) {
+ return result;
+ }
+ var isArr = isArray(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];
+ // shallow clone
+ if (!isObj || !deep) {
+ return isObj
+ ? (isArr ? slice(result) : assign({}, result))
+ : result;
}
- }
- // init cloned object
- if (!done) {
+ var ctor = ctorByClass[className];
+ switch (className) {
+ case boolClass:
+ case dateClass:
+ return new ctor(+result);
+
+ case numberClass:
+ case stringClass:
+ return new ctor(result);
+
+ case regexpClass:
+ return 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
result = isArr ? ctor(result.length) : {};
// add array properties assigned by `RegExp#exec`
@@ -1177,3976 +1166,4273 @@
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)(value, function(objValue, key) {
+ result[key] = clone(objValue, deep, callback, undefined, stackA, stackB);
+ });
+
+ return result;
}
- // 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
+ * `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
+ * 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) : undefined;
+ * });
+ *
+ * clone.node == view.node;
+ * // => false
+ */
+ function cloneDeep(value, callback, thisArg) {
+ return clone(value, true, callback, thisArg);
+ }
- /**
- * 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] Allows 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);
- /**
- * 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);
+ /**
+ * This method is similar to `_.find`, except that it returns the key of the
+ * element that passes the callback check, instead of the element itself.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @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.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Mixed} Returns the key of the found element, else `undefined`.
+ * @example
+ *
+ * _.findKey({ 'a': 1, 'b': 2, 'c': 3, 'd': 4 }, function(num) { return num % 2 == 0; });
+ * // => 'b'
+ */
+ function findKey(collection, callback, thisArg) {
+ var result;
+ callback = lodash.createCallback(callback, thisArg);
+ forOwn(collection, function(value, key, collection) {
+ if (callback(value, key, collection)) {
+ result = key;
+ return false;
+ }
+ });
+ 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);
- }
+ /**
+ * 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
});
- 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;
- }
+ /**
+ * 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);
- /**
- * 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;
+ /**
+ * 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();
}
- 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 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;
+ }
- /**
- * 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;
- }
+ /**
+ * 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' }
+ */
+ function invert(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = {};
- /**
- * 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) {
+ while (++index < length) {
+ var key = props[index];
+ result[object[key]] = key;
+ }
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;
+ /**
+ * 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;
}
- 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;
+ /**
+ * 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;
}
- // 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;
+ /**
+ * 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;
}
- 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 + '';
+
+ /**
+ * 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 ||
+ (support.argsClass ? className == argsClass : isArguments(value))) ||
+ (className == objectClass && typeof length == 'number' && isFunction(value.splice))) {
+ return !length;
+ }
+ forOwn(value, function() {
+ return (result = false);
+ });
+ return result;
}
- 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);
+
+ /**
+ * 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- {Array} [stackA=[]] Tracks traversed `a` objects.
+ * @param- {Array} [stackB=[]] Tracks traversed `b` objects.
+ * @returns {Boolean} Returns `true`, if the values are equivalent, 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
+ : lodash.createCallback(callback, thisArg, 2);
+
+ var result = callback(a, b);
+ if (typeof result != 'undefined') {
+ return !!result;
+ }
}
- // exit for functions and DOM nodes
- if (className != objectClass || (noNodeClass && (isNode(a) || isNode(b)))) {
+ // 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;
}
- // in older versions of Opera, `arguments` objects have `Array` constructors
- var ctorA = !argsAreObjects && isArguments(a) ? Object : a.constructor,
- ctorB = !argsAreObjects && isArguments(b) ? Object : 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
- )) {
+ // 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;
}
- }
- // 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;
+ 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 == String(b);
}
- }
- var size = 0;
- result = true;
+ var isArr = className == arrayClass;
+ if (!isArr) {
+ // unwrap any `lodash` wrapped values
+ if (hasOwnProperty.call(a, '__wrapped__ ') || hasOwnProperty.call(b, '__wrapped__')) {
+ return isEqual(a.__wrapped__ || a, b.__wrapped__ || b, callback, thisArg, stackA, stackB);
+ }
+ // exit for functions and DOM nodes
+ if (className != objectClass || (!support.nodeClass && (isNode(a) || isNode(b)))) {
+ return false;
+ }
+ // in older versions of Opera, `arguments` objects have `Array` constructors
+ var ctorA = !support.argsObject && isArguments(a) ? Object : a.constructor,
+ ctorB = !support.argsObject && isArguments(b) ? Object : 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 = []);
- // add `a` and `b` to the stack of traversed objects
- stackA.push(a);
- stackB.push(b);
+ var length = stackA.length;
+ while (length--) {
+ if (stackA[length] == a) {
+ return stackB[length] == b;
+ }
+ }
+ var size = 0;
+ result = true;
- // recursively compare objects and arrays (susceptible to call stack limits)
- if (isArr) {
- length = a.length;
- size = b.length;
+ // add `a` and `b` to the stack of traversed objects
+ stackA.push(a);
+ stackB.push(b);
- // compare lengths to determine if a deep comparison is necessary
- result = size == a.length;
- if (!result && !whereIndicator) {
- return result;
- }
- // deep compare the contents, ignoring non-numeric properties
- while (size--) {
- var index = length,
- value = b[size];
-
- if (whereIndicator) {
- while (index--) {
- if ((result = isEqual(a[index], value, callback, thisArg, stackA, stackB))) {
- break;
+ // recursively compare objects and arrays (susceptible to call stack limits)
+ if (isArr) {
+ length = a.length;
+ size = b.length;
+
+ // compare lengths to determine if a deep comparison is necessary
+ result = size == a.length;
+ if (!result && !whereIndicator) {
+ return result;
+ }
+ // deep compare the contents, ignoring non-numeric properties
+ while (size--) {
+ var index = length,
+ value = b[size];
+
+ if (whereIndicator) {
+ while (index--) {
+ if ((result = isEqual(a[index], value, callback, thisArg, stackA, stackB))) {
+ break;
+ }
}
+ } else if (!(result = isEqual(a[size], value, callback, thisArg, stackA, stackB))) {
+ break;
}
- } else if (!(result = isEqual(a[size], value, callback, thisArg, stackA, stackB))) {
- break;
}
+ return result;
+ }
+ // deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys`
+ // which, in this case, is more costly
+ forIn(b, function(value, key, b) {
+ if (hasOwnProperty.call(b, key)) {
+ // count the number of properties.
+ size++;
+ // deep compare each property value.
+ return (result = hasOwnProperty.call(a, key) && isEqual(a[key], value, callback, thisArg, stackA, stackB));
+ }
+ });
+
+ if (result && !whereIndicator) {
+ // ensure both objects have the same number of properties
+ forIn(a, function(value, key, a) {
+ if (hasOwnProperty.call(a, key)) {
+ // `size` will be `-1` if `a` has more properties than `b`
+ return (result = --size > -1);
+ }
+ });
}
return result;
}
- // deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys`
- // which, in this case, is more costly
- forIn(b, function(value, key, b) {
- if (hasOwnProperty.call(b, key)) {
- // count the number of properties.
- size++;
- // deep compare each property value.
- return (result = hasOwnProperty.call(a, key) && isEqual(a[key], value, callback, thisArg, stackA, stackB));
- }
- });
- if (result && !whereIndicator) {
- // ensure both objects have the same number of properties
- forIn(a, function(value, key, a) {
- if (hasOwnProperty.call(a, key)) {
- // `size` will be `-1` if `a` has more properties than `b`
- return (result = --size > -1);
- }
- });
+ /**
+ * 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.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true`, if the `value` is finite, else `false`.
+ * @example
+ *
+ * _.isFinite(-101);
+ * // => true
+ *
+ * _.isFinite('10');
+ * // => true
+ *
+ * _.isFinite(true);
+ * // => false
+ *
+ * _.isFinite('');
+ * // => false
+ *
+ * _.isFinite(Infinity);
+ * // => false
+ */
+ function isFinite(value) {
+ return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value));
}
- return result;
- }
- /**
- * 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.
- *
- * @static
- * @memberOf _
- * @category Objects
- * @param {Mixed} value The value to check.
- * @returns {Boolean} Returns `true`, if the `value` is finite, else `false`.
- * @example
- *
- * _.isFinite(-101);
- * // => true
- *
- * _.isFinite('10');
- * // => true
- *
- * _.isFinite(true);
- * // => false
- *
- * _.isFinite('');
- * // => false
- *
- * _.isFinite(Infinity);
- * // => false
- */
- function isFinite(value) {
- return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value));
- }
+ /**
+ * 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 value instanceof Function || toString.call(value) == funcClass;
+ };
+ }
- /**
- * 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 value instanceof Function || 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 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.
+ *
+ * @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 isNumber(value) && value != +value
+ }
- /**
- * Checks if `value` is `NaN`.
- *
- * Note: This is not the same as native `isNaN`, which will return `true` for
- * `undefined` and other values. See http://es5.github.com/#x15.1.2.4.
- *
- * @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 isNumber(value) && value != +value
- }
+ /**
+ * Checks if `value` is `null`.
+ *
+ * @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 `null`.
- *
- * @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 typeof value == 'number' || toString.call(value) == numberClass;
+ }
- /**
- * 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 typeof value == 'number' || toString.call(value) == numberClass;
- }
-
- /**
- * Checks if a given `value` is an object created by the `Object` constructor.
- *
- * @static
- * @memberOf _
- * @category Objects
- * @param {Mixed} value The value to check.
- * @returns {Boolean} Returns `true`, if `value` is a plain object, else `false`.
- * @example
- *
- * function Stooge(name, age) {
- * this.name = name;
- * this.age = age;
- * }
- *
- * _.isPlainObject(new Stooge('moe', 40));
- * // => false
- *
- * _.isPlainObject([1, 2, 3]);
- * // => false
- *
- * _.isPlainObject({ 'name': 'moe', 'age': 40 });
- * // => true
- */
- var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
- if (!(value && typeof value == 'object')) {
- return false;
- }
- var valueOf = value.valueOf,
- objProto = typeof valueOf == 'function' && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
+ /**
+ * Checks if a given `value` is an object created by the `Object` constructor.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true`, if `value` is a plain object, else `false`.
+ * @example
+ *
+ * function Stooge(name, age) {
+ * this.name = name;
+ * this.age = age;
+ * }
+ *
+ * _.isPlainObject(new Stooge('moe', 40));
+ * // => false
+ *
+ * _.isPlainObject([1, 2, 3]);
+ * // => false
+ *
+ * _.isPlainObject({ 'name': 'moe', 'age': 40 });
+ * // => true
+ */
+ var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
+ if (!(value && toString.call(value) == objectClass) || (!support.argsClass && isArguments(value))) {
+ return false;
+ }
+ var valueOf = value.valueOf,
+ objProto = typeof valueOf == 'function' && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
- return objProto
- ? value == objProto || (getPrototypeOf(value) == objProto && !isArguments(value))
- : shimIsPlainObject(value);
- };
+ return objProto
+ ? (value == objProto || getPrototypeOf(value) == objProto)
+ : shimIsPlainObject(value);
+ };
- /**
- * 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 value instanceof RegExp || toString.call(value) == regexpClass;
- }
+ /**
+ * 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 value instanceof RegExp || 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 typeof value == 'string' || toString.call(value) == stringClass;
- }
+ /**
+ * 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 typeof value == 'string' || toString.call(value) == stringClass;
+ }
- /**
- * Checks if `value` is `undefined`.
- *
- * @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 typeof value == 'undefined';
- }
+ /**
+ * Checks if `value` is `undefined`.
+ *
+ * @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 typeof value == 'undefined';
+ }
- /**
- * Recursively merges own enumerable properties of the source object(s), that
- * don't resolve to `undefined`, into 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 merged values of the destination
- * and source properties. If `callback` returns `undefined`, merging will be
- * handled by the method instead. The `callback` is bound to `thisArg` and
- * invoked with two arguments; (objectValue, sourceValue).
- *
- * @static
- * @memberOf _
- * @category Objects
- * @param {Object} object The destination object.
- * @param {Object} [source1, source2, ...] The source objects.
- * @param {Function} [callback] The function to customize merging properties.
- * @param {Mixed} [thisArg] The `this` binding of `callback`.
- * @param- {Object} [deepIndicator] Internally used to indicate that `stackA`
- * and `stackB` are arrays of traversed objects instead of source objects.
- * @param- {Array} [stackA=[]] Internally used to track traversed source objects.
- * @param- {Array} [stackB=[]] Internally used to associate values with their
- * source counterparts.
- * @returns {Object} Returns the destination object.
- * @example
- *
- * var names = {
- * 'stooges': [
- * { 'name': 'moe' },
- * { 'name': 'larry' }
- * ]
- * };
- *
- * var ages = {
- * 'stooges': [
- * { 'age': 40 },
- * { 'age': 50 }
- * ]
- * };
- *
- * _.merge(names, ages);
- * // => { 'stooges': [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }] }
- *
- * var food = {
- * 'fruits': ['apple'],
- * 'vegetables': ['beet']
- * };
- *
- * var otherFood = {
- * 'fruits': ['banana'],
- * 'vegetables': ['carrot']
- * };
- *
- * _.merge(food, otherFood, function(a, b) {
- * return _.isArray(a) ? a.concat(b) : undefined;
- * });
- * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] }
- */
- function merge(object, source, deepIndicator) {
- var args = arguments,
- index = 0,
- length = 2;
+ /**
+ * Recursively merges own enumerable properties of the source object(s), that
+ * don't resolve to `undefined`, into the destination object. Subsequent sources
+ * will overwrite property assignments of previous sources. If a `callback` function
+ * is passed, it will be executed to produce the merged values of the destination
+ * and source properties. If `callback` returns `undefined`, merging will be
+ * handled by the method instead. The `callback` is bound to `thisArg` and
+ * invoked with two arguments; (objectValue, sourceValue).
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The destination object.
+ * @param {Object} [source1, source2, ...] The source objects.
+ * @param {Function} [callback] The function to customize merging properties.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @param- {Object} [deepIndicator] Indicates that `stackA` and `stackB` are
+ * arrays of traversed objects, instead of source objects.
+ * @param- {Array} [stackA=[]] Tracks traversed source objects.
+ * @param- {Array} [stackB=[]] Associates values with source counterparts.
+ * @returns {Object} Returns the destination object.
+ * @example
+ *
+ * var names = {
+ * 'stooges': [
+ * { 'name': 'moe' },
+ * { 'name': 'larry' }
+ * ]
+ * };
+ *
+ * var ages = {
+ * 'stooges': [
+ * { 'age': 40 },
+ * { 'age': 50 }
+ * ]
+ * };
+ *
+ * _.merge(names, ages);
+ * // => { 'stooges': [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }] }
+ *
+ * var food = {
+ * 'fruits': ['apple'],
+ * 'vegetables': ['beet']
+ * };
+ *
+ * var otherFood = {
+ * 'fruits': ['banana'],
+ * 'vegetables': ['carrot']
+ * };
+ *
+ * _.merge(food, otherFood, function(a, b) {
+ * return _.isArray(a) ? a.concat(b) : undefined;
+ * });
+ * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] }
+ */
+ function merge(object, source, deepIndicator) {
+ var args = arguments,
+ index = 0,
+ length = 2;
- if (!isObject(object)) {
- return object;
- }
- if (deepIndicator === indicatorObject) {
- var callback = args[3],
- stackA = args[4],
- stackB = args[5];
- } else {
- stackA = [];
- stackB = [];
-
- // allows working with `_.reduce` and `_.reduceRight` without
- // using their `callback` arguments, `index|key` and `collection`
- if (typeof deepIndicator != 'number') {
- length = args.length;
+ if (!isObject(object)) {
+ return object;
}
- if (length > 3 && typeof args[length - 2] == 'function') {
- callback = createCallback(args[--length - 1], args[length--], 2);
- } else if (length > 2 && typeof args[length - 1] == 'function') {
- callback = args[--length];
+ if (deepIndicator === indicatorObject) {
+ var callback = args[3],
+ stackA = args[4],
+ stackB = args[5];
+ } else {
+ stackA = [];
+ stackB = [];
+
+ // allows working with `_.reduce` and `_.reduceRight` without
+ // using their `callback` arguments, `index|key` and `collection`
+ if (typeof deepIndicator != 'number') {
+ length = args.length;
+ }
+ if (length > 3 && typeof args[length - 2] == 'function') {
+ callback = lodash.createCallback(args[--length - 1], args[length--], 2);
+ } else if (length > 2 && typeof args[length - 1] == 'function') {
+ callback = args[--length];
+ }
}
- }
- while (++index < length) {
- (isArray(args[index]) ? forEach : forOwn)(args[index], function(source, key) {
- var found,
- isArr,
- result = source,
- value = object[key];
-
- if (source && ((isArr = isArray(source)) || isPlainObject(source))) {
- // avoid merging previously merged cyclic sources
- var stackLength = stackA.length;
- while (stackLength--) {
- if ((found = stackA[stackLength] == source)) {
- value = stackB[stackLength];
- break;
+ while (++index < length) {
+ (isArray(args[index]) ? forEach : forOwn)(args[index], function(source, key) {
+ var found,
+ isArr,
+ result = source,
+ value = object[key];
+
+ if (source && ((isArr = isArray(source)) || isPlainObject(source))) {
+ // avoid merging previously merged cyclic sources
+ var stackLength = stackA.length;
+ while (stackLength--) {
+ if ((found = stackA[stackLength] == source)) {
+ value = stackB[stackLength];
+ break;
+ }
}
- }
- if (!found) {
- value = isArr
- ? (isArray(value) ? value : [])
- : (isPlainObject(value) ? value : {});
+ if (!found) {
+ value = isArr
+ ? (isArray(value) ? value : [])
+ : (isPlainObject(value) ? value : {});
+
+ if (callback) {
+ result = callback(value, source);
+ if (typeof result != 'undefined') {
+ value = result;
+ }
+ }
+ // add `source` and associated `value` to the stack of traversed objects
+ stackA.push(source);
+ stackB.push(value);
+ // recursively merge objects and arrays (susceptible to call stack limits)
+ if (!callback) {
+ value = merge(value, source, indicatorObject, callback, stackA, stackB);
+ }
+ }
+ }
+ else {
if (callback) {
result = callback(value, source);
- if (typeof result != 'undefined') {
- value = result;
+ if (typeof result == 'undefined') {
+ result = source;
}
}
- // add `source` and associated `value` to the stack of traversed objects
- stackA.push(source);
- stackB.push(value);
-
- // recursively merge objects and arrays (susceptible to call stack limits)
- if (!callback) {
- value = merge(value, source, indicatorObject, callback, stackA, stackB);
- }
- }
- }
- else {
- if (callback) {
- result = callback(value, source);
- if (typeof result == 'undefined') {
- result = source;
+ if (typeof result != 'undefined') {
+ value = result;
}
}
- if (typeof result != 'undefined') {
- value = result;
- }
- }
- object[key] = value;
- });
- }
- return 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 a `callback` function 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 }, 'age');
- * // => { 'name': 'moe' }
- *
- * _.omit({ 'name': 'moe', 'age': 40 }, function(value) {
- * return typeof value == 'number';
- * });
- * // => { 'name': 'moe' }
- */
- function omit(object, callback, thisArg) {
- var isFunc = typeof callback == 'function',
- result = {};
-
- if (isFunc) {
- callback = createCallback(callback, thisArg);
- } else {
- var props = concat.apply(arrayRef, arguments);
- }
- forIn(object, function(value, key, object) {
- if (isFunc
- ? !callback(value, key, object)
- : indexOf(props, key, 1) < 0
- ) {
- result[key] = value;
+ object[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 });
- * // => [['moe', 30], ['larry', 40]] (order is not guaranteed)
- */
- function pairs(object) {
- var index = -1,
- props = keys(object),
- length = props.length,
- result = Array(length);
-
- while (++index < length) {
- var key = props[index];
- result[index] = [key, object[key]];
+ return object;
}
- 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 {Array|Function|String} callback|[prop1, prop2, ...] The function called
- * per iteration or properties to pick, either as individual arguments or arrays.
- * @param {Mixed} [thisArg] The `this` binding of `callback`.
- * @returns {Object} Returns an object composed of the picked properties.
- * @example
- *
- * _.pick({ 'name': 'moe', '_userid': 'moe1' }, 'name');
- * // => { 'name': 'moe' }
- *
- * _.pick({ 'name': 'moe', '_userid': 'moe1' }, function(value, key) {
- * return key.charAt(0) != '_';
- * });
- * // => { 'name': 'moe' }
- */
- function pick(object, callback, thisArg) {
- var result = {};
- if (typeof callback != 'function') {
- var index = 0,
- props = concat.apply(arrayRef, arguments),
- length = isObject(object) ? props.length : 0;
+ /**
+ * 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 a `callback` function 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 }, 'age');
+ * // => { 'name': 'moe' }
+ *
+ * _.omit({ 'name': 'moe', 'age': 40 }, function(value) {
+ * return typeof value == 'number';
+ * });
+ * // => { 'name': 'moe' }
+ */
+ function omit(object, callback, thisArg) {
+ var isFunc = typeof callback == 'function',
+ result = {};
- while (++index < length) {
- var key = props[index];
- if (key in object) {
- result[key] = object[key];
- }
+ if (isFunc) {
+ callback = lodash.createCallback(callback, thisArg);
+ } else {
+ var props = concat.apply(arrayRef, arguments);
}
- } else {
- callback = createCallback(callback, thisArg);
forIn(object, function(value, key, object) {
- if (callback(value, key, object)) {
+ if (isFunc
+ ? !callback(value, key, object)
+ : indexOf(props, key, 1) < 0
+ ) {
result[key] = value;
}
});
+ return result;
}
- 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 index = -1,
- props = keys(object),
- length = props.length,
- result = Array(length);
-
- while (++index < length) {
- result[index] = object[props[index]];
- }
- 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 });
+ * // => [['moe', 30], ['larry', 40]] (order is not guaranteed)
+ */
+ function pairs(object) {
+ var index = -1,
+ props = keys(object),
+ length = props.length,
+ result = Array(length);
- /**
- * Creates an array of elements from the specified indexes, or keys, of the
- * `collection`. Indexes may be specified as individual arguments or as arrays
- * of indexes.
- *
- * @static
- * @memberOf _
- * @category Collections
- * @param {Array|Object|String} collection The collection to iterate over.
- * @param {Array|Number|String} [index1, index2, ...] The indexes of
- * `collection` to retrieve, either as individual arguments or arrays.
- * @returns {Array} Returns a new array of elements corresponding to the
- * provided indexes.
- * @example
- *
- * _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]);
- * // => ['a', 'c', 'e']
- *
- * _.at(['moe', 'larry', 'curly'], 0, 2);
- * // => ['moe', 'curly']
- */
- function at(collection) {
- var index = -1,
- props = concat.apply(arrayRef, slice(arguments, 1)),
- length = props.length,
- result = Array(length);
-
- if (noCharByIndex && isString(collection)) {
- collection = collection.split('');
- }
- while(++index < length) {
- result[index] = collection[props[index]];
+ while (++index < length) {
+ var key = props[index];
+ result[index] = [key, object[key]];
+ }
+ return result;
}
- 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, fromIndex) {
- var index = -1,
- length = collection ? collection.length : 0,
- result = false;
-
- fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0;
- if (typeof length == 'number') {
- result = (isString(collection)
- ? collection.indexOf(target, fromIndex)
- : indexOf(collection, target, fromIndex)
- ) > -1;
- } else {
- each(collection, function(value) {
- if (++index >= fromIndex) {
- return !(result = value === target);
+ /**
+ * 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 {Array|Function|String} callback|[prop1, prop2, ...] The function called
+ * per iteration or properties to pick, either as individual arguments or arrays.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Object} Returns an object composed of the picked properties.
+ * @example
+ *
+ * _.pick({ 'name': 'moe', '_userid': 'moe1' }, 'name');
+ * // => { 'name': 'moe' }
+ *
+ * _.pick({ 'name': 'moe', '_userid': 'moe1' }, function(value, key) {
+ * return key.charAt(0) != '_';
+ * });
+ * // => { 'name': 'moe' }
+ */
+ function pick(object, callback, thisArg) {
+ var result = {};
+ if (typeof callback != 'function') {
+ var index = 0,
+ props = concat.apply(arrayRef, arguments),
+ length = isObject(object) ? props.length : 0;
+
+ while (++index < length) {
+ var key = props[index];
+ if (key in object) {
+ result[key] = object[key];
+ }
}
- });
+ } else {
+ callback = lodash.createCallback(callback, thisArg);
+ forIn(object, function(value, key, object) {
+ if (callback(value, key, object)) {
+ result[key] = value;
+ }
+ });
+ }
+ return result;
}
- return result;
- }
-
- /**
- * Creates an object composed of keys returned from running each element of the
- * `collection` through the given `callback`. The corresponding value of each key
- * is the number of times the key was returned by the `callback`. The `callback`
- * is bound to `thisArg` and invoked with three arguments; (value, index|key, collection).
- *
- * If a property name is passed for `callback`, the created "_.pluck" style
- * callback will return the property value of the given element.
- *
- * If an object is passed for `callback`, the created "_.where" style callback
- * will return `true` for elements that have the propeties of the given object,
- * else `false`.
- *
- * @static
- * @memberOf _
- * @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.
- * @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).
- *
- * If a property name is passed for `callback`, the created "_.pluck" style
- * callback will return the property value of the given element.
- *
- * If an object is passed for `callback`, the created "_.where" style callback
- * will return `true` for elements that have the propeties of the given object,
- * else `false`.
- *
- * @static
- * @memberOf _
- * @alias all
- * @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.
- * @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
- *
- * var stooges = [
- * { 'name': 'moe', 'age': 40 },
- * { 'name': 'larry', 'age': 50 }
- * ];
- *
- * // using "_.pluck" callback shorthand
- * _.every(stooges, 'age');
- * // => true
- *
- * // using "_.where" callback shorthand
- * _.every(stooges, { 'age': 50 });
- * // => false
- */
- function every(collection, callback, thisArg) {
- var result = true;
- callback = createCallback(callback, thisArg);
-
- if (isArray(collection)) {
+ /**
+ * 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] (order is not guaranteed)
+ */
+ function values(object) {
var index = -1,
- length = collection.length;
+ props = keys(object),
+ length = props.length,
+ result = Array(length);
while (++index < length) {
- if (!(result = !!callback(collection[index], index, collection))) {
- break;
- }
+ result[index] = object[props[index]];
}
- } else {
- each(collection, function(value, index, collection) {
- return (result = !!callback(value, index, collection));
- });
+ return result;
}
- 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).
- *
- * If a property name is passed for `callback`, the created "_.pluck" style
- * callback will return the property value of the given element.
- *
- * If an object is passed for `callback`, the created "_.where" style callback
- * will return `true` for elements that have the propeties of the given object,
- * else `false`.
- *
- * @static
- * @memberOf _
- * @alias select
- * @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.
- * @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]
- *
- * var food = [
- * { 'name': 'apple', 'organic': false, 'type': 'fruit' },
- * { 'name': 'carrot', 'organic': true, 'type': 'vegetable' }
- * ];
- *
- * // using "_.pluck" callback shorthand
- * _.filter(food, 'organic');
- * // => [{ 'name': 'carrot', 'organic': true, 'type': 'vegetable' }]
- *
- * // using "_.where" callback shorthand
- * _.filter(food, { 'type': 'fruit' });
- * // => [{ 'name': 'apple', 'organic': false, 'type': 'fruit' }]
- */
- function filter(collection, callback, thisArg) {
- var result = [];
- callback = createCallback(callback, thisArg);
+ /*--------------------------------------------------------------------------*/
- if (isArray(collection)) {
+ /**
+ * Creates an array of elements from the specified indexes, or keys, of the
+ * `collection`. Indexes may be specified as individual arguments or as arrays
+ * of indexes.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|String} collection The collection to iterate over.
+ * @param {Array|Number|String} [index1, index2, ...] The indexes of
+ * `collection` to retrieve, either as individual arguments or arrays.
+ * @returns {Array} Returns a new array of elements corresponding to the
+ * provided indexes.
+ * @example
+ *
+ * _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]);
+ * // => ['a', 'c', 'e']
+ *
+ * _.at(['moe', 'larry', 'curly'], 0, 2);
+ * // => ['moe', 'curly']
+ */
+ function at(collection) {
var index = -1,
- length = collection.length;
+ props = concat.apply(arrayRef, slice(arguments, 1)),
+ length = props.length,
+ result = Array(length);
- while (++index < length) {
- var value = collection[index];
- if (callback(value, index, collection)) {
- result.push(value);
- }
+ if (support.unindexedChars && isString(collection)) {
+ collection = collection.split('');
}
- } else {
- each(collection, function(value, index, collection) {
- if (callback(value, index, collection)) {
- result.push(value);
- }
- });
+ while(++index < length) {
+ result[index] = collection[props[index]];
+ }
+ return result;
}
- return result;
- }
- /**
- * Examines each element in a `collection`, returning the first that the `callback`
- * returns truthy for. The `callback` is bound to `thisArg` and invoked with three
- * arguments; (value, index|key, collection).
- *
- * If a property name is passed for `callback`, the created "_.pluck" style
- * callback will return the property value of the given element.
- *
- * If an object is passed for `callback`, the created "_.where" style callback
- * will return `true` for elements that have the propeties of the given object,
- * else `false`.
- *
- * @static
- * @memberOf _
- * @alias detect
- * @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.
- * @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
- *
- * var food = [
- * { 'name': 'apple', 'organic': false, 'type': 'fruit' },
- * { 'name': 'banana', 'organic': true, 'type': 'fruit' },
- * { 'name': 'beet', 'organic': false, 'type': 'vegetable' },
- * { 'name': 'carrot', 'organic': true, 'type': 'vegetable' }
- * ];
- *
- * // using "_.where" callback shorthand
- * var veggie = _.find(food, { 'type': 'vegetable' });
- * // => { 'name': 'beet', 'organic': false, 'type': 'vegetable' }
- *
- * // using "_.pluck" callback shorthand
- * var healthy = _.find(food, 'organic');
- * // => { 'name': 'banana', 'organic': true, 'type': 'fruit' }
- */
- 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=identity] 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 value (order is not guaranteed)
- */
- function forEach(collection, callback, thisArg) {
- if (callback && typeof thisArg == 'undefined' && isArray(collection)) {
+ /**
+ * 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, fromIndex) {
var index = -1,
- length = collection.length;
-
- while (++index < length) {
- if (callback(collection[index], index, collection) === false) {
- break;
- }
+ length = collection ? collection.length : 0,
+ result = false;
+
+ fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0;
+ if (typeof length == 'number') {
+ result = (isString(collection)
+ ? collection.indexOf(target, fromIndex)
+ : indexOf(collection, target, fromIndex)
+ ) > -1;
+ } else {
+ each(collection, function(value) {
+ if (++index >= fromIndex) {
+ return !(result = value === target);
+ }
+ });
}
- } else {
- each(collection, callback, thisArg);
+ return result;
}
- return collection;
- }
-
- /**
- * Creates an object composed of keys returned from running each element of the
- * `collection` through the `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).
- *
- * If a property name is passed for `callback`, the created "_.pluck" style
- * callback will return the property value of the given element.
- *
- * If an object is passed for `callback`, the created "_.where" style callback
- * will return `true` for elements that have the propeties of the given object,
- * else `false`
- *
- * @static
- * @memberOf _
- * @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.
- * @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] }
- *
- * // using "_.pluck" callback shorthand
- * _.groupBy(['one', 'two', 'three'], 'length');
- * // => { '3': ['one', 'two'], '5': ['three'] }
- */
- function groupBy(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] = []).push(value);
- });
- return result;
- }
-
- /**
- * Invokes the method named by `methodName` on each element in the `collection`,
- * returning an array of the results of each invoked method. Additional arguments
- * will be passed to each invoked method. If `methodName` is a function, it will
- * be invoked for, and `this` bound to, each element in the `collection`.
- *
- * @static
- * @memberOf _
- * @category Collections
- * @param {Array|Object|String} collection The collection to iterate over.
- * @param {Function|String} methodName The name of the method to invoke or
- * the function invoked per iteration.
- * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with.
- * @returns {Array} Returns a new array of the results of each invoked method.
- * @example
- *
- * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
- * // => [[1, 5, 7], [1, 2, 3]]
- *
- * _.invoke([123, 456], String.prototype.split, '');
- * // => [['1', '2', '3'], ['4', '5', '6']]
- */
- function invoke(collection, methodName) {
- var args = slice(arguments, 2),
- index = -1,
- isFunc = typeof methodName == 'function',
- length = collection ? collection.length : 0,
- result = Array(typeof length == 'number' ? length : 0);
-
- forEach(collection, function(value) {
- result[++index] = (isFunc ? methodName : value[methodName]).apply(value, args);
- });
- return result;
- }
-
- /**
- * Creates an array of values by running each element in the `collection`
- * through the `callback`. The `callback` is bound to `thisArg` and invoked with
- * three arguments; (value, index|key, collection).
- *
- * If a property name is passed for `callback`, the created "_.pluck" style
- * callback will return the property value of the given element.
- *
- * If an object is passed for `callback`, the created "_.where" style callback
- * will return `true` for elements that have the propeties of the given object,
- * else `false`.
- *
- * @static
- * @memberOf _
- * @alias collect
- * @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.
- * @param {Mixed} [thisArg] The `this` binding of `callback`.
- * @returns {Array} Returns a new array of the results of each `callback` execution.
- * @example
- *
- * _.map([1, 2, 3], function(num) { return num * 3; });
- * // => [3, 6, 9]
- *
- * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
- * // => [3, 6, 9] (order is not guaranteed)
- *
- * var stooges = [
- * { 'name': 'moe', 'age': 40 },
- * { 'name': 'larry', 'age': 50 }
- * ];
- *
- * // using "_.pluck" callback shorthand
- * _.map(stooges, 'name');
- * // => ['moe', 'larry']
- */
- function map(collection, callback, thisArg) {
- var index = -1,
- length = collection ? collection.length : 0,
- result = Array(typeof length == 'number' ? length : 0);
+ /**
+ * Creates an object composed of keys returned from running each element of the
+ * `collection` through the given `callback`. The corresponding value of each key
+ * is the number of times the key was returned by the `callback`. The `callback`
+ * is bound to `thisArg` and invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is passed for `callback`, the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @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.
+ * @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 = lodash.createCallback(callback, thisArg);
- callback = createCallback(callback, thisArg);
- if (isArray(collection)) {
- while (++index < length) {
- result[index] = callback(collection[index], index, collection);
- }
- } else {
- each(collection, function(value, key, collection) {
- result[++index] = callback(value, key, collection);
+ forEach(collection, function(value, key, collection) {
+ key = String(callback(value, key, collection));
+ (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1);
});
+ return result;
}
- return result;
- }
- /**
- * Retrieves the maximum value of an `array`. If `callback` is passed,
- * it will be executed for each value in the `array` to generate the
- * criterion by which the value is ranked. The `callback` is bound to
- * `thisArg` and invoked with three arguments; (value, index, collection).
- *
- * If a property name is passed for `callback`, the created "_.pluck" style
- * callback will return the property value of the given element.
- *
- * If an object is passed for `callback`, the created "_.where" style callback
- * will return `true` for elements that have the propeties of the given object,
- * else `false`.
- *
- * @static
- * @memberOf _
- * @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.
- * @param {Mixed} [thisArg] The `this` binding of `callback`.
- * @returns {Mixed} Returns the maximum value.
- * @example
- *
- * _.max([4, 2, 8, 6]);
- * // => 8
- *
- * var stooges = [
- * { 'name': 'moe', 'age': 40 },
- * { 'name': 'larry', 'age': 50 }
- * ];
- *
- * _.max(stooges, function(stooge) { return stooge.age; });
- * // => { 'name': 'larry', 'age': 50 };
- *
- * // using "_.pluck" callback shorthand
- * _.max(stooges, 'age');
- * // => { 'name': 'larry', 'age': 50 };
- */
- function max(collection, callback, thisArg) {
- var computed = -Infinity,
- result = computed;
+ /**
+ * 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).
+ *
+ * If a property name is passed for `callback`, the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias all
+ * @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.
+ * @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
+ *
+ * var stooges = [
+ * { 'name': 'moe', 'age': 40 },
+ * { 'name': 'larry', 'age': 50 }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.every(stooges, 'age');
+ * // => true
+ *
+ * // using "_.where" callback shorthand
+ * _.every(stooges, { 'age': 50 });
+ * // => false
+ */
+ function every(collection, callback, thisArg) {
+ var result = true;
+ callback = lodash.createCallback(callback, thisArg);
- if (!callback && isArray(collection)) {
- var index = -1,
- length = collection.length;
+ if (isArray(collection)) {
+ var index = -1,
+ length = collection.length;
- while (++index < length) {
- var value = collection[index];
- if (value > result) {
- result = value;
+ while (++index < length) {
+ if (!(result = !!callback(collection[index], index, collection))) {
+ break;
+ }
}
+ } else {
+ each(collection, function(value, index, collection) {
+ return (result = !!callback(value, index, collection));
+ });
}
- } else {
- callback = !callback && isString(collection)
- ? charAtCallback
- : createCallback(callback, thisArg);
-
- each(collection, function(value, index, collection) {
- var current = callback(value, index, collection);
- if (current > computed) {
- computed = current;
- result = value;
+ 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).
+ *
+ * If a property name is passed for `callback`, the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias select
+ * @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.
+ * @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]
+ *
+ * var food = [
+ * { 'name': 'apple', 'organic': false, 'type': 'fruit' },
+ * { 'name': 'carrot', 'organic': true, 'type': 'vegetable' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.filter(food, 'organic');
+ * // => [{ 'name': 'carrot', 'organic': true, 'type': 'vegetable' }]
+ *
+ * // using "_.where" callback shorthand
+ * _.filter(food, { 'type': 'fruit' });
+ * // => [{ 'name': 'apple', 'organic': false, 'type': 'fruit' }]
+ */
+ function filter(collection, callback, thisArg) {
+ var result = [];
+ callback = lodash.createCallback(callback, thisArg);
+
+ if (isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ var value = collection[index];
+ if (callback(value, index, collection)) {
+ result.push(value);
+ }
}
- });
+ } else {
+ each(collection, function(value, index, collection) {
+ if (callback(value, index, collection)) {
+ result.push(value);
+ }
+ });
+ }
+ return result;
}
- return result;
- }
- /**
- * Retrieves the minimum value of an `array`. If `callback` is passed,
- * it will be executed for each value in the `array` to generate the
- * criterion by which the value is ranked. The `callback` is bound to `thisArg`
- * and invoked with three arguments; (value, index, collection).
- *
- * If a property name is passed for `callback`, the created "_.pluck" style
- * callback will return the property value of the given element.
- *
- * If an object is passed for `callback`, the created "_.where" style callback
- * will return `true` for elements that have the propeties of the given object,
- * else `false`.
- *
- * @static
- * @memberOf _
- * @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.
- * @param {Mixed} [thisArg] The `this` binding of `callback`.
- * @returns {Mixed} Returns the minimum value.
- * @example
- *
- * _.min([4, 2, 8, 6]);
- * // => 2
- *
- * var stooges = [
- * { 'name': 'moe', 'age': 40 },
- * { 'name': 'larry', 'age': 50 }
- * ];
- *
- * _.min(stooges, function(stooge) { return stooge.age; });
- * // => { 'name': 'moe', 'age': 40 };
- *
- * // using "_.pluck" callback shorthand
- * _.min(stooges, 'age');
- * // => { 'name': 'moe', 'age': 40 };
- */
- function min(collection, callback, thisArg) {
- var computed = Infinity,
- result = computed;
+ /**
+ * Examines each element in a `collection`, returning the first that the `callback`
+ * returns truthy for. The `callback` is bound to `thisArg` and invoked with three
+ * arguments; (value, index|key, collection).
+ *
+ * If a property name is passed for `callback`, the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias detect
+ * @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.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Mixed} Returns the found element, else `undefined`.
+ * @example
+ *
+ * _.find([1, 2, 3, 4], function(num) { return num % 2 == 0; });
+ * // => 2
+ *
+ * var food = [
+ * { 'name': 'apple', 'organic': false, 'type': 'fruit' },
+ * { 'name': 'banana', 'organic': true, 'type': 'fruit' },
+ * { 'name': 'beet', 'organic': false, 'type': 'vegetable' }
+ * ];
+ *
+ * // using "_.where" callback shorthand
+ * _.find(food, { 'type': 'vegetable' });
+ * // => { 'name': 'beet', 'organic': false, 'type': 'vegetable' }
+ *
+ * // using "_.pluck" callback shorthand
+ * _.find(food, 'organic');
+ * // => { 'name': 'banana', 'organic': true, 'type': 'fruit' }
+ */
+ function find(collection, callback, thisArg) {
+ callback = lodash.createCallback(callback, thisArg);
- if (!callback && isArray(collection)) {
- var index = -1,
- length = collection.length;
+ if (isArray(collection)) {
+ var index = -1,
+ length = collection.length;
- while (++index < length) {
- var value = collection[index];
- if (value < result) {
- result = value;
+ while (++index < length) {
+ var value = collection[index];
+ if (callback(value, index, collection)) {
+ return value;
+ }
}
+ } else {
+ var result;
+ each(collection, function(value, index, collection) {
+ if (callback(value, index, collection)) {
+ result = value;
+ return false;
+ }
+ });
+ return result;
}
- } else {
- callback = !callback && isString(collection)
- ? charAtCallback
- : createCallback(callback, thisArg);
-
- each(collection, function(value, index, collection) {
- var current = callback(value, index, collection);
- if (current < computed) {
- computed = current;
- result = value;
+ }
+
+ /**
+ * 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=identity] 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 value (order is not guaranteed)
+ */
+ function forEach(collection, callback, thisArg) {
+ if (callback && typeof thisArg == 'undefined' && isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ if (callback(collection[index], index, collection) === false) {
+ break;
+ }
}
+ } else {
+ each(collection, callback, thisArg);
+ }
+ return collection;
+ }
+
+ /**
+ * Creates an object composed of keys returned from running each element of the
+ * `collection` through the `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).
+ *
+ * If a property name is passed for `callback`, the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`
+ *
+ * @static
+ * @memberOf _
+ * @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.
+ * @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] }
+ *
+ * // using "_.pluck" callback shorthand
+ * _.groupBy(['one', 'two', 'three'], 'length');
+ * // => { '3': ['one', 'two'], '5': ['three'] }
+ */
+ function groupBy(collection, callback, thisArg) {
+ var result = {};
+ callback = lodash.createCallback(callback, thisArg);
+
+ forEach(collection, function(value, key, collection) {
+ key = String(callback(value, key, collection));
+ (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value);
});
+ return result;
}
- return result;
- }
- /**
- * Retrieves the value of a specified property from all elements in the `collection`.
- *
- * @static
- * @memberOf _
- * @type Function
- * @category Collections
- * @param {Array|Object|String} collection The collection to iterate over.
- * @param {String} property The property to pluck.
- * @returns {Array} Returns a new array of property values.
- * @example
- *
- * var stooges = [
- * { 'name': 'moe', 'age': 40 },
- * { 'name': 'larry', 'age': 50 }
- * ];
- *
- * _.pluck(stooges, 'name');
- * // => ['moe', 'larry']
- */
- var pluck = map;
+ /**
+ * Invokes the method named by `methodName` on each element in the `collection`,
+ * returning an array of the results of each invoked method. Additional arguments
+ * will be passed to each invoked method. If `methodName` is a function, it will
+ * be invoked for, and `this` bound to, each element in the `collection`.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|String} collection The collection to iterate over.
+ * @param {Function|String} methodName The name of the method to invoke or
+ * the function invoked per iteration.
+ * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with.
+ * @returns {Array} Returns a new array of the results of each invoked method.
+ * @example
+ *
+ * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
+ * // => [[1, 5, 7], [1, 2, 3]]
+ *
+ * _.invoke([123, 456], String.prototype.split, '');
+ * // => [['1', '2', '3'], ['4', '5', '6']]
+ */
+ function invoke(collection, methodName) {
+ var args = slice(arguments, 2),
+ index = -1,
+ isFunc = typeof methodName == 'function',
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
- /**
- * Reduces a `collection` to a value that is the accumulated result of running
- * each element in the `collection` through the `callback`, where each successive
- * `callback` execution consumes the return value of the previous execution.
- * If `accumulator` is not passed, the first element of the `collection` will be
- * used as the initial `accumulator` value. The `callback` is bound to `thisArg`
- * and invoked with four arguments; (accumulator, value, index|key, collection).
- *
- * @static
- * @memberOf _
- * @alias foldl, inject
- * @category Collections
- * @param {Array|Object|String} collection The collection to iterate over.
- * @param {Function} [callback=identity] The function called per iteration.
- * @param {Mixed} [accumulator] Initial value of the accumulator.
- * @param {Mixed} [thisArg] The `this` binding of `callback`.
- * @returns {Mixed} Returns the accumulated value.
- * @example
- *
- * var sum = _.reduce([1, 2, 3], function(sum, num) {
- * return sum + num;
- * });
- * // => 6
- *
- * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
- * result[key] = num * 3;
- * return result;
- * }, {});
- * // => { 'a': 3, 'b': 6, 'c': 9 }
- */
- function reduce(collection, callback, accumulator, thisArg) {
- var noaccum = arguments.length < 3;
- callback = createCallback(callback, thisArg, 4);
+ forEach(collection, function(value) {
+ result[++index] = (isFunc ? methodName : value[methodName]).apply(value, args);
+ });
+ return result;
+ }
- if (isArray(collection)) {
+ /**
+ * Creates an array of values by running each element in the `collection`
+ * through the `callback`. The `callback` is bound to `thisArg` and invoked with
+ * three arguments; (value, index|key, collection).
+ *
+ * If a property name is passed for `callback`, the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias collect
+ * @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.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of the results of each `callback` execution.
+ * @example
+ *
+ * _.map([1, 2, 3], function(num) { return num * 3; });
+ * // => [3, 6, 9]
+ *
+ * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
+ * // => [3, 6, 9] (order is not guaranteed)
+ *
+ * var stooges = [
+ * { 'name': 'moe', 'age': 40 },
+ * { 'name': 'larry', 'age': 50 }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.map(stooges, 'name');
+ * // => ['moe', 'larry']
+ */
+ function map(collection, callback, thisArg) {
var index = -1,
- length = collection.length;
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
- if (noaccum) {
- accumulator = collection[++index];
- }
- while (++index < length) {
- accumulator = callback(accumulator, collection[index], index, collection);
+ callback = lodash.createCallback(callback, thisArg);
+ if (isArray(collection)) {
+ while (++index < length) {
+ result[index] = callback(collection[index], index, collection);
+ }
+ } else {
+ each(collection, function(value, key, collection) {
+ result[++index] = callback(value, key, collection);
+ });
}
- } else {
- each(collection, function(value, index, collection) {
- accumulator = noaccum
- ? (noaccum = false, value)
- : callback(accumulator, value, index, collection)
- });
+ return result;
}
- return accumulator;
- }
- /**
- * This method is similar to `_.reduce`, except that it iterates over a
- * `collection` from right to left.
- *
- * @static
- * @memberOf _
- * @alias foldr
- * @category Collections
- * @param {Array|Object|String} collection The collection to iterate over.
- * @param {Function} [callback=identity] The function called per iteration.
- * @param {Mixed} [accumulator] Initial value of the accumulator.
- * @param {Mixed} [thisArg] The `this` binding of `callback`.
- * @returns {Mixed} Returns the accumulated value.
- * @example
- *
- * var list = [[0, 1], [2, 3], [4, 5]];
- * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
- * // => [4, 5, 2, 3, 0, 1]
- */
- function reduceRight(collection, callback, accumulator, thisArg) {
- var iterable = collection,
- length = collection ? collection.length : 0,
- noaccum = arguments.length < 3;
-
- if (typeof length != 'number') {
- var props = keys(collection);
- length = props.length;
- } else if (noCharByIndex && isString(collection)) {
- iterable = collection.split('');
+ /**
+ * Retrieves the maximum value of an `array`. If `callback` is passed,
+ * it will be executed for each value in the `array` to generate the
+ * criterion by which the value is ranked. The `callback` is bound to
+ * `thisArg` and invoked with three arguments; (value, index, collection).
+ *
+ * If a property name is passed for `callback`, the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @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.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Mixed} Returns the maximum value.
+ * @example
+ *
+ * _.max([4, 2, 8, 6]);
+ * // => 8
+ *
+ * var stooges = [
+ * { 'name': 'moe', 'age': 40 },
+ * { 'name': 'larry', 'age': 50 }
+ * ];
+ *
+ * _.max(stooges, function(stooge) { return stooge.age; });
+ * // => { 'name': 'larry', 'age': 50 };
+ *
+ * // using "_.pluck" callback shorthand
+ * _.max(stooges, 'age');
+ * // => { 'name': 'larry', 'age': 50 };
+ */
+ function max(collection, callback, thisArg) {
+ var computed = -Infinity,
+ result = computed;
+
+ if (!callback && isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ var value = collection[index];
+ if (value > result) {
+ result = value;
+ }
+ }
+ } else {
+ callback = (!callback && isString(collection))
+ ? charAtCallback
+ : lodash.createCallback(callback, thisArg);
+
+ each(collection, function(value, index, collection) {
+ var current = callback(value, index, collection);
+ if (current > computed) {
+ computed = current;
+ result = value;
+ }
+ });
+ }
+ return result;
}
- callback = createCallback(callback, thisArg, 4);
- forEach(collection, function(value, index, collection) {
- index = props ? props[--length] : --length;
- accumulator = noaccum
- ? (noaccum = false, iterable[index])
- : callback(accumulator, iterable[index], index, collection);
- });
- return accumulator;
- }
- /**
- * The opposite of `_.filter`, this method returns the elements of a
- * `collection` that `callback` does **not** return truthy for.
- *
- * If a property name is passed for `callback`, the created "_.pluck" style
- * callback will return the property value of the given element.
- *
- * If an object is passed for `callback`, the created "_.where" style callback
- * will return `true` for elements that have the propeties of the given object,
- * else `false`.
- *
- * @static
- * @memberOf _
- * @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.
- * @param {Mixed} [thisArg] The `this` binding of `callback`.
- * @returns {Array} Returns a new array of elements that did **not** pass the
- * callback check.
- * @example
- *
- * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
- * // => [1, 3, 5]
- *
- * var food = [
- * { 'name': 'apple', 'organic': false, 'type': 'fruit' },
- * { 'name': 'carrot', 'organic': true, 'type': 'vegetable' }
- * ];
- *
- * // using "_.pluck" callback shorthand
- * _.reject(food, 'organic');
- * // => [{ 'name': 'apple', 'organic': false, 'type': 'fruit' }]
- *
- * // using "_.where" callback shorthand
- * _.reject(food, { 'type': 'fruit' });
- * // => [{ 'name': 'carrot', 'organic': true, 'type': 'vegetable' }]
- */
- function reject(collection, callback, thisArg) {
- callback = createCallback(callback, thisArg);
- return filter(collection, function(value, index, collection) {
- return !callback(value, index, collection);
- });
- }
-
- /**
- * Creates an array of shuffled `array` values, using a version of the
- * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle.
- *
- * @static
- * @memberOf _
- * @category Collections
- * @param {Array|Object|String} collection The collection to shuffle.
- * @returns {Array} Returns a new shuffled collection.
- * @example
- *
- * _.shuffle([1, 2, 3, 4, 5, 6]);
- * // => [4, 1, 6, 3, 5, 2]
- */
- function shuffle(collection) {
- var index = -1,
- length = collection ? collection.length : 0,
- result = Array(typeof length == 'number' ? length : 0);
-
- forEach(collection, function(value) {
- var rand = floor(nativeRandom() * (++index + 1));
- result[index] = result[rand];
- result[rand] = value;
- });
- return result;
- }
+ /**
+ * Retrieves the minimum value of an `array`. If `callback` is passed,
+ * it will be executed for each value in the `array` to generate the
+ * criterion by which the value is ranked. The `callback` is bound to `thisArg`
+ * and invoked with three arguments; (value, index, collection).
+ *
+ * If a property name is passed for `callback`, the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @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.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Mixed} Returns the minimum value.
+ * @example
+ *
+ * _.min([4, 2, 8, 6]);
+ * // => 2
+ *
+ * var stooges = [
+ * { 'name': 'moe', 'age': 40 },
+ * { 'name': 'larry', 'age': 50 }
+ * ];
+ *
+ * _.min(stooges, function(stooge) { return stooge.age; });
+ * // => { 'name': 'moe', 'age': 40 };
+ *
+ * // using "_.pluck" callback shorthand
+ * _.min(stooges, 'age');
+ * // => { 'name': 'moe', 'age': 40 };
+ */
+ function min(collection, callback, thisArg) {
+ var computed = Infinity,
+ result = computed;
+
+ if (!callback && isArray(collection)) {
+ var index = -1,
+ length = collection.length;
+
+ while (++index < length) {
+ var value = collection[index];
+ if (value < result) {
+ result = value;
+ }
+ }
+ } else {
+ callback = (!callback && isString(collection))
+ ? charAtCallback
+ : lodash.createCallback(callback, thisArg);
+
+ each(collection, function(value, index, collection) {
+ var current = callback(value, index, collection);
+ if (current < computed) {
+ computed = current;
+ result = value;
+ }
+ });
+ }
+ return result;
+ }
- /**
- * Gets the size of the `collection` by returning `collection.length` for arrays
- * and array-like objects or the number of own enumerable properties for objects.
- *
- * @static
- * @memberOf _
- * @category Collections
- * @param {Array|Object|String} collection The collection to inspect.
- * @returns {Number} Returns `collection.length` or number of own enumerable properties.
- * @example
- *
- * _.size([1, 2]);
- * // => 2
- *
- * _.size({ 'one': 1, 'two': 2, 'three': 3 });
- * // => 3
- *
- * _.size('curly');
- * // => 5
- */
- function size(collection) {
- var length = collection ? collection.length : 0;
- return typeof length == 'number' ? length : keys(collection).length;
- }
+ /**
+ * Retrieves the value of a specified property from all elements in the `collection`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Collections
+ * @param {Array|Object|String} collection The collection to iterate over.
+ * @param {String} property The property to pluck.
+ * @returns {Array} Returns a new array of property values.
+ * @example
+ *
+ * var stooges = [
+ * { 'name': 'moe', 'age': 40 },
+ * { 'name': 'larry', 'age': 50 }
+ * ];
+ *
+ * _.pluck(stooges, 'name');
+ * // => ['moe', 'larry']
+ */
+ var pluck = map;
- /**
- * Checks if the `callback` returns a truthy value for **any** element of a
- * `collection`. The function returns as soon as it finds passing value, and
- * does not iterate over the entire `collection`. The `callback` is bound to
- * `thisArg` and invoked with three arguments; (value, index|key, collection).
- *
- * If a property name is passed for `callback`, the created "_.pluck" style
- * callback will return the property value of the given element.
- *
- * If an object is passed for `callback`, the created "_.where" style callback
- * will return `true` for elements that have the propeties of the given object,
- * else `false`.
- *
- * @static
- * @memberOf _
- * @alias any
- * @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.
- * @param {Mixed} [thisArg] The `this` binding of `callback`.
- * @returns {Boolean} Returns `true` if any element passes the callback check,
- * else `false`.
- * @example
- *
- * _.some([null, 0, 'yes', false], Boolean);
- * // => true
- *
- * var food = [
- * { 'name': 'apple', 'organic': false, 'type': 'fruit' },
- * { 'name': 'carrot', 'organic': true, 'type': 'vegetable' }
- * ];
- *
- * // using "_.pluck" callback shorthand
- * _.some(food, 'organic');
- * // => true
- *
- * // using "_.where" callback shorthand
- * _.some(food, { 'type': 'meat' });
- * // => false
- */
- function some(collection, callback, thisArg) {
- var result;
- callback = createCallback(callback, thisArg);
+ /**
+ * Reduces a `collection` to a value that is the accumulated result of running
+ * each element in the `collection` through the `callback`, where each successive
+ * `callback` execution consumes the return value of the previous execution.
+ * If `accumulator` is not passed, the first element of the `collection` will be
+ * used as the initial `accumulator` value. The `callback` is bound to `thisArg`
+ * and invoked with four arguments; (accumulator, value, index|key, collection).
+ *
+ * @static
+ * @memberOf _
+ * @alias foldl, inject
+ * @category Collections
+ * @param {Array|Object|String} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {Mixed} [accumulator] Initial value of the accumulator.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Mixed} Returns the accumulated value.
+ * @example
+ *
+ * var sum = _.reduce([1, 2, 3], function(sum, num) {
+ * return sum + num;
+ * });
+ * // => 6
+ *
+ * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
+ * result[key] = num * 3;
+ * return result;
+ * }, {});
+ * // => { 'a': 3, 'b': 6, 'c': 9 }
+ */
+ function reduce(collection, callback, accumulator, thisArg) {
+ var noaccum = arguments.length < 3;
+ callback = lodash.createCallback(callback, thisArg, 4);
- if (isArray(collection)) {
- var index = -1,
- length = collection.length;
+ if (isArray(collection)) {
+ var index = -1,
+ length = collection.length;
- while (++index < length) {
- if ((result = callback(collection[index], index, collection))) {
- break;
+ if (noaccum) {
+ accumulator = collection[++index];
}
+ while (++index < length) {
+ accumulator = callback(accumulator, collection[index], index, collection);
+ }
+ } else {
+ each(collection, function(value, index, collection) {
+ accumulator = noaccum
+ ? (noaccum = false, value)
+ : callback(accumulator, value, index, collection)
+ });
+ }
+ return accumulator;
+ }
+
+ /**
+ * This method is similar to `_.reduce`, except that it iterates over a
+ * `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @alias foldr
+ * @category Collections
+ * @param {Array|Object|String} collection The collection to iterate over.
+ * @param {Function} [callback=identity] The function called per iteration.
+ * @param {Mixed} [accumulator] Initial value of the accumulator.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Mixed} Returns the accumulated value.
+ * @example
+ *
+ * var list = [[0, 1], [2, 3], [4, 5]];
+ * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
+ * // => [4, 5, 2, 3, 0, 1]
+ */
+ function reduceRight(collection, callback, accumulator, thisArg) {
+ var iterable = collection,
+ length = collection ? collection.length : 0,
+ noaccum = arguments.length < 3;
+
+ if (typeof length != 'number') {
+ var props = keys(collection);
+ length = props.length;
+ } else if (support.unindexedChars && isString(collection)) {
+ iterable = collection.split('');
}
- } else {
- each(collection, function(value, index, collection) {
- return !(result = callback(value, index, collection));
+ callback = lodash.createCallback(callback, thisArg, 4);
+ forEach(collection, function(value, index, collection) {
+ index = props ? props[--length] : --length;
+ accumulator = noaccum
+ ? (noaccum = false, iterable[index])
+ : callback(accumulator, iterable[index], index, collection);
});
+ return accumulator;
}
- return !!result;
- }
- /**
- * Creates an array of elements, sorted in ascending order by the results of
- * running each element in the `collection` through the `callback`. This method
- * performs a stable sort, that is, it will preserve the original sort order of
- * equal elements. The `callback` is bound to `thisArg` and invoked with three
- * arguments; (value, index|key, collection).
- *
- * If a property name is passed for `callback`, the created "_.pluck" style
- * callback will return the property value of the given element.
- *
- * If an object is passed for `callback`, the created "_.where" style callback
- * will return `true` for elements that have the propeties of the given object,
- * else `false`.
- *
- * @static
- * @memberOf _
- * @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.
- * @param {Mixed} [thisArg] The `this` binding of `callback`.
- * @returns {Array} Returns a new array of sorted elements.
- * @example
- *
- * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); });
- * // => [3, 1, 2]
- *
- * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math);
- * // => [3, 1, 2]
- *
- * // using "_.pluck" callback shorthand
- * _.sortBy(['banana', 'strawberry', 'apple'], 'length');
- * // => ['apple', 'banana', 'strawberry']
- */
- function sortBy(collection, callback, thisArg) {
- var index = -1,
- length = collection ? collection.length : 0,
- result = Array(typeof length == 'number' ? length : 0);
-
- callback = createCallback(callback, thisArg);
- forEach(collection, function(value, key, collection) {
- result[++index] = {
- 'criteria': callback(value, key, collection),
- 'index': index,
- 'value': value
- };
- });
+ /**
+ * The opposite of `_.filter`, this method returns the elements of a
+ * `collection` that `callback` does **not** return truthy for.
+ *
+ * If a property name is passed for `callback`, the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @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.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of elements that did **not** pass the
+ * callback check.
+ * @example
+ *
+ * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
+ * // => [1, 3, 5]
+ *
+ * var food = [
+ * { 'name': 'apple', 'organic': false, 'type': 'fruit' },
+ * { 'name': 'carrot', 'organic': true, 'type': 'vegetable' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.reject(food, 'organic');
+ * // => [{ 'name': 'apple', 'organic': false, 'type': 'fruit' }]
+ *
+ * // using "_.where" callback shorthand
+ * _.reject(food, { 'type': 'fruit' });
+ * // => [{ 'name': 'carrot', 'organic': true, 'type': 'vegetable' }]
+ */
+ function reject(collection, callback, thisArg) {
+ callback = lodash.createCallback(callback, thisArg);
+ return filter(collection, function(value, index, collection) {
+ return !callback(value, index, collection);
+ });
+ }
+
+ /**
+ * Creates an array of shuffled `array` values, using a version of the
+ * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|String} collection The collection to shuffle.
+ * @returns {Array} Returns a new shuffled collection.
+ * @example
+ *
+ * _.shuffle([1, 2, 3, 4, 5, 6]);
+ * // => [4, 1, 6, 3, 5, 2]
+ */
+ function shuffle(collection) {
+ var index = -1,
+ length = collection ? collection.length : 0,
+ result = Array(typeof length == 'number' ? length : 0);
- length = result.length;
- result.sort(compareAscending);
- while (length--) {
- result[length] = result[length].value;
+ forEach(collection, function(value) {
+ var rand = floor(nativeRandom() * (++index + 1));
+ result[index] = result[rand];
+ result[rand] = value;
+ });
+ return result;
}
- return result;
- }
- /**
- * Converts the `collection` to an array.
- *
- * @static
- * @memberOf _
- * @category Collections
- * @param {Array|Object|String} collection The collection to convert.
- * @returns {Array} Returns the new converted array.
- * @example
- *
- * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
- * // => [2, 3, 4]
- */
- function toArray(collection) {
- if (collection && typeof collection.length == 'number') {
- return noCharByIndex && isString(collection)
- ? collection.split('')
- : slice(collection);
+ /**
+ * Gets the size of the `collection` by returning `collection.length` for arrays
+ * and array-like objects or the number of own enumerable properties for objects.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|String} collection The collection to inspect.
+ * @returns {Number} Returns `collection.length` or number of own enumerable properties.
+ * @example
+ *
+ * _.size([1, 2]);
+ * // => 2
+ *
+ * _.size({ 'one': 1, 'two': 2, 'three': 3 });
+ * // => 3
+ *
+ * _.size('curly');
+ * // => 5
+ */
+ function size(collection) {
+ var length = collection ? collection.length : 0;
+ return typeof length == 'number' ? length : keys(collection).length;
}
- return values(collection);
- }
- /**
- * Examines each element in a `collection`, returning an array of all elements
- * that have the given `properties`. When checking `properties`, this method
- * performs a deep comparison between values to determine if they are equivalent
- * to each other.
- *
- * @static
- * @memberOf _
- * @type Function
- * @category Collections
- * @param {Array|Object|String} collection The collection to iterate over.
- * @param {Object} properties The object of property values to filter by.
- * @returns {Array} Returns a new array of elements that have the given `properties`.
- * @example
- *
- * var stooges = [
- * { 'name': 'moe', 'age': 40 },
- * { 'name': 'larry', 'age': 50 }
- * ];
- *
- * _.where(stooges, { 'age': 40 });
- * // => [{ 'name': 'moe', 'age': 40 }]
- */
- var where = filter;
+ /**
+ * Checks if the `callback` returns a truthy value for **any** element of a
+ * `collection`. The function returns as soon as it finds passing value, and
+ * does not iterate over the entire `collection`. The `callback` is bound to
+ * `thisArg` and invoked with three arguments; (value, index|key, collection).
+ *
+ * If a property name is passed for `callback`, the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias any
+ * @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.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Boolean} Returns `true` if any element passes the callback check,
+ * else `false`.
+ * @example
+ *
+ * _.some([null, 0, 'yes', false], Boolean);
+ * // => true
+ *
+ * var food = [
+ * { 'name': 'apple', 'organic': false, 'type': 'fruit' },
+ * { 'name': 'carrot', 'organic': true, 'type': 'vegetable' }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.some(food, 'organic');
+ * // => true
+ *
+ * // using "_.where" callback shorthand
+ * _.some(food, { 'type': 'meat' });
+ * // => false
+ */
+ function some(collection, callback, thisArg) {
+ var result;
+ callback = lodash.createCallback(callback, thisArg);
- /*--------------------------------------------------------------------------*/
+ if (isArray(collection)) {
+ var index = -1,
+ length = collection.length;
- /**
- * Creates an array with all falsey values of `array` removed. The values
- * `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey.
- *
- * @static
- * @memberOf _
- * @category Arrays
- * @param {Array} array The array to compact.
- * @returns {Array} Returns a new filtered array.
- * @example
- *
- * _.compact([0, 1, false, 2, '', 3]);
- * // => [1, 2, 3]
- */
- function compact(array) {
- var index = -1,
- length = array ? array.length : 0,
- result = [];
-
- while (++index < length) {
- var value = array[index];
- if (value) {
- result.push(value);
+ while (++index < length) {
+ if ((result = callback(collection[index], index, collection))) {
+ break;
+ }
+ }
+ } else {
+ each(collection, function(value, index, collection) {
+ return !(result = callback(value, index, collection));
+ });
}
+ return !!result;
}
- return result;
- }
- /**
- * Creates an array of `array` elements not present in the other arrays
- * using strict equality for comparisons, i.e. `===`.
- *
- * @static
- * @memberOf _
- * @category Arrays
- * @param {Array} array The array to process.
- * @param {Array} [array1, array2, ...] Arrays to check.
- * @returns {Array} Returns a new array of `array` elements not present in the
- * other arrays.
- * @example
- *
- * _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
- * // => [1, 3, 4]
- */
- function difference(array) {
- var index = -1,
- length = array ? array.length : 0,
- flattened = concat.apply(arrayRef, arguments),
- contains = cachedContains(flattened, length),
- result = [];
-
- while (++index < length) {
- var value = array[index];
- if (!contains(value)) {
- result.push(value);
+ /**
+ * Creates an array of elements, sorted in ascending order by the results of
+ * running each element in the `collection` through the `callback`. This method
+ * performs a stable sort, that is, it will preserve the original sort order of
+ * equal elements. The `callback` is bound to `thisArg` and invoked with three
+ * arguments; (value, index|key, collection).
+ *
+ * If a property name is passed for `callback`, the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @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.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new array of sorted elements.
+ * @example
+ *
+ * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); });
+ * // => [3, 1, 2]
+ *
+ * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math);
+ * // => [3, 1, 2]
+ *
+ * // using "_.pluck" callback shorthand
+ * _.sortBy(['banana', 'strawberry', 'apple'], 'length');
+ * // => ['apple', 'banana', 'strawberry']
+ */
+ function sortBy(collection, callback, thisArg) {
+ var index = -1,
+ 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
+ };
+ });
+
+ length = result.length;
+ result.sort(compareAscending);
+ while (length--) {
+ result[length] = result[length].value;
}
+ return result;
}
- return result;
- }
-
- /**
- * Gets the first element of the `array`. If a number `n` is passed, the first
- * `n` elements of the `array` are returned. If a `callback` function is passed,
- * the first elements the `callback` returns truthy for are returned. 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.
- *
- * If an object is passed for `callback`, the created "_.where" style callback
- * will return `true` for elements that have the propeties of the given object,
- * else `false`.
- *
- * @static
- * @memberOf _
- * @alias head, take
- * @category Arrays
- * @param {Array} array The array to query.
- * @param {Function|Object|Number|String} [callback|n] The function called
- * per element or the number of elements to return. If a property name or
- * object is passed, it will be used to create a "_.pluck" or "_.where"
- * style callback, respectively.
- * @param {Mixed} [thisArg] The `this` binding of `callback`.
- * @returns {Mixed} Returns the first element(s) of `array`.
- * @example
- *
- * _.first([1, 2, 3]);
- * // => 1
- *
- * _.first([1, 2, 3], 2);
- * // => [1, 2]
- *
- * _.first([1, 2, 3], function(num) {
- * return num < 3;
- * });
- * // => [1, 2]
- *
- * var food = [
- * { 'name': 'banana', 'organic': true },
- * { 'name': 'beet', 'organic': false },
- * ];
- *
- * // using "_.pluck" callback shorthand
- * _.first(food, 'organic');
- * // => [{ 'name': 'banana', 'organic': true }]
- *
- * var food = [
- * { 'name': 'apple', 'type': 'fruit' },
- * { 'name': 'banana', 'type': 'fruit' },
- * { 'name': 'beet', 'type': 'vegetable' }
- * ];
- *
- * // using "_.where" callback shorthand
- * _.first(food, { 'type': 'fruit' });
- * // => [{ 'name': 'apple', 'type': 'fruit' }, { 'name': 'banana', 'type': 'fruit' }]
- */
- function first(array, callback, thisArg) {
- if (array) {
- var n = 0,
- length = array.length;
- if (typeof callback != 'number' && callback != null) {
- var index = -1;
- callback = createCallback(callback, thisArg);
- while (++index < length && callback(array[index], index, array)) {
- n++;
- }
- } else {
- n = callback;
- if (n == null || thisArg) {
- return array[0];
- }
+ /**
+ * Converts the `collection` to an array.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|String} collection The collection to convert.
+ * @returns {Array} Returns the new converted array.
+ * @example
+ *
+ * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
+ * // => [2, 3, 4]
+ */
+ function toArray(collection) {
+ if (collection && typeof collection.length == 'number') {
+ return (support.unindexedChars && isString(collection))
+ ? collection.split('')
+ : slice(collection);
}
- return slice(array, 0, nativeMin(nativeMax(0, n), length));
+ return values(collection);
}
- }
- /**
- * Flattens a nested array (the nesting can be to any depth). If `shallow` is
- * truthy, `array` will only be flattened a single level.
- *
- * @static
- * @memberOf _
- * @category Arrays
- * @param {Array} array The array to compact.
- * @param {Boolean} shallow A flag to indicate only flattening a single level.
- * @returns {Array} Returns a new flattened array.
- * @example
- *
- * _.flatten([1, [2], [3, [[4]]]]);
- * // => [1, 2, 3, 4];
- *
- * _.flatten([1, [2], [3, [[4]]]], true);
- * // => [1, 2, 3, [[4]]];
- */
- function flatten(array, shallow) {
- var index = -1,
- length = array ? array.length : 0,
- result = [];
+ /**
+ * Examines each element in a `collection`, returning an array of all elements
+ * that have the given `properties`. When checking `properties`, this method
+ * performs a deep comparison between values to determine if they are equivalent
+ * to each other.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Collections
+ * @param {Array|Object|String} collection The collection to iterate over.
+ * @param {Object} properties The object of property values to filter by.
+ * @returns {Array} Returns a new array of elements that have the given `properties`.
+ * @example
+ *
+ * var stooges = [
+ * { 'name': 'moe', 'age': 40 },
+ * { 'name': 'larry', 'age': 50 }
+ * ];
+ *
+ * _.where(stooges, { 'age': 40 });
+ * // => [{ 'name': 'moe', 'age': 40 }]
+ */
+ var where = filter;
- while (++index < length) {
- var value = array[index];
+ /*--------------------------------------------------------------------------*/
- // recursively flatten arrays (susceptible to call stack limits)
- if (isArray(value)) {
- push.apply(result, shallow ? value : flatten(value));
- } else {
- result.push(value);
+ /**
+ * Creates an array with all falsey values of `array` removed. The values
+ * `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to compact.
+ * @returns {Array} Returns a new filtered array.
+ * @example
+ *
+ * _.compact([0, 1, false, 2, '', 3]);
+ * // => [1, 2, 3]
+ */
+ function compact(array) {
+ var index = -1,
+ length = array ? array.length : 0,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+ if (value) {
+ result.push(value);
+ }
}
+ return result;
}
- 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.
- *
- * @static
- * @memberOf _
- * @category Arrays
- * @param {Array} array The array to search.
- * @param {Mixed} value The value to search for.
- * @param {Boolean|Number} [fromIndex=0] The index to search from or `true` to
- * perform a binary search on a sorted `array`.
- * @returns {Number} Returns the index of the matched value or `-1`.
- * @example
- *
- * _.indexOf([1, 2, 3, 1, 2, 3], 2);
- * // => 1
- *
- * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3);
- * // => 4
- *
- * _.indexOf([1, 1, 2, 2, 3, 3], 2, true);
- * // => 2
- */
- function indexOf(array, value, fromIndex) {
- var index = -1,
- length = array ? array.length : 0;
-
- if (typeof fromIndex == 'number') {
- index = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0) - 1;
- } else if (fromIndex) {
- index = sortedIndex(array, value);
- return array[index] === value ? index : -1;
+ /**
+ * Creates an array of `array` elements not present in the other arrays
+ * using strict equality for comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to process.
+ * @param {Array} [array1, array2, ...] Arrays to check.
+ * @returns {Array} Returns a new array of `array` elements not present in the
+ * other arrays.
+ * @example
+ *
+ * _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
+ * // => [1, 3, 4]
+ */
+ function difference(array) {
+ var index = -1,
+ length = array ? array.length : 0,
+ flattened = concat.apply(arrayRef, arguments),
+ contains = cachedContains(flattened, length, 100),
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+ if (!contains(value)) {
+ result.push(value);
+ }
+ }
+ return result;
}
- while (++index < length) {
- if (array[index] === value) {
- return index;
+
+ /**
+ * This method is similar to `_.find`, except that it returns the index of
+ * the element that passes the callback check, instead of the element itself.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @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.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Mixed} Returns the index of the found element, else `-1`.
+ * @example
+ *
+ * _.findIndex(['apple', 'banana', 'beet'], function(food) { return /^b/.test(food); });
+ * // => 1
+ */
+ function findIndex(collection, callback, thisArg) {
+ var index = -1,
+ length = collection ? collection.length : 0;
+
+ callback = lodash.createCallback(callback, thisArg);
+ while (++index < length) {
+ if (callback(collection[index], index, collection)) {
+ return index;
+ }
}
+ return -1;
}
- return -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
- * is passed, the last elements the `callback` returns truthy for are excluded
- * from the result. 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.
- *
- * If an object is passed for `callback`, the created "_.where" style callback
- * will return `true` for elements that have the propeties of the given object,
- * else `false`.
- *
- * @static
- * @memberOf _
- * @category Arrays
- * @param {Array} array The array to query.
- * @param {Function|Object|Number|String} [callback|n=1] The function called
- * per element or the number of elements to exclude. If a property name or
- * object is passed, it will be used to create a "_.pluck" or "_.where"
- * style callback, respectively.
- * @param {Mixed} [thisArg] The `this` binding of `callback`.
- * @returns {Array} Returns a slice of `array`.
- * @example
- *
- * _.initial([1, 2, 3]);
- * // => [1, 2]
- *
- * _.initial([1, 2, 3], 2);
- * // => [1]
- *
- * _.initial([1, 2, 3], function(num) {
- * return num > 1;
- * });
- * // => [1]
- *
- * var food = [
- * { 'name': 'beet', 'organic': false },
- * { 'name': 'carrot', 'organic': true }
- * ];
- *
- * // using "_.pluck" callback shorthand
- * _.initial(food, 'organic');
- * // => [{ 'name': 'beet', 'organic': false }]
- *
- * var food = [
- * { 'name': 'banana', 'type': 'fruit' },
- * { 'name': 'beet', 'type': 'vegetable' },
- * { 'name': 'carrot', 'type': 'vegetable' }
- * ];
- *
- * // using "_.where" callback shorthand
- * _.initial(food, { 'type': 'vegetable' });
- * // => [{ 'name': 'banana', 'type': 'fruit' }]
- */
- function initial(array, callback, thisArg) {
- if (!array) {
- return [];
- }
- var n = 0,
- length = array.length;
-
- if (typeof callback != 'number' && callback != null) {
- var index = length;
- callback = createCallback(callback, thisArg);
- while (index-- && callback(array[index], index, array)) {
- n++;
+ /**
+ * Gets the first element of the `array`. If a number `n` is passed, the first
+ * `n` elements of the `array` are returned. If a `callback` function is passed,
+ * elements at the beginning of the array are returned as long as the `callback`
+ * returns truthy. 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.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias head, take
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|Number|String} [callback|n] The function called
+ * per element or the number of elements to return. If a property name or
+ * object is passed, it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Mixed} Returns the first element(s) of `array`.
+ * @example
+ *
+ * _.first([1, 2, 3]);
+ * // => 1
+ *
+ * _.first([1, 2, 3], 2);
+ * // => [1, 2]
+ *
+ * _.first([1, 2, 3], function(num) {
+ * return num < 3;
+ * });
+ * // => [1, 2]
+ *
+ * var food = [
+ * { 'name': 'banana', 'organic': true },
+ * { 'name': 'beet', 'organic': false },
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.first(food, 'organic');
+ * // => [{ 'name': 'banana', 'organic': true }]
+ *
+ * var food = [
+ * { 'name': 'apple', 'type': 'fruit' },
+ * { 'name': 'banana', 'type': 'fruit' },
+ * { 'name': 'beet', 'type': 'vegetable' }
+ * ];
+ *
+ * // using "_.where" callback shorthand
+ * _.first(food, { 'type': 'fruit' });
+ * // => [{ 'name': 'apple', 'type': 'fruit' }, { 'name': 'banana', 'type': 'fruit' }]
+ */
+ function first(array, callback, thisArg) {
+ if (array) {
+ var n = 0,
+ length = array.length;
+
+ if (typeof callback != 'number' && callback != null) {
+ var index = -1;
+ callback = lodash.createCallback(callback, thisArg);
+ while (++index < length && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = callback;
+ if (n == null || thisArg) {
+ return array[0];
+ }
+ }
+ return slice(array, 0, nativeMin(nativeMax(0, n), length));
}
- } else {
- n = (callback == null || thisArg) ? 1 : callback || n;
}
- return slice(array, 0, nativeMin(nativeMax(0, length - n), length));
- }
- /**
- * Computes the intersection of all the passed-in arrays using strict equality
- * for comparisons, i.e. `===`.
- *
- * @static
- * @memberOf _
- * @category Arrays
- * @param {Array} [array1, array2, ...] Arrays to process.
- * @returns {Array} Returns a new array of unique elements that are present
- * in **all** of the arrays.
- * @example
- *
- * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);
- * // => [1, 2]
- */
- function intersection(array) {
- var args = arguments,
- argsLength = args.length,
- cache = { '0': {} },
- index = -1,
- length = array ? array.length : 0,
- isLarge = length >= 100,
- result = [],
- seen = result;
-
- outer:
- while (++index < length) {
- var value = array[index];
- if (isLarge) {
- var key = value + '';
- var inited = hasOwnProperty.call(cache[0], key)
- ? !(seen = cache[0][key])
- : (seen = cache[0][key] = []);
+ /**
+ * Flattens a nested array (the nesting can be to any depth). If `isShallow`
+ * is truthy, `array` will only be flattened a single level. If `callback`
+ * is passed, each element of `array` is passed through a callback` before
+ * flattening. 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.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to compact.
+ * @param {Boolean} [isShallow=false] A flag to indicate only flattening a single level.
+ * @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.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a new flattened array.
+ * @example
+ *
+ * _.flatten([1, [2], [3, [[4]]]]);
+ * // => [1, 2, 3, 4];
+ *
+ * _.flatten([1, [2], [3, [[4]]]], true);
+ * // => [1, 2, 3, [[4]]];
+ *
+ * var stooges = [
+ * { 'name': 'curly', 'quotes': ['Oh, a wise guy, eh?', 'Poifect!'] },
+ * { 'name': 'moe', 'quotes': ['Spread out!', 'You knucklehead!'] }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.flatten(stooges, 'quotes');
+ * // => ['Oh, a wise guy, eh?', 'Poifect!', 'Spread out!', 'You knucklehead!']
+ */
+ function flatten(array, isShallow, callback, thisArg) {
+ var index = -1,
+ length = array ? array.length : 0,
+ result = [];
+
+ // juggle arguments
+ if (typeof isShallow != 'boolean' && isShallow != null) {
+ thisArg = callback;
+ callback = isShallow;
+ isShallow = false;
}
- if (inited || indexOf(seen, value) < 0) {
- if (isLarge) {
- seen.push(value);
+ if (callback != null) {
+ callback = lodash.createCallback(callback, thisArg);
+ }
+ while (++index < length) {
+ var value = array[index];
+ if (callback) {
+ value = callback(value, index, array);
}
- var argsIndex = argsLength;
- while (--argsIndex) {
- if (!(cache[argsIndex] || (cache[argsIndex] = cachedContains(args[argsIndex], 0, 100)))(value)) {
- continue outer;
- }
+ // recursively flatten arrays (susceptible to call stack limits)
+ if (isArray(value)) {
+ push.apply(result, isShallow ? value : flatten(value));
+ } else {
+ result.push(value);
}
- result.push(value);
}
+ return result;
}
- return result;
- }
- /**
- * Gets the last element of the `array`. If a number `n` is passed, the last
- * `n` elements of the `array` are returned. If a `callback` function is passed,
- * the last elements the `callback` returns truthy for are returned. 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.
- *
- * If an object is passed for `callback`, the created "_.where" style callback
- * will return `true` for elements that have the propeties of the given object,
- * else `false`.
- *
- * @static
- * @memberOf _
- * @category Arrays
- * @param {Array} array The array to query.
- * @param {Function|Object|Number|String} [callback|n] The function called
- * per element or the number of elements to return. If a property name or
- * object is passed, it will be used to create a "_.pluck" or "_.where"
- * style callback, respectively.
- * @param {Mixed} [thisArg] The `this` binding of `callback`.
- * @returns {Mixed} Returns the last element(s) of `array`.
- * @example
- *
- * _.last([1, 2, 3]);
- * // => 3
- *
- * _.last([1, 2, 3], 2);
- * // => [2, 3]
- *
- * _.last([1, 2, 3], function(num) {
- * return num > 1;
- * });
- * // => [2, 3]
- *
- * var food = [
- * { 'name': 'beet', 'organic': false },
- * { 'name': 'carrot', 'organic': true }
- * ];
- *
- * // using "_.pluck" callback shorthand
- * _.last(food, 'organic');
- * // => [{ 'name': 'carrot', 'organic': true }]
- *
- * var food = [
- * { 'name': 'banana', 'type': 'fruit' },
- * { 'name': 'beet', 'type': 'vegetable' },
- * { 'name': 'carrot', 'type': 'vegetable' }
- * ];
- *
- * // using "_.where" callback shorthand
- * _.last(food, { 'type': 'vegetable' });
- * // => [{ 'name': 'beet', 'type': 'vegetable' }, { 'name': 'carrot', 'type': 'vegetable' }]
- */
- function last(array, callback, thisArg) {
- if (array) {
+ /**
+ * 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.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {Mixed} value The value to search for.
+ * @param {Boolean|Number} [fromIndex=0] The index to search from or `true` to
+ * perform a binary search on a sorted `array`.
+ * @returns {Number} Returns the index of the matched value or `-1`.
+ * @example
+ *
+ * _.indexOf([1, 2, 3, 1, 2, 3], 2);
+ * // => 1
+ *
+ * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3);
+ * // => 4
+ *
+ * _.indexOf([1, 1, 2, 2, 3, 3], 2, true);
+ * // => 2
+ */
+ function indexOf(array, value, fromIndex) {
+ var index = -1,
+ length = array ? array.length : 0;
+
+ if (typeof fromIndex == 'number') {
+ index = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0) - 1;
+ } else if (fromIndex) {
+ index = sortedIndex(array, value);
+ return array[index] === value ? index : -1;
+ }
+ while (++index < length) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Gets all but the last element of `array`. If a number `n` is passed, the
+ * last `n` elements are excluded from the result. If a `callback` function
+ * is passed, elements at the end of the array are excluded from the result
+ * as long as the `callback` returns truthy. 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.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|Number|String} [callback|n=1] The function called
+ * per element or the number of elements to exclude. If a property name or
+ * object is passed, it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a slice of `array`.
+ * @example
+ *
+ * _.initial([1, 2, 3]);
+ * // => [1, 2]
+ *
+ * _.initial([1, 2, 3], 2);
+ * // => [1]
+ *
+ * _.initial([1, 2, 3], function(num) {
+ * return num > 1;
+ * });
+ * // => [1]
+ *
+ * var food = [
+ * { 'name': 'beet', 'organic': false },
+ * { 'name': 'carrot', 'organic': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.initial(food, 'organic');
+ * // => [{ 'name': 'beet', 'organic': false }]
+ *
+ * var food = [
+ * { 'name': 'banana', 'type': 'fruit' },
+ * { 'name': 'beet', 'type': 'vegetable' },
+ * { 'name': 'carrot', 'type': 'vegetable' }
+ * ];
+ *
+ * // using "_.where" callback shorthand
+ * _.initial(food, { 'type': 'vegetable' });
+ * // => [{ 'name': 'banana', 'type': 'fruit' }]
+ */
+ function initial(array, callback, thisArg) {
+ if (!array) {
+ return [];
+ }
var n = 0,
length = array.length;
if (typeof callback != 'number' && callback != null) {
var index = length;
- callback = createCallback(callback, thisArg);
+ callback = lodash.createCallback(callback, thisArg);
while (index-- && callback(array[index], index, array)) {
n++;
}
} else {
- n = callback;
- if (n == null || thisArg) {
- return array[length - 1];
- }
+ n = (callback == null || thisArg) ? 1 : callback || n;
}
- return slice(array, nativeMax(0, length - n));
+ return slice(array, 0, nativeMin(nativeMax(0, length - n), length));
}
- }
- /**
- * Gets the index at which the last occurrence of `value` is found 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 _
- * @category Arrays
- * @param {Array} array The array to search.
- * @param {Mixed} value The value to search for.
- * @param {Number} [fromIndex=array.length-1] The index to search from.
- * @returns {Number} Returns the index of the matched value or `-1`.
- * @example
- *
- * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
- * // => 4
- *
- * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3);
- * // => 1
- */
- function lastIndexOf(array, value, fromIndex) {
- var index = array ? array.length : 0;
- if (typeof fromIndex == 'number') {
- index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1;
- }
- while (index--) {
- if (array[index] === value) {
- return index;
+ /**
+ * Computes the intersection of all the passed-in arrays using strict equality
+ * for comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} [array1, array2, ...] Arrays to process.
+ * @returns {Array} Returns a new array of unique elements that are present
+ * in **all** of the arrays.
+ * @example
+ *
+ * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);
+ * // => [1, 2]
+ */
+ function intersection(array) {
+ var args = arguments,
+ argsLength = args.length,
+ cache = { '0': {} },
+ index = -1,
+ length = array ? array.length : 0,
+ isLarge = length >= 100,
+ result = [],
+ seen = result;
+
+ outer:
+ while (++index < length) {
+ var value = array[index];
+ if (isLarge) {
+ var key = String(value);
+ var inited = hasOwnProperty.call(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;
+ while (--argsIndex) {
+ if (!(cache[argsIndex] || (cache[argsIndex] = cachedContains(args[argsIndex], 0, 100)))(value)) {
+ continue outer;
+ }
+ }
+ result.push(value);
+ }
}
+ return result;
}
- return -1;
- }
- /**
- * Creates an object composed from arrays of `keys` and `values`. Pass either
- * a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`, or
- * two arrays, one of `keys` and one of corresponding `values`.
- *
- * @static
- * @memberOf _
- * @category Arrays
- * @param {Array} keys The array of keys.
- * @param {Array} [values=[]] The array of values.
- * @returns {Object} Returns an object composed of the given keys and
- * corresponding values.
- * @example
- *
- * _.object(['moe', 'larry'], [30, 40]);
- * // => { 'moe': 30, 'larry': 40 }
- */
- function object(keys, values) {
- var index = -1,
- length = keys ? keys.length : 0,
- result = {};
-
- while (++index < length) {
- var key = keys[index];
- if (values) {
- result[key] = values[index];
- } else {
- result[key[0]] = key[1];
+ /**
+ * Gets the last element of the `array`. If a number `n` is passed, the
+ * last `n` elements of the `array` are returned. If a `callback` function
+ * is passed, elements at the end of the array are returned as long as the
+ * `callback` returns truthy. 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.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|Number|String} [callback|n] The function called
+ * per element or the number of elements to return. If a property name or
+ * object is passed, it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Mixed} Returns the last element(s) of `array`.
+ * @example
+ *
+ * _.last([1, 2, 3]);
+ * // => 3
+ *
+ * _.last([1, 2, 3], 2);
+ * // => [2, 3]
+ *
+ * _.last([1, 2, 3], function(num) {
+ * return num > 1;
+ * });
+ * // => [2, 3]
+ *
+ * var food = [
+ * { 'name': 'beet', 'organic': false },
+ * { 'name': 'carrot', 'organic': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.last(food, 'organic');
+ * // => [{ 'name': 'carrot', 'organic': true }]
+ *
+ * var food = [
+ * { 'name': 'banana', 'type': 'fruit' },
+ * { 'name': 'beet', 'type': 'vegetable' },
+ * { 'name': 'carrot', 'type': 'vegetable' }
+ * ];
+ *
+ * // using "_.where" callback shorthand
+ * _.last(food, { 'type': 'vegetable' });
+ * // => [{ 'name': 'beet', 'type': 'vegetable' }, { 'name': 'carrot', 'type': 'vegetable' }]
+ */
+ function last(array, callback, thisArg) {
+ if (array) {
+ var n = 0,
+ length = array.length;
+
+ if (typeof callback != 'number' && callback != null) {
+ var index = length;
+ callback = lodash.createCallback(callback, thisArg);
+ while (index-- && callback(array[index], index, array)) {
+ n++;
+ }
+ } else {
+ n = callback;
+ if (n == null || thisArg) {
+ return array[length - 1];
+ }
+ }
+ return slice(array, nativeMax(0, length - n));
}
}
- return result;
- }
-
- /**
- * Creates an array of numbers (positive and/or negative) progressing from
- * `start` up to but not including `end`.
- *
- * @static
- * @memberOf _
- * @category Arrays
- * @param {Number} [start=0] The start of the range.
- * @param {Number} end The end of the range.
- * @param {Number} [step=1] The value to increment or descrement by.
- * @returns {Array} Returns a new range array.
- * @example
- *
- * _.range(10);
- * // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- *
- * _.range(1, 11);
- * // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- *
- * _.range(0, 30, 5);
- * // => [0, 5, 10, 15, 20, 25]
- *
- * _.range(0, -10, -1);
- * // => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
- *
- * _.range(0);
- * // => []
- */
- function range(start, end, step) {
- start = +start || 0;
- step = +step || 1;
- if (end == null) {
- end = start;
- start = 0;
- }
- // use `Array(length)` so V8 will avoid the slower "dictionary" mode
- // http://youtu.be/XAqIpGU8ZZk#t=17m25s
- var index = -1,
- length = nativeMax(0, ceil((end - start) / step)),
- result = Array(length);
-
- while (++index < length) {
- result[index] = start;
- start += step;
+ /**
+ * Gets the index at which the last occurrence of `value` is found 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 _
+ * @category Arrays
+ * @param {Array} array The array to search.
+ * @param {Mixed} value The value to search for.
+ * @param {Number} [fromIndex=array.length-1] The index to search from.
+ * @returns {Number} Returns the index of the matched value or `-1`.
+ * @example
+ *
+ * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
+ * // => 4
+ *
+ * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3);
+ * // => 1
+ */
+ function lastIndexOf(array, value, fromIndex) {
+ var index = array ? array.length : 0;
+ if (typeof fromIndex == 'number') {
+ index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1;
+ }
+ while (index--) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
}
- return result;
- }
- /**
- * The opposite of `_.initial`, this method gets all but the first value of `array`.
- * If a number `n` is passed, the first `n` values are excluded from the result.
- * If a `callback` function is passed, the first elements the `callback` returns
- * truthy for are excluded from the result. 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.
- *
- * If an object is passed for `callback`, the created "_.where" style callback
- * will return `true` for elements that have the propeties of the given object,
- * else `false`.
- *
- * @static
- * @memberOf _
- * @alias drop, tail
- * @category Arrays
- * @param {Array} array The array to query.
- * @param {Function|Object|Number|String} [callback|n=1] The function called
- * per element or the number of elements to exclude. If a property name or
- * object is passed, it will be used to create a "_.pluck" or "_.where"
- * style callback, respectively.
- * @param {Mixed} [thisArg] The `this` binding of `callback`.
- * @returns {Array} Returns a slice of `array`.
- * @example
- *
- * _.rest([1, 2, 3]);
- * // => [2, 3]
- *
- * _.rest([1, 2, 3], 2);
- * // => [3]
- *
- * _.rest([1, 2, 3], function(num) {
- * return num < 3;
- * });
- * // => [3]
- *
- * var food = [
- * { 'name': 'banana', 'organic': true },
- * { 'name': 'beet', 'organic': false },
- * ];
- *
- * // using "_.pluck" callback shorthand
- * _.rest(food, 'organic');
- * // => [{ 'name': 'beet', 'organic': false }]
- *
- * var food = [
- * { 'name': 'apple', 'type': 'fruit' },
- * { 'name': 'banana', 'type': 'fruit' },
- * { 'name': 'beet', 'type': 'vegetable' }
- * ];
- *
- * // using "_.where" callback shorthand
- * _.rest(food, { 'type': 'fruit' });
- * // => [{ 'name': 'beet', 'type': 'vegetable' }]
- */
- function rest(array, callback, thisArg) {
- if (typeof callback != 'number' && callback != null) {
- var n = 0,
- index = -1,
- length = array ? array.length : 0;
+ /**
+ * Creates an array of numbers (positive and/or negative) progressing from
+ * `start` up to but not including `end`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Number} [start=0] The start of the range.
+ * @param {Number} end The end of the range.
+ * @param {Number} [step=1] The value to increment or decrement by.
+ * @returns {Array} Returns a new range array.
+ * @example
+ *
+ * _.range(10);
+ * // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ *
+ * _.range(1, 11);
+ * // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ *
+ * _.range(0, 30, 5);
+ * // => [0, 5, 10, 15, 20, 25]
+ *
+ * _.range(0, -10, -1);
+ * // => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
+ *
+ * _.range(0);
+ * // => []
+ */
+ function range(start, end, step) {
+ start = +start || 0;
+ step = +step || 1;
- callback = createCallback(callback, thisArg);
- while (++index < length && callback(array[index], index, array)) {
- n++;
+ if (end == null) {
+ end = start;
+ start = 0;
}
- } else {
- n = (callback == null || thisArg) ? 1 : nativeMax(0, callback);
- }
- return slice(array, n);
- }
+ // use `Array(length)` so V8 will avoid the slower "dictionary" mode
+ // http://youtu.be/XAqIpGU8ZZk#t=17m25s
+ var index = -1,
+ length = nativeMax(0, ceil((end - start) / step)),
+ result = Array(length);
- /**
- * Uses a binary search to determine the smallest index at which the `value`
- * should be inserted into `array` in order to maintain the sort order of the
- * sorted `array`. If `callback` is passed, it will be executed for `value` and
- * each element in `array` to compute their sort ranking. The `callback` is
- * bound to `thisArg` and invoked with one argument; (value).
- *
- * If a property name is passed for `callback`, the created "_.pluck" style
- * callback will return the property value of the given element.
- *
- * If an object is passed for `callback`, the created "_.where" style callback
- * will return `true` for elements that have the propeties of the given object,
- * else `false`.
- *
- * @static
- * @memberOf _
- * @category Arrays
- * @param {Array} array The array to iterate over.
- * @param {Mixed} value The value to evaluate.
- * @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.
- * @param {Mixed} [thisArg] The `this` binding of `callback`.
- * @returns {Number} Returns the index at which the value should be inserted
- * into `array`.
- * @example
- *
- * _.sortedIndex([20, 30, 50], 40);
- * // => 2
- *
- * // using "_.pluck" callback shorthand
- * _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');
- * // => 2
- *
- * var dict = {
- * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 }
- * };
- *
- * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
- * return dict.wordToNumber[word];
- * });
- * // => 2
- *
- * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
- * return this.wordToNumber[word];
- * }, dict);
- * // => 2
- */
- function sortedIndex(array, value, callback, thisArg) {
- var low = 0,
- high = array ? array.length : low;
-
- // explicitly reference `identity` for better inlining in Firefox
- callback = callback ? createCallback(callback, thisArg, 1) : identity;
- value = callback(value);
-
- while (low < high) {
- var mid = (low + high) >>> 1;
- callback(array[mid]) < value
- ? low = mid + 1
- : high = mid;
+ while (++index < length) {
+ result[index] = start;
+ start += step;
+ }
+ return result;
}
- return low;
- }
- /**
- * Computes the union of the passed-in arrays using strict equality for
- * comparisons, i.e. `===`.
- *
- * @static
- * @memberOf _
- * @category Arrays
- * @param {Array} [array1, array2, ...] Arrays to process.
- * @returns {Array} Returns a new array of unique values, in order, that are
- * present in one or more of the arrays.
- * @example
- *
- * _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
- * // => [1, 2, 3, 101, 10]
- */
- function union() {
- return uniq(concat.apply(arrayRef, arguments));
- }
-
- /**
- * Creates a duplicate-value-free version of the `array` using strict equality
- * for comparisons, i.e. `===`. If the `array` is already sorted, passing `true`
- * for `isSorted` will run a faster algorithm. If `callback` is passed, each
- * element of `array` is passed through a callback` before uniqueness is computed.
- * The `callback` is bound to `thisArg` and invoked with 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.
- *
- * If an object is passed for `callback`, the created "_.where" style callback
- * will return `true` for elements that have the propeties of the given object,
- * else `false`.
- *
- * @static
- * @memberOf _
- * @alias unique
- * @category Arrays
- * @param {Array} array The array to process.
- * @param {Boolean} [isSorted=false] A flag to indicate that the `array` is already sorted.
- * @param {Function|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.
- * @param {Mixed} [thisArg] The `this` binding of `callback`.
- * @returns {Array} Returns a duplicate-value-free array.
- * @example
- *
- * _.uniq([1, 2, 1, 3, 1]);
- * // => [1, 2, 3]
- *
- * _.uniq([1, 1, 2, 2, 3], true);
- * // => [1, 2, 3]
- *
- * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return Math.floor(num); });
- * // => [1, 2, 3]
- *
- * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math);
- * // => [1, 2, 3]
- *
- * // using "_.pluck" callback shorthand
- * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
- * // => [{ 'x': 1 }, { 'x': 2 }]
- */
- function uniq(array, isSorted, callback, thisArg) {
- var index = -1,
- length = array ? array.length : 0,
- result = [],
- seen = result;
-
- // juggle arguments
- if (typeof isSorted == 'function') {
- thisArg = callback;
- callback = isSorted;
- isSorted = false;
- }
- // init value cache for large arrays
- var isLarge = !isSorted && length >= 75;
- if (isLarge) {
- var cache = {};
- }
- if (callback) {
- seen = [];
- callback = createCallback(callback, thisArg);
- }
- while (++index < length) {
- var value = array[index],
- computed = callback ? callback(value, index, array) : value;
+ /**
+ * The opposite of `_.initial`, this method gets all but the first value of
+ * `array`. If a number `n` is passed, the first `n` values are excluded from
+ * the result. If a `callback` function is passed, elements at the beginning
+ * of the array are excluded from the result as long as the `callback` returns
+ * truthy. 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.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias drop, tail
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|Number|String} [callback|n=1] The function called
+ * per element or the number of elements to exclude. If a property name or
+ * object is passed, it will be used to create a "_.pluck" or "_.where"
+ * style callback, respectively.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a slice of `array`.
+ * @example
+ *
+ * _.rest([1, 2, 3]);
+ * // => [2, 3]
+ *
+ * _.rest([1, 2, 3], 2);
+ * // => [3]
+ *
+ * _.rest([1, 2, 3], function(num) {
+ * return num < 3;
+ * });
+ * // => [3]
+ *
+ * var food = [
+ * { 'name': 'banana', 'organic': true },
+ * { 'name': 'beet', 'organic': false },
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.rest(food, 'organic');
+ * // => [{ 'name': 'beet', 'organic': false }]
+ *
+ * var food = [
+ * { 'name': 'apple', 'type': 'fruit' },
+ * { 'name': 'banana', 'type': 'fruit' },
+ * { 'name': 'beet', 'type': 'vegetable' }
+ * ];
+ *
+ * // using "_.where" callback shorthand
+ * _.rest(food, { 'type': 'fruit' });
+ * // => [{ 'name': 'beet', 'type': 'vegetable' }]
+ */
+ function rest(array, callback, thisArg) {
+ if (typeof callback != 'number' && callback != null) {
+ var n = 0,
+ index = -1,
+ length = array ? array.length : 0;
- if (isLarge) {
- var key = computed + '';
- var inited = hasOwnProperty.call(cache, key)
- ? !(seen = cache[key])
- : (seen = cache[key] = []);
- }
- if (isSorted
- ? !index || seen[seen.length - 1] !== computed
- : inited || indexOf(seen, computed) < 0
- ) {
- if (callback || isLarge) {
- seen.push(computed);
+ callback = lodash.createCallback(callback, thisArg);
+ while (++index < length && callback(array[index], index, array)) {
+ n++;
}
- result.push(value);
+ } else {
+ n = (callback == null || thisArg) ? 1 : nativeMax(0, callback);
}
+ return slice(array, n);
}
- return result;
- }
- /**
- * Creates an array with all occurrences of the passed values removed using
- * strict equality for comparisons, i.e. `===`.
- *
- * @static
- * @memberOf _
- * @category Arrays
- * @param {Array} array The array to filter.
- * @param {Mixed} [value1, value2, ...] Values to remove.
- * @returns {Array} Returns a new filtered array.
- * @example
- *
- * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
- * // => [2, 3, 4]
- */
- function without(array) {
- var index = -1,
- length = array ? array.length : 0,
- contains = cachedContains(arguments, 1),
- result = [];
-
- while (++index < length) {
- var value = array[index];
- if (!contains(value)) {
- result.push(value);
+ /**
+ * Uses a binary search to determine the smallest index at which the `value`
+ * should be inserted into `array` in order to maintain the sort order of the
+ * sorted `array`. If `callback` is passed, it will be executed for `value` and
+ * each element in `array` to compute their sort ranking. The `callback` is
+ * bound to `thisArg` and invoked with one argument; (value).
+ *
+ * If a property name is passed for `callback`, the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to iterate over.
+ * @param {Mixed} value The value to evaluate.
+ * @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.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Number} Returns the index at which the value should be inserted
+ * into `array`.
+ * @example
+ *
+ * _.sortedIndex([20, 30, 50], 40);
+ * // => 2
+ *
+ * // using "_.pluck" callback shorthand
+ * _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');
+ * // => 2
+ *
+ * var dict = {
+ * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 }
+ * };
+ *
+ * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+ * return dict.wordToNumber[word];
+ * });
+ * // => 2
+ *
+ * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+ * return this.wordToNumber[word];
+ * }, dict);
+ * // => 2
+ */
+ function sortedIndex(array, value, callback, thisArg) {
+ var low = 0,
+ high = array ? array.length : low;
+
+ // explicitly reference `identity` for better inlining in Firefox
+ callback = callback ? lodash.createCallback(callback, thisArg, 1) : identity;
+ value = callback(value);
+
+ while (low < high) {
+ var mid = (low + high) >>> 1;
+ (callback(array[mid]) < value)
+ ? low = mid + 1
+ : high = mid;
}
+ return low;
}
- return result;
- }
- /**
- * Groups the elements of each array at their corresponding indexes. Useful for
- * separate data sources that are coordinated through matching array indexes.
- * For a matrix of nested arrays, `_.zip.apply(...)` can transpose the matrix
- * in a similar fashion.
- *
- * @static
- * @memberOf _
- * @category Arrays
- * @param {Array} [array1, array2, ...] Arrays to process.
- * @returns {Array} Returns a new array of grouped elements.
- * @example
- *
- * _.zip(['moe', 'larry'], [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);
+ /**
+ * Computes the union of the passed-in arrays using strict equality for
+ * comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} [array1, array2, ...] Arrays to process.
+ * @returns {Array} Returns a new array of unique values, in order, that are
+ * present in one or more of the arrays.
+ * @example
+ *
+ * _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
+ * // => [1, 2, 3, 101, 10]
+ */
+ function union() {
+ return uniq(concat.apply(arrayRef, arguments));
+ }
- while (++index < length) {
- result[index] = pluck(arguments, index);
+ /**
+ * Creates a duplicate-value-free version of the `array` using strict equality
+ * for comparisons, i.e. `===`. If the `array` is already sorted, passing `true`
+ * for `isSorted` will run a faster algorithm. If `callback` is passed, each
+ * element of `array` is passed through a callback` before uniqueness is computed.
+ * The `callback` is bound to `thisArg` and invoked with 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.
+ *
+ * If an object is passed for `callback`, the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias unique
+ * @category Arrays
+ * @param {Array} array The array to process.
+ * @param {Boolean} [isSorted=false] A flag to indicate that the `array` is already sorted.
+ * @param {Function|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.
+ * @param {Mixed} [thisArg] The `this` binding of `callback`.
+ * @returns {Array} Returns a duplicate-value-free array.
+ * @example
+ *
+ * _.uniq([1, 2, 1, 3, 1]);
+ * // => [1, 2, 3]
+ *
+ * _.uniq([1, 1, 2, 2, 3], true);
+ * // => [1, 2, 3]
+ *
+ * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return Math.floor(num); });
+ * // => [1, 2, 3]
+ *
+ * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math);
+ * // => [1, 2, 3]
+ *
+ * // using "_.pluck" callback shorthand
+ * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 1 }, { 'x': 2 }]
+ */
+ function uniq(array, isSorted, callback, thisArg) {
+ var index = -1,
+ length = array ? array.length : 0,
+ result = [],
+ seen = 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 >= 75;
+ if (isLarge) {
+ var cache = {};
+ }
+ 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 = String(computed);
+ var inited = hasOwnProperty.call(cache, key)
+ ? !(seen = cache[key])
+ : (seen = cache[key] = []);
+ }
+ if (isSorted
+ ? !index || seen[seen.length - 1] !== computed
+ : inited || indexOf(seen, computed) < 0
+ ) {
+ if (callback || isLarge) {
+ seen.push(computed);
+ }
+ result.push(value);
+ }
+ }
+ return result;
}
- return result;
- }
- /*--------------------------------------------------------------------------*/
+ /**
+ * Creates an array with all occurrences of the passed values removed using
+ * strict equality for comparisons, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to filter.
+ * @param {Mixed} [value1, value2, ...] Values to remove.
+ * @returns {Array} Returns a new filtered array.
+ * @example
+ *
+ * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
+ * // => [2, 3, 4]
+ */
+ function without(array) {
+ var index = -1,
+ length = array ? array.length : 0,
+ contains = cachedContains(arguments, 1, 30),
+ result = [];
- /**
- * Creates a function that is restricted to executing `func` only after it is
- * called `n` times. The `func` is executed with the `this` binding of the
- * created function.
- *
- * @static
- * @memberOf _
- * @category Functions
- * @param {Number} n The number of times the function must be called before
- * it is executed.
- * @param {Function} func The function to restrict.
- * @returns {Function} Returns the new restricted function.
- * @example
- *
- * var renderNotes = _.after(notes.length, render);
- * _.forEach(notes, function(note) {
- * note.asyncSave({ 'success': renderNotes });
- * });
- * // `renderNotes` is run once, after all notes have saved
- */
- function after(n, func) {
- if (n < 1) {
- return func();
+ while (++index < length) {
+ var value = array[index];
+ if (!contains(value)) {
+ result.push(value);
+ }
+ }
+ return result;
}
- return function() {
- if (--n < 1) {
- return func.apply(this, arguments);
+
+ /**
+ * Groups the elements of each array at their corresponding indexes. Useful for
+ * separate data sources that are coordinated through matching array indexes.
+ * For a matrix of nested arrays, `_.zip.apply(...)` can transpose the matrix
+ * in a similar fashion.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} [array1, array2, ...] Arrays to process.
+ * @returns {Array} Returns a new array of grouped elements.
+ * @example
+ *
+ * _.zip(['moe', 'larry'], [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;
+ }
- /**
- * Creates a function that, when called, invokes `func` with the `this`
- * binding of `thisArg` and prepends any additional `bind` arguments to those
- * passed to the bound function.
- *
- * @static
- * @memberOf _
- * @category Functions
- * @param {Function} func The function to bind.
- * @param {Mixed} [thisArg] The `this` binding of `func`.
- * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied.
- * @returns {Function} Returns the new bound function.
- * @example
- *
- * var func = function(greeting) {
- * return greeting + ' ' + this.name;
- * };
- *
- * func = _.bind(func, { 'name': 'moe' }, 'hi');
- * func();
- * // => 'hi moe'
- */
- function bind(func, thisArg) {
- // use `Function#bind` if it exists and is fast
- // (in V8 `Function#bind` is slower except when partially applied)
- return isBindFast || (nativeBind && arguments.length > 2)
- ? nativeBind.call.apply(nativeBind, arguments)
- : createBound(func, thisArg, slice(arguments, 2));
- }
+ /**
+ * Creates an object composed from arrays of `keys` and `values`. Pass either
+ * a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`, or
+ * two arrays, one of `keys` and one of corresponding `values`.
+ *
+ * @static
+ * @memberOf _
+ * @alias object
+ * @category Arrays
+ * @param {Array} keys The array of keys.
+ * @param {Array} [values=[]] The array of values.
+ * @returns {Object} Returns an object composed of the given keys and
+ * corresponding values.
+ * @example
+ *
+ * _.zipObject(['moe', 'larry'], [30, 40]);
+ * // => { 'moe': 30, 'larry': 40 }
+ */
+ function zipObject(keys, values) {
+ var index = -1,
+ length = keys ? keys.length : 0,
+ result = {};
- /**
- * Binds methods on `object` to `object`, overwriting the existing method.
- * Method names may be specified as individual arguments or as arrays of method
- * names. If no method names are provided, all the function properties of `object`
- * will be bound.
- *
- * @static
- * @memberOf _
- * @category Functions
- * @param {Object} object The object to bind and assign the bound methods to.
- * @param {String} [methodName1, methodName2, ...] Method names on the object to bind.
- * @returns {Object} Returns `object`.
- * @example
- *
- * var view = {
- * 'label': 'docs',
- * 'onClick': function() { alert('clicked ' + this.label); }
- * };
- *
- * _.bindAll(view);
- * jQuery('#docs').on('click', view.onClick);
- * // => alerts 'clicked docs', when the button is clicked
- */
- function bindAll(object) {
- var funcs = concat.apply(arrayRef, arguments),
- index = funcs.length > 1 ? 0 : (funcs = functions(object), -1),
- length = funcs.length;
-
- while (++index < length) {
- var key = funcs[index];
- object[key] = bind(object[key], object);
+ while (++index < length) {
+ var key = keys[index];
+ if (values) {
+ result[key] = values[index];
+ } else {
+ result[key[0]] = key[1];
+ }
+ }
+ return result;
}
- return object;
- }
- /**
- * Creates a function that, when called, invokes the method at `object[key]`
- * and prepends any additional `bindKey` arguments to those passed to the bound
- * function. This method differs from `_.bind` by allowing bound functions to
- * reference methods that will be redefined or don't yet exist.
- * See http://michaux.ca/articles/lazy-function-definition-pattern.
- *
- * @static
- * @memberOf _
- * @category Functions
- * @param {Object} object The object the method belongs to.
- * @param {String} key The key of the method.
- * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied.
- * @returns {Function} Returns the new bound function.
- * @example
- *
- * var object = {
- * 'name': 'moe',
- * 'greet': function(greeting) {
- * return greeting + ' ' + this.name;
- * }
- * };
- *
- * var func = _.bindKey(object, 'greet', 'hi');
- * func();
- * // => 'hi moe'
- *
- * object.greet = function(greeting) {
- * return greeting + ', ' + this.name + '!';
- * };
- *
- * func();
- * // => 'hi, moe!'
- */
- function bindKey(object, key) {
- return createBound(object, key, slice(arguments, 2));
- }
+ /*--------------------------------------------------------------------------*/
- /**
- * Creates a function that is the composition of the passed functions,
- * where each function consumes the return value of the function that follows.
- * For example, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
- * Each function is executed with the `this` binding of the composed function.
- *
- * @static
- * @memberOf _
- * @category Functions
- * @param {Function} [func1, func2, ...] Functions to compose.
- * @returns {Function} Returns the new composed function.
- * @example
- *
- * var greet = function(name) { return 'hi ' + name; };
- * var exclaim = function(statement) { return statement + '!'; };
- * var welcome = _.compose(exclaim, greet);
- * welcome('moe');
- * // => 'hi moe!'
- */
- function compose() {
- var funcs = arguments;
- return function() {
- var args = arguments,
+ /**
+ * If `n` is greater than `0`, a function is created that is restricted to
+ * executing `func`, with the `this` binding and arguments of the created
+ * function, only after it is called `n` times. If `n` is less than `1`,
+ * `func` is executed immediately, without a `this` binding or additional
+ * arguments, and its result is returned.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Number} n The number of times the function must be called before
+ * it is executed.
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var renderNotes = _.after(notes.length, render);
+ * _.forEach(notes, function(note) {
+ * note.asyncSave({ 'success': renderNotes });
+ * });
+ * // `renderNotes` is run once, after all notes have saved
+ */
+ function after(n, func) {
+ if (n < 1) {
+ return func();
+ }
+ return function() {
+ if (--n < 1) {
+ return func.apply(this, arguments);
+ }
+ };
+ }
+
+ /**
+ * Creates a function that, when called, invokes `func` with the `this`
+ * binding of `thisArg` and prepends any additional `bind` arguments to those
+ * passed to the bound function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to bind.
+ * @param {Mixed} [thisArg] The `this` binding of `func`.
+ * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
+ * @example
+ *
+ * var func = function(greeting) {
+ * return greeting + ' ' + this.name;
+ * };
+ *
+ * func = _.bind(func, { 'name': 'moe' }, 'hi');
+ * func();
+ * // => 'hi moe'
+ */
+ function bind(func, thisArg) {
+ // use `Function#bind` if it exists and is fast
+ // (in V8 `Function#bind` is slower except when partially applied)
+ return support.fastBind || (nativeBind && arguments.length > 2)
+ ? nativeBind.call.apply(nativeBind, arguments)
+ : createBound(func, thisArg, slice(arguments, 2));
+ }
+
+ /**
+ * Binds methods on `object` to `object`, overwriting the existing method.
+ * Method names may be specified as individual arguments or as arrays of method
+ * names. If no method names are provided, all the function properties of `object`
+ * will be bound.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Object} object The object to bind and assign the bound methods to.
+ * @param {String} [methodName1, methodName2, ...] Method names on the object to bind.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * var view = {
+ * 'label': 'docs',
+ * 'onClick': function() { alert('clicked ' + this.label); }
+ * };
+ *
+ * _.bindAll(view);
+ * jQuery('#docs').on('click', view.onClick);
+ * // => alerts 'clicked docs', when the button is clicked
+ */
+ function bindAll(object) {
+ var funcs = concat.apply(arrayRef, arguments),
+ index = funcs.length > 1 ? 0 : (funcs = functions(object), -1),
length = funcs.length;
- while (length--) {
- args = [funcs[length].apply(this, args)];
+ while (++index < length) {
+ var key = funcs[index];
+ object[key] = bind(object[key], object);
}
- return args[0];
- };
- }
+ return object;
+ }
- /**
- * Creates a function that will delay the execution of `func` until after
- * `wait` milliseconds have elapsed since the last time it was invoked. Pass
- * `true` for `immediate` to cause debounce to invoke `func` on the leading,
- * instead of the trailing, edge of the `wait` timeout. Subsequent calls to
- * the debounced function will return the result of the last `func` call.
- *
- * @static
- * @memberOf _
- * @category Functions
- * @param {Function} func The function to debounce.
- * @param {Number} wait The number of milliseconds to delay.
- * @param {Boolean} immediate A flag to indicate execution is on the leading
- * edge of the timeout.
- * @returns {Function} Returns the new debounced function.
- * @example
- *
- * var lazyLayout = _.debounce(calculateLayout, 300);
- * jQuery(window).on('resize', lazyLayout);
- */
- function debounce(func, wait, immediate) {
- var args,
- result,
- thisArg,
- timeoutId;
-
- function delayed() {
- timeoutId = null;
- if (!immediate) {
- result = func.apply(thisArg, args);
+ /**
+ * Creates a function that, when called, invokes the method at `object[key]`
+ * and prepends any additional `bindKey` arguments to those passed to the bound
+ * function. This method differs from `_.bind` by allowing bound functions to
+ * reference methods that will be redefined or don't yet exist.
+ * See http://michaux.ca/articles/lazy-function-definition-pattern.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Object} object The object the method belongs to.
+ * @param {String} key The key of the method.
+ * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
+ * @example
+ *
+ * var object = {
+ * 'name': 'moe',
+ * 'greet': function(greeting) {
+ * return greeting + ' ' + this.name;
+ * }
+ * };
+ *
+ * var func = _.bindKey(object, 'greet', 'hi');
+ * func();
+ * // => 'hi moe'
+ *
+ * object.greet = function(greeting) {
+ * return greeting + ', ' + this.name + '!';
+ * };
+ *
+ * func();
+ * // => 'hi, moe!'
+ */
+ function bindKey(object, key) {
+ return createBound(object, key, slice(arguments, 2), indicatorObject);
+ }
+
+ /**
+ * Creates a function that is the composition of the passed functions,
+ * where each function consumes the return value of the function that follows.
+ * For example, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
+ * Each function is executed with the `this` binding of the composed function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} [func1, func2, ...] Functions to compose.
+ * @returns {Function} Returns the new composed function.
+ * @example
+ *
+ * var greet = function(name) { return 'hi ' + name; };
+ * var exclaim = function(statement) { return statement + '!'; };
+ * var welcome = _.compose(exclaim, greet);
+ * welcome('moe');
+ * // => 'hi moe!'
+ */
+ function compose() {
+ var funcs = arguments;
+ return function() {
+ var args = arguments,
+ length = funcs.length;
+
+ while (length--) {
+ args = [funcs[length].apply(this, args)];
+ }
+ return args[0];
+ };
+ }
+
+ /**
+ * 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`.
+ *
+ * Note: All Lo-Dash methods, that accept a `callback` argument, use `_.createCallback`.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @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.
+ * @example
+ *
+ * var stooges = [
+ * { 'name': 'moe', 'age': 40 },
+ * { 'name': 'larry', 'age': 50 }
+ * ];
+ *
+ * // wrap to create custom callback shorthands
+ * _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) {
+ * var match = /^(.+?)__([gl]t)(.+)$/.exec(callback);
+ * return !match ? func(callback, thisArg) : function(object) {
+ * return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3];
+ * };
+ * });
+ *
+ * _.filter(stooges, 'age__gt45');
+ * // => [{ 'name': 'larry', 'age': 50 }]
+ *
+ * // create mixins with support for "_.pluck" and "_.where" callback shorthands
+ * _.mixin({
+ * 'toLookup': function(collection, callback, thisArg) {
+ * callback = _.createCallback(callback, thisArg);
+ * return _.reduce(collection, function(result, value, index, collection) {
+ * return (result[callback(value, index, collection)] = value, result);
+ * }, {});
+ * }
+ * });
+ *
+ * _.toLookup(stooges, 'name');
+ * // => { 'moe': { 'name': 'moe', 'age': 40 }, 'larry': { 'name': 'larry', 'age': 50 } }
+ */
+ 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, collection) {
+ return func.call(thisArg, accumulator, value, index, collection);
+ };
+ }
+ return function(value, index, collection) {
+ return func.call(thisArg, value, index, collection);
+ };
+ }
+ return func;
}
- return function() {
- var isImmediate = immediate && !timeoutId;
- args = arguments;
- thisArg = this;
- clearTimeout(timeoutId);
- timeoutId = setTimeout(delayed, wait);
+ /**
+ * Creates a function that will delay the execution of `func` until after
+ * `wait` milliseconds have elapsed since the last time it was invoked. Pass
+ * `true` for `immediate` to cause debounce to invoke `func` on the leading,
+ * instead of the trailing, edge of the `wait` timeout. Subsequent calls to
+ * the debounced function will return the result of the last `func` call.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to debounce.
+ * @param {Number} wait The number of milliseconds to delay.
+ * @param {Boolean} immediate A flag to indicate execution is on the leading
+ * edge of the timeout.
+ * @returns {Function} Returns the new debounced function.
+ * @example
+ *
+ * var lazyLayout = _.debounce(calculateLayout, 300);
+ * jQuery(window).on('resize', lazyLayout);
+ */
+ function debounce(func, wait, immediate) {
+ var args,
+ result,
+ thisArg,
+ timeoutId;
- if (isImmediate) {
- result = func.apply(thisArg, args);
+ function delayed() {
+ timeoutId = null;
+ if (!immediate) {
+ result = func.apply(thisArg, args);
+ }
}
- return result;
- };
- }
+ return function() {
+ var isImmediate = immediate && !timeoutId;
+ args = arguments;
+ thisArg = this;
- /**
- * Executes the `func` function after `wait` milliseconds. Additional arguments
- * will be passed to `func` when it is invoked.
- *
- * @static
- * @memberOf _
- * @category Functions
- * @param {Function} func The function to delay.
- * @param {Number} wait The number of milliseconds to delay execution.
- * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with.
- * @returns {Number} Returns the `setTimeout` timeout id.
- * @example
- *
- * var log = _.bind(console.log, console);
- * _.delay(log, 1000, 'logged later');
- * // => 'logged later' (Appears after one second.)
- */
- function delay(func, wait) {
- var args = slice(arguments, 2);
- return setTimeout(function() { func.apply(undefined, args); }, wait);
- }
+ clearTimeout(timeoutId);
+ timeoutId = setTimeout(delayed, wait);
- /**
- * Defers executing the `func` function until the current call stack has cleared.
- * Additional arguments will be passed to `func` when it is invoked.
- *
- * @static
- * @memberOf _
- * @category Functions
- * @param {Function} func The function to defer.
- * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with.
- * @returns {Number} Returns the `setTimeout` timeout id.
- * @example
- *
- * _.defer(function() { alert('deferred'); });
- * // returns from the function before `alert` is called
- */
- function defer(func) {
- var args = slice(arguments, 1);
- return setTimeout(function() { func.apply(undefined, args); }, 1);
- }
- // use `setImmediate` if it's available in Node.js
- if (isV8 && freeModule && typeof setImmediate == 'function') {
- defer = bind(setImmediate, window);
- }
+ if (isImmediate) {
+ result = func.apply(thisArg, args);
+ }
+ return result;
+ };
+ }
- /**
- * 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.
- *
- * @static
- * @memberOf _
- * @category Functions
- * @param {Function} func The function to have its output memoized.
- * @param {Function} [resolver] A function used to resolve the cache key.
- * @returns {Function} Returns the new memoizing function.
- * @example
- *
- * var fibonacci = _.memoize(function(n) {
- * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
- * });
- */
- function memoize(func, resolver) {
- var cache = {};
- return function() {
- var key = (resolver ? resolver.apply(this, arguments) : arguments[0]) + '';
- return hasOwnProperty.call(cache, key)
- ? cache[key]
- : (cache[key] = func.apply(this, arguments));
- };
- }
+ /**
+ * Defers executing the `func` function until the current call stack has cleared.
+ * Additional arguments will be passed to `func` when it is invoked.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to defer.
+ * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with.
+ * @returns {Number} Returns the timer id.
+ * @example
+ *
+ * _.defer(function() { alert('deferred'); });
+ * // returns from the function before `alert` is called
+ */
+ function defer(func) {
+ var args = slice(arguments, 1);
+ return setTimeout(function() { func.apply(undefined, args); }, 1);
+ }
+ // use `setImmediate` if it's available in Node.js
+ if (isV8 && freeModule && typeof setImmediate == 'function') {
+ defer = bind(setImmediate, context);
+ }
- /**
- * 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
- * with the `this` binding of the created function.
- *
- * @static
- * @memberOf _
- * @category Functions
- * @param {Function} func The function to restrict.
- * @returns {Function} Returns the new restricted function.
- * @example
- *
- * var initialize = _.once(createApplication);
- * initialize();
- * initialize();
- * // `initialize` executes `createApplication` once
- */
- function once(func) {
- var ran,
- result;
+ /**
+ * Executes the `func` function after `wait` milliseconds. Additional arguments
+ * will be passed to `func` when it is invoked.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to delay.
+ * @param {Number} wait The number of milliseconds to delay execution.
+ * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with.
+ * @returns {Number} Returns the timer id.
+ * @example
+ *
+ * var log = _.bind(console.log, console);
+ * _.delay(log, 1000, 'logged later');
+ * // => 'logged later' (Appears after one second.)
+ */
+ function delay(func, wait) {
+ var args = slice(arguments, 2);
+ return setTimeout(function() { func.apply(undefined, args); }, wait);
+ }
- return function() {
- if (ran) {
- return result;
- }
- ran = true;
- result = func.apply(this, arguments);
+ /**
+ * 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.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to have its output memoized.
+ * @param {Function} [resolver] A function used to resolve the cache key.
+ * @returns {Function} Returns the new memoizing function.
+ * @example
+ *
+ * var fibonacci = _.memoize(function(n) {
+ * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
+ * });
+ */
+ function memoize(func, resolver) {
+ var cache = {};
+ return function() {
+ var key = String(resolver ? resolver.apply(this, arguments) : arguments[0]);
+ return hasOwnProperty.call(cache, key)
+ ? cache[key]
+ : (cache[key] = func.apply(this, arguments));
+ };
+ }
- // clear the `func` variable so the function may be garbage collected
- func = null;
- return result;
- };
- }
+ /**
+ * 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
+ * with the `this` binding of the created function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var initialize = _.once(createApplication);
+ * initialize();
+ * initialize();
+ * // `initialize` executes `createApplication` once
+ */
+ function once(func) {
+ var ran,
+ result;
- /**
- * Creates a function that, when called, invokes `func` with any additional
- * `partial` arguments prepended to those passed to the new function. This
- * method is similar to `_.bind`, except it does **not** alter the `this` binding.
- *
- * @static
- * @memberOf _
- * @category Functions
- * @param {Function} func The function to partially apply arguments to.
- * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied.
- * @returns {Function} Returns the new partially applied function.
- * @example
- *
- * var greet = function(greeting, name) { return greeting + ' ' + name; };
- * var hi = _.partial(greet, 'hi');
- * hi('moe');
- * // => 'hi moe'
- */
- function partial(func) {
- return createBound(func, slice(arguments, 1));
- }
+ return function() {
+ if (ran) {
+ return result;
+ }
+ ran = true;
+ result = func.apply(this, arguments);
- /**
- * This method is similar to `_.partial`, except that `partial` arguments are
- * appended to those passed to the new function.
- *
- * @static
- * @memberOf _
- * @category Functions
- * @param {Function} func The function to partially apply arguments to.
- * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied.
- * @returns {Function} Returns the new partially applied function.
- * @example
- *
- * var defaultsDeep = _.partialRight(_.merge, _.defaults);
- *
- * var options = {
- * 'variable': 'data',
- * 'imports': { 'jq': $ }
- * };
- *
- * defaultsDeep(options, _.templateSettings);
- *
- * options.variable
- * // => 'data'
- *
- * options.imports
- * // => { '_': _, 'jq': $ }
- */
- function partialRight(func) {
- return createBound(func, slice(arguments, 1), null, indicatorObject);
- }
+ // clear the `func` variable so the function may be garbage collected
+ func = null;
+ return result;
+ };
+ }
- /**
- * Creates a function that, when executed, will only call the `func`
- * function at most once per every `wait` milliseconds. If the throttled
- * function is invoked more than once during the `wait` timeout, `func` will
- * also be called on the trailing edge of the timeout. Subsequent calls to the
- * throttled function will return the result of the last `func` call.
- *
- * @static
- * @memberOf _
- * @category Functions
- * @param {Function} func The function to throttle.
- * @param {Number} wait The number of milliseconds to throttle executions to.
- * @returns {Function} Returns the new throttled function.
- * @example
- *
- * var throttled = _.throttle(updatePosition, 100);
- * jQuery(window).on('scroll', throttled);
- */
- function throttle(func, wait) {
- var args,
- result,
- thisArg,
- timeoutId,
- lastCalled = 0;
-
- function trailingCall() {
- lastCalled = new Date;
- timeoutId = null;
- result = func.apply(thisArg, args);
+ /**
+ * Creates a function that, when called, invokes `func` with any additional
+ * `partial` arguments prepended to those passed to the new function. This
+ * method is similar to `_.bind`, except it does **not** alter the `this` binding.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied.
+ * @returns {Function} Returns the new partially applied function.
+ * @example
+ *
+ * var greet = function(greeting, name) { return greeting + ' ' + name; };
+ * var hi = _.partial(greet, 'hi');
+ * hi('moe');
+ * // => 'hi moe'
+ */
+ function partial(func) {
+ return createBound(func, slice(arguments, 1));
}
- return function() {
- var now = new Date,
- remaining = wait - (now - lastCalled);
- args = arguments;
- thisArg = this;
+ /**
+ * This method is similar to `_.partial`, except that `partial` arguments are
+ * appended to those passed to the new function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied.
+ * @returns {Function} Returns the new partially applied function.
+ * @example
+ *
+ * var defaultsDeep = _.partialRight(_.merge, _.defaults);
+ *
+ * var options = {
+ * 'variable': 'data',
+ * 'imports': { 'jq': $ }
+ * };
+ *
+ * defaultsDeep(options, _.templateSettings);
+ *
+ * options.variable
+ * // => 'data'
+ *
+ * options.imports
+ * // => { '_': _, 'jq': $ }
+ */
+ function partialRight(func) {
+ return createBound(func, slice(arguments, 1), null, indicatorObject);
+ }
- if (remaining <= 0) {
- clearTimeout(timeoutId);
+ /**
+ * Creates a function that, when executed, will only call the `func`
+ * function at most once per every `wait` milliseconds. If the throttled
+ * function is invoked more than once during the `wait` timeout, `func` will
+ * also be called on the trailing edge of the timeout. Subsequent calls to the
+ * throttled function will return the result of the last `func` call.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to throttle.
+ * @param {Number} wait The number of milliseconds to throttle executions to.
+ * @returns {Function} Returns the new throttled function.
+ * @example
+ *
+ * var throttled = _.throttle(updatePosition, 100);
+ * jQuery(window).on('scroll', throttled);
+ */
+ function throttle(func, wait) {
+ var args,
+ result,
+ thisArg,
+ timeoutId,
+ lastCalled = 0;
+
+ function trailingCall() {
+ lastCalled = new Date;
timeoutId = null;
- lastCalled = now;
result = func.apply(thisArg, args);
}
- else if (!timeoutId) {
- timeoutId = setTimeout(trailingCall, remaining);
- }
- return result;
- };
- }
+ return function() {
+ var now = new Date,
+ remaining = wait - (now - lastCalled);
+
+ args = arguments;
+ thisArg = this;
+
+ if (remaining <= 0) {
+ clearTimeout(timeoutId);
+ timeoutId = null;
+ lastCalled = now;
+ result = func.apply(thisArg, args);
+ }
+ else if (!timeoutId) {
+ timeoutId = setTimeout(trailingCall, remaining);
+ }
+ return result;
+ };
+ }
- /**
- * Creates a function that passes `value` to the `wrapper` function as its
- * first argument. Additional arguments passed to the function are appended
- * to those passed to the `wrapper` function. The `wrapper` is executed with
- * the `this` binding of the created function.
- *
- * @static
- * @memberOf _
- * @category Functions
- * @param {Mixed} value The value to wrap.
- * @param {Function} wrapper The wrapper function.
- * @returns {Function} Returns the new function.
- * @example
- *
- * var hello = function(name) { return 'hello ' + name; };
- * hello = _.wrap(hello, function(func) {
- * return 'before, ' + func('moe') + ', after';
- * });
- * hello();
- * // => 'before, hello moe, after'
- */
- function wrap(value, wrapper) {
- return function() {
- var args = [value];
- push.apply(args, arguments);
- return wrapper.apply(this, args);
- };
- }
+ /**
+ * Creates a function that passes `value` to the `wrapper` function as its
+ * first argument. Additional arguments passed to the function are appended
+ * to those passed to the `wrapper` function. The `wrapper` is executed with
+ * the `this` binding of the created function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Mixed} value The value to wrap.
+ * @param {Function} wrapper The wrapper function.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var hello = function(name) { return 'hello ' + name; };
+ * hello = _.wrap(hello, function(func) {
+ * return 'before, ' + func('moe') + ', after';
+ * });
+ * hello();
+ * // => 'before, hello moe, after'
+ */
+ function wrap(value, wrapper) {
+ return function() {
+ var args = [value];
+ push.apply(args, arguments);
+ return wrapper.apply(this, args);
+ };
+ }
- /*--------------------------------------------------------------------------*/
+ /*--------------------------------------------------------------------------*/
- /**
- * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their
- * corresponding HTML entities.
- *
- * @static
- * @memberOf _
- * @category Utilities
- * @param {String} string The string to escape.
- * @returns {String} Returns the escaped string.
- * @example
- *
- * _.escape('Moe, Larry & Curly');
- * // => 'Moe, Larry & Curly'
- */
- function escape(string) {
- return string == null ? '' : (string + '').replace(reUnescapedHtml, escapeHtmlChar);
- }
+ /**
+ * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their
+ * corresponding HTML entities.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {String} string The string to escape.
+ * @returns {String} Returns the escaped string.
+ * @example
+ *
+ * _.escape('Moe, Larry & Curly');
+ * // => 'Moe, Larry & Curly'
+ */
+ function escape(string) {
+ return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar);
+ }
- /**
- * This function returns the first argument passed to it.
- *
- * @static
- * @memberOf _
- * @category Utilities
- * @param {Mixed} value Any value.
- * @returns {Mixed} Returns `value`.
- * @example
- *
- * var moe = { 'name': 'moe' };
- * moe === _.identity(moe);
- * // => true
- */
- function identity(value) {
- return value;
- }
+ /**
+ * This function returns the first argument passed to it.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Mixed} value Any value.
+ * @returns {Mixed} Returns `value`.
+ * @example
+ *
+ * var moe = { 'name': 'moe' };
+ * moe === _.identity(moe);
+ * // => true
+ */
+ function identity(value) {
+ return value;
+ }
- /**
- * Adds functions properties of `object` to the `lodash` function and chainable
- * wrapper.
- *
- * @static
- * @memberOf _
- * @category Utilities
- * @param {Object} object The object of function properties to add to `lodash`.
- * @example
- *
- * _.mixin({
- * 'capitalize': function(string) {
- * return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
- * }
- * });
- *
- * _.capitalize('moe');
- * // => 'Moe'
- *
- * _('moe').capitalize();
- * // => 'Moe'
- */
- function mixin(object) {
- forEach(functions(object), function(methodName) {
- var func = lodash[methodName] = object[methodName];
+ /**
+ * Adds functions properties of `object` to the `lodash` function and chainable
+ * wrapper.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Object} object The object of function properties to add to `lodash`.
+ * @example
+ *
+ * _.mixin({
+ * 'capitalize': function(string) {
+ * return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
+ * }
+ * });
+ *
+ * _.capitalize('moe');
+ * // => 'Moe'
+ *
+ * _('moe').capitalize();
+ * // => 'Moe'
+ */
+ function mixin(object) {
+ forEach(functions(object), function(methodName) {
+ var func = lodash[methodName] = object[methodName];
+
+ lodash.prototype[methodName] = function() {
+ var value = this.__wrapped__,
+ args = [value];
+
+ push.apply(args, arguments);
+ var result = func.apply(lodash, args);
+ return (value && typeof value == 'object' && value == result)
+ ? this
+ : new lodashWrapper(result);
+ };
+ });
+ }
- lodash.prototype[methodName] = function() {
- var args = [this.__wrapped__];
- push.apply(args, arguments);
- return new lodash(func.apply(lodash, args));
- };
- });
- }
+ /**
+ * Reverts the '_' variable to its previous value and returns a reference to
+ * the `lodash` function.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @returns {Function} Returns the `lodash` function.
+ * @example
+ *
+ * var lodash = _.noConflict();
+ */
+ function noConflict() {
+ context._ = oldDash;
+ return this;
+ }
- /**
- * Reverts the '_' variable to its previous value and returns a reference to
- * the `lodash` function.
- *
- * @static
- * @memberOf _
- * @category Utilities
- * @returns {Function} Returns the `lodash` function.
- * @example
- *
- * var lodash = _.noConflict();
- */
- function noConflict() {
- window._ = oldDash;
- return this;
- }
+ /**
+ * Converts the given `value` into an integer of the specified `radix`.
+ *
+ * Note: This method avoids differences in native ES3 and ES5 `parseInt`
+ * implementations. See http://es5.github.com/#E.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Mixed} value The value to parse.
+ * @returns {Number} Returns the new integer value.
+ * @example
+ *
+ * _.parseInt('08');
+ * // => 8
+ */
+ var parseInt = nativeParseInt('08') == 8 ? nativeParseInt : function(value, radix) {
+ // Firefox and Opera still follow the ES3 specified implementation of `parseInt`
+ return nativeParseInt(isString(value) ? value.replace(reLeadingZeros, '') : value, radix || 0);
+ };
- /**
- * Produces a random number between `min` and `max` (inclusive). If only one
- * argument is passed, a number between `0` and the given number will be returned.
- *
- * @static
- * @memberOf _
- * @category Utilities
- * @param {Number} [min=0] The minimum possible value.
- * @param {Number} [max=1] The maximum possible value.
- * @returns {Number} Returns a random number.
- * @example
- *
- * _.random(0, 5);
- * // => a number between 0 and 5
- *
- * _.random(5);
- * // => also a number between 0 and 5
- */
- function random(min, max) {
- if (min == null && max == null) {
- max = 1;
+ /**
+ * Produces a random number between `min` and `max` (inclusive). If only one
+ * argument is passed, a number between `0` and the given number will be returned.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Number} [min=0] The minimum possible value.
+ * @param {Number} [max=1] The maximum possible value.
+ * @returns {Number} Returns a random number.
+ * @example
+ *
+ * _.random(0, 5);
+ * // => a number between 0 and 5
+ *
+ * _.random(5);
+ * // => also a number between 0 and 5
+ */
+ function random(min, max) {
+ if (min == null && max == null) {
+ max = 1;
+ }
+ min = +min || 0;
+ if (max == null) {
+ max = min;
+ min = 0;
+ }
+ return min + floor(nativeRandom() * ((+max || 0) - min + 1));
}
- min = +min || 0;
- if (max == null) {
- max = min;
- min = 0;
+
+ /**
+ * 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,
+ * else the property value is returned. If `object` is falsey, then `undefined`
+ * is returned.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Object} object The object to inspect.
+ * @param {String} property The property to get the value of.
+ * @returns {Mixed} Returns the resolved value.
+ * @example
+ *
+ * var object = {
+ * 'cheese': 'crumpets',
+ * 'stuff': function() {
+ * return 'nonsense';
+ * }
+ * };
+ *
+ * _.result(object, 'cheese');
+ * // => 'crumpets'
+ *
+ * _.result(object, 'stuff');
+ * // => 'nonsense'
+ */
+ function result(object, property) {
+ var value = object ? object[property] : undefined;
+ return isFunction(value) ? object[property]() : value;
}
- return min + floor(nativeRandom() * ((+max || 0) - min + 1));
- }
- /**
- * Resolves the value of `property` on `object`. If `property` is a function,
- * it will be invoked and its result returned, else the property value is
- * returned. If `object` is falsey, then `null` is returned.
- *
- * @static
- * @memberOf _
- * @category Utilities
- * @param {Object} object The object to inspect.
- * @param {String} property The property to get the value of.
- * @returns {Mixed} Returns the resolved value.
- * @example
- *
- * var object = {
- * 'cheese': 'crumpets',
- * 'stuff': function() {
- * return 'nonsense';
- * }
- * };
- *
- * _.result(object, 'cheese');
- * // => 'crumpets'
- *
- * _.result(object, 'stuff');
- * // => 'nonsense'
- */
- function result(object, property) {
- var value = object ? object[property] : undefined;
- return isFunction(value) ? object[property]() : value;
- }
+ /**
+ * A micro-templating method that handles arbitrary delimiters, preserves
+ * whitespace, and correctly escapes quotes within interpolated code.
+ *
+ * Note: In the development build, `_.template` utilizes sourceURLs for easier
+ * debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
+ *
+ * Note: Lo-Dash may be used in Chrome extensions by either creating a `lodash csp`
+ * build and using precompiled templates, or loading Lo-Dash in a sandbox.
+ *
+ * For more information on precompiling templates see:
+ * http://lodash.com/#custom-builds
+ *
+ * For more information on Chrome extension sandboxes see:
+ * http://developer.chrome.com/stable/extensions/sandboxingEval.html
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {String} text The template text.
+ * @param {Obect} data The data object used to populate the text.
+ * @param {Object} options The options object.
+ * escape - The "escape" delimiter regexp.
+ * evaluate - The "evaluate" delimiter regexp.
+ * interpolate - The "interpolate" delimiter regexp.
+ * sourceURL - The sourceURL of the template's compiled source.
+ * variable - The data object variable name.
+ * @returns {Function|String} Returns a compiled function when no `data` object
+ * is given, else it returns the interpolated text.
+ * @example
+ *
+ * // using a compiled template
+ * var compiled = _.template('hello <%= name %>');
+ * compiled({ 'name': 'moe' });
+ * // => 'hello moe'
+ *
+ * var list = '<% _.forEach(people, function(name) { %><%= name %><% }); %>';
+ * _.template(list, { 'people': ['moe', 'larry'] });
+ * // => 'moelarry'
+ *
+ * // using the "escape" delimiter to escape HTML in data property values
+ * _.template('<%- value %>', { 'value': '
+
diff --git a/perf/perf.js b/perf/perf.js
index 4ff9cf5bc9..42d72b5495 100644
--- a/perf/perf.js
+++ b/perf/perf.js
@@ -14,7 +14,9 @@
);
var last = result[result.length - 1];
- result = (result.length > min && last != 'test.js') ? last : '../lodash.js';
+ result = (result.length > min && !/perf(?:\.js)?$/.test(last))
+ ? last
+ : '../lodash.js';
try {
result = require('fs').realpathSync(result);
@@ -23,42 +25,52 @@
return result;
}());
- /** Load Benchmark.js */
- var Benchmark =
- window.Benchmark || (
- Benchmark = load('../vendor/benchmark.js/benchmark.js') || window.Benchmark,
- Benchmark.Benchmark || Benchmark
- );
-
/** Load Lo-Dash */
- var lodash =
- window.lodash || (
- lodash = load(filePath) || window._,
- lodash = lodash._ || lodash,
- lodash.noConflict()
- );
+ var lodash = window.lodash || (window.lodash = (
+ lodash = load(filePath) || window._,
+ lodash = lodash._ || lodash,
+ lodash.noConflict()
+ ));
+
+ /** Load Benchmark.js */
+ var Benchmark = window.Benchmark || (window.Benchmark = (
+ Benchmark = load('../vendor/benchmark.js/benchmark.js') || window.Benchmark,
+ Benchmark = Benchmark.Benchmark || Benchmark,
+ Benchmark.runInContext(lodash.extend({}, window, { '_': lodash }))
+ ));
/** Load Underscore */
- var _ =
- window._ || (
- _ = load('../vendor/underscore/underscore.js') || window._,
- _._ || _
- );
+ var _ = window._ || (window._ = (
+ _ = load('../vendor/underscore/underscore.js') || window._,
+ _._ || _
+ ));
/** Used to access the Firebug Lite panel (set by `run`) */
var fbPanel;
+ /** Used to match path separators */
+ var rePathSeparator = /[\/\\]/;
+
+ /** Used to detect primitive types */
+ var rePrimitive = /^(?:boolean|number|string|undefined)$/;
+
+ /** Used to match RegExp special characters */
+ var reSpecialChars = /[.*+?^=!:${}()|[\]\/\\]/g;
+
/** Used to score performance */
var score = { 'a': 0, 'b': 0 };
/** Used to queue benchmark suites */
var suites = [];
+ /** Used to resolve a value's internal [[Class]] */
+ var toString = Object.prototype.toString;
+
/** The `ui` object */
- var ui = window.ui || {
+ var ui = window.ui || ({
'buildPath': basename(filePath, '.js'),
'otherPath': 'underscore'
- };
+ });
/** The Lo-Dash build basename */
var buildName = basename(ui.buildPath, '.js');
@@ -66,15 +78,14 @@
/** The other library basename */
var otherName = basename(ui.otherPath, '.js');
- /** Expose functions to the global object */
- window._ = _;
- window.Benchmark = Benchmark;
- window.lodash = lodash;
+ /** Detect if in a browser environment */
+ var isBrowser = isHostType(window, 'document') && isHostType(window, 'navigator');
- /** Add `console.log()` support for Narwhal and RingoJS */
- if (!window.console && window.print) {
- window.console = { 'log': window.print };
- }
+ /** Detect Java environment */
+ var isJava = !isBrowser && /Java/.test(toString.call(window.java));
+
+ /** Add `console.log()` support for Narwhal, Rhino, and RingoJS */
+ var console = window.console || (window.console = { 'log': window.print });
/*--------------------------------------------------------------------------*/
@@ -88,10 +99,10 @@
* @returns {String} Returns the basename.
*/
function basename(filePath, extension) {
- var result = (filePath || '').split(/[\\/]/).pop();
- return arguments.length < 2
+ var result = (filePath || '').split(rePathSeparator).pop();
+ return (arguments.length < 2)
? result
- : result.replace(RegExp(extension.replace(/[.*+?^=!:${}()|[\]\/\\]/g, '\\$&') + '$'), '');
+ : result.replace(RegExp(extension.replace(reSpecialChars, '\\$&') + '$'), '');
}
/**
@@ -107,6 +118,24 @@
return isFinite(result) ? result : 0;
}
+ /**
+ * Host objects can return type values that are different from their actual
+ * data type. The objects we are concerned with usually return non-primitive
+ * types of "object", "function", or "unknown".
+ *
+ * @private
+ * @param {Mixed} object The owner of the property.
+ * @param {String} property The property to check.
+ * @returns {Boolean} Returns `true` if the property value is a non-primitive, else `false`.
+ */
+ function isHostType(object, property) {
+ if (object == null) {
+ return false;
+ }
+ var type = typeof object[property];
+ return !rePrimitive.test(type) && (type != 'object' || !!object[property]);
+ }
+
/**
* Logs text to the console.
*
@@ -132,7 +161,7 @@
fbPanel.getElementById('fbPanel1');
log('\nSit back and relax, this may take a while.');
- suites[0].run();
+ suites[0].run({ 'async': !isJava });
}
/*--------------------------------------------------------------------------*/
@@ -175,7 +204,7 @@
if (suites.length) {
// run next suite
- suites[0].run();
+ suites[0].run({ 'async': !isJava });
}
else {
var fastestTotalHz = Math.max(score.a, score.b),
@@ -199,8 +228,7 @@
lodash.extend(Benchmark.options, {
'async': true,
'setup': '\
- var window = Function("return this || global")(),\
- _ = window._,\
+ var _ = window._,\
lodash = window.lodash,\
belt = this.name == "Lo-Dash" ? lodash : _;\
\
@@ -222,18 +250,18 @@
}\
\
if (typeof bind != "undefined") {\
- var contextObject = { "name": "moe" },\
+ var thisArg = { "name": "moe" },\
ctor = function() {};\
\
var func = function(greeting, punctuation) {\
return greeting + ", " + this.name + (punctuation || ".");\
};\
\
- var lodashBoundNormal = lodash.bind(func, contextObject),\
- lodashBoundPartial = lodash.bind(func, contextObject, "hi");\
+ var _boundNormal = _.bind(func, thisArg),\
+ _boundPartial = _.bind(func, thisArg, "hi");\
\
- var _boundNormal = _.bind(func, contextObject),\
- _boundPartial = _.bind(func, contextObject, "hi");\
+ var lodashBoundNormal = lodash.bind(func, thisArg),\
+ lodashBoundPartial = lodash.bind(func, thisArg, "hi");\
}\
\
if (typeof bindAll != "undefined") {\
@@ -248,6 +276,10 @@
}, {});\
}\
}\
+ if (typeof chaining != "undefined") {\
+ var _chaining = _.chain ? _(numbers).chain() : _(numbers),\
+ lodashChaining = lodash(numbers);\
+ }\
if (typeof compact != "undefined") {\
var uncompacted = numbers.slice();\
uncompacted[2] = false;\
@@ -445,16 +477,68 @@
\
var settingsObject = { "variable": "data" };\
\
- var lodashTpl = lodash.template(tpl),\
- lodashTplVerbose = lodash.template(tplVerbose, null, settingsObject);\
- \
var _tpl = _.template(tpl),\
_tplVerbose = _.template(tplVerbose, null, settingsObject);\
+ \
+ var lodashTpl = lodash.template(tpl),\
+ lodashTplVerbose = lodash.template(tplVerbose, null, settingsObject);\
+ }\
+ if (typeof where != "undefined") {\
+ var _findWhere = _.findWhere || _.find,\
+ lodashFindWhere = lodash.findWhere || lodash.find,\
+ whereObject = { "num": 9 };\
}'
});
/*--------------------------------------------------------------------------*/
+ suites.push(
+ Benchmark.Suite('`_(...)` with a number')
+ .add(buildName, '\
+ lodash(2)'
+ )
+ .add(otherName, '\
+ _(2)'
+ )
+ );
+
+ suites.push(
+ Benchmark.Suite('`_(...)` with an array')
+ .add(buildName, '\
+ lodash(numbers)'
+ )
+ .add(otherName, '\
+ _(numbers)'
+ )
+ );
+
+ suites.push(
+ Benchmark.Suite('`_(...)` with an object')
+ .add(buildName, '\
+ lodash(object)'
+ )
+ .add(otherName, '\
+ _(object)'
+ )
+ );
+
+ // avoid Underscore induced `OutOfMemoryError` in Rhino, Narwhal, and Ringo
+ if (!isJava) {
+ suites.push(
+ Benchmark.Suite('`_(...).tap(...)`')
+ .add(buildName, {
+ 'fn': 'lodashChaining.tap(lodash.identity)',
+ 'teardown': 'function chaining(){}'
+ })
+ .add(otherName, {
+ 'fn': '_chaining.tap(_.identity)',
+ 'teardown': 'function chaining(){}'
+ })
+ );
+ }
+
+ /*--------------------------------------------------------------------------*/
+
suites.push(
Benchmark.Suite('`_.bind` (uses native `Function#bind` if available and inferred fast)')
.add(buildName, {
@@ -650,13 +734,13 @@
);
suites.push(
- Benchmark.Suite('`_.difference` iterating 30 elements')
+ Benchmark.Suite('`_.difference` iterating 100 elements')
.add(buildName, {
- 'fn': 'lodash.difference(thirtyValues, thirtyValues2)',
+ 'fn': 'lodash.difference(hundredValues, hundredValues2)',
'teardown': 'function multiArrays(){}'
})
.add(otherName, {
- 'fn': '_.difference(thirtyValues, thirtyValues2)',
+ 'fn': '_.difference(hundredValues, hundredValues2)',
'teardown': 'function multiArrays(){}'
})
);
@@ -839,15 +923,20 @@
)
);
- suites.push(
- Benchmark.Suite('`_.find` with `properties`')
- .add(buildName, '\
- lodash.find(objects, { "num": 9 });'
- )
- .add(otherName, '\
- _.findWhere(objects, { "num": 9 });'
- )
- );
+ // avoid Underscore induced `OutOfMemoryError` in Rhino, Narwhal, and Ringo
+ if (!isJava) {
+ suites.push(
+ Benchmark.Suite('`_.find` with `properties`')
+ .add(buildName, {
+ 'fn': 'lodashFindWhere(objects, whereObject)',
+ 'teardown': 'function where(){}'
+ })
+ .add(otherName, {
+ 'fn': '_findWhere(objects, whereObject)',
+ 'teardown': 'function where(){}'
+ })
+ );
+ }
/*--------------------------------------------------------------------------*/
@@ -1228,7 +1317,7 @@
'teardown': 'function omit(){}'
})
.add(otherName, {
- 'fn': 'result = _.omit(wordToNumber, words)',
+ 'fn': '_.omit(wordToNumber, words)',
'teardown': 'function omit(){}'
})
);
@@ -1291,13 +1380,13 @@
Benchmark.Suite('`_.reduce` iterating an object')
.add(buildName, '\
lodash.reduce(object, function(result, value, key) {\
- result.push([key, value]);\
+ result.push(key, value);\
return result;\
}, []);'
)
.add(otherName, '\
_.reduce(object, function(result, value, key) {\
- result.push([key, value]);\
+ result.push(key, value);\
return result;\
}, []);'
)
@@ -1325,13 +1414,13 @@
Benchmark.Suite('`_.reduceRight` iterating an object')
.add(buildName, '\
lodash.reduceRight(object, function(result, value, key) {\
- result.push([key, value]);\
+ result.push(key, value);\
return result;\
}, []);'
)
.add(otherName, '\
_.reduceRight(object, function(result, value, key) {\
- result.push([key, value]);\
+ result.push(key, value);\
return result;\
}, []);'
)
@@ -1677,12 +1766,14 @@
suites.push(
Benchmark.Suite('`_.where`')
- .add(buildName, '\
- lodash.where(objects, { "num": 9 });'
- )
- .add(otherName, '\
- _.where(objects, { "num": 9 });'
- )
+ .add(buildName, {
+ 'fn': 'lodash.where(objects, whereObject);',
+ 'teardown': 'function where(){}'
+ })
+ .add(otherName, {
+ 'fn': '_.where(objects, whereObject);',
+ 'teardown': 'function where(){}'
+ })
);
/*--------------------------------------------------------------------------*/
@@ -1714,7 +1805,6 @@
if (Benchmark.platform + '') {
log(Benchmark.platform);
}
-
// in the browser, expose `run` to be called later
if (window.document && !window.phantom) {
window.run = run;
diff --git a/perf/run-perf.sh b/perf/run-perf.sh
index 8593cd6bd7..b1406315b4 100755
--- a/perf/run-perf.sh
+++ b/perf/run-perf.sh
@@ -3,7 +3,7 @@ cd "$(dirname "$0")"
echo "Running performance suite in node..."
node perf.js ../dist/lodash.js && node perf.js ../dist/lodash.min.js
-for cmd in rhino narwhal ringo phantomjs; do
+for cmd in rhino "rhino -require" narwhal ringo phantomjs; do
echo ""
echo "Running performance suite in $cmd..."
$cmd perf.js ../dist/lodash.compat.js && $cmd perf.js ../dist/lodash.compat.min.js
diff --git a/test/index.html b/test/index.html
index 182971f218..d813aa57f9 100644
--- a/test/index.html
+++ b/test/index.html
@@ -4,12 +4,18 @@
Lo-Dash Test Suite
+
-
-
+
+
+
```
@@ -68,25 +71,14 @@ load('benchmark.js');
In an AMD loader like [RequireJS](http://requirejs.org/):
```js
-require({
- 'paths': {
- 'benchmark': 'path/to/benchmark'
- }
-},
-['benchmark'], function(Benchmark) {
- console.log(Benchmark.version);
-});
-
-// or with platform.js
-// https://github.com/bestiejs/platform.js
require({
'paths': {
'benchmark': 'path/to/benchmark',
+ 'lodash': 'path/to/lodash',
'platform': 'path/to/platform'
}
},
-['benchmark', 'platform'], function(Benchmark, platform) {
- Benchmark.platform = platform;
+['benchmark'], function(Benchmark) {
console.log(Benchmark.platform.name);
});
```
@@ -108,7 +100,7 @@ suite.add('RegExp#test', function() {
console.log(String(event.target));
})
.on('complete', function() {
- console.log('Fastest is ' + this.filter('fastest').pluck('name'));
+ console.log('Fastest is ' + _.pluck(this.filter('fastest'), 'name'));
})
// run async
.run({ 'async': true });
@@ -121,7 +113,7 @@ suite.add('RegExp#test', function() {
## BestieJS
-Benchmark.js is part of the BestieJS *"Best in Class"* module collection. This means we promote solid browser/environment support, ES5 precedents, unit testing, and plenty of documentation.
+Benchmark.js is part of the BestieJS *"Best in Class"* module collection. This means we promote solid browser/environment support, ES5+ precedents, unit testing, and plenty of documentation.
## Authors
diff --git a/vendor/benchmark.js/benchmark.js b/vendor/benchmark.js/benchmark.js
index 6adf726ce4..eb274b420e 100644
--- a/vendor/benchmark.js/benchmark.js
+++ b/vendor/benchmark.js/benchmark.js
@@ -8,70 +8,37 @@
;(function(window, undefined) {
'use strict';
- /** Used to assign each benchmark an incrimented id */
- var counter = 0;
-
- /** Detect DOM document object */
- var doc = isHostType(window, 'document') && document;
-
/** Detect free variable `define` */
var freeDefine = typeof define == 'function' &&
typeof define.amd == 'object' && define.amd && define;
/** Detect free variable `exports` */
- var freeExports = typeof exports == 'object' && exports &&
- (typeof global == 'object' && global && global == global.global && (window = global), exports);
+ var freeExports = typeof exports == 'object' && exports;
+
+ /** Detect free variable `module` */
+ var freeModule = typeof module == 'object' && module && module.exports == freeExports && module;
/** Detect free variable `require` */
var freeRequire = typeof require == 'function' && require;
- /** Used to store the `Object` built-in in case it's overwritten later */
- var Object = window.Object;
-
- /** Used to crawl all properties regardless of enumerability */
- var getAllKeys = Object.getOwnPropertyNames;
-
- /** Used to get property descriptors */
- var getDescriptor = Object.getOwnPropertyDescriptor;
-
- /** Used in case an object doesn't have its own method */
- var hasOwnProperty = {}.hasOwnProperty;
-
- /** Used to check if an object is extensible */
- var isExtensible = Object.isExtensible || function() { return true; };
-
- /** Used to access Wade Simmons' Node microtime module */
- var microtimeObject = req('microtime');
-
- /** Used to access the browser's high resolution timer */
- var perfObject = isHostType(window, 'performance') && performance;
-
- /** Used to call the browser's high resolution timer */
- var perfName = perfObject && (
- perfObject.now && 'now' ||
- perfObject.webkitNow && 'webkitNow'
- );
-
- /** Used to access Node's high resolution timer */
- var processObject = isHostType(window, 'process') && process;
-
- /** Used to check if an own property is enumerable */
- var propertyIsEnumerable = {}.propertyIsEnumerable;
-
- /** Used to set property descriptors */
- var setDescriptor = Object.defineProperty;
-
- /** Used to resolve a value's internal [[Class]] */
- var toString = {}.toString;
+ /** Detect free variable `global` and use it as `window` */
+ var freeGlobal = typeof global == 'object' && global;
+ if (freeGlobal.global === freeGlobal) {
+ window = freeGlobal;
+ }
- /** Used to prevent a `removeChild` memory leak in IE < 9 */
- var trash = doc && doc.createElement('div');
+ /** Used to assign each benchmark an incrimented id */
+ var counter = 0;
- /** Used to integrity check compiled tests */
- var uid = 'uid' + (+new Date);
+ /** Used to detect primitive types */
+ var rePrimitive = /^(?:boolean|number|string|undefined)$/;
- /** Used to avoid infinite recursion when methods call each other */
- var calledBy = {};
+ /** Used to assign default `context` object properties */
+ var contextProps = [
+ 'Array', 'Date', 'Function', 'Math', 'Object', 'RegExp', 'String', '_',
+ 'clearTimeout', 'chrome', 'chromium', 'document', 'java', 'navigator',
+ 'performance', 'platform', 'process', 'runtime', 'setTimeout'
+ ];
/** Used to avoid hz of Infinity */
var divisors = {
@@ -128,3833 +95,2769 @@
'30': [13, 23, 33, 43, 54, 65, 76, 87, 98, 109, 120, 131, 143, 154, 166, 177, 189, 200, 212, 223, 235, 247, 258, 270, 282, 293, 305, 317]
};
+ /*--------------------------------------------------------------------------*/
+
/**
- * An object used to flag environments/features.
+ * Create a new `Benchmark` function using the given `context` object.
*
* @static
* @memberOf Benchmark
- * @type Object
- */
- var support = {};
+ * @param {Object} [context=window] The context object.
+ * @returns {Function} Returns the `Benchmark` function.
+ */
+ function runInContext(context) {
+ // exit early if unable to acquire lodash
+ var _ = context && context._ || req('lodash') || window._;
+ if (!_) {
+ Benchmark.runInContext = runInContext;
+ return Benchmark;
+ }
+ // Avoid issues with some ES3 environments that attempt to use values, named
+ // after built-in constructors like `Object`, for the creation of literals.
+ // ES5 clears this up by stating that literals must use built-in constructors.
+ // See http://es5.github.com/#x11.1.5.
+ context = context ? _.defaults(window.Object(), context, _.pick(window, contextProps)) : window;
+
+ /** Native constructor references */
+ var Array = context.Array,
+ Date = context.Date,
+ Function = context.Function,
+ Math = context.Math,
+ Object = context.Object,
+ RegExp = context.RegExp,
+ String = context.String;
+
+ /** Used for `Array` and `Object` method references */
+ var arrayRef = Array(),
+ objectRef = Object();
+
+ /** Native method shortcuts */
+ var abs = Math.abs,
+ clearTimeout = context.clearTimeout,
+ floor = Math.floor,
+ log = Math.log,
+ max = Math.max,
+ min = Math.min,
+ pow = Math.pow,
+ setTimeout = context.setTimeout,
+ shift = arrayRef.shift,
+ slice = arrayRef.slice,
+ sqrt = Math.sqrt,
+ toString = objectRef.toString;
+
+ /** Detect DOM document object */
+ var doc = isHostType(context, 'document') && context.document;
+
+ /** Used to access Wade Simmons' Node microtime module */
+ var microtimeObject = req('microtime');
+
+ /** Used to access the browser's high resolution timer */
+ var perfObject = isHostType(context, 'performance') && context.performance;
+
+ /** Used to call the browser's high resolution timer */
+ var perfName = perfObject && (
+ perfObject.now && 'now' ||
+ perfObject.webkitNow && 'webkitNow'
+ );
+
+ /** Used to access Node's high resolution timer */
+ var processObject = isHostType(context, 'process') && context.process;
+
+ /** Used to prevent a `removeChild` memory leak in IE < 9 */
+ var trash = doc && doc.createElement('div');
+
+ /** Used to integrity check compiled tests */
+ var uid = 'uid' + (+new Date);
+
+ /** Used to avoid infinite recursion when methods call each other */
+ var calledBy = {};
+
+ /**
+ * An object used to flag environments/features.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @type Object
+ */
+ var support = {};
+
+ (function() {
+
+ /**
+ * Detect Adobe AIR.
+ *
+ * @memberOf Benchmark.support
+ * @type Boolean
+ */
+ support.air = isClassOf(context.runtime, 'ScriptBridgingProxyObject');
+
+ /**
+ * Detect if in a browser environment.
+ *
+ * @memberOf Benchmark.support
+ * @type Boolean
+ */
+ support.browser = doc && isHostType(context, 'navigator') && !isHostType(context, 'phantom');
+
+ /**
+ * Detect if Java is enabled/exposed.
+ *
+ * @memberOf Benchmark.support
+ * @type Boolean
+ */
+ support.java = isClassOf(context.java, 'JavaPackage');
+
+ /**
+ * Detect if the Timers API exists.
+ *
+ * @memberOf Benchmark.support
+ * @type Boolean
+ */
+ support.timeout = isHostType(context, 'setTimeout') && isHostType(context, 'clearTimeout');
- (function() {
+ /**
+ * Detect if functions support decompilation.
+ *
+ * @name decompilation
+ * @memberOf Benchmark.support
+ * @type Boolean
+ */
+ try {
+ // Safari 2.x removes commas in object literals
+ // from Function#toString results
+ // http://webk.it/11609
+ // Firefox 3.6 and Opera 9.25 strip grouping
+ // parentheses from Function#toString results
+ // http://bugzil.la/559438
+ support.decompilation = Function(
+ 'return (' + (function(x) { return { 'x': '' + (1 + x) + '', 'y': 0 }; }) + ')'
+ )()(0).x === '1';
+ } catch(e) {
+ support.decompilation = false;
+ }
+ }());
/**
- * Detect Adobe AIR.
+ * Timer object used by `clock()` and `Deferred#resolve`.
*
- * @memberOf Benchmark.support
- * @type Boolean
+ * @private
+ * @type Object
*/
- support.air = isClassOf(window.runtime, 'ScriptBridgingProxyObject');
+ var timer = {
+
+ /**
+ * The timer namespace object or constructor.
+ *
+ * @private
+ * @memberOf timer
+ * @type Function|Object
+ */
+ 'ns': Date,
+
+ /**
+ * Starts the deferred timer.
+ *
+ * @private
+ * @memberOf timer
+ * @param {Object} deferred The deferred instance.
+ */
+ 'start': null, // lazy defined in `clock()`
+
+ /**
+ * Stops the deferred timer.
+ *
+ * @private
+ * @memberOf timer
+ * @param {Object} deferred The deferred instance.
+ */
+ 'stop': null // lazy defined in `clock()`
+ };
+
+ /*------------------------------------------------------------------------*/
/**
- * Detect if `arguments` objects have the correct internal [[Class]] value.
+ * The Benchmark constructor.
+ *
+ * @constructor
+ * @param {String} name A name to identify the benchmark.
+ * @param {Function|String} fn The test to benchmark.
+ * @param {Object} [options={}] Options object.
+ * @example
*
- * @memberOf Benchmark.support
- * @type Boolean
+ * // basic usage (the `new` operator is optional)
+ * var bench = new Benchmark(fn);
+ *
+ * // or using a name first
+ * var bench = new Benchmark('foo', fn);
+ *
+ * // or with options
+ * var bench = new Benchmark('foo', fn, {
+ *
+ * // displayed by Benchmark#toString if `name` is not available
+ * 'id': 'xyz',
+ *
+ * // called when the benchmark starts running
+ * 'onStart': onStart,
+ *
+ * // called after each run cycle
+ * 'onCycle': onCycle,
+ *
+ * // called when aborted
+ * 'onAbort': onAbort,
+ *
+ * // called when a test errors
+ * 'onError': onError,
+ *
+ * // called when reset
+ * 'onReset': onReset,
+ *
+ * // called when the benchmark completes running
+ * 'onComplete': onComplete,
+ *
+ * // compiled/called before the test loop
+ * 'setup': setup,
+ *
+ * // compiled/called after the test loop
+ * 'teardown': teardown
+ * });
+ *
+ * // or name and options
+ * var bench = new Benchmark('foo', {
+ *
+ * // a flag to indicate the benchmark is deferred
+ * 'defer': true,
+ *
+ * // benchmark test function
+ * 'fn': function(deferred) {
+ * // call resolve() when the deferred test is finished
+ * deferred.resolve();
+ * }
+ * });
+ *
+ * // or options only
+ * var bench = new Benchmark({
+ *
+ * // benchmark name
+ * 'name': 'foo',
+ *
+ * // benchmark test as a string
+ * 'fn': '[1,2,3,4].sort()'
+ * });
+ *
+ * // a test's `this` binding is set to the benchmark instance
+ * var bench = new Benchmark('foo', function() {
+ * 'My name is '.concat(this.name); // My name is foo
+ * });
*/
- support.argumentsClass = isClassOf(arguments, 'Arguments');
+ function Benchmark(name, fn, options) {
+ var bench = this;
+
+ // allow instance creation without the `new` operator
+ if (bench == null || bench.constructor != Benchmark) {
+ return new Benchmark(name, fn, options);
+ }
+ // juggle arguments
+ if (_.isPlainObject(name)) {
+ // 1 argument (options)
+ options = name;
+ }
+ else if (_.isFunction(name)) {
+ // 2 arguments (fn, options)
+ options = fn;
+ fn = name;
+ }
+ else if (_.isPlainObject(fn)) {
+ // 2 arguments (name, options)
+ options = fn;
+ fn = null;
+ bench.name = name;
+ }
+ else {
+ // 3 arguments (name, fn [, options])
+ bench.name = name;
+ }
+ setOptions(bench, options);
+
+ bench.id || (bench.id = ++counter);
+ bench.fn == null && (bench.fn = fn);
+
+ bench.stats = cloneDeep(bench.stats);
+ bench.times = cloneDeep(bench.times);
+ }
/**
- * Detect if in a browser environment.
+ * The Deferred constructor.
*
- * @memberOf Benchmark.support
- * @type Boolean
+ * @constructor
+ * @memberOf Benchmark
+ * @param {Object} clone The cloned benchmark instance.
*/
- support.browser = doc && isHostType(window, 'navigator');
+ function Deferred(clone) {
+ var deferred = this;
+ if (deferred == null || deferred.constructor != Deferred) {
+ return new Deferred(clone);
+ }
+ deferred.benchmark = clone;
+ clock(deferred);
+ }
/**
- * Detect if strings support accessing characters by index.
+ * The Event constructor.
*
- * @memberOf Benchmark.support
- * @type Boolean
+ * @constructor
+ * @memberOf Benchmark
+ * @param {String|Object} type The event type.
*/
- support.charByIndex =
- // IE 8 supports indexes on string literals but not string objects
- ('x'[0] + Object('x')[0]) == 'xx';
+ function Event(type) {
+ var event = this;
+ return (event == null || event.constructor != Event)
+ ? new Event(type)
+ : (type instanceof Event
+ ? type
+ : _.extend(event, { 'timeStamp': +new Date }, typeof type == 'string' ? { 'type': type } : type)
+ );
+ }
/**
- * Detect if strings have indexes as own properties.
+ * The Suite constructor.
+ *
+ * @constructor
+ * @memberOf Benchmark
+ * @param {String} name A name to identify the suite.
+ * @param {Object} [options={}] Options object.
+ * @example
+ *
+ * // basic usage (the `new` operator is optional)
+ * var suite = new Benchmark.Suite;
+ *
+ * // or using a name first
+ * var suite = new Benchmark.Suite('foo');
+ *
+ * // or with options
+ * var suite = new Benchmark.Suite('foo', {
+ *
+ * // called when the suite starts running
+ * 'onStart': onStart,
+ *
+ * // called between running benchmarks
+ * 'onCycle': onCycle,
+ *
+ * // called when aborted
+ * 'onAbort': onAbort,
+ *
+ * // called when a test errors
+ * 'onError': onError,
*
- * @memberOf Benchmark.support
- * @type Boolean
+ * // called when reset
+ * 'onReset': onReset,
+ *
+ * // called when the suite completes running
+ * 'onComplete': onComplete
+ * });
*/
- support.charByOwnIndex =
- // Narwhal, Rhino, RingoJS, IE 8, and Opera < 10.52 support indexes on
- // strings but don't detect them as own properties
- support.charByIndex && hasKey('x', '0');
+ function Suite(name, options) {
+ var suite = this;
+
+ // allow instance creation without the `new` operator
+ if (suite == null || suite.constructor != Suite) {
+ return new Suite(name, options);
+ }
+ // juggle arguments
+ if (isClassOf(name, 'Object')) {
+ // 1 argument (options)
+ options = name;
+ } else {
+ // 2 arguments (name [, options])
+ suite.name = name;
+ }
+ setOptions(suite, options);
+ }
+
+ /*------------------------------------------------------------------------*/
/**
- * Detect if Java is enabled/exposed.
+ * A deep clone utility.
*
- * @memberOf Benchmark.support
- * @type Boolean
- */
- support.java = isClassOf(window.java, 'JavaPackage');
+ * @private
+ * @param {Mixed} value The value to clone.
+ * @returns {Mixed} The cloned value.
+ */
+ var cloneDeep = _.partialRight(_.cloneDeep, function(value) {
+ // do not clone non-Object objects
+ return (typeof value == 'object' && !_.isArray(value) && !_.isPlainObject(value))
+ ? value
+ : undefined;
+ });
/**
- * Detect if the Timers API exists.
+ * Creates a function from the given arguments string and body.
*
- * @memberOf Benchmark.support
- * @type Boolean
- */
- support.timeout = isHostType(window, 'setTimeout') && isHostType(window, 'clearTimeout');
+ * @private
+ * @param {String} args The comma separated function arguments.
+ * @param {String} body The function body.
+ * @returns {Function} The new function.
+ */
+ function createFunction() {
+ // lazy define
+ createFunction = function(args, body) {
+ var result,
+ anchor = freeDefine ? freeDefine.amd : Benchmark,
+ prop = uid + 'createFunction';
+
+ runScript((freeDefine ? 'define.amd.' : 'Benchmark.') + prop + '=function(' + args + '){' + body + '}');
+ result = anchor[prop];
+ delete anchor[prop];
+ return result;
+ };
+ // fix JaegerMonkey bug
+ // http://bugzil.la/639720
+ createFunction = support.browser && (createFunction('', 'return"' + uid + '"') || noop)() == uid ? createFunction : Function;
+ return createFunction.apply(null, arguments);
+ }
/**
- * Detect if functions support decompilation.
+ * Delay the execution of a function based on the benchmark's `delay` property.
*
- * @name decompilation
- * @memberOf Benchmark.support
- * @type Boolean
+ * @private
+ * @param {Object} bench The benchmark instance.
+ * @param {Object} fn The function to execute.
*/
- try {
- // Safari 2.x removes commas in object literals
- // from Function#toString results
- // http://webk.it/11609
- // Firefox 3.6 and Opera 9.25 strip grouping
- // parentheses from Function#toString results
- // http://bugzil.la/559438
- support.decompilation = Function(
- 'return (' + (function(x) { return { 'x': '' + (1 + x) + '', 'y': 0 }; }) + ')'
- )()(0).x === '1';
- } catch(e) {
- support.decompilation = false;
+ function delay(bench, fn) {
+ bench._timerId = _.delay(fn, bench.delay * 1e3);
}
/**
- * Detect ES5+ property descriptor API.
+ * Destroys the given element.
*
- * @name descriptors
- * @memberOf Benchmark.support
- * @type Boolean
+ * @private
+ * @param {Element} element The element to destroy.
*/
- try {
- var o = {};
- support.descriptors = (setDescriptor(o, o, o), 'value' in getDescriptor(o, o));
- } catch(e) {
- support.descriptors = false;
+ function destroyElement(element) {
+ trash.appendChild(element);
+ trash.innerHTML = '';
}
/**
- * Detect ES5+ Object.getOwnPropertyNames().
+ * Gets the name of the first argument from a function's source.
*
- * @name getAllKeys
- * @memberOf Benchmark.support
- * @type Boolean
+ * @private
+ * @param {Function} fn The function.
+ * @returns {String} The argument name.
*/
- try {
- support.getAllKeys = /\bvalueOf\b/.test(getAllKeys(Object.prototype));
- } catch(e) {
- support.getAllKeys = false;
+ function getFirstArgument(fn) {
+ return (!_.has(fn, 'toString') &&
+ (/^[\s(]*function[^(]*\(([^\s,)]+)/.exec(fn) || 0)[1]) || '';
}
/**
- * Detect if own properties are iterated before inherited properties (all but IE < 9).
+ * Computes the geometric mean (log-average) of a sample.
+ * See http://en.wikipedia.org/wiki/Geometric_mean#Relationship_with_arithmetic_mean_of_logarithms.
*
- * @name iteratesOwnLast
- * @memberOf Benchmark.support
- * @type Boolean
+ * @private
+ * @param {Array} sample The sample.
+ * @returns {Number} The geometric mean.
*/
- support.iteratesOwnFirst = (function() {
- var props = [];
- function ctor() { this.x = 1; }
- ctor.prototype = { 'y': 1 };
- for (var prop in new ctor) { props.push(prop); }
- return props[0] == 'x';
- }());
+ function getGeometricMean(sample) {
+ return pow(Math.E, _.reduce(sample, function(sum, x) {
+ return sum + log(x);
+ }) / sample.length) || 0;
+ }
/**
- * Detect if a node's [[Class]] is resolvable (all but IE < 9)
- * and that the JS engine errors when attempting to coerce an object to a
- * string without a `toString` property value of `typeof` "function".
+ * Computes the arithmetic mean of a sample.
*
- * @name nodeClass
- * @memberOf Benchmark.support
- * @type Boolean
+ * @private
+ * @param {Array} sample The sample.
+ * @returns {Number} The mean.
*/
- try {
- support.nodeClass = ({ 'toString': 0 } + '', toString.call(doc || 0) != '[object Object]');
- } catch(e) {
- support.nodeClass = true;
+ function getMean(sample) {
+ return (_.reduce(sample, function(sum, x) {
+ return sum + x;
+ }) / sample.length) || 0;
}
- }());
-
- /**
- * Timer object used by `clock()` and `Deferred#resolve`.
- *
- * @private
- * @type Object
- */
- var timer = {
-
- /**
- * The timer namespace object or constructor.
- *
- * @private
- * @memberOf timer
- * @type Function|Object
- */
- 'ns': Date,
-
- /**
- * Starts the deferred timer.
- *
- * @private
- * @memberOf timer
- * @param {Object} deferred The deferred instance.
- */
- 'start': null, // lazy defined in `clock()`
-
- /**
- * Stops the deferred timer.
- *
- * @private
- * @memberOf timer
- * @param {Object} deferred The deferred instance.
- */
- 'stop': null // lazy defined in `clock()`
- };
-
- /** Shortcut for inverse results */
- var noArgumentsClass = !support.argumentsClass,
- noCharByIndex = !support.charByIndex,
- noCharByOwnIndex = !support.charByOwnIndex;
-
- /** Math shortcuts */
- var abs = Math.abs,
- floor = Math.floor,
- log = Math.log,
- max = Math.max,
- min = Math.min,
- pow = Math.pow,
- sqrt = Math.sqrt;
- /*--------------------------------------------------------------------------*/
-
- /**
- * The Benchmark constructor.
- *
- * @constructor
- * @param {String} name A name to identify the benchmark.
- * @param {Function|String} fn The test to benchmark.
- * @param {Object} [options={}] Options object.
- * @example
- *
- * // basic usage (the `new` operator is optional)
- * var bench = new Benchmark(fn);
- *
- * // or using a name first
- * var bench = new Benchmark('foo', fn);
- *
- * // or with options
- * var bench = new Benchmark('foo', fn, {
- *
- * // displayed by Benchmark#toString if `name` is not available
- * 'id': 'xyz',
- *
- * // called when the benchmark starts running
- * 'onStart': onStart,
- *
- * // called after each run cycle
- * 'onCycle': onCycle,
- *
- * // called when aborted
- * 'onAbort': onAbort,
- *
- * // called when a test errors
- * 'onError': onError,
- *
- * // called when reset
- * 'onReset': onReset,
- *
- * // called when the benchmark completes running
- * 'onComplete': onComplete,
- *
- * // compiled/called before the test loop
- * 'setup': setup,
- *
- * // compiled/called after the test loop
- * 'teardown': teardown
- * });
- *
- * // or name and options
- * var bench = new Benchmark('foo', {
- *
- * // a flag to indicate the benchmark is deferred
- * 'defer': true,
- *
- * // benchmark test function
- * 'fn': function(deferred) {
- * // call resolve() when the deferred test is finished
- * deferred.resolve();
- * }
- * });
- *
- * // or options only
- * var bench = new Benchmark({
- *
- * // benchmark name
- * 'name': 'foo',
- *
- * // benchmark test as a string
- * 'fn': '[1,2,3,4].sort()'
- * });
- *
- * // a test's `this` binding is set to the benchmark instance
- * var bench = new Benchmark('foo', function() {
- * 'My name is '.concat(this.name); // My name is foo
- * });
- */
- function Benchmark(name, fn, options) {
- var me = this;
+ /**
+ * Gets the source code of a function.
+ *
+ * @private
+ * @param {Function} fn The function.
+ * @param {String} altSource A string used when a function's source code is unretrievable.
+ * @returns {String} The function's source code.
+ */
+ function getSource(fn, altSource) {
+ var result = altSource;
+ if (isStringable(fn)) {
+ result = String(fn);
+ } else if (support.decompilation) {
+ // escape the `{` for Firefox 1
+ result = (/^[^{]+\{([\s\S]*)\}\s*$/.exec(fn) || 0)[1];
+ }
+ // trim string
+ result = (result || '').replace(/^\s+|\s+$/g, '');
- // allow instance creation without the `new` operator
- if (me == null || me.constructor != Benchmark) {
- return new Benchmark(name, fn, options);
- }
- // juggle arguments
- if (isClassOf(name, 'Object')) {
- // 1 argument (options)
- options = name;
- }
- else if (isClassOf(name, 'Function')) {
- // 2 arguments (fn, options)
- options = fn;
- fn = name;
- }
- else if (isClassOf(fn, 'Object')) {
- // 2 arguments (name, options)
- options = fn;
- fn = null;
- me.name = name;
- }
- else {
- // 3 arguments (name, fn [, options])
- me.name = name;
+ // detect strings containing only the "use strict" directive
+ return /^(?:\/\*+[\w|\W]*?\*\/|\/\/.*?[\n\r\u2028\u2029]|\s)*(["'])use strict\1;?$/.test(result)
+ ? ''
+ : result;
}
- setOptions(me, options);
- me.id || (me.id = ++counter);
- me.fn == null && (me.fn = fn);
- me.stats = deepClone(me.stats);
- me.times = deepClone(me.times);
- }
- /**
- * The Deferred constructor.
- *
- * @constructor
- * @memberOf Benchmark
- * @param {Object} clone The cloned benchmark instance.
- */
- function Deferred(clone) {
- var me = this;
- if (me == null || me.constructor != Deferred) {
- return new Deferred(clone);
+ /**
+ * Checks if an object is of the specified class.
+ *
+ * @private
+ * @param {Mixed} value The value to check.
+ * @param {String} name The name of the class.
+ * @returns {Boolean} Returns `true` if the value is of the specified class, else `false`.
+ */
+ function isClassOf(value, name) {
+ return value != null && toString.call(value) == '[object ' + name + ']';
}
- me.benchmark = clone;
- clock(me);
- }
-
- /**
- * The Event constructor.
- *
- * @constructor
- * @memberOf Benchmark
- * @param {String|Object} type The event type.
- */
- function Event(type) {
- var me = this;
- return (me == null || me.constructor != Event)
- ? new Event(type)
- : (type instanceof Event)
- ? type
- : extend(me, { 'timeStamp': +new Date }, typeof type == 'string' ? { 'type': type } : type);
- }
- /**
- * The Suite constructor.
- *
- * @constructor
- * @memberOf Benchmark
- * @param {String} name A name to identify the suite.
- * @param {Object} [options={}] Options object.
- * @example
- *
- * // basic usage (the `new` operator is optional)
- * var suite = new Benchmark.Suite;
- *
- * // or using a name first
- * var suite = new Benchmark.Suite('foo');
- *
- * // or with options
- * var suite = new Benchmark.Suite('foo', {
- *
- * // called when the suite starts running
- * 'onStart': onStart,
- *
- * // called between running benchmarks
- * 'onCycle': onCycle,
- *
- * // called when aborted
- * 'onAbort': onAbort,
- *
- * // called when a test errors
- * 'onError': onError,
- *
- * // called when reset
- * 'onReset': onReset,
- *
- * // called when the suite completes running
- * 'onComplete': onComplete
- * });
- */
- function Suite(name, options) {
- var me = this;
+ /**
+ * Host objects can return type values that are different from their actual
+ * data type. The objects we are concerned with usually return non-primitive
+ * types of "object", "function", or "unknown".
+ *
+ * @private
+ * @param {Mixed} object The owner of the property.
+ * @param {String} property The property to check.
+ * @returns {Boolean} Returns `true` if the property value is a non-primitive, else `false`.
+ */
+ function isHostType(object, property) {
+ if (object == null) {
+ return false;
+ }
+ var type = typeof object[property];
+ return !rePrimitive.test(type) && (type != 'object' || !!object[property]);
+ }
- // allow instance creation without the `new` operator
- if (me == null || me.constructor != Suite) {
- return new Suite(name, options);
+ /**
+ * Checks if a value can be safely coerced to a string.
+ *
+ * @private
+ * @param {Mixed} value The value to check.
+ * @returns {Boolean} Returns `true` if the value can be coerced, else `false`.
+ */
+ function isStringable(value) {
+ return _.has(value, 'toString') || isClassOf(value, 'String');
}
- // juggle arguments
- if (isClassOf(name, 'Object')) {
- // 1 argument (options)
- options = name;
- } else {
- // 2 arguments (name [, options])
- me.name = name;
+
+ /**
+ * A no-operation function.
+ *
+ * @private
+ */
+ function noop() {
+ // no operation performed
}
- setOptions(me, options);
- }
- /*--------------------------------------------------------------------------*/
+ /**
+ * A wrapper around require() to suppress `module missing` errors.
+ *
+ * @private
+ * @param {String} id The module id.
+ * @returns {Mixed} The exported module or `null`.
+ */
+ function req(id) {
+ try {
+ var result = freeExports && freeRequire(id);
+ } catch(e) { }
+ return result || null;
+ }
- /**
- * Note: Some array methods have been implemented in plain JavaScript to avoid
- * bugs in IE, Opera, Rhino, and Mobile Safari.
- *
- * IE compatibility mode and IE < 9 have buggy Array `shift()` and `splice()`
- * functions that fail to remove the last element, `object[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.
- *
- * In Opera < 9.50 and some older/beta Mobile Safari versions using `unshift()`
- * generically to augment the `arguments` object will pave the value at index 0
- * without incrimenting the other values's indexes.
- * https://github.com/documentcloud/underscore/issues/9
- *
- * Rhino and environments it powers, like Narwhal and RingoJS, may have
- * buggy Array `concat()`, `reverse()`, `shift()`, `slice()`, `splice()` and
- * `unshift()` functions that make sparse arrays non-sparse by assigning the
- * undefined indexes a value of undefined.
- * https://github.com/mozilla/rhino/commit/702abfed3f8ca043b2636efd31c14ba7552603dd
- */
+ /**
+ * Runs a snippet of JavaScript via script injection.
+ *
+ * @private
+ * @param {String} code The code to run.
+ */
+ function runScript(code) {
+ var anchor = freeDefine ? define.amd : Benchmark,
+ script = doc.createElement('script'),
+ sibling = doc.getElementsByTagName('script')[0],
+ parent = sibling.parentNode,
+ prop = uid + 'runScript',
+ prefix = '(' + (freeDefine ? 'define.amd.' : 'Benchmark.') + prop + '||function(){})();';
+
+ // Firefox 2.0.0.2 cannot use script injection as intended because it executes
+ // asynchronously, but that's OK because script injection is only used to avoid
+ // the previously commented JaegerMonkey bug.
+ try {
+ // remove the inserted script *before* running the code to avoid differences
+ // in the expected script element count/order of the document.
+ script.appendChild(doc.createTextNode(prefix + code));
+ anchor[prop] = function() { destroyElement(script); };
+ } catch(e) {
+ parent = parent.cloneNode(false);
+ sibling = null;
+ script.text = code;
+ }
+ parent.insertBefore(script, sibling);
+ delete anchor[prop];
+ }
- /**
- * Creates an array containing the elements of the host array followed by the
- * elements of each argument in order.
- *
- * @memberOf Benchmark.Suite
- * @returns {Array} The new array.
- */
- function concat() {
- var value,
- j = -1,
- length = arguments.length,
- result = slice.call(this),
- index = result.length;
-
- while (++j < length) {
- value = arguments[j];
- if (isClassOf(value, 'Array')) {
- for (var k = 0, l = value.length; k < l; k++, index++) {
- if (k in value) {
- result[index] = value[k];
+ /**
+ * A helper function for setting options/event handlers.
+ *
+ * @private
+ * @param {Object} bench The benchmark instance.
+ * @param {Object} [options={}] Options object.
+ */
+ function setOptions(bench, options) {
+ options = _.extend({}, bench.constructor.options, options);
+ bench.options = _.forOwn(options, function(value, key) {
+ if (value != null) {
+ // add event listeners
+ if (/^on[A-Z]/.test(key)) {
+ _.each(key.split(' '), function(key) {
+ bench.on(key.slice(2).toLowerCase(), value);
+ });
+ } else if (!_.has(bench, key)) {
+ bench[key] = cloneDeep(value);
}
}
- } else {
- result[index++] = value;
- }
+ });
}
- return result;
- }
- /**
- * Utility function used by `shift()`, `splice()`, and `unshift()`.
- *
- * @private
- * @param {Number} start The index to start inserting elements.
- * @param {Number} deleteCount The number of elements to delete from the insert point.
- * @param {Array} elements The elements to insert.
- * @returns {Array} An array of deleted elements.
- */
- function insert(start, deleteCount, elements) {
- // `result` should have its length set to the `deleteCount`
- // see https://bugs.ecmascript.org/show_bug.cgi?id=332
- var deleteEnd = start + deleteCount,
- elementCount = elements ? elements.length : 0,
- index = start - 1,
- length = start + elementCount,
- object = this,
- result = Array(deleteCount),
- tail = slice.call(object, deleteEnd);
-
- // delete elements from the array
- while (++index < deleteEnd) {
- if (index in object) {
- result[index - start] = object[index];
- delete object[index];
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Handles cycling/completing the deferred benchmark.
+ *
+ * @memberOf Benchmark.Deferred
+ */
+ function resolve() {
+ var deferred = this,
+ clone = deferred.benchmark,
+ bench = clone._original;
+
+ if (bench.aborted) {
+ // cycle() -> clone cycle/complete event -> compute()'s invoked bench.run() cycle/complete
+ deferred.teardown();
+ clone.running = false;
+ cycle(deferred);
}
- }
- // insert elements
- index = start - 1;
- while (++index < length) {
- object[index] = elements[index - start];
- }
- // append tail elements
- start = index--;
- length = max(0, (object.length >>> 0) - deleteCount + elementCount);
- while (++index < length) {
- if ((index - start) in tail) {
- object[index] = tail[index - start];
- } else if (index in object) {
- delete object[index];
+ else if (++deferred.cycles < clone.count) {
+ // continue the test loop
+ if (support.timeout) {
+ // use setTimeout to avoid a call stack overflow if called recursively
+ setTimeout(function() { clone.compiled.call(deferred, context, timer); }, 0);
+ } else {
+ clone.compiled.call(deferred, context, timer);
+ }
}
- }
- // delete excess elements
- deleteCount = deleteCount > elementCount ? deleteCount - elementCount : 0;
- while (deleteCount--) {
- index = length + deleteCount;
- if (index in object) {
- delete object[index];
+ else {
+ timer.stop(deferred);
+ deferred.teardown();
+ delay(clone, function() { cycle(deferred); });
}
}
- object.length = length;
- return result;
- }
- /**
- * Rearrange the host array's elements in reverse order.
- *
- * @memberOf Benchmark.Suite
- * @returns {Array} The reversed array.
- */
- function reverse() {
- var upperIndex,
- value,
- index = -1,
- object = Object(this),
- length = object.length >>> 0,
- middle = floor(length / 2);
-
- if (length > 1) {
- while (++index < middle) {
- upperIndex = length - index - 1;
- value = upperIndex in object ? object[upperIndex] : uid;
- if (index in object) {
- object[upperIndex] = object[index];
- } else {
- delete object[upperIndex];
- }
- if (value != uid) {
- object[index] = value;
- } else {
- delete object[index];
- }
- }
- }
- return object;
- }
-
- /**
- * Removes the first element of the host array and returns it.
- *
- * @memberOf Benchmark.Suite
- * @returns {Mixed} The first element of the array.
- */
- function shift() {
- return insert.call(this, 0, 1)[0];
- }
-
- /**
- * Creates an array of the host array's elements from the start index up to,
- * but not including, the end index.
- *
- * @memberOf Benchmark.Suite
- * @param {Number} start The starting index.
- * @param {Number} end The end index.
- * @returns {Array} The new array.
- */
- function slice(start, end) {
- var index = -1,
- object = Object(this),
- length = object.length >>> 0,
- result = [];
-
- start = toInteger(start);
- start = start < 0 ? max(length + start, 0) : min(start, length);
- start--;
- end = end == null ? length : toInteger(end);
- end = end < 0 ? max(length + end, 0) : min(end, length);
-
- while ((++index, ++start) < end) {
- if (start in object) {
- result[index] = object[start];
- }
- }
- return result;
- }
-
- /**
- * Allows removing a range of elements and/or inserting elements into the
- * host array.
- *
- * @memberOf Benchmark.Suite
- * @param {Number} start The start index.
- * @param {Number} deleteCount The number of elements to delete.
- * @param {Mixed} [val1, val2, ...] values to insert at the `start` index.
- * @returns {Array} An array of removed elements.
- */
- function splice(start, deleteCount) {
- var object = Object(this),
- length = object.length >>> 0;
-
- start = toInteger(start);
- start = start < 0 ? max(length + start, 0) : min(start, length);
-
- // support the de-facto SpiderMonkey extension
- // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice#Parameters
- // https://bugs.ecmascript.org/show_bug.cgi?id=429
- deleteCount = arguments.length == 1
- ? length - start
- : min(max(toInteger(deleteCount), 0), length - start);
-
- return insert.call(object, start, deleteCount, slice.call(arguments, 2));
- }
-
- /**
- * Converts the specified `value` to an integer.
- *
- * @private
- * @param {Mixed} value The value to convert.
- * @returns {Number} The resulting integer.
- */
- function toInteger(value) {
- value = +value;
- return value === 0 || !isFinite(value) ? value || 0 : value - (value % 1);
- }
-
- /**
- * Appends arguments to the host array.
- *
- * @memberOf Benchmark.Suite
- * @returns {Number} The new length.
- */
- function unshift() {
- var object = Object(this);
- insert.call(object, 0, 0, arguments);
- return object.length;
- }
-
- /*--------------------------------------------------------------------------*/
-
- /**
- * A generic `Function#bind` like method.
- *
- * @private
- * @param {Function} fn The function to be bound to `thisArg`.
- * @param {Mixed} thisArg The `this` binding for the given function.
- * @returns {Function} The bound function.
- */
- function bind(fn, thisArg) {
- return function() { fn.apply(thisArg, arguments); };
- }
-
- /**
- * Creates a function from the given arguments string and body.
- *
- * @private
- * @param {String} args The comma separated function arguments.
- * @param {String} body The function body.
- * @returns {Function} The new function.
- */
- function createFunction() {
- // lazy define
- createFunction = function(args, body) {
- var result,
- anchor = freeDefine ? define.amd : Benchmark,
- prop = uid + 'createFunction';
-
- runScript((freeDefine ? 'define.amd.' : 'Benchmark.') + prop + '=function(' + args + '){' + body + '}');
- result = anchor[prop];
- delete anchor[prop];
- return result;
- };
- // fix JaegerMonkey bug
- // http://bugzil.la/639720
- createFunction = support.browser && (createFunction('', 'return"' + uid + '"') || noop)() == uid ? createFunction : Function;
- return createFunction.apply(null, arguments);
- }
-
- /**
- * Delay the execution of a function based on the benchmark's `delay` property.
- *
- * @private
- * @param {Object} bench The benchmark instance.
- * @param {Object} fn The function to execute.
- */
- function delay(bench, fn) {
- bench._timerId = setTimeout(fn, bench.delay * 1e3);
- }
-
- /**
- * Destroys the given element.
- *
- * @private
- * @param {Element} element The element to destroy.
- */
- function destroyElement(element) {
- trash.appendChild(element);
- trash.innerHTML = '';
- }
-
- /**
- * Iterates over an object's properties, executing the `callback` for each.
- * Callbacks may terminate the loop by explicitly returning `false`.
- *
- * @private
- * @param {Object} object The object to iterate over.
- * @param {Function} callback The function executed per own property.
- * @param {Object} options The options object.
- * @returns {Object} Returns the object iterated over.
- */
- function forProps() {
- var forShadowed,
- skipSeen,
- forArgs = true,
- shadowed = ['constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf'];
-
- (function(enumFlag, key) {
- // must use a non-native constructor to catch the Safari 2 issue
- function Klass() { this.valueOf = 0; };
- Klass.prototype.valueOf = 0;
- // check various for-in bugs
- for (key in new Klass) {
- enumFlag += key == 'valueOf' ? 1 : 0;
- }
- // check if `arguments` objects have non-enumerable indexes
- for (key in arguments) {
- key == '0' && (forArgs = false);
- }
- // Safari 2 iterates over shadowed properties twice
- // http://replay.waybackmachine.org/20090428222941/http://tobielangel.com/2007/1/29/for-in-loop-broken-in-safari/
- skipSeen = enumFlag == 2;
- // IE < 9 incorrectly makes an object's properties non-enumerable if they have
- // the same name as other non-enumerable properties in its prototype chain.
- forShadowed = !enumFlag;
- }(0));
-
- // lazy define
- forProps = function(object, callback, options) {
- options || (options = {});
-
- var result = object;
- object = Object(object);
-
- var ctor,
- key,
- keys,
- skipCtor,
- done = !result,
- which = options.which,
- allFlag = which == 'all',
- index = -1,
- iteratee = object,
- length = object.length,
- ownFlag = allFlag || which == 'own',
- seen = {},
- skipProto = isClassOf(object, 'Function'),
- thisArg = options.bind;
-
- if (thisArg !== undefined) {
- callback = bind(callback, thisArg);
- }
- // iterate all properties
- if (allFlag && support.getAllKeys) {
- for (index = 0, keys = getAllKeys(object), length = keys.length; index < length; index++) {
- key = keys[index];
- if (callback(object[key], key, object) === false) {
- break;
- }
- }
- }
- // else iterate only enumerable properties
- else {
- for (key in object) {
- // 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 set a function's `prototype` property [[Enumerable]] value
- // to `true`. Because of this we standardize on skipping the `prototype`
- // property of functions regardless of their [[Enumerable]] value.
- if ((done =
- !(skipProto && key == 'prototype') &&
- !(skipSeen && (hasKey(seen, key) || !(seen[key] = true))) &&
- (!ownFlag || ownFlag && hasKey(object, key)) &&
- callback(object[key], key, object) === false)) {
- break;
- }
- }
- // in IE < 9 strings don't support accessing characters by index
- if (!done && (forArgs && isArguments(object) ||
- ((noCharByIndex || noCharByOwnIndex) && isClassOf(object, 'String') &&
- (iteratee = noCharByIndex ? object.split('') : object)))) {
- while (++index < length) {
- if ((done =
- callback(iteratee[index], String(index), object) === false)) {
- break;
- }
- }
- }
- if (!done && forShadowed) {
- // Because IE < 9 can't set the `[[Enumerable]]` attribute of an existing
- // property and the `constructor` property of a prototype defaults to
- // non-enumerable, we manually skip the `constructor` property when we
- // think we are iterating over a `prototype` object.
- ctor = object.constructor;
- skipCtor = ctor && ctor.prototype && ctor.prototype.constructor === ctor;
- for (index = 0; index < 7; index++) {
- key = shadowed[index];
- if (!(skipCtor && key == 'constructor') &&
- hasKey(object, key) &&
- callback(object[key], key, object) === false) {
- break;
- }
- }
- }
- }
- return result;
- };
- return forProps.apply(null, arguments);
- }
-
- /**
- * Gets the name of the first argument from a function's source.
- *
- * @private
- * @param {Function} fn The function.
- * @returns {String} The argument name.
- */
- function getFirstArgument(fn) {
- return (!hasKey(fn, 'toString') &&
- (/^[\s(]*function[^(]*\(([^\s,)]+)/.exec(fn) || 0)[1]) || '';
- }
-
- /**
- * Computes the geometric mean (log-average) of a sample.
- * See http://en.wikipedia.org/wiki/Geometric_mean#Relationship_with_arithmetic_mean_of_logarithms.
- *
- * @private
- * @param {Array} sample The sample.
- * @returns {Number} The geometric mean.
- */
- function getGeometricMean(sample) {
- return pow(Math.E, reduce(sample, function(sum, x) {
- return sum + log(x);
- }) / sample.length) || 0;
- }
-
- /**
- * Computes the arithmetic mean of a sample.
- *
- * @private
- * @param {Array} sample The sample.
- * @returns {Number} The mean.
- */
- function getMean(sample) {
- return (reduce(sample, function(sum, x) {
- return sum + x;
- }) / sample.length) || 0;
- }
-
- /**
- * Gets the source code of a function.
- *
- * @private
- * @param {Function} fn The function.
- * @param {String} altSource A string used when a function's source code is unretrievable.
- * @returns {String} The function's source code.
- */
- function getSource(fn, altSource) {
- var result = altSource;
- if (isStringable(fn)) {
- result = String(fn);
- } else if (support.decompilation) {
- // escape the `{` for Firefox 1
- result = (/^[^{]+\{([\s\S]*)\}\s*$/.exec(fn) || 0)[1];
- }
- // trim string
- result = (result || '').replace(/^\s+|\s+$/g, '');
-
- // detect strings containing only the "use strict" directive
- return /^(?:\/\*+[\w|\W]*?\*\/|\/\/.*?[\n\r\u2028\u2029]|\s)*(["'])use strict\1;?$/.test(result)
- ? ''
- : result;
- }
-
- /**
- * Checks if a value is an `arguments` object.
- *
- * @private
- * @param {Mixed} value The value to check.
- * @returns {Boolean} Returns `true` if the value is an `arguments` object, else `false`.
- */
- function isArguments() {
- // lazy define
- isArguments = function(value) {
- return toString.call(value) == '[object Arguments]';
- };
- if (noArgumentsClass) {
- isArguments = function(value) {
- return hasKey(value, 'callee') &&
- !(propertyIsEnumerable && propertyIsEnumerable.call(value, 'callee'));
- };
- }
- return isArguments(arguments[0]);
- }
-
- /**
- * Checks if an object is of the specified class.
- *
- * @private
- * @param {Mixed} value The value to check.
- * @param {String} name The name of the class.
- * @returns {Boolean} Returns `true` if the value is of the specified class, else `false`.
- */
- function isClassOf(value, name) {
- return value != null && toString.call(value) == '[object ' + name + ']';
- }
-
- /**
- * Host objects can return type values that are different from their actual
- * data type. The objects we are concerned with usually return non-primitive
- * types of object, function, or unknown.
- *
- * @private
- * @param {Mixed} object The owner of the property.
- * @param {String} property The property to check.
- * @returns {Boolean} Returns `true` if the property value is a non-primitive, else `false`.
- */
- function isHostType(object, property) {
- var type = object != null ? typeof object[property] : 'number';
- return !/^(?:boolean|number|string|undefined)$/.test(type) &&
- (type == 'object' ? !!object[property] : true);
- }
-
- /**
- * 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 the `value` is a plain `Object` object, else `false`.
- */
- function isPlainObject(value) {
- // avoid non-objects and false positives for `arguments` objects in IE < 9
- 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 ((support.nodeClass || !(typeof value.toString != 'function' && typeof (value + '') == 'string')) &&
- (!isClassOf(ctor, 'Function') || 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.
- if (support.iteratesOwnFirst) {
- forProps(value, function(subValue, subKey) {
- result = subKey;
- });
- return result === false || hasKey(value, result);
- }
- // 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.
- forProps(value, function(subValue, subKey) {
- result = !hasKey(value, subKey);
- return false;
- });
- return result === false;
- }
- return result;
- }
-
- /**
- * Checks if a value can be safely coerced to a string.
- *
- * @private
- * @param {Mixed} value The value to check.
- * @returns {Boolean} Returns `true` if the value can be coerced, else `false`.
- */
- function isStringable(value) {
- return hasKey(value, 'toString') || isClassOf(value, 'String');
- }
-
- /**
- * Wraps a function and passes `this` to the original function as the
- * first argument.
- *
- * @private
- * @param {Function} fn The function to be wrapped.
- * @returns {Function} The new function.
- */
- function methodize(fn) {
- return function() {
- var args = [this];
- args.push.apply(args, arguments);
- return fn.apply(null, args);
- };
- }
-
- /**
- * A no-operation function.
- *
- * @private
- */
- function noop() {
- // no operation performed
- }
-
- /**
- * A wrapper around require() to suppress `module missing` errors.
- *
- * @private
- * @param {String} id The module id.
- * @returns {Mixed} The exported module or `null`.
- */
- function req(id) {
- try {
- var result = freeExports && freeRequire(id);
- } catch(e) { }
- return result || null;
- }
-
- /**
- * Runs a snippet of JavaScript via script injection.
- *
- * @private
- * @param {String} code The code to run.
- */
- function runScript(code) {
- var anchor = freeDefine ? define.amd : Benchmark,
- script = doc.createElement('script'),
- sibling = doc.getElementsByTagName('script')[0],
- parent = sibling.parentNode,
- prop = uid + 'runScript',
- prefix = '(' + (freeDefine ? 'define.amd.' : 'Benchmark.') + prop + '||function(){})();';
-
- // Firefox 2.0.0.2 cannot use script injection as intended because it executes
- // asynchronously, but that's OK because script injection is only used to avoid
- // the previously commented JaegerMonkey bug.
- try {
- // remove the inserted script *before* running the code to avoid differences
- // in the expected script element count/order of the document.
- script.appendChild(doc.createTextNode(prefix + code));
- anchor[prop] = function() { destroyElement(script); };
- } catch(e) {
- parent = parent.cloneNode(false);
- sibling = null;
- script.text = code;
- }
- parent.insertBefore(script, sibling);
- delete anchor[prop];
- }
-
- /**
- * A helper function for setting options/event handlers.
- *
- * @private
- * @param {Object} bench The benchmark instance.
- * @param {Object} [options={}] Options object.
- */
- function setOptions(bench, options) {
- options = extend({}, bench.constructor.options, options);
- bench.options = forOwn(options, function(value, key) {
- if (value != null) {
- // add event listeners
- if (/^on[A-Z]/.test(key)) {
- forEach(key.split(' '), function(key) {
- bench.on(key.slice(2).toLowerCase(), value);
- });
- } else if (!hasKey(bench, key)) {
- bench[key] = deepClone(value);
- }
- }
- });
- }
-
- /*--------------------------------------------------------------------------*/
-
- /**
- * Handles cycling/completing the deferred benchmark.
- *
- * @memberOf Benchmark.Deferred
- */
- function resolve() {
- var me = this,
- clone = me.benchmark,
- bench = clone._original;
-
- if (bench.aborted) {
- // cycle() -> clone cycle/complete event -> compute()'s invoked bench.run() cycle/complete
- me.teardown();
- clone.running = false;
- cycle(me);
- }
- else if (++me.cycles < clone.count) {
- // continue the test loop
- if (support.timeout) {
- // use setTimeout to avoid a call stack overflow if called recursively
- setTimeout(function() { clone.compiled.call(me, timer); }, 0);
- } else {
- clone.compiled.call(me, timer);
- }
- }
- else {
- timer.stop(me);
- me.teardown();
- delay(clone, function() { cycle(me); });
- }
- }
-
- /*--------------------------------------------------------------------------*/
-
- /**
- * A deep clone utility.
- *
- * @static
- * @memberOf Benchmark
- * @param {Mixed} value The value to clone.
- * @returns {Mixed} The cloned value.
- */
- function deepClone(value) {
- var accessor,
- circular,
- clone,
- ctor,
- descriptor,
- extensible,
- key,
- length,
- markerKey,
- parent,
- result,
- source,
- subIndex,
- data = { 'value': value },
- index = 0,
- marked = [],
- queue = { 'length': 0 },
- unmarked = [];
-
- /**
- * An easily detectable decorator for cloned values.
- */
- function Marker(object) {
- this.raw = object;
- }
-
- /**
- * The callback used by `forProps()`.
- */
- function forPropsCallback(subValue, subKey) {
- // exit early to avoid cloning the marker
- if (subValue && subValue.constructor == Marker) {
- return;
- }
- // add objects to the queue
- if (subValue === Object(subValue)) {
- queue[queue.length++] = { 'key': subKey, 'parent': clone, 'source': value };
- }
- // assign non-objects
- else {
- try {
- // will throw an error in strict mode if the property is read-only
- clone[subKey] = subValue;
- } catch(e) { }
- }
- }
-
- /**
- * Gets an available marker key for the given object.
- */
- function getMarkerKey(object) {
- // avoid collisions with existing keys
- var result = uid;
- while (object[result] && object[result].constructor != Marker) {
- result += 1;
- }
- return result;
- }
-
- do {
- key = data.key;
- parent = data.parent;
- source = data.source;
- clone = value = source ? source[key] : data.value;
- accessor = circular = descriptor = false;
-
- // create a basic clone to filter out functions, DOM elements, and
- // other non `Object` objects
- if (value === Object(value)) {
- // use custom deep clone function if available
- if (isClassOf(value.deepClone, 'Function')) {
- clone = value.deepClone();
- } else {
- ctor = value.constructor;
- switch (toString.call(value)) {
- case '[object Array]':
- clone = new ctor(value.length);
- break;
-
- case '[object Boolean]':
- clone = new ctor(value == true);
- break;
-
- case '[object Date]':
- clone = new ctor(+value);
- break;
-
- case '[object Object]':
- isPlainObject(value) && (clone = {});
- break;
-
- case '[object Number]':
- case '[object String]':
- clone = new ctor(value);
- break;
-
- case '[object RegExp]':
- clone = ctor(value.source,
- (value.global ? 'g' : '') +
- (value.ignoreCase ? 'i' : '') +
- (value.multiline ? 'm' : ''));
- }
- }
- // continue clone if `value` doesn't have an accessor descriptor
- // http://es5.github.com/#x8.10.1
- if (clone && clone != value &&
- !(descriptor = source && support.descriptors && getDescriptor(source, key),
- accessor = descriptor && (descriptor.get || descriptor.set))) {
- // use an existing clone (circular reference)
- if ((extensible = isExtensible(value))) {
- markerKey = getMarkerKey(value);
- if (value[markerKey]) {
- circular = clone = value[markerKey].raw;
- }
- } else {
- // for frozen/sealed objects
- for (subIndex = 0, length = unmarked.length; subIndex < length; subIndex++) {
- data = unmarked[subIndex];
- if (data.object === value) {
- circular = clone = data.clone;
- break;
- }
- }
- }
- if (!circular) {
- // mark object to allow quickly detecting circular references and tie it to its clone
- if (extensible) {
- value[markerKey] = new Marker(clone);
- marked.push({ 'key': markerKey, 'object': value });
- } else {
- // for frozen/sealed objects
- unmarked.push({ 'clone': clone, 'object': value });
- }
- // iterate over object properties
- forProps(value, forPropsCallback, { 'which': 'all' });
- }
- }
- }
- if (parent) {
- // for custom property descriptors
- if (accessor || (descriptor && !(descriptor.configurable && descriptor.enumerable && descriptor.writable))) {
- if ('value' in descriptor) {
- descriptor.value = clone;
- }
- setDescriptor(parent, key, descriptor);
- }
- // for default property descriptors
- else {
- parent[key] = clone;
- }
- } else {
- result = clone;
- }
- } while ((data = queue[index++]));
-
- // remove markers
- for (index = 0, length = marked.length; index < length; index++) {
- data = marked[index];
- delete data.object[data.key];
- }
- return result;
- }
-
- /**
- * An iteration utility for arrays and objects.
- * Callbacks may terminate the loop by explicitly returning `false`.
- *
- * @static
- * @memberOf Benchmark
- * @param {Array|Object} object The object to iterate over.
- * @param {Function} callback The function called per iteration.
- * @param {Mixed} thisArg The `this` binding for the callback.
- * @returns {Array|Object} Returns the object iterated over.
- */
- function each(object, callback, thisArg) {
- var result = object;
- object = Object(object);
-
- var fn = callback,
- index = -1,
- length = object.length,
- isSnapshot = !!(object.snapshotItem && (length = object.snapshotLength)),
- isSplittable = (noCharByIndex || noCharByOwnIndex) && isClassOf(object, 'String'),
- isConvertable = isSnapshot || isSplittable || 'item' in object,
- origObject = object;
-
- // in Opera < 10.5 `hasKey(object, 'length')` returns `false` for NodeLists
- if (length === length >>> 0) {
- if (isConvertable) {
- // the third argument of the callback is the original non-array object
- callback = function(value, index) {
- return fn.call(this, value, index, origObject);
- };
- // in IE < 9 strings don't support accessing characters by index
- if (isSplittable) {
- object = object.split('');
- } else {
- object = [];
- while (++index < length) {
- // in Safari 2 `index in object` is always `false` for NodeLists
- object[index] = isSnapshot ? result.snapshotItem(index) : result[index];
- }
- }
- }
- forEach(object, callback, thisArg);
- } else {
- forOwn(object, callback, thisArg);
- }
- return result;
- }
-
- /**
- * Copies enumerable properties from the source(s) object to the destination object.
- *
- * @static
- * @memberOf Benchmark
- * @param {Object} destination The destination object.
- * @param {Object} [source={}] The source object.
- * @returns {Object} The destination object.
- */
- function extend(destination, source) {
- // Chrome < 14 incorrectly sets `destination` to `undefined` when we `delete arguments[0]`
- // http://code.google.com/p/v8/issues/detail?id=839
- var result = destination;
- delete arguments[0];
-
- forEach(arguments, function(source) {
- forProps(source, function(value, key) {
- result[key] = value;
- });
- });
- return result;
- }
-
- /**
- * A generic `Array#filter` like method.
- *
- * @static
- * @memberOf Benchmark
- * @param {Array} array The array to iterate over.
- * @param {Function|String} callback The function/alias called per iteration.
- * @param {Mixed} thisArg The `this` binding for the callback.
- * @returns {Array} A new array of values that passed callback filter.
- * @example
- *
- * // get odd numbers
- * Benchmark.filter([1, 2, 3, 4, 5], function(n) {
- * return n % 2;
- * }); // -> [1, 3, 5];
- *
- * // get fastest benchmarks
- * Benchmark.filter(benches, 'fastest');
- *
- * // get slowest benchmarks
- * Benchmark.filter(benches, 'slowest');
- *
- * // get benchmarks that completed without erroring
- * Benchmark.filter(benches, 'successful');
- */
- function filter(array, callback, thisArg) {
- var result;
-
- if (callback == 'successful') {
- // callback to exclude those that are errored, unrun, or have hz of Infinity
- callback = function(bench) { return bench.cycles && isFinite(bench.hz); };
- }
- else if (callback == 'fastest' || callback == 'slowest') {
- // get successful, sort by period + margin of error, and filter fastest/slowest
- result = filter(array, 'successful').sort(function(a, b) {
- a = a.stats; b = b.stats;
- return (a.mean + a.moe > b.mean + b.moe ? 1 : -1) * (callback == 'fastest' ? 1 : -1);
- });
- result = filter(result, function(bench) {
- return result[0].compare(bench) == 0;
- });
- }
- return result || reduce(array, function(result, value, index) {
- return callback.call(thisArg, value, index, array) ? (result.push(value), result) : result;
- }, []);
- }
-
- /**
- * A generic `Array#forEach` like method.
- * Callbacks may terminate the loop by explicitly returning `false`.
- *
- * @static
- * @memberOf Benchmark
- * @param {Array} array The array to iterate over.
- * @param {Function} callback The function called per iteration.
- * @param {Mixed} thisArg The `this` binding for the callback.
- * @returns {Array} Returns the array iterated over.
- */
- function forEach(array, callback, thisArg) {
- var index = -1,
- length = (array = Object(array)).length >>> 0;
-
- if (thisArg !== undefined) {
- callback = bind(callback, thisArg);
- }
- while (++index < length) {
- if (index in array &&
- callback(array[index], index, array) === false) {
- break;
- }
- }
- return array;
- }
-
- /**
- * Iterates over an object's own properties, executing the `callback` for each.
- * Callbacks may terminate the loop by explicitly returning `false`.
- *
- * @static
- * @memberOf Benchmark
- * @param {Object} object The object to iterate over.
- * @param {Function} callback The function executed per own property.
- * @param {Mixed} thisArg The `this` binding for the callback.
- * @returns {Object} Returns the object iterated over.
- */
- function forOwn(object, callback, thisArg) {
- return forProps(object, callback, { 'bind': thisArg, 'which': 'own' });
- }
-
- /**
- * Converts a number to a more readable comma-separated string representation.
- *
- * @static
- * @memberOf Benchmark
- * @param {Number} number The number to convert.
- * @returns {String} The more readable string representation.
- */
- function formatNumber(number) {
- number = String(number).split('.');
- return number[0].replace(/(?=(?:\d{3})+$)(?!\b)/g, ',') +
- (number[1] ? '.' + number[1] : '');
- }
-
- /**
- * Checks if an object has the specified key as a direct property.
- *
- * @static
- * @memberOf Benchmark
- * @param {Object} object The object to check.
- * @param {String} key The key to check for.
- * @returns {Boolean} Returns `true` if key is a direct property, else `false`.
- */
- function hasKey() {
- // lazy define for worst case fallback (not as accurate)
- hasKey = function(object, key) {
- var parent = object != null && (object.constructor || Object).prototype;
- return !!parent && key in Object(object) && !(key in parent && object[key] === parent[key]);
- };
- // for modern browsers
- if (isClassOf(hasOwnProperty, 'Function')) {
- hasKey = function(object, key) {
- return object != null && hasOwnProperty.call(object, key);
- };
- }
- // for Safari 2
- else if ({}.__proto__ == Object.prototype) {
- hasKey = function(object, key) {
- var result = false;
- if (object != null) {
- object = Object(object);
- object.__proto__ = [object.__proto__, object.__proto__ = null, result = key in object][0];
- }
- return result;
- };
- }
- return hasKey.apply(this, arguments);
- }
-
- /**
- * A generic `Array#indexOf` like method.
- *
- * @static
- * @memberOf Benchmark
- * @param {Array} array The array to iterate over.
- * @param {Mixed} value The value to search for.
- * @param {Number} [fromIndex=0] The index to start searching from.
- * @returns {Number} The index of the matched value or `-1`.
- */
- function indexOf(array, value, fromIndex) {
- var index = toInteger(fromIndex),
- length = (array = Object(array)).length >>> 0;
-
- index = (index < 0 ? max(0, length + index) : index) - 1;
- while (++index < length) {
- if (index in array && value === array[index]) {
- return index;
- }
- }
- return -1;
- }
-
- /**
- * Modify a string by replacing named tokens with matching object property values.
- *
- * @static
- * @memberOf Benchmark
- * @param {String} string The string to modify.
- * @param {Object} object The template object.
- * @returns {String} The modified string.
- */
- function interpolate(string, object) {
- forOwn(object, function(value, key) {
- // escape regexp special characters in `key`
- string = string.replace(
- RegExp('#\\{' + key.replace(/([.*+?^${}()|[\]\\])/g, '\\$1') + '\\}', 'g'),
- value.replace(/\$/g, '$$$$')
- );
- });
- return string;
- }
-
- /**
- * Invokes a method on all items in an array.
- *
- * @static
- * @memberOf Benchmark
- * @param {Array} benches Array of benchmarks to iterate over.
- * @param {String|Object} name The name of the method to invoke OR options object.
- * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with.
- * @returns {Array} A new array of values returned from each method invoked.
- * @example
- *
- * // invoke `reset` on all benchmarks
- * Benchmark.invoke(benches, 'reset');
- *
- * // invoke `emit` with arguments
- * Benchmark.invoke(benches, 'emit', 'complete', listener);
- *
- * // invoke `run(true)`, treat benchmarks as a queue, and register invoke callbacks
- * Benchmark.invoke(benches, {
- *
- * // invoke the `run` method
- * 'name': 'run',
- *
- * // pass a single argument
- * 'args': true,
- *
- * // treat as queue, removing benchmarks from front of `benches` until empty
- * 'queued': true,
- *
- * // called before any benchmarks have been invoked.
- * 'onStart': onStart,
- *
- * // called between invoking benchmarks
- * 'onCycle': onCycle,
- *
- * // called after all benchmarks have been invoked.
- * 'onComplete': onComplete
- * });
- */
- function invoke(benches, name) {
- var args,
- bench,
- queued,
- index = -1,
- eventProps = { 'currentTarget': benches },
- options = { 'onStart': noop, 'onCycle': noop, 'onComplete': noop },
- result = map(benches, function(bench) { return bench; });
-
- /**
- * Invokes the method of the current object and if synchronous, fetches the next.
- */
- function execute() {
- var listeners,
- async = isAsync(bench);
-
- if (async) {
- // use `getNext` as the first listener
- bench.on('complete', getNext);
- listeners = bench.events.complete;
- listeners.splice(0, 0, listeners.pop());
- }
- // execute method
- result[index] = isClassOf(bench && bench[name], 'Function') ? bench[name].apply(bench, args) : undefined;
- // if synchronous return true until finished
- return !async && getNext();
- }
-
- /**
- * Fetches the next bench or executes `onComplete` callback.
- */
- function getNext(event) {
- var cycleEvent,
- last = bench,
- async = isAsync(last);
-
- if (async) {
- last.off('complete', getNext);
- last.emit('complete');
- }
- // emit "cycle" event
- eventProps.type = 'cycle';
- eventProps.target = last;
- cycleEvent = Event(eventProps);
- options.onCycle.call(benches, cycleEvent);
-
- // choose next benchmark if not exiting early
- if (!cycleEvent.aborted && raiseIndex() !== false) {
- bench = queued ? benches[0] : result[index];
- if (isAsync(bench)) {
- delay(bench, execute);
- }
- else if (async) {
- // resume execution if previously asynchronous but now synchronous
- while (execute()) { }
- }
- else {
- // continue synchronous execution
- return true;
- }
- } else {
- // emit "complete" event
- eventProps.type = 'complete';
- options.onComplete.call(benches, Event(eventProps));
- }
- // When used as a listener `event.aborted = true` will cancel the rest of
- // the "complete" listeners because they were already called above and when
- // used as part of `getNext` the `return false` will exit the execution while-loop.
- if (event) {
- event.aborted = true;
- } else {
- return false;
- }
- }
-
- /**
- * Checks if invoking `Benchmark#run` with asynchronous cycles.
- */
- function isAsync(object) {
- // avoid using `instanceof` here because of IE memory leak issues with host objects
- var async = args[0] && args[0].async;
- return Object(object).constructor == Benchmark && name == 'run' &&
- ((async == null ? object.options.async : async) && support.timeout || object.defer);
- }
-
- /**
- * Raises `index` to the next defined index or returns `false`.
- */
- function raiseIndex() {
- var length = result.length;
- if (queued) {
- // if queued remove the previous bench and subsequent skipped non-entries
- do {
- ++index > 0 && shift.call(benches);
- } while ((length = benches.length) && !('0' in benches));
- }
- else {
- while (++index < length && !(index in result)) { }
- }
- // if we reached the last index then return `false`
- return (queued ? length : index < length) ? index : (index = false);
- }
-
- // juggle arguments
- if (isClassOf(name, 'String')) {
- // 2 arguments (array, name)
- args = slice.call(arguments, 2);
- } else {
- // 2 arguments (array, options)
- options = extend(options, name);
- name = options.name;
- args = isClassOf(args = 'args' in options ? options.args : [], 'Array') ? args : [args];
- queued = options.queued;
- }
-
- // start iterating over the array
- if (raiseIndex() !== false) {
- // emit "start" event
- bench = result[index];
- eventProps.type = 'start';
- eventProps.target = bench;
- options.onStart.call(benches, Event(eventProps));
-
- // end early if the suite was aborted in an "onStart" listener
- if (benches.aborted && benches.constructor == Suite && name == 'run') {
- // emit "cycle" event
- eventProps.type = 'cycle';
- options.onCycle.call(benches, Event(eventProps));
- // emit "complete" event
- eventProps.type = 'complete';
- options.onComplete.call(benches, Event(eventProps));
- }
- // else start
- else {
- if (isAsync(bench)) {
- delay(bench, execute);
- } else {
- while (execute()) { }
- }
- }
- }
- return result;
- }
-
- /**
- * Creates a string of joined array values or object key-value pairs.
- *
- * @static
- * @memberOf Benchmark
- * @param {Array|Object} object The object to operate on.
- * @param {String} [separator1=','] The separator used between key-value pairs.
- * @param {String} [separator2=': '] The separator used between keys and values.
- * @returns {String} The joined result.
- */
- function join(object, separator1, separator2) {
- var result = [],
- length = (object = Object(object)).length,
- arrayLike = length === length >>> 0;
-
- separator2 || (separator2 = ': ');
- each(object, function(value, key) {
- result.push(arrayLike ? value : key + separator2 + value);
- });
- return result.join(separator1 || ',');
- }
-
- /**
- * A generic `Array#map` like method.
- *
- * @static
- * @memberOf Benchmark
- * @param {Array} array The array to iterate over.
- * @param {Function} callback The function called per iteration.
- * @param {Mixed} thisArg The `this` binding for the callback.
- * @returns {Array} A new array of values returned by the callback.
- */
- function map(array, callback, thisArg) {
- return reduce(array, function(result, value, index) {
- result[index] = callback.call(thisArg, value, index, array);
- return result;
- }, Array(Object(array).length >>> 0));
- }
-
- /**
- * Retrieves the value of a specified property from all items in an array.
- *
- * @static
- * @memberOf Benchmark
- * @param {Array} array The array to iterate over.
- * @param {String} property The property to pluck.
- * @returns {Array} A new array of property values.
- */
- function pluck(array, property) {
- return map(array, function(object) {
- return object == null ? undefined : object[property];
- });
- }
-
- /**
- * A generic `Array#reduce` like method.
- *
- * @static
- * @memberOf Benchmark
- * @param {Array} array The array to iterate over.
- * @param {Function} callback The function called per iteration.
- * @param {Mixed} accumulator Initial value of the accumulator.
- * @returns {Mixed} The accumulator.
- */
- function reduce(array, callback, accumulator) {
- var noaccum = arguments.length < 3;
- forEach(array, function(value, index) {
- accumulator = noaccum ? (noaccum = false, value) : callback(accumulator, value, index, array);
- });
- return accumulator;
- }
+ /*------------------------------------------------------------------------*/
- /*--------------------------------------------------------------------------*/
+ /**
+ * A generic `Array#filter` like method.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @param {Array} array The array to iterate over.
+ * @param {Function|String} callback The function/alias called per iteration.
+ * @param {Mixed} thisArg The `this` binding for the callback.
+ * @returns {Array} A new array of values that passed callback filter.
+ * @example
+ *
+ * // get odd numbers
+ * Benchmark.filter([1, 2, 3, 4, 5], function(n) {
+ * return n % 2;
+ * }); // -> [1, 3, 5];
+ *
+ * // get fastest benchmarks
+ * Benchmark.filter(benches, 'fastest');
+ *
+ * // get slowest benchmarks
+ * Benchmark.filter(benches, 'slowest');
+ *
+ * // get benchmarks that completed without erroring
+ * Benchmark.filter(benches, 'successful');
+ */
+ function filter(array, callback, thisArg) {
+ if (callback === 'successful') {
+ // callback to exclude those that are errored, unrun, or have hz of Infinity
+ callback = function(bench) {
+ return bench.cycles && _.isFinite(bench.hz);
+ };
+ }
+ else if (callback === 'fastest' || callback === 'slowest') {
+ // get successful, sort by period + margin of error, and filter fastest/slowest
+ var result = filter(array, 'successful').sort(function(a, b) {
+ a = a.stats; b = b.stats;
+ return (a.mean + a.moe > b.mean + b.moe ? 1 : -1) * (callback === 'fastest' ? 1 : -1);
+ });
- /**
- * Aborts all benchmarks in the suite.
- *
- * @name abort
- * @memberOf Benchmark.Suite
- * @returns {Object} The suite instance.
- */
- function abortSuite() {
- var event,
- me = this,
- resetting = calledBy.resetSuite;
-
- if (me.running) {
- event = Event('abort');
- me.emit(event);
- if (!event.cancelled || resetting) {
- // avoid infinite recursion
- calledBy.abortSuite = true;
- me.reset();
- delete calledBy.abortSuite;
-
- if (!resetting) {
- me.aborted = true;
- invoke(me, 'abort');
- }
+ return _.filter(result, function(bench) {
+ return result[0].compare(bench) == 0;
+ });
}
+ return _.filter(array, callback, thisArg);
}
- return me;
- }
-
- /**
- * Adds a test to the benchmark suite.
- *
- * @memberOf Benchmark.Suite
- * @param {String} name A name to identify the benchmark.
- * @param {Function|String} fn The test to benchmark.
- * @param {Object} [options={}] Options object.
- * @returns {Object} The benchmark instance.
- * @example
- *
- * // basic usage
- * suite.add(fn);
- *
- * // or using a name first
- * suite.add('foo', fn);
- *
- * // or with options
- * suite.add('foo', fn, {
- * 'onCycle': onCycle,
- * 'onComplete': onComplete
- * });
- *
- * // or name and options
- * suite.add('foo', {
- * 'fn': fn,
- * 'onCycle': onCycle,
- * 'onComplete': onComplete
- * });
- *
- * // or options only
- * suite.add({
- * 'name': 'foo',
- * 'fn': fn,
- * 'onCycle': onCycle,
- * 'onComplete': onComplete
- * });
- */
- function add(name, fn, options) {
- var me = this,
- bench = Benchmark(name, fn, options),
- event = Event({ 'type': 'add', 'target': bench });
- if (me.emit(event), !event.cancelled) {
- me.push(bench);
+ /**
+ * Converts a number to a more readable comma-separated string representation.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @param {Number} number The number to convert.
+ * @returns {String} The more readable string representation.
+ */
+ function formatNumber(number) {
+ number = String(number).split('.');
+ return number[0].replace(/(?=(?:\d{3})+$)(?!\b)/g, ',') +
+ (number[1] ? '.' + number[1] : '');
}
- return me;
- }
-
- /**
- * Creates a new suite with cloned benchmarks.
- *
- * @name clone
- * @memberOf Benchmark.Suite
- * @param {Object} options Options object to overwrite cloned options.
- * @returns {Object} The new suite instance.
- */
- function cloneSuite(options) {
- var me = this,
- result = new me.constructor(extend({}, me.options, options));
-
- // copy own properties
- forOwn(me, function(value, key) {
- if (!hasKey(result, key)) {
- result[key] = value && isClassOf(value.clone, 'Function')
- ? value.clone()
- : deepClone(value);
- }
- });
- return result;
- }
- /**
- * An `Array#filter` like method.
- *
- * @name filter
- * @memberOf Benchmark.Suite
- * @param {Function|String} callback The function/alias called per iteration.
- * @returns {Object} A new suite of benchmarks that passed callback filter.
- */
- function filterSuite(callback) {
- var me = this,
- result = new me.constructor;
-
- result.push.apply(result, filter(me, callback));
- return result;
- }
-
- /**
- * Resets all benchmarks in the suite.
- *
- * @name reset
- * @memberOf Benchmark.Suite
- * @returns {Object} The suite instance.
- */
- function resetSuite() {
- var event,
- me = this,
- aborting = calledBy.abortSuite;
-
- if (me.running && !aborting) {
- // no worries, `resetSuite()` is called within `abortSuite()`
- calledBy.resetSuite = true;
- me.abort();
- delete calledBy.resetSuite;
- }
- // reset if the state has changed
- else if ((me.aborted || me.running) &&
- (me.emit(event = Event('reset')), !event.cancelled)) {
- me.running = false;
- if (!aborting) {
- invoke(me, 'reset');
- }
- }
- return me;
- }
+ /**
+ * Invokes a method on all items in an array.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @param {Array} benches Array of benchmarks to iterate over.
+ * @param {String|Object} name The name of the method to invoke OR options object.
+ * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with.
+ * @returns {Array} A new array of values returned from each method invoked.
+ * @example
+ *
+ * // invoke `reset` on all benchmarks
+ * Benchmark.invoke(benches, 'reset');
+ *
+ * // invoke `emit` with arguments
+ * Benchmark.invoke(benches, 'emit', 'complete', listener);
+ *
+ * // invoke `run(true)`, treat benchmarks as a queue, and register invoke callbacks
+ * Benchmark.invoke(benches, {
+ *
+ * // invoke the `run` method
+ * 'name': 'run',
+ *
+ * // pass a single argument
+ * 'args': true,
+ *
+ * // treat as queue, removing benchmarks from front of `benches` until empty
+ * 'queued': true,
+ *
+ * // called before any benchmarks have been invoked.
+ * 'onStart': onStart,
+ *
+ * // called between invoking benchmarks
+ * 'onCycle': onCycle,
+ *
+ * // called after all benchmarks have been invoked.
+ * 'onComplete': onComplete
+ * });
+ */
+ function invoke(benches, name) {
+ var args,
+ bench,
+ queued,
+ index = -1,
+ eventProps = { 'currentTarget': benches },
+ options = { 'onStart': noop, 'onCycle': noop, 'onComplete': noop },
+ result = _.toArray(benches);
- /**
- * Runs the suite.
- *
- * @name run
- * @memberOf Benchmark.Suite
- * @param {Object} [options={}] Options object.
- * @returns {Object} The suite instance.
- * @example
- *
- * // basic usage
- * suite.run();
- *
- * // or with options
- * suite.run({ 'async': true, 'queued': true });
- */
- function runSuite(options) {
- var me = this;
-
- me.reset();
- me.running = true;
- options || (options = {});
-
- invoke(me, {
- 'name': 'run',
- 'args': options,
- 'queued': options.queued,
- 'onStart': function(event) {
- me.emit(event);
- },
- 'onCycle': function(event) {
- var bench = event.target;
- if (bench.error) {
- me.emit({ 'type': 'error', 'target': bench });
+ /**
+ * Invokes the method of the current object and if synchronous, fetches the next.
+ */
+ function execute() {
+ var listeners,
+ async = isAsync(bench);
+
+ if (async) {
+ // use `getNext` as the first listener
+ bench.on('complete', getNext);
+ listeners = bench.events.complete;
+ listeners.splice(0, 0, listeners.pop());
}
- me.emit(event);
- event.aborted = me.aborted;
- },
- 'onComplete': function(event) {
- me.score = getGeometricMean(map(me, function(bench) {
- return bench.reference / (bench.times.period * 1e6);
- })) || 0;
-
- me.running = false;
- me.emit(event);
+ // execute method
+ result[index] = _.isFunction(bench && bench[name]) ? bench[name].apply(bench, args) : undefined;
+ // if synchronous return true until finished
+ return !async && getNext();
}
- });
- return me;
- }
-
- /*--------------------------------------------------------------------------*/
- /**
- * Executes all registered listeners of the specified event type.
- *
- * @memberOf Benchmark, Benchmark.Suite
- * @param {String|Object} type The event type or object.
- * @returns {Mixed} Returns the return value of the last listener executed.
- */
- function emit(type) {
- var listeners,
- me = this,
- event = Event(type),
- events = me.events,
- args = (arguments[0] = event, arguments);
-
- event.currentTarget || (event.currentTarget = me);
- event.target || (event.target = me);
- delete event.result;
-
- if (events && (listeners = hasKey(events, event.type) && events[event.type])) {
- forEach(listeners.slice(), function(listener) {
- if ((event.result = listener.apply(me, args)) === false) {
- event.cancelled = true;
+ /**
+ * Fetches the next bench or executes `onComplete` callback.
+ */
+ function getNext(event) {
+ var cycleEvent,
+ last = bench,
+ async = isAsync(last);
+
+ if (async) {
+ last.off('complete', getNext);
+ last.emit('complete');
}
- return !event.aborted;
- });
- }
- return event.result;
- }
-
- /**
- * Returns an array of event listeners for a given type that can be manipulated
- * to add or remove listeners.
- *
- * @memberOf Benchmark, Benchmark.Suite
- * @param {String} type The event type.
- * @returns {Array} The listeners array.
- */
- function listeners(type) {
- var me = this,
- events = me.events || (me.events = {});
-
- return hasKey(events, type) ? events[type] : (events[type] = []);
- }
-
- /**
- * Unregisters a listener for the specified event type(s),
- * or unregisters all listeners for the specified event type(s),
- * or unregisters all listeners for all event types.
- *
- * @memberOf Benchmark, Benchmark.Suite
- * @param {String} [type] The event type.
- * @param {Function} [listener] The function to unregister.
- * @returns {Object} The benchmark instance.
- * @example
- *
- * // unregister a listener for an event type
- * bench.off('cycle', listener);
- *
- * // unregister a listener for multiple event types
- * bench.off('start cycle', listener);
- *
- * // unregister all listeners for an event type
- * bench.off('cycle');
- *
- * // unregister all listeners for multiple event types
- * bench.off('start cycle complete');
- *
- * // unregister all listeners for all event types
- * bench.off();
- */
- function off(type, listener) {
- var me = this,
- events = me.events;
-
- events && each(type ? type.split(' ') : events, function(listeners, type) {
- var index;
- if (typeof listeners == 'string') {
- type = listeners;
- listeners = hasKey(events, type) && events[type];
- }
- if (listeners) {
- if (listener) {
- index = indexOf(listeners, listener);
- if (index > -1) {
- listeners.splice(index, 1);
+ // emit "cycle" event
+ eventProps.type = 'cycle';
+ eventProps.target = last;
+ cycleEvent = Event(eventProps);
+ options.onCycle.call(benches, cycleEvent);
+
+ // choose next benchmark if not exiting early
+ if (!cycleEvent.aborted && raiseIndex() !== false) {
+ bench = queued ? benches[0] : result[index];
+ if (isAsync(bench)) {
+ delay(bench, execute);
+ }
+ else if (async) {
+ // resume execution if previously asynchronous but now synchronous
+ while (execute()) { }
+ }
+ else {
+ // continue synchronous execution
+ return true;
}
} else {
- listeners.length = 0;
- }
- }
- });
- return me;
- }
-
- /**
- * Registers a listener for the specified event type(s).
- *
- * @memberOf Benchmark, Benchmark.Suite
- * @param {String} type The event type.
- * @param {Function} listener The function to register.
- * @returns {Object} The benchmark instance.
- * @example
- *
- * // register a listener for an event type
- * bench.on('cycle', listener);
- *
- * // register a listener for multiple event types
- * bench.on('start cycle', listener);
- */
- function on(type, listener) {
- var me = this,
- events = me.events || (me.events = {});
-
- forEach(type.split(' '), function(type) {
- (hasKey(events, type)
- ? events[type]
- : (events[type] = [])
- ).push(listener);
- });
- return me;
- }
-
- /*--------------------------------------------------------------------------*/
-
- /**
- * Aborts the benchmark without recording times.
- *
- * @memberOf Benchmark
- * @returns {Object} The benchmark instance.
- */
- function abort() {
- var event,
- me = this,
- resetting = calledBy.reset;
-
- if (me.running) {
- event = Event('abort');
- me.emit(event);
- if (!event.cancelled || resetting) {
- // avoid infinite recursion
- calledBy.abort = true;
- me.reset();
- delete calledBy.abort;
-
- if (support.timeout) {
- clearTimeout(me._timerId);
- delete me._timerId;
+ // emit "complete" event
+ eventProps.type = 'complete';
+ options.onComplete.call(benches, Event(eventProps));
}
- if (!resetting) {
- me.aborted = true;
- me.running = false;
+ // When used as a listener `event.aborted = true` will cancel the rest of
+ // the "complete" listeners because they were already called above and when
+ // used as part of `getNext` the `return false` will exit the execution while-loop.
+ if (event) {
+ event.aborted = true;
+ } else {
+ return false;
}
}
- }
- return me;
- }
-
- /**
- * Creates a new benchmark using the same test and options.
- *
- * @memberOf Benchmark
- * @param {Object} options Options object to overwrite cloned options.
- * @returns {Object} The new benchmark instance.
- * @example
- *
- * var bizarro = bench.clone({
- * 'name': 'doppelganger'
- * });
- */
- function clone(options) {
- var me = this,
- result = new me.constructor(extend({}, me, options));
-
- // correct the `options` object
- result.options = extend({}, me.options, options);
-
- // copy own custom properties
- forOwn(me, function(value, key) {
- if (!hasKey(result, key)) {
- result[key] = deepClone(value);
- }
- });
- return result;
- }
-
- /**
- * Determines if a benchmark is faster than another.
- *
- * @memberOf Benchmark
- * @param {Object} other The benchmark to compare.
- * @returns {Number} Returns `-1` if slower, `1` if faster, and `0` if indeterminate.
- */
- function compare(other) {
- var critical,
- zStat,
- me = this,
- sample1 = me.stats.sample,
- sample2 = other.stats.sample,
- size1 = sample1.length,
- size2 = sample2.length,
- maxSize = max(size1, size2),
- minSize = min(size1, size2),
- u1 = getU(sample1, sample2),
- u2 = getU(sample2, sample1),
- u = min(u1, u2);
-
- function getScore(xA, sampleB) {
- return reduce(sampleB, function(total, xB) {
- return total + (xB > xA ? 0 : xB < xA ? 1 : 0.5);
- }, 0);
- }
-
- function getU(sampleA, sampleB) {
- return reduce(sampleA, function(total, xA) {
- return total + getScore(xA, sampleB);
- }, 0);
- }
-
- function getZ(u) {
- return (u - ((size1 * size2) / 2)) / sqrt((size1 * size2 * (size1 + size2 + 1)) / 12);
- }
-
- // exit early if comparing the same benchmark
- if (me == other) {
- return 0;
- }
- // reject the null hyphothesis the two samples come from the
- // same population (i.e. have the same median) if...
- if (size1 + size2 > 30) {
- // ...the z-stat is greater than 1.96 or less than -1.96
- // http://www.statisticslectures.com/topics/mannwhitneyu/
- zStat = getZ(u);
- return abs(zStat) > 1.96 ? (zStat > 0 ? -1 : 1) : 0;
- }
- // ...the U value is less than or equal the critical U value
- // http://www.geoib.com/mann-whitney-u-test.html
- critical = maxSize < 5 || minSize < 3 ? 0 : uTable[maxSize][minSize - 3];
- return u <= critical ? (u == u1 ? 1 : -1) : 0;
- }
-
- /**
- * Reset properties and abort if running.
- *
- * @memberOf Benchmark
- * @returns {Object} The benchmark instance.
- */
- function reset() {
- var data,
- event,
- me = this,
- index = 0,
- changes = { 'length': 0 },
- queue = { 'length': 0 };
-
- if (me.running && !calledBy.abort) {
- // no worries, `reset()` is called within `abort()`
- calledBy.reset = true;
- me.abort();
- delete calledBy.reset;
- }
- else {
- // a non-recursive solution to check if properties have changed
- // http://www.jslab.dk/articles/non.recursive.preorder.traversal.part4
- data = { 'destination': me, 'source': extend({}, me.constructor.prototype, me.options) };
- do {
- forOwn(data.source, function(value, key) {
- var changed,
- destination = data.destination,
- currValue = destination[key];
-
- if (value && typeof value == 'object') {
- if (isClassOf(value, 'Array')) {
- // check if an array value has changed to a non-array value
- if (!isClassOf(currValue, 'Array')) {
- changed = currValue = [];
- }
- // or has changed its length
- if (currValue.length != value.length) {
- changed = currValue = currValue.slice(0, value.length);
- currValue.length = value.length;
- }
- }
- // check if an object has changed to a non-object value
- else if (!currValue || typeof currValue != 'object') {
- changed = currValue = {};
- }
- // register a changed object
- if (changed) {
- changes[changes.length++] = { 'destination': destination, 'key': key, 'value': currValue };
- }
- queue[queue.length++] = { 'destination': currValue, 'source': value };
- }
- // register a changed primitive
- else if (value !== currValue && !(value == null || isClassOf(value, 'Function'))) {
- changes[changes.length++] = { 'destination': destination, 'key': key, 'value': value };
- }
- });
- }
- while ((data = queue[index++]));
-
- // if changed emit the `reset` event and if it isn't cancelled reset the benchmark
- if (changes.length && (me.emit(event = Event('reset')), !event.cancelled)) {
- forEach(changes, function(data) {
- data.destination[data.key] = data.value;
- });
- }
- }
- return me;
- }
-
- /**
- * Displays relevant benchmark information when coerced to a string.
- *
- * @name toString
- * @memberOf Benchmark
- * @returns {String} A string representation of the benchmark instance.
- */
- function toStringBench() {
- var me = this,
- error = me.error,
- hz = me.hz,
- id = me.id,
- stats = me.stats,
- size = stats.sample.length,
- pm = support.java ? '+/-' : '\xb1',
- result = me.name || (isNaN(id) ? id : '');
-
- if (error) {
- result += ': ' + join(error);
- } else {
- result += ' x ' + formatNumber(hz.toFixed(hz < 100 ? 2 : 0)) + ' ops/sec ' + pm +
- stats.rme.toFixed(2) + '% (' + size + ' run' + (size == 1 ? '' : 's') + ' sampled)';
- }
- return result;
- }
-
- /*--------------------------------------------------------------------------*/
-
- /**
- * Clocks the time taken to execute a test per cycle (secs).
- *
- * @private
- * @param {Object} bench The benchmark instance.
- * @returns {Number} The time taken.
- */
- function clock() {
- var applet,
- options = Benchmark.options,
- template = { 'begin': 's$=new n$', 'end': 'r$=(new n$-s$)/1e3', 'uid': uid },
- timers = [{ 'ns': timer.ns, 'res': max(0.0015, getRes('ms')), 'unit': 'ms' }];
-
- // lazy define for hi-res timers
- clock = function(clone) {
- var deferred;
- if (clone instanceof Deferred) {
- deferred = clone;
- clone = deferred.benchmark;
- }
-
- var bench = clone._original,
- fn = bench.fn,
- fnArg = deferred ? getFirstArgument(fn) || 'deferred' : '',
- stringable = isStringable(fn);
-
- var source = {
- 'setup': getSource(bench.setup, preprocess('m$.setup()')),
- 'fn': getSource(fn, preprocess('m$.fn(' + fnArg + ')')),
- 'fnArg': fnArg,
- 'teardown': getSource(bench.teardown, preprocess('m$.teardown()'))
- };
-
- var count = bench.count = clone.count,
- decompilable = support.decompilation || stringable,
- id = bench.id,
- isEmpty = !(source.fn || stringable),
- name = bench.name || (typeof id == 'number' ? '' : id),
- ns = timer.ns,
- result = 0;
-
- // init `minTime` if needed
- clone.minTime = bench.minTime || (bench.minTime = bench.options.minTime = options.minTime);
- // repair nanosecond timer
- // (some Chrome builds erase the `ns` variable after millions of executions)
- if (applet) {
- try {
- ns.nanoTime();
- } catch(e) {
- // use non-element to avoid issues with libs that augment them
- ns = timer.ns = new applet.Packages.nano;
- }
+ /**
+ * Checks if invoking `Benchmark#run` with asynchronous cycles.
+ */
+ function isAsync(object) {
+ // avoid using `instanceof` here because of IE memory leak issues with host objects
+ var async = args[0] && args[0].async;
+ return Object(object).constructor == Benchmark && name == 'run' &&
+ ((async == null ? object.options.async : async) && support.timeout || object.defer);
}
- // Compile in setup/teardown functions and the test loop.
- // Create a new compiled test, instead of using the cached `bench.compiled`,
- // to avoid potential engine optimizations enabled over the life of the test.
- var compiled = bench.compiled = createFunction(preprocess('t$'), interpolate(
- preprocess(deferred
- ? 'var d$=this,#{fnArg}=d$,m$=d$.benchmark._original,f$=m$.fn,su$=m$.setup,td$=m$.teardown;' +
- // when `deferred.cycles` is `0` then...
- 'if(!d$.cycles){' +
- // set `deferred.fn`
- 'd$.fn=function(){var #{fnArg}=d$;if(typeof f$=="function"){try{#{fn}\n}catch(e$){f$(d$)}}else{#{fn}\n}};' +
- // set `deferred.teardown`
- 'd$.teardown=function(){d$.cycles=0;if(typeof td$=="function"){try{#{teardown}\n}catch(e$){td$()}}else{#{teardown}\n}};' +
- // execute the benchmark's `setup`
- 'if(typeof su$=="function"){try{#{setup}\n}catch(e$){su$()}}else{#{setup}\n};' +
- // start timer
- 't$.start(d$);' +
- // execute `deferred.fn` and return a dummy object
- '}d$.fn();return{}'
-
- : 'var r$,s$,m$=this,f$=m$.fn,i$=m$.count,n$=t$.ns;#{setup}\n#{begin};' +
- 'while(i$--){#{fn}\n}#{end};#{teardown}\nreturn{elapsed:r$,uid:"#{uid}"}'),
- source
- ));
-
- try {
- if (isEmpty) {
- // Firefox may remove dead code from Function#toString results
- // http://bugzil.la/536085
- throw new Error('The test "' + name + '" is empty. This may be the result of dead code removal.');
- }
- else if (!deferred) {
- // pretest to determine if compiled code is exits early, usually by a
- // rogue `return` statement, by checking for a return object with the uid
- bench.count = 1;
- compiled = (compiled.call(bench, timer) || {}).uid == uid && compiled;
- bench.count = count;
- }
- } catch(e) {
- compiled = null;
- clone.error = e || new Error(String(e));
- bench.count = count;
- }
- // fallback when a test exits early or errors during pretest
- if (decompilable && !compiled && !deferred && !isEmpty) {
- compiled = createFunction(preprocess('t$'), interpolate(
- preprocess(
- (clone.error && !stringable
- ? 'var r$,s$,m$=this,f$=m$.fn,i$=m$.count'
- : 'function f$(){#{fn}\n}var r$,s$,m$=this,i$=m$.count'
- ) +
- ',n$=t$.ns;#{setup}\n#{begin};m$.f$=f$;while(i$--){m$.f$()}#{end};' +
- 'delete m$.f$;#{teardown}\nreturn{elapsed:r$}'
- ),
- source
- ));
+ /**
+ * Raises `index` to the next defined index or returns `false`.
+ */
+ function raiseIndex() {
+ index++;
- try {
- // pretest one more time to check for errors
- bench.count = 1;
- compiled.call(bench, timer);
- bench.compiled = compiled;
- bench.count = count;
- delete clone.error;
- }
- catch(e) {
- bench.count = count;
- if (clone.error) {
- compiled = null;
- } else {
- bench.compiled = compiled;
- clone.error = e || new Error(String(e));
- }
+ // if queued remove the previous bench
+ if (queued && index > 0) {
+ shift.call(benches);
}
+ // if we reached the last index then return `false`
+ return (queued ? benches.length : index < result.length)
+ ? index
+ : (index = false);
}
- // assign `compiled` to `clone` before calling in case a deferred benchmark
- // immediately calls `deferred.resolve()`
- clone.compiled = compiled;
- // if no errors run the full test loop
- if (!clone.error) {
- result = compiled.call(deferred || bench, timer).elapsed;
- }
- return result;
- };
-
- /*------------------------------------------------------------------------*/
- /**
- * Gets the current timer's minimum resolution (secs).
- */
- function getRes(unit) {
- var measured,
- begin,
- count = 30,
- divisor = 1e3,
- ns = timer.ns,
- sample = [];
-
- // get average smallest measurable time
- while (count--) {
- if (unit == 'us') {
- divisor = 1e6;
- if (ns.stop) {
- ns.start();
- while (!(measured = ns.microseconds())) { }
- } else if (ns[perfName]) {
- divisor = 1e3;
- measured = Function('n', 'var r,s=n.' + perfName + '();while(!(r=n.' + perfName + '()-s)){};return r')(ns);
- } else {
- begin = ns();
- while (!(measured = ns() - begin)) { }
- }
+ // juggle arguments
+ if (_.isString(name)) {
+ // 2 arguments (array, name)
+ args = slice.call(arguments, 2);
+ } else {
+ // 2 arguments (array, options)
+ options = _.extend(options, name);
+ name = options.name;
+ args = _.isArray(args = 'args' in options ? options.args : []) ? args : [args];
+ queued = options.queued;
+ }
+
+ // start iterating over the array
+ if (raiseIndex() !== false) {
+ // emit "start" event
+ bench = result[index];
+ eventProps.type = 'start';
+ eventProps.target = bench;
+ options.onStart.call(benches, Event(eventProps));
+
+ // end early if the suite was aborted in an "onStart" listener
+ if (benches.aborted && benches.constructor == Suite && name == 'run') {
+ // emit "cycle" event
+ eventProps.type = 'cycle';
+ options.onCycle.call(benches, Event(eventProps));
+ // emit "complete" event
+ eventProps.type = 'complete';
+ options.onComplete.call(benches, Event(eventProps));
}
- else if (unit == 'ns') {
- divisor = 1e9;
- if (ns.nanoTime) {
- begin = ns.nanoTime();
- while (!(measured = ns.nanoTime() - begin)) { }
+ // else start
+ else {
+ if (isAsync(bench)) {
+ delay(bench, execute);
} else {
- begin = (begin = ns())[0] + (begin[1] / divisor);
- while (!(measured = ((measured = ns())[0] + (measured[1] / divisor)) - begin)) { }
- divisor = 1;
+ while (execute()) { }
}
}
- else {
- begin = new ns;
- while (!(measured = new ns - begin)) { }
- }
- // check for broken timers (nanoTime may have issues)
- // http://alivebutsleepy.srnet.cz/unreliable-system-nanotime/
- if (measured > 0) {
- sample.push(measured);
- } else {
- sample.push(Infinity);
- break;
- }
}
- // convert to seconds
- return getMean(sample) / divisor;
+ return result;
}
/**
- * Replaces all occurrences of `$` with a unique number and
- * template tokens with content.
- */
- function preprocess(code) {
- return interpolate(code, template).replace(/\$/g, /\d+/.exec(uid));
+ * Creates a string of joined array values or object key-value pairs.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @param {Array|Object} object The object to operate on.
+ * @param {String} [separator1=','] The separator used between key-value pairs.
+ * @param {String} [separator2=': '] The separator used between keys and values.
+ * @returns {String} The joined result.
+ */
+ function join(object, separator1, separator2) {
+ var result = [],
+ length = (object = Object(object)).length,
+ arrayLike = length === length >>> 0;
+
+ separator2 || (separator2 = ': ');
+ _.each(object, function(value, key) {
+ result.push(arrayLike ? value : key + separator2 + value);
+ });
+ return result.join(separator1 || ',');
}
/*------------------------------------------------------------------------*/
- // detect nanosecond support from a Java applet
- each(doc && doc.applets || [], function(element) {
- return !(timer.ns = applet = 'nanoTime' in element && element);
- });
-
- // check type in case Safari returns an object instead of a number
- try {
- if (typeof timer.ns.nanoTime() == 'number') {
- timers.push({ 'ns': timer.ns, 'res': getRes('ns'), 'unit': 'ns' });
- }
- } catch(e) { }
-
- // detect Chrome's microsecond timer:
- // enable benchmarking via the --enable-benchmarking command
- // line switch in at least Chrome 7 to use chrome.Interval
- try {
- if ((timer.ns = new (window.chrome || window.chromium).Interval)) {
- timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' });
+ /**
+ * Aborts all benchmarks in the suite.
+ *
+ * @name abort
+ * @memberOf Benchmark.Suite
+ * @returns {Object} The suite instance.
+ */
+ function abortSuite() {
+ var event,
+ suite = this,
+ resetting = calledBy.resetSuite;
+
+ if (suite.running) {
+ event = Event('abort');
+ suite.emit(event);
+ if (!event.cancelled || resetting) {
+ // avoid infinite recursion
+ calledBy.abortSuite = true;
+ suite.reset();
+ delete calledBy.abortSuite;
+
+ if (!resetting) {
+ suite.aborted = true;
+ invoke(suite, 'abort');
+ }
+ }
}
- } catch(e) { }
-
- // detect `performance.now` microsecond resolution timer
- if ((timer.ns = perfName && perfObject)) {
- timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' });
+ return suite;
}
- // detect Node's nanosecond resolution timer available in Node >= 0.8
- if (processObject && typeof (timer.ns = processObject.hrtime) == 'function') {
- timers.push({ 'ns': timer.ns, 'res': getRes('ns'), 'unit': 'ns' });
+ /**
+ * Adds a test to the benchmark suite.
+ *
+ * @memberOf Benchmark.Suite
+ * @param {String} name A name to identify the benchmark.
+ * @param {Function|String} fn The test to benchmark.
+ * @param {Object} [options={}] Options object.
+ * @returns {Object} The benchmark instance.
+ * @example
+ *
+ * // basic usage
+ * suite.add(fn);
+ *
+ * // or using a name first
+ * suite.add('foo', fn);
+ *
+ * // or with options
+ * suite.add('foo', fn, {
+ * 'onCycle': onCycle,
+ * 'onComplete': onComplete
+ * });
+ *
+ * // or name and options
+ * suite.add('foo', {
+ * 'fn': fn,
+ * 'onCycle': onCycle,
+ * 'onComplete': onComplete
+ * });
+ *
+ * // or options only
+ * suite.add({
+ * 'name': 'foo',
+ * 'fn': fn,
+ * 'onCycle': onCycle,
+ * 'onComplete': onComplete
+ * });
+ */
+ function add(name, fn, options) {
+ var suite = this,
+ bench = new Benchmark(name, fn, options),
+ event = Event({ 'type': 'add', 'target': bench });
+
+ if (suite.emit(event), !event.cancelled) {
+ suite.push(bench);
+ }
+ return suite;
}
- // detect Wade Simmons' Node microtime module
- if (microtimeObject && typeof (timer.ns = microtimeObject.now) == 'function') {
- timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' });
+ /**
+ * Creates a new suite with cloned benchmarks.
+ *
+ * @name clone
+ * @memberOf Benchmark.Suite
+ * @param {Object} options Options object to overwrite cloned options.
+ * @returns {Object} The new suite instance.
+ */
+ function cloneSuite(options) {
+ var suite = this,
+ result = new suite.constructor(_.extend({}, suite.options, options));
+
+ // copy own properties
+ _.forOwn(suite, function(value, key) {
+ if (!_.has(result, key)) {
+ result[key] = value && _.isFunction(value.clone)
+ ? value.clone()
+ : cloneDeep(value);
+ }
+ });
+ return result;
}
- // pick timer with highest resolution
- timer = reduce(timers, function(timer, other) {
- return other.res < timer.res ? other : timer;
- });
+ /**
+ * An `Array#filter` like method.
+ *
+ * @name filter
+ * @memberOf Benchmark.Suite
+ * @param {Function|String} callback The function/alias called per iteration.
+ * @returns {Object} A new suite of benchmarks that passed callback filter.
+ */
+ function filterSuite(callback) {
+ var suite = this,
+ result = new suite.constructor;
- // remove unused applet
- if (timer.unit != 'ns' && applet) {
- applet = destroyElement(applet);
- }
- // error if there are no working timers
- if (timer.res == Infinity) {
- throw new Error('Benchmark.js was unable to find a working timer.');
+ result.push.apply(result, filter(suite, callback));
+ return result;
}
- // use API of chosen timer
- if (timer.unit == 'ns') {
- if (timer.ns.nanoTime) {
- extend(template, {
- 'begin': 's$=n$.nanoTime()',
- 'end': 'r$=(n$.nanoTime()-s$)/1e9'
- });
- } else {
- extend(template, {
- 'begin': 's$=n$()',
- 'end': 'r$=n$(s$);r$=r$[0]+(r$[1]/1e9)'
- });
+
+ /**
+ * Resets all benchmarks in the suite.
+ *
+ * @name reset
+ * @memberOf Benchmark.Suite
+ * @returns {Object} The suite instance.
+ */
+ function resetSuite() {
+ var event,
+ suite = this,
+ aborting = calledBy.abortSuite;
+
+ if (suite.running && !aborting) {
+ // no worries, `resetSuite()` is called within `abortSuite()`
+ calledBy.resetSuite = true;
+ suite.abort();
+ delete calledBy.resetSuite;
}
- }
- else if (timer.unit == 'us') {
- if (timer.ns.stop) {
- extend(template, {
- 'begin': 's$=n$.start()',
- 'end': 'r$=n$.microseconds()/1e6'
- });
- } else if (perfName) {
- extend(template, {
- 'begin': 's$=n$.' + perfName + '()',
- 'end': 'r$=(n$.' + perfName + '()-s$)/1e3'
- });
- } else {
- extend(template, {
- 'begin': 's$=n$()',
- 'end': 'r$=(n$()-s$)/1e6'
- });
+ // reset if the state has changed
+ else if ((suite.aborted || suite.running) &&
+ (suite.emit(event = Event('reset')), !event.cancelled)) {
+ suite.running = false;
+ if (!aborting) {
+ invoke(suite, 'reset');
+ }
}
+ return suite;
}
- // define `timer` methods
- timer.start = createFunction(preprocess('o$'),
- preprocess('var n$=this.ns,#{begin};o$.elapsed=0;o$.timeStamp=s$'));
-
- timer.stop = createFunction(preprocess('o$'),
- preprocess('var n$=this.ns,s$=o$.timeStamp,#{end};o$.elapsed=r$'));
+ /**
+ * Runs the suite.
+ *
+ * @name run
+ * @memberOf Benchmark.Suite
+ * @param {Object} [options={}] Options object.
+ * @returns {Object} The suite instance.
+ * @example
+ *
+ * // basic usage
+ * suite.run();
+ *
+ * // or with options
+ * suite.run({ 'async': true, 'queued': true });
+ */
+ function runSuite(options) {
+ var suite = this;
- // resolve time span required to achieve a percent uncertainty of at most 1%
- // http://spiff.rit.edu/classes/phys273/uncert/uncert.html
- options.minTime || (options.minTime = max(timer.res / 2 / 0.01, 0.05));
- return clock.apply(null, arguments);
- }
+ suite.reset();
+ suite.running = true;
+ options || (options = {});
- /*--------------------------------------------------------------------------*/
+ invoke(suite, {
+ 'name': 'run',
+ 'args': options,
+ 'queued': options.queued,
+ 'onStart': function(event) {
+ suite.emit(event);
+ },
+ 'onCycle': function(event) {
+ var bench = event.target;
+ if (bench.error) {
+ suite.emit({ 'type': 'error', 'target': bench });
+ }
+ suite.emit(event);
+ event.aborted = suite.aborted;
+ },
+ 'onComplete': function(event) {
+ suite.running = false;
+ suite.emit(event);
+ }
+ });
+ return suite;
+ }
- /**
- * Computes stats on benchmark results.
- *
- * @private
- * @param {Object} bench The benchmark instance.
- * @param {Object} options The options object.
- */
- function compute(bench, options) {
- options || (options = {});
+ /*------------------------------------------------------------------------*/
- var async = options.async,
- elapsed = 0,
- initCount = bench.initCount,
- minSamples = bench.minSamples,
- queue = [],
- sample = bench.stats.sample;
+ /**
+ * Executes all registered listeners of the specified event type.
+ *
+ * @memberOf Benchmark, Benchmark.Suite
+ * @param {String|Object} type The event type or object.
+ * @returns {Mixed} Returns the return value of the last listener executed.
+ */
+ function emit(type) {
+ var listeners,
+ object = this,
+ event = Event(type),
+ events = object.events,
+ args = (arguments[0] = event, arguments);
+
+ event.currentTarget || (event.currentTarget = object);
+ event.target || (event.target = object);
+ delete event.result;
+
+ if (events && (listeners = _.has(events, event.type) && events[event.type])) {
+ _.each(listeners.slice(), function(listener) {
+ if ((event.result = listener.apply(object, args)) === false) {
+ event.cancelled = true;
+ }
+ return !event.aborted;
+ });
+ }
+ return event.result;
+ }
/**
- * Adds a clone to the queue.
+ * Returns an array of event listeners for a given type that can be manipulated
+ * to add or remove listeners.
+ *
+ * @memberOf Benchmark, Benchmark.Suite
+ * @param {String} type The event type.
+ * @returns {Array} The listeners array.
*/
- function enqueue() {
- queue.push(bench.clone({
- '_original': bench,
- 'events': {
- 'abort': [update],
- 'cycle': [update],
- 'error': [update],
- 'start': [update]
- }
- }));
+ function listeners(type) {
+ var object = this,
+ events = object.events || (object.events = {});
+
+ return _.has(events, type) ? events[type] : (events[type] = []);
}
/**
- * Updates the clone/original benchmarks to keep their data in sync.
+ * Unregisters a listener for the specified event type(s),
+ * or unregisters all listeners for the specified event type(s),
+ * or unregisters all listeners for all event types.
+ *
+ * @memberOf Benchmark, Benchmark.Suite
+ * @param {String} [type] The event type.
+ * @param {Function} [listener] The function to unregister.
+ * @returns {Object} The benchmark instance.
+ * @example
+ *
+ * // unregister a listener for an event type
+ * bench.off('cycle', listener);
+ *
+ * // unregister a listener for multiple event types
+ * bench.off('start cycle', listener);
+ *
+ * // unregister all listeners for an event type
+ * bench.off('cycle');
+ *
+ * // unregister all listeners for multiple event types
+ * bench.off('start cycle complete');
+ *
+ * // unregister all listeners for all event types
+ * bench.off();
*/
- function update(event) {
- var clone = this,
- type = event.type;
+ function off(type, listener) {
+ var object = this,
+ events = object.events;
- if (bench.running) {
- if (type == 'start') {
- // Note: `clone.minTime` prop is inited in `clock()`
- clone.count = bench.initCount;
+ if (!events) {
+ return object;
+ }
+ _.each(type ? type.split(' ') : events, function(listeners, type) {
+ var index;
+ if (typeof listeners == 'string') {
+ type = listeners;
+ listeners = _.has(events, type) && events[type];
}
- else {
- if (type == 'error') {
- bench.error = clone.error;
- }
- if (type == 'abort') {
- bench.abort();
- bench.emit('cycle');
+ if (listeners) {
+ if (listener) {
+ index = _.indexOf(listeners, listener);
+ if (index > -1) {
+ listeners.splice(index, 1);
+ }
} else {
- event.currentTarget = event.target = bench;
- bench.emit(event);
+ listeners.length = 0;
}
}
- } else if (bench.aborted) {
- // clear abort listeners to avoid triggering bench's abort/cycle again
- clone.events.abort.length = 0;
- clone.abort();
- }
+ });
+ return object;
}
/**
- * Determines if more clones should be queued or if cycling should stop.
- */
- function evaluate(event) {
- var critical,
- df,
- mean,
- moe,
- rme,
- sd,
- sem,
- variance,
- clone = event.target,
- done = bench.aborted,
- now = +new Date,
- size = sample.push(clone.times.period),
- maxedOut = size >= minSamples && (elapsed += now - clone.times.timeStamp) / 1e3 > bench.maxTime,
- times = bench.times,
- varOf = function(sum, x) { return sum + pow(x - mean, 2); };
-
- // exit early for aborted or unclockable tests
- if (done || clone.hz == Infinity) {
- maxedOut = !(size = sample.length = queue.length = 0);
- }
+ * Registers a listener for the specified event type(s).
+ *
+ * @memberOf Benchmark, Benchmark.Suite
+ * @param {String} type The event type.
+ * @param {Function} listener The function to register.
+ * @returns {Object} The benchmark instance.
+ * @example
+ *
+ * // register a listener for an event type
+ * bench.on('cycle', listener);
+ *
+ * // register a listener for multiple event types
+ * bench.on('start cycle', listener);
+ */
+ function on(type, listener) {
+ var object = this,
+ events = object.events || (object.events = {});
+
+ _.each(type.split(' '), function(type) {
+ (_.has(events, type)
+ ? events[type]
+ : (events[type] = [])
+ ).push(listener);
+ });
+ return object;
+ }
- if (!done) {
- // sample mean (estimate of the population mean)
- mean = getMean(sample);
- // sample variance (estimate of the population variance)
- variance = reduce(sample, varOf, 0) / (size - 1) || 0;
- // sample standard deviation (estimate of the population standard deviation)
- sd = sqrt(variance);
- // standard error of the mean (a.k.a. the standard deviation of the sampling distribution of the sample mean)
- sem = sd / sqrt(size);
- // degrees of freedom
- df = size - 1;
- // critical value
- critical = tTable[Math.round(df) || 1] || tTable.infinity;
- // margin of error
- moe = sem * critical;
- // relative margin of error
- rme = (moe / mean) * 100 || 0;
-
- extend(bench.stats, {
- 'deviation': sd,
- 'mean': mean,
- 'moe': moe,
- 'rme': rme,
- 'sem': sem,
- 'variance': variance
- });
+ /*------------------------------------------------------------------------*/
- // Abort the cycle loop when the minimum sample size has been collected
- // and the elapsed time exceeds the maximum time allowed per benchmark.
- // We don't count cycle delays toward the max time because delays may be
- // increased by browsers that clamp timeouts for inactive tabs.
- // https://developer.mozilla.org/en/window.setTimeout#Inactive_tabs
- if (maxedOut) {
- // reset the `initCount` in case the benchmark is rerun
- bench.initCount = initCount;
- bench.running = false;
- done = true;
- times.elapsed = (now - times.timeStamp) / 1e3;
- }
- if (bench.hz != Infinity) {
- bench.hz = 1 / mean;
- times.cycle = mean * bench.count;
- times.period = mean;
+ /**
+ * Aborts the benchmark without recording times.
+ *
+ * @memberOf Benchmark
+ * @returns {Object} The benchmark instance.
+ */
+ function abort() {
+ var event,
+ bench = this,
+ resetting = calledBy.reset;
+
+ if (bench.running) {
+ event = Event('abort');
+ bench.emit(event);
+ if (!event.cancelled || resetting) {
+ // avoid infinite recursion
+ calledBy.abort = true;
+ bench.reset();
+ delete calledBy.abort;
+
+ if (support.timeout) {
+ clearTimeout(bench._timerId);
+ delete bench._timerId;
+ }
+ if (!resetting) {
+ bench.aborted = true;
+ bench.running = false;
+ }
}
}
- // if time permits, increase sample size to reduce the margin of error
- if (queue.length < 2 && !maxedOut) {
- enqueue();
- }
- // abort the invoke cycle when done
- event.aborted = done;
+ return bench;
}
- // init queue and begin
- enqueue();
- invoke(queue, {
- 'name': 'run',
- 'args': { 'async': async },
- 'queued': true,
- 'onCycle': evaluate,
- 'onComplete': function() { bench.emit('complete'); }
- });
- }
+ /**
+ * Creates a new benchmark using the same test and options.
+ *
+ * @memberOf Benchmark
+ * @param {Object} options Options object to overwrite cloned options.
+ * @returns {Object} The new benchmark instance.
+ * @example
+ *
+ * var bizarro = bench.clone({
+ * 'name': 'doppelganger'
+ * });
+ */
+ function clone(options) {
+ var bench = this,
+ sample = bench.stats.sample,
+ result = new bench.constructor(_.extend({}, bench, options));
- /*--------------------------------------------------------------------------*/
+ // correct the `options` object
+ result.options = _.extend({}, bench.options, options);
- /**
- * Cycles a benchmark until a run `count` can be established.
- *
- * @private
- * @param {Object} clone The cloned benchmark instance.
- * @param {Object} options The options object.
- */
- function cycle(clone, options) {
- options || (options = {});
+ // copy own custom properties
+ _.forOwn(bench, function(value, key) {
+ if (!_.has(result, key)) {
+ result[key] = cloneDeep(value);
+ }
+ });
- var deferred;
- if (clone instanceof Deferred) {
- deferred = clone;
- clone = clone.benchmark;
+ return result;
}
- var clocked,
- cycles,
- divisor,
- event,
- minTime,
- period,
- async = options.async,
- bench = clone._original,
- count = clone.count,
- times = clone.times;
-
- // continue, if not aborted between cycles
- if (clone.running) {
- // `minTime` is set to `Benchmark.options.minTime` in `clock()`
- cycles = ++clone.cycles;
- clocked = deferred ? deferred.elapsed : clock(clone);
- minTime = clone.minTime;
-
- if (cycles > bench.cycles) {
- bench.cycles = cycles;
- }
- if (clone.error) {
- event = Event('error');
- event.message = clone.error;
- clone.emit(event);
- if (!event.cancelled) {
- clone.abort();
- }
+ /**
+ * Determines if a benchmark is faster than another.
+ *
+ * @memberOf Benchmark
+ * @param {Object} other The benchmark to compare.
+ * @returns {Number} Returns `-1` if slower, `1` if faster, and `0` if indeterminate.
+ */
+ function compare(other) {
+ var critical,
+ zStat,
+ bench = this,
+ sample1 = bench.stats.sample,
+ sample2 = other.stats.sample,
+ size1 = sample1.length,
+ size2 = sample2.length,
+ maxSize = max(size1, size2),
+ minSize = min(size1, size2),
+ u1 = getU(sample1, sample2),
+ u2 = getU(sample2, sample1),
+ u = min(u1, u2);
+
+ function getScore(xA, sampleB) {
+ return _.reduce(sampleB, function(total, xB) {
+ return total + (xB > xA ? 0 : xB < xA ? 1 : 0.5);
+ }, 0);
}
- }
- // continue, if not errored
- if (clone.running) {
- // time taken to complete last test cycle
- bench.times.cycle = times.cycle = clocked;
- // seconds per operation
- period = bench.times.period = times.period = clocked / count;
- // ops per second
- bench.hz = clone.hz = 1 / period;
- // avoid working our way up to this next time
- bench.initCount = clone.initCount = count;
- // do we need to do another cycle?
- clone.running = clocked < minTime;
-
- if (clone.running) {
- // tests may clock at `0` when `initCount` is a small number,
- // to avoid that we set its count to something a bit higher
- if (!clocked && (divisor = divisors[clone.cycles]) != null) {
- count = floor(4e6 / divisor);
- }
- // calculate how many more iterations it will take to achive the `minTime`
- if (count <= clone.count) {
- count += Math.ceil((minTime - clocked) / period);
- }
- clone.running = count != Infinity;
- }
- }
- // should we exit early?
- event = Event('cycle');
- clone.emit(event);
- if (event.aborted) {
- clone.abort();
- }
- // figure out what to do next
- if (clone.running) {
- // start a new cycle
- clone.count = count;
- if (deferred) {
- clone.compiled.call(deferred, timer);
- } else if (async) {
- delay(clone, function() { cycle(clone, options); });
- } else {
- cycle(clone);
+ function getU(sampleA, sampleB) {
+ return _.reduce(sampleA, function(total, xA) {
+ return total + getScore(xA, sampleB);
+ }, 0);
}
- }
- else {
- // fix TraceMonkey bug associated with clock fallbacks
- // http://bugzil.la/509069
- if (support.browser) {
- runScript(uid + '=1;delete ' + uid);
- }
- // done
- clone.emit('complete');
- }
- }
- /*--------------------------------------------------------------------------*/
+ function getZ(u) {
+ return (u - ((size1 * size2) / 2)) / sqrt((size1 * size2 * (size1 + size2 + 1)) / 12);
+ }
- /**
- * Runs the benchmark.
- *
- * @memberOf Benchmark
- * @param {Object} [options={}] Options object.
- * @returns {Object} The benchmark instance.
- * @example
- *
- * // basic usage
- * bench.run();
- *
- * // or with options
- * bench.run({ 'async': true });
- */
- function run(options) {
- var me = this,
- event = Event('start');
-
- // set `running` to `false` so `reset()` won't call `abort()`
- me.running = false;
- me.reset();
- me.running = true;
-
- me.count = me.initCount;
- me.times.timeStamp = +new Date;
- me.emit(event);
-
- if (!event.cancelled) {
- options = { 'async': ((options = options && options.async) == null ? me.async : options) && support.timeout };
-
- // for clones created within `compute()`
- if (me._original) {
- if (me.defer) {
- Deferred(me);
- } else {
- cycle(me, options);
- }
+ // exit early if comparing the same benchmark
+ if (bench == other) {
+ return 0;
}
- // for original benchmarks
- else {
- compute(me, options);
+ // reject the null hyphothesis the two samples come from the
+ // same population (i.e. have the same median) if...
+ if (size1 + size2 > 30) {
+ // ...the z-stat is greater than 1.96 or less than -1.96
+ // http://www.statisticslectures.com/topics/mannwhitneyu/
+ zStat = getZ(u);
+ return abs(zStat) > 1.96 ? (zStat > 0 ? -1 : 1) : 0;
}
+ // ...the U value is less than or equal the critical U value
+ // http://www.geoib.com/mann-whitney-u-test.html
+ critical = maxSize < 5 || minSize < 3 ? 0 : uTable[maxSize][minSize - 3];
+ return u <= critical ? (u == u1 ? 1 : -1) : 0;
}
- return me;
- }
-
- /*--------------------------------------------------------------------------*/
-
- // Firefox 1 erroneously defines variable and argument names of functions on
- // the function itself as non-configurable properties with `undefined` values.
- // The bugginess continues as the `Benchmark` constructor has an argument
- // named `options` and Firefox 1 will not assign a value to `Benchmark.options`,
- // making it non-writable in the process, unless it is the first property
- // assigned by for-in loop of `extend()`.
- extend(Benchmark, {
/**
- * The default options copied by benchmark instances.
+ * Reset properties and abort if running.
*
- * @static
* @memberOf Benchmark
- * @type Object
- */
- 'options': {
-
- /**
- * A flag to indicate that benchmark cycles will execute asynchronously
- * by default.
- *
- * @memberOf Benchmark.options
- * @type Boolean
- */
- 'async': false,
-
- /**
- * A flag to indicate that the benchmark clock is deferred.
- *
- * @memberOf Benchmark.options
- * @type Boolean
- */
- 'defer': false,
-
- /**
- * The delay between test cycles (secs).
- * @memberOf Benchmark.options
- * @type Number
- */
- 'delay': 0.005,
-
- /**
- * Displayed by Benchmark#toString when a `name` is not available
- * (auto-generated if absent).
- *
- * @memberOf Benchmark.options
- * @type String
- */
- 'id': undefined,
-
- /**
- * The default number of times to execute a test on a benchmark's first cycle.
- *
- * @memberOf Benchmark.options
- * @type Number
- */
- 'initCount': 1,
-
- /**
- * The maximum time a benchmark is allowed to run before finishing (secs).
- *
- * Note: Cycle delays aren't counted toward the maximum time.
- *
- * @memberOf Benchmark.options
- * @type Number
- */
- 'maxTime': 5,
-
- /**
- * The minimum sample size required to perform statistical analysis.
- *
- * @memberOf Benchmark.options
- * @type Number
- */
- 'minSamples': 5,
-
- /**
- * The time needed to reduce the percent uncertainty of measurement to 1% (secs).
- *
- * @memberOf Benchmark.options
- * @type Number
- */
- 'minTime': 0,
-
- /**
- * The name of the benchmark.
- *
- * @memberOf Benchmark.options
- * @type String
- */
- 'name': undefined,
-
- /**
- * An event listener called when the benchmark is aborted.
- *
- * @memberOf Benchmark.options
- * @type Function
- */
- 'onAbort': undefined,
-
- /**
- * An event listener called when the benchmark completes running.
- *
- * @memberOf Benchmark.options
- * @type Function
- */
- 'onComplete': undefined,
-
- /**
- * An event listener called after each run cycle.
- *
- * @memberOf Benchmark.options
- * @type Function
- */
- 'onCycle': undefined,
+ * @returns {Object} The benchmark instance.
+ */
+ function reset() {
+ var data,
+ event,
+ bench = this,
+ index = 0,
+ changes = { 'length': 0 },
+ queue = { 'length': 0 };
+
+ if (bench.running && !calledBy.abort) {
+ // no worries, `reset()` is called within `abort()`
+ calledBy.reset = true;
+ bench.abort();
+ delete calledBy.reset;
+ }
+ else {
+ // a non-recursive solution to check if properties have changed
+ // http://www.jslab.dk/articles/non.recursive.preorder.traversal.part4
+ data = { 'destination': bench, 'source': _.extend({}, bench.constructor.prototype, bench.options) };
+ do {
+ _.forOwn(data.source, function(value, key) {
+ var changed,
+ destination = data.destination,
+ currValue = destination[key];
+
+ if (value && typeof value == 'object') {
+ if (_.isArray(value)) {
+ // check if an array value has changed to a non-array value
+ if (!_.isArray(currValue)) {
+ changed = currValue = [];
+ }
+ // or has changed its length
+ if (currValue.length != value.length) {
+ changed = currValue = currValue.slice(0, value.length);
+ currValue.length = value.length;
+ }
+ }
+ // check if an object has changed to a non-object value
+ else if (!currValue || typeof currValue != 'object') {
+ changed = currValue = {};
+ }
+ // register a changed object
+ if (changed) {
+ changes[changes.length++] = { 'destination': destination, 'key': key, 'value': currValue };
+ }
+ queue[queue.length++] = { 'destination': currValue, 'source': value };
+ }
+ // register a changed primitive
+ else if (value !== currValue && !(value == null || _.isFunction(value))) {
+ changes[changes.length++] = { 'destination': destination, 'key': key, 'value': value };
+ }
+ });
+ }
+ while ((data = queue[index++]));
- /**
- * An event listener called when a test errors.
- *
- * @memberOf Benchmark.options
- * @type Function
- */
- 'onError': undefined,
+ // if changed emit the `reset` event and if it isn't cancelled reset the benchmark
+ if (changes.length && (bench.emit(event = Event('reset')), !event.cancelled)) {
+ _.each(changes, function(data) {
+ data.destination[data.key] = data.value;
+ });
+ }
+ }
+ return bench;
+ }
- /**
- * An event listener called when the benchmark is reset.
- *
- * @memberOf Benchmark.options
- * @type Function
- */
- 'onReset': undefined,
+ /**
+ * Displays relevant benchmark information when coerced to a string.
+ *
+ * @name toString
+ * @memberOf Benchmark
+ * @returns {String} A string representation of the benchmark instance.
+ */
+ function toStringBench() {
+ var bench = this,
+ error = bench.error,
+ hz = bench.hz,
+ id = bench.id,
+ stats = bench.stats,
+ size = stats.sample.length,
+ pm = support.java ? '+/-' : '\xb1',
+ result = bench.name || (_.isNaN(id) ? id : '');
- /**
- * An event listener called when the benchmark starts running.
- *
- * @memberOf Benchmark.options
- * @type Function
- */
- 'onStart': undefined,
+ if (error) {
+ result += ': ' + join(error);
+ } else {
+ result += ' x ' + formatNumber(hz.toFixed(hz < 100 ? 2 : 0)) + ' ops/sec ' + pm +
+ stats.rme.toFixed(2) + '% (' + size + ' run' + (size == 1 ? '' : 's') + ' sampled)';
+ }
+ return result;
+ }
- /**
- * The reference time taken to execute the test once (usecs).
- *
- * @memberOf Benchmark.options
- * @type Number
- */
- 'reference': 0
- },
+ /*------------------------------------------------------------------------*/
/**
- * Platform object with properties describing things like browser name,
- * version, and operating system.
+ * Clocks the time taken to execute a test per cycle (secs).
*
- * @static
- * @memberOf Benchmark
- * @type Object
- */
- 'platform': req('platform') || window.platform || {
+ * @private
+ * @param {Object} bench The benchmark instance.
+ * @returns {Number} The time taken.
+ */
+ function clock() {
+ var applet,
+ options = Benchmark.options,
+ timers = [{ 'ns': timer.ns, 'res': max(0.0015, getRes('ms')), 'unit': 'ms' }];
+
+ var templateData = {
+ 'begin': interpolate('s#=new n#'),
+ 'end': interpolate('r#=(new n#-s#)/1e3'),
+ 'uid': uid
+ };
- /**
- * The platform description.
- *
- * @memberOf Benchmark.platform
- * @type String
- */
- 'description': window.navigator && navigator.userAgent || null,
+ // lazy define for hi-res timers
+ clock = function(clone) {
+ var deferred;
+ if (clone instanceof Deferred) {
+ deferred = clone;
+ clone = deferred.benchmark;
+ }
- /**
- * The name of the browser layout engine.
- *
- * @memberOf Benchmark.platform
- * @type String|Null
- */
- 'layout': null,
+ var bench = clone._original,
+ fn = bench.fn,
+ fnArg = deferred ? getFirstArgument(fn) || 'deferred' : '',
+ stringable = isStringable(fn);
- /**
- * The name of the product hosting the browser.
- *
- * @memberOf Benchmark.platform
- * @type String|Null
- */
- 'product': null,
+ _.extend(templateData, {
+ 'setup': getSource(bench.setup, interpolate('m#.setup()')),
+ 'fn': getSource(fn, interpolate('m#.fn(' + fnArg + ')')),
+ 'fnArg': fnArg,
+ 'teardown': getSource(bench.teardown, interpolate('m#.teardown()'))
+ });
- /**
- * The name of the browser/environment.
- *
- * @memberOf Benchmark.platform
- * @type String|Null
- */
- 'name': null,
+ var count = bench.count = clone.count,
+ decompilable = support.decompilation || stringable,
+ id = bench.id,
+ isEmpty = !(templateData.fn || stringable),
+ name = bench.name || (typeof id == 'number' ? '' : id),
+ ns = timer.ns,
+ result = 0;
+
+ // init `minTime` if needed
+ clone.minTime = bench.minTime || (bench.minTime = bench.options.minTime = options.minTime);
+
+ // repair nanosecond timer
+ // (some Chrome builds erase the `ns` variable after millions of executions)
+ if (applet) {
+ try {
+ ns.nanoTime();
+ } catch(e) {
+ // use non-element to avoid issues with libs that augment them
+ ns = timer.ns = new applet.Packages.nano;
+ }
+ }
- /**
- * The name of the product's manufacturer.
- *
- * @memberOf Benchmark.platform
- * @type String|Null
- */
- 'manufacturer': null,
+ // Compile in setup/teardown functions and the test loop.
+ // Create a new compiled test, instead of using the cached `bench.compiled`,
+ // to avoid potential engine optimizations enabled over the life of the test.
+ var compiled = bench.compiled = createCompiled(
+ deferred
+ ? 'var d#=this,${fnArg}=d#,m#=d#.benchmark._original,f#=m#.fn,su#=m#.setup,td#=m#.teardown;' +
+ // when `deferred.cycles` is `0` then...
+ 'if(!d#.cycles){' +
+ // set `deferred.fn`
+ 'd#.fn=function(){var ${fnArg}=d#;if(typeof f#=="function"){try{${fn}\n}catch(e#){f#(d#)}}else{${fn}\n}};' +
+ // set `deferred.teardown`
+ 'd#.teardown=function(){d#.cycles=0;if(typeof td#=="function"){try{${teardown}\n}catch(e#){td#()}}else{${teardown}\n}};' +
+ // execute the benchmark's `setup`
+ 'if(typeof su#=="function"){try{${setup}\n}catch(e#){su#()}}else{${setup}\n};' +
+ // start timer
+ 't#.start(d#);' +
+ // execute `deferred.fn` and return a dummy object
+ '}d#.fn();return{}'
+
+ : 'var r#,s#,m#=this,f#=m#.fn,i#=m#.count,n#=t#.ns;${setup}\n${begin};' +
+ 'while(i#--){${fn}\n}${end};${teardown}\nreturn{elapsed:r#,uid:"${uid}"}'
+ );
- /**
- * The name of the operating system.
- *
- * @memberOf Benchmark.platform
- * @type String|Null
- */
- 'os': null,
+ try {
+ if (isEmpty) {
+ // Firefox may remove dead code from Function#toString results
+ // http://bugzil.la/536085
+ throw new Error('The test "' + name + '" is empty. This may be the result of dead code removal.');
+ }
+ else if (!deferred) {
+ // pretest to determine if compiled code is exits early, usually by a
+ // rogue `return` statement, by checking for a return object with the uid
+ bench.count = 1;
+ compiled = (compiled.call(bench, context, timer) || {}).uid == uid && compiled;
+ bench.count = count;
+ }
+ } catch(e) {
+ compiled = null;
+ clone.error = e || new Error(String(e));
+ bench.count = count;
+ }
+ // fallback when a test exits early or errors during pretest
+ if (decompilable && !compiled && !deferred && !isEmpty) {
+ compiled = createCompiled(
+ (clone.error && !stringable
+ ? 'var r#,s#,m#=this,f#=m#.fn,i#=m#.count'
+ : 'function f#(){${fn}\n}var r#,s#,m#=this,i#=m#.count'
+ ) +
+ ',n#=t#.ns;${setup}\n${begin};m#.f#=f#;while(i#--){m#.f#()}${end};' +
+ 'delete m#.f#;${teardown}\nreturn{elapsed:r#}'
+ );
+
+ try {
+ // pretest one more time to check for errors
+ bench.count = 1;
+ compiled.call(bench, context, timer);
+ bench.compiled = compiled;
+ bench.count = count;
+ delete clone.error;
+ }
+ catch(e) {
+ bench.count = count;
+ if (clone.error) {
+ compiled = null;
+ } else {
+ bench.compiled = compiled;
+ clone.error = e || new Error(String(e));
+ }
+ }
+ }
+ // assign `compiled` to `clone` before calling in case a deferred benchmark
+ // immediately calls `deferred.resolve()`
+ clone.compiled = compiled;
+ // if no errors run the full test loop
+ if (!clone.error) {
+ result = compiled.call(deferred || bench, context, timer).elapsed;
+ }
+ return result;
+ };
+
+ /*----------------------------------------------------------------------*/
/**
- * The alpha/beta release indicator.
- *
- * @memberOf Benchmark.platform
- * @type String|Null
+ * Creates a compiled function from the given function `body`.
*/
- 'prerelease': null,
+ function createCompiled(body) {
+ return createFunction(
+ interpolate('window,t#'),
+ 'var global = window, clearTimeout = global.clearTimeout, setTimeout = global.setTimeout;\n' +
+ interpolate(body)
+ );
+ }
/**
- * The browser/environment version.
- *
- * @memberOf Benchmark.platform
- * @type String|Null
+ * Gets the current timer's minimum resolution (secs).
*/
- 'version': null,
+ function getRes(unit) {
+ var measured,
+ begin,
+ count = 30,
+ divisor = 1e3,
+ ns = timer.ns,
+ sample = [];
+
+ // get average smallest measurable time
+ while (count--) {
+ if (unit == 'us') {
+ divisor = 1e6;
+ if (ns.stop) {
+ ns.start();
+ while (!(measured = ns.microseconds())) { }
+ } else if (ns[perfName]) {
+ divisor = 1e3;
+ measured = Function('n', 'var r,s=n.' + perfName + '();while(!(r=n.' + perfName + '()-s)){};return r')(ns);
+ } else {
+ begin = ns();
+ while (!(measured = ns() - begin)) { }
+ }
+ }
+ else if (unit == 'ns') {
+ divisor = 1e9;
+ if (ns.nanoTime) {
+ begin = ns.nanoTime();
+ while (!(measured = ns.nanoTime() - begin)) { }
+ } else {
+ begin = (begin = ns())[0] + (begin[1] / divisor);
+ while (!(measured = ((measured = ns())[0] + (measured[1] / divisor)) - begin)) { }
+ divisor = 1;
+ }
+ }
+ else {
+ begin = new ns;
+ while (!(measured = new ns - begin)) { }
+ }
+ // check for broken timers (nanoTime may have issues)
+ // http://alivebutsleepy.srnet.cz/unreliable-system-nanotime/
+ if (measured > 0) {
+ sample.push(measured);
+ } else {
+ sample.push(Infinity);
+ break;
+ }
+ }
+ // convert to seconds
+ return getMean(sample) / divisor;
+ }
/**
- * Return platform description when the platform object is coerced to a string.
- *
- * @memberOf Benchmark.platform
- * @type Function
- * @returns {String} The platform description.
+ * Interpolates a given template string.
*/
- 'toString': function() {
- return this.description || '';
+ function interpolate(string) {
+ // replaces all occurrences of `#` with a unique number and template tokens with content
+ return _.template(string.replace(/\#/g, /\d+/.exec(uid)), templateData || {});
}
- },
- /**
- * The semantic version number.
- *
- * @static
- * @memberOf Benchmark
- * @type String
- */
- 'version': '1.0.0',
+ /*----------------------------------------------------------------------*/
- // an object of environment/feature detection flags
- 'support': support,
+ // detect nanosecond support from a Java applet
+ _.each(doc && doc.applets || [], function(element) {
+ return !(timer.ns = applet = 'nanoTime' in element && element);
+ });
+
+ // check type in case Safari returns an object instead of a number
+ try {
+ if (typeof timer.ns.nanoTime() == 'number') {
+ timers.push({ 'ns': timer.ns, 'res': getRes('ns'), 'unit': 'ns' });
+ }
+ } catch(e) { }
- // clone objects
- 'deepClone': deepClone,
+ // detect Chrome's microsecond timer:
+ // enable benchmarking via the --enable-benchmarking command
+ // line switch in at least Chrome 7 to use chrome.Interval
+ try {
+ if ((timer.ns = new (context.chrome || context.chromium).Interval)) {
+ timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' });
+ }
+ } catch(e) { }
- // iteration utility
- 'each': each,
+ // detect `performance.now` microsecond resolution timer
+ if ((timer.ns = perfName && perfObject)) {
+ timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' });
+ }
- // augment objects
- 'extend': extend,
+ // detect Node's nanosecond resolution timer available in Node >= 0.8
+ if (processObject && typeof (timer.ns = processObject.hrtime) == 'function') {
+ timers.push({ 'ns': timer.ns, 'res': getRes('ns'), 'unit': 'ns' });
+ }
- // generic Array#filter
- 'filter': filter,
+ // detect Wade Simmons' Node microtime module
+ if (microtimeObject && typeof (timer.ns = microtimeObject.now) == 'function') {
+ timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' });
+ }
- // generic Array#forEach
- 'forEach': forEach,
+ // pick timer with highest resolution
+ timer = _.reduce(timers, function(timer, other) {
+ return other.res < timer.res ? other : timer;
+ });
- // generic own property iteration utility
- 'forOwn': forOwn,
+ // remove unused applet
+ if (timer.unit != 'ns' && applet) {
+ applet = destroyElement(applet);
+ }
+ // error if there are no working timers
+ if (timer.res == Infinity) {
+ throw new Error('Benchmark.js was unable to find a working timer.');
+ }
+ // use API of chosen timer
+ if (timer.unit == 'ns') {
+ if (timer.ns.nanoTime) {
+ _.extend(templateData, {
+ 'begin': interpolate('s#=n#.nanoTime()'),
+ 'end': interpolate('r#=(n#.nanoTime()-s#)/1e9')
+ });
+ } else {
+ _.extend(templateData, {
+ 'begin': interpolate('s#=n#()'),
+ 'end': interpolate('r#=n#(s#);r#=r#[0]+(r#[1]/1e9)')
+ });
+ }
+ }
+ else if (timer.unit == 'us') {
+ if (timer.ns.stop) {
+ _.extend(templateData, {
+ 'begin': interpolate('s#=n#.start()'),
+ 'end': interpolate('r#=n#.microseconds()/1e6')
+ });
+ } else if (perfName) {
+ _.extend(templateData, {
+ 'begin': interpolate('s#=n#.' + perfName + '()'),
+ 'end': interpolate('r#=(n#.' + perfName + '()-s#)/1e3')
+ });
+ } else {
+ _.extend(templateData, {
+ 'begin': interpolate('s#=n#()'),
+ 'end': interpolate('r#=(n#()-s#)/1e6')
+ });
+ }
+ }
- // converts a number to a comma-separated string
- 'formatNumber': formatNumber,
+ // define `timer` methods
+ timer.start = createFunction(
+ interpolate('o#'),
+ interpolate('var n#=this.ns,${begin};o#.elapsed=0;o#.timeStamp=s#')
+ );
- // generic Object#hasOwnProperty
- // (trigger hasKey's lazy define before assigning it to Benchmark)
- 'hasKey': (hasKey(Benchmark, ''), hasKey),
+ timer.stop = createFunction(
+ interpolate('o#'),
+ interpolate('var n#=this.ns,s#=o#.timeStamp,${end};o#.elapsed=r#')
+ );
- // generic Array#indexOf
- 'indexOf': indexOf,
+ // resolve time span required to achieve a percent uncertainty of at most 1%
+ // http://spiff.rit.edu/classes/phys273/uncert/uncert.html
+ options.minTime || (options.minTime = max(timer.res / 2 / 0.01, 0.05));
+ return clock.apply(null, arguments);
+ }
- // template utility
- 'interpolate': interpolate,
+ /*------------------------------------------------------------------------*/
- // invokes a method on each item in an array
- 'invoke': invoke,
+ /**
+ * Computes stats on benchmark results.
+ *
+ * @private
+ * @param {Object} bench The benchmark instance.
+ * @param {Object} options The options object.
+ */
+ function compute(bench, options) {
+ options || (options = {});
- // generic Array#join for arrays and objects
- 'join': join,
+ var async = options.async,
+ elapsed = 0,
+ initCount = bench.initCount,
+ minSamples = bench.minSamples,
+ queue = [],
+ sample = bench.stats.sample;
- // generic Array#map
- 'map': map,
+ /**
+ * Adds a clone to the queue.
+ */
+ function enqueue() {
+ queue.push(bench.clone({
+ '_original': bench,
+ 'events': {
+ 'abort': [update],
+ 'cycle': [update],
+ 'error': [update],
+ 'start': [update]
+ }
+ }));
+ }
- // retrieves a property value from each item in an array
- 'pluck': pluck,
+ /**
+ * Updates the clone/original benchmarks to keep their data in sync.
+ */
+ function update(event) {
+ var clone = this,
+ type = event.type;
+
+ if (bench.running) {
+ if (type == 'start') {
+ // Note: `clone.minTime` prop is inited in `clock()`
+ clone.count = bench.initCount;
+ }
+ else {
+ if (type == 'error') {
+ bench.error = clone.error;
+ }
+ if (type == 'abort') {
+ bench.abort();
+ bench.emit('cycle');
+ } else {
+ event.currentTarget = event.target = bench;
+ bench.emit(event);
+ }
+ }
+ } else if (bench.aborted) {
+ // clear abort listeners to avoid triggering bench's abort/cycle again
+ clone.events.abort.length = 0;
+ clone.abort();
+ }
+ }
- // generic Array#reduce
- 'reduce': reduce
- });
+ /**
+ * Determines if more clones should be queued or if cycling should stop.
+ */
+ function evaluate(event) {
+ var critical,
+ df,
+ mean,
+ moe,
+ rme,
+ sd,
+ sem,
+ variance,
+ clone = event.target,
+ done = bench.aborted,
+ now = +new Date,
+ size = sample.push(clone.times.period),
+ maxedOut = size >= minSamples && (elapsed += now - clone.times.timeStamp) / 1e3 > bench.maxTime,
+ times = bench.times,
+ varOf = function(sum, x) { return sum + pow(x - mean, 2); };
+
+ // exit early for aborted or unclockable tests
+ if (done || clone.hz == Infinity) {
+ maxedOut = !(size = sample.length = queue.length = 0);
+ }
- /*--------------------------------------------------------------------------*/
+ if (!done) {
+ // sample mean (estimate of the population mean)
+ mean = getMean(sample);
+ // sample variance (estimate of the population variance)
+ variance = _.reduce(sample, varOf, 0) / (size - 1) || 0;
+ // sample standard deviation (estimate of the population standard deviation)
+ sd = sqrt(variance);
+ // standard error of the mean (a.k.a. the standard deviation of the sampling distribution of the sample mean)
+ sem = sd / sqrt(size);
+ // degrees of freedom
+ df = size - 1;
+ // critical value
+ critical = tTable[Math.round(df) || 1] || tTable.infinity;
+ // margin of error
+ moe = sem * critical;
+ // relative margin of error
+ rme = (moe / mean) * 100 || 0;
+
+ _.extend(bench.stats, {
+ 'deviation': sd,
+ 'mean': mean,
+ 'moe': moe,
+ 'rme': rme,
+ 'sem': sem,
+ 'variance': variance
+ });
- extend(Benchmark.prototype, {
+ // Abort the cycle loop when the minimum sample size has been collected
+ // and the elapsed time exceeds the maximum time allowed per benchmark.
+ // We don't count cycle delays toward the max time because delays may be
+ // increased by browsers that clamp timeouts for inactive tabs.
+ // https://developer.mozilla.org/en/window.setTimeout#Inactive_tabs
+ if (maxedOut) {
+ // reset the `initCount` in case the benchmark is rerun
+ bench.initCount = initCount;
+ bench.running = false;
+ done = true;
+ times.elapsed = (now - times.timeStamp) / 1e3;
+ }
+ if (bench.hz != Infinity) {
+ bench.hz = 1 / mean;
+ times.cycle = mean * bench.count;
+ times.period = mean;
+ }
+ }
+ // if time permits, increase sample size to reduce the margin of error
+ if (queue.length < 2 && !maxedOut) {
+ enqueue();
+ }
+ // abort the invoke cycle when done
+ event.aborted = done;
+ }
- /**
- * The number of times a test was executed.
- *
- * @memberOf Benchmark
- * @type Number
- */
- 'count': 0,
+ // init queue and begin
+ enqueue();
+ invoke(queue, {
+ 'name': 'run',
+ 'args': { 'async': async },
+ 'queued': true,
+ 'onCycle': evaluate,
+ 'onComplete': function() { bench.emit('complete'); }
+ });
+ }
- /**
- * The number of cycles performed while benchmarking.
- *
- * @memberOf Benchmark
- * @type Number
- */
- 'cycles': 0,
+ /*------------------------------------------------------------------------*/
/**
- * The number of executions per second.
+ * Cycles a benchmark until a run `count` can be established.
*
- * @memberOf Benchmark
- * @type Number
+ * @private
+ * @param {Object} clone The cloned benchmark instance.
+ * @param {Object} options The options object.
*/
- 'hz': 0,
+ function cycle(clone, options) {
+ options || (options = {});
- /**
- * The compiled test function.
- *
- * @memberOf Benchmark
- * @type Function|String
- */
- 'compiled': undefined,
+ var deferred;
+ if (clone instanceof Deferred) {
+ deferred = clone;
+ clone = clone.benchmark;
+ }
- /**
- * The error object if the test failed.
- *
- * @memberOf Benchmark
- * @type Object
- */
- 'error': undefined,
+ var clocked,
+ cycles,
+ divisor,
+ event,
+ minTime,
+ period,
+ async = options.async,
+ bench = clone._original,
+ count = clone.count,
+ times = clone.times;
+
+ // continue, if not aborted between cycles
+ if (clone.running) {
+ // `minTime` is set to `Benchmark.options.minTime` in `clock()`
+ cycles = ++clone.cycles;
+ clocked = deferred ? deferred.elapsed : clock(clone);
+ minTime = clone.minTime;
- /**
- * The test to benchmark.
- *
- * @memberOf Benchmark
- * @type Function|String
- */
- 'fn': undefined,
+ if (cycles > bench.cycles) {
+ bench.cycles = cycles;
+ }
+ if (clone.error) {
+ event = Event('error');
+ event.message = clone.error;
+ clone.emit(event);
+ if (!event.cancelled) {
+ clone.abort();
+ }
+ }
+ }
- /**
- * A flag to indicate if the benchmark is aborted.
- *
- * @memberOf Benchmark
- * @type Boolean
- */
- 'aborted': false,
+ // continue, if not errored
+ if (clone.running) {
+ // time taken to complete last test cycle
+ bench.times.cycle = times.cycle = clocked;
+ // seconds per operation
+ period = bench.times.period = times.period = clocked / count;
+ // ops per second
+ bench.hz = clone.hz = 1 / period;
+ // avoid working our way up to this next time
+ bench.initCount = clone.initCount = count;
+ // do we need to do another cycle?
+ clone.running = clocked < minTime;
+
+ if (clone.running) {
+ // tests may clock at `0` when `initCount` is a small number,
+ // to avoid that we set its count to something a bit higher
+ if (!clocked && (divisor = divisors[clone.cycles]) != null) {
+ count = floor(4e6 / divisor);
+ }
+ // calculate how many more iterations it will take to achive the `minTime`
+ if (count <= clone.count) {
+ count += Math.ceil((minTime - clocked) / period);
+ }
+ clone.running = count != Infinity;
+ }
+ }
+ // should we exit early?
+ event = Event('cycle');
+ clone.emit(event);
+ if (event.aborted) {
+ clone.abort();
+ }
+ // figure out what to do next
+ if (clone.running) {
+ // start a new cycle
+ clone.count = count;
+ if (deferred) {
+ clone.compiled.call(deferred, context, timer);
+ } else if (async) {
+ delay(clone, function() { cycle(clone, options); });
+ } else {
+ cycle(clone);
+ }
+ }
+ else {
+ // fix TraceMonkey bug associated with clock fallbacks
+ // http://bugzil.la/509069
+ if (support.browser) {
+ runScript(uid + '=1;delete ' + uid);
+ }
+ // done
+ clone.emit('complete');
+ }
+ }
- /**
- * A flag to indicate if the benchmark is running.
- *
- * @memberOf Benchmark
- * @type Boolean
- */
- 'running': false,
+ /*------------------------------------------------------------------------*/
/**
- * Compiled into the test and executed immediately **before** the test loop.
+ * Runs the benchmark.
*
* @memberOf Benchmark
- * @type Function|String
+ * @param {Object} [options={}] Options object.
+ * @returns {Object} The benchmark instance.
* @example
*
* // basic usage
- * var bench = Benchmark({
- * 'setup': function() {
- * var c = this.count,
- * element = document.getElementById('container');
- * while (c--) {
- * element.appendChild(document.createElement('div'));
- * }
- * },
- * 'fn': function() {
- * element.removeChild(element.lastChild);
- * }
- * });
- *
- * // compiles to something like:
- * var c = this.count,
- * element = document.getElementById('container');
- * while (c--) {
- * element.appendChild(document.createElement('div'));
- * }
- * var start = new Date;
- * while (count--) {
- * element.removeChild(element.lastChild);
- * }
- * var end = new Date - start;
- *
- * // or using strings
- * var bench = Benchmark({
- * 'setup': '\
- * var a = 0;\n\
- * (function() {\n\
- * (function() {\n\
- * (function() {',
- * 'fn': 'a += 1;',
- * 'teardown': '\
- * }())\n\
- * }())\n\
- * }())'
- * });
+ * bench.run();
*
- * // compiles to something like:
- * var a = 0;
- * (function() {
- * (function() {
- * (function() {
- * var start = new Date;
- * while (count--) {
- * a += 1;
- * }
- * var end = new Date - start;
- * }())
- * }())
- * }())
+ * // or with options
+ * bench.run({ 'async': true });
*/
- 'setup': noop,
+ function run(options) {
+ var bench = this,
+ event = Event('start');
- /**
- * Compiled into the test and executed immediately **after** the test loop.
- *
- * @memberOf Benchmark
- * @type Function|String
- */
- 'teardown': noop,
+ // set `running` to `false` so `reset()` won't call `abort()`
+ bench.running = false;
+ bench.reset();
+ bench.running = true;
- /**
- * An object of stats including mean, margin or error, and standard deviation.
- *
- * @memberOf Benchmark
- * @type Object
- */
- 'stats': {
+ bench.count = bench.initCount;
+ bench.times.timeStamp = +new Date;
+ bench.emit(event);
+
+ if (!event.cancelled) {
+ options = { 'async': ((options = options && options.async) == null ? bench.async : options) && support.timeout };
+
+ // for clones created within `compute()`
+ if (bench._original) {
+ if (bench.defer) {
+ Deferred(bench);
+ } else {
+ cycle(bench, options);
+ }
+ }
+ // for original benchmarks
+ else {
+ compute(bench, options);
+ }
+ }
+ return bench;
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ // Firefox 1 erroneously defines variable and argument names of functions on
+ // the function itself as non-configurable properties with `undefined` values.
+ // The bugginess continues as the `Benchmark` constructor has an argument
+ // named `options` and Firefox 1 will not assign a value to `Benchmark.options`,
+ // making it non-writable in the process, unless it is the first property
+ // assigned by for-in loop of `_.extend()`.
+ _.extend(Benchmark, {
+
+ /**
+ * The default options copied by benchmark instances.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @type Object
+ */
+ 'options': {
+
+ /**
+ * A flag to indicate that benchmark cycles will execute asynchronously
+ * by default.
+ *
+ * @memberOf Benchmark.options
+ * @type Boolean
+ */
+ 'async': false,
+
+ /**
+ * A flag to indicate that the benchmark clock is deferred.
+ *
+ * @memberOf Benchmark.options
+ * @type Boolean
+ */
+ 'defer': false,
+
+ /**
+ * The delay between test cycles (secs).
+ * @memberOf Benchmark.options
+ * @type Number
+ */
+ 'delay': 0.005,
+
+ /**
+ * Displayed by Benchmark#toString when a `name` is not available
+ * (auto-generated if absent).
+ *
+ * @memberOf Benchmark.options
+ * @type String
+ */
+ 'id': undefined,
+
+ /**
+ * The default number of times to execute a test on a benchmark's first cycle.
+ *
+ * @memberOf Benchmark.options
+ * @type Number
+ */
+ 'initCount': 1,
+
+ /**
+ * The maximum time a benchmark is allowed to run before finishing (secs).
+ *
+ * Note: Cycle delays aren't counted toward the maximum time.
+ *
+ * @memberOf Benchmark.options
+ * @type Number
+ */
+ 'maxTime': 5,
+
+ /**
+ * The minimum sample size required to perform statistical analysis.
+ *
+ * @memberOf Benchmark.options
+ * @type Number
+ */
+ 'minSamples': 5,
+
+ /**
+ * The time needed to reduce the percent uncertainty of measurement to 1% (secs).
+ *
+ * @memberOf Benchmark.options
+ * @type Number
+ */
+ 'minTime': 0,
+
+ /**
+ * The name of the benchmark.
+ *
+ * @memberOf Benchmark.options
+ * @type String
+ */
+ 'name': undefined,
+
+ /**
+ * An event listener called when the benchmark is aborted.
+ *
+ * @memberOf Benchmark.options
+ * @type Function
+ */
+ 'onAbort': undefined,
+
+ /**
+ * An event listener called when the benchmark completes running.
+ *
+ * @memberOf Benchmark.options
+ * @type Function
+ */
+ 'onComplete': undefined,
+
+ /**
+ * An event listener called after each run cycle.
+ *
+ * @memberOf Benchmark.options
+ * @type Function
+ */
+ 'onCycle': undefined,
+
+ /**
+ * An event listener called when a test errors.
+ *
+ * @memberOf Benchmark.options
+ * @type Function
+ */
+ 'onError': undefined,
+
+ /**
+ * An event listener called when the benchmark is reset.
+ *
+ * @memberOf Benchmark.options
+ * @type Function
+ */
+ 'onReset': undefined,
+
+ /**
+ * An event listener called when the benchmark starts running.
+ *
+ * @memberOf Benchmark.options
+ * @type Function
+ */
+ 'onStart': undefined
+ },
+
+ /**
+ * Platform object with properties describing things like browser name,
+ * version, and operating system.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @type Object
+ */
+ 'platform': context.platform || req('platform') || ({
+ 'description': context.navigator && context.navigator.userAgent || null,
+ 'layout': null,
+ 'product': null,
+ 'name': null,
+ 'manufacturer': null,
+ 'os': null,
+ 'prerelease': null,
+ 'version': null,
+ 'toString': function() {
+ return this.description || '';
+ }
+ }),
+
+ /**
+ * The semantic version number.
+ *
+ * @static
+ * @memberOf Benchmark
+ * @type String
+ */
+ 'version': '1.0.0'
+ });
+
+ _.extend(Benchmark, {
+ 'filter': filter,
+ 'formatNumber': formatNumber,
+ 'invoke': invoke,
+ 'join': join,
+ 'runInContext': runInContext,
+ 'support': support
+ });
+
+ /*------------------------------------------------------------------------*/
+
+ _.extend(Benchmark.prototype, {
/**
- * The margin of error.
+ * The number of times a test was executed.
*
- * @memberOf Benchmark#stats
+ * @memberOf Benchmark
* @type Number
*/
- 'moe': 0,
+ 'count': 0,
/**
- * The relative margin of error (expressed as a percentage of the mean).
+ * The number of cycles performed while benchmarking.
*
- * @memberOf Benchmark#stats
+ * @memberOf Benchmark
* @type Number
*/
- 'rme': 0,
+ 'cycles': 0,
/**
- * The standard error of the mean.
+ * The number of executions per second.
*
- * @memberOf Benchmark#stats
+ * @memberOf Benchmark
* @type Number
*/
- 'sem': 0,
+ 'hz': 0,
/**
- * The sample standard deviation.
+ * The compiled test function.
*
- * @memberOf Benchmark#stats
- * @type Number
+ * @memberOf Benchmark
+ * @type Function|String
*/
- 'deviation': 0,
+ 'compiled': undefined,
/**
- * The sample arithmetic mean.
+ * The error object if the test failed.
*
- * @memberOf Benchmark#stats
- * @type Number
+ * @memberOf Benchmark
+ * @type Object
*/
- 'mean': 0,
+ 'error': undefined,
/**
- * The array of sampled periods.
+ * The test to benchmark.
*
- * @memberOf Benchmark#stats
- * @type Array
+ * @memberOf Benchmark
+ * @type Function|String
*/
- 'sample': [],
+ 'fn': undefined,
/**
- * The sample variance.
+ * A flag to indicate if the benchmark is aborted.
*
- * @memberOf Benchmark#stats
- * @type Number
+ * @memberOf Benchmark
+ * @type Boolean
*/
- 'variance': 0
- },
-
- /**
- * An object of timing data including cycle, elapsed, period, start, and stop.
- *
- * @memberOf Benchmark
- * @type Object
- */
- 'times': {
+ 'aborted': false,
/**
- * The time taken to complete the last cycle (secs).
+ * A flag to indicate if the benchmark is running.
*
- * @memberOf Benchmark#times
- * @type Number
+ * @memberOf Benchmark
+ * @type Boolean
*/
- 'cycle': 0,
+ 'running': false,
/**
- * The time taken to complete the benchmark (secs).
+ * Compiled into the test and executed immediately **before** the test loop.
*
- * @memberOf Benchmark#times
- * @type Number
+ * @memberOf Benchmark
+ * @type Function|String
+ * @example
+ *
+ * // basic usage
+ * var bench = Benchmark({
+ * 'setup': function() {
+ * var c = this.count,
+ * element = document.getElementById('container');
+ * while (c--) {
+ * element.appendChild(document.createElement('div'));
+ * }
+ * },
+ * 'fn': function() {
+ * element.removeChild(element.lastChild);
+ * }
+ * });
+ *
+ * // compiles to something like:
+ * var c = this.count,
+ * element = document.getElementById('container');
+ * while (c--) {
+ * element.appendChild(document.createElement('div'));
+ * }
+ * var start = new Date;
+ * while (count--) {
+ * element.removeChild(element.lastChild);
+ * }
+ * var end = new Date - start;
+ *
+ * // or using strings
+ * var bench = Benchmark({
+ * 'setup': '\
+ * var a = 0;\n\
+ * (function() {\n\
+ * (function() {\n\
+ * (function() {',
+ * 'fn': 'a += 1;',
+ * 'teardown': '\
+ * }())\n\
+ * }())\n\
+ * }())'
+ * });
+ *
+ * // compiles to something like:
+ * var a = 0;
+ * (function() {
+ * (function() {
+ * (function() {
+ * var start = new Date;
+ * while (count--) {
+ * a += 1;
+ * }
+ * var end = new Date - start;
+ * }())
+ * }())
+ * }())
*/
- 'elapsed': 0,
+ 'setup': noop,
/**
- * The time taken to execute the test once (secs).
+ * Compiled into the test and executed immediately **after** the test loop.
*
- * @memberOf Benchmark#times
- * @type Number
+ * @memberOf Benchmark
+ * @type Function|String
*/
- 'period': 0,
+ 'teardown': noop,
/**
- * A timestamp of when the benchmark started (ms).
+ * An object of stats including mean, margin or error, and standard deviation.
*
- * @memberOf Benchmark#times
- * @type Number
+ * @memberOf Benchmark
+ * @type Object
*/
- 'timeStamp': 0
- },
-
- // aborts benchmark (does not record times)
- 'abort': abort,
-
- // creates a new benchmark using the same test and options
- 'clone': clone,
-
- // compares benchmark's hertz with another
- 'compare': compare,
-
- // executes listeners
- 'emit': emit,
-
- // get listeners
- 'listeners': listeners,
-
- // unregister listeners
- 'off': off,
-
- // register listeners
- 'on': on,
-
- // reset benchmark properties
- 'reset': reset,
-
- // runs the benchmark
- 'run': run,
-
- // pretty print benchmark info
- 'toString': toStringBench
- });
-
- /*--------------------------------------------------------------------------*/
-
- extend(Deferred.prototype, {
-
- /**
- * The deferred benchmark instance.
- *
- * @memberOf Benchmark.Deferred
- * @type Object
- */
- 'benchmark': null,
-
- /**
- * The number of deferred cycles performed while benchmarking.
- *
- * @memberOf Benchmark.Deferred
- * @type Number
- */
- 'cycles': 0,
-
- /**
- * The time taken to complete the deferred benchmark (secs).
- *
- * @memberOf Benchmark.Deferred
- * @type Number
- */
- 'elapsed': 0,
-
- /**
- * A timestamp of when the deferred benchmark started (ms).
- *
- * @memberOf Benchmark.Deferred
- * @type Number
- */
- 'timeStamp': 0,
-
- // cycles/completes the deferred benchmark
- 'resolve': resolve
- });
-
- /*--------------------------------------------------------------------------*/
-
- extend(Event.prototype, {
-
- /**
- * A flag to indicate if the emitters listener iteration is aborted.
- *
- * @memberOf Benchmark.Event
- * @type Boolean
- */
- 'aborted': false,
-
- /**
- * A flag to indicate if the default action is cancelled.
- *
- * @memberOf Benchmark.Event
- * @type Boolean
- */
- 'cancelled': false,
-
- /**
- * The object whose listeners are currently being processed.
- *
- * @memberOf Benchmark.Event
- * @type Object
- */
- 'currentTarget': undefined,
-
- /**
- * The return value of the last executed listener.
- *
- * @memberOf Benchmark.Event
- * @type Mixed
- */
- 'result': undefined,
-
- /**
- * The object to which the event was originally emitted.
- *
- * @memberOf Benchmark.Event
- * @type Object
- */
- 'target': undefined,
-
- /**
- * A timestamp of when the event was created (ms).
- *
- * @memberOf Benchmark.Event
- * @type Number
- */
- 'timeStamp': 0,
+ 'stats': {
+
+ /**
+ * The margin of error.
+ *
+ * @memberOf Benchmark#stats
+ * @type Number
+ */
+ 'moe': 0,
+
+ /**
+ * The relative margin of error (expressed as a percentage of the mean).
+ *
+ * @memberOf Benchmark#stats
+ * @type Number
+ */
+ 'rme': 0,
+
+ /**
+ * The standard error of the mean.
+ *
+ * @memberOf Benchmark#stats
+ * @type Number
+ */
+ 'sem': 0,
+
+ /**
+ * The sample standard deviation.
+ *
+ * @memberOf Benchmark#stats
+ * @type Number
+ */
+ 'deviation': 0,
+
+ /**
+ * The sample arithmetic mean (secs).
+ *
+ * @memberOf Benchmark#stats
+ * @type Number
+ */
+ 'mean': 0,
+
+ /**
+ * The array of sampled periods.
+ *
+ * @memberOf Benchmark#stats
+ * @type Array
+ */
+ 'sample': [],
+
+ /**
+ * The sample variance.
+ *
+ * @memberOf Benchmark#stats
+ * @type Number
+ */
+ 'variance': 0
+ },
- /**
- * The event type.
- *
- * @memberOf Benchmark.Event
- * @type String
- */
- 'type': ''
- });
+ /**
+ * An object of timing data including cycle, elapsed, period, start, and stop.
+ *
+ * @memberOf Benchmark
+ * @type Object
+ */
+ 'times': {
+
+ /**
+ * The time taken to complete the last cycle (secs).
+ *
+ * @memberOf Benchmark#times
+ * @type Number
+ */
+ 'cycle': 0,
+
+ /**
+ * The time taken to complete the benchmark (secs).
+ *
+ * @memberOf Benchmark#times
+ * @type Number
+ */
+ 'elapsed': 0,
+
+ /**
+ * The time taken to execute the test once (secs).
+ *
+ * @memberOf Benchmark#times
+ * @type Number
+ */
+ 'period': 0,
+
+ /**
+ * A timestamp of when the benchmark started (ms).
+ *
+ * @memberOf Benchmark#times
+ * @type Number
+ */
+ 'timeStamp': 0
+ }
+ });
- /*--------------------------------------------------------------------------*/
+ _.extend(Benchmark.prototype, {
+ 'abort': abort,
+ 'clone': clone,
+ 'compare': compare,
+ 'emit': emit,
+ 'listeners': listeners,
+ 'off': off,
+ 'on': on,
+ 'reset': reset,
+ 'run': run,
+ 'toString': toStringBench
+ });
- /**
- * The default options copied by suite instances.
- *
- * @static
- * @memberOf Benchmark.Suite
- * @type Object
- */
- Suite.options = {
+ /*------------------------------------------------------------------------*/
- /**
- * The name of the suite.
- *
- * @memberOf Benchmark.Suite.options
- * @type String
- */
- 'name': undefined
- };
+ _.extend(Deferred.prototype, {
- /*--------------------------------------------------------------------------*/
+ /**
+ * The deferred benchmark instance.
+ *
+ * @memberOf Benchmark.Deferred
+ * @type Object
+ */
+ 'benchmark': null,
- extend(Suite.prototype, {
+ /**
+ * The number of deferred cycles performed while benchmarking.
+ *
+ * @memberOf Benchmark.Deferred
+ * @type Number
+ */
+ 'cycles': 0,
- /**
- * The number of benchmarks in the suite.
- *
- * @memberOf Benchmark.Suite
- * @type Number
- */
- 'length': 0,
+ /**
+ * The time taken to complete the deferred benchmark (secs).
+ *
+ * @memberOf Benchmark.Deferred
+ * @type Number
+ */
+ 'elapsed': 0,
- /**
- * A score computed using the normalized result of each benchmark in the suite.
- *
- * @memberOf Benchmark.Suite
- * @type Number
- */
- 'score': 0,
+ /**
+ * A timestamp of when the deferred benchmark started (ms).
+ *
+ * @memberOf Benchmark.Deferred
+ * @type Number
+ */
+ 'timeStamp': 0
+ });
- /**
- * A flag to indicate if the suite is aborted.
- *
- * @memberOf Benchmark.Suite
- * @type Boolean
- */
- 'aborted': false,
+ _.extend(Deferred.prototype, {
+ 'resolve': resolve
+ });
- /**
- * A flag to indicate if the suite is running.
- *
- * @memberOf Benchmark.Suite
- * @type Boolean
- */
- 'running': false,
+ /*------------------------------------------------------------------------*/
- /**
- * An `Array#forEach` like method.
- * Callbacks may terminate the loop by explicitly returning `false`.
- *
- * @memberOf Benchmark.Suite
- * @param {Function} callback The function called per iteration.
- * @returns {Object} The suite iterated over.
- */
- 'forEach': methodize(forEach),
+ _.extend(Event.prototype, {
- /**
- * An `Array#indexOf` like method.
- *
- * @memberOf Benchmark.Suite
- * @param {Mixed} value The value to search for.
- * @returns {Number} The index of the matched value or `-1`.
- */
- 'indexOf': methodize(indexOf),
+ /**
+ * A flag to indicate if the emitters listener iteration is aborted.
+ *
+ * @memberOf Benchmark.Event
+ * @type Boolean
+ */
+ 'aborted': false,
- /**
- * Invokes a method on all benchmarks in the suite.
- *
- * @memberOf Benchmark.Suite
- * @param {String|Object} name The name of the method to invoke OR options object.
- * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with.
- * @returns {Array} A new array of values returned from each method invoked.
- */
- 'invoke': methodize(invoke),
+ /**
+ * A flag to indicate if the default action is cancelled.
+ *
+ * @memberOf Benchmark.Event
+ * @type Boolean
+ */
+ 'cancelled': false,
- /**
- * Converts the suite of benchmarks to a string.
- *
- * @memberOf Benchmark.Suite
- * @param {String} [separator=','] A string to separate each element of the array.
- * @returns {String} The string.
- */
- 'join': [].join,
+ /**
+ * The object whose listeners are currently being processed.
+ *
+ * @memberOf Benchmark.Event
+ * @type Object
+ */
+ 'currentTarget': undefined,
- /**
- * An `Array#map` like method.
- *
- * @memberOf Benchmark.Suite
- * @param {Function} callback The function called per iteration.
- * @returns {Array} A new array of values returned by the callback.
- */
- 'map': methodize(map),
+ /**
+ * The return value of the last executed listener.
+ *
+ * @memberOf Benchmark.Event
+ * @type Mixed
+ */
+ 'result': undefined,
- /**
- * Retrieves the value of a specified property from all benchmarks in the suite.
- *
- * @memberOf Benchmark.Suite
- * @param {String} property The property to pluck.
- * @returns {Array} A new array of property values.
- */
- 'pluck': methodize(pluck),
+ /**
+ * The object to which the event was originally emitted.
+ *
+ * @memberOf Benchmark.Event
+ * @type Object
+ */
+ 'target': undefined,
- /**
- * Removes the last benchmark from the suite and returns it.
- *
- * @memberOf Benchmark.Suite
- * @returns {Mixed} The removed benchmark.
- */
- 'pop': [].pop,
+ /**
+ * A timestamp of when the event was created (ms).
+ *
+ * @memberOf Benchmark.Event
+ * @type Number
+ */
+ 'timeStamp': 0,
- /**
- * Appends benchmarks to the suite.
- *
- * @memberOf Benchmark.Suite
- * @returns {Number} The suite's new length.
- */
- 'push': [].push,
+ /**
+ * The event type.
+ *
+ * @memberOf Benchmark.Event
+ * @type String
+ */
+ 'type': ''
+ });
- /**
- * Sorts the benchmarks of the suite.
- *
- * @memberOf Benchmark.Suite
- * @param {Function} [compareFn=null] A function that defines the sort order.
- * @returns {Object} The sorted suite.
- */
- 'sort': [].sort,
+ /*------------------------------------------------------------------------*/
/**
- * An `Array#reduce` like method.
+ * The default options copied by suite instances.
*
+ * @static
* @memberOf Benchmark.Suite
- * @param {Function} callback The function called per iteration.
- * @param {Mixed} accumulator Initial value of the accumulator.
- * @returns {Mixed} The accumulator.
+ * @type Object
*/
- 'reduce': methodize(reduce),
+ Suite.options = {
- // aborts all benchmarks in the suite
- 'abort': abortSuite,
-
- // adds a benchmark to the suite
- 'add': add,
-
- // creates a new suite with cloned benchmarks
- 'clone': cloneSuite,
-
- // executes listeners of a specified type
- 'emit': emit,
+ /**
+ * The name of the suite.
+ *
+ * @memberOf Benchmark.Suite.options
+ * @type String
+ */
+ 'name': undefined
+ };
- // creates a new suite of filtered benchmarks
- 'filter': filterSuite,
+ /*------------------------------------------------------------------------*/
- // get listeners
- 'listeners': listeners,
+ _.extend(Suite.prototype, {
- // unregister listeners
- 'off': off,
+ /**
+ * The number of benchmarks in the suite.
+ *
+ * @memberOf Benchmark.Suite
+ * @type Number
+ */
+ 'length': 0,
- // register listeners
- 'on': on,
+ /**
+ * A flag to indicate if the suite is aborted.
+ *
+ * @memberOf Benchmark.Suite
+ * @type Boolean
+ */
+ 'aborted': false,
- // resets all benchmarks in the suite
- 'reset': resetSuite,
+ /**
+ * A flag to indicate if the suite is running.
+ *
+ * @memberOf Benchmark.Suite
+ * @type Boolean
+ */
+ 'running': false
+ });
- // runs all benchmarks in the suite
- 'run': runSuite,
+ _.extend(Suite.prototype, {
+ 'abort': abortSuite,
+ 'add': add,
+ 'clone': cloneSuite,
+ 'emit': emit,
+ 'filter': filterSuite,
+ 'join': arrayRef.join,
+ 'listeners': listeners,
+ 'off': off,
+ 'on': on,
+ 'pop': arrayRef.pop,
+ 'push': arrayRef.push,
+ 'reset': resetSuite,
+ 'run': runSuite,
+ 'reverse': arrayRef.reverse,
+ 'shift': shift,
+ 'sort': arrayRef.sort,
+ 'splice': arrayRef.splice,
+ 'unshift': arrayRef.unshift
+ });
- // array methods
- 'concat': concat,
+ /*------------------------------------------------------------------------*/
- 'reverse': reverse,
+ // expose Deferred, Event, and Suite
+ _.extend(Benchmark, {
+ 'Deferred': Deferred,
+ 'Event': Event,
+ 'Suite': Suite
+ });
- 'shift': shift,
+ /*------------------------------------------------------------------------*/
- 'slice': slice,
+ // 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) {
+ var func = arrayRef[methodName];
- 'splice': splice,
+ Suite.prototype[methodName] = function() {
+ var value = this,
+ result = func.apply(value, arguments);
- 'unshift': unshift
- });
+ if (value.length === 0) {
+ delete value[0];
+ }
+ return result;
+ };
+ });
+ }
+ // trigger clock's lazy define early to avoid a security error
+ if (support.air) {
+ clock({ '_original': { 'fn': noop, 'count': 1, 'options': {} } });
+ }
+ return Benchmark;
+ }
/*--------------------------------------------------------------------------*/
- // expose Deferred, Event and Suite
- extend(Benchmark, {
- 'Deferred': Deferred,
- 'Event': Event,
- 'Suite': Suite
- });
-
// expose Benchmark
// some AMD build optimizers, like r.js, check for specific condition patterns like the following:
if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
// define as an anonymous module so, through path mapping, it can be aliased
- define(function() {
- return Benchmark;
+ define(['lodash', 'platform'], function(_, platform) {
+ return runInContext({
+ '_': _,
+ 'platform': platform
+ });
});
}
- // check for `exports` after `define` in case a build optimizer adds an `exports` object
- else if (freeExports) {
- // in Node.js or RingoJS v0.8.0+
- if (typeof module == 'object' && module && module.exports == freeExports) {
- (module.exports = Benchmark).Benchmark = Benchmark;
+ else {
+ var Benchmark = runInContext();
+
+ // check for `exports` after `define` in case a build optimizer adds an `exports` object
+ if (freeExports && !freeExports.nodeType) {
+ // in Node.js or RingoJS v0.8.0+
+ if (freeModule) {
+ (freeModule.exports = Benchmark).Benchmark = Benchmark;
+ }
+ // in Narwhal or RingoJS v0.7.0-
+ else {
+ freeExports.Benchmark = Benchmark;
+ }
}
- // in Narwhal or RingoJS v0.7.0-
+ // in a browser or Rhino
else {
- freeExports.Benchmark = Benchmark;
+ window.Benchmark = Benchmark;
}
}
- // in a browser or Rhino
- else {
- // use square bracket notation so Closure Compiler won't munge `Benchmark`
- // http://code.google.com/closure/compiler/docs/api-tutorial3.html#export
- window['Benchmark'] = Benchmark;
- }
-
- // trigger clock's lazy define early to avoid a security error
- if (support.air) {
- clock({ '_original': { 'fn': noop, 'count': 1, 'options': {} } });
- }
}(this));
diff --git a/vendor/platform.js/README.md b/vendor/platform.js/README.md
index c2f1cb632f..219c00b2fd 100644
--- a/vendor/platform.js/README.md
+++ b/vendor/platform.js/README.md
@@ -8,7 +8,7 @@ Platform.js is for informational purposes only and **not** intended as a substit
## BestieJS
-Platform.js is part of the BestieJS *"Best in Class"* module collection. This means we promote solid browser/environment support, ES5 precedents, unit testing, and plenty of documentation.
+Platform.js is part of the BestieJS *"Best in Class"* module collection. This means we promote solid browser/environment support, ES5+ precedents, unit testing, and plenty of documentation.
## Documentation
@@ -18,7 +18,7 @@ For a list of upcoming features, check out our [roadmap](https://github.com/best
## Support
-Platform.js has been tested in at least Adobe AIR 3.1, Chrome 5-21, Firefox 1-14, IE 6-9, Opera 9.25-12, Safari 3-6, Node.js 0.8.6, Narwhal 0.3.2, RingoJS 0.8, and Rhino 1.7RC5.
+Platform.js has been tested in at least Chrome 5~25, Firefox 1~19, IE 6-10, Opera 9.25-12, Safari 3-6, Node.js 0.4.8-0.10.1, Narwhal 0.3.2, PhantomJS 1.8.1, RingoJS 0.9, and Rhino 1.7RC5.
## Installation and usage
diff --git a/vendor/platform.js/platform.js b/vendor/platform.js/platform.js
index d643c18833..30ce3e1c37 100644
--- a/vendor/platform.js/platform.js
+++ b/vendor/platform.js/platform.js
@@ -20,11 +20,14 @@
var reOpera = /Opera/;
/** Used to resolve a value's internal [[Class]] */
- var toString = {}.toString;
+ var toString = Object.prototype.toString;
/** Detect Java environment */
var java = /Java/.test(getClassOf(window.java)) && window.java;
+ /** Detect Rhino */
+ var rhino = java && getClassOf(window.environment) == 'Environment';
+
/** A character to represent alpha */
var alpha = java ? 'a' : '\u03b1';
@@ -581,10 +584,11 @@
// detect stubborn layout engines
if (layout == 'iCab' && parseFloat(version) > 3) {
layout = ['WebKit'];
- } else if (data =
- /Opera/.test(name) && 'Presto' ||
- /\b(?:Midori|Nook|Safari)\b/i.test(ua) && 'WebKit' ||
- !layout && /\bMSIE\b/i.test(ua) && (/^Mac/.test(os) ? 'Tasman' : 'Trident')) {
+ } else if ((data =
+ /Opera/.test(name) && 'Presto' ||
+ /\b(?:Midori|Nook|Safari)\b/i.test(ua) && 'WebKit' ||
+ !layout && /\bMSIE\b/i.test(ua) && (/^Mac/.test(os) ? 'Tasman' : 'Trident')
+ )) {
layout = [data];
}
// leverage environment features
@@ -597,7 +601,7 @@
arch = data.getProperty('os.arch');
os = os || data.getProperty('os.name') + ' ' + data.getProperty('os.version');
}
- if (typeof exports == 'object' && exports) {
+ if (freeExports) {
// if `thisBinding` is the [ModuleScope]
if (thisBinding == oldWin && typeof system == 'object' && (data = [system])[0]) {
os || (os = data[0].os || null);
@@ -610,23 +614,28 @@
name = 'Narwhal';
}
}
- } else if (typeof process == 'object' && (data = process)) {
+ }
+ else if (typeof process == 'object' && (data = process)) {
name = 'Node.js';
arch = data.arch;
os = data.platform;
version = /[\d.]+/.exec(data.version)[0];
}
- } else if (getClassOf(window.environment) == 'Environment') {
+ else if (rhino) {
+ name = 'Rhino';
+ }
+ }
+ else if (rhino) {
name = 'Rhino';
}
}
// detect Adobe AIR
- else if (getClassOf(data = window.runtime) == 'ScriptBridgingProxyObject') {
+ else if (getClassOf((data = window.runtime)) == 'ScriptBridgingProxyObject') {
name = 'Adobe AIR';
os = data.flash.system.Capabilities.os;
}
// detect PhantomJS
- else if (getClassOf(data = window.phantom) == 'RuntimeObject') {
+ else if (getClassOf((data = window.phantom)) == 'RuntimeObject') {
name = 'PhantomJS';
version = (data = data.version || null) && (data.major + '.' + data.minor + '.' + data.patch);
}
@@ -646,9 +655,10 @@
}
// detect prerelease phases
if (version && (data =
- /(?:[ab]|dp|pre|[ab]\d+pre)(?:\d+\+?)?$/i.exec(version) ||
- /(?:alpha|beta)(?: ?\d)?/i.exec(ua + ';' + (useFeatures && nav.appMinorVersion)) ||
- /\bMinefield\b/i.test(ua) && 'a')) {
+ /(?:[ab]|dp|pre|[ab]\d+pre)(?:\d+\+?)?$/i.exec(version) ||
+ /(?:alpha|beta)(?: ?\d)?/i.exec(ua + ';' + (useFeatures && nav.appMinorVersion)) ||
+ /\bMinefield\b/i.test(ua) && 'a'
+ )) {
prerelease = /b/i.test(data) ? 'beta' : 'alpha';
version = version.replace(RegExp(data + '\\+?$'), '') +
(prerelease == 'beta' ? beta : alpha) + (/\d+\+?/.exec(data) || '');
@@ -689,8 +699,9 @@
// detect BlackBerry OS version
// http://docs.blackberry.com/en/developers/deliverables/18169/HTTP_headers_sent_by_BB_Browser_1234911_11.jsp
else if (/BlackBerry/.test(product) && (data =
- (RegExp(product.replace(/ +/g, ' *') + '/([.\\d]+)', 'i').exec(ua) || 0)[1] ||
- version)) {
+ (RegExp(product.replace(/ +/g, ' *') + '/([.\\d]+)', 'i').exec(ua) || 0)[1] ||
+ version
+ )) {
os = 'Device Software ' + data;
version = null;
}
@@ -705,7 +716,7 @@
/Windows XP/.test(os) && version > 8 ||
version == 8 && !/Trident/.test(ua)
))
- ) && !reOpera.test(data = parse.call(forOwn, ua.replace(reOpera, '') + ';')) && data.name) {
+ ) && !reOpera.test((data = parse.call(forOwn, ua.replace(reOpera, '') + ';'))) && data.name) {
// when "indentifying", the UA contains both Opera and the other browser's name
data = 'ing as ' + data.name + ((data = data.version) ? ' ' + data : '');
@@ -793,7 +804,7 @@
}
}
// strip incorrect OS versions
- if (version && version.indexOf(data = /[\d.]+$/.exec(os)) == 0 &&
+ if (version && version.indexOf((data = /[\d.]+$/.exec(os))) == 0 &&
ua.indexOf('/' + data + '-') > -1) {
os = trim(os.replace(data, ''));
}
@@ -981,7 +992,7 @@
});
}
// check for `exports` after `define` in case a build optimizer adds an `exports` object
- else if (freeExports) {
+ else if (freeExports && !freeExports.nodeType) {
// in Narwhal, Node.js, or RingoJS
forOwn(parse(), function(value, key) {
freeExports[key] = value;
@@ -989,8 +1000,6 @@
}
// in a browser or Rhino
else {
- // use square bracket notation so Closure Compiler won't munge `platform`
- // http://code.google.com/closure/compiler/docs/api-tutorial3.html#export
- window['platform'] = parse();
+ window.platform = parse();
}
}(this));
diff --git a/vendor/qunit-clib/README.md b/vendor/qunit-clib/README.md
index 7c2edfa8f5..7b44ea8562 100644
--- a/vendor/qunit-clib/README.md
+++ b/vendor/qunit-clib/README.md
@@ -1,7 +1,7 @@
-# QUnit CLIB v1.2.0
+# QUnit CLIB v1.3.0
## command-line interface boilerplate
-QUnit CLIB helps extend QUnit's CLI support to many common CLI environments.
+QUnit CLIB helps extend QUnit’s CLI support to many common CLI environments.
## Screenshot
@@ -9,26 +9,29 @@ QUnit CLIB helps extend QUnit's CLI support to many common CLI environments.
## Support
-QUnit CLIB has been tested in at least Node.js 0.4.8-0.8.19, Narwhal v0.3.2, PhantomJS 1.8.1, RingoJS v0.9, and Rhino v1.7RC5.
+QUnit CLIB has been tested in at least Node.js 0.4.8-0.10.1, Narwhal 0.3.2, PhantomJS 1.8.1, RingoJS 0.9, and Rhino 1.7RC5.
## Usage
```js
-(function(window) {
+;(function(window) {
+ 'use strict';
// use a single "load" function
var load = typeof require == 'function' ? require : window.load;
// load QUnit and CLIB if needed
- var QUnit =
- window.QUnit || (
- window.addEventListener || (window.addEventListener = Function.prototype),
- window.setTimeout || (window.setTimeout = Function.prototype),
- window.QUnit = load('path/to/qunit.js') || window.QUnit,
- load('path/to/qunit-clib.js'),
- window.addEventListener === Function.prototype && delete window.addEventListener,
+ var QUnit = (function() {
+ var noop = Function.prototype;
+ return window.QUnit || (
+ window.addEventListener || (window.addEventListener = noop),
+ window.setTimeout || (window.setTimeout = noop),
+ window.QUnit = load('../vendor/qunit/qunit/qunit.js') || window.QUnit,
+ (load('../vendor/qunit-clib/qunit-clib.js') || { 'runInContext': noop }).runInContext(window),
+ addEventListener === noop && delete window.addEventListener,
window.QUnit
);
+ }());
// explicitly call `QUnit.module()` instead of `module()`
// in case we are in a CLI environment
@@ -38,9 +41,8 @@ QUnit CLIB has been tested in at least Node.js 0.4.8-0.8.19, Narwhal v0.3.2, Pha
// ...
});
- // must call `QUnit.start()` if using QUnit < 1.3.0 with Node.js or any
- // version of QUnit with Narwhal, PhantomJS, Rhino, or RingoJS
- if (!window.document) {
+ // call `QUnit.start()` for Narwhal, Node.js, PhantomJS, Rhino, and RingoJS
+ if (!window.document || window.phantom) {
QUnit.start();
}
}(typeof global == 'object' && global || this));
@@ -49,7 +51,6 @@ QUnit CLIB has been tested in at least Node.js 0.4.8-0.8.19, Narwhal v0.3.2, Pha
## Footnotes
1. QUnit v1.3.0 does not work with Narwhal or Ringo < v0.8.0
-
2. Rhino v1.7RC4 does not support timeout fallbacks `clearTimeout` and `setTimeout`
## Author
diff --git a/vendor/qunit-clib/qunit-clib.js b/vendor/qunit-clib/qunit-clib.js
index 7174febe15..eb89ef3c1c 100644
--- a/vendor/qunit-clib/qunit-clib.js
+++ b/vendor/qunit-clib/qunit-clib.js
@@ -1,5 +1,5 @@
/*!
- * QUnit CLI Boilerplate v1.2.0
+ * QUnit CLI Boilerplate v1.3.0
* Copyright 2011-2012 John-David Dalton
* Based on a gist by Jörn Zaefferer
* Available under MIT license
@@ -7,12 +7,28 @@
;(function(window) {
'use strict';
+ /** 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;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
/**
- * Timeout fallbacks based on the work of Andrea Giammarchi and Weston C.
- * https://github.com/WebReflection/wru/blob/master/src/rhinoTimers.js
- * http://stackoverflow.com/questions/2261705/how-to-run-a-javascript-function-asynchronously-without-using-settimeout
+ * Installs the CLI boilerplate additions on the given `context` object.
+ *
+ * @memberOf exports
+ * @param {Object} context The context object.
*/
- (function() {
+ function runInContext(context) {
+ // exit early if no `context` is provided or if `QUnit` does not exist
+ if (!context || !context.QUnit) {
+ return;
+ }
/**
* Schedules timer-based callbacks.
@@ -29,7 +45,7 @@
// https://bugzilla.mozilla.org/show_bug.cgi?id=775566
var task = ids[++counter] = new JavaAdapter(java.util.TimerTask, {
'run': function() {
- fn.apply(window, args);
+ fn.apply(context, args);
}
});
// support non-functions
@@ -53,7 +69,7 @@
/**
* Clears the delay set by `setInterval` or `setTimeout`.
*
- * @memberOf window
+ * @memberOf context
* @param {Number} id The ID of the timeout to be cleared.
*/
function clearTimer(id) {
@@ -67,7 +83,7 @@
/**
* Executes a code snippet or function repeatedly, with a delay between each call.
*
- * @memberOf window
+ * @memberOf context
* @param {Function|String} fn The function to call or string to evaluate.
* @oaram {Number} delay The number of milliseconds to delay each `fn` call.
* @param [arg1, arg2, ...] Arguments to invoke `fn` with.
@@ -80,7 +96,7 @@
/**
* Executes a code snippet or a function after specified delay.
*
- * @memberOf window
+ * @memberOf context
* @param {Function|String} fn The function to call or string to evaluate.
* @oaram {Number} delay The number of milliseconds to delay the `fn` call.
* @param [arg1, arg2, ...] Arguments to invoke `fn` with.
@@ -90,29 +106,17 @@
return schedule(fn, delay, slice.call(arguments, 2));
}
- try {
- var counter = 0,
- ids = {},
- slice = Array.prototype.slice,
- timer = new java.util.Timer;
-
- window.clearInterval =
- window.clearTimeout = clearTimer;
- window.setInterval = setInterval;
- window.setTimeout = setTimeout;
- } catch(e) { }
- }());
+ /*------------------------------------------------------------------------*/
- /*--------------------------------------------------------------------------*/
+ /** Add `console.log()` support for Narwhal, Rhino, and RingoJS */
+ var console = context.console || (context.console = { 'log': context.print });
- (function() {
+ /** Shorten `context.QUnit.QUnit` to `context.QUnit` */
+ var QUnit = context.QUnit = context.QUnit.QUnit || context.QUnit;
/** Used as a horizontal rule in console output */
var hr = '----------------------------------------';
- /** Shorten `window.QUnit.QUnit` to `window.QUnit` */
- window.QUnit && (QUnit = QUnit.QUnit || QUnit);
-
/**
* A logging callback triggered when all testing is completed.
*
@@ -134,14 +138,14 @@
console.log(' Finished in ' + details.runtime + ' milliseconds.');
console.log(hr);
- // exit out of Rhino
+ // exit out of Narhwal, Rhino, or Ringo
try {
quit();
} catch(e) { }
// exit out of Node.js or PhantomJS
try {
- var process = window.process || window.phantom;
+ var process = context.process || context.phantom;
if (details.failed) {
console.error('Error: ' + details.failed + ' of ' + details.total + ' tests failed.');
process.exit(1);
@@ -247,23 +251,42 @@
*/
'assertions': []
};
- }());
- /*--------------------------------------------------------------------------*/
+ /*------------------------------------------------------------------------*/
+
+ // Timeout fallbacks based on the work of Andrea Giammarchi and Weston C.
+ // https://github.com/WebReflection/wru/blob/master/src/rhinoTimers.js
+ // http://stackoverflow.com/questions/2261705/how-to-run-a-javascript-function-asynchronously-without-using-settimeout
+ try {
+ var counter = 0,
+ ids = {},
+ slice = Array.prototype.slice,
+ timer = new java.util.Timer;
+
+ context.clearInterval =
+ context.clearTimeout = clearTimer;
+ context.setInterval = setInterval;
+ context.setTimeout = setTimeout;
+ } catch(e) { }
- // expose shortcuts
- // exclude `module` because some environments have it as a built-in object
- ('asyncTest deepEqual equal equals expect notDeepEqual notEqual notStrictEqual ' +
- 'ok raises same start stop strictEqual test throws').replace(/\S+/g, function(methodName) {
- window[methodName] = QUnit[methodName];
- });
+ // expose shortcuts
+ // exclude `module` because some environments have it as a built-in object
+ ('asyncTest deepEqual equal equals expect notDeepEqual notEqual notStrictEqual ' +
+ 'ok raises same start stop strictEqual test throws').replace(/\S+/g, function(methodName) {
+ context[methodName] = QUnit[methodName];
+ });
- // add `console.log()` support for Narwhal, Rhino, and RingoJS
- if (!window.console && window.print) {
- window.console = { 'log': window.print };
+ // must call `QUnit.start()` in the test file if using QUnit < 1.3.0 with
+ // Node.js or any version of QUnit with Narwhal, PhantomJS, Rhino, or RingoJS
+ QUnit.init();
}
- // must call `QUnit.start()` in the test file if using QUnit < 1.3.0 with
- // Node.js or any version of QUnit with Narwhal, PhantomJS, Rhino, or RingoJS
- QUnit.init();
-}(typeof global == 'object' && global || this));
+ /*--------------------------------------------------------------------------*/
+
+ // expose QUnit CLIB
+ if (freeExports) {
+ freeExports.runInContext = runInContext;
+ } else {
+ runInContext(window);
+ }
+}(this));
diff --git a/vendor/requirejs/require.js b/vendor/requirejs/require.js
index 5b2687543f..062516acd9 100644
--- a/vendor/requirejs/require.js
+++ b/vendor/requirejs/require.js
@@ -1,5 +1,5 @@
/** vim: et:ts=4:sw=4:sts=4
- * @license RequireJS 2.1.4 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
+ * @license RequireJS 2.1.5 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
* Available via the MIT or new BSD license.
* see: http://github.com/jrburke/requirejs for details
*/
@@ -12,7 +12,7 @@ var requirejs, require, define;
(function (global) {
var req, s, head, baseElement, dataMain, src,
interactiveScript, currentlyAddingScript, mainScript, subPath,
- version = '2.1.4',
+ version = '2.1.5',
commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
jsSuffixRegExp = /\.js$/,
@@ -191,15 +191,21 @@ var requirejs, require, define;
var inCheckLoaded, Module, context, handlers,
checkLoadedTimeoutId,
config = {
+ //Defaults. Do not set a default for map
+ //config to speed up normalize(), which
+ //will run faster if there is no default.
waitSeconds: 7,
baseUrl: './',
paths: {},
pkgs: {},
shim: {},
- map: {},
config: {}
},
registry = {},
+ //registry of just enabled modules, to speed
+ //cycle breaking code when lots of modules
+ //are registered, but not activated.
+ enabledRegistry = {},
undefEvents = {},
defQueue = [],
defined = {},
@@ -295,7 +301,7 @@ var requirejs, require, define;
}
//Apply map config if available.
- if (applyMap && (baseParts || starMap) && map) {
+ if (applyMap && map && (baseParts || starMap)) {
nameParts = name.split('/');
for (i = nameParts.length; i > 0; i -= 1) {
@@ -576,6 +582,7 @@ var requirejs, require, define;
function cleanRegistry(id) {
//Clean up machinery used for waiting modules.
delete registry[id];
+ delete enabledRegistry[id];
}
function breakCycle(mod, traced, processed) {
@@ -624,7 +631,7 @@ var requirejs, require, define;
inCheckLoaded = true;
//Figure out the state of all the modules.
- eachProp(registry, function (mod) {
+ eachProp(enabledRegistry, function (mod) {
map = mod.map;
modId = map.id;
@@ -805,7 +812,7 @@ var requirejs, require, define;
},
/**
- * Checks is the module is ready to define itself, and if so,
+ * Checks if the module is ready to define itself, and if so,
* define it.
*/
check: function () {
@@ -883,7 +890,7 @@ var requirejs, require, define;
}
//Clean up
- delete registry[id];
+ cleanRegistry(id);
this.defined = true;
}
@@ -1049,6 +1056,7 @@ var requirejs, require, define;
},
enable: function () {
+ enabledRegistry[this.map.id] = this;
this.enabled = true;
//Set flag mentioning that the module is enabling,
@@ -1208,6 +1216,7 @@ var requirejs, require, define;
Module: Module,
makeModuleMap: makeModuleMap,
nextTick: req.nextTick,
+ onError: onError,
/**
* Set a configuration for the context.
@@ -1234,6 +1243,9 @@ var requirejs, require, define;
eachProp(cfg, function (value, prop) {
if (objs[prop]) {
if (prop === 'map') {
+ if (!config.map) {
+ config.map = {};
+ }
mixin(config[prop], value, true, true);
} else {
mixin(config[prop], value, true);
@@ -1345,7 +1357,7 @@ var requirejs, require, define;
//Synchronous access to one module. If require.get is
//available (as in the Node adapter), prefer that.
if (req.get) {
- return req.get(context, deps, relMap);
+ return req.get(context, deps, relMap, localRequire);
}
//Normalize module name, if it contains . or ..
@@ -1396,7 +1408,7 @@ var requirejs, require, define;
* plain URLs like nameToUrl.
*/
toUrl: function (moduleNamePlusExt) {
- var ext, url,
+ var ext,
index = moduleNamePlusExt.lastIndexOf('.'),
segment = moduleNamePlusExt.split('/')[0],
isRelative = segment === '.' || segment === '..';
@@ -1408,9 +1420,8 @@ var requirejs, require, define;
moduleNamePlusExt = moduleNamePlusExt.substring(0, index);
}
- url = context.nameToUrl(normalize(moduleNamePlusExt,
- relMap && relMap.id, true), ext || '.fake');
- return ext ? url : url.substring(0, url.length - 5);
+ return context.nameToUrl(normalize(moduleNamePlusExt,
+ relMap && relMap.id, true), ext, true);
},
defined: function (id) {
@@ -1529,7 +1540,7 @@ var requirejs, require, define;
* it is assumed to have already been normalized. This is an
* internal API, not a public one. Use toUrl for the public API.
*/
- nameToUrl: function (moduleName, ext) {
+ nameToUrl: function (moduleName, ext, skipExt) {
var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url,
parentPath;
@@ -1578,7 +1589,7 @@ var requirejs, require, define;
//Join the path parts together, then figure out if baseUrl is needed.
url = syms.join('/');
- url += (ext || (/\?/.test(url) ? '' : '.js'));
+ url += (ext || (/\?/.test(url) || skipExt ? '' : '.js'));
url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url;
}
@@ -1817,7 +1828,7 @@ var requirejs, require, define;
node.attachEvent('onreadystatechange', context.onScriptLoad);
//It would be great to add an error handler here to catch
//404s in IE9+. However, onreadystatechange will fire before
- //the error handler, so that does not help. If addEvenListener
+ //the error handler, so that does not help. If addEventListener
//is used, then IE will fire error before load, but we cannot
//use that pathway given the connect.microsoft.com issue
//mentioned above about not doing the 'script execute,
@@ -1846,16 +1857,24 @@ var requirejs, require, define;
return node;
} else if (isWebWorker) {
- //In a web worker, use importScripts. This is not a very
- //efficient use of importScripts, importScripts will block until
- //its script is downloaded and evaluated. However, if web workers
- //are in play, the expectation that a build has been done so that
- //only one script needs to be loaded anyway. This may need to be
- //reevaluated if other use cases become common.
- importScripts(url);
-
- //Account for anonymous modules
- context.completeLoad(moduleName);
+ try {
+ //In a web worker, use importScripts. This is not a very
+ //efficient use of importScripts, importScripts will block until
+ //its script is downloaded and evaluated. However, if web workers
+ //are in play, the expectation that a build has been done so that
+ //only one script needs to be loaded anyway. This may need to be
+ //reevaluated if other use cases become common.
+ importScripts(url);
+
+ //Account for anonymous modules
+ context.completeLoad(moduleName);
+ } catch (e) {
+ context.onError(makeError('importscripts',
+ 'importScripts failed for ' +
+ moduleName + ' at ' + url,
+ e,
+ [moduleName]));
+ }
}
};