/** 
 * Creates and starts the item scroller which must receive the ID of the node containing the elements to be scrolled, 
 * an optional increment size for the scroll (may be zero if auto), and an optional autoscroll time interval (millseconds). 
 * Transition is the number of milliseconds for the animation of shifting one increment.
 * @param viewport The jquery wrapper around the viewport container.
 * @param increment The number of pixels to shift.  If not defined then it will be pulled from the first child of the page.
 * @param autoTimer The number of milliseconds to wait between automatic shifts. If undefined or <= 0 then there won't be any auto shifts.
 * @param initialDelay The number of milliseconds to wait before starting automatic shifts, or zero if undefined or less than zero.
 * @param transition The number of milliseconds to animate the shifting of the page.  Will default to 1000.
 * @param resetPosition Whether the scroll position is reset upon releasing the item scroller.  This defaults to true.
 */
function ItemScroller(viewport, increment, autoTimer, initialDelay, transition, resetPosition) {
	this.viewport = viewport;
	this.increment = increment != undefined && increment != 0 ? this.stripMetric(increment) : undefined;
	
	if(resetPosition != undefined) {
		this.resetPosition = resetPosition;
	}
	
	this.init();
	
	if(autoTimer != undefined) {
		this.autoscroll(autoTimer, initialDelay);
	}
	
	if(transition != undefined && transition > 0) {
		this.transition = transition;
	}
}
ItemScroller.prototype.constructor = ItemScroller;
ItemScroller.prototype.viewport = null;
ItemScroller.prototype.page = null;
ItemScroller.prototype.increment = null;
ItemScroller.prototype.pageWidth = null;
ItemScroller.prototype.isShifting = false;
ItemScroller.prototype.autoscrollPause = false;
ItemScroller.prototype.isReleased = false;
ItemScroller.prototype.transition = 1000;
ItemScroller.prototype.copyCount = 0;
ItemScroller.prototype.resetPosition = true;
ItemScroller.prototype.init = function() {
	this.page = this.viewport.children(':first');
	var children = this.page.children();
	
	//Calculate the increment size for each left/right scroll.//
	if(this.increment == undefined) {
		this.increment = this.stripMetric(children.first().outerWidth());
	}
	
	//Calculate the width of the original set of displayed elements.//
	var viewportWidth = this.viewport.outerWidth();
	var pageWidth = 0;
	this.page.children().each(function() {
		pageWidth += $(this).outerWidth();
	});
	this.pageWidth = pageWidth;
	//Calculate the wrap size that will be needed on the tail of the original set of elements to simulate a wrap around effect.//
	var remainder = this.pageWidth / this.increment != 0 ? this.pageWidth % this.increment : this.increment;
	var tailLength = remainder;
	
	//Clone children until we have enough to properly simulate a wrap around scrolling action.//
	for(var index = 0; index < children.length && tailLength < viewportWidth; index++) {
		var next = $(children[index]);
		
		tailLength += next.outerWidth();
		next.clone(true, true).appendTo(this.page);
		this.copyCount++;
	}
	
	//Now: Fix the spacing the browser adds between inline elements that have any kind of spacing or line feeds between them (bad browsers!).//
	children = this.page.children();
	this.page.css({position: 'relative'});
	var offset = 0;
	children.each(function() {
		var next = $(this);
		
		next.css({position: 'absolute', left: offset, top: 0, display: 'block'});
		offset += next.outerWidth();
	});
	
	if(this.resetPosition) {
		//Start at the beginning.//
		this.viewport.scrollLeft(0);
	}
	else {
		var lastScrollLeft = this.viewport.attr('lastScrollLeft');
		
		if(lastScrollLeft) {
			this.viewport.scrollLeft(lastScrollLeft);
		}
	}
}
ItemScroller.prototype.release = function() {
	//this.page.stop(true, false);
	//this.page.css({left: 0});
	
	if(!this.released) {
		//Stop the animation.//
		this.viewport.stop(true, false);
		//Remove the children that were added upon initialization.//
		var children = this.page.children();
		children.slice(children.length - this.copyCount).detach();
		//Flag ourselves as released.//
		this.isReleased = true;
		
		if(this.resetPosition) {
			//Start at the beginning.//
			this.viewport.scrollLeft(0);
		}
		
		this.viewport.attr('lastScrollLeft', this.viewport.attr('nextScrollLeft'));
	}
}
ItemScroller.prototype.shiftLeft = function(fn) {
	if(!this.isShifting) {
		var pageLeft = this.viewport.scrollLeft();
		var newPageLeft = pageLeft + this.increment;
		var wrapPosition = this.pageWidth;
		var shift = this.pageWidth;
		var _this = this;
		
		this.isShifting = true;
		this.viewport.attr('nextScrollLeft', newPageLeft);
		this.viewport.animate({scrollLeft: newPageLeft}, {duration: this.transition, queue: true, step: function(now, fx) {
			if(fx.now >= wrapPosition) {
				fx.start -= shift;
				fx.end -= shift;
				fx.now -= shift;
			}
		}, complete: function() {
			_this.isShifting = false;
			if(fn) fn();
		}});
	}
}
ItemScroller.prototype.shiftRight = function(fn) {
	if(!this.isShifting) {
		var pageLeft = this.viewport.scrollLeft();
		var newPageLeft = pageLeft - this.increment;
		var wrapPosition = 0;
		var shift = this.pageWidth;
		var _this = this;
		
		this.isShifting = true;
		this.viewport.attr('nextScrollLeft', newPageLeft);
		this.viewport.animate({scrollLeft: newPageLeft}, {duration: this.transition, queue: true, step: function(now, fx) {
			if(fx.now <= wrapPosition) {
				fx.start += shift;
				fx.end += shift;
				fx.now += shift;
			}
		}, complete: function() {_this.isShifting = false; if(fn) fn();}});
	}
}
ItemScroller.prototype.autoscroll = function(delay, initialDelay) {
	var _this = this;
	/* Pause scrolling on mouse over.
	this.page.mouseover(function(eventObject) {
		_this.autoscrollPause = true;
	});
	this.page.mouseout(function(eventObject) {
		_this.autoscrollPause = false;
	});
	*/
	if(initialDelay == undefined || initialDelay <= 0) {
		this.autoscrollIncrement(delay);
	}
	else {
		window.setTimeout(function() {_this.autoscrollIncrement(delay);}, initialDelay);
	}
}
ItemScroller.prototype.autoscrollIncrement = function(delay) {
	var _this = this;
	
	if(!this.isReleased) {
		var fn = function() {
			window.setTimeout(function() {_this.autoscrollIncrement(delay);}, delay);
		};
		
		if(!this.autoscrollPause) {
			this.shiftLeft(fn);
		}
		else {
			fn();
		}
	}
}
ItemScroller.prototype.stripMetric = function(value) {
	return parseInt(value);
}
