├── .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