/*
d  		Day of the month as digits; no leading zero for single-digit days.
dd 		Day of the month as digits; leading zero for single-digit days.
ddd 	Day of the week as a three-letter abbreviation.
dddd 	Day of the week as its full name.
m 		Month as digits; no leading zero for single-digit months.
mm 		Month as digits; leading zero for single-digit months.
mmm 	Month as a three-letter abbreviation.
mmmm 	Month as its full name.
yy 		Year as last two digits; leading zero for years less than 10.
yyyy 	Year represented by four digits.
h 		Hours; no leading zero for single-digit hours (12-hour clock).
hh 		Hours; leading zero for single-digit hours (12-hour clock).
H 		Hours; no leading zero for single-digit hours (24-hour clock).
HH 		Hours; leading zero for single-digit hours (24-hour clock).
n 		Minutes; no leading zero for single-digit minutes.
nn 		Minutes; leading zero for single-digit minutes.
s 		Seconds; no leading zero for single-digit seconds.
ss 		Seconds; leading zero for single-digit seconds.
l or L 	Milliseconds. l gives 3 digits. L gives 2 digits.
t 		Lowercase time marker string: a or p.
T 		Uppercase time marker string: A or P.
tt 		Lowercase time marker string: am or pm.
TT 		Uppercase time marker string: AM or PM.

'…' or "…" 	Literal character sequence. Surrounding quotes are removed. Not necessary when using mask characters as part of larger words.
*/

Date.prototype.format = function(mask) {
	var d = this; // Needed for the replace() closure
	
	// If preferred, zeroise() can be moved out of the format() method for performance and reuse purposes

	var zeroize = function (value, length) {
		if (!length) length = 2;
		value = String(value);
		for (var i = 0, zeros = ''; i < (length - value.length); i++) {
			zeros += '0';
		}
		return zeros + value;
	};
	
	return mask.replace(/"[^"]*"|'[^']*'|\b(?:d{1,4}|m{1,4}|yy(?:yy)?|([hnHMs])\1?|t|T|TT|tt|[lL])\b/g, function($0) {
		switch($0) {
			case 'd':	return d.getDate();
			case 'dd':	return zeroize(d.getDate());
			case 'ddd':	return ['Sun','Mon','Tue','Wed','Thr','Fri','Sat'][d.getDay()];
			case 'dddd':return ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'][d.getDay()];
			case 'm':	return d.getMonth() + 1;
			case 'mm':	return zeroize(d.getMonth() + 1);
			case 'mmm':	return ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'][d.getMonth()];
			case 'mmmm':return ['January','February','March','April','May','June','July','August','September','October','November','December'][d.getMonth()];
			case 'yy':	return String(d.getFullYear()).substr(2);
			case 'yyyy':return d.getFullYear();
			case 'h':	return d.getHours() % 12 || 12;
			case 'hh':	return zeroize(d.getHours() % 12 || 12);
			case 'H':	return d.getHours();
			case 'HH':	return zeroize(d.getHours());
			case 'n':	return d.getMinutes();
			case 'nn':	return zeroize(d.getMinutes());
			case 's':	return d.getSeconds();
			case 'ss':	return zeroize(d.getSeconds());
			case 'l':	return zeroize(d.getMilliseconds(), 3);
			case 'L':	var m = d.getMilliseconds();
					if (m > 99) m = Math.round(m / 10);
					return zeroize(m);
					
			case 't':	return d.getHours() < 12 ? 'a' : 'p';
			case 'T':	return d.getHours() < 12 ? 'A' : 'P';
			case 'tt':	return d.getHours() < 12 ? 'am' : 'pm';
			case 'TT':	return d.getHours() < 12 ? 'AM' : 'PM';
			// Return quoted strings with the surrounding quotes removed
			default:	return $0.substr(1, $0.length - 2);
		}
	});
};
