if(!window.Richfaces) window.Richfaces = {};
Richfaces.Slider = Class.create();
Richfaces.Slider.prototype = {
	initialize: function(track, handle, input, options) {
		this.track	= $(track);
		this.handle = $(handle);
		this.input	= $(input) || document.getElementsByName(input)[0];

		this.options= options || {};
		
		this.classes = {};
		
		var tempClassName = this.handle.className;
		var baseClassName = this.trim(tempClassName.replace("sliderHandle",""));
		this.classes.handle = "sliderHandle " + baseClassName;
		this.classes.handleSelected = "sliderHandle sliderHandleSelected " + baseClassName + " " + this.options.handleSelectedClass;

		this.input.value = this.options.sliderValue;
		this.prevInputValue = this.input.value;
		this.range	 = this.options.range || $R(0,1);
		this.value	 = 0;
		this.minimum = this.options.minimum || this.range.start;
		this.maximum = this.options.maximum || this.range.end;
		this.digCount = 0;
		
		//Disable tip
		//this.labels	 = this.options.labels;

		this.step = this.options.step;
		if ( (this.step+"").indexOf(".")!=-1 ){
			var stepStr = (this.step+"");
			this.digCount = (stepStr.substring(stepStr.indexOf(".")+1,stepStr.length)).length;
		}
		this.availableValues = this.calculateAvailableValues();

		//Disable tip
		//this.tip.maxlength = (this.maximum + "").length + (this.digCount != 0 ? this.digCount + 1 : 0);

		this.trackWidth = this.options.trackWidth; // this is the width of the slider track.
		
		this.trackOffset = this.options.trackOffset; // this is the left offset of the slider track
		
		this.handleWidth = this.options.handleWidth; // this is the width of the slider handle.
		
		this.emptyValueHandleOffset = this.options.emptyValueHandleOffset; // this is the offset of the handle when value is empty
		
		this.active	 = false;
		this.dragging = false;
		this.editInFocus = false;

		this.disabled = this.options.disabled ? true : false;

		this.prevMouseUp = window.document.onmouseup;
		this.prevMouseMove = window.document.onmousemove;

		this.documentBodyOload	= this.load.bindAsEventListener(this);
		Event.observe(window, "load", this.documentBodyOload);

		this.eventWindowResized = this.windowResized.bindAsEventListener(this);
		Event.observe(window, "resize", this.eventWindowResized);

		if(!this.options.disabled){
			//this.eventMouseUp		= this.endDrag.bindAsEventListener(this);
			this.eventMouseUp		= this.processMouseUp.bindAsEventListener(this);
			this.eventMouseMove		= this.update.bindAsEventListener(this);
			this.eventMouseDown		= this.startDrag.bindAsEventListener(this);
			this.eventEditFocus		= this.editFocus.bindAsEventListener(this);
			this.eventEditBlur		= this.editBlur.bindAsEventListener(this);
			this.eventEditChange	= this.editChange.bindAsEventListener(this);
			this.eventEditValidate	= this.inputValidate.bindAsEventListener(this);
			this.eventInputChange	= this.inputChange.bindAsEventListener(this);
			this.eventKeyPress		= this.keyPress.bindAsEventListener(this);
			this.eventWindowMouseOut= this.windowMouseOut.bindAsEventListener(this);

			if (this.options.onerr != ""){
				this.eventError = new Function(this.options.onerr).bindAsEventListener(this);
			}
			if (this.options.onchange != ""){
				this.eventChanged = new Function("event",this.options.onchange).bindAsEventListener(this);
			}

			Event.observe(this.track, "mousedown", this.eventMouseDown);
			Event.observe(this.input, "keydown", this.eventEditValidate);
			Event.observe(this.input, "keyup", this.eventEditChange);
			Event.observe(this.input, "keypress", this.eventKeyPress);
			Event.observe(this.input, "focus", this.eventEditFocus);
			Event.observe(this.input, "blur", this.eventEditBlur);
			Event.observe(document, "mouseout", this.eventWindowMouseOut);
			if(this.input.onchange){
				this.eventInputOnChange = this.input.onchange.bindAsEventListener(this.input);
				this.input.onchange = null;
			}
			Event.observe(this.input, "change", this.eventInputChange);
		}
		this.initialized = true;

        this.setInitialValue(); 
        //Event.observe(window, "load", this.setInitialValue.bindAsEventListener(this)); //FIX RFA-190
		//Event.observe($(input), "propertychange", this.setInitialValue.bindAsEventListener(this));
			
		this.required = options.required;
		
		//Specify destructor
		this.track.component = this;
		this["rich:destructor"] = "destroy";
	},

	setInitialValue: function(){
		this.setValue(parseFloat(this.options.sliderValue || this.range.start));
        this.handle.style.visibility="visible";
		this.prevValue = this.value;
		this.valueChanged = false;
	},

   calculateAvailableValues : function(){
        var values = new Array();
        var value = this.roundFloat(this.minimum);
        var i = 0;
        while (value < this.maximum){
            values[i] = value;
            value = this.roundFloat(value + parseFloat(this.step));
            i++;
        }
        values[i] = this.roundFloat(this.maximum);

        return values;
    },

	roundFloat: function(x){
		if (!this.digCount)
			return Math.round(x);

		return parseFloat(Number(x).toFixed(this.digCount));
	},

	windowMouseOut : function(evt){
		var elt = null;
		if (evt.srcElement){
			elt = evt.toElement;
		} else {
			elt = evt.relatedTarget;
		}
		if (elt == null) {
			this.endDrag(evt);
		}
	},

	windowResized : function(evt){
		this.setValue(this.value);
	},

    getNearestValue: function(value){
        var pos;
        pos = this.binsearch(this.availableValues, value);
        if (pos>0) {
        	var prevPos = pos-1;
        	if ( Math.abs(value-this.availableValues[prevPos])<
        		 Math.abs(this.availableValues[pos]-value) ) {
        		pos = prevPos;
        	}
        }
        return this.roundFloat(this.availableValues[pos]);
    },

    binsearch: function(v, t) {
        var i = 0;
        var j = v.length - 1;
        var k;
        while (i < j) {
            k = Math.round((i + j) / 2 + 0.5) - 1;
            if (t <= v[k]) j = k;
            else i = k + 1;
        }

        return i;
    },


    setValue: function(sliderValue){
	    if (isNaN(sliderValue)){
	     sliderValue=0;
	    }
		var newValue = this.getNearestValue(sliderValue);
		this.value = newValue;
				
		if ((!this.editInFocus || newValue==sliderValue) && (this.required || "" != this.input.value || this.updating)){
			this.input.value = this.value;
	//		this.optionInput.value = this.value;
			this.handle.style.left = this.translateToPx(this.value);
		} else
		{
			this.handle.style.left = this.emptyValueHandleOffset + "px";
		}
		/* Disable tip
		if (!this.tip.firstChild) {
			if(this.labels){
				this.tip.appendChild(window.document.createTextNode(this.labels[this.value -1]));
			}else{
				this.tip.appendChild(window.document.createTextNode(this.value));
			}
		}
		if(this.labels){
			this.tip.firstChild.nodeValue= this.labels[this.value -1];
		}else{
			this.tip.firstChild.nodeValue= this.value;
		}
 		this.tip.style.left = this.handle.offsetLeft /*+ this.handle.offsetWidth/ + "px";
		*/
	},

	translateToPx: function(value) {
		return Math.round(this.trackOffset +
			((this.trackWidth - this.handleWidth)/(this.range.end-this.range.start)) *
			(value - this.range.start)) + "px";
	},

	translateToValue: function(offset) {
		return (((offset-this.trackOffset)/(this.trackWidth - this.handleWidth) *
			(this.range.end-this.range.start)) + this.range.start);
	},

	removePx: function(e){
		if ((e+"").indexOf("px")!=-1)
			return (e+"").substring(0,e.length-2);
		else
			return e;
	},

	startDrag: function(event) {
		if (this.editInFocus)
			this.input.blur();
		window.document.onmouseup		= this.eventMouseUp.bindAsEventListener(this);
		window.document.onmousemove		= this.eventMouseMove.bindAsEventListener(this);
		this.editBlur();
		this.prevMouseDownEvent = event;

		if(Event.isLeftClick(event)) {
			if(!this.disabled){
				var handle = Event.element(event);
				var pointer	= Event.pointerX(event);
				var offsets	= Position.cumulativeOffset(this.track);
				var offsetX = pointer - offsets[0];
				if (offsetX >= this.trackOffset && offsetX <= this.trackWidth + this.trackOffset) {
					this.handle.className = this.classes.handleSelected;
					/* Disable tip
					if (this.options.currValue){
						this.tip.style.display = "block";
					}
					*/
					//Richfaces.createEvent("mousedown", this.mainTable, null, null).fire();
					this.active = true;
					this.updating = true;
					this.setValue(this.translateToValue( ( pointer - offsets[0] ) -(this.handleWidth/2)));
					this.updating = false;
					var offsets	= Position.cumulativeOffset(this.handle);
					this.offsetX = pointer - offsets[0];
					
					Event.stop(event);
				}
			} else {
				Event.stop(event);
			}
		}
	},

	update: function(event) {
		this.updating = true;
		if(this.active) {

			if(!this.dragging) this.dragging = true;
			this.draw(event);
			Event.stop(event);
		}
		this.updating = false;
	},

	draw: function(event) {
		var pointer = Event.pointerX(event);
		var offsets = Position.cumulativeOffset(this.track);
		pointer -= this.offsetX + offsets[0];
		this.setValue(this.translateToValue( pointer ));
	},

	processMouseUp: function(event) {
		this.endDrag(event);
		this.fireClickIfNeeded(event);
	},

	endDrag: function(event) {
		window.document.onmouseup = this.prevMouseUp;
		window.document.onmousemove = this.prevMouseMove;
		/* Disable tip
		if (this.options.currValue){
			Element.hide(this.tip);
		}
		*/
		
		if (this.eventChanged && this.isValueChanged()){
			this.eventChanged(event);
		}
		this.handle.className = this.classes.handle;
		if(this.active && this.dragging) {
			this.active = false;
			this.dragging = false;
			//Richfaces.createEvent("mouseup", this.mainTable, null, null).fire();
			Event.stop(event);
		}
		if (RichFaces.navigatorType() != "MSIE")
			Richfaces.createEvent("change", this.input, null, null).fire();
	},

	fireClickIfNeeded: function(event){
		if ((this.prevMouseDownEvent.target != event.target
			&& RichFaces.navigatorType() == "FF")
			|| (RichFaces.getOperaVersion()
			&& RichFaces.getOperaVersion() < 9.0
			&& event.target.tagName.toLowerCase() != "div")) {
				//Richfaces.createEvent("click", this.mainTable, null, null).fire();
		}
	},

	isValueChanged : function(){
		var ret =this.prevValue != this.value
		this.prevValue = this.value;
		return ret;
	},

	inputChange: function(e) {
		this.editInFocus = false;
		if (isNaN(Number(this.input.value))){
			this.setValue(this.value);
		} else {
			if (this.outOfRange)
				if (this.eventError)
					this.eventError();
			this.setValue(Number(this.input.value));
		}
		this.value = this.input.value ? this.input.value : this.minimum;
		if(this.eventInputOnChange){
			this.eventInputOnChange();
		}
		if (this.eventChanged && this.isValueChanged()){
			this.eventChanged(e);
		}
	},

	inputValidate: function(e) {
		if ( e.keyCode == 13 ){
			if (isNaN(Number(this.input.value))){
				this.input.value = this.value;
				this.editBlur();
				this.setValue(this.value);
			}
		}
	},

	editChange: function(e) {
		if (isNaN(Number(this.input.value))){
			this.setValue(Number(this.value));
			this.input.value = this.value;
			
			if (this.eventError){
				this.eventError();
			}
		} else {
			if (!( e.keyCode > 37 && e.keyCode < 40 )){
				this.setValue(Number(this.input.value));
			}
		}

		if (e.keyCode == 13) {
			//Don't submit when user press enter
			//if (this.required || "" != this.input.value)
			//	this.input.value=this.getNearestValue(this.value);
			//this.input.form.submit();
		}
		
		//Don't fire event change when user enter
		//if (this.eventChanged && this.isValueChanged()){
		//	this.eventChanged(e);
		//}

	},
	
	keyPress: function(e) {
		if (e.keyCode == 13) {
			//Ignore Enter key
			Event.stop(e);
			
			//Move focus out of the textbox
			this.input.blur();
			
			// Always trigger event when user press enter
			this.eventInputChange();
		}
	},

	editFocus: function(){
		this.editInFocus = true;
	},

	editBlur: function(){
		this.editInFocus = false;
		// Always set the value and trigger event when the input lost focus
//	    if ((this.input.value+"").indexOf(this.value) != 0){
    		 this.setValue(this.input.value);
             this.eventInputChange();
//	    }
//	    else{
//		     this.setValue(this.input.value);
//		}
	},

	load: function(){
		// fix RF-895
		if(this.input.value){
			this.options.sliderValue = this.input.value;
		}
	
		this.setInitialValue();
		//this.setValue( this.value );
	},

	trim : function(str){
		return str.replace(/^\s+|\s+$/, '');
	},
	
	destroy: function() {
		this.active = false;
		this.dragging = false;
	}
}
