//
// create the GOOP Object
//
var GOOP = {
	version:.9,
	dom:false, // is the DOM ready?
	load:false // has the window loaded
};


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

//
// window onload, called automatically when the window has loaded
//
GOOP.onload = function() {
	GOOP.load = true;
	Reflect(window);
}


//
// extends the element(s) attaching the GOOP methods
//
GOOP.extend = function(element,methods) {
	
	// they didn't want it to be extended
	if(methods === false) 
		return element;
	
	// find out what methods to attach
	methods = methods || ['styles','events','dimensions','effects'];
	if(methods.indexOf('basic') == -1) 
		methods.unshift('basic');
	
	var l, i, m;
	
	// array of elements
	if(Object.prototype.toString.apply(element) === '[object Array]') {
		
		l = element.length;
		for(i = 0; i < l; i++)
			GOOP.extend(element[i],methods);
		
	// individual element
	} else {
		
		l = methods.length;
		for(i = 0; i < l; i++) {
			if(!element['goop_' + methods[i]]) {
				for(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,methods) {
		
		var search = search.split(' '),
			l = search.length, 
			context = [this],
			i, s, el, ell;
		
		for(i = 0; i < l; i++) {
			
			s = search[i];
			
			// ids
			if(s.indexOf('#') > -1) {
				el = $(s.split('#')[1],false);
				if(!el) return [];
				context = [el];
				continue;
			}
			
			// classes
			if(s.indexOf('.') > -1) {
				var cl = context.length,
				b = s.split('.'), 
				t = b[0] || '*', 
				c = b[1], 
				f = [], 
				j, k;
				
				for(j = 0; j < cl; j++) {
					el = context[j].getElementsByTagName(t.toUpperCase()), 
					ell = el.length;
					for(k = 0; k < ell; k++) 
						f.push(el[k]);
				}
				
				context = [],
				fl = f.length;
				for(j = 0; j < fl; 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, 
				cl = context.length,
				f = [], 
				j, k;
				
				for(j = 0; j < cl; j++) {
					el = context[j].getElementsByTagName(t.toUpperCase()),
					ell = el.length;
					for(k = 0; k < ell; 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
				if(ao == '=')
					// equality
					ftn = function(e) {
						return e.getAttribute(an) == av;
					};
				else if(ao == '~') 
					// match one of the space seperated words
					ftn = function(e) {
						return e.getAttribute(an).match(new RegExp('\\b' + av + '\\b'));
					};
				else if(ao == '|')
					// match start with value followed by options hyphen
					ftn = function(e) {
						return e.getAttribute(an).match(new RegExp('^' + av + '-?'));
					}
				else if(ao == '^')
					// match starts with
					ftn = function(e) {
						return e.getAttribute(an).indexOf(av) == 0;
					}
				else if(ao == '$') 
					// match ends with value - fails with warning in Opera 7
					ftn = function(e) {
						return e.getAttribute(an).lastIndexOf(av) == e.getAttribute(an).length - av.length;
					}
				else if(ao == '*') 
					// match ends with value
					ftn = function(e) {
						return e.getAttribute(an).indexOf(av) > -1;
					}
				else 
					// just test for existence of attribute
					ftn = function(e) {
						return e.getAttribute(an);
					}
					
				context = [],
				fl = f.length;
				for(j = 0; j < fl; j++) {
					if(!f[j].getAttribute(an)) 
						continue;
					if(ftn(f[j])) 
						context.push(f[j]);
				}
				continue;
			}
			
			// if we made it here, its just a tag name
			var cl = context.length,
			f = [], 
			j, k;
			for(j = 0; j < cl; j++) {
				el = context[j].getElementsByTagName(s.toUpperCase()),
				ell = el.length;
				for(k = 0; k < ell; k++) 
					f.push(el[k]);
			}
			context = f;
			
		}
		
		return context.length > 0 ? GOOP.extend(context,methods) : null;
		
	},

	getElement:function(search,methods) {
		var elements = this.getElements(search,methods);
		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]);
			return;
		}

		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.hasOwnProperty && this.hasOwnProperty(attr)) ? this[attr] = value : this.setAttribute(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')
			return this.innerText ? this.innerText : this.textContent;
		else
			return (this.hasOwnProperty && this.hasOwnProperty(attr)) ? this[attr] : this.getAttribute(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 i,
				l = element.length;
			for(i = 0; i < l; i++)
				this.adopt(element[i]);
		} 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 i,
				l = element.length;
			for(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);
		return this;
	},
	
	
	//
	// set and get misc properties and values in storage
	// thank you MooTools
	//
	_set:function(property,value) {
		this.storage = this.storage || [];
		
		if(typeof property != 'string') {
			for(var v in property)
				this._set(v,property[v]);
			return;
		}
		
		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 s in styles) 
			this.setStyle(s,styles[s]);
		return this;
	},

	getStyle:function(style) {

		if(style == 'opacity') {
			if(this._get('opacity') === 0) 
				return 0;
			if(Browser.name == 'Explorer') 
				return this._get('opacity') || this.currentStyle[style.camelCase()] || this.style.filter;
			else 
				return this._get('opacity') || document.defaultView.getComputedStyle(this,'').getPropertyValue(style) || this.style.opacity;
		} else if(style == 'float') {
			return Browser.name == 'Explorer' ? this.style.styleFloat : this.style.cssFloat;
		} else {
			if(Browser.name == 'Explorer')
				return this.style[style.camelCase()] || this.currentStyle[style.camelCase()];
			else
				return this.style[style.camelCase()] || document.defaultView.getComputedStyle(this,'').getPropertyValue(style);
		}

	},
	
	getStyles:function(styles) {
		var returned = {}, 
			sl = styles.length,
			i;
		for(i = 0; i < sl; i++)
			returned[styles[i]] = this.getStyle(styles[i]);
		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) {
			if(this._get('event_' + type)) 
				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(e,events[e]);
		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) {
			if(type === 'resize') 
				this._get('event_' + type)();
			else 
				this.fireEvent('on' + type,document.createEventObject());
		} else
			this['on' + type]();
		return this;
	},

	doEvents:function(events) {
		var i,
			l = events.length;
		for(i = 0; i < l; i++)
			this.doEvent(events[i]);
		return this;
	},
	
	// used if mouseenter and mouseleave events are being used
	_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() {
		if(/body/i.test(this.tagName)) {
			var b = document.body,
				e = document.documentElement;
			return {
					width:self.innerWidth || document.documentElement.clientWidth,
					height:self.innerHeight || document.documentElement.clientHeight,
					real_width:Math.max(Math.max(b.scrollWidth,e.scrollWidth),Math.max(b.clientWidth,e.clientWidth)), 
					real_height:Math.max(Math.max(b.scrollHeight,e.scrollHeight),Math.max(b.clientHeight,e.clientHeight))
					};
		} else 
			return {width:this.offsetWidth, height:this.offsetHeight};
	},

	getScroll:function() {
		if(/body|html/i.test(this.tagName))
			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(), 
			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};
	},
	
	// thank you - http://snippets.dzone.com/posts/show/913
	hitTest:function(el) {
		var a = this.getCoordinates(), 
		b = $(el,['dimensions']).getCoordinates();
		return ((a.left == b.left || (a.left > b.left ? a.left <= b.right : b.left <= a.right)) && (a.top == b.top || (a.top > b.top ? a.top <= b.bottom : b.top <= a.bottom)));
	}
	
};


// 
// 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.opacity || 100)),(action == 'out' ? 0 : (options.opacity || 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 p = this, // acts as parent
			interval = Math.ceil(1000 / (options.fps || 50)),
			tf = Math.ceil((options.duration || 500) / interval),
			step,
			flag; 
		p.started = options.start || function() {}; // function to execute at the start
		p.completed = options.complete || function() {}; // function to execute once completed
		
		// for color, background-color, and border-color - the transition is a bit different
		if(style == 'color' || style == 'background-color' || style == 'border-color')
			var brgb = Color.toRGB(from), ergb = Color.toRGB(to),
				step = [(ergb[0] - brgb[0]) / tf, (ergb[1] - brgb[1]) / tf, (ergb[2] - brgb[2]) / tf, 0];
		else
			step = (to.toInt() - from.toInt()) / tf;
	
		// make a tweentimer array
		if(!this.tweentimer) 
			this.tweentimer = [];
	
		// execute at start
		this.started();
	
		// firstly, stop the timer of that tweened style so we don't leak memory, then set the style
		this.stopTween(style).setStyle(style,from).tweentimer[style] = $interval(interval,function() {
			
			flag = false;
			
			// for color, background-color, and border-color
			if(style == 'color' || style == 'background-color' || style == 'border-color') {
				
				brgb[0] += step[0], 
				brgb[1] += step[1], 
				brgb[2] += step[2];
				
				p.setStyle(style,Color.toHEX(brgb));
				
				// are we done with the colors?
				if((brgb[0] + step[0]) >= ergb[0] && (brgb[1] + step[1]) >= ergb[1] && (brgb[2] + step[2]) >= ergb[2] && !flag) {
					if(step[3] == 2) 
						flag = true;
					step[3] = 1;
				} else if((brgb[0] + step[0]) <= ergb[0] && (brgb[1] + step[1]) <= ergb[1] && (brgb[2] + step[2]) <= ergb[2] && !flag) {
					if(step[3] == 1) 
						flag = true;
					step[3] = 2;
				}
				
				if(flag) {
					p.setStyle(style,to);
					p.stopTween(style);
				}
				
			} else {
				
				// get position
				var pos = p.getStyle(style).toFloat();
				
				// move it
				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);
					flag = true;
				}
				
			}
			
			// done moving
			if(flag)
				if(!p.anyTweens())
					p.completed(); // we do this so in the complete function we can use "this" or the passed variable/object
		});
	
		return this;
	},

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

	stopTweens:function(styles) {
		if(styles == "all") {
			var i;
			for(i in this.tweentimer) 
				this.stopTween(i);
		} else
			for(i = 0; i < styles.length; i++)
				this.stopTween(styles[i]);
		return this;
	},

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


// 
// global functions
//
function $(id,methods) {
	
	var el, i;
	
	if(typeof id == 'string') {
		
		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(i = 0; i < document.all[id].length; i++) {
					if(document.all[id][i].id == id)
						el = document.all[id][i];
				}
			}
		} 
		
		if(el != null) 
			GOOP.extend(el,methods);
			
	} else if(typeof id == 'object')
		el = GOOP.extend(id,methods);
	
	return el;
}

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

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

function $rand(n1,n2,w) {
	var n = (n2 * Math.random()) + n1;
	return !w ? Math.ceil(n) : n;
}

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);
}
function $clear(t) {
	clearTimeout(t);
	clearInterval(t);
}

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();
}


// huge thanks to supersleight
var fixPNG = {
	
	path:'transparent.gif',
	active:true,
	_class:[],
	_id:[],
	
	init:function(element,mode) {		
		element = element || document;
		if(Browser.name != 'Explorer') 
			return;
		if(Browser.version > 6) 
			return;
			
		var i, j, el, flag;
		
		for(i = element.all.length - 1, el = null; el = element.all[i]; i--) {
			
			flag = false;
			
			// ignores
			for(j = 0; j < this._class.length; j++) {
				if(el.className.match(new RegExp('(^|\\s)' + this._class[j] + '(?:\\s|$)')) != null) 
					flag = true;
			}
			for(j = 0; j < this._id.length; j++) {
				if(el.id == this._id[j]) 
					flag = true;
			}
			if(flag) continue;
			
			// 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,
			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
//
function Element(tag,options,methods) {
	
	options = options || {};
	
	var el = document.createElement(tag.toUpperCase()),
		op;
	GOOP.extend(el,methods);
	
	for(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
//
function SWF(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', 
		p;
	for(p in options.properties) 
		swf += ' ' + p + '="' + options.properties[p] + '"'; 
	swf += ' />';

	for(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
//
function Event(e,w) {
	
	var code,
		page,
		client,
		wheel,
		rightClick,
		w = w || window;
	
	if(e.type.match(/key/i)) {
		code = e.which || e.keyCode;
	} else if(e.type.match(/(click|mouse|menu)/i)) {
		page = {
			x:(e.pageX) ? e.pageX : e.clientX + document.body.getScroll().left,
			y:(e.pageY) ? e.pageY : e.clientY + document.body.getScroll().top
		};
		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))
			wheel = (e.wheelDelta) ? el.wheelDelta / 120 : -(e.detail || 0) / 3;
		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
// thank you http://www.thefutureoftheweb.com/blog/adddomloadevent
//
var DOM = {
	ftns:[],
	ready:function(ftn) {
		this.ftns.push(ftn);
	},
	init:function() {
		while(DOM.ftns.length > 0) 
			(DOM.ftns.shift())();
	}
};
(function() {
	
	// mozilla
	if(document.addEventListener)
		document.addEventListener('DOMContentLoaded',DOM.init,false);
	
	// safari
	if(/khtml|webkit/i.test(navigator.userAgent)) {
		var setInt = $interval(10,function() {
			if(/loaded|complete/i.test(document.readyState)) {
				$clear(setInt);
				DOM.init();
			}
		});
	}
	
	// ie
	/*@cc_on @*/
	/*@if (@_win32 || @_win64) @*/
		document.write('<script id="__ie_domready__" defer src=//0><\/scr'+'ipt>');
		document.getElementById('__ie_domready__').onreadystatechange = function() {
			if(this.readyState == 'complete') 
				DOM.init();
		};
	/*@end @*/
	
})();


//
// simple ajax
// thank you XHConn
//
function Ajax(options) {

	options = options || {};
	if(!options.url) 
		return;

	options.start = options.start || function() {};
	options.complete = options.complete || function() {};
	options.error = options.error || function() {};
	options.method = (options.method || 'post').toUpperCase();

	// set up xmlttp
	var xmlhttp;
	try { 
		xmlhttp = new ActiveXObject('Msxml2.XMLHTTP'); 
	} catch(e) { 
		try { 
			xmlhttp = new ActiveXObject('Microsoft.XMLHTTP'); 
		} catch(e) { 
			try { 
				xmlhttp = new XMLHttpRequest(); 
			} catch(e) { 
				xmlhttp = false; 
			}
		}
	}

	// stops the call
	this.stop = function() {
		xmlhttp.abort();
	}

	// sends the request
	this.send = function(query) {

		if(!xmlhttp) 
			return;
		xmlhttp.abort();

		options.start();

		xmlhttp.onreadystatechange = function() {
			if(xmlhttp.readyState == 4) {
			    if(xmlhttp.status == 200) 
			        options.complete(xmlhttp.responseText.trim());
			    else 
			        options.error();
			}
		}

        // set up url,'ck' is for cachekiller
		var url = options.url + (options.url.indexOf('?') == -1 ? '?' : '&') + (options.method == 'GET' ? query || '' : '') + (options.cache === true ? '' : '&ck=' + $time());
		
		// open the request
		xmlhttp.open(options.method,url,true);

		// was it a get or post
		if(options.method == 'GET')
			xmlhttp.send(null);
		else {
			xmlhttp.setRequestHeader('Method','POST ' + url + ' HTTP/1.1');
			xmlhttp.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
			xmlhttp.send(query || '');
		}

	}
}


//
// 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(';'), 
			cal = ca.length,
			i, c;
		for(i = 0; i < cal; i++) {
			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);
	}
};


//
// color
// thank you leigeber and linuxtopia
//
var Color = {
	toRGB:function(c) {
		c = c.replace(/\#/,'').toString().toLowerCase();
		if(c.length == 3) 
			c = c.charAt(0) + c.charAt(0) + c.charAt(1) + c.charAt(1) + c.charAt(2) + c.charAt(2);
		return [ c.substring(0,2).toInt(16), c.substring(2,4).toInt(16), c.substring(4,6).toInt(16) ];
	},
	toHEX:function(c) {
		return this._findHEX(c[0]) + this._findHEX(c[1]) + this._findHEX(c[2]);
	},
	_findHEX:function(c) {
		if(c == undefined || c == null) 
			return '00';
		c = c.toInt();
		if(c == 0 || isNaN(c)) 
			return '00';
		c = Math.max(0,c);
		c = Math.min(c,255);
		c = Math.round(c);
		return '0123456789ABCDEF'.charAt((c - c % 16) / 16) + '0123456789ABCDEF'.charAt(c % 16);
	}
};


// 
// JSON encode and decode
// thank you - http://www.openjs.com/scripts/data/json_encode.php (their example doesn't work, had to alter it a bit)
//
var JSON = {
	toString:function(obj) {
		var parts = [], flag = (Object.prototype.toString.apply(obj) === '[object Array]'), key, value, str;

		for(key in obj) {
			value = obj[key];
			if(typeof value == 'function') 
				continue;
			if(typeof value == 'object') {
				parts.push('"' + key + '":' + JSON.toString(value));
			} else {
				str = '';
				if(!flag) str += '"' + key + '":'

				if(typeof value == 'number') 
					str += value;
				else if(typeof value == false) 
					str += 'false';
				else if(typeof value == true) 
					str += 'true';
				else 
					str += '"' + value + '"';
				parts.push(str);
			}
		}

		return flag ? '[' + parts.join(',') + ']' : '{' + parts.join(',') + '}';
	},
	toObject:function(str) {
		return eval('(' + str + ')');
	}
}


//
// 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, 
			dataString, 
			dataProp,
			i;
		for(i = 0; i < l; i++) {
			dataString = data[i].string;
			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
//
function Accordion(id,options) {
	
	options = options || {};
	
	// set variables
	var element = $(id),
		headers = element.getElements(options.header || 'dt'),
		sliders = element.getElements(options.slider || 'dd');
	
	// set sliders
	sliders.each(function(el) {
		el.setStyles({
			height:0,
			display:'none',
			overflow:'hidden'
		});
	});
	
	// set headers
	headers.each(function(el) {
		el.addEvent('click',slide);
	});
	
	function slide() {
		var hl = headers.length,
			el, i;
		for(i = 0; i < hl; i++) {
			
			el = sliders[i].stopTweens('all');

			if(headers[i] == this && el.getStyle('display') == 'none') {
				el.setStyle('display','');
				moving(el,true);
			} else if(el.getStyle('display') == '' || el.getStyle('display') == 'block') {
				moving(el,false);
			}
			
		}
	}
	
	function moving(el,flag) {
		
		// declare some variables
		var l = el.childNodes.length,
			h = 0, i;
		
		// we don't need to extend the childnodes with GOOP; just get the offsetHeight, if its NOT undefined
		for(i = 0; i < l; i++)
			h += el.childNodes[i].offsetHeight || 0;
		
		if(flag) 
			el.morph({ 
				height:[0,h], 
				opacity:[0,100] 
			},{ 
				duration:300 
			});
		else
			el.morph({
				height:[h,0],
				opacity:[100,0]
			},{
				duration:300,
				complete:function() {
					this.setStyle('display','none');
				}
			});
	}
	
	// automatically show
	if(options.open > -1) 
		move(options.open);
	
	// move
	this.move = function(n) {
		headers[n].doEvent('click');
	}	
};


//
// tips
// thanks for reference leigeber
//
function Tip(id,options) {
	
	options = options || {};
	var element = $(id),
		tip = new Element('div',{
			'class':'tip' + (options._class ? ' tip_' + options._class : ''),
			html:(options.title ? '<strong>' + options.title + '</strong>' : '') + (options.message ? '<p>' + options.message + '</p>' : ''),
			setStyles:{
				opacity:0,
				display:'none',
				position:'absolute',
				zIndex:99
			}
		}).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',{
			opacity:options.opacity || 85
		});
	}
	
	// hide the tip
	function hide() {
		tip.fade('out',{
			complete:function() {
				this.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,
			left = (e.pageX ? e.pageX : e.clientX + document.body.getScroll().left) + 15;

		// fix position so it doesn't go off the page :)
		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:[],
	
	smoke:function(options) {
		
		options = options || {};
		
		// make an id for it; variable for height
		var id = 'smoke_' + $time(), 
			html = '',
			h = 0;
		
		// set the height
		this.growls.each(function(el) {
			h += el.getSize().height;
		});
		
		// set up html for the smoke
		html += options.close ? '<div class="close" style="display:none;" onclick="Growl.close("' + id + '");"></div>' : '';
		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',{
			id:id,
			'class':'smoke' + (options._class ? ' smoke_' + options._class : ''),
			html:html,
			setStyles:{
				opacity:0,
				position:'absolute',
				zIndex:99,
				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)._set('growl_opacity',options.opacity || 100));
		
		this.add('smoke');
	},
	
	bezel:function(options) {
		
		options = options || {};
		
		// set up html for the bezel
		var 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._class ? ' bezel_' + options._class : ''),
			html:html,
			setStyles:{
				opacity:0,
				position:'absolute',
				zIndex:99,
				height:options.height || 'auto',
				top:document.body.getScroll().top + 300,
				left:'50%'
			}
		})._set('duration',options.duration || 2000)._set('growl_opacity',options.opacity || 100));
		
		this.add('bezel');
	},
	
	mv:function(options) {
		
		options = options || {};
		
		// set up html for the mv
		var 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._class ? ' mv_' + options._class : ''),
			html:html,
			setStyles:{
				opacity:options.opacity || 100,
				position:'absolute',
				zIndex:99,
				width:document.body.getSize().real_width,
				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() {
							$timeout(el._get('duration'),function() {
								el.slide('out',{
									complete:function() {
										p.removeScroll();
										p.add(what);
									}
								});
							});
						}
					});
				}
			});
			
		} else {
			
			el.fade('in',{
				opacity:el._get('growl_opacity'),
				complete:function() {
					$timeout(el._get('duration'),function() {
						el.fade('out',{
							opacity:el._get('growl_opacity'),
							complete:function() {
								p.removeScroll();
								if(what == 'bezel')	
									p.add(what);
							}
						});
					});
				}
			});
			
			// 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() {
						p.growls.splice(i,1);
						this.destroy();
					}
				});
			}
		});
	}
};


// 
// Table Sorter
// HUGE THANKS to leigeber - this is completely rewritten from his famous TINY Table to fit with GOOP... no credit here
//
function TableSorter(id,options) {
	
	options = options || {};
	
	var element = $(id),
		thead = element.getElement('thead',[]), 
		tbody = element.getElement('tbody',[]),
		rows = tbody.getElements('tr',['styles']), 
		rows_length = rows.length,
		cells = [], 
		col = -1, 
		order, 
		pp = options.perpage || 20, 
		pages_max = 1, 
		pages_current = 1, 
		i, el;
	
	// set up thead cells
	for(i = 0; i < thead.getElement('tr').cells.length; i++) {
		el = $(thead.getElement('tr').cells[i],['events','dimensions']);
		if(!el.hasClass('nosort')) 
			el.addEvent('click',sort);
	};
	
	// make the cell array with objects
	for(i = 0; i < rows_length; i++) 
		cells[i] = {};
	
	// start and complete functions
	element.started = options.start || function() {};
	element.completed = options.complete || function() {};
	
	// auto sort
	if(options.sort > -1) {
		thead.getElement('tr').cells[options.sort].doEvent('click');
	} else {
		if(options.paginate)
			perpage(pp);
	}
	
	// search ability?
	if(options.search) {
		
		var tr = new Element('tr',{'class':'tablesorter_search'},[]);
		tr.inject(thead);

		for(i = 0; i < thead.getElement('tr',false).cells.length; i++) {
			el = new Element('td',{'class':'tablesorter_search'},[]);
			if(options.search.indexOf(i) > -1) {
				new Element('input',{
					type:'text',
					setStyles:{
						width:thead.getElement('tr',false).cells[i].getSize().width
					},
					addEvents:{
						keyup:search
					}
				}).inject(el);
			}
			el.inject(tr);
		}
	}
	
	// sort the table
	function sort() {

		element.started();
		
		var tr = tbody.getElement('tr.tablesorter_noresults'),
			tb;
		if(tr) 
			tr.destroy();
		
		// remove all header class names
		for(i = 0; i < thead.getElements('tr',false)[options.search ? 1 : 0].cells.length; i++)
			thead.getElements('tr',false)[options.search ? 1 : 0].cells[i].removeClass('asc').removeClass('desc');
		
		if(col == this.cellIndex) {
			cells.reverse();
			order = (order == 'asc') ? 'desc' : 'asc';
			this.set('class',order);
		} else {
			col = this.cellIndex;
			rows.each(function(el,i) {
				var v = el.setStyle('display','').cells[col];
				while(v.hasChildNodes()) v = v.firstChild;
				cells[i].v = (v.nodeValue || '').toLowerCase();
				cells[i].n = i;
			});
			cells.sort(compare);
			if(options.order) {
				order = options.order;
				options.order = -1;
				if(order == 'desc') cells.reverse();
			} else {
				order = 'asc';
			}
			this.set('class',order);
		}
		
		// create a new tbody and replace the old
		tb = new Element('tbody',{},[]);
		cells.each(function(el,i) {
			tb.adopt(rows[el.n].set('class', i % 2 ? 'even' : 'odd'));
		});
		tb.replaces(tbody);
		tbody = tb;
		
		// show the current page we are on
		if(options.paginate) 
			perpage(pp);
		
		element.completed();	
	}
	
	// comapre values
	function compare(f,c) {
		var g, h, i; 
		f = g = f.v, 
		c = h = c.v;
        i = f.replace(/(\$|\,)/g, '').toFloat(), 
		n = c.replace(/(\$|\,)/g, '').toFloat();
        if(!isNaN(i) && !isNaN(n)) g = i, h = n;
		i = Date.parse(f), n = Date.parse(c);
		if(!isNaN(i) && !isNaN(n)) g = i, h = n;
        return g > h ? 1 : (g < h ? -1 : 0);
    }

	function search() {
		
		var p = this, 
			tr = tbody.getElement('tr.tablesorter_noresults'),
			c = 0, 
			w = 1,
			v, f;
		
		if(this.value == '') {
			if(tr) 
				tr.destroy();
			perpage(pp);
		} else {

			// find the cell value we are looking in
			tbody.getElements('tr',['styles']).each(function(el) {
				if(!el.hasClass('tablesorter_noresults')) {
				
					v = el.cells[p.parentNode.cellIndex];
					while(v.hasChildNodes()) 
						v = v.firstChild;
					v = (v.nodeValue || '').toLowerCase();
				
					f = 0;
					if(v.indexOf(p.value) > -1 && c < pp) f = 1, c++;
				
					el.setStyle('display',f ? '' : 'none');
					if(f) w++, el.set('class',w % 2 ? 'even' : 'odd');
				}
			});
			
			// no found results
			if(c > 0) {
				if(tr) 
					tr.destroy();
			} else {
				if(tr) 
					return;
				tr = new Element('tr',{'class':'tablesorter_noresults'},[]);
				new Element('td',{
					colspan:thead.getElement('tr').cells.length,
					align:'center',
					html:'No Results'
				}).inject(tr.inject(tbody));
			}
			
		}
		
	}

	function move(d,f) {
		var s = d == 1 ? (f ? pages_max : pages_current + 1) : (f ? 1 : pages_current - 1);
		if(s > pages_max || s <= 0) 
			return;
		pages_current = s;
		page((s - 1) * pp);
		if(options._ids[0]) 
			$(options._ids[0]).set('html',pages_current);
	}
	
	function perpage(n) {
		pp = n.toInt();
		pages_max = Math.ceil(rows_length / pp);
		move(-1,true); // show the beginning
		if(options._ids[1]) 
			$(options._ids[1]).set('html',pages_max);
	}
	
	function page(n) {
		var m = n + pp;
		tbody.getElements('tr',['styles']).each(function(el,i) {
			el.setStyle('display',i >= n && i < m ? '' : 'none').set('class',i % 2 ? options.even || 'even' : options.odd || 'odd');
		});
	}
	
	// save for prototypes/methods
	this.move = move;
	this.perpage = perpage;
	this.page = page;
}


// 
// tab slider
// thank you for the idea creative pony and panic
//
function SlideTabs(id,options) {
	
	options = options || {};
	options.slide = options.slide == undefined ? true : options.slide;
	
	var element = $(id),
		tabs = element.getElements(options.tabs || 'li.tab'),
		pans = element.getElements(options.pans || 'div.pan'),
		count = tabs.length,
		pans_width = pans[0].getSize().width,
		wrapper_width = pans_width * count,
		current_pan_index,
		container,
		wrapper;
	
	// set tabs and pans
	setTabsPans();
	
	// make a wrapper
	wrapper = new Element('div',{
		id:'slidetab_wrapper',
		setStyles:{
			marginLeft:0,
			width:wrapper_width
		}
	}).wraps(pans);
	
	// make a container
	container = new Element('div',{
		id:'slidetab_container',
		setStyles:{
			overflow:'hidden',
			width:pans_width
		}
	}).wraps(wrapper);
	
	// auto show?
	if(options.show > -1) {
		var h = options.slide;
		options.slide = false;
		tabs[options.show].doEvent('click');
		options.slide = h;
	} else {
		tabs[0].doEvent('click');
	}
	
	// add event and fire it
	window.addEvent('resize',fixPans).doEvent('resize');
	
	// slide the wrapper
	function slide() {
		
		// change the class
		var p = this,
			next;
		tabs.each(function(el) {
			if(el == p) 
				el.addClass('current');
			else 
				el.removeClass('current');
		});
		
		next = -(this._get('slidetab_index') * pans_width);
		current_pan_index = this._get('slidetab_index');
		
		wrapper.tween('margin-left',wrapper.getStyle('margin-left').toInt(),next,{
			duration:options.slide ? 500 : 1
		});
	}
	
	function setTabsPans() {
		// set the header tab index and click event
		tabs.each(function(el,i) {
			el._set('slidetab_index',i).removeEvent('click',slide).addEvent('click',slide);
		});

		// set the width for the pans
		pans.each(function(el) {
			el.setStyles({
				width:pans_width,
				'float':'left'
			});
		});
	}
	
	function setWidths() {
		// get new width
		wrapper_width = pans_width * count;

		// reset the width for the pans
		pans.each(function(el) {
			el.setStyle('width',pans_width);
		});	
		
		// adjust the wrapper and container width
		container.setStyle('width',pans_width);
		wrapper.setStyle('width',wrapper_width).setStyle('margin-left',-(current_pan_index || 0 * pans_width));
	}
	
	// fix the pans when the window is resized
	function fixPans() {
		// gets new width
		pans_width = container.setStyle('width','100%').getSize().width;
		// resets the width
		setWidths();	
	}
	
	// add a tab
	this.addTab = function(obj) {
		
		// set the object
		obj = obj || {};
		obj.pos = obj.pos === undefined ? 'end' : obj.pos;
		var pos = obj.pos === 'end' ? tabs.length - 1 : obj.pos, 
			where = obj.pos === 'end' ? 'after' : 'before',
			tab = $(tabs[0].cloneNode(true)).set('html', obj.title || 'New Tab'), // get clones, then insert them
			pan = $(pans[0].cloneNode(true)).set('html', obj.content || 'New Content'); 
		tab.inject(tabs[pos], where);
		pan.inject(pans[pos], where);
		
		pos += where === 'after' ? 1 : 0;
		tabs.splice(pos, 0, tab);
		pans.splice(pos, 0, pan);
		
		// increment count
		count++;
		
		// set the tabs and pans again
		fixPans();
		setTabsPans();
		
		// show the new tab
		tabs[pos].doEvent('click');
	}
	
	// remove a tab
	this.removeTab = function(n) {
		if(!tabs[n]) 
			return;
		tabs[n].destroy(); // destroy
		pans[n].destroy();
		tabs.splice(n, 1); // remove
		pans.splice(n, 1);
		count--; // minus one
		fixPans(); // fix
		setTabsPans();
		if(n - 1 > -1)
			tabs[--n].doEvent('click'); // show the pan before the one removed
	}
	
	// removes the current tab
	this.removeCurrent = function() {
		this.removeTab(current_pan_index);
	}	
}


//
// autocomplete
//
function AutoComplete(id,options) {
	
	options = options || {};
	if(!options.path) 
		return;
	
	options.limit = options.limit || 20;
	
	var element = $(id),
		list = [], 
		selected = -1,
		ajax = new Ajax({
			url:options.path,
			complete:didSearch
		}),
		ul = new Element('ul',{
			'class':'autocomplete',
			setStyles:{
				display:'none',
				position:'absolute',
				zIndex:99
			}
		}).inject(document.body,'top');
	
	// add event to element
	element.addEvents({
		keyup:doSearch,
		blur:hideSearch
	});
	
	function hideSearch() {
		$timeout(500,function() {
			ul.setStyle('display','none');
		});
	}
	
	function doSearch(e) {
		
		// move the list 
		ul.setStyles({
			top:element.getPosition().top + element.getSize().height,
			left:element.getPosition().left
		});
		
		// determine what key was pressed
		switch(new Event(e).code) {
			case 37: // left arrow
				return;
			case 39: // right arrow
				return;
			case 38: // up arrow
				selected = selected > 0 ? selected - 1 : 0;
				showSelected();
				break;
			case 40: // down arrow
				selected = selected < list.length - 1 ? selected + 1 : list.length - 1;
				showSelected();
				break;
			case 13: // enter
				if(list[selected])
					list[selected].doEvent('click');
				break;
			case 27: // escape
				ul.setStyle('display','none');
				break;
			default:
				list = [], selected = -1;
				ajax.send('value=' + this.value);
				break;
		}	
	}
	
	function didSearch(html) {
		
		// empty all children inside of ul
		ul.empty();
		
		var results = JSON.toObject(html), 
			c = 0, 
			i;
		
		for(i in results) {

			list[c++] = new Element('li',{
				html:i,
				setStyles:{
					cursor:'pointer'
				},
				addEvents:{
					click:function() {
						window.location.href = this._get('autocomplete_click');
					},
					mouseover:function() { 
						selected = this._get('autocomplete_c');
						showSelected();
					}
				}
			},['styles','events'])._set('autocomplete_c',c - 1)._set('autocomplete_click',results[i]).inject(ul);
			
			// limit
			if(c >= options.limit) 
				break;
		}
		
		ul.setStyle('display',(c == 0 ? 'none' : ''));
	
	}
	
	function showSelected() {
		list.each(function(el,i) {
			if(selected == i)
				el.addClass('over');
			else 
				el.removeClass('over');
		});
	}
	
}


//
// Validate
// idea from leigeber
//
var Validate = {
	
	move:true, // true to move window to error, false otherwise
	goaway:true, // do you want the validate warning to go away?
	position:'right', // appears to the right, bottom, or top of the error
	offset_x:10,
	offset_y:5,
	duration:2500, // how long before it fades out
	
	// legal characers only
	legal:function(input,msg) {
		var value, flag;
		value = (typeof input == 'object' ? input.value : input).trim();
		flag = (value != '' ? !(/\W/).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, flag;
		value = (typeof input == 'object' ? input.value : input).trim();
		flag = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/.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, flag;
		value = (typeof input == 'object' ? input.value : input).trim();
		flag = /^\([1-9]\d{2}\)\s?\d{3}\-\d{4}$/.test(value);
		if(!flag && typeof input == 'object') 
			this.alert(input,msg || 'Invalid Phone');
		return flag;
	},
	
	// number
	number:function(input,msg) {
		var value, flag;
		value = (typeof input == 'object' ? input.value : input).trim();
		flag = /(^-?\d\d*\.\d*$)|(^-?\d\d*$)|(^-?\.\d\d*$)/.test(value);
		if(!flag && typeof input == 'object') 
			this.alert(input,msg || 'Invalid Number');
		return flag;
	},
	
	// letters only
	letters:function(input,msg) {
		var value, flag;
		value = (typeof input == 'object' ? input.value : input).trim();
		flag = /^[a-zA-Z]+$/.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, flag;
		value = (typeof input == 'object' ? input.value : input).trim();
		flag = /\d{5}(-\d{4})?/.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, flag;
		value = (typeof input == 'object' ? input.value : input).trim();
		flag = /^([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])$/.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, flag;
		value = (typeof input == 'object' ? input.value : input).trim();
		flag = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/.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;
		value = (typeof input == 'object' ? input.value : input).trim();
		value = value.substring(value.lastIndexOf('.') + 1).toLowerCase();
		flag = false;
		(ex || ['jpeg','jpg','gif','png','swf']).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, i;
		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(i = 0; i < input.length; i++) {
					if(input[i].checked) 
						value = 'checked'; // value is now set to something
				}
				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;
		
		// make local variables
		var p = this, 
			pos, 
			ox, 
			oy, 
			div, 
			el;
		
		// extend the element first, then we can get the dimensions
		GOOP.extend(element,['dimensions']);
		pos = element.getCoordinates();
		
		// find offset for x and y (or left and top)
		if(this.position == 'right') 
			ox = pos.left + pos.width + this.offset_x, 
			oy = pos.top - this.offset_y;
		else if(this.position == 'left') 
			ox = pos.left, 
			oy = pos.top - this.offset_y; // will be set later
		else if(this.position == 'top') 
			ox = pos.left, 
			oy = pos.top; // will be set later
		else if(this.position == 'bottom') 
			ox = pos.left, 
			oy = pos.bottom + this.offset_y;
		
		// wrapper
		div = new Element('div', {
			html:msg,
			'class':'validate',
			setStyles:{
				position:'absolute',
				zIndex:100,
				opacity:0,
				top:oy,
				left:ox
			}
		}).inject(document.body,'top').fade('in',{
			complete:function() {
				if(!p.goaway) return;
				el = this;
				$timeout(p.duration,function() {
					el.fade('out',{
						complete:function() {
							this.destroy();
						}
					});
				});
			}
		});
		
		// fix position IF it was set to top OR left... we do it here because padding and margin has yet to be determined
		if(this.position == 'top') 
			div.setStyle('top',div.getStyle('top').toInt() - div.getSize().height - this.offset_y);
		else if(this.position == 'left') 
			div.setStyle('left',div.getStyle('left').toInt() - div.getSize().width - this.offset_x);
		
		// 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)
			return;
		month++; // so it reads easier
		this.element.value = year + '-' + (month < 10 ? '0' : '') + month + '-' + (day < 10 ? '0' : '') + day;
		this.close();
	},
	
	// 
	changeDate:function(y,m) {
		if(m < 0) 
			y - 1, 
			m = 11;
		if(m > 11) 
			y + 1, 
			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(),
			month = (options.month != undefined ? options.month : date.getMonth()),
			year = (options.year != undefined ? options.year : date.getFullYear()),
			days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
			months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
			days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
			first_day = new Date(year, month, 1),
			start_day = first_day.getDay(),
			month_length = days_in_month[month],
			html, 
			day = 1,
			i, j;
			
		if(month == 1)
			if((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
				month_length++;
		// 
		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(i = 0; i < 7; i++)
			html += '<td>' + days[i] + '</td>';
		html += '</tr>';

		// days of month
		html += '<tr>';
		for(i = 0; i < 9; i++) {
			for(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',
				zIndex: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();
	}
	
};

//
// rounder - makes a rounded
// thank you Steven Wittens
//
function Rounder(id,options) {
	
	options = options || {};
	options.bg = options.bg || '000000';
	options.fg = options.fg || 'eeeeee';
	options.radius = options.radius || 8;
	options.size = options.size || 1;
	
	var element = $(id),
		html = element.get('html'); // get html and erase it
	element.set('html','');
	
	// round the element
	if(options.top === true || options.top == undefined) 
		_round(element,options,'top');
	rounder_content(element,options);
	if(options.bottom === true || options.bottom == undefined) 
		_round(element,options,'bottom');
	
	// put the html back in it, and fix the orginial element 
	element.setStyles({
		border:'none',
		backgroundColor:'transparent'
	}).getElement('div.rounder_content').set('html',html);

	function sqr(n) {
		return n * n;
	}

	function _round(el,options,where) {
		
		var div = new Element('div',{},[]),
			begin = where == 'top' ? 1 : options.radius,
			end = where == 'top' ? options.radius : 1,
			increment = where == 'top' ? 1 : -1,
			arc, i;
		
		for(i = begin; (where == 'top' ? i <= end : i >= end); i+=increment) {
			
			arc = -(Math.sqrt(1 - sqr(1 - i / options.radius)) * options.radius);
			
			div.adopt(new Element('div', {
				setStyles:{
					margin:'0px ' + (arc + options.radius) + 'px',
					padding:0,
					height:i == end ? Math.ceil(options.size / 2) : 1,
					backgroundColor:'#' + (i == 1 ? options.bg : options.fg),
					borderLeft:options.size + 'px solid #' + options.bg,
					borderRight:options.size + 'px solid #' + options.bg
				}
			}));
		}
		
		div.inject(el,where);
	}
	
	function rounder_content(el,options) {
		
		el.adopt(new Element('div',{
			'class':'rounder_content',
			setStyles:{
				backgroundColor:(options.fg != 'transparent' ? '#' : '') + options.fg,
				borderTop:options.top === false ? options.size + 'px solid #' + options.bg : 'none',
				borderRight:options.size + 'px solid #' + options.bg,
				borderBottom:options.bottom === false ? options.size + 'px solid #' + options.bg : 'none',
				borderLeft:options.size + 'px solid #' + options.bg
			}
		}));
		
	}
	
};


//
// drag and drop
// thank you Luke Breuer - http://luke.breuer.com/tutorial/javascript-drag-and-drop-tutorial.aspx
//
function DragDrop(id,options) {
	
	options = options || {};
	options.revert = options.revert === false ? false : true;
	options.x = options.x === false ? false : true;
	options.y = options.y === false ? false : true;
	options.disable = options.disable === false ? false : true;
	
	var element = $(id),
		handle = (options.handle ? element.getElement(options.handle) : null),
		sx = 0, 
		sy = 0, 
		ox = 0, 
		oy = 0, 
		z = element.getStyle('z-index'), 
		target = null, 
		drag = false;
	
	// can't be a static element
	element.setStyle('position','relative');
	
	// add the functions
	element.started = options.start || function() {};
	element.moved = options.move || function() {};
	element.completed = options.complete || function() {};

	document.body.addEvent('mousedown', mousedown);
	document.body.addEvent('mouseup', mouseup);
	
	// if strict, disable the text selection
	if(options.disable) {
		var t = handle ? handle : element;
		t.onselectstart = function() { return false; }
		t.onmousedown = function() { return false; }
		t.ondragstart = function() { return false; }
	}
	
	function mousedown(e) {
		
		target = $(e.target ? e.target : e.srcElement);
		
		// not the element we want to move
		if((target !== handle && handle != null) || (target !== element && handle == null)) 
			return;
		
		// check the click
		if(e.button == 1 && window.event != null || e.button == 0) {

			// dragging
			drag = true;

			// grab the mouse position
			e = new Event(e);
			sx = e.page.x;
			sy = e.page.y;
			
			// grab the clicked elements position
			ox = element.getStyle('left').toInt();
			oy = element.getStyle('top').toInt();
			ox = isNaN(ox) ? 0 : ox;
			oy = isNaN(oy) ? 0 : oy;
			
			// bring to the front, and start the function
			element.setStyle('z-index',99).started();
			
			// add event
			document.body.addEvent('mousemove', mousemove);
		}
	}
	
	function mousemove(e) {
		
		e = new Event(e);
		var x = ox + e.page.x - sx, 
			y = oy + e.page.y - sy;
		
		// move the element, execute the function
		element.setStyles({
			left:options.x ? x : '',
			top:options.y ? y : ''
		}).moved();
				
	}
	
	function mouseup() {
		
		// stop moving the element
		if(!drag) return;
		drag = false;
		
		// remove the event
		document.body.removeEvent('mousemove',mousemove);
		
		// completed
		element.completed();
		
		// do this at end
		if(!options.revert)
			element.setStyles({
				top:0,
				left:0,
				zIndex:z
			});
		else
			element.setStyle('z-index',z);
	}
	
}


//
// reflect
// reflections on images - big thanks to reflection.js 2.0 - http://cow.neondragon.net/stuff/reflection/
//
function Reflect(id,options) {
	
	options = options || {};
	options.height = options.height || .5;
	options.opacity = (options.opacity || 50) * .01;
	
	// to save an errors
	if(!GOOP.dom || !GOOP.load) {
		recheck(50);
		return;
	}
	
	var element = (this == window || id == window ? $$('img.reflect') : $(id));
	if(element == null) 
		return;
	
	if(Object.prototype.toString.apply(element) === '[object Array]')
		element.each(function(el) {
			add(el);
		});
	else
		add(element);
	
	// add the reflection
	function add(el) {
		
		// remove old reflection first
		remove(el);
		
		if(!el.tagName) {
			recheck(50);
			return;
		}
		
		var h = el.getSize().height, 
			w = el.getSize().width,
			rh = Math.floor(h * options.height),
			dh = Math.floor(h * (1 + options.height));
		
		if(Browser.name == 'Explorer') {
			
			var a, d, reflection;
			
			// fix hyberlinks
			if(el.parentNode.tagName == 'A')
				a = new Element('a',{'href':el.parentNode.href},[]);
			else
				d = new Element('div',{},[]);
			
			reflection = new Element('img',{
				src:el.get('src'),
				setStyles:{
					width:w,
					height:h,
					display:'block',
					marginBottom:-(h - rh),
					filter:'flipv progid:DXImageTransform.Microsoft.Alpha(opacity=' + (options.opacity * 100) + ', style=1, finishOpacity=0, startx=0, starty=0, finishx=0, finishy=' + (options.height * 100) + ')'
				}
			});
			
			reflection.inject((a || d).wraps(el));
			
		} else {
			
			var context,
				gradient,
				canvas = new Element('canvas',{
					'class':el.get('class'),
					width:w,
					height:h * .5
				});

			if(!canvas.getContext) 
				return;
			
			context = canvas.getContext('2d');
			context.save();
			context.translate(0,h-1);
			context.scale(1,-1);
			context.drawImage(el, 0, 0, w, h);
			context.restore();
			context.globalCompositeOperation = 'destination-out';
			
			gradient = context.createLinearGradient(0, 0, 0, rh);
			gradient.addColorStop(1, 'rgba(255, 255, 255, 1.0)');
			gradient.addColorStop(0, 'rgba(255, 255, 255, ' + (1 - options.opacity) + ')');
			
			context.fillStyle = gradient;
			context.rect(0, 0, w, rh * 2);
			context.fill();

			canvas.inject(new Element('div',{
				setStyles:{
					width:w
				}
			},['styles']).wraps(el.setStyle('vertical-align','bottom')));
			
		}
	}
	
	function remove(el) {
		if(!el.hasClass('reflect')) 
			return;
		var p = GOOP.extend(el.parentNode,[]), 
			c = p.getElement('canvas');
		if(c) {
			c.destroy();
			el.replaces(p);
		}
	}
	
	function recheck(m) {
		$timeout(m, function() { Reflect(window); });
	}	
}


//
// Overlay
// thanks leigeber for the idea
//

var Overlay = {
	divs:[],
	show:function(content,width,height,ajax) {
		
		// acts as parent
		var p = this,
			c, m;
		
		// create the elements
		if(!$('overlay_content')) {
			
			c = new Element('div', { 
				id:'overlay_content',
				setStyles:{ 
					opacity:0,
					position:'absolute',
					zIndex:99,
					width:width ? width : 'auto',
					height:height ? height : 'auto'
				} 
			});
			m = new Element('div', {
				id:'overlay_mask',
				setStyles:{
					opacity:0,
					position:'absolute',
					zIndex:99
				},
				addEvents:{
					click:function() { 
						p.hide(); 
					}
				}
			});
			
			this.divs.push(
				c.inject(document.body,'top').tween('opacity', 0, 100),
				m.inject(document.body,'top').tween('opacity', 0, 70)
			);
			
			window.addEvent('resize',this.resize);
		}

		// set the content
		c.set('html',content);
		
		if(!ajax)
			window.doEvent('resize');
		else
			new Ajax({
				url:ajax.url,
				complete:function(html) {
					c.set('html',html);
					window.doEvent('resize');
				}
			}).send(ajax.variables || '');
	},
	hide:function() {
		Overlay.divs.each(function(el) {
			el.tween('opacity', el.getStyle('opacity'), 0, {
				complete:function() { 
					this.destroy(); 
				}
			});
		});
		Overlay.divs = [];
		window.removeEvent('resize',Overlay.resize);
	},
	resize:function() {
		
		var c = Overlay.divs[0], 
			m = Overlay.divs[1], 
			b = document.body.getSize();
		
		c.setStyles({
			top:b.height / 2 - c.getSize().height / 2,
			left:b.width / 2 - c.getSize().width / 2
		});
				
		m.setStyles({
			width:b.real_width,
			height:b.real_height
		});
	}
};


//
// allow tabs
// thanks UvumiTools TextArea
//
function AllowTabs(id) {
	
	$(id).addEvent('keydown', function(e) {

		e = new Event(e);
		
		// tab
		if(e.code == 9) {
			e.event.preventDefault();
			
			if(Browser.name == 'Explorer') {
				var range = document.selection.createRange();
				range.text = '\t';
			} else {
				var start = this.selectionStart, end = this.selectionEnd, value = this.value;
				this.value = this.value.substring(0, start) + '\t' + this.value.substring(end, this.value.length);
				start++;
				this.setSelectionRange(start, start);
			}
		}
		
	});
	
}


//
// 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);
	return this;
}
Array.prototype.indexOf = function(search) {
	var l = this.length, i;
	for(i = 0; i < l; i++)
		if(this[i] == search) 
			return i;
	return -1;
}
Array.prototype.shuffle = function() {
	var l = this.length, i;
	for(i = 0; i < l; i++)
		this.push(this.splice(Math.floor(Math.random()*l),1));
	return this;
}

// numbers and string
Number.prototype.toInt = String.prototype.toInt = function(radix) {
	return parseInt(this, radix || 10);
}
Number.prototype.toFloat = String.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();
	});
}


//
GOOP.extend(window,['events']);
Browser.init();
DOM.ready(GOOP.ondom);
window.addEvent('load',GOOP.onload);