/**************************************************************************************************

Archivo ....... calendar.js
Proyecto ...... Genérico
Carpeta ....... /js
Creado en ..... MAR-2009
Modificado .... AGO-2009

Propósito
---------
Muestra un calendario pera seleccionar una fecha.

Funciones Públicas
------------------
showCalendar() --- Muestra el calendario.
hideCalendar() --- Oculta el calendario.

Funciones Auxiliares
--------------------
initCalendar() --- Inicializa el calendario. Llamada automática.
makeCalendar() --- Construye el calendario para un mes y año dados.
selectDate() ----- Carga la fecha del calendario en el control de referencia.
prevMonth() ------ Muestra el calendario del mes anterior.
nextMonth() ------ Muestra el calendario del mes siguiente.
parseDate() ------ Interpreta una fecha con formato. Devuelve un objeto Date().
makeDateString() - Genera un string con una fecha según un formato.
validateDate() --- Valida una fecha según un formato.
compareDates() --- Compara dos obtentos de tipo fecha y devuelve -1, 0 o 1.
**************************************************************************************************/

/**************************************************************************************************
VARIABLES
**************************************************************************************************/

var dayNames = new Array
( "Lun"
, "Mar"
, "Mie"
, "Jue"
, "Vie"
, "Sab"
, "Dom" );

var	monthNames = new Array
( "ENE"
, "FEB"
, "MAR"
, "ABR"
, "MAY"
, "JUN"
, "JUL"
, "AGO"
, "SEP"
, "OCT"
, "NOV"
, "DIC");

var validYears = new Array
( "2008"
, "2009" );

var globalDateSeparator = "-";
var globalDateControl = null;
var globalDateFormat = null;
var globalRootPath = "";


/**************************************************************************************************
PROCESO PRINCIPAL
**************************************************************************************************/

initCalendar();


/**************************************************************************************************
FUNCIONES PÚBLICAS
**************************************************************************************************/

function showCalendar(dateControl, dateFormat) {

	// Almacenamiento de los parámetros en variables globales
	globalDateControl = dateControl;
	globalDateFormat = dateFormat;

	// Si el control de referencia tiene una fecha, se usa como fecha inicial
	var workingDate = new Date();
	var val = dateControl.value;
	if (val) {
		auxDate = parseDate (val, dateFormat);
		if (auxDate)
			workingDate = auxDate;
	}

	// Se obtiene el día, el mes y el año de trabajo
	var workingDay		= workingDate.getDate();
	var workingMonth	= workingDate.getMonth();
	var workingYear		= workingDate.getFullYear();

	// Se genera la vista de mes del calendario
	makeCalendar(workingDay, workingMonth, workingYear);

	// Se hace visible el calendario
	var theDiv = document.getElementById ("calendar_div");
	if (theDiv) {

		// Se activa la visualización del calendario
		theDiv.style.display = "";

		// Se calcula la posición del calendario según el control origen
		var x = 0, y = 0;
		var elem = dateControl;
		do {
			x += elem.offsetLeft;
			y += elem.offsetTop;
			elem = elem.offsetParent;
		}
		while (elem);

		x -= window.pageXOffset;
		y -= window.pageYOffset;

		// Se establece la posición del calendario
		theDiv.style.left = x + "px";
		theDiv.style.top = y + "px";
	}
}

function hideCalendar() {

	var theDiv = document.getElementById ("calendar_div");
	if (theDiv)
		theDiv.style.display = "none";
}


/**************************************************************************************************
FUNCIONES AUXILIARES
**************************************************************************************************/

function initCalendar() {

	var strDivStyle = "";
	strDivStyle += "border: solid black 1px; ";
	strDivStyle += "position: fixed; ";
	strDivStyle += "display: none; ";
	strDivStyle += "padding: 3px; ";
	strDivStyle += "z-index: 1; ";
	
	var strHTML = "";
	strHTML += "<style>";

	strHTML += ".calendar { ";
	strHTML += "font-family: Arial, sans-serif; ";
	strHTML += "font-size: 14px; ";
	strHTML += "background-color: white; ";
	strHTML += "font-weight: normal; ";
	strHTML += "color: black; ";
	strHTML += "}";

	strHTML += "a.calendar { ";
	strHTML += "text-decoration: none; ";
	strHTML += "}";

	strHTML += "a.calendar:hover { ";
	strHTML += "background-color: black; ";
	strHTML += "color: white; ";
	strHTML += "}";

	strHTML += "a.caldayres { ";
	strHTML += "font-family: Arial, sans-serif; ";
	strHTML += "font-size: 14px; ";
	strHTML += "background-color: white; ";
	strHTML += "font-weight: bold; ";
	strHTML += "color: red; ";
	strHTML += "text-decoration: none; ";
	strHTML += "border: 1px dotted red; ";
	strHTML += "}";

	strHTML += "a.caldayres:hover { ";
	strHTML += "background-color: red; ";
	strHTML += "color: white; ";
	strHTML += "}";

	strHTML += ".calheader { ";
	strHTML += "background-color: gray; ";
	strHTML += "color: black; ";
	strHTML += "padding: 2px; ";
	strHTML += "}";

	strHTML += ".calbutton { ";
	strHTML += "border: solid 1px black; ";
	strHTML += "background-color: white; ";
	strHTML += "padding: 2px; ";
	strHTML += "</style>";

	strHTML += "<div ";
	strHTML += "id=\"calendar_div\" ";
	strHTML += "class=\"calendar\" ";
	strHTML += "style=\"" + strDivStyle + "\">";
	strHTML += "<span ";
	strHTML += "id =\"calendar_position\">";
	strHTML += "&nbsp;";
	strHTML += "</span>";
	strHTML += "</div>";

	document.write (strHTML);
}

function makeCalendar(workingDay, workingMonth, workingYear) {

	// Variable para contener el código HTML del calendario
	var strHTML = "";

	// Cabecera del calendario
	strHTML += "<table ";
	strHTML += "width=\"100%\" ";
	strHTML += "class=\"calheader\">";
	strHTML += "<tr>";
	strHTML += "<td ";
	strHTML += "style=\"text-align: left;\">";

	// Combobox de selección de meses
	strHTML += "<select ";
	strHTML += "onchange=\"makeCalendar(" + workingDay + ", this.value, " + workingYear + ");\">";
	for (var i = 0; i < 12; ++i) {
		strHTML += "<option ";
		if (i == workingMonth)
			strHTML += "selected ";
		strHTML += "value=\"" + i + "\">";
		strHTML += monthNames[i];
		strHTML += "</option>";
	}
	strHTML += "</select>";
	strHTML += "&nbsp;";

	// Combobox de selección de años
	strHTML += "<select ";
	strHTML += "onchange=\"javascript: makeCalendar(" + workingDay + ", " + workingMonth + ", this.value);\">";
	for (var i = 0; i < validYears.length; ++i) {
		strHTML += "<option ";
		if (validYears[i] == workingYear)
			strHTML += "selected ";
		strHTML += "value=\"" + validYears[i] + "\">";
		strHTML += validYears[i];
		strHTML += "</option>";
	}
	strHTML += "</select>";

	// Inicio botonera (barra de botones)
	strHTML += "</td>";
	strHTML += "<td ";
	strHTML += "style=\"text-align: right;\">";

	// Botón mes anterior
	strHTML += "<img ";
	strHTML += "width=\"9\" ";
	strHTML += "height=\"11\" ";
	strHTML += "class=\"calbutton\" ";
	strHTML += "title=\"Anterior\" ";
	strHTML += "src=\"" + globalRootPath + "images/izquierda.gif\" ";
	strHTML += "onclick=\"javascript: prevMonth(" + workingDay + ", " + workingMonth + ", " + workingYear + ");\" ";
	strHTML += "/>";
	strHTML += "&nbsp;";

	// Botón mes siguiente
	strHTML += "<img ";
	strHTML += "width=\"9\" ";
	strHTML += "height=\"11\" ";
	strHTML += "class=\"calbutton\" ";
	strHTML += "title=\"Siguiente\" ";
	strHTML += "src=\"" + globalRootPath + "images/derecha.gif\" ";
	strHTML += "onclick=\"javascript: nextMonth(" + workingDay + ", " + workingMonth + ", " + workingYear + ");\" ";
	strHTML += "/>";
	strHTML += "&nbsp;";
	
	// Botón cerrar calendario
	strHTML += "<img ";
	strHTML += "width=\"11\" ";
	strHTML += "height=\"11\" ";
	strHTML += "class=\"calbutton\" ";
	strHTML += "title=\"Cerrar\" ";
	strHTML += "src=\"" + globalRootPath + "images/cerrar.gif\" ";
	strHTML += "onclick=\"javascript: hideCalendar();\" ";
	strHTML += "/>";

	// Fin botonera (barra de botones)
	strHTML += "</td>";

	// Fin cabecera del calendario
	strHTML += "</tr>";
	strHTML += "</table>";

	// Inicio de la tabla HTML que contendrá los días del calendario
	strHTML += "<table ";
	strHTML += "class=\"calendar\">";
	strHTML += "<tr>";

	// Se dibujan los nombres de los días de la semana
	for (var i = 0; i < 7; ++i ) {
		strHTML += "<td ";
		strHTML += "style=\"width: 35px; text-align: right;\" ";
		strHTML += "class=\"calendar\">";
		strHTML += "<b>";
		strHTML += dayNames[i];
		strHTML += "</b>";
		strHTML += "</td>";
	}

	// Salto de línea
	strHTML += "</tr>";
	strHTML += "<tr>";

	// Se obtiene el día de la semana: 0=Lun, 1=Mar, ... 6=Dom
	var workingDate = new Date ();
	workingDate.setDate (1);
	workingDate.setMonth (workingMonth);
	workingDate.setFullYear (workingYear);
	var weekDay = workingDate.getDay();
	weekDay = (weekDay == 0) ? 6 : weekDay - 1;

	// Se unen las celdas para identar el primer día del mes
	if (weekDay > 0) {
		strHTML += "<td ";
		strHTML += "class=\"calendar\" ";
		strHTML += "colspan='" + weekDay + "'>";
		strHTML += "&nbsp;";
		strHTML += "</td>";
	}

	// Recorrido por los días del mes
	for (var currentDay = 1; currentDay < 32; ++currentDay) {

		// Validación de la fecha. Si no es válida se rompe el bucle.
		workingDate.setDate (currentDay);
		if (currentDay != workingDate.getDate())
			break;

		var strStyle = "calendar";
		if (workingDay == currentDay)
			strStyle = "caldayres";

		// Se dibuja el siguiente día del mes
		strHTML += "<td ";
		strHTML += "style='text-align: right;'>";
		strHTML += "<a ";
		strHTML += "class=\"" + strStyle + "\" ";
		strHTML += "href=\"javascript:selectDate(" + currentDay + ", " + workingMonth + ", " + workingYear + ");\">";
		strHTML += currentDay;
		strHTML += "</a>";
		strHTML += "</td>";

		// Los domingos se hace un salto de línea
		if (++weekDay > 6) {
			strHTML += "</tr>";
			strHTML += "<tr>";
			weekDay = 0;
		}
	}

	// Fin de la tabla HTML
	strHTML += "</tr>";
	strHTML += "</table>";

	// Se traspasa el código HTML completo a su lugar
	var elem = document.getElementById ("calendar_position");
	if (elem)
		elem.innerHTML = strHTML;
}

function selectDate (strDay, strMonth, strYear) {

	var theDate = makeDateString (strDay, strMonth, strYear, globalDateFormat);
	if (theDate) {
		globalDateControl.value = theDate;
		hideCalendar();
	}
}

function prevMonth (workingDay, workingMonth, workingYear) {

	// Se resta un mes y si es necesario un año
	if (--workingMonth < 0) {
		workingMonth = 11;
		workingYear--;
	}

	// Se comprueba que el año esté entre los años válidos y se construye el calendario
	for (var i = 0; i < validYears.length; ++i) {
		if (validYears[i] == workingYear) {
			makeCalendar (workingDay, workingMonth, workingYear);
			return;
		}
	}

	// Año no válido: Se pone la fecha mínima
	makeCalendar (workingDay, 0, validYear[0]);
}

function nextMonth (workingDay, workingMonth, workingYear) {

	// Se suma un mes y si es necesario un año
	if (++workingMonth >= 12) {
		workingMonth = 0;
		workingYear++;
	}

	// Se comprueba que el año esté entre los años válidos y se construye el calendario
	for (var i = 0; i < validYears.length; ++i) {
		if (validYears[i] == workingYear) {
			makeCalendar (workingDay, workingMonth, workingYear);
			return;
		}
	}

	// Año no válido: Se pone la fecha máxima
	makeCalendar (workingDay, 11, validYear[validYear.length - 1]);
}

/**************************************************************************************************
Función parseDate()
---------------------------------------------------------------------------------------------------
Interpreta una fecha en un string según el formato indicado. Devuelve un objeto Date() con la
fecha interpretada o false si se produce un error. Función inversa a makeDateString().

Parámetros
---------------------------------------------------------------------------------------------------
dateText --- Fecha a interpretar.
dateFormat - Formato de la fecha.

Formatos válidos
---------------------------------------------------------------------------------------------------
dd-mm-yyyy	mm-dd-yyyy	yyyy-mm-dd	yyyy-dd-mm
dd-mm-aaaa	mm-dd-aaaa	aaaa-mm-dd	aaaa-dd-mm

NOTAS
---------------------------------------------------------------------------------------------------
El separador de fechas es configurable mediante la variable "globalDateSeparator".
**************************************************************************************************/

function parseDate (dateText, dateFormat)
{
	arrayFormat	= dateFormat.split (globalDateSeparator);
	arrayDate = dateText.split (globalDateSeparator);
	if (arrayFormat.length != arrayDate.length)
		return false;

	var auxDate = new Date ();

	for	(var i = 0; i < 3; i++) {
		
		if (isNaN (arrayDate[i]))
			return false;

		var auxFormat = arrayFormat[i];
		var auxValue = parseInt (arrayDate[i], 10);

		if (auxFormat == "dd") {
			auxDate.setDate (auxValue);
		}
		else if (auxFormat == "mm") {
			auxDate.setMonth (auxValue - 1);
		}
		else if (auxFormat == "yyyy" || auxFormat == "aaaa") {
			auxDate.setYear (auxValue);
		}
		else
			return false;
	}

	return auxDate;
}

/**************************************************************************************************
Función makeDateString()
---------------------------------------------------------------------------------------------------
Escribe una fecha en un string con el formato adecuado. Devuelve un objeto string con la fecha
o false si se produce un error. Función inversa a parseDate().

Parámetros
---------------------------------------------------------------------------------------------------
strDay ----- Día del mes (1 a 31).
strMonth --- Mes del año (0 a 11).
strYear ---- Año con 4 dígitos.
dateFormat - Formato de la fecha.

Formatos válidos
---------------------------------------------------------------------------------------------------
dd-mm-yyyy 	mm-dd-yyyy	yyyy-mm-dd	yyyy-dd-mm
dd-mm-aaaa 	mm-dd-aaaa	aaaa-mm-dd	aaaa-dd-mm

NOTAS
---------------------------------------------------------------------------------------------------
El separador de fechas es configurable mediante la variable "globalDateSeparator".
**************************************************************************************************/

function makeDateString (strDay, strMonth, strYear, dateFormat) {

	var strDate = "";

	arrayFormat	= dateFormat.split (globalDateSeparator);
	for	(var i = 0; i < arrayFormat.length; i++) {

		if (strDate != "")
			strDate += globalDateSeparator;

		var auxFormat = arrayFormat[i];

		if (auxFormat == "dd") {

			auxDay = parseInt (strDay);
			if (auxDay < 10)
				auxDay = "0" + auxDay;

			strDate += auxDay;
		}
		else if (auxFormat == "mm") {

			auxMonth = 1 + parseInt (strMonth);
			if (auxMonth < 10)
				auxMonth = "0" + auxMonth;

			strDate += auxMonth;
		}
		else if (auxFormat == "yyyy" || auxFormat == "aaaa") {
			strDate += strYear;
		}
		else
			return false;
	}

	return strDate;
}


/**************************************************************************************************
Función validateDate()
---------------------------------------------------------------------------------------------------
Valida una fecha según un formato.

Parámetros
---------------------------------------------------------------------------------------------------
dateString - Fecha a validar.
dateFormat - Formato de la fecha.

Formatos válidos
---------------------------------------------------------------------------------------------------
dd-mm-yyyy	mm-dd-yyyy	yyyy-mm-dd	yyyy-dd-mm
dd-mm-aaaa	mm-dd-aaaa	aaaa-mm-dd	aaaa-dd-mm

NOTAS
---------------------------------------------------------------------------------------------------
El separador de fechas es configurable mediante la variable "globalDateSeparator".
**************************************************************************************************/

function validateDate (dateString, dateFormat) {

	// Validación de la longitud de la fecha
	if (dateString.length != 10)
		return false;

	// Obtención de los componentes de la fecha
	arrayFormat	= dateFormat.split (globalDateSeparator);
	arrayDate = dateString.split (globalDateSeparator);
	if (arrayFormat.length != 3
		|| arrayDate.length != 3)
		return false;

	var dia, mes, any;
	for	(var i = 0; i < 3; i++) {

		if (isNaN (arrayDate[i]))
			return false;

		var auxFormat = arrayFormat[i];
		var auxValue = arrayDate[i];

		if (auxFormat == "dd")
			dia = auxValue;

		else if (auxFormat == "mm")
			mes = auxValue;

		else if (auxFormat == "yyyy" || auxFormat == "aaaa")
			any = auxValue;

		else
			return false;
	}

	// Validación de los componentes de la fecha
	if (isNaN (dia)
		|| isNaN (mes)
		|| isNaN (any))
		return false;

	// Validación del mes
	if (mes < 1 || mes > 12)
		return false;

	// Validación del día
	var diasmes = [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
	if (dia < 1 || dia > diasmes[mes])
		return false;

	// Fin correcto
	return true
}

/**************************************************************************************************
Función compareDates()
---------------------------------------------------------------------------------------------------
Compara dos objetos fecha y devuelve 0 si son iguales, 1 si la primera es mayor o -1 si  la segunda
es mayor. Devuelve -99 si no se puede comparar.

Parámetros
---------------------------------------------------------------------------------------------------
date1 - Primera fecha a comparar.
date2 - Segunda fecha a comparar.
**************************************************************************************************/

function compareDates (date1, date2) {

	var aux1 = date1.getFullYear();
	var aux2 = date2.getFullYear();
	if (isNaN (aux1) || isNaN (aux2))
		return -99;

	if (aux1 > aux2) return 1;
	if (aux1 < aux2) return -1;

	aux1 = date1.getMonth();
	aux2 = date2.getMonth();
	if (isNaN (aux1) || isNaN (aux2))
		return -99;

	if (aux1 > aux2) return 1;
	if (aux1 < aux2) return -1;

	aux1 = date1.getDate();
	aux2 = date2.getDate();
	if (isNaN (aux1) || isNaN (aux2))
		return -99;

	if (aux1 > aux2) return 1;
	if (aux1 < aux2) return -1;

	return 0;
}
