commit extjs-2.2.1
[extjs.git] / source / widgets / DatePicker.js
1 /*\r
2  * Ext JS Library 2.2.1\r
3  * Copyright(c) 2006-2009, Ext JS, LLC.\r
4  * licensing@extjs.com\r
5  * \r
6  * http://extjs.com/license\r
7  */\r
8 \r
9 /**\r
10  * @class Ext.DatePicker\r
11  * @extends Ext.Component\r
12  * Simple date picker class.\r
13  * @constructor\r
14  * Create a new DatePicker\r
15  * @param {Object} config The config object\r
16  */\r
17 Ext.DatePicker = Ext.extend(Ext.Component, {\r
18     /**\r
19      * @cfg {String} todayText\r
20      * The text to display on the button that selects the current date (defaults to "Today")\r
21      */\r
22     todayText : "Today",\r
23     /**\r
24      * @cfg {String} okText\r
25      * The text to display on the ok button\r
26      */\r
27     okText : " OK ", //   to give the user extra clicking room\r
28     /**\r
29      * @cfg {String} cancelText\r
30      * The text to display on the cancel button\r
31      */\r
32     cancelText : "Cancel",\r
33     /**\r
34      * @cfg {String} todayTip\r
35      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")\r
36      */\r
37     todayTip : "{0} (Spacebar)",\r
38     /**\r
39      * @cfg {String} minText\r
40      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")\r
41      */\r
42     minText : "This date is before the minimum date",\r
43     /**\r
44      * @cfg {String} maxText\r
45      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")\r
46      */\r
47     maxText : "This date is after the maximum date",\r
48     /**\r
49      * @cfg {String} format\r
50      * The default date format string which can be overriden for localization support.  The format must be\r
51      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').\r
52      */\r
53     format : "m/d/y",\r
54     /**\r
55      * @cfg {String} disabledDaysText\r
56      * The tooltip to display when the date falls on a disabled day (defaults to "Disabled")\r
57      */\r
58     disabledDaysText : "Disabled",\r
59     /**\r
60      * @cfg {String} disabledDatesText\r
61      * The tooltip text to display when the date falls on a disabled date (defaults to "Disabled")\r
62      */\r
63     disabledDatesText : "Disabled",\r
64     /**\r
65      * @cfg {Boolean} constrainToViewport\r
66      * <b>Deprecated</b> (not currently used). True to constrain the date picker to the viewport (defaults to true)\r
67      */\r
68     constrainToViewport : true,\r
69     /**\r
70      * @cfg {Array} monthNames\r
71      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)\r
72      */\r
73     monthNames : Date.monthNames,\r
74     /**\r
75      * @cfg {Array} dayNames\r
76      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)\r
77      */\r
78     dayNames : Date.dayNames,\r
79     /**\r
80      * @cfg {String} nextText\r
81      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')\r
82      */\r
83     nextText: 'Next Month (Control+Right)',\r
84     /**\r
85      * @cfg {String} prevText\r
86      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')\r
87      */\r
88     prevText: 'Previous Month (Control+Left)',\r
89     /**\r
90      * @cfg {String} monthYearText\r
91      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')\r
92      */\r
93     monthYearText: 'Choose a month (Control+Up/Down to move years)',\r
94     /**\r
95      * @cfg {Number} startDay\r
96      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)\r
97      */\r
98     startDay : 0,\r
99     /**\r
100      * @cfg {Boolean} showToday\r
101      * False to hide the footer area containing the Today button and disable the keyboard handler for spacebar\r
102      * that selects the current date (defaults to true).\r
103      */\r
104     showToday : true,\r
105     /**\r
106      * @cfg {Date} minDate\r
107      * Minimum allowable date (JavaScript date object, defaults to null)\r
108      */\r
109     /**\r
110      * @cfg {Date} maxDate\r
111      * Maximum allowable date (JavaScript date object, defaults to null)\r
112      */\r
113     /**\r
114      * @cfg {Array} disabledDays\r
115      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).\r
116      */\r
117     /**\r
118      * @cfg {RegExp} disabledDatesRE\r
119      * JavaScript regular expression used to disable a pattern of dates (defaults to null).  The {@link #disabledDates}\r
120      * config will generate this regex internally, but if you specify disabledDatesRE it will take precedence over the \r
121      * disabledDates value.\r
122      */\r
123     /**\r
124      * @cfg {Array} disabledDates\r
125      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular\r
126      * expression so they are very powerful. Some examples:\r
127      * <ul>\r
128      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>\r
129      * <li>["03/08", "09/16"] would disable those days for every year</li>\r
130      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>\r
131      * <li>["03/../2006"] would disable every day in March 2006</li>\r
132      * <li>["^03"] would disable every day in every March</li>\r
133      * </ul>\r
134      * Note that the format of the dates included in the array should exactly match the {@link #format} config.\r
135      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to\r
136      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].\r
137      */\r
138 \r
139     // private\r
140     initComponent : function(){\r
141         Ext.DatePicker.superclass.initComponent.call(this);\r
142 \r
143         this.value = this.value ?\r
144                  this.value.clearTime() : new Date().clearTime();\r
145 \r
146         this.addEvents(\r
147             /**\r
148              * @event select\r
149              * Fires when a date is selected\r
150              * @param {DatePicker} this\r
151              * @param {Date} date The selected date\r
152              */\r
153             'select'\r
154         );\r
155 \r
156         if(this.handler){\r
157             this.on("select", this.handler,  this.scope || this);\r
158         }\r
159 \r
160         this.initDisabledDays();\r
161     },\r
162 \r
163     // private\r
164     initDisabledDays : function(){\r
165         if(!this.disabledDatesRE && this.disabledDates){\r
166             var dd = this.disabledDates;\r
167             var re = "(?:";\r
168             for(var i = 0; i < dd.length; i++){\r
169                 re += dd[i];\r
170                 if(i != dd.length-1) re += "|";\r
171             }\r
172             this.disabledDatesRE = new RegExp(re + ")");\r
173         }\r
174     },\r
175     \r
176     /**\r
177      * Replaces any existing disabled dates with new values and refreshes the DatePicker.\r
178      * @param {Array/RegExp} disabledDates An array of date strings (see the {@link #disabledDates} config\r
179      * for details on supported values), or a JavaScript regular expression used to disable a pattern of dates.\r
180      */\r
181     setDisabledDates : function(dd){\r
182         if(Ext.isArray(dd)){\r
183             this.disabledDates = dd;\r
184             this.disabledDatesRE = null;\r
185         }else{\r
186             this.disabledDatesRE = dd;\r
187         }\r
188         this.initDisabledDays();\r
189         this.update(this.value, true);\r
190     },\r
191     \r
192     /**\r
193      * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.\r
194      * @param {Array} disabledDays An array of disabled day indexes. See the {@link #disabledDays} config\r
195      * for details on supported values.\r
196      */\r
197     setDisabledDays : function(dd){\r
198         this.disabledDays = dd;\r
199         this.update(this.value, true);\r
200     },\r
201     \r
202     /**\r
203      * Replaces any existing {@link #minDate} with the new value and refreshes the DatePicker.\r
204      * @param {Date} value The minimum date that can be selected\r
205      */\r
206     setMinDate : function(dt){\r
207         this.minDate = dt;\r
208         this.update(this.value, true);\r
209     },\r
210     \r
211     /**\r
212      * Replaces any existing {@link #maxDate} with the new value and refreshes the DatePicker.\r
213      * @param {Date} value The maximum date that can be selected\r
214      */\r
215     setMaxDate : function(dt){\r
216         this.maxDate = dt;\r
217         this.update(this.value, true);\r
218     },\r
219 \r
220     /**\r
221      * Sets the value of the date field\r
222      * @param {Date} value The date to set\r
223      */\r
224     setValue : function(value){\r
225         var old = this.value;\r
226         this.value = value.clearTime(true);\r
227         if(this.el){\r
228             this.update(this.value);\r
229         }\r
230     },\r
231 \r
232     /**\r
233      * Gets the current selected value of the date field\r
234      * @return {Date} The selected date\r
235      */\r
236     getValue : function(){\r
237         return this.value;\r
238     },\r
239 \r
240     // private\r
241     focus : function(){\r
242         if(this.el){\r
243             this.update(this.activeDate);\r
244         }\r
245     },\r
246 \r
247     // private\r
248     onRender : function(container, position){\r
249         var m = [\r
250              '<table cellspacing="0">',\r
251                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',\r
252                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];\r
253         var dn = this.dayNames;\r
254         for(var i = 0; i < 7; i++){\r
255             var d = this.startDay+i;\r
256             if(d > 6){\r
257                 d = d-7;\r
258             }\r
259             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");\r
260         }\r
261         m[m.length] = "</tr></thead><tbody><tr>";\r
262         for(var i = 0; i < 42; i++) {\r
263             if(i % 7 == 0 && i != 0){\r
264                 m[m.length] = "</tr><tr>";\r
265             }\r
266             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';\r
267         }\r
268         m.push('</tr></tbody></table></td></tr>', \r
269                 this.showToday ? '<tr><td colspan="3" class="x-date-bottom" align="center"></td></tr>' : '', \r
270                 '</table><div class="x-date-mp"></div>');\r
271 \r
272         var el = document.createElement("div");\r
273         el.className = "x-date-picker";\r
274         el.innerHTML = m.join("");\r
275 \r
276         container.dom.insertBefore(el, position);\r
277 \r
278         this.el = Ext.get(el);\r
279         this.eventEl = Ext.get(el.firstChild);\r
280 \r
281         this.leftClickRpt = new Ext.util.ClickRepeater(this.el.child("td.x-date-left a"), {\r
282             handler: this.showPrevMonth,\r
283             scope: this,\r
284             preventDefault:true,\r
285             stopDefault:true\r
286         });\r
287 \r
288         this.rightClickRpt = new Ext.util.ClickRepeater(this.el.child("td.x-date-right a"), {\r
289             handler: this.showNextMonth,\r
290             scope: this,\r
291             preventDefault:true,\r
292             stopDefault:true\r
293         });\r
294 \r
295         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);\r
296 \r
297         this.monthPicker = this.el.down('div.x-date-mp');\r
298         this.monthPicker.enableDisplayMode('block');\r
299         \r
300         var kn = new Ext.KeyNav(this.eventEl, {\r
301             "left" : function(e){\r
302                 e.ctrlKey ?\r
303                     this.showPrevMonth() :\r
304                     this.update(this.activeDate.add("d", -1));\r
305             },\r
306 \r
307             "right" : function(e){\r
308                 e.ctrlKey ?\r
309                     this.showNextMonth() :\r
310                     this.update(this.activeDate.add("d", 1));\r
311             },\r
312 \r
313             "up" : function(e){\r
314                 e.ctrlKey ?\r
315                     this.showNextYear() :\r
316                     this.update(this.activeDate.add("d", -7));\r
317             },\r
318 \r
319             "down" : function(e){\r
320                 e.ctrlKey ?\r
321                     this.showPrevYear() :\r
322                     this.update(this.activeDate.add("d", 7));\r
323             },\r
324 \r
325             "pageUp" : function(e){\r
326                 this.showNextMonth();\r
327             },\r
328 \r
329             "pageDown" : function(e){\r
330                 this.showPrevMonth();\r
331             },\r
332 \r
333             "enter" : function(e){\r
334                 e.stopPropagation();\r
335                 return true;\r
336             },\r
337 \r
338             scope : this\r
339         });\r
340 \r
341         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});\r
342 \r
343         this.el.unselectable();\r
344         \r
345         this.cells = this.el.select("table.x-date-inner tbody td");\r
346         this.textNodes = this.el.query("table.x-date-inner tbody span");\r
347 \r
348         this.mbtn = new Ext.Button({\r
349             text: "&#160;",\r
350             tooltip: this.monthYearText,\r
351             renderTo: this.el.child("td.x-date-middle", true)\r
352         });\r
353 \r
354         this.mbtn.on('click', this.showMonthPicker, this);\r
355         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");\r
356 \r
357         if(this.showToday){\r
358             this.todayKeyListener = this.eventEl.addKeyListener(Ext.EventObject.SPACE, this.selectToday,  this);\r
359             var today = (new Date()).dateFormat(this.format);\r
360             this.todayBtn = new Ext.Button({\r
361                 renderTo: this.el.child("td.x-date-bottom", true),\r
362                 text: String.format(this.todayText, today),\r
363                 tooltip: String.format(this.todayTip, today),\r
364                 handler: this.selectToday,\r
365                 scope: this\r
366             });\r
367         }\r
368         \r
369         if(Ext.isIE){\r
370             this.el.repaint();\r
371         }\r
372         this.update(this.value);\r
373     },\r
374 \r
375     // private\r
376     createMonthPicker : function(){\r
377         if(!this.monthPicker.dom.firstChild){\r
378             var buf = ['<table border="0" cellspacing="0">'];\r
379             for(var i = 0; i < 6; i++){\r
380                 buf.push(\r
381                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',\r
382                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',\r
383                     i == 0 ?\r
384                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :\r
385                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'\r
386                 );\r
387             }\r
388             buf.push(\r
389                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',\r
390                     this.okText,\r
391                     '</button><button type="button" class="x-date-mp-cancel">',\r
392                     this.cancelText,\r
393                     '</button></td></tr>',\r
394                 '</table>'\r
395             );\r
396             this.monthPicker.update(buf.join(''));\r
397             this.monthPicker.on('click', this.onMonthClick, this);\r
398             this.monthPicker.on('dblclick', this.onMonthDblClick, this);\r
399 \r
400             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');\r
401             this.mpYears = this.monthPicker.select('td.x-date-mp-year');\r
402 \r
403             this.mpMonths.each(function(m, a, i){\r
404                 i += 1;\r
405                 if((i%2) == 0){\r
406                     m.dom.xmonth = 5 + Math.round(i * .5);\r
407                 }else{\r
408                     m.dom.xmonth = Math.round((i-1) * .5);\r
409                 }\r
410             });\r
411         }\r
412     },\r
413 \r
414     // private\r
415     showMonthPicker : function(){\r
416         this.createMonthPicker();\r
417         var size = this.el.getSize();\r
418         this.monthPicker.setSize(size);\r
419         this.monthPicker.child('table').setSize(size);\r
420 \r
421         this.mpSelMonth = (this.activeDate || this.value).getMonth();\r
422         this.updateMPMonth(this.mpSelMonth);\r
423         this.mpSelYear = (this.activeDate || this.value).getFullYear();\r
424         this.updateMPYear(this.mpSelYear);\r
425 \r
426         this.monthPicker.slideIn('t', {duration:.2});\r
427     },\r
428 \r
429     // private\r
430     updateMPYear : function(y){\r
431         this.mpyear = y;\r
432         var ys = this.mpYears.elements;\r
433         for(var i = 1; i <= 10; i++){\r
434             var td = ys[i-1], y2;\r
435             if((i%2) == 0){\r
436                 y2 = y + Math.round(i * .5);\r
437                 td.firstChild.innerHTML = y2;\r
438                 td.xyear = y2;\r
439             }else{\r
440                 y2 = y - (5-Math.round(i * .5));\r
441                 td.firstChild.innerHTML = y2;\r
442                 td.xyear = y2;\r
443             }\r
444             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');\r
445         }\r
446     },\r
447 \r
448     // private\r
449     updateMPMonth : function(sm){\r
450         this.mpMonths.each(function(m, a, i){\r
451             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');\r
452         });\r
453     },\r
454 \r
455     // private\r
456     selectMPMonth: function(m){\r
457         \r
458     },\r
459 \r
460     // private\r
461     onMonthClick : function(e, t){\r
462         e.stopEvent();\r
463         var el = new Ext.Element(t), pn;\r
464         if(el.is('button.x-date-mp-cancel')){\r
465             this.hideMonthPicker();\r
466         }\r
467         else if(el.is('button.x-date-mp-ok')){\r
468             var d = new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate());\r
469             if(d.getMonth() != this.mpSelMonth){\r
470                 // "fix" the JS rolling date conversion if needed\r
471                 d = new Date(this.mpSelYear, this.mpSelMonth, 1).getLastDateOfMonth();\r
472             }\r
473             this.update(d);\r
474             this.hideMonthPicker();\r
475         }\r
476         else if(pn = el.up('td.x-date-mp-month', 2)){\r
477             this.mpMonths.removeClass('x-date-mp-sel');\r
478             pn.addClass('x-date-mp-sel');\r
479             this.mpSelMonth = pn.dom.xmonth;\r
480         }\r
481         else if(pn = el.up('td.x-date-mp-year', 2)){\r
482             this.mpYears.removeClass('x-date-mp-sel');\r
483             pn.addClass('x-date-mp-sel');\r
484             this.mpSelYear = pn.dom.xyear;\r
485         }\r
486         else if(el.is('a.x-date-mp-prev')){\r
487             this.updateMPYear(this.mpyear-10);\r
488         }\r
489         else if(el.is('a.x-date-mp-next')){\r
490             this.updateMPYear(this.mpyear+10);\r
491         }\r
492     },\r
493 \r
494     // private\r
495     onMonthDblClick : function(e, t){\r
496         e.stopEvent();\r
497         var el = new Ext.Element(t), pn;\r
498         if(pn = el.up('td.x-date-mp-month', 2)){\r
499             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));\r
500             this.hideMonthPicker();\r
501         }\r
502         else if(pn = el.up('td.x-date-mp-year', 2)){\r
503             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));\r
504             this.hideMonthPicker();\r
505         }\r
506     },\r
507 \r
508     // private\r
509     hideMonthPicker : function(disableAnim){\r
510         if(this.monthPicker){\r
511             if(disableAnim === true){\r
512                 this.monthPicker.hide();\r
513             }else{\r
514                 this.monthPicker.slideOut('t', {duration:.2});\r
515             }\r
516         }\r
517     },\r
518 \r
519     // private\r
520     showPrevMonth : function(e){\r
521         this.update(this.activeDate.add("mo", -1));\r
522     },\r
523 \r
524     // private\r
525     showNextMonth : function(e){\r
526         this.update(this.activeDate.add("mo", 1));\r
527     },\r
528 \r
529     // private\r
530     showPrevYear : function(){\r
531         this.update(this.activeDate.add("y", -1));\r
532     },\r
533 \r
534     // private\r
535     showNextYear : function(){\r
536         this.update(this.activeDate.add("y", 1));\r
537     },\r
538 \r
539     // private\r
540     handleMouseWheel : function(e){\r
541         var delta = e.getWheelDelta();\r
542         if(delta > 0){\r
543             this.showPrevMonth();\r
544             e.stopEvent();\r
545         } else if(delta < 0){\r
546             this.showNextMonth();\r
547             e.stopEvent();\r
548         }\r
549     },\r
550 \r
551     // private\r
552     handleDateClick : function(e, t){\r
553         e.stopEvent();\r
554         if(t.dateValue && !Ext.fly(t.parentNode).hasClass("x-date-disabled")){\r
555             this.setValue(new Date(t.dateValue));\r
556             this.fireEvent("select", this, this.value);\r
557         }\r
558     },\r
559 \r
560     // private\r
561     selectToday : function(){\r
562         if(this.todayBtn && !this.todayBtn.disabled){\r
563                 this.setValue(new Date().clearTime());\r
564                 this.fireEvent("select", this, this.value);\r
565         }\r
566     },\r
567 \r
568     // private\r
569     update : function(date, forceRefresh){\r
570         var vd = this.activeDate;\r
571         this.activeDate = date;\r
572         if(!forceRefresh && vd && this.el){\r
573             var t = date.getTime();\r
574             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){\r
575                 this.cells.removeClass("x-date-selected");\r
576                 this.cells.each(function(c){\r
577                    if(c.dom.firstChild.dateValue == t){\r
578                        c.addClass("x-date-selected");\r
579                        setTimeout(function(){\r
580                             try{c.dom.firstChild.focus();}catch(e){}\r
581                        }, 50);\r
582                        return false;\r
583                    }\r
584                 });\r
585                 return;\r
586             }\r
587         }\r
588         var days = date.getDaysInMonth();\r
589         var firstOfMonth = date.getFirstDateOfMonth();\r
590         var startingPos = firstOfMonth.getDay()-this.startDay;\r
591 \r
592         if(startingPos <= this.startDay){\r
593             startingPos += 7;\r
594         }\r
595 \r
596         var pm = date.add("mo", -1);\r
597         var prevStart = pm.getDaysInMonth()-startingPos;\r
598 \r
599         var cells = this.cells.elements;\r
600         var textEls = this.textNodes;\r
601         days += startingPos;\r
602 \r
603         // convert everything to numbers so it's fast\r
604         var day = 86400000;\r
605         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();\r
606         var today = new Date().clearTime().getTime();\r
607         var sel = date.clearTime().getTime();\r
608         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;\r
609         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;\r
610         var ddMatch = this.disabledDatesRE;\r
611         var ddText = this.disabledDatesText;\r
612         var ddays = this.disabledDays ? this.disabledDays.join("") : false;\r
613         var ddaysText = this.disabledDaysText;\r
614         var format = this.format;\r
615         \r
616         if(this.showToday){\r
617             var td = new Date().clearTime();\r
618             var disable = (td < min || td > max || \r
619                 (ddMatch && format && ddMatch.test(td.dateFormat(format))) || \r
620                 (ddays && ddays.indexOf(td.getDay()) != -1));\r
621                         \r
622             this.todayBtn.setDisabled(disable);\r
623             this.todayKeyListener[disable ? 'disable' : 'enable']();\r
624         }\r
625 \r
626         var setCellClass = function(cal, cell){\r
627             cell.title = "";\r
628             var t = d.getTime();\r
629             cell.firstChild.dateValue = t;\r
630             if(t == today){\r
631                 cell.className += " x-date-today";\r
632                 cell.title = cal.todayText;\r
633             }\r
634             if(t == sel){\r
635                 cell.className += " x-date-selected";\r
636                 setTimeout(function(){\r
637                     try{cell.firstChild.focus();}catch(e){}\r
638                 }, 50);\r
639             }\r
640             // disabling\r
641             if(t < min) {\r
642                 cell.className = " x-date-disabled";\r
643                 cell.title = cal.minText;\r
644                 return;\r
645             }\r
646             if(t > max) {\r
647                 cell.className = " x-date-disabled";\r
648                 cell.title = cal.maxText;\r
649                 return;\r
650             }\r
651             if(ddays){\r
652                 if(ddays.indexOf(d.getDay()) != -1){\r
653                     cell.title = ddaysText;\r
654                     cell.className = " x-date-disabled";\r
655                 }\r
656             }\r
657             if(ddMatch && format){\r
658                 var fvalue = d.dateFormat(format);\r
659                 if(ddMatch.test(fvalue)){\r
660                     cell.title = ddText.replace("%0", fvalue);\r
661                     cell.className = " x-date-disabled";\r
662                 }\r
663             }\r
664         };\r
665 \r
666         var i = 0;\r
667         for(; i < startingPos; i++) {\r
668             textEls[i].innerHTML = (++prevStart);\r
669             d.setDate(d.getDate()+1);\r
670             cells[i].className = "x-date-prevday";\r
671             setCellClass(this, cells[i]);\r
672         }\r
673         for(; i < days; i++){\r
674             var intDay = i - startingPos + 1;\r
675             textEls[i].innerHTML = (intDay);\r
676             d.setDate(d.getDate()+1);\r
677             cells[i].className = "x-date-active";\r
678             setCellClass(this, cells[i]);\r
679         }\r
680         var extraDays = 0;\r
681         for(; i < 42; i++) {\r
682              textEls[i].innerHTML = (++extraDays);\r
683              d.setDate(d.getDate()+1);\r
684              cells[i].className = "x-date-nextday";\r
685              setCellClass(this, cells[i]);\r
686         }\r
687 \r
688         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());\r
689 \r
690         if(!this.internalRender){\r
691             var main = this.el.dom.firstChild;\r
692             var w = main.offsetWidth;\r
693             this.el.setWidth(w + this.el.getBorderWidth("lr"));\r
694             Ext.fly(main).setWidth(w);\r
695             this.internalRender = true;\r
696             // opera does not respect the auto grow header center column\r
697             // then, after it gets a width opera refuses to recalculate\r
698             // without a second pass\r
699             if(Ext.isOpera && !this.secondPass){\r
700                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";\r
701                 this.secondPass = true;\r
702                 this.update.defer(10, this, [date]);\r
703             }\r
704         }\r
705     },\r
706 \r
707     // private\r
708     beforeDestroy : function() {\r
709         if(this.rendered){\r
710             Ext.destroy(\r
711                 this.leftClickRpt,\r
712                 this.rightClickRpt,\r
713                 this.monthPicker,\r
714                 this.eventEl,\r
715                 this.mbtn,\r
716                 this.todayBtn\r
717             );\r
718         }\r
719     }\r
720 \r
721     /**\r
722      * @cfg {String} autoEl @hide\r
723      */\r
724 });\r
725 Ext.reg('datepicker', Ext.DatePicker);