var dbug = {
	logged: [],
	timers: {},
	firebug: false, 
	enabled: false, 
	log: function() {
		dbug.logged.push(arguments);
	},
	nolog: function(msg) {
		dbug.logged.push(arguments);
	},
	time: function(name){
		dbug.timers[name] = new Date().getTime();
	},
	timeEnd: function(name){
		if (dbug.timers[name]) {
			var end = new Date().getTime() - dbug.timers[name];
			dbug.timers[name] = false;
			dbug.log('%s: %s', name, end);
		} else dbug.log('no such timer: %s', name);
	},
	enable: function(silent) { 
		if(dbug.firebug) {
			try {
				dbug.enabled = true;
				dbug.log = console.debug || console.log;
				dbug.time = console.time;
				dbug.timeEnd = console.timeEnd;
				if(!silent) dbug.log('enabling dbug');
				for(var i=0;i<dbug.logged.length;i++){ dbug.log.apply(console, dbug.logged[i]); }
				dbug.logged=[];
			} catch(e) {
				dbug.enable.delay(400);
			}
		}
	},
	disable: function(){ 
		if(dbug.firebug) dbug.enabled = false;
		dbug.log = dbug.nolog;
		dbug.time = function(){};
		dbug.timeEnd = function(){};
	},
	cookie: function(set){
		var value = document.cookie.match('(?:^|;)\\s*jsdebug=([^;]*)');
		var debugCookie = value ? unescape(value[1]) : false;
		if((debugCookie != 'true' || set) && !set) {
			dbug.enable();
			dbug.log('setting debugging cookie');
			var date = new Date();
			date.setTime(date.getTime()+(24*60*60*1000));
			document.cookie = 'jsdebug=true;expires='+date.toGMTString()+';path=/;';
		} else dbug.disableCookie();
	},
	disableCookie: function(){
		dbug.log('disabling debugging cookie');
		document.cookie = 'jsdebug=false;path=/;';
	}
};
(function(){
	var fb = typeof console != "undefined";
	var debugMethods = ['debug','info','warn','error','assert','dir','dirxml'];
	var otherMethods = ['trace','group','groupEnd','profile','profileEnd','count'];
	function set(methodList, defaultFunction) {
		for(var i = 0; i < methodList.length; i++){
			dbug[methodList[i]] = (fb && console[methodList[i]])?console[methodList[i]]:defaultFunction;
		}
	};
	set(debugMethods, dbug.log);
	set(otherMethods, function(){});
})();
if (typeof console != "undefined" && console.warn){
	dbug.firebug = true;
	var value = document.cookie.match('(?:^|;)\\s*jsdebug=([^;]*)');
	var debugCookie = value ? unescape(value[1]) : false;
	if(window.location.href.indexOf("jsdebug=true")>0 || debugCookie=='true') dbug.enable();
	if(debugCookie=='true')dbug.log('debugging cookie enabled');
	if(window.location.href.indexOf("jsdebugCookie=true")>0){
		dbug.cookie();
		if(!dbug.enabled)dbug.enable();
	}
	if(window.location.href.indexOf("jsdebugCookie=false")>0)dbug.disableCookie();
}

new Native({name: 'Date', initialize: Date, protect: true});
['now','parse','UTC'].each(function(method){
	Native.genericize(Date, method, true);
});
Date.$Methods = new Hash();
["Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds", "Time", "TimezoneOffset", 
	"Week", "Timezone", "GMTOffset", "Ordinal", "DayOfYear", "LastMonth", "UTCDate", "UTCDay", "UTCFullYear",
	"AMPM", "UTCHours", "UTCMilliseconds", "UTCMinutes", "UTCMonth", "UTCSeconds"].each(function(method) {
	Date.$Methods.set(method.toLowerCase(), method);
});
$each({
	ms: "Milliseconds",
	year: "FullYear",
	min: "Minutes",
	mo: "Month",
	sec: "Seconds",
	hr: "Hours"
}, function(value, key){
	Date.$Methods.set(key, value);
});
Date.implement({
	set: function(key, value) {
		key = key.toLowerCase();
		var m = Date.$Methods;
		if (m.has(key)) this['set'+m.get(key)](value);
		return this;
	},
	get: function(key) {
		key = key.toLowerCase();
		var m = Date.$Methods;
		if (m.has(key)) return this['get'+m.get(key)]();
		return null;
	},
	clone: function() {
		return new Date(this.get('time'));
	},
	increment: function(interval, times) {
		return this.multiply(interval, times);
	},
	decrement: function(interval, times) {
		return this.multiply(interval, times, false);
	},
	multiply: function(interval, times, increment){
		interval = interval || 'day';
		times = $pick(times, 1);
		increment = $pick(increment, true);
		var multiplier = increment?1:-1;
		var month = this.format("%m").toInt()-1;
		var year = this.format("%Y").toInt();
		var time = this.get('time');
		var offset = 0;
		switch (interval) {
				case 'year':
					times.times(function(val) {
						if (Date.isLeapYear(year+val) && month > 1 && multiplier > 0) val++;
						if (Date.isLeapYear(year+val) && month <= 1 && multiplier < 0) val--;
						offset += Date.$units.year(year+val);
					});
					break;
				case 'month':
					times.times(function(val){
						if (multiplier < 0) val++;
						var mo = month+(val*multiplier);
						var year = year;
						if (mo < 0) {
							year--;
							mo = 12+mo;
						}
						if (mo > 11 || mo < 0) {
							year += (mo/12).toInt()*multiplier;
							mo = mo%12;
						}
						offset += Date.$units.month(mo, year);
					});
					break;
				default:
					offset = Date.$units[interval]()*times;
					break;
		}
		this.set('time', time+(offset*multiplier));
		return this;
	},
	isLeapYear: function() {
		return Date.isLeapYear(this.get('year'));
	},
	clearTime: function() {
		this.set('hr', 0);
		this.set('min',0);
		this.set('sec', 0);
		this.set('ms', 0);
		return this;
	},
	diff: function(d, resolution) {
		resolution = resolution || 'day';
		if($type(d) == 'string') d = Date.parse(d);
		switch (resolution) {
			case 'year':
				return d.format("%Y").toInt() - this.format("%Y").toInt();
				break;
			case 'month':
				var months = (d.format("%Y").toInt() - this.format("%Y").toInt())*12;
				return months + d.format("%m").toInt() - this.format("%m").toInt();
				break;
			default:
				var diff = d.get('time') - this.get('time');
				if (diff < 0 && Date.$units[resolution]() > (-1*(diff))) return 0;
				else if (diff >= 0 && diff < Date.$units[resolution]()) return 0;
				return ((d.get('time') - this.get('time')) / Date.$units[resolution]()).round();
		}
	},
	getWeek: function() {
		var day = (new Date(this.get('year'), 0, 1)).get('date');
		return Math.round((this.get('dayofyear') + (day > 3 ? day - 4 : day + 3)) / 7);
	},
	getTimezone: function() {
		return this.toString()
			.replace(/^.*? ([A-Z]{3}).[0-9]{4}.*$/, '$1')
			.replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/, '$1$2$3');
	},
	getGMTOffset: function() {
		var off = this.get('timezoneOffset');
		return ((off > 0) ? '-' : '+')
			+ Math.floor(Math.abs(off) / 60).zeroise(2)
			+ (off % 60).zeroise(2);
	},
	parse: function(str) {
		this.set('time', Date.parse(str));
		return this;
	},
	format: function(f) {
		f = f || "%x %X";
		if (!this.valueOf()) return 'invalid date';
		if (Date.$formats[f.toLowerCase()]) f = Date.$formats[f.toLowerCase()];
		var d = this;
		return f.replace(/\%([aAbBcdHIjmMpSUWwxXyYTZ])/g,
			function($1, $2) {
				switch ($2) {
					case 'a': return Date.$days[d.get('day')].substr(0, 3);
					case 'A': return Date.$days[d.get('day')];
					case 'b': return Date.$months[d.get('month')].substr(0, 3);
					case 'B': return Date.$months[d.get('month')];
					case 'c': return d.toString();
					case 'd': return d.get('date').zeroise(2);
					case 'H': return d.get('hr').zeroise(2);
					case 'I': return ((d.get('hr') % 12) || 12);
					case 'j': return d.get('dayofyear').zeroise(3);
					case 'm': return (d.get('mo') + 1).zeroise(2);
					case 'M': return d.get('min').zeroise(2);
					case 'p': return d.get('hr') < 12 ? 'AM' : 'PM';
					case 'S': return d.get('seconds').zeroise(2);
					case 'U': return d.get('week').zeroise(2);
					case 'W': throw new Error('%W is not supported yet');
					case 'w': return d.get('day');
					case 'x': return d.format('%m/%d/%Y');
					case 'X': return d.format('%I:%M%p');
					case 'y': return d.get('year').toString().substr(2);
					case 'Y': return d.get('year');
					case 'T': return d.get('GMTOffset');
					case 'Z': return d.get('Timezone');
					case '%': return '%';
				}
				return $2;
			}
		);
	},
	setAMPM: function(ampm){
		ampm = ampm.toUpperCase();
		if (this.format("%H").toInt() > 11 && ampm == "AM") 
			return this.decrement('hour', 12);
		else if (this.format("%H").toInt() < 12 && ampm == "PM")
			return this.increment('hour', 12);
		return this;
	}
});

Date.prototype.compare = Date.prototype.diff;
Date.prototype.strftime = Date.prototype.format;

Date.$nativeParse = Date.parse;

$extend(Date, {
	$months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
	$days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
	$daysInMonth: function(monthIndex, year) {
		if (Date.isLeapYear(year.toInt()) && monthIndex === 1) return 29;
		return [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][monthIndex];
	},
	$epoch: -1,
	$era: -2,
	$units: {
		ms: function(){return 1},
		second: function(){return 1000},
		minute: function(){return 60000},
		hour: function(){return 3600000},
		day: function(){return 86400000},
		week: function(){return 608400000},
		month: function(monthIndex, year) {
			var d = new Date();
			return Date.$daysInMonth($pick(monthIndex,d.format("%m").toInt()), $pick(year,d.format("%Y").toInt())) * 86400000;
		},
		year: function(year){
			year = year || new Date().format("%Y").toInt();
			return Date.isLeapYear(year.toInt())?31622400000:31536000000;
		}
	},
	$formats: {
		db: '%Y-%m-%d %H:%M:%S',
		compact: '%Y%m%dT%H%M%S',
		iso8601: '%Y-%m-%dT%H:%M:%S%T',
		rfc822: '%a, %d %b %Y %H:%M:%S %Z',
		'short': '%d %b %H:%M',
		'long': '%B %d, %Y %H:%M'
	},

	isLeapYear: function(yr) {
		return new Date(yr,2-1,29).getDate()==29;
	},

	parseUTC: function(value){
		var localDate = new Date(value);
		var utcSeconds = Date.UTC(localDate.get('year'), localDate.get('mo'),
		localDate.get('date'), localDate.get('hr'), localDate.get('min'), localDate.get('sec'));
		return new Date(utcSeconds);
	},
	parse: function(from) {
		var type = $type(from);
		if (type == 'number') return new Date(str);
		if (type != 'string') return from;
		if (!from.length) return null;
		for (var i = 0, j = Date.$parsePatterns.length; i < j; i++) {
			var r = Date.$parsePatterns[i].re.exec(from);
			if (r) {
				try {
					return Date.$parsePatterns[i].handler(r);
				} catch(e) {
					dbug.log('date parse error: ', e);
					return null;
				}
			}
		}
		return new Date(Date.$nativeParse(from));
	},

	parseMonth: function(month, num) {
		var ret = -1;
		switch ($type(month)) {
			case 'object':
				ret = Date.$months[month.get('mo')];
				break;
			case 'number':
				ret = Date.$months[month - 1] || false;
				if (!ret) throw new Error('Invalid month index value must be between 1 and 12:' + index);
				break;
			case 'string':
				var match = Date.$months.filter(function(name) {
					return this.test(name);
				}, new RegExp('^' + month, 'i'));
				if (!match.length) throw new Error('Invalid month string');
				if (match.length > 1) throw new Error('Ambiguous month');
				ret = match[0];
		}
		return (num) ? Date.$months.indexOf(ret) : ret;
	},

	parseDay: function(day, num) {
		var ret = -1;
		switch ($type(day)) {
			case 'number':
				ret = Date.$days[day - 1] || false;
				if (!ret) throw new Error('Invalid day index value must be between 1 and 7');
				break;
			case 'string':
				var match = Date.$days.filter(function(name) {
					return this.test(name);
				}, new RegExp('^' + day, 'i'));
				if (!match.length) throw new Error('Invalid day string');
				if (match.length > 1) throw new Error('Ambiguous day');
				ret = match[0];
		}
		return (num) ? Date.$days.indexOf(ret) : ret;
	},

	fixY2K: function(d){
		if (!isNaN(d)) {
			var newDate = new Date(d);
			if (newDate.get('year') < 2000 && d.toString().indexOf(newDate.get('year')) < 0) {
				newDate.increment('year', 100);
			}
			return newDate;
		} else return d;
	},

	$parsePatterns: [
		{
			re: /^(\d{1,2})[\.\-\/](\d{1,2})[\.\-\/](\d{2,4})$/,
			handler: function(bits){
				var d = new Date();
				d.set('year', bits[3]);
				d.set('month', bits[1].toInt() - 1, bits[2].toInt());
				return Date.fixY2K(d);
			}
		},
		{
			re: /^(\d{1,2})[\.\-\/](\d{1,2})[\.\-\/](\d{2,4})\s(\d{1,2}):(\d{1,2})(\w{2})$/,
			handler: function(bits){
				var d = new Date();
				d.set('year', bits[3]);
				d.set('month', bits[1] - 1);
				d.set('date', bits[2]);
				d.set('hr', bits[4]);
				d.set('min', bits[5]);
				d.set('ampm', bits[6]);
				return Date.fixY2K(d);
			}
		}
	]
});

Number.implement({
	zeroise: function(length) {
		return String(this).zeroise(length);
	}
});

String.implement({
	repeat: function(times) {
		var ret = [];
		for (var i = 0; i < times; i++) ret.push(this);
		return ret.join('');
	},
	zeroise: function(length) {
		return '0'.repeat(length - this.length) + this;
	}

});
Element.implement({
	tidy: function(){
		try {
			this.set('value', this.get('value').tidy());
		}catch(e){dbug.log('element.tidy error: %o', e);}
	},
	getTextInRange: function(start, end) {
		return this.get('value').substring(start, end);
	},
	getSelectedText: function() {
		if(Browser.Engine.trident) return document.selection.createRange().text;
		return this.get('value').substring(this.getSelectionStart(), this.getSelectionEnd());
	},
	getSelectionStart: function() {
		if(Browser.Engine.trident) {
			this.focus();
			var range = document.selection.createRange();
			if (range.compareEndPoints("StartToEnd", range) != 0) range.collapse(true);
			return range.getBookmark().charCodeAt(2) - 2;
		}
		return this.selectionStart;
	},
	getSelectionEnd: function() {
		if(Browser.Engine.trident) {
			var range = document.selection.createRange();
			if (range.compareEndPoints("StartToEnd", range) != 0) range.collapse(false);
			return range.getBookmark().charCodeAt(2) - 2;
		}
		return this.selectionEnd;
	},
	getSelectedRange: function() {
		return {
			start: this.getSelectionStart(),
			end: this.getSelectionEnd()
		}
	},
	setCaretPosition: function(pos) {
		if(pos == 'end') pos = this.get('value').length;
		this.selectRange(pos, pos);
		return this;
	},
	getCaretPosition: function() {
		return this.getSelectedRange().start;
	},
	selectRange: function(start, end) {
		this.focus();
		if(Browser.Engine.trident) {
			var range = this.createTextRange();
			range.collapse(true);
			range.moveStart('character', start);
			range.moveEnd('character', end - start);
			range.select();
			return this;
		}
		this.setSelectionRange(start, end);
		return this;
	},
	insertAtCursor: function(value, select) {
		var start = this.getSelectionStart();
		var end = this.getSelectionEnd();
		this.set('value', this.get('value').substring(0, start) + value + this.get('value').substring(end, this.get('value').length));
		 if($pick(select, true)) this.selectRange(start, start + value.length);
		else this.setCaretPosition(start + value.length);
		return this;
	},
	insertAroundCursor: function(options, select) {
		options = $merge({
			before: '',
			defaultMiddle: 'SOMETHING HERE',
			after: ''
		}, options);
		value = this.getSelectedText() || options.defaultMiddle;
		var start = this.getSelectionStart();
		var end = this.getSelectionEnd();
		if(start == end) {
			var text = this.get('value');
			this.set('value', text.substring(0, start) + options.before + value + options.after + text.substring(end, text.length));
			this.selectRange(start + options.before.length, end + options.before.length + value.length);
			text = null;
		} else {
			text = this.get('value').substring(start, end);
			this.set('value', this.get('value').substring(0, start) + options.before + text + options.after + this.get('value').substring(end, this.get('value').length));
			var selStart = start + options.before.length;
			if($pick(select, true)) this.selectRange(selStart, selStart + text.length);
			else this.setCaretPosition(selStart + text.length);
		}
		return this;
	}
});

Element.implement({

	expose: function(){
		if (this.getStyle('display') != 'none') return $empty;
		var before = {};
		['visibility', 'display', 'position'].each(function(style){
			before[style] = this.style[style]||'';
		}, this);
		this.setStyles({
			visibility: 'hidden',
			display: 'block',
			position:'absolute'
		});
		return (function(){
			this.setStyles(before);
		}).bind(this);
	},

	getDimensions: function(options) {
		options = $merge({computeSize: false},options);
		var dim = {};
		function getSize(el, options){
			if(options.computeSize) dim = el.getComputedSize(options);
			else {
				var sz = el.getSize();
				dim.width = sz.x;
				dim.height = sz.y;
			}
			return dim;
		}
		try {
			dim = getSize(this, options);
		}catch(e){}
		if(this.getStyle('display') == 'none'){
			var restore = this.expose();
			dim = getSize(this, options);
			restore();
		}
		return $merge(dim, {x: dim.width, y: dim.height});
	},

	getComputedSize: function(options){
		options = $merge({
			styles: ['padding','border'],
			plains: {height: ['top','bottom'], width: ['left','right']},
			mode: 'both'
		}, options);
		var size = {width: 0,height: 0};
		switch (options.mode){
			case 'vertical':
				delete size.width;
				delete options.plains.width;
				break;
			case 'horizontal':
				delete size.height;
				delete options.plains.height;
				break;
		}
		var getStyles = [];
		$each(options.plains, function(plain, key){
			plain.each(function(edge){
				options.styles.each(function(style){
					getStyles.push((style=="border")?style+'-'+edge+'-'+'width':style+'-'+edge);
				});
			});
		});
		var styles = this.getStyles.apply(this, getStyles);
		var subtracted = [];
		$each(options.plains, function(plain, key){
			size['total'+key.capitalize()] = 0;
			size['computed'+key.capitalize()] = 0;
			plain.each(function(edge){
				size['computed'+edge.capitalize()] = 0;
				getStyles.each(function(style,i){
					if(style.test(edge)) {
						styles[style] = styles[style].toInt();
						if(isNaN(styles[style]))styles[style]=0;
						size['total'+key.capitalize()] = size['total'+key.capitalize()]+styles[style];
						size['computed'+edge.capitalize()] = size['computed'+edge.capitalize()]+styles[style];
					}
					if(style.test(edge) && key!=style && 
						(style.test('border') || style.test('padding')) && !subtracted.contains(style)) {
						subtracted.push(style);
						size['computed'+key.capitalize()] = size['computed'+key.capitalize()]-styles[style];
					}
				});
			});
		});
		if($chk(size.width)) {
			size.width = size.width+this.offsetWidth+size.computedWidth;
			size.totalWidth = size.width + size.totalWidth;
			delete size.computedWidth;
		}
		if($chk(size.height)) {
			size.height = size.height+this.offsetHeight+size.computedHeight;
			size.totalHeight = size.height + size.totalHeight;
			delete size.computedHeight;
		}
		return $merge(styles, size);
	}
});

Element.implement({
	setPosition: function(options){
		$each(options, function(v, k){
			if (!$defined(v)) delete options[k];
		});
		options = $merge({
			relativeTo: document.body,
			position: {
				x: 'center',
				y: 'center'
			},
			edge: false,
			offset: {x:0,y:0},
			returnPos: false,
			relFixedPosition: false,
			ignoreMargins: false
		}, options);
		var parentOffset = {x: 0, y: 0};
		var parentPositioned = false;
		if(this.getParent() != document.body) {
			var parent = this.getParent();
			while(parent != document.body && parent.getStyle('position') == "static") {
				parent = parent.getParent();
			}
			if(parent != document.body) {
				parentOffset = parent.getPosition();
				parentPositioned = true;
			}
			options.offset.x = options.offset.x - parentOffset.x;
			options.offset.y = options.offset.y - parentOffset.y;
		}
		function fixValue(option) {
			if($type(option) != "string") return option;
			option = option.toLowerCase();
			var val = {};
			if(option.test('left')) val.x = 'left';
			else if(option.test('right')) val.x = 'right';
			else val.x = 'center';

			if(option.test('upper')||option.test('top')) val.y = 'top';
			else if (option.test('bottom')) val.y = 'bottom';
			else val.y = 'center';
			return val;
		};
		options.edge = fixValue(options.edge);
		options.position = fixValue(options.position);
		if(!options.edge) {
			if(options.position.x == 'center' && options.position.y == 'center') options.edge = {x:'center',y:'center'};
			else options.edge = {x:'left',y:'top'};
		}

		this.setStyle('position', 'absolute');
		var rel = $(options.relativeTo) || document.body;
		var top = (rel == document.body)?window.getScroll().y:rel.getTop();
		var left = (rel == document.body)?window.getScroll().x:rel.getLeft();

		if (top < 0) top = 0;
		if (left < 0) left = 0;
		var dim = this.getDimensions({computeSize: true, styles:['padding', 'border','margin']});
		if (options.ignoreMargins) {
			options.offset.x += ((options.edge && options.edge.x == "right")?dim['margin-right']:-dim['margin-left']);
			options.offset.y += ((options.edge && options.edge.y == "bottom")?dim['margin-bottom']:-dim['margin-top']);
		}
		var pos = {};
		var prefY = options.offset.y.toInt();
		var prefX = options.offset.x.toInt();
		switch(options.position.x) {
			case 'left':
				pos.x = left + prefX;
				break;
			case 'right':
				pos.x = left + prefX + rel.offsetWidth;
				break;
			default:
				pos.x = left + (((rel == document.body)?window.getSize().x:rel.offsetWidth)/2) + prefX;
				break;
		};
		switch(options.position.y) {
			case 'top':
				pos.y = top + prefY;
				break;
			case 'bottom':
				pos.y = top + prefY + rel.offsetHeight;
				break;
			default:
				pos.y = top + (((rel == document.body)?window.getSize().y:rel.offsetHeight)/2) + prefY;
				break;
		};

		if(options.edge){
			var edgeOffset = {};

			switch(options.edge.x) {
				case 'left':
					edgeOffset.x = 0;
					break;
				case 'right':
					edgeOffset.x = -dim.x-dim.computedRight-dim.computedLeft;
					break;
				default:
					edgeOffset.x = -(dim.x/2);
					break;
			};
			switch(options.edge.y) {
				case 'top':
					edgeOffset.y = 0;
					break;
				case 'bottom':
					edgeOffset.y = -dim.y-dim.computedTop-dim.computedBottom;
					break;
				default:
					edgeOffset.y = -(dim.y/2);
					break;
			};
			pos.x = pos.x+edgeOffset.x;
			pos.y = pos.y+edgeOffset.y;
		}
		pos = {
			left: ((pos.x >= 0 || parentPositioned)?pos.x:0).toInt(),
			top: ((pos.y >= 0 || parentPositioned)?pos.y:0).toInt()
		};
		if(rel.getStyle('position') == "fixed"||options.relFixedPosition) {
			pos.top = pos.top.toInt() + window.getScroll().y;
			pos.left = pos.left.toInt() + window.getScroll().x;
		}

		if(options.returnPos) return pos;
		else this.setStyles(pos);
		return this;
	}
});

Element.implement({
	isVisible: function() {
		return this.getStyle('display') != 'none';
	},
	toggle: function() {
		return this[this.isVisible() ? 'hide' : 'show']();
	},
	hide: function() {
		this.store('originalDisplay', this.getStyle('display')); 
		this.setStyle('display','none');
		return this;
	},
	show: function(display) {
		this.retrieve('originalDisplay', (this.retrieve('originalDisplay')=="none")
			?'block'
			:this.retrieve('originalDisplay'));
		this.setStyle('display',(display || this.retrieve('originalDisplay') || 'block'));
		return this;
	},
	swapClass: function(remove, add) {
		dbug.log(remove, add);
		return this.removeClass(remove).addClass(add);
	},
	toggleClass: function(c1, c2){
		if(this.hasClass(c1)) this.swapClass.run($A(arguments), this);
		else this.swapClass.run($A(arguments).reverse(), this);
	},
	fxOpacityOk: function(){
		if (!Browser.Engine.trident4)return true;
		var isColor = false;
		try {
			if (new Color(this.getStyle('backgroundColor'))) isColor = true;
		}catch(e){}
		return isColor;
	}
});
Fx.Reveal = new Class({
	Extends: Fx.Morph,
	options: {
		styles: ['padding','border','margin'],
		transitionOpacity: true,
		mode:'vertical',
		heightOverride: null,
		widthOverride: null
	},
	dissolve: function(){
		try {
			if(!this.hiding && !this.showing) {
				if(this.element.getStyle('display') != 'none'){
					this.hiding = true;
					this.showing = false;
					this.hidden = true;
					var startStyles = this.element.getComputedSize({
						styles: this.options.styles,
						mode: this.options.mode
					});
					var setToAuto = this.element.style.height === ""||this.element.style.height=="auto";
					if (this.element.fxOpacityOk() && this.options.transitionOpacity) startStyles.opacity = 1;
					var zero = {};
					$each(startStyles, function(style, name){
						zero[name] = 0; 
					}, this);
					var overflowBefore = this.element.getStyle('overflow');
					this.element.setStyle('overflow', 'hidden');
					if (!this.$chain) this.$chain = [];
					this.$chain.unshift(function(){
						if(this.hidden) {
							this.hiding = false;
							$each(startStyles, function(style, name) {
								startStyles[name] = style;
							}, this);
							this.element.setStyles($merge({display: 'none', overflow: overflowBefore}, startStyles));
							if (setToAuto) {
								this.element.setStyle('height', 'auto');
							}
						}
						this.callChain();
					}.bind(this));
					this.start(zero);
				} else {
					this.callChain.delay(10, this);
					this.fireEvent('onComplete', this.element);
				}
			}
		} catch(e) {
			this.hiding = false;
			this.element.hide();
			this.callChain.delay(10, this);
			this.fireEvent('onComplete', this.element);
		}
		return this;
	},
	reveal: function(){
		try {
			if(!this.showing && !this.hiding) {
				if(this.element.getStyle('display') == "none" || 
					 this.element.getStyle('visiblity') == "hidden" || 
					 this.element.getStyle('opacity')==0){
					this.showing = true;
					this.hiding = false;
					this.hidden = false;
					var before = this.element.getStyles('visibility', 'display', 'position');
					this.element.setStyles({
						visibility: 'hidden',
						display: 'block',
						position:'absolute'
					});
					var setToAuto = this.element.style.height === ""||this.element.style.height=="auto";
					if(this.element.fxOpacityOk() && this.options.transitionOpacity) this.element.setStyle('opacity',0);
					var startStyles = this.element.getComputedSize({
						styles: this.options.styles,
						mode: this.options.mode
					});
					this.element.setStyles(before);
					$each(startStyles, function(style, name) {
						startStyles[name] = style;
					}, this);
					if($chk(this.options.heightOverride)) startStyles['height'] = this.options.heightOverride.toInt();
					if($chk(this.options.widthOverride)) startStyles['width'] = this.options.widthOverride.toInt();
					if(this.element.fxOpacityOk() && this.options.transitionOpacity) startStyles.opacity = 1;
					var zero = { 
						height: 0,
						display: 'block'
					};
					$each(startStyles, function(style, name){ zero[name] = 0 }, this);
					var overflowBefore = this.element.getStyle('overflow');
					this.element.setStyles($merge(zero, {overflow: 'hidden'}));
					this.start(startStyles);
					if (!this.$chain) this.$chain = [];
					this.$chain.unshift(function(){
						if (!this.options.heightOverride && setToAuto) {
							if (["vertical", "both"].contains(this.options.mode)) this.element.setStyle('height', 'auto');
							if (["width", "both"].contains(this.options.mode)) this.element.setStyle('width', 'auto');
						}
						if(!this.hidden) this.showing = false;
						this.element.setStyle('overflow', overflowBefore);
						this.callChain();
					}.bind(this));
				} else {
					this.callChain();
					this.fireEvent('onComplete', this.element);
				}
			}
		} catch(e) {
			this.element.setStyles({
				display: 'block',
				visiblity: 'visible',
				opacity: 1
			});
			this.showing = false;
			this.callChain.delay(10, this);
			this.fireEvent('onComplete', this.element);
		}
		return this;
	},
	toggle: function(){
		try {
			if(this.element.getStyle('display') == "none" || 
				 this.element.getStyle('visiblity') == "hidden" || 
				 this.element.getStyle('opacity')==0){
				this.reveal();
			 } else {
				this.dissolve();
			}
		} catch(e) { this.show(); }
	 return this;
	}
});

Element.Properties.reveal = {

	set: function(options){
		var reveal = this.retrieve('reveal');
		if (reveal) reveal.cancel();
		return this.eliminate('reveal').store('reveal:options', $extend({link: 'cancel'}, options));
	},

	get: function(options){
		if (options || !this.retrieve('reveal')){
			if (options || !this.retrieve('reveal:options')) this.set('reveal', options);
			this.store('reveal', new Fx.Reveal(this, this.retrieve('reveal:options')));
		}
		return this.retrieve('reveal');
	}

};

Element.Properties.dissolve = Element.Properties.reveal;

Element.implement({

	reveal: function(options){
		this.get('reveal').reveal();
		return this;
	},

	dissolve: function(options){
		this.get('reveal').dissolve();
		return this;
	}

});
var Waiter = new Class({
	Implements: [Options, Events, Chain],
	options: {
		baseHref: '/_images/',
		containerProps: {
			styles: {
				position: 'absolute',
				display: 'none',
				opacity: 0,
				zIndex: 999,
				'text-align': 'center'
			},
			'class':'waiterContainer'
		},
		containerPosition: {},
		msg: false,
		msgProps: {
			styles: {
				'text-align': 'center',
				fontWeight: 'bold'
			},
			'class':'waiterMsg'
		},
		img: {
			src: '../_images/ajax-loader.gif',
			styles: {
				width: 220,
				height: 19
			},
			'class':'waiterImg'
		},
		layer:{
			styles: {
				width: 0,
				height: 0,
				position: 'absolute',
				zIndex: 998,
				display: 'none',
				opacity: 0.8,
				background: '#333333'
			},
			'class': 'waitingDiv'
		},
		fxOptions: {}
	},
	initialize: function(target, options){
		this.target = $(target)||$(document.body);
		this.setOptions(options);
		this.waiterContainer = new Element('div', this.options.containerProps).inject(document.body);
		if (this.options.msg) {
			this.msgContainer = new Element('div', this.options.msgProps);
			this.waiterContainer.adopt(this.msgContainer);
			if (!$(this.options.msg)) this.msg = new Element('p').appendText(this.options.msg);
			else this.msg = $(this.options.msg);
			this.msgContainer.adopt(this.msg);
		}
		if (this.options.img) {
			this.waiterImg = $(this.options.img.id) || new Element('img', $merge(this.options.img, {
				src: this.options.baseHref + this.options.img.src
			})).injectInside(this.waiterContainer);
		}
		this.waiterOverlay = $(this.options.layer.id) || new Element('div', this.options.layer).injectInside(document.body);
		try {
			if (this.options.useIframeShim) this.shim = new IframeShim(this.waiterOverlay, this.options.iframeShimOptions);
		} catch(e) {
			dbug.log("Waiter attempting to use IframeShim but failed; did you include IframeShim? Error: ", e);
			this.options.useIframeShim = false;
		}
		this.waiterFx = this.waiterFx || new Fx.Elements($$(this.waiterContainer, this.waiterOverlay), this.options.fxOptions);
	},
	toggle: function(element, show) {
		element = $(element) || $(this.active) || $(this.target);
		if (!$(element)) return this;
		if (this.active && element != this.active) return this.stop(this.start.bind(this, element));
		if((!this.active || show) && show !== false) this.start(element);
		else if(this.active && !show) this.stop();
		return this;
	},
	reset: function(){
		this.waiterFx.cancel().set({
			0: { opacity:[0]},
			1: { opacity:[0]}
		});
	},
	start: function(element){
		this.reset();
		element = $(element) || $(this.target);
		var start = function() {
			var dim = element.getComputedSize();
			this.active = element;
			this.waiterContainer.setPosition($merge({
				relativeTo: element
			}, this.options.containerPosition)).show();
			this.waiterOverlay.setStyles({
				width: dim.totalWidth,
				height: dim.totalHeight,
				display: 'block'
			}).setPosition({
				relativeTo: element,
				position: 'upperLeft'
			});
			if (this.options.useIframeShim) this.shim.show();
			this.waiterFx.start({
				0: { opacity:[1] },
				1: { opacity:[this.options.layer.styles.opacity]}
			}).chain(function(){
				if (this.acive == element) this.fireEvent('onShow', element);
				this.callChain();
			}.bind(this));
		}.bind(this);

		if (this.active && this.active != element) this.stop(start);
		else start();

		return this;
	},
	stop: function(callback){
		if (!this.active) {
			if ($type(callback) == "function") callback.attempt();
			return this;
		}
		this.waiterFx.cancel();
		this.waiterFx.clearChain();
		this.waiterFx.start({
			0: { opacity:[0]},
			1: { opacity:[0]}
		}).chain(function(){
			this.active = null;
			this.waiterOverlay.hide();
			if (this.options.useIframeShim) this.shim.hide();
			this.waiterContainer.hide();
			this.fireEvent('onHide', this.active);
			this.callChain();
			this.clearChain();
			if ($type(callback) == "function") callback.attempt();
		}.bind(this));
		return this;
	}
});

if (typeof Request.HTML != "undefined") {
	Request.HTML = new Class({
		Extends: Request.HTML,
		options: {
			useWaiter: false,
			waiterOptions: {},
			waiterTarget: false
		},
		initialize: function(options){
			this.parent(options);
			if (this.options.useWaiter && ($(this.options.update) || $(this.options.waiterTarget))) {
				this.waiter = new Waiter(this.options.waiterTarget || this.options.update, this.options.waiterOptions);
				['onComplete', 'onException', 'onCancel'].each(function(event){
					this.addEvent(event, this.waiter.stop.bind(this.waiter));
				}, this);
			}
		},
		send: function(options) {
			if (this.waiter) this.waiter.start().chain(this.parent.bind(this, options));
			else this.parent(options);
			return this;
		}
	});
}

/*
Script: FormValidator.js
	A css-class based form validation system.

License:
	http://clientside.cnet.com/wiki/cnet-libraries#license
*/
var InputValidator = new Class({
	Implements: [Options],
	initialize: function(className, options){
		this.setOptions({
			errorMsg: 'Validation failed.',
			test: function(field){return true}
		}, options);
		this.className = className;
	},
	test: function(field){
		if($(field)) return this.options.test($(field), this.getProps(field));
		else return false;
	},
	getError: function(field){
		var err = this.options.errorMsg;
		if($type(err) == "function") err = err($(field), this.getProps(field));
		return err;
	},
	getProps: function(field){
		if($(field) && $(field).get('validatorProps')){
			try {
				return JSON.decode($(field).get('validatorProps'));
			}catch(e){ return {}}
		} else {
			return {}
		}
	}
});

var FormValidator = new Class({
	Implements:[Options, Events],
	options: {
		fieldSelectors:"input, select, textarea",
		useTitles:false,
		evaluateOnSubmit:true,
		evaluateFieldsOnBlur: true,
		evaluateFieldsOnChange: false,
		serial: false,
		tinyMCE: false,
		warningPrefix: "Warning: ",
		errorPrefix: " < "
//  onFormValidate: function(isValid, form){},
//  onElementValidate: function(isValid, field){}
	},
	initialize: function(form, options){
		this.setOptions(options);
		this.form = $(form);
		this.form.addEvent('submit', this.onSubmit.bind(this));
		if(this.options.evaluateFieldsOnBlur) this.watchFields();
	
		var waiterExample = new Waiter();
		
		this.req = new Request({
				url: this.form.get('action'),
				evalScripts: true,
				onRequest: function() {
					waiterExample.start($('main'));  
				},
				onSuccess: function (text) {
					var response = JSON.decode.attempt(text);

					waiterExample.stop(waiterExample); 
					
					if (response) { this.processResponse(response); }    
					
				}.bind(this), 
				onFailure: function(instance) {
					waiterExample.stop(waiterExample); 
				},
				onCancel: function() {
					waiterExample.stop(waiterExample); 
				}
		});
	},
	getFields: function(){
		return this.fields = this.form.getElements(this.options.fieldSelectors)
	},
	watchFields: function(){
		this.getFields().each(function(el){
				el.addEvent('blur', this.validateField.pass([el, false], this));
			if(this.options.evaluateFieldsOnChange)
				el.addEvent('change', this.validateField.pass([el, true], this));
		}, this);
	},
	onSubmit: function(event){
		if (this.options.tinyMCE) {
			var content = tinyMCE.get(this.options.tinyMCE).getContent();
			$(this.options.tinyMCE).setProperty('value', content);
		}
	 
		if(!this.options.evaluateOnSubmit) {
			event.stop();
			this.req.send(this.form.toQueryString() + '&_ajax=1');
		}
		else {
			if(!this.validate(event)) { 
				event.stop();
			}
			else {
				event.stop();
				this.req.send(this.form.toQueryString() + '&_ajax=1');
			}
		}
	},
	reset: function() {
		this.getFields().each(this.resetField, this);
		return this;
	}, 
	processResponse: function(response) {
		//alert(response.type);
		if (response.type.toInt() == 1) {
			//alert(response.url);
			document.location = response.url;
		}
		else if (response.type.toInt() == 2) { 
			var el = $('errorDiv');
			var output = '';
			
			//  clear the errorDiv before displaying latest errors
			el.set('html', '');
			
			response.content.each(function(error) {
				output += error.message;
			});
			
			el.set('html', output);
			
			if(el && el.getStyle('display') == "none"){
				var fx = new Fx.Morph(el, {duration: 500, transition: Fx.Transitions.Sine.easeIn});
				fx.start({
					'display' : '',
					'opacity' : [0, 1]
				});
			}	
		} 
		else if (response.type.toInt() == 3){ 
			var el = $('recaptcha_response_field');
			var advice = this.makeAdvice('recaptcha-not-valid', el, 'Please validate captcha');
			this.insertAdvice(advice, el);
			this.showAdvice('recaptcha-not-valid', el);
		}
		else if (response.type.toInt() == 4){ 
			var req = new Request.HTML({
				url: response.url,
				update: $(response.container)
			}).get();
		}
	},
	validate: function(event) {
		var result = this.getFields().map(function(field) { return this.validateField(field, true); }, this);
		result = result.every(function(val){
			return val;
		});
		this.fireEvent('onFormValidate', [result, this.form, event]);
		return result;
	},
	validateField: function(field, force){
		if(this.paused) return true;
		field = $(field);
		var result = true;
		var failed = this.form.getElement('.validation-failed');
		var warned = this.form.getElement('.warning');		
		if(field && (!failed || force || field == failed || (failed && !this.options.serial))){
			var validators = field.className.split(" ").some(function(cn){
				return this.getValidator(cn);
			}, this);
			result = field.className.split(" ").map(function(className){
				return this.test(className,field);
			}, this).every(function(val){
				return val;
			});
			if (validators && !field.hasClass('warnOnly')){
				if(result) field.addClass('validation-passed').removeClass('validation-failed');
				else field.addClass('validation-failed').removeClass('validation-passed');
			}
			if(!warned || force || (warned && !this.options.serial)) {
				var warnings = field.className.split(" ").some(function(cn){
					if(cn.test('^warn-') || field.hasClass('warnOnly')) 
						return this.getValidator(cn.replace(/^warn-/,""));
					else return null;
				}, this);
				field.removeClass('warning');
				var warnResult = field.className.split(" ").map(function(cn){
					if(cn.test('^warn-') || field.hasClass('warnOnly')) 
						return this.test(cn.replace(/^warn-/,""), field, true);
					else return null;
				}, this);
			}
		}
		return result;
	},
	getPropName: function(className){
		return '__advice'+className;
	},
	test: function(className, field, warn){
		field = $(field);
		if(field.hasClass('ignoreValidation')) return true;
		warn = $pick(warn, false);
		if(field.hasClass('warnOnly')) warn = true;
		var isValid = true;
		if(field) {
			var validator = this.getValidator(className);
			if(validator && this.isVisible(field)) {
				isValid = validator.test(field);
				if(!isValid && validator.getError(field)){
					if(warn) field.addClass('warning');
					var advice = this.makeAdvice(className, field, validator.getError(field), warn);
					this.insertAdvice(advice, field);
					this.showAdvice(className, field);
				} else this.hideAdvice(className, field);
				this.fireEvent('onElementValidate', [isValid, field]);
			}
		}
		if(warn) return true;
		return isValid;
	},
	showAdvice: function(className, field){
		var advice = this.getAdvice(className, field);
		if(advice && !field[this.getPropName(className)] 
			 && (advice.getStyle('display') == "none" 
			 || advice.getStyle('visiblity') == "hidden" 
			 || advice.getStyle('opacity')==0)){
			field[this.getPropName(className)] = true;
			var fx = new Fx.Morph(advice, {duration: 500, transition: Fx.Transitions.Sine.easeIn});
			fx.start({
				'display' : '',
				'opacity' : [0, 1]
			});
		}
	},
	hideAdvice: function(className, field){
		var advice = this.getAdvice(className, field);
		if(advice && field[this.getPropName(className)]) {
			field[this.getPropName(className)] = false;
			//if element.cnet.js is present, transition the advice out
			if(advice.smoothHide) advice.smoothHide();
			else advice.setStyle('display','none');
		}
	},
	isVisible : function(field) {
		while(field != document.body) {
			if($(field).getStyle('display') == "none") return false;
			field = field.getParent();
		}
		return true;
	},
	getAdvice: function(className, field) {
		return $('advice-' + className + '-' + this.getFieldId(field))
	},
	makeAdvice: function(className, field, error, warn){
		var errorMsg = (warn)?this.options.warningPrefix:this.options.errorPrefix;
				errorMsg += (this.options.useTitles) ? $pick(field.title, error):error;
		var advice = this.getAdvice(className, field);
		if(!advice){
			var cssClass = (warn)?'warning-advice':'validation-advice';
			var x = $(field).getCoordinates().right + 10;
			var y = $(field).getCoordinates().top + 2;
			
			if (field.id.test("date")) { x += 20; }
			
			advice = new Element('span', {
				text: errorMsg,
				styles: { display: 'none' },
				id: 'advice-'+className+'-'+this.getFieldId(field)
			}).addClass(cssClass);
		} else{
			advice.set('html', errorMsg);
		}
		return advice;
	},
	insertAdvice: function(advice, field){
		switch (field.type.toLowerCase()) {
			case 'radio':
				var p = $(field.parentNode);
				if(p) {
					p.adopt(advice);
					break;
				}
			default: advice.inject($(field), 'after');
		};
	},
	getFieldId : function(field) {
		return field.id ? field.id : field.id = "input_"+field.name;
	},
	resetField: function(field) {
		field = $(field);
		if(field) {
			var cn = field.className.split(" ");
			cn.each(function(className) {
				if(className.test('^warn-')) className = className.replace(/^warn-/,"");
				var prop = this.getPropName(className);
				if(field[prop]) this.hideAdvice(className, field);
				field.removeClass('validation-failed');
				field.removeClass('warning');
				field.removeClass('validation-passed');
			}, this);
		}
		return this;
	},
	stop: function(){
		this.paused = true;
		return this;
	},
	start: function(){
		this.paused = false;
		return this;
	},
	ignoreField: function(field, warn){
		field = $(field);
		if(field){
			this.enforceField(field);
			if(warn) field.addClass('warnOnly');
			else field.addClass('ignoreValidation');
		}
		return this;
	},
	enforceField: function(field){
		field = $(field);
		if(field) field.removeClass('warnOnly').removeClass('ignoreValidation');
		return this;
	}
});

FormValidator.adders = {
	validators:{},
	add : function(className, options) {
		this.validators[className] = new InputValidator(className, options);
		//if this is a class
		//extend these validators into it
		if(!this.initialize){
			this.implement({
				validators: this.validators
			});
		}
	},
	addAllThese : function(validators) {
		$A(validators).each(function(validator) {
			this.add(validator[0], validator[1]);
		}, this);
	},
	getValidator: function(className){
		return this.validators[className];
	}
};
$extend(FormValidator, FormValidator.adders);
FormValidator.implement(FormValidator.adders);

FormValidator.add('IsEmpty', {
	errorMsg: false,
	test: function(element) { 
		if(element.type == "select-one"||element.type == "select")
			return !(element.selectedIndex >= 0 && element.options[element.selectedIndex].value != "");
		else
			return ((element.get('value') == null) || (element.get('value').length == 0));
	}
});


FormValidator.addAllThese([
	['required', {
		errorMsg: function(element){return 'This field is required.'}, 
		test: function(element) { 
			return !FormValidator.getValidator('IsEmpty').test(element); 
		}
	}],
	['minLength', {
		errorMsg: function(element, props){
			if($type(props.minLength))
				return 'Please enter at least ' + props.minLength + ' characters.';
			else return '';
		}, 
		test: function(element, props) {
			if($type(props.minLength)) return (element.get('value').length >= $pick(props.minLength, 0));
			else return true;
		}
	}],
	['maxLength', {
		errorMsg: function(element, props){
			//props is {maxLength:10}
			if($type(props.maxLength))
				return 'Please enter no more than ' + props.maxLength + ' characters (you entered ' + element.get('value').length + ' characters).';
			else return '';
		}, 
		test: function(element, props) {
			//if the value is <= than the maxLength value, element passes test
			return (element.get('value').length <= $pick(props.maxLength, 10000));
		}
	}],
	['validate-integer', {
		errorMsg: 'Please enter an number in this field.',
		test: function(element) {
				return FormValidator.getValidator('IsEmpty').test(element) || /^-{0,1}\d+$/.test(element.get('value'));
		}
	}],
	['validate-numeric', {
		errorMsg: 'Please enter only numeric values in this field ("1" or "1.1" or "-1" or "-1.1").', 
		test: function(element) {
			return FormValidator.getValidator('IsEmpty').test(element) || /^-{0,1}\d*\.{0,1}\d+$/.test(element.get('value'));
		}
	}],
	['validate-digits', {
		errorMsg: 'Please use numbers and punctuation only in this field (for example, a phone number with dashes or dots is permitted).', 
		test: function(element) {
			return FormValidator.getValidator('IsEmpty').test(element) || 
				(/[^a-zA-Z]/.test(element.get('value')) && /[\d]/.test(element.get('value')));
		}
	}],
	['validate-alpha', {
		errorMsg: 'Please use letters only (a-z) with in this field. No spaces or other characters are allowed.', 
		test: function (element) {
			return FormValidator.getValidator('IsEmpty').test(element) ||  /^[a-zA-Z]+$/.test(element.get('value'))
		}
	}],
	['validate-alphanum', {
		errorMsg: 'Please use only letters (a-z) or numbers (0-9) only in this field. No spaces or other characters are allowed.', 
		test: function(element) {
			return FormValidator.getValidator('IsEmpty').test(element) || !/\W/.test(element.get('value'))
		}
	}],
	['validate-phone', {
		errorMsg: 'Please a valid phone number.', 
		test: function(element) {
			return FormValidator.getValidator('IsEmpty').test(element) || /\(?[0-9]{3}\)?[-. ]?[0-9]{3}[-. ]?[0-9]{4}/.test(element.get('value'))
		}
	}],
	['validate-date', {
		errorMsg: function(element, props) {
			//if (Date.parse) {
			//	var format = props.dateFormat || "%x";
			//	return 'Please enter a valid date such as ' + new Date().format(format);
			//} else {
				return 'Please enter a valid date MM/DD/YYYY';
			//}
		},
		test: function(element, props) {
			if(FormValidator.getValidator('IsEmpty').test(element)) return true;
			/*if (Date.parse) {
				var format = props.dateFormat || "%x";
				var d = Date.parse(element.get('value'));
				var formatted = d.format(format);
				if (formatted != "invalid date") element.set('value', formatted);
				return !isNaN(d);
			} else {*/
				var regex = /^(\d{2})\/(\d{2})\/(\d{4})$/;
				if(!regex.test(element.get('value'))) return false;
				var d = new Date(element.get('value').replace(regex, '$1/$2/$3'));
				return (parseInt(RegExp.$1, 10) == (1+d.getMonth())) && 
					(parseInt(RegExp.$2, 10) == d.getDate()) && 
					(parseInt(RegExp.$3, 10) == d.getFullYear() );
			//}
		}
	}],
	['validate-email', {
		errorMsg: 'Not valid.', 
		test: function (element) {
			return FormValidator.getValidator('IsEmpty').test(element) || /\w{1,}[@][\w\-]{1,}([.]([\w\-]{1,})){1,3}$/.test(element.get('value'));
		}
	}],
	['validate-url', {
		errorMsg: 'Please enter a valid URL.', 
		test: function (element) {
			return FormValidator.getValidator('IsEmpty').test(element) || /^(http|https|ftp|rmtp|mms):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i.test(element.get('value'));
		}
	}],
	['validate-postal', {
		errorMsg: 'Please enter a valid postal code or Zipcode.', 
		test: function (element) {
			return FormValidator.getValidator('IsEmpty').test(element) || /^\d{5}-\d{4}|\d{5}|[A-Z]\d[A-Z][ ]?\d[A-Z]\d$/i.test(element.get('value'));
		}
	}],
	['validate-time', {
		errorMsg: 'Please enter a valid time.', 
		test: function (element) {
			return FormValidator.getValidator('IsEmpty').test(element) || /^([0-9]|[0-1][0-9]|[2][0-3]):([0-5][0-9])$/i.test(element.get('value'));
		}
	}],
	['validate-currency-dollar', {
		errorMsg: 'Please enter a valid $ amount. For example $100.00 .', 
		test: function(element) {
			// [$]1[##][,###]+[.##]
			// [$]1###+[.##]
			// [$]0.##
			// [$].##
			return FormValidator.getValidator('IsEmpty').test(element) ||  /^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/.test(element.get('value'));
		}
	}],
	['validate-radio-required', {
		errorMsg: 'Please select one.', 
		test: function (element) {
			var options = $$('input[name="' + element.name + '"]');
			var ok = false;
			options.each(function(el, i) {
				if (el.checked) ok = true;
			});
			return ok;
		}
	}],
	['validate-match', {
		errorMsg: 'Please confirm', 
		test: function (element) {
			if (element.value == $(element.getProperty('compare')).value) { return true; }
			else { return false; }
		}
	}],        
	['validate-one-required', {
		errorMsg: 'Please select one.', 
		test: function (element) {
			var p = element.parentNode;
			var options = p.getElements('input');
			return $A(options).some(function(el) {
				if (el.get('type') == 'checkbox') return el.get('checked');
				return el.get('value');
			});
		}
	}]
]);

/*
Script: OverText.js
	Shows text over an input that disappears when the user clicks into it. The text remains hidden if the user adds a value.

License:
	http://clientside.cnet.com/wiki/cnet-libraries#license
*/
var OverText = new Class({
	Implements: [Options, Events],
	options: {
//  textOverride: null,
		positionOptions: {
			position:"upperLeft",
			edge:"upperLeft",
			offset: {
				x: 4,
				y: 2
			}
		}
//  onTextHide: $empty,
//  onTextShow: $empty
	},
	overTxtEls: [],
	initialize: function(inputs, options) {
		this.setOptions(options);
		$$(inputs).each(function(el){
			if (this.overTxtEls.contains(el) || el.hasClass('overTxtSetup')) return;
			el.addClass('overTxtSetup');
			var val = this.options.textOverride || el.get('alt') || el.get('title');
			if (!val) return;
			this.overTxtEls.push(el);
			var txt = new Element('div', {
				'class': 'overTxtDiv',
				html: val,
				events: {
					click: this.hideTxt.pass(el, this)
				}
			}).inject(el, 'after');
			el.addEvents({
				focus: this.hideTxt.pass(el, this),
				blur: this.testOverTxt.pass(el, this),
				change: this.testOverTxt.pass(el, this)
			}).store('overtext', txt);
			window.addEvent('resize', this.repositionAll.bind(this));
			this.testOverTxt(el);
			this.repositionOverTxt(el);
		}, this);
	},
	hideTxt: function(el){
		var txt = el.retrieve('overtext');
		if (txt && txt.isVisible()) {
			txt.hide(); 
			try {
				el.fireEvent('focus').focus();
			} catch(e){} //IE barfs if you call focus on hidden elements
			this.fireEvent('onTextHide', [txt, el]);
		}
		return this;
	},
	showTxt: function(el){
		var txt = el.retrieve('overtext');
		if (txt && !txt.isVisible()) {
			txt.show();
			this.fireEvent('onTextShow', [txt, el]);
		}
		return this;
	},
	testOverTxt: function(el){
		if (el.get('value')) this.hideTxt(el);
		else this.showTxt(el);  
	},
	repositionAll: function(){
		this.overTxtEls.each(this.repositionOverTxt.bind(this));
		return this;
	},
	repositionOverTxt: function (el){
		try {
			var txt = el.retrieve('overtext');
			if (!txt || !el.getParent()) return;
			txt.setPosition($merge(this.options.positionOptions, {relativeTo: el}));
			if (el.offsetHeight) this.testOverTxt(el);
			else this.hideTxt(el);
		} catch(e){
			dbug.log('overTxt error: ', e);
		}        
		return this;
	}
});
