Code

Re-enabled schema checks
[gosa.git] / gosa-core / html / include / datepicker.js
1 /**
2  * DatePicker widget using Prototype and Scriptaculous.
3  * (c) 2007 Mathieu Jondet <mathieu@eulerian.com>
4  * Eulerian Technologies
5  *
6  * DatePicker is freely distributable under the same terms as Prototype.
7  *
8  *      Modified 10.06.2008
9  * by Manu <manu@bytefresser.de>
10  *
11  */
13 /**
14  * DatePickerFormatter class for matching and stringifying dates.
15  *
16  * By Arturas Slajus <x11@arturaz.net>.
17  */
18 var DatePickerFormatter = Class.create();
20 DatePickerFormatter.prototype = {
21         /**
22          * Create a DatePickerFormatter.
23          *
24          * format: specify a format by passing 3 value array consisting of
25          *   "yyyy", "mm", "dd". Default: ["yyyy", "mm", "dd"].
26          *
27          * separator: string for splitting the values. Default: "-".
28          *
29          * Use it like this:
30          *   var df = new DatePickerFormatter(["dd", "mm", "yyyy"], "/");
31          *   df.current_date();
32          *   df.match("7/7/2007");
33          */
34         initialize: function(format, separator) {
35                 
36                 if (Object.isUndefined(format))
37                         format = ["yyyy", "mm", "dd"];
38                 if (Object.isUndefined(separator))
39                         separator = "-";
40                 
41                 this._format                            = format;
42                 this.separator                          = separator;
43                     
44                 this._format_year_index = format.indexOf("yyyy");
45                 this._format_month_index= format.indexOf("mm");
46                 this._format_day_index  = format.indexOf("dd");
47                     
48                 this._year_regexp                       = /^\d{4}$/;
49                 this._month_regexp              = /^0\d|1[012]|\d$/;
50                 this._day_regexp                        = /^0\d|[12]\d|3[01]|\d$/;
51         },
52     
53         /**
54         * Match a string against date format.
55         * Returns: [year, month, day]
56         */
57         match: function(str) {
58         
59                 var d = str.split(this.separator);
60                 
61                 if (d.length < 3) {
62                         return false;
63                 }
64                 
65                 var year = d[this._format_year_index].match(this._year_regexp);
66                 if (year) {
67                         year = year[0]
68                 } else {
69                         return false
70                 }
71                 var month = d[this._format_month_index].match(this._month_regexp);
72                 if (month) {
73                         month = month[0]
74                 } else {
75                         return false
76                 }
77                 var day = d[this._format_day_index].match(this._day_regexp);
78                 if (day) {
79                         day = day[0]
80                 } else {
81                         return false
82                 }
83                 
84                 return [year, month, day];
85         },
86     
87         /**
88          * Return current date according to format.
89          */
90         current_date: function() {
91                 var d = new Date;
92                 return this.date_to_string (
93                         d.getFullYear(),
94                         d.getMonth() + 1,
95                         d.getDate()
96                 );
97         },
99         /**
100          * Return a stringified date accordint to format.
101          */
102         date_to_string: function(year, month, day, separator) {
103                 
104                 if (Object.isUndefined(separator))
105                         separator = this.separator;
106         
107                 var a = [0, 0, 0];
108                 a[this._format_year_index]      = year;
109                 a[this._format_month_index]= month.toPaddedString(2);
110                 a[this._format_day_index]       = day.toPaddedString(2);
111                 
112                 return a.join(separator);
113         }
114 }; 
116 /**
117  * DatePicker
118  */
119 var datepickers = $H();
121 var DatePicker  = Class.create();
123 DatePicker.prototype    = {
124         
125         Version                         : '0.9.4',
126         _relative                       : null,
127         _div                                    : null,
128         _zindex                         : 1,
129         _keepFieldEmpty : false,
130         _daysInMonth            : [31,28,31,30,31,30,31,31,30,31,30,31],
131         _dateFormat                     : [ ["dd", "mm", "yyyy"], "." ],
132         
133         /* language */
134         _language                       : 'de',
135         _language_month : $H({
136                 'fr'    : [ 'Janvier', 'F&#233;vrier', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Aout', 'Septembre', 'Octobre', 'Novembre', 'D&#233;cembre' ],
137                 'en'    : [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ],
138                 'es'    : [ 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre' ],
139                 'it'    : [ 'Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre' ],
140                 'de'    : [ 'Januar', 'Februar', 'M&#228;rz', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember' ],
141                 'pt'    : [ 'Janeiro', 'Fevereiro', 'Mar&#231;o', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro' ],
142                 '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' ],
143                 'lt'  : [ 'Sausis', 'Vasaris', 'Kovas', 'Balandis', 'Gegu&#382;&#279;', 'Bir&#382;elis', 'Liepa', 'Rugj&#363;tis', 'Rus&#279;jis', 'Spalis', 'Lapkritis', 'Gruodis' ],
144                 'nl'    : [ 'januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december' ],
145                 'dk'    : [ 'Januar', 'Februar', 'Marts', 'April', 'Maj', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'December' ],
146                 'no'    : [ 'Januar', 'Februar', 'Mars', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Desember' ],
147                 'lv'    : [ 'Janv&#257;ris', 'Febru&#257;ris', 'Marts', 'Apr&#299;lis', 'Maijs', 'J&#363;nijs', 'J&#363;lijs', 'Augusts', 'Septembris', 'Oktobris', 'Novembris', 'Decemberis' ],
148                 '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;' ],
149                 'fi'    : [ 'Tammikuu', 'Helmikuu', 'Maaliskuu', 'Huhtikuu', 'Toukokuu', 'Kes&#228;kuu', 'Hein&#228;kuu', 'Elokuu', 'Syyskuu', 'Lokakuu', 'Marraskuu', 'Joulukuu' ],
150                 'ro'    : [ 'Ianuarie', 'Februarie', 'Martie', 'Aprilie', 'Mai', 'Junie', 'Julie', 'August', 'Septembrie', 'Octombrie', 'Noiembrie', 'Decembrie' ],
151                 '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;'],
152                 'sv'    : [ 'Januari', 'Februari', 'Mars', 'April', 'Maj', 'Juni', 'Juli', 'Augusti', 'September', 'Oktober', 'November', 'December' ]
153         }),
154         _language_day   : $H({
155                 'fr'    : [ 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam', 'Dim' ],
156                 'en'    : [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ],
157                 'es'    : [ 'Lun', 'Mar', 'Mie', 'Jue', 'Vie', 'S&#224;b', 'Dom' ],
158                 'it'    : [ 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab', 'Dom' ],
159                 'de'    : [ 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So' ],
160                 'pt'    : [ 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'S&#225;', 'Dom' ],
161                 'hu'    : [ 'H&#233;', 'Ke', 'Sze', 'Cs&#252;', 'P&#233;', 'Szo', 'Vas' ],
162                 'lt'  : [ 'Pir', 'Ant', 'Tre', 'Ket', 'Pen', '&Scaron;e&scaron;', 'Sek' ],
163                 'nl'    : [ 'ma', 'di', 'wo', 'do', 'vr', 'za', 'zo' ],
164                 'dk'    : [ 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'L&#248;r', 'S&#248;n' ],
165                 'no'    : [ 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'L&#248;r', 'Sun' ],
166                 'lv'    : [ 'P', 'O', 'T', 'C', 'Pk', 'S', 'Sv' ],
167                 'ja'    : [ '&#26376;', '&#28779;', '&#27700;', '&#26408;', '&#37329;', '&#22303;', '&#26085;' ],
168                 'fi'    : [ 'Ma', 'Ti', 'Ke', 'To', 'Pe', 'La', 'Su' ],
169                 'ro'    : [ 'Lun', 'Mar', 'Mie', 'Joi', 'Vin', 'Sam', 'Dum' ],
170                 'zh'    : [ '&#21608;&#19968;', '&#21608;&#20108;', '&#21608;&#19977;', '&#21608;&#22235;', '&#21608;&#20116;', '&#21608;&#20845;', '&#21608;&#26085;' ],
171                 'sv'    : [ 'M&#229;n', 'Tis', 'Ons', 'Tor', 'Fre', 'L&#246;r', 'S&#246;n' ]
172         }),
173         _language_close : $H({
174                 'fr'    : 'fermer',
175                 'en'    : 'close',
176                 'es'    : 'cierre',
177                 'it'    : 'fine',
178                 'de'    : 'schliessen',
179                 'pt'    : 'fim',
180                 'hu'    : 'bez&#225;r',
181                 'lt'  : 'udaryti',
182                 'nl'    : 'sluiten',
183                 'dk'    : 'luk',
184                 'no'    : 'lukk',
185                 'lv'    : 'aizv&#275;rt',
186                 'ja'    : '&#38281;&#12376;&#12427;',
187                 'fi'    : 'sulje',
188                 'ro'    : 'inchide',
189                 'zh'    : '&#20851;&#32;&#38381',
190                 'sv'    : 'st&#228;ng'
191         }),
192         
193         /* date manipulation */
194         _todayDate                              : new Date(),
195         _current_date                   : null,
196         _clickCallback                  : Prototype.emptyFunction,
197         _cellCallback                   : Prototype.emptyFunction,
198         _id_datepicker                  : null,
199         _disablePastDate                : false,
200         _disableFutureDate      : false,
201         _oneDayInMs                             : 24 * 3600 * 1000,
202         
203         /* positionning */
204         _topOffset                              : 20,
205         _leftOffset                             : 0,
206         _isPositionned                  : false,
207         _relativePosition       : true,
208         _setPositionTop                 : 0,
209         _setPositionLeft                : 0,
210         _bodyAppend                             : false,
211         
212         /* Effects Adjustment */
213         _showEffect                             : "appear", 
214         _showDuration                   : 1,
215         _enableShowEffect       : true,
216         _closeEffect                    : "fade", 
217         _closeEffectDuration    : 0.3,
218         _enableCloseEffect      : true,
219         _closeTimer                             : null,
220         _enableCloseOnBlur      : false,
221         
222         /* afterClose                   : called when the close function is executed */
223         _afterClose                             : Prototype.emptyFunction,
224         
225         /* return the name of current month in appropriate language */
226         getMonthLocale                  : function ( month ) {
227                 return this._language_month.get(this._language)[month];
228         },
229         getLocaleClose                  : function () {
230                 return this._language_close.get(this._language);
231         },
232         _initCurrentDate : function () {
233                 
234                 /* Create the DateFormatter */
235                 this._df = new DatePickerFormatter(this._dateFormat[0], this._dateFormat[1]);
236                 
237                 /* check if value in field is proper, if not set to today */
238                 this._current_date = $F(this._relative);
239                 
240                 if (! this._df.match(this._current_date)) {
241                         
242                         this._current_date = this._df.current_date();
243                         
244                         /* set the field value ? */
245                         if (!this._keepFieldEmpty)
246                                 $(this._relative).value = this._current_date;
247                 }
248                 var a_date = this._df.match(this._current_date);
249                 this._current_year      = Number(a_date[0]);
250                 this._current_mon               = Number(a_date[1]) - 1;
251                 this._current_day               = Number(a_date[2]);
252         },
253  
254         /* init */
255         initialize      : function ( h_p ) {
257                 /* arguments */
258                 this._relative= h_p["relative"];
259                 if (h_p["language"])
260                         this._language = h_p["language"];
261                 this._zindex = ( h_p["zindex"] ) ? parseInt(Number(h_p["zindex"])) : 999;
262                 if (!Object.isUndefined(h_p["keepFieldEmpty"]))
263                         this._keepFieldEmpty    = h_p["keepFieldEmpty"];
264                 if (Object.isFunction(h_p["clickCallback"])) 
265                         this._clickCallback = h_p["clickCallback"];
266                 if (!Object.isUndefined(h_p["leftOffset"]))
267                         this._leftOffset = parseInt(h_p["leftOffset"]);
268                 if (!Object.isUndefined(h_p["topOffset"]))
269                         this._topOffset = parseInt(h_p["topOffset"]);
270                 if (!Object.isUndefined(h_p["relativePosition"]))
271                         this._relativePosition = h_p["relativePosition"];
272                 if (!Object.isUndefined(h_p["showEffect"]))
273                         this._showEffect = h_p["showEffect"];
274                 if (!Object.isUndefined(h_p["enableShowEffect"]))
275                         this._enableShowEffect = h_p["enableShowEffect"];
276                 if (!Object.isUndefined(h_p["showDuration"]))
277                         this._showDuration = h_p["showDuration"];
278                 if (!Object.isUndefined(h_p["closeEffect"]))
279                         this._closeEffect = h_p["closeEffect"];
280                 if (!Object.isUndefined(h_p["enableCloseEffect"]))
281                         this._enableCloseEffect = h_p["enableCloseEffect"];
282                 if (!Object.isUndefined(h_p["closeEffectDuration"]))
283                         this._closeEffectDuration = h_p["closeEffectDuration"];
284                 if (Object.isFunction(h_p["afterClose"]))
285                         this._afterClose = h_p["afterClose"];
286                 if (!Object.isUndefined(h_p["externalControl"]))
287                         this._externalControl = h_p["externalControl"];
288                 if (!Object.isUndefined(h_p["dateFormat"])) 
289                         this._dateFormat = h_p["dateFormat"];
290                 if (Object.isFunction(h_p["cellCallback"]))
291                         this._cellCallback = h_p["cellCallback"];
292                 this._setPositionTop    = ( h_p["setPositionTop"] ) ? parseInt(Number(h_p["setPositionTop"])) : 0;
293                 this._setPositionLeft = ( h_p["setPositionLeft"] ) ? parseInt(Number(h_p["setPositionLeft"])) : 0;
294                 if (!Object.isUndefined(h_p["enableCloseOnBlur"]) && h_p["enableCloseOnBlur"])
295                         this._enableCloseOnBlur = true;
296                 if (!Object.isUndefined(h_p["disablePastDate"]) && h_p["disablePastDate"])
297                         this._disablePastDate = true;
298                 if (!Object.isUndefined(h_p["disableFutureDate"]) && !h_p["disableFutureDate"])
299                         this._disableFutureDate = false;
300                         
301                 this._id_datepicker                             = 'datepicker-'+ this._relative;
302                 this._id_datepicker_prev                = this._id_datepicker +'-prev';
303                 this._id_datepicker_prev_year   = this._id_datepicker +'-prev-year';
304                 this._id_datepicker_next                = this._id_datepicker +'-next';
305                 this._id_datepicker_next_year   = this._id_datepicker +'-next-year';
306                 this._id_datepicker_hdr                 = this._id_datepicker +'-header';
307                 this._id_datepicker_ftr                 = this._id_datepicker +'-footer';
309                 /* build up calendar skel */
310                 this._div = new Element('div', { 
311                         id                      : this._id_datepicker,
312                         className       : 'datepicker',
313                         style           : 'display: none; z-index:'+ this._zindex });
314    
315 //              this._div.innerHTML = '<table><thead><tr><th width="10px" id="'+ this._id_datepicker_prev +'" style="cursor: pointer;">&nbsp;&lt;&lt;&nbsp;</th><th id="'+ this._id_datepicker_hdr +'" colspan="5"></th><th width="10px" id="'+ this._id_datepicker_next +'" style="cursor: pointer;">&nbsp;&gt;&gt;&nbsp;</th></tr></thead><tbody id="'+ this._id_datepicker +'-tbody"></tbody><tfoot><td colspan="7" id="'+ this._id_datepicker_ftr +'"></td></tfoot></table>';
316                 this._div.innerHTML = '<div class="datepicker-header"><table class="header" cellspacing="0"><tr><td id="'+ this._id_datepicker_prev_year +'" class="prev_year"> << </td><td id="'+ this._id_datepicker_prev +'" class="prev"> < </td><td id="'+ this._id_datepicker_hdr +'" class="header"></td><td id="'+ this._id_datepicker_next +'" class="next"> > </td><td id="'+ this._id_datepicker_next_year +'" class="next_year"> >> </td></tr></table></div><div class="datepicker-calendar"><table class="body"><tbody id="'+ this._id_datepicker +'-tbody"></tbody></table></div><div id="'+ this._id_datepicker_ftr +'" class="datepicker-footer"></div>';
317                 
318                 /* Build the datepicker icon */
319                 var datepickeropener = Builder.node('table',{className : "datepicker-opener-table"});
321                 var con = Builder.node('tr',{},[
322                     Builder.node('td',{className : "datepicker-opener", id : "datepicker-opener-"+ this._relative})
323                 ]);
324                 // insert into TBODY
325                 if (datepickeropener.childNodes[0] != undefined) {
326                         datepickeropener.childNodes[0].appendChild(con);
327                 } else {
328                         datepickeropener.appendChild(con);
329                 }
330         
331                 Event.observe(datepickeropener,'click', this.click.bindAsEventListener(this), false);
332         
333                 this.insertAfter($(this._relative).parentNode,datepickeropener,$(this._relative));
334                 /* End Build the datepicker icon */
335         
336                 /* finally declare the event listener on input field */
337                 //Event.observe(this._relative, 'click', this.click.bindAsEventListener(this), false);
338                 
339                 /* need to append on body when doc is loaded for IE */
340                 document.observe('dom:loaded', this.load.bindAsEventListener(this), false);
341   
342                 /* automatically close when blur event is triggered */
343                 if ( this._enableCloseOnBlur ) {
344                         Event.observe(this._relative, 'blur', function (e) { 
345                                 this._closeTimer = this.close.bind(this).delay(1); 
346                         }.bindAsEventListener(this));
347                         Event.observe(this._div, 'click', function (e) { 
348                                 if (this._closeTimer) { 
349                                         window.clearTimeout(this._closeTimer); 
350                                         this._closeTimer = null; 
351                                 } 
352                         });
353                 }
354         },
355         
356         /**
357          * load : called when document is fully-loaded to append datepicker
358          *                to main object.
359          */
360         load : function () {
361   
362                 /* if externalControl defined set the observer on it */
363                 if (this._externalControl) 
364                         Event.observe(this._externalControl, 'click', this.click.bindAsEventListener(this), false);
365                 
366                 /* append to page */
367                 if (this._relativeAppend) {
368                 
369                         /* append to parent node */
370                         if ($(this._relative).parentNode) {
371                                 this._div.innerHTML = this._wrap_in_iframe(this._div.innerHTML);
372                                 $(this._relative).parentNode.appendChild( this._div );
373                         }
374                 } else {
375                         
376                         /* append to body */
377                         var body        = document.getElementsByTagName("body").item(0);
378                         if (body) {
379                                 this._div.innerHTML = this._wrap_in_iframe(this._div.innerHTML);
380                                 body.appendChild(this._div);
381                         }
382                         if ( this._relativePosition ) {
383                                 var a_pos = Element.cumulativeOffset($(this._relative));
384                                 this.setPosition(a_pos[1], a_pos[0]);
385                         } else {
386                                 if (this._setPositionTop || this._setPositionLeft)
387                                         this.setPosition(this._setPositionTop, this._setPositionLeft);
388                         }
389                 }
390                 /* init the date in field if needed */
391                 this._initCurrentDate();
392                 
393                 /* set the close locale content */
394                 $(this._id_datepicker_ftr).innerHTML = this.getLocaleClose();
395   
396                 /* declare the observers for UI control */
397                 Event.observe($(this._id_datepicker_prev),              'click', this.prevMonth.bindAsEventListener(this), false);
398                 Event.observe($(this._id_datepicker_prev_year), 'click', this.prevYear.bindAsEventListener(this),       false);
399                 Event.observe($(this._id_datepicker_next),              'click', this.nextMonth.bindAsEventListener(this), false);
400                 Event.observe($(this._id_datepicker_next_year), 'click', this.nextYear.bindAsEventListener(this),       false);
401                 Event.observe($(this._id_datepicker_ftr),                       'click', this.close.bindAsEventListener(this),          false);
402                 
403         },
404         
405         insertAfter : function(parent, node, referenceNode) {
406                 parent.insertBefore(node, referenceNode.nextSibling);
407         },
408         
409         /* hack for buggy form elements layering in IE */
410         _wrap_in_iframe : function ( content ) {
411                 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;
412         },
413         
414         /**
415          * visible      : return the visibility status of the datepicker.
416          */
417         visible : function () {
418                 return $(this._id_datepicker).visible();
419         },
420         
421         /**
422          * click        : called when input element is clicked
423          */
424         click   : function () {
425         
426                 /* init the datepicker if it doesn't exists */
427                 if ( $(this._id_datepicker) == null ) this.load();
428                 var a_pos = Element.cumulativeOffset($(this._relative));
429                 this.setPosition(a_pos[1], a_pos[0]);
430                 if (!this._isPositionned && this._relativePosition) {
431                         /* position the datepicker relatively to element */
432                         var a_lt = Element.positionedOffset($(this._relative));
433                         $(this._id_datepicker).setStyle({
434                                 'left'  : Number(a_lt[0]+this._leftOffset)+'px',
435                                 'top'   : Number(a_lt[1]+this._topOffset)+'px'
436                         });
437                         this._isPositionned     = true;
438                 }
439                 if (!this.visible()) {
440                         this._initCurrentDate();
441                         this._redrawCalendar();
442                 }
443   
444                 /* eval the clickCallback function */
445                 eval(this._clickCallback());
446                 
447                 /* Effect toggle to fade-in / fade-out the datepicker */
448                 if ( this._enableShowEffect ) {
449                         new Effect.toggle(this._id_datepicker, this._showEffect, { duration: this._showDuration });
450                 } else {
451                         $(this._id_datepicker).show();
452                 }
453         },
454         /**
455          * close        : called when the datepicker is closed
456          */
457         close           : function () {
458                 if ( this._enableCloseEffect ) {
459                         switch(this._closeEffect) {
460                                 case 'puff': 
461                                         new Effect.Puff(this._id_datepicker, { duration : this._closeEffectDuration });
462                                 break;
463                                 case 'blindUp': 
464                                         new Effect.BlindUp(this._id_datepicker, { duration : this._closeEffectDuration });
465                                 break;
466                                 case 'dropOut': 
467                                         new Effect.DropOut(this._id_datepicker, { duration : this._closeEffectDuration }); 
468                                 break;
469                                 case 'switchOff': 
470                                         new Effect.SwitchOff(this._id_datepicker, { duration : this._closeEffectDuration }); 
471                                 break;
472                                 case 'squish': 
473                                         new Effect.Squish(this._id_datepicker, { duration : this._closeEffectDuration });
474                                 break;
475                                 case 'fold': 
476                                         new Effect.Fold(this._id_datepicker, { duration : this._closeEffectDuration });
477                                 break;
478                                 case 'shrink': 
479                                         new Effect.Shrink(this._id_datepicker, { duration : this._closeEffectDuration });
480                                 break;
481                                 default: 
482                                         new Effect.Fade(this._id_datepicker, {  duration : this._closeEffectDuration });
483                                 break;
484                         };
485                 } else {
486                         $(this._id_datepicker).hide();
487                 }
488                 eval(this._afterClose());
489         },
490         
491         /**
492          * setDateFormat
493          */
494         setDateFormat : function ( format, separator ) {
495                 if (Object.isUndefined(format))
496                         format = this._dateFormat[0];
497                 if (Object.isUndefined(separator))
498                         separator = this._dateFormat[1];
499                 this._dateFormat = [ format, separator ];
500         },
501         
502         /**
503          * setPosition  : set the position of the datepicker.
504          *  param : t=top | l=left
505          */
506         setPosition     : function ( t, l ) {
507                 var h_pos = { 'top' : '0px', 'left' : '0px' };
508                 if (!Object.isUndefined(t))
509                         h_pos['top'] = Number(t) + this._topOffset +'px';
510                 if (!Object.isUndefined(l))
511                         h_pos['left']= Number(l) + this._leftOffset +'px';
512                 $(this._id_datepicker).setStyle(h_pos);
513                 this._isPositionned = true;
514         },
515         
516         /**
517          * _getMonthDays : given the year and month find the number of days.
518          */
519         _getMonthDays : function ( year, month ) {
520                 if (((0 == (year%4)) && ((0 != (year%100)) || (0 == (year%400)))) && (month == 1))
521                         return 29;
522                 return this._daysInMonth[month];
523         },
524         
525         /**
526          * _buildCalendar       : draw the days array for current date
527          */
528         _buildCalendar : function () {
529                 
530                 var _self = this;
531                 var tbody = $(this._id_datepicker +'-tbody');
532                 try {
533                         while ( tbody.hasChildNodes() )
534                         tbody.removeChild(tbody.childNodes[0]);
535                 } catch ( e ) {};
537                 /* generate day headers */
538                 var trDay = new Element('tr');
539                 this._language_day.get(this._language).each( function ( item ) {
540                         var td = new Element('td');
541                         td.innerHTML = item;
542                         td.className = 'wday';
543                         trDay.appendChild( td );
544                 });
545                 tbody.appendChild( trDay );
546         
547                 /* generate the content of days */
548         
549                 /* build-up days matrix */
550                 var a_d = [
551                         [ 0, 0, 0, 0, 0, 0, 0 ],
552                         [ 0, 0, 0, 0, 0, 0, 0 ],
553                         [ 0, 0, 0, 0, 0, 0, 0 ],
554                         [ 0, 0, 0, 0, 0, 0, 0 ],
555                         [ 0, 0, 0, 0, 0, 0, 0 ],
556                         [ 0, 0, 0, 0, 0, 0, 0 ]
557                 ];
558                 
559                 /* set date at beginning of month to display */
560                 var d   = new Date(this._current_year, this._current_mon, 1, 12);
561                 
562                 /* start the day list on monday */
563                 var startIndex  = ( !d.getDay() ) ? 6 : d.getDay() - 1;
564                 var nbDaysInMonth       = this._getMonthDays(
565                 this._current_year, this._current_mon);
566                 var daysIndex = 1;
567                 
568                 for ( var j = startIndex; j < 7; j++ ) {
569                         a_d[0][j] = { 
570                                 d : daysIndex,
571                                 m : this._current_mon,
572                                 y : this._current_year 
573                         };
574                         daysIndex++;
575                 }
576                 
577                 var a_prevMY = this._prevMonthYear();
578                 var nbDaysInMonthPrev = this._getMonthDays(a_prevMY[1], a_prevMY[0]);
579                 for ( var j = 0; j < startIndex; j++ ) {
580                         a_d[0][j] = { 
581                                 d : Number(nbDaysInMonthPrev - startIndex + j + 1),
582                                 m : Number(a_prevMY[0]),
583                                 y : a_prevMY[1],
584                                 c : 'outbound'
585                         };
586                 }
587                 var switchNextMonth = false;
588                 var currentMonth = this._current_mon;
589                 var currentYear = this._current_year;
590                 for ( var i = 1; i < 6; i++ ) {
591                         for ( var j = 0; j < 7; j++ ) {
592                                 a_d[i][j] = { 
593                                         d : daysIndex,
594                                         m : currentMonth,
595                                         y : currentYear,
596                                         c : ( switchNextMonth ) ? 'outbound' : (((daysIndex == this._todayDate.getDate()) && (this._current_mon  == this._todayDate.getMonth()) && (this._current_year == this._todayDate.getFullYear())) ? 'today' : null)
597                                 };
598                                 daysIndex++;
599                                 
600                                 /* if at the end of the month : reset counter */
601                                 if (daysIndex > nbDaysInMonth ) {
602                                         daysIndex = 1;
603                                         switchNextMonth = true;
604                                         if (this._current_mon + 1 > 11 ) {
605                                                 currentMonth = 0;
606                                                 currentYear += 1;
607                                         } else {
608                                                 currentMonth += 1;
609                                         }
610                                 }
611                         }
612                 }
613                 
614                 /* generate days for current date */
615                 for ( var i = 0; i < 6; i++ ) {
616                         var tr = new Element('tr');
617                         for ( var j = 0; j < 7; j++ ) {
618                                 var h_ij        = a_d[i][j];
619                                 var td = new Element('td');
620                                 
621                                 /* id is : datepicker-day-mon-year or depending on language other way */
622                                 /* don't forget to add 1 on month for proper formmatting */
623                                 var id = $A([
624                                         this._relative,
625                                         this._df.date_to_string(h_ij["y"], h_ij["m"]+1, h_ij["d"], '-')
626                                 ]).join('-');
627                                 
628                                 /* set id and classname for cell if exists */
629                                 td.setAttribute('id', id);
630                                 if (h_ij["c"])
631                                         td.className    = h_ij["c"];
632                                         
633                                 /* on onclick : rebuild date value from id of current cell */
634                                 var _curDate = new Date();
635                                 
636                                 _curDate.setFullYear(h_ij["y"], h_ij["m"], h_ij["d"]);
637                                 if ( this._disablePastDate || this._disableFutureDate ) {
638                                         if ( this._disablePastDate ) {
639                                                 var _res        = ( _curDate >= this._todayDate ) ? true : false;
640                                                 this._bindCellOnClick( td, true, _res, h_ij["c"] );
641                                         }
642                                         if ( this._disableFutureDate ) {
643                                                 var _res        = ( this._todayDate.getTime() + this._oneDayInMs > _curDate.getTime() ) ? true : false;
644                                                 this._bindCellOnClick( td, true, _res,  h_ij["c"] );
645                                         }
646                                 } else {
647                                         this._bindCellOnClick( td, false );
648                                 }
649                                 td.innerHTML= h_ij["d"];
650                                 tr.appendChild( td );
651                         }
652                         tbody.appendChild( tr );
653                 }
654                 return  tbody;
655         },
657         /**
658          * _bindCellOnClick     : bind the cell onclick depending on status.
659          */
660         _bindCellOnClick : function ( td, wcompare, compareresult, h_ij_c ) {
661                 var doBind      = false;
662                 if ( wcompare ) {
663                         if ( compareresult ) {
664                                 doBind  = true;
665                         } else {
666                                 td.className= ( h_ij_c ) ? 'nclick_outbound' : 'nclick';
667                         }
668                 } else {
669                         doBind = true;
670                 }
671                 if ( doBind ) {
672                         var _self = this;
673                         td.onclick = function () { 
674                         $(_self._relative).value = String($(this).readAttribute('id')).replace(_self._relative+'-','').replace(/-/g, _self._df.separator);
675         
676                         /* if we have a cellCallback defined call it and pass it the cell */
677                         if (_self._cellCallback)
678                                 _self._cellCallback(this);
679                         _self.close(); 
680                         };
681                 }
682         },
683  
684         /**
685          * nextMonth    : redraw the calendar content for next month.
686          */
687         _nextMonthYear  : function () {
688                 var c_mon       = this._current_mon;
689                 var c_year      = this._current_year;
690                 if (c_mon + 1 > 11) {
691                         c_mon   = 0;
692                         c_year  += 1;
693                 } else {
694                         c_mon   += 1;
695                 }
696                 return [ c_mon, c_year ];
697         },
698  
699         nextMonth : function () {
700                 var a_next              = this._nextMonthYear();
701                 var _nextMon    = a_next[0];
702                 var _nextYear   = a_next[1];
703                 var _curDate    = new Date();
704                 _curDate.setFullYear(_nextYear, _nextMon, 1);
705                 var _res        = ( this._todayDate.getTime() + this._oneDayInMs > _curDate.getTime() ) ? true : false;
706                 if ( this._disableFutureDate && !_res )
707                         return;
708                 this._current_mon       = _nextMon;
709                 this._current_year = _nextYear;
710                 this._redrawCalendar();
711         },
712         
713         _nextYear : function () {
714                 var c_mon       = this._current_mon;
715                 var c_year      = this._current_year;
716                 c_year  += 1;
717                 return [ c_mon, c_year ];
718         },
719         
720         nextYear        : function () {
721                 var a_next = this._nextYear();
722                 this._current_mon       = a_next[0];
723                 this._current_year = a_next[1];
724                 this._redrawCalendar();
725         },
727         /**
728         * prevMonth     : redraw the calendar content for previous month.
729         */
730         _prevMonthYear  : function () {
731                 var c_mon       = this._current_mon;
732                 var c_year      = this._current_year;
733                 if (c_mon - 1 < 0) {
734                         c_mon   = 11;
735                         c_year  -= 1;
736                 } else {
737                         c_mon   -= 1;
738                 }
739                 return [ c_mon, c_year ];
740         },
741  
742         prevMonth : function () {
743                 var a_prev              = this._prevMonthYear();
744                 var _prevMon    = a_prev[0];
745                 var _prevYear   = a_prev[1];
746                 var _curDate    = new Date();
747                 _curDate.setFullYear(_prevYear, _prevMon, 1);
748                 var _res        = ( _curDate >= this._todayDate ) ? true : false;
749                 if ( this._disablePastDate && !_res )
750                         return;
751                 this._current_mon       = _prevMon;
752                 this._current_year = _prevYear;
753                 this._redrawCalendar();
754         },
755         
756         _prevYear : function () {
757                 var c_mon       = this._current_mon;
758                 var c_year      = this._current_year;
759                 c_year  -= 1;
760                 return [ c_mon, c_year ];
761         },
763         prevYear        : function () {
764                 var a_prev = this._prevYear();
765                 this._current_mon       = a_prev[0];
766                 this._current_year = a_prev[1];
767                 this._redrawCalendar();
768         },
769         
770         _redrawCalendar : function () {
771                 this._setLocaleHdr();
772                 this._buildCalendar();
773         },
774         
775         _setLocaleHdr : function () {
776                 
777                 /* prev year link */
778                 var a_prevy = this._prevYear();
779                 $(this._id_datepicker_prev_year).setAttribute('title', this.getMonthLocale(a_prevy[0]) +' '+ a_prevy[1]);
780                 
781                 /* prev link */
782                 var a_prev = this._prevMonthYear();
783                 $(this._id_datepicker_prev).setAttribute('title', this.getMonthLocale(a_prev[0]) +' '+ a_prev[1]);
784                 
785                 /* next link */
786                 var a_next = this._nextMonthYear();
787                 $(this._id_datepicker_next).setAttribute('title', this.getMonthLocale(a_next[0]) +' '+ a_next[1]);
788                 
789                 /* next year link */
790                 var a_nexty = this._nextYear();
791                 $(this._id_datepicker_next_year).setAttribute('title', this.getMonthLocale(a_nexty[0]) +' '+ a_nexty[1]);
792                 
793                 /* header */
794                 $(this._id_datepicker_hdr).update('&nbsp;&nbsp;&nbsp;'+ this.getMonthLocale(this._current_mon) +'&nbsp;'+ this._current_year +'&nbsp;&nbsp;&nbsp;');
795         }
796 };