-Ext.reg('colorpalette', Ext.ColorPalette);/**\r
- * @class Ext.DatePicker\r
- * @extends Ext.Component\r
- * Simple date picker class.\r
- * @constructor\r
- * Create a new DatePicker\r
- * @param {Object} config The config object\r
- * @xtype datepicker\r
- */\r
-Ext.DatePicker = Ext.extend(Ext.BoxComponent, {\r
- /**\r
- * @cfg {String} todayText\r
- * The text to display on the button that selects the current date (defaults to <tt>'Today'</tt>)\r
- */\r
- todayText : 'Today',\r
- /**\r
- * @cfg {String} okText\r
- * The text to display on the ok button (defaults to <tt>' OK '</tt> to give the user extra clicking room)\r
- */\r
- okText : ' OK ',\r
- /**\r
- * @cfg {String} cancelText\r
- * The text to display on the cancel button (defaults to <tt>'Cancel'</tt>)\r
- */\r
- cancelText : 'Cancel',\r
- /**\r
- * @cfg {String} todayTip\r
- * The tooltip to display for the button that selects the current date (defaults to <tt>'{current date} (Spacebar)'</tt>)\r
- */\r
- todayTip : '{0} (Spacebar)',\r
- /**\r
- * @cfg {String} minText\r
- * The error text to display if the minDate validation fails (defaults to <tt>'This date is before the minimum date'</tt>)\r
- */\r
- minText : 'This date is before the minimum date',\r
- /**\r
- * @cfg {String} maxText\r
- * The error text to display if the maxDate validation fails (defaults to <tt>'This date is after the maximum date'</tt>)\r
- */\r
- maxText : 'This date is after the maximum date',\r
- /**\r
- * @cfg {String} format\r
- * The default date format string which can be overriden for localization support. The format must be\r
- * valid according to {@link Date#parseDate} (defaults to <tt>'m/d/y'</tt>).\r
- */\r
- format : 'm/d/y',\r
- /**\r
- * @cfg {String} disabledDaysText\r
- * The tooltip to display when the date falls on a disabled day (defaults to <tt>'Disabled'</tt>)\r
- */\r
- disabledDaysText : 'Disabled',\r
- /**\r
- * @cfg {String} disabledDatesText\r
- * The tooltip text to display when the date falls on a disabled date (defaults to <tt>'Disabled'</tt>)\r
- */\r
- disabledDatesText : 'Disabled',\r
- /**\r
- * @cfg {Array} monthNames\r
- * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)\r
- */\r
- monthNames : Date.monthNames,\r
- /**\r
- * @cfg {Array} dayNames\r
- * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)\r
- */\r
- dayNames : Date.dayNames,\r
- /**\r
- * @cfg {String} nextText\r
- * The next month navigation button tooltip (defaults to <tt>'Next Month (Control+Right)'</tt>)\r
- */\r
- nextText : 'Next Month (Control+Right)',\r
- /**\r
- * @cfg {String} prevText\r
- * The previous month navigation button tooltip (defaults to <tt>'Previous Month (Control+Left)'</tt>)\r
- */\r
- prevText : 'Previous Month (Control+Left)',\r
- /**\r
- * @cfg {String} monthYearText\r
- * The header month selector tooltip (defaults to <tt>'Choose a month (Control+Up/Down to move years)'</tt>)\r
- */\r
- monthYearText : 'Choose a month (Control+Up/Down to move years)',\r
- /**\r
- * @cfg {Number} startDay\r
- * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)\r
- */\r
- startDay : 0,\r
- /**\r
- * @cfg {Boolean} showToday\r
- * False to hide the footer area containing the Today button and disable the keyboard handler for spacebar\r
- * that selects the current date (defaults to <tt>true</tt>).\r
- */\r
- showToday : true,\r
- /**\r
- * @cfg {Date} minDate\r
- * Minimum allowable date (JavaScript date object, defaults to null)\r
- */\r
- /**\r
- * @cfg {Date} maxDate\r
- * Maximum allowable date (JavaScript date object, defaults to null)\r
- */\r
- /**\r
- * @cfg {Array} disabledDays\r
- * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).\r
- */\r
- /**\r
- * @cfg {RegExp} disabledDatesRE\r
- * JavaScript regular expression used to disable a pattern of dates (defaults to null). The {@link #disabledDates}\r
- * config will generate this regex internally, but if you specify disabledDatesRE it will take precedence over the\r
- * disabledDates value.\r
- */\r
- /**\r
- * @cfg {Array} disabledDates\r
- * An array of 'dates' to disable, as strings. These strings will be used to build a dynamic regular\r
- * expression so they are very powerful. Some examples:\r
- * <ul>\r
- * <li>['03/08/2003', '09/16/2003'] would disable those exact dates</li>\r
- * <li>['03/08', '09/16'] would disable those days for every year</li>\r
- * <li>['^03/08'] would only match the beginning (useful if you are using short years)</li>\r
- * <li>['03/../2006'] would disable every day in March 2006</li>\r
- * <li>['^03'] would disable every day in every March</li>\r
- * </ul>\r
- * Note that the format of the dates included in the array should exactly match the {@link #format} config.\r
- * In order to support regular expressions, if you are using a date format that has '.' in it, you will have to\r
- * escape the dot when restricting dates. For example: ['03\\.08\\.03'].\r
- */\r
-\r
- // private\r
- initComponent : function(){\r
- Ext.DatePicker.superclass.initComponent.call(this);\r
-\r
- this.value = this.value ?\r
- this.value.clearTime() : new Date().clearTime();\r
-\r
- this.addEvents(\r
- /**\r
- * @event select\r
- * Fires when a date is selected\r
- * @param {DatePicker} this\r
- * @param {Date} date The selected date\r
- */\r
- 'select'\r
- );\r
-\r
- if(this.handler){\r
- this.on('select', this.handler, this.scope || this);\r
- }\r
-\r
- this.initDisabledDays();\r
- },\r
-\r
- // private\r
- initDisabledDays : function(){\r
- if(!this.disabledDatesRE && this.disabledDates){\r
- var dd = this.disabledDates,\r
- len = dd.length - 1,\r
- re = '(?:';\r
- \r
- Ext.each(dd, function(d, i){\r
- re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];\r
- if(i != len){\r
- re += '|';\r
- }\r
- }, this);\r
- this.disabledDatesRE = new RegExp(re + ')');\r
- }\r
- },\r
-\r
- /**\r
- * Replaces any existing disabled dates with new values and refreshes the DatePicker.\r
- * @param {Array/RegExp} disabledDates An array of date strings (see the {@link #disabledDates} config\r
- * for details on supported values), or a JavaScript regular expression used to disable a pattern of dates.\r
- */\r
- setDisabledDates : function(dd){\r
- if(Ext.isArray(dd)){\r
- this.disabledDates = dd;\r
- this.disabledDatesRE = null;\r
- }else{\r
- this.disabledDatesRE = dd;\r
- }\r
- this.initDisabledDays();\r
- this.update(this.value, true);\r
- },\r
-\r
- /**\r
- * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.\r
- * @param {Array} disabledDays An array of disabled day indexes. See the {@link #disabledDays} config\r
- * for details on supported values.\r
- */\r
- setDisabledDays : function(dd){\r
- this.disabledDays = dd;\r
- this.update(this.value, true);\r
- },\r
-\r
- /**\r
- * Replaces any existing {@link #minDate} with the new value and refreshes the DatePicker.\r
- * @param {Date} value The minimum date that can be selected\r
- */\r
- setMinDate : function(dt){\r
- this.minDate = dt;\r
- this.update(this.value, true);\r
- },\r
-\r
- /**\r
- * Replaces any existing {@link #maxDate} with the new value and refreshes the DatePicker.\r
- * @param {Date} value The maximum date that can be selected\r
- */\r
- setMaxDate : function(dt){\r
- this.maxDate = dt;\r
- this.update(this.value, true);\r
- },\r
-\r
- /**\r
- * Sets the value of the date field\r
- * @param {Date} value The date to set\r
- */\r
- setValue : function(value){\r
- var old = this.value;\r
- this.value = value.clearTime(true);\r
- if(this.el){\r
- this.update(this.value);\r
- }\r
- },\r
-\r
- /**\r
- * Gets the current selected value of the date field\r
- * @return {Date} The selected date\r
- */\r
- getValue : function(){\r
- return this.value;\r
- },\r
-\r
- // private\r
- focus : function(){\r
- if(this.el){\r
- this.update(this.activeDate);\r
- }\r
- },\r
- \r
- // private\r
- onEnable: function(initial){\r
- Ext.DatePicker.superclass.onEnable.call(this); \r
- this.doDisabled(false);\r
- this.update(initial ? this.value : this.activeDate);\r
- if(Ext.isIE){\r
- this.el.repaint();\r
- }\r
- \r
- },\r
- \r
- // private\r
- onDisable: function(){\r
- Ext.DatePicker.superclass.onDisable.call(this); \r
- this.doDisabled(true);\r
- if(Ext.isIE && !Ext.isIE8){\r
- /* Really strange problem in IE6/7, when disabled, have to explicitly\r
- * repaint each of the nodes to get them to display correctly, simply\r
- * calling repaint on the main element doesn't appear to be enough.\r
- */\r
- Ext.each([].concat(this.textNodes, this.el.query('th span')), function(el){\r
- Ext.fly(el).repaint();\r
- });\r
- }\r
- },\r
- \r
- // private\r
- doDisabled: function(disabled){\r
- this.keyNav.setDisabled(disabled);\r
- this.prevRepeater.setDisabled(disabled);\r
- this.nextRepeater.setDisabled(disabled);\r
- if(this.showToday){\r
- this.todayKeyListener.setDisabled(disabled);\r
- this.todayBtn.setDisabled(disabled);\r
- }\r
- },\r
-\r
- // private\r
- onRender : function(container, position){\r
- var m = [\r
- '<table cellspacing="0">',\r
- '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'"> </a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'"> </a></td></tr>',\r
- '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'],\r
- dn = this.dayNames,\r
- i;\r
- for(i = 0; i < 7; i++){\r
- var d = this.startDay+i;\r
- if(d > 6){\r
- d = d-7;\r
- }\r
- m.push('<th><span>', dn[d].substr(0,1), '</span></th>');\r
- }\r
- m[m.length] = '</tr></thead><tbody><tr>';\r
- for(i = 0; i < 42; i++) {\r
- if(i % 7 === 0 && i !== 0){\r
- m[m.length] = '</tr><tr>';\r
- }\r
- m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';\r
- }\r
- m.push('</tr></tbody></table></td></tr>',\r
- this.showToday ? '<tr><td colspan="3" class="x-date-bottom" align="center"></td></tr>' : '',\r
- '</table><div class="x-date-mp"></div>');\r
-\r
- var el = document.createElement('div');\r
- el.className = 'x-date-picker';\r
- el.innerHTML = m.join('');\r
-\r
- container.dom.insertBefore(el, position);\r
-\r
- this.el = Ext.get(el);\r
- this.eventEl = Ext.get(el.firstChild);\r
-\r
- this.prevRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-left a'), {\r
- handler: this.showPrevMonth,\r
- scope: this,\r
- preventDefault:true,\r
- stopDefault:true\r
- });\r
-\r
- this.nextRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-right a'), {\r
- handler: this.showNextMonth,\r
- scope: this,\r
- preventDefault:true,\r
- stopDefault:true\r
- });\r
-\r
- this.monthPicker = this.el.down('div.x-date-mp');\r
- this.monthPicker.enableDisplayMode('block');\r
-\r
- this.keyNav = new Ext.KeyNav(this.eventEl, {\r
- 'left' : function(e){\r
- if(e.ctrlKey){\r
- this.showPrevMonth();\r
- }else{\r
- this.update(this.activeDate.add('d', -1)); \r
- }\r
- },\r
-\r
- 'right' : function(e){\r
- if(e.ctrlKey){\r
- this.showNextMonth();\r
- }else{\r
- this.update(this.activeDate.add('d', 1)); \r
- }\r
- },\r
-\r
- 'up' : function(e){\r
- if(e.ctrlKey){\r
- this.showNextYear();\r
- }else{\r
- this.update(this.activeDate.add('d', -7));\r
- }\r
- },\r
-\r
- 'down' : function(e){\r
- if(e.ctrlKey){\r
- this.showPrevYear();\r
- }else{\r
- this.update(this.activeDate.add('d', 7));\r
- }\r
- },\r
-\r
- 'pageUp' : function(e){\r
- this.showNextMonth();\r
- },\r
-\r
- 'pageDown' : function(e){\r
- this.showPrevMonth();\r
- },\r
-\r
- 'enter' : function(e){\r
- e.stopPropagation();\r
- return true;\r
- },\r
-\r
- scope : this\r
- });\r
-\r
- this.el.unselectable();\r
-\r
- this.cells = this.el.select('table.x-date-inner tbody td');\r
- this.textNodes = this.el.query('table.x-date-inner tbody span');\r
-\r
- this.mbtn = new Ext.Button({\r
- text: ' ',\r
- tooltip: this.monthYearText,\r
- renderTo: this.el.child('td.x-date-middle', true)\r
- });\r
- this.mbtn.el.child('em').addClass('x-btn-arrow');\r
-\r
- if(this.showToday){\r
- this.todayKeyListener = this.eventEl.addKeyListener(Ext.EventObject.SPACE, this.selectToday, this);\r
- var today = (new Date()).dateFormat(this.format);\r
- this.todayBtn = new Ext.Button({\r
- renderTo: this.el.child('td.x-date-bottom', true),\r
- text: String.format(this.todayText, today),\r
- tooltip: String.format(this.todayTip, today),\r
- handler: this.selectToday,\r
- scope: this\r
- });\r
- }\r
- this.mon(this.eventEl, 'mousewheel', this.handleMouseWheel, this);\r
- this.mon(this.eventEl, 'click', this.handleDateClick, this, {delegate: 'a.x-date-date'});\r
- this.mon(this.mbtn, 'click', this.showMonthPicker, this);\r
- this.onEnable(true);\r
- },\r
-\r
- // private\r
- createMonthPicker : function(){\r
- if(!this.monthPicker.dom.firstChild){\r
- var buf = ['<table border="0" cellspacing="0">'];\r
- for(var i = 0; i < 6; i++){\r
- buf.push(\r
- '<tr><td class="x-date-mp-month"><a href="#">', Date.getShortMonthName(i), '</a></td>',\r
- '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', Date.getShortMonthName(i + 6), '</a></td>',\r
- i === 0 ?\r
- '<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
- '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'\r
- );\r
- }\r
- buf.push(\r
- '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',\r
- this.okText,\r
- '</button><button type="button" class="x-date-mp-cancel">',\r
- this.cancelText,\r
- '</button></td></tr>',\r
- '</table>'\r
- );\r
- this.monthPicker.update(buf.join(''));\r
-\r
- this.mon(this.monthPicker, 'click', this.onMonthClick, this);\r
- this.mon(this.monthPicker, 'dblclick', this.onMonthDblClick, this);\r
-\r
- this.mpMonths = this.monthPicker.select('td.x-date-mp-month');\r
- this.mpYears = this.monthPicker.select('td.x-date-mp-year');\r
-\r
- this.mpMonths.each(function(m, a, i){\r
- i += 1;\r
- if((i%2) === 0){\r
- m.dom.xmonth = 5 + Math.round(i * 0.5);\r
- }else{\r
- m.dom.xmonth = Math.round((i-1) * 0.5);\r
- }\r
- });\r
- }\r
- },\r
-\r
- // private\r
- showMonthPicker : function(){\r
- if(!this.disabled){\r
- this.createMonthPicker();\r
- var size = this.el.getSize();\r
- this.monthPicker.setSize(size);\r
- this.monthPicker.child('table').setSize(size);\r
-\r
- this.mpSelMonth = (this.activeDate || this.value).getMonth();\r
- this.updateMPMonth(this.mpSelMonth);\r
- this.mpSelYear = (this.activeDate || this.value).getFullYear();\r
- this.updateMPYear(this.mpSelYear);\r
-\r
- this.monthPicker.slideIn('t', {duration:0.2});\r
- }\r
- },\r
-\r
- // private\r
- updateMPYear : function(y){\r
- this.mpyear = y;\r
- var ys = this.mpYears.elements;\r
- for(var i = 1; i <= 10; i++){\r
- var td = ys[i-1], y2;\r
- if((i%2) === 0){\r
- y2 = y + Math.round(i * 0.5);\r
- td.firstChild.innerHTML = y2;\r
- td.xyear = y2;\r
- }else{\r
- y2 = y - (5-Math.round(i * 0.5));\r
- td.firstChild.innerHTML = y2;\r
- td.xyear = y2;\r
- }\r
- this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');\r
- }\r
- },\r
-\r
- // private\r
- updateMPMonth : function(sm){\r
- this.mpMonths.each(function(m, a, i){\r
- m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');\r
- });\r
- },\r
-\r
- // private\r
- selectMPMonth : function(m){\r
-\r
- },\r
-\r
- // private\r
- onMonthClick : function(e, t){\r
- e.stopEvent();\r
- var el = new Ext.Element(t), pn;\r
- if(el.is('button.x-date-mp-cancel')){\r
- this.hideMonthPicker();\r
- }\r
- else if(el.is('button.x-date-mp-ok')){\r
- var d = new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate());\r
- if(d.getMonth() != this.mpSelMonth){\r
- // 'fix' the JS rolling date conversion if needed\r
- d = new Date(this.mpSelYear, this.mpSelMonth, 1).getLastDateOfMonth();\r
- }\r
- this.update(d);\r
- this.hideMonthPicker();\r
- }\r
- else if((pn = el.up('td.x-date-mp-month', 2))){\r
- this.mpMonths.removeClass('x-date-mp-sel');\r
- pn.addClass('x-date-mp-sel');\r
- this.mpSelMonth = pn.dom.xmonth;\r
- }\r
- else if((pn = el.up('td.x-date-mp-year', 2))){\r
- this.mpYears.removeClass('x-date-mp-sel');\r
- pn.addClass('x-date-mp-sel');\r
- this.mpSelYear = pn.dom.xyear;\r
- }\r
- else if(el.is('a.x-date-mp-prev')){\r
- this.updateMPYear(this.mpyear-10);\r
- }\r
- else if(el.is('a.x-date-mp-next')){\r
- this.updateMPYear(this.mpyear+10);\r
- }\r
- },\r
-\r
- // private\r
- onMonthDblClick : function(e, t){\r
- e.stopEvent();\r
- var el = new Ext.Element(t), pn;\r
- if((pn = el.up('td.x-date-mp-month', 2))){\r
- this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));\r
- this.hideMonthPicker();\r
- }\r
- else if((pn = el.up('td.x-date-mp-year', 2))){\r
- this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));\r
- this.hideMonthPicker();\r
- }\r
- },\r
-\r
- // private\r
- hideMonthPicker : function(disableAnim){\r
- if(this.monthPicker){\r
- if(disableAnim === true){\r
- this.monthPicker.hide();\r
- }else{\r
- this.monthPicker.slideOut('t', {duration:0.2});\r
- }\r
- }\r
- },\r
-\r
- // private\r
- showPrevMonth : function(e){\r
- this.update(this.activeDate.add('mo', -1));\r
- },\r
-\r
- // private\r
- showNextMonth : function(e){\r
- this.update(this.activeDate.add('mo', 1));\r
- },\r
-\r
- // private\r
- showPrevYear : function(){\r
- this.update(this.activeDate.add('y', -1));\r
- },\r
-\r
- // private\r
- showNextYear : function(){\r
- this.update(this.activeDate.add('y', 1));\r
- },\r
-\r
- // private\r
- handleMouseWheel : function(e){\r
- e.stopEvent();\r
- if(!this.disabled){\r
- var delta = e.getWheelDelta();\r
- if(delta > 0){\r
- this.showPrevMonth();\r
- } else if(delta < 0){\r
- this.showNextMonth();\r
- }\r
- }\r
- },\r
-\r
- // private\r
- handleDateClick : function(e, t){\r
- e.stopEvent();\r
- if(!this.disabled && t.dateValue && !Ext.fly(t.parentNode).hasClass('x-date-disabled')){\r
- this.setValue(new Date(t.dateValue));\r
- this.fireEvent('select', this, this.value);\r
- }\r
- },\r
-\r
- // private\r
- selectToday : function(){\r
- if(this.todayBtn && !this.todayBtn.disabled){\r
- this.setValue(new Date().clearTime());\r
- this.fireEvent('select', this, this.value);\r
- }\r
- },\r
-\r
- // private\r
- update : function(date, forceRefresh){\r
- var vd = this.activeDate, vis = this.isVisible();\r
- this.activeDate = date;\r
- if(!forceRefresh && vd && this.el){\r
- var t = date.getTime();\r
- if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){\r
- this.cells.removeClass('x-date-selected');\r
- this.cells.each(function(c){\r
- if(c.dom.firstChild.dateValue == t){\r
- c.addClass('x-date-selected');\r
- if(vis){\r
- Ext.fly(c.dom.firstChild).focus(50);\r
- }\r
- return false;\r
- }\r
- });\r
- return;\r
- }\r
- }\r
- var days = date.getDaysInMonth();\r
- var firstOfMonth = date.getFirstDateOfMonth();\r
- var startingPos = firstOfMonth.getDay()-this.startDay;\r
-\r
- if(startingPos <= this.startDay){\r
- startingPos += 7;\r
- }\r
-\r
- var pm = date.add('mo', -1);\r
- var prevStart = pm.getDaysInMonth()-startingPos;\r
-\r
- var cells = this.cells.elements;\r
- var textEls = this.textNodes;\r
- days += startingPos;\r
-\r
- // convert everything to numbers so it's fast\r
- var day = 86400000;\r
- var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();\r
- var today = new Date().clearTime().getTime();\r
- var sel = date.clearTime().getTime();\r
- var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;\r
- var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;\r
- var ddMatch = this.disabledDatesRE;\r
- var ddText = this.disabledDatesText;\r
- var ddays = this.disabledDays ? this.disabledDays.join('') : false;\r
- var ddaysText = this.disabledDaysText;\r
- var format = this.format;\r
-\r
- if(this.showToday){\r
- var td = new Date().clearTime();\r
- var disable = (td < min || td > max ||\r
- (ddMatch && format && ddMatch.test(td.dateFormat(format))) ||\r
- (ddays && ddays.indexOf(td.getDay()) != -1));\r
-\r
- if(!this.disabled){\r
- this.todayBtn.setDisabled(disable);\r
- this.todayKeyListener[disable ? 'disable' : 'enable']();\r
- }\r
- }\r
-\r
- var setCellClass = function(cal, cell){\r
- cell.title = '';\r
- var t = d.getTime();\r
- cell.firstChild.dateValue = t;\r
- if(t == today){\r
- cell.className += ' x-date-today';\r
- cell.title = cal.todayText;\r
- }\r
- if(t == sel){\r
- cell.className += ' x-date-selected';\r
- if(vis){\r
- Ext.fly(cell.firstChild).focus(50);\r
- }\r
- }\r
- // disabling\r
- if(t < min) {\r
- cell.className = ' x-date-disabled';\r
- cell.title = cal.minText;\r
- return;\r
- }\r
- if(t > max) {\r
- cell.className = ' x-date-disabled';\r
- cell.title = cal.maxText;\r
- return;\r
- }\r
- if(ddays){\r
- if(ddays.indexOf(d.getDay()) != -1){\r
- cell.title = ddaysText;\r
- cell.className = ' x-date-disabled';\r
- }\r
- }\r
- if(ddMatch && format){\r
- var fvalue = d.dateFormat(format);\r
- if(ddMatch.test(fvalue)){\r
- cell.title = ddText.replace('%0', fvalue);\r
- cell.className = ' x-date-disabled';\r
- }\r
- }\r
- };\r
-\r
- var i = 0;\r
- for(; i < startingPos; i++) {\r
- textEls[i].innerHTML = (++prevStart);\r
- d.setDate(d.getDate()+1);\r
- cells[i].className = 'x-date-prevday';\r
- setCellClass(this, cells[i]);\r
- }\r
- for(; i < days; i++){\r
- var intDay = i - startingPos + 1;\r
- textEls[i].innerHTML = (intDay);\r
- d.setDate(d.getDate()+1);\r
- cells[i].className = 'x-date-active';\r
- setCellClass(this, cells[i]);\r
- }\r
- var extraDays = 0;\r
- for(; i < 42; i++) {\r
- textEls[i].innerHTML = (++extraDays);\r
- d.setDate(d.getDate()+1);\r
- cells[i].className = 'x-date-nextday';\r
- setCellClass(this, cells[i]);\r
- }\r
-\r
- this.mbtn.setText(this.monthNames[date.getMonth()] + ' ' + date.getFullYear());\r
-\r
- if(!this.internalRender){\r
- var main = this.el.dom.firstChild;\r
- var w = main.offsetWidth;\r
- this.el.setWidth(w + this.el.getBorderWidth('lr'));\r
- Ext.fly(main).setWidth(w);\r
- this.internalRender = true;\r
- // opera does not respect the auto grow header center column\r
- // then, after it gets a width opera refuses to recalculate\r
- // without a second pass\r
- if(Ext.isOpera && !this.secondPass){\r
- main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + 'px';\r
- this.secondPass = true;\r
- this.update.defer(10, this, [date]);\r
- }\r
- }\r
- },\r
-\r
- // private\r
- beforeDestroy : function() {\r
- if(this.rendered){\r
- this.keyNav.disable();\r
- this.keyNav = null;\r
- Ext.destroy(\r
- this.leftClickRpt,\r
- this.rightClickRpt,\r
- this.monthPicker,\r
- this.eventEl,\r
- this.mbtn,\r
- this.todayBtn\r
- );\r
- }\r
- }\r
-\r
- /**\r
- * @cfg {String} autoEl @hide\r
- */\r
-});\r
-\r
-Ext.reg('datepicker', Ext.DatePicker);\r
+</code></pre>
+ */
+ /**
+ * @cfg {Boolean} autoHeight
+ * <code>true</code> to use height:'auto', <code>false</code> to use fixed height (defaults to <code>false</code>).
+ * <b>Note</b>: Setting <code>autoHeight: true</code> means that the browser will manage the panel's height
+ * based on its contents, and that Ext will not manage it at all. If the panel is within a layout that
+ * manages dimensions (<code>fit</code>, <code>border</code>, etc.) then setting <code>autoHeight: true</code>
+ * can cause issues with scrolling and will not generally work as expected since the panel will take
+ * on the height of its contents rather than the height required by the Ext layout.
+ */
+
+
+ /**
+ * @cfg {String} baseCls
+ * The base CSS class to apply to this panel's element (defaults to <code>'x-panel'</code>).
+ * <p>Another option available by default is to specify <code>'x-plain'</code> which strips all styling
+ * except for required attributes for Ext layouts to function (e.g. overflow:hidden).
+ * See <code>{@link #unstyled}</code> also.</p>
+ */
+ baseCls : 'x-panel',
+ /**
+ * @cfg {String} collapsedCls
+ * A CSS class to add to the panel's element after it has been collapsed (defaults to
+ * <code>'x-panel-collapsed'</code>).
+ */
+ collapsedCls : 'x-panel-collapsed',
+ /**
+ * @cfg {Boolean} maskDisabled
+ * <code>true</code> to mask the panel when it is {@link #disabled}, <code>false</code> to not mask it (defaults
+ * to <code>true</code>). Either way, the panel will always tell its contained elements to disable themselves
+ * when it is disabled, but masking the panel can provide an additional visual cue that the panel is
+ * disabled.
+ */
+ maskDisabled : true,
+ /**
+ * @cfg {Boolean} animCollapse
+ * <code>true</code> to animate the transition when the panel is collapsed, <code>false</code> to skip the
+ * animation (defaults to <code>true</code> if the {@link Ext.Fx} class is available, otherwise <code>false</code>).
+ */
+ animCollapse : Ext.enableFx,
+ /**
+ * @cfg {Boolean} headerAsText
+ * <code>true</code> to display the panel <code>{@link #title}</code> in the <code>{@link #header}</code>,
+ * <code>false</code> to hide it (defaults to <code>true</code>).
+ */
+ headerAsText : true,
+ /**
+ * @cfg {String} buttonAlign
+ * The alignment of any {@link #buttons} added to this panel. Valid values are <code>'right'</code>,
+ * <code>'left'</code> and <code>'center'</code> (defaults to <code>'right'</code>).
+ */
+ buttonAlign : 'right',
+ /**
+ * @cfg {Boolean} collapsed
+ * <code>true</code> to render the panel collapsed, <code>false</code> to render it expanded (defaults to
+ * <code>false</code>).
+ */
+ collapsed : false,
+ /**
+ * @cfg {Boolean} collapseFirst
+ * <code>true</code> to make sure the collapse/expand toggle button always renders first (to the left of)
+ * any other tools in the panel's title bar, <code>false</code> to render it last (defaults to <code>true</code>).
+ */
+ collapseFirst : true,
+ /**
+ * @cfg {Number} minButtonWidth
+ * Minimum width in pixels of all {@link #buttons} in this panel (defaults to <code>75</code>)
+ */
+ minButtonWidth : 75,
+ /**
+ * @cfg {Boolean} unstyled
+ * Overrides the <code>{@link #baseCls}</code> setting to <code>{@link #baseCls} = 'x-plain'</code> which renders
+ * the panel unstyled except for required attributes for Ext layouts to function (e.g. overflow:hidden).
+ */
+ /**
+ * @cfg {String} elements
+ * A comma-delimited list of panel elements to initialize when the panel is rendered. Normally, this list will be
+ * generated automatically based on the items added to the panel at config time, but sometimes it might be useful to
+ * make sure a structural element is rendered even if not specified at config time (for example, you may want
+ * to add a button or toolbar dynamically after the panel has been rendered). Adding those elements to this
+ * list will allocate the required placeholders in the panel when it is rendered. Valid values are<div class="mdetail-params"><ul>
+ * <li><code>header</code></li>
+ * <li><code>tbar</code> (top bar)</li>
+ * <li><code>body</code></li>
+ * <li><code>bbar</code> (bottom bar)</li>
+ * <li><code>footer</code></li>
+ * </ul></div>
+ * Defaults to '<code>body</code>'.
+ */
+ elements : 'body',
+ /**
+ * @cfg {Boolean} preventBodyReset
+ * Defaults to <code>false</code>. When set to <code>true</code>, an extra css class <code>'x-panel-normal'</code>
+ * will be added to the panel's element, effectively applying css styles suggested by the W3C
+ * (see http://www.w3.org/TR/CSS21/sample.html) to the Panel's <b>body</b> element (not the header,
+ * footer, etc.).
+ */
+ preventBodyReset : false,
+
+ /**
+ * @cfg {Number/String} padding
+ * A shortcut for setting a padding style on the body element. The value can either be
+ * a number to be applied to all sides, or a normal css string describing padding.
+ * Defaults to <tt>undefined</tt>.
+ *
+ */
+ padding: undefined,
+
+ /** @cfg {String} resizeEvent
+ * The event to listen to for resizing in layouts. Defaults to <tt>'bodyresize'</tt>.
+ */
+ resizeEvent: 'bodyresize',
+
+ // protected - these could be used to customize the behavior of the window,
+ // but changing them would not be useful without further mofifications and
+ // could lead to unexpected or undesirable results.
+ toolTarget : 'header',
+ collapseEl : 'bwrap',
+ slideAnchor : 't',
+ disabledClass : '',
+
+ // private, notify box this class will handle heights
+ deferHeight : true,
+ // private
+ expandDefaults: {
+ duration : 0.25
+ },
+ // private
+ collapseDefaults : {
+ duration : 0.25
+ },
+
+ // private
+ initComponent : function(){
+ Ext.Panel.superclass.initComponent.call(this);
+
+ this.addEvents(
+ /**
+ * @event bodyresize
+ * Fires after the Panel has been resized.
+ * @param {Ext.Panel} p the Panel which has been resized.
+ * @param {Number} width The Panel body's new width.
+ * @param {Number} height The Panel body's new height.
+ */
+ 'bodyresize',
+ /**
+ * @event titlechange
+ * Fires after the Panel title has been {@link #title set} or {@link #setTitle changed}.
+ * @param {Ext.Panel} p the Panel which has had its title changed.
+ * @param {String} The new title.
+ */
+ 'titlechange',
+ /**
+ * @event iconchange
+ * Fires after the Panel icon class has been {@link #iconCls set} or {@link #setIconClass changed}.
+ * @param {Ext.Panel} p the Panel which has had its {@link #iconCls icon class} changed.
+ * @param {String} The new icon class.
+ * @param {String} The old icon class.
+ */
+ 'iconchange',
+ /**
+ * @event collapse
+ * Fires after the Panel has been collapsed.
+ * @param {Ext.Panel} p the Panel that has been collapsed.
+ */
+ 'collapse',
+ /**
+ * @event expand
+ * Fires after the Panel has been expanded.
+ * @param {Ext.Panel} p The Panel that has been expanded.
+ */
+ 'expand',
+ /**
+ * @event beforecollapse
+ * Fires before the Panel is collapsed. A handler can return false to cancel the collapse.
+ * @param {Ext.Panel} p the Panel being collapsed.
+ * @param {Boolean} animate True if the collapse is animated, else false.
+ */
+ 'beforecollapse',
+ /**
+ * @event beforeexpand
+ * Fires before the Panel is expanded. A handler can return false to cancel the expand.
+ * @param {Ext.Panel} p The Panel being expanded.
+ * @param {Boolean} animate True if the expand is animated, else false.
+ */
+ 'beforeexpand',
+ /**
+ * @event beforeclose
+ * Fires before the Panel is closed. Note that Panels do not directly support being closed, but some
+ * Panel subclasses do (like {@link Ext.Window}) or a Panel within a Ext.TabPanel. This event only
+ * applies to such subclasses.
+ * A handler can return false to cancel the close.
+ * @param {Ext.Panel} p The Panel being closed.
+ */
+ 'beforeclose',
+ /**
+ * @event close
+ * Fires after the Panel is closed. Note that Panels do not directly support being closed, but some
+ * Panel subclasses do (like {@link Ext.Window}) or a Panel within a Ext.TabPanel.
+ * @param {Ext.Panel} p The Panel that has been closed.
+ */
+ 'close',
+ /**
+ * @event activate
+ * Fires after the Panel has been visually activated.
+ * Note that Panels do not directly support being activated, but some Panel subclasses
+ * do (like {@link Ext.Window}). Panels which are child Components of a TabPanel fire the
+ * activate and deactivate events under the control of the TabPanel.
+ * @param {Ext.Panel} p The Panel that has been activated.
+ */
+ 'activate',
+ /**
+ * @event deactivate
+ * Fires after the Panel has been visually deactivated.
+ * Note that Panels do not directly support being deactivated, but some Panel subclasses
+ * do (like {@link Ext.Window}). Panels which are child Components of a TabPanel fire the
+ * activate and deactivate events under the control of the TabPanel.
+ * @param {Ext.Panel} p The Panel that has been deactivated.
+ */
+ 'deactivate'
+ );
+
+ if(this.unstyled){
+ this.baseCls = 'x-plain';
+ }
+
+
+ this.toolbars = [];
+ // shortcuts
+ if(this.tbar){
+ this.elements += ',tbar';
+ this.topToolbar = this.createToolbar(this.tbar);
+ delete this.tbar;
+
+ }
+ if(this.bbar){
+ this.elements += ',bbar';
+ this.bottomToolbar = this.createToolbar(this.bbar);
+ delete this.bbar;
+ }
+
+ if(this.header === true){
+ this.elements += ',header';
+ delete this.header;
+ }else if(this.headerCfg || (this.title && this.header !== false)){
+ this.elements += ',header';
+ }
+
+ if(this.footerCfg || this.footer === true){
+ this.elements += ',footer';
+ delete this.footer;
+ }
+
+ if(this.buttons){
+ this.fbar = this.buttons;
+ delete this.buttons;
+ }
+ if(this.fbar){
+ this.createFbar(this.fbar);
+ }
+ if(this.autoLoad){
+ this.on('render', this.doAutoLoad, this, {delay:10});
+ }
+ },
+
+ // private
+ createFbar : function(fbar){
+ var min = this.minButtonWidth;
+ this.elements += ',footer';
+ this.fbar = this.createToolbar(fbar, {
+ buttonAlign: this.buttonAlign,
+ toolbarCls: 'x-panel-fbar',
+ enableOverflow: false,
+ defaults: function(c){
+ return {
+ minWidth: c.minWidth || min
+ };
+ }
+ });
+ //@compat addButton and buttons could possibly be removed
+ //@target 4.0
+ /**
+ * This Panel's Array of buttons as created from the <code>{@link #buttons}</code>
+ * config property. Read only.
+ * @type Array
+ * @property buttons
+ */
+ this.fbar.items.each(function(c){
+ c.minWidth = c.minWidth || this.minButtonWidth;
+ }, this);
+ this.buttons = this.fbar.items.items;
+ },
+
+ // private
+ createToolbar: function(tb, options){
+ var result;
+ // Convert array to proper toolbar config
+ if(Ext.isArray(tb)){
+ tb = {
+ items: tb
+ };
+ }
+ result = tb.events ? Ext.apply(tb, options) : this.createComponent(Ext.apply({}, tb, options), 'toolbar');
+ this.toolbars.push(result);
+ return result;
+ },
+
+ // private
+ createElement : function(name, pnode){
+ if(this[name]){
+ pnode.appendChild(this[name].dom);
+ return;
+ }
+
+ if(name === 'bwrap' || this.elements.indexOf(name) != -1){
+ if(this[name+'Cfg']){
+ this[name] = Ext.fly(pnode).createChild(this[name+'Cfg']);
+ }else{
+ var el = document.createElement('div');
+ el.className = this[name+'Cls'];
+ this[name] = Ext.get(pnode.appendChild(el));
+ }
+ if(this[name+'CssClass']){
+ this[name].addClass(this[name+'CssClass']);
+ }
+ if(this[name+'Style']){
+ this[name].applyStyles(this[name+'Style']);
+ }
+ }
+ },
+
+ // private
+ onRender : function(ct, position){
+ Ext.Panel.superclass.onRender.call(this, ct, position);
+ this.createClasses();
+
+ var el = this.el,
+ d = el.dom,
+ bw,
+ ts;
+
+
+ if(this.collapsible && !this.hideCollapseTool){
+ this.tools = this.tools ? this.tools.slice(0) : [];
+ this.tools[this.collapseFirst?'unshift':'push']({
+ id: 'toggle',
+ handler : this.toggleCollapse,
+ scope: this
+ });
+ }
+
+ if(this.tools){
+ ts = this.tools;
+ this.elements += (this.header !== false) ? ',header' : '';
+ }
+ this.tools = {};
+
+ el.addClass(this.baseCls);
+ if(d.firstChild){ // existing markup
+ this.header = el.down('.'+this.headerCls);
+ this.bwrap = el.down('.'+this.bwrapCls);
+ var cp = this.bwrap ? this.bwrap : el;
+ this.tbar = cp.down('.'+this.tbarCls);
+ this.body = cp.down('.'+this.bodyCls);
+ this.bbar = cp.down('.'+this.bbarCls);
+ this.footer = cp.down('.'+this.footerCls);
+ this.fromMarkup = true;
+ }
+ if (this.preventBodyReset === true) {
+ el.addClass('x-panel-reset');
+ }
+ if(this.cls){
+ el.addClass(this.cls);
+ }
+
+ if(this.buttons){
+ this.elements += ',footer';
+ }
+
+ // This block allows for maximum flexibility and performance when using existing markup
+
+ // framing requires special markup
+ if(this.frame){
+ el.insertHtml('afterBegin', String.format(Ext.Element.boxMarkup, this.baseCls));
+
+ this.createElement('header', d.firstChild.firstChild.firstChild);
+ this.createElement('bwrap', d);
+
+ // append the mid and bottom frame to the bwrap
+ bw = this.bwrap.dom;
+ var ml = d.childNodes[1], bl = d.childNodes[2];
+ bw.appendChild(ml);
+ bw.appendChild(bl);
+
+ var mc = bw.firstChild.firstChild.firstChild;
+ this.createElement('tbar', mc);
+ this.createElement('body', mc);
+ this.createElement('bbar', mc);
+ this.createElement('footer', bw.lastChild.firstChild.firstChild);
+
+ if(!this.footer){
+ this.bwrap.dom.lastChild.className += ' x-panel-nofooter';
+ }
+ /*
+ * Store a reference to this element so:
+ * a) We aren't looking it up all the time
+ * b) The last element is reported incorrectly when using a loadmask
+ */
+ this.ft = Ext.get(this.bwrap.dom.lastChild);
+ this.mc = Ext.get(mc);
+ }else{
+ this.createElement('header', d);
+ this.createElement('bwrap', d);
+
+ // append the mid and bottom frame to the bwrap
+ bw = this.bwrap.dom;
+ this.createElement('tbar', bw);
+ this.createElement('body', bw);
+ this.createElement('bbar', bw);
+ this.createElement('footer', bw);
+
+ if(!this.header){
+ this.body.addClass(this.bodyCls + '-noheader');
+ if(this.tbar){
+ this.tbar.addClass(this.tbarCls + '-noheader');
+ }
+ }
+ }
+
+ if(Ext.isDefined(this.padding)){
+ this.body.setStyle('padding', this.body.addUnits(this.padding));
+ }
+
+ if(this.border === false){
+ this.el.addClass(this.baseCls + '-noborder');
+ this.body.addClass(this.bodyCls + '-noborder');
+ if(this.header){
+ this.header.addClass(this.headerCls + '-noborder');
+ }
+ if(this.footer){
+ this.footer.addClass(this.footerCls + '-noborder');
+ }
+ if(this.tbar){
+ this.tbar.addClass(this.tbarCls + '-noborder');
+ }
+ if(this.bbar){
+ this.bbar.addClass(this.bbarCls + '-noborder');
+ }
+ }
+
+ if(this.bodyBorder === false){
+ this.body.addClass(this.bodyCls + '-noborder');
+ }
+
+ this.bwrap.enableDisplayMode('block');
+
+ if(this.header){
+ this.header.unselectable();
+
+ // for tools, we need to wrap any existing header markup
+ if(this.headerAsText){
+ this.header.dom.innerHTML =
+ '<span class="' + this.headerTextCls + '">'+this.header.dom.innerHTML+'</span>';
+
+ if(this.iconCls){
+ this.setIconClass(this.iconCls);
+ }
+ }
+ }
+
+ if(this.floating){
+ this.makeFloating(this.floating);
+ }
+
+ if(this.collapsible && this.titleCollapse && this.header){
+ this.mon(this.header, 'click', this.toggleCollapse, this);
+ this.header.setStyle('cursor', 'pointer');
+ }
+ if(ts){
+ this.addTool.apply(this, ts);
+ }
+
+ // Render Toolbars.
+ if(this.fbar){
+ this.footer.addClass('x-panel-btns');
+ this.fbar.ownerCt = this;
+ this.fbar.render(this.footer);
+ this.footer.createChild({cls:'x-clear'});
+ }
+ if(this.tbar && this.topToolbar){
+ this.topToolbar.ownerCt = this;
+ this.topToolbar.render(this.tbar);
+ }
+ if(this.bbar && this.bottomToolbar){
+ this.bottomToolbar.ownerCt = this;
+ this.bottomToolbar.render(this.bbar);
+ }
+ },
+
+ /**
+ * Sets the CSS class that provides the icon image for this panel. This method will replace any existing
+ * icon class if one has already been set and fire the {@link #iconchange} event after completion.
+ * @param {String} cls The new CSS class name
+ */
+ setIconClass : function(cls){
+ var old = this.iconCls;
+ this.iconCls = cls;
+ if(this.rendered && this.header){
+ if(this.frame){
+ this.header.addClass('x-panel-icon');
+ this.header.replaceClass(old, this.iconCls);
+ }else{
+ var hd = this.header,
+ img = hd.child('img.x-panel-inline-icon');
+ if(img){
+ Ext.fly(img).replaceClass(old, this.iconCls);
+ }else{
+ Ext.DomHelper.insertBefore(hd.dom.firstChild, {
+ tag:'img', src: Ext.BLANK_IMAGE_URL, cls:'x-panel-inline-icon '+this.iconCls
+ });
+ }
+ }
+ }
+ this.fireEvent('iconchange', this, cls, old);
+ },
+
+ // private
+ makeFloating : function(cfg){
+ this.floating = true;
+ this.el = new Ext.Layer(Ext.apply({}, cfg, {
+ shadow: Ext.isDefined(this.shadow) ? this.shadow : 'sides',
+ shadowOffset: this.shadowOffset,
+ constrain:false,
+ shim: this.shim === false ? false : undefined
+ }), this.el);
+ },
+
+ /**
+ * Returns the {@link Ext.Toolbar toolbar} from the top (<code>{@link #tbar}</code>) section of the panel.
+ * @return {Ext.Toolbar} The toolbar
+ */
+ getTopToolbar : function(){
+ return this.topToolbar;
+ },
+
+ /**
+ * Returns the {@link Ext.Toolbar toolbar} from the bottom (<code>{@link #bbar}</code>) section of the panel.
+ * @return {Ext.Toolbar} The toolbar
+ */
+ getBottomToolbar : function(){
+ return this.bottomToolbar;
+ },
+
+ /**
+ * Adds a button to this panel. Note that this method must be called prior to rendering. The preferred
+ * approach is to add buttons via the {@link #buttons} config.
+ * @param {String/Object} config A valid {@link Ext.Button} config. A string will become the text for a default
+ * button config, an object will be treated as a button config object.
+ * @param {Function} handler The function to be called on button {@link Ext.Button#click}
+ * @param {Object} scope The scope (<code>this</code> reference) in which the button handler function is executed. Defaults to the Button.
+ * @return {Ext.Button} The button that was added
+ */
+ addButton : function(config, handler, scope){
+ if(!this.fbar){
+ this.createFbar([]);
+ }
+ if(handler){
+ if(Ext.isString(config)){
+ config = {text: config};
+ }
+ config = Ext.apply({
+ handler: handler,
+ scope: scope
+ }, config)
+ }
+ return this.fbar.add(config);
+ },
+
+ // private
+ addTool : function(){
+ if(!this.rendered){
+ if(!this.tools){
+ this.tools = [];
+ }
+ Ext.each(arguments, function(arg){
+ this.tools.push(arg)
+ }, this);
+ return;
+ }
+ // nowhere to render tools!
+ if(!this[this.toolTarget]){
+ return;
+ }
+ if(!this.toolTemplate){
+ // initialize the global tool template on first use
+ var tt = new Ext.Template(
+ '<div class="x-tool x-tool-{id}"> </div>'
+ );
+ tt.disableFormats = true;
+ tt.compile();
+ Ext.Panel.prototype.toolTemplate = tt;
+ }
+ for(var i = 0, a = arguments, len = a.length; i < len; i++) {
+ var tc = a[i];
+ if(!this.tools[tc.id]){
+ var overCls = 'x-tool-'+tc.id+'-over';
+ var t = this.toolTemplate.insertFirst(this[this.toolTarget], tc, true);
+ this.tools[tc.id] = t;
+ t.enableDisplayMode('block');
+ this.mon(t, 'click', this.createToolHandler(t, tc, overCls, this));
+ if(tc.on){
+ this.mon(t, tc.on);
+ }
+ if(tc.hidden){
+ t.hide();
+ }
+ if(tc.qtip){
+ if(Ext.isObject(tc.qtip)){
+ Ext.QuickTips.register(Ext.apply({
+ target: t.id
+ }, tc.qtip));
+ } else {
+ t.dom.qtip = tc.qtip;
+ }
+ }
+ t.addClassOnOver(overCls);
+ }
+ }
+ },
+
+ onLayout : function(shallow, force){
+ Ext.Panel.superclass.onLayout.apply(this, arguments);
+ if(this.hasLayout && this.toolbars.length > 0){
+ Ext.each(this.toolbars, function(tb){
+ tb.doLayout(undefined, force);
+ });
+ this.syncHeight();
+ }
+ },
+
+ syncHeight : function(){
+ var h = this.toolbarHeight,
+ bd = this.body,
+ lsh = this.lastSize.height,
+ sz;
+
+ if(this.autoHeight || !Ext.isDefined(lsh) || lsh == 'auto'){
+ return;
+ }
+
+
+ if(h != this.getToolbarHeight()){
+ h = Math.max(0, this.adjustBodyHeight(lsh - this.getFrameHeight()));
+ bd.setHeight(h);
+ sz = bd.getSize();
+ this.toolbarHeight = this.getToolbarHeight();
+ this.onBodyResize(sz.width, sz.height);
+ }
+ },
+
+ // private
+ onShow : function(){
+ if(this.floating){
+ return this.el.show();
+ }
+ Ext.Panel.superclass.onShow.call(this);
+ },
+
+ // private
+ onHide : function(){
+ if(this.floating){
+ return this.el.hide();
+ }
+ Ext.Panel.superclass.onHide.call(this);
+ },
+
+ // private
+ createToolHandler : function(t, tc, overCls, panel){
+ return function(e){
+ t.removeClass(overCls);
+ if(tc.stopEvent !== false){
+ e.stopEvent();
+ }
+ if(tc.handler){
+ tc.handler.call(tc.scope || t, e, t, panel, tc);
+ }
+ };
+ },
+
+ // private
+ afterRender : function(){
+ if(this.floating && !this.hidden){
+ this.el.show();
+ }
+ if(this.title){
+ this.setTitle(this.title);
+ }
+ Ext.Panel.superclass.afterRender.call(this); // do sizing calcs last
+ if (this.collapsed) {
+ this.collapsed = false;
+ this.collapse(false);
+ }
+ this.initEvents();
+ },
+
+ // private
+ getKeyMap : function(){
+ if(!this.keyMap){
+ this.keyMap = new Ext.KeyMap(this.el, this.keys);
+ }
+ return this.keyMap;
+ },
+
+ // private
+ initEvents : function(){
+ if(this.keys){
+ this.getKeyMap();
+ }
+ if(this.draggable){
+ this.initDraggable();
+ }
+ if(this.toolbars.length > 0){
+ Ext.each(this.toolbars, function(tb){
+ tb.doLayout();
+ tb.on({
+ scope: this,
+ afterlayout: this.syncHeight,
+ remove: this.syncHeight
+ });
+ }, this);
+ this.syncHeight();
+ }
+
+ },
+
+ // private
+ initDraggable : function(){
+ /**
+ * <p>If this Panel is configured {@link #draggable}, this property will contain
+ * an instance of {@link Ext.dd.DragSource} which handles dragging the Panel.</p>
+ * The developer must provide implementations of the abstract methods of {@link Ext.dd.DragSource}
+ * in order to supply behaviour for each stage of the drag/drop process. See {@link #draggable}.
+ * @type Ext.dd.DragSource.
+ * @property dd
+ */
+ this.dd = new Ext.Panel.DD(this, Ext.isBoolean(this.draggable) ? null : this.draggable);
+ },
+
+ // private
+ beforeEffect : function(anim){
+ if(this.floating){
+ this.el.beforeAction();
+ }
+ if(anim !== false){
+ this.el.addClass('x-panel-animated');
+ }
+ },
+
+ // private
+ afterEffect : function(anim){
+ this.syncShadow();
+ if(anim !== false){
+ this.el.removeClass('x-panel-animated');
+ }
+ },
+
+ // private - wraps up an animation param with internal callbacks
+ createEffect : function(a, cb, scope){
+ var o = {
+ scope:scope,
+ block:true
+ };
+ if(a === true){
+ o.callback = cb;
+ return o;
+ }else if(!a.callback){
+ o.callback = cb;
+ }else { // wrap it up
+ o.callback = function(){
+ cb.call(scope);
+ Ext.callback(a.callback, a.scope);
+ };
+ }
+ return Ext.applyIf(o, a);
+ },
+
+ /**
+ * Collapses the panel body so that it becomes hidden. Fires the {@link #beforecollapse} event which will
+ * cancel the collapse action if it returns false.
+ * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
+ * {@link #animCollapse} panel config)
+ * @return {Ext.Panel} this
+ */
+ collapse : function(animate){
+ if(this.collapsed || this.el.hasFxBlock() || this.fireEvent('beforecollapse', this, animate) === false){
+ return;
+ }
+ var doAnim = animate === true || (animate !== false && this.animCollapse);
+ this.beforeEffect(doAnim);
+ this.onCollapse(doAnim, animate);
+ return this;
+ },
+
+ // private
+ onCollapse : function(doAnim, animArg){
+ if(doAnim){
+ this[this.collapseEl].slideOut(this.slideAnchor,
+ Ext.apply(this.createEffect(animArg||true, this.afterCollapse, this),
+ this.collapseDefaults));
+ }else{
+ this[this.collapseEl].hide();
+ this.afterCollapse(false);
+ }
+ },
+
+ // private
+ afterCollapse : function(anim){
+ this.collapsed = true;
+ this.el.addClass(this.collapsedCls);
+ this.afterEffect(anim);
+ this.fireEvent('collapse', this);
+ },
+
+ /**
+ * Expands the panel body so that it becomes visible. Fires the {@link #beforeexpand} event which will
+ * cancel the expand action if it returns false.
+ * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
+ * {@link #animCollapse} panel config)
+ * @return {Ext.Panel} this
+ */
+ expand : function(animate){
+ if(!this.collapsed || this.el.hasFxBlock() || this.fireEvent('beforeexpand', this, animate) === false){
+ return;
+ }
+ var doAnim = animate === true || (animate !== false && this.animCollapse);
+ this.el.removeClass(this.collapsedCls);
+ this.beforeEffect(doAnim);
+ this.onExpand(doAnim, animate);
+ return this;
+ },
+
+ // private
+ onExpand : function(doAnim, animArg){
+ if(doAnim){
+ this[this.collapseEl].slideIn(this.slideAnchor,
+ Ext.apply(this.createEffect(animArg||true, this.afterExpand, this),
+ this.expandDefaults));
+ }else{
+ this[this.collapseEl].show();
+ this.afterExpand(false);
+ }
+ },
+
+ // private
+ afterExpand : function(anim){
+ this.collapsed = false;
+ this.afterEffect(anim);
+ if (this.deferLayout) {
+ delete this.deferLayout;
+ this.doLayout(true);
+ }
+ this.fireEvent('expand', this);
+ },
+
+ /**
+ * Shortcut for performing an {@link #expand} or {@link #collapse} based on the current state of the panel.
+ * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
+ * {@link #animCollapse} panel config)
+ * @return {Ext.Panel} this
+ */
+ toggleCollapse : function(animate){
+ this[this.collapsed ? 'expand' : 'collapse'](animate);
+ return this;
+ },
+
+ // private
+ onDisable : function(){
+ if(this.rendered && this.maskDisabled){
+ this.el.mask();
+ }
+ Ext.Panel.superclass.onDisable.call(this);
+ },
+
+ // private
+ onEnable : function(){
+ if(this.rendered && this.maskDisabled){
+ this.el.unmask();
+ }
+ Ext.Panel.superclass.onEnable.call(this);
+ },
+
+ // private
+ onResize : function(w, h){
+ if(Ext.isDefined(w) || Ext.isDefined(h)){
+ if(!this.collapsed){
+ // First, set the the Panel's body width.
+ // If we have auto-widthed it, get the resulting full offset width so we can size the Toolbars to match
+ // The Toolbars must not buffer this resize operation because we need to know their heights.
+
+ if(Ext.isNumber(w)){
+ this.body.setWidth(w = this.adjustBodyWidth(w - this.getFrameWidth()));
+ } else if (w == 'auto') {
+ w = this.body.setWidth('auto').dom.offsetWidth;
+ } else {
+ w = this.body.dom.offsetWidth;
+ }
+
+ if(this.tbar){
+ this.tbar.setWidth(w);
+ if(this.topToolbar){
+ this.topToolbar.setSize(w);
+ }
+ }
+ if(this.bbar){
+ this.bbar.setWidth(w);
+ if(this.bottomToolbar){
+ this.bottomToolbar.setSize(w);
+ // The bbar does not move on resize without this.
+ if (Ext.isIE) {
+ this.bbar.setStyle('position', 'static');
+ this.bbar.setStyle('position', '');
+ }
+ }
+ }
+ if(this.footer){
+ this.footer.setWidth(w);
+ if(this.fbar){
+ this.fbar.setSize(Ext.isIE ? (w - this.footer.getFrameWidth('lr')) : 'auto');
+ }
+ }
+
+ // At this point, the Toolbars must be layed out for getFrameHeight to find a result.
+ if(Ext.isNumber(h)){
+ h = Math.max(0, this.adjustBodyHeight(h - this.getFrameHeight()));
+ this.body.setHeight(h);
+ }else if(h == 'auto'){
+ this.body.setHeight(h);
+ }
+
+ if(this.disabled && this.el._mask){
+ this.el._mask.setSize(this.el.dom.clientWidth, this.el.getHeight());
+ }
+ }else{
+ // Adds an event to set the correct height afterExpand. This accounts for the deferHeight flag in panel
+ this.queuedBodySize = {width: w, height: h};
+ if(!this.queuedExpand && this.allowQueuedExpand !== false){
+ this.queuedExpand = true;
+ this.on('expand', function(){
+ delete this.queuedExpand;
+ this.onResize(this.queuedBodySize.width, this.queuedBodySize.height);
+ }, this, {single:true});
+ }
+ }
+ this.onBodyResize(w, h);
+ }
+ this.syncShadow();
+ Ext.Panel.superclass.onResize.call(this);
+ },
+
+ // private
+ onBodyResize: function(w, h){
+ this.fireEvent('bodyresize', this, w, h);
+ },
+
+ // private
+ getToolbarHeight: function(){
+ var h = 0;
+ if(this.rendered){
+ Ext.each(this.toolbars, function(tb){
+ h += tb.getHeight();
+ }, this);
+ }
+ return h;
+ },
+
+ // private
+ adjustBodyHeight : function(h){
+ return h;
+ },
+
+ // private
+ adjustBodyWidth : function(w){
+ return w;
+ },
+
+ // private
+ onPosition : function(){
+ this.syncShadow();
+ },
+
+ /**
+ * Returns the width in pixels of the framing elements of this panel (not including the body width). To
+ * retrieve the body width see {@link #getInnerWidth}.
+ * @return {Number} The frame width
+ */
+ getFrameWidth : function(){
+ var w = this.el.getFrameWidth('lr') + this.bwrap.getFrameWidth('lr');
+
+ if(this.frame){
+ var l = this.bwrap.dom.firstChild;
+ w += (Ext.fly(l).getFrameWidth('l') + Ext.fly(l.firstChild).getFrameWidth('r'));
+ w += this.mc.getFrameWidth('lr');
+ }
+ return w;
+ },
+
+ /**
+ * Returns the height in pixels of the framing elements of this panel (including any top and bottom bars and
+ * header and footer elements, but not including the body height). To retrieve the body height see {@link #getInnerHeight}.
+ * @return {Number} The frame height
+ */
+ getFrameHeight : function(){
+ var h = this.el.getFrameWidth('tb') + this.bwrap.getFrameWidth('tb');
+ h += (this.tbar ? this.tbar.getHeight() : 0) +
+ (this.bbar ? this.bbar.getHeight() : 0);
+
+ if(this.frame){
+ h += this.el.dom.firstChild.offsetHeight + this.ft.dom.offsetHeight + this.mc.getFrameWidth('tb');
+ }else{
+ h += (this.header ? this.header.getHeight() : 0) +
+ (this.footer ? this.footer.getHeight() : 0);
+ }
+ return h;
+ },
+
+ /**
+ * Returns the width in pixels of the body element (not including the width of any framing elements).
+ * For the frame width see {@link #getFrameWidth}.
+ * @return {Number} The body width
+ */
+ getInnerWidth : function(){
+ return this.getSize().width - this.getFrameWidth();
+ },
+
+ /**
+ * Returns the height in pixels of the body element (not including the height of any framing elements).
+ * For the frame height see {@link #getFrameHeight}.
+ * @return {Number} The body height
+ */
+ getInnerHeight : function(){
+ return this.getSize().height - this.getFrameHeight();
+ },
+
+ // private
+ syncShadow : function(){
+ if(this.floating){
+ this.el.sync(true);
+ }
+ },
+
+ // private
+ getLayoutTarget : function(){
+ return this.body;
+ },
+
+ // private
+ getContentTarget : function(){
+ return this.body;
+ },
+
+ /**
+ * <p>Sets the title text for the panel and optionally the {@link #iconCls icon class}.</p>
+ * <p>In order to be able to set the title, a header element must have been created
+ * for the Panel. This is triggered either by configuring the Panel with a non-blank <code>{@link #title}</code>,
+ * or configuring it with <code><b>{@link #header}: true</b></code>.</p>
+ * @param {String} title The title text to set
+ * @param {String} iconCls (optional) {@link #iconCls iconCls} A user-defined CSS class that provides the icon image for this panel
+ */
+ setTitle : function(title, iconCls){
+ this.title = title;
+ if(this.header && this.headerAsText){
+ this.header.child('span').update(title);
+ }
+ if(iconCls){
+ this.setIconClass(iconCls);
+ }
+ this.fireEvent('titlechange', this, title);
+ return this;
+ },
+
+ /**
+ * Get the {@link Ext.Updater} for this panel. Enables you to perform Ajax updates of this panel's body.
+ * @return {Ext.Updater} The Updater
+ */
+ getUpdater : function(){
+ return this.body.getUpdater();
+ },
+
+ /**
+ * Loads this content panel immediately with content returned from an XHR call.
+ * @param {Object/String/Function} config A config object containing any of the following options:
+<pre><code>
+panel.load({
+ url: 'your-url.php',
+ params: {param1: 'foo', param2: 'bar'}, // or a URL encoded string
+ callback: yourFunction,
+ scope: yourObject, // optional scope for the callback
+ discardUrl: false,
+ nocache: false,
+ text: 'Loading...',
+ timeout: 30,
+ scripts: false
+});
+</code></pre>
+ * The only required property is url. The optional properties nocache, text and scripts
+ * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their
+ * associated property on this panel Updater instance.
+ * @return {Ext.Panel} this
+ */
+ load : function(){
+ var um = this.body.getUpdater();
+ um.update.apply(um, arguments);
+ return this;
+ },
+
+ // private
+ beforeDestroy : function(){
+ Ext.Panel.superclass.beforeDestroy.call(this);
+ if(this.header){
+ this.header.removeAllListeners();
+ }
+ if(this.tools){
+ for(var k in this.tools){
+ Ext.destroy(this.tools[k]);
+ }
+ }
+ if(this.toolbars.length > 0){
+ Ext.each(this.toolbars, function(tb){
+ tb.un('afterlayout', this.syncHeight, this);
+ tb.un('remove', this.syncHeight, this);
+ }, this);
+ }
+ if(Ext.isArray(this.buttons)){
+ while(this.buttons.length) {
+ Ext.destroy(this.buttons[0]);
+ }
+ }
+ if(this.rendered){
+ Ext.destroy(
+ this.ft,
+ this.header,
+ this.footer,
+ this.toolbars,
+ this.tbar,
+ this.bbar,
+ this.body,
+ this.mc,
+ this.bwrap
+ );
+ if (this.fbar) {
+ Ext.destroy(
+ this.fbar,
+ this.fbar.el
+ );
+ }
+ }else{
+ Ext.destroy(
+ this.topToolbar,
+ this.bottomToolbar
+ );
+ }
+ },
+
+ // private
+ createClasses : function(){
+ this.headerCls = this.baseCls + '-header';
+ this.headerTextCls = this.baseCls + '-header-text';
+ this.bwrapCls = this.baseCls + '-bwrap';
+ this.tbarCls = this.baseCls + '-tbar';
+ this.bodyCls = this.baseCls + '-body';
+ this.bbarCls = this.baseCls + '-bbar';
+ this.footerCls = this.baseCls + '-footer';
+ },
+
+ // private
+ createGhost : function(cls, useShim, appendTo){
+ var el = document.createElement('div');
+ el.className = 'x-panel-ghost ' + (cls ? cls : '');
+ if(this.header){
+ el.appendChild(this.el.dom.firstChild.cloneNode(true));
+ }
+ Ext.fly(el.appendChild(document.createElement('ul'))).setHeight(this.bwrap.getHeight());
+ el.style.width = this.el.dom.offsetWidth + 'px';;
+ if(!appendTo){
+ this.container.dom.appendChild(el);
+ }else{
+ Ext.getDom(appendTo).appendChild(el);
+ }
+ if(useShim !== false && this.el.useShim !== false){
+ var layer = new Ext.Layer({shadow:false, useDisplay:true, constrain:false}, el);
+ layer.show();
+ return layer;
+ }else{
+ return new Ext.Element(el);
+ }
+ },
+
+ // private
+ doAutoLoad : function(){
+ var u = this.body.getUpdater();
+ if(this.renderer){
+ u.setRenderer(this.renderer);
+ }
+ u.update(Ext.isObject(this.autoLoad) ? this.autoLoad : {url: this.autoLoad});
+ },
+
+ /**
+ * Retrieve a tool by id.
+ * @param {String} id
+ * @return {Object} tool
+ */
+ getTool : function(id) {
+ return this.tools[id];
+ }
+
+/**
+ * @cfg {String} autoEl @hide
+ */
+});
+Ext.reg('panel', Ext.Panel);
+/**
+ * @class Ext.Editor
+ * @extends Ext.Component
+ * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
+ * @constructor
+ * Create a new Editor
+ * @param {Object} config The config object
+ * @xtype editor
+ */
+Ext.Editor = function(field, config){
+ if(field.field){
+ this.field = Ext.create(field.field, 'textfield');
+ config = Ext.apply({}, field); // copy so we don't disturb original config
+ delete config.field;
+ }else{
+ this.field = field;
+ }
+ Ext.Editor.superclass.constructor.call(this, config);
+};
+
+Ext.extend(Ext.Editor, Ext.Component, {
+ /**
+ * @cfg {Ext.form.Field} field
+ * The Field object (or descendant) or config object for field
+ */
+ /**
+ * @cfg {Boolean} allowBlur
+ * True to {@link #completeEdit complete the editing process} if in edit mode when the
+ * field is blurred. Defaults to <tt>false</tt>.
+ */
+ /**
+ * @cfg {Boolean/String} autoSize
+ * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
+ * or "height" to adopt the height only, "none" to always use the field dimensions. (defaults to false)
+ */
+ /**
+ * @cfg {Boolean} revertInvalid
+ * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
+ * validation fails (defaults to true)
+ */
+ /**
+ * @cfg {Boolean} ignoreNoChange
+ * True to skip the edit completion process (no save, no events fired) if the user completes an edit and
+ * the value has not changed (defaults to false). Applies only to string values - edits for other data types
+ * will never be ignored.
+ */
+ /**
+ * @cfg {Boolean} hideEl
+ * False to keep the bound element visible while the editor is displayed (defaults to true)
+ */
+ /**
+ * @cfg {Mixed} value
+ * The data value of the underlying field (defaults to "")
+ */
+ value : "",
+ /**
+ * @cfg {String} alignment
+ * The position to align to (see {@link Ext.Element#alignTo} for more details, defaults to "c-c?").
+ */
+ alignment: "c-c?",
+ /**
+ * @cfg {Array} offsets
+ * The offsets to use when aligning (see {@link Ext.Element#alignTo} for more details. Defaults to <tt>[0, 0]</tt>.
+ */
+ offsets: [0, 0],
+ /**
+ * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
+ * for bottom-right shadow (defaults to "frame")
+ */
+ shadow : "frame",
+ /**
+ * @cfg {Boolean} constrain True to constrain the editor to the viewport
+ */
+ constrain : false,
+ /**
+ * @cfg {Boolean} swallowKeys Handle the keydown/keypress events so they don't propagate (defaults to true)
+ */
+ swallowKeys : true,
+ /**
+ * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed. Defaults to <tt>true</tt>.
+ */
+ completeOnEnter : true,
+ /**
+ * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed. Defaults to <tt>true</tt>.
+ */
+ cancelOnEsc : true,
+ /**
+ * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
+ */
+ updateEl : false,
+
+ initComponent : function(){
+ Ext.Editor.superclass.initComponent.call(this);
+ this.addEvents(
+ /**
+ * @event beforestartedit
+ * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
+ * false from the handler of this event.
+ * @param {Editor} this
+ * @param {Ext.Element} boundEl The underlying element bound to this editor
+ * @param {Mixed} value The field value being set
+ */
+ "beforestartedit",
+ /**
+ * @event startedit
+ * Fires when this editor is displayed
+ * @param {Ext.Element} boundEl The underlying element bound to this editor
+ * @param {Mixed} value The starting field value
+ */
+ "startedit",
+ /**
+ * @event beforecomplete
+ * Fires after a change has been made to the field, but before the change is reflected in the underlying
+ * field. Saving the change to the field can be canceled by returning false from the handler of this event.
+ * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
+ * event will not fire since no edit actually occurred.
+ * @param {Editor} this
+ * @param {Mixed} value The current field value
+ * @param {Mixed} startValue The original field value
+ */
+ "beforecomplete",
+ /**
+ * @event complete
+ * Fires after editing is complete and any changed value has been written to the underlying field.
+ * @param {Editor} this
+ * @param {Mixed} value The current field value
+ * @param {Mixed} startValue The original field value
+ */
+ "complete",
+ /**
+ * @event canceledit
+ * Fires after editing has been canceled and the editor's value has been reset.
+ * @param {Editor} this
+ * @param {Mixed} value The user-entered field value that was discarded
+ * @param {Mixed} startValue The original field value that was set back into the editor after cancel
+ */
+ "canceledit",
+ /**
+ * @event specialkey
+ * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
+ * {@link Ext.EventObject#getKey} to determine which key was pressed.
+ * @param {Ext.form.Field} this
+ * @param {Ext.EventObject} e The event object
+ */
+ "specialkey"
+ );
+ },
+
+ // private
+ onRender : function(ct, position){
+ this.el = new Ext.Layer({
+ shadow: this.shadow,
+ cls: "x-editor",
+ parentEl : ct,
+ shim : this.shim,
+ shadowOffset: this.shadowOffset || 4,
+ id: this.id,
+ constrain: this.constrain
+ });
+ if(this.zIndex){
+ this.el.setZIndex(this.zIndex);
+ }
+ this.el.setStyle("overflow", Ext.isGecko ? "auto" : "hidden");
+ if(this.field.msgTarget != 'title'){
+ this.field.msgTarget = 'qtip';
+ }
+ this.field.inEditor = true;
+ this.mon(this.field, {
+ scope: this,
+ blur: this.onBlur,
+ specialkey: this.onSpecialKey
+ });
+ if(this.field.grow){
+ this.mon(this.field, "autosize", this.el.sync, this.el, {delay:1});
+ }
+ this.field.render(this.el).show();
+ this.field.getEl().dom.name = '';
+ if(this.swallowKeys){
+ this.field.el.swallowEvent([
+ 'keypress', // *** Opera
+ 'keydown' // *** all other browsers
+ ]);
+ }
+ },
+
+ // private
+ onSpecialKey : function(field, e){
+ var key = e.getKey(),
+ complete = this.completeOnEnter && key == e.ENTER,
+ cancel = this.cancelOnEsc && key == e.ESC;
+ if(complete || cancel){
+ e.stopEvent();
+ if(complete){
+ this.completeEdit();
+ }else{
+ this.cancelEdit();
+ }
+ if(field.triggerBlur){
+ field.triggerBlur();
+ }
+ }
+ this.fireEvent('specialkey', field, e);
+ },
+
+ /**
+ * Starts the editing process and shows the editor.
+ * @param {Mixed} el The element to edit
+ * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
+ * to the innerHTML of el.
+ */
+ startEdit : function(el, value){
+ if(this.editing){
+ this.completeEdit();
+ }
+ this.boundEl = Ext.get(el);
+ var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
+ if(!this.rendered){
+ this.render(this.parentEl || document.body);
+ }
+ if(this.fireEvent("beforestartedit", this, this.boundEl, v) !== false){
+ this.startValue = v;
+ this.field.reset();
+ this.field.setValue(v);
+ this.realign(true);
+ this.editing = true;
+ this.show();
+ }
+ },
+
+ // private
+ doAutoSize : function(){
+ if(this.autoSize){
+ var sz = this.boundEl.getSize(),
+ fs = this.field.getSize();
+
+ switch(this.autoSize){
+ case "width":
+ this.setSize(sz.width, fs.height);
+ break;
+ case "height":
+ this.setSize(fs.width, sz.height);
+ break;
+ case "none":
+ this.setSize(fs.width, fs.height);
+ break;
+ default:
+ this.setSize(sz.width, sz.height);
+ }
+ }
+ },
+
+ /**
+ * Sets the height and width of this editor.
+ * @param {Number} width The new width
+ * @param {Number} height The new height
+ */
+ setSize : function(w, h){
+ delete this.field.lastSize;
+ this.field.setSize(w, h);
+ if(this.el){
+ if(Ext.isGecko2 || Ext.isOpera){
+ // prevent layer scrollbars
+ this.el.setSize(w, h);
+ }
+ this.el.sync();
+ }
+ },
+
+ /**
+ * Realigns the editor to the bound field based on the current alignment config value.
+ * @param {Boolean} autoSize (optional) True to size the field to the dimensions of the bound element.
+ */
+ realign : function(autoSize){
+ if(autoSize === true){
+ this.doAutoSize();
+ }
+ this.el.alignTo(this.boundEl, this.alignment, this.offsets);
+ },
+
+ /**
+ * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
+ * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
+ */
+ completeEdit : function(remainVisible){
+ if(!this.editing){
+ return;
+ }
+ var v = this.getValue();
+ if(!this.field.isValid()){
+ if(this.revertInvalid !== false){
+ this.cancelEdit(remainVisible);
+ }
+ return;
+ }
+ if(String(v) === String(this.startValue) && this.ignoreNoChange){
+ this.hideEdit(remainVisible);
+ return;
+ }
+ if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
+ v = this.getValue();
+ if(this.updateEl && this.boundEl){
+ this.boundEl.update(v);
+ }
+ this.hideEdit(remainVisible);
+ this.fireEvent("complete", this, v, this.startValue);
+ }
+ },
+
+ // private
+ onShow : function(){
+ this.el.show();
+ if(this.hideEl !== false){
+ this.boundEl.hide();
+ }
+ this.field.show().focus(false, true);
+ this.fireEvent("startedit", this.boundEl, this.startValue);
+ },
+
+ /**
+ * Cancels the editing process and hides the editor without persisting any changes. The field value will be
+ * reverted to the original starting value.
+ * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
+ * cancel (defaults to false)
+ */
+ cancelEdit : function(remainVisible){
+ if(this.editing){
+ var v = this.getValue();
+ this.setValue(this.startValue);
+ this.hideEdit(remainVisible);
+ this.fireEvent("canceledit", this, v, this.startValue);
+ }
+ },
+
+ // private
+ hideEdit: function(remainVisible){
+ if(remainVisible !== true){
+ this.editing = false;
+ this.hide();
+ }
+ },
+
+ // private
+ onBlur : function(){
+ // selectSameEditor flag allows the same editor to be started without onBlur firing on itself
+ if(this.allowBlur !== true && this.editing && this.selectSameEditor !== true){
+ this.completeEdit();
+ }
+ },
+
+ // private
+ onHide : function(){
+ if(this.editing){
+ this.completeEdit();
+ return;
+ }
+ this.field.blur();
+ if(this.field.collapse){
+ this.field.collapse();
+ }
+ this.el.hide();
+ if(this.hideEl !== false){
+ this.boundEl.show();
+ }
+ },
+
+ /**
+ * Sets the data value of the editor
+ * @param {Mixed} value Any valid value supported by the underlying field
+ */
+ setValue : function(v){
+ this.field.setValue(v);
+ },
+
+ /**
+ * Gets the data value of the editor
+ * @return {Mixed} The data value
+ */
+ getValue : function(){
+ return this.field.getValue();
+ },
+
+ beforeDestroy : function(){
+ Ext.destroyMembers(this, 'field');
+
+ delete this.parentEl;
+ delete this.boundEl;
+ }
+});
+Ext.reg('editor', Ext.Editor);
+/**
+ * @class Ext.ColorPalette
+ * @extends Ext.Component
+ * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
+ * Here's an example of typical usage:
+ * <pre><code>
+var cp = new Ext.ColorPalette({value:'993300'}); // initial selected color
+cp.render('my-div');
+
+cp.on('select', function(palette, selColor){
+ // do something with selColor
+});
+</code></pre>
+ * @constructor
+ * Create a new ColorPalette
+ * @param {Object} config The config object
+ * @xtype colorpalette
+ */
+Ext.ColorPalette = Ext.extend(Ext.Component, {
+ /**
+ * @cfg {String} tpl An existing XTemplate instance to be used in place of the default template for rendering the component.
+ */
+ /**
+ * @cfg {String} itemCls
+ * The CSS class to apply to the containing element (defaults to 'x-color-palette')
+ */
+ itemCls : 'x-color-palette',
+ /**
+ * @cfg {String} value
+ * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
+ * the hex codes are case-sensitive.
+ */
+ value : null,
+ /**
+ * @cfg {String} clickEvent
+ * The DOM event that will cause a color to be selected. This can be any valid event name (dblclick, contextmenu).
+ * Defaults to <tt>'click'</tt>.
+ */
+ clickEvent :'click',
+ // private
+ ctype : 'Ext.ColorPalette',
+
+ /**
+ * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the {@link #select} event
+ */
+ allowReselect : false,
+
+ /**
+ * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
+ * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
+ * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
+ * of colors with the width setting until the box is symmetrical.</p>
+ * <p>You can override individual colors if needed:</p>
+ * <pre><code>
+var cp = new Ext.ColorPalette();
+cp.colors[0] = 'FF0000'; // change the first box to red
+</code></pre>
+
+Or you can provide a custom array of your own for complete control:
+<pre><code>
+var cp = new Ext.ColorPalette();
+cp.colors = ['000000', '993300', '333300'];
+</code></pre>
+ * @type Array
+ */
+ colors : [
+ '000000', '993300', '333300', '003300', '003366', '000080', '333399', '333333',
+ '800000', 'FF6600', '808000', '008000', '008080', '0000FF', '666699', '808080',
+ 'FF0000', 'FF9900', '99CC00', '339966', '33CCCC', '3366FF', '800080', '969696',
+ 'FF00FF', 'FFCC00', 'FFFF00', '00FF00', '00FFFF', '00CCFF', '993366', 'C0C0C0',
+ 'FF99CC', 'FFCC99', 'FFFF99', 'CCFFCC', 'CCFFFF', '99CCFF', 'CC99FF', 'FFFFFF'
+ ],
+
+ /**
+ * @cfg {Function} handler
+ * Optional. A function that will handle the select event of this palette.
+ * The handler is passed the following parameters:<div class="mdetail-params"><ul>
+ * <li><code>palette</code> : ColorPalette<div class="sub-desc">The {@link #palette Ext.ColorPalette}.</div></li>
+ * <li><code>color</code> : String<div class="sub-desc">The 6-digit color hex code (without the # symbol).</div></li>
+ * </ul></div>
+ */
+ /**
+ * @cfg {Object} scope
+ * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>
+ * function will be called. Defaults to this ColorPalette instance.
+ */
+
+ // private
+ initComponent : function(){
+ Ext.ColorPalette.superclass.initComponent.call(this);
+ this.addEvents(
+ /**
+ * @event select
+ * Fires when a color is selected
+ * @param {ColorPalette} this
+ * @param {String} color The 6-digit color hex code (without the # symbol)
+ */
+ 'select'
+ );
+
+ if(this.handler){
+ this.on('select', this.handler, this.scope, true);
+ }
+ },
+
+ // private
+ onRender : function(container, position){
+ this.autoEl = {
+ tag: 'div',
+ cls: this.itemCls
+ };
+ Ext.ColorPalette.superclass.onRender.call(this, container, position);
+ var t = this.tpl || new Ext.XTemplate(
+ '<tpl for="."><a href="#" class="color-{.}" hidefocus="on"><em><span style="background:#{.}" unselectable="on"> </span></em></a></tpl>'
+ );
+ t.overwrite(this.el, this.colors);
+ this.mon(this.el, this.clickEvent, this.handleClick, this, {delegate: 'a'});
+ if(this.clickEvent != 'click'){
+ this.mon(this.el, 'click', Ext.emptyFn, this, {delegate: 'a', preventDefault: true});
+ }
+ },
+
+ // private
+ afterRender : function(){
+ Ext.ColorPalette.superclass.afterRender.call(this);
+ if(this.value){
+ var s = this.value;
+ this.value = null;
+ this.select(s);
+ }
+ },
+
+ // private
+ handleClick : function(e, t){
+ e.preventDefault();
+ if(!this.disabled){
+ var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
+ this.select(c.toUpperCase());
+ }
+ },
+
+ /**
+ * Selects the specified color in the palette (fires the {@link #select} event)
+ * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
+ */
+ select : function(color){
+ color = color.replace('#', '');
+ if(color != this.value || this.allowReselect){
+ var el = this.el;
+ if(this.value){
+ el.child('a.color-'+this.value).removeClass('x-color-palette-sel');
+ }
+ el.child('a.color-'+color).addClass('x-color-palette-sel');
+ this.value = color;
+ this.fireEvent('select', this, color);
+ }
+ }
+
+ /**
+ * @cfg {String} autoEl @hide
+ */
+});
+Ext.reg('colorpalette', Ext.ColorPalette);
+/**
+ * @class Ext.DatePicker
+ * @extends Ext.Component
+ * <p>A popup date picker. This class is used by the {@link Ext.form.DateField DateField} class
+ * to allow browsing and selection of valid dates.</p>
+ * <p>All the string values documented below may be overridden by including an Ext locale file in
+ * your page.</p>
+ * @constructor
+ * Create a new DatePicker
+ * @param {Object} config The config object
+ * @xtype datepicker
+ */
+Ext.DatePicker = Ext.extend(Ext.BoxComponent, {
+ /**
+ * @cfg {String} todayText
+ * The text to display on the button that selects the current date (defaults to <code>'Today'</code>)
+ */
+ todayText : 'Today',
+ /**
+ * @cfg {String} okText
+ * The text to display on the ok button (defaults to <code>' OK '</code> to give the user extra clicking room)
+ */
+ okText : ' OK ',
+ /**
+ * @cfg {String} cancelText
+ * The text to display on the cancel button (defaults to <code>'Cancel'</code>)
+ */
+ cancelText : 'Cancel',
+ /**
+ * @cfg {Function} handler
+ * Optional. A function that will handle the select event of this picker.
+ * The handler is passed the following parameters:<div class="mdetail-params"><ul>
+ * <li><code>picker</code> : DatePicker<div class="sub-desc">This DatePicker.</div></li>
+ * <li><code>date</code> : Date<div class="sub-desc">The selected date.</div></li>
+ * </ul></div>
+ */
+ /**
+ * @cfg {Object} scope
+ * The scope (<code><b>this</b></code> reference) in which the <code>{@link #handler}</code>
+ * function will be called. Defaults to this DatePicker instance.
+ */
+ /**
+ * @cfg {String} todayTip
+ * A string used to format the message for displaying in a tooltip over the button that
+ * selects the current date. Defaults to <code>'{0} (Spacebar)'</code> where
+ * the <code>{0}</code> token is replaced by today's date.
+ */
+ todayTip : '{0} (Spacebar)',
+ /**
+ * @cfg {String} minText
+ * The error text to display if the minDate validation fails (defaults to <code>'This date is before the minimum date'</code>)
+ */
+ minText : 'This date is before the minimum date',
+ /**
+ * @cfg {String} maxText
+ * The error text to display if the maxDate validation fails (defaults to <code>'This date is after the maximum date'</code>)
+ */
+ maxText : 'This date is after the maximum date',
+ /**
+ * @cfg {String} format
+ * The default date format string which can be overriden for localization support. The format must be
+ * valid according to {@link Date#parseDate} (defaults to <code>'m/d/y'</code>).
+ */
+ format : 'm/d/y',
+ /**
+ * @cfg {String} disabledDaysText
+ * The tooltip to display when the date falls on a disabled day (defaults to <code>'Disabled'</code>)
+ */
+ disabledDaysText : 'Disabled',
+ /**
+ * @cfg {String} disabledDatesText
+ * The tooltip text to display when the date falls on a disabled date (defaults to <code>'Disabled'</code>)
+ */
+ disabledDatesText : 'Disabled',
+ /**
+ * @cfg {Array} monthNames
+ * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
+ */
+ monthNames : Date.monthNames,
+ /**
+ * @cfg {Array} dayNames
+ * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
+ */
+ dayNames : Date.dayNames,
+ /**
+ * @cfg {String} nextText
+ * The next month navigation button tooltip (defaults to <code>'Next Month (Control+Right)'</code>)
+ */
+ nextText : 'Next Month (Control+Right)',
+ /**
+ * @cfg {String} prevText
+ * The previous month navigation button tooltip (defaults to <code>'Previous Month (Control+Left)'</code>)
+ */
+ prevText : 'Previous Month (Control+Left)',
+ /**
+ * @cfg {String} monthYearText
+ * The header month selector tooltip (defaults to <code>'Choose a month (Control+Up/Down to move years)'</code>)
+ */
+ monthYearText : 'Choose a month (Control+Up/Down to move years)',
+ /**
+ * @cfg {Number} startDay
+ * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
+ */
+ startDay : 0,
+ /**
+ * @cfg {Boolean} showToday
+ * False to hide the footer area containing the Today button and disable the keyboard handler for spacebar
+ * that selects the current date (defaults to <code>true</code>).
+ */
+ showToday : true,
+ /**
+ * @cfg {Date} minDate
+ * Minimum allowable date (JavaScript date object, defaults to null)
+ */
+ /**
+ * @cfg {Date} maxDate
+ * Maximum allowable date (JavaScript date object, defaults to null)
+ */
+ /**
+ * @cfg {Array} disabledDays
+ * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
+ */
+ /**
+ * @cfg {RegExp} disabledDatesRE
+ * JavaScript regular expression used to disable a pattern of dates (defaults to null). The {@link #disabledDates}
+ * config will generate this regex internally, but if you specify disabledDatesRE it will take precedence over the
+ * disabledDates value.
+ */
+ /**
+ * @cfg {Array} disabledDates
+ * An array of 'dates' to disable, as strings. These strings will be used to build a dynamic regular
+ * expression so they are very powerful. Some examples:
+ * <ul>
+ * <li>['03/08/2003', '09/16/2003'] would disable those exact dates</li>
+ * <li>['03/08', '09/16'] would disable those days for every year</li>
+ * <li>['^03/08'] would only match the beginning (useful if you are using short years)</li>
+ * <li>['03/../2006'] would disable every day in March 2006</li>
+ * <li>['^03'] would disable every day in every March</li>
+ * </ul>
+ * Note that the format of the dates included in the array should exactly match the {@link #format} config.
+ * In order to support regular expressions, if you are using a date format that has '.' in it, you will have to
+ * escape the dot when restricting dates. For example: ['03\\.08\\.03'].
+ */
+
+ // private
+ // Set by other components to stop the picker focus being updated when the value changes.
+ focusOnSelect: true,
+
+ // private
+ initComponent : function(){
+ Ext.DatePicker.superclass.initComponent.call(this);
+
+ this.value = this.value ?
+ this.value.clearTime(true) : new Date().clearTime();
+
+ this.addEvents(
+ /**
+ * @event select
+ * Fires when a date is selected
+ * @param {DatePicker} this DatePicker
+ * @param {Date} date The selected date
+ */
+ 'select'
+ );
+
+ if(this.handler){
+ this.on('select', this.handler, this.scope || this);
+ }
+
+ this.initDisabledDays();
+ },
+
+ // private
+ initDisabledDays : function(){
+ if(!this.disabledDatesRE && this.disabledDates){
+ var dd = this.disabledDates,
+ len = dd.length - 1,
+ re = '(?:';
+
+ Ext.each(dd, function(d, i){
+ re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];
+ if(i != len){
+ re += '|';
+ }
+ }, this);
+ this.disabledDatesRE = new RegExp(re + ')');
+ }
+ },
+
+ /**
+ * Replaces any existing disabled dates with new values and refreshes the DatePicker.
+ * @param {Array/RegExp} disabledDates An array of date strings (see the {@link #disabledDates} config
+ * for details on supported values), or a JavaScript regular expression used to disable a pattern of dates.
+ */
+ setDisabledDates : function(dd){
+ if(Ext.isArray(dd)){
+ this.disabledDates = dd;
+ this.disabledDatesRE = null;
+ }else{
+ this.disabledDatesRE = dd;
+ }
+ this.initDisabledDays();
+ this.update(this.value, true);
+ },
+
+ /**
+ * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.
+ * @param {Array} disabledDays An array of disabled day indexes. See the {@link #disabledDays} config
+ * for details on supported values.
+ */
+ setDisabledDays : function(dd){
+ this.disabledDays = dd;
+ this.update(this.value, true);
+ },
+
+ /**
+ * Replaces any existing {@link #minDate} with the new value and refreshes the DatePicker.
+ * @param {Date} value The minimum date that can be selected
+ */
+ setMinDate : function(dt){
+ this.minDate = dt;
+ this.update(this.value, true);
+ },
+
+ /**
+ * Replaces any existing {@link #maxDate} with the new value and refreshes the DatePicker.
+ * @param {Date} value The maximum date that can be selected
+ */
+ setMaxDate : function(dt){
+ this.maxDate = dt;
+ this.update(this.value, true);
+ },
+
+ /**
+ * Sets the value of the date field
+ * @param {Date} value The date to set
+ */
+ setValue : function(value){
+ this.value = value.clearTime(true);
+ this.update(this.value);
+ },
+
+ /**
+ * Gets the current selected value of the date field
+ * @return {Date} The selected date
+ */
+ getValue : function(){
+ return this.value;
+ },
+
+ // private
+ focus : function(){
+ this.update(this.activeDate);
+ },
+
+ // private
+ onEnable: function(initial){
+ Ext.DatePicker.superclass.onEnable.call(this);
+ this.doDisabled(false);
+ this.update(initial ? this.value : this.activeDate);
+ if(Ext.isIE){
+ this.el.repaint();
+ }
+
+ },
+
+ // private
+ onDisable : function(){
+ Ext.DatePicker.superclass.onDisable.call(this);
+ this.doDisabled(true);
+ if(Ext.isIE && !Ext.isIE8){
+ /* Really strange problem in IE6/7, when disabled, have to explicitly
+ * repaint each of the nodes to get them to display correctly, simply
+ * calling repaint on the main element doesn't appear to be enough.
+ */
+ Ext.each([].concat(this.textNodes, this.el.query('th span')), function(el){
+ Ext.fly(el).repaint();
+ });
+ }
+ },
+
+ // private
+ doDisabled : function(disabled){
+ this.keyNav.setDisabled(disabled);
+ this.prevRepeater.setDisabled(disabled);
+ this.nextRepeater.setDisabled(disabled);
+ if(this.showToday){
+ this.todayKeyListener.setDisabled(disabled);
+ this.todayBtn.setDisabled(disabled);
+ }
+ },
+
+ // private
+ onRender : function(container, position){
+ var m = [
+ '<table cellspacing="0">',
+ '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'"> </a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'"> </a></td></tr>',
+ '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'],
+ dn = this.dayNames,
+ i;
+ for(i = 0; i < 7; i++){
+ var d = this.startDay+i;
+ if(d > 6){
+ d = d-7;
+ }
+ m.push('<th><span>', dn[d].substr(0,1), '</span></th>');
+ }
+ m[m.length] = '</tr></thead><tbody><tr>';
+ for(i = 0; i < 42; i++) {
+ if(i % 7 === 0 && i !== 0){
+ m[m.length] = '</tr><tr>';
+ }
+ m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
+ }
+ m.push('</tr></tbody></table></td></tr>',
+ this.showToday ? '<tr><td colspan="3" class="x-date-bottom" align="center"></td></tr>' : '',
+ '</table><div class="x-date-mp"></div>');
+
+ var el = document.createElement('div');
+ el.className = 'x-date-picker';
+ el.innerHTML = m.join('');
+
+ container.dom.insertBefore(el, position);
+
+ this.el = Ext.get(el);
+ this.eventEl = Ext.get(el.firstChild);
+
+ this.prevRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-left a'), {
+ handler: this.showPrevMonth,
+ scope: this,
+ preventDefault:true,
+ stopDefault:true
+ });
+
+ this.nextRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-right a'), {
+ handler: this.showNextMonth,
+ scope: this,
+ preventDefault:true,
+ stopDefault:true
+ });
+
+ this.monthPicker = this.el.down('div.x-date-mp');
+ this.monthPicker.enableDisplayMode('block');
+
+ this.keyNav = new Ext.KeyNav(this.eventEl, {
+ 'left' : function(e){
+ if(e.ctrlKey){
+ this.showPrevMonth();
+ }else{
+ this.update(this.activeDate.add('d', -1));
+ }
+ },
+
+ 'right' : function(e){
+ if(e.ctrlKey){
+ this.showNextMonth();
+ }else{
+ this.update(this.activeDate.add('d', 1));
+ }
+ },
+
+ 'up' : function(e){
+ if(e.ctrlKey){
+ this.showNextYear();
+ }else{
+ this.update(this.activeDate.add('d', -7));
+ }
+ },
+
+ 'down' : function(e){
+ if(e.ctrlKey){
+ this.showPrevYear();
+ }else{
+ this.update(this.activeDate.add('d', 7));
+ }
+ },
+
+ 'pageUp' : function(e){
+ this.showNextMonth();
+ },
+
+ 'pageDown' : function(e){
+ this.showPrevMonth();
+ },
+
+ 'enter' : function(e){
+ e.stopPropagation();
+ return true;
+ },
+
+ scope : this
+ });
+
+ this.el.unselectable();
+
+ this.cells = this.el.select('table.x-date-inner tbody td');
+ this.textNodes = this.el.query('table.x-date-inner tbody span');
+
+ this.mbtn = new Ext.Button({
+ text: ' ',
+ tooltip: this.monthYearText,
+ renderTo: this.el.child('td.x-date-middle', true)
+ });
+ this.mbtn.el.child('em').addClass('x-btn-arrow');
+
+ if(this.showToday){
+ this.todayKeyListener = this.eventEl.addKeyListener(Ext.EventObject.SPACE, this.selectToday, this);
+ var today = (new Date()).dateFormat(this.format);
+ this.todayBtn = new Ext.Button({
+ renderTo: this.el.child('td.x-date-bottom', true),
+ text: String.format(this.todayText, today),
+ tooltip: String.format(this.todayTip, today),
+ handler: this.selectToday,
+ scope: this
+ });
+ }
+ this.mon(this.eventEl, 'mousewheel', this.handleMouseWheel, this);
+ this.mon(this.eventEl, 'click', this.handleDateClick, this, {delegate: 'a.x-date-date'});
+ this.mon(this.mbtn, 'click', this.showMonthPicker, this);
+ this.onEnable(true);
+ },
+
+ // private
+ createMonthPicker : function(){
+ if(!this.monthPicker.dom.firstChild){
+ var buf = ['<table border="0" cellspacing="0">'];
+ for(var i = 0; i < 6; i++){
+ buf.push(
+ '<tr><td class="x-date-mp-month"><a href="#">', Date.getShortMonthName(i), '</a></td>',
+ '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', Date.getShortMonthName(i + 6), '</a></td>',
+ i === 0 ?
+ '<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>' :
+ '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
+ );
+ }
+ buf.push(
+ '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
+ this.okText,
+ '</button><button type="button" class="x-date-mp-cancel">',
+ this.cancelText,
+ '</button></td></tr>',
+ '</table>'
+ );
+ this.monthPicker.update(buf.join(''));
+
+ this.mon(this.monthPicker, 'click', this.onMonthClick, this);
+ this.mon(this.monthPicker, 'dblclick', this.onMonthDblClick, this);
+
+ this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
+ this.mpYears = this.monthPicker.select('td.x-date-mp-year');
+
+ this.mpMonths.each(function(m, a, i){
+ i += 1;
+ if((i%2) === 0){
+ m.dom.xmonth = 5 + Math.round(i * 0.5);
+ }else{
+ m.dom.xmonth = Math.round((i-1) * 0.5);
+ }
+ });
+ }
+ },
+
+ // private
+ showMonthPicker : function(){
+ if(!this.disabled){
+ this.createMonthPicker();
+ var size = this.el.getSize();
+ this.monthPicker.setSize(size);
+ this.monthPicker.child('table').setSize(size);
+
+ this.mpSelMonth = (this.activeDate || this.value).getMonth();
+ this.updateMPMonth(this.mpSelMonth);
+ this.mpSelYear = (this.activeDate || this.value).getFullYear();
+ this.updateMPYear(this.mpSelYear);
+
+ this.monthPicker.slideIn('t', {duration:0.2});
+ }
+ },
+
+ // private
+ updateMPYear : function(y){
+ this.mpyear = y;
+ var ys = this.mpYears.elements;
+ for(var i = 1; i <= 10; i++){
+ var td = ys[i-1], y2;
+ if((i%2) === 0){
+ y2 = y + Math.round(i * 0.5);
+ td.firstChild.innerHTML = y2;
+ td.xyear = y2;
+ }else{
+ y2 = y - (5-Math.round(i * 0.5));
+ td.firstChild.innerHTML = y2;
+ td.xyear = y2;
+ }
+ this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
+ }
+ },
+
+ // private
+ updateMPMonth : function(sm){
+ this.mpMonths.each(function(m, a, i){
+ m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
+ });
+ },
+
+ // private
+ selectMPMonth : function(m){
+
+ },
+
+ // private
+ onMonthClick : function(e, t){
+ e.stopEvent();
+ var el = new Ext.Element(t), pn;
+ if(el.is('button.x-date-mp-cancel')){
+ this.hideMonthPicker();
+ }
+ else if(el.is('button.x-date-mp-ok')){
+ var d = new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate());
+ if(d.getMonth() != this.mpSelMonth){
+ // 'fix' the JS rolling date conversion if needed
+ d = new Date(this.mpSelYear, this.mpSelMonth, 1).getLastDateOfMonth();
+ }
+ this.update(d);
+ this.hideMonthPicker();
+ }
+ else if((pn = el.up('td.x-date-mp-month', 2))){
+ this.mpMonths.removeClass('x-date-mp-sel');
+ pn.addClass('x-date-mp-sel');
+ this.mpSelMonth = pn.dom.xmonth;
+ }
+ else if((pn = el.up('td.x-date-mp-year', 2))){
+ this.mpYears.removeClass('x-date-mp-sel');
+ pn.addClass('x-date-mp-sel');
+ this.mpSelYear = pn.dom.xyear;
+ }
+ else if(el.is('a.x-date-mp-prev')){
+ this.updateMPYear(this.mpyear-10);
+ }
+ else if(el.is('a.x-date-mp-next')){
+ this.updateMPYear(this.mpyear+10);
+ }
+ },
+
+ // private
+ onMonthDblClick : function(e, t){
+ e.stopEvent();
+ var el = new Ext.Element(t), pn;
+ if((pn = el.up('td.x-date-mp-month', 2))){
+ this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
+ this.hideMonthPicker();
+ }
+ else if((pn = el.up('td.x-date-mp-year', 2))){
+ this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
+ this.hideMonthPicker();
+ }
+ },
+
+ // private
+ hideMonthPicker : function(disableAnim){
+ if(this.monthPicker){
+ if(disableAnim === true){
+ this.monthPicker.hide();
+ }else{
+ this.monthPicker.slideOut('t', {duration:0.2});
+ }
+ }
+ },
+
+ // private
+ showPrevMonth : function(e){
+ this.update(this.activeDate.add('mo', -1));
+ },
+
+ // private
+ showNextMonth : function(e){
+ this.update(this.activeDate.add('mo', 1));
+ },
+
+ // private
+ showPrevYear : function(){
+ this.update(this.activeDate.add('y', -1));
+ },
+
+ // private
+ showNextYear : function(){
+ this.update(this.activeDate.add('y', 1));
+ },
+
+ // private
+ handleMouseWheel : function(e){
+ e.stopEvent();
+ if(!this.disabled){
+ var delta = e.getWheelDelta();
+ if(delta > 0){
+ this.showPrevMonth();
+ } else if(delta < 0){
+ this.showNextMonth();
+ }
+ }
+ },
+
+ // private
+ handleDateClick : function(e, t){
+ e.stopEvent();
+ if(!this.disabled && t.dateValue && !Ext.fly(t.parentNode).hasClass('x-date-disabled')){
+ this.cancelFocus = this.focusOnSelect === false;
+ this.setValue(new Date(t.dateValue));
+ delete this.cancelFocus;
+ this.fireEvent('select', this, this.value);
+ }
+ },
+
+ // private
+ selectToday : function(){
+ if(this.todayBtn && !this.todayBtn.disabled){
+ this.setValue(new Date().clearTime());
+ this.fireEvent('select', this, this.value);
+ }
+ },
+
+ // private
+ update : function(date, forceRefresh){
+ if(this.rendered){
+ var vd = this.activeDate, vis = this.isVisible();
+ this.activeDate = date;
+ if(!forceRefresh && vd && this.el){
+ var t = date.getTime();
+ if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
+ this.cells.removeClass('x-date-selected');
+ this.cells.each(function(c){
+ if(c.dom.firstChild.dateValue == t){
+ c.addClass('x-date-selected');
+ if(vis && !this.cancelFocus){
+ Ext.fly(c.dom.firstChild).focus(50);
+ }
+ return false;
+ }
+ }, this);
+ return;
+ }
+ }
+ var days = date.getDaysInMonth(),
+ firstOfMonth = date.getFirstDateOfMonth(),
+ startingPos = firstOfMonth.getDay()-this.startDay;
+
+ if(startingPos < 0){
+ startingPos += 7;
+ }
+ days += startingPos;
+
+ var pm = date.add('mo', -1),
+ prevStart = pm.getDaysInMonth()-startingPos,
+ cells = this.cells.elements,
+ textEls = this.textNodes,
+ // convert everything to numbers so it's fast
+ day = 86400000,
+ d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime(),
+ today = new Date().clearTime().getTime(),
+ sel = date.clearTime(true).getTime(),
+ min = this.minDate ? this.minDate.clearTime(true) : Number.NEGATIVE_INFINITY,
+ max = this.maxDate ? this.maxDate.clearTime(true) : Number.POSITIVE_INFINITY,
+ ddMatch = this.disabledDatesRE,
+ ddText = this.disabledDatesText,
+ ddays = this.disabledDays ? this.disabledDays.join('') : false,
+ ddaysText = this.disabledDaysText,
+ format = this.format;
+
+ if(this.showToday){
+ var td = new Date().clearTime(),
+ disable = (td < min || td > max ||
+ (ddMatch && format && ddMatch.test(td.dateFormat(format))) ||
+ (ddays && ddays.indexOf(td.getDay()) != -1));
+
+ if(!this.disabled){
+ this.todayBtn.setDisabled(disable);
+ this.todayKeyListener[disable ? 'disable' : 'enable']();
+ }
+ }
+
+ var setCellClass = function(cal, cell){
+ cell.title = '';
+ var t = d.getTime();
+ cell.firstChild.dateValue = t;
+ if(t == today){
+ cell.className += ' x-date-today';
+ cell.title = cal.todayText;
+ }
+ if(t == sel){
+ cell.className += ' x-date-selected';
+ if(vis){
+ Ext.fly(cell.firstChild).focus(50);
+ }
+ }
+ // disabling
+ if(t < min) {
+ cell.className = ' x-date-disabled';
+ cell.title = cal.minText;
+ return;
+ }
+ if(t > max) {
+ cell.className = ' x-date-disabled';
+ cell.title = cal.maxText;
+ return;
+ }
+ if(ddays){
+ if(ddays.indexOf(d.getDay()) != -1){
+ cell.title = ddaysText;
+ cell.className = ' x-date-disabled';
+ }
+ }
+ if(ddMatch && format){
+ var fvalue = d.dateFormat(format);
+ if(ddMatch.test(fvalue)){
+ cell.title = ddText.replace('%0', fvalue);
+ cell.className = ' x-date-disabled';
+ }
+ }
+ };
+
+ var i = 0;
+ for(; i < startingPos; i++) {
+ textEls[i].innerHTML = (++prevStart);
+ d.setDate(d.getDate()+1);
+ cells[i].className = 'x-date-prevday';
+ setCellClass(this, cells[i]);
+ }
+ for(; i < days; i++){
+ var intDay = i - startingPos + 1;
+ textEls[i].innerHTML = (intDay);
+ d.setDate(d.getDate()+1);
+ cells[i].className = 'x-date-active';
+ setCellClass(this, cells[i]);
+ }
+ var extraDays = 0;
+ for(; i < 42; i++) {
+ textEls[i].innerHTML = (++extraDays);
+ d.setDate(d.getDate()+1);
+ cells[i].className = 'x-date-nextday';
+ setCellClass(this, cells[i]);
+ }
+
+ this.mbtn.setText(this.monthNames[date.getMonth()] + ' ' + date.getFullYear());
+
+ if(!this.internalRender){
+ var main = this.el.dom.firstChild,
+ w = main.offsetWidth;
+ this.el.setWidth(w + this.el.getBorderWidth('lr'));
+ Ext.fly(main).setWidth(w);
+ this.internalRender = true;
+ // opera does not respect the auto grow header center column
+ // then, after it gets a width opera refuses to recalculate
+ // without a second pass
+ if(Ext.isOpera && !this.secondPass){
+ main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + 'px';
+ this.secondPass = true;
+ this.update.defer(10, this, [date]);
+ }
+ }
+ }
+ },
+
+ // private
+ beforeDestroy : function() {
+ if(this.rendered){
+ Ext.destroy(
+ this.keyNav,
+ this.monthPicker,
+ this.eventEl,
+ this.mbtn,
+ this.nextRepeater,
+ this.prevRepeater,
+ this.cells.el,
+ this.todayBtn
+ );
+ delete this.textNodes;
+ delete this.cells.elements;
+ }
+ }
+
+ /**
+ * @cfg {String} autoEl @hide
+ */
+});
+
+Ext.reg('datepicker', Ext.DatePicker);