(
	function UNDERSCORE(){
			
		trim = function (value) {
			if( !isString(value) ) return value; //we can only trim strings
			var temp = value;
			var obj = /^(\s*)([\W\w]*)(\b\s*$)/;
			if (obj.test(temp)) { temp = temp.replace(obj, '$2'); }
			var obj = / +/g;
			temp = temp.replace(obj, " ");
			if (temp == " ") { temp = ""; }
			return temp;
		};
		
		isString = function (a) { return typeof a == 'string'; };
		isEmpty = function (str){ str = trim(str); return (str == null) || (str == 'null') || (str.length == 0) || (str == undefined);};

		indexOf = function ( arr, value ){
			for( var i = 0; i < arr.length; i++ ){
				if ( arr[i] == value ){
					return i;
				} 
			}
			return -1;
		};

		delegate = function (obj, method) {
			return function(){ return method.apply(obj, arguments ); }
		};
		
		isArray = function ( obj ) {  
				return obj && 
								obj.propertyIsEnumerable &&
								!(obj.propertyIsEnumerable('length')) && 
								typeof obj === 'object' && 
								typeof obj.length === 'number';
		}

		_ = {
		//****************************************************************************
		// Debug
		//****************************************************************************	
		initComplete : false,
		
		/**  displays all properties and functions for an object
		 *
		 * @param {object}	obj		string to clean up
		 * @param {bool} 	html	newlines to be displayed as br or \n
		 * @param {int}		indent	level of indentation for recursion
		 *
		 * returns string
		 */
		dumpObj : function ( obj , html, indent ){
			if(indent == null ) indent = 0;
			var thisLevel = ""; var indentText = "";
			for(var x = 0 ; x < indent ; x++) { indentText += "  "; }
			for(var a in obj) {
				var tmpO = obj[a];
				thisLevel += indentText + a + ": " + tmpO;
				thisLevel += (html) ? "<br />" : "\n";
			}
			return thisLevel;
		},
			
		//****************************************************************************
		// Event Chains
		//****************************************************************************			
		eventChains : {},
		
		pushEventChain : function ( chainName, callback ) {
			
			var workingChain = this.eventChains[ chainName ];
			
			if( !workingChain ){
				this.eventChains[ chainName ] = [];
				workingChain = this.eventChains[ chainName ];
			}
			
			workingChain.push( callback );
		},
				
		runEventChain : function ( chainName ) {
			
			var chain = this.eventChains[chainName];
			
			if( chain ){
				for( var eventID in chain )	{
					var event = chain[eventID];
					event();
				}
			}
			
		},
			
		//****************************************************************************
		// Timed Events
		//****************************************************************************
		eventsWaiting : {},
		
		setDelayedEvent:function( eventKey, callback, eventDelay ) {
			this.clearDelayedEvent(eventKey);
			this.eventsWaiting[eventKey] = setTimeout( callback, eventDelay );
		},
		
		clearDelayedEvent:function( eventKey ) {
			if( this.eventsWaiting[eventKey] ){
				clearTimeout( this.eventsWaiting[eventKey] );
				this.eventsWaiting[eventKey] = null;
			}
		},
		
		//****************************************************************************
		// Incomming Events
		//****************************************************************************
			/*
			 * Window has loaded
			 */
			__onload : function (){
				this.undef;
				this.px = 'px';
				
					//client wide storage
				this.highZ 	  	= 100;
				this.documentW 	= 0;
				this.documentH 	= 0;
				this.viewportW 	= 0;
				this.viewportH 	= 0;
				this.scrollLeft = 0;
				this.scrollTop 	= 0;
				this.dragObject = null;		//currently dragging object
			
				this.ns  = !!(document.layers && typeof document.classes != this.undef);
					
				//client wide events
				window.onresize = delegate( this, this.__resize);
				window.onscroll = delegate( this, this.__scroll);	
				document.onmousemove = delegate( this, this.__mouseMove);
				document.onmouseup = delegate( this, this.__mouseUp);
				
				this.__resize();
				this.__scroll();
				
				this.initComplete = true;
				
				this.runEventChain( 'onload' );
			},
			
			/*
			 * Page was resized
			 */
			__resize : function  (){
			 	var pageSizes = this.getPageSize();
				this.documentW = pageSizes[0];
				this.documentH = pageSizes[1];
				this.viewportW = pageSizes[2];
				this.viewportH = pageSizes[3];
				
				if( this._resize ) this._resize();
			},
			
			/*
			 * Page was scolled
			 */
			__scroll : function  (){
				var scrollValues = this.getPageScroll();
				this.scrollLeft = scrollValues[0];
				this.scrollTop = scrollValues[1];
				
				if( this._scroll ) this._scroll();
			},
			
		
			/*
			 * Mouse Events
			 */
			__mouseMove : function __mouseMove(e){
				//build event object
				if(!e) e = window.event;
				e = this.fixEventObject(e);
		
				if( this.dragObject ) {
					this.__drag(e);
					this.stopEvent(e.event);							//stop normal event operation, c'mon! we're draggin
				}
				
				if( this.resizeObject && this.resizeFunc ) {
					this.resizeFunc( e );
					this.stopEvent(e.event);
				}
			},
			
			__mouseUp : function __mouseUp( e ){
				
				//build event object
				if(!e) e = window.event;
				e = this.fixEventObject(e);
				
				//stop drag if we're dragging anything
				if( this.dragObject )	this.stopDrag(e);
				
				//stop resizing anything if we're resizing.
				if( this.resizeObject ) this.endResize( e );
				
			},
			
			//drag innerworkings
			//incomming call to move our currently dragging object
			__drag : function __drag( e ){
				
				var newX = (e.mouseX - this.dragObject.dsMx) + this.dragObject.dsx;
				var newY = (e.mouseY - this.dragObject.dsMy) + this.dragObject.dsy;
				
				//captureToParent
				if( this.dragObject.captureToParent ) {
					newX = Math.min( Math.max( newX , 0 ), this.innerW( this.dragObject.offsetParent ) - this.dragObject.dsw );
					newY = Math.min( Math.max( newY , 0 ), this.h( this.dragObject.offsetParent ) - this.dragObject.dsh );
				}
		
				if( this.dragObject.__drag ){
					this.dragObject.__drag( e );
				}
		
				this.x( this.dragObject, newX );
				this.y( this.dragObject, newY );
			},
		
		//****************************************************************************
		// Functions for retrieveing data
		//****************************************************************************

			/**	
			 * Core code from - quirksmode.com
			 * Edit for Firefox by pHaez
			 * 
			 * getPageSize()
			 * returns array with page width, height and window width, height
			 */	
			getPageSize : function getPageSize(){
				
				var xScroll, yScroll, pageWidth, pageHeight;
		
				var docBody = (document.compatMode && document.compatMode.toLowerCase() != "backcompat")?
					document.documentElement : (document.body || null);
				
				if (window.innerHeight && window.scrollMaxY) {	
					xScroll = window.innerWidth + window.scrollMaxX;
					yScroll = window.innerHeight + window.scrollMaxY;
				} else if (docBody.scrollHeight > docBody.offsetHeight){ // all but Explorer Mac
					xScroll = docBody.scrollWidth;
					yScroll = docBody.scrollHeight;
				} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
					xScroll = document.body.offsetWidth;
					yScroll = document.body.offsetHeight;
				}
				
				var windowWidth, windowHeight;
		
				if (self.innerHeight) {	// all except Explorer
					if(document.documentElement.clientWidth){
						windowWidth = document.documentElement.clientWidth; 
					} else {
						windowWidth = self.innerWidth;
					}
					windowHeight = self.innerHeight;
				} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
					windowWidth = document.documentElement.clientWidth;
					windowHeight = document.documentElement.clientHeight;
				} else if (document.body) { // other Explorers
					windowWidth = document.body.clientWidth;
					windowHeight = document.body.clientHeight;
				}	
				
				// for small pages with total height less then height of the viewport
				if(yScroll < windowHeight){
					pageHeight = windowHeight;
				} else { 
					pageHeight = yScroll;
				}
		
				// for small pages with total width less then width of the viewport
				if(xScroll < windowWidth){	
					pageWidth = xScroll;		
				} else {
					pageWidth = windowWidth;
				}
		
				var arrayPageSize = new Array(pageWidth, pageHeight, windowWidth, windowHeight);
				return arrayPageSize;
			},
			
			/**	
			 * Core code from - quirksmode.com
			 * getPageScroll()
			 * returns array with x,y page scroll values.
			 */	
			getPageScroll : function getPageScroll(){
			
				var xScroll, yScroll;
			
				if (self.pageYOffset) {
					yScroll = self.pageYOffset;
					xScroll = self.pageXOffset;
				} else if (document.documentElement && document.documentElement.scrollTop){	 // Explorer 6 Strict
					yScroll = document.documentElement.scrollTop;
					xScroll = document.documentElement.scrollLeft;
				} else if (document.body) {// all other Explorers
					yScroll = document.body.scrollTop;
					xScroll = document.body.scrollLeft;	
				}
			
				var arrayPageScroll = new Array(xScroll, yScroll);
				return arrayPageScroll;
			},

			
		//****************************************************************************
		// Cookie Functions
		//****************************************************************************
		setCookie : function ( name, value, path, domain, expires ) {
			var cookieString = escape(name) + "=" + escape(value) + "";
			
			if( expires ) 	cookieString += "; expires=" + expires.toGMTString();
			if( path ) 		cookieString += "; path=" + escape(path);
			if( domain ) 	cookieString += "; domain=" + domain;

			document.cookie = cookieString;
		},
		
		getCookie : function ( name ) {
			var nameEQ = name + "=";
			var ca = document.cookie.split(';');
			for(var i=0;i < ca.length;i++) {
				var c = ca[i];
				while (c.charAt(0)==' ') c = c.substring(1,c.length);
				if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
			}
			return null;
		},
		
		eraseCookie : function (name) {
			createCookie(name,"",-1);
		},

		//******************************************************************************************
		// DOM Element Handling
		//******************************************************************************************
			/**
			 * Create an element
			 * @param {Object} tag			HTML Tag
			 * @param {Object} id			Element ID
			 * @param {Object} classname	Element ClassName
			 * @param {Object} parent		Parent element
			 * @param {Object} innerhtml	internal HTML for this element
			 * @param {Object} value		default value of an input tag
			 * @return {Object} Newly created element
			 */
			create : function (tag, id, classname, parent, innerhtml, value){
		
				var newElem = document.createElement(tag);
				
				//if it's a form element we need to set the values now
				if (tag == 'input') {
					newElem.value = value;
				}
				
				//set all other values provided
				if (id) 
					newElem.id = id;
				if (classname) 
					newElem.className = classname;
				if (parent) 
					_.setParent(newElem, parent);
				if (innerhtml) 
					newElem.innerHTML = innerhtml;
				
				return newElem;
			},
			
			/**
			 * Get an HTML DOM Element by ID
			 * @param {Object} elementName
			 */		
			$ : function (elementName){
				if ( document.getElementById != undefined ) {
					return document.getElementById(elementName);
				} else {
					if ( document.all != undefined ) {
						return document.all[elementName]; 
					} else if ( document.layers != undefined ) {
						return document.layers[elementName];
					}
				}
			},
			
			/**
			 * Remove an element from the page
			 * @param {Object} element
			 */
			destroy : function destroy( element ){	
				try{ element.parentNode.removeChild( element );
				} catch ( ex ){ return false;
				} return true;
			},
			
			/**
			 * Move an elemet in the DOM
			 * @param {Object} elementA	Element to move
			 * @param {Object} elementB	Element to move to.
			 */
			setParent : function setParent( elementA, elementB ){
				if(elementA.parentNode){
					var elementRef = elementA.parentNode.removeChild(elementA);
					elementB.appendChild(elementRef);
				} else {
					elementB.appendChild(elementA);
				}
			},
			
			/**
			 * get the form value of an input, textarea, or select
			 * or set the value of an input, and supports selecting items in radio groups
			 * and checking checkboxes
			 * 
			 * to check a checkbox ...  $$(checkboxname, true )
			 * to set the value of the checkbox $$( checkboxname, 'true' )
			 * 
			 * this behavior is repeated on radio buttons
			 * 
			 * @param {Object} element
			 */
			$$ : function ( element, value ) {
				
				//did we pass a name to a form element
				if( isString( element ) ){
					
					var formElements = document.getElementsByName( element );
					var currentValue = null;

					//loop through all elements				
					for( var i = 0; i < formElements.length; i++){
						var testElem = formElements[i];

						//alert( _.dumpObj( testElem ) );

						//what kind of input
						switch( testElem.type.toLowerCase() ){
							
							//checkboxes need to be checked in order to really count. Although we don;t have multiple items,
							case 'checkbox':
								if( value ){
									testElem.value = value;
								} else {
									if ( testElem.checked ) {
										return true;
									} else {
										return false;
									}
								}
								break;
							//radio buttons have multiple items and requre the preceding loop and this conditional...
							case 'radio':
								if( value ){
									if( testElem.value == value ) {
										testElem.checked = true;
									} else {
										testElem.checked = false;
									}
								} else {
									if( testElem.checked ){
										return testElem.value;
									}
								}
								break;
								
							//text field? text area?
							default:
								//get or set
								if (value) {
									testElem.value = value;
								} else {
									currentValue = testElem.value;
								}
						}

					}
					
					//if we were setting a value and we're here.... we've made it
					if( value ){
						return true;
					}
					
					//return whatever we found (if anything)
					return currentValue;
				
				//not a string...? hopefully it's an HTMLElement with a value
				} else {
					
					try{
						
						if (value) {
							element.value = value;
						}
						
						return element.value;
					} catch ( ex ) { }
				}
				
				return null; //we fail
			},
			
		//******************************************************************************************
		//Event Managing
		//******************************************************************************************
			/**
			 * Prepare an event object to be processed by _
			 * @param {Object} e A new event object with all fields filled in
			 */
			fixEventObject : function fixEventObject(e) {
				if(!e) e = window.event;
				
				var newEvent = {};
				
				try{
					newEvent.targetElement = e.srcElement || e.target;
					newEvent.toElement = e.relatedTarget || e.toElement;
					newEvent.fromElement = e.relatedTarget || e.fromElement;
					newEvent.targetID = newEvent.targetElement.id || newEvent.targetElement.name;
					
					//update general mouse movement stats
					//this will only use scrollX and y ifthey are filled
					newEvent.mouseX = (e.pageX || e.clientX + this.scrollLeft);
					newEvent.mouseY = (e.pageY || e.clientY + this.scrollTop);
			
					//mouse placement over an element
					newEvent.offsetX  = (e.offsetX || e.layerX);
					newEvent.offsetY = (e.offsetY || e.layerY);
					
					newEvent.event = e;
				}
				catch ( ex ){ }
				return newEvent; //don;t know if this is necessary
			},
		
			/**
			 * Prevents an event from propagation further
			 * @param {Object} e	event to stop
			 */
			stopEvent : function  stopEvent(e){
				if(!e) e = window.event;
				try{ e.cancelBubble = true;	}catch(ex){ }
				try{ e.stopPropagation();	}catch(ex){ }
				try{ e.preventDefault();	}catch(ex){ }
				try{ e.returnValue = false;	}catch(ex){ }
		    },
			

		//******************************************************************************************
		//Element Sizing
		//
		// All functions MUST include a main(auto selecting) function and a seperate set and get
		// the non-auto=selecting-functions will be usefull for tweening
		// (the less conditionals the better)
		//****************************************************************************************** 
		
			/*
			 * Width Functions
			 */
			w : function w( element, value){
				if( value ){
					return this.w_set( element, value );
				}
				return this.w_get( element );
			},
			
				w_set : function ( element, value ){
					value = parseInt(value);
					value = Math.max( value, ( element.minx || 0) );
					try{ element.style.width = value + this.px; }
					catch (e){}
					return value;
				},
				
				w_get : function ( element ){
					try{
						return (this.ns) ?
							(element) ?	element.clip.width : 0
							: (element) ? (element.offsetWidth || element.style.pixelWidth || element.style.width || 0) : 0;
					} catch (e){ } return false;	
				},
				
			/*
			 * Height functions
			 */
			h : function h(element, value){
				if( value ){
					return this.h_set( element, value );
				}
				return this.h_get( element );
			},
			
				h_set : function( element, value ){
					value = Math.max( parseInt( value ), ( element.miny || 0));
					try{ element.style.height = value + this.px; }
					catch (e){}
					return value;
				},
				
				h_get : function( element ){
					try{
						return (this.ns) ?
							(element) ?	element.clip.height : 0
							: (element) ? (element.offsetHeight || element.style.pixelHeight || element.style.height || 0) : 0;
					} catch (e){ } return false;
				},

				
			/**	gets the client(inner) width of an element ( scrollbars not included )
			 * getvW
			 * @param {element} element		dom element to calculate for
			 * returns number
			 */
			innerW : function innerW( element ) {
				try{
				return (element.clientWidth || 0);
				} catch (e) {} return false;
			},
		
			/**	gets the client(inner) height of an element ( scrollbars not included )
			 * getH
			 * @param {element} element		dom element to calculate for.
			 * returns number
			 */
			innerH : function innerH( element ) {
				try{
				return (element.clientHeight || 0);
				} catch (e) {} return false;
			},
			
			
		//******************************************************************************************
		//Element Positioning
		//******************************************************************************************
			//------x------
			x : function x(element, value){
					return (value != undefined) ? this.x_set( element, value ) : this.x_get(element);
			},
			x_get : function ( element ){			return parseInt(element.offsetLeft) || 0;	},
			x_set : function ( element, value ){	return parseInt(element.style.left = parseInt(value) + this.px) || 0; },
			
			//------y------
			y : function y(element, value){
					return (value != undefined) ? this.y_set( element, value ) : this.y_get(element);
			},
			y_get : function ( element ){			return parseInt(element.offsetTop) || 0;	},
			y_set : function ( element, value ){	return parseInt(element.style.top = parseInt(value) + this.px) || 0; },
		
			//------z------
			z : function z(element, value){
				element.style.zIndex = value;
			},
			
			
			/**	gets the absolute X position of any element to the origin
			 * getAbsX
			 * @param {element} element		dom element to calculate for.
			 * returns number
			 */
			absX : function absX( element ){
				var curleft = 0;
				var tmpobj = element;
				if (tmpobj.offsetParent) {
					while (tmpobj.offsetParent) {
							curleft += tmpobj.offsetLeft;
							if( tmpobj.tagName.toLowerCase() == 'div' && element != tmpobj ){ curleft -= tmpobj.scrollLeft; }
							tmpobj = tmpobj.offsetParent;
						}
				}
				else if (tmpobj.x) { curleft += tmpobj.x; }
				return curleft;
			},
			
			/**	gets the absolute Y position of any element to the origin
			 * getAbsX
			 * @param {element} element		dom element to calculate for.
			 * returns number
			 */
			absY : function absY(element){
				var curtop = 0;
				var tmpobj = element;
				if (tmpobj.offsetParent) {
					while (tmpobj.offsetParent) {
						curtop += tmpobj.offsetTop;
						if( tmpobj.tagName.toLowerCase() == 'div' && element != tmpobj ){ curtop -= tmpobj.scrollTop; }
						tmpobj = tmpobj.offsetParent;
					}
				}
				else if (tmpobj.y) { curtop += tmpobj.y; }
				return curtop;
			},
				
			/**	gets the absolute X position of any element to the origin
			 * getAbsX
			 * @param {element} element		dom element to calculate for.
			 * returns number
			 */
			relativeAbsX : function relativeAbsX( elementA, elementB ){
				var curleft = 0;
				var tmpobj = elementA;
				if (tmpobj.offsetParent) {
					while (tmpobj.offsetParent && tmpobj != elementB) {
							curleft += tmpobj.offsetLeft;
							if( tmpobj.tagName.toLowerCase() == 'div' && elementA != tmpobj ){ curleft -= tmpobj.scrollLeft; }
							tmpobj = tmpobj.offsetParent;
						}
				}
				else if (tmpobj.x) { curleft += tmpobj.x; }
				return curleft;
			},
		
			/**	gets the absolute Y position of any element to the origin
			 * getAbsX
			 * @param {element} element		dom element to calculate for.
			 * returns number
			 */
			relativeAbsY : function relativeAbsY(elementA, elementB){
				var curtop = 0;
				var tmpobj = elementA;
				if (tmpobj.offsetParent) {
					while (tmpobj.offsetParent && tmpobj != elementB) {
						curtop += tmpobj.offsetTop;
						if( tmpobj.tagName.toLowerCase() == 'div' && elementA != tmpobj ){ curtop -= tmpobj.scrollTop; }
						tmpobj = tmpobj.offsetParent;
					}
				}
				else if (tmpobj.y) { curtop += tmpobj.y; }
				return curtop;
			},
			
	
		//******************************************************************************************
		//Graphical Procedures
		//******************************************************************************************
			borderColor : function ( element, hexColor ){
				try{
					element.style.borderColor = "#" + hexColor;
				} catch ( ex ){
					
				}
			},
			
			borderColorRGB : function ( element, r, g, b ){
				try{
					element.style.borderColor = "rgb( " + parseInt(r) + ", " + parseInt(g) + ", " + parseInt(b) + ")";
				} catch ( ex ){
				}
			},			
			bgColor : function (element, hexColor ){
				try{
					element.style.backgroundColor = "#" + hexColor;
				} catch ( ex ){
					
				}
			},
			bgColorRGB : function (element, r, g, b ){
				try{
					element.style.backgroundColor = "rgb( " + parseInt(r) + ", " + parseInt(g) + ", " + parseInt(b) + ")";
				} catch ( ex ){
					
				}
			},

			alpha :	function alpha(element, percent){
				try{
					if(typeof element.style.MozOpacity != undefined){	element.style.MozOpacity = percent / 100; }
					if(typeof element.style.filter != undefined){	element.style.filter = "Alpha(opacity="+parseInt(percent)+")";	}
					element.style.opacity = percent / 100;
				} catch (e) { }
				element.alpha = percent; return false;
			},
			
			showBlock : function showBlock	( element ){	element.style.display = 'block';		},
			hideBlock : function hideBlock	( element ){	element.style.display = 'none';			},
			
			visible : 	function visible	( element ){	element.style.visibility = 'visible';	},
			invisible : function invisible	( element ){	element.style.visibility = 'hidden';	},
			
			show : function show		( element ){	this.showBlock(element);	this.visible(element);	},
			hide : function hide		( element ){	this.hideBlock(element);	this.invisible(element);},
			
			bringToFront : function bringToFront( element ){
				this.highZ++;
				this.z( element, this.highZ );
			}
		};
		
		window.onload = delegate( _, _.__onload);
	}
)();


