From d3c9be58e4e97fbf59384acdabb04a33d6a7e996 Mon Sep 17 00:00:00 2001
From: Jackson Ray Hamilton
Date: Sun, 1 Feb 2015 23:42:01 -0800
Subject: [PATCH] Squashed commits
---
packages/context-coloring/.elpaignore | 13 +
packages/context-coloring/.gitignore | 2 +
packages/context-coloring/.travis.yml | 17 +
packages/context-coloring/Makefile | 31 +
packages/context-coloring/README.md | 161 +
.../benchmark/context-coloring-benchmark.el | 160 +
.../benchmark/fixtures/.dir-locals.el | 1 +
.../benchmark/fixtures/async-0.9.0.js | 1123 ++
.../benchmark/fixtures/jquery-2.1.1.js | 9190 ++++++++++++++
.../benchmark/fixtures/lodash-2.4.1.js | 6785 ++++++++++
.../benchmark/fixtures/mkdirp-0.5.0.js | 97 +
.../context-coloring/context-coloring-themes.el | 122 +
packages/context-coloring/context-coloring.el | 518 +
.../languages/javascript/.jshintrc | 24 +
.../languages/javascript/binaries/scopifier | 36 +
.../languages/javascript/libraries/escope.js | 1117 ++
.../languages/javascript/libraries/esprima.js | 3756 ++++++
.../languages/javascript/libraries/estraverse.js | 838 ++
.../languages/javascript/scopifier.js | 129 +
packages/context-coloring/libraries/.dir-locals.el | 2 +
packages/context-coloring/libraries/ert-async.el | 89 +
packages/context-coloring/libraries/js2-mode.el | 12331 +++++++++++++++++++
packages/context-coloring/scopifier.png | Bin 0 -> 2609 bytes
packages/context-coloring/screenshot.png | Bin 0 -> 22006 bytes
.../context-coloring/test/context-coloring-test.el | 375 +
.../context-coloring/test/fixtures/block-scopes.js | 6 +
packages/context-coloring/test/fixtures/catch.js | 8 +
.../test/fixtures/comments-and-strings.js | 3 +
.../test/fixtures/function-scopes.js | 5 +
packages/context-coloring/test/fixtures/global.js | 3 +
.../context-coloring/test/fixtures/key-names.js | 6 +
.../context-coloring/test/fixtures/key-values.js | 8 +
.../test/fixtures/property-lookup.js | 5 +
33 files changed, 36961 insertions(+)
create mode 100644 packages/context-coloring/.elpaignore
create mode 100644 packages/context-coloring/.gitignore
create mode 100644 packages/context-coloring/.travis.yml
create mode 100644 packages/context-coloring/Makefile
create mode 100644 packages/context-coloring/README.md
create mode 100644 packages/context-coloring/benchmark/context-coloring-benchmark.el
create mode 100644 packages/context-coloring/benchmark/fixtures/.dir-locals.el
create mode 100644 packages/context-coloring/benchmark/fixtures/async-0.9.0.js
create mode 100644 packages/context-coloring/benchmark/fixtures/jquery-2.1.1.js
create mode 100644 packages/context-coloring/benchmark/fixtures/lodash-2.4.1.js
create mode 100644 packages/context-coloring/benchmark/fixtures/mkdirp-0.5.0.js
create mode 100644 packages/context-coloring/context-coloring-themes.el
create mode 100644 packages/context-coloring/context-coloring.el
create mode 100644 packages/context-coloring/languages/javascript/.jshintrc
create mode 100755 packages/context-coloring/languages/javascript/binaries/scopifier
create mode 100644 packages/context-coloring/languages/javascript/libraries/escope.js
create mode 100644 packages/context-coloring/languages/javascript/libraries/esprima.js
create mode 100644 packages/context-coloring/languages/javascript/libraries/estraverse.js
create mode 100644 packages/context-coloring/languages/javascript/scopifier.js
create mode 100644 packages/context-coloring/libraries/.dir-locals.el
create mode 100644 packages/context-coloring/libraries/ert-async.el
create mode 100644 packages/context-coloring/libraries/js2-mode.el
create mode 100644 packages/context-coloring/scopifier.png
create mode 100644 packages/context-coloring/screenshot.png
create mode 100644 packages/context-coloring/test/context-coloring-test.el
create mode 100644 packages/context-coloring/test/fixtures/block-scopes.js
create mode 100644 packages/context-coloring/test/fixtures/catch.js
create mode 100644 packages/context-coloring/test/fixtures/comments-and-strings.js
create mode 100644 packages/context-coloring/test/fixtures/function-scopes.js
create mode 100644 packages/context-coloring/test/fixtures/global.js
create mode 100644 packages/context-coloring/test/fixtures/key-names.js
create mode 100644 packages/context-coloring/test/fixtures/key-values.js
create mode 100644 packages/context-coloring/test/fixtures/property-lookup.js
diff --git a/packages/context-coloring/.elpaignore b/packages/context-coloring/.elpaignore
new file mode 100644
index 0000000..5b2e6d9
--- /dev/null
+++ b/packages/context-coloring/.elpaignore
@@ -0,0 +1,13 @@
+.elpaignore
+.gitignore
+.jshintrc
+.travis.yml
+Makefile
+README.md
+benchmark
+libraries/.dir-locals.el
+libraries/ert-async.el
+libraries/js2-mode.el
+scopifier.png
+screenshot.png
+test
diff --git a/packages/context-coloring/.gitignore b/packages/context-coloring/.gitignore
new file mode 100644
index 0000000..d9c77ef
--- /dev/null
+++ b/packages/context-coloring/.gitignore
@@ -0,0 +1,2 @@
+*.elc
+/benchmark/logs/
diff --git a/packages/context-coloring/.travis.yml b/packages/context-coloring/.travis.yml
new file mode 100644
index 0000000..a3b3615
--- /dev/null
+++ b/packages/context-coloring/.travis.yml
@@ -0,0 +1,17 @@
+# https://github.com/rolandwalker/emacs-travis
+
+language: emacs-lisp
+
+env:
+ matrix:
+ - EMACS=emacs24
+
+install:
+ - if [ "$EMACS" = "emacs24" ]; then
+ sudo add-apt-repository -y ppa:cassou/emacs &&
+ sudo apt-get update -qq &&
+ sudo apt-get install -qq emacs24 emacs24-el;
+ fi
+
+script:
+ make test EMACS=${EMACS}
diff --git a/packages/context-coloring/Makefile b/packages/context-coloring/Makefile
new file mode 100644
index 0000000..e718601
--- /dev/null
+++ b/packages/context-coloring/Makefile
@@ -0,0 +1,31 @@
+EMACS = emacs
+
+all: clean compile test
+
+bench:
+ ${EMACS} -Q \
+ -L . \
+ -L libraries \
+ -l context-coloring \
+ -l benchmark/context-coloring-benchmark \
+ -f context-coloring-benchmark-run
+
+compile:
+ ${EMACS} -Q -batch \
+ -L libraries \
+ -f batch-byte-compile *.el libraries/*.el
+
+clean:
+ rm -f *.elc libraries/*.elc
+
+test:
+ ${EMACS} -Q -batch \
+ -L . \
+ -L libraries \
+ -l ert \
+ -l ert-async \
+ -l context-coloring \
+ -l test/context-coloring-test.el \
+ -f ert-run-tests-batch-and-exit
+
+.PHONY: all bench compile clean test
diff --git a/packages/context-coloring/README.md b/packages/context-coloring/README.md
new file mode 100644
index 0000000..c31f895
--- /dev/null
+++ b/packages/context-coloring/README.md
@@ -0,0 +1,161 @@
+# Context Coloring [![Build Status](https://travis-ci.org/jacksonrayhamilton/context-coloring.png?branch=develop)](https://travis-ci.org/jacksonrayhamilton/context-coloring)
+
+
+
+
+
+Highlights code according to function context.
+
+- Code in the global scope is one color. Code in functions within the global
+ scope is a different color, and code within such functions is another color,
+ and so on.
+- Identifiers retain the color of the scope in which they are declared.
+
+Lexical scope information at-a-glance can assist a programmer in understanding
+the overall structure of a program. It can help to curb nasty bugs like name
+shadowing. A rainbow can indicate excessive complexity. State change within a
+closure is easily monitored.
+
+By default, Context Coloring still highlights comments and strings
+syntactically. It is still easy to differentiate code from non-code, and strings
+cannot be confused for variables.
+
+This coloring strategy is probably more useful than conventional syntax
+highlighting. Highlighting keywords can help one to detect spelling errors, but
+a [linter][] could also spot those errors, and if integrated with [flycheck][],
+an extra spot opens up in your editing toolbelt.
+
+Give context coloring a try; you may find that it *changes the way you write
+code*.
+
+## Features
+
+- Supported languages: JavaScript
+- Light and dark (customizable) color schemes.
+- Very fast for files under 1000 lines.
+
+## Installation
+
+Requires Emacs 24+.
+
+JavaScript language support requires either [js2-mode][] or
+[Node.js 0.10+][node], respectively.
+
+### ELPA
+
+- `M-x package-refresh-contents RET`
+- `M-x package-install RET context-coloring RET`
+
+### Git
+
+- Clone this repository.
+
+```bash
+cd ~/.emacs.d/
+git clone https://github.com/jacksonrayhamilton/context-coloring.git
+```
+
+- Byte-compile the package for improved speed.
+
+```bash
+cd context-coloring/
+make compile
+```
+
+- Add the following to your `~/.emacs` file:
+
+```lisp
+(add-to-list 'load-path "~/.emacs.d/context-coloring")
+(require 'context-coloring)
+(add-hook 'js-mode-hook 'context-coloring-mode)
+```
+
+## Customizing
+
+Built-in themes are accessible via `context-coloring-load-theme`. Available
+themes are: `monokai`, `solarized`, `tango` and `zenburn`.
+
+```lisp
+(require 'context-coloring)
+(context-coloring-load-theme 'zenburn)
+```
+
+You can define your own themes, too:
+
+```lisp
+(context-coloring-define-theme
+ 'zenburn
+ :colors '("#DCDCCC"
+ "#93E0E3"
+ "#BFEBBF"
+ "#F0DFAF"
+ "#DFAF8F"
+ "#CC9393"
+ "#DC8CC3"
+ "#94BFF3"
+ "#9FC59F"
+ "#D0BF8F"
+ "#DCA3A3"))
+```
+
+## Extending
+
+To add support for a new language, write a "scopifier" for it, and define a new
+coloring dispatch strategy with `context-coloring-define-dispatch`. Then the
+plugin should handle the rest.
+
+A "scopifier" is a CLI program that reads a buffer's contents from stdin and
+writes a JSON array of numbers to stdout. Every three numbers in the array
+represent a range of color. For instance, if I fed the following string of
+JavaScript code to a scopifier,
+
+```js
+var a = function () {};
+```
+
+then the scopifier would produce the following array:
+
+```js
+[1,24,0,9,23,1]
+```
+
+Where, for every three numbers, the first number is a 1-indexed start [point][],
+the second number is an exclusive end point, and the third number is a scope
+level. The result of applying level 0 coloring to the range [1, 24) and then
+applying level 1 coloring to the range [9, 23) would result in the following
+coloring:
+
+
+
+
+
+If there is an abstract syntax tree generator for your language, you can walk
+the syntax tree, find variables and scopes, and build their positions and levels
+into an array like the one above.
+
+For example, a Ruby scopifier might be defined and implemented like this:
+
+```lisp
+(context-coloring-define-dispatch
+ 'ruby
+ :modes '(ruby-mode)
+ :executable "ruby"
+ :command "/home/username/scopifier")
+```
+
+```ruby
+#!/usr/bin/env ruby
+def scopifier(code)
+ # Parse code.
+ # Return an array.
+end
+print scopifier ARGF.read
+```
+
+[linter]: http://jshint.com/about/
+[flycheck]: http://www.flycheck.org/
+[zenburn]: http://github.com/bbatsov/zenburn-emacs
+[point]: http://www.gnu.org/software/emacs/manual/html_node/elisp/Point.html
+[js2-mode]: https://github.com/mooz/js2-mode
+[node]: http://nodejs.org/download/
+[load path]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Lisp-Libraries.html
diff --git a/packages/context-coloring/benchmark/context-coloring-benchmark.el b/packages/context-coloring/benchmark/context-coloring-benchmark.el
new file mode 100644
index 0000000..004b66f
--- /dev/null
+++ b/packages/context-coloring/benchmark/context-coloring-benchmark.el
@@ -0,0 +1,160 @@
+;;; benchmark/context-coloring-benchmark.el --- Benchmarks for context coloring. -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2014-2015 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see .
+
+;;; Code:
+
+(defconst context-coloring-benchmark-path
+ (file-name-directory (or load-file-name buffer-file-name))
+ "This file's directory.")
+
+(defun context-coloring-benchmark-resolve-path (path)
+ "Resolve PATH from this file's directory."
+ (expand-file-name path context-coloring-benchmark-path))
+
+(defun context-coloring-benchmark-log-results (result-file fixture)
+ "Log benchmarking results for FIXTURE to RESULT-FILE."
+ (elp-results)
+ (let ((results-buffer (current-buffer)))
+ (with-temp-buffer
+ (insert (concat fixture "\n"))
+ (prepend-to-buffer results-buffer (point-min) (point-max)))
+ (with-temp-buffer
+ (insert "\n")
+ (append-to-buffer results-buffer (point-min) (point-max))))
+ (make-directory (context-coloring-benchmark-resolve-path "./logs") t)
+ (append-to-file nil nil result-file))
+
+(defun context-coloring-benchmark-next-tick (function)
+ "Defer execution of FUNCTION to clear the stack and to ensure
+asynchrony."
+ (run-at-time 0.001 nil function))
+
+(defun context-coloring-benchmark-next (list continue stop)
+ "Run the next test in LIST by calling CONTINUE. When LIST is
+exhausted, call STOP instead."
+ (if (null list)
+ (context-coloring-benchmark-next-tick stop)
+ (context-coloring-benchmark-next-tick
+ (lambda ()
+ (funcall
+ continue
+ (car list)
+ (lambda ()
+ (context-coloring-benchmark-next (cdr list) continue stop)))))))
+
+(defun context-coloring-benchmark-async (title setup teardown fixtures callback)
+ "Measure the performance of all FIXTURES, calling CALLBACK when
+all are done."
+ (funcall setup)
+ (let ((result-file (context-coloring-benchmark-resolve-path
+ (format "./logs/results-%s-%s.log"
+ title (format-time-string "%s")))))
+ (context-coloring-benchmark-next
+ fixtures
+ (lambda (path next)
+ (let ((fixture (context-coloring-benchmark-resolve-path path))
+ advice)
+ (setq
+ advice
+ (let ((count 0))
+ (lambda (original-function)
+ (funcall
+ original-function
+ (lambda ()
+ (setq count (+ count 1))
+ ;; Test 5 times.
+ (if (= count 5)
+ (progn
+ (advice-remove 'context-coloring-colorize advice)
+ (kill-buffer)
+ (context-coloring-benchmark-log-results
+ result-file
+ fixture)
+ (funcall next))
+ (funcall 'context-coloring-colorize)))))))
+ (advice-add 'context-coloring-colorize :around advice)
+ (find-file fixture)))
+ (lambda ()
+ (funcall teardown)
+ (if callback (funcall callback))))))
+
+(defconst context-coloring-benchmark-js-fixtures
+ '("./fixtures/jquery-2.1.1.js"
+ "./fixtures/lodash-2.4.1.js"
+ "./fixtures/async-0.9.0.js"
+ "./fixtures/mkdirp-0.5.0.js")
+ "Arbitrary JavaScript files for performance scrutiny.")
+
+(defun context-coloring-benchmark-js-mode-setup ()
+ "Preparation logic for `js-mode'."
+ (add-hook 'js-mode-hook 'context-coloring-mode)
+ (elp-instrument-package "context-coloring-"))
+
+(defun context-coloring-benchmark-js-mode-teardown ()
+ "Cleanup logic for `js-mode'."
+ (remove-hook 'js-mode-hook 'context-coloring-mode))
+
+(defun context-coloring-benchmark-js-mode-run (callback)
+ "Benchmark `js-mode', then call CALLBACK."
+ (context-coloring-benchmark-async
+ "js-mode"
+ 'context-coloring-benchmark-js-mode-setup
+ 'context-coloring-benchmark-js-mode-teardown
+ context-coloring-benchmark-js-fixtures
+ callback))
+
+(defun context-coloring-benchmark-js2-mode-setup ()
+ "Preparation logic for `js2-mode'."
+ (require 'js2-mode)
+ (setq js2-mode-show-parse-errors nil)
+ (setq js2-mode-show-strict-warnings nil)
+ (add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
+ (add-hook 'js2-mode-hook 'context-coloring-mode)
+ (elp-instrument-package "context-coloring-"))
+
+(defun context-coloring-benchmark-js2-mode-teardown ()
+ "Cleanup logic for `js2-mode'."
+ (remove-hook 'js2-mode-hook 'context-coloring-mode)
+ (setq auto-mode-alist (delete '("\\.js\\'" . js2-mode)
+ auto-mode-alist))
+ (setq js2-mode-show-strict-warnings t)
+ (setq js2-mode-show-parse-errors t))
+
+(defun context-coloring-benchmark-js2-mode-run (callback)
+ "Benchmark `js2-mode', then call CALLBACK."
+ (context-coloring-benchmark-async
+ "js2-mode"
+ 'context-coloring-benchmark-js2-mode-setup
+ 'context-coloring-benchmark-js2-mode-teardown
+ context-coloring-benchmark-js-fixtures
+ callback))
+
+(defun context-coloring-benchmark-run ()
+ "Benchmark all modes, then exit."
+ (context-coloring-benchmark-next
+ '(context-coloring-benchmark-js-mode-run
+ context-coloring-benchmark-js2-mode-run)
+ (lambda (function next)
+ (funcall function next))
+ (lambda ()
+ (kill-emacs))))
+
+(provide 'context-coloring-benchmark)
+
+;;; context-coloring-benchmark.el ends here
diff --git a/packages/context-coloring/benchmark/fixtures/.dir-locals.el b/packages/context-coloring/benchmark/fixtures/.dir-locals.el
new file mode 100644
index 0000000..f3d5e97
--- /dev/null
+++ b/packages/context-coloring/benchmark/fixtures/.dir-locals.el
@@ -0,0 +1 @@
+((nil . ((buffer-read-only . t))))
diff --git a/packages/context-coloring/benchmark/fixtures/async-0.9.0.js b/packages/context-coloring/benchmark/fixtures/async-0.9.0.js
new file mode 100644
index 0000000..01e8afc
--- /dev/null
+++ b/packages/context-coloring/benchmark/fixtures/async-0.9.0.js
@@ -0,0 +1,1123 @@
+/*!
+ * async
+ * https://github.com/caolan/async
+ *
+ * Copyright 2010-2014 Caolan McMahon
+ * Released under the MIT license
+ */
+/*jshint onevar: false, indent:4 */
+/*global setImmediate: false, setTimeout: false, console: false */
+(function () {
+
+ var async = {};
+
+ // global on the server, window in the browser
+ var root, previous_async;
+
+ root = this;
+ if (root != null) {
+ previous_async = root.async;
+ }
+
+ async.noConflict = function () {
+ root.async = previous_async;
+ return async;
+ };
+
+ function only_once(fn) {
+ var called = false;
+ return function() {
+ if (called) throw new Error("Callback was already called.");
+ called = true;
+ fn.apply(root, arguments);
+ }
+ }
+
+ //// cross-browser compatiblity functions ////
+
+ var _toString = Object.prototype.toString;
+
+ var _isArray = Array.isArray || function (obj) {
+ return _toString.call(obj) === '[object Array]';
+ };
+
+ var _each = function (arr, iterator) {
+ if (arr.forEach) {
+ return arr.forEach(iterator);
+ }
+ for (var i = 0; i < arr.length; i += 1) {
+ iterator(arr[i], i, arr);
+ }
+ };
+
+ var _map = function (arr, iterator) {
+ if (arr.map) {
+ return arr.map(iterator);
+ }
+ var results = [];
+ _each(arr, function (x, i, a) {
+ results.push(iterator(x, i, a));
+ });
+ return results;
+ };
+
+ var _reduce = function (arr, iterator, memo) {
+ if (arr.reduce) {
+ return arr.reduce(iterator, memo);
+ }
+ _each(arr, function (x, i, a) {
+ memo = iterator(memo, x, i, a);
+ });
+ return memo;
+ };
+
+ var _keys = function (obj) {
+ if (Object.keys) {
+ return Object.keys(obj);
+ }
+ var keys = [];
+ for (var k in obj) {
+ if (obj.hasOwnProperty(k)) {
+ keys.push(k);
+ }
+ }
+ return keys;
+ };
+
+ //// exported async module functions ////
+
+ //// nextTick implementation with browser-compatible fallback ////
+ if (typeof process === 'undefined' || !(process.nextTick)) {
+ if (typeof setImmediate === 'function') {
+ async.nextTick = function (fn) {
+ // not a direct alias for IE10 compatibility
+ setImmediate(fn);
+ };
+ async.setImmediate = async.nextTick;
+ }
+ else {
+ async.nextTick = function (fn) {
+ setTimeout(fn, 0);
+ };
+ async.setImmediate = async.nextTick;
+ }
+ }
+ else {
+ async.nextTick = process.nextTick;
+ if (typeof setImmediate !== 'undefined') {
+ async.setImmediate = function (fn) {
+ // not a direct alias for IE10 compatibility
+ setImmediate(fn);
+ };
+ }
+ else {
+ async.setImmediate = async.nextTick;
+ }
+ }
+
+ async.each = function (arr, iterator, callback) {
+ callback = callback || function () {};
+ if (!arr.length) {
+ return callback();
+ }
+ var completed = 0;
+ _each(arr, function (x) {
+ iterator(x, only_once(done) );
+ });
+ function done(err) {
+ if (err) {
+ callback(err);
+ callback = function () {};
+ }
+ else {
+ completed += 1;
+ if (completed >= arr.length) {
+ callback();
+ }
+ }
+ }
+ };
+ async.forEach = async.each;
+
+ async.eachSeries = function (arr, iterator, callback) {
+ callback = callback || function () {};
+ if (!arr.length) {
+ return callback();
+ }
+ var completed = 0;
+ var iterate = function () {
+ iterator(arr[completed], function (err) {
+ if (err) {
+ callback(err);
+ callback = function () {};
+ }
+ else {
+ completed += 1;
+ if (completed >= arr.length) {
+ callback();
+ }
+ else {
+ iterate();
+ }
+ }
+ });
+ };
+ iterate();
+ };
+ async.forEachSeries = async.eachSeries;
+
+ async.eachLimit = function (arr, limit, iterator, callback) {
+ var fn = _eachLimit(limit);
+ fn.apply(null, [arr, iterator, callback]);
+ };
+ async.forEachLimit = async.eachLimit;
+
+ var _eachLimit = function (limit) {
+
+ return function (arr, iterator, callback) {
+ callback = callback || function () {};
+ if (!arr.length || limit <= 0) {
+ return callback();
+ }
+ var completed = 0;
+ var started = 0;
+ var running = 0;
+
+ (function replenish () {
+ if (completed >= arr.length) {
+ return callback();
+ }
+
+ while (running < limit && started < arr.length) {
+ started += 1;
+ running += 1;
+ iterator(arr[started - 1], function (err) {
+ if (err) {
+ callback(err);
+ callback = function () {};
+ }
+ else {
+ completed += 1;
+ running -= 1;
+ if (completed >= arr.length) {
+ callback();
+ }
+ else {
+ replenish();
+ }
+ }
+ });
+ }
+ })();
+ };
+ };
+
+
+ var doParallel = function (fn) {
+ return function () {
+ var args = Array.prototype.slice.call(arguments);
+ return fn.apply(null, [async.each].concat(args));
+ };
+ };
+ var doParallelLimit = function(limit, fn) {
+ return function () {
+ var args = Array.prototype.slice.call(arguments);
+ return fn.apply(null, [_eachLimit(limit)].concat(args));
+ };
+ };
+ var doSeries = function (fn) {
+ return function () {
+ var args = Array.prototype.slice.call(arguments);
+ return fn.apply(null, [async.eachSeries].concat(args));
+ };
+ };
+
+
+ var _asyncMap = function (eachfn, arr, iterator, callback) {
+ arr = _map(arr, function (x, i) {
+ return {index: i, value: x};
+ });
+ if (!callback) {
+ eachfn(arr, function (x, callback) {
+ iterator(x.value, function (err) {
+ callback(err);
+ });
+ });
+ } else {
+ var results = [];
+ eachfn(arr, function (x, callback) {
+ iterator(x.value, function (err, v) {
+ results[x.index] = v;
+ callback(err);
+ });
+ }, function (err) {
+ callback(err, results);
+ });
+ }
+ };
+ async.map = doParallel(_asyncMap);
+ async.mapSeries = doSeries(_asyncMap);
+ async.mapLimit = function (arr, limit, iterator, callback) {
+ return _mapLimit(limit)(arr, iterator, callback);
+ };
+
+ var _mapLimit = function(limit) {
+ return doParallelLimit(limit, _asyncMap);
+ };
+
+ // reduce only has a series version, as doing reduce in parallel won't
+ // work in many situations.
+ async.reduce = function (arr, memo, iterator, callback) {
+ async.eachSeries(arr, function (x, callback) {
+ iterator(memo, x, function (err, v) {
+ memo = v;
+ callback(err);
+ });
+ }, function (err) {
+ callback(err, memo);
+ });
+ };
+ // inject alias
+ async.inject = async.reduce;
+ // foldl alias
+ async.foldl = async.reduce;
+
+ async.reduceRight = function (arr, memo, iterator, callback) {
+ var reversed = _map(arr, function (x) {
+ return x;
+ }).reverse();
+ async.reduce(reversed, memo, iterator, callback);
+ };
+ // foldr alias
+ async.foldr = async.reduceRight;
+
+ var _filter = function (eachfn, arr, iterator, callback) {
+ var results = [];
+ arr = _map(arr, function (x, i) {
+ return {index: i, value: x};
+ });
+ eachfn(arr, function (x, callback) {
+ iterator(x.value, function (v) {
+ if (v) {
+ results.push(x);
+ }
+ callback();
+ });
+ }, function (err) {
+ callback(_map(results.sort(function (a, b) {
+ return a.index - b.index;
+ }), function (x) {
+ return x.value;
+ }));
+ });
+ };
+ async.filter = doParallel(_filter);
+ async.filterSeries = doSeries(_filter);
+ // select alias
+ async.select = async.filter;
+ async.selectSeries = async.filterSeries;
+
+ var _reject = function (eachfn, arr, iterator, callback) {
+ var results = [];
+ arr = _map(arr, function (x, i) {
+ return {index: i, value: x};
+ });
+ eachfn(arr, function (x, callback) {
+ iterator(x.value, function (v) {
+ if (!v) {
+ results.push(x);
+ }
+ callback();
+ });
+ }, function (err) {
+ callback(_map(results.sort(function (a, b) {
+ return a.index - b.index;
+ }), function (x) {
+ return x.value;
+ }));
+ });
+ };
+ async.reject = doParallel(_reject);
+ async.rejectSeries = doSeries(_reject);
+
+ var _detect = function (eachfn, arr, iterator, main_callback) {
+ eachfn(arr, function (x, callback) {
+ iterator(x, function (result) {
+ if (result) {
+ main_callback(x);
+ main_callback = function () {};
+ }
+ else {
+ callback();
+ }
+ });
+ }, function (err) {
+ main_callback();
+ });
+ };
+ async.detect = doParallel(_detect);
+ async.detectSeries = doSeries(_detect);
+
+ async.some = function (arr, iterator, main_callback) {
+ async.each(arr, function (x, callback) {
+ iterator(x, function (v) {
+ if (v) {
+ main_callback(true);
+ main_callback = function () {};
+ }
+ callback();
+ });
+ }, function (err) {
+ main_callback(false);
+ });
+ };
+ // any alias
+ async.any = async.some;
+
+ async.every = function (arr, iterator, main_callback) {
+ async.each(arr, function (x, callback) {
+ iterator(x, function (v) {
+ if (!v) {
+ main_callback(false);
+ main_callback = function () {};
+ }
+ callback();
+ });
+ }, function (err) {
+ main_callback(true);
+ });
+ };
+ // all alias
+ async.all = async.every;
+
+ async.sortBy = function (arr, iterator, callback) {
+ async.map(arr, function (x, callback) {
+ iterator(x, function (err, criteria) {
+ if (err) {
+ callback(err);
+ }
+ else {
+ callback(null, {value: x, criteria: criteria});
+ }
+ });
+ }, function (err, results) {
+ if (err) {
+ return callback(err);
+ }
+ else {
+ var fn = function (left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ };
+ callback(null, _map(results.sort(fn), function (x) {
+ return x.value;
+ }));
+ }
+ });
+ };
+
+ async.auto = function (tasks, callback) {
+ callback = callback || function () {};
+ var keys = _keys(tasks);
+ var remainingTasks = keys.length
+ if (!remainingTasks) {
+ return callback();
+ }
+
+ var results = {};
+
+ var listeners = [];
+ var addListener = function (fn) {
+ listeners.unshift(fn);
+ };
+ var removeListener = function (fn) {
+ for (var i = 0; i < listeners.length; i += 1) {
+ if (listeners[i] === fn) {
+ listeners.splice(i, 1);
+ return;
+ }
+ }
+ };
+ var taskComplete = function () {
+ remainingTasks--
+ _each(listeners.slice(0), function (fn) {
+ fn();
+ });
+ };
+
+ addListener(function () {
+ if (!remainingTasks) {
+ var theCallback = callback;
+ // prevent final callback from calling itself if it errors
+ callback = function () {};
+
+ theCallback(null, results);
+ }
+ });
+
+ _each(keys, function (k) {
+ var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]];
+ var taskCallback = function (err) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (args.length <= 1) {
+ args = args[0];
+ }
+ if (err) {
+ var safeResults = {};
+ _each(_keys(results), function(rkey) {
+ safeResults[rkey] = results[rkey];
+ });
+ safeResults[k] = args;
+ callback(err, safeResults);
+ // stop subsequent errors hitting callback multiple times
+ callback = function () {};
+ }
+ else {
+ results[k] = args;
+ async.setImmediate(taskComplete);
+ }
+ };
+ var requires = task.slice(0, Math.abs(task.length - 1)) || [];
+ var ready = function () {
+ return _reduce(requires, function (a, x) {
+ return (a && results.hasOwnProperty(x));
+ }, true) && !results.hasOwnProperty(k);
+ };
+ if (ready()) {
+ task[task.length - 1](taskCallback, results);
+ }
+ else {
+ var listener = function () {
+ if (ready()) {
+ removeListener(listener);
+ task[task.length - 1](taskCallback, results);
+ }
+ };
+ addListener(listener);
+ }
+ });
+ };
+
+ async.retry = function(times, task, callback) {
+ var DEFAULT_TIMES = 5;
+ var attempts = [];
+ // Use defaults if times not passed
+ if (typeof times === 'function') {
+ callback = task;
+ task = times;
+ times = DEFAULT_TIMES;
+ }
+ // Make sure times is a number
+ times = parseInt(times, 10) || DEFAULT_TIMES;
+ var wrappedTask = function(wrappedCallback, wrappedResults) {
+ var retryAttempt = function(task, finalAttempt) {
+ return function(seriesCallback) {
+ task(function(err, result){
+ seriesCallback(!err || finalAttempt, {err: err, result: result});
+ }, wrappedResults);
+ };
+ };
+ while (times) {
+ attempts.push(retryAttempt(task, !(times-=1)));
+ }
+ async.series(attempts, function(done, data){
+ data = data[data.length - 1];
+ (wrappedCallback || callback)(data.err, data.result);
+ });
+ }
+ // If a callback is passed, run this as a controll flow
+ return callback ? wrappedTask() : wrappedTask
+ };
+
+ async.waterfall = function (tasks, callback) {
+ callback = callback || function () {};
+ if (!_isArray(tasks)) {
+ var err = new Error('First argument to waterfall must be an array of functions');
+ return callback(err);
+ }
+ if (!tasks.length) {
+ return callback();
+ }
+ var wrapIterator = function (iterator) {
+ return function (err) {
+ if (err) {
+ callback.apply(null, arguments);
+ callback = function () {};
+ }
+ else {
+ var args = Array.prototype.slice.call(arguments, 1);
+ var next = iterator.next();
+ if (next) {
+ args.push(wrapIterator(next));
+ }
+ else {
+ args.push(callback);
+ }
+ async.setImmediate(function () {
+ iterator.apply(null, args);
+ });
+ }
+ };
+ };
+ wrapIterator(async.iterator(tasks))();
+ };
+
+ var _parallel = function(eachfn, tasks, callback) {
+ callback = callback || function () {};
+ if (_isArray(tasks)) {
+ eachfn.map(tasks, function (fn, callback) {
+ if (fn) {
+ fn(function (err) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (args.length <= 1) {
+ args = args[0];
+ }
+ callback.call(null, err, args);
+ });
+ }
+ }, callback);
+ }
+ else {
+ var results = {};
+ eachfn.each(_keys(tasks), function (k, callback) {
+ tasks[k](function (err) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (args.length <= 1) {
+ args = args[0];
+ }
+ results[k] = args;
+ callback(err);
+ });
+ }, function (err) {
+ callback(err, results);
+ });
+ }
+ };
+
+ async.parallel = function (tasks, callback) {
+ _parallel({ map: async.map, each: async.each }, tasks, callback);
+ };
+
+ async.parallelLimit = function(tasks, limit, callback) {
+ _parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback);
+ };
+
+ async.series = function (tasks, callback) {
+ callback = callback || function () {};
+ if (_isArray(tasks)) {
+ async.mapSeries(tasks, function (fn, callback) {
+ if (fn) {
+ fn(function (err) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (args.length <= 1) {
+ args = args[0];
+ }
+ callback.call(null, err, args);
+ });
+ }
+ }, callback);
+ }
+ else {
+ var results = {};
+ async.eachSeries(_keys(tasks), function (k, callback) {
+ tasks[k](function (err) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (args.length <= 1) {
+ args = args[0];
+ }
+ results[k] = args;
+ callback(err);
+ });
+ }, function (err) {
+ callback(err, results);
+ });
+ }
+ };
+
+ async.iterator = function (tasks) {
+ var makeCallback = function (index) {
+ var fn = function () {
+ if (tasks.length) {
+ tasks[index].apply(null, arguments);
+ }
+ return fn.next();
+ };
+ fn.next = function () {
+ return (index < tasks.length - 1) ? makeCallback(index + 1): null;
+ };
+ return fn;
+ };
+ return makeCallback(0);
+ };
+
+ async.apply = function (fn) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ return function () {
+ return fn.apply(
+ null, args.concat(Array.prototype.slice.call(arguments))
+ );
+ };
+ };
+
+ var _concat = function (eachfn, arr, fn, callback) {
+ var r = [];
+ eachfn(arr, function (x, cb) {
+ fn(x, function (err, y) {
+ r = r.concat(y || []);
+ cb(err);
+ });
+ }, function (err) {
+ callback(err, r);
+ });
+ };
+ async.concat = doParallel(_concat);
+ async.concatSeries = doSeries(_concat);
+
+ async.whilst = function (test, iterator, callback) {
+ if (test()) {
+ iterator(function (err) {
+ if (err) {
+ return callback(err);
+ }
+ async.whilst(test, iterator, callback);
+ });
+ }
+ else {
+ callback();
+ }
+ };
+
+ async.doWhilst = function (iterator, test, callback) {
+ iterator(function (err) {
+ if (err) {
+ return callback(err);
+ }
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (test.apply(null, args)) {
+ async.doWhilst(iterator, test, callback);
+ }
+ else {
+ callback();
+ }
+ });
+ };
+
+ async.until = function (test, iterator, callback) {
+ if (!test()) {
+ iterator(function (err) {
+ if (err) {
+ return callback(err);
+ }
+ async.until(test, iterator, callback);
+ });
+ }
+ else {
+ callback();
+ }
+ };
+
+ async.doUntil = function (iterator, test, callback) {
+ iterator(function (err) {
+ if (err) {
+ return callback(err);
+ }
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (!test.apply(null, args)) {
+ async.doUntil(iterator, test, callback);
+ }
+ else {
+ callback();
+ }
+ });
+ };
+
+ async.queue = function (worker, concurrency) {
+ if (concurrency === undefined) {
+ concurrency = 1;
+ }
+ function _insert(q, data, pos, callback) {
+ if (!q.started){
+ q.started = true;
+ }
+ if (!_isArray(data)) {
+ data = [data];
+ }
+ if(data.length == 0) {
+ // call drain immediately if there are no tasks
+ return async.setImmediate(function() {
+ if (q.drain) {
+ q.drain();
+ }
+ });
+ }
+ _each(data, function(task) {
+ var item = {
+ data: task,
+ callback: typeof callback === 'function' ? callback : null
+ };
+
+ if (pos) {
+ q.tasks.unshift(item);
+ } else {
+ q.tasks.push(item);
+ }
+
+ if (q.saturated && q.tasks.length === q.concurrency) {
+ q.saturated();
+ }
+ async.setImmediate(q.process);
+ });
+ }
+
+ var workers = 0;
+ var q = {
+ tasks: [],
+ concurrency: concurrency,
+ saturated: null,
+ empty: null,
+ drain: null,
+ started: false,
+ paused: false,
+ push: function (data, callback) {
+ _insert(q, data, false, callback);
+ },
+ kill: function () {
+ q.drain = null;
+ q.tasks = [];
+ },
+ unshift: function (data, callback) {
+ _insert(q, data, true, callback);
+ },
+ process: function () {
+ if (!q.paused && workers < q.concurrency && q.tasks.length) {
+ var task = q.tasks.shift();
+ if (q.empty && q.tasks.length === 0) {
+ q.empty();
+ }
+ workers += 1;
+ var next = function () {
+ workers -= 1;
+ if (task.callback) {
+ task.callback.apply(task, arguments);
+ }
+ if (q.drain && q.tasks.length + workers === 0) {
+ q.drain();
+ }
+ q.process();
+ };
+ var cb = only_once(next);
+ worker(task.data, cb);
+ }
+ },
+ length: function () {
+ return q.tasks.length;
+ },
+ running: function () {
+ return workers;
+ },
+ idle: function() {
+ return q.tasks.length + workers === 0;
+ },
+ pause: function () {
+ if (q.paused === true) { return; }
+ q.paused = true;
+ q.process();
+ },
+ resume: function () {
+ if (q.paused === false) { return; }
+ q.paused = false;
+ q.process();
+ }
+ };
+ return q;
+ };
+
+ async.priorityQueue = function (worker, concurrency) {
+
+ function _compareTasks(a, b){
+ return a.priority - b.priority;
+ };
+
+ function _binarySearch(sequence, item, compare) {
+ var beg = -1,
+ end = sequence.length - 1;
+ while (beg < end) {
+ var mid = beg + ((end - beg + 1) >>> 1);
+ if (compare(item, sequence[mid]) >= 0) {
+ beg = mid;
+ } else {
+ end = mid - 1;
+ }
+ }
+ return beg;
+ }
+
+ function _insert(q, data, priority, callback) {
+ if (!q.started){
+ q.started = true;
+ }
+ if (!_isArray(data)) {
+ data = [data];
+ }
+ if(data.length == 0) {
+ // call drain immediately if there are no tasks
+ return async.setImmediate(function() {
+ if (q.drain) {
+ q.drain();
+ }
+ });
+ }
+ _each(data, function(task) {
+ var item = {
+ data: task,
+ priority: priority,
+ callback: typeof callback === 'function' ? callback : null
+ };
+
+ q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item);
+
+ if (q.saturated && q.tasks.length === q.concurrency) {
+ q.saturated();
+ }
+ async.setImmediate(q.process);
+ });
+ }
+
+ // Start with a normal queue
+ var q = async.queue(worker, concurrency);
+
+ // Override push to accept second parameter representing priority
+ q.push = function (data, priority, callback) {
+ _insert(q, data, priority, callback);
+ };
+
+ // Remove unshift function
+ delete q.unshift;
+
+ return q;
+ };
+
+ async.cargo = function (worker, payload) {
+ var working = false,
+ tasks = [];
+
+ var cargo = {
+ tasks: tasks,
+ payload: payload,
+ saturated: null,
+ empty: null,
+ drain: null,
+ drained: true,
+ push: function (data, callback) {
+ if (!_isArray(data)) {
+ data = [data];
+ }
+ _each(data, function(task) {
+ tasks.push({
+ data: task,
+ callback: typeof callback === 'function' ? callback : null
+ });
+ cargo.drained = false;
+ if (cargo.saturated && tasks.length === payload) {
+ cargo.saturated();
+ }
+ });
+ async.setImmediate(cargo.process);
+ },
+ process: function process() {
+ if (working) return;
+ if (tasks.length === 0) {
+ if(cargo.drain && !cargo.drained) cargo.drain();
+ cargo.drained = true;
+ return;
+ }
+
+ var ts = typeof payload === 'number'
+ ? tasks.splice(0, payload)
+ : tasks.splice(0, tasks.length);
+
+ var ds = _map(ts, function (task) {
+ return task.data;
+ });
+
+ if(cargo.empty) cargo.empty();
+ working = true;
+ worker(ds, function () {
+ working = false;
+
+ var args = arguments;
+ _each(ts, function (data) {
+ if (data.callback) {
+ data.callback.apply(null, args);
+ }
+ });
+
+ process();
+ });
+ },
+ length: function () {
+ return tasks.length;
+ },
+ running: function () {
+ return working;
+ }
+ };
+ return cargo;
+ };
+
+ var _console_fn = function (name) {
+ return function (fn) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ fn.apply(null, args.concat([function (err) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (typeof console !== 'undefined') {
+ if (err) {
+ if (console.error) {
+ console.error(err);
+ }
+ }
+ else if (console[name]) {
+ _each(args, function (x) {
+ console[name](x);
+ });
+ }
+ }
+ }]));
+ };
+ };
+ async.log = _console_fn('log');
+ async.dir = _console_fn('dir');
+ /*async.info = _console_fn('info');
+ async.warn = _console_fn('warn');
+ async.error = _console_fn('error');*/
+
+ async.memoize = function (fn, hasher) {
+ var memo = {};
+ var queues = {};
+ hasher = hasher || function (x) {
+ return x;
+ };
+ var memoized = function () {
+ var args = Array.prototype.slice.call(arguments);
+ var callback = args.pop();
+ var key = hasher.apply(null, args);
+ if (key in memo) {
+ async.nextTick(function () {
+ callback.apply(null, memo[key]);
+ });
+ }
+ else if (key in queues) {
+ queues[key].push(callback);
+ }
+ else {
+ queues[key] = [callback];
+ fn.apply(null, args.concat([function () {
+ memo[key] = arguments;
+ var q = queues[key];
+ delete queues[key];
+ for (var i = 0, l = q.length; i < l; i++) {
+ q[i].apply(null, arguments);
+ }
+ }]));
+ }
+ };
+ memoized.memo = memo;
+ memoized.unmemoized = fn;
+ return memoized;
+ };
+
+ async.unmemoize = function (fn) {
+ return function () {
+ return (fn.unmemoized || fn).apply(null, arguments);
+ };
+ };
+
+ async.times = function (count, iterator, callback) {
+ var counter = [];
+ for (var i = 0; i < count; i++) {
+ counter.push(i);
+ }
+ return async.map(counter, iterator, callback);
+ };
+
+ async.timesSeries = function (count, iterator, callback) {
+ var counter = [];
+ for (var i = 0; i < count; i++) {
+ counter.push(i);
+ }
+ return async.mapSeries(counter, iterator, callback);
+ };
+
+ async.seq = function (/* functions... */) {
+ var fns = arguments;
+ return function () {
+ var that = this;
+ var args = Array.prototype.slice.call(arguments);
+ var callback = args.pop();
+ async.reduce(fns, args, function (newargs, fn, cb) {
+ fn.apply(that, newargs.concat([function () {
+ var err = arguments[0];
+ var nextargs = Array.prototype.slice.call(arguments, 1);
+ cb(err, nextargs);
+ }]))
+ },
+ function (err, results) {
+ callback.apply(that, [err].concat(results));
+ });
+ };
+ };
+
+ async.compose = function (/* functions... */) {
+ return async.seq.apply(null, Array.prototype.reverse.call(arguments));
+ };
+
+ var _applyEach = function (eachfn, fns /*args...*/) {
+ var go = function () {
+ var that = this;
+ var args = Array.prototype.slice.call(arguments);
+ var callback = args.pop();
+ return eachfn(fns, function (fn, cb) {
+ fn.apply(that, args.concat([cb]));
+ },
+ callback);
+ };
+ if (arguments.length > 2) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ return go.apply(this, args);
+ }
+ else {
+ return go;
+ }
+ };
+ async.applyEach = doParallel(_applyEach);
+ async.applyEachSeries = doSeries(_applyEach);
+
+ async.forever = function (fn, callback) {
+ function next(err) {
+ if (err) {
+ if (callback) {
+ return callback(err);
+ }
+ throw err;
+ }
+ fn(next);
+ }
+ next();
+ };
+
+ // Node.js
+ if (typeof module !== 'undefined' && module.exports) {
+ module.exports = async;
+ }
+ // AMD / RequireJS
+ else if (typeof define !== 'undefined' && define.amd) {
+ define([], function () {
+ return async;
+ });
+ }
+ // included directly via