DatePicker.js
上传用户:dawnssy
上传日期:2022-08-06
资源大小:9345k
文件大小:28k
源码类别:

JavaScript

开发平台:

JavaScript

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