1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2004, 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) 2004, 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  */
 27 
 28 /**
 29  * Creates a toolbar with the given buttons.
 30  * @class
 31  * This class represents a toolbar that contains buttons.
 32  * It can be easily created using a set of standard operations, and/or custom buttons
 33  * can be provided. This class is designed for use with items ({@link ZmItem}), so it can for
 34  * example contain a button with a tab submenu. See also {@link ZmActionMenu}.
 35  *
 36  * @author Conrad Damon
 37  *
 38  * @param {Hash}	params			a hash of parameters
 39  * @param	       {DwtComposite}	params.parent		the containing widget
 40  * @param	{Array}	params.buttons			a list of operation IDs
 41  * @param	{constant}	params.posStyle			the positioning style
 42  * @param	{String}	params.className			the CSS class name
 43  * @param	{Stirng}	params.buttonClassName	the CSS class name for buttons
 44  * @param	{Hash}	params.overrides			a hash of overrides by op ID
 45  * @param	{Array}	params.secondaryButtons		a list of operation IDs
 46  * @param	{Array}	params.rightSideButtons		a list of operation IDs
 47  * @param	{constant}	params.context			the vcontextID (used to generate button IDs)
 48  * @param	{constant}	params.toolbarType		the toolbar type (used to generate button IDs)
 49  * @param	{Boolean}	params.addTextElement		if true, add a text "button" (element) at the end (but before the view button, if any). This can be used for message counts etc
 50  * @param	{ZmController}	params.controller		the owning controller
 51  *
 52  * @extends		ZmToolBar
 53  */
 54 ZmButtonToolBar = function(params) {
 55 	if (arguments.length == 0) return;
 56 
 57 	if (!params.className && (params.controller && params.controller._elementsToHide == ZmAppViewMgr.LEFT_NAV)) {
 58 		params.className = "ZToolbar itemToolbar";
 59 	}
 60     params.className = params.className || "ZToolbar";
 61     params.id = params.context ? ZmId.getToolbarId(params.context, params.toolbarType) : null;
 62     ZmToolBar.call(this, params);
 63 	
 64 	this._context = params.context;
 65 	this._toolbarType = params.toolbarType;
 66 	this._buttonStyle = params.buttonClassName;
 67 
 68 	// standard buttons default to New/Tag/Print/Delete
 69 	var buttonOps = params.buttons;
 70 	if (!buttonOps) {
 71 		buttonOps = [ZmOperation.NEW_MENU, ZmOperation.TAG_MENU, ZmOperation.PRINT, ZmOperation.DELETE];
 72 	} else if (buttonOps == ZmOperation.NONE) {
 73 		buttonOps = null;
 74 	}
 75 	// weed out disabled ops, save list of ones that make it
 76 	/**
 77 	 * The operation list property.
 78 	 * @type Array
 79 	 */
 80 	this.opList = ZmOperation.filterOperations(buttonOps);
 81 
 82 	this._zimletButtonLocation = this.opList.length;
 83 
 84 	var secondaryOpList = ZmOperation.filterOperations(params.secondaryButtons);
 85 
 86 	if (secondaryOpList && secondaryOpList.length) {
 87 		this.opList.push(ZmOperation.SEP, ZmOperation.ACTIONS_MENU);
 88 	}
 89 
 90 	var rightSideOpList = ZmOperation.filterOperations(params.rightSideButtons);
 91 
 92 	if (rightSideOpList.length > 0 || params.addTextElement) {
 93 		this.opList.push(ZmOperation.FILLER);
 94 	}
 95 	
 96 	if (params.addTextElement) {
 97 		this.opList.push(ZmOperation.TEXT);
 98 	}
 99 
100 	if (rightSideOpList.length > 0) {
101 		this.opList = this.opList.concat(rightSideOpList);
102 	}
103 
104 	this._buttons = ZmOperation.createOperations(this, this.opList, params.overrides);
105 
106 	if (secondaryOpList && secondaryOpList.length) {
107 		var actionsButton =  this._secondaryButton = this.getButton(ZmOperation.ACTIONS_MENU);
108 
109 		actionsButton.noMenuBar = true;
110 
111 		var secondaryMenu = this._secondaryButtonMenu = new ZmActionMenu({parent: actionsButton, menuItems: ZmOperation.NONE, context: this._context, controller: params.controller});
112 		var secondaryButtons  = ZmOperation.createOperations(secondaryMenu, secondaryOpList, params.overrides);
113 		actionsButton.setMenu(secondaryMenu);
114 
115 		//add secondary buttons to buttons list as I believe from now on it shouldn't matter if they are primary or under the secondary "actions" menu.
116 		//that way we don't need to operate on 2 different collections when enabling/disabling, adding listeners, etc.
117 		//var secondaryButtons = secondaryMenu._menuItems;
118 		for (var id in secondaryButtons) {
119 			this._buttons[id] = secondaryButtons[id];
120 		}
121 		//same as buttons, with opList.
122 		this.opList = this.opList.concat(secondaryOpList);
123 
124 	}
125 
126 	//todo - I guess in the new UI a button (primary) will have either text or image. not both. Think of whether this precedence is still required then.
127 	this._createPrecedenceList(); //this is only done to the primary, not the secondary buttons (since the secondary are in a drop-down so removing one's image or text won't make sense.)
128 	
129 	this._inited = true;
130 };
131 
132 ZmButtonToolBar.prototype = new ZmToolBar;
133 ZmButtonToolBar.prototype.constructor = ZmButtonToolBar;
134 
135 ZmButtonToolBar.prototype.isZmButtonToolBar = true;
136 ZmButtonToolBar.prototype.toString = function() { return "ZmButtonToolBar"; };
137 
138 
139 // Public methods
140 
141 
142 /**
143  * Creates a button and adds its operation ID as data.
144  * 
145  * @param {String}	id			the name of the operation
146  * @param {Hash}	params		a hash of parameters
147  * @param {String}	params.text			a button text
148  * @param {String}	params.tooltip		a button tooltip text
149  * @param {String}	params.image			a icon class for the button
150  * @param {String}	params.disImage		a disabled version of icon
151  * @param {Boolean}	params.enabled		if <code>true</code>, button is enabled
152  * @param {String}	params.className		the CSS class name
153  * @param {String}	params.style			thebutton style
154  * @param {int} params.index			the position at which to add the button
155  * @param {Boolean}	params.showImageInToolbar	if <code>true</code>, the button should show image (default is false)
156  * @param {Boolean}	params.showTextInToolbar	if <code>true</code>, the button should show text (default is !params.showImageInToolbar)
157  */
158 ZmButtonToolBar.prototype.createOp =
159 function(id, params) {
160 	params.className = this._buttonStyle;
161 	var b;
162 	if (id == ZmOperation.TEXT) {
163 		var id;
164 		if (this._context) {
165 			var context = this._toolbarType ? [this._context, this._toolbarType].join("_") : this._context;
166 			id = [ZmId.WIDGET, AjxStringUtil.toMixed(context, "_", true), AjxStringUtil.toMixed(id, "_")].join("");
167 		}
168 		params.textClassName = params.textClassName || "DwtText ZWidgetTitle";
169 		b = new DwtText({parent:this, className:params.textClassName, id:id});
170 	} else {
171 		params.id = params.domId || (this._context ? ZmId.getButtonId(this._context, id, this._toolbarType) : null);
172 		params.textPrecedence = ZmOperation.getProp(id, "textPrecedence");
173 		params.iconPrecedence = ZmOperation.getProp(id, "iconPrecedence");
174 		var showImage = params.showImageInToolbar || false; //default to false;
175 		var showText = !showImage || params.showTextInToolbar;
176 		showImage = showImage || !params.text; //no text? gotta show image
177 		showText = showText || !params.image; //no image? gotta show text
178 		params.image = showImage && params.image;
179 		params.whatToShow = {showImage: showImage, showText: showText}
180 		b = this.createButton(id, params);
181 	}
182 	b.setData(ZmOperation.KEY_ID, id);
183 
184 	return b;
185 };
186 
187 /**
188  * Creates a zimlet button and adds its operation ID as data. This method selects the best location for the zimlet, so zimlets don't have to do it and it's consistent.
189  *
190  * for parameters see createOp
191  */
192 ZmButtonToolBar.prototype.createZimletOp =
193 function(id, params) {
194 	params.index = this._zimletButtonLocation;
195 	return this.createOp(id, params);
196 };
197 
198 
199 /**
200  * Adds the operation.
201  * 
202  * @param	{String}	id		the id
203  * @param	{int}		index	the index
204  */
205 ZmButtonToolBar.prototype.addOp =
206 function(id, index) {
207 	if(this.getOp(id)) {
208 		return;
209 	}
210 	ZmOperation.addOperation(this, id, this._buttons, index);
211 	AjxUtil.arrayAdd(this.opList, id, index);
212 };
213 
214 /**
215  * Removes the operation.
216  * 
217  * @param	{String}	id		the id
218  * 
219  * @see ZmOperation
220  */
221 ZmButtonToolBar.prototype.removeOp =
222 function(id) {
223 	ZmOperation.removeOperation(this, id, this._buttons);
224 	AjxUtil.arrayRemove(this.opList, id);
225 };
226 
227 /**
228  * Gets the button.
229  *
230  * @param {constant}	id		the button
231  * @return	{DwtButton}	the button
232  * 
233  * @see ZmOperation
234  */
235 ZmButtonToolBar.prototype.getOp =
236 function(id) {
237 	return this.getButton(id);
238 };
239 
240 /**
241  * Gets the menu tag sub-menu (if any).
242  * 
243  * @return	{ZmTagMenu}		the menu
244  */
245 ZmButtonToolBar.prototype.getTagMenu =
246 function() {
247 	var button = this.getButton(ZmOperation.TAG_MENU);
248 	if (button) {
249 		return button.getMenu();
250 	}
251 };
252 
253 /**
254  * gets the secondary menu (the "Actions" menu in the toolbar)
255  */
256 ZmButtonToolBar.prototype.getActionsMenu =
257 function() {
258 	return this._secondaryButtonMenu;
259 };
260 
261 /**
262  * gets the secondary button (the "Actions" button in the toolbar)
263  */
264 ZmButtonToolBar.prototype.getActionsButton =
265 function() {
266 	return this._secondaryButton;
267 };
268 
269 
270 //
271 // Private methods
272 //
273 
274 // Returns the ID for the given button.
275 ZmButtonToolBar.prototype._buttonId =
276 function(button) {
277 	return button.getData(ZmOperation.KEY_ID);
278 };
279