text: /**
   * Copyright (c) 2013 Petka Antonov
   * 
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:</p>
   * 
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   * 
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */
  "use strict";
  module.exports = function(Promise, INTERNAL) {
  var ASSERT = require("./assert.js");
  var ensureNotHandled = require("./errors.js").ensureNotHandled;
  var util = require("./util.js");
  var async = require("./async.js");
  var hasOwn = {}.hasOwnProperty;
  var isArray = util.isArray;
  
  function toResolutionValue(val) {
      switch(val) {
      case -1: return void 0;
      case -2: return [];
      case -3: return {};
      }
  }
  
  function PromiseArray(values, caller, boundTo) {
      var promise = this._promise = new Promise(INTERNAL);
      var parent = void 0;
      if (Promise.is(values)) {
          parent = values;
          if (values._cancellable()) {
              promise._setCancellable();
              promise._cancellationParent = values;
          }
          if (values._isBound()) {
              promise._setBoundTo(boundTo);
          }
      }
      promise._setTrace(caller, parent);
      this._values = values;
      this._length = 0;
      this._totalResolved = 0;
      this._init(void 0, -2);
  }
  PromiseArray.PropertiesPromiseArray = function() {};
  
  PromiseArray.prototype.length = function PromiseArray$length() {
      return this._length;
  };
  
  PromiseArray.prototype.promise = function PromiseArray$promise() {
      return this._promise;
  };
  
  PromiseArray.prototype._init =
  function PromiseArray$_init(_, resolveValueIfEmpty) {
      var values = this._values;
      if (Promise.is(values)) {
          if (values.isFulfilled()) {
              values = values._settledValue;
              if (!isArray(values)) {
                  var err = new Promise.TypeError("expecting an array, a promise or a thenable");
                  this.__hardReject__(err);
                  return;
              }
              this._values = values;
          }
          else if (values.isPending()) {
              values._then(
                  this._init,
                  this._reject,
                  void 0,
                  this,
                  resolveValueIfEmpty,
                  this.constructor
             );
              return;
          }
          else {
              this._reject(values._settledValue);
              return;
          }
      }
  
      if (values.length === 0) {
          this._resolve(toResolutionValue(resolveValueIfEmpty));
          return;
      }
      var len = values.length;
      var newLen = len;
      var newValues;
      if (this instanceof PromiseArray.PropertiesPromiseArray) {
          newValues = this._values;
      }
      else {
          newValues = new Array(len);
      }
      var isDirectScanNeeded = false;
      for (var i = 0; i < len; ++i) {
          var promise = values[i];
          if (promise === void 0 && !hasOwn.call(values, i)) {
              newLen--;
              continue;
          }
          var maybePromise = Promise._cast(promise, void 0, void 0);
          if (maybePromise instanceof Promise &&
              maybePromise.isPending()) {
              maybePromise._proxyPromiseArray(this, i);
          }
          else {
              isDirectScanNeeded = true;
          }
          newValues[i] = maybePromise;
      }
      if (newLen === 0) {
          if (resolveValueIfEmpty === -2) {
              this._resolve(newValues);
          }
          else {
              this._resolve(toResolutionValue(resolveValueIfEmpty));
          }
          return;
      }
      this._values = newValues;
      this._length = newLen;
      if (isDirectScanNeeded) {
          var scanMethod = newLen === len
              ? this._scanDirectValues
              : this._scanDirectValuesHoled;
          scanMethod.call(this, len);
      }
  };
  
  PromiseArray.prototype._settlePromiseAt =
  function PromiseArray$_settlePromiseAt(index) {
      var value = this._values[index];
      if (!Promise.is(value)) {
          this._promiseFulfilled(value, index);
      }
      else if (value.isFulfilled()) {
          this._promiseFulfilled(value._settledValue, index);
      }
      else if (value.isRejected()) {
          this._promiseRejected(value._settledValue, index);
      }
  };
  
  PromiseArray.prototype._scanDirectValuesHoled =
  function PromiseArray$_scanDirectValuesHoled(len) {
      for (var i = 0; i < len; ++i) {
          if (this._isResolved()) {
              break;
          }
          if (hasOwn.call(this._values, i)) {
              this._settlePromiseAt(i);
          }
      }
  };
  
  PromiseArray.prototype._scanDirectValues =
  function PromiseArray$_scanDirectValues(len) {
      for (var i = 0; i < len; ++i) {
          if (this._isResolved()) {
              break;
          }
          this._settlePromiseAt(i);
      }
  };
  
  PromiseArray.prototype._isResolved = function PromiseArray$_isResolved() {
      return this._values === null;
  };
  
  PromiseArray.prototype._resolve = function PromiseArray$_resolve(value) {
      this._values = null;
      this._promise._fulfill(value);
  };
  
  PromiseArray.prototype.__hardReject__ =
  PromiseArray.prototype._reject = function PromiseArray$_reject(reason) {
      ensureNotHandled(reason);
      this._values = null;
      this._promise._attachExtraTrace(reason);
      this._promise._reject(reason);
  };
  
  PromiseArray.prototype._promiseProgressed =
  function PromiseArray$_promiseProgressed(progressValue, index) {
      if (this._isResolved()) return;
      this._promise._progress({
          index: index,
          value: progressValue
      });
  };
  
  
  PromiseArray.prototype._promiseFulfilled =
  function PromiseArray$_promiseFulfilled(value, index) {
      if (this._isResolved()) return;
      this._values[index] = value;
      var totalResolved = ++this._totalResolved;
      if (totalResolved >= this._length) {
          this._resolve(this._values);
      }
  };
  
  PromiseArray.prototype._promiseRejected =
  function PromiseArray$_promiseRejected(reason, index) {
      if (this._isResolved()) return;
      this._totalResolved++;
      this._reject(reason);
  };
  
  return PromiseArray;
  };
  