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