(function($) {



	$.fn.extend({

		slider: function(options) {

			var args = Array.prototype.slice.call(arguments, 1);

			

			if ( options == "value" )

				return $.data(this[0], "ui-slider").value(arguments[1]);

			

			return this.each(function() {

				if (typeof options == "string") {

					var slider = $.data(this, "ui-slider");

					slider[options].apply(slider, args);



				} else if(!$.data(this, "ui-slider"))

					new $.ui.slider(this, options);

			});

		}

	});

	

	$.ui.slider = function(element, options) {



		//Initialize needed constants

		var self = this;

		this.element = $(element);

		$.data(element, "ui-slider", this);

		this.element.addClass("ui-slider");

		

		//Prepare the passed options

		this.options = $.extend({}, options);

		var o = this.options;

		$.extend(o, {

			axis: o.axis || (element.offsetWidth < element.offsetHeight ? 'vertical' : 'horizontal'),

			maxValue: !isNaN(parseInt(o.maxValue,10)) ? parseInt(o.maxValue,10) :  100,

			minValue: parseInt(o.minValue,10) || 0,

			startValue: parseInt(o.startValue,10) || 'none'		

		});

		

		//Prepare the real maxValue

		o.realMaxValue = o.maxValue - o.minValue;

		

		//Calculate stepping based on steps

		o.stepping = parseInt(o.stepping,10) || (o.steps ? o.realMaxValue/o.steps : 0);

		

		$(element).bind("setData.slider", function(event, key, value){

			self.options[key] = value;

		}).bind("getData.slider", function(event, key){

			return self.options[key];

		});



		//Initialize mouse and key events for interaction

		this.handle = o.handle ? $(o.handle, element) : $('> *', element);

		$(this.handle)

			.mouseInteraction({

				executor: this,

				delay: o.delay,

				distance: o.distance || 0,

				dragPrevention: o.prevention ? o.prevention.toLowerCase().split(',') : ['input','textarea','button','select','option'],

				start: this.start,

				stop: this.stop,

				drag: this.drag,

				condition: function(e, handle) {

					if(!this.disabled) {

						if(this.currentHandle) this.blur(this.currentHandle);

						this.focus(handle,1);

						return !this.disabled;

					}

				}

			})

			.wrap('<a href="javascript:void(0)"></a>')

			.parent()

				.bind('focus', function(e) { self.focus(this.firstChild); })

				.bind('blur', function(e) { self.blur(this.firstChild); })

				.bind('keydown', function(e) {

					if(/(37|39)/.test(e.keyCode))

						self.moveTo((e.keyCode == 37 ? '-' : '+')+'='+(self.options.stepping ? self.options.stepping : (self.options.realMaxValue / self.size)*5),this.firstChild);

				})

		;

		

		//Position the node

		if(o.helper == 'original' && (this.element.css('position') == 'static' || this.element.css('position') == '')) this.element.css('position', 'relative');

		

		//Prepare dynamic properties for later use

		if(o.axis == 'horizontal') {

			this.size = this.element.outerWidth();

			this.properties = ['left', 'width'];

		} else {

			this.size = this.element.outerHeight();

			this.properties = ['top', 'height'];

		}

		

		//Bind the click to the slider itself

		this.element.bind('click', function(e) { self.click.apply(self, [e]); });

		

		//Move the first handle to the startValue

		if(!isNaN(o.startValue)) this.moveTo(o.startValue, 0);

		

		//If we only have one handle, set the previous handle to this one to allow clicking before selecting the handle

		if(this.handle.length == 1) this.previousHandle = this.handle;

		

		

		if(this.handle.length == 2 && o.range) this.createRange();

	

	};

	

	$.extend($.ui.slider.prototype, {

		plugins: {},

		createRange: function() {

			this.rangeElement = $('<div></div>')

				.addClass('ui-slider-range')

				.css({ position: 'absolute' })

				.css(this.properties[0], parseInt($(this.handle[0]).css(this.properties[0]),10) + this.handleSize(0)/2)

				.css(this.properties[1], parseInt($(this.handle[1]).css(this.properties[0]),10) - parseInt($(this.handle[0]).css(this.properties[0]),10))

				.appendTo(this.element);

		},

		updateRange: function() {

				this.rangeElement.css(this.properties[0], parseInt($(this.handle[0]).css(this.properties[0]),10) + this.handleSize(0)/2);

				this.rangeElement.css(this.properties[1], parseInt($(this.handle[1]).css(this.properties[0]),10) - parseInt($(this.handle[0]).css(this.properties[0]),10));

		},

		getRange: function() {

			return this.rangeElement ? this.convertValue(parseInt(this.rangeElement.css(this.properties[1]),10)) : null;

		},

		ui: function(e) {

			return {

				instance: this,

				options: this.options,

				handle: this.currentHandle,

				value: this.value(),

				range: this.getRange()

			};

		},

		propagate: function(n,e) {

			$.ui.plugin.call(this, n, [e, this.ui()]);

			this.element.triggerHandler(n == "slide" ? n : "slide"+n, [e, this.ui()], this.options[n]);

		},

		destroy: function() {

			this.element

				.removeClass("ui-slider ui-slider-disabled")

				.removeData("ul-slider")

				.unbind(".slider");

			this.handles.removeMouseInteraction();

		},

		enable: function() {

			this.element.removeClass("ui-slider-disabled");

			this.disabled = false;

		},

		disable: function() {

			this.element.addClass("ui-slider-disabled");

			this.disabled = true;

		},

		focus: function(handle,hard) {

			this.currentHandle = $(handle).addClass('ui-slider-handle-active');

			if(hard) this.currentHandle.parent()[0].focus();

		},

		blur: function(handle) {

			$(handle).removeClass('ui-slider-handle-active');

			if(this.currentHandle && this.currentHandle[0] == handle) { this.previousHandle = this.currentHandle; this.currentHandle = null; };

		},

		value: function(handle) {

			if(this.handle.length == 1) this.currentHandle = this.handle;

			return ((parseInt($(handle != undefined ? this.handle[handle] || handle : this.currentHandle).css(this.properties[0]),10) / (this.size - this.handleSize())) * this.options.realMaxValue) + this.options.minValue;

		},

		convertValue: function(value) {

			return (value / (this.size - this.handleSize())) * this.options.realMaxValue;

		},

		translateValue: function(value) {

			return ((value - this.options.minValue) / this.options.realMaxValue) * (this.size - this.handleSize());

		},

		handleSize: function(handle) {

			return $(handle != undefined ? this.handle[handle] : this.currentHandle)['outer'+this.properties[1].substr(0,1).toUpperCase()+this.properties[1].substr(1)]();	

		},

		click: function(e) {

		

			// This method is only used if:

			// - The user didn't click a handle

			// - The Slider is not disabled

			// - There is a current, or previous selected handle (otherwise we wouldn't know which one to move)

			var pointer = [e.pageX,e.pageY];

			var clickedHandle = false; this.handle.each(function() { if(this == e.target) clickedHandle = true;  });

			if(clickedHandle || this.disabled || !(this.currentHandle || this.previousHandle)) return;



			//If a previous handle was focussed, focus it again

			if(this.previousHandle) this.focus(this.previousHandle, 1);

			

			//Move focussed handle to the clicked position

			this.offset = this.element.offset();

			this.moveTo(this.convertValue(e[this.properties[0] == 'top' ? 'pageY' : 'pageX'] - this.offset[this.properties[0]] - this.handleSize()/2));

			

		},

		start: function(e, handle) {

			

			var o = this.options;

			

			this.offset = this.element.offset();

			this.handleOffset = this.currentHandle.offset();

			this.clickOffset = { top: e.pageY - this.handleOffset.top, left: e.pageX - this.handleOffset.left };

			this.firstValue = this.value();

			

			this.propagate('start', e);

			return false;

						

		},

		stop: function(e) {

			this.propagate('stop', e);

			if(this.firstValue != this.value()) this.propagate('change', e);

			return false;

		},

		drag: function(e, handle) {



			var o = this.options;

			var position = { top: e.pageY - this.offset.top - this.clickOffset.top, left: e.pageX - this.offset.left - this.clickOffset.left};



			var modifier = position[this.properties[0]];			

			if(modifier >= this.size - this.handleSize()) modifier = this.size - this.handleSize();

			if(modifier <= 0) modifier = 0;

			

			if(o.stepping) {

				var value = this.convertValue(modifier);

				value = Math.round(value / o.stepping) * o.stepping;

				modifier = this.translateValue(value);	

			}



			if(this.rangeElement) {

				if(this.currentHandle[0] == this.handle[0] && modifier >= this.translateValue(this.value(1))) modifier = this.translateValue(this.value(1));

				if(this.currentHandle[0] == this.handle[1] && modifier <= this.translateValue(this.value(0))) modifier = this.translateValue(this.value(0));

			}	

			

			this.currentHandle.css(this.properties[0], modifier);

			if(this.rangeElement) this.updateRange();

			this.propagate('slide', e);

			return false;

			

		},

		moveTo: function(value, handle) {



			var o = this.options;

			if(handle == undefined && !this.currentHandle && this.handle.length != 1) return false; //If no handle has been passed, no current handle is available and we have multiple handles, return false

			if(handle == undefined && !this.currentHandle) handle = 0; //If only one handle is available, use it

			if(handle != undefined) this.currentHandle = this.previousHandle = $(this.handle[handle] || handle);



			if(value.constructor == String) value = /\-\=/.test(value) ? this.value() - parseInt(value.replace('-=', ''),10) : this.value() + parseInt(value.replace('+=', ''),10);

			if(o.stepping) value = Math.round(value / o.stepping) * o.stepping;

			value = this.translateValue(value);



			if(value >= this.size - this.handleSize()) value = this.size - this.handleSize();

			if(value <= 0) value = 0;

			if(this.rangeElement) {

				if(this.currentHandle[0] == this.handle[0] && value >= this.translateValue(this.value(1))) value = this.translateValue(this.value(1));

				if(this.currentHandle[0] == this.handle[1] && value <= this.translateValue(this.value(0))) value = this.translateValue(this.value(0));

			}

			

			this.currentHandle.css(this.properties[0], value);

			if(this.rangeElement) this.updateRange();

			

			this.propagate('start', null);

			this.propagate('stop', null);

			this.propagate('change', null);



		}

	});



})(jQuery);