/* ***************************************************************
 *
 * Copyright (c) TimeFrame, Inc. 2005, 2007
 * 
 * File: jswt-util.js
 * Created: January 05, 2006 (20060501)
 * Description:
 *
 * JavaScript Widget Library utility functions.
 * 
 * ***************************************************************
 * CHANGE HISTORY
 * ***************************************************************
 * DATE     USER       RECORD       DESCRIPTION
 * ---------------------------------------------------------------
 * 20060105 dewittsc   ------       Initial version.
 * 
 * *************************************************************** */
	
/* Gregorian Date Constants */

var			GREGORIAN_DAYS_OF_MONTH = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
var			GREGORIAN_DAY_NAMES_EN = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ];
var			GREGORIAN_MONTH_NAMES_EN = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ];
	
/* **********************************************************************
 * Generic Utilities
 * ********************************************************************** */
 
function arrayIndexOf( element )
{
	var array = this;
	var index = -1;
	
	for ( var i = 0; i < array.length; i++ )
	{
		if ( array[i] === element )
		{
			index = i;
			break;
		}	
	}
	
	return index;
}
 
function arrayContains( obj )
{
	var a = this;
	var i;
	var contains = false;
	
	for ( i = 0; i < a.length; i++ )
	{
		var ele = a[i];
		if ( ele == obj )
		{
			contains = true;
			break;
		}
	}
	
	return contains;
}

if ( Array.prototype.indexOf === undefined )
{
	Array.prototype.indexOf = arrayIndexOf;
}

if ( Array.prototype.contains === undefined )
{
	Array.prototype.contains = arrayContains;
}

function frontPad( s, len, pad )
{
	if ( typeof(s) != 'string' )
	{
		s = s.toString();
	}
	
	for ( var i = 0; i < ( len - s.length ); i++ )
	{
		s = pad + s;
	}
	
	return s;
}

function escapeMarkupChars( s )
{
	var	safe;
	
	safe = s.replace( /</gi, '&lt;' );
	safe = safe.replace( />/gi, '&gt;' );
	safe = safe.replace( /[\']/gi, '&apos;' );
	safe = safe.replace( /[\"]/gi, '&quot;' );
	
	return safe;
}

function trim(s)
{
	var ret = '';
	
	if ( s != null && s.length && s.length > 0  )
	{
		var start = 0;
		var end = s.length - 1;
		var c = '';
		
		while ( start < s.length && ( ( c = s.charAt(start) ) == ' ' || c == '\t' || c == '\r' || c == '\n' ) ) { start++; }
		while ( end > start && ( ( c = s.charAt(end) ) == ' ' || c == '\t' || c == '\r' || c == '\n' ) ) { end--; }
		
		if ( start <= end ) { ret = s.substring( start, end + 1 ); }
	}
	
	return ret;
}

/* **********************************************************************
 * Debug Utilities
 * ********************************************************************** */
 
function inspect( obj, obj_name )
{
	var result = obj_name + "[ ";
	
   	for (var i in obj) 
   	{
   		result += i + " = " + obj[i] + ",";
   	}
   	result += " ]";
   	
   	alert( result );
}
		
function initTrace(winId)
{
	if ( window[winId] == null || window[winId].closed )
	{
		window[winId] = window.open( 'about:blank', winId, 'width=800,height=800,scrollbars=yes,resizable=yes,status=yes' );
		window[winId].document.write( '<html><head><title>Js Trace Console - ' + winId + '</title>' );
		window[winId].document.write( '<style>DIV#jsTraceBuffer{border:1px solid gray;width:780px;height:780px;overflow:auto;text-align:left;font-family:Courier,monospace;font-size:small;}SPAN.jsTraceTs{font-weight:bold;font-family:Courier,monospace;font-size:small;}</style></head>' );
		window[winId].document.write( '</head><body><div id="jsTraceBuffer"></div></body></html>' );
		if ( window[winId].document.close ) { window[winId].document.close(); }
	}
	else
	{
		window[winId].focus();
	}
	window.jsTraceWin = window[winId];
	trace('initTrace -- activated ' + winId);
}

function trace( s )
{
	var buffer;
	
	if ( window.jsTraceWin )
	{
		buffer = getElement( 'jsTraceBuffer', window.jsTraceWin.document );
	}
	else
	{
		buffer = getElement( 'jsTraceBuffer' );
	}
	
	if ( buffer )
	{
		var now = new Date();
		var dateTime = frontPad(now.getMonth(),2,'0') + '.' +
					   frontPad(now.getDate(),2,'0') + '.' +
					   now.getFullYear() + ' ' +
					   frontPad(now.getHours(),2,'0') + ':' +
					   frontPad(now.getMinutes(),2,'0') + ':' +
					   frontPad(now.getSeconds(),2,'0') + '.' +
					   frontPad(now.getMilliseconds(),3,'0');
					   
		buffer.innerHTML = buffer.innerHTML + ('<span class="jsTraceTs">' + dateTime + '</span> ' + escapeMarkupChars(s) + '<br />' );
	}
}

function curryFunc( aThis, aFunc )
{
	var	curryFunc = function () { var thisFunc = arguments.callee; return thisFunc.aFunc.apply( thisFunc.aThis, arguments ); };
	
	curryFunc.aThis = aThis;
	curryFunc.aFunc = aFunc;	
	
	return curryFunc;
}

/* **********************************************************************
 * DOM Utilities
 * ********************************************************************** */
 
/*
 * Return the element with the given unique identifier.
 *
 * @param id   a unique id.
 * @return the element.
 */
 
function getElement( id, aDoc )
{
	var	obj = null;
	var doc = ( aDoc || document );
	
  	if (doc.getElementById)
  	{
  		obj = doc.getElementById(id);
  	}
  	else if (doc.all)
  	{
		obj = doc.all[id];
	}
 	else if (doc.layers)
  	{
   		obj = doc.layers[id];
  	}
  	
  	return obj;
}

/**
 * Return the value of the attribute with the given name on the given element.
 *
 * @param element   the element.
 * @param name      the attribute name.
 * @return the attribute value.
 */
 
function getAttributeValue( element, name )
{
	var value = null;
	
	value = element.getAttribute(name);
	if ( value == null )
	{
		if ( element.attributes )
		{
			value = element.attributes[name];
			
			if ( value == null && element.attributes.item )
			{
				value = element.attributes.item(name);
			}
		}
			
		if ( value == null )
		{
			value = element[name];  
		}
	}
	
	return value;	
}

/**
 * Return the first child node of the specified element with the given
 * name.  If more than one child with the given name exists, only the first
 * is returned.  If none exist, <code>null</code> is returned.
 *
 * @param element   the parent element.
 * @param name      the tag name.
 * @return the first child with the given name or <code>null</code>.
 */
  
function getChildByName( element, name )
{
	var children = element.childNodes;
	var child = null;
	
	if ( children != null )
	{
		var childCount = children.length;
		var i = 0;
		
		name = name.toLowerCase();
		
		for ( i = 0; i < childCount; i++ )
		{
			if ( children[i].nodeName.toLowerCase() == name )
			{
				child = children[i];
				break;
			}	
		}
	}
	
	return child;
}

/**
 * Return the first ancestor node of the specified element with the given
 * name.  If more than one ancestor with the given name exists, only the first
 * is returned.  If none exist, <code>null</code> is returned.
 *
 * @param element   the child element.
 * @param name      the tag name.
 * @return the first ancestor with the given name or <code>null</code>.
 */
 
function getAncestorByName( element, name )
{
	var	parent = element.parentNode;
	while ( parent != null )
	{
		if ( parent.nodeName.toLowerCase() == name )
		{
			break;
		}
		else
		{
			parent = parent.parentNode;
		}
	}
	return parent;
}

/**
 * Return the node value of the first child with the specified name of the specified parent.
 * If no such child exists, return the empty string.  This is a convenience method intended
 * to be used to retrieve the text value of child elements that contain a single text
 * node.
 *
 * @param parent   the parent node.
 * @param name     the name of the child node.
 * @return the value of the text node within the first named child of the parent. 
 */
 
function getChildNodeValue( parent, name )
{
	var element = getChildByName( parent, name );
	var text = '';
	
	if ( element != null && element.firstChild != null )
	{
	    text = element.firstChild.nodeValue;
	}
	
	return text;
}

 /* **********************************************************************
 * Widget Utilities
 * ********************************************************************** */

function invokeWidgetMethod( wid, methodName, args )
{
	var	peer = getElement(wid);
	var rc = true;
	
	if ( peer != null && peer.jsWidget )
	{
		var widget = peer.jsWidget;
		if ( widget[methodName] )
		{
			rc = widget[methodName].apply( widget, args );
		}
	}
	
	return rc;
}

/**
 * Convert the horizontal alignment bit for this widget to an HTML align value.
 *
 * @param widget   the widget.
 * @return the valign value.
 */
 	
function jsResolveAlignment( alignment )
{
	var align = 'center';

	if ( ( alignment & HALIGN_LEFT ) != 0 ) { align = 'left'; }
	else if ( ( alignment & HALIGN_CENTER ) != 0 ) { align = 'center'; }
	else if ( ( alignment & HALIGN_RIGHT ) != 0 ) { align = 'right'; }
	
	return align;
}

/**
 * Convert the vertical alignment bit for this widget to an HTML valign value.
 *
 * @param widget   the widget.
 * @return the valign value.
 */
 
function jsResolveVerticalAlignment( alignment )
{
	var align = 'middle';
	
	if ( ( alignment & VALIGN_TOP ) != 0 ) { align = 'top'; }
	else if ( ( alignment & VALIGN_MIDDLE ) != 0 ) { align = 'middle'; }
	else if ( ( alignment & VALIGN_BOTTOM ) != 0 ) { align = 'bottom'; }
	
	return align;
}

/* **********************************************************************
 * Event Utilities
 * ********************************************************************** */
	 
/**
 * Add an event listener to the given property on the given element.
 * If an event listener already exists, the new listener will be added after
 * this listener.
 *
 * @param element    the element.
 * @param attr       the property.
 * @param listener   the listener.
 */
 
function jsAddEventListener( element, attr, listener )
{
	var	currListener = element[attr];
	var handlerProp = attr + '_evtp';
	
	if ( element[handlerProp] == null )
	{
		element[handlerProp] = new EventProxy();
		
		if ( currListener != null )
		{
			element[handlerProp].addEventListener( currListener );
		}
		element[handlerProp].addEventListener( listener );
		
		element[attr] = function( event ) { return this[handlerProp].fireEvent( getEvent( event ) ); };
	}
	else
	{
		element[handlerProp].addEventListener( listener );
	}
}

/**
 * Remove an event listener from the given property on the given element.
 *
 * @param element    the element.
 * @param attr       the property.
 * @param listener   the listener.
 */
 
function jsRemoveEventListener( element, attr, listener )
{
	var currListener = element[attr];
	var handlerProp = attr + '_evtp';
	
	if ( element[handlerProp] )
	{
		var listeners;
		var listenerCount;
		
	 	element[handlerProp].removeEventListener( listener ); 
	 	listeners = element[handlerProp].getEventListeners();
	 	listenerCount = listeners.length;
	 	
		if ( listenerCount <= 1 )
		{
			if ( listenerCount == 1 && !listeners[0].handleEvent )
			{
				element[attr] = listeners[0];
			}
			else
			{ 
				element[attr] = null;
			}
			delete element[handlerProp];
		}
	}
}
 
/**
 * Return the last event.
 *
 * @param event  a potential event.
 * @return the last event that occurred.
 */
 
function getEvent( event )
{
	if ( !event && window.event )
	{
		event = window.event;
	}
	return event;
}

/**
 * Return the 'onXXXXXX' type string for the given event.
 *
 * @param event   the event.
 * @return the 'onXXXXXX' type string.
 */
 
function getEventType( event )
{
	return 'on' + event.type;
}

/**
 * Return the HTML element that triggered the given event.
 *
 * @param event   the event.
 * @return the HTML event that triggered the event.
 */
 
function getEventTarget( event )
{
	var target = null;
	
	if ( event.target )
	{
		target = event.target;
	}
	else if ( event.srcElement )
	{
		target = event.srcElement;
	}
	
	return target;
}

/**
 * Create and return an event object for use in synthesizing
 * generic UI events.
 *
 * @param type   the event type.
 * @return the event object.
 */
 
function createEvent( type, canBubble )
{
	var event = null;
	
	if ( document.createEvent )
	{
		event = document.createEvent( "HTMLEvents" );
		event.initEvent( type, canBubble, true );
	}
	else if ( document.createEventObject )
	{
		event = document.createEventObject();
		event.cancelBubble = !canBubble;
		event.type = type;
	}
	
	return event;
}

/**
 * Stop the propagation of the given event.
 *
 * @param event   the event. 
 */

function stopEventPropagation( event )
{
	if ( event.stopPropagation )
	{
		event.stopPropagation();
	}
	else
	{
		event.cancelBubble = true;
	}
}

/**
 * Return the key that was pressed for the given key event.
 *
 * @param event   the event.
 */
 
function getKey( event )
{
	var	key;
	
	if ( event.which )
	{
		key = event.which;
	}
	else if ( event.keyCode )
	{
		key = event.keyCode;
	}
	
	return key;
}

/**
 * Return true if the given key code represents the enter key.
 *
 * @param key   the key code.
 */
 
function isEnterKey( key )
{
	return ( key == 13 || key == 10 );
}

/* **********************************************************************
 * Date Utilities
 * ********************************************************************** */

/**
 * Parse a string in the form YYYY-MM-DD (the standard format returned from MySQL) into a
 * Javascript Date object.
 *
 * @param s   the string.
 * @return the date object for <code>s</code>.
 */
 
function stringToDate( s, regex )
{
	if ( !regex )
	{
		regex = /([0-9]{4})\-([0-1][0-9])\-([0-3][0-9])/i;
	}
	
	var	arr = s.match(regex);
	var date = null;

	if ( arr.length >= 4 )
	{
		date = new Date( arr[1], arr[2] - 1, arr[3] );
	}
	
	return ( date != null ? date : new Date() );
}
 
/**
 * Return the given date as a string using the specified formatting.
 *
 * @param date        the date.
 * @param shortForm   the short form flag.
 * @return a date string in long format.
 */
 
function dateToString( date, shortForm )
{
	var s = '';
	
	if ( shortForm )
	{
		/* <NON-NLS> */
		s = date.getFullYear() + '-' + frontPad( date.getMonth() + 1, 2, '0' ) + '-' + frontPad( date.getDate(), 2, '0' );	
		/* </NON-NLS> */
	}
	else
	{ 
		/* <NON-NLS> */
		s = GREGORIAN_DAY_NAMES_EN[date.getDay()] + ', ' +
			GREGORIAN_MONTH_NAMES_EN[date.getMonth()] + ' ' +
			date.getDate() + ', ' +
			date.getFullYear();
		/* </NON-NLS> */
	}
	
	return s;
}
 
/* **********************************************************************
 * DHTML Utilities
 * ********************************************************************** */

/*
 * Standard handler for text link over effects.
 *
 * @param link   the link object.
 */
 
function setStyleClass( element, styleClass )
{
	element.className = styleClass;
}

/**
 * Set the focus of the given element to the given value.
 *
 * @param element   the element.
 * @param focus     the focus flag.
 */
 
function setFocus( element, focus )
{
	if ( focus && element.focus )
	{
		element.focus();
	}
	else if ( !focus && element.blur )
	{
		element.blur();
	}
}

/**
 * Return the actual <code>(top, left)</code> coordinates of the onscreen
 * <code>element</code> by iterating up its parent tree using tail recursion.
 *
 * @param element   the element.
 * @return the coordinates of the <code>element</code>s origin.
 */
 
function getScreenOrigin( element )
{
	var parent = element.offsetParent;
	var origin;

	if ( parent == null )
	{
		origin = new Point( element.offsetTop, element.offsetLeft );
	}
	else
	{
		origin = getScreenOrigin( parent );
		origin.x += element.offsetTop;
		origin.y += element.offsetLeft;
	}
	
	return origin;
}

function getViewportBounds()
{
	var top, left;
	var width, height;
	
	if ( self.screenTop )
	{
		top = self.screenTop;
		left = self.screenLeft;
	}
	else if ( self.screenY )
	{
		top = self.screenY;
		left = self.screenX;
	}
	
	/* START Ripped from quirksmode.org (http://www.quirksmode.org/viewport/compatibility.html) */
	
	if ( self.innerHeight ) // all except Explorer
	{
		width = self.innerWidth;
		height = self.innerHeight;
	}
	else if ( document.documentElement && document.documentElement.clientHeight )  // Explorer 6 Strict Mode
	{
		width = document.documentElement.clientWidth;
		height = document.documentElement.clientHeight;
	}
	else if ( document.body ) // other Explorers
	{
		width = document.body.clientWidth;
		height = document.body.clientHeight;
	}	
	
	/* END Ripped from quirksmode.org (http://www.quirksmode.org/viewport/compatibility.html) */
	
	return new Rect( new Point( top, left ), width, height );	
}

function getDsocOrigin()
{
	if (window.pageYOffset!==undefined) { return new Point(window.pageXOffset,window.pageYOffset); }
	else if (document.documentElement&&document.documentElement.scrollTop!==undefined) { return new Point(document.documentElement.scrollLeft,document.documentElement.scrollTop); }
	else if (document.body&&document.body.scrollTop!==undefined) { return new Point(document.body.scrollLeft,document.body.scrollTop); }
	else { return new Point(0,0); }
}

/* **********************************************************************
 * Animation
 * ********************************************************************** */

/**
 * Fade the specified style <code>attribute</code> of the specified
 * <code>element</code> through a gradient beginning at the specified
 * <code>start</code>ing RGB and ending at the specified <code>end</code>ing
 * RGB using the specified <code>steps</code> and repeat <code>rate</code>.
 *
 * @param element     the element.
 * @param attribute   <code>0</code> to fade the background color,
 *                    <code>1</code> to fade the border color,
 *                    <code>2</code> to fade the foreground color.
 * @param start       the starting RGB in the form <code>RRGGBB</code>.
 * @param end         the ending RGB in the form <code>RRGGBB</code>.
 * @param steps       the number of steps in the gradient.
 * @param rate        the rate of change for the gradient.
 */
 
function fade( element, attribute, start, end, steps, rate, loopType, loopIters )
{
	var	startRgb = new Rgb( start );
	var endRgb = new Rgb( end );
	var deltaR = startRgb.r - endRgb.r;
	var deltaG = startRgb.g - endRgb.g;
	var deltaB = startRgb.b - endRgb.b;
	var stepR = deltaR / steps;
	var stepG = deltaG / steps;
	var stepB = deltaB / steps;
	var stepi = 1;
	
	var r = startRgb.r;
	var g = startRgb.g;
	var b = startRgb.b;
	
	element.looping = ( loopType != null && loopIters != 0 );
	
	var timeoutFunc =
		function()
		{
			var again = true;
			
			if ( stepi == steps )
			{
				r = endRgb.r;
				g = endRgb.g;
				b = endRgb.b;
				
				if ( loopType != null && element.looping )
				{
					if ( loopType == 0 && loopIters != 0 ) // symmetrical
					{
						var tempRgb = startRgb;
						startRgb = endRgb;
						endRgb = tempRgb;
						deltaR = startRgb.r - endRgb.r;
						deltaG = startRgb.g - endRgb.g;
						deltaB = startRgb.b - endRgb.b;
						stepR = deltaR / steps;
						stepG = deltaG / steps;
						stepB = deltaB / steps;
						stepi = 1;
						if ( loopIters > 0 ) { loopIters-- };
					}
					else if ( loopType == 1 && loopIters != 0 ) // asymmetrical
					{
						r = startRgb.r;
						g = startRgb.g;
						b = startRgb.b;
						stepi = 1;
						if ( loopIters > 0 ) { loopIters-- };					
					}
				}
				else
				{
					again = false;	
				}			
			}
			else
			{	
				r -= stepR;
				g -= stepG;
				b -= stepB;
				stepi++;
			}
			
			var newRgb = "rgb( " + Math.round(r) + "," + Math.round(g) + "," + Math.round(b) + ")";
			
			if ( attribute == 0 ) {	element.style.backgroundColor = newRgb; }
			else if ( attribute == 1 ) { element.style.borderColor = newRgb; }
			else if ( attribute == 2 ) { element.style.color = newRgb; }
	
			if ( again ) { setTimeout( timeoutFunc, rate ); }
		};
	
	var newRgb = "rgb( " + r + "," + g + "," + b + ")";
	
	if ( attribute == 0 ) {	element.style.backgroundColor = newRgb; }
	else if ( attribute == 1 ) { element.style.borderColor = newRgb; }
	else if ( attribute == 2 ) { element.style.color = newRgb; }
	
	setTimeout( timeoutFunc, rate );
}

function createClipState( box, transition, show )
{
	var method = ( transition.method || 'slide' );
	var direction = ( transition.variation || 'right' );
	var state = { method: method,
			 	  direction: direction,
			 	  rate: ( transition.rate || 25 ),
			 	  frames: ( transition.frames || 15 ),
			 	  show: show,
			 	  clip: ( show ? new Rect( new Point( 0, 0 ), 0, 0 ) : new Rect( new Point( 0, 0 ), box.offsetWidth, box.offsetHeight ) ) };
			 	  
	if ( method == 'sliver' )
	{
		state.sliverRatio = ( transition.sliverRatio || 0.5 );
	}		
	else if ( method == 'transparent' )
	{
		state.maxOpacity = ( transition.maxOpacity || 100 );
	}	
	
	if(transition.callback){state.callback=transition.callback;}
	
	return state;
}

function initClipState( box, state )
{
	var method = state.method;
	var direction = state.direction;
	var show = state.show;
	
	switch ( method )
	{
		case 'slide':	
			if ( direction == 'right' ) 
			{ 
				state.slideDelta = ( box.offsetWidth / state.frames );
				if ( show ) { state.clip = new Rect( new Point( 0, 0 ), 0, box.offsetHeight ); }
			}
			else if ( direction  == 'left' ) 
			{ 
				state.slideDelta = ( box.offsetWidth / state.frames );
				if ( show ) { state.clip = new Rect( new Point( 0, box.offsetWidth ), 0, box.offsetHeight ); } 
			}
			else if ( direction == 'down' ) 
			{ 
				state.slideDelta = ( box.offsetHeight / state.frames );
				if ( show ) { state.clip = new Rect( new Point( 0, 0 ), box.offsetWidth, 0 ); }
			}
			else if ( direction == 'up' ) 
			{ 
				state.slideDelta = ( box.offsetHeight / state.frames );
				if ( show ) { state.clip = new Rect( new Point( box.offsetHeight, 0 ), box.offsetWidth, 0 ); }
			}
			break;
			
		case 'shutter':
			if ( direction == 'center' || direction == 'hcenter' )
			{
				var centerx = box.offsetWidth / 2;
				state.slideWidthDelta = ( box.offsetWidth / state.frames );
				state.slideXDelta = ( state.slideWidthDelta / 2 );
				if ( show ) { state.clip = new Rect( new Point( 0, centerx ), 0, box.offsetHeight ); }
			}
			else if ( direction == 'vcenter' )
			{
				var centery = box.offsetHeight / 2;
				state.slideHeightDelta = ( box.offsetHeight / state.frames );
				state.slideYDelta = ( state.slideHeightDelta / 2 );
				if ( show ) { state.clip = new Rect( new Point( centery, 0 ), box.offsetWidth, 0 );	}		
			}
			break;
			
		case 'shield':
			if ( direction == 'bottomRight' )
			{
				state.slideHeightDelta = ( box.offsetHeight / state.frames );
				state.slideWidthDelta = ( box.offsetWidth / state.frames );
				if ( show ) { state.clip = new Rect( new Point( 0, 0 ), 0, 0 ); }
			}
			else if ( direction == 'topRight' )
			{
				state.slideHeightDelta = ( box.offsetHeight / state.frames );
				state.slideWidthDelta = ( box.offsetWidth / state.frames );
				if ( show ) { state.clip = new Rect( new Point( box.offsetHeight, 0 ), 0, 0 ); }
			}	
			else if ( direction == 'bottomLeft' )
			{
				state.slideHeightDelta = ( box.offsetHeight / state.frames );
				state.slideWidthDelta = ( box.offsetWidth / state.frames );
				if ( show ) { state.clip = new Rect( new Point( 0, box.offsetWidth ), 0, 0 ); }
			}	
			else if ( direction == 'topLeft' )
			{
				state.slideHeightDelta = ( box.offsetHeight / state.frames );
				state.slideWidthDelta = ( box.offsetWidth / state.frames );
				if ( show ) { state.clip = new Rect( new Point( box.offsetHeight, box.offsetWidth ), 0, 0 ); }
			}	
			else if ( direction == 'center' )
			{
				var centerx = ( box.offsetWidth / 2 );
				var centery = ( box.offsetHeight / 2 );
				state.slideHeightDelta = ( box.offsetHeight / state.frames );
				state.slideYDelta = ( state.slideHeightDelta / 2 );
				state.slideWidthDelta = ( box.offsetWidth / state.frames);
				state.slideXDelta = ( state.slideWidthDelta / 2 );
				if ( show ) { state.clip = new Rect( new Point( centery, centerx ), 0, 0 ); }
			}								
			break;
			
		case 'sliver':
			if ( direction == 'topCenterDown' )
			{
				var centerx = box.offsetWidth / 2;
				var sliverRatio = state.sliverRatio;
				state.frameCount = 0;
				state.sliverFrames = ( state.frames * sliverRatio );
				state.shutterFrames = ( state.frames - state.sliverFrames );
				state.slideHeightDelta = ( box.offsetHeight / state.sliverFrames );
				state.slideWidthDelta = ( box.offsetWidth / state.shutterFrames );
				state.slideXDelta = ( state.slideWidthDelta / 2 );
				if ( show ) { state.clip = new Rect( new Point( 0, centerx - 3 ), 6, 0 ); }		
			}
			else if ( direction == 'hcenterOut' )
			{
				var centerx = box.offsetWidth / 2;
				var centery = box.offsetHeight / 2;
				var sliverRatio = state.sliverRatio;
				state.frameCount = 0;
				state.sliverFrames = ( state.frames * sliverRatio );
				state.shutterFrames = ( state.frames - state.sliverFrames );
				state.slideHeightDelta = ( box.offsetHeight / state.sliverFrames );
				state.slideYDelta = ( state.slideHeightDelta / 2 );
				state.slideWidthDelta = ( box.offsetWidth / state.shutterFrames );
				state.slideXDelta = ( state.slideWidthDelta / 2 );
				if ( show ) { state.clip = new Rect( new Point( centery, centerx - 3 ), 6, 0 );	}		
			}
			else if ( direction == 'vcenterOut' )
			{
				var centerx = box.offsetWidth / 2;
				var centery = box.offsetHeight / 2;
				var sliverRatio = state.sliverRatio;
				state.frameCount = 0;
				state.sliverFrames = ( state.frames * sliverRatio );
				state.shutterFrames = ( state.frames - state.sliverFrames );
				state.slideHeightDelta = ( box.offsetHeight / state.shutterFrames );
				state.slideYDelta = ( state.slideHeightDelta / 2 );
				state.slideWidthDelta = ( box.offsetWidth / state.sliverFrames );
				state.slideXDelta = ( state.slideWidthDelta / 2 );
				if ( show ) { state.clip = new Rect( new Point( centery - 3, centerx ), 0, 6 );	}		
			}
			break;
			
		case 'transparent':
			state.clip = new Rect( new Point( 0, 0 ), box.offsetWidth, box.offsetHeight );
			state.opacityDelta = ( state.frames == 0 ? state.maxOpacity : ( state.maxOpacity / state.frames ) );
			if ( show )
			{
				state.opacity = 0;
				box.style.filter = "alpha(opacity=0)";
				box.style.opacity = "0.0";
				box.style.MozOpacity = "0.0";
			}
			else
			{
				state.opacity = state.maxOpacity;
			}
			break;
	}
}

function calculateNextClip( box, state )
{
	var method = state.method;
	var direction = state.direction;
	var show = state.show;
	var clipRect = state.clip;
	
	switch ( method )
	{
		case 'slide':
			if ( direction == 'right' ) 
			{ 
				if ( show ) { clipRect.width = Math.min( clipRect.width + state.slideDelta, box.offsetWidth ); }
				else { clipRect.width = Math.max( clipRect.width - state.slideDelta, 0 ); } 
			}
			else if ( direction == 'left' ) 
			{ 
				if ( show )
				{
					clipRect.left = Math.max( clipRect.left - state.slideDelta, 0 );
					clipRect.width = Math.min( clipRect.width + state.slideDelta, box.offsetWidth ); 
				}
				else
				{
					clipRect.left = Math.min( clipRect.left + state.slideDelta, box.offsetWidth );
					clipRect.width = Math.max( clipRect.width - state.slideDelta, 0 ); 				
				}
			}
			else if ( direction == 'down' ) 
			{ 
				if ( show ) { clipRect.height = Math.min( clipRect.height + state.slideDelta, box.offsetHeight ); }
				else { clipRect.height = Math.max( clipRect.height - state.slideDelta, 0 ); } 
			}
			else if ( direction == 'up' ) 
			{ 
				if ( show )
				{
					clipRect.top = Math.max( clipRect.top - state.slideDelta, 0 );
					clipRect.height = Math.min( clipRect.height + state.slideDelta, box.offsetHeight );
				}
				else
				{
					clipRect.top = Math.min( clipRect.top + state.slideDelta, box.offsetHeight );
					clipRect.height = Math.max( clipRect.height - state.slideDelta, 0 );				
				} 
			}
			break;
			
		case 'shutter':
			if ( direction == 'center' || direction == 'hcenter' )
			{
				if ( show )
				{
					clipRect.left = Math.max( clipRect.left - state.slideXDelta, 0 );
					clipRect.width = Math.min( clipRect.width + state.slideWidthDelta, box.offsetWidth );
				}
				else
				{
					var centerx = box.offsetWidth / 2;
					clipRect.left = Math.min( clipRect.left + state.slideXDelta, centerx );
					clipRect.width = Math.max( clipRect.width - state.slideWidthDelta, 0 );
				} 
			}
			else if ( direction == 'vcenter' )
			{
				if ( show )
				{
					clipRect.top = Math.max( clipRect.top - state.slideYDelta, 0 );
					clipRect.height = Math.min( clipRect.height + state.slideHeightDelta, box.offsetHeight );
				}
				else
				{
					var centery = box.offsetHeight / 2;
					clipRect.top = Math.min( clipRect.top + state.slideYDelta, centery );
					clipRect.height = Math.max( clipRect.height - state.slideHeightDelta, 0 );				
				}				
			}
			break;
			
		case 'shield':
			if ( direction == 'bottomRight' )
			{
				if ( show )
				{
					clipRect.width = Math.min( clipRect.width + state.slideWidthDelta, box.offsetWidth );
					clipRect.height = Math.min( clipRect.height + state.slideHeightDelta, box.offsetHeight );
				}
				else
				{
					clipRect.width = Math.max( clipRect.width - state.slideWidthDelta, 0 );
					clipRect.height = Math.max( clipRect.height - state.slideHeightDelta, 0 );				
				}
			}
			else if ( direction == 'topRight' )
			{
				if ( show )
				{
					clipRect.width = Math.min( clipRect.width + state.slideWidthDelta, box.offsetWidth );
					clipRect.top = Math.max( clipRect.top - state.slideHeightDelta, 0 );
					clipRect.height = Math.min( clipRect.height + state.slideHeightDelta, box.offsetHeight );			
				}
				else
				{
					clipRect.width = Math.max( clipRect.width - state.slideWidthDelta, 0 );
					clipRect.top = Math.min( clipRect.top + state.slideHeightDelta, box.offsetHeight );
					clipRect.height = Math.max( clipRect.height - state.slideHeightDelta, 0 );				
				}
			}
			else if ( direction == 'bottomLeft' )
			{
				if ( show )
				{
					clipRect.left = Math.max( clipRect.left - state.slideWidthDelta, 0 );
					clipRect.width = Math.min( clipRect.width + state.slideWidthDelta, box.offsetWidth );
					clipRect.height = Math.min( clipRect.height + state.slideHeightDelta, box.offsetHeight );
				}
				else
				{
					clipRect.left = Math.min( clipRect.left + state.slideWidthDelta, box.offsetWidth );
					clipRect.width = Math.max( clipRect.width - state.slideWidthDelta, 0 );
					clipRect.height = Math.max( clipRect.height - state.slideHeightDelta, 0 );				
				}
			}
			else if ( direction == 'topLeft' )
			{
				if ( show )
				{
					clipRect.left = Math.max( clipRect.left - state.slideWidthDelta, 0 );
					clipRect.width = Math.min( clipRect.width + state.slideWidthDelta, box.offsetWidth );
					clipRect.top = Math.max( clipRect.top - state.slideHeightDelta, 0 );
					clipRect.height = Math.min( clipRect.height + state.slideHeightDelta, box.offsetHeight );									
				}
				else
				{
					clipRect.left = Math.min( clipRect.left + state.slideWidthDelta, box.offsetWidth );
					clipRect.width = Math.max( clipRect.width - state.slideWidthDelta, 0 );
					clipRect.top = Math.min( clipRect.top + state.slideHeightDelta, box.offsetHeight );
					clipRect.height = Math.max( clipRect.height - state.slideHeightDelta, 0 );													
				}
			}
			else if ( direction == 'center' )
			{
				if ( show )
				{
					clipRect.left = Math.max( clipRect.left - state.slideXDelta, 0 );
					clipRect.width = Math.min( clipRect.width + state.slideWidthDelta, box.offsetWidth );
					clipRect.top = Math.max( clipRect.top - state.slideYDelta, 0 );
					clipRect.height = Math.min( clipRect.height + state.slideHeightDelta, box.offsetHeight );
				}
				else
				{
					var	centerx = ( box.offsetWidth / 2 );
					var centery = ( box.offsetHeight / 2 );
					clipRect.left = Math.min( clipRect.left + state.slideXDelta, centerx );
					clipRect.width = Math.max( clipRect.width - state.slideWidthDelta, 0 );
					clipRect.top = Math.min( clipRect.top + state.slideYDelta, centery );
					clipRect.height = Math.max( clipRect.height - state.slideHeightDelta, 0 );	
				}
			}
			break;
			
		case 'sliver':
			if ( direction == 'topCenterDown' )
			{
				if ( show )
				{
					if ( state.frameCount < state.sliverFrames )
					{
						clipRect.height = Math.min( clipRect.height + state.slideHeightDelta, box.offsetHeight );
					}
					else
					{
						clipRect.left = Math.max( clipRect.left - state.slideXDelta, 0 );
						clipRect.width = Math.min( clipRect.width + state.slideWidthDelta, box.offsetWidth );					
					}
				}
				else
				{
					if ( state.frameCount < state.shutterFrames )
					{
						var centerx = ( box.offsetWidth / 2 ) - 3;
						clipRect.left = Math.min( clipRect.left + state.slideXDelta, centerx );
						clipRect.width = Math.max( clipRect.width - state.slideWidthDelta, 6 );										
					}
					else
					{
						clipRect.height = Math.max( clipRect.height - state.slideHeightDelta, 0 );
					}				
				}
				state.frameCount++;
			}
			else if ( direction == 'hcenterOut' )
			{
				if ( show )
				{
					if ( state.frameCount < state.sliverFrames )
					{
						clipRect.top = Math.max( clipRect.top - state.slideYDelta, 0 );
						clipRect.height = Math.min( clipRect.height + state.slideHeightDelta, box.offsetHeight );
					}
					else
					{
						clipRect.left = Math.max( clipRect.left - state.slideXDelta, 0 );
						clipRect.width = Math.min( clipRect.width + state.slideWidthDelta, box.offsetWidth );					
					}	
				}
				else
				{
					if ( state.frameCount < state.shutterFrames )
					{
						var centerx = ( box.offsetWidth / 2 ) - 3;
						clipRect.left = Math.min( clipRect.left + state.slideXDelta, centerx );
						clipRect.width = Math.max( clipRect.width - state.slideWidthDelta, 6 );										
					}
					else
					{
						var centery = ( box.offsetHeight / 2 );
						clipRect.top = Math.min( clipRect.top + state.slideYDelta, centery );
						clipRect.height = Math.max( clipRect.height - state.slideHeightDelta, 0 );
					}					
				}	
				state.frameCount++;	
			}
			else if ( direction == 'vcenterOut' )
			{
				if ( show )
				{
					if ( state.frameCount < state.sliverFrames )
					{
						clipRect.left = Math.max( clipRect.left - state.slideXDelta, 0 );
						clipRect.width = Math.min( clipRect.width + state.slideWidthDelta, box.offsetWidth );
					}
					else
					{
						clipRect.top = Math.max( clipRect.top - state.slideYDelta, 0 );
						clipRect.height = Math.min( clipRect.height + state.slideHeightDelta, box.offsetHeight );					
					}
				}
				else
				{
					if ( state.frameCount < state.shutterFrames )
					{
						var centery = ( box.offsetHeight / 2 ) - 3;
						clipRect.top = Math.min( clipRect.top + state.slideYDelta, centery );
						clipRect.height = Math.max( clipRect.height - state.slideHeightDelta, 6 );						
					}
					else
					{
						var centerx = ( box.offsetWidth / 2 );
						clipRect.left = Math.min( clipRect.left + state.slideXDelta, centerx );
						clipRect.width = Math.max( clipRect.width - state.slideWidthDelta, 0 );						
					}
				}
				state.frameCount++;		
			}			
			break;	
			
		case 'transparent':
			if ( show )
			{
				state.opacity = Math.min( state.opacity + state.opacityDelta, state.maxOpacity );
			}
			else
			{
				state.opacity = Math.max( state.opacity - state.opacityDelta, 0 );
			}
			box.style.filter = "alpha(opacity=" + state.opacity + ")";
			box.style.opacity = "" + (state.opacity / 100 );
			box.style.MozOpacity = "" + (state.opacity / 100);			
			break;		
	}
	
	return clipRect.toCssString();
}

function animationComplete( box, state )
{
	var method = state.method;
	var direction = state.direction;
	var show = state.show;
	var clipRect = state.clip;
	var complete = false;
	
	switch ( method )
	{
		case 'slide':
			if ( direction == 'right' ) { complete = ( show ? clipRect.width >= box.offsetWidth : clipRect.width <= 0 ); }
			else if ( direction == 'left' ) { complete = ( show ? clipRect.left <= 0 : clipRect.left >= box.offsetWidth ); }
			else if ( direction == 'down' ) { complete = ( show ? clipRect.height >= box.offsetHeight : clipRect.height <= 0 ); }
			else if ( direction == 'up' ) { complete = ( show ? clipRect.top <= 0 : clipRect.top >= box.offsetHeight ); }
			break;
			
		case 'shutter':
			if ( direction == 'center' || direction == 'hcenter' ) { complete = ( show ? clipRect.left <= 0 : clipRect.width <= 0 ); }
			else if ( direction == 'vcenter' ) { complete = ( show ? clipRect.top <= 0 : clipRect.height <= 0 ); }
			break;
			
		case 'shield':
			if ( direction == 'bottomRight' ) { complete = ( show ? ( clipRect.height >= box.offsetHeight && clipRect.width >= box.offsetWidth ) : ( clipRect.height <= 0 && clipRect.width <= 0 ) ); }
			else if ( direction == 'topRight' ) { complete = ( show ? ( clipRect.top <= 0 && clipRect.height >= box.offsetHeight && clipRect.width >= box.offsetWidth ) : ( clipRect.top >= box.offsetHeight && clipRect.height <= 0 && clipRect.width <= 0 ) ); }
			else if ( direction == 'bottomLeft' ) { complete = ( show ? ( clipRect.left <= 0 && clipRect.height >= box.offsetHeight && clipRect.width >= box.offsetWidth ) : ( clipRect.left >= box.offsetWidth && clipRect.height <= 0 && clipRect.width <= 0 ) ); }
			else if ( direction == 'topLeft' ) { complete = ( show ? ( clipRect.top <= 0 && clipRect.left <= 0 && clipRect.height >= box.offsetHeight && clipRect.width >= box.offsetWidth ) : ( clipRect.top >= box.offsetHeight && clipRect.left >= box.offsetWidth && clipRect.height <= 0 && clipRect.width <= 0 ) ); }
			else if ( direction == 'center' ) { complete = ( show ? ( clipRect.top <= 0 && clipRect.left <= 0 && clipRect.height >= box.offsetHeight && clipRect.width >= box.offsetWidth ) : ( clipRect.top >= ( box.offsetHeight / 2 ) && clipRect.left >= ( box.offsetWidth / 2 ) && clipRect.height <= 0 && clipRect.width <= 0 ) ); } 
			break;
			
		case 'sliver':
			if ( direction == 'topCenterDown' ) { complete = ( show ? clipRect.left <= 0 : clipRect.height <= 0 ); }
			else if ( direction == 'hcenterOut' ) { complete = ( show ? clipRect.left <= 0 : clipRect.height <= 0 ); }
			else if ( direction == 'vcenterOut' ) { complete = ( show ? clipRect.top <= 0 : clipRect.width <= 0 ); }
			break;
			
		case 'transparent':
			complete = ( show ? state.opacity >= 100 : state.opacity <= 0 );
			break;
	}
	
	return complete;
}

/**
 * Show the given CSS layout box with the given (top,left)
 * coordinate.  If <code>animate</code> is <code>true</code>,
 * animate the box by growing the right clip coordinate from 0 to 
 * its max (slide open right).
 * 
 * @param boxId     the box ID.
 * @param top       the top y coordinate.
 * @param left      the left x coordinate.
 * @param animate   <code>true</code> to animate the box right clip coordinate.
 */
 
function showBox( box, boxType, transition, top, left )
{
	if ( top )
	{ 
		box.style.top = top + 'px';
	}
	
	if ( left )
	{
		box.style.left = left + 'px';
	}
	
	if ( transition )
	{	
		var state;
		
		state = createClipState( box, transition, true );
		box.style.clip = state.clip.toCssString();
		box.style.display = ( boxType || 'block' );
		
		initClipState( box, state );
		box.style.clip = state.clip.toCssString();

		var timeoutFunc = 
			function()
			{
				box.style.clip = calculateNextClip( box, state );				
				if ( !animationComplete( box, state ) )
				{
					setTimeout( timeoutFunc, state.rate );
				}else{
					if(transition.callback){
						transition.callback(box);
					}
				}			
			};
			
		setTimeout( timeoutFunc, state.rate );
	}
	else
	{
		box.style.display = ( boxType || 'block' );
	}
}

/**
 * Hide the given CSS layout box. If <code>animate</code> is <code>true</code>,
 * animate the box by shrinking the right clip coordinate from max to 
 * its 0 (slide close right).
 *
 * @param boxId     the box ID.
 * @param animate   <code>true</code> to animate the box right coordinate.
 */
 
function hideBox( box, transition )
{
	if ( box.style.display != 'none' )
	{
		if ( transition )
		{
			var state;
			
			state = createClipState( box, transition, false );
			initClipState( box, state );
			box.style.clip = state.clip.toCssString();

			var timeoutFunc = 
				function()
				{
					box.style.clip = calculateNextClip( box, state );				
					if ( !animationComplete( box, state ) )
					{
						setTimeout( timeoutFunc, state.rate );
					}else{
						if(transition.callback){
							transition.callback(box);
						}
					}	
				};
				
			setTimeout( timeoutFunc, state.rate );
		}
		else
		{
			box.style.display = 'none';
		}
	}
}

function showLightBox(ulayid,olayid,transition,focusable){
	var underlay=getElement(ulayid);
	var overlay=getElement(olayid);
	if(underlay&&overlay){
		var dsoc=getDsocOrigin();
		var bounds=getViewportBounds();
		underlay.style.top='0px';
		underlay.style.left='0px';
		var width,height;
		var top,left;
		overlay.style.display='block';
		width=overlay.offsetWidth;
		height=overlay.offsetHeight;
		top=((bounds.height-height)/2)+dsoc.y;
		left=((bounds.width-width)/2)+dsoc.x;
		if(top<0) top=0;
		if(left<0) left=0;
		overlay.style.top=top+'px';
		overlay.style.left=left+'px';
		underlay.style.width=(bounds.width>document.body.scrollWidth?bounds.width:document.body.scrollWidth)+'px';
		underlay.style.height=(bounds.height>document.body.scrollHeight?bounds.height:document.body.scrollHeight)+'px';
		underlay.style.display='block';
		underlay.style.visibility='visible';
		overlay.style.display='none';
		overlay.style.visibility='visible';
		showBox(overlay,null,transition,top,left);
		if(focusable){
			var f=getElement(focusable);
			if(f&&f.focus){f.focus();}
		}
	}
	return false;
}

function hideLightBox(ulayid,olayid,transition){
	var underlay=getElement(ulayid);
	var overlay=getElement(olayid);
	if(underlay&&overlay){
		var callback=function(box){
			overlay.style.visibility='hidden';
			underlay.style.visibility='hidden';
			underlay.style.display='none';
		};
		transition.callback=callback;
		hideBox(overlay,transition);
	}
	return false;
}


/* **********************************************************************
 * AJAX Utilities
 * ********************************************************************** */
 
/**
 * Send an asynchronous request to the given URL and notify
 * via the given callback when changes occur.
 *
 * @param url        the url.
 * @param callback   the callback function.
 */
 
function sendAjaxRequest( url, method, callback, async )
{
	var req;
	
    // branch for native XMLHttpRequest object
    if(window.XMLHttpRequest) 
    {
    	try 
    	{
			req = new XMLHttpRequest();
        } 
        catch(e) 
        {
			req = false;
        }
    // branch for IE/Windows ActiveX version
    } 
    else if(window.ActiveXObject) 
    {
       	try 
       	{
        	req = new ActiveXObject("Msxml2.XMLHTTP");
      	} 
      	catch(e) 
      	{
        	try 
        	{
          		req = new ActiveXObject("Microsoft.XMLHTTP");
        	} 
        	catch(e) 
        	{
          		req = false;
        	}
		}
    }
    
	if(req) 
	{
		if ( async )
		{
			req.onreadystatechange = 
				function() 
				{
					if ( callback.handleAjaxResponse ) { callback.handleAjaxResponse( req ); }
					else { callback( req ); }
	            };
	    }
	    
		req.open(method, url, async);
		req.send("");
		
		if ( !async )
		{
			if ( callback.handleAjaxResponse ) { callback.handleAjaxResponse( req ); }
			else { callback( req ); }	
		}		
	}
}
