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