| "use strict"; | 
| module.exports = function(Promise, tryConvertToPromise, NEXT_FILTER) { | 
| var util = require("./util"); | 
| var CancellationError = Promise.CancellationError; | 
| var errorObj = util.errorObj; | 
| var catchFilter = require("./catch_filter")(NEXT_FILTER); | 
|   | 
| function PassThroughHandlerContext(promise, type, handler) { | 
|     this.promise = promise; | 
|     this.type = type; | 
|     this.handler = handler; | 
|     this.called = false; | 
|     this.cancelPromise = null; | 
| } | 
|   | 
| PassThroughHandlerContext.prototype.isFinallyHandler = function() { | 
|     return this.type === 0; | 
| }; | 
|   | 
| function FinallyHandlerCancelReaction(finallyHandler) { | 
|     this.finallyHandler = finallyHandler; | 
| } | 
|   | 
| FinallyHandlerCancelReaction.prototype._resultCancelled = function() { | 
|     checkCancel(this.finallyHandler); | 
| }; | 
|   | 
| function checkCancel(ctx, reason) { | 
|     if (ctx.cancelPromise != null) { | 
|         if (arguments.length > 1) { | 
|             ctx.cancelPromise._reject(reason); | 
|         } else { | 
|             ctx.cancelPromise._cancel(); | 
|         } | 
|         ctx.cancelPromise = null; | 
|         return true; | 
|     } | 
|     return false; | 
| } | 
|   | 
| function succeed() { | 
|     return finallyHandler.call(this, this.promise._target()._settledValue()); | 
| } | 
| function fail(reason) { | 
|     if (checkCancel(this, reason)) return; | 
|     errorObj.e = reason; | 
|     return errorObj; | 
| } | 
| function finallyHandler(reasonOrValue) { | 
|     var promise = this.promise; | 
|     var handler = this.handler; | 
|   | 
|     if (!this.called) { | 
|         this.called = true; | 
|         var ret = this.isFinallyHandler() | 
|             ? handler.call(promise._boundValue()) | 
|             : handler.call(promise._boundValue(), reasonOrValue); | 
|         if (ret === NEXT_FILTER) { | 
|             return ret; | 
|         } else if (ret !== undefined) { | 
|             promise._setReturnedNonUndefined(); | 
|             var maybePromise = tryConvertToPromise(ret, promise); | 
|             if (maybePromise instanceof Promise) { | 
|                 if (this.cancelPromise != null) { | 
|                     if (maybePromise._isCancelled()) { | 
|                         var reason = | 
|                             new CancellationError("late cancellation observer"); | 
|                         promise._attachExtraTrace(reason); | 
|                         errorObj.e = reason; | 
|                         return errorObj; | 
|                     } else if (maybePromise.isPending()) { | 
|                         maybePromise._attachCancellationCallback( | 
|                             new FinallyHandlerCancelReaction(this)); | 
|                     } | 
|                 } | 
|                 return maybePromise._then( | 
|                     succeed, fail, undefined, this, undefined); | 
|             } | 
|         } | 
|     } | 
|   | 
|     if (promise.isRejected()) { | 
|         checkCancel(this); | 
|         errorObj.e = reasonOrValue; | 
|         return errorObj; | 
|     } else { | 
|         checkCancel(this); | 
|         return reasonOrValue; | 
|     } | 
| } | 
|   | 
| Promise.prototype._passThrough = function(handler, type, success, fail) { | 
|     if (typeof handler !== "function") return this.then(); | 
|     return this._then(success, | 
|                       fail, | 
|                       undefined, | 
|                       new PassThroughHandlerContext(this, type, handler), | 
|                       undefined); | 
| }; | 
|   | 
| Promise.prototype.lastly = | 
| Promise.prototype["finally"] = function (handler) { | 
|     return this._passThrough(handler, | 
|                              0, | 
|                              finallyHandler, | 
|                              finallyHandler); | 
| }; | 
|   | 
|   | 
| Promise.prototype.tap = function (handler) { | 
|     return this._passThrough(handler, 1, finallyHandler); | 
| }; | 
|   | 
| Promise.prototype.tapCatch = function (handlerOrPredicate) { | 
|     var len = arguments.length; | 
|     if(len === 1) { | 
|         return this._passThrough(handlerOrPredicate, | 
|                                  1, | 
|                                  undefined, | 
|                                  finallyHandler); | 
|     } else { | 
|          var catchInstances = new Array(len - 1), | 
|             j = 0, i; | 
|         for (i = 0; i < len - 1; ++i) { | 
|             var item = arguments[i]; | 
|             if (util.isObject(item)) { | 
|                 catchInstances[j++] = item; | 
|             } else { | 
|                 return Promise.reject(new TypeError( | 
|                     "tapCatch statement predicate: " | 
|                     + "expecting an object but got " + util.classString(item) | 
|                 )); | 
|             } | 
|         } | 
|         catchInstances.length = j; | 
|         var handler = arguments[i]; | 
|         return this._passThrough(catchFilter(catchInstances, handler, this), | 
|                                  1, | 
|                                  undefined, | 
|                                  finallyHandler); | 
|     } | 
|   | 
| }; | 
|   | 
| return PassThroughHandlerContext; | 
| }; |