├── .github
└── workflows
│ ├── cd.yml
│ ├── ci.yml
│ └── prepublish.yml
├── .gitignore
├── .jshintrc
├── CHANGELOG.md
├── CONTRIBUTING.md
├── DEPLOYING.md
├── Gruntfile.js
├── LICENSE
├── README.md
├── bower.json
├── docs
├── index.md
├── underscore.array.builders.js.md
├── underscore.array.selectors.js.md
├── underscore.collections.walk.js.md
├── underscore.function.arity.js.md
├── underscore.function.combinators.js.md
├── underscore.function.iterators.js.md
├── underscore.function.predicates.js.md
├── underscore.object.builders.js.md
├── underscore.object.selectors.js.md
├── underscore.util.existential.js.md
├── underscore.util.operators.js.md
├── underscore.util.strings.js.md
└── underscore.util.trampolines.js.md
├── examples
└── walk-examples.js.md
├── index.js
├── package.json
├── test
├── array.builders.js
├── array.selectors.js
├── collections.walk.js
├── dist-concat.html
├── dist-min.html
├── function.arity.js
├── function.combinators.js
├── function.dispatch.js
├── function.iterators.js
├── function.predicates.js
├── index.html
├── object.builders.js
├── object.selectors.js
├── util.existential.js
├── util.operators.js
├── util.strings.js
└── util.trampolines.js
├── tocdoc.css
├── underscore.array.builders.js
├── underscore.array.selectors.js
├── underscore.collections.walk.js
├── underscore.function.arity.js
├── underscore.function.combinators.js
├── underscore.function.dispatch.js
├── underscore.function.iterators.js
├── underscore.function.predicates.js
├── underscore.object.builders.js
├── underscore.object.selectors.js
├── underscore.util.existential.js
├── underscore.util.operators.js
├── underscore.util.strings.js
├── underscore.util.trampolines.js
└── yarn.lock
/.github/workflows/cd.yml:
--------------------------------------------------------------------------------
1 | # This workflow is like CI, but also builds the documentation and deploys to NPM
2 | # and gh-pages.
3 |
4 | name: Continuous Deployment
5 |
6 | on:
7 | push:
8 | branches: [prepublish]
9 | workflow_run:
10 | workflows: [Prepublication staging]
11 | types: [completed]
12 |
13 | jobs:
14 | deploy:
15 |
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 | - uses: actions/checkout@v2
20 | with:
21 | ref: prepublish
22 | - name: Use Node.js 14.x
23 | uses: actions/setup-node@v1
24 | with:
25 | node-version: 14.x
26 | - name: Configure yarn cache path
27 | run: yarn config set cache-folder ~/.yarn-cache
28 | - name: Restore yarn cache
29 | uses: actions/cache@v2
30 | with:
31 | path: ~/.yarn-cache
32 | key: yarn-cache-14.x
33 | restore-keys: |
34 | yarn-cache-
35 | - name: Restore node_modules
36 | uses: actions/cache@v2
37 | with:
38 | path: node_modules
39 | key: node-modules-14.x-${{ hashFiles('yarn.lock') }}
40 | restore-keys: |
41 | node-modules-14.x-
42 | node-modules-
43 | - name: Install dependencies
44 | run: yarn
45 | - name: Build dist files and docs and run the tests
46 | run: yarn grunt dist docco tocdoc
47 | - name: Commit the build output
48 | uses: EndBug/add-and-commit@v5
49 | with:
50 | add: dist/ index.html
51 | message: Autocommit build output (continuous deployment)
52 | env:
53 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
54 | - name: Publish to NPM
55 | run: yarn publish
56 | env:
57 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
58 | - name: Merge into gh-pages
59 | uses: devmasx/merge-branch@v1.3.0
60 | with:
61 | type: now
62 | target_branch: gh-pages
63 | github_token: ${{ secrets.GITHUB_TOKEN }}
64 | - uses: actions/checkout@v2
65 | with:
66 | ref: gh-pages
67 | - name: Tag the release
68 | uses: Klemensas/action-autotag@stable
69 | with:
70 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
71 | tag_prefix: v
72 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | # This workflow will install the node_modules, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Continuous Integration
5 |
6 | on:
7 | push:
8 | branches: [master]
9 | pull_request:
10 |
11 | jobs:
12 | build:
13 |
14 | runs-on: ubuntu-latest
15 |
16 | strategy:
17 | matrix:
18 | node-version: [10.x, 14.x]
19 |
20 | steps:
21 | - uses: actions/checkout@v2
22 | - name: Use Node.js ${{ matrix.node-version }}
23 | uses: actions/setup-node@v1
24 | with:
25 | node-version: ${{ matrix.node-version }}
26 | - name: Configure yarn cache path
27 | run: yarn config set cache-folder ~/.yarn-cache
28 | - name: Restore yarn cache
29 | uses: actions/cache@v2
30 | with:
31 | path: ~/.yarn-cache
32 | key: yarn-cache-${{ matrix.node-version }}
33 | restore-keys: |
34 | yarn-cache-
35 | - name: Restore node_modules
36 | uses: actions/cache@v2
37 | with:
38 | path: node_modules
39 | key: node-modules-${{ matrix.node-version }}-${{ hashFiles('yarn.lock') }}
40 | restore-keys: |
41 | node-modules-${{ matrix.node-version }}-
42 | node-modules-
43 | - name: Install dependencies
44 | run: yarn
45 | - name: Run linter, concat, minifier and tests
46 | run: yarn dist
47 |
--------------------------------------------------------------------------------
/.github/workflows/prepublish.yml:
--------------------------------------------------------------------------------
1 | # As an alternative to a manual push to the prepublish branch, this workflow can
2 | # be triggered on a release branch by assigning a special label to its pull
3 | # request in order to set the CD circus in motion.
4 |
5 | name: Prepublication staging
6 |
7 | on:
8 | pull_request:
9 | types: [labeled]
10 | # Would filter by branch here, but GH Actions wrongly decides not to
11 | # trigger the workflow if we do this.
12 |
13 | jobs:
14 | stage:
15 | runs-on: ubuntu-latest
16 | # Filtering by branch here instead. Credit due to @MiguelSavignano.
17 | # https://github.com/devmasx/merge-branch/issues/11
18 | if: contains(github.event.pull_request.head.ref, 'release/') || contains(github.event.pull_request.head.ref, 'hotfix/')
19 | steps:
20 | - uses: actions/checkout@v2
21 | - name: Merge into prepublish
22 | uses: devmasx/merge-branch@v1.3.0
23 | with:
24 | label_name: ready to launch
25 | target_branch: prepublish
26 | github_token: ${{ secrets.GITHUB_TOKEN }}
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | raw
2 | node_modules
3 | .DS_Store
4 | docs/*.css
5 | docs/*.html
6 | docs/public
7 | examples/*.css
8 | examples/*.html
9 | examples/public
10 | bower_components/*
11 | dist/
12 | /index.html
13 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "globals": {
3 | "_": true
4 | },
5 | "es3": true,
6 | "indent": 2,
7 | "camelcase": true,
8 | "eqnull": true,
9 | "forin": true,
10 | "newcap": true,
11 | "-W058": false
12 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ### Changelog
2 |
3 | #### 0.3.0
4 |
5 | * Contrib now requires Underscore 1.6.0 or higher.
6 | * Rename `partition` and `partitionAll` to `chunk` and `chunkAll` to avoid name conflicts with Underscore's `partition` - [#115][115]
7 | * Added `toQuery` and `fromQuery` - [#134][134]
8 | * Switch `always` to an alias of Underscore's `constant`. - [#132][132]
9 | * The combinators sub-library now supports method combinators - [#14][14]
10 |
11 | [115]:https://github.com/documentcloud/underscore-contrib/issues/115
12 | [134]:https://github.com/documentcloud/underscore-contrib/issues/134
13 | [132]:https://github.com/documentcloud/underscore-contrib/issues/132
14 | [14]:https://github.com/documentcloud/underscore-contrib/issues/14
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute to Underscore-contrib
2 |
3 | ## 1. Search the existing issues
4 |
5 | Before you open a ticket or send a pull request, [search](https://github.com/documentcloud/underscore-contrib/issues) for previous discussions about the same feature or issue. Add to the earlier ticket if you find one.
6 |
7 | ## 2. Fork the project
8 |
9 | Create your own fork of contrib where you can make your changes.
10 |
11 | ## 3. Install development dependencies
12 |
13 | Make sure you have [Node.js][node] and [Yarn][yarn] installed. Then install contrib's development dependencies with `yarn`. Note that these dependencies include the [Grunt CLI][cli] which we use for task automation.
14 |
15 | ## 4. Change the code
16 |
17 | Make your code changes, ensuring they adhere to the same [coding style as Underscore][style]. Do **not** edit the files in `dist/`.
18 |
19 | Make any necessary updates to the qUnit tests found in `test/`. Run `yarn test` to catch any test failures or lint issues. You can also use `yarn grunt watch:test` to get instant feedback each time you save a file.
20 |
21 | ## 5. Update the docs
22 |
23 | Make any necessary documentation updates in `docs/`. Do **not** edit `index.html` directly.
24 |
25 | After updating the docs, run `yarn tocdoc` to rebuild the `index.html` file. Visually inspect `index.html` in your browser to ensure the generated docs look nice.
26 |
27 | ## 6. Send a pull request
28 |
29 | Send a pull request to `documentcloud/underscore-contrib` for feedback.
30 |
31 | If modifications are necessary, make the changes and rerun `yarn test` or `yarn tocdoc` as needed.
32 |
33 | And hopefully your pull request will be merged by the core team! :-)
34 |
35 | [style]:https://github.com/documentcloud/underscore/blob/master/underscore.js
36 | [node]:http://nodejs.org/
37 | [yarn]:https://classic.yarnpkg.com/
38 | [cli]:http://gruntjs.com/getting-started#installing-the-cli
39 |
--------------------------------------------------------------------------------
/DEPLOYING.md:
--------------------------------------------------------------------------------
1 | # How to cut a new release for Underscore-contrib
2 |
3 | This is a checklist for repository maintainers. It covers all the steps involved in releasing a new version, including publishing to NPM and updating the `gh-pages` branch. We have tried to automate as many of these steps as possible using GitHub Actions. The workflows involved are in the (hidden) `.github` directory.
4 |
5 | A "regular" release includes new changes that were introduced to the `master` branch since the previous release. A *hotfix* release instead skips any such changes and only addresses urgent problems with the previous release.
6 |
7 |
8 | ## Steps
9 |
10 | 1. Ensure your local `master` branch descends from the previous release branch and that it is in a state that you want to release. **Exception:** for hotfixes, ensure you have an up-to-date local copy of the previous release branch instead.
11 | 2. Decide on the next version number, respecting [semver][semver]. For the sake of example we'll use `1.2.3` as a placeholder version throughout this checklist.
12 | 3. Create a `release/1.2.3` branch based on `master`. **Exception:** if this is a hotfix, start from the previous release branch instead and call it `hotfix/1.2.3`.
13 | 4. Bump the version number in the `package.json` and the `yarn.lock` by running the command `yarn version --no-git-tag-version`, with the extra flag `--major`, `--minor` or `--patch` depending on which part of the version number you're incrementing (e.g., `--patch` if you're bumping from 1.2.2 to 1.2.3). Note that you can configure `yarn` to always include the `--no-git-tag-version` flag so you don't have to type it every time.
14 | 5. Bump the version number in the source code and in `docs/index.md` accordingly.
15 | 6. Commit the changes from steps 4 and 5 with a commit message of the format `Bump the version to 1.2.3`.
16 | 7. Add more commits to extend the change log and to update any other documentation that might require it. Ensure that all documentation looks good.
17 | 8. Push the release branch and create a pull request against `master` (also if it is a hotfix).
18 | 9. At your option, have the release branch reviewed and add more commits to satisfy any change requests.
19 | 10. The "Continuous Integration" workflow will trigger automatically on the pull request. This runs the test suite against multiple environments. Wait for all checks to complete.
20 | 11. If any checks fail, add more commits to fix and repeat from step 10.
21 | 12. When all reviewers are satisfied and all checks pass, apply the "ready to launch" label to the pull request. This will trigger the "Prepublication staging" workflow, which will merge the release branch into the `prepublish` branch. Merge manually if the workflow fails for whatever reason. **Note:** do *not* merge into `master` yet.
22 | 13. The merging of new changes into `prepublish` will trigger the "Continuous Deployment" workflow, which is documented below. **Pay attention as the deployment workflow progresses.**
23 | 14. If the deployment workflow fails, identify the failing step and address as quickly as possible. Possible solution steps include:
24 | - Adding new commits to the release branch and repeating from step 12 (most likely if the docs fail to build for some reason).
25 | - Manually pushing new commits to the `prepublish` branch and repeating from step 13 (this might be needed in case of merge conflicts, although this is highly unlikely).
26 | - Manually running `yarn publish` (if something is wrong with the NPM registry or the authentication credentials).
27 | - Manually merging `prepublish` into `gh-pages` (in unlikely case of merge conflicts).
28 | - Manually tagging the release on `gh-pages`.
29 | 15. When the deployment workflow has completed, double-check that the new version was published to NPM, that the website was updated and that the repository contains a tag for the new release.
30 | 16. Finally merge the release branch into `master`, but keep the branch around for a few days in case you need to do a hotfix.
31 | 17. Delete the release branch. You can still restore it if necessary, by tracing the history graph two commits back from the release tag.
32 |
33 |
34 | ## Automated continuous deployment workflow
35 |
36 | This workflow is defined in `.github/workflows/cd.yml`. Note that roughly the first half of the steps in that file consists of basic boilerplate to check out the source and install dependencies.
37 |
38 | The publishing to NPM depends on a [secret][secrets] named `NPM_TOKEN`, representing the API token of the NPM account that owns the `underscore-contrib` package. Only admins of the documentcloud organization can access this setting.
39 |
40 | 1. Checkout the source, restore caches and install the dependencies.
41 | 2. Run `yarn grunt dist docco tocdoc`.
42 | 3. Commit the output of the previous step to the `prepublish` branch.
43 | 4. Publish to NPM.
44 | 5. Merge the `prepublish` branch into `gh-pages`.
45 | 6. Tag the tip of `gh-pages` with the version number in the `package.json`.
46 |
47 |
48 | [semver]: https://semver.org
49 | [secrets]: https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets
50 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 | grunt.loadNpmTasks("grunt-contrib-concat");
3 | grunt.loadNpmTasks("grunt-contrib-uglify");
4 | grunt.loadNpmTasks('grunt-contrib-qunit');
5 | grunt.loadNpmTasks('grunt-contrib-watch');
6 | grunt.loadNpmTasks('grunt-contrib-jshint');
7 | grunt.loadNpmTasks("grunt-docco");
8 | grunt.loadNpmTasks("grunt-tocdoc");
9 |
10 | grunt.initConfig({
11 | pkg: grunt.file.readJSON('package.json'),
12 |
13 | contribBanner:
14 | "// <%= 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 | "// > <%= pkg.name %> may be freely distributed under the <%= pkg.license %> license.\n\n",
19 |
20 | concat: {
21 | all: {
22 | src: "underscore.*.js",
23 | dest: "dist/underscore-contrib.js",
24 | options: { banner: "<%= contribBanner %>" }
25 | }
26 | },
27 |
28 | uglify: {
29 | all: {
30 | files: { "dist/underscore-contrib.min.js": "dist/underscore-contrib.js" },
31 | options: { banner: "<%= contribBanner %>" }
32 | }
33 | },
34 |
35 | qunit: {
36 | main: ['test/index.html'],
37 | concat: ['test/dist-concat.html'],
38 | min: ['test/dist-min.html']
39 | },
40 |
41 | jshint: {
42 | all: [
43 | "*.js",
44 | "test/*.js"
45 | ],
46 | options: {
47 | jshintrc: true
48 | }
49 | },
50 |
51 | watch: {
52 | test: {
53 | files: [
54 | "underscore.*.js",
55 | "test/*.js"
56 | ],
57 | tasks: ["test"]
58 | }
59 | },
60 |
61 | tocdoc: {
62 | api: {
63 | files: {
64 | 'index.html': ['docs/*.md', 'CHANGELOG.md']
65 | },
66 | options: {
67 | scripts: [
68 | 'test/vendor/underscore.js',
69 | 'dist/underscore-contrib.js'
70 | ]
71 | }
72 | }
73 | },
74 |
75 | docco: {
76 | docs: {
77 | src: ['docs/*.md'],
78 | options: {
79 | output: 'docs/'
80 | }
81 | },
82 | examples: {
83 | src: ['examples/*.md'],
84 | options: {
85 | output: 'examples/'
86 | }
87 | }
88 | }
89 | });
90 |
91 | grunt.registerTask('test', ['jshint', 'qunit:main']);
92 | grunt.registerTask('default', ['test']);
93 | grunt.registerTask('dist', ['test', 'concat', 'qunit:concat', 'uglify', 'qunit:min']);
94 | grunt.registerTask('doc', ['test', 'tocdoc']);
95 | };
96 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Jeremy Ashkenas, Michael Fogus, DocumentCloud and Investigative Reporters & Editors
2 |
3 | Permission is hereby granted, free of charge, to any person
4 | obtaining a copy of this software and associated documentation
5 | files (the "Software"), to deal in the Software without
6 | restriction, including without limitation the rights to use,
7 | copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the
9 | Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | underscore-contrib
2 | ==================
3 |
4 | The brass buckles on Underscore's utility belt -- a contributors' library for [Underscore](http://underscorejs.org/).
5 |
6 | Links
7 | -----
8 |
9 | * [Documentation](http://documentcloud.github.io/underscore-contrib/)
10 | * [Source repository](https://github.com/documentcloud/underscore-contrib)
11 | * [Tickets and bug reports](https://github.com/documentcloud/underscore-contrib/issues?state=open)
12 | * [Maintainer's website](http://www.fogus.me)
13 |
14 | Why underscore-contrib?
15 | -----------------------
16 |
17 | While Underscore provides a bevy of useful tools to support functional programming in JavaScript, it can't
18 | (and shouldn't) be everything to everyone. Underscore-contrib is intended as a home for functions that, for
19 | various reasons, don't belong in Underscore proper. In particular, it aims to be:
20 |
21 | * a home for functions that are limited in scope, but solve certain point problems, and
22 | * a proving ground for features that belong in Underscore proper, but need some advocacy and/or evolution
23 | (or devolution) to get them there.
24 |
25 | Use
26 | ---
27 |
28 | First, you’ll need Underscore. Then you can grab the relevant underscore-contrib libraries and simply add
29 | something
30 | like the following to your pages:
31 |
32 |
33 |
34 |
35 | At the moment there are no cross-contrib dependencies (i.e. each library can stand by itself), but that may
36 | change in the future.
37 |
38 | Contributing
39 | ------------
40 |
41 | There is still a lot of work to do around perf, documentation, examples, testing and distribution so any help
42 | in those areas is welcomed. Pull requests are accepted, but please search the [issues](https://github.com/documentcloud/underscore-contrib/issues)
43 | before proposing a new sub-contrib or addition. Additionally, all patches and proposals should have strong
44 | documentation, motivating cases and tests. It would be nice if we could not only provide useful tools built on
45 | Underscore, but also provide an educational experience for why and how one might use them.
46 |
47 | Other (potentially) useful sub-contribs include the following:
48 |
49 | * String utilities
50 | * Date/time utilities
51 | * Validators
52 | * Iterators
53 | * Generators
54 | * Promises
55 | * Monads
56 | * Currying
57 | * Laziness
58 | * Multimethods
59 |
60 | What do these mean? Well, that’s up for discussion. :-)
61 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "underscore-contrib",
3 | "version": "0.3.0",
4 | "main": "dist/underscore-contrib.min.js",
5 | "homepage": "https://github.com/documentcloud/underscore-contrib",
6 | "authors": [
7 | "fogus "
8 | ],
9 | "description": "The brass buckles on Underscore's utility belt",
10 | "keywords": [
11 | "underscore",
12 | "underscorejs",
13 | "documentcloud",
14 | "functional",
15 | "javascript"
16 | ],
17 | "license": "MIT",
18 | "ignore": [
19 | "**/.*",
20 | "node_modules",
21 | "bower_components",
22 | "test",
23 | "tests"
24 | ],
25 | "dependencies" : {
26 | "underscore" : ">=1.6.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # Underscore-contrib (0.3.0)
2 |
3 | > The brass buckles on Underscore's utility belt - a contributors' library for [Underscore](http://underscorejs.org/).
4 |
5 | ## Introduction
6 |
7 | ### Places
8 |
9 | * [Documentation](http://documentcloud.github.io/underscore-contrib/)
10 | * [Source repository](https://github.com/documentcloud/underscore-contrib)
11 | * [Tickets and bug reports](https://github.com/documentcloud/underscore-contrib/issues?state=open)
12 | * [Maintainer's website](http://www.fogus.me)
13 |
14 | ### Why underscore-contrib?
15 |
16 | While Underscore provides a bevy of useful tools to support functional programming in JavaScript, it can't
17 | (and shouldn't) be everything to everyone. Underscore-contrib is intended as a home for functions that, for
18 | various reasons, don't belong in Underscore proper. In particular, it aims to be:
19 |
20 | * a home for functions that are limited in scope, but solve certain point problems, and
21 | * a proving ground for features that belong in Underscore proper, but need some advocacy and/or evolution
22 | (or devolution) to get them there.
23 |
24 | ### Use
25 |
26 | #### In the Browser
27 |
28 | First, you'll need Underscore **version 1.6.0 or higher**. Then you can grab the relevant underscore-contrib sub-libraries and simply add something like
29 | the following to your pages:
30 |
31 | ```html
32 |
33 |
34 | ```
35 |
36 | At the moment there are no cross-contrib dependencies (i.e. each sub-library
37 | can stand by itself), but that may change in the future.
38 |
39 | #### In Node.js
40 |
41 | Using contrib in Node is very simple. Just install it with npm:
42 |
43 | ```
44 | npm install underscore-contrib --save
45 | ```
46 |
47 | Then require it within your project like so:
48 |
49 | ```javascript
50 | var _ = require('underscore-contrib');
51 | ```
52 |
53 | The `_` variable will be a copy of Underscore with contrib's methods already
54 | mixed in.
55 |
56 | ### License
57 |
58 | _.contrib is open sourced under the [MIT license](https://github.com/documentcloud/underscore-contrib/blob/master/LICENSE).
59 |
60 | ## Sub-libraries
61 |
62 | The _.contrib library currently contains a number of related capabilities, aggregated into the following files.
63 |
64 | - [underscore.array.builders](#array.builders) - functions to build arrays
65 | - [underscore.array.selectors](#array.selectors) - functions to take things from arrays
66 | - [underscore.collections.walk](#collections.walk) - functions to walk and transform nested JavaScript objects
67 | - [underscore.function.arity](#function.arity) - functions to manipulate and fix function argument arity
68 | - [underscore.function.combinators](#function.combinators) - functions to combine functions to make new functions
69 | - [underscore.function.iterators](#function.iterators) - functions to lazily produce, manipulate and consume sequence iterators
70 | - [underscore.function.predicates](#function.predicates) - functions that return `true` or `false` based on some criteria
71 | - [underscore.object.builders](#object.builders) - functions to build JavaScript objects
72 | - [underscore.object.selectors](#object.selectors) - functions to pick things from JavaScript objects
73 | - [underscore.util.existential](#util.existential) - functions that check for the existence or truthiness of JavaScript data types
74 | - [underscore.util.operators](#util.operators) - functions that wrap common (or missing) JavaScript operators
75 | - [underscore.util.strings](#util.strings) - functions to work with strings
76 | - [underscore.util.trampolines](#util.trampolines) - functions to facilitate calling functions recursively without blowing the stack
77 |
78 | The links above are to the annotated source code. Full-blown _.contrib documentation is in the works. Contributors welcomed.
79 |
80 |
--------------------------------------------------------------------------------
/docs/underscore.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:** `_.partitionBy(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 | --------------------------------------------------------------------------------
166 |
--------------------------------------------------------------------------------
/docs/underscore.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 | containsAtLeast: function(list, count, value)
15 | containsAtMost: function(list, count, value)
16 | _.walk.collect = _.walk.map;
17 |
--------------------------------------------------------------------------------
/docs/underscore.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/underscore.function.iterators.js.md:
--------------------------------------------------------------------------------
1 | ### function.iterators
2 |
3 | > Functions to iterate over collections.
4 |
5 | --------------------------------------------------------------------------------
6 |
7 | #### iterators.accumulate
8 |
9 | **Signature:** `_.iterators.accumulate(iter:Function, binaryFn:Function, initial:Any)`
10 |
11 | Returns a function that when called will iterate one step with `iter` and return
12 | the value currently accumulated by using `binaryFn`. The function will return `undefined` once all values have been iterated over.
13 |
14 | ```javascript
15 | var generalsIterator = _.iterators.List(["Hannibal", "Scipio"]);
16 |
17 | function countLetters(memo, element) {
18 | return memo + element.length;
19 | }
20 |
21 | var generalsAcc = _.iterators.accumulate(generalsIterator, countLetters, 0);
22 |
23 | generalsAcc();
24 | // => 8
25 |
26 | generalsAcc();
27 | // => 14
28 |
29 | generalsAcc();
30 | // => undefined
31 | ```
32 |
33 | --------------------------------------------------------------------------------
34 |
35 | #### iterators.accumulateWithReturn
36 |
37 | **Signature:** `_.iterators.accumulateWithReturn(iter:Function, binaryFn:Function, initial:Any)`
38 |
39 | Acts similarly to accumulate, except that `binaryFn` is expected to return an
40 | array of two elements. The value of the first element is given to the next run
41 | of `binaryFn`. The value of the second element is yielded by the iterator.
42 |
43 |
44 | ```javascript
45 | var fiveIter = _.iterators.List([1, 2, 3, 4, 5]);
46 |
47 | function adderWithMessage (state, element) {
48 | return [state + element, 'Total is ' + (state + element)];
49 | }
50 |
51 | var i = _.iterators.accumulateWithReturn(fiveIter, adderWithMessage, 0);
52 |
53 | i();
54 | // => "Total is 1"
55 |
56 | i();
57 | // => "Total is 3"
58 |
59 | i();
60 | // => "Total is 6"
61 | ```
62 |
63 | --------------------------------------------------------------------------------
64 |
65 | #### iterators.drop
66 |
67 | **Signature:** `_.iterators.drop(iter:Function[, numberToDrop:Number])`
68 |
69 | Given an iterator function `iter`, will return a new iterator which iterates
70 | over the same values as `iter`, except that the first `numberToDrop` values
71 | will be omitted. If `numberToDrop` is not provided, it will default to `1`.
72 |
73 | ```javascript
74 | var deityIter = _.iterators.List(["Zeus", "Apollo", "Athena", "Aphrodite"]);
75 |
76 | var goddessIter = _.iterators.drop(deityIter, 2);
77 |
78 | goddessIter();
79 | // => "Athena"
80 |
81 | goddessIter();
82 | // => "Aphrodite"
83 | ```
84 |
85 | --------------------------------------------------------------------------------
86 |
87 | #### iterators.foldl
88 |
89 | **Signature:** `_.iterators.foldl(iter:Function, binaryFn:Function[, seed:Any])`
90 |
91 | **Aliases:** `iterators.reduce`
92 |
93 | Boils down the values given by `iter` into a single value. The `seed` is the
94 | initial state. The `binaryFn` is given two arguments: the `seed` and the
95 | current value yielded by `iter`.
96 |
97 | ```javascript
98 | var sybylIter = _.iterators.List(["cumaean", "tiburtine"]);
99 |
100 | function commaString (a, b) { return a + ", " + b; }
101 |
102 | _.iterators.foldl(sybylIter, commaString);
103 | // => "cumaean, tiburtine"
104 | ```
105 |
106 | --------------------------------------------------------------------------------
107 |
108 | #### iterators.K
109 |
110 | **Signature:** `_.iterators.K(value:Any)`
111 |
112 | **Aliases:** `iterators.constant`
113 |
114 | Returns a function that when invoked will always return `value`.
115 |
116 | ```javascript
117 | var ceasar = _.iterators.K("Ceasar");
118 |
119 | ceasar();
120 | // => "ceasar"
121 | ```
122 |
123 | --------------------------------------------------------------------------------
124 |
125 | #### iterators.List
126 |
127 | **Signature:** `_.iterators.List(array:Array)`
128 |
129 | Returns an iterator that when invoked will iterate over the contents of `array`.
130 |
131 | ```javascript
132 | var triumvirIter = _.iterators.List(["Ceasar", "Pompey", "Crassus"]);
133 |
134 | triumvirIter();
135 | // => "Ceasar"
136 |
137 | triumvirIter();
138 | // => "Pompey"
139 |
140 | triumvirIter();
141 | // => "Crassus"
142 | ```
143 |
144 | --------------------------------------------------------------------------------
145 |
146 | #### iterators.map
147 |
148 | **Signature:** `_.iterators.map(iter:Function, unaryFn:Function)`
149 |
150 | Returns a new iterator function which on each iteration will return the result
151 | of running `iter`'s current value through `unaryFn`.
152 |
153 | ```javascript
154 | var notablesIter = _.iterators.List(["Socrates", "Plato"]);
155 |
156 | function postfixAthenian (val) {
157 | return val + ", Athenian";
158 | }
159 |
160 | var notableAtheniansIter = _.iterators.map(notablesIter, postfixAthenian);
161 |
162 | notableAtheniansIter();
163 | // => "Socrates, Athenian"
164 |
165 | notableAtheniansIter();
166 | // => "Plato, Athenian"
167 | ```
168 |
169 | --------------------------------------------------------------------------------
170 |
171 | #### iterators.mapcat
172 |
173 | **Signature:** `_.iterators.mapcat(iter:Function, unaryFn:Function)`
174 |
175 | Returns an iterator which is the result of flattening the contents of `iter`,
176 | and mapping the results with `unaryFn`.
177 |
178 | ```javascript
179 | function naturalSmallerThan (x) {
180 | return _.iterators.List(_.range(0, x));
181 | }
182 |
183 | var treeIter = _.iterators.Tree([1, [2]]);
184 |
185 | var smallerThanIter = _.iterators.mapcat(treeIter, naturalSmallerThan);
186 |
187 | smallerThanIter();
188 | // => 0
189 |
190 | smallerThanIter();
191 | // => 0
192 |
193 | smallerThanIter();
194 | // => 1
195 | ```
196 |
197 | --------------------------------------------------------------------------------
198 |
199 | #### iterators.numbers
200 |
201 | **Signature:** `_.iterators.numbers([start:Number])`
202 |
203 | Returns an iterator of integers which will begin with `start` and increment by
204 | one for each invocation. If `start` is not provided it will default to `1`.
205 |
206 | ```javascript
207 | var twoAndUp = _.iterators.numbers(2);
208 |
209 | twoAndUp();
210 | // => 2
211 |
212 | twoAndUp();
213 | // => 3
214 |
215 | twoAndUp();
216 | // => 4
217 | ```
218 |
219 | --------------------------------------------------------------------------------
220 |
221 | #### iterators.range
222 |
223 | **Signature:** `_.iterators.range([from:Number, to:Number, by:Number])`
224 |
225 | Returns an iterator whose values consist of numbers beginning with `from`, ending with `to`, in steps of size `by`.
226 |
227 | ```javascript
228 | var by5 = _.iterators.range(5, Infinity, 5);
229 |
230 | by5();
231 | // => 5
232 |
233 | by5();
234 | // => 10
235 |
236 | by5();
237 | // => 15
238 | ```
239 |
240 | --------------------------------------------------------------------------------
241 |
242 | #### iterators.reject
243 |
244 | **Signature:** `_.iterators.reject(iter:Function, unaryPredicatFn:Function)`
245 |
246 | Returns an iterator consisting of the values of `iter` which are not flagged
247 | `true` by `unaryPredicateFn`.
248 |
249 | ```javascript
250 | var philosophers = ["Anaximander", "Socrates", "Heraclitus"];
251 |
252 | var philosophersIter = _.iterators.List(philosophers);
253 |
254 | function isSocrates (val) {
255 | return val === "Socrates";
256 | }
257 |
258 | var preSocraticsIter = _.iterators.reject(philosophersIter, isSocrates);
259 |
260 | preSocraticsIter()
261 | // => "Anaximander"
262 |
263 | preSocraticsIter()
264 | // => "Heraclitus"
265 | ```
266 |
267 | --------------------------------------------------------------------------------
268 |
269 | #### iterators.select
270 |
271 | **Signature:** `_.iterators.select(iter:Function, unaryPredicatFn:Function)`
272 |
273 | **Aliases:** `iterators.find`, `iteraters.filter`
274 |
275 | Returns an iterator consisting of the values of `iter` which are flagged
276 | `true` by `unaryPredicateFn`.
277 |
278 | ```javascript
279 | var philosophers = ["Anaximander", "Socrates", "Heraclitus"];
280 |
281 | var philosophersIter = _.iterators.List(philosophers);
282 |
283 | function isSocrates (val) {
284 | return val === "Socrates";
285 | }
286 |
287 | var socraticIter = _.iterators.select(philosophersIter, isSocrates);
288 |
289 | socraticIter()
290 | // => "Socrates"
291 | ```
292 |
293 | --------------------------------------------------------------------------------
294 |
295 | #### iterators.slice
296 |
297 | **Signature:** `_.iterators.slice(iter:Function, numberToDrop:Number, numberToTake:Number)`
298 |
299 | Returns an iterator whose values consist of `iter`'s after removing
300 | `numberToDrop` from the head, and a maxiumum of `numberToTake` of the remaining.
301 | If `numberToTake` is not specified, all of `iter`'s remaining values will be
302 | used.
303 |
304 | ```javascript
305 | var emperors = ["Augustus", "Tiberius", "Caligula", "Claudius"];
306 |
307 | var emperorsIter = _.iterators.List(emperors);
308 |
309 | var middleEmperorsIter = _.iterators.slice(emperorsIter, 1, 2);
310 |
311 | middleEmperorsIter();
312 | // => "Tiberius"
313 |
314 | middleEmperorsIter();
315 | // => "Caligula"
316 |
317 | middleEmperorsIter();
318 | // => undefined
319 | ```
320 |
321 | --------------------------------------------------------------------------------
322 |
323 | #### iterators.take
324 |
325 | **Signature:** `_.iterators.take(iter:Function[, numberToTake:Number])`
326 |
327 | Returns an iterator consisting of the first `numberToTake` values yielded by
328 | `iter`. If `numberToTake` is not provided, it will default to `1`.
329 |
330 | ```javascript
331 | var byzantineEmperors = ["Constantine", "Constantius", "Constans"];
332 |
333 | var byzantineEmperorsIter = _.iterators.List(byzantineEmperors);
334 |
335 | var firstTwoEmperorsIter = _.iterators.take(byzantineEmperorsIter, 2);
336 |
337 | firstTwoEmperorsIter();
338 | // => "Constantine"
339 |
340 | firstTwoEmperorsIter();
341 | // => "Constantius"
342 |
343 | firstTwoEmperorsIter();
344 | // => undefined
345 | ```
346 |
347 | --------------------------------------------------------------------------------
348 |
349 | #### iterators.Tree
350 |
351 | **Signature:** `_.iterators.Tree(array:Array);`
352 |
353 | Returns an iterator that yields the individual values of a tree `array`.
354 |
355 | ```javascript
356 | var rulers = ["Augustus", ["Constantine"], ["Leo", ["Charlemagne"]]];
357 |
358 | var rulersIter = _.iterators.Tree(rulers);
359 |
360 | rulersIter();
361 | // => "Augustus"
362 |
363 | rulersIter();
364 | // => "Constantine"
365 |
366 | rulersIter();
367 | // => "Leo"
368 |
369 | rulersIter();
370 | // => "Charlemagne"
371 | ```
372 |
373 | --------------------------------------------------------------------------------
374 |
375 | #### iterators.unfold
376 |
377 | **Signature:** `_.iterators.unfold(seed:Any, unaryFn:Function)`
378 |
379 | ```javascript
380 | function greatify (val) {
381 | return val + " the Great";
382 | }
383 |
384 | var greatIter = _.iterators.unfold("Constantine", greatify);
385 |
386 | greatIter();
387 | // => "Constantine the Great"
388 |
389 | greatIter();
390 | // => "Constantine the Great the Great"
391 |
392 | greatIter();
393 | // => "Constantine the Great the Great the Great"
394 | ```
395 |
396 | --------------------------------------------------------------------------------
397 |
398 | #### iterators.unfoldWithReturn
399 |
400 | **Signature:** `_.iterators.unfold(seed:Any, unaryFn:Function)`
401 |
402 | Acts similarly to unfold, except that `unaryFn` is expected to return an array
403 | with two elements. The value of the first element is given to the next run of
404 | `unaryFn`. The value of the second element is yielded by the iterator.
405 |
406 | ```javascript
407 | var i = _.iterators.unfoldWithReturn(1, function(n) {
408 | return [n + 1, n * n];
409 | });
410 |
411 | i();
412 | // => 1
413 |
414 | i();
415 | // => 4
416 |
417 | i();
418 | // => 9
419 | ```
420 |
--------------------------------------------------------------------------------
/docs/underscore.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:** Underscore 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/underscore.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 | Returns a new object resulting from merging the passed objects. Objects
28 | are processed in order, so each will override properties of the same
29 | name occurring in earlier arguments.
30 |
31 | Returns `null` if called without arguments.
32 |
33 | ```javascript
34 | var a = {a: "alpha"};
35 | var b = {b: "beta"};
36 |
37 | var threeGreekLetters = _.merge(a, b, {g: "gamma"});
38 |
39 | a;
40 | // => {a: "alpha"}
41 |
42 | b;
43 | // => {b: "beta"}
44 |
45 | threeGreekLetters;
46 | // => { a: "alpha", b: "beta", g: "gamma" }
47 | ```
48 |
49 | --------------------------------------------------------------------------------
50 |
51 | #### renameKeys
52 |
53 | **Signature:** `_.renameKeys(obj:Object, keyMap:Object)`
54 |
55 | Takes an object (`obj`) and a map of keys (`keyMap`) and returns a new object
56 | where the keys of `obj` have been renamed as specified in `keyMap`.
57 |
58 | ```javascript
59 | _.renameKeys({ a: 1, b: 2 }, { a: "alpha", b: "beta" });
60 | // => { alpha: 1, beta: 2 }
61 | ```
62 |
63 | --------------------------------------------------------------------------------
64 |
65 | #### setPath
66 |
67 | **Signature:** `_.setPath(obj:Object, value:Any, ks:Array, defaultValue:Any)`
68 |
69 | Sets the value of a property at any depth in `obj` based on the path described
70 | by the `ks` array. If any of the properties in the `ks` path don't exist, they
71 | will be created with `defaultValue`.
72 |
73 | Note that the original object will *not* be mutated. Instead, `obj` will
74 | be cloned deeply.
75 |
76 |
77 |
78 | ```javascript
79 |
80 | var obj = {};
81 |
82 | var plotinusObj = _.setPath(obj, "Plotinus", ["Platonism", "Neoplatonism"], {});
83 |
84 | obj;
85 | // => {}
86 |
87 | plotinusObj;
88 | // => { Platonism: { Neoplatonism: "Plotinus" } }
89 |
90 | obj === plotinusObj;
91 | // => false;
92 |
93 | ```
94 |
95 | --------------------------------------------------------------------------------
96 |
97 | #### snapshot
98 |
99 | **Signature:** `_.snapshot(obj:Object)`
100 |
101 | Snapshots/clones an object deeply.
102 |
103 | ```javascript
104 | var schools = { plato: "Academy", aristotle: "Lyceum" };
105 |
106 | _.snapshot(schools);
107 | // => { plato: "Academy", aristotle: "Lyceum" }
108 |
109 | schools === _.snapshot(schools);
110 | // => false
111 | ```
112 |
113 | --------------------------------------------------------------------------------
114 |
115 | #### updatePath
116 |
117 | **Signature:** `_.updatePath(obj:Object, fun:Function, ks:Array, defaultValue:Any)`
118 |
119 | Updates the value at any depth in a nested object based on the path described by
120 | the `ks` array. The function `fun` is called with the current value and is
121 | expected to return a replacement value. If no keys are provided, then the
122 | object itself is presented to `fun`. If a property in the path is missing, then
123 | it will be created with `defaultValue`.
124 |
125 | Note that the original object will *not* be mutated. Instead, `obj` will
126 | be cloned deeply.
127 |
128 | ```javascript
129 | var imperialize = function (val) {
130 | if (val == "Republic") return "Empire";
131 | else return val;
132 | };
133 |
134 | _.updatePath({ rome: "Republic" }, imperialize, ["rome"]);
135 | // => { rome: "Empire" }
136 |
137 | var obj = { earth: { rome: "Republic" } };
138 | var imperialObj = _.updatePath(obj, imperialize, ["earth", "rome"]);
139 |
140 | imperialObj;
141 | // => { earth: { rome: "Empire" }}
142 |
143 | obj;
144 | // => { earth: { rome: "Republic" }}
145 |
146 | obj === imperialObj;
147 | // => false
148 | ```
149 |
150 | --------------------------------------------------------------------------------
151 |
152 | #### omitPath
153 |
154 | **Signature:** `_.omitPath(obj:Object, ks:String|Array)`
155 |
156 | Returns a copy of `obj` excluding the value represented by the `ks` path.
157 | Path may be given as an array or as a dot-separated string.
158 |
159 | ```javascript
160 | var test = {
161 | foo: true,
162 | bar: false,
163 | baz: 42,
164 | dada: {
165 | carlos: {
166 | pepe: 9
167 | },
168 | pedro: 'pedro'
169 | }
170 | };
171 |
172 | _.omitPath(test, 'dada.carlos.pepe');
173 | // => {foo: true, bar: false, baz: 42, dada: {carlos: {}, pedro: 'pedro'}}
174 | ```
175 |
--------------------------------------------------------------------------------
/docs/underscore.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 of key names or as a single string
50 | using JavaScript property access notation.
51 | Returns `undefined` if the path cannot be reached.
52 |
53 | ```javascript
54 | var countries = {
55 | greece: {
56 | athens: {
57 | playwright: "Sophocles"
58 | },
59 | notableFigures: ["Alexander", "Hippocrates", "Thales"]
60 | }
61 | }
62 | };
63 |
64 | _.getPath(countries, "greece.athens.playwright");
65 | // => "Sophocles"
66 |
67 | _.getPath(countries, "greece.sparta.playwright");
68 | // => undefined
69 |
70 | _.getPath(countries, ["greece", "athens", "playwright"]);
71 | // => "Sophocles"
72 |
73 | _.getPath(countries, ["greece", "sparta", "playwright"]);
74 | // => undefined
75 |
76 | _.getPath(countries, ["greece", "notableFigures", 1]);
77 | // => "Hippocrates"
78 |
79 | _.getPath(countries, "greece.notableFigures[2]");
80 | // => "Thales"
81 |
82 | _.getPath(countries, "greece['notableFigures'][2]")
83 | // => "Thales"
84 | ```
85 |
86 | --------------------------------------------------------------------------------
87 |
88 | #### hasPath
89 |
90 | **Signature:** `_.hasPath(obj:Object, ks:String|Array)`
91 |
92 | Returns a boolean indicating whether there is a property at the path described
93 | by the keys given. Keys may be given as an array of key names or as a single string
94 | using JavaScript property access notation.
95 |
96 | ```javascript
97 | var countries = {
98 | greece: {
99 | athens: {
100 | playwright: "Sophocles"
101 | },
102 | notableFigures: ["Alexander", "Hippocrates", "Thales"]
103 | }
104 | }
105 | };
106 |
107 | _.hasPath(countries, "greece.athens.playwright");
108 | // => true
109 |
110 | _.hasPath(countries, "greece.sparta.playwright");
111 | // => false
112 |
113 | _.hasPath(countries, ["greece", "athens", "playwright"]);
114 | // => true
115 |
116 | _.hasPath(countries, ["greece", "sparta", "playwright"]);
117 | // => false
118 |
119 | _.hasPath(countries, ["greece", "notableFigures", 1]);
120 | // => true
121 |
122 | _.hasPath(countries, "greece.notableFigures[2]");
123 | // => true
124 |
125 | _.hasPath(countries, "greece['notableFigures'][2]");
126 | // => true
127 |
128 | _.hasPath(countries, "greece.sparta[2]");
129 | // => false
130 | ```
131 |
132 | --------------------------------------------------------------------------------
133 |
134 | #### keysFromPath
135 |
136 | **Signature:** `_.keysFromPath(path:String)`
137 |
138 | Takes a string in JavaScript object path notation and returns an array of keys.
139 |
140 | ```javascript
141 | _.keysFromPath("rome.emperors[0]['first-name']");
142 | // => ["rome", "emperors", "0", "first-name"]
143 | ```
144 |
145 | --------------------------------------------------------------------------------
146 |
147 | #### kv
148 |
149 | **Signature:** `_.kv(obj:Object, key:String)`
150 |
151 | Returns the key/value pair for a given property in an object, undefined if not found.
152 |
153 | ```javascript
154 | var playAuthor = {
155 | "Medea": "Aeschylus"
156 | };
157 |
158 | _.kv(playAuthor, "Medea");
159 | // => ["Medea", "Aeschylus"]
160 |
161 | _.kv(playAuthor, "Hamlet");
162 | // => undefined
163 | ```
164 |
165 | --------------------------------------------------------------------------------
166 |
167 | #### omitWhen
168 |
169 | **Signature:** `_.omitWhen(obj, pred:Function)`
170 |
171 | Returns a copy of `obj` omitting any properties that the predicate (`pred`)
172 | function returns `true` for. The predicat function is invoked with each
173 | property value, like so: `pred(propValue)`.
174 |
175 | ```javascript
176 | var playwrights = {
177 | euripedes: "Greece",
178 | shakespere: "England"
179 | };
180 |
181 | _.omitWhen(playwrights, function (country) { return country == "England" });
182 | // => { euripedes: "Greece" }
183 | ```
184 |
185 | --------------------------------------------------------------------------------
186 |
187 | #### pickWhen
188 |
189 | **Signature:** `_.pickWhen(obj:Object, pred:Function)`
190 |
191 | Returns a copy of `obj` containing only properties that the predicate (`pred`)
192 | function returns `true` for. The predicate function is invoked with each
193 | property value, like so: `pred(propValue)`.
194 |
195 | ```javascript
196 | var playwrights = {
197 | euripedes: "Greece",
198 | shakespere: "England"
199 | };
200 |
201 | _.pickWhen(playwrights, function (country) { return country == "England" });
202 | // => { shakespeare: "England" }
203 | ```
204 |
205 | --------------------------------------------------------------------------------
206 |
207 | #### selectKeys
208 |
209 | **Signature:** `_.selectKeys(obj:Object, ks:Array);
210 |
211 | Returns a copy of `obj` containing only the properties listed in the `ks` array.
212 |
213 | ```javascript
214 | var philosopherCities = {
215 | Philo: "Alexandria",
216 | Plato: "Athens",
217 | Plotinus: "Rome"
218 | }
219 |
220 | _.selectKeys(philosopherCities, ["Plato", "Plotinus"]);
221 | // => { Plato: "Athens", Plotinus: "Rome" }
222 | ```
223 |
--------------------------------------------------------------------------------
/docs/underscore.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/underscore.util.operators.js.md:
--------------------------------------------------------------------------------
1 | ### util.operators
2 |
3 | > Functions which wrap JavaScript's operators.
4 |
5 | --------------------------------------------------------------------------------
6 |
7 | #### add
8 |
9 | **Signature:** `_.add(value:Number, value:Number[, value:Number...])`, `_.add(values:Array)`
10 |
11 | Returns the sum of the arguments.
12 |
13 | ```javascript
14 | _.add(1, 2, 3, 4);
15 | // => 10
16 |
17 | _.add([1, 2, 3, 4]);
18 | // => 10
19 | ```
20 |
21 | --------------------------------------------------------------------------------
22 |
23 | #### bitwiseAnd
24 |
25 | **Signature:** `_.bitwiseAnd(value:Any, value:Any[, value:Any...])`, `_.bitwiseAnd(values:Array)`
26 |
27 | Returns the result of using the `&` operator on the arguments.
28 |
29 | ```javascript
30 | _.bitwiseAnd(1, 3);
31 | // => 1
32 |
33 | _.bitwiseAnd(1, 3, 2);
34 | // => 0
35 |
36 | _.bitwiseAnd([1, 3, 2]);
37 | // => 0
38 | ```
39 |
40 | --------------------------------------------------------------------------------
41 |
42 | #### bitwiseLeft
43 |
44 | **Signature:** `_.bitwiseLeft(value:Any, value:Any[, value:Any...])`, `_.bitwiseLeft(values:Array)`
45 |
46 | Returns the result of using the `<<` operator on the arguments.
47 |
48 | ```javascript
49 | _.bitwiseLeft(1, 3);
50 | // => 8
51 |
52 | _.bitwiseLeft(1, 3, 2);
53 | // => 32
54 |
55 | _.bitwiseLeft([1, 3, 2]);
56 | // => 32
57 | ```
58 |
59 | --------------------------------------------------------------------------------
60 |
61 | #### bitwiseRight
62 |
63 | **Signature:** `_.bitwiseRight(value:Any, value:Any[, value:Any...])`, `_.bitwiseRight(values:Array)`
64 |
65 | Returns the result of using the `>>` operator on the arguments.
66 |
67 | ```javascript
68 | _.bitwiseRight(3, 1);
69 | // => 1
70 |
71 | _.bitwiseRight(3, 1, 3);
72 | // => 0
73 |
74 | _.bitwiseRight([3, 1, 3]);
75 | // => 0
76 | ```
77 |
78 | --------------------------------------------------------------------------------
79 |
80 | #### bitwiseNot
81 |
82 | **Signature:** `_.bitwiseNot(value:Any)`
83 |
84 | Returns the result of using the `~` operator on the value.
85 |
86 | ```javascript
87 | _.bitwiseNot(1);
88 | // => -2
89 |
90 | _.bitwiseNot(2);
91 | // => -3
92 | ```
93 |
94 | --------------------------------------------------------------------------------
95 |
96 | #### bitwiseOr
97 |
98 | **Signature:** `_.bitwiseOr(value:Any, value:Any[, value:Any...])`, `_.bitwiseOr(values:Array)`
99 |
100 | Returns the result of using the `|` operator on the arguments.
101 |
102 | ```javascript
103 | _.bitwiseOr(1, 3);
104 | // => 3
105 |
106 | _.bitwiseOr(1, 3, 4);
107 | // => 7
108 |
109 | _.bitwiseOr([1, 3, 4]);
110 | // => 7
111 | ```
112 |
113 | --------------------------------------------------------------------------------
114 |
115 | #### bitwiseXor
116 |
117 | **Signature:** `_.bitwiseXor(value:Any, value:Any[, value:Any...])`,`_.bitwiseXor(values:Array)`
118 |
119 | Returns the result of using the `^` operator on the arguments.
120 |
121 | ```javascript
122 | _.bitwiseXor(1, 3);
123 | // => 2
124 |
125 | _.bitwiseXor(1, 3, 3);
126 | // => 1
127 |
128 | _.bitwiseXor([1, 3, 3]);
129 | // => 1
130 | ```
131 |
132 | --------------------------------------------------------------------------------
133 |
134 | #### bitwiseZ
135 |
136 | **Signature:** `_.bitwiseZ(value:Any, value:Any[, value:Any...])`, `_.bitwiseZ(values:Array)`
137 |
138 | Returns the result of using the `>>>` operator on the arguments.
139 |
140 | ```javascript
141 | _.bitwiseZ(72, 32);
142 | // => 72
143 |
144 | _.bitwiseZ(72, 32, 2);
145 | // => 18
146 |
147 | _.bitwiseZ([72, 32, 2]);
148 | // => 18
149 | ```
150 |
151 | --------------------------------------------------------------------------------
152 |
153 | #### dec
154 |
155 | **Signature:** `_.dec(value:Number)`
156 |
157 | Returns the result of decrementing the value by `1`.
158 |
159 | ```javascript
160 | _.dec(2);
161 | // => 1
162 | ```
163 |
164 | --------------------------------------------------------------------------------
165 |
166 | #### div
167 |
168 | **Signature:** `_.div(value:Number, value:Number[, value:Number...])`, `_.div(values:Array)`
169 |
170 | Returns the quotient of the arguments.
171 |
172 | ```javascript
173 | _.div(8, 2);
174 | // => 4
175 |
176 | _.div(8, 2, 2);
177 | // => 2
178 |
179 | _.div([8, 2, 2]);
180 | // => 2
181 | ```
182 |
183 | --------------------------------------------------------------------------------
184 |
185 | #### eq
186 |
187 | **Signature:** `_.eq(value:Any, value:Any[, value:Any...])`,`_.eq(values:Array)`
188 |
189 | Compares the arguments with loose equality (`==`).
190 |
191 | ```javascript
192 | _.eq(1, "1");
193 | // => true
194 |
195 | _.eq(1, 15);
196 | // => false
197 |
198 | _.eq(1, true, "1");
199 | // => true
200 |
201 | _.eq(1, 1, 15);
202 | // => false
203 |
204 | _.eq([1, 1, 15]);
205 | // => false
206 | ```
207 |
208 | --------------------------------------------------------------------------------
209 |
210 | #### gt
211 |
212 | **Signature:** `_.gt(value:Any, value:Any[, value:Any...])`, `_.gt(values:Array)`
213 |
214 | Checks whether each argument is greater than the previous argument.
215 |
216 | ```javascript
217 | _.gt(1, 2);
218 | // => true
219 |
220 | _.gt(1, 2, 3);
221 | // => true
222 |
223 | _.gt(1, 6, 2);
224 | // => false
225 |
226 | _.gt([1, 6, 2]);
227 | // => false
228 | ```
229 |
230 | --------------------------------------------------------------------------------
231 |
232 | #### gte
233 |
234 | **Signature:** `_.gte(value:Any, value:Any[, value:Any...])`, `_.gte(values:Array)`
235 |
236 | Checks whether each argument is greater than or equal to the previous argument.
237 |
238 | ```javascript
239 | _.gte(1, 2);
240 | // => true
241 |
242 | _.gte(1, 1, 3);
243 | // => true
244 |
245 | _.gte(1, 6, 2);
246 | // => false
247 |
248 | _.gte([1, 6, 2]);
249 | // => false
250 |
251 | ```
252 |
253 | --------------------------------------------------------------------------------
254 |
255 | #### inc
256 |
257 | **Signature:** `_.inc(value:Number)`
258 |
259 | Returns the result of incrementing the value by `1`.
260 |
261 | ```javascript
262 | _.inc(2);
263 | // => 3
264 | ```
265 |
266 | --------------------------------------------------------------------------------
267 |
268 | #### lt
269 |
270 | **Signature:** `_.lt(value:Any, value:Any[, value:Any...])`, `_.lt(values:Array)`
271 |
272 | Checks whether each argument is less than the previous argument.
273 |
274 | ```javascript
275 | _.lt(2, 1);
276 | // => true
277 |
278 | _.lt(2, 1, 0);
279 | // => true
280 |
281 | _.lt(2, 1, 12);
282 | // => false
283 |
284 | _.lt([2, 1, 12]);
285 | // => false
286 | ```
287 |
288 | --------------------------------------------------------------------------------
289 |
290 | #### lte
291 |
292 | **Signature:** `_.lte(value:Any, value:Any[, value:Any...])`, `_.lte(values:Array)`
293 |
294 | Checks whether each argument is less than or equal to the previous argument.
295 |
296 | ```javascript
297 | _.lte(2, 1);
298 | // => true
299 |
300 | _.lte(2, 1, 1);
301 | // => true
302 |
303 | _.lte(2, 1, 12);
304 | // => false
305 |
306 | _.lte([2, 1, 12]);
307 | // => false
308 |
309 | ```
310 |
311 | --------------------------------------------------------------------------------
312 |
313 | #### mul
314 |
315 | **Signature:** `_.mul(value:Number, value:Number[, value:Number...])`, `_.mul(values:Array)`
316 |
317 | Returns the product of the arguments.
318 |
319 | ```javascript
320 | _.mul(1, 2, 3, 4);
321 | // => 24
322 |
323 | _.mul([1, 2, 3, 4]);
324 | // => 24
325 | ```
326 |
327 | --------------------------------------------------------------------------------
328 |
329 | #### mod
330 |
331 | **Signature:** `_.mod(dividend:Number, divisor:Number)`
332 |
333 | Returns the remainder of dividing `dividend` by `divisor`.
334 |
335 | ```javascript
336 | _.mod(26, 5);
337 | // => 1
338 |
339 | _.mod(14, 3);
340 | // => 2
341 | ```
342 |
343 | --------------------------------------------------------------------------------
344 |
345 | #### neg
346 |
347 | **Signature:** `_.neg(num:Number)`
348 |
349 | Returns a new number with the opposite sign value of `num`.
350 |
351 | ```javascript
352 | _.neg(5);
353 | // => -5
354 |
355 | _.neg(-3);
356 | // => 3
357 | ```
358 |
359 | --------------------------------------------------------------------------------
360 |
361 | #### neq
362 |
363 | **Signature:** `_.neq(value:Any, value:Any[, value:Any...])`, `_.neq(values:Array)`
364 |
365 | Checks whether each argument is not equal to the previous argument, using loose
366 | inequality (`!=`).
367 |
368 | ```javascript
369 | _.neq(2, 1);
370 | // => true
371 |
372 | _.neq(2, 1, 1);
373 | // => true
374 |
375 | _.neq(1, 1);
376 | // => false
377 |
378 | _.neq([1, 1]);
379 | // => false
380 |
381 | ```
382 |
383 | --------------------------------------------------------------------------------
384 |
385 | #### not
386 |
387 | **Signature:** `_.not(value:Any)`
388 |
389 | Returns a boolean which is the opposite of the truthiness of the original value.
390 |
391 | ```javascript
392 | _.not(0);
393 | // => true
394 |
395 | _.not(1);
396 | // => false
397 |
398 | _.not(true);
399 | // => false
400 |
401 | _.not(false);
402 | // => true
403 |
404 | _.not({});
405 | // => false
406 |
407 | _.not(null);
408 | // => true
409 | ```
410 |
411 | --------------------------------------------------------------------------------
412 |
413 | #### seq
414 |
415 | **Signature:** `_.seq(value:Any, value:Any[, value:Any...])`, `_.seq(values:Array)`
416 |
417 | Checks whether the arguments are strictly equal (`===`) to each other.
418 |
419 | ```javascript
420 | _.seq(2, 2);
421 | // => true
422 |
423 | _.seq(2, "2");
424 | // => false
425 |
426 | _.seq(2, 2, 2);
427 | // => true
428 |
429 | _.seq([2, 2, 2]);
430 | // => true
431 |
432 | ```
433 |
434 | --------------------------------------------------------------------------------
435 |
436 | #### sneq
437 |
438 | **Signature:** `_.sneq(value:Any, value:Any[, value:Any...])`, `_.sneq(values:Array)`
439 |
440 | Checks whether the arguments are strictly not equal (`!==`) to each other.
441 |
442 | ```javascript
443 | _.sneq(2, 2);
444 | // => false
445 |
446 | _.sneq(2, "2");
447 | // => true
448 |
449 | _.sneq(2, 2, 2);
450 | // => false
451 |
452 | _.sneq([2, 2, 2]);
453 | // => false
454 |
455 | ```
456 |
457 | --------------------------------------------------------------------------------
458 |
459 | #### sub
460 |
461 | **Signature:** `_.sub(value:Number, value:Number[, value:Number...])`, `_.sub(values:Array)`
462 |
463 | Returns the difference of the arguments.
464 |
465 | ```javascript
466 | _.sub(10, 3);
467 | // => 7
468 |
469 | _.sub(10, 3, 5);
470 | // => 2
471 |
472 | _.sub([10, 3, 5]);
473 | // => 2
474 | ```
--------------------------------------------------------------------------------
/docs/underscore.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 | #### strContains
61 |
62 | **Signature:** `_.strContains(str:String, search:String)`
63 |
64 | Reports whether a string contains a search string.
65 |
66 | ```javascript
67 | _.strContains("Acropolis", "polis");
68 | // => true
69 | ```
70 |
71 | --------------------------------------------------------------------------------
72 |
73 | #### toDash
74 |
75 | **Signature:** `_.toDash(string:String)`
76 |
77 | Converts a camel case string to a dashed string. Opposite of [camelCase](#camelcase).
78 |
79 | ```javascript
80 | _.toDash("thisIsSparta");
81 | // => "this-is-sparta"
82 | ```
83 |
84 | --------------------------------------------------------------------------------
85 |
86 | #### toQuery
87 |
88 | **Signature:** `_.toQuery(obj:Object)`
89 |
90 | Takes an object and converts it into an equivalent URL query string. Opposite
91 | of [fromQuery](#fromquery).
92 |
93 | ```javascript
94 | _.toQuery({ forms: { perfect: "circle", imperfect: "square" } });
95 | // => "forms%5Bperfect%5D=circle&forms%5Bimperfect%5D=square"
96 | ```
97 |
98 | --------------------------------------------------------------------------------
--------------------------------------------------------------------------------
/docs/underscore.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(Math.abs(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 | --------------------------------------------------------------------------------
54 |
--------------------------------------------------------------------------------
/examples/walk-examples.js.md:
--------------------------------------------------------------------------------
1 | Examples for _.walk
2 | ===================
3 |
4 | The _.walk module (underscore.collections.walk.js) provides implementations of
5 | the [Underscore collection functions](http://underscorejs.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 [Underscore's 'each' function][each]. For example, take
15 | a simple tree:
16 |
17 | [each]: http://underscorejs.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 | [Underscore collection functions](http://underscorejs.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(_.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 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | require('./underscore.array.builders');
2 | require('./underscore.array.selectors');
3 | require('./underscore.collections.walk');
4 | require('./underscore.function.arity');
5 | require('./underscore.function.combinators');
6 | require('./underscore.function.dispatch');
7 | require('./underscore.function.iterators');
8 | require('./underscore.function.predicates');
9 | require('./underscore.object.builders');
10 | require('./underscore.object.selectors');
11 | require('./underscore.util.existential');
12 | require('./underscore.util.operators');
13 | require('./underscore.util.strings');
14 | require('./underscore.util.trampolines');
15 |
16 | module.exports = require('underscore');
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "underscore-contrib",
3 | "version": "0.3.0",
4 | "main": "index.js",
5 | "dependencies": {
6 | "underscore": "1.10.2"
7 | },
8 | "devDependencies": {
9 | "grunt": "^1.5.3",
10 | "grunt-cli": "^1.3.2",
11 | "grunt-contrib-concat": "1.0.1",
12 | "grunt-contrib-jshint": "^2.1.0",
13 | "grunt-contrib-qunit": "^4.0.0",
14 | "grunt-contrib-uglify": "^5.0.0",
15 | "grunt-contrib-watch": "^1.1.0",
16 | "grunt-docco": "~0.5.0",
17 | "grunt-tocdoc": "0.1.1",
18 | "jquery": "3.5.1",
19 | "jslitmus": "^0.1.0",
20 | "qunit": "^2.11.0"
21 | },
22 | "repository": {
23 | "type": "git",
24 | "url": "https://github.com/documentcloud/underscore-contrib.git"
25 | },
26 | "license": "MIT",
27 | "author": {
28 | "name": "Fogus",
29 | "email": "me@fogus.me",
30 | "url": "http://www.fogus.me"
31 | },
32 | "scripts": {
33 | "test": "grunt test",
34 | "dist": "grunt dist",
35 | "tocdoc": "grunt tocdoc",
36 | "docco": "grunt docco"
37 | },
38 | "homepage": "https://github.com/documentcloud/underscore-contrib"
39 | }
40 |
--------------------------------------------------------------------------------
/test/array.selectors.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 |
3 | QUnit.module("underscore.array.selectors");
4 |
5 | QUnit.test("second", function(assert) {
6 | var a = [1,2,3,4,5];
7 |
8 | assert.equal(_.second(a), 2, 'should retrieve the 2nd element in an array');
9 | assert.deepEqual(_.second(a, 5), [2,3,4,5], 'should retrieve all but the first element in an array');
10 | assert.deepEqual(_.map([a,_.rest(a)], _.second), [2,3], 'should be usable in _.map');
11 | });
12 |
13 | QUnit.test("third", function(assert) {
14 | var a = [1,2,3,4,5];
15 |
16 | assert.equal(_.third(a), 3, 'should retrieve the 3rd element in an array');
17 | assert.deepEqual(_.third(a, 5), [3,4,5], 'should retrieve all but the first and second element in an array');
18 | assert.deepEqual(_.map([a,_.rest(a)], _.third), [3,4], 'should be usable in _.map');
19 | });
20 |
21 | QUnit.test("takeWhile", function(assert) {
22 | var isNeg = function(n) { return n < 0; };
23 |
24 | assert.deepEqual(_.takeWhile([-2,-1,0,1,2], isNeg), [-2,-1], 'should take elements until a function goes truthy');
25 | assert.deepEqual(_.takeWhile([1,-2,-1,0,1,2], isNeg), [], 'should take elements until a function goes truthy');
26 | });
27 |
28 | QUnit.test("dropWhile", function(assert) {
29 | var isNeg = function(n) { return n < 0; };
30 |
31 | assert.deepEqual(_.dropWhile([-2,-1,0,1,2], isNeg), [0,1,2], 'should drop elements until a function goes truthy');
32 | assert.deepEqual(_.dropWhile([0,1,2], isNeg), [0,1,2], 'should drop elements until a function goes truthy');
33 | assert.deepEqual(_.dropWhile([-2,-1], isNeg), [], 'should drop elements until a function goes truthy');
34 | assert.deepEqual(_.dropWhile([1,-2,-1,0,1,2], isNeg), [1,-2,-1,0,1,2], 'should take elements until a function goes truthy');
35 | assert.deepEqual(_.dropWhile([], isNeg), [], 'should handle empty arrays');
36 | });
37 |
38 | QUnit.test("splitWith", function(assert) {
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 | assert.deepEqual(_.splitWith(a, lessEq3p), [[1,2,3], [4,5]], 'should split an array when a function goes false');
44 | assert.deepEqual(_.splitWith(a, lessEq3p$), [[1,2,3], [4,5]], 'should split an array when a function goes false');
45 | assert.deepEqual(_.splitWith([], lessEq3p$), [[],[]], 'should split an empty array into two empty arrays');
46 | });
47 |
48 | QUnit.test("partitionBy", function(assert) {
49 | var a = [1, 2, null, false, undefined, 3, 4];
50 |
51 | assert.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 | QUnit.test("best", function(assert) {
55 | var a = [1,2,3,4,5];
56 |
57 | assert.deepEqual(_.best(a, function(x,y) { return x > y; }), 5, 'should identify the best value based on criteria');
58 | });
59 |
60 | QUnit.test("keep", function(assert) {
61 | var a = _.range(10);
62 | var eveny = function(e) { return (_.isEven(e)) ? e : undefined; };
63 |
64 | assert.deepEqual(_.keep(a, eveny), [0,2,4,6,8], 'should keep only even numbers in a range tagged with null fails');
65 | assert.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 | QUnit.test("nth", function(assert) {
69 | var a = ['a','b','c'];
70 | var b = [['a'],['b'],[]];
71 |
72 | assert.equal(_.nth(a,0), 'a', 'should return the element at a given index into an array');
73 | assert.equal(_.nth(a,100), undefined, 'should return undefined if out of bounds');
74 | assert.deepEqual(_.map(b,function(e) { return _.nth(e,0); }), ['a','b',undefined], 'should be usable in _.map');
75 | });
76 |
77 | QUnit.test("nths", function(assert) {
78 | var a = ['a','b','c', 'd'];
79 |
80 | assert.deepEqual(_.nths(a,1), ['b'], 'should return the element at a given index into an array');
81 | assert.deepEqual(_.nths(a,1,3), ['b', 'd'], 'should return the elements at given indices into an array');
82 | assert.deepEqual(_.nths(a,1,5,3), ['b', undefined, 'd'], 'should return undefined if out of bounds');
83 |
84 | assert.deepEqual(_.nths(a,[1]), ['b'], 'should return the element at a given index into an array');
85 | assert.deepEqual(_.nths(a,[1,3]), ['b', 'd'], 'should return the elements at given indices into an array');
86 | assert.deepEqual(_.nths(a,[1,5,3]), ['b', undefined, 'd'], 'should return undefined if out of bounds');
87 | });
88 |
89 | QUnit.test("valuesAt", function(assert) {
90 | assert.equal(_.valuesAt, _.nths, 'valuesAt should be alias for nths');
91 | });
92 |
93 | QUnit.test("binPick", function(assert) {
94 | var a = ['a','b','c', 'd'];
95 |
96 | assert.deepEqual(_.binPick(a, false, true), ['b'], 'should return the element at a given index into an array');
97 | assert.deepEqual(_.binPick(a, false, true, false, true), ['b', 'd'], 'should return the elements at given indices into an array');
98 | assert.deepEqual(_.binPick(a, false, true, false, true, true), ['b', 'd', undefined], 'should return undefined if out of bounds');
99 |
100 | assert.deepEqual(_.binPick(a, [false, true]), ['b'], 'should return the element at a given index into an array');
101 | assert.deepEqual(_.binPick(a, [false, true, false, true]), ['b', 'd'], 'should return the elements at given indices into an array');
102 | assert.deepEqual(_.binPick(a, [false, true, false, true, true]), ['b', 'd', undefined], 'should return undefined if out of bounds');
103 | });
104 | });
105 |
106 |
--------------------------------------------------------------------------------
/test/collections.walk.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 |
3 | QUnit.module("underscore.collections.walk");
4 |
5 | var getSimpleTestTree = function() {
6 | return {
7 | val: 0,
8 | l: { val: 1, l: { val: 2 }, r: { val: 3 } },
9 | r: { val: 4, l: { val: 5 }, r: { val: 6 } }
10 | };
11 | };
12 |
13 | var getMixedTestTree = function() {
14 | return {
15 | current:
16 | { city: 'Munich', aliases: ['Muenchen'], population: 1378000 },
17 | previous: [
18 | { city: 'San Francisco', aliases: ['SF', 'San Fran'], population: 812826 },
19 | { city: 'Toronto', aliases: ['TO', 'T-dot'], population: 2615000 }
20 | ]
21 | };
22 | };
23 |
24 | var getArrayValues = function() {
25 | return ["a", "a", "a", "a", "b", "c", "d", "e" ];
26 | };
27 |
28 | QUnit.test("basic", function(assert) {
29 | // Updates the value of `node` to be the sum of the values of its subtrees.
30 | // Ignores leaf nodes.
31 | var visitor = function(node) {
32 | if (node.l && node.r)
33 | node.val = node.l.val + node.r.val;
34 | };
35 |
36 | var tree = getSimpleTestTree();
37 | _.walk.postorder(tree, visitor);
38 | assert.equal(tree.val, 16, 'should visit subtrees first');
39 |
40 | tree = getSimpleTestTree();
41 | _.walk.preorder(tree, visitor);
42 | assert.equal(tree.val, 5, 'should visit subtrees after the node itself');
43 | });
44 |
45 | QUnit.test("circularRefs", function(assert) {
46 | var tree = getSimpleTestTree();
47 | tree.l.l.r = tree;
48 | assert.throws(function() { _.walk.preorder(tree, _.identity); }, TypeError, 'preorder throws an exception');
49 | assert.throws(function() { _.walk.postrder(tree, _.identity); }, TypeError, 'postorder throws an exception');
50 |
51 | tree = getSimpleTestTree();
52 | tree.r.l = tree.r;
53 | assert.throws(function() { _.walk.preorder(tree, _.identity); }, TypeError, 'exception for a self-referencing node');
54 | });
55 |
56 | QUnit.test("simpleMap", function(assert) {
57 | var visitor = function(node, key, parent) {
58 | if (_.has(node, 'val')) return node.val;
59 | if (key !== 'val') throw new Error('Leaf node with incorrect key');
60 | return this.leafChar || '-';
61 | };
62 | var visited = _.walk.map(getSimpleTestTree(), _.walk.preorder, visitor).join('');
63 | assert.equal(visited, '0-1-2-3-4-5-6-', 'pre-order map');
64 |
65 | visited = _.walk.map(getSimpleTestTree(), _.walk.postorder, visitor).join('');
66 | assert.equal(visited, '---2-31--5-640', 'post-order map');
67 |
68 | var context = { leafChar: '*' };
69 | visited = _.walk.map(getSimpleTestTree(), _.walk.preorder, visitor, context).join('');
70 | assert.equal(visited, '0*1*2*3*4*5*6*', 'pre-order with context');
71 |
72 | visited = _.walk.map(getSimpleTestTree(), _.walk.postorder, visitor, context).join('');
73 | assert.equal(visited, '***2*31**5*640', 'post-order with context');
74 |
75 | if (document.querySelector) {
76 | var root = document.querySelector('#map-test');
77 | var ids = _.walk.map(root, _.walk.preorder, function(el) { return el.id; });
78 | assert.deepEqual(ids, ['map-test', 'id1', 'id2'], 'preorder map with DOM elements');
79 |
80 | ids = _.walk.map(root, _.walk.postorder, function(el) { return el.id; });
81 | assert.deepEqual(ids, ['id1', 'id2', 'map-test'], 'postorder map with DOM elements');
82 | }
83 | });
84 |
85 | QUnit.test("mixedMap", function(assert) {
86 | var visitor = function(node, key, parent) {
87 | return _.isString(node) ? node.toLowerCase() : null;
88 | };
89 |
90 | var tree = getMixedTestTree();
91 | var preorderResult = _.walk.map(tree, _.walk.preorder, visitor);
92 | assert.equal(preorderResult.length, 19, 'all nodes are visited');
93 | assert.deepEqual(_.reject(preorderResult, _.isNull),
94 | ['munich', 'muenchen', 'san francisco', 'sf', 'san fran', 'toronto', 'to', 't-dot'],
95 | 'pre-order map on a mixed tree');
96 |
97 | var postorderResult = _.walk.map(tree, _.walk.postorder, visitor);
98 | assert.deepEqual(preorderResult.sort(), postorderResult.sort(), 'post-order map on a mixed tree');
99 |
100 | tree = [['foo'], tree];
101 | var result = _.walk.map(tree, _.walk.postorder, visitor);
102 | assert.deepEqual(_.difference(result, postorderResult), ['foo'], 'map on list of trees');
103 | });
104 |
105 | QUnit.test("pluck", function(assert) {
106 | var tree = getSimpleTestTree();
107 | tree.val = { val: 'z' };
108 |
109 | var plucked = _.walk.pluckRec(tree, 'val');
110 | assert.equal(plucked.shift(), tree.val);
111 | assert.equal(plucked.join(''), 'z123456', 'pluckRec is recursive');
112 |
113 | plucked = _.walk.pluck(tree, 'val');
114 | assert.equal(plucked.shift(), tree.val);
115 | assert.equal(plucked.join(''), '123456', 'regular pluck is not recursive');
116 |
117 | tree.l.r.foo = 42;
118 | assert.equal(_.walk.pluck(tree, 'foo'), 42, 'pluck a value from deep in the tree');
119 |
120 | tree = getMixedTestTree();
121 | assert.deepEqual(_.walk.pluck(tree, 'city'), ['Munich', 'San Francisco', 'Toronto'], 'pluck from a mixed tree');
122 | tree = [tree, { city: 'Loserville', population: 'you' }];
123 | assert.deepEqual(_.walk.pluck(tree, 'population'), [1378000, 812826, 2615000, 'you'], 'pluck from a list of trees');
124 | });
125 |
126 | QUnit.test("reduce", function(assert) {
127 | var add = function(a, b) { return a + b; };
128 | var leafMemo = [];
129 | var sum = function(memo, node) {
130 | if (_.isObject(node))
131 | return _.reduce(memo, add, 0);
132 |
133 | assert.strictEqual(memo, leafMemo);
134 | return node;
135 | };
136 | var tree = getSimpleTestTree();
137 | assert.equal(_.walk.reduce(tree, sum, leafMemo), 21);
138 |
139 | // A more useful example: transforming a tree.
140 |
141 | // Returns a new node where the left and right subtrees are swapped.
142 | var mirror = function(memo, node) {
143 | if (!_.has(node, 'r')) return node;
144 | return _.extend(_.clone(node), { l: memo.r, r: memo.l });
145 | };
146 | // Returns the '-' for internal nodes, and the value itself for leaves.
147 | var toString = function(node) { return _.has(node, 'val') ? '-' : node; };
148 |
149 | tree = _.walk.reduce(getSimpleTestTree(), mirror);
150 | assert.equal(_.walk.reduce(tree, sum, leafMemo), 21);
151 | assert.equal(_.walk.map(tree, _.walk.preorder, toString).join(''), '-0-4-6-5-1-3-2', 'pre-order map');
152 | });
153 |
154 | QUnit.test("find", function(assert) {
155 | var tree = getSimpleTestTree();
156 |
157 | // Returns a visitor function that will succeed when a node with the given
158 | // value is found, and then raise an exception the next time it's called.
159 | var findValue = function(value) {
160 | var found = false;
161 | return function(node) {
162 | if (found) throw 'already found!';
163 | found = (node.val === value);
164 | return found;
165 | };
166 | };
167 |
168 | assert.equal(_.walk.find(tree, findValue(0)).val, 0);
169 | assert.equal(_.walk.find(tree, findValue(6)).val, 6);
170 | assert.deepEqual(_.walk.find(tree, findValue(99)), undefined);
171 | });
172 |
173 | QUnit.test("filter", function(assert) {
174 | var tree = getSimpleTestTree();
175 | tree.r.val = '.oOo.'; // Remove one of the numbers.
176 | var isEvenNumber = function(x) {
177 | return _.isNumber(x) && x % 2 === 0;
178 | };
179 |
180 | assert.equal(_.walk.filter(tree, _.walk.preorder, _.isObject).length, 7, 'filter objects');
181 | assert.equal(_.walk.filter(tree, _.walk.preorder, _.isNumber).length, 6, 'filter numbers');
182 | assert.equal(_.walk.filter(tree, _.walk.postorder, _.isNumber).length, 6, 'postorder filter numbers');
183 | assert.equal(_.walk.filter(tree, _.walk.preorder, isEvenNumber).length, 3, 'filter even numbers');
184 |
185 | // With the identity function, only the value '0' should be omitted.
186 | assert.equal(_.walk.filter(tree, _.walk.preorder, _.identity).length, 13, 'filter on identity function');
187 | });
188 |
189 | QUnit.test("reject", function(assert) {
190 | var tree = getSimpleTestTree();
191 | tree.r.val = '.oOo.'; // Remove one of the numbers.
192 |
193 | assert.equal(_.walk.reject(tree, _.walk.preorder, _.isObject).length, 7, 'reject objects');
194 | assert.equal(_.walk.reject(tree, _.walk.preorder, _.isNumber).length, 8, 'reject numbers');
195 | assert.equal(_.walk.reject(tree, _.walk.postorder, _.isNumber).length, 8, 'postorder reject numbers');
196 |
197 | // With the identity function, only the value '0' should be kept.
198 | assert.equal(_.walk.reject(tree, _.walk.preorder, _.identity).length, 1, 'reject with identity function');
199 | });
200 |
201 | QUnit.test("customTraversal", function(assert) {
202 | var tree = getSimpleTestTree();
203 |
204 | // Set up a walker that will not traverse the 'val' properties.
205 | var walker = _.walk(function(node) {
206 | return _.omit(node, 'val');
207 | });
208 | var visitor = function(node) {
209 | if (!_.isObject(node)) throw new Error("Leaf value visited when it shouldn't be");
210 | };
211 | assert.equal(walker.pluck(tree, 'val').length, 7, 'pluck with custom traversal');
212 | assert.equal(walker.pluckRec(tree, 'val').length, 7, 'pluckRec with custom traversal');
213 |
214 | assert.equal(walker.map(tree, _.walk.postorder, _.identity).length, 7, 'traversal strategy is dynamically scoped');
215 |
216 | // Check that the default walker is unaffected.
217 | assert.equal(_.walk.map(tree, _.walk.postorder, _.identity).length, 14, 'default map still works');
218 | assert.equal(_.walk.pluckRec(tree, 'val').join(''), '0123456', 'default pluckRec still works');
219 | });
220 |
221 | QUnit.test("containsAtLeast", function(assert){
222 | var array = getArrayValues();
223 |
224 | assert.equal(_.walk.containsAtLeast(array, 3, "a"), true, "list contains at least 3 items");
225 | assert.equal(_.walk.containsAtLeast(array, 1, "b"), true, "list contains at least 1 items");
226 | assert.equal(_.walk.containsAtLeast(array, 1, "f"), false, "list doesn't contain item for that value");
227 | });
228 |
229 | QUnit.test("containsAtMost", function(assert){
230 | var array = getArrayValues();
231 |
232 | assert.equal(_.walk.containsAtMost(array, 4, "a"), true, "list contains at most 4 items");
233 | assert.equal(_.walk.containsAtMost(array, 1, "b"), true, "list contains at most 1 value");
234 | assert.equal(_.walk.containsAtMost(array, 1, "f"), true, "list contains at most 1 value");
235 | });
236 | });
237 |
--------------------------------------------------------------------------------
/test/dist-concat.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Underscore-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 | Underscore-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/function.arity.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 |
3 | QUnit.module("underscore.function.arity");
4 |
5 | QUnit.test("fix", function(assert) {
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 | QUnit.test("arity", function(assert) {
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 | QUnit.test("curry", function(assert) {
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 | var failure = false;
85 | try {
86 | curried(1, 2999);
87 | } catch (e) {
88 | failure = true;
89 | } finally {
90 | assert.equal(failure, true, "Curried functions only accept one argument at a time");
91 | }
92 | });
93 |
94 | QUnit.test("curry2", function(assert) {
95 |
96 | function echo () { return [].slice.call(arguments, 0); }
97 |
98 | assert.deepEqual(echo(1, 2), [1, 2], "Control test");
99 | assert.deepEqual(_.curry2(echo)(1)(2), [1, 2], "Accepts curried arguments");
100 |
101 | });
102 |
103 | QUnit.test("curryRight2", function(assert) {
104 |
105 | function echo () { return [].slice.call(arguments, 0); }
106 |
107 | assert.deepEqual(echo(1, 2), [1, 2], "Control test");
108 | assert.deepEqual(_.curryRight2(echo)(1)(2), [2, 1], "Reverses curried arguments");
109 | assert.equal(_.curryRight2, _.rcurry2, "should have alias 'rcurry2'");
110 | });
111 |
112 | QUnit.test("curry3", function(assert) {
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(_.curry3(echo)(1)(2)(3), [1, 2, 3], "Accepts curried arguments");
118 |
119 | });
120 |
121 | QUnit.test("curryRight3", function(assert) {
122 |
123 | function echo () { return [].slice.call(arguments, 0); }
124 |
125 | assert.deepEqual(echo(1, 2, 3), [1, 2, 3], "Control test");
126 | assert.deepEqual(_.curryRight3(echo)(1)(2)(3), [3, 2, 1], "Reverses curried arguments");
127 | assert.equal(_.curryRight3, _.rcurry3, "should have alias 'rcurry3'");
128 | });
129 |
130 | QUnit.test("enforce", function(assert) {
131 | function binary (a, b) {
132 | return a + b;
133 | }
134 | function ternary (a, b, c) {
135 | return a + b + c;
136 | }
137 | function altTernary (a, b, c) {
138 | return a - b - c;
139 | }
140 | var fBinary = _.enforce(binary),
141 | fTernary = _.enforce(ternary),
142 | fAltTernary = _.enforce(altTernary),
143 | failure = false;
144 | try {
145 | fBinary(1);
146 | } catch (e) {
147 | failure = true;
148 | } finally {
149 | assert.equal(failure, true, "Binary must have two arguments.");
150 | }
151 | assert.equal(fBinary(1, 2), 3, "Function returns after proper application");
152 |
153 | failure = false;
154 | try {
155 | fTernary(1, 3);
156 | } catch (e) {
157 | failure = true;
158 | } finally {
159 | assert.equal(failure, true, "Ternary must have three arguments.");
160 | }
161 | assert.equal(fTernary(1, 2, 3), 6, "Function returns after proper application");
162 | assert.equal(fAltTernary(1, 2, 3), -4, "Function cache does not collide");
163 | });
164 | });
165 |
--------------------------------------------------------------------------------
/test/function.dispatch.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 |
3 | QUnit.module("underscore.function.dispatch");
4 |
5 | QUnit.test('attempt', function(assert) {
6 | var obj = {x: '', y: function() { return true; }, z: function() { return _.toArray(arguments).join(''); }};
7 | assert.strictEqual(_.attempt(obj, 'x'), undefined);
8 | assert.strictEqual(_.attempt(obj, 'y'), true);
9 | assert.strictEqual(_.attempt(obj, 'z', 1, 2, 3), '123');
10 | assert.strictEqual(_.attempt(null, 'x'), undefined);
11 | assert.strictEqual(_.attempt(undefined, 'x'), undefined);
12 | });
13 |
14 | });
15 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Underscore-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 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/test/object.builders.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 |
3 | QUnit.module("underscore.object.builders");
4 |
5 | QUnit.test("merge", function(assert) {
6 | var o = {'a': 1, 'b': 2};
7 |
8 | assert.deepEqual(_.merge(o), {a: 1, b: 2}, 'should return a copy of the object if given only one');
9 | assert.deepEqual(_.merge({'a': 1, 'b': 2}, {b: 42}), {'a': 1, b: 42}, 'should merge two objects');
10 | assert.deepEqual(_.merge({a: 1, b: 2}, {b: 42}, {c: 3}), {a: 1, b: 42, c: 3}, 'should merge three or more objects');
11 | assert.deepEqual(_.merge({a: 1, b: 2}, {b: 42}, {c: 3}, {c: 4}), {a: 1, b: 42, c: 4}, 'should merge three or more objects');
12 |
13 | var a = {'a': 1, 'b': 2};
14 | var $ = _.merge(a, {'a': 42});
15 |
16 | assert.deepEqual(a, {'a': 1, 'b': 2}, 'should not modify the original');
17 | });
18 |
19 | QUnit.test("renameKeys", function(assert) {
20 | 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');
21 |
22 | var a = {'a': 1, 'b': 2};
23 | var $ = _.renameKeys(a, {'a': 'A'});
24 |
25 | assert.deepEqual(a, {'a': 1, 'b': 2}, 'should not modify the original');
26 | });
27 |
28 | QUnit.test("snapshot", function(assert) {
29 | var o = {'a': 1, 'b': 2};
30 | var oSnap = _.snapshot(o);
31 |
32 | var a = [1,2,3,4];
33 | var aSnap = _.snapshot(a);
34 |
35 | var n = [1,{a: 1, b: [1,2,3]},{},4];
36 | var nSnap = _.snapshot(n);
37 |
38 | var c = [1,{a: 1, b: [1,2,3]},{},4];
39 | var cSnap = _.snapshot(c);
40 | c[1].b = 42;
41 |
42 | assert.deepEqual(o, oSnap, 'should create a deep copy of an object');
43 | assert.deepEqual(a, aSnap, 'should create a deep copy of an array');
44 | assert.deepEqual(n, nSnap, 'should create a deep copy of an array');
45 | assert.deepEqual(nSnap, [1,{a: 1, b: [1,2,3]},{},4], 'should allow changes to the original to not change copies');
46 | });
47 |
48 | QUnit.test("setPath", function(assert) {
49 | var obj = {a: {b: {c: 42, d: 108}}};
50 | var ary = ['a', ['b', ['c', 'd'], 'e']];
51 | var nest = [1, {a: 2, b: [3,4], c: 5}, 6];
52 |
53 | assert.deepEqual(_.setPath(obj, 9, ['a', 'b', 'c']), {a: {b: {c: 9, d: 108}}}, '');
54 | assert.deepEqual(_.setPath(ary, 9, [1, 1, 0]), ['a', ['b', [9, 'd'], 'e']], '');
55 | assert.deepEqual(_.setPath(nest, 9, [1, 'b', 1]), [1, {a: 2, b: [3,9], c: 5}, 6], '');
56 |
57 | assert.deepEqual(_.setPath(obj, 9, 'a'), {a: 9}, '');
58 | assert.deepEqual(_.setPath(ary, 9, 1), ['a', 9], '');
59 |
60 | assert.deepEqual(obj, {a: {b: {c: 42, d: 108}}}, 'should not modify the original object');
61 | assert.deepEqual(ary, ['a', ['b', ['c', 'd'], 'e']], 'should not modify the original array');
62 | assert.deepEqual(nest, [1, {a: 2, b: [3,4], c: 5}, 6], 'should not modify the original nested structure');
63 | });
64 |
65 | QUnit.test("updatePath", function(assert) {
66 | var obj = {a: {b: {c: 42, d: 108}}};
67 | var ary = ['a', ['b', ['c', 'd'], 'e']];
68 | var nest = [1, {a: 2, b: [3,4], c: 5}, 6];
69 |
70 | assert.deepEqual(_.updatePath(obj, _.always(9), ['a', 'b', 'c']), {a: {b: {c: 9, d: 108}}}, '');
71 | assert.deepEqual(_.updatePath(ary, _.always(9), [1, 1, 0]), ['a', ['b', [9, 'd'], 'e']], '');
72 | assert.deepEqual(_.updatePath(nest, _.always(9), [1, 'b', 1]), [1, {a: 2, b: [3,9], c: 5}, 6], '');
73 |
74 | assert.deepEqual(_.updatePath(obj, _.always(9), 'a'), {a: 9}, '');
75 | assert.deepEqual(_.updatePath(ary, _.always(9), 1), ['a', 9], '');
76 |
77 | assert.deepEqual(obj, {a: {b: {c: 42, d: 108}}}, 'should not modify the original object');
78 | assert.deepEqual(ary, ['a', ['b', ['c', 'd'], 'e']], 'should not modify the original array');
79 | assert.deepEqual(nest, [1, {a: 2, b: [3,4], c: 5}, 6], 'should not modify the original nested structure');
80 | });
81 |
82 | QUnit.test("omitPath", function(assert){
83 | var a = {
84 | foo: true,
85 | bar: false,
86 | baz: 42,
87 | dada: {
88 | carlos: { pepe: 9 },
89 | pedro: 'pedro',
90 | list: [
91 | {file: '..', more: {other: { a: 1, b: 2}}, name: 'aa'},
92 | {file: '..', name: 'bb'}
93 | ]
94 | }
95 | };
96 |
97 | assert.deepEqual(_.omitPath(a, 'dada.carlos.pepe'), {
98 | foo: true,
99 | bar: false,
100 | baz: 42,
101 | dada: {
102 | carlos: {},
103 | pedro: 'pedro',
104 | list: [
105 | {file: '..', more: {other: { a: 1, b: 2}} , name: 'aa'},
106 | {file: '..', name: 'bb'}
107 | ]
108 | }
109 | }, "should return an object without the value that represent the path");
110 |
111 | assert.deepEqual(_.omitPath(a, 'dada.carlos'), {
112 | foo: true,
113 | bar: false,
114 | baz: 42,
115 | dada: {
116 | pedro: 'pedro',
117 | list: [
118 | {file: '..', more: {other: { a: 1, b: 2}} , name: 'aa'},
119 | {file: '..', name: 'bb'}
120 | ]
121 | }
122 | }, "should return an object without the value that represent the path");
123 |
124 | assert.deepEqual(_.omitPath(a, ''), {
125 | foo: true,
126 | bar: false,
127 | baz: 42,
128 | dada: {
129 | carlos: { pepe: 9 },
130 | pedro: 'pedro',
131 | list: [
132 | {file: '..', more: {other: { a: 1, b: 2}} , name: 'aa'},
133 | {file: '..', name: 'bb'}
134 | ]
135 | }
136 | }, "should return the whole object because the path is empty");
137 |
138 | assert.deepEqual(_.omitPath(a, 'dada.list.0.file'), {
139 | foo: true,
140 | bar: false,
141 | baz: 42,
142 | dada: {
143 | carlos: { pepe: 9 },
144 | pedro: 'pedro',
145 | list: [
146 | {name: 'aa', more: {other: { a: 1, b: 2}}},
147 | {file: '..', name: 'bb'}
148 | ]
149 | }
150 | }, "should return an object without the value in an element of the list");
151 |
152 | assert.deepEqual(_.omitPath(a, 'dada.list.1.name'), {
153 | foo: true,
154 | bar: false,
155 | baz: 42,
156 | dada: {
157 | carlos: { pepe: 9 },
158 | pedro: 'pedro',
159 | list: [
160 | {name: 'aa', file: '..', more: {other: { a: 1, b: 2}}},
161 | {file: '..'}
162 | ]
163 | }
164 | }, "should return an object without the value in each object of the list");
165 |
166 | assert.deepEqual(_.omitPath(a, 'dada.list'), {
167 | foo: true,
168 | bar: false,
169 | baz: 42,
170 | dada: {
171 | carlos: { pepe: 9 },
172 | pedro: 'pedro'
173 | }
174 | }, "should return an object without the list");
175 |
176 | assert.deepEqual(_.omitPath(a, 'dada.list.0.more.other.a'), {
177 | foo: true,
178 | bar: false,
179 | baz: 42,
180 | dada: {
181 | carlos: { pepe: 9 },
182 | pedro: 'pedro',
183 | list: [
184 | {file: '..', more: {other: { b: 2}} , name: 'aa'},
185 | {file: '..', name: 'bb'}
186 | ]
187 | }
188 | },
189 | "should return an object without the value inside the values of the list"
190 | );
191 | });
192 | });
193 |
--------------------------------------------------------------------------------
/test/object.selectors.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 |
3 | QUnit.module("underscore.object.selectors");
4 |
5 | QUnit.test("accessor", function(assert) {
6 | var a = [{a: 1, b: 2}, {c: 3}];
7 |
8 | assert.equal(_.accessor('a')(a[0]), 1, 'should return a function that plucks');
9 | assert.equal(_.accessor('a')(a[1]), undefined, 'should return a function that plucks, or returns undefined');
10 | assert.deepEqual(_.map(a, _.accessor('a')), [1, undefined], 'should return a function that plucks');
11 | });
12 |
13 | QUnit.test("dictionary", function(assert) {
14 | var a = [{a: 1, b: 2}, {c: 3}];
15 |
16 | assert.equal(_.dictionary(a[0])('a'), 1, 'should return a function that acts as a dictionary');
17 | assert.equal(_.dictionary(a[1])('a'), undefined, 'should return a function that acts as a dictionary, or returns undefined');
18 | });
19 |
20 | QUnit.test("selectKeys", function(assert) {
21 | assert.deepEqual(_.selectKeys({'a': 1, 'b': 2}, ['a']), {'a': 1}, 'shold return a map of the desired keys');
22 | assert.deepEqual(_.selectKeys({'a': 1, 'b': 2}, ['z']), {}, 'shold return an empty map if the desired keys are not present');
23 | });
24 |
25 | QUnit.test("kv", function(assert) {
26 | assert.deepEqual(_.kv({'a': 1, 'b': 2}, 'a'), ['a', 1], 'should return the key/value pair at the desired key');
27 | assert.equal(_.kv({'a': 1, 'b': 2}, 'z'), undefined, 'shold return undefined if the desired key is not present');
28 | });
29 |
30 | QUnit.test("getPath", function(assert) {
31 | var deepObject = { a: { b: { c: "c" } }, falseVal: false, nullVal: null, undefinedVal: undefined, arrayVal: ["arr"], deepArray: { contents: ["da1", "da2"] } };
32 | var weirdObject = { "D'artagnan": { "[0].[1]": "myValue" }, element: { "0": { "prop.1": "value1" } } };
33 | var deepArr = [[["thirdLevel"]]];
34 | var ks = ["a", "b", "c"];
35 |
36 | assert.strictEqual(_.getPath(deepObject, ks), "c", "should get a deep property's value from objects");
37 | assert.deepEqual(ks, ["a", "b", "c"], "should not have mutated ks argument");
38 | assert.strictEqual(_.getPath(deepArr, [0, 0, 0]), "thirdLevel", "should get a deep property's value from arrays");
39 | assert.strictEqual(_.getPath(deepObject, ["arrayVal", 0]), "arr", "should get a deep property's value from nested arrays and objects");
40 | assert.strictEqual(_.getPath(deepObject, ["deepArray", "contents", 1]), "da2", "should get a deep property's value within arrays inside deep objects, from an array");
41 | assert.strictEqual(_.getPath(deepObject, "deepArray.contents[0]"), "da1", "should get a deep property's value within arrays inside deep objects, from a complete javascript path");
42 |
43 | assert.strictEqual(_.getPath(deepObject, ["undefinedVal"]), undefined, "should return undefined for undefined properties");
44 | assert.strictEqual(_.getPath(deepObject, ["a", "notHere"]), undefined, "should return undefined for non-existent properties");
45 | assert.strictEqual(_.getPath(deepObject, ["nullVal"]), null, "should return null for null properties");
46 | assert.strictEqual(_.getPath(deepObject, ["nullVal", "notHere", "notHereEither"]), undefined, "should return undefined for non-existent descendents of null properties");
47 |
48 | assert.strictEqual(_.getPath(weirdObject, ["D'artagnan", "[0].[1]"]), "myValue", "should be able to traverse complex property names, from an array");
49 | assert.strictEqual(_.getPath(weirdObject, "[\"D'artagnan\"]['[0].[1]']"), "myValue", "should be able to traverse complex property names, from an accessor string");
50 | assert.strictEqual(_.getPath(weirdObject, "element[0]['prop.1']"), "value1", "should be able to traverse complex property names, from an accessor string");
51 |
52 | assert.strictEqual(_.getPath(deepObject, "a.b.c"), "c", "should work with keys written in dot notation");
53 | assert.strictEqual(_.getPath({}, "myPath.deepProperty"), undefined, "should not break with empty objects and deep paths");
54 | });
55 |
56 | QUnit.test("hasPath", function(assert) {
57 | var deepObject = { a: { b: { c: "c" } }, falseVal: false, nullVal: null, undefinedVal: undefined, arrayVal: ["arr"], deepArray: { contents: ["da1", "da2"] } };
58 | var weirdObject = { "D'artagnan": { "[0].[1]": "myValue" }, element: { "0": { "prop.1": "value1" } } };
59 | var ks = ["a", "b", "c"];
60 |
61 | assert.strictEqual(_.hasPath(deepObject, ["notHere", "notHereEither"]), false, "should return false if the path doesn't exist");
62 | assert.strictEqual(_.hasPath(deepObject, ks), true, "should return true if the path exists");
63 | assert.deepEqual(ks, ["a", "b", "c"], "should not have mutated ks argument");
64 |
65 | assert.strictEqual(_.hasPath(deepObject, ["deepArray", "contents", 1]), true, "should return true for an existing value within arrays inside deep objects, from an array");
66 |
67 | assert.strictEqual(_.hasPath(deepObject, ["arrayVal", 0]), true, "should return true for an array's index if it is defined");
68 | assert.strictEqual(_.hasPath(deepObject, ["arrayVal", 999]), false, "should return false for an array's index if it is not defined");
69 |
70 | assert.strictEqual(_.hasPath(deepObject, ["nullVal"]), true, "should return true for null properties");
71 | assert.strictEqual(_.hasPath(deepObject, ["undefinedVal"]), true, "should return true for properties that were explicitly assigned undefined");
72 |
73 | assert.strictEqual(_.hasPath(weirdObject, ["D'artagnan", "[0].[1]"]), true, "should return true for complex property names, from an array");
74 | assert.strictEqual(_.hasPath(weirdObject, "[\"D'artagnan\"]['[0].[1]']"), true, "should return true for complex property names, from an accessor string");
75 | assert.strictEqual(_.hasPath(weirdObject, "element[0]['prop.1']"), true, "should be return true for complex property names, from an accessor string");
76 | assert.strictEqual(_.hasPath(weirdObject, ["D'artagnan", "[0].[2]"]), false, "should return false for non-existent complex property names, from an array");
77 | assert.strictEqual(_.hasPath(weirdObject, "[\"D'artagnan\"]['[0].[2]']"), false, "should return true for non-existent complex property names, from an accessor string");
78 | assert.strictEqual(_.hasPath(weirdObject, "element[0]['prop.2']"), false, "should be return true for non-existent complex property names, from an accessor string");
79 |
80 | assert.strictEqual(_.hasPath(deepObject, ["nullVal", "notHere"]), false, "should return false for descendants of null properties");
81 | assert.strictEqual(_.hasPath(deepObject, ["undefinedVal", "notHere"]), false, "should return false for descendants of undefined properties");
82 |
83 | assert.strictEqual(_.hasPath(deepObject, "a.b.c"), true, "should work with keys written in dot notation.");
84 |
85 | assert.strictEqual(_.hasPath(null, []), true, "should return true for null and undefined when passed no keys");
86 | assert.strictEqual(_.hasPath(void 0, []), true);
87 | assert.strictEqual(_.hasPath(null, ['']), false, "should return false (not throw) on null/undefined given keys");
88 | assert.strictEqual(_.hasPath(void 0, ['']), false);
89 |
90 | assert.strictEqual(_.hasPath(deepObject, "a.b.c.d"), false, "should return false for keys which doesn't exist on nested existing objects");
91 | });
92 |
93 | QUnit.test("keysFromPath", function(assert) {
94 | assert.deepEqual(_.keysFromPath("a.b.c"), ["a", "b", "c"], "should convert a path into an array of keys");
95 | assert.deepEqual(_.keysFromPath("a[0].b['c']"), ["a", "0", "b", "c"], "should handle bracket notation");
96 | assert.deepEqual(_.keysFromPath("[\"D'artagnan\"]['[0].[1]']"), ["D'artagnan", "[0].[1]"], "should handle complex paths");
97 | });
98 |
99 | QUnit.test("pickWhen", function(assert) {
100 | var a = {foo: true, bar: false, baz: 42};
101 |
102 | assert.deepEqual(_.pickWhen(a, _.truthy), {foo: true, baz: 42}, "should return an object with kvs that return a truthy value for the given predicate");
103 | });
104 |
105 | QUnit.test("omitWhen", function(assert) {
106 | var a = {foo: [], bar: "", baz: "something", quux: ['a']};
107 |
108 | assert.deepEqual(_.omitWhen(a, _.isEmpty), {baz: "something", quux: ['a']}, "should return an object with kvs that return a falsey value for the given predicate");
109 | });
110 | });
111 |
--------------------------------------------------------------------------------
/test/util.existential.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 |
3 | QUnit.module("underscore.util.existential");
4 |
5 | QUnit.test("exists", function(assert) {
6 | assert.equal(_.exists(null), false, 'should know that null is not existy');
7 | assert.equal(_.exists(undefined), false, 'should know that undefined is not existy');
8 |
9 | assert.equal(_.exists(1), true, 'should know that all but null and undefined are existy');
10 | assert.equal(_.exists(0), true, 'should know that all but null and undefined are existy');
11 | assert.equal(_.exists(-1), true, 'should know that all but null and undefined are existy');
12 | assert.equal(_.exists(3.14), true, 'should know that all but null and undefined are existy');
13 | assert.equal(_.exists('undefined'), true, 'should know that all but null and undefined are existy');
14 | assert.equal(_.exists(''), true, 'should know that all but null and undefined are existy');
15 | assert.equal(_.exists(NaN), true, 'should know that all but null and undefined are existy');
16 | assert.equal(_.exists(Infinity), true, 'should know that all but null and undefined are existy');
17 | assert.equal(_.exists(true), true, 'should know that all but null and undefined are existy');
18 | assert.equal(_.exists(false), true, 'should know that all but null and undefined are existy');
19 | assert.equal(_.exists(function(){}), true, 'should know that all but null and undefined are existy');
20 | });
21 |
22 | QUnit.test("truthy", function(assert) {
23 | assert.equal(_.truthy(null), false, 'should know that null, undefined and false are not truthy');
24 | assert.equal(_.truthy(undefined), false, 'should know that null, undefined and false are not truthy');
25 | assert.equal(_.truthy(false), false, 'should know that null, undefined and false are not truthy');
26 |
27 | assert.equal(_.truthy(1), true, 'should know that everything else is truthy');
28 | assert.equal(_.truthy(0), true, 'should know that everything else is truthy');
29 | assert.equal(_.truthy(-1), true, 'should know that everything else is truthy');
30 | assert.equal(_.truthy(3.14), true, 'should know that everything else is truthy');
31 | assert.equal(_.truthy('undefined'), true, 'should know that everything else is truthy');
32 | assert.equal(_.truthy(''), true, 'should know that everything else is truthy');
33 | assert.equal(_.truthy(NaN), true, 'should know that everything else is truthy');
34 | assert.equal(_.truthy(Infinity), true, 'should know that everything else is truthy');
35 | assert.equal(_.truthy(true), true, 'should know that everything else is truthy');
36 | assert.equal(_.truthy(function(){}), true, 'should know that everything else is truthy');
37 | });
38 |
39 | QUnit.test("falsey", function(assert) {
40 | assert.equal(_.falsey(null), true, 'should know that null, undefined and false are not falsey');
41 | assert.equal(_.falsey(undefined), true, 'should know that null, undefined and false are not falsey');
42 | assert.equal(_.falsey(false), true, 'should know that null, undefined and false are not falsey');
43 |
44 | assert.equal(_.falsey(1), false, 'should know that everything else is falsey');
45 | assert.equal(_.falsey(0), false, 'should know that everything else is falsey');
46 | assert.equal(_.falsey(-1), false, 'should know that everything else is falsey');
47 | assert.equal(_.falsey(3.14), false, 'should know that everything else is falsey');
48 | assert.equal(_.falsey('undefined'), false, 'should know that everything else is falsey');
49 | assert.equal(_.falsey(''), false, 'should know that everything else is falsey');
50 | assert.equal(_.falsey(NaN), false, 'should know that everything else is falsey');
51 | assert.equal(_.falsey(Infinity), false, 'should know that everything else is falsey');
52 | assert.equal(_.falsey(true), false, 'should know that everything else is falsey');
53 | assert.equal(_.falsey(function(){}), false, 'should know that everything else is falsey');
54 | });
55 |
56 | QUnit.test('firstExisting', function(assert) {
57 | assert.equal(_.firstExisting('first', 'second'), 'first', 'should return the first existing value');
58 | assert.equal(_.firstExisting(null, 'second'), 'second', 'should ignore null');
59 | assert.equal(_.firstExisting(void 0, 'second'), 'second', 'should ignore undefined');
60 | assert.equal(_.firstExisting(null, void 0, 'third'), 'third', 'should work with more arguments');
61 | });
62 |
63 | });
64 |
--------------------------------------------------------------------------------
/test/util.operators.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 |
3 | QUnit.module("underscore.util.operators");
4 |
5 | QUnit.test("add", function(assert) {
6 | assert.equal(_.add(1, 1), 2, '1 + 1 = 2');
7 | assert.equal(_.add(3, 5), 8, '3 + 5 = 8');
8 | assert.equal(_.add(1, 2, 3, 4), 10, 'adds multiple operands');
9 | assert.equal(_.add([1, 2, 3, 4]), 10, 'adds multiple operands, when specified in an array');
10 | });
11 |
12 | QUnit.test("sub", function(assert) {
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 | assert.equal(_.sub([10, 9, 8, 7]), -14, 'subtracts multiple operands, when specified in an array');
17 | });
18 |
19 | QUnit.test("mul", function(assert) {
20 | assert.equal(_.mul(1, 1), 1, '1 * 1 = 1');
21 | assert.equal(_.mul(5, 3), 15, '5 * 3 = 15');
22 | assert.equal(_.mul(1, 2, 3, 4), 24, 'multiplies multiple operands');
23 | assert.equal(_.mul([1, 2, 3, 4]), 24, 'multiplies multiple operands, when specified in an array');
24 | });
25 |
26 | QUnit.test("div", function(assert) {
27 | assert.equal(_.div(1, 1), 1, '1 / 1 = 1');
28 | assert.equal(_.div(15, 3), 5, '15 / 3 = 5');
29 | assert.equal(_.div(15, 0), Infinity, '15 / 0 = Infinity');
30 | assert.equal(_.div(24, 2, 2, 2), 3, 'divides multiple operands');
31 | assert.equal(_.div([24, 2, 2, 2]), 3, 'divides multiple operands, when specified in an array');
32 | });
33 |
34 | QUnit.test("mod", function(assert) {
35 | assert.equal(_.mod(3, 2), 1, '3 / 2 = 1');
36 | assert.equal(_.mod(15, 3), 0, '15 / 3 = 0');
37 | });
38 |
39 | QUnit.test("inc", function(assert) {
40 | assert.equal(_.inc(1), 2, '++1 = 2');
41 | assert.equal(_.inc(15), 16, '++15 = 16');
42 | });
43 |
44 | QUnit.test("dec", function(assert) {
45 | assert.equal(_.dec(2), 1, '--2 = 1');
46 | assert.equal(_.dec(15), 14, '--15 = 15');
47 | });
48 |
49 | QUnit.test("neg", function(assert) {
50 | assert.equal(_.neg(2), -2, 'opposite of 2');
51 | assert.equal(_.neg(-2), 2, 'opposite of -2');
52 | assert.equal(_.neg(true), -1, 'opposite of true');
53 | });
54 |
55 | QUnit.test("eq", function(assert) {
56 | assert.equal(_.eq(1, 1), true, '1 == 1');
57 | assert.equal(_.eq(1, true), true, '1 == true');
58 | assert.equal(_.eq(1, false), false, '1 != false');
59 | assert.equal(_.eq(1, '1'), true, '1 == "1"');
60 | assert.equal(_.eq(1, 'one'), false, '1 != "one"');
61 | assert.equal(_.eq(0, 0), true, '0 == 0');
62 | assert.equal(_.eq(0, false), true, '0 == false');
63 | assert.equal(_.eq(0, '0'), true, '0 == "0"');
64 | assert.equal(_.eq({}, {}), false, '{} == {}');
65 | assert.equal(false, false, 'failing placeholder');
66 | assert.equal(_.eq(0, 0, 1), false, 'compares a list of arguments');
67 | assert.equal(_.eq([0, 0, 1]), false, 'compares a list of arguments, when specified as an array');
68 | });
69 | QUnit.test("seq", function(assert) {
70 | assert.equal(_.seq(1, 1), true, '1 === 1');
71 | assert.equal(_.seq(1, '1'), false, '1 !== "1"');
72 | assert.equal(_.seq(0, 0, 1), false, 'compares a list of arguments');
73 | assert.equal(_.seq([0, 0, 1]), false, 'compares a list of arguments, when specified as an array');
74 | });
75 | QUnit.test("neq", function(assert) {
76 | assert.equal(_.neq('a', 'b'), true, '"a" != "b"');
77 | assert.equal(_.neq(1, '1'), false, '1 == "1"');
78 | assert.equal(_.neq(0, 0, 1), true, 'compares a list of arguments');
79 | assert.equal(_.neq([0, 0, 1]), true, 'compares a list of arguments, when specified as an array');
80 | });
81 | QUnit.test("sneq", function(assert) {
82 | assert.equal(_.sneq('a', 'b'), true, '"a" !== "b"');
83 | assert.equal(_.sneq(1, '1'), true, '1 !== "1"');
84 | assert.equal(_.sneq(0, 0, 1), true, 'compares a list of arguments');
85 | assert.equal(_.sneq([0, 0, 1]), true, 'compares a list of arguments, when specified as an array');
86 | });
87 | QUnit.test("not", function(assert) {
88 | assert.equal(_.not(true), false, 'converts true to false');
89 | assert.equal(_.not(false), true, 'converts false to true');
90 | assert.equal(_.not('truthy'), false, 'converts truthy values to false');
91 | assert.equal(_.not(null), true, 'converts falsy values to true');
92 | });
93 | QUnit.test("gt", function(assert) {
94 | assert.equal(_.gt(3, 2), true, '3 > 2');
95 | assert.equal(_.gt(1, 3), false, '1 > 3');
96 | assert.equal(_.gt(1, 2, 1), false, 'compares a list of arguments');
97 | assert.equal(_.gt([1, 2, 1]), false, 'compares a list of arguments, when specified as an array');
98 | });
99 | QUnit.test("lt", function(assert) {
100 | assert.equal(_.lt(3, 2), false, '3 < 2');
101 | assert.equal(_.lt(1, 3), true, '1 < 3');
102 | assert.equal(_.lt(1, 2, 1), false, 'compares a list of arguments');
103 | assert.equal(_.lt([1, 2, 1]), false, 'compares a list of arguments, when specified as an array');
104 | });
105 | QUnit.test("gte", function(assert) {
106 | assert.equal(_.gte(3, 2), true, '3 >= 2');
107 | assert.equal(_.gte(1, 3), false, '1 >= 3');
108 | assert.equal(_.gte(3, 3), true, '3 >= 3');
109 | assert.equal(_.gte(2, 3, 1), false, 'compares a list of arguments');
110 | assert.equal(_.gte([2, 3, 1]), false, 'compares a list of arguments, when specified as an array');
111 | });
112 | QUnit.test("lte", function(assert) {
113 | assert.equal(_.lte(3, 2), false, '3 <= 2');
114 | assert.equal(_.lte(1, 3), true, '1 <= 3');
115 | assert.equal(_.lte(3, 3), true, '3 <= 3');
116 | assert.equal(_.lte(2, 2, 1), false, 'compares a list of arguments');
117 | assert.equal(_.lte([2, 2, 1]), false, 'compares a list of arguments, when specified as an array');
118 | });
119 | QUnit.test("bitwiseAnd", function(assert) {
120 | assert.equal(_.bitwiseAnd(1, 1), 1, '1 & 1');
121 | assert.equal(_.bitwiseAnd(1, 0), 0, '1 & 0');
122 | assert.equal(_.bitwiseAnd(1, 1, 0), 0, 'operates on multiple arguments');
123 | assert.equal(_.bitwiseAnd([1, 1, 0]), 0, 'operates on multiple arguments, when specified as an array');
124 | });
125 | QUnit.test("bitwiseOr", function(assert) {
126 | assert.equal(_.bitwiseOr(1, 1), 1, '1 | 1');
127 | assert.equal(_.bitwiseOr(1, 0), 1, '1 | 0');
128 | assert.equal(_.bitwiseOr(1, 1, 2), 3, 'operates on multiple arguments');
129 | assert.equal(_.bitwiseOr([1, 1, 2]), 3, 'operates on multiple arguments, when specified as an array');
130 | });
131 | QUnit.test("bitwiseXor", function(assert) {
132 | assert.equal(_.bitwiseXor(1, 1), 0, '1 ^ 1');
133 | assert.equal(_.bitwiseXor(1, 2), 3, '1 ^ 2');
134 | assert.equal(_.bitwiseXor(1, 2, 3), 0, 'operates on multiple arguments');
135 | assert.equal(_.bitwiseXor([1, 2, 3]), 0, 'operates on multiple arguments, when specified as an array');
136 | });
137 | QUnit.test("bitwiseNot", function(assert) {
138 | assert.equal(_.bitwiseNot(1), -2, '~1');
139 | assert.equal(_.bitwiseNot(2), -3, '~2');
140 | });
141 | QUnit.test("bitwiseLeft", function(assert) {
142 | assert.equal(_.bitwiseLeft(1, 1), 2, '1 << 1');
143 | assert.equal(_.bitwiseLeft(1, 0), 1, '1 << 0');
144 | assert.equal(_.bitwiseLeft(1, 1, 1), 4, 'operates on multiple arguments');
145 | assert.equal(_.bitwiseLeft([1, 1, 1]), 4, 'operates on multiple arguments, when specified as an array');
146 | });
147 | QUnit.test("bitwiseRight", function(assert) {
148 | assert.equal(_.bitwiseRight(1, 1), 0, '1 >> 1');
149 | assert.equal(_.bitwiseRight(2, 1), 1, '2 >> 1');
150 | assert.equal(_.bitwiseRight(3, 1, 1), 0, 'operates on multiple arguments');
151 | assert.equal(_.bitwiseRight([3, 1, 1]), 0, 'operates on multiple arguments, when specified as an array');
152 | });
153 | QUnit.test("bitwiseZ", function(assert) {
154 | assert.equal(_.bitwiseZ(-1, 1), 2147483647, '-1 >>> 1');
155 | assert.equal(_.bitwiseZ(-1, 1, 1), 1073741823, 'operates on multiple arguments');
156 | assert.equal(_.bitwiseZ([-1, 1, 1]), 1073741823, 'operates on multiple arguments, when specified as an array');
157 | });
158 |
159 | });
160 |
--------------------------------------------------------------------------------
/test/util.strings.js:
--------------------------------------------------------------------------------
1 |
2 | $(document).ready(function() {
3 |
4 | QUnit.module('underscore.util.strings');
5 |
6 | QUnit.test('explode', function(assert) {
7 | assert.deepEqual(_.explode('Virgil'), ['V','i','r','g','i','l'], 'Should explode a string into an array of characters.');
8 | });
9 |
10 | QUnit.test('fromQuery', function(assert) {
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 | assert.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 | QUnit.test('implode', function(assert) {
30 | assert.equal(_.implode(['H','o','m','e','r']), 'Homer', 'Should implode an array of characters into a single string.');
31 | });
32 |
33 | QUnit.test('camelCase', function(assert) {
34 | assert.equal(_.camelCase('punic-wars'), 'punicWars', 'Should convert a dashed-format string to camelCase.');
35 | });
36 |
37 | QUnit.test('toDash', function(assert) {
38 | assert.equal(_.toDash('trojanWar'), 'trojan-war', 'Should convert a camelCase string to dashed-format.');
39 | assert.equal(_.toDash('PersianWar'), 'persian-war', 'Should convert a PascalCase string to dashed-format.');
40 | });
41 |
42 | QUnit.test('toQuery', function(assert) {
43 | var obj = {'foo&bar': 'baz', 'test': 'total success', 'nested': {'works': 'too'}, 'isn\'t': ['that', 'cool?']};
44 | assert.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');
45 | assert.equal(_.toQuery(obj), jQuery.param(obj), 'query serialization matchs jQuery.param()');
46 | assert.equal(_.toQuery({a: []}), '', 'empty array params produce the empty string');
47 | assert.equal(_.toQuery({a: [], b: []}), '', 'multiple empty array params do not lead to spurious ampersands');
48 | assert.equal(_.toQuery({a: null, b: undefined}), 'a=null&b=undefined', 'respects null and undefined');
49 | });
50 |
51 | QUnit.test('strContains', function(assert) {
52 | assert.equal(_.strContains('Metaphysics', 'physics'), true, 'Should return true if string contains search string.');
53 | assert.equal(_.strContains('Poetics', 'prose'), false, 'Should return false if string does not contain search string.');
54 |
55 | var thrower = function() { _.strContains([], ''); };
56 | assert.throws(thrower, TypeError, 'Throws TypeError if first argument is not a string.');
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/test/util.trampolines.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 |
3 | QUnit.module("underscore.util.trampolines");
4 |
5 | QUnit.test("trampoline", function(assert) {
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 | assert.equal(_.trampoline(evenOline, 55000), true, 'should trampoline two mutually recursive functions');
21 | assert.equal(_.trampoline(evenOline, 0), true, 'should trampoline two mutually recursive functions');
22 | assert.equal(_.trampoline(evenOline, 111111), false, 'should trampoline two mutually recursive functions');
23 | assert.equal(_.trampoline(oddOline, 1), true, 'should trampoline two mutually recursive functions');
24 | assert.equal(_.trampoline(oddOline, 11111), true, 'should trampoline two mutually recursive functions');
25 | assert.equal(_.trampoline(oddOline, 22), false, 'should trampoline two mutually recursive functions');
26 | });
27 |
28 | });
29 |
--------------------------------------------------------------------------------
/underscore.array.builders.js:
--------------------------------------------------------------------------------
1 | // Underscore-contrib (underscore.array.builders.js 0.3.0)
2 | // (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
3 | // Underscore-contrib may be freely distributed under the MIT license.
4 |
5 | (function() {
6 |
7 | // Baseline setup
8 | // --------------
9 |
10 | // Establish the root object, `window` in the browser, or `require` it on the server.
11 | if (typeof exports === 'object') {
12 | _ = module.exports = require('underscore');
13 | }
14 |
15 | // Helpers
16 | // -------
17 |
18 | // Create quick reference variables for speed access to core prototypes.
19 | var slice = Array.prototype.slice,
20 | sort = Array.prototype.sort;
21 |
22 | var existy = function(x) { return x != null; };
23 |
24 | // Mixing in the array builders
25 | // ----------------------------
26 |
27 | _.mixin({
28 | // Concatenates one or more arrays given as arguments. If given objects and
29 | // scalars as arguments `cat` will plop them down in place in the result
30 | // array. If given an `arguments` object, `cat` will treat it like an array
31 | // and concatenate it likewise.
32 | cat: function() {
33 | return _.flatten(arguments, true);
34 | },
35 |
36 | // 'Constructs' an array by putting an element at its front
37 | cons: function(head, tail) {
38 | return _.cat([head], tail);
39 | },
40 |
41 | // Takes an array and chunks it some number of times into
42 | // sub-arrays of size n. Allows and optional padding array as
43 | // the third argument to fill in the tail chunk when n is
44 | // not sufficient to build chunks of the same size.
45 | chunk: function(array, n, pad) {
46 | var p = function(array) {
47 | if (array == null) return [];
48 |
49 | var part = _.take(array, n);
50 |
51 | if (n === _.size(part)) {
52 | return _.cons(part, p(_.drop(array, n)));
53 | }
54 | else {
55 | return pad ? [_.take(_.cat(part, pad), n)] : [];
56 | }
57 | };
58 |
59 | return p(array);
60 | },
61 |
62 | // Takes an array and chunks it some number of times into
63 | // sub-arrays of size n. If the array given cannot fill the size
64 | // needs of the final chunk then a smaller chunk is used
65 | // for the last.
66 | chunkAll: function(array, n, step) {
67 | step = (step != null) ? step : n;
68 |
69 | var p = function(array, n, step) {
70 | if (_.isEmpty(array)) return [];
71 |
72 | return _.cons(_.take(array, n),
73 | p(_.drop(array, step), n, step));
74 | };
75 |
76 | return p(array, n, step);
77 | },
78 |
79 | // Maps a function over an array and concatenates all of the results.
80 | mapcat: function(array, fun) {
81 | return _.flatten(_.map(array, fun), true);
82 | },
83 |
84 | // Returns an array with some item between each element
85 | // of a given array.
86 | interpose: function(array, inter) {
87 | if (!_.isArray(array)) throw new TypeError;
88 | var sz = _.size(array);
89 | if (sz === 0) return array;
90 | if (sz === 1) return array;
91 |
92 | return slice.call(_.mapcat(array, function(elem) {
93 | return _.cons(elem, [inter]);
94 | }), 0, -1);
95 | },
96 |
97 | // Weaves two or more arrays together
98 | weave: function(/* args */) {
99 | if (!_.some(arguments)) return [];
100 | if (arguments.length == 1) return arguments[0];
101 |
102 | return _.filter(_.flatten(_.zip.apply(null, arguments), true), function(elem) {
103 | return elem != null;
104 | });
105 | },
106 | interleave: _.weave,
107 |
108 | // Returns an array of a value repeated a certain number of
109 | // times.
110 | repeat: function(t, elem) {
111 | return _.times(t, function() { return elem; });
112 | },
113 |
114 | // Returns an array built from the contents of a given array repeated
115 | // a certain number of times.
116 | cycle: function(t, elems) {
117 | return _.flatten(_.times(t, function() { return elems; }), true);
118 | },
119 |
120 | // Returns an array with two internal arrays built from
121 | // taking an original array and spliting it at an index.
122 | splitAt: function(array, index) {
123 | return [_.take(array, index), _.drop(array, index)];
124 | },
125 |
126 | // Call a function recursively f(f(f(args))) until a second
127 | // given function goes falsey. Expects a seed value to start.
128 | iterateUntil: function(doit, checkit, seed) {
129 | var ret = [];
130 | var result = doit(seed);
131 |
132 | while (checkit(result)) {
133 | ret.push(result);
134 | result = doit(result);
135 | }
136 |
137 | return ret;
138 | },
139 |
140 | // Takes every nth item from an array, returning an array of
141 | // the results.
142 | takeSkipping: function(array, n) {
143 | var ret = [];
144 | var sz = _.size(array);
145 |
146 | if (n <= 0) return [];
147 | if (n === 1) return array;
148 |
149 | for(var index = 0; index < sz; index += n) {
150 | ret.push(array[index]);
151 | }
152 |
153 | return ret;
154 | },
155 |
156 | // Returns an array of each intermediate stage of a call to
157 | // a `reduce`-like function.
158 | reductions: function(array, fun, init) {
159 | var ret = [];
160 | var acc = init;
161 |
162 | _.each(array, function(v,k) {
163 | acc = fun(acc, array[k]);
164 | ret.push(acc);
165 | });
166 |
167 | return ret;
168 | },
169 |
170 | // Runs its given function on the index of the elements rather than
171 | // the elements themselves, keeping all of the truthy values in the end.
172 | keepIndexed: function(array, pred) {
173 | return _.filter(_.map(_.range(_.size(array)), function(i) {
174 | return pred(i, array[i]);
175 | }),
176 | existy);
177 | },
178 |
179 | // Accepts an array-like object (other than strings) as an argument and
180 | // returns an array whose elements are in the reverse order. Unlike the
181 | // built-in `Array.prototype.reverse` method, this does not mutate the
182 | // original object. Note: attempting to use this method on a string will
183 | // result in a `TypeError`, as it cannot properly reverse unicode strings.
184 | reverseOrder: function(obj) {
185 | if (typeof obj == 'string')
186 | throw new TypeError('Strings cannot be reversed by _.reverseOrder');
187 | return slice.call(obj).reverse();
188 | },
189 |
190 | // Returns copy or array sorted according to arbitrary ordering
191 | // order must be an array of values; defines the custom sort
192 | // key must be a valid argument to _.iteratee
193 | collate: function(array, order, key) {
194 | if (typeof array.length != "number") throw new TypeError("expected an array-like first argument");
195 | if (typeof order.length != "number") throw new TypeError("expected an array-like second argument");
196 |
197 | var original = slice.call(array);
198 | var valA, valB;
199 | var cb = _.iteratee(key);
200 | return sort.call(original, function (a, b) {
201 | var rankA = _.indexOf(order, cb(a));
202 | var rankB = _.indexOf(order, cb(b));
203 |
204 | if(rankA === -1) return 1;
205 | if(rankB === -1) return -1;
206 |
207 | return rankA - rankB;
208 | });
209 | },
210 |
211 | // Creates an array with all possible combinations of elements from
212 | // the given arrays
213 | combinations: function(){
214 | return _.reduce(slice.call(arguments, 1),function(ret,newarr){
215 | return _.reduce(ret,function(memo,oldi){
216 | return memo.concat(_.map(newarr,function(newi){
217 | return oldi.concat([newi]);
218 | }));
219 | },[]);
220 | },_.map(arguments[0],function(i){return [i];}));
221 | }
222 |
223 | });
224 |
225 | }).call(this);
226 |
--------------------------------------------------------------------------------
/underscore.array.selectors.js:
--------------------------------------------------------------------------------
1 | // Underscore-contrib (underscore.array.selectors.js 0.3.0)
2 | // (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
3 | // Underscore-contrib may be freely distributed under the MIT license.
4 |
5 | (function() {
6 |
7 | // Baseline setup
8 | // --------------
9 |
10 | // Establish the root object, `window` in the browser, or `require` it on the server.
11 | if (typeof exports === 'object') {
12 | _ = module.exports = require('underscore');
13 | }
14 |
15 | // Helpers
16 | // -------
17 |
18 | // Create quick reference variables for speed access to core prototypes.
19 | var slice = Array.prototype.slice,
20 | concat = Array.prototype.concat;
21 |
22 | var existy = function(x) { return x != null; };
23 | var truthy = function(x) { return (x !== false) && existy(x); };
24 | var isSeq = function(x) { return (_.isArray(x)) || (_.isArguments(x)); };
25 |
26 | // Mixing in the array selectors
27 | // ----------------------------
28 |
29 | _.mixin({
30 | // Returns the second element of an array. Passing **n** will return all but
31 | // the first of the head N values in the array. The **guard** check allows it
32 | // to work with `_.map`.
33 | second: function(array, n, guard) {
34 | if (array == null) return void 0;
35 | return (n != null) && !guard ? slice.call(array, 1, n) : array[1];
36 | },
37 |
38 | // Returns the third element of an array. Passing **n** will return all but
39 | // the first two of the head N values in the array. The **guard** check allows it
40 | // to work with `_.map`.
41 | third: function(array, n, guard) {
42 | if (array == null) return void 0;
43 | return (n != null) && !guard ? slice.call(array, 2, n) : array[2];
44 | },
45 |
46 | // A function to get at an index into an array
47 | nth: function(array, index, guard) {
48 | if ((index != null) && !guard) return array[index];
49 | },
50 |
51 | // A function to get values via indices into an array
52 | nths: nths = function(array, indices) {
53 | if (array == null) return void 0;
54 |
55 | if (isSeq(indices))
56 | return _(indices).map(function(i){return array[i];});
57 | else
58 | return nths(array, slice.call(arguments, 1));
59 | },
60 | valuesAt: nths,
61 |
62 | // A function to get at "truthily" indexed values
63 | // bin refers to "binary" nature of true/false values in binIndices
64 | // but can also be thought of as putting array values into either "take" or "don't" bins
65 | binPick: function binPick(array, binIndices) {
66 | if (array == null) return void 0;
67 |
68 | if (isSeq(binIndices))
69 | return _.nths(array, _.range(binIndices.length).filter(function(i){return binIndices[i];}));
70 | else
71 | return binPick(array, slice.call(arguments, 1));
72 | },
73 |
74 | // Takes all items in an array while a given predicate returns truthy.
75 | takeWhile: function(array, pred) {
76 | if (!isSeq(array)) throw new TypeError;
77 |
78 | var sz = _.size(array);
79 |
80 | for (var index = 0; index < sz; index++) {
81 | if(!truthy(pred(array[index]))) {
82 | break;
83 | }
84 | }
85 |
86 | return _.take(array, index);
87 | },
88 |
89 | // Drops all items from an array while a given predicate returns truthy.
90 | dropWhile: function(array, pred) {
91 | if (!isSeq(array)) throw new TypeError;
92 |
93 | var sz = _.size(array);
94 |
95 | for (var index = 0; index < sz; index++) {
96 | if(!truthy(pred(array[index])))
97 | break;
98 | }
99 |
100 | return _.drop(array, index);
101 | },
102 |
103 | // Returns an array with two internal arrays built from
104 | // taking an original array and spliting it at the index
105 | // where a given function goes falsey.
106 | splitWith: function(array, pred) {
107 | return [_.takeWhile(array, pred), _.dropWhile(array, pred)];
108 | },
109 |
110 | // Takes an array and partitions it as the given predicate changes
111 | // truth sense.
112 | partitionBy: function(array, fun){
113 | if (_.isEmpty(array) || !existy(array)) return [];
114 |
115 | var fst = _.first(array);
116 | var fstVal = fun(fst);
117 | var run = concat.call([fst], _.takeWhile(_.rest(array), function(e) {
118 | return _.isEqual(fstVal, fun(e));
119 | }));
120 |
121 | return concat.call([run], _.partitionBy(_.drop(array, _.size(run)), fun));
122 | },
123 |
124 | // Returns the 'best' value in an array based on the result of a
125 | // given function.
126 | best: function(array, fun) {
127 | return _.reduce(array, function(x, y) {
128 | return fun(x, y) ? x : y;
129 | });
130 | },
131 |
132 | // Returns an array of existy results of a function over an source array.
133 | keep: function(array, fun) {
134 | if (!isSeq(array)) throw new TypeError("expected an array as the first argument");
135 |
136 | return _.filter(_.map(array, function(e) {
137 | return fun(e);
138 | }), existy);
139 | }
140 | });
141 |
142 | }).call(this);
143 |
--------------------------------------------------------------------------------
/underscore.collections.walk.js:
--------------------------------------------------------------------------------
1 | // Underscore-contrib (underscore.collections.walk.js 0.3.0)
2 | // (c) 2013 Patrick Dubroy
3 | // Underscore-contrib may be freely distributed under the MIT license.
4 |
5 | (function() {
6 |
7 | // Baseline setup
8 | // --------------
9 |
10 | // Establish the root object, `window` in the browser, or `require` it on the server.
11 | if (typeof exports === 'object') {
12 | _ = module.exports = require('underscore');
13 | }
14 |
15 | // Helpers
16 | // -------
17 |
18 | // An internal object that can be returned from a visitor function to
19 | // prevent a top-down walk from walking subtrees of a node.
20 | var stopRecursion = {};
21 |
22 | // An internal object that can be returned from a visitor function to
23 | // cause the walk to immediately stop.
24 | var stopWalk = {};
25 |
26 | var notTreeError = 'Not a tree: same object found in two different branches';
27 |
28 | // Implements the default traversal strategy: if `obj` is a DOM node, walk
29 | // its DOM children; otherwise, walk all the objects it references.
30 | function defaultTraversal(obj) {
31 | return _.isElement(obj) ? obj.children : obj;
32 | }
33 |
34 | // Walk the tree recursively beginning with `root`, calling `beforeFunc`
35 | // before visiting an objects descendents, and `afterFunc` afterwards.
36 | // If `collectResults` is true, the last argument to `afterFunc` will be a
37 | // collection of the results of walking the node's subtrees.
38 | function walkImpl(root, traversalStrategy, beforeFunc, afterFunc, context, collectResults) {
39 | var visited = [];
40 | return (function _walk(value, key, parent) {
41 | // Keep track of objects that have been visited, and throw an exception
42 | // when trying to visit the same object twice.
43 | if (_.isObject(value)) {
44 | if (visited.indexOf(value) >= 0) throw new TypeError(notTreeError);
45 | visited.push(value);
46 | }
47 |
48 | if (beforeFunc) {
49 | var result = beforeFunc.call(context, value, key, parent);
50 | if (result === stopWalk) return stopWalk;
51 | if (result === stopRecursion) return;
52 | }
53 |
54 | var subResults;
55 | var target = traversalStrategy(value);
56 | if (_.isObject(target) && !_.isEmpty(target)) {
57 | // If collecting results from subtrees, collect them in the same shape
58 | // as the parent node.
59 | if (collectResults) subResults = _.isArray(value) ? [] : {};
60 |
61 | var stop = _.any(target, function(obj, key) {
62 | var result = _walk(obj, key, value);
63 | if (result === stopWalk) return true;
64 | if (subResults) subResults[key] = result;
65 | });
66 | if (stop) return stopWalk;
67 | }
68 | if (afterFunc) return afterFunc.call(context, value, key, parent, subResults);
69 | })(root);
70 | }
71 |
72 | // Internal helper providing the implementation for `pluck` and `pluckRec`.
73 | function pluck(obj, propertyName, recursive) {
74 | var results = [];
75 | this.preorder(obj, function(value, key) {
76 | if (!recursive && key == propertyName)
77 | return stopRecursion;
78 | if (_.has(value, propertyName))
79 | results[results.length] = value[propertyName];
80 | });
81 | return results;
82 | }
83 |
84 | var exports = {
85 | // Performs a preorder traversal of `obj` and returns the first value
86 | // which passes a truth test.
87 | find: function(obj, visitor, context) {
88 | var result;
89 | this.preorder(obj, function(value, key, parent) {
90 | if (visitor.call(context, value, key, parent)) {
91 | result = value;
92 | return stopWalk;
93 | }
94 | }, context);
95 | return result;
96 | },
97 |
98 | // Recursively traverses `obj` and returns all the elements that pass a
99 | // truth test. `strategy` is the traversal function to use, e.g. `preorder`
100 | // or `postorder`.
101 | filter: function(obj, strategy, visitor, context) {
102 | var results = [];
103 | if (obj == null) return results;
104 | strategy(obj, function(value, key, parent) {
105 | if (visitor.call(context, value, key, parent)) results.push(value);
106 | }, null, this._traversalStrategy);
107 | return results;
108 | },
109 |
110 | // Recursively traverses `obj` and returns all the elements for which a
111 | // truth test fails.
112 | reject: function(obj, strategy, visitor, context) {
113 | return this.filter(obj, strategy, function(value, key, parent) {
114 | return !visitor.call(context, value, key, parent);
115 | });
116 | },
117 |
118 | // Produces a new array of values by recursively traversing `obj` and
119 | // mapping each value through the transformation function `visitor`.
120 | // `strategy` is the traversal function to use, e.g. `preorder` or
121 | // `postorder`.
122 | map: function(obj, strategy, visitor, context) {
123 | var results = [];
124 | strategy(obj, function(value, key, parent) {
125 | results[results.length] = visitor.call(context, value, key, parent);
126 | }, null, this._traversalStrategy);
127 | return results;
128 | },
129 |
130 | // Return the value of properties named `propertyName` reachable from the
131 | // tree rooted at `obj`. Results are not recursively searched; use
132 | // `pluckRec` for that.
133 | pluck: function(obj, propertyName) {
134 | return pluck.call(this, obj, propertyName, false);
135 | },
136 |
137 | // Version of `pluck` which recursively searches results for nested objects
138 | // with a property named `propertyName`.
139 | pluckRec: function(obj, propertyName) {
140 | return pluck.call(this, obj, propertyName, true);
141 | },
142 |
143 | // Recursively traverses `obj` in a depth-first fashion, invoking the
144 | // `visitor` function for each object only after traversing its children.
145 | // `traversalStrategy` is intended for internal callers, and is not part
146 | // of the public API.
147 | postorder: function(obj, visitor, context, traversalStrategy) {
148 | traversalStrategy = traversalStrategy || this._traversalStrategy;
149 | walkImpl(obj, traversalStrategy, null, visitor, context);
150 | },
151 |
152 | // Recursively traverses `obj` in a depth-first fashion, invoking the
153 | // `visitor` function for each object before traversing its children.
154 | // `traversalStrategy` is intended for internal callers, and is not part
155 | // of the public API.
156 | preorder: function(obj, visitor, context, traversalStrategy) {
157 | traversalStrategy = traversalStrategy || this._traversalStrategy;
158 | walkImpl(obj, traversalStrategy, visitor, null, context);
159 | },
160 |
161 | // Builds up a single value by doing a post-order traversal of `obj` and
162 | // calling the `visitor` function on each object in the tree. For leaf
163 | // objects, the `memo` argument to `visitor` is the value of the `leafMemo`
164 | // argument to `reduce`. For non-leaf objects, `memo` is a collection of
165 | // the results of calling `reduce` on the object's children.
166 | reduce: function(obj, visitor, leafMemo, context) {
167 | var reducer = function(value, key, parent, subResults) {
168 | return visitor(subResults || leafMemo, value, key, parent);
169 | };
170 | return walkImpl(obj, this._traversalStrategy, null, reducer, context, true);
171 | },
172 |
173 | // Determine if the array contains a number of repated values
174 | containsAtLeast: function(list, count, value) {
175 | var filtered = _.filter(list, function(item) {
176 | return _.isEqual(item, value);
177 | });
178 | return _.gte(_.size(filtered), count);
179 | },
180 |
181 | // Determine if the array contains a number of repated values
182 | containsAtMost: function(list, count, value) {
183 | var filtered = _.filter(list, function(item) {
184 | return _.isEqual(item, value);
185 | });
186 | return _.lte(_.size(filtered), count);
187 | }
188 | };
189 |
190 | // Set up aliases to match those in underscore.js.
191 | exports.collect = exports.map;
192 | exports.detect = exports.find;
193 | exports.select = exports.filter;
194 |
195 | // Returns an object containing the walk functions. If `traversalStrategy`
196 | // is specified, it is a function determining how objects should be
197 | // traversed. Given an object, it returns the object to be recursively
198 | // walked. The default strategy is equivalent to `_.identity` for regular
199 | // objects, and for DOM nodes it returns the node's DOM children.
200 | _.walk = function(traversalStrategy) {
201 | var walker = _.clone(exports);
202 |
203 | // Bind all of the public functions in the walker to itself. This allows
204 | // the traversal strategy to be dynamically scoped.
205 | _.bindAll.apply(null, [walker].concat(_.keys(walker)));
206 |
207 | walker._traversalStrategy = traversalStrategy || defaultTraversal;
208 | return walker;
209 | };
210 |
211 | // Use `_.walk` as a namespace to hold versions of the walk functions which
212 | // use the default traversal strategy.
213 | _.extend(_.walk, _.walk());
214 | }).call(this);
215 |
--------------------------------------------------------------------------------
/underscore.function.arity.js:
--------------------------------------------------------------------------------
1 | // Underscore-contrib (underscore.function.arity.js 0.3.0)
2 | // (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
3 | // Underscore-contrib may be freely distributed under the MIT license.
4 |
5 | (function() {
6 |
7 | // Baseline setup
8 | // --------------
9 |
10 | // Establish the root object, `window` in the browser, or `require` it on the server.
11 | if (typeof exports === 'object') {
12 | _ = module.exports = require('underscore');
13 | }
14 |
15 | // Helpers
16 | // -------
17 |
18 | function enforcesUnary (fn) {
19 | return function mustBeUnary () {
20 | if (arguments.length === 1) {
21 | return fn.apply(this, arguments);
22 | }
23 | else throw new RangeError('Only a single argument may be accepted.');
24 |
25 | };
26 | }
27 |
28 | // Curry
29 | // -------
30 | var curry = (function () {
31 | function collectArgs(func, that, argCount, args, newArg, reverse) {
32 | if (reverse === true) {
33 | args.unshift(newArg);
34 | } else {
35 | args.push(newArg);
36 | }
37 | if (args.length == argCount) {
38 | return func.apply(that, args);
39 | } else {
40 | return enforcesUnary(function () {
41 | return collectArgs(func, that, argCount, args.slice(0), arguments[0], reverse);
42 | });
43 | }
44 | }
45 | return function curry (func, reverse) {
46 | var that = this;
47 | return enforcesUnary(function () {
48 | return collectArgs(func, that, func.length, [], arguments[0], reverse);
49 | });
50 | };
51 | }());
52 |
53 | // Enforce Arity
54 | // --------------------
55 | var enforce = (function () {
56 | var CACHE = [];
57 | return function enforce (func) {
58 | if (typeof func !== 'function') {
59 | throw new Error('Argument 1 must be a function.');
60 | }
61 | var funcLength = func.length;
62 | if (CACHE[funcLength] === undefined) {
63 | CACHE[funcLength] = function (enforceFunc) {
64 | return function () {
65 | if (arguments.length !== funcLength) {
66 | throw new RangeError(funcLength + ' arguments must be applied.');
67 | }
68 | return enforceFunc.apply(this, arguments);
69 | };
70 | };
71 | }
72 | return CACHE[funcLength](func);
73 | };
74 | }());
75 |
76 | // Right curry variants
77 | // ---------------------
78 | var curryRight = function (func) {
79 | return curry.call(this, func, true);
80 | };
81 |
82 | var curryRight2 = function (fun) {
83 | return enforcesUnary(function (last) {
84 | return enforcesUnary(function (first) {
85 | return fun.call(this, first, last);
86 | });
87 | });
88 | };
89 |
90 | var curryRight3 = function (fun) {
91 | return enforcesUnary(function (last) {
92 | return enforcesUnary(function (second) {
93 | return enforcesUnary(function (first) {
94 | return fun.call(this, first, second, last);
95 | });
96 | });
97 | });
98 | };
99 |
100 | // Mixing in the arity functions
101 | // -----------------------------
102 |
103 | _.mixin({
104 | // ### Fixed arguments
105 |
106 | // Fixes the arguments to a function based on the parameter template defined by
107 | // the presence of values and the `_` placeholder.
108 | fix: function(fun) {
109 | var fixArgs = _.rest(arguments);
110 |
111 | var f = function() {
112 | var args = fixArgs.slice();
113 | var arg = 0;
114 |
115 | for ( var i = 0; i < (args.length || arg < arguments.length); i++ ) {
116 | if ( args[i] === _ ) {
117 | args[i] = arguments[arg++];
118 | }
119 | }
120 |
121 | return fun.apply(null, args);
122 | };
123 |
124 | f._original = fun;
125 |
126 | return f;
127 | },
128 |
129 | unary: function (fun) {
130 | return function unary (a) {
131 | return fun.call(this, a);
132 | };
133 | },
134 |
135 | binary: function (fun) {
136 | return function binary (a, b) {
137 | return fun.call(this, a, b);
138 | };
139 | },
140 |
141 | ternary: function (fun) {
142 | return function ternary (a, b, c) {
143 | return fun.call(this, a, b, c);
144 | };
145 | },
146 |
147 | quaternary: function (fun) {
148 | return function quaternary (a, b, c, d) {
149 | return fun.call(this, a, b, c, d);
150 | };
151 | },
152 |
153 | // Flexible curry function with strict arity.
154 | // Argument application left to right.
155 | // source: https://github.com/eborden/js-curry
156 | curry: curry,
157 |
158 | // Flexible right to left curry with strict arity.
159 | curryRight: curryRight,
160 | rCurry: curryRight, // alias for backwards compatibility
161 |
162 |
163 | curry2: function (fun) {
164 | return enforcesUnary(function curried (first) {
165 | return enforcesUnary(function (last) {
166 | return fun.call(this, first, last);
167 | });
168 | });
169 | },
170 |
171 | curry3: function (fun) {
172 | return enforcesUnary(function (first) {
173 | return enforcesUnary(function (second) {
174 | return enforcesUnary(function (last) {
175 | return fun.call(this, first, second, last);
176 | });
177 | });
178 | });
179 | },
180 |
181 | // reverse currying for functions taking two arguments.
182 | curryRight2: curryRight2,
183 | rcurry2: curryRight2, // alias for backwards compatibility
184 |
185 | curryRight3: curryRight3,
186 | rcurry3: curryRight3, // alias for backwards compatibility
187 |
188 | // Dynamic decorator to enforce function arity and defeat varargs.
189 | enforce: enforce
190 | });
191 |
192 | _.arity = (function () {
193 | // Allow 'new Function', as that is currently the only reliable way
194 | // to manipulate function.length
195 | /* jshint -W054 */
196 | var FUNCTIONS = {};
197 | return function arity (numberOfArgs, fun) {
198 | if (FUNCTIONS[numberOfArgs] == null) {
199 | var parameters = new Array(numberOfArgs);
200 | for (var i = 0; i < numberOfArgs; ++i) {
201 | parameters[i] = "__" + i;
202 | }
203 | var pstr = parameters.join();
204 | var code = "return function ("+pstr+") { return fun.apply(this, arguments); };";
205 | FUNCTIONS[numberOfArgs] = new Function(['fun'], code);
206 | }
207 | if (fun == null) {
208 | return function (fun) { return arity(numberOfArgs, fun); };
209 | }
210 | else return FUNCTIONS[numberOfArgs](fun);
211 | };
212 | })();
213 |
214 | }).call(this);
215 |
--------------------------------------------------------------------------------
/underscore.function.combinators.js:
--------------------------------------------------------------------------------
1 | // Underscore-contrib (underscore.function.combinators.js 0.3.0)
2 | // (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
3 | // Underscore-contrib may be freely distributed under the MIT license.
4 |
5 | (function() {
6 |
7 | // Baseline setup
8 | // --------------
9 |
10 | // Establish the root object, `window` in the browser, or `require` it on the server.
11 | if (typeof exports === 'object') {
12 | _ = module.exports = require('underscore');
13 | }
14 |
15 | // Helpers
16 | // -------
17 |
18 | var existy = function(x) { return x != null; };
19 | var truthy = function(x) { return (x !== false) && existy(x); };
20 | var __reverse = [].reverse;
21 | var __slice = [].slice;
22 | var __map = [].map;
23 | var curry2 = function (fun) {
24 | return function curried (first, optionalLast) {
25 | if (arguments.length === 1) {
26 | return function (last) {
27 | return fun(first, last);
28 | };
29 | }
30 | else return fun(first, optionalLast);
31 | };
32 | };
33 |
34 | var createPredicateApplicator = function (funcToInvoke /*, preds */) {
35 | var preds = _(arguments).tail();
36 |
37 | return function (objToCheck) {
38 | var array = _(objToCheck).cat();
39 |
40 | return _[funcToInvoke](array, function (e) {
41 | return _[funcToInvoke](preds, function (p) {
42 | return p(e);
43 | });
44 | });
45 | };
46 | };
47 |
48 | // n.b. depends on underscore.function.arity.js
49 | // n.b. depends on underscore.array.builders.js
50 |
51 | // Takes a target function and a mapping function. Returns a function
52 | // that applies the mapper to its arguments before evaluating the body.
53 | function baseMapArgs (fun, mapFun) {
54 | return _.arity(fun.length, function () {
55 | return fun.apply(this, __map.call(arguments, mapFun));
56 | });
57 | }
58 |
59 | // Mixing in the combinator functions
60 | // ----------------------------------
61 |
62 | _.mixin({
63 | // Provide "always" alias for backwards compatibility
64 | always: _.constant,
65 |
66 | // Takes some number of functions, either as an array or variadically
67 | // and returns a function that takes some value as its first argument
68 | // and runs it through a pipeline of the original functions given.
69 | pipeline: function(/*, funs */){
70 | var funs = (_.isArray(arguments[0])) ? arguments[0] : arguments;
71 |
72 | return function(seed) {
73 | return _.reduce(funs,
74 | function(l,r) { return r(l); },
75 | seed);
76 | };
77 | },
78 |
79 | // Composes a bunch of predicates into a single predicate that
80 | // checks all elements of an array for conformance to all of the
81 | // original predicates.
82 | conjoin: _.partial(createPredicateApplicator, ('every')),
83 |
84 | // Composes a bunch of predicates into a single predicate that
85 | // checks all elements of an array for conformance to any of the
86 | // original predicates.
87 | disjoin: _.partial(createPredicateApplicator, 'some'),
88 |
89 | // Takes a predicate-like and returns a comparator (-1,0,1).
90 | comparator: function(fun) {
91 | return function(x, y) {
92 | if (truthy(fun(x, y)))
93 | return -1;
94 | else if (truthy(fun(y, x)))
95 | return 1;
96 | else
97 | return 0;
98 | };
99 | },
100 |
101 | // Returns a function that reverses the sense of a given predicate-like.
102 | complement: function(pred) {
103 | return function() {
104 | return !pred.apply(this, arguments);
105 | };
106 | },
107 |
108 | // Takes a function expecting varargs and
109 | // returns a function that takes an array and
110 | // uses its elements as the args to the original
111 | // function
112 | splat: function(fun) {
113 | return function(array) {
114 | return fun.apply(this, array);
115 | };
116 | },
117 |
118 | // Takes a function expecting an array and returns
119 | // a function that takes varargs and wraps all
120 | // in an array that is passed to the original function.
121 | unsplat: function(fun) {
122 | var funLength = fun.length;
123 |
124 | if (funLength < 1) {
125 | return fun;
126 | }
127 | else if (funLength === 1) {
128 | return function () {
129 | return fun.call(this, __slice.call(arguments, 0));
130 | };
131 | }
132 | else {
133 | return function () {
134 | var numberOfArgs = arguments.length,
135 | namedArgs = __slice.call(arguments, 0, funLength - 1),
136 | numberOfMissingNamedArgs = Math.max(funLength - numberOfArgs - 1, 0),
137 | argPadding = new Array(numberOfMissingNamedArgs),
138 | variadicArgs = __slice.call(arguments, fun.length - 1);
139 |
140 | return fun.apply(this, namedArgs.concat(argPadding).concat([variadicArgs]));
141 | };
142 | }
143 | },
144 |
145 | // Same as unsplat, but the rest of the arguments are collected in the
146 | // first parameter, e.g. unsplatl( function (args, callback) { ... ]})
147 | unsplatl: function(fun) {
148 | var funLength = fun.length;
149 |
150 | if (funLength < 1) {
151 | return fun;
152 | }
153 | else if (funLength === 1) {
154 | return function () {
155 | return fun.call(this, __slice.call(arguments, 0));
156 | };
157 | }
158 | else {
159 | return function () {
160 | var numberOfArgs = arguments.length,
161 | namedArgs = __slice.call(arguments, Math.max(numberOfArgs - funLength + 1, 0)),
162 | variadicArgs = __slice.call(arguments, 0, Math.max(numberOfArgs - funLength + 1, 0));
163 |
164 | return fun.apply(this, [variadicArgs].concat(namedArgs));
165 | };
166 | }
167 | },
168 |
169 | // map the arguments of a function
170 | mapArgs: curry2(baseMapArgs),
171 |
172 | // Returns a function that returns an array of the calls to each
173 | // given function for some arguments.
174 | juxt: function(/* funs */) {
175 | var funs = arguments;
176 |
177 | return function(/* args */) {
178 | var args = arguments;
179 | return _.map(funs, function(f) {
180 | return f.apply(this, args);
181 | }, this);
182 | };
183 | },
184 |
185 | // Returns a function that protects a given function from receiving
186 | // non-existy values. Each subsequent value provided to `fnull` acts
187 | // as the default to the original function should a call receive non-existy
188 | // values in the defaulted arg slots.
189 | fnull: function(fun /*, defaults */) {
190 | var defaults = _.rest(arguments);
191 |
192 | return function(/*args*/) {
193 | var args = _.toArray(arguments);
194 | var sz = _.size(defaults);
195 |
196 | for(var i = 0; i < sz; i++) {
197 | if (!existy(args[i]))
198 | args[i] = defaults[i];
199 | }
200 |
201 | return fun.apply(this, args);
202 | };
203 | },
204 |
205 | // Flips the first two args of a function
206 | flip2: function(fun) {
207 | return function(/* args */) {
208 | var flipped = __slice.call(arguments);
209 | flipped[0] = arguments[1];
210 | flipped[1] = arguments[0];
211 |
212 | return fun.apply(this, flipped);
213 | };
214 | },
215 |
216 | // Flips an arbitrary number of args of a function
217 | flip: function(fun) {
218 | return function(/* args */) {
219 | var reversed = __reverse.call(arguments);
220 |
221 | return fun.apply(this, reversed);
222 | };
223 | },
224 |
225 | // Takes a method-style function (one which uses `this`) and pushes
226 | // `this` into the argument list. The returned function uses its first
227 | // argument as the receiver/context of the original function, and the rest
228 | // of the arguments are used as the original's entire argument list.
229 | functionalize: function(method) {
230 | return function(ctx /*, args */) {
231 | return method.apply(ctx, _.rest(arguments));
232 | };
233 | },
234 |
235 | // Takes a function and pulls the first argument out of the argument
236 | // list and into `this` position. The returned function calls the original
237 | // with its receiver (`this`) prepending the argument list. The original
238 | // is called with a receiver of `null`.
239 | methodize: function(func) {
240 | return function(/* args */) {
241 | return func.apply(null, _.cons(this, arguments));
242 | };
243 | },
244 |
245 | k: _.always,
246 | t: _.pipeline
247 | });
248 |
249 | _.unsplatr = _.unsplat;
250 |
251 | // map the arguments of a function, takes the mapping function
252 | // first so it can be used as a combinator
253 | _.mapArgsWith = curry2(_.flip(baseMapArgs));
254 |
255 | // Returns function property of object by name, bound to object
256 | _.bound = function(obj, fname) {
257 | var fn = obj[fname];
258 | if (!_.isFunction(fn))
259 | throw new TypeError("Expected property to be a function");
260 | return _.bind(fn, obj);
261 | };
262 |
263 | }).call(this);
264 |
--------------------------------------------------------------------------------
/underscore.function.dispatch.js:
--------------------------------------------------------------------------------
1 | // Underscore-contrib (underscore.function.dispatch.js 0.3.0)
2 | // (c) 2013 Justin Ridgewell
3 | // Underscore-contrib may be freely distributed under the MIT license.
4 |
5 | (function() {
6 |
7 | // Baseline setup
8 | // --------------
9 |
10 | // Establish the root object, `window` in the browser, or `require` it on the server.
11 | if (typeof exports === 'object') {
12 | _ = module.exports = require('underscore');
13 | }
14 |
15 | // Helpers
16 | // -------
17 |
18 | // Create quick reference variable for speed.
19 | var slice = Array.prototype.slice;
20 |
21 | // Mixing in the attempt function
22 | // ------------------------
23 |
24 | _.mixin({
25 | // If object is not undefined or null then invoke the named `method` function
26 | // with `object` as context and arguments; otherwise, return undefined.
27 | attempt: function(object, method) {
28 | if (object == null) return void 0;
29 | var func = object[method];
30 | var args = slice.call(arguments, 2);
31 | return _.isFunction(func) ? func.apply(object, args) : void 0;
32 | }
33 | });
34 |
35 | }).call(this);
36 |
--------------------------------------------------------------------------------
/underscore.function.iterators.js:
--------------------------------------------------------------------------------
1 | // Underscore-contrib (underscore.function.iterators.js 0.3.0)
2 | // (c) 2013 Michael Fogus and DocumentCloud Inc.
3 | // Underscore-contrib may be freely distributed under the MIT license.
4 |
5 | (function() {
6 |
7 | // Baseline setup
8 | // --------------
9 |
10 | // Establish the root object, `window` in the browser, or `require` it on the server.
11 | if (typeof exports === 'object') {
12 | _ = module.exports = require('underscore');
13 | }
14 |
15 | // Helpers
16 | // -------
17 |
18 | var HASNTBEENRUN = {};
19 |
20 | function unary (fun) {
21 | return function (first) {
22 | return fun.call(this, first);
23 | };
24 | }
25 |
26 | function binary (fun) {
27 | return function (first, second) {
28 | return fun.call(this, first, second);
29 | };
30 | }
31 |
32 | // Mixing in the iterator functions
33 | // --------------------------------
34 |
35 | function foldl (iter, binaryFn, seed) {
36 | var state, element;
37 | if (seed !== void 0) {
38 | state = seed;
39 | }
40 | else {
41 | state = iter();
42 | }
43 | element = iter();
44 | while (element != null) {
45 | state = binaryFn.call(element, state, element);
46 | element = iter();
47 | }
48 | return state;
49 | }
50 |
51 | function unfold (seed, unaryFn) {
52 | var state = HASNTBEENRUN;
53 | return function () {
54 | if (state === HASNTBEENRUN) {
55 | state = seed;
56 | } else if (state != null) {
57 | state = unaryFn.call(state, state);
58 | }
59 |
60 | return state;
61 | };
62 | }
63 |
64 | // note that the unfoldWithReturn behaves differently than
65 | // unfold with respect to the first value returned
66 | function unfoldWithReturn (seed, unaryFn) {
67 | var state = seed,
68 | pair,
69 | value;
70 | return function () {
71 | if (state != null) {
72 | pair = unaryFn.call(state, state);
73 | value = pair[1];
74 | state = value != null ? pair[0] : void 0;
75 | return value;
76 | }
77 | else return void 0;
78 | };
79 | }
80 |
81 | function accumulate (iter, binaryFn, initial) {
82 | var state = initial;
83 | return function () {
84 | var element = iter();
85 | if (element == null) {
86 | return element;
87 | }
88 | else {
89 | if (state === void 0) {
90 | state = element;
91 | } else {
92 | state = binaryFn.call(element, state, element);
93 | }
94 |
95 | return state;
96 | }
97 | };
98 | }
99 |
100 | function accumulateWithReturn (iter, binaryFn, initial) {
101 | var state = initial,
102 | stateAndReturnValue,
103 | element;
104 | return function () {
105 | element = iter();
106 | if (element == null) {
107 | return element;
108 | }
109 | else {
110 | if (state === void 0) {
111 | state = element;
112 | return state;
113 | }
114 | else {
115 | stateAndReturnValue = binaryFn.call(element, state, element);
116 | state = stateAndReturnValue[0];
117 | return stateAndReturnValue[1];
118 | }
119 | }
120 | };
121 | }
122 |
123 | function map (iter, unaryFn) {
124 | return function() {
125 | var element;
126 | element = iter();
127 | if (element != null) {
128 | return unaryFn.call(element, element);
129 | } else {
130 | return void 0;
131 | }
132 | };
133 | }
134 |
135 | function mapcat(iter, unaryFn) {
136 | var lastIter = null;
137 | return function() {
138 | var element;
139 | var gen;
140 | if (lastIter == null) {
141 | gen = iter();
142 | if (gen == null) {
143 | lastIter = null;
144 | return void 0;
145 | }
146 | lastIter = unaryFn.call(gen, gen);
147 | }
148 | while (element == null) {
149 | element = lastIter();
150 | if (element == null) {
151 | gen = iter();
152 | if (gen == null) {
153 | lastIter = null;
154 | return void 0;
155 | }
156 | else {
157 | lastIter = unaryFn.call(gen, gen);
158 | }
159 | }
160 | }
161 | return element;
162 | };
163 | }
164 |
165 | function select (iter, unaryPredicateFn) {
166 | return function() {
167 | var element;
168 | element = iter();
169 | while (element != null) {
170 | if (unaryPredicateFn.call(element, element)) {
171 | return element;
172 | }
173 | element = iter();
174 | }
175 | return void 0;
176 | };
177 | }
178 |
179 | function reject (iter, unaryPredicateFn) {
180 | return select(iter, function (something) {
181 | return !unaryPredicateFn(something);
182 | });
183 | }
184 |
185 | function find (iter, unaryPredicateFn) {
186 | return select(iter, unaryPredicateFn)();
187 | }
188 |
189 | function slice (iter, numberToDrop, numberToTake) {
190 | var count = 0;
191 | while (numberToDrop-- > 0) {
192 | iter();
193 | }
194 | if (numberToTake != null) {
195 | return function() {
196 | if (++count <= numberToTake) {
197 | return iter();
198 | } else {
199 | return void 0;
200 | }
201 | };
202 | }
203 | else return iter;
204 | }
205 |
206 | function drop (iter, numberToDrop) {
207 | return slice(iter, numberToDrop == null ? 1 : numberToDrop);
208 | }
209 |
210 | function take (iter, numberToTake) {
211 | return slice(iter, 0, numberToTake == null ? 1 : numberToTake);
212 | }
213 |
214 | function List (array) {
215 | var index = 0;
216 | return function() {
217 | return array[index++];
218 | };
219 | }
220 |
221 | function cycle(array) {
222 | var index = 0,
223 | length = array.length;
224 | return function() {
225 | return array[index++ % length];
226 | };
227 | }
228 |
229 | function Tree (array) {
230 | var index, myself, state;
231 | index = 0;
232 | state = [];
233 | myself = function() {
234 | var element, tempState;
235 | element = array[index++];
236 | if (element instanceof Array) {
237 | state.push({
238 | array: array,
239 | index: index
240 | });
241 | array = element;
242 | index = 0;
243 | return myself();
244 | } else if (element === void 0) {
245 | if (state.length > 0) {
246 | tempState = state.pop();
247 | array = tempState.array;
248 | index = tempState.index;
249 | return myself();
250 | } else {
251 | return void 0;
252 | }
253 | } else {
254 | return element;
255 | }
256 | };
257 | return myself;
258 | }
259 |
260 | function K (value) {
261 | return function () {
262 | return value;
263 | };
264 | }
265 |
266 | function upRange (from, to, by) {
267 | return function () {
268 | var was;
269 |
270 | if (from > to) {
271 | return void 0;
272 | }
273 | else {
274 | was = from;
275 | from = from + by;
276 | return was;
277 | }
278 | };
279 | }
280 |
281 | function downRange (from, to, by) {
282 | return function () {
283 | var was;
284 |
285 | if (from < to) {
286 | return void 0;
287 | }
288 | else {
289 | was = from;
290 | from = from - by;
291 | return was;
292 | }
293 | };
294 | }
295 |
296 | function range (from, to, by) {
297 | if (from == null) {
298 | return upRange(1, Infinity, 1);
299 | }
300 | else if (to == null) {
301 | return upRange(from, Infinity, 1);
302 | }
303 | else if (by == null) {
304 | if (from <= to) {
305 | return upRange(from, to, 1);
306 | }
307 | else return downRange(from, to, 1);
308 | }
309 | else if (by > 0) {
310 | return upRange(from, to, by);
311 | }
312 | else if (by < 0) {
313 | return downRange(from, to, Math.abs(by));
314 | }
315 | else return k(from);
316 | }
317 |
318 | var numbers = unary(range);
319 |
320 | _.iterators = {
321 | accumulate: accumulate,
322 | accumulateWithReturn: accumulateWithReturn,
323 | foldl: foldl,
324 | reduce: foldl,
325 | unfold: unfold,
326 | unfoldWithReturn: unfoldWithReturn,
327 | map: map,
328 | mapcat: mapcat,
329 | select: select,
330 | reject: reject,
331 | filter: select,
332 | find: find,
333 | slice: slice,
334 | drop: drop,
335 | take: take,
336 | List: List,
337 | Tree: Tree,
338 | constant: K,
339 | K: K,
340 | numbers: numbers,
341 | range: range,
342 | cycle: cycle
343 | };
344 |
345 | }).call(this, void 0);
346 |
--------------------------------------------------------------------------------
/underscore.function.predicates.js:
--------------------------------------------------------------------------------
1 | // Underscore-contrib (underscore.function.predicates.js 0.3.0)
2 | // (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
3 | // Underscore-contrib may be freely distributed under the MIT license.
4 |
5 | // Establish the root object, `window` in the browser, or `require` it on the server.
6 | if (typeof exports === 'object') {
7 | _ = module.exports = require('underscore');
8 | }
9 |
10 | // Helpers
11 | // -------
12 |
13 |
14 | // Mixing in the predicate functions
15 | // ---------------------------------
16 |
17 | _.mixin({
18 | // A wrapper around instanceof
19 | isInstanceOf: function(x, t) { return (x instanceof t); },
20 |
21 | // An associative object is one where its elements are
22 | // accessed via a key or index. (i.e. array and object)
23 | isAssociative: function(x) { return _.isArray(x) || _.isObject(x) || _.isArguments(x); },
24 |
25 | // An indexed object is anything that allows numerical index for
26 | // accessing its elements (e.g. arrays and strings). NOTE: Underscore
27 | // does not support cross-browser consistent use of strings as array-like
28 | // objects, so be wary in IE 8 when using String objects and IE<8.
29 | // on string literals & objects.
30 | isIndexed: function(x) { return _.isArray(x) || _.isString(x) || _.isArguments(x); },
31 |
32 | // A seq is something considered a sequential composite type (i.e. arrays and `arguments`).
33 | isSequential: function(x) { return (_.isArray(x)) || (_.isArguments(x)); },
34 |
35 | // Check if an object is an object literal, since _.isObject(function() {}) === _.isObject([]) === true
36 | isPlainObject: function(x) { return _.isObject(x) && x.constructor === Object; },
37 |
38 | // These do what you think that they do
39 | isZero: function(x) { return 0 === x; },
40 | isEven: function(x) { return _.isFinite(x) && (x & 1) === 0; },
41 | isOdd: function(x) { return _.isFinite(x) && !_.isEven(x); },
42 | isPositive: function(x) { return x > 0; },
43 | isNegative: function(x) { return x < 0; },
44 | isValidDate: function(x) { return _.isDate(x) && !_.isNaN(x.getTime()); },
45 |
46 | // A numeric is a variable that contains a numeric value, regardless its type
47 | // It can be a String containing a numeric value, exponential notation, or a Number object
48 | // See here for more discussion: http://stackoverflow.com/questions/18082/validate-numbers-in-javascript-isnumeric/1830844#1830844
49 | isNumeric: function(n) {
50 | return !isNaN(parseFloat(n)) && isFinite(n);
51 | },
52 |
53 | // An integer contains an optional minus sign to begin and only the digits 0-9
54 | // Objects that can be parsed that way are also considered ints, e.g. "123"
55 | // Floats that are mathematically equal to integers are considered integers, e.g. 1.0
56 | // See here for more discussion: http://stackoverflow.com/questions/1019515/javascript-test-for-an-integer
57 | isInteger: function(i) {
58 | return _.isNumeric(i) && i % 1 === 0;
59 | },
60 |
61 | // A float is a numbr that is not an integer.
62 | isFloat: function(n) {
63 | return _.isNumeric(n) && !_.isInteger(n);
64 | },
65 |
66 | // checks if a string is a valid JSON
67 | isJSON: function(str) {
68 | try {
69 | JSON.parse(str);
70 | } catch (e) {
71 | return false;
72 | }
73 | return true;
74 | },
75 |
76 | // Returns true if its arguments are monotonically
77 | // increaing values; false otherwise.
78 | isIncreasing: function() {
79 | var count = _.size(arguments);
80 | if (count === 1) return true;
81 | if (count === 2) return arguments[0] < arguments[1];
82 |
83 | for (var i = 1; i < count; i++) {
84 | if (arguments[i-1] >= arguments[i]) {
85 | return false;
86 | }
87 | }
88 |
89 | return true;
90 | },
91 |
92 | // Returns true if its arguments are monotonically
93 | // decreaing values; false otherwise.
94 | isDecreasing: function() {
95 | var count = _.size(arguments);
96 | if (count === 1) return true;
97 | if (count === 2) return arguments[0] > arguments[1];
98 |
99 | for (var i = 1; i < count; i++) {
100 | if (arguments[i-1] <= arguments[i]) {
101 | return false;
102 | }
103 | }
104 |
105 | return true;
106 | }
107 | });
108 |
--------------------------------------------------------------------------------
/underscore.object.builders.js:
--------------------------------------------------------------------------------
1 | // Underscore-contrib (underscore.object.builders.js 0.3.0)
2 | // (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
3 | // Underscore-contrib may be freely distributed under the MIT license.
4 |
5 | (function() {
6 |
7 | // Baseline setup
8 | // --------------
9 |
10 | // Establish the root object, `window` in the browser, or `require` it on the server.
11 | if (typeof exports === 'object') {
12 | _ = module.exports = require('underscore');
13 | }
14 |
15 | // Helpers
16 | // -------
17 |
18 | // Create quick reference variables for speed access to core prototypes.
19 | var slice = Array.prototype.slice,
20 | concat = Array.prototype.concat;
21 |
22 | var existy = function(x) { return x != null; };
23 | var truthy = function(x) { return (x !== false) && existy(x); };
24 | var isAssociative = function(x) { return _.isArray(x) || _.isObject(x); };
25 | var curry2 = function(fun) {
26 | return function(last) {
27 | return function(first) {
28 | return fun(first, last);
29 | };
30 | };
31 | };
32 |
33 | // Mixing in the object builders
34 | // ----------------------------
35 |
36 | _.mixin({
37 | // Merges two or more objects starting with the left-most and
38 | // applying the keys right-word
39 | // {any:any}* -> {any:any}
40 | merge: function(/* objs */){
41 | var dest = _.some(arguments) ? {} : null;
42 |
43 | if (truthy(dest)) {
44 | _.extend.apply(null, concat.call([dest], _.toArray(arguments)));
45 | }
46 |
47 | return dest;
48 | },
49 |
50 | // Takes an object and another object of strings to strings where the second
51 | // object describes the key renaming to occur in the first object.
52 | renameKeys: function(obj, kobj) {
53 | return _.reduce(kobj, function(o, nu, old) {
54 | if (existy(obj[old])) {
55 | o[nu] = obj[old];
56 | return o;
57 | }
58 | else
59 | return o;
60 | },
61 | _.omit.apply(null, concat.call([obj], _.keys(kobj))));
62 | },
63 |
64 | // Snapshots an object deeply. Based on the version by
65 | // [Keith Devens](http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone)
66 | // until we can find a more efficient and robust way to do it.
67 | snapshot: function(obj) {
68 | if(obj == null || typeof(obj) != 'object') {
69 | return obj;
70 | }
71 |
72 | var temp = new obj.constructor();
73 |
74 | for(var key in obj) {
75 | if (obj.hasOwnProperty(key)) {
76 | temp[key] = _.snapshot(obj[key]);
77 | }
78 | }
79 |
80 | return temp;
81 | },
82 |
83 | // Updates the value at any depth in a nested object based on the
84 | // path described by the keys given. The function provided is supplied
85 | // the current value and is expected to return a value for use as the
86 | // new value. If no keys are provided, then the object itself is presented
87 | // to the given function.
88 | updatePath: function(obj, fun, ks, defaultValue) {
89 | if (!isAssociative(obj)) throw new TypeError("Attempted to update a non-associative object.");
90 | if (!existy(ks)) return fun(obj);
91 |
92 | var deepness = _.isArray(ks);
93 | var keys = deepness ? ks : [ks];
94 | var ret = deepness ? _.snapshot(obj) : _.clone(obj);
95 | var lastKey = _.last(keys);
96 | var target = ret;
97 |
98 | _.each(_.initial(keys), function(key) {
99 | if (defaultValue && !_.has(target, key)) {
100 | target[key] = _.clone(defaultValue);
101 | }
102 | target = target[key];
103 | });
104 |
105 | target[lastKey] = fun(target[lastKey]);
106 | return ret;
107 | },
108 |
109 | // Returns an object excluding the value represented by the path
110 | omitPath: function(obj, ks, copy){
111 | if (!obj) return copy;
112 | if (typeof ks == "string") ks = ks.split(".");
113 | if (!copy) copy = obj = _.snapshot(obj);
114 | if (ks.length > 1) return _.omitPath(obj[ks[0]], _.tail(ks), copy);
115 | delete obj[ks[0]];
116 | return copy;
117 | },
118 |
119 | // Sets the value at any depth in a nested object based on the
120 | // path described by the keys given.
121 | setPath: function(obj, value, ks, defaultValue) {
122 | if (!existy(ks)) throw new TypeError("Attempted to set a property at a null path.");
123 |
124 | return _.updatePath(obj, function() { return value; }, ks, defaultValue);
125 | },
126 |
127 | // Returns an object where each element of an array is keyed to
128 | // the number of times that it occurred in said array.
129 | frequencies: curry2(_.countBy)(_.identity)
130 | });
131 |
132 | }).call(this);
133 |
--------------------------------------------------------------------------------
/underscore.object.selectors.js:
--------------------------------------------------------------------------------
1 | // Underscore-contrib (underscore.object.selectors.js 0.3.0)
2 | // (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
3 | // Underscore-contrib may be freely distributed under the MIT license.
4 |
5 | (function() {
6 |
7 | // Baseline setup
8 | // --------------
9 |
10 | // Establish the root object, `window` in the browser, or `require` it on the server.
11 | if (typeof exports === 'object') {
12 | _ = module.exports = require('underscore');
13 | }
14 |
15 | // Helpers
16 | // -------
17 |
18 | // Create quick reference variables for speed access to core prototypes.
19 | var concat = Array.prototype.concat;
20 | var ArrayProto = Array.prototype;
21 | var slice = ArrayProto.slice;
22 |
23 | // Will take a path like 'element[0][1].subElement["Hey!.What?"]["[hey]"]'
24 | // and return ["element", "0", "1", "subElement", "Hey!.What?", "[hey]"]
25 | function keysFromPath(path) {
26 | // from http://codereview.stackexchange.com/a/63010/8176
27 | /**
28 | * Repeatedly capture either:
29 | * - a bracketed expression, discarding optional matching quotes inside, or
30 | * - an unbracketed expression, delimited by a dot or a bracket.
31 | */
32 | var re = /\[("|')(.+)\1\]|([^.\[\]]+)/g;
33 |
34 | var elements = [];
35 | var result;
36 | while ((result = re.exec(path)) !== null) {
37 | elements.push(result[2] || result[3]);
38 | }
39 | return elements;
40 | }
41 |
42 | // Mixing in the object selectors
43 | // ------------------------------
44 |
45 | _.mixin({
46 | // Returns a function that will attempt to look up a named field
47 | // in any object that it's given.
48 | accessor: function(field) {
49 | return function(obj) {
50 | return (obj && obj[field]);
51 | };
52 | },
53 |
54 | // Given an object, returns a function that will attempt to look up a field
55 | // that it's given.
56 | dictionary: function (obj) {
57 | return function(field) {
58 | return (obj && field && obj[field]);
59 | };
60 | },
61 |
62 | // Like `_.pick` except that it takes an array of keys to pick.
63 | selectKeys: function (obj, ks) {
64 | return _.pick.apply(null, concat.call([obj], ks));
65 | },
66 |
67 | // Returns the key/value pair for a given property in an object, undefined if not found.
68 | kv: function(obj, key) {
69 | if (_.has(obj, key)) {
70 | return [key, obj[key]];
71 | }
72 |
73 | return void 0;
74 | },
75 |
76 | // Gets the value at any depth in a nested object based on the
77 | // path described by the keys given. Keys may be given as an array
78 | // or as a dot-separated string.
79 | getPath: function getPath (obj, ks) {
80 | ks = typeof ks == "string" ? keysFromPath(ks) : ks;
81 |
82 | var i = -1, length = ks.length;
83 |
84 | // If the obj is null or undefined we have to break as
85 | // a TypeError will result trying to access any property
86 | // Otherwise keep incrementally access the next property in
87 | // ks until complete
88 | while (++i < length && obj != null) {
89 | obj = obj[ks[i]];
90 | }
91 | return i === length ? obj : void 0;
92 | },
93 |
94 | // Returns a boolean indicating whether there is a property
95 | // at the path described by the keys given
96 | hasPath: function hasPath (obj, ks) {
97 | ks = typeof ks == "string" ? keysFromPath(ks) : ks;
98 |
99 | var i = -1, length = ks.length;
100 | while (++i < length && _.isObject(obj)) {
101 | if (ks[i] in obj) {
102 | obj = obj[ks[i]];
103 | } else {
104 | return false;
105 | }
106 | }
107 | return i === length;
108 | },
109 |
110 | keysFromPath: keysFromPath,
111 |
112 | pickWhen: function(obj, pred) {
113 | var copy = {};
114 |
115 | _.each(obj, function(value, key) {
116 | if (pred(obj[key])) copy[key] = obj[key];
117 | });
118 |
119 | return copy;
120 | },
121 |
122 | omitWhen: function(obj, pred) {
123 | return _.pickWhen(obj, function(e) { return !pred(e); });
124 | }
125 |
126 | });
127 |
128 | }).call(this);
129 |
--------------------------------------------------------------------------------
/underscore.util.existential.js:
--------------------------------------------------------------------------------
1 | // Underscore-contrib (underscore.util.existential.js 0.3.0)
2 | // (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
3 | // Underscore-contrib may be freely distributed under the MIT license.
4 |
5 | // Establish the root object, `window` in the browser, or `require` it on the server.
6 | if (typeof exports === 'object') {
7 | _ = module.exports = require('underscore');
8 | }
9 |
10 | // Mixing in the truthiness
11 | // ------------------------
12 |
13 | _.mixin({
14 | exists: function(x) { return x != null; },
15 | truthy: function(x) { return (x !== false) && _.exists(x); },
16 | falsey: function(x) { return !_.truthy(x); },
17 | not: function(b) { return !b; },
18 | firstExisting: function() {
19 | for (var i = 0; i < arguments.length; i++) {
20 | if (arguments[i] != null) return arguments[i];
21 | }
22 | }
23 | });
24 |
--------------------------------------------------------------------------------
/underscore.util.operators.js:
--------------------------------------------------------------------------------
1 | // Underscore-contrib (underscore.function.arity.js 0.3.0)
2 | // (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
3 | // Underscore-contrib may be freely distributed under the MIT license.
4 |
5 | (function() {
6 |
7 | // Baseline setup
8 | // --------------
9 |
10 | // Establish the root object, `window` in the browser, or `require` it on the server.
11 | if (typeof exports === 'object') {
12 | _ = module.exports = require('underscore');
13 | }
14 |
15 | // Setup for variadic operators
16 | // ----------------------------
17 |
18 | // Turn a binary math operator into a variadic operator
19 | function variadicMath(operator) {
20 | return variaderize(function(numbersToOperateOn) {
21 | return _.reduce(numbersToOperateOn, operator);
22 | });
23 | }
24 |
25 | // Turn a binary comparator into a variadic comparator
26 | function variadicComparator(comparator) {
27 | return variaderize(function(itemsToCompare) {
28 | var result;
29 |
30 | for (var i = 0; i < itemsToCompare.length - 1; i++) {
31 | result = comparator(itemsToCompare[i], itemsToCompare[i + 1]);
32 | if (result === false) return result;
33 | }
34 |
35 | return result;
36 | });
37 | }
38 |
39 | // Converts a unary function that operates on an array into one that also works with a variable number of arguments
40 | function variaderize(func) {
41 | return function (args) {
42 | var allArgs = isArrayLike(args) ? args : arguments;
43 | return func(allArgs);
44 | };
45 | }
46 |
47 | function isArrayLike(obj) {
48 | return obj != null && typeof obj.length === "number";
49 | }
50 |
51 | // Turn a boolean-returning function into one with the opposite meaning
52 | function invert(fn) {
53 | return function() {
54 | return !fn.apply(this, arguments);
55 | };
56 | }
57 |
58 | // Basic math operators
59 | function add(x, y) {
60 | return x + y;
61 | }
62 |
63 | function sub(x, y) {
64 | return x - y;
65 | }
66 |
67 | function mul(x, y) {
68 | return x * y;
69 | }
70 |
71 | function div(x, y) {
72 | return x / y;
73 | }
74 |
75 | function mod(x, y) {
76 | return x % y;
77 | }
78 |
79 | function inc(x) {
80 | return ++x;
81 | }
82 |
83 | function dec(x) {
84 | return --x;
85 | }
86 |
87 | function neg(x) {
88 | return -x;
89 | }
90 |
91 | // Bitwise operators
92 | function bitwiseAnd(x, y) {
93 | return x & y;
94 | }
95 |
96 | function bitwiseOr(x, y) {
97 | return x | y;
98 | }
99 |
100 | function bitwiseXor(x, y) {
101 | return x ^ y;
102 | }
103 |
104 | function bitwiseLeft(x, y) {
105 | return x << y;
106 | }
107 |
108 | function bitwiseRight(x, y) {
109 | return x >> y;
110 | }
111 |
112 | function bitwiseZ(x, y) {
113 | return x >>> y;
114 | }
115 |
116 | function bitwiseNot(x) {
117 | return ~x;
118 | }
119 |
120 | // Basic comparators
121 | function eq(x, y) {
122 | return x == y;
123 | }
124 |
125 | function seq(x, y) {
126 | return x === y;
127 | }
128 |
129 | // Not
130 | function not(x) {
131 | return !x;
132 | }
133 |
134 | // Relative comparators
135 | function gt(x, y) {
136 | return x > y;
137 | }
138 |
139 | function lt(x, y) {
140 | return x < y;
141 | }
142 |
143 | function gte(x, y) {
144 | return x >= y;
145 | }
146 |
147 | function lte(x, y) {
148 | return x <= y;
149 | }
150 |
151 | // Mixing in the operator functions
152 | // -----------------------------
153 |
154 | _.mixin({
155 | add: variadicMath(add),
156 | sub: variadicMath(sub),
157 | mul: variadicMath(mul),
158 | div: variadicMath(div),
159 | mod: mod,
160 | inc: inc,
161 | dec: dec,
162 | neg: neg,
163 | eq: variadicComparator(eq),
164 | seq: variadicComparator(seq),
165 | neq: invert(variadicComparator(eq)),
166 | sneq: invert(variadicComparator(seq)),
167 | not: not,
168 | gt: variadicComparator(gt),
169 | lt: variadicComparator(lt),
170 | gte: variadicComparator(gte),
171 | lte: variadicComparator(lte),
172 | bitwiseAnd: variadicMath(bitwiseAnd),
173 | bitwiseOr: variadicMath(bitwiseOr),
174 | bitwiseXor: variadicMath(bitwiseXor),
175 | bitwiseNot: bitwiseNot,
176 | bitwiseLeft: variadicMath(bitwiseLeft),
177 | bitwiseRight: variadicMath(bitwiseRight),
178 | bitwiseZ: variadicMath(bitwiseZ)
179 | });
180 | }).call(this);
181 |
--------------------------------------------------------------------------------
/underscore.util.strings.js:
--------------------------------------------------------------------------------
1 | // Underscore-contrib (underscore.util.strings.js 0.3.0)
2 | // (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
3 | // Underscore-contrib may be freely distributed under the MIT license.
4 |
5 | (function() {
6 |
7 | // Baseline setup
8 | // --------------
9 |
10 | // Establish the root object, `window` in the browser, or `require` it on the server.
11 | if (typeof exports === 'object') {
12 | _ = module.exports = require('underscore');
13 | }
14 |
15 | // Helpers
16 | // -------
17 |
18 | // No reason to create regex more than once
19 | var plusRegex = /\+/g;
20 | var bracketRegex = /(?:([^\[]+))|(?:\[(.*?)\])/g;
21 |
22 | var urlDecode = function(s) {
23 | return decodeURIComponent(s.replace(plusRegex, '%20'));
24 | };
25 | var urlEncode = function(s) {
26 | return encodeURIComponent(s);
27 | };
28 |
29 | var buildParams = function(prefix, val, top) {
30 | if (_.isUndefined(top)) top = true;
31 | if (_.isArray(val)) {
32 | return _.compact(_.map(val, function(value, key) {
33 | return buildParams(top ? key : prefix + '[]', value, false);
34 | })).join('&');
35 | } else if (_.isObject(val)) {
36 | return _.compact(_.map(val, function(value, key) {
37 | return buildParams(top ? key : prefix + '[' + key + ']', value, false);
38 | })).join('&');
39 | } else {
40 | return urlEncode(prefix) + '=' + urlEncode(val);
41 | }
42 | };
43 |
44 | // Mixing in the string utils
45 | // ----------------------------
46 |
47 | _.mixin({
48 | // Explodes a string into an array of chars
49 | explode: function(s) {
50 | return s.split('');
51 | },
52 |
53 | // Parses a query string into a hash
54 | fromQuery: function(str) {
55 | var parameters = str.split('&'),
56 | obj = {},
57 | parameter,
58 | key,
59 | match,
60 | lastKey,
61 | subKey,
62 | depth;
63 |
64 | // Iterate over key/value pairs
65 | _.each(parameters, function(parameter) {
66 | parameter = parameter.split('=');
67 | key = urlDecode(parameter[0]);
68 | lastKey = key;
69 | depth = obj;
70 |
71 | // Reset so we don't have issues when matching the same string
72 | bracketRegex.lastIndex = 0;
73 |
74 | // Attempt to extract nested values
75 | while ((match = bracketRegex.exec(key)) !== null) {
76 | if (!_.isUndefined(match[1])) {
77 |
78 | // If we're at the top nested level, no new object needed
79 | subKey = match[1];
80 |
81 | } else {
82 |
83 | // If we're at a lower nested level, we need to step down, and make
84 | // sure that there is an object to place the value into
85 | subKey = match[2];
86 | depth[lastKey] = depth[lastKey] || (subKey ? {} : []);
87 | depth = depth[lastKey];
88 | }
89 |
90 | // Save the correct key as a hash or an array
91 | lastKey = subKey || _.size(depth);
92 | }
93 |
94 | // Assign value to nested object
95 | depth[lastKey] = urlDecode(parameter[1]);
96 | });
97 |
98 | return obj;
99 | },
100 |
101 | // Implodes and array of chars into a string
102 | implode: function(a) {
103 | return a.join('');
104 | },
105 |
106 | // Converts a string to camel case
107 | camelCase : function( string ){
108 | return string.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
109 | },
110 |
111 | // Converts camel case to dashed (opposite of _.camelCase)
112 | toDash : function( string ){
113 | string = string.replace(/([A-Z])/g, function($1){return "-"+$1.toLowerCase();});
114 | // remove first dash
115 | return ( string.charAt( 0 ) == '-' ) ? string.substr(1) : string;
116 | },
117 |
118 | // Creates a query string from a hash
119 | toQuery: function(obj) {
120 | return buildParams('', obj);
121 | },
122 |
123 | // Reports whether a string contains a search string.
124 | strContains: function(str, search) {
125 | if (typeof str != 'string') throw new TypeError;
126 | return (str.indexOf(search) != -1);
127 | }
128 |
129 | });
130 | }).call(this);
131 |
--------------------------------------------------------------------------------
/underscore.util.trampolines.js:
--------------------------------------------------------------------------------
1 | // Underscore-contrib (underscore.util.trampolines.js 0.3.0)
2 | // (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
3 | // Underscore-contrib may be freely distributed under the MIT license.
4 |
5 | // Establish the root object, `window` in the browser, or `require` it on the server.
6 | if (typeof exports === 'object') {
7 | _ = module.exports = require('underscore');
8 | }
9 |
10 | // Mixing in the truthiness
11 | // ------------------------
12 |
13 | _.mixin({
14 | done: function(value) {
15 | var ret = _(value);
16 | ret.stopTrampoline = true;
17 | return ret;
18 | },
19 |
20 | trampoline: function(fun /*, args */) {
21 | var result = fun.apply(fun, _.rest(arguments));
22 |
23 | while (_.isFunction(result)) {
24 | result = result();
25 | if ((result instanceof _) && (result.stopTrampoline)) break;
26 | }
27 |
28 | return result.value();
29 | }
30 | });
31 |
--------------------------------------------------------------------------------