// Copyright (c) 2006 Sébastien Gruhier (http://xilinus.com, http://itseb.com)
//
// 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:
//
// 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.
//
// VERSION 0.26

var Carousel = Class.create();
Carousel.prototype = {
  // Constructor
  initialize: function(carouselElemID) {
    this.carouselElemID = carouselElemID;
    
    this.options = Object.extend({
      numVisible:           4,
      scrollInc:            4,
      startIndex:        0,
      animParameters:      {},
      buttonStateHandler:  null,
      animHandler:         null,
      ajaxHandler:         null,
      initDoneHandler:     null,
      queue:               "carousel",
      size:                0,
      prevElementID:       "prev-arrow",
      nextElementID:       "next-arrow",
      ajaxParameters:      null,
      url:                 null
        }, arguments[1] || {});

        this.initDone = false;
        this.animRunning = "none";
            this.requestIsRunning = false;
   
        // add afterFinish options to animParameters (store old function)
        this.animAfterFinish = this.options.animParameters.afterFinish;
        Object.extend(this.options.animParameters, {afterFinish:  this._animDone.bind(this), queue: { position:'end', scope: this.options.queue }});
     
        // Event bindings
        this.prevScroll = this._prevScroll.bindAsEventListener(this);
        this.nextScroll = this._nextScroll.bindAsEventListener(this);
        this.onComplete = this._onComplete.bindAsEventListener(this);
        this.onFailure  = this._onFailure.bindAsEventListener(this);

        Event.observe(this.options.prevElementID, "click", this.prevScroll);
        Event.observe(this.options.nextElementID, "click", this.nextScroll);
       
        // Get DOM UL element
        var carouselListClass = "carousel-list";
        this.carouselList = document.getElementsByClassName(carouselListClass, $(carouselElemID))[0]
        this.options.size =  $(this.carouselList.getElementsByTagName("li")).length;
        // Init data
        this._init();
  },
 
  // Destructor
     destroy: function() {
      Event.stopObserving(this.options.prevElementID, "click", this.prevScroll);
      Event.stopObserving(this.options.nextElementID, "click", this.nextScroll);
    },
   
  /* "Private" functions */
  _init: function() {
    this.currentIndex = this.options.startIndex;
    this.direction = 1;
     
    // Ajax content
    if (this.options.url && this.options.size == 0)
        this._request(this.currentIndex, this.options.numVisible);     
      else {
        // Static content or ajax with first elements loaded
        this._initFirstRun();       
      }
  },
  _initFirstRun: function(){
      if (this.initDone == false) {
          // First run, compute li size
        this._getLiElementSize()
        this.currentIndex = this.options.startIndex;
        this.initDone = true;
        if (this.options.initDoneHandler)
                this.options.initDoneHandler(this);
 
        // Update button states
          this._updateButtonStateHandler(this.options.prevElementID, (this.currentIndex > 0));
          if(this.options.url){
              this._updateButtonStateHandler(this.options.nextElementID, this.options.size == this.options.numVisible);
        }else{
              this._updateButtonStateHandler(this.options.nextElementID, this.options.size > this.options.numVisible);
          }
          this.noMoreImages = this.options.size < this.options.numVisible
          return true;
    }     
    return false;
  }, 
  _prevScroll: function(event) {
    if (this.animRunning != "none" || this.currentIndex == 0)
      return;

    var inc = this.options.scrollInc;

    if (this.currentIndex - inc < 0)
      inc = this.currentIndex;

     if (this.options.url && (this.currentIndex - inc) < this.options.startIndex){
     	 var newIndex =  (this.currentIndex - inc < 0 ? 0 : this.currentIndex - inc)
         this.options.startIndex = newIndex;
         this.direction = -1;
    	 this._request(newIndex,this.currentIndex - newIndex);
     }else {
        this._scroll(inc)              
     }   
    
     return false;
  },
 
  _nextScroll: function(event) {   
    if (this.animRunning != "none")
      return false;
           
    // Check if there are enough elements in cache
    if (this.currentIndex + this.options.numVisible + this.options.scrollInc <= this.options.size)
      this._scroll(-this.options.scrollInc);
    else {
          // Compute how many are in the cache
          this.nbInCache = this.options.size - (this.currentIndex + this.options.numVisible);
    if (this.options.url && this.noMoreImages == false){
        this.direction = 1;
        this._request(this.currentIndex + this.options.numVisible, this.options.scrollInc);
    }else  {
        if (this.nbInCache > 0){
                  this._scroll(-this.nbInCache);
              }
        }
     }
      return false;
  },
 
  _request: function(start, amount) {
    if (this.options.url && !this.requestIsRunning) {
    	this.requestIsRunning = true;
     
      	if (this.options.ajaxHandler)
      		this.options.ajaxHandler(this, "before");
     
      	var params = "index=" + start + "&amount=" + amount; 
      	if (this.options.ajaxParameters != null)
        	params += "&" + this.options.ajaxParameters
     
          	new Ajax.Request(this.options.url, {parameters: params, onComplete: this.onComplete, onFailure: this.onFailure});
  	}
  },
 
  _onComplete: function(originalRequest){
        this.requestIsRunning = false;
        // Add the new li's to the end or to the start depending on the direction  
        this.carouselList.innerHTML = this.direction > 0 ?  (this.carouselList.innerHTML +  originalRequest.responseText): (originalRequest.responseText + this.carouselList.innerHTML);
        // Compute how many new elements we have
        var size = this.options.size;
        this.options.size = this.carouselList.getElementsByTagName("li").length;
        var inc = this.options.size - size;   
           
    if(!this._initFirstRun()){
      // Add images
      if (inc > 0) {
          if(this.direction < 0){     
              var carousel = this;          
              new Effect.MoveBy(this.carouselList, 0, -(inc * this.elementSize), {duration:0.0, afterFinish: function(){
                  carousel._scroll(-inc * carousel.direction, carousel.noMoreImages);
              }});   
          }else{
            this._scroll(-inc * this.direction, this.noMoreImages);
        }
      } else {
        // No more images, disable next button   
        if (this.nbInCache >0)
        this._scroll(-this.nbInCache, true);
       
        this._updateButtonStateHandler(this.options.nextElementID, false);
      }
    }
    if (this.options.ajaxHandler)
        this.options.ajaxHandler(this, "after");
  },
  _onFailure: function(originalRequest){   
    this.requestIsRunning = false;
  },
  _animDone: function(event){  
    if (this.options.animHandler)
      this.options.animHandler(this.carouselElemID, "after", this.animRunning);
    
    this.animRunning = "none";
    // Call animAfterFinish if exists
    if (this.animAfterFinish)
      this.animAfterFinish(event);
  },
 
  _updateButtonStateHandler: function(button, state) {
        if (this.options.buttonStateHandler)
            this.options.buttonStateHandler(button, state)
   },
 
  _scroll: function(delta, forceDisableNext) {     
    this.animRunning = delta > 0 ? "prev" : "next";
   
    if (this.options.animHandler)
      this.options.animHandler(this.carouselElemID, "before", this.animRunning);

    new Effect.MoveBy(this.carouselList, 0, delta * this.elementSize, this.options.animParameters);
    this.currentIndex -= delta;
    this._updateButtonStateHandler(this.options.prevElementID, this.currentIndex != 0);
   
    if (this.options.url && this.noMoreImages == false)
      enable = true;
    else
      enable = (this.currentIndex + this.options.numVisible < this.options.size);
    this._updateButtonStateHandler(this.options.nextElementID, (forceDisableNext ? false : enable));
  },
 
  _getLiElementSize: function() {
    var li = $(this.carouselList.getElementsByTagName("li")[0]);
        this.elementSize = li.getDimensions().width + parseFloat(li.getStyle("margin-left")) + parseFloat(li.getStyle("margin-right"));
  }
}
   



 