if (!Date.prototype.getFullYear)
{
	Date.prototype.getFullYear = function(){
		var yy = this.getYear();
		return (yy < 1900 ? yy + 1900 : yy);
	};
}

String.prototype.times = function(n){
	var s = '';
	for(var i=0;i<n;i++)
		s += this;
	return s;
};
String.prototype.escapeStr = function(){
	return this.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
};
String.prototype.cleanStr = function(){
	return this.replace(/[^\w\s\.\-]/gi, " ").replace(/  +/gi, " ").trim();
};
String.prototype.addSlashes = function(){
	return this.replace(/([!@#$%^&*()+=\[\]\\';,./{}|":<>?~_-])/g, "\\$1");
};

String.prototype.reverse = function(){
	return this.split('').reverse().join('');
};
String.prototype.ucfirst = function(){
	return this.charAt(0).toUpperCase() + this.slice(1);
};

Date.prototype.addSeconds = function(seconds) {
	this.setSeconds(this.getSeconds() + Number(seconds));
	return this;
};

Date.prototype.addMinutes = function(minutes) {
	this.setMinutes(this.getMinutes() + Number(minutes));
	return this;
};

Date.prototype.addHours = function(hours) {
	this.setHours(this.getHours() + Number(hours));
	return this;
};

Date.prototype.addDays = function(days) {
	this.setDate(this.getDate() + Number(days));
	return this;
};

Date.prototype.addWeeks = function(weeks) {
	weeks = Number(weeks) || 1;
	this.setDate(this.getDate() + (7*weeks));
	return this;
};

Date.prototype.addMonth = function(months) {
	this.setMonth(this.getMonth() + Number(months));
	return this;
};

Date.prototype.addYear = function(years) {
	this.setFullYear(this.getFullYear() + Number(years));
	return this;
};

Date.prototype.getWeek = function() {
	var onejan = new Date(this.getFullYear(), 0, 1);
	return Math.ceil((((this - onejan) / 86400000) + onejan.getDay()+1)/7);
};

Date.prototype.isAM = function(){
	return (this.getHours() < 12);
};

Date.prototype.isPM = function(){
	return (this.getHours() > 12);
};

Date.prototype.isBefore = function(date2) {
	if (date2 == null) return false;
	return (this.getTime() < date2.getTime());
};

Date.prototype.isAfter = function(date2) {
	if (date2 == null) return false;
	return (this.getTime() > date2.getTime());
};

Date.prototype.clearTime = function() {
	this.setHours(0);
	this.setMinutes(0);
	this.setSeconds(0);
	this.setMilliseconds(0);
	return this;
};

Date.prototype.toInt = function() {
	return this.getFullYear() * 10000 + (this.getMonth() + 1) * 100 + this.getDate();
};

Date.fromInt = function(dateInt) {
	var year = parseInt(dateInt / 10000);
	var month = parseInt((dateInt - year * 10000) / 100);
	var day	 = parseInt(dateInt - year * 10000 - month * 100);

	return new Date(year, month - 1, day, 12, 0, 0);
};

Date.prototype.isToday = function() {
	return this.toInt() == new Date().toInt();
};
Date.prototype.isYesterday = function() {
	return new Date(this).addDays(1).isToday();
};
Date.prototype.isTomorrow = function() {
	return new Date(this).addDays(-1).isToday();
};
Date.prototype.isFuture = function() {
	return this.getTime() > new Date().getTime();
};
Date.prototype.isPast = function() {
	return this.getTime() < new Date().getTime();
};
Date.prototype.getWeekStart = function(firstDayOfWeek){
	firstDayOfWeek = firstDayOfWeek || Date.firstDayOfWeek || 0;
	var ret = new Date(this);
	return new Date(ret.setDate(ret.getDate() - Math.abs(ret.getDay() - firstDayOfWeek)));
};
Date.prototype.getWeekEnd = function(firstDayOfWeek){
	firstDayOfWeek = firstDayOfWeek || Date.firstDayOfWeek || 0;
	return this.getWeekStart(firstDayOfWeek).addDays(6);
};
Date.prototype.isThisWeek = function(){
	return new Date(this) >= new Date().getWeekStart() && new Date(this) <= new Date().getWeekEnd();
};
Date.prototype.isLastWeek = function(){
	var dt = new Date().addDays(-7);
	return new Date(this) >= dt.getWeekStart() && new Date(this) <= dt.getWeekEnd();
};
Date.prototype.isNextWeek = function(){
	var dt = new Date().addDays(7);
	return new Date(this) > dt.getWeekStart() && new Date(this) < dt.getWeekEnd();
};
Date.prototype.isThisMonth = function(){
	return new Date(this) >= new Date().getMonthStart() && new Date(this) <= new Date().getMonthEnd();
};
Date.prototype.isLastMonth = function(){
	var dt = new Date().getMonthStart().addMonth(-1);
	return new Date(this) >= dt.getMonthStart() && new Date(this) <= dt.getMonthEnd();
};
Date.prototype.isNextMonth = function(){
	var dt = new Date().getMonthStart().addMonth(1);
	return new Date(this) > dt.getMonthStart() && new Date(this) < dt.getMonthEnd();
};
Date.prototype.setFirstDayOfThisWeek = function(firstDayOfWeek) {
	firstDayOfWeek = firstDayOfWeek || Date.firstDayOfWeek;
	this.setDate(this.getDate() - this.getDay() + firstDayOfWeek - (this.getDay() == 0 && firstDayOfWeek != 0 ? 7 : 0));
};
Date.prototype.getNthWeek = function(){
	var onejan = new Date(this.getFullYear(),0,1);
	return Math.ceil((((this - onejan) / 86400000) + onejan.getDay()+1)/7);
};
Date.prototype.dayPos = function() {
	return Date.dayNames.indexOf(this.format("dddd").ucfirst())+1;
};

Date.prototype.getMonthStart = function(setToThis){
	var newDate = new Date(this.getFullYear(), this.getMonth());
	if (setToThis)
	{
		this.setTime(newDate.getTime());
	}
	return newDate;
};

Date.prototype.isMonthStart = function(){
	return (this.getMonthStart().toInt() == this.toInt());
};

Date.prototype.getMonthEnd = function(setToThis){
	var newDate = new Date(this.getFullYear(), this.getMonth()+1, 0);
	if (setToThis)
	{
		this.setTime(newDate.getTime());
	}
	return newDate;
};

Date.prototype.isMonthEnd = function(){
	return (this.getMonthEnd().toInt() == this.toInt());
};

(function(){
	var defaults = {
		order: 'DMY',
		strict: false,
		date: 0 // -1-Month Start, 0-Today, 1-Month End
	};

	var months = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];

	var abs = ["AM", "PM", "AFTERNOON", "MORNING"];

	var zp = function(val){
		return ((["00", "01", "02", "03", "04", "05", "06", "07", "08", "09"])[val] || val);
	};

	var mark = function (str, val) {
		var lval = val.toLowerCase();
		var regex = new RegExp('^' + lval + '|(.*[^:alpha:])' + lval, 'g');
		return str.replace(regex, '$1' + val);
	};

	var normalize = function (str) {
		str = str.toLowerCase();
		str = str.replace(/[^:a-z0-9]/g, '-');
		for (var i = 0; i < months.length; i++) str = mark(str, months[i]);
		for (var i = 0; i < abs.length; i++) str = mark(str, abs[i]);
		str = str.replace(/[a-z]/g, '');
		str = str.replace(/([0-9])([A-Z])/g, '$1-$2');
		str = ('-' + str + '-').replace(/-+/g, '-');
		return str;
	};

	var find_time = function (norm) {
		var obj = { date: norm, time: '' };
		obj.time = norm.replace(/^.*-(\d\d?(:\d\d){1,2}(:\d\d\d)?(-(AM|PM))?)-.*$/, '$1');
		if (obj.time == obj.date){
			obj.time = norm.replace(/^.*-(\d\d?-(AM|PM))-.*$/, '$1');
		}
		if (obj.time == obj.date) {
			obj.time = '';
		}
		obj.date = norm.replace(obj.time, '');
		obj.time = ('-' + obj.time + '-').replace(/-+/g, '-');
		obj.date = ('-' + obj.date + '-').replace(/-+/g, '-');
		return obj;
	};

	var find_year = function (norm) {
		var year = null;

		// Check for a 4-digit year
		year = norm.replace(/^.*-(\d\d\d\d)-.*$/, '$1');
		if (year != norm) return year; else year = null;

		// Check for a 2-digit year, over 32.
		year = norm.replace(/^.*-((3[2-9])|([4-9][0-9]))-.*$/, '$1');
		if (year != norm) return year; else year = null;

		// Day is always by month, so check for explicit months in 
		// first or third spot
		year = norm.replace(/^.*-[A-Z]{3}-\d\d?-(\d\d?)-.*$/, '$1');
		if (year != norm) return year; else year = null;
		year = norm.replace(/^.*-(\d\d?)-\d\d?-[A-Z]{3}-.*$/, '$1');
		if (year != norm) return year; else year = null;

		// If all else fails, use the setting for the position of the year.
		var pos = '$3';
		if (defaults.opts.order.charAt(0) == 'Y') pos = '$1';
		else if (defaults.opts.order.charAt(1) == 'Y') pos = '$2';
		if (defaults.opts.order.indexOf("D") < 0)
		{
			year = norm.replace(/^.*-([A-Z]{3}|\d{1,2})-(\d\d?)-.*$/, pos);
		}
		else
		{
			year = norm.replace(/^.*-(\d\d?)-([A-Z]{3}|\d{1,2})-(\d\d?)-.*$/, pos);
		}

		if (year != norm) return year; else year = null;

		return year;
	};

	var find_month = function (norm, year) {
		// Check for an explicity month
		var matches = norm.match(/[A-Z]{3}/);
		if (matches && matches.length) return matches[0];

		// Remove the year, and unless obviously wrong, use order
		// to chose which one to use for month.
		var parts = norm.replace(year + '-', '').split('-');
		var order = defaults.opts.order;
		if (parts.length != 4)
		{
			if (order.indexOf("D") < 0) {
				return (parseInt(parts[1], 10) <= 12) ? parts[1] : null;
			}

			return null;
		}

		
		var md = order.indexOf('M') < order.indexOf('D') ? 1 : 2;
		return (parseInt(parts[md], 10) <= 12) ? parts[md] : parts[md == 1 ? 2 : 1];
	};

	var find_day = function (norm, year, month) {
	return norm.replace(year, '').replace(new RegExp("\\-" + month + "\\-", "ig"), '').replace(/-/g, '');
		// return norm.replace(year, '').replace(new RegExp(month + "(?!\\d+)", "ig"), '').replace(/-/g, '');
	};

	var create_absolute = function (obj) {
		if (obj.date == "-")
		{
			return new Date("");
		}

		var time = obj.time.replace(/[-APM]/g, '');
		var parts = time.split(':');
		parts[1] = parts[1] || 0;
		parts[2] = parts[2] || 0;
		parts[3] = parts[3] || 0;
		var ihr = parseInt(parts[0], 10);
		if (obj.time.match(/-AM-/) && ihr == 12) parts[0] = 0;
		else if (obj.time.match(/-PM-/) && ihr < 12) parts[0] = ihr + 12;
		parts[0] = ("0" + parts[0]).substring(("0" + parts[0]).length - 2);
		parts[1] = ("0" + parts[1]).substring(("0" + parts[1]).length - 2);
		parts[2] = ("0" + parts[2]).substring(("0" + parts[2]).length - 2);
		time = parts[0] + ":" + parts[1] + ":" + parts[2];
		var millisecs = parts[3];

		var strict = defaults.opts.strict;
		if (!obj.year && !strict) obj.year = (new Date()).getFullYear();
		var year = parseInt(obj.year, 10);
		if (year < 100) {
			year += (year < 70 ? 2000 : 1900);
		}

		if (!obj.month && !strict) obj.month = (new Date()).getMonth() + 1;
		var month = String(obj.month);
		if (month.match(/[A-Z]{3}/)) {
			month = "JAN-FEB-MAR-APR-MAY-JUN-JUL-AUG-SEP-OCT-NOV-DEC-".indexOf(month) / 4 + 1;
		}
		month = ("0" + month).substring(("0" + month).length - 2);

		if (!obj.day && !strict) {
			// obj.day = (new Date()).getDate();
			obj.day = 1;// Dummy Date
		} else {
			defaults.date = null;
		}

		var day = ("0" + obj.day).substring(("0" + obj.day).length - 2);

		var date = new Date();
		date.setTime(Date.parse(year + '/' + month + '/' + day + ' ' + time));
		date.setMilliseconds(millisecs);

		if (defaults.date === -1) {
			date.setDate(1);
		} else if (defaults.date === 1) {
			date.setMonth(date.getMonth() + 1);
			date.setDate(0);
		}

		return date;
	};

	var parse = function (norm) {
		return absolute(norm);
	};

	var absolute = function (norm) {
		var obj = find_time(norm);
		obj.norm = norm;
		obj.year = find_year(obj.date);
		obj.month = find_month(obj.date, obj.year);
		obj.day = find_day(obj.date, obj.year, obj.month);
		return create_absolute(obj);
	};

	Date.fromString = function (fuzz, opts) {
		opts = opts || {};
		defaults.date = opts.date !== undefined ? opts.date : 0;

		defaults.opts = {
			order: defaults.order,
			strict: defaults.strict
		};

		if (opts.order) defaults.opts.order = opts.order;
		if (opts.strict != undefined) defaults.opts.strict = opts.strict;
		var date = parse(normalize(fuzz));
		return date;
	};
})();

(function(){
	var _date = {
		monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
		shortMonth: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
		dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
		shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
		tinyDays: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
		firstDayOfWeek: 0,
		pad: ["00", "01", "02", "03", "04", "05", "06", "07", "08", "09"]
	};

	var zp = function(val){
		return (_date.pad[val] || val);
	}

	/*
	String.prototype.times = function(n){
		var s = '';
		for(var i=0;i<n;i++)
			s	+= this;
		return s;
	};
	String.prototype.zp = function(){
		return (_date.pad[this] || this);
		// return '0'.times(n-this.length) + this;
	};
	Number.prototype.zp = function(n){
		return (_date.pad[this] || this);
		// return this.toString().zp(n);
	};
	*/
	String.prototype.toDate = function (isStrict){
		var str = this;

		if (str == '0000-00-00 00:00:00' || str == '0000-00-00') {
			return isStrict != undefined ? isStrict : new Date();
		}

		var matches = str.match(/(\d{4})[-|\/](\d{2})[-|\/](\d{2}) (\d{2}):(\d{2}):(\d{2})/);
		if (matches && matches.length == 7)
		{
			return new Date(matches[1]-0, matches[2]-1, matches[3]-0, matches[4]-0, matches[5]-0, matches[6]-0);
		}

		matches = str.match(/^(\d{4})[-|\/](\d{2})[-|\/](\d{2})$/);
		if (matches && matches.length == 4)
		{
			var curDate = new Date();
			return new Date(matches[1]-0, matches[2]-1, matches[3]-0, curDate.getHours(), curDate.getMinutes(), curDate.getSeconds());
		}
	};

	String.prototype.toUtcDate = function(){
		var dt = this.toDate();
		return new Date(Date.UTC(dt.getFullYear(), dt.getMonth(), dt.getDate(), dt.getHours(), dt.getMinutes(), dt.getSeconds(), dt.getMilliseconds()));
	}

	var _dateRegex = /(yyyy|yyy|yy|y|MMMM|MMM|MM|M|dd|d|EEEE|EEE|EE|hh|h|HH|H|mm|m|ss|s|a)/gi;
	Date.prototype.format = function(f) {
		if(!this.valueOf())
			return null;

		var d = this;

		return (f || "y-MM-dd").replace(_dateRegex, function(val){
			switch (val) {
				case 'yyyy':
				case 'yyy':
				case 'yy':
				case 'y':
					return d.getFullYear();
				case 'MMMM':
					return _date.monthNames[d.getMonth()];
				case 'MMM':
					return _date.shortMonth[d.getMonth()];
				case 'MM':
					return zp(d.getMonth()+1);
				case 'M':
					return (d.getMonth()+1);
				case 'dd':
					return zp(d.getDate());
				case 'd':
					return d.getDate();
				case 'EEEE':
					return _date.dayNames[d.getDay()];
				case 'EEE':
					return _date.shortDays[d.getDay()];
				case 'EE':
					return _date.tinyDays[d.getDay()];
				case 'hh':
					return zp((d.getHours()%12) || 12);
				case 'h':
					return ((d.getHours()%12) || 12);
				case 'HH':
					return zp(d.getHours());
				case 'H':
					return d.getHours();
				case 'mm':
					return zp(d.getMinutes());
				case 'm':
					return d.getMinutes();
				case 'ss':
					return zp(d.getSeconds());
				case 's':
					return d.getSeconds();
				case 'a':
					return d.getHours()<12 ? 'AM' : 'PM';
			}
		});
	};
})();
