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