1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 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) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 25 /** 26 * Creates a color picker displaying "Web safe" colors. 27 * @constructor 28 * @class 29 * Instances of this class may be 30 * used with {@link DwtMenu} to create a {@link DwtColorPicker} menu. Clicking on a color cell generates a 31 * DwtSelectionEvent the detail attribute of which contains the color string associated 32 * the cell on which the user clicked 33 * 34 * 35 * @author Ross Dargahi 36 * 37 * @param {hash} params a hash of parameters 38 * @param {DwtComposite} params.parent the parent widget 39 * @param {string} params.className a CSS class 40 * @param {constant} params.posStyle the positioning style 41 * @param {boolean} params.hideNoFill True to hide the no-fill/use-default option 42 * @param {string} params.noFillLabel the no-fill label 43 * @param {boolean} params.allowColorInput if <code>true</code>, allow a text field to allow user to input their customized RGB value 44 * @param {string} params.defaultColor Default color. 45 * 46 * @extends DwtControl 47 */ 48 DwtColorPicker = function(params) { 49 if (arguments.length == 0) return; 50 params = Dwt.getParams(arguments, DwtColorPicker.PARAMS); 51 52 params.className = params.className || "DwtColorPicker"; 53 DwtComposite.call(this, params); 54 55 this._hideNoFill = params.hideNoFill; 56 this._noFillLabel = params.noFillLabel; 57 this._allowColorInput = params.allowColorInput; 58 this._defaultColor = params.defaultColor || "#000000"; 59 this._createHtml(); 60 }; 61 62 DwtColorPicker.prototype = new DwtComposite; 63 DwtColorPicker.prototype.constructor = DwtColorPicker; 64 65 DwtColorPicker.prototype.toString = function() { 66 return "DwtColorPicker"; 67 }; 68 69 DwtColorPicker.PARAMS = ["parent", "className", "posStyle", "noFillLabel", "allowColorInput", "defaultColor"]; 70 71 // 72 // Constants 73 // 74 75 // RE to parse out components out of a "rgb(r, g, b);" string 76 DwtColorPicker._RGB_RE = /^rgb\(([0-9]{1,3}),\s*([0-9]{1,3}),\s*([0-9]{1,3})\)$/; 77 DwtColorPicker._HEX_RE = /^\#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i; 78 79 // 80 // Data 81 // 82 83 DwtColorPicker.prototype.TEMPLATE = "dwt.Widgets#DwtColorPicker"; 84 85 // 86 // Public methods 87 // 88 89 /** 90 * Adds a listener to be notified when the button is pressed. 91 * 92 * @param {AjxListener} listener a listener 93 */ 94 DwtColorPicker.prototype.addSelectionListener = 95 function(listener) { 96 this.addListener(DwtEvent.SELECTION, listener); 97 }; 98 99 /** 100 * Removes a selection listener. 101 * 102 * @param {AjxListener} listener a listener 103 */ 104 DwtColorPicker.prototype.removeSelectionListener = 105 function(listener) { 106 this.removeListener(DwtEvent.SELECTION, listener); 107 }; 108 109 DwtColorPicker.prototype.dispose = 110 function () { 111 if (this._disposed) { return; } 112 DwtControl.prototype.dispose.call(this); 113 }; 114 115 DwtColorPicker.prototype._createHtml = function(templateId) { 116 this._createHtmlFromTemplate(templateId||this.TEMPLATE, {id:this.getHtmlElement().id}); 117 }; 118 119 DwtColorPicker.prototype._createHtmlFromTemplate = function(templateId, data) { 120 data.allowColorInput = this._allowColorInput; 121 data.hideNoFill = this._hideNoFill; 122 data.noFillLabel = this._noFillLabel; 123 DwtComposite.prototype._createHtmlFromTemplate.apply(this, arguments); 124 125 // create controls 126 if (data.allowColorInput) { 127 var inputEl = document.getElementById(data.id+"_input"); 128 var inputParams = { 129 parent: this, 130 validationStyle: DwtInputField.CONTINUAL_VALIDATION, //update the preview for each key up 131 errorIconStyle: DwtInputField.ERROR_ICON_RIGHT, 132 validator: DwtColorPicker.__isValidInputValue 133 }; 134 var input = this._colorInput = new DwtInputField(inputParams); 135 input.replaceElement(inputEl); 136 // Add callback for update the preview when the input value is validated. 137 var updateCallback = new AjxCallback(this, this._updatePreview); 138 input.setValidationCallback(updateCallback); 139 140 var error = this._error = new DwtLabel({parent:this}); 141 var errorEl = document.getElementById(data.id+"_error"); 142 error.replaceElement(errorEl); 143 error.setVisible(false); 144 145 this._preview = document.getElementById(data.id+"_preview"); 146 147 var buttonEl = document.getElementById(data.id+"_button"); 148 var button = new DwtButton({parent:this}); 149 button.setText(AjxMsg.setColor); 150 button.replaceElement(buttonEl); 151 button.addSelectionListener(new AjxListener(this, this._handleSetColor)); 152 } 153 154 var buttonEl = document.getElementById(data.id+"_default"); 155 if (buttonEl) { 156 if (!DwtColorPicker.Button) { 157 DwtColorPicker.__defineClasses(); 158 } 159 var button = this._defaultColorButton = new DwtColorPicker.Button({parent:this}); 160 button.setText(data.noFillLabel || AjxMsg.colorsUseDefault); 161 button.replaceElement(buttonEl); 162 button.addSelectionListener(new AjxListener(this, this._handleColorSelect, [0])); 163 } 164 165 // set color handlers 166 var colorsEl = document.getElementById(data.id+"_colors"); 167 var mouseOver = AjxEnv.isIE ? DwtEvent.ONMOUSEENTER : DwtEvent.ONMOUSEOVER; 168 var mouseOut = AjxEnv.isIE ? DwtEvent.ONMOUSELEAVE : DwtEvent.ONMOUSEOUT; 169 170 Dwt.setHandler(colorsEl, DwtEvent.ONMOUSEDOWN, AjxCallback.simpleClosure(this._handleMouseDown, this)); 171 Dwt.setHandler(colorsEl, DwtEvent.ONMOUSEUP, AjxCallback.simpleClosure(this._handleMouseUp, this)); 172 Dwt.setHandler(colorsEl, mouseOver, AjxCallback.simpleClosure(this._handleMouseOver, this)); 173 Dwt.setHandler(colorsEl, mouseOut, AjxCallback.simpleClosure(this._handleMouseOut, this)); 174 }; 175 176 DwtColorPicker.prototype._handleMouseOver = function(htmlEvent) { 177 var event = DwtUiEvent.getEvent(htmlEvent); 178 var target = DwtUiEvent.getTarget(event); 179 if (!Dwt.hasClass(target, "Color")) return; 180 181 this._handleMouseOut(htmlEvent); 182 Dwt.addClass(target, DwtControl.HOVER); 183 this._mouseOverEl = target; 184 }; 185 186 DwtColorPicker.prototype._handleMouseOut = function(htmlEvent) { 187 if (this._mouseOverEl) { 188 Dwt.delClass(this._mouseOverEl, DwtControl.HOVER); 189 } 190 this._mouseOverEl = null; 191 }; 192 193 DwtColorPicker.prototype._handleMouseDown = function(htmlEvent) { 194 var event = DwtUiEvent.getEvent(htmlEvent); 195 var target = DwtUiEvent.getTarget(event); 196 this._mouseDownEl = Dwt.hasClass(target, "Color") ? target : null; 197 }; 198 DwtColorPicker.prototype._handleMouseUp = function(htmlEvent) { 199 var event = DwtUiEvent.getEvent(htmlEvent); 200 var target = DwtUiEvent.getTarget(event); 201 if (this._mouseDownEl != target) return; 202 203 var cssColor = DwtCssStyle.getProperty(target, "background-color"); 204 this._handleColorSelect(DwtColorPicker.__color2hex(cssColor)); 205 }; 206 207 DwtColorPicker.prototype._handleSetColor = function(evt) { 208 var color = this._colorInput.getValue(); 209 if (color) { 210 color = DwtColorPicker.__color2hex(color); 211 if(!color) 212 return; 213 this._handleColorSelect(color); 214 } 215 }; 216 217 DwtColorPicker.prototype._handleColorSelect = function(color) { 218 this._inputColor = color; 219 220 // If our parent is a menu then we need to have it close 221 if (this.parent instanceof DwtMenu) { 222 DwtMenu.closeActiveMenu(); 223 } 224 225 // Call Listeners on mouseEv.target.id 226 if (this.isListenerRegistered(DwtEvent.SELECTION)) { 227 var selEvent = DwtShell.selectionEvent; 228 // DwtUiEvent.copy(selEvent, htmlEvent); 229 selEvent.item = this; 230 selEvent.detail = this._inputColor; 231 this.notifyListeners(DwtEvent.SELECTION, selEvent); 232 } 233 }; 234 235 /** 236 * Gets the input color. 237 * 238 * @return {string} the color (in hex) from the input color field 239 */ 240 DwtColorPicker.prototype.getInputColor = function () { 241 return this._inputColor; 242 }; 243 244 DwtColorPicker.prototype.setDefaultColor = function (color) { 245 if(this._defaultColorButton) { 246 this._defaultColorButton.setDefaultColor(color); 247 } 248 }; 249 250 DwtColorPicker.__color2hex = function(s) { 251 //in IE we can't get the calculated value so for white/black we get white/black (of course it could be set the the hex value in the markup but this is more bulletproof to make sure here) 252 if (s == "white") { 253 return "#FFFFFF"; 254 } 255 if (s == "black") { 256 return "#000000"; 257 } 258 259 var m = s && s.match(DwtColorPicker._RGB_RE); 260 if (m) { 261 // each component should be in range of (0 - 255) 262 for( var i = 1; i <= 3; i++ ) { 263 if(parseInt(m[i]) > 255) 264 return ""; 265 } 266 return AjxColor.color(m[1], m[2], m[3]); 267 } 268 m = s && s.match(DwtColorPicker._HEX_RE); 269 if (m) { 270 return s; 271 } 272 return ""; 273 }; 274 275 DwtColorPicker.__isValidInputValue = function(s) { 276 // null is valid for we consider the condition 277 // the user delete all the word it has been input 278 if (!s) 279 return s; 280 var r = DwtColorPicker.__color2hex(s); 281 if (!r) { 282 throw AjxMsg.colorFormatError; 283 } 284 return s; 285 }; 286 287 DwtColorPicker.prototype._updatePreview = function(inputelement, isValid, value){ 288 if (isValid) { 289 value = DwtColorPicker.__color2hex(value); 290 Dwt.setVisible(this._preview, true); 291 this._preview.style.backgroundColor = value; 292 this._error.setVisible(false); 293 } 294 else { 295 Dwt.setVisible(this._preview, false); 296 this._error.setVisible(true); 297 this._error.setText(AjxMsg.colorFormatError); 298 } 299 }; 300 // 301 // Classes 302 // 303 304 DwtColorPicker.__defineClasses = function() { 305 // HACK: This defines the custom button after the color picker has 306 // HACK: been initialized and instantiated so that we dont' get 307 // HACK: weird dependency issues. (I noticed this in particular 308 // HACK: in the admin client.) 309 DwtColorPicker.Button = function(params) { 310 params.className = params.className || "DwtColorPickerButton"; 311 DwtButton.call(this, params); 312 this._colorDiv = document.getElementById(this.getHtmlElement().id+"_color"); 313 }; 314 DwtColorPicker.Button.prototype = new DwtButton; 315 DwtColorPicker.Button.prototype.constructor = DwtColorPicker.Button; 316 317 DwtColorPicker.Button.prototype.setDefaultColor = function(color) { 318 this._colorDiv.style.backgroundColor = (color === null) ? "" : color; 319 }; 320 321 DwtColorPicker.Button.prototype.toString = function() { 322 return "DwtColorPickerButton"; 323 }; 324 325 DwtColorPicker.Button.prototype.TEMPLATE = "dwt.Widgets#DwtColorPickerButton"; 326 }; 327