“use strict”; module.exports = function(Promise, PromiseArray, apiRejection, debug) { var util = require(“./util”); var tryCatch = util.tryCatch; var errorObj = util.errorObj; var async = Promise._async;

Promise.prototype = Promise.prototype.cancel = function() {

if (!debug.cancellation()) return this._warn("cancellation is disabled");

var promise = this;
var child = promise;
while (promise._isCancellable()) {
    if (!promise._cancelBy(child)) {
        if (child._isFollowing()) {
            child._followee().cancel();
        } else {
            child._cancelBranched();
        }
        break;
    }

    var parent = promise._cancellationParent;
    if (parent == null || !parent._isCancellable()) {
        if (promise._isFollowing()) {
            promise._followee().cancel();
        } else {
            promise._cancelBranched();
        }
        break;
    } else {
        if (promise._isFollowing()) promise._followee().cancel();
        promise._setWillBeCancelled();
        child = promise;
        promise = parent;
    }
}

};

Promise.prototype._branchHasCancelled = function() {

this._branchesRemainingToCancel--;

};

Promise.prototype._enoughBranchesHaveCancelled = function() {

return this._branchesRemainingToCancel === undefined ||
       this._branchesRemainingToCancel <= 0;

};

Promise.prototype._cancelBy = function(canceller) {

if (canceller === this) {
    this._branchesRemainingToCancel = 0;
    this._invokeOnCancel();
    return true;
} else {
    this._branchHasCancelled();
    if (this._enoughBranchesHaveCancelled()) {
        this._invokeOnCancel();
        return true;
    }
}
return false;

};

Promise.prototype._cancelBranched = function() {

if (this._enoughBranchesHaveCancelled()) {
    this._cancel();
}

};

Promise.prototype._cancel = function() {

if (!this._isCancellable()) return;
this._setCancelled();
async.invoke(this._cancelPromises, this, undefined);

};

Promise.prototype._cancelPromises = function() {

if (this._length() > 0) this._settlePromises();

};

Promise.prototype._unsetOnCancel = function() {

this._onCancelField = undefined;

};

Promise.prototype._isCancellable = function() {

return this.isPending() && !this._isCancelled();

};

Promise.prototype.isCancellable = function() {

return this.isPending() && !this.isCancelled();

};

Promise.prototype._doInvokeOnCancel = function(onCancelCallback, internalOnly) {

if (util.isArray(onCancelCallback)) {
    for (var i = 0; i < onCancelCallback.length; ++i) {
        this._doInvokeOnCancel(onCancelCallback[i], internalOnly);
    }
} else if (onCancelCallback !== undefined) {
    if (typeof onCancelCallback === "function") {
        if (!internalOnly) {
            var e = tryCatch(onCancelCallback).call(this._boundValue());
            if (e === errorObj) {
                this._attachExtraTrace(e.e);
                async.throwLater(e.e);
            }
        }
    } else {
        onCancelCallback._resultCancelled(this);
    }
}

};

Promise.prototype._invokeOnCancel = function() {

var onCancelCallback = this._onCancel();
this._unsetOnCancel();
async.invoke(this._doInvokeOnCancel, this, onCancelCallback);

};

Promise.prototype._invokeInternalOnCancel = function() {

if (this._isCancellable()) {
    this._doInvokeOnCancel(this._onCancel(), true);
    this._unsetOnCancel();
}

};

Promise.prototype._resultCancelled = function() {

this.cancel();

};

};