// MIT license (by Elan Shanker).
(function(globals) {
  'use strict';

  var nextTick = function (fn) {
    if (typeof process === 'undefined' || !(process.nextTick)) {
      setTimeout(fn, 0);
    } else {
      process.nextTick(fn);
    }
  };

  var makeIterator = 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);
  };

  var waterfall = function (tasks, callback) {
    callback = callback || function () {};
    if (tasks.constructor !== Array) {
      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);
          }
          nextTick(function () {
            iterator.apply(null, args);
          });
        }
      };
    };
    wrapIterator(makeIterator(tasks))();
  };

  if (typeof define === 'function' && typeof define.amd === 'object') {
    define(waterfall); // RequireJS
  } else if (typeof module === 'object' && module.exports) {
    module.exports = waterfall; // CommonJS
  } else {
    globals.asyncEach = waterfall; // <script>
  }
})(this);

// MIT license (by Elan Shanker).
(function(globals) {
  'use strict';

  var nextTick = function (fn) {
    if (typeof process === 'undefined' || !(process.nextTick)) {
      setTimeout(fn, 0);
    } else {
      process.nextTick(fn);
    }
  };

  var makeIterator = 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);
  };

  var waterfall = function (tasks, callback) {
    callback = callback || function () {};
    if (tasks.constructor !== Array) {
      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);
          }
          nextTick(function () {
            iterator.apply(null, args);
          });
        }
      };
    };
    wrapIterator(makeIterator(tasks))();
  };

  if (typeof define === 'function' && typeof define.amd === 'object') {
    define(waterfall); // RequireJS
  } else if (typeof module === 'object' && module.exports) {
    module.exports = waterfall; // CommonJS
  } else {
    globals.asyncWaterfall = waterfall; // <script>
  }
})(this);

fix globals binding