function Calendar(placeholder, calendarSchedules) {
	this.superClass();
	
	((placeholder != null) ? placeholder : document.body).appendChild(this.placeholder = LZ.createContainer(true));
	
	this.date = new Date();
	this.dayNames = ["D", "S", "T", "Q", "Q", "S", "S"];
	this.monthNames = ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"];
	
	this.placeholder.style.width = Calendar.WIDTH + "px";
	
	this.loadMonth();
	
	this.placeholder.appendChild(this.phDays = LZ.createContainer());
	
	if (calendarSchedules != null) this.setSchedules(calendarSchedules);
	else {
		this.schedules = {};
		this.loadDays();
	}
}

Calendar.BG_COLOR = "#000000";
Calendar.LINES_COLOR = "#444444"; // cor das linhas
Calendar.FONT = "Verdana, Arial, Tahoma, Helvetica"; // fonte
Calendar.FONT_COLOR = "#FFFFFF"; // cor da fonte
Calendar.FONT_COLOR_TODAY = "#FF0000"; // cor da fonte (dia corrente)
Calendar.FONT_COLOR_DISABLED = "#666666"; // cor da fonte (dias desabilitados)
Calendar.FONT_COLOR_SCHEDULE = "#FF9900";
Calendar.FONT_SIZE = 14; // tamanho da fonte
Calendar.HORIZONTAL_MARGIN = 1;
Calendar.LINE_HEIGHT = 24;
Calendar.IMG_LEFT_ARROW;
Calendar.IMG_RIGHT_ARROW;
Calendar.WIDTH = 174;

Calendar.prototype.superClass = DisplayElement;
Calendar.prototype.placeholder;
Calendar.prototype.phDays;
Calendar.prototype.days;
Calendar.prototype.schedules;
Calendar.prototype.date;
Calendar.prototype.month;
Calendar.prototype.dayNames;
Calendar.prototype.monthNames;

Calendar.prototype.setSchedules = function(calendarSchedules) {
	var total = calendarSchedules.length;
	
	this.schedules = {};
	
	for (var i = 0; i < total; i++) {
		var date = calendarSchedules[i];
		
		this.schedules[this.generateScheduleKey(date.getDate(), date.getMonth(), date.getFullYear())] = date;
	}
	
	this.loadDays(); // reload
};

Calendar.prototype.loadMonth = function() {
	var ref = this;
	var monthPH = LZ.createContainer();
	var lArrow;
	var rArrow;
	
	this.month = LZ.createContainer(true);
	lArrow = LZ.createContainer(true);
	rArrow = LZ.createContainer();
	
	if (Calendar.IMG_LEFT_ARROW != null) {
		var img = document.createElement("img");
		
		img.src = Calendar.IMG_LEFT_ARROW;
		img.border = 0;
		img.style.marginTop = "4px";
		lArrow.appendChild(img);
	}
	else {
		lArrow.style.fontSize = Calendar.FONT_SIZE + "px";
		lArrow.innerHTML = "&laquo;";
	}
	
	if (Calendar.IMG_RIGHT_ARROW != null) {
		var img = document.createElement("img");
		
		img.border = 0;
		img.src = Calendar.IMG_RIGHT_ARROW;
		img.style.marginTop = "4px";
		rArrow.appendChild(img);
	}
	else {
		rArrow.style.fontSize = Calendar.FONT_SIZE + "px";
		rArrow.innerHTML = "&raquo;";
	}
	
	lArrow.onmousedown = function(e) {
		var currentMonth = ref.date.getMonth();
		
		if (currentMonth > 0) ref.date.setMonth(currentMonth - 1);
		else {
			ref.date.setFullYear(ref.date.getFullYear() - 1);
			ref.date.setMonth(11);
		}
		
		ref.writeMonth();
		ref.loadDays();
		
		ref.dispatchEvent(new CalendarEvent(CalendarEvent.CHANGE_MONTH, ref.date));
	};
	
	rArrow.onmousedown = function(e) {
		var currentMonth = ref.date.getMonth();
		
		if (currentMonth < 11) ref.date.setMonth(currentMonth + 1);
		else {
			ref.date.setFullYear(ref.date.getFullYear() + 1);
			ref.date.setMonth(0);
		}
		
		ref.writeMonth();
		ref.loadDays();
		
		ref.dispatchEvent(new CalendarEvent(CalendarEvent.CHANGE_MONTH, ref.date));
	};

	lArrow.style.position = rArrow.style.position = this.month.style.position = "relative";
	lArrow.style.cursor = rArrow.style.cursor = "pointer";
	lArrow.style.color = rArrow.style.color = Calendar.FONT_COLOR;
	this.month.style.fontFamily = Calendar.FONT;
	this.month.style.fontSize = Calendar.FONT_SIZE + "px";
	this.month.style.color = Calendar.FONT_COLOR;
	this.month.style.fontWeight = "bold";
	this.month.style.textAlign = "center";
	this.month.style.width = (Calendar.WIDTH - 18) + "px";
	monthPH.style.margin = "0 auto 6px auto";
	
	this.writeMonth();
	
	monthPH.appendChild(lArrow);
	monthPH.appendChild(this.month);
	monthPH.appendChild(rArrow);
	this.placeholder.appendChild(monthPH);
};

Calendar.prototype.loadDays = function() {
	var ref = this;
	var currentMonth = this.date.getMonth();
	var currentYear = this.date.getFullYear();
	
	this.date.setMonth(this.date.getMonth() + 1); // próximo mês
	this.date.setDate(0); // dia "0" próx. mês (último dia mês atual)
	
	var totalDays = this.date.getDate();
	
	this.date.setDate(1); // volta mês atual
	this.date.setMonth(currentMonth);
	this.date.setFullYear(currentYear);
	
	var weekDay = this.date.getDay();
	var totalWeeks = Math.ceil((totalDays + weekDay)/7);
	var column;
	var height = Calendar.LINE_HEIGHT*(totalWeeks + 1);
	
	if (this.days != null) this.phDays.removeChild(this.days);
	
	this.days = LZ.createContainer(true);
	this.days.style.backgroundColor = Calendar.LINES_COLOR;
	
	for (var i = 0; i < totalWeeks*7; i++) {
		if ((column = this.days.childNodes[i%7]) == null) {
			column = LZ.createContainer(true);
			column.style.fontFamily = Calendar.FONT;
			column.style.color = Calendar.FONT_COLOR;
			column.style.fontSize = Calendar.FONT_SIZE + "px";
			column.style.textAlign = "center";
			column.style.padding = "0 3px 0 3px";
			
			if (i > 0) column.style.marginLeft = Calendar.HORIZONTAL_MARGIN + "px";
			
			column.style.height = height + "px";
			column.style.lineHeight = Calendar.LINE_HEIGHT + "px";
			column.style.backgroundColor = Calendar.BG_COLOR;
			column.innerHTML = "<b>" + this.dayNames[i] + "</b><br />";
			
			this.days.appendChild(column);
		}
		
		var phDay = document.createElement("span");
		var isLimitDown = i < weekDay;
		
		if (!isLimitDown && i < (totalDays + weekDay)) {
			var day = (i - weekDay + 1);
			var dateToday = new Date();
			var isToday = (day == dateToday.getDate() && currentMonth == dateToday.getMonth() && currentYear == dateToday.getFullYear());
			var scheduleDate = this.schedules[this.generateScheduleKey(day, currentMonth, currentYear)];
			
			if (scheduleDate != null) {
				phDay.style.color = Calendar.FONT_COLOR_SCHEDULE;
				phDay.style.cursor = "pointer";
				phDay.style.textDecoration = "underline";
				phDay["scheduleDate"] = scheduleDate;
				
				phDay.onclick = function() {
					ref.dispatchEvent(new CalendarEvent(CalendarEvent.CLICK_DAY, this["scheduleDate"]));
				};
			}
			
			if (isToday) phDay.style.color = Calendar.FONT_COLOR_TODAY;
			
			phDay.innerHTML = day;
		}
		else {
			phDay.style.color = Calendar.FONT_COLOR_DISABLED;
			
			if (isLimitDown) phDay.innerHTML = String(totalDays - (weekDay - 1 - i));
			else phDay.innerHTML = String(i - totalDays - weekDay + 1);
		}
		
		phDay.style.fontFamily = Calendar.FONT;
		phDay.style.fontSize = Calendar.FONT_SIZE + "px";
		
		column.appendChild(phDay);
		column.appendChild(document.createElement("br"));
	}
	
	this.date.setDate(1);
	this.date.setMonth(currentMonth);
	this.date.setFullYear(currentYear);
	this.phDays.appendChild(this.days);
};

Calendar.prototype.generateScheduleKey = function(year, month, day) {
	return year + "-" + month + "-" + day;
};

Calendar.prototype.writeMonth = function() {
	this.month.innerHTML = this.monthNames[this.date.getMonth()] + ", " + this.date.getFullYear();
};