	//// BEGIN HEADER ////////////////////////////////////////////////////////////////////////////////
	//////////////////////////////////////////////////////////////////////////////////////////////////
	////                                                                                          ////
	////    MOTIONHANDLER CLASS.                                                                  ////
	////                                                                                          ////
	////    Copyright 2007, Jose Cao-Garcia                                                       ////
	////                                                                                          ////
	////    This software is licensed under the Creative Commons                                  ////
	////    Attribution-ShareAlike 2.5 License:                                                   ////
	////    <http://creativecommons.org/licenses/by-sa/2.5/legalcode>                             ////
	////                                                                                          ////
	////    HELP/INFO/DEVELOPER CONTACT: jose@jcao.com, http://jcao.com                           ////
	////                                                                                          ////
	//////////////////////////////////////////////////////////////////////////////////////////////////
	//////////////////////////////////////////////////////////////////////////////////////////////////
	////                                                                                          ////
	////    THIS IS A GENERAL PURPOSE, CSS BASED ANIMATION CLASS, developed by Jose               ////
	////    Cao-Garcia. This is a work in progress. For documentation and more recent,            ////
	////    updated versions, please visit http://jcao.com.                                       ////
	////                                                                                          ////
	////    This script is designed to be simple and powerful, allowing for efficient             ////
	////    manipulation of multiple css properties across multiple objects to be                 ////
	////    concurrently animated over varying durations of time. It is not meant to be a         ////
	////    full-fledged animation "framework", but rather a simple, compact and powerful         ////
	////    tool for time-based manipulation of CSS properties.                                   ////
	////                                                                                          ////
	////    currently, no documentation exists for this script. I will eventually post            ////
	////    documentation for this script (or a more mature release) at my website, along         ////
	////    with other scripts.                                                                   ////
	////                                                                                          ////
	//////////////////////////////////////////////////////////////////////////////////////////////////
	//// END HEADER //////////////////////////////////////////////////////////////////////////////////




	// V2.2 21/05/06 ~ 8:31 PM - more notes below scripting.
		// Minor upgrade:
		//       -- removed a few eval statements, cleaned up math a bit, tried to make things a bit more efficient here and there




	//// GENERAL-PURPOSE RUNTIME-BASED ANIMATION CLASS
		function motionHandler(fps, onPause) {
			var root          = this;
			root.objs         = new Array();
			root.framesPerSec = (fps >= 4) ? fps : 4;
			root.msecPerStep  = parseInt(1000/root.framesPerSec);
			root.duration     = .5; // default rate (seconds to completion);
			root.timeout      = false;
			root.onPause      = (onPause) ? onPause : false;
		////////////////////////////////////////////////////////////////////////////////////
		//// sets an object property in motion
		////////////////////////////////////////////////////////////////////////////////////
				root.set = function(thisObj, thisProp, target, duration, postProc) {
					// process arguments
					// note: we are not validating for matching units ... unmatching units will yeild erratic behaviour
						var getThisObj    = thisObj;
						var getUnits      = (typeof(target) == 'object') ? root.units(target[1])[0] : root.units(target)[0];
						var getActual     = root.getProp(thisObj, thisProp);
						var getActual     = (getActual != '') ? root.units(getActual)[1] : false;
						var getOrigin     = (typeof(target) != 'number') ? root.units(target[0])[1] : getActual;
						var getTarget     = (typeof(target) == 'object') ? root.units(target[1])[1] : root.units(target)[1];
						var getduration   = (duration) ? duration : root.duration;
						var getpostProc   = (postProc && (typeof(postProc) == 'string' || typeof(postProc) == 'function')) ? postProc : false;
					// validate arguments
						if (!thisObj || (!thisProp || typeof(thisProp) != 'string') || (!target || typeof(target) == 'number') || (duration && parseFloat(duration) == 'NaN') || (postProc && typeof(postProc) == 'undefined')) {
						// in the input is invalid, throw a useful error.
							var errorStr = 'motionhandler.set(thisObj=' + thisObj + ', thisProp=' + thisProp + ', target=' + target;
							if (duration) {  errorStr += ', duration=' + duration; }
							if (postProc) {  errorStr += ', postProc=' + postProc; }
							throw(errorStr + ')')
					// if the input is valid, set element in motion
						} else if (getTarget != getActual) {
						// find object, if allready in array
							var objIndex = root.objs.length; for (var loop in root.objs) { if (root.objs[loop]['objId'] == thisObj) { var objIndex = loop; } }
						// create index for object if none
							if (objIndex == root.objs.length) {
								root.objs[objIndex]          = new Object();
								root.objs[objIndex]['objId'] = getThisObj;
								root.objs[objIndex]['props'] = new Object();
							}
						// insert all necessary parameters for this property
							root.objs[objIndex]['props'][thisProp]          = new Object();
							root.objs[objIndex]['props'][thisProp].target   = parseFloat(getTarget.toPrecision(9));
							root.objs[objIndex]['props'][thisProp].units    = getUnits;
							root.objs[objIndex]['props'][thisProp].origin   = parseFloat(getOrigin.toPrecision(9));
							root.objs[objIndex]['props'][thisProp].duration = parseFloat(getduration.toPrecision(5));
							root.objs[objIndex]['props'][thisProp].postProc = getpostProc;
							root.objs[objIndex]['props'][thisProp].current  = (getActual !== false) ? parseFloat(getActual.toPrecision(9)) : parseFloat(getOrigin);
						// set loop in motion
							if (!root.timeout) { 
								root.timeout = setTimeout(root.step, root.msecPerStep);
							}
						}
				}
		////////////////////////////////////////////////////////////////////////////////////
		//// stop motion for a property, an object, or all objects
		////////////////////////////////////////////////////////////////////////////////////
				root.stop = function(thisObj, thisProp) {
				// if no object, kill all
					if (arguments.length == 0) { for (var loop in root.objs) { root.objs = new Array(); } }
				// if no property, kill object
					if (thisObj && !thisProp) {
						for (var loop in root.objs) {
							if (root.objs[loop]['objId'] == thisObj) {
								root.killNode(loop);
							}
						}
					}
				// kill object property
					if (thisObj && thisProp) {
						for (var loop in root.objs) {
						// get the right object index and kill property;
							if(root.objs[loop]['objId'] == thisObj) {
								delete root.objs[loop]['props'][thisProp];
							}
						}
					}
				}
		////////////////////////////////////////////////////////////////////////////////////
		//// kill an object alltogether. DO NOT CALL EXTERNALLY. 
		////////////////////////////////////////////////////////////////////////////////////
				root.killNode = function(index) {
					if (index > 0) { root.objs.splice(index, index); }
					else if (index < 1) { root.objs.shift(); }
				}
		////////////////////////////////////////////////////////////////////////////////////
		//// run down actObjs array and adjust each item individually as needed
		////////////////////////////////////////////////////////////////////////////////////
				root.step = function() {
					if (root.objs.length > 0) {
						for (var objLoop in root.objs) {
							if (!root.objs[objLoop]['objId'] || typeof(root.objs[objLoop]['objId'].nodeName) == 'undefined') {  // hope this is faster than !eval(root.objs[objLoop]['objId'])
							// if object ceases to exist in dom, kill reference
								root.killNode(objLoop);
							} else {
								var thisObj = root.objs[objLoop]['objId']
								for (var thisProp in root.objs[objLoop]['props']) {
									var hasProps = true;
								// get properties for the object/property
									var target     = root.objs[objLoop]['props'][thisProp].target;
									var units      = root.objs[objLoop]['props'][thisProp].units;
									var origin     = root.objs[objLoop]['props'][thisProp].origin;
									var duration   = root.objs[objLoop]['props'][thisProp].duration;
									var postProc   = root.objs[objLoop]['props'][thisProp].postProc;
									var current    = root.objs[objLoop]['props'][thisProp].current;
									if (thisObj) { root.setStyle(thisObj, thisProp, target, units, origin, duration, postProc, current, objLoop); }
								// stop if the actual obj style matches target or if the target has been reached.
									if (parseFloat(root.getProp(thisObj, thisProp)) == target) { root.stop(thisObj, thisProp); }
									else if (current == target) { root.stop(thisObj, thisProp); }
								}
							}
						// kill object if no properties
							if(!hasProps) { root.stop(thisObj) }
						}
					// handle loop timer
						if (root.objs.length == 0) {
							clearTimeout(root.timeout);
							root.timeout = false;
						// if an onpause function is specified
							if (root.onPause) { root.onPause(); }
						} else {
							root.timeout = setTimeout(root.step, root.msecPerStep);
						}
					}
				}
		////////////////////////////////////////////////////////////////////////////////////
		//// sets incremental styling for an element
		////////////////////////////////////////////////////////////////////////////////////
				root.setStyle = function(thisObj, thisProp, target, units, origin, duration, postProc, current, objIndex) {
				// do some math, determine what the new value should be
					var difference = Math.abs(Math.min(target,origin) - Math.max(target,origin)); 
					var stepLength = (difference / Math.round(duration*root.framesPerSec));
					var newValue   = (Math.max(target,origin) == target) ? parseFloat((current + stepLength).toPrecision(9)) : parseFloat((current - stepLength).toPrecision(9));
					var tripTotal  = Math.abs(Math.min(newValue,origin) - Math.max(newValue,origin));
					var tripLeft   = (difference - tripTotal);
				// seems un-necessary this line may not be necessary any longer .... check and see
					if (parseFloat(newValue) == parseFloat(target)) { newValue = parseFloat(target); }
				// set to target, and stop property if we have reached the end of this animation
					if (tripTotal >= difference || tripLeft < stepLength) {
						newValue = target;
						root.objs[objIndex]['props'][thisProp].current = target;
					} else {
						root.objs[objIndex]['props'][thisProp].current = newValue;
					} 
				// any post-processing happens here (to add different motion styles or integrate
				// with other functions/classes). Recommend using function call for the latter
				// and reserving the switch for motion styling.
					if (postProc && typeof(postProc) == 'string') {
						switch (postProc) {
							case 'linear': newValue = newValue; break;
							case 'leftByWidth': thisObj.style.marginLeft = (0 - newValue) + 'em'; newValue = newValue; break;
						}
					}
				// apply styling to the object!!
				if (thisProp == 'opacity' && client.engine == 'msie') {
					thisObj.style.filter = 'alpha(opacity=' + Math.round(root.units(units, newValue) * 100) + ')';
				} else {
					eval('thisObj.style.' + thisProp + ' = \'' + root.units(units, newValue) + '\'');
				}
				// if postProc is a function we call it AFTER we set styling.
					if (postProc && typeof(postProc) == 'function' && newValue == target) { postProc(thisObj, thisProp, target, units, origin, duration, current, objIndex); }
				}
		////////////////////////////////////////////////////////////////////////////////////
		//// returns units. if no value, returns type. if value returns formatted value
		////////////////////////////////////////////////////////////////////////////////////
				root.units = function(strUnit, value) {
					strUnit = strUnit.toString();
					var thisUnit = strUnit;
					// test for various (screen-based) units of measure
					// omitting print-based units, for obvious reasons
						if (/em$/.test(strUnit))               { thisUnit = 'em';  }
						else if (/px$/.test(strUnit))          { thisUnit = 'px';  }
						else if (/^#/.test(strUnit))           { thisUnit = 'hex'; }
						else if (/^rgb/.test(strUnit))         { thisUnit = 'rgb'; }
					// plain numeric values
						else if (strUnit.indexOf('.') != -1)   { thisUnit = 'dec'; }
						else if (strUnit.indexOf('.') == -1)   { thisUnit = 'int'; }
					// return unit type if no value
					if (value == null) { 
						switch(thisUnit) {
							case 'em':  var thisValue  = parseFloat(strUnit); break;
							case 'px':  var thisValue  = parseInt(strUnit); break;
							case 'hex': var thisValue  = strUnit; break;
							case 'rgb': var thisValue  = strUnit.split('(')[1].split(')')[0]; break;
						// plain numeric values
							case 'int': var thisValue  = parseInt(strUnit);
							case 'dec': var thisValue  = parseFloat(strUnit);
						}
						return [thisUnit, thisValue];
					} else {
						switch(thisUnit) {
							case 'em':  thisUnit  = value + 'em'; break;
							case 'px':  thisUnit  = Math.round(value) + 'px'; break;
							case 'hex': thisUnit  = value; break;
							case 'rgb': thisUnit  = 'rgb(' + value + ')'; break;
						// plain numeric values
							case 'int': thisUnit  = parseInt(value);
							case 'dec': thisUnit  = parseFloat(value);
						}
						return thisUnit;
					}
				}
		////////////////////////////////////////////////////////////////////////////////////
		//// Only necessary to support MSIE's incredibly irritating opacity prop.
		////////////////////////////////////////////////////////////////////////////////////
				root.getProp = function(thisObj, thisProp) {
					if (thisProp == 'opacity' && client.engine == 'msie') {
						var realProp = (thisObj.style.filter != '') ? thisObj.style.filter.split('=')[1].split(')')[0] : '';
					} else {
						var realProp = eval('thisObj.style.' + thisProp);
					}
					return realProp;
				}
		}





	//// INITIALIZE OBJECT FOR REST OF THE UI
		var motion    = new motionHandler(12);