/*!

* @overview RSVP - a tiny implementation of Promises/A+.
* @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors
* @license   Licensed under MIT license
*            See https://raw.githubusercontent.com/tildeio/rsvp.js/master/LICENSE
* @version   3.0.14
*/

(function() {

"use strict";

function $$rsvp$events$$indexOf(callbacks, callback) {
  for (var i=0, l=callbacks.length; i<l; i++) {
    if (callbacks[i] === callback) { return i; }
  }

  return -1;
}

function $$rsvp$events$$callbacksFor(object) {
  var callbacks = object._promiseCallbacks;

  if (!callbacks) {
    callbacks = object._promiseCallbacks = {};
  }

  return callbacks;
}

var $$rsvp$events$$default = {

  /**
    `RSVP.EventTarget.mixin` extends an object with EventTarget methods. For
    Example:

    ```javascript
    var object = {};

    RSVP.EventTarget.mixin(object);

    object.on('finished', function(event) {
      // handle event
    });

    object.trigger('finished', { detail: value });
    ```

    `EventTarget.mixin` also works with prototypes:

    ```javascript
    var Person = function() {};
    RSVP.EventTarget.mixin(Person.prototype);

    var yehuda = new Person();
    var tom = new Person();

    yehuda.on('poke', function(event) {
      console.log('Yehuda says OW');
    });

    tom.on('poke', function(event) {
      console.log('Tom says OW');
    });

    yehuda.trigger('poke');
    tom.trigger('poke');
    ```

    @method mixin
    @for RSVP.EventTarget
    @private
    @param {Object} object object to extend with EventTarget methods
  */
  mixin: function(object) {
    object.on = this.on;
    object.off = this.off;
    object.trigger = this.trigger;
    object._promiseCallbacks = undefined;
    return object;
  },

  /**
    Registers a callback to be executed when `eventName` is triggered

    ```javascript
    object.on('event', function(eventInfo){
      // handle the event
    });

    object.trigger('event');
    ```

    @method on
    @for RSVP.EventTarget
    @private
    @param {String} eventName name of the event to listen for
    @param {Function} callback function to be called when the event is triggered.
  */
  on: function(eventName, callback) {
    var allCallbacks = $$rsvp$events$$callbacksFor(this), callbacks;

    callbacks = allCallbacks[eventName];

    if (!callbacks) {
      callbacks = allCallbacks[eventName] = [];
    }

    if ($$rsvp$events$$indexOf(callbacks, callback) === -1) {
      callbacks.push(callback);
    }
  },

  /**
    You can use `off` to stop firing a particular callback for an event:

    ```javascript
    function doStuff() { // do stuff! }
    object.on('stuff', doStuff);

    object.trigger('stuff'); // doStuff will be called

    // Unregister ONLY the doStuff callback
    object.off('stuff', doStuff);
    object.trigger('stuff'); // doStuff will NOT be called
    ```

    If you don't pass a `callback` argument to `off`, ALL callbacks for the
    event will not be executed when the event fires. For example:

    ```javascript
    var callback1 = function(){};
    var callback2 = function(){};

    object.on('stuff', callback1);
    object.on('stuff', callback2);

    object.trigger('stuff'); // callback1 and callback2 will be executed.

    object.off('stuff');
    object.trigger('stuff'); // callback1 and callback2 will not be executed!
    ```

    @method off
    @for RSVP.EventTarget
    @private
    @param {String} eventName event to stop listening to
    @param {Function} callback optional argument. If given, only the function
    given will be removed from the event's callback queue. If no `callback`
    argument is given, all callbacks will be removed from the event's callback
    queue.
  */
  off: function(eventName, callback) {
    var allCallbacks = $$rsvp$events$$callbacksFor(this), callbacks, index;

    if (!callback) {
      allCallbacks[eventName] = [];
      return;
    }

    callbacks = allCallbacks[eventName];

    index = $$rsvp$events$$indexOf(callbacks, callback);

    if (index !== -1) { callbacks.splice(index, 1); }
  },

  /**
    Use `trigger` to fire custom events. For example:

    ```javascript
    object.on('foo', function(){
      console.log('foo event happened!');
    });
    object.trigger('foo');
    // 'foo event happened!' logged to the console
    ```

    You can also pass a value as a second argument to `trigger` that will be
    passed as an argument to all event listeners for the event:

    ```javascript
    object.on('foo', function(value){
      console.log(value.name);
    });

    object.trigger('foo', { name: 'bar' });
    // 'bar' logged to the console
    ```

    @method trigger
    @for RSVP.EventTarget
    @private
    @param {String} eventName name of the event to be triggered
    @param {Any} options optional value to be passed to any event handlers for
    the given `eventName`
  */
  trigger: function(eventName, options) {
    var allCallbacks = $$rsvp$events$$callbacksFor(this), callbacks, callback;

    if (callbacks = allCallbacks[eventName]) {
      // Don't cache the callbacks.length since it may grow
      for (var i=0; i<callbacks.length; i++) {
        callback = callbacks[i];

        callback(options);
      }
    }
  }
};

var $$rsvp$config$$config = {
  instrument: false
};

$$rsvp$events$$default.mixin($$rsvp$config$$config);

function $$rsvp$config$$configure(name, value) {
  if (name === 'onerror') {
    // handle for legacy users that expect the actual
    // error to be passed to their function added via
    // `RSVP.configure('onerror', someFunctionHere);`
    $$rsvp$config$$config.on('error', value);
    return;
  }

  if (arguments.length === 2) {
    $$rsvp$config$$config[name] = value;
  } else {
    return $$rsvp$config$$config[name];
  }
}

function $$utils$$objectOrFunction(x) {
  return typeof x === 'function' || (typeof x === 'object' && x !== null);
}

function $$utils$$isFunction(x) {
  return typeof x === 'function';
}

function $$utils$$isMaybeThenable(x) {
  return typeof x === 'object' && x !== null;
}

var $$utils$$_isArray;

if (!Array.isArray) {
  $$utils$$_isArray = function (x) {
    return Object.prototype.toString.call(x) === '[object Array]';
  };
} else {
  $$utils$$_isArray = Array.isArray;
}

var $$utils$$isArray = $$utils$$_isArray;
var $$utils$$now = Date.now || function() { return new Date().getTime(); };
function $$utils$$F() { }

var $$utils$$o_create = (Object.create || function (o) {
  if (arguments.length > 1) {
    throw new Error('Second argument not supported');
  }
  if (typeof o !== 'object') {
    throw new TypeError('Argument must be an object');
  }
  $$utils$$F.prototype = o;
  return new $$utils$$F();
});

var $$instrument$$queue = [];

var $$instrument$$default = function instrument(eventName, promise, child) {
  if (1 === $$instrument$$queue.push({
      name: eventName,
      payload: {
        guid: promise._guidKey + promise._id,
        eventName: eventName,
        detail: promise._result,
        childGuid: child && promise._guidKey + child._id,
        label: promise._label,
        timeStamp: $$utils$$now(),
        stack: new Error(promise._label).stack
      }})) {

        setTimeout(function() {
          var entry;
          for (var i = 0; i < $$instrument$$queue.length; i++) {
            entry = $$instrument$$queue[i];
            $$rsvp$config$$config.trigger(entry.name, entry.payload);
          }
          $$instrument$$queue.length = 0;
        }, 50);
      }
  };

function $$$internal$$noop() {}
var $$$internal$$PENDING   = void 0;
var $$$internal$$FULFILLED = 1;
var $$$internal$$REJECTED  = 2;
var $$$internal$$GET_THEN_ERROR = new $$$internal$$ErrorObject();

function $$$internal$$getThen(promise) {
  try {
    return promise.then;
  } catch(error) {
    $$$internal$$GET_THEN_ERROR.error = error;
    return $$$internal$$GET_THEN_ERROR;
  }
}

function $$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) {
  try {
    then.call(value, fulfillmentHandler, rejectionHandler);
  } catch(e) {
    return e;
  }
}

function $$$internal$$handleForeignThenable(promise, thenable, then) {
  $$rsvp$config$$config.async(function(promise) {
    var sealed = false;
    var error = $$$internal$$tryThen(then, thenable, function(value) {
      if (sealed) { return; }
      sealed = true;
      if (thenable !== value) {
        $$$internal$$resolve(promise, value);
      } else {
        $$$internal$$fulfill(promise, value);
      }
    }, function(reason) {
      if (sealed) { return; }
      sealed = true;

      $$$internal$$reject(promise, reason);
    }, 'Settle: ' + (promise._label || ' unknown promise'));

    if (!sealed && error) {
      sealed = true;
      $$$internal$$reject(promise, error);
    }
  }, promise);
}

function $$$internal$$handleOwnThenable(promise, thenable) {
  if (thenable._state === $$$internal$$FULFILLED) {
    $$$internal$$fulfill(promise, thenable._result);
  } else if (promise._state === $$$internal$$REJECTED) {
    $$$internal$$reject(promise, thenable._result);
  } else {
    $$$internal$$subscribe(thenable, undefined, function(value) {
      if (thenable !== value) {
        $$$internal$$resolve(promise, value);
      } else {
        $$$internal$$fulfill(promise, value);
      }
    }, function(reason) {
      $$$internal$$reject(promise, reason);
    });
  }
}

function $$$internal$$handleMaybeThenable(promise, maybeThenable) {
  if (maybeThenable.constructor === promise.constructor) {
    $$$internal$$handleOwnThenable(promise, maybeThenable);
  } else {
    var then = $$$internal$$getThen(maybeThenable);

    if (then === $$$internal$$GET_THEN_ERROR) {
      $$$internal$$reject(promise, $$$internal$$GET_THEN_ERROR.error);
    } else if (then === undefined) {
      $$$internal$$fulfill(promise, maybeThenable);
    } else if ($$utils$$isFunction(then)) {
      $$$internal$$handleForeignThenable(promise, maybeThenable, then);
    } else {
      $$$internal$$fulfill(promise, maybeThenable);
    }
  }
}

function $$$internal$$resolve(promise, value) {
  if (promise === value) {
    $$$internal$$fulfill(promise, value);
  } else if ($$utils$$objectOrFunction(value)) {
    $$$internal$$handleMaybeThenable(promise, value);
  } else {
    $$$internal$$fulfill(promise, value);
  }
}

function $$$internal$$publishRejection(promise) {
  if (promise._onerror) {
    promise._onerror(promise._result);
  }

  $$$internal$$publish(promise);
}

function $$$internal$$fulfill(promise, value) {
  if (promise._state !== $$$internal$$PENDING) { return; }

  promise._result = value;
  promise._state = $$$internal$$FULFILLED;

  if (promise._subscribers.length === 0) {
    if ($$rsvp$config$$config.instrument) {
      $$instrument$$default('fulfilled', promise);
    }
  } else {
    $$rsvp$config$$config.async($$$internal$$publish, promise);
  }
}

function $$$internal$$reject(promise, reason) {
  if (promise._state !== $$$internal$$PENDING) { return; }
  promise._state = $$$internal$$REJECTED;
  promise._result = reason;

  $$rsvp$config$$config.async($$$internal$$publishRejection, promise);
}

function $$$internal$$subscribe(parent, child, onFulfillment, onRejection) {
  var subscribers = parent._subscribers;
  var length = subscribers.length;

  parent._onerror = null;

  subscribers[length] = child;
  subscribers[length + $$$internal$$FULFILLED] = onFulfillment;
  subscribers[length + $$$internal$$REJECTED]  = onRejection;

  if (length === 0 && parent._state) {
    $$rsvp$config$$config.async($$$internal$$publish, parent);
  }
}

function $$$internal$$publish(promise) {
  var subscribers = promise._subscribers;
  var settled = promise._state;

  if ($$rsvp$config$$config.instrument) {
    $$instrument$$default(settled === $$$internal$$FULFILLED ? 'fulfilled' : 'rejected', promise);
  }

  if (subscribers.length === 0) { return; }

  var child, callback, detail = promise._result;

  for (var i = 0; i < subscribers.length; i += 3) {
    child = subscribers[i];
    callback = subscribers[i + settled];

    if (child) {
      $$$internal$$invokeCallback(settled, child, callback, detail);
    } else {
      callback(detail);
    }
  }

  promise._subscribers.length = 0;
}

function $$$internal$$ErrorObject() {
  this.error = null;
}

var $$$internal$$TRY_CATCH_ERROR = new $$$internal$$ErrorObject();

function $$$internal$$tryCatch(callback, detail) {
  try {
    return callback(detail);
  } catch(e) {
    $$$internal$$TRY_CATCH_ERROR.error = e;
    return $$$internal$$TRY_CATCH_ERROR;
  }
}

function $$$internal$$invokeCallback(settled, promise, callback, detail) {
  var hasCallback = $$utils$$isFunction(callback),
      value, error, succeeded, failed;

  if (hasCallback) {
    value = $$$internal$$tryCatch(callback, detail);

    if (value === $$$internal$$TRY_CATCH_ERROR) {
      failed = true;
      error = value.error;
      value = null;
    } else {
      succeeded = true;
    }

    if (promise === value) {
      $$$internal$$reject(promise, new TypeError('A promises callback cannot return that same promise.'));
      return;
    }

  } else {
    value = detail;
    succeeded = true;
  }

  if (promise._state !== $$$internal$$PENDING) {
    // noop
  } else if (hasCallback && succeeded) {
    $$$internal$$resolve(promise, value);
  } else if (failed) {
    $$$internal$$reject(promise, error);
  } else if (settled === $$$internal$$FULFILLED) {
    $$$internal$$fulfill(promise, value);
  } else if (settled === $$$internal$$REJECTED) {
    $$$internal$$reject(promise, value);
  }
}

function $$$internal$$initializePromise(promise, resolver) {
  try {
    resolver(function resolvePromise(value){
      $$$internal$$resolve(promise, value);
    }, function rejectPromise(reason) {
      $$$internal$$reject(promise, reason);
    });
  } catch(e) {
    $$$internal$$reject(promise, e);
  }
}

function $$enumerator$$makeSettledResult(state, position, value) {
  if (state === $$$internal$$FULFILLED) {
    return {
      state: 'fulfilled',
      value: value
    };
  } else {
    return {
      state: 'rejected',
      reason: value
    };
  }
}

function $$enumerator$$Enumerator(Constructor, input, abortOnReject, label) {
  this._instanceConstructor = Constructor;
  this.promise = new Constructor($$$internal$$noop, label);
  this._abortOnReject = abortOnReject;

  if (this._validateInput(input)) {
    this._input     = input;
    this.length     = input.length;
    this._remaining = input.length;

    this._init();

    if (this.length === 0) {
      $$$internal$$fulfill(this.promise, this._result);
    } else {
      this.length = this.length || 0;
      this._enumerate();
      if (this._remaining === 0) {
        $$$internal$$fulfill(this.promise, this._result);
      }
    }
  } else {
    $$$internal$$reject(this.promise, this._validationError());
  }
}

$$enumerator$$Enumerator.prototype._validateInput = function(input) {
  return $$utils$$isArray(input);
};

$$enumerator$$Enumerator.prototype._validationError = function() {
  return new Error('Array Methods must be provided an Array');
};

$$enumerator$$Enumerator.prototype._init = function() {
  this._result = new Array(this.length);
};

var $$enumerator$$default = $$enumerator$$Enumerator;

$$enumerator$$Enumerator.prototype._enumerate = function() {
  var length  = this.length;
  var promise = this.promise;
  var input   = this._input;

  for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) {
    this._eachEntry(input[i], i);
  }
};

$$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) {
  var c = this._instanceConstructor;
  if ($$utils$$isMaybeThenable(entry)) {
    if (entry.constructor === c && entry._state !== $$$internal$$PENDING) {
      entry._onerror = null;
      this._settledAt(entry._state, i, entry._result);
    } else {
      this._willSettleAt(c.resolve(entry), i);
    }
  } else {
    this._remaining--;
    this._result[i] = this._makeResult($$$internal$$FULFILLED, i, entry);
  }
};

$$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) {
  var promise = this.promise;

  if (promise._state === $$$internal$$PENDING) {
    this._remaining--;

    if (this._abortOnReject && state === $$$internal$$REJECTED) {
      $$$internal$$reject(promise, value);
    } else {
      this._result[i] = this._makeResult(state, i, value);
    }
  }

  if (this._remaining === 0) {
    $$$internal$$fulfill(promise, this._result);
  }
};

$$enumerator$$Enumerator.prototype._makeResult = function(state, i, value) {
  return value;
};

$$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) {
  var enumerator = this;

  $$$internal$$subscribe(promise, undefined, function(value) {
    enumerator._settledAt($$$internal$$FULFILLED, i, value);
  }, function(reason) {
    enumerator._settledAt($$$internal$$REJECTED, i, reason);
  });
};

var $$promise$all$$default = function all(entries, label) {
  return new $$enumerator$$default(this, entries, true /* abort on reject */, label).promise;
};

var $$promise$race$$default = function race(entries, label) {
  /*jshint validthis:true */
  var Constructor = this;

  var promise = new Constructor($$$internal$$noop, label);

  if (!$$utils$$isArray(entries)) {
    $$$internal$$reject(promise, new TypeError('You must pass an array to race.'));
    return promise;
  }

  var length = entries.length;

  function onFulfillment(value) {
    $$$internal$$resolve(promise, value);
  }

  function onRejection(reason) {
    $$$internal$$reject(promise, reason);
  }

  for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) {
    $$$internal$$subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection);
  }

  return promise;
};

var $$promise$resolve$$default = function resolve(object, label) {
  /*jshint validthis:true */
  var Constructor = this;

  if (object && typeof object === 'object' && object.constructor === Constructor) {
    return object;
  }

  var promise = new Constructor($$$internal$$noop, label);
  $$$internal$$resolve(promise, object);
  return promise;
};

var $$promise$reject$$default = function reject(reason, label) {
  /*jshint validthis:true */
  var Constructor = this;
  var promise = new Constructor($$$internal$$noop, label);
  $$$internal$$reject(promise, reason);
  return promise;
};

var $$rsvp$promise$$guidKey = 'rsvp_' + $$utils$$now() + '-';
var $$rsvp$promise$$counter = 0;

function $$rsvp$promise$$needsResolver() {
  throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
}

function $$rsvp$promise$$needsNew() {
  throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
}

var $$rsvp$promise$$default = $$rsvp$promise$$Promise;

/**
  Promise objects represent the eventual result of an asynchronous operation. The
  primary way of interacting with a promise is through its `then` method, which
  registers callbacks to receive either a promise’s eventual value or the reason
  why the promise cannot be fulfilled.

  Terminology
  -----------

  - `promise` is an object or function with a `then` method whose behavior conforms to this specification.
  - `thenable` is an object or function that defines a `then` method.
  - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
  - `exception` is a value that is thrown using the throw statement.
  - `reason` is a value that indicates why a promise was rejected.
  - `settled` the final resting state of a promise, fulfilled or rejected.

  A promise can be in one of three states: pending, fulfilled, or rejected.

  Promises that are fulfilled have a fulfillment value and are in the fulfilled
  state.  Promises that are rejected have a rejection reason and are in the
  rejected state.  A fulfillment value is never a thenable.

  Promises can also be said to *resolve* a value.  If this value is also a
  promise, then the original promise's settled state will match the value's
  settled state.  So a promise that *resolves* a promise that rejects will
  itself reject, and a promise that *resolves* a promise that fulfills will
  itself fulfill.

  Basic Usage:
  ------------

  ```js
  var promise = new Promise(function(resolve, reject) {
    // on success
    resolve(value);

    // on failure
    reject(reason);
  });

  promise.then(function(value) {
    // on fulfillment
  }, function(reason) {
    // on rejection
  });
  ```

  Advanced Usage:
  ---------------

  Promises shine when abstracting away asynchronous interactions such as
  `XMLHttpRequest`s.

  ```js
  function getJSON(url) {
    return new Promise(function(resolve, reject){
      var xhr = new XMLHttpRequest();

      xhr.open('GET', url);
      xhr.onreadystatechange = handler;
      xhr.responseType = 'json';
      xhr.setRequestHeader('Accept', 'application/json');
      xhr.send();

      function handler() {
        if (this.readyState === this.DONE) {
          if (this.status === 200) {
            resolve(this.response);
          } else {
            reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
          }
        }
      };
    });
  }

  getJSON('/posts.json').then(function(json) {
    // on fulfillment
  }, function(reason) {
    // on rejection
  });
  ```

  Unlike callbacks, promises are great composable primitives.

  ```js
  Promise.all([
    getJSON('/posts'),
    getJSON('/comments')
  ]).then(function(values){
    values[0] // => postsJSON
    values[1] // => commentsJSON

    return values;
  });
  ```

  @class RSVP.Promise
  @param {function} resolver
  @param {String} label optional string for labeling the promise.
  Useful for tooling.
  @constructor
*/
function $$rsvp$promise$$Promise(resolver, label) {
  this._id = $$rsvp$promise$$counter++;
  this._label = label;
  this._state = undefined;
  this._result = undefined;
  this._subscribers = [];

  if ($$rsvp$config$$config.instrument) {
    $$instrument$$default('created', this);
  }

  if ($$$internal$$noop !== resolver) {
    if (!$$utils$$isFunction(resolver)) {
      $$rsvp$promise$$needsResolver();
    }

    if (!(this instanceof $$rsvp$promise$$Promise)) {
      $$rsvp$promise$$needsNew();
    }

    $$$internal$$initializePromise(this, resolver);
  }
}

// deprecated
$$rsvp$promise$$Promise.cast = $$promise$resolve$$default;

$$rsvp$promise$$Promise.all = $$promise$all$$default;
$$rsvp$promise$$Promise.race = $$promise$race$$default;
$$rsvp$promise$$Promise.resolve = $$promise$resolve$$default;
$$rsvp$promise$$Promise.reject = $$promise$reject$$default;

$$rsvp$promise$$Promise.prototype = {
  constructor: $$rsvp$promise$$Promise,

  _guidKey: $$rsvp$promise$$guidKey,

  _onerror: function (reason) {
    $$rsvp$config$$config.trigger('error', reason);
  },

/**
  The primary way of interacting with a promise is through its `then` method,
  which registers callbacks to receive either a promise's eventual value or the
  reason why the promise cannot be fulfilled.

  ```js
  findUser().then(function(user){
    // user is available
  }, function(reason){
    // user is unavailable, and you are given the reason why
  });
  ```

  Chaining
  --------

  The return value of `then` is itself a promise.  This second, 'downstream'
  promise is resolved with the return value of the first promise's fulfillment
  or rejection handler, or rejected if the handler throws an exception.

  ```js
  findUser().then(function (user) {
    return user.name;
  }, function (reason) {
    return 'default name';
  }).then(function (userName) {
    // If `findUser` fulfilled, `userName` will be the user's name, otherwise it
    // will be `'default name'`
  });

  findUser().then(function (user) {
    throw new Error('Found user, but still unhappy');
  }, function (reason) {
    throw new Error('`findUser` rejected and we're unhappy');
  }).then(function (value) {
    // never reached
  }, function (reason) {
    // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
    // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
  });
  ```
  If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.

  ```js
  findUser().then(function (user) {
    throw new PedagogicalException('Upstream error');
  }).then(function (value) {
    // never reached
  }).then(function (value) {
    // never reached
  }, function (reason) {
    // The `PedgagocialException` is propagated all the way down to here
  });
  ```

  Assimilation
  ------------

  Sometimes the value you want to propagate to a downstream promise can only be
  retrieved asynchronously. This can be achieved by returning a promise in the
  fulfillment or rejection handler. The downstream promise will then be pending
  until the returned promise is settled. This is called *assimilation*.

  ```js
  findUser().then(function (user) {
    return findCommentsByAuthor(user);
  }).then(function (comments) {
    // The user's comments are now available
  });
  ```

  If the assimliated promise rejects, then the downstream promise will also reject.

  ```js
  findUser().then(function (user) {
    return findCommentsByAuthor(user);
  }).then(function (comments) {
    // If `findCommentsByAuthor` fulfills, we'll have the value here
  }, function (reason) {
    // If `findCommentsByAuthor` rejects, we'll have the reason here
  });
  ```

  Simple Example
  --------------

  Synchronous Example

  ```javascript
  var result;

  try {
    result = findResult();
    // success
  } catch(reason) {
    // failure
  }
  ```

  Errback Example

  ```js
  findResult(function(result, err){
    if (err) {
      // failure
    } else {
      // success
    }
  });
  ```

  Promise Example;

  ```javascript
  findResult().then(function(result){
    // success
  }, function(reason){
    // failure
  });
  ```

  Advanced Example
  --------------

  Synchronous Example

  ```javascript
  var author, books;

  try {
    author = findAuthor();
    books  = findBooksByAuthor(author);
    // success
  } catch(reason) {
    // failure
  }
  ```

  Errback Example

  ```js

  function foundBooks(books) {

  }

  function failure(reason) {

  }

  findAuthor(function(author, err){
    if (err) {
      failure(err);
      // failure
    } else {
      try {
        findBoooksByAuthor(author, function(books, err) {
          if (err) {
            failure(err);
          } else {
            try {
              foundBooks(books);
            } catch(reason) {
              failure(reason);
            }
          }
        });
      } catch(error) {
        failure(err);
      }
      // success
    }
  });
  ```

  Promise Example;

  ```javascript
  findAuthor().
    then(findBooksByAuthor).
    then(function(books){
      // found books
  }).catch(function(reason){
    // something went wrong
  });
  ```

  @method then
  @param {Function} onFulfilled
  @param {Function} onRejected
  @param {String} label optional string for labeling the promise.
  Useful for tooling.
  @return {Promise}
*/
  then: function(onFulfillment, onRejection, label) {
    var parent = this;
    var state = parent._state;

    if (state === $$$internal$$FULFILLED && !onFulfillment || state === $$$internal$$REJECTED && !onRejection) {
      if ($$rsvp$config$$config.instrument) {
        $$instrument$$default('chained', this, this);
      }
      return this;
    }

    parent._onerror = null;

    var child = new this.constructor($$$internal$$noop, label);
    var result = parent._result;

    if ($$rsvp$config$$config.instrument) {
      $$instrument$$default('chained', parent, child);
    }

    if (state) {
      var callback = arguments[state - 1];
      $$rsvp$config$$config.async(function(){
        $$$internal$$invokeCallback(state, child, callback, result);
      });
    } else {
      $$$internal$$subscribe(parent, child, onFulfillment, onRejection);
    }

    return child;
  },

/**
  `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
  as the catch block of a try/catch statement.

  ```js
  function findAuthor(){
    throw new Error('couldn't find that author');
  }

  // synchronous
  try {
    findAuthor();
  } catch(reason) {
    // something went wrong
  }

  // async with promises
  findAuthor().catch(function(reason){
    // something went wrong
  });
  ```

  @method catch
  @param {Function} onRejection
  @param {String} label optional string for labeling the promise.
  Useful for tooling.
  @return {Promise}
*/
  'catch': function(onRejection, label) {
    return this.then(null, onRejection, label);
  },

/**
  `finally` will be invoked regardless of the promise's fate just as native
  try/catch/finally behaves

  Synchronous example:

  ```js
  findAuthor() {
    if (Math.random() > 0.5) {
      throw new Error();
    }
    return new Author();
  }

  try {
    return findAuthor(); // succeed or fail
  } catch(error) {
    return findOtherAuther();
  } finally {
    // always runs
    // doesn't affect the return value
  }
  ```

  Asynchronous example:

  ```js
  findAuthor().catch(function(reason){
    return findOtherAuther();
  }).finally(function(){
    // author was either found, or not
  });
  ```

  @method finally
  @param {Function} callback
  @param {String} label optional string for labeling the promise.
  Useful for tooling.
  @return {Promise}
*/
  'finally': function(callback, label) {
    var constructor = this.constructor;

    return this.then(function(value) {
      return constructor.resolve(callback()).then(function(){
        return value;
      });
    }, function(reason) {
      return constructor.resolve(callback()).then(function(){
        throw reason;
      });
    }, label);
  }
};

function $$rsvp$node$$Result() {
  this.value = undefined;
}

var $$rsvp$node$$ERROR = new $$rsvp$node$$Result();
var $$rsvp$node$$GET_THEN_ERROR = new $$rsvp$node$$Result();

function $$rsvp$node$$getThen(obj) {
  try {
   return obj.then;
  } catch(error) {
    $$rsvp$node$$ERROR.value= error;
    return $$rsvp$node$$ERROR;
  }
}

function $$rsvp$node$$tryApply(f, s, a) {
  try {
    f.apply(s, a);
  } catch(error) {
    $$rsvp$node$$ERROR.value = error;
    return $$rsvp$node$$ERROR;
  }
}

function $$rsvp$node$$makeObject(_, argumentNames) {
  var obj = {};
  var name;
  var i;
  var length = _.length;
  var args = new Array(length);

  for (var x = 0; x < length; x++) {
    args[x] = _[x];
  }

  for (i = 0; i < argumentNames.length; i++) {
    name = argumentNames[i];
    obj[name] = args[i + 1];
  }

  return obj;
}

function $$rsvp$node$$arrayResult(_) {
  var length = _.length;
  var args = new Array(length - 1);

  for (var i = 1; i < length; i++) {
    args[i - 1] = _[i];
  }

  return args;
}

function $$rsvp$node$$wrapThenable(then, promise) {
  return {
    then: function(onFulFillment, onRejection) {
      return then.call(promise, onFulFillment, onRejection);
    }
  };
}

var $$rsvp$node$$default = function denodeify(nodeFunc, options) {
  var fn = function() {
    var self = this;
    var l = arguments.length;
    var args = new Array(l + 1);
    var arg;
    var promiseInput = false;

    for (var i = 0; i < l; ++i) {
      arg = arguments[i];

      if (!promiseInput) {
        // TODO: clean this up
        promiseInput = $$rsvp$node$$needsPromiseInput(arg);
        if (promiseInput === $$rsvp$node$$GET_THEN_ERROR) {
          var p = new $$rsvp$promise$$default($$$internal$$noop);
          $$$internal$$reject(p, $$rsvp$node$$GET_THEN_ERROR.value);
          return p;
        } else if (promiseInput && promiseInput !== true) {
          arg = $$rsvp$node$$wrapThenable(promiseInput, arg);
        }
      }
      args[i] = arg;
    }

    var promise = new $$rsvp$promise$$default($$$internal$$noop);

    args[l] = function(err, val) {
      if (err)
        $$$internal$$reject(promise, err);
      else if (options === undefined)
        $$$internal$$resolve(promise, val);
      else if (options === true)
        $$$internal$$resolve(promise, $$rsvp$node$$arrayResult(arguments));
      else if ($$utils$$isArray(options))
        $$$internal$$resolve(promise, $$rsvp$node$$makeObject(arguments, options));
      else
        $$$internal$$resolve(promise, val);
    };

    if (promiseInput) {
      return $$rsvp$node$$handlePromiseInput(promise, args, nodeFunc, self);
    } else {
      return $$rsvp$node$$handleValueInput(promise, args, nodeFunc, self);
    }
  };

  fn.__proto__ = nodeFunc;

  return fn;
};

function $$rsvp$node$$handleValueInput(promise, args, nodeFunc, self) {
  var result = $$rsvp$node$$tryApply(nodeFunc, self, args);
  if (result === $$rsvp$node$$ERROR) {
    $$$internal$$reject(promise, result.value);
  }
  return promise;
}

function $$rsvp$node$$handlePromiseInput(promise, args, nodeFunc, self){
  return $$rsvp$promise$$default.all(args).then(function(args){
    var result = $$rsvp$node$$tryApply(nodeFunc, self, args);
    if (result === $$rsvp$node$$ERROR) {
      $$$internal$$reject(promise, result.value);
    }
    return promise;
  });
}

function $$rsvp$node$$needsPromiseInput(arg) {
  if (arg && typeof arg === 'object') {
    if (arg.constructor === $$rsvp$promise$$default) {
      return true;
    } else {
      return $$rsvp$node$$getThen(arg);
    }
  } else {
    return false;
  }
}

var $$rsvp$all$$default = function all(array, label) {
  return $$rsvp$promise$$default.all(array, label);
};

function $$rsvp$all$settled$$AllSettled(Constructor, entries, label) {
  this._superConstructor(Constructor, entries, false /* don't abort on reject */, label);
}

$$rsvp$all$settled$$AllSettled.prototype = $$utils$$o_create($$enumerator$$default.prototype);
$$rsvp$all$settled$$AllSettled.prototype._superConstructor = $$enumerator$$default;
$$rsvp$all$settled$$AllSettled.prototype._makeResult = $$enumerator$$makeSettledResult;

$$rsvp$all$settled$$AllSettled.prototype._validationError = function() {
  return new Error('allSettled must be called with an array');
};

var $$rsvp$all$settled$$default = function allSettled(entries, label) {
  return new $$rsvp$all$settled$$AllSettled($$rsvp$promise$$default, entries, label).promise;
};

var $$rsvp$race$$default = function race(array, label) {
  return $$rsvp$promise$$default.race(array, label);
};

function $$promise$hash$$PromiseHash(Constructor, object, label) {
  this._superConstructor(Constructor, object, true, label);
}

var $$promise$hash$$default = $$promise$hash$$PromiseHash;
$$promise$hash$$PromiseHash.prototype = $$utils$$o_create($$enumerator$$default.prototype);
$$promise$hash$$PromiseHash.prototype._superConstructor = $$enumerator$$default;

$$promise$hash$$PromiseHash.prototype._init = function() {
  this._result = {};
};

$$promise$hash$$PromiseHash.prototype._validateInput = function(input) {
  return input && typeof input === 'object';
};

$$promise$hash$$PromiseHash.prototype._validationError = function() {
  return new Error('Promise.hash must be called with an object');
};

$$promise$hash$$PromiseHash.prototype._enumerate = function() {
  var promise = this.promise;
  var input   = this._input;
  var results = [];

  for (var key in input) {
    if (promise._state === $$$internal$$PENDING && input.hasOwnProperty(key)) {
      results.push({
        position: key,
        entry: input[key]
      });
    }
  }

  var length = results.length;
  this._remaining = length;
  var result;

  for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) {
    result = results[i];
    this._eachEntry(result.entry, result.position);
  }
};

var $$rsvp$hash$$default = function hash(object, label) {
  return new $$promise$hash$$default($$rsvp$promise$$default, object, label).promise;
};

function $$rsvp$hash$settled$$HashSettled(Constructor, object, label) {
  this._superConstructor(Constructor, object, false, label);
}

$$rsvp$hash$settled$$HashSettled.prototype = $$utils$$o_create($$promise$hash$$default.prototype);
$$rsvp$hash$settled$$HashSettled.prototype._superConstructor = $$enumerator$$default;
$$rsvp$hash$settled$$HashSettled.prototype._makeResult = $$enumerator$$makeSettledResult;

$$rsvp$hash$settled$$HashSettled.prototype._validationError = function() {
  return new Error('hashSettled must be called with an object');
};

var $$rsvp$hash$settled$$default = function hashSettled(object, label) {
  return new $$rsvp$hash$settled$$HashSettled($$rsvp$promise$$default, object, label).promise;
};

var $$rsvp$rethrow$$default = function rethrow(reason) {
  setTimeout(function() {
    throw reason;
  });
  throw reason;
};

var $$rsvp$defer$$default = function defer(label) {
  var deferred = { };

  deferred.promise = new $$rsvp$promise$$default(function(resolve, reject) {
    deferred.resolve = resolve;
    deferred.reject = reject;
  }, label);

  return deferred;
};

var $$rsvp$map$$default = function map(promises, mapFn, label) {
  return $$rsvp$promise$$default.all(promises, label).then(function(values) {
    if (!$$utils$$isFunction(mapFn)) {
      throw new TypeError("You must pass a function as map's second argument.");
    }

    var length = values.length;
    var results = new Array(length);

    for (var i = 0; i < length; i++) {
      results[i] = mapFn(values[i]);
    }

    return $$rsvp$promise$$default.all(results, label);
  });
};

var $$rsvp$resolve$$default = function resolve(value, label) {
  return $$rsvp$promise$$default.resolve(value, label);
};

var $$rsvp$reject$$default = function reject(reason, label) {
  return $$rsvp$promise$$default.reject(reason, label);
};

var $$rsvp$filter$$default = function filter(promises, filterFn, label) {
  return $$rsvp$promise$$default.all(promises, label).then(function(values) {
    if (!$$utils$$isFunction(filterFn)) {
      throw new TypeError("You must pass a function as filter's second argument.");
    }

    var length = values.length;
    var filtered = new Array(length);

    for (var i = 0; i < length; i++) {
      filtered[i] = filterFn(values[i]);
    }

    return $$rsvp$promise$$default.all(filtered, label).then(function(filtered) {
      var results = new Array(length);
      var newLength = 0;

      for (var i = 0; i < length; i++) {
        if (filtered[i]) {
          results[newLength] = values[i];
          newLength++;
        }
      }

      results.length = newLength;

      return results;
    });
  });
};

var $$rsvp$asap$$len = 0;

var $$rsvp$asap$$default = function asap(callback, arg) {
  $$rsvp$asap$$queue[$$rsvp$asap$$len] = callback;
  $$rsvp$asap$$queue[$$rsvp$asap$$len + 1] = arg;
  $$rsvp$asap$$len += 2;
  if ($$rsvp$asap$$len === 2) {
    // If len is 1, that means that we need to schedule an async flush.
    // If additional callbacks are queued before the queue is flushed, they
    // will be processed by this flush that we are scheduling.
    $$rsvp$asap$$scheduleFlush();
  }
};

var $$rsvp$asap$$browserGlobal = (typeof window !== 'undefined') ? window : {};
var $$rsvp$asap$$BrowserMutationObserver = $$rsvp$asap$$browserGlobal.MutationObserver || $$rsvp$asap$$browserGlobal.WebKitMutationObserver;

// test for web worker but not in IE10
var $$rsvp$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' &&
  typeof importScripts !== 'undefined' &&
  typeof MessageChannel !== 'undefined';

// node
function $$rsvp$asap$$useNextTick() {
  return function() {
    process.nextTick($$rsvp$asap$$flush);
  };
}

function $$rsvp$asap$$useMutationObserver() {
  var iterations = 0;
  var observer = new $$rsvp$asap$$BrowserMutationObserver($$rsvp$asap$$flush);
  var node = document.createTextNode('');
  observer.observe(node, { characterData: true });

  return function() {
    node.data = (iterations = ++iterations % 2);
  };
}

// web worker
function $$rsvp$asap$$useMessageChannel() {
  var channel = new MessageChannel();
  channel.port1.onmessage = $$rsvp$asap$$flush;
  return function () {
    channel.port2.postMessage(0);
  };
}

function $$rsvp$asap$$useSetTimeout() {
  return function() {
    setTimeout($$rsvp$asap$$flush, 1);
  };
}

var $$rsvp$asap$$queue = new Array(1000);

function $$rsvp$asap$$flush() {
  for (var i = 0; i < $$rsvp$asap$$len; i+=2) {
    var callback = $$rsvp$asap$$queue[i];
    var arg = $$rsvp$asap$$queue[i+1];

    callback(arg);

    $$rsvp$asap$$queue[i] = undefined;
    $$rsvp$asap$$queue[i+1] = undefined;
  }

  $$rsvp$asap$$len = 0;
}

var $$rsvp$asap$$scheduleFlush;

// Decide what async method to use to triggering processing of queued callbacks:
if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') {
  $$rsvp$asap$$scheduleFlush = $$rsvp$asap$$useNextTick();
} else if ($$rsvp$asap$$BrowserMutationObserver) {
  $$rsvp$asap$$scheduleFlush = $$rsvp$asap$$useMutationObserver();
} else if ($$rsvp$asap$$isWorker) {
  $$rsvp$asap$$scheduleFlush = $$rsvp$asap$$useMessageChannel();
} else {
  $$rsvp$asap$$scheduleFlush = $$rsvp$asap$$useSetTimeout();
}

// default async is asap;
$$rsvp$config$$config.async = $$rsvp$asap$$default;

var $$rsvp$$cast = $$rsvp$resolve$$default;

function $$rsvp$$async(callback, arg) {
  $$rsvp$config$$config.async(callback, arg);
}

function $$rsvp$$on() {
  $$rsvp$config$$config.on.apply($$rsvp$config$$config, arguments);
}

function $$rsvp$$off() {
  $$rsvp$config$$config.off.apply($$rsvp$config$$config, arguments);
}

// Set up instrumentation through `window.__PROMISE_INTRUMENTATION__`
if (typeof window !== 'undefined' && typeof window['__PROMISE_INSTRUMENTATION__'] === 'object') {
  var $$rsvp$$callbacks = window['__PROMISE_INSTRUMENTATION__'];
  $$rsvp$config$$configure('instrument', true);
  for (var $$rsvp$$eventName in $$rsvp$$callbacks) {
    if ($$rsvp$$callbacks.hasOwnProperty($$rsvp$$eventName)) {
      $$rsvp$$on($$rsvp$$eventName, $$rsvp$$callbacks[$$rsvp$$eventName]);
    }
  }
}

var rsvp$umd$$RSVP = {
  'race': $$rsvp$race$$default,
  'Promise': $$rsvp$promise$$default,
  'allSettled': $$rsvp$all$settled$$default,
  'hash': $$rsvp$hash$$default,
  'hashSettled': $$rsvp$hash$settled$$default,
  'denodeify': $$rsvp$node$$default,
  'on': $$rsvp$$on,
  'off': $$rsvp$$off,
  'map': $$rsvp$map$$default,
  'filter': $$rsvp$filter$$default,
  'resolve': $$rsvp$resolve$$default,
  'reject': $$rsvp$reject$$default,
  'all': $$rsvp$all$$default,
  'rethrow': $$rsvp$rethrow$$default,
  'defer': $$rsvp$defer$$default,
  'EventTarget': $$rsvp$events$$default,
  'configure': $$rsvp$config$$configure,
  'async': $$rsvp$$async
};

/* global define:true module:true window: true */
if (typeof define === 'function' && define.amd) {
  define(function() { return rsvp$umd$$RSVP; });
} else if (typeof module !== 'undefined' && module.exports) {
  module.exports = rsvp$umd$$RSVP;
} else if (typeof this !== 'undefined') {
  this['RSVP'] = rsvp$umd$$RSVP;
}

}).call(this);