1 /*
  2     Copyright 2008-2013
  3         Matthias Ehmann,
  4         Michael Gerhaeuser,
  5         Carsten Miller,
  6         Bianca Valentin,
  7         Alfred Wassermann,
  8         Peter Wilfahrt
  9 
 10     This file is part of JSXGraph.
 11 
 12     JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
 13 
 14     You can redistribute it and/or modify it under the terms of the
 15 
 16       * GNU Lesser General Public License as published by
 17         the Free Software Foundation, either version 3 of the License, or
 18         (at your option) any later version
 19       OR
 20       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
 21 
 22     JSXGraph is distributed in the hope that it will be useful,
 23     but WITHOUT ANY WARRANTY; without even the implied warranty of
 24     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 25     GNU Lesser General Public License for more details.
 26 
 27     You should have received a copy of the GNU Lesser General Public License and
 28     the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/>
 29     and <http://opensource.org/licenses/MIT/>.
 30  */
 31 
 32 
 33 /*global JXG: true, define: true*/
 34 
 35 /*jslint nomen: true, plusplus: true*/
 36 
 37 /* depends:
 38  jxg
 39  utils/type
 40  math/math
 41  */
 42 
 43 /**
 44  * Functions for color conversions. This was originally based on a class to parse color values by
 45  * Stoyan Stefanov <sstoo@gmail.com> (see http://www.phpied.com/rgb-color-parser-in-javascript/)
 46  */
 47 
 48 define(['jxg', 'utils/type', 'math/math'], function (JXG, Type, Mat) {
 49 
 50     "use strict";
 51 
 52     // private constants and helper functions
 53 
 54     // simple colors contains string color constants that can be used in various browser
 55     // in javascript
 56     var simpleColors = {
 57             aliceblue: 'f0f8ff',
 58             antiquewhite: 'faebd7',
 59             aqua: '00ffff',
 60             aquamarine: '7fffd4',
 61             azure: 'f0ffff',
 62             beige: 'f5f5dc',
 63             bisque: 'ffe4c4',
 64             black: '000000',
 65             blanchedalmond: 'ffebcd',
 66             blue: '0000ff',
 67             blueviolet: '8a2be2',
 68             brown: 'a52a2a',
 69             burlywood: 'deb887',
 70             cadetblue: '5f9ea0',
 71             chartreuse: '7fff00',
 72             chocolate: 'd2691e',
 73             coral: 'ff7f50',
 74             cornflowerblue: '6495ed',
 75             cornsilk: 'fff8dc',
 76             crimson: 'dc143c',
 77             cyan: '00ffff',
 78             darkblue: '00008b',
 79             darkcyan: '008b8b',
 80             darkgoldenrod: 'b8860b',
 81             darkgray: 'a9a9a9',
 82             darkgreen: '006400',
 83             darkkhaki: 'bdb76b',
 84             darkmagenta: '8b008b',
 85             darkolivegreen: '556b2f',
 86             darkorange: 'ff8c00',
 87             darkorchid: '9932cc',
 88             darkred: '8b0000',
 89             darksalmon: 'e9967a',
 90             darkseagreen: '8fbc8f',
 91             darkslateblue: '483d8b',
 92             darkslategray: '2f4f4f',
 93             darkturquoise: '00ced1',
 94             darkviolet: '9400d3',
 95             deeppink: 'ff1493',
 96             deepskyblue: '00bfff',
 97             dimgray: '696969',
 98             dodgerblue: '1e90ff',
 99             feldspar: 'd19275',
100             firebrick: 'b22222',
101             floralwhite: 'fffaf0',
102             forestgreen: '228b22',
103             fuchsia: 'ff00ff',
104             gainsboro: 'dcdcdc',
105             ghostwhite: 'f8f8ff',
106             gold: 'ffd700',
107             goldenrod: 'daa520',
108             gray: '808080',
109             green: '008000',
110             greenyellow: 'adff2f',
111             honeydew: 'f0fff0',
112             hotpink: 'ff69b4',
113             indianred : 'cd5c5c',
114             indigo : '4b0082',
115             ivory: 'fffff0',
116             khaki: 'f0e68c',
117             lavender: 'e6e6fa',
118             lavenderblush: 'fff0f5',
119             lawngreen: '7cfc00',
120             lemonchiffon: 'fffacd',
121             lightblue: 'add8e6',
122             lightcoral: 'f08080',
123             lightcyan: 'e0ffff',
124             lightgoldenrodyellow: 'fafad2',
125             lightgrey: 'd3d3d3',
126             lightgreen: '90ee90',
127             lightpink: 'ffb6c1',
128             lightsalmon: 'ffa07a',
129             lightseagreen: '20b2aa',
130             lightskyblue: '87cefa',
131             lightslateblue: '8470ff',
132             lightslategray: '778899',
133             lightsteelblue: 'b0c4de',
134             lightyellow: 'ffffe0',
135             lime: '00ff00',
136             limegreen: '32cd32',
137             linen: 'faf0e6',
138             magenta: 'ff00ff',
139             maroon: '800000',
140             mediumaquamarine: '66cdaa',
141             mediumblue: '0000cd',
142             mediumorchid: 'ba55d3',
143             mediumpurple: '9370d8',
144             mediumseagreen: '3cb371',
145             mediumslateblue: '7b68ee',
146             mediumspringgreen: '00fa9a',
147             mediumturquoise: '48d1cc',
148             mediumvioletred: 'c71585',
149             midnightblue: '191970',
150             mintcream: 'f5fffa',
151             mistyrose: 'ffe4e1',
152             moccasin: 'ffe4b5',
153             navajowhite: 'ffdead',
154             navy: '000080',
155             oldlace: 'fdf5e6',
156             olive: '808000',
157             olivedrab: '6b8e23',
158             orange: 'ffa500',
159             orangered: 'ff4500',
160             orchid: 'da70d6',
161             palegoldenrod: 'eee8aa',
162             palegreen: '98fb98',
163             paleturquoise: 'afeeee',
164             palevioletred: 'd87093',
165             papayawhip: 'ffefd5',
166             peachpuff: 'ffdab9',
167             peru: 'cd853f',
168             pink: 'ffc0cb',
169             plum: 'dda0dd',
170             powderblue: 'b0e0e6',
171             purple: '800080',
172             red: 'ff0000',
173             rosybrown: 'bc8f8f',
174             royalblue: '4169e1',
175             saddlebrown: '8b4513',
176             salmon: 'fa8072',
177             sandybrown: 'f4a460',
178             seagreen: '2e8b57',
179             seashell: 'fff5ee',
180             sienna: 'a0522d',
181             silver: 'c0c0c0',
182             skyblue: '87ceeb',
183             slateblue: '6a5acd',
184             slategray: '708090',
185             snow: 'fffafa',
186             springgreen: '00ff7f',
187             steelblue: '4682b4',
188             tan: 'd2b48c',
189             teal: '008080',
190             thistle: 'd8bfd8',
191             tomato: 'ff6347',
192             turquoise: '40e0d0',
193             violet: 'ee82ee',
194             violetred: 'd02090',
195             wheat: 'f5deb3',
196             white: 'ffffff',
197             whitesmoke: 'f5f5f5',
198             yellow: 'ffff00',
199             yellowgreen: '9acd32'
200         },
201         // array of color definition objects
202         colorDefs = [{
203             re: /^\s*rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*([\d\.]{1,3})\s*\)\s*$/,
204             example: ['rgba(123, 234, 45, 0.5)', 'rgba(255,234,245,1.0)'],
205             process: function (bits) {
206                 return [
207                     parseInt(bits[1], 10),
208                     parseInt(bits[2], 10),
209                     parseInt(bits[3], 10)
210                 ];
211             }
212         }, {
213             re: /^\s*rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)\s*$/,
214             example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
215             process: function (bits) {
216                 return [
217                     parseInt(bits[1], 10),
218                     parseInt(bits[2], 10),
219                     parseInt(bits[3], 10)
220                 ];
221             }
222         }, {
223             re: /^(\w{2})(\w{2})(\w{2})$/,
224             example: ['#00ff00', '336699'],
225             process: function (bits) {
226                 return [
227                     parseInt(bits[1], 16),
228                     parseInt(bits[2], 16),
229                     parseInt(bits[3], 16)
230                 ];
231             }
232         }, {
233             re: /^(\w{1})(\w{1})(\w{1})$/,
234             example: ['#fb0', 'f0f'],
235             process: function (bits) {
236                 return [
237                     parseInt(bits[1] + bits[1], 16),
238                     parseInt(bits[2] + bits[2], 16),
239                     parseInt(bits[3] + bits[3], 16)
240                 ];
241             }
242         }];
243 
244     /**
245      * Converts a valid HTML/CSS color string into a rgb value array. This is the base
246      * function for the following wrapper functions which only adjust the output to
247      * different flavors like an object, string or hex values.
248      * @param {String,Array,Number} color A valid HTML or CSS styled color value, e.g. '#12ab21', '#abc', 'black',
249      * or 'rgb(12, 132, 233)'. This can also be an array containing three color values either from 0.0 to 1.0 or
250      * from 0 to 255. They will be interpreted as red, green, and blue values. In case this is a number this method
251      * expects the parameters ag and ab.
252      * @param {Number} ag
253      * @param {Number} ab
254      * @returns {Array} RGB color values as an array [r, g, b] with values ranging from 0 to 255.
255      */
256     JXG.rgbParser = function (color, ag, ab) {
257         var color_string, channels, re, processor, bits, i,
258             r, g, b,
259             values = color,
260             testFloat;
261 
262         if (!Type.exists(color)) {
263             return [];
264         }
265 
266         if (Type.exists(ag) && Type.exists(ab)) {
267             values = [color, ag, ab];
268         }
269 
270         color_string = values;
271 
272         testFloat = false;
273         if (Type.isArray(color_string)) {
274             for (i = 0; i < 3; i++) {
275                 testFloat = testFloat || /\./.test(values[i].toString());
276             }
277 
278             for (i = 0; i < 3; i++) {
279                 testFloat = testFloat && (values[i] >= 0.0) && (values[i] <= 1.0);
280             }
281 
282             if (testFloat) {
283                 return [Math.ceil(values[0] * 255), Math.ceil(values[1] * 255), Math.ceil(values[2] * 255)];
284             }
285 
286             return values;
287         }
288 
289         if (typeof values === 'string') {
290             color_string = values;
291         }
292 
293         // strip any leading #
294         if (color_string.charAt(0) === '#') { // remove # if any
295             color_string = color_string.substr(1, 6);
296         }
297 
298         color_string = color_string.replace(/ /g, '').toLowerCase();
299 
300         // before getting into regexps, try simple matches
301         // and overwrite the input
302         color_string = simpleColors[color_string] || color_string;
303 
304         // search through the colorDefs definitions to find a match
305         for (i = 0; i < colorDefs.length; i++) {
306             re = colorDefs[i].re;
307             processor = colorDefs[i].process;
308             bits = re.exec(color_string);
309 
310             if (bits) {
311                 channels = processor(bits);
312                 r = channels[0];
313                 g = channels[1];
314                 b = channels[2];
315             }
316 
317         }
318 
319         if (isNaN(r) || isNaN(g) || isNaN(b)) {
320             return [];
321         }
322 
323         // validate/cleanup values
324         r = (r < 0 || isNaN(r)) ? 0 : ((r > 255) ? 255 : r);
325         g = (g < 0 || isNaN(g)) ? 0 : ((g > 255) ? 255 : g);
326         b = (b < 0 || isNaN(b)) ? 0 : ((b > 255) ? 255 : b);
327 
328         return [r, g, b];
329     };
330 
331     /**
332      * Converts a valid HTML/CSS color string into a string of the 'rgb(r, g, b)' format.
333      * @param {String,Array,Number} color A valid HTML or CSS styled color value, e.g. '#12ab21', '#abc', 'black',
334      * or 'rgb(12, 132, 233)'. This can also be an array containing three color values either from 0.0 to 1.0 or
335      * from 0 to 255. They will be interpreted as red, green, and blue values. In case this is a number this method
336      * expects the parameters ag and ab.
337      * @param {Number} ag
338      * @param {Number} ab
339      * @returns {String} A 'rgb(r, g, b)' formatted string
340      */
341     JXG.rgb2css = function (color, ag, ab) {
342         var r;
343 
344         r = JXG.rgbParser(color, ag, ab);
345 
346         return 'rgb(' + r[0] + ', ' + r[1] + ', ' + r[2] + ')';
347     };
348 
349     /**
350      * Converts a valid HTML/CSS color string into a HTML rgb string.
351      * @param {String,Array,Number} color A valid HTML or CSS styled color value, e.g. '#12ab21', '#abc', 'black',
352      * or 'rgb(12, 132, 233)'. This can also be an array containing three color values either from 0.0 to 1.0 or
353      * from 0 to 255. They will be interpreted as red, green, and blue values. In case this is a number this method
354      * expects the parameters ag and ab.
355      * @param {Number} ag
356      * @param {Number} ab
357      * @returns {String} A '#rrggbb' formatted string
358      */
359     JXG.rgb2hex = function (color, ag, ab) {
360         var r, g, b;
361 
362         r = JXG.rgbParser(color, ag, ab);
363         g = r[1];
364         b = r[2];
365         r = r[0];
366         r = r.toString(16);
367         g = g.toString(16);
368         b = b.toString(16);
369 
370         if (r.length === 1) {
371             r = '0' + r;
372         }
373 
374         if (g.length === 1) {
375             g = '0' + g;
376         }
377 
378         if (b.length === 1) {
379             b = '0' + b;
380         }
381 
382         return '#' + r + g + b;
383     };
384 
385     /**
386      * Converts a valid HTML/CSS color string from the '#rrggbb' format into the 'rgb(r, g, b)' format.
387      * @param {String} hex A valid HTML or CSS styled color value, e.g. '#12ab21', '#abc', or 'black'
388      * @deprecated Use {@link JXG#rgb2css} instead.
389      * @returns {String} A 'rgb(r, g, b)' formatted string
390      */
391     JXG.hex2rgb = function (hex) {
392         JXG.deprecated('JXG.hex2rgb()', 'JXG.rgb2css()');
393         return JXG.rgb2css(hex);
394     };
395 
396     /**
397      * Converts HSV color to RGB color.
398      * Based on C Code in "Computer Graphics -- Principles and Practice,"
399      * Foley et al, 1996, p. 593.
400      * See also http://www.efg2.com/Lab/Graphics/Colors/HSV.htm
401      * @param {Number} H value between 0 and 360
402      * @param {Number} S value between 0.0 (shade of gray) to 1.0 (pure color)
403      * @param {Number} V value between 0.0 (black) to 1.0 (white)
404      * @returns {String} RGB color string
405      */
406     JXG.hsv2rgb = function (H, S, V) {
407         var R, G, B, f, i, hTemp, p, q, t;
408 
409         H = ((H % 360.0) + 360.0) % 360;
410 
411         if (S === 0) {
412             if (isNaN(H) || H < Mat.eps) {
413                 R = V;
414                 G = V;
415                 B = V;
416             } else {
417                 return '#ffffff';
418             }
419         } else {
420             if (H >= 360) {
421                 hTemp = 0.0;
422             } else {
423                 hTemp = H;
424             }
425 
426             // h is now IN [0,6)
427             hTemp = hTemp / 60;
428             // largest integer <= h
429             i = Math.floor(hTemp);
430             // fractional part of h
431             f = hTemp - i;
432             p = V * (1.0 - S);
433             q = V * (1.0 - (S * f));
434             t = V * (1.0 - (S * (1.0 - f)));
435 
436             switch (i) {
437             case 0:
438                 R = V;
439                 G = t;
440                 B = p;
441                 break;
442             case 1:
443                 R = q;
444                 G = V;
445                 B = p;
446                 break;
447             case 2:
448                 R = p;
449                 G = V;
450                 B = t;
451                 break;
452             case 3:
453                 R = p;
454                 G = q;
455                 B = V;
456                 break;
457             case 4:
458                 R = t;
459                 G = p;
460                 B = V;
461                 break;
462             case 5:
463                 R = V;
464                 G = p;
465                 B = q;
466                 break;
467             }
468         }
469 
470         R = Math.round(R * 255).toString(16);
471         R = (R.length === 2) ? R : ((R.length === 1) ? '0' + R : '00');
472         G = Math.round(G * 255).toString(16);
473         G = (G.length === 2) ? G : ((G.length === 1) ? '0' + G : '00');
474         B = Math.round(B * 255).toString(16);
475         B = (B.length === 2) ? B : ((B.length === 1) ? '0' + B : '00');
476 
477         return ['#', R, G, B].join('');
478     };
479 
480     /**
481      * Converts a color from the RGB color space into the HSV space. Input can be any valid HTML/CSS color definition.
482      * @param {String,Array,Number} color A valid HTML or CSS styled color value, e.g. '#12ab21', '#abc', 'black',
483      * or 'rgb(12, 132, 233)'. This can also be an array containing three color values either from 0.0 to 1.0 or
484      * from 0 to 255. They will be interpreted as red, green, and blue values. In case this is a number this method
485      * expects the parameters ag and ab.
486      * @param {Number} ag
487      * @param {Number} ab
488      * @returns {Array} Contains the h, s, and v value in this order.
489      * @see http://zach.in.tu-clausthal.de/teaching/cg1_0708/folien/13_color_3_4up.pdf
490      */
491     JXG.rgb2hsv = function (color, ag, ab) {
492         var r, g, b, fr, fg, fb, fmax, fmin, h, s, v, max, min;
493 
494         r = JXG.rgbParser(color, ag, ab);
495 
496         g = r[1];
497         b = r[2];
498         r = r[0];
499         fr = r / 255.0;
500         fg = g / 255.0;
501         fb = b / 255.0;
502         max = Math.max(r, g, b);
503         min = Math.min(r, g, b);
504         fmax = max / 255.0;
505         fmin = min / 255.0;
506 
507         v = fmax;
508         s = 0.0;
509 
510         if (v > 0) {
511             s = (v - fmin) / v;
512         }
513 
514         h = 1.0 / (fmax - fmin);
515 
516         if (s > 0) {
517             if (max === r) {
518                 h = (fg - fb) * h;
519             } else if (max === g) {
520                 h = 2 + (fb - fr) * h;
521             } else {
522                 h = 4 + (fr - fg) * h;
523             }
524         }
525 
526         h *= 60;
527 
528         if (h < 0) {
529             h += 360;
530         }
531 
532         if (max === min) {
533             h = 0.0;
534         }
535 
536         return [h, s, v];
537     };
538 
539 
540     /**
541      * Converts a color from the RGB color space into the LMS space. Input can be any valid HTML/CSS color definition.
542      * @param {String,Array,Number} color A valid HTML or CSS styled color value, e.g. '#12ab21', '#abc', 'black',
543      * or 'rgb(12, 132, 233)'. This can also be an array containing three color values either from 0.0 to 1.0 or
544      * from 0 to 255. They will be interpreted as red, green, and blue values. In case this is a number this method
545      * expects the parameters ag and ab.
546      * @param {Number} ag
547      * @param {Number} ab
548      * @returns {Array} Contains the l, m, and s value in this order.
549      */
550     JXG.rgb2LMS = function (color, ag, ab) {
551         var r, g, b, l, m, s, ret,
552             // constants
553             matrix = [[0.05059983, 0.08585369, 0.00952420],
554                 [0.01893033, 0.08925308, 0.01370054],
555                 [0.00292202, 0.00975732, 0.07145979]];
556 
557         r = JXG.rgbParser(color, ag, ab);
558         g = r[1];
559         b = r[2];
560         r = r[0];
561 
562         // de-gamma
563         // Maybe this can be made faster by using a cache
564         r = Math.pow(r, 0.476190476);
565         g = Math.pow(g, 0.476190476);
566         b = Math.pow(b, 0.476190476);
567 
568         l = r * matrix[0][0] + g * matrix[0][1] + b * matrix[0][2];
569         m = r * matrix[1][0] + g * matrix[1][1] + b * matrix[1][2];
570         s = r * matrix[2][0] + g * matrix[2][1] + b * matrix[2][2];
571 
572         ret = [l, m, s];
573         ret.l = l;
574         ret.m = m;
575         ret.s = s;
576 
577         return ret;
578     };
579 
580     /**
581      * Convert color information from LMS to RGB color space.
582      * @param {Number} l
583      * @param {Number} m
584      * @param {Number} s
585      * @returns {Array} Contains the r, g, and b value in this order.
586      */
587     JXG.LMS2rgb = function (l, m, s) {
588         var r, g, b, ret,
589             // constants
590             matrix = [[30.830854, -29.832659, 1.610474],
591                 [-6.481468, 17.715578, -2.532642],
592                 [-0.375690, -1.199062, 14.273846]],
593 
594             // re-gamma, inspired by GIMP modules/display-filter-color-blind.c:
595             // Copyright (C) 2002-2003 Michael Natterer <mitch@gimp.org>,
596             //                         Sven Neumann <sven@gimp.org>,
597             //                         Robert Dougherty <bob@vischeck.com> and
598             //                         Alex Wade <alex@vischeck.com>
599             // This code is an implementation of an algorithm described by Hans Brettel,
600             // Francoise Vienot and John Mollon in the Journal of the Optical Society of
601             // America V14(10), pg 2647. (See http://vischeck.com/ for more info.)
602             lut_lookup = function (value) {
603                 var offset = 127, step = 64;
604 
605                 while (step > 0) {
606                     if (Math.pow(offset, 0.476190476) > value) {
607                         offset -= step;
608                     } else {
609                         if (Math.pow(offset + 1, 0.476190476) > value) {
610                             return offset;
611                         }
612 
613                         offset += step;
614                     }
615 
616                     step /= 2;
617                 }
618 
619                 /*  the algorithm above can't reach 255  */
620                 if (offset === 254 && 13.994955247 < value) {
621                     return 255;
622                 }
623 
624                 return offset;
625             };
626 
627         // transform back to rgb
628         r = l * matrix[0][0] + m * matrix[0][1] + s * matrix[0][2];
629         g = l * matrix[1][0] + m * matrix[1][1] + s * matrix[1][2];
630         b = l * matrix[2][0] + m * matrix[2][1] + s * matrix[2][2];
631 
632         r = lut_lookup(r);
633         g = lut_lookup(g);
634         b = lut_lookup(b);
635 
636         ret = [r, g, b];
637         ret.r = r;
638         ret.g = g;
639         ret.b = b;
640 
641         return ret;
642     };
643 
644     /**
645      * Splits a RGBA color value like #112233AA into it's RGB and opacity parts.
646      * @param {String} rgba A RGBA color value
647      * @returns {Array} An array containing the rgb color value in the first and the opacity in the second field.
648      */
649     JXG.rgba2rgbo = function (rgba) {
650         var opacity;
651 
652         if (rgba.length === 9 && rgba.charAt(0) === '#') {
653             opacity = parseInt(rgba.substr(7, 2).toUpperCase(), 16) / 255;
654             rgba = rgba.substr(0, 7);
655         } else {
656             opacity = 1;
657         }
658 
659         return [rgba, opacity];
660     };
661 
662     /**
663      * Generates a RGBA color value like #112233AA from it's RGB and opacity parts.
664      * @param {String} rgb A RGB color value.
665      * @param {Number} o The desired opacity >=0, <=1.
666      * @returns {String} The RGBA color value.
667      */
668     JXG.rgbo2rgba = function (rgb, o) {
669         var rgba;
670 
671         if (rgb === 'none') {
672             return rgb;
673         }
674 
675         rgba = Math.round(o * 255).toString(16);
676         if (rgba.length === 1) {
677             rgba = "0" + rgba;
678         }
679 
680         return rgb + rgba;
681     };
682 
683     /**
684      * Decolorizes the given color.
685      * @param {String} color HTML string containing the HTML color code.
686      * @returns {String} Returns a HTML color string
687      */
688     JXG.rgb2bw = function (color) {
689         var x, tmp, arr,
690             HexChars = "0123456789ABCDEF";
691 
692         if (color === 'none') {
693             return color;
694         }
695 
696         arr = JXG.rgbParser(color);
697         x = Math.floor(0.3 * arr[0] + 0.59 * arr[1] + 0.11 * arr[2]);
698 
699         // rgbParser and Math.floor ensure that x is 0 <= x <= 255.
700         // Bitwise operators can be used.
701         /*jslint bitwise: true*/
702         tmp = HexChars.charAt((x >> 4) & 0xf) + HexChars.charAt(x & 0xf);
703 
704         color = "#" + tmp + tmp + tmp;
705 
706         return color;
707     };
708 
709     /**
710      * Converts a color into how a colorblind human approximately would see it.
711      * @param {String} color HTML string containing the HTML color code.
712      * @param {String} deficiency The type of color blindness. Possible
713      * options are <i>protanopia</i>, <i>deuteranopia</i>, and <i>tritanopia</i>.
714      * @returns {String} Returns a HTML color string
715      */
716     JXG.rgb2cb = function (color, deficiency) {
717         var rgb, l, m, s, lms, tmp,
718             a1, b1, c1, a2, b2, c2,
719             inflection,
720             HexChars = "0123456789ABCDEF";
721 
722         if (color === 'none') {
723             return color;
724         }
725 
726         lms = JXG.rgb2LMS(color);
727         l = lms[0];
728         m = lms[1];
729         s = lms[2];
730 
731         deficiency = deficiency.toLowerCase();
732 
733         switch (deficiency) {
734         case "protanopia":
735             a1 = -0.06150039994295001;
736             b1 = 0.08277001656812001;
737             c1 = -0.013200141220000003;
738             a2 = 0.05858939668799999;
739             b2 = -0.07934519995360001;
740             c2 = 0.013289415272000003;
741             inflection = 0.6903216543277437;
742 
743             tmp = s / m;
744 
745             if (tmp < inflection) {
746                 l = -(b1 * m + c1 * s) / a1;
747             } else {
748                 l = -(b2 * m + c2 * s) / a2;
749             }
750             break;
751         case "tritanopia":
752             a1 = -0.00058973116217;
753             b1 = 0.007690316482;
754             c1 = -0.01011703519052;
755             a2 = 0.025495080838999994;
756             b2 = -0.0422740347;
757             c2 = 0.017005316784;
758             inflection = 0.8349489908460004;
759 
760             tmp = m / l;
761 
762             if (tmp < inflection) {
763                 s = -(a1 * l + b1 * m) / c1;
764             } else {
765                 s = -(a2 * l + b2 * m) / c2;
766             }
767             break;
768         default:
769             a1 = -0.06150039994295001;
770             b1 = 0.08277001656812001;
771             c1 = -0.013200141220000003;
772             a2 = 0.05858939668799999;
773             b2 = -0.07934519995360001;
774             c2 = 0.013289415272000003;
775             inflection = 0.5763833686400911;
776 
777             tmp = s / l;
778 
779             if (tmp < inflection) {
780                 m = -(a1 * l + c1 * s) / b1;
781             } else {
782                 m = -(a2 * l + c2 * s) / b2;
783             }
784             break;
785         }
786 
787         rgb = JXG.LMS2rgb(l, m, s);
788 
789         // LMS2rgb returns an array of values ranging from 0 to 255 (both included)
790         // bitwise operators are safe to use.
791         /*jslint bitwise: true*/
792         tmp = HexChars.charAt((rgb[0] >> 4) & 0xf) + HexChars.charAt(rgb[0] & 0xf);
793         color = "#" + tmp;
794         tmp = HexChars.charAt((rgb[1] >> 4) & 0xf) + HexChars.charAt(rgb[1] & 0xf);
795         color += tmp;
796         tmp = HexChars.charAt((rgb[2] >> 4) & 0xf) + HexChars.charAt(rgb[2] & 0xf);
797         color += tmp;
798 
799         return color;
800     };
801 
802     /**
803      * Determines highlight color to a given color. Done by reducing (or increasing) the opacity,
804      * @param {String} color HTML RGBA string containing the HTML color code.
805      * @returns {String} Returns a HTML RGBA color string
806      */
807     JXG.autoHighlight = function (colstr) {
808         var col = JXG.rgba2rgbo(colstr),
809             c = col[0],
810             opa = col[1];
811 
812         if (colstr.charAt(0) === '#') {
813             if (opa < 0.3) {
814                 opa *= 1.8;
815             } else {
816                 opa *= 0.4;
817             }
818 
819             return JXG.rgbo2rgba(c, opa);
820         }
821 
822         return colstr;
823     };
824 
825     return JXG;
826 });
827