/* ImageReady (standalone) - part of Voilà
 * http://voila.nickstakenburg.com
 * MIT License
 */
var ImageReady = function() {
  return this.initialize.apply(this, Array.prototype.slice.call(arguments));
};

$.extend(ImageReady.prototype, {
  supports: {
    naturalWidth: (function() {
      return "naturalWidth" in new Image();
    })()
  },

  // NOTE: setTimeouts allow callbacks to be attached
  initialize: function(img, successCallback, errorCallback) {
    this.img = $(img)[0];
    this.successCallback = successCallback;
    this.errorCallback = errorCallback;
    this.isLoaded = false;

    this.options = $.extend(
      {
        method: "naturalWidth",
        pollFallbackAfter: 1000
      },
      arguments[3] || {}
    );

    // a fallback is used when we're not polling for naturalWidth/Height
    // IE6-7 also use this to add support for naturalWidth/Height
    if (!this.supports.naturalWidth || this.options.method === "onload") {
      setTimeout($.proxy(this.fallback, this));
      return;
    }

    // can exit out right away if we have a naturalWidth
    if (this.img.complete && $.type(this.img.naturalWidth) !== "undefined") {
      setTimeout(
        $.proxy(function() {
          if (this.img.naturalWidth > 0) {
            this.success();
          } else {
            this.error();
          }
        }, this)
      );
      return;
    }

    // we instantly bind to onerror so we catch right away
    $(this.img).bind(
      "error",
      $.proxy(function() {
        setTimeout(
          $.proxy(function() {
            this.error();
          }, this)
        );
      }, this)
    );

    this.intervals = [
      [1000, 10],
      [2 * 1000, 50],
      [4 * 1000, 100],
      [20 * 1000, 500]
    ];

    // for testing, 2sec delay
    // this.intervals = [[20 * 1000, 2000]];

    this._ipos = 0;
    this._time = 0;
    this._delay = this.intervals[this._ipos][1];

    // start polling
    this.poll();
  },

  poll: function() {
    this._polling = setTimeout(
      $.proxy(function() {
        if (this.img.naturalWidth > 0) {
          this.success();
          return;
        }

        // update time spend
        this._time += this._delay;

        // use a fallback after waiting
        if (
          this.options.pollFallbackAfter &&
          this._time >= this.options.pollFallbackAfter &&
          !this._usedPollFallback
        ) {
          this._usedPollFallback = true;
          this.fallback();
        }

        // next i within the interval
        if (this._time > this.intervals[this._ipos][0]) {
          // if there's no next interval, we asume
          // the image image errored out
          if (!this.intervals[this._ipos + 1]) {
            this.error();
            return;
          }

          this._ipos++;

          // update to the new bracket
          this._delay = this.intervals[this._ipos][1];
        }

        this.poll();
      }, this),
      this._delay
    );
  },

  fallback: function() {
    var img = new Image();
    this._fallbackImg = img;

    img.onload = $.proxy(function() {
      img.onload = function() {};

      if (!this.supports.naturalWidth) {
        this.img.naturalWidth = img.width;
        this.img.naturalHeight = img.height;
      }

      this.success();
    }, this);

    img.onerror = $.proxy(this.error, this);

    img.src = this.img.src;
  },

  abort: function() {
    if (this._fallbackImg) {
      this._fallbackImg.onload = function() {};
    }

    if (this._polling) {
      clearTimeout(this._polling);
      this._polling = null;
    }
  },

  success: function() {
    if (this._calledSuccess) return;
    this._calledSuccess = true;

    this.isLoaded = true;
    this.successCallback(this);
  },

  error: function() {
    if (this._calledError) return;
    this._calledError = true;

    this.abort();
    if (this.errorCallback) this.errorCallback(this);
  }
});
