
/**
 * @constructor
 */
compono.Calendar = new compono.Object("Calendar");

/**
 * Colecao de meses 
 */
compono.Calendar.month = [];

/**
 * Adiciona a colecao de meses
 * @param {Array} range Colecao
 */
compono.Calendar.month.AddRange = function(range){
	for(var i=0, s; s = range[i]; i++){
		this.push(s);
	}
};

/**
 * Colecao de semanas
 */
compono.Calendar.week = [];

/**
 * Adiciona o range de semanas 
 * @param {Array} range Colecao
 */
compono.Calendar.week.AddRange = function(range){
	for(var i=0, s; s = range[i]; i++){
		this.push(s);
	}
};

compono.Calendar.date = new Date();
compono.Calendar.input = "";
compono.Calendar.trigger = "";
compono.Calendar.container = "";
compono.Calendar.activeDay = "";

compono.Calendar.inputDateFormat = "dd/mm/yyyy";
compono.Calendar.outputDateFormat = "dd/mm/yyyy";

compono.Calendar.openup = false;

compono.Calendar.GetMonth = function(){
	return this.date.getMonth();
};
compono.Calendar.GetMonthName = function(){
	return this.month[this.date.getMonth()];
};
compono.Calendar.GetMonthDays = function(){
	return this.date.getMonthDays(this.date.getFullYear(), this.date.getMonth());
};
compono.Calendar.SetMonth = function(month){
	this.date.setMonth(month)
};
compono.Calendar.IncreaseMonth = function(){
	var m = this.GetMonth() + 1;
	this.SetMonth(m)
	this.Render();
};
compono.Calendar.DecreaseMonth = function(){
	var m = this.GetMonth() - 1;
	this.SetMonth(m)
	this.Render();
};

compono.Calendar.GetWeek = function(){
	return this.date.getDay();
};
compono.Calendar.GetWeekName = function(){
	return this.week[this.date.getDay()];
};

compono.Calendar.GetDay = function(){
	return this.date.getDate();
};

compono.Calendar.SetDay = function(day){
	this.date.setDate(day);
};

compono.Calendar.DeActiveDay = function(anchorDay){
	this.removeClassName("cur",this.activeDay)
};

compono.Calendar.ActiveDay = function(anchor, day){
	if(this.activeDay !== anchor){
		this.DeActiveDay()
	}
	this.activeDay = anchor;
	this.appendClassName("cur",this.activeDay)

	this.SetDay(day);
	this.input.value = this.OutputDate(this.outputDateFormat);
	
	//Executando funo do atributo plotado pelo validador no onChange do campo texto 
	if(this.input.onchange&&typeof(this.input.onchange)=='function'){
		this.input.onchange();
	}
	
	this.Hide();
	this.input.select();
	
};

compono.Calendar.GetYear = function(){
	return this.date.getFullYear();
};
compono.Calendar.GetShortYear = function(){
	return this.date.getYear();
};
compono.Calendar.SetYear = function(year){
	this.date.setFullYear(year);
};

/**
 * SetShortYear
 * Seta o ano por 2 caracteres;
 */
compono.Calendar.SetShortYear = function(year){
	this.date.setYear(year);
};

/**
 * IncreaseYear
 * Aumenta o Ano em 1;
 */
compono.Calendar.IncreaseYear = function(){
	var m = this.GetYear() + 1;
	this.SetYear(m);
	this.Render();
};

/**
 * DecreaseYear
 * Diminui o Ano em 1;
 */
compono.Calendar.DecreaseYear = function(){
	var m = this.GetYear() - 1;
	this.SetYear(m);
	this.Render();
};		

/**
 * SetDate
 * @param {String} Ano;
 * @param {String} Mes;
 * @param {String} Dia;
 */
compono.Calendar.SetDate = function(year,month,day){
	this.date = new Date(year,month-1,day);
		
};

/**
 * SetToday
 * @param {Date} Data corrente;
 */
compono.Calendar.SetToday = function(date){
	this.today = date;
	this.date = date;
};

/**
 * BuildToday
 * Popula o Anchor com o a data atual (passada pelo server);
 */
compono.Calendar.BuildToday = function(){
	var c, t, s;
	c = this.getByClassName("a" ,"today", this.container)
	if(!c.todayBuilded){
		/* #MonthLength */ 
		t = document.createTextNode(	
			this.today.name + " : " + this.today.getDate() + "/" + (this.today.getMonth()+1) + "/" + this.today.getFullYear()
		);
		c.appendChild(t);
		c.onclick = function(){
			var $ = compono.Calendar;
			$.date = $.today;
			$.input.value = $.OutputDate($.outputDateFormat);
			$.Hide();
		}
		c.todayBuilded = true;
	}
	else{
		compono.removeAllChildNodes(c);
		t = document.createTextNode(
			this.today.name + " : " + this.today.getDate() + "/" + (this.today.getMonth()+1) + "/" + this.today.getFullYear()
		);
		c.appendChild(t);
	}
}

/**
 * BuildWeek
 * Cria a lista da dias da Semana, baseado no Array "week";
 */
compono.Calendar.BuildWeek = function(){
	var c, d, t;
	if(!this.weekBuilded){
		c = this.getByClassName("ul" ,"week", this.container);
		this.removeAllChildNodes(c);
		for(var i=0; i<this.week.length;i++){
			d = document.createElement("li");
			t = document.createTextNode(	this.week[i]	);
			c.appendChild(d);
			d.appendChild(t);
		}
		this.weekBuilded = true;
	}
};

/**
 * BuildDays
 * Cria a lista da dias baseado na data vinda do campo;
 * Acha o dia da semana que comea o ms indicado e cria blocos vazios;
 */	
compono.Calendar.BuildDays = function(){
	var container, anchorNode, textNode, weekDay, days, activeDay, length;
	
	days = 1;
	length = this.GetMonthDays();
	container = this.getByClassName("div" ,"days", this.container)
	
	/* dia da semana do primeiro dia do ms */
	weekDay = new Date(this.date.getFullYear(), this.date.getMonth()).getDay() - 1;
	this.removeAllChildNodes(container) /* remove outros dias */
	
	for(var i=0; i < (length + weekDay + 1);i++)
	{
		anchorNode = document.createElement('a');
		if(i > weekDay){
			anchorNode.href = "javascript:void(0)";
			anchorNode.day = days++;
			textNode = document.createTextNode( anchorNode.day );
			anchorNode.appendChild(textNode);
			anchorNode.onclick = function(){
				compono.Calendar.ActiveDay(this, this.day);
			}
		}else{
			this.appendClassName("off",anchorNode);
			anchorNode.disabled = true;
		}
		container.appendChild(anchorNode);
	}
	
	/* today */
	
	/* Dia selecionado */
	activeDay = container.childNodes[ weekDay + this.date.getDate() ];
	
	this.activeDay = activeDay;
	this.appendClassName("cur",activeDay);
};

/**
 * BuildMonth
 * Joga o nome do ms no campo;	
 */	
compono.Calendar.BuildMonth = function()
{
	var container = this.getByClassName("input" ,"month", this.container)
	container.onchange = function(){
		var i = compono.Calendar.month.indexOf(this.value.toUpperCase());
		if(i > -1){
			this.Calendar.SetMonth(i);
			this.Calendar.Render();
		}else{
			this.value = compono.Calendar.GetMonthName();
		}
	}
	
	container.onfocus = function(){
		compono.Calendar.appendClassName("focus",this);
		this.select();
	}
	
	container.onblur = function(){
		compono.Calendar.removeClassName("focus",this);
	}

	/*	IE no entende as setas do teclado no evento onkeypress, 
		deve-se usar onkeydown ou onkeyup */
	function onkey(e){
		if(!e)e=window.event;
		switch(e.keyCode){
			case 38: /*top*/
			case 39: /*right*/
				compono.Calendar.IncreaseMonth();
				break;
			case 40: /*bottom*/
			case 37: /*left*/
				compono.Calendar.DecreaseMonth();
				break;
			default:
		}
	}
	
	if(window.attachEvent){
		container.onkeyup = onkey;
	}else{
		container.onkeypress = onkey;
	}
	
	container.value = this.GetMonthName();
};

/**
 * BuildYear
 * Joga o ano no campo;	
 */	
compono.Calendar.BuildYear = function(){
	var container = this.getByClassName("input" ,"year", this.container);

	container.onchange = function(){
		if(this.value.length == 0) this.value = compono.Calendar.GetYear();
		compono.Calendar.SetYear(parseInt(this.value));
		compono.Calendar.Render();
	}
	container.onfocus = function(){
		compono.Calendar.appendClassName("focus",this);
		this.select();
	}
	container.onblur = function(){
		compono.Calendar.removeClassName("focus",this);
	}
	
	function onkey(e){
		if(!e)e=window.event;
		var $ = compono.Calendar;
		//console.log(e.keyCode)

		switch(e.keyCode){
			case 38: /*top*/
			case 39: /*right*/
				$.IncreaseYear();
				break;
			case 40: /*bottom*/
			case 37: /*left*/
				$.DecreaseYear();
				break;
			default:
				var s = String.fromCharCode(	window.attachEvent ? e.keyCode : e.charCode		)
				if(!this.pattern) this.pattern = new RegExp("^[0-9]+$");
				if(!this.pattern.test(s)){ $.Event.preventDefault(e); }
		}
	}
	container.onkeypress = onkey;
	container.value = this.GetYear();
};

/**
 * Render
 * Renderiza o calendrio;	
 */		
compono.Calendar.Render = function(){
	this.BuildYear();
	this.BuildMonth();
	this.BuildWeek();
	this.BuildDays();
	this.BuildToday();
};

/**
 * Positioning
 * Posiciona o calendrio de acordo com o icone;
 */
compono.Calendar.Positioning = function(){	
	var p = this.findElementPosition(this.trigger);
	this.container.style.top = (p.top + this.trigger.offsetHeight) + "px";	
	this.container.style.left = p.left + "px";
};

/** @ignore */
compono.Calendar.RegExpDateFormat = /[mdy]+/gi;
/** @ignore */
compono.Calendar.RegExpValueFormat = /\d+/g;

/**	
 * InputDate
 * Depura a string de data baseado no formato
 * http://www.merlyn.demon.co.uk/js-dates.htm
 * http://www.merlyn.demon.co.uk/leapyear.htm
 * @argument	{String} value Data em String;
 * @argument	{String} format Formato da data;
 * @return		{Date, Bollean} Se a data montada for invalida com a 
					data escrita entao o metodo retorna false, isso porque o objeto Date incrementa a data qdo
					os valores passados estao fora do range, exemplo 29/02/2007 o objeto Date construiria 01/03/2007;
 */
compono.Calendar.InputDate = function(value,format){
	var d, formats, values, yy, mm, dd;
	
	formats = format.match(this.RegExpDateFormat);
	values = value.match(this.RegExpValueFormat);
	
	

	/* Testa valores de values e formats antes do uso. A falta do teste ocasionava um erro de
	indice nos arrays gerados. Ex de erro gerado: Uma data somente com letras 'asdfg' gera
	um array de values nulo */
	if (values && formats){
		for( var i=formats.length-1, f, v; f=formats[i], v=values[i]; i--){
			v=parseInt(v);
			switch(f.toLowerCase()){
				case "yyyy": case "yy": yy = v;			break;
				case "mm": case "m":	mm = v - 1;		break;
				case "dd": case "d":	dd = v;			break;
			}
		}
		d = new Date(yy,mm,dd);
		if(d.getMonth()==mm && d.getDate()==dd && (d.getFullYear==yy||d.getFullYear().toString().lastIndexOf(yy)>-1)){
			return d;	
		}
	}
	return false;
};

/**	
 * OutputDate
 * Depura a data e retorna uma string de acordo com o formato;
 *
 * @argument	{String} format formato da data a ser retornada;
 * @return		{String} string com a data no formato
 */	
compono.Calendar.OutputDate = function(format){
	
	var date = format;
	
	var reFormat = new RegExp("[mdy]+","gi");
	var formats = format.match(reFormat);
	
	for( var i=0, f; f = formats[i]; i++){
		switch(f){
			case "yyyy":
				date = date.replace("yyyy", this.GetYear());
				break;
			case "yy":
				date = date.replace("yy", this.GetShortYear());
				break;
			case "mm":
				var m = this.GetMonth() + 1;
				if(m < 10) m = "0" + m.toString();
				date = date.replace("mm", m);
				break;
			case "m":
				var m = this.GetMonth() + 1;
				date = date.replace("m", m);
				break;
			case "dd":
				var d = this.GetDay();
				if(d < 10) d = "0" + d.toString();
				date = date.replace("dd", d);
				break;
			case "d":
				var d = this.GetDay();
				date = date.replace("d", d);
				break;
		}
	}	
	
	return date;
};

/**	
 * SHOW
 * Abre o calendario
 * @param {Object} config, objeto com propriedades definidas
 * @
 * @requires	{Node} input: campo que contm a string de data;
 * @requires	{Node} trigger: icone que abre o calendrio;
 * @requires	{String} format: formato da data;
 * {String} ouputFormat: formato da data na resposta do calendario;
 * {String} inputFormat: formato da data em que o calendrio ser montado;
 */
compono.Calendar.Show = function(config, evt)
{
	if(!this.today) return false;
	
	if(!this.openup){
	
		this.input = config.input;
		this.trigger = config.trigger;
		this.container = compono.getByID( config.component );
		this.today.name = config.todayString;
		this.outputDateFormat = config.outputFormat || config.format || config.inputFormat || this.outputDateFormat;
		this.inputDateFormat = config.inputFormat || config.format || config.outputFormat || this.inputDateFormat;
	
		if(config.input.value){
		
			var date = this.InputDate(config.input.value, config.format);
			if(date && this.date !== date){
				this.date = date;
			}else{
				this.date = new Date(this.today);
			}
			this.input.select();
		
		}else{
			this.date = new Date(this.today);
		}
		
		this.Render();

		this.appendClassName("calendar-openup",this.container)
		
		this.Positioning();
		this.openup = true;
		
		this.DocumentEventMouseDown = function(){
			compono.Calendar.Hide();
		}
		this.EventMouseDown = function(e){
			compono.Calendar.Event.stopPropagation(e);
		}

		this.Event.add("mousedown",this.DocumentEventMouseDown,document);
		this.Event.add("mousedown",this.EventMouseDown,this.container);
		this.Event.add("mousedown",this.EventMouseDown,this.input);
		
		function onkey(e){
			if(!e)e=window.event;
			var $ = compono.Calendar;
			
			switch(e.keyCode){
				case 9: /*tab*/
					compono.Calendar.Hide();
				case 27: /*esc*/
					compono.Calendar.Hide();
			}
		}
		config.input.onkeydown = onkey;
		
		this.Windowless();
		
	}else{
		this.Hide();
	}
	return true;
};

/**	
 * HIDE
 * Fecha o calendrio retirando todos os eventos relativos aos itens;
 */
compono.Calendar.Hide = function(){
	
	this.removeClassName("calendar-openup",this.container);
	this.openup = false;
	this.Windowless();
	
	this.Event.remove("mousedown",this.DocumentEventMouseDown,document);
	this.Event.remove("mousedown",this.EventMouseDown,this.container);
	this.Event.remove("mousedown",this.EventMouseDown,this.input);
	
};

/**
 * Windowless Elements
 * Cria um iframe logo abaixo do body do componente, para esse div nao fique abaixo dos selects;
 * @see http://dotnetjunkies.com/WebLog/jking/archive/2003/07/21/488.aspx
 * @see http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/q177/3/78.asp&NoWebContent=1
 * @param {Node} body Elemento em que ser includo o iframe
 */
compono.Calendar.Windowless = function(body){
	var iframe;

	if(!window.attachEvent) return false;
	
	if(!body) body = this.container;
	
	if(!body.WindowlessImplemented){
		
		body.iframe = document.createElement("iframe");
		body.iframe.src = "javascript:false;";
		body.iframe.scrolling = "no";
		body.iframe.frameBorder = "no";
		body.iframe.hideFocus = true;
		this.appendClassName("calendar-windowless", body.iframe);
		
		body.insertAdjacentElement("afterEnd", body.iframe)
		body.WindowlessImplemented = true;
	}
	
	if(this.openup){
		body.iframe.style.width = body.offsetWidth + "px";
		body.iframe.style.height = body.offsetHeight + "px";
		body.iframe.style.top = body.offsetTop + "px";
		body.iframe.style.left = body.offsetLeft + "px";
		this.appendClassName("calendar-windowless-openup", body.iframe);
	}else{
		this.removeClassName("calendar-windowless-openup", body.iframe);
	}
	return true;
}
