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