1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc.
  5  *
  6  * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at: https://www.zimbra.com/license
  9  * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15
 10  * have been added to cover use of software over a computer network and provide for limited attribution
 11  * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B.
 12  *
 13  * Software distributed under the License is distributed on an "AS IS" basis,
 14  * WITHOUT WARRANTY OF ANY KIND, either express or implied.
 15  * See the License for the specific language governing rights and limitations under the License.
 16  * The Original Code is Zimbra Open Source Web Client.
 17  * The Initial Developer of the Original Code is Zimbra, Inc.  All rights to the Original Code were
 18  * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015.
 19  *
 20  * All portions of the code are Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * Creates a new appointment recurrence dialog. The view displays itself on construction.
 26  * @constructor
 27  * @class
 28  * This class provides a dialog for creating/editing recurrences for an appointment
 29  *
 30  * @author Parag Shah
 31  * 
 32  * @param {ZmControl}	parent			the element that created this view
 33  * @param {String}	className 		optional class name for this view
 34  * 
 35  * @extends		DwtDialog
 36  */
 37 ZmApptRecurDialog = function(parent, uid, className) {
 38 	DwtDialog.call(this, {parent:parent, className:className, title:ZmMsg.customRepeat});
 39 
 40 	// set html content once (hence, in ctor)
 41 	this.setContent(this._setHtml(uid));
 42 	this._createRepeatSections(uid);
 43 	this._createDwtObjects(uid);
 44 	this._cacheFields();
 45 	this._addEventHandlers();
 46 	this._createTabGroup();
 47 
 48 	this.setButtonListener(DwtDialog.OK_BUTTON, new AjxListener(this, this._okListener));
 49 	this.addSelectionListener(DwtDialog.CANCEL_BUTTON, new AjxListener(this, this._cancelListener));
 50 };
 51 
 52 ZmApptRecurDialog.prototype = new DwtDialog;
 53 ZmApptRecurDialog.prototype.constructor = ZmApptRecurDialog;
 54 
 55 
 56 // Consts
 57 
 58 ZmApptRecurDialog.REPEAT_OPTIONS = [
 59 	{ label: ZmMsg.none, 			value: ZmRecurrence.NONE,		selected: true 	},
 60 	{ label: ZmMsg.daily, 			value: ZmRecurrence.DAILY,		selected: false },
 61 	{ label: ZmMsg.weekly, 			value: ZmRecurrence.WEEKLY,		selected: false },
 62 	{ label: ZmMsg.monthly, 		value: ZmRecurrence.MONTHLY,	selected: false },
 63 	{ label: ZmMsg.yearly, 			value: ZmRecurrence.YEARLY,		selected: false }];
 64 
 65 
 66 // Public methods
 67 
 68 ZmApptRecurDialog.prototype.toString = 
 69 function() {
 70 	return "ZmApptRecurDialog";
 71 };
 72 
 73 ZmApptRecurDialog.prototype.getTabGroupMember = function() {
 74 	return this._tabGroup;
 75 };
 76 
 77 ZmApptRecurDialog.prototype.initialize = 
 78 function(startDate, endDate, repeatType, appt) {
 79     this._startDate = new Date(startDate.getTime());
 80 	this._endDate = new Date(endDate.getTime());
 81     this._origRefDate = startDate;
 82     // based on repeat type, setup the repeat type values
 83 	var repeatType = repeatType || ZmRecurrence.DAILY;
 84 	this._repeatSelect.setSelectedValue(repeatType);
 85 	this._setRepeatSection(repeatType);
 86 
 87 	// dont bother initializing if user is still mucking around
 88 	if (this._saveState)
 89 		return;
 90 
 91 	var startDay = this._startDate.getDay();
 92 	var startDate = this._startDate.getDate();
 93 	var startMonth = this._startDate.getMonth();
 94 
 95 	// reset time based fields
 96 	this._endByField.setValue(AjxDateUtil.simpleComputeDateStr(this._startDate));
 97     this._weeklySelectButton._selected = startDay;
 98     this._weeklySelectButton.setDisplayState(DwtControl.SELECTED);
 99 
100     var formatter = new AjxMessageFormat(ZmMsg.recurWeeklyEveryWeekday);
101     var dayFormatter = formatter.getFormatsByArgumentIndex()[0];
102     this._weeklySelectButton.setText(dayFormatter.format(this._origRefDate));
103 
104     this._weeklyCheckboxes[startDay].checked = true;
105 	this._monthlyDayField.setValue(startDate);
106 	this._monthlyWeekdaySelect.setSelected(startDay);
107 	this._yearlyDayField.setValue(startDate);
108 	this._yearlyMonthSelect.setSelected(startMonth);
109 	this._yearlyWeekdaySelect.setSelected(startDay);
110 	this._yearlyMonthSelectEx.setSelected(startMonth);
111 
112 	this._isDirty = false;
113 
114 	// if given appt object, means user is editing existing appointment's recur rules
115 	if (appt) {
116 		this._populateForEdit(appt);
117 	}
118 };
119 
120 ZmApptRecurDialog.prototype.isDirty =
121 function() {
122 	return this._isDirty;
123 };
124 
125 /**
126  * Gets the selected repeat value.
127  * 
128  * @return	{constant}	the repeat value
129  */
130 ZmApptRecurDialog.prototype.getSelectedRepeatValue = 
131 function() {
132 	return this._repeatSelect.getValue();
133 };
134 
135 /**
136  * Sets repeat end values.
137  * 
138  * @param	{ZmAppt}	appt		the appointment
139  */
140 ZmApptRecurDialog.prototype.setRepeatEndValues = 
141 function(appt) {
142     var recur = appt._recurrence;
143 	recur.repeatEndType = this._getRadioOptionValue(this._repeatEndName);
144 
145 	// add any details for the select option
146 	if (recur.repeatEndType == "A")
147 		recur.repeatEndCount = this._endIntervalField.getValue();
148 	else if (recur.repeatEndType == "D")
149 		recur.repeatEndDate = AjxDateUtil.simpleParseDateStr(this._endByField.getValue());
150 };
151 
152 /**
153  * Sets custom daily values.
154  * 
155  * @param	{ZmAppt}	appt		the appointment
156  */
157 ZmApptRecurDialog.prototype.setCustomDailyValues = 
158 function(appt) {
159 	var recur = appt._recurrence;
160 	var value = this._getRadioOptionValue(this._dailyRadioName);
161     recur._startDate = new Date(this._origRefDate);	
162 	recur.repeatCustom = "1";
163 	recur.repeatWeekday = false;
164 	
165 	if (value == "2") {
166 		recur.repeatWeekday = true;
167         //Let's check if it is sat/sunday today
168         var d = new Date(this._origRefDate); //Using the start date specified...can be in the past
169         if(d.getDay()==AjxDateUtil.SUNDAY || d.getDay()==AjxDateUtil.SATURDAY){
170             recur._startDate = AjxDateUtil.getDateForNextDay(d,AjxDateUtil.MONDAY); // get subsequent monday, weekday
171         }
172    		recur.repeatCustomCount = 1;
173     } else {
174 		recur.repeatCustomCount = value == "3" ? (Number(this._dailyField.getValue())) : 1;
175 	}
176 };
177 
178 /**
179  * Sets custom weekly values.
180  * 
181  * @param	{ZmAppt}	appt		the appointment
182  */
183 ZmApptRecurDialog.prototype.setCustomWeeklyValues =
184 function(appt) {
185     var recur = appt._recurrence;
186 	recur.repeatWeeklyDays = []
187 	recur.repeatCustom = "1";
188     recur._startDate = new Date(this._origRefDate);
189 	var value = this._getRadioOptionValue(this._weeklyRadioName);
190     var currentDay = recur._startDate.getDay();
191 	if (value == "1") {
192 		recur.repeatCustomCount = 1;
193         var startDay = this._weeklySelectButton._selected;
194         switch(startDay){
195             case 7: //Separator
196                     break;
197             case 8: //Mon, wed, Fri
198                     recur.repeatWeeklyDays.push(ZmCalItem.SERVER_WEEK_DAYS[AjxDateUtil.MONDAY]);
199                     recur.repeatWeeklyDays.push(ZmCalItem.SERVER_WEEK_DAYS[AjxDateUtil.WEDNESDAY]);
200                     recur.repeatWeeklyDays.push(ZmCalItem.SERVER_WEEK_DAYS[AjxDateUtil.FRIDAY]);
201                     startDay = AjxDateUtil.MONDAY;
202                     while(startDay < currentDay){
203                         startDay += 2;
204                     }
205                     break;
206             case 9: //Tue, Thu
207                     recur.repeatWeeklyDays.push(ZmCalItem.SERVER_WEEK_DAYS[AjxDateUtil.TUESDAY]);
208                     recur.repeatWeeklyDays.push(ZmCalItem.SERVER_WEEK_DAYS[AjxDateUtil.THURSDAY]);
209                     startDay = AjxDateUtil.TUESDAY;
210                     while(startDay < currentDay){
211                         startDay += 2;
212                     }
213                     break;
214             case 10: //Sat, Sunday
215                     recur.repeatWeeklyDays.push(ZmCalItem.SERVER_WEEK_DAYS[AjxDateUtil.SATURDAY]);
216                     recur.repeatWeeklyDays.push(ZmCalItem.SERVER_WEEK_DAYS[AjxDateUtil.SUNDAY]);
217                     startDay = currentDay == AjxDateUtil.SUNDAY? currentDay:AjxDateUtil.SATURDAY;
218                     break;
219             default:
220                     recur.repeatWeeklyDays.push(ZmCalItem.SERVER_WEEK_DAYS[this._weeklySelectButton._selected/*getValue()*/]);
221                     break;
222         }
223         recur._startDate = AjxDateUtil.getDateForNextDay(new Date(this._origRefDate),startDay);
224         //recur._endDate = recur._startDate;
225     } else {
226 		recur.repeatCustomCount = Number(this._weeklyField.getValue());
227         var selectedDays = [];
228         for (var i = 0; i < this._weeklyCheckboxes.length; i++) {
229 			if (this._weeklyCheckboxes[i].checked){
230                 selectedDays.push(i);
231                 recur.repeatWeeklyDays.push(ZmCalItem.SERVER_WEEK_DAYS[i]);
232             }
233         }
234         var startDay = currentDay;
235         for(var i =0; i < selectedDays.length;i++){
236             var startDay = selectedDays[i];
237             if(startDay >= currentDay) { //In past
238                 break;
239             }
240         }
241         recur._startDate = AjxDateUtil.getDateForNextDay(new Date(this._origRefDate),startDay);
242     }
243 };
244 
245 /**
246  * Sets custom monthly values.
247  * 
248  * @param	{ZmAppt}	appt		the appointment
249  */
250 ZmApptRecurDialog.prototype.setCustomMonthlyValues =
251 function(appt) {
252 	var recur = appt._recurrence;
253 	recur.repeatCustom = "1";
254    	var value = this._getRadioOptionValue(this._monthlyRadioName);
255     recur._startDate = new Date(this._origRefDate);
256 	if (value == "1") {
257 		recur.repeatCustomType = "S";
258 		recur.repeatCustomCount = this._monthlyMonthField.getValue();
259 		recur.repeatMonthlyDayList = [this._monthlyDayField.getValue()];
260         recur.repeatCustomMonthDay = this._monthlyDayField.getValue();
261         recur._startDate.setDate(recur.repeatCustomMonthDay);
262         var today = new Date(this._origRefDate); //Reference date...
263         var diff = (today - recur._startDate);
264         if(diff >= AjxDateUtil.MSEC_PER_DAY || today.getDate() > recur._startDate.getDate()){ // was in the past, so let's use the next date
265             recur._startDate.setMonth(recur._startDate.getMonth()+1);
266         }
267 
268     } else {
269 		recur.repeatCustomType = "O";
270 		recur.repeatCustomCount = this._monthlyMonthFieldEx.getValue();
271 		recur.repeatBySetPos = this._monthlyDaySelect.getValue();
272 		recur.repeatCustomDayOfWeek = ZmCalItem.SERVER_WEEK_DAYS[this._monthlyWeekdaySelect.getValue()];
273         recur.repeatCustomDays = this.getWeekdaySelectValue(this._monthlyWeekdaySelect);
274 
275         if(recur.repeatBySetPos==-1){ // Last day
276             var lastDate = new Date(this._origRefDate);
277             lastDate.setDate(AjxDateUtil.daysInMonth(lastDate.getFullYear(),lastDate.getMonth())); //Date is now last date of this month
278             var lastDayDate = this.getPossibleStartDate(this._monthlyWeekdaySelect.getValue(), lastDate, recur.repeatBySetPos);
279 
280             //Check if it is already paased
281             var today = new Date(this._origRefDate);
282             var diff = (today - lastDayDate);
283             var isInPast = today.getTime() > lastDayDate.getTime();
284             if(diff >= AjxDateUtil.MSEC_PER_DAY || isInPast ){ //In the past
285                 // Go for next month
286                 lastDate.setMonth(lastDate.getMonth()+1);
287                 recur._startDate = this.getPossibleStartDate(this._monthlyWeekdaySelect.getValue(), lastDate, recur.repeatBySetPos);                              
288             }else{
289                  recur._startDate = lastDayDate;
290             }
291         }else{
292             var first = new Date(this._origRefDate);
293             first.setDate(1);  
294             recur._startDate = this.getPossibleStartDate(this._monthlyWeekdaySelect.getValue(), first, recur.repeatBySetPos); //AjxDateUtil.getDateForNextDay(first,this.getFirstWeekDayOffset(this._monthlyWeekdaySelect),recur.repeatBySetPos);
295              //Check if it is already paased
296             var today = new Date(this._origRefDate);
297             var diff = (today - recur._startDate);
298             var isInPast = today.getTime() > recur._startDate.getTime();
299             if(diff >= AjxDateUtil.MSEC_PER_DAY || isInPast){ //In the past
300                 // Go for next month, find the date as per rule
301                 first.setMonth(first.getMonth() + 1);//Next month
302                 recur._startDate = this.getPossibleStartDate(this._monthlyWeekdaySelect.getValue(), first, recur.repeatBySetPos);
303             }
304         }
305     }
306 };
307 
308 /**
309  * Sets custom yearly values.
310  * 
311  * @param	{ZmAppt}	appt		the appointment
312  */
313 ZmApptRecurDialog.prototype.setCustomYearlyValues =
314 function(appt) {
315 	appt._recurrence.repeatCustom = "1";
316     var recur = appt._recurrence;
317     recur._startDate = new Date(this._origRefDate);
318 	var value = this._getRadioOptionValue(this._yearlyRadioName);
319 
320 	if (value == "1") {
321 		appt._recurrence.repeatCustomType = "S";
322 		appt._recurrence.repeatCustomMonthDay = this._yearlyDayField.getValue();
323 		appt._recurrence.repeatYearlyMonthsList = this._yearlyMonthSelect.getValue() + 1;
324         //Create date out of it
325         var d  = new Date(this._origRefDate);
326         d.setDate(appt._recurrence.repeatCustomMonthDay);
327         d.setMonth(this._yearlyMonthSelect.getValue());
328         //Try to judge, if this date is in future
329         var today = new Date(this._origRefDate);
330         var diff = (today - d);
331         var isInPast = today.getTime() > d.getTime();
332         if( diff >= AjxDateUtil.MSEC_PER_DAY || isInPast){ //In the past
333             d.setFullYear(d.getFullYear()+1);
334         }
335         appt._recurrence._startDate = d;
336         appt._recurrence._endDate = d;
337     } else {
338 		appt._recurrence.repeatCustomType = "O";
339 		appt._recurrence.repeatBySetPos = this._yearlyDaySelect.getValue();
340 		appt._recurrence.repeatCustomDayOfWeek = ZmCalItem.SERVER_WEEK_DAYS[this._yearlyWeekdaySelect.getValue()];
341         appt._recurrence.repeatCustomDays = this.getWeekdaySelectValue(this._yearlyWeekdaySelect);
342 
343         appt._recurrence.repeatYearlyMonthsList = this._yearlyMonthSelectEx.getValue() + 1;
344         var d = new Date(this._origRefDate);
345         d.setMonth(this._yearlyMonthSelectEx.getValue());
346         //Check if date is in past
347         if(appt._recurrence.repeatBySetPos < 0){ // we want last day
348             d.setDate(AjxDateUtil.daysInMonth(d.getFullYear(),d.getMonth()));
349         }else{
350             d.setDate(1);
351         }
352         var dt = this.getPossibleStartDate(this._yearlyWeekdaySelect.getValue(), d, appt._recurrence.repeatBySetPos);
353 
354         var today = new Date(this._origRefDate);
355         var diff = (today -dt);
356         var isInPast = today.getTime() > dt.getTime();
357         if(diff >= AjxDateUtil.MSEC_PER_DAY || isInPast){ // In the past
358             d.setFullYear(d.getFullYear()+1);
359             if(appt._recurrence.repeatBySetPos < 0){ // we want last day
360                 d.setDate(AjxDateUtil.daysInMonth(d.getFullYear(),d.getMonth()));
361             }else{
362                 d.setDate(1);
363             }
364         }
365         appt._recurrence._startDate = this.getPossibleStartDate(this._yearlyWeekdaySelect.getValue(), d, appt._recurrence.repeatBySetPos);
366         appt._recurrence._endDate = appt._recurrence._startDate;
367     }
368 };
369 
370 ZmApptRecurDialog.prototype.addSelectionListener = 
371 function(buttonId, listener) {
372 	this._button[buttonId].addSelectionListener(listener);
373 };
374 
375 ZmApptRecurDialog.prototype.clearState = 
376 function() {
377 	this._saveState = false;
378 	this._cleanup();
379 };
380 
381 ZmApptRecurDialog.prototype.isValid = 
382 function() {
383 	var valid = true;
384 
385 	// ONLY for the selected options, check if their fields are valid
386 	var repeatValue = this._repeatSelect.getValue();
387 
388 	if (repeatValue == ZmRecurrence.DAILY) {
389 		if (this._dailyFieldRadio.checked)
390 			valid = this._dailyField.isValid();
391 		if (!valid)
392 			this._dailyField.blur();
393 	} else if (repeatValue == ZmRecurrence.WEEKLY) {
394 		if (this._weeklyFieldRadio.checked) {
395 			valid = this._weeklyField.isValid();
396 			if (valid) {
397 				valid = false;
398 				for (var i=0; i<this._weeklyCheckboxes.length; i++) {
399 					if (this._weeklyCheckboxes[i].checked) {
400 						valid = true;
401 						break;
402 					}
403 				}
404 			}
405 			// weekly section is special - force a focus if valid to clear out error
406 			this._weeklyField.focus();
407 			this._weeklyField.blur();
408 		}
409 	} else if (repeatValue == ZmRecurrence.MONTHLY) {
410 		if (this._monthlyDefaultRadio.checked) {
411 			valid = this._monthlyMonthField.isValid() && this._monthlyDayField.isValid();
412 			if (!valid) {
413 				this._monthlyMonthField.blur();
414 				this._monthlyDayField.blur();
415 			}
416 		} else {
417 			valid = this._monthlyMonthFieldEx.isValid();
418 			if (!valid)
419 				this._monthlyMonthFieldEx.blur();
420 		}
421 	} else if (repeatValue == ZmRecurrence.YEARLY) {
422 		if (this._yearlyDefaultRadio.checked)
423 			valid = this._yearlyDayField.isValid();
424 		if (!valid)
425 			this._yearlyDayField.blur();
426 	}
427 
428 	// check end section
429 	if (valid) {
430 		if (this._endAfterRadio.checked) {
431 			valid = this._endIntervalField.isValid();
432 			if (!valid)
433 				this._endIntervalField.blur();
434 		} else if (this._endByRadio.checked) {
435 			valid = this._endByField.isValid();
436 			if (!valid)
437 				this._endByField.blur();
438 		}
439 	}
440 
441 	return valid;
442 };
443 
444 
445 // Private / protected methods
446  
447 ZmApptRecurDialog.prototype._setHtml = 
448 function(uid) {
449 	this._repeatSelectId = Dwt.getNextId();
450 	this._repeatSectionId = Dwt.getNextId();
451 	this._repeatEndDivId = Dwt.getNextId();
452 	var html = new Array();
453 	var i = 0;
454 	
455 	html[i++] = "<table width=450>";
456 	html[i++] = "<tr><td><fieldset";
457 	if (AjxEnv.isMozilla)
458 		html[i++] = " style='border:1px dotted #555'";
459 	html[i++] = "><legend style='color:#555555'>";
460 	html[i++] = ZmMsg.repeat;
461 	html[i++] = "</legend><div style='height:110px'>";
462 	html[i++] = "<div id='";
463 	html[i++] = this._repeatSelectId;
464 	html[i++] = "' style='margin-bottom:.25em;'></div><div id='";
465 	html[i++] = this._repeatSectionId;
466 	html[i++] = "'></div>";
467 	html[i++] = "</div></fieldset></td></tr>";
468 	html[i++] = "<tr><td><div id='";
469 	html[i++] = this._repeatEndDivId;
470 	html[i++] = "'><fieldset";
471 	if (AjxEnv.isMozilla)
472 		html[i++] = " style='border:1px dotted #555'";
473 	html[i++] = "><legend style='color:#555'>";
474 	html[i++] = ZmMsg.end;
475 	html[i++] = "</legend>";
476 	html[i++] = this._getEndHtml(uid);
477 	html[i++] = "</fieldset></div></td></tr>";
478 	html[i++] = "</table>";
479 
480 	return html.join("");
481 };
482 
483 ZmApptRecurDialog.prototype._getEndHtml = 
484 function(uid) {
485 	this._repeatEndName = Dwt.getNextId();
486 	this._noEndDateRadioId = "NO_END_DATE_RADIO_" + uid; // Dwt.getNextId();
487 	this._endByRadioId = "END_BY_RADIO_" + uid; // Dwt.getNextId();
488 	this._endAfterRadioId = "END_AFTER_RADIO_" + uid; // Dwt.getNextId();
489     // unique ids for endIntervalFieldId and endByField
490     this._endIntervalFieldId = "END_INTERVAL_FIELD_" + uid; // Dwt.getNextId();
491 	this._endByFieldId = "END_BY_FIELD_" + uid; // Dwt.getNextId();
492 	this._endByButtonId = "END_BY_BUTTON_" + uid; // Dwt.getNextId();
493 
494 	var html = new Array();
495 	var i = 0;
496 
497 	// start table
498 	html[i++] = "<table class='ZRadioButtonTable'>";
499 	// no end date
500 	html[i++] = "<tr><td width=1%><input checked value='N' type='radio' name='";
501 	html[i++] = this._repeatEndName;
502 	html[i++] = "' id='";
503 	html[i++] = this._noEndDateRadioId;
504 	html[i++] = "'></td><td colspan=2>";
505 	html[i++] = "<label for='";
506 	html[i++] = this._noEndDateRadioId;
507 	html[i++] = "'>"
508 	html[i++] = ZmMsg.recurEndNone;
509 	html[i++] = "</label>"
510 	html[i++] = "</td></tr>";
511 	// end after <num> occurrences
512 	html[i++] = "<tr><td><input type='radio' value='A' name='";
513 	html[i++] = this._repeatEndName;
514 	html[i++] = "' id='";
515 	html[i++] = this._endAfterRadioId;
516 	html[i++] = "'></td><td colspan=2>";
517 	html[i++] = "<table><tr>";
518 	var formatter = new AjxMessageFormat(ZmMsg.recurEndNumber);
519 	var segments = formatter.getSegments();
520 	for (var s = 0; s < segments.length; s++) {
521 		html[i++] = "<td>";
522 		var segment = segments[s];
523 		if (segment instanceof AjxMessageFormat.MessageSegment && 
524 			segment.getIndex() == 0) {
525 			html[i++] = "<span id='";
526 			html[i++] = this._endIntervalFieldId;
527 			html[i++] = "' class='ZInlineInput'></span>";
528 		}
529 		else {
530 			html[i++] = "<label for='";
531 			html[i++] = this._endAfterRadioId;
532 			html[i++] = "'>";
533 			html[i++] = segment.toSubPattern();
534 			html[i++] = "</label>";
535 		}
536 		html[i++] = "</td>";
537 	}
538 	html[i++] = "</tr></table>";
539 	html[i++] = "</td></tr>";
540 	// end by <date>
541 	html[i++] = "<tr><td><input type='radio' value='D' name='";
542 	html[i++] = this._repeatEndName;
543 	html[i++] = "' id='";
544 	html[i++] = this._endByRadioId;
545 	html[i++] = "'></td><td>";
546 	html[i++] = "<table><tr>";
547 	var formatter = new AjxMessageFormat(ZmMsg.recurEndByDate);
548 	var segments = formatter.getSegments();
549 	for (var s = 0; s < segments.length; s++) {
550 		var segment = segments[s];
551 		if (segment instanceof AjxMessageFormat.MessageSegment && 
552 			segment.getIndex() == 0) {
553 			html[i++] = "<td id='";
554 			html[i++] = this._endByFieldId;
555 			html[i++] = "' style='padding:0 0 0 .5em'></td><td id='";
556 			html[i++] = this._endByButtonId;
557 			html[i++] = "' style='padding:0 .5em 0 0'></td>";
558 		}
559 		else {
560 			html[i++] = "<td style='padding-left:2px;padding-right:2px'>";
561 			html[i++] = "<label for='";
562                         html[i++] = this._endByRadioId;
563                         html[i++] = "'>";
564 			html[i++] = segment.toSubPattern();
565                         html[i++] = "</label>";
566 		}
567 		html[i++] = "</td>";
568 	}
569 	html[i++] = "</tr></table>";
570 	html[i++] = "</td></tr>";
571 	// end table
572 	html[i++] = "</table>";
573 	return html.join("");
574 };
575 
576 ZmApptRecurDialog.prototype._createRepeatSections = 
577 function(uid) {
578 	var sectionDiv = document.getElementById(this._repeatSectionId);
579 	if (sectionDiv) {
580 		var div = document.createElement("div");
581 		div.style.position = "relative";
582 		div.style.display = "none";
583 		div.id = this._repeatDailyId = "REPEAT_DAILY_DIV_" + uid; //Dwt.getNextId();
584 		div.innerHTML = this._createRepeatDaily(uid);
585 		sectionDiv.appendChild(div);
586 
587 		var div = document.createElement("div");
588 		div.style.position = "relative";
589 		div.style.display = "none";
590 		div.id = this._repeatWeeklyId = "REPEAT_WEEKLY_DIV_" + uid; // Dwt.getNextId();
591 		div.innerHTML = this._createRepeatWeekly(uid);
592 		sectionDiv.appendChild(div);
593 	
594 		var div = document.createElement("div");
595 		div.style.position = "relative";
596 		div.style.display = "none";
597 		div.id = this._repeatMonthlyId = "REPEAT_MONTHLY_DIV_" + uid; // Dwt.getNextId();
598 		div.innerHTML = this._createRepeatMonthly(uid);
599 		sectionDiv.appendChild(div);
600 	
601 		var div = document.createElement("div");
602 		div.style.position = "relative";
603 		div.style.display = "none";
604 		div.id = this._repeatYearlyId = "REPEAT_YEARLY_DIV_" + uid; // Dwt.getNextId();
605 		div.innerHTML = this._createRepeatYearly(uid);
606 		sectionDiv.appendChild(div);
607 	}
608 };
609 
610 ZmApptRecurDialog.prototype._createRepeatDaily = 
611 function(uid) {
612 	this._dailyRadioName = "DAILY_RADIO_" + uid; // Dwt.getNextId();
613 	this._dailyDefaultId = "DAILY_DEFAULT_" + uid; // Dwt.getNextId();
614 	this._dailyWeekdayId = "DAILY_WEEKDAY_" + uid; // Dwt.getNextId();
615 	this._dailyFieldRadioId = "DAILY_FIELD_RADIO_" + uid; // Dwt.getNextId();
616 	this._dailyFieldId = "DAILY_FIELD_" + uid; // Dwt.getNextId();
617 
618 	var html = new Array();
619 	var i = 0;
620 
621 	// start table
622 	html[i++] = "<table class='ZRadioButtonTable'>";
623 	// every day
624 	html[i++] = "<tr><td><input checked value='1' type='radio' name='";
625 	html[i++] = this._dailyRadioName;
626 	html[i++] = "' id='";
627 	html[i++] = this._dailyDefaultId;
628 	html[i++] = "'></td>";
629 	html[i++] = "<td>";
630 	html[i++] = "<label for='";
631 	html[i++] = this._dailyDefaultId;
632 	html[i++] = "'>";
633 	html[i++] = ZmMsg.recurDailyEveryDay;
634 	html[i++] = "</label>"
635 	html[i++] = "</td></tr>";
636 	// every weekday
637 	html[i++] = "<tr><td><input value='2' type='radio' name='";
638 	html[i++] = this._dailyRadioName;
639 	html[i++] = "' id='";
640 	html[i++] = this._dailyWeekdayId;
641 	html[i++] = "'></td>";
642 	html[i++] = "<td>";
643 	html[i++] = "<label for='";
644 	html[i++] = this._dailyWeekdayId;
645 	html[i++] = "'>";
646 	html[i++] = ZmMsg.recurDailyEveryWeekday;
647 	html[i++] = "</label>";
648 	html[i++] = "</td></tr>";
649 	// every <num> days
650 	html[i++] = "<tr><td><input value='3' type='radio' name='";
651 	html[i++] = this._dailyRadioName;
652 	html[i++] = "' id='";
653 	html[i++] = this._dailyFieldRadioId;
654 	html[i++] = "'></td><td>";
655 	html[i++] = "<table><tr>";
656 	var formatter = new AjxMessageFormat(ZmMsg.recurDailyEveryNumDays);
657 	var segments = formatter.getSegments();
658 	for (var s = 0; s < segments.length; s++) {
659 		html[i++] = "<td>";
660 		var segment = segments[s];
661 		if (segment instanceof AjxMessageFormat.MessageSegment &&
662 			segment.getIndex() == 0) {
663 			html[i++] = "<span id='";
664 			html[i++] = this._dailyFieldId;
665 			html[i++] = "' class='ZInlineInput'></span>";
666 		}
667 		else {
668 			html[i++] = "<label for='";
669                         html[i++] = this._dailyFieldRadioId;
670                         html[i++] = "'>";
671 			html[i++] = segment.toSubPattern();
672                         html[i++] = "</label>";
673 		}
674 		html[i++] = "</td>";
675 	}
676 	html[i++] = "</tr></table>";
677 	html[i++] = "</td></tr>";
678 	// end table
679 	html[i++] = "</table>";
680 	return html.join("");
681 };
682 
683 ZmApptRecurDialog.prototype._createRepeatWeekly = 
684 function(uid) {
685 	this._weeklyRadioName = "WEEKLY_RADIO_" + uid; //Dwt.getNextId();
686 	this._weeklyCheckboxName = "WEEKLY_CHECKBOX_NAME_" + uid ;//Dwt.getNextId();
687 	this._weeklyDefaultId = "WEEKLY_DEFAULT_" + uid ; //Dwt.getNextId();
688 	this._weeklySelectId = "WEEKLY_SELECT_" + uid ;//Dwt.getNextId();
689 	this._weeklyFieldRadioId = "WEEKLY_FIELD_RADIO_" + uid //Dwt.getNextId();
690 	this._weeklyFieldId = "WEEKLY_FIELD_" + uid ;//Dwt.getNextId();
691 
692 	var html = new Array();
693 	var i = 0;
694 
695 	// start table
696 	html[i++] = "<table class='ZRadioButtonTable'>";
697 	// every <weekday>
698 	html[i++] = "<tr><td><input checked value='1' type='radio' name='";
699 	html[i++] = this._weeklyRadioName;
700 	html[i++] = "' id='";
701 	html[i++] = this._weeklyDefaultId;
702 	html[i++] = "'></td><td>";
703 	html[i++] = "<table><tr>";
704 	var formatter = new AjxMessageFormat(ZmMsg.recurWeeklyEveryWeekday);
705 	var segments = formatter.getSegments();
706 	for (var s = 0; s < segments.length; s++) {
707 		var segment = segments[s];
708 		var index = segment instanceof AjxMessageFormat.MessageSegment
709 				  ? segment.getIndex() : -1;
710 		if (index == 0) {
711 			html[i++] = "<td id='";
712 			html[i++] = this._weeklySelectId;
713 			html[i++] = "' style='padding:0 .5em'>";
714 		}
715 		else {
716 			html[i++] = "<td>";
717 			html[i++] = "<label for='";
718 			html[i++] = this._weeklyDefaultId;
719 			html[i++] = "'>";
720 			html[i++] = segment.toSubPattern();
721 			html[i++] = "</label>";
722 		}
723 		html[i++] = "</td>";
724 	}
725 	html[i++] = "</tr></table>";
726 	html[i++] = "</td></tr>";
727 	// every <num> weeks on <days of week>
728 	html[i++] = "<tr valign='top'><td><input value='2' type='radio' name='";
729 	html[i++] = this._weeklyRadioName;
730 	html[i++] = "' id='";
731 	html[i++] = this._weeklyFieldRadioId;
732 	html[i++] = "'></td>";
733 	html[i++] = "<td>";
734 	html[i++] = "<table><tr>";
735 	var formatter = new AjxMessageFormat(ZmMsg.recurWeeklyEveryNumWeeksDate);
736 	var segments = formatter.getSegments();
737 	for (var s = 0; s < segments.length; s++) {
738 		var segment = segments[s];
739 		var index = segment instanceof AjxMessageFormat.MessageSegment
740 				  ? segment.getIndex() : -1;
741 		if (index == 0) {
742 			html[i++] = "<td id='";
743 			html[i++] = this._weeklyFieldId;
744 			html[i++] = "' style='padding:0 .5em'>";
745 		}
746 		else if (index == 1) {
747 			html[i++] = "<td>";
748 			html[i++] = "<table style='margin-top:.25em;'><tr>";
749 			for (var j = 0; j < AjxDateUtil.WEEKDAY_MEDIUM.length; j++) {
750 				var checkBoxId = Dwt.getNextId(this._weeklyCheckboxName + "_");
751 				html[i++] = "<td><input type='checkbox' name='";
752 				html[i++] = this._weeklyCheckboxName;
753 				html[i++] = "' id='"
754 				html[i++] = checkBoxId;
755 				html[i++] = "'></td><td style='padding-right:.75em;'>";
756 				html[i++] = "<label for='";
757 				html[i++] = checkBoxId;
758 				html[i++] = "'>";
759 				html[i++] = AjxDateUtil.WEEKDAY_MEDIUM[j];
760 				html[i++] = "</label>";
761 				html[i++] = "</td>";
762 			}
763 			html[i++] = "</tr></table>";
764 		}
765 		else if (index == 2) {
766 			html[i++] = "</td></tr></table>";
767 			html[i++] = "<table><tr>";
768 			continue;
769 		}
770 		else {
771 			html[i++] = "<td>";
772 			html[i++] = "<label for='";
773 			html[i++] = this._weeklyFieldRadioId;
774 			html[i++] = "'>";
775 			html[i++] = segment.toSubPattern();
776 			html[i++] = "</label>";
777 		}
778 		html[i++] = "</td>";
779 	}
780 	html[i++] = "</tr></table>";
781 	html[i++] = "</td></tr>";
782 	// end table
783 	html[i++] = "</table>";
784 
785 	return html.join("");
786 };
787 
788 ZmApptRecurDialog.prototype._createRepeatMonthly = 
789 function(uid) {
790 	this._monthlyRadioName = "MONTHLY_RADIO_" + uid ;//Dwt.getNextId();
791 	this._monthlyDefaultId = "MONTHLY_DEFAULT_" + uid;// Dwt.getNextId();
792 	this._monthlyDayFieldId = "MONTHLY_DAY_FIELD_ID_" + uid; // Dwt.getNextId();
793 	this._monthlyMonthFieldId = "MONTHLY_MONTH_FIELD_" + uid; //Dwt.getNextId();
794 	this._monthlyFieldRadioId = "MONTHLY_FIELD_RADIO_" + uid; //Dwt.getNextId();
795 	this._monthlyDaySelectId = "MONTHLY_DAY_SELECT_" + uid; // Dwt.getNextId();
796 	this._monthlyWeekdaySelectId = "MONTHLY_WEEKDAY_SELECT_" + uid;// Dwt.getNextId();
797 	this._monthlyMonthFieldExId = "MONTHLY_MONTH_FIELD_EX_" + uid; // Dwt.getNextId();
798 
799 	var html = new Array();
800 	var i = 0;
801 
802 	// start table
803 	html[i++] = "<table class='ZRadioButtonTable'>";
804 	// every <num> months on the <day>
805 	html[i++] = "<tr><td><input checked value='1' type='radio' name='";
806 	html[i++] = this._monthlyRadioName;
807 	html[i++] = "' id='";
808 	html[i++] = this._monthlyDefaultId;
809 	html[i++] = "'></td>";
810 	html[i++] = "<td>";
811 	html[i++] = "<table class='ZPropertySheet' cellspacing='6'><tr>";
812 	var formatter = new AjxMessageFormat(ZmMsg.recurMonthlyEveryNumMonthsDate);
813 	var segments = formatter.getSegments();
814 	for (var s = 0; s < segments.length; s++) {
815 		html[i++] = "<td>";
816 		var segment = segments[s];
817 		var index = segment instanceof AjxMessageFormat.MessageSegment
818 				  ? segment.getIndex() : -1;
819 		if (index == 0) {
820 			html[i++] = "<span id='";
821 			html[i++] = this._monthlyDayFieldId;
822 			html[i++] = "' class='ZInlineInput'></span>";
823 		}
824 		else if (index == 1) {
825 			html[i++] = "<span id='";
826 			html[i++] = this._monthlyMonthFieldId;
827 			html[i++] = "' class='ZInlineInput'></span>";
828 		}
829 		else {
830 			html[i++] = "<label for='";
831 			html[i++] = this._monthlyDefaultId;
832 			html[i++] = "'>";
833 			html[i++] = segment.toSubPattern();
834 			html[i++] = "</label>";
835 		}
836 		html[i++] = "</td>";
837 	}
838 	html[i++] = "</tr></table>";
839 	html[i++] = "</td></tr>";
840 	// every <num> months on the <ordinal> <weekday>
841 	html[i++] = "<tr><td><input value='2' type='radio' name='";
842 	html[i++] = this._monthlyRadioName;
843 	html[i++] = "' id='";
844 	html[i++] = this._monthlyFieldRadioId;
845 	html[i++] = "'></td>";
846 	html[i++] = "<td>";
847 	html[i++] = "<table class='ZPropertySheet' cellspacing='6'><tr>";
848 	var formatter = new AjxMessageFormat(ZmMsg.recurMonthlyEveryNumMonthsNumDay);
849 	var segments = formatter.getSegments();
850 	for (var s = 0; s < segments.length; s++) {
851 		var segment = segments[s];
852 		var index = segment instanceof AjxMessageFormat.MessageSegment
853 				  ? segment.getIndex() : -1;
854 		if (index == 0) {
855 			html[i++] = "<td id='";
856 			html[i++] = this._monthlyDaySelectId;
857 			html[i++] = "' style='overflow:hidden;'>";
858 		}
859 		else if (index == 1) {
860 			html[i++] = "<td id='";
861 			html[i++] = this._monthlyWeekdaySelectId;
862 			html[i++] = "' style='overflow:hidden;'>";
863 		}
864 		else if (index == 2) {
865 			html[i++] = "<td><span id='";
866 			html[i++] = this._monthlyMonthFieldExId;
867 			html[i++] = "' class='ZInlineInput'></span>";
868 		}
869 		else {
870 			html[i++] = "<td>";
871 			html[i++] = "<label for='";
872 			html[i++] = this._monthlyFieldRadioId;
873 			html[i++] = "'>";
874 			html[i++] = segment.toSubPattern();
875 			html[i++] = "</label>";
876 		}
877 		html[i++] = "</td>";
878 	}
879 	html[i++] = "</tr></table>";
880 	html[i++] = "</td></tr>";
881 	// end table
882 	html[i++] = "</table>";
883 
884 	return html.join("");
885 };
886 
887 ZmApptRecurDialog.prototype._createRepeatYearly =
888 function(uid) {
889 	this._yearlyDefaultId = "YEALY_DEFAULT_" + uid ; //Dwt.getNextId();
890 	this._yearlyRadioName = "YEARLY_RADIO_" + uid; //Dwt.getNextId();
891 	this._yearlyMonthSelectId = "YEARLY_MONTH_SELECT_" + uid; // Dwt.getNextId();
892 	this._yearlyDayFieldId = "YEARLY_DAY_FIELD_" + uid; // Dwt.getNextId();
893 	this._yearlyDaySelectId = "YEARLY_DAY_SELECT_" + uid; // Dwt.getNextId();
894 	this._yearlyWeekdaySelectId ="YEARLY_WEEKDAY_SELECT_" + uid; //Dwt.getNextId();
895 	this._yearlyMonthSelectExId ="YEARLY_MONTH_SELECT_EX_" + uid; // Dwt.getNextId();
896 	this._yearlyFieldRadioId = "YEARLY_FIELD_RADIO_" + uid;// Dwt.getNextId();
897 
898 	var html = new Array();
899 	var i = 0;
900 
901 	// start table
902 	html[i++] = "<table class='ZRadioButtonTable'>";
903 	// every year on <month> <day>
904 	html[i++] = "<tr><td><input checked value='1' type='radio' name='";
905 	html[i++] = this._yearlyRadioName;
906 	html[i++] = "' id='";
907 	html[i++] = this._yearlyDefaultId;
908 	html[i++] = "'></td><td>";
909 	html[i++] = "<table class='ZPropertySheet' cellspacing='6'><tr>";
910 	var formatter = new AjxMessageFormat(ZmMsg.recurYearlyEveryDate);
911 	var segments = formatter.getSegments();
912 	for (var s = 0; s < segments.length; s++) {
913 		var segment = segments[s];
914 		var index = segment instanceof AjxMessageFormat.MessageSegment
915 				  ? segment.getIndex() : -1;
916 		if (index == 0) {
917 			html[i++] = "<td id='";
918 			html[i++] = this._yearlyMonthSelectId;
919 			html[i++] = "' style='overflow:hidden;'>";
920 		}
921 		else if (index == 1) {
922 			html[i++] = "<td><span id='";
923 			html[i++] = this._yearlyDayFieldId;
924 			html[i++] = "' class='ZInlineInput'></span>";
925 		}
926 		else {
927 			html[i++] = "<td>";
928 			html[i++] = "<label for='";
929 			html[i++] = this._yearlyDefaultId;
930 			html[i++] = "'>";
931 			html[i++] = segment.toSubPattern();
932 			html[i++] = "</label>";
933 		}
934 		html[i++] = "</td>";
935 	}
936 	html[i++] = "</tr></table>";
937 	html[i++] = "</td></tr>";
938 	// every year on <ordinal> <weekday> of <month>
939 	html[i++] = "<tr><td><input value='2' type='radio' name='";
940 	html[i++] = this._yearlyRadioName;
941 	html[i++] = "' id='";
942 	html[i++] = this._yearlyFieldRadioId;
943 	html[i++] = "'></td><td>";
944 	html[i++] = "<table class='ZPropertySheet' cellspacing='6'><tr>";
945 	var formatter = new AjxMessageFormat(ZmMsg.recurYearlyEveryMonthNumDay);
946 	var segments = formatter.getSegments();
947 	for (var s = 0; s < segments.length; s++) {
948 		var segment = segments[s];
949 		var index = segment instanceof AjxMessageFormat.MessageSegment
950 				  ? segment.getIndex() : -1;
951 		if (index == 0) {
952 			html[i++] = "<td id='";
953 			html[i++] = this._yearlyDaySelectId;
954 			html[i++] = "' style='overflow:hidden;'>";
955 		}
956 		else if (index == 1) {
957 			html[i++] = "<td id='";
958 			html[i++] = this._yearlyWeekdaySelectId;
959 			html[i++] = "' style='overflow:hidden;'>";
960 		}
961 		else if (index == 2) {
962 			html[i++] = "<td id='";
963 			html[i++] = this._yearlyMonthSelectExId;
964 			html[i++] = "' style='overflow:hidden;'>";
965 		}
966 		else {
967 			html[i++] = "<td>";
968 			html[i++] = "<label for='";
969 			html[i++] = this._yearlyFieldRadioId;
970 			html[i++] = "'>";
971 			html[i++] = segment.toSubPattern();
972 			html[i++] = "</label>";
973 		}
974 		html[i++] = "</td>";
975 	}
976 	html[i++] = "</tr></table>";
977 	html[i++] = "</td></tr>";
978 	// end table
979 	html[i++] = "</table>";
980 	return html.join("");
981 };
982 
983 ZmApptRecurDialog.prototype._createDwtObjects =
984 function(uid) {
985 	// create all DwtSelect's
986 	this._createSelects();
987 
988 	// create mini calendar button for end by field
989 	var dateButtonListener = new AjxListener(this, this._endByButtonListener);
990 	var dateCalSelectionListener = new AjxListener(this, this._dateCalSelectionListener);
991 	ZmCalendarApp.createMiniCalButton(this, this._endByButtonId, dateButtonListener, dateCalSelectionListener);
992 
993 	// create all DwtInputField's
994 	this._createInputs(uid);
995 };
996 
997 ZmApptRecurDialog.prototype._createSelects = 
998 function() {
999 	this._repeatSelect = new DwtSelect({parent:this});
1000 	this._repeatSelect.addChangeListener(new AjxListener(this, this._repeatChangeListener));
1001 	for (var i = 0; i < ZmApptRecurDialog.REPEAT_OPTIONS.length; i++) {
1002 		var option = ZmApptRecurDialog.REPEAT_OPTIONS[i];
1003 		this._repeatSelect.addOption(option.label, option.selected, option.value);
1004 	}
1005 	this._repeatSelect.reparentHtmlElement(this._repeatSelectId);
1006 	delete this._repeatSelectId;
1007 
1008 	var selectChangeListener = new AjxListener(this, this._selectChangeListener);
1009 	this._weeklySelectButton = new DwtButton({parent:this});//new DwtSelect({parent:this});
1010     var wMenu = new ZmPopupMenu(this._weeklySelectButton);
1011     this._weeklySelectButton.setMenu(wMenu);
1012     //this._weeklySelect.addChangeListener(selectChangeListener);
1013 	var formatter = new AjxMessageFormat(ZmMsg.recurWeeklyEveryWeekday);
1014 	var dayFormatter = formatter.getFormatsByArgumentIndex()[0];
1015 	var day = new Date();
1016 	day.setDate(day.getDate() - day.getDay());
1017     var monwedfri = new Array();
1018     var tuethu = new Array();
1019     var satsun = new Array();
1020     for (var i = 0; i < 7; i++) {
1021 		//this._weeklySelect.addOption(dayFormatter.format(day), false, i);
1022         var mi = new DwtMenuItem({parent:wMenu, style:DwtMenuItem.CHECK_STYLE, radioGroupId:i});
1023         mi.setText(dayFormatter.format(day));
1024         mi.addSelectionListener(selectChangeListener);
1025         mi.setData("index",i);
1026         switch(day.getDay()){
1027             case AjxDateUtil.SUNDAY:
1028             case AjxDateUtil.SATURDAY: satsun.push(dayFormatter.format(day)); break;
1029 
1030             case AjxDateUtil.MONDAY:
1031             case AjxDateUtil.WEDNESDAY:
1032             case AjxDateUtil.FRIDAY: monwedfri.push(dayFormatter.format(day)); break;
1033 
1034             case AjxDateUtil.TUESDAY:
1035             case AjxDateUtil.THURSDAY: tuethu.push(dayFormatter.format(day)); break;
1036         }
1037         day.setDate(day.getDate() + 1);
1038 	}
1039     //Separator
1040     new DwtMenuItem({parent:wMenu, style:DwtMenuItem.SEPARATOR_STYLE, radioGroupId:i++});  //Pos 7 is separator
1041     //Add some custom pattern options too
1042     //this._weeklySelect.addOption(monwedfri.join(", "), false, i++);
1043     var mi = new DwtMenuItem({parent:wMenu, radioGroupId:i});
1044     mi.setText(monwedfri.join(", "));
1045     mi.addSelectionListener(selectChangeListener);
1046     mi.setData("index",i++);
1047     //this._weeklySelect.addOption(tuethu.join(", "), false, i++);
1048     mi = new DwtMenuItem({parent:wMenu, radioGroupId:i});
1049     mi.setText(tuethu.join(", "));
1050     mi.addSelectionListener(selectChangeListener);
1051     mi.setData("index",i++);
1052     //Let's correct the sequence
1053     var satsun1 = [satsun[1],satsun[0]];
1054     //this._weeklySelect.addOption(satsun1.join(", "), false, i++);
1055     mi = new DwtMenuItem({parent:wMenu, radioGroupId:i});
1056     mi.setText(satsun1.join(", "));
1057     mi.addSelectionListener(selectChangeListener);
1058     mi.setData("index",i++);
1059     wMenu.setSelectedItem(new Date().getDay());
1060     this._weeklySelectButton.setText(wMenu.getItem(new Date().getDay()).getText());
1061     
1062     //this._weeklySelect.setv
1063     this._weeklySelectButton.reparentHtmlElement(this._weeklySelectId);
1064 	delete this._weeklySelectId;
1065 
1066 	this._monthlyDaySelect = new DwtSelect({parent:this});
1067 	this._monthlyDaySelect.addChangeListener(selectChangeListener);
1068 	var formatter = new AjxMessageFormat(ZmMsg.recurMonthlyEveryNumMonthsNumDay);
1069 	var ordinalFormatter = formatter.getFormatsByArgumentIndex()[0];
1070 	var limits = ordinalFormatter.getLimits();
1071 	var formats = ordinalFormatter.getFormats();
1072 	for (var i = 0; i < limits.length; i++) {
1073 		var index = (i + 1) % limits.length;
1074 		var label = formats[index].format();
1075 		var value = Math.floor(limits[index]);
1076 		this._monthlyDaySelect.addOption(label, false, value);
1077 	}
1078 	this._monthlyDaySelect.reparentHtmlElement(this._monthlyDaySelectId);
1079 	delete this._monthlyDaySelectId;
1080 
1081 	this._monthlyWeekdaySelect = new DwtSelect({parent:this});
1082 	this._monthlyWeekdaySelect.addChangeListener(selectChangeListener);
1083 	var formatter = new AjxMessageFormat(ZmMsg.recurMonthlyEveryNumMonthsNumDay);
1084 	var dayFormatter = formatter.getFormatsByArgumentIndex()[1];
1085 	var day = new Date();
1086 	day.setDate(day.getDate() - day.getDay());
1087 
1088     this._monthlyWeekdaySelect.addOption(ZmMsg.recurrenceRuleDay, false, ZmRecurrence.RECURRENCE_DAY);
1089     this._monthlyWeekdaySelect.addOption(ZmMsg.recurrenceRuleWeekend, false, ZmRecurrence.RECURRENCE_WEEKEND);
1090     this._monthlyWeekdaySelect.addOption(ZmMsg.recurrenceRuleWeekday, false, ZmRecurrence.RECURRENCE_WEEKDAY);
1091 
1092     for (var i = 0; i < 7; i++) {
1093 		this._monthlyWeekdaySelect.addOption(dayFormatter.format(day), false, i);
1094 		day.setDate(day.getDate() + 1);
1095 	}
1096 	this._monthlyWeekdaySelect.reparentHtmlElement(this._monthlyWeekdaySelectId);
1097 	delete this._monthlyWeekdaySelectId;
1098 
1099 	this._yearlyMonthSelect = new DwtSelect({parent:this});
1100 	this._yearlyMonthSelect.addChangeListener(selectChangeListener);
1101 	var formatter = new AjxMessageFormat(ZmMsg.recurYearlyEveryDate);
1102 	var monthFormatter = formatter.getFormatsByArgumentIndex()[0];
1103 	var month = new Date();
1104 	month.setDate(1);
1105 	for (var i = 0; i < 12; i++) {
1106 		month.setMonth(i);
1107 		this._yearlyMonthSelect.addOption(monthFormatter.format(month), false, i);
1108 	}
1109 	this._yearlyMonthSelect.reparentHtmlElement(this._yearlyMonthSelectId);
1110 	delete this._yearlyMonthSelectId;
1111 
1112 	this._yearlyDaySelect = new DwtSelect({parent:this});
1113 	this._yearlyDaySelect.addChangeListener(selectChangeListener);
1114 	var formatter = new AjxMessageFormat(ZmMsg.recurYearlyEveryMonthNumDay);
1115 	var ordinalFormatter = formatter.getFormatsByArgumentIndex()[0];
1116 	var limits = ordinalFormatter.getLimits();
1117 	var formats = ordinalFormatter.getFormats();
1118 	for (var i = 0; i < limits.length; i++) {
1119 		var index = (i + 1) % limits.length;
1120 		var label = formats[index].format();
1121 		var value = Math.floor(limits[index]);
1122 		this._yearlyDaySelect.addOption(label, false, value);
1123 	}
1124 	this._yearlyDaySelect.reparentHtmlElement(this._yearlyDaySelectId);
1125 	delete this._yearlyDaySelectId;
1126 
1127 	this._yearlyWeekdaySelect = new DwtSelect({parent:this});
1128 	this._yearlyWeekdaySelect.addChangeListener(selectChangeListener);
1129 	var formatter = new AjxMessageFormat(ZmMsg.recurYearlyEveryMonthNumDay);
1130 	var dayFormatter = formatter.getFormatsByArgumentIndex()[1];
1131 	var day = new Date();
1132 	day.setDate(day.getDate() - day.getDay());
1133 
1134     this._yearlyWeekdaySelect.addOption(ZmMsg.recurrenceRuleDay, false, ZmRecurrence.RECURRENCE_DAY);
1135     this._yearlyWeekdaySelect.addOption(ZmMsg.recurrenceRuleWeekend, false, ZmRecurrence.RECURRENCE_WEEKEND);
1136     this._yearlyWeekdaySelect.addOption(ZmMsg.recurrenceRuleWeekday, false, ZmRecurrence.RECURRENCE_WEEKDAY);
1137 
1138 	for (var i = 0; i < 7; i++) {
1139 		this._yearlyWeekdaySelect.addOption(dayFormatter.format(day), false, i);
1140 		day.setDate(day.getDate() + 1);
1141 	}
1142 	this._yearlyWeekdaySelect.reparentHtmlElement(this._yearlyWeekdaySelectId);
1143 	delete this._yearlyWeekdaySelectId;
1144 
1145 	this._yearlyMonthSelectEx = new DwtSelect({parent:this});
1146 	this._yearlyMonthSelectEx.addChangeListener(selectChangeListener);
1147 	for (var i = 0; i < AjxDateUtil.MONTH_LONG.length; i++)
1148 		this._yearlyMonthSelectEx.addOption(AjxDateUtil.MONTH_LONG[i], false, i);
1149 	this._yearlyMonthSelectEx.reparentHtmlElement(this._yearlyMonthSelectExId);
1150 	delete this._yearlyMonthSelectExId;
1151 };
1152 
1153 ZmApptRecurDialog.prototype._createInputs = 
1154 function(uid) {
1155 	// create inputs for end fields
1156 	this._endIntervalField = new DwtInputField({parent: this, type: DwtInputField.INTEGER,
1157 												initialValue: "1", size: 3, maxLen: 3,
1158 												errorIconStyle: DwtInputField.ERROR_ICON_NONE, 
1159 												validationStyle: DwtInputField.ONEXIT_VALIDATION, 
1160 												validator: this._positiveIntValidator, 
1161 												validatorCtxtObj: this, inputId:"RECUR_END_INTERVAL_FIELD_" + uid});
1162 	this._endIntervalField.setDisplay(Dwt.DISPLAY_INLINE);
1163 	this._endIntervalField.reparentHtmlElement(this._endIntervalFieldId);
1164 	delete this._endIntervalFieldId;
1165 
1166 	this._endByField = new DwtInputField({parent: this, type: DwtInputField.DATE,
1167 										  size: 10, maxLen: 10,
1168 										  errorIconStyle: DwtInputField.ERROR_ICON_NONE,
1169 										  validationStyle: DwtInputField.ONEXIT_VALIDATION,
1170 										  validator: this._endByDateValidator, 
1171 										  validatorCtxtObj: this, inputId:"RECUR_END_BY_FIELD_" + uid});
1172 	this._endByField.setDisplay(Dwt.DISPLAY_INLINE);
1173 	this._endByField.reparentHtmlElement(this._endByFieldId);
1174 	Dwt.setSize(this._endByField.getInputElement(), Dwt.DEFAULT, "22");
1175 	delete this._endByFieldId;
1176 
1177 	// create inputs for day fields
1178 	this._dailyField = new DwtInputField({parent: this, type: DwtInputField.INTEGER,
1179 										  initialValue: "2", size: 3, maxLen: 2,
1180 										  errorIconStyle: DwtInputField.ERROR_ICON_NONE,
1181 										  validationStyle: DwtInputField.ONEXIT_VALIDATION,
1182 										  validator: this._positiveIntValidator,
1183 										  validatorCtxtObj: this, inputId: "RECUR_DAILY_FIELD_" + uid});
1184 	this._dailyField.setDisplay(Dwt.DISPLAY_INLINE);
1185 	this._dailyField.reparentHtmlElement(this._dailyFieldId);
1186 	delete this._dailyFieldId;
1187 
1188 	// create inputs for week fields
1189 	this._weeklyField = new DwtInputField({parent: this, type: DwtInputField.INTEGER,
1190 										   initialValue: "1", size: 2, maxLen: 2,
1191 										   errorIconStyle: DwtInputField.ERROR_ICON_NONE,
1192 										   validationStyle: DwtInputField.ONEXIT_VALIDATION,
1193 										   validator: this._weeklyValidator,
1194 										   validatorCtxtObj: this, inputId:"RECUR_WEEKLY_FIELD_" + uid});
1195 	this._weeklyField.setDisplay(Dwt.DISPLAY_INLINE);
1196 	this._weeklyField.reparentHtmlElement(this._weeklyFieldId);
1197 	delete this._weeklyFieldId;
1198 
1199 	// create inputs for month fields
1200 	this._monthlyDayField = new DwtInputField({parent: this, type: DwtInputField.INTEGER,
1201 											   initialValue: "1", size: 2, maxLen: 2,
1202 											   errorIconStyle: DwtInputField.ERROR_ICON_NONE,
1203 											   validationStyle: DwtInputField.ONEXIT_VALIDATION,
1204 											   validatorCtxtObj: this, inputId:"RECUR_MONTHLY_DAY_FIELD_" + uid});
1205 	this._monthlyDayField.setDisplay(Dwt.DISPLAY_INLINE);
1206 	this._monthlyDayField.reparentHtmlElement(this._monthlyDayFieldId);
1207 	this._monthlyDayField.setValidNumberRange(1, 31);
1208 	delete this._monthlyDayFieldId;
1209 
1210 	this._monthlyMonthField = new DwtInputField({parent: this, type: DwtInputField.INTEGER,
1211 											   initialValue: "1", size: 2, maxLen: 2,
1212 											   errorIconStyle: DwtInputField.ERROR_ICON_NONE,
1213 											   validationStyle: DwtInputField.ONEXIT_VALIDATION,
1214 											   validator: this._positiveIntValidator,
1215 											   validatorCtxtObj: this, inputId:"RECUR_MONTHLY_MONTH_FIELD_" + uid});
1216 	this._monthlyMonthField.setDisplay(Dwt.DISPLAY_INLINE);
1217 	this._monthlyMonthField.reparentHtmlElement(this._monthlyMonthFieldId);
1218 	delete this._monthlyMonthFieldId;
1219 
1220 	this._monthlyMonthFieldEx = new DwtInputField({parent: this, type: DwtInputField.INTEGER,
1221 												   initialValue: "1", size: 2, maxLen: 2,
1222 												   errorIconStyle: DwtInputField.ERROR_ICON_NONE,
1223 												   validationStyle: DwtInputField.ONEXIT_VALIDATION,
1224 												   validator: this._positiveIntValidator,
1225 												   validatorCtxtObj: this, inputId:"RECUR_MONTHLY_MONTH_FIELD_EX_" + uid});
1226 	this._monthlyMonthFieldEx.setDisplay(Dwt.DISPLAY_INLINE);
1227 	this._monthlyMonthFieldEx.reparentHtmlElement(this._monthlyMonthFieldExId);
1228 	delete this._monthlyMonthFieldExId;
1229 
1230 	// create inputs for year fields
1231 	this._yearlyDayField = new DwtInputField({parent: this, type: DwtInputField.INTEGER,
1232 											  initialValue: "1", size: 2, maxLen: 2,
1233 											  errorIconStyle: DwtInputField.ERROR_ICON_NONE,
1234 											  validationStyle: DwtInputField.ONEXIT_VALIDATION,
1235 											  validator: this._yearlyDayValidator,
1236 											  validatorCtxtObj: this, inputId:"RECUR_YEARLY_DAY_FIELD_" + uid});
1237 	this._yearlyDayField.setDisplay(Dwt.DISPLAY_INLINE);
1238 	this._yearlyDayField.reparentHtmlElement(this._yearlyDayFieldId);
1239 	delete this._yearlyDayFieldId;
1240 };
1241 
1242 ZmApptRecurDialog.prototype._cacheFields = 
1243 function() {
1244 	this._noEndDateRadio = document.getElementById(this._noEndDateRadioId);			delete this._noEndDateRadioId;
1245 	this._endByRadio = document.getElementById(this._endByRadioId); 				delete this._endByRadioId;
1246 	this._endAfterRadio = document.getElementById(this._endAfterRadioId); 			delete this._endAfterRadioId;
1247 	this._repeatSectionDiv = document.getElementById(this._repeatSectionId); 		delete this._repeatSectionId;
1248 	this._repeatEndDiv = document.getElementById(this._repeatEndDivId);				delete this._repeatEndDivId;
1249 	this._repeatDailyDiv = document.getElementById(this._repeatDailyId); 			delete this._repeatDailyId;
1250 	this._repeatWeeklyDiv = document.getElementById(this._repeatWeeklyId); 			delete this._repeatWeeklyId;
1251 	this._repeatMonthlyDiv = document.getElementById(this._repeatMonthlyId); 		delete this._repeatMonthlyId;
1252 	this._repeatYearlyDiv = document.getElementById(this._repeatYearlyId); 			delete this._repeatYearlyId;
1253 	this._dailyDefaultRadio = document.getElementById(this._dailyDefaultId); 		delete this._dailyDefaultId;
1254 	this._dailyWeekdayRadio = document.getElementById(this._dailyWeekdayId);		delete this._dailyWeekdayId;
1255 	this._dailyFieldRadio = document.getElementById(this._dailyFieldRadioId); 		delete this._dailyFieldRadioId;
1256 	this._weeklyDefaultRadio = document.getElementById(this._weeklyDefaultId); 		delete this._weeklyDefaultId;
1257 	this._weeklyFieldRadio = document.getElementById(this._weeklyFieldRadioId);		delete this._weeklyFieldRadioId;
1258 	this._weeklyCheckboxes = document.getElementsByName(this._weeklyCheckboxName);
1259 	this._monthlyDefaultRadio = document.getElementById(this._monthlyDefaultId); 	delete this._monthlyDefaultId;
1260 	this._monthlyFieldRadio = document.getElementById(this._monthlyFieldRadioId); 	delete this._monthlyFieldRadioId;
1261 	this._yearlyDefaultRadio = document.getElementById(this._yearlyDefaultId); 		delete this._yearlyDefaultId;
1262 	this._yearlyFieldRadio = document.getElementById(this._yearlyFieldRadioId); 	delete this._yearlyFieldRadioId;
1263 };
1264 
1265 ZmApptRecurDialog.prototype._addEventHandlers = 
1266 function() {
1267 	var ardId = AjxCore.assignId(this);
1268 
1269 	// add event listeners where necessary
1270 	this._setFocusHandler(this._endIntervalField, ardId);
1271 	this._setFocusHandler(this._endByField, ardId);
1272 	this._setFocusHandler(this._dailyField, ardId);
1273 	this._setFocusHandler(this._weeklyField, ardId);
1274 	this._setFocusHandler(this._monthlyDayField, ardId);
1275 	this._setFocusHandler(this._monthlyMonthField, ardId);
1276 	this._setFocusHandler(this._monthlyMonthFieldEx, ardId);
1277 	this._setFocusHandler(this._yearlyDayField, ardId);
1278 
1279 	var cboxCount = this._weeklyCheckboxes.length;
1280 	for (var i = 0; i < cboxCount; i++) {
1281 		var checkbox = this._weeklyCheckboxes[i]; 
1282 		Dwt.setHandler(checkbox, DwtEvent.ONFOCUS, ZmApptRecurDialog._onCheckboxFocus);
1283 		checkbox._recurDialogId = ardId;
1284 	}
1285 };
1286 
1287 ZmApptRecurDialog.prototype._createTabGroup = function() {
1288 	var allId		= this._htmlElId;
1289 	var repeatId	= allId+"_repeat";
1290 	var endId		= allId+"_end";
1291 	var controlsId	= allId+"_controls";
1292 
1293 	// section tab groups
1294 	this._sectionTabGroups = {};
1295 	for (var i = 0; i < ZmApptRecurDialog.REPEAT_OPTIONS.length; i++) {
1296 		var type = ZmApptRecurDialog.REPEAT_OPTIONS[i].value;
1297 		this._sectionTabGroups[type] = new DwtTabGroup(repeatId+"_"+type);
1298 	}
1299 
1300 	// section: daily
1301 	var daily = this._sectionTabGroups[ZmRecurrence.DAILY];
1302 	daily.addMember(this._dailyDefaultRadio); // radio: every day
1303 	daily.addMember(this._dailyWeekdayRadio); // radio: every weekday
1304 	daily.addMember(this._dailyFieldRadio); // radio: every {# days}
1305 	daily.addMember(this._dailyField); // input: # days
1306 	// section: weekly
1307 	var weekly = this._sectionTabGroups[ZmRecurrence.WEEKLY];
1308 	weekly.addMember(this._weeklyDefaultRadio); // radio: every {day}
1309 	weekly.addMember(this._weeklySelectButton); // select: day
1310 	weekly.addMember(this._weeklyFieldRadio); // radio: every {# weeks} on {days}
1311 	var checkboxes = new Array(this._weeklyCheckboxes.length);
1312 	for (var i = 0; i < checkboxes.length; i++) {
1313 		checkboxes[i] = this._weeklyCheckboxes[i];
1314 	}
1315 	this.__addTabMembers(
1316 		weekly, ZmMsg.recurWeeklyEveryNumWeeksDate,
1317 		this._weeklyField, // input: # weeks
1318 		checkboxes // checkboxes: weekdays
1319 	);
1320 
1321 	// section: monthly
1322 	var monthly = this._sectionTabGroups[ZmRecurrence.MONTHLY];
1323 	monthly.addMember(this._monthlyDefaultRadio); // radio: day {date} of every {# months}
1324 	this.__addTabMembers(
1325 		monthly, ZmMsg.recurMonthlyEveryNumMonthsDate,
1326 		this._monthlyDayField, // input: date
1327 		this._monthlyMonthField // input: # months
1328 	);
1329 	monthly.addMember(this._monthlyFieldRadio); // radio: {ordinal} {weekday} of every {# months}
1330 	this.__addTabMembers(
1331 		monthly, ZmMsg.recurMonthlyEveryNumMonthsNumDay,
1332 		this._monthlyDaySelect, // select: ordinal
1333 		this._monthlyWeekdaySelect, // select: weekday
1334 		this._monthlyMonthFieldEx // input: # months
1335 	);
1336 
1337 	// section: yearly
1338 	var yearly = this._sectionTabGroups[ZmRecurrence.YEARLY];
1339 	yearly.addMember(this._yearlyDefaultRadio); // radio: every year on {month} {date}
1340 	this.__addTabMembers(
1341 		yearly, ZmMsg.recurYearlyEveryDate,
1342 		this._yearlyMonthSelect, // select: month
1343 		this._yearlyDayField // input: date
1344 	);
1345 	yearly.addMember(this._yearlyFieldRadio); // radio: {ordinal} {weekday} of every {month}
1346 	this.__addTabMembers(
1347 		yearly, ZmMsg.recurYearlyEveryMonthNumDay,
1348 		this._yearlyDaySelect, // select: ordinal
1349 		this._yearlyWeekdaySelect, // select: weekday
1350 		this._yearlyMonthSelectEx // select: month
1351 	);
1352 
1353 	// misc. tab groups
1354 	this._repeatTabGroup = new DwtTabGroup(repeatId);
1355 	this._endTabGroup = new DwtTabGroup(endId);
1356 	this._endTabGroup.addMember(this._noEndDateRadio); // radio: none
1357 	this._endTabGroup.addMember(this._endAfterRadio); // radio: after {# occurrences}
1358 	this._endTabGroup.addMember(this._endIntervalField); // input: # occurrences
1359 	this._endTabGroup.addMember(this._endByRadio); // radio: end by {date}
1360 	this._endTabGroup.addMember(this._endByField); // input: date
1361 	this._endTabGroup.addMember(); // button: date picker
1362 
1363 	this._controlsTabGroup = new DwtTabGroup(controlsId);
1364 
1365 	// primary tab group
1366 	this._tabGroup = new DwtTabGroup(allId);
1367 	this._tabGroup.addMember(this._repeatSelect);
1368 	this._tabGroup.addMember(this._controlsTabGroup);
1369 	this._tabGroup.addMember(this.getButton(DwtDialog.OK_BUTTON));
1370 	this._tabGroup.addMember(this.getButton(DwtDialog.CANCEL_BUTTON));
1371 };
1372 
1373 ZmApptRecurDialog.prototype.__addTabMembers =
1374 function(tabGroup, pattern, member1 /*, ..., memberN */) {
1375 	var segments = new AjxMessageFormat(pattern).getSegments();
1376 	for (var s = 0; s < segments.length; s++) {
1377 		var segment = segments[s];
1378 		var index = segment instanceof AjxMessageFormat.MessageSegment
1379 				  ? segment.getIndex() : -1;
1380 		if (index != -1) {
1381 			var member = arguments[2 + index];
1382 			if (member instanceof Array) {
1383 				for (var i = 0; i < member.length; i++) {
1384 					tabGroup.addMember(member[i]);
1385 				}
1386 			}
1387 			else {
1388 				tabGroup.addMember(member);
1389 			}
1390 		}
1391 	}
1392 };
1393 
1394 ZmApptRecurDialog.prototype._setFocusHandler = 
1395 function(dwtObj, ardId) {
1396 	var inputEl = dwtObj.getInputElement();
1397 	Dwt.setHandler(inputEl, DwtEvent.ONFOCUS, ZmApptRecurDialog._onFocus);
1398 	inputEl._recurDialogId = ardId;
1399 }
1400 
1401 ZmApptRecurDialog.prototype._setRepeatSection = 
1402 function(repeatType) {
1403 	var isNone = repeatType == ZmRecurrence.NONE;
1404 
1405     Dwt.setVisible(this._repeatSectionDiv, !isNone);
1406     Dwt.setVisible(this._repeatEndDiv, !isNone);
1407 
1408 	var newSection = null;
1409 	switch (repeatType) {
1410 		case ZmRecurrence.DAILY:	newSection = this._repeatDailyDiv; break;
1411 		case ZmRecurrence.WEEKLY:	newSection = this._repeatWeeklyDiv; break;
1412 		case ZmRecurrence.MONTHLY:	newSection = this._repeatMonthlyDiv; break;
1413 		case ZmRecurrence.YEARLY:	newSection = this._repeatYearlyDiv; break;
1414 	}
1415 
1416 	this._controlsTabGroup.removeAllMembers();
1417 	if (newSection) {
1418 		if (this._currentSection) {
1419 			Dwt.setVisible(this._currentSection, false);
1420 		}
1421         Dwt.setVisible(newSection, true);
1422         this._currentSection = newSection;
1423 
1424         this._repeatTabGroup.removeAllMembers();
1425         this._repeatTabGroup.addMember(this._sectionTabGroups[repeatType]);
1426 
1427         this._controlsTabGroup.addMember(this._repeatTabGroup);
1428         this._controlsTabGroup.addMember(this._endTabGroup);
1429 
1430         this.resizeSelect(repeatType);
1431 	}
1432 };
1433 
1434 ZmApptRecurDialog.prototype.resizeSelect =
1435 function(repeatType) {
1436     if(repeatType = ZmRecurrence.MONTHLY) {
1437         this._resizeSelect(this._monthlyDaySelect);
1438         this._resizeSelect(this._monthlyWeekdaySelect);
1439     }
1440 
1441     if(repeatType = ZmRecurrence.YEARLY) {
1442         this._resizeSelect(this._yearlyMonthSelect);
1443         this._resizeSelect(this._yearlyDaySelect);
1444         this._resizeSelect(this._yearlyWeekdaySelect);
1445         this._resizeSelect(this._yearlyMonthSelectEx);
1446     }
1447 };
1448 
1449 ZmApptRecurDialog.prototype._resizeSelect =
1450 function(selectObj) {
1451     if(!selectObj) return;
1452     selectObj.autoResize();
1453 };
1454 
1455 
1456 ZmApptRecurDialog.prototype._cleanup =
1457 function() {
1458 	// dont bother cleaning up if user is still mucking around
1459 	if (this._saveState) return;
1460 
1461 	// TODO: 
1462 	// - dont cleanup for section that was picked if user clicks OK
1463 	
1464 	// reset end section
1465 	this._noEndDateRadio.checked = true;
1466 	this._endIntervalField.setValue("1");
1467 	// reset daily section
1468 	this._dailyDefaultRadio.checked = true;
1469 	this._dailyField.setValue("2");
1470 	// reset weekly section
1471 	this._weeklyDefaultRadio.checked = true;
1472 	this._weeklyField.setValue("2");
1473 	for (var i = 0; i < this._weeklyCheckboxes.length; i++)
1474 		this._weeklyCheckboxes[i].checked = false;
1475 	// reset monthly section
1476 	this._monthlyDefaultRadio.checked = true;
1477 	this._monthlyMonthField.setValue("1");
1478 	this._monthlyMonthFieldEx.setValue("1");
1479 	this._monthlyDaySelect.setSelected(0);
1480 	// reset yearly section
1481 	this._yearlyDefaultRadio.checked = true;
1482 	this._yearlyDaySelect.setSelected(0);
1483 };
1484 
1485 ZmApptRecurDialog.prototype._getRadioOptionValue = 
1486 function(radioName) {	
1487 	var options = document.getElementsByName(radioName);
1488 	if (options) {
1489 		for (var i = 0; i < options.length; i++) {
1490 			if (options[i].checked)
1491 				return options[i].value;
1492 		}
1493 	}
1494 	return null;
1495 };
1496 
1497 // depending on the repeat type, populates repeat section as necessary
1498 ZmApptRecurDialog.prototype._populateForEdit = 
1499 function(appt) {
1500     var recur = appt._recurrence;
1501 	if (recur.repeatType == ZmRecurrence.NONE) return;
1502 
1503 	if (recur.repeatType == ZmRecurrence.DAILY) {
1504 		var dailyRadioOptions = document.getElementsByName(this._dailyRadioName);
1505 		if (recur.repeatWeekday) {
1506 			dailyRadioOptions[1].checked = true;
1507 		} else if (recur.repeatCustomCount > 1) {
1508 			this._dailyField.setValue(recur.repeatCustomCount);
1509 			dailyRadioOptions[2].checked = true;
1510 		}
1511 	} else if (recur.repeatType == ZmRecurrence.WEEKLY) {
1512 		var weeklyRadioOptions = document.getElementsByName(this._weeklyRadioName);
1513 		if (recur.repeatCustomCount == 1 && recur.repeatWeeklyDays.length == 1) {
1514 			weeklyRadioOptions[0].checked = true;
1515             //Do not check the custom checkboxes if every weekday option is selected
1516             this._weeklyCheckboxes[this._startDate.getDay()].checked = false;
1517 			for (var j = 0; j < ZmCalItem.SERVER_WEEK_DAYS.length; j++) {
1518 				if (recur.repeatWeeklyDays[0] == ZmCalItem.SERVER_WEEK_DAYS[j]) {
1519 					this._weeklySelectButton._selected = j;
1520                     this._weeklySelectButton.setDisplayState(DwtControl.SELECTED);
1521 
1522                     var formatter = new AjxMessageFormat(ZmMsg.recurWeeklyEveryWeekday);
1523                     var dayFormatter = formatter.getFormatsByArgumentIndex()[0];
1524                     this._weeklySelectButton.setText(dayFormatter.format(this._startDate));
1525 
1526                     break;
1527 				}
1528 			}
1529 		} else {
1530 			weeklyRadioOptions[1].checked = true;
1531 			this._weeklyField.setValue(recur.repeatCustomCount);
1532 			// xxx: minor hack-- uncheck this since we init'd it earlier
1533 			// Check if we have repeatWeeklyDays set
1534 			if (recur.repeatWeeklyDays.length) {
1535                 this._weeklyCheckboxes[this._startDate.getDay()].checked = false;
1536             }
1537 			for (var i = 0; i < recur.repeatWeeklyDays.length; i++) {
1538 				for (var j = 0; j < ZmCalItem.SERVER_WEEK_DAYS.length; j++) {
1539 					if (recur.repeatWeeklyDays[i] == ZmCalItem.SERVER_WEEK_DAYS[j]) {
1540 						this._weeklyCheckboxes[j].checked = true;
1541 						break;
1542 					}
1543 				}
1544 			}
1545 		}
1546 	} else if (recur.repeatType == ZmRecurrence.MONTHLY) {
1547 		var monthlyRadioOptions = document.getElementsByName(this._monthlyRadioName);
1548 		if (recur.repeatCustomType == "S") {
1549 			monthlyRadioOptions[0].checked = true;
1550 			this._monthlyDayField.setValue(recur.repeatMonthlyDayList[0]);
1551 			this._monthlyMonthField.setValue(recur.repeatCustomCount);
1552 		} else {
1553 			monthlyRadioOptions[1].checked = true;
1554 			this._monthlyDaySelect.setSelectedValue(recur.repeatBySetPos);
1555 
1556             if(recur.repeatCustomDays) {
1557                 var monthlyDay = this.getRecurrenceWeekDaySelection(recur.repeatCustomDays);
1558                 this._monthlyWeekdaySelect.setSelectedValue(monthlyDay);
1559             }
1560 			this._monthlyMonthFieldEx.setValue(recur.repeatCustomCount);
1561 		}
1562 	} else if (recur.repeatType == ZmRecurrence.YEARLY) {
1563 		var yearlyRadioOptions = document.getElementsByName(this._yearlyRadioName);
1564 		if (recur.repeatCustomType == "S") {
1565 			yearlyRadioOptions[0].checked = true;
1566 			this._yearlyDayField.setValue(recur.repeatCustomMonthDay);
1567 			this._yearlyMonthSelect.setSelectedValue(Number(recur.repeatYearlyMonthsList)-1);
1568 		} else {
1569 			yearlyRadioOptions[1].checked = true;
1570 			this._yearlyDaySelect.setSelectedValue(recur.repeatBySetPos);
1571 
1572             if(recur.repeatCustomDays) {
1573                 var weekDayVal = this.getRecurrenceWeekDaySelection(recur.repeatCustomDays);
1574                 this._yearlyWeekdaySelect.setSelectedValue(weekDayVal);
1575             }
1576 			this._yearlyMonthSelectEx.setSelectedValue(Number(recur.repeatYearlyMonthsList)-1);
1577 		}
1578 	}
1579 
1580 	// populate recurrence ending rules
1581 	if (recur.repeatEndType != "N") {
1582 		var endRadioOptions = document.getElementsByName(this._repeatEndName);
1583 		if (recur.repeatEndType == "A") {
1584 			endRadioOptions[1].checked = true;
1585 			this._endIntervalField.setValue(recur.repeatEndCount);
1586 		} else {
1587 			endRadioOptions[2].checked = true;
1588 			this._endByField.setValue(AjxDateUtil.simpleComputeDateStr(recur.repeatEndDate));
1589 		}
1590 	}
1591 };
1592 
1593 /**
1594  * Gets the week day selection.
1595  * 
1596  * @param	{String}	repeatCustomDays		the repeat custom days
1597  * 
1598  * @return	{constant}	the week day selection (see <code>ZmRecurrence.RECURRENCE_</code> constants
1599  * @see	ZmRecurrence
1600  */
1601 ZmApptRecurDialog.prototype.getRecurrenceWeekDaySelection =
1602 function(repeatCustomDays) {
1603 
1604     if(repeatCustomDays instanceof Array) {
1605         repeatCustomDays = repeatCustomDays.join(",");
1606     }
1607 
1608     if(repeatCustomDays == ZmCalItem.SERVER_WEEK_DAYS.join(",")) {
1609         return ZmRecurrence.RECURRENCE_DAY;
1610     }
1611 
1612     var weekDays = ZmCalItem.SERVER_WEEK_DAYS.slice(1,6);
1613     if(repeatCustomDays == weekDays.join(",")) {
1614         return ZmRecurrence.RECURRENCE_WEEKDAY;
1615     }
1616 
1617     var weekEndDays = [ZmCalItem.SERVER_WEEK_DAYS[0], ZmCalItem.SERVER_WEEK_DAYS[6]];
1618     if(repeatCustomDays == weekEndDays.join(",")) {
1619         return ZmRecurrence.RECURRENCE_WEEKEND;
1620     }
1621 
1622 
1623     for (var i = 0; i < ZmCalItem.SERVER_WEEK_DAYS.length; i++) {
1624         if (ZmCalItem.SERVER_WEEK_DAYS[i] == repeatCustomDays) {
1625             return i;
1626             break;
1627         }
1628     }
1629 
1630 };
1631 
1632 ZmApptRecurDialog.prototype.getWeekdaySelectValue =
1633 function(weekdaySelect) {
1634 
1635     var day = weekdaySelect.getValue();
1636 
1637     if(ZmCalItem.SERVER_WEEK_DAYS[day]) {
1638         return [ZmCalItem.SERVER_WEEK_DAYS[day]];        
1639     }
1640 
1641     if(day == ZmRecurrence.RECURRENCE_DAY) {
1642         return ZmCalItem.SERVER_WEEK_DAYS;
1643     }else if(day == ZmRecurrence.RECURRENCE_WEEKDAY) {
1644         return ZmCalItem.SERVER_WEEK_DAYS.slice(1,6);
1645     }else if(day == ZmRecurrence.RECURRENCE_WEEKEND) {
1646         return [ZmCalItem.SERVER_WEEK_DAYS[0], ZmCalItem.SERVER_WEEK_DAYS[6]];
1647     }
1648 
1649 };
1650 
1651 ZmApptRecurDialog.prototype.getFirstWeekDayOffset =
1652 function(weekDaySelect) {
1653     var weekDayVal = weekDaySelect.getValue();
1654     var dayVal = 0;
1655     if(ZmCalItem.SERVER_WEEK_DAYS[weekDayVal]) {
1656        dayVal = weekDayVal;
1657     }else if(weekDayVal == ZmRecurrence.RECURRENCE_DAY || weekDayVal == ZmRecurrence.RECURRENCE_WEEKEND) {
1658         //if the selection is just the day or weekend than first day (Sunday) is selected
1659         dayVal = 0;
1660     }else if(weekDayVal == ZmRecurrence.RECURRENCE_WEEKDAY) {
1661         dayVal = 1;
1662     }
1663     return dayVal;
1664 };
1665 
1666 ZmApptRecurDialog.prototype.getPossibleStartDate =
1667 function(weekDayVal, lastDate, repeatBySetPos) {
1668     //weekday select might contain normal weekdays, day, weekend values also
1669     if(ZmCalItem.SERVER_WEEK_DAYS[weekDayVal]) {
1670         return AjxDateUtil.getDateForThisDay(lastDate, weekDayVal, repeatBySetPos); //Last day of next month/year
1671     }else if(weekDayVal == ZmRecurrence.RECURRENCE_DAY) {
1672         var dayOffset = ((repeatBySetPos==-1)? 0 : (repeatBySetPos-1));
1673         lastDate.setDate(lastDate.getDate() + dayOffset);
1674         return lastDate;
1675     }else if(weekDayVal == ZmRecurrence.RECURRENCE_WEEKDAY) {
1676         return AjxDateUtil.getDateForThisWorkWeekDay(lastDate, repeatBySetPos);
1677     }else if(weekDayVal == ZmRecurrence.RECURRENCE_WEEKEND) {
1678         var lastSunday = AjxDateUtil.getDateForThisDay(lastDate, AjxDateUtil.SUNDAY, repeatBySetPos);
1679         var lastSaturday = AjxDateUtil.getDateForThisDay(lastDate, AjxDateUtil.SATURDAY, repeatBySetPos);
1680         //nearest possible weekend
1681         if(repeatBySetPos < 0) {
1682             return (lastSaturday.getTime() > lastSunday.getTime()) ? lastSaturday : lastSunday;
1683         }else {
1684             return (lastSaturday.getTime() > lastSunday.getTime()) ? lastSunday : lastSaturday;                        
1685         }
1686     }
1687 }
1688 // Listeners
1689 
1690 ZmApptRecurDialog.prototype._repeatChangeListener =
1691 function(ev) {
1692 	var newValue = ev._args.newValue;
1693 	this._setRepeatSection(newValue);
1694 };
1695 
1696 ZmApptRecurDialog.prototype._selectChangeListener = 
1697 function(ev) {
1698     if(ev.item && ev.item instanceof DwtMenuItem){
1699        this._weeklyDefaultRadio.checked = true;
1700        this._weeklySelectButton.setText(ev.item.getText());
1701        this._weeklySelectButton._selected = ev.item.getData("index");
1702        this._weeklySelectButton.setDisplayState(DwtControl.SELECTED);
1703        return;
1704     }
1705     switch (ev._args.selectObj) {
1706 		case this._weeklySelectButton:			this._weeklyDefaultRadio.checked = true; break;
1707 		case this._monthlyDaySelect:
1708 		case this._monthlyWeekdaySelect:	this._monthlyFieldRadio.checked = true; break;
1709 		case this._yearlyMonthSelect:
1710 			this._yearlyDefaultRadio.checked = true;
1711 			this._yearlyDayField.validate();
1712 			break;
1713 		case this._yearlyDaySelect:
1714 		case this._yearlyWeekdaySelect:
1715 		case this._yearlyMonthSelectEx: 	this._yearlyFieldRadio.checked = true; break;
1716 	}
1717 };
1718 
1719 ZmApptRecurDialog.prototype._endByButtonListener = 
1720 function(ev) {
1721 	var menu = ev.item.getMenu();
1722 	var cal = menu.getItem(0);
1723 	var initDate = this._endByField.isValid()
1724 		? new Date(AjxDateUtil.simpleParseDateStr(this._endByField.getValue()))
1725 		: new Date();
1726 	cal.setDate(initDate, true);
1727 	ev.item.popup();
1728 };
1729 
1730 ZmApptRecurDialog.prototype._dateCalSelectionListener = 
1731 function(ev) {
1732 	this._endByField.setValue(AjxDateUtil.simpleComputeDateStr(ev.detail));
1733 	this._endByRadio.checked = true;
1734 };
1735 
1736 ZmApptRecurDialog.prototype._okListener = 
1737 function() {
1738 	this._saveState = true;
1739 	this._isDirty = true;
1740 };
1741 
1742 ZmApptRecurDialog.prototype._cancelListener = 
1743 function() {
1744 	this._cleanup();
1745 };
1746 
1747 
1748 // Callbacks
1749 
1750 ZmApptRecurDialog.prototype._positiveIntValidator =
1751 function(value) {
1752 	DwtInputField.validateInteger(value);
1753 	if (parseInt(value) < 1) {
1754 		throw ZmMsg.errorLessThanOne;
1755 	}
1756 	return value;
1757 };
1758 
1759 ZmApptRecurDialog.prototype._yearlyDayValidator =
1760 function(value) {
1761 	DwtInputField.validateInteger(value);
1762 	var dpm = AjxDateUtil._daysPerMonth[this._yearlyMonthSelect.getValue()];
1763 	if (value < 1)
1764 		throw AjxMessageFormat.format(AjxMsg.numberLessThanMin, 1);
1765 	if (value > dpm) {
1766 		throw AjxMessageFormat.format(AjxMsg.numberMoreThanMax, dpm);
1767 	}
1768 	return value;
1769 };
1770 
1771 ZmApptRecurDialog.prototype._endByDateValidator =
1772 function(value) {
1773 	DwtInputField.validateDate(value);
1774 	var endByDate = AjxDateUtil.simpleParseDateStr(value);
1775 	if (endByDate == null || endByDate.valueOf() < this._startDate.valueOf()) {
1776 		throw ZmMsg.errorEndByDate;
1777 	}
1778 	return value;
1779 };
1780 
1781 ZmApptRecurDialog.prototype._weeklyValidator =
1782 function(value) {
1783 	value = this._positiveIntValidator(value);
1784 	// make sure at least one day of the week is selected
1785 	var checked = false;
1786 	for (var i=0; i<this._weeklyCheckboxes.length; i++) {
1787 		if (this._weeklyCheckboxes[i].checked) {
1788 			checked = true;
1789 			break;
1790 		}
1791 	}
1792 	if (!checked) {
1793 		throw ZmMsg.errorNoWeekdayChecked;
1794 	}
1795 	return value;
1796 };
1797 
1798 
1799 // Static methods
1800 
1801 ZmApptRecurDialog._onCheckboxFocus = function(ev) {
1802 	var el = DwtUiEvent.getTarget(ev);
1803 	var ard = AjxCore.objectWithId(el._recurDialogId);
1804 	ard._weeklyFieldRadio.checked = true;
1805 };
1806 
1807 ZmApptRecurDialog._onFocus =
1808 function(ev) {
1809 	ev || (ev = window.event);
1810 
1811 	var el = DwtUiEvent.getTarget(ev);
1812 	var ard = AjxCore.objectWithId(el._recurDialogId);
1813 	var dwtObj = DwtControl.findControl(el);
1814 	switch (dwtObj) {
1815 		case ard._endIntervalField: 	ard._endAfterRadio.checked = true; break;
1816 		case ard._endByField: 			ard._endByRadio.checked = true; break;
1817 		case ard._dailyField: 			ard._dailyFieldRadio.checked = true; break;
1818 		case ard._weeklyField: 			ard._weeklyFieldRadio.checked = true; break;
1819 		case ard._monthlyMonthField:
1820 		case ard._monthlyDayField: 		ard._monthlyDefaultRadio.checked = true; break;
1821 		case ard._monthlyMonthFieldEx: 	ard._monthlyFieldRadio.checked = true; break;
1822 		case ard._yearlyDayField: 		ard._yearlyDefaultRadio.checked = true; break;
1823 	}
1824 
1825 	appCtxt.getKeyboardMgr().grabFocus(dwtObj);
1826 };
1827