// BEGIN: DATE OBJECT PATCHES

/** Adds the number of days array to the Date object. */
Date._MD = new Array(31,28,31,30,31,30,31,31,30,31,30,31);

/** Constants used for time computations */
Date.SECOND = 1000 /* milliseconds */;
Date.MINUTE = 60 * Date.SECOND;
Date.HOUR   = 60 * Date.MINUTE;
Date.DAY    = 24 * Date.HOUR;
Date.WEEK   =  7 * Date.DAY;

Date.parseDate = function(str, format) {
	var today = new Date();
	var y = 0;
	var m = -1;
	var d = 0;
	var hr = 0;
	var min = 0;

	var i = 0, j = 0;
	var substr;
	
	var i_val=0;
	var i_format=0;
	
	while(i_format < format.length) {
		switch(format.charAt(i_format)) {
			case "%":
				i_format++;
				
				switch(format.charAt(i_format)) {
					case "Y":
						y = str.getInt(i_val, 4, 4);
						i_val += 4;
						break;
					case "m":
						m = str.getInt(i_val, 2, 2);
						if(m == null || m < 1 || m > 12) return today;
						i_val += 2;
						m--;
						break;
					case "d":
						d = str.getInt(i_val, 2, 2);
						if(d == null || d < 1 || d > 31) return today;
						i_val += 2;
						break;
					case "e":
						d = str.getInt(i_val, 1, 2);
						if(d == null || d < 1 || d > 31) return today;
						i_val += (d < 10) ? 1 : 2;
						break;
					case "L":
						m = str.getInt(i_val, 1, 2);
						if(m == null || m < 1 || m > 12) return today;
						i_val += (m < 10) ? 1 : 2;
						m--;
						break;
				}
				
				i_format++;
				
				break;
			case "'":
				i = format.indexOf("'", i_format+1);
				
				if(i_format+1 == i-1) substr = "'";
				else substr = format.substring(i_format+1, i-1);
				
				if(str.substr(i_val, substr.length) != substr) return today;
					
				i_val += substr.length;
				i_format = i+1;
				break;
			default:
				i_val++;
				i_format++;
		}
	}
	
	if(isNaN(y)) y = today.getFullYear();
	if(isNaN(m)) m = today.getMonth();
	if(isNaN(d)) d = today.getDate();
	if(isNaN(hr)) hr = today.getHours();
	if(isNaN(min)) min = today.getMinutes();
	if(y != 0 && m != -1 && d != 0) {
		if(m == 2) {
			// Check for leap year
			if((y%4 == 0 && y%100 != 0) || y%400 == 0) { // leap year
				if(d > 29) return today;
			}
			else if(d > 28) return today;
		}
		if(m == 4 || m == 6 || m == 9 || m == 11) {
			if(d > 30) return today;
		}
		return new Date(y, m, d, hr, min, 0);
	}
	else
		return today;
};

/** Returns the number of days in the current month */
Date.prototype.getMonthDays = function(month) {
	var year = this.getFullYear();
	if (typeof month == "undefined") {
		month = this.getMonth();
	}
	if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) {
		return 29;
	} else {
		return Date._MD[month];
	}
};

/** Returns the number of day in the year. */
Date.prototype.getDayOfYear = function() {
	var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
	var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0);
	var time = now - then;
	return Math.floor(time / Date.DAY);
};

/** Returns the number of the week in year, as defined in ISO 8601. */
Date.prototype.getWeekNumber = function() {
	var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
	var DoW = d.getDay();
	d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
	var ms = d.valueOf(); // GMT
	d.setMonth(0);
	d.setDate(4); // Thu in Week 1
	return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
};

/** Checks date and time equality */
Date.prototype.equalsTo = function(date) {
	return ((this.getFullYear() == date.getFullYear()) &&
		(this.getMonth() == date.getMonth()) &&
		(this.getDate() == date.getDate()) &&
		(this.getHours() == date.getHours()) &&
		(this.getMinutes() == date.getMinutes()));
};

/** Set only the year, month, date parts (keep existing time) */
Date.prototype.setDateOnly = function(date) {
	var tmp = new Date(date);
	this.setDate(1);
	this.setFullYear(tmp.getFullYear());
	this.setMonth(tmp.getMonth());
	this.setDate(tmp.getDate());
};

/** Prints the date in a string according to the given format. */
Date.prototype.print = function (str) {
	var m = this.getMonth();
	var d = this.getDate();
	var y = this.getFullYear();
	var wn = this.getWeekNumber();
	var w = this.getDay();
	var s = {};
	var hr = this.getHours();
	var pm = (hr >= 12);
	var ir = (pm) ? (hr - 12) : hr;
	var dy = this.getDayOfYear();
	if (ir == 0)
		ir = 12;
	var min = this.getMinutes();
	var sec = this.getSeconds();
	s["%a"] = Date._SDN[w]; // abbreviated weekday name [FIXME: I18N]
	s["%A"] = Date._DN[w]; // full weekday name
	s["%b"] = Date._SMN[m]; // abbreviated month name [FIXME: I18N]
	s["%B"] = Date._MN[m]; // full month name
	// FIXME: %c : preferred date and time representation for the current locale
	s["%C"] = 1 + Math.floor(y / 100); // the century number
	s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31)
	s["%e"] = d; // the day of the month (range 1 to 31)
	// FIXME: %D : american date style: %m/%d/%y
	// FIXME: %E, %F, %G, %g, %h (man strftime)
	s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format)
	s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format)
	s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366)
	s["%k"] = hr;		// hour, range 0 to 23 (24h format)
	s["%l"] = ir;		// hour, range 1 to 12 (12h format)
	s["%m"] = (m < 9) ? ("0" + (1+m)) : (1+m); // month, range 01 to 12
	s["%L"] = 1+m; // month, range 1 to 12
	s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59
	s["%n"] = "\n";		// a newline character
	s["%p"] = pm ? "PM" : "AM";
	s["%P"] = pm ? "pm" : "am";
	// FIXME: %r : the time in am/pm notation %I:%M:%S %p
	// FIXME: %R : the time in 24-hour notation %H:%M
	s["%s"] = Math.floor(this.getTime() / 1000);
	s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59
	s["%t"] = "\t";		// a tab character
	// FIXME: %T : the time in 24-hour notation (%H:%M:%S)
	s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn;
	s["%u"] = w + 1;	// the day of the week (range 1 to 7, 1 = MON)
	s["%w"] = w;		// the day of the week (range 0 to 6, 0 = SUN)
	// FIXME: %x : preferred date representation for the current locale without the time
	// FIXME: %X : preferred time representation for the current locale without the date
	s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99)
	s["%Y"] = y;		// year with the century
	s["%%"] = "%";		// a literal '%' character

	var re = /%./g;
	if (!Date.is_ie5 && !Date.is_khtml)
		return str.replace(re, function (par) { return s[par] || par; });

	var a = str.match(re);
	for (var i = 0; i < a.length; i++) {
		var tmp = s[a[i]];
		if (tmp) {
			re = new RegExp(a[i], 'g');
			str = str.replace(re, tmp);
		}
	}

	return str;
};

Date.prototype.__msh_oldSetFullYear = Date.prototype.setFullYear;
Date.prototype.setFullYear = function(y) {
	var d = new Date(this);
	d.__msh_oldSetFullYear(y);
	if (d.getMonth() != this.getMonth())
		this.setDate(28);
	this.__msh_oldSetFullYear(y);
};

// END: DATE OBJECT PATCHES

/// detect a special case of "web browser"
Date.is_ie = ( /msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent) );

Date.is_ie5 = ( Date.is_ie && /msie 5\.0/i.test(navigator.userAgent) );

/// detect Opera browser
Date.is_opera = /opera/i.test(navigator.userAgent);

/// detect KHTML-based browsers
Date.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent);
