“use strict”; module.exports = function(Promise,

apiRejection,
INTERNAL,
tryConvertToPromise) {

var errors = require(“./errors.js”); var TypeError = errors.TypeError; var util = require(“./util.js”); var errorObj = util.errorObj; var tryCatch = util.tryCatch; var yieldHandlers = [];

function promiseFromYieldHandler(value, yieldHandlers, traceParent) {

for (var i = 0; i < yieldHandlers.length; ++i) {
    traceParent._pushContext();
    var result = tryCatch(yieldHandlers[i])(value);
    traceParent._popContext();
    if (result === errorObj) {
        traceParent._pushContext();
        var ret = Promise.reject(errorObj.e);
        traceParent._popContext();
        return ret;
    }
    var maybePromise = tryConvertToPromise(result, traceParent);
    if (maybePromise instanceof Promise) return maybePromise;
}
return null;

}

function PromiseSpawn(generatorFunction, receiver, yieldHandler, stack) {

var promise = this._promise = new Promise(INTERNAL);
promise._captureStackTrace();
this._stack = stack;
this._generatorFunction = generatorFunction;
this._receiver = receiver;
this._generator = undefined;
this._yieldHandlers = typeof yieldHandler === "function"
    ? [yieldHandler].concat(yieldHandlers)
    : yieldHandlers;

}

PromiseSpawn.prototype.promise = function () {

return this._promise;

};

PromiseSpawn.prototype._run = function () {

this._generator = this._generatorFunction.call(this._receiver);
this._receiver =
    this._generatorFunction = undefined;
this._next(undefined);

};

PromiseSpawn.prototype._continue = function (result) {

if (result === errorObj) {
    return this._promise._rejectCallback(result.e, false, true);
}

var value = result.value;
if (result.done === true) {
    this._promise._resolveCallback(value);
} else {
    var maybePromise = tryConvertToPromise(value, this._promise);
    if (!(maybePromise instanceof Promise)) {
        maybePromise =
            promiseFromYieldHandler(maybePromise,
                                    this._yieldHandlers,
                                    this._promise);
        if (maybePromise === null) {
            this._throw(
                new TypeError(
                    "A value %s was yielded that could not be treated as a promise\u000a\u000a    See http://goo.gl/4Y4pDk\u000a\u000a".replace("%s", value) +
                    "From coroutine:\u000a" +
                    this._stack.split("\n").slice(1, -7).join("\n")
                )
            );
            return;
        }
    }
    maybePromise._then(
        this._next,
        this._throw,
        undefined,
        this,
        null
   );
}

};

PromiseSpawn.prototype._throw = function (reason) {

this._promise._attachExtraTrace(reason);
this._promise._pushContext();
var result = tryCatch(this._generator["throw"])
    .call(this._generator, reason);
this._promise._popContext();
this._continue(result);

};

PromiseSpawn.prototype._next = function (value) {

this._promise._pushContext();
var result = tryCatch(this._generator.next).call(this._generator, value);
this._promise._popContext();
this._continue(result);

};

Promise.coroutine = function (generatorFunction, options) {

if (typeof generatorFunction !== "function") {
    throw new TypeError("generatorFunction must be a function\u000a\u000a    See http://goo.gl/6Vqhm0\u000a");
}
var yieldHandler = Object(options).yieldHandler;
var PromiseSpawn$ = PromiseSpawn;
var stack = new Error().stack;
return function () {
    var generator = generatorFunction.apply(this, arguments);
    var spawn = new PromiseSpawn$(undefined, undefined, yieldHandler,
                                  stack);
    spawn._generator = generator;
    spawn._next(undefined);
    return spawn.promise();
};

};

Promise.coroutine.addYieldHandler = function(fn) {

if (typeof fn !== "function") throw new TypeError("fn must be a function\u000a\u000a    See http://goo.gl/916lJJ\u000a");
yieldHandlers.push(fn);

};

Promise.spawn = function (generatorFunction) {

if (typeof generatorFunction !== "function") {
    return apiRejection("generatorFunction must be a function\u000a\u000a    See http://goo.gl/6Vqhm0\u000a");
}
var spawn = new PromiseSpawn(generatorFunction, this);
var ret = spawn.promise();
spawn._run(Promise.spawn);
return ret;

}; };