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) {
36 if (Object.isUndefined(format))
37 format = ["yyyy", "mm", "dd"];
38 if (Object.isUndefined(separator))
39 separator = "-";
41 this._format = format;
42 this.separator = separator;
44 this._format_year_index = format.indexOf("yyyy");
45 this._format_month_index= format.indexOf("mm");
46 this._format_day_index = format.indexOf("dd");
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 },
53 /**
54 * Match a string against date format.
55 * Returns: [year, month, day]
56 */
57 match: function(str) {
59 var d = str.split(this.separator);
61 if (d.length < 3) {
62 return false;
63 }
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 }
84 return [year, month, day];
85 },
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) {
104 if (Object.isUndefined(separator))
105 separator = this.separator;
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);
112 return a.join(separator);
113 }
114 };
116 /**
117 * DatePicker
118 */
119 var datepickers = $H();
121 var DatePicker = Class.create();
123 DatePicker.prototype = {
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"], "." ],
133 /* language */
134 _language : 'de',
135 _language_month : $H({
136 'fr' : [ 'Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Aout', 'Septembre', 'Octobre', 'Novembre', 'Dé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ärz', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember' ],
141 'pt' : [ 'Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro' ],
142 'hu' : [ 'Január', 'Február', 'Március', 'Április', 'Május', 'Június', 'Július', 'Augusztus', 'Szeptember', 'Október', 'November', 'December' ],
143 'lt' : [ 'Sausis', 'Vasaris', 'Kovas', 'Balandis', 'Gegužė', 'Birželis', 'Liepa', 'Rugjūtis', 'Rusė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āris', 'Februāris', 'Marts', 'Aprīlis', 'Maijs', 'Jūnijs', 'Jūlijs', 'Augusts', 'Septembris', 'Oktobris', 'Novembris', 'Decemberis' ],
148 'ja' : [ '1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月' ],
149 'fi' : [ 'Tammikuu', 'Helmikuu', 'Maaliskuu', 'Huhtikuu', 'Toukokuu', 'Kesäkuu', 'Heinäkuu', 'Elokuu', 'Syyskuu', 'Lokakuu', 'Marraskuu', 'Joulukuu' ],
150 'ro' : [ 'Ianuarie', 'Februarie', 'Martie', 'Aprilie', 'Mai', 'Junie', 'Julie', 'August', 'Septembrie', 'Octombrie', 'Noiembrie', 'Decembrie' ],
151 'zh' : [ '1 月', '2 月', '3 月', '4 月', '5 月', '6 月', '7 月', '8 月', '9 月', '10月', '11月', '12月'],
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à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á', 'Dom' ],
161 'hu' : [ 'Hé', 'Ke', 'Sze', 'Csü', 'Pé', 'Szo', 'Vas' ],
162 'lt' : [ 'Pir', 'Ant', 'Tre', 'Ket', 'Pen', 'Šeš', 'Sek' ],
163 'nl' : [ 'ma', 'di', 'wo', 'do', 'vr', 'za', 'zo' ],
164 'dk' : [ 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør', 'Søn' ],
165 'no' : [ 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør', 'Sun' ],
166 'lv' : [ 'P', 'O', 'T', 'C', 'Pk', 'S', 'Sv' ],
167 'ja' : [ '月', '火', '水', '木', '金', '土', '日' ],
168 'fi' : [ 'Ma', 'Ti', 'Ke', 'To', 'Pe', 'La', 'Su' ],
169 'ro' : [ 'Lun', 'Mar', 'Mie', 'Joi', 'Vin', 'Sam', 'Dum' ],
170 'zh' : [ '周一', '周二', '周三', '周四', '周五', '周六', '周日' ],
171 'sv' : [ 'Mån', 'Tis', 'Ons', 'Tor', 'Fre', 'Lör', 'Sö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ár',
181 'lt' : 'udaryti',
182 'nl' : 'sluiten',
183 'dk' : 'luk',
184 'no' : 'lukk',
185 'lv' : 'aizvērt',
186 'ja' : '閉じる',
187 'fi' : 'sulje',
188 'ro' : 'inchide',
189 'zh' : '关 闭',
190 'sv' : 'stäng'
191 }),
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,
203 /* positionning */
204 _topOffset : 20,
205 _leftOffset : 0,
206 _isPositionned : false,
207 _relativePosition : true,
208 _setPositionTop : 0,
209 _setPositionLeft : 0,
210 _bodyAppend : false,
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,
222 /* afterClose : called when the close function is executed */
223 _afterClose : Prototype.emptyFunction,
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 () {
234 /* Create the DateFormatter */
235 this._df = new DatePickerFormatter(this._dateFormat[0], this._dateFormat[1]);
237 /* check if value in field is proper, if not set to today */
238 this._current_date = $F(this._relative);
240 if (! this._df.match(this._current_date)) {
242 this._current_date = this._df.current_date();
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 },
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;
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 });
315 // this._div.innerHTML = '<table><thead><tr><th width="10px" id="'+ this._id_datepicker_prev +'" style="cursor: pointer;"> << </th><th id="'+ this._id_datepicker_hdr +'" colspan="5"></th><th width="10px" id="'+ this._id_datepicker_next +'" style="cursor: pointer;"> >> </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>';
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 }
330 Event.observe(datepickeropener,'click', this.click.bindAsEventListener(this), false);
332 this.insertAfter($(this._relative).parentNode,datepickeropener,$(this._relative));
333 /* End Build the datepicker icon */
335 /* finally declare the event listener on input field */
336 //Event.observe(this._relative, 'click', this.click.bindAsEventListener(this), false);
338 /* need to append on body when doc is loaded for IE */
339 document.observe('dom:loaded', this.load.bindAsEventListener(this), false);
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 },
355 /**
356 * load : called when document is fully-loaded to append datepicker
357 * to main object.
358 */
359 load : function () {
361 /* if externalControl defined set the observer on it */
362 if (this._externalControl)
363 Event.observe(this._externalControl, 'click', this.click.bindAsEventListener(this), false);
365 /* append to page */
366 if (this._relativeAppend) {
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 {
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();
392 /* set the close locale content */
393 $(this._id_datepicker_ftr).innerHTML = this.getLocaleClose();
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);
402 },
404 insertAfter : function(parent, node, referenceNode) {
405 parent.insertBefore(node, referenceNode.nextSibling);
406 },
408 /* hack for buggy form elements layering in IE */
409 _wrap_in_iframe : function ( content ) {
410 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;
411 },
413 /**
414 * visible : return the visibility status of the datepicker.
415 */
416 visible : function () {
417 return $(this._id_datepicker).visible();
418 },
420 /**
421 * click : called when input element is clicked
422 */
423 click : function () {
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 }
443 /* eval the clickCallback function */
444 eval(this._clickCallback());
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 },
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 },
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 },
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 },
524 /**
525 * _buildCalendar : draw the days array for current date
526 */
527 _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 ) {};
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 );
546 /* generate the content of days */
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 ];
558 /* set date at beginning of month to display */
559 var d = new Date(this._current_year, this._current_mon, 1, 12);
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;
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 }
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++;
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 }
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');
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('-');
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"];
632 /* on onclick : rebuild date value from id of current cell */
633 var _curDate = new Date();
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);
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 },
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 },
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 },
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 },
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 },
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 },
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 },
769 _redrawCalendar : function () {
770 this._setLocaleHdr();
771 this._buildCalendar();
772 },
774 _setLocaleHdr : function () {
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]);
780 /* prev link */
781 var a_prev = this._prevMonthYear();
782 $(this._id_datepicker_prev).setAttribute('title', this.getMonthLocale(a_prev[0]) +' '+ a_prev[1]);
784 /* next link */
785 var a_next = this._nextMonthYear();
786 $(this._id_datepicker_next).setAttribute('title', this.getMonthLocale(a_next[0]) +' '+ a_next[1]);
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]);
792 /* header */
793 $(this._id_datepicker_hdr).update(' '+ this.getMonthLocale(this._current_mon) +' '+ this._current_year +' ');
794 }
795 };