mlCalendar=function(){
	if(typeof isDate != 'function') document.write('<script language="JavaScript" src="/core/tools/lib/js/jsDate.js"></script>');
	if(typeof isDate != 'function'){
		alert('mlCalendar requires jsDate.js library.');
		return;
	}
	/*if(typeof mlSelect != 'function') document.write('<script language="JavaScript" src="/core/tools/lib/js/mlSelect.js"></script>');
	if(typeof mlSelect != 'function'){
		alert('mlCalendar requires mlSelect.js component.');
		return;
	}*/
	this.init();
	var self=this;
	
}

mlCalendar.prototype.init=function(){
	this.setDefaults();
	this.setDate(new Date());
	this.monDayArr=this.defaults['monDayArr'];
	this.dayNameArr=this.defaults['dayNameArr'];
	
}
mlCalendar.prototype.setDefaults=function(){
	this.defaults=[];
	with(this){
		defaults['viewList']=['month','week','day'];
		defaults['defViewId']=0;
		defaults['showMonthHeader']=true;
		defaults['showWeekNum']=true;
		defaults['showWeekHeader']=true;
		defaults['weekHeaderPosition']='inside';		// posible values: inside / row
		defaults['weekCols']=3;
		defaults['monDayArr']=[31,28,31,30,31,30,31,31,30,31,30,31];
		defaults['monthNameArr']=['January','February','March','April','May','June','July','August','September','October','November','December'];
		defaults['dayNameArr']=['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
		defaults['calItemDisplayType']='inline';
		defaults['dayNumAsImg']=true;
		defaults['numberImgPath']='/image/';
		defaults['numberImgPatt']='day_{number}.gif';
		defaults['dayNumImgClass']='dayNumImg';
		defaults['dayNumBoxClass']='dayNumBox';
		defaults['cursor']=[];
		defaults['cursor']['default']='default';
		defaults['cursor']['busy']='wait';
		//defaults['']=;
	}
	this.viewList=this.defaults['viewList'];
	this.activeView=this.defaults['defViewId'];
	this.showMonthHeader=this.defaults['showMonthHeader'];
	this.showWeekNum=this.defaults['showWeekNum'];
	this.showWeekHeader=this.defaults['showWeekHeader'];
	this.weekHeaderPosition=this.defaults['weekHeaderPosition'];
	this.weekCols=this.defaults['weekCols'];
	this.monDayArr=this.defaults['monDayArr'];
	this.monthNameArr=this.defaults['monthNameArr'];
	this.dayNameArr=this.defaults['dayNameArr'];
	this.calItemDisplayType=this.defaults['calItemDisplayType'];
	this.dsDatesLoaded=false;
	this.gridClassNames=[];
	this.dayNumAsImg=this.defaults['dayNumAsImg'];
	this.numberImgPath=this.defaults['numberImgPath'];
	this.numberImgPatt=this.defaults['numberImgPatt'];
	this.dayNumImgClass=this.defaults['dayNumImgClass'];
	this.dayNumBoxClass=this.defaults['dayNumBoxClass'];
	this.cursor=this.defaults['cursor'];
	this.initViews();
}
mlCalendar.prototype.applyDefaults=function(){
	for(var i in this.defaults) {}
}
mlCalendar.prototype.initViews=function(){
	this.views=[];
	for(var i in this.viewList){
		this.views[this.viewList[i]]=[];
		var v=this.views[this.viewList[i]];
		v['name']=this.viewList[i];
		v['dayFieldArr']=[];
		v['dateElementArr']=[];
	}
}
mlCalendar.prototype.getActiveViewName=function(){return this.viewList[this.activeView];}
mlCalendar.prototype.setProperty=function(pName_s,pValue){this[pName_s]=pValue;}
mlCalendar.prototype.setDate=function(pDate_d,pRefillGrids_b){
	if(typeof this.onPreSetDate=='function') this.onPreSetDate();
	this.setCursor(this.cursor['busy']);
	if(!pDate_d) alert('This is not a valid date.');
	this.currDat=pDate_d;
	this.currYear=datePart('yyyy',this.currDat);
	this.currMonth=datePart('m',this.currDat);
	this.currDay=datePart('d',this.currDat);
	this.setDateRange();
	if(pRefillGrids_b) this.refillGrids();
	if(typeof this.onPostSetDate=='function') setTimeout(this.onPostSetDate,0);
}
mlCalendar.prototype.setDateRange=function(){
	switch(this.getActiveViewName()){
		case 'month': {
			this.firstDate=new Date(this.currYear,this.currMonth-1,01,00,00,00,000);
			this.lastDate=new Date(this.currYear,this.currMonth-1,getDaysInMonth(this.firstDate),00,00,00,000);
			break;
		}
		case 'week': {
			var d=datePart('w',this.currDat)==0?6:datePart('w',this.currDat)-1;
			this.firstDate=dateAdd('d',-d,this.currDat);
			this.lastDate=dateAdd('d',6,this.firstDate);
			break;
		}
		case 'day': {
			this.firstDate=this.currDat;
			this.lastDate=this.currDat;
			break;
		}
	}
}
mlCalendar.prototype.skipDat=function(pType_s,pVal_i){
	if(typeof this.onPreSkipDat=='function') this.onPreSkipDat();
	if(pType_s=='w')pVal_i=pVal_i*7;
	this.setDate(dateAdd(pType_s,pVal_i,this.currDat));
	this.refillGrids();
	if(typeof this.onPostSkipDat=='function') setTimeout(this.onPostSkipDat,0);
}
mlCalendar.prototype.refillGrids=function(){
	if(typeof this.onPreRefillGrids=='function') this.onPreRefillGrids();
	this.fillMonthGrid();
	this.fillWeekGrid();
	this.fillDayGrid();
	if(typeof this.onPostRefillGrids=='function') setTimeout(this.onPostRefillGrids,0);
}
mlCalendar.prototype.writeDate=function(pElement_o){pElement_o.innerHTML=dateFormat(this.currDat,'dd.mm.yyyy hh:nn:ss:l')}
mlCalendar.prototype.addGridTbl=function(pParentNode_o,pId_s,pClassName_s){
	var oGridTbl=pParentNode_o.appendChild(document.createElement('table'));
	oGridTbl.id=pId_s;
	oGridTbl.className=pClassName_s;
	return oGridTbl;
}
mlCalendar.prototype.addGridRows=function(pParentNode_o,pRows_i,pCells_i,pHeader_b,pClassNames_a){
	if(pHeader_b){
		var th=pParentNode_o.createTHead();
		if(pClassNames_a['th']) th.className=pClassNames_a['th'];
		var tmpRow=th.insertRow(0);
		if(pClassNames_a['th_tr']) tmpRow.className=pClassNames_a['th_tr'];
		else if(pClassNames_a['tr']) tmpRow.className=pClassNames_a['tr'];
		for(var ci=0;ci<pCells_i;ci++){
			tmpCol=tmpRow.insertCell(ci);
			if(pClassNames_a['th_td']) tmpCol.className=pClassNames_a['th_td'];
			else if(pClassNames_a['td']) tmpCol.className=pClassNames_a['td'];
		}
	}
	for(var ri=pHeader_b?1:0;ri<pRows_i;ri++){
		var tmpRow=pParentNode_o.insertRow(ri);
		if(pClassNames_a['tr']) tmpRow.className=pClassNames_a['tr'];
		for(var ci=0;ci<pCells_i;ci++){
			tmpCol=tmpRow.insertCell(ci);
			if(pClassNames_a['td']) tmpCol.className=pClassNames_a['td'];
		}
	}
}
mlCalendar.prototype.cleanGrid=function(pParentNode_o,pClassName_s,pStartRow_i,pStartCol_i,pHideRow_b){
	this.views[this.getActiveViewName()]['dayFieldArr']=[];
	for(var ri=pStartRow_i;ri<pParentNode_o.rows.length;ri++){
		for(var ci=pStartCol_i;ci<pParentNode_o.rows[ri].cells.length;ci++) {
			var c=pParentNode_o.rows[ri].cells[ci];
			c.innerHTML='';
			if(pClassName_s!='') c.className=pClassName_s;
		}
		if(pHideRow_b) pParentNode_o.rows[ri].style.display='none';
	}
}
mlCalendar.prototype.drawDayNumber=function(pDay_i,pField_o,pBoxClass_s,pPath_s,pAddZero_b){
	if(this.dayNumAsImg) {
		var d=pDay_i;
		if(pField_o.childNodes[0]&&pField_o.childNodes[0].id.indexOf('numBox_')>=0) var numBox=pField_o.childNodes[0];
		else var numBox=this.appendElement('div','numBox_'+d,pField_o,pBoxClass_s,'');
		if(isFinite(d)){
			if(d>31) { // if it's a year
				var img=numBox.appendChild(document.createElement('img'));
				img.src=pPath_s+this.numberImgPatt.replace(new RegExp('\{number\}','gi'),Math.floor(d/1000));
				img.className=this.dayNumImgClass;
				var img=numBox.appendChild(document.createElement('img'));
				img.src=pPath_s+this.numberImgPatt.replace(new RegExp('\{number\}','gi'),Math.floor(d%1000/100));
				img.className=this.dayNumImgClass;
				var img=numBox.appendChild(document.createElement('img'));
				img.src=pPath_s+this.numberImgPatt.replace(new RegExp('\{number\}','gi'),Math.floor(d%100/10));
				img.className=this.dayNumImgClass;
			}
			else if(d>=10)	{
				var img=numBox.appendChild(document.createElement('img'));
				img.src=pPath_s+this.numberImgPatt.replace(new RegExp('\{number\}','gi'),Math.floor(d/10));
				img.className=this.dayNumImgClass;
			} else if(pAddZero_b){
				var img=numBox.appendChild(document.createElement('img'));
				img.src=pPath_s+this.numberImgPatt.replace(new RegExp('\{number\}','gi'),0);
				img.className=this.dayNumImgClass;
			}
			var img=numBox.appendChild(document.createElement('img'));
			img.src=pPath_s+this.numberImgPatt.replace(new RegExp('\{number\}','gi'),d%10);
			img.className=this.dayNumImgClass;
		} else if(d=='.') {
			var img=numBox.appendChild(document.createElement('img'));
			img.src=pPath_s+this.numberImgPatt.replace(new RegExp('\{number\}','gi'),'dot');
			img.className=this.dayNumImgClass;
		}
	}
	else pField_o.innerHTML=pDay_i;
}
mlCalendar.prototype.drawMonthGrid=function(pParentNode_o){
	this.monthGridTbl=oGrid=this.addGridTbl(pParentNode_o,'calMonthGrid','calMonthGrid');
	this.addGridRows(oGrid,7,8,this.showMonthHeader,this.gridClassNames['month']);
	for(var ci=1;ci<=7;ci++) oGrid.rows[0].cells[ci].innerHTML=this.dayNameArr[ci-1];
	this.views['month']['gridElement']=$('calMonthGrid');
	this.monthGridState='empty';
	return oGrid;
}
mlCalendar.prototype.fillMonthGrid=function(){
	if(typeof this.onPreFillMonthGrid=='function') this.onPreFillMonthGrid();
	var oGrid=$('calMonthGrid');
	this.cleanGrid($('calMonthGrid'),this.gridClassNames['month']['td']?this.gridClassNames['month']['td']:'',1,1,true);
	this.monthGridState='empty';
	firstDayInMonth=this.firstDate.getDay()==0?7:this.firstDate.getDay();
	lastDayInMonth=this.lastDate.getDay()==0?7:this.lastDate.getDay();
	var ri=1;
	var ci=firstDayInMonth-1;
	var c=oGrid.rows[ri].cells[0];
	oGrid.rows[ri].style.display='';
	c.innerHTML=datePart('ww',this.firstDate);	// week number
	for(var i=1;i<firstDayInMonth;i++) {				// populate empty cells before first day of current month
		c=oGrid.rows[ri].cells[i];
		c.innerHTML='';
		if(c.getAttribute('mlDate')) c.removeAttribute('mlDate');
		if(this.gridClassNames['month']['td']) c.className=this.gridClassNames['month']['td'];
	}
	for(var i=firstDayInMonth;i<firstDayInMonth+getDaysInMonth(this.currDat);i++){				// populate days of current month
		var d=dateAdd('d',i-firstDayInMonth,this.firstDate);
		ci++;
		c=oGrid.rows[ri].cells[ci];
		c.setAttribute('mlDate',dateFormat(d,'yyyy-mm-dd'));
		if(this.gridClassNames['month']['td_dat']) c.className=this.gridClassNames['month']['td_dat'];
		// insert day number
		this.drawDayNumber(datePart('d',d),c,this.gridClassNames['month']['numBox']?this.gridClassNames['month']['numBox']:this.dayNumBoxClass,this.numberImgPath.replace(new RegExp('\{view\}','gi'),'month'));
		// insert content box
		var ne=this.appendElement('div','contBox_'+datePart('d',d),c,this.gridClassNames[this.getActiveViewName()]['contBox'],'');
		if(i%7==0&&i<firstDayInMonth+getDaysInMonth(this.currDat)-1){
			ri++;
			ci=0;
			oGrid.rows[ri].style.display='';
			oGrid.rows[ri].cells[ci].innerHTML=datePart('ww',dateAdd('d',1,d));
		}
		// open day view on double click
		for(var j=0;j<this.viewList.length;j++){
			var self=this;
			if(this.viewList[j]=='day') Spry.Utils.addEventListener(c,'dblclick',function(e){var d=self.getFieldDate(e.srcElement?e.srcElement:e.target);if(isDate(d))self.jumpTo('day',d);e.cancelBubble=true;return false;},false);
		}
	}
	for(var i=ci+1;i<=7;i++){											// populate empty cells after last day of current month
		c=oGrid.rows[ri].cells[i];
		c.innerHTML='';
		if(c.getAttribute('mlDate')) c.removeAttribute('mlDate');
		if(this.gridClassNames['month']['td']) c.className=this.gridClassNames['month']['td'];
	}
	if(this.showWeekNum){													// display week number column (hidden by default)
		for(var ri=this.showMonthHeader?1:0;ri<oGrid.rows.length;ri++){
			if(this.gridClassNames['month']['td_weekNum']) oGrid.rows[ri].cells[0].className=this.gridClassNames['month']['td_weekNum'];
		}
	}
	this.monthGridState='filled';
	if(typeof this.onPostFillMonthGrid=='function') setTimeout(this.onPostFillMonthGrid,0);
}
mlCalendar.prototype.drawWeekGrid=function(pParentNode_o){
	var oGrid=this.addGridTbl(pParentNode_o,'calWeekGrid','calWeekGrid');
	switch(this.weekCols){
		case 3:{
			this.addGridRows(oGrid,this.weekHeaderPosition=='row'?4:2,3,false,this.gridClassNames['week']);
			if(this.showWeekHeader&&this.weekHeaderPosition=='row'){
				for(var ci=0;ci<5;ci++) {
					oGrid.rows[ci<3?0:2].cells[ci].innerHTML=this.dayNameArr[ci];
				}
			}
			break;
		}
		default: {
			this.addGridRows(oGrid,2,7,this.showWeekHeader,this.gridClassNames['week']);
			if(this.showWeekHeader&&this.weekHeaderPosition=='row') 
				for(var ci=0;ci<7;ci++) 
					oGrid.rows[0].cells[ci].innerHTML=this.dayNameArr[ci];
			break;
		}
	}
	this.views['week']['gridElement']=$('calWeekGrid');
	this.weekGridState='empty';
	return oGrid;
}
mlCalendar.prototype.fillWeekGrid=function(){
	if(typeof this.onPreFillWeekGrid=='function') this.onPreFillWeekGrid();
	var oGrid=$('calWeekGrid');
	this.cleanGrid($('calWeekGrid'),this.gridClassNames['week']['td']?this.gridClassNames['week']['td']:'',0,0);
	this.weekGridState='empty';
	if(datePart('w',this.currDat)==0)firstDatInWeek=dateAdd('d',-6,this.currDat);
	else firstDatInWeek=dateAdd('d',-datePart('w',this.currDat)+1,this.currDat);
	lastDatInWeek=dateAdd('d',6,firstDatInWeek);
	var self=this;
	switch(this.weekCols){
		case 3:{
			var ri=this.showWeekHeader&&this.weekHeaderPosition=='row'?1:0;
			// fill day elements
			for(var ci=0;ci<7;ci++){
				var d=dateAdd('d',ci,firstDatInWeek);
				if(ci==3) ri++;
				if(ci==6) var c=oGrid.rows[ri].cells[2];
				else if(ci<3) var c=oGrid.rows[ri].cells[ci];
				else var c=oGrid.rows[ri].cells[ci-3];
				if(this.showWeekHeader){
					var className=this.gridClassNames['week']['th_td']?this.gridClassNames['week']['th_td']:'';
					var ne=this.appendElement('div','wdHead_'+ci,c,className,this.dayNameArr[ci]);
				}
				var ne=this.appendElement('div','wdCont_'+ci,c,'','');
				ne.setAttribute('mlDate',dateFormat(d,'yyyy-mm-dd'));
				if(this.gridClassNames['week']['td_dat']) {
					if(ci>4) ne.className=this.gridClassNames['week']['td_dat']+'We';
					else ne.className=this.gridClassNames['week']['td_dat'];
				}
				if(this.gridClassNames['week']['numBox']) var cls=ci>4?this.gridClassNames['week']['numBox']+'We':this.gridClassNames['week']['numBox'];
				else var cls=this.dayNumBoxClass;
				this.drawDayNumber(datePart('d',d),ne,cls,this.numberImgPath.replace(new RegExp('\{view\}','gi'),ci>4?'month':'week'));
				Spry.Utils.addEventListener(c,'dblclick',function(e){var d=self.getFieldDate(e.srcElement?e.srcElement:e.target);if(isDate(d))self.jumpTo('day',d);e.cancelBubble=true;return false;},false);
			}
			break;
		}
		default: {
			var r=this.showWeekHeader&&this.weekHeaderPosition=='row'?1:0;
			for(var i=0;i<7;i++) {
				var d=dateAdd('d',i,firstDatInWeek);
				var c=oGrid.rows[r].cells[i];
				c.setAttribute('mlDate',dateFormat(d,'yyyy-mm-dd'));
				if(this.gridClassNames['week']['td_dat']) c.className=this.gridClassNames['week']['td_dat'];
				if(this.gridClassNames['week']['numBox']) var cls=ci>4?this.gridClassNames['week']['numBox']+'We':this.gridClassNames['week']['numBox'];
				else var cls=this.dayNumBoxClass;
				this.drawDayNumber(datePart('d',d),ne,cls,this.numberImgPath.replace(new RegExp('\{view\}','gi'),ci>4?'month':'week'));
				Spry.Utils.addEventListener(c,'dblclick',function(e){var d=self.getFieldDate(e.srcElement?e.srcElement:e.target);if(isDate(d))self.jumpTo('day',d);e.cancelBubble=true;return false;},false);
			}
			oGrid.rows[r].style.display='';
			break;
		}
	}
	this.weekGridState='filled';
	if(typeof this.onPostFillWeekGrid=='function') setTimeout(this.onPostFillWeekGrid,0);
}
mlCalendar.prototype.fillDayGrid=function(){
	var oGrid=$('calDayGrid');
	var d=this.currDat;
	oGrid.className=this.gridClassNames['day']['td']?this.gridClassNames['day']['td']:'';
	oGrid.innerHTML='';
	oGrid.setAttribute('mlDate',dateFormat(d,'yyyy-mm-dd'));
	this.dayGridState='empty';
	this.drawDayNumber(datePart('d',d),oGrid,this.gridClassNames['day']['numBox'],this.numberImgPath.replace(new RegExp('\{view\}','gi'),'week'),true);
	this.drawDayNumber('.',oGrid,this.gridClassNames['day']['numBox'],this.numberImgPath.replace(new RegExp('\{view\}','gi'),'week'));
	this.drawDayNumber(datePart('m',d),oGrid,this.gridClassNames['day']['numBox'],this.numberImgPath.replace(new RegExp('\{view\}','gi'),'week'),true);
	this.drawDayNumber('.',oGrid,this.gridClassNames['day']['numBox'],this.numberImgPath.replace(new RegExp('\{view\}','gi'),'week'));
	this.drawDayNumber(datePart('yyyy',d),oGrid,this.gridClassNames['day']['numBox'],this.numberImgPath.replace(new RegExp('\{view\}','gi'),'week'),true);
	this.dayGridState='filled';
}
mlCalendar.prototype.initDayFieldArr=function(){
	for(var i in this.views){
		switch(this.views[i]['name']){
			case 'month': {this.views[i]['dayFieldArr']=new Array(getDaysInMonth(this.firstDate));break;}
			case 'week': {this.views[i]['dayFieldArr']=new Array(7);break;}
			case 'day': {this.views[i]['dayFieldArr']=[];break;}
			default: {this.views[i]['dayFieldArr']=[];break;}
		}
	}
}
mlCalendar.prototype.getDayField=function(pDate_i){
	var a=this.views[this.getActiveViewName()]['dayFieldArr'];
	switch(this.views[this.getActiveViewName()]['name']){
		case 'month': {
			var d=datePart('d',pDate_i);
			if(typeof a[d]=='object') var retVal=a[d];
			else var retVal=this.searchDayField(pDate_i);
			break;
		}
		case 'week': {
			var d=datePart('w',pDate_i)==0?6:datePart('w',pDate_i)-1;
			if(typeof a[d]=='object') var retVal=a[d];
			else var retVal=this.searchDayField(pDate_i);
			break;
		}
		case 'day': {
			if(typeof a[0]=='object') var retVal=a[0];
			else var retVal=this.searchDayField(pDate_i);
			break;
		}
		default: {var retVal=a[0];break;}
	}
	return retVal;
}
mlCalendar.prototype.searchDayField=function(pDate_i){
	var a=this.views[this.getActiveViewName()]['dayFieldArr'];
	switch(this.views[this.getActiveViewName()]['name']){
		case 'month': {
			var d=datePart('d',pDate_i);
			var firstDayInGrid=this.firstDate.getDay()==0?6:this.firstDate.getDay()-1;
			var dPos=firstDayInGrid+d-1;
			var rPos=Math.floor(dPos/7)+1;
			var cPos=(dPos%7)+1;
			a[d]=this.views[this.getActiveViewName()]['gridElement'].rows[rPos].cells[cPos];
			var retVal=a[d];
			break;
		}
		case 'week': {
			var cPos=datePart('w',pDate_i)==0?6:datePart('w',pDate_i)-1;
			switch(this.weekCols){
				case 3: {
					if(cPos==6) {
						var ri=1;
						var ci=2;
						var cn=this.showWeekHeader?3:1;
					} else if(cPos<3) {
						var ri=0;
						var ci=cPos;
						var cn=this.showWeekHeader?1:0;
					} else {
						var ri=1;
						var ci=cPos-3;
						var cn=this.showWeekHeader?1:0;
					}
					a[cPos]=this.views[this.getActiveViewName()]['gridElement'].rows[ri].cells[ci].childNodes[cn];
					break;
				}
				default: {
					var r=this.showWeekHeader&&this.weekHeaderPosition=='row'?1:0;
					a[cPos]=this.views[this.getActiveViewName()]['gridElement'].rows[r].cells[cPos];
					break;
				}
			}
			var retVal=a[cPos];
			break;
		}
		case 'day': {
			a[0]=$('calDayGrid');
			var retVal=a[0];
			break;
		}
	}
	return retVal;
}
mlCalendar.prototype.appendElement=function(pType_s,pId_s,pParentNode_o,pClassName_s,pContent_s){
	var retVal=pParentNode_o.appendChild(document.createElement(pType_s));
	retVal.id=pId_s;
	retVal.className=pClassName_s;
	if(pContent_s!='') retVal.innerHTML=pContent_s;
	return retVal;
}
mlCalendar.prototype.jumpTo=function(pView_s,pDate_d){
	if(!pDate_d) return false;
	this.setDate(pDate_d,true);
	this.calContainer.showPanel(pView_s);
	if(typeof this.onViewChange=='function') this.onViewChange();
}
mlCalendar.prototype.getFieldDate=function(pElement_o){
	var ele=pElement_o;
	while(ele.parentNode){
		if(ele.getAttribute('mlDate')) return parseDate(ele.getAttribute('mlDate'));
		else ele=ele.parentNode;
	}
	return null;
}
mlCalendar.prototype.getCursor=function(){return document.body.style.cursor?document.body.style.cursor:null;}
mlCalendar.prototype.setCursor=function(pCursor_s){if(document.body)document.body.style.cursor=pCursor_s;}
mlCalendar.prototype.setCursorBusy=function(){this.setCursor('busy');}
mlCalendar.prototype.resetCursor=function(){this.setCursor('default');}
