text: /* Lightweight Promise implementation.
   *
   * [Promises spec](https://github.com/promises-aplus/promises-spec)
   * 
   * ```javascript
   * function get(url) 
   * {
   *     return new Promise(function (resolve, reject)
   *     {
   *         var req = new XMLHttpRequest();
   *         req.open('GET', url);
   *         req.onload = function () 
   *         {
   *             req.status == 200 ? resolve(req.reponse) : reject(Error(req.statusText));
   *         };
   *         req.onerror = function () { reject(Error('Network Error')) };
   *         req.send();
   *     });
   * }
   * 
   * get('test.json').then(function (result) 
   * {
   *     // Do something...
   * });
   * ```
   */
  
  /* module
   * env: all
   * test: manual
   */
  
  _('Class isObj isFn State bind nextTick noop');
  
  var Promise = (exports = Class(
      {
          initialize: function Promise(fn) {
              if (!isObj(this))
                  throw new TypeError('Promises must be constructed via new');
              if (!isFn(fn)) throw new TypeError(fn + ' is not a function');
  
              var self = this;
  
              this._state = new State('pending', {
                  fulfill: { from: 'pending', to: 'fulfilled' },
                  reject: { from: 'pending', to: 'rejected' },
                  adopt: { from: 'pending', to: 'adopted' }
              })
                  .on('fulfill', assignVal)
                  .on('reject', assignVal)
                  .on('adopt', assignVal);
  
              function assignVal(val) {
                  self._value = val;
              }
  
              this._handled = false;
              this._value = undefined;
              this._deferreds = [];
  
              doResolve(fn, this);
          },
          catch: function(onRejected) {
              return this.then(null, onRejected);
          },
          then: function(onFulfilled, onRejected) {
              var promise = new Promise(noop);
  
              handle(this, new Handler(onFulfilled, onRejected, promise));
  
              return promise;
          }
      },
      {
          all: function(arr) {
              var args = toArr(arr);
  
              return new Promise(function(resolve, reject) {
                  if (args.length === 0) return resolve([]);
  
                  var remaining = args.length;
  
                  function res(i, val) {
                      try {
                          if (val && (isObj(val) || isFn(val))) {
                              var then = val.then;
                              if (isFn(then)) {
                                  then.call(
                                      val,
                                      function(val) {
                                          res(i, val);
                                      },
                                      reject
                                  );
  
                                  return;
                              }
                          }
  
                          args[i] = val;
  
                          if (--remaining === 0) resolve(args);
                      } catch (e) {
                          reject(e);
                      }
                  }
  
                  for (var i = 0; i < args.length; i++) res(i, args[i]);
              });
          },
          resolve: function(val) {
              if (val && isObj(val) && val.constructor === Promise) return val;
  
              return new Promise(function(resolve) {
                  resolve(val);
              });
          },
          reject: function(val) {
              return new Promise(function(resolve, reject) {
                  reject(val);
              });
          },
          race: function(values) {
              return new Promise(function(resolve, reject) {
                  for (var i = 0, len = values.length; i < len; i++) {
                      values[i].then(resolve, reject);
                  }
              });
          }
      }
  ));
  
  var Handler = Class({
      initialize: function Handler(onFulfilled, onRejected, promise) {
          this.onFulfilled = isFn(onFulfilled) ? onFulfilled : null;
          this.onRejected = isFn(onRejected) ? onRejected : null;
          this.promise = promise;
      }
  });
  
  function reject(self, err) {
      self._state.reject(err);
      finale(self);
  }
  
  function resolve(self, val) {
      try {
          if (val === self)
              throw new TypeError('A promise cannot be resolved with itself');
          if (val && (isObj(val) || isFn(val))) {
              var then = val.then;
              if (val instanceof Promise) {
                  self._state.adopt(val);
                  return finale(self);
              }
  
              if (isFn(then)) return doResolve(bind(then, val), self);
          }
  
          self._state.fulfill(val);
          finale(self);
      } catch (e) {
          reject(self, e);
      }
  }
  
  function finale(self) {
      for (var i = 0, len = self._deferreds.length; i < len; i++) {
          handle(self, self._deferreds[i]);
      }
  
      self._deferreds = null;
  }
  
  function handle(self, deferred) {
      while (self._state.is('adopted')) self = self._value;
  
      if (self._state.is('pending')) return self._deferreds.push(deferred);
  
      self._handled = true;
  
      nextTick(function() {
          var isFulfilled = self._state.is('fulfilled');
  
          var cb = isFulfilled ? deferred.onFulfilled : deferred.onRejected;
  
          if (cb === null)
              return (isFulfilled ? resolve : reject)(
                  deferred.promise,
                  self._value
              );
  
          var ret;
  
          try {
              ret = cb(self._value);
          } catch (e) {
              return reject(deferred.promise, e);
          }
  
          resolve(deferred.promise, ret);
      });
  }
  
  function doResolve(fn, self) {
      var done = false;
  
      try {
          fn(
              function(val) {
                  if (done) return;
                  done = true;
                  resolve(self, val);
              },
              function(reason) {
                  if (done) return;
                  done = true;
                  reject(self, reason);
              }
          );
      } catch (e) {
          if (done) return;
          done = true;
          reject(self, e);
      }
  }
  