1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2011, 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) 2011, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25 * Simple dialog allowing user to choose between an Instance or Series for an appointment
 26 * @constructor
 27 * @class
 28 *
 29 * @author Santosh Sutar
 30 * @param parent			the element that created this view
 31 * 
 32 * 
 33 * @extends		DwtDialog
 34 * @private
 35 */
 36 ZmCalPrintDialog = function(params) {
 37 
 38     var print = new DwtDialog_ButtonDescriptor(ZmCalPrintDialog.PRINT_BUTTON, ZmMsg.print, DwtDialog.ALIGN_RIGHT);
 39     var cancel = new DwtDialog_ButtonDescriptor(ZmCalPrintDialog.PRINT_CANCEL_BUTTON, ZmMsg.cancel, DwtDialog.ALIGN_RIGHT);
 40     var parent = params.parent || appCtxt.getShell();
 41 	ZmDialog.call(this, {parent:parent, standardButtons:[DwtDialog.NO_BUTTONS], extraButtons: [print, cancel]});
 42 
 43 	this.setButtonListener(ZmCalPrintDialog.PRINT_BUTTON, new AjxListener(this, this._printButtonListener));
 44 	this.setButtonListener(ZmCalPrintDialog.PRINT_CANCEL_BUTTON, new AjxListener(this, this._printCancelButtonListener));
 45     this.setTitle(ZmMsg.printCalendar);
 46 	this.setContent(this._setHtml());
 47     this._createControls();
 48     this._setViewOptions();
 49 };
 50 
 51 ZmCalPrintDialog.prototype = new ZmDialog;
 52 ZmCalPrintDialog.prototype.constructor = ZmCalPrintDialog;
 53 
 54 ZmCalPrintDialog.PRINT_BUTTON = ++DwtDialog.LAST_BUTTON;
 55 ZmCalPrintDialog.PRINT_CANCEL_BUTTON = ++DwtDialog.LAST_BUTTON;
 56 ZmCalPrintDialog.DATE_FORMAT = "yyyyMMddTHHmmss";
 57 ZmCalPrintDialog.TIME_FORMAT = "HH:mm";
 58 
 59 // Public methods
 60 
 61 ZmCalPrintDialog.prototype.toString =
 62 function() {
 63 	return "ZmCalPrintDialog";
 64 };
 65 
 66 ZmCalPrintDialog.prototype.popup =
 67 function(params) {
 68     //this._keyPressedInField = false; //see comment in _handleKeyUp
 69 
 70 	// use reasonable defaults
 71 	params = params || {};
 72 
 73 	var treeIds = this._treeIds = (params.treeIds && params.treeIds.length)
 74 		? params.treeIds : [ZmOrganizer.FOLDER];
 75 
 76     //Omit the trash form the tree view
 77 	var omitParam = {};
 78     omitParam[ZmOrganizer.ID_TRASH] = true;
 79 
 80 	var popupParams = {
 81 		treeIds:		treeIds,
 82 		omit:			omitParam,
 83 		fieldId:		this._htmlElId + "_calTreeContainer",
 84 		overviewId:		params.overviewId,
 85 		noRootSelect:	params.noRootSelect,
 86 		treeStyle:		params.treeStyle || DwtTree.SINGLE_STYLE,	// we don't want checkboxes!
 87 		appName:		params.appName,
 88 		selectable:		false,
 89 		forceSingle:	params.forceSingle
 90 	};
 91 
 92 	// make sure the requisite packages are loaded
 93 	var treeIdMap = {};
 94 	for (var i = 0; i < treeIds.length; i++) {
 95 		treeIdMap[treeIds[i]] = true;
 96 	}
 97 	var ov = this._setOverview(popupParams, popupParams.forceSingle);
 98 	ZmDialog.prototype.popup.call(this);
 99 
100     this.currentViewId = params.currentViewId;
101     var cv = ZmCalViewController.VIEW_TO_OP[params.currentViewId];
102     if(cv == ZmOperation.WORK_WEEK_VIEW) {
103         cv = ZmOperation.WEEK_VIEW;
104     }
105     else if (cv == ZmOperation.FB_VIEW) {
106         cv = ZmOperation.DAY_VIEW;
107     }
108     this._viewSelect.setSelectedValue(cv);
109     this._setViewOptions();
110     this.setWorkingHours(params.workHours);
111     this._selDate.setValue(params.currentDate);
112     this._dateRangeFrom.setValue(new Date(params.timeRange.start));
113     this._dateRangeTo.setValue(new Date(params.timeRange.end));
114     if(ZmId.VIEW_CAL_WORK_WEEK == this.currentViewId) {
115         document.getElementById(this._htmlElId + "_workDaysOnly").checked = true;
116     }
117 };
118 
119 ZmCalPrintDialog.prototype.setWorkingHours =
120 function(wHrs) {
121     var fromTime = new Date(),
122         toTime = new Date();
123     fromTime.setHours(wHrs.startTime[0]/100, wHrs.startTime[0]%100, 0, 0);
124     toTime.setHours(wHrs.endTime[0]/100, wHrs.endTime[0]%100, 0, 0);
125     this._fromTimeSelect.set(fromTime);
126     this._toTimeSelect.set(toTime);
127 };
128 
129 // Private / protected methods
130 
131 ZmCalPrintDialog.prototype._setHtml =
132 function() {
133 	var html = AjxTemplate.expand("calendar.Calendar#PrintDialog", {id: this._htmlElId});
134     return html;
135 };
136 
137 ZmCalPrintDialog.prototype._createControls =
138 function() {
139     var i,
140         op,
141         list,
142         dateRangeRadio = document.getElementById(this._htmlElId + "_dateRangeRadio"),
143         selDateRadio = document.getElementById(this._htmlElId + "_selDateRadio"),
144         radioListener = AjxCallback.simpleClosure(this._setSelectedDateRadioListener, this);
145     this._selDate = new ZmDateInput(this, this._htmlElId + "_selDate", this._htmlElId + "_selDateContainer");
146 
147     this._todayButton = new DwtButton({parent:this, parentElement:this._htmlElId + "_todayButtonContainer"});
148     this._todayButton.setText(ZmMsg.today);
149     this._todayButton.addSelectionListener(new AjxListener(this, this._setDateToToday))
150 
151     this._dateRangeFrom = new ZmDateInput(this, this._htmlElId + "_dateRangeFrom", this._htmlElId + "_dateRangeFromContainer");
152     this._dateRangeTo = new ZmDateInput(this, this._htmlElId + "_dateRangeTo", this._htmlElId + "_dateRangeToContainer");
153 
154     this._viewSelect = new DwtSelect({parent:this, parentElement:this._htmlElId + "_printViewContainer"});
155     list = [
156 		ZmOperation.DAY_VIEW, ZmOperation.WEEK_VIEW,
157 		ZmOperation.MONTH_VIEW, ZmOperation.CAL_LIST_VIEW
158 	];
159     for(i=0; i<list.length; i++) {
160         op = ZmOperation.defineOperation(list[i]);
161         this._viewSelect.addOption(op.text, false, op.id, op.image);
162     }
163 
164     this._viewSelect.addChangeListener(new AjxListener(this, this._setViewOptions));
165 
166     this._fromTimeSelect = new DwtTimeInput(this, DwtTimeInput.START, this._htmlElId + "_fromHoursContainer");
167 	this._toTimeSelect = new DwtTimeInput(this, DwtTimeInput.END, this._htmlElId + "_toHoursContainer");
168     this._printErrorMsgContainer = document.getElementById(this._htmlElId + "_printErrorMsgContainer");
169 
170     dateRangeRadio.onclick = radioListener;
171     selDateRadio.onclick = radioListener;
172 };
173 
174 ZmCalPrintDialog.prototype._setSelectedDateRadioListener =
175 function(ev) {
176     var target = DwtUiEvent.getTarget(ev);
177     if(target.id == this._htmlElId + "_selDateRadio") {
178         this._setSelectedDateEnabled(true);
179     }
180     else {
181         this._setSelectedDateEnabled(false);
182     }
183 };
184 
185 ZmCalPrintDialog.prototype._setDateToToday =
186 function(ev) {
187     var d = new Date();
188     this._selDate.setValue(d);
189 };
190 
191 ZmCalPrintDialog.prototype._setSelectedDateEnabled =
192 function(enabled) {
193     var dateRangeRadio = document.getElementById(this._htmlElId + "_dateRangeRadio"),
194         selDateRadio = document.getElementById(this._htmlElId + "_selDateRadio");
195     if(enabled) {
196         //Disable the date range controls
197         this._dateRangeFrom.setEnabled(false);
198         this._dateRangeTo.setEnabled(false);
199         dateRangeRadio.checked = false;
200         //dateRangeRadio.disabled = true;
201 
202         //Enable selected date controls
203         this._selDate.setEnabled(true);
204         this._todayButton.setEnabled(true);
205         selDateRadio.checked = true;
206         //selDateRadio.disabled = false;
207     }
208     else {
209         //Enable date range controls
210         this._dateRangeFrom.setEnabled(true);
211         this._dateRangeTo.setEnabled(true);
212         dateRangeRadio.checked = true;
213         //dateRangeRadio.disabled = false;
214 
215         //Disable selected date controls
216         this._selDate.setEnabled(false);
217         this._todayButton.setEnabled(false);
218         selDateRadio.checked = false;
219         //selDateRadio.disabled = true;
220     }
221 };
222 
223 ZmCalPrintDialog.prototype._validateDateTime =
224 function(ev) {
225     var hoursContainer = document.getElementById(this._htmlElId + "_hoursContainer"),
226         isValid = false;
227 
228     if (this._selDate.getEnabled()) { //If we have choosen "Selected Date"
229         isValid = this._selDate.getValue();
230     }
231     else if (this._dateRangeFrom.getEnabled() &&
232         this._dateRangeTo.getEnabled()) {  //If we have choosen "Date Range"
233         var startDate = this._dateRangeFrom.getTimeValue();
234         var endDate = this._dateRangeTo.getTimeValue();
235         isValid = startDate && endDate && endDate >= startDate;
236     }
237 
238     if(isValid //If either "Selected Date" or "Date Range" is correct then we go for time validation
239             && Dwt.getVisible(hoursContainer)
240             && (this._selDate.getEnabled() || (startDate === endDate))) { //Only if dates are same does Time comparison matter
241         var startTime = this._fromTimeSelect.getValue();
242         var endTime = this._toTimeSelect.getValue();
243 
244         if(endTime < startTime) {
245             isValid = false;
246         }
247     }
248 
249     if(!isValid) {
250         Dwt.setDisplay(this._printErrorMsgContainer, Dwt.DISPLAY_BLOCK);
251         this._printErrorMsgContainer.innerHTML = ZmMsg.errorInvalidDates;
252     }
253     return isValid;
254 };
255 
256 ZmCalPrintDialog.prototype._setViewOptions =
257 function(ev) {
258     var val = this._viewSelect.getValue();
259 
260     var workDaysOnlyContainer = document.getElementById(this._htmlElId + "_workDaysOnlyContainer");
261     var oneWeekPerPageContainer = document.getElementById(this._htmlElId + "_oneWeekPerPageContainer");
262     var oneDayPerPageContainer = document.getElementById(this._htmlElId + "_oneDayPerPageContainer");
263     var includeMiniCalContainer = document.getElementById(this._htmlElId + "_includeMiniCalContainer");
264     var hoursContainer = document.getElementById(this._htmlElId + "_hoursContainer");
265 
266 
267     Dwt.setDisplay(includeMiniCalContainer, Dwt.DISPLAY_BLOCK);
268     Dwt.setDisplay(hoursContainer, Dwt.DISPLAY_BLOCK);
269     this._resetCheckboxes(false);
270 
271     switch(val) {
272         case ZmOperation.FB_VIEW:
273         case ZmOperation.DAY_VIEW:
274             Dwt.setDisplay(workDaysOnlyContainer, Dwt.DISPLAY_NONE);
275             Dwt.setDisplay(oneWeekPerPageContainer, Dwt.DISPLAY_NONE);
276             Dwt.setDisplay(oneDayPerPageContainer, Dwt.DISPLAY_BLOCK);
277 
278             this._setSelectedDateEnabled(true);
279             break;
280 
281         case ZmOperation.WORK_WEEK_VIEW:
282         case ZmOperation.WEEK_VIEW:
283             Dwt.setDisplay(workDaysOnlyContainer, Dwt.DISPLAY_BLOCK);
284             Dwt.setDisplay(oneWeekPerPageContainer, Dwt.DISPLAY_BLOCK);
285             Dwt.setDisplay(oneDayPerPageContainer, Dwt.DISPLAY_NONE);
286 
287             this._setSelectedDateEnabled(false);
288             break;
289 
290         case ZmOperation.MONTH_VIEW:
291             Dwt.setDisplay(workDaysOnlyContainer, Dwt.DISPLAY_BLOCK);
292             Dwt.setDisplay(oneWeekPerPageContainer, Dwt.DISPLAY_NONE);
293             Dwt.setDisplay(oneDayPerPageContainer, Dwt.DISPLAY_NONE);
294             Dwt.setDisplay(hoursContainer, Dwt.DISPLAY_NONE);
295 
296             this._setSelectedDateEnabled(false);
297             break;
298 
299         case ZmOperation.CAL_LIST_VIEW:
300             Dwt.setDisplay(workDaysOnlyContainer, Dwt.DISPLAY_NONE);
301             Dwt.setDisplay(oneWeekPerPageContainer, Dwt.DISPLAY_NONE);
302             Dwt.setDisplay(oneDayPerPageContainer, Dwt.DISPLAY_NONE);
303             Dwt.setDisplay(hoursContainer, Dwt.DISPLAY_NONE);
304 
305             this._setSelectedDateEnabled(false);
306             break;
307     }
308 };
309 
310 
311 ZmCalPrintDialog.prototype._printCancelButtonListener =
312 function() {
313     this.popdown();
314 };
315 
316 ZmCalPrintDialog.prototype._printButtonListener =
317 function() {
318     if(!this._validateDateTime()) {
319         return false;
320     }
321     var url = this._getPrintOptions();
322     this.popdown();
323     window.open(url, "_blank");
324 };
325 
326 ZmCalPrintDialog.prototype.popdown =
327 function() {
328     Dwt.setDisplay(this._printErrorMsgContainer, Dwt.DISPLAY_NONE);
329     this._resetCheckboxes(false);
330     DwtDialog.prototype.popdown.call(this);
331 };
332 
333 ZmCalPrintDialog.prototype._resetCheckboxes =
334 function(value) {
335     document.getElementById(this._htmlElId + "_workDaysOnly").checked = value;
336     document.getElementById(this._htmlElId + "_oneWeekPerPage").checked = value;
337     document.getElementById(this._htmlElId + "_oneDayPerPage").checked = value;
338     document.getElementById(this._htmlElId + "_includeMiniCal").checked = value;
339 };
340 
341 ZmCalPrintDialog.prototype._getPrintViewName =
342 function(view) {
343     var viewStyle;
344     switch (view) {
345         case ZmId.VIEW_CAL_DAY: 		viewStyle = "day"; break;
346         case ZmId.VIEW_CAL_WORK_WEEK:	viewStyle = "workWeek"; break;
347         case ZmId.VIEW_CAL_WEEK:		viewStyle = "week"; break;
348         case ZmId.VIEW_CAL_LIST:	    viewStyle = "list"; break;
349         default:						viewStyle = "month"; break;				// default is month
350     }
351     return viewStyle;
352 };
353 
354 ZmCalPrintDialog.prototype._getPrintOptions =
355 function() {
356     var cals,
357         calIds = [],
358         treeView,
359         i=0,
360         j=0,
361         params = [],
362         printURL = "",
363         selDate = this._selDate.getEnabled() ? this._selDate.getValue() : "",
364         dateRangeFrom = this._dateRangeFrom.getEnabled() ? this._dateRangeFrom.getValue() : new Date(selDate.getTime()),
365         dateRangeTo = this._dateRangeTo.getEnabled() ? this._dateRangeTo.getValue() : new Date(selDate.getTime()),
366         fromTime = AjxDateFormat.format(ZmCalPrintDialog.TIME_FORMAT, this._fromTimeSelect.getValue()),
367         toTime = AjxDateFormat.format(ZmCalPrintDialog.TIME_FORMAT, this._toTimeSelect.getValue()),
368         viewSelected = ZmCalViewController.OP_TO_VIEW[this._viewSelect.getValue()],
369         viewStyle = this._getPrintViewName(viewSelected),
370         workDaysOnly = document.getElementById(this._htmlElId + "_workDaysOnly").checked,
371         oneWeekPerPage = document.getElementById(this._htmlElId + "_oneWeekPerPage").checked,
372         oneDayPerPage = document.getElementById(this._htmlElId + "_oneDayPerPage").checked,
373         includeMiniCal = document.getElementById(this._htmlElId + "_includeMiniCal").checked,
374 
375     //Create the string and pass it to the URL
376     treeView = this._opc.getOverview(this._curOverviewId).getTreeView(ZmOrganizer.CALENDAR);
377     cals = treeView.getSelected();
378 
379     for(j=0; j<cals.length; j++) {
380         calIds.push(cals[j].id);
381     }
382 
383     if(viewSelected == ZmId.VIEW_CAL_MONTH) {
384         var endMonthDate = AjxDateUtil._daysPerMonth[dateRangeTo.getMonth()];
385         if (dateRangeTo.getMonth() == '1' && !AjxDateUtil.isLeapYear(dateRangeTo.getYear())) {
386             //By default,_daysPerMonth sets number of days for Feb as 29.
387             //If it's not a leap year, set it to 28.
388             endMonthDate = '28';
389         }
390         dateRangeTo.setDate(endMonthDate);
391         dateRangeFrom.setDate(1);
392     }
393     else if(viewSelected == ZmId.VIEW_CAL_WEEK ||
394             viewSelected == ZmId.VIEW_CAL_WORK_WEEK) {
395         var fdow = appCtxt.get(ZmSetting.CAL_FIRST_DAY_OF_WEEK) || 0;
396         dateRangeFrom = AjxDateUtil.getFirstDayOfWeek(dateRangeFrom, fdow);
397         dateRangeTo = AjxDateUtil.getLastDayOfWeek(dateRangeTo, fdow);
398     }
399 
400     dateRangeTo.setHours(23, 59, 59, 999);
401     dateRangeFrom = AjxDateFormat.format(ZmCalPrintDialog.DATE_FORMAT, dateRangeFrom);
402     dateRangeTo = AjxDateFormat.format(ZmCalPrintDialog.DATE_FORMAT, dateRangeTo);
403 
404     params[i++] = "/h/printcalendar?";
405     params[i++] = "l=";
406     params[i++] = calIds.join(',');
407     params[i++] = "&origView=";
408     params[i++] = this._getPrintViewName(this.currentViewId);
409     params[i++] = "&view=";
410     params[i++] = viewStyle;
411     params[i++] = "&date=";
412     params[i++] = dateRangeFrom;
413     params[i++] = "&endDate=";
414     params[i++] = dateRangeTo;
415     params[i++] = "&ft=";
416     params[i++] = fromTime;
417     params[i++] = "&tt=";
418     params[i++] = toTime;
419     params[i++] = "&wd=";
420     params[i++] = workDaysOnly;
421     params[i++] = "&ow=";
422     params[i++] = oneWeekPerPage;
423     params[i++] = "&od=";
424     params[i++] = oneDayPerPage;
425     params[i++] = "&imc=";
426     params[i++] = includeMiniCal;
427     params[i++] = "&wdays=";
428     params[i++] = workDaysOnly ? ZmCalPrintDialog.encodeWorkingDays() : "";
429     params[i++] = "&tz=";
430     params[i++] = AjxTimezone.getServerId(AjxTimezone.DEFAULT);
431     params[i++] = "&skin=";
432     params[i++] = appCurrentSkin;
433 
434     printURL = appContextPath + params.join("");
435     return printURL;
436 };
437 
438 ZmCalPrintDialog.encodeWorkingDays = function () {
439     var wHrs = ZmCalBaseView.parseWorkingHours(ZmCalBaseView.getWorkingHours()),
440         wDays = [];
441     for (var i=0; i<wHrs.length; i++) {
442         if(wHrs[i].isWorkingDay) {
443             wDays.push(i);
444         }
445     }
446     return wDays.join(",");
447 };
448 
449 
450 ZmDateInput = function(parent, id, parentElement) {
451 	if (arguments.length == 0) return;
452 	DwtComposite.call(this, {parent:parent, className:"ZmDateInput", parentElement: parentElement});
453     this.id = id || Dwt.getNextId();
454     this._setHtml(id);
455 };
456 
457 ZmDateInput.prototype = new DwtComposite;
458 ZmDateInput.prototype.constructor = ZmDateInput;
459 
460 ZmDateInput.prototype.getValue =
461 function() {
462     var date = "";
463     if(this.getEnabled()) {
464         date = AjxDateUtil.simpleParseDateStr(this._dateInputField.getValue());
465         //date.setHours(23, 59, 59, 999);
466     }
467     return date;
468 };
469 
470 ZmDateInput.prototype.getTimeValue =
471 function() {
472     var dateObj = this.getEnabled() && AjxDateUtil.simpleParseDateStr(this._dateInputField.getValue());
473     return dateObj ? dateObj.getTime() : null;
474 };
475 
476 ZmDateInput.prototype.setValue =
477 function(date) {
478     this._dateInputField.setValue(AjxDateUtil.simpleComputeDateStr(date));
479 };
480 
481 ZmDateInput.prototype.setEnabled =
482 function(enabled) {
483     this._dateInputField.setEnabled(enabled);
484     this._dateButton.setEnabled(enabled);
485 };
486 
487 ZmDateInput.prototype.getEnabled =
488 function(enabled) {
489     return this._dateInputField.getEnabled() && this._dateButton.getEnabled();
490 };
491 
492 
493 // Private / protected methods
494 
495 ZmDateInput.prototype._setHtml =
496 function(id) {
497     var dateButtonListener = new AjxListener(this, this._dateButtonListener),
498 	    dateCalSelectionListener = new AjxListener(this, this._dateCalSelectionListener);
499 
500     this.getHtmlElement().innerHTML = AjxTemplate.expand("calendar.Appointment#ApptTimeInput", {id: this._htmlElId});
501     this._dateButton = ZmCalendarApp.createMiniCalButton(this.parent, this._htmlElId + "_timeSelectBtn", dateButtonListener, dateCalSelectionListener);
502     this._dateButton.setSize("20");
503 
504     //create time select input field
505     var params = {
506         parent: this,
507         parentElement: (this._htmlElId + "_timeSelectInput"),
508         type: DwtInputField.STRING,
509         errorIconStyle: DwtInputField.ERROR_ICON_NONE,
510         validationStyle: DwtInputField.CONTINUAL_VALIDATION,
511         inputId: id
512     };
513 
514     this._dateInputField = new DwtInputField(params);
515     var timeInputEl = this._dateInputField.getInputElement();
516     Dwt.setSize(timeInputEl, "80px", "2rem");
517     timeInputEl.typeId = this.id;
518 };
519 
520 ZmDateInput.prototype._dateButtonListener =
521 function(ev) {
522     var cal,
523         menu,
524         calDate = AjxDateUtil.simpleParseDateStr(this._dateInputField.getValue());
525 
526 	// if date was input by user and its foobar, reset to today's date
527 	if (isNaN(calDate)) {
528 		calDate = new Date();
529 		this._dateInputField.setValue(AjxDateUtil.simpleComputeDateStr(calDate));
530 	}
531 
532 	// always reset the date to current field's date
533 	menu = ev.item.getMenu();
534 	cal = menu.getItem(0);
535 	cal.setDate(calDate, true);
536 	ev.item.popup();
537 };
538 
539 ZmDateInput.prototype._dateCalSelectionListener =
540 function(ev) {
541 	var newDate = AjxDateUtil.simpleComputeDateStr(ev.detail);
542     this._dateInputField.setValue(newDate);
543 
544 };
545