(function($) { $.fn.sliderbar = function(options) {
	
	var defaults = {
		speed: 500,
		steps: null,
		orientation: 'horizontal',
		ticks: true,
		classPrefix: 'sliderBar',
		slide: null,
		before: null,
		after: null,
		change: null
	};
	var options = $.extend(defaults, options);
	
	return this.each(function() {
		var $node = $(this);
		$node.addClass('sliderBar ' + options.classPrefix + ' ' + options.classPrefix + '_' + options.orientation);
		$node.wrap($('<div class="sliderBarContainer ' + options.classPrefix + 'Container ' + options.classPrefix + 'Container' + '_' + options.orientation + '" />'));
		var $container = $node.parent()
		var $handle = $('<span class="sliderBarHandle '+options.classPrefix+'Handle"/>');
		$node.append($handle);
		$node.sliding = false;
		
		// Some default options
		options.width = $node.width();
		options.height = $node.height();
		options.dragging = false;
		options.stepPositions = Array();
		options.current = 0;
		options.handleSize = {
			w: $handle.outerWidth(),
			h: $handle.outerHeight(),
			cl: ($handle.outerWidth() / 2),
			ct: ($handle.outerHeight() / 2)
		};
		
		if(options.orientation == 'horizontal') {
			$handle.css('left', -options.handleSize.cl);
		} else if(options.orientation == 'vertical') {
			$handle.css('top', -options.handleSize.ct);
		}
		
		if(options.steps) {
			options.steps = parseInt(options.steps);
			if(options.steps > 1) {
				var stepLength = (options.orientation == 'horizontal' ? options.width : options.height) / options.steps;
				var snapOffset = 5;
				for(var i = 0; i < options.steps + 1; i++) {
					options.stepPositions.push(i * stepLength);
				}
			}
		}
		
		var $tickContainer = null;
		if(options.ticks) {
			$tickContainer = $('<div class="sliderBarTicks '+options.classPrefix+'Ticks" style="position: relative;" />');
			$container.append($tickContainer);
			// <span class="celSliderTick" style="position: absolute;  <?= $direction . ": " . $i * $percent ?>%;">&nbsp;</span>
			for(var s in options.stepPositions) {
				var style = "left: " + options.stepPositions[s] + "px;";
				if(options.orientation == 'vertical') {
					style = "top: " + options.stepPositions[s] + "px;";
				}
				$('<span class="celSliderTick" style="position: absolute; ' + style + '"></span>')
					.appendTo($tickContainer);
			}
		}
		
		$node.getNearestNumber = function(a, n) {
			if((l = a.length) < 2)
				return(l - 1);
			for(var l, p = Math.abs(a[--l] - n); l--;)
				if(p < (p = Math.abs(a[l] - n)))
					break;
			return(l + 1);
		}
		
		$node.getValue = function(evt) {
			var value = null;
			if(options.orientation == 'horizontal') {
				var x = evt.pageX - $node.offset().left;
			 	value = $node.getNearestNumber(options.stepPositions, x);
			} else if(options.orientation == 'vertical') {
				var y = evt.pageY - $node.offset().top;
				value = $node.getNearestNumber(options.stepPositions, y);
			}
			return(value);
		}
		$node.getHandlePosition = function(evt) {
			var pos = null;
			if(options.orientation == 'horizontal') {
				pos = evt.pageX - $node.offset().left - ($handle.outerWidth() / 2);
			} else if(options.orientation = 'vertical') {
				pos = evt.pageY - $node.offset().top - ($handle.outerHeight() / 2);
			}
			return(pos);
		}
		
		// Bind events
		$node.bind('slideTo', function(evt, value) {
			if(options.dragging || options.sliding)
				return;
			
			options.sliding = true;
			
			if($.isFunction(options.slide)) {
				options.slide(evt, value);
			}
			
			if(options.orientation == 'horizontal') {
				$handle.stop().animate({
					left: options.stepPositions[value] - options.handleSize.cl
				}, options.speed, function() {
					$node.trigger('change', value);
					options.sliding = false;
				});
			} else if(options.orientation == 'vertical') {
				$handle.stop().animate({
					top: options.stepPositions[value] - options.handleSize.ct
				}, options.speed, function() {
					$node.trigger('change', value);
					options.sliding = false;
				});
			}
		});
		
		$node.bind('change', function(evt, value) {
			if($.isFunction(options.change)) {
				options.change(evt, value);
			}
		});

		$container.mousedown(function(evt) {
			var value = null;
			if($.isFunction(options.before)) {
				if(options.steps) {
					value = $node.getValue(evt);
				}
				options.before(evt, value);
			}			

			value = $node.getHandlePosition(evt);
			if(options.steps) {
				value = $node.getValue(evt);
				if(value !== null)
					$node.trigger('slideTo', value.toString());
				return;
			}
			if(options.orientation == 'horizontal') {
				$handle.stop().animate({
					left: value - options.handleSize.cl
				}, options.speed);
			} else if(options.orientation == 'vertical') {
				$handle.stop().animate({
					top: value - options.handleSize.ct
				}, options.speed);
			}
		});
		
		$handle.mousedown(function(evt) {
			evt.preventDefault();
			options.dragging = true;
		});
		
		$('body').mouseup(function(evt) {
			evt.preventDefault();
			var value = null;
			
			if($.isFunction(options.after)) {
				if(options.steps) {
					value = $node.getValue(evt);
				}
				options.after(evt, value);
			}
			if(options.steps && options.dragging) {
				value = $node.getValue(evt);
				options.dragging = false;
				
				if(options.orientation == 'horizontal') {
					$handle.stop().animate({
						left: options.stepPositions[value] - options.handleSize.cl
					}, options.speed, function() {
						$node.trigger('change', value);
						options.sliding = false;
					});
				} else if(options.orientation == 'vertical') {
					$handle.stop().animate({
						top: options.stepPositions[value] - options.handleSize.ct
					}, options.speed, function() {
						$node.trigger('change', value);
						options.sliding = false;
					});
				}
			}
		});
		
		$('body').mousemove(function(evt) {
			if(!options.dragging)
				return;
			
			if($.isFunction(options.slide)) {
				var pos = $node.getHandlePosition(evt);				
				if(options.steps) {
					var value = $node.getNearestNumber(options.stepPositions, pos);
					if(options.current != value) {
						options.slide(evt, value);
						options.current = value;
					}
				} else
					options.slide(evt, pos);
			}
			
			if(options.orientation == 'horizontal') {
				var left = evt.pageX - $node.offset().left - ($handle.outerWidth() / 2);
				if(left >= -options.handleSize.cl && left <= (options.width - options.handleSize.cl))
					$handle.css('left', left);
			} else if(options.orientation == 'vertical') {
				var top = evt.pageY - $node.offset().top - ($handle.outerHeight() / 2);
				if(top >= -options.handleSize.ct && top <= (options.height - options.handleSize.ct))
					$handle.css('top', top);
			}
		});
		
		$handle.hover(function() {
			$(this).addClass('handleOver');
		}, function() {
			$(this).removeClass('handleOver');
		});
		
		$node.hover(function() {
			$(this).addClass('sliderBarOver');
		}, function() {
			$(this).removeClass('sliderBarOver');
		});
	});
	
}})(jQuery);
