/**
 * #############################################################################
 * #    (C) 1997-2009 Jesse Quattlebaum, All Rights Reserved                   #
 * #############################################################################
 *
 * ### INFO #################
 * psytoplasm.js (born from baseUtils.js)
 * To provide extended, enhanced and easy to use javascript functionality
 *
 * @author Jesse Quattlebaum <__psyjoniz__@__gmail__.__com__>
 * @version 0.1
 * 
 * ### TODO ##################
 * - comment methods
 *   2007.6.27 - JQ - workin on it
 * - implement correct box model stuff: http://www.456bereastreet.com/archive/200612/internet_explorer_and_the_css_box_model/
 * - make setting/getting width/height conform to *correct* box model spec
 * - on change border adjust width/height to conform to correct box model spec
 * - 2007.6.27 - JQ - implement function versions of trim/rtrim/ltrim
 *   2007.6.28 - JQ - DONE
 * - 2007.6.28 - JQ - fix Ajax for data transport via POST
 *
 * ### LICENSE ###############
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notices, this 
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products 
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


function pageElem(){
	this.extElem = new Elem();
	this.elems = Array();
	
	return this;
}

pageElem.prototype.extend = function(element){
	Elem = this.extElem;
	
	/* for(thing in Elem){
		if(!element[thing]){
			element[thing] = Elem[thing];
		}
	} */
	
	
		element.show              = Elem.show;
		element.hide              = Elem.hide;
		element.isVisible         = Elem.isVisible;
		element.getClasses        = Elem.getClass;
		element.hasClass          = Elem.hasClass;
		element.addClass          = Elem.addClass;
		element.addClasses        = Elem.addClasses;
		element.removeClass       = Elem.removeClass;
		element.removeClasses     = Elem.removeClasses;
		element.getStyle          = Elem.getStyle;
		element.setStyle          = Elem.setStyle;
		element.setStyles         = Elem.setStyles;
		element.getAllStyles      = Elem.getAllStyles;
		element.addMethod         = Elem.addMethod;
		element.addMethods        = Elem.addMethods;
		element.getHeight         = Elem.getHeight;
		element.getWidth          = Elem.getWidth;
		element.getLeft           = Elem.getLeft;
		element.getTop            = Elem.getTop;
		element.move              = Elem.move;
		element.getParent         = Elem.getParent;
		element.getDimensions     = Elem.getDimensions;
		element.update            = Elem.update;
		element.serialize         = Elem.serialize;
		element.unserialize       = Elem.unserialize;
		element.getAttribute      = Elem.getAttribute;
		element.opacity           = Elem.opacity;
		element.init              = Elem.init;
		element.getValue          = Elem.getValue;
		element.setValue          = Elem.setValue;
		element.centerElement     = Elem.centerElement;
		element.addEvent          = Elem.addEvent;
		element.removeEvent       = Elem.removeEvent;
                element.enable            = Elem.enable;
		element.disable           = Elem.disable;
		element.pulse             = Elem.pulse;
	
	/* var the_el = element.init(); */
	var the_el = element;
	/* var d = new Date();
	var el_id = d.getTime();
	
	element.id = el_id;
	pageElem.elems[el_id] = element; */
	
	return the_el;
};

pageElem.prototype.create = function(elem){
	var the_el = document.createElement(elem);
	return this.extend(the_el);
}

/**
 * New element methods ;)
 */

function Elem(){
	return this.init();
}

Elem.prototype.enable = function() {
	this.disabled = false;
}

Elem.prototype.disable = function() {
	this.disabled = true;
}

Elem.prototype.init = function(){
	this.classNames = Array();
	this.styles = Array();
	this.origStyle = Array();
	this.child_elems = Array();
	
	/* extract class names to local array */
	if(this.className != undefined){
		var classes = this.className.split(' ');
		for(i = 0; i < classes.length; i++){
			this.classNames.push(classes[i]);
		}
	}
	
	/* extract style information */
	var css_styles = this.getAllStyles();
	for(i = 0; i < css_styles.length; i++){
		this.setStyle(css_styles[i].name,css_styles[i].value);
	}
	
	return this;
}

Elem.prototype.show = function(effect){
	if(!this.isVisible()){
		if(this.hide_method != undefined){
			effect = this.hide_method;
			this.hide_method = false;
		}
		if(effect != undefined && typeof(effect) == 'object'){
			switch(effect.type){
				case 'fade':
					Effects.fade(this,this.orig_opacity,effect.time);
					break;
				case 'slide':
					var dims = {
						'left':this.orig_left,
						'top':this.orig_top,
						'height':this.orig_height,
						'width':this.orig_width
					};
					
					if(this.orig_border != undefined){
						this.setStyle('border',this.orig_border);
					}
					
					Effects.slide(this,dims,effect);
					
					/* this.setStyles(this.origStyle); */
					setTimeout('getElem("' + this.id + '").setStyles(getElem("' + this.id + '").origStyle)',effect.time);
					
					/* this.setStyle('overflow','visible'); */
					
					/* do{
						chi = this.child_elems.pop();
						chi.setStyles(chi.origStyle);
						chi.setStyle('padding','10px');
						chi.getParent().setStyle('background','red');
					}while(this.child_elems.length > 0); */
					
					break;
			}
		}else{
			this.style.display = this.visible_style;
		}
	}
	return this;
};

Elem.prototype.getParent = function(){
	if(this.parentNode != undefined){
		return pageElem.extend(this.parentNode);
	}
	return this;
}

Elem.prototype.hide = function(effect){
	if(this.isVisible()){
		if(effect != undefined && typeof(effect) == 'object'){
			this.hide_method = effect;
			switch(effect.type){
				case 'fade':
					this.setStyle('width',this.getWidth() + 'px');
					this.orig_opacity = this.opacity();
					Effects.fade(this,0,effect.time);
					break;
					
				case 'slide':
					this.origStyle = this.getAllStyles();
					
					this.orig_height = this.getHeight();
					this.orig_width = this.getWidth();
					this.orig_left = this.getLeft();
					this.orig_top = this.getTop();
					this.orig_border = this.getStyle('border');
					
					this.setStyle('padding','0px');
					this.setStyle('overflow','hidden');
					this.setStyle('margin','0px');
					this.setStyle('border','0px none');
					
					var inners = this.getElementsByTagName('div');
					
					/* for(i = 0; i < inners.length; i++){
						var el = pageElem.extend(inners.item(i));
						el.setStyle('lineHeight','0px');
						el.origStyle = el.getAllStyles();
						this.child_elems.push(el);
					} */
					
					var dims = {
						'left':this.orig_left,
						'top':this.orig_top,
						'height':0,
						'width':this.orig_width
					};
					
					if(Browser.getCompat() == 'IE'){
						dims.height = '1';
					}
					
					Effects.slide(this,dims,effect);
					break;
			}
		}else{
			this.visible_style = this.style.display;
			this.style.display = 'none';
		}
	}
	return this;
};

Elem.prototype.isVisible = function(){
	if(this.style.display == 'none' || this.style.display == 'hidden' || this.opacity() == 0 || this.getHeight() == 0){
		return false;
	}
	return true;
};

Elem.prototype.getClasses = function(){
	if(this.classNames.length < 1){
		this.init();
	}
	return this.classNames;
};

Elem.prototype.hasClass = function(class_name){
	
	if(this.className.indexOf(class_name) > -1){
		return true;
	}
	
	if(typeof(this.classNames) != 'undefined'){
		for(i = 0; i < this.classNames.length; i++){
			if(this.classNames[i] == class_name){
				return true;
			}
		}
	}
	
	return false;
};

Elem.prototype.addClass = function(class_name){
	if(class_name == undefined){
		return;
	}
	if(!this.hasClass(class_name)){
		this.className = this.className+' '+class_name;
		this.classNames = this.className.split(' ');
	}
};

Elem.prototype.addClasses = function(class_names){
	if(typeof(class_names) == 'array'){
		for(i = 0; i < class_names.length; i++){
			this.addClass(class_names[i]);
		}
	}
};

Elem.prototype.removeClass = function(class_name){
	if(this.hasClass(class_name)){
		var rpl = '/' + class_name + '/';
		this.className = this.className.replace(eval(rpl),'');
		this.classNames = this.className.split(' ');
	}
};

Elem.prototype.removeClasses = function(class_names){
	if(typeof(class_names) == 'array'){
		for(x = 0; x < class_names.length; x++){
			this.removeClass(class_names[i]);
		}
	}
};

Elem.prototype.getStyle = function(name){
	var real_val = false;
	var local_val = false;
	var style_name = name.jsToStyle();
	
	switch(name){
		case 'opacity':
			if(Browser.getCompat() == 'IE'){
				if(typeof(this.styles) != 'undefined'){
					real_val = this.styles['opacity'];
				}
			}else{
				real_val = this.style.getPropertyValue(style_name);
			}
			
			if(typeof(this.styles) == 'undefined'){
				this.styles = [];
			}
			
			this.styles['opacity'] = real_val;
			
			break;
		
		default:
			//real_val = eval("this.style." + name);
			real_val = this.style.getPropertyValue(style_name);
			
			if(this.styles[name] != undefined){
				local_val = this.styles[name];
			}
			
			if(local_val != real_val){
				this.styles[name] = real_val;
			}
	}
	
	return real_val;
};

Elem.prototype.getAllStyles = function(){
	var ret_st = Array();
	for(style in this.style){
		if(isNaN(style) && style != 'length'){
			
			var value = eval("this.style." + style);
			
			if(Browser.getCompat() == 'IE'){
				value = new String(value);
			}
			
			if(typeof(value) == 'function' || value == undefined){
				continue;
			}
			
			if(value.length < 1 || value.trim() == ''){
				continue;
			}
			
			var new_st = {'name':style,'value':value};
			ret_st.push(new_st);
		}
	}
	return ret_st;
}

Elem.prototype.setStyle = function(name,value){
	/* alert('setting ' + name + ' to ' + value); */
	switch(name){
		case 'opacity':
			this.styles['opacity'] = value;
			if(Browser.getCompat() == 'MOZILLA'){
				if(value > 1){
					this.style.setProperty('opacity',(value / 100),null);
				}else{
					this.style.setProperty('opacity',value,null);
				}
			}else{
				this.style.filter = 'alpha(opacity=' + value + ')';
			}
			break;
		
		default:
			/* name = name.toLowerCase(); */
			var js_name = name;
			var style_name = name.jsToStyle();
			
			/* alert(name + ' - ' + js_name + ' - ' + style_name); */
			if(typeof(this.styles) == 'undefined'){
				this.styles = [];
			}
			this.styles[js_name] = value;
			this.styles[style_name] = this.styles[js_name];
			if(Browser.getCompat() == 'MOZILLA'){
				this.style.setProperty(js_name,value,null);
			}else{
				eval("this.style." + js_name + "='" + value + "'");
			}
			
			break;
	}
}

Elem.prototype.setStyles = function(styles){
	if(typeof(styles) == 'object'){
		for(s = 0; s < styles.length; s++){
			/* alert('setting ' + styles[s].name + ' to ' + styles[s].value); */
			this.setStyle(styles[s].name,styles[s].value);
		}
	}
}

Elem.prototype.addMethod = function(name,method){
	if(eval("this." + name) == undefined){
		eval("this." + name + ' = ' + method);
		return true;
	}
	return false;
};

Elem.prototype.addMethods = function(methods){
	if(typeof(methods) == 'object'){
		for(x = 0; x < methods.length; x++){
			var name = methods[x];
			var method = eval("methods." + name);
			this.addMethod(name,method);
		}
	}
};

Elem.prototype.getHeight = function(){
	var hei = false;
	if(this.offsetHeight != undefined){
		hei = this.offsetHeight;
	}else if(this.getStyle('height')){
		hei = this.getStyle('height').replace(/px/,'');
	}
	
	if(Browser.getCompat() == 'MOZILLA' && hei){
		var num = 0;
		if(this.getStyle('borderTopWidth')){
			num = this.getStyle('borderTopWidth').replace(/px/,'');
			hei = hei - num;
		}
		
		if(this.getStyle('borderBottomWidth')){
			num = this.getStyle('borderBottomWidth').replace(/px/,'');
			hei = hei - num;
		}
		
		if(this.getStyle('paddingTop')){
			num = this.getStyle('paddingTop').replace(/px/,'');
			hei = hei - num;
		}
		
		if(this.getStyle('paddingBottom')){
			num = this.getStyle('paddingBottom').replace(/px/,'');
			hei = hei - num;
		}
	}
	
	return hei;
};

Elem.prototype.getWidth = function(){

	var wid = false;
	
	if(this.offsetWidth != undefined){
		wid = this.offsetWidth;
	}else if(this.getStyle('width')){
		wid = this.getStyle('width').replace(/px/,'');
	}
	
	if(Browser.getCompat() == 'MOZILLA' && wid){
		var num = 0;
		if(this.getStyle('borderLeftWidth')){
			num = this.getStyle('borderLeftWidth').replace(/px/,'');
			wid = wid - num;
		}
		
		if(this.getStyle('borderRightWidth')){
			num = this.getStyle('borderRightWidth').replace(/px/,'');
			wid = wid - num;
		}
		
		if(this.getStyle('paddingLeft')){
			num = this.getStyle('paddingLeft').replace(/px/,'');
			wid = wid - num;
		}
		
		if(this.getStyle('paddingRight')){
			num = this.getStyle('paddingRight').replace(/px/,'');
			wid = wid - num;
		}
	}
	return wid;
};

Elem.prototype.getLeft = function(){
	var l = 0;
	var obj = this;
	
	if(obj.offsetParent){
		/* l = obj.offsetLeft; */
		while(obj.offsetParent){
			l+= obj.offsetLeft;
			obj = obj.offsetParent;
		}
	}
	
	if(this.getStyle('marginLeft')){
		l = l - this.getStyle('marginLeft').replace(/px/,'');
	}
	
	return l;
}

Elem.prototype.getTop = function(){
	var t = 0;
	var obj = this;
	
	if(obj.offsetParent){
		/* t = obj.offsetTop; */
		while(obj.offsetParent){
			t+= obj.offsetTop;
			obj = obj.offsetParent;
		}
	}
	
	if(this.getStyle('marginTop')){
		t = t - this.getStyle('marginTop').replace(/px/,'');
	}
	
	return t;
}

Elem.prototype.move = function(pos){
	if(typeof(pos) == 'object'){
		this.setStyle('position','absolute');
		this.setStyle('left', pos.x);
		this.setStyle('top',  pos.y);
	}
}

Elem.prototype.getDimensions = function(){
	var w = this.getWidth();
	var h = this.getHeight();
	var l = this.getLeft();
	var t = this.getTop();
	
	return {'width':w, 'height':h, 'left':l, 'top':t};
};

Elem.prototype.update = function(new_content){
	var nc = new String(new_content);
	this.innerHTML = nc.parseScripts();
};

Elem.prototype.serialize = function(){
	/* not sure what we're gonna do here */
};

Elem.prototype.unserialize = function(){
	/* not sure what we're gonna do here */
};

Elem.prototype.getAttribute = function(attrib_name){
	/* ?? */
};

Elem.prototype.opacity = function(){
	var opac = false;
	
	if(opac = this.getStyle('opacity')){
		if(Browser.getCompat() == 'MOZILLA'){
			return opac * 100;
		}else{
			return opac;
		}
	}
	
	return '100';
}

//===========================================================================
// Function:       Elem.getValue()
// Purpose:        get the value of an element
// History:        2007.06.27 - JQuattlebaum - updated to make sure we are
//                              returning a string and not an object
//===========================================================================
Elem.prototype.getValue = function(){
	var val = false;
	if(this.type == undefined){
		this.type = 'div';
	}
	if(this.type != undefined){
		switch(this.type){
			// GENERAL FORM INPUTS
			case 'text':
			case 'password':
			case 'hidden':
			case 'input':
				val = this.value;
			break;

			// MULTIPLE SELECT BOX
			case 'select-multiple':
			var t = [];
				for(var i = 0; i < this.options.length; i++){
					if(this.options[i].selected){
						t.push(this.options[i].value);
					}
				}
				if(t.length > 0){
					val = t
				}
			break;

			// SELECT BOX
			case 'select-one':
				val = this.options[this.selectedIndex].value;
			break;

			// TEXTAREA
			case 'textarea':
				val = this.value;
			break;

			// DIV
			case 'div':
			default:
				val = this.innerHTML;
		}
	}
	//do final check and false it if the value we found was empty or an object
	if(val != false && trim(val) == '' || val == 'object') {
		val = false;
	}
	return val;
}

Elem.prototype.setValue = function(val){
	if(this.type == undefined){
		this.type = 'div';
	}
	if(this.type != undefined){
		switch(this.type){
			case 'text':
			case 'hidden':
			case 'input':
				this.value = val;
				break;
				
			/*
			case 'select-multiple':
			var t = [];
				for(var i = 0; i < this.options.length; i++){
					if(this.options[i].value == val){
						this.options[i].selected = true;
					}
				}
				if(t.length > 0){
					val = t
				}
				break;
			*/
			case 'select-one':
				for(var i = 0; i < this.options.length; i++){
					if(this.options[i].value == val){
						this.options[i].selected = true;
						break;
					}
				}
				break;
				
			case 'textarea':
				this.value = val;
				break;
			case 'div':
			default:
				this.innerHTML = val;
				break;
		}
	}
	return true;
}

Elem.prototype.centerElement = function(){
	var d = this.getDimensions();
	var w = document.body.offsetWidth;
	var h = document.body.offsetHeight;
	
	if(self.innerHeight){
		w = self.innerWidth;
		h = self.innerHeight;
	}else if(document.documentElement && document.documentElement.clientHeight){
		w = document.documentElement.clientWidth;
		h = document.documentElement.clientHeight;
	}else if(document.body){
		w = document.body.clientWidth;
		h = document.body.clientHeight;
	}
	
	var x = (w / 2) - (d.width / 2);
	var y = (h / 2) - (d.height / 2);
	
	this.setStyle('left',x + 'px');
	this.setStyle('top',y + 'px');
	
	return this;
}

/**
 * taken from http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html 
 * and tweaked a bit to meet my needs
 */
Elem.prototype.addEvent = function(type, fn){
	if(this.addEventListener){
		this.addEventListener( type, fn, false );
	}else if(this.attachEvent){
		/* this["e"+type+fn] = fn;
		this[type+fn] = function() { this["e"+type+fn]( window.event ); } */
		this.attachEvent( "on"+type, fn );
	}
}

/**
 * taken from http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html 
 * and tweaked a bit to meet my needs
 */
Elem.prototype.removeEvent = function(type, fn){
	if(this.removeEventListener){
		this.removeEventListener( type, fn, false );
	}else if (this.detachEvent){
		this.detachEvent( "on"+type, fn );
		/* this[type+fn] = null;
		this["e"+type+fn] = null; */
	}
}

Elem.prototype.pulse = function(low, high, speed, steps, low_pause, high_pause) {
	//colorKey : index for hexadecimal calculations (this needs to be abstracted to its own ignorant method)
	var colorKey = '0123456789abcdef';
	//baseColor : our color frame of reference
	var baseColor = this.style.backgroundColor;
	if(baseColor == '') {
		baseColor = '898989';
	}
	//low : low color to pulsate from - if not set, low should be a few shades lower than the initial bg color value
	if(low == '') {
		low = '000000';
	}
	//high : high color to pulsate to - if not set should be a bit higher than the initial bg color value
	if(high == '') {
		high = 'ffffff';
	}
	//speed : speed of transition between pulse steps
	//steps : number of steps from low to high
	//low_pause : pause duration for low color
	//high_pause : pause duration for high color
	alert('colorKey : '+colorKey+'\nbaseColor : '+baseColor+'\nlow : '+low+'\nhigh : '+high+'\nspeed : '+speed+'\nsteps : '+steps+'\nlow_pause : '+low_pause+'\nhigh_pause : '+high_pause);
	
}

/**
 * Effects methods
 */

function Effects(){
	return this;
}

Effects.prototype.fade = function(elem,fade_to,time){
	var speed = Math.round(time / 100);
	var timer = 0;
	var cur_opac = Elem.opacity();
	
	if(fade_to > cur_opac){
		for(i = cur_opac; i <= fade_to; i++) {
			setTimeout("getElem('" + Elem.id + "').setStyle('opacity','" + i + "')",(timer * speed));
			timer++;
		}
	}else if(fade_to < cur_opac){
		for(i = cur_opac; i >= fade_to; i--) {
			setTimeout("getElem('" + Elem.id + "').setStyle('opacity','" + i + "')",(timer * speed));
			timer++;
		}
	}
}

Effects.prototype.slide = function(elem,pos,effects){
	var time = Effects.time;
	var direction = Effects.direction;
	var move = Effects.move;
	
	var speed = Math.round(time / 100);
	var timer = 0;
	var cur_dim = Elem.getDimensions();
	
	if(pos.left < cur_dim.left){
		for(i = cur_dim.left; i >= pos.left; i--){
			setTimeout("getElem('" + Elem.id + "').setStyle('left','" + i + "');",(timer * speed));
			timer++;
		}
	}else if(pos.left > cur_dim.left){
		for(i = cur_dim.left; i <= pos.left; i++){
			setTimeout("getElem('" + Elem.id + "').setStyle('left','" + i + "');",(timer * speed));
			timer++;
		}
	}
	
	timer = 0;
	
	if(pos.top < cur_dim.top){
		for(i = cur_dim.top; i >= pos.top; i--){
			setTimeout("getElem('" + Elem.id + "').setStyle('top','" + i + "');",(timer * speed));
			timer++;
		}
	}else if(pos.top > cur_dim.top){
		for(i = cur_dim.top; i <= pos.top; i++){
			setTimeout("getElem('" + Elem.id + "').setStyle('top','" + i + "');",(timer * speed));
			timer++;
		}
	}
	
	timer = 0;
	
	/* alert(pos.height + ' -- ' + cur_dim.height); */
	
	if(pos.height < cur_dim.height){
		for(i = cur_dim.height; i >= pos.height; i--){
			setTimeout("getElem('" + Elem.id + "').setStyle('height','" + i + "');",(timer * speed));
			timer++;
		}
	}else if(pos.height > cur_dim.height){
		for(i = cur_dim.height; i <= pos.height; i++){
			setTimeout("getElem('" + Elem.id + "').setStyle('height','" + i + "');",(timer * speed));
			timer++;
		}
	}
	
	timer = 0;
	
	if(pos.width < cur_dim.width){
		for(i = cur_dim.width; i >= pos.width; i--){
			setTimeout("getElem('" + Elem.id + "').setStyle('width','" + i + "');",(timer * speed));
			timer++;
		}
	}else if(pos.width > cur_dim.width){
		for(i = cur_dim.width; i <= pos.width; i++){
			setTimeout("getElem('" + Elem.id + "').setSetyle('width','" + i + "');",(timer * speed));
			timer++;
		}
	}
}


/**
 * String methods
 */

String.prototype.styleToJs = function(){
	var name_parts = this.split('-');
	var cap_first = false;
	if(name.indexOf('-') == 0){
		cap_first = true;
	}
	var ret_name = '';
	for(n = 0; n < name_parts.length; n++){
		var first_char = name_parts[n].substr(0,1).toUpperCase();
		var sanitized = '';
		
		if(n == 0){
			if(cap_first){
				sanitized = first_char + name_parts[n].substr(1);
			}else{
				sanitized = name_parts[n];
			}
		}else{
			sanitized = first_char + name_parts[n].substr(1);
		}
		
		ret_name+= sanitized;
	}
	
	return ret_name;
}

String.prototype.jsToStyle = function(){
	var ret_name = '';
	for(r = 0; r < this.length; r++){
		var the_char = this.substr(r,1);
		if(the_char.isUpperCase()){
			ret_name+= '-' + the_char.toLowerCase();
		}else{
			ret_name+= the_char;
		}
	}
	return ret_name;
}

String.prototype.isUpperCase = function(){
	var str = this;
	for(u = 0; u < str.length; u++){
		var chr = str.substring(u,1);
		var up_chr = chr.toUpperCase();
		if(chr.charCodeAt(0) != up_chr.charCodeAt(0)){
			return false;
		}
	}
	return true;
}

String.prototype.isLowerCase = function(){
	for(u = 0; u < this.length; u++){
		var chr = this.substr(u,1);
		var lo_chr = chr.toLowerCase();
		if(chr.charCodeAt(0) != lo_chr.charCodeAt(0)){
			return false;
		}
	}
	return true;
}

String.prototype.parseScripts = function(){
	var ret = this;
	var scripts = ret.match(/<script>([^<\/sc]*)<\/script>/ig);
	if(scripts != undefined){
		for(s = 0; s < scripts.length; s++){
			var script = scripts[s].replace(/<script>([^<\/sc]*)<\/script>/i,"$1");
			eval(script);
			var script_tag = scripts[s].escapeChars();
			var rpl_str = "/" + script_tag + "/i";
			ret = ret.replace(eval(rpl_str),"");
		}
	}
	return ret;
}

String.prototype.escapeChars = function(){
	var ret = this;
	ret = ret.replace(/\//,"\\/")
	ret = ret.replace(/\(/,'\\(');
	ret = ret.replace(/\)/,'\\)');
	return ret;
}

String.prototype.trim = function(){
	return this.ltrim().rtrim(); //this is an object, not a string
}

String.prototype.ltrim = function(){
	return this.replace(/^\s*/,''); //again, object, not string
}

String.prototype.rtrim = function(){
	return this.replace(/\s*$/,''); //OOOOOOBJECT!  NOOOOOT STRIIIIING!!!  how does this even work for you????????
}

/**
 * Browser methods
 */
 
function Browser(){
	this.init();
	return this;
}

Browser.prototype.browser_ver = false;
Browser.prototype.browser_compat = false;
Browser.prototype.platform = false;

Browser.prototype.init = function(){
	if(navigator.userAgent == undefined && navigator.vendor != undefined){
		if(navigator.vendor.indexOf('Apple') > -1){
			this.browser_ver = 'SAFARI';
			this.browser_compat = 'MOZILLA';
		}else if(navigator.vendor.indexOf('iCab') > -1){
			this.browser_ver = 'ICAB';
			this.browser_compat = 'MOZILLA';
		}else if(navigator.vendor.indexOf('KDE') > -1){
			this.browser_ver = 'KONQUEROR';
			this.browser_compat = 'MOZILLA';
		}
	}else{
		if(navigator.userAgent.indexOf('MSIE') > -1){
			this.browser_ver = 'IE';
			this.browser_compat = 'IE';
		}else if(navigator.userAgent.indexOf('Mozilla') > -1 || navigator.userAgent.indexOf('Firefox') > -1){
			this.browser_ver = 'MOZILLA';
			this.browser_compat = 'MOZILLA';
		}
	}
	
	if(navigator.platform != undefined){
		if(navigator.platform.indexOf('Mac') > -1){
			this.platform = 'MAC';
		}else if(navigator.platform.indexOf('Win') > -1){
			this.platform = 'WIN';
		}else if(navigator.platform.indexOf('Linux') > -1){
			this.platform = 'NIX';
		}else{
			this.platform = 'UNKNOWN';
		}
	}
}

Browser.prototype.getBrowser = function(){
	return this.browser_ver;
}

Browser.prototype.getCompat = function(){
	return this.browser_compat;
}

Browser.prototype.getPlatform = function(){
	return this.platform;
}

Browser.prototype.getViewportWidth = function(){
	var wid = 0;
	var marg_left = 0;
	var marg_right = 0;
	if(this.getCompat() == 'MOZILLA'){
		wid = document.body.clientWidth;
	}else if(this.getCompat() == 'IE'){
		wid = document.body.scrollWidth;
	}
	
	/* alert(document.body.style.marginLeft);
	alert(document.body.marginLeft);
	alert(document.body.style.margin);
	alert(document.body.style.marginTop);
	alert(document.body.style.marginLeftWidth); */
	
	/* marg_left = document.body.style.marginLeft.replace(/px/,'');
	marg_right = document.body.style.marginRight.replace(/px/,''); */
	
	wid = wid - marg_left;
	wid = wid - marg_right;
	
	return wid;
}

/**
 * ajax stuff
 */
function Ajax(args){
	if(typeof(args) == 'object'){
		this.setParams(args);
	}
	
	this.payload = null;
	this.requestCount = 0;
	this.status = 0;
	this.to = false;
	this.xmlObjs = Array();
	
	if(!this.type){
		this.type = 'GET';
	}
	
	return this;
}

Ajax.prototype.send = function(){
	this.xmlObjs[this.requestCount] = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("MSXML2.XMLHTTP");
	xmlObj = this.xmlObjs[this.requestCount];
	
	xmlObj.open(this.type, this.url, true);
	id = this.requestCount;
	xmlObj.onreadystatechange = function(){
		switch(Ajax.xmlObjs[id].readyState){
			case 0:
				Ajax.noInit();
				break;
			case 1:
				Ajax.onLoadStart();
				break;
			case 2:
				Ajax.onLoadFinish();
				break;
			case 3:
				Ajax.onInteractive();
				break;
			case 4:
				Ajax.onComplete();
				break;
		}
	}
	
	if(this.type == 'POST'){
		xmlObj.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		xmlObj.setRequestHeader("Content-length", this.payload.length);
		xmlObj.setRequestHeader("Connection", "close");
	}
	this.requestCount++;
	xmlObj.send(this.payload);
	return id;
}

Ajax.prototype.registerCallback = function(trigger,toCall){
	switch(trigger){
		case 'onLoadStart':
			this.onLoadStart = toCall;
			break;
		case 'onChange':
			this.onChange = toCall;
			break;
		case 'onComplete':
			this.onComplete = toCall;
			break;
	}
}

Ajax.prototype.setParams = function(args){
	if(typeof(args) == 'object'){
		if(args.type){
			this.type = args.type;
		}
		
		if(args.url){
			this.url = args.url;
		}
		
		if(args.payload){
			this.payload = args.payload;
		}
		
		if(args.onChange){
			this.registerCallback('onChange',args.onChange);
		}
		
		if(args.onComplete){
			this.registerCallback('onComplete',args.onComplete);
		}
		
		if(args.onLoadStart){
			this.registerCallback('onLoadStart',args.onLoadStart);
		}
	}
}

Ajax.prototype.getResponse = function(id){
	if(id != undefined){
		return this.xmlObjs[id].responseText;
	}
	return false;
}

Ajax.prototype.noInit = function(){
	/* obj not init'd */
}

Ajax.prototype.onLoadStart = function(){
	/* alert('loading'); */
}

Ajax.prototype.onLoadFinish = function(){
	/* alert('loaded'); */
}

Ajax.prototype.onInteractive = function(){
	/* alert('interactive'); */ 
}

Ajax.prototype.onChange = function(){
	/* alert('changing'); */
}

Ajax.prototype.onComplete = function(){
	/* alert('complete'); */
}

/* page load stuff */
var pageElem;
var Effects;
var Browser;
var Ajax;
var psy_loaded = false;

function loadPsytoplasm(){
	pageElem = new pageElem();
	
	Effects = new Effects();
	Browser = new Browser();
	Ajax = new Ajax();
	psy_loaded = true;
}

var onloads = [];

function addOnLoad(f){
	this.onloads.push(f);
}

function doOnLoad(){
	for(var l = 0; l < onloads.length; l++){
		setTimeout(onloads[l],0);
	}
}

window.onload = doOnLoad;

/* addOnLoad(loadPsytoplasm); */

function getElem(el_id){
	if(pageElem.elems[el_id] != undefined){
		el = pageElem.elems[el_id];
	}else if(el = document.getElementById(el_id)){
		pageElem.elems[el_id] = pageElem.extend(el);
	}
	return el;
};
/* aliases */
function getObj(e){
	return getElem(e);
}
function getObject(e){
	return getElem(e);
}
function getElement(e){
	return getElem(e);
}

//===========================================================================
// Function:       print_r()
// Purpose:        return structure of an object
// Example:        alert(print_r(OObj, true, '\t'));
// IMPORTANT NOTE: When using the recursive flag of this function
//                 you *have* to be careful as JS likes to loop back around
//                 between objects infinitly.  a future 'seen' flag will
//                 (hopefully) get built in.
// History:        2005.12.02 - JQuattlebaum - Initial Revision
//===========================================================================
function print_r(OObj, recurse, preChr) {
        if(typeof OObj == 'object') {
                var treeDisplay = '';
                for(var key in OObj) {
                        treeDisplay += preChr+'['+key+'] => \''+OObj[key]+'\' ('+typeof OObj[key]+')\n';
                        if(recurse && typeof OObj[key] == 'object') {
                                treeDisplay += print_r(OObj[key], recurse, preChr+'\t');
                        }
                }
                return treeDisplay;
        } else {
                return 'not an object!';
        }
}

//===========================================================================
// Functions:     trim(), ltrim(), rtrim()
// Purpose:       provide trim functionality
// History:       2006.5.13 - JQuattlebaum - Initial Revision
//                2006.8.3 - JQuattlebaum - Revised
//===========================================================================
function ltrim(str){
	return str.replace(/^\s+/g,'');
}
function rtrim(str){
	return str.replace(/\s+$/g,'');
}
function trim(str){
	return ltrim(rtrim(str));
}

//===========================================================================
// Function:       isEmailValid(str)
// Purpose:        only if str is a valid email address return true
// History:        2007.10.21 - JQuattlebaum - Initial Revision
//===========================================================================
function isEmailValid(str) {
	var invalidRegExp = /(@.*@)|(\.\.)|(@\.)|(\.@)|(^\.)/;
	var regExp = /^.+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,3}|[0-9]{1,3})(\]?)$/;
	if(!invalidRegExp.test(str) && regExp.test(str)) {
		return true;
	}
	return false;
}

//===========================================================================
// Function:       getElementsByClassName(cName, tagName, rootNode)
// Purpose:        return a collection of elements by class name
// Example:        getElementsByClassName('foo_bar'); // gives me all objects with class name 'foo_bar' under document
//                 getElementsByClassName('foo_bar', 'select'); // gives me all select objects with class name 'foo_bar' under document.
//                 getElementsByClassName('foo_bar', 'select', document.getElementById('bar_foo')); // gives me all select objects with class name 'foo_bar' under bar_foo object accessed by id
//                 getElementsByClassName('foo_bar', 'select', document.forms.bar_foo); // gives me all select objects with class name 'foo_bar' under form 'bar_foo'.
// History:        2007.10.19 - JQuattlebaum - Initial Revision
//===========================================================================
function getElementsByClassName(cName, tagName, rootNode) {
	if(tagName  == null) { tagName  = '*';      }
	if(rootNode == null) { rootNode = document; }
	var tags = rootNode.getElementsByTagName(tagName);
	var tagsLength = tags.length;
	var classNamePattern = new RegExp("(^|\\s)"+cName+"(\\s|$)");
	var elementCollection = new Array();
	for (x = 0, y = 0; x < tagsLength; x++) {
		if ( classNamePattern.test(tags[x].className) ) {
			elementCollection[y] = tags[x];
			y++;
		}
	}
	return elementCollection;
}
/* supplimental extension of document for getElementsByClassName() */
document.getElementsByClassName = function(cName, tagName, rootNode) { return getElementsByClassName(cName, tagName, rootNode); }

/* load the thing */
if(psy_loaded == false){
	loadPsytoplasm();
}
