function Calendar(contentDiv)
{
	this.init = function(contentDiv)
	{
		//	settings
		this.aMonths = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ];
		this.aDays = [ "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su" ];
		this.isSaturdayRed = true;
		this.isSundayRed = true;
		this.isUSformat = false;
		this.minDate = new Date(2000, 0, 1);
		this.maxDate = new Date(2049, 11, 31);
		this.todayText = "today";
		this.cellWidth = 22;
		this.showToday = false;

		//	init		
		this.date = null;
		this.month = null;
		this.year = null;
		this.isOpen = false;
		this.onDocumentKeyHandler = this.onWindowKeyHandler = this.onFocusHandler = null;

		if (this.isUSformat)
		{
			this.getWeekDay = this.getWeekDayUS;
			var tmpDay = this.aDays[0];
			this.aDays[0] = this.aDays[6];
			this.aDays[6] = tmpDay;
			this.saturdayIndex = 6;
			this.sundayIndex = 0;
		}
		else
		{
			this.getWeekDay = this.getWeekDayEurope;
			this.saturdayIndex = 5;
			this.sundayIndex = 6;
		}
		
		this.createPopup(contentDiv);
		return this;
	}

	this.getWeekDayUS = function(date)
	{
		return date.getDay();
	}
	
	this.getWeekDayEurope = function(date)
	{
		return (date.getDay() + 6) % 7;
	}

	this.isRed = function(ndx)
	{
	    return ndx == this.saturdayIndex && this.isSaturdayRed
			|| ndx == this.sundayIndex && this.isSundayRed;
	}
    
	//this.isDutchMode = false;
	this.isHolidayMode = true;

	this.isHoliday = function(odate)
	{
	    //should be overriden in CPCalendar control	    
	    return false;
	}
		
	this.isDutchHoliday = function(odate)
	{
	    //here month start from 0, i.e. real month - 1;
	    //day start from 1
	    var d = odate.getDate();
	    var m = odate.getMonth() + 1;
	    var y = odate.getFullYear();

 /* 
list of holidays 
Christmas holiday 2011             Saturday 24 December till Sunday 8 January 2012
Good Friday                             Friday 6 April 2012
Easter                                      Saturdays 7 April - Monday 9 April 2012
Queensday                               Monday 30 April 2012
Ascension day                         Thursday 17 May 2012
2e pinksterdag (Whitsunday)      Sunday 27  May - Monday 28 May 2012

 */


	    if (y == 2011 && m == 12 && d >= 24) return true;
	    if (y == 2012 && m == 1 && d <= 8) return true;
	    if (y == 2012 && m == 4 && d >= 6 && d <= 9) return true;
	    if (y == 2012 && m == 4 && d == 30) return true;
	    if (y == 2012 && m == 5 && d == 17) return true;
	    if (y == 2012 && m == 5 && (d == 27 || d == 28)) return true;
	    return false;
	}
	
	
	this.createPopup = function(contentDiv)
	{
		this.oPopup = contentDiv;
		this.oPopup.innerHTML = this.buildCalendar();
		document.CE = this;
	}

	this.buildCalendar = function()
	{
		var content = "";
		content += "<div class='calendarDiv' onmouseup='try{window.getSelection().removeAllRanges();}catch(e){}' style='width:"
			+ (this.cellWidth * 7) + "px; height:" + (this.showToday? 152 : 134) + "px;border:solid 1px black'>";
		
		content += "<table border=0 cellspacing=0 cellpadding=0 width='100%' height='100%'><tr><td valign='top'>"
		//	month and year selectors
		content += "<table onselectstart='return false;' border=0 width='100%' cellpadding=0 cellspacing=0 class='gray' height=1><tr> \
			<td class='slider'> \
				<img class='hand' onmouseup='document.CE.slideYear(-1);' title='Previous year' src='/celsiusproapp/images/calendar/ll.gif' /> \
			</td><td class='slider'> \
				<img class='hand' onmouseup='document.CE.slideMonth(-1);' title='Previous month' src='/celsiusproapp/images/calendar/l.gif' /> \
			</td><td align='center' valign='middle'><b><nobr id='cld$MonthYear'>---<nobr></b></td> \
			</td><td class='slider'> \
				<img class='hand' onmouseup='document.CE.slideMonth(1);' title='Next month' src='/celsiusproapp/images/calendar/r.gif' /> \
			<td class='slider'> \
				<img class='hand' onmouseup='document.CE.slideYear(1);' title='Next year' src='/celsiusproapp/images/calendar/rr.gif' /> \
			</td></tr></table>";

		//	date selector
		content += "<div id='cld$Grid' style='width:100%'>---</div>";
		
		//	today selector
		if (this.showToday)
		{
			content += "</td></tr><tr><td valign='bottom'><div id='cld$findToday' onselectstart='return false;' class='today' \
				onclick='document.CE.setDate();' title='Find today'>"
				+ this.todayText + "</div>";
		}
		content += "</td></tr></table> </div>";
		return content;
	}
	
	this.buildGrid = function(argDate)
	{
		var pointGif = "/celsiusproapp/images/empty.gif";
		var styleToday = " style='border:solid 1px #aaa'";
		
		this.date = argDate.getDate();
		this.month = argDate.getMonth();
		this.year = argDate.getFullYear();
		
		try { this.ge("cld$findToday").className = this.isDisabled(new Date())? "todayDisabled" : "today"; }
		catch(e){}
		
		var iDate;
		var sCurr = this.year + " ";
		var sToday = new Date();
		sToday = sToday.getFullYear() + " " + sToday.getMonth() + "" + sToday.getDate();
		var oDate = new Date(argDate);
		oDate.setDate(1);
		var iWeekDay = this.getWeekDay(oDate);
		
		var widths = "";
		var tdDays = ""
		for (var i = 0; i < 7; i++)
		{
			widths += "<td><img src='" + pointGif + "' width=" + this.cellWidth + " height=1 /></td>";
			tdDays += "<th"
				+ (this.isRed(i)? " class='red'>" : ">") 
				+ this.aDays[i] + "</th>";
		}
		
		var content = "<table onselectstart='return false;' border=0 cellspacing=0 cellpadding=0 width='1' class='grid'>"
			+ "<tr>" + widths + "</tr>" 
			+ "<tr>" + tdDays + "</tr>";
		var td;
		
		// previous month
		var oPrev = new Date(oDate);
		oPrev.setDate(1 - iWeekDay);
		var edgeMonth = this.month - 1;
		for (iDate = oPrev.getDate(); iWeekDay > 0; iWeekDay--)
		{
			td = "<td ";
			if (this.isDisabled(oPrev))
				td += " class='disabled'";
			else
				td += " onclick='document.CE.slideDateTo(null, " + edgeMonth + ", " + iDate + ", 1)' class='edge'";
			
			if (sCurr + edgeMonth + iDate == sToday)
				td += styleToday;
			
			content += td + ">" + iDate + "</td>";
			++iDate;
			oPrev.setDate(iDate);
		}
		
		// current month
		for (iDate = 1; oDate.getMonth() == this.month; oDate.setDate(++iDate))
		{
			td = "<td ";
			iWeekDay = this.getWeekDay(oDate);
			var dateText = iDate;

			if (this.isDisabled(oDate) || (this.isHolidayMode && this.isHoliday(oDate)) )
				td += "class='disabled'";
			else
			{
			    if (this.isRed(iWeekDay))
			        dateText = "<font color='red'>" + dateText + "</font>";

			    td += "id='cld$_" + iDate + "' onclick='document.CE.slideDateTo(null, null, " + iDate + ", 1)' ondblclick='document.CE.selectDate()'"
					+ (iDate == this.date? " class='selected'" : "")
			}
			if (sCurr + this.month + iDate == sToday)
				td += styleToday;

			content += td + ">" + dateText + "</td>";
			if (iWeekDay == 6)
				content += "</tr><tr>";
		}
		
		// next month
		edgeMonth = this.month + 1;
		for (iDate = 1; iWeekDay < 6; iWeekDay++)
		{
			td = "<td ";
			if (this.isDisabled(oDate))
				td += " class='disabled'";
			else
				td += " onclick='document.CE.slideDateTo(null, " + edgeMonth + ", " + iDate + ", 1)' class='edge'";
			
			if (sCurr + edgeMonth + iDate == sToday)
				td += styleToday;
			
			content += td + ">" + iDate + "</td>";
			++iDate;
			oDate.setDate(iDate);
		}
		content += "</tr></table>";
		return content;
	}
	
	this.isDisabled = function(dt)
	{
		return dt.getTime() < this.minDate.getTime()
			|| dt.getTime() > this.maxDate.getTime();
	}
	
	this.markDate = function(date)
	{
		try
		{
			this.ge("cld$_" + date).className = "selected"; 
			this.ge("cld$_" + this.date).className = "";			
		} catch(e) { }
		this.date = date;
	}
	
	this.slideDateTo = function(year, month, date, doSelect)
	{
		if (year == null) year = this.year;
		if (month == null) month = this.month;
		if (date == null) date = this.date;
		this.setDate(year, month, date, true);
		
		if (doSelect)
			this.selectDate();
	}
	
	this.slideDay = function(days, doSelect)
	{
		this.slideDateTo(null, null, this.date + days, doSelect);
	}
	
	this.slideMonth = function(months, doSelect)
	{
		oDate = new Date(this.year, this.month + months, this.date);
		if (oDate.getDate() != this.date)
			oDate.setDate(0);
		this.slideDateTo(oDate.getFullYear(), oDate.getMonth(), oDate.getDate(), doSelect);
	}
	
	this.slideYear = function(years, doSelect)
	{
		this.slideMonth(12 * years, doSelect);
	}
	
	this.onKey = function(ev)
	{
		if (ev != null)
			event = ev;
		event.cancelBubble = true;
		switch (event.keyCode)
		{
			case 37:	this.slideDay(-1);	break;
			case 39:	this.slideDay(1);	break;
			case 38:	this.slideDay(-7);	break;
			case 40:	this.slideDay(7);	break;
			case 33:	event.shiftKey || event.ctrlKey? this.slideYear(-1) : this.slideMonth(-1);	break;
			case 34:	event.shiftKey || event.ctrlKey? this.slideYear(1) : this.slideMonth(1);	break;
			case 13:	this.selectDate();	break;
			case 27:	this.hide();
			default:	return;
		}
		if (this.isOpen)
			this.ge("cld$Grid").focus();
		try { event.preventDefault(); } catch(e) {}
		event.returnValue = false;
	}
	
	this.setDate = function(year, month, date, selfCall)
	{
		var oDate;
		if (arguments.length == 0)
		{
			oDate = new Date();
			selfCall = true;
		}
		else
		{
			if (!selfCall)
			{
				//	normalizing
				month = Math.max(0, Math.min(11, month));
				date = Math.max(1, Math.min(31, date));
				oDate = new Date(year, month, date);
				if (oDate.getDate() != date)
					oDate.setDate(0);
			}
			else
				oDate = new Date(year, month, date);
		}
		
		//date = Math.max(1, Math.min(31, date));
		
		//	don't allow set disabled date from outside
		if (!selfCall && this.isDisabled(oDate))
			return;
		
		if (oDate.getTime() != new Date(this.year, this.month, this.date).getTime())
		{
			if (oDate.getFullYear() == this.year && oDate.getMonth() == this.month)
			{
				//	if the same month selected
				if (this.isDisabled(oDate))
					return;
				else
					this.markDate(oDate.getDate());
			}
			else	//	another month selected
			{
				//	move date to edge position
				if (oDate < this.minDate)
					oDate = new Date(this.minDate);
				else if (oDate > this.maxDate)
					oDate = new Date(this.maxDate);

				this.ge("cld$MonthYear").innerHTML = this.aMonths[oDate.getMonth()] + "&nbsp; " + oDate.getFullYear();
				this.ge("cld$Grid").innerHTML = this.buildGrid(oDate);
			}
		}
	}
	
	this.splitDateString = function(str)
	{
		var parts = str.replace(/\s/g, "").split(".");
		var date = Math.abs(parseInt(parts[0], 10)), month = Math.abs(parseInt(parts[1], 10)) - 1, year = Math.abs(parseInt(parts[2], 10));
		if (isNaN(date + month + year))
			return null;
		date = Math.min(31, Math.max(1, date));
		month = Math.min(11, Math.max(0, month));
		if (year < 100)
			year += 2000;
		return [year, month, date];
	}
	
	this.selectDate = function()
	{
		//if (this.cantSelect)
		//	return;
			
		with (this)
		{
			targetElement.value = zo(date) + "." + zo(month + 1) + "." + zo(year);
			var el = targetElement;
			hide();
			el.focus();
		}
		
		try { this.onSelect(this.targetElement, this); }
		catch(e) {}
	}
	
	this.zo = function(num)
	{
		return num > 0 && num < 10? "0" + num : num;
	}
	
	this.ge = function(id)
	{
		return Calendar.ge(id);
	}
	
	this.getAbsolutePosition = function(el)
	{
		return Calendar.getAbsolutePosition(el);
	}

	
	this.show = function(anchorEl, offsetLeft, offsetTop)
	{
		if (this.isOpen)
		{
			if (this.targetElement == anchorEl)
				return this.hide();
			else
				this.hide();
		}

		//this.cantSelect = false;
		
		this.onDocumentKeyHandler = document.onkeydown;
		this.onWindowKeyHandler = window.onkeydown;
		
		window.onkeydown = document.CE.onKey;
		try { document.onkeydown = function(ev) { document.CE.onKey(ev); };  }
		catch(e) {}

		this.targetElement = anchorEl;
		this.onFocusHandler = this.targetElement.onfocus;
		this.targetElement.onfocus = this.onFocus_inputField;

		try { this.onShow(this.targetElement, this); }
		catch(e) {}
		
		var parts = this.splitDateString(anchorEl.value);
		parts == null? this.setDate() : this.setDate(parts[0], parts[1], parts[2]);
		
		var pos = this.getAbsolutePosition(anchorEl);
		
		pos.x += (offsetLeft == null? 0 : offsetLeft);
		pos.y += (offsetTop == null? anchorEl.clientHeight + 4 : offsetTop);

		try { this.onPosition(this.targetElement, this, pos); }
		catch(e) {}
		
		this.oPopup.style.left = parseInt(pos.x) + "px";
		this.oPopup.style.top = parseInt(pos.y) + "px";

		this.oPopup.style.display = "block";
		this.isOpen = true;
		this.ge("cld$Grid").focus();
	}
	
	this.onFocus_inputField = function()
	{
		document.CE.hide();
	}
	
	this.hide = function()
	{
		try { window.onkeydown = null; } catch(e) {}
		try { document.onkeydown = null; } catch(e) {}
		
		document.onkeydown = this.onDocumentKeyHandler;
		window.onkeydown = this.onWindowKeyHandler;

		this.oPopup.style.display = "none";
		if (this.targetElement)
		{
			this.targetElement.onfocus = this.onFocusHandler;
			this.targetElement = null;
		}
		this.isOpen = false;
	}

	return this.init(contentDiv);
}

Calendar.ge = function(id)
{
	return document.getElementById(id);
}

Calendar.getAbsolutePosition = function(el)
{
	var x = 0, y = 0;
	var isOffsetParent = true;
	
	while (el)
	{
		if (isOffsetParent && el.offsetLeft != null)
			x += el.offsetLeft;
		if (el.scrollLeft != null && el.offsetParent != null)
			x -= el.scrollLeft;
		if (isOffsetParent && el.offsetTop != null)
			y += el.offsetTop;
		if (el.scrollTop != null && el.offsetParent != null)
			y -= el.scrollTop;
		
		isOffsetParent = el.offsetParent == el.parentNode;
		el = el.parentNode;
	}
	
	return { x: x, y: y }
}
