/*
Autor: Daniel Pastor Romero
Fecha creación: 11-Ene-2010
Fecha última modificación: -

Para documentación, ver el final de este archivo...
*/

Date.prototype.a_cero=function()
{
	this.setMilliseconds(0);
	this.setSeconds(0);
	this.setMinutes(0);
	this.setHours(0);
}

function Calendario_vf(v_contenedor, v_metodo, v_mes, v_anho)	//Este es el único lugar en que v_mes es 1-12...
{
	this.mes=null;	//Se guarda internamente desde 0 a 11, por concordancia con el objeto Date.
	this.anho=null;

	this.primer_dia=null;	//Representan el primer y último día a mostrar.
	this.ultimo_dia=null;
	this.dias=new Array();	//Será un array de dias...
	
	this.contenedor=null;	//Lo contiene todo...

	this.selector=null; 	//Contenedor del selector
	this.cabecera=null;	//Contenedor de la cabecera.
	this.calendario=null;	//Contenedor de los días.

	this.metodo_callback=v_metodo;	//Método de callback para cada día...
        this.metodo_actualizar = null; //Metodo que refresca el calendario con utilidad extra
	this.padre=null; //Por si el calendario está embebido en algo a lo que tenemos que acceder...

	this.crear(v_contenedor, v_mes, v_anho);
}

Calendario_vf.prototype.asignar_a=function(elemento)
{
	this.padre=elemento;
}

/*
Autor: Daniel Pastor Romero
Fecha creación: 11-Ene-2010
Fecha última modificación: -

Funcionalidad: Actualiza el calendario: asigna un nuevo mes y año y rellena los
nuevos días del calendario.
Recibe: Un mes (de 0 a 11) y un año de cuatro dígitos.
Devuelve: Nada
*/

Calendario_vf.prototype.actualizar=function(v_mes, v_anho) //v_mes entra como 0-11...
{
	this.inicializar_fechas(v_mes, v_anho);
	this.rellenar_dias_calendario();
        if (this.metodo_actualizar != null)
            {
                this.metodo_actualizar();
            }
}


Calendario_vf.prototype.asignar_metodo_actualizar = function(v_metodo)
{
    this.metodo_actualizar = null;
    this.metodo_actualizar = v_metodo;
}


/*
Autor: Daniel Pastor Romero
Fecha creación: 11-Ene-2010
Fecha última modificación: -

Funcionalidad: Obtiene la cantidad de días del mes para el mes y año que se han
pasado. Hace los cálculos de los años bisiestos.
Recibe: El mes (de 0 a 11) y el año.
Devuelve: La cantidad de días en el mes.
*/

Calendario_vf.prototype.dias_en_mes=function(v_mes, v_anho) //v_mes entra como 0-11...
{
	//Es año bisiesto?...
	var bisiesto=false;
	var dias_por_mes=Array(31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

	if (v_anho % 400 == 0) bisiesto=true;
	else if(v_anho % 4 == 0 && v_anho % 100 != 0) bisiesto=true;

	if(bisiesto)
	{
		dias_por_mes[1]=28;	
	}

	return dias_por_mes[v_mes];
}

/*
Autor: Daniel Pastor Romero
Fecha creación: 11-Ene-2010
Fecha última modificación: -

Funcionalidad: El constructor... Nunca lo llamamos a mano sino que se llama 
sólo al hacer un "new Calendario_vf".
Recibe: El contenedor del calendario (una capa). El mes y el año son opcionales,
en su ausencia se asigna el día de hoy.
Devuelve: Nada.
*/

Calendario_vf.prototype.crear=function(v_contenedor, v_mes, v_anho)	//Inicializar el calendario...
{
	if(!v_contenedor)
	{
		alert('ERROR: Sin contenedor');
	}
	else
	{	
		this.inicializar_fechas(v_mes-1, v_anho);
		this.inicializar_contenedor(v_contenedor);		
	}
}

/*
Autor: Daniel Pastor Romero
Fecha creación: 11-Ene-2010
Fecha última modificación: -

Funcionalidad: Calcula las fechas que gobiernan la funcionalidad del calendario:
básicamente la primera y la última fecha... Además asigna "this.mes" y
"this.anho". Este debe ser el único punto en que estos datos se modifiquen.
Recibe: El mes (0-11) y el año.
Devuelve: Nada.
*/

Calendario_vf.prototype.inicializar_fechas=function(v_mes, v_anho)
{
	//Inicializamos el mes y año...
	var temp=new Date();

	if(!v_mes || !v_anho)	//Por defecto es "Hoy".
	{
		this.mes=temp.getMonth();
		this.anho=temp.getFullYear();
	}
	else
	{
		this.mes=v_mes;
		this.anho=v_anho;
	}

	temp.setFullYear(this.anho, this.mes, 1);		

	/*
	Con el mes y año podemos calcular el primer lunes y el último domingo...
	Básicamente desde "temp" (día 1) vamos atrás hasta un Lunes y luego
	hacia adelante desde el último día del mes...
	*/

	this.primer_dia=new Date(); 
	this.primer_dia.setFullYear(this.anho, this.mes, 1);	//No usamos =, parece que asigna referencias.

	this.ultimo_dia=new Date(); 
	this.ultimo_dia.setFullYear(this.anho, this.mes, this.dias_en_mes(this.mes, this.anho) );

	//El primer día.
	var i=0;

	while(this.primer_dia.getDay() != 1)
	{
		this.primer_dia.setDate(this.primer_dia.getDate()-1);
	}

	//El último día.

	while(this.ultimo_dia.getDay() != 0)
	{
		this.ultimo_dia.setDate(this.ultimo_dia.getDate()+1);
	}

	this.primer_dia.a_cero();
	this.ultimo_dia.a_cero();
}

/*
Autor: Daniel Pastor Romero
Fecha creación: 11-Ene-2010
Fecha última modificación: -

Funcionalidad: Crea el contenedor del calendario, asigna la cabecera de los días
de la semana y, además, rellena por primera vez los días del calendario.
Recibe: El contenedor, la capa dentro de la cual vamos a montar el calendario.
Devuelve: Nada.
*/

Calendario_vf.prototype.inicializar_contenedor=function(v_contenedor)
{
	var dias_cabecera=Array('L', 'M', 'X', 'J', 'V', 'S', 'D');
	var i=0;
	this.contenedor=v_contenedor;

	//El selector...
	this.selector=new this.Selector(this);

	//Rellenamos la cabecera...
	this.cabecera=montar_elemento('div', 'cabecera_calendario', null, this.contenedor);
	for(i; i<dias_cabecera.length; i++)
	{
		montar_elemento('div', 'dia', dias_cabecera[i], this.cabecera);
	}
	montar_elemento('div', 'clear', null, this.cabecera);

	//Rellenamos los dias..
	this.calendario=montar_elemento('div', 'contenedor_calendario', null, this.contenedor);
	this.rellenar_dias_calendario();
	montar_elemento('div', 'clear', null, this.contenedor); 	//Poner el clear...
}

/*
Autor: Daniel Pastor Romero
Fecha creación: 11-Ene-2010
Fecha última modificación: -

Funcionalidad: Itera desde la fecha de inicio hasta la fecha de fin y va creando
los días entre medio. Los días se crean en un array para poder referirse a ellos
y además se van asimilando al contenedor de días del calendario.
Recibe: Nada.
Devuelve: Nada.
*/

Calendario_vf.prototype.rellenar_dias_calendario=function()
{
	var aquello=this;
	var x=0;

	//Limpiar array de dias...
	for(x in this.dias)
	{
		this.dias[x].contenedor.onclick=null;
	}

	this.dias.splice(0, this.dias.length);

	//Limpiar los dias...
	eliminar_contenido(this.calendario);

	var temp=new Date();
	temp.setFullYear(this.primer_dia.getFullYear(), this.primer_dia.getMonth(), this.primer_dia.getDate());	
	temp.a_cero();

	while(this.ultimo_dia >= temp)
	{
		//Si intentamos crear con temp pasamos una referencia de la fecha y TODAS se crean con la misma...
		var fecha_crear=new Date();

		fecha_crear.setFullYear(temp.getFullYear(), temp.getMonth(), temp.getDate());	
		fecha_crear.a_cero();

		this.dias.push(new this.Dia(fecha_crear, this));
		this.calendario.appendChild(this.dias[this.dias.length-1].contenedor);

		temp.setDate(temp.getDate()+1);	
	}

	//Asignar los eventos...
	if(this.metodo_callback)
	{			
		for(x in this.dias)
		{
			var cerrado = function(elemento)
			{							
    				return function()				
				{
					aquello.metodo_callback(elemento); 
				};
			};

			//Nos aseguramos de que sólo podemos hacer click en el mes actual.
			if(this.dias[x].fecha.getMonth() == this.mes)
			{
				var funcion_temporal = cerrado(this.dias[x]);
				this.dias[x].contenedor.onclick=funcion_temporal;
			}
	
			/*
			Esto se merece un comentario en profundidad... Si 
			simplemente iteramos y nos dedicamos a hacer:
			this.dias[x].contenedor.onclick=function(){aquello.metodo_callback(aquello.dias[x]);};
			no explota pero cuando ha terminado todos los clicks 
			hacen referencia al último que se hizo: de alguna forma
			guarda referencia a la x y cuando haces click en cualquier 
			cosa llama a la función con el último valor de x.
			
			Si creamos ese "cerrado" que devuelve una función podemos
			escapar del valor que fuera: se llama a cerrado con el
			objeto y se nos devuelve una función con la copia local
			del objeto. De este modo se escapa
			*/
		}
	}
}

/*
Se incluye una clase día. Básicamente está aquí para poderla extender con más
funcionalidades. Cada uno de estos días tiene el contenedor que se muestra en
en calendario y, por otro lado, los datos propios de la fecha.
*/

Calendario_vf.prototype.Dia=function(v_fecha, v_padre)
{
	this.padre=v_padre;
	this.fecha=v_fecha;

	this.contenedor=null;
	this.dia=null;
	this.crear();
}

/*
Autor: Daniel Pastor Romero
Fecha creación: 11-Ene-2010
Fecha última modificación: -

Funcionalidad: El constructor.
Recibe: Nada.
Devuelve: Nada.
*/

Calendario_vf.prototype.Dia.prototype.crear=function()
{	
	var clases='';
	var aquello=this;
	var hoy=new Date();
	hoy.a_cero();

	if(this.fecha.getDay()==1) clases+=' primero';
	else if(this.fecha.getDay()==0) clases+=' ultimo';

	if(this.fecha.getMonth()!=this.padre.mes) clases+=' otro_mes';
	if(this.fecha.getTime()==hoy.getTime()) clases+=' hoy';	//Milisegundos...

	this.contenedor=montar_elemento('div', 'contenedor_dia '+clases, null, null);
	this.dia=montar_elemento('div', 'dia', this.fecha.getDate(), this.contenedor);
}

Calendario_vf.prototype.Selector=function(v_padre)
{
	this.padre=v_padre;
	var aquello=this;

	this.contenedor=null;
	this.desplegable_mes=null;
	this.desplegable_anho=null;

	this.crear();

	this.desplegable_mes.onchange=function() {aquello.padre.actualizar(aquello.desplegable_mes.value, aquello.desplegable_anho.value);}
	this.desplegable_anho.onchange=function() {aquello.padre.actualizar(aquello.desplegable_mes.value, aquello.desplegable_anho.value);}
}

Calendario_vf.prototype.Selector.prototype.crear=function()
{
	var nombre_meses=Array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre');
	var i=0;
	var hoy=new Date();
	hoy.a_cero();

	this.contenedor=montar_elemento('div', 'seleccion_fecha', null, this.padre.contenedor);

	this.desplegable_mes=montar_elemento('select', null, null, this.contenedor);
	for(i=0; i<nombre_meses.length; i++)
	{
		insertar_opcion(i, nombre_meses[i], this.desplegable_mes);
	}	
	this.desplegable_mes.value=hoy.getMonth();	//Marcamos el mes actual...

	this.desplegable_anho=montar_elemento('select', null, null, this.contenedor);
	for(i=hoy.getFullYear(); i<(hoy.getFullYear()+20); i++)
	{
		insertar_opcion(i, i, this.desplegable_anho);
	}	
	this.desplegable_anho.value=hoy.getFullYear();
}

/*
********************************************************************************
Calendario.
********************************************************************************

Este es el calendario propio de la casa. Contiene el propio "calendario" y 
además las clases "Dia" y "Selector" que están relacionadas.

--------------------------------------------------------------------------------
Listado de Propiedades:
--------------------------------------------------------------------------------

>>mes: Entre 0 y 11, es el mes que se está representando.

>>anho: 4 dígitos, es el año que se está representando.

>>primer_dia: Del tipo Date, es la fecha del primer Lunes que se muestra en el
calendario, aunque no sea el primer día del mes.

>>ultimo_dia: Del tipo Date, es la fecha del último Domingo que se muestra en el
calendario, aunque no sea el primer día del mes.

>>dias: Del tipo Array(), inicialmente vacío, se rellena con un array de tipo 
Dia cuando se generan los días del calendario. Puede usarse para operar sobre
cada día de forma individual.

>>contenedor: Una referencia al elemento del HTML que contendrá todo el
calendario... 

>>selector: En principio es null aunque al crear el calendario es una referencia
al objeto de tipo Selector que esté asociado al calendario.

>>cabecera: En principio es null aunque al crear el calendario se convierte en
una referencia a la capa que contiene la cabecera de los días.

>>calendario: En principio es null aunque al crear el calendario se convierte
en una referencia a la capa que contiene todos los días.

>>metodo_callback: En principio es null. Si se asigna debe hacerse a una función
externa que se ejecutará cuando se haga click en un día. La función debe recibir
un parámetro que será (de forma automática) el objeto de la clase Dia en el que
se ha hecho click.

>>padre: En principio es null. Si se asigna simplemente establece un vínculo
entre el calendario y otro elemento cualquiera para poder acceder al mismo 
desde el contexto del calendario (por ejemplo, en la función callback).

--------------------------------------------------------------------------------
Calendario_vf(v_contenedor, v_metodo, [v_mes], [v_anho])
--------------------------------------------------------------------------------

>>Constructor, llama a crear() y asigna algunos miembros del calendario. 

v_contenedor: El contenedor donde vamos a colocar el calendario, usualmente una
capa. Necesitamos la referencia al elemento, no la id o algo así.

v_método: El método que ocurrirá cuando se haga click en cualquiera de los días
hábiles del mes. El método debe ser externo al calendario y siempre recibe un
parámetro de forma automática: el Dia completo en el que se ha hecho click.

v_mes [opcional]: El mes en el que aparece el calendario. Si se omiten el mes y
el año se usa la fecha actual. El mes está entre 1-12

v_anho [opcional]: El año en el que aparece el calendario. Si se omiten el mes y
el año se usa la fecha actual. El año es de 4 dígitos.

--------------------------------------------------------------------------------
asignar_a(elemento)
--------------------------------------------------------------------------------

>>Establece una relación "arbitraria" entre el calendario y cualquier otro 
elemento. A partir de ese momento se puede acceder al otro elemento usando
"this.padre". No es obligatorio enlazar el calendario a otros elementos.

elemento: La referencia al elemento al cual el calendario está enlazado. Debe
ser una referencia, no nos sirve un id o nombre.

--------------------------------------------------------------------------------
actualizar(v_mes, v_anho)
--------------------------------------------------------------------------------

>>Actualiza el contenido completo del calendario y lo coloca con el mes y año
que se especifiquen. Incluye llamadas implicitas a "inicializar_fechas" y
"rellenar_dias_calendario".

v_mes: El mes que debe usar en un rango de 0-11.
v_anho: El año que debe usar. 4 dígitos.

--------------------------------------------------------------------------------
dias_en_mes=function(v_mes, v_anho)
--------------------------------------------------------------------------------

>>Esta función es para el uso interno del calendario y devuelve los días que 
tiene el mes especificado en el año especificado. Por lo general no tiene
sentido llamarla pero tampoco es necesario hacerla privada.

v_mes: El mes que debe usar. Rango de 0-11.
v_anho: El año que debe usar. 4 dígitos.

--------------------------------------------------------------------------------
crear(v_contenedor, [v_mes], [v_anho])
--------------------------------------------------------------------------------

>>Hace la función de "costructor" puesto que se le llama automáticamente al 
hacer "new calendario_vf". Inicializa las fechas del calendario y su contenedor
de forma automática. Este método es el responsable de inicializar el contenedor
del calendario... No sería conveniente llamar a este método.

v_contenedor: La capa que contendrá el calendario. Debe ser una referencia al
elemento y no el id, nombre o cualquier otra cosa. Si no se especifica el 
calendario no se mostrará y el comportamiento es indeterminado (además del 
alert).

v_mes [opcional]: El mes en el que se representará el calendario. Si se omiten 
mes y año se tomará la fecha actual. Entre 0-11.
v_anho [opcional]: El año en que se representará el calendario. Si se omiten 
mes y año se tomará la fecha actual. 4 dígitos.

--------------------------------------------------------------------------------
inicializar_fechas(v_mes, v_anho)
--------------------------------------------------------------------------------

>>Una función para uso interno del calendario que establece una serie de valores
en los miembros del calendario. No tiene sentido llamarla de por si.

v_mes: El mes a partir del cual se inicializan los datos. Entre 0-11.
v_anho: El año a partir del cual se inicializan los datos. 4 dígitos.

--------------------------------------------------------------------------------
inicializar_contenedor(v_contenedor)
--------------------------------------------------------------------------------

>>Una función para uso interno del calendario a la cual se llama desde crear.
Se encarga de generar la cabecera, selector, contenedor y almanaque del 
calendario. Incluye, además, una llamada a "rellenar_dias_calendario" con lo 
cual al llamar a esta función se rellenan los días.

v_contenedor: La capa que contiene al calendario. Debe ser una referencia: no
nos sirve un id o nombre.

--------------------------------------------------------------------------------
rellenar_dias_calendario()
--------------------------------------------------------------------------------

>>Genera los elementos visuales de cada uno de los días del mes concreto. Los
días generados (de la clase Dia) se almacenan en un array (que se limpia 
previamente) de días para su referencia individual. Si se ha asignado un método
de callback este es el punto en el cual a cada día se asigna el evento onclick.


********************************************************************************
Día
********************************************************************************

La clase Día... Representa a un día en el calendario. Es la conjunción de su
contenedor visual y su código propio.

--------------------------------------------------------------------------------
Propiedades:
--------------------------------------------------------------------------------

>>padre: Hace referencia al calendario. Desde el calendario podemos hacer 
referencia del día desde el array de dias y desde el día al calendario por medio
de esta propiedad. Esto puede ser útil en la función callback con lo que se
puede hacer parametro_1.padre y referenciar al calendario.

>>fecha: Del tipo Date, es la fecha completa del día que se representa. Es la
propiedad que tiene más sentido en la función Callback. Conviene mirar la 
documentación del tipo Date para ver cómo funciona.

>>contenedor: Hace referencia a la capa que es el "recuadro" de cada uno de los
días. Este recuadro es el que tiene las "clases". Dentro de este recuadro hay
otra capa que contiene el número del día.

>>dia: Dentro del contenedor, es la capa que contiene el número del día.

--------------------------------------------------------------------------------
Dia(v_fecha, v_padre)
--------------------------------------------------------------------------------

>>El "constructor" del día, realmente nunca lo manipularemos ni haremos nada
con él: simplemente lo documentamos y el calendario ya se encarga de todo.

v_fecha: Del tipo Date, es la fecha del día que se está representando.
v_padre: De forma automática hace referencia al calendario que creó los días.

--------------------------------------------------------------------------------
crear()
--------------------------------------------------------------------------------

>>Complemento del constructor, se encarga de inicializar el contenedor, asignar
las clases del mismo en función del día que sea y poco más... Desde aquí NO se
organizan los eventos onclick ni nada por el estilo, de hecho la clase Dia
no debería ser accesible excepto para consultar.

********************************************************************************
Selector
********************************************************************************

La clase Selector, es el selector de año y mes para mover el calendario.

--------------------------------------------------------------------------------
Propiedades:
--------------------------------------------------------------------------------

>>padre: De forma automática hace referencia al calendario al que pertenece.

>>aquello: Simplemente es "this" accesible desde el ámbito de funciones 
internas. No tiene otro sentido.

>>contenedor: La capa que contiene los dos "select".

>>desplegable_mes: El "select" para los meses.

>>desplegable_anho: El "select" para los años.

--------------------------------------------------------------------------------
Selector(v_padre)
--------------------------------------------------------------------------------

>>Hace las veces de constructor aunque nunca lo llamaremos puesto que el 
calendario se encarga automáticamente del llamar a la función con el parámetro
adecuado.

v_padre: Una referencia al elemento que será el padre del calendario.

--------------------------------------------------------------------------------
crear()
--------------------------------------------------------------------------------

>>Complemento del constructor, se encarga de generar el contenedor y los select,
así como de rellenarlos con datos pertinentes.

/******************************************************************************/
