/* Animated Scrolling 
** 4/20/2011 BMM
**  To do
**  - add option for pausing on hover
**  - add option for manual scrolling
**  - add option for non looping
*/

/*  
**	Create the scroller
**
**  Parameters
**  frameId : id of the element whose child elements will scroll 
**	opts : map of optional settings (see Options)
**
**  Options
**  scroll_interval : time between individual scroll steps, in milliseconds.  default is 40
**  scroll_dist : distance to scroll each step, in pixels.  default is 2
**  start_dir : starting direction of scrolling.  1(default): left, -1: right, 0:paused
**  no_buttons : set to true to hide control buttons.  default is false
**  pause_button_value : value of pause input button.  default is ll
**  forward_button_value : value of forward input button. default is &raquo;
**  reverse_button_value : value of reverse input button.  default is &laquo;
**  orientation : vertical or horizontal.  default is horizontal.
*/
function scroller(frameId, opts) {
	if (!opts)
		opts = {};
	
	var self = this;
	
	this.frameId = frameId;
	this.frameSelect = '#'+frameId;  // for laziness :p
	this.frame = $(this.frameSelect);
	this.timer = false;
	this.scrInterval = opts.scrInterval? opts.scroll_interval : 40;  // in milliseconds
	this.scrDist = opts.scrDist? opts.scroll_dist : 2 // in pixels
	this.scrDir = opts.startDir? opts.start_dir : 1; // 1:forward  -1:reverse  0:pause
	this.vertical = (opts.orientation && opts.orientation == "vertical"? true : false)
	this.opts = opts;
	
	
	// wrap all the child elements inside the frame
	$(this.frameSelect).children().wrapAll('<div class="scroll_set" />');
	this.scrollSet = $(this.frameSelect+' .scroll_set');
	
	// wrap scrollSet in the scrolling element
	this.scrollSet.wrap('<div class="scroller" />');
	this.scroller = $(this.frameSelect+' .scroller');
	
	// set required styles for elements
	this.frame.css({'position' : 'relative'});
	this.scroller.css({'position' : 'relative', 'overflow' : 'hidden', 'width' : this.frame.width()+'px', 'height' :  this.frame.height()+'px'});
	this.scrollSet.css({'position': 'absolute'});

	// we need to add some buffer elements to either side of the scrollSet for seamless looping
	var scrsize = (this.vertical? this.scroller.height() : this.scroller.width());
	var cursize = 0;
	var i = 0;
	while (cursize < scrsize) {
		var copy = this.scrollSet.children().eq(i).clone();
		this.scrollSet.append(copy);
		cursize += (this.vertical? copy.outerHeight(true) : copy.outerWidth(true));
		i++;
	}
	this.startPos = cursize;
	i++;
	cursize = 0;
	while (cursize < scrsize) {
		var copy = this.scrollSet.children().eq(0-i).clone();
		this.scrollSet.prepend(copy);
		cursize += (this.vertical? copy.outerHeight(true) : copy.outerWidth(true));
		i++;
	}
	this.endPos = cursize;	

	// We need to set the width of the scrollSet, otherwise it will default
	// to the width of the scroller and elements will wrap rather than be on one line.
	// By moving the scrollSet to the left,  it will expand to fill the scroller
	// plus the distance it was moved to the left. Therefore we can just 
	// keep moving it to the left until the width stops growing, and then we'll 
	// know what the width should be.
	if (this.vertical) {
		var height = this.scrollSet.height();
		var mult = 10;
		this.scrollSet.css('top','-1000px');
		while (this.scrollSet.height() != height) {
			height = this.scrollSet.height();
			this.scrollSet.css('top', '-'+(1000*mult)+'px');
			mult *= 10;
		}
		this.scrollSet.css('height',height);		
		this.scrollSet.css('top','0px');
		
		// move scroller to starting position
		this.scroller[0].scrollTop = this.startPos;		
	} else {
		var width = this.scrollSet.width();
		var mult = 10;
		this.scrollSet.css('left','-1000px');
		while (this.scrollSet.width() != width) {
			width = this.scrollSet.width();
			this.scrollSet.css('left', '-'+(1000*mult)+'px');
			mult *= 10;
		}
		this.scrollSet.css('width',width);		
		this.scrollSet.css('left','0px');
		
		// move scroller to starting position
		this.scroller[0].scrollLeft = this.startPos;
	}
	
	// add control buttons
	if (!opts.no_buttons) {
		$('<div class="buttons" style="position:absolute;"></div>').appendTo(this.frame);
	
		$('<input type="button" value="'+(opts.reverse_button_value? opts.reverse_button_value : '&laquo;')+'" class="reverse_button" name="reverse_button" />').appendTo(self.frameSelect+' div.buttons').click(function () {self.reverse();});		
		$('<input type="button" value="'+(opts.pause_button_value? opts.pause_button_value : 'll')+'" class="pause_button" name="pause_button" />').appendTo(self.frameSelect+' div.buttons').click(function () {self.pause();});	
		$('<input type="button" value="'+(opts.forward_button_value? opts.forward_button_value : '&raquo;')+'" class="forward_button" name="forward_button" />').appendTo(self.frameSelect+' div.buttons').click(function () {self.forward();});		
	}
	
	return this;
}

// Start scrolling forward
scroller.prototype.forward = function () {
	this.scrDir = 1;
	this.scroll();
}

// Start scrolling in reverse
scroller.prototype.reverse = function () {
	this.scrDir = -1;
	this.scroll();
}

// Pause the scroller
scroller.prototype.pause = function () {
	this.scrDir = 0;
	this.scroll();
}

// Start scrolling
scroller.prototype.scroll = function() {
	var self = this;
	
	// clear the timer
	if (this.timer) {
		clearInterval(this.timer);
		this.timer = false;
	}
	
	// unselect all buttons
	if (!this.opts.no_buttons) {		
		$(this.frameSelect+' div.buttons input').attr("class", function() {
			$(this).attr("class", $(this).attr("name"));																		
		});
	}
	
	if (this.scrDir > 0) {
		// set the forward button to selected
		if (!this.opts.no_buttons) {
			$(this.frameSelect+' div.buttons input.forward_button').attr("class", "forward_button selected");
		}
		
		// The maximum scrollLeft/Top value for the scrolling element
		if (this.vertical)			
			this.maxPos =  this.scrollSet.height() - this.scroller.height();
		else
			this.maxPos =  this.scrollSet.width() - this.scroller.width();
	
		// set the scroll timer
		this.timer = setInterval(
			function() {				
				if (self.vertical) {
					if (self.scroller[0].scrollTop < self.maxPos) {
						self.scroller[0].scrollTop += self.scrDist;	
					} else {
						// end has been reached, move scroller back to start
						self.scroller[0].scrollTop = (self.startPos + self.endPos + self.scrDist) - self.scroller.height();
					}
				} else {
					if (self.scroller[0].scrollLeft < self.maxPos) {
						self.scroller[0].scrollLeft += self.scrDist;	
					} else {
						// end has been reached, move scroller back to start
						self.scroller[0].scrollLeft = (self.startPos + self.endPos + self.scrDist) - self.scroller.width();
					}
				}
			},
			this.scrInterval
		);
	} else if (this.scrDir < 0) {
		// set the reverse button to selected
		if (!this.opts.no_buttons) {
			$(this.frameSelect+' div.buttons input.reverse_button').attr("class", "reverse_button selected");
		}
		
		// set the scroll timer
		this.timer = setInterval(
			function() {				
				if (self.vertical) {
					if (self.scroller[0].scrollTop > 0) {
						self.scroller[0].scrollTop -= self.scrDist;
					} else {
						// end has been reached, move scroller back to start
						self.scroller[0].scrollTop = self.scrollSet.height() - (self.startPos + self.endPos + self.scrDist);
					}
				} else {
					if (self.scroller[0].scrollLeft > 0) {
						self.scroller[0].scrollLeft -= self.scrDist;
					} else {
						// end has been reached, move scroller back to start
						self.scroller[0].scrollLeft = self.scrollSet.width() - (self.startPos + self.endPos + self.scrDist);
					}
				}
			},
			this.scrInterval
		);
	} else {
		// set the pause button to selected
		if (!this.opts.no_buttons) {
			$(this.frameSelect+' div.buttons input.pause_button').attr("class", "pause_button selected");
		}
	}
}
	

	

	

