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 /**
 25  * @param {hash} params Constructor parameters.
 26  * @param {DwtComposite} params.parent Parent control.
 27  * @param {string} params.image Item image to display next to each color choice.
 28  * @param {boolean} params.hideNone True to hide the "None" option.
 29  * @param {boolean} params.hideNoFill True to hide the no-fill/use-default option.
 30  */
 31 ZmColorMenu = function(params) {
 32     if (arguments.length == 0) return;
 33     params.className = params.className || "ZmColorMenu DwtMenu";
 34     DwtMenu.call(this, params);
 35     this._hideNone = params.hideNone;
 36     this._hideNoFill = params.hideNoFill;
 37     this.setImage(params.image);
 38     this._populateMenu();
 39 };
 40 ZmColorMenu.prototype = new DwtMenu;
 41 ZmColorMenu.prototype.constructor = ZmColorMenu;
 42 
 43 ZmColorMenu.prototype.toString = function() {
 44     return "ZmColorMenu";
 45 };
 46 
 47 //
 48 // Constants
 49 //
 50 
 51 ZmColorMenu.__KEY_COLOR = "color";
 52 
 53 //
 54 // Public methods
 55 //
 56 
 57 ZmColorMenu.prototype.setImage = function(image) {
 58     if (this._image != image) {
 59         this._image = image;
 60         var children = this.getChildren();
 61         for (var i = 0; i < children.length; i++) {
 62             var child = children[i];
 63             var color = child.getData(ZmColorMenu.__KEY_COLOR);
 64 			var displayColor = color || ZmOrganizer.COLOR_VALUES[ZmOrganizer.ORG_DEFAULT_COLOR]; //default to gray
 65             var icon = [image, displayColor].join(",color=");
 66             child.setImage(icon);
 67         }
 68     }
 69 };
 70 ZmColorMenu.prototype.getImage = function() {
 71     return this._image;
 72 };
 73 
 74 ZmColorMenu.prototype.getTextForColor = function(color) {
 75     color = String(color).toLowerCase();
 76     if (!color.match(/^#/)) color = ZmOrganizer.COLOR_VALUES[color];
 77     var children = this.getChildren();
 78     for (var i = 0; i < children.length; i++) {
 79         var mi = children[i];
 80         if (mi.getData(ZmColorMenu.__KEY_COLOR) == color) {
 81             return mi.getText();
 82         }
 83     }
 84     return ZmMsg.custom;
 85 };
 86 
 87 ZmColorMenu.prototype.showMoreColors = function() {
 88     if (this.parent && this.parent.getMenu() == this) {
 89         var menu = this.parent.getMenu();
 90         var moreMenu = this._getMoreColorMenu();
 91         this.parent.setMenu(moreMenu);
 92         if (menu.isPoppedUp()) {
 93             var loc = menu.getLocation();
 94             menu.popdown();
 95             moreMenu.popup(0, loc.x, loc.y);
 96         }
 97         else {
 98             this.parent.popup();
 99         }
100     }
101 };
102 
103 ZmColorMenu.prototype.showLessColors = function() {
104     if (this.parent && this.parent.getMenu() != this) {
105         var menu = this.parent.getMenu();
106         this.parent.setMenu(this);
107         if (menu.isPoppedUp()) {
108             var loc = menu.getLocation();
109             menu.popdown();
110             this.popup(0, loc.x, loc.y);
111         }
112         else {
113             this.parent.popup();
114         }
115     }
116 };
117 
118 //
119 // DwtMenu methods
120 //
121 
122 ZmColorMenu.prototype.addSelectionListener = function(listener) {
123     DwtMenu.prototype.addSelectionListener.apply(this, arguments);
124     this._getMoreColorMenu().addSelectionListener(listener);
125 };
126 ZmColorMenu.prototype.removeSelectionListener = function(listener) {
127     DwtMenu.prototype.removeSelectionListener.apply(this, arguments);
128     this._getMoreColorMenu().removeSelectionListener(listener);
129 };
130 
131 //
132 // Protected methods
133 //
134 
135 ZmColorMenu.prototype._populateMenu = function() {
136     var list = ZmOrganizer.COLOR_VALUES;
137     for (var id = 0; id < list.length; id++) {
138         var color = ZmOrganizer.COLOR_VALUES[id];
139         if (!color && this._hideNone) continue;
140 		var displayColor = color || ZmOrganizer.COLOR_VALUES[ZmOrganizer.ORG_DEFAULT_COLOR]; //default to gray
141         var image = this._image ? [this._image, displayColor].join(",color=") : null;
142         var text = ZmOrganizer.COLOR_TEXT[id];
143         var menuItemId = "COLOR_" + id;
144         var menuItem = new DwtMenuItem({parent:this, id:menuItemId});
145         menuItem.setImage(image);
146         menuItem.setText(text);
147         menuItem.setData(ZmOperation.MENUITEM_ID, id);
148         menuItem.setData(ZmColorMenu.__KEY_COLOR, color);
149     }
150     var callback = new AjxCallback(this, this.showMoreColors); 
151     var showMoreItem = new ZmColorMenuItem({parent:this,callback:callback, id:"SHOW_MORE_ITEMS"});
152     showMoreItem.setText(ZmMsg.colorsShowMore);
153 };
154 
155 ZmColorMenu.prototype._getMoreColorMenu = function() {
156     if (!this._moreMenu) {
157         var callback = new AjxCallback(this, this.showLessColors);
158         this._moreMenu = new ZmMoreColorMenu({parent:this.parent,callback:callback,hideNoFill:this._hideNoFill});
159     }
160     return this._moreMenu;
161 };
162 
163 //
164 // Classes
165 //
166 
167 ZmMoreColorMenu = function(params) {
168     params.style = DwtMenu.COLOR_PICKER_STYLE;
169     DwtMenu.call(this, params);
170     this._colorPicker = new DwtColorPicker({parent:this,hideNoFill:params.hideNoFill});
171     this._colorPicker.getData = this.__DwtColorPicker_getData; // HACK
172     var showLessItem = new ZmColorMenuItem({parent:this,callback:params.callback});
173     showLessItem.setText(ZmMsg.colorsShowLess);
174 };
175 ZmMoreColorMenu.prototype = new DwtMenu;
176 ZmMoreColorMenu.prototype.constructor = ZmMoreColorMenu;
177 
178 ZmMoreColorMenu.prototype.toString = function() {
179     return "ZmMoreColorMenu";
180 };
181 
182 /**
183  * <strong>Note:</strong>
184  * This method is run in the context of the color picker!
185  *
186  * @private
187  */
188 ZmMoreColorMenu.prototype.__DwtColorPicker_getData = function(key) {
189     // HACK: This is to fake the color picker as a menu item whose
190     // HACK: id is the selected color.
191     if (key == ZmOperation.MENUITEM_ID) {
192         return this.getInputColor();
193     }
194     return DwtColorPicker.prototype.getData.apply(this, arguments);
195 };
196 
197 ZmMoreColorMenu.prototype.setDefaultColor = function(color) {
198     this._colorPicker.setDefaultColor(color);
199 }
200 
201 /**
202  * A custom menu item class for the "More colors..." and
203  * "Fewer colors..." options which should not leave space for
204  * an image next to the text. A sub-class is also needed so
205  * that we can avoid the default handling of the item click.
206  *
207  * @param params
208  */
209 ZmColorMenuItem = function(params) {
210     DwtMenuItem.call(this, params);
211     this.callback = params.callback;
212     // HACK: This is needed because we no-op the add/removeSelectionListener
213     // HACK: methods so that external people can't register listeners but we
214     // HACK: still want to handle a true selection to call the callback.
215     DwtMenuItem.prototype.addSelectionListener.call(this, new AjxListener(this, this.__handleItemSelect));
216 };
217 ZmColorMenuItem.prototype = new DwtMenuItem;
218 ZmColorMenuItem.prototype.constructor = ZmColorMenuItem;
219 
220 ZmColorMenuItem.prototype.toString = function() {
221     return "ZmColorMenuItem";
222 };
223 
224 ZmColorMenuItem.prototype.TEMPLATE = "zimbra.Widgets#ZmColorMenuItem";
225 
226 // DwtMenuItem methods
227 
228 ZmColorMenuItem.prototype.addSelectionListener = function() {}; // no-op
229 ZmColorMenuItem.prototype.removeSelectionListener = function() {}; // no-op
230 
231 ZmColorMenuItem.prototype.__handleItemSelect = function() {
232     if (this.callback) {
233         this.callback.run();
234     }
235 };
236