├── .editorconfig
├── .gitignore
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── images
├── forcebalance-off.gif
├── forcebalance-on.gif
└── zwei-top-level-expression-hack.png
├── keymaps
└── atom-parinfer.json
├── lib
└── atom-parinfer.js
├── menus
└── atom-parinfer.json
├── node_modules
├── .bin
│ └── rimraf
├── .yarn-integrity
├── async
│ ├── LICENSE
│ ├── README.md
│ ├── lib
│ │ └── async.js
│ └── package.json
├── fs-plus
│ ├── LICENSE.md
│ ├── README.md
│ ├── lib
│ │ └── fs-plus.js
│ ├── node_modules
│ │ └── .bin
│ │ │ └── rimraf
│ └── package.json
├── mkdirp
│ ├── LICENSE
│ ├── index.js
│ ├── package.json
│ └── readme.markdown
├── rimraf
│ ├── LICENSE
│ ├── README.md
│ ├── bin.js
│ ├── package.json
│ └── rimraf.js
├── underscore-plus
│ ├── LICENSE.md
│ ├── README.md
│ ├── lib
│ │ └── underscore-plus.js
│ └── package.json
└── underscore
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── underscore-min.js
│ └── underscore.js
├── package.json
├── project.clj
├── src-cljs
└── atom_parinfer
│ ├── core.cljs
│ └── util.cljs
├── styles
└── atom-parinfer.less
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs.
2 | # More information at http://EditorConfig.org
3 |
4 | # Do not check for any .editorconfig files above this directory
5 | root = true
6 |
7 | # All files
8 | [*]
9 | charset = utf-8
10 | end_of_line = lf
11 | indent_size = 2
12 | indent_style = space
13 | insert_final_newline = true
14 | trim_trailing_whitespace = true
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .DS_Store
3 | npm-debug.log
4 |
5 | # ignore CLJS compiler target folder
6 | target
7 |
8 | # ignore diff files
9 | *.diff
10 |
11 | # I don't want to accidentally commit my test files
12 | example1.cljs
13 | example2.cljs
14 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 1.24.1 - 2020-09-07
2 | * Fix depreciation warning - [Issue #109]
3 |
4 | ## 1.24.0 - 2020-09-07
5 | * Add configurable comment character. Thank you [@swlkr] for [PR #106]!
6 | * Update to parinfer.js v3.13.0 (@oakmac fork)
7 | * Change default for Smart Mode to "on". Remove "experimental" language.
8 | * Bump project dependencies to latest
9 | * Add Fennel (`.fnl`) and Janet (`.janet`) to default file extensions
10 |
11 | [@swlkr]:https://github.com/swlkr
12 | [PR #106]:https://github.com/oakmac/atom-parinfer/pull/106
13 |
14 | ## 1.23.0 - 2018-07-03
15 | * Remove `TextBuffer.setTextInRange` depreciation warning. [Issue #101]
16 | * Bump ClojureScript version to 1.10.339
17 |
18 | ## 1.22.0 - 2018-02-04
19 | * Update to parinfer.js v3.12.0 [PR #99]
20 |
21 | ## 1.21.0 - 2018-01-28
22 | * Fix for Atom v1.23.3 [PR #97]
23 |
24 | ## 1.20.0 - 2017-08-08
25 | * Do not require restart when switching between Indent and Smart mode.
26 |
27 | ## 1.19.1-3 - 2017-08-08
28 | * Remove `TextEditor.editorElement` depreciation warning. [Issue #92]
29 |
30 | ## 1.19.0 - 2017-08-07
31 | * Upgrade to parinfer.js v3.10.0
32 | * Use Atom Config instead of files in the user's home directory.
33 | * Add support for `smartMode`, `tabStops`, and error markers (thanks [@shaunlebron]!)
34 |
35 | [@shaunlebron]:https://github.com/shaunlebron
36 |
37 | ## 1.18.0 - 2017-06-03
38 | * Use Google Closure Advanced Mode optimizations. Significantly reduce plugin file size.
39 |
40 | ## 1.17.0 - 2017-01-12
41 | * Update deprecated CSS selectors. [Issue #73] and [Issue #74]
42 |
43 | ## 1.16.0 - 2016-08-14
44 | * Bump to parinfer.js v1.8.1
45 | * Only dim inferred parens in Indent Mode. [Issue #65]
46 | * Add `previewCursorScope` option (default is off). [Issue #67]
47 |
48 | ## 1.15.0 - 2016-06-12
49 | * Add CSS for dimming trailing parens. [PR #59]
50 | * Fix issue with loading file extensions on startup. [Issue #60]
51 |
52 | ## 1.14.0 - 2016-02-21
53 | * Correctly apply the cursor result from Parinfer.
54 |
55 | ## 1.13.0 - 2016-02-21
56 | * Fix issue with multi-cursor editing. [Issue #49]
57 |
58 | ## 1.12.1 - 2016-02-21
59 | * Upgrade to ClojureScript 1.7.228
60 | * Replace `rodnaph/lowline` with Google Closure functions
61 |
62 | ## 1.12.0 - 2016-02-21
63 | * Upgrade to Parinfer v1.6.1
64 | * Increase debounce interval to 20ms
65 |
66 | ## 1.11.0 - 2016-02-12
67 | * Change key bindings to use Cmd on Mac
68 |
69 | ## 1.10.0 - 2016-02-04
70 | * Upgrade to Parinfer v1.5.3
71 | * Add `.el` (Emacs Lisp) as a default file extension
72 | * Remove the LRU cache
73 | * Fix a bug with the parent expression hack. [Issue #35]
74 |
75 | ## 1.9.0 - 2016-02-02
76 | * Upgrade to Parinfer v1.5.2
77 |
78 | ## 1.8.0 - 2016-01-25
79 | * Upgrade to Parinfer v1.5.1
80 |
81 | ## 1.7.0 - 2016-01-11
82 | * Add new `split-lines` function. [PR #43]
83 |
84 | ## 1.6.0 - 2016-01-11
85 | * Fix index out of range vector error. [Issue #42]
86 |
87 | ## 1.5.0 - 2016-01-09
88 | * Fix a bug with CRLF lines. [Issue #37]
89 |
90 | ## 1.4.0 - 2016-01-08
91 | * Allow user to skip the open file dialogs via config flag. [Issue #34]
92 |
93 | ## 1.3.0 - 2016-01-06
94 | * Upgrade to Parinfer v1.4.0
95 |
96 | ## 1.0.0 - 2015-12-23
97 | * Upgrade to the new JavaScript version of Parinfer. Everything should be much faster now.
98 |
99 | ## 0.10.0 - 2015-12-19
100 | * Prompt the user when first opening a file if Paren Mode didn't succeed or
101 | needs to make changes to the file. Issues [#18] and [#24].
102 |
103 | ## 0.6.0 - 2015-11-13
104 | * Re-write of atom-parinfer in ClojureScript
105 |
106 | ## 0.3.0 - 2015-11-08
107 | * Correctly watch the cursor for changes
108 | * Cleaner event triggering
109 |
110 | ## 0.2.0 - 2015-11-08
111 | * Initial release!
112 | * Parinfer now usable in a major text editor. One small step for parenthesis,
113 | one giant leap for Lisp ;)
114 |
115 | [#18]:https://github.com/oakmac/atom-parinfer/issues/18
116 | [#24]:https://github.com/oakmac/atom-parinfer/issues/24
117 | [Issue #34]:https://github.com/oakmac/atom-parinfer/issues/34
118 | [Issue #37]:https://github.com/oakmac/atom-parinfer/issues/37
119 | [Issue #42]:https://github.com/oakmac/atom-parinfer/issues/42
120 | [Issue #35]:https://github.com/oakmac/atom-parinfer/issues/35
121 | [Issue #49]:https://github.com/oakmac/atom-parinfer/issues/49
122 | [Issue #60]:https://github.com/oakmac/atom-parinfer/issues/60
123 | [Issue #65]:https://github.com/oakmac/atom-parinfer/issues/65
124 | [Issue #67]:https://github.com/oakmac/atom-parinfer/issues/67
125 | [Issue #73]:https://github.com/oakmac/atom-parinfer/issues/73
126 | [Issue #74]:https://github.com/oakmac/atom-parinfer/issues/74
127 | [Issue #92]:https://github.com/oakmac/atom-parinfer/issues/92
128 | [Issue #101]:https://github.com/oakmac/atom-parinfer/issues/101
129 | [Issue #109]:https://github.com/oakmac/atom-parinfer/issues/109
130 | [PR #43]:https://github.com/oakmac/atom-parinfer/pull/43
131 | [PR #59]:https://github.com/oakmac/atom-parinfer/pull/59
132 | [PR #97]:https://github.com/oakmac/atom-parinfer/pull/97
133 | [PR #99]:https://github.com/oakmac/atom-parinfer/pull/99
134 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | ISC License
2 |
3 | Copyright (c) 2015, Chris Oakman
4 |
5 | Permission to use, copy, modify, and/or distribute this software for any purpose
6 | with or without fee is hereby granted, provided that the above copyright notice
7 | and this permission notice appear in all copies.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
11 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
13 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
14 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
15 | THIS SOFTWARE.
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Parinfer for Atom
2 |
3 | A [Parinfer] package for [Atom].
4 |
5 | [Parinfer]:http://shaunlebron.github.io/parinfer/
6 | [Atom]:https://atom.io/
7 |
8 | ## What is Parinfer?
9 |
10 | Parinfer is a text editing mode that can infer Lisp code structure from
11 | indentation (and vice versa). A detailed explanation of Parinfer can be found
12 | [here].
13 |
14 | Put simply: the goal of Parinfer is to make it so you never have to think about
15 | "balancing your parens" when writing or editing Lisp code. Just indent your code
16 | as normal and Parinfer will infer the intended paren structure.
17 |
18 | [here]:http://shaunlebron.github.io/parinfer/
19 |
20 | ## Installation
21 |
22 | 1. Install [Atom]
23 | 1. In Atom, pull up the Settings tab by pressing Ctrl+,
24 | (Cmd+, on Mac) or using the main menu Edit -->
25 | Preferences
26 | 1. Click on the Install tab
27 | 1. Search for "parinfer" and find this package
28 | 1. Press the Install button :)
29 |
30 | ## Usage
31 |
32 | ### File Extensions
33 |
34 | Once the package has been installed, it will automatically load in the
35 | background when you open Atom and watch for file extensions of popular Lisp
36 | languages. The file extensions are [listed here] and can be changed in the
37 | Settings.
38 |
39 | [listed here]:https://github.com/oakmac/atom-parinfer/blob/b167244ea38b35c2d9a00dba76833f06a9bf7367/src-cljs/atom_parinfer/core.cljs#L30-L42
40 |
41 | ### Opening a File
42 |
43 | When a file with a recognized extension is first opened, Parinfer runs [Paren
44 | Mode] on the entire file and one of three things will happen (in order of
45 | likelihood):
46 |
47 | * **The file was unchanged.** You will be automatically dropped into [Indent
48 | Mode]. This is the most common scenario once you start using Parinfer
49 | regularly.
50 | * **Paren Mode changed the file.** You will be prompted to apply the changes
51 | (recommended) and then dropped into Indent Mode. This is common the first time
52 | you use Parinfer on a file.
53 | * **Paren Mode failed.** This scenario is uncommon and almost certainly means
54 | you have unbalanced parens in your file (ie: it will not compile). A prompt
55 | will show and you will be dropped into Paren Mode in order to fix the syntax
56 | problem.
57 |
58 | Running Paren Mode is a necessary first step before Indent Mode can be safely
59 | turned on. See [Fixing existing files] for more information.
60 |
61 | If you do not want to be prompted when opening a new file, the prompts can be
62 | disabled in the Settings.
63 |
64 | Please be aware that - depending on the indentation and formatting in your Lisp
65 | files - this initial processing may result in a large diff the first time it
66 | happens. Once you start using Indent Mode regularly, this initial processing is
67 | unlikely to result in a large diff (or any diff at all). You may even discover
68 | that applying Paren Mode to a file can result in [catching very hard-to-find
69 | bugs] in your existing code! As usual, developers are responsible for reviewing
70 | their diffs before a code commit :)
71 |
72 | If you want to convert a project over to Parinfer-compatible indentation, please
73 | check out the [Parlinter] project.
74 |
75 | [Paren Mode]:http://shaunlebron.github.io/parinfer/#paren-mode
76 | [Indent Mode]:http://shaunlebron.github.io/parinfer/#indent-mode
77 | [Fixing existing files]:http://shaunlebron.github.io/parinfer/#fixing-existing-files
78 | [catching very hard-to-find bugs]:https://github.com/oakmac/atom-parinfer/commit/d4b49ec2636fd0530f3f2fbca9924db6c97d3a8f
79 | [Parlinter]:https://github.com/shaunlebron/parlinter
80 |
81 | ### Hotkeys and Status Bar
82 |
83 | | Command | Windows/Linux | Mac |
84 | |-----------------------|----------------------------------------------:|----------------------------------------------|
85 | | Turn on / Toggle Mode | Ctrl+Shift+( | Cmd+Shift+( |
86 | | Turn off | Ctrl+Shift+) | Cmd+Shift+) |
87 |
88 | The status bar will indicate which mode you are in or show nothing if Parinfer
89 | is turned off.
90 |
91 | If you are in Paren Mode and Parinfer detects unbalanced parens (ie: code that
92 | will not compile), the status bar text will be red. Note that this will never
93 | happen in Indent Mode because Parinfer ensures that parens are always balanced.
94 | Also note that there is a [known bug] with this feature due to the "parent
95 | expression" hack explained below.
96 |
97 | [known bug]:https://github.com/oakmac/atom-parinfer/issues/32
98 |
99 | ## Known Limitations
100 |
101 | This extension uses a hack for performance reasons that may act oddly in certain
102 | circumstances. It assumes that an open paren followed by an alpha character -
103 | ie: regex `^\([a-zA-Z]` - at the start of a line is the beginning of a new
104 | "parent expression" and tells the Parinfer algorithm to start analyzing from
105 | there until the next line that matches the same regex. Most of the time this is
106 | probably a correct assumption, but might break inside multi-line strings or
107 | other non-standard circumstances. This is tracked at [Issue #9]; please add to
108 | that if you experience problems.
109 |
110 | Interestingly, [Shaun] discovered that this hack is not new. Someone else used
111 | the same approach [36 years ago] :)
112 |
113 | [Issue #9]:https://github.com/oakmac/atom-parinfer/issues/9
114 | [Shaun]:https://github.com/shaunlebron/
115 | [36 years ago]:images/zwei-top-level-expression-hack.png
116 |
117 | ## Plugin Development Setup
118 |
119 | Install [Leiningen].
120 |
121 | ```sh
122 | # clone this repo to your homedir
123 | cd ~
124 | git clone https://github.com/oakmac/atom-parinfer.git
125 |
126 | # symlink the repo to the Atom packages folder
127 | ln -s ~/atom-parinfer ~/.atom/packages/parinfer
128 |
129 | # compile CLJS files
130 | lein cljsbuild auto
131 | ```
132 |
133 | Then run Atom on a Lisp file. Some development notes:
134 |
135 | - `View > Developer > Reload Window` (to reload plugin changes)
136 | - `View > Developer > Toggle Developer Tools` (to see console)
137 |
138 | [Leiningen]:http://leiningen.org
139 |
140 | ## License
141 |
142 | [ISC License](LICENSE.md)
143 |
--------------------------------------------------------------------------------
/images/forcebalance-off.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oakmac/atom-parinfer/3e12b9722466e163a0b536ec71c7c3e4a34a0e53/images/forcebalance-off.gif
--------------------------------------------------------------------------------
/images/forcebalance-on.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oakmac/atom-parinfer/3e12b9722466e163a0b536ec71c7c3e4a34a0e53/images/forcebalance-on.gif
--------------------------------------------------------------------------------
/images/zwei-top-level-expression-hack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oakmac/atom-parinfer/3e12b9722466e163a0b536ec71c7c3e4a34a0e53/images/zwei-top-level-expression-hack.png
--------------------------------------------------------------------------------
/keymaps/atom-parinfer.json:
--------------------------------------------------------------------------------
1 | {
2 | ".platform-linux atom-workspace": {
3 | "ctrl-(": "parinfer:toggle-mode",
4 | "ctrl-)": "parinfer:disable"
5 | },
6 | ".platform-win32 atom-workspace": {
7 | "ctrl-(": "parinfer:toggle-mode",
8 | "ctrl-)": "parinfer:disable"
9 | },
10 | ".platform-darwin atom-workspace": {
11 | "cmd-(": "parinfer:toggle-mode",
12 | "cmd-)": "parinfer:disable"
13 | },
14 |
15 | "atom-text-editor:not([mini])": {
16 | "tab": "parinfer:next-tab-stop",
17 | "shift-tab": "parinfer:prev-tab-stop"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/menus/atom-parinfer.json:
--------------------------------------------------------------------------------
1 | {
2 | "menu": [
3 | {
4 | "label": "Packages",
5 | "submenu": [
6 | {
7 | "label": "Parinfer",
8 | "submenu": [
9 | {
10 | "label": "Toggle Mode",
11 | "command": "parinfer:toggle-mode"
12 | },
13 | {
14 | "label": "Disable",
15 | "command": "parinfer:disable"
16 | }
17 | ]
18 | }
19 | ]
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/node_modules/.bin/rimraf:
--------------------------------------------------------------------------------
1 | ../rimraf/bin.js
--------------------------------------------------------------------------------
/node_modules/.yarn-integrity:
--------------------------------------------------------------------------------
1 | {
2 | "systemParams": "linux-x64-72",
3 | "modulesFolders": [
4 | "node_modules"
5 | ],
6 | "flags": [],
7 | "linkedModules": [],
8 | "topLevelPatterns": [
9 | "@chrisoakman/parinfer@3.13.0",
10 | "fs-plus@3.1.1"
11 | ],
12 | "lockfileEntries": {
13 | "@chrisoakman/parinfer@3.13.0": "https://registry.yarnpkg.com/@chrisoakman/parinfer/-/parinfer-3.13.0.tgz#f62d9e5165b2bbbea683f3241aa27b8f3b1de88d",
14 | "async@^1.5.2": "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a",
15 | "balanced-match@^1.0.0": "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767",
16 | "brace-expansion@^1.1.7": "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd",
17 | "concat-map@0.0.1": "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b",
18 | "fs-plus@3.1.1": "https://registry.yarnpkg.com/fs-plus/-/fs-plus-3.1.1.tgz#02c085ba0a013084cff2f3e89b17c60c1d9b4ab5",
19 | "fs.realpath@^1.0.0": "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f",
20 | "glob@^7.1.3": "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6",
21 | "inflight@^1.0.4": "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9",
22 | "inherits@2": "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c",
23 | "minimatch@^3.0.4": "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083",
24 | "minimist@^1.2.5": "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602",
25 | "mkdirp@^0.5.1": "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def",
26 | "once@^1.3.0": "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1",
27 | "path-is-absolute@^1.0.0": "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f",
28 | "rimraf@^2.5.2": "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec",
29 | "underscore-plus@1.x": "https://registry.yarnpkg.com/underscore-plus/-/underscore-plus-1.7.0.tgz#107f1900c520ac1fefe4edec6580a7ff08a99d0f",
30 | "underscore@^1.9.1": "https://registry.yarnpkg.com/underscore/-/underscore-1.11.0.tgz#dd7c23a195db34267186044649870ff1bab5929e",
31 | "wrappy@1": "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
32 | },
33 | "files": [],
34 | "artifacts": {}
35 | }
--------------------------------------------------------------------------------
/node_modules/async/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2010-2014 Caolan McMahon
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/node_modules/async/lib/async.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * async
3 | * https://github.com/caolan/async
4 | *
5 | * Copyright 2010-2014 Caolan McMahon
6 | * Released under the MIT license
7 | */
8 | (function () {
9 |
10 | var async = {};
11 | function noop() {}
12 | function identity(v) {
13 | return v;
14 | }
15 | function toBool(v) {
16 | return !!v;
17 | }
18 | function notId(v) {
19 | return !v;
20 | }
21 |
22 | // global on the server, window in the browser
23 | var previous_async;
24 |
25 | // Establish the root object, `window` (`self`) in the browser, `global`
26 | // on the server, or `this` in some virtual machines. We use `self`
27 | // instead of `window` for `WebWorker` support.
28 | var root = typeof self === 'object' && self.self === self && self ||
29 | typeof global === 'object' && global.global === global && global ||
30 | this;
31 |
32 | if (root != null) {
33 | previous_async = root.async;
34 | }
35 |
36 | async.noConflict = function () {
37 | root.async = previous_async;
38 | return async;
39 | };
40 |
41 | function only_once(fn) {
42 | return function() {
43 | if (fn === null) throw new Error("Callback was already called.");
44 | fn.apply(this, arguments);
45 | fn = null;
46 | };
47 | }
48 |
49 | function _once(fn) {
50 | return function() {
51 | if (fn === null) return;
52 | fn.apply(this, arguments);
53 | fn = null;
54 | };
55 | }
56 |
57 | //// cross-browser compatiblity functions ////
58 |
59 | var _toString = Object.prototype.toString;
60 |
61 | var _isArray = Array.isArray || function (obj) {
62 | return _toString.call(obj) === '[object Array]';
63 | };
64 |
65 | // Ported from underscore.js isObject
66 | var _isObject = function(obj) {
67 | var type = typeof obj;
68 | return type === 'function' || type === 'object' && !!obj;
69 | };
70 |
71 | function _isArrayLike(arr) {
72 | return _isArray(arr) || (
73 | // has a positive integer length property
74 | typeof arr.length === "number" &&
75 | arr.length >= 0 &&
76 | arr.length % 1 === 0
77 | );
78 | }
79 |
80 | function _arrayEach(arr, iterator) {
81 | var index = -1,
82 | length = arr.length;
83 |
84 | while (++index < length) {
85 | iterator(arr[index], index, arr);
86 | }
87 | }
88 |
89 | function _map(arr, iterator) {
90 | var index = -1,
91 | length = arr.length,
92 | result = Array(length);
93 |
94 | while (++index < length) {
95 | result[index] = iterator(arr[index], index, arr);
96 | }
97 | return result;
98 | }
99 |
100 | function _range(count) {
101 | return _map(Array(count), function (v, i) { return i; });
102 | }
103 |
104 | function _reduce(arr, iterator, memo) {
105 | _arrayEach(arr, function (x, i, a) {
106 | memo = iterator(memo, x, i, a);
107 | });
108 | return memo;
109 | }
110 |
111 | function _forEachOf(object, iterator) {
112 | _arrayEach(_keys(object), function (key) {
113 | iterator(object[key], key);
114 | });
115 | }
116 |
117 | function _indexOf(arr, item) {
118 | for (var i = 0; i < arr.length; i++) {
119 | if (arr[i] === item) return i;
120 | }
121 | return -1;
122 | }
123 |
124 | var _keys = Object.keys || function (obj) {
125 | var keys = [];
126 | for (var k in obj) {
127 | if (obj.hasOwnProperty(k)) {
128 | keys.push(k);
129 | }
130 | }
131 | return keys;
132 | };
133 |
134 | function _keyIterator(coll) {
135 | var i = -1;
136 | var len;
137 | var keys;
138 | if (_isArrayLike(coll)) {
139 | len = coll.length;
140 | return function next() {
141 | i++;
142 | return i < len ? i : null;
143 | };
144 | } else {
145 | keys = _keys(coll);
146 | len = keys.length;
147 | return function next() {
148 | i++;
149 | return i < len ? keys[i] : null;
150 | };
151 | }
152 | }
153 |
154 | // Similar to ES6's rest param (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html)
155 | // This accumulates the arguments passed into an array, after a given index.
156 | // From underscore.js (https://github.com/jashkenas/underscore/pull/2140).
157 | function _restParam(func, startIndex) {
158 | startIndex = startIndex == null ? func.length - 1 : +startIndex;
159 | return function() {
160 | var length = Math.max(arguments.length - startIndex, 0);
161 | var rest = Array(length);
162 | for (var index = 0; index < length; index++) {
163 | rest[index] = arguments[index + startIndex];
164 | }
165 | switch (startIndex) {
166 | case 0: return func.call(this, rest);
167 | case 1: return func.call(this, arguments[0], rest);
168 | }
169 | // Currently unused but handle cases outside of the switch statement:
170 | // var args = Array(startIndex + 1);
171 | // for (index = 0; index < startIndex; index++) {
172 | // args[index] = arguments[index];
173 | // }
174 | // args[startIndex] = rest;
175 | // return func.apply(this, args);
176 | };
177 | }
178 |
179 | function _withoutIndex(iterator) {
180 | return function (value, index, callback) {
181 | return iterator(value, callback);
182 | };
183 | }
184 |
185 | //// exported async module functions ////
186 |
187 | //// nextTick implementation with browser-compatible fallback ////
188 |
189 | // capture the global reference to guard against fakeTimer mocks
190 | var _setImmediate = typeof setImmediate === 'function' && setImmediate;
191 |
192 | var _delay = _setImmediate ? function(fn) {
193 | // not a direct alias for IE10 compatibility
194 | _setImmediate(fn);
195 | } : function(fn) {
196 | setTimeout(fn, 0);
197 | };
198 |
199 | if (typeof process === 'object' && typeof process.nextTick === 'function') {
200 | async.nextTick = process.nextTick;
201 | } else {
202 | async.nextTick = _delay;
203 | }
204 | async.setImmediate = _setImmediate ? _delay : async.nextTick;
205 |
206 |
207 | async.forEach =
208 | async.each = function (arr, iterator, callback) {
209 | return async.eachOf(arr, _withoutIndex(iterator), callback);
210 | };
211 |
212 | async.forEachSeries =
213 | async.eachSeries = function (arr, iterator, callback) {
214 | return async.eachOfSeries(arr, _withoutIndex(iterator), callback);
215 | };
216 |
217 |
218 | async.forEachLimit =
219 | async.eachLimit = function (arr, limit, iterator, callback) {
220 | return _eachOfLimit(limit)(arr, _withoutIndex(iterator), callback);
221 | };
222 |
223 | async.forEachOf =
224 | async.eachOf = function (object, iterator, callback) {
225 | callback = _once(callback || noop);
226 | object = object || [];
227 |
228 | var iter = _keyIterator(object);
229 | var key, completed = 0;
230 |
231 | while ((key = iter()) != null) {
232 | completed += 1;
233 | iterator(object[key], key, only_once(done));
234 | }
235 |
236 | if (completed === 0) callback(null);
237 |
238 | function done(err) {
239 | completed--;
240 | if (err) {
241 | callback(err);
242 | }
243 | // Check key is null in case iterator isn't exhausted
244 | // and done resolved synchronously.
245 | else if (key === null && completed <= 0) {
246 | callback(null);
247 | }
248 | }
249 | };
250 |
251 | async.forEachOfSeries =
252 | async.eachOfSeries = function (obj, iterator, callback) {
253 | callback = _once(callback || noop);
254 | obj = obj || [];
255 | var nextKey = _keyIterator(obj);
256 | var key = nextKey();
257 | function iterate() {
258 | var sync = true;
259 | if (key === null) {
260 | return callback(null);
261 | }
262 | iterator(obj[key], key, only_once(function (err) {
263 | if (err) {
264 | callback(err);
265 | }
266 | else {
267 | key = nextKey();
268 | if (key === null) {
269 | return callback(null);
270 | } else {
271 | if (sync) {
272 | async.setImmediate(iterate);
273 | } else {
274 | iterate();
275 | }
276 | }
277 | }
278 | }));
279 | sync = false;
280 | }
281 | iterate();
282 | };
283 |
284 |
285 |
286 | async.forEachOfLimit =
287 | async.eachOfLimit = function (obj, limit, iterator, callback) {
288 | _eachOfLimit(limit)(obj, iterator, callback);
289 | };
290 |
291 | function _eachOfLimit(limit) {
292 |
293 | return function (obj, iterator, callback) {
294 | callback = _once(callback || noop);
295 | obj = obj || [];
296 | var nextKey = _keyIterator(obj);
297 | if (limit <= 0) {
298 | return callback(null);
299 | }
300 | var done = false;
301 | var running = 0;
302 | var errored = false;
303 |
304 | (function replenish () {
305 | if (done && running <= 0) {
306 | return callback(null);
307 | }
308 |
309 | while (running < limit && !errored) {
310 | var key = nextKey();
311 | if (key === null) {
312 | done = true;
313 | if (running <= 0) {
314 | callback(null);
315 | }
316 | return;
317 | }
318 | running += 1;
319 | iterator(obj[key], key, only_once(function (err) {
320 | running -= 1;
321 | if (err) {
322 | callback(err);
323 | errored = true;
324 | }
325 | else {
326 | replenish();
327 | }
328 | }));
329 | }
330 | })();
331 | };
332 | }
333 |
334 |
335 | function doParallel(fn) {
336 | return function (obj, iterator, callback) {
337 | return fn(async.eachOf, obj, iterator, callback);
338 | };
339 | }
340 | function doParallelLimit(fn) {
341 | return function (obj, limit, iterator, callback) {
342 | return fn(_eachOfLimit(limit), obj, iterator, callback);
343 | };
344 | }
345 | function doSeries(fn) {
346 | return function (obj, iterator, callback) {
347 | return fn(async.eachOfSeries, obj, iterator, callback);
348 | };
349 | }
350 |
351 | function _asyncMap(eachfn, arr, iterator, callback) {
352 | callback = _once(callback || noop);
353 | arr = arr || [];
354 | var results = _isArrayLike(arr) ? [] : {};
355 | eachfn(arr, function (value, index, callback) {
356 | iterator(value, function (err, v) {
357 | results[index] = v;
358 | callback(err);
359 | });
360 | }, function (err) {
361 | callback(err, results);
362 | });
363 | }
364 |
365 | async.map = doParallel(_asyncMap);
366 | async.mapSeries = doSeries(_asyncMap);
367 | async.mapLimit = doParallelLimit(_asyncMap);
368 |
369 | // reduce only has a series version, as doing reduce in parallel won't
370 | // work in many situations.
371 | async.inject =
372 | async.foldl =
373 | async.reduce = function (arr, memo, iterator, callback) {
374 | async.eachOfSeries(arr, function (x, i, callback) {
375 | iterator(memo, x, function (err, v) {
376 | memo = v;
377 | callback(err);
378 | });
379 | }, function (err) {
380 | callback(err, memo);
381 | });
382 | };
383 |
384 | async.foldr =
385 | async.reduceRight = function (arr, memo, iterator, callback) {
386 | var reversed = _map(arr, identity).reverse();
387 | async.reduce(reversed, memo, iterator, callback);
388 | };
389 |
390 | async.transform = function (arr, memo, iterator, callback) {
391 | if (arguments.length === 3) {
392 | callback = iterator;
393 | iterator = memo;
394 | memo = _isArray(arr) ? [] : {};
395 | }
396 |
397 | async.eachOf(arr, function(v, k, cb) {
398 | iterator(memo, v, k, cb);
399 | }, function(err) {
400 | callback(err, memo);
401 | });
402 | };
403 |
404 | function _filter(eachfn, arr, iterator, callback) {
405 | var results = [];
406 | eachfn(arr, function (x, index, callback) {
407 | iterator(x, function (v) {
408 | if (v) {
409 | results.push({index: index, value: x});
410 | }
411 | callback();
412 | });
413 | }, function () {
414 | callback(_map(results.sort(function (a, b) {
415 | return a.index - b.index;
416 | }), function (x) {
417 | return x.value;
418 | }));
419 | });
420 | }
421 |
422 | async.select =
423 | async.filter = doParallel(_filter);
424 |
425 | async.selectLimit =
426 | async.filterLimit = doParallelLimit(_filter);
427 |
428 | async.selectSeries =
429 | async.filterSeries = doSeries(_filter);
430 |
431 | function _reject(eachfn, arr, iterator, callback) {
432 | _filter(eachfn, arr, function(value, cb) {
433 | iterator(value, function(v) {
434 | cb(!v);
435 | });
436 | }, callback);
437 | }
438 | async.reject = doParallel(_reject);
439 | async.rejectLimit = doParallelLimit(_reject);
440 | async.rejectSeries = doSeries(_reject);
441 |
442 | function _createTester(eachfn, check, getResult) {
443 | return function(arr, limit, iterator, cb) {
444 | function done() {
445 | if (cb) cb(getResult(false, void 0));
446 | }
447 | function iteratee(x, _, callback) {
448 | if (!cb) return callback();
449 | iterator(x, function (v) {
450 | if (cb && check(v)) {
451 | cb(getResult(true, x));
452 | cb = iterator = false;
453 | }
454 | callback();
455 | });
456 | }
457 | if (arguments.length > 3) {
458 | eachfn(arr, limit, iteratee, done);
459 | } else {
460 | cb = iterator;
461 | iterator = limit;
462 | eachfn(arr, iteratee, done);
463 | }
464 | };
465 | }
466 |
467 | async.any =
468 | async.some = _createTester(async.eachOf, toBool, identity);
469 |
470 | async.someLimit = _createTester(async.eachOfLimit, toBool, identity);
471 |
472 | async.all =
473 | async.every = _createTester(async.eachOf, notId, notId);
474 |
475 | async.everyLimit = _createTester(async.eachOfLimit, notId, notId);
476 |
477 | function _findGetResult(v, x) {
478 | return x;
479 | }
480 | async.detect = _createTester(async.eachOf, identity, _findGetResult);
481 | async.detectSeries = _createTester(async.eachOfSeries, identity, _findGetResult);
482 | async.detectLimit = _createTester(async.eachOfLimit, identity, _findGetResult);
483 |
484 | async.sortBy = function (arr, iterator, callback) {
485 | async.map(arr, function (x, callback) {
486 | iterator(x, function (err, criteria) {
487 | if (err) {
488 | callback(err);
489 | }
490 | else {
491 | callback(null, {value: x, criteria: criteria});
492 | }
493 | });
494 | }, function (err, results) {
495 | if (err) {
496 | return callback(err);
497 | }
498 | else {
499 | callback(null, _map(results.sort(comparator), function (x) {
500 | return x.value;
501 | }));
502 | }
503 |
504 | });
505 |
506 | function comparator(left, right) {
507 | var a = left.criteria, b = right.criteria;
508 | return a < b ? -1 : a > b ? 1 : 0;
509 | }
510 | };
511 |
512 | async.auto = function (tasks, concurrency, callback) {
513 | if (typeof arguments[1] === 'function') {
514 | // concurrency is optional, shift the args.
515 | callback = concurrency;
516 | concurrency = null;
517 | }
518 | callback = _once(callback || noop);
519 | var keys = _keys(tasks);
520 | var remainingTasks = keys.length;
521 | if (!remainingTasks) {
522 | return callback(null);
523 | }
524 | if (!concurrency) {
525 | concurrency = remainingTasks;
526 | }
527 |
528 | var results = {};
529 | var runningTasks = 0;
530 |
531 | var hasError = false;
532 |
533 | var listeners = [];
534 | function addListener(fn) {
535 | listeners.unshift(fn);
536 | }
537 | function removeListener(fn) {
538 | var idx = _indexOf(listeners, fn);
539 | if (idx >= 0) listeners.splice(idx, 1);
540 | }
541 | function taskComplete() {
542 | remainingTasks--;
543 | _arrayEach(listeners.slice(0), function (fn) {
544 | fn();
545 | });
546 | }
547 |
548 | addListener(function () {
549 | if (!remainingTasks) {
550 | callback(null, results);
551 | }
552 | });
553 |
554 | _arrayEach(keys, function (k) {
555 | if (hasError) return;
556 | var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]];
557 | var taskCallback = _restParam(function(err, args) {
558 | runningTasks--;
559 | if (args.length <= 1) {
560 | args = args[0];
561 | }
562 | if (err) {
563 | var safeResults = {};
564 | _forEachOf(results, function(val, rkey) {
565 | safeResults[rkey] = val;
566 | });
567 | safeResults[k] = args;
568 | hasError = true;
569 |
570 | callback(err, safeResults);
571 | }
572 | else {
573 | results[k] = args;
574 | async.setImmediate(taskComplete);
575 | }
576 | });
577 | var requires = task.slice(0, task.length - 1);
578 | // prevent dead-locks
579 | var len = requires.length;
580 | var dep;
581 | while (len--) {
582 | if (!(dep = tasks[requires[len]])) {
583 | throw new Error('Has nonexistent dependency in ' + requires.join(', '));
584 | }
585 | if (_isArray(dep) && _indexOf(dep, k) >= 0) {
586 | throw new Error('Has cyclic dependencies');
587 | }
588 | }
589 | function ready() {
590 | return runningTasks < concurrency && _reduce(requires, function (a, x) {
591 | return (a && results.hasOwnProperty(x));
592 | }, true) && !results.hasOwnProperty(k);
593 | }
594 | if (ready()) {
595 | runningTasks++;
596 | task[task.length - 1](taskCallback, results);
597 | }
598 | else {
599 | addListener(listener);
600 | }
601 | function listener() {
602 | if (ready()) {
603 | runningTasks++;
604 | removeListener(listener);
605 | task[task.length - 1](taskCallback, results);
606 | }
607 | }
608 | });
609 | };
610 |
611 |
612 |
613 | async.retry = function(times, task, callback) {
614 | var DEFAULT_TIMES = 5;
615 | var DEFAULT_INTERVAL = 0;
616 |
617 | var attempts = [];
618 |
619 | var opts = {
620 | times: DEFAULT_TIMES,
621 | interval: DEFAULT_INTERVAL
622 | };
623 |
624 | function parseTimes(acc, t){
625 | if(typeof t === 'number'){
626 | acc.times = parseInt(t, 10) || DEFAULT_TIMES;
627 | } else if(typeof t === 'object'){
628 | acc.times = parseInt(t.times, 10) || DEFAULT_TIMES;
629 | acc.interval = parseInt(t.interval, 10) || DEFAULT_INTERVAL;
630 | } else {
631 | throw new Error('Unsupported argument type for \'times\': ' + typeof t);
632 | }
633 | }
634 |
635 | var length = arguments.length;
636 | if (length < 1 || length > 3) {
637 | throw new Error('Invalid arguments - must be either (task), (task, callback), (times, task) or (times, task, callback)');
638 | } else if (length <= 2 && typeof times === 'function') {
639 | callback = task;
640 | task = times;
641 | }
642 | if (typeof times !== 'function') {
643 | parseTimes(opts, times);
644 | }
645 | opts.callback = callback;
646 | opts.task = task;
647 |
648 | function wrappedTask(wrappedCallback, wrappedResults) {
649 | function retryAttempt(task, finalAttempt) {
650 | return function(seriesCallback) {
651 | task(function(err, result){
652 | seriesCallback(!err || finalAttempt, {err: err, result: result});
653 | }, wrappedResults);
654 | };
655 | }
656 |
657 | function retryInterval(interval){
658 | return function(seriesCallback){
659 | setTimeout(function(){
660 | seriesCallback(null);
661 | }, interval);
662 | };
663 | }
664 |
665 | while (opts.times) {
666 |
667 | var finalAttempt = !(opts.times-=1);
668 | attempts.push(retryAttempt(opts.task, finalAttempt));
669 | if(!finalAttempt && opts.interval > 0){
670 | attempts.push(retryInterval(opts.interval));
671 | }
672 | }
673 |
674 | async.series(attempts, function(done, data){
675 | data = data[data.length - 1];
676 | (wrappedCallback || opts.callback)(data.err, data.result);
677 | });
678 | }
679 |
680 | // If a callback is passed, run this as a controll flow
681 | return opts.callback ? wrappedTask() : wrappedTask;
682 | };
683 |
684 | async.waterfall = function (tasks, callback) {
685 | callback = _once(callback || noop);
686 | if (!_isArray(tasks)) {
687 | var err = new Error('First argument to waterfall must be an array of functions');
688 | return callback(err);
689 | }
690 | if (!tasks.length) {
691 | return callback();
692 | }
693 | function wrapIterator(iterator) {
694 | return _restParam(function (err, args) {
695 | if (err) {
696 | callback.apply(null, [err].concat(args));
697 | }
698 | else {
699 | var next = iterator.next();
700 | if (next) {
701 | args.push(wrapIterator(next));
702 | }
703 | else {
704 | args.push(callback);
705 | }
706 | ensureAsync(iterator).apply(null, args);
707 | }
708 | });
709 | }
710 | wrapIterator(async.iterator(tasks))();
711 | };
712 |
713 | function _parallel(eachfn, tasks, callback) {
714 | callback = callback || noop;
715 | var results = _isArrayLike(tasks) ? [] : {};
716 |
717 | eachfn(tasks, function (task, key, callback) {
718 | task(_restParam(function (err, args) {
719 | if (args.length <= 1) {
720 | args = args[0];
721 | }
722 | results[key] = args;
723 | callback(err);
724 | }));
725 | }, function (err) {
726 | callback(err, results);
727 | });
728 | }
729 |
730 | async.parallel = function (tasks, callback) {
731 | _parallel(async.eachOf, tasks, callback);
732 | };
733 |
734 | async.parallelLimit = function(tasks, limit, callback) {
735 | _parallel(_eachOfLimit(limit), tasks, callback);
736 | };
737 |
738 | async.series = function(tasks, callback) {
739 | _parallel(async.eachOfSeries, tasks, callback);
740 | };
741 |
742 | async.iterator = function (tasks) {
743 | function makeCallback(index) {
744 | function fn() {
745 | if (tasks.length) {
746 | tasks[index].apply(null, arguments);
747 | }
748 | return fn.next();
749 | }
750 | fn.next = function () {
751 | return (index < tasks.length - 1) ? makeCallback(index + 1): null;
752 | };
753 | return fn;
754 | }
755 | return makeCallback(0);
756 | };
757 |
758 | async.apply = _restParam(function (fn, args) {
759 | return _restParam(function (callArgs) {
760 | return fn.apply(
761 | null, args.concat(callArgs)
762 | );
763 | });
764 | });
765 |
766 | function _concat(eachfn, arr, fn, callback) {
767 | var result = [];
768 | eachfn(arr, function (x, index, cb) {
769 | fn(x, function (err, y) {
770 | result = result.concat(y || []);
771 | cb(err);
772 | });
773 | }, function (err) {
774 | callback(err, result);
775 | });
776 | }
777 | async.concat = doParallel(_concat);
778 | async.concatSeries = doSeries(_concat);
779 |
780 | async.whilst = function (test, iterator, callback) {
781 | callback = callback || noop;
782 | if (test()) {
783 | var next = _restParam(function(err, args) {
784 | if (err) {
785 | callback(err);
786 | } else if (test.apply(this, args)) {
787 | iterator(next);
788 | } else {
789 | callback.apply(null, [null].concat(args));
790 | }
791 | });
792 | iterator(next);
793 | } else {
794 | callback(null);
795 | }
796 | };
797 |
798 | async.doWhilst = function (iterator, test, callback) {
799 | var calls = 0;
800 | return async.whilst(function() {
801 | return ++calls <= 1 || test.apply(this, arguments);
802 | }, iterator, callback);
803 | };
804 |
805 | async.until = function (test, iterator, callback) {
806 | return async.whilst(function() {
807 | return !test.apply(this, arguments);
808 | }, iterator, callback);
809 | };
810 |
811 | async.doUntil = function (iterator, test, callback) {
812 | return async.doWhilst(iterator, function() {
813 | return !test.apply(this, arguments);
814 | }, callback);
815 | };
816 |
817 | async.during = function (test, iterator, callback) {
818 | callback = callback || noop;
819 |
820 | var next = _restParam(function(err, args) {
821 | if (err) {
822 | callback(err);
823 | } else {
824 | args.push(check);
825 | test.apply(this, args);
826 | }
827 | });
828 |
829 | var check = function(err, truth) {
830 | if (err) {
831 | callback(err);
832 | } else if (truth) {
833 | iterator(next);
834 | } else {
835 | callback(null);
836 | }
837 | };
838 |
839 | test(check);
840 | };
841 |
842 | async.doDuring = function (iterator, test, callback) {
843 | var calls = 0;
844 | async.during(function(next) {
845 | if (calls++ < 1) {
846 | next(null, true);
847 | } else {
848 | test.apply(this, arguments);
849 | }
850 | }, iterator, callback);
851 | };
852 |
853 | function _queue(worker, concurrency, payload) {
854 | if (concurrency == null) {
855 | concurrency = 1;
856 | }
857 | else if(concurrency === 0) {
858 | throw new Error('Concurrency must not be zero');
859 | }
860 | function _insert(q, data, pos, callback) {
861 | if (callback != null && typeof callback !== "function") {
862 | throw new Error("task callback must be a function");
863 | }
864 | q.started = true;
865 | if (!_isArray(data)) {
866 | data = [data];
867 | }
868 | if(data.length === 0 && q.idle()) {
869 | // call drain immediately if there are no tasks
870 | return async.setImmediate(function() {
871 | q.drain();
872 | });
873 | }
874 | _arrayEach(data, function(task) {
875 | var item = {
876 | data: task,
877 | callback: callback || noop
878 | };
879 |
880 | if (pos) {
881 | q.tasks.unshift(item);
882 | } else {
883 | q.tasks.push(item);
884 | }
885 |
886 | if (q.tasks.length === q.concurrency) {
887 | q.saturated();
888 | }
889 | });
890 | async.setImmediate(q.process);
891 | }
892 | function _next(q, tasks) {
893 | return function(){
894 | workers -= 1;
895 |
896 | var removed = false;
897 | var args = arguments;
898 | _arrayEach(tasks, function (task) {
899 | _arrayEach(workersList, function (worker, index) {
900 | if (worker === task && !removed) {
901 | workersList.splice(index, 1);
902 | removed = true;
903 | }
904 | });
905 |
906 | task.callback.apply(task, args);
907 | });
908 | if (q.tasks.length + workers === 0) {
909 | q.drain();
910 | }
911 | q.process();
912 | };
913 | }
914 |
915 | var workers = 0;
916 | var workersList = [];
917 | var q = {
918 | tasks: [],
919 | concurrency: concurrency,
920 | payload: payload,
921 | saturated: noop,
922 | empty: noop,
923 | drain: noop,
924 | started: false,
925 | paused: false,
926 | push: function (data, callback) {
927 | _insert(q, data, false, callback);
928 | },
929 | kill: function () {
930 | q.drain = noop;
931 | q.tasks = [];
932 | },
933 | unshift: function (data, callback) {
934 | _insert(q, data, true, callback);
935 | },
936 | process: function () {
937 | while(!q.paused && workers < q.concurrency && q.tasks.length){
938 |
939 | var tasks = q.payload ?
940 | q.tasks.splice(0, q.payload) :
941 | q.tasks.splice(0, q.tasks.length);
942 |
943 | var data = _map(tasks, function (task) {
944 | return task.data;
945 | });
946 |
947 | if (q.tasks.length === 0) {
948 | q.empty();
949 | }
950 | workers += 1;
951 | workersList.push(tasks[0]);
952 | var cb = only_once(_next(q, tasks));
953 | worker(data, cb);
954 | }
955 | },
956 | length: function () {
957 | return q.tasks.length;
958 | },
959 | running: function () {
960 | return workers;
961 | },
962 | workersList: function () {
963 | return workersList;
964 | },
965 | idle: function() {
966 | return q.tasks.length + workers === 0;
967 | },
968 | pause: function () {
969 | q.paused = true;
970 | },
971 | resume: function () {
972 | if (q.paused === false) { return; }
973 | q.paused = false;
974 | var resumeCount = Math.min(q.concurrency, q.tasks.length);
975 | // Need to call q.process once per concurrent
976 | // worker to preserve full concurrency after pause
977 | for (var w = 1; w <= resumeCount; w++) {
978 | async.setImmediate(q.process);
979 | }
980 | }
981 | };
982 | return q;
983 | }
984 |
985 | async.queue = function (worker, concurrency) {
986 | var q = _queue(function (items, cb) {
987 | worker(items[0], cb);
988 | }, concurrency, 1);
989 |
990 | return q;
991 | };
992 |
993 | async.priorityQueue = function (worker, concurrency) {
994 |
995 | function _compareTasks(a, b){
996 | return a.priority - b.priority;
997 | }
998 |
999 | function _binarySearch(sequence, item, compare) {
1000 | var beg = -1,
1001 | end = sequence.length - 1;
1002 | while (beg < end) {
1003 | var mid = beg + ((end - beg + 1) >>> 1);
1004 | if (compare(item, sequence[mid]) >= 0) {
1005 | beg = mid;
1006 | } else {
1007 | end = mid - 1;
1008 | }
1009 | }
1010 | return beg;
1011 | }
1012 |
1013 | function _insert(q, data, priority, callback) {
1014 | if (callback != null && typeof callback !== "function") {
1015 | throw new Error("task callback must be a function");
1016 | }
1017 | q.started = true;
1018 | if (!_isArray(data)) {
1019 | data = [data];
1020 | }
1021 | if(data.length === 0) {
1022 | // call drain immediately if there are no tasks
1023 | return async.setImmediate(function() {
1024 | q.drain();
1025 | });
1026 | }
1027 | _arrayEach(data, function(task) {
1028 | var item = {
1029 | data: task,
1030 | priority: priority,
1031 | callback: typeof callback === 'function' ? callback : noop
1032 | };
1033 |
1034 | q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item);
1035 |
1036 | if (q.tasks.length === q.concurrency) {
1037 | q.saturated();
1038 | }
1039 | async.setImmediate(q.process);
1040 | });
1041 | }
1042 |
1043 | // Start with a normal queue
1044 | var q = async.queue(worker, concurrency);
1045 |
1046 | // Override push to accept second parameter representing priority
1047 | q.push = function (data, priority, callback) {
1048 | _insert(q, data, priority, callback);
1049 | };
1050 |
1051 | // Remove unshift function
1052 | delete q.unshift;
1053 |
1054 | return q;
1055 | };
1056 |
1057 | async.cargo = function (worker, payload) {
1058 | return _queue(worker, 1, payload);
1059 | };
1060 |
1061 | function _console_fn(name) {
1062 | return _restParam(function (fn, args) {
1063 | fn.apply(null, args.concat([_restParam(function (err, args) {
1064 | if (typeof console === 'object') {
1065 | if (err) {
1066 | if (console.error) {
1067 | console.error(err);
1068 | }
1069 | }
1070 | else if (console[name]) {
1071 | _arrayEach(args, function (x) {
1072 | console[name](x);
1073 | });
1074 | }
1075 | }
1076 | })]));
1077 | });
1078 | }
1079 | async.log = _console_fn('log');
1080 | async.dir = _console_fn('dir');
1081 | /*async.info = _console_fn('info');
1082 | async.warn = _console_fn('warn');
1083 | async.error = _console_fn('error');*/
1084 |
1085 | async.memoize = function (fn, hasher) {
1086 | var memo = {};
1087 | var queues = {};
1088 | var has = Object.prototype.hasOwnProperty;
1089 | hasher = hasher || identity;
1090 | var memoized = _restParam(function memoized(args) {
1091 | var callback = args.pop();
1092 | var key = hasher.apply(null, args);
1093 | if (has.call(memo, key)) {
1094 | async.setImmediate(function () {
1095 | callback.apply(null, memo[key]);
1096 | });
1097 | }
1098 | else if (has.call(queues, key)) {
1099 | queues[key].push(callback);
1100 | }
1101 | else {
1102 | queues[key] = [callback];
1103 | fn.apply(null, args.concat([_restParam(function (args) {
1104 | memo[key] = args;
1105 | var q = queues[key];
1106 | delete queues[key];
1107 | for (var i = 0, l = q.length; i < l; i++) {
1108 | q[i].apply(null, args);
1109 | }
1110 | })]));
1111 | }
1112 | });
1113 | memoized.memo = memo;
1114 | memoized.unmemoized = fn;
1115 | return memoized;
1116 | };
1117 |
1118 | async.unmemoize = function (fn) {
1119 | return function () {
1120 | return (fn.unmemoized || fn).apply(null, arguments);
1121 | };
1122 | };
1123 |
1124 | function _times(mapper) {
1125 | return function (count, iterator, callback) {
1126 | mapper(_range(count), iterator, callback);
1127 | };
1128 | }
1129 |
1130 | async.times = _times(async.map);
1131 | async.timesSeries = _times(async.mapSeries);
1132 | async.timesLimit = function (count, limit, iterator, callback) {
1133 | return async.mapLimit(_range(count), limit, iterator, callback);
1134 | };
1135 |
1136 | async.seq = function (/* functions... */) {
1137 | var fns = arguments;
1138 | return _restParam(function (args) {
1139 | var that = this;
1140 |
1141 | var callback = args[args.length - 1];
1142 | if (typeof callback == 'function') {
1143 | args.pop();
1144 | } else {
1145 | callback = noop;
1146 | }
1147 |
1148 | async.reduce(fns, args, function (newargs, fn, cb) {
1149 | fn.apply(that, newargs.concat([_restParam(function (err, nextargs) {
1150 | cb(err, nextargs);
1151 | })]));
1152 | },
1153 | function (err, results) {
1154 | callback.apply(that, [err].concat(results));
1155 | });
1156 | });
1157 | };
1158 |
1159 | async.compose = function (/* functions... */) {
1160 | return async.seq.apply(null, Array.prototype.reverse.call(arguments));
1161 | };
1162 |
1163 |
1164 | function _applyEach(eachfn) {
1165 | return _restParam(function(fns, args) {
1166 | var go = _restParam(function(args) {
1167 | var that = this;
1168 | var callback = args.pop();
1169 | return eachfn(fns, function (fn, _, cb) {
1170 | fn.apply(that, args.concat([cb]));
1171 | },
1172 | callback);
1173 | });
1174 | if (args.length) {
1175 | return go.apply(this, args);
1176 | }
1177 | else {
1178 | return go;
1179 | }
1180 | });
1181 | }
1182 |
1183 | async.applyEach = _applyEach(async.eachOf);
1184 | async.applyEachSeries = _applyEach(async.eachOfSeries);
1185 |
1186 |
1187 | async.forever = function (fn, callback) {
1188 | var done = only_once(callback || noop);
1189 | var task = ensureAsync(fn);
1190 | function next(err) {
1191 | if (err) {
1192 | return done(err);
1193 | }
1194 | task(next);
1195 | }
1196 | next();
1197 | };
1198 |
1199 | function ensureAsync(fn) {
1200 | return _restParam(function (args) {
1201 | var callback = args.pop();
1202 | args.push(function () {
1203 | var innerArgs = arguments;
1204 | if (sync) {
1205 | async.setImmediate(function () {
1206 | callback.apply(null, innerArgs);
1207 | });
1208 | } else {
1209 | callback.apply(null, innerArgs);
1210 | }
1211 | });
1212 | var sync = true;
1213 | fn.apply(this, args);
1214 | sync = false;
1215 | });
1216 | }
1217 |
1218 | async.ensureAsync = ensureAsync;
1219 |
1220 | async.constant = _restParam(function(values) {
1221 | var args = [null].concat(values);
1222 | return function (callback) {
1223 | return callback.apply(this, args);
1224 | };
1225 | });
1226 |
1227 | async.wrapSync =
1228 | async.asyncify = function asyncify(func) {
1229 | return _restParam(function (args) {
1230 | var callback = args.pop();
1231 | var result;
1232 | try {
1233 | result = func.apply(this, args);
1234 | } catch (e) {
1235 | return callback(e);
1236 | }
1237 | // if result is Promise object
1238 | if (_isObject(result) && typeof result.then === "function") {
1239 | result.then(function(value) {
1240 | callback(null, value);
1241 | })["catch"](function(err) {
1242 | callback(err.message ? err : new Error(err));
1243 | });
1244 | } else {
1245 | callback(null, result);
1246 | }
1247 | });
1248 | };
1249 |
1250 | // Node.js
1251 | if (typeof module === 'object' && module.exports) {
1252 | module.exports = async;
1253 | }
1254 | // AMD / RequireJS
1255 | else if (typeof define === 'function' && define.amd) {
1256 | define([], function () {
1257 | return async;
1258 | });
1259 | }
1260 | // included directly via