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  * @overview
 26  * This file contains a dialog.
 27  * 
 28  */
 29 
 30 /**
 31  * Creates a dialog.
 32  * @class
 33  * This class is a base class for miscellaneous organizer-related dialogs. An instance
 34  * of this class can be re-used to show different overviews.
 35  *
 36  * @author Conrad Damon
 37  *
 38  * @param {Hash}	params		a hash of parameters
 39  * @param	{DwtControl}	params.parent		the parent widget
 40  * @param	{DwtMsgDialog}	params.msgDialog		the message dialog
 41  * @param	{String}	params.className		the CSS class name
 42  * @param	{String}	params.title		the dialog title
 43  * @param	{Array|constant}	params.standardButtons		an array of standard buttons to include. Defaults to {@link DwtDialog.OK_BUTTON} and {@link DwtDialog.CANCEL_BUTTON}.
 44  * @param	{Array}	params.extraButtons		a list of {@link DwtDialog_ButtonDescriptor} objects describing custom buttons to add to the dialog
 45  * @param	{DwtControl}	params.view				the dialog contents
 46  * 
 47  * @extends	DwtDialog
 48  */
 49 ZmDialog = function(params) {
 50 	if (arguments.length == 0) { return; }
 51 
 52 	DwtDialog.call(this, params);
 53 
 54 	if (!params.view) {
 55 		this.setContent(this._contentHtml());
 56 	}
 57 
 58 	if (this._button[DwtDialog.OK_BUTTON]) {
 59 		this.setButtonListener(DwtDialog.OK_BUTTON, new AjxListener(this, this._okButtonListener));
 60 	}
 61 
 62 	this._overview = {};
 63 	this._opc = appCtxt.getOverviewController();
 64 
 65 	this._baseTabGroupSize = this._tabGroup.size();
 66 };
 67 
 68 ZmDialog.prototype = new DwtDialog;
 69 ZmDialog.prototype.constructor = ZmDialog;
 70 
 71 ZmDialog.prototype.isZmDialog = true;
 72 ZmDialog.prototype.toString = function() { return "ZmDialog"; };
 73 
 74 /**
 75  * @private
 76  */
 77 ZmDialog.prototype._contentHtml =
 78 function() {
 79 	return "";
 80 };
 81 
 82 /**
 83  * Sets the view for this dialog.
 84  * 
 85  * @param	{DwtComposite}		newView		the view
 86  * @param	{Boolean}		noReset		if <code>true</code>, do not reset the dialog; <code>false</code> otherwise
 87  */
 88 ZmDialog.prototype.setView =
 89 function(newView, noReset) {
 90 	this.reset();
 91 	if (newView) {
 92         var contentDiv = this._getContentDiv();
 93         var el = newView.getHtmlElement();
 94 		contentDiv.parentNode.replaceChild(el, contentDiv);
 95 		this._contentDiv = el;
 96 	}
 97 };
 98 
 99 ZmDialog.prototype.popup =
100 function() {
101 	// Bug 38281: for multiuse dialogs, we need to re-add the discretionary
102 	// tab stops to the base list (the dialog buttons)
103 	this._tabGroup.__members._array.splice(this._baseTabGroupSize);
104 	this._tabGroup.addMember(this._getTabGroupMembers());
105 
106 	DwtDialog.prototype.popup.call(this);
107 	if (this._focusElement) {
108 		appCtxt.getKeyboardMgr().grabFocus(this._focusElement);	
109 	}
110 };
111 
112 ZmDialog.prototype.reset =
113 function() {
114 	if (this._nameField) {
115 		this._nameField.value = "";
116 	}
117 	DwtDialog.prototype.reset.call(this);
118 };
119 
120 /**
121  * @private
122  */
123 ZmDialog.prototype._okButtonListener =
124 function() {
125 	this.popdown();
126 };
127 
128 /**
129  * @private
130  */
131 ZmDialog.prototype._setNameField =
132 function(fieldId) {
133 	this._nameField = this._focusElement = document.getElementById(fieldId);
134 	if (this._enterListener) {
135 		this.addEnterListener(new AjxListener(this, this._enterListener));
136 	}
137 };
138 
139 /**
140  * Displays the given list of tree views in an overview, creating it if
141  * necessary, and appends the overview to an element in the dialog. Since
142  * dialogs may be reused, it is possible that it will display different
143  * overviews. That is handled by making sure that only the current overview is
144  * visible.
145  * 
146  * @param params		[hash]				hash of params:
147  *        treeIds		[array]				list of tree views to show
148  *        omit			[hash]				IDs of organizers to exclude
149  *        fieldId		[string]			DOM ID of element that contains overview
150  *        overviewId	[string]*			ID for the overview
151  *        noRootSelect	[boolean]*			if true, don't make root tree item(s) selectable
152  * @param forceSingle	[boolean]*			if true, don't make multi-account overviews
153  * @private
154  */
155 ZmDialog.prototype._setOverview =
156 function(params, forceSingle) {
157 
158 	// when in multi-account mode, hide the old overview since we don't know
159 	// whether we want to show an overview container or just a single-account overview.
160 	if (appCtxt.multiAccounts) {
161 		var oldOverview = this._opc.getOverviewContainer(this._curOverviewId) ||
162 						  this._opc.getOverview(this._curOverviewId);
163 
164 		if (oldOverview) {
165 			oldOverview.setVisible(false);
166 		}
167 	}
168 
169 	// multi-account uses overview container
170 	if (appCtxt.multiAccounts && !forceSingle) {
171 		// use overviewId as the containerId; container will assign overviewId's
172 		var containerId = this._curOverviewId = params.overviewId;
173 		var ovContainer = this._opc.getOverviewContainer(containerId);
174 		if (!ovContainer) {
175 			var overviewParams = {
176 				overviewClass:	"dialogOverviewContainer",
177 				headerClass:	"DwtTreeItem",
178 				noTooltips:		true,
179 				treeStyle:		params.treeStyle,
180 				treeIds:		params.treeIds,
181 				overviewTrees:	params.overviewTrees,
182 				omit:			params.omit,
183 				omitPerAcct:	params.omitPerAcct,
184 				selectable:		params.selectable
185 			};
186 			var containerParams = {
187 				appName: params.appName,
188 				containerId: containerId
189 			};
190 			ovContainer = this._opc.createOverviewContainer(containerParams, overviewParams);
191 			ovContainer.setSize(Dwt.DEFAULT, "200");
192 			document.getElementById(params.fieldId).appendChild(ovContainer.getHtmlElement());
193 		}
194 
195 		// make overview container visible
196 		ovContainer.setVisible(true);
197 
198 		return ovContainer;
199 	}
200 
201 	// single-account overview handling
202 	var overviewId = this._curOverviewId = params.overviewId;
203 	var overview = this._opc.getOverview(overviewId);
204 	if (!overview) {
205 		var ovParams = {
206 			overviewId:		overviewId,
207 			overviewClass:	params.overviewClass || "dialogOverview",
208 			headerClass:	"DwtTreeItem",
209 			noTooltips:		true,
210 			treeStyle:		params.treeStyle,
211 			dynamicWidth:	params.dynamicWidth,
212 			treeIds:		params.treeIds,
213 			account:		((appCtxt.multiAccounts && params.forceSingle) ? appCtxt.getActiveAccount() : (params.account || appCtxt.getActiveAccount())),
214 			skipImplicit: 	true,
215 			appName:        params.appName
216 		};
217 		overview = this._overview[overviewId] = this._opc.createOverview(ovParams);
218 		this._renderOverview(overview, params.treeIds, params.omit, params.noRootSelect);
219 		document.getElementById(params.fieldId).appendChild(overview.getHtmlElement());
220 	}
221 	else {
222 		//this might change between clients so have to update this.
223 		this._setRootSelection(overview, params.treeIds, params.noRootSelect);
224 	}
225 
226 	this._makeOverviewVisible(overviewId);
227 
228 	return overview;
229 };
230 
231 /**
232  * @private
233  */
234 ZmDialog.prototype._makeOverviewVisible =
235 function(overviewId) {
236 	for (var id in this._overview) {
237 		this._overview[id].setVisible(id == overviewId);
238 	}
239 };
240 
241 /**
242  * Renders the tree views in the overview, and makes the header items
243  * selectable (since they can generally be targets of whatever action the dialog
244  * is facilitating).
245  * 
246  * @param overview		[ZmOverview]		the overview
247  * @param treeIds		[array]				list of tree views to show
248  * @param omit			[hash]*				IDs of organizers to exclude
249  * @param noRootSelect	[boolean]*			if true, don't make root tree item(s) selectable
250  * @private
251  */
252 ZmDialog.prototype._renderOverview =
253 function(overview, treeIds, omit, noRootSelect) {
254 	overview.set(treeIds, omit);
255 	this._setRootSelection(overview, treeIds, noRootSelect);
256 };
257 
258 ZmDialog.prototype._setRootSelection =
259 function(overview, treeIds, noRootSelect) {
260 	for (var i = 0; i < treeIds.length; i++) {
261 		var treeView = overview.getTreeView(treeIds[i]);
262 		var hi = treeView && treeView.getHeaderItem();
263 		if (hi) {
264 			hi.enableSelection(!noRootSelect);
265 		}
266 	}
267 };
268 
269 
270 /**
271  * @private
272  */
273 ZmDialog.prototype._getOverview =
274 function() {
275 	return this._overview[this._curOverviewId];
276 };
277 
278 /**
279  * @private
280  */
281 ZmDialog.prototype._getInputFields =
282 function() {
283 	return (this._nameField) ? [this._nameField] : null;
284 };
285 
286 /**
287  * @private
288  */
289 ZmDialog.prototype._showError =
290 function(msg, loc) {
291 	var nLoc = loc || (new DwtPoint(this.getLocation().x + 50, this.getLocation().y + 100));
292 	var msgDialog = appCtxt.getMsgDialog();
293 
294 	msgDialog.reset();
295     msgDialog.setMessage(AjxStringUtil.htmlEncode(msg), DwtMessageDialog.CRITICAL_STYLE);
296 	msgDialog.popup(nLoc);
297 };
298 
299 /**
300  * @private
301  */
302 ZmDialog.prototype._getTabGroupMembers =
303 function() {
304 	return this._nameField ? [ this._nameField ] : [];
305 };
306