1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2010, 2011, 2012, 2013, 2014, 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) 2010, 2011, 2012, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 /**
 24  * Creates a generic time picker dialog
 25  * @constructor
 26  * @class
 27  * 
 28  * @extends		ZmDialog
 29  * 
 30  */
 31 ZmTimeDialog = function(params) {
 32 	params.id = Dwt.getNextId("ZmTimeDialog_");
 33 	ZmDialog.call(this, params);
 34 	var html = AjxTemplate.expand("share.Dialogs#ZmTimeDialog", {id: this._htmlElId, description: ZmMsg.sendLaterDescription, label: ZmMsg.time});
 35 	this.setContent(html);
 36 	this.setTitle(ZmMsg.sendLaterTitle);
 37 	this._createDwtObjects();
 38 };
 39 
 40 ZmTimeDialog.prototype = new ZmDialog;
 41 ZmTimeDialog.prototype.constructor = ZmTimeDialog;
 42 
 43 // Public
 44 
 45 ZmTimeDialog.prototype.toString = 
 46 function() {
 47 	return "ZmTimeDialog";
 48 };
 49 
 50 ZmTimeDialog.prototype.initialize = 
 51 function() {
 52 	// Init Date / time picker
 53 	var now = new Date();
 54 	this._dateField.value = AjxDateUtil.simpleComputeDateStr(now);
 55 	this.showTimeFields(true);
 56 	this._timeSelect.set(now);
 57 
 58 	// Init Timezone picker
 59 	var options = AjxTimezone.getAbbreviatedZoneChoices();
 60 	var serverIdMap = {};
 61 	var serverId;
 62 	if (options.length != this._tzCount) {
 63 		this._tzCount = options.length;
 64 		this._tzoneSelect.clearOptions();
 65 		for (var i = 0; i < options.length; i++) {
 66 			if (!options[i].autoDetected) {
 67 				serverId = options[i].value;
 68 				serverIdMap[serverId] = true;
 69 				this._tzoneSelect.addOption(options[i]);
 70 			}
 71 		}
 72 	}
 73 	this.autoSelectTimezone();
 74 };
 75 
 76 
 77 
 78 ZmTimeDialog.prototype.isValid = 
 79 function() {
 80 	return true;
 81 };
 82 
 83 ZmTimeDialog.prototype.isDirty = 
 84 function() {
 85 	return true;
 86 };
 87 
 88 ZmTimeDialog.prototype.getValue =
 89 function() {
 90 	var date = this._timeSelect.getValue(AjxDateUtil.simpleParseDateStr(this._dateField.value));
 91 	var timezone = this._tzoneSelect.getValue();
 92 	return {date: date, timezone: timezone};
 93 }
 94 
 95 ZmTimeDialog.prototype.isValidDateStr =
 96 function() {
 97     return AjxDateUtil.isValidSimpleDateStr(this._dateField.value);
 98 };
 99 
100 ZmTimeDialog.prototype.popup =
101 function() {
102 	this.initialize();
103 	ZmDialog.prototype.popup.call(this);
104 	if (!this._tabGroupComplete) {
105 		var members = [this._dateField, this._dateButton, this._timeSelect.getInputField(), this._tzoneSelect];
106 		for (var i = 0; i < members.length; i++) {
107 			this._tabGroup.addMember(members[i], i);
108 		}
109 		this._tabGroupComplete = true;
110 	}
111 	this._tabGroup.setFocusMember(this._dateField);
112 };
113 
114 // Private / protected methods
115 
116 ZmTimeDialog.prototype._createDwtObjects =
117 function() {
118 
119 	var dateButtonListener = new AjxListener(this, this._dateButtonListener);
120 	var dateCalSelectionListener = new AjxListener(this, this._dateCalSelectionListener);
121 
122 	this._dateButton = this.createMiniCalButton(this._htmlElId + "_miniCal", dateButtonListener, dateCalSelectionListener);
123 
124 	// create selects for Time section
125 	var timeSelectListener = new AjxListener(this, this._timeChangeListener);
126 	
127 	this._timeSelect = new DwtTimeInput(this, DwtTimeInput.START);
128 	this._timeSelect.addChangeListener(timeSelectListener);
129 	this._timeSelect.reparentHtmlElement(this._htmlElId + "_time");
130 
131 	this._dateField = Dwt.byId(this._htmlElId + "_date");
132 
133 	this._tzoneSelect = new DwtSelect({parent:this, parentElement: (this._htmlElId + "_tzSelect"), layout:DwtMenu.LAYOUT_SCROLL, maxRows: 7, id: Dwt.getNextId("TimeZoneSelect_")});
134 };
135 
136 ZmTimeDialog.prototype.showTimeFields = 
137 function(show) {
138 	Dwt.setVisibility(this._timeSelect.getHtmlElement(), show);
139 };
140 
141 // Listeners
142 
143 ZmTimeDialog.prototype._dateButtonListener = 
144 function(ev) {
145 	var calDate = AjxDateUtil.simpleParseDateStr(this._dateField.value);
146 
147 	// if date was input by user and its foobar, reset to today's date
148 	if (calDate === null || isNaN(calDate)) {
149 		calDate = new Date();
150 		var field = this._dateField;
151 		field.value = AjxDateUtil.simpleComputeDateStr(calDate);
152 	}
153 
154 	// always reset the date to current field's date
155 	var menu = ev.item.getMenu();
156 	var cal = menu.getItem(0);
157 	cal.setDate(calDate, true);
158 	ev.item.popup();
159 };
160 
161 ZmTimeDialog.prototype._dateCalSelectionListener = 
162 function(ev) {
163 	var parentButton = ev.item.parent.parent;
164 
165 	// do some error correction... maybe we can optimize this?
166 	var sd = AjxDateUtil.simpleParseDateStr(this._dateField.value);
167 	var newDate = AjxDateUtil.simpleComputeDateStr(ev.detail);
168 
169 	// change the start/end date if they mismatch
170 	if (parentButton == this._dateButton) {
171 		this._dateField.value = newDate;
172 	}
173 };
174 
175 ZmTimeDialog.prototype.createMiniCalButton =
176 function(buttonId, dateButtonListener, dateCalSelectionListener) {
177 	// create button
178 	var dateButton = new DwtButton({parent:this});
179 	dateButton.addDropDownSelectionListener(dateButtonListener);
180 	dateButton.setData(Dwt.KEY_ID, buttonId);
181 	if (AjxEnv.isIE) {
182 		dateButton.setSize("20");
183 	}
184 
185 	// create menu for button
186 	var calMenu = new DwtMenu({parent:dateButton, style:DwtMenu.CALENDAR_PICKER_STYLE});
187 	calMenu.setSize("150");
188 	calMenu._table.width = "100%";
189 	dateButton.setMenu(calMenu, true);
190 
191 	// create mini cal for menu for button
192 	var cal = new DwtCalendar({parent:calMenu});
193 	cal.setData(Dwt.KEY_ID, buttonId);
194 	cal.setSkipNotifyOnPage(true);
195 	var fdow = appCtxt.get(ZmSetting.CAL_FIRST_DAY_OF_WEEK) || 0;
196 	cal.setFirstDayOfWeek(fdow);
197 	cal.addSelectionListener(dateCalSelectionListener);
198 	// add settings change listener on mini cal in case first day of week setting changes
199 	// safety check since this is static code (may not have loaded calendar)
200 	var fdowSetting = appCtxt.getSettings().getSetting(ZmSetting.CAL_FIRST_DAY_OF_WEEK);
201 	if (fdowSetting) {
202 		var listener = new AjxListener(null, ZmCalendarApp._settingChangeListener, cal);
203 		fdowSetting.addChangeListener(listener);
204 	}
205 
206 	// reparent and cleanup
207 	dateButton.reparentHtmlElement(buttonId);
208 	delete buttonId;
209 
210 	return dateButton;
211 };
212 
213 ZmTimeDialog.prototype.autoSelectTimezone =
214 function() {
215 	if (AjxTimezone.DEFAULT_RULE.autoDetected) {
216 
217 		var cRule = AjxTimezone.DEFAULT_RULE;
218 		var standardOffsetMatch, daylightOffsetMatch, transMatch;
219 
220 		for (var i in AjxTimezone.MATCHING_RULES) {
221 			var rule = AjxTimezone.MATCHING_RULES[i];
222 			if (rule.autoDetected) continue;
223 			if (rule.standard.offset == cRule.standard.offset) {
224 
225 				if (!standardOffsetMatch)
226 					standardOffsetMatch = rule.serverId;
227 
228 				var isDayLightOffsetMatching = (cRule.daylight && rule.daylight && (rule.daylight.offset == cRule.daylight.offset));
229 
230 				if(isDayLightOffsetMatching) {
231 					if (!daylightOffsetMatch)
232 						daylightOffsetMatch = rule.serverId;
233 					var isTransYearMatching = (rule.daylight.trans[0] == cRule.daylight.trans[0]);
234 					var isTransMonthMatching = (rule.daylight.trans[1] == cRule.daylight.trans[1]);
235 					if (isTransYearMatching && isTransMonthMatching && !transMatch)
236 						transMatch = rule.serverId;
237 				}
238 			}
239 		}
240 		//select closest matching timezone
241 		var serverId = transMatch ? transMatch : (daylightOffsetMatch || standardOffsetMatch);
242 		if (serverId) this._tzoneSelect.setSelectedValue(serverId);
243 	} else {
244 		var tz = AjxTimezone.getServerId(AjxTimezone.DEFAULT);
245 		this._tzoneSelect.setSelectedValue(tz);
246 	}
247 };
248