3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * A month picker component. This class is used by the {@link Ext.picker.Date Date picker} class
17 * to allow browsing and selection of year/months combinations.
19 Ext.define('Ext.picker.Month', {
20 extend: 'Ext.Component',
21 requires: ['Ext.XTemplate', 'Ext.util.ClickRepeater', 'Ext.Date', 'Ext.button.Button'],
22 alias: 'widget.monthpicker',
23 alternateClassName: 'Ext.MonthPicker',
26 '<div id="{id}-bodyEl" class="{baseCls}-body">',
27 '<div class="{baseCls}-months">',
29 '<div class="{parent.baseCls}-item {parent.baseCls}-month"><a href="#" hidefocus="on">{.}</a></div>',
32 '<div class="{baseCls}-years">',
33 '<div class="{baseCls}-yearnav">',
34 '<button id="{id}-prevEl" class="{baseCls}-yearnav-prev"></button>',
35 '<button id="{id}-nextEl" class="{baseCls}-yearnav-next"></button>',
38 '<div class="{parent.baseCls}-item {parent.baseCls}-year"><a href="#" hidefocus="on">{.}</a></div>',
41 '<div class="' + Ext.baseCSSPrefix + 'clear"></div>',
43 '<tpl if="showButtons">',
44 '<div id="{id}-buttonsEl" class="{baseCls}-buttons"></div>',
49 * @cfg {String} okText The text to display on the ok button.
54 * @cfg {String} cancelText The text to display on the cancel button.
59 * @cfg {String} baseCls The base CSS class to apply to the picker element. Defaults to <tt>'x-monthpicker'</tt>
61 baseCls: Ext.baseCSSPrefix + 'monthpicker',
64 * @cfg {Boolean} showButtons True to show ok and cancel buttons below the picker.
69 * @cfg {String} selectedCls The class to be added to selected items in the picker. Defaults to
70 * <tt>'x-monthpicker-selected'</tt>
74 * @cfg {Date/Number[]} value The default value to set. See {@link #setValue}
78 // used when attached to date picker which isnt showing buttons
79 smallCls: Ext.baseCSSPrefix + 'monthpicker-small',
83 yearOffset: 5, // 10 years in total, 2 per row
84 monthOffset: 6, // 12 months, 2 per row
86 // private, inherit docs
87 initComponent: function(){
90 me.selectedCls = me.baseCls + '-selected';
94 * Fires when the cancel button is pressed.
95 * @param {Ext.picker.Month} this
101 * Fires when a month is clicked.
102 * @param {Ext.picker.Month} this
103 * @param {Array} value The current value
108 * @event monthdblclick
109 * Fires when a month is clicked.
110 * @param {Ext.picker.Month} this
111 * @param {Array} value The current value
117 * Fires when the ok button is pressed.
118 * @param {Ext.picker.Month} this
119 * @param {Array} value The current value
125 * Fires when a month/year is selected.
126 * @param {Ext.picker.Month} this
127 * @param {Array} value The current value
133 * Fires when a year is clicked.
134 * @param {Ext.picker.Month} this
135 * @param {Array} value The current value
140 * @event yeardblclick
141 * Fires when a year is clicked.
142 * @param {Ext.picker.Month} this
143 * @param {Array} value The current value
148 me.addCls(me.smallCls);
150 me.setValue(me.value);
151 me.activeYear = me.getYear(new Date().getFullYear() - 4, -4);
155 // private, inherit docs
156 onRender: function(ct, position){
160 shortName = Ext.Date.getShortMonthName,
161 monthLen = me.monthOffset;
163 for (; i < monthLen; ++i) {
164 months.push(shortName(i), shortName(i + monthLen));
167 Ext.apply(me.renderData, {
169 years: me.getYears(),
170 showButtons: me.showButtons
173 me.addChildEls('bodyEl', 'prevEl', 'nextEl', 'buttonsEl');
175 me.callParent(arguments);
178 // private, inherit docs
179 afterRender: function(){
182 buttonsEl = me.buttonsEl;
186 me.mon(body, 'click', me.onBodyClick, me);
187 me.mon(body, 'dblclick', me.onBodyClick, me);
189 // keep a reference to the year/month elements since we'll be re-using them
190 me.years = body.select('.' + me.baseCls + '-year a');
191 me.months = body.select('.' + me.baseCls + '-month a');
193 if (me.showButtons) {
194 me.okBtn = Ext.create('Ext.button.Button', {
197 handler: me.onOkClick,
200 me.cancelBtn = Ext.create('Ext.button.Button', {
203 handler: me.onCancelClick,
208 me.backRepeater = Ext.create('Ext.util.ClickRepeater', me.prevEl, {
209 handler: Ext.Function.bind(me.adjustYear, me, [-me.totalYears])
212 me.prevEl.addClsOnOver(me.baseCls + '-yearnav-prev-over');
213 me.nextRepeater = Ext.create('Ext.util.ClickRepeater', me.nextEl, {
214 handler: Ext.Function.bind(me.adjustYear, me, [me.totalYears])
216 me.nextEl.addClsOnOver(me.baseCls + '-yearnav-next-over');
221 * Set the value for the picker.
222 * @param {Date/Number[]} value The value to set. It can be a Date object, where the month/year will be extracted, or
223 * it can be an array, with the month as the first index and the year as the second.
224 * @return {Ext.picker.Month} this
226 setValue: function(value){
228 active = me.activeYear,
229 offset = me.monthOffset,
234 me.value = [null, null];
235 } else if (Ext.isDate(value)) {
236 me.value = [value.getMonth(), value.getFullYear()];
238 me.value = [value[0], value[1]];
244 if ((year < active || year > active + me.yearOffset)) {
245 me.activeYear = year - me.yearOffset + 1;
255 * Gets the selected value. It is returned as an array [month, year]. It may
256 * be a partial value, for example [null, 2010]. The month is returned as
258 * @return {Number[]} The selected value
260 getValue: function(){
265 * Checks whether the picker has a selection
266 * @return {Boolean} Returns true if both a month and year have been selected
268 hasSelection: function(){
269 var value = this.value;
270 return value[0] !== null && value[1] !== null;
274 * Get an array of years to be pushed in the template. It is not in strict
275 * numerical order because we want to show them in columns.
277 * @return {Number[]} An array of years
279 getYears: function(){
281 offset = me.yearOffset,
282 start = me.activeYear, // put the "active" year on the left
283 end = start + offset,
287 for (; i < end; ++i) {
288 years.push(i, i + offset);
295 * Update the years in the body based on any change
298 updateBody: function(){
302 yearNumbers = me.getYears(),
303 cls = me.selectedCls,
304 value = me.getYear(null),
306 monthOffset = me.monthOffset,
310 years.removeCls(cls);
311 months.removeCls(cls);
312 years.each(function(el, all, index){
313 year = yearNumbers[index];
314 el.dom.innerHTML = year;
316 el.dom.className = cls;
319 if (month !== null) {
320 if (month < monthOffset) {
323 month = (month - monthOffset) * 2 + 1;
325 months.item(month).addCls(cls);
331 * Gets the current year value, or the default.
333 * @param {Number} defaultValue The default value to use if the year is not defined.
334 * @param {Number} offset A number to offset the value by
335 * @return {Number} The year value
337 getYear: function(defaultValue, offset) {
338 var year = this.value[1];
339 offset = offset || 0;
340 return year === null ? defaultValue : year + offset;
344 * React to clicks on the body
347 onBodyClick: function(e, t) {
349 isDouble = e.type == 'dblclick';
351 if (e.getTarget('.' + me.baseCls + '-month')) {
353 me.onMonthClick(t, isDouble);
354 } else if (e.getTarget('.' + me.baseCls + '-year')) {
356 me.onYearClick(t, isDouble);
361 * Modify the year display by passing an offset.
362 * @param {Number} [offset=10] The offset to move by.
364 adjustYear: function(offset){
365 if (typeof offset != 'number') {
366 offset = this.totalYears;
368 this.activeYear += offset;
373 * React to the ok button being pressed
376 onOkClick: function(){
377 this.fireEvent('okclick', this, this.value);
381 * React to the cancel button being pressed
384 onCancelClick: function(){
385 this.fireEvent('cancelclick', this);
389 * React to a month being clicked
391 * @param {HTMLElement} target The element that was clicked
392 * @param {Boolean} isDouble True if the event was a doubleclick
394 onMonthClick: function(target, isDouble){
396 me.value[0] = me.resolveOffset(me.months.indexOf(target), me.monthOffset);
398 me.fireEvent('month' + (isDouble ? 'dbl' : '') + 'click', me, me.value);
399 me.fireEvent('select', me, me.value);
403 * React to a year being clicked
405 * @param {HTMLElement} target The element that was clicked
406 * @param {Boolean} isDouble True if the event was a doubleclick
408 onYearClick: function(target, isDouble){
410 me.value[1] = me.activeYear + me.resolveOffset(me.years.indexOf(target), me.yearOffset);
412 me.fireEvent('year' + (isDouble ? 'dbl' : '') + 'click', me, me.value);
413 me.fireEvent('select', me, me.value);
418 * Returns an offsetted number based on the position in the collection. Since our collections aren't
419 * numerically ordered, this function helps to normalize those differences.
421 * @param {Object} index
422 * @param {Object} offset
423 * @return {Number} The correctly offsetted number
425 resolveOffset: function(index, offset){
426 if (index % 2 === 0) {
429 return offset + Math.floor(index / 2);
433 // private, inherit docs
434 beforeDestroy: function(){
436 me.years = me.months = null;
437 Ext.destroyMembers(me, 'backRepeater', 'nextRepeater', 'okBtn', 'cancelBtn');