├── .gitignore
├── .travis.yml
├── CONTRIBUTING.md
├── Gruntfile.js
├── LICENSE
├── README.md
├── common-js
├── _.array.builders.js
├── _.array.selectors.js
├── _.collections.walk.js
├── _.function.arity.js
├── _.function.combinators.js
├── _.function.iterators.js
├── _.function.predicates.js
├── _.object.builders.js
├── _.object.selectors.js
├── _.util.existential.js
├── _.util.operators.js
├── _.util.strings.js
└── _.util.trampolines.js
├── component.json
├── dist
├── lodash-contrib.commonjs.js
├── lodash-contrib.js
└── lodash-contrib.min.js
├── docs
├── _.array.builders.js.md
├── _.array.selectors.js.md
├── _.collections.walk.js.md
├── _.function.arity.js.md
├── _.function.combinators.js.md
├── _.function.iterators.js.md
├── _.function.predicates.js.md
├── _.object.builders.js.md
├── _.object.selectors.js.md
├── _.util.existential.js.md
├── _.util.operators.js.md
├── _.util.strings.js.md
├── _.util.trampolines.js.md
└── index.md
├── examples
├── builders-examples.js.md
├── combinators-examples.js.md
└── walk-examples.js.md
├── gh-pages
├── _.array.builders.js.html
├── _.array.selectors.js.html
├── _.collections.walk.js.html
├── _.function.arity.js.html
├── _.function.combinators.js.html
├── _.function.iterators.js.html
├── _.function.predicates.js.html
├── _.object.builders.js.html
├── _.object.selectors.js.html
├── _.util.existential.js.html
├── _.util.operators.js.html
├── _.util.strings.js.html
├── _.util.trampolines.js.html
├── docco.css
├── index.html
└── public
│ ├── fonts
│ ├── aller-bold.eot
│ ├── aller-bold.ttf
│ ├── aller-bold.woff
│ ├── aller-light.eot
│ ├── aller-light.ttf
│ ├── aller-light.woff
│ ├── novecento-bold.eot
│ ├── novecento-bold.ttf
│ └── novecento-bold.woff
│ └── stylesheets
│ └── normalize.css
├── package.json
├── test
├── array.selectors.js
├── browserified.html
├── collections.walk.js
├── dist-min.html
├── function.combinators.js
├── function.iterators.js
├── function.predicates.js
├── index.html
├── mocha
│ ├── array.builders.js
│ ├── function.arity.js
│ ├── object.builders.js
│ ├── util.operators.js
│ ├── util.strings.js
│ └── vanilla.js
├── object.selectors.js
├── util.existential.js
├── util.strings.js
├── util.trampolines.js
└── vendor
│ ├── jquery.js
│ ├── jslitmus.js
│ ├── lodash.js
│ ├── qunit.css
│ ├── qunit.js
│ └── runner.js
└── tocdoc.css
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea
2 | /gen/
3 | raw
4 | node_modules
5 | .DS_Store
6 | docs/*.css
7 | docs/*.html
8 | docs/public
9 | examples/*.css
10 | examples/*.html
11 | examples/public
12 | *.log
13 | bower_components/
14 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - '6'
5 | notifications:
6 | slack: empeeric:Hsm2xE5SNniMiUAEgM0Ngcnb
7 | deploy:
8 | provider: npm
9 | email: me@refack.com
10 | api_key:
11 | secure: eLMBTF/VA5zprsHZY16xnUHf7wbnr9a/xVk6UsMxYg87K6EnT/2/jwfQ28cFhEn5DFiEnyAQjLG63ERxhKIIcXb4Z37XQJrjLoVKg0yaM/pIGiEyZ5WJw2sIwtzU43d2D/us0aCcLvJILYWajQglc1F61fTUxA3Fg555UUtDQU0=
12 | on:
13 | tags: true
14 | repo: node4good/lodash-contrib
15 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## How to contribute to lodash-contrib
2 |
3 | * Before you open a ticket or send a pull request, please [search](https://github.com/empeeric/lodash-contrib/issues) for previous discussions about the same feature or issue. Add to the earlier ticket if you find one.
4 |
5 | * Before sending a pull request for a feature, be sure to have [tests like found in lodash](http://lodashjs.org/test/). Tests may be run in a browser by opening `test/index.html`. Tests and linting can be run in the terminal by using the `grunt test` command, or `grunt watch:test` to automatically rerun after file save.
6 |
7 | * Use the same coding [style as lodash](https://github.com/documentcloud/lodash/blob/master/lodash.js).
8 |
9 | * In your pull request, do not add documentation, edit the files in `dist/` or use grunt to re-build the files in `dist/`. We'll do those things before cutting a new release.
10 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function (grunt) {
2 | var _ = require('lodash');
3 |
4 | grunt.loadNpmTasks('grunt-browserify');
5 | grunt.loadNpmTasks('grunt-contrib-copy');
6 | grunt.loadNpmTasks("grunt-contrib-uglify");
7 | grunt.loadNpmTasks('grunt-contrib-qunit');
8 | grunt.loadNpmTasks('grunt-contrib-jshint');
9 | grunt.loadNpmTasks("grunt-mocha-test");
10 |
11 | grunt.initConfig({
12 | pkg: grunt.file.readJSON('package.json'),
13 |
14 | contribBanner: "// <%= pkg.name %> v<%= pkg.version %>\n" +
15 | "// =========================\n\n" +
16 | "// > <%= pkg.homepage %>\n" +
17 | "// > (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors\n" +
18 | "// > (c) 2016 Refael Ackermann & node4good.org\n" +
19 | "// > <%= pkg.name %> may be freely distributed under the <%= pkg.license %> license.\n\n",
20 |
21 |
22 | copy: {
23 | main: {
24 | expand: true,
25 | flatten: true,
26 | src: 'gen/lodash*.js',
27 | dest: 'dist/'
28 | }
29 | },
30 |
31 |
32 | uglify: {
33 | all: {
34 | files: { "gen/lodash-contrib.min.js": "gen/lodash-contrib.js" }
35 | }
36 | },
37 |
38 |
39 | mochaTest: {
40 | test: {
41 | src: ['test/mocha/*.*'],
42 | options: {
43 | reporter: "spec"
44 | }
45 | }
46 | },
47 |
48 |
49 | qunit: {
50 | main: ['test/index.html'],
51 | minified: ['test/dist-min.html'],
52 | browserified: ['test/browserified.html']
53 | },
54 |
55 |
56 | jshint: {
57 | all: [
58 | "*.js",
59 | "test/*.js"
60 | ],
61 | options: {
62 | es3: true, // Enforce ES3 compatibility
63 | indent: 2, // Indent by 2 spaces
64 | camelcase: true, // All vars must be camelCase or UPPER_WITH_UNDERSCORES
65 | eqnull: true, // Allow 'x == null' convention
66 | forin: true, // Require `for x in y` to filter with `hasOwnProperty`
67 | newcap: true, // Require constructor names to be capitalized
68 | "-W058": false // Allow 'new Constructor' without parens
69 | }
70 | },
71 |
72 |
73 | browserWrap: {
74 | all: {
75 | files: [
76 | {
77 | src: ['common-js/*.*'],
78 | dst: 'gen/temp-scaffold.js'
79 | }
80 | ]
81 | }
82 | },
83 |
84 |
85 | commonjsWrap: {
86 | all: {
87 | files: [
88 | {
89 | src: ['gen/temp-scaffold.js'],
90 | dst: 'gen/lodash-contrib.commonjs.js'
91 | }
92 | ]
93 | }
94 | },
95 |
96 |
97 | browserify: {
98 | dist: {
99 | files: {
100 | 'gen/lodash-contrib.js': 'gen/temp-scaffold.js'
101 | }
102 | },
103 | test: {
104 | files: {
105 | 'gen/double.browserified.js': 'gen/lodash-contrib.js'
106 | },
107 | browserifyOptions: { debug: true }
108 | }
109 | }
110 | });
111 |
112 |
113 | grunt.registerMultiTask('browserWrap', 'index.js scaffolding task.', function () {
114 | grunt.log.writeln('Generating first pass browserWrap.js');
115 | var setup = this.files.pop();
116 | var code = setup.src.reduce(function (seed, val) { return seed + 'require("../' + val + '")(_);\n'; }, '');
117 | grunt.file.write(setup.dst, code);
118 | });
119 |
120 |
121 | grunt.registerMultiTask('commonjsWrap', 'index.js scaffolding task.', function () {
122 | grunt.log.writeln('Generating first pass commonjsWrap.js');
123 | var code = 'var _ = module.exports = require("lodash").runInContext();\n\n';
124 |
125 | var setup = this.files.pop();
126 | code += setup.src.reduce(function (seed, val) { return seed + grunt.file.read(val); }, '');
127 | grunt.file.write(setup.dst, code);
128 |
129 | code += '\n //Adding explicit method names for static analysis\n';
130 | var ctrb1 = require('./' + setup.dst);
131 | _(ctrb1).keys().sortBy().forEach(function (name) {
132 | var len = Math.max(20 - name.length, 2);
133 | var arr = new Array(len);
134 | var aligner = arr.join(' ');
135 | code += 'module.exports.' + name + aligner + ' = _.' + name + ';\n';
136 | });
137 | grunt.file.write(setup.dst, code);
138 | });
139 |
140 |
141 | grunt.registerTask('gen', ['browserWrap', 'commonjsWrap', 'browserify:dist', 'uglify', 'browserify:test', 'copy']);
142 | grunt.registerTask('test', ['jshint', 'mochaTest', 'qunit:main', 'qunit:browserified', 'qunit:minified']);
143 | grunt.registerTask('default', ['gen', 'test']);
144 | };
145 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Jeremy Ashkenas, Michael Fogus, DocumentCloud and Investigative Reporters & Editors
2 | Copyright (c) 2013 Refael Ackermann & Empeeric
3 |
4 | Permission is hereby granted, free of charge, to any person
5 | obtaining a copy of this software and associated documentation
6 | files (the "Software"), to deal in the Software without
7 | restriction, including without limitation the rights to use,
8 | copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the
10 | Software is furnished to do so, subject to the following
11 | conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 | OTHER DEALINGS IN THE SOFTWARE.
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | The brass buckles on lodash's utility belt
2 |
3 | Basically a [`lodash`](http://lodash.com/) compatible fork of [`underscore-contrib`](https://github.com/documentcloud/underscore-contrib)
4 |
5 | lodash-contrib
6 | ==============
7 | [](https://travis-ci.org/node4good/lodash-contrib)
8 |
9 | Links
10 | -----
11 |
12 | * [Documentation](https://github.com/node4good/lodash-contrib/blob/master/docs/index.md)
13 | * [Source repository](https://github.com/Empeeric/lodash-contrib)
14 | * [Tickets and bug reports](https://github.com/Empeeric/lodash-contrib/issues?state=open)
15 |
16 | Why lodash-contrib?
17 | -----------------------
18 |
19 | While lodash provides a bevy of useful tools to support functional programming in JavaScript, it can't
20 | (and shouldn't) be everything to everyone. lodash-contrib is intended as a home for functions that, for
21 | various reasons, don't belong in lodash proper. In particular, it aims to be:
22 |
23 | * a home for functions that are limited in scope, but solve certain point problems, and
24 | * a proving ground for features that belong in lodash proper, but need some advocacy and/or evolution
25 | (or devolution) to get them there.
26 |
27 | Use
28 | ---
29 |
30 | ####Web
31 |
32 | First, you’ll need lodash. Then you can grab the relevant lodash-contrib libraries and simply add something like the following to your pages:
33 | ```html
34 |
35 |
36 | ```
37 |
38 | You could also use [browserify](http://browserify.org/) to bundle your code into a JavaScript file that you can include in a web page.
39 | Require `lodash-contrib` in your main script file (e.g. `test.js`) like so:
40 |
41 | ```javascript
42 | var _ = require('lodash-contrib');
43 |
44 | // YOUR CODE COMES HERE
45 | console.log(_.truthyAll(0, 1, 2, 'lodash-contrib!'));
46 | ```
47 |
48 | then you could run `browserify test.js -o browserified.js` to get `lodash`, `lodash-contrib` and your code into `browserified.js`.
49 |
50 | ####Node
51 |
52 | Just run `npm install lodash-contrib`, you don't need to have lodash as it will be grabbed as a dependency.
53 |
54 | Contributing
55 | ------------
56 |
57 | **We need some docs sync** since rebasing to version 3 (some methods renamed xxxContrib)
58 |
59 | There is still a lot of work to do around perf, documentation, examples, testing and distribution so any help
60 | in those areas is welcomed. Pull requests are accepted, but please search the [issues](https://github.com/empeeric/lodash-contrib/issues)
61 | before proposing a new sub-contrib or addition. Additionally, all patches and proposals should have strong
62 | documentation, motivating cases and tests. It would be nice if we could not only provide useful tools built on
63 | lodash, but also provide an educational experience for why and how one might use them.
64 |
65 | Other (potentially) useful sub-contribs include the following:
66 |
67 | * String utilities
68 | * Date/time utilities
69 | * Validators
70 | * Iterators
71 | * Generators
72 | * Promises
73 | * Monads
74 | * Currying
75 | * Laziness
76 | * Multimethods
77 |
78 | What do these mean? Well, that’s up for discussion. :-)
79 |
--------------------------------------------------------------------------------
/common-js/_.array.builders.js:
--------------------------------------------------------------------------------
1 | module.exports = function(_) {
2 | // Create quick reference variables for speed access to core prototypes.
3 | var slice = Array.prototype.slice,
4 | concat = Array.prototype.concat,
5 | sort = Array.prototype.sort;
6 |
7 | var existy = function(x) { return x != null; };
8 |
9 | // Mixing in the array builders
10 | // ----------------------------
11 |
12 | _.mixin({
13 | // Concatenates one or more arrays given as arguments. If given objects and
14 | // scalars as arguments `cat` will plop them down in place in the result
15 | // array. If given an `arguments` object, `cat` will treat it like an array
16 | // and concatenate it likewise.
17 | cat: function() {
18 | return _.reduce(arguments, function(acc, elem) {
19 | if (_.isArguments(elem)) {
20 | return concat.call(acc, slice.call(elem));
21 | }
22 | else {
23 | return concat.call(acc, elem);
24 | }
25 | }, []);
26 | },
27 |
28 | // 'Constructs' an array by putting an element at its front
29 | cons: function(head, tail) {
30 | return _.cat([head], tail);
31 | },
32 |
33 | // Takes an array and chunks it some number of times into
34 | // sub-arrays of size n. Allows and optional padding array as
35 | // the third argument to fill in the tail chunk when n is
36 | // not sufficient to build chunks of the same size.
37 | chunkContrib: function(array, n, pad) {
38 | var args = arguments;
39 | var p = function(array) {
40 | if (array == null) return [];
41 |
42 | var part = _.take(array, n);
43 |
44 | if (n === _.size(part)) {
45 | return _.cons(part, p(_.drop(array, n)));
46 | }
47 | else if (args.length === 3) {
48 | pad = _.isArray(pad) ? pad : _.repeatContrib(n, pad);
49 | return [_.take(_.cat(part, pad), n)];
50 | }
51 | else {
52 | return [];
53 | }
54 | };
55 |
56 | return p(array);
57 | },
58 |
59 | // Takes an array and chunks it some number of times into
60 | // sub-arrays of size n. If the array given cannot fill the size
61 | // needs of the final chunk then a smaller chunk is used
62 | // for the last.
63 | chunkAll: function(array, n, step) {
64 | step = (step != null) ? step : n;
65 |
66 | var p = function(array, n, step) {
67 | if (_.isEmpty(array)) return [];
68 |
69 | return _.cons(_.take(array, n),
70 | p(_.drop(array, step), n, step));
71 | };
72 |
73 | return p(array, n, step);
74 | },
75 |
76 | // Maps a function over an array and concatenates all of the results.
77 | mapcat: function(array, fun) {
78 | return _.cat.apply(null, _.map(array, fun));
79 | },
80 |
81 | // Returns an array with some item between each element
82 | // of a given array.
83 | interpose: function(array, inter) {
84 | if (!_.isArray(array)) throw new TypeError;
85 | var sz = _.size(array);
86 | if (sz === 0) return array;
87 | if (sz === 1) return array;
88 |
89 | return slice.call(_.mapcat(array, function(elem) {
90 | return _.cons(elem, [inter]);
91 | }), 0, -1);
92 | },
93 |
94 | // Weaves two or more arrays together
95 | weave: function(/* args */) {
96 | if (!_.some(arguments)) return [];
97 | if (arguments.length == 1) return arguments[0];
98 |
99 | return _.filter(_.flatten(_.zip.apply(null, arguments), false), function(elem) {
100 | return elem != null;
101 | });
102 | },
103 | interleave: _.weave,
104 |
105 | // Returns an array of a value repeated a certain number of
106 | // times.
107 | repeatContrib: function(t, elem) {
108 | return _.times(t, function() { return elem; });
109 | },
110 |
111 | // Returns an array built from the contents of a given array repeated
112 | // a certain number of times.
113 | cycle: function(t, elems) {
114 | return _.flatten(_.times(t, function() { return elems; }), true);
115 | },
116 |
117 | // Returns an array with two internal arrays built from
118 | // taking an original array and spliting it at an index.
119 | splitAt: function(array, index) {
120 | return [_.take(array, index), _.drop(array, index)];
121 | },
122 |
123 | // Call a function recursively f(f(f(args))) until a second
124 | // given function goes falsey. Expects a seed value to start.
125 | iterateUntil: function(doit, checkit, seed) {
126 | var ret = [];
127 | var result = doit(seed);
128 |
129 | while (checkit(result)) {
130 | ret.push(result);
131 | result = doit(result);
132 | }
133 |
134 | return ret;
135 | },
136 |
137 | // Takes every nth item from an array, returning an array of
138 | // the results.
139 | takeSkipping: function(array, n) {
140 | var ret = [];
141 | var sz = _.size(array);
142 |
143 | if (n <= 0) return [];
144 | if (n === 1) return array;
145 |
146 | for(var index = 0; index < sz; index += n) {
147 | ret.push(array[index]);
148 | }
149 |
150 | return ret;
151 | },
152 |
153 | // Returns an array of each intermediate stage of a call to
154 | // a `reduce`-like function.
155 | reductions: function(array, fun, init) {
156 | var ret = [];
157 | var acc = init;
158 |
159 | _.each(array, function(v,k) {
160 | acc = fun(acc, array[k]);
161 | ret.push(acc);
162 | });
163 |
164 | return ret;
165 | },
166 |
167 | // Runs its given function on the index of the elements rather than
168 | // the elements themselves, keeping all of the truthy values in the end.
169 | keepIndexed: function(array, pred) {
170 | return _.filter(_.map(_.range(_.size(array)), function(i) {
171 | return pred(i, array[i]);
172 | }),
173 | existy);
174 | },
175 |
176 | // Accepts an array-like object (other than strings) as an argument and
177 | // returns an array whose elements are in the reverse order. Unlike the
178 | // built-in `Array.prototype.reverse` method, this does not mutate the
179 | // original object. Note: attempting to use this method on a string will
180 | // result in a `TypeError`, as it cannot properly reverse unicode strings.
181 |
182 | reverseOrder: function(obj) {
183 | if (typeof obj == 'string')
184 | throw new TypeError('Strings cannot be reversed by _.reverseOrder');
185 | return slice.call(obj).reverse();
186 | },
187 |
188 |
189 | // Returns copy or array sorted according to arbitrary ordering
190 | // order must be an array of values; defines the custom sort
191 | // key must be one of: missing/null, a string, or a function;
192 | collate: function(array, order, key) {
193 | if (!_.isArray(array)) throw new TypeError("expected an array as the first argument");
194 | if (!_.isArray(order)) throw new TypeError("expected an array as the second argument");
195 |
196 | return sort.call(array, function (a, b) {
197 | if(_.isFunction(key)) {
198 | valA = key.call(a);
199 | valB = key.call(b);
200 | } else if(existy(key)) {
201 | valA = a[key];
202 | valB = b[key];
203 | } else {
204 | valA = a;
205 | valB = b;
206 | }
207 |
208 | var rankA = _.indexOf(order, valA);
209 | var rankB = _.indexOf(order, valB);
210 |
211 | if(rankA === -1) return 1;
212 | if(rankB === -1) return -1;
213 |
214 | return rankA - rankB;
215 | });
216 | }
217 | });
218 |
219 | };
220 |
--------------------------------------------------------------------------------
/common-js/_.array.selectors.js:
--------------------------------------------------------------------------------
1 | module.exports = function(_) {
2 | // Create quick reference variables for speed access to core prototypes.
3 | var slice = Array.prototype.slice,
4 | concat = Array.prototype.concat;
5 |
6 | var existy = function(x) { return x != null; };
7 | var truthy = function(x) { return (x !== false) && existy(x); };
8 | var isSeq = function(x) { return (_.isArray(x)) || (_.isArguments(x)); };
9 |
10 | function nths(array, indices) {
11 | if (array == null) return void 0;
12 |
13 | if (isSeq(indices))
14 | return _(indices).map(function(i){return array[i];}).valueOf();
15 | else
16 | return nths(array, slice.call(arguments, 1));
17 | }
18 |
19 | // Mixing in the array selectors
20 | // ----------------------------
21 |
22 | _.mixin({
23 | // Returns the second element of an array. Passing **n** will return all but
24 | // the first of the head N values in the array. The **guard** check allows it
25 | // to work with `_.map`.
26 | second: function(array, n, guard) {
27 | if (array == null) return void 0;
28 | return (n != null) && !guard ? slice.call(array, 1, n) : array[1];
29 | },
30 |
31 | // Returns the third element of an array. Passing **n** will return all but
32 | // the first two of the head N values in the array. The **guard** check allows it
33 | // to work with `_.map`.
34 | third: function(array, n, guard) {
35 | if (array == null) return void 0;
36 | return (n != null) && !guard ? slice.call(array, 2, n) : array[2];
37 | },
38 |
39 | // A function to get at an index into an array
40 | nth: function(array, index, guard) {
41 | if ((index != null) && !guard) return array[index];
42 | },
43 |
44 | // A function to get values via indices into an array
45 | nths: nths,
46 | valuesAt: nths,
47 |
48 | // A function to get at "truthily" indexed values
49 | // bin refers to "binary" nature of true/false values in binIndices
50 | // but can also be thought of as putting array values into either "take" or "don't" bins
51 | binPick: function binPick(array, binIndices) {
52 | if (array == null) return void 0;
53 |
54 | if (isSeq(binIndices))
55 | return _.nths(array, _.range(binIndices.length).filter(function(i){return binIndices[i];}));
56 | else
57 | return binPick(array, slice.call(arguments, 1));
58 | },
59 |
60 | // Returns an array with two internal arrays built from
61 | // taking an original array and spliting it at the index
62 | // where a given function goes falsey.
63 | splitWith: function(array, pred) {
64 | return [_.takeWhile(array, pred), _.dropWhile(array, pred)];
65 | },
66 |
67 | // Takes an array and partitions it as the given predicate changes
68 | // truth sense.
69 | partitionBy: function(array, fun){
70 | if (_.isEmpty(array) || !existy(array)) return [];
71 |
72 | var fst = _.first(array);
73 | var fstVal = fun(fst);
74 | var run = concat.call([fst], _.takeWhile(_.rest(array), function(e) {
75 | return _.isEqual(fstVal, fun(e));
76 | }));
77 |
78 | return concat.call([run], _.partitionBy(_.drop(array, _.size(run)), fun));
79 | },
80 |
81 | // Returns the 'best' value in an array based on the result of a
82 | // given function.
83 | best: function(array, fun) {
84 | return _.reduce(array, function(x, y) {
85 | return fun(x, y) ? x : y;
86 | });
87 | },
88 |
89 | // Returns an array of existy results of a function over an source array.
90 | keep: function(array, fun) {
91 | if (!isSeq(array)) throw new TypeError("expected an array as the first argument");
92 |
93 | return _.filter(_.map(array, function(e) {
94 | return fun(e);
95 | }), existy);
96 | }
97 | });
98 |
99 | };
100 |
--------------------------------------------------------------------------------
/common-js/_.collections.walk.js:
--------------------------------------------------------------------------------
1 | module.exports = function (_) {
2 | // Helpers
3 | // -------
4 |
5 | // An internal object that can be returned from a visitor function to
6 | // prevent a top-down walk from walking subtrees of a node.
7 | var stopRecursion = {};
8 |
9 | // An internal object that can be returned from a visitor function to
10 | // cause the walk to immediately stop.
11 | var stopWalk = {};
12 |
13 | var notTreeError = 'Not a tree: same object found in two different branches';
14 |
15 | // Implements the default traversal strategy: if `obj` is a DOM node, walk
16 | // its DOM children; otherwise, walk all the objects it references.
17 | function defaultTraversal(obj) {
18 | return _.isElement(obj) ? obj.children : obj;
19 | }
20 |
21 | // Walk the tree recursively beginning with `root`, calling `beforeFunc`
22 | // before visiting an objects descendents, and `afterFunc` afterwards.
23 | // If `collectResults` is true, the last argument to `afterFunc` will be a
24 | // collection of the results of walking the node's subtrees.
25 | function walkImpl(root, traversalStrategy, beforeFunc, afterFunc, context, collectResults) {
26 | var visited = [];
27 | return (function _walk(value, key, parent) {
28 | // Keep track of objects that have been visited, and throw an exception
29 | // when trying to visit the same object twice.
30 | if (_.isObject(value)) {
31 | if (visited.indexOf(value) >= 0) throw new TypeError(notTreeError);
32 | visited.push(value);
33 | }
34 |
35 | if (beforeFunc) {
36 | var result = beforeFunc.call(context, value, key, parent);
37 | if (result === stopWalk) return stopWalk;
38 | if (result === stopRecursion) return;
39 | }
40 |
41 | var subResults;
42 | var target = traversalStrategy(value);
43 | if (_.isObject(target) && !_.isEmpty(target)) {
44 | // If collecting results from subtrees, collect them in the same shape
45 | // as the parent node.
46 | if (collectResults) subResults = _.isArray(value) ? [] : {};
47 |
48 | var stop = _.any(target, function(obj, key) {
49 | var result = _walk(obj, key, value);
50 | if (result === stopWalk) return true;
51 | if (subResults) subResults[key] = result;
52 | });
53 | if (stop) return stopWalk;
54 | }
55 | if (afterFunc) return afterFunc.call(context, value, key, parent, subResults);
56 | })(root);
57 | }
58 |
59 | // Internal helper providing the implementation for `pluck` and `pluckRec`.
60 | function pluck(obj, propertyName, recursive) {
61 | var results = [];
62 | this.preorder(obj, function(value, key) {
63 | if (!recursive && key == propertyName)
64 | return stopRecursion;
65 | if (_.has(value, propertyName))
66 | results[results.length] = value[propertyName];
67 | });
68 | return results;
69 | }
70 |
71 | var exports = {
72 | // Performs a preorder traversal of `obj` and returns the first value
73 | // which passes a truth test.
74 | find: function(obj, visitor, context) {
75 | var result;
76 | this.preorder(obj, function(value, key, parent) {
77 | if (visitor.call(context, value, key, parent)) {
78 | result = value;
79 | return stopWalk;
80 | }
81 | }, context);
82 | return result;
83 | },
84 |
85 | // Recursively traverses `obj` and returns all the elements that pass a
86 | // truth test. `strategy` is the traversal function to use, e.g. `preorder`
87 | // or `postorder`.
88 | filter: function(obj, strategy, visitor, context) {
89 | var results = [];
90 | if (obj == null) return results;
91 | strategy(obj, function(value, key, parent) {
92 | if (visitor.call(context, value, key, parent)) results.push(value);
93 | }, null, this._traversalStrategy);
94 | return results;
95 | },
96 |
97 | // Recursively traverses `obj` and returns all the elements for which a
98 | // truth test fails.
99 | reject: function(obj, strategy, visitor, context) {
100 | return this.filter(obj, strategy, function(value, key, parent) {
101 | return !visitor.call(context, value, key, parent);
102 | });
103 | },
104 |
105 | // Produces a new array of values by recursively traversing `obj` and
106 | // mapping each value through the transformation function `visitor`.
107 | // `strategy` is the traversal function to use, e.g. `preorder` or
108 | // `postorder`.
109 | map: function(obj, strategy, visitor, context) {
110 | var results = [];
111 | strategy(obj, function(value, key, parent) {
112 | results[results.length] = visitor.call(context, value, key, parent);
113 | }, null, this._traversalStrategy);
114 | return results;
115 | },
116 |
117 | // Return the value of properties named `propertyName` reachable from the
118 | // tree rooted at `obj`. Results are not recursively searched; use
119 | // `pluckRec` for that.
120 | pluck: function(obj, propertyName) {
121 | return pluck.call(this, obj, propertyName, false);
122 | },
123 |
124 | // Version of `pluck` which recursively searches results for nested objects
125 | // with a property named `propertyName`.
126 | pluckRec: function(obj, propertyName) {
127 | return pluck.call(this, obj, propertyName, true);
128 | },
129 |
130 | // Recursively traverses `obj` in a depth-first fashion, invoking the
131 | // `visitor` function for each object only after traversing its children.
132 | // `traversalStrategy` is intended for internal callers, and is not part
133 | // of the public API.
134 | postorder: function(obj, visitor, context, traversalStrategy) {
135 | traversalStrategy = traversalStrategy || this._traversalStrategy;
136 | walkImpl(obj, traversalStrategy, null, visitor, context);
137 | },
138 |
139 | // Recursively traverses `obj` in a depth-first fashion, invoking the
140 | // `visitor` function for each object before traversing its children.
141 | // `traversalStrategy` is intended for internal callers, and is not part
142 | // of the public API.
143 | preorder: function(obj, visitor, context, traversalStrategy) {
144 | traversalStrategy = traversalStrategy || this._traversalStrategy;
145 | walkImpl(obj, traversalStrategy, visitor, null, context);
146 | },
147 |
148 | // Builds up a single value by doing a post-order traversal of `obj` and
149 | // calling the `visitor` function on each object in the tree. For leaf
150 | // objects, the `memo` argument to `visitor` is the value of the `leafMemo`
151 | // argument to `reduce`. For non-leaf objects, `memo` is a collection of
152 | // the results of calling `reduce` on the object's children.
153 | reduce: function(obj, visitor, leafMemo, context) {
154 | var reducer = function(value, key, parent, subResults) {
155 | return visitor(subResults || leafMemo, value, key, parent);
156 | };
157 | return walkImpl(obj, this._traversalStrategy, null, reducer, context, true);
158 | }
159 | };
160 |
161 | // Set up aliases to match those in lodash.js.
162 | exports.collect = exports.map;
163 | exports.detect = exports.find;
164 | exports.select = exports.filter;
165 |
166 | // Returns an object containing the walk functions. If `traversalStrategy`
167 | // is specified, it is a function determining how objects should be
168 | // traversed. Given an object, it returns the object to be recursively
169 | // walked. The default strategy is equivalent to `_.identity` for regular
170 | // objects, and for DOM nodes it returns the node's DOM children.
171 | function walk(traversalStrategy) {
172 | var walker = _.clone(exports);
173 |
174 | // Bind all of the public functions in the walker to itself. This allows
175 | // the traversal strategy to be dynamically scoped.
176 | _.bindAll.apply(null, [walker].concat(_.keys(walker)));
177 |
178 | walker._traversalStrategy = traversalStrategy || defaultTraversal;
179 | return walker;
180 | }
181 |
182 | // Use `_.walk` as a namespace to hold versions of the walk functions which
183 | // use the default traversal strategy.
184 | _.extend(walk, walk());
185 |
186 | _.mixin({walk: walk});
187 | };
188 |
--------------------------------------------------------------------------------
/common-js/_.function.arity.js:
--------------------------------------------------------------------------------
1 | module.exports = function (_) {
2 |
3 | // Helpers
4 | // -------
5 |
6 | function enforcesUnary(fn) {
7 | return function mustBeUnary() {
8 | if (arguments.length === 1) {
9 | return fn.apply(this, arguments);
10 | }
11 | else throw new RangeError('Only a single argument may be accepted.');
12 |
13 | };
14 | }
15 |
16 | // Curry
17 | // -------
18 | var curry = (function () {
19 | function collectArgs(func, that, argCount, args, newArg, reverse) {
20 | if (reverse === true) {
21 | args.unshift(newArg);
22 | } else {
23 | args.push(newArg);
24 | }
25 | if (args.length == argCount) {
26 | return func.apply(that, args);
27 | } else {
28 | return enforcesUnary(function () {
29 | return collectArgs(func, that, argCount, args.slice(0), arguments[0], reverse);
30 | });
31 | }
32 | }
33 |
34 | return function curry(func, reverse) {
35 | var that = this;
36 | return enforcesUnary(function () {
37 | return collectArgs(func, that, func.length, [], arguments[0], reverse);
38 | });
39 | };
40 | }());
41 |
42 | // Enforce Arity
43 | // --------------------
44 | var enforce = (function () {
45 | var CACHE = [];
46 | return function enforce(func) {
47 | if (typeof func !== 'function') {
48 | throw new Error('Argument 1 must be a function.');
49 | }
50 | var funcLength = func.length;
51 | if (CACHE[funcLength] === undefined) {
52 | CACHE[funcLength] = function (enforceFunc) {
53 | return function () {
54 | if (arguments.length !== funcLength) {
55 | throw new RangeError(funcLength + ' arguments must be applied.');
56 | }
57 | return enforceFunc.apply(this, arguments);
58 | };
59 | };
60 | }
61 | return CACHE[funcLength](func);
62 | };
63 | }());
64 |
65 | // Right curry variants
66 | // ---------------------
67 | var curryRight = function (func) {
68 | return curry.call(this, func, true);
69 | };
70 |
71 | var curryRight2 = function (fun) {
72 | return enforcesUnary(function (last) {
73 | return enforcesUnary(function (first) {
74 | return fun.call(this, first, last);
75 | });
76 | });
77 | };
78 |
79 | var curryRight3 = function (fun) {
80 | return enforcesUnary(function (last) {
81 | return enforcesUnary(function (second) {
82 | return enforcesUnary(function (first) {
83 | return fun.call(this, first, second, last);
84 | });
85 | });
86 | });
87 | };
88 |
89 | // Mixing in the arity functions
90 | // -----------------------------
91 |
92 | _.mixin({
93 | // ### Fixed arguments
94 |
95 | // Fixes the arguments to a function based on the parameter template defined by
96 | // the presence of values and the `_` placeholder.
97 | fix: function (fun) {
98 | var fixArgs = _.tail(arguments);
99 |
100 | var f = function () {
101 | var args = fixArgs.slice();
102 | var arg = 0;
103 |
104 | for (var i = 0; i < (args.length || arg < arguments.length); i++) {
105 | if (args[i] === _) {
106 | args[i] = arguments[arg++];
107 | }
108 | }
109 |
110 | return fun.apply(null, args);
111 | };
112 |
113 | f._original = fun;
114 |
115 | return f;
116 | },
117 |
118 | unary: function (fun) {
119 | return function unary(a) {
120 | return fun.call(this, a);
121 | };
122 | },
123 |
124 | binary: function (fun) {
125 | return function binary(a, b) {
126 | return fun.call(this, a, b);
127 | };
128 | },
129 |
130 | ternary: function (fun) {
131 | return function ternary(a, b, c) {
132 | return fun.call(this, a, b, c);
133 | };
134 | },
135 |
136 | quaternary: function (fun) {
137 | return function quaternary(a, b, c, d) {
138 | return fun.call(this, a, b, c, d);
139 | };
140 | },
141 |
142 | rCurry: curryRight, // alias for backwards compatibility
143 |
144 | curry2: function (fun) {
145 | return enforcesUnary(function curried(first) {
146 | return enforcesUnary(function (last) {
147 | return fun.call(this, first, last);
148 | });
149 | });
150 | },
151 |
152 | curry3: function (fun) {
153 | return enforcesUnary(function (first) {
154 | return enforcesUnary(function (second) {
155 | return enforcesUnary(function (last) {
156 | return fun.call(this, first, second, last);
157 | });
158 | });
159 | });
160 | },
161 |
162 | // reverse currying for functions taking two arguments.
163 | curryRight2: curryRight2,
164 | rcurry2: curryRight2, // alias for backwards compatibility
165 |
166 | curryRight3: curryRight3,
167 | rcurry3: curryRight3, // alias for backwards compatibility
168 |
169 | // Dynamic decorator to enforce function arity and defeat varargs.
170 | enforce: enforce,
171 |
172 | arity: (function () {
173 | // Allow 'new Function', as that is currently the only reliable way
174 | // to manipulate function.length
175 | /* jshint -W054 */
176 | var FUNCTIONS = {};
177 | return function arity(numberOfArgs, fun) {
178 | if (FUNCTIONS[numberOfArgs] == null) {
179 | var parameters = new Array(numberOfArgs);
180 | for (var i = 0; i < numberOfArgs; ++i) {
181 | parameters[i] = "__" + i;
182 | }
183 | var pstr = parameters.join();
184 | var code = "return function (" + pstr + ") { return fun.apply(this, arguments); };";
185 | FUNCTIONS[numberOfArgs] = new Function(['fun'], code);
186 | }
187 | if (fun == null) {
188 | return function (fun) { return arity(numberOfArgs, fun); };
189 | }
190 | else return FUNCTIONS[numberOfArgs](fun);
191 | };
192 | })()
193 | });
194 |
195 |
196 | };
197 |
--------------------------------------------------------------------------------
/common-js/_.function.combinators.js:
--------------------------------------------------------------------------------
1 | module.exports = function (_) {
2 |
3 | // Helpers
4 | // -------
5 |
6 | var existy = function(x) { return x != null; };
7 | var truthy = function(x) { return (x !== false) && existy(x); };
8 | var __reverse = [].reverse;
9 | var __slice = [].slice;
10 | var __map = [].map;
11 | var curry2 = function (fun) {
12 | return function curried (first, optionalLast) {
13 | if (arguments.length === 1) {
14 | return function (last) {
15 | return fun(first, last);
16 | };
17 | }
18 | else return fun(first, optionalLast);
19 | };
20 | };
21 |
22 | // n.b. depends on lodash.function.arity.js
23 |
24 | // Takes a target function and a mapping function. Returns a function
25 | // that applies the mapper to its arguments before evaluating the body.
26 | function baseMapArgs (fun, mapFun) {
27 | return _.arity(fun.length, function () {
28 | return fun.apply(this, __map.call(arguments, mapFun));
29 | });
30 | }
31 |
32 | // Mixing in the combinator functions
33 | // ----------------------------------
34 |
35 | _.mixin({
36 | // Provide "always" alias for backwards compatibility
37 | always: _.constant,
38 |
39 | // Takes some number of functions, either as an array or variadically
40 | // and returns a function that takes some value as its first argument
41 | // and runs it through a pipeline of the original functions given.
42 | pipeline: function(/*, funs */){
43 | var funs = (_.isArray(arguments[0])) ? arguments[0] : arguments;
44 |
45 | return function(seed) {
46 | return _.reduce(funs,
47 | function(l,r) { return r(l); },
48 | seed);
49 | };
50 | },
51 | composeRight: _.pipeline,
52 |
53 | // Composes a bunch of predicates into a single predicate that
54 | // checks all elements of an array for conformance to all of the
55 | // original predicates.
56 | conjoin: function(/* preds */) {
57 | var preds = arguments;
58 |
59 | return function(array) {
60 | return _.every(array, function(e) {
61 | return _.every(preds, function(p) {
62 | return p(e);
63 | });
64 | });
65 | };
66 | },
67 |
68 | // Composes a bunch of predicates into a single predicate that
69 | // checks all elements of an array for conformance to any of the
70 | // original predicates.
71 | disjoin: function(/* preds */) {
72 | var preds = arguments;
73 |
74 | return function(array) {
75 | return _.some(array, function(e) {
76 | return _.some(preds, function(p) {
77 | return p(e);
78 | });
79 | });
80 | };
81 | },
82 |
83 | // Takes a predicate-like and returns a comparator (-1,0,1).
84 | comparator: function(fun) {
85 | return function(x, y) {
86 | if (truthy(fun(x, y)))
87 | return -1;
88 | else if (truthy(fun(y, x)))
89 | return 1;
90 | else
91 | return 0;
92 | };
93 | },
94 |
95 | // Returns a function that reverses the sense of a given predicate-like.
96 | complement: function(pred) {
97 | return function() {
98 | return !pred.apply(this, arguments);
99 | };
100 | },
101 |
102 | // Takes a function expecting varargs and
103 | // returns a function that takes an array and
104 | // uses its elements as the args to the original
105 | // function
106 | splat: function(fun) {
107 | return function(array) {
108 | return fun.apply(this, array);
109 | };
110 | },
111 |
112 | // Takes a function expecting an array and returns
113 | // a function that takes varargs and wraps all
114 | // in an array that is passed to the original function.
115 | unsplat: function(fun) {
116 | var funLength = fun.length;
117 |
118 | if (funLength < 1) {
119 | return fun;
120 | }
121 | else if (funLength === 1) {
122 | return function () {
123 | return fun.call(this, __slice.call(arguments, 0));
124 | };
125 | }
126 | else {
127 | return function () {
128 | var numberOfArgs = arguments.length,
129 | namedArgs = __slice.call(arguments, 0, funLength - 1),
130 | numberOfMissingNamedArgs = Math.max(funLength - numberOfArgs - 1, 0),
131 | argPadding = new Array(numberOfMissingNamedArgs),
132 | variadicArgs = __slice.call(arguments, fun.length - 1);
133 |
134 | return fun.apply(this, namedArgs.concat(argPadding).concat([variadicArgs]));
135 | };
136 | }
137 | },
138 |
139 | // Same as unsplat, but the rest of the arguments are collected in the
140 | // first parameter, e.g. unsplatl( function (args, callback) { ... ]})
141 | unsplatl: function(fun) {
142 | var funLength = fun.length;
143 |
144 | if (funLength < 1) {
145 | return fun;
146 | }
147 | else if (funLength === 1) {
148 | return function () {
149 | return fun.call(this, __slice.call(arguments, 0));
150 | };
151 | }
152 | else {
153 | return function () {
154 | var numberOfArgs = arguments.length,
155 | namedArgs = __slice.call(arguments, Math.max(numberOfArgs - funLength + 1, 0)),
156 | variadicArgs = __slice.call(arguments, 0, Math.max(numberOfArgs - funLength + 1, 0));
157 |
158 | return fun.apply(this, [variadicArgs].concat(namedArgs));
159 | };
160 | }
161 | },
162 |
163 | // map the arguments of a function
164 | mapArgs: curry2(baseMapArgs),
165 |
166 | // Returns a function that returns an array of the calls to each
167 | // given function for some arguments.
168 | juxt: function(/* funs */) {
169 | var funs = arguments;
170 |
171 | return function(/* args */) {
172 | var args = arguments;
173 | return _.map(funs, function(f) {
174 | return f.apply(this, args);
175 | }, this);
176 | };
177 | },
178 |
179 | // Returns a function that protects a given function from receiving
180 | // non-existy values. Each subsequent value provided to `fnull` acts
181 | // as the default to the original function should a call receive non-existy
182 | // values in the defaulted arg slots.
183 | fnull: function(fun /*, defaults */) {
184 | var defaults = _.rest(arguments);
185 |
186 | return function(/*args*/) {
187 | var args = _.toArray(arguments);
188 | var sz = _.size(defaults);
189 |
190 | for(var i = 0; i < sz; i++) {
191 | if (!existy(args[i]))
192 | args[i] = defaults[i];
193 | }
194 |
195 | return fun.apply(this, args);
196 | };
197 | },
198 |
199 | // Flips the first two args of a function
200 | flip2: function(fun) {
201 | return function(/* args */) {
202 | var flipped = __slice.call(arguments);
203 | flipped[0] = arguments[1];
204 | flipped[1] = arguments[0];
205 |
206 | return fun.apply(this, flipped);
207 | };
208 | },
209 |
210 | // Flips an arbitrary number of args of a function
211 | flip: function(fun) {
212 | return function(/* args */) {
213 | var reversed = __reverse.call(arguments);
214 |
215 | return fun.apply(this, reversed);
216 | };
217 | },
218 |
219 | // Takes a method-style function (one which uses `this`) and pushes
220 | // `this` into the argument list. The returned function uses its first
221 | // argument as the receiver/context of the original function, and the rest
222 | // of the arguments are used as the original's entire argument list.
223 | functionalize: function(method) {
224 | return function(ctx /*, args */) {
225 | return method.apply(ctx, _.rest(arguments));
226 | };
227 | },
228 |
229 | // Takes a function and pulls the first argument out of the argument
230 | // list and into `this` position. The returned function calls the original
231 | // with its receiver (`this`) prepending the argument list. The original
232 | // is called with a receiver of `null`.
233 | methodize: function(func) {
234 | return function(/* args */) {
235 | return func.apply(null, _.cons(this, arguments));
236 | };
237 | },
238 |
239 | k: _.always,
240 | t: _.pipeline
241 | });
242 |
243 | _.unsplatr = _.unsplat;
244 |
245 | // map the arguments of a function, takes the mapping function
246 | // first so it can be used as a combinator
247 | _.mapArgsWith = curry2(_.flip(baseMapArgs));
248 |
249 | // Returns function property of object by name, bound to object
250 | _.bound = function(obj, fname) {
251 | var fn = obj[fname];
252 | if (!_.isFunction(fn))
253 | throw new TypeError("Expected property to be a function");
254 | return _.bind(fn, obj);
255 | };
256 |
257 | };
258 |
--------------------------------------------------------------------------------
/common-js/_.function.iterators.js:
--------------------------------------------------------------------------------
1 | module.exports = function (_) {
2 |
3 | // Helpers
4 | // -------
5 |
6 | var HASNTBEENRUN = {};
7 |
8 | function unary (fun) {
9 | return function (first) {
10 | return fun.call(this, first);
11 | };
12 | }
13 |
14 | function binary (fun) {
15 | return function (first, second) {
16 | return fun.call(this, first, second);
17 | };
18 | }
19 |
20 | // Mixing in the iterator functions
21 | // --------------------------------
22 |
23 | function foldl (iter, binaryFn, seed) {
24 | var state, element;
25 | if (seed !== void 0) {
26 | state = seed;
27 | }
28 | else {
29 | state = iter();
30 | }
31 | element = iter();
32 | while (element != null) {
33 | state = binaryFn.call(element, state, element);
34 | element = iter();
35 | }
36 | return state;
37 | }
38 |
39 | function unfold (seed, unaryFn) {
40 | var state = HASNTBEENRUN;
41 | return function () {
42 | if (state === HASNTBEENRUN) {
43 | state = seed;
44 | } else if (state != null) {
45 | state = unaryFn.call(state, state);
46 | }
47 |
48 | return state;
49 | };
50 | }
51 |
52 | // note that the unfoldWithReturn behaves differently than
53 | // unfold with respect to the first value returned
54 | function unfoldWithReturn (seed, unaryFn) {
55 | var state = seed,
56 | pair,
57 | value;
58 | return function () {
59 | if (state != null) {
60 | pair = unaryFn.call(state, state);
61 | value = pair[1];
62 | state = value != null ? pair[0] : void 0;
63 | return value;
64 | }
65 | else return void 0;
66 | };
67 | }
68 |
69 | function accumulate (iter, binaryFn, initial) {
70 | var state = initial;
71 | return function () {
72 | var element = iter();
73 | if (element == null) {
74 | return element;
75 | }
76 | else {
77 | if (state === void 0) {
78 | state = element;
79 | } else {
80 | state = binaryFn.call(element, state, element);
81 | }
82 |
83 | return state;
84 | }
85 | };
86 | }
87 |
88 | function accumulateWithReturn (iter, binaryFn, initial) {
89 | var state = initial,
90 | stateAndReturnValue,
91 | element;
92 | return function () {
93 | element = iter();
94 | if (element == null) {
95 | return element;
96 | }
97 | else {
98 | if (state === void 0) {
99 | state = element;
100 | return state;
101 | }
102 | else {
103 | stateAndReturnValue = binaryFn.call(element, state, element);
104 | state = stateAndReturnValue[0];
105 | return stateAndReturnValue[1];
106 | }
107 | }
108 | };
109 | }
110 |
111 | function map (iter, unaryFn) {
112 | return function() {
113 | var element;
114 | element = iter();
115 | if (element != null) {
116 | return unaryFn.call(element, element);
117 | } else {
118 | return void 0;
119 | }
120 | };
121 | }
122 |
123 | function mapcat(iter, unaryFn) {
124 | var lastIter = null;
125 | return function() {
126 | var element;
127 | var gen;
128 | if (lastIter == null) {
129 | gen = iter();
130 | if (gen == null) {
131 | lastIter = null;
132 | return void 0;
133 | }
134 | lastIter = unaryFn.call(gen, gen);
135 | }
136 | while (element == null) {
137 | element = lastIter();
138 | if (element == null) {
139 | gen = iter();
140 | if (gen == null) {
141 | lastIter = null;
142 | return void 0;
143 | }
144 | else {
145 | lastIter = unaryFn.call(gen, gen);
146 | }
147 | }
148 | }
149 | return element;
150 | };
151 | }
152 |
153 | function select (iter, unaryPredicateFn) {
154 | return function() {
155 | var element;
156 | element = iter();
157 | while (element != null) {
158 | if (unaryPredicateFn.call(element, element)) {
159 | return element;
160 | }
161 | element = iter();
162 | }
163 | return void 0;
164 | };
165 | }
166 |
167 | function reject (iter, unaryPredicateFn) {
168 | return select(iter, function (something) {
169 | return !unaryPredicateFn(something);
170 | });
171 | }
172 |
173 | function find (iter, unaryPredicateFn) {
174 | return select(iter, unaryPredicateFn)();
175 | }
176 |
177 | function slice (iter, numberToDrop, numberToTake) {
178 | var count = 0;
179 | while (numberToDrop-- > 0) {
180 | iter();
181 | }
182 | if (numberToTake != null) {
183 | return function() {
184 | if (++count <= numberToTake) {
185 | return iter();
186 | } else {
187 | return void 0;
188 | }
189 | };
190 | }
191 | else return iter;
192 | }
193 |
194 | function drop (iter, numberToDrop) {
195 | return slice(iter, numberToDrop == null ? 1 : numberToDrop);
196 | }
197 |
198 | function take (iter, numberToTake) {
199 | return slice(iter, 0, numberToTake == null ? 1 : numberToTake);
200 | }
201 |
202 | function List (array) {
203 | var index = 0;
204 | return function() {
205 | return array[index++];
206 | };
207 | }
208 |
209 | function Tree (array) {
210 | var index, myself, state;
211 | index = 0;
212 | state = [];
213 | myself = function() {
214 | var element, tempState;
215 | element = array[index++];
216 | if (element instanceof Array) {
217 | state.push({
218 | array: array,
219 | index: index
220 | });
221 | array = element;
222 | index = 0;
223 | return myself();
224 | } else if (element === void 0) {
225 | if (state.length > 0) {
226 | tempState = state.pop();
227 | array = tempState.array;
228 | index = tempState.index;
229 | return myself();
230 | } else {
231 | return void 0;
232 | }
233 | } else {
234 | return element;
235 | }
236 | };
237 | return myself;
238 | }
239 |
240 | function K (value) {
241 | return function () {
242 | return value;
243 | };
244 | }
245 |
246 | function upRange (from, to, by) {
247 | return function () {
248 | var was;
249 |
250 | if (from > to) {
251 | return void 0;
252 | }
253 | else {
254 | was = from;
255 | from = from + by;
256 | return was;
257 | }
258 | };
259 | }
260 |
261 | function downRange (from, to, by) {
262 | return function () {
263 | var was;
264 |
265 | if (from < to) {
266 | return void 0;
267 | }
268 | else {
269 | was = from;
270 | from = from - by;
271 | return was;
272 | }
273 | };
274 | }
275 |
276 | function range (from, to, by) {
277 | if (from == null) {
278 | return upRange(1, Infinity, 1);
279 | }
280 | else if (to == null) {
281 | return upRange(from, Infinity, 1);
282 | }
283 | else if (by == null) {
284 | if (from <= to) {
285 | return upRange(from, to, 1);
286 | }
287 | else return downRange(from, to, 1);
288 | }
289 | else if (by > 0) {
290 | return upRange(from, to, by);
291 | }
292 | else if (by < 0) {
293 | return downRange(from, to, Math.abs(by));
294 | }
295 | else return k(from);
296 | }
297 |
298 | var numbers = unary(range);
299 |
300 | _.iterators = {
301 | accumulate: accumulate,
302 | accumulateWithReturn: accumulateWithReturn,
303 | foldl: foldl,
304 | reduce: foldl,
305 | unfold: unfold,
306 | unfoldWithReturn: unfoldWithReturn,
307 | map: map,
308 | mapcat: mapcat,
309 | select: select,
310 | reject: reject,
311 | filter: select,
312 | find: find,
313 | slice: slice,
314 | drop: drop,
315 | take: take,
316 | List: List,
317 | Tree: Tree,
318 | constant: K,
319 | K: K,
320 | numbers: numbers,
321 | range: range
322 | };
323 |
324 | };
325 |
--------------------------------------------------------------------------------
/common-js/_.function.predicates.js:
--------------------------------------------------------------------------------
1 | module.exports = function (_) {
2 |
3 | // Mixing in the predicate functions
4 | // ---------------------------------
5 |
6 | _.mixin({
7 | // A wrapper around instanceof
8 | isInstanceOf: function(x, t) { return (x instanceof t); },
9 |
10 | // An associative object is one where its elements are
11 | // accessed via a key or index. (i.e. array and object)
12 | isAssociative: function(x) { return _.isArray(x) || _.isObject(x) || _.isArguments(x); },
13 |
14 | // An indexed object is anything that allows numerical index for
15 | // accessing its elements (e.g. arrays and strings). NOTE: lodash
16 | // does not support cross-browser consistent use of strings as array-like
17 | // objects, so be wary in IE 8 when using String objects and IE<8.
18 | // on string literals & objects.
19 | isIndexed: function(x) { return _.isArray(x) || _.isString(x) || _.isArguments(x); },
20 |
21 | // A seq is something considered a sequential composite type (i.e. arrays and `arguments`).
22 | isSequential: function(x) { return (_.isArray(x)) || (_.isArguments(x)); },
23 |
24 | // These do what you think that they do
25 | isZero: function(x) { return 0 === x; },
26 | isEven: function(x) { return _.isFinite(x) && (x & 1) === 0; },
27 | isOdd: function(x) { return _.isFinite(x) && !_.isEven(x); },
28 | isPositive: function(x) { return x > 0; },
29 | isNegative: function(x) { return x < 0; },
30 | isValidDate: function(x) { return _.isDate(x) && !_.isNaN(x.getTime()); },
31 |
32 | // A numeric is a variable that contains a numeric value, regardless its type
33 | // It can be a String containing a numeric value, exponential notation, or a Number object
34 | // See here for more discussion: http://stackoverflow.com/questions/18082/validate-numbers-in-javascript-isnumeric/1830844#1830844
35 | isNumeric: function(n) {
36 | return !isNaN(parseFloat(n)) && isFinite(n);
37 | },
38 |
39 | // An integer contains an optional minus sign to begin and only the digits 0-9
40 | // Objects that can be parsed that way are also considered ints, e.g. "123"
41 | // Floats that are mathematically equal to integers are considered integers, e.g. 1.0
42 | // See here for more discussion: http://stackoverflow.com/questions/1019515/javascript-test-for-an-integer
43 | isInteger: function(i) {
44 | return _.isNumeric(i) && i % 1 === 0;
45 | },
46 |
47 | // A float is a numbr that is not an integer.
48 | isFloat: function(n) {
49 | return _.isNumeric(n) && !_.isInteger(n);
50 | },
51 |
52 | // checks if a string is a valid JSON
53 | isJSON: function(str) {
54 | try {
55 | JSON.parse(str);
56 | } catch (e) {
57 | return false;
58 | }
59 | return true;
60 | },
61 |
62 | // Returns true if its arguments are monotonically
63 | // increaing values; false otherwise.
64 | isIncreasing: function() {
65 | var count = _.size(arguments);
66 | if (count === 1) return true;
67 | if (count === 2) return arguments[0] < arguments[1];
68 |
69 | for (var i = 1; i < count; i++) {
70 | if (arguments[i-1] >= arguments[i]) {
71 | return false;
72 | }
73 | }
74 |
75 | return true;
76 | },
77 |
78 | // Returns true if its arguments are monotonically
79 | // decreaing values; false otherwise.
80 | isDecreasing: function() {
81 | var count = _.size(arguments);
82 | if (count === 1) return true;
83 | if (count === 2) return arguments[0] > arguments[1];
84 |
85 | for (var i = 1; i < count; i++) {
86 | if (arguments[i-1] <= arguments[i]) {
87 | return false;
88 | }
89 | }
90 |
91 | return true;
92 | }
93 | });
94 |
95 | };
96 |
--------------------------------------------------------------------------------
/common-js/_.object.builders.js:
--------------------------------------------------------------------------------
1 | module.exports = function (_) {
2 |
3 | // Helpers
4 | // -------
5 |
6 | // Create quick reference variables for speed access to core prototypes.
7 | var slice = Array.prototype.slice,
8 | concat = Array.prototype.concat;
9 |
10 | var existy = function(x) { return x != null; };
11 | var truthy = function(x) { return (x !== false) && existy(x); };
12 | var isAssociative = function(x) { return _.isArray(x) || _.isObject(x); };
13 | var curry2 = function(fun) {
14 | return function(last) {
15 | return function(first) {
16 | return fun(first, last);
17 | };
18 | };
19 | };
20 |
21 | // Mixing in the object builders
22 | // ----------------------------
23 |
24 | _.mixin({
25 | // Takes an object and another object of strings to strings where the second
26 | // object describes the key renaming to occur in the first object.
27 | renameKeys: function(obj, kobj) {
28 | return _.reduce(kobj, function(o, nu, old) {
29 | if (existy(obj[old])) {
30 | o[nu] = obj[old];
31 | return o;
32 | }
33 | else
34 | return o;
35 | },
36 | _.omit.apply(null, concat.call([obj], _.keys(kobj))));
37 | },
38 |
39 | // Snapshots an object deeply. Based on the version by
40 | // [Keith Devens](http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone)
41 | // until we can find a more efficient and robust way to do it.
42 | snapshot: function(obj) {
43 | if(obj == null || typeof(obj) != 'object') {
44 | return obj;
45 | }
46 |
47 | var temp = new obj.constructor();
48 |
49 | for(var key in obj) {
50 | if (obj.hasOwnProperty(key)) {
51 | temp[key] = _.snapshot(obj[key]);
52 | }
53 | }
54 |
55 | return temp;
56 | },
57 |
58 | // Updates the value at any depth in a nested object based on the
59 | // path described by the keys given. The function provided is supplied
60 | // the current value and is expected to return a value for use as the
61 | // new value. If no keys are provided, then the object itself is presented
62 | // to the given function.
63 | updatePath: function(obj, fun, ks, defaultValue) {
64 | if (!isAssociative(obj)) throw new TypeError("Attempted to update a non-associative object.");
65 | if (!existy(ks)) return fun(obj);
66 |
67 | var deepness = _.isArray(ks);
68 | var keys = deepness ? ks : [ks];
69 | var ret = deepness ? _.snapshot(obj) : _.clone(obj);
70 | var lastKey = _.last(keys);
71 | var target = ret;
72 |
73 | _.each(_.initial(keys), function(key) {
74 | if (defaultValue && !_.has(target, key)) {
75 | target[key] = _.clone(defaultValue);
76 | }
77 | target = target[key];
78 | });
79 |
80 | target[lastKey] = fun(target[lastKey]);
81 | return ret;
82 | },
83 |
84 | // Sets the value at any depth in a nested object based on the
85 | // path described by the keys given.
86 | setPath: function(obj, value, ks, defaultValue) {
87 | if (!existy(ks)) throw new TypeError("Attempted to set a property at a null path.");
88 |
89 | return _.updatePath(obj, function() { return value; }, ks, defaultValue);
90 | },
91 |
92 | // Returns an object where each element of an array is keyed to
93 | // the number of times that it occurred in said array.
94 | frequencies: curry2(_.countBy)(_.identity)
95 | });
96 |
97 | };
98 |
--------------------------------------------------------------------------------
/common-js/_.object.selectors.js:
--------------------------------------------------------------------------------
1 | module.exports = function (_) {
2 |
3 | // Helpers
4 | // -------
5 |
6 | // Create quick reference variables for speed access to core prototypes.
7 | var concat = Array.prototype.concat;
8 | var ArrayProto = Array.prototype;
9 | var slice = ArrayProto.slice;
10 |
11 | // Mixing in the object selectors
12 | // ------------------------------
13 |
14 | _.mixin({
15 | // Returns a function that will attempt to look up a named field
16 | // in any object that it's given.
17 | accessor: function(field) {
18 | return function(obj) {
19 | return (obj && obj[field]);
20 | };
21 | },
22 |
23 | // Given an object, returns a function that will attempt to look up a field
24 | // that it's given.
25 | dictionary: function (obj) {
26 | return function(field) {
27 | return (obj && field && obj[field]);
28 | };
29 | },
30 |
31 | // Like `_.pick` except that it takes an array of keys to pick.
32 | selectKeys: function (obj, ks) {
33 | return _.pick.apply(null, concat.call([obj], ks));
34 | },
35 |
36 | // Returns the key/value pair for a given property in an object, undefined if not found.
37 | kv: function(obj, key) {
38 | if (_.has(obj, key)) {
39 | return [key, obj[key]];
40 | }
41 |
42 | return void 0;
43 | },
44 |
45 | // Gets the value at any depth in a nested object based on the
46 | // path described by the keys given. Keys may be given as an array
47 | // or as a dot-separated string.
48 | getPath: function getPath (obj, ks) {
49 | if (typeof ks == "string") ks = ks.split(".");
50 |
51 | // If we have reached an undefined property
52 | // then stop executing and return undefined
53 | if (obj === undefined) return void 0;
54 |
55 | // If the path array has no more elements, we've reached
56 | // the intended property and return its value
57 | if (ks.length === 0) return obj;
58 |
59 | // If we still have elements in the path array and the current
60 | // value is null, stop executing and return undefined
61 | if (obj === null) return void 0;
62 |
63 | return getPath(obj[_.first(ks)], _.rest(ks));
64 | },
65 |
66 | // Returns a boolean indicating whether there is a property
67 | // at the path described by the keys given
68 | hasPath: function hasPath (obj, ks) {
69 | if (typeof ks == "string") ks = ks.split(".");
70 |
71 | var numKeys = ks.length;
72 |
73 | if (obj == null && numKeys > 0) return false;
74 |
75 | if (_.contains(['boolean', 'string', 'number'], typeof obj)) return false;
76 |
77 | if (!(ks[0] in obj)) return false;
78 |
79 | if (numKeys === 1) return true;
80 |
81 | return hasPath(obj[_.first(ks)], _.rest(ks));
82 | },
83 |
84 | pickWhen: function(obj, pred) {
85 | var copy = {};
86 |
87 | _.each(obj, function(value, key) {
88 | if (pred(obj[key])) copy[key] = obj[key];
89 | });
90 |
91 | return copy;
92 | },
93 |
94 | omitWhen: function(obj, pred) {
95 | return _.pickWhen(obj, function(e) { return !pred(e); });
96 | }
97 |
98 | });
99 |
100 | };
101 |
--------------------------------------------------------------------------------
/common-js/_.util.existential.js:
--------------------------------------------------------------------------------
1 | module.exports = function(_) {
2 |
3 | // Mixing in the truthiness
4 | // ------------------------
5 |
6 | _.mixin({
7 | exists: function(x) { return x != null; },
8 | truthy: function(x) { return (x !== false) && _.exists(x); },
9 | falsey: function(x) { return !_.truthy(x); },
10 | not: function(b) { return !b; },
11 |
12 | existsAll: function() { return _.every(arguments, _.exists); },
13 | truthyAll: function() { return _.every(arguments, _.truthy); },
14 | falseyAll: function() { return _.every(arguments, _.falsey); },
15 | firstExisting: function() {
16 | for (var i = 0; i < arguments.length; i++) {
17 | if (_.exists(arguments[i])) return arguments[i];
18 | }
19 | }
20 | });
21 |
22 | };
23 |
24 |
--------------------------------------------------------------------------------
/common-js/_.util.operators.js:
--------------------------------------------------------------------------------
1 | module.exports = function (_) {
2 |
3 | // Setup for variadic operators
4 | // ----------------------------
5 |
6 | // Turn a binary math operator into a variadic operator
7 | function variadicMath(operator) {
8 | return function() {
9 | return _.reduce(arguments, operator);
10 | };
11 | }
12 |
13 | // Turn a binary comparator into a variadic comparator
14 | function variadicComparator(comparator) {
15 | return function() {
16 | var result;
17 | for (var i = 0; i < arguments.length - 1; i++) {
18 | result = comparator(arguments[i], arguments[i + 1]);
19 | if (result === false) return result;
20 | }
21 | return result;
22 | };
23 | }
24 |
25 | // Turn a boolean-returning function into one with the opposite meaning
26 | function invert(fn) {
27 | return function() {
28 | return !fn.apply(this, arguments);
29 | };
30 | }
31 |
32 | // Basic math operators
33 | function add(x, y) {
34 | return x + y;
35 | }
36 |
37 | function sub(x, y) {
38 | return x - y;
39 | }
40 |
41 | function mul(x, y) {
42 | return x * y;
43 | }
44 |
45 | function div(x, y) {
46 | return x / y;
47 | }
48 |
49 | function mod(x, y) {
50 | return x % y;
51 | }
52 |
53 | function inc(x) {
54 | return ++x;
55 | }
56 |
57 | function dec(x) {
58 | return --x;
59 | }
60 |
61 | function neg(x) {
62 | return -x;
63 | }
64 |
65 | // Bitwise operators
66 | function bitwiseAnd(x, y) {
67 | return x & y;
68 | }
69 |
70 | function bitwiseOr(x, y) {
71 | return x | y;
72 | }
73 |
74 | function bitwiseXor(x, y) {
75 | return x ^ y;
76 | }
77 |
78 | function bitwiseLeft(x, y) {
79 | return x << y;
80 | }
81 |
82 | function bitwiseRight(x, y) {
83 | return x >> y;
84 | }
85 |
86 | function bitwiseZ(x, y) {
87 | return x >>> y;
88 | }
89 |
90 | function bitwiseNot(x) {
91 | return ~x;
92 | }
93 |
94 | // Basic comparators
95 | function eq(x, y) {
96 | return x == y;
97 | }
98 |
99 | function seq(x, y) {
100 | return x === y;
101 | }
102 |
103 | // Not
104 | function not(x) {
105 | return !x;
106 | }
107 |
108 | // Relative comparators
109 | function gt(x, y) {
110 | return x > y;
111 | }
112 |
113 | function lt(x, y) {
114 | return x < y;
115 | }
116 |
117 | function gte(x, y) {
118 | return x >= y;
119 | }
120 |
121 | function lte(x, y) {
122 | return x <= y;
123 | }
124 |
125 | // Mixing in the operator functions
126 | // -----------------------------
127 |
128 | _.mixin({
129 | addContrib: variadicMath(add),
130 | sub: variadicMath(sub),
131 | mul: variadicMath(mul),
132 | div: variadicMath(div),
133 | mod: mod,
134 | inc: inc,
135 | dec: dec,
136 | neg: neg,
137 | eqContrib: variadicComparator(eq),
138 | seq: variadicComparator(seq),
139 | neq: invert(variadicComparator(eq)),
140 | sneq: invert(variadicComparator(seq)),
141 | not: not,
142 | gtContrib: variadicComparator(gt),
143 | ltContrib: variadicComparator(lt),
144 | gteContrib: variadicComparator(gte),
145 | lteContrib: variadicComparator(lte),
146 | bitwiseAnd: variadicMath(bitwiseAnd),
147 | bitwiseOr: variadicMath(bitwiseOr),
148 | bitwiseXor: variadicMath(bitwiseXor),
149 | bitwiseNot: bitwiseNot,
150 | bitwiseLeft: variadicMath(bitwiseLeft),
151 | bitwiseRight: variadicMath(bitwiseRight),
152 | bitwiseZ: variadicMath(bitwiseZ)
153 | });
154 | };
155 |
--------------------------------------------------------------------------------
/common-js/_.util.strings.js:
--------------------------------------------------------------------------------
1 | module.exports = function (_) {
2 |
3 | // Helpers
4 | // -------
5 |
6 | // No reason to create regex more than once
7 | var REGEX = {
8 | boundary: /(\b.)/g,
9 | bracket: /(?:([^\[]+))|(?:\[(.*?)\])/g,
10 | capitalLetters: /([A-Z])/g,
11 | dot: /\./g,
12 | htmlTags: /<\/?[^<>]*>/gi,
13 | lowerThenUpper: /([a-z])([A-Z])/g,
14 | nonCamelCase: /[-_\s](\w)/g,
15 | plus: /\+/g,
16 | regex: /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,
17 | space: / /g,
18 | underscore: /_/g,
19 | upperThenLower: /\b([A-Z]+)([A-Z])([a-z])/g
20 | };
21 |
22 | var urlDecode = function (s) {
23 | return decodeURIComponent(s.replace(REGEX.plus, '%20'));
24 | };
25 |
26 | var buildParams = function (prefix, val, top) {
27 | if (_.isUndefined(top)) top = true;
28 | if (_.isArray(val)) {
29 | return _.map(val, function (value, key) {
30 | return buildParams(top ? key : prefix + '[]', value, false);
31 | }).join('&');
32 | } else if (_.isObject(val)) {
33 | return _.map(val, function (value, key) {
34 | return buildParams(top ? key : prefix + '[' + key + ']', value, false);
35 | }).join('&');
36 | } else {
37 | return encodeURIComponent(prefix) + '=' + encodeURIComponent(val);
38 | }
39 | };
40 |
41 | // Mixing in the string utils
42 | // ----------------------------
43 |
44 | _.mixin({
45 | // Explodes a string into an array of chars
46 | explode: function (s) {
47 | return s.split('');
48 | },
49 |
50 | // Parses a query string into a hash
51 | fromQuery: function (str) {
52 | var parameters = str.split('&'),
53 | obj = {},
54 | parameter,
55 | key,
56 | match,
57 | lastKey,
58 | subKey,
59 | depth;
60 |
61 | // Iterate over key/value pairs
62 | _.each(parameters, function (parameter) {
63 | parameter = parameter.split('=');
64 | key = urlDecode(parameter[0]);
65 | lastKey = key;
66 | depth = obj;
67 |
68 | // Reset so we don't have issues when matching the same string
69 | REGEX.bracket.lastIndex = 0;
70 |
71 | // Attempt to extract nested values
72 | while ((match = REGEX.bracket.exec(key)) !== null) {
73 | if (!_.isUndefined(match[1])) {
74 |
75 | // If we're at the top nested level, no new object needed
76 | subKey = match[1];
77 |
78 | } else {
79 |
80 | // If we're at a lower nested level, we need to step down, and make
81 | // sure that there is an object to place the value into
82 | subKey = match[2];
83 | depth[lastKey] = depth[lastKey] || (subKey ? {} : []);
84 | depth = depth[lastKey];
85 | }
86 |
87 | // Save the correct key as a hash or an array
88 | lastKey = subKey || _.size(depth);
89 | }
90 |
91 | // Assign value to nested object
92 | depth[lastKey] = urlDecode(parameter[1]);
93 | });
94 |
95 | return obj;
96 | },
97 |
98 | // Implodes and array of chars into a string
99 | implode: function (a) {
100 | return a.join('');
101 | },
102 |
103 | // Converts camel case to dashed (opposite of _.camelCase)
104 | toDash: function (string) {
105 | string = string.replace(REGEX.capitalLetters, function ($1) {return "-" + $1.toLowerCase();});
106 | // remove first dash
107 | return ( string.charAt(0) == '-' ) ? string.substr(1) : string;
108 | },
109 |
110 | // Creates a query string from a hash
111 | toQuery: function (obj) {
112 | return buildParams('', obj);
113 | },
114 |
115 | // Reports whether a string contains a search string.
116 | strContains: function (str, search) {
117 | if (typeof str != 'string') throw new TypeError( 'First argument to strContains must be a string' );
118 | return (str.indexOf(search) != -1);
119 | },
120 |
121 | // Upper case first letter in every word.
122 | titleCase: function capitalize(string) {
123 | return string.replace(REGEX.boundary, function ($1) {return $1.toUpperCase();});
124 | },
125 |
126 | // Slugify a string. Makes lowercase, and converts dots and spaces to dashes.
127 | slugify: function (urlString) {
128 | return urlString.replace(REGEX.lowerThenUpper, '$1-$2')
129 | .replace(REGEX.space, '-')
130 | .replace(REGEX.dot, '-')
131 | .toLowerCase();
132 | },
133 |
134 | // Humanize a slug by adding spaces in place of underscores and between words
135 | humanize: function (slugish) {
136 | return _.capitalize(slugish
137 | // Replace _ with a space
138 | .replace(REGEX.underscore, ' ')
139 | // insert a space between lower & upper
140 | .replace(REGEX.lowerThenUpper, '$1 $2')
141 | // space before last upper in a sequence followed by lower
142 | .replace(REGEX.upperThenLower, '$1 $2$3')
143 | );
144 | },
145 |
146 | // Strip HTML-ish tags from string
147 | stripTags: function (suspectString) {
148 | var str = suspectString.replace(REGEX.htmlTags, '');
149 | return str;
150 | }
151 |
152 | });
153 | };
154 |
--------------------------------------------------------------------------------
/common-js/_.util.trampolines.js:
--------------------------------------------------------------------------------
1 | module.exports = function (_) {
2 |
3 | // Helpers
4 | // -------
5 |
6 |
7 | // Mixing in the truthiness
8 | // ------------------------
9 |
10 | _.mixin({
11 | done: function(value) {
12 | var ret = _(value);
13 | ret.stopTrampoline = true;
14 | return ret;
15 | },
16 |
17 | trampoline: function(fun /*, args */) {
18 | var result = fun.apply(fun, _.rest(arguments));
19 |
20 | while (_.isFunction(result)) {
21 | result = result();
22 | if ((result instanceof _) && (result.stopTrampoline)) break;
23 | }
24 |
25 | return result.value();
26 | }
27 | });
28 |
29 | };
30 |
--------------------------------------------------------------------------------
/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lodash-contrib",
3 | "description": "The brass buckles on lodash's utility belt",
4 | "repo": "node4good/lodash-contrib",
5 | "version": "41200.0.0",
6 | "dependencies": {
7 | "lodash/lodash": "4.12.0"
8 | },
9 | "main": "dist/lodash-contrib.js",
10 | "scripts": [
11 | "dist/lodash-contrib.js"
12 | ],
13 | "license": "MIT"
14 | }
15 |
--------------------------------------------------------------------------------
/docs/_.array.selectors.js.md:
--------------------------------------------------------------------------------
1 | ### array.selectors
2 |
3 | > Functions to take things from arrays. View Annotated Source
4 |
5 | --------------------------------------------------------------------------------
6 |
7 | #### best
8 |
9 | **Signature:** `_.best(array:Array, fun:Function)`
10 |
11 | Returns the "best" value in an array based on the result of a given function.
12 |
13 | ```javascript
14 | _.best([1, 2, 3, 4, 5], function(x, y) {
15 | return x > y;
16 | });
17 | //=> 5
18 | ```
19 |
20 | --------------------------------------------------------------------------------
21 |
22 | #### dropWhile
23 |
24 | **Signature:** `_.dropWhile(array:Array, pred:Function)`
25 |
26 | Drops elements for which the given function returns a truthy value.
27 |
28 | ```javascript
29 | _.dropWhile([-2,-1,0,1,2], isNeg);
30 | //=> [0,1,2]
31 | ```
32 |
33 | --------------------------------------------------------------------------------
34 |
35 | #### keep
36 |
37 | **Signature:** `_.keep(array:Array, fun:Function)`
38 |
39 | Returns an array of existy results of a function over a source array.
40 |
41 | ```javascript
42 | _.keep([1, 2, 3, 4, 5], function(val) {
43 | if (val % 3 === 0) {
44 | return val;
45 | }
46 | });
47 | // => [3]
48 | ```
49 |
50 | --------------------------------------------------------------------------------
51 |
52 | #### nth
53 |
54 | **Signature:** `_.nth(array:Array, index:Number[, guard:Any])`
55 |
56 | The `_.nth` function is a convenience for the equivalent `array[n]`. The
57 | optional `guard` value allows `_.nth` to work correctly as a callback for
58 | `_.map`.
59 |
60 | ```javascript
61 | _.nth(['a','b','c'], 2);
62 | //=> 'c'
63 | ```
64 |
65 | If given an index out of bounds then `_.nth` will return `undefined`:
66 |
67 | ```javascript
68 | _.nth(['a','b','c'], 2000);
69 | //=> undefined
70 | ```
71 |
72 | The `_.nth` function can also be used in conjunction with `_.map` and `_.compact` like so:
73 |
74 | ```javascript
75 | var b = [['a'],['b'],[]];
76 |
77 | _.compact(_.map(b, function(e) { return _.nth(e,0) }));
78 | //=> ['a','b']
79 | ```
80 |
81 | If wrapping a function around `_.nth` is too tedious or you'd like to partially apply the index then Underscore-contrib offers any of `_.flip2`, `_.fix` or `_.curryRight2` to solve this.
82 |
83 | --------------------------------------------------------------------------------
84 |
85 | #### partitionBy
86 |
87 | **Signature:** `_.keep(array:Array, fun:Function)`
88 |
89 | Takes an array and partitions it into sub-arrays as the given predicate changes
90 | truth sense.
91 |
92 | ```javascript
93 | _.partitionBy([1,2,2,3,1,1,5], _.isEven);
94 | // => [[1],[2,2],[3,1,1,5]]
95 |
96 | _.partitionBy([1,2,2,3,1,1,5], _.identity);
97 | // => [[1],[2,2],[3],[1,1],[5]]
98 | ```
99 |
100 | --------------------------------------------------------------------------------
101 |
102 | #### second
103 |
104 | **Signature:** `_.second(array:Array, index:Number[, guard:Any])`
105 |
106 | The `_.second` function is a convenience for the equivalent `array[1]`. The
107 | optional `guard` value allows `_.second` to work correctly as a callback for
108 | `_.map`.
109 |
110 | ```javascript
111 | _.second(['a','b']);
112 | //=> 'b'
113 |
114 | _.map([['a','b'], _.range(10,20)], _.second);
115 | //=> ['b',11]
116 | ```
117 |
118 | You can also pass an optional number to the `_.second` function to take a number of elements from an array starting with the second element and ending at the given index:
119 |
120 | ```javascript
121 | _.second(_.range(10), 5)
122 | //=> [1, 2, 3, 4]
123 | ```
124 |
125 | --------------------------------------------------------------------------------
126 |
127 | #### takeWhile
128 |
129 | **Signature:** `_.takeWhile(array:Array, pred:Function)`
130 |
131 | The `_.takeWhile` function takes an array and a function and returns a new array containing the first n elements in the original array for which the given function returns a truthy value:
132 |
133 | ```javascript
134 | var isNeg = function(n) { return n < 0; };
135 |
136 | _.takeWhile([-2,-1,0,1,2], isNeg);
137 | //=> [-2,-1]
138 | ```
139 |
140 | --------------------------------------------------------------------------------
141 |
142 | #### third
143 |
144 | **Signature:** `_.third(array:Array, index:Number[, guard:Any])`
145 |
146 | The `_.third` function is a convenience for the equivalent `array[2]`. The
147 | optional `guard` value allows `_.third` to work correctly as a callback for
148 | `_.map`.
149 |
150 | ```javascript
151 | _.third(['a','b','c']);
152 | //=> 'c'
153 |
154 | _.map([['a','b','c'], _.range(10,20)], _.third);
155 | //=> ['c',12]
156 | ```
157 |
158 | You can also pass an optional number to the `_.third` function to take a number of elements from an array starting with the third element and ending at the given index:
159 |
160 | ```javascript
161 | _.third(_.range(10), 5)
162 | //=> [2, 3, 4]
163 | ```
164 |
165 | --------------------------------------------------------------------------------
--------------------------------------------------------------------------------
/docs/_.collections.walk.js.md:
--------------------------------------------------------------------------------
1 | ### collections.walk
2 |
3 | > Functions to recursively walk collections which are trees.
4 |
5 | Documentation should use [Journo](https://github.com/jashkenas/journo) formats and standards.
6 |
7 | _.walk = walk;
8 | postorder: function(obj, visitor, context)
9 | preorder: function(obj, visitor, context)
10 | walk(obj, visitor, null, context)
11 | map: function(obj, strategy, visitor, context)
12 | pluck: function(obj, propertyName)
13 | pluckRec: function(obj, propertyName)
14 | _.walk.collect = _.walk.map;
15 |
16 |
--------------------------------------------------------------------------------
/docs/_.function.arity.js.md:
--------------------------------------------------------------------------------
1 | ### function.arity
2 |
3 | > Functions which manipulate the way functions work with their arguments.
4 |
5 | --------------------------------------------------------------------------------
6 |
7 | #### arity
8 |
9 | **Signature:** `_.arity(numberOfArgs:Number, fun:Function)`
10 |
11 | Returns a new function which is equivalent to `fun`, except that the new
12 | function's `length` property is equal to `numberOfArgs`. This does **not** limit
13 | the function to using that number of arguments. It's only effect is on the
14 | reported length.
15 |
16 | ```javascript
17 | function addAll() {
18 | var sum = 0;
19 | for (var i = 0; i < arguments.length; i++) {
20 | sum = sum + arguments[i];
21 | }
22 | return sum;
23 | }
24 |
25 | addAll.length
26 | // => 0
27 |
28 | var addAllWithFixedLength = _.arity(2, addAll);
29 |
30 | addAllWithFixedLength.length
31 | // => 2
32 |
33 | addAllWithFixedLength(1, 1, 1, 1);
34 | // => 4
35 | ```
36 |
37 | --------------------------------------------------------------------------------
38 |
39 | #### binary
40 |
41 | **Signature:** `_.binary(fun:Function)`
42 |
43 | Returns a new function which accepts only two arguments and passes these
44 | arguments to `fun`. Additional arguments are discarded.
45 |
46 | ```javascript
47 | function addAll() {
48 | var sum = 0;
49 | for (var i = 0; i < arguments.length; i++) {
50 | sum = sum + arguments[i];
51 | }
52 | return sum;
53 | }
54 |
55 | var add2 = _.binary(addAll);
56 |
57 | add2(1, 1);
58 | // => 2
59 |
60 | add2(1, 1, 1, 1);
61 | // => 2
62 | ```
63 |
64 | --------------------------------------------------------------------------------
65 |
66 | #### curry
67 |
68 | **Signature:** `_.curry(func:Function[, reverse:Boolean])`
69 |
70 | Returns a curried version of `func`. If `reverse` is true, arguments will be
71 | processed from right to left.
72 |
73 | ```javascript
74 | function add3 (x, y, z) {
75 | return x + y + z;
76 | }
77 |
78 | var curried = _.curry(add3);
79 | // => function
80 |
81 | curried(1);
82 | // => function
83 |
84 | curried(1)(2);
85 | // => function
86 |
87 | curried(1)(2)(3);
88 | // => 6
89 | ```
90 |
91 | --------------------------------------------------------------------------------
92 |
93 | #### curry2
94 |
95 | **Signature:** `_.curry2(fun:Function)`
96 |
97 | Returns a curried version of `func`, but will curry exactly two arguments, no
98 | more or less.
99 |
100 | ```javascript
101 | function add2 (a, b) {
102 | return a + b;
103 | }
104 |
105 | var curried = _.curry2(add2);
106 | // => function
107 |
108 | curried(1);
109 | // => function
110 |
111 | curried(1)(2);
112 | // => 3
113 | ```
114 |
115 | --------------------------------------------------------------------------------
116 |
117 | #### curry3
118 |
119 | **Signature:** `_.curry3(fun:Function)`
120 |
121 | Returns a curried version of `func`, but will curry exactly three arguments, no
122 | more or less.
123 |
124 | ```javascript
125 | function add3 (a, b, c) {
126 | return a + b + c;
127 | }
128 |
129 | var curried = _.curry3(add3);
130 | // => function
131 |
132 | curried(1);
133 | // => function
134 |
135 | curried(1)(2);
136 | // => function
137 |
138 | curried(1)(2)(3);
139 | // => 6
140 | ```
141 |
142 | --------------------------------------------------------------------------------
143 |
144 | #### curryRight
145 |
146 | **Signature:** `_.curryRight(func:Function)`
147 |
148 | **Aliases:** `_.rCurry`
149 |
150 | Returns a curried version of `func` where arguments are processed from right
151 | to left.
152 |
153 | ```javascript
154 | function divide (a, b) {
155 | return a / b;
156 | }
157 |
158 | var curried = _.curryRight(divide);
159 | // => function
160 |
161 | curried(1);
162 | // => function
163 |
164 | curried(1)(2);
165 | // => 2
166 |
167 | curried(2)(1);
168 | // => 0.5
169 | ```
170 |
171 | --------------------------------------------------------------------------------
172 |
173 | #### curryRight2
174 |
175 | **Signature:** `_.curryRight2(func:Function)`
176 |
177 | **Aliases:** `_.rcurry2`
178 |
179 | Returns a curried version of `func` where a maxium of two arguments are
180 | processed from right to left.
181 |
182 | ```javascript
183 | function concat () {
184 | var str = "";
185 |
186 | for (var i = 0; i < arguments.length; i++) {
187 | str = str + arguments[i];
188 | }
189 |
190 | return str;
191 | }
192 |
193 | var curried = _.curryRight2(concat);
194 | // => function
195 |
196 | curried("a");
197 | // => function
198 |
199 | curried("a")("b");
200 | // => "ba"
201 |
202 | ```
203 |
204 | --------------------------------------------------------------------------------
205 |
206 | #### curryRight3
207 |
208 | **Signature:** `_.curryRight3(func:Function)`
209 |
210 | **Aliases:** `_.rcurry3`
211 |
212 | Returns a curried version of `func` where a maxium of three arguments are
213 | processed from right to left.
214 |
215 | ```javascript
216 | function concat () {
217 | var str = "";
218 |
219 | for (var i = 0; i < arguments.length; i++) {
220 | str = str + arguments[i];
221 | }
222 |
223 | return str;
224 | }
225 |
226 | var curried = _.curryRight3(concat);
227 | // => function
228 |
229 | curried("a");
230 | // => function
231 |
232 | curried("a")("b");
233 | // => function
234 |
235 | curried("a")("b")("c");
236 | // => "cba"
237 |
238 | ```
239 |
240 |
241 | --------------------------------------------------------------------------------
242 |
243 | #### fix
244 |
245 | **Signature:** `_.fix(fun:Function[, value:Any...])`
246 |
247 | Fixes the arguments to a function based on the parameter template defined by
248 | the presence of values and the `_` placeholder.
249 |
250 | ```javascript
251 | function add3 (a, b, c) {
252 | return a + b + c;
253 | }
254 |
255 | var fixedFirstAndLast = _.fix(add3, 1, _, 3);
256 | // => function
257 |
258 | fixedFirstAndLast(2);
259 | // => 6
260 |
261 | fixedFirstAndLast(10);
262 | // => 14
263 | ```
264 |
265 | --------------------------------------------------------------------------------
266 |
267 | #### quaternary
268 |
269 | **Signature:** `_.quaternary(fun:Function)`
270 |
271 | Returns a new function which accepts only four arguments and passes these
272 | arguments to `fun`. Additional arguments are discarded.
273 |
274 | ```javascript
275 | function addAll() {
276 | var sum = 0;
277 | for (var i = 0; i < arguments.length; i++) {
278 | sum = sum + arguments[i];
279 | }
280 | return sum;
281 | }
282 |
283 | var add4 = _.quaternary(addAll);
284 |
285 | add4(1, 1, 1, 1);
286 | // => 4
287 |
288 | add4(1, 1, 1, 1, 1, 1);
289 | // => 4
290 | ```
291 |
292 | --------------------------------------------------------------------------------
293 |
294 | #### ternary
295 |
296 | **Signature:** `_.ternary(fun:Function)`
297 |
298 | Returns a new function which accepts only three arguments and passes these
299 | arguments to `fun`. Additional arguments are discarded.
300 |
301 | ```javascript
302 | function addAll() {
303 | var sum = 0;
304 | for (var i = 0; i < arguments.length; i++) {
305 | sum = sum + arguments[i];
306 | }
307 | return sum;
308 | }
309 |
310 | var add3 = _.ternary(addAll);
311 |
312 | add3(1, 1, 1);
313 | // => 3
314 |
315 | add3(1, 1, 1, 1, 1, 1);
316 | // => 3
317 | ```
318 |
319 | --------------------------------------------------------------------------------
320 |
321 | #### unary
322 |
323 | **Signature:** `_.unary(fun:Function)`
324 |
325 | Returns a new function which accepts only one argument and passes this
326 | argument to `fun`. Additional arguments are discarded.
327 |
328 | ```javascript
329 | function logArgs() {
330 | console.log(arguments);
331 | }
332 |
333 | var logOneArg = _.unary(logArgs);
334 |
335 | logOneArg("first");
336 | // => ["first"]
337 |
338 | logOneArg("first", "second");
339 | // => ["first"]
340 | ```
341 |
342 | --------------------------------------------------------------------------------
--------------------------------------------------------------------------------
/docs/_.function.predicates.js.md:
--------------------------------------------------------------------------------
1 | ### function.predicates
2 |
3 | > Functions which return whether the input meets a condition.
4 |
5 | --------------------------------------------------------------------------------
6 |
7 | #### isAssociative
8 |
9 | **Signature:** `isAssociative(value:Any)`
10 |
11 | Returns a boolean indicating whether or not the value is an associative object.
12 | An associative object is one where its elements can be accessed via a key or
13 | index (e.g. arrays, `arguments`, objects).
14 |
15 | ```javascript
16 | _.isAssociative(["Athens", "Sparta"]);
17 | // => true
18 |
19 | _.isAssociative(42);
20 | // => false
21 | ```
22 | --------------------------------------------------------------------------------
23 |
24 | #### isDecreasing
25 |
26 | **Signature:** `_.isDecreasing(values:Any...)`
27 |
28 | Checks whether the arguments are monotonically decreasing values (i.e. whether
29 | each argument is less than the previous argument.)
30 |
31 | ```javascript
32 | _.isDecreasing(3, 2, 1);
33 | // => true
34 |
35 | _.isDecreasing(15, 12, 2);
36 | // => true
37 |
38 | _.isDecreasing(2, 3);
39 | // => false
40 | ```
41 |
42 | --------------------------------------------------------------------------------
43 |
44 | #### isEven
45 |
46 | **Signature:** `_.isEven(value:Any)`
47 |
48 | Checks whether the value is an even number.
49 |
50 | ```javascript
51 | _.isEven(12);
52 | // => true
53 |
54 | _.isEven(3);
55 | // => false
56 |
57 | _.isEven({});
58 | // => false
59 | ```
60 |
61 | --------------------------------------------------------------------------------
62 |
63 | #### isFloat
64 |
65 | **Signature:** `_.isFloat(value:Any)`
66 |
67 | Checks whether the value is a "float." For the purposes of this function, a float
68 | is a numeric value that is not an integer. A numeric value may be a number, a
69 | string containing a number, a `Number` object, etc.
70 |
71 | **NOTE:** JavaScript itself makes no distinction between integers and floats. For
72 | the purposes of this function both `1` and `1.0` are considered integers.
73 |
74 | ```javascript
75 | _.isFloat(1.1);
76 | // => true
77 |
78 | _.isFloat(1);
79 | // => false
80 |
81 | _.isFloat(1.0);
82 | // => false
83 |
84 | _.isFloat("2.15");
85 | // => true
86 | ```
87 |
88 | --------------------------------------------------------------------------------
89 |
90 | #### isIncreasing
91 |
92 | **Signature:** `_.isIncreasing(value:Any...)`
93 |
94 | Checks whether the arguments are monotonically increasing values (i.e. each
95 | argument is greater than the previous argument.)
96 |
97 | ```javascript
98 | _.isIncreasing(1, 12, 15);
99 | // => true
100 |
101 | _.isIncreasing(1);
102 | // => true
103 |
104 | _.isIncreasing(5, 4);
105 | // => false
106 | ```
107 |
108 | --------------------------------------------------------------------------------
109 |
110 | #### isIndexed
111 |
112 | **Signature:** `_.isIndexed(value:Any)`
113 |
114 | Checks whether the value is "indexed." An indexed value is one which accepts a
115 | numerical index to access its elements. (e.g. arrays and strings)
116 |
117 | **NOTE:** lodash does not support cross-browser consistent use of strings as
118 | array-like values, so be wary in IE 8 when using string objects and in IE7 and
119 | earlier when using string literals & objects.
120 |
121 | ```javascript
122 | _.isIndexed("Socrates");
123 | // => true
124 |
125 | _.isIndexed({poison: "hemlock"});
126 | // => false
127 | ```
128 |
129 | --------------------------------------------------------------------------------
130 |
131 | #### isInstanceOf
132 |
133 | **Signature:** `_.isInstanceOf(value:Any, constructor:Function)`
134 |
135 | Checks whether the value is an instance of the constructor.
136 |
137 | ```javascript
138 | _.isInstanceOf(new Date(), Date);
139 | // => true
140 |
141 | _.isInstanceOf("Hippocrates", RegExp);
142 | // => false
143 | ```
144 |
145 | --------------------------------------------------------------------------------
146 |
147 | #### isInteger
148 |
149 | **Signature:** `_.isInteger(value:Any)`
150 |
151 | Checks whether the value is a numeric integer. A numeric value can be a string
152 | containing a number, a `Number` object, etc.
153 |
154 | ```javascript
155 | _.isInteger(18);
156 | // => true
157 |
158 | _.isInteger("18");
159 | // => true
160 |
161 | _.isInteger(2.5);
162 | // => false
163 |
164 | _.isInteger(-1);
165 | // => true
166 | ```
167 |
168 | --------------------------------------------------------------------------------
169 |
170 | #### isJSON
171 |
172 | **Signature:** `_.isJSON(value:Any)`
173 |
174 | Checks whether the value is valid JSON. [See the spec](http://www.json.org/) for
175 | more information on what constitutes valid JSON.
176 |
177 | **NOTE:** This function relies on `JSON.parse` which is not available in IE7 and
178 | earlier.
179 |
180 | ```javascript
181 | _.isJSON('{ "name": "Crockford" }');
182 | // => true
183 |
184 | _.isJSON({ name: "Crocket" });
185 | // => false
186 | ```
187 |
188 | --------------------------------------------------------------------------------
189 |
190 | #### isNegative
191 |
192 | **Signature:** `_.isNegative(value:Any)`
193 |
194 | Checks whether the value is a negative number.
195 |
196 | ```javascript
197 | _.isNegative(-2);
198 | // => true
199 |
200 | _.isNegative(5);
201 | // => false
202 | ```
203 |
204 | --------------------------------------------------------------------------------
205 |
206 | #### isNumeric
207 |
208 | **Signature:** `_.isNumeric(value:Any)`
209 |
210 | A numeric is something that contains a numeric value, regardless of its type. It
211 | can be a string containing a numeric value, exponential notation, a `Number`
212 | object, etc.
213 |
214 | ```javascript
215 | _.isNumeric("14");
216 | // => true
217 |
218 | _.isNumeric("fourteen");
219 | // => false
220 | ```
221 |
222 | --------------------------------------------------------------------------------
223 |
224 | #### isOdd
225 |
226 | **Signature:** `_.isOdd(value:Any)`
227 |
228 | Checks whether the value is an odd number.
229 |
230 | ```javascript
231 | _.isOdd(3);
232 | // => true
233 |
234 | _.isOdd(2);
235 | // => false
236 |
237 | _.isOdd({});
238 | // => false
239 | ```
240 |
241 | --------------------------------------------------------------------------------
242 |
243 | #### isPlainObject
244 |
245 | **Signature:** `_.isPlainObject(value:Any);`
246 |
247 | Checks whether the value is a "plain" object created as an object literal (`{}`)
248 | or explicitly constructed with `new Object()`. Instances of other constructors
249 | are **not** plain objects.
250 |
251 | ```javascript
252 | _.isPlainObject({});
253 | // => true
254 |
255 | _.isPlainObject(new Date());
256 | // => false
257 |
258 | _.isPlainObject([]);
259 | // => false
260 | ```
261 |
262 | --------------------------------------------------------------------------------
263 |
264 | #### isPositive
265 |
266 | **Signature:** `_.isPositive(value:Any)`
267 |
268 | Checks whether the value is a positive number.
269 |
270 | ```javascript
271 | _.isPositive(21);
272 | // => true
273 |
274 | _.isPositive(-3);
275 | // => false
276 | ```
277 |
278 | --------------------------------------------------------------------------------
279 |
280 | #### isSequential
281 |
282 | **Signature:** `_.isSequential(value:Any)`
283 |
284 | Checks whether the value is a sequential composite type (i.e. arrays and
285 | `arguments`).
286 |
287 | ```javascript
288 | _.isSequential(["Herodotus", "Thucidydes");
289 | // => true
290 |
291 | _.isSequential(new Date);
292 | // => false
293 | ```
294 |
295 | --------------------------------------------------------------------------------
296 |
297 | #### isValidDate
298 |
299 | **Signature:** `_.isValidDate(value:Any)`
300 |
301 | Checks whether the value is a valid date. That is, the value is both an instance
302 | of `Date` and it represents an actual date.
303 |
304 | Warning: This function does not verify
305 | whether the original input to `Date` is a real date. For instance,
306 | `new Date("02/30/2014")` is considered a valid date because `Date` coerces that
307 | into a representation of 03/02/2014. To validate strings representing a date,
308 | consider using a date/time library like [Moment.js.][momentjs]
309 |
310 | ```javascript
311 | _.isValidDate(new Date("January 1, 1900"));
312 | // => true
313 |
314 | _.isValidDate(new Date("The Last Great Time War"));
315 | // => false
316 | ```
317 |
318 | --------------------------------------------------------------------------------
319 |
320 | #### isZero
321 |
322 | **Signature:** `_.isZero(value:Any)`
323 |
324 | Checks whether the value is `0`.
325 |
326 | ```javascript
327 | _.isZero(0);
328 | // => true
329 |
330 | _.isZero("Pythagoras");
331 | // => false
332 | ```
333 |
334 | --------------------------------------------------------------------------------
335 |
336 | [momentjs]:http://momentjs.com/
--------------------------------------------------------------------------------
/docs/_.object.builders.js.md:
--------------------------------------------------------------------------------
1 | ### object.builders
2 |
3 | > Functions to build objects.
4 |
5 | --------------------------------------------------------------------------------
6 |
7 | #### frequencies
8 |
9 | **Signature:** `_.frequencies(arr:Array)`
10 |
11 | Returns an object whose property keys are the values of `arr`'s elements. The
12 | property values are a count of how many times that value appeared in `arr`.
13 |
14 | ```javascript
15 | var citations = ["Plato", "Aristotle", "Plotinus", "Plato"];
16 |
17 | _.frequencies(citations);
18 | // => { Plato: 2, Aristotle: 1, Plotinus: 1 }
19 | ```
20 |
21 | --------------------------------------------------------------------------------
22 |
23 | #### merge
24 |
25 | **Signature:** `_.merge(obj1:Object[, obj:Object...])`
26 |
27 | Merges two or more objects starting with the left-most and applying the keys
28 | rightward.
29 |
30 | ```javascript
31 | _.merge({ a: "alpha" }, { b: "beta" });
32 | // => { a: "alpha", b: "beta" }
33 | ```
34 |
35 | --------------------------------------------------------------------------------
36 |
37 | #### renameKeys
38 |
39 | **Signature:** `_.renameKeys(obj:Object, keyMap:Object)`
40 |
41 | Takes an object (`obj`) and a map of keys (`keyMap`) and returns a new object
42 | where the keys of `obj` have been renamed as specified in `keyMap`.
43 |
44 | ```javascript
45 | _.renameKeys({ a: 1, b: 2 }, { a: "alpha", b: "beta" });
46 | // => { alpha: 1, beta: 2 }
47 | ```
48 |
49 | --------------------------------------------------------------------------------
50 |
51 | #### setPath
52 |
53 | **Signature:** `_.setPath(obj:Object, value:Any, ks:Array, defaultValue:Any)`
54 |
55 | Sets the value of a property at any depth in `obj` based on the path described
56 | by the `ks` array. If any of the properties in the `ks` path don't exist, they
57 | will be created with `defaultValue`.
58 |
59 | See `_.updatePath` about `obj` not being mutated in the process by cloning it.
60 |
61 | ```javascript
62 | _.setPath({}, "Plotinus", ["Platonism", "Neoplatonism"], {});
63 | // => { Platonism: { Neoplatonism: "Plotinus" } }
64 | ```
65 |
66 | --------------------------------------------------------------------------------
67 |
68 | #### snapshot
69 |
70 | **Signature:** `_.snapshot(obj:Object)`
71 |
72 | Snapshots/clones an object deeply.
73 |
74 | ```javascript
75 | var schools = { plato: "Academy", aristotle: "Lyceum" };
76 |
77 | _.snapshot(schools);
78 | // => { plato: "Academy", aristotle: "Lyceum" }
79 |
80 | schools === _.snapshot(schools);
81 | // => false
82 | ```
83 |
84 | --------------------------------------------------------------------------------
85 |
86 | **Signature:** `_.updatePath(obj:Object, fun:Function, ks:Array, defaultValue:Any)`
87 |
88 | Updates the value at any depth in a nested object based on the path described by
89 | the `ks` array. The function `fun` is called with the current value and is
90 | expected to return a replacement value. If no keys are provided, then the
91 | object itself is presented to `fun`. If a property in the path is missing, then
92 | it will be created with `defaultValue`.
93 |
94 | Note that the original object will *not* be mutated. Instead, `obj` will
95 | be cloned deeply.
96 |
97 | ```javascript
98 | var imperialize = function (val) {
99 | if (val == "Republic") return "Empire";
100 | else return val;
101 | };
102 |
103 | _.updatePath({ rome: "Republic" }, imperialize, ["rome"]);
104 | // => { rome: "Empire" }
105 |
106 | var obj = { earth: { rome: "Republic" } };
107 | var imperialObj = _.updatePath(obj, imperialize, ["earth", "rome"]);
108 |
109 | imperialObj;
110 | // => { earth: { rome: "Empire" }}
111 |
112 | obj;
113 | // => { earth: { rome: "Republic" }}
114 |
115 | obj === imperialObj;
116 | // => false
117 | ```
118 |
--------------------------------------------------------------------------------
/docs/_.object.selectors.js.md:
--------------------------------------------------------------------------------
1 | ### object.selectors
2 |
3 | > Functions to select values from an object.
4 |
5 | --------------------------------------------------------------------------------
6 |
7 | #### accessor
8 |
9 | **Signature:** `_.accessor(field:String)`
10 |
11 | Returns a function that will attempt to look up a named field in any object
12 | that it is given.
13 |
14 | ```javascript
15 | var getName = _.accessor('name');
16 |
17 | getName({ name: 'Seneca' });
18 | // => 'Seneca'
19 | ```
20 |
21 | --------------------------------------------------------------------------------
22 |
23 | #### dictionary
24 |
25 | **Signature:** `_.dictionary(obj:Object)`
26 |
27 | Given an object, returns a function that will attempt to look up a field that
28 | it is given.
29 |
30 | ```javascript
31 | var generals = {
32 | rome: "Scipio",
33 | carthage: "Hannibal"
34 | };
35 |
36 | var getGeneralOf = _.dictionary(generals);
37 |
38 | getGeneralOf("rome");
39 | // => "Scipio"
40 | ```
41 |
42 | --------------------------------------------------------------------------------
43 |
44 | #### getPath
45 |
46 | **Signature:** `_.getPath(obj:Object, ks:String|Array)`
47 |
48 | Gets the value at any depth in a nested object based on the path described by
49 | the keys given. Keys may be given as an array or as a dot-separated string.
50 | Returns `undefined` if the path cannot be reached.
51 |
52 | ```javascript
53 | var countries = {
54 | greece: {
55 | athens: {
56 | playwright: "Sophocles"
57 | }
58 | }
59 | }
60 | };
61 |
62 | _.getPath(countries, "greece.athens.playwright");
63 | // => "Sophocles"
64 |
65 | _.getPath(countries, "greece.sparta.playwright");
66 | // => undefined
67 |
68 | _.getPath(countries, ["greece", "athens", "playwright"]);
69 | // => "Sophocles"
70 |
71 | _.getPath(countries, ["greece", "sparta", "playwright"]);
72 | // => undefined
73 | ```
74 |
75 | --------------------------------------------------------------------------------
76 |
77 | #### hasPath
78 |
79 | **Signature:** `_.hasPath(obj:Object, ks:String|Array)`
80 |
81 | Returns a boolean indicating whether there is a property at the path described
82 | by the keys given. Keys may be given as an array or as a dot-separated string.
83 |
84 | ```javascript
85 | var countries = {
86 | greece: {
87 | athens: {
88 | playwright: "Sophocles"
89 | }
90 | }
91 | }
92 | };
93 |
94 | _.hasPath(countries, "greece.athens.playwright");
95 | // => true
96 |
97 | _.hasPath(countries, "greece.sparta.playwright");
98 | // => false
99 |
100 | _.hasPath(countries, ["greece", "athens", "playwright"]);
101 | // => true
102 |
103 | _.hasPath(countries, ["greece", "sparta", "playwright"]);
104 | // => false
105 | ```
106 |
107 | --------------------------------------------------------------------------------
108 |
109 | #### kv
110 |
111 | **Signature:** `_.kv(obj:Object, key:String)`
112 |
113 | Returns the key/value pair for a given property in an object, undefined if not found.
114 |
115 | ```javascript
116 | var playAuthor = {
117 | "Medea": "Aeschylus"
118 | };
119 |
120 | _.kv(playAuthor, "Medea");
121 | // => ["Medea", "Aeschylus"]
122 |
123 | _.kv(playAuthor, "Hamlet");
124 | // => undefined
125 | ```
126 |
127 | --------------------------------------------------------------------------------
128 |
129 | #### omitWhen
130 |
131 | **Signature:** `_.omitWhen(obj, pred:Function)`
132 |
133 | Returns a copy of `obj` omitting any properties that the predicate (`pred`)
134 | function returns `true` for. The predicat function is invoked with each
135 | property value, like so: `pred(propValue)`.
136 |
137 | ```javascript
138 | var playwrights = {
139 | euripedes: "Greece",
140 | shakespere: "England"
141 | };
142 |
143 | _.omitWhen(obj, function (country) { return country == "England" });
144 | // => { euripedes: "Greece" }
145 | ```
146 |
147 | --------------------------------------------------------------------------------
148 |
149 | #### pickWhen
150 |
151 | **Signature:** `_.pickWhen(obj:Object, pred:Function)`
152 |
153 | Returns a copy of `obj` containing only properties that the predicate (`pred`)
154 | function returns `true` for. The predicate function is invoked with each
155 | property value, like so: `pred(propValue)`.
156 |
157 | ```javascript
158 | var playwrights = {
159 | euripedes: "Greece",
160 | shakespere: "England"
161 | };
162 |
163 | _.pickWhen(obj, function (country) { return country == "England" });
164 | // => { shakespeare: "England" }
165 | ```
166 |
167 | --------------------------------------------------------------------------------
168 |
169 | #### selectKeys
170 |
171 | **Signature:** `_.selectKeys(obj:Object, ks:Array);
172 |
173 | Returns a copy of `obj` containing only the properties listed in the `ks` array.
174 |
175 | ```javascript
176 | var philosopherCities = {
177 | Philo: "Alexandria",
178 | Plato: "Athens",
179 | Plotinus: "Rome"
180 | }
181 |
182 | _.selectKeys(philosopherCities, ["Plato", "Plotinus"]);
183 | // => { Plato: "Athens", Plotinus: "Rome" }
184 | ```
185 |
--------------------------------------------------------------------------------
/docs/_.util.existential.js.md:
--------------------------------------------------------------------------------
1 | ### util.existential
2 |
3 | > Functions which deal with whether a value "exists."
4 |
5 | --------------------------------------------------------------------------------
6 |
7 | #### exists
8 |
9 | **Signature:** `_.exists(value:Any)`
10 |
11 | Checks whether or not the value is "existy." Both `null` and `undefined` are
12 | considered non-existy values. All other values are existy.
13 |
14 | ```javascript
15 | _.exists(null);
16 | // => false
17 |
18 | _.exists(undefined);
19 | // => false
20 |
21 | _.exists({});
22 | // = > true
23 |
24 | _.exists("Sparta");
25 | // => true
26 | ```
27 |
28 | --------------------------------------------------------------------------------
29 |
30 | #### falsey
31 |
32 | **Signature:** `_.falsey(value:Any)`
33 |
34 | Checks whether the value is falsey. A falsey value is one which coerces to
35 | `false` in a boolean context.
36 |
37 | ```javascript
38 | _.falsey(0);
39 | // => true
40 |
41 | _.falsey("");
42 | // => true
43 |
44 | _.falsey({});
45 | // => false
46 |
47 | _.falsey("Corinth");
48 | // => false
49 | ```
50 |
51 | --------------------------------------------------------------------------------
52 |
53 | #### firstExisting
54 |
55 | **Signature:** `_.firstExisting(value:Any[, value:Any...])`
56 |
57 | Returns the first existy argument from the argument list.
58 |
59 | ```javascript
60 | _.firstExisting("Socrates", "Plato");
61 | // => "Socrates"
62 |
63 | _.firstExisting(null, undefined, "Heraclitus");
64 | // => "Heraclitus"
65 | ```
66 |
67 | --------------------------------------------------------------------------------
68 |
69 | #### not
70 |
71 | **Signature:** `_.not(value:Any)`
72 |
73 | Returns a boolean which is the opposite of the truthiness of the original value.
74 |
75 | ```javascript
76 | _.not(0);
77 | // => true
78 |
79 | _.not(1);
80 | // => false
81 |
82 | _.not(true);
83 | // => false
84 |
85 | _.not(false);
86 | // => true
87 |
88 | _.not({});
89 | // => false
90 |
91 | _.not(null);
92 | // => true
93 | ```
94 |
95 | --------------------------------------------------------------------------------
96 |
97 | #### truthy
98 |
99 | **Signature:** `_.truthy(value:Any)`
100 |
101 | Checks whether the value is truthy. A truthy value is one which coerces to
102 | `true` in a boolean context.
103 |
104 | ```javascript
105 | _.truthy({});
106 | // => true
107 |
108 | _.truthy("Athens");
109 | // => true
110 |
111 | _.truthy(0);
112 | // => false
113 |
114 | _.truthy("");
115 | // => false
116 | ```
117 |
118 | --------------------------------------------------------------------------------
--------------------------------------------------------------------------------
/docs/_.util.strings.js.md:
--------------------------------------------------------------------------------
1 | ### util.strings
2 |
3 | > Functions for working with strings.
4 |
5 | --------------------------------------------------------------------------------
6 |
7 | #### camelCase
8 |
9 | **Signature:** `_.camelCase(string:String)`
10 |
11 | Converts a dash-separated string to camel case. Opposite of [toDash](#todash).
12 |
13 | ```javascript
14 | _.camelCase("ancient-greece");
15 | // => "ancientGreece"
16 | ```
17 |
18 | --------------------------------------------------------------------------------
19 |
20 | #### explode
21 |
22 | **Signature:** `_.explode(s:String)`
23 |
24 | Explodes a string into an array of characters. Opposite of [implode](#implode).
25 |
26 | ```javascript
27 | _.explode("Plato");
28 | // => ["P", "l", "a", "t", "o"]
29 | ```
30 |
31 | --------------------------------------------------------------------------------
32 |
33 | #### fromQuery
34 |
35 | **Signature:** `_.fromQuery(str:String)`
36 |
37 | Takes a URL query string and converts it into an equivalent JavaScript object.
38 | Opposite of [toQuery](#toquery)
39 |
40 | ```javascript
41 | _.fromQuery("forms%5Bperfect%5D=circle&forms%5Bimperfect%5D=square");
42 | // => { forms: { perfect: "circle", imperfect: "square" } }
43 | ```
44 |
45 | --------------------------------------------------------------------------------
46 |
47 | #### implode
48 |
49 | **Signature:** `_.implode(a:Array)`
50 |
51 | Implodes an array of strings into a single string. Opposite of [explode](#explode).
52 |
53 | ```javascript
54 | _.implode(["H", "o", "m", "e", "r"]);
55 | // => "Homer"
56 | ```
57 |
58 | --------------------------------------------------------------------------------
59 |
60 | #### slugify
61 |
62 | **Signature:** `_.slugify(str:String)`
63 |
64 | Slugifies a string, converting spaces and dots to dashes and inserting dashes between words.
65 |
66 | ```javascript
67 | _.slugify("ExampleString.that-covers-it.all");
68 | // => "example-string-that-covers-it-all"
69 | ```
70 |
71 | --------------------------------------------------------------------------------
72 |
73 | #### strContains
74 |
75 | **Signature:** `_.strContains(str:String, search:String)`
76 |
77 | Reports whether a string contains a search string.
78 |
79 | ```javascript
80 | _.strContains("Acropolis", "polis");
81 | // => true
82 | ```
83 |
84 | --------------------------------------------------------------------------------
85 |
86 | #### toDash
87 |
88 | **Signature:** `_.toDash(string:String)`
89 |
90 | Converts a camel case string to a dashed string. Opposite of [camelCase](#camelcase).
91 |
92 | ```javascript
93 | _.toDash("thisIsSparta");
94 | // => "this-is-sparta"
95 | ```
96 |
97 | --------------------------------------------------------------------------------
98 |
99 | #### toQuery
100 |
101 | **Signature:** `_.toQuery(obj:Object)`
102 |
103 | Takes an object and converts it into an equivalent URL query string. Opposite
104 | of [fromQuery](#fromquery).
105 |
106 | ```javascript
107 | _.toQuery({ forms: { perfect: "circle", imperfect: "square" } });
108 | // => "forms%5Bperfect%5D=circle&forms%5Bimperfect%5D=square"
109 | ```
110 |
111 | --------------------------------------------------------------------------------
112 |
--------------------------------------------------------------------------------
/docs/_.util.trampolines.js.md:
--------------------------------------------------------------------------------
1 | ### util.trampolines
2 |
3 | > Trampoline functions.
4 |
5 | --------------------------------------------------------------------------------
6 |
7 | #### done
8 |
9 | **Signature:** `_.done(value:Any)`
10 |
11 | A utility for wrapping a function's return values so they can be used by
12 | `_.trampoline`. [See below](#trampoline).
13 |
14 | --------------------------------------------------------------------------------
15 |
16 | #### trampoline
17 |
18 | **Signature:** `_.trampoline(fun:Function[, args:Any...])`
19 |
20 | Provides a way of creating recursive functions that won't exceed a JavaScript
21 | engine's maximum recursion depth. Rather than writing a naive recursive
22 | function, the function's base cases must return `_.done(someValue)`, and
23 | recursive calls must be wrapped in a returned function.
24 |
25 | In order to create a trampolined function that can be used in the same way as
26 | a naive recursive function, use `_.partial` as illustrated below.
27 |
28 | ```javascript
29 | function isEvenNaive (num) {
30 | if (num === 0) return true;
31 | if (num === 1) return false;
32 | return isEvenNaive(num - 2);
33 | }
34 |
35 | isEvenNaive(99999);
36 | // => InternalError: too much recursion
37 |
38 | function isEvenInner (num) {
39 | if (num === 0) return _.done(true);
40 | if (num === 1) return _.done(false);
41 | return function () { return isEvenInner(Math.abs(num) - 2); };
42 | }
43 |
44 | _.trampoline(isEvenInner, 99999);
45 | // => false
46 |
47 | var isEven = _.partial(_.trampoline, isEvenInner);
48 |
49 | isEven(99999);
50 | // => false
51 | ```
52 |
53 | --------------------------------------------------------------------------------
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # lodash-contrib
2 |
3 | > The brass buckles on lodash's utility belt - a contributors' library for [lodash](http://lodash.com/).
4 |
5 | ## Introduction
6 |
7 | ### Places
8 |
9 | * [Documentation](#sub-libraries)
10 | * [Source repository](https://github.com/empeeric/lodash-contrib)
11 | * [Tickets and bug reports](https://github.com/empeeric/lodash-contrib/issues?state=open)
12 |
13 | ### Why lodash-contrib?
14 |
15 | While lodash provides a bevy of useful tools to support functional programming in JavaScript, it can't
16 | (and shouldn't) be everything to everyone. lodash-contrib is intended as a home for functions that, for
17 | various reasons, don't belong in lodash proper. In particular, it aims to be:
18 |
19 | * a home for functions that are limited in scope, but solve certain point problems, and
20 | * a proving ground for features that belong in lodash proper, but need some advocacy and/or evolution
21 | (or devolution) to get them there.
22 |
23 | ### Use
24 |
25 | #### In the Browser
26 |
27 | First, you’ll need lodash. Then you can grab the relevant lodash-contrib libraries and simply add
28 | the following to your pages:
29 |
30 | ```html
31 |
32 |
33 | ```
34 |
35 | At the moment there are no cross-contrib dependencies (i.e. each sub-library
36 | can stand by itself), but that may change in the future.
37 |
38 | ### License
39 |
40 | lodash-contrib is open sourced under the [MIT license](https://github.com/Empeeric/lodash-contrib/blob/master/LICENSE).
41 |
42 | ## Sub-libraries
43 |
44 | The lodash-contrib library currently contains a number of related capabilities, aggregated into the following files.
45 |
46 | - [_.array.builders](_.array.builders.js.md#arraybuilders) - functions to build arrays
47 | - [_.array.selectors](_.array.selectors.js.md#arrayselectors) - functions to take things from arrays
48 | - [_.collections.walk](_.collections.walk.js.md#collectionswalk) - functions to walk and transform nested JavaScript objects
49 | - [_.function.arity](_.function.arity.js.md#functionarity) - functions to manipulate and fix function argument arity
50 | - [_.function.combinators](_.function.combinators.js.md#functioncombinators) - functions to combine functions to make new functions
51 | - [_.function.iterators](_.function.iterators.js.md#functioniterators) - functions to lazily produce, manipulate and consume sequence iterators
52 | - [_.function.predicates](_.function.predicates.js.md#functionpredicates) - functions that return `true` or `false` based on some criteria
53 | - [_.object.builders](_.object.builders.js.md#objectbuilders) - functions to build JavaScript objects
54 | - [_.object.selectors](_.object.selectors.js.md#objectselectors) - functions to pick things from JavaScript objects
55 | - [_.util.existential](_.util.existential.js.md#utilexistential) - functions that check for the existence or truthiness of JavaScript data types
56 | - [_.util.operators](_.util.operators.js.md#utiloperators) - functions that wrap common (or missing) JavaScript operators
57 | - [_.util.strings](_.util.strings.js.md#utilstrings) - functions to work with strings
58 | - [_.util.trampolines](_.util.trampolines.js.md#utiltrampolines) - functions to facilitate calling functions recursively without blowing the stack
59 |
60 | The links above are to the annotated source code. Full-blown _.contrib documentation is in the works. Contributors welcomed.
61 |
62 |
63 |
--------------------------------------------------------------------------------
/examples/builders-examples.js.md:
--------------------------------------------------------------------------------
1 | Examples for builder functions
2 | ===================
3 |
4 | `_.object.builders.js` contains functions that are useful when small changes to JavaScript
5 | objects are needed.
6 |
7 | Each section gives use cases showing how a given function could be used:
8 |
9 | * [_.merge](#_merge-objs-)
10 | * [_.renameKeys](#_renamekeysobj-kobj)
11 | * [_.snapshot](#_snapshotobj)
12 | * [_.updatePath](#_updatepathobj-fun-ks-defaultvalue)
13 | * [_.setPath](#_setpathobj-value-ks-defaultvalue)
14 | * [_.frequencies](#_frequenciesobj)
15 |
16 | For some more insights have a look at [the tests](https://github.com/node4good/lodash-contrib/blob/master/test/object.builders.js).
17 |
18 | _.merge(/* objs */)
19 | -------------------
20 |
21 | Merges two or more objects starting with the left-most and applying the keys right-word.
22 |
23 | **Arguments**
24 |
25 | 1. an arbitrary number of parameters consisting of JavaScript objects, all of which remain unchanged.
26 |
27 | **Returns**
28 |
29 | (Object): the merged object.
30 |
31 | **Example**
32 |
33 | ```javascript
34 | var merged = _.merge({a: 1, b: 1}, {a:2, c: 2}, {a: 3});
35 | merged.a; // → 3
36 | merged.b; // → 1
37 | merged.c; // → 2
38 | ```
39 |
40 | _.renameKeys(obj, kobj)
41 | -----------------------
42 |
43 | Takes an object and another object of strings to strings where the second object describes
44 | the key renaming to occur in the first object.
45 |
46 | **Arguments**
47 |
48 | 1. `obj` that will be used as starting point, it remains unchanged.
49 | 2. `kobj` string to string mapping defining the renaming.
50 |
51 | **Returns**
52 |
53 | (Object): `obj` with renamed keys.
54 |
55 | **Example**
56 |
57 | ```javascript
58 | var obj = {
59 | id: 'tester@test.com',
60 | name: 'tester'
61 | };
62 |
63 | var user = _.renameKeys(obj, { 'id': 'email'});
64 | obj.id; // → 'tester@test.com'
65 | user.id; // → undefined
66 | user.email; // → 'tester@test.com'
67 | ```
68 |
69 | _.snapshot(obj)
70 | ---------------
71 |
72 | Snapshots an object deeply. Based on the [version by Keith Devens](http://archive.today/FVuq2).
73 |
74 | **Arguments**
75 |
76 | 1. `obj` that will be snapshotted.
77 |
78 | **Returns**
79 |
80 | (Object): copy of `obj`.
81 |
82 | **Example**
83 |
84 | ```javascript
85 | var coordsFirstTick = {x: 1, y: 1};
86 | var coordsSecondTick = _.snapshot(coordsFirstTick);
87 | coordsSecondTick.x = 2;
88 | console.log(coordsFirstTick.x); // → 1
89 | ```
90 |
91 | _.updatePath(obj, fun, ks, defaultValue)
92 | ----------------------------------------
93 |
94 | Updates the value at any depth in a nested object based on the path described by
95 | the keys given. The function provided is supplied the current value and is expected
96 | to return a value for use as the new value. If no keys are provided, then the object
97 | itself is presented to the given function.
98 |
99 | **Arguments**
100 |
101 | 1. `obj` that will be updated.
102 | 2. `fun` that will be applied on the last element of `ks`.
103 | 3. `ks` as a list of steps specifying the path to the attribute that will be updated.
104 | 4. `defaultValue` that will be used if the key does not exist in `obj`.
105 |
106 | **Returns**
107 |
108 | (Objct): `obj` with updated keys.
109 |
110 | **Example**
111 |
112 | ```javascript
113 | // we could either give a path to a value in a nested array
114 | var nested = [0, 1, [3, 3], 4];
115 | _.updatePath(nested, _.always(2), [2, 0]); // → [0, 1, [2, 3], 4]
116 | nested; // → [0, 1, [3, 3], 4]
117 |
118 | // ...or we could pass an array of keys to traverse
119 | var nested = {
120 | one: {
121 | two: {
122 | three: 4
123 | }
124 | }
125 | };
126 | _.updatePath(nested, _.always(3), ['one', 'two', 'three']).one.two.three; // → 3
127 | nested.one.two.three; // → 4
128 | ```
129 |
130 | _.setPath(obj, value, ks, defaultValue)
131 | ----------------------------------------
132 |
133 | Sets the value at any depth in a nested object based on the path described by the keys given.
134 |
135 | See [_.updatePath](#_updatepathobj-fun-ks-defaultvalue), this is just syntactic sugar that allows you to pass a value as second
136 | parameter, without having to wrap it into a function by yourself.
137 |
138 |
139 | _.frequencies(obj)
140 | ------------------------------
141 |
142 | Returns an object where each element of an array is keyed to the number of times that
143 | it occurred in said array.
144 |
145 | **Arguments**
146 |
147 | 1. `obj` as array or object, as long as the object is not nested the expected object
148 | gets returned.
149 |
150 | **Returns**
151 |
152 | (Object): with the values as keys and the occurrencies of these as values.
153 |
154 | **Example**
155 |
156 | ```javascript
157 | _.frequencies([0, 2, 2, 7, 7, 7]); // → { 0: 1, 2: 2, 7: 3}
158 | _.frequencies({a: 1, b: 1, c: 2, d: 1}) // → {1: 3, 2: 1}
159 | ```
160 |
--------------------------------------------------------------------------------
/examples/walk-examples.js.md:
--------------------------------------------------------------------------------
1 | Examples for _.walk
2 | ===================
3 |
4 | The _.walk module (lodash.collections.walk.js) provides implementations of
5 | the [lodash collection functions](http://lodashjs.org/#collections)
6 | that are specialized for operating on nested JavaScript objects that form
7 | trees.
8 |
9 | Basic Traversal
10 | ---------------
11 |
12 | The most basic operation on a tree is to iterate through all its nodes, which
13 | is provided by `_.walk.preorder` and `_.walk.postorder`. They can be used in
14 | much the same way as [lodash's 'each' function][each]. For example, take
15 | a simple tree:
16 |
17 | [each]: http://lodashjs.org/#each
18 |
19 | var tree = {
20 | 'name': { 'first': 'Bucky', 'last': 'Fuller' },
21 | 'occupations': ['designer', 'inventor']
22 | };
23 |
24 | We can do a preorder traversal of the tree:
25 |
26 | _.walk.preorder(tree, function(value, key, parent) {
27 | console.log(key + ': ' + value);
28 | });
29 |
30 | which produces the following output:
31 |
32 | undefined: [object Object]
33 | name: [object Object]
34 | first: Bucky
35 | last: Fuller
36 | occupations: designer,inventor
37 | 0: designer
38 | 1: inventor
39 |
40 | A preorder traversal visits the nodes in the tree in a top-down fashion: first
41 | the root node is visited, then all of its child nodes are recursively visited.
42 | `_.walk.postorder` does the opposite, calling the visitor function for a node
43 | only after visiting all of its child nodes.
44 |
45 | Collection Functions
46 | --------------------
47 |
48 | The \_.walk module provides versions of most of the
49 | [lodash collection functions](http://lodashjs.org/#collections), with
50 | some small differences that make them better suited for operating on trees. For
51 | example, you can use `_.walk.filter` to get a list of all the strings in a tree:
52 |
53 | _.walk.filter(tree, _.walk.preorder, _.isString);
54 |
55 | Like many other functions in _.walk, the argument to `filter` is a function
56 | indicating in what order the nodes should be visited. Currently, only
57 | `preorder` and `postorder` are supported.
58 |
59 | Custom Walkers
60 | --------------
61 |
62 | Sometimes, you have a tree structure that can't be naively traversed. A good
63 | example of this is a DOM tree: because each element has a reference to its
64 | parent, a naive walk would encounter circular references. To handle such cases,
65 | you can create a custom walker by invoking `_.walk` as a function, and passing
66 | it a function which returns the descendants of a given node. E.g.:
67 |
68 | var domWalker = _.walk(function(el) {
69 | return el.children;
70 | });
71 |
72 | The resulting object has the same functions as `_.walk`, but parameterized
73 | to use the custom walking behavior:
74 |
75 | var buttons = domWalker.filter(_.walk.preorder, function(el) {
76 | return el.tagName === 'BUTTON';
77 | });
78 |
79 | However, it's not actually necessary to create custom walkers for DOM nodes --
80 | _.walk handles DOM nodes specially by default.
81 |
82 | Parse Trees
83 | -----------
84 |
85 | A _parse tree_ is tree that represents the syntactic structure of a formal
86 | language. For example, the arithmetic expression `1 + (4 + 2) * 7` might have the
87 | following parse tree:
88 |
89 | var tree = {
90 | 'type': 'Addition',
91 | 'left': { 'type': 'Value', 'value': 1 },
92 | 'right': {
93 | 'type': 'Multiplication',
94 | 'left': {
95 | 'type': 'Addition',
96 | 'left': { 'type': 'Value', 'value': 4 },
97 | 'right': { 'type': 'Value', 'value': 2 }
98 | },
99 | 'right': { 'type': 'Value', 'value': 7 }
100 | }
101 | };
102 |
103 | We can create a custom walker for this parse tree:
104 |
105 | var parseTreeWalker = _.walk(function(node) {
106 | return _.pick(node, 'left', 'right');
107 | });
108 |
109 | Using the `find` function, we could find the first occurrence of the addition
110 | operator. It uses a pre-order traversal of the tree, so the following code
111 | will produce the root node (`tree`):
112 |
113 | parseTreeWalker.find(tree, function(node) {
114 | return node.type === 'Addition';
115 | });
116 |
117 | We could use the `reduce` function to evaluate the arithmetic expression
118 | represented by the tree. The following code will produce `43`:
119 |
120 | parseTreeWalker.reduce(tree, function(memo, node) {
121 | if (node.type === 'Value') return node.value;
122 | if (node.type === 'Addition') return memo.left + memo.right;
123 | if (node.type === 'Multiplication') return memo.left * memo.right;
124 | });
125 |
126 | When the visitor function is called on a node, the `memo` argument contains
127 | the results of calling `reduce` on each of the node's subtrees. To evaluate a
128 | node, we just need to add or multiply the results of the left and right
129 | subtrees of the node.
130 |
--------------------------------------------------------------------------------
/gh-pages/_.collections.walk.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | _.collections.walk.js.md
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Jump To …
17 | +
18 |
19 |
94 |
95 |
96 |
97 |
136 |
137 |
138 |
139 |
--------------------------------------------------------------------------------
/gh-pages/_.util.strings.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | _.util.strings.js.md
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Jump To …
17 | +
18 |
19 |
94 |
95 |
96 |
97 |
182 |
183 |
184 |
185 |
--------------------------------------------------------------------------------
/gh-pages/_.util.trampolines.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | _.util.trampolines.js.md
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Jump To …
17 | +
18 |
19 |
94 |
95 |
96 |
97 |
197 |
198 |
199 |
200 |
--------------------------------------------------------------------------------
/gh-pages/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | lodash-contrib
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Jump To …
17 | +
18 |
19 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
107 |
lodash-contrib
108 |
109 | The brass buckles on lodash’s utility belt - a contributors’ library for lodash .
110 |
111 |
Introduction
112 |
Places
113 |
118 |
Why lodash-contrib?
119 |
While lodash provides a bevy of useful tools to support functional programming in JavaScript, it can’t
120 | (and shouldn’t) be everything to everyone. lodash-contrib is intended as a home for functions that, for
121 | various reasons, don’t belong in lodash proper. In particular, it aims to be:
122 |
123 | a home for functions that are limited in scope, but solve certain point problems, and
124 | a proving ground for features that belong in lodash proper, but need some advocacy and/or evolution
125 | (or devolution) to get them there.
126 |
127 |
Use
128 |
In the Browser
129 |
First, you’ll need lodash. Then you can grab the relevant lodash-contrib libraries and simply add
130 | the following to your pages:
131 |
<script type ="text/javascript" src ="lodash.js" > </script >
132 | <script type ="text/javascript" src ="lodash.object.builders.js" > </script >
133 |
134 |
At the moment there are no cross-contrib dependencies (i.e. each sub-library
135 | can stand by itself), but that may change in the future.
136 |
License
137 |
lodash-contrib is open sourced under the MIT license .
138 |
Sub-libraries
139 |
The lodash-contrib library currently contains a number of related capabilities, aggregated into the following files.
140 |
155 |
The links above are to the annotated source code. Full-blown _.contrib documentation is in the works. Contributors welcomed.
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
--------------------------------------------------------------------------------
/gh-pages/public/fonts/aller-bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/node4good/lodash-contrib/91dded5d52f6dca50a4c74782740b02478c2c548/gh-pages/public/fonts/aller-bold.eot
--------------------------------------------------------------------------------
/gh-pages/public/fonts/aller-bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/node4good/lodash-contrib/91dded5d52f6dca50a4c74782740b02478c2c548/gh-pages/public/fonts/aller-bold.ttf
--------------------------------------------------------------------------------
/gh-pages/public/fonts/aller-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/node4good/lodash-contrib/91dded5d52f6dca50a4c74782740b02478c2c548/gh-pages/public/fonts/aller-bold.woff
--------------------------------------------------------------------------------
/gh-pages/public/fonts/aller-light.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/node4good/lodash-contrib/91dded5d52f6dca50a4c74782740b02478c2c548/gh-pages/public/fonts/aller-light.eot
--------------------------------------------------------------------------------
/gh-pages/public/fonts/aller-light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/node4good/lodash-contrib/91dded5d52f6dca50a4c74782740b02478c2c548/gh-pages/public/fonts/aller-light.ttf
--------------------------------------------------------------------------------
/gh-pages/public/fonts/aller-light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/node4good/lodash-contrib/91dded5d52f6dca50a4c74782740b02478c2c548/gh-pages/public/fonts/aller-light.woff
--------------------------------------------------------------------------------
/gh-pages/public/fonts/novecento-bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/node4good/lodash-contrib/91dded5d52f6dca50a4c74782740b02478c2c548/gh-pages/public/fonts/novecento-bold.eot
--------------------------------------------------------------------------------
/gh-pages/public/fonts/novecento-bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/node4good/lodash-contrib/91dded5d52f6dca50a4c74782740b02478c2c548/gh-pages/public/fonts/novecento-bold.ttf
--------------------------------------------------------------------------------
/gh-pages/public/fonts/novecento-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/node4good/lodash-contrib/91dded5d52f6dca50a4c74782740b02478c2c548/gh-pages/public/fonts/novecento-bold.woff
--------------------------------------------------------------------------------
/gh-pages/public/stylesheets/normalize.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v2.0.1 | MIT License | git.io/normalize */
2 |
3 | /* ==========================================================================
4 | HTML5 display definitions
5 | ========================================================================== */
6 |
7 | /*
8 | * Corrects `block` display not defined in IE 8/9.
9 | */
10 |
11 | article,
12 | aside,
13 | details,
14 | figcaption,
15 | figure,
16 | footer,
17 | header,
18 | hgroup,
19 | nav,
20 | section,
21 | summary {
22 | display: block;
23 | }
24 |
25 | /*
26 | * Corrects `inline-block` display not defined in IE 8/9.
27 | */
28 |
29 | audio,
30 | canvas,
31 | video {
32 | display: inline-block;
33 | }
34 |
35 | /*
36 | * Prevents modern browsers from displaying `audio` without controls.
37 | * Remove excess height in iOS 5 devices.
38 | */
39 |
40 | audio:not([controls]) {
41 | display: none;
42 | height: 0;
43 | }
44 |
45 | /*
46 | * Addresses styling for `hidden` attribute not present in IE 8/9.
47 | */
48 |
49 | [hidden] {
50 | display: none;
51 | }
52 |
53 | /* ==========================================================================
54 | Base
55 | ========================================================================== */
56 |
57 | /*
58 | * 1. Sets default font family to sans-serif.
59 | * 2. Prevents iOS text size adjust after orientation change, without disabling
60 | * user zoom.
61 | */
62 |
63 | html {
64 | font-family: sans-serif; /* 1 */
65 | -webkit-text-size-adjust: 100%; /* 2 */
66 | -ms-text-size-adjust: 100%; /* 2 */
67 | }
68 |
69 | /*
70 | * Removes default margin.
71 | */
72 |
73 | body {
74 | margin: 0;
75 | }
76 |
77 | /* ==========================================================================
78 | Links
79 | ========================================================================== */
80 |
81 | /*
82 | * Addresses `outline` inconsistency between Chrome and other browsers.
83 | */
84 |
85 | a:focus {
86 | outline: thin dotted;
87 | }
88 |
89 | /*
90 | * Improves readability when focused and also mouse hovered in all browsers.
91 | */
92 |
93 | a:active,
94 | a:hover {
95 | outline: 0;
96 | }
97 |
98 | /* ==========================================================================
99 | Typography
100 | ========================================================================== */
101 |
102 | /*
103 | * Addresses `h1` font sizes within `section` and `article` in Firefox 4+,
104 | * Safari 5, and Chrome.
105 | */
106 |
107 | h1 {
108 | font-size: 2em;
109 | }
110 |
111 | /*
112 | * Addresses styling not present in IE 8/9, Safari 5, and Chrome.
113 | */
114 |
115 | abbr[title] {
116 | border-bottom: 1px dotted;
117 | }
118 |
119 | /*
120 | * Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
121 | */
122 |
123 | b,
124 | strong {
125 | font-weight: bold;
126 | }
127 |
128 | /*
129 | * Addresses styling not present in Safari 5 and Chrome.
130 | */
131 |
132 | dfn {
133 | font-style: italic;
134 | }
135 |
136 | /*
137 | * Addresses styling not present in IE 8/9.
138 | */
139 |
140 | mark {
141 | background: #ff0;
142 | color: #000;
143 | }
144 |
145 |
146 | /*
147 | * Corrects font family set oddly in Safari 5 and Chrome.
148 | */
149 |
150 | code,
151 | kbd,
152 | pre,
153 | samp {
154 | font-family: monospace, serif;
155 | font-size: 1em;
156 | }
157 |
158 | /*
159 | * Improves readability of pre-formatted text in all browsers.
160 | */
161 |
162 | pre {
163 | white-space: pre;
164 | white-space: pre-wrap;
165 | word-wrap: break-word;
166 | }
167 |
168 | /*
169 | * Sets consistent quote types.
170 | */
171 |
172 | q {
173 | quotes: "\201C" "\201D" "\2018" "\2019";
174 | }
175 |
176 | /*
177 | * Addresses inconsistent and variable font size in all browsers.
178 | */
179 |
180 | small {
181 | font-size: 80%;
182 | }
183 |
184 | /*
185 | * Prevents `sub` and `sup` affecting `line-height` in all browsers.
186 | */
187 |
188 | sub,
189 | sup {
190 | font-size: 75%;
191 | line-height: 0;
192 | position: relative;
193 | vertical-align: baseline;
194 | }
195 |
196 | sup {
197 | top: -0.5em;
198 | }
199 |
200 | sub {
201 | bottom: -0.25em;
202 | }
203 |
204 | /* ==========================================================================
205 | Embedded content
206 | ========================================================================== */
207 |
208 | /*
209 | * Removes border when inside `a` element in IE 8/9.
210 | */
211 |
212 | img {
213 | border: 0;
214 | }
215 |
216 | /*
217 | * Corrects overflow displayed oddly in IE 9.
218 | */
219 |
220 | svg:not(:root) {
221 | overflow: hidden;
222 | }
223 |
224 | /* ==========================================================================
225 | Figures
226 | ========================================================================== */
227 |
228 | /*
229 | * Addresses margin not present in IE 8/9 and Safari 5.
230 | */
231 |
232 | figure {
233 | margin: 0;
234 | }
235 |
236 | /* ==========================================================================
237 | Forms
238 | ========================================================================== */
239 |
240 | /*
241 | * Define consistent border, margin, and padding.
242 | */
243 |
244 | fieldset {
245 | border: 1px solid #c0c0c0;
246 | margin: 0 2px;
247 | padding: 0.35em 0.625em 0.75em;
248 | }
249 |
250 | /*
251 | * 1. Corrects color not being inherited in IE 8/9.
252 | * 2. Remove padding so people aren't caught out if they zero out fieldsets.
253 | */
254 |
255 | legend {
256 | border: 0; /* 1 */
257 | padding: 0; /* 2 */
258 | }
259 |
260 | /*
261 | * 1. Corrects font family not being inherited in all browsers.
262 | * 2. Corrects font size not being inherited in all browsers.
263 | * 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome
264 | */
265 |
266 | button,
267 | input,
268 | select,
269 | textarea {
270 | font-family: inherit; /* 1 */
271 | font-size: 100%; /* 2 */
272 | margin: 0; /* 3 */
273 | }
274 |
275 | /*
276 | * Addresses Firefox 4+ setting `line-height` on `input` using `!important` in
277 | * the UA stylesheet.
278 | */
279 |
280 | button,
281 | input {
282 | line-height: normal;
283 | }
284 |
285 | /*
286 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
287 | * and `video` controls.
288 | * 2. Corrects inability to style clickable `input` types in iOS.
289 | * 3. Improves usability and consistency of cursor style between image-type
290 | * `input` and others.
291 | */
292 |
293 | button,
294 | html input[type="button"], /* 1 */
295 | input[type="reset"],
296 | input[type="submit"] {
297 | -webkit-appearance: button; /* 2 */
298 | cursor: pointer; /* 3 */
299 | }
300 |
301 | /*
302 | * Re-set default cursor for disabled elements.
303 | */
304 |
305 | button[disabled],
306 | input[disabled] {
307 | cursor: default;
308 | }
309 |
310 | /*
311 | * 1. Addresses box sizing set to `content-box` in IE 8/9.
312 | * 2. Removes excess padding in IE 8/9.
313 | */
314 |
315 | input[type="checkbox"],
316 | input[type="radio"] {
317 | box-sizing: border-box; /* 1 */
318 | padding: 0; /* 2 */
319 | }
320 |
321 | /*
322 | * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
323 | * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
324 | * (include `-moz` to future-proof).
325 | */
326 |
327 | input[type="search"] {
328 | -webkit-appearance: textfield; /* 1 */
329 | -moz-box-sizing: content-box;
330 | -webkit-box-sizing: content-box; /* 2 */
331 | box-sizing: content-box;
332 | }
333 |
334 | /*
335 | * Removes inner padding and search cancel button in Safari 5 and Chrome
336 | * on OS X.
337 | */
338 |
339 | input[type="search"]::-webkit-search-cancel-button,
340 | input[type="search"]::-webkit-search-decoration {
341 | -webkit-appearance: none;
342 | }
343 |
344 | /*
345 | * Removes inner padding and border in Firefox 4+.
346 | */
347 |
348 | button::-moz-focus-inner,
349 | input::-moz-focus-inner {
350 | border: 0;
351 | padding: 0;
352 | }
353 |
354 | /*
355 | * 1. Removes default vertical scrollbar in IE 8/9.
356 | * 2. Improves readability and alignment in all browsers.
357 | */
358 |
359 | textarea {
360 | overflow: auto; /* 1 */
361 | vertical-align: top; /* 2 */
362 | }
363 |
364 | /* ==========================================================================
365 | Tables
366 | ========================================================================== */
367 |
368 | /*
369 | * Remove most spacing between table cells.
370 | */
371 |
372 | table {
373 | border-collapse: collapse;
374 | border-spacing: 0;
375 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lodash-contrib",
3 | "description": "The brass buckles on lodash's utility belt",
4 | "version": "4.1200.1",
5 | "main": "dist/lodash-contrib.commonjs.js",
6 | "dependencies": {
7 | "lodash": "4.12.x"
8 | },
9 | "devDependencies": {
10 | "chai": "",
11 | "grunt": "",
12 | "grunt-browserify": "",
13 | "grunt-cli": "",
14 | "grunt-contrib-concat": "",
15 | "grunt-contrib-copy": "",
16 | "grunt-contrib-jshint": "",
17 | "grunt-contrib-qunit": "",
18 | "grunt-contrib-uglify": "",
19 | "grunt-mocha-test": "",
20 | "mocha": ""
21 | },
22 | "repository": "node4good/lodash-contrib",
23 | "license": "MIT",
24 | "author": "Refael Ackermann (http://refack.com)",
25 | "contributors": [
26 | "Fogus (http://www.fogus.me)",
27 | "John-David Dalton (http://allyoucanleet.com/)"
28 | ],
29 | "scripts": {
30 | "dist": "grunt gen",
31 | "mypublish": "npm run dist && npm version patch && git push --follow-tags",
32 | "test": "grunt default"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/test/array.selectors.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 |
3 | module("lodash.array.selectors");
4 |
5 | test("second", function() {
6 | var a = [1,2,3,4,5];
7 |
8 | equal(_.second(a), 2, 'should retrieve the 2nd element in an array');
9 | deepEqual(_.second(a, 5), [2,3,4,5], 'should retrieve all but the first element in an array');
10 | deepEqual(_.map([a,_.rest(a)], _.second), [2,3], 'should be usable in _.map');
11 | });
12 |
13 | test("third", function() {
14 | var a = [1,2,3,4,5];
15 |
16 | equal(_.third(a), 3, 'should retrieve the 3rd element in an array');
17 | deepEqual(_.third(a, 5), [3,4,5], 'should retrieve all but the first and second element in an array');
18 | deepEqual(_.map([a,_.rest(a)], _.third), [3,4], 'should be usable in _.map');
19 | });
20 |
21 | test("takeWhile", function() {
22 | var isNeg = function(n) { return n < 0; };
23 |
24 | deepEqual(_.takeWhile([-2,-1,0,1,2], isNeg), [-2,-1], 'should take elements until a function goes truthy');
25 | deepEqual(_.takeWhile([1,-2,-1,0,1,2], isNeg), [], 'should take elements until a function goes truthy');
26 | });
27 |
28 | test("dropWhile", function() {
29 | var isNeg = function(n) { return n < 0; };
30 |
31 | deepEqual(_.dropWhile([-2,-1,0,1,2], isNeg), [0,1,2], 'should drop elements until a function goes truthy');
32 | deepEqual(_.dropWhile([0,1,2], isNeg), [0,1,2], 'should drop elements until a function goes truthy');
33 | deepEqual(_.dropWhile([-2,-1], isNeg), [], 'should drop elements until a function goes truthy');
34 | deepEqual(_.dropWhile([1,-2,-1,0,1,2], isNeg), [1,-2,-1,0,1,2], 'should take elements until a function goes truthy');
35 | deepEqual(_.dropWhile([], isNeg), [], 'should handle empty arrays');
36 | });
37 |
38 | test("splitWith", function() {
39 | var a = [1,2,3,4,5];
40 | var lessEq3p = function(n) { return n <= 3; };
41 | var lessEq3p$ = function(n) { return (n <= 3) ? true : null; };
42 |
43 | deepEqual(_.splitWith(a, lessEq3p), [[1,2,3], [4,5]], 'should split an array when a function goes false');
44 | deepEqual(_.splitWith(a, lessEq3p$), [[1,2,3], [4,5]], 'should split an array when a function goes false');
45 | deepEqual(_.splitWith([], lessEq3p$), [[],[]], 'should split an empty array into two empty arrays');
46 | });
47 |
48 | test("partitionBy", function() {
49 | var a = [1, 2, null, false, undefined, 3, 4];
50 |
51 | deepEqual(_.partitionBy(a, _.truthy), [[1,2], [null, false, undefined], [3,4]], 'should partition an array as a given predicate changes truth sense');
52 | });
53 |
54 | test("best", function() {
55 | var a = [1,2,3,4,5];
56 |
57 | deepEqual(_.best(a, function(x,y) { return x > y; }), 5, 'should identify the best value based on criteria');
58 | });
59 |
60 | test("keep", function() {
61 | var a = _.range(10);
62 | var eveny = function(e) { return (_.isEven(e)) ? e : undefined; };
63 |
64 | deepEqual(_.keep(a, eveny), [0,2,4,6,8], 'should keep only even numbers in a range tagged with null fails');
65 | deepEqual(_.keep(a, _.isEven), [true, false, true, false, true, false, true, false, true, false], 'should keep all existy values corresponding to a predicate over a range');
66 | });
67 |
68 | test("nth", function() {
69 | var a = ['a','b','c'];
70 | var b = [['a'],['b'],[]];
71 |
72 | equal(_.nth(a,0), 'a', 'should return the element at a given index into an array');
73 | equal(_.nth(a,100), undefined, 'should return undefined if out of bounds');
74 | deepEqual(_.map(b,function(e) { return _.nth(e,0); }), ['a','b',undefined], 'should be usable in _.map');
75 | });
76 |
77 | test("nths", function() {
78 | var a = ['a','b','c', 'd'];
79 |
80 | deepEqual(_.nths(a,1), ['b'], 'should return the element at a given index into an array');
81 | deepEqual(_.nths(a,1,3), ['b', 'd'], 'should return the elements at given indices into an array');
82 | deepEqual(_.nths(a,1,5,3), ['b', undefined, 'd'], 'should return undefined if out of bounds');
83 |
84 | deepEqual(_.nths(a,[1]), ['b'], 'should return the element at a given index into an array');
85 | deepEqual(_.nths(a,[1,3]), ['b', 'd'], 'should return the elements at given indices into an array');
86 | deepEqual(_.nths(a,[1,5,3]), ['b', undefined, 'd'], 'should return undefined if out of bounds');
87 | });
88 |
89 | test("valuesAt", function() {
90 | equal(_.valuesAt, _.nths, 'valuesAt should be alias for nths');
91 | });
92 |
93 | test("binPick", function() {
94 | var a = ['a','b','c', 'd'];
95 |
96 | deepEqual(_.binPick(a, false, true), ['b'], 'should return the element at a given index into an array');
97 | deepEqual(_.binPick(a, false, true, false, true), ['b', 'd'], 'should return the elements at given indices into an array');
98 | deepEqual(_.binPick(a, false, true, false, true, true), ['b', 'd', undefined], 'should return undefined if out of bounds');
99 |
100 | deepEqual(_.binPick(a, [false, true]), ['b'], 'should return the element at a given index into an array');
101 | deepEqual(_.binPick(a, [false, true, false, true]), ['b', 'd'], 'should return the elements at given indices into an array');
102 | deepEqual(_.binPick(a, [false, true, false, true, true]), ['b', 'd', undefined], 'should return undefined if out of bounds');
103 | });
104 | });
105 |
106 |
--------------------------------------------------------------------------------
/test/browserified.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | lodash-contrib Test Suite
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/test/dist-min.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | lodash-contrib Test Suite
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | lodash-contrib Test Suite
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/test/mocha/function.arity.js:
--------------------------------------------------------------------------------
1 | var _ = require('../..');
2 | var assert = require('assert');
3 |
4 | describe("lodash.function.arity", function () {
5 | describe("fix", function() {
6 | var over = function(t, m, b) { return t / m / b; };
7 | var t = _.fix(over, 10, _, _);
8 | assert.equal(t(5, 2), 1, 'should return a function partially applied for some number of arbitrary args marked by _');
9 | assert.equal(t(10, 2), 0.5, 'should return a function partially applied for some number of arbitrary args marked by _');
10 | assert.equal(t(10, 5), 0.2, 'should return a function partially applied for some number of arbitrary args marked by _');
11 |
12 | var f = function () {
13 | return _.map(arguments, function (arg) {
14 | return typeof arg;
15 | }).join(', ');
16 | };
17 | var g = _.fix(f, _, _, 3);
18 | assert.equal(g(1), 'number, undefined, number', 'should fill "undefined" if argument not given');
19 | g(1, 2);
20 | assert.equal(g(1), 'number, undefined, number', 'should not remember arguments between calls');
21 |
22 | assert.equal(_.fix(parseInt, _, 10)('11'), 11, 'should "fix" common js foibles');
23 |
24 | assert.equal(_.fix(f, _, 3)(1,'a'), 'number, number', 'should ignore extra parameters');
25 |
26 | });
27 |
28 | describe("arity", function () {
29 | function variadic () { return arguments.length; }
30 | function unvariadic (a, b, c) { return arguments.length; }
31 |
32 | assert.equal( _.arity(unvariadic.length, variadic).length, unvariadic.length, "should set the length");
33 | assert.equal( _.arity(3, variadic)(1, 2, 3, 4, 5), unvariadic(1, 2, 3, 4, 5), "shouldn't trim arguments");
34 | assert.equal( _.arity(3, variadic)(1), unvariadic(1), "shouldn't pad arguments");
35 |
36 | // this is the big use case for _.arity:
37 |
38 | function reverse (list) {
39 | return [].reduce.call(list, function (acc, element) {
40 | acc.unshift(element);
41 | return acc;
42 | }, []);
43 | }
44 |
45 | function naiveFlip (fun) {
46 | return function () {
47 | return fun.apply(this, reverse(arguments));
48 | };
49 | }
50 |
51 | function echo (a, b, c) { return [a, b, c]; }
52 |
53 | assert.deepEqual(naiveFlip(echo)(1, 2, 3), [3, 2, 1], "naive flip flips its arguments");
54 | assert.notEqual(naiveFlip(echo).length, echo.length, "naiveFlip gets its arity wrong");
55 |
56 | function flipWithArity (fun) {
57 | return _.arity(fun.length, naiveFlip(fun));
58 | }
59 |
60 | assert.deepEqual(flipWithArity(echo)(1, 2, 3), [3, 2, 1], "flipWithArity flips its arguments");
61 | assert.equal(flipWithArity(echo).length, echo.length, "flipWithArity gets its arity correct");
62 |
63 | });
64 |
65 | describe("curry", function() {
66 | var func = function (x, y, z) {
67 | return x + y + z;
68 | },
69 | curried = _.curry(func),
70 | rCurried = _.curryRight(func);
71 |
72 | assert.equal(func(1, 2, 3), 6, "Test pure function");
73 | assert.equal(typeof curried, 'function', "Curry returns a function");
74 | assert.equal(typeof curried(1), 'function', "Curry returns a function after partial application");
75 | assert.equal(curried(1)(2)(3), 6, "Curry returns a value after total application");
76 | assert.equal(curried(1)(2)(3), 6, "Curry invocations have no side effects and do not interact with each other");
77 | assert.equal(curried(2)(4)(8), 14, "Curry invocations have no side effects and do not interact with each other");
78 | assert.equal(rCurried('a')('b')('c'), 'cba', "Flipped curry applies arguments in reverse.");
79 |
80 | var addyz = curried(1);
81 | assert.equal(addyz(2)(3), 6, "Partial applications can be used multiple times");
82 | assert.equal(addyz(2)(4), 7, "Partial applications can be used multiple times");
83 | });
84 |
85 | describe("curry2", function () {
86 |
87 | function echo () { return [].slice.call(arguments, 0); }
88 |
89 | assert.deepEqual(echo(1, 2), [1, 2], "Control test");
90 | assert.deepEqual(_.curry2(echo)(1)(2), [1, 2], "Accepts curried arguments");
91 |
92 | });
93 |
94 | describe("curryRight2", function () {
95 |
96 | function echo () { return [].slice.call(arguments, 0); }
97 |
98 | assert.deepEqual(echo(1, 2), [1, 2], "Control test");
99 | assert.deepEqual(_.curryRight2(echo)(1)(2), [2, 1], "Reverses curried arguments");
100 | assert.equal(_.curryRight2, _.rcurry2, "should have alias 'rcurry2'");
101 | });
102 |
103 | describe("curry3", function () {
104 |
105 | function echo () { return [].slice.call(arguments, 0); }
106 |
107 | assert.deepEqual(echo(1, 2, 3), [1, 2, 3], "Control test");
108 | assert.deepEqual(_.curry3(echo)(1)(2)(3), [1, 2, 3], "Accepts curried arguments");
109 |
110 | });
111 |
112 | describe("curryRight3", function () {
113 |
114 | function echo () { return [].slice.call(arguments, 0); }
115 |
116 | assert.deepEqual(echo(1, 2, 3), [1, 2, 3], "Control test");
117 | assert.deepEqual(_.curryRight3(echo)(1)(2)(3), [3, 2, 1], "Reverses curried arguments");
118 | assert.equal(_.curryRight3, _.rcurry3, "should have alias 'rcurry3'");
119 | });
120 |
121 | describe("enforce", function () {
122 | function binary (a, b) {
123 | return a + b;
124 | }
125 | function ternary (a, b, c) {
126 | return a + b + c;
127 | }
128 | function altTernary (a, b, c) {
129 | return a - b - c;
130 | }
131 | var fBinary = _.enforce(binary),
132 | fTernary = _.enforce(ternary),
133 | fAltTernary = _.enforce(altTernary),
134 | failure = false;
135 | try {
136 | fBinary(1);
137 | } catch (e) {
138 | failure = true;
139 | } finally {
140 | assert.equal(failure, true, "Binary must have two arguments.");
141 | }
142 | assert.equal(fBinary(1, 2), 3, "Function returns after proper application");
143 |
144 | failure = false;
145 | try {
146 | fTernary(1, 3);
147 | } catch (e) {
148 | failure = true;
149 | } finally {
150 | assert.equal(failure, true, "Ternary must have three arguments.");
151 | }
152 | assert.equal(fTernary(1, 2, 3), 6, "Function returns after proper application");
153 | assert.equal(fAltTernary(1, 2, 3), -4, "Function cache does not collide");
154 | });
155 | });
156 |
--------------------------------------------------------------------------------
/test/mocha/object.builders.js:
--------------------------------------------------------------------------------
1 | var _ = require('../..');
2 | var assert = require('assert');
3 |
4 | describe("lodash.object.builders", function () {
5 |
6 | it("renameKeys", function() {
7 | assert.deepEqual(_.renameKeys({'a': 1, 'b': 2}, {'a': 'A'}), {'b': 2, 'A': 1}, 'should rename the keys in the first object to the mapping in the second object');
8 |
9 | var a = {'a': 1, 'b': 2};
10 | var $ = _.renameKeys(a, {'a': 'A'});
11 |
12 | assert.deepEqual(a, {'a': 1, 'b': 2}, 'should not modify the original');
13 | });
14 |
15 | it("snapshot", function() {
16 | var o = {'a': 1, 'b': 2};
17 | var oSnap = _.snapshot(o);
18 |
19 | var a = [1,2,3,4];
20 | var aSnap = _.snapshot(a);
21 |
22 | var n = [1,{a: 1, b: [1,2,3]},{},4];
23 | var nSnap = _.snapshot(n);
24 |
25 | var c = [1,{a: 1, b: [1,2,3]},{},4];
26 | var cSnap = _.snapshot(c);
27 | c[1].b = 42;
28 |
29 | assert.deepEqual(o, oSnap, 'should create a deep copy of an object');
30 | assert.deepEqual(a, aSnap, 'should create a deep copy of an array');
31 | assert.deepEqual(n, nSnap, 'should create a deep copy of an array');
32 | assert.deepEqual(nSnap, [1,{a: 1, b: [1,2,3]},{},4], 'should allow changes to the original to not change copies');
33 | });
34 |
35 | it("setPath", function() {
36 | var obj = {a: {b: {c: 42, d: 108}}};
37 | var ary = ['a', ['b', ['c', 'd'], 'e']];
38 | var nest = [1, {a: 2, b: [3,4], c: 5}, 6];
39 |
40 | assert.deepEqual(_.setPath(obj, 9, ['a', 'b', 'c']), {a: {b: {c: 9, d: 108}}}, '');
41 | assert.deepEqual(_.setPath(ary, 9, [1, 1, 0]), ['a', ['b', [9, 'd'], 'e']], '');
42 | assert.deepEqual(_.setPath(nest, 9, [1, 'b', 1]), [1, {a: 2, b: [3,9], c: 5}, 6], '');
43 |
44 | assert.deepEqual(_.setPath(obj, 9, 'a'), {a: 9}, '');
45 | assert.deepEqual(_.setPath(ary, 9, 1), ['a', 9], '');
46 |
47 | assert.deepEqual(obj, {a: {b: {c: 42, d: 108}}}, 'should not modify the original object');
48 | assert.deepEqual(ary, ['a', ['b', ['c', 'd'], 'e']], 'should not modify the original array');
49 | assert.deepEqual(nest, [1, {a: 2, b: [3,4], c: 5}, 6], 'should not modify the original nested structure');
50 | });
51 |
52 | it("updatePath", function() {
53 | var obj = {a: {b: {c: 42, d: 108}}};
54 | var ary = ['a', ['b', ['c', 'd'], 'e']];
55 | var nest = [1, {a: 2, b: [3,4], c: 5}, 6];
56 |
57 | assert.deepEqual(_.updatePath(obj, _.always(9), ['a', 'b', 'c']), {a: {b: {c: 9, d: 108}}}, '');
58 | assert.deepEqual(_.updatePath(ary, _.always(9), [1, 1, 0]), ['a', ['b', [9, 'd'], 'e']], '');
59 | assert.deepEqual(_.updatePath(nest, _.always(9), [1, 'b', 1]), [1, {a: 2, b: [3,9], c: 5}, 6], '');
60 |
61 | assert.deepEqual(_.updatePath(obj, _.always(9), 'a'), {a: 9}, '');
62 | assert.deepEqual(_.updatePath(ary, _.always(9), 1), ['a', 9], '');
63 |
64 | assert.deepEqual(obj, {a: {b: {c: 42, d: 108}}}, 'should not modify the original object');
65 | assert.deepEqual(ary, ['a', ['b', ['c', 'd'], 'e']], 'should not modify the original array');
66 | assert.deepEqual(nest, [1, {a: 2, b: [3,4], c: 5}, 6], 'should not modify the original nested structure');
67 | });
68 |
69 | });
70 |
--------------------------------------------------------------------------------
/test/mocha/util.operators.js:
--------------------------------------------------------------------------------
1 | var _ = require('../..');
2 | var assert = require('assert');
3 |
4 | describe("lodash.util.operators", function () {
5 |
6 | it("addContrib", function() {
7 | assert.equal(_.addContrib(1, 1), 2, '1 + 1 = 2');
8 | assert.equal(_.addContrib(3, 5), 8, '3 + 5 = 8');
9 | assert.equal(_.addContrib(1, 2, 3, 4), 10, 'adds multiple operands');
10 | });
11 |
12 | it("sub", function() {
13 | assert.equal(_.sub(1, 1), 0, '1 - 1 = 0');
14 | assert.equal(_.sub(5, 3), 2, '5 - 3 = 2');
15 | assert.equal(_.sub(10, 9, 8, 7), -14, 'subtracts multiple operands');
16 | });
17 |
18 | it("mul", function() {
19 | assert.equal(_.mul(1, 1), 1, '1 * 1 = 1');
20 | assert.equal(_.mul(5, 3), 15, '5 * 3 = 15');
21 | assert.equal(_.mul(1, 2, 3, 4), 24, 'multiplies multiple operands');
22 | });
23 |
24 | it("div", function() {
25 | assert.equal(_.div(1, 1), 1, '1 / 1 = 1');
26 | assert.equal(_.div(15, 3), 5, '15 / 3 = 5');
27 | assert.equal(_.div(15, 0), Infinity, '15 / 0 = Infinity');
28 | assert.equal(_.div(24, 2, 2, 2), 3, 'divides multiple operands');
29 | });
30 |
31 | it("mod", function() {
32 | assert.equal(_.mod(3, 2), 1, '3 / 2 = 1');
33 | assert.equal(_.mod(15, 3), 0, '15 / 3 = 0');
34 | });
35 |
36 | it("inc", function() {
37 | assert.equal(_.inc(1), 2, '++1 = 2');
38 | assert.equal(_.inc(15), 16, '++15 = 16');
39 | });
40 |
41 | it("dec", function() {
42 | assert.equal(_.dec(2), 1, '--2 = 1');
43 | assert.equal(_.dec(15), 14, '--15 = 15');
44 | });
45 |
46 | it("neg", function() {
47 | assert.equal(_.neg(2), -2, 'opposite of 2');
48 | assert.equal(_.neg(-2), 2, 'opposite of -2');
49 | assert.equal(_.neg(true), -1, 'opposite of true');
50 | });
51 |
52 | it("eqContrib", function() {
53 | assert.equal(_.eqContrib(1, 1), true, '1 == 1');
54 | assert.equal(_.eqContrib(1, true), true, '1 == true');
55 | assert.equal(_.eqContrib(1, false), false, '1 != false');
56 | assert.equal(_.eqContrib(1, '1'), true, '1 == "1"');
57 | assert.equal(_.eqContrib(1, 'one'), false, '1 != "one"');
58 | assert.equal(_.eqContrib(0, 0), true, '0 == 0');
59 | assert.equal(_.eqContrib(0, false), true, '0 == false');
60 | assert.equal(_.eqContrib(0, '0'), true, '0 == "0"');
61 | assert.equal(_.eqContrib({}, {}), false, '{} == {}');
62 | assert.equal(_.eqContrib(0, 0, 1), false, 'compares a list of arguments');
63 | });
64 | it("seq", function() {
65 | assert.equal(_.seq(1, 1), true, '1 === 1');
66 | assert.equal(_.seq(1, '1'), false, '1 !== "1"');
67 | assert.equal(_.seq(0, 0, 1), false, 'compares a list of arguments');
68 | });
69 | it("neq", function() {
70 | assert.equal(_.neq('a', 'b'), true, '"a" != "b"');
71 | assert.equal(_.neq(1, '1'), false, '1 == "1"');
72 | assert.equal(_.neq(0, 0, 1), true, 'compares a list of arguments');
73 | });
74 | it("sneq", function() {
75 | assert.equal(_.sneq('a', 'b'), true, '"a" !== "b"');
76 | assert.equal(_.sneq(1, '1'), true, '1 !== "1"');
77 | assert.equal(_.sneq(0, 0, 1), true, 'compares a list of arguments');
78 | });
79 | it("not", function() {
80 | assert.equal(_.not(true), false, 'converts true to false');
81 | assert.equal(_.not(false), true, 'converts false to true');
82 | assert.equal(_.not('truthy'), false, 'converts truthy values to false');
83 | assert.equal(_.not(null), true, 'converts falsy values to true');
84 | });
85 | it("gtContrib", function() {
86 | assert.equal(_.gtContrib(3, 2), true, '3 > 2');
87 | assert.equal(_.gtContrib(1, 3), false, '1 > 3');
88 | assert.equal(_.gtContrib(1, 2, 1), false, 'compares a list of arguments');
89 | });
90 | it("ltContrib", function() {
91 | assert.equal(_.ltContrib(3, 2), false, '3 < 2');
92 | assert.equal(_.ltContrib(1, 3), true, '1 < 3');
93 | assert.equal(_.ltContrib(1, 2, 1), false, 'compares a list of arguments');
94 | });
95 | it("gteContrib", function() {
96 | assert.equal(_.gteContrib(3, 2), true, '3 >= 2');
97 | assert.equal(_.gteContrib(1, 3), false, '1 >= 3');
98 | assert.equal(_.gteContrib(3, 3), true, '3 >= 3');
99 | assert.equal(_.gteContrib(2, 3, 1), false, 'compares a list of arguments');
100 | });
101 | it("lteContrib", function() {
102 | assert.equal(_.lteContrib(3, 2), false, '3 <= 2');
103 | assert.equal(_.lteContrib(1, 3), true, '1 <= 3');
104 | assert.equal(_.lteContrib(3, 3), true, '3 <= 3');
105 | assert.equal(_.lteContrib(2, 2, 1), false, 'compares a list of arguments');
106 | });
107 | it("bitwiseAnd", function() {
108 | assert.equal(_.bitwiseAnd(1, 1), 1, '1 & 1');
109 | assert.equal(_.bitwiseAnd(1, 0), 0, '1 & 0');
110 | assert.equal(_.bitwiseAnd(1, 1, 0), 0, 'operates on multiple arguments');
111 | });
112 | it("bitwiseOr", function() {
113 | assert.equal(_.bitwiseOr(1, 1), 1, '1 | 1');
114 | assert.equal(_.bitwiseOr(1, 0), 1, '1 | 0');
115 | assert.equal(_.bitwiseOr(1, 1, 2), 3, 'operates on multiple arguments');
116 | });
117 | it("bitwiseXor", function() {
118 | assert.equal(_.bitwiseXor(1, 1), 0, '1 ^ 1');
119 | assert.equal(_.bitwiseXor(1, 2), 3, '1 ^ 2');
120 | assert.equal(_.bitwiseXor(1, 2, 3), 0, 'operates on multiple arguments');
121 | });
122 | it("bitwiseNot", function() {
123 | assert.equal(_.bitwiseNot(1), -2, '~1');
124 | assert.equal(_.bitwiseNot(2), -3, '~2');
125 | });
126 | it("bitwiseLeft", function() {
127 | assert.equal(_.bitwiseLeft(1, 1), 2, '1 << 1');
128 | assert.equal(_.bitwiseLeft(1, 0), 1, '1 << 0');
129 | assert.equal(_.bitwiseLeft(1, 1, 1), 4, 'operates on multiple arguments');
130 | });
131 | it("bitwiseRight", function() {
132 | assert.equal(_.bitwiseRight(1, 1), 0, '1 >> 1');
133 | assert.equal(_.bitwiseRight(2, 1), 1, '2 >> 1');
134 | assert.equal(_.bitwiseRight(3, 1, 1), 0, 'operates on multiple arguments');
135 | });
136 | it("bitwiseZ", function() {
137 | assert.equal(_.bitwiseZ(-1, 1), 2147483647, '-1 >>> 1');
138 | assert.equal(_.bitwiseZ(-1, 1, 1), 1073741823, 'operates on multiple arguments');
139 | });
140 |
141 | });
142 |
--------------------------------------------------------------------------------
/test/mocha/util.strings.js:
--------------------------------------------------------------------------------
1 | var _ = require('../..');
2 | var assert = require('assert');
3 | var expect = require('chai').expect;
4 |
5 | describe("util.string", function () {
6 | describe("fromQuery", function () {
7 | it("can convert a query string to a hash", function (done) {
8 | var query = 'foo%26bar=baz&test=total+utter+success';
9 | assert(_.isEqual(_.fromQuery(query), {'foo&bar': 'baz', 'test': 'total utter success'}));
10 | done();
11 | });
12 | });
13 |
14 | describe("toQuery", function () {
15 | it('can convert a hash to a query string', function (done) {
16 | var obj = {'foo&bar': 'baz', 'test': 'total success'};
17 | assert.equal(_.toQuery(obj), 'foo%26bar=baz&test=total%20success', 'can convert a hash to a query string');
18 | done();
19 | });
20 | });
21 |
22 | describe("Title Case", function () {
23 | it('can convert a sentance to a Title Case', function (done) {
24 | assert.equal(_.titleCase('Hey, dudes!'), 'Hey, Dudes!');
25 | assert.equal(_.titleCase('(Where are you?)'), '(Where Are You?)');
26 | assert.equal(_.titleCase('Boogaloo dudes (Stand up, come on!)'), 'Boogaloo Dudes (Stand Up, Come On!)');
27 | done();
28 | });
29 | });
30 |
31 | describe('slugify', function () {
32 |
33 | it('lower-cases strings for slugs', function () {
34 | expect(_.slugify('String')).to.equal('string');
35 | });
36 |
37 | it('converts a string with spaces into a slug', function () {
38 | expect(_.slugify('string with spaces')).to.equal('string-with-spaces');
39 | });
40 |
41 | it('converts a string with dots into a slug', function () {
42 | expect(_.slugify('string.with.dots')).to.equal('string-with-dots');
43 | });
44 |
45 | it('converts TitleCase strings into slugs', function () {
46 | expect(_.slugify('TitleCase')).to.equal('title-case');
47 | });
48 |
49 | it('leaves strings that are already slugs alone', function () {
50 | expect(_.slugify('i-am-a-slug')).to.equal('i-am-a-slug');
51 | });
52 |
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/test/mocha/vanilla.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 |
3 | describe("vanilla", function () {
4 | it("should install the extensions", function (done) {
5 | var contrib = require('../..');
6 | assert('walk' in contrib);
7 | done();
8 | });
9 |
10 | it("but should leave 'lodash' alone", function (done) {
11 | var lodash = require('lodash');
12 | assert(!('walk' in lodash));
13 | done();
14 | });
15 |
16 | it("but should not override methods", function (done) {
17 | var lodash = require('lodash');
18 | var contrib = require('../..');
19 | var methods = lodash.remove(lodash.keys(lodash), '_');
20 | lodash.forEach(methods, function(m) {
21 | assert.equal(contrib[m].toString(), lodash[m].toString(), m + ' should be the same');
22 | });
23 | done();
24 | });
25 | });
26 |
27 |
--------------------------------------------------------------------------------
/test/object.selectors.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 |
3 | module("lodash.object.selectors");
4 |
5 | test("accessor", function() {
6 | var a = [{a: 1, b: 2}, {c: 3}];
7 |
8 | equal(_.accessor('a')(a[0]), 1, 'should return a function that plucks');
9 | equal(_.accessor('a')(a[1]), undefined, 'should return a function that plucks, or returns undefined');
10 | deepEqual(_.map(a, _.accessor('a')), [1, undefined], 'should return a function that plucks');
11 | });
12 |
13 | test("dictionary", function() {
14 | var a = [{a: 1, b: 2}, {c: 3}];
15 |
16 | equal(_.dictionary(a[0])('a'), 1, 'should return a function that acts as a dictionary');
17 | equal(_.dictionary(a[1])('a'), undefined, 'should return a function that acts as a dictionary, or returns undefined');
18 | });
19 |
20 | test("selectKeys", function() {
21 | deepEqual(_.selectKeys({'a': 1, 'b': 2}, ['a']), {'a': 1}, 'shold return a map of the desired keys');
22 | deepEqual(_.selectKeys({'a': 1, 'b': 2}, ['z']), {}, 'shold return an empty map if the desired keys are not present');
23 | });
24 |
25 | test("kv", function() {
26 | deepEqual(_.kv({'a': 1, 'b': 2}, 'a'), ['a', 1], 'should return the key/value pair at the desired key');
27 | equal(_.kv({'a': 1, 'b': 2}, 'z'), undefined, 'shold return undefined if the desired key is not present');
28 | });
29 |
30 | test("getPath", function() {
31 | var deepObject = { a: { b: { c: "c" } }, falseVal: false, nullVal: null, undefinedVal: undefined, arrayVal: ["arr"] };
32 | var deepArr = [[["thirdLevel"]]];
33 | var ks = ["a", "b", "c"];
34 |
35 | strictEqual(_.getPath(deepObject, ks), "c", "should get a deep property's value from objects");
36 | deepEqual(ks, ["a", "b", "c"], "should not have mutated ks argument");
37 | strictEqual(_.getPath(deepArr, [0, 0, 0]), "thirdLevel", "should get a deep property's value from arrays");
38 | strictEqual(_.getPath(deepObject, ["arrayVal", 0]), "arr", "should get a deep property's value from nested arrays and objects");
39 |
40 | strictEqual(_.getPath(deepObject, ["undefinedVal"]), undefined, "should return undefined for undefined properties");
41 | strictEqual(_.getPath(deepObject, ["a", "notHere"]), undefined, "should return undefined for non-existent properties");
42 | strictEqual(_.getPath(deepObject, ["nullVal"]), null, "should return null for null properties");
43 | strictEqual(_.getPath(deepObject, ["nullVal", "notHere", "notHereEither"]), undefined, "should return undefined for non-existent descendents of null properties");
44 |
45 | strictEqual(_.getPath(deepObject, "a.b.c"), "c", "should work with keys written in dot notation");
46 | });
47 |
48 | test("hasPath", function() {
49 | var deepObject = { a: { b: { c: "c" } }, falseVal: false, numberVal: 123, stringVal: 'str', nullVal: null, undefinedVal: undefined, arrayVal: ["arr"] };
50 | var ks = ["a", "b", "c"];
51 |
52 | strictEqual(_.hasPath(deepObject, ["notHere", "notHereEither"]), false, "should return false if the path doesn't exist");
53 | strictEqual(_.hasPath(deepObject, ks), true, "should return true if the path exists");
54 | deepEqual(ks, ["a", "b", "c"], "should not have mutated ks argument");
55 |
56 | strictEqual(_.hasPath(deepObject, ["arrayVal", 0]), true, "should return true for an array's index if it is defined");
57 | strictEqual(_.hasPath(deepObject, ["arrayVal", 999]), false, "should return false for an array's index if it is not defined");
58 |
59 | strictEqual(_.hasPath(deepObject, ["nullVal"]), true, "should return true for null properties");
60 | strictEqual(_.hasPath(deepObject, ["undefinedVal"]), true, "should return true for properties that were explicitly assigned undefined");
61 |
62 | strictEqual(_.hasPath(deepObject, ["nullVal", "notHere"]), false, "should return false for descendants of null properties");
63 | strictEqual(_.hasPath(deepObject, ["undefinedVal", "notHere"]), false, "should return false for descendants of undefined properties");
64 | strictEqual(_.hasPath(deepObject, ["falseVal", "notHere"]), false, "should return false for descendants of a boolean literal");
65 | strictEqual(_.hasPath(deepObject, ["stringVal", "notHere"]), false, "should return false for descendants of a string literal");
66 | strictEqual(_.hasPath(deepObject, ["numberVal", "notHere"]), false, "should return false for descendants of a number literal");
67 |
68 | strictEqual(_.hasPath(deepObject, "a.b.c"), true, "should work with keys written in dot notation.");
69 | });
70 |
71 | test("pickWhen", function() {
72 | var a = {foo: true, bar: false, baz: 42};
73 |
74 | deepEqual(_.pickWhen(a, _.truthy), {foo: true, baz: 42}, "should return an object with kvs that return a truthy value for the given predicate");
75 | });
76 |
77 | test("omitWhen", function() {
78 | var a = {foo: [], bar: "", baz: "something", quux: ['a']};
79 |
80 | deepEqual(_.omitWhen(a, _.isEmpty), {baz: "something", quux: ['a']}, "should return an object with kvs that return a falsey value for the given predicate");
81 | });
82 | });
83 |
--------------------------------------------------------------------------------
/test/util.existential.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 |
3 | module("lodash.util.existential");
4 |
5 | test("exists", function() {
6 | equal(_.exists(null), false, 'should know that null is not existy');
7 | equal(_.exists(undefined), false, 'should know that undefined is not existy');
8 |
9 | equal(_.exists(1), true, 'should know that all but null and undefined are existy');
10 | equal(_.exists(0), true, 'should know that all but null and undefined are existy');
11 | equal(_.exists(-1), true, 'should know that all but null and undefined are existy');
12 | equal(_.exists(3.14), true, 'should know that all but null and undefined are existy');
13 | equal(_.exists('undefined'), true, 'should know that all but null and undefined are existy');
14 | equal(_.exists(''), true, 'should know that all but null and undefined are existy');
15 | equal(_.exists(NaN), true, 'should know that all but null and undefined are existy');
16 | equal(_.exists(Infinity), true, 'should know that all but null and undefined are existy');
17 | equal(_.exists(true), true, 'should know that all but null and undefined are existy');
18 | equal(_.exists(false), true, 'should know that all but null and undefined are existy');
19 | equal(_.exists(function(){}), true, 'should know that all but null and undefined are existy');
20 |
21 | equal(_.existsAll(0, null, '2'), false, 'should know that null is not existy');
22 | equal(_.existsAll(0, undefined, '2'), false, 'should know that undefined is not existy');
23 | equal(_.existsAll(0, 'undefined', NaN, Infinity, true, false, function(){}), true, 'should know that all but null and undefined are existy');
24 | });
25 |
26 | test("truthy", function() {
27 | equal(_.truthy(null), false, 'should know that null, undefined and false are not truthy');
28 | equal(_.truthy(undefined), false, 'should know that null, undefined and false are not truthy');
29 | equal(_.truthy(false), false, 'should know that null, undefined and false are not truthy');
30 | equal(_.truthy(1), true, 'should know that everything else is truthy');
31 | equal(_.truthy(0), true, 'should know that everything else is truthy');
32 | equal(_.truthy(-1), true, 'should know that everything else is truthy');
33 | equal(_.truthy(3.14), true, 'should know that everything else is truthy');
34 | equal(_.truthy('undefined'), true, 'should know that everything else is truthy');
35 | equal(_.truthy(''), true, 'should know that everything else is truthy');
36 | equal(_.truthy(NaN), true, 'should know that everything else is truthy');
37 | equal(_.truthy(Infinity), true, 'should know that everything else is truthy');
38 | equal(_.truthy(true), true, 'should know that everything else is truthy');
39 | equal(_.truthy(function(){}), true, 'should know that everything else is truthy');
40 |
41 | equal(_.truthyAll(0, null, '2'), false, 'should know that null is not truthy');
42 | equal(_.truthyAll(0, undefined, '2'), false, 'should know that undefined is not truthy');
43 | equal(_.truthyAll(0, undefined, '2'), false, 'should know that false is not truthy');
44 | equal(_.truthyAll(0, 'undefined', NaN, Infinity, true, function(){}), true, 'should know that all but null and undefined are truthy');
45 | });
46 |
47 | test("falsey", function() {
48 | equal(_.falsey(null), true, 'should know that null, undefined and false are falsey');
49 | equal(_.falsey(undefined), true, 'should know that null, undefined and false are falsey');
50 | equal(_.falsey(false), true, 'should know that null, undefined and false are falsey');
51 |
52 | equal(_.falsey(1), false, 'should know that everything else is not falsey');
53 | equal(_.falsey(0), false, 'should know that everything else is not falsey');
54 | equal(_.falsey(-1), false, 'should know that everything else is not falsey');
55 | equal(_.falsey(3.14), false, 'should know that everything else is not falsey');
56 | equal(_.falsey('undefined'), false, 'should know that everything else is not falsey');
57 | equal(_.falsey(''), false, 'should know that everything else is not falsey');
58 | equal(_.falsey(NaN), false, 'should know that everything else is not falsey');
59 | equal(_.falsey(Infinity), false, 'should know that everything else is not falsey');
60 | equal(_.falsey(true), false, 'should know that everything else is not falsey');
61 | equal(_.falsey(function(){}), false, 'should know that everything else is not falsey');
62 |
63 | equal(!_.truthyAll(1, null, '2'), true, '!_.truthyAll should know that null is falsey even when veriadic');
64 | equal(!_.truthyAll(1, undefined, '2'), true, '!_.truthyAll should know that undefined is falsey even when veriadic');
65 | equal(!_.truthyAll(1, false, '2'), true, '!_.truthyAll should know that false is falsey even when veriadic');
66 | equal(!_.truthyAll(1, 'undefined', NaN, Infinity, true, function(){}), false, '!_.truthyAll should know that only null undefined and false are falsey even when veriadic');
67 |
68 | equal(_.falseyAll(false, null), true, '_.falseyAll should know that null is falsey even when veriadic');
69 | equal(_.falseyAll(false, undefined), true, '_.falseyAll should know that undefined is falsey even when veriadic');
70 | equal(_.falseyAll(false, false), true, '_.falseyAll should know that false is falsey even when veriadic');
71 | equal(_.falseyAll(false, null, undefined), true, '_.falseyAll should know that only null undefined and false are falsey even when veriadic');
72 | });
73 |
74 | test('firstExisting', function() {
75 | equal(_.firstExisting('first', 'second'), 'first', 'should return the first existing value');
76 | equal(_.firstExisting(null, 'second'), 'second', 'should ignore null');
77 | equal(_.firstExisting(void 0, 'second'), 'second', 'should ignore undefined');
78 | equal(_.firstExisting(null, void 0, 'third'), 'third', 'should work with more arguments');
79 | });
80 |
81 | });
82 |
--------------------------------------------------------------------------------
/test/util.strings.js:
--------------------------------------------------------------------------------
1 |
2 | $(document).ready(function() {
3 |
4 | module('lodash.util.strings');
5 |
6 | test('explode', function() {
7 | deepEqual(_.explode('Virgil'), ['V','i','r','g','i','l'], 'Should explode a string into an array of characters.');
8 | });
9 |
10 | test('fromQuery', function() {
11 | var query = 'foo%5Bbar%5D%5Bbaz%5D%5Bblargl%5D=blah&foo%5Bbar%5D%5Bbaz%5D%5Bblargr%5D=woop&blar=bluh&abc[]=123&abc[]=234';
12 | ok(_.isEqual(_.fromQuery(query), {
13 | 'foo': {
14 | 'bar': {
15 | 'baz': {
16 | 'blargl': 'blah',
17 | 'blargr': 'woop'
18 | }
19 | }
20 | },
21 | 'blar': 'bluh',
22 | 'abc': [
23 | '123',
24 | '234'
25 | ]
26 | }), 'can convert a query string to a hash');
27 | });
28 |
29 | test('implode', function() {
30 | equal(_.implode(['H','o','m','e','r']), 'Homer', 'Should implode an array of characters into a single string.');
31 | });
32 |
33 | test('toDash', function() {
34 | equal(_.toDash('trojanWar'), 'trojan-war', 'Should convert a camelCase string to dashed-format.');
35 | equal(_.toDash('PersianWar'), 'persian-war', 'Should convert a PascalCase string to dashed-format.');
36 | });
37 |
38 | test('toQuery', function() {
39 | var obj = {'foo&bar': 'baz', 'test': 'total success', 'nested': {'works': 'too'}, 'isn\'t': ['that', 'cool?']};
40 | equal(_.toQuery(obj), 'foo%26bar=baz&test=total%20success&nested%5Bworks%5D=too&isn\'t%5B%5D=that&isn\'t%5B%5D=cool%3F', 'can convert a hash to a query string');
41 | });
42 |
43 | test('strContains', function() {
44 | equal(_.strContains('Metaphysics', 'physics'), true, 'Should return true if string contains search string.');
45 | equal(_.strContains('Poetics', 'prose'), false, 'Should return false if string does not contain search string.');
46 |
47 | var thrower = function() { _.strContains([], ''); };
48 | throws(thrower, TypeError, 'Throws TypeError if first argument is not a string.');
49 | });
50 |
51 | test('humanize', function() {
52 | equal(_.humanize("lowercase"), "Lowercase");
53 | equal(_.humanize("Class"), "Class");
54 | equal(_.humanize("MyClass"), "My Class");
55 | equal(_.humanize("HTML"), "HTML");
56 | equal(_.humanize("PDFLoader"), "PDF Loader");
57 | equal(_.humanize("AString"), "A String");
58 | equal(_.humanize("SimpleXMLParser"), "Simple XML Parser");
59 | equal(_.humanize("LastUpdateDateInt"), "Last Update Date Int");
60 | equal(_.humanize("LastUpdate_date_IntHTML"), "Last Update date Int HTML");
61 | });
62 |
63 | test( 'slugify', function() {
64 | equal(_.slugify('String'), 'string', 'lower-cases strings for slugs');
65 | equal(_.slugify('string with spaces'), 'string-with-spaces', 'converts a string with spaces into a slug');
66 | equal(_.slugify('string.with.dots'), 'string-with-dots', 'converts a string with dots into a slug');
67 | equal(_.slugify('TitleCase'), 'title-case', 'converts TitleCase strings into slugs');
68 | equal(_.slugify('i-am-a-slug'), 'i-am-a-slug', 'leaves strings that are already slugs alone');
69 | });
70 | });
71 |
--------------------------------------------------------------------------------
/test/util.trampolines.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 |
3 | module("lodash.util.trampolines");
4 |
5 | test("trampoline", function() {
6 | var oddOline = function(n) {
7 | if (n === 0)
8 | return _.done(false);
9 | else
10 | return _.partial(evenOline, Math.abs(n) - 1);
11 | };
12 |
13 | var evenOline = function(n) {
14 | if (n === 0)
15 | return _.done(true);
16 | else
17 | return _.partial(oddOline, Math.abs(n) - 1);
18 | };
19 |
20 | equal(_.trampoline(evenOline, 55000), true, 'should trampoline two mutually recursive functions');
21 | equal(_.trampoline(evenOline, 0), true, 'should trampoline two mutually recursive functions');
22 | equal(_.trampoline(evenOline, 111111), false, 'should trampoline two mutually recursive functions');
23 | equal(_.trampoline(oddOline, 1), true, 'should trampoline two mutually recursive functions');
24 | equal(_.trampoline(oddOline, 11111), true, 'should trampoline two mutually recursive functions');
25 | equal(_.trampoline(oddOline, 22), false, 'should trampoline two mutually recursive functions');
26 | });
27 |
28 | });
29 |
--------------------------------------------------------------------------------
/test/vendor/qunit.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * QUnit 1.18.0
3 | * http://qunitjs.com/
4 | *
5 | * Copyright jQuery Foundation and other contributors
6 | * Released under the MIT license
7 | * http://jquery.org/license
8 | *
9 | * Date: 2015-04-03T10:23Z
10 | */
11 |
12 | /** Font Family and Sizes */
13 |
14 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
15 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
16 | }
17 |
18 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
19 | #qunit-tests { font-size: smaller; }
20 |
21 |
22 | /** Resets */
23 |
24 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
25 | margin: 0;
26 | padding: 0;
27 | }
28 |
29 |
30 | /** Header */
31 |
32 | #qunit-header {
33 | padding: 0.5em 0 0.5em 1em;
34 |
35 | color: #8699A4;
36 | background-color: #0D3349;
37 |
38 | font-size: 1.5em;
39 | line-height: 1em;
40 | font-weight: 400;
41 |
42 | border-radius: 5px 5px 0 0;
43 | }
44 |
45 | #qunit-header a {
46 | text-decoration: none;
47 | color: #C2CCD1;
48 | }
49 |
50 | #qunit-header a:hover,
51 | #qunit-header a:focus {
52 | color: #FFF;
53 | }
54 |
55 | #qunit-testrunner-toolbar label {
56 | display: inline-block;
57 | padding: 0 0.5em 0 0.1em;
58 | }
59 |
60 | #qunit-banner {
61 | height: 5px;
62 | }
63 |
64 | #qunit-testrunner-toolbar {
65 | padding: 0.5em 1em 0.5em 1em;
66 | color: #5E740B;
67 | background-color: #EEE;
68 | overflow: hidden;
69 | }
70 |
71 | #qunit-userAgent {
72 | padding: 0.5em 1em 0.5em 1em;
73 | background-color: #2B81AF;
74 | color: #FFF;
75 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
76 | }
77 |
78 | #qunit-modulefilter-container {
79 | float: right;
80 | padding: 0.2em;
81 | }
82 |
83 | .qunit-url-config {
84 | display: inline-block;
85 | padding: 0.1em;
86 | }
87 |
88 | .qunit-filter {
89 | display: block;
90 | float: right;
91 | margin-left: 1em;
92 | }
93 |
94 | /** Tests: Pass/Fail */
95 |
96 | #qunit-tests {
97 | list-style-position: inside;
98 | }
99 |
100 | #qunit-tests li {
101 | padding: 0.4em 1em 0.4em 1em;
102 | border-bottom: 1px solid #FFF;
103 | list-style-position: inside;
104 | }
105 |
106 | #qunit-tests > li {
107 | display: none;
108 | }
109 |
110 | #qunit-tests li.running,
111 | #qunit-tests li.pass,
112 | #qunit-tests li.fail,
113 | #qunit-tests li.skipped {
114 | display: list-item;
115 | }
116 |
117 | #qunit-tests.hidepass li.running,
118 | #qunit-tests.hidepass li.pass {
119 | visibility: hidden;
120 | position: absolute;
121 | width: 0px;
122 | height: 0px;
123 | padding: 0;
124 | border: 0;
125 | margin: 0;
126 | }
127 |
128 | #qunit-tests li strong {
129 | cursor: pointer;
130 | }
131 |
132 | #qunit-tests li.skipped strong {
133 | cursor: default;
134 | }
135 |
136 | #qunit-tests li a {
137 | padding: 0.5em;
138 | color: #C2CCD1;
139 | text-decoration: none;
140 | }
141 |
142 | #qunit-tests li p a {
143 | padding: 0.25em;
144 | color: #6B6464;
145 | }
146 | #qunit-tests li a:hover,
147 | #qunit-tests li a:focus {
148 | color: #000;
149 | }
150 |
151 | #qunit-tests li .runtime {
152 | float: right;
153 | font-size: smaller;
154 | }
155 |
156 | .qunit-assert-list {
157 | margin-top: 0.5em;
158 | padding: 0.5em;
159 |
160 | background-color: #FFF;
161 |
162 | border-radius: 5px;
163 | }
164 |
165 | .qunit-collapsed {
166 | display: none;
167 | }
168 |
169 | #qunit-tests table {
170 | border-collapse: collapse;
171 | margin-top: 0.2em;
172 | }
173 |
174 | #qunit-tests th {
175 | text-align: right;
176 | vertical-align: top;
177 | padding: 0 0.5em 0 0;
178 | }
179 |
180 | #qunit-tests td {
181 | vertical-align: top;
182 | }
183 |
184 | #qunit-tests pre {
185 | margin: 0;
186 | white-space: pre-wrap;
187 | word-wrap: break-word;
188 | }
189 |
190 | #qunit-tests del {
191 | background-color: #E0F2BE;
192 | color: #374E0C;
193 | text-decoration: none;
194 | }
195 |
196 | #qunit-tests ins {
197 | background-color: #FFCACA;
198 | color: #500;
199 | text-decoration: none;
200 | }
201 |
202 | /*** Test Counts */
203 |
204 | #qunit-tests b.counts { color: #000; }
205 | #qunit-tests b.passed { color: #5E740B; }
206 | #qunit-tests b.failed { color: #710909; }
207 |
208 | #qunit-tests li li {
209 | padding: 5px;
210 | background-color: #FFF;
211 | border-bottom: none;
212 | list-style-position: inside;
213 | }
214 |
215 | /*** Passing Styles */
216 |
217 | #qunit-tests li li.pass {
218 | color: #3C510C;
219 | background-color: #FFF;
220 | border-left: 10px solid #C6E746;
221 | }
222 |
223 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
224 | #qunit-tests .pass .test-name { color: #366097; }
225 |
226 | #qunit-tests .pass .test-actual,
227 | #qunit-tests .pass .test-expected { color: #999; }
228 |
229 | #qunit-banner.qunit-pass { background-color: #C6E746; }
230 |
231 | /*** Failing Styles */
232 |
233 | #qunit-tests li li.fail {
234 | color: #710909;
235 | background-color: #FFF;
236 | border-left: 10px solid #EE5757;
237 | white-space: pre;
238 | }
239 |
240 | #qunit-tests > li:last-child {
241 | border-radius: 0 0 5px 5px;
242 | }
243 |
244 | #qunit-tests .fail { color: #000; background-color: #EE5757; }
245 | #qunit-tests .fail .test-name,
246 | #qunit-tests .fail .module-name { color: #000; }
247 |
248 | #qunit-tests .fail .test-actual { color: #EE5757; }
249 | #qunit-tests .fail .test-expected { color: #008000; }
250 |
251 | #qunit-banner.qunit-fail { background-color: #EE5757; }
252 |
253 | /*** Skipped tests */
254 |
255 | #qunit-tests .skipped {
256 | background-color: #EBECE9;
257 | }
258 |
259 | #qunit-tests .qunit-skipped-label {
260 | background-color: #F4FF77;
261 | display: inline-block;
262 | font-style: normal;
263 | color: #366097;
264 | line-height: 1.8em;
265 | padding: 0 0.5em;
266 | margin: -0.4em 0.4em -0.4em 0;
267 | }
268 |
269 | /** Result */
270 |
271 | #qunit-testresult {
272 | padding: 0.5em 1em 0.5em 1em;
273 |
274 | color: #2B81AF;
275 | background-color: #D2E0E6;
276 |
277 | border-bottom: 1px solid #FFF;
278 | }
279 | #qunit-testresult .module-name {
280 | font-weight: 700;
281 | }
282 |
283 | /** Fixture */
284 |
285 | #qunit-fixture {
286 | position: absolute;
287 | top: -10000px;
288 | left: -10000px;
289 | width: 1000px;
290 | height: 1000px;
291 | }
292 |
--------------------------------------------------------------------------------
/test/vendor/runner.js:
--------------------------------------------------------------------------------
1 | //https://github.com/jonkemp/qunit-phantomjs-runner
2 | /*
3 | * QtWebKit-powered headless test runner using PhantomJS
4 | *
5 | * PhantomJS binaries: http://phantomjs.org/download.html
6 | * Requires PhantomJS 1.6+ (1.7+ recommended)
7 | *
8 | * Run with:
9 | * phantomjs runner.js [url-of-your-qunit-testsuite]
10 | *
11 | * e.g.
12 | * phantomjs runner.js http://localhost/qunit/test/index.html
13 | */
14 |
15 | /*jshint latedef:false */
16 | /*global phantom:false, require:false, console:false, window:false, QUnit:false */
17 |
18 | (function () {
19 | 'use strict';
20 |
21 | var url, page, timeout,
22 | args = require('system').args;
23 |
24 | // arg[0]: scriptName, args[1...]: arguments
25 | if (args.length < 2) {
26 | console.error('Usage:\n phantomjs [phantom arguments] runner.js [url-of-your-qunit-testsuite] [timeout-in-seconds]');
27 | exit(1);
28 | }
29 |
30 | url = args[1];
31 |
32 | if (args[2] !== undefined) {
33 | timeout = parseInt(args[2], 10);
34 | }
35 |
36 | page = require('webpage').create();
37 |
38 | // Route `console.log()` calls from within the Page context to the main Phantom context (i.e. current `this`)
39 | page.onConsoleMessage = function (msg) {
40 | console.log(msg);
41 | };
42 |
43 | page.onInitialized = function () {
44 | page.evaluate(addLogging);
45 | };
46 |
47 | page.onCallback = function (message) {
48 | var result,
49 | failed;
50 |
51 | if (message) {
52 | if (message.name === 'QUnit.done') {
53 | result = message.data;
54 | failed = !result || !result.total || result.failed;
55 |
56 | if (!result.total) {
57 | console.error('No tests were executed. Are you loading tests asynchronously?');
58 | }
59 |
60 | exit(failed ? 1 : 0);
61 | }
62 | }
63 | };
64 |
65 | page.open(url, function (status) {
66 | if (status !== 'success') {
67 | console.error('Unable to access network: ' + status);
68 | exit(1);
69 | } else {
70 | // Cannot do this verification with the 'DOMContentLoaded' handler because it
71 | // will be too late to attach it if a page does not have any script tags.
72 | var qunitMissing = page.evaluate(function () {
73 | return (typeof QUnit === 'undefined' || !QUnit);
74 | });
75 | if (qunitMissing) {
76 | console.error('The `QUnit` object is not present on this page.');
77 | exit(1);
78 | }
79 |
80 | // Set a default timeout value if the user does not provide one
81 | if (typeof timeout === 'undefined') {
82 | timeout = 5;
83 | }
84 |
85 | // Set a timeout on the test running, otherwise tests with async problems will hang forever
86 | setTimeout(function () {
87 | console.error('The specified timeout of ' + timeout + ' seconds has expired. Aborting...');
88 | exit(1);
89 | }, timeout * 1000);
90 |
91 | // Do nothing... the callback mechanism will handle everything!
92 | }
93 | });
94 |
95 | function addLogging() {
96 | window.document.addEventListener('DOMContentLoaded', function () {
97 | var currentTestAssertions = [];
98 |
99 | QUnit.log(function (details) {
100 | var response;
101 |
102 | // Ignore passing assertions
103 | if (details.result) {
104 | return;
105 | }
106 |
107 | response = details.message || '';
108 |
109 | if (typeof details.expected !== 'undefined') {
110 | if (response) {
111 | response += ', ';
112 | }
113 |
114 | response += 'expected: ' + details.expected + ', but was: ' + details.actual;
115 | }
116 |
117 | if (details.source) {
118 | response += '\n' + details.source;
119 | }
120 |
121 | currentTestAssertions.push('Failed assertion: ' + response);
122 | });
123 |
124 | QUnit.testDone(function (result) {
125 | var i,
126 | len,
127 | name = '';
128 |
129 | if (result.module) {
130 | name += result.module + ': ';
131 | }
132 | name += result.name;
133 |
134 | if (result.failed) {
135 | console.log('\n' + 'Test failed: ' + name);
136 |
137 | for (i = 0, len = currentTestAssertions.length; i < len; i++) {
138 | console.log(' ' + currentTestAssertions[i]);
139 | }
140 | }
141 |
142 | currentTestAssertions.length = 0;
143 | });
144 |
145 | QUnit.done(function (result) {
146 | console.log('\n' + 'Took ' + result.runtime + 'ms to run ' + result.total + ' tests. ' + result.passed + ' passed, ' + result.failed + ' failed.');
147 |
148 | if (typeof window.callPhantom === 'function') {
149 | window.callPhantom({
150 | 'name': 'QUnit.done',
151 | 'data': result
152 | });
153 | }
154 | });
155 | }, false);
156 | }
157 |
158 | function exit(code) {
159 | if (page) {
160 | page.close();
161 | }
162 | setTimeout(function () {
163 | phantom.exit(code);
164 | }, 0);
165 | }
166 | })();
167 |
--------------------------------------------------------------------------------