/*-----------------------------------------------------------------------------
 * Floatable Object that allows for the left navigation elevator functionality.
 * This can be accomplished in an easier fashion but this has a nice effect of
 * smoothly settling into place (servo).
 * 
 * Modifications:
 * Date        By          Description
 * ----------  ----------  -------------------------------------------------
 * 11/14/2006  Garth       Initial.
 *---------------------------------------------------------------------------*/

/* http://www.quirksmode.org/dom/getstyles.html
offsetHeight  	height in pixels
offsetLeft 	distance of paragraph from the left, in pixels
(left of what? see below)
offsetTop 	distance of paragraph from the top, in pixels
(top of what? see below)
offsetWidth 	width in pixels

also:
http://www.quirksmode.org/js/fixedmenu.html
*/



// NOTES: 
// - JavaScript functions are objects with properties...
// - style property only reflects the inline styles of an _element - NOT CSS values.
// - prototype is like a class variable or function.  If inline anonymous then
//   function is like a local variable and consumes memory.  Prototypes have issues
//   accessing all the variables due to the context switch depending on what object
//   is calling and the circumstances e.g. if an event handler then can't do it
//   using "this.".
// - implemented as anonymous because it is more self contained AND really don't
//   care about memory usage since there should only be one or two of these things 
//   running at any given time (menu or help popup).

// Constructor.
// Arguments: 
//   - elementIDOrObject = HTML Element ID/object of the container to be floated.
//   - allowVerticalMovement = Flag that controls if the _element is allowed to 
//     float vertically.
//   - allowHorizontalMovement = Flag that controls if the _element is allowed to 
//     float horizontally.
// Returns: new ElementElevator object.
function ElementElevator(elementIDOrObject, allowVerticalMovement, allowHorizontalMovement)
{
	// current object handle so that event handlers and the timer object work.
	// if using "this." those calls are associated with an event so their context
	// is different and is interp as being relative to them (e.g. the current 
	// object).  So the context variable is used in those instances.
	var elementElevator = this;

	// _element to be moved/floated.  If doesn't exist return null...
	var _element = new EnhancedElement(elementIDOrObject);

	// starting position - min values.  These are absolute values because the 
	// scrolling positions are absolute positions.  In past had this init code
	// in the scrollto method because IE wasn't getting the _element in the document
	// in time for this code to set appropriately.  
	var _startingTop     = _element.getAbsoluteTopPosition();
	var _startingLeft    = _element.getAbsoluteLeftPosition();

	// current absolute left/top x/y values.  These are NOT the same as _element.style.top
	// value but could be if menu container is absolutely positioned.  These are
	// absolute for ease of use with the start and destination.  When scrolling 
	// this value is adjusted until it reaches the absolute desired destination
	// postion and the.  The style.top value is adjusted based on this current
	// absolute position value and the container's position value.
	var _currentTop      = _startingTop;
	var _currentLeft     = _startingLeft;

	// Containers position values.  These values are used to adjust the final 
	// style.top/left value so that relative or absolute positioning can be used.
	var _topAdjustment   = _element.getTopPosition()  - _startingTop;
	var _leftAdjustment  = _element.getLeftPosition() - _startingLeft;

	// Final absolute coordinate based on the values set via the scrollTo function.
	var _destinationLeft = 0;
	var _destinationTop  = 0;

	// properties that can be used by other code/user preferences to control if 
	// the menu should be allowed to move.
	this.allowVerticalMovement   = (allowVerticalMovement   ? true : false);
	this.allowHorizontalMovement = (allowHorizontalMovement ? true : false);

	// internals that are used to control if the container should be moved.  The resize handler 
	// sets these values.  Null is used to flag the scroll handler to call the 
	// check sizes function.
	var _isAbleToMoveVertically   = false;
	var _isAbleToMoveHorizontally = false;

	// move timer ID.  Used to instan or kill the move timer object.  
	// The interval properties are exposed for outside control and specify the 
	// frequency inwhich the move function is called (in milli-seconds).
	var _moveTimerID       = null;
	this.startMoveInterval = 200;	// kickoff value - typically set higher so current scroll.
	this.moveInterval      = 100;	// how often the elevator move is performed




	// Checks _element size vs the window size and sets the "float" flags as needed.
	// Get the window size to see if the menu is larger than the window.  If it
	// is, then set so the menu does not move.
	setIsAbleToMove = function()
	{
		var windowWidth  = 0;
		var windowHeight = 0;
		if(typeof(window.innerWidth) == "number") 
		{
			//Non-IE
			windowWidth  = window.innerWidth;
			windowHeight = window.innerHeight;
		} 
		else if(document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight))
		{
			//IE 6+ in 'standards compliant mode'
			windowWidth  = document.documentElement.clientWidth;
			windowHeight = document.documentElement.clientHeight;
		} 
		else if(document.body && (document.body.clientWidth || document.body.clientHeight))
		{
			//IE 4 compatible
			windowWidth  = document.body.clientWidth;
			windowHeight = document.body.clientHeight;
		}

		_isAbleToMoveVertically   = elementElevator.allowVerticalMovement   && (windowHeight > _element.offsetHeight);
		_isAbleToMoveHorizontally = elementElevator.allowHorizontalMovement && (windowWidth  > _element.offsetWidth);

		// If window size less than the _element then set don't scroll and reset
		// the element's position to its starting place.
		if(!_isAbleToMoveVertically  && !_isAbleToMoveHorizontally) _moveTimerID = null;		// clear
		if(!_isAbleToMoveHorizontally) _element.style.left = (_startingLeft + _leftAdjustment) + "px";
		if(!_isAbleToMoveVertically)   _element.style.top  = (_startingTop  + _topAdjustment)  + "px";
	} //~~~~~~~~~~~~~~~~~~~~~~~ End of Function ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~



	// now that it's defined, call to initialize the isFloatable variables
	setIsAbleToMove();	



	// Checks _element size vs the window size and sets the "float" flags as needed.
	this.windowResizeHandler = function(evt)
	{
		setIsAbleToMove();

		// use to clear/turn off the window scroll handler but since this is now
		// done in the event manager and this object doesn't know about it then
		// it's probably best to leave as is and let something higher up control.
	} //~~~~~~~~~~~~~~~~~~~~~~~ End of Function ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~



	// internal helper that localizes the check for if container is positioned 
	// at the destination position.
	isScrolling = function()
	{
		return( (_destinationTop != _currentTop) || (_destinationLeft != _currentLeft) );
	} //~~~~~~~~~~~~~~~~~~~~~~~ End of Function ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~



	// window scroll handler calls this.  This could be put into the scroll handler
	// if left split out then this can be called independently.
	this.scrollTo = function(scrollTop, scrollLeft)
	{
		// do some range checking/adjustment
		if(scrollLeft < _startingLeft)
			_destinationLeft = _startingLeft;
		else
			_destinationLeft = scrollLeft;

		if(scrollTop < _startingTop)
			_destinationTop = _startingTop;
		else
			_destinationTop = scrollTop;

		// call the move function via timer to get smooth "lag" into place effect.
		if(_moveTimerID != null) clearTimeout(_moveTimerID);
		_moveTimerID = setTimeout(elementElevator.move, elementElevator.startMoveInterval);
	} //~~~~~~~~~~~~~~~~~~~~~~~ End of Function ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~



	// set the position to 1/2 the distance then set the timer to call again
	// until current == destination.
	this.move = function()
	{
		// style.top returns "123px" if using then need to parseInt(_element.style.top)
	
		// docs say that Math.round(x) rounds negative values down so that
		// 3.6 ==> 4 and -3.6 ==< -4 but in FF this isn't the case.  To get 
		// around this, an if then is used whereas it could be a simple += 
		// statement without doing any diffs and if diff < 0 etc.
		var diff = 0;
		if(_isAbleToMoveVertically)
		{
			diff = _destinationTop - _currentTop;
			if(diff == -1) 
				_currentTop = _destinationTop;
			else if(diff != 0)
				_currentTop += Math.round(diff/2);

			_element.style.top = (_currentTop + _topAdjustment) + "px";	// added px suffix to be 100% compliant
		}

		if(_isAbleToMoveHorizontally)
		{
			diff = _destinationLeft - _currentLeft;
			if(diff == -1) 
				_currentLeft = _destinationLeft;
			else if(diff != 0)
				_currentLeft += Math.round(diff/2);
			_element.style.left = (_currentLeft + _leftAdjustment) + "px";
		}

		if(isScrolling()) 
			_moveTimerID = setTimeout(elementElevator.move, elementElevator.moveInterval);
		else
			_moveTimerID = null;
	} //~~~~~~~~~~~~~~~~~~~~~~~ End of Function ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~



	// Window Scroll event handler which is registered with the window object.
	this.windowScrollHandler = function(evt)
	{
		var top = 0, left = 0;
		if(window.innerHeight)
		{
			top  = window.pageYOffset;
			left = window.pageXOffset;
		}
		else if(document.documentElement && document.documentElement.scrollTop)
		{
			top  = document.documentElement.scrollTop;
			left = document.documentElement.scrollLeft;
		}
		else if(document.body)
		{
			top  = document.body.scrollTop;
			left = document.body.scrollLeft;
		}
		elementElevator.scrollTo(top, left);
	} //~~~~~~~~~~~~~~~~~~~~~~~ End of Function ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

} //~~~~~~~~~~~~~~~~~~~~~~~~~~ End of Constructor ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/*=============================== End of File ================================*/


