var BehaviourBaseClass = new Class({
    initialize: function(){
        this.behaviours = [];
        var bhvr = this;
        window.addEvent('domready', function(){ bhvr.apply()});
    },
    register: function(actions){
        if(! this.behaviours.test(actions))
            this.behaviours.push(actions);
    },
    apply: function(actions) {
        if ($type(actions)!='array') {
            actions = this.behaviours;
        }
        actions.each(function(bhvrs){
            for (bhvr in bhvrs){
                //try {
                    if($type(bhvrs[bhvr])=='function') {
                        $$(bhvr).each(function(el){
                            bhvrs[bhvr](el);
                        });
                    }
                //} catch(e){}
            }
        });
    }
});

/**
 * Date Class - Parsing and more
 * 
 * Originally by Nicholas Barthelemy for Jester
 * https://svn.nbarthelemy.com/date-js/
 * 
 * @todo:
 *  - Extend parser with "Date input formats", http://www.gnu.org/software/tar/manual/html_node/tar_109.html
 *
 * @version		prerelease
 *
 * @license		MIT-style license
 * @author		Harald Kirschner <mail [at] digitarald.de>
 * @copyright	Author
 */

$native(Date);

Date.extend({

	clone: function() {
		return new Date(this.getTime());
	},

	increment: function(interval, times) {
		this.setTime(this.getTime() + Date.$units[interval || 'day'] * (times || 1));
		return this;
	},

	decrement: function(interval, times) { //definitely, incremenet with third param.
		this.setTime(this.getTime() - Date.$units[interval || 'day'] * (times || 1));
		return this;
	},

	clearTime: function() {
		this.setHours(0);
		this.setMinutes(0);
		this.setSeconds(0);
		this.setMilliseconds(0);
		return this;
	},

	diff: function(d, resolution) {
		if($type(d) == 'string') d = Date.parse(d);
		return Math.floor((this.getTime() - d.getTime()) / Date.$units[resolution || 'day']);
	},

	getOrdinal: function() {
		var str = this.toString();
		var test = str.substr(-(Math.min(str.length, 2)));
		return (test > 3 && test < 21) ? 'th' : ['th', 'st', 'nd', 'rd', 'th'][Math.min(this % 10, 4)];
	},

	getDayOfYear: function() {
		return ((Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 1, 0, 0, 0)
			- Date.UTC(this.getFullYear(), 0, 1, 0, 0, 0) ) / Date.$units.day);
	},

	lastDayofMonth: function() {
		var ret = this.clone();
		ret.setMonth(ret.getMonth() + 1, 0);
		return ret.getDate();
	},

	getWeek: function() {
		var day = (new Date(this.getFullYear(), 0, 1)).getDate();
		return Math.round((this.getDayOfYear() + (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.getTimezoneOffset();
		return ((off > 0) ? '-' : '+')
			+ Math.floor(Math.abs(off) / 60).zeroise(2)
			+ (off % 60).zeroise(2);
	},

	isLeapYear: function() {
		return Date.isLeapYear(this.getYear());
	},

	daysInMonth: function(month, year) {
		return Date.isLeapYear(this.getMonth(), this.getYear());
	},

	parse: function(str) {
		this.setTime(Date.parse(str));
		return this;
	},

	format: function(f) {
		if (!this.valueOf()) return '&nbsp;';
		//replace short-hand with actual format
		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.getDay()].substr(0, 3);
					case 'A': return Date.$days[d.getDay()];
					case 'b': return Date.$months[d.getMonth()].substr(0, 3);
					case 'B': return Date.$months[d.getMonth()];
					case 'c': return d.toString();
					case 'd': return d.getDate().zeroise(2);
					case 'H': return d.getHours().zeroise(2);
					case 'I': return ((d.getHours() % 12) || 12).zeroise(2);
					case 'j': return d.getDayOfYear().zeroise(3);
					case 'm': return (d.getMonth() + 1).zeroise(2);
					case 'M': return d.getMinutes().zeroise(2);
					case 'p': return d.getHours() < 12 ? 'AM' : 'PM';
					case 'S': return d.getSeconds().zeroise(2);
					case 'U': return d.getWeek().zeroise(2);
					case 'W': throw new Error('%W is not supported yet');
					case 'w': return d.getDay();
					case 'x': return d.format('%m/%d/%Y');
					case 'X': return d.format('%I:%M%p');
					case 'y': return d.getFullYear().toString().substr(2);
					case 'Y': return d.getFullYear();
					case 'T': return d.getGMTOffset();
					case 'Z': return d.getTimezone();
					case '%': return '%';
				}
				return $2;
			}
		);
	}

});

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: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
	$epoch: -1,
	$era: -2,
	$units: {
		ms: 1,
		second: 1000,
		minute: 60000,
		hour: 3600000,
		day: 86400000,
		week: 608400000,
		month: 2678400000,
		year: 31536000000
	},
	$formats: {
		db: '%Y-%m-%d %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(year) {
		return (((year % 4) === 0) && ((year % 1000) !== 0) || ((year % 4000) === 0));
	},

	daysInMonth: function(month, year) {
		month = (month + 12) % 12;
		if (month == 1 && Date.isLeapYear(year)) return 29;
		return Date.$daysInMonth[month];
	},

	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) return Date.$parsePatterns[i].handler(r);
		}
		return new Date(Date.$nativeParse(from));
	},

	parseMonth: function(month, num) {
		var ret = -1;
		switch ($type(month)) {
			case 'object':
				ret = Date.$months[month.getMonth()];
				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;
	},

	$parsePatterns: [
	{
		re: /^(\d{1,2})\/(\d{1,2})\/(\d{2,4})$/,
		handler: function(bits) {
			var d = new Date();
			d.setYear(bits[3]);
			d.setMonth(bits[1].toInt() - 1, bits[2].toInt());
			return d;
		}
	}, {
		re: /^(\d{4})(?:-?(\d{2})(?:-?(\d{2})(?:[T ](\d{2})(?::?(\d{2})(?::?(\d{2})(?:\.(\d+))?)?)?(?:Z|(?:([-+])(\d{2})(?::?(\d{2}))?)?)?)?)?)?$/,
		handler: function(bits) {
			var offset = 0;
			var d = new Date(bits[1], 0, 1);
			if (bits[2]) d.setMonth(bits[2] - 1);
			if (bits[3]) d.setDate(bits[3]);
			if (bits[4]) d.setHours(bits[4]);
			if (bits[5]) d.setMinutes(bits[5]);
			if (bits[6]) d.setSeconds(bits[6]);
			if (bits[7]) d.setMilliseconds(('0.' + bits[7]).toInt() * 1000);
			if (bits[9]) {
				offset = (bits[9].toInt() * 60) + bits[10].toInt();
				offset *= ((bits[8] == '-') ? 1 : -1);
			}
			offset -= d.getTimezoneOffset();
			d.setTime((d * 1) + (offset * 60 * 1000).toInt())
			return d;
		}
	}, {
		re: /^tod/i,
		handler: function() {
			return new Date();
		}
	}, {
		re: /^tom/i,
		handler: function() {
			var d = new Date();
			d.setDate(d.getDate() + 1);
			return d;
		}
	}, {
		re: /^yes/i,
		handler: function() {
			var d = new Date();
			d.setDate(d.getDate() - 1);
			return d;
		}
	}, {
		re: /^(\d{1,2})(st|nd|rd|th)?$/i,
		handler: function(bits) {
			var d = new Date();
			d.setDate(bits[1].toInt());
			return d;
		}
	}, {
		re: /^(\d{1,2})(?:st|nd|rd|th)? (\w+)$/i,
		handler: function(bits) {
			var d = new Date();
			d.setMonth(Date.parseMonth(bits[2], true), bits[1].toInt());
			return d;
		}
	}, {
		re: /^(\d{1,2})(?:st|nd|rd|th)? (\w+),? (\d{4})$/i,
		handler: function(bits) {
			var d = new Date();
			d.setMonth(Date.parseMonth(bits[2], true), bits[1].toInt());
			d.setYear(bits[3]);
			return d;
		}
	}, {
		re: /^(\w+) (\d{1,2})(?:st|nd|rd|th)?,? (\d{4})$/i,
		handler: function(bits) {
			var d = new Date();
			d.setMonth(Date.parseMonth(bits[1], true), bits[2].toInt());
			d.setYear(bits[3]);
			return d;
		}
	}, {
		re: /^next (\w+)$/i,
		handler: function(bits) {
			var d = new Date();
			var day = d.getDay();
			var newDay = Date.parseDay(bits[1], true);
			var addDays = newDay - day;
			if (newDay <= day) {
				addDays += 7;
			}
			d.setDate(d.getDate() + addDays);
			return d;
		}
	}, {
		re: /^last (\w+)$/i,
		handler: function(bits) {
			throw new Error('Not yet implemented');
		}
	}]
});

Number.extend({

	zeroise: function(length) {
		return String(this).zeroise(length);
	}

});

String.extend({

	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;
	}

});