1// MIT license (by Elan Shanker).
2(function(globals) {
3  'use strict';
4
5  var executeSync = function(){
6    var args = Array.prototype.slice.call(arguments);
7    if (typeof args[0] === 'function'){
8      args[0].apply(null, args.splice(1));
9    }
10  };
11
12  var executeAsync = function(fn){
13    if (typeof setImmediate === 'function') {
14      setImmediate(fn);
15    } else if (typeof process !== 'undefined' && process.nextTick) {
16      process.nextTick(fn);
17    } else {
18      setTimeout(fn, 0);
19    }
20  };
21
22  var makeIterator = function (tasks) {
23    var makeCallback = function (index) {
24      var fn = function () {
25        if (tasks.length) {
26          tasks[index].apply(null, arguments);
27        }
28        return fn.next();
29      };
30      fn.next = function () {
31        return (index < tasks.length - 1) ? makeCallback(index + 1): null;
32      };
33      return fn;
34    };
35    return makeCallback(0);
36  };
37
38  var _isArray = Array.isArray || function(maybeArray){
39    return Object.prototype.toString.call(maybeArray) === '[object Array]';
40  };
41
42  var waterfall = function (tasks, callback, forceAsync) {
43    var nextTick = forceAsync ? executeAsync : executeSync;
44    callback = callback || function () {};
45    if (!_isArray(tasks)) {
46      var err = new Error('First argument to waterfall must be an array of functions');
47      return callback(err);
48    }
49    if (!tasks.length) {
50      return callback();
51    }
52    var wrapIterator = function (iterator) {
53      return function (err) {
54        if (err) {
55          callback.apply(null, arguments);
56          callback = function () {};
57        } else {
58          var args = Array.prototype.slice.call(arguments, 1);
59          var next = iterator.next();
60          if (next) {
61            args.push(wrapIterator(next));
62          } else {
63            args.push(callback);
64          }
65          nextTick(function () {
66            iterator.apply(null, args);
67          });
68        }
69      };
70    };
71    wrapIterator(makeIterator(tasks))();
72  };
73
74  if (typeof define !== 'undefined' && define.amd) {
75    define([], function () {
76      return waterfall;
77    }); // RequireJS
78  } else if (typeof module !== 'undefined' && module.exports) {
79    module.exports = waterfall; // CommonJS
80  } else {
81    globals.waterfall = waterfall; // <script>
82  }
83})(this);
84