Code

Substract height of any plugin-action divs in order to not hide buttons
[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", id: this._id_datepicker + '_image'});
320                 var con = Builder.node('tr',{},[
321                     Builder.node('td',{className : "datepicker-opener", id : "datepicker-opener-"+ this._relative})
322                 ]);
323                 // insert into TBODY
324                 if (datepickeropener.childNodes[0] != undefined) {
325                         datepickeropener.childNodes[0].appendChild(con);
326                 } else {
327                         datepickeropener.appendChild(con);
328                 }
329         
330                 Event.observe(datepickeropener,'click', this.click.bindAsEventListener(this), false);
331         
332                 this.insertAfter($(this._relative).parentNode,datepickeropener,$(this._relative));
333                 /* End Build the datepicker icon */
334         
335                 /* finally declare the event listener on input field */
336                 //Event.observe(this._relative, 'click', this.click.bindAsEventListener(this), false);
337                 
338                 /* need to append on body when doc is loaded for IE */
339                 document.observe('dom:loaded', this.load.bindAsEventListener(this), false);
340   
341                 /* automatically close when blur event is triggered */
342                 if ( this._enableCloseOnBlur ) {
343                         Event.observe(this._relative, 'blur', function (e) { 
344                                 this._closeTimer = this.close.bind(this).delay(1); 
345                         }.bindAsEventListener(this));
346                         Event.observe(this._div, 'click', function (e) { 
347                                 if (this._closeTimer) { 
348                                         window.clearTimeout(this._closeTimer); 
349                                         this._closeTimer = null; 
350                                 } 
351                         });
352                 }
353         },
354         
355         /**
356          * load : called when document is fully-loaded to append datepicker
357          *                to main object.
358          */
359         load : function () {
360   
361                 /* if externalControl defined set the observer on it */
362                 if (this._externalControl) 
363                         Event.observe(this._externalControl, 'click', this.click.bindAsEventListener(this), false);
364                 
365                 /* append to page */
366                 if (this._relativeAppend) {
367                 
368                         /* append to parent node */
369                         if ($(this._relative).parentNode) {
370                                 this._div.innerHTML = this._wrap_in_iframe(this._div.innerHTML);
371                                 $(this._relative).parentNode.appendChild( this._div );
372                         }
373                 } else {
374                         
375                         /* append to body */
376                         var body        = document.getElementsByTagName("body").item(0);
377                         if (body) {
378                                 this._div.innerHTML = this._wrap_in_iframe(this._div.innerHTML);
379                                 body.appendChild(this._div);
380                         }
381                         if ( this._relativePosition ) {
382                                 var a_pos = Element.cumulativeOffset($(this._relative));
383                                 this.setPosition(a_pos[1], a_pos[0]);
384                         } else {
385                                 if (this._setPositionTop || this._setPositionLeft)
386                                         this.setPosition(this._setPositionTop, this._setPositionLeft);
387                         }
388                 }
389                 /* init the date in field if needed */
390                 this._initCurrentDate();
391                 
392                 /* set the close locale content */
393                 $(this._id_datepicker_ftr).innerHTML = this.getLocaleClose();
394   
395                 /* declare the observers for UI control */
396                 Event.observe($(this._id_datepicker_prev),              'click', this.prevMonth.bindAsEventListener(this), false);
397                 Event.observe($(this._id_datepicker_prev_year), 'click', this.prevYear.bindAsEventListener(this),       false);
398                 Event.observe($(this._id_datepicker_next),              'click', this.nextMonth.bindAsEventListener(this), false);
399                 Event.observe($(this._id_datepicker_next_year), 'click', this.nextYear.bindAsEventListener(this),       false);
400                 Event.observe($(this._id_datepicker_ftr),                       'click', this.close.bindAsEventListener(this),          false);
401                 
402         },
403         
404         insertAfter : function(parent, node, referenceNode) {
405                 parent.insertBefore(node, referenceNode.nextSibling);
406         },
407         
408         /* hack for buggy form elements layering in IE */
409         _wrap_in_iframe : function ( content ) {
410                 return  ( Prototype.Browser.IE && msieversion() < 8 ) ? "<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;
411         },
412         
413         /**
414          * visible      : return the visibility status of the datepicker.
415          */
416         visible : function () {
417                 return $(this._id_datepicker).visible();
418         },
419         
420         /**
421          * click        : called when input element is clicked
422          */
423         click   : function () {
424         
425                 /* init the datepicker if it doesn't exists */
426                 if ( $(this._id_datepicker) == null ) this.load();
427                 var a_pos = Element.cumulativeOffset($(this._relative));
428                 this.setPosition(a_pos[1], a_pos[0]);
429                 if (!this._isPositionned && this._relativePosition) {
430                         /* position the datepicker relatively to element */
431                         var a_lt = Element.positionedOffset($(this._relative));
432                         $(this._id_datepicker).setStyle({
433                                 'left'  : Number(a_lt[0]+this._leftOffset)+'px',
434                                 'top'   : Number(a_lt[1]+this._topOffset)+'px'
435                         });
436                         this._isPositionned     = true;
437                 }
438                 if (!this.visible()) {
439                         this._initCurrentDate();
440                         this._redrawCalendar();
441                 }
442   
443                 /* eval the clickCallback function */
444                 eval(this._clickCallback());
445                 
446                 /* Effect toggle to fade-in / fade-out the datepicker */
447                 if ( this._enableShowEffect ) {
448                         new Effect.toggle(this._id_datepicker, this._showEffect, { duration: this._showDuration });
449                 } else {
450                         $(this._id_datepicker).show();
451                 }
452         },
453         /**
454          * close        : called when the datepicker is closed
455          */
456         close           : function () {
457                 if ( this._enableCloseEffect ) {
458                         switch(this._closeEffect) {
459                                 case 'puff': 
460                                         new Effect.Puff(this._id_datepicker, { duration : this._closeEffectDuration });
461                                 break;
462                                 case 'blindUp': 
463                                         new Effect.BlindUp(this._id_datepicker, { duration : this._closeEffectDuration });
464                                 break;
465                                 case 'dropOut': 
466                                         new Effect.DropOut(this._id_datepicker, { duration : this._closeEffectDuration }); 
467                                 break;
468                                 case 'switchOff': 
469                                         new Effect.SwitchOff(this._id_datepicker, { duration : this._closeEffectDuration }); 
470                                 break;
471                                 case 'squish': 
472                                         new Effect.Squish(this._id_datepicker, { duration : this._closeEffectDuration });
473                                 break;
474                                 case 'fold': 
475                                         new Effect.Fold(this._id_datepicker, { duration : this._closeEffectDuration });
476                                 break;
477                                 case 'shrink': 
478                                         new Effect.Shrink(this._id_datepicker, { duration : this._closeEffectDuration });
479                                 break;
480                                 default: 
481                                         new Effect.Fade(this._id_datepicker, {  duration : this._closeEffectDuration });
482                                 break;
483                         };
484                 } else {
485                         $(this._id_datepicker).hide();
486                 }
487                 eval(this._afterClose());
488         },
489         
490         /**
491          * setDateFormat
492          */
493         setDateFormat : function ( format, separator ) {
494                 if (Object.isUndefined(format))
495                         format = this._dateFormat[0];
496                 if (Object.isUndefined(separator))
497                         separator = this._dateFormat[1];
498                 this._dateFormat = [ format, separator ];
499         },
500         
501         /**
502          * setPosition  : set the position of the datepicker.
503          *  param : t=top | l=left
504          */
505         setPosition     : function ( t, l ) {
506                 var h_pos = { 'top' : '0px', 'left' : '0px' };
507                 if (!Object.isUndefined(t))
508                         h_pos['top'] = Number(t) + this._topOffset +'px';
509                 if (!Object.isUndefined(l))
510                         h_pos['left']= Number(l) + this._leftOffset +'px';
511                 $(this._id_datepicker).setStyle(h_pos);
512                 this._isPositionned = true;
513         },
514         
515         /**
516          * _getMonthDays : given the year and month find the number of days.
517          */
518         _getMonthDays : function ( year, month ) {
519                 if (((0 == (year%4)) && ((0 != (year%100)) || (0 == (year%400)))) && (month == 1))
520                         return 29;
521                 return this._daysInMonth[month];
522         },
523         
524         /**
525          * _buildCalendar       : draw the days array for current date
526          */
527         _buildCalendar : function () {
528                 
529                 var _self = this;
530                 var tbody = $(this._id_datepicker +'-tbody');
531                 try {
532                         while ( tbody.hasChildNodes() )
533                         tbody.removeChild(tbody.childNodes[0]);
534                 } catch ( e ) {};
536                 /* generate day headers */
537                 var trDay = new Element('tr');
538                 this._language_day.get(this._language).each( function ( item ) {
539                         var td = new Element('td');
540                         td.innerHTML = item;
541                         td.className = 'wday';
542                         trDay.appendChild( td );
543                 });
544                 tbody.appendChild( trDay );
545         
546                 /* generate the content of days */
547         
548                 /* build-up days matrix */
549                 var a_d = [
550                         [ 0, 0, 0, 0, 0, 0, 0 ],
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                 ];
557                 
558                 /* set date at beginning of month to display */
559                 var d   = new Date(this._current_year, this._current_mon, 1, 12);
560                 
561                 /* start the day list on monday */
562                 var startIndex  = ( !d.getDay() ) ? 6 : d.getDay() - 1;
563                 var nbDaysInMonth       = this._getMonthDays(
564                 this._current_year, this._current_mon);
565                 var daysIndex = 1;
566                 
567                 for ( var j = startIndex; j < 7; j++ ) {
568                         a_d[0][j] = { 
569                                 d : daysIndex,
570                                 m : this._current_mon,
571                                 y : this._current_year 
572                         };
573                         daysIndex++;
574                 }
575                 
576                 var a_prevMY = this._prevMonthYear();
577                 var nbDaysInMonthPrev = this._getMonthDays(a_prevMY[1], a_prevMY[0]);
578                 for ( var j = 0; j < startIndex; j++ ) {
579                         a_d[0][j] = { 
580                                 d : Number(nbDaysInMonthPrev - startIndex + j + 1),
581                                 m : Number(a_prevMY[0]),
582                                 y : a_prevMY[1],
583                                 c : 'outbound'
584                         };
585                 }
586                 var switchNextMonth = false;
587                 var currentMonth = this._current_mon;
588                 var currentYear = this._current_year;
589                 for ( var i = 1; i < 6; i++ ) {
590                         for ( var j = 0; j < 7; j++ ) {
591                                 a_d[i][j] = { 
592                                         d : daysIndex,
593                                         m : currentMonth,
594                                         y : currentYear,
595                                         c : ( switchNextMonth ) ? 'outbound' : (((daysIndex == this._todayDate.getDate()) && (this._current_mon  == this._todayDate.getMonth()) && (this._current_year == this._todayDate.getFullYear())) ? 'today' : null)
596                                 };
597                                 daysIndex++;
598                                 
599                                 /* if at the end of the month : reset counter */
600                                 if (daysIndex > nbDaysInMonth ) {
601                                         daysIndex = 1;
602                                         switchNextMonth = true;
603                                         if (this._current_mon + 1 > 11 ) {
604                                                 currentMonth = 0;
605                                                 currentYear += 1;
606                                         } else {
607                                                 currentMonth += 1;
608                                         }
609                                 }
610                         }
611                 }
612                 
613                 /* generate days for current date */
614                 for ( var i = 0; i < 6; i++ ) {
615                         var tr = new Element('tr');
616                         for ( var j = 0; j < 7; j++ ) {
617                                 var h_ij        = a_d[i][j];
618                                 var td = new Element('td');
619                                 
620                                 /* id is : datepicker-day-mon-year or depending on language other way */
621                                 /* don't forget to add 1 on month for proper formmatting */
622                                 var id = $A([
623                                         this._relative,
624                                         this._df.date_to_string(h_ij["y"], h_ij["m"]+1, h_ij["d"], '-')
625                                 ]).join('-');
626                                 
627                                 /* set id and classname for cell if exists */
628                                 td.setAttribute('id', id);
629                                 if (h_ij["c"])
630                                         td.className    = h_ij["c"];
631                                         
632                                 /* on onclick : rebuild date value from id of current cell */
633                                 var _curDate = new Date();
634                                 
635                                 _curDate.setFullYear(h_ij["y"], h_ij["m"], h_ij["d"]);
636                                 if ( this._disablePastDate || this._disableFutureDate ) {
637                                         if ( this._disablePastDate ) {
638                                                 var _res        = ( _curDate >= this._todayDate ) ? true : false;
639                                                 this._bindCellOnClick( td, true, _res, h_ij["c"] );
640                                         }
641                                         if ( this._disableFutureDate ) {
642                                                 var _res        = ( this._todayDate.getTime() + this._oneDayInMs > _curDate.getTime() ) ? true : false;
643                                                 this._bindCellOnClick( td, true, _res,  h_ij["c"] );
644                                         }
645                                 } else {
646                                         this._bindCellOnClick( td, false );
647                                 }
648                                 td.innerHTML= h_ij["d"];
649                                 tr.appendChild( td );
650                         }
651                         tbody.appendChild( tr );
652                 }
653                 return  tbody;
654         },
656         /**
657          * _bindCellOnClick     : bind the cell onclick depending on status.
658          */
659         _bindCellOnClick : function ( td, wcompare, compareresult, h_ij_c ) {
660                 var doBind      = false;
661                 if ( wcompare ) {
662                         if ( compareresult ) {
663                                 doBind  = true;
664                         } else {
665                                 td.className= ( h_ij_c ) ? 'nclick_outbound' : 'nclick';
666                         }
667                 } else {
668                         doBind = true;
669                 }
670                 if ( doBind ) {
671                         var _self = this;
672                         td.onclick = function () { 
673                         $(_self._relative).value = String($(this).readAttribute('id')).replace(_self._relative+'-','').replace(/-/g, _self._df.separator);
674         
675                         /* if we have a cellCallback defined call it and pass it the cell */
676                         if (_self._cellCallback)
677                                 _self._cellCallback(this);
678                         _self.close(); 
679                         };
680                 }
681         },
682  
683         /**
684          * nextMonth    : redraw the calendar content for next month.
685          */
686         _nextMonthYear  : function () {
687                 var c_mon       = this._current_mon;
688                 var c_year      = this._current_year;
689                 if (c_mon + 1 > 11) {
690                         c_mon   = 0;
691                         c_year  += 1;
692                 } else {
693                         c_mon   += 1;
694                 }
695                 return [ c_mon, c_year ];
696         },
697  
698         nextMonth : function () {
699                 var a_next              = this._nextMonthYear();
700                 var _nextMon    = a_next[0];
701                 var _nextYear   = a_next[1];
702                 var _curDate    = new Date();
703                 _curDate.setFullYear(_nextYear, _nextMon, 1);
704                 var _res        = ( this._todayDate.getTime() + this._oneDayInMs > _curDate.getTime() ) ? true : false;
705                 if ( this._disableFutureDate && !_res )
706                         return;
707                 this._current_mon       = _nextMon;
708                 this._current_year = _nextYear;
709                 this._redrawCalendar();
710         },
711         
712         _nextYear : function () {
713                 var c_mon       = this._current_mon;
714                 var c_year      = this._current_year;
715                 c_year  += 1;
716                 return [ c_mon, c_year ];
717         },
718         
719         nextYear        : function () {
720                 var a_next = this._nextYear();
721                 this._current_mon       = a_next[0];
722                 this._current_year = a_next[1];
723                 this._redrawCalendar();
724         },
726         /**
727         * prevMonth     : redraw the calendar content for previous month.
728         */
729         _prevMonthYear  : function () {
730                 var c_mon       = this._current_mon;
731                 var c_year      = this._current_year;
732                 if (c_mon - 1 < 0) {
733                         c_mon   = 11;
734                         c_year  -= 1;
735                 } else {
736                         c_mon   -= 1;
737                 }
738                 return [ c_mon, c_year ];
739         },
740  
741         prevMonth : function () {
742                 var a_prev              = this._prevMonthYear();
743                 var _prevMon    = a_prev[0];
744                 var _prevYear   = a_prev[1];
745                 var _curDate    = new Date();
746                 _curDate.setFullYear(_prevYear, _prevMon, 1);
747                 var _res        = ( _curDate >= this._todayDate ) ? true : false;
748                 if ( this._disablePastDate && !_res )
749                         return;
750                 this._current_mon       = _prevMon;
751                 this._current_year = _prevYear;
752                 this._redrawCalendar();
753         },
754         
755         _prevYear : function () {
756                 var c_mon       = this._current_mon;
757                 var c_year      = this._current_year;
758                 c_year  -= 1;
759                 return [ c_mon, c_year ];
760         },
762         prevYear        : function () {
763                 var a_prev = this._prevYear();
764                 this._current_mon       = a_prev[0];
765                 this._current_year = a_prev[1];
766                 this._redrawCalendar();
767         },
768         
769         _redrawCalendar : function () {
770                 this._setLocaleHdr();
771                 this._buildCalendar();
772         },
773         
774         _setLocaleHdr : function () {
775                 
776                 /* prev year link */
777                 var a_prevy = this._prevYear();
778                 $(this._id_datepicker_prev_year).setAttribute('title', this.getMonthLocale(a_prevy[0]) +' '+ a_prevy[1]);
779                 
780                 /* prev link */
781                 var a_prev = this._prevMonthYear();
782                 $(this._id_datepicker_prev).setAttribute('title', this.getMonthLocale(a_prev[0]) +' '+ a_prev[1]);
783                 
784                 /* next link */
785                 var a_next = this._nextMonthYear();
786                 $(this._id_datepicker_next).setAttribute('title', this.getMonthLocale(a_next[0]) +' '+ a_next[1]);
787                 
788                 /* next year link */
789                 var a_nexty = this._nextYear();
790                 $(this._id_datepicker_next_year).setAttribute('title', this.getMonthLocale(a_nexty[0]) +' '+ a_nexty[1]);
791                 
792                 /* header */
793                 $(this._id_datepicker_hdr).update('&nbsp;&nbsp;&nbsp;'+ this.getMonthLocale(this._current_mon) +'&nbsp;'+ this._current_year +'&nbsp;&nbsp;&nbsp;');
794         }
795 };
798 function msieversion()
800    var ua = window.navigator.userAgent
801    var msie = ua.indexOf ( "MSIE " )
802    if ( msie > 0 )      // If Internet Explorer, return version number
803       return parseInt (ua.substring (msie+5, ua.indexOf (".", msie )))
804    else                 // If another browser, return 0
805       return 0