1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 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) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * @overview
 26  * This file contains the edit task view classes.
 27  */
 28 
 29 /**
 30  * Creates the edit task view.
 31  * @class
 32  * This class represents the edit task view.
 33  * 
 34  * @param	{DwtComposite}	parent		the parent
 35  * @param	{ZmTaskController}		controller		the controller
 36  * 
 37  * @extends		ZmCalItemEditView
 38  */
 39 ZmTaskEditView = function(parent, controller) {
 40 
 41     this._view = controller.getCurrentViewId();
 42 	this._sessionId = controller.getSessionId();
 43 
 44 	var idParams = {
 45 		skinComponent:  ZmId.SKIN_APP_MAIN,
 46 		app:            ZmId.APP_TASKS,
 47 		componentType:  ZmId.WIDGET_VIEW,
 48 		componentName:  ZmId.VIEW_TASKEDIT
 49 	};
 50 
 51 	var domId = ZmId.create(idParams, "A task editing view");
 52     ZmCalItemEditView.call(this, parent, null, controller, null, DwtControl.ABSOLUTE_STYLE, "ZmTaskEditView", domId);
 53 };
 54 
 55 ZmTaskEditView.prototype = new ZmCalItemEditView;
 56 ZmTaskEditView.prototype.constructor = ZmTaskEditView;
 57 
 58 
 59 // Consts
 60 
 61 /**
 62  * Defines the priority values.
 63  * 
 64  * @see		ZmCalItem
 65  */
 66 ZmTaskEditView.PRIORITY_VALUES = [
 67 	ZmCalItem.PRIORITY_LOW,
 68 	ZmCalItem.PRIORITY_NORMAL,
 69 	ZmCalItem.PRIORITY_HIGH ];
 70 
 71 /**
 72  * Defines the status values.
 73  * 
 74  * @see		ZmCalendarApp
 75  */
 76 ZmTaskEditView.STATUS_VALUES = [
 77 	ZmCalendarApp.STATUS_NEED,
 78 	ZmCalendarApp.STATUS_COMP,
 79 	ZmCalendarApp.STATUS_INPR,
 80 	ZmCalendarApp.STATUS_WAIT,
 81 	ZmCalendarApp.STATUS_DEFR ];
 82 
 83 // Message dialog placement
 84 ZmTaskEditView.DIALOG_X = 50;
 85 ZmTaskEditView.DIALOG_Y = 100;
 86 
 87 // Public Methods
 88 
 89 /**
 90  * Returns a string representation of the object.
 91  * 
 92  * @return		{String}		a string representation of the object
 93  */
 94 ZmTaskEditView.prototype.toString =
 95 function() {
 96 	return "ZmTaskEditView";
 97 };
 98 
 99 /**
100  * @private
101  */
102 ZmTaskEditView.prototype.set =
103 function(calItem, mode, isDirty) {
104 	this.initialize(calItem, mode, isDirty);
105 
106 	// HACK: TEV takes too long to init so design mode never gets set properly
107 	if (AjxEnv.isGeckoBased) {
108 		var ta = new AjxTimedAction(this, this.reEnableDesignMode);
109 		AjxTimedAction.scheduleAction(ta, 500);
110 	}
111 };
112 
113 /**
114  * Gets the controller.
115  * 
116  * @return	{ZmTaskController}		the controller
117  */
118 ZmTaskEditView.prototype.getController =
119 function() {
120 	return this._controller;
121 };
122 
123 ZmTaskEditView.prototype._getClone =
124 function() {
125 	return ZmTask.quickClone(this._calItem);
126 };
127 
128 ZmTaskEditView.prototype._populateForEdit =
129 function(calItem, mode) {
130 	ZmCalItemEditView.prototype._populateForEdit.call(this, calItem, mode);
131 
132 	if (calItem.startDate) {
133 		var sd = new Date(calItem.startDate.getTime());
134 		this._startDateField.value = AjxDateUtil.simpleComputeDateStr(sd);
135 	}
136 	if (calItem.endDate) {
137 		var ed = new Date(calItem.endDate.getTime());
138 		this._endDateField.value = AjxDateUtil.simpleComputeDateStr(ed);
139 	}
140 
141 
142     var rd = new Date(calItem.remindDate.getTime());
143 
144     if (calItem.alarm) {
145         if (calItem.remindDate && calItem._reminderAbs) {
146             this._remindTimeSelect.set(calItem.remindDate);
147         }
148     } else {
149         var now = AjxDateUtil.roundTimeMins(new Date(), 30);
150         this._remindTimeSelect.set(now);        
151     }
152 
153     if (this._hasReminderSupport) {
154         this._remindDateField.value = AjxDateUtil.simpleComputeDateStr(rd);
155         this._reminderCheckbox.setSelected(calItem.alarm);
156         this._setRemindersEnabled(calItem.alarm);
157 		
158         if (calItem.alarmActions.contains(ZmCalItem.ALARM_EMAIL)) {
159             this._reminderEmailCheckbox.setSelected(true);
160         }
161         if (calItem.alarmActions.contains(ZmCalItem.ALARM_DEVICE_EMAIL)) {
162             this._reminderDeviceEmailCheckbox.setSelected(true);
163         }
164     }
165 
166 	this._location.setValue(calItem.getLocation());
167 	this._setPriority(calItem.priority);
168 	this._statusSelect.setSelectedValue(calItem.status);
169     this._pCompleteSelectInput.setValue(this.formatPercentComplete(calItem.pComplete));
170     if (!this._notesHtmlEditor.getContent() && calItem.message){
171         this._notesHtmlEditor.setContent(calItem.message.getInviteDescriptionContentValue(ZmMimeTable.TEXT_PLAIN) || "");
172     }
173     this._setEmailReminderControls();
174 };
175 
176 ZmTaskEditView.prototype._populateForSave =
177 function(calItem) {
178 
179 	ZmCalItemEditView.prototype._populateForSave.call(this, calItem);
180 
181 	calItem.location = this._location.getValue();
182 	// TODO - normalize
183 	var startDate = AjxDateUtil.simpleParseDateStr(this._startDateField.value);
184 	var endDate = AjxDateUtil.simpleParseDateStr(this._endDateField.value);
185 
186 	if (startDate) {
187 		calItem.setStartDate(startDate, true);
188 	} else {
189 		calItem.startDate = null;	// explicitly null out in case item has old data
190 	}
191 
192 	if (endDate) {
193 		calItem.setEndDate(endDate, true);
194 	} else {
195 		calItem.endDate = null;		// explicitly null out in case item has old data
196 	}
197 
198     //set reminder
199     var reminders = [
200         { control: this._reminderEmailCheckbox,       action: ZmCalItem.ALARM_EMAIL        },
201         { control: this._reminderDeviceEmailCheckbox, action: ZmCalItem.ALARM_DEVICE_EMAIL }
202     ];
203     if (this._hasReminderSupport && this._reminderCheckbox.isSelected()) {
204         var remindDate = AjxDateUtil.simpleParseDateStr(this._remindDateField.value);
205         calItem.alarm = true;
206         calItem.remindDate = remindDate;
207         remindDate = this._remindTimeSelect.getValue(remindDate);
208         var remindFmtStr = AjxDateUtil.getServerDateTime(remindDate,true);
209         calItem.setTaskReminder(remindFmtStr);
210         for (var i = 0; i < reminders.length; i++) {
211             var reminder = reminders[i];
212             if (reminder.control && reminder.control.isSelected()) {
213                 calItem.addReminderAction(reminder.action);
214             }
215             else {
216                 calItem.removeReminderAction(reminder.action);
217             }
218         }
219     } else {
220        calItem.alarm = false;
221        calItem.remindDate = new Date();
222        calItem.setTaskReminder(null);
223     }
224     
225 	calItem.setAllDayEvent(true);
226     var completion = this.getpCompleteInputValue();
227     // Should always be valid at this point - made it past isValid
228     calItem.pComplete = completion.valid ? completion.percent : 0;
229 	calItem.priority = this._getPriority();
230 	calItem.status = this._statusSelect.getValue();
231 
232     //bug:51913 disable alarm when stats is completed
233     if (calItem.pComplete === 100 && this._statusSelect.getValue() === ZmCalendarApp.STATUS_COMP) {
234        calItem.alarm = false;
235        calItem.remindDate = new Date();
236        calItem.setTaskReminder(null);
237        for (var i = 0; i < reminders.length; i++) {
238            var reminder = reminders[i];
239            if (reminder.control && reminder.control.isSelected()) {
240                calItem.removeReminderAction(reminder.action);
241            }
242        }
243     }
244 
245 //	XXX: uncomment when supported
246 //	this._getRecurrence(calItem);	// set any recurrence rules LAST
247 
248 	return calItem;
249 };
250 
251 ZmTaskEditView.prototype.isValid =
252 function() {
253 	var errorMsg;
254 	var subj = AjxStringUtil.trim(this._subjectField.getValue());
255     if (subj && subj.length) {
256 		var startDate = AjxStringUtil.trim(this._startDateField.value);
257 		var endDate =   AjxStringUtil.trim(this._endDateField.value);
258 		if (startDate.length > 0 && (!DwtTimeSelect.validStartEnd(this._startDateField, this._endDateField))) {
259 			if(endDate.length <= 0) {
260 				errorMsg = ZmMsg.errorEmptyTaskDueDate;
261 			} else {
262 				errorMsg = ZmMsg.errorInvalidDates;
263 			}
264 		}
265 		var remindTime =  DwtTimeSelect.parse(this._remindTimeSelect.getInputField().getValue());
266 		if (!remindTime) {
267 			errorMsg = AjxMsg.invalidTimeString;
268 		}
269 		var completion =  this.getpCompleteInputValue();
270 		if (!completion.valid) {
271 			errorMsg = ZmMsg.errorInvalidPercentage;
272 		} else if ((completion.percent < 0) || (completion.percent > 100)) {
273 			errorMsg = ZmMsg.errorInvalidPercentage;
274 		}
275     } else {
276 		errorMsg = ZmMsg.errorMissingSubject;
277 	}
278 
279 	if (errorMsg) {
280 		throw errorMsg;
281 	}
282 
283 	return true;
284 };
285 
286 ZmTaskEditView.prototype.cleanup =
287 function() {
288 	ZmCalItemEditView.prototype.cleanup.call(this);
289 
290 	this._startDateField.value = "";
291 	this._endDateField.value = "";
292 };
293 
294 
295 // Private/protected Methods
296 
297 ZmTaskEditView.prototype._createHTML =
298 function() {
299 	//this._repeatDescId		= this._htmlElId + "_repeatDesc";
300     this._isAppt = false;
301 	var subs = {
302 		id: this._htmlElId,
303 		height: (this.parent.getSize().y - 30),
304 		locationId: (this._htmlElId + "_location"),
305 		isGalEnabled: appCtxt.get(ZmSetting.GAL_ENABLED),
306 		isAppt: false
307 	};
308 
309 	// XXX: rename template name to CalItem#CalItemEdit
310 	this.getHtmlElement().innerHTML = AjxTemplate.expand("calendar.Appointment#EditView", subs);
311 };
312 
313 ZmTaskEditView.prototype._getPriorityImage =
314 function(flag) {
315 	if (ZmCalItem.isPriorityHigh(flag))	{ return "PriorityHigh_list"; }
316 	if (ZmCalItem.isPriorityLow(flag))	{ return "PriorityLow_list"; }
317 	return "PriorityNormal_list";
318 };
319 
320 ZmTaskEditView.prototype._getPriorityText =
321 function(flag) {
322 	if (ZmCalItem.isPriorityHigh(flag))	{ return ZmMsg.high; }
323 	if (ZmCalItem.isPriorityLow(flag))	{ return ZmMsg.low; }
324 	return ZmMsg.normal;
325 };
326 
327 ZmTaskEditView.prototype._createPriorityMenuItem =
328 function(menu, text, flag) {
329 	// I prefer a readable ID part for the priority, over the 1, 5 and 9 that those constants are set to.
330 	var priorityId = ZmCalItem.isPriorityHigh(flag) ? "high" : ZmCalItem.isPriorityLow(flag) ? "low" : "normal";
331 	var item = DwtMenuItem.create({parent: menu, imageInfo: this._getPriorityImage(flag), text: text, id: Dwt.getNextId("EditTaskPriorityMenu_" + priorityId + "_")});
332 	item._priorityFlag = flag;
333 	item.addSelectionListener(this._priorityMenuListnerObj);
334 };
335 
336 ZmTaskEditView.prototype._priorityButtonMenuCallback =
337 function() {
338 	var menu = new DwtMenu({parent: this._prioritySelect, id: Dwt.getNextId("EditTaskPriorityMenu_")});
339 	this._priorityMenuListnerObj = new AjxListener(this, this._priorityMenuListner);
340 	this._createPriorityMenuItem(menu, ZmMsg.high, ZmCalItem.PRIORITY_HIGH);
341 	this._createPriorityMenuItem(menu, ZmMsg.normal, ZmCalItem.PRIORITY_NORMAL);
342 	this._createPriorityMenuItem(menu, ZmMsg.low, ZmCalItem.PRIORITY_LOW);
343 	return menu;
344 };
345 
346 ZmTaskEditView.prototype._priorityMenuListner =
347 function(ev) {
348 	this._setPriority(ev.dwtObj._priorityFlag);
349 };
350 
351 ZmTaskEditView.prototype._getPriority =
352 function() {
353 	return (this._prioritySelect)
354 		? (this._prioritySelect._priorityFlag || "") : "";
355 };
356 
357 ZmTaskEditView.prototype._setPriority =
358 function(flag) {
359 	if (this._prioritySelect) {
360 		flag = flag || "";
361 		this._prioritySelect.setImage(this._getPriorityImage(flag));
362         this._prioritySelect.setText(this._getPriorityText(flag))
363 		this._prioritySelect._priorityFlag = flag;
364 	}
365 };
366 
367 ZmTaskEditView.prototype.getpCompleteInputValue = function() {
368     var pValue  = this._pCompleteSelectInput.getValue();
369     pValue      = pValue.replace(/[%\u066A]/g,"");  // also check for Arabic % character
370 	pValue      = pValue.trim();
371     var valid = /^\d*$/.test(pValue);
372     var percent = 0;
373     if (valid) {
374         percent = Math.round(pValue);
375     }
376 
377   return { valid: valid, percent: percent};
378 };
379 
380 ZmTaskEditView.prototype._unSelectRemindersCheckbox = function() {
381     var reminders = [
382         { control: this._reminderEmailCheckbox},
383         { control: this._reminderDeviceEmailCheckbox},
384         { control: this._reminderCheckbox}
385     ];
386     for (var i = 0; i < reminders.length; i++) {
387         var reminder = reminders[i];
388         if (reminder.control) {
389             reminder.control.setSelected(false);
390         }
391     }
392 };
393 
394 ZmTaskEditView.prototype.formatPercentComplete = function(pValue) {
395 
396    var formatter = new AjxMessageFormat(AjxMsg.percentageString);
397    if(AjxUtil.isString(pValue) && pValue.indexOf("%") != -1) {
398        return formatter.format(Math.round(pValue.replace(/[%\u066A]/g,"")));  // also check for Arabic % character
399    } else {
400        return formatter.format(Math.round(pValue));
401    }
402 };
403 
404 ZmTaskEditView.prototype._createWidgets =
405 function(width) {
406 	ZmCalItemEditView.prototype._createWidgets.call(this, width);
407 
408 	// add location
409 	var params = {parent: this, type: DwtInputField.STRING};
410 	this._location = new DwtInputField(params);
411 	Dwt.setSize(this._location.getInputElement(), width, "2rem");
412 	this._location.reparentHtmlElement(this._htmlElId + "_location");
413 
414 	// add priority DwtButton
415 	this._prioritySelect = new DwtButton({parent:this});
416     this._prioritySelect.setSize(60, Dwt.DEFAULT);
417 	this._prioritySelect.setMenu(new AjxCallback(this, this._priorityButtonMenuCallback));
418 	this._prioritySelect.reparentHtmlElement(this._htmlElId + "_priority");
419 
420 	var listener = new AjxListener(this, this._selectListener);
421 	// add status DwtSelect
422 	this._statusSelect = new DwtSelect({parent:this});
423 	for (var i = 0; i < ZmTaskEditView.STATUS_VALUES.length; i++) {
424 		var v = ZmTaskEditView.STATUS_VALUES[i];
425 		this._statusSelect.addOption(ZmCalItem.getLabelForStatus(v), i==0, v);
426 	}
427 	this._statusSelect.addChangeListener(listener);
428 	this._statusSelect.reparentHtmlElement(this._htmlElId + "_status");
429 
430     var params = {
431         parent: this,
432         parentElement: (this._htmlElId + "_pCompleteSelectInput"),
433         type: DwtInputField.STRING,
434         errorIconStyle: DwtInputField.ERROR_ICON_NONE,
435         validationStyle: DwtInputField.CONTINUAL_VALIDATION
436     };
437     this._pCompleteSelectInput = new DwtInputField(params);
438     var pCompleteInputEl = this._pCompleteSelectInput.getInputElement();
439     Dwt.setSize(pCompleteInputEl, Dwt.DEFAULT, "2rem");
440     pCompleteInputEl.onblur = AjxCallback.simpleClosure(this._handleCompleteOnBlur, this, pCompleteInputEl);
441 
442     var pCompleteButtonListener = new AjxListener(this, this._pCompleteButtonListener);
443     var pCompleteSelectListener = new AjxListener(this, this._pCompleteSelectListener);
444     this._pCompleteButton = ZmTasksApp.createpCompleteButton(this, this._htmlElId + "_pCompleteSelect", pCompleteButtonListener, pCompleteSelectListener);
445 
446 	this._hasReminderSupport = Dwt.byId(this._htmlElId + "_reminderCheckbox") != null;
447 
448     if (this._hasReminderSupport) {
449         // reminder date DwtButton's
450         var remindDateBtnListener = new AjxListener(this, this._remindDateBtnListener);
451         var remindDateCalSelectionListener = new AjxListener(this, this._dateCalSelectionListener);
452 
453         this._reminderLabel = Dwt.byId(this._htmlElId+"_reminderLabel");
454 
455         this._reminderCheckbox = new DwtCheckbox({parent:this});
456         this._reminderCheckbox.replaceElement(this._htmlElId+"_reminderCheckbox");
457         this._reminderCheckbox.addSelectionListener(new AjxListener(this, this._setEmailReminderControls));
458 
459         this._remindDateField = document.getElementById(this._htmlElId + "_remindDateField");
460         this._remindDateButton = ZmCalendarApp.createMiniCalButton(this, this._htmlElId + "_remindMiniCalBtn", remindDateBtnListener, remindDateCalSelectionListener);
461         this._remindDateButton.reparentHtmlElement(this._htmlElId + "_remindMiniCalBtn");
462 
463         // time DwtTimeSelect
464         this._remindTimeSelect = new DwtTimeInput(this, DwtTimeInput.START);
465         this._remindTimeSelect.reparentHtmlElement(this._htmlElId + "_remindTimeSelect");
466 
467         this._reminderEmailCheckbox = new DwtCheckbox({parent: this});
468         this._reminderEmailCheckbox.replaceElement(document.getElementById(this._htmlElId + "_reminderEmailCheckbox"));
469         this._reminderEmailCheckbox.setText(ZmMsg.email);
470         this._reminderDeviceEmailCheckbox = new DwtCheckbox({parent: this});
471         this._reminderDeviceEmailCheckbox.replaceElement(document.getElementById(this._htmlElId + "_reminderDeviceEmailCheckbox"));
472         this._reminderDeviceEmailCheckbox.setText(ZmMsg.deviceEmail);
473         this._reminderConfigure = new DwtText({parent:this,className:"FakeAnchor"});
474         this._reminderConfigure.setText(ZmMsg.remindersConfigure);
475         this._reminderConfigure.replaceElement(document.getElementById(this._htmlElId+"_reminderConfigure"));
476         this._setEmailReminderControls();
477         
478         var settings = appCtxt.getSettings();
479         var listener = new AjxListener(this, this._settingChangeListener);
480         settings.getSetting(ZmSetting.CAL_EMAIL_REMINDERS_ADDRESS).addChangeListener(listener);
481         settings.getSetting(ZmSetting.CAL_DEVICE_EMAIL_REMINDERS_ADDRESS).addChangeListener(listener);
482     }
483 };
484 
485 ZmTaskEditView.prototype._remindDateBtnListener =
486 function(ev) {
487 	var calDate = ev.item == this._remindDateButton
488 		? AjxDateUtil.simpleParseDateStr(this._remindDateField.value)
489 		: null;
490 
491 	// if date was input by user and its foobar, reset to today's date
492 	if (calDate == null || isNaN(calDate)) {
493 		calDate = new Date();
494 		var field = ev.item == this._remindDateButton ? this._remindDateField : null;
495 
496         var calEndDate = AjxDateUtil.simpleParseDateStr(this._endDateField.value);
497         if (calEndDate != null || isNaN(calEndDate)) {
498             calDate = calEndDate;
499         }
500         
501         field.value = AjxDateUtil.simpleComputeDateStr(calDate);
502 	}
503 
504 	// always reset the date to current field's date
505 	var menu = ev.item.getMenu();
506 	var cal = menu.getItem(0);
507 	cal.setDate(calDate, true);
508 	ev.item.popup();
509 };
510 
511 ZmTaskEditView.prototype._checkReminderDate =
512 function(){
513     var currDate = new Date();
514     var rd = AjxDateUtil.simpleParseDateStr(this._remindDateField.value);
515     if (rd.valueOf() < currDate.valueOf()){
516         this._remindDateField.value = AjxDateFormat.getDateInstance(AjxDateFormat.SHORT).format(currDate);
517     }
518 };
519 
520 ZmTaskEditView.prototype._dateCalSelectionListener = function(ev) {
521 
522     ZmCalItemEditView.prototype._dateCalSelectionListener.call(this,ev);
523 
524     var parentButton = ev.item.parent.parent;
525 	var newDate = AjxDateUtil.simpleComputeDateStr(ev.detail);
526 
527     var ed = AjxDateUtil.simpleParseDateStr(this._endDateField.value);
528     var sd = AjxDateUtil.simpleParseDateStr(this._startDateField.value);
529     var rd = AjxDateUtil.simpleParseDateStr(this._remindDateField.value);
530 
531 	// change the start/end date if they mismatch
532 	if (parentButton == this._endDateButton) {
533 		if(rd && (rd.valueOf() > ev.detail.valueOf())) {
534             this._remindDateField.value = newDate;
535         }
536 		//this._endDateField.value = newDate;
537 	} else if(parentButton == this._remindDateButton) {
538         if (ed && (ed.valueOf() < ev.detail.valueOf())) {
539 			this._endDateField.value = newDate;
540         }
541         if (ed == null && sd && (sd.valueOf() < ev.detail.valueOf())) {
542 			this._startDateField.value = newDate;
543         }
544 		this._remindDateField.value = newDate;
545 	} else if(parentButton == this._startDateButton) {
546         if (ed == null && rd && (rd.valueOf() > ev.detail.valueOf())) {
547               this._remindDateField.value = newDate;
548         }
549     }
550     this._checkReminderDate();
551 };
552 
553 
554 ZmTaskEditView.prototype._addEventHandlers =
555 function() {
556 	var edvId = AjxCore.assignId(this);
557 
558 	// add event listeners where necessary
559 	//Dwt.setHandler(this._repeatDescField, DwtEvent.ONCLICK, ZmCalItemEditView._onClick);
560 	//Dwt.setHandler(this._repeatDescField, DwtEvent.ONMOUSEOVER, ZmCalItemEditView._onMouseOver);
561 	//Dwt.setHandler(this._repeatDescField, DwtEvent.ONMOUSEOUT, ZmCalItemEditView._onMouseOut);
562 
563 	//this._repeatDescField._editViewId =
564     if (this._hasReminderSupport) {
565         // TODO: What is this for?
566         this._reminderCheckbox._editViewId = edvId;
567     }
568 };
569 
570 // cache all input fields so we dont waste time traversing DOM each time
571 ZmTaskEditView.prototype._cacheFields =
572 function() {
573 	ZmCalItemEditView.prototype._cacheFields.call(this);
574 	// HACK: hide all recurrence-related fields until tasks supports it
575 	//this._repeatSelect.setVisibility(false);
576 	//var repeatLabel = document.getElementById(this._htmlElId + "_repeatLabel");
577 	//Dwt.setVisibility(repeatLabel, false);
578 	//Dwt.setVisibility(this._repeatDescField, false);
579     this._setRemindersEnabled(false);
580 };
581 
582 // Returns a string representing the form content
583 ZmTaskEditView.prototype._formValue =
584 function(excludeAttendees) {
585 	var vals = [];
586 
587 	vals.push(this._subjectField.getValue());
588 	vals.push(this._location.getValue());
589 	vals.push(this._getPriority());
590 	vals.push(this._folderSelect.getValue());
591     var completion = this.getpCompleteInputValue();
592 	vals.push(completion.valid ? completion.percent : 0);
593 	vals.push(this._statusSelect.getValue());
594 	var startDate = AjxDateUtil.simpleParseDateStr(this._startDateField.value);
595 	if (startDate) vals.push(AjxDateUtil.getServerDateTime(startDate));
596 	var endDate = AjxDateUtil.simpleParseDateStr(this._endDateField.value);
597 	if (endDate) vals.push(AjxDateUtil.getServerDateTime(endDate));
598 
599     if (this._hasReminderSupport) {
600         var hasReminder = this._reminderCheckbox.isSelected();
601         vals.push(hasReminder);
602         if (hasReminder) {
603             var remindDate = AjxDateUtil.simpleParseDateStr(this._remindDateField.value);
604             remindDate = this._remindTimeSelect.getValue(remindDate);
605             if(remindDate) {
606                 vals.push(
607                     AjxDateUtil.getServerDateTime(remindDate)
608                 );
609             }
610             vals.push(this._reminderEmailCheckbox.isSelected());
611             vals.push(this._reminderDeviceEmailCheckbox.isSelected());
612         }
613     }
614 
615 	//vals.push(this._repeatSelect.getValue());
616 	vals.push(this._notesHtmlEditor.getContent());
617 
618 	var str = vals.join("|");
619 	str = str.replace(/\|+/, "|");
620 	return str;
621 };
622 
623 ZmTaskEditView.prototype._addTabGroupMembers =
624 function(tabGroup) {
625 	tabGroup.addMember(this._subjectField);
626 	tabGroup.addMember(this._location);
627 
628 	var bodyFieldId = this._notesHtmlEditor.getBodyFieldId();
629 	tabGroup.addMember(document.getElementById(bodyFieldId));
630 };
631 
632 // Consistent spot to locate various dialogs
633 ZmTaskEditView.prototype._getDialogXY =
634 function() {
635 	var loc = Dwt.toWindow(this.getHtmlElement(), 0, 0);
636 	return new DwtPoint(loc.x + ZmTaskEditView.DIALOG_X, loc.y + ZmTaskEditView.DIALOG_Y);
637 };
638 
639 ZmTaskEditView.prototype._setPercentCompleteFields =
640 function(isComplete) {
641 	var val = isComplete
642 		? ZmTaskEditView.STATUS_VALUES[1]
643 		: ZmTaskEditView.STATUS_VALUES[0];
644 	this._statusSelect.setSelectedValue(val);
645     this._pCompleteSelectInput.setValue(this.formatPercentComplete(isComplete ? 100 : 0));
646 };
647 
648 // Listeners
649 ZmTaskEditView.prototype._handleCompleteOnBlur =
650 function(inputEl) {
651 	var pCompleteString = inputEl.value;
652     if (!pCompleteString) {
653 		inputEl.value = this.formatPercentComplete(0);
654 		return;
655 	}
656     var newVal = this.getpCompleteInputValue();
657     if (newVal.valid) {
658         if (newVal.percent == 100) {
659             this._statusSelect.setSelectedValue(ZmCalendarApp.STATUS_COMP);
660             this._unSelectRemindersCheckbox();   //bug:51913 disable alarm when stats is completed
661         } else if (newVal.percent == 0) {
662             this._statusSelect.setSelectedValue(ZmCalendarApp.STATUS_NEED);
663         } else if ((newVal.percent > 0 || newVal.percent < 100) && (this._statusSelect.getValue() != ZmCalendarApp.STATUS_COMP ||
664                     this._statusSelect.getValue() != ZmCalendarApp.STATUS_NEED)) {
665             this._statusSelect.setSelectedValue(ZmCalendarApp.STATUS_INPR);
666         }
667         inputEl.value = this.formatPercentComplete(pCompleteString);
668     }
669 };
670 
671 ZmTaskEditView.prototype._pCompleteButtonListener =
672 function(ev) {
673 	var menu = ev.item.getMenu();
674 	ev.item.popup();
675 };
676 
677 ZmTaskEditView.prototype._pCompleteSelectListener =
678 function(ev) {
679 	if(ev.item && ev.item instanceof DwtMenuItem){
680         var newVal = ev.item.getData("value");
681         this._pCompleteSelectInput.setValue(ev.item.getText());
682 
683         if (newVal == 100) {
684 			this._statusSelect.setSelectedValue(ZmCalendarApp.STATUS_COMP);
685             this._unSelectRemindersCheckbox();  //bug:51913 disable alarm when stats is completed
686 		} else if (newVal == 0) {
687 			this._statusSelect.setSelectedValue(ZmCalendarApp.STATUS_NEED);
688 		} else if ((newVal > 0 || newVal < 100) && (this._statusSelect.getValue() != ZmCalendarApp.STATUS_COMP || this._statusSelect.getValue() != ZmCalendarApp.STATUS_NEED))
689 		{
690 			this._statusSelect.setSelectedValue(ZmCalendarApp.STATUS_INPR);
691 		}
692 		this._setEmailReminderControls();
693         return;
694     }
695 };
696 
697 ZmTaskEditView.prototype._selectListener =
698 function(ev) {
699 	var newVal = ev._args.newValue;
700 	var oldVal = ev._args.oldValue;
701 
702 	if (newVal == oldVal) { return; }
703 
704 	var selObj = ev._args.selectObj;
705 
706 	if (selObj == this._statusSelect) {
707 		if (newVal == ZmCalendarApp.STATUS_COMP) {
708 			this._pCompleteSelectInput.setValue(this.formatPercentComplete(100));
709             this._unSelectRemindersCheckbox();    //bug:51913 disable alarm when stats is completed
710 		} else if (newVal == ZmCalendarApp.STATUS_NEED) {
711 			this._pCompleteSelectInput.setValue(this.formatPercentComplete(0));
712 		} else if (newVal == ZmCalendarApp.STATUS_INPR) {
713             var completion = this.getpCompleteInputValue();
714             if (completion.valid && (completion.percent == 100)) {
715 				this._pCompleteSelectInput.setValue(this.formatPercentComplete(0));
716 			}
717 		}
718 	} else {
719 		if (newVal == 100) {
720 			this._statusSelect.setSelectedValue(ZmCalendarApp.STATUS_COMP);
721             this._unSelectRemindersCheckbox();
722 		} else if (newVal == 0) {
723 			this._statusSelect.setSelectedValue(ZmCalendarApp.STATUS_NEED);
724 		} else if ((oldVal == 0 || oldVal == 100) &&
725 			 		(newVal > 0 || newVal < 100) &&
726 					(this._statusSelect.getValue() == ZmCalendarApp.STATUS_COMP ||
727 					 this._statusSelect.getValue() == ZmCalendarApp.STATUS_NEED))
728 		{
729 			this._statusSelect.setSelectedValue(ZmCalendarApp.STATUS_INPR);
730 		}
731 	}
732 	this._setEmailReminderControls();
733 };
734 
735 
736 // Callbacks
737 
738 ZmTaskEditView.prototype._handleOnClick =
739 function(el) {
740 		ZmCalItemEditView.prototype._handleOnClick.call(this, el);
741 };
742 
743 
744 ZmTaskEditView.prototype._setRemindersEnabled =
745 function(isEnabled) {
746     if (this._hasReminderSupport) {
747         this._remindDateButton.setEnabled(isEnabled);
748         this._remindTimeSelect.setEnabled(isEnabled);
749         Dwt.addClass(this._remindDateField.parentNode, !isEnabled ? 'DWTInputField-disabled' : 'DWTInputField', !isEnabled ? 'DWTInputField' : 'DWTInputField-disabled');
750         this._remindDateField.disabled = !isEnabled;
751     }
752 };
753 
754 ZmTaskEditView.prototype._setRemindersConfigureEnabled = function(enabled) {
755 	this._reminderConfigure.setEnabled(enabled);
756     this._reminderConfigure.getHtmlElement().onclick = enabled ? AjxCallback.simpleClosure(skin.gotoPrefs, skin, "NOTIFICATIONS") : null;
757 };
758 
759 //
760 // ZmCalItemEditView methods
761 //
762 
763 ZmTaskEditView.prototype._setEmailReminderControls = function() {
764     if (this._hasReminderSupport) {
765 
766 		ZmCalItemEditView.prototype._setEmailReminderControls.apply(this, arguments);
767 
768 		// Bug 55392: Disable reminders altogether when task is completed
769 		var remindersEnabled = (this._statusSelect.getValue() != ZmCalendarApp.STATUS_COMP);
770 		Dwt.condClass(this._reminderLabel, remindersEnabled, "", "ZDisabled");
771 		this._reminderCheckbox.setEnabled(remindersEnabled);
772 
773 		// primary reminder checkbox overrides other values
774 		var isSelected = this._reminderCheckbox.isSelected();
775 		this._setRemindersEnabled(isSelected);
776 		if (!isSelected) {
777 		    this._reminderEmailCheckbox.setEnabled(false);
778 		    this._reminderDeviceEmailCheckbox.setEnabled(false);
779 		}
780 		this._setRemindersConfigureEnabled(isSelected);
781 		this._reminderDeviceEmailCheckbox.setVisible(appCtxt.get(ZmSetting.CAL_DEVICE_EMAIL_REMINDERS_ENABLED));
782 	}
783 };
784 
785 ZmTaskEditView.prototype.adjustReminderValue = function(calItem) {
786     // no-op
787 };
788 
789 ZmTaskEditView.prototype._resetReminders = function() {
790     // no-op
791 };
792