diff --git a/.gitignore b/.gitignore
index 58b805fea8..37a7af5c7d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
+*.custom.*
.DS_Store
+dist/
node_modules/
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
index 47d156244d..ddd6fc35d8 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -16,3 +16,9 @@
[submodule "vendor/uglifyjs"]
path = vendor/uglifyjs
url = git://github.com/mishoo/UglifyJS.git
+[submodule "vendor/underscore"]
+ path = vendor/underscore
+ url = git://github.com/documentcloud/underscore.git
+[submodule "vendor/backbone"]
+ path = vendor/backbone
+ url = git://github.com/documentcloud/backbone.git
diff --git a/.npmignore b/.npmignore
index db58007bb8..1c8397f333 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,5 +1,19 @@
+*.custom.*
+*.min.*
.*
-dist/*
+build.*
+build/
+dist/
doc/*.php
+node_modules/
+perf/*.html
+perf/*.sh
test/*.html
-vendor/
\ No newline at end of file
+test/*.sh
+vendor/backbone/
+vendor/closure-compiler/
+vendor/docdown/
+vendor/firebug-lite/
+vendor/requirejs/
+vendor/uglifyjs/
+vendor/underscore/
\ No newline at end of file
diff --git a/README.md b/README.md
index cde5bfc880..0a439a9694 100644
--- a/README.md
+++ b/README.md
@@ -1,40 +1,66 @@
-# Lo-Dash v0.1.0
+# Lo-Dash v0.2.0
-A drop-in replacement for [Underscore.js](https://github.com/documentcloud/underscore/) that delivers up to [8x performance improvements](http://jsperf.com/lodash-underscore#chart=bar), [bug fixes](https://github.com/bestiejs/lodash/blob/master/test/test.js#L71), and additional features.
+A drop-in replacement for Underscore.js, from the devs behind [jsPerf.com](http://jsperf.com), that delivers [performance improvements](http://jsperf.com/lodash-underscore#filterby=family), bug fixes, and additional features.
-## BestieJS
+Lo-Dash’s performance is gained by avoiding slower native methods, instead opting for simplified non-ES5 compliant methods optimized for common usage, and by leveraging function compilation to reduce the number of overall function calls.
-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.
+## Dive in
-## Documentation
+We’ve got [API docs](http://lodash.com/docs) and [unit tests](http://lodash.com/tests).
-The documentation for Lo-Dash can be viewed here: [/doc/README.md](https://github.com/bestiejs/lodash/blob/master/doc/README.md#readme)
+For a list of upcoming features, check out our [roadmap](https://github.com/bestiejs/lodash/wiki/Roadmap).
-Underscore's [documentation](http://documentcloud.github.com/underscore/) may also be used.
+## Screencasts
-For a list of upcoming features, check out our [roadmap](https://github.com/bestiejs/lodash/wiki/Roadmap).
+For more information check out these screencasts over Lo-Dash:
+
+ * [Introducing Lo-Dash](http://dl.dropbox.com/u/513327/allyoucanleet/post/20/file/screencast.mp4)
+ * [Optimizations and custom builds](http://dl.dropbox.com/u/513327/allyoucanleet/post/21/file/screencast.mp4)
+
+## Features
+
+ * AMD loader support
+ * [_.bind](http://lodash.com/docs#_bindfunc--arg1-arg2-) supports *"lazy"* binding
+ * [_.debounce](http://lodash.com/docs#_debouncefunc-wait-immediate)’ed functions match [_.throttle](http://lodash.com/docs#_throttlefunc-wait)’ed functions’ return value behavior
+ * [_.forEach](http://lodash.com/docs#_foreachcollection-callback--thisarg) is chainable
+ * [_.groupBy](http://lodash.com/docs#_groupbycollection-callback--thisarg) accepts a third `thisArg` argument
+ * [_.partial](http://lodash.com/docs#_partialfunc--arg1-arg2-) for more functional fun
+ * [_.size](http://lodash.com/docs#_sizecollection) returns the `length` of string values
+
+## Support
+
+Lo-Dash has been tested in at least Chrome 5-19, Firefox 1.5-12, IE 6-9, Opera 9.25-11.64, Safari 3.0.4-5.1.3, Node.js 0.4.8-0.6.18, Narwhal 0.3.2, RingoJS 0.8, and Rhino 1.7RC3.
-## So What's The Secret?
+## Custom builds
-Lo-Dash's performance is gained by avoiding native methods, instead opting for simplified non-ES5 compliant methods optimized for common usage, and by leveraging function compilation to reduce the number of overall function calls.
+Custom builds make it easy to create lightweight versions of Lo-Dash containing only the methods you need.
+We handle all the method dependency and alias mapping for you.
-## What else?
+Custom builds may be created in two ways:
-Lo-Dash comes with AMD loader support baked in, chainable `_.each`, and will [soon address](https://github.com/bestiejs/lodash/wiki/Roadmap) cross-browser object iteration issues.
+ 1. Use the`include` argument to pass the names of the methods to include in the build.
+~~~ bash
+node build include=each,filter,map,noConflict
+node build include="each, filter, map, noConflict"
+~~~
-## Screencast
+ 2. Use the `exclude` argument to pass the names of the methods to exclude from the build.
+~~~ bash
+node build exclude=isNaN,isUndefined,union,zip
+node build exclude="isNaN, isUndefined, union, zip"
+~~~
-For more information check out [this screencast](http://dl.dropbox.com/u/513327/allyoucanleet/post/20/file/screencast.mp4) over Lo-Dash.
+Custom builds are saved to `lodash.custom.js` and `lodash.custom.min.js`.
## Installation and usage
-In a browser:
+In browsers:
~~~ html
~~~
-Via [npm](http://npmjs.org/):
+Using [npm](http://npmjs.org/):
~~~ bash
npm install lodash
@@ -61,15 +87,12 @@ load('lodash.js');
In an AMD loader like [RequireJS](http://requirejs.org/):
~~~ js
-// opt-in
-define.amd.lodash = true;
-
require({
'paths': {
- 'lodash': 'path/to/lodash'
+ 'underscore': 'path/to/lodash'
}
},
-['lodash'], function(_) {
+['underscore'], function(_) {
console.log(_.VERSION);
});
~~~
@@ -91,7 +114,101 @@ cd lodash
git submodule update --init
~~~
-Feel free to fork and send pull requests if you see improvements!
+## Closed Underscore.js issues
+
+ * Fix Firefox, IE, Opera, and Safari object iteration bugs [#376](https://github.com/documentcloud/underscore/issues/376)
+ * Handle arrays with `undefined` values correctly in IE < 9 [#601](https://github.com/documentcloud/underscore/issues/601)
+ * Methods should work on pages with incorrectly shimmed native methods [#7](https://github.com/documentcloud/underscore/issues/7)
+ * Register as AMD module, but still export to global [#431](https://github.com/documentcloud/underscore/pull/431)
+ * `_.forEach` should be chainable [#142](https://github.com/documentcloud/underscore/issues/142)
+ * `_isNaN(new Number(NaN))` should return `true`
+ * `_.reduceRight` should pass correct callback arguments when iterating objects
+ * `_.size` should return the `length` of string values
+
+## Optimized methods (50+)
+
+ * `_.bind`
+ * `_.bindAll`
+ * `_.clone`
+ * `_.compact`
+ * `_.contains`, `_.include`
+ * `_.defaults`
+ * `_.defer`
+ * `_.difference`
+ * `_.each`
+ * `_.escape`
+ * `_.every`, `_.all`
+ * `_.extend`
+ * `_.filter`, `_.select`
+ * `_.find`, `_.detect`
+ * `_.flatten`
+ * `_.forEach`, `_.each`
+ * `_.functions`, `_.methods`
+ * `_.groupBy`
+ * `_.indexOf`
+ * `_.intersection`, `_.intersect`
+ * `_.invoke`
+ * `_.isEmpty`
+ * `_.isEqual`
+ * `_.isFinite`
+ * `_.isObject`
+ * `_.isString`
+ * `_.keys`
+ * `_.lastIndexOf`
+ * `_.map`, `_.collect`
+ * `_.max`
+ * `_.memoize`
+ * `_.min`
+ * `_.mixin`
+ * `_.pick`
+ * `_.pluck`
+ * `_.reduce`, `_.foldl`, `_.inject`
+ * `_.reject`
+ * `_.result`
+ * `_.shuffle`
+ * `_.some`, `_.any`
+ * `_.sortBy`
+ * `_.sortedIndex`
+ * `_.template`
+ * `_.throttle`
+ * `_.toArray`
+ * `_.union`
+ * `_.uniq`, `_.unique`
+ * `_.values`
+ * `_.without`
+ * `_.wrap`
+ * `_.zip`
+ * plus all `_(...)` method wrappers
+
+## Changelog
+
+### v0.2.0
+
+ * Added custom build options
+ * Added default `_.templateSettings.variable` value
+ * Added *"lazy bind"* support to `_.bind`
+ * Added native method overwrite detection to avoid bad native shims
+ * Added support for more AMD build optimizers and aliasing as the *"underscore"* module
+ * Added `thisArg` to `_.groupBy`
+ * Added whitespace to compiled strings
+ * Added `_.partial`
+ * Commented the `iterationFactory` options object
+ * Ensured `_.max` and `_.min` support extremely large arrays
+ * Fixed IE < 9 `[DontEnum]` bug and Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1’s prototype property iteration bug
+ * Inlined `_.isFunction` calls.
+ * Made `_.debounce`’ed functions match `_.throttle`’ed functions’ return value behavior
+ * Made `_.escape` no longer translate the *">"* character
+ * Fixed `clearTimeout` typo
+ * Simplified all methods in the *"Arrays"* category
+ * Optimized `_.debounce`, `_.escape`, `_.flatten`, `_.forEach`, `_.groupBy`, `_.intersection`, `_.invoke`, `_.isObject`, `_.max`, `_.min`, `_.pick`, `_.shuffle`, `_.sortedIndex`, `_.template`, `_.throttle`, `_.union`, `_.uniq`
+
+### v0.1.0
+
+ * Initial release
+
+## 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.
## Author
@@ -102,3 +219,5 @@ Feel free to fork and send pull requests if you see improvements!
* [Kit Cambridge](http://kitcambridge.github.com/)
[](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter")
+* [Mathias Bynens](http://mathiasbynens.be/)
+ [](https://twitter.com/mathias "Follow @mathias on Twitter")
diff --git a/build.js b/build.js
index f1b52347f3..9b88cec177 100755
--- a/build.js
+++ b/build.js
@@ -2,253 +2,457 @@
;(function() {
'use strict';
- /** The Node filesystem, path, and child process modules */
+ /** The Node filesystem and path modules */
var fs = require('fs'),
- path = require('path'),
- spawn = require('child_process').spawn;
-
- /** The build directory containing the build scripts */
- var buildPath = path.join(__dirname, 'build');
-
- /** The directory where the Closure Compiler is located */
- var closurePath = path.join(__dirname, 'vendor', 'closure-compiler', 'compiler.jar');
-
- /** The distribution directory */
- var distPath = path.join(__dirname, 'dist');
+ path = require('path');
/** Load other modules */
- var preprocess = require(path.join(buildPath, 'pre-compile')),
- postprocess = require(path.join(buildPath, 'post-compile')),
- uglifyJS = require(path.join(__dirname, 'vendor', 'uglifyjs', 'uglify-js'));
-
- /** Used to shares values between multiple callbacks */
- var accumulator = {
- 'compiled': {},
- 'uglified': {}
+ var lodash = require(path.join(__dirname, 'lodash')),
+ minify = require(path.join(__dirname, 'build', 'minify'));
+
+ /** Flag used to specify a custom build */
+ var isCustom = false;
+
+ /** Shortcut used to convert array-like objects to arrays */
+ var slice = [].slice;
+
+ /** The lodash.js source */
+ var source = fs.readFileSync(path.join(__dirname, 'lodash.js'), 'utf8');
+
+ /** Used to associate aliases with their real names */
+ var aliasToRealMap = {
+ 'all': 'every',
+ 'any': 'some',
+ 'collect': 'map',
+ 'detect': 'find',
+ 'each': 'forEach',
+ 'foldl': 'reduce',
+ 'foldr': 'reduceRight',
+ 'head': 'first',
+ 'include': 'contains',
+ 'inject': 'reduce',
+ 'intersect': 'intersection',
+ 'methods': 'functions',
+ 'select': 'filter',
+ 'tail': 'rest',
+ 'take': 'first',
+ 'unique': 'uniq'
};
- /** Closure Compiler command-line options */
- var closureOptions = [
- '--compilation_level=ADVANCED_OPTIMIZATIONS',
- '--language_in=ECMASCRIPT5_STRICT',
- '--warning_level=QUIET'
- ];
-
- /** Gzip command-line options */
- var gzipOptions = ['-9f', '-c'];
+ /** Used to associate real names with their aliases */
+ var realToAliasMap = {
+ 'contains': ['include'],
+ 'every': ['all'],
+ 'filter': ['select'],
+ 'find': ['detect'],
+ 'first': ['head', 'take'],
+ 'forEach': ['each'],
+ 'functions': ['methods'],
+ 'intersection': ['intersect'],
+ 'map': ['collect'],
+ 'reduce': ['foldl', 'inject'],
+ 'reduceRight': ['foldr'],
+ 'rest': ['tail'],
+ 'some': ['any'],
+ 'uniq': ['unique']
+ };
- /** The pre-processed Lo-Dash source */
- var source = preprocess(fs.readFileSync(path.join(__dirname, 'lodash.js'), 'utf8'));
+ /** Used to track function dependencies */
+ var dependencyMap = {
+ 'after': [],
+ 'bind': [],
+ 'bindAll': ['bind'],
+ 'chain': [],
+ 'clone': ['extend', 'isArray'],
+ 'compact': [],
+ 'compose': [],
+ 'contains': ['createIterator'],
+ 'createIterator': [],
+ 'debounce': [],
+ 'defaults': ['createIterator'],
+ 'defer': [],
+ 'delay': [],
+ 'difference': ['indexOf'],
+ 'escape': [],
+ 'every': ['bind', 'createIterator', 'identity'],
+ 'extend': ['createIterator'],
+ 'filter': ['bind', 'createIterator', 'identity'],
+ 'find': ['createIterator'],
+ 'first': [],
+ 'flatten': ['isArray'],
+ 'forEach': ['bind', 'createIterator'],
+ 'functions': ['createIterator'],
+ 'groupBy': ['bind', 'createIterator'],
+ 'has': [],
+ 'identity': [],
+ 'indexOf': ['sortedIndex'],
+ 'initial': [],
+ 'intersection': ['every', 'indexOf'],
+ 'invoke': [],
+ 'isArguments': [],
+ 'isArray': [],
+ 'isBoolean': [],
+ 'isDate': [],
+ 'isElement': [],
+ 'isEmpty': ['createIterator'],
+ 'isEqual': [],
+ 'isFinite': [],
+ 'isFunction': [],
+ 'isNaN': [],
+ 'isNull': [],
+ 'isNumber': [],
+ 'isObject': [],
+ 'isRegExp': [],
+ 'isString': [],
+ 'isUndefined': [],
+ 'keys': ['createIterator'],
+ 'last': [],
+ 'lastIndexOf': [],
+ 'map': ['bind', 'createIterator', 'identity'],
+ 'max': ['bind'],
+ 'memoize': [],
+ 'min': ['bind'],
+ 'mixin': ['forEach'],
+ 'noConflict': [],
+ 'once': [],
+ 'partial': [],
+ 'pick': [],
+ 'pluck': ['createIterator'],
+ 'range': [],
+ 'reduce': ['bind', 'createIterator'],
+ 'reduceRight': ['bind', 'keys'],
+ 'reject': ['bind', 'createIterator', 'identity'],
+ 'rest': [],
+ 'result': [],
+ 'shuffle': [],
+ 'size': ['keys'],
+ 'some': ['bind', 'createIterator', 'identity'],
+ 'sortBy': ['bind', 'map', 'pluck'],
+ 'sortedIndex': [],
+ 'tap': [],
+ 'template': ['escape'],
+ 'throttle': [],
+ 'times': ['bind'],
+ 'toArray': ['values'],
+ 'union': ['indexOf'],
+ 'uniq': ['indexOf'],
+ 'uniqueId': [],
+ 'values': ['createIterator'],
+ 'without': ['indexOf'],
+ 'wrap': [],
+ 'zip': ['max', 'pluck']
+ };
/*--------------------------------------------------------------------------*/
/**
- * Invokes a process with the given `name`, `parameters`, and `source` (used as
- * the standard input). Yields the result to a `callback` function. The optional
- * `encoding` argument specifies the output stream encoding.
+ * Gets the aliases associated with a given `funcName`.
*
* @private
- * @param {String} name The name of the process.
- * @param {Array} parameters An array of arguments to proxy to the process.
- * @param {String} source The standard input to proxy to the process.
- * @param {String} [encoding] The expected encoding of the output stream.
- * @param {Function} callback The function to call once the process completes.
+ * @param {String} funcName The name of the function to get aliases for.
+ * @returns {Array} Returns an array of aliases.
*/
- function invoke(name, parameters, source, encoding, callback) {
- // the standard error stream, standard output stream, and process instance
- var error = '',
- output = '',
- process = spawn(name, parameters);
-
- // juggle arguments
- if (typeof encoding == 'string' && callback != null) {
- // explicitly set the encoding of the output stream if one is specified
- process.stdout.setEncoding(encoding);
- } else {
- callback = encoding;
- encoding = null;
- }
-
- process.stdout.on('data', function(data) {
- // append the data to the output stream
- output += data;
- });
-
- process.stderr.on('data', function(data) {
- // append the error message to the error stream
- error += data;
- });
-
- process.on('exit', function(status) {
- var exception = null;
- // `status` contains the process exit code
- if (status) {
- exception = new Error(error);
- exception.status = status;
- }
- callback(exception, output);
- });
-
- // proxy the standard input to the process
- process.stdin.end(source);
+ function getAliases(funcName) {
+ return realToAliasMap[funcName] || [];
}
- /*--------------------------------------------------------------------------*/
-
/**
- * Compresses a `source` string using the Closure Compiler. Yields the
- * minified result, and any exceptions encountered, to a `callback` function.
+ * Gets an array of depenants for a function by the given `funcName`.
*
* @private
- * @param {String} source The JavaScript source to minify.
- * @param {Function} callback The function to call once the process completes.
+ * @param {String} funcName The name of the function to query.
+ * @returns {Array} Returns an array of function dependants.
*/
- function closureCompile(source, callback) {
- console.log('Compressing lodash.js using the Closure Compiler...');
- invoke('java', ['-jar', closurePath].concat(closureOptions), source, callback);
+ function getDependants(funcName) {
+ // iterate over `dependencyMap`, adding the names of functions that
+ // have `funcName` as a dependency
+ return lodash.reduce(dependencyMap, function(result, dependencies, otherName) {
+ if (dependencies.indexOf(funcName) > -1) {
+ result.push(otherName);
+ }
+ return result;
+ }, []);
}
/**
- * Compresses a `source` string using UglifyJS. Yields the result to a
- * `callback` function. This function is synchronous; the `callback` is used
- * for symmetry.
+ * Gets an array of dependencies for a function of the given `funcName`.
*
* @private
- * @param {String} source The JavaScript source to minify.
- * @param {Function} callback The function to call once the process completes.
+ * @param {String} funcName The name of the function to query.
+ * @returns {Array} Returns an array of function dependencies.
*/
- function uglify(source, callback) {
- var exception,
- result,
- ugly = uglifyJS.uglify;
-
- console.log('Compressing lodash.js using UglifyJS...');
-
- try {
- result = ugly.gen_code(
- // enable unsafe transformations
- ugly.ast_squeeze_more(
- ugly.ast_squeeze(
- // munge variable and function names, excluding the special `define`
- // function exposed by AMD loaders
- ugly.ast_mangle(uglifyJS.parser.parse(source), {
- 'except': ['define']
- }
- ))), {
- 'ascii_only': true
- });
- } catch(e) {
- exception = e;
+ function getDependencies(funcName) {
+ var dependencies = dependencyMap[funcName],
+ result = [];
+
+ if (!dependencies) {
+ return result;
}
- // lines are restricted to 500 characters for consistency with the Closure Compiler
- callback(exception, result && ugly.split_lines(result, 500));
+ // recursively accumulate the dependencies of the `funcName` function, and
+ // the dependencies of its dependencies, and so on.
+ return dependencies.reduce(function(result, otherName) {
+ result.push.apply(result, getDependencies(otherName).concat(otherName));
+ return result;
+ }, result);
}
- /*--------------------------------------------------------------------------*/
-
/**
- * The `closureCompile()` callback.
+ * Gets the real name, not alias, of a given `funcName`.
*
* @private
- * @param {Object|Undefined} exception The error object.
- * @param {String} result The resulting minified source.
+ * @param {String} funcName The name of the function to resolve.
+ * @returns {String} Returns the real name.
*/
- function onClosureCompile(exception, result) {
- if (exception) {
- throw exception;
- }
- // store the post-processed Closure Compiler result and gzip it
- accumulator.compiled.source = result = postprocess(result);
- invoke('gzip', gzipOptions, result, 'binary', onClosureGzip);
+ function getRealName(funcName) {
+ return aliasToRealMap[funcName] || funcName;
}
/**
- * The Closure Compiler `gzip` callback.
+ * Determines if all functions of the given names have been removed from the `source`.
*
* @private
- * @param {Object|Undefined} exception The error object.
- * @param {String} result The resulting gzipped source.
+ * @param {String} source The source to inspect.
+ * @param {String} [funcName1, funcName2, ...] The names of functions to check.
+ * @returns {Boolean} Returns `true` if all functions have been removed, else `false`.
*/
- function onClosureGzip(exception, result) {
- if (exception) {
- throw exception;
- }
- // store the gzipped result and report the size
- accumulator.compiled.gzip = result;
- console.log('Done. Size: %d KB.', result.length);
+ function isRemoved(source) {
+ return slice.call(arguments, 1).every(function(funcName) {
+ return !matchFunction(source, funcName);
+ });
+ }
- // next, minify using UglifyJS
- uglify(source, onUglify);
+ /**
+ * Searches the `source` for a `funcName` function declaration, expression, or
+ * assignment and returns the matched snippet.
+ *
+ * @private
+ * @param {String} source The source to inspect.
+ * @param {String} funcName The name of the function to match.
+ * @returns {String} Returns the matched function snippet.
+ */
+ function matchFunction(source, funcName) {
+ var result = source.match(RegExp(
+ // match multi-line comment block (could be on a single line)
+ '\\n +/\\*[^*]*\\*+(?:[^/][^*]*\\*+)*/\\n' +
+ // begin non-capturing group
+ '(?:' +
+ // match a function declaration
+ '( +)function ' + funcName + '\\b[\\s\\S]+?\\n\\1}|' +
+ // match a variable declaration with `createIterator`
+ ' +var ' + funcName + ' *= *(?:[a-zA-Z]+ *\\|\\| *)?createIterator\\((?:{|[a-zA-Z])[\\s\\S]+?\\);|' +
+ // match a variable declaration with function expression
+ '( +)var ' + funcName + ' *= *(?:[a-zA-Z]+ *\\|\\| *)?function[\\s\\S]+?\\n\\2};' +
+ // end non-capturing group
+ ')\\n'
+ ));
+
+ return result ? result[0] : '';
}
/**
- * The `uglify()` callback.
+ * Removes the all references to `refName` from the `createIterator` source.
*
* @private
- * @param {Object|Undefined} exception The error object.
- * @param {String} result The resulting minified source.
+ * @param {String} source The source to process.
+ * @param {String} refName The name of the reference to remove.
+ * @returns {String} Returns the modified source.
*/
- function onUglify(exception, result) {
- if (exception) {
- throw exception;
- }
- // store the post-processed Uglified result and gzip it
- accumulator.uglified.source = postprocess(result);
- invoke('gzip', gzipOptions, result, 'binary', onUglifyGzip);
+ function removeFromCreateIterator(source, refName) {
+ var snippet = matchFunction(source, 'createIterator'),
+ modified = snippet.replace(RegExp('\\b' + refName + '\\b,? *', 'g'), '');
+
+ return source.replace(snippet, modified);
}
/**
- * The UglifyJS `gzip` callback.
+ * Removes the `funcName` function declaration, expression, or assignment and
+ * associated code from the `source`.
*
* @private
- * @param {Object|Undefined} exception The error object.
- * @param {String} result The resulting gzipped source.
+ * @param {String} source The source to process.
+ * @param {String} funcName The name of the function to remove.
+ * @returns {String} Returns the source with the function removed.
*/
- function onUglifyGzip(exception, result) {
- if (exception) {
- throw exception;
+ function removeFunction(source, funcName) {
+ var modified,
+ snippet = matchFunction(source, funcName);
+
+ // exit early if function is not found
+ if (!snippet) {
+ return source;
}
- // store the gzipped result and report the size
- accumulator.uglified.gzip = result;
- console.log('Done. Size: %d KB.', result.length);
- // finish by choosing the smallest compressed file
- onComplete();
+ // remove function
+ source = source.replace(matchFunction(source, funcName), '');
+
+ // grab the method assignments snippet
+ snippet = source.match(/lodash\.VERSION *= *[\s\S]+?\/\*-+\*\/\n/)[0];
+
+ // remove assignment and aliases
+ modified = getAliases(funcName).concat(funcName).reduce(function(result, otherName) {
+ return result.replace(RegExp('(?:\\n *//.*\\s*)* *lodash\\.' + otherName + ' *= *.+\\n'), '');
+ }, snippet);
+
+ // replace with the modified snippet
+ source = source.replace(snippet, modified);
+
+ return removeFromCreateIterator(source, funcName);
}
/**
- * The callback executed after JavaScript source is minified and gzipped.
+ * Removes a given variable from the `source`.
*
* @private
+ * @param {String} source The source to process.
+ * @param {String} varName The name of the variable to remove.
+ * @returns {String} Returns the source with the variable removed.
*/
- function onComplete() {
- var compiled = accumulator.compiled,
- uglified = accumulator.uglified;
-
- // save the Closure Compiled version to disk
- fs.writeFileSync(path.join(distPath, 'lodash.compiler.js'), compiled.source);
- // explicit 'binary' is necessary to ensure the stream is written correctly
- fs.writeFileSync(path.join(distPath, 'lodash.compiler.js.gz'), compiled.gzip, 'binary');
-
- // save the Uglified version to disk
- fs.writeFileSync(path.join(distPath, 'lodash.uglify.js'), uglified.source);
- fs.writeFileSync(path.join(distPath, 'lodash.uglify.js.gz'), uglified.gzip, 'binary');
-
- // select the smallest gzipped file and use its minified form as the
- // official minified release (ties go to Closure Compiler)
- fs.writeFileSync(path.join(__dirname, 'lodash.min.js'),
- uglified.gzip.length < compiled.gzip.length
- ? uglified.source
- : compiled.source
- );
+ function removeVar(source, varName) {
+ source = source.replace(RegExp(
+ // begin non-capturing group
+ '(?:' +
+ // match multi-line comment block
+ '(?:\\n +/\\*[^*]*\\*+(?:[^/][^*]*\\*+)*/)?\\n' +
+ // match a variable declaration that's not part of a declaration list
+ '( +)var ' + varName + ' *= *(?:.*?;|(?:Function\\(.+?|.*?[^,])\\n[\\s\\S]+?\\n\\1.+?;)\\n|' +
+ // match a variable in a declaration list
+ '\\n +' + varName + ' *=.*?,' +
+ // end non-capturing group
+ ')'
+ ), '');
+
+ // remove a varaible at the start of a variable declaration list
+ source = source.replace(RegExp('(var +)' + varName + ' *=.+?,\\s+'), '$1');
+
+ // remove a variable at the end of a variable declaration list
+ source = source.replace(RegExp(',\\s*' + varName + ' *=.*?;'), ';');
+
+ return removeFromCreateIterator(source, varName);
}
/*--------------------------------------------------------------------------*/
- // create the destination directory if it doesn't exist
- if (!path.existsSync(distPath)) {
- fs.mkdirSync(distPath);
- }
+ // inline `iteratorTemplate`
+ (function() {
+ var iteratorTemplate = lodash._iteratorTemplate,
+ code = /^function[^{]+{([\s\S]+?)}$/.exec(iteratorTemplate)[1];
+
+ // remove whitespace from template
+ code = code.replace(/\[object |else if|function | in |return\s+[\w']|throw |typeof |var |\\\\n|\\n|\s+/g, function(match) {
+ return match == false || match == '\\n' ? '' : match;
+ });
+
+ // remove unnecessary code
+ code = code
+ .replace(/\|\|\{\}|,__t,__j=Array.prototype.join|function print[^}]+}|\+''/g, '')
+ .replace(/(\{);|;(\})/g, '$1$2')
+ .replace(/\(\(__t=\(([^)]+)\)\)==null\?'':__t\)/g, '$1');
+
+ // ensure escaped characters are interpreted correctly inside the `Function()` string
+ code = code.replace(/\\/g, '\\\\');
+
+ // add `code` to `Function()`
+ code = '$1Function(\'object\',\n$2 "' + code + '"\n$2);\n';
+
+ // replace `template()` with `Function()`
+ source = source.replace(/(( +)var iteratorTemplate *= *)([\s\S]+?\n\2.+?);\n/, code);
+
+ // remove pseudo private property `_iteratorTemplate`
+ source = source.replace(/(?:\s*\/\/.*)*\s*lodash\._iteratorTemplate\b.+\n/, '\n');
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ // custom build
+ process.argv.some(function(arg) {
+ // exit early if not the "exclude" or "include" command option
+ var pair = arg.match(/^(exclude|include)=(.*)$/);
+ if (!pair) {
+ return false;
+ }
+
+ var filterType = pair[1],
+ filterNames = lodash.intersection(Object.keys(dependencyMap), pair[2].split(/, */).map(getRealName));
+
+ // set custom build flag
+ isCustom = true;
+
+ // remove the specified functions and their dependants
+ if (filterType == 'exclude') {
+ filterNames.forEach(function(funcName) {
+ getDependants(funcName).concat(funcName).forEach(function(otherName) {
+ source = removeFunction(source, otherName);
+ });
+ });
+ }
+ // else remove all but the specified functions and their dependencies
+ else {
+ filterNames = lodash.uniq(filterNames.reduce(function(result, funcName) {
+ result.push.apply(result, getDependencies(funcName).concat(funcName));
+ return result;
+ }, []));
+
+ lodash.each(dependencyMap, function(dependencies, otherName) {
+ if (filterNames.indexOf(otherName) < 0) {
+ source = removeFunction(source, otherName);
+ }
+ });
+ }
+
+ // remove associated functions, variables and code snippets
+ if (isRemoved(source, 'isArguments')) {
+ // remove `isArguments` if-statement
+ source = source.replace(/(?:\s*\/\/.*)*\s*if *\(!isArguments[^)]+\)[\s\S]+?};?\s*}\n/, '');
+ }
+ if (isRemoved(source, 'mixin')) {
+ // remove `LoDash` constructor
+ source = removeFunction(source, 'LoDash');
+ // remove `LoDash` calls
+ source = source.replace(/(?:new +LoDash(?!\()|(?:new +)?LoDash\([^)]*\));?/g, '');
+ // remove `LoDash.prototype` additions
+ source = source.replace(/(?:\s*\/\/.*)*\s*LoDash.prototype *=[\s\S]+?\/\*-+\*\//, '');
+ }
+ if (isRemoved(source, 'template')) {
+ // remove `templateSettings` assignment
+ source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *lodash\.templateSettings[\s\S]+?};\n/, '');
+ }
+ if (isRemoved(source, 'max', 'min')) {
+ source = removeVar(source, 'argsLimit');
+ // remove `argsLimit` try-catch
+ source = source.replace(/\n *try\s*\{[\s\S]+?argsLimit *=[\s\S]+?catch[^}]+}\n/, '');
+ }
+ if (isRemoved(source, 'isArray', 'isEmpty', 'isEqual', 'size')) {
+ source = removeVar(source, 'arrayClass');
+ }
+ if (isRemoved(source, 'bind', 'functions', 'groupBy', 'invoke', 'isEqual', 'isFunction', 'result', 'sortBy', 'toArray')) {
+ source = removeVar(source, 'funcClass');
+ }
+ if (isRemoved(source, 'clone', 'isObject', 'keys')) {
+ source = removeVar(source, 'objectTypes');
+ source = removeFromCreateIterator(source, 'objectTypes');
+ }
+ if (isRemoved(source, 'isEmpty', 'isEqual', 'isString', 'size')) {
+ source = removeVar(source, 'stringClass');
+ }
+
+ // consolidate consecutive horizontal rule comment separators
+ source = source.replace(/(?:\s*\/\*-+\*\/\s*){2,}/g, function(separators) {
+ return separators.match(/^\s*/)[0] + separators.slice(separators.lastIndexOf('/*'));
+ });
+
+ return true;
+ });
+
// begin the minification process
- closureCompile(source, onClosureCompile);
+ if (isCustom) {
+ fs.writeFileSync(path.join(__dirname, 'lodash.custom.js'), source);
+ minify(source, 'lodash.custom.min', function(result) {
+ fs.writeFileSync(path.join(__dirname, 'lodash.custom.min.js'), result);
+ });
+ }
+ else {
+ minify(source, 'lodash.min', function(result) {
+ fs.writeFileSync(path.join(__dirname, 'lodash.min.js'), result);
+ });
+ }
}());
diff --git a/build/minify.js b/build/minify.js
new file mode 100755
index 0000000000..39efec5e09
--- /dev/null
+++ b/build/minify.js
@@ -0,0 +1,341 @@
+#!/usr/bin/env node
+;(function() {
+ 'use strict';
+
+ /** The Node filesystem, path, `zlib`, and child process modules */
+ var fs = require('fs'),
+ gzip = require('zlib').gzip,
+ path = require('path'),
+ spawn = require('child_process').spawn;
+
+ /** The directory that is the base of the repository */
+ var basePath = path.join(__dirname, '../');
+
+ /** The directory where the Closure Compiler is located */
+ var closurePath = path.join(basePath, 'vendor', 'closure-compiler', 'compiler.jar');
+
+ /** The distribution directory */
+ var distPath = path.join(basePath, 'dist');
+
+ /** Load other modules */
+ var preprocess = require(path.join(__dirname, 'pre-compile')),
+ postprocess = require(path.join(__dirname, 'post-compile')),
+ uglifyJS = require(path.join(basePath, 'vendor', 'uglifyjs', 'uglify-js'));
+
+ /** Closure Compiler command-line options */
+ var closureOptions = [
+ '--compilation_level=ADVANCED_OPTIMIZATIONS',
+ '--language_in=ECMASCRIPT5_STRICT',
+ '--warning_level=QUIET'
+ ];
+
+ /** Reassign `existsSync` for older versions of Node */
+ fs.existsSync || (fs.existsSync = path.existsSync);
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The exposed `minify` function minifies a given `source` and invokes the
+ * `onComplete` callback when finished.
+ *
+ * @param {String} source The source to minify.
+ * @param {String} workingName The name to give temporary files creates during the minification process.
+ * @param {Function} onComplete A function called when minification has completed.
+ */
+ function minify(source, workingName, onComplete) {
+ new Minify(source, workingName, onComplete);
+ }
+
+ /**
+ * The Minify constructor used to keep state of each `minify` invocation.
+ *
+ * @private
+ * @constructor
+ * @param {String} source The source to minify.
+ * @param {String} workingName The name to give temporary files creates during the minification process.
+ * @param {Function} onComplete A function called when minification has completed.
+ */
+ function Minify(source, workingName, onComplete) {
+ // create the destination directory if it doesn't exist
+ if (!fs.existsSync(distPath)) {
+ fs.mkdirSync(distPath);
+ }
+
+ this.compiled = {};
+ this.hybrid = {};
+ this.uglified = {};
+ this.onComplete = onComplete;
+ this.source = source = preprocess(source);
+ this.workingName = workingName;
+
+ // begin the minification process
+ closureCompile.call(this, source, onClosureCompile.bind(this));
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Compresses a `source` string using the Closure Compiler. Yields the
+ * minified result, and any exceptions encountered, to a `callback` function.
+ *
+ * @private
+ * @param {String} source The JavaScript source to minify.
+ * @param {String} [message] The message to log.
+ * @param {Function} callback The function to call once the process completes.
+ */
+ function closureCompile(source, message, callback) {
+ // the standard error stream, standard output stream, and Closure Compiler process
+ var error = '',
+ output = '',
+ compiler = spawn('java', ['-jar', closurePath].concat(closureOptions));
+
+ // juggle arguments
+ if (typeof message == 'function') {
+ callback = message;
+ message = null;
+ }
+
+ console.log(message == null
+ ? 'Compressing ' + this.workingName + ' using the Closure Compiler...'
+ : message
+ );
+
+ compiler.stdout.on('data', function(data) {
+ // append the data to the output stream
+ output += data;
+ });
+
+ compiler.stderr.on('data', function(data) {
+ // append the error message to the error stream
+ error += data;
+ });
+
+ compiler.on('exit', function(status) {
+ var exception = null;
+
+ // `status` contains the process exit code
+ if (status) {
+ exception = new Error(error);
+ exception.status = status;
+ }
+ callback(exception, output);
+ });
+
+ // proxy the standard input to the Closure Compiler
+ compiler.stdin.end(source);
+ }
+
+ /**
+ * Compresses a `source` string using UglifyJS. Yields the result to a
+ * `callback` function. This function is synchronous; the `callback` is used
+ * for symmetry.
+ *
+ * @private
+ * @param {String} source The JavaScript source to minify.
+ * @param {String} [message] The message to log.
+ * @param {Function} callback The function to call once the process completes.
+ */
+ function uglify(source, message, callback) {
+ var exception,
+ result,
+ ugly = uglifyJS.uglify;
+
+ // juggle arguments
+ if (typeof message == 'function') {
+ callback = message;
+ message = null;
+ }
+
+ console.log(message == null
+ ? 'Compressing ' + this.workingName + ' using UglifyJS...'
+ : message
+ );
+
+ try {
+ result = ugly.gen_code(
+ // enable unsafe transformations
+ ugly.ast_squeeze_more(
+ ugly.ast_squeeze(
+ // munge variable and function names, excluding the special `define`
+ // function exposed by AMD loaders
+ ugly.ast_mangle(uglifyJS.parser.parse(source), {
+ 'except': ['define']
+ }
+ ))), {
+ 'ascii_only': true
+ });
+ } catch(e) {
+ exception = e;
+ }
+ // lines are restricted to 500 characters for consistency with the Closure Compiler
+ callback(exception, result && ugly.split_lines(result, 500));
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The `closureCompile()` callback.
+ *
+ * @private
+ * @param {Object|Undefined} exception The error object.
+ * @param {String} result The resulting minified source.
+ */
+ function onClosureCompile(exception, result) {
+ if (exception) {
+ throw exception;
+ }
+ // store the post-processed Closure Compiler result and gzip it
+ this.compiled.source = result = postprocess(result);
+ gzip(result, onClosureGzip.bind(this));
+ }
+
+ /**
+ * The Closure Compiler `gzip` callback.
+ *
+ * @private
+ * @param {Object|Undefined} exception The error object.
+ * @param {Buffer} result The resulting gzipped source.
+ */
+ function onClosureGzip(exception, result) {
+ if (exception) {
+ throw exception;
+ }
+ // store the gzipped result and report the size
+ this.compiled.gzip = result;
+ console.log('Done. Size: %d bytes.', result.length);
+
+ // next, minify the source using only UglifyJS
+ uglify.call(this, this.source, onUglify.bind(this));
+ }
+
+ /**
+ * The `uglify()` callback.
+ *
+ * @private
+ * @param {Object|Undefined} exception The error object.
+ * @param {String} result The resulting minified source.
+ */
+ function onUglify(exception, result) {
+ if (exception) {
+ throw exception;
+ }
+ // store the post-processed Uglified result and gzip it
+ this.uglified.source = result = postprocess(result);
+ gzip(result, onUglifyGzip.bind(this));
+ }
+
+ /**
+ * The UglifyJS `gzip` callback.
+ *
+ * @private
+ * @param {Object|Undefined} exception The error object.
+ * @param {Buffer} result The resulting gzipped source.
+ */
+ function onUglifyGzip(exception, result) {
+ if (exception) {
+ throw exception;
+ }
+ var message = 'Compressing ' + this.workingName + ' using hybrid minification...';
+
+ // store the gzipped result and report the size
+ this.uglified.gzip = result;
+ console.log('Done. Size: %d bytes.', result.length);
+
+ // next, minify the Closure Compiler minified source using UglifyJS
+ uglify.call(this, this.compiled.source, message, onHybrid.bind(this));
+ }
+
+ /**
+ * The hybrid `uglify()` callback.
+ *
+ * @private
+ * @param {Object|Undefined} exception The error object.
+ * @param {String} result The resulting minified source.
+ */
+ function onHybrid(exception, result) {
+ if (exception) {
+ throw exception;
+ }
+ // store the post-processed Uglified result and gzip it
+ this.hybrid.source = result = postprocess(result);
+ gzip(result, onHybridGzip.bind(this));
+ }
+
+ /**
+ * The hybrid `gzip` callback.
+ *
+ * @private
+ * @param {Object|Undefined} exception The error object.
+ * @param {Buffer} result The resulting gzipped source.
+ */
+ function onHybridGzip(exception, result) {
+ if (exception) {
+ throw exception;
+ }
+ // store the gzipped result and report the size
+ this.hybrid.gzip = result;
+ console.log('Done. Size: %d bytes.', result.length);
+
+ // finish by choosing the smallest compressed file
+ onComplete.call(this);
+ }
+
+ /**
+ * The callback executed after JavaScript source is minified and gzipped.
+ *
+ * @private
+ */
+ function onComplete() {
+ var compiled = this.compiled,
+ hybrid = this.hybrid,
+ name = this.workingName,
+ uglified = this.uglified;
+
+ // save the Closure Compiled version to disk
+ fs.writeFileSync(path.join(distPath, name + '.compiler.js'), compiled.source);
+ fs.writeFileSync(path.join(distPath, name + '.compiler.js.gz'), compiled.gzip);
+
+ // save the Uglified version to disk
+ fs.writeFileSync(path.join(distPath, name + '.uglify.js'), uglified.source);
+ fs.writeFileSync(path.join(distPath, name + '.uglify.js.gz'), uglified.gzip);
+
+ // save the hybrid minified version to disk
+ fs.writeFileSync(path.join(distPath, name + '.hybrid.js'), hybrid.source);
+ fs.writeFileSync(path.join(distPath, name + '.hybrid.js.gz'), hybrid.gzip);
+
+ // select the smallest gzipped file and use its minified counterpart as the
+ // official minified release (ties go to Closure Compiler)
+ var min = Math.min(compiled.gzip.length, hybrid.gzip.length, uglified.gzip.length);
+
+ // pass the minified source to the minify instances "onComplete" callback
+ this.onComplete(
+ compiled.gzip.length == min
+ ? compiled.source
+ : uglified.gzip.length == min
+ ? uglified.source
+ : hybrid.source
+ );
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ // expose `minify`
+ if (module != require.main) {
+ module.exports = minify;
+ }
+ else {
+ // read the JavaScript source file from the first argument if the script
+ // was invoked directly (e.g. `node minify.js source.js`) and write to
+ // the same file
+ (function() {
+ var filePath = process.argv[2],
+ dirPath = path.dirname(filePath),
+ source = fs.readFileSync(filePath, 'utf8'),
+ workingName = path.basename(filePath, '.js') + '.min';
+
+ minify(source, workingName, function(result) {
+ fs.writeFileSync(path.join(dirPath, workingName + '.js'), result);
+ });
+ }());
+ }
+}());
diff --git a/build/post-compile.js b/build/post-compile.js
index 1f33a3315f..c93a0773ff 100644
--- a/build/post-compile.js
+++ b/build/post-compile.js
@@ -8,7 +8,7 @@
/** The minimal license/copyright template */
var licenseTemplate =
'/*!\n' +
- ' Lo-Dash @VERSION github.com/bestiejs/lodash/blob/master/LICENSE.txt\n' +
+ ' Lo-Dash @VERSION lodash.com/license\n' +
' Underscore.js 1.3.3 github.com/documentcloud/underscore/blob/master/LICENSE\n' +
'*/';
@@ -18,19 +18,36 @@
* Post-process a given minified JavaScript `source`, preparing it for
* deployment.
*
- * @private
* @param {String} source The source to process.
* @returns {String} Returns the processed source.
*/
function postprocess(source) {
+ // exit early if snippet isn't found
+ var snippet = /VERSION\s*[=:]\s*([\'"])(.*?)\1/.exec(source);
+ if (!snippet) {
+ return source;
+ }
+
// set the version
- var license = licenseTemplate.replace('@VERSION', (/VERSION:([\'"])(.*?)\1/).exec(source).pop());
+ var license = licenseTemplate.replace('@VERSION', snippet[2]);
+
// move vars exposed by Closure Compiler into the IIFE
source = source.replace(/^([^(\n]+)\s*(\(function[^)]+\){)/, '$2$1');
+
// use double quotes consistently
source = source.replace(/'use strict'/, '"use strict"');
+
+ // unescape properties (i.e. foo["bar"] => foo.bar)
+ source = source.replace(/(\w)\["([^."]+)"\]/g, '$1.$2');
+
+ // correct AMD module definition for AMD build optimizers
+ source = source.replace(/("function")==(typeof define)&&\(?("object")==(typeof define\.amd)(&&define\.amd)\)?/, '$2==$1&&$4==$3$5');
+
// add license
- return license + '\n;' + source;
+ source = license + '\n;' + source;
+
+ // add trailing semicolon
+ return source.replace(/[\s;]*$/, ';');
}
/*--------------------------------------------------------------------------*/
diff --git a/build/pre-compile.js b/build/pre-compile.js
index 7314d2c07e..fad4c8a3b6 100644
--- a/build/pre-compile.js
+++ b/build/pre-compile.js
@@ -5,65 +5,65 @@
/** The Node filesystem module */
var fs = require('fs');
- /** Used to minify string values embedded in compiled strings */
- var compiledValues = [
- 'arrays',
- 'objects'
- ];
-
/** Used to minify variables embedded in compiled strings */
var compiledVars = [
'accumulator',
+ 'args',
'array',
'arrayClass',
'bind',
'callback',
'className',
'collection',
- 'computed',
'concat',
- 'current',
+ 'ctor',
'false',
'funcClass',
'hasOwnProperty',
'identity',
'index',
'indexOf',
- 'Infinity',
- 'initial',
'isArray',
'isEmpty',
+ 'isFunc',
'length',
'object',
- 'Math',
+ 'objectTypes',
+ 'noaccum',
+ 'prop',
'property',
'result',
+ 'skipProto',
'slice',
'source',
+ 'sourceIndex',
'stringClass',
'target',
'thisArg',
'toString',
'true',
'undefined',
- 'value',
- 'values'
+ 'value'
];
- /** Used to minify `iterationFactory` option properties */
- var iterationFactoryOptions = [
- 'afterLoop',
+ /** Used to minify `compileIterator` option properties */
+ var iteratorOptions = [
'args',
'array',
+ 'arrayBranch',
'beforeLoop',
'bottom',
- 'exits',
+ 'exit',
+ 'firstArg',
+ 'hasExp',
+ 'hasDontEnumBug',
'inLoop',
'init',
- 'iterate',
+ 'iteratedObject',
'loopExp',
'object',
- 'returns',
+ 'objectBranch',
+ 'shadowed',
'top',
'useHas'
];
@@ -74,21 +74,121 @@
/** Used protect the specified properties from getting minified */
var propWhitelist = [
'_',
+ '_wrapped',
+ 'after',
+ 'all',
'amd',
+ 'any',
+ 'bind',
+ 'bindAll',
'chain',
'clearTimeout',
+ 'clone',
+ 'collect',
+ 'compact',
+ 'compose',
+ 'contains',
'criteria',
+ 'debounce',
+ 'defaults',
+ 'defer',
+ 'delay',
+ 'detect',
+ 'difference',
+ 'each',
+ 'environment',
+ 'escape',
'escape',
'evaluate',
+ 'every',
+ 'extend',
+ 'filter',
+ 'find',
+ 'first',
+ 'flatten',
+ 'foldl',
+ 'foldr',
+ 'forEach',
+ 'functions',
+ 'groupBy',
+ 'has',
+ 'head',
+ 'identity',
+ 'include',
+ 'indexOf',
+ 'initial',
+ 'inject',
'interpolate',
+ 'intersect',
+ 'intersection',
+ 'invoke',
+ 'isArguments',
+ 'isArray',
+ 'isBoolean',
+ 'isDate',
+ 'isElement',
+ 'isEmpty',
+ 'isEqual',
'isEqual',
'isFinite',
- 'lodash',
+ 'isFinite',
+ 'isFunction',
+ 'isNaN',
+ 'isNull',
+ 'isNumber',
+ 'isObject',
+ 'isRegExp',
+ 'isString',
+ 'isUndefined',
+ 'keys',
+ 'last',
+ 'lastIndexOf',
+ 'map',
+ 'max',
+ 'memoize',
+ 'methods',
+ 'min',
+ 'mixin',
+ 'noConflict',
+ 'once',
+ 'opera',
+ 'partial',
+ 'pick',
+ 'pluck',
+ 'range',
+ 'reduce',
+ 'reduceRight',
+ 'reject',
+ 'rest',
+ 'result',
+ 'select',
'setTimeout',
+ 'shuffle',
+ 'size',
+ 'some',
+ 'sortBy',
+ 'sortedIndex',
+ 'source',
+ 'tail',
+ 'take',
+ 'tap',
+ 'template',
'templateSettings',
+ 'throttle',
+ 'times',
+ 'toArray',
'toArray',
+ 'union',
+ 'uniq',
+ 'unique',
+ 'uniqueId',
'value',
- 'variable'
+ 'values',
+ 'variable',
+ 'VERSION',
+ 'without',
+ 'wrap',
+ 'zip'
];
/*--------------------------------------------------------------------------*/
@@ -96,7 +196,6 @@
/**
* Pre-process a given JavaScript `source`, preparing it for minification.
*
- * @private
* @param {String} source The source to process.
* @returns {String} Returns the processed source.
*/
@@ -105,87 +204,115 @@
source = source.replace(/\/\*![\s\S]+?\*\//, '');
// correct JSDoc tags for Closure Compiler
- source = source.replace(/@(?:alias|category)[^\n]*/g, '');
+ source = source.replace(/@(?:alias|category)\b.*/g, '');
- // add brackets to whitelisted properties so Closure Compiler won't mung them.
+ // add brackets to whitelisted properties so Closure Compiler won't mung them
// http://code.google.com/closure/compiler/docs/api-tutorial3.html#export
- source = source.replace(RegExp('\\.(' + iterationFactoryOptions.concat(propWhitelist).join('|') + ')\\b', 'g'), "['$1']");
+ source = source.replace(RegExp('\\.(' + propWhitelist.join('|') + ')\\b', 'g'), "['$1']");
- // minify `sortBy` and `template` methods
- ['sortBy', 'template'].forEach(function(methodName) {
- var properties = ['criteria', 'value'],
- snippet = source.match(RegExp('(\\n\\s*)function ' + methodName + '[\\s\\S]+?\\1}'))[0],
- result = snippet;
-
- // minify property strings
- properties.forEach(function(property, index) {
- result = result.replace(RegExp("'" + property + "'", 'g'), "'" + minNames[index] + "'");
+ // remove whitespace from string literals
+ source = source.replace(/'(?:(?=(\\?))\1.)*?'/g, function(string) {
+ // avoids removing the '\n' of the `escapes` object
+ return string.replace(/\[object |else if|function | in |return\s+[\w']|throw |typeof |use strict|var |'\\n'|\\\\n|\\n|\s+/g, function(match) {
+ return match == false || match == '\\n' ? '' : match;
});
+ });
- // remove escaped newlines in strings
- result = result.replace(/\\n/g, '');
+ // minify `_.sortBy` internal properties
+ (function() {
+ var properties = ['criteria', 'value'],
+ snippet = (source.match(/( +)function sortBy\b[\s\S]+?\n\1}/) || 0)[0],
+ result = snippet;
- // replace with modified snippet
- source = source.replace(snippet, result);
- });
+ if (snippet) {
+ // minify property strings
+ properties.forEach(function(property, index) {
+ result = result.replace(RegExp("'" + property + "'", 'g'), "'" + minNames[index] + "'");
+ });
+ // replace with modified snippet
+ source = source.replace(snippet, result);
+ }
+ }());
- // minify all `iterationFactory` related snippets
- source.match(
+ // minify all compilable snippets
+ var snippets = source.match(
RegExp([
- // match variables storing `iterationFactory` options
- 'var [a-zA-Z]+FactoryOptions\\s*=\\s*\\{[\\s\\S]+?};\\n',
- // match the the `iterationFactory` function
- '(\\n\\s*)function iterationFactory[\\s\\S]+?\\1}',
- // match methods created by `iterationFactor` calls
- 'iterationFactory\\((?:[\'{]|[a-zA-Z]+,)[\\s\\S]+?\\);\\n'
+ // match the `iteratorTemplate`
+ 'var iteratorTemplate\\b[\\s\\S]+?\\);\\n',
+ // match methods created by `createIterator` calls
+ 'createIterator\\((?:{|[a-zA-Z]+)[\\s\\S]+?\\);\\n',
+ // match variables storing `createIterator` options
+ '( +)var [a-zA-Z]+IteratorOptions\\b[\\s\\S]+?\\n\\1}',
+ // match the the `createIterator` function
+ '( +)function createIterator\\b[\\s\\S]+?\\n\\2}'
].join('|'), 'g')
- )
- .forEach(function(snippet, index) {
- var result = snippet;
+ );
- // add `true` and `false` arguments to be minified
- if (/function iterationFactory/.test(snippet)) {
+ // exit early if no compilable snippets
+ if (!snippets) {
+ return source;
+ }
+
+ snippets.forEach(function(snippet, index) {
+ var isCreateIterator = /function createIterator\b/.test(snippet),
+ isIteratorTemplate = /var iteratorTemplate\b/.test(snippet),
+ result = snippet;
+
+
+ // add brackets to whitelisted properties so Closure Compiler won't mung them
+ result = result.replace(RegExp('\\.(' + iteratorOptions.join('|') + ')\\b', 'g'), "['$1']");
+
+ if (isCreateIterator) {
+ // add `true` and `false` arguments to be minified
result = result
- .replace(/(Function\('[\s\S]+?)undefined/, '$1true,false,undefined')
- .replace(/\)\([^)]+/, '$&,true,false');
+ .replace(/(Function\(\s*'[\s\S]+?)undefined/, '$1true,false,undefined')
+ .replace(/factory\([^)]+/, '$&,true,false');
- // replace with modified snippet early and clip snippet
+ // replace with modified snippet early and clip snippet so other arguments
+ // aren't minified
source = source.replace(snippet, result);
- snippet = result = result.replace(/\)\([\s\S]+$/, '');
+ snippet = result = result.replace(/factory\([\s\S]+$/, '');
}
- // minify snippet variables/arguments
+ // minify snippet variables / arguments
compiledVars.forEach(function(variable, index) {
- result = result.replace(RegExp('([^.]\\b|\\\\n)' + variable + '\\b(?!\'\\s*[\\]:])', 'g'), '$1' + minNames[index]);
+ // ensure properties in compiled strings aren't minified
+ result = result.replace(RegExp('([^.]\\b)' + variable + '\\b(?!\' *[\\]:])', 'g'), '$1' + minNames[index]);
+
// correct `typeof x == 'object'`
if (variable == 'object') {
result = result.replace(RegExp("(typeof [^']+')" + minNames[index] + "'", 'g'), "$1object'");
}
- // correct boolean literals
- if (variable == 'true' || variable == 'false') {
+ // correct external boolean literals
+ else if (variable == 'true' || variable == 'false') {
result = result
- .replace(RegExp(':\\s*' + minNames[index] + '\\s*,', 'g'), ':' + variable + ',')
- .replace(RegExp('\\s*' + minNames[index] + '\\s*;', 'g'), variable + ';');
+ .replace(RegExp(': *' + minNames[index] + ',', 'g'), ':' + variable + ',')
+ .replace(RegExp('\\b' + minNames[index] + ';', 'g'), variable + ';');
}
});
- // minify snippet values
- compiledValues.forEach(function(value, index) {
- result = result.replace(RegExp("'" + value + "'", 'g'), "'" + minNames[index] + "'");
- });
-
- // minify iterationFactory option property strings
- iterationFactoryOptions.forEach(function(property, index) {
- if (property == 'array' || property == 'object') {
- result = result.replace(RegExp("'" + property + "'(\\s*[\\]:])", 'g'), "'" + minNames[index] + "'$1");
- } else {
- result = result.replace(RegExp("'" + property + "'", 'g'), "'" + minNames[index] + "'");
+ // minify `createIterator` option property names
+ iteratorOptions.forEach(function(property, index) {
+ if (isIteratorTemplate) {
+ // minify property names as interpolated template variables
+ result = result.replace(RegExp('\\b' + property + '\\b', 'g'), minNames[index]);
+ }
+ else {
+ if (property == 'array' || property == 'object') {
+ // minify "array" and "object" sub property names
+ result = result.replace(RegExp("'" + property + "'( *[\\]:])", 'g'), "'" + minNames[index] + "'$1");
+ }
+ else {
+ // minify property name strings
+ result = result.replace(RegExp("'" + property + "'", 'g'), "'" + minNames[index] + "'");
+ // minify property names in regexps and accessors
+ if (isCreateIterator) {
+ result = result.replace(RegExp('([\\.|/])' + property + '\\b' , 'g'), '$1' + minNames[index]);
+ }
+ }
}
});
- // remove escaped newlines in strings
- result = result.replace(/\\n/g, '');
-
// replace with modified snippet
source = source.replace(snippet, result);
});
@@ -198,7 +325,8 @@
// expose `preprocess`
if (module != require.main) {
module.exports = preprocess;
- } else {
+ }
+ else {
// read the JavaScript source file from the first argument if the script
// was invoked directly (e.g. `node pre-compile.js source.js`) and write to
// the same file
diff --git a/doc/README.md b/doc/README.md
index 9e0760f184..b1f21d887d 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -1,95 +1,103 @@
-# Lo-Dash v0.1.0
-
-
-
-
-
-
-## `_`
-* [`_`](#_)
-* [`_.VERSION`](#_.VERSION)
-* [`_.after`](#_.after)
-* [`_.bind`](#_.bind)
-* [`_.bindAll`](#_.bindAll)
-* [`_.chain`](#_.chain)
-* [`_.chain`](#_.chain)
-* [`_.clone`](#_.clone)
-* [`_.compact`](#_.compact)
-* [`_.compose`](#_.compose)
-* [`_.contains`](#_.contains)
-* [`_.debounce`](#_.debounce)
-* [`_.defaults`](#_.defaults)
-* [`_.defer`](#_.defer)
-* [`_.delay`](#_.delay)
-* [`_.difference`](#_.difference)
-* [`_.escape`](#_.escape)
-* [`_.every`](#_.every)
-* [`_.extend`](#_.extend)
-* [`_.filter`](#_.filter)
-* [`_.find`](#_.find)
-* [`_.first`](#_.first)
-* [`_.flatten`](#_.flatten)
-* [`_.forEach`](#_.forEach)
-* [`_.functions`](#_.functions)
-* [`_.groupBy`](#_.groupBy)
-* [`_.has`](#_.has)
-* [`_.identity`](#_.identity)
-* [`_.indexOf`](#_.indexOf)
-* [`_.initial`](#_.initial)
-* [`_.intersection`](#_.intersection)
-* [`_.invoke`](#_.invoke)
-* [`_.isArguments`](#_.isArguments)
-* [`_.isArray`](#_.isArray)
-* [`_.isBoolean`](#_.isBoolean)
-* [`_.isDate`](#_.isDate)
-* [`_.isElement`](#_.isElement)
-* [`_.isEmpty`](#_.isEmpty)
-* [`_.isEqual`](#_.isEqual)
-* [`_.isFinite`](#_.isFinite)
-* [`_.isFunction`](#_.isFunction)
-* [`_.isNaN`](#_.isNaN)
-* [`_.isNull`](#_.isNull)
-* [`_.isNumber`](#_.isNumber)
-* [`_.isObject`](#_.isObject)
-* [`_.isRegExp`](#_.isRegExp)
-* [`_.isString`](#_.isString)
-* [`_.isUndefined`](#_.isUndefined)
-* [`_.keys`](#_.keys)
-* [`_.last`](#_.last)
-* [`_.lastIndexOf`](#_.lastIndexOf)
-* [`_.map`](#_.map)
-* [`_.max`](#_.max)
-* [`_.memoize`](#_.memoize)
-* [`_.min`](#_.min)
-* [`_.mixin`](#_.mixin)
-* [`_.noConflict`](#_.noConflict)
-* [`_.once`](#_.once)
-* [`_.pick`](#_.pick)
-* [`_.pluck`](#_.pluck)
-* [`_.range`](#_.range)
-* [`_.reduce`](#_.reduce)
-* [`_.reduceRight`](#_.reduceRight)
-* [`_.reject`](#_.reject)
-* [`_.rest`](#_.rest)
-* [`_.result`](#_.result)
-* [`_.shuffle`](#_.shuffle)
-* [`_.size`](#_.size)
-* [`_.some`](#_.some)
-* [`_.sortBy`](#_.sortBy)
-* [`_.sortedIndex`](#_.sortedIndex)
-* [`_.tap`](#_.tap)
-* [`_.template`](#_.template)
-* [`_.throttle`](#_.throttle)
-* [`_.times`](#_.times)
-* [`_.toArray`](#_.toArray)
-* [`_.union`](#_.union)
-* [`_.uniq`](#_.uniq)
-* [`_.uniqueId`](#_.uniqueId)
-* [`_.value`](#_.value)
-* [`_.values`](#_.values)
-* [`_.without`](#_.without)
-* [`_.wrap`](#_.wrap)
-* [`_.zip`](#_.zip)
+# Lo-Dash v0.2.0
+
+
+
+
+
+
+## `_`
+* [`_`](#_value)
+* [`_.VERSION`](#_version)
+* [`_.after`](#_aftern-func)
+* [`_.bind`](#_bindfunc--arg1-arg2-)
+* [`_.bindAll`](#_bindallobject--methodname1-methodname2-)
+* [`_.chain`](#_chainvalue)
+* [`_.clone`](#_clonevalue)
+* [`_.compact`](#_compactarray)
+* [`_.compose`](#_composefunc1-func2-)
+* [`_.contains`](#_containscollection-target)
+* [`_.debounce`](#_debouncefunc-wait-immediate)
+* [`_.defaults`](#_defaultsobject--defaults1-defaults2-)
+* [`_.defer`](#_deferfunc--arg1-arg2-)
+* [`_.delay`](#_delayfunc-wait--arg1-arg2-)
+* [`_.difference`](#_differencearray--array1-array2-)
+* [`_.escape`](#_escapestring)
+* [`_.every`](#_everycollection-callback--thisarg)
+* [`_.extend`](#_extendobject--source1-source2-)
+* [`_.filter`](#_filtercollection-callback--thisarg)
+* [`_.find`](#_findcollection-callback--thisarg)
+* [`_.first`](#_firstarray--n-guard)
+* [`_.flatten`](#_flattenarray-shallow)
+* [`_.forEach`](#_foreachcollection-callback--thisarg)
+* [`_.functions`](#_functionsobject)
+* [`_.groupBy`](#_groupbycollection-callback--thisarg)
+* [`_.has`](#_hasobject-property)
+* [`_.identity`](#_identityvalue)
+* [`_.indexOf`](#_indexofarray-value--issortedfalse)
+* [`_.initial`](#_initialarray--n-guard)
+* [`_.intersection`](#_intersectionarray1-array2-)
+* [`_.invoke`](#_invokearray-methodname--arg1-arg2-)
+* [`_.isArguments`](#_isargumentsvalue)
+* [`_.isArray`](#_isarrayvalue)
+* [`_.isBoolean`](#_isbooleanvalue)
+* [`_.isDate`](#_isdatevalue)
+* [`_.isElement`](#_iselementvalue)
+* [`_.isEmpty`](#_isemptyvalue)
+* [`_.isEqual`](#_isequala-b--stack)
+* [`_.isFinite`](#_isfinitevalue)
+* [`_.isFunction`](#_isfunctionvalue)
+* [`_.isNaN`](#_isnanvalue)
+* [`_.isNull`](#_isnullvalue)
+* [`_.isNumber`](#_isnumbervalue)
+* [`_.isObject`](#_isobjectvalue)
+* [`_.isRegExp`](#_isregexpvalue)
+* [`_.isString`](#_isstringvalue)
+* [`_.isUndefined`](#_isundefinedvalue)
+* [`_.keys`](#_keysobject)
+* [`_.last`](#_lastarray--n-guard)
+* [`_.lastIndexOf`](#_lastindexofarray-value)
+* [`_.map`](#_mapcollection-callback--thisarg)
+* [`_.max`](#_maxarray--callback-thisarg)
+* [`_.memoize`](#_memoizefunc--resolver)
+* [`_.min`](#_minarray--callback-thisarg)
+* [`_.mixin`](#_mixinobject)
+* [`_.noConflict`](#_noconflict)
+* [`_.once`](#_oncefunc)
+* [`_.partial`](#_partialfunc--arg1-arg2-)
+* [`_.pick`](#_pickobject--prop1-prop2-)
+* [`_.pluck`](#_pluckcollection-property)
+* [`_.range`](#_rangestart0-end--step1)
+* [`_.reduce`](#_reducecollection-callback--accumulator-thisarg)
+* [`_.reduceRight`](#_reducerightcollection-callback--accumulator-thisarg)
+* [`_.reject`](#_rejectcollection-callback--thisarg)
+* [`_.rest`](#_restarray--n-guard)
+* [`_.result`](#_resultobject-property)
+* [`_.shuffle`](#_shufflearray)
+* [`_.size`](#_sizecollection)
+* [`_.some`](#_somecollection-callback--thisarg)
+* [`_.sortBy`](#_sortbycollection-callback--thisarg)
+* [`_.sortedIndex`](#_sortedindexarray-value--callback)
+* [`_.tap`](#_tapvalue-interceptor)
+* [`_.template`](#_templatetext-data-options)
+* [`_.throttle`](#_throttlefunc-wait)
+* [`_.times`](#_timesn-callback--thisarg)
+* [`_.toArray`](#_toarraycollection)
+* [`_.union`](#_unionarray1-array2-)
+* [`_.uniq`](#_uniqarray--issortedfalse-callback)
+* [`_.uniqueId`](#_uniqueidprefix)
+* [`_.values`](#_valuescollection)
+* [`_.without`](#_withoutarray--value1-value2-)
+* [`_.wrap`](#_wrapfunc-wrapper--arg1-arg2-)
+* [`_.zip`](#_ziparray1-array2-)
+
+
+
+
+
+
+## `_.prototype`
+* [`_.prototype.chain`](#_prototypechain)
+* [`_.prototype.value`](#_prototypevalue)
@@ -97,10 +105,11 @@
## `_.templateSettings`
-* [`_.templateSettings`](#_.templateSettings)
-* [`_.templateSettings.escape`](#_.templateSettings.escape)
-* [`_.templateSettings.evaluate`](#_.templateSettings.evaluate)
-* [`_.templateSettings.interpolate`](#_.templateSettings.interpolate)
+* [`_.templateSettings`](#_templatesettings)
+* [`_.templateSettings.escape`](#_templatesettingsescape)
+* [`_.templateSettings.evaluate`](#_templatesettingsevaluate)
+* [`_.templateSettings.interpolate`](#_templatesettingsinterpolate)
+* [`_.templateSettings.variable`](#_templatesettingsvariable)
@@ -117,43 +126,52 @@
-### `_(value)`
+
+
+
+### `_(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L289 "View in source") [Ⓣ][1]
+
The `lodash` function.
-[▲][1]
#### Arguments
-1. `value` *(Mixed)*: The value to wrap in a `Lodash` instance.
+1. `value` *(Mixed)*: The value to wrap in a `LoDash` instance.
#### Returns
-*(Object)*: Returns a `Lodash` instance.
+*(Object)*: Returns a `LoDash` instance.
+
+* * *
-## `_`
-### `_(value)`
-The `lodash` function.
-[▲][1]
-### `_.VERSION`
+### `_.VERSION`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3066 "View in source") [Ⓣ][1]
+
*(String)*: The semantic version number.
-[▲][1]
+
+* * *
-### `_.after(times, func)`
-Creates a new function that is restricted to executing only after it is called a given number of `times`.
-[▲][1]
+
+
+
+### `_.after(n, func)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1680 "View in source") [Ⓣ][1]
+
+Creates a new function that is restricted to executing only after it is called `n` times.
#### Arguments
-1. `times` *(Number)*: The number of times the function must be called before it is executed.
+1. `n` *(Number)*: The number of times the function must be called before it is executed.
2. `func` *(Function)*: The function to restrict.
#### Returns
@@ -168,42 +186,74 @@ _.forEach(notes, function(note) {
// renderNotes is run once, after all notes have saved.
~~~
+* * *
+
-### `_.bind(func [, arg1, arg2, ...])`
-Creates a new function that, when called, invokes `func` with the `this` binding of `thisArg` and prepends additional arguments to those passed to the bound function.
-[▲][1]
+
+
+
+### `_.bind(func [, arg1, arg2, ...])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1731 "View in source") [Ⓣ][1]
+
+Creates a new function that, when called, invokes `func` with the `this` binding of `thisArg` and prepends any additional `bind` arguments to those passed to the bound function. Lazy defined methods may be bound by passing the object they are bound to as `func` and the method name as `thisArg`.
#### Arguments
-1. `func` *(Function)*: The function to bind.
-2. `[arg1, arg2, ...]` *(Mixed)*: Arguments to prepend to those passed to the bound function.
+1. `func` *(Function|Object)*: The function to bind or the object the method belongs to.
+2. `[arg1, arg2, ...]` *(Mixed)*: Arguments to be partially applied.
#### Returns
*(Function)*: Returns the new bound function.
#### Example
~~~ js
+// basic bind
var func = function(greeting) { return greeting + ': ' + this.name; };
func = _.bind(func, { 'name': 'moe' }, 'hi');
func();
// => 'hi: moe'
+
+// lazy bind
+var object = {
+ 'name': 'moe',
+ 'greet': function(greeting) {
+ return greeting + ': ' + this.name;
+ }
+};
+
+var func = _.bind(object, 'greet', 'hi');
+func();
+// => 'hi: moe'
+
+object.greet = function(greeting) {
+ return greeting + ' ' + this.name + '!';
+};
+
+func();
+// => 'hi moe!'
~~~
+* * *
+
-### `_.bindAll(object [, methodName1, methodName2, ...])`
+
+
+
+### `_.bindAll(object [, methodName1, methodName2, ...])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1794 "View in source") [Ⓣ][1]
+
Binds methods on the `object` to the object, overwriting the non-bound method. If no method names are provided, all the function properties of the `object` will be bound.
-[▲][1]
#### Arguments
1. `object` *(Object)*: The object to bind and assign the bound methods to.
-2. `[methodName1, methodName2, ...]` *(Mixed)*: Method names on the object to bind.
+2. `[methodName1, methodName2, ...]` *(String)*: Method names on the object to bind.
#### Returns
*(Object)*: Returns the `object`.
@@ -221,32 +271,20 @@ jQuery('#lodash_button').on('click', buttonView.onClick);
// => When the button is clicked, `this.label` will have the correct value
~~~
+* * *
+
-### `_.chain()`
-Extracts the value from a wrapped chainable object.
-[▲][1]
-
-#### Returns
-*(Mixed)*: Returns the wrapped object.
-
-#### Example
-~~~ js
-_([1, 2, 3]).value();
-// => [1, 2, 3]
-~~~
-
-
-
-### `_.chain(value)`
+### `_.chain(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3018 "View in source") [Ⓣ][1]
+
Wraps the value in a `lodash` chainable object.
-[▲][1]
#### Arguments
1. `value` *(Mixed)*: The value to wrap.
@@ -270,14 +308,20 @@ var youngest = _.chain(stooges)
// => 'moe is 40'
~~~
+* * *
+
-### `_.clone(value)`
+
+
+
+### `_.clone(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2120 "View in source") [Ⓣ][1]
+
Create a shallow clone of the `value`. Any nested objects or arrays will be assigned by reference and not cloned.
-[▲][1]
#### Arguments
1. `value` *(Mixed)*: The value to clone.
@@ -291,14 +335,20 @@ _.clone({ 'name': 'moe' });
// => { 'name': 'moe' };
~~~
+* * *
+
-### `_.compact(array)`
+
+
+
+### `_.compact(array)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1022 "View in source") [Ⓣ][1]
+
Produces a new array with all falsey values of `array` removed. The values `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey.
-[▲][1]
#### Arguments
1. `array` *(Array)*: The array to compact.
@@ -312,17 +362,23 @@ _.compact([0, 1, false, 2, '', 3]);
// => [1, 2, 3]
~~~
+* * *
+
-### `_.compose([func1, func2, ...])`
+
+
+
+### `_.compose([func1, func2, ...])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1826 "View in source") [Ⓣ][1]
+
Creates a new function that is the composition of the passed functions, where each function consumes the return value of the function that follows. In math terms, composing thefunctions `f()`, `g()`, and `h()` produces `f(g(h()))`.
-[▲][1]
#### Arguments
-1. `[func1, func2, ...]` *(Mixed)*: Functions to compose.
+1. `[func1, func2, ...]` *(Function)*: Functions to compose.
#### Returns
*(Function)*: Returns the new composed function.
@@ -336,14 +392,20 @@ welcome('moe');
// => 'hi: moe!'
~~~
+* * *
+
-### `_.contains(collection, target)`
+
+
+
+### `_.contains(collection, target)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L551 "View in source") [Ⓣ][1]
+
Checks if a given `target` value is present in a `collection` using strict equality for comparisons, i.e. `===`.
-[▲][1]
#### Arguments
1. `collection` *(Array|Object)*: The collection to iterate over.
@@ -358,18 +420,24 @@ _.contains([1, 2, 3], 3);
// => true
~~~
+* * *
+
-### `_.debounce(func, wait, immediate)`
-Creates a new function that will postpone its execution until after `wait` milliseconds have elapsed since the last time it was invoked. Pass `true` for `immediate` to cause debounce to invoke the function on the leading, instead of the trailing, edge of the `wait` timeout.
-[▲][1]
+
+
+
+### `_.debounce(func, wait, immediate)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1859 "View in source") [Ⓣ][1]
+
+Creates a new function that will delay the execution of `func` until after `wait` milliseconds have elapsed since the last time it was invoked. Pass `true` for `immediate` to cause debounce to invoke `func` on the leading, instead of the trailing, edge of the `wait` timeout. Subsequent calls to the debounced function will return the result of the last `func` call.
#### Arguments
1. `func` *(Function)*: The function to debounce.
-2. `wait` *(Number)*: The number of milliseconds to postone.
+2. `wait` *(Number)*: The number of milliseconds to delay.
3. `immediate` *(Boolean)*: A flag to indicate execution is on the leading edge of the timeout.
#### Returns
@@ -381,18 +449,24 @@ var lazyLayout = _.debounce(calculateLayout, 300);
jQuery(window).on('resize', lazyLayout);
~~~
+* * *
+
-### `_.defaults(object [, defaults1, defaults2, ..])`
+
+
+
+### `_.defaults(object [, defaults1, defaults2, ...])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2143 "View in source") [Ⓣ][1]
+
Assigns missing properties in `object` with default values from the defaults objects. As soon as a property is set, additional defaults of the same property will be ignored.
-[▲][1]
#### Arguments
1. `object` *(Object)*: The object to populate.
-2. `[defaults1, defaults2, ..]` *(Object)*: The defaults objects to apply to `object`.
+2. `[defaults1, defaults2, ...]` *(Object)*: The defaults objects to apply to `object`.
#### Returns
*(Object)*: Returns `object`.
@@ -404,14 +478,20 @@ _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'lots' });
// => { 'flavor': 'chocolate', 'sprinkles': 'lots' }
~~~
+* * *
+
-### `_.defer(func [, arg1, arg2, ...])`
-Defers invoking the `func` function until the current call stack has cleared. Additional arguments are passed to `func` when it is invoked.
-[▲][1]
+
+
+
+### `_.defer(func [, arg1, arg2, ...])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1924 "View in source") [Ⓣ][1]
+
+Defers executing the `func` function until the current call stack has cleared. Additional arguments are passed to `func` when it is invoked.
#### Arguments
1. `func` *(Function)*: The function to defer.
@@ -426,14 +506,20 @@ _.defer(function() { alert('deferred'); });
// Returns from the function before the alert runs.
~~~
+* * *
+
-### `_.delay(func, wait [, arg1, arg2, ...])`
-Invokes the `func` function after `wait` milliseconds. Additional arguments are passed `func` when it is invoked.
-[▲][1]
+
+
+
+### `_.delay(func, wait [, arg1, arg2, ...])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1904 "View in source") [Ⓣ][1]
+
+Executes the `func` function after `wait` milliseconds. Additional arguments are passed to `func` when it is invoked.
#### Arguments
1. `func` *(Function)*: The function to delay.
@@ -450,18 +536,24 @@ _.delay(log, 1000, 'logged later');
// => 'logged later' (Appears after one second.)
~~~
+* * *
+
-### `_.difference(array [, array1, array2, ...])`
+
+
+
+### `_.difference(array [, array1, array2, ...])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1051 "View in source") [Ⓣ][1]
+
Produces a new array of `array` values not present in the other arrays using strict equality for comparisons, i.e. `===`.
-[▲][1]
#### Arguments
1. `array` *(Array)*: The array to process.
-2. `[array1, array2, ...]` *(Mixed)*: Arrays to check.
+2. `[array1, array2, ...]` *(Array)*: Arrays to check.
#### Returns
*(Array)*: Returns a new array of `array` values not present in the other arrays.
@@ -472,14 +564,20 @@ _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
// => [1, 3, 4]
~~~
+* * *
+
-### `_.escape(string)`
-Escapes a string for insertion into HTML, replacing `&`, `<`, `>`, `"`, `'`, and `/` characters.
-[▲][1]
+
+
+
+### `_.escape(string)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2714 "View in source") [Ⓣ][1]
+
+Escapes a string for insertion into HTML, replacing `&`, `<`, `"`, `'`, and `/` characters.
#### Arguments
1. `string` *(String)*: The string to escape.
@@ -493,14 +591,20 @@ _.escape('Curly, Larry & Moe');
// => "Curly, Larry & Moe"
~~~
+* * *
+
-### `_.every(collection, callback [, thisArg])`
-Checks if the `callback` returns truthy for **all** values of a `collection`. The `callback` is invoked with `3` arguments; for arrays they are *(value, index, array)* and for objects they are *(value, key, object)*.
-[▲][1]
+
+
+
+### `_.every(collection, callback [, thisArg])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L575 "View in source") [Ⓣ][1]
+
+Checks if the `callback` returns a truthy value for **all** elements of a `collection`. The `callback` is invoked with `3` arguments; for arrays they are *(value, index, array)* and for objects they are *(value, key, object)*.
#### Arguments
1. `collection` *(Array|Object)*: The collection to iterate over.
@@ -513,21 +617,27 @@ Checks if the `callback` returns truthy for **all** values of a `collection`. Th
#### Example
~~~ js
_.every([true, 1, null, 'yes'], Boolean);
-=> false
+// => false
~~~
+* * *
+
-### `_.extend(object [, source1, source2, ..])`
+
+
+
+### `_.extend(object [, source1, source2, ...])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2162 "View in source") [Ⓣ][1]
+
Copies enumerable properties from the source objects to the `destination` object. Subsequent sources will overwrite propery assignments of previous sources.
-[▲][1]
#### Arguments
1. `object` *(Object)*: The destination object.
-2. `[source1, source2, ..]` *(Object)*: The source objects.
+2. `[source1, source2, ...]` *(Object)*: The source objects.
#### Returns
*(Object)*: Returns the destination object.
@@ -538,14 +648,20 @@ _.extend({ 'name': 'moe' }, { 'age': 40 });
// => { 'name': 'moe', 'age': 40 }
~~~
+* * *
+
-### `_.filter(collection, callback [, thisArg])`
+
+
+
+### `_.filter(collection, callback [, thisArg])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L596 "View in source") [Ⓣ][1]
+
Examines each value in a `collection`, returning an array of all values the `callback` returns truthy for. The `callback` is invoked with `3` arguments; for arrays they are *(value, index, array)* and for objects they are *(value, key, object)*.
-[▲][1]
#### Arguments
1. `collection` *(Array|Object)*: The collection to iterate over.
@@ -561,14 +677,20 @@ var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; })
// => [2, 4, 6]
~~~
+* * *
+
-### `_.find(collection, callback [, thisArg])`
+
+
+
+### `_.find(collection, callback [, thisArg])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L618 "View in source") [Ⓣ][1]
+
Examines each value in a `collection`, returning the first one the `callback` returns truthy for. The function returns as soon as it finds an acceptable value, and does not iterate over the entire `collection`. The `callback` is invoked with `3` arguments; for arrays they are *(value, index, array)* and for objects they are *(value, key, object)*.
-[▲][1]
#### Arguments
1. `collection` *(Array|Object)*: The collection to iterate over.
@@ -584,14 +706,20 @@ var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
// => 2
~~~
+* * *
+
-### `_.first(array [, n, guard])`
+
+
+
+### `_.first(array [, n, guard])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1084 "View in source") [Ⓣ][1]
+
Gets the first value of the `array`. Pass `n` to return the first `n` values of the `array`.
-[▲][1]
#### Arguments
1. `array` *(Array)*: The array to query.
@@ -607,14 +735,20 @@ _.first([5, 4, 3, 2, 1]);
// => 5
~~~
+* * *
+
-### `_.flatten(array, shallow)`
+
+
+
+### `_.flatten(array, shallow)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1106 "View in source") [Ⓣ][1]
+
Flattens a nested array *(the nesting can be to any depth)*. If `shallow` is truthy, `array` will only be flattened a single level.
-[▲][1]
#### Arguments
1. `array` *(Array)*: The array to compact.
@@ -632,14 +766,20 @@ _.flatten([1, [2], [3, [[4]]]], true);
// => [1, 2, 3, [[4]]];
~~~
+* * *
+
-### `_.forEach(collection, callback [, thisArg])`
+
+
+
+### `_.forEach(collection, callback [, thisArg])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L644 "View in source") [Ⓣ][1]
+
Iterates over a `collection`, executing the `callback` for each value in the `collection`. The `callback` is bound to the `thisArg` value, if one is passed. The `callback` is invoked with `3` arguments; for arrays they are *(value, index, array)* and for objects they are *(value, key, object)*.
-[▲][1]
#### Arguments
1. `collection` *(Array|Object)*: The collection to iterate over.
@@ -651,21 +791,27 @@ Iterates over a `collection`, executing the `callback` for each value in the `co
#### Example
~~~ js
-_.forEach([1, 2, 3], function(num) { alert(num); });
-// => alerts each number in turn...
-
_.forEach({ 'one': 1, 'two': 2, 'three': 3}, function(num) { alert(num); });
-// => alerts each number in turn...
+// => alerts each number in turn
+
+_([1, 2, 3]).forEach(function(num) { alert(num); }).join(',');
+// => alerts each number in turn and returns '1,2,3'
~~~
+* * *
+
-### `_.functions(object)`
+
+
+
+### `_.functions(object)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2179 "View in source") [Ⓣ][1]
+
Produces a sorted array of the properties, own and inherited, of `object` that have function values.
-[▲][1]
#### Arguments
1. `object` *(Object)*: The object to inspect.
@@ -679,14 +825,20 @@ _.functions(_);
// => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
~~~
+* * *
+
-### `_.groupBy(collection, callback [, thisArg])`
+
+
+
+### `_.groupBy(collection, callback [, thisArg])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L673 "View in source") [Ⓣ][1]
+
Splits a `collection` into sets, grouped by the result of running each value through `callback`. The `callback` is invoked with `3` arguments; for arrays they are *(value, index, array)* and for objects they are *(value, key, object)*. The `callback` argument may also be the name of a property to group by.
-[▲][1]
#### Arguments
1. `collection` *(Array|Object)*: The collection to iterate over.
@@ -701,18 +853,27 @@ Splits a `collection` into sets, grouped by the result of running each value thr
_.groupBy([1.3, 2.1, 2.4], function(num) { return Math.floor(num); });
// => { '1': [1.3], '2': [2.1, 2.4] }
+_.groupBy([1.3, 2.1, 2.4], function(num) { return this.floor(num); }, Math);
+// => { '1': [1.3], '2': [2.1, 2.4] }
+
_.groupBy(['one', 'two', 'three'], 'length');
// => { '3': ['one', 'two'], '5': ['three'] }
~~~
+* * *
+
-### `_.has(object, property)`
+
+
+
+### `_.has(object, property)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2202 "View in source") [Ⓣ][1]
+
Checks if the specified object `property` exists and is a direct property, instead of an inherited property.
-[▲][1]
#### Arguments
1. `object` *(Object)*: The object to check.
@@ -727,14 +888,20 @@ _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b');
// => true
~~~
+* * *
+
-### `_.identity(value)`
+
+
+
+### `_.identity(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2741 "View in source") [Ⓣ][1]
+
This function simply returns the first argument passed to it. Note: It is used throughout Lo-Dash as a default callback.
-[▲][1]
#### Arguments
1. `value` *(Mixed)*: Any value.
@@ -749,14 +916,20 @@ moe === _.identity(moe);
// => true
~~~
+* * *
+
-### `_.indexOf(array, value [, isSorted=false])`
+
+
+
+### `_.indexOf(array, value [, isSorted=false])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1143 "View in source") [Ⓣ][1]
+
Gets the index at which the first occurrence of `value` is found using strict equality for comparisons, i.e. `===`. If the `array` is already sorted, passing `true` for `isSorted` will run a faster binary search.
-[▲][1]
#### Arguments
1. `array` *(Array)*: The array to search.
@@ -772,14 +945,20 @@ _.indexOf([1, 2, 3], 2);
// => 1
~~~
+* * *
+
-### `_.initial(array [, n, guard])`
+
+
+
+### `_.initial(array [, n, guard])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1177 "View in source") [Ⓣ][1]
+
Gets all but the last value of the `array`. Pass `n` to exclude the last `n` values from the result.
-[▲][1]
#### Arguments
1. `array` *(Array)*: The array to query.
@@ -795,17 +974,23 @@ _.initial([5, 4, 3, 2, 1]);
// => [5, 4, 3, 2]
~~~
+* * *
+
-### `_.intersection([array1, array2, ...])`
+
+
+
+### `_.intersection([array1, array2, ...])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1196 "View in source") [Ⓣ][1]
+
Computes the intersection of all the passed-in arrays.
-[▲][1]
#### Arguments
-1. `[array1, array2, ...]` *(Mixed)*: Arrays to process.
+1. `[array1, array2, ...]` *(Array)*: Arrays to process.
#### Returns
*(Array)*: Returns a new array of unique values, in order, that are present in **all** of the arrays.
@@ -816,17 +1001,23 @@ _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);
// => [1, 2]
~~~
+* * *
+
-### `_.invoke(collection, methodName [, arg1, arg2, ...])`
+
+
+
+### `_.invoke(array, methodName [, arg1, arg2, ...])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1229 "View in source") [Ⓣ][1]
+
Calls the method named by `methodName` for each value of the `collection`. Additional arguments will be passed to each invoked method.
-[▲][1]
#### Arguments
-1. `collection` *(Array|Object)*: The collection to iterate over.
+1. `array` *(Array)*: The array to iterate over.
2. `methodName` *(String)*: The name of the method to invoke.
3. `[arg1, arg2, ...]` *(Mixed)*: Arguments to invoke the method with.
@@ -839,14 +1030,20 @@ _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
// => [[1, 5, 7], [1, 2, 3]]
~~~
+* * *
+
-### `_.isArguments(value)`
+
+
+
+### `_.isArguments(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2222 "View in source") [Ⓣ][1]
+
Checks if a `value` is an `arguments` object.
-[▲][1]
#### Arguments
1. `value` *(Mixed)*: The value to check.
@@ -863,14 +1060,20 @@ _.isArguments([1, 2, 3]);
// => false
~~~
+* * *
+
-### `_.isArray(value)`
+
+
+
+### `_.isArray(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L327 "View in source") [Ⓣ][1]
+
Checks if a `value` is an array.
-[▲][1]
#### Arguments
1. `value` *(Mixed)*: The value to check.
@@ -887,14 +1090,20 @@ _.isArray([1, 2, 3]);
// => true
~~~
+* * *
+
-### `_.isBoolean(value)`
+
+
+
+### `_.isBoolean(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2245 "View in source") [Ⓣ][1]
+
Checks if a `value` is a boolean *(`true` or `false`)* value.
-[▲][1]
#### Arguments
1. `value` *(Mixed)*: The value to check.
@@ -908,14 +1117,20 @@ _.isBoolean(null);
// => false
~~~
+* * *
+
-### `_.isDate(value)`
+
+
+
+### `_.isDate(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2262 "View in source") [Ⓣ][1]
+
Checks if a `value` is a date.
-[▲][1]
#### Arguments
1. `value` *(Mixed)*: The value to check.
@@ -929,14 +1144,20 @@ _.isDate(new Date);
// => true
~~~
+* * *
+
-### `_.isElement(value)`
+
+
+
+### `_.isElement(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2279 "View in source") [Ⓣ][1]
+
Checks if a `value` is a DOM element.
-[▲][1]
#### Arguments
1. `value` *(Mixed)*: The value to check.
@@ -950,14 +1171,20 @@ _.isElement(document.body);
// => true
~~~
+* * *
+
-### `_.isEmpty(value)`
+
+
+
+### `_.isEmpty(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L348 "View in source") [Ⓣ][1]
+
Checks if a `value` is empty. Arrays or strings with a length of `0` and objects with no enumerable own properties are considered "empty".
-[▲][1]
#### Arguments
1. `value` *(Mixed)*: The value to check.
@@ -974,14 +1201,20 @@ _.isEmpty({});
// => true
~~~
+* * *
+
-### `_.isEqual(a, b [, stack])`
+
+
+
+### `_.isEqual(a, b [, stack])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2306 "View in source") [Ⓣ][1]
+
Performs a deep comparison between two values to determine if they are equivalent to each other.
-[▲][1]
#### Arguments
1. `a` *(Mixed)*: The value to compare.
@@ -1003,14 +1236,20 @@ _.isEqual(moe, clone);
// => true
~~~
+* * *
+
-### `_.isFinite(value)`
+
+
+
+### `_.isFinite(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2458 "View in source") [Ⓣ][1]
+
Checks if a `value` is a finite number.
-[▲][1]
#### Arguments
1. `value` *(Mixed)*: The value to check.
@@ -1030,14 +1269,20 @@ _.isFinite(Infinity);
// => false
~~~
+* * *
+
-### `_.isFunction(value)`
+
+
+
+### `_.isFunction(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2475 "View in source") [Ⓣ][1]
+
Checks if a `value` is a function.
-[▲][1]
#### Arguments
1. `value` *(Mixed)*: The value to check.
@@ -1051,14 +1296,20 @@ _.isFunction(''.concat);
// => true
~~~
+* * *
+
-### `_.isNaN(value)`
+
+
+
+### `_.isNaN(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2526 "View in source") [Ⓣ][1]
+
Checks if a `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.
-[▲][1]
#### Arguments
1. `value` *(Mixed)*: The value to check.
@@ -1081,14 +1332,20 @@ _.isNaN(undefined);
// => false
~~~
+* * *
+
-### `_.isNull(value)`
+
+
+
+### `_.isNull(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2548 "View in source") [Ⓣ][1]
+
Checks if a `value` is `null`.
-[▲][1]
#### Arguments
1. `value` *(Mixed)*: The value to check.
@@ -1105,14 +1362,20 @@ _.isNull(undefined);
// => false
~~~
+* * *
+
-### `_.isNumber(value)`
+
+
+
+### `_.isNumber(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2565 "View in source") [Ⓣ][1]
+
Checks if a `value` is a number.
-[▲][1]
#### Arguments
1. `value` *(Mixed)*: The value to check.
@@ -1126,14 +1389,20 @@ _.isNumber(8.4 * 5;
// => true
~~~
+* * *
+
-### `_.isObject(value)`
-Checks if a `value` is an object.
-[▲][1]
+
+
+
+### `_.isObject(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2496 "View in source") [Ⓣ][1]
+
+Checks if a `value` is the language type of Object. *(e.g. arrays, functions, objects, regexps, `new Number(0)*`, and `new String('')`)
#### Arguments
1. `value` *(Mixed)*: The value to check.
@@ -1150,16 +1419,22 @@ _.isObject(1);
// => false
~~~
+* * *
+
-### `_.isRegExp(value)`
-Checks if a `value` is a regular expression.
-[▲][1]
-#### Arguments
+
+
+### `_.isRegExp(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2582 "View in source") [Ⓣ][1]
+
+Checks if a `value` is a regular expression.
+
+#### Arguments
1. `value` *(Mixed)*: The value to check.
#### Returns
@@ -1171,14 +1446,20 @@ _.isRegExp(/moe/);
// => true
~~~
+* * *
+
-### `_.isString(value)`
+
+
+
+### `_.isString(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2599 "View in source") [Ⓣ][1]
+
Checks if a `value` is a string.
-[▲][1]
#### Arguments
1. `value` *(Mixed)*: The value to check.
@@ -1192,14 +1473,20 @@ _.isString('moe');
// => true
~~~
+* * *
+
-### `_.isUndefined(value)`
+
+
+
+### `_.isUndefined(value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2616 "View in source") [Ⓣ][1]
+
Checks if a `value` is `undefined`.
-[▲][1]
#### Arguments
1. `value` *(Mixed)*: The value to check.
@@ -1213,14 +1500,20 @@ _.isUndefined(void 0);
// => true
~~~
+* * *
+
-### `_.keys(object)`
+
+
+
+### `_.keys(object)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2633 "View in source") [Ⓣ][1]
+
Produces an array of the `object`'s enumerable own property names.
-[▲][1]
#### Arguments
1. `object` *(Object)*: The object to inspect.
@@ -1234,14 +1527,20 @@ _.keys({ 'one': 1, 'two': 2, 'three': 3 });
// => ['one', 'two', 'three']
~~~
+* * *
+
-### `_.last(array [, n, guard])`
+
+
+
+### `_.last(array [, n, guard])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1259 "View in source") [Ⓣ][1]
+
Gets the last value of the `array`. Pass `n` to return the lasy `n` values of the `array`.
-[▲][1]
#### Arguments
1. `array` *(Array)*: The array to query.
@@ -1257,14 +1556,20 @@ _.last([5, 4, 3, 2, 1]);
// => 1
~~~
+* * *
+
-### `_.lastIndexOf(array, value)`
+
+
+
+### `_.lastIndexOf(array, value)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1279 "View in source") [Ⓣ][1]
+
Gets the index at which the last occurrence of `value` is found using strict equality for comparisons, i.e. `===`.
-[▲][1]
#### Arguments
1. `array` *(Array)*: The array to search.
@@ -1279,14 +1584,20 @@ _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
// => 4
~~~
+* * *
+
-### `_.map(collection, callback [, thisArg])`
+
+
+
+### `_.map(collection, callback [, thisArg])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L707 "View in source") [Ⓣ][1]
+
Produces a new array of values by mapping each value in the `collection` through a transformation `callback`. The `callback` is bound to the `thisArg` value, if one is passed. The `callback` is invoked with `3` arguments; for arrays they are *(value, index, array)* and for objects they are *(value, key, object)*.
-[▲][1]
#### Arguments
1. `collection` *(Array|Object)*: The collection to iterate over.
@@ -1305,17 +1616,23 @@ _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
// => [3, 6, 9]
~~~
+* * *
+
-### `_.max(collection [, callback, thisArg])`
-Retrieves the maximum value of a `collection`. If `callback` is passed, it will be executed for each value in the `collection` to generate the criterion by which the value is ranked. The `callback` is invoked with `3` arguments; for arrays they are *(value, index, array)* and for objects they are *(value, key, object)*.
-[▲][1]
+
+
+
+### `_.max(array [, callback, thisArg])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1316 "View in source") [Ⓣ][1]
+
+Retrieves the maximum value of an `array`. If `callback` is passed, it will be executed for each value in the `array` to generate the criterion by which the value is ranked. The `callback` is invoked with `3` arguments; *(value, index, array)*.
#### Arguments
-1. `collection` *(Array|Object)*: The collection to iterate over.
+1. `array` *(Array)*: The array to iterate over.
2. `[callback]` *(Function)*: The function called per iteration.
3. `[thisArg]` *(Mixed)*: The `this` binding for the callback.
@@ -1334,14 +1651,20 @@ _.max(stooges, function(stooge) { return stooge.age; });
// => { 'name': 'curly', 'age': 60 };
~~~
+* * *
+
-### `_.memoize(func [, resolver])`
+
+
+
+### `_.memoize(func [, resolver])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1947 "View in source") [Ⓣ][1]
+
Creates a new function that memoizes the result of `func`. If `resolver` is passed, it will be used to determine the cache key for storing the result based on the arguments passed to the memoized function. By default, the first argument passed to the memoized function is used as the cache key.
-[▲][1]
#### Arguments
1. `func` *(Function)*: The function to have its output memoized.
@@ -1357,17 +1680,23 @@ var fibonacci = _.memoize(function(n) {
});
~~~
+* * *
+
-### `_.min(collection [, callback, thisArg])`
-Retrieves the minimum value of a `collection`. If `callback` is passed, it will be executed for each value in the `collection` to generate the criterion by which the value is ranked. The `callback` is invoked with `3` arguments; for arrays they are *(value, index, array)* and for objects they are *(value, key, object)*.
-[▲][1]
+
+
+
+### `_.min(array [, callback, thisArg])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1366 "View in source") [Ⓣ][1]
+
+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 invoked with `3` arguments; *(value, index, array)*.
#### Arguments
-1. `collection` *(Array|Object)*: The collection to iterate over.
+1. `array` *(Array)*: The array to iterate over.
2. `[callback]` *(Function)*: The function called per iteration.
3. `[thisArg]` *(Mixed)*: The `this` binding for the callback.
@@ -1380,14 +1709,20 @@ _.min([10, 5, 100, 2, 1000]);
// => 2
~~~
+* * *
+
-### `_.mixin(object)`
+
+
+
+### `_.mixin(object)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2767 "View in source") [Ⓣ][1]
+
Adds functions properties of `object` to the `lodash` function and chainable wrapper.
-[▲][1]
#### Arguments
1. `object` *(Object)*: The object of function properties to add to `lodash`.
@@ -1407,14 +1742,20 @@ _('larry').capitalize();
// => 'Larry'
~~~
+* * *
+
-### `_.noConflict()`
+
+
+
+### `_.noConflict()`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2798 "View in source") [Ⓣ][1]
+
Reverts the '_' variable to its previous value and returns a reference to the `lodash` function.
-[▲][1]
#### Returns
*(Function)*: Returns the `lodash` function.
@@ -1424,14 +1765,20 @@ Reverts the '_' variable to its previous value and returns a reference to the `l
var lodash = _.noConflict();
~~~
+* * *
+
-### `_.once(func)`
+
+
+
+### `_.once(func)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1973 "View in source") [Ⓣ][1]
+
Creates a new function that is restricted to one execution. Repeat calls to the function will return the value of the first call.
-[▲][1]
#### Arguments
1. `func` *(Function)*: The function to restrict.
@@ -1447,18 +1794,54 @@ initialize();
// Application is only created once.
~~~
+* * *
+
+
+
+
+
+
+
+
+
+### `_.partial(func [, arg1, arg2, ...])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2006 "View in source") [Ⓣ][1]
+
+Creates a new function that, when called, invokes `func` with any additional `partial` arguments prepended to those passed to the partially applied function. This method is similar `bind`, except it does **not** alter the `this` binding.
+
+#### Arguments
+1. `func` *(Function)*: The function to partially apply arguments to.
+2. `[arg1, arg2, ...]` *(Mixed)*: Arguments to be partially applied.
+
+#### Returns
+*(Function)*: Returns the new partially applied function.
+
+#### Example
+~~~ js
+var greet = function(greeting, name) { return greeting + ': ' + name; };
+var hi = _.partial(greet, 'hi');
+hi('moe');
+// => 'hi: moe'
+~~~
+
+* * *
+
-### `_.pick(object [, prop1, prop2, ..])`
+
+
+
+### `_.pick(object [, prop1, prop2, ...])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2655 "View in source") [Ⓣ][1]
+
Creates an object composed of the specified properties. Property names may be specified as individual arguments or as arrays of property names.
-[▲][1]
#### Arguments
1. `object` *(Object)*: The object to pluck.
-2. `[prop1, prop2, ..]` *(Object)*: The properties to pick.
+2. `[prop1, prop2, ...]` *(Object)*: The properties to pick.
#### Returns
*(Object)*: Returns an object composed of the picked properties.
@@ -1469,14 +1852,20 @@ _.pick({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'name', 'age');
// => { 'name': 'moe', 'age': 40 }
~~~
+* * *
+
-### `_.pluck(collection, property)`
+
+
+
+### `_.pluck(collection, property)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L729 "View in source") [Ⓣ][1]
+
Retrieves the value of a specified property from all values in a `collection`.
-[▲][1]
#### Arguments
1. `collection` *(Array|Object)*: The collection to iterate over.
@@ -1497,14 +1886,20 @@ _.pluck(stooges, 'name');
// => ['moe', 'larry', 'curly']
~~~
+* * *
+
-### `_.range([start=0], end [, step=1])`
+
+
+
+### `_.range([start=0], end [, step=1])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1424 "View in source") [Ⓣ][1]
+
Creates an array of numbers *(positive and/or negative)* progressing from `start` up to but not including `stop`. This method is a port of Python's `range()` function. See http://docs.python.org/library/functions.html#range.
-[▲][1]
#### Arguments
1. `[start=0]` *(Number)*: The start of the range.
@@ -1532,14 +1927,20 @@ _.range(0);
// => []
~~~
+* * *
+
-### `_.reduce(collection, callback [, accumulator, thisArg])`
+
+
+
+### `_.reduce(collection, callback [, accumulator, thisArg])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L759 "View in source") [Ⓣ][1]
+
Boils down a `collection` to a single value. The initial state of the reduction is `accumulator` and each successive step of it should be returned by the `callback`. The `callback` is bound to the `thisArg` value, if one is passed. The `callback` is invoked with `4` arguments; for arrays they are *(accumulator, value, index, array)* and for objects they are *(accumulator, value, key, object)*.
-[▲][1]
#### Arguments
1. `collection` *(Array|Object)*: The collection to iterate over.
@@ -1556,14 +1957,20 @@ var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; });
// => 6
~~~
+* * *
+
-### `_.reduceRight(collection, callback [, accumulator, thisArg])`
+
+
+
+### `_.reduceRight(collection, callback [, accumulator, thisArg])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L799 "View in source") [Ⓣ][1]
+
The right-associative version of `_.reduce`. The `callback` is bound to the `thisArg` value, if one is passed. The `callback` is invoked with `4` arguments; for arrays they are *(accumulator, value, index, array)* and for objects they are *(accumulator, value, key, object)*.
-[▲][1]
#### Arguments
1. `collection` *(Array|Object)*: The collection to iterate over.
@@ -1581,14 +1988,20 @@ var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
// => [4, 5, 2, 3, 0, 1]
~~~
+* * *
+
-### `_.reject(collection, callback [, thisArg])`
+
+
+
+### `_.reject(collection, callback [, thisArg])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L852 "View in source") [Ⓣ][1]
+
The opposite of `_.filter`, this method returns the values of a `collection` that `callback` does **not** return truthy for. The `callback` is invoked with `3` arguments; for arrays they are *(value, index, array)* and for objects they are *(value, key, object)*.
-[▲][1]
#### Arguments
1. `collection` *(Array|Object)*: The collection to iterate over.
@@ -1604,14 +2017,20 @@ var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
// => [1, 3, 5]
~~~
+* * *
+
-### `_.rest(array [, n, guard])`
+
+
+
+### `_.rest(array [, n, guard])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1460 "View in source") [Ⓣ][1]
+
The opposite of `_.initial`, this method gets all but the first value of the `array`. Pass `n` to exclude the first `n` values from the result.
-[▲][1]
#### Arguments
1. `array` *(Array)*: The array to query.
@@ -1627,14 +2046,20 @@ _.rest([5, 4, 3, 2, 1]);
// => [4, 3, 2, 1]
~~~
+* * *
+
-### `_.result(object, property)`
+
+
+
+### `_.result(object, property)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2828 "View in source") [Ⓣ][1]
+
Resolves the value of `property` on `object`. If the property is a function it will be invoked and its result returned, else the property value is returned.
-[▲][1]
#### Arguments
1. `object` *(Object)*: The object to inspect.
@@ -1659,17 +2084,23 @@ _.result(object, 'stuff');
// => 'nonsense'
~~~
+* * *
+
-### `_.shuffle(collection)`
-Produces a new array of shuffled `collection` values, using a version of the Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle.
-[▲][1]
+
+
+
+### `_.shuffle(array)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1478 "View in source") [Ⓣ][1]
+
+Produces a new array of shuffled `array` values, using a version of the Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle.
#### Arguments
-1. `collection` *(Array|Object)*: The collection to shuffle.
+1. `array` *(Array)*: The array to shuffle.
#### Returns
*(Array)*: Returns a new shuffled array.
@@ -1680,14 +2111,20 @@ _.shuffle([1, 2, 3, 4, 5, 6]);
// => [4, 1, 6, 3, 5, 2]
~~~
+* * *
+
-### `_.size(collection)`
-Gets the number of values in the `collection`.
-[▲][1]
+
+
+
+### `_.size(collection)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L875 "View in source") [Ⓣ][1]
+
+Gets the number of values in the `collection` or the `length` of a string value.
#### Arguments
1. `collection` *(Array|Object)*: The collection inspect.
@@ -1697,18 +2134,30 @@ Gets the number of values in the `collection`.
#### Example
~~~ js
+_.size([1, 2]);
+// => 2
+
_.size({ 'one': 1, 'two': 2, 'three': 3 });
// => 3
+
+_.size('curly');
+// => 5
~~~
+* * *
+
-### `_.some(collection, callback [, thisArg])`
-Checks if the `callback` returns truthy for **any** value of a `collection`. The function returns as soon as it finds passing value, and does not iterate over the entire `collection`. The `callback` is invoked with `3` arguments; for arrays they are *(value, index, array)* and for objects they are *(value, key, object)*.
-[▲][1]
+
+
+
+### `_.some(collection, callback [, thisArg])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L951 "View in source") [Ⓣ][1]
+
+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 invoked with `3` arguments; for arrays they are *(value, index, array)* and for objects they are *(value, key, object)*.
#### Arguments
1. `collection` *(Array|Object)*: The collection to iterate over.
@@ -1724,14 +2173,20 @@ _.some([null, 0, 'yes', false]);
// => true
~~~
+* * *
+
-### `_.sortBy(collection, callback [, thisArg])`
+
+
+
+### `_.sortBy(collection, callback [, thisArg])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L905 "View in source") [Ⓣ][1]
+
Produces a new sorted array, ranked in ascending order by the results of running each value of a `collection` through `callback`. The `callback` is invoked with `3` arguments; for arrays they are *(value, index, array)* and for objects they are *(value, key, object)*. The `callback` argument may also be the name of a property to sort by *(e.g. 'length')*.
-[▲][1]
#### Arguments
1. `collection` *(Array|Object)*: The collection to iterate over.
@@ -1745,16 +2200,25 @@ Produces a new sorted array, ranked in ascending order by the results of running
~~~ js
_.sortBy([1, 2, 3, 4, 5, 6], function(num) { return Math.sin(num); });
// => [5, 4, 6, 3, 1, 2]
+
+_.sortBy([1, 2, 3, 4, 5, 6], function(num) { return this.sin(num); }, Math);
+// => [5, 4, 6, 3, 1, 2]
~~~
+* * *
+
-### `_.sortedIndex(array, value [, callback])`
-Uses a binary search to determine the smallest index at which the `value` should be inserted into the `collection` in order to maintain the sort order of the `collection`. If `callback` is passed, it will be executed for each value in the `collection` to compute their sort ranking. The `callback` is invoked with `1` argument.
-[▲][1]
+
+
+
+### `_.sortedIndex(array, value [, callback])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1512 "View in source") [Ⓣ][1]
+
+Uses a binary search to determine the smallest index at which the `value` should be inserted into the `collection` in order to maintain the sort order of the `collection`. If `callback` is passed, it will be executed for each value in the `collection` to compute their sort ranking. The `callback` is invoked with `1` argument; *(value)*.
#### Arguments
1. `array` *(Array)*: The array to iterate over.
@@ -1770,14 +2234,20 @@ _.sortedIndex([10, 20, 30, 40, 50], 35);
// => 3
~~~
+* * *
+
-### `_.tap(value, interceptor)`
+
+
+
+### `_.tap(value, interceptor)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2693 "View in source") [Ⓣ][1]
+
Invokes `interceptor` with the `value` as the first argument, and then returns `value`. The primary purpose of this method is to "tap into" a method chain, in order to performoperations on intermediate results within the chain.
-[▲][1]
#### Arguments
1. `value` *(Mixed)*: The value to pass to `callback`.
@@ -1797,14 +2267,20 @@ _.chain([1,2,3,200])
// => [4, 40000]
~~~
+* * *
+
-### `_.template(text, data, options)`
+
+
+
+### `_.template(text, data, options)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2888 "View in source") [Ⓣ][1]
+
A JavaScript micro-templating method, similar to John Resig's implementation. Lo-Dash templating handles arbitrary delimiters, preserves whitespace, and correctly escapes quotes within interpolated code.
-[▲][1]
#### Arguments
1. `text` *(String)*: The template text.
@@ -1854,14 +2330,20 @@ _.template('<%= data.hasWith %>', { 'hasWith': 'no' }, { 'variable': 'data' });
~~~
+* * *
+
-### `_.throttle(func, wait)`
-Creates a new function that, when invoked, will only call the original function at most once per every `wait` milliseconds.
-[▲][1]
+
+
+
+### `_.throttle(func, wait)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2042 "View in source") [Ⓣ][1]
+
+Creates a new function that, when executed, will only call the `func` function at most once per every `wait` milliseconds. If the throttled function is invoked more than once, `func` will also be called on the trailing edge of the `wait` timeout. Subsequent calls to the throttled function will return the result of the last `func` call.
#### Arguments
1. `func` *(Function)*: The function to throttle.
@@ -1876,14 +2358,20 @@ var throttled = _.throttle(updatePosition, 100);
jQuery(window).on('scroll', throttled);
~~~
+* * *
+
-### `_.times(n, callback [, thisArg])`
-Executes the `callback` function `n` times.
-[▲][1]
+
+
+
+### `_.times(n, callback [, thisArg])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2965 "View in source") [Ⓣ][1]
+
+Executes the `callback` function `n` times. The `callback` is invoked with `1` argument; *(index)*.
#### Arguments
1. `n` *(Number)*: The number of times to execute the callback.
@@ -1895,14 +2383,20 @@ Executes the `callback` function `n` times.
_.times(3, function() { genie.grantWish(); });
~~~
+* * *
+
-### `_.toArray(collection)`
+
+
+
+### `_.toArray(collection)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L970 "View in source") [Ⓣ][1]
+
Converts the `collection`, into an array. Useful for converting the `arguments` object.
-[▲][1]
#### Arguments
1. `collection` *(Array|Object)*: The collection to convert.
@@ -1916,17 +2410,23 @@ Converts the `collection`, into an array. Useful for converting the `arguments`
// => [2, 3, 4]
~~~
+* * *
+
-### `_.union([array1, array2, ...])`
+
+
+
+### `_.union([array1, array2, ...])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1545 "View in source") [Ⓣ][1]
+
Computes the union of the passed-in arrays.
-[▲][1]
#### Arguments
-1. `[array1, array2, ...]` *(Mixed)*: Arrays to process.
+1. `[array1, array2, ...]` *(Array)*: Arrays to process.
#### Returns
*(Array)*: Returns a new array of unique values, in order, that are present in one or more of the arrays.
@@ -1937,14 +2437,20 @@ _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
// => [1, 2, 3, 101, 10]
~~~
+* * *
+
-### `_.uniq(array [, isSorted=false, callback])`
+
+
+
+### `_.uniq(array [, isSorted=false, callback])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1580 "View in source") [Ⓣ][1]
+
Produces 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 value of `array` is passed through a transformation `callback` before uniqueness is computed. The `callback` is invoked with `3` arguments; *(value, index, array)*.
-[▲][1]
#### Arguments
1. `array` *(Array)*: The array to process.
@@ -1960,14 +2466,20 @@ _.uniq([1, 2, 1, 3, 1, 4]);
// => [1, 2, 3, 4]
~~~
+* * *
+
-### `_.uniqueId([prefix])`
+
+
+
+### `_.uniqueId([prefix])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2988 "View in source") [Ⓣ][1]
+
Generates a unique id. If `prefix` is passed, the id will be appended to it.
-[▲][1]
#### Arguments
1. `[prefix]` *(String)*: The value to prefix the id with.
@@ -1981,32 +2493,20 @@ _.uniqueId('contact_');
// => 'contact_104'
~~~
+* * *
+
-### `_.value()`
-Extracts the value from a wrapped chainable object.
-[▲][1]
-
-#### Returns
-*(Mixed)*: Returns the wrapped object.
-
-#### Example
-~~~ js
-_([1, 2, 3]).value();
-// => [1, 2, 3]
-~~~
-
-
-
-### `_.values(collection)`
+### `_.values(collection)`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L998 "View in source") [Ⓣ][1]
+
Produces an array of enumerable own property values of the `collection`.
-[▲][1]
#### Arguments
1. `collection` *(Array|Object)*: The collection to inspect.
@@ -2020,14 +2520,20 @@ _.values({ 'one': 1, 'two': 2, 'three': 3 });
// => [1, 2, 3]
~~~
+* * *
+
-### `_.without(array [, value1, value2, ...])`
-Produces a new array with all occurrences of the values removed using strict equality for comparisons, i.e. `===`.
-[▲][1]
+
+
+
+### `_.without(array [, value1, value2, ...])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1618 "View in source") [Ⓣ][1]
+
+Produces a new array with all occurrences of the passed values removed using strict equality for comparisons, i.e. `===`.
#### Arguments
1. `array` *(Array)*: The array to filter.
@@ -2042,14 +2548,20 @@ _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
// => [2, 3, 4]
~~~
+* * *
+
-### `_.wrap(func, wrapper [, arg1, arg2, ...])`
+
+
+
+### `_.wrap(func, wrapper [, arg1, arg2, ...])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2094 "View in source") [Ⓣ][1]
+
Create a new function that passes the `func` function to the `wrapper` function as its first argument. Additional arguments are appended to those passed to the `wrapper` function.
-[▲][1]
#### Arguments
1. `func` *(Function)*: The function to wrap.
@@ -2069,17 +2581,23 @@ hello();
// => 'before, hello: moe, after'
~~~
+* * *
+
-### `_.zip([array1, array2, ...])`
+
+
+
+### `_.zip([array1, array2, ...])`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1648 "View in source") [Ⓣ][1]
+
Merges together the values of each of the arrays with the value at the corresponding position. 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.
-[▲][1]
#### Arguments
-1. `[array1, array2, ...]` *(Mixed)*: Arrays to process.
+1. `[array1, array2, ...]` *(Array)*: Arrays to process.
#### Returns
*(Array)*: Returns a new array of merged arrays.
@@ -2090,6 +2608,63 @@ _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
// => [['moe', 30, true], ['larry', 40, false], ['curly', 50, false]]
~~~
+* * *
+
+
+
+
+
+
+
+
+
+## `_.prototype`
+
+
+
+
+
+
+### `_.prototype.chain()`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3036 "View in source") [Ⓣ][1]
+
+Extracts the value from a wrapped chainable object.
+
+#### Returns
+*(Mixed)*: Returns the wrapped object.
+
+#### Example
+~~~ js
+_([1, 2, 3]).value();
+// => [1, 2, 3]
+~~~
+
+* * *
+
+
+
+
+
+
+
+
+
+### `_.prototype.value()`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3053 "View in source") [Ⓣ][1]
+
+Extracts the value from a wrapped chainable object.
+
+#### Returns
+*(Mixed)*: Returns the wrapped object.
+
+#### Example
+~~~ js
+_([1, 2, 3]).value();
+// => [1, 2, 3]
+~~~
+
+* * *
+
@@ -2102,36 +2677,75 @@ _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
-### `_.templateSettings`
+
+
+
+### `_.templateSettings`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3076 "View in source") [Ⓣ][1]
+
*(Object)*: By default, Lo-Dash uses ERB-style template delimiters, change the following template settings to use alternative delimiters.
-[▲][1]
+
+* * *
-### `_.templateSettings.escape`
+
+
+
+### `_.templateSettings.escape`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3085 "View in source") [Ⓣ][1]
+
*(RegExp)*: Used to detect `data` property values to be HTML-escaped.
-[▲][1]
+
+* * *
-### `_.templateSettings.evaluate`
+
+
+
+### `_.templateSettings.evaluate`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3094 "View in source") [Ⓣ][1]
+
*(RegExp)*: Used to detect code to be evaluated.
-[▲][1]
+
+* * *
-### `_.templateSettings.interpolate`
+
+
+
+### `_.templateSettings.interpolate`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3103 "View in source") [Ⓣ][1]
+
*(RegExp)*: Used to detect `data` property values to inject.
-[▲][1]
+
+* * *
+
+
+
+
+
+
+
+
+
+### `_.templateSettings.variable`
+# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3112 "View in source") [Ⓣ][1]
+
+*(String)*: Used to reference the data object in the template text.
+
+* * *
@@ -2142,4 +2756,4 @@ _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
- [1]: #readme "Jump back to the TOC."
\ No newline at end of file
+ [1]: #toc "Jump back to the TOC."
\ No newline at end of file
diff --git a/doc/parse.php b/doc/parse.php
index 658c1ffb2f..aa87779dc0 100644
--- a/doc/parse.php
+++ b/doc/parse.php
@@ -21,7 +21,7 @@
// generate Markdown
$markdown = docdown(array(
'path' => '../' . $file,
- 'title' => 'Lo-Dash v0.1.0',
+ 'title' => 'Lo-Dash v0.2.0',
'url' => 'https://github.com/bestiejs/lodash/blob/master/lodash.js'
));
diff --git a/lodash.js b/lodash.js
index e4d5038d5f..ca8747d4ce 100644
--- a/lodash.js
+++ b/lodash.js
@@ -1,53 +1,106 @@
/*!
- * Lo-Dash v0.1.0
+ * Lo-Dash v0.2.0
* Copyright 2012 John-David Dalton
* Based on Underscore.js 1.3.3, copyright 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
*
- * Available under MIT license
+ * Available under MIT license
*/
;(function(window, undefined) {
'use strict';
- /** Used to escape and unescape characters in templates */
+ /** Detect free variable `exports` */
+ var freeExports = typeof exports == 'object' && exports &&
+ (typeof global == 'object' && global && global == global.global && (window = global), exports);
+
+ /**
+ * Used to detect the JavaScript engine's argument length limit.
+ *
+ * The initial value of `argsLimit` is low enough not to cause uncatchable
+ * errors in Java and avoid locking up older browsers like Safari 3.
+ *
+ * Some engines have a limit on the number of arguments functions can accept
+ * before clipping the argument length or throwing an error.
+ * https://bugs.webkit.org/show_bug.cgi?id=80797
+ *
+ * For example Firefox's limits have been observed to be at least:
+ * Firefox 2 - 35,535
+ * Firefox 3.6 - 16,777,215
+ * Firefox 4-7 - 523,264
+ * Firefox >= 8 - Throws error
+ */
+ var argsLimit = 5e4;
+
+ try {
+ (function() {
+ argsLimit = arguments.length;
+ }).apply(null, Array(argsLimit));
+ } catch(e) { }
+
+ /** Used to escape characters in templates */
var escapes = {
'\\': '\\',
"'": "'",
- 'r': '\r',
- 'n': '\n',
- 't': '\t',
- 'u2028': '\u2028',
- 'u2029': '\u2029'
+ '\n': 'n',
+ '\r': 'r',
+ '\t': 't',
+ '\u2028': 'u2028',
+ '\u2029': 'u2029'
};
- // assign the result as keys and the keys as values
- (function() {
- for (var prop in escapes) {
- escapes[escapes[prop]] = prop;
- }
- }());
-
- /** Detect free variable `exports` */
- var freeExports = typeof exports == 'object' && exports &&
- (typeof global == 'object' && global && global == global.global && (window = global), exports);
+ /**
+ * Detect the JScript [[DontEnum]] bug:
+ * In IE < 9 an objects own properties, shadowing non-enumerable ones, are
+ * made non-enumerable as well.
+ */
+ var hasDontEnumBug = !{ 'valueOf': 0 }.propertyIsEnumerable('valueOf');
/** Used to generate unique IDs */
var idCounter = 0;
+ /** Used to determine if values are of the language type Object */
+ var objectTypes = {
+ 'boolean': false,
+ 'function': true,
+ 'object': true,
+ 'number': false,
+ 'string': false,
+ 'undefined': false
+ };
+
/** Used to restore the original `_` reference in `noConflict` */
var oldDash = window._;
- /** Used to replace unescape characters with their escaped counterpart */
- var reEscaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
+ /** Used to match the "escape" template delimiters */
+ var reEscapeDelimiter = /<%-([\s\S]+?)%>/g;
- /**
- * Used for `templateSettings` properties such as `escape`, `evaluate`,
- * or `interpolate` with explicitly assigned falsey values to ensure no match
- * is made.
- */
- var reNoMatch = /.^/;
+ /** Used to match the "evaluate" template delimiters */
+ var reEvaluateDelimiter = /<%([\s\S]+?)%>/g;
+
+ /** Used to match the "interpolate" template delimiters */
+ var reInterpolateDelimiter = /<%=([\s\S]+?)%>/g;
+
+ /** Used to detect if a method is native */
+ var reNative = RegExp('^' + ({}.valueOf + '')
+ .replace(/[.*+?^=!:${}()|[\]\/\\]/g, '\\$&')
+ .replace(/valueOf|for [^\]]+/g, '.+?') + '$');
+
+ /** Used to match tokens in template text */
+ var reToken = /__token__(\d+)/g;
+
+ /** Used to match unescaped characters in template text */
+ var reUnescaped = /['\n\r\t\u2028\u2029\\]/g;
+
+ /** Used to fix the JScript [[DontEnum]] bug */
+ var shadowed = [
+ 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
+ 'toLocaleString', 'toString', 'valueOf'
+ ];
- /** Used to replace escaped characters with their unescaped counterpart */
- var reUnescaper = /\\(\\|'|r|n|t|u2028|u2029)/g;
+ /** Used to replace template delimiters */
+ var token = '__token__';
+
+ /** Used to store tokenized template text snippets */
+ var tokenized = [];
/** Object#toString result shortcuts */
var arrayClass = '[object Array]',
@@ -60,123 +113,196 @@
/** Native prototype shortcuts */
var ArrayProto = Array.prototype,
- ObjProto = Object.prototype;
+ ObjectProto = Object.prototype;
/** Native method shortcuts */
var concat = ArrayProto.concat,
- hasOwnProperty = ObjProto.hasOwnProperty,
+ hasOwnProperty = ObjectProto.hasOwnProperty,
push = ArrayProto.push,
slice = ArrayProto.slice,
- toString = ObjProto.toString,
- unshift = ArrayProto.unshift;
+ toString = ObjectProto.toString;
+
+ /* Used if `Function#bind` exists and is inferred to be fast (i.e. all but V8) */
+ var nativeBind = reNative.test(nativeBind = slice.bind) &&
+ /\n|Opera/.test(nativeBind + toString.call(window.opera)) && nativeBind;
/* Native method shortcuts for methods with the same name as other `lodash` methods */
- var nativeIsArray = Array.isArray,
+ var nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray,
nativeIsFinite = window.isFinite,
- nativeKeys = Object.keys;
+ nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys;
/** Timer shortcuts */
var clearTimeout = window.clearTimeout,
setTimeout = window.setTimeout;
- /** Compilation options for `_.difference` */
- var differenceFactoryOptions = {
- 'args': 'array',
- 'top': 'var values=concat.apply([],slice.call(arguments,1))',
- 'init': '[]',
- 'inLoop': 'if(indexOf(values,array[index])<0)result.push(array[index])'
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The template used to create iterator functions.
+ *
+ * @private
+ * @param {Obect} data The data object used to populate the text.
+ * @returns {String} Returns the interpolated text.
+ */
+ var iteratorTemplate = template(
+ // assign the `result` variable an initial value
+ 'var index, result<% if (init) { %> = <%= init %><% } %>;\n' +
+ // add code to exit early or do so if the first argument is falsey
+ '<%= exit %>;\n' +
+ // add code after the exit snippet but before the iteration branches
+ '<%= top %>;\n' +
+
+ // the following branch is for iterating arrays and array-like objects
+ '<% if (arrayBranch) { %>' +
+ 'var length = <%= firstArg %>.length; index = -1;' +
+ ' <% if (objectBranch) { %>\nif (length === +length) {<% } %>\n' +
+ ' <%= arrayBranch.beforeLoop %>;\n' +
+ ' while (<%= arrayBranch.loopExp %>) {\n' +
+ ' <%= arrayBranch.inLoop %>;\n' +
+ ' }' +
+ ' <% if (objectBranch) { %>\n}\n<% }' +
+ '}' +
+
+ // the following branch is for iterating an object's own/inherited properties
+ 'if (objectBranch) {' +
+ ' if (arrayBranch) { %>else {\n<% }' +
+ ' if (!hasDontEnumBug) { %> var skipProto = typeof <%= iteratedObject %> == \'function\';\n<% } %>' +
+ ' <%= objectBranch.beforeLoop %>;\n' +
+ ' for (<%= objectBranch.loopExp %>) {' +
+ ' \n<%' +
+ ' if (hasDontEnumBug) {' +
+ ' if (useHas) { %> if (<%= hasExp %>) {\n <% } %>' +
+ ' <%= objectBranch.inLoop %>;<%' +
+ ' if (useHas) { %>\n }<% }' +
+ ' }' +
+ ' else {' +
+ ' %>' +
+
+ // Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
+ // (if the prototype or a property on the prototype has been set)
+ // incorrectly sets a function's `prototype` property [[Enumerable]]
+ // value to `true`. Because of this Lo-Dash standardizes on skipping
+ // the the `prototype` property of functions regardless of its
+ // [[Enumerable]] value.
+ ' if (!(skipProto && index == \'prototype\')<% if (useHas) { %> && <%= hasExp %><% } %>) {\n' +
+ ' <%= objectBranch.inLoop %>;\n' +
+ ' }' +
+ ' <% } %>\n' +
+ ' }' +
+
+ // Because IE < 9 can't set the `[[Enumerable]]` attribute of an
+ // existing property and the `constructor` property of a prototype
+ // defaults to non-enumerable, Lo-Dash skips the `constructor`
+ // property when it infers it's iterating over a `prototype` object.
+ ' <% if (hasDontEnumBug) { %>\n' +
+ ' var ctor = <%= iteratedObject %>.constructor;\n' +
+ ' <% for (var k = 0; k < 7; k++) { %>\n' +
+ ' index = \'<%= shadowed[k] %>\';\n' +
+ ' if (<%' +
+ ' if (shadowed[k] == \'constructor\') {' +
+ ' %>!(ctor && ctor.prototype === <%= iteratedObject %>) && <%' +
+ ' } %><%= hasExp %>) {\n' +
+ ' <%= objectBranch.inLoop %>;\n' +
+ ' }<%' +
+ ' }' +
+ ' }' +
+ ' if (arrayBranch) { %>\n}<% }' +
+ '} %>\n' +
+
+ // add code to the bottom of the iteration function
+ '<%= bottom %>;\n' +
+ // finally, return the `result`
+ 'return result'
+
+ , null, {
+ 'evaluate': reEvaluateDelimiter,
+ 'interpolate': reInterpolateDelimiter
+ });
+
+ /**
+ * Reusable iterator options shared by
+ * `every`, `filter`, `find`, `forEach`,`groupBy`, `map`, `reject`, and `some`.
+ */
+ var baseIteratorOptions = {
+ 'args': 'collection, callback, thisArg',
+ 'init': 'collection',
+ 'top':
+ 'if (!callback) {\n' +
+ ' callback = identity\n' +
+ '}\n' +
+ 'else if (thisArg) {\n' +
+ ' callback = bind(callback, thisArg)\n' +
+ '}',
+ 'inLoop': 'callback(collection[index], index, collection)'
};
- /** Compilation options for `_.every` */
- var everyFactoryOptions = {
+ /** Reusable iterator options for `every` and `some` */
+ var everyIteratorOptions = {
'init': 'true',
- 'inLoop': 'if(!callback(collection[index],index,collection))return !result'
+ 'inLoop': 'if (!callback(collection[index], index, collection)) return !result'
};
- /** Compilation options for `_.extend` */
- var extendFactoryOptions = {
+ /** Reusable iterator options for `defaults` and `extend` */
+ var extendIteratorOptions = {
'args': 'object',
'init': 'object',
- 'beforeLoop': 'for(var source,j=1,length=arguments.length;j=computed)computed=current,result=collection[index]'
- };
-
/*--------------------------------------------------------------------------*/
/**
* The `lodash` function.
*
* @name _
- * @param {Mixed} value The value to wrap in a `Lodash` instance.
- * @returns {Object} Returns a `Lodash` instance.
+ * @constructor
+ * @param {Mixed} value The value to wrap in a `LoDash` instance.
+ * @returns {Object} Returns a `LoDash` instance.
*/
function lodash(value) {
// allow invoking `lodash` without the `new` operator
- return new Lodash(value);
+ return new LoDash(value);
}
/**
- * Creates a `Lodash` instance that wraps a value to allow chaining.
+ * Creates a `LoDash` instance that wraps a value to allow chaining.
*
* @private
* @constructor
* @param {Mixed} value The value to wrap.
*/
- function Lodash(value) {
+ function LoDash(value) {
+ // exit early if already wrapped
+ if (value && value._wrapped) {
+ return value;
+ }
this._wrapped = value;
}
@@ -198,12 +324,12 @@
* _.isArray([1, 2, 3]);
* // => true
*/
- var isArray = nativeIsArray || function isArray(value) {
+ var isArray = nativeIsArray || function(value) {
return toString.call(value) == arrayClass;
};
/**
- * Checks if a `value` is empty. Arrays or strings with a length of 0 and
+ * Checks if a `value` is empty. Arrays or strings with a length of `0` and
* objects with no enumerable own properties are considered "empty".
*
* @static
@@ -219,131 +345,189 @@
* _.isEmpty({});
* // => true
*/
- var isEmpty = iterationFactory({
+ var isEmpty = createIterator({
'args': 'value',
- 'iterate': 'objects',
- 'top': 'var className=toString.call(value)',
'init': 'true',
- 'beforeLoop': 'if(className==arrayClass||className==stringClass)return !value.length',
- 'inLoop': 'return false'
+ 'top':
+ 'var className = toString.call(value);\n' +
+ 'if (className == arrayClass || className == stringClass) return !value.length',
+ 'inLoop': {
+ 'object': 'return false'
+ }
});
/*--------------------------------------------------------------------------*/
/**
- * Compiles iteration functions.
+ * Creates compiled iteration functions. The iteration function will be created
+ * to iterate over only objects if the first argument of `options.args` is
+ * "object" or `options.inLoop.array` is falsey.
*
* @private
- * @param {Object} [options1, options2, ..] The compile options objects.
+ * @param {Object} [options1, options2, ...] The compile options objects.
+ *
+ * args - A string of comma separated arguments the iteration function will
+ * accept.
+ *
+ * init - A string to specify the initial value of the `result` variable.
+ *
+ * exit - A string of code to use in place of the default exit-early check
+ * of `if (!arguments[0]) return result`.
+ *
+ * top - A string of code to execute after the exit-early check but before
+ * the iteration branches.
+ *
+ * beforeLoop - A string or object containing an "array" or "object" property
+ * of code to execute before the array or object loops.
+ *
+ * loopExp - A string or object containing an "array" or "object" property
+ * of code to execute as the array or object loop expression.
+ *
+ * useHas - A boolean to specify whether or not to use `hasOwnProperty` checks
+ * in the object loop.
+ *
+ * inLoop - A string or object containing an "array" or "object" property
+ * of code to execute in the array or object loops.
+ *
+ * bottom - A string of code to execute after the iteration branches but
+ * before the `result` is returned.
+ *
* @returns {Function} Returns the compiled function.
*/
- function iterationFactory() {
- var prop,
+ function createIterator() {
+ var object,
+ prop,
+ value,
index = -1,
- array = {},
- object = {},
- options = {},
- props = ['beforeLoop', 'loopExp', 'inLoop', 'afterLoop'];
-
- // use simple loops to merge options because `extend` isn't defined yet
- while (++index < arguments.length) {
- for (prop in arguments[index]) {
- options[prop] = arguments[index][prop];
+ length = arguments.length;
+
+ // merge options into a template data object
+ var data = {
+ 'bottom': '',
+ 'exit': '',
+ 'init': '',
+ 'top': '',
+ 'arrayBranch': { 'loopExp': '++index < length' },
+ 'objectBranch': {}
+ };
+
+ while (++index < length) {
+ object = arguments[index];
+ for (prop in object) {
+ value = (value = object[prop]) == null ? '' : value;
+ // keep this regexp explicit for the build pre-process
+ if (/beforeLoop|loopExp|inLoop/.test(prop)) {
+ if (typeof value == 'string') {
+ value = { 'array': value, 'object': value };
+ }
+ data.arrayBranch[prop] = value.array;
+ data.objectBranch[prop] = value.object;
+ } else {
+ data[prop] = value;
+ }
}
}
- // assign the `array` and `object` branch options
- while ((prop = props.pop())) {
- if (typeof options[prop] == 'object') {
- array[prop] = options[prop].array;
- object[prop] = options[prop].object;
- } else {
- array[prop] = object[prop] = options[prop] || '';
- }
+ // set additional template data values
+ var args = data.args,
+ arrayBranch = data.arrayBranch,
+ objectBranch = data.objectBranch,
+ firstArg = /^[^,]+/.exec(args)[0],
+ loopExp = objectBranch.loopExp,
+ iteratedObject = /\S+$/.exec(loopExp || firstArg)[0];
+
+ data.firstArg = firstArg;
+ data.hasDontEnumBug = hasDontEnumBug;
+ data.hasExp = 'hasOwnProperty.call(' + iteratedObject + ', index)';
+ data.iteratedObject = iteratedObject;
+ data.shadowed = shadowed;
+ data.useHas = data.useHas !== false;
+
+ if (!data.exit) {
+ data.exit = 'if (' + firstArg + ' == null) return result';
+ }
+ if (firstArg == 'object' || !arrayBranch.inLoop) {
+ data.arrayBranch = null;
+ }
+ if (!loopExp) {
+ objectBranch.loopExp = 'index in ' + iteratedObject;
}
+ // create the function factory
+ var factory = Function(
+ 'arrayClass, bind, concat, funcClass, hasOwnProperty, identity, indexOf, ' +
+ 'isArray, isEmpty, objectTypes, slice, stringClass, toString, undefined',
+ '"use strict"; return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}'
+ );
+ // return the compiled function
+ return factory(
+ arrayClass, bind, concat, funcClass, hasOwnProperty, identity, indexOf,
+ isArray, isEmpty, objectTypes, slice, stringClass, toString
+ );
+ }
- var args = options.args,
- firstArg = /^[^,]+/.exec(args)[0],
- init = options.init,
- iterate = options.iterate,
- arrayBranch = !(args == 'object' || iterate == 'objects'),
- objectBranch = !(args == 'array' || iterate == 'arrays'),
- useHas = options.useHas !== false;
-
- // stings used to compile methods are minified during the build process
- return Function('arrayClass,bind,concat,funcClass,hasOwnProperty,identity,' +
- 'indexOf,Infinity,isArray,isEmpty,Math,slice,stringClass,' +
- 'toString,undefined',
- // compile the function in strict mode
- '"use strict";' +
- // compile the arguments the function accepts
- 'return function(' + args + '){\n' +
- // add code to the top of the iteration method
- (options.top || '') + ';\n' +
- // assign the `result` variable an initial value
- ('var index, result' + (init ? '=' + init : '')) + ';\n' +
- // exit early if the first argument, e.g. `collection`, is nullish
- 'if(' + firstArg + '==undefined)return ' + (options.exits || 'result') + ';\n' +
- // the following branch is for iterating arrays and array-like objects
- (arrayBranch
- // initialize `length` and `index` variables
- ? 'var length=' + firstArg + '.length;\nindex=-1;\n' +
- // check if the `collection` is array-like when there is an object iteration branch
- ((objectBranch ? 'if(length===+length){\n' : '') +
- // add code before the while-loop
- (array.beforeLoop || '') + ';\n' +
- // add a custom loop expression
- 'while(' + (array.loopExp || '++index true
*/
- var contains = iterationFactory({
- 'args': 'collection,target',
+ var contains = createIterator({
+ 'args': 'collection, target',
'init': 'false',
- 'inLoop': 'if(collection[index]===target)return true'
+ 'inLoop': 'if (collection[index] === target) return true'
});
/**
- * Checks if the `callback` returns truthy for **all** values of a `collection`.
- * The `callback` is invoked with 3 arguments; for arrays they are
- * (value, index, array) and for objects they are (value, key, object).
+ * Checks if the `callback` returns a truthy value for **all** elements of a
+ * `collection`. The `callback` is invoked with 3 arguments; for arrays they
+ * are (value, index, array) and for objects they are (value, key, object).
*
* @static
* @memberOf _
@@ -386,9 +570,9 @@
* @example
*
* _.every([true, 1, null, 'yes'], Boolean);
- * => false
+ * // => false
*/
- var every = iterationFactory(forEachFactoryOptions, everyFactoryOptions);
+ var every = createIterator(baseIteratorOptions, everyIteratorOptions);
/**
* Examines each value in a `collection`, returning an array of all values the
@@ -409,7 +593,7 @@
* var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
* // => [2, 4, 6]
*/
- var filter = iterationFactory(forEachFactoryOptions, filterFactoryOptions);
+ var filter = createIterator(baseIteratorOptions, filterIteratorOptions);
/**
* Examines each value in a `collection`, returning the first one the `callback`
@@ -431,8 +615,8 @@
* var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
* // => 2
*/
- var find = iterationFactory(forEachFactoryOptions, {
- 'inLoop': 'if(callback(collection[index],index,collection))return collection[index]'
+ var find = createIterator(baseIteratorOptions, {
+ 'inLoop': 'if (callback(collection[index], index, collection)) return collection[index]'
});
/**
@@ -451,13 +635,15 @@
* @returns {Array|Object} Returns the `collection`.
* @example
*
- * _.forEach([1, 2, 3], function(num) { alert(num); });
- * // => alerts each number in turn...
- *
* _.forEach({ 'one': 1, 'two': 2, 'three': 3}, function(num) { alert(num); });
- * // => alerts each number in turn...
+ * // => alerts each number in turn
+ *
+ * _([1, 2, 3]).forEach(function(num) { alert(num); }).join(',');
+ * // => alerts each number in turn and returns '1,2,3'
*/
- var forEach = iterationFactory(forEachFactoryOptions);
+ var forEach = createIterator(baseIteratorOptions, {
+ 'top': 'if (thisArg) callback = bind(callback, thisArg)'
+ });
/**
* Splits a `collection` into sets, grouped by the result of running each value
@@ -478,46 +664,23 @@
* _.groupBy([1.3, 2.1, 2.4], function(num) { return Math.floor(num); });
* // => { '1': [1.3], '2': [2.1, 2.4] }
*
+ * _.groupBy([1.3, 2.1, 2.4], function(num) { return this.floor(num); }, Math);
+ * // => { '1': [1.3], '2': [2.1, 2.4] }
+ *
* _.groupBy(['one', 'two', 'three'], 'length');
* // => { '3': ['one', 'two'], '5': ['three'] }
*/
- function groupBy(collection, callback, thisArg) {
- var result = {};
- if (!isFunction(callback)) {
- var prop = callback;
- callback = function(collection) { return collection[prop]; };
- }
- forEach(collection, function(value, index, collection) {
- var prop = callback(value, index, collection);
- (result[prop] || (result[prop] = [])).push(value);
- });
- return result;
- }
-
- /**
- * Calls the method named by `methodName` for each value of the `collection`.
- * Additional arguments will be passed to each invoked method.
- *
- * @static
- * @memberOf _
- * @category Collections
- * @param {Array|Object} collection The collection to iterate over.
- * @param {String} methodName The name of the method to invoke.
- * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with.
- * @returns {Array} Returns a new array of values returned from each invoked method.
- * @example
- *
- * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
- * // => [[1, 5, 7], [1, 2, 3]]
- */
- function invoke(collection, methodName) {
- var args = slice.call(arguments, 2),
- isFunc = isFunction(methodName);
-
- return map(collection, function(value) {
- return (isFunc ? methodName || value : value[methodName]).apply(value, args);
- });
- }
+ var groupBy = createIterator(baseIteratorOptions, {
+ 'init': '{}',
+ 'top':
+ 'var prop, isFunc = toString.call(callback) == funcClass;\n' +
+ 'if (isFunc && thisArg) callback = bind(callback, thisArg)',
+ 'inLoop':
+ 'prop = isFunc\n' +
+ ' ? callback(collection[index], index, collection)\n' +
+ ' : collection[index][callback];\n' +
+ '(result[prop] || (result[prop] = [])).push(collection[index])'
+ });
/**
* Produces a new array of values by mapping each value in the `collection`
@@ -541,58 +704,7 @@
* _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
* // => [3, 6, 9]
*/
- var map = iterationFactory(forEachFactoryOptions, mapFactoryOptions);
-
- /**
- * Retrieves the maximum value of a `collection`. If `callback` is passed,
- * it will be executed for each value in the `collection` to generate the
- * criterion by which the value is ranked. The `callback` is invoked with 3
- * arguments; for arrays they are (value, index, array) and for objects they
- * are (value, key, object).
- *
- * @static
- * @memberOf _
- * @category Collections
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [callback] The function called per iteration.
- * @param {Mixed} [thisArg] The `this` binding for the callback.
- * @returns {Mixed} Returns the maximum value.
- * @example
- *
- * var stooges = [
- * { 'name': 'moe', 'age': 40 },
- * { 'name': 'larry', 'age': 50 },
- * { 'name': 'curly', 'age': 60 }
- * ];
- *
- * _.max(stooges, function(stooge) { return stooge.age; });
- * // => { 'name': 'curly', 'age': 60 };
- */
- var max = iterationFactory(forEachFactoryOptions, maxFactoryOptions);
-
- /**
- * Retrieves the minimum value of a `collection`. If `callback` is passed,
- * it will be executed for each value in the `collection` to generate the
- * criterion by which the value is ranked. The `callback` is invoked with 3
- * arguments; for arrays they are (value, index, array) and for objects they
- * are (value, key, object).
- *
- * @static
- * @memberOf _
- * @category Collections
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [callback] The function called per iteration.
- * @param {Mixed} [thisArg] The `this` binding for the callback.
- * @returns {Mixed} Returns the minimum value.
- * @example
- *
- * _.min([10, 5, 100, 2, 1000]);
- * // => 2
- */
- var min = iterationFactory(forEachFactoryOptions, maxFactoryOptions, {
- 'top': maxFactoryOptions.top.replace('-', '').replace('max', 'min'),
- 'inLoop': maxFactoryOptions.inLoop.replace('>=', '<')
- });
+ var map = createIterator(baseIteratorOptions, mapIteratorOptions);
/**
* Retrieves the value of a specified property from all values in a `collection`.
@@ -614,11 +726,11 @@
* _.pluck(stooges, 'name');
* // => ['moe', 'larry', 'curly']
*/
- var pluck = iterationFactory(mapFactoryOptions, {
- 'args': 'collection,property',
+ var pluck = createIterator(mapIteratorOptions, {
+ 'args': 'collection, property',
'inLoop': {
- 'array': 'result[index]=collection[index][property]',
- 'object': 'result[result.length]=collection[index][property]'
+ 'array': 'result[index] = collection[index][property]',
+ 'object': 'result.push(collection[index][property])'
}
});
@@ -644,24 +756,22 @@
* var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; });
* // => 6
*/
- var reduce = iterationFactory({
- 'args':
- 'collection,callback,accumulator,thisArg',
+ var reduce = createIterator({
+ 'args': 'collection, callback, accumulator, thisArg',
+ 'init': 'accumulator',
'top':
- 'var initial=arguments.length>2;\n' +
- 'if(thisArg)callback=bind(callback,thisArg)',
- 'init':
- 'accumulator',
+ 'var noaccum = arguments.length < 3;\n' +
+ 'if (thisArg) callback = bind(callback, thisArg)',
'beforeLoop': {
- 'array': 'if(!initial)result=collection[++index]'
+ 'array': 'if (noaccum) result = collection[++index]'
},
'inLoop': {
'array':
- 'result=callback(result,collection[index],index,collection)',
+ 'result = callback(result, collection[index], index, collection)',
'object':
- 'result=initial\n' +
- '?callback(result,collection[index],index,collection)\n' +
- ':(initial=true,collection[index])'
+ 'result = noaccum\n' +
+ ' ? (noaccum = false, collection[index])\n' +
+ ' : callback(result, collection[index], index, collection)'
}
});
@@ -686,37 +796,39 @@
* var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
* // => [4, 5, 2, 3, 0, 1]
*/
- function reduceRight(collection, callback, result, thisArg) {
- var initial = arguments.length > 2;
- if (collection == undefined) {
- return result;
+ function reduceRight(collection, callback, accumulator, thisArg) {
+ if (!collection) {
+ return accumulator;
}
+
+ var length = collection.length,
+ noaccum = arguments.length < 3;
+
if(thisArg) {
callback = bind(callback, thisArg);
}
- var length = collection.length;
if (length === +length) {
- if (length && !initial) {
- result = collection[--length];
+ if (length && noaccum) {
+ accumulator = collection[--length];
}
while (length--) {
- result = callback(result, collection[length], length, collection);
+ accumulator = callback(accumulator, collection[length], length, collection);
}
- return result;
+ return accumulator;
}
var prop,
props = keys(collection);
length = props.length;
- if (length && !initial) {
- result = collection[props[--length]];
+ if (length && noaccum) {
+ accumulator = collection[props[--length]];
}
while (length--) {
prop = props[length];
- result = callback(result, collection[prop], prop, collection);
+ accumulator = callback(accumulator, collection[prop], prop, collection);
}
- return result;
+ return accumulator;
}
/**
@@ -737,38 +849,12 @@
* var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
* // => [1, 3, 5]
*/
- var reject = iterationFactory(forEachFactoryOptions, filterFactoryOptions, {
- 'inLoop': '!' + filterFactoryOptions.inLoop
+ var reject = createIterator(baseIteratorOptions, filterIteratorOptions, {
+ 'inLoop': '!' + filterIteratorOptions.inLoop
});
/**
- * Produces a new array of shuffled `collection` 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} collection The collection to shuffle.
- * @returns {Array} Returns a new shuffled array.
- * @example
- *
- * _.shuffle([1, 2, 3, 4, 5, 6]);
- * // => [4, 1, 6, 3, 5, 2]
- */
- function shuffle(collection) {
- var rand,
- result = [];
-
- forEach(collection, function(value, index) {
- rand = Math.floor(Math.random() * (index + 1));
- result[index] = result[rand];
- result[rand] = value;
- });
- return result;
- }
-
- /**
- * Gets the number of values in the `collection`.
+ * Gets the number of values in the `collection` or the `length` of a string value.
*
* @static
* @memberOf _
@@ -777,8 +863,14 @@
* @returns {Number} Returns the number of values in the collection.
* @example
*
+ * _.size([1, 2]);
+ * // => 2
+ *
* _.size({ 'one': 1, 'two': 2, 'three': 3 });
* // => 3
+ *
+ * _.size('curly');
+ * // => 5
*/
function size(collection) {
var className = toString.call(collection);
@@ -806,9 +898,12 @@
*
* _.sortBy([1, 2, 3, 4, 5, 6], function(num) { return Math.sin(num); });
* // => [5, 4, 6, 3, 1, 2]
+ *
+ * _.sortBy([1, 2, 3, 4, 5, 6], function(num) { return this.sin(num); }, Math);
+ * // => [5, 4, 6, 3, 1, 2]
*/
function sortBy(collection, callback, thisArg) {
- if (!isFunction(callback)) {
+ if (toString.call(callback) != funcClass) {
var prop = callback;
callback = function(collection) { return collection[prop]; };
} else if (thisArg) {
@@ -834,11 +929,11 @@
}
/**
- * Checks if the `callback` returns truthy for **any** value of a `collection`.
- * The function returns as soon as it finds passing value, and does not iterate
- * over the entire `collection`. The `callback` is invoked with 3 arguments; for
- * arrays they are (value, index, array) and for objects they are
- * (value, key, object).
+ * 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 invoked
+ * with 3 arguments; for arrays they are (value, index, array) and for objects
+ * they are (value, key, object).
*
* @static
* @memberOf _
@@ -853,43 +948,11 @@
* _.some([null, 0, 'yes', false]);
* // => true
*/
- var some = iterationFactory(forEachFactoryOptions, everyFactoryOptions, {
+ var some = createIterator(baseIteratorOptions, everyIteratorOptions, {
'init': 'false',
- 'inLoop': everyFactoryOptions.inLoop.replace('!', '')
+ 'inLoop': everyIteratorOptions.inLoop.replace('!', '')
});
- /**
- * Uses a binary search to determine the smallest index at which the `value`
- * should be inserted into the `collection` in order to maintain the sort order
- * of the `collection`. If `callback` is passed, it will be executed for each
- * value in the `collection` to compute their sort ranking. The `callback` is
- * invoked with 1 argument.
- *
- * @static
- * @memberOf _
- * @category Collections
- * @param {Array} array The array to iterate over.
- * @param {Mixed} value The value to evaluate.
- * @param {Function} [callback] The function called per iteration.
- * @returns {Number} Returns the index at which the value should be inserted
- * into the collection.
- * @example
- *
- * _.sortedIndex([10, 20, 30, 40, 50], 35);
- * // => 3
- */
- function sortedIndex(array, object, callback) {
- var low = 0,
- high = array.length;
-
- callback || (callback = identity);
- while (low < high) {
- var mid = (low + high) >> 1;
- callback(array[mid]) < callback(object) ? (low = mid + 1) : (high = mid);
- }
- return low;
- }
-
/**
* Converts the `collection`, into an array. Useful for converting the
* `arguments` object.
@@ -908,7 +971,7 @@
if (!collection) {
return [];
}
- if (isFunction(collection.toArray)) {
+ if (toString.call(collection.toArray) == funcClass) {
return collection.toArray();
}
var length = collection.length;
@@ -932,11 +995,11 @@
* _.values({ 'one': 1, 'two': 2, 'three': 3 });
* // => [1, 2, 3]
*/
- var values = iterationFactory(mapFactoryOptions, {
+ var values = createIterator(mapIteratorOptions, {
'args': 'collection',
'inLoop': {
- 'array': 'result[index]=collection[index]',
- 'object': 'result[result.length]=collection[index]'
+ 'array': 'result[index] = collection[index]',
+ 'object': 'result.push(collection[index])'
}
});
@@ -956,11 +1019,18 @@
* _.compact([0, 1, false, 2, '', 3]);
* // => [1, 2, 3]
*/
- var compact = iterationFactory({
- 'args': 'array',
- 'init': '[]',
- 'inLoop': 'if(array[index])result.push(array[index])'
- });
+ function compact(array) {
+ var index = -1,
+ length = array.length,
+ result = [];
+
+ while (++index < length) {
+ if (array[index]) {
+ result.push(array[index]);
+ }
+ }
+ return result;
+ }
/**
* Produces a new array of `array` values not present in the other arrays
@@ -970,7 +1040,7 @@
* @memberOf _
* @category Arrays
* @param {Array} array The array to process.
- * @param {Mixed} [array1, array2, ...] Arrays to check.
+ * @param {Array} [array1, array2, ...] Arrays to check.
* @returns {Array} Returns a new array of `array` values not present in the
* other arrays.
* @example
@@ -978,7 +1048,19 @@
* _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
* // => [1, 3, 4]
*/
- var difference = iterationFactory(differenceFactoryOptions);
+ function difference(array) {
+ var index = -1,
+ length = array.length,
+ result = [],
+ flattened = concat.apply(result, slice.call(arguments, 1));
+
+ while (++index < length) {
+ if (indexOf(flattened, array[index]) < 0) {
+ result.push(array[index]);
+ }
+ }
+ return result;
+ }
/**
* Gets the first value of the `array`. Pass `n` to return the first `n` values
@@ -1023,16 +1105,22 @@
*/
function flatten(array, shallow) {
if (shallow) {
- return concat.apply([], array);
+ return concat.apply(ArrayProto, array);
}
- return reduce(array, function(accumulator, value) {
+ var value,
+ index = -1,
+ length = array.length,
+ result = [];
+
+ while (++index < length) {
+ value = array[index];
if (isArray(value)) {
- push.apply(accumulator, flatten(value));
- return accumulator;
+ push.apply(result, flatten(value));
+ } else {
+ result.push(value);
}
- accumulator.push(value);
- return accumulator;
- }, []);
+ }
+ return result;
}
/**
@@ -1054,7 +1142,7 @@
*/
function indexOf(array, value, isSorted) {
var index, length;
- if (array == undefined) {
+ if (!array) {
return -1;
}
if (isSorted) {
@@ -1097,7 +1185,7 @@
* @memberOf _
* @alias intersect
* @category Arrays
- * @param {Mixed} [array1, array2, ...] Arrays to process.
+ * @param {Array} [array1, array2, ...] Arrays to process.
* @returns {Array} Returns a new array of unique values, in order, that are
* present in **all** of the arrays.
* @example
@@ -1106,12 +1194,49 @@
* // => [1, 2]
*/
function intersection(array) {
- var rest = slice.call(arguments, 1);
- return filter(uniq(array), function(value) {
- return every(rest, function(other) {
- return indexOf(other, value) >= 0;
- });
- });
+ var value,
+ index = -1,
+ length = array.length,
+ others = slice.call(arguments, 1),
+ result = [];
+
+ while (++index < length) {
+ value = array[index];
+ if (indexOf(result, value) < 0 &&
+ every(others, function(other) { return indexOf(other, value) > -1; })) {
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Calls the method named by `methodName` for each value of the `collection`.
+ * Additional arguments will be passed to each invoked method.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to iterate over.
+ * @param {String} methodName The name of the method to invoke.
+ * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with.
+ * @returns {Array} Returns a new array of values returned from each invoked method.
+ * @example
+ *
+ * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
+ * // => [[1, 5, 7], [1, 2, 3]]
+ */
+ function invoke(array, methodName) {
+ var args = slice.call(arguments, 2),
+ index = -1,
+ length = array.length,
+ isFunc = toString.call(methodName) == funcClass,
+ result = [];
+
+ while (++index < length) {
+ result[index] = (isFunc ? methodName : array[index][methodName]).apply(array[index], args);
+ }
+ return result;
}
/**
@@ -1152,7 +1277,7 @@
* // => 4
*/
function lastIndexOf(array, value) {
- if (array == undefined) {
+ if (!array) {
return -1;
}
var index = array.length;
@@ -1164,6 +1289,109 @@
return -1;
}
+ /**
+ * Retrieves the maximum value of an `array`. If `callback` is passed,
+ * it will be executed for each value in the `array` to generate the
+ * criterion by which the value is ranked. The `callback` is invoked with 3
+ * arguments; (value, index, array).
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to iterate over.
+ * @param {Function} [callback] The function called per iteration.
+ * @param {Mixed} [thisArg] The `this` binding for the callback.
+ * @returns {Mixed} Returns the maximum value.
+ * @example
+ *
+ * var stooges = [
+ * { 'name': 'moe', 'age': 40 },
+ * { 'name': 'larry', 'age': 50 },
+ * { 'name': 'curly', 'age': 60 }
+ * ];
+ *
+ * _.max(stooges, function(stooge) { return stooge.age; });
+ * // => { 'name': 'curly', 'age': 60 };
+ */
+ function max(array, callback, thisArg) {
+ var current,
+ computed = -Infinity,
+ index = -1,
+ length = array.length,
+ result = computed;
+
+ if (!callback) {
+ // fast path for arrays of numbers
+ if (array[0] === +array[0] && length <= argsLimit) {
+ // some JavaScript engines have a limit on the number of arguments functions
+ // can accept before clipping the argument length or throwing an error
+ try {
+ return Math.max.apply(Math, array);
+ } catch(e) { }
+ }
+ if (!array.length) {
+ return result;
+ }
+ } else if (thisArg) {
+ callback = bind(callback, thisArg);
+ }
+ while (++index < length) {
+ current = callback ? callback(array[index], index, array) : array[index];
+ if (current > computed) {
+ computed = current;
+ result = array[index];
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves the minimum value of an `array`. If `callback` is passed,
+ * it will be executed for each value in the `array` to generate the
+ * criterion by which the value is ranked. The `callback` is invoked with 3
+ * arguments; (value, index, array).
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to iterate over.
+ * @param {Function} [callback] The function called per iteration.
+ * @param {Mixed} [thisArg] The `this` binding for the callback.
+ * @returns {Mixed} Returns the minimum value.
+ * @example
+ *
+ * _.min([10, 5, 100, 2, 1000]);
+ * // => 2
+ */
+ function min(array, callback, thisArg) {
+ var current,
+ computed = Infinity,
+ index = -1,
+ length = array.length,
+ result = computed;
+
+ if (!callback) {
+ if (array[0] === +array[0] && length <= argsLimit) {
+ try {
+ return Math.min.apply(Math, array);
+ } catch(e) { }
+ }
+ if (!array.length) {
+ return result;
+ }
+ } else if (thisArg) {
+ callback = bind(callback, thisArg);
+ }
+ while (++index < length) {
+ current = callback ? callback(array[index], index, array) : array[index];
+ if (current < computed) {
+ computed = current;
+ result = array[index];
+ }
+ }
+ return result;
+ }
+
/**
* Creates an array of numbers (positive and/or negative) progressing from
* `start` up to but not including `stop`. This method is a port of Python's
@@ -1229,8 +1457,75 @@
* _.rest([5, 4, 3, 2, 1]);
* // => [4, 3, 2, 1]
*/
- function rest(array, n, guard) {
- return slice.call(array, (n == undefined || guard) ? 1 : n);
+ function rest(array, n, guard) {
+ return slice.call(array, (n == undefined || guard) ? 1 : n);
+ }
+
+ /**
+ * Produces a new array of shuffled `array` values, using a version of the
+ * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to shuffle.
+ * @returns {Array} Returns a new shuffled array.
+ * @example
+ *
+ * _.shuffle([1, 2, 3, 4, 5, 6]);
+ * // => [4, 1, 6, 3, 5, 2]
+ */
+ function shuffle(array) {
+ var rand,
+ index = -1,
+ length = array.length,
+ result = Array(length);
+
+ while (++index < length) {
+ rand = Math.floor(Math.random() * (index + 1));
+ result[index] = result[rand];
+ result[rand] = array[index];
+ }
+ return result;
+ }
+
+ /**
+ * Uses a binary search to determine the smallest index at which the `value`
+ * should be inserted into the `collection` in order to maintain the sort order
+ * of the `collection`. If `callback` is passed, it will be executed for each
+ * value in the `collection` to compute their sort ranking. The `callback` is
+ * invoked with 1 argument; (value).
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to iterate over.
+ * @param {Mixed} value The value to evaluate.
+ * @param {Function} [callback] The function called per iteration.
+ * @returns {Number} Returns the index at which the value should be inserted
+ * into the collection.
+ * @example
+ *
+ * _.sortedIndex([10, 20, 30, 40, 50], 35);
+ * // => 3
+ */
+ function sortedIndex(array, value, callback) {
+ var mid,
+ low = 0,
+ high = array.length;
+
+ if (callback) {
+ value = callback(value);
+ }
+ while (low < high) {
+ mid = (low + high) >> 1;
+ if ((callback ? callback(array[mid]) : array[mid]) < value) {
+ low = mid + 1;
+ } else {
+ high = mid;
+ }
+ }
+ return low;
}
/**
@@ -1239,7 +1534,7 @@
* @static
* @memberOf _
* @category Arrays
- * @param {Mixed} [array1, array2, ...] Arrays to process.
+ * @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
@@ -1248,7 +1543,17 @@
* // => [1, 2, 3, 101, 10]
*/
function union() {
- return uniq(flatten(arguments, true));
+ var index = -1,
+ result = [],
+ flattened = concat.apply(result, arguments),
+ length = flattened.length;
+
+ while (++index < length) {
+ if (indexOf(result, flattened[index]) < 0) {
+ result.push(flattened[index]);
+ }
+ }
+ return result;
}
/**
@@ -1273,27 +1578,31 @@
* // => [1, 2, 3, 4]
*/
function uniq(array, isSorted, callback) {
- var initial = callback ? map(array, callback) : array,
- result = [];
+ var computed,
+ index = -1,
+ length = array.length,
+ result = [],
+ seen = [];
- // the `isSorted` flag is irrelevant if the array only contains two elements.
- if (array.length < 3) {
+ if (length < 3) {
isSorted = true;
}
- reduce(initial, function(accumulator, value, index) {
- if (isSorted ? last(accumulator) !== value || !accumulator.length : indexOf(accumulator, value) < 0) {
- accumulator.push(value);
+ while (++index < length) {
+ computed = callback ? callback(array[index]) : array[index];
+ if (isSorted
+ ? !index || seen[seen.length - 1] !== computed
+ : indexOf(seen, computed) < 0
+ ) {
+ seen.push(computed);
result.push(array[index]);
}
- return accumulator;
- }, []);
-
+ }
return result;
}
/**
- * Produces a new array with all occurrences of the values removed using strict
- * equality for comparisons, i.e. `===`.
+ * Produces a new array with all occurrences of the passed values removed using
+ * strict equality for comparisons, i.e. `===`.
*
* @static
* @memberOf _
@@ -1306,10 +1615,19 @@
* _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
* // => [2, 3, 4]
*/
- var without = iterationFactory(differenceFactoryOptions, {
- 'top': 'var values=slice.call(arguments,1)',
- 'init': '[]'
- });
+ function without(array) {
+ var excluded = slice.call(arguments, 1),
+ index = -1,
+ length = array.length,
+ result = [];
+
+ while (++index < length) {
+ if (indexOf(excluded, array[index]) < 0) {
+ result.push(array[index]);
+ }
+ }
+ return result;
+ }
/**
* Merges together the values of each of the arrays with the value at the
@@ -1320,7 +1638,7 @@
* @static
* @memberOf _
* @category Arrays
- * @param {Mixed} [array1, array2, ...] Arrays to process.
+ * @param {Array} [array1, array2, ...] Arrays to process.
* @returns {Array} Returns a new array of merged arrays.
* @example
*
@@ -1342,12 +1660,12 @@
/**
* Creates a new function that is restricted to executing only after it is
- * called a given number of `times`.
+ * called `n` times.
*
* @static
* @memberOf _
* @category Functions
- * @param {Number} times The number of times the function must be called before
+ * @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.
@@ -1359,12 +1677,12 @@
* });
* // renderNotes is run once, after all notes have saved.
*/
- function after(times, func) {
- if (times < 1) {
+ function after(n, func) {
+ if (n < 1) {
return func();
}
return function() {
- if (--times < 1) {
+ if (--n < 1) {
return func.apply(this, arguments);
}
};
@@ -1372,31 +1690,81 @@
/**
* Creates a new function that, when called, invokes `func` with the `this`
- * binding of `thisArg` and prepends additional arguments to those passed to
- * the bound function.
+ * binding of `thisArg` and prepends any additional `bind` arguments to those
+ * passed to the bound function. Lazy defined methods may be bound by passing
+ * the object they are bound to as `func` and the method name as `thisArg`.
*
* @static
* @memberOf _
* @category Functions
- * @param {Function} func The function to bind.
- * @param @param {Mixed} [thisArg] The `this` binding of `func`.
- * @param {Mixed} [arg1, arg2, ...] Arguments to prepend to those passed to the bound function.
+ * @param {Function|Object} func The function to bind or the object the method belongs to.
+ * @param @param {Mixed} [thisArg] The `this` binding of `func` or the method name.
+ * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied.
* @returns {Function} Returns the new bound function.
* @example
*
+ * // basic bind
* var func = function(greeting) { return greeting + ': ' + this.name; };
* func = _.bind(func, { 'name': 'moe' }, 'hi');
* func();
* // => 'hi: moe'
+ *
+ * // lazy bind
+ * var object = {
+ * 'name': 'moe',
+ * 'greet': function(greeting) {
+ * return greeting + ': ' + this.name;
+ * }
+ * };
+ *
+ * var func = _.bind(object, 'greet', 'hi');
+ * func();
+ * // => 'hi: moe'
+ *
+ * object.greet = function(greeting) {
+ * return greeting + ' ' + this.name + '!';
+ * };
+ *
+ * func();
+ * // => 'hi moe!'
*/
function bind(func, thisArg) {
- var args = slice.call(arguments, 2),
- argsLength = args.length;
+ var methodName,
+ isFunc = toString.call(func) == funcClass;
+
+ // juggle arguments
+ if (!isFunc) {
+ methodName = thisArg;
+ thisArg = func;
+ }
+ // use if `Function#bind` is faster
+ else if (nativeBind) {
+ func = nativeBind.call.apply(nativeBind, arguments);
+ return function() {
+ return arguments.length ? func.apply(undefined, arguments) : func();
+ };
+ }
+
+ var partialArgs = slice.call(arguments, 2),
+ partialArgsLength = partialArgs.length;
return function() {
- args.length = argsLength;
- push.apply(args, arguments);
- return func.apply(thisArg, args);
+ var result,
+ args = arguments;
+
+ if (!isFunc) {
+ func = thisArg[methodName];
+ }
+ if (partialArgsLength) {
+ if (args.length) {
+ partialArgs.length = partialArgsLength;
+ push.apply(partialArgs, args);
+ }
+ args = partialArgs;
+ }
+ result = args.length ? func.apply(thisArg, args) : func.call(thisArg);
+ partialArgs.length = partialArgsLength;
+ return result;
};
}
@@ -1409,7 +1777,7 @@
* @memberOf _
* @category Functions
* @param {Object} object The object to bind and assign the bound methods to.
- * @param {Mixed} [methodName1, methodName2, ...] Method names on the object to bind.
+ * @param {String} [methodName1, methodName2, ...] Method names on the object to bind.
* @returns {Object} Returns the `object`.
* @example
*
@@ -1445,7 +1813,7 @@
* @static
* @memberOf _
* @category Functions
- * @param {Mixed} [func1, func2, ...] Functions to compose.
+ * @param {Function} [func1, func2, ...] Functions to compose.
* @returns {Function} Returns the new composed function.
* @example
*
@@ -1469,16 +1837,17 @@
}
/**
- * Creates a new function that will postpone its execution until after `wait`
- * milliseconds have elapsed since the last time it was invoked. Pass `true`
- * for `immediate` to cause debounce to invoke the function on the leading,
- * instead of the trailing, edge of the `wait` timeout.
+ * Creates a new function that will delay the execution of `func` until after
+ * `wait` milliseconds have elapsed since the last time it was invoked. Pass
+ * `true` for `immediate` to cause debounce to invoke `func` on the leading,
+ * instead of the trailing, edge of the `wait` timeout. Subsequent calls to
+ * the debounced function will return the result of the last `func` call.
*
* @static
* @memberOf _
* @category Functions
* @param {Function} func The function to debounce.
- * @param {Number} wait The number of milliseconds to postone.
+ * @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.
@@ -1488,27 +1857,36 @@
* jQuery(window).on('resize', lazyLayout);
*/
function debounce(func, wait, immediate) {
- var timeout;
+ var args,
+ result,
+ thisArg,
+ timeoutId;
+
+ function delayed() {
+ timeoutId = undefined;
+ if (!immediate) {
+ func.apply(thisArg, args);
+ }
+ }
+
return function() {
- var args = arguments,
- thisArg = this;
+ var isImmediate = immediate && !timeoutId;
+ args = arguments;
+ thisArg = this;
- if (immediate && !timeout) {
- func.apply(thisArg, args);
+ clearTimeout(timeoutId);
+ timeoutId = setTimeout(delayed, wait);
+
+ if (isImmediate) {
+ result = func.apply(thisArg, args);
}
- clearTimeout(timeout);
- timeout = setTimeout(function() {
- timeout = undefined;
- if (!immediate) {
- func.apply(thisArg, args);
- }
- }, wait);
+ return result;
};
}
/**
- * Invokes the `func` function after `wait` milliseconds. Additional arguments
- * are passed `func` when it is invoked.
+ * Executes the `func` function after `wait` milliseconds. Additional arguments
+ * are passed to `func` when it is invoked.
*
* @static
* @memberOf _
@@ -1529,7 +1907,7 @@
}
/**
- * Defers invoking the `func` function until the current call stack has cleared.
+ * Defers executing the `func` function until the current call stack has cleared.
* Additional arguments are passed to `func` when it is invoked.
*
* @static
@@ -1607,8 +1985,48 @@
}
/**
- * Creates a new function that, when invoked, will only call the original
- * function at most once per every `wait` milliseconds.
+ * Creates a new function that, when called, invokes `func` with any additional
+ * `partial` arguments prepended to those passed to the partially applied
+ * function. This method is similar `bind`, except it does **not** alter the
+ * `this` binding.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied.
+ * @returns {Function} Returns the new partially applied function.
+ * @example
+ *
+ * var greet = function(greeting, name) { return greeting + ': ' + name; };
+ * var hi = _.partial(greet, 'hi');
+ * hi('moe');
+ * // => 'hi: moe'
+ */
+ function partial(func) {
+ var args = slice.call(arguments, 1),
+ argsLength = args.length;
+
+ return function() {
+ var result,
+ others = arguments;
+
+ if (others.length) {
+ args.length = argsLength;
+ push.apply(args, others);
+ }
+ result = args.length == 1 ? func.call(this, args[0]) : func.apply(this, args);
+ args.length = argsLength;
+ return result;
+ };
+ }
+
+ /**
+ * Creates a new function that, when executed, will only call the `func`
+ * function at most once per every `wait` milliseconds. If the throttled function
+ * is invoked more than once, `func` will also be called on the trailing edge
+ * of the `wait` timeout. Subsequent calls to the throttled function will
+ * return the result of the last `func` call.
*
* @static
* @memberOf _
@@ -1622,29 +2040,32 @@
* jQuery(window).on('scroll', throttled);
*/
function throttle(func, wait) {
- var args, more, result, thisArg, throttling, timeout,
- whenDone = debounce(function() { more = throttling = false; }, wait);
+ var args,
+ result,
+ thisArg,
+ timeoutId,
+ lastCalled = 0;
+
+ function trailingCall() {
+ lastCalled = new Date;
+ timeoutId = undefined;
+ func.apply(thisArg, args);
+ }
return function() {
+ var now = new Date,
+ remain = wait - (now - lastCalled);
+
args = arguments;
thisArg = this;
- if (!timeout) {
- timeout = setTimeout(function() {
- timeout = undefined;
- if (more) {
- func.apply(thisArg, args);
- }
- whenDone();
- }, wait);
- }
- if (throttling) {
- more = true;
- } else {
+ if (remain <= 0) {
+ lastCalled = now;
result = func.apply(thisArg, args);
}
- whenDone();
- throttling = true;
+ else if (!timeoutId) {
+ timeoutId = setTimeout(trailingCall, remain);
+ }
return result;
};
}
@@ -1673,7 +2094,9 @@
function wrap(func, wrapper) {
return function() {
var args = [func];
- push.apply(args, arguments);
+ if (arguments.length) {
+ push.apply(args, arguments);
+ }
return wrapper.apply(this, args);
};
}
@@ -1695,10 +2118,9 @@
* // => { 'name': 'moe' };
*/
function clone(value) {
- if (value !== Object(value)) {
- return value;
- }
- return isArray(value) ? value.slice() : extend({}, value);
+ return objectTypes[typeof value] && value !== null
+ ? (isArray(value) ? value.slice() : extend({}, value))
+ : value;
}
/**
@@ -1710,7 +2132,7 @@
* @memberOf _
* @category Objects
* @param {Object} object The object to populate.
- * @param {Object} [defaults1, defaults2, ..] The defaults objects to apply to `object`.
+ * @param {Object} [defaults1, defaults2, ...] The defaults objects to apply to `object`.
* @returns {Object} Returns `object`.
* @example
*
@@ -1718,8 +2140,8 @@
* _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'lots' });
* // => { 'flavor': 'chocolate', 'sprinkles': 'lots' }
*/
- var defaults = iterationFactory(extendFactoryOptions, {
- 'inLoop': 'if(object[index]==undefined)' + extendFactoryOptions.inLoop
+ var defaults = createIterator(extendIteratorOptions, {
+ 'inLoop': 'if (object[index] == undefined)' + extendIteratorOptions.inLoop
});
/**
@@ -1730,14 +2152,14 @@
* @memberOf _
* @category Objects
* @param {Object} object The destination object.
- * @param {Object} [source1, source2, ..] The source objects.
+ * @param {Object} [source1, source2, ...] The source objects.
* @returns {Object} Returns the destination object.
* @example
*
* _.extend({ 'name': 'moe' }, { 'age': 40 });
* // => { 'name': 'moe', 'age': 40 }
*/
- var extend = iterationFactory(extendFactoryOptions);
+ var extend = createIterator(extendIteratorOptions);
/**
* Produces a sorted array of the properties, own and inherited, of `object`
@@ -1754,11 +2176,12 @@
* _.functions(_);
* // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
*/
- var functions = iterationFactory(keysFactoryOptions, {
- 'top': '',
+ var functions = createIterator({
+ 'args': 'object',
+ 'init': '[]',
'useHas': false,
- 'inLoop': 'if(toString.call(object[index])==funcClass)result.push(index)',
- 'returns': 'result.sort()'
+ 'inLoop': 'if (toString.call(object[index]) == funcClass) result.push(index)',
+ 'bottom': 'result.sort()'
});
/**
@@ -1776,8 +2199,8 @@
* _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b');
* // => true
*/
- function has(object, prop) {
- return hasOwnProperty.call(object, prop);
+ function has(object, property) {
+ return hasOwnProperty.call(object, property);
}
/**
@@ -1796,12 +2219,12 @@
* _.isArguments([1, 2, 3]);
* // => false
*/
- var isArguments = function isArguments(value) {
+ var isArguments = function(value) {
return toString.call(value) == '[object Arguments]';
};
// fallback for browser like IE<9 which detect `arguments` as `[object Object]`
if (!isArguments(arguments)) {
- isArguments = function isArguments(value) {
+ isArguments = function(value) {
return !!(value && hasOwnProperty.call(value, 'callee'));
};
}
@@ -1900,10 +2323,10 @@
b = b._wrapped;
}
// invoke a custom `isEqual` method if one is provided
- if (a.isEqual && isFunction(a.isEqual)) {
+ if (a.isEqual && toString.call(a.isEqual) == funcClass) {
return a.isEqual(b);
}
- if (b.isEqual && isFunction(b.isEqual)) {
+ if (b.isEqual && toString.call(b.isEqual) == funcClass) {
return b.isEqual(a);
}
// compare [[Class]] names
@@ -1952,7 +2375,8 @@
}
}
- var result = true,
+ var index = -1,
+ result = true,
size = 0;
// add the first collection to the stack of traversed objects
@@ -1967,8 +2391,7 @@
if (result) {
// deep compare the contents, ignoring non-numeric properties
while (size--) {
- // ensure commutative equality for sparse arrays
- if (!(result = size in a == size in b && isEqual(a[size], b[size], stack))) {
+ if (!(result = isEqual(a[size], b[size], stack))) {
break;
}
}
@@ -1978,26 +2401,35 @@
if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) {
return false;
}
- // deep compare objects
+ // deep compare objects.
for (var prop in a) {
if (hasOwnProperty.call(a, prop)) {
- // count the expected number of properties
+ // count the number of properties.
size++;
- // deep compare each member
+ // deep compare each property value.
if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) {
break;
}
}
}
- // ensure that both objects contain the same number of properties
+ // ensure both objects have the same number of properties
if (result) {
for (prop in b) {
- if (hasOwnProperty.call(b, prop) && !(size--)) {
- break;
- }
+ if (hasOwnProperty.call(b, prop) && !(size--)) break;
}
result = !size;
}
+ // handle JScript [[DontEnum]] bug
+ if (result && hasDontEnumBug) {
+ while (++index < 7) {
+ prop = shadowed[index];
+ if (hasOwnProperty.call(a, prop)) {
+ if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) {
+ break;
+ }
+ }
+ }
+ }
}
// remove the first collection from the stack of traversed objects
stack.pop();
@@ -2045,7 +2477,8 @@
}
/**
- * Checks if a `value` is an object.
+ * Checks if a `value` is the language type of Object.
+ * (e.g. arrays, functions, objects, regexps, `new Number(0)`, and `new String('')`)
*
* @static
* @memberOf _
@@ -2061,7 +2494,9 @@
* // => false
*/
function isObject(value) {
- return value === Object(value);
+ // check if the value is the ECMAScript language type of Object
+ // http://es5.github.com/#x8
+ return objectTypes[typeof value] && value !== null;
}
/**
@@ -2195,7 +2630,12 @@
* _.keys({ 'one': 1, 'two': 2, 'three': 3 });
* // => ['one', 'two', 'three']
*/
- var keys = nativeKeys || iterationFactory(keysFactoryOptions);
+ var keys = nativeKeys || createIterator({
+ 'args': 'object',
+ 'exit': 'if (!objectTypes[typeof object] || object === null) throw TypeError()',
+ 'init': '[]',
+ 'inLoop': 'result.push(index)'
+ });
/**
* Creates an object composed of the specified properties. Property names may
@@ -2205,7 +2645,7 @@
* @memberOf _
* @category Objects
* @param {Object} object The object to pluck.
- * @param {Object} [prop1, prop2, ..] The properties to pick.
+ * @param {Object} [prop1, prop2, ...] The properties to pick.
* @returns {Object} Returns an object composed of the picked properties.
* @example
*
@@ -2214,11 +2654,12 @@
*/
function pick(object) {
var prop,
- index = -1,
- props = flatten(slice.call(arguments, 1)),
+ index = 0,
+ props = concat.apply(ArrayProto, arguments),
length = props.length,
result = {};
+ // start `index` at `1` to skip `object`
while (++index < length) {
prop = props[index];
if (prop in object) {
@@ -2257,7 +2698,7 @@
/*--------------------------------------------------------------------------*/
/**
- * Escapes a string for insertion into HTML, replacing `&`, `<`, `>`, `"`, `'`,
+ * Escapes a string for insertion into HTML, replacing `&`, `<`, `"`, `'`,
* and `/` characters.
*
* @static
@@ -2271,10 +2712,12 @@
* // => "Curly, Larry & Moe"
*/
function escape(string) {
+ // the `>` character doesn't require escaping in HTML and has no special
+ // meaning unless it's part of a tag or an unquoted attribute value
+ // http://mathiasbynens.be/notes/ambiguous-ampersands (semi-related fun fact)
return (string + '')
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/\//g,'/');
@@ -2325,16 +2768,17 @@
forEach(functions(object), function(methodName) {
var func = lodash[methodName] = object[methodName];
- lodash.prototype[methodName] = function() {
- // 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
- var args = slice.call(arguments);
- unshift.call(args, this._wrapped);
-
- var result = func.apply(lodash, args);
- return this._chain ? new Lodash(result).chain() : result;
+ LoDash.prototype[methodName] = function() {
+ var args = [this._wrapped];
+ if (arguments.length) {
+ push.apply(args, arguments);
+ }
+ var result = args.length == 1 ? func.call(lodash, args[0]) : func.apply(lodash, args);
+ if (this._chain) {
+ result = new LoDash(result);
+ result._chain = true;
+ }
+ return result;
};
});
}
@@ -2382,56 +2826,13 @@
* // => 'nonsense'
*/
function result(object, property) {
- if (object == undefined) {
+ if (!object) {
return null;
}
var value = object[property];
- return isFunction(value) ? object[property]() : value;
- }
-
- /**
- * Executes the `callback` function `n` times.
- *
- * @static
- * @memberOf _
- * @category Utilities
- * @param {Number} n The number of times to execute the callback.
- * @param {Function} callback The function called per iteration.
- * @param {Mixed} [thisArg] The `this` binding for the callback.
- * @example
- *
- * _.times(3, function() { genie.grantWish(); });
- */
- function times(n, callback, thisArg) {
- if (thisArg) {
- callback = bind(callback, thisArg);
- }
- for (var index = 0; index < n; index++) {
- callback(index);
- }
- }
-
- /**
- * Generates a unique id. If `prefix` is passed, the id will be appended to it.
- *
- * @static
- * @memberOf _
- * @category Utilities
- * @param {String} [prefix] The value to prefix the id with.
- * @returns {Number|String} Returns a numeric id if no prefix is passed, else
- * a string id may be returned.
- * @example
- *
- * _.uniqueId('contact_');
- * // => 'contact_104'
- */
- function uniqueId(prefix) {
- var id = idCounter++;
- return prefix ? prefix + id : id;
+ return toString.call(value) == funcClass ? object[property]() : value;
}
- /*--------------------------------------------------------------------------*/
-
/**
* A JavaScript micro-templating method, similar to John Resig's implementation.
* Lo-Dash templating handles arbitrary delimiters, preserves whitespace, and
@@ -2485,47 +2886,108 @@
*
*/
function template(text, data, options) {
- options = defaults(options || {}, lodash.templateSettings);
-
- // Compile the template source, taking care to escape characters that
- // cannot be included in string literals and then unescape them in code
- // blocks.
- var source = "__p+='" + text
- .replace(reEscaper, function(match) {
- return '\\' + escapes[match];
- })
- .replace(options.escape || reNoMatch, function(match, code) {
- return "'+\n((__t=(" + unescape(code) + "))==null?'':_.escape(__t))+\n'";
- })
- .replace(options.interpolate || reNoMatch, function(match, code) {
- return "'+\n((__t=(" + unescape(code) + "))==null?'':__t)+\n'";
- })
- .replace(options.evaluate || reNoMatch, function(match, code) {
- return "';\n" + unescape(code) + ";\n__p+='";
- }) + "';\n";
-
- // if a variable is not specified, place data values in local scope
- if (!options.variable) {
- source = 'with(object||{}){\n' + source + '\n}\n';
- }
-
- source = 'var __t,__j=Array.prototype.join,__p="";' +
- 'function print(){__p+=__j.call(arguments,"")}\n' +
- source + 'return __p';
-
- var render = Function(options.variable || 'object', '_', source);
- if (data) {
- return render(data, lodash);
+ options || (options = {});
+
+ var result,
+ defaults = lodash.templateSettings || {},
+ escapeDelimiter = options.escape,
+ evaluateDelimiter = options.evaluate,
+ interpolateDelimiter = options.interpolate,
+ variable = options.variable;
+
+ // use template defaults if no option is provided
+ if (escapeDelimiter == null) {
+ escapeDelimiter = defaults.escape;
+ }
+ if (evaluateDelimiter == null) {
+ evaluateDelimiter = defaults.evaluate;
+ }
+ if (interpolateDelimiter == null) {
+ interpolateDelimiter = defaults.interpolate;
}
- var template = function(data) {
- return render.call(this, data, lodash);
- };
+ // tokenize delimiters to avoid escaping them
+ if (escapeDelimiter) {
+ text = text.replace(escapeDelimiter, tokenizeEscape);
+ }
+ if (interpolateDelimiter) {
+ text = text.replace(interpolateDelimiter, tokenizeInterpolate);
+ }
+ if (evaluateDelimiter) {
+ text = text.replace(evaluateDelimiter, tokenizeEvaluate);
+ }
+
+ // escape characters that cannot be included in string literals and
+ // detokenize delimiter code snippets
+ text = "__p='" + text.replace(reUnescaped, escapeChar).replace(reToken, detokenize) + "';\n";
+
+ // clear stored code snippets
+ tokenized.length = 0;
+
+ // if `options.variable` is not specified, add `data` to the top of the scope chain
+ if (!variable) {
+ variable = defaults.variable || 'object';
+ text = 'with (' + variable + ' || {}) {\n' + text + '\n}\n';
+ }
+
+ text = 'function(' + variable + ') {\n' +
+ 'var __p, __t, __j = Array.prototype.join;\n' +
+ 'function print() { __p += __j.call(arguments, \'\') }\n' +
+ text +
+ 'return __p\n}';
+
+ result = Function('_', 'return ' + text)(lodash);
+
+ if (data) {
+ return result(data);
+ }
+ // provide the compiled function's source via its `toString()` method, in
+ // supported environments, or the `source` property as a convenience for
+ // build time precompilation
+ result.source = text;
+ return result;
+ }
- // provide the compiled function source as a convenience for build time precompilation
- template.source = 'function(' + (options.variable || 'object') + '){\n' + source + '\n}';
+ /**
+ * Executes the `callback` function `n` times. The `callback` is invoked with
+ * 1 argument; (index).
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {Number} n The number of times to execute the callback.
+ * @param {Function} callback The function called per iteration.
+ * @param {Mixed} [thisArg] The `this` binding for the callback.
+ * @example
+ *
+ * _.times(3, function() { genie.grantWish(); });
+ */
+ function times(n, callback, thisArg) {
+ if (thisArg) {
+ callback = bind(callback, thisArg);
+ }
+ for (var index = 0; index < n; index++) {
+ callback(index);
+ }
+ }
- return template;
+ /**
+ * Generates a unique id. If `prefix` is passed, the id will be appended to it.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {String} [prefix] The value to prefix the id with.
+ * @returns {Number|String} Returns a numeric id if no prefix is passed, else
+ * a string id may be returned.
+ * @example
+ *
+ * _.uniqueId('contact_');
+ * // => 'contact_104'
+ */
+ function uniqueId(prefix) {
+ var id = idCounter++;
+ return prefix ? prefix + id : id;
}
/*--------------------------------------------------------------------------*/
@@ -2554,7 +3016,9 @@
* // => 'moe is 40'
*/
function chain(value) {
- return new Lodash(value).chain();
+ value = new LoDash(value);
+ value._chain = true;
+ return value;
}
/**
@@ -2569,7 +3033,7 @@
* _([1, 2, 3]).value();
* // => [1, 2, 3]
*/
- function chainWrapper() {
+ function wrapperChain() {
this._chain = true;
return this;
}
@@ -2577,6 +3041,7 @@
/**
* Extracts the value from a wrapped chainable object.
*
+ * @name value
* @memberOf _
* @category Chaining
* @returns {Mixed} Returns the wrapped object.
@@ -2585,203 +3050,226 @@
* _([1, 2, 3]).value();
* // => [1, 2, 3]
*/
- function value() {
+ function wrapperValue() {
return this._wrapped;
}
/*--------------------------------------------------------------------------*/
- extend(lodash, {
+ /**
+ * The semantic version number.
+ *
+ * @static
+ * @memberOf _
+ * @type String
+ */
+ lodash.VERSION = '0.2.0';
+
+ /**
+ * By default, Lo-Dash uses ERB-style template delimiters, change the
+ * following template settings to use alternative delimiters.
+ *
+ * @static
+ * @memberOf _
+ * @type Object
+ */
+ lodash.templateSettings = {
+
+ /**
+ * Used to detect `data` property values to be HTML-escaped.
+ *
+ * @static
+ * @memberOf _.templateSettings
+ * @type RegExp
+ */
+ 'escape': reEscapeDelimiter,
/**
- * The semantic version number.
+ * Used to detect code to be evaluated.
*
* @static
- * @memberOf _
- * @type String
+ * @memberOf _.templateSettings
+ * @type RegExp
*/
- 'VERSION': '0.1.0',
+ 'evaluate': reEvaluateDelimiter,
/**
- * By default, Lo-Dash uses ERB-style template delimiters, change the
- * following template settings to use alternative delimiters.
+ * Used to detect `data` property values to inject.
*
* @static
- * @memberOf _
- * @type Object
+ * @memberOf _.templateSettings
+ * @type RegExp
*/
- 'templateSettings': {
-
- /**
- * Used to detect `data` property values to be HTML-escaped.
- *
- * @static
- * @memberOf _.templateSettings
- * @type RegExp
- */
- 'escape': /<%-([\s\S]+?)%>/g,
-
- /**
- * Used to detect code to be evaluated.
- *
- * @static
- * @memberOf _.templateSettings
- * @type RegExp
- */
- 'evaluate': /<%([\s\S]+?)%>/g,
-
- /**
- * Used to detect `data` property values to inject.
- *
- * @static
- * @memberOf _.templateSettings
- * @type RegExp
- */
- 'interpolate': /<%=([\s\S]+?)%>/g
- },
+ 'interpolate': reInterpolateDelimiter,
- // assign static methods
- 'after': after,
- 'bind': bind,
- 'bindAll': bindAll,
- 'chain': chain,
- 'clone': clone,
- 'compact': compact,
- 'compose': compose,
- 'contains': contains,
- 'debounce': debounce,
- 'defaults': defaults,
- 'defer': defer,
- 'delay': delay,
- 'difference': difference,
- 'escape': escape,
- 'every': every,
- 'extend': extend,
- 'filter': filter,
- 'find': find,
- 'first': first,
- 'flatten': flatten,
- 'forEach': forEach,
- 'functions': functions,
- 'groupBy': groupBy,
- 'has': has,
- 'identity': identity,
- 'indexOf': indexOf,
- 'initial': initial,
- 'intersection': intersection,
- 'invoke': invoke,
- 'isArguments': isArguments,
- 'isArray': isArray,
- 'isBoolean': isBoolean,
- 'isDate': isDate,
- 'isElement': isElement,
- 'isEmpty': isEmpty,
- 'isEqual': isEqual,
- 'isFinite': isFinite,
- 'isFunction': isFunction,
- 'isNaN': isNaN,
- 'isNull': isNull,
- 'isNumber': isNumber,
- 'isObject': isObject,
- 'isRegExp': isRegExp,
- 'isString': isString,
- 'isUndefined': isUndefined,
- 'keys': keys,
- 'last': last,
- 'lastIndexOf': lastIndexOf,
- 'map': map,
- 'max': max,
- 'memoize': memoize,
- 'min': min,
- 'mixin': mixin,
- 'noConflict': noConflict,
- 'once': once,
- 'pick': pick,
- 'pluck': pluck,
- 'range': range,
- 'reduce': reduce,
- 'reduceRight': reduceRight,
- 'reject': reject,
- 'rest': rest,
- 'result': result,
- 'shuffle': shuffle,
- 'size': size,
- 'some': some,
- 'sortBy': sortBy,
- 'sortedIndex': sortedIndex,
- 'tap': tap,
- 'template': template,
- 'throttle': throttle,
- 'times': times,
- 'toArray': toArray,
- 'union': union,
- 'uniq': uniq,
- 'uniqueId': uniqueId,
- 'values': values,
- 'without': without,
- 'wrap': wrap,
- 'zip': zip,
-
- // assign aliases
- 'all': every,
- 'any': some,
- 'collect': map,
- 'detect': find,
- 'each': forEach,
- 'foldl': reduce,
- 'foldr': reduceRight,
- 'head': first,
- 'include': contains,
- 'inject': reduce,
- 'intersect': intersection,
- 'methods': functions,
- 'select': filter,
- 'tail': rest,
- 'take': first,
- 'unique': uniq
- });
+ /**
+ * Used to reference the data object in the template text.
+ *
+ * @static
+ * @memberOf _.templateSettings
+ * @type String
+ */
+ 'variable': 'object'
+ };
+
+ // assign static methods
+ lodash.after = after;
+ lodash.bind = bind;
+ lodash.bindAll = bindAll;
+ lodash.chain = chain;
+ lodash.clone = clone;
+ lodash.compact = compact;
+ lodash.compose = compose;
+ lodash.contains = contains;
+ lodash.debounce = debounce;
+ lodash.defaults = defaults;
+ lodash.defer = defer;
+ lodash.delay = delay;
+ lodash.difference = difference;
+ lodash.escape = escape;
+ lodash.every = every;
+ lodash.extend = extend;
+ lodash.filter = filter;
+ lodash.find = find;
+ lodash.first = first;
+ lodash.flatten = flatten;
+ lodash.forEach = forEach;
+ lodash.functions = functions;
+ lodash.groupBy = groupBy;
+ lodash.has = has;
+ lodash.identity = identity;
+ lodash.indexOf = indexOf;
+ lodash.initial = initial;
+ lodash.intersection = intersection;
+ lodash.invoke = invoke;
+ lodash.isArguments = isArguments;
+ lodash.isArray = isArray;
+ lodash.isBoolean = isBoolean;
+ lodash.isDate = isDate;
+ lodash.isElement = isElement;
+ lodash.isEmpty = isEmpty;
+ lodash.isEqual = isEqual;
+ lodash.isFinite = isFinite;
+ lodash.isFunction = isFunction;
+ lodash.isNaN = isNaN;
+ lodash.isNull = isNull;
+ lodash.isNumber = isNumber;
+ lodash.isObject = isObject;
+ lodash.isRegExp = isRegExp;
+ lodash.isString = isString;
+ lodash.isUndefined = isUndefined;
+ lodash.keys = keys;
+ lodash.last = last;
+ lodash.lastIndexOf = lastIndexOf;
+ lodash.map = map;
+ lodash.max = max;
+ lodash.memoize = memoize;
+ lodash.min = min;
+ lodash.mixin = mixin;
+ lodash.noConflict = noConflict;
+ lodash.once = once;
+ lodash.partial = partial;
+ lodash.pick = pick;
+ lodash.pluck = pluck;
+ lodash.range = range;
+ lodash.reduce = reduce;
+ lodash.reduceRight = reduceRight;
+ lodash.reject = reject;
+ lodash.rest = rest;
+ lodash.result = result;
+ lodash.shuffle = shuffle;
+ lodash.size = size;
+ lodash.some = some;
+ lodash.sortBy = sortBy;
+ lodash.sortedIndex = sortedIndex;
+ lodash.tap = tap;
+ lodash.template = template;
+ lodash.throttle = throttle;
+ lodash.times = times;
+ lodash.toArray = toArray;
+ lodash.union = union;
+ lodash.uniq = uniq;
+ lodash.uniqueId = uniqueId;
+ lodash.values = values;
+ lodash.without = without;
+ lodash.wrap = wrap;
+ lodash.zip = zip;
+
+ // assign aliases
+ lodash.all = every;
+ lodash.any = some;
+ lodash.collect = map;
+ lodash.detect = find;
+ lodash.each = forEach;
+ lodash.foldl = reduce;
+ lodash.foldr = reduceRight;
+ lodash.head = first;
+ lodash.include = contains;
+ lodash.inject = reduce;
+ lodash.intersect = intersection;
+ lodash.methods = functions;
+ lodash.select = filter;
+ lodash.tail = rest;
+ lodash.take = first;
+ lodash.unique = uniq;
+
+ // add pseudo private template used and removed during the build process
+ lodash._iteratorTemplate = iteratorTemplate;
/*--------------------------------------------------------------------------*/
- // assign private `Lodash` constructor's prototype
- Lodash.prototype = lodash.prototype;
+ // assign private `LoDash` constructor's prototype
+ LoDash.prototype = lodash.prototype;
- // add all of the static functions to `Lodash.prototype`
+ // add all static functions to `LoDash.prototype`
mixin(lodash);
+ // add `LoDash.prototype.chain` after calling `mixin()` to avoid overwriting
+ // it with the wrapped `lodash.chain`
+ LoDash.prototype.chain = wrapperChain;
+ LoDash.prototype.value = wrapperValue;
+
// add all mutator Array functions to the wrapper.
forEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {
var func = ArrayProto[methodName];
- lodash.prototype[methodName] = function() {
+ LoDash.prototype[methodName] = function() {
var value = this._wrapped;
- func.apply(value, arguments);
-
+ if (arguments.length) {
+ func.apply(value, arguments);
+ } else {
+ func.call(value);
+ }
// IE compatibility mode and IE < 9 have buggy Array `shift()` and `splice()`
- // functions that fail to remove the last element, `object[0]`, of
+ // 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.
if (value.length === 0) {
delete value[0];
}
- return this._chain ? new Lodash(value).chain() : value;
+ if (this._chain) {
+ value = new LoDash(value);
+ value._chain = true;
+ }
+ return value;
};
});
// add all accessor Array functions to the wrapper.
forEach(['concat', 'join', 'slice'], function(methodName) {
var func = ArrayProto[methodName];
- Lodash.prototype[methodName] = function() {
- var result = func.apply(this._wrapped, arguments);
- return this._chain ? new Lodash(result).chain() : result;
- };
- });
- // add `chain` and `value` after calling to `mixin()` to avoid getting wrapped
- extend(Lodash.prototype, {
- 'chain': chainWrapper,
- 'value': value
+ LoDash.prototype[methodName] = function() {
+ var value = this._wrapped,
+ result = arguments.length ? func.apply(value, arguments) : func.call(value);
+
+ if (this._chain) {
+ result = new LoDash(result);
+ result._chain = true;
+ }
+ return result;
+ };
});
/*--------------------------------------------------------------------------*/
@@ -2796,23 +3284,20 @@
else {
freeExports._ = lodash;
}
- } else {
- // in a browser or Rhino
+ }
+ // in a browser or Rhino
+ else {
+ // Expose Lo-Dash to the global object even when an AMD loader is present in
+ // case Lo-Dash was injected by a third-party script and not intended to be
+ // loaded as a module. The global assignment can be reverted in the Lo-Dash
+ // module via its `noConflict()` method.
window._ = lodash;
- // Expose Lo-Dash as an AMD module, but only for AMD loaders that understand
- // the issues with loading multiple versions of Lo-Dash in a page that all
- // might call `define()`. The loader will indicate they have special
- // allowances for multiple Lo-Dash versions by specifying
- // `define.amd.lodash=true`. Register as a named module, since Lo-Dash can
- // be concatenated with other files that may use `define()`, but not use a
- // proper concatenation script that understands anonymous AMD modules.
- // Lowercase `lodash` is used because AMD module names are derived from
- // file names, and Lo-Dash is normally delivered in a lowercase file name.
- // Do this after assigning Lo-Dash the global so that if an AMD module wants
- // to call `noConflict()` to hide this version of Lo-Dash, it will work.
- if (typeof define == 'function' && typeof define.amd == 'object' && define.amd && define.amd.lodash) {
- define('lodash', function() {
+ // 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
+ // referenced as the "underscore" module
+ define(function() {
return lodash;
});
}
diff --git a/lodash.min.js b/lodash.min.js
index 935626073f..d7fcf19f10 100644
--- a/lodash.min.js
+++ b/lodash.min.js
@@ -1,26 +1,30 @@
/*!
- Lo-Dash 0.1.0 github.com/bestiejs/lodash/blob/master/LICENSE.txt
+ Lo-Dash 0.2.0 lodash.com/license
Underscore.js 1.3.3 github.com/documentcloud/underscore/blob/master/LICENSE
*/
-;(function(r,h){"use strict";var n=!0,o=!1;function Q(a){return"[object Arguments]"==k.call(a)}function i(a){return new p(a)}function p(a){this.o=a}function g(){for(var a,b=-1,c={},d={},f={},e=["d","j","g","a"];++b>1;c(a[e])a.length&&(b=n);z(c,function(c,e,j){if(b?ha(c)!==e||!c.length:0>E(c,e))c.push(e),d.push(a[j]);
-return c},[]);return d}function u(a,b){var c=l.call(arguments,2),d=c.length;return function(){c.length=d;V.apply(c,arguments);return a.apply(b,c)}}function ja(a,b,c){var d;return function(){var f=arguments,e=this;c&&!d&&a.apply(e,f);va(d);d=J(function(){d=h;c||a.apply(e,f)},b)}}function Y(a,b,c){c||(c=[]);if(a===b)return 0!==a||1/a==1/b;if(a==h||b==h)return a===b;a.p&&(a=a.o);b.p&&(b=b.o);if(a.isEqual&&q(a.isEqual))return a.isEqual(b);if(b.isEqual&&q(b.isEqual))return b.isEqual(a);var d=k.call(a);
-if(d!=k.call(b))return o;switch(d){case G:return a==""+b;case K:return a!=+a?b!=+b:0==a?1/a==1/b:a==+b;case ka:case la:return+a==+b;case ma:return a.source==b.source&&a.global==b.global&&a.multiline==b.multiline&&a.ignoreCase==b.ignoreCase}if("object"!=typeof a||"object"!=typeof b)return o;for(var f=c.length;f--;)if(c[f]==a)return n;var f=n,e=0;c.push(a);if(d==D){if(e=a.length,f=e==b.length)for(;e--&&(f=e in a==e in b&&Y(a[e],b[e],c)););}else{if("constructor"in a!="constructor"in b||a.constructor!=
-b.constructor)return o;for(var j in a)if(s.call(a,j)&&(e++,!(f=s.call(b,j)&&Y(a[j],b[j],c))))break;if(f){for(j in b)if(s.call(b,j)&&!e--)break;f=!e}}c.pop();return f}function q(a){return k.call(a)==ca}function R(a){return a}function na(a){t(L(a),function(b){var c=i[b]=a[b];i.prototype[b]=function(){var a=l.call(arguments);wa.call(a,this.o);a=c.apply(i,a);return this.p?(new p(a)).chain():a}})}var y={"\\":"\\","'":"'",r:"\r",n:"\n",t:"\t",u2028:"\u2028",u2029:"\u2029"};(function(){for(var a in y)y[y[a]]=
-a})();var Z="object"==typeof exports&&exports&&("object"==typeof global&&global&&global==global.global&&(r=global),exports),xa=0,ya=r._,za=/\\|'|\r|\n|\t|\u2028|\u2029/g,$=/.^/,ua=/\\(\\|'|r|n|t|u2028|u2029)/g,D="[object Array]",ka="[object Boolean]",la="[object Date]",ca="[object Function]",K="[object Number]",ma="[object RegExp]",G="[object String]",v=Array.prototype,M=Object.prototype,ba=v.concat,s=M.hasOwnProperty,V=v.push,l=v.slice,k=M.toString,wa=v.unshift,Aa=r.isFinite,M=Object.keys,va=r.clearTimeout,
-J=r.setTimeout,N={b:"b",m:"var I=i.apply([],z.call(arguments,1))",h:"[]",g:"if(p(I,b[o])<0)y.push(b[o])"},w={h:"F",g:"if(!e(g[o],o,g))return !y"},aa={b:"v",h:"v",d:"for(var A,j=1,u=arguments.length;j=h)h=j,y=g[o]"},F=Array.isArray||function(a){return k.call(a)==D},da=g({b:"H",i:"b",m:"var f=E.call(H)",h:"F",d:"if(f==c||f==B)return !H.length",g:"return k"}),pa=g({b:"g,C",h:"k",g:"if(g[o]===C)return F"}),X=g(m,w),W=g(m,B),qa=g(m,{g:"if(e(g[o],o,g))return g[o]"}),t=g(m),A=g(m,C),ra=g(m,x),x=g(m,x,{m:x.m.replace("-","").replace("max",
-"min"),g:x.g.replace(">=","<")}),O=g(C,{b:"g,x",g:{c:"y[o]=g[o][x]",k:"y[y.length]=g[o][x]"}}),z=g({b:"g,e,a,D",m:"var r=arguments.length>2;if(D)e=d(e,D)",h:"a",d:{c:"if(!r)y=g[++o]"},g:{c:"y=e(y,g[o],o,g)",k:"y=r?e(y,g[o],o,g):(r=F,g[o])"}}),B=g(m,B,{g:"!"+B.g}),w=g(m,w,{h:"k",g:w.g.replace("!","")}),sa=g(C,{b:"g",g:{c:"y[o]=g[o]",k:"y[y.length]=g[o]"}}),C=g({b:"b",h:"[]",g:"if(b[o])y.push(b[o])"}),m=g(N),N=g(N,{m:"var I=z.call(arguments,1)",h:"[]"}),ta=g(aa,{g:"if(v[o]==G)"+aa.g}),P=g(aa),L=g(oa,
-{m:"",n:o,g:"if(E.call(v[o])==l)y.push(o)",l:"y.sort()"});Q(arguments)||(Q=function(a){return!(!a||!s.call(a,"callee"))});var T=M||g(oa);P(i,{VERSION:"0.1.0",templateSettings:{escape:/<%-([\s\S]+?)%>/g,evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g},after:function(a,b){return 1>a?b():function(){if(1>--a)return b.apply(this,arguments)}},bind:u,bindAll:function(a){var b=arguments,c=1;1==b.length&&(c=0,b=L(a));for(var d=b.length;c/g,
-">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")},every:X,extend:P,filter:W,find:qa,first:U,flatten:H,forEach:t,functions:L,groupBy:function(a,b){var c={};if(!q(b))var d=b,b=function(a){return a[d]};t(a,function(a,e,d){e=b(a,e,d);(c[e]||(c[e]=[])).push(a)});return c},has:function(a,b){return s.call(a,b)},identity:R,indexOf:E,initial:function(a,b,c){return l.call(a,0,-(b==h||c?1:b))},intersection:ga,invoke:function(a,b){var c=l.call(arguments,2),d=q(b);return A(a,function(a){return(d?
-b||a:a[b]).apply(a,c)})},isArguments:Q,isArray:F,isBoolean:function(a){return a===n||a===o||k.call(a)==ka},isDate:function(a){return k.call(a)==la},isElement:function(a){return!!(a&&1==a.nodeType)},isEmpty:da,isEqual:Y,isFinite:function(a){return Aa(a)&&k.call(a)==K},isFunction:q,isNaN:function(a){return k.call(a)==K&&a!=+a},isNull:function(a){return null===a},isNumber:function(a){return k.call(a)==K},isObject:function(a){return a===Object(a)},isRegExp:function(a){return k.call(a)==ma},isString:function(a){return k.call(a)==
-G},isUndefined:function(a){return a===h},keys:T,last:ha,lastIndexOf:function(a,b){if(a==h)return-1;for(var c=a.length;c--;)if(a[c]===b)return c;return-1},map:A,max:ra,memoize:function(a,b){var c={};return function(){var d=b?b.apply(this,arguments):arguments[0];return s.call(c,d)?c[d]:c[d]=a.apply(this,arguments)}},min:x,mixin:na,noConflict:function(){r._=ya;return this},once:function(a){var b,c=o;return function(){if(c)return b;c=n;return b=a.apply(this,arguments)}},pick:function(a){for(var b,c=-1,
-d=H(l.call(arguments,1)),f=d.length,e={};++carguments.length&&(b=a||0,a=0);for(var d=-1,f=Math.max(Math.ceil((b-a)/c),0),e=Array(f);++dd?1:0}),"b")},sortedIndex:fa,tap:function(a,b){b(a);return a},template:function(a,b,c){function d(a){return f.call(this,a,i)}c=ta(c||{},i.templateSettings);a="__p+='"+a.replace(za,function(a){return"\\"+y[a]}).replace(c.escape||$,function(a,b){return"'+((__t=("+
-S(b)+"))==null?'':_['escape'](__t))+'"}).replace(c.interpolate||$,function(a,b){return"'+((__t=("+S(b)+"))==null?'':__t)+'"}).replace(c.evaluate||$,function(a,b){return"';"+S(b)+";__p+='"})+"';";c.variable||(a="with(object||{}){"+a+"}");var a='var __t,__j=Array.prototype.join,__p="";function print(){__p+=__j.call(arguments,"")}'+a+"return __p",f=Function(c.variable||"object","_",a);if(b)return f(b,i);d.source="function("+(c.variable||"object")+"){"+a+"}";return d},throttle:function(a,b){var c,d,f,
-e,g,i,k=ja(function(){d=g=o},b);return function(){c=arguments;e=this;i||(i=J(function(){i=h;d&&a.apply(e,c);k()},b));g?d=n:f=a.apply(e,c);k();g=n;return f}},times:function(a,b,c){c&&(b=u(b,c));for(c=0;carguments.length;c&&(b=v(b,c));if(e===+e){for(e&&f&&(d=a[--e]);e--;)d=b(d,a[e],e,a);return d}var g=U(a);for((e=g.length)&&f&&(d=a[g[--e]]);e--;)f=g[e],d=b(d,a[f],f,a);return d}function V(a,b,d){return b==m||d?a[0]:l.call(a,0,b)}function fa(a,b){if(b)return D.apply(z,a);for(var d,c=-1,e=a.length,f=[];++cw(f,b)&&W(e,function(a){return-1c&&(c=d,g=a[e]);return g}function ja(a,b,d){return l.call(a,b==m||d?1:b)}function ga
+(a,b,d){var c,e=0,f=a.length;for(d&&(b=d(b));e>1,(d?d(a[c]):a[c])f&&(b=n);++ew(i,c))i.push(c),g.push(a[e]);return g}function v(a,b){var d,c=h.call(a)==s;if(c){if(B)return a=B.call.apply(B,arguments),function(){return arguments.length?a.apply(m,arguments):a()}}else d=b,b=a;var e=l.call(arguments,2),f=e.length;return function(){var g;return g=arguments,c||(a=b[d
+]),f&&(g.length&&(e.length=f,E.apply(e,g)),g=e),g=g.length?a.apply(b,g):a.call(b),e.length=f,g}}function M(a,b,d){d||(d=[]);if(a===b)return 0!==a||1/a==1/b;if(a==m||b==m)return a===b;a.s&&(a=a._wrapped),b.s&&(b=b._wrapped);if(a.isEqual&&h.call(a.isEqual)==s)return a.isEqual(b);if(b.isEqual&&h.call(b.isEqual)==s)return b.isEqual(a);var c=h.call(a);if(c!=h.call(b))return r;switch(c){case K:return a==""+b;case N:return a!=+a?b!=+b:0==a?1/a==1/b:a==+b;case la:case ma:return+a==+b;case na:return a.source==
+b.source&&a.global==b.global&&a.multiline==b.multiline&&a.ignoreCase==b.ignoreCase}if("object"!=typeof a||"object"!=typeof b)return r;for(var e=d.length;e--;)if(d[e]==a)return n;var e=-1,f=n,g=0;d.push(a);if(c==I){if(g=a.length,f=g==b.length)for(;g--&&(f=M(a[g],b[g],d)););}else{if("constructor"in a!="constructor"in b||a.constructor!=b.constructor)return r;for(var i in a)if(t.call(a,i)&&(g++,!(f=t.call(b,i)&&M(a[i],b[i],d))))break;if(f){for(i in b)if(t.call(b,i)&&!(g--))break;f=!g}if(f&&H)for(;7>++
+e&&(i=ba[e],!t.call(a,i)||!!(f=t.call(b,i)&&M(a[i],b[i],d))););}return d.pop(),f}function ca(a){return a}function oa(a){F(O(a),function(b){var d=c[b]=a[b];p.prototype[b]=function(){var a=[this._wrapped];return arguments.length&&E.apply(a,arguments),a=1==a.length?d.call(c,a[0]):d.apply(c,a),this.s&&(a=new p(a),a.s=n),a}})}var n=!0,o=null,r=!1,X="object"==typeof exports&&exports&&("object"==typeof global&&global&&global==global.global&&(u=global),exports),L=5e4;try{(function(){L=arguments.length}).
+apply(o,Array(L))}catch(Ha){}var wa={"\\":"\\","'":"'","\n":"n","\r":"r"," ":"t","\u2028":"u2028","\u2029":"u2029"},H=!{valueOf:0}.propertyIsEnumerable("valueOf"),Aa=0,S={"boolean":r,"function":n,object:n,number:r,string:r,"undefined":r},Ba=u._,A=RegExp("^"+({}.valueOf+"").replace(/[.*+?^=!:${}()|[\]\/\\]/g,"\\$&").replace(/valueOf|for [^\]]+/g,".+?")+"$"),Ca=/__token__(\d+)/g,Da=/['\n\r\t\u2028\u2029\\]/g,ba="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf"
+.split(" "),T="__token__",x=[],I="[object Array]",la="[object Boolean]",ma="[object Date]",s="[object Function]",N="[object Number]",na="[object RegExp]",K="[object String]",z=Array.prototype,y=Object.prototype,D=z.concat,t=y.hasOwnProperty,E=z.push,l=z.slice,h=y.toString,B=A.test(B=l.bind)&&/\n|Opera/.test(B+h.call(u.opera))&&B,C=A.test(C=Array.isArray)&&C,Ea=u.isFinite,Y=A.test(Y=Object.keys)&&Y,Fa=u.clearTimeout,P=u.setTimeout,ta=Function("u","var __p;with(u){__p='var o,z';if(k){__p+='='+k};__p+=';'+f+';'+q+';';if(c){__p+='var t='+g+'.length;o=-1;';if(o){__p+='if(t===+t){'};__p+=''+c['d']+';while('+c['m']+'){'+c['j']+'}';if(o){__p+='}'}}if(o){if(c){__p+='else{'}if(!i){__p+='var A=typeof '+l+'==\\'function\\';'};__p+=''+o['d']+';for('+o['m']+'){';if(i){if(r){__p+='if('+h+'){'};__p+=''+o['j']+';';if(r){__p+='}'}}else{__p+='if(!(A&&o==\\'prototype\\')';if(r){__p+='&&'+h};__p+='){'+o['j']+'}'};__p+='}';if(i){__p+='var j='+l+'.constructor;';for(var k=0;k<7;k++){__p+='o=\\''+p[k]+'\\';if(';if(p[k]=='constructor'){__p+='!(j&&j.prototype==='+l+')&&'};__p+=''+h+'){'+o['j']+'}'}}if(c){__p+='}'}};__p+=''+e+';return z'}return __p"
+),q={a:"h,f,G",k:"h",q:"if(!f){f=n}else if(G){f=e(f,G)}",j:"f(h[o],o,h)"},Z={k:"I",j:"if(!f(h[o],o,h))return!z"},A={a:"u",k:"u",q:"for(var C,D=1,t=arguments.length;D/g,evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,variable:"object"},c.after=
+function(a,b){return 1>a?b():function(){if(1>--a)return b.apply(this,arguments)}},c.bind=v,c.bindAll=function(a){var b=arguments,d=1;1==b.length&&(d=0,b=O(a));for(var c=b.length;dw(e,a[b])&&c.push(a[b]);return c},c.escape=function(a){return(a+"").replace(/&/g,"&").replace(/arguments.length&&(b=a||0,a=0);for(var c=-1,e=Math.max(Math.ceil((b-a)/d),0),f=Array(e);++cc?1:0}),"b")},c.sortedIndex=ga,c.tap=function(a,b)
+{return b(a),a},c.template=function(a,b,d){d||(d={});var k;k=c.templateSettings||{};var e=d.escape,f=d.evaluate,g=d.interpolate,d=d.variable;return e==o&&(e=k.escape),f==o&&(f=k.evaluate),g==o&&(g=k.interpolate),e&&(a=a.replace(e,xa)),g&&(a=a.replace(g,ya)),f&&(a=a.replace(f,za)),a="__p='"+a.replace(Da,va).replace(Ca,ua)+"';\n",x.length=0,d||(d=k.variable||"object",a="with("+d+"||{}){"+a+"}"),a="function("+d+"){var __p,__t,__j=Array.prototype.join;function print(){__p+=__j.call(arguments,'')}"+a+"return __p}"
+,k=Function("_","return "+a)(c),b?k(b):(k.source=a,k)},c.throttle=function(a,b){function d(){i=new Date,g=m,a.apply(f,c)}var c,e,f,g,i=0;return function(){var h=new Date,j=b-(h-i);return c=arguments,f=this,0>=j?(i=h,e=a.apply(f,c)):g||(g=P(d,j)),e}},c.times=function(a,b,d){d&&(b=v(b,d));for(d=0;dw(b,d[a])&&b.push(d[a]);return b},c.uniq=ka,c.uniqueId=function(a){var b=Aa++;return a?a+b:b},c.values=ra,c.without=function(a){for(var b=l.call(arguments,1),d=-1,c=a.length,e=[];++dw(b,a[d])&&e.push(a[d]);return e},c.wrap=function(a,b){return function(){var c=[a];return arguments.length&&E.apply(c,arguments),b.apply(this,c)}},c.zip=function(){for(var a=-1,b=ia(Q(arguments,"length")),c=Array(b);++a
+
+
+
+ Lo-Dash Performance Suite
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/perf/perf.js b/perf/perf.js
new file mode 100644
index 0000000000..c07fa18018
--- /dev/null
+++ b/perf/perf.js
@@ -0,0 +1,200 @@
+(function(window) {
+
+ /** Use a single load function */
+ var load = typeof require == 'function' ? require : window.load;
+
+ /** 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('../lodash.min.js') || window._,
+ lodash = lodash._ || lodash,
+ lodash.noConflict()
+ );
+
+ /** Load Underscore */
+ var _ =
+ window._ || (
+ _ = load('../vendor/underscore/underscore-min.js') || window._,
+ _._ || _
+ );
+
+ /** Used to score Lo-Dash and Underscore performance */
+ var score = { 'lodash': 0, 'underscore': 0 };
+
+ /** Used to queue benchmark suites */
+ var suites = [];
+
+ /** Add `console.log()` support for Narwhal and RingoJS */
+ window.console || (window.console = { 'log': window.print });
+
+ /** Expose functions to the global object */
+ window._ = _;
+ window.Benchmark = Benchmark;
+ window.lodash = lodash;
+
+ /*--------------------------------------------------------------------------*/
+
+ lodash.extend(Benchmark.options, {
+ 'async': true,
+ 'setup': function() {
+ var window = Function('return this || global')(),
+ _ = window._,
+ lodash = window.lodash,
+ numbers = [],
+ object = {};
+
+ for (var index = 0; index < 20; index++) {
+ numbers[index] = index;
+ object['key' + index] = index;
+ }
+
+ var objects = lodash.map(numbers, function(n) {
+ return { 'num': n };
+ });
+ }
+ });
+
+ lodash.extend(Benchmark.Suite.options, {
+ 'onStart': function() {
+ console.log('\n' + this.name + ':');
+ },
+ 'onCycle': function(event) {
+ console.log(event.target + '');
+ },
+ 'onComplete': function() {
+ var fastest = this.filter('fastest').pluck('name'),
+ lodashHz = 1 / (this[0].stats.mean + this[0].stats.moe),
+ underscoreHz = 1 / (this[1].stats.mean + this[1].stats.moe);
+
+ if (fastest.length > 1) {
+ console.log('It\'s too close to call.');
+ lodashHz = underscoreHz = Math.min(lodashHz, underscoreHz);
+ } else {
+ console.log(fastest + ' is the fastest.');
+ }
+ // add score adjusted for margin of error
+ score.lodash += lodashHz;
+ score.underscore += underscoreHz;
+
+ // remove current suite from queue
+ suites.shift();
+
+ if (suites.length) {
+ // run next suite
+ suites[0].run();
+ }
+ else {
+ // report results
+ if (score.lodash >= score.underscore) {
+ console.log('\nLo-Dash is ' + (score.lodash / score.underscore).toFixed(2) + 'x faster than Underscore.');
+ } else {
+ console.log('\nUnderscore is ' + (score.underscore / score.lodash).toFixed(2) + 'x faster than Lo-Dash.');
+ }
+ }
+ }
+ });
+
+ /*--------------------------------------------------------------------------*/
+
+ suites.push(
+ Benchmark.Suite('each array')
+ .add('Lo-Dash', function() {
+ var timesTwo = [];
+ lodash.each(numbers, function(num) {
+ timesTwo.push(num * 2);
+ });
+ })
+ .add('Underscore', function() {
+ var timesTwo = [];
+ _.each(numbers, function(num) {
+ timesTwo.push(num * 2);
+ });
+ })
+ );
+
+ /*--------------------------------------------------------------------------*/
+
+ suites.push(
+ Benchmark.Suite('each object')
+ .add('Lo-Dash', function() {
+ var timesTwo = [];
+ lodash.each(object, function(num) {
+ timesTwo.push(num * 2);
+ });
+ })
+ .add('Underscore', function() {
+ var timesTwo = [];
+ _.each(object, function(num) {
+ timesTwo.push(num * 2);
+ });
+ })
+ );
+
+ /*--------------------------------------------------------------------------*/
+
+ suites.push(
+ Benchmark.Suite('keys')
+ .add('Lo-Dash', function() {
+ lodash.keys(object);
+ })
+ .add('Underscore', function() {
+ _.keys(object);
+ })
+ );
+
+ /*--------------------------------------------------------------------------*/
+
+ suites.push(
+ Benchmark.Suite('map')
+ .add('Lo-Dash', function() {
+ lodash.map(objects, function(obj) {
+ return obj.num;
+ });
+ })
+ .add('Underscore', function() {
+ _.map(objects, function(obj) {
+ return obj.num;
+ });
+ })
+ );
+
+ /*--------------------------------------------------------------------------*/
+
+ suites.push(
+ Benchmark.Suite('pluck')
+ .add('Lo-Dash', function() {
+ lodash.pluck(objects, 'num');
+ })
+ .add('Underscore', function() {
+ _.pluck(objects, 'num');
+ })
+ );
+
+ /*--------------------------------------------------------------------------*/
+
+ suites.push(
+ Benchmark.Suite('values')
+ .add('Lo-Dash', function() {
+ lodash.values(objects);
+ })
+ .add('Underscore', function() {
+ _.values(objects);
+ })
+ );
+
+ /*--------------------------------------------------------------------------*/
+
+ if (Benchmark.platform + '') {
+ console.log(Benchmark.platform + '');
+ }
+ // start suites
+ suites[0].run();
+
+}(typeof global == 'object' && global || this));
diff --git a/perf/run-perf.sh b/perf/run-perf.sh
new file mode 100755
index 0000000000..02141a5d94
--- /dev/null
+++ b/perf/run-perf.sh
@@ -0,0 +1,9 @@
+cd "$(dirname "$0")"
+for cmd in node narwhal ringo rhino; do
+ echo ""
+ echo "Running performance suite in $cmd..."
+ $cmd perf.js
+done
+echo ""
+echo "Running performance suite in a browser..."
+open index.html
\ No newline at end of file
diff --git a/test/backbone.html b/test/backbone.html
new file mode 100644
index 0000000000..87bcc06e3f
--- /dev/null
+++ b/test/backbone.html
@@ -0,0 +1,39 @@
+
+
+
+
+ Backbone Test Suite
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/index.html b/test/index.html
index 4a1b117a10..6574537380 100644
--- a/test/index.html
+++ b/test/index.html
@@ -6,13 +6,22 @@
-
-
-
-
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+