(function ($) {
	
	/**
	 * Scrollbar
	 * 
	 * @constructor
	 * @param {Object} node HTMLNode
	 * @param {Object} options Options
	 */
	function ScrollBar (node, options) {
		this.options = $.extend({
			orientation: 'horizontal',
			tick: 24,
			value_min: 0,
			value_max: 100,
			drag_offset_start: -8,
			drag_offset_end: 10
		}, options || {});
		
		if (this.options.orientation == 'vertical') {
			this.k_start = 'top';
			this.k_size = 'height';
			this.k_mouse = 'clientY';
		}
		
		this.node = $(node);
		this.node_drag = this.node.find('.drag');
		
		this.node_drag.bind('selectstart', function () { return false; });
		this.node_drag.bind('mousedown', $.proxy(this._down, this));
		$(document.body).bind('mouseup', $.proxy(this._up, this));
		
		this._moveProx = $.proxy(this._move, this);
		
		this.pos = {
			min: this.options.drag_offset_start,
			max: this.node[this.k_size]() - this.options.drag_offset_end,
			pos: 0
		};
		
		this.mouse = {
			min: 0,
			max: 0,
			correction: 0,
			last: 0
		};
		
		window.scrollb = this;
	}
	
	ScrollBar.prototype = {
		options: null,
		
		node: null,
		node_drag: null,
		
		dragging: false,
		
		interval: null,
		pos: null,
		mouse: null,
		
		value: 0,
		
		k_mouse: 'clientX',
		k_start: 'left',
		k_size: 'width',
		
		_down: function (evt) {
			//IE reports button "1", not "0"
			if (!this.dragging && (evt.button == 0 || ($.browser.msie && evt.button == 1))) {
				var offset = this.node.offset();
				var offset_drag = this.node_drag.offset();
				
				this.mouse.min = offset[this.k_start] + this.pos.min;
				this.mouse.max = offset[this.k_start] + this.pos.max;
				this.mouse.correction = evt[this.k_mouse] - offset_drag[this.k_start];
				
				var p = evt[this.k_mouse] - this.mouse.correction;
				this.mouse.last = Math.min(Math.max(p, this.mouse.min), this.mouse.max);
				
				this.interval = setInterval($.proxy(this._tick, this), this.options.tick);
				this.dragging = true;
				
				this.node.trigger('scrollstart');
				$(document.body).bind('mousemove', this._moveProx);
			}
			
			return false;
		},
		
		_tick: function () {
			if (this.dragging) {
				var pos = (this.mouse.last - this.mouse.min) / (this.mouse.max - this.mouse.min);
				if (pos != this.pos.pos) {
					this.pos.pos = pos;
					
					var p = pos * (this.pos.max - this.pos.min) + this.pos.min;
					this.node_drag.css(this.k_start, p + 'px');
					
					this.value = (this.options.value_max - this.options.value_min) * pos + this.options.value_min;
					
					this.node.trigger('scroll', {value: this.value, percentage: pos});
				}
			}
		},
		
		_moveProx: null,
		_move: function (evt) {
			var p = evt[this.k_mouse] - this.mouse.correction;
			this.mouse.last = Math.min(Math.max(p, this.mouse.min), this.mouse.max);
		},
		
		_up: function () {
			if (this.dragging) {
				clearInterval(this.interval);
				
				this.node.trigger('scrollend');
				$(document.body).unbind('mousemove', this._moveProx);
				
				this.pos.pos = this.pos.last;
				this.dragging = false;
				
				return false;
			}
		},
		
		sync: function (pos) {
			var p = pos * (this.pos.max - this.pos.min) + this.pos.min;
			this.node_drag.css(this.k_start, p + 'px');
		}
	};
	
	
	/*
	 * jQuery plugin
	 */
	$.fn.scrollbar = function (options) {
		var self = $(this);
		
		self.each(function () {
			if (!$(this).data('scrollbar')) {
				$(this).data('scrollbar', new ScrollBar($(this), options));
			}
		});
		
		return self;
	};
	
})(jQuery);
