/*
A range slider contains:
	Track
	High slider
	Low slider
	On slide, sliders value gets updated. Also, green connecting bar width/pos is updated.
	on Change, sliders boundaries are adjusted to prevent passing each other.
	Serialize function
*/
function rangeSlider(preferences) {
	this.n_track         = null;                    // Track on which the sliders will reside
	this.n_slider_low    = null;                    // Slider for the low value
	this.n_slider_high   = null;                    // Slider for the high value
	this.n_text_low      = null;                    // Text for the low value
	this.n_text_high     = null;                    // Text for the high value
	this.n_connector     = null;                    // Element whose position and width will change to "connect" the two sliders
	this.o_slider        = null;
	this.preferences     = preferences;
	this.highValue       = null;
	this.lowValue        = null;
	this.recentRequests  = Array();
	this.p_slider_high   = null;
	this.p_slider_low    = null;
	this.p_track         = null;
	this.p_max           = 100;
	this.p_min           = 0;
	this.p_startHigh     = 100;
	this.p_startLow      = 0;
	this.p_textNudgeY    = -12;
	this.p_textNudgeX    = 0;
	for (n in preferences) {
		this['p_'+n] = preferences[n];
	}

	this.construct = function() {
		// Create two new sliders
		this.n_track        = this.p_track;
		this.n_slider_low   = this.p_slider_low;
		this.n_slider_high  = this.p_slider_high;
		this.n_text_low     = this.p_text_low;
		this.n_text_high    = this.p_text_high;
		this.n_connector    = this.p_connector;

		this.verifyNodes();

		this.o_slider = new Control.Slider(
			Array(this.n_slider_low, this.n_slider_high),
			this.p_track,
			{
				onChange: this.sliderChange.bind(this),
				onSlide: this.sliderChange.bind(this)
			}
		);
		this.sliderChange([this.toFraction(this.p_startLow), this.toFraction(this.p_startHigh)]);
	}

	this.verifyNodes = function() {
		for (var i in this) {
			if (i.substring(0,2) == 'n_') {
				if (!isTag(this[i])) this.error('Property is not a node: '+i);
			}
		}
	}

	this.error = function(text) {
		alert('RangeSlider.js Warning:\n\n'+text);
	}

	/**
	 * Verifies that this event call is a result of
	 * the user sliding a slider, and not our own
	 * deliberate calling of setValue()
	 */
	this.sliderChange = function(v) {
		// If the value being sent is different than the one we already have
		// on file for this slider, then order an update, otherwise, ignore.
		if (this.toValue(v[0]) != this.lowValue) this.setSlider(v[0], 'low', true);
		if (this.toValue(v[1]) != this.highValue) this.setSlider(v[1], 'high', true);
	}

	this.setSlider = function(v, slider, noreset) {
		var newValue       = this.toValue(v);
		var originalValue  = newValue;
		var high_n         = this['n_slider_high'];
		var low_n          = this['n_slider_low'];
		var n              = this['n_slider_'+slider];
		switch (slider) {
			case 'high':
				var handleIndex = 0;
				if (this.lowValue) newValue = Math.max(newValue, this.lowValue);
				this.highValue                    = newValue;
				if (high_n) high_n.style.zIndex   = 1000;
				if (low_n) low_n.style.zIndex     = 100;
			break;
			case 'low':
				var handleIndex = 1;
				if (this.highValue) newValue = Math.min(newValue, this.highValue);
				this.lowValue                     = newValue;
				if (low_n) low_n.style.zIndex     = 1000;
				if (high_n) high_n.style.zIndex   = 100;
			break;
		}
		var stuck = (newValue != originalValue);

		this.setSliderValue(newValue, slider);
		this['n_text_'+slider].innerHTML = '$'+parseInt(newValue);

		if (!stuck) {
			// Find left and right side of each one
			var low_pos = Position.cumulativeOffset(low_n);
			var high_pos = Position.cumulativeOffset(high_n);
			var track_pos = Position.cumulativeOffset(this.n_track);
			var high_size = Element.getDimensions(high_n);

			// Set connector's width to the difference
			this.n_connector.style.width = (high_pos[0] - low_pos[0])+'px';
			// Set the connector's left to slider_low's left
			var nudgeX = (document.all) ? -16 : 0;
			this.n_connector.style.left = (low_pos[0]-track_pos[0]+nudgeX)+'px';

			// Move the text to follow
			switch(slider) {
				case 'high':
					this.n_text_high.style.left = (high_pos[0]+this.p_textNudgeX)+'px';
					this.n_text_high.style.top = (high_pos[1]+this.p_textNudgeY)+'px';
				break;
				case 'low':
					var textWidth = Element.getDimensions(this.n_text_low);
					this.n_text_low.style.left = (low_pos[0]-textWidth.width+this.p_textNudgeX)+'px';
					this.n_text_low.style.top = (low_pos[1]+this.p_textNudgeY)+'px';
				break;
			}
		}
	}

	/**
	 * Sets the value of a slider without generating an onSlide or onChange
	 * event callback
	 */
	this.setSliderValue = function(value, slider) {
		switch(slider) {
			case 'low':
				this.lowValue = value;
				var index = 0;
				document.getElementsByName("lowprice")[0].value = value;
			break;
			case 'high':
				this.highValue = value;
				var index = 1;
				document.getElementsByName("highprice")[0].value = value;
			break;
			default:
				alert('Unknown slider: '+slider);
			break;
		}
		this.o_slider.setValue(this.toFraction(value), index);
		this.o_slider.activeHandleIdx = index;
	}

	/**
	 * Converts a fractional value to a value in the range
	 * that is specified by min and max
	 */
	this.toValue = function(v) {
		return (v*this.p_max)+this.p_min;
	}
	/**
	 * Opposite of .toValue
	 */
	this.toFraction = function(v) {
		return (v-this.p_min)/this.p_max;
	}

	/**
	 * Returns high value
	 */
	this.getHigh = function() {
		return this.highValue;
	}
	/**
	 * Returns low value
	 */
	this.getLow = function() {
		return this.lowValue;
	}

	this.construct();
}

// Here, we extend the Scriptaculous slider.
Control.Slider.prototype.setValue = function(sliderValue, handleIdx){
	if(!this.active) {
		this.activeHandle    = this.handles[handleIdx];
		this.activeHandleIdx = handleIdx;
		this.updateStyles();
	}
	handleIdx = handleIdx || this.activeHandleIdx || 0;
	handleIdx = Math.max(0, handleIdx);
	if(this.initialized && this.restricted) {
		if((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
			sliderValue = this.values[handleIdx-1];
		if((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
			sliderValue = this.values[handleIdx+1];
	}
	sliderValue = this.getNearestValue(sliderValue);
	this.values[handleIdx] = sliderValue;
	this.value = this.values[0]; // assure backwards compat

	this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =
		this.translateToPx(sliderValue);

	this.drawSpans();
	if(!this.dragging || !this.event) this.updateFinished();
};
Control.Slider.prototype.startDrag = function(event) {
	if(Event.isLeftClick(event)) {
		if(!this.disabled){
			this.active = true;

			var handle = Event.element(event);
			if (handle != this.handles[0] && handle != this.handles[1]) handle = this.track;
			var pointer  = [Event.pointerX(event), Event.pointerY(event)];
			// find the handle (prevents issues with Safari)
			while((this.handles.indexOf(handle) == -1) && handle.parentNode)
				handle = handle.parentNode;

			this.activeHandle    = handle;
			this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
			this.updateStyles();

			var offsets  = Position.cumulativeOffset(this.activeHandle);
			this.offsetX = (pointer[0] - offsets[0]);
			this.offsetY = (pointer[1] - offsets[1]);

		}
		Event.stop(event);
	}
};

// Returns true if the argument given corresponds to an HTML tag DOM element,
// otherwise returns false.
function isTag(element) {
	if (typeof(element) == 'undefined') return false;
	if(element.nodeType == 1) return true;
	return false;
}

// Activates a parameterized slider on the page
Behaviour.register({
	'.range': function(el) {
		var track = el.getElementsByTagName('div')[0];
		var minimum = document.getElementsByName("minimum")[0].value;
		var maximum = document.getElementsByName("maximum")[0].value;
		var low = document.getElementsByName("low")[0].value;
		var high = document.getElementsByName("high")[0].value;	
		
		var connector = track.getElementsByTagName('div')[0];
		var sliders = document.getElementsByClassName('slider', track);
		for (var x = 0; x<sliders.length; x++) {
			if (Element.hasClassName(sliders[x], 'left')) var sliderLow = sliders[x];
			if (Element.hasClassName(sliders[x], 'right')) var sliderHigh = sliders[x];
		}
		var sliderTexts = document.getElementsByClassName('sliderText', el);
		for (var x = 0; x<sliderTexts.length; x++) {
			if (Element.hasClassName(sliderTexts[x], 'left')) var sliderTextLow = sliderTexts[x];
			if (Element.hasClassName(sliderTexts[x], 'right')) var sliderTextHigh = sliderTexts[x];
		}
		new rangeSlider({
			slider_high: sliderHigh,
			slider_low: sliderLow,
			text_high: sliderTextHigh,
			text_low: sliderTextLow,
			track: track,
			connector: connector,
			min: parseInt(minimum),
			max: parseInt(maximum),
			startHigh: high,
			startLow: low
		});
	}
});