﻿/**
 * @author pop webdev [cn]
 * @version: 0.6.
 * @classDescription: Content Carousel
 * @dependencies: Prototype v1.6.1.
 */

// start InfiniteCarousel
var InfiniteCarousel = Class.create ({
	initialize: function(container,track,items,lnkPrev,lnkNext,options)
	{
		// ** set initial values ** //
		this.container = container;										// $ container for the entire widget
		this.track = track;												// $ inner track containes items
		this.trackID = this.track.identify();							// get ID of carousel track
		this.items = items;												// $$ all the items in the carousel
		this.length = this.items.size();								// length of items
		this.lnkPrev = lnkPrev;											// $ prev link
		this.lnkNext = lnkNext;											// $ next link
		this.track.writeAttribute('role', 'listbox');					// add aria role 'listbox' to track
		this.items.invoke('writeAttribute', 'role', 'option');			// add aria role 'option' carousel items
		this.lnkPrev.writeAttribute('aria-controls', this.trackID);		// add aria role to prev link
		this.lnkNext.writeAttribute('aria-controls', this.trackID);		// add aria role to next link
		this.options = Object.extend({
			initialIndex: 0,											// index of initial item
			itemsPerGroup: 1,											// items per group
			visibleItems: 4,											// number of items visible
			speed: 0.5,													// animation speed
			activeItemClassName: 'active',								// css class for active tab links, not used for this object
			disabledLinkClassName: 'disabled',							// disabled class for prev/next links
			loops: false												// boolean: enable 'infinite' looping
		}, options || {});
		this.isAnimating = false;																			// boolean: flag for animation
		if (this.options.initialIndex >= this.length) {this.options.initialIndex = 0;}						// reset initial index if too high
		if (this.length <= this.options.visibleItems) {this.options.loops = false;}							// short-circuit loops option if not enough items

		// ** set initial conditions ** //
		this.currentIndex = this.options.initialIndex;														// current index = first item
		this.nextIndex = this.currentIndex;																	// check against curentIndex when looping
		this.scrollAmt = (this.items[0].getWidth() * -1);													// amt to scroll by = items width
		if (this.options.loops)
		{
			this.cloneItems = this.track.innerHTML;															// create duplicate of items html
			this.track.insert({top: this.cloneItems}).insert({bottom: this.cloneItems});					// insert duplicate items for 'infinite' looping
			this.currentIndex += this.length;																// reset current index
		}
		this.firstPos;													// declare first position (firstPos == initial index within duplicated 'top' items)
		this.lastPos;													// declare last position (lastPos == initial index within duplicated 'bottom' items)

		// ** bind events and go! ** //
		this.lnkPrev.observe('click', this.__ClickPrev.bindAsEventListener(this));							// observe prev link click
		this.lnkNext.observe('click', this.__ClickNext.bindAsEventListener(this));							// observe next link click
		this.setInitialPos();																				// init position of track
		if (this.length <= this.options.visibleItems)
		{
			this.lnkPrev.addClassName(this.options.disabledLinkClassName);									// disable previous link if only one group
			this.lnkNext.addClassName(this.options.disabledLinkClassName);									// disable next link if only one group
		}
    },
    setInitialPos: function()
    {
		if (this.options.loops)
		{
			this.firstPos = this.currentIndex - this.options.visibleItems;			// set firstPos to first item - num visible items
			this.lastPos = this.currentIndex * 2;									// set lastPos to first item of duplicate group
		} else {
			this.firstPos = 0;														// set firstPos to first item
			this.lastPos = this.length - this.options.visibleItems;					// set lastPos to last item - num visible items (adj -1 ?)
		}
		this.MoveIt(this.currentIndex);
    },
    __ClickPrev: function(e)
    {
        e.stop();
        //var el = e.findElement('a');
        if (!this.isAnimating && !this.lnkPrev.hasClassName(this.options.disabledLinkClassName))
        {
			if (this.options.loops)
			{
				this.currentIndex -= this.options.itemsPerGroup;
				this.MoveIt(this.currentIndex);
			} else {
				if (this.currentIndex != this.firstPos)
				{
					this.currentIndex -= this.options.itemsPerGroup;
					this.MoveIt(this.currentIndex);
				}
			}
        }
    },
    __ClickNext: function(e)
    {
        e.stop();
        //var el = e.findElement('a');
        if (!this.isAnimating && !this.lnkNext.hasClassName(this.options.disabledLinkClassName))
        {
			if (this.options.loops)
			{
				this.currentIndex += this.options.itemsPerGroup;
				this.MoveIt(this.currentIndex);
			} else {
				if (this.currentIndex != this.lastPos)
				{
					this.currentIndex += this.options.itemsPerGroup;
					this.MoveIt(this.currentIndex);
				}
			}
        }
    },
    MoveIt: function(index)
    {
		//this.currentIndex = index; // only currentIndex should ever be passed to MoveIt
		var moveTrack = new Effect.Move(this.track, {
			x: this.scrollAmt * index,
			y: 0,
			mode: 'absolute',
			fps: 100,
			duration: this.options.speed,
			transition: Effect.Transitions.easeOutExpo,
            beforeStart: function() {
                this.isAnimating = true;
                this.UpdateLinks();
            }.bind(this),
            afterFinish: function() {
                this.isAnimating = false;
                if (this.options.loops) {this.AdjustPosition(index);}
            }.bind(this)
		});
    },
    AdjustPosition: function(index)
    {
		if (this.currentIndex <= this.firstPos)
		{
			this.currentIndex += this.length;
			this.track.setStyle({left: (this.scrollAmt * this.currentIndex) + 'px'});
		}
		if (this.currentIndex >= this.lastPos)
		{
			this.currentIndex -= this.length;
			this.track.setStyle({left: (this.scrollAmt * this.currentIndex) + 'px'});
		}
    },
    UpdateLinks: function()
    {
		this.lnkPrev.removeClassName(this.options.disabledLinkClassName);
		this.lnkNext.removeClassName(this.options.disabledLinkClassName);
		// set 'disabled' class on prev/next links
		if (!this.options.loops)
		{
			if (this.currentIndex == this.firstPos)
			{
				this.lnkPrev.addClassName(this.options.disabledLinkClassName);
			}
			if (this.currentIndex == this.lastPos)
			{
				this.lnkNext.addClassName(this.options.disabledLinkClassName);
			}
		}
    }
});
// end InfiniteCarousel

// start Carousel
var Carousel = Class.create ({
    initialize: function(container,track,items,lnkPrev,lnkNext,options)
    {
		this.container = container;					// $ container for the entire widget
		this.track = track;							// $ inner track containes items
		this.items = items;							// $$ all the items in the carousel
		this.length = this.items.size();			// length of items
		this.lnkPrev = lnkPrev;						// $ previous link
		this.lnkNext = lnkNext;						// $ next link
		this.options = Object.extend({
			initialIndex: 0,						// index of initial item
			itemsPerGroup: 1,						// items per group
			visibleItems: 4,						// number of items visible
			speed: 0.5,								// animation speed
			activeItemClassName: 'active',			// css class for active tab links, not used for this object
			disabledLinkClassName: 'disabled',		// disabled class for prev/next links
			loops: false,							// first item loops to last, last item loops to first
			tabLinks: false							// optional tab-style links
		}, options || {});
		if (this.options.initialIndex >= this.length) {this.options.initialIndex = 0;}						// reset initial index if too high
		this.groupsLength = (this.length / this.options.itemsPerGroup).ceil();								// number of groups, default: groups length == items length
		this.scrollAmt = (this.items[0].getWidth() * this.options.itemsPerGroup) * -1;						// amt to scroll by, items per group * items width
		this.groupIndex = (((this.options.initialIndex + 1) / this.options.itemsPerGroup).ceil()) -1;		// get initial group index based on initial item index
		this.isAnimating = false;																			// boolean, flag for animation
        this.lnkPrev.observe('click', this.__ClickPrev.bindAsEventListener(this));							// observe prev link click
        this.lnkNext.observe('click', this.__ClickNext.bindAsEventListener(this));							// observe next link click
		if (this.options.tabLinks)
		{
			var boundLinkClick = this.__ClickItem.bindAsEventListener(this);
			this.options.tabLinks.invoke('observe', 'click', boundLinkClick);
        }
		//set initial position
		this.MoveIt(this.groupIndex);
		if (this.groupsLength == 1)
		{
			this.lnkPrev.addClassName(this.options.disabledLinkClassName);									// disable previous link if only one group
			this.lnkNext.addClassName(this.options.disabledLinkClassName);									// disable next link if only one group
			this.container.addClassName('carousel-disabled');												// disable carousel if only one group
		}
    },
    __ClickPrev: function(e)
    {
        e.stop();
        //var el = e.findElement('a');
        if (!this.isAnimating)
        {
			if (this.options.loops)
			{
				if (this.groupIndex == 0) {this.groupIndex = this.groupsLength}
				this.groupIndex--;
				this.MoveIt(this.groupIndex);
			} else {
				if (this.groupIndex != 0)
				{
					this.groupIndex--;
					this.MoveIt(this.groupIndex);
				}
			}
        }
    },
    __ClickNext: function(e)
    {
        e.stop();
        //var el = e.findElement('a');
        if (!this.isAnimating)
        {
			if (this.options.loops)
			{
				this.groupIndex++;
				if (this.groupIndex == this.groupsLength) {this.groupIndex = 0}
				this.MoveIt(this.groupIndex);
			} else {
				if (this.groupIndex != this.groupsLength -1)
				{
					this.groupIndex++;
					this.MoveIt(this.groupIndex);
				}
			}
        }
    },
    __ClickItem: function(e)
    {
        e.stop();
        var el = e.findElement('a');
        for (var i=0; i<this.length; i++)
        {
	        if (this.options.tabLinks[i] == el)
	        {
		        this.groupIndex = i;
		        this.MoveIt(this.groupIndex);
		        break;
	        }
        }
    },
    MoveIt: function(index)
    {
		this.moveEffect = new Effect.Move(this.track, {
			x: this.scrollAmt * index,
			y: 0,
			mode: 'absolute',
			fps: 100,
			duration: this.options.speed,
			transition: Effect.Transitions.easeOutExpo,
            beforeStart: function() {
                this.isAnimating = true;
                this.UpdateLinks();
            }.bind(this),
            afterFinish: function() {
                this.isAnimating = false;
            }.bind(this)
		});
		if (this.options.tabLinks)
		{
			for (var i=0; i<this.length; i++)
			{
				if (i == index)
				{
					this.options.tabLinks[i].up().addClassName(this.options.activeItemClassName);
				} else {
					this.options.tabLinks[i].up().removeClassName(this.options.activeItemClassName);
				}
			}
        }
    },
    UpdateLinks: function()
    {
		this.lnkPrev.removeClassName(this.options.disabledLinkClassName);
		this.lnkNext.removeClassName(this.options.disabledLinkClassName);

        if (!this.options.loops && (this.groupIndex == 0))
        {
			this.lnkPrev.addClassName(this.options.disabledLinkClassName);
        }
        if (!this.options.loops && (this.groupIndex == this.groupsLength -1))
        {
			this.lnkNext.addClassName(this.options.disabledLinkClassName);
        }
    }
});
// end Carousel

