/** * Represents an RGB color and provides helper functions on it e.g. to get * color components in HSL color space. */Ext.define('Ext.util.Color', { alternateClassName: 'Ext.draw.Color', statics: { colorToHexRe: /(.*?)rgb\((\d+),\s*(\d+),\s*(\d+)\)/, rgbToHexRe: /\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/, rgbaToHexRe: /\s*rgba\((\d+),\s*(\d+),\s*(\d+),\s*([.\d]+)\)/, hexRe: /\s*#([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)\s*/, // Note that 'none' ia an invalid color string. // When assigned to the fillStyle/strokeStyle/shadowColor properties // of a Canvas context, those properties won't change their values. NONE: 'none', RGBA_NONE: 'rgba(0, 0, 0, 0)' }, isColor: true, /** * @cfg {Number} lightnessFactor * * The default factor to compute the lighter or darker color. */ lightnessFactor: 0.2, /** * @constructor * @param {Number} red Red component (0..255) * @param {Number} green Green component (0..255) * @param {Number} blue Blue component (0..255) * @param {Number} [alpha=1] (optional) Alpha component (0..1) */ constructor: function(red, green, blue, alpha) { this.setRGB(red, green, blue, alpha); }, clone: function() { var me = this; return new this.self(me.r, me.g, me.b, me.a); }, setRGB: function(red, green, blue, alpha) { var me = this; me.r = Math.min(255, Math.max(0, red)); me.g = Math.min(255, Math.max(0, green)); me.b = Math.min(255, Math.max(0, blue)); if (alpha === undefined) { me.a = 1; } else { me.a = Math.min(1, Math.max(0, alpha)); } }, /** * The the brightness of a color as defined by W3C: * https://www.w3.org/TR/AERT#color-contrast * @return {Number} The brightness, between `0` and `100`. */ getBrightness: function() { var r = this.r / 255 * 100, g = this.g / 255 * 100, b = this.b / 255 * 100; return ((r * 299) + (g * 587) + (b * 114)) / 1000; }, /** * Returns the gray value (0 to 255) of the color. * * The gray value is calculated using the formula r*0.3 + g*0.59 + b*0.11. * * @return {Number} */ getGrayscale: function() { // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale return this.r * 0.3 + this.g * 0.59 + this.b * 0.11; }, /** * Get the equivalent HSL components of the color. * @return {Number[]} */ getHSL: function() { var me = this, r = me.r / 255, g = me.g / 255, b = me.b / 255, max = Math.max(r, g, b), min = Math.min(r, g, b), delta = max - min, h, s = 0, l = 0.5 * (max + min); // min==max means achromatic (hue is undefined) if (min !== max) { s = (l <= 0.5) ? delta / (max + min) : delta / (2 - max - min); if (r === max) { h = 60 * (g - b) / delta; } else if (g === max) { h = 120 + 60 * (b - r) / delta; } else { h = 240 + 60 * (r - g) / delta; } if (h < 0) { h += 360; } if (h >= 360) { h -= 360; } } return [h, s, l]; }, /** * Get the equivalent HSV components of the color. * @return {Number[]} */ getHSV: function() { var me = this, r = me.r / 255, g = me.g / 255, b = me.b / 255, max = Math.max(r, g, b), min = Math.min(r, g, b), C = max - min, h, s = 0, v = max; // min == max means achromatic (hue is undefined) if (min != max) { // eslint-disable-line eqeqeq s = v ? C / v : 0; if (r === max) { h = 60 * (g - b) / C; } else if (g === max) { h = 60 * (b - r) / C + 120; } else { h = 60 * (r - g) / C + 240; } if (h < 0) { h += 360; } if (h >= 360) { h -= 360; } } return [h, s, v]; }, /** * Set current color based on the specified HSL values. * * @param {Number} h Hue component [0..360) * @param {Number} s Saturation component [0..1] * @param {Number} l Lightness component [0..1] * @return {Ext.util.Color} */ setHSL: function(h, s, l) { var me = this, abs = Math.abs, c, x, m; h = (h % 360 + 360) % 360; s = s > 1 ? 1 : s < 0 ? 0 : s; l = l > 1 ? 1 : l < 0 ? 0 : l; if (s === 0 || h === null) { l *= 255; me.setRGB(l, l, l); } else { // http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL h /= 60; c = s * (1 - abs(2 * l - 1)); // chroma x = c * (1 - abs(h % 2 - 1)); // second largest component m = l - c / 2; // lightness adjustment m *= 255; c *= 255; x *= 255; switch (Math.floor(h)) { case 0: me.setRGB(c + m, x + m, m); break; case 1: me.setRGB(x + m, c + m, m); break; case 2: me.setRGB(m, c + m, x + m); break; case 3: me.setRGB(m, x + m, c + m); break; case 4: me.setRGB(x + m, m, c + m); break; case 5: me.setRGB(c + m, m, x + m); break; } } return me; }, /** * Set current color based on the specified HSV values. * * @param {Number} h Hue component [0..360) * @param {Number} s Saturation component [0..1] * @param {Number} v Value component [0..1] * @return {Ext.util.Color} */ setHSV: function(h, s, v) { var me = this, c, x, m; h = (h % 360 + 360) % 360; s = s > 1 ? 1 : s < 0 ? 0 : s; v = v > 1 ? 1 : v < 0 ? 0 : v; if (s === 0 || h === null) { v *= 255; me.setRGB(v, v, v); } else { // http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV h /= 60; c = v * s; // chroma x = c * (1 - Math.abs(h % 2 - 1)); // second largest component m = v - c; // value adjustment m *= 255; c *= 255; x *= 255; switch (Math.floor(h)) { case 0: me.setRGB(c + m, x + m, m); break; case 1: me.setRGB(x + m, c + m, m); break; case 2: me.setRGB(m, c + m, x + m); break; case 3: me.setRGB(m, x + m, c + m); break; case 4: me.setRGB(x + m, m, c + m); break; case 5: me.setRGB(c + m, m, x + m); break; } } return me; }, /** * Returns a new color that is lighter than this color in the HSL color space. * @param {Number} [factor=0.2] Lighter factor (0..1). * @return {Ext.util.Color} */ createLighter: function(factor) { var color = this.clone(); color.lighten(factor); return color; }, /** * Lighten this color in the HSL color space. * @param {Number} [factor=0.2] Lighten factor (0..1). */ lighten: function(factor) { var hsl; if (!factor && factor !== 0) { factor = this.lightnessFactor; } hsl = this.getHSL(); this.setHSL(hsl[0], hsl[1], Ext.Number.constrain(hsl[2] + factor, 0, 1)); }, /** * Returns a new color that is darker than this color in the HSL color space. * @param {Number} [factor=0.2] Darker factor (0..1). * @return {Ext.util.Color} */ createDarker: function(factor) { var color = this.clone(); color.darken(factor); return color; }, /** * Darken this color in the HSL color space. * @param {Number} [factor=0.2] Darken factor (0..1). */ darken: function(factor) { if (!factor && factor !== 0) { factor = this.lightnessFactor; } return this.lighten(-factor); }, /** * toString() returns a color in hex format ('#rrggbb') if the alpha is 1. If the * alpha is less than one, toString() returns the color in RGBA format ('rgba(255,0,0,0.3)'). * * @return {String} */ toString: function() { var me = this, round = Math.round, r, g, b; if (me.a === 1) { r = round(me.r).toString(16); g = round(me.g).toString(16); b = round(me.b).toString(16); r = (r.length === 1) ? '0' + r : r; g = (g.length === 1) ? '0' + g : g; b = (b.length === 1) ? '0' + b : b; return ['#', r, g, b].join(''); } else { return 'rgba(' + [ round(me.r), round(me.g), round(me.b), me.a === 0 ? 0 : me.a.toFixed(15) ].join(', ') + ')'; // Even though things like 'rgba(0,0,0,0)' will probably get converted to // 'rgba(0, 0, 0, 0)' when assigned to ctx.fillStyle or ctx.strokeStyle, // we can't be sure this is the case for every browser, so for consistency // with the Ext.draw.Color.RGBA_NONE (which is used a lot for checks) // we join using the ', ' and not ',' here. } }, /** * Get this color in hexadecimal format. * @return {String} The color in hexadecimal format. */ toHex: function(color) { var r = this.r, g = this.g, b = this.b, rgb = b | (g << 8) | (r << 16); return '#' + ('000000' + rgb.toString(16)).slice(-6); }, /** * Parse the string and set the current color. * * Supported formats: * * + '#rrggbb' * + '#rgb', 'rgb(r,g,b)' * + 'rgba(r,g,b,a)' * + supported CSS color names (e.g., 'black', 'white', etc). * * If the string is not recognized, setFromString returns rgba(0,0,0,0). * * @param {String} str Color as string. * @return this */ setFromString: function(str) { var values, r, g, b, a = 1, parse = parseInt; if (str === Ext.util.Color.NONE) { this.r = this.g = this.b = this.a = 0; return this; } if ((str.length === 4 || str.length === 7) && str.substr(0, 1) === '#') { values = str.match(Ext.util.Color.hexRe); if (values) { r = parse(values[1], 16) >> 0; g = parse(values[2], 16) >> 0; b = parse(values[3], 16) >> 0; if (str.length === 4) { r += (r * 16); g += (g * 16); b += (b * 16); } } } else if ((values = str.match(Ext.util.Color.rgbToHexRe))) { r = +values[1]; g = +values[2]; b = +values[3]; } else if ((values = str.match(Ext.util.Color.rgbaToHexRe))) { r = +values[1]; g = +values[2]; b = +values[3]; a = +values[4]; } else { if (Ext.util.Color.ColorList.hasOwnProperty(str.toLowerCase())) { return this.setFromString(Ext.util.Color.ColorList[str.toLowerCase()]); } } if (typeof r === 'undefined') { return this; } this.r = r; this.g = g; this.b = b; this.a = a; return this; }}, function() { var flyColor = new this(); this.addStatics({ /** * Returns a flyweight instance of Ext.util.Color. * * Can be called with either a CSS color string or with separate * arguments for red, green, blue, alpha. * * @param {Number/String} red Red component (0..255) or CSS color string. * @param {Number} [green] Green component (0..255) * @param {Number} [blue] Blue component (0..255) * @param {Number} [alpha=1] Alpha component (0..1) * @return {Ext.util.Color} * @static */ fly: function(red, green, blue, alpha) { switch (arguments.length) { case 1: flyColor.setFromString(red); break; case 3: case 4: flyColor.setRGB(red, green, blue, alpha); break; default: return null; } return flyColor; }, ColorList: { aliceblue: '#f0f8ff', antiquewhite: '#faebd7', aqua: '#00ffff', aquamarine: '#7fffd4', azure: '#f0ffff', beige: '#f5f5dc', bisque: '#ffe4c4', black: '#000000', blanchedalmond: '#ffebcd', blue: '#0000ff', blueviolet: '#8a2be2', brown: '#a52a2a', burlywood: '#deb887', cadetblue: '#5f9ea0', chartreuse: '#7fff00', chocolate: '#d2691e', coral: '#ff7f50', cornflowerblue: '#6495ed', cornsilk: '#fff8dc', crimson: '#dc143c', cyan: '#00ffff', darkblue: '#00008b', darkcyan: '#008b8b', darkgoldenrod: '#b8860b', darkgray: '#a9a9a9', darkgreen: '#006400', darkkhaki: '#bdb76b', darkmagenta: '#8b008b', darkolivegreen: '#556b2f', darkorange: '#ff8c00', darkorchid: '#9932cc', darkred: '#8b0000', darksalmon: '#e9967a', darkseagreen: '#8fbc8f', darkslateblue: '#483d8b', darkslategray: '#2f4f4f', darkturquoise: '#00ced1', darkviolet: '#9400d3', deeppink: '#ff1493', deepskyblue: '#00bfff', dimgray: '#696969', dodgerblue: '#1e90ff', firebrick: '#b22222', floralwhite: '#fffaf0', forestgreen: '#228b22', fuchsia: '#ff00ff', gainsboro: '#dcdcdc', ghostwhite: '#f8f8ff', gold: '#ffd700', goldenrod: '#daa520', gray: '#808080', green: '#008000', greenyellow: '#adff2f', honeydew: '#f0fff0', hotpink: '#ff69b4', indianred: '#cd5c5c', indigo: '#4b0082', ivory: '#fffff0', khaki: '#f0e68c', lavender: '#e6e6fa', lavenderblush: '#fff0f5', lawngreen: '#7cfc00', lemonchiffon: '#fffacd', lightblue: '#add8e6', lightcoral: '#f08080', lightcyan: '#e0ffff', lightgoldenrodyellow: '#fafad2', lightgray: '#d3d3d3', lightgrey: '#d3d3d3', lightgreen: '#90ee90', lightpink: '#ffb6c1', lightsalmon: '#ffa07a', lightseagreen: '#20b2aa', lightskyblue: '#87cefa', lightslategray: '#778899', lightsteelblue: '#b0c4de', lightyellow: '#ffffe0', lime: '#00ff00', limegreen: '#32cd32', linen: '#faf0e6', magenta: '#ff00ff', maroon: '#800000', mediumaquamarine: '#66cdaa', mediumblue: '#0000cd', mediumorchid: '#ba55d3', mediumpurple: '#9370d8', mediumseagreen: '#3cb371', mediumslateblue: '#7b68ee', mediumspringgreen: '#00fa9a', mediumturquoise: '#48d1cc', mediumvioletred: '#c71585', midnightblue: '#191970', mintcream: '#f5fffa', mistyrose: '#ffe4e1', moccasin: '#ffe4b5', navajowhite: '#ffdead', navy: '#000080', oldlace: '#fdf5e6', olive: '#808000', olivedrab: '#6b8e23', orange: '#ffa500', orangered: '#ff4500', orchid: '#da70d6', palegoldenrod: '#eee8aa', palegreen: '#98fb98', paleturquoise: '#afeeee', palevioletred: '#d87093', papayawhip: '#ffefd5', peachpuff: '#ffdab9', peru: '#cd853f', pink: '#ffc0cb', plum: '#dda0dd', powderblue: '#b0e0e6', purple: '#800080', red: '#ff0000', rosybrown: '#bc8f8f', royalblue: '#4169e1', saddlebrown: '#8b4513', salmon: '#fa8072', sandybrown: '#f4a460', seagreen: '#2e8b57', seashell: '#fff5ee', sienna: '#a0522d', silver: '#c0c0c0', skyblue: '#87ceeb', slateblue: '#6a5acd', slategray: '#708090', snow: '#fffafa', springgreen: '#00ff7f', steelblue: '#4682b4', tan: '#d2b48c', teal: '#008080', thistle: '#d8bfd8', tomato: '#ff6347', turquoise: '#40e0d0', violet: '#ee82ee', wheat: '#f5deb3', white: '#ffffff', whitesmoke: '#f5f5f5', yellow: '#ffff00', yellowgreen: '#9acd32' }, /** * Create a new color based on the specified HSL values. * * @param {Number} h Hue component [0..360) * @param {Number} s Saturation component [0..1] * @param {Number} l Lightness component [0..1] * @return {Ext.util.Color} * @static */ fromHSL: function(h, s, l) { return (new this(0, 0, 0, 0)).setHSL(h, s, l); }, /** * Create a new color based on the specified HSV values. * * @param {Number} h Hue component [0..360) * @param {Number} s Saturation component [0..1] * @param {Number} v Value component [0..1] * @return {Ext.util.Color} * @static */ fromHSV: function(h, s, v) { return (new this(0, 0, 0, 0)).setHSL(h, s, v); }, /** * Parse the string and create a new color. * * Supported formats: * * + '#rrggbb' * + '#rgb', 'rgb(r,g,b)' * + 'rgba(r,g,b,a)' * + supported CSS color names (e.g., 'black', 'white', etc). * * If the string is not recognized, fromString returns rgba(0,0,0,0). * * @param {String} color Color as string. * @return {Ext.util.Color} * @static */ fromString: function(color) { return (new this(0, 0, 0, 0)).setFromString(color); }, /** * Convenience method for creating a color. * * Can be called with several different combinations of arguments: * * // Ext.util.Color is returned unchanged. * Ext.util.Color.create(new Ext.util.color(255, 0, 0, 0)); * * // CSS color string. * Ext.util.Color.create("red"); * * // Array of red, green, blue, alpha * Ext.util.Color.create([255, 0, 0, 0]); * * // Separate arguments of red, green, blue, alpha * Ext.util.Color.create(255, 0, 0, 0); * * // Returns black when no arguments given. * Ext.util.Color.create(); * * @param {Array} arg * @param {Ext.util.Color/String/Number[]/Number} [arg.red] Red component (0..255), * CSS color string or array of all components. * @param {Number} [arg.green] Green component (0..255) * @param {Number} [arg.blue] Blue component (0..255) * @param {Number} [arg.alpha=1] Alpha component (0..1) * @return {Ext.util.Color} * @static */ create: function(arg) { if (arg instanceof this) { return arg; } else if (Ext.isArray(arg)) { return new Ext.util.Color(arg[0], arg[1], arg[2], arg[3]); } else if (Ext.isString(arg)) { return Ext.util.Color.fromString(arg); } else if (arguments.length > 2) { return new Ext.util.Color(arguments[0], arguments[1], arguments[2], arguments[3]); } else { return new Ext.util.Color(0, 0, 0, 0); } } });});