(function($){
	
	$.fn.slider = function(options) {
		
		var defaults = {
			range:[0,1],
			snap:0,
			value:0,
			keyable:false
		}
		
		var opts = $.extend(defaults,options);
		opts.value = Math.max(Math.min(opts.value,opts.range[1]),opts.range[0]);
		
		return $(this).each(function() {
			
			var $this = $(this);
			var timer = null;
			
			var Utils = {
				cancel:function(e){e.preventDefault();return false;}	
			}
			
			var Slider = {
				
				dragging:false,
				
				disabled:false,
				
				offset:0,
				
				dx:0,
				
				init:function() {
					$this.append('<div class="slider_track"><div class="slider_track_highlight"></div><div class="slider_handle"><a class="slider_handle_inner" href="#"></a></div></div>')
							.find('div.slider_handle')
								.mousedown(function(e){return Slider.mousedown(e);})
								.click(function(){return false;})
								.bind('drag',Utils.cancel)
								.css({left:100*(opts.value-opts.range[0])/(opts.range[1]-opts.range[0])+'%'})
								.find('a.slider_handle_inner')
									.focus(function(){$this.trigger('slider.start');})
									.keypress(function(e){opts.keypressable=true;if(opts.keyable)Slider.keymove(e);})
									.keyup(function(e){if(opts.keyable&&!opts.keypressable)Slider.keymove(e);})
									.blur(function(e){$this.trigger('slider.stop',Slider.value);})
								.end()
							.end()
							.find('div.slider_track')
								.click(function(e){
									if(!Slider.disabled){
										Slider.mousemove(e,true);
										$this.trigger('slider.stop',Slider.value);
									}
								});
					
					if(opts.keyable) {
						$this.find('a.slider_handle_inner').attr('tabindex',0);
					}
					
					this.offset = $this.offset().left;
								
					$(document).mouseup(function(){return Slider.mouseup();})
								.mousemove(function(e){return Slider.mousemove(e);});
								
					$this.bind('slider.set',function(e,v){Slider.set(v);})
							.bind('slider.bounds.lower',function(e,v){opts.range[0]=v;Slider.set(Slider.value);})
							.bind('slider.bounds.upper',function(e,v){opts.range[1]=v;Slider.set(Slider.value);})
							.bind('slider.disable',function(){Slider.disable();})
							.bind('slider.enable',function(){Slider.enable();});
					
					Slider.set(opts.value);
					
					$this.trigger('slider.ready')
				},
				
				mousedown:function(e) {
					if(!this.disabled) {
						this.dragging = true;
						this.dx = e.pageX - $this.find('div.slider_handle').offset().left + parseInt($this.find('div.slider_handle').css('marginLeft'));
						$this.trigger('slider.start');
					}
					return false;
				},
				
				mouseup:function(e) {
					if(this.dragging) {
						this.dragging = false;
						$this.trigger('slider.stop',this.value);
					}
				},
				
				mousemove:function(e,force) {
					if(this.dragging || force) {
						var p = e.pageX - this.offset - this.dx,
							w = $('div.slider_track',$this).width(),
							padl = parseInt($('div.slider_track',$this).css('paddingLeft')),
							padr = parseInt($('div.slider_track',$this).css('paddingRight'));
						
						p = Math.max(padl,p);
						p = Math.min(w+padl,p);
						
						if(opts.snap) {
							p = w*Math.round(opts.snap*p/w)/opts.snap;
						}
						
						this.value = opts.range[0] + (opts.range[1] - opts.range[0])*p/w;
						
						$this.find('div.slider_handle').css({left:p + padl +'px'});
						$this.find('div.slider_track_highlight').css({width:p + padl +'px'});
						$this.trigger('slider.move',this.value);
					}
					return false;
				},
				
				keymove:function(e) {
					var d = 0,
						v = this.value,
						key = e.charCode||e.keyCode;
						
					switch(key) {
						case 37:
							d = -1;
							break;
						case 39:
							d = 1;
							break;
					}
					if(opts.snap) {
						v += d*(opts.range[1]-opts.range[0])/opts.snap;	
					}
					else {
						v += d*(opts.range[1]-opts.range[0])/10;
					}
					this.set(v,true);
					$this.trigger('slider.keymove',v);
					clearTimeout(timer);
					timer = setTimeout(function(){$this.trigger('slider.stop',v);},1000);
				},
				
				set:function(v,jump) {
					if(!this.disabled && !isNaN(v)) {
						v = Math.max(Math.min(opts.range[1],v),opts.range[0]);
						if(opts.snap) {
							if(opts.range[0] != opts.range[1]) {
								v = opts.range[0] + (opts.range[1]-opts.range[0])*Math.ceil(opts.snap*(v-opts.range[0])/(opts.range[1]-opts.range[0]))/opts.snap;
							}
							else {
								v = opts.range[0];
							}
						}
						var w = $('div.slider_track',$this).width(),
							pad = parseInt($('div.slider_track',$this).css('paddingLeft'));
							if(opts.range[0] != opts.range[1]) {
								var left = w*(v-opts.range[0])/(opts.range[1]-opts.range[0]) + pad + 'px';
							}
							else {
								var left = pad + 'px';
							}
						if(!jump) {
							$this.find('div.slider_handle').animate({left:left});
							$this.find('div.slider_track_highlight').animate({width:left});
						}
						else {
							$this.find('div.slider_handle').css({left:left});
							$this.find('div.slider_track_highlight').css({width:left});
						}
						this.value = v;
						$this.trigger('slider.move',this.value);
					}
				},
				
				disable:function() {
					$this.addClass('disabled');
					Slider.disabled = true;
				},
				
				enable:function() {
					$this.removeClass('disabled');
					Slider.disabled = false;
				}
				
			}
			
			Slider.init();
			
		});
		
	}
	
	
})(jQuery);