/**
 * DatePicker widget using Prototype and Scriptaculous.
 * (c) 2007 Mathieu Jondet <mathieu@eulerian.com>
 * Eulerian Technologies
 *
 * DatePicker is freely distributable under the same terms as Prototype.
 *
 */

/**
 * DatePickerFormatter class for matching and stringifying dates.
 *
 * By Arturas Slajus <x11@arturaz.net>.
 */
var DatePickerFormatter = Class.create();
DatePickerFormatter.prototype = {
    /**
     * Create a DatePickerFormatter.
     *
     * format: specify a format by passing 3 value array consisting of
     *   "yyyy", "mm", "dd". Default: ["yyyy", "mm", "dd"].
     *
     * separator: string for splitting the values. Default: "-".
     *
     * Use it like this:
     *   var df = new DatePickerFormatter(["dd", "mm", "yyyy"], "/");
     *   df.current_date();
     *   df.match("7/7/2007");
     */
    initialize: function(format, separator) {
        if (Object.isUndefined(format))
	 format = ["yyyy", "mm", "dd"];
        if (Object.isUndefined(separator))
	 separator = "-";

        this._format 	= format;
        this.separator	= separator;
                
        this._format_year_index	= format.indexOf("yyyy");
        this._format_month_index= format.indexOf("mm");
        this._format_day_index	= format.indexOf("dd");
                
        this._year_regexp	= /^\d{4}$/;
        this._month_regexp 	= /^0\d|1[012]|\d$/;
        this._day_regexp 	= /^0\d|[12]\d|3[01]|\d$/;
    },
    
    /**
     * Match a string against date format.
     * Returns: [year, month, day]
     */
    match: function(str) {
        var d = str.split(this.separator);
        
        if (d.length < 3)
	 return false;
        
        var year = d[this._format_year_index].match(this._year_regexp);
        if (year) { year = year[0] } else { return false }
        var month = d[this._format_month_index].match(this._month_regexp);
        if (month) { month = month[0] } else { return false }
        var day = d[this._format_day_index].match(this._day_regexp);
        if (day) { day = day[0] } else { return false }
        
        return [year, month, day];
    },
    
    /**
     * Return current date according to format.
     */
    current_date: function() {
        var d = new Date;
        return this.date_to_string(
            d.getFullYear(),
            d.getMonth() + 1,
            d.getDate()
       );
    },
    
    /**
     * Return a stringified date accordint to format.
     */
    date_to_string: function(year, month, day, separator) {
        if (Object.isUndefined(separator))
	 separator = this.separator;

        var a = [0, 0, 0];
        a[this._format_year_index]	= year;
        a[this._format_month_index] 	= month.toPaddedString(2);
        a[this._format_day_index] 	= day.toPaddedString(2);
        
        return a.join(separator);
    }
}; 


/**
 * DatePicker
 */

var DatePicker	= Class.create();

DatePicker.prototype = {
	Version: '0.9.4',
	_relative: null,
	_relativePositionTo: null,
	_div: null,
	_zindex: 1,
	_keepFieldEmpty: false,
	_daysInMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
	_dateFormat: [["dd", "mm", "yyyy"], "/"],
	/* language */
	_language: 'fr',
	_language_month: $H({
		'fr': ['Janvier', 'F&#233;vrier', 'Mars', 'Avril', 'Mai', 'Juin',
   'Juillet', 'Aout', 'Septembre', 'Octobre', 'Novembre', 'D&#233;cembre'],
		'en': ['January', 'February', 'March', 'April', 'May',
   'June', 'July', 'August', 'September', 'October', 'November', 'December'],
		'sp': ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
   'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
		'it': ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno',
   'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'],
		'de': ['Januar', 'Februar', 'M&#228;rz', 'April', 'Mai', 'Juni',
   'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
		'pt': ['Janeiro', 'Fevereiro', 'Mar&#231;o', 'Abril', 'Maio', 'Junho',
   'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
		'hu': ['Janu&#225;r', 'Febru&#225;r', 'M&#225;rcius', '&#193;prilis',
   'M&#225;jus', 'J&#250;nius', 'J&#250;lius', 'Augusztus', 'Szeptember',
   'Okt&#243;ber', 'November', 'December'],
		'lt': ['Sausis', 'Vasaris', 'Kovas', 'Balandis', 'Gegu&#382;&#279;',
   'Bir&#382;elis', 'Liepa', 'Rugj&#363;tis', 'Rus&#279;jis', 'Spalis',
   'Lapkritis', 'Gruodis'],
		'nl': ['januari', 'februari', 'maart', 'april', 'mei', 'juni',
   'juli', 'augustus', 'september', 'oktober', 'november', 'december'],
		'dk': ['Januar', 'Februar', 'Marts', 'April', 'Maj',
   'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'December'],
		'no': ['Januar', 'Februar', 'Mars', 'April', 'Mai', 'Juni',
   'Juli', 'August', 'September', 'Oktober', 'November', 'Desember'],
		'lv': ['Janv&#257;ris', 'Febru&#257;ris', 'Marts', 'Apr&#299;lis', 'Maijs',
   'J&#363;nijs', 'J&#363;lijs', 'Augusts', 'Septembris', 'Oktobris',
   'Novembris', 'Decemberis'],
		'ja': ['1&#26376;', '2&#26376;', '3&#26376;', '4&#26376;', '5&#26376;',
   '6&#26376;', '7&#26376;', '8&#26376;', '9&#26376;', '10&#26376;',
   '11&#26376;', '12&#26376;'],
		'fi': ['Tammikuu', 'Helmikuu', 'Maaliskuu', 'Huhtikuu', 'Toukokuu',
   'Kes&#228;kuu', 'Hein&#228;kuu', 'Elokuu', 'Syyskuu', 'Lokakuu',
   'Marraskuu', 'Joulukuu'],
		'ro': ['Ianuarie', 'Februarie', 'Martie', 'Aprilie', 'Mai', 'Junie',
   'Julie', 'August', 'Septembrie', 'Octombrie', 'Noiembrie', 'Decembrie'],
		'zh': ['1&#32;&#26376;', '2&#32;&#26376;', '3&#32;&#26376;',
   '4&#32;&#26376;', '5&#32;&#26376;', '6&#32;&#26376;', '7&#32;&#26376;',
   '8&#32;&#26376;', '9&#32;&#26376;', '10&#26376;', '11&#26376;', '12&#26376;'],
		'sv': ['Januari', 'Februari', 'Mars', 'April', 'Maj', 'Juni',
   'Juli', 'Augusti', 'September', 'Oktober', 'November', 'December']
	}),
	_language_day: $H({
		'fr': ['Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam', 'Dim'],
		'en': ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
		'sp': ['Lun', 'Mar', 'Mie', 'Jue', 'Vie', 'S&#224;b', 'Dom'],
		'it': ['Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab', 'Dom'],
		'de': ['Mon', 'Die', 'Mit', 'Don', 'Fre', 'Sam', 'Son'],
		'pt': ['Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'S&#225;b', 'Dom'],
		'hu': ['H&#233;', 'Ke', 'Sze', 'Cs&#252;', 'P&#233;', 'Szo', 'Vas'],
		'lt': ['Pir', 'Ant', 'Tre', 'Ket', 'Pen', '&Scaron;e&scaron;', 'Sek'],
		'nl': ['ma', 'di', 'wo', 'do', 'vr', 'za', 'zo'],
		'dk': ['Man', 'Tir', 'Ons', 'Tor', 'Fre', 'L&#248;r', 'S&#248;n'],
		'no': ['Man', 'Tir', 'Ons', 'Tor', 'Fre', 'L&#248;r', 'Sun'],
		'lv': ['P', 'O', 'T', 'C', 'Pk', 'S', 'Sv'],
		'ja': ['&#26376;', '&#28779;', '&#27700;', '&#26408;', '&#37329;',
   '&#22303;', '&#26085;'],
		'fi': ['Ma', 'Ti', 'Ke', 'To', 'Pe', 'La', 'Su'],
		'ro': ['Lun', 'Mar', 'Mie', 'Joi', 'Vin', 'Sam', 'Dum'],
		'zh': ['&#21608;&#19968;', '&#21608;&#20108;', '&#21608;&#19977;',
   '&#21608;&#22235;', '&#21608;&#20116;', '&#21608;&#20845;',
   '&#21608;&#26085;'],
		'sv': ['M&#229;n', 'Tis', 'Ons', 'Tor', 'Fre', 'L&#246;r',
   'S&#246;n']
	}),
	_language_close: $H({
		'fr': 'fermer',
		'en': 'close',
		'sp': 'cierre',
		'it': 'fine',
		'de': 'schliessen',
		'pt': 'fim',
		'hu': 'bez&#225;r',
		'lt': 'udaryti',
		'nl': 'sluiten',
		'dk': 'luk',
		'no': 'lukk',
		'lv': 'aizv&#275;rt',
		'ja': '&#38281;&#12376;&#12427;',
		'fi': 'sulje',
		'ro': 'inchide',
		'zh': '&#20851;&#32;&#38381',
		'sv': 'st&#228;ng'
	}),
	/* date manipulation */
	_todayDate: new Date(),
	_current_date: null,
	_clickCallback: Prototype.emptyFunction,
	_cellCallback: Prototype.emptyFunction,
	_id_datepicker: null,
	_disablePastDate: false,
	_disableFutureDate: true,
	_enableYearBrowse: false,
	_oneDayInMs: 24 * 3600 * 1000,
	/* positionning */
	_topOffset: 20,
	_leftOffset: 0,
	_isPositionned: false,
	_relativePosition: true,
	_setPositionTop: 0,
	_setPositionLeft: 0,
	_bodyAppend: false,
	_contentAppend: '',
	_showEvent: 'click',
	/* Effects Adjustment */
	_showEffect: "appear",
	_showDuration: 1,
	_enableShowEffect: true,
	_closeEffect: "fade",
	_closeEffectDuration: 0.3,
	_enableCloseEffect: true,
	_closeTimer: null,
	_enableCloseOnBlur: false,
	/* afterClose : called when the close function is executed */
	_afterClose: Prototype.emptyFunction,
	/* return the name of current month in appropriate language */
	getMonthLocale: function(month)
	{
		return this._language_month.get(this._language)[month];
	},
	getLocaleClose: function()
	{
		return this._language_close.get(this._language);
	},
	_initCurrentDate: function()
	{
		/* Create the DateFormatter */
		this._df = new DatePickerFormatter(this._dateFormat[0], this._dateFormat[1]);
		/* check if value in field is proper, if not set to today */
		this._current_date = $F(this._relative);
		if (!this._df.match(this._current_date))
		{
			this._current_date = this._df.current_date();
			/* set the field value ? */
			if (!this._keepFieldEmpty)
				$(this._relative).value = this._current_date;
		}
		var a_date = this._df.match(this._current_date);
		this._current_year = Number(a_date[0]);
		this._current_mon = Number(a_date[1]) - 1;
		this._current_day = Number(a_date[2]);
	},
	/* init */
	initialize: function(h_p)
	{
		/* arguments */
		this._relative = h_p["relative"];
		this._relativePositionTo = h_p["relativePositionTo"] == null ? this._relative : h_p["relativePositionTo"];

		if (h_p["language"])
			this._language = h_p["language"];
		this._zindex = (h_p["zindex"]) ? parseInt(Number(h_p["zindex"])) : 1;
		if (!Object.isUndefined(h_p["keepFieldEmpty"]))
			this._keepFieldEmpty = h_p["keepFieldEmpty"];
		if (Object.isFunction(h_p["clickCallback"]))
			this._clickCallback = h_p["clickCallback"];
		if (!Object.isUndefined(h_p["leftOffset"]))
			this._leftOffset = parseInt(h_p["leftOffset"]);
		if (!Object.isUndefined(h_p["topOffset"]))
			this._topOffset = parseInt(h_p["topOffset"]);
		if (!Object.isUndefined(h_p["relativePosition"]))
			this._relativePosition = h_p["relativePosition"];
		if (!Object.isUndefined(h_p["showEvent"]))
			this._showEvent = h_p["showEvent"];
		if (!Object.isUndefined(h_p["showEffect"]))
			this._showEffect = h_p["showEffect"];
		if (!Object.isUndefined(h_p["contentAppend"]))
			this._contentAppend = h_p["contentAppend"];
		if (!Object.isUndefined(h_p["enableShowEffect"]))
			this._enableShowEffect = h_p["enableShowEffect"];
		if (!Object.isUndefined(h_p["showDuration"]))
			this._showDuration = h_p["showDuration"];
		if (!Object.isUndefined(h_p["closeEffect"]))
			this._closeEffect = h_p["closeEffect"];
		if (!Object.isUndefined(h_p["enableCloseEffect"]))
			this._enableCloseEffect = h_p["enableCloseEffect"];
		if (!Object.isUndefined(h_p["closeEffectDuration"]))
			this._closeEffectDuration = h_p["closeEffectDuration"];
		if (Object.isFunction(h_p["afterClose"]))
			this._afterClose = h_p["afterClose"];
		if (!Object.isUndefined(h_p["externalControl"]))
			this._externalControl = h_p["externalControl"];
		if (!Object.isUndefined(h_p["dateFormat"]))
			this._dateFormat = h_p["dateFormat"];
		if (Object.isFunction(h_p["cellCallback"]))
			this._cellCallback = h_p["cellCallback"];
		this._setPositionTop = (h_p["setPositionTop"]) ?
   parseInt(Number(h_p["setPositionTop"])) : 0;
		this._setPositionLeft = (h_p["setPositionLeft"]) ?
   parseInt(Number(h_p["setPositionLeft"])) : 0;
		if (!Object.isUndefined(h_p["enableCloseOnBlur"]) && h_p["enableCloseOnBlur"])
			this._enableCloseOnBlur = true;
		if (!Object.isUndefined(h_p["disablePastDate"]) && h_p["disablePastDate"])
			this._disablePastDate = true;
		if (!Object.isUndefined(h_p["disableFutureDate"]) &&
   !h_p["disableFutureDate"])
			this._disableFutureDate = false;
		if (!Object.isUndefined(h_p["enableYearBrowse"]))
			this._enableYearBrowse = true;
		this._id_datepicker = 'datepicker-' + this._relative;
		this._id_datepicker_prev = this._id_datepicker + '-prev';
		this._id_datepicker_next = this._id_datepicker + '-next';
		this._id_datepicker_prev_year = this._id_datepicker_prev + '-year';
		this._id_datepicker_next_year = this._id_datepicker_next + '-year';
		this._id_datepicker_hdr = this._id_datepicker + '-header';
		this._id_datepicker_ftr = this._id_datepicker + '-footer';

		/* build up calendar skel */
		this._div = new Element('div', {
			id: this._id_datepicker,
			className: 'datepicker',
			style: 'display: none; z-index:' + this._zindex
		});
		this._div.className = 'datepicker';
		this._div.innerHTML = '<table><thead><tr>' + ((this._enableYearBrowse) ? '<th width="10px" id="' + this._id_datepicker_prev_year + '" style="cursor: pointer;">&nbsp;&lt;&nbsp;</th>' : '') + '<th width="10px" id="' + this._id_datepicker_prev + '" style="cursor: pointer;">&nbsp;&lt;&lt;&nbsp;</th><th id="' + this._id_datepicker_hdr + '" colspan="' + ((this._enableYearBrowse) ? 3 : 5) + '"></th><th width="10px" id="' + this._id_datepicker_next + '" style="cursor: pointer;">&nbsp;&gt;&gt;&nbsp;</th>' + ((this._enableYearBrowse) ? '<th width="10px" id="' + this._id_datepicker_next_year + '" style="cursor: pointer;">&nbsp;&gt;&nbsp;</th>' : '') + '</tr></thead><tbody id="' + this._id_datepicker + '-tbody"></tbody><tfoot><td colspan="7" id="' + this._id_datepicker_ftr + '"></td></tfoot></table>';
		/* finally declare the event listener on input field */
		Event.observe(this._relative,
    this._showEvent, this.click.bindAsEventListener(this), false);
		/* need to append on body when doc is loaded for IE */
		document.observe('dom:loaded', this.load.bindAsEventListener(this), false);
		/* automatically close when blur event is triggered */
		if (this._enableCloseOnBlur)
		{
			Event.observe(this._relative, 'blur', function(e)
			{
				this._closeTimer = this.close.bind(this).delay(2);
			} .bindAsEventListener(this));
			Event.observe(this._div, 'click', function(e)
			{
				if (this._closeTimer)
				{
					window.clearTimeout(this._closeTimer);
					this._closeTimer = null;
				}
			});
		}
	},
	/**
	* load	: called when document is fully-loaded to append datepicker
	*		  to main object.
	*/
	load: function()
	{
		/* if externalControl defined set the observer on it */
		if (this._externalControl)
			Event.observe(this._externalControl, 'click',
    this.click.bindAsEventListener(this), false);
		/* append to page */
		if (this._relativeAppend)
		{
			/* append to parent node */
			if ($(this._relative).parentNode)
			{
				this._div.innerHTML = this._wrap_in_iframe(this._div.innerHTML);
				$(this._relative).parentNode.appendChild(this._div);
			}
		} else
		{
			/* append to body tag or to provided contentAppend id */
			var body = (this._contentAppend) ?
    $(this._contentAppend) : document.getElementsByTagName("body").item(0);
			if (body)
			{
				this._div.innerHTML = this._wrap_in_iframe(this._div.innerHTML);
				body.appendChild(this._div);
			}
			if (this._relativePosition)
			{
				var a_pos = Element.cumulativeOffset($(this._relativePositionTo));
				this.setPosition(a_pos[1], a_pos[0]);
			}
			else
			{
				if (this._setPositionTop || this._setPositionLeft)
					this.setPosition(this._setPositionTop, this._setPositionLeft);
			}
		}
		/* init the date in field if needed */
		this._initCurrentDate();
		/* set the close locale content */
		$(this._id_datepicker_ftr).innerHTML = this.getLocaleClose();
		/* declare the observers for UI control */
		Event.observe($(this._id_datepicker_prev),
    'click', this.prevMonth.bindAsEventListener(this), false);
		Event.observe($(this._id_datepicker_next),
    'click', this.nextMonth.bindAsEventListener(this), false);
		if (this._enableYearBrowse)
		{
			Event.observe($(this._id_datepicker_prev_year),
     'click', this.prevYear.bindAsEventListener(this), false);
			Event.observe($(this._id_datepicker_next_year),
     'click', this.nextYear.bindAsEventListener(this), false);
		}
		Event.observe($(this._id_datepicker_ftr),
    'click', this.close.bindAsEventListener(this), false);
	},
	/* hack for buggy form elements layering in IE */
	_wrap_in_iframe: function(content)
	{
		return (Prototype.Browser.IE) ?
   "<div style='height:167px;width:185px;background-color:white;align:left'><iframe width='100%' height='100%' marginwidth='0' marginheight='0' frameborder='0' src='about:blank' style='filter:alpha(Opacity=50);'></iframe><div style='position:absolute;background-color:white;top:2px;left:2px;width:180px'>" + content + "</div></div>" : content;
	},
	/**
	* visible	: return the visibility status of the datepicker.
	*/
	visible: function()
	{
		return ($(this._id_datepicker)) ?
   $(this._id_datepicker).visible() : false;
	},
	/**
	* click	: called when input element is clicked
	*/
	click: function()
	{
		/* init the datepicker if it doesn't exists */
		if ($(this._id_datepicker) == null) this.load();
		//if (!this._isPositionned && this._relativePosition)
		if (this._relativePosition)
		{
			/* position the datepicker relatively to element */
			var a_lt = Element.cumulativeOffset($(this._relativePositionTo));
			$(this._id_datepicker).setStyle({ left: Number(a_lt[0] + this._leftOffset) + 'px', top: Number(a_lt[1] + this._topOffset) + 'px' });
			this._isPositionned = true;
		}
		if (!this.visible())
		{
			this._initCurrentDate();
			this._redrawCalendar();
		}
		/* eval the clickCallback function */
		eval(this._clickCallback());
		/* Effect toggle to fade-in / fade-out the datepicker */
		if (this._enableShowEffect)
		{
			new Effect.toggle(this._id_datepicker,
     this._showEffect, { duration: this._showDuration });
		} else
		{
			$(this._id_datepicker).show();
		}
		/* clean timer */
		if (this._closeTimer)
		{
			window.clearTimeout(this._closeTimer);
			this._closeTimer = null;
		}
	},
	/**
	* close	: called when the datepicker is closed
	*/
	close: function()
	{
		if (this._enableCloseEffect)
		{
			switch (this._closeEffect)
			{
				case 'puff':
					new Effect.Puff(this._id_datepicker, {
						duration: this._closeEffectDuration
					});
					break;
				case 'blindUp':
					new Effect.BlindUp(this._id_datepicker, {
						duration: this._closeEffectDuration
					});
					break;
				case 'dropOut':
					new Effect.DropOut(this._id_datepicker, {
						duration: this._closeEffectDuration
					});
					break;
				case 'switchOff':
					new Effect.SwitchOff(this._id_datepicker, {
						duration: this._closeEffectDuration
					});
					break;
				case 'squish':
					new Effect.Squish(this._id_datepicker, {
						duration: this._closeEffectDuration
					});
					break;
				case 'fold':
					new Effect.Fold(this._id_datepicker, {
						duration: this._closeEffectDuration
					});
					break;
				case 'shrink':
					new Effect.Shrink(this._id_datepicker, {
						duration: this._closeEffectDuration
					});
					break;
				default:
					new Effect.Fade(this._id_datepicker, {
						duration: this._closeEffectDuration
					});
					break;
			};
		} else
		{
			$(this._id_datepicker).hide();
		}
		eval(this._afterClose());
	},
	/**
	* setDateFormat
	*/
	setDateFormat: function(format, separator)
	{
		if (Object.isUndefined(format))
			format = this._dateFormat[0];
		if (Object.isUndefined(separator))
			separator = this._dateFormat[1];
		this._dateFormat = [format, separator];
	},
	/**
	* setPosition	: set the position of the datepicker.
	*  param : t=top | l=left
	*/
	setPosition: function(t, l)
	{
		var h_pos = { 'top': '0px', 'left': '0px' };
		if (!Object.isUndefined(t))
			h_pos['top'] = Number(t) + this._topOffset + 'px';
		if (!Object.isUndefined(l))
			h_pos['left'] = Number(l) + this._leftOffset + 'px';
		$(this._id_datepicker).setStyle(h_pos);
		this._isPositionned = true;
	},
	/**
	* _getMonthDays : given the year and month find the number of days.
	*/
	_getMonthDays: function(year, month)
	{
		if (((0 == (year % 4)) &&
   ((0 != (year % 100)) || (0 == (year % 400)))) && (month == 1))
			return 29;
		return this._daysInMonth[month];
	},
	/**
	* _buildCalendar	: draw the days array for current date
	*/
	_buildCalendar: function()
	{
		var _self = this;
		var tbody = $(this._id_datepicker + '-tbody');
		try
		{
			while (tbody.hasChildNodes())
				tbody.removeChild(tbody.childNodes[0]);
		} catch (e) { };
		/* generate day headers */
		var trDay = new Element('tr');
		this._language_day.get(this._language).each(function(item)
		{
			var td = new Element('td');
			td.innerHTML = item;
			td.className = 'wday';
			trDay.appendChild(td);
		});
		tbody.appendChild(trDay);
		/* generate the content of days */

		/* build-up days matrix */
		var a_d = [[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0]
   , [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0]
   , [0, 0, 0, 0, 0, 0, 0]
  ];
		/* set date at beginning of month to display */
		var d = new Date(this._current_year, this._current_mon, 1, 12);
		/* start the day list on monday */
		var startIndex = (!d.getDay()) ? 6 : d.getDay() - 1;
		var nbDaysInMonth = this._getMonthDays(
    this._current_year, this._current_mon);
		var daysIndex = 1;
		for (var j = startIndex; j < 7; j++)
		{
			a_d[0][j] = {
				d: daysIndex
    , m: this._current_mon
    , y: this._current_year
			};
			daysIndex++;
		}
		var a_prevMY = this._prevMonthYear();
		var nbDaysInMonthPrev = this._getMonthDays(a_prevMY[1], a_prevMY[0]);
		for (var j = 0; j < startIndex; j++)
		{
			a_d[0][j] = {
				d: Number(nbDaysInMonthPrev - startIndex + j + 1)
    , m: Number(a_prevMY[0])
    , y: a_prevMY[1]
    , c: 'outbound'
			};
		}
		var switchNextMonth = false;
		var currentMonth = this._current_mon;
		var currentYear = this._current_year;
		for (var i = 1; i < 6; i++)
		{
			for (var j = 0; j < 7; j++)
			{
				a_d[i][j] = {
					d: daysIndex
     , m: currentMonth
     , y: currentYear
     , c: (switchNextMonth) ? 'outbound' : (
      ((daysIndex == this._todayDate.getDate()) &&
        (this._current_mon == this._todayDate.getMonth()) &&
        (this._current_year == this._todayDate.getFullYear())) ? 'today' : null)
				};
				daysIndex++;
				/* if at the end of the month : reset counter */
				if (daysIndex > nbDaysInMonth)
				{
					daysIndex = 1;
					switchNextMonth = true;
					if (this._current_mon + 1 > 11)
					{
						currentMonth = 0;
						currentYear += 1;
					} else
					{
						currentMonth += 1;
					}
				}
			}
		}
		/* generate days for current date */
		for (var i = 0; i < 6; i++)
		{
			var tr = new Element('tr');
			for (var j = 0; j < 7; j++)
			{
				var h_ij = a_d[i][j];
				var td = new Element('td');
				/* id is : datepicker-day-mon-year or depending on language other way */
				/* don't forget to add 1 on month for proper formmatting */
				var id = $A([
     this._relative,
     this._df.date_to_string(h_ij["y"], h_ij["m"] + 1, h_ij["d"], '-')
    ]).join('-');
				/* set id and classname for cell if exists */
				td.setAttribute('id', id);
				if (h_ij["c"])
					td.className = h_ij["c"];
				/* on onclick : rebuild date value from id of current cell */
				var _curDate = new Date();
				_curDate.setFullYear(h_ij["y"], h_ij["m"], h_ij["d"]);
				if (this._disablePastDate || this._disableFutureDate)
				{
					if (this._disablePastDate)
					{
						var _res = (_curDate >= this._todayDate) ? true : false;
						this._bindCellOnClick(td, true, _res, h_ij["c"]);
					}
					if (this._disableFutureDate)
					{
						var _res = (this._todayDate.getTime() + this._oneDayInMs > _curDate.getTime()) ? true : false;
						this._bindCellOnClick(td, true, _res, h_ij["c"]);
					}
				} else
				{
					this._bindCellOnClick(td, false);
				}
				td.innerHTML = h_ij["d"];
				tr.appendChild(td);
			}
			tbody.appendChild(tr);
		}
		return tbody;
	},
	/**
	* _bindCellOnClick	: bind the cell onclick depending on status.
	*/
	_bindCellOnClick: function(td, wcompare, compareresult, h_ij_c)
	{
		var doBind = false;
		if (wcompare)
		{
			if (compareresult)
			{
				doBind = true;
			} else
			{
				td.className = (h_ij_c) ? 'nclick_outbound' : 'nclick';
			}
		} else
		{
			doBind = true;
		}
		if (doBind)
		{
			var _self = this;
			td.onclick = function()
			{
				$(_self._relative).value = String($(this).readAttribute('id')
      ).replace(_self._relative + '-', '').replace(/-/g, _self._df.separator);
				/* if we have a cellCallback defined call it and pass it the cell */
				if (_self._cellCallback)
					_self._cellCallback(this);
				_self.close();
			};
		}
	},
	/**
	* nextMonth	: redraw the calendar content for next month.
	*/
	_nextMonthYear: function()
	{
		var c_mon = this._current_mon;
		var c_year = this._current_year;
		if (c_mon + 1 > 11)
		{
			c_mon = 0;
			c_year += 1;
		} else
		{
			c_mon += 1;
		}
		return [c_mon, c_year];
	},
	nextMonth: function()
	{
		var a_next = this._nextMonthYear();
		var _nextMon = a_next[0];
		var _nextYear = a_next[1];
		var _curDate = new Date(); _curDate.setFullYear(_nextYear, _nextMon, 1);
		var _res = (this._todayDate.getTime() + this._oneDayInMs > _curDate.getTime()) ? true : false;
		if (this._disableFutureDate && !_res)
			return;
		this._current_mon = _nextMon;
		this._current_year = _nextYear;
		this._redrawCalendar();
	},
	/**
	* prevMonth	: redraw the calendar content for previous month.
	*/
	_prevMonthYear: function()
	{
		var c_mon = this._current_mon;
		var c_year = this._current_year;
		if (c_mon - 1 < 0)
		{
			c_mon = 11;
			c_year -= 1;
		} else
		{
			c_mon -= 1;
		}
		return [c_mon, c_year];
	},
	prevMonth: function()
	{
		var a_prev = this._prevMonthYear();
		var _prevMon = a_prev[0];
		var _prevYear = a_prev[1];
		var _curDate = new Date(); _curDate.setFullYear(_prevYear, _prevMon, 1);
		var _res = (_curDate >= this._todayDate) ? true : false;
		if (this._disablePastDate && !_res && (_prevMon != this._todayDate.getMonth()))
			return;
		this._current_mon = _prevMon;
		this._current_year = _prevYear;
		this._redrawCalendar();
	},
	/**
	* prevYear	: redraw the calendar content for prev year.
	*/
	_prevYear: function()
	{
		var c_mon = this._current_mon;
		var c_year = (this._current_year - 1);

		return [c_mon, c_year];
	},
	prevYear: function()
	{
		var a_next = this._prevYear();
		var _nextMon = a_next[0];
		var _nextYear = a_next[1];
		var _curDate = new Date(); _curDate.setFullYear(_nextYear, _nextMon, 1);
		var _res = (this._todayDate.getTime() + this._oneDayInMs > _curDate.getTime()) ? true : false;
		if (this._disableFutureDate && !_res)
			return;
		this._current_mon = _nextMon;
		this._current_year = _nextYear;
		this._redrawCalendar();
	},

	/**
	* nextYear	: redraw the calendar content for next year.
	*/
	_nextYear: function()
	{
		var c_mon = this._current_mon;
		var c_year = (this._current_year + 1);

		return [c_mon, c_year];
	},
	nextYear: function()
	{
		var a_next = this._nextYear();
		var _nextMon = a_next[0];
		var _nextYear = a_next[1];
		var _curDate = new Date(); _curDate.setFullYear(_nextYear, _nextMon, 1);
		var _res = (this._todayDate.getTime() + this._oneDayInMs > _curDate.getTime()) ? true : false;
		if (this._disableFutureDate && !_res)
			return;
		this._current_mon = _nextMon;
		this._current_year = _nextYear;
		this._redrawCalendar();
	},

	_redrawCalendar: function()
	{
		this._setLocaleHdr(); this._buildCalendar();
	},
	_setLocaleHdr: function()
	{
		/* next link */
		var a_next = this._nextMonthYear();
		$(this._id_datepicker_next).setAttribute('title',
   this.getMonthLocale(a_next[0]) + ' ' + a_next[1]);
		/* prev link */
		var a_prev = this._prevMonthYear();
		$(this._id_datepicker_prev).setAttribute('title',
   this.getMonthLocale(a_prev[0]) + ' ' + a_prev[1]);
		/* year browse */
		if (this._enableYearBrowse)
		{
			var a_next_y = this._nextYear();
			$(this._id_datepicker_next_year).setAttribute('title',
     this.getMonthLocale(a_next_y[0]) + ' ' + a_next_y[1]);
			var a_prev_y = this._prevYear();
			$(this._id_datepicker_prev_year).setAttribute('title',
     this.getMonthLocale(a_prev_y[0]) + ' ' + a_prev_y[1]);
		}
		/* header */
		$(this._id_datepicker_hdr).update('&nbsp;&nbsp;&nbsp;' + this.getMonthLocale(this._current_mon) + '&nbsp;' + this._current_year + '&nbsp;&nbsp;&nbsp;');
	}
};

