1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2009, 2010, 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) 2009, 2010, 2012, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * Creates a color object.
 26  * @class
 27  * This class represents a color and is useful for color operations inspired by the code in SkinResources.java.
 28  * 
 29  */
 30 AjxColor = function(r, g, b) {
 31 	if (arguments.length == 0) return;
 32 	this.r = r;
 33 	this.g = g;
 34 	this.b = b;
 35 };
 36 
 37 /**
 38  * Returns a string representation of the object.
 39  * 
 40  * @return		{string}		a string representation of the object
 41  */
 42 AjxColor.prototype.toString = function() {
 43 	return AjxColor.color(this.r, this.g, this.b);
 44 };
 45 
 46 //
 47 // Static functions
 48 //
 49 
 50 /**
 51  * Returns the RGB components (as an array) of the given color.
 52  *
 53  * @param {string}	color 	the color string defined as "#rrggbb"
 54  * @return	{array}		the color
 55  */
 56 AjxColor.components = function(color) {
 57 	var m = AjxColor.__RE.exec(color);
 58 	return m ? [parseInt(m[1],16),parseInt(m[2],16),parseInt(m[3],16)] : null;
 59 };
 60 
 61 /**
 62  * Returns a color string of the form "#rrggbb" from the given color
 63  * components.
 64  *
 65  * @param {number}	r the Red component value between 0 and 255, inclusive
 66  * @param {number}	g the Green component value between 0 and 255, inclusive
 67  * @param {number}	b the Blue component value between 0 and 255, inclusive
 68  * @return	{string}	the color string
 69  */
 70 AjxColor.color = function(r, g, b) {
 71 	return [
 72 		"#",
 73 		AjxColor.__pad(Number(Math.round(r)).toString(16), 2),
 74 		AjxColor.__pad(Number(Math.round(g)).toString(16), 2),
 75 		AjxColor.__pad(Number(Math.round(b)).toString(16), 2)
 76 	].join("");
 77 };
 78 
 79 /**
 80  * Returns a color string that is the inverse of the given color.
 81  *
 82  * @param color [string] Color value defined as "#rrggbb".
 83  */
 84 //AjxColor.invert = function(color) {
 85 //	var n = ~parseInt(color.substr(1),16) & 0x0FFFFFF;
 86 //	return AjxColor.color((n >> 16) & 0x0FF, (n >> 8) & 0x0FF, n & 0x0FF);
 87 //};
 88 
 89 /**
 90  * Lightens the specified color by the given amount.
 91  *
 92  * @param {string}	color 	the color value defined as "#rrggbb"
 93  * @param {number}	delta the amount to change
 94  * @return	{string}	the color string
 95  */
 96 AjxColor.lighten = function(color, delta) {
 97 	var comps = AjxColor.components(color);
 98 	return comps ? AjxColor.color(
 99 		AjxColor.__lighten(comps[0],delta),
100 		AjxColor.__lighten(comps[1],delta),
101 		AjxColor.__lighten(comps[2],delta)
102 	) : "";
103 };
104 
105 /**
106  * Darkens the specified color by the given amount.
107  *
108  * @param {string}	color 	the color value defined as "#rrggbb"
109  * @param {number}	delta the amount to change
110  * @return	{string}	the color string
111  */
112 AjxColor.darken = function(color, delta) {
113 	var comps = AjxColor.components(color);
114 	return comps ? AjxColor.color(
115 		AjxColor.__darken(comps[0],delta),
116 		AjxColor.__darken(comps[1],delta),
117 		AjxColor.__darken(comps[2],delta)
118 	) : "";
119 };
120 
121 /**
122  * Deepens the specified color. This operation is different than darken
123  * because it retains the brightness of the color even when it gets
124  * darker. Just making a color darker tends to result in a color that
125  * is "muddy".
126  * <p>
127  * The color is deepened by first determining the largest individual
128  * component value and then multiplying each component value by the ratio
129  * of its value to the largest value. Then, optionally, each value is
130  * multiplied by the adjustment value in order to deepen a little more
131  * or a little less. Typical adjustment values are around 1 such as .9
132  * or 1.1.
133  *
134  * @param {string}	color 	the color value defined as "#rrggbb"
135  * @param {number}		[adjustment]	the multiplier adjustment
136  * @return	{string}	the color string
137  */
138 AjxColor.deepen = function(color, adjustment) {
139 	var comps = AjxColor.components(color);
140 	var index = 0;
141 	for (var i = 1; i < comps.length; i++) {
142 		if (comps[i] > comps[index]) {
143 			index = i;
144 		}
145 	}
146 	for (var i = 0; i < comps.length; i++) {
147 		var multiplier = comps[index] ? (comps[i] / comps[index]) : 1;
148 		comps[i] = Math.floor(comps[i] * multiplier * (adjustment || 1));
149 	}
150 	return AjxColor.color(comps[0],comps[1],comps[2]);
151 };
152 
153 //
154 // Private
155 //
156 
157 AjxColor.__RE = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i;
158 
159 AjxColor.__pad = function(value, width, prefix) {
160 	if (!prefix) prefix = "0";
161 	var s = String(value);
162 	for (var i = s.length; i < width; i++) {
163 		s = prefix + s;
164 	}
165 	return s;
166 };
167 
168 AjxColor.__lighten = function(value, delta) {
169 	return Math.max(0, Math.min(255, value + (255-value)*delta));
170 };
171 AjxColor.__darken = function(value, delta) {
172 	return Math.max(0, Math.min(255, value + (1-value)*delta));
173 };
174