/**
 * Date picker
 * Author: Stefan Petre www.eyecon.ro
 * Uses JavaScript Micro-Templating by John Resig - http://ejohn.org/
 */

// JSLint validation
/*global document, formatDate, hide, jQuery, prepareDate, tmpl*/

(function ($) {
	var DatePicker = function () {
    var iPickerCount = 0;
		var	tpl = {
			//wrapper: '<div class="datepicker"><div class="datepickerContainer"><table cellspacing="0" cellpadding="0"><tbody><tr></tr></tbody></table></div></div>',
      wrapper: '<div class="datepicker"><table cellspacing="0" cellpadding="0"><tbody><tr></tr></tbody></table></div>',
			head: [
				'<td>',
				'<table cellspacing="0" cellpadding="0">',
					'<thead>',
						'<tr class="datepickerHeadRow">',
							'<th class="datepickerGoPrev"><a href="#"><span><%=prev%></span></a></th>',
							'<th colspan="5" class="datepickerMonth"><a href="#"><span></span></a></th>',
							'<th class="datepickerGoNext"><a href="#"><span><%=next%></span></a></th>',
						'</tr>',
						'<tr class="datepickerDoW">',
							'<th><span><%=day1%></span></th>',
							'<th><span><%=day2%></span></th>',
							'<th><span><%=day3%></span></th>',
							'<th><span><%=day4%></span></th>',
							'<th><span><%=day5%></span></th>',
							'<th><span><%=day6%></span></th>',
							'<th><span><%=day7%></span></th>',
						'</tr>',
					'</thead>',
				'</table></td>'
			],
			days: [
				'<tbody class="datepickerDays">',
					'<tr>',
						'<td class="<%=weeks[0].days[0].classname%>"><a href="#"><span><%=weeks[0].days[0].text%></span></a></td>',
						'<td class="<%=weeks[0].days[1].classname%>"><a href="#"><span><%=weeks[0].days[1].text%></span></a></td>',
						'<td class="<%=weeks[0].days[2].classname%>"><a href="#"><span><%=weeks[0].days[2].text%></span></a></td>',
						'<td class="<%=weeks[0].days[3].classname%>"><a href="#"><span><%=weeks[0].days[3].text%></span></a></td>',
						'<td class="<%=weeks[0].days[4].classname%>"><a href="#"><span><%=weeks[0].days[4].text%></span></a></td>',
						'<td class="<%=weeks[0].days[5].classname%>"><a href="#"><span><%=weeks[0].days[5].text%></span></a></td>',
						'<td class="<%=weeks[0].days[6].classname%>"><a href="#"><span><%=weeks[0].days[6].text%></span></a></td>',
					'</tr>',
					'<tr>',
						'<td class="<%=weeks[1].days[0].classname%>"><a href="#"><span><%=weeks[1].days[0].text%></span></a></td>',
						'<td class="<%=weeks[1].days[1].classname%>"><a href="#"><span><%=weeks[1].days[1].text%></span></a></td>',
						'<td class="<%=weeks[1].days[2].classname%>"><a href="#"><span><%=weeks[1].days[2].text%></span></a></td>',
						'<td class="<%=weeks[1].days[3].classname%>"><a href="#"><span><%=weeks[1].days[3].text%></span></a></td>',
						'<td class="<%=weeks[1].days[4].classname%>"><a href="#"><span><%=weeks[1].days[4].text%></span></a></td>',
						'<td class="<%=weeks[1].days[5].classname%>"><a href="#"><span><%=weeks[1].days[5].text%></span></a></td>',
						'<td class="<%=weeks[1].days[6].classname%>"><a href="#"><span><%=weeks[1].days[6].text%></span></a></td>',
					'</tr>',
					'<tr>',
						'<td class="<%=weeks[2].days[0].classname%>"><a href="#"><span><%=weeks[2].days[0].text%></span></a></td>',
						'<td class="<%=weeks[2].days[1].classname%>"><a href="#"><span><%=weeks[2].days[1].text%></span></a></td>',
						'<td class="<%=weeks[2].days[2].classname%>"><a href="#"><span><%=weeks[2].days[2].text%></span></a></td>',
						'<td class="<%=weeks[2].days[3].classname%>"><a href="#"><span><%=weeks[2].days[3].text%></span></a></td>',
						'<td class="<%=weeks[2].days[4].classname%>"><a href="#"><span><%=weeks[2].days[4].text%></span></a></td>',
						'<td class="<%=weeks[2].days[5].classname%>"><a href="#"><span><%=weeks[2].days[5].text%></span></a></td>',
						'<td class="<%=weeks[2].days[6].classname%>"><a href="#"><span><%=weeks[2].days[6].text%></span></a></td>',
					'</tr>',
					'<tr>',
						'<td class="<%=weeks[3].days[0].classname%>"><a href="#"><span><%=weeks[3].days[0].text%></span></a></td>',
						'<td class="<%=weeks[3].days[1].classname%>"><a href="#"><span><%=weeks[3].days[1].text%></span></a></td>',
						'<td class="<%=weeks[3].days[2].classname%>"><a href="#"><span><%=weeks[3].days[2].text%></span></a></td>',
						'<td class="<%=weeks[3].days[3].classname%>"><a href="#"><span><%=weeks[3].days[3].text%></span></a></td>',
						'<td class="<%=weeks[3].days[4].classname%>"><a href="#"><span><%=weeks[3].days[4].text%></span></a></td>',
						'<td class="<%=weeks[3].days[5].classname%>"><a href="#"><span><%=weeks[3].days[5].text%></span></a></td>',
						'<td class="<%=weeks[3].days[6].classname%>"><a href="#"><span><%=weeks[3].days[6].text%></span></a></td>',
					'</tr>',
					'<tr>',
						'<td class="<%=weeks[4].days[0].classname%>"><a href="#"><span><%=weeks[4].days[0].text%></span></a></td>',
						'<td class="<%=weeks[4].days[1].classname%>"><a href="#"><span><%=weeks[4].days[1].text%></span></a></td>',
						'<td class="<%=weeks[4].days[2].classname%>"><a href="#"><span><%=weeks[4].days[2].text%></span></a></td>',
						'<td class="<%=weeks[4].days[3].classname%>"><a href="#"><span><%=weeks[4].days[3].text%></span></a></td>',
						'<td class="<%=weeks[4].days[4].classname%>"><a href="#"><span><%=weeks[4].days[4].text%></span></a></td>',
						'<td class="<%=weeks[4].days[5].classname%>"><a href="#"><span><%=weeks[4].days[5].text%></span></a></td>',
						'<td class="<%=weeks[4].days[6].classname%>"><a href="#"><span><%=weeks[4].days[6].text%></span></a></td>',
					'</tr>',
					'<tr>',
						'<td class="<%=weeks[5].days[0].classname%>"><a href="#"><span><%=weeks[5].days[0].text%></span></a></td>',
						'<td class="<%=weeks[5].days[1].classname%>"><a href="#"><span><%=weeks[5].days[1].text%></span></a></td>',
						'<td class="<%=weeks[5].days[2].classname%>"><a href="#"><span><%=weeks[5].days[2].text%></span></a></td>',
						'<td class="<%=weeks[5].days[3].classname%>"><a href="#"><span><%=weeks[5].days[3].text%></span></a></td>',
						'<td class="<%=weeks[5].days[4].classname%>"><a href="#"><span><%=weeks[5].days[4].text%></span></a></td>',
						'<td class="<%=weeks[5].days[5].classname%>"><a href="#"><span><%=weeks[5].days[5].text%></span></a></td>',
						'<td class="<%=weeks[5].days[6].classname%>"><a href="#"><span><%=weeks[5].days[6].text%></span></a></td>',
					'</tr>',
				'</tbody>'
			],
			months: [
				'<tbody class="datepickerMonths">',
					'<tr>',
						'<td class="<%=months[0].classname%>" colspan="2"><a href="#"><span><%=months[0].text%></span></a></td>',
						'<td class="<%=months[1].classname%>" colspan="2"><a href="#"><span><%=months[1].text%></span></a></td>',
						'<td class="<%=months[2].classname%>" colspan="2"><a href="#"><span><%=months[2].text%></span></a></td>',
						'<td class="<%=months[3].classname%>" colspan="2"><a href="#"><span><%=months[3].text%></span></a></td>',
					'</tr>',
					'<tr>',
						'<td class="<%=months[4].classname%>" colspan="2"><a href="#"><span><%=months[4].text%></span></a></td>',
						'<td class="<%=months[5].classname%>" colspan="2"><a href="#"><span><%=months[5].text%></span></a></td>',
						'<td class="<%=months[6].classname%>" colspan="2"><a href="#"><span><%=months[6].text%></span></a></td>',
						'<td class="<%=months[7].classname%>" colspan="2"><a href="#"><span><%=months[7].text%></span></a></td>',
					'</tr>',
					'<tr>',
						'<td class="<%=months[8].classname%>" colspan="2"><a href="#"><span><%=months[8].text%></span></a></td>',
						'<td class="<%=months[9].classname%>" colspan="2"><a href="#"><span><%=months[9].text%></span></a></td>',
						'<td class="<%=months[10].classname%>" colspan="2"><a href="#"><span><%=months[10].text%></span></a></td>',
						'<td class="<%=months[11].classname%>" colspan="2"><a href="#"><span><%=months[11].text%></span></a></td>',
					'</tr>',
				'</tbody>'
			]
		},
		defaults = {
			prev: '<img src="images/arrow_left.png" alt="" />',
			next: '<img src="images/arrow_right.png" alt="" />',
			format: 'Y-m-d',
			onRender: function(){return {};},
			onChange: function(){return true;},
			onShow: function(){return true;},
			onBeforeShow: function(){return true;},
			onHide: function(){return true;},
			locale: {
				days: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"],
				daysShort: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"],
				months: ["Januar", "Februar", "M&auml;rz", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"],
				monthsShort: ["Jan.", "Feb.", "M&auml;rz", "Apr.", "Mai", "Juni", "Juli", "Aug.", "Sep.", "Okt.", "Nov.", "Dez."]
			}
		},
		fill = function(el) {
			var options = $(el).data('datepicker');
			var cal = $(el);
			var date, data, dow, month, cnt = 0, week, days, indic, indic2, html, tblCal;
			cal.find('td>table tbody').remove();
			date = new Date(options.current);
			tblCal = cal.find('table').eq(1);
			if (tblCal[0].className == 'datepickerViewDays') {
				dow = formatDate(date, 'B Y');
			} else {
				dow = date.getFullYear();
			}
			tblCal.find('thead tr:first th:eq(1) span').html(dow);
			date.setDate(1);
			data = {weeks:[]};
			month = date.getMonth();
			dow = (date.getDay() - 1) % 7;
			date.addDays(-(dow + (dow < 0 ? 7 : 0)));
			week = -1;
			cnt = 0;
			while (cnt < 42) {
				indic = parseInt(cnt/7,10);
				indic2 = cnt%7;
				if (!data.weeks[indic]) {
					data.weeks[indic] = {
						days: []
					};
				}
				data.weeks[indic].days[indic2] = {
					text: date.getDate(),
					classname: []
				};
				if (month != date.getMonth()) {
					data.weeks[indic].days[indic2].classname.push('datepickerNotInMonth');
				}
				if (date.getDay() === 0) {
					data.weeks[indic].days[indic2].classname.push('datepickerSunday');
				}
				if (date.getDay() == 6) {
					data.weeks[indic].days[indic2].classname.push('datepickerSaturday');
				}
				var fromUser = options.onRender(date);
				var val = date.valueOf();
				if (fromUser.selected || options.date == val || $.inArray(val, options.date) > -1) {
					data.weeks[indic].days[indic2].classname.push('datepickerSelected');
				}
				if (fromUser.disabled) {
					data.weeks[indic].days[indic2].classname.push('datepickerDisabled');
				}
				if (fromUser.className) {
					data.weeks[indic].days[indic2].classname.push(fromUser.className);
				}
				data.weeks[indic].days[indic2].classname = data.weeks[indic].days[indic2].classname.join(' ');
				cnt++;
				date.addDays(1);
			}
			html = tmpl(tpl.days.join(''), data);
			data = {months: []};
			for (cnt = 0; cnt < 12; cnt++) {
				if (!data.months[cnt]) {
					data.months[cnt] = {};
				}
				data.months[cnt].text = options.locale.monthsShort[cnt];
				if ((cnt < Date.today().getMonth() && date.getYear() == (date.getMonth() === 0 ? Date.today().getYear() + 1 : Date.today().getYear()))
						|| date.getYear() < (date.getMonth() === 0 ? Date.today().getYear() + 1 : Date.today().getYear())) {
					//data.months[cnt].classname = "datepickerDisabled";
				} else {
					data.months[cnt].classname = "";
				}
			}
			html = tmpl(tpl.months.join(''), data) + html;
			tblCal.append(html);
		},
		parseDate = function (date, format) {
			if (date.constructor == Date) {
				return new Date(date);
			}
			var parts = date.split(/\W+/);
			var against = format.split(/\W+/), d, m, y, h, min, now = new Date();
			for (var i = 0; i < parts.length; i++) {
				switch (against[i]) {
					case 'd':
					case 'e':
						d = parseInt(parts[i],10);
						break;
					case 'm':
						m = parseInt(parts[i], 10) - 1;
						break;
					case 'Y':
					case 'y':
						y = parseInt(parts[i], 10);
						y += y > 100 ? 0 : (y < 29 ? 2000 : 1900);
						break;
					case 'H':
					case 'I':
					case 'k':
					case 'l':
						h = parseInt(parts[i], 10);
						break;
					case 'P':
					case 'p':
						if (/pm/i.test(parts[i]) && h < 12) {
							h += 12;
						} else if (/am/i.test(parts[i]) && h >= 12) {
							h -= 12;
						}
						break;
					case 'M':
						min = parseInt(parts[i], 10);
						break;
				}
			}
			return new Date(
				y||now.getFullYear(),
				m||now.getMonth(),
				d||now.getDate(),
				h||now.getHours(),
				min||now.getMinutes(), 0);
		},
		formatDate = function(date, format) {
			var m = date.getMonth();
			var d = date.getDate();
			var y = date.getFullYear();
			var w = date.getDay();
			var s = {};
			var hr = date.getHours();
			var min = date.getMinutes();
			var sec = date.getSeconds();
			var parts = format.split(''), part;
			for (var i = 0; i < parts.length; i++) {
				part = parts[i];
				switch (parts[i]) {
					case 'a':
						part = date.getDayName();
						break;
					case 'A':
						part = date.getDayName(true);
						break;
					case 'b':
						part = date.getMonthName();
						break;
					case 'B':
						part = date.getMonthName(true);
						break;
					case 'C':
						part = 1 + Math.floor(y / 100);
						break;
					case 'd':
						part = (d < 10) ? ("0" + d) : d;
						break;
					case 'e':
						part = d;
						break;
					case 'H':
						part = (hr < 10) ? ("0" + hr) : hr;
						break;
					case 'k':
						part = hr;
						break;
					case 'm':
						part = (m < 9) ? ("0" + (1+m)) : (1+m);
						break;
					case 'M':
						part = (min < 10) ? ("0" + min) : min;
						break;
					case 's':
						part = Math.floor(date.getTime() / 1000);
						break;
					case 'S':
						part = (sec < 10) ? ("0" + sec) : sec;
						break;
					case 'u':
						part = w + 1;
						break;
					case 'w':
						part = w;
						break;
					case 'Y':
						part = y;
						break;
				}
				parts[i] = part;
			}
			return parts.join('');
		},
		extendDate = function(options) {
			if (Date.prototype.tempDate) {
				return;
			}
			Date.prototype.tempDate = null;
			Date.prototype.months = options.months;
			Date.prototype.monthsShort = options.monthsShort;
			Date.prototype.days = options.days;
			Date.prototype.daysShort = options.daysShort;
			Date.prototype.getMonthName = function(fullName) {
				return this[fullName ? 'months' : 'monthsShort'][this.getMonth()];
			};
			Date.prototype.getDayName = function(fullName) {
				return this[fullName ? 'days' : 'daysShort'][this.getDay()];
			};
			Date.prototype.addDays = function (n) {
				this.setDate(this.getDate() + n);
				this.tempDate = this.getDate();
			};
			Date.prototype.addMonths = function (n) {
				if (this.tempDate === null) {
					this.tempDate = this.getDate();
				}
				this.setDate(1);
				this.setMonth(this.getMonth() + n);
				this.setDate(Math.min(this.tempDate, this.getMaxDays()));
			};
			Date.prototype.addYears = function (n) {
				if (this.tempDate === null) {
					this.tempDate = this.getDate();
				}
				this.setDate(1);
				this.setFullYear(this.getFullYear() + n);
				this.setDate(Math.min(this.tempDate, this.getMaxDays()));
			};
			Date.prototype.getMaxDays = function() {
				var tmpDate = new Date(Date.parse(this)),
					d = 28, m = tmpDate.getMonth();
				while (tmpDate.getMonth() == m) {
					d++;
					tmpDate.setDate(d);
				}
				return d - 1;
			};
		},
		click = function(ev) {
			if ($(ev.target).is('img')) {
				ev.target = ev.target.parentNode;
			}
			if ($(ev.target).is('span')) {
				ev.target = ev.target.parentNode;
			}
			var el = $(ev.target);
			if (el.is('a')) {
				ev.target.blur();
				if (el.hasClass('datepickerDisabled')) {
					return false;
				}
				var options = $(this).data('datepicker');
				var parentEl = el.parent();
				var tblEl = parentEl.parent().parent().parent();
				var tblIndex = $('table', this).index(tblEl.get(0)) - 1;
				var tmp = new Date(options.current);
				var changed = false;
				var fillIt = false;
				if (parentEl.is('th')) {
					if (parentEl.hasClass('datepickerMonth')) {
						tmp.addMonths(tblIndex - 0);
						switch (tblEl.get(0).className) {
							case 'datepickerViewDays':
								tblEl.get(0).className = 'datepickerViewMonths';
								el.find('span').html(tmp.getFullYear());
								break;
							case 'datepickerViewMonths':
								tblEl.get(0).className = 'datepickerViewDays';
								el.find('span').html(formatDate(tmp, 'B Y'));
								break;
						}
					} else if (parentEl.parent().parent().is('thead')) {
						switch (tblEl.get(0).className) {
							case 'datepickerViewDays':
								options.current.addMonths(parentEl.hasClass('datepickerGoPrev') ? -1 : 1);
								break;
							case 'datepickerViewMonths':
								options.current.addYears(parentEl.hasClass('datepickerGoPrev') ? -1 : 1);
								break;
						}
						fillIt = true;
					}
				} else if (parentEl.is('td') && !parentEl.hasClass('datepickerDisabled')) {
					if (tblEl.get(0).className == 'datepickerViewMonths') {
						options.current.setMonth(tblEl.find('tbody.datepickerMonths td').index(parentEl));
						options.current.setFullYear(parseInt(tblEl.find('thead th.datepickerMonth span').text(), 10));
						options.current.addMonths(0 - tblIndex);
						tblEl.get(0).className = 'datepickerViewDays';
					} else {
						var val = parseInt(el.text(), 10);
						tmp.addMonths(tblIndex - 0);
						if (parentEl.hasClass('datepickerNotInMonth')) {
							tmp.addMonths(val > 15 ? -1 : 1);
						}
						tmp.setDate(val);
						options.date = tmp.valueOf();
						changed = true;
					}
					fillIt = true;
				}
				if (fillIt) {
					fill(this);
				}
				if (changed) {
					options.onChange.apply(this, prepareDate(options));
				}
			}
			return false;
		},
		prepareDate = function (options) {
			var tmp = new Date(options.date);
			return [formatDate(tmp, options.format), tmp];
		},
		isChildOf = function(parentEl, el, container) {
			if (parentEl == el) {
				return true;
			}
			if (parentEl.contains) {
				return parentEl.contains(el);
			}
			if ( parentEl.compareDocumentPosition ) {
				return !!(parentEl.compareDocumentPosition(el) & 16);
			}
			var prEl = el.parentNode;
			while (prEl && prEl != container) {
				if (prEl == parentEl) {
					return true;
				}
				prEl = prEl.parentNode;
			}
			return false;
		},
		show = function (ev) {
			var cal = $('#' + $(this).data('datepickerId'));
			if (!cal.is(':visible')) {
				var options = cal.data('datepicker');
				options.onBeforeShow.apply(this, [cal.get(0)]);
				if (options.onShow.apply(this, [cal.get(0)]) !== false) {
					cal.slideDown("fast");
				}
				$(document).bind('mousedown', {cal: cal, trigger: this}, hide);
			}
			return false;
		},
		hide = function (ev) {
			if (ev.target != ev.data.trigger && !isChildOf(ev.data.cal.get(0), ev.target, ev.data.cal.get(0))) {
				if (ev.data.cal.data('datepicker').onHide.apply(this, [ev.data.cal.get(0)]) !== false) {
					ev.data.cal.slideUp("normal");
				}
				$(document).unbind('mousedown', hide);
			}
		};
		return {
			init: function(options) {
        iPickerCount++;
				options = $.extend({}, defaults, options||{});
				extendDate(options.locale);
				return this.each(function() {
					if (!$(this).data('datepicker')) {
						if (options.date.constructor == String) {
							options.date = parseDate(options.date, options.format);
							options.date.setHours(0,0,0,0);
						}
						options.date = options.date.valueOf();
						if (!options.current) {
							options.current = new Date();
						} else {
							options.current = parseDate(options.current, options.format);
						} 
						options.current.setDate(1);
						options.current.setHours(0,0,0,0);
						options.id = 'datepicker_' + iPickerCount;
						$(this).data('datepickerId', options.id);
						var cal = $(tpl.wrapper).attr('id', options.id).bind('click', click).data('datepicker', options);
						if (options.className) {
							cal.addClass(options.className);
						}
						var cnt = 1;
						cal.find('tr:first').append(
							tmpl(tpl.head.join(''), {
								prev: options.prev,
								next: options.next,
								day1: options.locale.daysShort[(cnt++)%7],
								day2: options.locale.daysShort[(cnt++)%7],
								day3: options.locale.daysShort[(cnt++)%7],
								day4: options.locale.daysShort[(cnt++)%7],
								day5: options.locale.daysShort[(cnt++)%7],
								day6: options.locale.daysShort[(cnt++)%7],
								day7: options.locale.daysShort[(cnt++)%7]
							})
						);
						cal.find('tr:first table').addClass('datepickerViewDays');
						fill(cal.get(0));
						cal.appendTo("#datePickerHook");
						$(this).bind('click', show);
					}
				});
			},
			showPicker: function() {
				return this.each(function() {
					if ($(this).data('datepickerId')) {
						show.apply(this);
					}
				});
			},
			hidePicker: function() {
				return this.each(function() {
					if ($(this).data('datepickerId')) {
						$('#' + $(this).data('datepickerId')).hide();
					}
				});
			},
			setDate: function(date, shiftTo) {
				return this.each(function() {
					if ($(this).data('datepickerId')) {
						var cal = $('#' + $(this).data('datepickerId'));
						var options = cal.data('datepicker');
						options.date = date;
						if (options.date.constructor == String) {
							options.date = parseDate(options.date, options.format);
							options.date.setHours(0,0,0,0);
						}
						options.date = options.date.valueOf();
						if (shiftTo) {
							options.current = new Date (options.date);
						}
						fill(cal.get(0));
					}
				});
			},
			getDate: function(formated) {
				if (this.size() > 0) {
					return prepareDate($('#' + $(this).data('datepickerId')).data('datepicker'))[formated ? 0 : 1];
				}
			}
		};
	}();
	$.fn.extend({
		DatePicker: DatePicker.init,
		DatePickerHide: DatePicker.hide,
		DatePickerShow: DatePicker.show,
		DatePickerSetDate: DatePicker.setDate,
		DatePickerGetDate: DatePicker.getDate
	});
})(jQuery);
