5 See <http://mochikit.com/> for documentation, downloads, license, etc.
7 (c) 2005 Bob Ippolito and others. All rights Reserved.
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide('MochiKit.Color');
13 dojo.require('MochiKit.Base');
14 dojo.require('MochiKit.DOM');
15 dojo.require('MochiKit.Style');
18 if (typeof(JSAN) != 'undefined') {
19 JSAN.use("MochiKit.Base", []);
20 JSAN.use("MochiKit.DOM", []);
21 JSAN.use("MochiKit.Style", []);
25 if (typeof(MochiKit.Base) == 'undefined') {
29 throw "MochiKit.Color depends on MochiKit.Base";
33 if (typeof(MochiKit.DOM) == 'undefined') {
37 throw "MochiKit.Color depends on MochiKit.DOM";
41 if (typeof(MochiKit.Style) == 'undefined') {
45 throw "MochiKit.Color depends on MochiKit.Style";
48 if (typeof(MochiKit.Color) == "undefined") {
52 MochiKit.Color.NAME = "MochiKit.Color";
53 MochiKit.Color.VERSION = "1.4";
55 MochiKit.Color.__repr__ = function () {
56 return "[" + this.NAME + " " + this.VERSION + "]";
59 MochiKit.Color.toString = function () {
60 return this.__repr__();
64 /** @id MochiKit.Color.Color */
65 MochiKit.Color.Color = function (red, green, blue, alpha) {
66 if (typeof(alpha) == 'undefined' || alpha === null) {
80 MochiKit.Color.Color.prototype = {
82 __class__: MochiKit.Color.Color,
84 /** @id MochiKit.Color.Color.prototype.colorWithAlpha */
85 colorWithAlpha: function (alpha) {
87 var m = MochiKit.Color;
88 return m.Color.fromRGB(rgb.r, rgb.g, rgb.b, alpha);
91 /** @id MochiKit.Color.Color.prototype.colorWithHue */
92 colorWithHue: function (hue) {
93 // get an HSL model, and set the new hue...
94 var hsl = this.asHSL();
96 var m = MochiKit.Color;
97 // convert back to RGB...
98 return m.Color.fromHSL(hsl);
101 /** @id MochiKit.Color.Color.prototype.colorWithSaturation */
102 colorWithSaturation: function (saturation) {
103 // get an HSL model, and set the new hue...
104 var hsl = this.asHSL();
106 var m = MochiKit.Color;
107 // convert back to RGB...
108 return m.Color.fromHSL(hsl);
111 /** @id MochiKit.Color.Color.prototype.colorWithLightness */
112 colorWithLightness: function (lightness) {
113 // get an HSL model, and set the new hue...
114 var hsl = this.asHSL();
116 var m = MochiKit.Color;
117 // convert back to RGB...
118 return m.Color.fromHSL(hsl);
121 /** @id MochiKit.Color.Color.prototype.darkerColorWithLevel */
122 darkerColorWithLevel: function (level) {
123 var hsl = this.asHSL();
124 hsl.l = Math.max(hsl.l - level, 0);
125 var m = MochiKit.Color;
126 return m.Color.fromHSL(hsl);
129 /** @id MochiKit.Color.Color.prototype.lighterColorWithLevel */
130 lighterColorWithLevel: function (level) {
131 var hsl = this.asHSL();
132 hsl.l = Math.min(hsl.l + level, 1);
133 var m = MochiKit.Color;
134 return m.Color.fromHSL(hsl);
137 /** @id MochiKit.Color.Color.prototype.blendedColor */
138 blendedColor: function (other, /* optional */ fraction) {
139 if (typeof(fraction) == 'undefined' || fraction === null) {
142 var sf = 1.0 - fraction;
146 return MochiKit.Color.Color.fromRGB(
147 (s.r * sf) + (d.r * df),
148 (s.g * sf) + (d.g * df),
149 (s.b * sf) + (d.b * df),
150 (s.a * sf) + (d.a * df)
154 /** @id MochiKit.Color.Color.prototype.compareRGB */
155 compareRGB: function (other) {
156 var a = this.asRGB();
157 var b = other.asRGB();
158 return MochiKit.Base.compare(
159 [a.r, a.g, a.b, a.a],
164 /** @id MochiKit.Color.Color.prototype.isLight */
165 isLight: function () {
166 return this.asHSL().b > 0.5;
169 /** @id MochiKit.Color.Color.prototype.isDark */
170 isDark: function () {
171 return (!this.isLight());
174 /** @id MochiKit.Color.Color.prototype.toHSLString */
175 toHSLString: function () {
176 var c = this.asHSL();
177 var ccc = MochiKit.Color.clampColorComponent;
178 var rval = this._hslString;
181 ccc(c.h, 360).toFixed(0)
182 + "," + ccc(c.s, 100).toPrecision(4) + "%"
183 + "," + ccc(c.l, 100).toPrecision(4) + "%"
188 rval = "hsl(" + mid + ")";
193 rval = "hsla(" + mid + "," + a + ")";
195 this._hslString = rval;
200 /** @id MochiKit.Color.Color.prototype.toRGBString */
201 toRGBString: function () {
203 var ccc = MochiKit.Color.clampColorComponent;
204 var rval = this._rgbString;
207 ccc(c.r, 255).toFixed(0)
208 + "," + ccc(c.g, 255).toFixed(0)
209 + "," + ccc(c.b, 255).toFixed(0)
212 rval = "rgba(" + mid + "," + c.a + ")";
214 rval = "rgb(" + mid + ")";
216 this._rgbString = rval;
221 /** @id MochiKit.Color.Color.prototype.asRGB */
223 return MochiKit.Base.clone(this.rgb);
226 /** @id MochiKit.Color.Color.prototype.toHexString */
227 toHexString: function () {
228 var m = MochiKit.Color;
230 var ccc = MochiKit.Color.clampColorComponent;
231 var rval = this._hexString;
234 m.toColorPart(ccc(c.r, 255)) +
235 m.toColorPart(ccc(c.g, 255)) +
236 m.toColorPart(ccc(c.b, 255))
238 this._hexString = rval;
243 /** @id MochiKit.Color.Color.prototype.asHSV */
247 if (typeof(hsv) == 'undefined' || hsv === null) {
248 hsv = MochiKit.Color.rgbToHSV(this.rgb);
251 return MochiKit.Base.clone(hsv);
254 /** @id MochiKit.Color.Color.prototype.asHSL */
258 if (typeof(hsl) == 'undefined' || hsl === null) {
259 hsl = MochiKit.Color.rgbToHSL(this.rgb);
262 return MochiKit.Base.clone(hsl);
265 /** @id MochiKit.Color.Color.prototype.toString */
266 toString: function () {
267 return this.toRGBString();
270 /** @id MochiKit.Color.Color.prototype.repr */
273 var col = [c.r, c.g, c.b, c.a];
274 return this.__class__.NAME + "(" + col.join(", ") + ")";
279 // Constructor methods
281 MochiKit.Base.update(MochiKit.Color.Color, {
282 /** @id MochiKit.Color.Color.fromRGB */
283 fromRGB: function (red, green, blue, alpha) {
284 // designated initializer
285 var Color = MochiKit.Color.Color;
286 if (arguments.length == 1) {
291 if (typeof(rgb.a) == 'undefined') {
297 return new Color(red, green, blue, alpha);
300 /** @id MochiKit.Color.Color.fromHSL */
301 fromHSL: function (hue, saturation, lightness, alpha) {
302 var m = MochiKit.Color;
303 return m.Color.fromRGB(m.hslToRGB.apply(m, arguments));
306 /** @id MochiKit.Color.Color.fromHSV */
307 fromHSV: function (hue, saturation, value, alpha) {
308 var m = MochiKit.Color;
309 return m.Color.fromRGB(m.hsvToRGB.apply(m, arguments));
312 /** @id MochiKit.Color.Color.fromName */
313 fromName: function (name) {
314 var Color = MochiKit.Color.Color;
315 // Opera 9 seems to "quote" named colors(?!)
316 if (name.charAt(0) == '"') {
317 name = name.substr(1, name.length - 2);
319 var htmlColor = Color._namedColors[name.toLowerCase()];
320 if (typeof(htmlColor) == 'string') {
321 return Color.fromHexString(htmlColor);
322 } else if (name == "transparent") {
323 return Color.transparentColor();
328 /** @id MochiKit.Color.Color.fromString */
329 fromString: function (colorString) {
330 var self = MochiKit.Color.Color;
331 var three = colorString.substr(0, 3);
332 if (three == "rgb") {
333 return self.fromRGBString(colorString);
334 } else if (three == "hsl") {
335 return self.fromHSLString(colorString);
336 } else if (colorString.charAt(0) == "#") {
337 return self.fromHexString(colorString);
339 return self.fromName(colorString);
343 /** @id MochiKit.Color.Color.fromHexString */
344 fromHexString: function (hexCode) {
345 if (hexCode.charAt(0) == '#') {
346 hexCode = hexCode.substring(1);
350 if (hexCode.length == 3) {
351 for (i = 0; i < 3; i++) {
352 hex = hexCode.substr(i, 1);
353 components.push(parseInt(hex + hex, 16) / 255.0);
356 for (i = 0; i < 6; i += 2) {
357 hex = hexCode.substr(i, 2);
358 components.push(parseInt(hex, 16) / 255.0);
361 var Color = MochiKit.Color.Color;
362 return Color.fromRGB.apply(Color, components);
366 _fromColorString: function (pre, method, scales, colorCode) {
367 // parses either HSL or RGB
368 if (colorCode.indexOf(pre) === 0) {
369 colorCode = colorCode.substring(colorCode.indexOf("(", 3) + 1, colorCode.length - 1);
371 var colorChunks = colorCode.split(/\s*,\s*/);
372 var colorFloats = [];
373 for (var i = 0; i < colorChunks.length; i++) {
374 var c = colorChunks[i];
376 var three = c.substring(c.length - 3);
377 if (c.charAt(c.length - 1) == '%') {
378 val = 0.01 * parseFloat(c.substring(0, c.length - 1));
379 } else if (three == "deg") {
380 val = parseFloat(c) / 360.0;
381 } else if (three == "rad") {
382 val = parseFloat(c) / (Math.PI * 2);
384 val = scales[i] * parseFloat(c);
386 colorFloats.push(val);
388 return this[method].apply(this, colorFloats);
391 /** @id MochiKit.Color.Color.fromComputedStyle */
392 fromComputedStyle: function (elem, style) {
393 var d = MochiKit.DOM;
394 var cls = MochiKit.Color.Color;
395 for (elem = d.getElement(elem); elem; elem = elem.parentNode) {
396 var actualColor = MochiKit.Style.getStyle.apply(d, arguments);
400 var color = cls.fromString(actualColor);
404 if (color.asRGB().a > 0) {
411 /** @id MochiKit.Color.Color.fromBackground */
412 fromBackground: function (elem) {
413 var cls = MochiKit.Color.Color;
414 return cls.fromComputedStyle(
415 elem, "backgroundColor", "background-color") || cls.whiteColor();
418 /** @id MochiKit.Color.Color.fromText */
419 fromText: function (elem) {
420 var cls = MochiKit.Color.Color;
421 return cls.fromComputedStyle(
422 elem, "color", "color") || cls.blackColor();
425 /** @id MochiKit.Color.Color.namedColors */
426 namedColors: function () {
427 return MochiKit.Base.clone(MochiKit.Color.Color._namedColors);
432 // Module level functions
434 MochiKit.Base.update(MochiKit.Color, {
435 /** @id MochiKit.Color.clampColorComponent */
436 clampColorComponent: function (v, scale) {
440 } else if (v > scale) {
447 _hslValue: function (n1, n2, hue) {
450 } else if (hue < 0.0) {
455 val = n1 + (n2 - n1) * hue;
456 } else if (hue < 3.0) {
458 } else if (hue < 4.0) {
459 val = n1 + (n2 - n1) * (4.0 - hue);
466 /** @id MochiKit.Color.hsvToRGB */
467 hsvToRGB: function (hue, saturation, value, alpha) {
468 if (arguments.length == 1) {
478 if (saturation === 0) {
483 var i = Math.floor(hue * 6);
484 var f = (hue * 6) - i;
485 var p = value * (1 - saturation);
486 var q = value * (1 - (saturation * f));
487 var t = value * (1 - (saturation * (1 - f)));
489 case 1: red = q; green = value; blue = p; break;
490 case 2: red = p; green = value; blue = t; break;
491 case 3: red = p; green = q; blue = value; break;
492 case 4: red = t; green = p; blue = value; break;
493 case 5: red = value; green = p; blue = q; break;
494 case 6: // fall through
495 case 0: red = value; green = t; blue = p; break;
506 /** @id MochiKit.Color.hslToRGB */
507 hslToRGB: function (hue, saturation, lightness, alpha) {
508 if (arguments.length == 1) {
518 if (saturation === 0) {
524 if (lightness <= 0.5) {
525 m2 = lightness * (1.0 + saturation);
527 m2 = lightness + saturation - (lightness * saturation);
529 var m1 = (2.0 * lightness) - m2;
530 var f = MochiKit.Color._hslValue;
532 red = f(m1, m2, h6 + 2);
533 green = f(m1, m2, h6);
534 blue = f(m1, m2, h6 - 2);
544 /** @id MochiKit.Color.rgbToHSV */
545 rgbToHSV: function (red, green, blue, alpha) {
546 if (arguments.length == 1) {
553 var max = Math.max(Math.max(red, green), blue);
554 var min = Math.min(Math.min(red, green), blue);
562 var delta = (max - min);
563 saturation = delta / max;
566 hue = (green - blue) / delta;
567 } else if (green == max) {
568 hue = 2 + ((blue - red) / delta);
570 hue = 4 + ((red - green) / delta);
588 /** @id MochiKit.Color.rgbToHSL */
589 rgbToHSL: function (red, green, blue, alpha) {
590 if (arguments.length == 1) {
597 var max = Math.max(red, Math.max(green, blue));
598 var min = Math.min(red, Math.min(green, blue));
601 var lightness = (max + min) / 2.0;
602 var delta = max - min;
607 if (lightness <= 0.5) {
608 saturation = delta / (max + min);
610 saturation = delta / (2 - max - min);
613 hue = (green - blue) / delta;
614 } else if (green == max) {
615 hue = 2 + ((blue - red) / delta);
617 hue = 4 + ((red - green) / delta);
636 /** @id MochiKit.Color.toColorPart */
637 toColorPart: function (num) {
638 num = Math.round(num);
639 var digits = num.toString(16);
646 __new__: function () {
647 var m = MochiKit.Base;
648 /** @id MochiKit.Color.fromRGBString */
649 this.Color.fromRGBString = m.bind(
650 this.Color._fromColorString, this.Color, "rgb", "fromRGB",
651 [1.0/255.0, 1.0/255.0, 1.0/255.0, 1]
653 /** @id MochiKit.Color.fromHSLString */
654 this.Color.fromHSLString = m.bind(
655 this.Color._fromColorString, this.Color, "hsl", "fromHSL",
656 [1.0/360.0, 0.01, 0.01, 1]
659 var third = 1.0 / 3.0;
660 /** @id MochiKit.Color.colors */
662 // NSColor colors plus transparent
663 /** @id MochiKit.Color.blackColor */
665 /** @id MochiKit.Color.blueColor */
667 /** @id MochiKit.Color.brownColor */
668 brown: [0.6, 0.4, 0.2],
669 /** @id MochiKit.Color.cyanColor */
671 /** @id MochiKit.Color.darkGrayColor */
672 darkGray: [third, third, third],
673 /** @id MochiKit.Color.grayColor */
674 gray: [0.5, 0.5, 0.5],
675 /** @id MochiKit.Color.greenColor */
677 /** @id MochiKit.Color.lightGrayColor */
678 lightGray: [2 * third, 2 * third, 2 * third],
679 /** @id MochiKit.Color.magentaColor */
681 /** @id MochiKit.Color.orangeColor */
683 /** @id MochiKit.Color.purpleColor */
684 purple: [0.5, 0, 0.5],
685 /** @id MochiKit.Color.redColor */
687 /** @id MochiKit.Color.transparentColor */
688 transparent: [0, 0, 0, 0],
689 /** @id MochiKit.Color.whiteColor */
691 /** @id MochiKit.Color.yellowColor */
695 var makeColor = function (name, r, g, b, a) {
696 var rval = this.fromRGB(r, g, b, a);
697 this[name] = function () { return rval; };
701 for (var k in colors) {
702 var name = k + "Color";
703 var bindArgs = m.concat(
704 [makeColor, this.Color, name],
707 this.Color[name] = m.bind.apply(null, bindArgs);
710 var isColor = function () {
711 for (var i = 0; i < arguments.length; i++) {
712 if (!(arguments[i] instanceof Color)) {
719 var compareColor = function (a, b) {
720 return a.compareRGB(b);
723 m.nameFunctions(this);
725 m.registerComparator(this.Color.NAME, isColor, compareColor);
728 ":common": this.EXPORT,
729 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
735 MochiKit.Color.EXPORT = [
739 MochiKit.Color.EXPORT_OK = [
740 "clampColorComponent",
748 MochiKit.Color.__new__();
750 MochiKit.Base._exportSymbols(this, MochiKit.Color);
752 // Full table of css3 X11 colors <http://www.w3.org/TR/css3-color/#X11COLORS>
754 MochiKit.Color.Color._namedColors = {
755 aliceblue: "#f0f8ff",
756 antiquewhite: "#faebd7",
758 aquamarine: "#7fffd4",
763 blanchedalmond: "#ffebcd",
765 blueviolet: "#8a2be2",
767 burlywood: "#deb887",
768 cadetblue: "#5f9ea0",
769 chartreuse: "#7fff00",
770 chocolate: "#d2691e",
772 cornflowerblue: "#6495ed",
778 darkgoldenrod: "#b8860b",
780 darkgreen: "#006400",
782 darkkhaki: "#bdb76b",
783 darkmagenta: "#8b008b",
784 darkolivegreen: "#556b2f",
785 darkorange: "#ff8c00",
786 darkorchid: "#9932cc",
788 darksalmon: "#e9967a",
789 darkseagreen: "#8fbc8f",
790 darkslateblue: "#483d8b",
791 darkslategray: "#2f4f4f",
792 darkslategrey: "#2f4f4f",
793 darkturquoise: "#00ced1",
794 darkviolet: "#9400d3",
796 deepskyblue: "#00bfff",
799 dodgerblue: "#1e90ff",
800 firebrick: "#b22222",
801 floralwhite: "#fffaf0",
802 forestgreen: "#228b22",
804 gainsboro: "#dcdcdc",
805 ghostwhite: "#f8f8ff",
807 goldenrod: "#daa520",
810 greenyellow: "#adff2f",
814 indianred: "#cd5c5c",
819 lavenderblush: "#fff0f5",
820 lawngreen: "#7cfc00",
821 lemonchiffon: "#fffacd",
822 lightblue: "#add8e6",
823 lightcoral: "#f08080",
824 lightcyan: "#e0ffff",
825 lightgoldenrodyellow: "#fafad2",
826 lightgray: "#d3d3d3",
827 lightgreen: "#90ee90",
828 lightgrey: "#d3d3d3",
829 lightpink: "#ffb6c1",
830 lightsalmon: "#ffa07a",
831 lightseagreen: "#20b2aa",
832 lightskyblue: "#87cefa",
833 lightslategray: "#778899",
834 lightslategrey: "#778899",
835 lightsteelblue: "#b0c4de",
836 lightyellow: "#ffffe0",
838 limegreen: "#32cd32",
842 mediumaquamarine: "#66cdaa",
843 mediumblue: "#0000cd",
844 mediumorchid: "#ba55d3",
845 mediumpurple: "#9370db",
846 mediumseagreen: "#3cb371",
847 mediumslateblue: "#7b68ee",
848 mediumspringgreen: "#00fa9a",
849 mediumturquoise: "#48d1cc",
850 mediumvioletred: "#c71585",
851 midnightblue: "#191970",
852 mintcream: "#f5fffa",
853 mistyrose: "#ffe4e1",
855 navajowhite: "#ffdead",
859 olivedrab: "#6b8e23",
861 orangered: "#ff4500",
863 palegoldenrod: "#eee8aa",
864 palegreen: "#98fb98",
865 paleturquoise: "#afeeee",
866 palevioletred: "#db7093",
867 papayawhip: "#ffefd5",
868 peachpuff: "#ffdab9",
872 powderblue: "#b0e0e6",
875 rosybrown: "#bc8f8f",
876 royalblue: "#4169e1",
877 saddlebrown: "#8b4513",
879 sandybrown: "#f4a460",
885 slateblue: "#6a5acd",
886 slategray: "#708090",
887 slategrey: "#708090",
889 springgreen: "#00ff7f",
890 steelblue: "#4682b4",
895 turquoise: "#40e0d0",
899 whitesmoke: "#f5f5f5",
901 yellowgreen: "#9acd32"