“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 = 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; };