/*
 * jquery.expandableScroller.js
 *
 * Developed by Erik Porter for www.deltafaucet.com
 * Structure based off of jScrollPane by Kelvin Luck
 */

/**
 *
 *  $('xxx').expandableScroller({ 
 *	  offsetX: width of each item in the scroller, 
 *	  offsetY: height of each item in the scroller,
 *    itemTag: the tag to base item count on,
 *    openText: text for the expand link,
 *    closeText: text for the close link,
 *    trayPadding: padding in pixels for each side of the scroller,
 *	  scrollbarHeight: height of the scrollbar,
 *	  scrollbarMargin: space to leave above the scrollbar,
 *    showArrows: whether to display scroll arrows or not,
 *	  arrowSize: width of arrows,
 *	  dragMinWidth:  minimum width of scrollbar,
 *	  dragMaxWidth: maximum width of the scrollbar,
 *    animateInterval: interval in milliseconds to update an animating scroller,
 *    animateStep: amount to divide the remaining scroll distance by when animating,
 *    scrollAcceleration: divisor to determine how fast auto-scrolling accelorates,
 *    maxScrollSpeed : maximum auto-scrolling speed,
 *    toggle : whether to show the toggle link
 *  });
 *
 */



(function($) {

	$.fn.expandableScroller = function(options) {
		
		return this.each(function() {
			$.expandableScroller(this, options);
		});
	};
	
	$.expandableScroller = function(container, options) {
	
		var defaults = {
			offsetX: 150,
			offsetY: 130,
			itemTag: 'div',
			openText: 'show all at once',
			closeText: 'collapse',
			trayPadding: 0,
			scrollbarHeight: 21, 
			scrollbarMargin: 0,
			showArrows: true,
			arrowSize: 6,
			animateTo : false,
			dragMinWidth : 1,
			dragMaxWidth : 99999,
			animateInterval: 100,
			animateStep: 3,
			scrollAcceleration: 6,
			maxScrollSpeed : 12,
			wheelSpeed : 18,
			toggle : true
		};
		
		var options = $.extend(defaults, options);
		
		var $thisScroller = $(container);		

		var paneWidth = $thisScroller.innerWidth();
		var paneHeight = options.offsetY;
		var trackWidth = paneWidth;
		
		if ($thisScroller.is('.expandableScroller')) {
		
			//alert('scroll exists');
			var currentScrollPosition = 0;
			
			var itemCnt = $('>.scrollWrapper >.scrollTray >'+options.itemTag, $thisScroller).length;
			var contentWidth = (itemCnt * options.offsetX) + (options.trayPadding * 2);
			
			$('>.scrollBar, >.leftFade, >.rightFade, >.leftArrow, >.rightArrow', $thisScroller).remove();
		
		} else {
			
			var itemCnt = $('>'+options.itemTag, $thisScroller).length;
			var contentWidth = (itemCnt * options.offsetX) + (options.trayPadding * 2);
			$thisScroller.currentState = 'closed';
			
			var currentPos = 0;
			var currentScrollPosition = 0;
			
			// Wrap scrollable items in scroller divs and set it's width and overflow
			$thisScroller.addClass('expandableScroller').css({ position: 'relative' }).find('>'+options.itemTag)
			.wrapAll(
				//Wrap scrollable items in an outer wrapper with overflow hidden
				$('<div></div>').attr({'className':'scrollWrapper'})
				.css({
					overflow: 'hidden',
					position: 'relative',
					width: paneWidth + 'px'
				})
			)
			.wrapAll(
				//Wrap scrollable items in a div to slide back and forth
				 $('<div></div>').attr({'className':'scrollTray'})
				.css({
					width: contentWidth+'px',
					height: paneHeight + '',
					padding: '0 '+  options.trayPadding +'px',
					position: 'relative'
				})
			);
		}
		
		var contentHeight = options.offsetY;
		var percentInView = paneWidth / contentWidth;
		//alert('itemCnt= '+ itemCnt +', paneWidth= '+paneWidth + ', contentWidth= '+ contentWidth);
		
		//alert(percentInView);
		
		if (percentInView < .99) {
			
			
			$thisScroller.append(
				
				// append scrollbar div
				$('<div></div>').attr({'className':'scrollBar'})
				.css({
					position: 'relative',
					zIndex: '5',
					height: options.scrollbarHeight + 'px'
					//bottom: 0,
					//left: options.arrowSize +'px'
				}).append(
					$('<div></div>').attr({'className':'scrollBarDrag'}).css({ height: options.scrollbarHeight+'px', position: 'absolute', zIndex: 10, cursor: 'pointer' })
					.append(
						$('<div></div>').attr({'className':'scrollBarDragLeft'}).css({'height': options.scrollbarHeight+'px'}),
						$('<div></div>').attr({'className':'scrollBarDragRight'}).css({'height': options.scrollbarHeight+'px'})
					)
				),
				
				$('<div></div>').attr({'className':'leftFade'})
				.css({
					position: 'absolute',
					zIndex: 1,
					left: 0,
					top: 0,
					height: options.offsetY + 'px'
				}),
				
				$('<div></div>').attr({'className':'rightFade'})
				.css({
					position: 'absolute',
					zIndex: 1,
					right: 0,
					top: 0,
					height: options.offsetY + 'px'
				})
			);
			
			if (options.toggle) {
			
				$thisScroller.append(
					
					// append link to toggle display of scroller to show all items at once
					$('<div>'+ options.openText +'</div>').attr({'className':'toggleLink'})
					.addClass($thisScroller.currentState)
					.css({
						cursor: 'pointer'
					})
					.toggle(
						function() {
							$thisScroller.currentState = 'open';
							currentPos = $('>.scrollWrapper >.scrollTray', $thisScroller).css('left');
							if (currentPos == 'auto') {
								currentPos = '0px';
							}
							
							$(this).text( options.closeText );
							
							$('>.leftArrow, >.rightArrow, >.leftFade, >.rightFade, >.scrollBar', $thisScroller).hide();
							
							$('>.scrollWrapper >.scrollTray', $thisScroller).animate({
								left: '0px',
								width: '936px',
								paddingLeft: '0px',
								paddingRight: '0px'
							}, 200, function(){
								
								//alert($('>.scrollWrapper >.scrollTray', $thisScroller).height());
								
								$('.scrollWrapper, .scrollTray', $thisScroller).css({
									height: 'auto'
								});
								
								//$thisScroller.css({
								//	height: $('>.scrollWrapper', $thisScroller).height() + 'px'
								//});
							});
						},
						function() {
							$thisScroller.currentState = 'closed';
							$('>.scrollWrapper >.scrollTray', $thisScroller).animate({
								left: currentPos,
								width: contentWidth,
								paddingLeft: options.trayPadding + 'px',
								paddingRight: options.trayPadding + 'px'
							}, 200, function(){
								$('>.leftArrow, >.rightArrow, >.leftFade, >.rightFade, >.scrollBar', $thisScroller).show();
							
							});
							$(this).text( options.openText );
							
						}
					)
					
				)
			}
			
			
			//Set up scroll arrow functionality
			// taken from jScrollPane and modified
			var currentArrowButton;
			var currentArrowDirection;
			var currentArrowInterval;
			var currentArrowInc;
			var currentArrowCounter;
			var whileArrowButtonDown = function()
			{
				positionDrag(dragPosition + (currentArrowDirection * currentArrowInc) );
				if ( (currentArrowCounter%options.scrollAcceleration ==0) && (currentArrowInc < options.maxScrollSpeed) ) {
					currentArrowInc ++;
				}
				currentArrowCounter++;
			};
			var onArrowMouseUp = function(event)
			{
				$('body').unbind('mouseout', onArrowMouseUp);
				currentArrowButton.removeClass('activeArrowButton');
				clearInterval(currentArrowInterval);
			};
			var onArrowMouseDown = function() {
				$('body').bind('mouseout', onArrowMouseUp);
				currentArrowButton.addClass('activeArrowButton');
				currentArrowCounter = 0;
				currentArrowInc = 0;
				whileArrowButtonDown();
				currentArrowInterval = setInterval(whileArrowButtonDown, 20);
			};
			
			
			$thisScroller.append(
				
				// append leftArrow div
				$('<a></a>').attr({'className':'leftArrow'})
				//.css({ position: 'absolute', height: options.offsetY + 'px', top: 0, left: 0 })
				.bind('mouseover', function()
				{
					currentArrowButton = $(this);
					currentArrowDirection = -1;
					onArrowMouseDown();
					this.blur();
					return false;
				}),
				
				// append rightArrow div
				$('<a></a>').attr({'className':'rightArrow'})
				//.css({ position: 'absolute', width: options.arrowSize + 'px', height: options.offsetY + 'px', top: 0, right: 0 })
				.bind('mouseover', function()
				{
					currentArrowButton = $(this);
					currentArrowDirection = 1;
					onArrowMouseDown();
					this.blur();
					return false;
				})
			);
			
			
			
			//Set up scrollbar functionality
			// taken from jScrollPane and modified
			
			var $track = $('>.scrollBar', $thisScroller);
			var $drag = $('>.scrollBar >.scrollBarDrag', $thisScroller);
			
			var $container = ('>.scrollWrapper', $thisScroller);
			var $tray = $('>.scrollWrapper >.scrollTray', $thisScroller).css({
				//'position':'absolute',
				//'overflow':'visible'
			});
			
			var currentOffset;
			var maxX;
			var mouseWheelMultiplier;
			// store this in a seperate variable so we can keep track more accurately than just updating the css property..
			var dragPosition = 0;
			var dragMiddle = percentInView*paneWidth/2;
			
			// pos function borrowed from tooltip plugin and adapted...
			var getPos = function (event, c) {
				var p = c == 'X' ? 'Left' : 'Top';
				return event['page' + c] || (event['client' + c] + (document.documentElement['scroll' + p] || document.body['scroll' + p])) || 0;
			};
			
			var ignoreNativeDrag = function() {	return false; };
			
			var initDrag = function()
			{
				ceaseAnimation();
				currentOffset = $drag.offset(false);
				currentOffset.left -= dragPosition;
				maxX = trackWidth - $drag[0].offsetWidth;
				mouseWheelMultiplier = 2 * options.wheelSpeed * maxX / contentWidth;
			};
			
			var onStartDrag = function(event)
			{
				initDrag();
				
				dragMiddle = getPos(event, 'X') - dragPosition - currentOffset.left;
				$('body').bind('mouseup', onStopDrag).bind('mousemove', updateScroll);
				if ($.browser.msie) {
					$('body').bind('dragstart', ignoreNativeDrag).bind('selectstart', ignoreNativeDrag);
				}
				return false;
			};
			var onStopDrag = function()
			{
				$('body').unbind('mouseup', onStopDrag).unbind('mousemove', updateScroll);
				dragMiddle = percentInView*paneWidth/2;
				if ($.browser.msie) {
					$('body').unbind('dragstart', ignoreNativeDrag).unbind('selectstart', ignoreNativeDrag);
				}
			};
			var positionDrag = function(destX)
			{
				destX = destX < 0 ? 0 : (destX > maxX ? maxX : destX);
				dragPosition = destX;
				$drag.css({'left':destX+'px'});
				var p = destX / maxX;
				$tray.css({'left':((paneWidth-contentWidth)*p) + 'px'});
				$thisScroller.trigger('scroll');
			};
			var updateScroll = function(e)
			{
				positionDrag(getPos(e, 'X') - currentOffset.left - dragMiddle);
			};
			
			
			var dragW = Math.max(Math.min(percentInView*(paneWidth), options.dragMaxWidth), options.dragMinWidth);
			
			$drag.css({'width':dragW+'px'}).bind('mousedown', onStartDrag);
			
			var trackScrollInterval;
			var trackScrollInc;
			var trackScrollMousePos;
			var doTrackScroll = function()
			{
				if (trackScrollInc > 8 || trackScrollInc%4==0) {
					positionDrag((dragPosition - ((dragPosition - trackScrollMousePos) / 2)));
				}
				trackScrollInc ++;
			};
			var onStopTrackClick = function()
			{
				clearInterval(trackScrollInterval);
				$('body').unbind('mouseup', onStopTrackClick).unbind('mousemove', onTrackMouseMove);
			};
			var onTrackMouseMove = function(event)
			{
				trackScrollMousePos = getPos(event, 'X') - currentOffset.left - dragMiddle;
			};
			var onTrackClick = function(event)
			{
				initDrag();
				onTrackMouseMove(event);
				trackScrollInc = 0;
				$('body').bind('mouseup', onStopTrackClick).bind('mousemove', onTrackMouseMove);
				trackScrollInterval = setInterval(doTrackScroll, 100);
				doTrackScroll();
			};
			
			$track.bind('mousedown', onTrackClick);
			
			// if the mousewheel plugin has been included then also react to the mousewheel
			if ($container.mousewheel) {
				$container.mousewheel(
					function (event, delta) {
						initDrag();
						ceaseAnimation();
						var d = dragPosition;
						positionDrag(dragPosition - delta * mouseWheelMultiplier);
						var dragOccured = d != dragPosition;
						return !dragOccured;
					},
					false
				);					
			}
			var _animateToPosition;
			var _animateToInterval;
			function animateToPosition()
			{
				var diff = (_animateToPosition - dragPosition) / options.animateStep;
				if (diff > 1 || diff < -1) {
					positionDrag(dragPosition + diff);
				} else {
					positionDrag(_animateToPosition);
					ceaseAnimation();
				}
			}
			var ceaseAnimation = function()
			{
				if (_animateToInterval) {
					clearInterval(_animateToInterval);
					delete _animateToPosition;
				}
			};
			var scrollTo = function(pos, preventAni)
			{
				if (typeof pos == "string") {
					$e = $(pos, this);
					if (!$e.length) return;
					pos = $e.offset({relativeTo:this}).top;
				}
				ceaseAnimation();
				var destDragPosition = -pos/(paneWidth-contentWidth) * maxX;
				if (!preventAni || options.animateTo) {
					_animateToPosition = destDragPosition;
					_animateToInterval = setInterval(animateToPosition, options.animateInterval);
				} else {
					positionDrag(destDragPosition);
				}
			};
			$thisScroller[0].scrollTo = scrollTo;
			
			$thisScroller[0].scrollBy = function(delta)
			{
				var currentPos = -parseInt($tray.css('left')) || 0;
				scrollTo(currentPos + delta);
			};
			
			initDrag();
			
			//scrollTo(-currentScrollPosition, true);
			
			
	
		} //end if
	
	} //end $.expandableScroller
	

})(jQuery);

// clean up expandos? pulled from jScrollPane
jQuery(window)
	.bind('unload', function() {
		//alert('kill expandos');
		$('.expandableScroller').each(function(){
		
		});
	}
);