diff --git a/.travis.yml b/.travis.yml
index 7cc82bd0ef..2ea46dcbba 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,7 +5,9 @@ node_js:
- "0.10"
env:
global:
- - BIN="node" BUILD=false MAKE=false OPTION=""
+ - BIN="node" BUILD=false COMPAT=false MAKE=false OPTION="" SAUCE_LABS=false
+ - SAUCE_USERNAME="jdalton"
+ - secure: "woILQltl1pI3DgadZ5NrcqntdPvnRmQBwIVNZL91Ht5d9snIhgyAixI6xNAS8F8BzD9RzqzVPHay5sHfn+GhNaojcaiHs1nXAbdyclevMyfP+3MQ1HGfMSU0bv1GdT35LJ+C0u4Y3SuuZSbBlNEeLXRPMngPZahf4xL8RsZz/is="
matrix:
- BUILD="compat"
- BUILD="modern"
@@ -34,25 +36,45 @@ matrix:
env: BIN="ringo" BUILD="compat"
- node_js: "0.10"
env: BIN="ringo" BUILD="legacy"
+ - node_js: "0.8"
+ env: SAUCE_LABS=true BUILD="compat"
+ - node_js: "0.8"
+ env: SAUCE_LABS=true BUILD="modern"
+ - node_js: "0.8"
+ env: SAUCE_LABS=true BUILD="legacy"
+ - node_js: "0.8"
+ env: SAUCE_LABS=true BUILD="mobile"
+ - node_js: "0.8"
+ env: SAUCE_LABS=true BUILD="underscore"
git:
- depth: 1
+ depth: 10
branches:
only:
- master
before_install:
- - "[ $BIN == 'istanbul' ] && npm install -g istanbul || true"
+ - "([ $BUILD == 'legacy' ] || [ $BUILD == 'mobile' ] || [ $BUILD == 'modern' ]) && MAKE=true || true"
+ - "([ $BUILD == 'compat' ] || [ $BUILD == 'legacy' ]) && COMPAT=true || true"
+ - "[ $SAUCE_LABS != false ] && npm i ecstatic@\"~0.4.0\" request@\"~2.27.0\" sauce-tunnel@\"~1.1.0\" || true"
+ - "[ $BIN == 'istanbul' ] && npm i -g istanbul@\"~0.1.0\" || true"
- "[ $BIN == 'narwhal' ] && wget https://github.com/280north/narwhal/archive/v0.3.2.zip && sudo unzip v0.3.2 -d /opt/ && rm v0.3.2.zip || true"
- "[ $BIN == 'narwhal' ] && sudo ln -s /opt/narwhal-0.3.2/bin/narwhal /usr/local/bin/narwhal && sudo chmod +x /usr/local/bin/narwhal || true"
- "[ $BIN == 'rhino' ] && sudo mkdir /opt/rhino-1.7R5 && sudo wget -O /opt/rhino-1.7R5/js.jar https://oss.sonatype.org/content/repositories/snapshots/org/mozilla/rhino/1.7R5-SNAPSHOT/rhino-1.7R5-20120629.144839-4.jar || true"
- "[ $BIN == 'rhino' ] && echo -e '#!/bin/sh\\njava -jar /opt/rhino-1.7R5/js.jar $@' | sudo tee /usr/local/bin/rhino && sudo chmod +x /usr/local/bin/rhino || true"
- "[ $BIN == 'ringo' ] && wget http://ringojs.org/downloads/ringojs-0.9.zip && sudo unzip ringojs-0.9 -d /opt && rm ringojs-0.9.zip || true"
- "[ $BIN == 'ringo' ] && sudo ln -s /opt/ringojs-0.9/bin/ringo /usr/local/bin/ringo && sudo chmod +x /usr/local/bin/ringo || true"
-script:
- - "[ $BIN == 'istanbul' ] && $BIN cover ./test/test.js || true"
- - "[ $BUILD != false ] && [ $BUILD != 'compat' ] && [ $BUILD != 'modern' ] && MAKE=true || true"
- - "[ $MAKE != false ] && git clone --depth=1 --branch=master git://github.com/lodash/lodash-cli.git ./node_modules/lodash-cli || true"
+ - "[ $MAKE != false ] && git clone --depth=10 --branch=master git://github.com/lodash/lodash-cli.git ./node_modules/lodash-cli || true"
- "[ $MAKE != false ] && mkdir ./node_modules/lodash-cli/node_modules && cd ./node_modules/lodash-cli/node_modules/ && ln -s ../../../ ./lodash && cd ../ && npm i . && cd ../../ || true"
- "[ $MAKE != false ] && node ./node_modules/lodash-cli/bin/lodash $BUILD -o ./dist/lodash.$BUILD.js || true"
- - "[ $BUILD != false ] && cd ./test || true"
- - "[ $BUILD != false ] && $BIN $OPTION ./test.js ../dist/lodash.$BUILD.js || true"
- - "[ $BUILD != false ] && $BIN $OPTION ./test.js ../dist/lodash.$BUILD.min.js || true"
+script:
+ - "[ $BIN == 'istanbul' ] && $BIN cover ./test/test.js || true"
+ - "([ $SAUCE_LABS != false ] || [ $BUILD == false ]) && true || cd ./test"
+ - "([ $SAUCE_LABS != false ] || [ $BUILD == false ]) && true || $BIN $OPTION ./test.js ../dist/lodash.$BUILD.js"
+ - "([ $SAUCE_LABS != false ] || [ $BUILD == false ]) && true || $BIN $OPTION ./test.js ../dist/lodash.$BUILD.min.js"
+ - "([ $SAUCE_LABS == false ] || [ $BUILD == 'underscore' ]) && true || node ./test/saucelabs.js \"test/index.html?build=lodash-$BUILD\""
+ - "([ $SAUCE_LABS == false ] || [ $BUILD == 'underscore' ]) && true || node ./test/saucelabs.js \"test/index.html?build=../dist/lodash.$BUILD.js\""
+ - "[ $SAUCE_LABS == false ] && true || node ./test/saucelabs.js \"test/backbone.html?build=lodash-$BUILD\""
+ - "[ $SAUCE_LABS == false ] && true || node ./test/saucelabs.js \"test/backbone.html?build=../dist/lodash.$BUILD.js\""
+ - "[ $SAUCE_LABS == false ] && true || node ./test/saucelabs.js \"test/underscore.html?build=lodash-$BUILD\""
+ - "[ $SAUCE_LABS == false ] && true || node ./test/saucelabs.js \"test/underscore.html?build=../dist/lodash.$BUILD.js\""
+ - "([ $SAUCE_LABS == false ] || [ $COMPAT == false ]) && true || node ./test/saucelabs.js \"test/index.html?build=lodash-$BUILD&compat=7\""
+ - "([ $SAUCE_LABS == false ] || [ $COMPAT == false ]) && true || node ./test/saucelabs.js \"test/index.html?build=../dist/lodash.$BUILD.js&compat=7\""
diff --git a/README.md b/README.md
index 3a2abc5321..854345f794 100644
--- a/README.md
+++ b/README.md
@@ -1,29 +1,33 @@
-# Lo-Dash v2.2.1
+# Lo-Dash v2.3.0
A utility library delivering consistency, [customization](http://lodash.com/custom-builds), [performance](http://lodash.com/benchmarks), & [extras](http://lodash.com/#features).
## Download
+Check out our [wiki]([https://github.com/lodash/lodash/wiki/build-differences]) for details over the differences between builds.
+
* Modern builds perfect for newer browsers/environments:
-[Development](https://raw.github.com/lodash/lodash/2.2.1/dist/lodash.js) &
-[Production](https://raw.github.com/lodash/lodash/2.2.1/dist/lodash.min.js)
+[Development](https://raw.github.com/lodash/lodash/2.3.0/dist/lodash.js) &
+[Production](https://raw.github.com/lodash/lodash/2.3.0/dist/lodash.min.js)
* Compatibility builds for older environment support too:
-[Development](https://raw.github.com/lodash/lodash/2.2.1/dist/lodash.compat.js) &
-[Production](https://raw.github.com/lodash/lodash/2.2.1/dist/lodash.compat.min.js)
+[Development](https://raw.github.com/lodash/lodash/2.3.0/dist/lodash.compat.js) &
+[Production](https://raw.github.com/lodash/lodash/2.3.0/dist/lodash.compat.min.js)
* Underscore builds to use as a drop-in replacement:
-[Development](https://raw.github.com/lodash/lodash/2.2.1/dist/lodash.underscore.js) &
-[Production](https://raw.github.com/lodash/lodash/2.2.1/dist/lodash.underscore.min.js)
+[Development](https://raw.github.com/lodash/lodash/2.3.0/dist/lodash.underscore.js) &
+[Production](https://raw.github.com/lodash/lodash/2.3.0/dist/lodash.underscore.min.js)
+
+CDN copies are available on [cdnjs](http://cdnjs.com/libraries/lodash.js/) & [jsDelivr](http://www.jsdelivr.com/#!lodash). For smaller file sizes, create [custom builds](http://lodash.com/custom-builds) with only the features needed.
-CDN copies are available on [cdnjs](http://cdnjs.com/libraries/lodash.js/) & [jsDelivr](http://www.jsdelivr.com/#!lodash).
-For smaller file sizes, create [custom builds](http://lodash.com/custom-builds) with only the features needed.
-Love modules? We’ve got you covered with [lodash-amd](https://npmjs.org/package/lodash-amd), [lodash-node](https://npmjs.org/package/lodash-node), & [npm packages](https://npmjs.org/browse/keyword/lodash-modularized) per method.
+Love modules? We’ve got you covered with [lodash-amd](https://npmjs.org/package/lodash-amd), [lodash-es6](https://github.com/lodash/lodash-es6), [lodash-node](https://npmjs.org/package/lodash-node), & [npm packages](https://npmjs.org/browse/keyword/lodash-modularized) per method.
## Dive in
-There’s plenty of [documentation](http://lodash.com/docs), [unit tests](http://lodash.com/tests), & [benchmarks](http://lodash.com/benchmarks).
-For a list of upcoming features, check out our [roadmap](https://github.com/lodash/lodash/wiki/Roadmap).
-The full changelog for this release is available on our [wiki](https://github.com/lodash/lodash/wiki/Changelog).
+There’s plenty of **[documentation](http://lodash.com/docs)**, [unit tests](http://lodash.com/tests), & [benchmarks](http://lodash.com/benchmarks).
+Check out DevDocs as a fast, organized, & searchable interface for our documentation.
+
+The full changelog for this release is available on our [wiki](https://github.com/lodash/lodash/wiki/Changelog).
+A list of upcoming features is available on our [roadmap](https://github.com/lodash/lodash/wiki/Roadmap).
## Features *not* in Underscore
@@ -34,6 +38,7 @@ The full changelog for this release is available on our [wiki](https://github.co
* [_.clone](http://lodash.com/docs#clone) supports shallow cloning of `Date` & `RegExp` objects
* [_.cloneDeep](http://lodash.com/docs#cloneDeep) for deep cloning arrays & objects
* [_.contains](http://lodash.com/docs#contains) accepts a `fromIndex`
+ * [_.create](http://lodash.com/docs#create) for easier object inheritance
* [_.createCallback](http://lodash.com/docs#createCallback) for extending callbacks in methods & mixins
* [_.curry](http://lodash.com/docs#curry) for creating [curried](http://hughfdjackson.com/javascript/2013/07/06/why-curry-helps/) functions
* [_.debounce](http://lodash.com/docs#debounce) & [_.throttle](http://lodash.com/docs#throttle) accept additional `options` for more control
@@ -44,6 +49,7 @@ The full changelog for this release is available on our [wiki](https://github.co
* [_.isPlainObject](http://lodash.com/docs#isPlainObject) for checking if values are created by `Object`
* [_.memoize](http://lodash.com/docs#memoize) exposes the `cache` of memoized functions
* [_.merge](http://lodash.com/docs#merge) for a deep [_.extend](http://lodash.com/docs#extend)
+ * [_.noop](http://lodash.com/docs#noop) for function placeholders
* [_.parseInt](http://lodash.com/docs#parseInt) for consistent behavior
* [_.partialRight](http://lodash.com/docs#partialRight) for [partial application](http://lodash.com/docs#partial) from the right
* [_.pull](http://lodash.com/docs#pull) & [_.remove](http://lodash.com/docs#remove) for mutating arrays
@@ -65,6 +71,9 @@ The full changelog for this release is available on our [wiki](https://github.co
## Resources
+ * Podcasts
+ - [JavaScript Jabber](http://javascriptjabber.com/079-jsj-lo-dash-with-john-david-dalton/)
+
* Posts
- [Say “Hello” to Lo-Dash](http://kitcambridge.be/blog/say-hello-to-lo-dash/)
- [Custom builds in Lo-Dash 2.0](http://kitcambridge.be/blog/custom-builds-in-lo-dash-2-dot-0/)
@@ -77,9 +86,14 @@ The full changelog for this release is available on our [wiki](https://github.co
- [Testing](https://vimeo.com/45865290)
- [CascadiaJS ’12](http://www.youtube.com/watch?v=dpPy4f_SeEk)
+ A list of other community created podcasts, posts, & videos is available on our [wiki](https://github.com/lodash/lodash/wiki/Resources).
+
## Support
-Tested in Chrome 5~29, Firefox 2~24, IE 6-10, Opera 9.25~16, Safari 3-6, Node.js 0.6.8-0.10.20, Narwhal 0.3.2, PhantomJS 1.9.2, RingoJS 0.9, & Rhino 1.7RC5.
+Tested in Chrome 5~30, Firefox 2~25, IE 6-11, Opera 9.25~17, Safari 3-7, Node.js 0.6.8-0.10.21, Narwhal 0.3.2, PhantomJS 1.9.2, RingoJS 0.9, & Rhino 1.7RC5.
+
+Special thanks to [Sauce Labs](https://saucelabs.com/) for providing automated browser testing.
+[](https://saucelabs.com/ "Sauce Labs: Selenium Testing & More")
## Installation & usage
@@ -109,7 +123,6 @@ var _ = require('lodash/dist/lodash.underscore');
**Notes:**
* Don’t assign values to [special variable](http://nodejs.org/api/repl.html#repl_repl_features) `_` when in the REPL
* If Lo-Dash is installed globally, run [`npm ln lodash`](http://blog.nodejs.org/2011/03/23/npm-1-0-global-vs-local-installation/) in your project’s root directory *before* requiring it
- * Node.js 0.10.8-0.10.11 [have](https://github.com/joyent/node/issues/5622) [bugs](https://github.com/joyent/node/issues/5688) preventing minified builds
In [Rhino](http://www.mozilla.org/rhino/):
@@ -132,12 +145,12 @@ require({
## Author
-| [](http://twitter.com/jdalton "Follow @jdalton on Twitter") |
+| [](https://twitter.com/jdalton "Follow @jdalton on Twitter") |
|---|
| [John-David Dalton](http://allyoucanleet.com/) |
## Contributors
-| [](http://twitter.com/blainebublitz "Follow @BlaineBublitz on Twitter") | [](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter") | [](http://twitter.com/mathias "Follow @mathias on Twitter") |
+| [](https://twitter.com/blainebublitz "Follow @BlaineBublitz on Twitter") | [](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter") | [](https://twitter.com/mathias "Follow @mathias on Twitter") |
|---|---|---|
-| [Blaine Bublitz](http://iceddev.com/) | [Kit Cambridge](http://kitcambridge.github.io/) | [Mathias Bynens](http://mathiasbynens.be/) |
+| [Blaine Bublitz](http://www.iceddev.com/) | [Kit Cambridge](http://kitcambridge.be/) | [Mathias Bynens](http://mathiasbynens.be/) |
diff --git a/bower.json b/bower.json
index 352320e2cc..57ac7ae2a3 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
{
"name": "lodash",
- "version": "2.2.1",
+ "version": "2.3.0",
"main": "dist/lodash.compat.js",
"ignore": [
".*",
diff --git a/component.json b/component.json
index 5750a62152..8be106fbd6 100644
--- a/component.json
+++ b/component.json
@@ -1,7 +1,7 @@
{
"name": "lodash",
"repo": "lodash/lodash",
- "version": "2.2.1",
+ "version": "2.3.0",
"description": "A utility library delivering consistency, customization, performance, & extras.",
"license": "MIT",
"keywords": ["amd", "browser", "client", "functional", "server", "util"],
diff --git a/dist/lodash.compat.js b/dist/lodash.compat.js
index e48344d4cf..957c070c07 100644
--- a/dist/lodash.compat.js
+++ b/dist/lodash.compat.js
@@ -1,6 +1,6 @@
/**
* @license
- * Lo-Dash 2.2.1 (Custom Build)
+ * Lo-Dash 2.3.0 (Custom Build)
* Build: `lodash -o ./dist/lodash.compat.js`
* Copyright 2012-2013 The Dojo Foundation
* Based on Underscore.js 1.5.2
@@ -58,7 +58,7 @@
var reFlags = /\w*$/;
/** Used to detected named functions */
- var reFuncName = /^function[ \n\r\t]+\w/;
+ var reFuncName = /^\s*function[ \n\r\t]+\w/;
/** Used to match "interpolate" template delimiters */
var reInterpolate = /<%=([\s\S]+?)%>/g;
@@ -391,15 +391,6 @@
return typeof value.toString != 'function' && typeof (value + '') == 'string';
}
- /**
- * A no-operation function.
- *
- * @private
- */
- function noop() {
- // no operation performed
- }
-
/**
* Releases the given array back to the array pool.
*
@@ -505,11 +496,14 @@
/** Used to restore the original `_` reference in `noConflict` */
var oldDash = context._;
+ /** Used to resolve the internal [[Class]] of values */
+ var toString = objectProto.toString;
+
/** Used to detect if a method is native */
var reNative = RegExp('^' +
- String(objectProto.valueOf)
+ String(toString)
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
- .replace(/valueOf|for [^\]]+/g, '.+?') + '$'
+ .replace(/toString| for [^\]]+/g, '.*?') + '$'
);
/** Native method shortcuts */
@@ -522,13 +516,16 @@
now = reNative.test(now = Date.now) && now || function() { return +new Date; },
push = arrayRef.push,
propertyIsEnumerable = objectProto.propertyIsEnumerable,
- setImmediate = context.setImmediate,
setTimeout = context.setTimeout,
- splice = arrayRef.splice,
- toString = objectProto.toString,
- unshift = arrayRef.unshift;
+ splice = arrayRef.splice;
+
+ /** Used to detect `setImmediate` in Node.js */
+ var setImmediate = typeof (setImmediate = freeGlobal && moduleExports && freeGlobal.setImmediate) == 'function' &&
+ !reNative.test(setImmediate) && setImmediate;
+ /** Used to set meta data on functions */
var defineProperty = (function() {
+ // IE 8 only accepts DOM elements
try {
var o = {},
func = reNative.test(func = Object.defineProperty) && func,
@@ -538,8 +535,7 @@
}());
/* Native method shortcuts for methods with the same name as other `lodash` methods */
- var nativeBind = reNative.test(nativeBind = toString.bind) && nativeBind,
- nativeCreate = reNative.test(nativeCreate = Object.create) && nativeCreate,
+ var nativeCreate = reNative.test(nativeCreate = Object.create) && nativeCreate,
nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray,
nativeIsFinite = context.isFinite,
nativeIsNaN = context.isNaN,
@@ -547,12 +543,7 @@
nativeMax = Math.max,
nativeMin = Math.min,
nativeParseInt = context.parseInt,
- nativeRandom = Math.random,
- nativeSlice = arrayRef.slice;
-
- /** Detect various environments */
- var isIeOpera = reNative.test(context.attachEvent),
- isV8 = nativeBind && !/\n|true/.test(nativeBind + isIeOpera);
+ nativeRandom = Math.random;
/** Used to lookup a built-in constructor by [[Class]] */
var ctorByClass = {};
@@ -575,10 +566,10 @@
(function() {
var length = shadowedProps.length;
while (length--) {
- var prop = shadowedProps[length];
+ var key = shadowedProps[length];
for (var className in nonEnumProps) {
- if (hasOwnProperty.call(nonEnumProps, className) && !hasOwnProperty.call(nonEnumProps[className], prop)) {
- nonEnumProps[className][prop] = false;
+ if (hasOwnProperty.call(nonEnumProps, className) && !hasOwnProperty.call(nonEnumProps[className], key)) {
+ nonEnumProps[className][key] = false;
}
}
}
@@ -599,15 +590,16 @@
*
* The chainable wrapper functions are:
* `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
- * `compose`, `concat`, `countBy`, `createCallback`, `curry`, `debounce`,
- * `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`, `forEach`,
- * `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `functions`,
- * `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, `invoke`, `keys`,
- * `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`, `once`, `pairs`,
- * `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`, `range`, `reject`,
- * `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, `sortBy`, `splice`,
- * `tap`, `throttle`, `times`, `toArray`, `transform`, `union`, `uniq`, `unshift`,
- * `unzip`, `values`, `where`, `without`, `wrap`, and `zip`
+ * `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`,
+ * `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`,
+ * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`,
+ * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`,
+ * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`,
+ * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`,
+ * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`,
+ * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`,
+ * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`,
+ * and `zip`
*
* The non-chainable wrapper functions are:
* `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`,
@@ -687,8 +679,8 @@
props = [];
ctor.prototype = { 'valueOf': 1, 'y': 1 };
- for (var prop in new ctor) { props.push(prop); }
- for (prop in arguments) { }
+ for (var key in new ctor) { props.push(key); }
+ for (key in arguments) { }
/**
* Detect if an `arguments` object's [[Class]] is resolvable (all but Firefox < 4, IE < 9).
@@ -728,14 +720,6 @@
*/
support.enumPrototypes = propertyIsEnumerable.call(ctor, 'prototype');
- /**
- * Detect if `Function#bind` exists and is inferred to be fast (all but V8).
- *
- * @memberOf _.support
- * @type boolean
- */
- support.fastBind = nativeBind && !isV8;
-
/**
* Detect if functions can be decompiled by `Function#toString`
* (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps).
@@ -760,7 +744,7 @@
* @memberOf _.support
* @type boolean
*/
- support.nonEnumArgs = prop != 0;
+ support.nonEnumArgs = key != 0;
/**
* Detect if properties shadowing those on `Object.prototype` are non-enumerable.
@@ -984,19 +968,53 @@
/*--------------------------------------------------------------------------*/
+ /**
+ * The base implementation of `_.bind` that creates the bound function and
+ * sets its meta data.
+ *
+ * @private
+ * @param {Array} bindData The bind data array.
+ * @returns {Function} Returns the new bound function.
+ */
+ function baseBind(bindData) {
+ var func = bindData[0],
+ partialArgs = bindData[2],
+ thisArg = bindData[4];
+
+ function bound() {
+ // `Function#bind` spec
+ // http://es5.github.io/#x15.3.4.5
+ if (partialArgs) {
+ var args = partialArgs.slice();
+ push.apply(args, arguments);
+ }
+ // mimic the constructor's `return` behavior
+ // http://es5.github.io/#x13.2.2
+ if (this instanceof bound) {
+ // ensure `new bound` is an instance of `func`
+ var thisBinding = baseCreate(func.prototype),
+ result = func.apply(thisBinding, args || arguments);
+ return isObject(result) ? result : thisBinding;
+ }
+ return func.apply(thisArg, args || arguments);
+ }
+ setBindData(bound, bindData);
+ return bound;
+ }
+
/**
* The base implementation of `_.clone` without argument juggling or support
* for `thisArg` binding.
*
* @private
* @param {*} value The value to clone.
- * @param {boolean} [deep=false] Specify a deep clone.
+ * @param {boolean} [isDeep=false] Specify a deep clone.
* @param {Function} [callback] The function to customize cloning values.
* @param {Array} [stackA=[]] Tracks traversed source objects.
* @param {Array} [stackB=[]] Associates clones with source counterparts.
* @returns {*} Returns the cloned value.
*/
- function baseClone(value, deep, callback, stackA, stackB) {
+ function baseClone(value, isDeep, callback, stackA, stackB) {
if (callback) {
var result = callback(value);
if (typeof result != 'undefined') {
@@ -1029,7 +1047,7 @@
return value;
}
var isArr = isArray(value);
- if (deep) {
+ if (isDeep) {
// check for circular references and return corresponding clone
var initedStack = !stackA;
stackA || (stackA = getArray());
@@ -1056,7 +1074,7 @@
}
}
// exit for shallow clone
- if (!deep) {
+ if (!isDeep) {
return result;
}
// add the source value to the stack of traversed objects
@@ -1066,7 +1084,7 @@
// recursively populate clone (susceptible to call stack limits)
(isArr ? baseEach : forOwn)(value, function(objValue, key) {
- result[key] = baseClone(objValue, deep, callback, stackA, stackB);
+ result[key] = baseClone(objValue, isDeep, callback, stackA, stackB);
});
if (initedStack) {
@@ -1076,6 +1094,32 @@
return result;
}
+ /**
+ * The base implementation of `_.create` without support for assigning
+ * properties to the created object.
+ *
+ * @private
+ * @param {Object} prototype The object to inherit from.
+ * @returns {Object} Returns the new object.
+ */
+ function baseCreate(prototype, properties) {
+ return isObject(prototype) ? nativeCreate(prototype) : {};
+ }
+ // fallback for browsers without `Object.create`
+ if (!nativeCreate) {
+ baseCreate = (function() {
+ function Object() {}
+ return function(prototype) {
+ if (isObject(prototype)) {
+ Object.prototype = prototype;
+ var result = new Object;
+ Object.prototype = null;
+ }
+ return result || context.Object();
+ };
+ }());
+ }
+
/**
* The base implementation of `_.createCallback` without support for creating
* "_.pluck" or "_.where" style callbacks.
@@ -1090,24 +1134,30 @@
if (typeof func != 'function') {
return identity;
}
- // exit early if there is no `thisArg`
- if (typeof thisArg == 'undefined') {
+ // exit early for no `thisArg` or already bound by `Function#bind`
+ if (typeof thisArg == 'undefined' || !('prototype' in func)) {
return func;
}
- var bindData = func.__bindData__ || (support.funcNames && !func.name);
+ var bindData = func.__bindData__;
if (typeof bindData == 'undefined') {
- var source = reThis && fnToString.call(func);
- if (!support.funcNames && source && !reFuncName.test(source)) {
- bindData = true;
+ if (support.funcNames) {
+ bindData = !func.name;
}
- if (support.funcNames || !bindData) {
- // checks if `func` references the `this` keyword and stores the result
- bindData = !support.funcDecomp || reThis.test(source);
- setBindData(func, bindData);
+ bindData = bindData || !support.funcDecomp;
+ if (!bindData) {
+ var source = fnToString.call(func);
+ if (!support.funcNames) {
+ bindData = !reFuncName.test(source);
+ }
+ if (!bindData) {
+ // checks if `func` references the `this` keyword and stores the result
+ bindData = reThis.test(source);
+ setBindData(func, bindData);
+ }
}
}
// exit early if there are no `this` references or `func` is bound
- if (bindData !== true && (bindData && bindData[1] & 1)) {
+ if (bindData === false || (bindData !== true && bindData[1] & 1)) {
return func;
}
switch (argCount) {
@@ -1127,6 +1177,96 @@
return bind(func, thisArg);
}
+ /**
+ * The base implementation of `createWrapper` that creates the wrapper and
+ * sets its meta data.
+ *
+ * @private
+ * @param {Array} bindData The bind data array.
+ * @returns {Function} Returns the new function.
+ */
+ function baseCreateWrapper(bindData) {
+ var func = bindData[0],
+ bitmask = bindData[1],
+ partialArgs = bindData[2],
+ partialRightArgs = bindData[3],
+ thisArg = bindData[4],
+ arity = bindData[5];
+
+ var isBind = bitmask & 1,
+ isBindKey = bitmask & 2,
+ isCurry = bitmask & 4,
+ isCurryBound = bitmask & 8,
+ key = func;
+
+ function bound() {
+ var thisBinding = isBind ? thisArg : this;
+ if (partialArgs) {
+ var args = partialArgs.slice();
+ push.apply(args, arguments);
+ }
+ if (partialRightArgs || isCurry) {
+ args || (args = slice(arguments));
+ if (partialRightArgs) {
+ push.apply(args, partialRightArgs);
+ }
+ if (isCurry && args.length < arity) {
+ bitmask |= 16 & ~32;
+ return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]);
+ }
+ }
+ args || (args = arguments);
+ if (isBindKey) {
+ func = thisBinding[key];
+ }
+ if (this instanceof bound) {
+ thisBinding = baseCreate(func.prototype);
+ var result = func.apply(thisBinding, args);
+ return isObject(result) ? result : thisBinding;
+ }
+ return func.apply(thisBinding, args);
+ }
+ setBindData(bound, bindData);
+ return bound;
+ }
+
+ /**
+ * The base implementation of `_.difference` that accepts a single array
+ * of values to exclude.
+ *
+ * @private
+ * @param {Array} array The array to process.
+ * @param {Array} [values] The array of values to exclude.
+ * @returns {Array} Returns a new array of filtered values.
+ */
+ function baseDifference(array, values) {
+ var index = -1,
+ indexOf = getIndexOf(),
+ length = array ? array.length : 0,
+ isLarge = length >= largeArraySize && indexOf === baseIndexOf,
+ result = [];
+
+ if (isLarge) {
+ var cache = createCache(values);
+ if (cache) {
+ indexOf = cacheIndexOf;
+ values = cache;
+ } else {
+ isLarge = false;
+ }
+ }
+ while (++index < length) {
+ var value = array[index];
+ if (indexOf(values, value) < 0) {
+ result.push(value);
+ }
+ }
+ if (isLarge) {
+ releaseObject(values);
+ }
+ return result;
+ }
+
/**
* The base implementation of `_.flatten` without support for callback
* shorthands or `thisArg` binding.
@@ -1134,11 +1274,11 @@
* @private
* @param {Array} array The array to flatten.
* @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
- * @param {boolean} [isArgArrays=false] A flag to restrict flattening to arrays and `arguments` objects.
+ * @param {boolean} [isStrict=false] A flag to restrict flattening to arrays and `arguments` objects.
* @param {number} [fromIndex=0] The index to start from.
* @returns {Array} Returns a new flattened array.
*/
- function baseFlatten(array, isShallow, isArgArrays, fromIndex) {
+ function baseFlatten(array, isShallow, isStrict, fromIndex) {
var index = (fromIndex || 0) - 1,
length = array ? array.length : 0,
result = [];
@@ -1150,7 +1290,7 @@
&& (isArray(value) || isArguments(value))) {
// recursively flatten arrays (susceptible to call stack limits)
if (!isShallow) {
- value = baseFlatten(value, isShallow, isArgArrays);
+ value = baseFlatten(value, isShallow, isStrict);
}
var valIndex = -1,
valLength = value.length,
@@ -1160,7 +1300,7 @@
while (++valIndex < valLength) {
result[resIndex++] = value[valIndex];
}
- } else if (!isArgArrays) {
+ } else if (!isStrict) {
result.push(value);
}
}
@@ -1243,8 +1383,11 @@
var isArr = className == arrayClass;
if (!isArr) {
// unwrap any `lodash` wrapped values
- if (hasOwnProperty.call(a, '__wrapped__ ') || hasOwnProperty.call(b, '__wrapped__')) {
- return baseIsEqual(a.__wrapped__ || a, b.__wrapped__ || b, callback, isWhere, stackA, stackB);
+ var aWrapped = hasOwnProperty.call(a, '__wrapped__'),
+ bWrapped = hasOwnProperty.call(b, '__wrapped__');
+
+ if (aWrapped || bWrapped) {
+ return baseIsEqual(aWrapped ? a.__wrapped__ : a, bWrapped ? b.__wrapped__ : b, callback, isWhere, stackA, stackB);
}
// exit for functions and DOM nodes
if (className != objectClass || (!support.nodeClass && (isNode(a) || isNode(b)))) {
@@ -1255,10 +1398,10 @@
ctorB = !support.argsObject && isArguments(b) ? Object : b.constructor;
// non `Object` object instances with different constructors are not equal
- if (ctorA != ctorB && !(
- isFunction(ctorA) && ctorA instanceof ctorA &&
- isFunction(ctorB) && ctorB instanceof ctorB
- )) {
+ if (ctorA != ctorB &&
+ !(isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB) &&
+ ('constructor' in a && 'constructor' in b)
+ ) {
return false;
}
}
@@ -1401,6 +1544,19 @@
});
}
+ /**
+ * The base implementation of `_.random` without argument juggling or support
+ * for returning floating-point numbers.
+ *
+ * @private
+ * @param {number} min The minimum possible value.
+ * @param {number} max The maximum possible value.
+ * @returns {number} Returns a random number.
+ */
+ function baseRandom(min, max) {
+ return min + floor(nativeRandom() * (max - min + 1));
+ }
+
/**
* The base implementation of `_.uniq` without support for callback shorthands
* or `thisArg` binding.
@@ -1505,16 +1661,15 @@
* provided to the new function.
* @param {*} [thisArg] The `this` binding of `func`.
* @param {number} [arity] The arity of `func`.
- * @returns {Function} Returns the new bound function.
+ * @returns {Function} Returns the new function.
*/
- function createBound(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) {
+ function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) {
var isBind = bitmask & 1,
isBindKey = bitmask & 2,
isCurry = bitmask & 4,
isCurryBound = bitmask & 8,
isPartial = bitmask & 16,
- isPartialRight = bitmask & 32,
- key = func;
+ isPartialRight = bitmask & 32;
if (!isBindKey && !isFunction(func)) {
throw new TypeError;
@@ -1528,74 +1683,36 @@
isPartialRight = partialRightArgs = false;
}
var bindData = func && func.__bindData__;
- if (bindData) {
+ if (bindData && bindData !== true) {
+ bindData = bindData.slice();
+
+ // set `thisBinding` is not previously bound
if (isBind && !(bindData[1] & 1)) {
bindData[4] = thisArg;
}
+ // set if previously bound but not currently (subsequent curried functions)
if (!isBind && bindData[1] & 1) {
bitmask |= 8;
}
+ // set curried arity if not yet set
if (isCurry && !(bindData[1] & 4)) {
bindData[5] = arity;
}
+ // append partial left arguments
if (isPartial) {
push.apply(bindData[2] || (bindData[2] = []), partialArgs);
}
+ // append partial right arguments
if (isPartialRight) {
push.apply(bindData[3] || (bindData[3] = []), partialRightArgs);
}
+ // merge flags
bindData[1] |= bitmask;
- return createBound.apply(null, bindData);
- }
- // use `Function#bind` if it exists and is fast
- // (in V8 `Function#bind` is slower except when partially applied)
- if (isBind && !(isBindKey || isCurry || isPartialRight) &&
- (support.fastBind || (nativeBind && isPartial))) {
- if (isPartial) {
- var args = [thisArg];
- push.apply(args, partialArgs);
- }
- var bound = isPartial
- ? nativeBind.apply(func, args)
- : nativeBind.call(func, thisArg);
- }
- else {
- bound = function() {
- // `Function#bind` spec
- // http://es5.github.io/#x15.3.4.5
- var args = arguments,
- thisBinding = isBind ? thisArg : this;
-
- if (isCurry || isPartial || isPartialRight) {
- args = nativeSlice.call(args);
- if (isPartial) {
- unshift.apply(args, partialArgs);
- }
- if (isPartialRight) {
- push.apply(args, partialRightArgs);
- }
- if (isCurry && args.length < arity) {
- bitmask |= 16 & ~32;
- return createBound(func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity);
- }
- }
- if (isBindKey) {
- func = thisBinding[key];
- }
- if (this instanceof bound) {
- // ensure `new bound` is an instance of `func`
- thisBinding = createObject(func.prototype);
-
- // mimic the constructor's `return` behavior
- // http://es5.github.io/#x13.2.2
- var result = func.apply(thisBinding, args);
- return isObject(result) ? result : thisBinding;
- }
- return func.apply(thisBinding, args);
- };
+ return createWrapper.apply(null, bindData);
}
- setBindData(bound, nativeSlice.call(arguments));
- return bound;
+ // fast path for `_.bind`
+ var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper;
+ return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]);
}
/**
@@ -1646,28 +1763,6 @@
);
}
- /**
- * Creates a new object with the specified `prototype`.
- *
- * @private
- * @param {Object} prototype The prototype object.
- * @returns {Object} Returns the new object.
- */
- function createObject(prototype) {
- return isObject(prototype) ? nativeCreate(prototype) : {};
- }
- // fallback for browsers without `Object.create`
- if (!nativeCreate) {
- createObject = function(prototype) {
- if (isObject(prototype)) {
- noop.prototype = prototype;
- var result = new noop;
- noop.prototype = null;
- }
- return result || {};
- };
- }
-
/**
* Used by `escape` to convert characters to HTML entities.
*
@@ -1697,7 +1792,7 @@
*
* @private
* @param {Function} func The function to set data on.
- * @param {*} value The value to set.
+ * @param {Array} value The data array to set.
*/
var setBindData = !defineProperty ? noop : function(func, value) {
descriptor.value = value;
@@ -1781,7 +1876,7 @@
if (!support.argsClass) {
isArguments = function(value) {
return value && typeof value == 'object' && typeof value.length == 'number' &&
- hasOwnProperty.call(value, 'callee') || false;
+ hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee') || false;
};
}
@@ -1937,16 +2032,16 @@
* @returns {Object} Returns the destination object.
* @example
*
- * _.assign({ 'name': 'moe' }, { 'age': 40 });
- * // => { 'name': 'moe', 'age': 40 }
+ * _.assign({ 'name': 'fred' }, { 'employer': 'slate' });
+ * // => { 'name': 'fred', 'employer': 'slate' }
*
* var defaults = _.partialRight(_.assign, function(a, b) {
* return typeof a == 'undefined' ? b : a;
* });
*
- * var food = { 'name': 'apple' };
- * defaults(food, { 'name': 'banana', 'type': 'fruit' });
- * // => { 'name': 'apple', 'type': 'fruit' }
+ * var object = { 'name': 'barney' };
+ * defaults(object, { 'name': 'fred', 'employer': 'slate' });
+ * // => { 'name': 'barney', 'employer': 'slate' }
*/
var assign = createIterator(defaultsIteratorOptions, {
'top':
@@ -1962,7 +2057,7 @@
});
/**
- * Creates a clone of `value`. If `deep` is `true` nested objects will also
+ * Creates a clone of `value`. If `isDeep` is `true` nested objects will also
* be cloned, otherwise they will be assigned by reference. If a callback
* is provided it will be executed to produce the cloned values. If the
* callback returns `undefined` cloning will be handled by the method instead.
@@ -1972,23 +2067,23 @@
* @memberOf _
* @category Objects
* @param {*} value The value to clone.
- * @param {boolean} [deep=false] Specify a deep clone.
+ * @param {boolean} [isDeep=false] Specify a deep clone.
* @param {Function} [callback] The function to customize cloning values.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {*} Returns the cloned value.
* @example
*
- * var stooges = [
- * { 'name': 'moe', 'age': 40 },
- * { 'name': 'larry', 'age': 50 }
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
* ];
*
- * var shallow = _.clone(stooges);
- * shallow[0] === stooges[0];
+ * var shallow = _.clone(characters);
+ * shallow[0] === characters[0];
* // => true
*
- * var deep = _.clone(stooges, true);
- * deep[0] === stooges[0];
+ * var deep = _.clone(characters, true);
+ * deep[0] === characters[0];
* // => false
*
* _.mixin({
@@ -2001,15 +2096,15 @@
* clone.childNodes.length;
* // => 0
*/
- function clone(value, deep, callback, thisArg) {
+ function clone(value, isDeep, callback, thisArg) {
// allows working with "Collections" methods without using their `index`
- // and `collection` arguments for `deep` and `callback`
- if (typeof deep != 'boolean' && deep != null) {
+ // and `collection` arguments for `isDeep` and `callback`
+ if (typeof isDeep != 'boolean' && isDeep != null) {
thisArg = callback;
- callback = deep;
- deep = false;
+ callback = isDeep;
+ isDeep = false;
}
- return baseClone(value, deep, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
+ return baseClone(value, isDeep, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
}
/**
@@ -2032,13 +2127,13 @@
* @returns {*} Returns the deep cloned value.
* @example
*
- * var stooges = [
- * { 'name': 'moe', 'age': 40 },
- * { 'name': 'larry', 'age': 50 }
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
* ];
*
- * var deep = _.cloneDeep(stooges);
- * deep[0] === stooges[0];
+ * var deep = _.cloneDeep(characters);
+ * deep[0] === characters[0];
* // => false
*
* var view = {
@@ -2057,6 +2152,42 @@
return baseClone(value, true, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
}
+ /**
+ * Creates an object that inherits from the given `prototype` object. If a
+ * `properties` object is provided its own enumerable properties are assigned
+ * to the created object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} prototype The object to inherit from.
+ * @param {Object} [properties] The properties to assign to the object.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * function Circle() {
+ * Shape.call(this);
+ * }
+ *
+ * Circle.prototype = _.create(Shape.prototype, { 'constructor': Circle });
+ *
+ * var circle = new Circle;
+ * circle instanceof Circle;
+ * // => true
+ *
+ * circle instanceof Shape;
+ * // => true
+ */
+ function create(prototype, properties) {
+ var result = baseCreate(prototype);
+ return properties ? assign(result, properties) : result;
+ }
+
/**
* Assigns own enumerable properties of source object(s) to the destination
* object for all destination properties that resolve to `undefined`. Once a
@@ -2073,9 +2204,9 @@
* @returns {Object} Returns the destination object.
* @example
*
- * var food = { 'name': 'apple' };
- * _.defaults(food, { 'name': 'banana', 'type': 'fruit' });
- * // => { 'name': 'apple', 'type': 'fruit' }
+ * var object = { 'name': 'barney' };
+ * _.defaults(object, { 'name': 'fred', 'employer': 'slate' });
+ * // => { 'name': 'barney', 'employer': 'slate' }
*/
var defaults = createIterator(defaultsIteratorOptions);
@@ -2083,6 +2214,13 @@
* This method is like `_.findIndex` except that it returns the key of the
* first element that passes the callback check, instead of the element itself.
*
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
* @static
* @memberOf _
* @category Objects
@@ -2094,10 +2232,24 @@
* @returns {string|undefined} Returns the key of the found element, else `undefined`.
* @example
*
- * _.findKey({ 'a': 1, 'b': 2, 'c': 3, 'd': 4 }, function(num) {
- * return num % 2 == 0;
+ * var characters = {
+ * 'barney': { 'age': 36, 'blocked': false },
+ * 'fred': { 'age': 40, 'blocked': true },
+ * 'pebbles': { 'age': 1, 'blocked': false }
+ * };
+ *
+ * _.findKey(characters, function(chr) {
+ * return chr.age < 40;
* });
- * // => 'b' (property order is not guaranteed across environments)
+ * // => 'barney' (property order is not guaranteed across environments)
+ *
+ * // using "_.where" callback shorthand
+ * _.findKey(characters, { 'age': 1 });
+ * // => 'pebbles'
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findKey(characters, 'blocked');
+ * // => 'fred'
*/
function findKey(object, callback, thisArg) {
var result;
@@ -2115,6 +2267,13 @@
* This method is like `_.findKey` except that it iterates over elements
* of a `collection` in the opposite order.
*
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
* @static
* @memberOf _
* @category Objects
@@ -2126,10 +2285,24 @@
* @returns {string|undefined} Returns the key of the found element, else `undefined`.
* @example
*
- * _.findLastKey({ 'a': 1, 'b': 2, 'c': 3, 'd': 4 }, function(num) {
- * return num % 2 == 1;
+ * var characters = {
+ * 'barney': { 'age': 36, 'blocked': true },
+ * 'fred': { 'age': 40, 'blocked': false },
+ * 'pebbles': { 'age': 1, 'blocked': true }
+ * };
+ *
+ * _.findLastKey(characters, function(chr) {
+ * return chr.age < 40;
* });
- * // => returns `c`, assuming `_.findKey` returns `a`
+ * // => returns `pebbles`, assuming `_.findKey` returns `barney`
+ *
+ * // using "_.where" callback shorthand
+ * _.findLastKey(characters, { 'age': 40 });
+ * // => 'fred'
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findLastKey(characters, 'blocked');
+ * // => 'pebbles'
*/
function findLastKey(object, callback, thisArg) {
var result;
@@ -2159,18 +2332,20 @@
* @returns {Object} Returns `object`.
* @example
*
- * function Dog(name) {
- * this.name = name;
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
* }
*
- * Dog.prototype.bark = function() {
- * console.log('Woof, woof!');
+ * Shape.prototype.move = function(x, y) {
+ * this.x += x;
+ * this.y += y;
* };
*
- * _.forIn(new Dog('Dagny'), function(value, key) {
+ * _.forIn(new Shape, function(value, key) {
* console.log(key);
* });
- * // => logs 'bark' and 'name' (property order is not guaranteed across environments)
+ * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments)
*/
var forIn = createIterator(eachIteratorOptions, forOwnIteratorOptions, {
'useHas': false
@@ -2189,18 +2364,20 @@
* @returns {Object} Returns `object`.
* @example
*
- * function Dog(name) {
- * this.name = name;
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
* }
*
- * Dog.prototype.bark = function() {
- * console.log('Woof, woof!');
+ * Shape.prototype.move = function(x, y) {
+ * this.x += x;
+ * this.y += y;
* };
*
- * _.forInRight(new Dog('Dagny'), function(value, key) {
+ * _.forInRight(new Shape, function(value, key) {
* console.log(key);
* });
- * // => logs 'name' and 'bark' assuming `_.forIn ` logs 'bark' and 'name'
+ * // => logs 'move', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'move'
*/
function forInRight(object, callback, thisArg) {
var pairs = [];
@@ -2328,8 +2505,8 @@
* @returns {Object} Returns the created inverted object.
* @example
*
- * _.invert({ 'first': 'moe', 'second': 'larry' });
- * // => { 'moe': 'first', 'larry': 'second' }
+ * _.invert({ 'first': 'fred', 'second': 'barney' });
+ * // => { 'fred': 'first', 'barney': 'second' }
*/
function invert(object) {
var index = -1,
@@ -2358,7 +2535,8 @@
* // => false
*/
function isBoolean(value) {
- return value === true || value === false || toString.call(value) == boolClass;
+ return value === true || value === false ||
+ value && typeof value == 'object' && toString.call(value) == boolClass || false;
}
/**
@@ -2375,7 +2553,7 @@
* // => true
*/
function isDate(value) {
- return value ? (typeof value == 'object' && toString.call(value) == dateClass) : false;
+ return value && typeof value == 'object' && toString.call(value) == dateClass || false;
}
/**
@@ -2392,7 +2570,7 @@
* // => true
*/
function isElement(value) {
- return value ? value.nodeType === 1 : false;
+ return value && value.nodeType === 1 || false;
}
/**
@@ -2452,13 +2630,13 @@
* @returns {boolean} Returns `true` if the values are equivalent, else `false`.
* @example
*
- * var moe = { 'name': 'moe', 'age': 40 };
- * var copy = { 'name': 'moe', 'age': 40 };
+ * var object = { 'name': 'fred' };
+ * var copy = { 'name': 'fred' };
*
- * moe == copy;
+ * object == copy;
* // => false
*
- * _.isEqual(moe, copy);
+ * _.isEqual(object, copy);
* // => true
*
* var words = ['hello', 'goodbye'];
@@ -2627,7 +2805,8 @@
* // => true
*/
function isNumber(value) {
- return typeof value == 'number' || toString.call(value) == numberClass;
+ return typeof value == 'number' ||
+ value && typeof value == 'object' && toString.call(value) == numberClass || false;
}
/**
@@ -2640,18 +2819,18 @@
* @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
* @example
*
- * function Stooge(name, age) {
- * this.name = name;
- * this.age = age;
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
* }
*
- * _.isPlainObject(new Stooge('moe', 40));
+ * _.isPlainObject(new Shape);
* // => false
*
* _.isPlainObject([1, 2, 3]);
* // => false
*
- * _.isPlainObject({ 'name': 'moe', 'age': 40 });
+ * _.isPlainObject({ 'x': 0, 'y': 0 });
* // => true
*/
var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
@@ -2676,11 +2855,11 @@
* @returns {boolean} Returns `true` if the `value` is a regular expression, else `false`.
* @example
*
- * _.isRegExp(/moe/);
+ * _.isRegExp(/fred/);
* // => true
*/
function isRegExp(value) {
- return (value && objectTypes[typeof value]) ? toString.call(value) == regexpClass : false;
+ return value && objectTypes[typeof value] && toString.call(value) == regexpClass || false;
}
/**
@@ -2693,11 +2872,12 @@
* @returns {boolean} Returns `true` if the `value` is a string, else `false`.
* @example
*
- * _.isString('moe');
+ * _.isString('fred');
* // => true
*/
function isString(value) {
- return typeof value == 'string' || toString.call(value) == stringClass;
+ return typeof value == 'string' ||
+ value && typeof value == 'object' && toString.call(value) == stringClass || false;
}
/**
@@ -2737,21 +2917,21 @@
* @example
*
* var names = {
- * 'stooges': [
- * { 'name': 'moe' },
- * { 'name': 'larry' }
+ * 'characters': [
+ * { 'name': 'barney' },
+ * { 'name': 'fred' }
* ]
* };
*
* var ages = {
- * 'stooges': [
- * { 'age': 40 },
- * { 'age': 50 }
+ * 'characters': [
+ * { 'age': 36 },
+ * { 'age': 40 }
* ]
* };
*
* _.merge(names, ages);
- * // => { 'stooges': [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }] }
+ * // => { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] }
*
* var food = {
* 'fruits': ['apple'],
@@ -2775,6 +2955,7 @@
if (!isObject(object)) {
return object;
}
+
// allows working with `_.reduce` and `_.reduceRight` without using
// their `index` and `collection` arguments
if (typeof args[2] != 'number') {
@@ -2785,7 +2966,7 @@
} else if (length > 2 && typeof args[length - 1] == 'function') {
callback = args[--length];
}
- var sources = nativeSlice.call(arguments, 1, length),
+ var sources = slice(arguments, 1, length),
index = -1,
stackA = getArray(),
stackB = getArray();
@@ -2816,32 +2997,38 @@
* @returns {Object} Returns an object without the omitted properties.
* @example
*
- * _.omit({ 'name': 'moe', 'age': 40 }, 'age');
- * // => { 'name': 'moe' }
+ * _.omit({ 'name': 'fred', 'age': 40 }, 'age');
+ * // => { 'name': 'fred' }
*
- * _.omit({ 'name': 'moe', 'age': 40 }, function(value) {
+ * _.omit({ 'name': 'fred', 'age': 40 }, function(value) {
* return typeof value == 'number';
* });
- * // => { 'name': 'moe' }
+ * // => { 'name': 'fred' }
*/
function omit(object, callback, thisArg) {
- var indexOf = getIndexOf(),
- isFunc = typeof callback == 'function',
- result = {};
+ var result = {};
+ if (typeof callback != 'function') {
+ var props = [];
+ forIn(object, function(value, key) {
+ props.push(key);
+ });
+ props = baseDifference(props, baseFlatten(arguments, true, false, 1));
- if (isFunc) {
- callback = lodash.createCallback(callback, thisArg, 3);
+ var index = -1,
+ length = props.length;
+
+ while (++index < length) {
+ var key = props[index];
+ result[key] = object[key];
+ }
} else {
- var props = baseFlatten(arguments, true, false, 1);
+ callback = lodash.createCallback(callback, thisArg, 3);
+ forIn(object, function(value, key, object) {
+ if (!callback(value, key, object)) {
+ result[key] = value;
+ }
+ });
}
- forIn(object, function(value, key, object) {
- if (isFunc
- ? !callback(value, key, object)
- : indexOf(props, key) < 0
- ) {
- result[key] = value;
- }
- });
return result;
}
@@ -2856,8 +3043,8 @@
* @returns {Array} Returns new array of key-value pairs.
* @example
*
- * _.pairs({ 'moe': 30, 'larry': 40 });
- * // => [['moe', 30], ['larry', 40]] (property order is not guaranteed across environments)
+ * _.pairs({ 'barney': 36, 'fred': 40 });
+ * // => [['barney', 36], ['fred', 40]] (property order is not guaranteed across environments)
*/
function pairs(object) {
var index = -1,
@@ -2891,13 +3078,13 @@
* @returns {Object} Returns an object composed of the picked properties.
* @example
*
- * _.pick({ 'name': 'moe', '_userid': 'moe1' }, 'name');
- * // => { 'name': 'moe' }
+ * _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name');
+ * // => { 'name': 'fred' }
*
- * _.pick({ 'name': 'moe', '_userid': 'moe1' }, function(value, key) {
+ * _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) {
* return key.charAt(0) != '_';
* });
- * // => { 'name': 'moe' }
+ * // => { 'name': 'fred' }
*/
function pick(object, callback, thisArg) {
var result = {};
@@ -2934,7 +3121,7 @@
* @static
* @memberOf _
* @category Objects
- * @param {Array|Object} collection The collection to iterate over.
+ * @param {Array|Object} object The object to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {*} [accumulator] The custom accumulator value.
* @param {*} [thisArg] The `this` binding of `callback`.
@@ -2956,8 +3143,6 @@
*/
function transform(object, callback, accumulator, thisArg) {
var isArr = isArray(object);
- callback = baseCreateCallback(callback, thisArg, 4);
-
if (accumulator == null) {
if (isArr) {
accumulator = [];
@@ -2965,12 +3150,15 @@
var ctor = object && object.constructor,
proto = ctor && ctor.prototype;
- accumulator = createObject(proto);
+ accumulator = baseCreate(proto);
}
}
- (isArr ? baseEach : forOwn)(object, function(value, index, object) {
- return callback(accumulator, value, index, object);
- });
+ if (callback) {
+ callback = lodash.createCallback(callback, thisArg, 4);
+ (isArr ? baseEach : forOwn)(object, function(value, index, object) {
+ return callback(accumulator, value, index, object);
+ });
+ }
return accumulator;
}
@@ -3019,8 +3207,8 @@
* _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]);
* // => ['a', 'c', 'e']
*
- * _.at(['moe', 'larry', 'curly'], 0, 2);
- * // => ['moe', 'curly']
+ * _.at(['fred', 'barney', 'pebbles'], 0, 2);
+ * // => ['fred', 'pebbles']
*/
function at(collection) {
var args = arguments,
@@ -3059,10 +3247,10 @@
* _.contains([1, 2, 3], 1, 2);
* // => false
*
- * _.contains({ 'name': 'moe', 'age': 40 }, 'moe');
+ * _.contains({ 'name': 'fred', 'age': 40 }, 'fred');
* // => true
*
- * _.contains('curly', 'ur');
+ * _.contains('pebbles', 'eb');
* // => true
*/
function contains(collection, target, fromIndex) {
@@ -3149,20 +3337,20 @@
* else `false`.
* @example
*
- * _.every([true, 1, null, 'yes'], Boolean);
+ * _.every([true, 1, null, 'yes']);
* // => false
*
- * var stooges = [
- * { 'name': 'moe', 'age': 40 },
- * { 'name': 'larry', 'age': 50 }
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
* ];
*
* // using "_.pluck" callback shorthand
- * _.every(stooges, 'age');
+ * _.every(characters, 'age');
* // => true
*
* // using "_.where" callback shorthand
- * _.every(stooges, { 'age': 50 });
+ * _.every(characters, { 'age': 36 });
* // => false
*/
function every(collection, callback, thisArg) {
@@ -3213,18 +3401,18 @@
* var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
* // => [2, 4, 6]
*
- * var food = [
- * { 'name': 'apple', 'organic': false, 'type': 'fruit' },
- * { 'name': 'carrot', 'organic': true, 'type': 'vegetable' }
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true }
* ];
*
* // using "_.pluck" callback shorthand
- * _.filter(food, 'organic');
- * // => [{ 'name': 'carrot', 'organic': true, 'type': 'vegetable' }]
+ * _.filter(characters, 'blocked');
+ * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
*
* // using "_.where" callback shorthand
- * _.filter(food, { 'type': 'fruit' });
- * // => [{ 'name': 'apple', 'organic': false, 'type': 'fruit' }]
+ * _.filter(characters, { 'age': 36 });
+ * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
*/
function filter(collection, callback, thisArg) {
var result = [];
@@ -3274,24 +3462,24 @@
* @returns {*} Returns the found element, else `undefined`.
* @example
*
- * _.find([1, 2, 3, 4], function(num) {
- * return num % 2 == 0;
- * });
- * // => 2
- *
- * var food = [
- * { 'name': 'apple', 'organic': false, 'type': 'fruit' },
- * { 'name': 'banana', 'organic': true, 'type': 'fruit' },
- * { 'name': 'beet', 'organic': false, 'type': 'vegetable' }
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true },
+ * { 'name': 'pebbles', 'age': 1, 'blocked': false }
* ];
*
+ * _.find(characters, function(chr) {
+ * return chr.age < 40;
+ * });
+ * // => { 'name': 'barney', 'age': 36, 'blocked': false }
+ *
* // using "_.where" callback shorthand
- * _.find(food, { 'type': 'vegetable' });
- * // => { 'name': 'beet', 'organic': false, 'type': 'vegetable' }
+ * _.find(characters, { 'age': 1 });
+ * // => { 'name': 'pebbles', 'age': 1, 'blocked': false }
*
* // using "_.pluck" callback shorthand
- * _.find(food, 'organic');
- * // => { 'name': 'banana', 'organic': true, 'type': 'fruit' }
+ * _.find(characters, 'blocked');
+ * // => { 'name': 'fred', 'age': 40, 'blocked': true }
*/
function find(collection, callback, thisArg) {
callback = lodash.createCallback(callback, thisArg, 3);
@@ -3356,6 +3544,10 @@
* (value, index|key, collection). Callbacks may exit iteration early by
* explicitly returning `false`.
*
+ * Note: As with other "Collections" methods, objects with a `length` property
+ * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
+ * may be used for object iteration.
+ *
* @static
* @memberOf _
* @alias each
@@ -3506,7 +3698,7 @@
* _.indexBy(keys, function(key) { return String.fromCharCode(key.code); });
* // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
*
- * _.indexBy(stooges, function(key) { this.fromCharCode(key.code); }, String);
+ * _.indexBy(characters, function(key) { this.fromCharCode(key.code); }, String);
* // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
*/
var indexBy = createAggregator(function(result, value, key) {
@@ -3536,7 +3728,7 @@
* // => [['1', '2', '3'], ['4', '5', '6']]
*/
function invoke(collection, methodName) {
- var args = nativeSlice.call(arguments, 2),
+ var args = slice(arguments, 2),
index = -1,
isFunc = typeof methodName == 'function',
length = collection ? collection.length : 0,
@@ -3578,14 +3770,14 @@
* _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
* // => [3, 6, 9] (property order is not guaranteed across environments)
*
- * var stooges = [
- * { 'name': 'moe', 'age': 40 },
- * { 'name': 'larry', 'age': 50 }
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
* ];
*
* // using "_.pluck" callback shorthand
- * _.map(stooges, 'name');
- * // => ['moe', 'larry']
+ * _.map(characters, 'name');
+ * // => ['barney', 'fred']
*/
function map(collection, callback, thisArg) {
var index = -1,
@@ -3633,23 +3825,28 @@
* _.max([4, 2, 8, 6]);
* // => 8
*
- * var stooges = [
- * { 'name': 'moe', 'age': 40 },
- * { 'name': 'larry', 'age': 50 }
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
* ];
*
- * _.max(stooges, function(stooge) { return stooge.age; });
- * // => { 'name': 'larry', 'age': 50 };
+ * _.max(characters, function(chr) { return chr.age; });
+ * // => { 'name': 'fred', 'age': 40 };
*
* // using "_.pluck" callback shorthand
- * _.max(stooges, 'age');
- * // => { 'name': 'larry', 'age': 50 };
+ * _.max(characters, 'age');
+ * // => { 'name': 'fred', 'age': 40 };
*/
function max(collection, callback, thisArg) {
var computed = -Infinity,
result = computed;
- if (!callback && isArray(collection)) {
+ // allows working with functions like `_.map` without using
+ // their `index` argument as a callback
+ if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
+ callback = null;
+ }
+ if (callback == null && isArray(collection)) {
var index = -1,
length = collection.length;
@@ -3660,7 +3857,7 @@
}
}
} else {
- callback = (!callback && isString(collection))
+ callback = (callback == null && isString(collection))
? charAtCallback
: lodash.createCallback(callback, thisArg, 3);
@@ -3703,23 +3900,28 @@
* _.min([4, 2, 8, 6]);
* // => 2
*
- * var stooges = [
- * { 'name': 'moe', 'age': 40 },
- * { 'name': 'larry', 'age': 50 }
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
* ];
*
- * _.min(stooges, function(stooge) { return stooge.age; });
- * // => { 'name': 'moe', 'age': 40 };
+ * _.min(characters, function(chr) { return chr.age; });
+ * // => { 'name': 'barney', 'age': 36 };
*
* // using "_.pluck" callback shorthand
- * _.min(stooges, 'age');
- * // => { 'name': 'moe', 'age': 40 };
+ * _.min(characters, 'age');
+ * // => { 'name': 'barney', 'age': 36 };
*/
function min(collection, callback, thisArg) {
var computed = Infinity,
result = computed;
- if (!callback && isArray(collection)) {
+ // allows working with functions like `_.map` without using
+ // their `index` argument as a callback
+ if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
+ callback = null;
+ }
+ if (callback == null && isArray(collection)) {
var index = -1,
length = collection.length;
@@ -3730,7 +3932,7 @@
}
}
} else {
- callback = (!callback && isString(collection))
+ callback = (callback == null && isString(collection))
? charAtCallback
: lodash.createCallback(callback, thisArg, 3);
@@ -3746,7 +3948,7 @@
}
/**
- * Retrieves the value of a specified property from all elements in the `collection`.
+ * Retrieves the value of a specified property from all elements in the collection.
*
* @static
* @memberOf _
@@ -3757,13 +3959,13 @@
* @returns {Array} Returns a new array of property values.
* @example
*
- * var stooges = [
- * { 'name': 'moe', 'age': 40 },
- * { 'name': 'larry', 'age': 50 }
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
* ];
*
- * _.pluck(stooges, 'name');
- * // => ['moe', 'larry']
+ * _.pluck(characters, 'name');
+ * // => ['barney', 'fred']
*/
var pluck = map;
@@ -3799,7 +4001,7 @@
*/
function reduce(collection, callback, accumulator, thisArg) {
var noaccum = arguments.length < 3;
- callback = baseCreateCallback(callback, thisArg, 4);
+ callback = lodash.createCallback(callback, thisArg, 4);
if (isArray(collection)) {
var index = -1,
@@ -3842,7 +4044,7 @@
*/
function reduceRight(collection, callback, accumulator, thisArg) {
var noaccum = arguments.length < 3;
- callback = baseCreateCallback(callback, thisArg, 4);
+ callback = lodash.createCallback(callback, thisArg, 4);
forEachRight(collection, function(value, index, collection) {
accumulator = noaccum
? (noaccum = false, value)
@@ -3876,18 +4078,18 @@
* var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
* // => [1, 3, 5]
*
- * var food = [
- * { 'name': 'apple', 'organic': false, 'type': 'fruit' },
- * { 'name': 'carrot', 'organic': true, 'type': 'vegetable' }
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true }
* ];
*
* // using "_.pluck" callback shorthand
- * _.reject(food, 'organic');
- * // => [{ 'name': 'apple', 'organic': false, 'type': 'fruit' }]
+ * _.reject(characters, 'blocked');
+ * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
*
* // using "_.where" callback shorthand
- * _.reject(food, { 'type': 'fruit' });
- * // => [{ 'name': 'carrot', 'organic': true, 'type': 'vegetable' }]
+ * _.reject(characters, { 'age': 36 });
+ * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
*/
function reject(collection, callback, thisArg) {
callback = lodash.createCallback(callback, thisArg, 3);
@@ -3904,8 +4106,8 @@
* @category Collections
* @param {Array|Object|string} collection The collection to sample.
* @param {number} [n] The number of elements to sample.
- * @param- {Object} [guard] Allows working with functions, like `_.map`,
- * without using their `key` and `object` arguments as sources.
+ * @param- {Object} [guard] Allows working with functions like `_.map`
+ * without using their `index` arguments as `n`.
* @returns {Array} Returns the random sample(s) of `collection`.
* @example
*
@@ -3916,14 +4118,13 @@
* // => [3, 1]
*/
function sample(collection, n, guard) {
- var length = collection ? collection.length : 0;
- if (typeof length != 'number') {
+ if (collection && typeof collection.length != 'number') {
collection = values(collection);
} else if (support.unindexedChars && isString(collection)) {
collection = collection.split('');
}
if (n == null || guard) {
- return collection ? collection[random(length - 1)] : undefined;
+ return collection ? collection[baseRandom(0, collection.length - 1)] : undefined;
}
var result = shuffle(collection);
result.length = nativeMin(nativeMax(0, n), result.length);
@@ -3950,7 +4151,7 @@
result = Array(typeof length == 'number' ? length : 0);
forEach(collection, function(value) {
- var rand = random(++index);
+ var rand = baseRandom(0, ++index);
result[index] = result[rand];
result[rand] = value;
});
@@ -3974,7 +4175,7 @@
* _.size({ 'one': 1, 'two': 2, 'three': 3 });
* // => 3
*
- * _.size('curly');
+ * _.size('pebbles');
* // => 5
*/
function size(collection) {
@@ -4011,17 +4212,17 @@
* _.some([null, 0, 'yes', false], Boolean);
* // => true
*
- * var food = [
- * { 'name': 'apple', 'organic': false, 'type': 'fruit' },
- * { 'name': 'carrot', 'organic': true, 'type': 'vegetable' }
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true }
* ];
*
* // using "_.pluck" callback shorthand
- * _.some(food, 'organic');
+ * _.some(characters, 'blocked');
* // => true
*
* // using "_.where" callback shorthand
- * _.some(food, { 'type': 'meat' });
+ * _.some(characters, { 'age': 1 });
* // => false
*/
function some(collection, callback, thisArg) {
@@ -4139,16 +4340,16 @@
* @returns {Array} Returns a new array of elements that have the given properties.
* @example
*
- * var stooges = [
- * { 'name': 'curly', 'age': 30, 'quotes': ['Oh, a wise guy, eh?', 'Poifect!'] },
- * { 'name': 'moe', 'age': 40, 'quotes': ['Spread out!', 'You knucklehead!'] }
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] },
+ * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }
* ];
*
- * _.where(stooges, { 'age': 40 });
- * // => [{ 'name': 'moe', 'age': 40, 'quotes': ['Spread out!', 'You knucklehead!'] }]
+ * _.where(characters, { 'age': 36 });
+ * // => [{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }]
*
- * _.where(stooges, { 'quotes': ['Poifect!'] });
- * // => [{ 'name': 'curly', 'age': 30, 'quotes': ['Oh, a wise guy, eh?', 'Poifect!'] }]
+ * _.where(characters, { 'pets': ['dino'] });
+ * // => [{ 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }]
*/
var where = filter;
@@ -4190,7 +4391,7 @@
* @memberOf _
* @category Arrays
* @param {Array} array The array to process.
- * @param {...Array} [array] The arrays of values to exclude.
+ * @param {...Array} [values] The arrays of values to exclude.
* @returns {Array} Returns a new array of filtered values.
* @example
*
@@ -4198,39 +4399,20 @@
* // => [1, 3, 4]
*/
function difference(array) {
- var index = -1,
- indexOf = getIndexOf(),
- length = array ? array.length : 0,
- seen = baseFlatten(arguments, true, true, 1),
- result = [];
-
- var isLarge = length >= largeArraySize && indexOf === baseIndexOf;
-
- if (isLarge) {
- var cache = createCache(seen);
- if (cache) {
- indexOf = cacheIndexOf;
- seen = cache;
- } else {
- isLarge = false;
- }
- }
- while (++index < length) {
- var value = array[index];
- if (indexOf(seen, value) < 0) {
- result.push(value);
- }
- }
- if (isLarge) {
- releaseObject(seen);
- }
- return result;
+ return baseDifference(array, baseFlatten(arguments, true, true, 1));
}
/**
* This method is like `_.find` except that it returns the index of the first
* element that passes the callback check, instead of the element itself.
*
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
* @static
* @memberOf _
* @category Arrays
@@ -4242,9 +4424,23 @@
* @returns {number} Returns the index of the found element, else `-1`.
* @example
*
- * _.findIndex(['apple', 'banana', 'beet'], function(food) {
- * return /^b/.test(food);
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': false },
+ * { 'name': 'fred', 'age': 40, 'blocked': true },
+ * { 'name': 'pebbles', 'age': 1, 'blocked': false }
+ * ];
+ *
+ * _.findIndex(characters, function(chr) {
+ * return chr.age < 20;
* });
+ * // => 2
+ *
+ * // using "_.where" callback shorthand
+ * _.findIndex(characters, { 'age': 36 });
+ * // => 0
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findIndex(characters, 'blocked');
* // => 1
*/
function findIndex(array, callback, thisArg) {
@@ -4264,6 +4460,13 @@
* This method is like `_.findIndex` except that it iterates over elements
* of a `collection` from right to left.
*
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
* @static
* @memberOf _
* @category Arrays
@@ -4275,9 +4478,23 @@
* @returns {number} Returns the index of the found element, else `-1`.
* @example
*
- * _.findLastIndex(['apple', 'banana', 'beet'], function(food) {
- * return /^b/.test(food);
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'blocked': true },
+ * { 'name': 'fred', 'age': 40, 'blocked': false },
+ * { 'name': 'pebbles', 'age': 1, 'blocked': true }
+ * ];
+ *
+ * _.findLastIndex(characters, function(chr) {
+ * return chr.age > 30;
* });
+ * // => 1
+ *
+ * // using "_.where" callback shorthand
+ * _.findLastIndex(characters, { 'age': 36 });
+ * // => 0
+ *
+ * // using "_.pluck" callback shorthand
+ * _.findLastIndex(characters, 'blocked');
* // => 2
*/
function findLastIndex(array, callback, thisArg) {
@@ -4328,24 +4545,19 @@
* });
* // => [1, 2]
*
- * var food = [
- * { 'name': 'banana', 'organic': true },
- * { 'name': 'beet', 'organic': false },
+ * var characters = [
+ * { 'name': 'barney', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
* ];
*
* // using "_.pluck" callback shorthand
- * _.first(food, 'organic');
- * // => [{ 'name': 'banana', 'organic': true }]
- *
- * var food = [
- * { 'name': 'apple', 'type': 'fruit' },
- * { 'name': 'banana', 'type': 'fruit' },
- * { 'name': 'beet', 'type': 'vegetable' }
- * ];
+ * _.first(characters, 'blocked');
+ * // => [{ 'name': 'barney', 'blocked': true, 'employer': 'slate' }]
*
* // using "_.where" callback shorthand
- * _.first(food, { 'type': 'fruit' });
- * // => [{ 'name': 'apple', 'type': 'fruit' }, { 'name': 'banana', 'type': 'fruit' }]
+ * _.pluck(_.first(characters, { 'employer': 'slate' }), 'name');
+ * // => ['barney', 'fred']
*/
function first(array, callback, thisArg) {
var n = 0,
@@ -4398,20 +4610,20 @@
* _.flatten([1, [2], [3, [[4]]]], true);
* // => [1, 2, 3, [[4]]];
*
- * var stooges = [
- * { 'name': 'curly', 'quotes': ['Oh, a wise guy, eh?', 'Poifect!'] },
- * { 'name': 'moe', 'quotes': ['Spread out!', 'You knucklehead!'] }
+ * var characters = [
+ * { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] },
+ * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }
* ];
*
* // using "_.pluck" callback shorthand
- * _.flatten(stooges, 'quotes');
- * // => ['Oh, a wise guy, eh?', 'Poifect!', 'Spread out!', 'You knucklehead!']
+ * _.flatten(characters, 'pets');
+ * // => ['hoppy', 'baby puss', 'dino']
*/
function flatten(array, isShallow, callback, thisArg) {
// juggle arguments
if (typeof isShallow != 'boolean' && isShallow != null) {
thisArg = callback;
- callback = !(thisArg && thisArg[isShallow] === array) ? isShallow : null;
+ callback = (typeof isShallow != 'function' && thisArg && thisArg[isShallow] === array) ? null : isShallow;
isShallow = false;
}
if (callback != null) {
@@ -4491,24 +4703,19 @@
* });
* // => [1]
*
- * var food = [
- * { 'name': 'beet', 'organic': false },
- * { 'name': 'carrot', 'organic': true }
+ * var characters = [
+ * { 'name': 'barney', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
* ];
*
* // using "_.pluck" callback shorthand
- * _.initial(food, 'organic');
- * // => [{ 'name': 'beet', 'organic': false }]
- *
- * var food = [
- * { 'name': 'banana', 'type': 'fruit' },
- * { 'name': 'beet', 'type': 'vegetable' },
- * { 'name': 'carrot', 'type': 'vegetable' }
- * ];
+ * _.initial(characters, 'blocked');
+ * // => [{ 'name': 'barney', 'blocked': false, 'employer': 'slate' }]
*
* // using "_.where" callback shorthand
- * _.initial(food, { 'type': 'vegetable' });
- * // => [{ 'name': 'banana', 'type': 'fruit' }]
+ * _.pluck(_.initial(characters, { 'employer': 'na' }), 'name');
+ * // => ['barney', 'fred']
*/
function initial(array, callback, thisArg) {
var n = 0,
@@ -4621,24 +4828,19 @@
* });
* // => [2, 3]
*
- * var food = [
- * { 'name': 'beet', 'organic': false },
- * { 'name': 'carrot', 'organic': true }
+ * var characters = [
+ * { 'name': 'barney', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
* ];
*
* // using "_.pluck" callback shorthand
- * _.last(food, 'organic');
- * // => [{ 'name': 'carrot', 'organic': true }]
- *
- * var food = [
- * { 'name': 'banana', 'type': 'fruit' },
- * { 'name': 'beet', 'type': 'vegetable' },
- * { 'name': 'carrot', 'type': 'vegetable' }
- * ];
+ * _.pluck(_.last(characters, 'blocked'), 'name');
+ * // => ['fred', 'pebbles']
*
* // using "_.where" callback shorthand
- * _.last(food, { 'type': 'vegetable' });
- * // => [{ 'name': 'beet', 'type': 'vegetable' }, { 'name': 'carrot', 'type': 'vegetable' }]
+ * _.last(characters, { 'employer': 'na' });
+ * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
*/
function last(array, callback, thisArg) {
var n = 0,
@@ -4664,6 +4866,13 @@
* equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used
* as the offset from the end of the collection.
*
+ * If a property name is provided for `callback` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `callback` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
* @static
* @memberOf _
* @category Arrays
@@ -4742,17 +4951,17 @@
* @returns {Array} Returns a new range array.
* @example
*
- * _.range(10);
- * // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ * _.range(4);
+ * // => [0, 1, 2, 3]
*
- * _.range(1, 11);
- * // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ * _.range(1, 5);
+ * // => [1, 2, 3, 4]
*
- * _.range(0, 30, 5);
- * // => [0, 5, 10, 15, 20, 25]
+ * _.range(0, 20, 5);
+ * // => [0, 5, 10, 15]
*
- * _.range(0, -10, -1);
- * // => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
+ * _.range(0, -4, -1);
+ * // => [0, -1, -2, -3]
*
* _.range(1, 4, 0);
* // => [1, 1, 1]
@@ -4768,7 +4977,7 @@
end = start;
start = 0;
}
- // use `Array(length)` so engines, like Chakra and V8, avoid slower modes
+ // use `Array(length)` so engines like Chakra and V8 avoid slower modes
// http://youtu.be/XAqIpGU8ZZk#t=17m25s
var index = -1,
length = nativeMax(0, ceil((end - start) / (step || 1))),
@@ -4868,24 +5077,19 @@
* });
* // => [3]
*
- * var food = [
- * { 'name': 'banana', 'organic': true },
- * { 'name': 'beet', 'organic': false },
+ * var characters = [
+ * { 'name': 'barney', 'blocked': true, 'employer': 'slate' },
+ * { 'name': 'fred', 'blocked': false, 'employer': 'slate' },
+ * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
* ];
*
* // using "_.pluck" callback shorthand
- * _.rest(food, 'organic');
- * // => [{ 'name': 'beet', 'organic': false }]
- *
- * var food = [
- * { 'name': 'apple', 'type': 'fruit' },
- * { 'name': 'banana', 'type': 'fruit' },
- * { 'name': 'beet', 'type': 'vegetable' }
- * ];
+ * _.pluck(_.rest(characters, 'blocked'), 'name');
+ * // => ['fred', 'pebbles']
*
* // using "_.where" callback shorthand
- * _.rest(food, { 'type': 'fruit' });
- * // => [{ 'name': 'beet', 'type': 'vegetable' }]
+ * _.rest(characters, { 'employer': 'slate' });
+ * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
*/
function rest(array, callback, thisArg) {
if (typeof callback != 'number' && callback != null) {
@@ -5034,7 +5238,7 @@
// juggle arguments
if (typeof isSorted != 'boolean' && isSorted != null) {
thisArg = callback;
- callback = !(thisArg && thisArg[isSorted] === array) ? isSorted : null;
+ callback = (typeof isSorted != 'function' && thisArg && thisArg[isSorted] === array) ? null : isSorted;
isSorted = false;
}
if (callback != null) {
@@ -5059,7 +5263,7 @@
* // => [2, 3, 4]
*/
function without(array) {
- return difference(array, nativeSlice.call(arguments, 1));
+ return baseDifference(array, slice(arguments, 1));
}
/**
@@ -5075,8 +5279,8 @@
* @returns {Array} Returns a new array of grouped elements.
* @example
*
- * _.zip(['moe', 'larry'], [30, 40], [true, false]);
- * // => [['moe', 30, true], ['larry', 40, false]]
+ * _.zip(['fred', 'barney'], [30, 40], [true, false]);
+ * // => [['fred', 30, true], ['barney', 40, false]]
*/
function zip() {
var array = arguments.length > 1 ? arguments : arguments[0],
@@ -5105,8 +5309,8 @@
* corresponding values.
* @example
*
- * _.zipObject(['moe', 'larry'], [30, 40]);
- * // => { 'moe': 30, 'larry': 40 }
+ * _.zipObject(['fred', 'barney'], [30, 40]);
+ * // => { 'fred': 30, 'barney': 40 }
*/
function zipObject(keys, values) {
var index = -1,
@@ -5179,14 +5383,14 @@
* return greeting + ' ' + this.name;
* };
*
- * func = _.bind(func, { 'name': 'moe' }, 'hi');
+ * func = _.bind(func, { 'name': 'fred' }, 'hi');
* func();
- * // => 'hi moe'
+ * // => 'hi fred'
*/
function bind(func, thisArg) {
return arguments.length > 2
- ? createBound(func, 17, nativeSlice.call(arguments, 2), null, thisArg)
- : createBound(func, 1, null, null, thisArg);
+ ? createWrapper(func, 17, slice(arguments, 2), null, thisArg)
+ : createWrapper(func, 1, null, null, thisArg);
}
/**
@@ -5220,7 +5424,7 @@
while (++index < length) {
var key = funcs[index];
- object[key] = createBound(object[key], 1, null, null, object);
+ object[key] = createWrapper(object[key], 1, null, null, object);
}
return object;
}
@@ -5242,7 +5446,7 @@
* @example
*
* var object = {
- * 'name': 'moe',
+ * 'name': 'fred',
* 'greet': function(greeting) {
* return greeting + ' ' + this.name;
* }
@@ -5250,19 +5454,19 @@
*
* var func = _.bindKey(object, 'greet', 'hi');
* func();
- * // => 'hi moe'
+ * // => 'hi fred'
*
* object.greet = function(greeting) {
- * return greeting + ', ' + this.name + '!';
+ * return greeting + 'ya ' + this.name + '!';
* };
*
* func();
- * // => 'hi, moe!'
+ * // => 'hiya fred!'
*/
function bindKey(object, key) {
return arguments.length > 2
- ? createBound(key, 19, nativeSlice.call(arguments, 2), null, object)
- : createBound(key, 3, null, null, object);
+ ? createWrapper(key, 19, slice(arguments, 2), null, object)
+ : createWrapper(key, 3, null, null, object);
}
/**
@@ -5279,7 +5483,7 @@
* @example
*
* var realNameMap = {
- * 'curly': 'jerome'
+ * 'pebbles': 'penelope'
* };
*
* var format = function(name) {
@@ -5292,8 +5496,8 @@
* };
*
* var welcome = _.compose(greet, format);
- * welcome('curly');
- * // => 'Hiya Jerome!'
+ * welcome('pebbles');
+ * // => 'Hiya Penelope!'
*/
function compose() {
var funcs = arguments,
@@ -5330,9 +5534,9 @@
* @returns {Function} Returns a callback function.
* @example
*
- * var stooges = [
- * { 'name': 'moe', 'age': 40 },
- * { 'name': 'larry', 'age': 50 }
+ * var characters = [
+ * { 'name': 'barney', 'age': 36 },
+ * { 'name': 'fred', 'age': 40 }
* ];
*
* // wrap to create custom callback shorthands
@@ -5343,8 +5547,8 @@
* };
* });
*
- * _.filter(stooges, 'age__gt45');
- * // => [{ 'name': 'larry', 'age': 50 }]
+ * _.filter(characters, 'age__gt38');
+ * // => [{ 'name': 'fred', 'age': 40 }]
*/
function createCallback(func, thisArg, argCount) {
var type = typeof func;
@@ -5413,7 +5617,7 @@
*/
function curry(func, arity) {
arity = typeof arity == 'number' ? arity : (+arity || func.length);
- return createBound(func, 4, null, null, null, arity);
+ return createWrapper(func, 4, null, null, null, arity);
}
/**
@@ -5490,6 +5694,9 @@
if (isCalled) {
lastCalled = now();
result = func.apply(thisArg, args);
+ if (!timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
}
} else {
timeoutId = setTimeout(delayed, remaining);
@@ -5504,6 +5711,9 @@
if (trailing || (maxWait !== wait)) {
lastCalled = now();
result = func.apply(thisArg, args);
+ if (!timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
}
};
@@ -5519,8 +5729,10 @@
if (!maxTimeoutId && !leading) {
lastCalled = stamp;
}
- var remaining = maxWait - (stamp - lastCalled);
- if (remaining <= 0) {
+ var remaining = maxWait - (stamp - lastCalled),
+ isCalled = remaining <= 0;
+
+ if (isCalled) {
if (maxTimeoutId) {
maxTimeoutId = clearTimeout(maxTimeoutId);
}
@@ -5531,12 +5743,19 @@
maxTimeoutId = setTimeout(maxDelayed, remaining);
}
}
- if (!timeoutId && wait !== maxWait) {
+ if (isCalled && timeoutId) {
+ timeoutId = clearTimeout(timeoutId);
+ }
+ else if (!timeoutId && wait !== maxWait) {
timeoutId = setTimeout(delayed, wait);
}
if (leadingCall) {
+ isCalled = true;
result = func.apply(thisArg, args);
}
+ if (isCalled && !timeoutId && !maxTimeoutId) {
+ args = thisArg = null;
+ }
return result;
};
}
@@ -5560,11 +5779,11 @@
if (!isFunction(func)) {
throw new TypeError;
}
- var args = nativeSlice.call(arguments, 1);
+ var args = slice(arguments, 1);
return setTimeout(function() { func.apply(undefined, args); }, 1);
}
// use `setImmediate` if available in Node.js
- if (isV8 && moduleExports && typeof setImmediate == 'function') {
+ if (setImmediate) {
defer = function(func) {
if (!isFunction(func)) {
throw new TypeError;
@@ -5594,7 +5813,7 @@
if (!isFunction(func)) {
throw new TypeError;
}
- var args = nativeSlice.call(arguments, 2);
+ var args = slice(arguments, 2);
return setTimeout(function() { func.apply(undefined, args); }, wait);
}
@@ -5618,19 +5837,22 @@
* return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
* });
*
+ * fibonacci(9)
+ * // => 34
+ *
* var data = {
- * 'moe': { 'name': 'moe', 'age': 40 },
- * 'curly': { 'name': 'curly', 'age': 60 }
+ * 'fred': { 'name': 'fred', 'age': 40 },
+ * 'pebbles': { 'name': 'pebbles', 'age': 1 }
* };
*
* // modifying the result cache
- * var stooge = _.memoize(function(name) { return data[name]; }, _.identity);
- * stooge('curly');
- * // => { 'name': 'curly', 'age': 60 }
+ * var get = _.memoize(function(name) { return data[name]; }, _.identity);
+ * get('pebbles');
+ * // => { 'name': 'pebbles', 'age': 1 }
*
- * stooge.cache.curly.name = 'jerome';
- * stooge('curly');
- * // => { 'name': 'jerome', 'age': 60 }
+ * get.cache.pebbles.name = 'penelope';
+ * get('pebbles');
+ * // => { 'name': 'penelope', 'age': 1 }
*/
function memoize(func, resolver) {
if (!isFunction(func)) {
@@ -5700,11 +5922,11 @@
*
* var greet = function(greeting, name) { return greeting + ' ' + name; };
* var hi = _.partial(greet, 'hi');
- * hi('moe');
- * // => 'hi moe'
+ * hi('fred');
+ * // => 'hi fred'
*/
function partial(func) {
- return createBound(func, 16, nativeSlice.call(arguments, 1));
+ return createWrapper(func, 16, slice(arguments, 1));
}
/**
@@ -5735,7 +5957,7 @@
* // => { '_': _, 'jq': $ }
*/
function partialRight(func) {
- return createBound(func, 32, null, nativeSlice.call(arguments, 1));
+ return createWrapper(func, 32, null, slice(arguments, 1));
}
/**
@@ -5786,8 +6008,7 @@
debounceOptions.maxWait = wait;
debounceOptions.trailing = trailing;
- var result = debounce(func, wait, debounceOptions);
- return result;
+ return debounce(func, wait, debounceOptions);
}
/**
@@ -5804,22 +6025,15 @@
* @returns {Function} Returns the new function.
* @example
*
- * var hello = function(name) { return 'hello ' + name; };
- * hello = _.wrap(hello, function(func) {
- * return 'before, ' + func('moe') + ', after';
+ * var p = _.wrap(_.escape, function(func, text) {
+ * return '' + func(text) + '
';
* });
- * hello();
- * // => 'before, hello moe, after'
+ *
+ * p('Fred, Wilma, & Pebbles');
+ * // => 'Fred, Wilma, & Pebbles
'
*/
function wrap(value, wrapper) {
- if (!isFunction(wrapper)) {
- throw new TypeError;
- }
- return function() {
- var args = [value];
- push.apply(args, arguments);
- return wrapper.apply(this, args);
- };
+ return createWrapper(wrapper, 16, [value]);
}
/*--------------------------------------------------------------------------*/
@@ -5835,8 +6049,8 @@
* @returns {string} Returns the escaped string.
* @example
*
- * _.escape('Moe, Larry & Curly');
- * // => 'Moe, Larry & Curly'
+ * _.escape('Fred, Wilma, & Pebbles');
+ * // => 'Fred, Wilma, & Pebbles'
*/
function escape(string) {
return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar);
@@ -5852,8 +6066,8 @@
* @returns {*} Returns `value`.
* @example
*
- * var moe = { 'name': 'moe' };
- * moe === _.identity(moe);
+ * var object = { 'name': 'fred' };
+ * _.identity(object) === object;
* // => true
*/
function identity(value) {
@@ -5877,11 +6091,11 @@
* }
* });
*
- * _.capitalize('moe');
- * // => 'Moe'
+ * _.capitalize('fred');
+ * // => 'Fred'
*
- * _('moe').capitalize();
- * // => 'Moe'
+ * _('fred').capitalize();
+ * // => 'Fred'
*/
function mixin(object, source) {
var ctor = object,
@@ -5929,6 +6143,22 @@
return this;
}
+ /**
+ * A no-operation function.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @example
+ *
+ * var object = { 'name': 'fred' };
+ * _.noop(object) === undefined;
+ * // => true
+ */
+ function noop() {
+ // no operation performed
+ }
+
/**
* Converts the given value into an integer of the specified radix.
* If `radix` is `undefined` or `0` a `radix` of `10` is used unless the
@@ -5949,7 +6179,7 @@
* // => 8
*/
var parseInt = nativeParseInt(whitespace + '08') == 8 ? nativeParseInt : function(value, radix) {
- // Firefox and Opera still follow the ES3 specified implementation of `parseInt`
+ // Firefox < 21 and Opera < 15 follow the ES3 specified implementation of `parseInt`
return nativeParseInt(isString(value) ? value.replace(reLeadingSpacesAndZeros, '') : value, radix || 0);
};
@@ -6004,10 +6234,11 @@
} else {
max = +max || 0;
}
- var rand = nativeRandom();
- return (floating || min % 1 || max % 1)
- ? nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand +'').length - 1)))), max)
- : min + floor(rand * (max - min + 1));
+ if (floating || min % 1 || max % 1) {
+ var rand = nativeRandom();
+ return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand +'').length - 1)))), max);
+ }
+ return baseRandom(min, max);
}
/**
@@ -6052,7 +6283,7 @@
* debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
*
* For more information on precompiling templates see:
- * http://lodash.com/#custom-builds
+ * http://lodash.com/custom-builds
*
* For more information on Chrome extension sandboxes see:
* http://developer.chrome.com/stable/extensions/sandboxingEval.html
@@ -6075,8 +6306,8 @@
*
* // using the "interpolate" delimiter to create a compiled template
* var compiled = _.template('hello <%= name %>');
- * compiled({ 'name': 'moe' });
- * // => 'hello moe'
+ * compiled({ 'name': 'fred' });
+ * // => 'hello fred'
*
* // using the "escape" delimiter to escape HTML in data property values
* _.template('<%- value %>', { 'value': '
-
+
diff --git a/perf/perf.js b/perf/perf.js
index 1479af3cce..34d84eda2d 100644
--- a/perf/perf.js
+++ b/perf/perf.js
@@ -1,16 +1,16 @@
-(function(window) {
+;(function(root) {
/** Use a single "load" function */
- var load = typeof require == 'function' ? require : window.load;
+ var load = typeof require == 'function' ? require : root.load;
/** The file path of the Lo-Dash file to test */
var filePath = (function() {
var min = 0;
- var result = window.phantom
+ var result = root.phantom
? phantom.args
- : (window.system
+ : (root.system
? (min = 1, system.args)
- : (window.process ? (min = 2, process.argv) : (window.arguments || []))
+ : (root.process ? (min = 2, process.argv) : (root.arguments || []))
);
var last = result[result.length - 1];
@@ -26,22 +26,22 @@
}());
/** Load Lo-Dash */
- var lodash = window.lodash || (window.lodash = (
- lodash = load(filePath) || window._,
+ var lodash = root.lodash || (root.lodash = (
+ lodash = load(filePath) || root._,
lodash = lodash._ || lodash,
lodash.noConflict()
));
/** Load Benchmark.js */
- var Benchmark = window.Benchmark || (window.Benchmark = (
- Benchmark = load('../vendor/benchmark.js/benchmark.js') || window.Benchmark,
+ var Benchmark = root.Benchmark || (root.Benchmark = (
+ Benchmark = load('../vendor/benchmark.js/benchmark.js') || root.Benchmark,
Benchmark = Benchmark.Benchmark || Benchmark,
- Benchmark.runInContext(lodash.extend({}, window, { '_': lodash }))
+ Benchmark.runInContext(lodash.extend({}, root, { '_': lodash }))
));
/** Load Underscore */
- var _ = window._ || (window._ = (
- _ = load('../vendor/underscore/underscore.js') || window._,
+ var _ = root._ || (root._ = (
+ _ = load('../vendor/underscore/underscore.js') || root._,
_._ || _
));
@@ -67,28 +67,28 @@
var toString = Object.prototype.toString;
/** The `ui` object */
- var ui = window.ui || (window.ui = {
+ var ui = root.ui || (root.ui = {
'buildPath': basename(filePath, '.js'),
'otherPath': 'underscore'
});
/** The Lo-Dash build basename */
- var buildName = window.buildName = basename(ui.buildPath, '.js');
+ var buildName = root.buildName = basename(ui.buildPath, '.js');
/** The other library basename */
- var otherName = window.otherName = (function() {
+ var otherName = root.otherName = (function() {
var result = basename(ui.otherPath, '.js');
return result + (result == buildName ? ' (2)' : '');
}());
/** Detect if in a browser environment */
- var isBrowser = isHostType(window, 'document') && isHostType(window, 'navigator');
+ var isBrowser = isHostType(root, 'document') && isHostType(root, 'navigator');
/** Detect Java environment */
- var isJava = !isBrowser && /Java/.test(toString.call(window.java));
+ var isJava = !isBrowser && /Java/.test(toString.call(root.java));
/** Add `console.log()` support for Narwhal, Rhino, and RingoJS */
- var console = window.console || (window.console = { 'log': window.print });
+ var console = root.console || (root.console = { 'log': root.print });
/*--------------------------------------------------------------------------*/
@@ -173,7 +173,7 @@
* @private (@public in the browser)
*/
function run() {
- fbPanel = (fbPanel = window.document && document.getElementById('FirebugUI')) &&
+ fbPanel = (fbPanel = root.document && document.getElementById('FirebugUI')) &&
(fbPanel = (fbPanel = fbPanel.contentWindow || fbPanel.contentDocument).document || fbPanel) &&
fbPanel.getElementById('fbPanel1');
@@ -257,8 +257,8 @@
lodash.extend(Benchmark.options, {
'async': true,
'setup': '\
- var _ = window._,\
- lodash = window.lodash,\
+ var _ = global._,\
+ lodash = global.lodash,\
belt = this.name == buildName ? lodash : _;\
\
var index,\
@@ -280,11 +280,10 @@
}\
\
if (typeof bind != "undefined") {\
- var thisArg = { "name": "moe" },\
- ctor = function() {};\
+ var thisArg = { "name": "fred" };\
\
var func = function(greeting, punctuation) {\
- return greeting + ", " + this.name + (punctuation || ".");\
+ return greeting + " " + this.name + (punctuation || ".");\
};\
\
var _boundNormal = _.bind(func, thisArg),\
@@ -296,8 +295,8 @@
lodashBoundPartial = lodash.bind(func, thisArg, "hi");\
\
for (index = 0; index < 10; index++) {\
- _boundMultiple = _.bind(_boundMultiple, { "name": "moe" + index });\
- lodashBoundMultiple = lodash.bind(lodashBoundMultiple, { "name": "moe" + index });\
+ _boundMultiple = _.bind(_boundMultiple, { "name": "fred" + index });\
+ lodashBoundMultiple = lodash.bind(lodashBoundMultiple, { "name": "fred" + index });\
}\
}\
\
@@ -483,6 +482,15 @@
}\
}\
\
+ if (typeof partial != "undefined") {\
+ var func = function(greeting, punctuation) {\
+ return greeting + " fred" + (punctuation || ".");\
+ };\
+ \
+ var _partial = _.partial(func, "hi"),\
+ lodashPartial = lodash.partial(func, "hi");\
+ }\
+ \
if (typeof template != "undefined") {\
var tplData = {\
"header1": "Header1",\
@@ -537,6 +545,18 @@
lodashFindWhere = lodash.findWhere || lodash.find,\
whereObject = { "num": 9 };\
}\
+ if (typeof wrap != "undefined") {\
+ var add = function(a, b) {\
+ return a + b;\
+ };\
+ \
+ var average = function(func, a, b) {\
+ return (func(a, b) / 2).toFixed(2);\
+ };\
+ \
+ var _wrapped = _.wrap(add, average);\
+ lodashWrapped = lodash.wrap(add, average);\
+ }\
if (typeof zip != "undefined") {\
var unzipped = [["a", "b", "c"], [1, 2, 3], [true, false, true]];\
}'
@@ -592,25 +612,13 @@
/*--------------------------------------------------------------------------*/
suites.push(
- Benchmark.Suite('`_.bind` (uses native `Function#bind` if available and inferred fast)')
+ Benchmark.Suite('`_.bind`')
.add(buildName, {
- 'fn': 'lodash.bind(func, { "name": "moe" })',
+ 'fn': 'lodash.bind(func, { "name": "fred" })',
'teardown': 'function bind(){}'
})
.add(otherName, {
- 'fn': '_.bind(func, { "name": "moe" })',
- 'teardown': 'function bind(){}'
- })
- );
-
- suites.push(
- Benchmark.Suite('bound call')
- .add(buildName, {
- 'fn': 'lodashBoundNormal()',
- 'teardown': 'function bind(){}'
- })
- .add(otherName, {
- 'fn': '_boundNormal()',
+ 'fn': '_.bind(func, { "name": "fred" })',
'teardown': 'function bind(){}'
})
);
@@ -628,19 +636,7 @@
);
suites.push(
- Benchmark.Suite('bound and partially applied call (uses native `Function#bind` if available)')
- .add(buildName, {
- 'fn': 'lodashBoundPartial()',
- 'teardown': 'function bind(){}'
- })
- .add(otherName, {
- 'fn': '_boundPartial()',
- 'teardown': 'function bind(){}'
- })
- );
-
- suites.push(
- Benchmark.Suite('bound and partially applied call with arguments (uses native `Function#bind` if available)')
+ Benchmark.Suite('bound and partially applied call with arguments')
.add(buildName, {
'fn': 'lodashBoundPartial("!")',
'teardown': 'function bind(){}'
@@ -1466,6 +1462,32 @@
/*--------------------------------------------------------------------------*/
+ suites.push(
+ Benchmark.Suite('`_.partial`')
+ .add(buildName, {
+ 'fn': 'lodash.partial(func, "hi")',
+ 'teardown': 'function partial(){}'
+ })
+ .add(otherName, {
+ 'fn': '_.partial(func, "hi")',
+ 'teardown': 'function partial(){}'
+ })
+ );
+
+ suites.push(
+ Benchmark.Suite('partially applied call with arguments')
+ .add(buildName, {
+ 'fn': 'lodashPartial("!")',
+ 'teardown': 'function partial(){}'
+ })
+ .add(otherName, {
+ 'fn': '_partial("!")',
+ 'teardown': 'function partial(){}'
+ })
+ );
+
+ /*--------------------------------------------------------------------------*/
+
suites.push(
Benchmark.Suite('`_.pick`')
.add(buildName, '\
@@ -1946,6 +1968,20 @@
/*--------------------------------------------------------------------------*/
+ suites.push(
+ Benchmark.Suite('`_.wrap` result called')
+ .add(buildName, {
+ 'fn': 'lodashWrapped(2, 5)',
+ 'teardown': 'function wrap(){}'
+ })
+ .add(otherName, {
+ 'fn': '_wrapped(2, 5)',
+ 'teardown': 'function wrap(){}'
+ })
+ );
+
+ /*--------------------------------------------------------------------------*/
+
suites.push(
Benchmark.Suite('`_.zip`')
.add(buildName, {
@@ -1964,8 +2000,8 @@
log(Benchmark.platform);
}
// in the browser, expose `run` to be called later
- if (window.document && !window.phantom) {
- window.run = run;
+ if (root.document && !root.phantom) {
+ root.run = run;
} else {
run();
}
diff --git a/test/asset/test-ui.js b/test/asset/test-ui.js
index 29dd1f9c3f..ab5b03851d 100644
--- a/test/asset/test-ui.js
+++ b/test/asset/test-ui.js
@@ -34,47 +34,6 @@
/*--------------------------------------------------------------------------*/
- // expose `ui.urlParams` properties
- ui.urlParams = {
- 'build': build,
- 'loader': loader
- };
-
- // expose Lo-Dash build file path
- ui.buildPath = (function() {
- var result;
- switch (build) {
- case 'lodash-compat': result = 'dist/lodash.compat.min.js'; break;
- case 'lodash-modern-dev': result = 'dist/lodash.js'; break;
- case 'lodash-modern': result = 'dist/lodash.min.js'; break;
- case 'lodash-legacy': result = 'dist/lodash.legacy.min.js'; break;
- case 'lodash-mobile': result = 'dist/lodash.mobile.min.js'; break;
- case 'lodash-underscore': result = 'dist/lodash.underscore.min.js'; break;
- case 'lodash-custom-dev': result = 'lodash.custom.js'; break;
- case 'lodash-custom': result = 'lodash.custom.min.js'; break;
- case 'lodash-compat-dev':
- case null: result = 'lodash.js'; break;
- default: return build;
- }
- return basePath + result;
- }());
-
- // expose module loader file path
- ui.loaderPath = (function() {
- var result;
- switch (loader) {
- case 'curl': result = 'vendor/curl/dist/curl-kitchen-sink/curl.js'; break;
- case 'dojo': result = 'vendor/dojo/dojo.js'; break;
- case 'requirejs':
- case null: result = 'vendor/requirejs/require.js'; break;
- default: return loader;
- }
- return basePath + result;
- }());
-
- // used to indicate testing a modularized build
- ui.isModularize = /\b(?:commonjs|(index|main)\.js|lodash-(?:amd|node)|modularize|npm)\b/.test([location.pathname, location.search, ui.buildPath]);
-
// initialize controls
addListener(window, 'load', function() {
function eventHandler(event) {
@@ -118,11 +77,11 @@
loaderList.selectedIndex = (function() {
switch (loader) {
- case 'none': return 0
case 'curl': return 1;
case 'dojo': return 2;
- case 'requirejs':
- case null: return 3;
+ case 'requirejs': return 3;
+ case 'none':
+ case null: return 0;
}
return -1;
}());
@@ -168,6 +127,47 @@
init();
});
+ // expose Lo-Dash build file path
+ ui.buildPath = (function() {
+ var result;
+ switch (build) {
+ case 'lodash-compat': result = 'dist/lodash.compat.min.js'; break;
+ case 'lodash-modern-dev': result = 'dist/lodash.js'; break;
+ case 'lodash-modern': result = 'dist/lodash.min.js'; break;
+ case 'lodash-legacy': result = 'dist/lodash.legacy.min.js'; break;
+ case 'lodash-mobile': result = 'dist/lodash.mobile.min.js'; break;
+ case 'lodash-underscore': result = 'dist/lodash.underscore.min.js'; break;
+ case 'lodash-custom-dev': result = 'lodash.custom.js'; break;
+ case 'lodash-custom': result = 'lodash.custom.min.js'; break;
+ case null: build = 'lodash-compat-dev';
+ case 'lodash-compat-dev': result = 'lodash.js'; break;
+ default: return build;
+ }
+ return basePath + result;
+ }());
+
+ // expose module loader file path
+ ui.loaderPath = (function() {
+ var result;
+ switch (loader) {
+ case 'curl': result = 'vendor/curl/dist/curl-kitchen-sink/curl.js'; break;
+ case 'dojo': result = 'vendor/dojo/dojo.js'; break;
+ case 'requirejs': result = 'vendor/requirejs/require.js'; break;
+ case null: loader = 'none'; return '';
+ default: return loader;
+ }
+ return basePath + result;
+ }());
+
+ // expose `ui.urlParams` properties
+ ui.urlParams = {
+ 'build': build,
+ 'loader': loader
+ };
+
+ // used to indicate testing a modularized build
+ ui.isModularize = /\b(?:commonjs|(index|main)\.js|lodash-(?:amd|node)|modularize|npm)\b/.test([location.pathname, location.search, ui.buildPath]);
+
// expose `ui`
window.ui = ui;
diff --git a/test/backbone.html b/test/backbone.html
index a4962d73cb..8b9fa6be66 100644
--- a/test/backbone.html
+++ b/test/backbone.html
@@ -26,6 +26,72 @@ Test