diff --git a/.travis.yml b/.travis.yml index b9d6964b6a..2b22b6adb1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,4 +4,4 @@ node_js: - 0.8 before_script: - "curl -H 'Accept: application/vnd.github.v3.raw' https://api.github.com/repos/bestiejs/lodash/git/blobs/a2787b470c577cee2404d186c562dd9835f779f5 | tar xvz -C vendor" - - "curl -H 'Accept: application/vnd.github.v3.raw' https://api.github.com/repos/bestiejs/lodash/git/blobs/3390b259e04829538e4d3635d12b317dd6103eca | tar xvz -C vendor" + - "curl -H 'Accept: application/vnd.github.v3.raw' https://api.github.com/repos/bestiejs/lodash/git/blobs/7ecae09d413eb48dd5785fe877f24e60ac3bbcef | tar xvz -C vendor" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..ca4191ac06 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,35 @@ +# Contributing to Lo-Dash + +If you’d like to contribute a feature or bug fix, you can [fork](https://help.github.com/articles/fork-a-repo) Lo-Dash, commit your changes, and [send a pull request](https://help.github.com/articles/using-pull-requests). +Please make sure to [search the issue tracker](https://github.com/bestiejs/lodash/issues) first; your issue may have already been discussed or fixed in `master`. + +## Tests + +Include updated unit tests in the `test` directory as part of your pull request. +You can run the tests from the command line via `npm test`, or open `test/index.html` in a web browser. +The `test/run-test.sh` script attempts to run the tests in [Rhino](http://www.mozilla.org/rhino/), [RingoJS](http://ringojs.org/), [Narwhal](https://github.com/280north/narwhal), and [Node](http://nodejs.org/), before running them in your default browser. +The [Backbone](http://backbonejs.org/) and [Underscore](http://http://underscorejs.org/) test suites are included as well. + +## Contributor License Agreement + +Lo-Dash is preparing to join [Dojo Foundation](http://dojofoundation.org/). +As such, we request that all contributors sign the Dojo Foundation [contributor license agreement](http://dojofoundation.org/about/claForm). + +For more information about CLAs, please check out Alex Russell’s excellent post, ["Why Do I Need to Sign This?"](http://infrequently.org/2008/06/why-do-i-need-to-sign-this/). + +## Coding Guidelines + +In addition to the following guidelines, please follow the conventions already established in the code. + +- **Spacing**:
+ Use two spaces for indentation. No tabs. + +- **Naming**:
+ Keep variable and method names concise and descriptive.
+ Variable names `index`, `collection`, and `callback` are preferable to `i`, `arr`, and `fn`. + +- **Quotes**:
+ Single-quoted strings are preferred to double-quoted strings; however, please use a double-quoted string if the value contains a single-quote character to avoid unnecessary escaping. + +- **Comments**:
+ Please use single-line comments to annotate significant additions, and [JSDoc-style](http://www.2ality.com/2011/08/jsdoc-intro.html) comments for new methods. diff --git a/LICENSE.txt b/LICENSE.txt index b194ad1deb..cd3ae1777d 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,5 +1,5 @@ Copyright 2012 John-David Dalton -Based on Underscore.js 1.3.3, copyright 2009-2012 Jeremy Ashkenas, +Based on Underscore.js 1.4.3, copyright 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. Permission is hereby granted, free of charge, to any person obtaining diff --git a/README.md b/README.md index 06e15d91be..9d6c4893b8 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,23 @@ -# Lo-Dash v1.0.0-rc.2 +# Lo-Dash v1.0.0-rc.3 [![build status](https://secure.travis-ci.org/bestiejs/lodash.png)](http://travis-ci.org/bestiejs/lodash) -A utility library, usable as a drop-in replacement for Underscore, delivering [performance](http://lodash.com/benchmarks), [bug fixes](https://github.com/bestiejs/lodash#resolved-underscorejs-issues), and [additional features](http://lodash.com/#features). +An alternative to Underscore.js, delivering [consistency](https://github.com/bestiejs/lodash#resolved-underscorejs-issues), [customization](https://github.com/bestiejs/lodash#custom-builds), [performance](http://lodash.com/benchmarks), and [extra features](https://github.com/bestiejs/lodash#features). ## Download * Lo-Dash builds:
-[Development](https://raw.github.com/bestiejs/lodash/v1.0.0-rc.2/lodash.js) and -[Production](https://raw.github.com/bestiejs/lodash/v1.0.0-rc.2/lodash.min.js) +[Development](https://raw.github.com/bestiejs/lodash/v1.0.0-rc.3/lodash.js) and +[Production](https://raw.github.com/bestiejs/lodash/v1.0.0-rc.3/lodash.min.js) -* Underscore builds:
-[Development](https://raw.github.com/bestiejs/lodash/v1.0.0-rc.2/lodash.underscore.js) and -[Production](https://raw.github.com/bestiejs/lodash/v1.0.0-rc.2/lodash.underscore.min.js) +* Underscore compatibility builds:
+[Development](https://raw.github.com/bestiejs/lodash/v1.0.0-rc.3/lodash.underscore.js) and +[Production](https://raw.github.com/bestiejs/lodash/v1.0.0-rc.3/lodash.underscore.min.js) -* CDN copies of ≤ v1.0.0-rc.2’s builds are available on [cdnjs](http://cdnjs.com/) thanks to [CloudFlare](http://www.cloudflare.com/):
-[Lo-Dash development](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.0-rc.2/lodash.js), -[Lo-Dash production](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.0-rc.2/lodash.min.js), -[Underscore development](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.0-rc.2/lodash.underscore.js), and -[Underscore production](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.0-rc.2/lodash.underscore.min.js) +* CDN copies of ≤ v1.0.0-rc.3’s builds are available on [cdnjs](http://cdnjs.com/) thanks to [CloudFlare](http://www.cloudflare.com/):
+[Lo-Dash dev](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.0-rc.3/lodash.js), +[Lo-Dash prod](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.0-rc.3/lodash.min.js), +[Underscore compat-dev](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.0-rc.3/lodash.underscore.js), and +[Underscore compat-prod](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.0-rc.3/lodash.underscore.min.js) * For optimal file size, [create a custom build](https://github.com/bestiejs/lodash#custom-builds) with only the features you need @@ -25,8 +25,6 @@ A utility library, usable as a drop-in replacement for Underscore, delivering [p We’ve got [API docs](http://lodash.com/docs), [benchmarks](http://lodash.com/benchmarks), and [unit tests](http://lodash.com/tests). -Create your own benchmarks at [jsPerf](http://jsperf.com), or [search](http://jsperf.com/search?q=lodash) for existing ones. - For a list of upcoming features, check out our [roadmap](https://github.com/bestiejs/lodash/wiki/Roadmap). ## Screencasts @@ -43,9 +41,9 @@ For more information check out these screencasts over Lo-Dash: ## Features * AMD loader support ([RequireJS](http://requirejs.org/), [curl.js](https://github.com/cujojs/curl), etc.) - * [_(…)](http://lodash.com/docs#_) supports intuitive chaining without calling [_(…).chain](http://lodash.com/docs#prototype_chain) + * [_(…)](http://lodash.com/docs#_) supports intuitive chaining * [_.bindKey](http://lodash.com/docs#bindKey) for binding [*“lazy”* defined](http://michaux.ca/articles/lazy-function-definition-pattern) methods - * [_.clone](http://lodash.com/docs#clone) supports *“deep”* cloning + * [_.cloneDeep](http://lodash.com/docs#cloneDeep) for *“deep”* cloning arrays and objects * [_.contains](http://lodash.com/docs#contains) accepts a `fromIndex` argument * [_.forEach](http://lodash.com/docs#forEach) is chainable and supports exiting iteration early * [_.forIn](http://lodash.com/docs#forIn) for iterating over an object’s own and inherited properties @@ -60,7 +58,7 @@ For more information check out these screencasts over Lo-Dash: ## Support -Lo-Dash has been tested in at least Chrome 5~23, Firefox 1~16, IE 6-10, Opera 9.25-12, Safari 3-6, Node.js 0.4.8-0.8.15, Narwhal 0.3.2, RingoJS 0.8, and Rhino 1.7RC5. +Lo-Dash has been tested in at least Chrome 5~23, Firefox 1~17, IE 6-10, Opera 9.25-12, Safari 3-6, Node.js 0.4.8-0.8.16, Narwhal 0.3.2, RingoJS 0.8, and Rhino 1.7RC5. ## Custom builds @@ -228,31 +226,51 @@ require({ ## Resolved Underscore.js issues - * Allow iteration of objects with a `length` property [[#799](https://github.com/documentcloud/underscore/pull/799), [test](https://github.com/bestiejs/lodash/blob/v1.0.0-rc.2/test/test.js#L605-L611)] - * Fix cross-browser object iteration bugs [[#60](https://github.com/documentcloud/underscore/issues/60), [#376](https://github.com/documentcloud/underscore/issues/376), [test](https://github.com/bestiejs/lodash/blob/v1.0.0-rc.2/test/test.js#L618-L642)] - * Methods should work on pages with incorrectly shimmed native methods [[#7](https://github.com/documentcloud/underscore/issues/7), [#742](https://github.com/documentcloud/underscore/issues/742), [test](https://github.com/bestiejs/lodash/blob/v1.0.0-rc.2/test/test.js#L140-L146)] - * `_.isEmpty` should support jQuery/MooTools DOM query collections [[#690](https://github.com/documentcloud/underscore/pull/690), [test](https://github.com/bestiejs/lodash/blob/v1.0.0-rc.2/test/test.js#L807-L812)] - * `_.isObject` should avoid V8 bug [#2291](http://code.google.com/p/v8/issues/detail?id=2291) [[#605](https://github.com/documentcloud/underscore/issues/605), [test](https://github.com/bestiejs/lodash/blob/v1.0.0-rc.2/test/test.js#L888-L900)] - * `_.keys` should work with `arguments` objects cross-browser [[#396](https://github.com/documentcloud/underscore/issues/396), [test](https://github.com/bestiejs/lodash/blob/v1.0.0-rc.2/test/test.js#L981-L983)] - * `_.range` should coerce arguments to numbers [[#634](https://github.com/documentcloud/underscore/issues/634), [#683](https://github.com/documentcloud/underscore/issues/683), [test](https://github.com/bestiejs/lodash/blob/v1.0.0-rc.2/test/test.js#L1382-L1385)] + * Allow iteration of objects with a `length` property [[#799](https://github.com/documentcloud/underscore/pull/799), [test](https://github.com/bestiejs/lodash/blob/v1.0.0-rc.3/test/test.js#L605-L611)] + * Fix cross-browser object iteration bugs [[#60](https://github.com/documentcloud/underscore/issues/60), [#376](https://github.com/documentcloud/underscore/issues/376), [test](https://github.com/bestiejs/lodash/blob/v1.0.0-rc.3/test/test.js#L618-L642)] + * Methods should work on pages with incorrectly shimmed native methods [[#7](https://github.com/documentcloud/underscore/issues/7), [#742](https://github.com/documentcloud/underscore/issues/742), [test](https://github.com/bestiejs/lodash/blob/v1.0.0-rc.3/test/test.js#L140-L146)] + * `_.isEmpty` should support jQuery/MooTools DOM query collections [[#690](https://github.com/documentcloud/underscore/pull/690), [test](https://github.com/bestiejs/lodash/blob/v1.0.0-rc.3/test/test.js#L807-L812)] + * `_.isObject` should avoid V8 bug [#2291](http://code.google.com/p/v8/issues/detail?id=2291) [[#605](https://github.com/documentcloud/underscore/issues/605), [test](https://github.com/bestiejs/lodash/blob/v1.0.0-rc.3/test/test.js#L888-L900)] + * `_.keys` should work with `arguments` objects cross-browser [[#396](https://github.com/documentcloud/underscore/issues/396), [test](https://github.com/bestiejs/lodash/blob/v1.0.0-rc.3/test/test.js#L982-L984)] + * `_.range` should coerce arguments to numbers [[#634](https://github.com/documentcloud/underscore/issues/634), [#683](https://github.com/documentcloud/underscore/issues/683), [test](https://github.com/bestiejs/lodash/blob/v1.0.0-rc.3/test/test.js#L1383-L1386)] ## Release Notes -### v1.0.0-rc.2 ### +### v1.0.0-rc.3 + +#### Compatibility Warnings + + * Made `_#join`, `_#pop`, and `_#shift` wrapper methods return unwrapped values + * Made *“Functions”* methods wrapper counterparts return wrapped values + * Removed `_.chain` and `_#chain` methods + +#### Changes + + * Added [_.cloneDeep](http://lodash.com/docs#cloneDeep) alias of `_.clone(…, true)` + * Added `_.once` to the `backbone` build method dependencies + * Ensured `backbone` builds implement Underscore’s chaining behavior + * Ensured the `settings=…` build option doesn’t clobber the default `moduleId` + * Ensured Lo-Dash’s `npm` package installation avoids erroring when no other modules have been globally installed + * Made compiled templates loaded via AMD use the Lo-Dash module for their `_` references + * Removed the *“Collections”* method `_.forEach` dependency from *“Arrays”* method `_.intersection` + * Optimized `_.isArray` and `_.isFunction` fallbacks as well as
+ `_.intersection`, `_.isDate`, `_.isRegExp`, `_.reduce`, `_.reduceRight`, `_.union`, and `_.uniq` + +### v1.0.0-rc.2 * Specified more method chaining behaviors * Updated `underscore` build compatibility to v1.4.3 -### v1.0.0-rc.1 ### +### v1.0.0-rc.1 -#### Compatibility Warnings #### +#### Compatibility Warnings - * Made `_(…)` chain automatically without needing to call `_#chain` + * Made `_(…)` intuitively chain without needing to call `_#chain` * Made `_.isEqual` equate `arguments` objects to similar `Object` objects * Made `_.clone` copy the enumerable properties of `arguments` objects and objects
created by constructors other than `Object` are cloned to plain `Object` objects -#### Changes #### +#### Changes * Ensure Lo-Dash runs in the JS engine embedded in Adobe products * Ensured `_.reduce` and `_.reduceRight` pass the correct number of `callback` arguments diff --git a/build.js b/build.js index e3c447ab44..899d190a8b 100755 --- a/build.js +++ b/build.js @@ -70,11 +70,11 @@ 'bind': ['isFunction', 'isObject'], 'bindAll': ['bind', 'functions'], 'bindKey': ['isFunction', 'isObject'], - 'chain': ['mixin'], 'clone': ['assign', 'forEach', 'forOwn', 'isArray', 'isObject'], + 'cloneDeep': ['clone'], 'compact': [], 'compose': [], - 'contains': ['forEach', 'indexOf', 'isString'], + 'contains': ['indexOf', 'isString'], 'countBy': ['forEach'], 'debounce': [], 'defaults': ['isArguments'], @@ -82,12 +82,12 @@ 'delay': [], 'difference': ['indexOf'], 'escape': [], - 'every': ['forEach', 'isArray'], - 'filter': ['forEach', 'isArray'], + 'every': ['isArray'], + 'filter': ['isArray'], 'find': ['forEach'], 'first': [], 'flatten': ['isArray'], - 'forEach': ['identity', 'isArguments', 'isString'], + 'forEach': ['identity', 'isArguments', 'isArray', 'isString'], 'forIn': ['identity', 'isArguments'], 'forOwn': ['identity', 'isArguments'], 'functions': ['forIn', 'isFunction'], @@ -96,7 +96,7 @@ 'identity': [], 'indexOf': ['sortedIndex'], 'initial': [], - 'intersection': ['filter', 'indexOf'], + 'intersection': ['indexOf'], 'invert': ['forOwn'], 'invoke': ['forEach'], 'isArguments': [], @@ -119,11 +119,11 @@ 'keys': ['forOwn', 'isArguments', 'isObject'], 'last': [], 'lastIndexOf': [], - 'map': ['forEach', 'isArray'], - 'max': ['forEach', 'isArray', 'isString'], + 'map': ['isArray'], + 'max': ['isArray', 'isString'], 'memoize': [], 'merge': ['forOwn', 'isArray', 'isPlainObject'], - 'min': ['forEach', 'isArray', 'isString'], + 'min': ['isArray', 'isString'], 'mixin': ['forEach', 'forOwn', 'functions'], 'noConflict': [], 'object': [], @@ -135,14 +135,14 @@ 'pluck': ['map'], 'random': [], 'range': [], - 'reduce': ['forEach'], + 'reduce': ['isArray'], 'reduceRight': ['forEach', 'isString', 'keys'], 'reject': ['filter'], 'rest': [], 'result': ['isFunction'], 'shuffle': ['forEach'], 'size': ['keys'], - 'some': ['forEach', 'isArray'], + 'some': ['isArray'], 'sortBy': ['forEach'], 'sortedIndex': ['identity'], 'tap': ['mixin'], @@ -159,7 +159,10 @@ 'where': ['filter', 'keys'], 'without': ['indexOf'], 'wrap': [], - 'zip': ['max', 'pluck'] + 'zip': ['max', 'pluck'], + + // method used by the `backbone` and `underscore` builds + 'chain': ['mixin'] }; /** Used to inline `iteratorTemplate` */ @@ -171,7 +174,7 @@ 'hasDontEnumBug', 'isKeysFast', 'objectLoop', - 'noArgsEnum', + 'nonEnumArgs', 'noCharByIndex', 'shadowed', 'top', @@ -217,6 +220,7 @@ 'max', 'min', 'mixin', + 'once', 'pick', 'reduce', 'reduceRight', @@ -237,6 +241,7 @@ /** List of methods used by Underscore */ var underscoreMethods = _.without.apply(_, [allMethods].concat([ 'bindKey', + 'cloneDeep', 'forIn', 'forOwn', 'isPlainObject', @@ -255,7 +260,157 @@ /*--------------------------------------------------------------------------*/ /** - * Adds the given build `commands` to the copyright/license header of `source`. + * Adds support for Underscore style chaining to the `source`. + * + * @private + * @param {String} source The source to process. + * @returns {String} Returns the modified source. + */ + function addChainMethods(source) { + // add `_.chain` + source = source.replace(matchFunction(source, 'tap'), function(match) { + return [ + '', + ' /**', + ' * Creates a `lodash` object that wraps the given `value`.', + ' *', + ' * @static', + ' * @memberOf _', + ' * @category Chaining', + ' * @param {Mixed} value The value to wrap.', + ' * @returns {Object} Returns the wrapper object.', + ' * @example', + ' *', + ' * var stooges = [', + " * { 'name': 'moe', 'age': 40 },", + " * { 'name': 'larry', 'age': 50 },", + " * { 'name': 'curly', 'age': 60 }", + ' * ];', + ' *', + ' * var youngest = _.chain(stooges)', + ' * .sortBy(function(stooge) { return stooge.age; })', + " * .map(function(stooge) { return stooge.name + ' is ' + stooge.age; })", + ' * .first();', + " * // => 'moe is 40'", + ' */', + ' function chain(value) {', + ' value = new lodash(value);', + ' value.__chain__ = true;', + ' return value;', + ' }', + '', + match + ].join('\n'); + }); + + // add `wrapperChain` + source = source.replace(matchFunction(source, 'wrapperToString'), function(match) { + return [ + '', + ' /**', + ' * Enables method chaining on the wrapper object.', + ' *', + ' * @name chain', + ' * @memberOf _', + ' * @category Chaining', + ' * @returns {Mixed} Returns the wrapper object.', + ' * @example', + ' *', + ' * var sum = _([1, 2, 3])', + ' * .chain()', + ' * .reduce(function(sum, num) { return sum + num; })', + ' * .value()', + ' * // => 6`', + ' */', + ' function wrapperChain() {', + ' this.__chain__ = true;', + ' return this;', + ' }', + '', + match + ].join('\n'); + }); + + // add `lodash.chain` assignment + source = source.replace(getMethodAssignments(source), function(match) { + return match.replace(/^(?: *\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/\n)?( *)lodash\.VERSION *=/m, '$1lodash.chain = chain;\n\n$&'); + }); + + // add `lodash.prototype.chain` assignment + source = source.replace(/^( *)lodash\.prototype\.value *=.+\n/m, '$1lodash.prototype.chain = wrapperChain;\n$&'); + + // remove `lodash.prototype.toString` and `lodash.prototype.valueOf` assignments + source = source.replace(/^ *lodash\.prototype\.(?:toString|valueOf) *=.+\n/gm, ''); + + // remove `lodash.prototype` batch method assignments + source = source.replace(/(?:\s*\/\/.*)*\n( *)forOwn\(lodash, *function\(func, *methodName\)[\s\S]+?\n\1}.+/g, ''); + + // move `mixin(lodash)` to after the method assignments + source = source.replace(/(?:\s*\/\/.*)*\s*mixin\(lodash\).+/, ''); + source = source.replace(getMethodAssignments(source), function(match) { + return match + [ + '', + '', + ' // add functions to `lodash.prototype`', + ' mixin(lodash);' + ].join('\n'); + }); + + // add `__chain__` checks to `_.mixin` + source = source.replace(matchFunction(source, 'mixin'), function(match) { + return match.replace(/^( *)return new lodash.+/m, function() { + var indent = arguments[1]; + return indent + [ + 'if (this.__chain__) {', + ' result = new lodash(result);', + ' result.__chain__ = true;', + '}', + 'return result;' + ].join('\n' + indent); + }); + }); + + // replace wrapper `Array` method assignments + source = source.replace(/^(?: *\/\/.*\n)*( *)each\(\['[\s\S]+?\n\1}$/m, function() { + return [ + ' // add `Array` mutator functions to the wrapper', + " each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {", + ' var func = arrayRef[methodName];', + ' lodash.prototype[methodName] = function() {', + ' var value = this.__wrapped__;', + ' func.apply(value, arguments);', + '', + ' // avoid array-like object bugs with `Array#shift` and `Array#splice`', + ' // in Firefox < 10 and IE < 9', + ' if (hasObjectSpliceBug && value.length === 0) {', + ' delete value[0];', + ' }', + ' return this;', + ' };', + ' });', + '', + ' // add `Array` accessor functions to the wrapper', + " each(['concat', 'join', 'slice'], function(methodName) {", + ' var func = arrayRef[methodName];', + ' lodash.prototype[methodName] = function() {', + ' var value = this.__wrapped__,', + ' result = func.apply(value, arguments);', + '', + ' if (this.__chain__) {', + ' result = new lodash(result);', + ' result.__chain__ = true;', + ' }', + ' return result;', + ' };', + ' });' + ].join('\n'); + }); + + return source; + } + + /** + * Adds build `commands` to the copyright/license header of the `source`. * * @private * @param {String} source The source to process. @@ -284,7 +439,8 @@ }); // add build commands to copyright/license header return ( - parts[0] + parts[1] + parts[2] + ' (Custom Build)' + parts[3] + '\n' + + parts[0] + + parts[1] + parts[2] + ' (Custom Build)' + parts[3] + '\n' + parts[1] + ' Build: `lodash ' + commands.join(' ') + '`' ); }); @@ -336,6 +492,7 @@ source.push( " if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {", " define(['" + options.moduleId + "'], function(lodash) {", + ' _ = lodash;', ' lodash.templates = lodash.extend(lodash.templates || {}, templates);', ' });', " } else if (freeExports) {", @@ -746,6 +903,29 @@ return source; } + /** + * Removes all `argsAreObjects` references from `source`. + * + * @private + * @param {String} source The source to process. + * @returns {String} Returns the modified source. + */ + function removeArgsAreObjects(source) { + source = removeVar(source, 'argsAreObjects'); + + // remove `argsAreObjects` from `_.isArray` + source = source.replace(matchFunction(source, 'isArray'), function(match) { + return match.replace(/\(argsAreObjects && *([^)]+)\)/g, '$1'); + }); + + // remove `argsAreObjects` from `_.isEqual` + source = source.replace(matchFunction(source, 'isEqual'), function(match) { + return match.replace(/!argsAreObjects[^:]+:\s*/g, ''); + }); + + return source; + } + /** * Removes all `noArgsClass` references from `source`. * @@ -761,11 +941,6 @@ return match.replace(/ *\|\| *\(noArgsClass *&&[^)]+?\)\)/g, ''); }); - // remove `noArgsClass` from `_.isEqual` - source = source.replace(matchFunction(source, 'isEqual'), function(match) { - return match.replace(/noArgsClass[^:]+:\s*/g, ''); - }); - return source; } @@ -798,6 +973,19 @@ return source; } + /** + * Removes all `hasObjectSpliceByg` references from `source`. + * + * @private + * @param {String} source The source to process. + * @returns {String} Returns the modified source. + */ + function removeHasObjectSpliceBug(source) { + return removeVar(source, 'hasObjectSpliceBug') + // remove `hasObjectSpliceBug` fix from the `Array` function mixins + .replace(/(?:\s*\/\/.*)*\n( *)if *\(hasObjectSpliceBug[\s\S]+?(?:{\s*}|\n\1})/, ''); + } + /** * Removes a given variable from `source`. * @@ -1050,7 +1238,7 @@ var templateSettings = options.reduce(function(result, value) { var match = value.match(/settings=(.+)$/); return match - ? Function('return {' + match[1].replace(/^{|}$/g, '') + '}')() + ? _.assign(result, Function('return {' + match[1].replace(/^{|}$/g, '') + '}')()) : result; }, _.assign(_.clone(_.templateSettings), { 'moduleId': moduleId @@ -1108,11 +1296,11 @@ dependencyMap.reduceRight = ['forEach', 'keys']; } if (isUnderscore) { - dependencyMap.contains = ['forEach', 'indexOf']; + dependencyMap.contains = ['indexOf']; dependencyMap.isEqual = ['isArray', 'isFunction']; dependencyMap.isEmpty = ['isArray', 'isString']; - dependencyMap.max = ['forEach', 'isArray']; - dependencyMap.min = ['forEach', 'isArray']; + dependencyMap.max = ['isArray']; + dependencyMap.min = ['isArray']; dependencyMap.pick = []; dependencyMap.template = ['defaults', 'escape']; @@ -1174,7 +1362,11 @@ source = replaceVar(source, 'noArgsClass', 'true'); source = removeKeysOptimization(source); } - else if (isUnderscore) { + if (isBackbone || isUnderscore) { + // add Underscore style chaining + source = addChainMethods(source); + } + if (isUnderscore) { // remove unneeded variables source = removeVar(source, 'cloneableClasses'); source = removeVar(source, 'ctorByClass'); @@ -1201,15 +1393,6 @@ ' }' ].join('\n')); - // replace `_.chain` - source = replaceFunction(source, 'chain', [ - ' function chain(value) {', - ' value = new lodash(value);', - ' value.__chain__ = true;', - ' return value;', - ' }' - ].join('\n')); - // replace `_.clone` if (useUnderscoreClone) { source = replaceFunction(source, 'clone', [ @@ -1229,7 +1412,7 @@ " if (typeof length == 'number') {", ' result = indexOf(collection, target) > -1;', ' } else {', - ' forEach(collection, function(value) {', + ' each(collection, function(value) {', ' return (result = value === target) && indicatorObject;', ' });', ' }', @@ -1280,19 +1463,23 @@ ' function intersection(array) {', ' var args = arguments,', ' argsLength = args.length,', + ' index = -1,', + ' length = array ? array.length : 0,', ' result = [];', '', - ' forEach(array, function(value) {', + ' outer:', + ' while (++index < length) {', + ' var value = array[index];', ' if (indexOf(result, value) < 0) {', - ' var length = argsLength;', - ' while (--length) {', - ' if (indexOf(args[length], value) < 0) {', - ' return;', + ' var argsIndex = argsLength;', + ' while (--argsIndex) {', + ' if (indexOf(args[argsIndex], value) < 0) {', + ' continue outer;', ' }', ' }', ' result.push(value);', ' }', - ' });', + ' }', ' return result;', ' }' ].join('\n')); @@ -1459,36 +1646,11 @@ ' }' ].join('\n')); - // replace `wrapperChain` - source = replaceFunction(source, 'wrapperChain', [ - ' function wrapperChain() {', - ' this.__chain__ = true;', - ' return this;', - ' }' - ].join('\n')); - - // add `__chain__` checks to `_.mixin` and `Array` function wrappers - _.each([ - matchFunction(source, 'mixin'), - /(?:\s*\/\/.*)*\n( *)forEach\(\['[\s\S]+?\n\1}.+/g - ], function(pattern) { - source = source.replace(pattern, function(match) { - return match.replace(/( *)return new lodash\(([^)]+)\).+/, function(submatch, indent, varName) { - return indent + [ - 'if (this.__chain__) {', - ' varName = new lodash(varName);', - ' varName.__chain__ = true;', - '}', - 'return varName;' - ].join('\n' + indent) - .replace(/varName/g, varName); - }); - }); - }); - - // remove `arguments` object check from `_.isEqual` + // remove `arguments` object and `argsAreObjects` check from `_.isEqual` source = source.replace(matchFunction(source, 'isEqual'), function(match) { - return match.replace(/^ *if *\(.+== argsClass[^}]+}\n/gm, '') + return match + .replace(/^ *if *\(.+== argsClass[^}]+}\n/gm, '') + .replace(/!argsAreObjects[^:]+:\s*/g, ''); }); // remove conditional `charCodeCallback` use from `_.max` and `_.min` @@ -1498,23 +1660,6 @@ }); }); - // remove `lodash.prototype.toString` and `lodash.prototype.valueOf` assignments - source = source.replace(/^ *lodash\.prototype\.(?:toString|valueOf) *=.+\n/gm, ''); - - // remove `lodash.prototype` batch method assignments - source = source.replace(/(?:\s*\/\/.*)*\n( *)forOwn\(lodash, *function\(func, *methodName\)[\s\S]+?\n\1}.+/g, ''); - - // move `mixin(lodash)` to after the method assignments - source = source.replace(/(?:\s*\/\/.*)*\s*mixin\(lodash\).+/, ''); - source = source.replace(getMethodAssignments(source), function(match) { - return match + [ - '', - '', - ' // add functions to `lodash.prototype`', - ' mixin(lodash);' - ].join('\n'); - }); - // remove unused features from `createBound` if (buildMethods.indexOf('partial') == -1) { source = source.replace(matchFunction(source, 'createBound'), function(match) { @@ -1617,7 +1762,8 @@ if (isMobile) { // inline all functions defined with `createIterator` _.functions(lodash).forEach(function(methodName) { - var reFunc = RegExp('(\\bvar ' + methodName + ' *= *)createIterator\\(((?:{|[a-zA-Z])[\\s\\S]+?)\\);\\n'); + // strip leading underscores to match pseudo private functions + var reFunc = RegExp('(\\bvar ' + methodName.replace(/^_/, '') + ' *= *)createIterator\\(((?:{|[a-zA-Z])[\\s\\S]+?)\\);\\n'); if (reFunc.test(source)) { // extract, format, and inject the compiled function's source code source = source.replace(reFunc, function(match, captured) { @@ -1660,13 +1806,15 @@ }); }()); - // remove chainability from `_.forEach` - source = source.replace(matchFunction(source, 'forEach'), function(match) { - return match.replace(/return result([};\s]+)$/, '$1'); + // remove chainability from `each` and `_.forEach` + _.each(['each', 'forEach'], function(methodName) { + source = source.replace(matchFunction(source, methodName), function(match) { + return match.replace(/\n *return .+?([};\s]+)$/, '$1'); + }); }); - // unexpose "exit early" feature from `_.forEach`, `_.forIn`, and `_.forOwn` - _.each(['forEach', 'forIn', 'forOwn'], function(methodName) { + // unexpose "exit early" feature of `each`, `_.forEach`, `_.forIn`, and `_.forOwn` + _.each(['each', 'forEach', 'forIn', 'forOwn'], function(methodName) { source = source.replace(matchFunction(source, methodName), function(match) { return match.replace(/=== *false\)/g, '=== indicatorObject)'); }); @@ -1688,10 +1836,9 @@ }); } else { + source = removeArgsAreObjects(source); + source = removeHasObjectSpliceBug(source); source = removeIsArgumentsFallback(source); - - // remove `hasObjectSpliceBug` fix from the mutator Array functions mixin - source = source.replace(/(?:\s*\/\/.*)*\n( *)if *\(hasObjectSpliceBug[\s\S]+?\n\1}/, ''); } // remove `thisArg` from unexposed `forIn` and `forOwn` @@ -1708,10 +1855,10 @@ } }); - // remove `hasDontEnumBug`, `iteratesOwnLast`, and `noArgsEnum` declarations and assignments + // remove `hasDontEnumBug`, `iteratesOwnLast`, and `nonEnumArgs` declarations and assignments source = source .replace(/^ *\(function\(\) *{[\s\S]+?}\(1\)\);\n/m, '') - .replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var (?:hasDontEnumBug|iteratesOwnLast|noArgsEnum).+\n/g, ''); + .replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var (?:hasDontEnumBug|iteratesOwnLast|nonEnumArgs).+\n/g, ''); // remove `iteratesOwnLast` from `shimIsPlainObject` source = source.replace(matchFunction(source, 'shimIsPlainObject'), function(match) { @@ -1835,7 +1982,7 @@ source = removeIsFunctionFallback(source); } if (isRemoved(source, 'mixin')) { - source = removeVar(source, 'hasObjectSpliceBug'); + source = removeHasObjectSpliceBug(source); // simplify the `lodash` function source = replaceFunction(source, 'lodash', [ @@ -1847,9 +1994,9 @@ // remove all `lodash.prototype` additions source = source .replace(/(?:\s*\/\/.*)*\n( *)forOwn\(lodash, *function\(func, *methodName\)[\s\S]+?\n\1}.+/g, '') - .replace(/(?:\s*\/\/.*)*\n( *)forEach\(\['[\s\S]+?\n\1}.+/g, '') - .replace(/(?:\s*\/\/.*)*\s*mixin\(lodash\).+\n/, '') - .replace(/(?:\s*\/\/.*)*\s*lodash\.prototype.+\n/, ''); + .replace(/(?:\s*\/\/.*)*\n( *)each\(\['[\s\S]+?\n\1}.+/g, '') + .replace(/(?:\s*\/\/.*)*\s*lodash\.prototype.+\n/g, '') + .replace(/(?:\s*\/\/.*)*\s*mixin\(lodash\).+\n/, ''); } // assign debug source before further modifications that rely on the minifier @@ -1866,6 +2013,7 @@ } if (isRemoved(source, 'isPlainObject')) { source = removeVar(source, 'getPrototypeOf'); + source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var iteratesOwnLast;|.+?iteratesOwnLast *=.+/g, ''); } if (isRemoved(source, 'keys')) { source = removeFunction(source, 'shimKeys'); @@ -1882,7 +2030,8 @@ } if ((source.match(/\bcreateIterator\b/g) || []).length < 2) { source = removeFunction(source, 'createIterator'); - source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var noArgsEnum;|.+?noArgsEnum *=.+/g, ''); + source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var hasDontEnumBug;|.+?hasDontEnumBug *=.+/g, ''); + source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var nonEnumArgs;|.+?nonEnumArgs *=.+/g, ''); } if (isRemoved(source, 'createIterator', 'bind', 'keys', 'template')) { source = removeVar(source, 'isBindFast'); @@ -1891,18 +2040,12 @@ if (isRemoved(source, 'createIterator', 'bind', 'isArray', 'isPlainObject', 'keys', 'template')) { source = removeVar(source, 'reNative'); } - if (isRemoved(source, 'createIterator', 'isEqual')) { - source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var hasDontEnumBug;|.+?hasDontEnumBug *=.+/g, ''); - } - if (isRemoved(source, 'createIterator', 'isPlainObject')) { - source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var iteratesOwnLast;|.+?iteratesOwnLast *=.+/g, ''); - } if (isRemoved(source, 'createIterator', 'keys')) { source = removeVar(source, 'nativeKeys'); source = removeKeysOptimization(source); } - if (!source.match(/var (?:hasDontEnumBug|iteratesOwnLast|noArgsEnum)\b/g)) { - // remove `hasDontEnumBug`, `iteratesOwnLast`, and `noArgsEnum` assignments + if (!source.match(/var (?:hasDontEnumBug|iteratesOwnLast|nonEnumArgs)\b/g)) { + // remove `hasDontEnumBug`, `iteratesOwnLast`, and `nonEnumArgs` assignments source = source.replace(/ *\(function\(\) *{[\s\S]+?}\(1\)\);\n/, ''); } } diff --git a/build/post-compile.js b/build/post-compile.js index 4d7ba68b8d..6ea6cda9f2 100644 --- a/build/post-compile.js +++ b/build/post-compile.js @@ -9,11 +9,11 @@ var licenseTemplate = { 'lodash': '/*!\n' + - ' Lo-Dash @VERSION lodash.com/license\n' + + ' Lo-Dash <%= VERSION %> lodash.com/license\n' + ' Underscore.js 1.4.3 underscorejs.org/LICENSE\n' + '*/', 'underscore': - '/*! Underscore.js @VERSION underscorejs.org/LICENSE */' + '/*! Underscore.js <%= VERSION %> underscorejs.org/LICENSE */' }; /*--------------------------------------------------------------------------*/ @@ -32,13 +32,17 @@ // correct overly aggressive Closure Compiler advanced optimizations source = source.replace(/prototype\s*=\s*{\s*valueOf\s*:\s*1\s*}/, 'prototype={valueOf:1,y:1}'); - // unescape properties (i.e. foo["bar"] => foo.bar) + // unescape properties (e.g. foo["bar"] => foo.bar) source = source.replace(/(\w)\["([^."]+)"\]/g, function(match, left, right) { return /\W/.test(right) ? match : (left + '.' + right); }); - // correct AMD module definition for AMD build optimizers - source = source.replace(/("function")\s*==\s*(typeof define)\s*&&\s*\(?\s*("object")\s*==\s*(typeof define\.amd)\s*&&\s*(define\.amd)\s*\)?/, '$2==$1&&$4==$3&&$5'); + // flip `typeof` expressions to help optimize Safari and + // correct the AMD module definition for AMD build optimizers + // (e.g. from `"number" == typeof x` to `typeof x == "number") + source = source.replace(/(return)?("[^"]+")\s*([!=]=)\s*(typeof(?:\s*\([^)]+\)|\s+[\w.]+))/g, function(match, ret, type, equality, expression) { + return (ret ? ret + ' ' : '') + expression + equality + type; + }); // add trailing semicolon if (source) { @@ -51,7 +55,7 @@ } // add copyright/license header return licenseTemplate[/call\(this\);?$/.test(source) ? 'underscore' : 'lodash'] - .replace('@VERSION', snippet[2]) + '\n;' + source; + .replace('<%= VERSION %>', snippet[2]) + '\n;' + source; } /*--------------------------------------------------------------------------*/ diff --git a/build/post-install.js b/build/post-install.js index 9656f5af47..8b59affce7 100644 --- a/build/post-install.js +++ b/build/post-install.js @@ -20,11 +20,14 @@ var closureId = 'a2787b470c577cee2404d186c562dd9835f779f5'; /** The Git object ID of `uglifyjs.tar.gz` */ - var uglifyId = '3390b259e04829538e4d3635d12b317dd6103eca'; + var uglifyId = '7ecae09d413eb48dd5785fe877f24e60ac3bbcef'; /** The media type for raw blob data */ var mediaType = 'application/vnd.github.v3.raw'; + /** Reassign `existsSync` for older versions of Node */ + fs.existsSync || (fs.existsSync = path.existsSync); + /** Used to reference parts of the blob href */ var location = (function() { var host = 'api.github.com', @@ -101,7 +104,8 @@ exec('npm -g root', function(exception, stdout) { if (!exception) { try { - var isGlobal = path.resolve(basePath, '..') == fs.realpathSync(stdout.trim()); + var root = stdout.trim(), + isGlobal = fs.existsSync(root) && path.resolve(basePath, '..') == fs.realpathSync(root); } catch(e) { exception = e; } diff --git a/build/pre-compile.js b/build/pre-compile.js index c877082cb7..6bcc7ea18f 100644 --- a/build/pre-compile.js +++ b/build/pre-compile.js @@ -41,7 +41,7 @@ 'hasDontEnumBug', 'isKeysFast', 'objectLoop', - 'noArgsEnum', + 'nonEnumArgs', 'noCharByIndex', 'shadowed', 'top', @@ -68,8 +68,8 @@ 'bind', 'bindAll', 'bindKey', - 'chain', 'clone', + 'cloneDeep', 'collect', 'compact', 'compose', @@ -188,8 +188,9 @@ 'wrap', 'zip', - // property used by the `lodash underscore` build + // properties used by the `backbone` and `underscore` builds '__chain__', + 'chain', // properties used by underscore.js '_chain', diff --git a/doc/README.md b/doc/README.md index b85a94e74d..1341019713 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,4 +1,4 @@ -# Lo-Dash v1.0.0-rc.2 +# Lo-Dash v1.0.0-rc.3 @@ -36,9 +36,7 @@ ## `Chaining` * [`_`](#_value) -* [`_.chain`](#_chainvalue) * [`_.tap`](#_tapvalue-interceptor) -* [`_.prototype.chain`](#_prototypechain) * [`_.prototype.toString`](#_prototypetostring) * [`_.prototype.value`](#_prototypevalueof) * [`_.prototype.valueOf`](#_prototypevalueof) @@ -109,6 +107,7 @@ ## `Objects` * [`_.assign`](#_assignobject--source1-source2-) * [`_.clone`](#_clonevalue-deep) +* [`_.cloneDeep`](#_clonedeepvalue) * [`_.defaults`](#_defaultsobject--default1-default2-) * [`_.extend`](#_assignobject--source1-source2-) * [`_.forIn`](#_forinobject--callbackidentity-thisarg) @@ -187,7 +186,7 @@ ### `_.compact(array)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2634 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2714 "View in source") [Ⓣ][1] Creates an array with all falsey values of `array` removed. The values `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey. @@ -211,7 +210,7 @@ _.compact([0, 1, false, 2, '', 3]); ### `_.difference(array [, array1, array2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2664 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2744 "View in source") [Ⓣ][1] Creates an array of `array` elements not present in the other arrays using strict equality for comparisons, i.e. `===`. @@ -236,7 +235,7 @@ _.difference([1, 2, 3, 4, 5], [5, 2, 10]); ### `_.first(array [, n])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2699 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2779 "View in source") [Ⓣ][1] Gets the first element of the `array`. Pass `n` to return the first `n` elements of the `array`. @@ -264,7 +263,7 @@ _.first([5, 4, 3, 2, 1]); ### `_.flatten(array, shallow)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2726 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2806 "View in source") [Ⓣ][1] Flattens a nested array *(the nesting can be to any depth)*. If `shallow` is truthy, `array` will only be flattened a single level. @@ -292,7 +291,7 @@ _.flatten([1, [2], [3, [[4]]]], true); ### `_.indexOf(array, value [, fromIndex=0])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2768 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2848 "View in source") [Ⓣ][1] Gets the index at which the first occurrence of `value` is found using strict equality for comparisons, i.e. `===`. If the `array` is already sorted, passing `true` for `fromIndex` will run a faster binary search. @@ -324,7 +323,7 @@ _.indexOf([1, 1, 2, 2, 3, 3], 2, true); ### `_.initial(array [, n=1])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2803 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2883 "View in source") [Ⓣ][1] Gets all but the last element of `array`. Pass `n` to exclude the last `n` elements from the result. @@ -349,7 +348,7 @@ _.initial([3, 2, 1]); ### `_.intersection([array1, array2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2827 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2907 "View in source") [Ⓣ][1] Computes the intersection of all the passed-in arrays using strict equality for comparisons, i.e. `===`. @@ -357,7 +356,7 @@ Computes the intersection of all the passed-in arrays using strict equality for 1. `[array1, array2, ...]` *(Array)*: Arrays to process. #### Returns -*(Array)*: Returns a new array of unique elements, in order, that are present in **all** of the arrays. +*(Array)*: Returns a new array of unique elements that are present in **all** of the arrays. #### Example ```js @@ -373,7 +372,7 @@ _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); ### `_.last(array [, n])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2865 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2960 "View in source") [Ⓣ][1] Gets the last element of the `array`. Pass `n` to return the last `n` elements of the `array`. @@ -398,7 +397,7 @@ _.last([3, 2, 1]); ### `_.lastIndexOf(array, value [, fromIndex=array.length-1])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2892 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2987 "View in source") [Ⓣ][1] Gets the index at which the last occurrence of `value` is found using strict equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the offset from the end of the collection. @@ -427,7 +426,7 @@ _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3); ### `_.object(keys [, values=[]])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2922 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3017 "View in source") [Ⓣ][1] Creates an object composed from arrays of `keys` and `values`. Pass either a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`, or two arrays, one of `keys` and one of corresponding `values`. @@ -452,7 +451,7 @@ _.object(['moe', 'larry', 'curly'], [30, 40, 50]); ### `_.range([start=0], end [, step=1])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2967 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3062 "View in source") [Ⓣ][1] Creates an array of numbers *(positive and/or negative)* progressing from `start` up to but not including `stop`. This method is a port of Python's `range()` function. See http://docs.python.org/library/functions.html#range. @@ -490,7 +489,7 @@ _.range(0); ### `_.rest(array [, n=1])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3006 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3101 "View in source") [Ⓣ][1] The opposite of `_.initial`, this method gets all but the first value of `array`. Pass `n` to exclude the first `n` values from the result. @@ -518,7 +517,7 @@ _.rest([3, 2, 1]); ### `_.sortedIndex(array, value [, callback=identity|property, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3050 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3145 "View in source") [Ⓣ][1] Uses a binary search to determine the smallest index at which the `value` should be inserted into `array` in order to maintain the sort order of the sorted `array`. If `callback` is passed, it will be executed for `value` and each element in `array` to compute their sort ranking. The `callback` is bound to `thisArg` and invoked with one argument; *(value)*. The `callback` argument may also be the name of a property to order by. @@ -562,7 +561,7 @@ _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) { ### `_.union([array1, array2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3081 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3177 "View in source") [Ⓣ][1] Computes the union of the passed-in arrays using strict equality for comparisons, i.e. `===`. @@ -586,7 +585,7 @@ _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); ### `_.uniq(array [, isSorted=false, callback=identity, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3115 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3211 "View in source") [Ⓣ][1] Creates a duplicate-value-free version of the `array` using strict equality for comparisons, i.e. `===`. If the `array` is already sorted, passing `true` for `isSorted` will run a faster algorithm. If `callback` is passed, each element of `array` is passed through a callback` before uniqueness is computed. The `callback` is bound to `thisArg` and invoked with three arguments; *(value, index, array)*. @@ -625,7 +624,7 @@ _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math); ### `_.without(array [, value1, value2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3173 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3270 "View in source") [Ⓣ][1] Creates an array with all occurrences of the passed values removed using strict equality for comparisons, i.e. `===`. @@ -650,7 +649,7 @@ _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); ### `_.zip([array1, array2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3204 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3301 "View in source") [Ⓣ][1] Groups the elements of each array at their corresponding indexes. Useful for separate data sources that are coordinated through matching array indexes. For a matrix of nested arrays, `_.zip.apply(...)` can transpose the matrix in a similar fashion. @@ -681,48 +680,23 @@ _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]); ### `_(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L254 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L278 "View in source") [Ⓣ][1] -The `lodash` function. +Creates a `lodash` object, that wraps the given `value`, to enable method chaining. -#### Arguments -1. `value` *(Mixed)*: The value to wrap in a `lodash` instance. - -#### Returns -*(Object)*: Returns a `lodash` instance. +The chainable wrapper functions are:
+`after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, `compose`, `concat`, `countBy`, `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`, `forEach`, `forIn`, `forOwn`, `functions`, `groupBy`, `initial`, `intersection`, `invert`, `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`, `once`, `pairs`, `partial`, `pick`, `pluck`, `push`, `range`, `reject`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `union`, `uniq`, `unshift`, `values`, `where`, `without`, `wrap`, and `zip` -* * * - - +The non-chainable wrapper functions are:
+`clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `has`, `identity`, `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`, `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`, `lastIndexOf`, `mixin`, `noConflict`, `pop`, `random`, `reduce`, `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `template`, `unescape`, and `uniqueId` - - - -### `_.chain(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4074 "View in source") [Ⓣ][1] - -Wraps the value in a `lodash` wrapper object. +The wrapper functions `first` and `last` return wrapped values when `n` is passed, otherwise they return unwrapped values. #### Arguments -1. `value` *(Mixed)*: The value to wrap. +1. `value` *(Mixed)*: The value to wrap in a `lodash` instance. #### Returns -*(Object)*: Returns the wrapper object. - -#### Example -```js -var stooges = [ - { 'name': 'moe', 'age': 40 }, - { 'name': 'larry', 'age': 50 }, - { 'name': 'curly', 'age': 60 } -]; - -var youngest = _.chain(stooges) - .sortBy(function(stooge) { return stooge.age; }) - .map(function(stooge) { return stooge.name + ' is ' + stooge.age; }) - .first(); -// => 'moe is 40' -``` +*(Object)*: Returns a `lodash` instance. * * * @@ -732,7 +706,7 @@ var youngest = _.chain(stooges) ### `_.tap(value, interceptor)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4099 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4168 "View in source") [Ⓣ][1] Invokes `interceptor` with the `value` as the first argument, and then returns `value`. The purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain. @@ -759,32 +733,10 @@ _.chain([1, 2, 3, 200]) - - -### `_.prototype.chain()` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4122 "View in source") [Ⓣ][1] - -This function returns the wrapper object. Note: This function is defined to ensure the existing wrapper object is returned, instead of creating a new wrapper object like the `_.chain` method does. - -#### Returns -*(Mixed)*: Returns the wrapper object. - -#### Example -```js -var wrapped = _([1, 2, 3]); -wrapped === wrapped.chain(); -// => true -``` - -* * * - - - - ### `_.prototype.toString()` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4138 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4185 "View in source") [Ⓣ][1] Produces the `toString` result of the wrapped value. @@ -805,7 +757,7 @@ _([1, 2, 3]).toString(); ### `_.prototype.valueOf()` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4155 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4202 "View in source") [Ⓣ][1] Extracts the wrapped value. @@ -836,7 +788,7 @@ _([1, 2, 3]).valueOf(); ### `_.contains(collection, target [, fromIndex=0])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1904 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1953 "View in source") [Ⓣ][1] Checks if a given `target` element is present in a `collection` using strict equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the offset from the end of the collection. @@ -874,7 +826,7 @@ _.contains('curly', 'ur'); ### `_.countBy(collection, callback|property [, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1951 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2000 "View in source") [Ⓣ][1] Creates an object composed of keys returned from running each element of `collection` through a `callback`. The corresponding value of each key is the number of times the key was returned by `callback`. The `callback` is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. The `callback` argument may also be the name of a property to count by *(e.g. 'length')*. @@ -906,7 +858,7 @@ _.countBy(['one', 'two', 'three'], 'length'); ### `_.every(collection [, callback=identity, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1980 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2030 "View in source") [Ⓣ][1] Checks if the `callback` returns a truthy value for **all** elements of a `collection`. The `callback` is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. @@ -935,7 +887,7 @@ _.every([true, 1, null, 'yes'], Boolean); ### `_.filter(collection [, callback=identity, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2019 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2069 "View in source") [Ⓣ][1] Examines each element in a `collection`, returning an array of all elements the `callback` returns truthy for. The `callback` is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. @@ -964,7 +916,7 @@ var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }) ### `_.find(collection [, callback=identity, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2063 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2113 "View in source") [Ⓣ][1] Examines each element in a `collection`, returning the first one the `callback` returns truthy for. The function returns as soon as it finds an acceptable element, and does not iterate over the entire `collection`. The `callback` is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. @@ -993,7 +945,7 @@ var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); ### `_.forEach(collection [, callback=identity, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2097 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2148 "View in source") [Ⓣ][1] Iterates over a `collection`, executing the `callback` for each element in the `collection`. The `callback` is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. Callbacks may exit iteration early by explicitly returning `false`. @@ -1025,7 +977,7 @@ _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); ### `_.groupBy(collection, callback|property [, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2125 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2190 "View in source") [Ⓣ][1] Creates an object composed of keys returned from running each element of `collection` through a `callback`. The corresponding value of each key is an array of elements passed to `callback` that returned the key. The `callback` is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. The `callback` argument may also be the name of a property to group by *(e.g. 'length')*. @@ -1057,7 +1009,7 @@ _.groupBy(['one', 'two', 'three'], 'length'); ### `_.invoke(collection, methodName [, arg1, arg2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2157 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2223 "View in source") [Ⓣ][1] Invokes the method named by `methodName` on each element in the `collection`, returning an array of the results of each invoked method. Additional arguments will be passed to each invoked method. If `methodName` is a function it will be invoked for, and `this` bound to, each element in the `collection`. @@ -1086,7 +1038,7 @@ _.invoke([123, 456], String.prototype.split, ''); ### `_.map(collection [, callback=identity, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2189 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2255 "View in source") [Ⓣ][1] Creates an array of values by running each element in the `collection` through a `callback`. The `callback` is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. @@ -1118,7 +1070,7 @@ _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); ### `_.max(collection [, callback, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2231 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2297 "View in source") [Ⓣ][1] Retrieves the maximum value of an `array`. If `callback` is passed, it will be executed for each value in the `array` to generate the criterion by which the value is ranked. The `callback` is bound to `thisArg` and invoked with three arguments; *(value, index, collection)*. @@ -1150,7 +1102,7 @@ _.max(stooges, function(stooge) { return stooge.age; }); ### `_.min(collection [, callback, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2277 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2343 "View in source") [Ⓣ][1] Retrieves the minimum value of an `array`. If `callback` is passed, it will be executed for each value in the `array` to generate the criterion by which the value is ranked. The `callback` is bound to `thisArg` and invoked with three arguments; *(value, index, collection)*. @@ -1176,7 +1128,7 @@ _.min([10, 5, 100, 2, 1000]); ### `_.pluck(collection, property)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2326 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2392 "View in source") [Ⓣ][1] Retrieves the value of a specified property from all elements in the `collection`. @@ -1207,7 +1159,7 @@ _.pluck(stooges, 'name'); ### `_.reduce(collection [, callback=identity, accumulator, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2350 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2416 "View in source") [Ⓣ][1] Boils down a `collection` to a single value. The initial state of the reduction is `accumulator` and each successive step of it should be returned by the `callback`. The `callback` is bound to `thisArg` and invoked with `4` arguments; for arrays they are *(accumulator, value, index|key, collection)*. @@ -1237,7 +1189,7 @@ var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; }); ### `_.reduceRight(collection [, callback=identity, accumulator, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2379 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2458 "View in source") [Ⓣ][1] The right-associative version of `_.reduce`. @@ -1268,7 +1220,7 @@ var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []); ### `_.reject(collection [, callback=identity, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2417 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2496 "View in source") [Ⓣ][1] The opposite of `_.filter`, this method returns the values of a `collection` that `callback` does **not** return truthy for. @@ -1294,7 +1246,7 @@ var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); ### `_.shuffle(collection)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2438 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2517 "View in source") [Ⓣ][1] Creates an array of shuffled `array` values, using a version of the Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. @@ -1318,7 +1270,7 @@ _.shuffle([1, 2, 3, 4, 5, 6]); ### `_.size(collection)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2470 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2549 "View in source") [Ⓣ][1] Gets the size of the `collection` by returning `collection.length` for arrays and array-like objects or the number of own enumerable properties for objects. @@ -1348,7 +1300,7 @@ _.size('curly'); ### `_.some(collection [, callback=identity, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2495 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2574 "View in source") [Ⓣ][1] Checks if the `callback` returns a truthy value for **any** element of a `collection`. The function returns as soon as it finds passing value, and does not iterate over the entire `collection`. The `callback` is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. @@ -1377,7 +1329,7 @@ _.some([null, 0, 'yes', false], Boolean); ### `_.sortBy(collection, callback|property [, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2541 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2620 "View in source") [Ⓣ][1] Creates an array, stable sorted in ascending order by the results of running each element of `collection` through a `callback`. The `callback` is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. The `callback` argument may also be the name of a property to sort by *(e.g. 'length')*. @@ -1409,7 +1361,7 @@ _.sortBy(['larry', 'brendan', 'moe'], 'length'); ### `_.toArray(collection)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2573 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2653 "View in source") [Ⓣ][1] Converts the `collection` to an array. @@ -1433,7 +1385,7 @@ Converts the `collection` to an array. ### `_.where(collection, properties)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2604 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2684 "View in source") [Ⓣ][1] Examines each element in a `collection`, returning an array of all elements that contain the given `properties`. @@ -1471,7 +1423,7 @@ _.where(stooges, { 'age': 40 }); ### `_.after(n, func)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3237 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3334 "View in source") [Ⓣ][1] Creates a function that is restricted to executing `func` only after it is called `n` times. The `func` is executed with the `this` binding of the created function. @@ -1499,7 +1451,7 @@ _.forEach(notes, function(note) { ### `_.bind(func [, thisArg, arg1, arg2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3270 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3367 "View in source") [Ⓣ][1] Creates a function that, when called, invokes `func` with the `this` binding of `thisArg` and prepends any additional `bind` arguments to those passed to the bound function. @@ -1530,7 +1482,7 @@ func(); ### `_.bindAll(object [, methodName1, methodName2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3300 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3397 "View in source") [Ⓣ][1] Binds methods on `object` to `object`, overwriting the existing method. If no method names are provided, all the function properties of `object` will be bound. @@ -1561,7 +1513,7 @@ jQuery('#lodash_button').on('click', buttonView.onClick); ### `_.bindKey(object, key [, arg1, arg2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3346 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3443 "View in source") [Ⓣ][1] Creates a function that, when called, invokes the method at `object[key]` and prepends any additional `bindKey` arguments to those passed to the bound function. This method differs from `_.bind` by allowing bound functions to reference methods that will be redefined or don't yet exist. See http://michaux.ca/articles/lazy-function-definition-pattern. @@ -1602,7 +1554,7 @@ func(); ### `_.compose([func1, func2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3369 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3466 "View in source") [Ⓣ][1] Creates a function that is the composition of the passed functions, where each function consumes the return value of the function that follows. In math terms, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`. Each function is executed with the `this` binding of the composed function. @@ -1629,7 +1581,7 @@ welcome('moe'); ### `_.debounce(func, wait, immediate)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3402 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3499 "View in source") [Ⓣ][1] Creates a function that will delay the execution of `func` until after `wait` milliseconds have elapsed since the last time it was invoked. Pass `true` for `immediate` to cause debounce to invoke `func` on the leading, instead of the trailing, edge of the `wait` timeout. Subsequent calls to the debounced function will return the result of the last `func` call. @@ -1655,7 +1607,7 @@ jQuery(window).on('resize', lazyLayout); ### `_.defer(func [, arg1, arg2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3466 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3563 "View in source") [Ⓣ][1] Defers executing the `func` function until the current call stack has cleared. Additional arguments will be passed to `func` when it is invoked. @@ -1680,7 +1632,7 @@ _.defer(function() { alert('deferred'); }); ### `_.delay(func, wait [, arg1, arg2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3446 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3543 "View in source") [Ⓣ][1] Executes the `func` function after `wait` milliseconds. Additional arguments will be passed to `func` when it is invoked. @@ -1707,7 +1659,7 @@ _.delay(log, 1000, 'logged later'); ### `_.memoize(func [, resolver])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3490 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3587 "View in source") [Ⓣ][1] Creates a function that memoizes the result of `func`. If `resolver` is passed, it will be used to determine the cache key for storing the result based on the arguments passed to the memoized function. By default, the first argument passed to the memoized function is used as the cache key. The `func` is executed with the `this` binding of the memoized function. @@ -1733,7 +1685,7 @@ var fibonacci = _.memoize(function(n) { ### `_.once(func)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3517 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3614 "View in source") [Ⓣ][1] Creates a function that is restricted to execute `func` once. Repeat calls to the function will return the value of the first call. The `func` is executed with the `this` binding of the created function. @@ -1759,7 +1711,7 @@ initialize(); ### `_.partial(func [, arg1, arg2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3552 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3649 "View in source") [Ⓣ][1] Creates a function that, when called, invokes `func` with any additional `partial` arguments prepended to those passed to the new function. This method is similar to `bind`, except it does **not** alter the `this` binding. @@ -1786,7 +1738,7 @@ hi('moe'); ### `_.throttle(func, wait)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3574 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3671 "View in source") [Ⓣ][1] Creates a function that, when executed, will only call the `func` function at most once per every `wait` milliseconds. If the throttled function is invoked more than once during the `wait` timeout, `func` will also be called on the trailing edge of the timeout. Subsequent calls to the throttled function will return the result of the last `func` call. @@ -1811,7 +1763,7 @@ jQuery(window).on('scroll', throttled); ### `_.wrap(value, wrapper)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3627 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3724 "View in source") [Ⓣ][1] Creates a function that passes `value` to the `wrapper` function as its first argument. Additional arguments passed to the function are appended to those passed to the `wrapper` function. The `wrapper` is executed with the `this` binding of the created function. @@ -1847,7 +1799,7 @@ hello(); ### `_.assign(object [, source1, source2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L786 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L813 "View in source") [Ⓣ][1] Assigns own enumerable properties of source object(s) to the `destination` object. Subsequent sources will overwrite propery assignments of previous sources. @@ -1875,9 +1827,9 @@ _.assign({ 'name': 'moe' }, { 'age': 40 }); ### `_.clone(value, deep)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L985 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1003 "View in source") [Ⓣ][1] -Creates a clone of `value`. If `deep` is `true`, all nested objects will also be cloned, otherwise they will be assigned by reference. Functions and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and objects created by constructors other than `Object` are cloned to plain `Object` objects. Note: Lo-Dash's deep clone functionality is loosely based on the structured clone algorithm. See http://www.w3.org/TR/html5/common-dom-interfaces.html#internal-structured-cloning-algorithm. +Creates a clone of `value`. If `deep` is `true`, nested objects will also be cloned, otherwise they will be assigned by reference. #### Arguments 1. `value` *(Mixed)*: The value to clone. @@ -1894,9 +1846,6 @@ var stooges = [ { 'name': 'curly', 'age': 60 } ]; -_.clone({ 'name': 'moe' }); -// => { 'name': 'moe' } - var shallow = _.clone(stooges); shallow[0] === stooges[0]; // => true @@ -1911,10 +1860,43 @@ deep[0] === stooges[0]; + + +### `_.cloneDeep(value)` +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1098 "View in source") [Ⓣ][1] + +Creates a deep clone of `value`. Functions and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and objects created by constructors other than `Object` are cloned to plain `Object` objects. + +Note: This function is loosely based on the structured clone algorithm. See http://www.w3.org/TR/html5/common-dom-interfaces.html#internal-structured-cloning-algorithm. + +#### Arguments +1. `value` *(Mixed)*: The value to deep clone. + +#### Returns +*(Mixed)*: Returns the deep cloned `value`. + +#### Example +```js +var stooges = [ + { 'name': 'moe', 'age': 40 }, + { 'name': 'larry', 'age': 50 }, + { 'name': 'curly', 'age': 60 } +]; + +var deep = _.cloneDeep(stooges); +deep[0] === stooges[0]; +// => false +``` + +* * * + + + + ### `_.defaults(object [, default1, default2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1073 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1120 "View in source") [Ⓣ][1] Assigns own enumerable properties of source object(s) to the `destination` object for all `destination` properties that resolve to `null`/`undefined`. Once a property is set, additional defaults of the same property will be ignored. @@ -1940,7 +1922,7 @@ _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); ### `_.forIn(object [, callback=identity, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L842 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L869 "View in source") [Ⓣ][1] Iterates over `object`'s own and inherited enumerable properties, executing the `callback` for each property. The `callback` is bound to `thisArg` and invoked with three arguments; *(value, key, object)*. Callbacks may exit iteration early by explicitly returning `false`. @@ -1976,7 +1958,7 @@ _.forIn(new Dog('Dagny'), function(value, key) { ### `_.forOwn(object [, callback=identity, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L866 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L893 "View in source") [Ⓣ][1] Iterates over an object's own enumerable properties, executing the `callback` for each property. The `callback` is bound to `thisArg` and invoked with three arguments; *(value, key, object)*. Callbacks may exit iteration early by explicitly returning `false`. @@ -2004,7 +1986,7 @@ _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { ### `_.functions(object)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1092 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1139 "View in source") [Ⓣ][1] Creates a sorted array of all enumerable properties, own and inherited, of `object` that have function values. @@ -2031,7 +2013,7 @@ _.functions(_); ### `_.has(object, property)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1117 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1164 "View in source") [Ⓣ][1] Checks if the specified object `property` exists and is a direct property, instead of an inherited property. @@ -2056,7 +2038,7 @@ _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); ### `_.invert(object)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1134 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1181 "View in source") [Ⓣ][1] Creates an object composed of the inverted keys and values of the given `object`. @@ -2080,7 +2062,7 @@ _.invert({ 'first': 'Moe', 'second': 'Larry', 'third': 'Curly' }); ### `_.isArguments(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L804 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L831 "View in source") [Ⓣ][1] Checks if `value` is an `arguments` object. @@ -2107,7 +2089,7 @@ _.isArguments([1, 2, 3]); ### `_.isArray(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1158 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1205 "View in source") [Ⓣ][1] Checks if `value` is an array. @@ -2134,7 +2116,7 @@ _.isArray([1, 2, 3]); ### `_.isBoolean(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1175 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1224 "View in source") [Ⓣ][1] Checks if `value` is a boolean *(`true` or `false`)* value. @@ -2158,7 +2140,7 @@ _.isBoolean(null); ### `_.isDate(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1192 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1241 "View in source") [Ⓣ][1] Checks if `value` is a date. @@ -2182,7 +2164,7 @@ _.isDate(new Date); ### `_.isElement(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1209 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1258 "View in source") [Ⓣ][1] Checks if `value` is a DOM element. @@ -2206,7 +2188,7 @@ _.isElement(document.body); ### `_.isEmpty(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1234 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1283 "View in source") [Ⓣ][1] Checks if `value` is empty. Arrays, strings, or `arguments` objects with a length of `0` and objects with no own enumerable properties are considered "empty". @@ -2236,7 +2218,7 @@ _.isEmpty(''); ### `_.isEqual(a, b)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1276 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1325 "View in source") [Ⓣ][1] Performs a deep comparison between two values to determine if they are equivalent to each other. @@ -2267,9 +2249,11 @@ _.isEqual(moe, clone); ### `_.isFinite(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1428 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1477 "View in source") [Ⓣ][1] -Checks if `value` is, or can be coerced to, a finite number. Note: This is not the same as native `isFinite`, which will return true for booleans and empty strings. See http://es5.github.com/#x15.1.2.5. +Checks if `value` is, or can be coerced to, a finite number. + +Note: This is not the same as native `isFinite`, which will return true for booleans and empty strings. See http://es5.github.com/#x15.1.2.5. #### Arguments 1. `value` *(Mixed)*: The value to check. @@ -2303,7 +2287,7 @@ _.isFinite(Infinity); ### `_.isFunction(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1445 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1494 "View in source") [Ⓣ][1] Checks if `value` is a function. @@ -2327,9 +2311,11 @@ _.isFunction(_); ### `_.isNaN(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1508 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1557 "View in source") [Ⓣ][1] + +Checks if `value` is `NaN`. -Checks if `value` is `NaN`. Note: This is not the same as native `isNaN`, which will return `true` for `undefined` and other values. See http://es5.github.com/#x15.1.2.4. +Note: This is not the same as native `isNaN`, which will return `true` for `undefined` and other values. See http://es5.github.com/#x15.1.2.4. #### Arguments 1. `value` *(Mixed)*: The value to check. @@ -2360,7 +2346,7 @@ _.isNaN(undefined); ### `_.isNull(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1530 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1579 "View in source") [Ⓣ][1] Checks if `value` is `null`. @@ -2387,7 +2373,7 @@ _.isNull(undefined); ### `_.isNumber(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1547 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1596 "View in source") [Ⓣ][1] Checks if `value` is a number. @@ -2411,7 +2397,7 @@ _.isNumber(8.4 * 5); ### `_.isObject(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1475 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1524 "View in source") [Ⓣ][1] Checks if `value` is the language type of Object. *(e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)* @@ -2441,7 +2427,7 @@ _.isObject(1); ### `_.isPlainObject(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1575 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1624 "View in source") [Ⓣ][1] Checks if a given `value` is an object created by the `Object` constructor. @@ -2476,7 +2462,7 @@ _.isPlainObject({ 'name': 'moe', 'age': 40 }); ### `_.isRegExp(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1600 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1649 "View in source") [Ⓣ][1] Checks if `value` is a regular expression. @@ -2500,7 +2486,7 @@ _.isRegExp(/moe/); ### `_.isString(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1617 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1666 "View in source") [Ⓣ][1] Checks if `value` is a string. @@ -2524,7 +2510,7 @@ _.isString('moe'); ### `_.isUndefined(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1634 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1683 "View in source") [Ⓣ][1] Checks if `value` is `undefined`. @@ -2548,7 +2534,7 @@ _.isUndefined(void 0); ### `_.keys(object)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1651 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1700 "View in source") [Ⓣ][1] Creates an array composed of the own enumerable property names of `object`. @@ -2572,7 +2558,7 @@ _.keys({ 'one': 1, 'two': 2, 'three': 3 }); ### `_.merge(object [, source1, source2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1689 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1738 "View in source") [Ⓣ][1] Merges enumerable properties of the source object(s) into the `destination` object. Subsequent sources will overwrite propery assignments of previous sources. @@ -2607,7 +2593,7 @@ _.merge(stooges, ages); ### `_.omit(object, callback|[prop1, prop2, ..., thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1763 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1812 "View in source") [Ⓣ][1] Creates a shallow clone of `object` excluding the specified properties. Property names may be specified as individual arguments or as arrays of property names. If `callback` is passed, it will be executed for each property in the `object`, omitting the properties `callback` returns truthy for. The `callback` is bound to `thisArg` and invoked with three arguments; *(value, key, object)*. @@ -2638,7 +2624,7 @@ _.omit({ 'name': 'moe', '_hint': 'knucklehead', '_seed': '96c4eb' }, function(va ### `_.pairs(object)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1797 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1846 "View in source") [Ⓣ][1] Creates a two dimensional array of the given object's key-value pairs, i.e. `[[key1, value1], [key2, value2]]`. @@ -2662,7 +2648,7 @@ _.pairs({ 'moe': 30, 'larry': 40, 'curly': 50 }); ### `_.pick(object, callback|[prop1, prop2, ..., thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1830 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1879 "View in source") [Ⓣ][1] Creates a shallow clone of `object` composed of the specified properties. Property names may be specified as individual arguments or as arrays of property names. If `callback` is passed, it will be executed for each property in the `object`, picking the properties `callback` returns truthy for. The `callback` is bound to `thisArg` and invoked with three arguments; *(value, key, object)*. @@ -2693,7 +2679,7 @@ _.pick({ 'name': 'moe', '_hint': 'knucklehead', '_seed': '96c4eb' }, function(va ### `_.values(object)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1867 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1916 "View in source") [Ⓣ][1] Creates an array composed of the own enumerable property values of `object`. @@ -2724,7 +2710,7 @@ _.values({ 'one': 1, 'two': 2, 'three': 3 }); ### `_.escape(string)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3651 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3748 "View in source") [Ⓣ][1] Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their corresponding HTML entities. @@ -2748,9 +2734,9 @@ _.escape('Moe, Larry & Curly'); ### `_.identity(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3671 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3766 "View in source") [Ⓣ][1] -This function returns the first argument passed to it. Note: This function is used throughout Lo-Dash as a default callback. +This function returns the first argument passed to it. #### Arguments 1. `value` *(Mixed)*: Any value. @@ -2773,7 +2759,7 @@ moe === _.identity(moe); ### `_.mixin(object)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3697 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3792 "View in source") [Ⓣ][1] Adds functions properties of `object` to the `lodash` function and chainable wrapper. @@ -2803,7 +2789,7 @@ _('curly').capitalize(); ### `_.noConflict()` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3723 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3818 "View in source") [Ⓣ][1] Reverts the '_' variable to its previous value and returns a reference to the `lodash` function. @@ -2823,7 +2809,7 @@ var lodash = _.noConflict(); ### `_.random([min=0, max=1])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3746 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3841 "View in source") [Ⓣ][1] Produces a random number between `min` and `max` *(inclusive)*. If only one argument is passed, a number between `0` and the given number will be returned. @@ -2851,7 +2837,7 @@ _.random(5); ### `_.result(object, property)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3784 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3879 "View in source") [Ⓣ][1] Resolves the value of `property` on `object`. If `property` is a function it will be invoked and its result returned, else the property value is returned. If `object` is falsey, then `null` is returned. @@ -2886,9 +2872,13 @@ _.result(object, 'stuff'); ### `_.template(text, data, options)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3869 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3964 "View in source") [Ⓣ][1] + +A micro-templating method that handles arbitrary delimiters, preserves whitespace, and correctly escapes quotes within interpolated code. + +Note: In the development build `_.template` utilizes sourceURLs for easier debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl -A micro-templating method that handles arbitrary delimiters, preserves whitespace, and correctly escapes quotes within interpolated code. Note: In the development build `_.template` utilizes sourceURLs for easier debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl Note: Lo-Dash may be used in Chrome extensions by either creating a `lodash csp` build and avoiding `_.template` use, or loading Lo-Dash in a sandboxed page. See http://developer.chrome.com/trunk/extensions/sandboxingEval.html +Note: Lo-Dash may be used in Chrome extensions by either creating a `lodash csp` build and avoiding `_.template` use, or loading Lo-Dash in a sandboxed page. See http://developer.chrome.com/trunk/extensions/sandboxingEval.html #### Arguments 1. `text` *(String)*: The template text. @@ -2960,7 +2950,7 @@ fs.writeFileSync(path.join(cwd, 'jst.js'), '\ ### `_.times(n, callback [, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4000 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4095 "View in source") [Ⓣ][1] Executes the `callback` function `n` times, returning an array of the results of each `callback` execution. The `callback` is bound to `thisArg` and invoked with one argument; *(index)*. @@ -2992,7 +2982,7 @@ _.times(3, function(n) { this.cast(n); }, mage); ### `_.unescape(string)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4026 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4121 "View in source") [Ⓣ][1] The opposite of `_.escape`, this method converts the HTML entities `&`, `<`, `>`, `"`, and `'` in `string` to their corresponding characters. @@ -3016,7 +3006,7 @@ _.unescape('Moe, Larry & Curly'); ### `_.uniqueId([prefix])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4046 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4141 "View in source") [Ⓣ][1] Generates a unique ID. If `prefix` is passed, the ID will be appended to it. @@ -3050,7 +3040,7 @@ _.uniqueId(); ### `_.VERSION` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4318 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4366 "View in source") [Ⓣ][1] *(String)*: The semantic version number. @@ -3062,7 +3052,7 @@ _.uniqueId(); ### `_.templateSettings` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L275 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L299 "View in source") [Ⓣ][1] *(Object)*: By default, the template delimiters used by Lo-Dash are similar to those in embedded Ruby *(ERB)*. Change the following template settings to use alternative delimiters. @@ -3074,7 +3064,7 @@ _.uniqueId(); ### `_.templateSettings.escape` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L284 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L308 "View in source") [Ⓣ][1] *(RegExp)*: Used to detect `data` property values to be HTML-escaped. @@ -3086,7 +3076,7 @@ _.uniqueId(); ### `_.templateSettings.evaluate` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L293 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L317 "View in source") [Ⓣ][1] *(RegExp)*: Used to detect code to be evaluated. @@ -3098,7 +3088,7 @@ _.uniqueId(); ### `_.templateSettings.interpolate` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L302 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L326 "View in source") [Ⓣ][1] *(RegExp)*: Used to detect `data` property values to inject. @@ -3110,7 +3100,7 @@ _.uniqueId(); ### `_.templateSettings.variable` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L311 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L335 "View in source") [Ⓣ][1] *(String)*: Used to reference the data object in the template text. diff --git a/doc/parse.php b/doc/parse.php index 7bca1f165f..fe04c261d6 100644 --- a/doc/parse.php +++ b/doc/parse.php @@ -21,7 +21,7 @@ // generate Markdown $markdown = docdown(array( 'path' => '../' . $file, - 'title' => 'Lo-Dash v1.0.0-rc.2', + 'title' => 'Lo-Dash v1.0.0-rc.3', 'toc' => 'categories', 'url' => 'https://github.com/bestiejs/lodash/blob/master/lodash.js' )); diff --git a/lodash.js b/lodash.js index 1664a12964..f4a8bb12db 100644 --- a/lodash.js +++ b/lodash.js @@ -1,5 +1,5 @@ /*! - * Lo-Dash 1.0.0-rc.2 + * Lo-Dash 1.0.0-rc.3 * (c) 2012 John-David Dalton * Based on Underscore.js 1.4.3 * (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. @@ -116,8 +116,7 @@ stringClass = '[object String]'; /** Detect various environments */ - var isFirefox = !/1/.test(Function('1')), - isIeOpera = !!window.attachEvent, + var isIeOpera = !!window.attachEvent, isV8 = nativeBind && !/\n|true/.test(nativeBind + isIeOpera); /* Detect if `Function#bind` exists and is inferred to be fast (all but V8) */ @@ -151,20 +150,23 @@ arrayRef.splice.call(hasObjectSpliceBug, 0, 1), hasObjectSpliceBug[0]); /** Detect if an `arguments` object's indexes are non-enumerable (IE < 9) */ - var noArgsEnum = true; + var nonEnumArgs = true; (function() { var props = []; function ctor() { this.x = 1; } ctor.prototype = { 'valueOf': 1, 'y': 1 }; for (var prop in new ctor) { props.push(prop); } - for (prop in arguments) { noArgsEnum = !prop; } + for (prop in arguments) { nonEnumArgs = !prop; } hasDontEnumBug = !/valueOf/.test(props); iteratesOwnLast = props[0] != 'x'; }(1)); - /** Detect if an `arguments` object's [[Class]] is unresolvable (Firefox < 4, IE < 9) */ + /** Detect if `arguments` objects are `Object` objects (all but Opera < 10.5) */ + var argsAreObjects = arguments.constructor == Object; + + /** Detect if `arguments` objects [[Class]] is unresolvable (Firefox < 4, IE < 9) */ var noArgsClass = !isArguments(arguments); /** @@ -243,7 +245,29 @@ /*--------------------------------------------------------------------------*/ /** - * The `lodash` function. + * Creates a `lodash` object, that wraps the given `value`, to enable + * method chaining. + * + * The chainable wrapper functions are: + * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, `compose`, + * `concat`, `countBy`, `debounce`, `defaults`, `defer`, `delay`, `difference`, + * `filter`, `flatten`, `forEach`, `forIn`, `forOwn`, `functions`, `groupBy`, + * `initial`, `intersection`, `invert`, `invoke`, `keys`, `map`, `max`, `memoize`, + * `merge`, `min`, `object`, `omit`, `once`, `pairs`, `partial`, `pick`, `pluck`, + * `push`, `range`, `reject`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, + * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `union`, `uniq`, + * `unshift`, `values`, `where`, `without`, `wrap`, and `zip` + * + * The non-chainable wrapper functions are: + * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `has`, `identity`, + * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, `isEmpty`, + * `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`, `isObject`, + * `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`, `lastIndexOf`, + * `mixin`, `noConflict`, `pop`, `random`, `reduce`, `reduceRight`, `result`, + * `shift`, `size`, `some`, `sortedIndex`, `template`, `unescape`, and `uniqueId` + * + * The wrapper functions `first` and `last` return wrapped values when `n` is + * passed, otherwise they return unwrapped values. * * @name _ * @constructor @@ -313,25 +337,6 @@ /*--------------------------------------------------------------------------*/ - /** - * Creates a function from the given `args` and `body` strings. - * - * @private - * @param {String} args The comma separated function arguments. - * @param {String} body The function body. - * @returns {Function} The new function. - */ - function createFunction(args, body) { - // the newline, in `'\n}'`, is required to avoid errors if `body` ends - // with a single line comment - return window.eval('(function(' + args + ') {' + body + '\n})'); - } - // use `eval` to avoid Firefox's unoptimized `Function` constructor - // http://bugzil.la/804933 - if (isIeOpera || isV8 || !isFirefox) { - createFunction = Function; - } - /** * The template used to create iterator functions. * @@ -373,7 +378,7 @@ // object iteration: // add support for iterating over `arguments` objects if needed - ' <% } else if (noArgsEnum) { %>\n' + + ' <% } else if (nonEnumArgs) { %>\n' + ' var length = iteratee.length; index = -1;\n' + ' if (length && isArguments(iteratee)) {\n' + ' while (++index < length) {\n' + @@ -436,7 +441,7 @@ ' }' + ' <% } %>' + ' <% } %>' + - ' <% if (arrayLoop || noArgsEnum) { %>\n}<% } %>\n' + + ' <% if (arrayLoop || nonEnumArgs) { %>\n}<% } %>\n' + // add code to the bottom of the iteration function '<%= bottom %>;\n' + @@ -455,9 +460,9 @@ }; /** - * Reusable iterator options shared by `forEach`, `forIn`, and `forOwn`. + * Reusable iterator options shared by `each`, `forIn`, and `forOwn`. */ - var forEachIteratorOptions = { + var eachIteratorOptions = { 'args': 'collection, callback, thisArg', 'top': "callback = callback && typeof thisArg == 'undefined' ? callback : createCallback(callback, thisArg)", 'arrayLoop': 'if (callback(iteratee[index], index, collection) === false) return result', @@ -611,9 +616,11 @@ * @param {Function|String} [func=identity|property] The function called per * iteration or property name to query. * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @param {Object} [accumulating] Used to indicate that the callback should + * accept an `accumulator` argument. * @returns {Function} Returns a callback function. */ - function createCallback(func, thisArg) { + function createCallback(func, thisArg, accumulating) { if (!func) { return identity; } @@ -623,6 +630,11 @@ }; } if (typeof thisArg != 'undefined') { + if (accumulating) { + return function(accumulator, value, index, object) { + return func.call(thisArg, accumulator, value, index, object); + }; + } return function(value, index, object) { return func.call(thisArg, value, index, object); }; @@ -651,7 +663,7 @@ 'hasDontEnumBug': hasDontEnumBug, 'isKeysFast': isKeysFast, 'objectLoop': '', - 'noArgsEnum': noArgsEnum, + 'nonEnumArgs': nonEnumArgs, 'noCharByIndex': noCharByIndex, 'shadowed': shadowed, 'top': '', @@ -668,7 +680,7 @@ data.firstArg = /^[^,]+/.exec(args)[0]; // create the function factory - var factory = createFunction( + var factory = Function( 'createCallback, hasOwnProperty, isArguments, isString, objectTypes, ' + 'nativeKeys, propertyIsEnumerable', 'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}' @@ -680,6 +692,21 @@ ); } + /** + * A function compiled to iterate `arguments` objects, arrays, objects, and + * strings consistenly across environments, executing the `callback` for each + * element in the `collection`. The `callback` is bound to `thisArg` and invoked + * with three arguments; (value, index|key, collection). Callbacks may exit + * iteration early by explicitly returning `false`. + * + * @private + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Array|Object|String} Returns `collection`. + */ + var each = createIterator(eachIteratorOptions); + /** * Used by `template` to escape characters for inclusion in compiled * string literals. @@ -839,7 +866,7 @@ * }); * // => alerts 'name' and 'bark' (order is not guaranteed) */ - var forIn = createIterator(forEachIteratorOptions, forOwnIteratorOptions, { + var forIn = createIterator(eachIteratorOptions, forOwnIteratorOptions, { 'useHas': false }); @@ -863,7 +890,7 @@ * }); * // => alerts '0', '1', and 'length' (order is not guaranteed) */ - var forOwn = createIterator(forEachIteratorOptions, forOwnIteratorOptions); + var forOwn = createIterator(eachIteratorOptions, forOwnIteratorOptions); /** * A fallback implementation of `isPlainObject` that checks if a given `value` @@ -943,14 +970,8 @@ /*--------------------------------------------------------------------------*/ /** - * Creates a clone of `value`. If `deep` is `true`, all nested objects will - * also be cloned, otherwise they will be assigned by reference. Functions and - * DOM nodes are **not** cloned. The enumerable properties of `arguments` objects - * and objects created by constructors other than `Object` are cloned to plain - * `Object` objects. - * - * Note: Lo-Dash's deep clone functionality is loosely based on the structured clone algorithm. - * See http://www.w3.org/TR/html5/common-dom-interfaces.html#internal-structured-cloning-algorithm. + * Creates a clone of `value`. If `deep` is `true`, nested objects will also + * be cloned, otherwise they will be assigned by reference. * * @static * @memberOf _ @@ -971,9 +992,6 @@ * { 'name': 'curly', 'age': 60 } * ]; * - * _.clone({ 'name': 'moe' }); - * // => { 'name': 'moe' } - * * var shallow = _.clone(stooges); * shallow[0] === stooges[0]; * // => true @@ -1052,6 +1070,35 @@ return result; } + /** + * Creates a deep clone of `value`. Functions and DOM nodes are **not** cloned. + * The enumerable properties of `arguments` objects and objects created by + * constructors other than `Object` are cloned to plain `Object` objects. + * + * Note: This function is loosely based on the structured clone algorithm. + * See http://www.w3.org/TR/html5/common-dom-interfaces.html#internal-structured-cloning-algorithm. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to deep clone. + * @returns {Mixed} Returns the deep cloned `value`. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * var deep = _.cloneDeep(stooges); + * deep[0] === stooges[0]; + * // => false + */ + function cloneDeep(value) { + return clone(value, true); + } + /** * Assigns own enumerable properties of source object(s) to the `destination` * object for all `destination` properties that resolve to `null`/`undefined`. @@ -1156,7 +1203,9 @@ * // => true */ var isArray = nativeIsArray || function(value) { - return toString.call(value) == arrayClass; + // `instanceof` may cause a memory leak in IE 7 if `value` is a host object + // http://ajaxian.com/archives/working-aroung-the-instanceof-memory-leak + return (argsAreObjects && value instanceof Array) || toString.call(value) == arrayClass; }; /** @@ -1190,7 +1239,7 @@ * // => true */ function isDate(value) { - return toString.call(value) == dateClass; + return value instanceof Date || toString.call(value) == dateClass; } /** @@ -1327,8 +1376,8 @@ return false; } // in older versions of Opera, `arguments` objects have `Array` constructors - var ctorA = noArgsClass && isArguments(a) ? Object : a.constructor, - ctorB = noArgsClass && isArguments(b) ? Object : b.constructor; + var ctorA = !argsAreObjects && isArguments(a) ? Object : a.constructor, + ctorB = !argsAreObjects && isArguments(b) ? Object : b.constructor; // non `Object` object instances with different constructors are not equal if (ctorA != ctorB && !( @@ -1448,7 +1497,7 @@ // fallback for older versions of Chrome and Safari if (isFunction(/x/)) { isFunction = function(value) { - return toString.call(value) == funcClass; + return value instanceof Function || toString.call(value) == funcClass; }; } @@ -1598,7 +1647,7 @@ * // => true */ function isRegExp(value) { - return toString.call(value) == regexpClass; + return value instanceof RegExp || toString.call(value) == regexpClass; } /** @@ -1913,7 +1962,7 @@ : indexOf(collection, target, fromIndex) ) > -1; } else { - forEach(collection, function(value) { + each(collection, function(value) { if (++index >= fromIndex) { return !(result = value === target); } @@ -1951,6 +2000,7 @@ function countBy(collection, callback, thisArg) { var result = {}; callback = createCallback(callback, thisArg); + forEach(collection, function(value, key, collection) { key = callback(value, key, collection); (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1); @@ -1991,7 +2041,7 @@ } } } else { - forEach(collection, function(value, index, collection) { + each(collection, function(value, index, collection) { return (result = !!callback(value, index, collection)); }); } @@ -2031,7 +2081,7 @@ } } } else { - forEach(collection, function(value, index, collection) { + each(collection, function(value, index, collection) { if (callback(value, index, collection)) { result.push(value); } @@ -2063,6 +2113,7 @@ function find(collection, callback, thisArg) { var result; callback = createCallback(callback, thisArg); + forEach(collection, function(value, index, collection) { if (callback(value, index, collection)) { result = value; @@ -2094,7 +2145,21 @@ * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); * // => alerts each number value (order is not guaranteed) */ - var forEach = createIterator(forEachIteratorOptions); + function forEach(collection, callback, thisArg) { + if (callback && typeof thisArg == 'undefined' && isArray(collection)) { + var index = -1, + length = collection.length; + + while (++index < length) { + if (callback(collection[index], index, collection) === false) { + break; + } + } + } else { + each(collection, callback, thisArg); + } + return collection; + } /** * Creates an object composed of keys returned from running each element of @@ -2125,6 +2190,7 @@ function groupBy(collection, callback, thisArg) { var result = {}; callback = createCallback(callback, thisArg); + forEach(collection, function(value, key, collection) { key = callback(value, key, collection); (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value); @@ -2197,7 +2263,7 @@ result[index] = callback(collection[index], index, collection); } } else { - forEach(collection, function(value, key, collection) { + each(collection, function(value, key, collection) { result[++index] = callback(value, key, collection); }); } @@ -2239,7 +2305,7 @@ ? charAtCallback : createCallback(callback, thisArg); - forEach(collection, function(value, index, collection) { + each(collection, function(value, index, collection) { var current = callback(value, index, collection); if (current > computed) { computed = current; @@ -2285,7 +2351,7 @@ ? charAtCallback : createCallback(callback, thisArg); - forEach(collection, function(value, index, collection) { + each(collection, function(value, index, collection) { var current = callback(value, index, collection); if (current < computed) { computed = current; @@ -2349,12 +2415,25 @@ */ function reduce(collection, callback, accumulator, thisArg) { var noaccum = arguments.length < 3; - callback || (callback = identity); - forEach(collection, function(value, index, collection) { - accumulator = noaccum - ? (noaccum = false, value) - : callback.call(thisArg, accumulator, value, index, collection) - }); + callback = createCallback(callback, thisArg, indicatorObject); + + if (isArray(collection)) { + var index = -1, + length = collection.length; + + if (noaccum) { + accumulator = collection[++index]; + } + while (++index < length) { + accumulator = callback(accumulator, collection[index], index, collection); + } + } else { + each(collection, function(value, index, collection) { + accumulator = noaccum + ? (noaccum = false, value) + : callback(accumulator, value, index, collection) + }); + } return accumulator; } @@ -2387,12 +2466,12 @@ } else if (noCharByIndex && isString(collection)) { iteratee = collection.split(''); } - callback || (callback = identity); + callback = createCallback(callback, thisArg, indicatorObject); forEach(collection, function(value, index, collection) { index = props ? props[--length] : --length; accumulator = noaccum ? (noaccum = false, iteratee[index]) - : callback.call(thisArg, accumulator, iteratee[index], index, collection); + : callback(accumulator, iteratee[index], index, collection); }); return accumulator; } @@ -2506,7 +2585,7 @@ } } } else { - forEach(collection, function(value, index, collection) { + each(collection, function(value, index, collection) { return !(result = callback(value, index, collection)); }); } @@ -2541,6 +2620,7 @@ function sortBy(collection, callback, thisArg) { var result = []; callback = createCallback(callback, thisArg); + forEach(collection, function(value, index, collection) { result.push({ 'criteria': callback(value, index, collection), @@ -2817,8 +2897,8 @@ * @memberOf _ * @category Arrays * @param {Array} [array1, array2, ...] Arrays to process. - * @returns {Array} Returns a new array of unique elements, in order, that are - * present in **all** of the arrays. + * @returns {Array} Returns a new array of unique elements that are present + * in **all** of the arrays. * @example * * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); @@ -2827,20 +2907,35 @@ function intersection(array) { var args = arguments, argsLength = args.length, - cache = {}, - result = []; + cache = { '0': {} }, + index = -1, + length = array ? array.length : 0, + isLarge = length >= 100, + result = [], + seen = result; - forEach(array, function(value) { - if (indexOf(result, value) < 0) { - var length = argsLength; - while (--length) { - if (!(cache[length] || (cache[length] = cachedContains(args[length])))(value)) { - return; + outer: + while (++index < length) { + var value = array[index]; + if (isLarge) { + var key = value + ''; + var inited = hasOwnProperty.call(cache[0], key) + ? !(seen = cache[0][key]) + : (seen = cache[0][key] = []); + } + if (inited || indexOf(seen, value) < 0) { + if (isLarge) { + seen.push(value); + } + var argsIndex = argsLength; + while (--argsIndex) { + if (!(cache[argsIndex] || (cache[argsIndex] = cachedContains(args[argsIndex], 0, 100)))(value)) { + continue outer; } } result.push(value); } - }); + } return result; } @@ -3051,9 +3146,10 @@ var low = 0, high = array ? array.length : low; - // explicitly reference `identity` for better engine inlining + // explicitly reference `identity` for better inlining in Firefox callback = callback ? createCallback(callback, thisArg) : identity; value = callback(value); + while (low < high) { var mid = (low + high) >>> 1; callback(array[mid]) < value @@ -3125,7 +3221,7 @@ isSorted = false; } // init value cache for large arrays - var isLarge = !isSorted && length > 74; + var isLarge = !isSorted && length >= 75; if (isLarge) { var cache = {}; } @@ -3138,13 +3234,14 @@ computed = callback ? callback(value, index, array) : value; if (isLarge) { - // manually coerce `computed` to a string because `hasOwnProperty`, in - // some older versions of Firefox, coerces objects incorrectly - seen = hasOwnProperty.call(cache, computed + '') ? cache[computed] : (cache[computed] = []); + var key = computed + ''; + var inited = hasOwnProperty.call(cache, key) + ? !(seen = cache[key]) + : (seen = cache[key] = []); } if (isSorted ? !index || seen[seen.length - 1] !== computed - : indexOf(seen, computed) < 0 + : inited || indexOf(seen, computed) < 0 ) { if (callback || isLarge) { seen.push(computed); @@ -3655,8 +3752,6 @@ /** * This function returns the first argument passed to it. * - * Note: This function is used throughout Lo-Dash as a default callback. - * * @static * @memberOf _ * @category Utilities @@ -3958,7 +4053,7 @@ : ''; try { - result = createFunction('_', 'return ' + source + sourceURL)(lodash); + result = Function('_', 'return ' + source + sourceURL)(lodash); } catch(e) { e.source = source; throw e; @@ -4049,32 +4144,6 @@ /*--------------------------------------------------------------------------*/ - /** - * Wraps the value in a `lodash` wrapper object. - * - * @static - * @memberOf _ - * @category Chaining - * @param {Mixed} value The value to wrap. - * @returns {Object} Returns the wrapper object. - * @example - * - * var stooges = [ - * { 'name': 'moe', 'age': 40 }, - * { 'name': 'larry', 'age': 50 }, - * { 'name': 'curly', 'age': 60 } - * ]; - * - * var youngest = _.chain(stooges) - * .sortBy(function(stooge) { return stooge.age; }) - * .map(function(stooge) { return stooge.name + ' is ' + stooge.age; }) - * .first(); - * // => 'moe is 40' - */ - function chain(value) { - return new lodash(value); - } - /** * Invokes `interceptor` with the `value` as the first argument, and then * returns `value`. The purpose of this method is to "tap into" a method chain, @@ -4101,28 +4170,6 @@ return value; } - /** - * This function returns the wrapper object. - * - * Note: This function is defined to ensure the existing wrapper object is - * returned, instead of creating a new wrapper object like the `_.chain` - * method does. - * - * @name chain - * @deprecated - * @memberOf _ - * @category Chaining - * @returns {Mixed} Returns the wrapper object. - * @example - * - * var wrapped = _([1, 2, 3]); - * wrapped === wrapped.chain(); - * // => true - */ - function wrapperChain() { - return this; - } - /** * Produces the `toString` result of the wrapped value. * @@ -4136,7 +4183,7 @@ * // => '1,2,3' */ function wrapperToString() { - return String(this.__wrapped__); + return this.__wrapped__ + ''; } /** @@ -4159,12 +4206,18 @@ /*--------------------------------------------------------------------------*/ // add functions that return wrapped values when chaining + lodash.after = after; lodash.assign = assign; + lodash.bind = bind; lodash.bindAll = bindAll; - lodash.chain = chain; + lodash.bindKey = bindKey; lodash.compact = compact; + lodash.compose = compose; lodash.countBy = countBy; + lodash.debounce = debounce; lodash.defaults = defaults; + lodash.defer = defer; + lodash.delay = delay; lodash.difference = difference; lodash.filter = filter; lodash.flatten = flatten; @@ -4180,11 +4233,14 @@ lodash.keys = keys; lodash.map = map; lodash.max = max; + lodash.memoize = memoize; lodash.merge = merge; lodash.min = min; lodash.object = object; lodash.omit = omit; + lodash.once = once; lodash.pairs = pairs; + lodash.partial = partial; lodash.pick = pick; lodash.pluck = pluck; lodash.range = range; @@ -4193,6 +4249,7 @@ lodash.shuffle = shuffle; lodash.sortBy = sortBy; lodash.tap = tap; + lodash.throttle = throttle; lodash.times = times; lodash.toArray = toArray; lodash.union = union; @@ -4200,6 +4257,7 @@ lodash.values = values; lodash.where = where; lodash.without = without; + lodash.wrap = wrap; lodash.zip = zip; // add aliases @@ -4218,15 +4276,9 @@ /*--------------------------------------------------------------------------*/ // add functions that return unwrapped values when chaining - lodash.after = after; - lodash.bind = bind; - lodash.bindKey = bindKey; lodash.clone = clone; - lodash.compose = compose; + lodash.cloneDeep = cloneDeep; lodash.contains = contains; - lodash.debounce = debounce; - lodash.defer = defer; - lodash.delay = delay; lodash.escape = escape; lodash.every = every; lodash.find = find; @@ -4251,11 +4303,8 @@ lodash.isString = isString; lodash.isUndefined = isUndefined; lodash.lastIndexOf = lastIndexOf; - lodash.memoize = memoize; lodash.mixin = mixin; lodash.noConflict = noConflict; - lodash.once = once; - lodash.partial = partial; lodash.random = random; lodash.reduce = reduce; lodash.reduceRight = reduceRight; @@ -4264,10 +4313,8 @@ lodash.some = some; lodash.sortedIndex = sortedIndex; lodash.template = template; - lodash.throttle = throttle; lodash.unescape = unescape; lodash.uniqueId = uniqueId; - lodash.wrap = wrap; // add aliases lodash.all = every; @@ -4316,42 +4363,60 @@ * @memberOf _ * @type String */ - lodash.VERSION = '1.0.0-rc.2'; + lodash.VERSION = '1.0.0-rc.3'; // add "Chaining" functions to the wrapper - lodash.prototype.chain = wrapperChain; lodash.prototype.toString = wrapperToString; lodash.prototype.value = wrapperValueOf; lodash.prototype.valueOf = wrapperValueOf; - // add mutator `Array` functions to the wrapper - forEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) { + // add `Array` functions that return unwrapped values + each(['join', 'pop', 'shift'], function(methodName) { var func = arrayRef[methodName]; lodash.prototype[methodName] = function() { - var value = this.__wrapped__; - func.apply(value, arguments); + return func.apply(this.__wrapped__, arguments); + }; + }); - // avoid array-like object bugs with `Array#shift` and `Array#splice` - // in Firefox < 10 and IE < 9 - if (hasObjectSpliceBug && value.length === 0) { - delete value[0]; - } + // add `Array` functions that return the wrapped value + each(['push', 'reverse', 'sort', 'unshift'], function(methodName) { + var func = arrayRef[methodName]; + lodash.prototype[methodName] = function() { + func.apply(this.__wrapped__, arguments); return this; }; }); - // add accessor `Array` functions to the wrapper - forEach(['concat', 'join', 'slice'], function(methodName) { + // add `Array` functions that return new wrapped values + each(['concat', 'slice', 'splice'], function(methodName) { var func = arrayRef[methodName]; lodash.prototype[methodName] = function() { - var value = this.__wrapped__, - result = func.apply(value, arguments); - + var result = func.apply(this.__wrapped__, arguments); return new lodash(result); }; }); + // avoid array-like object bugs with `Array#shift` and `Array#splice` + // in Firefox < 10 and IE < 9 + if (hasObjectSpliceBug) { + each(['pop', 'shift', 'splice'], function(methodName) { + var func = arrayRef[methodName], + isSplice = methodName == 'splice'; + + lodash.prototype[methodName] = function() { + var value = this.__wrapped__, + result = func.apply(value, arguments); + + if (value.length === 0) { + delete value[0]; + } + return isSplice ? new lodash(result) : result; + }; + }); + } + // add pseudo private property to be used and removed during the build process + lodash._each = each; lodash._iteratorTemplate = iteratorTemplate; /*--------------------------------------------------------------------------*/ diff --git a/lodash.min.js b/lodash.min.js index acde0a7488..40126c4a5d 100644 --- a/lodash.min.js +++ b/lodash.min.js @@ -1,41 +1,42 @@ /*! - Lo-Dash 1.0.0-rc.2 lodash.com/license + Lo-Dash 1.0.0-rc.3 lodash.com/license Underscore.js 1.4.3 underscorejs.org/LICENSE */ -;(function(e,t){function s(e){if(e&&"object"==typeof e&&e.__wrapped__)return e;if(!(this instanceof s))return new s(e);this.__wrapped__=e}function o(t,n){return e.eval("(function("+t+"){"+n+"})")}function u(e,t,n){t||(t=0);var r=e.length,i=r-t>=(n||it);if(i)for(var s={},n=t-1;++nt||"undefined"==typeof e)return 1;if(ei;i++)r+="i='"+e.j[i]+"';if(","constructor"==e.j[i]&&(r+="!(f&&f.prototype===l)&&"),r+="h.call(l,i)){"+e.g+"}"}if(e.b||e.h)r+="}";return r+=e.c+";return t" -,o("e,h,j,k,p,n,s","return function("+t+"){"+r+"}")(c,Tt,b,A,on,_t,Ct)}function p(e){return"\\"+un[e]}function d(e){return dn[e]}function v(e){return"function"!=typeof e.toString&&"string"==typeof (e+"")}function m(){}function g(e,t,n){t||(t=0),"undefined"==typeof n&&(n=e?e.length:0);for(var r=-1,n=n-t||0,i=Array(0>n?0:n);++rn?Dt(0,s+n):n)||0;return"number"==typeof s?o=-1<(A(e)?e.indexOf(t,n):W(e,t,n)):wn(e,function(e){if(++r>=n)return!(o=e===t)}),o}function D(e,t,r){var i=n,t=c -(t,r);if(gn(e))for(var r=-1,s=e.length;++rr&&(r=n,o=e)});else for(;++io&&(o=e[i]);return o}function F(e,t){return B(e,t+"")}function I(e,t,n,r){var s=3>arguments.length;return t||(t=K),wn(e,function(e,o,u){n=s?(s=i,e):t.call(r,n,e,o,u)}),n}function q(e,t,n,r){var s=e,o=e?e.length:0,u=3>arguments.length;if("number"!=typeof o)var a=bn(e),o=a.length;else en&&A(e)&& -(s=e.split(""));return t||(t=K),wn(e,function(e,f,l){f=a?a[--o]:--o,n=u?(u=i,s[f]):t.call(r,n,s[f],f,l)}),n}function R(e,t,n){var r,t=c(t,n);if(gn(e))for(var n=-1,i=e.length;++nn?Dt(0,i+n):n||0)-1;else if(n)return r=V(e,t),e[r]===t?r:-1;for(;++r>>1,n(e[r])W(a,h))(n||f)&&a.push(h),u.push(r)}return u}function J(e,t){return $t||Lt&&2|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/,ut=/&(?:amp|lt|gt|quot|#x27);/g,at=/\b__p\+='';/g,ft=/\b(__p\+=)''\+/g,lt=/(__e\(.*?\)|\b__t\))\+'';/g,ct=/\w*$/,ht=/(?:__e|__t=)\(\s*(?![\d\s"']|this\.)/g,pt=RegExp("^"+(tt.valueOf+"").replace(/[.*+?^=!:${}()|[\]\/\\]/g,"\\$&").replace(/valueOf|for [^\]]+/g,".+?")+"$"),dt=/\$\{((?:(?=\\?)\\?[\s\S])*?)}/g,vt=/<%=([\s\S]+?)%>/g,mt=/($^)/,gt=/[&<>"']/g,yt=/['\n\r\t\u2028\u2029\\]/g,bt="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf" -.split(" "),wt=Math.ceil,Et=et.concat,St=Math.floor,xt=pt.test(xt=Object.getPrototypeOf)&&xt,Tt=tt.hasOwnProperty,Nt=et.push,Ct=tt.propertyIsEnumerable,kt=tt.toString,Lt=pt.test(Lt=g.bind)&&Lt,At=pt.test(At=Array.isArray)&&At,Ot=e.isFinite,Mt=e.isNaN,_t=pt.test(_t=Object.keys)&&_t,Dt=Math.max,Pt=Math.min,Ht=Math.random,Bt="[object Arguments]",jt="[object Array]",Ft="[object Boolean]",It="[object Date]",qt="[object Number]",Rt="[object Object]",Ut="[object RegExp]",zt="[object String]",Wt=!/1/.test -(Function("1")),Xt=!!e.attachEvent,Vt=Lt&&!/\n|true/.test(Lt+Xt),$t=Lt&&!Vt,Jt=_t&&(Xt||Vt),Kt,Qt,Gt=(Gt={0:1,length:1},et.splice.call(Gt,0,1),Gt[0]),Yt=n;(function(){function e(){this.x=1}var t=[];e.prototype={valueOf:1,y:1};for(var n in new e)t.push(n);for(n in arguments)Yt=!n;Kt=!/valueOf/.test(t),Qt="x"!=t[0]})(1);var Zt=!b(arguments),en="xx"!="x"[0]+Object("x")[0];try{var tn=("[object Object]",kt.call(document)==Rt)}catch(nn){}var rn={"[object Function]":i};rn[Bt]=rn[jt]=rn[Ft]=rn[It]=rn[qt]= -rn[Rt]=rn[Ut]=rn[zt]=n;var sn={};sn[jt]=Array,sn[Ft]=Boolean,sn[It]=Date,sn[Rt]=Object,sn[qt]=Number,sn[Ut]=RegExp,sn[zt]=String;var on={"boolean":i,"function":n,object:n,number:i,string:i,"undefined":i},un={"\\":"\\","'":"'","\n":"n","\r":"r"," ":"t","\u2028":"u2028","\u2029":"u2029"};s.templateSettings={escape:/<%-([\s\S]+?)%>/g,evaluate:/<%([\s\S]+?)%>/g,interpolate:vt,variable:""};if(Xt||Vt||!Wt)o=Function;var an={a:"o,v,g",k:"for(var a=1,b=typeof g=='number'?2:arguments.length;a":">",'"':""","'":"'"},vn=T(dn),mn=h(an,{g:"if(t[i]==null)"+an.g}),gn=At||function(e){return kt.call(e)==jt};C(/x/)&&(C=function(e){return"[object Function]"==kt.call(e)});var yn=xt?function(e){if(!e||"object"!=typeof -e)return i;var t=e.valueOf,n="function"==typeof t&&(n=xt(t))&&xt(n);return n?e==n||xt(e)==n&&!b(e):w(e)}:w,bn=_t?function(e){return"function"==typeof e&&Ct.call(e,"prototype")?E(e):k(e)?_t(e):[]}:E,wn=h(fn);s.assign=cn,s.bindAll=function(e){for(var t=arguments,n=1W(i,e)){for(var s=n;--s;)if(!(r[s]||(r[s]=u(t[s])))(e))return;i.push(e)}}),i},s.invert=T,s.invoke=function(e,t){var n=g(arguments,2),r="function"==typeof t,i=[];return wn(e,function(e){i.push((r?t:e[t]).apply(e,n))}),i},s.keys=bn,s.map=B,s.max=j,s.merge=O,s.min=function(e,t,n){var r=Infinity,i=-1,s=e?e.length:0,o=r;if(t||!gn(e))t=!t&&A(e)?a:c(t,n),wn(e,function(e,n,i) -{n=t(e,n,i),nW(s,n,1))i[n]=e}),i},s.pairs=function(e){var t=[];return pn(e,function(e,n){t.push([n,e])}),t},s.pick=function(e,t,n){var r={};if("function"!=typeof t)for(var i=0,s=Et.apply -(et,arguments),o=s.length;++ie?t():function(){if(1>--e)return t.apply(this,arguments)}},s.bind=J,s.bindKey=function(e,t){return l(e,t,g -(arguments,2))},s.clone=S,s.compose=function(){var e=arguments;return function(){for(var t=arguments,n=e.length;n--;)t=[e[n].apply(this,t)];return t[0]}},s.contains=_,s.debounce=function(e,t,n){function i(){a=r,n||(o=e.apply(u,s))}var s,o,u,a;return function(){var r=n&&!a;return s=arguments,u=this,clearTimeout(a),a=setTimeout(i,t),r&&(o=e.apply(u,s)),o}},s.defer=function(e){var n=g(arguments,1);return setTimeout(function(){e.apply(t,n)},1)},s.delay=function(e,n){var r=g(arguments,2);return setTimeout -(function(){e.apply(t,r)},n)},s.escape=function(e){return e==r?"":(e+"").replace(gt,d)},s.every=D,s.find=H,s.has=function(e,t){return e?Tt.call(e,t):i},s.identity=K,s.indexOf=W,s.isArguments=b,s.isArray=gn,s.isBoolean=function(e){return e===n||e===i||kt.call(e)==Ft},s.isDate=function(e){return kt.call(e)==It},s.isElement=function(e){return e?1===e.nodeType:i},s.isEmpty=function(e){var t=n;if(!e)return t;var r=kt.call(e),s=e.length;return r==jt||r==zt||r==Bt||Zt&&b(e)||r==Rt&&"number"==typeof s&&C -(e.splice)?!s:(pn(e,function(){return t=i}),t)},s.isEqual=N,s.isFinite=function(e){return Ot(e)&&!Mt(parseFloat(e))},s.isFunction=C,s.isNaN=function(e){return L(e)&&e!=+e},s.isNull=function(e){return e===r},s.isNumber=L,s.isObject=k,s.isPlainObject=yn,s.isRegExp=function(e){return kt.call(e)==Ut},s.isString=A,s.isUndefined=function(e){return"undefined"==typeof e},s.lastIndexOf=function(e,t,n){var r=e?e.length:0;for("number"==typeof n&&(r=(0>n?Dt(0,r+n):Pt(n,r-1))+1);r--;)if(e[r]===t)return r;return-1 -},s.memoize=function(e,t){var n={};return function(){var r=t?t.apply(this,arguments):arguments[0];return Tt.call(n,r)?n[r]:n[r]=e.apply(this,arguments)}},s.mixin=Q,s.noConflict=function(){return e._=st,this},s.once=function(e){var t,s=i;return function(){return s?t:(s=n,t=e.apply(this,arguments),e=r,t)}},s.partial=function(e){return l(e,g(arguments,1))},s.random=function(e,t){return e==r&&t==r&&(t=1),e=+e||0,t==r&&(t=e,e=0),e+St(Ht()*((+t||0)-e+1))},s.reduce=I,s.reduceRight=q,s.result=function(e, -t){var n=e?e[t]:r;return C(n)?e[t]():n},s.size=function(e){var t=e?e.length:0;return"number"==typeof t?t:bn(e).length},s.some=R,s.sortedIndex=V,s.template=function(e,t,n){e||(e=""),n||(n={});var r,i,u=s.templateSettings,a=0,f=n.interpolate||u.interpolate||mt,l="__p+='",c=n.variable||u.variable,h=c;e.replace(RegExp((n.escape||u.escape||mt).source+"|"+f.source+"|"+(f===vt?dt:mt).source+"|"+(n.evaluate||u.evaluate||mt).source+"|$","g"),function(t,n,i,s,o,u){return i||(i=s),l+=e.slice(a,u).replace(yt -,p),n&&(l+="'+__e("+n+")+'"),o&&(l+="';"+o+";__p+='"),i&&(l+="'+((__t=("+i+"))==null?'':__t)+'"),r||(r=o||ot.test(n||i)),a=u+t.length,t}),l+="';\n",h||(c="obj",r?l="with("+c+"){"+l+"}":(n=RegExp("(\\(\\s*)"+c+"\\."+c+"\\b","g"),l=l.replace(ht,"$&"+c+".").replace(n,"$1__d"))),l=(r?l.replace(at,""):l).replace(ft,"$1").replace(lt,"$1;"),l="function("+c+"){"+(h?"":c+"||("+c+"={});")+"var __t,__p='',__e=_.escape"+(r?",__j=Array.prototype.join;function print(){__p+=__j.call(arguments,'')}":(h?"":",__d="+ -c+"."+c+"||"+c)+";")+l+"return __p}";try{i=o("_","return "+l)(s)}catch(d){throw d.source=l,d}return t?i(t):(i.source=l,i)},s.throttle=function(e,t){function n(){a=new Date,u=r,s=e.apply(o,i)}var i,s,o,u,a=0;return function(){var f=new Date,l=t-(f-a);return i=arguments,o=this,0>=l?(clearTimeout(u),u=r,a=f,s=e.apply(o,i)):u||(u=setTimeout(n,l)),s}},s.unescape=function(e){return e==r?"":(e+"").replace(ut,y)},s.uniqueId=function(e){return(e==r?"":e+"")+ ++nt},s.wrap=function(e,t){return function(){var n= -[e];return Nt.apply(n,arguments),t.apply(this,n)}},s.all=D,s.any=R,s.detect=H,s.foldl=I,s.foldr=q,s.include=_,s.inject=I,pn(s,function(e,t){s.prototype[t]||(s.prototype[t]=function(){var t=[this.__wrapped__];return Nt.apply(t,arguments),e.apply(s,t)})}),s.first=U,s.last=function(e,t,n){if(e){var i=e.length;return t==r||n?e[i-1]:g(e,Dt(0,i-t))}},s.take=U,s.head=U,pn(s,function(e,t){s.prototype[t]||(s.prototype[t]=function(t,n){var i=e(this.__wrapped__,t,n);return t==r||n?i:new s(i)})}),s.VERSION="1.0.0-rc.2" -,s.prototype.chain=function(){return this},s.prototype.toString=function(){return""+this.__wrapped__},s.prototype.value=G,s.prototype.valueOf=G,wn("pop push reverse shift sort splice unshift".split(" "),function(e){var t=et[e];s.prototype[e]=function(){var e=this.__wrapped__;return t.apply(e,arguments),Gt&&e.length===0&&delete e[0],this}}),wn(["concat","join","slice"],function(e){var t=et[e];s.prototype[e]=function(){var e=t.apply(this.__wrapped__,arguments);return new s(e)}}),typeof define=="function"&&typeof -define.amd=="object"&&define.amd?(e._=s,define(function(){return s})):Y?"object"==typeof module&&module&&module.exports==Y?(module.exports=s)._=s:Y._=s:e._=s})(this); \ No newline at end of file +;(function(e,t){function n(e){if(e&&typeof e=="object"&&e.__wrapped__)return e;if(!(this instanceof n))return new n(e);this.__wrapped__=e}function r(e,t,n){t||(t=0);var r=e.length,i=r-t>=(n||tt);if(i)for(var s={},n=t-1;++nt||typeof e=="undefined")return 1;if( +ei;i++)r+="i='"+e.j[i]+"';if(","constructor"==e.j[i]&&(r+="!(f&&f.prototype===l)&&"),r+="h.call(l,i)){"+e.g+"}"}if(e.b||e.h)r+="}";return r+=e.c+";return t" +,n("e,h,j,k,p,n,s","return function("+t+"){"+r+"}")(u,Et,v,N,nn,At,xt)}function f(e){return"\\"+rn[e]}function l(e){return hn[e]}function c(e){return typeof e.toString!="function"&&typeof (e+"")=="string"}function h(){}function p(e,t,n){t||(t=0),typeof n=="undefined"&&(n=e?e.length:0);for(var r=-1,n=n-t||0,i=Array(0>n?0:n);++rn?Ot(0,i+n):n)||0;return typeof i=="number"?s=-1<(N(e)?e.indexOf(t,n):R(e,t,n)):an(e,function(e){if(++r>=n)return!(s=e===t)}),s} +function A(e,t,n){var r=!0,t=u(t,n);if(vn(e))for(var n=-1,i=e.length;++nr&&(r=n,a=e)});else for(;++sa&&(a=e[s]);return a}function H(e,t){return D(e,t+"")}function B(e,t,n,r){var i=3>arguments.length,t=u(t,r,et);if(vn(e)){var s=-1,o= +e.length;for(i&&(n=e[++s]);++sarguments.length;if(typeof s!="number")var a=gn(e),s=a.length;else Gt&&N(e)&&(i=e.split(""));return t=u(t,r,et),_(e,function(e,r,u){r=a?a[--s]:--s,n=o?(o=!1,i[r]):t(n,i[r],r,u)}),n}function F(e,t,n){var r,t=u(t,n);if(vn(e))for(var n=-1,i=e.length;++nn?Ot(0,i+n):n||0)-1;else if(n)return r=z(e,t),e[r]===t?r:-1;for(;++r>>1,n(e[r])R(a,c))(n||f)&&a.push(c),o.push(r)}return o}function X(e,t){return zt||Nt&&2|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/,it=/&(?:amp|lt|gt|quot|#x27);/g,st=/\b__p\+='';/g,ot=/\b(__p\+=)''\+/g,ut=/(__e\(.*?\)|\b__t\))\+'';/g,at=/\w*$/,ft=/(?:__e|__t=)\(\s*(?![\d\s"']|this\.)/g +,lt=RegExp("^"+(Y.valueOf+"").replace(/[.*+?^=!:${}()|[\]\/\\]/g,"\\$&").replace(/valueOf|for [^\]]+/g,".+?")+"$"),ct=/\$\{((?:(?=\\?)\\?[\s\S])*?)}/g,ht=/<%=([\s\S]+?)%>/g,pt=/($^)/,dt=/[&<>"']/g,vt=/['\n\r\t\u2028\u2029\\]/g,mt="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),gt=Math.ceil,yt=G.concat,bt=Math.floor,wt=lt.test(wt=Object.getPrototypeOf)&&wt,Et=Y.hasOwnProperty,St=G.push,xt=Y.propertyIsEnumerable,Tt=Y.toString,Nt=lt.test(Nt= +p.bind)&&Nt,Ct=lt.test(Ct=Array.isArray)&&Ct,kt=e.isFinite,Lt=e.isNaN,At=lt.test(At=Object.keys)&&At,Ot=Math.max,Mt=Math.min,_t=Math.random,Dt="[object Arguments]",Pt="[object Array]",Ht="[object Boolean]",Bt="[object Date]",jt="[object Number]",Ft="[object Object]",It="[object RegExp]",qt="[object String]",Rt=!!e.attachEvent,Ut=Nt&&!/\n|true/.test(Nt+Rt),zt=Nt&&!Ut,Wt=At&&(Rt||Ut),Xt,Vt,$t=($t={0:1,length:1},G.splice.call($t,0,1),$t[0]),Jt=!0;(function(){function e(){this.x=1}var t=[];e.prototype= +{valueOf:1,y:1};for(var n in new e)t.push(n);for(n in arguments)Jt=!n;Xt=!/valueOf/.test(t),Vt="x"!=t[0]})(1);var Kt=arguments.constructor==Object,Qt=!v(arguments),Gt="xx"!="x"[0]+Object("x")[0];try{var Yt=("[object Object]",Tt.call(document)==Ft)}catch(Zt){}var en={"[object Function]":!1};en[Dt]=en[Pt]=en[Ht]=en[Bt]=en[jt]=en[Ft]=en[It]=en[qt]=!0;var tn={};tn[Pt]=Array,tn[Ht]=Boolean,tn[Bt]=Date,tn[Ft]=Object,tn[jt]=Number,tn[It]=RegExp,tn[qt]=String;var nn={"boolean":!1,"function":!0,object:!0, +number:!1,string:!1,"undefined":!1},rn={"\\":"\\","'":"'","\n":"n","\r":"r"," ":"t","\u2028":"u2028","\u2029":"u2029"};n.templateSettings={escape:/<%-([\s\S]+?)%>/g,evaluate:/<%([\s\S]+?)%>/g,interpolate:ht,variable:""};var sn={a:"o,v,g",k:"for(var a=1,b=typeof g=='number'?2:arguments.length;a":">",'"':""","'":"'"},pn=w(hn),dn=a(sn,{g:"if(t[i]==null)"+sn.g}),vn=Ct||function(e){return Kt&&e instanceof Array||Tt.call(e)==Pt};S(/x/)&&(S=function(e){return e instanceof Function||"[object Function]"==Tt.call(e)});var mn=wt?function(e){if(!e||typeof e!="object")return!1;var t=e.valueOf,n=typeof t=="function"&&(n=wt(t))&&wt(n);return n?e==n||wt(e)==n&&!v(e):m(e) +}:m,gn=At?function(e){return typeof e=="function"&&xt.call(e,"prototype")?g(e):x(e)?At(e):[]}:g;n.after=function(e,t){return 1>e?t():function(){if(1>--e)return t.apply(this,arguments)}},n.assign=fn,n.bind=X,n.bindAll=function(e){for(var t=arguments,n=1R(f,l)){u&&f.push(l);for(var h=n;--h;)if(!(i[h]||(i[h]=r(t[h],0,100)))(l))continue e;a.push(l)}}return a},n.invert=w,n.invoke=function(e,t){var n=p(arguments,2),r=typeof t=="function",i=[];return _(e,function(e){i.push((r?t:e[t]).apply(e,n))}),i},n.keys=gn,n.map=D, +n.max=P,n.memoize=function(e,t){var n={};return function(){var r=t?t.apply(this,arguments):arguments[0];return Et.call(n,r)?n[r]:n[r]=e.apply(this,arguments)}},n.merge=C,n.min=function(e,t,n){var r=Infinity,s=-1,o=e?e.length:0,a=r;if(t||!vn(e))t=!t&&N(e)?i:u(t,n),an(e,function(e,n,i){n=t(e,n,i),nR(s,n,1))i[n]=e}),i},n.once=function(e){var t,n=!1;return function(){return n?t:(n=!0,t=e.apply(this,arguments),e=null,t)}},n.pairs=function(e){var t=[];return cn(e,function(e,n){t.push([n,e])}),t},n.partial=function(e){return o(e,p(arguments,1))},n.pick=function(e,t,n){var r={};if(typeof t!="function")for(var i=0,s=yt.apply(G,arguments),o=s.length;++i=f?(clearTimeout(o),o=null,u=a,i=e.apply(s,r)):o||(o=setTimeout(n,f)),i}},n.times=function(e,t,n){for(var e=+e||0,r=-1,i=Array(e);++rn?Ot(0,r+n):Mt(n,r-1))+1);r--;)if(e[r]===t)return r;return-1},n.mixin=$,n.noConflict=function(){return e._=nt,this},n.random=function(e,t){return null==e&&null==t&&(t=1),e=+e||0,null==t&&(t=e,e=0),e+bt(_t()*((+t||0)-e+1))},n.reduce=B,n.reduceRight=j,n.result=function(e,t){var n=e?e[t]:null;return S(n)?e[t]():n},n.size=function(e){var t=e?e.length:0; +return typeof t=="number"?t:gn(e).length},n.some=F,n.sortedIndex=z,n.template=function(e,t,r){e||(e=""),r||(r={});var i,s,o=n.templateSettings,u=0,a=r.interpolate||o.interpolate||pt,l="__p+='",c=r.variable||o.variable,h=c;e.replace(RegExp((r.escape||o.escape||pt).source+"|"+a.source+"|"+(a===ht?ct:pt).source+"|"+(r.evaluate||o.evaluate||pt).source+"|$","g"),function(t,n,r,s,o,a){return r||(r=s),l+=e.slice(u,a).replace(vt,f),n&&(l+="'+__e("+n+")+'"),o&&(l+="';"+o+";__p+='"),r&&(l+="'+((__t=("+r+"))==null?'':__t)+'" +),i||(i=o||rt.test(n||r)),u=a+t.length,t}),l+="';\n",h||(c="obj",i?l="with("+c+"){"+l+"}":(r=RegExp("(\\(\\s*)"+c+"\\."+c+"\\b","g"),l=l.replace(ft,"$&"+c+".").replace(r,"$1__d"))),l=(i?l.replace(st,""):l).replace(ot,"$1").replace(ut,"$1;"),l="function("+c+"){"+(h?"":c+"||("+c+"={});")+"var __t,__p='',__e=_.escape"+(i?",__j=Array.prototype.join;function print(){__p+=__j.call(arguments,'')}":(h?"":",__d="+c+"."+c+"||"+c)+";")+l+"return __p}";try{s=Function("_","return "+l)(n)}catch(p){throw p.source= +l,p}return t?s(t):(s.source=l,s)},n.unescape=function(e){return null==e?"":(e+"").replace(it,d)},n.uniqueId=function(e){return(null==e?"":e+"")+ ++Z},n.all=A,n.any=F,n.detect=M,n.foldl=B,n.foldr=j,n.include=L,n.inject=B,cn(n,function(e,t){n.prototype[t]||(n.prototype[t]=function(){var t=[this.__wrapped__];return St.apply(t,arguments),e.apply(n,t)})}),n.first=I,n.last=function(e,t,n){if(e){var r=e.length;return null==t||n?e[r-1]:p(e,Ot(0,r-t))}},n.take=I,n.head=I,cn(n,function(e,t){n.prototype[t]|| +(n.prototype[t]=function(t,r){var i=e(this.__wrapped__,t,r);return null==t||r?i:new n(i)})}),n.VERSION="1.0.0-rc.3",n.prototype.toString=function(){return this.__wrapped__+""},n.prototype.value=J,n.prototype.valueOf=J,an(["join","pop","shift"],function(e){var t=G[e];n.prototype[e]=function(){return t.apply(this.__wrapped__,arguments)}}),an(["push","reverse","sort","unshift"],function(e){var t=G[e];n.prototype[e]=function(){return t.apply(this.__wrapped__,arguments),this}}),an(["concat","slice","splice" +],function(e){var t=G[e];n.prototype[e]=function(){var e=t.apply(this.__wrapped__,arguments);return new n(e)}}),$t&&an(["pop","shift","splice"],function(e){var t=G[e],r="splice"==e;n.prototype[e]=function(){var e=this.__wrapped__,i=t.apply(e,arguments);return 0===e.length&&delete e[0],r?new n(i):i}}),typeof define=="function"&&typeof define.amd=="object"&&define.amd?(e._=n,define(function(){return n})):K?typeof module=="object"&&module&&module.exports==K?(module.exports=n)._=n:K._=n:e._=n})(this); \ No newline at end of file diff --git a/lodash.underscore.js b/lodash.underscore.js index 7760563d10..a68dd807b9 100644 --- a/lodash.underscore.js +++ b/lodash.underscore.js @@ -1,5 +1,5 @@ /*! - * Lo-Dash 1.0.0-rc.2 (Custom Build) + * Lo-Dash 1.0.0-rc.3 (Custom Build) * Build: `lodash underscore -d -o ./lodash.underscore.js` * (c) 2012 John-David Dalton * Based on Underscore.js 1.4.3 @@ -133,6 +133,9 @@ var hasObjectSpliceBug = (hasObjectSpliceBug = { '0': 1, 'length': 1 }, arrayRef.splice.call(hasObjectSpliceBug, 0, 1), hasObjectSpliceBug[0]); + /** Detect if `arguments` objects are `Object` objects (all but Opera < 10.5) */ + var argsAreObjects = arguments.constructor == Object; + /** * Detect lack of support for accessing string characters by index: * @@ -182,7 +185,29 @@ /*--------------------------------------------------------------------------*/ /** - * The `lodash` function. + * Creates a `lodash` object, that wraps the given `value`, to enable + * method chaining. + * + * The chainable wrapper functions are: + * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, `compose`, + * `concat`, `countBy`, `debounce`, `defaults`, `defer`, `delay`, `difference`, + * `filter`, `flatten`, `forEach`, `forIn`, `forOwn`, `functions`, `groupBy`, + * `initial`, `intersection`, `invert`, `invoke`, `keys`, `map`, `max`, `memoize`, + * `merge`, `min`, `object`, `omit`, `once`, `pairs`, `partial`, `pick`, `pluck`, + * `push`, `range`, `reject`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, + * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `union`, `uniq`, + * `unshift`, `values`, `where`, `without`, `wrap`, and `zip` + * + * The non-chainable wrapper functions are: + * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `has`, `identity`, + * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, `isEmpty`, + * `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`, `isObject`, + * `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`, `lastIndexOf`, + * `mixin`, `noConflict`, `pop`, `random`, `reduce`, `reduceRight`, `result`, + * `shift`, `size`, `some`, `sortedIndex`, `template`, `unescape`, and `uniqueId` + * + * The wrapper functions `first` and `last` return wrapped values when `n` is + * passed, otherwise they return unwrapped values. * * @name _ * @constructor @@ -251,6 +276,7 @@ }; /*--------------------------------------------------------------------------*/ + /** Reusable iterator options for `assign` and `defaults` */ var assignIteratorOptions = { 'args': 'object, source, guard', @@ -262,9 +288,9 @@ }; /** - * Reusable iterator options shared by `forEach`, `forIn`, and `forOwn`. + * Reusable iterator options shared by `each`, `forIn`, and `forOwn`. */ - var forEachIteratorOptions = { + var eachIteratorOptions = { 'args': 'collection, callback, thisArg', 'top': "callback = callback && typeof thisArg == 'undefined' ? callback : createCallback(callback, thisArg)", 'arrayLoop': 'if (callback(iteratee[index], index, collection) === false) return result', @@ -366,9 +392,11 @@ * @param {Function|String} [func=identity|property] The function called per * iteration or property name to query. * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @param {Object} [accumulating] Used to indicate that the callback should + * accept an `accumulator` argument. * @returns {Function} Returns a callback function. */ - function createCallback(func, thisArg) { + function createCallback(func, thisArg, accumulating) { if (!func) { return identity; } @@ -378,6 +406,11 @@ }; } if (typeof thisArg != 'undefined') { + if (accumulating) { + return function(accumulator, value, index, object) { + return func.call(thisArg, accumulator, value, index, object); + }; + } return function(value, index, object) { return func.call(thisArg, value, index, object); }; @@ -405,7 +438,7 @@ 'bottom': '', 'hasDontEnumBug': hasDontEnumBug, 'objectLoop': '', - 'noArgsEnum': noArgsEnum, + 'nonEnumArgs': nonEnumArgs, 'noCharByIndex': noCharByIndex, 'shadowed': shadowed, 'top': '', @@ -434,6 +467,38 @@ ); } + /** + * A function compiled to iterate `arguments` objects, arrays, objects, and + * strings consistenly across environments, executing the `callback` for each + * element in the `collection`. The `callback` is bound to `thisArg` and invoked + * with three arguments; (value, index|key, collection). Callbacks may exit + * iteration early by explicitly returning `false`. + * + * @private + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Array|Object|String} Returns `collection`. + */ + var each = function (collection, callback, thisArg) { + var index, iteratee = collection, result = collection; + if (!collection) return result; + callback = callback && typeof thisArg == 'undefined' ? callback : createCallback(callback, thisArg); + var length = iteratee.length; index = -1; + if (typeof length == 'number') { + while (++index < length) { + if (callback(iteratee[index], index, collection) === indicatorObject) return result + } + } + else { + for (index in iteratee) { + if (hasOwnProperty.call(iteratee, index)) { + if (callback(iteratee[index], index, collection) === indicatorObject) return result; + } + } + } + }; + /** * Used by `template` to escape characters for inclusion in compiled * string literals. @@ -718,14 +783,8 @@ /*--------------------------------------------------------------------------*/ /** - * Creates a clone of `value`. If `deep` is `true`, all nested objects will - * also be cloned, otherwise they will be assigned by reference. Functions and - * DOM nodes are **not** cloned. The enumerable properties of `arguments` objects - * and objects created by constructors other than `Object` are cloned to plain - * `Object` objects. - * - * Note: Lo-Dash's deep clone functionality is loosely based on the structured clone algorithm. - * See http://www.w3.org/TR/html5/common-dom-interfaces.html#internal-structured-cloning-algorithm. + * Creates a clone of `value`. If `deep` is `true`, nested objects will also + * be cloned, otherwise they will be assigned by reference. * * @static * @memberOf _ @@ -746,9 +805,6 @@ * { 'name': 'curly', 'age': 60 } * ]; * - * _.clone({ 'name': 'moe' }); - * // => { 'name': 'moe' } - * * var shallow = _.clone(stooges); * shallow[0] === stooges[0]; * // => true @@ -880,7 +936,9 @@ * // => true */ var isArray = nativeIsArray || function(value) { - return toString.call(value) == arrayClass; + // `instanceof` may cause a memory leak in IE 7 if `value` is a host object + // http://ajaxian.com/archives/working-aroung-the-instanceof-memory-leak + return (argsAreObjects && value instanceof Array) || toString.call(value) == arrayClass; }; /** @@ -914,7 +972,7 @@ * // => true */ function isDate(value) { - return toString.call(value) == dateClass; + return value instanceof Date || toString.call(value) == dateClass; } /** @@ -1162,7 +1220,7 @@ // fallback for older versions of Chrome and Safari if (isFunction(/x/)) { isFunction = function(value) { - return toString.call(value) == funcClass; + return value instanceof Function || toString.call(value) == funcClass; }; } @@ -1276,7 +1334,7 @@ * // => true */ function isRegExp(value) { - return toString.call(value) == regexpClass; + return value instanceof RegExp || toString.call(value) == regexpClass; } /** @@ -1485,7 +1543,7 @@ if (typeof length == 'number') { result = indexOf(collection, target) > -1; } else { - forEach(collection, function(value) { + each(collection, function(value) { return (result = value === target) && indicatorObject; }); } @@ -1521,6 +1579,7 @@ function countBy(collection, callback, thisArg) { var result = {}; callback = createCallback(callback, thisArg); + forEach(collection, function(value, key, collection) { key = callback(value, key, collection); (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1); @@ -1561,7 +1620,7 @@ } } } else { - forEach(collection, function(value, index, collection) { + each(collection, function(value, index, collection) { return !(result = !!callback(value, index, collection)) && indicatorObject; }); } @@ -1601,7 +1660,7 @@ } } } else { - forEach(collection, function(value, index, collection) { + each(collection, function(value, index, collection) { if (callback(value, index, collection)) { result.push(value); } @@ -1633,6 +1692,7 @@ function find(collection, callback, thisArg) { var result; callback = createCallback(callback, thisArg); + forEach(collection, function(value, index, collection) { if (callback(value, index, collection)) { result = value; @@ -1664,25 +1724,20 @@ * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); * // => alerts each number value (order is not guaranteed) */ - var forEach = function (collection, callback, thisArg) { - var index, iteratee = collection, result = collection; - if (!collection) return result; - callback = callback && typeof thisArg == 'undefined' ? callback : createCallback(callback, thisArg); - var length = iteratee.length; index = -1; - if (typeof length == 'number') { + function forEach(collection, callback, thisArg) { + if (callback && typeof thisArg == 'undefined' && isArray(collection)) { + var index = -1, + length = collection.length; + while (++index < length) { - if (callback(iteratee[index], index, collection) === indicatorObject) return result - } - } - else { - for (index in iteratee) { - if (hasOwnProperty.call(iteratee, index)) { - if (callback(iteratee[index], index, collection) === indicatorObject) return result; + if (callback(collection[index], index, collection) === indicatorObject) { + break; } - } - } - - }; + } + } else { + each(collection, callback, thisArg); + }; + } /** * Creates an object composed of keys returned from running each element of @@ -1713,6 +1768,7 @@ function groupBy(collection, callback, thisArg) { var result = {}; callback = createCallback(callback, thisArg); + forEach(collection, function(value, key, collection) { key = callback(value, key, collection); (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value); @@ -1785,7 +1841,7 @@ result[index] = callback(collection[index], index, collection); } } else { - forEach(collection, function(value, key, collection) { + each(collection, function(value, key, collection) { result[++index] = callback(value, key, collection); }); } @@ -1825,7 +1881,7 @@ if (callback || !isArray(collection)) { callback = createCallback(callback, thisArg); - forEach(collection, function(value, index, collection) { + each(collection, function(value, index, collection) { var current = callback(value, index, collection); if (current > computed) { computed = current; @@ -1869,7 +1925,7 @@ if (callback || !isArray(collection)) { callback = createCallback(callback, thisArg); - forEach(collection, function(value, index, collection) { + each(collection, function(value, index, collection) { var current = callback(value, index, collection); if (current < computed) { computed = current; @@ -1933,12 +1989,25 @@ */ function reduce(collection, callback, accumulator, thisArg) { var noaccum = arguments.length < 3; - callback || (callback = identity); - forEach(collection, function(value, index, collection) { - accumulator = noaccum - ? (noaccum = false, value) - : callback.call(thisArg, accumulator, value, index, collection) - }); + callback = createCallback(callback, thisArg, indicatorObject); + + if (isArray(collection)) { + var index = -1, + length = collection.length; + + if (noaccum) { + accumulator = collection[++index]; + } + while (++index < length) { + accumulator = callback(accumulator, collection[index], index, collection); + } + } else { + each(collection, function(value, index, collection) { + accumulator = noaccum + ? (noaccum = false, value) + : callback(accumulator, value, index, collection) + }); + } return accumulator; } @@ -1969,12 +2038,12 @@ var props = keys(collection); length = props.length; } - callback || (callback = identity); + callback = createCallback(callback, thisArg, indicatorObject); forEach(collection, function(value, index, collection) { index = props ? props[--length] : --length; accumulator = noaccum ? (noaccum = false, iteratee[index]) - : callback.call(thisArg, accumulator, iteratee[index], index, collection); + : callback(accumulator, iteratee[index], index, collection); }); return accumulator; } @@ -2088,7 +2157,7 @@ } } } else { - forEach(collection, function(value, index, collection) { + each(collection, function(value, index, collection) { return (result = callback(value, index, collection)) && indicatorObject; }); } @@ -2123,6 +2192,7 @@ function sortBy(collection, callback, thisArg) { var result = []; callback = createCallback(callback, thisArg); + forEach(collection, function(value, index, collection) { result.push({ 'criteria': callback(value, index, collection), @@ -2396,8 +2466,8 @@ * @memberOf _ * @category Arrays * @param {Array} [array1, array2, ...] Arrays to process. - * @returns {Array} Returns a new array of unique elements, in order, that are - * present in **all** of the arrays. + * @returns {Array} Returns a new array of unique elements that are present + * in **all** of the arrays. * @example * * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); @@ -2406,19 +2476,23 @@ function intersection(array) { var args = arguments, argsLength = args.length, + index = -1, + length = array ? array.length : 0, result = []; - forEach(array, function(value) { + outer: + while (++index < length) { + var value = array[index]; if (indexOf(result, value) < 0) { - var length = argsLength; - while (--length) { - if (indexOf(args[length], value) < 0) { - return; + var argsIndex = argsLength; + while (--argsIndex) { + if (indexOf(args[argsIndex], value) < 0) { + continue outer; } } result.push(value); } - }); + } return result; } @@ -2629,9 +2703,10 @@ var low = 0, high = array ? array.length : low; - // explicitly reference `identity` for better engine inlining + // explicitly reference `identity` for better inlining in Firefox callback = callback ? createCallback(callback, thisArg) : identity; value = callback(value); + while (low < high) { var mid = (low + high) >>> 1; callback(array[mid]) < value @@ -3161,8 +3236,6 @@ /** * This function returns the first argument passed to it. * - * Note: This function is used throughout Lo-Dash as a default callback. - * * @static * @memberOf _ * @category Utilities @@ -3501,7 +3574,7 @@ /*--------------------------------------------------------------------------*/ /** - * Wraps the value in a `lodash` wrapper object. + * Creates a `lodash` object that wraps the given `value`. * * @static * @memberOf _ @@ -3555,22 +3628,19 @@ } /** - * This function returns the wrapper object. - * - * Note: This function is defined to ensure the existing wrapper object is - * returned, instead of creating a new wrapper object like the `_.chain` - * method does. + * Enables method chaining on the wrapper object. * * @name chain - * @deprecated * @memberOf _ * @category Chaining * @returns {Mixed} Returns the wrapper object. * @example * - * var wrapped = _([1, 2, 3]); - * wrapped === wrapped.chain(); - * // => true + * var sum = _([1, 2, 3]) + * .chain() + * .reduce(function(sum, num) { return sum + num; }) + * .value() + * // => 6` */ function wrapperChain() { this.__chain__ = true; @@ -3590,7 +3660,7 @@ * // => '1,2,3' */ function wrapperToString() { - return String(this.__wrapped__); + return this.__wrapped__ + ''; } /** @@ -3612,11 +3682,17 @@ /*--------------------------------------------------------------------------*/ + // add functions that return wrapped values when chaining + lodash.after = after; + lodash.bind = bind; lodash.bindAll = bindAll; - lodash.chain = chain; lodash.compact = compact; + lodash.compose = compose; lodash.countBy = countBy; + lodash.debounce = debounce; lodash.defaults = defaults; + lodash.defer = defer; + lodash.delay = delay; lodash.difference = difference; lodash.filter = filter; lodash.flatten = flatten; @@ -3630,9 +3706,11 @@ lodash.keys = keys; lodash.map = map; lodash.max = max; + lodash.memoize = memoize; lodash.min = min; lodash.object = object; lodash.omit = omit; + lodash.once = once; lodash.pairs = pairs; lodash.pick = pick; lodash.pluck = pluck; @@ -3642,6 +3720,7 @@ lodash.shuffle = shuffle; lodash.sortBy = sortBy; lodash.tap = tap; + lodash.throttle = throttle; lodash.times = times; lodash.toArray = toArray; lodash.union = union; @@ -3649,6 +3728,7 @@ lodash.values = values; lodash.where = where; lodash.without = without; + lodash.wrap = wrap; lodash.zip = zip; // add aliases @@ -3664,14 +3744,8 @@ /*--------------------------------------------------------------------------*/ // add functions that return unwrapped values when chaining - lodash.after = after; - lodash.bind = bind; lodash.clone = clone; - lodash.compose = compose; lodash.contains = contains; - lodash.debounce = debounce; - lodash.defer = defer; - lodash.delay = delay; lodash.escape = escape; lodash.every = every; lodash.find = find; @@ -3694,10 +3768,8 @@ lodash.isString = isString; lodash.isUndefined = isUndefined; lodash.lastIndexOf = lastIndexOf; - lodash.memoize = memoize; lodash.mixin = mixin; lodash.noConflict = noConflict; - lodash.once = once; lodash.random = random; lodash.reduce = reduce; lodash.reduceRight = reduceRight; @@ -3706,10 +3778,8 @@ lodash.some = some; lodash.sortedIndex = sortedIndex; lodash.template = template; - lodash.throttle = throttle; lodash.unescape = unescape; lodash.uniqueId = uniqueId; - lodash.wrap = wrap; // add aliases lodash.all = every; @@ -3732,6 +3802,8 @@ /*--------------------------------------------------------------------------*/ + lodash.chain = chain; + /** * The semantic version number. * @@ -3739,7 +3811,7 @@ * @memberOf _ * @type String */ - lodash.VERSION = '1.0.0-rc.2'; + lodash.VERSION = '1.0.0-rc.3'; // add functions to `lodash.prototype` mixin(lodash); @@ -3748,8 +3820,8 @@ lodash.prototype.chain = wrapperChain; lodash.prototype.value = wrapperValueOf; - // add mutator `Array` functions to the wrapper - forEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) { + // add `Array` mutator functions to the wrapper + each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) { var func = arrayRef[methodName]; lodash.prototype[methodName] = function() { var value = this.__wrapped__; @@ -3764,8 +3836,8 @@ }; }); - // add accessor `Array` functions to the wrapper - forEach(['concat', 'join', 'slice'], function(methodName) { + // add `Array` accessor functions to the wrapper + each(['concat', 'join', 'slice'], function(methodName) { var func = arrayRef[methodName]; lodash.prototype[methodName] = function() { var value = this.__wrapped__, diff --git a/lodash.underscore.min.js b/lodash.underscore.min.js index afeffa6a05..daba5025c4 100644 --- a/lodash.underscore.min.js +++ b/lodash.underscore.min.js @@ -1,33 +1,33 @@ /*! - Lo-Dash 1.0.0-rc.2 (Custom Build) lodash.com/license + Lo-Dash 1.0.0-rc.3 (Custom Build) lodash.com/license Build: `lodash underscore -m -o ./lodash.underscore.min.js` Underscore.js 1.4.3 underscorejs.org/LICENSE */ -;(function(e,t){function n(e){if(e&&"object"==typeof e&&e.__wrapped__)return e;if(!(this instanceof n))return new n(e);this.__wrapped__=e}function r(e,t){var n=e.b,r=t.b,e=e.a,t=t.a;if(e!==t){if(e>t||"undefined"==typeof e)return 1;if(en?0:n);++rr&&(r=n,u=e)});else for(;++iu&&(u=e[i]);return u}function L(e,t){return C(e,t+"")}function A(e,t,n,r){var i=3>arguments.length;return t||(t=I),At(e,function(e,s,o){n=i?(i=!1,e):t.call(r,n,e,s,o)}),n}function O(e,t,n,r){var i=e?e.length:0,s=3>arguments.length;if("number"!=typeof i)var o=Lt(e),i=o.length;return t||(t=I -),At(e,function(u,a,f){a=o?o[--i]:--i,n=s?(s=!1,e[a]):t.call(r,n,e[a],a,f)}),n}function M(e,t,n){var r,t=s(t,n);if(kt(e))for(var n=-1,i=e.length;++nn?ft -(0,i+n):n||0)-1;else if(n)return r=B(e,t),e[r]===t?r:-1;for(;++r>>1,n(e[r])P(a,f))n&&a.push(f),u.push(r)}return u}function F(e,t){return bt|| -it&&2"']/g,G=/['\n\r\t\u2028\u2029\\]/g,Y=Math.ceil,Z=z.concat,et=Math.floor,tt=U.hasOwnProperty,nt=z.push,rt=U.toString,it=J.test(it=f.bind)&&it,st=J.test(st=Array.isArray)&&st,ot=e.isFinite,ut=e.isNaN,at=J.test(at=Object.keys)&&at,ft=Math.max,lt=Math.min,ct=Math.random,ht="[object Array]",pt="[object Boolean]",dt="[object Date]",vt="[object Number]",mt="[object Object]",gt="[object RegExp]",yt="[object String]",U=!!e.attachEvent,U=it&&!/\n|true/ -.test(it+U),bt=it&&!U,wt=(wt={0:1,length:1},z.splice.call(wt,0,1),wt[0]),Et={"boolean":!1,"function":!0,object:!0,number:!1,string:!1,"undefined":!1},St={"\\":"\\","'":"'","\n":"n","\r":"r"," ":"t","\u2028":"u2028","\u2029":"u2029"};n.templateSettings={escape:/<%-([\s\S]+?)%>/g,evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,variable:""},n.isArguments=function(e){return"[object Arguments]"==rt.call(e)},n.isArguments(arguments)||(n.isArguments=function(e){return e?tt.call(e,"callee"):!1} -);var xt=function(e,t){var n;if(!e)return e;t||(t=I);for(n in e)if(t(e[n],n,e)===X)break;return e},Tt=function(e,t){var n;if(!e)return e;t||(t=I);for(n in e)if(tt.call(e,n)&&t(e[n],n,e)===X)break;return e},Nt={"&":"&","<":"<",">":">",'"':""","'":"'"},Ct=v(Nt),kt=st||function(e){return rt.call(e)==ht};g(/x/)&&(g=function(e){return"[object Function]"==rt.call(e)});var Lt=at?function(e){return y(e)?at(e):[]}:h,At=function(e,t,n){if(!e)return e;var t=t&&"undefined"==typeof n?t:s(t -,n),r=e.length,n=-1;if("number"==typeof r){for(;++nP(r,s,n)&&i.push(s)}return i},n.filter=T,n.flatten=D,n.forEach=At,n.functions=d,n.groupBy=function(e,t,n){var r={},t=s(t,n);return At(e,function(e,n,i){n=t(e,n,i),(tt.call(r,n)?r[n]:r[n]=[]).push(e)}),r},n.initial=function(e,t,n){if(!e)return[];var r=e.length;return f(e,0,lt(ft(0,r-(null==t||n?1:t||0)),r))},n.intersection=function(e){var t=arguments,n=t.length -,r=[];return At(e,function(e){if(0>P(r,e)){for(var i=n;--i;)if(0>P(t[i],e))return;r.push(e)}}),r},n.invert=v,n.invoke=function(e,t){var n=f(arguments,2),r="function"==typeof t,i=[];return At(e,function(e){i.push((r?t:e[t]).apply(e,n))}),i},n.keys=Lt,n.map=C,n.max=k,n.min=function(e,t,n){var r=Infinity,i=-1,o=e?e.length:0,u=r;if(t||!kt(e))t=s(t,n),At(e,function(e,n,i){n=t(e,n,i),nP(t,r,1)&&(n[r]=e)}),n},n.pairs=function(e){var t=[];return Tt(e,function(e,n){t.push([n,e])}),t},n.pick=function(e){for(var t=0,n=Z.apply(z,arguments),r=n.length,i={};++tP(arguments,i,1)&&r.push(i)}return r},n.zip=function(e){for(var t=-1,n=e?k(L(arguments,"length")):0,r=Array(n);++te?t():function(){if(1>--e)return t.apply(this,arguments)}},n.bind=F,n.clone=function(e){return e&&Et[typeof e]?kt(e)?f(e):c({},e):e},n.compose=function(){var e=arguments;return function(){for(var t=arguments,n=e.length;n--;)t=[e[n].apply(this,t)];return t[0]}},n.contains=S,n.debounce=function(e,t,n){function r(){u=null,n||(s=e.apply(o,i))}var i,s,o,u;return function(){var a=n&&!u;return i=arguments,o=this,clearTimeout(u),u=setTimeout( -r,t),a&&(s=e.apply(o,i)),s}},n.defer=function(e){var n=f(arguments,1);return setTimeout(function(){e.apply(t,n)},1)},n.delay=function(e,n){var r=f(arguments,2);return setTimeout(function(){e.apply(t,r)},n)},n.escape=function(e){return null==e?"":(e+"").replace(Q,u)},n.every=x,n.find=N,n.has=function(e,t){return e?tt.call(e,t):!1},n.identity=I,n.indexOf=P,n.isArray=kt,n.isBoolean=function(e){return!0===e||!1===e||rt.call(e)==pt},n.isDate=function(e){return rt.call(e)==dt},n.isElement=function(e){return e?1=== -e.nodeType:!1},n.isEmpty=function(e){if(!e)return!0;if(kt(e)||w(e))return!e.length;for(var t in e)if(tt.call(e,t))return!1;return!0},n.isEqual=m,n.isFinite=function(e){return ot(e)&&!ut(parseFloat(e))},n.isFunction=g,n.isNaN=function(e){return b(e)&&e!=+e},n.isNull=function(e){return null===e},n.isNumber=b,n.isObject=y,n.isRegExp=function(e){return rt.call(e)==gt},n.isString=w,n.isUndefined=function(e){return"undefined"==typeof e},n.lastIndexOf=function(e,t,n){var r=e?e.length:0;for("number"==typeof -n&&(r=(0>n?ft(0,r+n):lt(n,r-1))+1);r--;)if(e[r]===t)return r;return-1},n.memoize=function(e,t){var n={};return function(){var r=t?t.apply(this,arguments):arguments[0];return tt.call(n,r)?n[r]:n[r]=e.apply(this,arguments)}},n.mixin=q,n.noConflict=function(){return e._=V,this},n.once=function(e){var t,n=!1;return function(){return n?t:(n=!0,t=e.apply(this,arguments),e=null,t)}},n.random=function(e,t){return null==e&&null==t&&(t=1),e=+e||0,null==t&&(t=e,e=0),e+et(ct()*((+t||0)-e+1))},n.reduce=A,n.reduceRight= -O,n.result=function(e,t){var n=e?e[t]:null;return g(n)?e[t]():n},n.size=function(e){var t=e?e.length:0;return"number"==typeof t?t:Lt(e).length},n.some=M,n.sortedIndex=B,n.template=function(e,t,r){e||(e="");var r=p({},r,n.templateSettings),i=0,s="__p+='",u=r.variable;e.replace(RegExp((r.escape||K).source+"|"+(r.interpolate||K).source+"|"+(r.evaluate||K).source+"|$","g"),function(t,n,r,u,a){s+=e.slice(i,a).replace(G,o),s+=n?"'+_['escape']("+n+")+'":u?"';"+u+";__p+='":r?"'+((__t=("+r+"))==null?'':__t)+'" -:"",i=a+t.length}),s+="';\n",u||(u="obj",s="with("+u+"||{}){"+s+"}"),s="function("+u+"){var __t,__p='',__j=Array.prototype.join;function print(){__p+=__j.call(arguments,'')}"+s+"return __p}";try{var a=Function("_","return "+s)(n)}catch(f){throw f.source=s,f}return t?a(t):(a.source=s,a)},n.throttle=function(e,t){function n(){u=new Date,o=null,i=e.apply(s,r)}var r,i,s,o,u=0;return function(){var a=new Date,f=t-(a-u);return r=arguments,s=this,0>=f?(clearTimeout(o),o=null,u=a,i=e.apply(s,r)):o||(o=setTimeout -(n,f)),i}},n.unescape=function(e){return null==e?"":(e+"").replace($,l)},n.uniqueId=function(e){var t=++W+"";return e?e+t:t},n.wrap=function(e,t){return function(){var n=[e];return nt.apply(n,arguments),t.apply(this,n)}},n.all=x,n.any=M,n.detect=N,n.foldl=A,n.foldr=O,n.include=S,n.inject=A,n.first=_,n.last=function(e,t,n){if(e){var r=e.length;return null==t||n?e[r-1]:f(e,ft(0,r-t))}},n.take=_,n.head=_,n.VERSION="1.0.0-rc.2",q(n),n.prototype.chain=function(){return this.__chain__=!0,this},n.prototype -.value=function(){return this.__wrapped__},At("pop push reverse shift sort splice unshift".split(" "),function(e){var t=z[e];n.prototype[e]=function(){var e=this.__wrapped__;return t.apply(e,arguments),wt&&e.length===0&&delete e[0],this}}),At(["concat","join","slice"],function(e){var t=z[e];n.prototype[e]=function(){var e=t.apply(this.__wrapped__,arguments);return this.__chain__&&(e=new n(e),e.__chain__=!0),e}}),R?"object"==typeof module&&module&&module.exports==R?(module.exports=n)._=n:R._=n:e._= -n})(this); \ No newline at end of file +;(function(e,t){function n(e){if(e&&typeof e=="object"&&e.__wrapped__)return e;if(!(this instanceof n))return new n(e);this.__wrapped__=e}function r(e,t){var n=e.b,r=t.b,e=e.a,t=t.a;if(e!==t){if(e>t||typeof e=="undefined")return 1;if(en?0:n);++rr&&(r=n,u=e)});else for(;++iu&&(u=e[i]);return u}function A(e,t){return k(e,t+"")}function O(e,t,n,r){var i=3>arguments.length,t=s(t,r,V);if(Ot(e)){var o=-1 +,u=e.length;for(i&&(n=e[++o]);++oarguments.length;if(typeof i!="number")var u=Mt(e),i=u.length;return t=s(t,r,V),C(e,function(r,s,a){s=u?u[--i]:--i,n=o?(o=!1,e[s]):t(n,e[s],s,a)}),n}function _(e,t,n){var r,t=s(t,n);if(Ot(e))for(var n=-1,i=e.length;++nn?lt(0,i+n):n||0)-1;else if(n)return r=j(e,t),e[r]===t?r:-1;for(;++r>>1,n(e[r])H(a,f))n&&a.push(f),u.push(r)}return u}function I(e,t){return wt||st&&2"']/g,Y=/['\n\r\t\u2028\u2029\\]/g,Z=Math.ceil,et=W.concat,tt=Math.floor,nt=z.hasOwnProperty,rt=W.push,it=z.toString,st=K.test(st=f.bind)&&st,ot=K.test(ot=Array.isArray)&&ot,ut=e.isFinite,at=e.isNaN,ft= +K.test(ft=Object.keys)&&ft,lt=Math.max,ct=Math.min,ht=Math.random,pt="[object Array]",dt="[object Boolean]",vt="[object Date]",mt="[object Number]",gt="[object Object]",yt="[object RegExp]",bt="[object String]",z=!!e.attachEvent,z=st&&!/\n|true/.test(st+z),wt=st&&!z,Et=(Et={0:1,length:1},W.splice.call(Et,0,1),Et[0]),St=arguments.constructor==Object,xt={"boolean":!1,"function":!0,object:!0,number:!1,string:!1,"undefined":!1},Tt={"\\":"\\","'":"'","\n":"n","\r":"r"," ":"t","\u2028":"u2028","\u2029" +:"u2029"};n.templateSettings={escape:/<%-([\s\S]+?)%>/g,evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,variable:""};var Nt=function(e,t,n){if(!e)return e;var t=t&&typeof n=="undefined"?t:s(t,n),r=e.length,n=-1;if(typeof r=="number"){for(;++n":">",'"':""","'":"'"},At=v(Lt),Ot=ot||function(e){return St&&e instanceof Array||it.call(e)==pt};g(/x/)&&(g=function(e){return e instanceof Function||"[object Function]"==it.call(e)});var Mt=ft?function(e){return y(e)?ft(e):[]}:h;n.after=function(e,t){return 1> +e?t():function(){if(1>--e)return t.apply(this,arguments)}},n.bind=I,n.bindAll=function(e){for(var t=arguments,n=1H(r,s,n)&&i.push(s)}return i},n.filter=T,n.flatten=P,n.forEach=C,n.functions=d,n.groupBy=function(e,t,n){var r={},t=s(t,n);return C(e,function(e,n,i){n=t(e,n,i),(nt.call(r,n)?r[n]:r[n]=[]).push(e)}),r},n.initial=function(e,t,n){if(!e)return[];var r=e.length;return f(e,0,ct(lt(0,r-(null==t||n?1:t||0)),r))},n.intersection=function(e){var t=arguments,n=t.length,r=-1,i=e?e.length:0,s=[];e:for(;++rH(s,o)){for(var u=n;--u;)if(0>H(t[u],o))continue e;s.push(o)}}return s},n.invert= +v,n.invoke=function(e,t){var n=f(arguments,2),r=typeof t=="function",i=[];return C(e,function(e){i.push((r?t:e[t]).apply(e,n))}),i},n.keys=Mt,n.map=k,n.max=L,n.memoize=function(e,t){var n={};return function(){var r=t?t.apply(this,arguments):arguments[0];return nt.call(n,r)?n[r]:n[r]=e.apply(this,arguments)}},n.min=function(e,t,n){var r=Infinity,i=-1,o=e?e.length:0,u=r;if(t||!Ot(e))t=s(t,n),Nt(e,function(e,n,i){n=t(e,n,i),nH(t,r,1)&&(n[r]=e)}),n},n.once=function(e){var t,n=!1;return function(){return n?t:(n=!0,t=e.apply(this,arguments),e=null,t)}},n.pairs=function(e){var t=[];return kt(e,function(e,n){t.push([n,e])}),t},n.pick=function(e){for(var t=0,n=et.apply(W,arguments),r=n.length,i={};++t=f?(clearTimeout(o),o=null,u=a,i=e.apply(s,r)):o||(o=setTimeout(n,f)),i}},n.times=function(e,t,n){for(var e=+e||0,r=-1,i=Array(e);++rH(arguments,i,1)&&r.push(i)}return r},n.wrap=function(e,t){return function(){var n=[e];return rt.apply(n,arguments),t.apply(this,n)}},n.zip=function(e){for(var t=-1,n=e?L(A(arguments,"length")):0,r=Array(n);++tn?lt(0,r+n):ct(n,r-1))+1);r--;)if(e[r]===t)return r; +return-1},n.mixin=R,n.noConflict=function(){return e._=$,this},n.random=function(e,t){return null==e&&null==t&&(t=1),e=+e||0,null==t&&(t=e,e=0),e+tt(ht()*((+t||0)-e+1))},n.reduce=O,n.reduceRight=M,n.result=function(e,t){var n=e?e[t]:null;return g(n)?e[t]():n},n.size=function(e){var t=e?e.length:0;return typeof t=="number"?t:Mt(e).length},n.some=_,n.sortedIndex=j,n.template=function(e,t,r){e||(e="");var r=p({},r,n.templateSettings),i=0,s="__p+='",u=r.variable;e.replace(RegExp((r.escape||Q).source+"|"+ +(r.interpolate||Q).source+"|"+(r.evaluate||Q).source+"|$","g"),function(t,n,r,u,a){s+=e.slice(i,a).replace(Y,o),s+=n?"'+_['escape']("+n+")+'":u?"';"+u+";__p+='":r?"'+((__t=("+r+"))==null?'':__t)+'":"",i=a+t.length}),s+="';\n",u||(u="obj",s="with("+u+"||{}){"+s+"}"),s="function("+u+"){var __t,__p='',__j=Array.prototype.join;function print(){__p+=__j.call(arguments,'')}"+s+"return __p}";try{var a=Function("_","return "+s)(n)}catch(f){throw f.source=s,f}return t?a(t):(a.source=s,a)},n.unescape=function( +e){return null==e?"":(e+"").replace(J,l)},n.uniqueId=function(e){var t=++X+"";return e?e+t:t},n.all=x,n.any=_,n.detect=N,n.foldl=O,n.foldr=M,n.include=S,n.inject=O,n.first=D,n.last=function(e,t,n){if(e){var r=e.length;return null==t||n?e[r-1]:f(e,lt(0,r-t))}},n.take=D,n.head=D,n.chain=function(e){return e=new n(e),e.__chain__=!0,e},n.VERSION="1.0.0-rc.3",R(n),n.prototype.chain=function(){return this.__chain__=!0,this},n.prototype.value=function(){return this.__wrapped__},Nt("pop push reverse shift sort splice unshift" +.split(" "),function(e){var t=W[e];n.prototype[e]=function(){var e=this.__wrapped__;return t.apply(e,arguments),Et&&e.length===0&&delete e[0],this}}),Nt(["concat","join","slice"],function(e){var t=W[e];n.prototype[e]=function(){var e=t.apply(this.__wrapped__,arguments);return this.__chain__&&(e=new n(e),e.__chain__=!0),e}}),U?typeof module=="object"&&module&&module.exports==U?(module.exports=n)._=n:U._=n:e._=n})(this); \ No newline at end of file diff --git a/package.json b/package.json index b2a0a20832..35922b1db3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "lodash", - "version": "1.0.0-rc.2", - "description": "A utility library, usable as a drop-in replacement for Underscore, delivering performance, bug fixes, and additional features.", + "version": "1.0.0-rc.3", + "description": "An alternative to Underscore.js, delivering consistency, customization, performance, and extra features.", "homepage": "http://lodash.com", "main": "./lodash", "keywords": [ diff --git a/perf/perf.js b/perf/perf.js index a8d80a1084..d8dda55b30 100644 --- a/perf/perf.js +++ b/perf/perf.js @@ -297,41 +297,77 @@ nestedNumbers2 = [1, [2], [3, [[4]]]];\ \ for (index = 0; index < limit; index++) {\ - numbers2[index] = index;\ object2["key" + index] = index;\ objects2[index] = { "num": index };\ + numbers2[index] = index;\ }\ }\ \ if (typeof multiArrays != "undefined") {\ - var twentyFiveValues = Array(25),\ + var twentyValues = Array(20),\ + twentyValues2 = Array(20),\ + twentyFiveValues = Array(25),\ twentyFiveValues2 = Array(25),\ + thirtyValues = Array(30),\ + thirtyValues2 = Array(30),\ + fortyValues = Array(40),\ + fortyValues2 = Array(40),\ fiftyValues = Array(50),\ + fiftyValues2 = Array(50),\ seventyFiveValues = Array(75),\ seventyFiveValues2 = Array(75),\ + hundredValues = Array(100),\ + hundredValues2 = Array(100),\ lowerChars = "abcdefghijklmnopqrstuvwxyz".split(""),\ upperChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");\ \ - for (index = 0; index < 75; index++) {\ - if (index < 26) {\ - if (index < 20) {\ - twentyFiveValues[index] = lowerChars[index];\ - twentyFiveValues2[index] = upperChars[index];\ - }\ - else if (index < 25) {\ - twentyFiveValues[index] =\ - twentyFiveValues2[index] = index;\ - }\ + for (index = 0; index < 100; index++) {\ + if (index < 15) {\ + twentyValues[index] = lowerChars[index];\ + twentyValues2[index] = upperChars[index];\ + }\ + if (index < 20) {\ + twentyValues[index] =\ + twentyValues2[index] = index;\ + \ + twentyFiveValues[index] = lowerChars[index];\ + twentyFiveValues2[index] = upperChars[index];\ + }\ + if (index < 25) {\ + twentyFiveValues[index] =\ + twentyFiveValues2[index] = index;\ + \ + thirtyValues[index] =\ + fortyValues[index] =\ fiftyValues[index] =\ - seventyFiveValues[index] = lowerChars[index];\ - seventyFiveValues2[index] = upperChars[index];\ + seventyFiveValues[index] =\ + hundredValues[index] = lowerChars[index];\ + \ + thirtyValues2[index] =\ + fortyValues2[index] =\ + fiftyValues2[index] =\ + seventyFiveValues2[index] =\ + hundredValues2[index] = upperChars[index];\ }\ else {\ + if (index < 30) {\ + thirtyValues[index] =\ + thirtyValues2[index] = index;\ + }\ + if (index < 40) {\ + fortyValues[index] =\ + fortyValues2[index] = index;\ + }\ if (index < 50) {\ - fiftyValues[index] = index;\ + fiftyValues[index] =\ + fiftyValues2[index] = index;\ + }\ + if (index < 75) {\ + seventyFiveValues[index] =\ + seventyFiveValues2[index] = index;\ }\ - seventyFiveValues[index] = index;\ - seventyFiveValues2[index] = index + (index < 60 ? 75 : 0);\ + hundredValues[index] =\ + hundredValues2[index] = index;\ }\ }\ }\ @@ -630,45 +666,33 @@ suites.push( Benchmark.Suite('`_.difference`') .add(buildName, '\ - lodash.difference(numbers, fourNumbers, twoNumbers)' + lodash.difference(numbers, twoNumbers, fourNumbers)' ) .add(otherName, '\ - _.difference(numbers, fourNumbers, twoNumbers)' + _.difference(numbers, twoNumbers, fourNumbers)' ) ); suites.push( - Benchmark.Suite('`_.difference` iterating 25 elements') + Benchmark.Suite('`_.difference` iterating 30 elements') .add(buildName, { - 'fn': 'lodash.difference(twentyFiveValues, twentyFiveValues2)', + 'fn': 'lodash.difference(thirtyValues, thirtyValues2)', 'teardown': 'function multiArrays(){}' }) .add(otherName, { - 'fn': '_.difference(twentyFiveValues, twentyFiveValues2)', + 'fn': '_.difference(thirtyValues, thirtyValues2)', 'teardown': 'function multiArrays(){}' }) ); suites.push( - Benchmark.Suite('`_.difference` iterating 50 and 75 elements') + Benchmark.Suite('`_.difference` iterating 20 and 40 elements') .add(buildName, { - 'fn': 'lodash.difference(fiftyValues, seventyFiveValues2)', + 'fn': 'lodash.difference(twentyValues, fortyValues2)', 'teardown': 'function multiArrays(){}' }) .add(otherName, { - 'fn': '_.difference(fiftyValues, seventyFiveValues2)', - 'teardown': 'function multiArrays(){}' - }) - ); - - suites.push( - Benchmark.Suite('`_.difference` iterating 75 elements') - .add(buildName, { - 'fn': 'lodash.difference(seventyFiveValues, seventyFiveValues2)', - 'teardown': 'function multiArrays(){}' - }) - .add(otherName, { - 'fn': '_.difference(seventyFiveValues, seventyFiveValues2)', + 'fn': '_.difference(twentyValues, fortyValues2)', 'teardown': 'function multiArrays(){}' }) ); @@ -936,45 +960,21 @@ suites.push( Benchmark.Suite('`_.intersection`') .add(buildName, '\ - lodash.intersection(numbers, fourNumbers, twoNumbers)' + lodash.intersection(numbers, twoNumbers, fourNumbers)' ) .add(otherName, '\ - _.intersection(numbers, fourNumbers, twoNumbers)' + _.intersection(numbers, twoNumbers, fourNumbers)' ) ); suites.push( - Benchmark.Suite('`_.intersection` iterating 25 elements') + Benchmark.Suite('`_.intersection` iterating 100 elements') .add(buildName, { - 'fn': 'lodash.intersection(twentyFiveValues, twentyFiveValues2)', + 'fn': 'lodash.intersection(hundredValues, hundredValues2)', 'teardown': 'function multiArrays(){}' }) .add(otherName, { - 'fn': '_.intersection(twentyFiveValues, twentyFiveValues2)', - 'teardown': 'function multiArrays(){}' - }) - ); - - suites.push( - Benchmark.Suite('`_.intersection` iterating 50 and 75 elements') - .add(buildName, { - 'fn': 'lodash.intersection(fiftyValues, seventyFiveValues2)', - 'teardown': 'function multiArrays(){}' - }) - .add(otherName, { - 'fn': '_.intersection(fiftyValues, seventyFiveValues2)', - 'teardown': 'function multiArrays(){}' - }) - ); - - suites.push( - Benchmark.Suite('`_.intersection` iterating 75 elements') - .add(buildName, { - 'fn': 'lodash.intersection(seventyFiveValues, seventyFiveValues2)', - 'teardown': 'function multiArrays(){}' - }) - .add(otherName, { - 'fn': '_.intersection(seventyFiveValues, seventyFiveValues2)', + 'fn': '_.intersection(hundredValues, hundredValues2)', 'teardown': 'function multiArrays(){}' }) ); @@ -1624,51 +1624,51 @@ suites.push( Benchmark.Suite('`_.union`') .add(buildName, '\ - lodash.union(numbers, fourNumbers, twoNumbers)' + lodash.union(numbers, twoNumbers, fourNumbers)' ) .add(otherName, '\ - _.union(numbers, fourNumbers, twoNumbers)' + _.union(numbers, twoNumbers, fourNumbers)' ) ); + suites.push( + Benchmark.Suite('`_.union` iterating an array of 75 elements') + .add(buildName, { + 'fn': 'lodash.union(fiftyValues, twentyFiveValues2);', + 'teardown': 'function multiArrays(){}' + }) + .add(otherName, { + 'fn': '_.union(fiftyValues, twentyFiveValues2);', + 'teardown': 'function multiArrays(){}' + }) + ); + /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.uniq`') .add(buildName, '\ - lodash.uniq(numbers.concat(fourNumbers, twoNumbers))' + lodash.uniq(numbers.concat(twoNumbers, fourNumbers))' ) .add(otherName, '\ - _.uniq(numbers.concat(fourNumbers, twoNumbers))' + _.uniq(numbers.concat(twoNumbers, fourNumbers))' ) ); suites.push( Benchmark.Suite('`_.uniq` with `callback`') .add(buildName, '\ - lodash.uniq(numbers.concat(fourNumbers, twoNumbers), function(num) {\ + lodash.uniq(numbers.concat(twoNumbers, fourNumbers), function(num) {\ return num % 2;\ });' ) .add(otherName, '\ - _.uniq(numbers.concat(fourNumbers, twoNumbers), function(num) {\ + _.uniq(numbers.concat(twoNumbers, fourNumbers), function(num) {\ return num % 2;\ })' ) ); - suites.push( - Benchmark.Suite('`_.uniq` iterating an array of 50 elements') - .add(buildName, { - 'fn': 'lodash.uniq(twentyFiveValues.concat(twentyFiveValues2));', - 'teardown': 'function multiArrays(){}' - }) - .add(otherName, { - 'fn': '_.uniq(twentyFiveValues.concat(twentyFiveValues2));', - 'teardown': 'function multiArrays(){}' - }) - ); - suites.push( Benchmark.Suite('`_.uniq` iterating an array of 75 elements') .add(buildName, { @@ -1681,18 +1681,6 @@ }) ); - suites.push( - Benchmark.Suite('`_.uniq` iterating an array of 100 elements') - .add(buildName, { - 'fn': 'lodash.uniq(seventyFiveValues.concat(twentyFiveValues2));', - 'teardown': 'function multiArrays(){}' - }) - .add(otherName, { - 'fn': '_.uniq(seventyFiveValues.concat(twentyFiveValues2));', - 'teardown': 'function multiArrays(){}' - }) - ); - /*--------------------------------------------------------------------------*/ suites.push( @@ -1730,37 +1718,13 @@ ); suites.push( - Benchmark.Suite('`_.without` iterating an array of 25 elements') - .add(buildName, { - 'fn': 'lodash.without.apply(lodash, [twentyFiveValues].concat(twentyFiveValues2));', - 'teardown': 'function multiArrays(){}' - }) - .add(otherName, { - 'fn': '_.without.apply(_, [twentyFiveValues].concat(twentyFiveValues2));', - 'teardown': 'function multiArrays(){}' - }) - ); - - suites.push( - Benchmark.Suite('`_.without` iterating an array of 75 and 50 elements') - .add(buildName, { - 'fn': 'lodash.without.apply(lodash, [seventyFiveValues2].concat(fiftyValues));', - 'teardown': 'function multiArrays(){}' - }) - .add(otherName, { - 'fn': '_.without.apply(_, [seventyFiveValues2].concat(fiftyValues));', - 'teardown': 'function multiArrays(){}' - }) - ); - - suites.push( - Benchmark.Suite('`_.without` iterating an array of 75 elements') + Benchmark.Suite('`_.without` iterating an array of 20 elements') .add(buildName, { - 'fn': 'lodash.without.apply(lodash, [seventyFiveValues].concat(seventyFiveValues2));', + 'fn': 'lodash.without.apply(lodash, [twentyValues].concat(twentyValues2));', 'teardown': 'function multiArrays(){}' }) .add(otherName, { - 'fn': '_.without.apply(_, [seventyFiveValues].concat(seventyFiveValues2));', + 'fn': '_.without.apply(_, [twentyValues].concat(twentyValues2));', 'teardown': 'function multiArrays(){}' }) ); diff --git a/test/backbone.html b/test/backbone.html index 001e5a973d..0392a66eb7 100644 --- a/test/backbone.html +++ b/test/backbone.html @@ -12,7 +12,7 @@
-

Backbone Speed Suite

+

Test

@@ -29,9 +29,23 @@

Test

); + + diff --git a/test/test-build.js b/test/test-build.js index 18a475a38a..d59674686e 100644 --- a/test/test-build.js +++ b/test/test-build.js @@ -54,9 +54,9 @@ }; /** List of all Lo-Dash methods */ - var allMethods = _.functions(_).filter(function(methodName) { - return !/^_/.test(methodName); - }); + var allMethods = _.functions(_) + .filter(function(methodName) { return !/^_/.test(methodName); }) + .concat('chain'); /** List of "Arrays" category methods */ var arraysMethods = [ @@ -86,7 +86,6 @@ /** List of "Chaining" category methods */ var chainingMethods = [ - 'chain', 'mixin', 'tap', 'value' @@ -148,6 +147,7 @@ var objectsMethods = [ 'assign', 'clone', + 'cloneDeep', 'defaults', 'extend', 'forIn', @@ -229,6 +229,7 @@ 'max', 'min', 'mixin', + 'once', 'pick', 'reduce', 'reduceRight', @@ -249,6 +250,7 @@ /** List of methods used by Underscore */ var underscoreMethods = _.without.apply(_, [allMethods].concat([ 'bindKey', + 'cloneDeep', 'forIn', 'forOwn', 'isPlainObject', @@ -445,7 +447,7 @@ console.log(e); pass = false; } - equal(pass, true, '_.' + methodName + ': ' + message); + ok(pass, '_.' + methodName + ': ' + message); } /*--------------------------------------------------------------------------*/ @@ -489,61 +491,69 @@ context._ = _; vm.runInContext(source, context); - var templates = context._.templates; - equal(templates.a(data.a).replace(/[\r\n]+/g, ''), '
  • moe
  • larry
  • curly
', basename); - equal(templates.b(data.b), 'Hello stooge.', basename); - equal(templates.c(data.c), 'Hello ES6!', basename); + equal(_.templates.a(data.a).replace(/[\r\n]+/g, ''), '
  • moe
  • larry
  • curly
', basename); + equal(_.templates.b(data.b), 'Hello stooge.', basename); + equal(_.templates.c(data.c), 'Hello ES6!', basename); delete _.templates; start(); }); }); - ['', 'moduleId=underscore'].forEach(function(command) { + var commands = [ + '', + 'moduleId=underscore' + ]; + + commands.forEach(function(command) { asyncTest('`lodash template=*.jst` exports=amd' + (command ? ' ' + command : ''), function() { var start = _.after(2, _.once(QUnit.start)); build(['-s', 'template=' + templatePath + '/*.jst', 'exports=amd'].concat(command || []), function(source, filePath) { var moduleId, basename = path.basename(filePath, '.js'), - context = createContext(), - pass = false; + context = createContext(); - (context.define = function(requires, factory) { + context.define = function(requires, factory) { factory(_); - var templates = _.templates; - moduleId = requires + ''; - pass = 'a' in templates && 'b' in templates; - }) - .amd = {}; + moduleId = requires[0]; + }; + context.define.amd = {}; vm.runInContext(source, context); - equal(moduleId, command ? 'underscore' : 'lodash'); - ok(pass, basename); + equal(moduleId, (command ? 'underscore' : 'lodash'), basename); + ok('a' in _.templates && 'b' in _.templates, basename); delete _.templates; start(); }); }); - }); - asyncTest('`lodash settings=...`', function() { - var start = _.after(2, _.once(QUnit.start)); + asyncTest('`lodash settings=...`' + (command ? ' ' + command : ''), function() { + var start = _.after(2, _.once(QUnit.start)); - build(['-s', 'template=' + templatePath + '/*.tpl', 'settings={interpolate:/\\{\\{([\\s\\S]+?)\\}\\}/}'], function(source, filePath) { - var basename = path.basename(filePath, '.js'), - context = createContext(); + build(['-s', 'template=' + templatePath + '/*.tpl', 'settings={interpolate:/\\{\\{([\\s\\S]+?)\\}\\}/}'].concat(command || []), function(source, filePath) { + var moduleId, + basename = path.basename(filePath, '.js'), + context = createContext(); - var data = { - 'd': { 'name': 'Mustache' } - }; + var data = { + 'd': { 'name': 'Mustache' } + }; - context._ = _; - vm.runInContext(source, context); - var templates = context._.templates; + context.define = function(requires, factory) { + factory(_); + moduleId = requires[0]; + }; - equal(templates.d(data.d), 'Hello Mustache!', basename); - start(); + context.define.amd = {}; + vm.runInContext(source, context); + + equal(moduleId, (command ? 'underscore' : 'lodash'), basename); + equal(_.templates.d(data.d), 'Hello Mustache!', basename); + delete _.templates; + start(); + }); }); }); }()); @@ -553,6 +563,9 @@ QUnit.module('independent builds'); (function() { + var reComment = /\/\*![\s\S]+?\*\//, + reCustom = /Custom Build/; + asyncTest('debug only', function() { var start = _.once(QUnit.start); build(['-d', '-s'], function(source, filePath) { @@ -565,6 +578,9 @@ var start = _.once(QUnit.start); build(['-d', '-s', 'backbone'], function(source, filePath) { equal(path.basename(filePath, '.js'), 'lodash.custom'); + + var comment = source.match(reComment); + ok(reCustom.test(comment)); start(); }); }); @@ -581,6 +597,9 @@ var start = _.once(QUnit.start); build(['-m', '-s', 'backbone'], function(source, filePath) { equal(path.basename(filePath, '.js'), 'lodash.custom.min'); + + var comment = source.match(reComment); + ok(reCustom.test(comment)); start(); }); }); @@ -625,7 +644,51 @@ return pass; }); - equal(actual, true, basename); + ok(actual, basename); + start(); + }); + }); + }); + }()); + + /*--------------------------------------------------------------------------*/ + + QUnit.module('underscore chaining methods'); + + (function() { + var commands = [ + 'backbone', + 'underscore' + ]; + + commands.forEach(function(command) { + asyncTest('`lodash ' + command +'`', function() { + var start = _.after(2, _.once(QUnit.start)); + + build(['-s', command], function(source, filePath) { + var basename = path.basename(filePath, '.js'), + context = createContext(); + + vm.runInContext(source, context); + var lodash = context._; + + ok(lodash.chain(1) instanceof lodash, '_.chain: ' + basename); + ok(lodash(1).chain() instanceof lodash, '_#chain: ' + basename); + + var wrapped = lodash(1); + strictEqual(wrapped.identity(), 1, '_(...) wrapped values are not chainable by default: ' + basename); + equal(String(wrapped) === '1', false, '_#toString should not be implemented: ' + basename); + equal(Number(wrapped) === 1 , false, '_#valueOf should not be implemented: ' + basename); + + wrapped.chain(); + ok(wrapped.has('x') instanceof lodash, '_#has returns wrapped values when chaining: ' + basename); + ok(wrapped.join() instanceof lodash, '_#join returns wrapped values when chaining: ' + basename); + + wrapped = lodash([1, 2, 3]); + ok(wrapped.pop() instanceof lodash, '_#pop returns wrapped values: ' + basename); + ok(wrapped.shift() instanceof lodash, '_#shift returns wrapped values: ' + basename); + deepEqual(wrapped.splice(0, 0).value(), [2], '_#splice returns wrapper: ' + basename); + start(); }); }); @@ -652,10 +715,10 @@ var object = { 'fn': lodash.bind(function(foo) { return foo + this.bar; }, { 'bar': 1 }, 1) }; equal(object.fn(), 2, '_.bind: ' + basename); - ok(lodash.clone(array, true)[0] === array[0], '_.clone should be shallow: ' + basename); - equal(lodash.contains({ 'a': 1, 'b': 2 }, 1), true, '_.contains should work with objects: ' + basename); - equal(lodash.contains([1, 2, 3], 1, 2), true, '_.contains should ignore `fromIndex`: ' + basename); - equal(lodash.every([true, false, true]), false, '_.every: ' + basename); + strictEqual(lodash.clone(array, true)[0], array[0], '_.clone should be shallow: ' + basename); + ok(lodash.contains({ 'a': 1, 'b': 2 }, 1), '_.contains should work with objects: ' + basename); + ok(lodash.contains([1, 2, 3], 1, 2), '_.contains should ignore `fromIndex`: ' + basename); + ok(!lodash.every([true, false, true]), '_.every: ' + basename); function Foo() {} Foo.prototype = { 'a': 1 }; @@ -694,18 +757,10 @@ actual = lodash.pick(object, function(value) { return value != 3; }); deepEqual(_.keys(actual), [], '_.pick should not accept a `callback`: ' + basename); - equal(lodash.some([false, true, false]), true, '_.some: ' + basename); + ok(lodash.some([false, true, false]), '_.some: ' + basename); equal(lodash.template('${a}', object), '${a}', '_.template should ignore ES6 delimiters: ' + basename); equal(lodash.uniqueId(0), '1', '_.uniqueId should ignore a prefix of `0`: ' + basename); - var wrapped = lodash(1); - equal(wrapped.clone() instanceof lodash, false, '_(...) wrapped values are not chainable by default: ' + basename); - equal(String(wrapped) === '1', false, '_#toString should not be implemented: ' + basename); - equal(Number(wrapped) === 1 , false, '_#valueOf should not be implemented: ' + basename); - - wrapped.chain(); - equal(wrapped.has('x') instanceof lodash, true, '_#has returns wrapped values when chaining: ' + basename); - start(); }); }); @@ -806,8 +861,8 @@ case 1: context.exports = {}; vm.runInContext(source, context); - ok(context._ === undefined, basename); - ok(_.isFunction(context.exports._), basename) + ok(_.isFunction(context.exports._), basename); + strictEqual(context._, undefined, basename); break; case 2: @@ -819,13 +874,13 @@ context.exports = {}; context.module = { 'exports': context.exports }; vm.runInContext(source, context); - ok(context._ === undefined, basename); ok(_.isFunction(context.module.exports), basename); + strictEqual(context._, undefined, basename); break; case 4: vm.runInContext(source, context); - ok(context._ === undefined, basename); + strictEqual(context._, undefined, basename); } start(); }); @@ -874,7 +929,12 @@ QUnit.module('output options'); (function() { - ['-o a.js', '--output a.js'].forEach(function(command, index) { + var commands = [ + '-o a.js', + '--output a.js' + ]; + + commands.forEach(function(command, index) { asyncTest('`lodash ' + command +'`', function() { var start = _.once(QUnit.start); @@ -891,7 +951,12 @@ QUnit.module('stdout options'); (function() { - ['-c', '--stdout'].forEach(function(command, index) { + var commands = [ + '-c', + '--stdout' + ]; + + commands.forEach(function(command, index) { asyncTest('`lodash ' + command +'`', function() { var written, start = _.once(QUnit.start), @@ -931,7 +996,6 @@ } catch(e) { console.log(e); } - var underscore = context._ || {}; ok(_.isString(underscore.VERSION)); ok(!/Lo-Dash/.test(result) && result.match(/\n/g).length < source.match(/\n/g).length); @@ -972,7 +1036,7 @@ deepEqual(lodash.merge(object1, object2), object3, basename); deepEqual(lodash.sortBy([3, 2, 1], _.identity), array, basename); - equal(lodash.isEqual(circular1, circular2), true, basename); + ok(lodash.isEqual(circular1, circular2), basename); var actual = lodash.clone(circular1, true); ok(actual != circular1 && actual.b == actual, basename); diff --git a/test/test.js b/test/test.js index 623c2ce0cc..c1b12c4707 100644 --- a/test/test.js +++ b/test/test.js @@ -243,7 +243,7 @@ /*--------------------------------------------------------------------------*/ - QUnit.module('lodash.clone'); + QUnit.module('cloning'); (function() { function Klass() { this.a = 1; } @@ -277,11 +277,11 @@ _.forOwn(objects, function(object, key) { test('should deep clone ' + key, function() { - var clone = _.clone(object, true); + var clone = _.cloneDeep(object); ok(_.isEqual(object, clone)); if (_.isObject(object)) { - ok(clone !== object); + notStrictEqual(clone, object); } else { skipTest(); } @@ -290,8 +290,8 @@ _.forOwn(nonCloneable, function(object, key) { test('should not clone ' + key, function() { - ok(_.clone(object) === object); - ok(_.clone(object, true) === object); + strictEqual(_.clone(object), object); + strictEqual(_.cloneDeep(object), object); }); }); @@ -304,7 +304,7 @@ test('should deep clone `index` and `input` array properties', function() { var array = /x/.exec('x'), - actual = _.clone(array, true); + actual = _.cloneDeep(array); equal(actual.index, 0); equal(actual.input, 'x'); @@ -319,15 +319,15 @@ object.foo.b.foo.c = object; object.bar.b = object.foo.b; - var clone = _.clone(object, true); + var clone = _.cloneDeep(object); ok(clone.bar.b === clone.foo.b && clone === clone.foo.b.foo.c && clone !== object); }); test('should clone problem JScript properties (test in IE < 9)', function() { deepEqual(_.clone(shadowed), shadowed); - ok(_.clone(shadowed) != shadowed); - deepEqual(_.clone(shadowed, true), shadowed); - ok(_.clone(shadowed, true) != shadowed); + notEqual(_.clone(shadowed), shadowed); + deepEqual(_.cloneDeep(shadowed), shadowed); + notEqual(_.cloneDeep(shadowed), shadowed); }); }(1, 2, 3)); @@ -343,7 +343,7 @@ }, function(collection, key) { test('should work with ' + key + ' and a positive `fromIndex`', function() { - equal(_.contains(collection, 1, 2), true); + ok(_.contains(collection, 1, 2)); }); test('should work with ' + key + ' and a `fromIndex` >= collection\'s length', function() { @@ -354,12 +354,12 @@ }); test('should work with ' + key + ' and a negative `fromIndex`', function() { - equal(_.contains(collection, 2, -3), true); + ok(_.contains(collection, 2, -3)); }); test('should work with ' + key + ' and a negative `fromIndex` <= negative collection\'s length', function() { - equal(_.contains(collection, 1, -6), true); - equal(_.contains(collection, 2, -8), true); + ok(_.contains(collection, 1, -6)); + ok(_.contains(collection, 2, -8)); }); }); @@ -369,8 +369,8 @@ }, function(collection, key) { test('should work with a string ' + key + ' for `collection`', function() { - equal(_.contains(collection, 'bc'), true); - equal(_.contains(collection, 'd'), false); + ok(_.contains(collection, 'bc')); + ok(!_.contains(collection, 'd')); }); }); }()); @@ -453,7 +453,7 @@ (function() { test('should return `false` as soon as the `callback` result is falsey', function() { - equal(_.every([true, null, true], _.identity), false); + ok(!_.every([true, null, true], _.identity)); }); }()); @@ -770,7 +770,7 @@ (function() { test('should use strict equality in its duck type check', function() { var element = window.document ? document.body : { 'nodeType': 1 }; - equal(_.isElement(element), true); + ok(_.isElement(element)); equal(_.isElement({ 'nodeType': new Number(1) }), false); equal(_.isElement({ 'nodeType': true }), false); @@ -794,21 +794,21 @@ test('skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', function() { function Foo() {} Foo.prototype.a = 1; - equal(_.isEmpty(Foo), true); + ok(_.isEmpty(Foo)); Foo.prototype = { 'a': 1 }; - equal(_.isEmpty(Foo), true); + ok(_.isEmpty(Foo)); }); test('should work with an object that has a `length` property', function() { - equal(_.isEmpty({ 'length': 0 }), false); + ok(!_.isEmpty({ 'length': 0 })); }); test('should work with jQuery/MooTools DOM query collections', function() { function Foo(elements) { Array.prototype.push.apply(this, elements); } Foo.prototype = { 'length': 0, 'splice': Array.prototype.splice }; - equal(_.isEmpty(new Foo([])), true); + ok(_.isEmpty(new Foo([]))); }); test('should work with `arguments` objects (test in IE < 9)', function() { @@ -826,8 +826,8 @@ args2 = (function() { return arguments; }(1, 2, 3)), args3 = (function() { return arguments; }(1, 2)); - equal(_.isEqual(args1, args2), true); - equal(_.isEqual(args1, args3), false); + ok(_.isEqual(args1, args2)); + ok(!_.isEqual(args1, args3)); }); test('fixes the JScript [[DontEnum]] bug (test in IE < 9)', function() { @@ -838,7 +838,7 @@ // ensure `_._object` is assigned (unassigned in Opera 10.00) if (_._object) { var object = { 'a': 1, 'b': 2, 'c': 3 }; - equal(_.isEqual(object, _._object), true); + ok(_.isEqual(object, _._object)); } else { skipTest(); @@ -865,18 +865,18 @@ (function() { test('should return `false` for non-numeric values', function() { - equal(_.isFinite(null), false); - equal(_.isFinite([]), false); - equal(_.isFinite(true), false); - equal(_.isFinite(''), false); - equal(_.isFinite(' '), false); - equal(_.isFinite('2px'), false); + ok(!_.isFinite(null)); + ok(!_.isFinite([])); + ok(!_.isFinite(true)); + ok(!_.isFinite('')); + ok(!_.isFinite(' ')); + ok(!_.isFinite('2px')); }); test('should return `true` for numeric string values', function() { - equal(_.isFinite('2'), true); - equal(_.isFinite('0'), true); - equal(_.isFinite('08'), true); + ok(_.isFinite('2')); + ok(_.isFinite('0')); + ok(_.isFinite('08')); }); }()); @@ -906,7 +906,7 @@ (function() { test('returns `true` for `new Number(NaN)`', function() { - equal(_.isNaN(new Number(NaN)), true) + ok(_.isNaN(new Number(NaN))); }); }()); @@ -920,9 +920,9 @@ this.a = 1; } - equal(_.isPlainObject(new Foo(1)), false); - equal(_.isPlainObject([1, 2, 3]), false); - equal(_.isPlainObject({ 'a': 1 }), true); + ok(!_.isPlainObject(new Foo(1))); + ok(!_.isPlainObject([1, 2, 3])); + ok(_.isPlainObject({ 'a': 1 })); }); }()); @@ -947,7 +947,8 @@ 'isRegExp', 'isString', 'isUndefined' - ], function(methodName) { + ], + function(methodName) { var func = _[methodName]; test('lodash.' + methodName + ' should return a boolean', function() { @@ -1357,7 +1358,7 @@ while ((new Date - start) < 50 && actual == 5) { actual = _.random(5); } - ok(actual != 5); + notEqual(actual, 5); }); }()); @@ -1540,7 +1541,7 @@ (function() { test('should return `true` as soon as the `callback` result is truthy', function() { - equal(_.some([null, true, null], _.identity), true); + ok(_.some([null, true, null], _.identity)); }); }()); @@ -1667,7 +1668,8 @@ '<%= new Boolean %>': 'false', '<%= typeof a %>': 'number', '<%= void a %>': '' - }, function(value, key) { + }, + function(value, key) { var compiled = _.template(key), data = { 'a': 1, 'b': 2 }; @@ -1816,12 +1818,12 @@ actual = counter + 2; throttled(); throttled(); - }, 64); + }, 128); setTimeout(function() { equal(counter, actual); QUnit.start(); - }, 128); + }, 256); ok(counter > 1); }); @@ -1938,7 +1940,7 @@ test('should coerce the prefix argument to a string', function() { var actual = [_.uniqueId(3), _.uniqueId(2), _.uniqueId(1)]; - equal(/3\d+,2\d+,1\d+/.test(actual), true); + ok(/3\d+,2\d+,1\d+/.test(actual)); }); }()); @@ -2045,24 +2047,20 @@ /*--------------------------------------------------------------------------*/ - QUnit.module('lodash(...) methods capable of returning wrapped and unwrapped values'); + QUnit.module('lodash(...) methods that return wrapped values'); (function() { var array = [1, 2, 3], wrapped = _(array); var funcs = [ - 'first', - 'last' + 'concat', + 'splice' ]; _.each(funcs, function(methodName) { - test('_.' + methodName + ' should return an unwrapped value', function() { - equal(typeof wrapped[methodName](), 'number'); - }); - test('_.' + methodName + ' should return a wrapped value', function() { - ok(wrapped[methodName](1) instanceof _); + ok(wrapped[methodName]() instanceof _); }); }); }()); @@ -2076,6 +2074,7 @@ wrapped = _(array); var funcs = [ + 'clone', 'contains', 'every', 'find', @@ -2098,19 +2097,46 @@ 'isRegExp', 'isString', 'isUndefined', + 'join', 'last', + 'pop', + 'shift', 'reduce', 'reduceRight', 'some' ]; _.each(funcs, function(methodName) { - test('_.' + methodName + ' should return unwrapped values', function() { + test('_.' + methodName + ' should return an unwrapped value', function() { var result = methodName == 'reduceRight' ? wrapped[methodName](_.identity) - : wrapped[methodName]; + : wrapped[methodName](); + + equal(result instanceof _, false); + }); + }); + }()); + + /*--------------------------------------------------------------------------*/ + + QUnit.module('lodash(...) methods capable of returning wrapped and unwrapped values'); - notEqual(typeof result, 'object', '_.' + methodName + ' returns unwrapped values'); + (function() { + var array = [1, 2, 3], + wrapped = _(array); + + var funcs = [ + 'first', + 'last' + ]; + + _.each(funcs, function(methodName) { + test('_.' + methodName + ' should return an unwrapped value', function() { + equal(typeof wrapped[methodName](), 'number'); + }); + + test('_.' + methodName + ' should return a wrapped value', function() { + ok(wrapped[methodName](1) instanceof _); }); }); }()); @@ -2122,19 +2148,20 @@ (function() { test('should allow falsey arguments', function() { var returnArrays = [ - 'filter', - 'invoke', - 'map', - 'pluck', - 'reject', - 'shuffle', - 'sortBy', - 'toArray', - 'where' + 'filter', + 'invoke', + 'map', + 'pluck', + 'reject', + 'shuffle', + 'sortBy', + 'toArray', + 'where' ]; var funcs = _.without.apply(_, [_.functions(_)].concat([ '_', + '_each', '_iteratorTemplate', 'after', 'bind', @@ -2220,7 +2247,7 @@ } if (expected === null) { - deepEqual(thisArg, null, message); + strictEqual(thisArg, null, message); } else { equal(thisArg, expected, message); } diff --git a/test/underscore.html b/test/underscore.html index 9f2d1974a9..564c77a1fb 100644 --- a/test/underscore.html +++ b/test/underscore.html @@ -5,7 +5,7 @@ Underscore Test Suite @@ -32,6 +32,62 @@ + diff --git a/vendor/backbone/backbone.js b/vendor/backbone/backbone.js index 105f2fc76b..38cc991290 100644 --- a/vendor/backbone/backbone.js +++ b/vendor/backbone/backbone.js @@ -1,4 +1,4 @@ -// Backbone.js 0.9.2 +// Backbone.js 0.9.9 // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. // Backbone may be freely distributed under the MIT license. @@ -19,10 +19,10 @@ var previousBackbone = root.Backbone; // Create a local reference to array methods. - var ArrayProto = Array.prototype; - var push = ArrayProto.push; - var slice = ArrayProto.slice; - var splice = ArrayProto.splice; + var array = []; + var push = array.push; + var slice = array.slice; + var splice = array.splice; // The top-level namespace. All public Backbone classes and modules will // be attached to this. Exported for both CommonJS and the browser. @@ -34,7 +34,7 @@ } // Current version of the library. Keep in sync with `package.json`. - Backbone.VERSION = '0.9.2'; + Backbone.VERSION = '0.9.9'; // Require Underscore, if we're on the server, and it's not already present. var _ = root._; @@ -64,12 +64,49 @@ // Backbone.Events // --------------- - // Regular expression used to split event strings + // Regular expression used to split event strings. var eventSplitter = /\s+/; + // Implement fancy features of the Events API such as multiple event + // names `"change blur"` and jQuery-style event maps `{change: action}` + // in terms of the existing API. + var eventsApi = function(obj, action, name, rest) { + if (!name) return true; + if (typeof name === 'object') { + for (var key in name) { + obj[action].apply(obj, [key, name[key]].concat(rest)); + } + } else if (eventSplitter.test(name)) { + var names = name.split(eventSplitter); + for (var i = 0, l = names.length; i < l; i++) { + obj[action].apply(obj, [names[i]].concat(rest)); + } + } else { + return true; + } + }; + + // Optimized internal dispatch function for triggering events. Tries to + // keep the usual cases speedy (most Backbone events have 3 arguments). + var triggerEvents = function(obj, events, args) { + var ev, i = -1, l = events.length; + switch (args.length) { + case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); + return; + case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0]); + return; + case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0], args[1]); + return; + case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0], args[1], args[2]); + return; + default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); + } + }; + // A module that can be mixed in to *any object* in order to provide it with - // custom events. You may bind with `on` or remove with `off` callback functions - // to an event; `trigger`-ing an event fires all callbacks in succession. + // custom events. You may bind with `on` or remove with `off` callback + // functions to an event; `trigger`-ing an event fires all callbacks in + // succession. // // var object = {}; // _.extend(object, Backbone.Events); @@ -78,49 +115,58 @@ // var Events = Backbone.Events = { - // Bind one or more space separated events, `events`, to a `callback` - // function. Passing `"all"` will bind the callback to all events fired. - on: function(events, callback, context) { - var calls, event, list; - if (!callback) return this; - - events = events.split(eventSplitter); - calls = this._callbacks || (this._callbacks = {}); - - while (event = events.shift()) { - list = calls[event] || (calls[event] = []); - list.push(callback, context); - } - + // Bind one or more space separated events, or an events map, + // to a `callback` function. Passing `"all"` will bind the callback to + // all events fired. + on: function(name, callback, context) { + if (!(eventsApi(this, 'on', name, [callback, context]) && callback)) return this; + this._events || (this._events = {}); + var list = this._events[name] || (this._events[name] = []); + list.push({callback: callback, context: context, ctx: context || this}); return this; }, - // Remove one or many callbacks. If `context` is null, removes all callbacks - // with that function. If `callback` is null, removes all callbacks for the - // event. If `events` is null, removes all bound callbacks for all events. - off: function(events, callback, context) { - var event, calls, list, i; + // Bind events to only be triggered a single time. After the first time + // the callback is invoked, it will be removed. + once: function(name, callback, context) { + if (!(eventsApi(this, 'once', name, [callback, context]) && callback)) return this; + var self = this; + var once = _.once(function() { + self.off(name, once); + callback.apply(this, arguments); + }); + once._callback = callback; + this.on(name, once, context); + return this; + }, - // No events, or removing *all* events. - if (!(calls = this._callbacks)) return this; - if (!(events || callback || context)) { - delete this._callbacks; + // Remove one or many callbacks. If `context` is null, removes all + // callbacks with that function. If `callback` is null, removes all + // callbacks for the event. If `events` is null, removes all bound + // callbacks for all events. + off: function(name, callback, context) { + var list, ev, events, names, i, l, j, k; + if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this; + if (!name && !callback && !context) { + this._events = {}; return this; } - events = events ? events.split(eventSplitter) : _.keys(calls); - - // Loop through the callback list, splicing where appropriate. - while (event = events.shift()) { - if (!(list = calls[event]) || !(callback || context)) { - delete calls[event]; - continue; - } - - for (i = list.length - 2; i >= 0; i -= 2) { - if (!(callback && list[i] !== callback || context && list[i + 1] !== context)) { - list.splice(i, 2); + names = name ? [name] : _.keys(this._events); + for (i = 0, l = names.length; i < l; i++) { + name = names[i]; + if (list = this._events[name]) { + events = []; + if (callback || context) { + for (j = 0, k = list.length; j < k; j++) { + ev = list[j]; + if ((callback && callback !== (ev.callback._callback || ev.callback)) || + (context && context !== ev.context)) { + events.push(ev); + } + } } + this._events[name] = events; } } @@ -131,51 +177,53 @@ // passed the same arguments as `trigger` is, apart from the event name // (unless you're listening on `"all"`, which will cause your callback to // receive the true name of the event as the first argument). - trigger: function(events) { - var event, calls, list, i, length, args, all, rest; - if (!(calls = this._callbacks)) return this; - - rest = []; - events = events.split(eventSplitter); - - // Fill up `rest` with the callback arguments. Since we're only copying - // the tail of `arguments`, a loop is much faster than Array#slice. - for (i = 1, length = arguments.length; i < length; i++) { - rest[i - 1] = arguments[i]; - } + trigger: function(name) { + if (!this._events) return this; + var args = slice.call(arguments, 1); + if (!eventsApi(this, 'trigger', name, args)) return this; + var events = this._events[name]; + var allEvents = this._events.all; + if (events) triggerEvents(this, events, args); + if (allEvents) triggerEvents(this, allEvents, arguments); + return this; + }, - // For each event, walk through the list of callbacks twice, first to - // trigger the event, then to trigger any `"all"` callbacks. - while (event = events.shift()) { - // Copy callback lists to prevent modification. - if (all = calls.all) all = all.slice(); - if (list = calls[event]) list = list.slice(); - - // Execute event callbacks. - if (list) { - for (i = 0, length = list.length; i < length; i += 2) { - list[i].apply(list[i + 1] || this, rest); - } - } + // An inversion-of-control version of `on`. Tell *this* object to listen to + // an event in another object ... keeping track of what it's listening to. + listenTo: function(object, events, callback) { + var listeners = this._listeners || (this._listeners = {}); + var id = object._listenerId || (object._listenerId = _.uniqueId('l')); + listeners[id] = object; + object.on(events, callback || this, this); + return this; + }, - // Execute "all" callbacks. - if (all) { - args = [event].concat(rest); - for (i = 0, length = all.length; i < length; i += 2) { - all[i].apply(all[i + 1] || this, args); - } + // Tell this object to stop listening to either specific events ... or + // to every object it's currently listening to. + stopListening: function(object, events, callback) { + var listeners = this._listeners; + if (!listeners) return; + if (object) { + object.off(events, callback, this); + if (!events && !callback) delete listeners[object._listenerId]; + } else { + for (var id in listeners) { + listeners[id].off(null, null, this); } + this._listeners = {}; } - return this; } - }; // Aliases for backwards compatibility. Events.bind = Events.on; Events.unbind = Events.off; + // Allow the `Backbone` object to serve as a global event bus, for folks who + // want global "pubsub" in a convenient place. + _.extend(Backbone, Events); + // Backbone.Model // -------------- @@ -187,17 +235,12 @@ this.cid = _.uniqueId('c'); this.changed = {}; this.attributes = {}; - this._escapedAttributes = {}; - this._modelState = []; + this._changes = []; if (options && options.collection) this.collection = options.collection; if (options && options.parse) attrs = this.parse(attrs); - if (defaults = _.result(this, 'defaults')) { - attrs = _.extend({}, defaults, attrs); - } + if (defaults = _.result(this, 'defaults')) _.defaults(attrs, defaults); this.set(attrs, {silent: true}); - this._cleanChange = true; - this._modelState = []; - this._currentState = _.clone(this.attributes); + this._currentAttributes = _.clone(this.attributes); this._previousAttributes = _.clone(this.attributes); this.initialize.apply(this, arguments); }; @@ -208,27 +251,6 @@ // A hash of attributes whose current and previous value differ. changed: null, - // Whether there is a pending request to fire in the final `change` loop. - _pending: false, - - // Whether the model is in the midst of a change cycle. - _changing: false, - - // Whether there has been a `set` call since the last - // calculation of the changed hash, for efficiency. - _cleanChange: true, - - // The model state used for comparison in determining if a - // change should be fired. - _currentState: null, - - // An array queue of all changes attributed to a model - // on the next non-silent change event. - _modelState: null, - - // A hash of the model's attributes when the last `change` occured. - _previousAttributes: null, - // The default name for the JSON `id` attribute is `"id"`. MongoDB and // CouchDB users may want to set this to `"_id"`. idAttribute: 'id', @@ -254,10 +276,7 @@ // Get the HTML-escaped value of an attribute. escape: function(attr) { - var html; - if (html = this._escapedAttributes[attr]) return html; - var val = this.get(attr); - return this._escapedAttributes[attr] = _.escape(val == null ? '' : '' + val); + return _.escape(this.get(attr)); }, // Returns `true` if the attribute contains a value that is not null @@ -284,9 +303,6 @@ var silent = options && options.silent; var unset = options && options.unset; - if (attrs instanceof Model) attrs = attrs.attributes; - if (unset) for (attr in attrs) attrs[attr] = void 0; - // Run validation. if (!this._validate(attrs, options)) return false; @@ -294,24 +310,19 @@ if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; var now = this.attributes; - var esc = this._escapedAttributes; // For each `set` attribute... for (attr in attrs) { val = attrs[attr]; - // If an escaped attr exists, and the new and current value differ, remove the escaped attr. - if (esc[attr] && !_.isEqual(now[attr], val) || (unset && _.has(now, attr))) delete esc[attr]; - - // Update or delete the current value. + // Update or delete the current value, and track the change. unset ? delete now[attr] : now[attr] = val; - - // Track any action on the attribute. - this._modelState.push(attr, val, unset); + this._changes.push(attr, val); } - // Signal that the model's state has potentially changed. - this._cleanChange = false; + // Signal that the model's state has potentially changed, and we need + // to recompute the actual changes. + this._hasComputed = false; // Fire the `"change"` events. if (!silent) this.change(options); @@ -321,15 +332,15 @@ // Remove an attribute from the model, firing `"change"` unless you choose // to silence it. `unset` is a noop if the attribute doesn't exist. unset: function(attr, options) { - options = _.extend({}, options, {unset: true}); - return this.set(attr, null, options); + return this.set(attr, void 0, _.extend({}, options, {unset: true})); }, // Clear all attributes on the model, firing `"change"` unless you choose // to silence it. clear: function(options) { - options = _.extend({}, options, {unset: true}); - return this.set(_.clone(this.attributes), options); + var attrs = {}; + for (var key in this.attributes) attrs[key] = void 0; + return this.set(attrs, _.extend({}, options, {unset: true})); }, // Fetch the model from the server. If the server's representation of the @@ -341,7 +352,7 @@ var model = this; var success = options.success; options.success = function(resp, status, xhr) { - if (!model.set(model.parse(resp, xhr), options)) return false; + if (!model.set(model.parse(resp), options)) return false; if (success) success(model, resp, options); }; return this.sync('read', this, options); @@ -383,14 +394,16 @@ var success = options.success; options.success = function(resp, status, xhr) { done = true; - var serverAttrs = model.parse(resp, xhr); + var serverAttrs = model.parse(resp); if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs); if (!model.set(serverAttrs, options)) return false; if (success) success(model, resp, options); }; // Finish configuring and sending the Ajax request. - var xhr = this.sync(this.isNew() ? 'create' : 'update', this, options); + var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); + if (method == 'patch') options.attrs = attrs; + var xhr = this.sync(method, this, options); // When using `wait`, reset attributes to original values unless // `success` has been called already. @@ -440,7 +453,7 @@ // **parse** converts a response into the hash of attributes to be `set` on // the model. The default implementation is just to pass the response along. - parse: function(resp, xhr) { + parse: function(resp) { return resp; }, @@ -458,14 +471,15 @@ // a `"change:attribute"` event for each changed attribute. // Calling this will cause all objects observing the model to update. change: function(options) { - var i, changing = this._changing; + var changing = this._changing; this._changing = true; // Generate the changes to be triggered on the model. - var triggers = this._changeCenter(true); - this._pending = triggers.length; + var triggers = this._computeChanges(true); + + this._pending = !!triggers.length; - for (i = triggers.length - 2; i >= 0; i -= 2) { + for (var i = triggers.length - 2; i >= 0; i -= 2) { this.trigger('change:' + triggers[i], this, triggers[i + 1], options); } @@ -478,14 +492,14 @@ this._previousAttributes = _.clone(this.attributes); } - this._changing = null; + this._changing = false; return this; }, // Determine if the model has changed since the last `"change"` event. // If you specify an attribute name, determine if that attribute has changed. hasChanged: function(attr) { - if (!this._cleanChange) this._changeCenter(); + if (!this._hasComputed) this._computeChanges(); if (attr == null) return !_.isEmpty(this.changed); return _.has(this.changed, attr); }, @@ -506,39 +520,36 @@ return changed; }, - // Calculates and handles any changes in `this._modelState`, - // checking against `this._currentState` to determine current changes. - _changeCenter: function (change) { + // Looking at the built up list of `set` attribute changes, compute how + // many of the attributes have actually changed. If `loud`, return a + // boiled-down list of only the real changes. + _computeChanges: function(loud) { this.changed = {}; - var local = {}; + var already = {}; var triggers = []; - var modelState = this._modelState; - var currentState = this._currentState; + var current = this._currentAttributes; + var changes = this._changes; // Loop through the current queue of potential model changes. - for (var i = modelState.length - 3; i >= 0; i -= 3) { - var key = modelState[i], val = modelState[i + 1], unset = modelState[i + 2]; - - // If the item hasn't been set locally this round, proceed. - if (!local[key]) { - local[key] = val; - - // Check if the attribute has been modified since the last change, - // and update `this.changed` accordingly. - if (currentState[key] !== val || (_.has(currentState, key) && unset)) { - this.changed[key] = val; - - // Triggers & modifications are only created inside a `change` call. - if (!change) continue; - triggers.push(key, val); - (!unset) ? currentState[key] = val : delete currentState[key]; - } + for (var i = changes.length - 2; i >= 0; i -= 2) { + var key = changes[i], val = changes[i + 1]; + if (already[key]) continue; + already[key] = true; + + // Check if the attribute has been modified since the last change, + // and update `this.changed` accordingly. If we're inside of a `change` + // call, also add a trigger to the list. + if (!_.isEqual(current[key], val)) { + this.changed[key] = val; + if (!loud) continue; + triggers.push(key, val); + current[key] = val; } - modelState.splice(i,3); } + if (loud) this._changes = []; // Signals `this.changed` is current to prevent duplicate calls from `this.hasChanged`. - this._cleanChange = true; + this._hasComputed = true; return triggers; }, @@ -555,17 +566,11 @@ return _.clone(this._previousAttributes); }, - // Check if the model is currently in a valid state. It's only possible to - // get into an *invalid* state if you're using silent changes. - isValid: function(options) { - return !this.validate || !this.validate(this.attributes, options); - }, - // Run validation against the next complete set of model attributes, // returning `true` if all is well. If a specific `error` callback has // been passed, call that instead of firing the general `"error"` event. _validate: function(attrs, options) { - if (options && options.silent || !this.validate) return true; + if (!this.validate) return true; attrs = _.extend({}, this.attributes, attrs); var error = this.validate(attrs, options); if (!error) return true; @@ -588,10 +593,7 @@ if (options.comparator !== void 0) this.comparator = options.comparator; this._reset(); this.initialize.apply(this, arguments); - if (models) { - if (options.parse) models = this.parse(models); - this.reset(models, {silent: true, parse: options.parse}); - } + if (models) this.reset(models, _.extend({silent: true}, options)); }; // Define the Collection's inheritable methods. @@ -619,8 +621,9 @@ // Add a model, or list of models to the set. Pass **silent** to avoid // firing the `add` event for every new model. add: function(models, options) { - var i, args, length, model, existing, sort; + var i, args, length, model, existing, needsSort; var at = options && options.at; + var sort = ((options && options.sort) == null ? true : options.sort); models = _.isArray(models) ? models.slice() : [models]; // Turn bare objects into model references, and prevent invalid models @@ -638,8 +641,8 @@ // optionally merge it into the existing model. if (existing || this._byCid[model.cid]) { if (options && options.merge && existing) { - existing.set(model, options); - sort = true; + existing.set(model.attributes, options); + needsSort = sort; } models.splice(i, 1); continue; @@ -653,14 +656,14 @@ } // See if sorting is needed, update `length` and splice in new models. - if (models.length) sort = true; + if (models.length) needsSort = sort; this.length += models.length; args = [at != null ? at : this.models.length, 0]; push.apply(args, models); splice.apply(this.models, args); // Sort the collection if appropriate. - if (sort && this.comparator && at == null) this.sort({silent: true}); + if (needsSort && this.comparator && at == null) this.sort({silent: true}); if (options && options.silent) return this; @@ -679,7 +682,7 @@ options || (options = {}); models = _.isArray(models) ? models.slice() : [models]; for (i = 0, l = models.length; i < l; i++) { - model = this.getByCid(models[i]) || this.get(models[i]); + model = this.get(models[i]); if (!model) continue; delete this._byId[model.id]; delete this._byCid[model.cid]; @@ -698,7 +701,7 @@ // Add a model to the end of the collection. push: function(model, options) { model = this._prepareModel(model, options); - this.add(model, options); + this.add(model, _.extend({at: this.length}, options)); return model; }, @@ -729,14 +732,9 @@ }, // Get a model from the set by id. - get: function(id) { - if (id == null) return void 0; - return this._byId[id.id != null ? id.id : id]; - }, - - // Get a model from the set by client id. - getByCid: function(cid) { - return cid && this._byCid[cid.cid || cid]; + get: function(obj) { + if (obj == null) return void 0; + return this._byId[obj.id != null ? obj.id : obj] || this._byCid[obj.cid || obj]; }, // Get the model at the given index. @@ -769,7 +767,7 @@ this.models.sort(_.bind(this.comparator, this)); } - if (!options || !options.silent) this.trigger('reset', this, options); + if (!options || !options.silent) this.trigger('sort', this, options); return this; }, @@ -778,16 +776,56 @@ return _.invoke(this.models, 'get', attr); }, + // Smartly update a collection with a change set of models, adding, + // removing, and merging as necessary. + update: function(models, options) { + var model, i, l, existing; + var add = [], remove = [], modelMap = {}; + var idAttr = this.model.prototype.idAttribute; + options = _.extend({add: true, merge: true, remove: true}, options); + if (options.parse) models = this.parse(models); + + // Allow a single model (or no argument) to be passed. + if (!_.isArray(models)) models = models ? [models] : []; + + // Proxy to `add` for this case, no need to iterate... + if (options.add && !options.remove) return this.add(models, options); + + // Determine which models to add and merge, and which to remove. + for (i = 0, l = models.length; i < l; i++) { + model = models[i]; + existing = this.get(model.id || model.cid || model[idAttr]); + if (options.remove && existing) modelMap[existing.cid] = true; + if ((options.add && !existing) || (options.merge && existing)) { + add.push(model); + } + } + if (options.remove) { + for (i = 0, l = this.models.length; i < l; i++) { + model = this.models[i]; + if (!modelMap[model.cid]) remove.push(model); + } + } + + // Remove models (if applicable) before we add and merge the rest. + if (remove.length) this.remove(remove, options); + if (add.length) this.add(add, options); + return this; + }, + // When you have more items than you want to add or remove individually, // you can reset the entire set with a new list of models, without firing // any `add` or `remove` events. Fires `reset` when finished. reset: function(models, options) { + options || (options = {}); + if (options.parse) models = this.parse(models); for (var i = 0, l = this.models.length; i < l; i++) { this._removeReference(this.models[i]); } + options.previousModels = this.models; this._reset(); if (models) this.add(models, _.extend({silent: true}, options)); - if (!options || !options.silent) this.trigger('reset', this, options); + if (!options.silent) this.trigger('reset', this, options); return this; }, @@ -800,7 +838,8 @@ var collection = this; var success = options.success; options.success = function(resp, status, xhr) { - collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options); + var method = options.update ? 'update' : 'reset'; + collection[method](resp, options); if (success) success(collection, resp, options); }; return this.sync('read', this, options); @@ -826,7 +865,7 @@ // **parse** converts a response into a list of models to be added to the // collection. The default implementation is just to pass it through. - parse: function(resp, xhr) { + parse: function(resp) { return resp; }, @@ -859,7 +898,7 @@ options || (options = {}); options.collection = this; var model = new this.model(attrs, options); - if (!model._validate(model.attributes, options)) return false; + if (!model._validate(attrs, options)) return false; return model; }, @@ -932,7 +971,7 @@ var optionalParam = /\((.*?)\)/g; var namedParam = /:\w+/g; var splatParam = /\*\w+/g; - var escapeRegExp = /[-{}[\]+?.,\\^$|#\s]/g; + var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; // Set up all inheritable **Backbone.Router** properties and methods. _.extend(Router.prototype, Events, { @@ -1003,7 +1042,7 @@ this.handlers = []; _.bindAll(this, 'checkUrl'); - // #1653 - Ensure that `History` can be used outside of the browser. + // Ensure that `History` can be used outside of the browser. if (typeof window !== 'undefined') { this.location = window.location; this.history = window.history; @@ -1051,7 +1090,7 @@ fragment = this.getHash(); } } - return decodeURIComponent(fragment.replace(routeStripper, '')); + return fragment.replace(routeStripper, ''); }, // Start the hash change handling, returning `true` if the current URL matches @@ -1199,7 +1238,7 @@ var href = location.href.replace(/(javascript:|#).*$/, ''); location.replace(href + '#' + fragment); } else { - // #1649 - Some browsers require that `hash` contains a leading #. + // Some browsers require that `hash` contains a leading #. location.hash = '#' + fragment; } } @@ -1251,20 +1290,11 @@ return this; }, - // Clean up references to this view in order to prevent latent effects and - // memory leaks. - dispose: function() { - this.undelegateEvents(); - if (this.model && this.model.off) this.model.off(null, null, this); - if (this.collection && this.collection.off) this.collection.off(null, null, this); - return this; - }, - - // Remove this view from the DOM. Note that the view isn't present in the - // DOM by default, so calling this method may be a no-op. + // Remove this view by taking the element out of the DOM, and removing any + // applicable Backbone.Events listeners. remove: function() { - this.dispose(); this.$el.remove(); + this.stopListening(); return this; }, @@ -1364,6 +1394,7 @@ var methodMap = { 'create': 'POST', 'update': 'PUT', + 'patch': 'PATCH', 'delete': 'DELETE', 'read': 'GET' }; @@ -1401,9 +1432,9 @@ } // Ensure that we have the appropriate request data. - if (!options.data && model && (method === 'create' || method === 'update')) { + if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) { params.contentType = 'application/json'; - params.data = JSON.stringify(model.toJSON(options)); + params.data = JSON.stringify(options.attrs || model.toJSON(options)); } // For older servers, emulate JSON by encoding the request into an HTML-form. @@ -1414,7 +1445,7 @@ // For older servers, emulate HTTP by mimicking the HTTP method with `_method` // And an `X-HTTP-Method-Override` header. - if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE')) { + if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) { params.type = 'POST'; if (options.emulateJSON) params.data._method = type; var beforeSend = options.beforeSend; @@ -1442,7 +1473,9 @@ }; // Make the request, allowing the user to override any Ajax options. - return Backbone.ajax(_.extend(params, options)); + var xhr = Backbone.ajax(_.extend(params, options)); + model.trigger('request', model, xhr, options); + return xhr; }; // Set the default implementation of `Backbone.ajax` to proxy through to `$`. diff --git a/vendor/backbone/test/collection.js b/vendor/backbone/test/collection.js index cabb65ca4c..339a50e8b2 100644 --- a/vendor/backbone/test/collection.js +++ b/vendor/backbone/test/collection.js @@ -18,17 +18,21 @@ $(document).ready(function() { })); - test("new and sort", 7, function() { + test("new and sort", 9, function() { + var counter = 0; + col.on('sort', function(){ counter++; }); equal(col.first(), a, "a should be first"); equal(col.last(), d, "d should be last"); col.comparator = function(a, b) { return a.id > b.id ? -1 : 1; }; col.sort(); + equal(counter, 1); equal(col.first(), a, "a should be first"); equal(col.last(), d, "d should be last"); col.comparator = function(model) { return model.id; }; col.sort(); + equal(counter, 2); equal(col.first(), d, "d should be first"); equal(col.last(), a, "a should be last"); equal(col.length, 4); @@ -58,10 +62,10 @@ $(document).ready(function() { strictEqual(collection.last().get('a'), 4); }); - test("get, getByCid", 3, function() { + test("get", 3, function() { equal(col.get(0), d); equal(col.get(2), b); - equal(col.getByCid(col.first().cid), col.first()); + equal(col.get(col.first().cid), col.first()); }); test("get with non-default ids", 2, function() { @@ -304,13 +308,13 @@ $(document).ready(function() { var colE = new Backbone.Collection([e]); var colF = new Backbone.Collection([f]); ok(e != f); - ok(colE.length == 1); - ok(colF.length == 1); + ok(colE.length === 1); + ok(colF.length === 1); colE.remove(e); equal(passed, false); - ok(colE.length == 0); + ok(colE.length === 0); colF.remove(e); - ok(colF.length == 0); + ok(colF.length === 0); equal(passed, true); }); @@ -338,13 +342,13 @@ $(document).ready(function() { }); equal(colE, e.collection); colF.remove(e); - ok(colF.length == 0); - ok(colE.length == 1); + ok(colF.length === 0); + ok(colE.length === 1); equal(counter, 1); equal(colE, e.collection); colE.remove(e); equal(null, e.collection); - ok(colE.length == 0); + ok(colE.length === 0); equal(counter, 2); }); @@ -354,8 +358,8 @@ $(document).ready(function() { var colE = new Backbone.Collection([e]); var colF = new Backbone.Collection([e]); e.destroy(); - ok(colE.length == 0); - ok(colF.length == 0); + ok(colE.length === 0); + ok(colF.length === 0); equal(undefined, e.collection); }); @@ -365,8 +369,8 @@ $(document).ready(function() { var colE = new Backbone.Collection([e]); var colF = new Backbone.Collection([e]); e.destroy(); - ok(colE.length == 0); - ok(colF.length == 0); + ok(colE.length === 0); + ok(colF.length === 0); equal(undefined, e.collection); }); @@ -382,6 +386,19 @@ $(document).ready(function() { equal(this.syncArgs.options.parse, false); }); + test("ensure fetch only parses once", 1, function() { + var collection = new Backbone.Collection; + var counter = 0; + collection.parse = function(models) { + counter++; + return models; + }; + collection.url = '/test'; + collection.fetch(); + this.syncArgs.options.success([]); + equal(counter, 1); + }); + test("create", 4, function() { var collection = new Backbone.Collection; collection.url = '/test'; @@ -542,7 +559,7 @@ $(document).ready(function() { equal(col.length, 0); }); - test("#861, adding models to a collection which do not pass validation", 2, function() { + test("#861, adding models to a collection which do not pass validation", function() { var Model = Backbone.Model.extend({ validate: function(attrs) { if (attrs.id == 3) return "id can't be 3"; @@ -567,9 +584,9 @@ $(document).ready(function() { validate: function(attrs){ if (!attrs.valid) return 'invalid'; } }); var model = new collection.model({id: 1, valid: true}); - collection.add([model, {id: 2}]);; + collection.add([model, {id: 2}]); model.trigger('test'); - ok(collection.getByCid(model.cid)); + ok(collection.get(model.cid)); ok(collection.get(1)); ok(!collection.get(2)); equal(collection.length, 1); @@ -726,4 +743,171 @@ $(document).ready(function() { c.add({id: 4}); }); + test("#1407 parse option on constructor parses collection and models", 2, function() { + var model = { + namespace : [{id: 1}, {id:2}] + }; + var Collection = Backbone.Collection.extend({ + model: Backbone.Model.extend({ + parse: function(model) { + model.name = 'test'; + return model; + } + }), + parse: function(model) { + return model.namespace; + } + }); + var c = new Collection(model, {parse:true}); + + equal(c.length, 2); + equal(c.at(0).get('name'), 'test'); + }); + + test("#1407 parse option on reset parses collection and models", 2, function() { + var model = { + namespace : [{id: 1}, {id:2}] + }; + var Collection = Backbone.Collection.extend({ + model: Backbone.Model.extend({ + parse: function(model) { + model.name = 'test'; + return model; + } + }), + parse: function(model) { + return model.namespace; + } + }); + var c = new Collection(); + c.reset(model, {parse:true}); + + equal(c.length, 2); + equal(c.at(0).get('name'), 'test'); + }); + + + test("Reset includes previous models in triggered event.", 1, function() { + var model = new Backbone.Model(); + var collection = new Backbone.Collection([model]) + .on('reset', function(collection, options) { + deepEqual(options.previousModels, [model]); + }); + collection.reset([]); + }); + + test("update", function() { + var m1 = new Backbone.Model(); + var m2 = new Backbone.Model({id: 2}); + var m3 = new Backbone.Model(); + var c = new Backbone.Collection([m1, m2]); + + // Test add/change/remove events + c.on('add', function(model) { + strictEqual(model, m3); + }); + c.on('change', function(model) { + strictEqual(model, m2); + }); + c.on('remove', function(model) { + strictEqual(model, m1); + }); + + // remove: false doesn't remove any models + c.update([], {remove: false}); + strictEqual(c.length, 2); + + // add: false doesn't add any models + c.update([m1, m2, m3], {add: false}); + strictEqual(c.length, 2); + + // merge: false doesn't change any models + c.update([m1, {id: 2, a: 1}], {merge: false}); + strictEqual(m2.get('a'), void 0); + + // add: false, remove: false only merges existing models + c.update([m1, {id: 2, a: 0}, m3, {id: 4}], {add: false, remove: false}); + strictEqual(c.length, 2); + strictEqual(m2.get('a'), 0); + + // default options add/remove/merge as appropriate + c.update([{id: 2, a: 1}, m3]); + strictEqual(c.length, 2); + strictEqual(m2.get('a'), 1); + + // Test removing models not passing an argument + c.off('remove').on('remove', function(model) { + ok(model === m2 || model === m3); + }); + c.update([]); + strictEqual(c.length, 0); + }); + + test("update with only cids", 3, function() { + var m1 = new Backbone.Model; + var m2 = new Backbone.Model; + var c = new Backbone.Collection; + c.update([m1, m2]); + equal(c.length, 2); + c.update([m1]); + equal(c.length, 1); + c.update([m1, m1, m1, m2, m2], {remove: false}); + equal(c.length, 2); + }); + + test("update with only idAttribute", 3, function() { + var m1 = { _id: 1 }; + var m2 = { _id: 2 }; + var col = Backbone.Collection.extend({ + model: Backbone.Model.extend({ + idAttribute: '_id' + }) + }); + var c = new col; + c.update([m1, m2]); + equal(c.length, 2); + c.update([m1]); + equal(c.length, 1); + c.update([m1, m1, m1, m2, m2], {remove: false}); + equal(c.length, 2); + }); + + test("#1894 - Push should not trigger a sort", 0, function() { + var Collection = Backbone.Collection.extend({ + comparator: 'id', + sort: function() { + ok(false); + } + }); + new Collection().push({id: 1}); + }); + + // test("`update` with non-normal id", function() { + // var Collection = Backbone.Collection.extend({ + // model: Backbone.Model.extend({idAttribute: '_id'}) + // }); + // var collection = new Collection({_id: 1}); + // collection.update([{_id: 1, a: 1}], {add: false}); + // equal(collection.first().get('a'), 1); + // }); + + test("#1894 - `sort` can optionally be turned off", 0, function() { + var Collection = Backbone.Collection.extend({ + comparator: 'id', + sort: function() { ok(true); } + }); + new Collection().add({id: 1}, {sort: false}); + }); + + test("#1915 - `parse` data in the right order in `update`", function() { + var collection = new (Backbone.Collection.extend({ + parse: function (data) { + strictEqual(data.status, 'ok'); + return data.data; + } + })); + var res = {status: 'ok', data:[{id: 1}]} + collection.update(res, {parse: true}); + }); + }); diff --git a/vendor/backbone/test/events.js b/vendor/backbone/test/events.js index c10a728364..c3f1d61dd0 100644 --- a/vendor/backbone/test/events.js +++ b/vendor/backbone/test/events.js @@ -17,7 +17,7 @@ $(document).ready(function() { test("binding and triggering multiple events", 4, function() { var obj = { counter: 0 }; - _.extend(obj,Backbone.Events); + _.extend(obj, Backbone.Events); obj.on('a b c', function() { obj.counter += 1; }); @@ -35,6 +35,57 @@ $(document).ready(function() { equal(obj.counter, 5); }); + test("binding and triggering with event maps", function() { + var obj = { counter: 0 }; + _.extend(obj, Backbone.Events); + + var increment = function() { + this.counter += 1; + }; + + obj.on({ + a: increment, + b: increment, + c: increment + }, obj); + + obj.trigger('a'); + equal(obj.counter, 1); + + obj.trigger('a b'); + equal(obj.counter, 3); + + obj.trigger('c'); + equal(obj.counter, 4); + + obj.off({ + a: increment, + c: increment + }, obj); + obj.trigger('a b c'); + equal(obj.counter, 5); + }); + + test("listenTo and stopListening", 1, function() { + var a = _.extend({}, Backbone.Events); + var b = _.extend({}, Backbone.Events); + a.listenTo(b, 'all', function(){ ok(true); }); + b.trigger('anything'); + a.listenTo(b, 'all', function(){ ok(false); }); + a.stopListening(); + b.trigger('anything'); + }); + + test("listenTo and stopListening with event maps", 1, function() { + var a = _.extend({}, Backbone.Events); + var b = _.extend({}, Backbone.Events); + a.listenTo(b, {change: function(){ ok(true); }}); + b.trigger('change'); + a.listenTo(b, {change: function(){ ok(false); }}); + a.stopListening(); + b.trigger('change'); + }); + test("trigger all for each event", 3, function() { var a, b, obj = { counter: 0 }; _.extend(obj, Backbone.Events); @@ -192,4 +243,121 @@ $(document).ready(function() { obj.trigger('event'); }); + test("once", 2, function() { + // Same as the previous test, but we use once rather than having to explicitly unbind + var obj = { counterA: 0, counterB: 0 }; + _.extend(obj, Backbone.Events); + var incrA = function(){ obj.counterA += 1; obj.trigger('event'); }; + var incrB = function(){ obj.counterB += 1; }; + obj.once('event', incrA); + obj.once('event', incrB); + obj.trigger('event'); + equal(obj.counterA, 1, 'counterA should have only been incremented once.'); + equal(obj.counterB, 1, 'counterB should have only been incremented once.'); + }); + + test("once variant one", 3, function() { + var f = function(){ ok(true); }; + + var a = _.extend({}, Backbone.Events).once('event', f); + var b = _.extend({}, Backbone.Events).on('event', f); + + a.trigger('event'); + + b.trigger('event'); + b.trigger('event'); + }); + + test("once variant two", 3, function() { + var f = function(){ ok(true); }; + var obj = _.extend({}, Backbone.Events); + + obj + .once('event', f) + .on('event', f) + .trigger('event') + .trigger('event'); + }); + + test("once with off", 0, function() { + var f = function(){ ok(true); }; + var obj = _.extend({}, Backbone.Events); + + obj.once('event', f); + obj.off('event', f); + obj.trigger('event'); + }); + + test("once with event maps", function() { + var obj = { counter: 0 }; + _.extend(obj, Backbone.Events); + + var increment = function() { + this.counter += 1; + }; + + obj.once({ + a: increment, + b: increment, + c: increment + }, obj); + + obj.trigger('a'); + equal(obj.counter, 1); + + obj.trigger('a b'); + equal(obj.counter, 2); + + obj.trigger('c'); + equal(obj.counter, 3); + + obj.trigger('a b c'); + equal(obj.counter, 3); + }); + + test("once with off only by context", 0, function() { + var context = {}; + var obj = _.extend({}, Backbone.Events); + obj.once('event', function(){ ok(false); }, context); + obj.off(null, null, context); + obj.trigger('event'); + }); + + test("Backbone object inherits Events", function() { + ok(Backbone.on === Backbone.Events.on); + }); + + asyncTest("once with asynchronous events", 1, function() { + var func = _.debounce(function() { ok(true); start(); }, 50); + var obj = _.extend({}, Backbone.Events).once('async', func); + + obj.trigger('async'); + obj.trigger('async'); + }); + + test("once with multiple events.", 2, function() { + var obj = _.extend({}, Backbone.Events); + obj.once('x y', function() { ok(true); }); + obj.trigger('x y'); + }); + + test("Off during iteration with once.", 2, function() { + var obj = _.extend({}, Backbone.Events); + var f = function(){ this.off('event', f); }; + obj.on('event', f); + obj.once('event', function(){}); + obj.on('event', function(){ ok(true); }); + + obj.trigger('event'); + obj.trigger('event'); + }); + + test("`once` on `all` should work as expected", 1, function() { + Backbone.once('all', function() { + ok(true); + Backbone.trigger('all'); + }); + Backbone.trigger('all'); + }); + }); diff --git a/vendor/backbone/test/model.js b/vendor/backbone/test/model.js index fd0be2d5b3..c097298016 100644 --- a/vendor/backbone/test/model.js +++ b/vendor/backbone/test/model.js @@ -55,6 +55,18 @@ $(document).ready(function() { equal(model.get('value'), 2); }); + test("initialize with defaults", 2, function() { + var Model = Backbone.Model.extend({ + defaults: { + first_name: 'Unknown', + last_name: 'Unknown' + } + }); + var model = new Model({'first_name': 'John'}); + equal(model.get('first_name'), 'John'); + equal(model.get('last_name'), 'Unknown'); + }); + test("parse can return null", 1, function() { var Model = Backbone.Model.extend({ parse: function(obj) { @@ -114,7 +126,7 @@ $(document).ready(function() { var foo = new Backbone.Model({p: 1}); var bar = new Backbone.Model({p: 2}); - bar.set(foo.clone(), {unset: true}); + bar.set(foo.clone().attributes, {unset: true}); equal(foo.get('p'), 1); equal(bar.get('p'), undefined); }); @@ -201,6 +213,27 @@ $(document).ready(function() { equal(a.id, undefined, "Unsetting the id should remove the id property."); }); + test("set triggers changes in the correct order", function() { + var value = null; + var model = new Backbone.Model; + model.on('last', function(){ value = 'last'; }); + model.on('first', function(){ value = 'first'; }); + model.trigger('first'); + model.trigger('last'); + equal(value, 'last'); + }); + + test("set falsy values in the correct order", 1, function() { + var model = new Backbone.Model({result: 'result'}); + model.on('change', function() { + equal(model.changed.result, false); + }); + model.set({result: void 0}, {silent: true}); + model.set({result: null}, {silent: true}); + model.set({result: false}, {silent: true}); + model.change(); + }); + test("multiple unsets", 1, function() { var i = 0; var counter = function(){ i++; }; @@ -261,7 +294,7 @@ $(document).ready(function() { }); var model = new Defaulted({two: null}); equal(model.get('one'), 1); - equal(model.get('two'), null); + equal(model.get('two'), 2); Defaulted = Backbone.Model.extend({ defaults: function() { return { @@ -270,9 +303,9 @@ $(document).ready(function() { }; } }); - var model = new Defaulted({two: null}); + model = new Defaulted({two: null}); equal(model.get('one'), 3); - equal(model.get('two'), null); + equal(model.get('two'), 4); }); test("change, hasChanged, changedAttributes, previous, previousAttributes", 12, function() { @@ -293,7 +326,6 @@ $(document).ready(function() { equal(model.hasChanged('name'), true); model.change(); equal(model.get('name'), 'Rob'); - }); test("changedAttributes", 3, function() { @@ -351,24 +383,26 @@ $(document).ready(function() { equal(lastError, "Can't change admin status."); }); - test("isValid", 5, function() { - var model = new Backbone.Model({valid: true}); - model.validate = function(attrs) { - if (!attrs.valid) return "invalid"; - }; - equal(model.isValid(), true); - equal(model.set({valid: false}), false); - equal(model.isValid(), true); - ok(model.set('valid', false, {silent: true})); - equal(model.isValid(), false); - }); - test("save", 2, function() { doc.save({title : "Henry V"}); equal(this.syncArgs.method, 'update'); ok(_.isEqual(this.syncArgs.model, doc)); }); + test("save with PATCH", function() { + doc.clear().set({id: 1, a: 1, b: 2, c: 3, d: 4}); + doc.save(); + equal(this.syncArgs.method, 'update'); + equal(this.syncArgs.options.attrs, undefined); + + doc.save({b: 2, d: 4}, {patch: true}); + equal(this.syncArgs.method, 'patch'); + equal(_.size(this.syncArgs.options.attrs), 2); + equal(this.syncArgs.options.attrs.d, 4); + equal(this.syncArgs.options.attrs.a, undefined); + equal(this.ajaxSettings.data, "{\"b\":2,\"d\":4}"); + }); + test("save in positional style", 1, function() { var model = new Backbone.Model(); model.sync = function(method, model, options) { @@ -402,7 +436,7 @@ $(document).ready(function() { ok(true, "non-persisted model should not call sync"); }); - test("validate", 7, function() { + test("validate", function() { var lastError; var model = new Backbone.Model(); model.validate = function(attrs) { @@ -415,8 +449,6 @@ $(document).ready(function() { equal(result, model); equal(model.get('a'), 100); equal(lastError, undefined); - result = model.set({admin: true}, {silent: true}); - equal(model.get('admin'), true); result = model.set({a: 200, admin: false}); equal(lastError, "Can't change admin status."); equal(result, false); @@ -560,9 +592,9 @@ $(document).ready(function() { ok(model.get('x') === a); }); - test("unset fires change for undefined attributes", 1, function() { + test("unset does not fire a change for undefined attributes", 0, function() { var model = new Backbone.Model({x: undefined}); - model.on('change:x', function(){ ok(true); }); + model.on('change:x', function(){ ok(false); }); model.unset('x'); }); @@ -639,7 +671,7 @@ $(document).ready(function() { equal(model.get('x'), 3); }); - test("save with wait validates attributes", 1, function() { + test("save with wait validates attributes", function() { var model = new Backbone.Model(); model.url = '/test'; model.validate = function() { ok(true); }; @@ -774,12 +806,6 @@ $(document).ready(function() { model.set({a: true}); }); - test("#1179 - isValid returns true in the absence of validate.", 1, function() { - var model = new Backbone.Model(); - model.validate = null; - ok(model.isValid()); - }); - test("#1122 - clear does not alter options.", 1, function() { var model = new Backbone.Model(); var options = {}; @@ -917,4 +943,10 @@ $(document).ready(function() { deepEqual(changes, ['a']); }); + test("#1943 change calculations should use _.isEqual", function() { + var model = new Backbone.Model({a: {key: 'value'}}); + model.set('a', {key:'value'}, {silent:true}); + equal(model.changedAttributes(), false); + }); + }); diff --git a/vendor/backbone/test/router.js b/vendor/backbone/test/router.js index f9327fcc35..50be7effb7 100644 --- a/vendor/backbone/test/router.js +++ b/vendor/backbone/test/router.js @@ -107,7 +107,7 @@ $(document).ready(function() { }, optionalItem: function(arg){ - this.arg = arg !== undefined ? arg : null; + this.arg = arg != void 0 ? arg : null; }, splat : function(args) { @@ -139,7 +139,7 @@ $(document).ready(function() { location.replace('http://example.com#search/news'); Backbone.history.checkUrl(); equal(router.query, 'news'); - equal(router.page, undefined); + equal(router.page, void 0); equal(lastRoute, 'search'); equal(lastArgs[0], 'news'); }); @@ -207,7 +207,7 @@ $(document).ready(function() { test("routes (optional)", 2, function() { location.replace('http://example.com#optional'); Backbone.history.checkUrl(); - equal(router.arg, null); + ok(!router.arg); location.replace('http://example.com#optional/thing'); Backbone.history.checkUrl(); equal(router.arg, 'thing'); @@ -265,12 +265,12 @@ $(document).ready(function() { if (!Backbone.history.iframe) ok(true); }); - test("route callback gets passed decoded values", 3, function() { + test("#967 - Route callback gets passed encoded values.", 3, function() { var route = 'has%2Fslash/complex-has%23hash/has%20space'; Backbone.history.navigate(route, {trigger: true}); - equal(router.first, 'has/slash'); - equal(router.part, 'has#hash'); - equal(router.rest, 'has space'); + strictEqual(router.first, 'has%2Fslash'); + strictEqual(router.part, 'has%23hash'); + strictEqual(router.rest, 'has%20space'); }); test("correctly handles URLs with % (#868)", 3, function() { @@ -279,7 +279,7 @@ $(document).ready(function() { location.replace('http://example.com#search/fat'); Backbone.history.checkUrl(); equal(router.query, 'fat'); - equal(router.page, undefined); + equal(router.page, void 0); equal(lastRoute, 'search'); }); diff --git a/vendor/backbone/test/view.js b/vendor/backbone/test/view.js index 400c80eb6d..444d90b2c4 100644 --- a/vendor/backbone/test/view.js +++ b/vendor/backbone/test/view.js @@ -41,7 +41,7 @@ $(document).ready(function() { var div = view.make('div', {id: 'test-div'}, 0); equal($(div).text(), '0'); - var div = view.make('div', {id: 'test-div'}, ''); + div = view.make('div', {id: 'test-div'}, ''); equal($(div).text(), ''); }); @@ -171,7 +171,7 @@ $(document).ready(function() { return { title: 'title1', acceptText: 'confirm' - } + }; } }); @@ -310,14 +310,11 @@ $(document).ready(function() { ok(new View().$el.is('p')); }); - test("dispose", 0, function() { + test("views stopListening", 0, function() { var View = Backbone.View.extend({ - events: { - click: function() { ok(false); } - }, initialize: function() { - this.model.on('all x', function(){ ok(false); }, this); - this.collection.on('all x', function(){ ok(false); }, this); + this.listenTo(this.model, 'all x', function(){ ok(false); }, this); + this.listenTo(this.collection, 'all x', function(){ ok(false); }, this); } }); @@ -326,22 +323,9 @@ $(document).ready(function() { collection: new Backbone.Collection }); - view.dispose(); + view.stopListening(); view.model.trigger('x'); view.collection.trigger('x'); - view.$el.click(); - }); - - test("dispose with non Backbone objects", 0, function() { - var view = new Backbone.View({model: {}, collection: {}}); - view.dispose(); - }); - - test("view#remove calls dispose.", 1, function() { - var view = new Backbone.View(); - - view.dispose = function() { ok(true); }; - view.remove(); }); test("Provide function for el.", 1, function() { @@ -364,16 +348,16 @@ $(document).ready(function() { counter++; } }); - + var view = new View({events:{'click #test':'increment'}}); var view2 = new View({events:function(){ return {'click #test':'increment'}; }}); - + view.$('#test').trigger('click'); view2.$('#test').trigger('click'); equal(counter, 2); - + view.$('#test').trigger('click'); view2.$('#test').trigger('click'); equal(counter, 4); diff --git a/vendor/benchmark.js/benchmark.js b/vendor/benchmark.js/benchmark.js index 5b63f8baa0..fe5ff3db06 100644 --- a/vendor/benchmark.js/benchmark.js +++ b/vendor/benchmark.js/benchmark.js @@ -25,6 +25,9 @@ /** Detect free variable `require` */ var freeRequire = typeof require == 'function' && require; + /** Used to store the `Object` built-in in case it's overwritten later */ + var Object = window.Object; + /** Used to crawl all properties regardless of enumerability */ var getAllKeys = Object.getOwnPropertyNames; diff --git a/vendor/docdown/src/DocDown/Entry.php b/vendor/docdown/src/DocDown/Entry.php index 933bf4005d..e545d33607 100644 --- a/vendor/docdown/src/DocDown/Entry.php +++ b/vendor/docdown/src/DocDown/Entry.php @@ -187,7 +187,10 @@ public function getDesc() { preg_match('#/\*\*(?:\s*\*)?([\s\S]*?)(?=\*\s\@[a-z]|\*/)#', $this->entry, $result); if (count($result)) { $type = $this->getType(); - $result = trim(preg_replace('/(?:^|\n)\s*\* ?/', ' ', $result[1])); + $result = preg_replace('/:\n *\* */', ":
\n", $result[1]); + $result = preg_replace('/(?:^|\n) *\*\n *\* */', "\n\n", $result); + $result = preg_replace('/(?:^|\n) *\* ?/', ' ', $result); + $result = trim($result); $result = ($type == 'Function' ? '' : '(' . str_replace('|', ', ', trim($type, '{}')) . '): ') . $result; } $this->_desc = $result; diff --git a/vendor/firebug-lite/skin/xp/firebug-1.3a2.css b/vendor/firebug-lite/skin/xp/firebug-1.3a2.css index 42f9faf5d5..d929b2bf17 100644 --- a/vendor/firebug-lite/skin/xp/firebug-1.3a2.css +++ b/vendor/firebug-lite/skin/xp/firebug-1.3a2.css @@ -70,7 +70,7 @@ html, body { body { font-family: Lucida Grande, Tahoma, sans-serif; font-size: 11px; - background: #fff; + background: #fff; } .clear { @@ -103,7 +103,7 @@ body { border: 1px solid #ccc; margin: 0 5px 0 0; background: #fff url(search.png) no-repeat 4px 2px; - padding-left: 20px; + padding-left: 20px; font-size: 11px; } @@ -124,7 +124,7 @@ body { height: 14px; background: url(errorIcon.png) no-repeat; color: #f00; - font-weight: bold; + font-weight: bold; } #fbMiniErrors { @@ -139,7 +139,7 @@ body { margin: 3px 4px 0; height: 20px; width: 20px; - float: right; + float: right; background: url(sprite.png) 0 -135px; cursor: pointer; } @@ -192,10 +192,10 @@ body { } /************************************************************************************************ - Sub-Layout + Sub-Layout *************************************************************************************************/ -/* fbToolbar +/* fbToolbar *************************************************************************************************/ #fbToolbarIcon { float: left; @@ -237,7 +237,7 @@ body { position: relative; top: 5px; line-height: 19px; - cursor: default; + cursor: default; } .fbToolbarSeparator{ @@ -262,7 +262,7 @@ body { .fbStatusBar span a:hover { color: blue; - cursor: pointer; + cursor: pointer; } @@ -307,7 +307,7 @@ body { padding-left: 10px; } -/* body +/* body *************************************************************************************************/ .fbPanel { display: none; @@ -340,7 +340,7 @@ body { visibility: hidden !important; } -/* fbBottom +/* fbBottom *************************************************************************************************/ #fbCommand { @@ -391,7 +391,7 @@ div.fbFitHeight { Layout Controls *************************************************************************************************/ -/* fbToolbar buttons +/* fbToolbar buttons *************************************************************************************************/ #fbWindowButtons a { font-size: 1px; @@ -420,7 +420,7 @@ div.fbFitHeight { background: url(sprite.png) -48px -119px; } -/* fbPanelBarBox tabs +/* fbPanelBarBox tabs *************************************************************************************************/ .fbTab { text-decoration: none; @@ -475,7 +475,7 @@ a.fbTab:hover .fbTabR { background: url(sprite.png) -8px -96px !important; } -/* splitters +/* splitters *************************************************************************************************/ #fbHSplitter { position: absolute; @@ -571,7 +571,7 @@ div.objectBox-element { color: #fff !important; } -/* Webkit CSS Hack - bug in "highlight" named color */ +/* Webkit CSS Hack - bug in "highlight" named color */ @media screen and (-webkit-min-device-pixel-ratio:0) { .selectedElement{ background: #316AC5; diff --git a/vendor/firebug-lite/skin/xp/firebug.css b/vendor/firebug-lite/skin/xp/firebug.css index a1465ec5f3..f3bdd8cdee 100644 --- a/vendor/firebug-lite/skin/xp/firebug.css +++ b/vendor/firebug-lite/skin/xp/firebug.css @@ -136,10 +136,10 @@ h1.groupHeader { position: relative; top: -7px; left: -5px; - + outline: none; resize: none; - + /* _border: 1px solid #999 !important; _padding: 1px !important; @@ -311,17 +311,17 @@ h1.groupHeader { outline: none; background-color: transparent } - - .useA11y .a11yCSSView .focusRow:focus .cssSelector, - .useA11y .a11yCSSView .focusRow:focus .cssPropName, + + .useA11y .a11yCSSView .focusRow:focus .cssSelector, + .useA11y .a11yCSSView .focusRow:focus .cssPropName, .useA11y .a11yCSSView .focusRow:focus .cssPropValue, - .useA11y .a11yCSSView .computedStyleRow:focus, + .useA11y .a11yCSSView .computedStyleRow:focus, .useA11y .a11yCSSView .groupHeader:focus { outline: 2px solid #FF9933; outline-offset: -2px; background-color: #FFFFD6; } - + .useA11y .a11yCSSView .groupHeader:focus { outline-offset: -2px; } @@ -731,7 +731,7 @@ h1.groupHeader { /* Time Info tip */ .timeInfoTip { - width: 150px; + width: 150px; height: 40px } @@ -944,7 +944,7 @@ h1.groupHeader { /*overflow-x: auto; HTML is damaged in case of big (2-3MB) responses */ } -/* replaced by .netInfoTextSelected for IE6 support +/* replaced by .netInfoTextSelected for IE6 support .netInfoText[selected="true"] { display: block; } @@ -963,7 +963,7 @@ h1.groupHeader { } .netInfoPostText .netInfoParamName { - width: 1px; /* Google Chrome need this otherwise the first column of + width: 1px; /* Google Chrome need this otherwise the first column of the post variables table will be larger than expected */ } @@ -1148,7 +1148,7 @@ h1.groupHeader { } * html .opened .spyHead .spyTitle, -* html .opened .logGroupLabel, +* html .opened .logGroupLabel, * html .opened .memberLabelCell .memberLabel { background-image: url(tree_close.gif); background-repeat: no-repeat; @@ -1677,7 +1677,7 @@ h1.groupHeader { /* .logRow-errorMessage > .hasTwisty > .errorTitle, .logRow-spy .spyHead .spyTitle, -.logGroup > .logRow +.logGroup > .logRow */ .logRow-errorMessage .hasTwisty .errorTitle, .logRow-spy .spyHead .spyTitle, @@ -2056,11 +2056,11 @@ h1.groupHeader { width: 10px; height: 10px; margin-top: 6px; - background: url(tabMenuTarget.png); + background: url(tabMenuTarget.png); } .fbTabMenuTarget:hover { - background: url(tabMenuTargetHover.png); + background: url(tabMenuTargetHover.png); } .fbShadow { @@ -2098,7 +2098,7 @@ h1.groupHeader { padding: 1px 18px 0; text-decoration: none; color: #000; - cursor: default; + cursor: default; background: #ACA899; margin: 4px 0; } @@ -2149,7 +2149,7 @@ h1.groupHeader { } .fbMenuShortcut { - padding-right: 85px; + padding-right: 85px; } .fbMenuShortcutKey { @@ -2274,7 +2274,7 @@ h1.groupHeader { margin: 0; padding: 0; overflow: hidden; - + font-family: Lucida Grande, Tahoma, sans-serif; font-size: 11px; background: #fff; @@ -2311,7 +2311,7 @@ h1.groupHeader { margin: 0 5px 0 0; background: #fff url(search.png) no-repeat 4px 2px !important; background: #fff url(search.gif) no-repeat 4px 2px; - padding-left: 20px; + padding-left: 20px; font-size: 11px; } @@ -2333,7 +2333,7 @@ h1.groupHeader { background: url(errorIcon.png) no-repeat !important; background: url(errorIcon.gif) no-repeat; color: #f00; - font-weight: bold; + font-weight: bold; } #fbMiniErrors { @@ -2348,7 +2348,7 @@ h1.groupHeader { margin: 3px 4px 0; height: 20px; width: 20px; - float: right; + float: right; background: url(sprite.png) 0 -135px; cursor: pointer; } @@ -2403,10 +2403,10 @@ h1.groupHeader { } /************************************************************************************************ - Sub-Layout + Sub-Layout *************************************************************************************************/ -/* fbToolbar +/* fbToolbar *************************************************************************************************/ #fbToolbarIcon { float: left; @@ -2472,7 +2472,7 @@ h1.groupHeader { #fbStatusBarBox { top: 4px; - cursor: default; + cursor: default; } .fbToolbarSeparator { @@ -2500,7 +2500,7 @@ h1.groupHeader { .fbStatusBar a:hover { color: blue; - cursor: pointer; + cursor: pointer; } @@ -2545,7 +2545,7 @@ h1.groupHeader { padding-left: 4px; } -/* body +/* body *************************************************************************************************/ .fbPanel { display: none; @@ -2609,7 +2609,7 @@ h1.groupHeader { position: absolute; right: 2px; bottom: 3px; - + z-index: 99; } @@ -2624,7 +2624,7 @@ h1.groupHeader { visibility: hidden !important; } -/* fbBottom +/* fbBottom *************************************************************************************************/ #fbCommand { @@ -2689,7 +2689,7 @@ div.fbFitHeight { Layout Controls *************************************************************************************************/ -/* fbToolbar buttons +/* fbToolbar buttons *************************************************************************************************/ .fbSmallButton { overflow: hidden; @@ -2729,7 +2729,7 @@ div.fbFitHeight { } -/* fbPanelBarBox tabs +/* fbPanelBarBox tabs *************************************************************************************************/ .fbTab { text-decoration: none; @@ -2785,7 +2785,7 @@ a.fbTab:hover .fbTabR { background: url(sprite.png) -8px -96px !important; } -/* splitters +/* splitters *************************************************************************************************/ #fbHSplitter { position: fixed; @@ -2888,7 +2888,7 @@ div.objectBox-element { position: relative; } -/* Webkit CSS Hack - bug in "highlight" named color */ +/* Webkit CSS Hack - bug in "highlight" named color */ @media screen and (-webkit-min-device-pixel-ratio:0) { .selectedElement{ background: #316AC5; @@ -2903,7 +2903,7 @@ div.objectBox-element { } /* TODO: remove this? */ -/* TODO: xxxpedro - IE need this in windowless mode (cnn.com) check if the issue is related to +/* TODO: xxxpedro - IE need this in windowless mode (cnn.com) check if the issue is related to position. if so, override it at chrome.js initialization when creating the div */ .logRow { position: relative; @@ -2939,9 +2939,9 @@ position. if so, override it at chrome.js initialization when creating the div * .objectBox-string { color: red; - + /* TODO: xxxpedro make long strings break line */ - /*white-space: pre; */ + /*white-space: pre; */ } .objectBox-number { diff --git a/vendor/firebug-lite/skin/xp/firebug.html b/vendor/firebug-lite/skin/xp/firebug.html index aa078099fb..c2b5c43472 100644 --- a/vendor/firebug-lite/skin/xp/firebug.html +++ b/vendor/firebug-lite/skin/xp/firebug.html @@ -14,71 +14,71 @@ - - - +
     
- +
- +   - - - + Inspect - + Clear - + - - + - - +
- +
- +