/*
This Slider works for both horizontal and vertical sliders
It can work as single slider
It can work together with other slider to set low and high ranges.
event-driven action implemented so far:
	1) mousedown and drag the slider until mouse up
	2) mouseclick on the slit to cause slider jump to the mouse click position
*/
function Slider(
	inSliderId,
	itsDimension,	//'x' for horizontal, 'y' for vertical
	inValue,	//the initial value of the slider
	itsLength,	// the slider length in pixels.
	inCount,	// the number of accepted values in the interval.
	itsDecimals,	// Number of decimals to be displayed.
	inFrom,	//the minimum display value
	inTo,		//the maximum display value
	itsType)	//value: min or max -- optional
{
	var self = this;
	var itsValue = inValue;
	var itsEndPosition; //the position where the slider end up with at the end of the event.
	var itsSliderObj = DOM.getElementById(inSliderId);
	var itsValueCount =inCount ? inCount - 1 : itsLength; // Allowed number of values in the interval
	var itsScale = (inTo - inFrom) / itsLength; // Slider-display scale [value-change per pixel of movement].

	var itsFrom, itsTo;
	var itsMin;		//allow slide range min
	var itsMax;		//allow slide range max
	var itsStartX;  //coordinate x at the start of an event
	var itsStartY;	//coordinate y at the start of an event
	var itsStartPositionX; //relative position (left) to its parent at the start of an event
	var itsStartPositionY; //relative position (top) to its parent at the start of an event
	var isMouseOver;
	var itsSubscriptionSliders = new Array();
	var itsSubscriptionSliderPositions = new Array();

	//set the slider to the initial state
	self.initialize = function()
	{
		//adjust the variable for different type of slider (horizontal and vertical)
		if (itsDimension == 'x')
		{
			itsFrom = inFrom;
			itsTo = inTo;
		}
		else if (itsDimension == 'y')
		{
			itsFrom = inTo;
			itsTo = inFrom;
			itsScale = -itsScale; // Invert scale for vertical sliders. "Higher is more."
		}

		if(itsScale == 0 || itsScale == NaN){
			itsScale = 1;
		}

		//set slide to the position matching the initialvalue
		var theInitialPos = (itsValue - itsFrom)/(itsScale);
		setPosition(itsSliderObj,itsDimension,theInitialPos);

		Publisher(self);
	};


	self.slide = function(inEvent,inOtherSliderId)
	{
		var theEvent = (!inEvent) ? window.event : inEvent; // The mousemove event

		//set the range which the slider is allow to move
		//look for any other slider which subscribe to this, and what type
		if (itsType == null || itsType == undefined || inOtherSliderId == undefined)
		{
			itsMin = 0;
			itsMax = itsLength;
		}
		else //looking for other subscriber slider position
		{
			var	theSubscriberPosition = self.getSubscriberPosition(inOtherSliderId);//ask subscriber for its position

			if (itsType == "min")
			{
				itsMin = 0;
				itsMax = (theSubscriberPosition) ? theSubscriberPosition : itsLength;
			}
			else if (itsType == "max")
			{
				itsMin = (theSubscriberPosition) ? theSubscriberPosition : 0;
				itsMax = itsLength;
			}
		}

		itsStartX = theEvent.screenX;	// Horizontal mouse position at start of slide.
		itsStartY = theEvent.screenY;	// Vertical mouse position at start of slide.


		itsStartPositionX = getPosition(itsSliderObj,"x",false); // Sliders horizontal position at start of slide.
		itsStartPositionY = getPosition(itsSliderObj,"y",false); // Sliders vertical position at start of slide.

		isMouseOver = true
		document.onmousemove = self.moveSlider // Start the action if the mouse is dragged.
		document.onmouseup = self.sliderMouseUp // Stop sliding.
	};

	self.moveSlider = function(inEvent)
	{
		var theEvent = (!inEvent) ? window.event : inEvent; // The mousemove event
		if (isMouseOver) { // Only if slider is dragged
			var thePos;
			if (itsDimension == "x")
				thePos = itsStartPositionX + theEvent.screenX - itsStartX ;// Horizontal mouse position relative to allowed slider positions
			else if (itsDimension == "y")
				thePos = itsStartPositionY + theEvent.screenY - itsStartY; // Vertical mouse position relative to the allowed slider positions

			//Limit slider movement
			if (thePos > itsMax) thePos = itsMax;
			if (thePos < itsMin) thePos = itsMin;
			setPosition(itsSliderObj,itsDimension,thePos);

			//sliderVal = x + y // pixel value of slider regardless of orientation
			if (itsDimension == "x")
				itsValue = self.calculateValue(thePos + itsStartPositionY);
			else if (itsDimension == "y")
				itsValue = self.calculateValue(itsStartPositionX + thePos);

			self.notifySubscribers(itsValue,null,null); //tell any display subscriber the value

			return false
		}
		return
	};

	self.calculateValue = function(inSliderVal)
	{
		sliderPos = (itsLength / itsValueCount) * Math.round(itsValueCount * inSliderVal / itsLength)
		return (Math.round((sliderPos * itsScale + itsFrom) *
				Math.pow(10, itsDecimals)) / Math.pow(10, itsDecimals));
	};

	self.sliderMouseUp = function()
	{
		isMouseOver = false; // Stop the sliding.
		itsValue = (itsValue) ? itsValue : 0;// Find last display value.
		itsEndPosition = (itsValue - itsFrom)/(itsScale); // Calculate slider position (regardless of orientation).
		// Snap slider to corresponding display position.
		setPosition(itsSliderObj,itsDimension,itsEndPosition);
		self.notifySubscribers(itsValue,itsSliderObj.id,itsEndPosition,'mouseup');

		if (document.removeEventListener) { // Remove event listeners from 'document' (Moz&co).
			document.removeEventListener('mousemove', self.moveSlider,true);
			document.removeEventListener('mouseup', self.sliderMouseUp,true);
		}
		else if (document.detachEvent) { // Remove event listeners from 'document' (IE&co).
			document.detachEvent('onmousemove', self.moveSlider);
			document.detachEvent('onmouseup', self.sliderMouseUp);
		}


		document.onmousemove = null; // stop the action if the mouse is dragged.
		document.onmouseup = null;// Stop sliding.
	};

	self.jumpTo = function(inEvent,inSlit,inSliderSizeAdjustment)
	{
		var theEvent = (!inEvent) ? window.event : inEvent; // The mouseclick event
		var theWindowX = (window.screenX) ? window.screenX : window.screenLeft;
		var theWindowY = (window.screenY) ? window.screenY : window.screenTop;

		var theSlitPos = getPosition(DOM.getElementById(inSlit),itsDimension,true);
		//get the screen position when the slider is at the start point of the sllide
		if (itsDimension == "x")
			itsEndPosition = theEvent.screenX - theWindowX - theSlitPos - inSliderSizeAdjustment;
		else if(itsDimension == "y")
			itsEndPosition = theEvent.screenY - theWindowY - theSlitPos - inSliderSizeAdjustment;

		if(itsEndPosition < 0) itsEndPosition = 0;
		if(itsEndPosition > itsLength) itsEndPosition = itsLength;

		var theOtherDimension;
		if (itsDimension == "x") theOtherDimension = "y";
		if (itsDimension == "y") theOtherDimension = "x";
		itsValue = self.calculateValue(itsEndPosition + getPosition(itsSliderObj,theOtherDimension,false));
		setPosition(itsSliderObj,itsDimension,itsEndPosition);
		self.notifySubscribers(itsValue,null,itsEndPosition);
	};

	self.getValue = function()
	{
		return (itsValue);
	};


	self.getType = function()
	{
		return itsType;
	};

	self.setSubscriberPosition = function(inValue,inObjId,inPosition)
	{
		if (inObjId == null)	return;

		var theIndex = itsSubscriptionSliders.indexOf(inObjId);
		if (theIndex < 0)
			theIndex = itsSubscriptionSliders.length;
		itsSubscriptionSliders[theIndex] = inObjId;
		itsSubscriptionSliderPositions[theIndex] = inPosition;
	};

	self.getSubscriberPosition = function(inObjId)
	{
		var theIndex = itsSubscriptionSliders.indexOf(inObjId);
		if (theIndex >= 0)
			return itsSubscriptionSliderPositions[theIndex];
		else
		{
			if (itsType == "min") return itsLength;
			else if (itsType == "max")	return 0;
			else return null;
		}
	};

	self.setSliderPositionByValue = function(inValue)
	{
		var thePosition = (inValue - itsFrom) / itsScale;
		itsValue = inValue;
		setPosition(itsSliderObj,itsDimension,thePosition);
	};


	/*
	get the position of an element
	parameters:
	inElement -- the target element object
	inDimension -- "x" the left
				   "y" the top
	inIsAbolute -- true: position of an element relative to its parent element
				   false: the absolute coordinants of the element
	*/
	function getPosition(inElement,inDimension,inIsAbsolute)
	{
		var theX = 0;
		var theY = 0;

		if (inElement.offsetParent)
		{
			theX = inElement.offsetLeft;
			theY = inElement.offsetTop;
			if (inIsAbsolute)
			{
				while (inElement = inElement.offsetParent)
				{
					theX += inElement.offsetLeft;
					theY += inElement.offsetTop;
				}
			}
		}
		else
		{
        	theX += theElement.x;
			theY += theElement.y;
		}

		if (inDimension == 'x')
			return theX;
		if (inDimension == 'y')
			return theY;
	}


	/*
	set the element to the specified position
	parameters:
	inElement -- the target element
	inDimension -- "x" the left
			       "y" the top
	inPos -- the relative position of the element to its immediate parent
	*/
	function setPosition(inElement,inDimension,inPos)
	{
		if (inDimension == "x")
		{
			if (inElement.style && inElement.style.left)
				inElement.style.left = inPos + "px";
			else if (inElement.style && inElement.style.pixelLeft)
				inElement.style.pixelLeft = inPos + "px";
		}
		else if (inDimension == "y")
		{
			if (inElement.style && inElement.style.top)
				inElement.style.top = inPos;
			else if (inElement.style && inElement.style.pixelTop)
			inElement.style.pixelTop = inPos;
		}
	}
}


/**
	This type of slider composes of a min-slider and max-slider to set the range of choice
	It also includes optional slider value displays to show the value of the min and max value selected
	The min and max value is saved as Properties with a default "min" and "max" as keys
*/
function DoubleSliders(inMinID,
						 inMaxID,
						 itsLength,	// the slider length in pixels.
						 inCount,	// the number of accepted values in the interval.
						 inDecimals,	// Number of decimals to be displayed.
						 itsFrom,	//the minimum display value
						 itsTo, //the maximum display value
						 itsMinDisplay,
			 		     itsMaxDisplay
						 )
{
	var self = this;
	var itsMinSlider = new Slider(inMinID,"x",itsFrom,itsLength,inCount,inDecimals,itsFrom,itsTo,"min");
	var itsMaxSlider = new Slider(inMaxID,"x",itsTo,itsLength,inCount,inDecimals,itsFrom,itsTo,"max");
	var itsProperties = new Properties();
	var itsFilterOn;

	self.initialize = function()
	{
		var theMinValue;
		var theMaxValue;

		Publisher(self);

		//set sliders to the initial positions
		itsMinSlider.initialize();
		itsMaxSlider.initialize();

		itsMinSlider.addSubscriber(itsMaxSlider,itsMaxSlider.setSubscriberPosition);
		itsMinSlider.addSubscriber(self,self.changeState);
		itsMaxSlider.addSubscriber(itsMinSlider,itsMinSlider.setSubscriberPosition);
		itsMaxSlider.addSubscriber(self,self.changeState);

		if (itsMinDisplay  && itsMaxDisplay)
		{
			itsMinDisplay.setValue(itsMinSlider.getValue());
			itsMaxDisplay.setValue(itsMaxSlider.getValue());
			itsMinSlider.addSubscriber(itsMinDisplay,itsMinDisplay.setValue);
			itsMaxSlider.addSubscriber(itsMaxDisplay,itsMaxDisplay.setValue);
		}
		self.changeState();
	};

	self.setSlidersByProperties = function(inProperties)
	{
		var theMinValue;
		var theMaxValue;

		if(!inProperties)
			return;

		theMinValue = inProperties.getProperty("min");
		theMaxValue = inProperties.getProperty("max");

		if(theMinValue)
		{
			itsMinSlider.setSliderPositionByValue(theMinValue);
			if(itsMinDisplay) itsMinDisplay.setValue(itsMinSlider.getValue());

		}
		if(theMaxValue)
		{
			itsMaxSlider.setSliderPositionByValue(theMaxValue);
			if(itsMaxDisplay) itsMaxDisplay.setValue(itsMaxSlider.getValue());
		}

	};


	self.slide = function(inEvent,inType,inPairSliderID)
	{
		if (inType == "min")
			itsMinSlider.slide(inEvent,inPairSliderID);
		else if(inType = "max")
			itsMaxSlider.slide(inEvent,inPairSliderID);

		if(inEvent.type=="mouseup") {
			self.changeState();
		}
	};


	self.reset = function()
	{
		itsMinSlider.setSliderPositionByValue(itsFrom);
		itsMaxSlider.setSliderPositionByValue(itsTo);
		itsProperties.clear();
		itsFilterOn = false;
		self.notifySubscribers(itsProperties,itsFilterOn);
		// in some cases we need self.notifySubscribers(itsValue,null,null) in other cases we need to pass itProperties. ugh, >:| this is some api....need to deal with displays manually
		if (itsMinDisplay  && itsMaxDisplay)
		{
			itsMinDisplay.setValue(itsMinSlider.getValue());
			itsMaxDisplay.setValue(itsMaxSlider.getValue());
		}
	};

	self.changeState = function()
	{
		//check the position of both slider, if any of them off their origianl position, set the filter on
		//this criteria could check if the property exist, filter is on which will depend on business rule
		if (itsMinSlider.getValue() == itsFrom && itsMaxSlider.getValue() == itsTo)
			itsFilterOn = false;
		else
			itsFilterOn = true;

		self.setProperties();
		if (!itsFilterOn)
			self.reset();

		//any slider is adjust to the beginning value
		var isAtBeginning = (itsMinSlider.getValue() == itsFrom || itsMaxSlider.getValue() == itsTo);
		if(arguments.length>4 & arguments[3] == "mouseup") {
			self.notifySubscribers(itsProperties,itsFilterOn, isAtBeginning,'mouseup');
		} else {
			self.notifySubscribers(itsProperties,itsFilterOn, isAtBeginning);
		}
	};

	self.getProperties = function()
	{
		return itsProperties;
	};

	self.setProperties = function()
	{
		if (itsProperties == null)
			itsProperties = new Properties();
		if (itsFilterOn)
		{
			itsProperties.setProperty("min",itsMinSlider.getValue());
			itsProperties.setProperty("max",itsMaxSlider.getValue());
		}
	};

	self.getFilterOn = function()
	{
		return itsFilterOn;
	};

}
