]> git.proxmox.com Git - extjs.git/blame - extjs/classic/classic/src/picker/Date.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / src / picker / Date.js
CommitLineData
6527f429
DM
1/**\r
2 * A date picker. This class is used by the Ext.form.field.Date field to allow browsing and selection of valid\r
3 * dates in a popup next to the field, but may also be used with other components.\r
4 *\r
5 * Typically you will need to implement a handler function to be notified when the user chooses a date from the picker;\r
6 * you can register the handler using the {@link #select} event, or by implementing the {@link #handler} method.\r
7 *\r
8 * By default the user will be allowed to pick any date; this can be changed by using the {@link #minDate},\r
9 * {@link #maxDate}, {@link #disabledDays}, {@link #disabledDatesRE}, and/or {@link #disabledDates} configs.\r
10 *\r
11 * All the string values documented below may be overridden by including an Ext locale file in your page.\r
12 *\r
13 * @example\r
14 * Ext.create('Ext.panel.Panel', {\r
15 * title: 'Choose a future date:',\r
16 * width: 200,\r
17 * bodyPadding: 10,\r
18 * renderTo: Ext.getBody(),\r
19 * items: [{\r
20 * xtype: 'datepicker',\r
21 * minDate: new Date(),\r
22 * handler: function(picker, date) {\r
23 * // do something with the selected date\r
24 * }\r
25 * }]\r
26 * });\r
27 */\r
28Ext.define('Ext.picker.Date', {\r
29 extend: 'Ext.Component',\r
30 requires: [\r
31 'Ext.XTemplate',\r
32 'Ext.button.Button',\r
33 'Ext.button.Split',\r
34 'Ext.util.ClickRepeater',\r
35 'Ext.util.KeyNav',\r
36 'Ext.fx.Manager',\r
37 'Ext.picker.Month'\r
38 ],\r
39 alias: 'widget.datepicker',\r
40 alternateClassName: 'Ext.DatePicker',\r
41 \r
42 //<locale>\r
43 /**\r
44 * @cfg {String} todayText\r
45 * The text to display on the button that selects the current date\r
46 */\r
47 todayText: 'Today',\r
48 //</locale>\r
49 \r
50 //<locale>\r
51 /**\r
52 * @cfg {String} ariaTitle\r
53 * The text to display for the aria title\r
54 */\r
55 ariaTitle: 'Date Picker: {0}',\r
56 //</locale>\r
57 \r
58 //<locale>\r
59 /**\r
60 * @cfg {String} ariaTitleDateFormat\r
61 * The date format to display for the current value in the {@link #ariaTitle}\r
62 */\r
63 ariaTitleDateFormat: 'F d',\r
64 //</locale>\r
65\r
66 /**\r
67 * @cfg {Function} handler\r
68 * Optional. A function that will handle the select event of this picker. The handler is passed the following\r
69 * parameters:\r
70 *\r
71 * - `picker` : Ext.picker.Date\r
72 *\r
73 * This Date picker.\r
74 *\r
75 * - `date` : Date\r
76 *\r
77 * The selected date.\r
78 */\r
79\r
80 /**\r
81 * @cfg {Object} scope\r
82 * The scope (`this` reference) in which the `{@link #handler}` function will be called.\r
83 *\r
84 * Defaults to this DatePicker instance.\r
85 */\r
86\r
87 //<locale>\r
88 /**\r
89 * @cfg {String} todayTip\r
90 * A string used to format the message for displaying in a tooltip over the button that selects the current date.\r
91 * The `{0}` token in string is replaced by today's date.\r
92 */\r
93 todayTip: '{0} (Spacebar)',\r
94 //</locale>\r
95\r
96 //<locale>\r
97 /**\r
98 * @cfg {String} minText\r
99 * The error text to display if the minDate validation fails.\r
100 */\r
101 minText: 'This date is before the minimum date',\r
102 //</locale>\r
103 \r
104 //<locale>\r
105 /**\r
106 * @cfg {String} ariaMinText The text that will be announced by Assistive Technologies\r
107 * such as screen readers when user is navigating to the cell which date is less than\r
108 * {@link #minDate}.\r
109 */\r
110 ariaMinText: "This date is before the minimum date",\r
111 //</locale>\r
112\r
113 //<locale>\r
114 /**\r
115 * @cfg {String} maxText\r
116 * The error text to display if the maxDate validation fails.\r
117 */\r
118 maxText: 'This date is after the maximum date',\r
119 //</locale>\r
120 \r
121 //<locale>\r
122 /**\r
123 * @cfg {String} ariaMaxText The text that will be announced by Assistive Technologies\r
124 * such as screen readers when user is navigating to the cell which date is later than\r
125 * {@link #maxDate}.\r
126 */\r
127 ariaMaxText: "This date is after the maximum date",\r
128 //</locale>\r
129\r
130 /**\r
131 * @cfg {String} format\r
132 * The default date format string which can be overriden for localization support. The format must be valid\r
133 * according to {@link Ext.Date#parse} (defaults to {@link Ext.Date#defaultFormat}).\r
134 */\r
135\r
136 //<locale>\r
137 /**\r
138 * @cfg {String} disabledDaysText\r
139 * The tooltip to display when the date falls on a disabled day.\r
140 */\r
141 disabledDaysText: 'Disabled',\r
142 //</locale>\r
143 \r
144 //<locale>\r
145 /**\r
146 * @cfg {String} ariaDisabledDaysText The text that Assistive Technologies such as screen readers\r
147 * will announce when the date falls on a disabled day of week.\r
148 */\r
149 ariaDisabledDaysText: "This day of week is disabled",\r
150 //</locale>\r
151 \r
152 //<locale>\r
153 /**\r
154 * @cfg {String} disabledDatesText\r
155 * The tooltip text to display when the date falls on a disabled date.\r
156 */\r
157 disabledDatesText: 'Disabled',\r
158 //</locale>\r
159\r
160 //<locale>\r
161 /**\r
162 * @cfg {String} ariaDisabledDatesText The text that Assistive Technologies such as screen readers\r
163 * will announce when the date falls on a disabled date.\r
164 */\r
165 ariaDisabledDatesText: "This date is disabled",\r
166 \r
167 //</locale>\r
168 /**\r
169 * @cfg {String[]} monthNames\r
170 * An array of textual month names which can be overriden for localization support (defaults to Ext.Date.monthNames)\r
171 * @deprecated This config is deprecated. In future the month names will be retrieved from {@link Ext.Date}\r
172 */\r
173\r
174 /**\r
175 * @cfg {String[]} dayNames\r
176 * An array of textual day names which can be overriden for localization support (defaults to Ext.Date.dayNames)\r
177 * @deprecated This config is deprecated. In future the day names will be retrieved from {@link Ext.Date}\r
178 */\r
179\r
180 //<locale>\r
181 /**\r
182 * @cfg {String} nextText\r
183 * The next month navigation button tooltip\r
184 */\r
185 nextText: 'Next Month (Control+Right)',\r
186 //</locale>\r
187\r
188 //<locale>\r
189 /**\r
190 * @cfg {String} prevText\r
191 * The previous month navigation button tooltip\r
192 */\r
193 prevText: 'Previous Month (Control+Left)',\r
194 //</locale>\r
195\r
196 //<locale>\r
197 /**\r
198 * @cfg {String} monthYearText\r
199 * The header month selector tooltip\r
200 */\r
201 monthYearText: 'Choose a month (Control+Up/Down to move years)',\r
202 //</locale>\r
203 \r
204 //<locale>\r
205 /**\r
206 * @cfg {String} monthYearFormat\r
207 * The date format for the header month\r
208 */\r
209 monthYearFormat: 'F Y',\r
210 //</locale>\r
211\r
212 //<locale>\r
213 /**\r
214 * @cfg {Number} [startDay=undefined]\r
215 * Day index at which the week should begin, 0-based.\r
216 *\r
217 * Defaults to `0` (Sunday).\r
218 */\r
219 startDay: 0,\r
220 //</locale>\r
221\r
222 //<locale>\r
223 /**\r
224 * @cfg {Boolean} showToday\r
225 * False to hide the footer area containing the Today button and disable the keyboard handler for spacebar that\r
226 * selects the current date.\r
227 */\r
228 showToday: true,\r
229 //</locale>\r
230\r
231 /**\r
232 * @cfg {Date} [minDate=null]\r
233 * Minimum allowable date (JavaScript date object)\r
234 */\r
235\r
236 /**\r
237 * @cfg {Date} [maxDate=null]\r
238 * Maximum allowable date (JavaScript date object)\r
239 */\r
240\r
241 /**\r
242 * @cfg {Number[]} [disabledDays=null]\r
243 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday.\r
244 */\r
245\r
246 /**\r
247 * @cfg {RegExp} [disabledDatesRE=null]\r
248 * JavaScript regular expression used to disable a pattern of dates. The {@link #disabledDates}\r
249 * config will generate this regex internally, but if you specify disabledDatesRE it will take precedence over the\r
250 * disabledDates value.\r
251 */\r
252\r
253 /**\r
254 * @cfg {String[]} disabledDates\r
255 * An array of 'dates' to disable, as strings. These strings will be used to build a dynamic regular expression so\r
256 * they are very powerful. Some examples:\r
257 *\r
258 * - ['03/08/2003', '09/16/2003'] would disable those exact dates\r
259 * - ['03/08', '09/16'] would disable those days for every year\r
260 * - ['^03/08'] would only match the beginning (useful if you are using short years)\r
261 * - ['03/../2006'] would disable every day in March 2006\r
262 * - ['^03'] would disable every day in every March\r
263 *\r
264 * Note that the format of the dates included in the array should exactly match the {@link #format} config. In order\r
265 * to support regular expressions, if you are using a date format that has '.' in it, you will have to escape the\r
266 * dot when restricting dates. For example: ['03\\.08\\.03'].\r
267 */\r
268\r
269 /**\r
270 * @cfg {Boolean} disableAnim\r
271 * True to disable animations when showing the month picker.\r
272 */\r
273 disableAnim: false,\r
274\r
275 /**\r
276 * @cfg {String} [baseCls='x-datepicker']\r
277 * The base CSS class to apply to this components element.\r
278 */\r
279 baseCls: Ext.baseCSSPrefix + 'datepicker',\r
280\r
281 /**\r
282 * @cfg {String} [selectedCls='x-datepicker-selected']\r
283 * The class to apply to the selected cell.\r
284 */\r
285\r
286 /**\r
287 * @cfg {String} [disabledCellCls='x-datepicker-disabled']\r
288 * The class to apply to disabled cells.\r
289 */\r
290\r
291 //<locale>\r
292 /**\r
293 * @cfg {String} longDayFormat\r
294 * The format for displaying a date in a longer format.\r
295 */\r
296 longDayFormat: 'F d, Y',\r
297 //</locale>\r
298\r
299 /**\r
300 * @cfg {Object} keyNavConfig\r
301 * Specifies optional custom key event handlers for the {@link Ext.util.KeyNav} attached to this date picker. Must\r
302 * conform to the config format recognized by the {@link Ext.util.KeyNav} constructor. Handlers specified in this\r
303 * object will replace default handlers of the same name.\r
304 */\r
305\r
306 /**\r
307 * @cfg {String}\r
308 * The {@link Ext.button.Button#ui} to use for the date picker's footer buttons.\r
309 */\r
310 footerButtonUI: 'default',\r
311\r
312 isDatePicker: true,\r
313 \r
314 ariaRole: 'region',\r
315 focusable: true,\r
316\r
317 childEls: [\r
318 'innerEl', 'eventEl', 'prevEl', 'nextEl', 'middleBtnEl', 'footerEl'\r
319 ],\r
320 \r
321 border: true,\r
322 \r
323 /**\r
324 * @cfg\r
325 * @inheritdoc\r
326 */\r
327 renderTpl: [\r
328 '<div id="{id}-innerEl" data-ref="innerEl" role="presentation">',\r
329 '<div class="{baseCls}-header">',\r
330 '<div id="{id}-prevEl" data-ref="prevEl" class="{baseCls}-prev {baseCls}-arrow" role="presentation" title="{prevText}"></div>',\r
331 '<div id="{id}-middleBtnEl" data-ref="middleBtnEl" class="{baseCls}-month" role="heading">{%this.renderMonthBtn(values, out)%}</div>',\r
332 '<div id="{id}-nextEl" data-ref="nextEl" class="{baseCls}-next {baseCls}-arrow" role="presentation" title="{nextText}"></div>',\r
333 '</div>',\r
334 '<table role="grid" id="{id}-eventEl" data-ref="eventEl" class="{baseCls}-inner" cellspacing="0" tabindex="0">',\r
335 '<thead>',\r
336 '<tr role="row">',\r
337 '<tpl for="dayNames">',\r
338 '<th role="columnheader" class="{parent.baseCls}-column-header" aria-label="{.}">',\r
339 '<div role="presentation" class="{parent.baseCls}-column-header-inner">{.:this.firstInitial}</div>',\r
340 '</th>',\r
341 '</tpl>',\r
342 '</tr>',\r
343 '</thead>',\r
344 '<tbody>',\r
345 '<tr role="row">',\r
346 '<tpl for="days">',\r
347 '{#:this.isEndOfWeek}',\r
348 '<td role="gridcell">',\r
349 '<div hidefocus="on" class="{parent.baseCls}-date"></div>',\r
350 '</td>',\r
351 '</tpl>',\r
352 '</tr>',\r
353 '</tbody>',\r
354 '</table>',\r
355 '<tpl if="showToday">',\r
356 '<div id="{id}-footerEl" data-ref="footerEl" role="presentation" class="{baseCls}-footer">{%this.renderTodayBtn(values, out)%}</div>',\r
357 '</tpl>',\r
358 // These elements are used with Assistive Technologies such as screen readers\r
359 '<div id="{id}-todayText" class="' + Ext.baseCSSPrefix + 'hidden-clip">{todayText}.</div>',\r
360 '<div id="{id}-ariaMinText" class="' + Ext.baseCSSPrefix + 'hidden-clip">{ariaMinText}.</div>',\r
361 '<div id="{id}-ariaMaxText" class="' + Ext.baseCSSPrefix + 'hidden-clip">{ariaMaxText}.</div>',\r
362 '<div id="{id}-ariaDisabledDaysText" class="' + Ext.baseCSSPrefix + 'hidden-clip">{ariaDisabledDaysText}.</div>',\r
363 '<div id="{id}-ariaDisabledDatesText" class="' + Ext.baseCSSPrefix + 'hidden-clip">{ariaDisabledDatesText}.</div>',\r
364 '</div>',\r
365 {\r
366 firstInitial: function(value) {\r
367 return Ext.picker.Date.prototype.getDayInitial(value);\r
368 },\r
369 isEndOfWeek: function(value) {\r
370 // convert from 1 based index to 0 based\r
371 // by decrementing value once.\r
372 value--;\r
373 var end = value % 7 === 0 && value !== 0;\r
374 return end ? '</tr><tr role="row">' : '';\r
375 },\r
376 renderTodayBtn: function(values, out) {\r
377 Ext.DomHelper.generateMarkup(values.$comp.todayBtn.getRenderTree(), out);\r
378 },\r
379 renderMonthBtn: function(values, out) {\r
380 Ext.DomHelper.generateMarkup(values.$comp.monthBtn.getRenderTree(), out);\r
381 }\r
382 }\r
383 ],\r
384\r
385 // Default value used to initialise each date in the DatePicker.\r
386 // __Note:__ 12 noon was chosen because it steers well clear of all DST timezone changes.\r
387 initHour: 12, // 24-hour format\r
388\r
389 numDays: 42,\r
390\r
391 /**\r
392 * @event select\r
393 * Fires when a date is selected\r
394 * @param {Ext.picker.Date} this DatePicker\r
395 * @param {Date} date The selected date\r
396 */\r
397\r
398 initComponent: function() {\r
399 var me = this,\r
400 clearTime = Ext.Date.clearTime;\r
401\r
402 me.selectedCls = me.baseCls + '-selected';\r
403 me.disabledCellCls = me.baseCls + '-disabled';\r
404 me.prevCls = me.baseCls + '-prevday';\r
405 me.activeCls = me.baseCls + '-active';\r
406 me.cellCls = me.baseCls + '-cell';\r
407 me.nextCls = me.baseCls + '-prevday';\r
408 me.todayCls = me.baseCls + '-today';\r
409 \r
410 \r
411 if (!me.format) {\r
412 me.format = Ext.Date.defaultFormat;\r
413 }\r
414 if (!me.dayNames) {\r
415 me.dayNames = Ext.Date.dayNames;\r
416 }\r
417 me.dayNames = me.dayNames.slice(me.startDay).concat(me.dayNames.slice(0, me.startDay));\r
418\r
419 me.callParent();\r
420\r
421 me.value = me.value ? clearTime(me.value, true) : clearTime(new Date());\r
422\r
423 me.initDisabledDays();\r
424 },\r
425\r
426 // Keep the tree structure correct for Ext.form.field.Picker input fields which poke a 'pickerField' reference down into their pop-up pickers.\r
427 getRefOwner: function() {\r
428 return this.pickerField || this.callParent();\r
429 },\r
430\r
431 getRefItems: function() {\r
432 var results = [],\r
433 monthBtn = this.monthBtn,\r
434 todayBtn = this.todayBtn;\r
435\r
436 if (monthBtn) {\r
437 results.push(monthBtn);\r
438 }\r
439\r
440 if (todayBtn) {\r
441 results.push(todayBtn);\r
442 }\r
443 return results;\r
444 },\r
445\r
446 beforeRender: function() {\r
447 /*\r
448 * days array for looping through 6 full weeks (6 weeks * 7 days)\r
449 * Note that we explicitly force the size here so the template creates\r
450 * all the appropriate cells.\r
451 */\r
452 var me = this,\r
453 encode = Ext.String.htmlEncode,\r
454 days = new Array(me.numDays),\r
455 today = Ext.Date.format(new Date(), me.format);\r
456\r
457 if (me.padding && !me.width) {\r
458 me.cacheWidth();\r
459 }\r
460\r
461 me.monthBtn = new Ext.button.Split({\r
462 ownerCt: me,\r
463 ownerLayout: me.getComponentLayout(),\r
464 text: '',\r
465 tooltip: me.monthYearText,\r
466 tabIndex: -1,\r
467 ariaRole: 'presentation',\r
468 listeners: {\r
469 click: me.doShowMonthPicker,\r
470 arrowclick: me.doShowMonthPicker,\r
471 scope: me\r
472 }\r
473 });\r
474\r
475 if (me.showToday) {\r
476 me.todayBtn = new Ext.button.Button({\r
477 ui: me.footerButtonUI,\r
478 ownerCt: me,\r
479 ownerLayout: me.getComponentLayout(),\r
480 text: Ext.String.format(me.todayText, today),\r
481 tooltip: Ext.String.format(me.todayTip, today),\r
482 tooltipType: 'title',\r
483 tabIndex: -1,\r
484 ariaRole: 'presentation',\r
485 handler: me.selectToday,\r
486 scope: me\r
487 });\r
488 }\r
489\r
490 me.callParent();\r
491\r
492 Ext.applyIf(me, {\r
493 renderData: {}\r
494 });\r
495\r
496 Ext.apply(me.renderData, {\r
497 dayNames: me.dayNames,\r
498 showToday: me.showToday,\r
499 prevText: encode(me.prevText),\r
500 nextText: encode(me.nextText),\r
501 todayText: encode(me.todayText),\r
502 ariaMinText: encode(me.ariaMinText),\r
503 ariaMaxText: encode(me.ariaMaxText),\r
504 ariaDisabledDaysText: encode(me.ariaDisabledDaysText),\r
505 ariaDisabledDatesText: encode(me.ariaDisabledDatesText),\r
506 days: days\r
507 });\r
508\r
509 me.protoEl.unselectable();\r
510 },\r
511\r
512 cacheWidth: function() {\r
513 var me = this,\r
514 padding = me.parseBox(me.padding),\r
515 widthEl = Ext.getBody().createChild({\r
516 cls: me.baseCls + ' ' + me.borderBoxCls,\r
517 style: 'position:absolute;top:-1000px;left:-1000px;'\r
518 });\r
519\r
520 me.self.prototype.width = widthEl.getWidth() + padding.left + padding.right;\r
521 widthEl.destroy();\r
522 },\r
523\r
524 /**\r
525 * @inheritdoc\r
526 * @private\r
527 */\r
528 onRender: function(container, position) {\r
529 var me = this;\r
530\r
531 me.callParent(arguments);\r
532\r
533 me.cells = me.eventEl.select('tbody td');\r
534 me.textNodes = me.eventEl.query('tbody td div');\r
535 \r
536 me.eventEl.set({ 'aria-labelledby': me.monthBtn.id });\r
537\r
538 me.mon(me.eventEl, {\r
539 scope: me,\r
540 mousewheel: me.handleMouseWheel,\r
541 click: {\r
542 fn: me.handleDateClick,\r
543 delegate: 'div.' + me.baseCls + '-date'\r
544 }\r
545 });\r
546 \r
547 },\r
548\r
549 /**\r
550 * @inheritdoc\r
551 * @private\r
552 */\r
553 initEvents: function() {\r
554 var me = this,\r
555 pickerField = me.pickerField,\r
556 eDate = Ext.Date,\r
557 day = eDate.DAY;\r
558\r
559 me.callParent();\r
560\r
561 // If we're part of a date field, don't allow us to focus, the field will\r
562 // handle that. If we are standalone, then allow the default behaviour\r
563 // to occur to receive focus\r
564 if (pickerField) {\r
565 me.el.on('mousedown', me.onMouseDown, me);\r
566 }\r
567\r
568 // Month button is pointer interactive only, it should not be allowed to focus.\r
569 me.monthBtn.el.on('mousedown', me.onMouseDown, me);\r
570\r
571 me.prevRepeater = new Ext.util.ClickRepeater(me.prevEl, {\r
572 handler: me.showPrevMonth,\r
573 scope: me,\r
574 mousedownStopEvent: true\r
575 });\r
576\r
577 me.nextRepeater = new Ext.util.ClickRepeater(me.nextEl, {\r
578 handler: me.showNextMonth,\r
579 scope: me,\r
580 mousedownStopEvent: true\r
581 });\r
582\r
583 me.keyNav = new Ext.util.KeyNav(me.eventEl, Ext.apply({\r
584 scope: me,\r
585\r
586 left: function(e) {\r
587 if (e.ctrlKey) {\r
588 e.preventDefault();\r
589 me.showPrevMonth();\r
590 } else {\r
591 me.update(eDate.add(me.activeDate, day, -1));\r
592 }\r
593 },\r
594\r
595 right: function(e){\r
596 if (e.ctrlKey) {\r
597 e.preventDefault();\r
598 me.showNextMonth();\r
599 } else {\r
600 me.update(eDate.add(me.activeDate, day, 1));\r
601 }\r
602 },\r
603\r
604 up: function(e) {\r
605 if (e.ctrlKey) {\r
606 me.showNextYear();\r
607 } else {\r
608 me.update(eDate.add(me.activeDate, day, -7));\r
609 }\r
610 },\r
611\r
612 down: function(e) {\r
613 if (e.ctrlKey) {\r
614 me.showPrevYear();\r
615 } else {\r
616 me.update(eDate.add(me.activeDate, day, 7));\r
617 }\r
618 },\r
619\r
620 pageUp: function(e) {\r
621 if (e.ctrlKey) {\r
622 me.showPrevYear();\r
623 } else {\r
624 me.showPrevMonth();\r
625 }\r
626 },\r
627\r
628 pageDown: function(e) {\r
629 if (e.ctrlKey) {\r
630 me.showNextYear();\r
631 } else {\r
632 me.showNextMonth();\r
633 }\r
634 },\r
635\r
636 tab: function(e) {\r
637 // When the picker is floating and attached to an input field, its\r
638 // 'select' handler will focus the inputEl so when navigation happens\r
639 // it does so as if the input field was focused all the time.\r
640 // This is the desired behavior and we try not to interfere with it\r
641 // in the picker itself, see below.\r
642 me.handleTabKey(e);\r
643 \r
644 // Allow default behaviour of TAB - it MUST be allowed to navigate.\r
645 return true;\r
646 },\r
647\r
648 enter: function(e) {\r
649 me.handleDateClick(e, me.activeCell.firstChild);\r
650 },\r
651\r
652 space: function() {\r
653 me.setValue(new Date(me.activeCell.firstChild.dateValue));\r
654 var startValue = me.startValue,\r
655 value = me.value,\r
656 pickerValue;\r
657\r
658 if (pickerField) {\r
659 pickerValue = pickerField.getValue();\r
660 if (pickerValue && startValue && pickerValue.getTime() === value.getTime()) {\r
661 pickerField.setValue(startValue);\r
662 } else {\r
663 pickerField.setValue(value);\r
664 }\r
665 }\r
666 },\r
667\r
668 home: function(e) {\r
669 me.update(eDate.getFirstDateOfMonth(me.activeDate));\r
670 },\r
671\r
672 end: function(e) {\r
673 me.update(eDate.getLastDateOfMonth(me.activeDate));\r
674 }\r
675 }, me.keyNavConfig));\r
676\r
677 if (me.disabled) {\r
678 me.syncDisabled(true);\r
679 }\r
680 me.update(me.value);\r
681 },\r
682\r
683 onMouseDown: function(e) {\r
684 e.preventDefault();\r
685 },\r
686\r
687 handleTabKey: function(e) {\r
688 var me = this,\r
689 t = me.getSelectedDate(me.activeDate),\r
690 handler = me.handler;\r
691\r
692 // The following code is like handleDateClick without the e.stopEvent()\r
693 if (!me.disabled && t.dateValue && !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)) {\r
694 me.setValue(new Date(t.dateValue));\r
695 me.fireEvent('select', me, me.value);\r
696 if (handler) {\r
697 handler.call(me.scope || me, me, me.value);\r
698 }\r
699 me.onSelect();\r
700 }\r
701 // Even if the above condition is not met we have to let the field know\r
702 // that we're tabbing out - that's user action we can do nothing about\r
703 else {\r
704 me.fireEventArgs('tabout', [me]);\r
705 }\r
706 },\r
707\r
708 getSelectedDate: function (date) {\r
709 var me = this,\r
710 t = date.getTime(),\r
711 cells = me.cells,\r
712 cls = me.selectedCls,\r
713 cellItems = cells.elements,\r
714 cLen = cellItems.length,\r
715 cell, c;\r
716\r
717 cells.removeCls(cls);\r
718\r
719 for (c = 0; c < cLen; c++) {\r
720 cell = cellItems[c].firstChild;\r
721 if (cell.dateValue === t) {\r
722 return cell;\r
723 }\r
724 }\r
725 return null;\r
726 },\r
727\r
728 /**\r
729 * Setup the disabled dates regex based on config options\r
730 * @private\r
731 */\r
732 initDisabledDays: function() {\r
733 var me = this,\r
734 dd = me.disabledDates,\r
735 re = '(?:',\r
736 len,\r
737 d, dLen, dI;\r
738\r
739 if(!me.disabledDatesRE && dd){\r
740 len = dd.length - 1;\r
741\r
742 dLen = dd.length;\r
743\r
744 for (d = 0; d < dLen; d++) {\r
745 dI = dd[d];\r
746\r
747 re += Ext.isDate(dI) ? '^' + Ext.String.escapeRegex(Ext.Date.dateFormat(dI, me.format)) + '$' : dI;\r
748 if (d !== len) {\r
749 re += '|';\r
750 }\r
751 }\r
752\r
753 me.disabledDatesRE = new RegExp(re + ')');\r
754 }\r
755 },\r
756\r
757 /**\r
758 * Replaces any existing disabled dates with new values and refreshes the DatePicker.\r
759 * @param {String[]/RegExp} disabledDates An array of date strings (see the {@link #disabledDates} config for\r
760 * details on supported values), or a JavaScript regular expression used to disable a pattern of dates.\r
761 * @return {Ext.picker.Date} this\r
762 */\r
763 setDisabledDates: function(dd) {\r
764 var me = this;\r
765\r
766 if (Ext.isArray(dd)) {\r
767 me.disabledDates = dd;\r
768 me.disabledDatesRE = null;\r
769 } else {\r
770 me.disabledDatesRE = dd;\r
771 }\r
772 me.initDisabledDays();\r
773 me.update(me.value, true);\r
774 return me;\r
775 },\r
776\r
777 /**\r
778 * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.\r
779 * @param {Number[]} disabledDays An array of disabled day indexes. See the {@link #disabledDays} config for details\r
780 * on supported values.\r
781 * @return {Ext.picker.Date} this\r
782 */\r
783 setDisabledDays: function(dd) {\r
784 this.disabledDays = dd;\r
785 return this.update(this.value, true);\r
786 },\r
787\r
788 /**\r
789 * Replaces any existing {@link #minDate} with the new value and refreshes the DatePicker.\r
790 * @param {Date} value The minimum date that can be selected\r
791 * @return {Ext.picker.Date} this\r
792 */\r
793 setMinDate: function(dt) {\r
794 this.minDate = dt;\r
795 return this.update(this.value, true);\r
796 },\r
797\r
798 /**\r
799 * Replaces any existing {@link #maxDate} with the new value and refreshes the DatePicker.\r
800 * @param {Date} value The maximum date that can be selected\r
801 * @return {Ext.picker.Date} this\r
802 */\r
803 setMaxDate: function(dt) {\r
804 this.maxDate = dt;\r
805 return this.update(this.value, true);\r
806 },\r
807\r
808 /**\r
809 * Sets the value of the date field\r
810 * @param {Date} value The date to set\r
811 * @return {Ext.picker.Date} this\r
812 */\r
813 setValue: function(value) {\r
814 // If passed a null value just pass in a new date object.\r
815 this.value = Ext.Date.clearTime(value || new Date(), true);\r
816 return this.update(this.value);\r
817 },\r
818\r
819 /**\r
820 * Gets the current selected value of the date field\r
821 * @return {Date} The selected date\r
822 */\r
823 getValue: function() {\r
824 return this.value;\r
825 },\r
826\r
827 //<locale type="function">\r
828 /**\r
829 * Gets a single character to represent the day of the week\r
830 * @return {String} The character\r
831 */\r
832 getDayInitial: function(value) {\r
833 return value.substr(0,1);\r
834 },\r
835 //</locale>\r
836\r
837 /**\r
838 * @inheritdoc\r
839 * @private\r
840 */\r
841 onEnable: function() {\r
842 var me = this;\r
843\r
844 me.callParent();\r
845 me.syncDisabled(false);\r
846 me.update(me.activeDate);\r
847\r
848 },\r
849\r
850 /**\r
851 * @inheritdoc\r
852 * @private\r
853 */\r
854 onShow: function() {\r
855 var me = this;\r
856\r
857 me.callParent();\r
858 me.syncDisabled(false);\r
859 if (me.pickerField) {\r
860 me.startValue = me.pickerField.getValue();\r
861 }\r
862 },\r
863 \r
864 /**\r
865 * @inheritdoc\r
866 * @private\r
867 */\r
868 onHide: function() {\r
869 this.callParent();\r
870 this.syncDisabled(true);\r
871 },\r
872\r
873 /**\r
874 * @inheritdoc\r
875 * @private\r
876 */\r
877 onDisable: function() {\r
878 this.callParent();\r
879 this.syncDisabled(true);\r
880 },\r
881\r
882 /**\r
883 * Get the current active date.\r
884 * @private\r
885 * @return {Date} The active date\r
886 */\r
887 getActive: function(){\r
888 return this.activeDate || this.value;\r
889 },\r
890\r
891 /**\r
892 * Run any animation required to hide/show the month picker.\r
893 * @private\r
894 * @param {Boolean} isHide True if it's a hide operation\r
895 */\r
896 runAnimation: function(isHide){\r
897 var picker = this.monthPicker,\r
898 options = {\r
899 duration: 200,\r
900 callback: function() {\r
901 picker.setVisible(!isHide);\r
902 }\r
903 };\r
904\r
905 if (isHide) {\r
906 picker.el.slideOut('t', options);\r
907 } else {\r
908 picker.el.slideIn('t', options);\r
909 }\r
910 },\r
911\r
912 /**\r
913 * Hides the month picker, if it's visible.\r
914 * @param {Boolean} [animate] Indicates whether to animate this action. If the animate\r
915 * parameter is not specified, the behavior will use {@link #disableAnim} to determine\r
916 * whether to animate or not.\r
917 * @return {Ext.picker.Date} this\r
918 */\r
919 hideMonthPicker: function(animate){\r
920 var me = this,\r
921 picker = me.monthPicker;\r
922\r
923 if (picker && picker.isVisible()) {\r
924 if (me.shouldAnimate(animate)) {\r
925 me.runAnimation(true);\r
926 } else {\r
927 picker.hide();\r
928 }\r
929 }\r
930 return me;\r
931 },\r
932 \r
933 doShowMonthPicker: function() {\r
934 // Wrap in an extra call so we can prevent the button\r
935 // being passed as an animation parameter.\r
936 this.showMonthPicker();\r
937 },\r
938 \r
939 doHideMonthPicker: function() {\r
940 // Wrap in an extra call so we can prevent this\r
941 // being passed as an animation parameter\r
942 this.hideMonthPicker();\r
943 },\r
944\r
945 /**\r
946 * Show the month picker\r
947 * @param {Boolean} [animate] Indicates whether to animate this action. If the animate\r
948 * parameter is not specified, the behavior will use {@link #disableAnim} to determine\r
949 * whether to animate or not.\r
950 * @return {Ext.picker.Date} this\r
951 */\r
952 showMonthPicker: function(animate) {\r
953 var me = this,\r
954 el = me.el,\r
955 picker;\r
956 \r
957 if (me.rendered && !me.disabled) {\r
958 picker = me.createMonthPicker(); \r
959 if (!picker.isVisible()) {\r
960 picker.setValue(me.getActive());\r
961 picker.setSize(el.getSize());\r
962\r
963 // Null out floatParent so that the [-1, -1] position is not made relative to this\r
964 picker.floatParent = null;\r
965 picker.setPosition(-el.getBorderWidth('l'), -el.getBorderWidth('t'));\r
966 if (me.shouldAnimate(animate)) {\r
967 me.runAnimation(false);\r
968 } else {\r
969 picker.show();\r
970 }\r
971 }\r
972 }\r
973 return me;\r
974 },\r
975 \r
976 /**\r
977 * Checks whether a hide/show action should animate\r
978 * @private\r
979 * @param {Boolean} [animate] A possible animation value\r
980 * @return {Boolean} Whether to animate the action\r
981 */\r
982 shouldAnimate: function(animate) {\r
983 return Ext.isDefined(animate) ? animate : !this.disableAnim;\r
984 },\r
985\r
986 /**\r
987 * Create the month picker instance\r
988 * @private\r
989 * @return {Ext.picker.Month} picker\r
990 */\r
991 createMonthPicker: function() {\r
992 var me = this,\r
993 picker = me.monthPicker;\r
994\r
995 if (!picker) {\r
996 me.monthPicker = picker = new Ext.picker.Month({\r
997 renderTo: me.el,\r
998 // We need to set the ownerCmp so that owns() can correctly\r
999 // match up the component hierarchy so that focus does not leave\r
1000 // an owning picker field if/when this gets focus.\r
1001 ownerCmp: me,\r
1002 floating: true,\r
1003 padding: me.padding,\r
1004 shadow: false,\r
1005 small: me.showToday === false,\r
1006 footerButtonUI: me.footerButtonUI,\r
1007 listeners: {\r
1008 scope: me,\r
1009 cancelclick: me.onCancelClick,\r
1010 okclick: me.onOkClick,\r
1011 yeardblclick: me.onOkClick,\r
1012 monthdblclick: me.onOkClick\r
1013 }\r
1014 });\r
1015 if (!me.disableAnim) {\r
1016 // hide the element if we're animating to prevent an initial flicker\r
1017 picker.el.setStyle('display', 'none');\r
1018 }\r
1019 picker.hide();\r
1020 me.on('beforehide', me.doHideMonthPicker, me);\r
1021 }\r
1022 return picker;\r
1023 },\r
1024\r
1025 /**\r
1026 * Respond to an ok click on the month picker\r
1027 * @private\r
1028 */\r
1029 onOkClick: function(picker, value) {\r
1030 var me = this,\r
1031 month = value[0],\r
1032 year = value[1],\r
1033 date = new Date(year, month, me.getActive().getDate());\r
1034\r
1035 if (date.getMonth() !== month) {\r
1036 // 'fix' the JS rolling date conversion if needed\r
1037 date = Ext.Date.getLastDateOfMonth(new Date(year, month, 1));\r
1038 }\r
1039 me.setValue(date);\r
1040 me.hideMonthPicker();\r
1041 },\r
1042\r
1043 /**\r
1044 * Respond to a cancel click on the month picker\r
1045 * @private\r
1046 */\r
1047 onCancelClick: function() {\r
1048 this.selectedUpdate(this.activeDate);\r
1049 this.hideMonthPicker();\r
1050 },\r
1051\r
1052 /**\r
1053 * Show the previous month.\r
1054 * @param {Object} e\r
1055 * @return {Ext.picker.Date} this\r
1056 */\r
1057 showPrevMonth: function(e) {\r
1058 return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.MONTH, -1));\r
1059 },\r
1060\r
1061 /**\r
1062 * Show the next month.\r
1063 * @param {Object} e\r
1064 * @return {Ext.picker.Date} this\r
1065 */\r
1066 showNextMonth: function(e) {\r
1067 return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.MONTH, 1));\r
1068 },\r
1069\r
1070 /**\r
1071 * Show the previous year.\r
1072 * @return {Ext.picker.Date} this\r
1073 */\r
1074 showPrevYear: function() {\r
1075 return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.YEAR, -1));\r
1076 },\r
1077\r
1078 /**\r
1079 * Show the next year.\r
1080 * @return {Ext.picker.Date} this\r
1081 */\r
1082 showNextYear: function() {\r
1083 return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.YEAR, 1));\r
1084 },\r
1085\r
1086 /**\r
1087 * Respond to the mouse wheel event\r
1088 * @private\r
1089 * @param {Ext.event.Event} e\r
1090 */\r
1091 handleMouseWheel: function(e) {\r
1092 var delta;\r
1093 \r
1094 e.stopEvent();\r
1095 \r
1096 if (!this.disabled) {\r
1097 delta = e.getWheelDelta();\r
1098 \r
1099 if (delta > 0) {\r
1100 this.showPrevMonth();\r
1101 }\r
1102 else if (delta < 0) {\r
1103 this.showNextMonth();\r
1104 }\r
1105 }\r
1106 },\r
1107\r
1108 /**\r
1109 * Respond to a date being clicked in the picker\r
1110 * @private\r
1111 * @param {Ext.event.Event} e\r
1112 * @param {HTMLElement} t\r
1113 */\r
1114 handleDateClick: function(e, t) {\r
1115 var me = this,\r
1116 handler = me.handler;\r
1117\r
1118 e.stopEvent();\r
1119 \r
1120 if (!me.disabled && t.dateValue && !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)) {\r
1121 me.setValue(new Date(t.dateValue));\r
1122 me.fireEvent('select', me, me.value);\r
1123 \r
1124 if (handler) {\r
1125 handler.call(me.scope || me, me, me.value);\r
1126 }\r
1127 \r
1128 // event handling is turned off on hide\r
1129 // when we are using the picker in a field\r
1130 // therefore onSelect comes AFTER the select\r
1131 // event.\r
1132 me.onSelect();\r
1133 }\r
1134 },\r
1135\r
1136 /**\r
1137 * Perform any post-select actions\r
1138 * @private\r
1139 */\r
1140 onSelect: function() {\r
1141 if (this.hideOnSelect) {\r
1142 this.hide();\r
1143 }\r
1144 },\r
1145\r
1146 /**\r
1147 * Sets the current value to today.\r
1148 * @return {Ext.picker.Date} this\r
1149 */\r
1150 selectToday: function() {\r
1151 var me = this,\r
1152 btn = me.todayBtn,\r
1153 handler = me.handler;\r
1154\r
1155 if (btn && !btn.disabled) {\r
1156 me.setValue(Ext.Date.clearTime(new Date()));\r
1157 me.fireEvent('select', me, me.value);\r
1158 if (handler) {\r
1159 handler.call(me.scope || me, me, me.value);\r
1160 }\r
1161 me.onSelect();\r
1162 }\r
1163 return me;\r
1164 },\r
1165\r
1166 /**\r
1167 * Update the selected cell\r
1168 * @private\r
1169 * @param {Date} date The new date\r
1170 */\r
1171 selectedUpdate: function(date) {\r
1172 var me = this,\r
1173 t = date.getTime(),\r
1174 cells = me.cells,\r
1175 cls = me.selectedCls,\r
1176 c,\r
1177 cLen = cells.getCount(),\r
1178 cell;\r
1179 \r
1180 me.eventEl.dom.setAttribute('aria-busy', 'true');\r
1181 \r
1182 cell = me.activeCell;\r
1183 \r
1184 if (cell) {\r
1185 Ext.fly(cell).removeCls(cls);\r
1186 cell.setAttribute('aria-selected', false);\r
1187 }\r
1188\r
1189 for (c = 0; c < cLen; c++) {\r
1190 cell = cells.item(c);\r
1191\r
1192 if (me.textNodes[c].dateValue === t) {\r
1193 me.activeCell = cell.dom;\r
1194 me.eventEl.dom.setAttribute('aria-activedescendant', cell.dom.id);\r
1195 cell.dom.setAttribute('aria-selected', true);\r
1196 cell.addCls(cls);\r
1197 me.fireEvent('highlightitem', me, cell);\r
1198 break;\r
1199 }\r
1200 }\r
1201 \r
1202 me.eventEl.dom.removeAttribute('aria-busy');\r
1203 },\r
1204\r
1205 /**\r
1206 * Update the contents of the picker for a new month\r
1207 * @private\r
1208 * @param {Date} date The new date\r
1209 */\r
1210 fullUpdate: function(date) {\r
1211 var me = this,\r
1212 cells = me.cells.elements,\r
1213 textNodes = me.textNodes,\r
1214 disabledCls = me.disabledCellCls,\r
1215 eDate = Ext.Date,\r
1216 i = 0,\r
1217 extraDays = 0,\r
1218 newDate = +eDate.clearTime(date, true),\r
1219 today = +eDate.clearTime(new Date()),\r
1220 min = me.minDate ? eDate.clearTime(me.minDate, true) : Number.NEGATIVE_INFINITY,\r
1221 max = me.maxDate ? eDate.clearTime(me.maxDate, true) : Number.POSITIVE_INFINITY,\r
1222 ddMatch = me.disabledDatesRE,\r
1223 ddText = me.disabledDatesText,\r
1224 ddays = me.disabledDays ? me.disabledDays.join('') : false,\r
1225 ddaysText = me.disabledDaysText,\r
1226 format = me.format,\r
1227 days = eDate.getDaysInMonth(date),\r
1228 firstOfMonth = eDate.getFirstDateOfMonth(date),\r
1229 startingPos = firstOfMonth.getDay() - me.startDay,\r
1230 previousMonth = eDate.add(date, eDate.MONTH, -1),\r
1231 ariaTitleDateFormat = me.ariaTitleDateFormat,\r
1232 prevStart, current, disableToday, tempDate, setCellClass, html, cls,\r
1233 formatValue, value;\r
1234\r
1235 if (startingPos < 0) {\r
1236 startingPos += 7;\r
1237 }\r
1238\r
1239 days += startingPos;\r
1240 prevStart = eDate.getDaysInMonth(previousMonth) - startingPos;\r
1241 current = new Date(previousMonth.getFullYear(), previousMonth.getMonth(), prevStart, me.initHour);\r
1242\r
1243 if (me.showToday) {\r
1244 tempDate = eDate.clearTime(new Date());\r
1245 disableToday = (tempDate < min || tempDate > max ||\r
1246 (ddMatch && format && ddMatch.test(eDate.dateFormat(tempDate, format))) ||\r
1247 (ddays && ddays.indexOf(tempDate.getDay()) !== -1));\r
1248\r
1249 if (!me.disabled) {\r
1250 me.todayBtn.setDisabled(disableToday);\r
1251 }\r
1252 }\r
1253\r
1254 setCellClass = function(cellIndex, cls){\r
1255 var cell = cells[cellIndex],\r
1256 describedBy = [];\r
1257 \r
1258 // Cells are not rendered with ids\r
1259 if (!cell.hasAttribute('id')) {\r
1260 cell.setAttribute('id', me.id + '-cell-' + cellIndex);\r
1261 }\r
1262 \r
1263 // store dateValue number as an expando\r
1264 value = +eDate.clearTime(current, true);\r
1265 cell.firstChild.dateValue = value;\r
1266 \r
1267 cell.setAttribute('aria-label', eDate.format(current, ariaTitleDateFormat));\r
1268 \r
1269 // Here and below we can't use title attribute instead of data-qtip\r
1270 // because JAWS will announce title value before cell content\r
1271 // which is not what we need. Also we are using aria-describedby attribute\r
1272 // and not placing the text in aria-label because some cells may have\r
1273 // compound descriptions (like Today and Disabled day).\r
1274 cell.removeAttribute('aria-describedby');\r
1275 cell.removeAttribute('data-qtip');\r
1276 \r
1277 if (value === today) {\r
1278 cls += ' ' + me.todayCls;\r
1279 describedBy.push(me.id + '-todayText');\r
1280 }\r
1281 \r
1282 if (value === newDate) {\r
1283 me.activeCell = cell;\r
1284 me.eventEl.dom.setAttribute('aria-activedescendant', cell.id);\r
1285 cell.setAttribute('aria-selected', true);\r
1286 cls += ' ' + me.selectedCls;\r
1287 me.fireEvent('highlightitem', me, cell);\r
1288 }\r
1289 else {\r
1290 cell.setAttribute('aria-selected', false);\r
1291 }\r
1292\r
1293 if (value < min) {\r
1294 cls += ' ' + disabledCls;\r
1295 describedBy.push(me.id + '-ariaMinText');\r
1296 cell.setAttribute('data-qtip', me.minText);\r
1297 }\r
1298 else if (value > max) {\r
1299 cls += ' ' + disabledCls;\r
1300 describedBy.push(me.id + '-ariaMaxText');\r
1301 cell.setAttribute('data-qtip', me.maxText);\r
1302 }\r
1303 else if (ddays && ddays.indexOf(current.getDay()) !== -1){\r
1304 cell.setAttribute('data-qtip', ddaysText);\r
1305 describedBy.push(me.id + '-ariaDisabledDaysText');\r
1306 cls += ' ' + disabledCls;\r
1307 }\r
1308 else if (ddMatch && format){\r
1309 formatValue = eDate.dateFormat(current, format);\r
1310 if(ddMatch.test(formatValue)){\r
1311 cell.setAttribute('data-qtip', ddText.replace('%0', formatValue));\r
1312 describedBy.push(me.id + '-ariaDisabledDatesText');\r
1313 cls += ' ' + disabledCls;\r
1314 }\r
1315 }\r
1316 \r
1317 if (describedBy.length) {\r
1318 cell.setAttribute('aria-describedby', describedBy.join(' '));\r
1319 }\r
1320 \r
1321 cell.className = cls + ' ' + me.cellCls;\r
1322 };\r
1323 \r
1324 me.eventEl.dom.setAttribute('aria-busy', 'true');\r
1325\r
1326 for (; i < me.numDays; ++i) {\r
1327 if (i < startingPos) {\r
1328 html = (++prevStart);\r
1329 cls = me.prevCls;\r
1330 } else if (i >= days) {\r
1331 html = (++extraDays);\r
1332 cls = me.nextCls;\r
1333 } else {\r
1334 html = i - startingPos + 1;\r
1335 cls = me.activeCls;\r
1336 }\r
1337 textNodes[i].innerHTML = html;\r
1338 current.setDate(current.getDate() + 1);\r
1339 setCellClass(i, cls);\r
1340 }\r
1341 \r
1342 me.eventEl.dom.removeAttribute('aria-busy');\r
1343\r
1344 me.monthBtn.setText(Ext.Date.format(date, me.monthYearFormat));\r
1345 },\r
1346\r
1347 /**\r
1348 * Update the contents of the picker\r
1349 * @private\r
1350 * @param {Date} date The new date\r
1351 * @param {Boolean} forceRefresh True to force a full refresh\r
1352 */\r
1353 update: function(date, forceRefresh) {\r
1354 var me = this,\r
1355 active = me.activeDate;\r
1356\r
1357 if (me.rendered) {\r
1358 me.activeDate = date;\r
1359 if (!forceRefresh && active && me.el &&\r
1360 active.getMonth() === date.getMonth() &&\r
1361 active.getFullYear() === date.getFullYear()) {\r
1362 me.selectedUpdate(date, active);\r
1363 } else {\r
1364 me.fullUpdate(date, active);\r
1365 }\r
1366 }\r
1367 return me;\r
1368 },\r
1369\r
1370 /**\r
1371 * @private\r
1372 * @inheritdoc\r
1373 */\r
1374 beforeDestroy: function() {\r
1375 var me = this;\r
1376\r
1377 if (me.rendered) {\r
1378 Ext.destroy(\r
1379 me.keyNav,\r
1380 me.monthPicker,\r
1381 me.monthBtn,\r
1382 me.nextRepeater,\r
1383 me.prevRepeater,\r
1384 me.todayBtn,\r
1385 me.todayElSpan\r
1386 );\r
1387 delete me.textNodes;\r
1388 delete me.cells.elements;\r
1389 }\r
1390 me.callParent();\r
1391 },\r
1392\r
1393 privates: {\r
1394 // Do the job of a container layout at this point even though we are not a Container.\r
1395 // TODO: Refactor as a Container.\r
1396 finishRenderChildren: function () {\r
1397 var me = this;\r
1398\r
1399 me.callParent();\r
1400 me.monthBtn.finishRender();\r
1401 if (me.showToday) {\r
1402 me.todayBtn.finishRender();\r
1403 }\r
1404 },\r
1405 \r
1406 getFocusEl: function() {\r
1407 return this.eventEl;\r
1408 },\r
1409\r
1410 /**\r
1411 * Set the disabled state of various internal components\r
1412 * @param {Boolean} disabled\r
1413 * @private\r
1414 */\r
1415 syncDisabled: function (disabled) {\r
1416 var me = this,\r
1417 keyNav = me.keyNav;\r
1418\r
1419 // If we have one, we have all\r
1420 if (keyNav) {\r
1421 keyNav.setDisabled(disabled);\r
1422 me.prevRepeater.setDisabled(disabled);\r
1423 me.nextRepeater.setDisabled(disabled);\r
1424 if (me.todayBtn) {\r
1425 me.todayBtn.setDisabled(disabled);\r
1426 }\r
1427 }\r
1428 }\r
1429 }\r
1430});\r