fix change event on reset
[tine20] / tine20 / Tinebase / js / ux / form / PeriodPicker.js
1 /*
2  * Tine 2.0
3  *
4  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
5  * @author      Cornelius Weiss <c.weiss@metaways.de>
6  * @copyright   Copyright (c) 2017 Metaways Infosystems GmbH (http://www.metaways.de)
7  *
8  */
9
10 /*global Ext*/
11
12 Ext.ns('Ext.ux', 'Ext.ux.form');
13
14 /**
15  * A combination range and paging control
16  *
17  * @namespace   Ext.ux.form
18  * @class       Ext.ux.form.PeriodPicker
19  * @extends     Ext.form.Field
20  */
21 Ext.ux.form.PeriodPicker = Ext.extend(Ext.form.Field, {
22
23     /**
24      * @cfg {String} availableRanges
25      * ranges available in range picker
26      * _('Day'), _('Week'), _('Month'), _('Year')
27      */
28     availableRanges: 'day,week,month',
29
30     /**
31      * @cfg {String} range
32      * initial range
33      */
34     range: 'month',
35
36     /**
37      * @cfg {Date} startDate
38      * defaults to toDay, will be constraint to period, gets overwritten by value
39      */
40     startDate: null,
41
42     defaultAutoCreate: {
43         tag: 'table',
44         cls: 'ux-pp-field',
45         cn: [{
46             tag: 'tr',
47             cn: [{
48                 tag: 'td',
49                 cls: 'ux-pp-range'
50             }, {
51                 tag: 'td',
52                 cls: 'ux-pp-prev'
53             }, {
54                 tag: 'td',
55                 cls: 'ux-pp-period'
56             }, {
57                 tag: 'td',
58                 cls: 'ux-pp-next'
59             }]
60         }]
61     },
62
63     initComponent: function() {
64         Ext.ux.form.PeriodPicker.superclass.initComponent.call(this);
65
66         this.value = Ext.ux.form.PeriodPicker.getPeriod(this.value ? this.value.from : this.startDate || new Date(), this.range);
67         this.startDate = this.value.from;
68         this.startValue = this.value;
69     },
70
71     /**
72      * @return {Object} {from: Date, until: Date}
73      */
74     getValue: function() {
75         return this.value;
76     },
77
78     /**
79      * @param {Object} value {from: Date, until: Date}
80      */
81     setValue: function(value) {
82         this.range = Ext.ux.form.PeriodPicker.getRange(value);
83         this.value = Ext.ux.form.PeriodPicker.getPeriod(value.from, this.range);
84         this.startDate = this.value.from;
85
86         this.getRangeCombo().setValue(this.range);
87         var dateString;
88
89         switch(this.range) {
90             case 'day':
91                 dateString = Tine.Tinebase.common.dateRenderer(this.startDate);
92                 break;
93             case 'week':
94                 // NOTE: '+1' is to ensure we display the ISO8601 based week where weeks always start on monday!
95                 var wkStart = this.startDate.add(Date.DAY, this.startDate.getDay() < 1 ? 1 : 0);
96
97                 dateString = wkStart.getWeekOfYear() + ' ' + this.startDate.format('Y');
98                 break;
99             case 'month':
100                 dateString = Ext.DatePicker.prototype.monthNames[this.startDate.getMonth()] + ' ' + this.startDate.format('Y');
101                 break;
102             case 'year':
103                 dateString = this.startDate.format('Y');
104                 break;
105         }
106         this.setPeriodText(dateString);
107
108         if (JSON.stringify(this.value) != JSON.stringify(this.startValue)){
109             this.fireEvent('change', this, this.value, this.startValue);
110         }
111     },
112
113     reset : function() {
114         this.originalValue = this.startValue;
115
116         Ext.ux.form.PeriodPicker.superclass.reset.apply(this, arguments);
117     },
118
119     setStartDate: function(startDate) {
120         var value = Ext.ux.form.PeriodPicker.getPeriod(startDate, this.range);
121         this.setValue(value);
122     },
123
124     setPeriodText: function(text) {
125         this.el.child('.ux-pp-period').update(Ext.util.Format.htmlEncode(text));
126     },
127
128     // private
129     onRangeComboChange: function() {
130         this.setValue(Ext.ux.form.PeriodPicker.getPeriod(this.startDate, this.getRangeCombo().getValue()));
131     },
132
133     // private
134     onClick: function(e) {
135         var prev = e.getTarget('.ux-pp-prev'),
136             next = e.getTarget('.ux-pp-next'),
137             period = e.getTarget('.ux-pp-period');
138
139         if (next) {
140             this.setStartDate(this.value.until.add(Date.DAY, 1));
141         } else if (prev) {
142             this.setStartDate(this.value.from.add(Date.DAY, -1));
143         } else if (period) {
144             this.getDatePickerMenu().show(period);
145         }
146     },
147
148     // private
149     onRender : function(ct, position){
150         this.doc = Ext.isIE ? Ext.getBody() : Ext.getDoc();
151         Ext.ux.form.PeriodPicker.superclass.onRender.call(this, ct, position);
152
153         var rangeCombo = this.getRangeCombo();
154         rangeCombo.render(this.el.child('.ux-pp-range'));
155         rangeCombo.setWidth(this.getEl().getWidth() * 0.4);
156
157         this.setValue(this.value);
158         this.mon(this.getEl(), 'click', this.onClick, this);
159     },
160
161     // private
162     onResize: function (w, h) {
163         Ext.ux.form.PeriodPicker.superclass.onResize.apply(this, arguments);
164
165         this.getRangeCombo().setWidth(this.getEl().getWidth() * 0.4);
166     },
167
168     getRangeCombo: function() {
169         if (! this.rangeCombo) {
170             var fieldDef = [];
171             Ext.each(this.availableRanges.split(','), function(range) {
172                 fieldDef.push([range, i18n._hidden(Ext.util.Format.capitalize(range))]);
173             });
174
175             this.rangeCombo = new Ext.form.ComboBox({
176                 typeAhead: true,
177                 triggerAction: 'all',
178                 mode: 'local',
179                 forceSelection: true,
180                 allowEmpty: false,
181                 editable: false,
182                 store: fieldDef,
183                 // value: this.range,
184                 listeners: {
185                     scope: this,
186                     select: this.onRangeComboChange
187                 }
188             });
189         }
190
191         return this.rangeCombo;
192     },
193
194     /**
195      * returns a new datepickerMenu
196      *
197      * @returns {Ext.menu.DateMenu}
198      */
199     getDatePickerMenu: function() {
200             var me = this;
201
202             return new Ext.menu.DateMenu({
203                 value: this.startDate,
204                 hideOnClick: true,
205                 focusOnSelect: true,
206                 plugins: [new Ext.ux.DatePickerWeekPlugin({
207                     weekHeaderString: Tine.Tinebase.appMgr.get('Calendar').i18n._hidden('WK'),
208                     inspectMonthPickerClick: function(btn, e) {
209                         if (e.getTarget('button')) {
210                             me.getRangeCombo().setValue('month');
211                             me.range = 'month';
212                             me.setStartDate(this.activeDate);
213                             this.destroy();
214                             return false;
215                         }
216                     }
217                 })],
218                 listeners: {
219                     scope: this,
220                     select: function(picker, value, weekNumber) {
221                         this.getRangeCombo().setValue(weekNumber ? 'week' : 'day');
222                         this.range = weekNumber ? 'week' : 'day';
223                         this.setStartDate(value);
224                     }
225                 }
226             });
227     }
228 });
229
230 /**
231  * gets period
232  *
233  * @static
234  * @param {Date} startDate date within period
235  * @param {String} range day|week|month|year
236  * @return {Object} {from: Date, until: Date}
237  */
238 Ext.ux.form.PeriodPicker.getPeriod = function(startDate, range) {
239     var from, until;
240     switch(range) {
241         case 'day':
242             from = startDate.clearTime(true);
243             until = from.add(Date.DAY, 1);
244             break;
245         case 'week':
246             from = startDate.clearTime(true).add(Date.DAY, -1 * startDate.getDay())
247                 .add(Date.DAY, Ext.DatePicker.prototype.startDay - (startDate.getDay() == 0 ? 7 : 0));
248             until = from.add(Date.DAY, 7);
249             break;
250         case 'month':
251             from = startDate.clearTime(true).getFirstDateOfMonth();
252             until = from.getLastDateOfMonth().add(Date.DAY, 1);
253             break;
254         case 'year':
255             var year = this.startDate.format('y');
256             from = Date.parse(year + '-0-0 00:00:00', 'Y-m-d H:i:s');
257             until = Date.parse(++year + '-0-0 00:00:00', 'Y-m-d H:i:s');
258             break;
259     }
260
261     return {from: from, until: until};
262 };
263
264 /**
265  * gets period
266  *
267  * @static
268  * @param {Object} {from: Date, until: Date}
269  * @return {String} range day|week|month|year
270  */
271 Ext.ux.form.PeriodPicker.getRange = function(period) {
272     var ms = period.from.getElapsed(period.until),
273         msDay = 86400000;
274
275     if (ms > msDay * 300) {
276         return 'year';
277     } else if (ms > msDay * 20) {
278         return 'month';
279     } else if (ms > msDay * 5) {
280         return 'week';
281     } else {
282         return 'day'
283     }
284
285 }
286 Ext.reg('ux-period-picker', Ext.ux.form.PeriodPicker)