Do not create recurring events that are longer than the interval
[tine20] / tine20 / Calendar / js / RrulePanel.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) 2007-2015 Metaways Infosystems GmbH (http://www.metaways.de)
7  */
8
9 Ext.ns('Tine.Calendar');
10
11 Tine.Calendar.RrulePanel = Ext.extend(Ext.Panel, {
12     
13     /**
14      * @static
15      */
16     wkdays: ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'],
17     /**
18      * @property
19      */    
20     activeRuleCard: null,
21     
22     /**
23      * the event edit dialog (parent)
24      * @type Tine.Calendar.EventEditDialog
25      */
26     eventEditDialog: null,
27     
28     layout: 'form',
29     frame: true,
30     
31     initComponent: function() {
32         this.app = Tine.Tinebase.appMgr.get('Calendar');
33         
34         this.title = this.app.i18n._('Recurrances');
35
36         this.defaults = {
37             border: false
38         };
39         
40         this.NONEcard = new Ext.Panel({
41             freq: 'NONE',
42             html: this.app.i18n._('No recurring rule defined')
43         });
44         this.NONEcard.setRule = Ext.emptyFn;
45         this.NONEcard.fillDefaults = Ext.emptyFn;
46         this.NONEcard.getRule = function() {
47             return null;
48         };
49         this.NONEcard.isValid = function() {
50             return true;
51         };
52         
53         this.DAILYcard = new Tine.Calendar.RrulePanel.DAILYcard({rrulePanel: this});
54         this.WEEKLYcard = new Tine.Calendar.RrulePanel.WEEKLYcard({rrulePanel: this});
55         this.MONTHLYcard = new Tine.Calendar.RrulePanel.MONTHLYcard({rrulePanel: this});
56         this.YEARLYcard = new Tine.Calendar.RrulePanel.YEARLYcard({rrulePanel: this});
57         
58         this.ruleCards = new Ext.Panel({
59             layout: 'card',
60             activeItem: 0,
61             style: 'padding: 10px 0 0 10px;',
62             items: [
63                 this.NONEcard,
64                 this.DAILYcard,
65                 this.WEEKLYcard,
66                 this.MONTHLYcard,
67                 this.YEARLYcard
68             ]
69         });
70
71         this.idPrefix = Ext.id();
72         
73         this.tbar = [{
74             id: this.idPrefix + 'tglbtn' + 'NONE',
75             xtype: 'tbbtnlockedtoggle',
76             enableToggle: true,
77             text: this.app.i18n._('None'),
78             handler: this.onFreqChange.createDelegate(this, ['NONE']),
79             toggleGroup: this.idPrefix + 'freqtglgroup'
80         }, {
81             id: this.idPrefix + 'tglbtn' + 'DAILY',
82             xtype: 'tbbtnlockedtoggle',
83             enableToggle: true,
84             text: this.app.i18n._('Daily'),
85             handler: this.onFreqChange.createDelegate(this, ['DAILY']),
86             toggleGroup: this.idPrefix + 'freqtglgroup'
87         }, {
88             id: this.idPrefix + 'tglbtn' + 'WEEKLY',
89             xtype: 'tbbtnlockedtoggle',
90             enableToggle: true,
91             text: this.app.i18n._('Weekly'),
92             handler: this.onFreqChange.createDelegate(this, ['WEEKLY']),
93             toggleGroup: this.idPrefix + 'freqtglgroup'
94         }, {
95             id: this.idPrefix + 'tglbtn' + 'MONTHLY',
96             xtype: 'tbbtnlockedtoggle',
97             enableToggle: true,
98             text: this.app.i18n._('Monthly'),
99             handler: this.onFreqChange.createDelegate(this, ['MONTHLY']),
100             toggleGroup: this.idPrefix + 'freqtglgroup'
101         }, {
102             id: this.idPrefix + 'tglbtn' + 'YEARLY',
103             xtype: 'tbbtnlockedtoggle',
104             enableToggle: true,
105             text: this.app.i18n._('Yearly'),
106             handler: this.onFreqChange.createDelegate(this, ['YEARLY']),
107             toggleGroup: this.idPrefix + 'freqtglgroup'
108         }];
109         
110         this.items = [
111             this.ruleCards
112         ];
113         
114         Tine.Calendar.RrulePanel.superclass.initComponent.call(this);
115     },
116     
117     isValid: function() {
118         return this.activeRuleCard.isValid(this.record);
119     },
120     
121     onFreqChange: function(freq) {
122         this.ruleCards.layout.setActiveItem(this[freq + 'card']);
123         this.ruleCards.layout.layout();
124         this.activeRuleCard = this[freq + 'card'];
125     },
126     
127     /**
128      * disable contents not panel
129      */
130     setDisabled: function(v) {
131         this.items.each(function(item) {
132             item.setDisabled(v);
133         }, this);
134     },
135     
136     onRecordLoad: function(record) {
137         this.record = record;
138         
139         if (! this.record.get('editGrant') || this.record.isRecurException()) {
140             this.setDisabled(true);
141         }
142         
143         this.rrule = this.record.get('rrule');
144         
145         var dtstart = this.record.get('dtstart');
146         if (Ext.isDate(dtstart)) {
147             var byday      = Tine.Calendar.RrulePanel.prototype.wkdays[dtstart.format('w')];
148             var bymonthday = dtstart.format('j');
149             var bymonth    = dtstart.format('n');
150             
151             this.WEEKLYcard.setRule({
152                 interval: 1,
153                 byday: byday
154             });
155             this.MONTHLYcard.setRule({
156                 interval: 1,
157                 byday: '1' + byday,
158                 bymonthday: bymonthday
159             });
160             this.YEARLYcard.setRule({
161                 byday: '1' + byday,
162                 bymonthday: bymonthday,
163                 bymonth: bymonth
164             });
165         }
166         
167         var freq = this.rrule && this.rrule.freq ? this.rrule.freq : 'NONE';
168         
169         var freqBtn = Ext.getCmp(this.idPrefix + 'tglbtn' + freq);
170         freqBtn.toggle(true);
171         
172         this.activeRuleCard = this[freq + 'card'];
173         this.ruleCards.activeItem = this.activeRuleCard;
174         
175         this.activeRuleCard.setRule(this.rrule);
176         
177         if (this.record.isRecurException()) {
178             this.activeRuleCard = this.NONEcard;
179             this.items.each(function(item) {
180                 item.setDisabled(true);
181             }, this);
182             
183             this.NONEcard.html = this.app.i18n._("Exceptions of reccuring events can't have recurrences themselves.");
184         }
185     },
186     
187     onRecordUpdate: function(record) {
188         var rrule = this.activeRuleCard.rendered ? this.activeRuleCard.getRule() : this.rrule;
189         
190         if (! this.rrule && rrule) {
191             // mark as new rule to avoid series confirm dlg
192             rrule.newrule = true;
193         }
194         
195         record.set('rrule', '');
196         record.set('rrule', rrule);
197     }
198 });
199
200 Tine.Calendar.RrulePanel.AbstractCard = Ext.extend(Ext.Panel, {
201     border: false,
202     layout: 'form',
203     labelAlign: 'side',
204     autoHeight: true,
205     
206     getRule: function() {
207         
208         var rrule = {
209             freq    : this.freq,
210             interval: this.interval.getValue()
211         };
212         
213         if (this.untilRadio.checked) {
214             rrule.until = this.until.getRawValue();
215             rrule.until = rrule.until ? Date.parseDate(rrule.until, this.until.format) : null;
216             
217             
218             if (Ext.isDate(rrule.until)) {
219                 // make sure, last reccurance is included
220                 rrule.until = rrule.until.clearTime(true).add(Date.HOUR, 24).add(Date.SECOND, -1).format(Date.patterns.ISO8601Long);
221             }
222         } else {
223             rrule.count = this.count.getValue() || 1;
224         }
225             
226         
227         return rrule;
228     },
229     
230     onAfterUnitTriggerClick: function() {
231         if (! this.until.getValue()) {
232             var dtstart = this.rrulePanel.record.get('dtstart');
233             this.until.menu.picker.setValue(dtstart);
234         }
235     },
236     
237     initComponent: function() {
238         this.app = Tine.Tinebase.appMgr.get('Calendar');
239         
240         this.limitId = Ext.id();
241         
242         this.untilRadio = new Ext.form.Radio({
243             requiredGrant : 'editGrant',
244             hideLabel     : true,
245             boxLabel      : this.app.i18n._('at'), 
246             name          : this.limitId + 'LimitRadioGroup', 
247             inputValue    : 'UNTIL',
248             checked       : true,
249             listeners     : {
250                 check: this.onLimitRadioCheck.createDelegate(this)
251             }
252         });
253         
254         this.until = new Ext.form.DateField({
255             requiredGrant : 'editGrant',
256             width         : 100,
257             emptyText     : this.app.i18n._('never'),
258             onTriggerClick: Ext.form.DateField.prototype.onTriggerClick.createSequence(this.onAfterUnitTriggerClick, this),
259             listeners: {
260                 scope: this,
261                 // so dumb!
262                 render: function(f) {f.wrap.setWidth.defer(100, f.wrap, [f.initialConfig.width]);}
263             }
264         });
265         
266         var countStringParts = this.app.i18n._('after {0} occurrences').split('{0}'),
267             countBeforeString = countStringParts[0],
268             countAfterString = countStringParts[1];
269         
270         this.countRadio = new Ext.form.Radio({
271             requiredGrant : 'editGrant',
272             hideLabel     : true,
273             boxLabel      : countBeforeString, 
274             name          : this.limitId + 'LimitRadioGroup', 
275             inputValue    : 'COUNT',
276             listeners     : {
277                 check: this.onLimitRadioCheck.createDelegate(this)
278             }
279         });
280         
281         this.count = new Ext.form.NumberField({
282             requiredGrant : 'editGrant',
283             style         : 'text-align:right;',
284             //fieldLabel    : this.intervalBeforeString,
285             width         : 40,
286             minValue      : 1,
287             disabled      : true,
288             allowBlank    : false
289         });
290         
291         var intervalPars = this.intervalString.split('{0}');
292         var intervalBeforeString = intervalPars[0];
293         var intervalAfterString = intervalPars[1];
294         
295         this.interval = new Ext.form.NumberField({
296             requiredGrant : 'editGrant',
297             style         : 'text-align:right;',
298             //fieldLabel    : this.intervalBeforeString,
299             minValue      : 1,
300             allowBlank    : false,
301             value         : 1,
302             width         : 40
303         });
304         
305         if (! this.items) {
306             this.items = [];
307         }
308         
309         if (this.freq != 'YEARLY') {
310             this.items = [{
311                 layout: 'column',
312                 items: [{
313                     width: 70,
314                     html: intervalBeforeString
315                 },
316                     this.interval,
317                 {
318                     style: 'padding-top: 2px;',
319                     html: intervalAfterString
320                 }]
321             }].concat(this.items);
322         }
323         
324         this.items = this.items.concat({
325             layout: 'form',
326             html: '<div style="padding-top: 5px;">' + this.app.i18n._('End') + '</div>' +
327                     '<div style="position: relative;">' +
328                     '<div style="position: relative;">' +
329                         '<table><tr>' +
330                             '<td width="65" id="' + this.limitId + 'untilRadio"></td>' +
331                             '<td width="100" id="' + this.limitId + 'until"></td>' +
332                         '</tr></table>' +
333                     '</div>' +
334                     '<div style="position: relative;">' +
335                         '<table><tr>' +
336                             '<td width="65" id="' + this.limitId + 'countRadio"></td>' +
337                             '<td width="40" id="' + this.limitId + 'count"></td>' +
338                             '<td width="40" style="padding-left: 5px" >' + countAfterString + '</td>' +
339                          '</tr></table>' +
340                     '</div>' +
341                 '</div>',
342                 listeners: {
343                    scope: this,
344                    render: this.onLimitRender
345                 }
346         });
347         
348         Tine.Calendar.RrulePanel.AbstractCard.superclass.initComponent.call(this);
349     },
350     
351     onLimitRender: function() {
352         var untilradioel = Ext.get(this.limitId + 'untilRadio');
353         var untilel = Ext.get(this.limitId + 'until');
354         
355         var countradioel = Ext.get(this.limitId + 'countRadio');
356         var countel = Ext.get(this.limitId + 'count');
357         
358         if (! (untilradioel && countradioel)) {
359             return this.onLimitRender.defer(100, this, arguments);
360         }
361         
362         this.untilRadio.render(untilradioel);
363         this.until.render(untilel);
364         this.until.wrap.setWidth(80);
365         
366         this.countRadio.render(countradioel);
367         this.count.render(countel);
368     },
369     
370     onLimitRadioCheck: function(radio, checked) {
371         switch(radio.inputValue) {
372             case 'UNTIL':
373                 this.count.setDisabled(checked);
374                 break;
375             case 'COUNT':
376                 this.until.setDisabled(checked);
377                 break;
378         }
379     },
380     
381     isValid: function(record) {
382         var until = this.until.getValue(),
383             freq = this.freq;
384         
385         if (Ext.isDate(until) && Ext.isDate(record.get('dtstart'))) {
386             if (until.getTime() < record.get('dtstart').getTime()) {
387                 this.until.markInvalid(this.app.i18n._('Until has to be after event start'));
388                 return false;
389             }
390         } 
391         
392         if (Ext.isDate(record.get('dtend')) && Ext.isDate(record.get('dtstart'))) {
393             var dayDifference = (record.get('dtend').getTime() - record.get('dtstart').getTime()) / 1000 / 60 / 60 / 24,
394                 dtendField = this.rrulePanel.eventEditDialog.getForm().findField('dtend');
395             
396             if(freq == 'DAILY' && dayDifference >= 1) {
397                 dtendField.markInvalid(this.app.i18n._('The event is longer than the recurring interval'));
398                 return false;
399             } else if(freq == 'WEEKLY' && dayDifference >= 7) {
400                 dtendField.markInvalid(this.app.i18n._('The event is longer than the recurring interval'));
401                 return false;
402             } else if(freq == 'MONTHLY' && dayDifference >= 28) {
403                 dtendField.markInvalid(this.app.i18n._('The event is longer than the recurring interval'));
404                 return false;
405             } else if(freq == 'YEARLY' && dayDifference >= 365) {
406                 dtendField.markInvalid(this.app.i18n._('The event is longer than the recurring interval'));
407                 return false;
408             }
409         }
410         
411         return true;
412     },
413     
414     setRule: function(rrule) {
415         this.interval.setValue(rrule.interval || 1);
416         var date = Date.parseDate(rrule.until, Date.patterns.ISO8601Long);
417         this.until.value = date;
418         
419         if (rrule.count) {
420             this.count.value = rrule.count;
421                 
422             this.untilRadio.setValue(false);
423             this.countRadio.setValue(true);
424             this.onLimitRadioCheck(this.untilRadio, false);
425             this.onLimitRadioCheck(this.countRadio, true);
426         }
427     }
428 });
429
430 Tine.Calendar.RrulePanel.DAILYcard = Ext.extend(Tine.Calendar.RrulePanel.AbstractCard, {
431     
432     freq: 'DAILY',
433     
434     initComponent: function() {
435         this.app = Tine.Tinebase.appMgr.get('Calendar');
436         
437         this.intervalString = this.app.i18n._('Every {0}. Day');
438         
439         Tine.Calendar.RrulePanel.DAILYcard.superclass.initComponent.call(this);
440     }
441 });
442
443 Tine.Calendar.RrulePanel.WEEKLYcard = Ext.extend(Tine.Calendar.RrulePanel.AbstractCard, {
444     
445     freq: 'WEEKLY',
446     
447     getRule: function() {
448         var rrule = Tine.Calendar.RrulePanel.WEEKLYcard.superclass.getRule.call(this);
449         
450         var bydayArray = [];
451         this.byday.items.each(function(cb) {
452             if (cb.checked) {
453                 bydayArray.push(cb.name);
454             }
455         }, this);
456         
457         rrule.byday = bydayArray.join();
458         if (! rrule.byday) {
459             rrule.byday = this.byDayValue;
460         }
461         
462         rrule.wkst = this.wkst || Tine.Calendar.RrulePanel.prototype.wkdays[Ext.DatePicker.prototype.startDay];
463         
464         return rrule;
465     },
466     
467     initComponent: function() {
468         this.app = Tine.Tinebase.appMgr.get('Calendar');
469         
470         this.intervalString = this.app.i18n._('Every {0}. Week at');
471         
472         var bydayItems = [];
473         for (var i=0,d; i<7; i++) {
474             d = (i+Ext.DatePicker.prototype.startDay)%7
475             bydayItems.push({
476                 boxLabel: Date.dayNames[d],
477                 name: Tine.Calendar.RrulePanel.prototype.wkdays[d]
478             })
479         }
480         
481         this.byday = new Ext.form.CheckboxGroup({
482             requiredGrant : 'editGrant',
483             style: 'padding-top: 5px;',
484             hideLabel: true,
485             items: bydayItems
486         });
487         
488         this.items = [this.byday];
489         
490         Tine.Calendar.RrulePanel.WEEKLYcard.superclass.initComponent.call(this);
491     },
492     
493     setRule: function(rrule) {
494         Tine.Calendar.RrulePanel.WEEKLYcard.superclass.setRule.call(this, rrule);
495         this.wkst = rrule.wkst;
496         
497         if (rrule.byday) {
498             this.byDayValue = rrule.byday;
499             
500             var bydayArray = rrule.byday.split(',');
501             
502             if (Ext.isArray(this.byday.items)) {
503                 // on initialisation items are not renderd
504                 Ext.each(this.byday.items, function(cb) {
505                     if (bydayArray.indexOf(cb.name) != -1) {
506                         cb.checked = true;
507                     }
508                 }, this);
509             } else {
510                 // after items are rendered
511                 this.byday.items.each(function(cb) {
512                     if (bydayArray.indexOf(cb.name) != -1) {
513                         cb.setValue(true);
514                     }
515                 }, this);
516             }
517         }
518     }
519 });
520
521 Tine.Calendar.RrulePanel.MONTHLYcard = Ext.extend(Tine.Calendar.RrulePanel.AbstractCard, {
522     
523     freq: 'MONTHLY',
524     
525     getRule: function() {
526         var rrule = Tine.Calendar.RrulePanel.MONTHLYcard.superclass.getRule.call(this);
527         
528         if (this.bydayRadio.checked) {
529             rrule.byday = this.wkNumber.getValue() + this.wkDay.getValue();
530         } else {
531             rrule.bymonthday = this.bymonthdayday.getValue();
532         }
533         
534         return rrule;
535     },
536     
537     initComponent: function() {
538         this.app = Tine.Tinebase.appMgr.get('Calendar');
539         
540         this.intervalString = this.app.i18n._('Every {0}. Month');
541         
542         this.idPrefix = Ext.id();
543         
544         this.bydayRadio = new Ext.form.Radio({
545             hideLabel: true,
546             boxLabel: this.app.i18n._('at the'), 
547             name: this.idPrefix + 'byRadioGroup', 
548             inputValue: 'BYDAY',
549             checked: true,
550             listeners: {
551                 check: this.onByRadioCheck.createDelegate(this)
552             }
553         });
554
555         this.wkNumber = new Ext.form.ComboBox({
556             requiredGrant : 'editGrant',
557             width: 80,
558             listWidth: 80,
559             triggerAction : 'all',
560             hideLabel     : true,
561             value         : 1,
562             editable      : false,
563             mode          : 'local',
564             store         : [
565                 [1,  this.app.i18n._('first')  ],
566                 [2,  this.app.i18n._('second') ],
567                 [3,  this.app.i18n._('third')  ],
568                 [4,  this.app.i18n._('fourth') ],
569                 [-1, this.app.i18n._('last')   ]
570             ]
571         });
572         
573         var wkdayItems = [];
574         for (var i=0,d; i<7; i++) {
575             d = (i+Ext.DatePicker.prototype.startDay)%7
576             Tine.Calendar.RrulePanel.prototype.wkdays[d];
577             wkdayItems.push([Tine.Calendar.RrulePanel.prototype.wkdays[d], Date.dayNames[d]]);
578         }
579         
580         this.wkDay = new Ext.form.ComboBox({
581             requiredGrant : 'editGrant',
582             width         : 100,
583             listWidth     : 100,
584             triggerAction : 'all',
585             hideLabel     : true,
586             value         : Tine.Calendar.RrulePanel.prototype.wkdays[Ext.DatePicker.prototype.startDay],
587             editable      : false,
588             mode          : 'local',
589             store         : wkdayItems
590         });
591         
592         this.bymonthdayRadio = new Ext.form.Radio({
593             requiredGrant : 'editGrant',
594             hideLabel     : true,
595             boxLabel      : this.app.i18n._('at the'), 
596             name          : this.idPrefix + 'byRadioGroup', 
597             inputValue    : 'BYMONTHDAY',
598             listeners     : {
599                 check: this.onByRadioCheck.createDelegate(this)
600             }
601         });
602         
603         this.bymonthdayday = new Ext.form.NumberField({
604             requiredGrant : 'editGrant',
605             style         : 'text-align:right;',
606             hideLabel     : true,
607             width         : 40,
608             value         : 1,
609             disabled      : true
610         });
611         
612         this.items = [{
613             html: '<div style="padding-top: 5px;">' + 
614                     '<div style="position: relative;">' +
615                         '<table><tr>' +
616                             '<td style="position: relative;" width="65" id="' + this.idPrefix + 'bydayradio"></td>' +
617                             '<td width="100" id="' + this.idPrefix + 'bydaywknumber"></td>' +
618                             '<td width="110" id="' + this.idPrefix + 'bydaywkday"></td>' +
619                         '</tr></table>' +
620                     '</div>' +
621                     '<div style="position: relative;">' +
622                         '<table><tr>' +
623                             '<td width="65" id="' + this.idPrefix + 'bymonthdayradio"></td>' +
624                             '<td width="40" id="' + this.idPrefix + 'bymonthdayday"></td>' +
625                             '<td>.</td>' +
626                          '</tr></table>' +
627                     '</div>' +
628                 '</div>',
629             listeners: {
630                 scope: this,
631                 render: this.onByRender
632             }
633         }];
634         
635         Tine.Calendar.RrulePanel.MONTHLYcard.superclass.initComponent.call(this);
636     },
637     
638     onByRadioCheck: function(radio, checked) {
639         switch(radio.inputValue) {
640             case 'BYDAY':
641                 this.bymonthdayday.setDisabled(checked);
642                 break;
643             case 'BYMONTHDAY':
644                 this.wkNumber.setDisabled(checked);
645                 this.wkDay.setDisabled(checked);
646                 break;
647         }
648     },
649     
650     onByRender: function() {
651         var bybayradioel = Ext.get(this.idPrefix + 'bydayradio');
652         var bybaywknumberel = Ext.get(this.idPrefix + 'bydaywknumber');
653         var bybaywkdayel = Ext.get(this.idPrefix + 'bydaywkday');
654         
655         var bymonthdayradioel = Ext.get(this.idPrefix + 'bymonthdayradio');
656         var bymonthdaydayel = Ext.get(this.idPrefix + 'bymonthdayday');
657         
658         if (! (bybayradioel && bymonthdayradioel)) {
659             return this.onByRender.defer(100, this, arguments);
660         }
661         
662         this.bydayRadio.render(bybayradioel);
663         this.wkNumber.render(bybaywknumberel);
664         this.wkNumber.wrap.setWidth(80);
665         this.wkDay.render(bybaywkdayel);
666         this.wkDay.wrap.setWidth(100);
667         
668         this.bymonthdayRadio.render(bymonthdayradioel);
669         this.bymonthdayday.render(bymonthdaydayel);
670     },
671     
672     setRule: function(rrule) {
673         Tine.Calendar.RrulePanel.MONTHLYcard.superclass.setRule.call(this, rrule);
674         
675         if (rrule.byday) {
676             this.bydayRadio.setValue(true);
677             this.bymonthdayRadio.setValue(false);
678             this.onByRadioCheck(this.bydayRadio, true);
679             this.onByRadioCheck(this.bymonthdayRadio, false);
680             
681             var parts = rrule.byday.match(/([\-\d]{1,2})([A-Z]{2})/);
682             this.wkNumber.setValue(parts[1]);
683             this.wkDay.setValue(parts[2]);
684             
685         }
686         
687         if (rrule.bymonthday) {
688             this.bydayRadio.setValue(false);
689             this.bymonthdayRadio.setValue(true);
690             this.onByRadioCheck(this.bydayRadio, false);
691             this.onByRadioCheck(this.bymonthdayRadio, true);
692             
693             this.bymonthdayday.setValue(rrule.bymonthday);
694         }
695
696     }
697     
698 });
699
700 Tine.Calendar.RrulePanel.YEARLYcard = Ext.extend(Tine.Calendar.RrulePanel.AbstractCard, {
701     
702     freq: 'YEARLY',
703     
704     getRule: function() {
705         var rrule = Tine.Calendar.RrulePanel.MONTHLYcard.superclass.getRule.call(this);
706         
707         if (this.bydayRadio.checked) {
708             rrule.byday = this.wkNumber.getValue() + this.wkDay.getValue();
709         } else {
710             rrule.bymonthday = this.bymonthdayday.getValue();
711         }
712         
713         rrule.bymonth = this.bymonth.getValue();
714         return rrule;
715     },
716     
717     initComponent: function() {
718         this.app = Tine.Tinebase.appMgr.get('Calendar');
719         
720         this.intervalString = this.app.i18n._('Every {0}. Year');
721         
722         this.idPrefix = Ext.id();
723         
724         this.bydayRadio = new Ext.form.Radio({
725             requiredGrant : 'editGrant',
726             hideLabel     : true,
727             boxLabel      : this.app.i18n._('at the'), 
728             name          : this.idPrefix + 'byRadioGroup', 
729             inputValue    : 'BYDAY',
730             listeners     : {
731                 check: this.onByRadioCheck.createDelegate(this)
732             }
733         });
734
735         this.wkNumber = new Ext.form.ComboBox({
736             requiredGrant : 'editGrant',
737             width         : 80,
738             listWidth     : 80,
739             triggerAction : 'all',
740             hideLabel     : true,
741             value         : 1,
742             editable      : false,
743             mode          : 'local',
744             disabled      : true,
745             store         : [
746                 [1,  this.app.i18n._('first')  ],
747                 [2,  this.app.i18n._('second') ],
748                 [3,  this.app.i18n._('third')  ],
749                 [4,  this.app.i18n._('fourth') ],
750                 [-1, this.app.i18n._('last')   ]
751             ]
752         });
753         
754         var wkdayItems = [];
755         for (var i=0,d; i<7; i++) {
756             d = (i+Ext.DatePicker.prototype.startDay)%7
757             Tine.Calendar.RrulePanel.prototype.wkdays[d];
758             wkdayItems.push([Tine.Calendar.RrulePanel.prototype.wkdays[d], Date.dayNames[d]]);
759         }
760         
761         this.wkDay = new Ext.form.ComboBox({
762             requiredGrant : 'editGrant',
763             width         : 100,
764             listWidth     : 100,
765             triggerAction : 'all',
766             hideLabel     : true,
767             value         : Tine.Calendar.RrulePanel.prototype.wkdays[Ext.DatePicker.prototype.startDay],
768             editable      : false,
769             mode          : 'local',
770             store         : wkdayItems,
771             disabled      : true
772         });
773         
774         this.bymonthdayRadio = new Ext.form.Radio({
775             requiredGrant : 'editGrant',
776             hideLabel     : true,
777             boxLabel      : this.app.i18n._('at the'), 
778             name          : this.idPrefix + 'byRadioGroup', 
779             inputValue    : 'BYMONTHDAY',
780             checked       : true,
781             listeners     : {
782                 check: this.onByRadioCheck.createDelegate(this)
783             }
784         });
785         
786         this.bymonthdayday = new Ext.form.NumberField({
787             requiredGrant : 'editGrant',
788             style         : 'text-align:right;',
789             hideLabel     : true,
790             width         : 40,
791             value         : 1
792         });
793         
794         var monthItems = [];
795         for (var i=0; i<Date.monthNames.length; i++) {
796             monthItems.push([i+1, Date.monthNames[i]]);
797         }
798         
799         this.bymonth = new Ext.form.ComboBox({
800             requiredGrant : 'editGrant',
801             width         : 100,
802             listWidth     : 100,
803             triggerAction : 'all',
804             hideLabel     : true,
805             value         : 1,
806             editable      : false,
807             mode          : 'local',
808             store         : monthItems
809         });
810         
811         this.items = [{
812             html: '<div style="padding-top: 5px;">' +
813                     '<div style="position: relative;">' +
814                         '<table><tr>' +
815                             '<td style="position: relative;" width="65" id="' + this.idPrefix + 'bydayradio"></td>' +
816                             '<td width="100" id="' + this.idPrefix + 'bydaywknumber"></td>' +
817                             '<td width="110" id="' + this.idPrefix + 'bydaywkday"></td>' +
818                             //'<td style="padding-left: 10px">' + this.app.i18n._('of') + '</td>' +
819                         '</tr></table>' +
820                     '</div>' +
821                     '<div style="position: relative;">' +
822                         '<table><tr>' +
823                             '<td width="65" id="' + this.idPrefix + 'bymonthdayradio"></td>' +
824                             '<td width="40" id="' + this.idPrefix + 'bymonthdayday"></td>' +
825                             '<td>.</td>' +
826                             '<td width="15" style="padding-left: 37px">' + this.app.i18n._('of') + '</td>' +
827                             '<td width="100" id="' + this.idPrefix + 'bymonth"></td>' +
828                          '</tr></table>' +
829                     '</div>' +
830                 '</div>',
831             listeners: {
832                 scope: this,
833                 render: this.onByRender
834             }
835         }];
836         Tine.Calendar.RrulePanel.YEARLYcard.superclass.initComponent.call(this);
837     },
838     
839     onByRadioCheck: function(radio, checked) {
840         switch(radio.inputValue) {
841             case 'BYDAY':
842                 this.bymonthdayday.setDisabled(checked);
843                 break;
844             case 'BYMONTHDAY':
845                 this.wkNumber.setDisabled(checked);
846                 this.wkDay.setDisabled(checked);
847                 break;
848         }
849     },
850     
851     onByRender: function() {
852         var bybayradioel = Ext.get(this.idPrefix + 'bydayradio');
853         var bybaywknumberel = Ext.get(this.idPrefix + 'bydaywknumber');
854         var bybaywkdayel = Ext.get(this.idPrefix + 'bydaywkday');
855         
856         var bymonthdayradioel = Ext.get(this.idPrefix + 'bymonthdayradio');
857         var bymonthdaydayel = Ext.get(this.idPrefix + 'bymonthdayday');
858
859         var bymonthel = Ext.get(this.idPrefix + 'bymonth');
860         
861         if (! (bybayradioel && bymonthdayradioel)) {
862             return this.onByRender.defer(100, this, arguments);
863         }
864         
865         this.bydayRadio.render(bybayradioel);
866         this.wkNumber.render(bybaywknumberel);
867         this.wkNumber.wrap.setWidth(80);
868         this.wkDay.render(bybaywkdayel);
869         this.wkDay.wrap.setWidth(100);
870         
871         this.bymonthdayRadio.render(bymonthdayradioel);
872         this.bymonthdayday.render(bymonthdaydayel);
873         
874         this.bymonth.render(bymonthel);
875         this.bymonth.wrap.setWidth(100);
876     },
877     
878     setRule: function(rrule) {
879         Tine.Calendar.RrulePanel.MONTHLYcard.superclass.setRule.call(this, rrule);
880         
881         if (rrule.byday) {
882             this.bydayRadio.setValue(true);
883             this.bymonthdayRadio.setValue(false);
884             this.onByRadioCheck(this.bydayRadio, true);
885             this.onByRadioCheck(this.bymonthdayRadio, false);
886             
887             var parts = rrule.byday.match(/([\-\d]{1,2})([A-Z]{2})/);
888             this.wkNumber.setValue(parts[1]);
889             this.wkDay.setValue(parts[2]);
890             
891         }
892         
893         if (rrule.bymonthday) {
894             this.bydayRadio.setValue(false);
895             this.bymonthdayRadio.setValue(true);
896             this.onByRadioCheck(this.bydayRadio, false);
897             this.onByRadioCheck(this.bymonthdayRadio, true);
898             
899             this.bymonthdayday.setValue(rrule.bymonthday);
900         }
901         
902         this.bymonth.setValue(rrule.bymonth);
903
904     }
905 });