Código de la clase calendar

Código de la clase calendar

Actualización Julio 2012: He incorporado un resaltador de código que mostrará el código actual de un documento HTML, JS, CSS o PHP. Lo que se expone aquí puede haber sido modificado. Vea el actual en calendar.js

/*
    =========================================================================
    Software: calendar.js
    Description: Calendario que actualiza festivos.
    Contact: http://www.wextensible.com
    Copyright (c) 2010, Andrés de la Paz

    Se implementa un CALENDARIO para semanas lunes a domingo, desde 1/1/1601 a 31/12/2299,
    que actualiza los días festivos y sitúa jueves y viernes santo de cada año en su lugar.

    Puede encontrar la documentación completa en
    http://www.wextensible.com/como-se-hace/objetos-javascript-calendario/objetos-javascript-5.html

    ACTUALIZACIONES:
    AGO 2010.- Habían dos propiedades que tenían referencias al DOM y que no eran consistentes
    cuando interaccionaban con otros objetos javascript que modificarán ese DOM. Esas propiedades
    se obtenían de los argumentos del constructor dondeTabla y dondeTitulo, dos referencias
    a dos elementos HTML donde crear la tabla del calendario y donde poner el título, por lo tanto
    había que pasarlos como objetos HTMLElement. AHORA SON ARGUMENTOS STRING con los identificadores
    id de esos elementos. Luego internamente ya se convierten en referencias al DOM con
    document.getElementById(). También hemos incorporado control de errores que antes no había.


    ==========================================================================
*/
//El 01/01/1601 fue lunes
var ANYO_INICIO = 1601
//Limitado por el cálculo de semana santa
var ANYO_FIN = 2299
//domingo 31/12/1600, día previo al 01/01/1601 que fue lunes
var DIA_INICIO = 6
//rectifica posición primer día en cada mes
var RECTIF = Array(0,3,3,6,1,4,6,2,5,0,3,5);
//nombres de meses
var NOMBRES_MES = Array("Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto",
        "Septiembre","Octubre","Noviembre","Diciembre");
//días en cada mes
var DIAS_MES = Array(31,28,31,30,31,30,31,31,30,31,30,31);
//El primer día es el lunes, con índice 0
var NOMBRES_DIAS_SEMANA = Array("Lunes", "Martes", "Miércoles", "Jueves", "Viernes",
        "Sábado", "Domingo");

//Contenido: día; mes; nombre fiesta; 1=estatal 2=autonómica
var FESTIVOS = Array(
Array(1,1,"Año Nuevo",1),
Array(6,1,"Reyes",1),
Array(28,2,"Día de Andalucía",2),
Array(1,3,"Día de las Islas Baleares",2),
Array(23,4,"Día de Castilla y León; Día de Aragón",2),
Array(1,5,"Fiesta del Trabajo",1),
Array(2,5,"Día de la Comunidad de Madrid",2),
Array(30,5,"Día de Canarias",2),
Array(31,5,"Día Castilla-La Mancha",2),
Array(9,6,"Día de la Región de Murcia; Día de la Rioja",2),
Array(25,7,"Santiago Apostol-Día Nacional Galicia",2),
Array(28,7,"Día de las Instituciones (de Cantabria)",2),
Array(15,8,"Asunción de la Virgen",1),
Array(2,9,"Día de la Ciudad Autónoma Ceuta",2),
Array(8,9,"Día de Asturias; Día de Extremadura",2),
Array(11,9,"Fiesta Nacional de Cataluña",2),
Array(9,10,"Día de la Comunidad Valenciana",2),
Array(12,10,"Fiesta Nacional de España",1),
Array(1,11,"Todos los Santos",1),
Array(6,12,"Día de la Constitución",1),
Array(8,12,"La Inmaculada",1),
Array(25,12,"Navidad",1)
)

//constantes para el cálculo del domingo de pascua en semana santa
var GAUSS_PASCUA = Array(
Array(1583,1699,22,2),
Array(1700,1799,23,3),
Array(1800,1899,23,4),
Array(1900,2099,24,5),
Array(2100,2199,24,6),
Array(2200,2299,25,0)
)
/* Esta función no se usa pero sirve para construir los términos del array
 * de desplazamiento de días var RECTIF = Array(0,3,3,6,1,4,6,2,5,0,3,5);
 * En lugar de acudir a esta función es más rápido fijarla como una
 * constante pues son valores que no varían.
 */
function construyeRECTIF(){
    //En este array ponemos los restos del mes anterior
    //que son los desplazamientos en el mes siguiente
    var arrRestos = new Array(12);
    arrRestos[0] = 0;
    for (var i=0; i<12; i++){
        arrRestos[i+1] = DIAS_MES[i] % 7;
    }
    //Luego hacemos una suma acumulada de restos
    var arrSumas = new Array(12);
    arrSumas[0] = 0;
    for (var i=1; i<12; i++) {
        arrSumas[i] = arrRestos[i] + arrSumas[i-1];
    }
    //Finalmente extraemos el resto de cada acumulado
    var arrFinal = new Array(12);
    for (var i=0; i<12; i++) {
        arrFinal[i] = arrSumas[i] % 7;
    }
    return arrFinal;
}
/* Comprueba si un año es bisiesto:
 * Un año es bisiesto si es divisible por 4, excepto el último de
 * cada siglo (aquel divisible por 100), salvo que este último sea
 * divisible por 400.
 */
function esBisiesto(anyo) {
    if ((anyo % 4) == 0) {
        var logA = ((anyo % 100) == 0)
        var logB = ((anyo % 400) != 0)
        if (!(logA && logB)) {
            return true
        } else {
            return false
        }
    } else {
        return false
    }
}
/* ALGORITMO (GAUSS) PARA CALCULAR CUANDO CAE EL DOMINGO DE PASCUA DE UN AÑO
 * En Wikipedia http://es.wikipedia.org/wiki/C%C3%A1lculo_de_la_fecha_de_Pascua
 * aparece una extensa descripción del algoritmo:
 * a = Año mod 19
 * b = Año mod 4
 * c = Año mod 7
 * d = (19a + f) mod 30
 * e = (2b + 4c + 6d + g) mod 7
 * Siendo f, g unas constantes de la tabla siguiente
 * 1583 - 1699 --> f=22 g=2
 * 1700 - 1799 --> f=23 g=3
 * 1800 - 1899 --> f=23 g=4
 * 1900 - 2099 --> f=24 g=5
 * 2100 - 2199 --> f=24 g=6
 * 2200 - 2299 --> f=25 g=0
 * Si d + e < 10 --> PASCUA = (d + e + 22) Marzo sino PASCUA = (d + e - 9) Abril
 * Tener en cuenta las excepciones:
 * Si PASCUA == 26 de Abril --> PASCUA = 19 de Abril.
 * Si PASCUA == 25 de Abril, d==28, e==6, a>10 --> PASCUA = 18 Abril.
 * Aquí he confeccionado una versión particular para mi calendario:
 */
function calculaPascua(anyo){
    var pascua = Array(0,0)
    var a = anyo % 19
    var b = anyo % 4
    var c = anyo % 7
    var f = 0
    var g = 0
    var encontrado = false
    for (var k=0; k<GAUSS_PASCUA.length; k++) {
        if ((anyo >= GAUSS_PASCUA[k][0])&&(anyo <= GAUSS_PASCUA[k][1])){
            f = GAUSS_PASCUA[k][2]
            g = GAUSS_PASCUA[k][3]
            encontrado = true
            break;
        }
    }
    if (encontrado){
        var d = (19 * a + f) % 30
        var e = (2 * b + 4 * c + 6 * d + g) % 7
        if ((d+e) < 10) {
            pascua[0] = d + e + 22
            pascua[1] = 3
        } else {
            pascua[0] = d + e - 9
            pascua[1] = 4
        }
        if (pascua[1] == 4) {
            if (pascua[0] == 26) {
                pascua[0] = 19
            } else if ((pascua[0] == 25)&&(d == 28)&&(e == 6)&&(a > 10)){
                pascua[0] = 18
            }
        }
    }
    return pascua
}
/* Devuelve el díaSemana (0=lunes, ... , 6=domingo) de la fecha dada con
 * dia (1-31),  mes (1-12) y año dado (1601--2299)
 * Este día de la semana es necesario para posicionar el día 1 de cada mes.
 * Partiendo del 31/12/1600 que fue domingo (6º día de la semana), vamos sumando
 * todos los días que hay hasta hoy. Luego hallamos el resto al dividir por 7 y
 * nos da un número 0 a 6 que se corresponde con el día de la semana actual.
 */
function buscaDiaSemana(dia, mes, anyo) {
    //Días del mes más día de la semana de inicio
    var diferDias = dia + DIA_INICIO;
    //Rectificar por desplazamiento de los días en los meses
    diferDias += RECTIF[mes - 1];
    //Rectificar por el desplazamiento de un dia por año
    diferDias += (anyo - ANYO_INICIO);
    //Rectificar por años bisiestos
    diferDias += parseInt((anyo - ANYO_INICIO) / 4);
    //Deducir los años divisibles por 100 que no lo sean por 400 no bisiestos
    var j = parseInt((anyo - ANYO_INICIO) / 100);
    var k = parseInt((anyo - ANYO_INICIO) / 400);
    diferDias -= (j-k);
    //Rectificar si el año actual es bisiesto
    if ((esBisiesto(anyo))&&(mes>2)) diferDias++;
    //Día de la semana a buscar
    diferDias = diferDias % 7;
    return diferDias;
}
/* Fecha actual que se devuelve en un array con 4 posiciones,
 * el primero es el día de la semana, los 3 centrales son dia,
 * mes y año y el último es un string con la fecha en formato
 * "d/m/aaaa"
 */
function fechaActual(){
    try {
        var fecha = new Date();
        var dia = fecha.getDate();
        //0:domingo ... 6: sábado
        var diaSemana = fecha.getDay();
        //1:enero ... 12: diciembre
        var mes = fecha.getMonth() + 1;
        var anyo = fecha.getFullYear();
        var diamesanyo = dia + "/" + mes + "/" + anyo;
        return Array(diaSemana, dia, mes, anyo, diamesanyo);
    } catch(e) {
        return null;
    }
}
/* Comprueba si una fecha es correcta. Debe pasarse como un string
 * "d/m/aaaa" y devuelve un array (dia, mes, año) con enteros
 * 1..31, 1..12 y año
 * Se pueden pasar varios tipos de separadores.
 * Si hay algún error retorna nulo.
 * Necesita las constantes DIAS_MES, ANYO_INICIO, ANYO_FIN
 * El array devuelto tiene 4 posiciones, los 3 primeros para día, mes
 * y año como enteros y el último un string con la fecha "d/m/aaaa".
 * Los arrays son objetos que pueden combinar enteros y string.
 */
function compruebaFecha(fecha){ 
    try {
        var arr = null;
        if (typeof(fecha) == "string"){
            var splites = new Array("/", "-", ".", "_", ",", ";");
            var temp = null;
            for (var i=0; i<splites.length; i++){
                var car = splites[i];
                temp = fecha.split(car);
                if (temp.length == 3) break;
            }
            if (temp != null) {
                var dia = parseInt(temp[0]);
                var mes = parseInt(temp[1]);
                var anyo = parseInt(temp[2]);
                if ((anyo >= ANYO_INICIO)&&(anyo <= ANYO_FIN)){
                    if ((mes >= 1)&&(mes <= 12)){
                        var diasMes = DIAS_MES[mes-1];
                        if (esBisiesto(anyo)) diasMes++;
                        if ((dia >= 1)&&(dia <= diasMes)){
                            arr = new Array(dia, mes, anyo, dia+"/"+mes+"/"+anyo);
                        }
                    }
                }
            }
        }
        return arr;
    } catch(e) {
        return null;
    }
}
/* Constructor del calendario con variable = new calendar(...)
 * Argumentos:
 * - nombreInstancia: un string obligatorio para pasarlo igual que el nombre de la
 *   variable con la que se instancia el calendario.
 * - dondeTabla: un string obligatorio con el identificador "id" del elemento
 *   de bloque donde se albergará la tabla del calendario.
 * - dondeTitulo: un string opcional donde se albergará el título. Si no se pasa o
 *   es una cadena vacía "" se alberga en el <caption> de la tabla.
 * - fecha: un string opcional con la fecha inicial en formato "d/m/aaaa". Si no
 *   se pasa o es "" entonces se pone la fecha actual.
 * - conSeleccionDia: un booleano opcional para poder seleccionar un dia.
 */
function calendar(nombreInstancia, dondeTabla, dondeTitulo, fecha, conSeleccionDia) {
   /* Esta propiedad nos dirá true si recoge todos los argumentos correctos
     */
    this.creado = false;
    /* String obligatorio con el nombre de la variable con la que se creó la instancia
     * del calendario.
     */
    this.nombre = "";
    if ((nombreInstancia != null)&&(nombreInstancia != "")){
        if (typeof(nombreInstancia) == "string") {
            this.nombre = nombreInstancia;
        } else {
            alert("Error al crear un calendario: el nombreInstancia es obligatorio" +
                    "e igual a la variable de la instancia de este calendario.");
            return;
        }
    } else {
        alert("Error al crear un calendario: el nombreInstancia es obligatorio");
        return;
    }
    /* String obligatorio con el atributo id del elemento de bloque donde se va a insertar la tabla
     * del calendario. No se verifica que este id corresponda a un elemento que exista
     * en el DOM. Si no se pasa o se pasa una cadena vacía no se podrá construir el
     * calendario
     */
    this.idDondeTabla = "";
    if ((dondeTabla != null)&&(dondeTabla != "")){
        if (typeof(dondeTabla) == "string"){
            this.idDondeTabla = dondeTabla;
        } else {
            alert("Error, se esperaba el argumento 'dondeTabla' como un string para " +
                    "crear el calendario '" + this.nombre + "'");
            return
        }
    } else {
        alert("Error, se esperaba el argumento 'dondeTabla' para " +
                "crear el calendario '" + this.nombre + "'");
        return;
    }
    /* String opcional con el atributo id del elemento donde se va a insertar el título
     * del calendario. No se verifica que este id corresponda a un elemento que exista
     * en el DOM. Si no se pasa el argumento o se pasa una cadena vacía, el título se
     * pondrá en el <caption> de la tabla
     */
    this.idDondeTitulo = "";
    if ((dondeTitulo != null)&&(dondeTitulo != "")){
        if (typeof(dondeTitulo) == "string"){
            this.idDondeTitulo = dondeTitulo;
        } else {
            alert("Error, se esperaba el argumento 'dondeTitulo' como un string para " +
                    "crear el calendario '" + this.nombre + "'");
            return
        }
    } else {
        //en este caso el título se pondrá en el <caption> de la tabla aunque algunos
        //métodos como situar fecha y mover de mes-año no podrán usarse.
    }
    /* obtenemos la fecha de hoy como un array (dia-semana, dia, mes, año)
     * y la ponemos por defecto en las propiedades this.dia, this.mes y this.anyo
     */
    var esHoy = fechaActual();
    this.dia = esHoy[1];
    this.mes = esHoy[2];
    this.anyo = esHoy[3];
    
    /* El argumento fecha es opcional y se espera como un string "d/m/aaaa".
     * Si no se pasa el argumento o se pasa una cadena vacía se pondrá la fecha
     * de esHoy.
     */
    if ((fecha != null)&&(fecha != "")){
        var arr = compruebaFecha(fecha);
        if (arr != null) {
            this.dia = arr[0];
            this.mes = arr[1];
            this.anyo = arr[2];
        } else {
            alert("Error, el argumento 'fecha' es incorrecto para " +
                    "crear el calendario '" + this.nombre + "'");
            return;
        }

    }
    /* booleano opcional para crear calendario con selección de día
     *
     */
    this.seleccionDia = false;
    if (conSeleccionDia != null){
        this.seleccionDia = conSeleccionDia;
    }

    //Este array tiene índice siguiente: 0=domingo, 1=festivo nacional, 2=festivo autonomico
    this.estiloFestivo = Array("color: maroon", "color: red", "color: blue");
    //Para el estilo puede usarse cualquier cosa. Aquí optamos por colores para domingos
    // y festivos, diferenciando entre fiestas estatales y autonómicas.
    //El estilo del día actual se pinta al final, por lo que es bueno poner otro estilo que
    //no modifique el color de letra.
    this.estiloDiaActual = "background-color: yellow";
    //Estilo para el día seleccionado en la propiedad "dia" 
    this.estiloDiaSeleccionado = "outline: green double 4px";
    //Estilo para el día NO seleccionado     
    this.estiloDiaNoSeleccionado = "outline: none";
    //Si sale por aquí es que el calendario se creó adecuadamente. Luego falta pasarle
    //el método construyeCalendario() para construirlo en HTML de forma dinámica
    this.creado = true;
    
    /* Método para construir el calendario después de aplicarlo a una nueva variable
     * con new calendar()
     */
    this.construyeCalendario = function construyeCalendario(){
        try{
            var ubicaTabla = document.getElementById(this.idDondeTabla);
            if (ubicaTabla == null) {
                alert("No se pudo ubicar la tabla del calendario '" + this.nombre +
                        "' en el id='" + this.idUbicaTabla + "'");
                return;
            }
            var mesCorto = NOMBRES_MES[this.mes - 1].substring(0,3);
            var mesCortoAnyo = mesCorto.toUpperCase() + " " + this.anyo;
            var ubicaTitulo = null;
            if (this.idDondeTitulo != ""){
                ubicaTitulo = document.getElementById(this.idDondeTitulo);
                if (ubicaTitulo != null) {
                    if (ubicaTitulo.tagName.toLowerCase() == "input"){
                        ubicaTitulo.value = mesCortoAnyo;
                        ubicaTitulo.title = "Cambiar mes (1-12) y año (1601-2299), introducir 'm/aaaa'";
                    } else {
                        ubicaTitulo.innerHTML = mesCortoAnyo;
                    }
                } else  {
                    alert("No se pudo ubicar el título del calendario '" + this.nombre +
                            "' en el id='" + this.idUbicaTitulo + "'");
                    return
                }
            } else {
                //pondremos el título en el <caption> de la tabla
            }
            var juevesSanto = 999
            var mesJuevesSanto = 999
            var viernesSanto = 999
            var mesViernesSanto = 999
            if ((this.mes == 3)||(this.mes == 4)) {
                var pasc = calculaPascua(this.anyo)
                if ((pasc[0] > 0)&&(pasc[1] > 0)) {
                    juevesSanto = pasc[0] - 3
                    mesJuevesSanto = pasc[1]
                    if (juevesSanto < 1) {
                        juevesSanto = 31 + juevesSanto
                        mesJuevesSanto = 3
                    }
                    viernesSanto = pasc[0] - 2
                    mesViernesSanto = pasc[1]
                    if (viernesSanto < 1){
                        viernesSanto = 31 + viernesSanto
                        mesViernesSanto = 3
                    }

                }
            }
            //La tabla se crea sin bordes ni estilo.
            var thtml = "<table border='0'>"
            if (this.idDondeTitulo == "") {
                thtml += "<caption>" + mesCortoAnyo + "</caption>"
            }
            if (esBisiesto(this.anyo)) {
                DIAS_MES[1] = 29
            } else {
                DIAS_MES[1] = 28
            }
            //Cogemos estas primeras letras de los nombres de la semana
            var numLetras = 2;
            thtml += "<tr><th>" + NOMBRES_DIAS_SEMANA[0].substring(0, numLetras) + "</th>" +
            "<th>" + NOMBRES_DIAS_SEMANA[1].substring(0, numLetras) + "</th>" +
            "<th>" + NOMBRES_DIAS_SEMANA[2].substring(0, numLetras) + "</th>" +
            "<th>" + NOMBRES_DIAS_SEMANA[3].substring(0, numLetras) + "</th>" +
            "<th>" + NOMBRES_DIAS_SEMANA[4].substring(0, numLetras) + "</th>" +
            "<th>" + NOMBRES_DIAS_SEMANA[5].substring(0, numLetras) + "</th>" +
            "<th style=\"" + this.estiloFestivo[0] + "\">" +
            NOMBRES_DIAS_SEMANA[6].substring(0, numLetras) + "</th></tr>"
            var j = buscaDiaSemana(1, this.mes, this.anyo)
            var k = 1
            var n = 1
            var blancos = true
            var diasMes = DIAS_MES[this.mes - 1]
            while(k <= diasMes) {
                thtml += "<tr>"
                for (i=0; i<7; i++) {
                    thtml += "<td "
                    if (((j != i)&&(k<=6)&&(blancos))||(k > diasMes)) {
                        //espacios en blanco
                        thtml += ">"
                    } else { //días
                        blancos = false
                        var esFestivo = false
                        var fiesta = ""
                        var region = 0
                        for (var f=0; f<FESTIVOS.length; f++) {
                            if ((FESTIVOS[f][0] == k)&&(FESTIVOS[f][1] == this.mes)){
                                esFestivo = true
                                fiesta = FESTIVOS[f][2]
                                region = FESTIVOS[f][3]
                                break
                            }
                        }
                        if ((this.mes == 3)||(this.mes == 4)) {
                            if ((this.mes == mesJuevesSanto)&&(k == juevesSanto)) {
                                esFestivo = true
                                region = 1
                                fiesta = "Jueves Santo"
                            }
                            if ((this.mes == mesViernesSanto)&&(k == viernesSanto)) {
                                esFestivo = true
                                region = 1
                                fiesta = "Viernes Santo"
                            }
                        }
                        var estilo = ""
                        if (i==6) estilo = this.estiloFestivo[0]
                        if (esFestivo) {
                            //sobreescribe el color de los domingos
                            estilo = this.estiloFestivo[region]
                            thtml += " title='" + fiesta + "'"
                        }
                        var hoy = fechaActual()
                        if ((k == hoy[1])&&(this.mes == hoy[2])&&(this.anyo == hoy[3])) {
                            //agrega este estilo al anterior
                            if (estilo != "") estilo += "; "
                            estilo += this.estiloDiaActual
                        }
                        if (estilo != "") thtml += " style='" + estilo;
                        if (this.seleccionDia){
                            if (this.dia == k) {
                                if (estilo != "") {
                                    thtml += "; ";
                                } else {
                                    thtml += " style='"
                                }
                                thtml += this.estiloDiaSeleccionado + "' "
                            } else if (estilo != "") {
                                thtml += "' "
                            }
                            thtml += " onclick = \"" + this.nombre + ".seleccionarDia(this);\" ";
                        } else if (estilo != "") {
                            thtml += "' "
                        }
                        thtml += ">" + k
                        k++
                    }
                    thtml += "</td>"
                }
                thtml += "</tr>"
            }
            thtml += "</table>";
            ubicaTabla.innerHTML = thtml;
        } catch (e) {
            alert("Error en calendario '" + this.nombre + "' con this.construyeCalendario(): " + e.message)
        }
    }
    /* Después de crear un calendario con new calendar() podemos cambiar el estilo de los
     * días festivos, actual y seleccionado mediante esta función.
     * En 'estilo' pasamos la cadena de estilo.
     * En 'queDia' pasamos "domingo", "fiesta-estatal", "fiesta-autonomica", "dia-actual",
     * "dia-seleccionado" o "dia-no-seleccionado"
     */
    this.cambiaEstiloDias = function cambiaEstiloDias(estilo, queDia) {
        try {
            if (this.creado){
                switch (queDia){
                    case "domingo": {
                        this.estiloFestivo[0] = estilo;
                        break;
                    }
                    case "fiesta-estatal": {
                        this.estiloFestivo[1] = estilo;
                        break;
                    }
                    case "fiesta-autonomica": {
                        this.estiloFestivo[2] = estilo;
                        break;
                    }
                    case "dia-actual": {
                        this.estiloDiaActual = estilo;
                        break;
                    }
                    case "dia-seleccionado": {
                        this.estiloDiaSeleccionado = estilo;
                        break;
                    }
                    case "dia-no-seleccionado":{
                        this.estiloDiaNoSeleccionado = estilo;
                        break;
                    }
                }
            }
        } catch(e) {
            alert("Error en calendario '" + this.nombre + "' con this.cambiaEstiloDias(): " + e.message);
        }
    }
    /* Sitúa el calendario en la fecha de hoy
     *
     */
    this.situaHoy = function situaHoy(){
        try {
            if (this.creado){
                var hoy = fechaActual();
                this.dia = hoy[1];
                this.mes = hoy[2];
                this.anyo = hoy[3];
                this.construyeCalendario()
            }
        } catch(e) {
            alert("Error en calendario '" + this.nombre + "' con this.situaHoy(): " + e.message);
        }
    }
    /* Sitúa el calendario en una fecha que debe pasarse como una
     * cadena "d/m/aaaa"
     */
    this.situaFecha = function situaFecha(fechaString){
        try {
            if (this.creado){
                var arrFecha = compruebaFecha(fechaString);
                if (arrFecha != null){
                    this.dia = arrFecha[0];
                    this.mes = arrFecha[1];
                    this.anyo = arrFecha[2];
                    this.construyeCalendario();
                    return fechaString;
                }
            }
            return "Error";
        } catch(e) {
            alert("Error en calendario '" + this.nombre + "' con this.situaFecha(): " + e.message);
        }
    }
    /* Permite mover un mes antes o después. Se puede habilitar con eventos
     * onclick por ejemplo poniéndolo en elementos de la página
     */
    this.mueveCalendar = function mueveCalendar(masMenos) {
        try {
            if (this.creado){
                mes = this.mes
                anyo = this.anyo
                if (masMenos == 1) {
                    mes++
                    if (mes > 12) {
                        mes = 1
                        anyo++
                        if (anyo > ANYO_FIN) return
                    }
                } else {
                    mes--
                    if (mes < 1) {
                        mes = 12
                        anyo--
                        if (anyo < ANYO_INICIO) return
                    }
                }
                this.mes = mes
                this.anyo = anyo
                //comprueba el día, si el anterior es por ejemplo 31 y
                //ahora estamos en un mes de 30, debe tenerlo en cuenta
                var diasMes = DIAS_MES[mes-1];
                if ((esBisiesto(anyo))&&(mes==2)) diasMes++;
                if(this.dia > diasMes) this.dia = diasMes;                
                this.construyeCalendario()
            }
        } catch(e) {
            alert("Error en calendario '" + this.nombre + "' con this.mueveCalendar(): " + e.message);
        }
    }
    /* Cambia por ejemplo "JUN 2010" por "6/2010" y que
     * el usuario pueda cambiar de mes y año en el cuadro input
     */
    this.entraFecha = function entraFecha(inpute){
        try {
            var ubicaTabla = document.getElementById(this.idDondeTabla);
            var ubicaTitulo = document.getElementById(this.idDondeTitulo);
            if ((this.creado)&&(ubicaTabla != null)&&(ubicaTitulo != null)&&
                    (ubicaTitulo.tagName.toLowerCase() == "input")){
                var dato = inpute.value
                var arr = dato.split(" ")
                var texto = this.mes + "/" + this.anyo
                if (arr.length == 2){
                    var mes = 0
                    for (i=0; i<NOMBRES_MES.length; i++){
                        var mesCorto = NOMBRES_MES[i].substring(0,3)
                        mesCorto = mesCorto.toUpperCase()
                        if (mesCorto == arr[0]) {
                            mes = i+1
                            break
                        }
                    }
                    if (mes>0){
                        texto = mes + "/" + arr[1]
                    }
                }
                inpute.value = texto
            }
        } catch(e) {
            alert("Error en calendario '" + this.nombre + "' con this.entraFecha(): " + e.message);
        }
    }
    /* Mueve a un mes/año determinado mediante el uso de un input type texto
     *
     */
    this.mueveMesAnyo = function mueveMesAnyo(inpute) {
        try {
            var ubicaTabla = document.getElementById(this.idDondeTabla);
            var ubicaTitulo = document.getElementById(this.idDondeTitulo);
            if ((this.creado)&&(ubicaTabla != null)&&(ubicaTitulo != null)&&
                    (ubicaTitulo.tagName.toLowerCase() == "input")){
                var dato = inpute.value
                if ((dato != "")&&(dato != null)) {
                    if ((dato.length == 6)||(dato.length == 7)) {
                        if (dato.indexOf("/") > -1) {
                            var arr = dato.split("/")
                            if (!isNaN(arr[0])){
                                var unMes = parseInt(arr[0])
                                if ((unMes > 0)&&(unMes < 13)) {
                                    if (!isNaN(arr[1])) {
                                        var unAnyo = parseInt(arr[1])
                                        if ((unAnyo > 1600)&&(unAnyo < 2300)) {
                                            this.mes = unMes
                                            this.anyo = unAnyo
                                            this.anyo = unAnyo;
                                            //comprueba el día, si el anterior es por ejemplo 31 y
                                            //ahora estamos en un mes de 30, debe tenerlo en cuenta
                                            var diasMes = DIAS_MES[unMes-1];
                                            if ((esBisiesto(unAnyo))&&(unMes==2)) diasMes++;
                                            if(this.dia > diasMes) this.dia = diasMes;                                              
                                            this.construyeCalendario()
                                            return
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                //si sale por aquí es que el valor del input no es correcto y
                //vuelve a poner lo que había
                var mesCorto = NOMBRES_MES[this.mes - 1].substring(0,3)
                var thtml = mesCorto.toUpperCase() + " " + this.anyo
                inpute.value = thtml
            }
        } catch(e) {
            alert("Error en calendario '" + this.nombre + "' con this.mueveMesAnyo(): " + e.message);
        }
    }
    /* Devuelve la fecha almacenada en las propiedades dia, mes
     * y anyo en varios formatos.
     */
    this.devuelveFecha = function devuelveFecha(formato){
        try {
            switch (formato.toLowerCase()){
                case ("fecha-larga"): return this.dia + "/" + this.mes + "/" + this.anyo;
                case ("fecha-corta"): return this.dia + "/" + this.mes + "/" +
                    this.anyo.toString().substring(2,4);
                case ("fecha-dia-semana"): {
                    var dsem = buscaDiaSemana(this.dia, this.mes, this.anyo);
                    return NOMBRES_DIAS_SEMANA[dsem] + ", " + this.dia + " de " +
                    NOMBRES_MES[this.mes - 1] + " de " + this.anyo;
                }
                case "array": return new Array(this.dia, this.mes, this.anyo);
                case "date": return new Date(this.anyo, this.mes - 1, this.dia);
            }
        } catch(e) {
            alert("Error en calendario '" + this.nombre + "' con this.devuelveFecha(): " + e.message);
            return null;
        }
    }
    /* Un mensaje de información con alert() de javascript.
     *
     */
    this.mensaje = function mensaje(){
        var men = "Se indican las fiestas estatales y autonómicas de uso más frecuente. "
        men += "Pueden existir otras fiestas de carácter autonómico que no se reflejan. "
        men += "Algunas fiestas estatales pueden ser sustituidas por locales en otras fechas. "
        men += "Si caen en domingo pueden ser trasladadas a lunes. Consulte la legislación estatal y "
        men += "autonómica al respecto para cada año."
        alert(men)
    }
    /* Extraer las propiedades del calendario
     *
     */
    this.verComoEs = function verComoEs(){
        try {
            var cad = "El calendario no se ha creado.";
            if (this.creado){
                cad = "<p>PROPIEDADES DEL CALENDARIO <br/>";
                for (unaPropiedad in this){
                    if (typeof(this[unaPropiedad]) != "function"){
                        if (this[unaPropiedad] == null){
                            cad += unaPropiedad + ": NULO<br />";
                        } else {
                            cad += unaPropiedad + ": " + this[unaPropiedad].toString() + "<br />";
                        }
                    }
                }
                cad += "</p>"
            }
            return cad;
        } catch(e) {
            alert("Error en calendario '" + this.nombre + "' con verComoEs(): " + e.message);
            return "";
        }
    }
    /* Este debería ser un método privado de la clase pues sólo tiene un uso interno, aunque
     * podría ser llamado desde el exterior pero no es el caso.
     */
    this.borrarSeleccionDia = function borrarSeleccionDia(){
         try {
            if (this.creado){
                var ubicaTabla = document.getElementById(this.idDondeTabla);
                if (ubicaTabla != null) {
                    var coleccion = ubicaTabla.getElementsByTagName("table");
                    if (coleccion.length > 0) {
                        var tabla = coleccion[0];
                        var celdas = tabla.getElementsByTagName("td");
                        for (var i=0; i<celdas.length; i++){
                            //Firefox y Safari no soporta innerText que sería lo adecuado
                            var dia = celdas[i].innerHTML;
                            if (dia != "") {
                                celdas[i].style.cssText += "; " + this.estiloDiaNoSeleccionado;

                            }
                        }
                    }
                }
            }
        } catch (e) {
            alert("Error en calendario '" + this.nombre + "' con this.borrarSeleccionDia(): " + e.message);
        }
    }
    /* Este debería ser un método privado de la clase pues sólo tiene un uso interno, aunque
     * podría ser llamado desde el exterior pero no es el caso.
     */
    this.seleccionarDia = function seleccionarDia(celda){
        try {
            if (this.creado){
                this.borrarSeleccionDia();
                //Firefox y Safari no soporta celda.innerText que sería lo adecuado
                this.dia = parseInt(celda.innerHTML);
                celda.style.cssText += "; " + this.estiloDiaSeleccionado;
            }
        } catch (e) {
            alert("Error en calendario '" + this.nombre + "' con this.seleccionarDia(): " + e.message);
        }
    }


}