﻿/**
 * @author Sergey Chikuyonok
 * @copyright 2006 Art. Lebedev Studio | http://www.artlebedev.ru
 */

/**
 * @constructor
 * @param {Date} oDate
 * @param {Element} elemContainer
 * @param {Function} fCallback
 */
function Calendar(oDate, elemContainer, fCallback){
	this.elemContainer=elemContainer;
	this.fCallback=fCallback;
	
	var aDivs=this.elemContainer.getElementsByTagName('div');
	for(var i=0; i<aDivs.length; i++){
		if(matchClass(aDivs[i], 'calendar'))
			this.elemCalendar=aDivs[i];
		else if(matchClass(aDivs[i], 'month-list'))
			this.elemMonthList=aDivs[i];
		else if(matchClass(aDivs[i], 'year-list'))
			this.elemYearList=aDivs[i];
	}
	
	this.elemHeader=document.createElement('h2');
	this.elemCalendar.appendChild(this.elemHeader);
	
	this.SetMinDate(new Date(1900, 0, 1));
	this.SetMaxDate(new Date(2100, 11, 31));
	this.SetCurrentDate(oDate);
	
	addEvent(this.elemContainer, 'click', CancelEvent);
}

/**
 * @param {Date} oDate
 */
Calendar.prototype.SetMinDate=function(oDate){
	this.oMinDate=oDate;
}

/**
 * @return {Date}
 */
Calendar.prototype.GetMinDate=function(){
	return new Date(this.oMinDate.toString());
}

/**
 * @param {Date} oDate
 */
Calendar.prototype.SetMaxDate=function(oDate){
	this.oMaxDate=oDate;
}

/**
 * @return {Date}
 */
Calendar.prototype.GetMaxDate=function(){
	return new Date(this.oMaxDate.toString());
}

Calendar.prototype.SetCurrentDate=function(oDate){
	this.oCurrentDate=oDate;
	this.Callback();
}

/**
 * @return {Date}
 */
Calendar.prototype.GetCurrentDate=function(){
	return new Date(this.oCurrentDate.toString());
}

Calendar.prototype.GetTotalDays=function(oDate){
	switch(oDate.getMonth()){
		case 1: //february
			return (oDate.getFullYear()%4 == 0) ? 29 : 28;
			break;
		case 0:
		case 2:
		case 4:
		case 6:
		case 7:
		case 9:
		case 11:
			return 31;
			break;
		default:
			return 30;
	}
}

/**
 * Creates calendar for specified date
 * @param {Date} oDate
 */
Calendar.prototype.CreateCalendar=function(oDate){
	var iTotalDays = this.GetTotalDays(oDate);
	
	var dtFirst=new Date(oDate.getFullYear(), oDate.getMonth(), 1);
	var dtTest=new Date(oDate.getFullYear(), oDate.getMonth(), 1);
	var dtLast=new Date(oDate.getFullYear(), oDate.getMonth(), iTotalDays);
	var iFirstDay=dtFirst.getDay();
	iFirstDay=(!iFirstDay) ? 6 : iFirstDay-1;
	
	var iLastDay=dtLast.getDay();
	var iRange=iTotalDays+7-iLastDay;
	var iToday=oDate.getDate();
	
	var elemTable=document.createElement('table');
	var elemTBody=document.createElement('tbody');
	var elemTr, elemTd, elemDiv;
	var i=-iFirstDay;
	while(i<iRange){
		elemTr=document.createElement('tr');
		
		for(var j=0; j<7; j++, i++){
			var elemTd=document.createElement('td');
			if(i>=0 && i<iTotalDays){
				dtTest.setDate(i+1);
				elemDiv=document.createElement('span');
				elemDiv.appendChild(document.createTextNode(i+1));
				elemTd.appendChild(elemDiv);
				if(!this.IsAvailable(dtTest))
					addClass(elemTd, 'disabled');
			}
			if(i+1 == iToday)
				addClass(elemTd, 'selected');
			elemTr.appendChild(elemTd);
		}
		elemTBody.appendChild(elemTr);
	}
	
	elemTable.appendChild(elemTBody);
	
	this.ClearElement(this.elemCalendar);
	this.ClearElement(this.elemHeader);

	this.elemHeader.appendChild(document.createTextNode(this.GetCurrentDate().format('%F, %Y')));
	this.elemCalendar.appendChild(this.elemHeader);
	this.elemCalendar.appendChild(elemTable);
	
	elemTable=this.elemCalendar.getElementsByTagName('table')[0];
	
	var _me=this;
	addEvent(elemTable, 'click', function(evt){_me.SelectDate(evt)});
}

/**
 * @param {Date} oDate
 */
Calendar.prototype.CreateMonthList=function(oDate){
	var dtStart=new Date(oDate.getFullYear(), 0, 1);
	var dtEnd=new Date(oDate.getFullYear(), 0, 1);
	var iCurrentMonth=this.GetCurrentDate().getMonth();
	var elemUl=document.createElement('ul');
	var elemLi, elemSpan;
	for(var i=0; i<12; i++){
		dtStart.setMonth(i);
		dtEnd=new Date(dtStart.getFullYear(), i, this.GetTotalDays(dtStart));
		elemLi=document.createElement('li');
		elemSpan=document.createElement('span');
		elemSpan.appendChild(document.createTextNode(dtStart.format('%F')));
		elemLi.appendChild(elemSpan);
		if(!this.IsAvailable(dtStart) && !this.IsAvailable(dtEnd))
			addClass(elemLi, 'disabled');
		if(i == iCurrentMonth)
			addClass(elemLi, 'selected');
		
		elemUl.appendChild(elemLi);
	}
	
	this.ClearElement(this.elemMonthList);
	this.elemMonthList.appendChild(elemUl);
	
	var _me=this;
	addEvent(elemUl, 'click', function(evt){_me.SelectMonth(evt)});
}

/**
 * @param {Date} oDate
 */
Calendar.prototype.CreateYearList=function(oDate){
	var iYearStart=this.GetMinDate().getFullYear();
	var iYearEnd=this.GetMaxDate().getFullYear();
	var iCurrentYear=this.GetCurrentDate().getFullYear();
	var elemUl=document.createElement('ul');
	var elemLi, elemSpan;
	for(var i=iYearStart; i<=iYearEnd; i++){
		elemLi=document.createElement('li');
		elemSpan=document.createElement('span');
		elemSpan.appendChild(document.createTextNode(i));
		elemLi.appendChild(elemSpan);
		elemUl.appendChild(elemLi);
		if(i == iCurrentYear)
			addClass(elemLi, 'selected');
	}
	
	this.ClearElement(this.elemYearList);
	this.elemYearList.appendChild(elemUl);
	
	var _me=this;
	addEvent(elemUl, 'click', function(evt){_me.SelectYear(evt)});
}

/**
 * @param {Element} elem
 */
Calendar.prototype.ClearElement=function(elem){
	for(var i=elem.childNodes.length; i>0; i--){
		elem.removeChild(elem.childNodes[i-1]);
	}
}

Calendar.prototype.Build=function(){
	var dt=this.GetCurrentDate();
	this.CreateCalendar(dt);
	this.CreateMonthList(dt);
	this.CreateYearList(dt);
}

/**
 * @param {Date} oDate
 */
Calendar.prototype.IsAvailable=function(oDate){
	var dtMin=this.GetMinDate();
	var dtMax=this.GetMaxDate();
	var iTime=oDate.getTime();
	if(dtMin && dtMax)
		return (dtMin.getTime() <= iTime && iTime <= dtMax.getTime());
	else if(dtMin)
		return (dtMin.getTime() <= iTime);
	else if(dtMax)
		return (iTime <= dtMax.getTime());
	return true;
}

Calendar.prototype.Show=function(){
	removeClass(this.elemContainer, 'hidden');
}

Calendar.prototype.Hide=function(){
	addClass(this.elemContainer, 'hidden');
}

Calendar.prototype.Callback=function(){
	if(this.fCallback)
		this.fCallback(this.GetCurrentDate());
}

Calendar.prototype.Toggle=function(evt){
	if((evt=checkEvent(evt))){
		if(matchClass(this.elemContainer, 'hidden'))
			this.Show();
		else	
			this.Hide();
		CancelEvent(evt);
	}
}

/**
 * Selects cell
 * @param {Event} evt
 * @return {Element, null}
 */
Calendar.SelectCell=function(evt){
	if((evt=checkEvent(evt))){
		var elemParent=evt.target;
		var elemCell;
		do{
			if(elemParent.nodeType == 1){
				if(elemParent.tagName == 'TD' || elemParent.tagName == 'LI')
					elemCell=elemParent;
				else if(elemParent.tagName == 'TABLE' || elemParent.tagName == 'UL')
					break;
			}
		}while(elemParent=elemParent.parentNode);
		
		if(elemParent && elemCell && !matchClass(elemCell, 'disabled')){
			return elemCell;
		}
	}
	return null;
}

Calendar.CancelClick=function(){
	return false;
}

Calendar.prototype.SelectDate=function(evt){
	if((evt=checkEvent(evt))){
		var elem=this.constructor.SelectCell(evt);
		if(elem){
			this.constructor.HiliteChild(elem.parentNode.parentNode, elem.tagName, elem.firstChild.firstChild.nodeValue);
			this.SetDay(Number(elem.firstChild.firstChild.nodeValue));
			CancelEvent(evt);
		}
	}
}

Calendar.prototype.SelectMonth=function(evt){
	if((evt=checkEvent(evt))){
		var elem=this.constructor.SelectCell(evt);
		if(elem){
			this.constructor.HiliteChild(elem.parentNode, elem.tagName, elem.firstChild.firstChild.nodeValue);
			var elemParent=elem.parentNode;
			for(var i=0; i<elemParent.childNodes.length; i++){
				if(elemParent.childNodes[i] == elem){
					this.SetMonth(i);
					break;
				}
			}
			
			this.constructor.HiliteChild(this.elemCalendar.getElementsByTagName('tbody')[0], 'td', this.GetCurrentDate().getDate());
			this.CreateCalendar(this.GetCurrentDate());
			CancelEvent(evt);
		}
	}
}

Calendar.prototype.SelectYear=function(evt){
	if((evt=checkEvent(evt))){
		var elem=this.constructor.SelectCell(evt);
		if(elem){
			this.constructor.HiliteChild(elem.parentNode, elem.tagName, elem.firstChild.firstChild.nodeValue);
			this.SetYear(Number(elem.firstChild.firstChild.nodeValue));
			
			var dt=this.GetCurrentDate();
			
			this.constructor.HiliteChild(this.elemCalendar.getElementsByTagName('tbody')[0], 'td', dt.getDate());
			this.constructor.HiliteChild(this.elemMonthList.getElementsByTagName('ul')[0], 'li', dt.format('%F'));
			this.CreateMonthList(dt);
			this.CreateCalendar(dt);
			CancelEvent(evt);
		}
	}
}

/**
 * @param {Element} elemParent
 * @param {String} sTag
 * @param {String} sValue
 */
Calendar.HiliteChild=function(elemParent, sTag, sValue){
	var aChild=elemParent.getElementsByTagName(sTag);
	for(var i=0; i<aChild.length; i++){
		if(aChild[i].firstChild){
			if(aChild[i].firstChild.firstChild.nodeValue == sValue)
				addClass(aChild[i], 'selected');
			else
				removeClass(aChild[i], 'selected');
		}
	}
}
/**
 * Prevents IE from navigating to hyperlink
 * @param {Object} elem
 */
Calendar.StopClickInIE=function(elem){
	var aHref=elem.getElementsByTagName('a');
	for(var i=0; i<aHref.length; i++){
		aHref[i].onclick=this.constructor.CancelClick;
	}
}

Calendar.prototype.SetDay=function(iDayNum){
	var dt=this.GetCurrentDate();
	dt.setDate(iDayNum);
	this.SetCurrentDate(dt);
}

Calendar.prototype.SetMonth=function(iMonthNum){
	var dt=this.GetCurrentDate();
	var iTotalDays=this.GetTotalDays(new Date(dt.getYear(), iMonthNum, 1));
	if(iTotalDays < dt.getDate())
		dt.setDate(iTotalDays);
	
	dt.setMonth(iMonthNum);
	
	if(dt.getTime() < this.GetMinDate().getTime())
		dt=this.GetMinDate();
	else if(dt.getTime() > this.GetMaxDate().getTime())
		dt=this.GetMaxDate();
	this.SetCurrentDate(dt);
}

Calendar.prototype.SetYear=function(iYearNum){
	var dt=this.GetCurrentDate();
	var iTotalDays=this.GetTotalDays(new Date(iYearNum, dt.getMonth(), 1));
	if(iTotalDays < dt.getDate())
		dt.setDate(iTotalDays);
	
	dt.setYear(iYearNum);
	
	if(dt.getTime() < this.GetMinDate().getTime())
		dt=this.GetMinDate();
	else if(dt.getTime() > this.GetMaxDate().getTime())
		dt=this.GetMaxDate();
	this.SetCurrentDate(dt);
}