joliclic

home

  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  }