/*
Dual slider and range modifications by Brian Ward to Slider and Range
classes by Erik Arvidsson (http://webfx.eae.net/contact.html#erikk)
For WebFX (http://webfx.eae.net/)
Copyright (c) 2002, 2003, 2006 Erik Arvidsson, 2007 Brian Ward

Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License.  You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0

Unless  required  by  applicable law or  agreed  to  in  writing,  software
distributed under the License is distributed on an  "AS IS" BASIS,  WITHOUT
WARRANTIES OR  CONDITIONS OF ANY KIND,  either express or implied.  See the
License  for the  specific language  governing permissions  and limitations
under the License.
*/

function Range2() {
	this._value = 0;
	this._minimum = 0;
	this._maximum = 12;
	this._extent = 0;
	this._value2 = this._maximum;

	/* minimum range */
	this._minrange = 1;

	this._isChanging = false;
}

Range2.prototype.setValue = function (value) {
	value = Math.round(parseFloat(value));
	if (isNaN(value)) return;
	if (this._value != value) {
		if (value + this._extent > this._value2)
			this._value = this._value2 - this._extent;
		else if (value < this._minimum)
			this._value = this._minimum;
		else
			this._value = value;

		if (this._value == this._value2)
		    this._value -= this._minrange;

		if (!this._isChanging && typeof this.onchange == "function")
			 this.onchange();
	}
};

Range2.prototype.setValue2 = function (value) {
	value = Math.round(parseFloat(value));
	if (isNaN(value)) return;
	if (this._value2 != value) {
		if (value + this._extent > this._maximum)
			this._value2 = this._maximum - this._extent;
		else if (value < this._value)
			this._value2 = this._value;
		else
			this._value2 = value;

		if (this._value == this._value2)
		    this._value2 += this._minrange;

		if (!this._isChanging && typeof this.onchange == "function")
			 this.onchange();
	}
};

Range2.prototype.getValue = function () {
	return this._value;
};
Range2.prototype.getValue2 = function () {
	return this._value2;
};

Range2.prototype.setExtent = function (extent) {
	if (this._extent != extent) {
		if (extent < 0)
			this._extent = 0;
		else if (this._value + extent > this._maximum)
			this._extent = this._maximum - this._value;
		else
			this._extent = extent;
		if (!this._isChanging && typeof this.onchange == "function")
			this.onchange();
	}
};

Range2.prototype.getExtent = function () {
	return this._extent;
};

Range2.prototype.setMinimum = function (minimum) {
	if (this._minimum != minimum) {
		var oldIsChanging = this._isChanging;
		this._isChanging = true;

		this._minimum = minimum;

		if (minimum > this._value)
			this.setValue(minimum);
		if (minimum > this._maximum) {
			this._extent = 0;
			this.setMaximum(minimum);
			this.setValue(minimum)
		}
		if (minimum + this._extent > this._maximum)
			this._extent = this._maximum - this._minimum;

		this._isChanging = oldIsChanging;
		if (!this._isChanging && typeof this.onchange == "function")
			this.onchange();
	}
};

Range2.prototype.getMinimum = function () {
	return this._minimum;
};

Range2.prototype.setMaximum = function (maximum) {
	if (this._maximum != maximum) {
		var oldIsChanging = this._isChanging;
		this._isChanging = true;

		this._maximum = maximum;

		if (maximum < this._value)
			this.setValue(maximum - this._extent);
		if (maximum < this._minimum) {
			this._extent = 0;
			this.setMinimum(maximum);
			this.setValue(this._maximum);
		}
		if (maximum < this._minimum + this._extent)
			this._extent = this._maximum - this._minimum;
		if (maximum < this._value + this._extent)
			this._extent = this._maximum - this._value;

		this._isChanging = oldIsChanging;
		if (!this._isChanging && typeof this.onchange == "function")
			this.onchange();
	}
};

Range2.prototype.getMaximum = function () {
	return this._maximum;
};

/* start slider */

Slider.isSupported = typeof document.createElement != "undefined" &&
	typeof document.documentElement != "undefined" &&
	typeof document.documentElement.offsetWidth == "number";


function Slider(oElement, oInput) {
	if (!oElement) return;
	this._orientation = "sliderhorizontal";
	this._range = new Range2();
	this._range.setExtent(0);
	this._blockIncrement = 10;
	this._unitIncrement = 1;

	if (Slider.isSupported && oElement) {

		this.document = oElement.ownerDocument || oElement.document;
		
		this.element = oElement;
		this.element.slider = this;
		this.element.unselectable = "on";

		this.onrelease = function() { return true; };

		// add class name tag to class name
		this.element.className = this._orientation + " " + this.classNameTag + " " + this.element.className;

		// create line
		this.line = this.document.createElement("DIV");
		this.line.className = "line";
		this.line.unselectable = "on";
		this.line.appendChild(this.document.createElement("DIV"));
		this.element.appendChild(this.line);

		// create handle
		this.handle1 = this.document.createElement("DIV");
		this.handle1.className = "handle_left";
		this.handle1.unselectable = "on";
		this.handle1.appendChild(this.document.createElement("DIV"));
		this.handle1.firstChild.appendChild(
			this.document.createTextNode(String.fromCharCode(160)));
		this.element.appendChild(this.handle1);

		// create handle #2
		this.handle2 = this.document.createElement("DIV");
		this.handle2.className = "handle_right";
		this.handle2.unselectable = "on";
		this.handle2.appendChild(this.document.createElement("DIV"));
		this.handle2.firstChild.appendChild(
			this.document.createTextNode(String.fromCharCode(160)));
		this.element.appendChild(this.handle2);

	}

	this.input = oInput;

	// events
	var oThis = this;
	this._range.onchange = function () {
	    oThis.recalculate();
	    if (typeof oThis.onchange == "function")
		oThis.onchange();
	};
	if (Slider.isSupported && oElement) {
	    this.element.onfocus = Slider.eventHandlers.onfocus;
	    this.element.onblur = Slider.eventHandlers.onblur;
	    this.element.onmousedown = Slider.eventHandlers.onmousedown;
	    this.element.onmouseover = Slider.eventHandlers.onmouseover;
	    this.element.onmouseout	= Slider.eventHandlers.onmouseout;
	    this.element.onkeydown	= Slider.eventHandlers.onkeydown;
	    this.element.onkeypress	= Slider.eventHandlers.onkeypress;
	    this.element.onmousewheel = Slider.eventHandlers.onmousewheel;
	    this.handle1.onselectstart =
	    this.handle2.onselectstart =
	    this.element.onselectstart = function () { return false; };

	    // extra recalculate for ie
	    window.setTimeout(function() {
		    oThis.recalculate();
	    }, 1);
	}
	else {
		this.input.onchange = function (e) {
			oThis.setValue(oThis.input.value);
		};
	}
    }

Slider.eventHandlers = {

	// helpers to make events a bit easier
	getEvent:	function (e, el) {
		if (!e) {
			if (el)
				e = el.document.parentWindow.event;
			else
				e = window.event;
		}
		if (!e.srcElement) {
			var el = e.target;
			while (el != null && el.nodeType != 1)
				el = el.parentNode;
			e.srcElement = el;
		}
		if (typeof e.offsetX == "undefined") {
			e.offsetX = e.layerX;
			e.offsetY = e.layerY;
		}

		return e;
	},

	getDocument:	function (e) {
		if (e.target)
			return e.target.ownerDocument;
		return e.srcElement.document;
	},

	getSlider:	function (e) {
		var el = e.target || e.srcElement;
		while (el != null && el.slider == null)	{
			el = el.parentNode;
		}
		if (el)
			return el.slider;
		return null;
	},

	getLine:	function (e) {
		var el = e.target || e.srcElement;
		while (el != null && el.className != "line")	{
			el = el.parentNode;
		}
		return el;
	},

	getHandle:	function (e) {
		var el = e.target || e.srcElement;
		var re = /handle/;
		while (el != null && !re.test(el.className))	{
			el = el.parentNode;
		}
		return el;
	},
	// end helpers

	onfocus:	function (e) {
		var s = this.slider;
		s._focused = true;
		s.handle1.className = "handle hover";
	},

	onblur:	function (e) {
		var s = this.slider
		s._focused = false;
		s.handle1.className = "handle";
	},

	onmouseover:	function (e) {
		e = Slider.eventHandlers.getEvent(e, this);
		var s = this.slider;
		if (e.srcElement == s.handle1)
			s.handle1.className = "handle hover";
	},

	onmouseout:	function (e) {
		e = Slider.eventHandlers.getEvent(e, this);
		var s = this.slider;
		if (e.srcElement == s.handle1 && !s._focused)
			s.handle1.className = "handle";
	},

	onmousedown:	function (e) {
		e = Slider.eventHandlers.getEvent(e, this);
		var s = this.slider;
		if (s.element.focus)
			s.element.focus();

		Slider._currentInstance = s;
		var doc = s.document;

		if (doc.addEventListener) {
			doc.addEventListener("mousemove", Slider.eventHandlers.onmousemove, true);
			doc.addEventListener("mouseup", Slider.eventHandlers.onmouseup, true);
		}
		else if (doc.attachEvent) {
			doc.attachEvent("onmousemove", Slider.eventHandlers.onmousemove);
			doc.attachEvent("onmouseup", Slider.eventHandlers.onmouseup);
			doc.attachEvent("onlosecapture", Slider.eventHandlers.onmouseup);
			s.element.setCapture();
		}

		handle = Slider.eventHandlers.getHandle(e);
		if (handle) {	// start drag
			if (handle == s.handle2) {
				v  = s.getValue2();
			} else {
				v  = s.getValue();
			}
			Slider._sliderDragData = {
				handle:		handle,
				screenX:	e.screenX,
				screenY:	e.screenY,
				dx:			e.screenX - handle.offsetLeft,
				dy:			e.screenY - handle.offsetTop,
				startValue:	v,
				slider:		s
			};
		}
	},

	onmousemove:	function (e) {
		e = Slider.eventHandlers.getEvent(e, this);

		if (Slider._sliderDragData) {	// drag
			var s = Slider._sliderDragData.slider;

			var boundSize = s.getMaximum() - s.getMinimum();
			var size, pos, reset;
			var handle = Slider._sliderDragData.handle;

			size = s.element.offsetWidth - handle.offsetWidth;
			pos = e.screenX - Slider._sliderDragData.dx;
			reset = Math.abs(e.screenY - Slider._sliderDragData.screenY) > 100;

			if (handle == s.handle1) {
				s.setValue(reset ? Slider._sliderDragData.startValue :
					s.getMinimum() + boundSize * pos / size);
			} else {
				s.setValue2(reset ? Slider._sliderDragData.startValue :
					s.getMinimum() + boundSize * pos / size);
			}
			return false;
		}
		else {
			var s = Slider._currentInstance;
			if (s != null) {
				var lineEl = Slider.eventHandlers.getLine(e);
				s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0);
				s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0);
			}
		}

	},

	onmouseup:	function (e) {
		e = Slider.eventHandlers.getEvent(e, this);
		var s = Slider._currentInstance;
		var doc = s.document;
		if (doc.removeEventListener) {
			doc.removeEventListener("mousemove", Slider.eventHandlers.onmousemove, true);
			doc.removeEventListener("mouseup", Slider.eventHandlers.onmouseup, true);
		}
		else if (doc.detachEvent) {
			doc.detachEvent("onmousemove", Slider.eventHandlers.onmousemove);
			doc.detachEvent("onmouseup", Slider.eventHandlers.onmouseup);
			doc.detachEvent("onlosecapture", Slider.eventHandlers.onmouseup);
			s.element.releaseCapture();
		}

		if (Slider._sliderDragData) {	// end drag
			Slider._sliderDragData = null;
		}
		else {
			s._increasing = null;
		}

		Slider._currentInstance.onrelease();

		Slider._currentInstance = null;
	},

	onkeydown:	function (e) {
		e = Slider.eventHandlers.getEvent(e, this);
		//var s = Slider.eventHandlers.getSlider(e);
		var s = this.slider;
		var kc = e.keyCode;
		switch (kc) {
			case 33:	// page up
				s.setValue(s.getValue() + s.getBlockIncrement());
				break;
			case 34:	// page down
				s.setValue(s.getValue() - s.getBlockIncrement());
				break;
			case 38:	// up
			case 39:	// right
				s.setValue(s.getValue() + s.getUnitIncrement());
				break;

			case 37:	// left
			case 40:	// down
				s.setValue(s.getValue() - s.getUnitIncrement());
				break;
		}

		if (kc >= 33 && kc <= 40) {
			return false;
		}
	},

	onkeypress:	function (e) {
		e = Slider.eventHandlers.getEvent(e, this);
		var kc = e.keyCode;
		if (kc >= 33 && kc <= 40) {
			return false;
		}
	},

	onmousewheel:	function (e) {
		e = Slider.eventHandlers.getEvent(e, this);
		var s = this.slider;
		if (s._focused) {
			s.setValue(s.getValue() + e.wheelDelta / 120 * s.getUnitIncrement());
			// windows inverts this on horizontal sliders. That does not
			// make sense to me
			return false;
		}
	}
};



Slider.prototype.classNameTag = "dynamic-slider-control",

Slider.prototype.setValue = function (v) {
	this._range.setValue(v);
	this.input.value = this.getValue();
};
Slider.prototype.setValue2 = function (v) {
	this._range.setValue2(v);
	this.input.value = this.getValue2();
};

Slider.prototype.getValue = function () {
	return this._range.getValue();
};
Slider.prototype.getValue2 = function () {
	return this._range.getValue2();
};

Slider.prototype.setMinimum = function (v) {
	this._range.setMinimum(v);
	this.input.value = this.getValue();
};

Slider.prototype.getMinimum = function () {
	return this._range.getMinimum();
};

Slider.prototype.setMaximum = function (v) {
	this._range.setMaximum(v);
	this.input.value = this.getValue();
};

Slider.prototype.getMaximum = function () {
	return this._range.getMaximum();
};

Slider.prototype.setUnitIncrement = function (v) {
	this._unitIncrement = v;
};

Slider.prototype.getUnitIncrement = function () {
	return this._unitIncrement;
};

Slider.prototype.setBlockIncrement = function (v) {
	this._blockIncrement = v;
};

Slider.prototype.getBlockIncrement = function () {
	return this._blockIncrement;
};

Slider.prototype.recalculate = function() {
	if (!Slider.isSupported || !this.element) return;

	var w = this.element.offsetWidth;
	var h = this.element.offsetHeight;
	var hw = this.handle1.offsetWidth;
	var hh = this.handle1.offsetHeight;
	var hw2 = this.handle2.offsetWidth;
	var hh2 = this.handle2.offsetHeight;
	var lw = this.line.offsetWidth;
	var lh = this.line.offsetHeight;

	// this assumes a border-box layout

	this.handle1.style.left = (w - hw) * (this.getValue() - this.getMinimum()) /
		(this.getMaximum() - this.getMinimum()) + "px";
	this.handle1.style.top = (h - hh) / 2 + "px";

	/* handle 2 */
	this.handle2.style.left = (w - hw2) * (this.getValue2() - this.getMinimum()) /
		(this.getMaximum() - this.getMinimum()) + "px";
	this.handle2.style.top = (h - hh2) / 2 + "px";

	this.line.style.top = (h - lh) / 2 + "px";
	this.line.style.left = hw / 2 + "px";
	//this.line.style.right = hw / 2 + "px";
	this.line.style.width = Math.max(0, w - hw - 2)+ "px";
	this.line.firstChild.style.width = Math.max(0, w - hw - 4)+ "px";
};

