/////////////////////////////////
// Built in Date Class upgrade //
// * this code comes first     //
/////////////////////////////////

Date.isLeapYear=function(year) {
	return (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) ? true:false;
}

Date.prototype.isLeapYear=function() {
	var year=this.getFullYear();
	return (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) ? true:false;
}

Date.prototype.getNumOfDaysInMonth=function() {
	var numOfDaysInMonthArray=new Array(31,28,31,30,31,30,31,31,30,31,30,31);
	var currentMonth=this.getMonth();	
	if (currentMonth!=1 || (currentMonth==1 && !this.isLeapYear())) {
		return numOfDaysInMonthArray[currentMonth];
	} else {
		return 29;
	}
}

////////////////////
// Calendar Class //
////////////////////

function Calendar(weekDaysLabels, startWithDayOfTheWeek) {
	this.calendarDate=null;
	this.weekDaysLabels=weekDaysLabels;
	this.selectedDays=new Array();
	this.calendarCellObj=new Array();
	this.columnDayOrder=this.getDayOrderArray(startWithDayOfTheWeek);
}

Calendar.prototype.getDayOrderArray= function (startWithDayOfTheWeek) {
	var dayArray=new Array();
	for (var i=startWithDayOfTheWeek; i<7; i++) {
		dayArray[i-startWithDayOfTheWeek]=i;
	}
	for (var i=0; i<startWithDayOfTheWeek; i++) {
		dayArray[i+7-startWithDayOfTheWeek]=i;
	}
	return dayArray;		
}

Calendar.prototype.isDateValid=function(dayNum) {
	if (dayNum<=this.calendarDate.getNumOfDaysInMonth()) {
		return true; 
	} else {
		return false;
	}
}

Calendar.prototype.changeDate=function (newYear,newMonth) {
	this.calendarDate=new Date(newYear, newMonth);
} 

Calendar.prototype.getHTML=function(newYear,newMonth) {
	var firstDayInNextWeek,calendarTable,firstDayInMonth;
	this.changeDate(newYear,newMonth);
	calendarTable=document.createElement("TABLE");
	firstDayInMonth=new Date(this.calendarDate.getFullYear(),this.calendarDate.getMonth(),1);
	this.getTableHeader(calendarTable);
	firstDayInNextWeek=this.getTableRow(calendarTable,firstDayInMonth.getDay(),1);
	while (this.isDateValid(firstDayInNextWeek)) {
		firstDayInNextWeek=this.getTableRow(calendarTable,this.columnDayOrder[0],firstDayInNextWeek);
	}
	return calendarTable;
}

Calendar.prototype.getTableHeader=function(tableObj) {
	var headerCell;
	var headerRow=tableObj.insertRow(0);
	for (i=0;i<7;i++) {
		headerCell=document.createElement("TH");
		headerCell.appendChild(document.createTextNode(this.weekDaysLabels[this.columnDayOrder[i]]));
		headerRow.appendChild(headerCell);
	}
}

Calendar.prototype.getTableRow=function(tableObj,firstDayofTheWeek,startDate) {
	var calendarRow=tableObj.insertRow(tableObj.rows.length);
	var columnIndex=0;
	var currentDayNum=startDate;

	while (firstDayofTheWeek!=this.columnDayOrder[columnIndex]) {
		calendarRow.insertCell(calendarRow.cells.length);
		columnIndex++;
	}
	
	for(var i=columnIndex; i<7; i++) {
		var calendarCell=calendarRow.insertCell(calendarRow.cells.length);
		calendarCell.style.cursor="pointer";
		calendarCell.appendChild(document.createTextNode(currentDayNum));
		calendarRow.appendChild(calendarCell);
		this.calendarCellObj[currentDayNum]=calendarCell;
		currentDayNum++;
		columnIndex++;				
		if (!this.isDateValid(currentDayNum)) break;
	}
		
	for (var j=0; j<7-columnIndex; j++) {
		calendarRow.insertCell(calendarRow.cells.length);
	}
	return currentDayNum;
}

Calendar.prototype.setSelectedDate=function(dayNum) {
	this.selectedDays=new Array();
	this.selectedDays[0]=dayNum;
}

Calendar.prototype.setSelectedDates=function(daysNumArray) {
	this.selectedDays=daysNumArray;
}

Calendar.prototype.displaySelectedDates=function() {
	for (var i=1; i<this.calendarCellObj.length; i++) {
		this.calendarCellObj[i].removeAttribute("class");
		this.calendarCellObj[i].removeAttribute("className");	
	}
	for (var i=0; i<this.selectedDays.length; i++) {
		this.calendarCellObj[this.selectedDays[i]].setAttribute("class","markedday");
		this.calendarCellObj[this.selectedDays[i]].setAttribute("className","markedday");
	}
}

Calendar.prototype.getCalendarCellObjectArray=function() {
	return this.calendarCellObj;
}

//////////////////////////////
// CalendarDatePicker Class //
//////////////////////////////

function CalendarDatePicker(containerId,containerCSSClass,yearSelected,monthSelected,daySelected,weekDaysLabelsArray,startWithDayOfTheWeek,monthsArray,yearUpImgFile,yearDownImgFile) {
	this.listeners=new Array();
	this.containerObj=this.getContainerObject(containerId,containerCSSClass);
	this.yearSelected=yearSelected;
	this.monthSelected=monthSelected;
	this.daySelected=daySelected;
	this.pickerCalendar=new Calendar(weekDaysLabelsArray, startWithDayOfTheWeek);
	this.pickerControlPanel=new CalendarDatePickerControlPanel(yearSelected,monthSelected,monthsArray,yearUpImgFile,yearDownImgFile);
	this.calendarTable=null;
	this.init();
}

CalendarDatePicker.prototype.getContainerObject=function(containerId,containerCSSClass) {
	var container=document.getElementById(containerId);
	container.setAttribute("class",containerCSSClass);
	container.setAttribute("className",containerCSSClass);
	return container;
}

CalendarDatePicker.prototype.init=function() {
	var obj=this;	
	this.calendarTable=this.pickerCalendar.getHTML(this.yearSelected,this.monthSelected);
	this.containerObj.appendChild(this.pickerControlPanel.getControlPanel());
	this.containerObj.appendChild(this.calendarTable);
	this.displaySelectedDay(this.yearSelected,this.monthSelected);
	this.pickerControlPanel.setOnChangeHandler(changeMonth);	
	this.initDateCells();
	function changeMonth() {
		var yearViewed=obj.pickerControlPanel.getYearSelected();
		var monthViewed=obj.pickerControlPanel.getMonthSelected();
		obj.calendarTable=obj.pickerCalendar.getHTML(yearViewed,monthViewed);
		obj.containerObj.removeChild(obj.containerObj.getElementsByTagName("TABLE")[0]);
		obj.containerObj.appendChild(obj.calendarTable);
		obj.displaySelectedDay(yearViewed,monthViewed);
		obj.initDateCells();
	}
}

CalendarDatePicker.prototype.initDateCells=function() {
	var obj=this;
	var cellsArray=this.pickerCalendar.getCalendarCellObjectArray();
	for (var i=1;i<cellsArray.length;i++) {
		cellsArray[i].onclick=cellClickHandler;
	}

	function cellClickHandler() {
		var dateNumber=this.firstChild.nodeValue;
		obj.yearSelected=obj.pickerControlPanel.getYearSelected();
		obj.monthSelected=obj.pickerControlPanel.getMonthSelected();
		obj.daySelected=dateNumber;
		obj.displaySelectedDay(obj.pickerControlPanel.getYearSelected(),obj.pickerControlPanel.getMonthSelected());
		// inform listeners about date change
		for (var i=0; i<obj.listeners.length; i++) {
			obj.listeners[i].onDateChange(new ChangedDateEvent(obj.yearSelected,obj.monthSelected,obj.daySelected));
		}
	}
}

CalendarDatePicker.prototype.displaySelectedDay=function(yearViewed,monthViewed) {
	if (this.yearSelected==yearViewed && this.monthSelected==monthViewed) {
		this.pickerCalendar.setSelectedDate(this.daySelected);
		this.pickerCalendar.displaySelectedDates();
	}
}

// listenerObj must implement onDateChange(ChangedDateEvent event) method

CalendarDatePicker.prototype.addListener=function(listenerObj) {
	for (var i=0; i<this.listeners.length; i++) {
		if (this.listeners[i]==listenerObj) return;
	}
	this.listeners[this.listeners.length]=listenerObj;
}

CalendarDatePicker.prototype.removeListener=function(listenerObj) {
	for (var i=0; i<this.listeners.length; i++) {
		if (this.listeners[i]==listenerObj) {
			this.listeners.splice(i,1);
			return;
		}
	}
}

////////////////////////////
// ChangedDateEvent Class //
////////////////////////////

function ChangedDateEvent(year,month,day) {
	this.year=year;
	this.month=month;
	this.day=day;
}

ChangedDateEvent.prototype.getYear=function() {
	return Number(this.year);
}

ChangedDateEvent.prototype.getMonth=function() {
	return Number(this.month);
}

ChangedDateEvent.prototype.getDay=function() {
	return Number(this.day);
}

//////////////////////////////////////////
// CalendarDatePickerControlPanel Class //
//////////////////////////////////////////

function CalendarDatePickerControlPanel(year,month,monthsArray,yearUpImgFile,yearDownImgFile) {
	this.yearField=null;
	this.monthSelector=this.getMonthSelector(monthsArray,month);
	this.yearSelectorGroup=this.getYearSelectorGroup(year,yearUpImgFile,yearDownImgFile);
	this.changeHandler=null;
}

CalendarDatePickerControlPanel.prototype.getMonthSelector=function(monthsArray,monthSelected) {
	var monthSelector=document.createElement("SELECT");
	for (var i=0; i<monthsArray.length; i++) {
		var monthOption=document.createElement("OPTION");
		monthOption.appendChild(document.createTextNode(monthsArray[i]));
		monthOption.value=i;
		if (i==monthSelected) {
			monthOption.selected=true;
		}
		monthSelector.appendChild(monthOption);
	}
	monthSelector.setAttribute("class","datepickermonth");
	monthSelector.setAttribute("className","datepickermonth");
	return monthSelector;
}

CalendarDatePickerControlPanel.prototype.getYearSelectorGroup=function(yearSelected,yearUpImgFile,yearDownImgFile) {
	var obj=this;
	var yearUp=document.createElement("IMG");
	yearUp.setAttribute("src",yearUpImgFile);	
	yearUp.setAttribute("class","datepickeryearup");
	yearUp.setAttribute("className","datepickeryearup");
	yearUp.onmousedown=function() {
		obj.yearField.value=Number(obj.yearField.value)+1;
		obj.changeHandler();
	}
	var yearDown=document.createElement("IMG");
	yearDown.setAttribute("src",yearDownImgFile);	
	yearDown.setAttribute("class","datepickeryeardown");
	yearDown.setAttribute("className","datepickeryeardown");
	yearDown.onmousedown=function() {
		obj.yearField.value=Number(obj.yearField.value)-1;
		obj.changeHandler();
	}
	this.yearField=document.createElement("INPUT");
	this.yearField.type="text";
	this.yearField.value=yearSelected;
	this.yearField.maxLength=4;
	this.yearField.setAttribute("class","datepickeryearfield");
	this.yearField.setAttribute("className","datepickeryearfield");
	this.yearField.onkeyup=function() {
		if (this.value.length==4) {
			obj.changeHandler();
		}
	}
	var yearSelector=document.createDocumentFragment();
	yearSelector.appendChild(yearDown);
	yearSelector.appendChild(this.yearField);
	yearSelector.appendChild(yearUp);
	return yearSelector;	
}

CalendarDatePickerControlPanel.prototype.getControlPanel=function() {
	var container=document.createElement("DIV");
	container.setAttribute("class","datepickercontrolpanel");
	container.setAttribute("className","datepickercontrolpanel");
	container.appendChild(this.monthSelector);
	container.appendChild(this.yearSelectorGroup);
	return container;
}

CalendarDatePickerControlPanel.prototype.getYearSelected=function() {
	return Number(this.yearField.value);
}

CalendarDatePickerControlPanel.prototype.getMonthSelected=function() {
	return(this.monthSelector.options[this.monthSelector.selectedIndex].value);
}

CalendarDatePickerControlPanel.prototype.setOnChangeHandler=function (eventHandler) {
	this.changeHandler=eventHandler;
	this.monthSelector.onchange=eventHandler;
}

/////////////////////////
// DateFormField Class //
/////////////////////////

function DateFormField(calendarDatePickerObj, calendarContainerDivId, dateValueField, hideShowButtonId, onClickHideShowHandler) {
	this.calendarDatePickerObj=calendarDatePickerObj;
	this.calendarContainerDiv=document.getElementById(calendarContainerDivId);
	this.dateValueField=document.getElementById(dateValueField);
	this.hideShowButton=document.getElementById(hideShowButtonId);
	this.calendarIsVisible=false;
	this.onClickHideShowHandler=onClickHideShowHandler;
	this.init();
}

DateFormField.prototype.init=function() {
	var obj=this;
	this.setValueField(this.calendarDatePickerObj.yearSelected,this.calendarDatePickerObj.monthSelected,this.calendarDatePickerObj.daySelected);
	this.hideShowButton.onclick=function() {
		if (obj.calendarIsVisible) {
			obj.hideCalendar();
		} else {
			obj.showCalendar();
		}
		if (obj.onClickHideShowHandler!==null) (obj.onClickHideShowHandler)();
	}
	this.calendarDatePickerObj.addListener(this);
}

DateFormField.prototype.onDateChange=function(changedDateEvent) {
	this.setValueField(changedDateEvent.getYear(),changedDateEvent.getMonth(),changedDateEvent.getDay());
}

DateFormField.prototype.hideCalendar=function() {
	this.calendarContainerDiv.style.display="none";
	this.calendarIsVisible=false;
}

DateFormField.prototype.showCalendar=function() {
	this.calendarContainerDiv.style.display="block";
	this.calendarIsVisible=true;
}
/*
DateFormField.prototype.displayDate=function(year,month,date) {
	while (this.dateDisplayContainer.hasChildNodes()) {
		this.dateDisplayContainer.removeChild(this.dateDisplayContainer.firstChild);
	}
	this.dateDisplayContainer.appendChild(document.createTextNode(date+"."+(month+1)+"."+year+"."));
}
*/
DateFormField.prototype.setValueField=function(year,month,date) {
	this.dateValueField.value=date+" / "+(month+1)+" / "+year;
}

