1 /* 2 * Copyright (C) 2009 Apple Inc. All rights reserved. 3 * Copyright (C) 2009 Joseph Pecoraro 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15 * its contributors may be used to endorse or promote products derived 16 * from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 /** 31 * @param {Array.<number>} rgba 32 * @param {string=} format 33 * @param {string=} originalText 34 * @constructor 35 */ 36 WebInspector.Color = function(rgba, format, originalText) 37 { 38 this._rgba = rgba; 39 this._originalText = originalText || null; 40 this._format = format || null; 41 if (typeof this._rgba[3] === "undefined") 42 this._rgba[3] = 1; 43 for (var i = 0; i < 4; ++i) { 44 if (this._rgba[i] < 0) 45 this._rgba[i] = 0; 46 if (this._rgba[i] > 1) 47 this._rgba[i] = 1; 48 } 49 } 50 51 /** 52 * @param {string} text 53 * @return {?WebInspector.Color} 54 */ 55 WebInspector.Color.parse = function(text) 56 { 57 // Simple - #hex, rgb(), nickname, hsl() 58 var value = text.toLowerCase().replace(/\s+/g, ""); 59 var simple = /^(?:#([0-9a-f]{3,6})|rgb\(([^)]+)\)|(\w+)|hsl\(([^)]+)\))$/i; 60 var match = value.match(simple); 61 if (match) { 62 if (match[1]) { // hex 63 var hex = match[1].toUpperCase(); 64 var format; 65 if (hex.length === 3) { 66 format = WebInspector.Color.Format.ShortHEX; 67 hex = hex.charAt(0) + hex.charAt(0) + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2); 68 } else 69 format = WebInspector.Color.Format.HEX; 70 var r = parseInt(hex.substring(0,2), 16); 71 var g = parseInt(hex.substring(2,4), 16); 72 var b = parseInt(hex.substring(4,6), 16); 73 return new WebInspector.Color([r / 255, g / 255, b / 255, 1], format, text); 74 } 75 76 if (match[2]) { // rgb 77 var rgbString = match[2].split(/\s*,\s*/); 78 var rgba = [ WebInspector.Color._parseRgbNumeric(rgbString[0]), 79 WebInspector.Color._parseRgbNumeric(rgbString[1]), 80 WebInspector.Color._parseRgbNumeric(rgbString[2]), 1 ]; 81 return new WebInspector.Color(rgba, WebInspector.Color.Format.RGB, text); 82 } 83 84 if (match[3]) { // nickname 85 var nickname = match[3].toLowerCase(); 86 if (nickname in WebInspector.Color.Nicknames) { 87 var rgba = WebInspector.Color.Nicknames[nickname]; 88 var color = WebInspector.Color.fromRGBA(rgba); 89 color._format = WebInspector.Color.Format.Nickname; 90 color._originalText = nickname; 91 return color; 92 } 93 return null; 94 } 95 96 if (match[4]) { // hsl 97 var hslString = match[4].replace(/%/g, "").split(/\s*,\s*/); 98 var hsla = [ WebInspector.Color._parseHueNumeric(hslString[0]), 99 WebInspector.Color._parseSatLightNumeric(hslString[1]), 100 WebInspector.Color._parseSatLightNumeric(hslString[2]), 1 ]; 101 var rgba = WebInspector.Color._hsl2rgb(hsla); 102 return new WebInspector.Color(rgba, WebInspector.Color.Format.HSL, text); 103 } 104 105 return null; 106 } 107 108 // Advanced - rgba(), hsla() 109 var advanced = /^(?:rgba\(([^)]+)\)|hsla\(([^)]+)\))$/; 110 match = value.match(advanced); 111 if (match) { 112 if (match[1]) { // rgba 113 var rgbaString = match[1].split(/\s*,\s*/); 114 var rgba = [ WebInspector.Color._parseRgbNumeric(rgbaString[0]), 115 WebInspector.Color._parseRgbNumeric(rgbaString[1]), 116 WebInspector.Color._parseRgbNumeric(rgbaString[2]), 117 WebInspector.Color._parseAlphaNumeric(rgbaString[3]) ]; 118 return new WebInspector.Color(rgba, WebInspector.Color.Format.RGBA, text); 119 } 120 121 if (match[2]) { // hsla 122 var hslaString = match[2].replace(/%/g, "").split(/\s*,\s*/); 123 var hsla = [ WebInspector.Color._parseHueNumeric(hslaString[0]), 124 WebInspector.Color._parseSatLightNumeric(hslaString[1]), 125 WebInspector.Color._parseSatLightNumeric(hslaString[2]), 126 WebInspector.Color._parseAlphaNumeric(hslaString[3]) ]; 127 var rgba = WebInspector.Color._hsl2rgb(hsla); 128 return new WebInspector.Color(rgba, WebInspector.Color.Format.HSLA, text); 129 } 130 } 131 132 return null; 133 } 134 135 /** 136 * @param {Array.<number>} rgba 137 * @return {WebInspector.Color} 138 */ 139 WebInspector.Color.fromRGBA = function(rgba) 140 { 141 return new WebInspector.Color([rgba[0] / 255, rgba[1] / 255, rgba[2] / 255, rgba[3]]); 142 } 143 144 /** 145 * @param {Array.<number>} hsva 146 * @return {WebInspector.Color} 147 */ 148 WebInspector.Color.fromHSVA = function(hsva) 149 { 150 var h = hsva[0]; 151 var s = hsva[1]; 152 var v = hsva[2]; 153 154 var t = (2 - s) * v; 155 if (v === 0 || s === 0) 156 s = 0; 157 else 158 s *= v / (t < 1 ? t : 2 - t); 159 var hsla = [h, s, t / 2, hsva[3]]; 160 161 return new WebInspector.Color(WebInspector.Color._hsl2rgb(hsla), WebInspector.Color.Format.HSLA); 162 } 163 164 WebInspector.Color.prototype = { 165 /** 166 * @return {?string} 167 */ 168 format: function() 169 { 170 return this._format; 171 }, 172 173 /** 174 * @return {Array.<number>} HSLA with components within [0..1] 175 */ 176 hsla: function() 177 { 178 if (this._hsla) 179 return this._hsla; 180 var r = this._rgba[0]; 181 var g = this._rgba[1]; 182 var b = this._rgba[2]; 183 var max = Math.max(r, g, b); 184 var min = Math.min(r, g, b); 185 var diff = max - min; 186 var add = max + min; 187 188 if (min === max) 189 var h = 0; 190 else if (r === max) 191 var h = ((1/6 * (g - b) / diff) + 1) % 1; 192 else if (g === max) 193 var h = (1/6 * (b - r) / diff) + 1/3; 194 else 195 var h = (1/6 * (r - g) / diff) + 2/3; 196 197 var l = 0.5 * add; 198 199 if (l === 0) 200 var s = 0; 201 else if (l === 1) 202 var s = 1; 203 else if (l <= 0.5) 204 var s = diff / add; 205 else 206 var s = diff / (2 - add); 207 208 this._hsla = [h, s, l, this._rgba[3]]; 209 return this._hsla; 210 }, 211 212 /** 213 * @return {Array.<number>} HSVA with components within [0..1] 214 */ 215 hsva: function() 216 { 217 var hsla = this.hsla(); 218 var h = hsla[0]; 219 var s = hsla[1]; 220 var l = hsla[2]; 221 222 s *= l < 0.5 ? l : 1 - l; 223 return [h, s !== 0 ? 2 * s / (l + s) : 0, (l + s), hsla[3]]; 224 }, 225 226 /** 227 * @return {boolean} 228 */ 229 hasAlpha: function() 230 { 231 return this._rgba[3] !== 1; 232 }, 233 234 /** 235 * @return {boolean} 236 */ 237 canBeShortHex: function() 238 { 239 if (this.hasAlpha()) 240 return false; 241 for (var i = 0; i < 3; ++i) { 242 var c = Math.round(this._rgba[i] * 255); 243 if (c % 17) 244 return false; 245 } 246 return true; 247 }, 248 249 /** 250 * @return {?string} 251 */ 252 toString: function(format) 253 { 254 if (!format) 255 format = this._format; 256 257 /** 258 * @param {number} value 259 * @return {number} 260 */ 261 function toRgbValue(value) 262 { 263 return Math.round(value * 255); 264 } 265 266 /** 267 * @param {number} value 268 * @return {string} 269 */ 270 function toHexValue(value) 271 { 272 var hex = Math.round(value * 255).toString(16); 273 return hex.length === 1 ? "0" + hex : hex; 274 } 275 276 /** 277 * @param {number} value 278 * @return {string} 279 */ 280 function toShortHexValue(value) 281 { 282 return (Math.round(value * 255) / 17).toString(16); 283 } 284 285 switch (format) { 286 case WebInspector.Color.Format.Original: 287 return this._originalText; 288 case WebInspector.Color.Format.RGB: 289 if (this.hasAlpha()) 290 return null; 291 return String.sprintf("rgb(%d, %d, %d)", toRgbValue(this._rgba[0]), toRgbValue(this._rgba[1]), toRgbValue(this._rgba[2])); 292 case WebInspector.Color.Format.RGBA: 293 return String.sprintf("rgba(%d, %d, %d, %f)", toRgbValue(this._rgba[0]), toRgbValue(this._rgba[1]), toRgbValue(this._rgba[2]), this._rgba[3]); 294 case WebInspector.Color.Format.HSL: 295 if (this.hasAlpha()) 296 return null; 297 var hsl = this.hsla(); 298 return String.sprintf("hsl(%d, %d%, %d%)", Math.round(hsl[0] * 360), Math.round(hsl[1] * 100), Math.round(hsl[2] * 100)); 299 case WebInspector.Color.Format.HSLA: 300 var hsla = this.hsla(); 301 return String.sprintf("hsla(%d, %d%, %d%, %f)", Math.round(hsla[0] * 360), Math.round(hsla[1] * 100), Math.round(hsla[2] * 100), hsla[3]); 302 case WebInspector.Color.Format.HEX: 303 if (this.hasAlpha()) 304 return null; 305 return String.sprintf("#%s%s%s", toHexValue(this._rgba[0]), toHexValue(this._rgba[1]), toHexValue(this._rgba[2])).toUpperCase(); 306 case WebInspector.Color.Format.ShortHEX: 307 if (!this.canBeShortHex()) 308 return null; 309 return String.sprintf("#%s%s%s", toShortHexValue(this._rgba[0]), toShortHexValue(this._rgba[1]), toShortHexValue(this._rgba[2])).toUpperCase(); 310 case WebInspector.Color.Format.Nickname: 311 return this.nickname(); 312 } 313 314 return this._originalText; 315 }, 316 317 /** 318 * @return {Array.<number>} 319 */ 320 _canonicalRGBA: function() 321 { 322 var rgba = new Array(3); 323 for (var i = 0; i < 3; ++i) 324 rgba[i] = Math.round(this._rgba[i] * 255); 325 if (this._rgba[3] !== 1) 326 rgba.push(this._rgba[3]); 327 return rgba; 328 }, 329 330 /** 331 * @return {?string} nickname 332 */ 333 nickname: function() 334 { 335 if (!WebInspector.Color._rgbaToNickname) { 336 WebInspector.Color._rgbaToNickname = {}; 337 for (var nickname in WebInspector.Color.Nicknames) { 338 var rgba = WebInspector.Color.Nicknames[nickname]; 339 WebInspector.Color._rgbaToNickname[rgba] = nickname; 340 } 341 } 342 343 return WebInspector.Color._rgbaToNickname[this._canonicalRGBA()] || null; 344 }, 345 346 /** 347 * @return {DOMAgent.RGBA} 348 */ 349 toProtocolRGBA: function() 350 { 351 var rgba = this._canonicalRGBA(); 352 var result = { r: rgba[0], g: rgba[1], b: rgba[2] }; 353 if (rgba[3] !== 1) 354 result.a = rgba[3]; 355 return result; 356 } 357 } 358 359 /** 360 * @param {string} value 361 * return {number} 362 */ 363 WebInspector.Color._parseRgbNumeric = function(value) 364 { 365 var parsed = parseInt(value, 10); 366 if (value.indexOf("%") !== -1) 367 parsed /= 100; 368 else 369 parsed /= 255; 370 return parsed; 371 } 372 373 /** 374 * @param {string} value 375 * return {number} 376 */ 377 WebInspector.Color._parseHueNumeric = function(value) 378 { 379 return isNaN(value) ? 0 : (parseFloat(value) / 360) % 1; 380 } 381 382 /** 383 * @param {string} value 384 * return {number} 385 */ 386 WebInspector.Color._parseSatLightNumeric = function(value) 387 { 388 return parseFloat(value) / 100; 389 } 390 391 /** 392 * @param {string} value 393 * return {number} 394 */ 395 WebInspector.Color._parseAlphaNumeric = function(value) 396 { 397 return isNaN(value) ? 0 : parseFloat(value); 398 } 399 400 /** 401 * @param {Array.<number>} hsl 402 * @return {Array.<number>} 403 */ 404 WebInspector.Color._hsl2rgb = function(hsl) 405 { 406 var h = hsl[0]; 407 var s = hsl[1]; 408 var l = hsl[2]; 409 410 function hue2rgb(p, q, h) 411 { 412 if (h < 0) 413 h += 1; 414 else if (h > 1) 415 h -= 1; 416 417 if ((h * 6) < 1) 418 return p + (q - p) * h * 6; 419 else if ((h * 2) < 1) 420 return q; 421 else if ((h * 3) < 2) 422 return p + (q - p) * ((2 / 3) - h) * 6; 423 else 424 return p; 425 } 426 427 if (s < 0) 428 s = 0; 429 430 if (l <= 0.5) 431 var q = l * (1 + s); 432 else 433 var q = l + s - (l * s); 434 435 var p = 2 * l - q; 436 437 var tr = h + (1 / 3); 438 var tg = h; 439 var tb = h - (1 / 3); 440 441 var r = hue2rgb(p, q, tr); 442 var g = hue2rgb(p, q, tg); 443 var b = hue2rgb(p, q, tb); 444 return [r, g, b, hsl[3]]; 445 } 446 447 WebInspector.Color.Nicknames = { 448 "aliceBlue": [240,248,255], 449 "antiqueWhite": [250,235,215], 450 "aquamarine": [127,255,212], 451 "azure": [240,255,255], 452 "beige": [245,245,220], 453 "bisque": [255,228,196], 454 "black": [0,0,0], 455 "blanchedAlmond": [255,235,205], 456 "blue": [0,0,255], 457 "blueViolet": [138,43,226], 458 "brown": [165,42,42], 459 "burlyWood": [222,184,135], 460 "cadetBlue": [95,158,160], 461 "chartreuse": [127,255,0], 462 "chocolate": [210,105,30], 463 "coral": [255,127,80], 464 "cornflowerBlue": [100,149,237], 465 "cornsilk": [255,248,220], 466 "crimson": [237,20,61], 467 "cyan": [0,255,255], 468 "darkBlue": [0,0,139], 469 "darkCyan": [0,139,139], 470 "darkGoldenrod": [184,134,11], 471 "darkGray": [169,169,169], 472 "darkGreen": [0,100,0], 473 "darkKhaki": [189,183,107], 474 "darkMagenta": [139,0,139], 475 "darkOliveGreen": [85,107,47], 476 "darkOrange": [255,140,0], 477 "darkOrchid": [153,50,204], 478 "darkRed": [139,0,0], 479 "darkSalmon": [233,150,122], 480 "darkSeaGreen": [143,188,143], 481 "darkSlateBlue": [72,61,139], 482 "darkSlateGray": [47,79,79], 483 "darkTurquoise": [0,206,209], 484 "darkViolet": [148,0,211], 485 "deepPink": [255,20,147], 486 "deepSkyBlue": [0,191,255], 487 "dimGray": [105,105,105], 488 "dodgerBlue": [30,144,255], 489 "fireBrick": [178,34,34], 490 "floralWhite": [255,250,240], 491 "forestGreen": [34,139,34], 492 "gainsboro": [220,220,220], 493 "ghostWhite": [248,248,255], 494 "gold": [255,215,0], 495 "goldenrod": [218,165,32], 496 "gray": [128,128,128], 497 "green": [0,128,0], 498 "greenYellow": [173,255,47], 499 "honeyDew": [240,255,240], 500 "hotPink": [255,105,180], 501 "indianRed": [205,92,92], 502 "indigo": [75,0,130], 503 "ivory": [255,255,240], 504 "khaki": [240,230,140], 505 "lavender": [230,230,250], 506 "lavenderBlush": [255,240,245], 507 "lawnGreen": [124,252,0], 508 "lemonChiffon": [255,250,205], 509 "lightBlue": [173,216,230], 510 "lightCoral": [240,128,128], 511 "lightCyan": [224,255,255], 512 "lightGoldenrodYellow":[250,250,210], 513 "lightGreen": [144,238,144], 514 "lightGrey": [211,211,211], 515 "lightPink": [255,182,193], 516 "lightSalmon": [255,160,122], 517 "lightSeaGreen": [32,178,170], 518 "lightSkyBlue": [135,206,250], 519 "lightSlateGray": [119,136,153], 520 "lightSteelBlue": [176,196,222], 521 "lightYellow": [255,255,224], 522 "lime": [0,255,0], 523 "limeGreen": [50,205,50], 524 "linen": [250,240,230], 525 "magenta": [255,0,255], 526 "maroon": [128,0,0], 527 "mediumAquaMarine": [102,205,170], 528 "mediumBlue": [0,0,205], 529 "mediumOrchid": [186,85,211], 530 "mediumPurple": [147,112,219], 531 "mediumSeaGreen": [60,179,113], 532 "mediumSlateBlue": [123,104,238], 533 "mediumSpringGreen": [0,250,154], 534 "mediumTurquoise": [72,209,204], 535 "mediumVioletRed": [199,21,133], 536 "midnightBlue": [25,25,112], 537 "mintCream": [245,255,250], 538 "mistyRose": [255,228,225], 539 "moccasin": [255,228,181], 540 "navajoWhite": [255,222,173], 541 "navy": [0,0,128], 542 "oldLace": [253,245,230], 543 "olive": [128,128,0], 544 "oliveDrab": [107,142,35], 545 "orange": [255,165,0], 546 "orangeRed": [255,69,0], 547 "orchid": [218,112,214], 548 "paleGoldenrod": [238,232,170], 549 "paleGreen": [152,251,152], 550 "paleTurquoise": [175,238,238], 551 "paleVioletRed": [219,112,147], 552 "papayaWhip": [255,239,213], 553 "peachPuff": [255,218,185], 554 "peru": [205,133,63], 555 "pink": [255,192,203], 556 "plum": [221,160,221], 557 "powderBlue": [176,224,230], 558 "purple": [128,0,128], 559 "red": [255,0,0], 560 "rosyBrown": [188,143,143], 561 "royalBlue": [65,105,225], 562 "saddleBrown": [139,69,19], 563 "salmon": [250,128,114], 564 "sandyBrown": [244,164,96], 565 "seaGreen": [46,139,87], 566 "seaShell": [255,245,238], 567 "sienna": [160,82,45], 568 "silver": [192,192,192], 569 "skyBlue": [135,206,235], 570 "slateBlue": [106,90,205], 571 "slateGray": [112,128,144], 572 "snow": [255,250,250], 573 "springGreen": [0,255,127], 574 "steelBlue": [70,130,180], 575 "tan": [210,180,140], 576 "teal": [0,128,128], 577 "thistle": [216,191,216], 578 "tomato": [255,99,71], 579 "turquoise": [64,224,208], 580 "violet": [238,130,238], 581 "wheat": [245,222,179], 582 "white": [255,255,255], 583 "whiteSmoke": [245,245,245], 584 "yellow": [255,255,0], 585 "yellowGreen": [154,205,50], 586 "transparent": [0, 0, 0, 0], 587 }; 588 589 WebInspector.Color.PageHighlight = { 590 Content: WebInspector.Color.fromRGBA([111, 168, 220, .66]), 591 ContentLight: WebInspector.Color.fromRGBA([111, 168, 220, .5]), 592 ContentOutline: WebInspector.Color.fromRGBA([9, 83, 148]), 593 Padding: WebInspector.Color.fromRGBA([147, 196, 125, .55]), 594 PaddingLight: WebInspector.Color.fromRGBA([147, 196, 125, .4]), 595 Border: WebInspector.Color.fromRGBA([255, 229, 153, .66]), 596 BorderLight: WebInspector.Color.fromRGBA([255, 229, 153, .5]), 597 Margin: WebInspector.Color.fromRGBA([246, 178, 107, .66]), 598 MarginLight: WebInspector.Color.fromRGBA([246, 178, 107, .5]), 599 EventTarget: WebInspector.Color.fromRGBA([255, 196, 196, .66]) 600 } 601 602 WebInspector.Color.Format = { 603 Original: "original", 604 Nickname: "nickname", 605 HEX: "hex", 606 ShortHEX: "shorthex", 607 RGB: "rgb", 608 RGBA: "rgba", 609 HSL: "hsl", 610 HSLA: "hsla" 611 } 612