//
// create the GOOP Object
//
var GOOP = {};


//
// initator, called automatically when the DOM is ready
//
GOOP.init = function() {
	GOOP.extend(document.body,["events","dimensions"]);
};


//
// extends the element(s) attaching the GOOP methods
//
GOOP.extend = function(element,methods) {
	
	// find out what methods to attach
	methods = methods || "all";
	if(methods == "all") {
		methods = ["styles","events","dimensions","effects"];
	}
	if(methods.indexOf("basic") == -1) {
		methods.unshift("basic");
	}
	
	// array of elements
	if(element[0] && element.tagName == undefined) {
		
		var l = element.length;
		for(var i = 0; i < l; i++) {
			GOOP.extend(element[i],methods);
		}
		
	// individual element
	} else {
		
		var l = methods.length;
		for(var i = 0; i < l; i++) {
			if(!element["goop_" + methods[i]]) {
				for(var m in GOOP[methods[i]]) {
					element[m] = GOOP[methods[i]][m];
				}
				element["goop_" + methods[i]] = true;
			}
		}
		
	}
	
	return element;
	
};


//
// basic, or most commonly used from the other extended types
//
GOOP.basic = {
	
	//
	// get elements
	// thank you simon willision (http://simonwillison.net/2003/Mar/25/getElementsBySelector/)
	//
	getElements:function(search) {
		
		var search = search.split(" ");
		var l = search.length;
		var context = new Array(this);
		
		for(var i = 0; i < l; i++) {
			
			var s = search[i];
			
			// ids
			if(s.indexOf("#") > -1) {
				var b = s.split("#");
				var t = b[0], id = b[1];
				var el = document.getElementById(id);
				if(!el) { return new Array(); }
				context = new Array(el);
				continue;
			}
			
			// classes
			if(s.indexOf(".") > -1) {
				var b = s.split(".");
				var t = b[0] || "*", c = b[1], f = new Array();
				for(var j = 0; j < context.length; j++) {
					var el = context[j].getElementsByTagName(t.toUpperCase());
					for(var k = 0; k < el.length; k++) {
						f.push(el[k]);
					}
				}
				context = new Array();
				for(var j = 0; j < f.length; j++) {
					if(f[j].className && f[j].className.match(new RegExp('(^|\\s)' + c + '(?:\\s|$)')) != null) {
						context.push(f[j]);
					}
				}
				continue;
			}
			
			// code to deal with attribute selectors
			if(s.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
				var t = RegExp.$1 || "*", an = RegExp.$2, ao = RegExp.$3, av = RegExp.$4, f = new Array();
				for(var j = 0; j < context.length; j++) {
					var el = context[j].getElementsByTagName(t.toUpperCase());
					for(var k = 0; k < el.length; k++) {
						f.push(el[k]);
					}
				}
				if(Browser.name == "Explorer" && an.toLowerCase() == "class") { an = "className"; } // for IE bug
				var ftn; // this function will be used to filter the elements
				switch(ao) {
					case "=": ftn = function(e) { return (e.getAttribute(an) == av); }; break; // equality
					case "~": ftn = function(e) { return (e.getAttribute(an).match(new RegExp("\\b" + av + "\\b"))); }; break; // match one of the space seperated words
					case "|": ftn = function(e) { return (e.getAttribute(an).match(new RegExp("^" + av + "-?"))); }; break; // match start with value followed by options hyphen
					case "^": ftn = function(e) { return (e.getAttribute(an).indexOf(av) == 0); }; break; // match starts with
					case "$": ftn = function(e) { return (e.getAttribute(an).lastIndexOf(av) == e.getAttribute(an).length - av.length); }; break; // match wnds with value - fails with warning in Opera 7
					case "*": ftn = function(e) { return (e.getAttribute(an).indexOf(av) > -1); }; break; // match ends with value
					default: ftn = function(e) { return e.getAttribute(an); }; // just test for existence of attribute
				}
				context = new Array();
				for(var j = 0; j < f.length; j++) {
					if(ftn(f[j])) {
						context.push(f[j]);
					}
				}
				continue;
			}
			
			// if we made it here, its just a tag name
			var f = new Array();
			for(var j = 0; j < context.length; j++) {
				var el = context[j].getElementsByTagName(s.toUpperCase());
				for(var k = 0; k < el.length; k++) {
					f.push(el[k]);
				}
			}
			context = f;
			
		}
		
		return (context.length > 0 ? GOOP.extend(context) : null);
		
	},

	getElement:function(search) {
		var elements = this.getElements(search);
		return (elements != null ? elements[0] : null);
	},


	//
	// set and get properties
	//
	set:function(attr,value) {
		
		if(typeof attr != "string") {
			for(var i in attr) {
				this.set(i,attr[i]);
			}
		}

		if(attr == "html") {
			this.innerHTML = value;
		} else if(attr == "class") {
			this.className = value;
		} else if(attr == "id") {
			this.id = value;
		} else if(attr == "text") {
			if(this.innerText) {
				this.innerText = value;
			} else {
				this.textContent = value;
			}
		} else {
			this[attr] = value;
		}

		return this;

	},

	get:function(attr) {

		if(attr == "html") {
			return this.innerHTML;
		} else if(attr == "class") {
			return this.className;
		} else if(attr == "id") {
			return this.id;
		} else if(attr == "text") {
			if(this.innerText) {
				return this.innerText;
			} else {
				return this.textContent;
			}
		} else {
			return this[attr];
		}

	},
				
	hasClass:function(name) {
		return (this.get("class").match(new RegExp('(^|\\s)' + name + '(?:\\s|$)')) != null ? true : false);
	},

	addClass:function(name) {
		if(!this.hasClass(name)) {
			this.set("class",(this.get("class") + " " + name).trim());
		}
		return this;
	},

	removeClass:function(name) {
		this.set("class",this.get("class").replace(new RegExp('(^|\\s)' + name + '(?:\\s|$)'), '$1'));
		return this;
	},
	
	
	//
	// inserters - replace, adopt, grab, inject, wraps, and destroy
	// huge thanks to MooTools
	//
	inserters:function(where,context,element) {
		if(where == "after") {
			if(!element.parentNode) { return; }
			var next = element.nextSibling;
			(next) ? element.parentNode.insertBefore(context,next) : element.parentNode.appendChild(context);
		} else if(where == "before") {
			if(element.parentNode) {
				element.parentNode.insertBefore(context,element);
			}
		} else if(where == "bottom") {
			element.appendChild(context);
		} else if(where == "top") {
			var first = element.firstChild;
			(first) ? element.insertBefore(context,first) : element.appendChild(context);
		}
	},
	
	replaces:function(element) {
		element.parentNode.replaceChild(this, element);
		return this;
	},

	adopt:function(element) {
		if(element[0]) {
			var p = this; // acts as parent
			element.each(function(el) {
				p.adopt(el);
			});
		} else {
			this.appendChild(element);
		}
		return this;
	},
	
	grab:function(element,where) {
		this.inserters((where || "bottom"),element,this);
		return this;
	},
	
	inject:function(element,where) {
		this.inserters((where || "bottom"),this,element);
		return this;
	},
	
	wraps:function(element,where) {
		if(element[0]) {
			var l = element.length
			for(var i = 0; i < l; i++) {
				this.wraps(element[i],where);
			}
		} else {
			this.replaces(element).grab(element,where);
		}
		return this;
	},

	destroy:function() {
		return this.parentNode.removeChild(this);
	},
	
	empty:function() {
		while(this.hasChildNodes()) {
			this.removeChild(this.firstChild);
		}
	},
	
	
	//
	// set and get misc properties and values in storage
	// thank you MooTools
	//
	_set:function(property,value) {
		this.storage = this.storage || [];
		this.storage[property] = value;
		return this;
	},
	
	_get:function(property) {
		if(!this.storage) { return false; }
		if(this.storage[property] == 0 || this.storage[property] == "0") { return 0; }
		return this.storage[property] || false;
	}
	
};



// 
// styles
// thank you MooTools
//
GOOP.styles = {
	
	setStyle:function(style,value) {

		if(style == "opacity") {
			if(!this.currentStyle || !this.currentStyle.hasLayout) { this.style.zoom = 1; }
			this.style.opacity = value * .01;
			this.style.filter = "alpha(opacity=" + value + ")";
			value = (value < 0 ? 0 : (value > 100 ? 100 : value)); // can't be > 100 || < 0
			this._set("opacity",value.toInt()); // stored for reference
		} else if(style == "float") {
			if(Browser.name == "Explorer") {
				this.style.styleFloat = value;
			} else {
				this.style.cssFloat = value;
			}
		} else {
			this.style[style.camelCase()] = value + (typeof value == "number" && style.camelCase() != "zIndex" ? "px" : '');
		}

		return this;
	},

	setStyles:function(styles) {
		for(var style in styles) {
			this.setStyle(style,styles[style]);
		}
		return this;
	},

	getStyle:function(style) {

		if(style == "opacity") {
			if(this._get("opacity") == 0) { return 0; }
			if(Browser.name == "Explorer") {
				return this._get("opacity") || this.style.filter;
			} else {
				return this._get("opacity") || this.style.opacity;
			}
		} else if(style == "float") {
			if(Browser.name == "Explorer") {
				return this.style.styleFloat;
			} else {
				return this.style.cssFloat;
			}
		} else {
			return this.style[style.camelCase()];
		}

	},
	
	getStyles:function(styles) {
		var returned = {};
		for(var style in styles) {
			returned[style] = this.getStyle(style);
		}
		return returned;
	}
	
};


// 
// events to attach to elements
//
GOOP.events = {
	
	addEvent:function(type,ftn) {
		if(this.addEventListener) {
			
			// HUGE thanks to "incoherent babble" at http://blog.stchur.com/2007/03/15/mouseenter-and-mouseleave-events-for-firefox-and-other-non-ie-browsers/
			
			if(type.match(/mouseenter|mouseleave/i)) {
				this.addEventListener((type.match(/mouseenter/i) ? "mouseover" : "mouseout"),this._mouseEvent(ftn),false);
			} else {
				this.addEventListener(type,ftn,false);
			}
			
		} else if(this.attachEvent) {
			var p = this;
			this._set("event_" + type, function() { return ftn.apply(p,[window.event]); });
			this.attachEvent("on" + type,this._get("event_" + type));
		} else {
			this["on" + type] = ftn;
		}
		return this;
	},

	addEvents:function(events) {
		for(var e in events) {
			this.addEvent(e,events[e]);
		}
		return this;
	},

	removeEvent:function(type,ftn) {
		if(this.removeEventListener) {
			this.removeEventListener(type,ftn,false);
		} else if(this.detachEvent) {
			this.detachEvent("on" + type,this._get("event_" + type));
		} else {
			this["on" + type] = null;
		}
		return this;			
	},

	removeEvents:function(events) {
		for(var e in events) {
			this.removeEvent(events);
		}
		return this;
	},

	doEvent:function(type) {
		if(document.createEvent) {
			var e = document.createEvent("HTMLEvents");
			e.initEvent(type,true,true);
			this.dispatchEvent(e);
		} else if(document.createEventObject) {
			this._set("event_" + type,null);
			this.fireEvent("on" + type,document.createEventObject());
		} else {
			this["on" + type]();
		}
		return this;
	},

	doEvents:function(events) {
		var l = events.length;
		for(var i = 0; i < l; i++) {
			this.doEvent(events[i]);
		}
		return this;
	},
	
	_mouseEvent:function(ftn) {
		return function(e) {
			if(this === e.relatedTarget || this._childOf(e.relatedTarget)) { return; }
			ftn.call(this,e);
		}
	},
	
	_childOf:function(target) {
		if(this === target) { return false; }
		while(target && target !== this) {
			target = target.parentNode;
		}
		return target === this;
	}
	
};


//
// dimensions
// thanks MooTools
// 
GOOP.dimensions = {
	
	getSize:function() {
		return {width:this.offsetWidth, height:this.offsetHeight};
	},

	getScroll:function() {
		var tag = this.tagName.toLowerCase();
		if(tag.indexOf("body") > -1 || tag.indexOf("html") > -1) {
			return {left:(window.pageYOffset || document.body.scrollLeft || document.documentElement.scrollLeft), top:(window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop)};
		} else {
			return {left:this.scrollLeft, top:this.scrollTop};
		}
	},

	getPosition:function() {
		var s = this.getScroll(), l = this.offsetLeft, t = this.offsetTop, p = this.offsetParent;
		while(p != null) {
			l += p.offsetLeft;
			t += p.offsetTop;
			p = p.offsetParent;
		}
		return {left:l + s.left, top:t + s.top};
	},

	getCoordinates:function() {
		var s = this.getSize();
		var p = this.getPosition();
		return {left:p.left, top:p.top, width:s.width, height:s.height, right:p.left + s.width, bottom:p.top + s.height};
	}
	
};


// 
// effects
// thank you MooTools
//
GOOP.effects = {
	
	fade:function(action,options) {

		options = options || {};
		action = (action || "out");
	
		if(action == "in") {
			if(!this.getStyle("opacity")) { 
				this.setStyle("opacity",0); 
			}
		} else if(action == "out") {
			if(!this.getStyle("opacity")) { 
				this.setStyle("opacity",100); 
			}
		}

		this.tween("opacity",(action == "in" ? 0 : (options.max || 100)),(action == "out" ? 0 : (options.max || 100)),options);
	
		return this;
	
	},

	hide:function(options) {
		options = options || {};
		options.direction = (options.direction || "top");
		options.duration = 1;
		this.slide("out",options);
		return this;
	},
	
	show:function(options) {
		options = options || {};
		options.direction = (options.direction || "top");
		options.duration = 1;
		this.slide("in",options);
		return this;
	},

	toggle:function(options) {
	
		options = options || {};
		options.direction = (options.direction || "top");
	
		var pos = this.getStyle("margin-" + options.direction);
		this.slide((pos == '' || pos.toInt() >= 0 ? "out" : "in"),options);
	
		return this;
	
	},

	slide:function(action,options) {
	
		options = options || {};
		options.direction = (options.direction || "top");
		action = (action || "out");
	
		// make a parent div to wrap it in
		var p = GOOP.extend(this.parentNode,[]);
		if(p.tagName != "DIV" || !p.hasClass("slide")) {
			new Element("div",{
				"class":"slide",
				"setStyles":{
					"margin":0,
					"padding":0,
					"overflow":"hidden"
				}
			}).wraps(this);
		}
	
		// get current position
		var pos = this.getStyle("margin-" + options.direction);
		if(pos == '') {
			this.setStyle("margin-" + options.direction,0);
			pos = 0;
		}
	
		// check if we should slide
		if(pos.toInt() <= 0 && action == "in") {
			this.tween("margin-" + options.direction,pos.toInt(),0,options);
		} else if(pos.toInt() >= 0 && action == "out") {
			this.tween("margin-" + options.direction,0,-this.getSize()[(options.direction == "top" || options.direction == "bottom" ? "height" : "width")],options);
		}
	
		return this;
	
	},

	morph:function(styles,options) {
		options = options || {};
		for(var i in styles) {
			this.tween(i,styles[i][0],styles[i][1],options);
		}
		return this;
	},

	tween:function(style,from,to,options) {

		options = options || {};
		var complete = options.complete || function() {}; // function to execute once completed
		var start = options.start || function() {}; // function execute from the start
		var diff = to.toInt() - from.toInt();
		var interval = Math.ceil(1000 / (options.fps || 50));
		var tf = Math.ceil((options.duration || 500) / interval);
		var step = diff / tf;
	
		var p = this; // acts as parent
	
		// make a tweentimer array
		if(!this.tweentimer) {
			this.tweentimer = new Array();
		}
	
		// execute at start
		start();
	
		// firstly, stop the timer so we don't leak memory
		this.stopTween(style);
		this.tweentimer[style] = setInterval(function() {
			
			var pos = p.getStyle(style);
		
			// set if it returns empty
			if(pos == '') {
				p.setStyle(style,from);
				pos = from;
			} else {
				pos = pos.toFloat();
			}
		
			// move
			if(step > 0 && (pos + step) < to) {
				p.setStyle(style,pos + step);
			} else if(step < 0 && (pos + step) > to) {
				p.setStyle(style,pos + step);
			} else {
				p.setStyle(style,to);
				p.stopTween(style);
				if(!p.anyTweens()) {
					complete(p);
				}
			}
			
		},interval);
	
		return this;
	
	},

	stopTween:function(style) {
		if(!this.tweentimer) { return; }
		clearInterval(this.tweentimer[style]);
		this.tweentimer[style] = -1;
	},

	stopTweens:function(styles) {
		if(styles == "all") {
			for(var i in this.tweentimer) {
				this.stopTween(i);
			}
		} else {
			for(var i in styles) {
				this.stopTween(i);
			}
		}
	},

	anyTweens:function() {
		if(!this.tweentimer) { return false; }
		var flag = false;
		for(var i in this.tweentimer) {
			flag = (this.tweentimer[i] > -1 ? true : flag);
		}
		return flag;
	}
	
};		


// 
// global functions
//
function $(id,methods) {
	if(typeof id == "string") {
		
		var el = document.getElementById(id) || null;
		
		// for ie only, becareful of the 'name' and 'id'
		if(Browser.name == "Explorer" && el != null) {
			if(el.id != id) {
				for(var i = 0; i < document.all[id].length; i++) {
					if(document.all[id][i].id == id) {
						el = document.all[id][i];
						break;
					}
				}
			}
		} 
		
		if(el != null) { GOOP.extend(el,methods); }
		return el;
	} else if(typeof id == "object") {
		var el = GOOP.extend(id,methods);
		return el;
	}
}

function $$(search) {
	return document.body.getElements(search);
}

function $time() {
	return (new Date).getTime();
}

function $rand(n1,n2) {
	return Math.ceil(n2 * Math.random()) + n1;
}

function jump2field(el,to,form) {
	
	// find form if not passed
	if(form) {
		form = document.forms[form];
	} else {
		form = el.parentNode;
		while(form.tagName != "FORM") {
			form = form.parentNode;
		}
	}
	
	// never found, and size is not set
	if(!form && (!el.size || !el.maxLength)) { return; }
	
	// jump to field if we meet the length
	if(el.value.length == (el.maxLength > -1 ? el.maxLength : el.size)) {
		if(form[to]) {
			form[to].focus();
		}
	}
	
}

function $focus(form,name) {
	
	if(!form) { form = document.forms[0]; }
	else { form = document.forms[form]; }
	
	if(!form) { return; }
	
	if(name) {
		if(form[name]) {
			form[name].focus();
		}
	} else {
		for(var i in form) {
			if(!form[i]) { continue; }
			if(!form[i].type) { continue; }
			if(form[i].type.toLowerCase() == "text" || form[i].type.toLowerCase() == "textarea") {
				form[i].focus();
				break;
			}
		}
	}
	
}

function $timeout(mil,ftn) {
	return setTimeout(ftn,mil);
}

function $interval(mil,ftn) {
	return setInterval(ftn,mil);
}


// huge thanks to supersleight
var fixPNG = {
	
	path:"transparent.gif",
	active:true,
	
	init:function(element,mode) {		
		element = element || document;
		if(Browser.name == "Explorer" && Browser.version <= 6) {
			
			for(var i = element.all.length - 1, el = null; el = element.all[i]; i--) {
				
				// backgrounds
				if(el.currentStyle.backgroundImage.match(/\.png/i) != null) { fixPNG.bg_fix(el,mode); }

				// images
				if(el.tagName.toLowerCase() == "img" && el.src.match(/\.png/i) != null) { fixPNG.el_fix(el,mode); }

				// apply position to active elements
				if(this.active && (el.tagName.toLowerCase() == "a" || el.tagName.toLowerCase() == "input") && el.style.position === '') { el.style.position = "relative"; }
				
			}
			
		}
	},
	
	bg_fix:function(element,mode) {
		var bg = element.currentStyle.backgroundImage;
		var src = bg.substring(5,bg.length-2); // strips the src from inside: url("path/to/file.png")
		if(element.currentStyle.backgroundRepeat == "no-repeat") {
			mode = mode || "crop";
		}
		element.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "'), sizingMethod='" + (mode || "scale") + "')"; 
		element.style.backgroundImage = "url(" + this.path + ")";
	},
	
	el_fix:function(element,mode) {
		var src = element.src;
		element.style.width = element.width + "px";
		element.style.height = element.height + "px";
		element.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "',sizingMethod='" + (mode || "scale") + "')";
		element.src = this.path;
	}
	
};


//
// creates an element and gets extended by GOOP
//
var Element = function(tag,options) {
	
	options = options || {};
	
	var el = document.createElement(tag.toUpperCase());
	el = GOOP.extend(el);
	
	for(var op in options) {
		if(typeof op == "string") {
			if(typeof el[op] == "function") {
				el[op](options[op]);
			} else {
				el.set(op,options[op]);
			}
		} else if(typeof op == "object") {
			el[op](options[op]);
		}
	}

	return el;
	
};


//
// swf
// thank you MooTools
//
var SWF = function(path,options) {

	options = options || {};
	options = {
		id:options.id || "swf_" + $time(),
		container:options.container || null,
		width:options.width || 1,
		height:options.height || 1,
		flashVars:options.flashVars || '',
		properties:options.properties || {},
		params:options.params || {
			quality:"high",
			allowScriptAccess:"always",
			wMode:"transparent"
		}
	};
	
	options.properties.id = options.id;			
	options.properties.width = options.width;
	options.properties.height = options.height;
	options.params.flashVars = options.flashVars;
	
	if(Browser.name == "Explorer") {
		options.properties.classid = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
		options.params.movie = path;
	} else {
		options.properties.type = "application/x-shockwave-flash";
		options.properties.data = path;
	}

	var swf = "<object";
	for(var p in options.properties) {
		swf += " " + p + "=\"" + options.properties[p] + "\""; 
	}
	swf += " />";

	for(var p in options.params) {
		swf += "<param name=\"" + p + "\" value=\"" + options.params[p] + "\" />";
	}
	swf += "</object>";
	
	// write it inside of a new div
	$(options.container).adopt(new Element("div").set("html",swf));
	
};


//
// event, makes the event crossbrowser
// HUGE thanks to MooTools
//
var Event = function(e,w) {
	
	w = w || window;
	
	if(e.type.match(/key/i)) {
		var code = e.which || e.keyCode;
	} else if(e.type.match(/(click|mouse|menu)/i)) {
		var page = {
			x:(e.pageX) ? e.pageX : e.clientX + document.body.getScroll().left,
			y:(e.pageY) ? e.pageY : e.clientY + document.body.getScroll().top
		};
		var client = {
			x:(e.pageX) ? e.pageX - w.pageXOffset : e.clientX,
			y:(e.pageY) ? e.pageY - w.pageYOffset : e.clientY
		}
		if(e.type.match(/dommousescroll|mousewheel/i)) {
			var wheel = (e.wheelDelta) ? el.wheelDelta / 120 : -(e.detail || 0) / 3;
		}
		var rightClick = (e.which == 3) || (e.button == 2);
	}
	
	return {
		event:e,
		type:e.type,
		wheel:wheel,
		page:page,
		client:client,
		rightClick:rightClick,
		code:code
	}
	
}


// 
// DOM ready, automatically is called
//
var DOM = {
	count:10,
	ftns:new Array(),
	ready:function(ftn) {
		this.ftns.push(ftn);
	},
	init:function() {
		var l = this.ftns.length;
		for(var i = 0; i < l; i++) {
			(this.ftns.shift())();
		}
	}
};
(function() {

	// now lets check if its ready
	// if it reaches the </body> tag, it should be ready... we also have a counter to check several times just in case
	var timer = 1000;
	var setInt = setInterval(function() {
		if(document.getElementsByTagName("HTML")[0].innerHTML.toLowerCase().lastIndexOf("</body>") > -1) {
			if(DOM.count == 0) {
				clearInterval(setInt);
				DOM.init();
			} else {
				DOM.count--;
			}
		} else {
			timer--;
			if(timer == 0) {
				clearInterval(setInt);
			}
		}
	},10);
	
})();
DOM.ready(GOOP.init);


//
// simple ajax
// thank you XHConn
//
var Ajax = function(options) {
	
	// use this for reference in the prototype
	
	this.options = options || {};
	if(!options.url) { return; }
	this.url = options.url;
	this.options.cache = (!options.cache ? false : (options.cache === false ? false : true));
	this.options.start = options.start || function() {};
	this.options.complete = options.complete || function() {};
	this.options.method = (options.method || "post").toUpperCase();
	this.time = 0; // tracks the amount of time taken for the request
	
	// set up xmlttp
	this.xmlhttp;
	try { this.xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); }
	catch(e) { 
		try { this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); }
		catch(e) { 
			try { this.xmlhttp = new XMLHttpRequest(); } 
			catch(e) { this.xmlhttp = false; }
		}
	}
	
};
Ajax.prototype.stop = function() {
	this.xmlhttp.abort();
};
Ajax.prototype.send = function(query) {
	
	// stop previous request
	this.stop();
	
	// start the time
	this.time = $time();
	
	// start function
	this.options.start();
	
	var p = this; // acts as parent
	this.xmlhttp.onreadystatechange = function() {
		if(p.xmlhttp.readyState == 4 && p.xmlhttp.status == 200) {
			p.time = $time() - p.time;
			p.options.complete(p.xmlhttp.responseText.trim());
		}
	}	
	
	// automatically put a cache killer in, if cache is true
	var url = this.url + "?" + (!this.options.cache ? "c=" + $time() + "&" : '') + (query || '');

	// was it a get or a post?
	if(this.options.method == "GET") {
		this.xmlhttp.open("GET",url,true);
		this.xmlhttp.send(null);
	} else {
		var q = url.indexOf("?");
		this.xmlhttp.open("POST",url.substring(0,q),true);
		this.xmlhttp.setRequestHeader("Method","POST " + url.substring(0,q) + " HTTP/1.1");
		this.xmlhttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
		this.xmlhttp.send(url.substring(q + 1));
	}
	
};


//
// cookies
// thank you Dhryn and quirksmode
//
var Cookie = {
	set:function(name,value,days) {
		var date = new Date();
		date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
		var expires = "; expires=" + date.toGMTString();
		document.cookie = name + "=" + value + expires + "; path=/";
	},
	get:function(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(name + "=") == 0) { 
				return c.substring((name + "=").length,c.length); 
			}
		}
		return false;
	},
	unset:function(name) {
		this.set(name,'',-1);
	}
};


//
// Browser detection 
// thank you quirksmode
//
var Browser = {
	init:function() {
		this.name = this.searchString(this.dataBrowser) || "Unknown";
		this.version = this.searchVersion(navigator.userAgent) || this.searchVersion(navigator.appVersion) || "Unknown";
		this.platform = this.searchString(this.dataOS) || "Unknown";
	},
	searchString:function(data) {
		var l = data.length
		for(var i = 0; i < l; i++) {
			var dataString = data[i].string;
			var dataProp = data[i].prop;
			this.versionSearchString = data[i].versionSearch || data[i].identity;
			if(dataString) {
				if(dataString.indexOf(data[i].subString) != -1) {
					return data[i].identity;
				}
			} else if(dataProp) {
				return data[i].identity;
			}
		}
	},
	searchVersion:function(dataString) {
		var index = dataString.indexOf(this.versionSearchString);
		if (index == -1) return;
		return dataString.substring(index + this.versionSearchString.length + 1).toFloat();
	},
	dataBrowser:[
		{
			string:navigator.userAgent,
			subString:"Chrome",
			identity:"Chrome"
		},
		{ 	string:navigator.userAgent,
			subString:"OmniWeb",
			versionSearch:"OmniWeb/",
			identity:"OmniWeb"
		},
		{
			string:navigator.vendor,
			subString:"Apple",
			identity:"Safari",
			versionSearch:"Version"
		},
		{
			prop:window.opera,
			identity:"Opera"
		},
		{
			string:navigator.vendor,
			subString:"iCab",
			identity:"iCab"
		},
		{
			string:navigator.vendor,
			subString: "KDE",
			identity: "Konqueror"
		},
		{
			string: navigator.userAgent,
			subString: "Firefox",
			identity: "Firefox"
		},
		{
			string: navigator.vendor,
			subString:"Camino",
			identity:"Camino"
		},
		{
			string:navigator.userAgent,
			subString:"Netscape",
			identity:"Netscape"
		},
		{
			string:navigator.userAgent,
			subString:"MSIE",
			identity:"Explorer",
			versionSearch:"MSIE"
		},
		{
			string:navigator.userAgent,
			subString:"Gecko",
			identity:"Mozilla",
			versionSearch:"rv"
		},
		{
			string:navigator.userAgent,
			subString:"Mozilla",
			identity:"Netscape",
			versionSearch:"Mozilla"
		}
	],
	dataOS:[
		{
			string:navigator.platform,
			subString:"Win",
			identity:"Windows"
		},
		{
			string:navigator.platform,
			subString:"Mac",
			identity:"Mac"
		},
		{
			string:navigator.platform,
			subString:"Linux",
			identity:"Linux"
		}
	]

};
	

//
// accordion effect
// thank you leigeber
//
var Accordion = function(id,options) {
	
	options = options || {};
	
	// set variables
	var element = $(id);	
	var headers = element.getElements(options.header || "dt");
	var sliders = element.getElements(options.slider || "dd");
	var count = headers.length;
	this.headers = headers; // store for prototype
	
	// set sliders
	sliders.each(function(el) {
		el._set("accordion_height",el.getSize().height);
		el.setStyles({
			"height":0,
			"display":"none",
			"overflow":"hidden"
		});
	});
	
	// set headers
	headers.each(function(el) {
		el.addEvent("click",slide);
	});
	
	// auto show
	this.open(options.open);
	
	function slide() {
		for(var i = 0; i < count; i++) {
			
			s = sliders[i];
			s.stopTweens("all");

			if(headers[i] == this && s.getStyle("display") == "none") {
				s.setStyle("display",'');
				move(s,true);
			} else if(s.getStyle("display") == '') {
				move(s,false);
			}
			
		}	
	}
	
	function move(c,flag) {
		if(flag) {
			c.morph({
				"height":[0,c._get("accordion_height")],
				"opacity":[0,100]
			},{ 
				duration:300 
			});
		} else if(!flag) {
			c.morph({
				"height":[c._get("accordion_height"),0],
				"opacity":[100,0]
			},{
				duration:300,
				complete:function(el) {
					el.setStyle("display","none");
				}
			});
		}
	}
	
};
Accordion.prototype.open = function(num) {	
	if(!num) { return; }
	this.headers[num].doEvent("click");
}
Accordion.prototype.close = function(num) {
	if(!num) { return; }
	this.headers[num].doEvent("click");
}


//
// tips
// thanks for reference leigeber
//
var Tip = function(id,options) {
	
	options = options || {};
	var element = $(id);
	
	// create the tip div
	var tip = new Element("div",{
		"class":"tip",
		"html":(options.title ? "<strong>" + options.title + "</strong>" : '') + (options.message ? "<p>" + options.message + "</p>" : ''),
		"setStyles":{
			"opacity":0,
			"display":"none",
			"position":"absolute",
			"z-index":99,
			"width":options.width || 300,
			"background-color":options.bg || "#000",
			"color":"#fff"
		}
	}).inject(document.body,"top");
	
	// add events
	element.addEvents({
		"mouseover":show,
		"mouseout":hide,
		"mousemove":follow
	});
	
	// show the tip
	function show() {
		tip.setStyle("display","block").fade("in",{
			max:options.alpha || 85
		});
	}
	
	// hide the tip
	function hide() {
		tip.fade("out",{
			complete:function(el) {
				el.setStyle("display","none");
			}
		});
	}
	
	// follow the cursor
	function follow(e) {

		// get position
		var top = (e.pageY ? e.pageY : e.clientY + document.body.getScroll().top) - tip.getSize().height - 15;
		var left = (e.pageX ? e.pageX : e.clientX + document.body.getScroll().left) + 15;

		// fix position
		if(top < 0) {
			top = (e.pageY ? e.pageY : e.clientY) + 15;
		}
		if((left + tip.getSize().width) > document.body.getSize().width) {
			left = (e.pageX ? e.pageX : e.clientX) - tip.getSize().width - 15;
		}

		// set position, finally
		tip.setStyles({
			"top":top,
			"left":left
		});
		
	}
	
}


// 
// growl effect
// thanks for reference digitarald and Growl
//
var Growl = {
	
	growls:new Array(),
	
	smoke:function(options) {
		
		options = options || {};
		
		// make an id for it; variable for height
		var id = "smoke_" + $rand(0,999999), h = 0;
		
		// set the height
		this.growls.each(function(el) {
			h += el.getSize().height;
		});
		
		// set up html for the smoke
		var html = '';
		html += (options.close ? "<div class=\"close\" style=\"display:none;\" onclick=\"Growl.close('" + id + "');\"></div>" : '');
		html += (options.image ? "<div class=\"image\" style=\"float:left;\"><img src=\"" + options.image + "\" /></div>" : '');
		html += "<div class=\"message\" style=\"float:left;\"><strong>" + (options.title || '') + "</strong><p>" + (options.message || '') + "</p></div>";
		html += "<div style=\"clear:both;\"></div>";
		
		// create the element and effects and put it in array
		this.growls.push(new Element("div",{
			"id":id,
			"class":"smoke" + (options.klass ? " smoke_" + options.klass : ''),
			"html":html,
			"setStyles":{
				"opacity":0,
				"position":"absolute",
				"z-index":99,
				"width":options.width || 300,
				"top":document.body.getScroll().top + h,
				"right":0
			}
		}).addEvents({
			mouseover:function() {
				var c = this.getElement("div.close");
				if(c) { c.setStyle("display","block"); }
			},
			mouseout:function() {
				var c = this.getElement("div.close");
				if(c) { c.setStyle("display","none"); }
			}
		})._set("duration",options.duration || 2000));
		
		this.add("smoke");
		
	},
	
	bezel:function(options) {
		
		options = options || {};
		
		// set up html for the bezel
		var html = '';
		html += (options.image ? "<img src=\"" + options.image + "\" /><br />" : '');
		html += "<div class=\"message\"><strong>" + (options.title || '') + "</strong><p>" + (options.message || '') + "</p></div>";
		
		// create the element and effects and put it in array
		this.growls.push(new Element("div",{
			"class":"bezel" + (options.klass ? " bezel_" + options.klass : ''),
			"html":html,
			"setStyles":{
				"position":"absolute",
				"z-index":99,
				"width":options.width || 300,
				"height":options.height || "auto",
				"top":document.body.getScroll().top + 300,
				"left":"50%",
				"opacity":0
			}
		})._set("duration",options.duration || 2000));
		
		this.add("bezel");
		
	},
	
	mv:function(options) {
		
		options = options || {};
		
		// set up html for the mv
		var html = '';
		html += (options.image ? "<div class=\"image\"><img src=\"" + options.image + "\" /></div>" : '');
		html += "<div class=\"message\"><strong>" + (options.title || '') + "</strong><p>" + (options.message || '') + "</p></div>";
		html += "<div style=\"clear:both;\"></div>";
		
		// create the element and effects and put it in array
		this.growls.push(new Element("div",{
			"class":"mv" + (options.klass ? " mv_" + options.klass : ''),
			"html":html,
			"setStyles":{
				"position":"absolute",
				"z-index":99,
				"width":"100%",
				"top":0,
				"left":0
			}
		})._set("duration",options.duration || 2000));
		
		this.add("mv");
		
	},
	
	doScroll:function() {
		var h = 0, m = 0;
		Growl.growls.each(function(el,i) {
			m = el.getStyle("margin");
			h += (i > 0 ? el.getSize().height + (m != '' ? m.toInt() : 0) : 0);
			el.setStyle((el.hasClass("bezel") ? "margin-" : '') + "top",document.body.getScroll().top + h);
		});
	},
	
	addScroll:function() {
		window.addEvent("scroll",this.doScroll);
	},
	
	removeScroll:function() {
		if(this.growls.length > 0) { 
			this.growls.shift().destroy();
		} else {
			window.removeEvent("scroll",this.doScroll);
		}
	},
	
	add:function(what) {
		
		// add scroll event
		if(this.growls.length == 0) {
			return;
		} else if(this.growls.length == 1) {
			this.addScroll();
		}
		
		// acts as parent; get the first element, then put it back
		var p = this, el = this.growls.pop();
		this.growls.push(el);
		
		// only smoke can have more than one at a time
		if(what == "bezel") {
			if($$("div.bezel") != null) { return; }
		} else if(what == "mv") {
			if($$("div.mv") != null) { return; }
		}
		
		// place in body at the top
		el.inject(document.body,"top");
	
		if(what == "mv") {
			
			el.setStyle("visibility","hidden").hide({
				complete:function() {
					el.setStyle("visibility","visible").slide("in",{
						complete:function() {
							setTimeout(function() {
								el.slide("out",{
									complete:function() {
										p.removeScroll();
										p.add(what);
									}
								});
							},el._get("duration"));
						}
					});
				}
			});
			
		} else {
			
			el.fade("in",{
				max:70,
				complete:function() {
					setTimeout(function() {
						el.fade("out",{
							complete:function() {
								p.removeScroll();
								if(what == "bezel") {
									p.add(what);
								}
							}
						});
					},el._get("duration"));
				}
			});
			
			// makes it centered
			if(what == "bezel") {
				el.setStyle("margin-left",-(el.getSize().width / 2));
			}
			
		}
		
	},
	
	close:function(id) {
		var p = this;
		this.growls.each(function(el,i) {
			if(el.get("id") == id) {
				el.fade("out",{
					complete:function(e) {
						p.growls.splice(i,1);
						e.destroy();
					}
				});
			}
		});
	}
	
};


// 
// Table Sorter
// huge thanks to leigeber
//
var TableSorter = function(id,options) {
	
	options = options || {};
	
	var element = $(id);	
	var tbody = element.getElement("tbody");
	var rows = element.getElementsByTagName("TR"); // we don't want to extend the TRs with GOOPs elements
	var count = rows.length;
	
	var cell = new Array();
	var columns = 0;
	
	// set up headers
	for(var i = 0; i < count; i++) {
		if(i == 0) {
			var c = GOOP.extend(rows[i].cells);
			var columns = c.length;
			for(var j = 0; j < columns; j++) {
				if(!c[j].hasClass("tablesorter_nosort")) {
					c[j].addClass("tablesorter_head");
					c[j].addEvent("click",sort);
				}
			}
		} else {
			cell[i - 1] = {};
		}
	}
	
	// sort the table
	function sort() {

		for(var i = 0; i < count - 1; i++) {
			var v = rows[i + 1].cells[this.cellIndex]
			while(v.hasChildNodes()) v = v.firstChild;
			cell[i].value = (v != null ? v.nodeValue : '');
			cell[i].o = i + 1;
		}
		
		for(var i = 0; i < columns; i++) {
			var c = rows[0].cells[i];
			if(!c.hasClass("tablesorter_nosort")) {
				c.addClass("tablesorter_head");
			}
		}
		
		// desc or asc, lets remember
		if(!element.tablesorter_reverse) {
			element.tablesorter_reverse = new Array();
		}

		if(element.tablesorter_lastcolumn == this.cellIndex) {
			element.tablesorter_reverse[this.cellIndex] = !element.tablesorter_reverse[this.cellIndex];
		}
		
		element.tablesorter_lastcolumn = this.cellIndex;
		
		if(element.tablesorter_reverse[this.cellIndex]) {
			cell.reverse(compare);
		} else {
			cell.sort(compare);
		}
		
		// create a new tbody
		var tb = new Element("tbody");
		tb.adopt(rows[0]);
		for(var i = 0; i < count; i++) {
			if(rows[i]) {
				tb.adopt(rows[cell[i].o - 1].cloneNode(true));
			}
		}
		tb.replaces(tbody);
		tbody = tb;

	}
	
	function compare(f,c) {
		f = f.value;
		c = c.value;
        var i = f.replace(/(\$|\,)/g, '').toFloat();
		var n = c.replace(/(\$|\,)/g, '').toFloat();
        if (!isNaN(i) && !isNaN(n)) {
            f = i;
            c = n;
        }
        return (f > c ? 1 : (f < c ? -1 : 0))
    }
	
};


// 
// tab slider
// thank you for the idea creative pony and panic
//
var SlideTabs = function(id,options) {
	
	options = options || {};
	options.slide = (options.slide == undefined ? true : options.slide);
	
	var element = $(id);
	var tabs = element.getElements("ul li");
	var pans = element.getElements("div.tab");
	var count = tabs.length;
	var current_pan_index;

	// get width, include the padding if any
	var pans_width = options.width || pans[0].getSize().width;
	pans_width = (options.wholebody ? document.body.getSize().width : pans_width);
	var wrapper_width = pans_width * count;
	
	
	// set the header tab index and click event
	tabs.each(function(el,i) {
		el._set("slidetab_index",i);
		el.addEvent("click",slide);
	});
	
	// set the width for the pans
	pans.each(function(el) {
		el.setStyles({
			"width":pans_width,
			"float":"left"
		});
	});
	
	// make a container
	var container = new Element("div",{
		"id":"slidetab_container",
		"setStyles":{
			"overflow":"hidden",
			"width":pans_width
		}
	});
	
	// now make a wrapper
	var wrapper = new Element("div",{
		"id":"slidetab_wrapper",
		"setStyles":{
			"margin-left":0,
			"width":wrapper_width
		}
	});
	
	// put the pans in the wrapper, then the wrapper in the container
	wrapper.wraps(pans);
	container.wraps(wrapper);

	// adjust the onreize for the window, if we sent the option to do so... this is only necessary if it strecths the whole window
	if(options.wholebody) {
		window.addEvent("resize",fixpans);
		window.doEvent("resize");
	}
	
	
	// slide the wrapper
	function slide() {
		
		var p = this;
		tabs.each(function(el) {
			if(el == p) {
				el.addClass("current");
			} else {
				el.removeClass("current");
			}
		});
		
		var next = -(this._get("slidetab_index") * pans_width);
		current_pan_index = this._get("slidetab_index");
		
		if(options.slide) {
			
			var current = wrapper.getStyle("margin-left").toInt();
		
			wrapper.tween("margin-left",current,next,{
				duration:500
			});
			
		} else {
			wrapper.setStyle("margin-left",next);
		}
		
	}
	
	// fix the pans when resize
	function fixpans() {
		
		// get new width
		pans_width = document.body.getSize().width;
		wrapper_width = pans_width * count;
		
		// reset the width for the pans
		pans.each(function(el) {
			el.setStyles({
				"width":pans_width,
				"float":"left"
			});
		});		
		
		// adjust the wrapper and container width
		container.setStyle("width",pans_width)
		wrapper.setStyle("width",wrapper_width);
		wrapper.setStyle("margin-left",-(current_pan_index * pans_width));
		
	}
	
};


// 
// wysiwyg editor
// 
var Wysiwyg = function(id,options) {
	
	options = options || {};
	
	var p = this; // acts as parent
	var element = $(id).setStyle("display","none");
	this.element = element; // save for prototype reference
	this.iframe; // saved for prototype reference, will contain the iframe
	var doc; // will contain the iframe document
	
	// button commands
	var buttons = {
		strong:["text_bold.png","bold",null],
		em:["text_italic.png","italic",null],
		u:["text_underline.png","underline",null],
		left:["text_align_left.png","justifyleft",null],
		center:["text_align_center.png","justifycenter",null],
		right:["text_align_right.png","justifyright",null],
		indent:["text_indent.png","indent",null],
		outdent:["text_indent_remove.png","outdent",null],
		h1:["text_heading_1.png","formatblock","<H1>"],
		h2:["text_heading_2.png","formatblock","<H2>"],
		h3:["text_heading_3.png","formatblock","<H3>"],
		p:["text_paragraph.png","formatblock","<P>"],
		ul:["text_list_bullets.png","insertunorderedlist",null],
		ol:["text_list_numbers.png","insertorderedlist",null],
		img:["image.png","insertimage","Type the URL","http://"],
		link:["link.png","createlink","Type the URL to the Image","http://"],
		unlink:["link_break.png","unlink",null]
	};
		
	// make the area to wrapper
	var wrapper = new Element("div",{
		"id":"wysiwyg_wrapper",
		"setStyles":{
			"width":options.width || element.getSize().width || 600
		}
	});
	
	// tools for wysiwyg
	var tools = new Element("div",{
		"class":"wysiwyg_tools",
		"setStyles":{
			"width":"100%"
		}
	});

	// iframe to type in
	var iframe = new Element("iframe",{
		"setStyles":{
			"width":"100%",
			"height":options.height || element.getSize().height || 450
		}
	}).addEvent("load",function() {
		doc = this.contentWindow.document;
		doc.designMode = "on";
		doc.body.innerHTML = p.element.value;
	});
	this.iframe = iframe;
	
	// setup buttons in wysiwyg_tools
	(options.buttons || ["strong","em","u",null,"left","center","right","indent","outdent",null,"h1","h2","h3","p","ul","ol",null,"img","link","unlink"]).each(function(b,i) {
		if(b == null) {
			new Element("div",{ "class":"spacer" }).inject(tools);
		} else {
			new Element("img",{ 
				"class":"btn",
				"src":(options.path || "wysiwyg/") + buttons[b][0],
				"alt":b
			}).addEvent("click",exec)._set("wysiwyg_btn",b).inject(tools);
		}
	});
	
	// add the wysiwyg wrapper, tools, and iframe
	wrapper.inject(element,"before");
	wrapper.adopt([tools,iframe]);
	
	function exec() {
		
		var b = buttons[this._get("wysiwyg_btn")];
		if(b[1] == "insertimage" || b[1] == "createlink") {
			var v = prompt(b[2],b[3]);
			if(v) { 
				doc.execCommand(b[1],false,v);
			}
		} else {
			doc.execCommand(b[1],false,b[2]);
		}
		
	}
	
};
Wysiwyg.prototype.toTextarea = function() {
	this.element.value = this.iframe.contentWindow.document.body.innerHTML;
}



//
// autocomplete
//
var AutoComplete = function(id,options) {
	
	options = options || {};
	if(!options.path) { return; }
	
	var element = $(id);
	var ajax = new Ajax({
		url:options.path,
		cache:false,
		complete:didSearch
	});
	
	// make the ul tag
	var ul = new Element("ul",{
		"class":"autocomplete",
		"setStyles":{
			"display":"none",
			"position":"absolute",
			"z-index":99
		}
	}).inject(document.body,"top");
	
	// add event to element
	element.addEvents({
		"keyup":doSearch,
		"blur":hideSearch
	});
	
	function hideSearch() {
		setTimeout(function() {
			ul.setStyle("display","none");
		},500);
	}
	
	function doSearch() {
		ul.setStyles({
			"top":element.getPosition().top + element.getSize().height,
			"left":element.getPosition().left
		});
		ajax.send("value=" + this.value);
	}
	
	function didSearch(html) {
		
		// empty all children inside of ul
		//ul.set("html",'');
		ul.empty();
		
		var results = html.toJSON();

		var c = 0;
		for(var i in results) {
			c++;
			new Element("li",{
				"addEvents":{
					mouseover:function() { this.addClass("over"); },
					mouseout:function() { this.removeClass("over"); }
				}
			}).set("html","<a href=\"" + results[i] + "\">" + i + "</a>").inject(ul);
		}
		
		ul.setStyle("display",(c == 0 ? "none" : ''));
		
	}
	
}


//
// Validate
// idea from leigeber
//
var Validate = {
	
	move:true, // true to move window to error, false otherwise
	offset_x:10,
	offset_y:5,
	duration:2500, // how long before it fades out
	
	// legal characers only
	legal:function(input,msg) {
		var value, filter, flag;
		value = (typeof input == "object" ? input.value : input).trim();
		filter = /\W/;
		flag = (value != '' ? !filter.test(value) : false);
		if(!flag && typeof input == "object") {
			this.alert(input,msg || "Invalid Characters");
		}
		return flag;
	},

	// validate email - sample@sample.com
	email:function(input,msg) {
		var value, filter, flag;
		value = (typeof input == "object" ? input.value : input).trim();
		filter = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
		flag = filter.test(value);
		if(!flag && typeof input == "object") {
			this.alert(input,msg || "Invalid Email");
		}
		return flag;
	},
	
	// phone - (123) 456-7890
	phone:function(input,msg) {
		var value, filter, flag;
		value = (typeof input == "object" ? input.value : input).trim();
		filter = /^\([1-9]\d{2}\)\s?\d{3}\-\d{4}$/;
		flag = filter.test(value);
		if(!flag && typeof input == "object") {
			this.alert(input,msg || "Invalid Phone");
		}
		return flag;
	},
	
	// number
	number:function(input,msg) {
		var value, filter, flag;
		value = (typeof input == "object" ? input.value : input).trim();
		filter = /(^-?\d\d*\.\d*$)|(^-?\d\d*$)|(^-?\.\d\d*$)/;
		flag = filter.test(value);
		if(!flag && typeof input == "object") {
			this.alert(input,msg || "Invalid Number");
		}
		return flag;
	},
	
	// letters only
	letters:function(input,msg) {
		var value, filter, flag;
		value = (typeof input == "object" ? input.value : input).trim();
		filter = /^[a-zA-Z]+$/;
		flag = filter.test(value);
		if(!flag && typeof input == "object") {
			this.alert(input,msg || "Characters Only");
		}
		return flag;
	},
	
	// zip - 12345 or 12345-1234
	zip:function(input,msg) {
		var value, filter, flag;
		value = (typeof input == "object" ? input.value : input).trim();
		filter = /\d{5}(-\d{4})?/;
		flag = filter.test(value);
		if(!flag && typeof input == "object") {
			this.alert(input,msg || "Invalid Zip");
		}
		return flag;
	},
	
	// date - MM/DD/YYYY
	date:function(input,msg) {
		var value, filter, flag;
		value = (typeof input == "object" ? input.value : input).trim();
		filter = /^([1-9]|0[1-9]|1[012])\D([1-9]|0[1-9]|[12][0-9]|3[01])\D(19[0-9][0-9]|20[0-9][0-9])$/;
		flag = filter.test(value);
		if(!flag && typeof input == "object") {
			this.alert(input,msg || "Invalid Date");
		}
		return flag;
	},
	
	// url - http://www.sample.com or www.sample.com
	url:function(input,msg) {
		var value, filter, flag;
		value = (typeof input == "object" ? input.value : input).trim();
		filter = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
		flag = filter.test(value);
		if(!flag && typeof input == "object") {
			this.alert(input,msg || "Invalid URL");
		}
		return flag;
	},
	
	// extension - default: jpeg, jpg, gif, png, swf ... pass an array to match your own
	ext:function(input,msg,ex) {
		var value, flag;
		ex = ex || ["jpeg","jpg","gif","png","swf"];
		value = (typeof input == "object" ? input.value : input).trim();
		value = value.substring(value.lastIndexOf(".") + 1).toLowerCase();
		flag = false;
		ex.each(function(e) {
			if(e == value) {
				flag = true;
			}	
		});
		if(!flag && typeof input == "object") {
			this.alert(input,msg || "Invalid Extension Type");
		}
		return flag;
	},
	
	// determines if the input field is empty
	empty:function(input,msg) {
		var value, flag;
		if(typeof input == "object") {
			if(input.tagName == "SELECT") {
				value = input.options[input.selectedIndex].value.trim();
			} else if(input.tagName == "INPUT" || input.tagName == "TEXTAREA") {
				value = input.value.trim();
			} else if(input[0] && input.tagName == undefined) {
				value = ''; // lets assume its not checked
				for(var i = 0; i < input.length; i++) {
					if(input[i].checked) {
						value = "checked"; // value is set to something..
						break;
					}
				}				
				input = input[0]; // for the error message, if any
			}
		} else {
			value = input.trim();
		}
		flag = value != '';
		if(!flag && typeof input == "object") {
			this.alert(input,msg || "Can not be empty");
		}
		return !flag;
	},
	
	// alerts the warning
	alert:function(element,msg) {
		
		if(!element || !msg || typeof element != "object") { return; }
		
		// acts as parent
		var p = this;
		
		// extend the element first, then we can get the dimensions
		GOOP.extend(element,["dimensions"]);
		var pos = element.getCoordinates();
		
		// wrapper
		new Element("div", {
			"html":msg,
			"class":"validate",
			"setStyles":{
				"position":"absolute",
				"z-index":99,
				"opacity":0,
				"top":pos.top - this.offset_y,
				"left":pos.left + pos.width + this.offset_x
			}
		}).inject(document.body,"top").fade("in",{
			complete:function(el) {
				setTimeout(function() {
					el.fade("out", {
						complete:function(el) {
							el.destroy();
						}
					});
				}, p.duration);
			}
		});
		
		// move to element
		if(this.move) {
			window.scrollTo(pos.left, pos.top);
		}
		
	}
	
};


//
// date picker
// reference from jszen.blogspot.com
//
var DatePicker = {
	
	element:null,
	
	//
	picked:function(year,month,day) {
		if(this.element == null || !year || !month || !day) { return; }
		month++;
		this.element.value = year + "-" + (month < 10 ? "0" : '') + month + "-" + (day < 10 ? "0" : '') + day;
		this.close();
	},
	
	// 
	changeDate:function(y,m) {
		if(m < 0) { y--; m = 11; }
		if(m > 11) { y++; m = 0; }
		this.close();
		this.open(this.element,{year:y,month:m});
	},

	// open the datepicker
	open:function(element,options) {

		this.element = (element || this.element);
		if(!this.element) { return; }
		if($("datepicker") != null) { return; }
		
		options = options || {};
		
		var date = new Date();
		var month = (options.month != undefined ? options.month : date.getMonth());
		var year = (options.year != undefined ? options.year : date.getFullYear());
		
		var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
		var months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
		var days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

		var first_day = new Date(year, month, 1);
		var start_day = first_day.getDay();
		var month_length = days_in_month[month];
		if(month == 1) {
			if((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
				month_length++;
			}
		}

		// 
		var html = '';
		html += "<table class=\"datepicker\">";

		// close
		html += "<tr><td align=\"right\" colspan=\"7\"><a href=\"javascript:DatePicker.close();\" class=\"close\">Close</a></td></tr>";

		// month and year
		html += "<tr><th colspan=\"7\">" + months[month] + " " + year + "</th></tr>";
		
		// options
		html += "<tr class=\"datepicker_options\">";
		html += "<td align=\"center\"><a href=\"javascript:DatePicker.changeDate(" + (year - 1) + "," + month + ");\"><<</a></td>";
		html += "<td align=\"center\"><a href=\"javascript:DatePicker.changeDate(" + year + "," + (month - 1) + ");\"><</a></td>";
		html += "<td align=\"center\" colspan=\"3\"><a href=\"javascript:DatePicker.changeDate(" + date.getFullYear() + "," + date.getMonth() + ");\">Today</a></td>";
		html += "<td align=\"center\"><a href=\"javascript:DatePicker.changeDate(" + year + "," + (month + 1) + ");\">></a></td>";
		html += "<td align=\"center\"><a href=\"javascript:DatePicker.changeDate(" + (year + 1) + "," + month + ");\">>></a></td>";
		html += "</tr>";

		// days of week
		html += "<tr class=\"datepicker_header\">";
		for(var i = 0; i < 7; i++) {
			html += "<td>" + days[i] + "</td>";
		}
		html += "</tr>";

		// days of month
		var day = 1;
		html += "<tr>";
		for(var i = 0; i < 9; i++) {
			for(var j = 0; j < 7; j++) {
				html += "<td class=\"day\" onclick=\"DatePicker.picked(" + year + "," + month + "," + day + ");\">";
				if(day <= month_length && (i > 0 || j >= start_day)) {
					
					if(year == date.getFullYear() && month == date.getMonth() && day == date.getDate()) {
						html += "<strong>" + day + "</strong>";
					} else {
						html += day;
					}
					
					day++;
				}
				html += "</td>";
			}
			if(day > month_length) {
				break;
			} else {
				html += "</tr><tr>";
			}
		}

		// done
		html += "</tr></table>";

		// extend the element, then we can get the dimensions
		GOOP.extend(this.element,["dimensions"]);
		var pos = this.element.getCoordinates();

		// wrapper
		new Element("div",{
			"html":html,
			"id":"datepicker",
			"setStyles":{
				"position":"absolute",
				"z-index":99,
				"top":pos.top,
				"left":pos.left + pos.width + 10
			}
		}).inject(document.body,"top");
		
	},
	
	// closes the datepicker
	close:function() {
		var dp = $("datepicker");
		if(dp != null) {
			dp.destroy();
		}
	}
	
};


//
// extended prototypes
// thank you MooTools
//

// array
Array.prototype.each = function(ftn) {
	var l = this.length, i;
	for(i = 0; i < l; i++) {
		ftn(this[i],i);
	}
}
Array.prototype.indexOf = function(search) {
	for(var i in this) {
		if(this[i] == search) {
			return i;
		}
	}
	return -1;
}

// numbers and string
String.prototype.toInt = function(base) {
	return parseInt(this, base || 10);
}
Number.prototype.toInt = function(base) {
	return parseInt(this, base || 10);
}
String.prototype.toFloat = function() {
	return parseFloat(this);
}
Number.prototype.toFloat = function() {
	return parseFloat(this);
}

String.prototype.trim = function() {
	return this.replace(/^\s+|\s+$/g, '');
}

String.prototype.camelCase = function() {
	return this.replace(/-\D/g, function(match) {
		return match.charAt(1).toUpperCase();
	});
}

String.prototype.toJSON = function(flag) {
	if(flag) {
		return '"' + this.replace(/[\x00-\x1f\\"]/g,{'\b':'\\b', '\t':'\\t', '\n':'\\n', '\f':'\\f', '\r':'\\r', '"':'\\"', '\\':'\\\\'}) + '"';
	} else {
		return eval("(" + this + ")");
	}
}


// 
// doesn't need to have the DOM to be ready
// 
GOOP.extend(window,["events"]);
Browser.init(); // detect browser