//----- BEGIN Prototype Library Extensions -----
if ( typeof Element != "undefined" ) {
	//Array extensions
	Object.extend(Array.prototype, {
		/**
		 * If val is, returns position 0 to length, otherwise returns -1
		 */
		search: function(val)
		{
			for ( var i=0; i<this.length; i++ ) {
				if ( this[i] == val ) {
					return i;
					
				}
				
			}//for i
			
			return -1;
			
		},//end search
		
		/**
		 * Returns true if the array contains value, false otherwise -- 
		 * this could be a very expensive function -- use it with care
		 */
		contains : function(val)
		{
			return ( this.search(val) != -1 );
			
		},//end contains
		
		/**
		 * Removes null, undefined, and duplicate values and returns a new array
		 */
		minimize : function()
		{
			var newarray = new Array();
			
			for ( var i=0; i<this.length; i++ ) {
				if ( this[i] != null && !newarray.contains(this[i]) ) {
					newarray.push(this[i]);
					
				}
				
			}//for i
			
			return newarray;
			
		}//end minimize
		
	});	
	
	Object.extend(Event, {
		/**
		 * Override the default observe
		 */
		observe : function(element, name, observer, useCapture) 
		{
			element = $(element);
	    	useCapture = useCapture || false;
	
	    	if ( name == 'keypress' && (Prototype.Browser.WebKit || element.attachEvent) ) {
	    		name = 'keydown';	
	    		
	    	} else if ( name == 'mouseenter' &&  typeof element.addEventListener != 'undefined' ) {
	    		name = 'mouseover';
	    		observer = this._mouseEnter(observer);
	    		
	    	} else if ( name == 'mouseleave' && typeof element.addEventListener != 'undefined' ) {
	    		name = 'mouseout';
	    		observer = this._mouseEnter(observer);
	    		
	    	}
	    	
	    	Event._observeAndCache(element, name, observer, useCapture);
		},
		
		/**
		 * This is only for NON-IE browsers!!  It checks to see if the mouse is entering a child or not
		 */
		_mouseEnter : function(func)
		{			
	   		return function(evt)
	   		{
	   			if ( this == evt.relatedTarget || Element.isDescendant(this, evt.relatedTarget) ) {
	   				if ( this == evt.relatedTarget ) {
	   					//console.log('this and relatedTarget are the same');
	   				} else if ( Element.isDescendant(this, evt.relatedTarget) ) {
	   					//console.log('relatedTarget is my descendent');
	   					
	   				}
	   			
	   				return;
	   				
	   			}
	   			
	   			func.call(this, evt);
	   			
	   		}
	   		
		}//end _mouseEnter
	
	});
	

	
	//String extensions
	Object.extend(String.prototype, {		
			/**
			 * Trim the right side of the string
			 */
			rtrim: function() 
			{
				return this.replace(/\s+$/g, "");	
				
			},
			
			/**
			 * Trim the left side of the string
			 */
			ltrim: function() 
			{
				return this.replace(/^\s+/g, "");
				
			},
			
			/**
			 * Trim both sides of the stirng
			 */
			trim: function() 
			{
				return this.rtrim().ltrim();
				
			},
			
			/**
			 * Returns true if the string is a valid email address, false otherwise
			 */
			isEmail: function() 
			{
				return this.match(/^[\d\w\/+!=#|$?%{^&}*`'~-][\d\w\/\.+!=#|$?%{^&}*`'~-]*@[A-Z0-9][A-Z0-9.-]{0,61}[A-Z0-9]\.[A-Z]{2,6}$/i) != null;	
				
			},
			
			/**
			 * Works just like PHP's ucfirst - upper cases the first character in the string - leaves the rest of the string alone
			 */
			upperCaseFirst: function()
			{
				if ( this.length <= 1 ) {
					return this.toUpperCase();
					
				}
				
				var first = this.substring(0, 1);
				var rest = this.substring(1);
				
				return first.toUpperCase() + rest;
			
			},
			
			/**
			 * Uppercases all words, words are delimited by a white space (space, form-feed, newline, carriage return, tab), again, only touches the first character 
			 * of each word
			 */
			upperCaseWords: function()
			{
				var white_space = [ " ", "\n", "\r", "\t", "\f" ];
				var work_string = this;
				
				var output = "";
				var toupper = false;
				
				for ( var i=0; i<work_string.length; i++ ) {
					var ch = work_string[i];
					
					if ( toupper || i == 0 ) {
						output += ch.toUpperCase();
						toupper = false;
						
					} else {
						output += ch;
						
					}
					
					if ( white_space.search(ch) != -1 ) {
						toupper = true;
						
					}
					
				}//for i
				
				return output;
				
				
			},
			
			upperCaseTitle: function()
			{
				var special = [ "'", '"', '-', '(', '[' ];
				var work_string = this.upperCaseWords();
				
				var output = "";
				var toupper = false;
				
				for ( var i=0; i<work_string.length; i++ ) {
					var ch = work_string[i];
					
					if ( toupper || i == 0 ) {
						output += ch.toUpperCase();
						toupper = false;
						
					} else {
						output += ch;
						
					}
					
					if ( special.search(ch) != -1 ) {
						toupper = true;
						
					}
					
				}//for i
				
				return output;
				
			}
			
		}
		
	);
	
	/**
	 * Returns true if child is a descendant of element
	 */
	Element.isDescendant = function(element, child)
	{
		if ( element == child ) {
			return false;
				
		}
			
		while ( child && child != element ) {
			try {
				child = child.parentNode;
				
			} catch ( e ) {
				child = element;
				
			}
				
		}//while
			
		return child == element;
			
	}//end isDescendant
	
	
	/**
	 * Returns the next sibling of the same tag or null
	 */
	Element.getNextSibling = function(element)
	{
		var parent = element.parentNode;
		var arr_children = Element.getChildElementsByTagName(parent, element.tagName);
		if ( arr_children == null || arr_children.length == 1 ) {
			//None found...should never happen, or length of one (the one we are looking for siblings for) return null
			return null;
			
		}
		
		//Otherwise, loop through until we find element
		for ( var i=0; i<arr_children.length; i++ ) {
			if ( arr_children[i] == element ) {
				if ( (i+1) <= arr_children.length-1 ) {
					return arr_children[(i+1)]
					
				}
				
			}
			
		}//for
		
		return null;
		
	}//end getNextSibling
	
	/**
	 * Returns the previous sibling of the same tag or null
	 */
	Element.getPreviousSibling = function(element)
	{
		var parent = element.parentNode;
		var arr_children = Element.getChildElementsByTagName(parent, element.tagName);
		if ( arr_children == null || arr_children.length == 1 ) {
			//None found...should never happen, or length of one (the one we are looking for siblings for) return null
			return null;
			
		}
		
		//Otherwise, loop through until we find element
		for ( var i=0; i<arr_children.length; i++ ) {
			if ( arr_children[i] == element ) {
				if ( (i-1) >= 0 ) {
					return arr_children[(i-1)];
					
				}
				
			}
			
		}//for
		
		return null;
		
	}//end getPreviousSibling
	
	/**
	 * Traverses the DOM tree upwards, searching for the first element
	 * with the given tagName, starting with the element passed in.
	 *
	 * Note: This is MUCH like Event.findElement - except it's not just 
	 * for events baby!
	 */
	Element.getAncestorByTagName = function(element, tagName)
	{
		var curr_elem = element;
		
		do {
			curr_elem = curr_elem.parentNode;
			
		} while ( curr_elem != null && curr_elem.tagName != tagName );
		
		return curr_elem;
		
	}//end getAncestorByTagName
	
	/**
	 * Traverses the childNodes array of element and returns an array of only those 
	 * children with the matching tagName
	 */
	Element.getChildElementsByTagName = function(element, tagName)
	{	
		if ( !element.childNodes ) {
			return null;
			
		}
		
		if ( element.childNodes.length == 0 ) {
			return null;
			
		}
		
		var arr_return = new Array();
		for ( var i=0; i<element.childNodes.length; i++ ) {
			if ( element.childNodes[i].nodeType == 1 && element.childNodes[i].tagName.toLowerCase() == tagName.toLowerCase() ) {
				arr_return.push(element.childNodes[i]);
				
			}
			
		}//for
		
		if ( arr_return.length == 0 ) {
			return null;
			
		} else {
			return arr_return;
			
		}
		
	}//end getChildElementsByTagName
	
	Element.getFirstChildElementByTagName = function(element, tagName)
	{
		if ( !element.childNodes ) {
			throw new Error("Element has no child nodes");
			
		}
		
		if ( element.childNodes.length == 0 ) {
			throw new Error("Element has no children");
			
		}
		
		for ( var i=0; i<element.childNodes.length; i++ ) {
			if ( element.childNodes[i].nodeType == 1 && element.childNodes[i].tagName.toLowerCase() == tagName.toLowerCase() ) {
				return element.childNodes[i];
				
			}
			
		}//for
		
		throw new Error("Element of tagName " + tagName + " not found");
		
	}//end getChildElementsByTagName
	
	Element.getWidth = function(element)
	{
		var dims = Element.getDimensions(element);
		return dims.width;
		
	}//end getWidth
	
	Element.getHeight = function(element)
	{
		var dims = Element.getDimensions(element);
		return dims.height;
		
	}//end getHeight

} else {
	throw('Include prototype before the root.js file.')
	
}
