/*
 *  Project: Input Creative Infinite Slide
 *  Version: 0.9.1
 *  Description: An infinite slide jQuery plugin capable of sliding elements of any width.
 *  Author: Alex Arnell
 *  License: BSD
 *  Copyright: Input Creative 2011
 */

;(function ( $, window, document, undefined ) {

  // Create the defaults once
  var pluginName = 'slide',
  defaults = {
    speed: 500,
    pinned: false,
  };

  function waitForImages($imgs, callback) {
    var alreadyLoaded = [];

    $imgs.on('load', function(img) {
      if (jQuery.inArray(img, alreadyLoaded) == -1){
        alreadyLoaded.push(img);
        if (alreadyLoaded.length == $imgs.length){
          callback.call();
        }
      }
    }).each(function(img) {
      if(this.complete || (jQuery.browser.msie && parseInt(jQuery.browser.version) == 6))
        $(this).trigger("load");
    });
  }

  // The actual plugin constructor
  function Slide( element, options ) {
    this.el = element;
    this.$el = $(element);

    // only accept 'left' or 'right' or false/undefined
    if (!(options.pinned === 'left' || options.pinned === 'right' || options.pinned === false)) {
      delete options.pinned;
    }

    this.options = $.extend( {}, defaults, options) ;

    this._defaults = defaults;
    this._name = pluginName;

    waitForImages($('img', this.el), $.proxy(this.init, this));
  }

  Slide.prototype.init = function () {
    var $el = this.$el,
    base = this,

    slideWidth = base._slideWidth = $el.outerWidth(),
    slideHeight = $el.outerHeight(),

    $items = $el.children().wrapAll('<div></div>'),

    $wrapper = $items.parent().addClass('wrapper').css({
      'position': 'absolute',
      'top': 0,
      'height': slideHeight
    }).wrapAll('<div></div>'),

    $slide = this._$slide = $wrapper.parent().addClass('slide').css({
      'overflow': 'hidden',
      'position': 'absolute',
      'top': 0,
      'height': slideHeight,
      'width': slideWidth
    });

    // should infinitely rotate around so we need to clone some elements
    // need to figure out how many items fit in the slide area at the start
    // and end of the $items array

    var accumulator = 0,
    requiredWidth = slideWidth * (this.options.pinned === 'left' ? 2 : 1);
    var postfixItems = $.grep($items, function(item, index){
      var retVal = accumulator < requiredWidth;
      accumulator += $(item).outerWidth();
      return retVal;
    });

    accumulator = 0;
    requiredWidth = slideWidth * (this.options.pinned === 'right' ? 2 : 1);
    var prefixItems = $.grep($items.toArray().reverse(), function(item, index){
      var retVal = accumulator < requiredWidth;
      accumulator += $(item).outerWidth();
      return retVal;
    }).reverse();

    // now that we have the elements we stitch them into place

    $items.filter(':first').before($(prefixItems).clone().addClass('cloned'));
    $items.filter(':last').after($(postfixItems).clone().addClass('cloned'));
    $items = $wrapper.children().css('float', 'left');

    // figure out the total width of all the elements and get an
    // array of each slide elements width so that we can animate sliding
    // the elements left and right and store our actual start and end
    // indexes in the items

    this._actualStart = prefixItems.length;
    this._actualEnd = $items.length - postfixItems.length - prefixItems.length;
    this._totalWidth = 0;
    accumulator = 0 - slideWidth;

    var aligns = this._aligns = $.map($items, function(item) {
      var prev = base._totalWidth, width = $(item).outerWidth();
      console.log(item.src, width);
      base._totalWidth += width;
      accumulator += width;
      return { left : prev, right : accumulator };
    });

    // give us ample room to pad with elements

    $wrapper.css('width', base._totalWidth);

    // scroll to back to the first element again
    base.reset();

    // finally attach some events that can be triggered

    $el.bind({
      'prev.slide': $.proxy(Slide.prototype.prev, base),
      'next.slide': $.proxy(Slide.prototype.next, base),
      'reset.slide': $.proxy(Slide.prototype.reset, base)
    });
  };

  Slide.prototype.next = function() {
    return this._goto(this._lastItem + 1, this.options.pinned || 'right', true);
  };

  Slide.prototype.prev = function() {
    return this._goto(this._lastItem - 1, this.options.pinned || 'left', true);
  };

  Slide.prototype.reset = function() {
    this._goto(this._actualStart, 'left', false);
  };

  Slide.prototype._goto = function(item, edge, animate) {
    var base = this,
    $slide = base._$slide,
    xpos = base._aligns[item][edge];

    base._lastItem = item;

    if (animate === true) {
      function infinite_fix() {
        if (item == 0) {
          base._goto(base._actualEnd, 'left', false);
        } else if (item == base._aligns.length - 1) {
          base._goto(base._actualStart, 'left', false);
        }
      };

      $slide.filter(':not(:animated)').
        animate({ scrollLeft : xpos }, base.options.speed, infinite_fix);
    } else {
      $slide.scrollLeft(xpos);
    }

    return false;
  };

  // hook into jQuery fn and provide access to the defaults

  $.fn[pluginName] = function ( options ) {
    return this.each(function () {
      if (!$.data(this, 'plugin_' + pluginName)) {
        $.data(this, 'plugin_' + pluginName, new Slide( this, options || {} ));
      }
    });
  };

  $.fn[pluginName].defaults = defaults;

})(jQuery, window, document);

