/*----------------------------------------------------------------------------\
|                                Slider 1.02                                  |
|-----------------------------------------------------------------------------|
|                         Created by Erik Arvidsson                           |
|                  (http://webfx.eae.net/contact.html#erik)                   |
|                      For WebFX (http://webfx.eae.net/)                      |
|-----------------------------------------------------------------------------|
| A slider control that degrades to an input control for non supported        |
| browsers.                                                                   |
|-----------------------------------------------------------------------------|
|                  Copyright (c) 1999 - 2002 Erik Arvidsson                   |
|-----------------------------------------------------------------------------|
| This software is provided "as is", without warranty of any kind, express or |
| implied, including  but not limited  to the warranties of  merchantability, |
| fitness for a particular purpose and noninfringement. In no event shall the |
| authors or  copyright  holders be  liable for any claim,  damages or  other |
| liability, whether  in an  action of  contract, tort  or otherwise, arising |
| from,  out of  or in  connection with  the software or  the  use  or  other |
| dealings in the software.                                                   |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| This  software is  available under the  three different licenses  mentioned |
| below.  To use this software you must chose, and qualify, for one of those. |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| The WebFX Non-Commercial License          http://webfx.eae.net/license.html |
| Permits  anyone the right to use the  software in a  non-commercial context |
| free of charge.                                                             |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| The WebFX Commercial license           http://webfx.eae.net/commercial.html |
| Permits the  license holder the right to use  the software in a  commercial |
| context. Such license must be specifically obtained, however it's valid for |
| any number of  implementations of the licensed software.                    |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| GPL - The GNU General Public License    http://www.gnu.org/licenses/gpl.txt |
| Permits anyone the right to use and modify the software without limitations |
| as long as proper  credits are given  and the original  and modified source |
| code are included. Requires  that the final product, software derivate from |
| the original  source or any  software  utilizing a GPL  component, such  as |
| this, is also licensed under the GPL license.                               |
|-----------------------------------------------------------------------------|
| 2002-10-14 | Original version released                                      |
| 2003-03-27 | Added a test in the constructor for missing oElement arg       |
| 2003-11-27 | Only use mousewheel when focused                               |
| 2006-04-12 | Check for having focus bevore change value                     |
| 2006-01-13 | Numbers (float) possible                                       |
|            | Property '_digits' / Methods 'setDigits' / 'getDigits' added   |
|-----------------------------------------------------------------------------|
| Dependencies: timer.js - an OO abstraction of timers                        |
|               range.js - provides the data model for the slider             |
|               winclassic.css or any other css file describing the look      |
|-----------------------------------------------------------------------------|
| Created 2002-10-14 | All changes are in the log above. | Updated 2003-1-27  |
\----------------------------------------------------------------------------*/


Slider.isSupported = typeof document.createElement != "undefined" &&
    typeof document.documentElement != "undefined" &&
    typeof document.documentElement.offsetWidth == "number";


function Slider(oElement, oInput, iMinValue, iMaxValue, sOrientation, oDisplay, bEvntExec)
{
	if (!oElement || !oInput)
	{
		alert("An error has been occured creating a new class instance!")
		return;
	}

	this._minValue = (!isNaN(iMinValue)) ? iMinValue : 0;
	this._maxValue = (!isNaN(iMaxValue)) ? iMaxValue : 100;
	this._orientation = sOrientation || "horizontal";
	this._range = new Range(this._minValue, this._maxValue);
	this._range.setExtent(0);
	this._blockIncrement = 100;
	this._unitIncrement = 1;
	this._digits = 0;
	this._timer = new Timer(100);
	this._changeOnFirstClick = false;
	this._executeEvent = (bEvntExec && bEvntExec.valueOf() == true) ? true : false;
	this._mouseUpEvent = 'alert("The mouse has been released from the current slider!");';

	if (Slider.isSupported && oElement)
	{
		this.document = oElement.ownerDocument || oElement.document;

		this.element = oElement;
		this.element.slider = this;
		this.element.unselectable = "on";

		// 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.handle = this.document.createElement("div");
		this.handle.className = "handle";
		this.handle.unselectable = "on";
		this.handle.appendChild(this.document.createElement("div"));
		this.handle.firstChild.appendChild(this.document.createTextNode(String.fromCharCode(160)));
		this.element.appendChild(this.handle);
	}

	this.input = oInput;
	if (oDisplay)
	{
		this.display = oDisplay;
	}
	else
	{
		this.display = 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.element.onselectstart    = function() {return false;};

		this._timer.ontimer = function()
		{
			oThis.ontimer();
		};

		// extra recalculate for ie
		window.setTimeout(function()
		{
			oThis.recalculate();
		}, 1);
	}
	else
	{
		this.input.onchange = function(e)
		{
			oThis.setValue(oThis.input.value);
		};
		this.display.onchange = function(e)
		{
			oThis.setValue(oThis.display.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.handle.className = "handle hover";
	},

	onblur:			function(e)
	{
		var s = this.slider;
		s._focused = false;
		s.handle.className = "handle";

		if (s._executeEvent == true)
		{
			eval(s._mouseUpEvent);
		}
	},

	onmouseover:	function(e)
	{
		e = Slider.eventHandlers.getEvent(e, this);
		var s = this.slider;
		if (e.srcElement == s.handle)
		{
			s.handle.className = "handle hover";
		}
	},

	onmouseout:		function(e)
	{
		e = Slider.eventHandlers.getEvent(e, this);
		var s = this.slider;
		if (e.srcElement == s.handle && !s._focused)
		{
			s.handle.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();
		}

		// start drag
		if (Slider.eventHandlers.getHandle(e))
		{
			Slider._sliderDragData = {
				screenX:	e.screenX,
				screenY:	e.screenY,
				dx:			e.screenX - s.handle.offsetLeft,
				dy:			e.screenY - s.handle.offsetTop,
				startValue:	s.getValue(),
				slider:		s
			};
		}
		else
		{
			var lineEl = Slider.eventHandlers.getLine(e);
			s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0);
			s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0);
			s._increasing = null;
			s.ontimer();
		}
	},

	onmousemove:	function(e)
	{
		e = Slider.eventHandlers.getEvent(e, this);

		// drag
		if (Slider._sliderDragData)
		{
			var s = Slider._sliderDragData.slider;

			var boundSize = s.getMaximum() - s.getMinimum();
			var size, pos, reset;

			if (s._orientation == "horizontal")
			{
				size = s.element.offsetWidth - s.handle.offsetWidth;
				pos = e.screenX - Slider._sliderDragData.dx;
				reset = Math.abs(e.screenY - Slider._sliderDragData.screenY) > 100;
			}
			else
			{
				size = s.element.offsetHeight - s.handle.offsetHeight;
				pos = s.element.offsetHeight - s.handle.offsetHeight - (e.screenY - Slider._sliderDragData.dy);
				reset = Math.abs(e.screenX - Slider._sliderDragData.screenX) > 100;
			}
			s.setValue(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();
		}

		// end drag
		if (Slider._sliderDragData)
		{
			Slider._sliderDragData = null;
		}
		else
		{
			s._timer.stop();
			s._increasing = null;
		}

		if (s._executeEvent == true)
		{
			eval(s._mouseUpEvent);
		}

		Slider._currentInstance = null;
	},

	onkeydown:		function(e)
	{
		e = Slider.eventHandlers.getEvent(e, this);
		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 35:	// end
				s.setValue(s.getOrientation() == "horizontal" ? s.getMaximum() : s.getMinimum());
				break;
			case 36:	// home
				s.setValue(s.getOrientation() == "horizontal" ? s.getMinimum() : s.getMaximum());
				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)
		{
			// windows inverts this on horizontal sliders. That does not make sense to me
			s.setValue(s.getValue() + e.wheelDelta / 120 * s.getUnitIncrement());
			return false;
		}
	}
};



Slider.prototype.classNameTag = "dynamic-slider-control",

Slider.prototype.setMouseEvnt = function(v) 
{
	this._mouseUpEvent = v;
}

Slider.prototype.setValue = function(v) 
{
	this._range.setValue(v);
	this.input.value = this.getValue();
	this.display.value = this.getValue();
};

Slider.prototype.getValue = function()
{
	return parseFloat(this._range.getValue().toFixed(this._digits));
};

Slider.prototype.setMinimum = function(v)
{
	this._range.setMinimum(v);
	this.input.value = this.getValue();
	this.display.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();
	this.display.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.getOrientation = function()
{
	return this._orientation;
};

Slider.prototype.setOrientation = function(sOrientation)
{
	if (sOrientation != this._orientation)
	{
		if (Slider.isSupported && this.element)
		{
			// add class name tag to class name
			this.element.className = this.element.className.replace(this._orientation, sOrientation);
		}
		this._orientation = sOrientation;
		this.recalculate();
    }
};

Slider.prototype.setFocusFlag = function(f)
{
	this._changeOnFirstClick = f;
}

Slider.prototype.getFocusFlag = function()
{
	return this._changeOnFirstClick;
}

Slider.prototype.setDigits = function(v)
{
	this._digits = parseInt(v,10);
}

Slider.prototype.getDigits = function()
{
	return this._digits;
}

Slider.prototype.recalculate = function()
{
	if (!Slider.isSupported || !this.element) return;

	var w = this.element.offsetWidth;
	var h = this.element.offsetHeight;
	var hw = this.handle.offsetWidth;
	var hh = this.handle.offsetHeight;
	var lw = this.line.offsetWidth;
	var lh = this.line.offsetHeight;

	// this assumes a border-box layout
	if (this._orientation == "horizontal")
	{
		this.handle.style.left = Math.ceil((w - hw) * (this.getValue() - this.getMinimum()) / (this.getMaximum() - this.getMinimum())) + "px";
		this.handle.style.top = Math.ceil((h - hh) / 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";
	}
	else
	{
		this.handle.style.left = Math.ceil((w - hw) / 2) + "px";
		this.handle.style.top = Math.ceil(h - hh - (h - hh) * (this.getValue() - this.getMinimum()) / (this.getMaximum() - this.getMinimum())) + "px";

		this.line.style.left = (w - lw) / 2 + "px";
		this.line.style.top = hh / 2 + "px";
		this.line.style.height = Math.max(0, h - hh - 2) + "px";				//hard coded border width
		//this.line.style.bottom = hh / 2 + "px";
		this.line.firstChild.style.height = Math.max(0, h - hh - 4) + "px";		//hard coded border width
	}
};

Slider.prototype.ontimer = function()
{
	var hw = this.handle.offsetWidth;
	var hh = this.handle.offsetHeight;
	var hl = this.handle.offsetLeft;
	var ht = this.handle.offsetTop;

	// Handle needs to have focus bevor a mouseclick changes the value
	if (this._changeOnFirstClick || this.handle.parentNode.slider._focused && this.handle.parentNode.slider._focused == true)
	{
		if (this._orientation == "horizontal")
		{
			if (this._mouseX > hl + hw && (this._increasing == null || this._increasing))
			{
				this.setValue(this.getValue() + this.getBlockIncrement());
				this._increasing = true;
			}
			else if (this._mouseX < hl && (this._increasing == null || !this._increasing))
			{
				this.setValue(this.getValue() - this.getBlockIncrement());
				this._increasing = false;
			}
		}
		else
		{
			if (this._mouseY > ht + hh && (this._increasing == null || !this._increasing))
			{
				this.setValue(this.getValue() - this.getBlockIncrement());
				this._increasing = false;
			}
			else if (this._mouseY < ht && (this._increasing == null || this._increasing))
			{
				this.setValue(this.getValue() + this.getBlockIncrement());
				this._increasing = true;
			}
		}
	}

	this._timer.start();
};

