1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 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) 2011, 2012, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * @overview
 26  */
 27 
 28 /**
 29  * Creates an empty contact menu.
 30  * @class
 31  * This class represents a menu structure of contact groups that to which
 32  * contacts can be added. It also provides ability for user to create a new
 33  * contact group.
 34  *
 35  * @param {DwtControl}	parent		the parent widget
 36  * @param {ZmController}	controller	the owning controller
 37  * 
 38  * @extends		ZmPopupMenu
 39  */
 40 ZmContactGroupMenu = function(parent, controller) {
 41 
 42 	// create a menu (though we don't put anything in it yet) so that parent widget shows it has one
 43 	ZmPopupMenu.call(this, parent, null, parent.getHTMLElId() + "|GROUP_MENU", controller);
 44 
 45 	parent.setMenu(this);
 46 	this._addHash = {};
 47 	this._removeHash = {};
 48 	this._evtMgr = new AjxEventMgr();
 49 	this._desiredState = true;
 50 	this._items = null;
 51 	this._dirty = true;
 52 
 53 	// Use a delay to make sure our slow popup operation isn't called when someone
 54 	// is just rolling over a menu item to get somewhere else.
 55 	if (parent instanceof DwtMenuItem) {
 56 		parent.setHoverDelay(ZmContactGroupMenu._HOVER_TIME);
 57 	}
 58 };
 59 
 60 ZmContactGroupMenu.prototype = new ZmPopupMenu;
 61 ZmContactGroupMenu.prototype.constructor = ZmContactGroupMenu;
 62 
 63 ZmContactGroupMenu.KEY_GROUP_EVENT		= "_contactGroupEvent_";
 64 ZmContactGroupMenu.KEY_GROUP_ADDED		= "_contactGroupAdded_";
 65 ZmContactGroupMenu.MENU_ITEM_ADD_ID	    = "group_add";
 66 
 67 ZmContactGroupMenu._HOVER_TIME = 200;
 68 
 69 ZmContactGroupMenu.prototype.toString =
 70 function() {
 71 	return "ZmContactGroupMenu";
 72 };
 73 
 74 ZmContactGroupMenu.prototype.addSelectionListener = 
 75 function(listener) {
 76 	this._evtMgr.addListener(DwtEvent.SELECTION, listener);
 77 };
 78 
 79 ZmContactGroupMenu.prototype.removeSelectionListener = 
 80 function(listener) {
 81 	this._evtMgr.removeListener(DwtEvent.SELECTION, listener);    	
 82 };
 83 
 84 ZmContactGroupMenu.prototype.setEnabled =
 85 function(enabled) {
 86 	this._desiredState = enabled;
 87 	if (enabled && !this._contactGroupList) { return; }
 88 
 89 	this.parent.setEnabled(enabled);
 90 };
 91 
 92 // Dynamically set the list of contact groups that can be added/removed based on the given list of items.
 93 ZmContactGroupMenu.prototype.set =
 94 function(items, contactGroupList, newDisabled) {
 95 	DBG.println(AjxDebug.DBG3, "set contact group menu");
 96 	this._contactGroupList = contactGroupList;
 97 	this._items = items;
 98 	this._dirty = true;
 99 	this._newDisabled = newDisabled;
100 
101 	this.parent.setEnabled(true);
102 
103 	// Turn on the hover delay.
104 	if (this.parent instanceof DwtMenuItem) {
105 		this.parent.setHoverDelay(ZmContactGroupMenu._HOVER_TIME);
106 	}
107 };
108 
109 ZmContactGroupMenu.prototype._doPopup =
110 function(x, y, kbGenerated) {
111 	if (this._dirty) {
112 		// reset the menu
113 		this.removeChildren();
114 
115 		if (this._contactGroupList) {
116 			var groupNames = [];
117 			for (var i=0; i<this._contactGroupList.length; i++) {
118 				var contact = ZmContact.getContactFromCache(this._contactGroupList[i].id);
119 				if (contact && !ZmContact.isInTrash(contact)) {
120 					groupNames.push(ZmContact.getAttr(this._contactGroupList[i], "nickname"));
121 				}
122 				else {
123 					this._contactGroupList[i] = {id: false};
124 				}
125 			}
126 			this._render(groupNames);
127 		}
128 		this._dirty = false;
129 
130 		// Remove the hover delay to prevent flicker when mousing around.
131 		if (this.parent instanceof DwtMenuItem) {
132 			this.parent.setHoverDelay(0);
133 		}
134 	}
135 	ZmPopupMenu.prototype._doPopup.call(this, x, y, kbGenerated);
136 };
137 
138 ZmContactGroupMenu.prototype._render =
139 function(groupNames, addRemove) {
140 
141 	for (var i = 0; i < this._contactGroupList.length; i++) {
142 		this._addNewGroup(this, this._contactGroupList[i], true, null, this._addHash);
143 	}
144 
145 	if (this._contactGroupList.length) {
146 		new DwtMenuItem({parent:this, style:DwtMenuItem.SEPARATOR_STYLE});
147 	}
148 
149 	// add static "New Contact Group" menu item
150 	var miNew = this._menuItems[ZmContactGroupMenu.MENU_ITEM_ADD_ID] = new DwtMenuItem({parent:this, id: this._htmlElId + "|NEWGROUP"});
151 	miNew.setText(AjxStringUtil.htmlEncode(ZmMsg.newGroup));
152 	miNew.setImage("NewGroup");
153 	if (this._newDisabled) {
154 		miNew.setEnabled(false);
155 	}
156 	else {
157 		miNew.setData(ZmContactGroupMenu.KEY_GROUP_EVENT, ZmEvent.E_CREATE);
158 		miNew.addSelectionListener(this._menuItemSelectionListener.bind(this), 0);
159 	}
160 };
161 
162 ZmContactGroupMenu.groupNameLength = 20;
163 ZmContactGroupMenu.prototype._addNewGroup =
164 function(menu, group, add, index, groupHash) {
165 	var nickName = ZmContact.getAttr(group, "nickname") || group.fileAsStr;
166 	if (nickName) {
167 		var mi = new DwtMenuItem({parent:menu, index:index});
168 		var groupName = AjxStringUtil.clipByLength(nickName, ZmContactGroupMenu.groupNameLength);
169 		mi.setText(groupName);
170 		mi.setImage("Group");
171 		mi.addSelectionListener(this._menuItemSelectionListener.bind(this), 0);
172 		mi.setData(ZmContactGroupMenu.KEY_GROUP_EVENT, ZmEvent.E_MODIFY);
173 		mi.setData(ZmContactGroupMenu.KEY_GROUP_ADDED, add);
174 		mi.setData(Dwt.KEY_OBJECT, group);
175 	}
176 };
177 
178 ZmContactGroupMenu.prototype._menuItemSelectionListener =
179 function(ev) {
180 	// Only notify if the node is one of our nodes
181 	if (ev.item.getData(ZmContactGroupMenu.KEY_GROUP_EVENT)) {
182 		this._evtMgr.notifyListeners(DwtEvent.SELECTION, ev.item);
183 	}
184 };
185 
186 /**
187  * Sets "New Contact Group" to be enabled/disabled
188  * @param {Boolean} disabled true to set disabled
189  */
190 ZmContactGroupMenu.prototype.setNewDisabled = 
191 function(disabled) {
192 	this._newDisabled = disabled;	
193 };