Date.fullYearStart = '20';
(function() {
	function add(name, method) {
		if( !Date.prototype[name] ) {
			Date.prototype[name] = method;
		}
	};
	add("isLeapYear", function() {
		var y = this.getFullYear();
		return (y%4==0 && y%100!=0) || y%400==0;
	});
	add("isWeekend", function() {
		return this.getDay()==0 || this.getDay()==6;
	});
	add("isWeekDay", function() {
		return !this.isWeekend();
	});
	add("getDaysInMonth", function() {
		return [31,(this.isLeapYear() ? 29:28),31,30,31,30,31,31,30,31,30,31][this.getMonth()];
	});
	add("getDayName", function(abbreviated) {
		return abbreviated ? Date.abbrDayNames[this.getDay()] : Date.dayNames[this.getDay()];
	});
	add("getMonthName", function(abbreviated) {
		return abbreviated ? Date.abbrMonthNames[this.getMonth()] : Date.monthNames[this.getMonth()];
	});
	add("getDayOfYear", function() {
		var tmpdtm = new Date("1/1/" + this.getFullYear());
		return Math.floor((this.getTime() - tmpdtm.getTime()) / 86400000);
	});
	add("getWeekOfYear", function() {
		return Math.ceil(this.getDayOfYear() / 7);
	});
	add("setDayOfYear", function(day) {
		this.setMonth(0);
		this.setDate(day);
		return this;
	});
	add("subYears", function(num) {
		this.setFullYear(this.getFullYear() - num);
		return this;
	});
	add("addYears", function(num) {
		this.setFullYear(this.getFullYear() + num);
		return this;
	});
	add("addMonths", function(num) {
		var tmpdtm = this.getDate();

		this.setMonth(this.getMonth() + num);

		if (tmpdtm > this.getDate())
			this.addDays(-this.getDate());

		return this;
	});
	add("addDays", function(num) {
		this.setDate(this.getDate() + num);
		return this;
	});
	add("addHours", function(num) {
		this.setHours(this.getHours() + num);
		return this;
	});
	add("addMinutes", function(num) {
		this.setMinutes(this.getMinutes() + num);
		return this;
	});
	add("addSeconds", function(num) {
		this.setSeconds(this.getSeconds() + num);
		return this;
	});
	add("zeroTime", function() {
		this.setMilliseconds(0);
		this.setSeconds(0);
		this.setMinutes(0);
		this.setHours(0);
		return this;
	});
	add("asString", function() {
		var r = Date.format;
		return r
			.split('yyyy').join(this.getFullYear())
			.split('yy').join((this.getFullYear() + '').substring(2))
			.split('mmm').join(this.getMonthName(true))
			.split('mm').join(_zeroPad(this.getMonth()+1))
			.split('dd').join(_zeroPad(this.getDate()));
	});
	Date.fromString = function(s)
	{
		if(!s) return new Date();

		var f = Date.format;
		var d = new Date('01/01/1977');
		var iY = f.indexOf('yyyy');
		if (iY > -1) {
			d.setFullYear(Number(s.substr(iY, 4)));
		} else {
			// TODO - this doesn't work very well - are there any rules for what is meant by a two digit year?
			d.setFullYear(Number(Date.fullYearStart + s.substr(f.indexOf('yy'), 2)));
		}
		var iM = f.indexOf('mmm');
		if (iM > -1) {
			var mStr = s.substr(iM, 3);
			for (var i=0; i<Date.abbrMonthNames.length; i++) {
				if (Date.abbrMonthNames[i] == mStr) break;
			}
			d.setMonth(i);
		} else {
			d.setMonth(Number(s.substr(f.indexOf('mm'), 2)) - 1);
		}
		d.setDate(Number(s.substr(f.indexOf('dd'), 2)));
		if (isNaN(d.getTime())) {
			return false;
		}
		return d;
	};

	// utility method
	var _zeroPad = function(num) {
		var s = '0'+num;
		return s.substring(s.length-2);
		//return ('0'+num).substring(-2); // doesn't work on IE :(
	};

})();
// ============================== Calendar object ============================== //
function isDate(string){
	var re = new RegExp("^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$","i");
	if (re.test(string) == false || string == '') {
		return false;
	}
	return true;
};
function Calendar(selectedStr) {
	this.date = new Date();
	this.html = '';
	this.selectedStr = (selectedStr); // || new Date().asString() ;

};
Calendar.prototype.setDate = function(o) {
	var n = new Date();
	this.month = (isNaN(o.month) || o.month == null) ? n.getMonth() : o.month;
	this.year  = (isNaN(o.year) || o.year == null) ? n.getFullYear() : o.year;
	return {month:this.month,year:this.year};
};
Calendar.prototype.setLinkedDate = function(o) {
	this.linkedDate = o;
};
Calendar.prototype.dateToObject = function(string) {
	var s = Date.fromString(string);
	if(!s || s.getFullYear()<0) var s = new Date();
	//var s = (isDate(string)) ? Date.fromString(string) : new Date();
	return {day:s.getDate(),month:s.getMonth(),year:s.getFullYear()};
};
Calendar.prototype.go = function(go) {
	var o = {month:this.month,year:this.year};
	if(go=="next"&&o.month++>=11){
		o.month=0;o.year++;
	}
	if(go=="prev"&&o.month--<=0){
		o.month=11;o.year--;
	}
	//return this.setDate(o);
	return o;
};
Calendar.prototype.build = function(p) {
	this.generateMonth(this.selectedStr,p);
	for(i=1;i<p.loop;i++) {
		this.setDate(this.go("next"));
		this.generateMonth(this.selectedStr,p);
	}
	return this.html;
};
Calendar.prototype.generateMonth = function(selectedStr,p){
	if(!this.month&&!this.year) this.setDate();

	var fd = new Date(this.year, this.month).getDay() - Date.firstDayOfWeek; // First Day
	var monthLength = new Date(this.year, this.month).getDaysInMonth();
	var monthName = Date.monthNames[this.month];

	var selected = new Date.fromString(selectedStr);
	var today = new Date().zeroTime();
	var maxOutDate = p.outBlock;
	var maxRetDate = p.inBlock;
	var blocked = new Date().zeroTime().addDays(p.blockedDays);
	var linked = (this.linkedDate) ? new Date(this.linkedDate.year,this.linkedDate.month,this.linkedDate.day).zeroTime() : false ;

	var html = '<table class="calendar-table" cellpadding="0" cellspacing="0">';
	html += '<tr><th colspan="7">'+monthName + "&nbsp;" + this.year+'</th></tr>';
	html += '<tr class="calendar-header">';
	for(var i = Date.firstDayOfWeek, j=0; i <= 12; i++ ){
		html += '<td class="calendar-header-day">'+Date.abbrDayNames[(i>6) ? i-7 : i]+'</td>';
		if(j++>=6){ break; }
	}
	html += '</tr><tr>';

	var day = 1;
	for (var i = 0; i < 9; i++) {
	for (var j = 0; j <= 6; j++) {
		html += '<td class="calendar-day">';
		if (day <= monthLength && (i > 0 || j >= ((fd>=0) ? fd : 7+fd))) {
			var calDay = new Date(this.year,this.month,day).zeroTime();
			css = 'month-day';
			css += (calDay-today==0) ? ' is-today' : '';
			css += (calDay.isWeekend()) ? ' is-weekend' : '';
			css += (calDay<blocked && p.blockedDays!=false) ? ' is-blocked' : '';
			css += (calDay<p.blockTo && p.blockTo!=false) ? ' is-disabled' : '';
			css += (calDay>p.blockFrom && p.blockFrom!=false) ? ' is-disabled' : '';
			css += (calDay-selected==0) ? ' is-selected' : '';
			css += (linked&&calDay<linked) ? ' is-linked-disabled' : '';
			css += (linked&&calDay-linked==0) ? ' is-linked' : '';
			css += (!linked&&calDay>maxOutDate || linked&&calDay>maxRetDate) ? ' is-out-of-range' : '';
			html += '<a class="'+css+'" rel="'+calDay.asString()+'">'+day+'</a>';
			day++;
		} else {
			html += '&nbsp;';
		}
		html += '</td>';
	}
	if (day > monthLength) { break; } else { html += '</tr><tr>'; }
	}
	html += '</tr></table>';
	this.html += html;
	return this.html;
};
jQuery.fn.extend({
	esky_calendar_render : function(parameters) {
		$this = $(this);
		var defaults = {
				thisInput: false,
				linkedInput: false,
				month: null,
				year: null,
				loop: 1,
				go: false,
				fade: false,
				outBlock: new Date().zeroTime().addDays(364),
				inBlock: new Date().zeroTime().addDays(363),
				blockTo: new Date().zeroTime(),
				blockFrom: false,
				blockedDays: 0,
				i18n: esky_calendar_translate
			};

		var p = $.extend({},defaults, parameters);
		var tI = document.getElementById(p.thisInput).value;
		var c = new Calendar(tI);

		if (p.linkedInput!=false) {
			var lI = document.getElementById(p.linkedInput).value;
			if (p.thisInput != p.linkedInput && (!p.month && !p.year)) {
				$.extend(p, c.dateToObject(lI));
				c.setLinkedDate(c.dateToObject(lI));
			};
			if (p.thisInput != p.linkedInput && ((!p.month && !p.year) || p.go != false)) {
				c.setLinkedDate(c.dateToObject(lI));
			};
		};

		var d = (!p.month && !p.year) ? c.dateToObject(tI) : p ;
		c.setDate(d);

		var pv = $.extend({},p,c.go("prev"),{go:"prev"});
		var px = $.extend({},p,c.go("next"),{go:"next"});

		var cd = new Date();
		var pc = $.extend({},p,{month:cd.getMonth(),year:cd.getFullYear(),go:true});

		$this
			.html(c.build(p))
			.prepend(
				$('<div class="calendar-navigation"></div>')
				.prepend($('<a href="javascript:void(0);" class="calendar-button calendar-next" title="'+cal_text.NEXT+'">&raquo;</a>').click(function(e){
					$this.esky_calendar_render(px);
					e.stopPropagation();
				}))
				.prepend($('<a href="javascript:void(0);" class="calendar-button calendar-previous" title="'+cal_text.PREV+'">&laquo;</a>').click(function(e){
					$this.esky_calendar_render(pv);
					e.stopPropagation();
				}))
			)
			.append($('<a href="javascript:" class="calendar-button calendar-close">'+cal_text.CLOSE+'</a>').click(function(){$this.remove()}));
 

		$('a.month-day').not('.is-disabled,.is-blocked')
		.click(function(){
			$('#'+p.thisInput).not(':disabled').val( $(this).attr('rel') ).removeClass('virgin');
			$this.remove();
		})
		.mouseover(function(){
			$(this).addClass('is-hover');
		})
		.mouseout(function(){
			$(this).removeClass('is-hover');
		});

		$('a.is-linked').not('.is-disabled').unbind().click(function(){
			alert(p.i18n.OutGreaterThenReturn);
			return false;
		});
		$('a.is-blocked').not('.is-disabled,.is-linked,.is-linked-disabled').unbind().click(function(){
			alert(p.i18n.OutDateToClose);
			return false;
		});
		$('a.is-out-of-range').unbind().click(function(){
			alert(p.i18n.DateOutOfRange);
			return false;
		});
		$('a.is-disabled,a.is-linked-disabled').unbind().click(function(){
			return false;
		});

		$this.bgIframe();
		return $this;
	},

	esky_calendar : function(parameters){
		$('#esky_calendar').remove();
		var $this = $(this);
		var xy = $this.prev().offset();
		var top = (parameters.top) ? parameters.top : xy.top;
		var left = (parameters.left) ? parameters.left : xy.left;
		$('body').append(
			$('<div></div>')
			.attr('id','esky_calendar')
			.css({position:'absolute'})
			.hide()
		);
		if(parameters&&parameters.fade>0){
			$('#esky_calendar').esky_calendar_render(parameters).fadeIn(parameters.fade);
		}else{
			$('#esky_calendar').esky_calendar_render(parameters).show();
		}
		return $this;
	}
});

