// listScroller.js v1.0
//
// Copyright (c) 2007 Einstein Industries
// Author: kmiller@einsteinindustries.com
// 
// ListScroller is freely distributable under the terms of an MIT-style license.
//
//

/*-----------------------------------------------------------------------------------------------*/

if (typeof Effect == 'undefined')
	throw("listScroller.js requires including script.aculo.us' effects.js library!");

var listScroller = Class.create();
listScroller.prototype = {
	//
	// Setup the Variables
	//
	element : null,
	elementWidth : 0,
	elementHeight : 0,
	elementChildren : null,
	childrenWidth : 0,
	childrenHeight : 0,
	currentIndex : 0,
	scrolling : false,
	moveClone : false,
	scrollToElement : -1,
	scrollBy : 0,
	//
	//Initialize the scroller
	//
	initialize: function(id, options) {
		this.options = Object.extend({
			childrenVisible : 5,
			duration : 0.7,
			scrollerType : 'horizontal',
			scrollerIndicator : null,
			child : {
				isElement : true,
				selector : 'li'
			},
			linkText : {	
				previous : 'left',
				next : 'right'
			},
			outputOrder : ['previous', 'scroller', 'next'],
			animationHandler : false,
			disableClone : false
		}, options || {});
		if (this.options.child.isElement) {
			this.elementChildren = $(id).getElementsByTagName(this.options.child.selector);
		} else {
			this.elementChildren = $$('#'+id+' '+this.options.child.selector);
		}
		this._setupScroller(id);
		this.childrenCount = this.elementChildren.length;
		this.childrenWidth = this._getElementWidth();
		this.childrenHeight = this._getElementHeight();
		if (this.options.scrollerType == 'horizontal') {
			this.elementWidth = this.childrenCount*this.childrenWidth;
			this.elementHeight = this.childrenHeight;
		} else if (this.options.scrollerType == 'vertical') {
			this.elementWidth = this.childrenWidth;
			this.elementHeight = this.childrenCount*this.childrenHeight;				
		}
		this._createControls(id);	
		this.element = $(id);					
	},
	//
	// Scroll
	//
	scroll : function (e, direction) {
		if (!this.scrolling) {
			// Determine where and how we are scrolling
			if (this.scrollToElement == -1) {
				this.scrollBy = this.options.childrenVisible;
			} else {
				if (this.scrollToElement > this.currentIndex) {
					this.scrollBy = this.scrollToElement-this.currentIndex;
					direction = -1;
					if ($(this.element.id+'-clone')) $(this.element.id+'-clone').remove();
				} else if (this.scrollToElement < this.currentIndex) {
					this.scrollBy = this.currentIndex-this.scrollToElement;
					direction = 1;
				} else {
					return;
				}
			}
			// What kind of scroller do we have?
			if (this.options.scrollerType == 'horizontal') {
				var move = this._moveHorizontal(direction);
			} else if (this.options.scrollerType == 'vertical') {
				var move = this._moveVertical(direction);				
			}
			// We need to set this for the sake of Opera
			if (!$(this.element.id).style.top || !$(this.element.id).style.left) {
				$(this.element.id).style.top = '0px';
				$(this.element.id).style.left = '0px';
			}
			// Scroll Window
			this._setStatus(true);
			var scrollItems = new Effect.Parallel([
		 			new Effect.MoveBy(this.element.id, move[0], move[1], {duration: this.options.duration, transition: Effect.Transitions.sinoidal}),
		 			new Effect.MoveBy(this.element.id+'-clone', move[0], move[1], {duration: this.options.duration, transition: Effect.Transitions.sinoidal})
		 		], {
					duration: this.options.duration, 
					afterFinish: function(){ 
						if (this.moveClone) {
							this._replaceClone();
						}
						this._setStatus(false);
					}.bind(this)
			});	 
			// Set the Index and Replace/Remove Clone if needed				
			this.currentIndex = this.currentIndex+(-1*(direction*this.scrollBy));
			if (this.currentIndex >= this.childrenCount) {
				this.currentIndex = this.currentIndex-this.childrenCount;
				this.moveClone = true;
			} else if (this.currentIndex <= 0 && -(this.currentIndex)+this.scrollBy >= this.childrenCount) {
				this.currentIndex = this.childrenCount+this.currentIndex;
				this.moveClone = true;
			}
		}
	},
	//
	// Move the Scroller Horizontally
	//
	_moveHorizontal : function(direction) {
		// Build and Position Clone
		if (!$(this.element.id+'-clone')) {
			if ($(this.element.id).style.left) {
				var push = parseFloat($(this.element.id).style.left);
			} else {
				var push = 0;	
			}
			if (this.options.disableClone) {
				var scrollerClone = document.createElement('div');
			} else {
				var scrollerClone = this.element.cloneNode(true); 
			}
			scrollerClone.id = this.element.id+'-clone';
			if (direction > 0) {
				scrollerClone.style.left = -(this.elementWidth-push)+'px';
			} else {
				scrollerClone.style.left = (push+this.elementWidth)+'px';
			}
			scrollerClone.style.top = '0px'; // We need to set this for the sake of Opera
			$(this.element.id+'-container').insertBefore(scrollerClone, this.element);
			this._pause(100); // We need to pause for a moment to make sure the clone has been added.
		} else {
			if (((direction > 0 || (this.currentIndex+this.scrollBy) == 0) && (this.currentIndex+this.scrollBy) <= this.childrenCount) || (direction < 0 && (this.currentIndex) < this.scrollBy)) {
				$(this.element.id+'-clone').style.left = -(this.elementWidth-parseFloat($(this.element.id).style.left))+'px';
			} else {
				$(this.element.id+'-clone').style.left = (parseFloat($(this.element.id).style.left)+this.elementWidth)+'px';
			}
		}
		return new Array(0, direction*this.scrollBy*this.childrenWidth);	
	},
	//
	// Move the Scroller Vertically
	//
	_moveVertical : function(direction) {
		// Build and Position Clone
		if (!$(this.element.id+'-clone')) {
			if ($(this.element.id).style.top) {
				var push = parseFloat($(this.element.id).style.top);
			} else {
				var push = 0;	
			}
			if (this.options.disableClone) {
				var scrollerClone = document.createElement('div');
			} else {
				var scrollerClone = this.element.cloneNode(true); 
			}
			scrollerClone.id = this.element.id+'-clone';
			if (direction > 0) {
				scrollerClone.style.top = -(this.elementHeight-push)+'px';
			} else {
				scrollerClone.style.top = (push+this.elementHeight)+'px';
			}
			scrollerClone.style.left = '0px'; // We need to set this for the sake of Opera
			$(this.element.id+'-container').insertBefore(scrollerClone, this.element);
			this._pause(100); // We need to pause for a moment to make sure the clone has been added.
		} else {
			if (((direction > 0 || (this.currentIndex+this.scrollBy) == 0) && (this.currentIndex+this.scrollBy) <= this.childrenCount) || (direction < 0 && (this.currentIndex) < this.scrollBy)) {
				$(this.element.id+'-clone').style.top = -(this.elementHeight-parseFloat($(this.element.id).style.top))+'px';
			} else {
				$(this.element.id+'-clone').style.top = (parseFloat($(this.element.id).style.top)+this.elementHeight)+'px';
			}
		}
		return new Array(direction*this.scrollBy*this.childrenHeight, 0);	
	},
	//
	// Setup Scroll to go to the element
	//
	gotoElement : function(element) {
		this.scrollToElement = element-1;
		this.scroll(false, null);
	},
	//
	// Replace the clone (This is the magic)
	//
	_replaceClone : function() {
		$(this.element.id).remove();
		$(this.element.id+'-clone').id = this.element.id;
		this.element = $(this.element.id);
		this.moveClone = false;
	},
	//
	// Set the Status of the scroller
	 //
	_setStatus : function(status) {
		this._animationHandler(status);
		this.scrolling = status;
		this.scrollToElement = -1;
	},
	//
	// Use the animation handler
	//
	_animationHandler : function(status) {
		if (this.options.animationHandler) {
			this.options.animationHandler(this.element.id, status);
		}
	},
	//
	// Get the width of an element
	//
	_getElementWidth: function() {
		var child = $(this.elementChildren[0]);
		return parseFloat(child.getDimensions().width)+parseFloat(child.getStyle('margin-left'))+parseFloat(child.getStyle('margin-right'));
	},
	//
	// Get the height of an element
	//
	_getElementHeight: function() {
		var child = $(this.elementChildren[0]);
		return parseFloat(child.getDimensions().height)+parseFloat(child.getStyle('margin-top'))+parseFloat(child.getStyle('margin-bottom'));
	},
	_pause : function(milliseconds) {
		var date = new Date();
		var curentDate = null;
		do {
			var curentDate = new Date();
		} while(curentDate - date < milliseconds);
	},
	//
	// Setup basic required CSS for the Scroller
	//
	_setupScroller : function(id) {
		$(id).style.listStyleType = 'none';
		$(id).style.position = 'absolute';
		for(var x = 0; x < this.elementChildren.length; x++) {
			this.elementChildren[x].style.display = 'block';
			this.elementChildren[x].style.overflow = 'hidden';
			if (this.options.scrollerType == 'horizontal') {
				this.elementChildren[x].style.cssFloat = 'left';
			}
		}
	},
	//
	// Create the markup and required CSS for the Scroller
	//
	_createControls : function(id) {
		// Create Wrapper
		var wrapper = document.createElement('div');
		wrapper.setAttribute('id', id+'-wrapper');

		for (var x = 0; x < this.options.outputOrder.length; x++) {
			switch (this.options.outputOrder[x]) {
				case 'previous':
					// Create Left Link and Container
					var previous = document.createElement('div');
					previous.setAttribute('id', id+'-previous');
					var previousLink = document.createElement('a');
					previousLink.setAttribute('id', id+'-previous-link');
					previousLink.setAttribute('href', 'javascript: void(0);');
					previousLink.style.display = 'block';
					previousLink.innerHTML = '<span>'+this.options.linkText.previous+'</span>';
					if (this.childrenCount >= this.options.childrenVisible) {
						Event.observe(previousLink, 'click', this.scroll.bindAsEventListener(this, 1));
						previousLink.onclick = function() {return false;};	
					} else {
						previousLink.className = 'inactive';
					}
					previous.appendChild(previousLink);
					wrapper.appendChild(previous);
					break;
				
				case 'scroller':
					// Create Scroller Container			
					var parent = $(id).parentNode;
					var container = document.createElement('div');
					container.setAttribute('id', id+'-container');
					if (this.options.scrollerType == 'horizontal') {
						container.style.width = this.childrenWidth*this.options.childrenVisible+'px';
						container.style.height = this.elementHeight+'px';
					} else if (this.options.scrollerType == 'vertical') {
						container.style.height = this.childrenHeight*this.options.childrenVisible+'px';
						container.style.width = this.elementWidth+'px';				
					}
					container.style.position = 'relative';
					container.style.overflow = 'hidden';
					var scrollerClone = $(id).cloneNode(true); 
					if (this.options.scrollerType == 'horizontal') {
						scrollerClone.style.width = this.childrenCount*this.childrenWidth+'px';
					} else if (this.options.scrollerType == 'vertical') {
						scrollerClone.style.height = this.childrenCount*this.childrenHeight+'px';		
					}
					$(id).remove();
					container.appendChild(scrollerClone);
					wrapper.appendChild(container);
					break;
					
				case 'next':
					// Create Right Link and Container
					var next = document.createElement('div');
					next.setAttribute('id', id+'-next');
					var nextLink = document.createElement('a');
					nextLink.setAttribute('id', id+'-next-link');
					nextLink.setAttribute('href', 'javascript: void(0);');
					nextLink.style.display = 'block';
					nextLink.innerHTML = '<span>'+this.options.linkText.next+'</span>';
					if (this.childrenCount >= this.options.childrenVisible) {
						Event.observe(nextLink, 'click', this.scroll.bindAsEventListener(this, -1));
						nextLink.onclick = function() {return false;};	
					} else {
						nextLink.className = 'inactive';
					}
					next.appendChild(nextLink);
					wrapper.appendChild(next);
					break;
				
				default:
					break;
			}
		}
		parent.insertBefore(wrapper, $(id));
		// Evaluate any scripts in the scroller ie. Form Observers
		($(id+'-wrapper').innerHTML).evalScripts();
	}
}