1 /** 2 * Returns the {@link pv.Color} for the specified color format string. Colors 3 * may have an associated opacity, or alpha channel. Color formats are specified 4 * by CSS Color Modular Level 3, using either in RGB or HSL color space. For 5 * example:<ul> 6 * 7 * <li>#f00 // #rgb 8 * <li>#ff0000 // #rrggbb 9 * <li>rgb(255, 0, 0) 10 * <li>rgb(100%, 0%, 0%) 11 * <li>hsl(0, 100%, 50%) 12 * <li>rgba(0, 0, 255, 0.5) 13 * <li>hsla(120, 100%, 50%, 1) 14 * 15 * </ul>The SVG 1.0 color keywords names are also supported, such as "aliceblue" 16 * and "yellowgreen". The "transparent" keyword is supported for fully- 17 * transparent black. 18 * 19 * <p>If the <tt>format</tt> argument is already an instance of <tt>Color</tt>, 20 * the argument is returned with no further processing. 21 * 22 * @param {string} format the color specification string, such as "#f00". 23 * @returns {pv.Color} the corresponding <tt>Color</tt>. 24 * @see <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">SVG color 25 * keywords</a> 26 * @see <a href="http://www.w3.org/TR/css3-color/">CSS3 color module</a> 27 */ 28 pv.color = function(format) { 29 if (format.rgb) return format.rgb(); 30 31 /* Handle hsl, rgb. */ 32 var m1 = /([a-z]+)\((.*)\)/i.exec(format); 33 if (m1) { 34 var m2 = m1[2].split(","), a = 1; 35 switch (m1[1]) { 36 case "hsla": 37 case "rgba": { 38 a = parseFloat(m2[3]); 39 if (!a) return pv.Color.transparent; 40 break; 41 } 42 } 43 switch (m1[1]) { 44 case "hsla": 45 case "hsl": { 46 var h = parseFloat(m2[0]), // degrees 47 s = parseFloat(m2[1]) / 100, // percentage 48 l = parseFloat(m2[2]) / 100; // percentage 49 return (new pv.Color.Hsl(h, s, l, a)).rgb(); 50 } 51 case "rgba": 52 case "rgb": { 53 function parse(c) { // either integer or percentage 54 var f = parseFloat(c); 55 return (c[c.length - 1] == '%') ? Math.round(f * 2.55) : f; 56 } 57 var r = parse(m2[0]), g = parse(m2[1]), b = parse(m2[2]); 58 return pv.rgb(r, g, b, a); 59 } 60 } 61 } 62 63 /* Named colors. */ 64 var named = pv.Color.names[format]; 65 if (named) return named; 66 67 /* Hexadecimal colors: #rgb and #rrggbb. */ 68 if (format.charAt(0) == "#") { 69 var r, g, b; 70 if (format.length == 4) { 71 r = format.charAt(1); r += r; 72 g = format.charAt(2); g += g; 73 b = format.charAt(3); b += b; 74 } else if (format.length == 7) { 75 r = format.substring(1, 3); 76 g = format.substring(3, 5); 77 b = format.substring(5, 7); 78 } 79 return pv.rgb(parseInt(r, 16), parseInt(g, 16), parseInt(b, 16), 1); 80 } 81 82 /* Otherwise, pass-through unsupported colors. */ 83 return new pv.Color(format, 1); 84 }; 85 86 /** 87 * Constructs a color with the specified color format string and opacity. This 88 * constructor should not be invoked directly; use {@link pv.color} instead. 89 * 90 * @class Represents an abstract (possibly translucent) color. The color is 91 * divided into two parts: the <tt>color</tt> attribute, an opaque color format 92 * string, and the <tt>opacity</tt> attribute, a float in [0, 1]. The color 93 * space is dependent on the implementing class; all colors support the 94 * {@link #rgb} method to convert to RGB color space for interpolation. 95 * 96 * <p>See also the <a href="../../api/Color.html">Color guide</a>. 97 * 98 * @param {string} color an opaque color format string, such as "#f00". 99 * @param {number} opacity the opacity, in [0,1]. 100 * @see pv.color 101 */ 102 pv.Color = function(color, opacity) { 103 /** 104 * An opaque color format string, such as "#f00". 105 * 106 * @type string 107 * @see <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">SVG color 108 * keywords</a> 109 * @see <a href="http://www.w3.org/TR/css3-color/">CSS3 color module</a> 110 */ 111 this.color = color; 112 113 /** 114 * The opacity, a float in [0, 1]. 115 * 116 * @type number 117 */ 118 this.opacity = opacity; 119 }; 120 121 /** 122 * Returns a new color that is a brighter version of this color. The behavior of 123 * this method may vary slightly depending on the underlying color space. 124 * Although brighter and darker are inverse operations, the results of a series 125 * of invocations of these two methods might be inconsistent because of rounding 126 * errors. 127 * 128 * @param [k] {number} an optional scale factor; defaults to 1. 129 * @see #darker 130 * @returns {pv.Color} a brighter color. 131 */ 132 pv.Color.prototype.brighter = function(k) { 133 return this.rgb().brighter(k); 134 }; 135 136 /** 137 * Returns a new color that is a brighter version of this color. The behavior of 138 * this method may vary slightly depending on the underlying color space. 139 * Although brighter and darker are inverse operations, the results of a series 140 * of invocations of these two methods might be inconsistent because of rounding 141 * errors. 142 * 143 * @param [k] {number} an optional scale factor; defaults to 1. 144 * @see #brighter 145 * @returns {pv.Color} a darker color. 146 */ 147 pv.Color.prototype.darker = function(k) { 148 return this.rgb().darker(k); 149 }; 150 151 /** 152 * Constructs a new RGB color with the specified channel values. 153 * 154 * @param {number} r the red channel, an integer in [0,255]. 155 * @param {number} g the green channel, an integer in [0,255]. 156 * @param {number} b the blue channel, an integer in [0,255]. 157 * @param {number} [a] the alpha channel, a float in [0,1]. 158 * @returns pv.Color.Rgb 159 */ 160 pv.rgb = function(r, g, b, a) { 161 return new pv.Color.Rgb(r, g, b, (arguments.length == 4) ? a : 1); 162 }; 163 164 /** 165 * Constructs a new RGB color with the specified channel values. 166 * 167 * @class Represents a color in RGB space. 168 * 169 * @param {number} r the red channel, an integer in [0,255]. 170 * @param {number} g the green channel, an integer in [0,255]. 171 * @param {number} b the blue channel, an integer in [0,255]. 172 * @param {number} a the alpha channel, a float in [0,1]. 173 * @extends pv.Color 174 */ 175 pv.Color.Rgb = function(r, g, b, a) { 176 pv.Color.call(this, a ? ("rgb(" + r + "," + g + "," + b + ")") : "none", a); 177 178 /** 179 * The red channel, an integer in [0, 255]. 180 * 181 * @type number 182 */ 183 this.r = r; 184 185 /** 186 * The green channel, an integer in [0, 255]. 187 * 188 * @type number 189 */ 190 this.g = g; 191 192 /** 193 * The blue channel, an integer in [0, 255]. 194 * 195 * @type number 196 */ 197 this.b = b; 198 199 /** 200 * The alpha channel, a float in [0, 1]. 201 * 202 * @type number 203 */ 204 this.a = a; 205 }; 206 pv.Color.Rgb.prototype = pv.extend(pv.Color); 207 208 /** 209 * Constructs a new RGB color with the same green, blue and alpha channels as 210 * this color, with the specified red channel. 211 * 212 * @param {number} r the red channel, an integer in [0,255]. 213 */ 214 pv.Color.Rgb.prototype.red = function(r) { 215 return pv.rgb(r, this.g, this.b, this.a); 216 }; 217 218 /** 219 * Constructs a new RGB color with the same red, blue and alpha channels as this 220 * color, with the specified green channel. 221 * 222 * @param {number} g the green channel, an integer in [0,255]. 223 */ 224 pv.Color.Rgb.prototype.green = function(g) { 225 return pv.rgb(this.r, g, this.b, this.a); 226 }; 227 228 /** 229 * Constructs a new RGB color with the same red, green and alpha channels as 230 * this color, with the specified blue channel. 231 * 232 * @param {number} b the blue channel, an integer in [0,255]. 233 */ 234 pv.Color.Rgb.prototype.blue = function(b) { 235 return pv.rgb(this.r, this.g, b, this.a); 236 }; 237 238 /** 239 * Constructs a new RGB color with the same red, green and blue channels as this 240 * color, with the specified alpha channel. 241 * 242 * @param {number} a the alpha channel, a float in [0,1]. 243 */ 244 pv.Color.Rgb.prototype.alpha = function(a) { 245 return pv.rgb(this.r, this.g, this.b, a); 246 }; 247 248 /** 249 * Returns the RGB color equivalent to this color. This method is abstract and 250 * must be implemented by subclasses. 251 * 252 * @returns {pv.Color.Rgb} an RGB color. 253 * @function 254 * @name pv.Color.prototype.rgb 255 */ 256 257 /** 258 * Returns this. 259 * 260 * @returns {pv.Color.Rgb} this. 261 */ 262 pv.Color.Rgb.prototype.rgb = function() { return this; }; 263 264 /** 265 * Returns a new color that is a brighter version of this color. This method 266 * applies an arbitrary scale factor to each of the three RGB components of this 267 * color to create a brighter version of this color. Although brighter and 268 * darker are inverse operations, the results of a series of invocations of 269 * these two methods might be inconsistent because of rounding errors. 270 * 271 * @param [k] {number} an optional scale factor; defaults to 1. 272 * @see #darker 273 * @returns {pv.Color.Rgb} a brighter color. 274 */ 275 pv.Color.Rgb.prototype.brighter = function(k) { 276 k = Math.pow(0.7, arguments.length ? k : 1); 277 var r = this.r, g = this.g, b = this.b, i = 30; 278 if (!r && !g && !b) return pv.rgb(i, i, i, this.a); 279 if (r && (r < i)) r = i; 280 if (g && (g < i)) g = i; 281 if (b && (b < i)) b = i; 282 return pv.rgb( 283 Math.min(255, Math.floor(r / k)), 284 Math.min(255, Math.floor(g / k)), 285 Math.min(255, Math.floor(b / k)), 286 this.a); 287 }; 288 289 /** 290 * Returns a new color that is a darker version of this color. This method 291 * applies an arbitrary scale factor to each of the three RGB components of this 292 * color to create a darker version of this color. Although brighter and darker 293 * are inverse operations, the results of a series of invocations of these two 294 * methods might be inconsistent because of rounding errors. 295 * 296 * @param [k] {number} an optional scale factor; defaults to 1. 297 * @see #brighter 298 * @returns {pv.Color.Rgb} a darker color. 299 */ 300 pv.Color.Rgb.prototype.darker = function(k) { 301 k = Math.pow(0.7, arguments.length ? k : 1); 302 return pv.rgb( 303 Math.max(0, Math.floor(k * this.r)), 304 Math.max(0, Math.floor(k * this.g)), 305 Math.max(0, Math.floor(k * this.b)), 306 this.a); 307 }; 308 309 /** 310 * Constructs a new HSL color with the specified values. 311 * 312 * @param {number} h the hue, an integer in [0, 360]. 313 * @param {number} s the saturation, a float in [0, 1]. 314 * @param {number} l the lightness, a float in [0, 1]. 315 * @param {number} [a] the opacity, a float in [0, 1]. 316 * @returns pv.Color.Hsl 317 */ 318 pv.hsl = function(h, s, l, a) { 319 return new pv.Color.Hsl(h, s, l, (arguments.length == 4) ? a : 1); 320 }; 321 322 /** 323 * Constructs a new HSL color with the specified values. 324 * 325 * @class Represents a color in HSL space. 326 * 327 * @param {number} h the hue, an integer in [0, 360]. 328 * @param {number} s the saturation, a float in [0, 1]. 329 * @param {number} l the lightness, a float in [0, 1]. 330 * @param {number} a the opacity, a float in [0, 1]. 331 * @extends pv.Color 332 */ 333 pv.Color.Hsl = function(h, s, l, a) { 334 pv.Color.call(this, "hsl(" + h + "," + (s * 100) + "%," + (l * 100) + "%)", a); 335 336 /** 337 * The hue, an integer in [0, 360]. 338 * 339 * @type number 340 */ 341 this.h = h; 342 343 /** 344 * The saturation, a float in [0, 1]. 345 * 346 * @type number 347 */ 348 this.s = s; 349 350 /** 351 * The lightness, a float in [0, 1]. 352 * 353 * @type number 354 */ 355 this.l = l; 356 357 /** 358 * The opacity, a float in [0, 1]. 359 * 360 * @type number 361 */ 362 this.a = a; 363 }; 364 pv.Color.Hsl.prototype = pv.extend(pv.Color); 365 366 /** 367 * Constructs a new HSL color with the same saturation, lightness and alpha as 368 * this color, and the specified hue. 369 * 370 * @param {number} h the hue, an integer in [0, 360]. 371 */ 372 pv.Color.Hsl.prototype.hue = function(h) { 373 return pv.hsl(h, this.s, this.l, this.a); 374 }; 375 376 /** 377 * Constructs a new HSL color with the same hue, lightness and alpha as this 378 * color, and the specified saturation. 379 * 380 * @param {number} s the saturation, a float in [0, 1]. 381 */ 382 pv.Color.Hsl.prototype.saturation = function(s) { 383 return pv.hsl(this.h, s, this.l, this.a); 384 }; 385 386 /** 387 * Constructs a new HSL color with the same hue, saturation and alpha as this 388 * color, and the specified lightness. 389 * 390 * @param {number} l the lightness, a float in [0, 1]. 391 */ 392 pv.Color.Hsl.prototype.lightness = function(l) { 393 return pv.hsl(this.h, this.s, l, this.a); 394 }; 395 396 /** 397 * Constructs a new HSL color with the same hue, saturation and lightness as 398 * this color, and the specified alpha. 399 * 400 * @param {number} a the opacity, a float in [0, 1]. 401 */ 402 pv.Color.Hsl.prototype.alpha = function(a) { 403 return pv.hsl(this.h, this.s, this.l, a); 404 }; 405 406 /** 407 * Returns the RGB color equivalent to this HSL color. 408 * 409 * @returns {pv.Color.Rgb} an RGB color. 410 */ 411 pv.Color.Hsl.prototype.rgb = function() { 412 var h = this.h, s = this.s, l = this.l; 413 414 /* Some simple corrections for h, s and l. */ 415 h = h % 360; if (h < 0) h += 360; 416 s = Math.max(0, Math.min(s, 1)); 417 l = Math.max(0, Math.min(l, 1)); 418 419 /* From FvD 13.37, CSS Color Module Level 3 */ 420 var m2 = (l <= .5) ? (l * (1 + s)) : (l + s - l * s); 421 var m1 = 2 * l - m2; 422 function v(h) { 423 if (h > 360) h -= 360; 424 else if (h < 0) h += 360; 425 if (h < 60) return m1 + (m2 - m1) * h / 60; 426 if (h < 180) return m2; 427 if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60; 428 return m1; 429 } 430 function vv(h) { 431 return Math.round(v(h) * 255); 432 } 433 434 return pv.rgb(vv(h + 120), vv(h), vv(h - 120), this.a); 435 }; 436 437 /** 438 * @private SVG color keywords, per CSS Color Module Level 3. 439 * 440 * @see <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">SVG color 441 * keywords</a> 442 */ 443 pv.Color.names = { 444 aliceblue: "#f0f8ff", 445 antiquewhite: "#faebd7", 446 aqua: "#00ffff", 447 aquamarine: "#7fffd4", 448 azure: "#f0ffff", 449 beige: "#f5f5dc", 450 bisque: "#ffe4c4", 451 black: "#000000", 452 blanchedalmond: "#ffebcd", 453 blue: "#0000ff", 454 blueviolet: "#8a2be2", 455 brown: "#a52a2a", 456 burlywood: "#deb887", 457 cadetblue: "#5f9ea0", 458 chartreuse: "#7fff00", 459 chocolate: "#d2691e", 460 coral: "#ff7f50", 461 cornflowerblue: "#6495ed", 462 cornsilk: "#fff8dc", 463 crimson: "#dc143c", 464 cyan: "#00ffff", 465 darkblue: "#00008b", 466 darkcyan: "#008b8b", 467 darkgoldenrod: "#b8860b", 468 darkgray: "#a9a9a9", 469 darkgreen: "#006400", 470 darkgrey: "#a9a9a9", 471 darkkhaki: "#bdb76b", 472 darkmagenta: "#8b008b", 473 darkolivegreen: "#556b2f", 474 darkorange: "#ff8c00", 475 darkorchid: "#9932cc", 476 darkred: "#8b0000", 477 darksalmon: "#e9967a", 478 darkseagreen: "#8fbc8f", 479 darkslateblue: "#483d8b", 480 darkslategray: "#2f4f4f", 481 darkslategrey: "#2f4f4f", 482 darkturquoise: "#00ced1", 483 darkviolet: "#9400d3", 484 deeppink: "#ff1493", 485 deepskyblue: "#00bfff", 486 dimgray: "#696969", 487 dimgrey: "#696969", 488 dodgerblue: "#1e90ff", 489 firebrick: "#b22222", 490 floralwhite: "#fffaf0", 491 forestgreen: "#228b22", 492 fuchsia: "#ff00ff", 493 gainsboro: "#dcdcdc", 494 ghostwhite: "#f8f8ff", 495 gold: "#ffd700", 496 goldenrod: "#daa520", 497 gray: "#808080", 498 green: "#008000", 499 greenyellow: "#adff2f", 500 grey: "#808080", 501 honeydew: "#f0fff0", 502 hotpink: "#ff69b4", 503 indianred: "#cd5c5c", 504 indigo: "#4b0082", 505 ivory: "#fffff0", 506 khaki: "#f0e68c", 507 lavender: "#e6e6fa", 508 lavenderblush: "#fff0f5", 509 lawngreen: "#7cfc00", 510 lemonchiffon: "#fffacd", 511 lightblue: "#add8e6", 512 lightcoral: "#f08080", 513 lightcyan: "#e0ffff", 514 lightgoldenrodyellow: "#fafad2", 515 lightgray: "#d3d3d3", 516 lightgreen: "#90ee90", 517 lightgrey: "#d3d3d3", 518 lightpink: "#ffb6c1", 519 lightsalmon: "#ffa07a", 520 lightseagreen: "#20b2aa", 521 lightskyblue: "#87cefa", 522 lightslategray: "#778899", 523 lightslategrey: "#778899", 524 lightsteelblue: "#b0c4de", 525 lightyellow: "#ffffe0", 526 lime: "#00ff00", 527 limegreen: "#32cd32", 528 linen: "#faf0e6", 529 magenta: "#ff00ff", 530 maroon: "#800000", 531 mediumaquamarine: "#66cdaa", 532 mediumblue: "#0000cd", 533 mediumorchid: "#ba55d3", 534 mediumpurple: "#9370db", 535 mediumseagreen: "#3cb371", 536 mediumslateblue: "#7b68ee", 537 mediumspringgreen: "#00fa9a", 538 mediumturquoise: "#48d1cc", 539 mediumvioletred: "#c71585", 540 midnightblue: "#191970", 541 mintcream: "#f5fffa", 542 mistyrose: "#ffe4e1", 543 moccasin: "#ffe4b5", 544 navajowhite: "#ffdead", 545 navy: "#000080", 546 oldlace: "#fdf5e6", 547 olive: "#808000", 548 olivedrab: "#6b8e23", 549 orange: "#ffa500", 550 orangered: "#ff4500", 551 orchid: "#da70d6", 552 palegoldenrod: "#eee8aa", 553 palegreen: "#98fb98", 554 paleturquoise: "#afeeee", 555 palevioletred: "#db7093", 556 papayawhip: "#ffefd5", 557 peachpuff: "#ffdab9", 558 peru: "#cd853f", 559 pink: "#ffc0cb", 560 plum: "#dda0dd", 561 powderblue: "#b0e0e6", 562 purple: "#800080", 563 red: "#ff0000", 564 rosybrown: "#bc8f8f", 565 royalblue: "#4169e1", 566 saddlebrown: "#8b4513", 567 salmon: "#fa8072", 568 sandybrown: "#f4a460", 569 seagreen: "#2e8b57", 570 seashell: "#fff5ee", 571 sienna: "#a0522d", 572 silver: "#c0c0c0", 573 skyblue: "#87ceeb", 574 slateblue: "#6a5acd", 575 slategray: "#708090", 576 slategrey: "#708090", 577 snow: "#fffafa", 578 springgreen: "#00ff7f", 579 steelblue: "#4682b4", 580 tan: "#d2b48c", 581 teal: "#008080", 582 thistle: "#d8bfd8", 583 tomato: "#ff6347", 584 turquoise: "#40e0d0", 585 violet: "#ee82ee", 586 wheat: "#f5deb3", 587 white: "#ffffff", 588 whitesmoke: "#f5f5f5", 589 yellow: "#ffff00", 590 yellowgreen: "#9acd32", 591 transparent: pv.Color.transparent = pv.rgb(0, 0, 0, 0) 592 }; 593 594 /* Initialized named colors. */ 595 (function() { 596 var names = pv.Color.names; 597 for (var name in names) names[name] = pv.color(names[name]); 598 })(); 599