String.prototype.pad = function(len, chr, pos)
{
	if (!len || this.length >= len || !chr) return this;
	var str = new String(this);

	switch (pos) {
		case 'right':
			while (str.length < len) { str += chr };
		break;
		
		default: // left;
			var s = new String();
			while (s.length < (len - str.length)) s += chr;
			str = s.concat(str);
		break;
	}
	return str;
};

Date.prototype.format = function(format)
{
	var returnStr = '';
	var replace = Date.replaceChars;
	for (var i = 0; i < format.length; i++) {
		var curChar = format.charAt(i);
		if (replace[curChar])
			returnStr += replace[curChar].call(this);
		else
			returnStr += curChar;
	}
	return returnStr;
};

Date.replaceChars =
{
	shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
	longMonths: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
	shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
	longDays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
	
	// Day
	d: function() { return (this.getDate() < 10 ? '0' : '') + this.getDate(); },
	D: function() { return Date.replaceChars.shortDays[this.getDay()]; },
	j: function() { return this.getDate(); },
	l: function() { return Date.replaceChars.longDays[this.getDay()]; },
	N: function() { return this.getDay() + 1; },
	S: function() { return (this.getDate() % 10 == 1 && this.getDate() != 11 ? 'st' : (this.getDate() % 10 == 2 && this.getDate() != 12 ? 'nd' : (this.getDate() % 10 == 3 && this.getDate() != 13 ? 'rd' : 'th'))); },
	w: function() { return this.getDay(); },
	z: function() { return "Not Yet Supported"; },
	// Week
	W: function() { return "Not Yet Supported"; },
	// Month
	F: function() { return Date.replaceChars.longMonths[this.getMonth()]; },
	m: function() { return (this.getMonth()+1 < 10 ? '0' : '') + (this.getMonth() + 1); },
	M: function() { return Date.replaceChars.shortMonths[this.getMonth()]; },
	n: function() { return this.getMonth() + 1; },
	t: function() { return "Not Yet Supported"; },
	// Year
	L: function() { return "Not Yet Supported"; },
	o: function() { return "Not Supported"; },
	Y: function() { return this.getFullYear(); },
	y: function() { return ('' + this.getFullYear()).substr(2); },
	// Time
	a: function() { return this.getHours() < 12 ? 'am' : 'pm'; },
	A: function() { return this.getHours() < 12 ? 'AM' : 'PM'; },
	B: function() { return "Not Yet Supported"; },
	g: function() { return this.getHours() == 0 ? 12 : (this.getHours() > 12 ? this.getHours() - 12 : this.getHours()); },
	G: function() { return this.getHours(); },
	h: function() { return (this.getHours() < 10 || (12 < this.getHours() < 22) ? '0' : '') + (this.getHours() < 10 ? this.getHours() + 1 : this.getHours() - 12); },
	H: function() { return (this.getHours() < 10 ? '0' : '') + this.getHours(); },
	i: function() { return (this.getMinutes() < 10 ? '0' : '') + this.getMinutes(); },
	s: function() { return (this.getSeconds() < 10 ? '0' : '') + this.getSeconds(); },
	// Timezone
	e: function() { return "Not Yet Supported"; },
	I: function() { return "Not Supported"; },
	O: function() { return (this.getTimezoneOffset() < 0 ? '-' : '+') + (this.getTimezoneOffset() / 60 < 10 ? '0' : '') + (this.getTimezoneOffset() / 60) + '00'; },
	T: function() { return "Not Yet Supported"; },
	Z: function() { return this.getTimezoneOffset() * 60; },
	// Full Date/Time
	c: function() { return "Not Yet Supported"; },
	r: function() { return this.toString(); },
	U: function() { return this.getTime() / 1000; }
}

var DatePicker = 
{
	d: new Array(),
	data: new Array(),
	dayArrayShort: new Array('Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'),
	dayArrayMed: new Array('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'),
	dayArrayLong: new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'),
	monthArrayShort: new Array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'),
	monthArrayMed: new Array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'),
	monthArrayLong: new Array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'),
	params: { format:'Y-m-d H:i:s', showTime:true, minuteIncrement:5 },
	
	init: function(event, fieldName, dateString, params)
	{
		event.cancelBubble = true;
		var e = (event.srcElement) ? event.srcElement : event.target;
		
		if (params) for (var i in params) { this.params[i] = params[i]; }
		
		if (!this.data[fieldName]) this.data[fieldName] = (dateString) ? new Date(this.parseDate(dateString)) : new Date();
		this.d[fieldName] = new Date(this.data[fieldName].getTime());
		
		if (document.getElementById("dp_" + fieldName))
		{
			this.exit(fieldName);
		}
		else this.display(fieldName, e);
	},
	
	parseDate: function(dateStr)
	{
		var a = dateStr.split(" ");
		if (!a[0]) return "";
		var d = a[0].split("-");
		if (!d[0]) return "";
		a[0] = new Array(this.monthArrayShort[d[1] - 1], parseInt(d[2]), parseInt(d[0])).join(" ");
		return a.join(" ");
	},
	
	display: function(fieldName, parent)
	{
		var x = parent.offsetLeft;
		var y = parent.offsetTop + parent.offsetHeight;
		
		while (parent.offsetParent)
		{
			parent = parent.offsetParent;
			x += parent.offsetLeft;
			y += parent.offsetTop;
		}
		
		var node = document.createElement("div");
		node.setAttribute("id", "dp_" + fieldName);
		node.setAttribute("class", "dp_div");
		node.setAttribute("className", "dp_div");
		document.body.appendChild(node);
		
		var div = document.getElementById("dp_" + fieldName);
			
		with (div)
		{
			style.position = "absolute";
			style.left = x + "px";
			style.top = y + "px";
			style.zIndex = 1000000;
		}
		
		div.innerHTML = this.build(fieldName);
		
		if (navigator.userAgent.toLowerCase().indexOf("opera") != -1) return;
		
		try
		{
			if (!document.getElementById("dp_iframe_" + fieldName))
			{
				var newNode = document.createElement("iframe");
				newNode.setAttribute("id", "dp_iframe_" + fieldName);
				newNode.setAttribute("src", "javascript:false;");
				newNode.setAttribute("scrolling", "no");
				newNode.setAttribute ("frameborder", "0");
				document.body.appendChild(newNode);
			}
			
			var iframe = document.getElementById("dp_iframe_" + fieldName);

			try
			{
				with (iframe.style)
				{
					position = "absolute";
					width = div.offsetWidth;
					height = div.offsetHeight;
					top = div.style.top;
					left = div.style.left;
					zIndex = div.style.zIndex - 1;
					visibility = div.style.visibility;
					display = div.style.display;
				}
			} 
			catch(e) {}
		}
		catch (ee) {}
	},
	
	build: function(fieldName)
	{
		var d = this.d[fieldName];
		
		var crlf = "\r\n";
		var html = '<table class="dp_table">' + crlf;
		html += '<tbody>' + crlf;
		
		// header
		html += '<tr>' + crlf;
		html += '<td class="dp_btn" onclick="DatePicker.showHelp();" title="Help">?</td>' + crlf;
		html += '<td class="dp_month" colspan="5">' + this.monthArrayLong[d.getMonth()] +  '</td>' + crlf;
		html += '<td class="dp_btn" onclick="DatePicker.exit(\'' + fieldName + '\');" title="Close">x</td>' + crlf;
		html += '</tr>' + crlf;
		
		// next/prev month/year buttons
		html += '<tr>' + crlf;
		html += '<td class="dp_btn" onclick="DatePicker.setYear(event, -1);" title="Previous Year">&laquo;</td>' + crlf;
		html += '<td class="dp_btn" onclick="DatePicker.setMonth(event, -1);" title="Previous Month">&lt;</td>' + crlf;
		html += '<td class="dp_year" colspan="3">' + d.getFullYear() + '</td>' + crlf;
		html += '<td class="dp_btn" onclick="DatePicker.setMonth(event, 1);" title="Next Month">&gt;</td>' + crlf;
		html += '<td class="dp_btn" onclick="DatePicker.setYear(event, 1);" title="Next Year">&raquo;</td>' + crlf;
		html += '</tr>' + crlf;
		
		// spacer
		html += '<tr height="5" />' + crlf;
		
		// days of week
		html += '<tr>' + crlf;
		for (var i = 0; i < this.dayArrayMed.length; i++) {	html += '<td class="dp_wkdy">' + this.dayArrayMed[i] + '</td>' + crlf; }
		html += '</tr>' + crlf;
		
		html += '<tr id="dp_days">' + this.buildMonth(fieldName) + '</tr>' + crlf;
		
		// spacer
		html += '<tr height="5" />' + crlf;
		
		if (this.params.showTime)
		{
			// time
			html += '<tr>' + crlf;
			html += '<td colspan="7">';
			
			// ::hour
			html += '<select class="dp_select" onchange="DatePicker.setHour(event, this.selectedIndex);">' + crlf;
			
			var setVal = d.getHours();
			
			if (setVal > 11) setVal -= 12;
			
			for (var i = 0; i < 12; i++)
			{
				var selected = (setVal == i) ? " selected" : "";
				var txt = (i == 0) ? "12" : i.toString();
				if (txt.length < 2) txt = "0" + txt;
				
				html += '<option class="dp_select" value="' + i + '"' + selected + '>' + txt + '</option>' + crlf;
			}
			html += "</select>&nbsp;<b>:</b>&nbsp;" 
			
			// ::minute
			html += '<select class="dp_select" onchange="DatePicker.setMinute(event, this.options[this.selectedIndex].value);">' + crlf;
			
			var setVal = d.getMinutes();
			var inc = this.params.minuteIncrement;
			if (d.getMinutes() % inc > 0) setVal += (inc - (d.getMinutes() % inc));
			
			for (var i = 0; i < 60; i += inc)
			{
				var selected = (setVal == i) ? " selected" : "";
				var val = (i < 10) ? "0" + i.toString() : i.toString();
				html += '<option class="dp_select"' + selected + ' value="' + parseInt(val) + '">' + val + '</option>' + crlf;
			}
			html += '</select>&nbsp;';
			
			// ::meridian
			html += '<select class="dp_select" onchange="DatePicker.setMeridian(event, this.selectedIndex);">' + crlf;
			
			var meridianOptions = new Array("AM", "PM");
			var setVal = (d.getHours() < 12) ? 0 : 1;
				
			for (var i = 0; i < meridianOptions.length; i++)
			{
				var selected = (i == setVal) ? " selected" : "";
				html += '<option class="dp_select"' + selected + '>' + meridianOptions[i] + '</option>' + crlf;
			}
			html += '</select>';
			
			html += '</td>' + crlf;
			html += '</tr>' + crlf;
		}
		
		html += '</tbody>' + crlf;
		html += '</table>';
		
		return html;
	},
	
	buildMonth: function(fieldName)
	{
		var d = new Date(this.d[fieldName].getTime());
		d.setDate(1);
		
		var cols = 0;
		var html = "";
		
		var m = new Date(d.getTime());
		m.setMonth(m.getMonth() - 1);
		var daysInPrevMonth = 32 - new Date(m.getFullYear(), m.getMonth(), 32).getDate();
		
		for (var i = 0; i < d.getDay(); i++)
		{
			cols++;
			m.setDate(daysInPrevMonth - (d.getDay() - i) + 1);
	  		html += '<td class="dp_day">' + m.getDate() + '</td>';
		}
		
		do {
			cols++;
			var className = this.match(d, this.data[fieldName]) ? "dp_day_on" : "dp_day_off";
			var onClick = ' onclick="DatePicker.setDate(event, \'' + d.getDate() +  '\');DatePicker.exit(\'' + fieldName + '\');"';
			html += '<td class="' + className + '"' + onClick + '>' + d.getDate() + '</td>';
			if (d.getDay() == 6) html += '</tr><tr>';
			d.setDate(d.getDate() + 1);
		}
		while (d.getDate() > 1);
		
		if (cols < 42)
		{
			m = new Date(d.getTime());
			m.setMonth(d.getMonth());
			m.setDate(1);
			
			for (var i = cols; i < 42; i++)
			{
		  		html += '<td class="dp_day">' + m.getDate() + '</td>';
				if (m.getDay() == 6) html += '</tr><tr>';
				m.setDate(m.getDate() + 1);
			}
		}
		
		return html;
	},
	
	match: function(d1, d2)
	{
		return (d1.getFullYear() == d2.getFullYear() && d1.getMonth() == d2.getMonth() && d1.getDate() == d2.getDate());
	},
	
	setYear: function(event, val)
	{
		this.set(event, 'year', val);
	},
	
	setMonth: function(event, val)
	{
		this.set(event, 'month', val);
	},
	
	setDate: function(event, val)
	{
		this.set(event, 'date', val);
	},
	
	setHour: function(event, val)
	{
		this.set(event, 'hour', val);
	},
	
	setMinute: function(event, val)
	{
		this.set(event, 'minute', val);
	},
	
	setMeridian: function(event, val)
	{
		this.set(event, 'meridian', val);
	},
	
	set: function(event, key, val)
	{
		event.cancelBubble = true;
		var e = (event.srcElement) ? event.srcElement : event.target;
		
		var fieldName = this.getFieldName(event);
		var d = this.d[fieldName];
		var save = false;
		
		switch (key.toLowerCase())
		{
			case 'date':
				d.setDate(val);
				save = true;
			break;
			
			case 'month':
				d.setMonth(d.getMonth() + val);
				var arr = this.getElementsByClassName(e.parentNode.parentNode, "dp_month");
				if (arr[0]) arr[0].innerHTML = this.monthArrayLong[d.getMonth()];
				
				if (d.getMonth() == 0 && val > 0) { key = 'year'; val = 0; }
				else if (d.getMonth() == 11 && val < 0) { key = 'year'; val = 0; }
				else break;
			
			case 'year':
				d.setFullYear(d.getFullYear() + val);
				var arr = this.getElementsByClassName(e.parentNode.parentNode, "dp_year");
				if (arr[0]) arr[0].innerHTML = d.getFullYear();
			break;
			
			case 'minute':
				d.setMinutes(val);
				save = true;
			break;
			
			case 'hour':
				var childNodes = e.parentNode.childNodes;
				if (childNodes[childNodes.length - 1].selectedIndex == 1) val += 12; // adjust for meridian
				d.setHours(val);
				save = true;
			break;
			
			case 'meridian':
				if (val == 0 && d.getHours() > 11) d.setHours(d.getHours() - 12);
				else if (val == 1 && d.getHours() < 12) d.setHours(d.getHours() + 12);
				save = true;
			break;
		}
		
		if (save) this.data[fieldName] = new Date(d.getTime());
		
		var div = document.getElementById("dp_" + fieldName);
		div.innerHTML = this.build(fieldName);
	},
	
	getContainer: function(e, className)
	{
		var parent = e.parentNode;
		
		while (parent.nodeName.toLowerCase() != "body")
		{
			if (parent.className == className) break;
			parent = parent.parentNode;
		}
		
		return parent;
	},
	
	getFieldName: function(event)
	{
		event.cancelBubble = true;
		var e = (event.srcElement) ? event.srcElement : event.target;
		var parent = this.getContainer(e, "dp_div");
		return (parent.id) ? parent.id.substr(3) : "";
	},
	
	getElementsByClassName: function(parent, className)
	{
		if (!parent) var parent = document;
		var arr = new Array(); 
		var elements = parent.getElementsByTagName("*");
		
		for (var cls, i = 0; (e = elements[i]); i++)
		{
			if (e.className == className) arr[arr.length] = e;
		}
		return arr;
	},
	
	showHelp: function()
	{
		var crlf = "\r\n";
		var str = "Date Selection:" + crlf;
		str += "- Use the &laquo; and &raquo; buttons to change the year." + crlf;
		str += "- Use the &lt; and &gt; buttons to change the month." + crlf;
		
		var html_entity_decode = function(s)
		{
			var ta = document.createElement("textarea");
  			ta.innerHTML = s.replace(/</g, "&lt;").replace(/>/g, "&gt;");
  			return ta.value;
		};
		
		alert(html_entity_decode(str));
	},
	
	update: function(fieldName)
	{
		var d = this.data[fieldName];
		$(fieldName).value = d.format(this.params.format);
	},
	
	exit: function(fieldName)
	{
		this.update(fieldName);
		
		var div = document.getElementById("dp_" + fieldName);
		if (div) div.parentNode.removeChild(div);
		
		var iframe = document.getElementById("dp_iframe_" + fieldName);
		if (iframe) iframe.parentNode.removeChild(iframe);

	}
};