| "use strict"; | 
| module.exports = function(Promise, | 
|                           PromiseArray, | 
|                           apiRejection, | 
|                           tryConvertToPromise, | 
|                           INTERNAL, | 
|                           debug) { | 
| var util = require("./util"); | 
| var tryCatch = util.tryCatch; | 
|   | 
| function ReductionPromiseArray(promises, fn, initialValue, _each) { | 
|     this.constructor$(promises); | 
|     var context = Promise._getContext(); | 
|     this._fn = util.contextBind(context, fn); | 
|     if (initialValue !== undefined) { | 
|         initialValue = Promise.resolve(initialValue); | 
|         initialValue._attachCancellationCallback(this); | 
|     } | 
|     this._initialValue = initialValue; | 
|     this._currentCancellable = null; | 
|     if(_each === INTERNAL) { | 
|         this._eachValues = Array(this._length); | 
|     } else if (_each === 0) { | 
|         this._eachValues = null; | 
|     } else { | 
|         this._eachValues = undefined; | 
|     } | 
|     this._promise._captureStackTrace(); | 
|     this._init$(undefined, -5); | 
| } | 
| util.inherits(ReductionPromiseArray, PromiseArray); | 
|   | 
| ReductionPromiseArray.prototype._gotAccum = function(accum) { | 
|     if (this._eachValues !== undefined && | 
|         this._eachValues !== null && | 
|         accum !== INTERNAL) { | 
|         this._eachValues.push(accum); | 
|     } | 
| }; | 
|   | 
| ReductionPromiseArray.prototype._eachComplete = function(value) { | 
|     if (this._eachValues !== null) { | 
|         this._eachValues.push(value); | 
|     } | 
|     return this._eachValues; | 
| }; | 
|   | 
| ReductionPromiseArray.prototype._init = function() {}; | 
|   | 
| ReductionPromiseArray.prototype._resolveEmptyArray = function() { | 
|     this._resolve(this._eachValues !== undefined ? this._eachValues | 
|                                                  : this._initialValue); | 
| }; | 
|   | 
| ReductionPromiseArray.prototype.shouldCopyValues = function () { | 
|     return false; | 
| }; | 
|   | 
| ReductionPromiseArray.prototype._resolve = function(value) { | 
|     this._promise._resolveCallback(value); | 
|     this._values = null; | 
| }; | 
|   | 
| ReductionPromiseArray.prototype._resultCancelled = function(sender) { | 
|     if (sender === this._initialValue) return this._cancel(); | 
|     if (this._isResolved()) return; | 
|     this._resultCancelled$(); | 
|     if (this._currentCancellable instanceof Promise) { | 
|         this._currentCancellable.cancel(); | 
|     } | 
|     if (this._initialValue instanceof Promise) { | 
|         this._initialValue.cancel(); | 
|     } | 
| }; | 
|   | 
| ReductionPromiseArray.prototype._iterate = function (values) { | 
|     this._values = values; | 
|     var value; | 
|     var i; | 
|     var length = values.length; | 
|     if (this._initialValue !== undefined) { | 
|         value = this._initialValue; | 
|         i = 0; | 
|     } else { | 
|         value = Promise.resolve(values[0]); | 
|         i = 1; | 
|     } | 
|   | 
|     this._currentCancellable = value; | 
|   | 
|     for (var j = i; j < length; ++j) { | 
|         var maybePromise = values[j]; | 
|         if (maybePromise instanceof Promise) { | 
|             maybePromise.suppressUnhandledRejections(); | 
|         } | 
|     } | 
|   | 
|     if (!value.isRejected()) { | 
|         for (; i < length; ++i) { | 
|             var ctx = { | 
|                 accum: null, | 
|                 value: values[i], | 
|                 index: i, | 
|                 length: length, | 
|                 array: this | 
|             }; | 
|   | 
|             value = value._then(gotAccum, undefined, undefined, ctx, undefined); | 
|   | 
|             if ((i & 127) === 0) { | 
|                 value._setNoAsyncGuarantee(); | 
|             } | 
|         } | 
|     } | 
|   | 
|     if (this._eachValues !== undefined) { | 
|         value = value | 
|             ._then(this._eachComplete, undefined, undefined, this, undefined); | 
|     } | 
|     value._then(completed, completed, undefined, value, this); | 
| }; | 
|   | 
| Promise.prototype.reduce = function (fn, initialValue) { | 
|     return reduce(this, fn, initialValue, null); | 
| }; | 
|   | 
| Promise.reduce = function (promises, fn, initialValue, _each) { | 
|     return reduce(promises, fn, initialValue, _each); | 
| }; | 
|   | 
| function completed(valueOrReason, array) { | 
|     if (this.isFulfilled()) { | 
|         array._resolve(valueOrReason); | 
|     } else { | 
|         array._reject(valueOrReason); | 
|     } | 
| } | 
|   | 
| function reduce(promises, fn, initialValue, _each) { | 
|     if (typeof fn !== "function") { | 
|         return apiRejection("expecting a function but got " + util.classString(fn)); | 
|     } | 
|     var array = new ReductionPromiseArray(promises, fn, initialValue, _each); | 
|     return array.promise(); | 
| } | 
|   | 
| function gotAccum(accum) { | 
|     this.accum = accum; | 
|     this.array._gotAccum(accum); | 
|     var value = tryConvertToPromise(this.value, this.array._promise); | 
|     if (value instanceof Promise) { | 
|         this.array._currentCancellable = value; | 
|         return value._then(gotValue, undefined, undefined, this, undefined); | 
|     } else { | 
|         return gotValue.call(this, value); | 
|     } | 
| } | 
|   | 
| function gotValue(value) { | 
|     var array = this.array; | 
|     var promise = array._promise; | 
|     var fn = tryCatch(array._fn); | 
|     promise._pushContext(); | 
|     var ret; | 
|     if (array._eachValues !== undefined) { | 
|         ret = fn.call(promise._boundValue(), value, this.index, this.length); | 
|     } else { | 
|         ret = fn.call(promise._boundValue(), | 
|                               this.accum, value, this.index, this.length); | 
|     } | 
|     if (ret instanceof Promise) { | 
|         array._currentCancellable = ret; | 
|     } | 
|     var promiseCreated = promise._popContext(); | 
|     debug.checkForgottenReturns( | 
|         ret, | 
|         promiseCreated, | 
|         array._eachValues !== undefined ? "Promise.each" : "Promise.reduce", | 
|         promise | 
|     ); | 
|     return ret; | 
| } | 
| }; |