| "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["break"] = 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(); | 
| }; | 
|   | 
| }; |