joliclic
1 /* ***** BEGIN LICENSE BLOCK ***** 2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 3 * 4 * The contents of this file are subject to the Mozilla Public License Version 5 * 1.1 (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * http://www.mozilla.org/MPL/ 8 * 9 * Software distributed under the License is distributed on an "AS IS" basis, 10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 * for the specific language governing rights and limitations under the 12 * License. 13 * 14 * The Original Code is Khromaxul 15 * 16 * The Initial Developer of the Original Code is 17 * Nicolas Martin. 18 * Portions created by the Initial Developer are Copyright (C) 2009 19 * the Initial Developer. All Rights Reserved. 20 * 21 * Contributor(s): 22 * Nicolas Martin <joliclic@gmail.com> 23 * 24 * Alternatively, the contents of this file may be used under the terms of 25 * either the GNU General Public License Version 2 or later (the "GPL"), or 26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 27 * in which case the provisions of the GPL or the LGPL are applicable instead 28 * of those above. If you wish to allow use of your version of this file only 29 * under the terms of either the GPL or the LGPL, and not to allow others to 30 * use your version of this file under the terms of the MPL, indicate your 31 * decision by deleting the provisions above and replace them with the notice 32 * and other provisions required by the GPL or the LGPL. If you do not delete 33 * the provisions above, a recipient may use your version of this file under 34 * the terms of any one of the MPL, the GPL or the LGPL. 35 * 36 * ***** END LICENSE BLOCK ***** */ 37 38 if (!kh) var kh = {}; 39 40 /** 41 * @namespace 42 */ 43 kh.color = { 44 /** 45 * @param {Integer} i 46 * @param {Integer} min 47 * @param {Integer} max 48 * @returns {Integer} 49 */ 50 forceIntRange: function kh_color_forceIntRange(i, min, max) { 51 i = parseInt(i); 52 if (isNaN(i)) i = min; 53 if (i < min) i = min; 54 if (i > max) i = max; 55 // note: i = Math.min(max, Math.max(min, i)); is slowest X 2 56 57 return i; 58 } 59 } 60 61 /** 62 * r, g, b Integer in [0, 255] 63 * @constructor 64 */ 65 kh.color.RGB = function kh_color_RGB(r, g, b) { 66 /** the red value of the color in [0, 255] */ 67 this.r = kh.color.forceIntRange(r, 0, 255); 68 /** the green value of the color in [0, 255] */ 69 this.g = kh.color.forceIntRange(g, 0, 255); 70 /** the blue value of the color in [0, 255] */ 71 this.b = kh.color.forceIntRange(b, 0, 255); 72 } 73 74 /** 75 * h in [0, 360], s and v in [0, 100] 76 * @constructor 77 */ 78 kh.color.HSV = function kh_color_HSV(h, s, v) { 79 /** the hue of the color in [0, 360] */ 80 this.h = kh.color.forceIntRange(h, 0, 360); 81 /** the saturation of the color in [0, 100] */ 82 this.s = kh.color.forceIntRange(s, 0, 100); 83 /** the value of the color in [0, 100] */ 84 this.v = kh.color.forceIntRange(v, 0, 100); 85 } 86 87 /** 88 * h in [0, 360], s and l in [0, 100] 89 * @constructor 90 */ 91 kh.color.HSL = function kh_color_HSL(h, s, l) { 92 /** the hue of the color in [0, 360] */ 93 this.h = kh.color.forceIntRange(h, 0, 360); 94 /** the saturation of the color in [0, 100] */ 95 this.s = kh.color.forceIntRange(s, 0, 100); 96 /** the lightness of the color in [0, 100] */ 97 this.l = kh.color.forceIntRange(l, 0, 100); 98 } 99 100 kh.color.RGB.prototype = { 101 /** 102 * String conversion 103 * @returns {String} 104 */ 105 toString: function ColorRGB_toString() { 106 return "rgb(" + this.r + ', ' + this.g + ", " + this.b + ")"; 107 }, 108 109 /** 110 * HSV conversion 111 * @returns {HSV} object 112 */ 113 toHSV: function ColorRGB_toHSV() { 114 var hsv = new kh.color.HSV(); 115 116 var r = this.r / 255; 117 var g = this.g / 255; 118 var b = this.b / 255; 119 120 var max = Math.max(r, g, b); 121 var min = Math.min(r, g, b); 122 var delta = max - min; 123 124 hsv.v = Math.round(max * 100); 125 126 if (max == 0) 127 hsv.s = 0; 128 else 129 hsv.s = Math.round(100 * delta / max); 130 131 if (max == min) { 132 hsv.h = 0; 133 } else if (max == r) { 134 hsv.h = Math.round(60 * (g - b) / delta); 135 } else if (max == g) { 136 hsv.h = Math.round(120 + 60 * (b - r) / delta); 137 } else if (max == b) { 138 hsv.h = Math.round(240 + 60 * (r - g) / delta); 139 } 140 141 if (hsv.h < 0) hsv.h += 360; 142 143 return hsv; 144 }, 145 146 /** 147 * Hexadecimal representation 148 * 149 * @param {Boolean} threedigit - return short representation 150 * (3 digits, ex: #fff) if possible (default = false) 151 * @param {Boolean} sharp - return a # as first character (default = false) 152 * @returns {String} 153 */ 154 toHEXA: function ColorRGB_toHTML(a3digit, aSharp) { 155 var hex_r = this.hex(this.r); 156 var hex_g = this.hex(this.g); 157 var hex_b = this.hex(this.b); 158 159 if (a3digit && hex_r[0] == hex_r[1] && hex_g[0] == hex_g[1] 160 && hex_b[0] == hex_b[1]) { 161 hex_r = hex_r[0]; 162 hex_g = hex_g[0]; 163 hex_b = hex_b[0]; 164 } 165 166 if (aSharp) 167 return "#" + hex_r + hex_g + hex_b; 168 else 169 return hex_r + hex_g + hex_b; 170 }, 171 172 /** 173 * convert a integer in hexadecimal (should be < 256) 174 * @private 175 * @param {Integer} n 176 * @returns {String} 177 */ 178 hex: function ColorRGB_hex(n) { 179 var x = n.toString(16); 180 if (x.length == 1) 181 x = '0' + x; 182 183 return x; 184 }, 185 186 /** 187 * returns the integer nearest the value that be used in the short HTML 188 * representation (like in #fff) 189 * @param {Integer} n 190 * @returns {Integer} 191 */ 192 websafeInt: function ColorRGB_websafeInt(n) { 193 return Math.round(n / 51) * 51; 194 //return Math.round(n / 17) * 17; 195 }, 196 197 /** 198 * Indicate if the current color is websafe, 199 * http://en.wikipedia.org/wiki/Web_colors#Web-safe_colors 200 * @returns {Boolean} 201 */ 202 get isWebSafe() { 203 var str = this.toHEXA(true, false); 204 if (str.length == 3 && /[0369cf]{3}/i.test(str)) 205 return true; 206 207 return false; 208 }, 209 210 /** 211 * Returns the nearest color that is websafe, 212 * http://en.wikipedia.org/wiki/Web_colors#Web-safe_colors 213 * @returns {RGB} object 214 */ 215 toWebSafe: function ColorRGB_toWebSafe() { 216 if (this.isWebSafe) 217 return new kh.color.RGB(this.r, this.g, this.b); 218 else 219 return new kh.color.RGB(this.websafeInt(this.r), 220 this.websafeInt(this.g), 221 this.websafeInt(this.b)); 222 }, 223 224 /** 225 * HSL conversion 226 * @returns {HSL} object 227 */ 228 toHSL: function ColorRGB_toHSL() { 229 var hsl = new kh.color.HSL(); 230 231 var r = this.r / 255; 232 var g = this.g / 255; 233 var b = this.b / 255; 234 235 var max = Math.max(r, g, b); 236 var min = Math.min(r, g, b); 237 var delta = max - min; 238 239 var sum = max + min; 240 hsl.l = Math.round(sum * 50); 241 242 if (max == min) { 243 hsl.s = 0 244 } else { 245 if (hsl.l <= 50) 246 hsl.s = Math.round(100 * delta / sum); 247 else 248 hsl.s = Math.round(100 * delta / (2 - sum)); 249 } 250 251 if (max == min) { 252 hsl.h = 0; 253 } else if (max == r) { 254 hsl.h = Math.round(60 * (g - b) / delta); 255 } else if (max == g) { 256 hsl.h = Math.round(120 + 60 * (b - r) / delta); 257 } else if (max == b) { 258 hsl.h = Math.round(240 + 60 * (r - g) / delta); 259 } 260 261 if (hsl.h < 0) hsl.h += 360; 262 263 return hsl; 264 }, 265 266 /** 267 * the intensity of the color, like in YUV 268 * @returns {Float} in [0, 1] 269 */ 270 get intensity() { 271 //return 0.30 * this.r + 0.59 * this.g + 0.11 * this.b; 272 return 0.299 * this.r + 0.587 * this.g + 0.114 * this.b; 273 } 274 } 275 276 kh.color.HSV.prototype = { 277 /** 278 * String conversion 279 * @returns {String} 280 */ 281 toString: function ColorHSV_toString() { 282 return "hsv(" + this.h + ', ' + this.s + ", " + this.v + ")"; 283 }, 284 285 /** 286 * RGB conversion 287 * @returns {RGB} object 288 */ 289 toRGB: function ColorHSV_toRGB() { 290 var rgb = new kh.color.RGB(); 291 292 var hf; 293 if (this.h == 360) hf = 0; 294 else hf = this.h / 60; 295 296 var i = Math.floor(hf); 297 var f = hf - i; 298 299 var p = this.v / 100 * (1 - this.s / 100); 300 var q = this.v / 100 * (1 - f * this.s / 100); 301 var t = this.v / 100 * (1 - (1 - f) * this.s / 100); 302 303 p = Math.round(p * 255); 304 q = Math.round(q * 255); 305 t = Math.round(t * 255); 306 var v = Math.round(this.v * 2.55); 307 308 switch (i) { 309 case 0: 310 rgb.r = v; rgb.g = t; rgb.b = p; 311 break; 312 case 1: 313 rgb.r = q; rgb.g = v; rgb.b = p; 314 break; 315 case 2: 316 rgb.r = p; rgb.g = v; rgb.b = t; 317 break; 318 case 3: 319 rgb.r = p; rgb.g = q; rgb.b = v; 320 break; 321 case 4: 322 rgb.r = t; rgb.g = p; rgb.b = v; 323 break; 324 case 5: 325 rgb.r = v; rgb.g = p; rgb.b = q; 326 break; 327 } 328 329 return rgb; 330 }, 331 332 /** 333 * HSL conversion 334 * @returns {HSL} object 335 */ 336 toHSL: function ColorHSV_toHSL() { 337 var hsl = new kh.color.HSL(); 338 hsl.h = this.h; 339 340 var sr = this.s / 100, 341 vr = this.v / 100; 342 343 var l = vr * (1 - sr / 2); 344 hsl.l = Math.round(100 * l); 345 if (hsl.l <= 50) { 346 if (sr == 0) 347 hsl.s = 0; 348 else 349 hsl.s = Math.round(100 * sr / (2 - sr)); 350 } else { 351 if (l == 0) 352 hsl.s = 0; 353 else 354 hsl.s = Math.round(50 * sr * vr / (1 - l)); 355 } 356 357 return hsl; 358 } 359 } 360 361 kh.color.HSL.prototype = { 362 /** 363 * String conversion 364 * @param {Boolean} aNoPercent don't add '%' to s and l properties, 365 * optional, default = false. 366 * @returns {String} 367 */ 368 toString: function ColorHSL_toString(aNoPercent) { 369 if (aNoPercent) 370 return "hsl(" + this.h + ', ' + this.s + ", " + this.l + ")"; 371 372 return "hsl(" + this.h + ', ' + this.s + "%, " + this.l + "%)"; 373 }, 374 375 /** 376 * RGB conversion 377 * @returns {RGB} object 378 */ 379 toRGB: function ColorHSL_toRGB() { 380 var rgb = new kh.color.RGB(); 381 382 if (this.s == 0) { 383 // gray 384 rgb.r = rgb.g = rgb.b = Math.round(this.l * 2.55); 385 386 return rgb; 387 } 388 389 var hr = this.h / 60, 390 sr = this.s / 100, 391 lr = this.l / 100; 392 393 var v2; 394 if (this.l < 50) 395 v2 = lr * (1 + sr); 396 else 397 v2 = lr + sr - lr * sr; 398 399 var v1 = 2 * lr - v2; 400 401 rgb.r = Math.round(255 * this.h2rgb(v1, v2, (hr + 2))); 402 rgb.g = Math.round(255 * this.h2rgb(v1, v2, hr)); 403 rgb.b = Math.round(255 * this.h2rgb(v1, v2, (hr - 2))); 404 405 return rgb; 406 }, 407 408 /** 409 * see http://www.easyrgb.com/index.php?X=MATH&H=19#text19 410 * @param {Float} aV1 411 * @param {Float} aV2 412 * @param {Float} aH 413 * 414 * @returns {Int} 415 */ 416 h2rgb: function ColorHSL_hue2rgb(aV1, aV2, aH) { 417 if (aH < 0) aH += 6; 418 if (aH > 6) aH -= 6; 419 420 //alert(aH) 421 if (aH < 1) 422 return aV1 + (aV2 - aV1) * aH; 423 if (aH < 3) 424 return aV2; 425 if (aH < 4) 426 return aV1 + (aV2 - aV1) * (4 - aH); 427 428 return aV1; 429 }, 430 431 /** 432 * HSV conversion 433 * @returns {HSV} object 434 */ 435 toHSV: function ColorHSL_toHSV() { 436 var hsv = new kh.color.HSV(); 437 hsv.h = this.h; 438 439 var sr = this.s / 100, 440 lr = this.l / 100; 441 442 if (this.l <= 50) { 443 hsv.s = Math.round(200 * sr / (1 - sr)); 444 hsv.v = Math.round(this.l * (1 + sr)); 445 } else { 446 hsv.v = Math.round(100 * (sr * (1 - lr) + lr)); 447 if (hsv.v == 0) { 448 hsv.s = 0; 449 } else { 450 var a = sr * (1 - lr); 451 hsv.s = Math.round(200 * a / (a + lr)); 452 } 453 } 454 455 return hsv; 456 } 457 } 458 459 /** 460 * Returns a instance of RGB object, from a string parameter.<br /> 461 * <pre> 462 * Recognized strings: 463 * 464 * * 6 digits hexadecimal, begining with a sharp (#) or not (ex: #ffffff, ffffff) 465 * * 3 digits hexadecimal, begining with a sharp (#) or not (ex: #fff, fff) 466 * * rgb notation with integer (ex: rgb(0, 100, 255)) 467 * * rgb notation with percentage (ex: rgb(0%, 50%, 100%)) 468 * * hsv notation (ex: hsv(200, 50, 50)) 469 * * hsl notation (ex: hsl(200, 50, 50) or hsl(200, 50%, 50%)) 470 * * HTML 4 names 471 * * CSS 3 names. Optional, you just need to link to the file 'kh-csscolors.js' in your document.<br /> 472 * </pre> 473 * If no conversion can be done, a default RGB is returned (rgb(0,0,0) = black)<br /> 474 * 475 * <pre> 476 * TODO: 477 * defined in CSS 3 : <del>hsl(n,n,n)</del> hsla(n,n,n,n) rgba(n, n, n, n) 478 * <del>named svg colors ?</del> 479 * </pre> 480 * 481 * @param {String} aStr 482 * @returns {RGB} object 483 */ 484 kh.color.cssToRGB = function kh_color_cssToRGB(aStr) { 485 var r, g, b; 486 487 var re, res; 488 489 // 6 digits hexadecimal 490 re = /^\s*#?([\dabcdef]{2})([\dabcdef]{2})([\dabcdef]{2})\s*/i; 491 res = re.exec(aStr); 492 if (res) { 493 r = parseInt(res[1].toUpperCase(), 16); 494 g = parseInt(res[2].toUpperCase(), 16); 495 b = parseInt(res[3].toUpperCase(), 16); 496 497 return new kh.color.RGB(r, g, b); 498 } 499 500 // 3 digits hexadecimal 501 re = /^\s*#?([\dabcdef])([\dabcdef])([\dabcdef])\s*/i; 502 res = re.exec(aStr); 503 if (res) { 504 r = res[1].toUpperCase(); 505 r += "" + r; 506 r = parseInt(r, 16); 507 508 g = res[2].toUpperCase(); 509 g += "" + g; 510 g = parseInt(g, 16); 511 512 b = res[3].toUpperCase(); 513 b += "" + b; 514 b = parseInt(b, 16); 515 516 return new kh.color.RGB(r, g, b); 517 } 518 519 // rgb(n, n, n) 520 re = /^\s*rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)\s*/; 521 res = re.exec(aStr); 522 if (res) { 523 return new kh.color.RGB(res[1], res[2], res[3]); 524 } 525 526 // rgb(n%, n%, n%) 527 re = /^\s*rgb\(\s*(\d+(?:\.\d*)?)%\s*,\s*(\d+(?:\.\d*)?)%\s*,\s*(\d+(?:\.\d*)?)%\s*\)\s*/; 528 res = re.exec(aStr); 529 if (res) { 530 r = parseFloat(res[1]); 531 g = parseFloat(res[2]); 532 b = parseFloat(res[3]); 533 534 r = Math.round(r * 255); 535 g = Math.round(g * 255); 536 b = Math.round(b * 255); 537 538 return new kh.color.RGB(r, g, b); 539 } 540 541 // hsv(n, n, n) 542 re = /^\s*hsv\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)\s*/; 543 res = re.exec(aStr); 544 if (res) { 545 return new kh.color.HSV(res[1], res[2], res[3]).toRGB(); 546 } 547 548 // hsl(n, n, n) and hsl(n, n%, n%) 549 re = /^\s*hsl\(\s*(\d{1,3})\s*,\s*(\d+(?:\.\d*)?)%?\s*,\s*(\d+(?:\.\d*)?)%?\s*\)\s*/; 550 res = re.exec(aStr); 551 if (res) { 552 return new kh.color.HSL(res[1], res[2], res[3]).toRGB(); 553 } 554 555 // named HTML 4 colors 556 re = /^\s*(\w+)/; 557 res = re.exec(aStr); 558 if (res) { 559 var name = res[1].toLowerCase(); 560 if (KH_NAMED_HTML4_COLOR[name]) { 561 var c = KH_NAMED_HTML4_COLOR[name]; 562 563 //return new kh.color.RGB(c.r, c.g, c.b); 564 return new kh.color.RGB(c[0], c[1], c[2]); 565 } 566 567 // CSS 3 extended list (SVG), if the definition is available 568 if (typeof(KH_NAMED_CSS_COLOR) != "undefined" 569 && KH_NAMED_CSS_COLOR[name]) { 570 var c = KH_NAMED_CSS_COLOR[name]; 571 572 return new kh.color.RGB(c[0], c[1], c[2]); 573 } 574 } 575 576 return new kh.color.RGB(); 577 } 578 579 /** 580 * r,g,b values of the W3C HTML color names 581 * 582 * @constant 583 */ 584 const KH_NAMED_HTML4_COLOR = { 585 // name: [r, g, b] 586 aqua : [ 0, 255, 255], 587 black : [ 0, 0, 0], 588 blue : [ 0, 0, 255], 589 fuchsia: [255, 0, 255], 590 gray : [128, 128, 128], 591 green : [ 0, 128, 0], 592 lime : [ 0, 255, 0], 593 maroon : [ 0, 128, 0], 594 navy : [ 0, 0, 128], 595 olive : [128, 128, 0], 596 purple : [128, 0, 128], 597 red : [255, 0, 0], 598 silver : [192, 192, 192], 599 teal : [ 0, 128, 128], 600 white : [255, 255, 255], 601 yellow : [255, 255, 0] 602 }