/* 
 * jQuery UI - Scrollbar plugin
 * Create custom scrollbars that behave like native scrollbars
 *
 * @author Zach Waugh <zwaugh@gmail.com>
 * @version 1.1
 * @requires ui.core.js
 *
 * Copyright (c) 2009 Zach Waugh MIT License
 */

(function($){
	/**
	 * Main plugin function
	 */
	$.widget("ui.scrollbar", 
	{
		_init: function()
		{
			var self = this, options = this.options;
			this._isDragging = false;
			this._handleOffset = 0;
			this._scrollbarContents = this.element.find('.ui-scrollbar-contents');
			this._timer = null;
			this.element.addClass('ui-scrollable').css('overflow', options.overflow);
			
			if (options.vertical)
			{
				this._initVertical();
			}
			else
			{
				this._initHorizontal();
			}
		}, 
		
	/**
	 * Initialize a vertical scrollbar
	 */
	_initVertical: function ()
	{
		var self = this;
		
		// Get height of scrollable area
		var windowHeight = this.element.innerHeight();
		this._windowHeight = windowHeight;
		
		var contentsHeight = this._scrollbarContents.outerHeight(true);
		this._contentsHeight = contentsHeight;

		// Only add scrollbar if needed
		if (contentsHeight > windowHeight)
		{
			var trackHeight = windowHeight - (this.options.buttonHeight * 2);
			var handleHeight = 37; //(windowHeight / contentsHeight) * trackHeight;
			
			var scrollbar = '<div class="ui-scrollbar ui-scrollbar-vertical">';
			scrollbar += '<a href="#" class="ui-scrollbar-button-up"></a>';
			scrollbar += '<div class="ui-scrollbar-track" style="top:' + this.options.buttonHeight + 'px;height:' + trackHeight + 'px;">';
			scrollbar += '<div class="ui-scrollbar-handle" style="height:' + handleHeight + 'px;"></div>';
			scrollbar += '</div>'; // .ui-scrollbar-track
			scrollbar += '<a href="#" class="ui-scrollbar-button-down"></a>';
			scrollbar += '</div>'; // .ui-scrollbar
			
			this._scrollbarContents.css({position: 'absolute', top: 0});
			this.element.prepend(scrollbar);

			// cache references to scrollbar pieces
			this._scrollbar = $('.ui-scrollbar', this.element);
			this._scrollbarTrack = $('.ui-scrollbar-track', this.element);
			this._scrollbarHandle = $('.ui-scrollbar-handle', this.element);
		
			// Bind Events
			this._scrollbarTrack.mousedown(function(event) { return self._start(event); });
			this.element.mousewheel(function(event, delta) { return self._scrollWheelVertical(event, delta); });
			
			this.element.find('.ui-scrollbar-button-up').mousedown(function(event) { return self._goUp(); }).mouseup(function() { return self._clearTimer(); }).click(function(){ return false; });
			this.element.find('.ui-scrollbar-button-down').mousedown(function(event) { return self._goDown(); }).mouseup(function() { return self._clearTimer(); }).click(function(){ return false; });
		}
	},
	
	/**
	 * Initialize a horizontal scrollbar
	 */
	_initHorizontal: function ()
	{
		var self = this;
		
		// Get width of scrollable area
		var windowWidth = this.element.innerWidth();
		this._windowWidth = windowWidth;
		
		//var contentsWidth = this._scrollbarContents.innerWidth();
		// Compute actual width of scrollbar contents
		// TODO: code assumes contents is wrapped in a ul - should make option
		var contentsWidth = this._scrollbarContents.find('li').outerWidth(true) * this._scrollbarContents.find('li').length;
		// Update container with actual width
		this._scrollbarContents.css('width', contentsWidth);
		
		this._contentsWidth = contentsWidth;

		// Only add scrollbar if needed
		if (contentsWidth > windowWidth)
		{
			var trackWidth = windowWidth - (this.options.buttonWidth * 2);
			var handleWidth = (windowWidth / contentsWidth) * trackWidth;
			
			var scrollbar = '<div class="ui-scrollbar ui-scrollbar-horizontal">';
			scrollbar += '<a href="#" class="ui-scrollbar-button-left"></a>';
			scrollbar += '<div class="ui-scrollbar-track" style="left:' + this.options.buttonWidth + 'px;width:' + trackWidth + 'px;">';
			scrollbar += '<div class="ui-scrollbar-handle" style="width:' + handleWidth + 'px;"></div>';
			scrollbar += '</div>'; // .ui-scrollbar-track
			scrollbar += '<a href="#" class="ui-scrollbar-button-right"></a>';
			scrollbar += '</div>'; // .ui-scrollbar
			
			this._scrollbarContents.css({position: 'absolute', left: 0})
			this.element.prepend(scrollbar);

			// cache references to scrollbar pieces
			this._scrollbar = $('.ui-scrollbar', this.element);
			this._scrollbarTrack = $('.ui-scrollbar-track', this.element);
			this._scrollbarHandle = $('.ui-scrollbar-handle', this.element);
		
			// Bind Events
			this._scrollbarTrack.mousedown(function(event) { return self._start(event); });
			this.element.mousewheel(function(event, delta) { return self._scrollWheelHorizontal(event, delta); });
			
			this.element.find('.ui-scrollbar-button-left').mousedown(function(event) { return self._goLeft(); }).mouseup(function() { return self._clearTimer(); }).click(function(){ return false; });
			this.element.find('.ui-scrollbar-button-right').mousedown(function(event) { return self._goRight(); }).mouseup(function() { return self._clearTimer(); }).click(function(){ return false; });
		}
	},

	/**
	 * Event handler - mousedown
	 * Called when mouse is clicked in scrollbar or scrollbar handle
	 * @params event (jQuery Event Object)
	 */
	_start: function(event)
	{
		var self = this;
		
		$('html').mouseup(function(event) { return self._stop(event); }).mousemove(function(event) { return self._drag(event); }).mouseleave(function(event) { return self._stop(event); });

		// scroll contents if clicked in scrollbar track and not handle
		if (event.target == this._scrollbarTrack.get(0))
		{
			var offset = this._relativeMousePosition(event);

			if (this.options.vertical)
			{
				this._scrollVertical(offset);
			}
			else
			{
				this._scrollHorizontal(offset);
			}
		}
		else
		{
			this._handleOffset = this._calcHandleOffset(event);
		}

		this._isDragging = true;

		return false;
	},

	/**
	 * Event Handler - mousemove
	 * Called while mouse is moving after startDrag event
	 */
	_drag: function(event)
	{
		if (this._isDragging)
		{
			var position = this._relativeMousePosition(event) - this._handleOffset;
			
			if (this.options.vertical)
			{
				this._scrollVertical(position);
			}
			else
			{
				this._scrollHorizontal(position);
			}
		}

		return false;
	},

	/**
	 * Event Handler - mouseup
	 * unbind events and change state
	 */
	_stop: function(event)
	{
		if (this._isDragging)
		{
			// unbind events when dragging ends
			$('html').unbind('mouseup').unbind('mousemove').unbind('mouseleave');

			this._isDragging = false;
		}
		
		return false;
	},
	
	/**
	 * Update scrollbar vertical
	 */
	updateScrollBarVertical: function()
	{
		var contentsHeight = this._scrollbarContents.outerHeight(true);
		this._contentsHeight = contentsHeight;
		var windowHeight = this._windowHeight;
		
		if (contentsHeight > windowHeight)
		{
			var trackHeight = windowHeight - (this.options.buttonHeight * 2);
			var handleHeight = (windowHeight / contentsHeight) * trackHeight;
		
			this._scrollbarTrack.css({height: parseInt(trackHeight)});		
			this._scrollbarHandle.css({height: parseInt(handleHeight)});
		}
		else
		{
			this._scrollbarHandle.css('height', this._scrollbarTrack.height());
		}
		
		this._scrollVertical(0);
	},
	
	/**
	 * Function the actually scrolls the content and positions the handle (Vertically)
	 */
	_scrollVertical: function(top)
	{
		if (this._contentsHeight != this._scrollbarContents.outerHeight(true))
		{
			this.updateScrollBarVertical();
		}
	
		if (top < 0)
		{
			top = 0;
		}
		else if (top >= (this._scrollbarTrack.height() - this._scrollbarHandle.height()))
		{
			top = this._scrollbarTrack.height() - this._scrollbarHandle.height();
		}

		// Set handle position
		this._scrollbarHandle.css({top: top});

		// Scroll the contents
		this._scrollContentsVertical(top);
	},
	
	/**
	 * Function the actually scrolls the content and positions the handle (Horizontally)
	 */
	_scrollHorizontal: function(left)
	{
		if (left < 0)
		{
			left = 0;
		}
		else if (left >= (this._scrollbarTrack.width() - this._scrollbarHandle.width()))
		{
			left = this._scrollbarTrack.width() - this._scrollbarHandle.width();
		}

		// Set handle position
		this._scrollbarHandle.css({left: left});

		// Scroll the contents
		this._scrollContentsHorizontal(left);
	},

	/**
	 * Scroll content pane vertically
	 */
	_scrollContentsVertical: function(top)
	{
		var scrollbar_height = this._scrollbarTrack.height() - this._scrollbarHandle.height();
		var percent = top / scrollbar_height;
		var contents_top = (this._contentsHeight - this.element.height()) * percent * -1;

		this._scrollbarContents.css({top: contents_top});
	},

	/**
	 * Scroll content pane horizontally
	 */
	_scrollContentsHorizontal: function(left)
	{
		var scrollbar_width = this._scrollbarTrack.width() - this._scrollbarHandle.width();
		var percent = left / scrollbar_width;
		var contents_left = (this._contentsWidth - this.element.width()) * percent * -1;

		this._scrollbarContents.css({left: contents_left});
	},

	/**
	 * Handle moving left by clicking scrollbar arrow
	 */
	_goLeft: function ()
	{
		var self = this;
		
		// Scroll once immediately
		this._scrollHorizontal(parseInt(this._scrollbarHandle.css('left')) - this.options.scrollInterval);
		
		this._timer = setInterval(function() {
			self._scrollHorizontal(parseInt(self._scrollbarHandle.css('left')) - self.options.scrollInterval);
		}, 50);
		
		return false;
	},
	
	/**
	 * Handle moving right by clicking scrollbar arrow
	 */
	_goRight: function ()
	{
		var self = this;
		
		// Scroll once immediately
		this._scrollHorizontal(parseInt(this._scrollbarHandle.css('left')) + this.options.scrollInterval);
		
		// Setup timer to keep scrolling while mouse is down
		this._timer = setInterval(function() {
			self._scrollHorizontal(parseInt(self._scrollbarHandle.css('left')) + self.options.scrollInterval);
		}, 50);
		
		return false;
	},
	
	/**
	 * Handle moving up by clicking scrollbar arrow
	 */
	_goUp: function ()
	{
		var self = this;
		
		// Scroll once immediately
		this._scrollVertical(parseInt(this._scrollbarHandle.css('top')) - this.options.scrollInterval);
		
		this._timer = setInterval(function() {
			self._scrollVertical(parseInt(self._scrollbarHandle.css('top')) - self.options.scrollInterval);
		}, 50);
		
		return false;
	},
	
	/**
	 * Handle moving down by clicking scrollbar arrow
	 */
	_goDown: function ()
	{
		var self = this;
		
		// Scroll once immediately
		this._scrollVertical(parseInt(this._scrollbarHandle.css('top')) + this.options.scrollInterval);
		
		// Setup timer to keep scrolling while mouse is down
		this._timer = setInterval(function() {
			self._scrollVertical(parseInt(self._scrollbarHandle.css('top')) + self.options.scrollInterval);
		}, 50);
		
		return false;
	},
	
	_clearTimer: function ()
	{
		clearInterval(this._timer);
		
		return false;
	},
	
	/**
	 * Event Handler - mousewheel (vertical)
	 * respond to mousewheel moving
	 */
	_scrollWheelVertical: function(event, delta)
	{
		var top;

		if (delta > 0)
		{
			top = parseInt(this._scrollbarHandle.css('top'));
			top -= (delta * this.options.scrollInterval);
		}
		else
		{
			top = parseInt(this._scrollbarHandle.css('top'));
			top += (delta * -1 * this.options.scrollInterval);
		}

		this._scrollVertical(top);

		return false;
	},

	/**
	 * Event Handler - mousewheel (horizontal)
	 * respond to mousewheel moving
	 */
	_scrollWheelHorizontal: function(event, delta)
	{
		var left;

		if (delta > 0)
		{
			left = parseInt(this._scrollbarHandle.css('left'));
			left -= (delta * this.options.scrollInterval);
		}
		else
		{
			left = parseInt(this._scrollbarHandle.css('left'));
			left += (delta * -1 * this.options.scrollInterval);
		}

		this._scrollHorizontal(left);

		return false;
	},
	
	/**
	 * Calculate the absolute mouse position in the window to the relative position in the scrollbar
	 */
	_relativeMousePosition: function(event)
	{
		if (this.options.vertical)
		{
			return event.pageY - this.element.offset().top - this.options.buttonWidth;
		}
		else
		{
			return event.pageX - this.element.offset().left - this.options.buttonWidth;
		}
	},

	/**
	 * Calculate where the mouse is clicked within the scroll handle
	 */
	_calcHandleOffset: function(event)
	{
		var position = (this.options.vertical) ? 'top' : 'left';
		return this._relativeMousePosition(event) - parseInt(this._scrollbarHandle.css(position));
	}
});

})(jQuery);

$.extend($.ui.scrollbar, {
	version: "1.7.1",
	defaults: {
		vertical: true,
		buttonWidth: 18,
		buttonHeight: 18,
		scrollInterval: 30,
		overflow: 'hidden'
	}
});


/*! Copyright (c) 2009 Brandon Aaron (http://brandonaaron.net)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
 * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
 *
 * Version: 3.0.2
 * 
 * Requires: 1.2.2+
 */

(function($) {

var types = ['DOMMouseScroll', 'mousewheel'];

$.event.special.mousewheel = {
	setup: function() {
		if ( this.addEventListener )
			for ( var i=types.length; i; )
				this.addEventListener( types[--i], handler, false );
		else
			this.onmousewheel = handler;
	},
	
	teardown: function() {
		if ( this.removeEventListener )
			for ( var i=types.length; i; )
				this.removeEventListener( types[--i], handler, false );
		else
			this.onmousewheel = null;
	}
};

$.fn.extend({
	mousewheel: function(fn) {
		return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
	},
	
	unmousewheel: function(fn) {
		return this.unbind("mousewheel", fn);
	}
});


function handler(event) {
	var args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true;
	
	event = $.event.fix(event || window.event);
	event.type = "mousewheel";
	
	if ( event.wheelDelta ) delta = event.wheelDelta/120;
	if ( event.detail     ) delta = -event.detail/3;
	
	// Add events and delta to the front of the arguments
	args.unshift(event, delta);

	return $.event.handle.apply(this, args);
}

})(jQuery);