function Calendar(arg) {
	this.debug = false;
	this.msie = (navigator.appName == 'Microsoft Internet Explorer') ? true : false;
	this.weekStartDay = 1;
	this.functions.parent = this;
	this.initialisation(arg);
}

Calendar.prototype.constructor = Calendar;

// Methods
Calendar.prototype = {
	initialisation: function(arg) {
		var Calendar = this;
		if(this.debug) { window.alert('Calendar.init();'); }
		this.setLanguage(arg['lang']);
		this.css_pfx = (arg['css_pfx'])	? arg['css_pfx']+'_' : 'cal_';
		
		// Date objects
		this.date = new Object();
		
		this.date.now = new Date();
		this.date.current = this.date.now;
		this.date.selected = this.date.now;
		
		this.date.daysPerMonth = new Array(31,28,31,30,31,30,31,31,30,31,30,31);
		
		switch(typeof(arg['date_s'])) {
			case 'string':
			(arg['date_s'] == 'now') ? this.date.s = new Date(this.date.now.getFullYear(),this.date.now.getMonth(),this.date.now.getDate()-1) : new Date(arg['date_s'].split('-')[2],(arg['date_s'].split('-')[1]-1),arg['date_s'].split('-')[0]);
			break;
			case 'object':
			this.date.s = arg['date_s'];
			break;
			default:
			this.date.s = new Date(this.date.now.getFullYear(),this.date.now.getMonth(),(this.date.now.getDate()-1));
			break;
		}
		
		switch(typeof(arg['date_e'])) {
			case 'string':
			this.date.e = new Date(arg['date_e'].split('-')[2],(arg['date_e'].split('-')[1]-1),arg['date_e'].split('-')[0]);
			break;
			case 'object':
			this.date.e = arg['date_e'];
			break;
			default:
			this.date.e = null;
			break;
		}
		
		this.date.dsbWeekDays = ('object' == typeof(arg['dsb_d'])) ? arg['dsb_d'] : this.setDsbWeekDays(arg['dsb_d']);
		
		this.HTML = new Object();
		this.HTML.anchor = ('string' == typeof(arg['anchor'])) ? document.getElementById(arg['anchor']) : arg['anchor'];
		this.HTML.opener = ('string' == typeof(arg['opener'])) ? document.getElementById(arg['opener']) : arg['opener'];
		this.HTML.opener.style.cursor = 'pointer';
		
		addEvent(this.HTML.opener,'click',function() { Calendar.show(); });
		
		// Set default valid dates interval
		if(this.date.s != null) { this.addDisabledDates(null,this.date.s); }
		if(this.date.e != null) { this.addDisabledDates(this.date.e,null); }
		
		if(this.msie) {
			addEvent(document,'click',function() { Calendar.isClickedOut(); });
		}
		else {
			addEvent(document,'click',function(e) { Calendar.isClickedOut(e); });
		}
		
		addEvent(window,'load',function() { Calendar.setHTML(); });

	},
	
	addDisabledDates: function(s,e) {
		//window.alert(s+'\n'+e);
		if(arguments.length == 1) { e = s; }
		if(s == null && e == null) { return false; }
		if(this.disabledDatesExpression != '') { this.disabledDatesExpression += "||"; }
		
		if(s != null) { s = ''+s.getFullYear()+addZero((s.getMonth()))+addZero((s.getDate())); }
		if(e != null) { e = ''+e.getFullYear()+addZero((e.getMonth()))+addZero((e.getDate())); }
		
		if(s == null) { this.disabledDatesExpression += '(ds <= '+e+')'; }
		else if(e == null) { this.disabledDatesExpression += '(ds >= '+s+')'; }
		else { this.disabledDatesExpression += '(ds >= '+s+' && ds <= '+e+')'; }
		return true;
	},
	
	drag: function(e) {
		var Calendar = this;
		if(!e) { e = window.event; }
		document.onmousemove = function(e) {
			if(!e) { e = window.event; }
			var y, x;
			var tableTop = parseInt(Calendar.HTML.cal.offsetTop);
			var tableLeft = parseInt(Calendar.HTML.cal.offsetLeft);
			switch(navigator.appName) {
				case 'Microsoft Internet Explorer':
				x = e.clientX;
				y = e.clientY;
				document.onmousemove = function() {
					e = window.event;
					if((tableTop + e.clientY - y) >= 0 && (tableTop + Calendar.HTML.cal.offsetHeight + e.clientY - y) <= 768) {
						Calendar.HTML.cal.style.top  = (tableTop + e.clientY - y) + "px";
					}
					if((tableLeft + e.clientX - x) >= 0 && (tableLeft + Calendar.HTML.cal.offsetWidth + e.clientX - x) <= 1024) {
						Calendar.HTML.cal.style.left = (tableLeft + e.clientX - x) + "px";
					}
					return true;
				}
				break;
					
				default:
				x = e.pageX;
				y = e.pageY;
				document.onmousemove = function(e) {
					if((tableTop + e.pageY - y) >= 0 && (tableTop + Calendar.HTML.cal.offsetHeight + e.pageY - y) <= 768) {
						Calendar.HTML.cal.style.top  = (tableTop + e.pageY - y) + "px";
					}
					if((tableLeft + e.pageX - x) >= 0 && (tableLeft + Calendar.HTML.cal.offsetWidth + e.pageX - x) <= 1024) {
						Calendar.HTML.cal.style.left = (tableLeft + e.pageX - x) + "px";
					}
					return true;
				}
				break;
			}
			return true;
		};
		return true;
	},
	
	isClickedOut: function(e) {
		if(!e)	{ e = window.event; element = e.srcElement; }
		else	{ element = e.target; }
		bool = false;
		while(false == bool && undefined != element) {
			if(element == this.HTML.cal || element == this.HTML.opener) { return false; }
			if(element.tagName.toLowerCase() == "html") {
				this.hide();
				return true;
			}
			element = element.parentNode;
		}
		return false;
	},
	
	setSelectedDate: function() {
		this.date.selected = (arguments.length == 1) ? arguments[0] : new Date(arguments[0],(arguments[1]-1),arguments[2]);
		if(this.debug) { window.alert('Calendar.setSelectedDate('+this.date.selected+')'); }
		return true;
	},
	
	setDsbWeekDays: function(dsb_d) {
		this.date.dsbWeekDays = new Object();
		if(undefined != dsb_d) {
			for(i = 0; i < dsb_d.length; i++) {
				this.date.dsbWeekDays[dsb_d[i]] = true;
			}
		}
	},
	
	setPos: function() {
		this.HTML.cal.style.top = (getTop(this.HTML.anchor)+this.HTML.anchor.offsetHeight)+'px';
		this.HTML.cal.style.left = (getLeft(this.HTML.anchor)+this.HTML.anchor.offsetWidth+20)+'px';
	},
	
	setHTML: function() {
		var Calendar = this;
		// Table head
		thd = document.createElement('thead');
		// Months name and arrows
		thd.appendChild(document.createElement('tr'));
		thd.childNodes[0].appendChild(document.createElement('td'));
		thd.childNodes[0].appendChild(document.createElement('th'));
		thd.childNodes[0].appendChild(document.createElement('td'));
		thd.childNodes[0].childNodes[0].className = this.css_pfx + 'prv_m';
		thd.childNodes[0].childNodes[1].colSpan = "5";	// HACK IE
		thd.childNodes[0].childNodes[1].className = this.css_pfx + 'cur_m';
		thd.childNodes[0].childNodes[1].style.cursor = 'move';
		thd.childNodes[0].childNodes[2].className = this.css_pfx + 'nxt_m';
		// Days abbreviation
		thd.appendChild(document.createElement('tr'));
		thd.childNodes[1].className = this.css_pfx + 'day_abb';
		for(i=0;i<7;i++) {
			txt = '';
			thd.childNodes[1].appendChild(document.createElement('th'));
			thd.childNodes[1].childNodes[i].setAttribute('title',TLT.day.name[this.lang][((this.weekStartDay+i)%7)] + txt);
			thd.childNodes[1].childNodes[i].innerHTML = TLT.day.abbr[this.lang][((this.weekStartDay+i)%7)];
			/*
			txt = (this.date.dsbWeekDays[i+1]) ? ' - '+TLT.closed[this.lang] : '';
			thd.childNodes[1].childNodes[i].className = (this.date.dsbWeekDays[i+1]) ? 'dsb' : null;
			*/
		}
		// Table foot
		tfo = document.createElement('tfoot');
		// Today
		tfo.appendChild(document.createElement('tr'));
		tfo.childNodes[0].appendChild(document.createElement('th'));
		tfo.childNodes[0].childNodes[0].className = this.css_pfx + 'cur';
		tfo.childNodes[0].childNodes[0].innerHTML = TLT.tdy[this.lang];
		tfo.childNodes[0].childNodes[0].colSpan = '7';	//	HACK IE
		// Table body
		tbd = document.createElement('tbody');
		for(i=0;i<6;i++) {
			tbd.appendChild(document.createElement('tr'));
			for(j=0;j<7;j++) {
				tbd.childNodes[i].appendChild(document.createElement('td'));
			}
		}
		// Table
		this.HTML.cal = document.createElement('table');
		this.HTML.cal.className = this.css_pfx + 'tbl';
		this.HTML.cal.style.position = 'absolute';
		this.HTML.cal.style.display = 'none';
		this.HTML.cal.style.zIndex = '100';
		this.HTML.cal.appendChild(thd);
		this.HTML.cal.appendChild(tfo);
		this.HTML.cal.appendChild(tbd);
		this.HTML.cal.prv = thd.childNodes[0].childNodes[0];
		this.HTML.cal.cur = thd.childNodes[0].childNodes[1];
		this.HTML.cal.nxt = thd.childNodes[0].childNodes[2];
		this.HTML.cal.tdy = tfo.childNodes[0].childNodes[0];
		this.HTML.cal.tbd = tbd;
		document.body.appendChild(this.HTML.cal);
		
		addEvent(this.HTML.cal.cur,'mousedown',function() { Calendar.drag(); });
		addEvent(document,'mouseup',function() { document.onmousemove = null; });
		
		this.setPos();
	},

	setInnerHTML: function() {
		var Calendar = this;
		//window.alert('setInnerHTML: '+arguments[0]+','+arguments[1]+','+arguments[2]);
		this.date.selected = this.functions.getSelectedDate();
		switch(arguments.length) {
			case 1:
			var y = arguments[0].getFullYear();
			var m = arguments[0].getMonth()+1;
			var d = arguments[0].getDate();
			break;
			
			case 2:
			var y = arguments[1];
			var m = arguments[0];
			var d = this.date.now.getDate();
			break;
			
			case 3:
			var y = arguments[0];
			var m = arguments[1];
			var d = arguments[2];
			this.date.selected = new Date(arguments[0],arguments[1]-1,arguments[2]);
			break;
			
			case 0:
			default:
			var y = this.date.now.getFullYear();
			var m = this.date.now.getMonth()+1;
			var d = this.date.now.getDate();
			break;
		}
		//window.alert('setInnerHTML: '+y+','+m+','+d);
		this.date.daysPerMonth[1] = (((y%4==0)&&(y%100!=0))||(y%400==0)) ? 29 : 28;
		
		var dsp_y = y; 
		var dsp_m = m; 
		var dsp_d = 1; 
		
		var m_first_d = new Date(y,m-1,1).getDay(); 
		var offset = 0;
		offset = (m_first_d >= this.weekStartDay) ? m_first_d-this.weekStartDay : 7-this.weekStartDay+m_first_d ;
		
		if(offset > 0) {
			dsp_m--;
			if(dsp_m < 1) { dsp_m = 12; dsp_y--; }
			dsp_d = this.date.daysPerMonth[dsp_m-1]-offset+1;
		}
		
		var nxt_m = m+1;
		var nxt_m_y = y;
		if(nxt_m > 12) { nxt_m=1; nxt_m_y++; }
		var lst_m = m-1;
		var lst_m_y = y;
		if(lst_m < 1) { lst_m=12; lst_m_y--; }
		
		this.HTML.cal.cur.innerHTML	= TLT.mth.name[this.lang][m-1] + ' ' + y;
		this.HTML.cal.prv.onclick = function() { Calendar.setInnerHTML(lst_m,lst_m_y); };
		this.HTML.cal.nxt.onclick = function() { Calendar.setInnerHTML(nxt_m,nxt_m_y); };
		
		for(i=0;i<6;i++) {
			for(j=0;j<7;j++) {
				if((dsp_m-1) == this.date.selected.getMonth() && dsp_d == this.date.selected.getDate() && dsp_y == this.date.selected.getFullYear()) {
					css_class = 'cur_d';
				}
				else if(dsp_m == m) {
					css_class = 'cur_m';
				}
				else {
					css_class = 'oth_m';
				}
				
				this.HTML.cal.tbd.rows[i].cells[j].innerHTML = dsp_d;
				this.HTML.cal.tbd.rows[i].cells[j].setAttribute('d',dsp_d);
				this.HTML.cal.tbd.rows[i].cells[j].setAttribute('m',dsp_m);
				this.HTML.cal.tbd.rows[i].cells[j].setAttribute('y',dsp_y);
				this.HTML.cal.tbd.rows[i].cells[j].setAttribute('title','');
				this.HTML.cal.tbd.rows[i].cells[j].className = this.css_pfx+css_class;
				this.HTML.cal.tbd.rows[i].cells[j].onclick = function() {
					Calendar.setSelectedDate(parseInt(this.getAttribute('y'),10),parseInt(this.getAttribute('m'),10),parseInt(this.getAttribute('d'),10));
					Calendar.functions.returnSelectedDate(parseInt(this.getAttribute('y'),10),parseInt(this.getAttribute('m'),10),parseInt(this.getAttribute('d'),10));
					Calendar.hide();
				};
				
				special = this.functions.controlDate(dsp_y,dsp_m,dsp_d,0,0,0);
				if(special != true) {
					this.HTML.cal.tbd.rows[i].cells[j].setAttribute('title',special['title']);
					this.HTML.cal.tbd.rows[i].cells[j].className += ' '+special['class'];
					if(!special['event']) { this.HTML.cal.tbd.rows[i].cells[j].onclick = null; }
				}
				/*
				if(spc == null && this.date.dsbWeekDays[j+1]) {
					this.HTML.cal.tbd.rows[i].cells[j].className += ' dsb';
					this.HTML.cal.tbd.rows[i].cells[j].setAttribute('title',((this.date.dsbWeekDays[j+1]) ? TLT.day.name[this.lang][j+1]+' - '+ TLT.closed[this.lang] : ''));
					this.HTML.cal.tbd.rows[i].cells[j].onclick = null;
				}
				*/
				dsp_d++;
				if(dsp_d > this.date.daysPerMonth[dsp_m-1]) { dsp_d=1; dsp_m++; }
				if(dsp_m > 12) { dsp_m=1; dsp_y++; }
				
			}
		}
		
		/*
		var current_weekday = this.date.now.getDay() - this.weekStartDay;
		if(current_weekday < 0) { current_weekday += 7; }
		if(this.date.dsbWeekDays[current_weekday+1]) {
			addClassName(this.HTML.cal.tdy,'dsb');
			this.HTML.cal.tdy.onclick = null;
		}
		else {
		*/
			//removeClassName(this.HTML.cal.tdy,'dsb');
			this.HTML.cal.tdy.onclick = function() {
				Calendar.setSelectedDate(parseInt(Calendar.date.now.getFullYear()),parseInt(Calendar.date.now.getMonth())+1,parseInt(Calendar.date.now.getDate()));
				Calendar.setInnerHTML();
				Calendar.functions.returnSelectedDate(parseInt(Calendar.date.now.getFullYear()),parseInt(Calendar.date.now.getMonth())+1,parseInt(Calendar.date.now.getDate()));
			};
		//}
	},
	
	setLanguage: function(lang) {
		switch(lang) {
			case 'eng':
				this.lang = 'eng';
				break;
			case 'fre':
				this.lang = 'fre';
				break;
			case 'ger':
				this.lang = 'ger';
				break;
			case 'ita':
				this.lang = 'ita';
				break;
			case 'nld':
				this.lang = 'nld';
				break;
			case 'spa':
				this.lang = 'spa';
				break;
		}
		return true;
	},
	
	hide: function() {
		(undefined != this.HTML.cal) ? this.HTML.cal.style.display = 'none' : false;
	},
	
	show: function() {
		if(undefined != this.HTML.cal) {
			this.setPos();
			this.setInnerHTML(this.date.selected);
			this.HTML.cal.style.display = 'block';
		}
	},
	
	// Overwritable functions
	functions: {
		/*
			input	: year,month,day,hour,minute
			output	: false || object {title;class;event}
		*/
		controlDate: function() {
			// date = new Date(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4]);
			//return {'title':'Test !','class':'test','event':true};
			return false;
		},
		
		/*
			input	: year,month,day,hour,minute
			output	: false || object {title;class;event}
		*/
		controlWeekDay: function() {
			// date = new Date(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4]);
			//return {'title':'Test !','class':'test','event':true};
			return false;
		},
		
		/*
			input	: none
			output	: date 
		*/
		getSelectedDate: function() {
			return this.parent.date.selected;
		},
		
		/*
			input	: none
			output	: [hour,minute] 
		*/
		getSelectedTime: function() {
			return [this.parent.date.selected.getHours(),this.parent.date.selected.getMinutes()];
		},
		
		returnSelectedDate: function() {
			window.alert(arguments[0]+','+arguments[1]+','+arguments[2]);
			return (arguments.length == 3) ? true : false;
		},
		
		returnSelectedTime: function() {
			window.alert(arguments[0]+','+arguments[1]);
			return (arguments.length == 2) ? true : false;
		}
	}
};