2 Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
8 * Provides color conversion and validation utils
9 * @class YAHOO.util.Color
10 * @namespace YAHOO.util
12 YAHOO
.util
.Color = function() {
14 var HCHARS
="0123456789ABCDEF", lang
=YAHOO
.lang
;
19 * Converts 0-1 to 0-255
21 * @param n {float} the number to convert
22 * @return {int} a number 0-255
24 real2dec: function(n
) {
25 return Math
.min(255, Math
.round(n
*256));
29 * Converts HSV (h[0-360], s[0-1]), v[0-1] to RGB [255,255,255]
31 * @param h {int|[int, float, float]} the hue, or an
32 * array containing all three parameters
33 * @param s {float} the saturation
34 * @param v {float} the value/brightness
35 * @return {[int, int, int]} the red, green, blue values in
38 hsv2rgb: function(h
, s
, v
) {
40 if (lang
.isArray(h
)) {
41 return this.hsv2rgb
.call(this, h
[0], h
[1], h
[2]);
44 var r
, g
, b
, i
, f
, p
, q
, t
;
45 i
= Math
.floor((h
/60)%6);
51 case 0: r
=v
; g
=t
; b
=p
; break;
52 case 1: r
=q
; g
=v
; b
=p
; break;
53 case 2: r
=p
; g
=v
; b
=t
; break;
54 case 3: r
=p
; g
=q
; b
=v
; break;
55 case 4: r
=t
; g
=p
; b
=v
; break;
56 case 5: r
=v
; g
=p
; b
=q
; break;
61 return [fn(r
), fn(g
), fn(b
)];
65 * Converts to RGB [255,255,255] to HSV (h[0-360], s[0-1]), v[0-1]
67 * @param r {int|[int, int, int]} the red value, or an
68 * array containing all three parameters
69 * @param g {int} the green value
70 * @param b {int} the blue value
71 * @return {[int, float, float]} the value converted to hsv
73 rgb2hsv: function(r
, g
, b
) {
75 if (lang
.isArray(r
)) {
76 return this.rgb2hsv
.call(this, r
[0], r
[1], r
[2]);
83 var min
,max
,delta
,h
,s
,v
;
84 min
= Math
.min(Math
.min(r
,g
),b
);
85 max
= Math
.max(Math
.max(r
,g
),b
);
90 case r
: h
=60*(g
-b
)/delta
;
95 case g
: h
=(60*(b
-r
)/delta
)+120; break;
96 case b
: h
=(60*(r
-g
)/delta
)+240; break;
99 s
= (max
=== 0) ? 0 : 1-(min
/max
);
101 var hsv
= [Math
.round(h
), s
, max
];
108 * Converts decimal rgb values into a hex string
109 * 255,255,255 -> FFFFFF
111 * @param r {int|[int, int, int]} the red value, or an
112 * array containing all three parameters
113 * @param g {int} the green value
114 * @param b {int} the blue value
115 * @return {string} the hex string
117 rgb2hex: function(r
, g
, b
) {
118 if (lang
.isArray(r
)) {
119 return this.rgb2hex
.call(this, r
[0], r
[1], r
[2]);
123 return f(r
) + f(g
) + f(b
);
127 * Converts an int 0...255 to hex pair 00...FF
129 * @param n {int} the number to convert
130 * @return {string} the hex equivalent
132 dec2hex: function(n
) {
134 n
= (lang
.isNumber(n
)) ? n
: 0;
135 n
= (n
> 255 || n
< 0) ? 0 : n
;
137 return HCHARS
.charAt((n
- n
% 16) / 16) + HCHARS
.charAt(n
% 16);
141 * Converts a hex pair 00...FF to an int 0...255
143 * @param str {string} the hex pair to convert
144 * @return {int} the decimal
146 hex2dec: function(str
) {
147 var f = function(c
) {
148 return HCHARS
.indexOf(c
.toUpperCase());
153 return ((f(s
[0]) * 16) + f(s
[1]));
157 * Converts a hex string to rgb
159 * @param str {string} the hex string
160 * @return {[int, int, int]} an array containing the rgb values
162 hex2rgb: function(s
) {
163 var f
= this.hex2dec
;
164 return [f(s
.substr(0, 2)), f(s
.substr(2, 2)), f(s
.substr(4, 2))];
168 * Returns the closest websafe color to the supplied rgb value.
170 * @param r {int|[int, int, int]} the red value, or an
171 * array containing all three parameters
172 * @param g {int} the green value
173 * @param b {int} the blue value
174 * @return {[int, int, int]} an array containing the closes
175 * websafe rgb colors.
177 websafe: function(r
, g
, b
) {
179 if (lang
.isArray(r
)) {
180 return this.websafe
.call(this, r
[0], r
[1], r
[2]);
183 // returns the closest match [0, 51, 102, 153, 204, 255]
184 var f = function(v
) {
185 if (lang
.isNumber(v
)) {
186 v
= Math
.min(Math
.max(0, v
), 255);
188 for (i
=0; i
<256; i
=i
+51) {
190 if (v
>= i
&& v
<= next
) {
191 return (v
-i
> 25) ? next
: i
;
199 return [f(r
), f(g
), f(b
)];
210 * The colorpicker module provides a widget for selecting colors
211 * @module colorpicker
212 * @requires yahoo, dom, event, element, slider
216 * A widget to select colors
217 * @namespace YAHOO.widget
218 * @class YAHOO.widget.ColorPicker
219 * @extends YAHOO.util.Element
221 * @param {HTMLElement | String | Object} el(optional) The html
222 * element that represents the colorpicker, or the attribute object to use.
223 * An element will be created if none provided.
224 * @param {Object} attr (optional) A key map of the colorpicker's
225 * initial attributes. Ignored if first arg is attributes object.
227 YAHOO
.widget
.ColorPicker = function(el
, attr
) {
228 pickercount
= pickercount
+ 1;
230 if (arguments
.length
=== 1 && !YAHOO
.lang
.isString(el
) && !el
.nodeName
) {
231 attr
= el
; // treat first arg as attr object
232 el
= attr
.element
|| null;
235 if (!el
&& !attr
.element
) { // create if we dont have one
236 el
= _createHostElement
.call(this, attr
);
239 YAHOO
.widget
.ColorPicker
.superclass
.constructor.call(this, el
, attr
);
242 YAHOO
.extend(YAHOO
.widget
.ColorPicker
, YAHOO
.util
.Element
);
244 var proto
= YAHOO
.widget
.ColorPicker
.prototype,
245 Slider
=YAHOO
.widget
.Slider
,
246 Color
=YAHOO
.util
.Color
,
247 Dom
= YAHOO
.util
.Dom
,
248 Event
= YAHOO
.util
.Event
,
250 sub
= lang
.substitute
;
253 var b
= "yui-picker";
256 * The element ids used by this control
263 * The id for the "red" form field
267 * @default yui-picker-r
272 * The id for the "red" hex pair output
276 * @default yui-picker-rhex
281 * The id for the "green" form field
285 * @default yui-picker-g
290 * The id for the "green" hex pair output
294 * @default yui-picker-ghex
300 * The id for the "blue" form field
304 * @default yui-picker-b
309 * The id for the "blue" hex pair output
313 * @default yui-picker-bhex
318 * The id for the "hue" form field
322 * @default yui-picker-h
327 * The id for the "saturation" form field
331 * @default yui-picker-s
336 * The id for the "value" form field
340 * @default yui-picker-v
345 * The id for the picker region slider
346 * @property ID.PICKER_BG
349 * @default yui-picker-bg
351 PICKER_BG
: b
+ "-bg",
354 * The id for the picker region thumb
355 * @property ID.PICKER_THUMB
358 * @default yui-picker-thumb
360 PICKER_THUMB
: b
+ "-thumb",
363 * The id for the hue slider
364 * @property ID.HUE_BG
367 * @default yui-picker-hue-bg
369 HUE_BG
: b
+ "-hue-bg",
372 * The id for the hue thumb
373 * @property ID.HUE_THUMB
376 * @default yui-picker-hue-thumb
378 HUE_THUMB
: b
+ "-hue-thumb",
381 * The id for the hex value form field
385 * @default yui-picker-hex
390 * The id for the color swatch
391 * @property ID.SWATCH
394 * @default yui-picker-swatch
396 SWATCH
: b
+ "-swatch",
399 * The id for the websafe color swatch
400 * @property ID.WEBSAFE_SWATCH
403 * @default yui-picker-websafe-swatch
405 WEBSAFE_SWATCH
: b
+ "-websafe-swatch",
408 * The id for the control details
409 * @property ID.CONTROLS
411 * @default yui-picker-controls
413 CONTROLS
: b
+ "-controls",
416 * The id for the rgb controls
417 * @property ID.RGB_CONTROLS
419 * @default yui-picker-rgb-controls
421 RGB_CONTROLS
: b
+ "-rgb-controls",
424 * The id for the hsv controls
425 * @property ID.HSV_CONTROLS
427 * @default yui-picker-hsv-controls
429 HSV_CONTROLS
: b
+ "-hsv-controls",
432 * The id for the hsv controls
433 * @property ID.HEX_CONTROLS
435 * @default yui-picker-hex-controls
437 HEX_CONTROLS
: b
+ "-hex-controls",
440 * The id for the hex summary
441 * @property ID.HEX_SUMMARY
443 * @default yui-picker-hex-summary
445 HEX_SUMMARY
: b
+ "-hex-summary",
448 * The id for the controls section header
449 * @property ID.CONTROLS_LABEL
451 * @default yui-picker-controls-label
453 CONTROLS_LABEL
: b
+ "-controls-label"
457 * Constants for any script-generated messages. The values here
458 * are the default messages. They can be updated by providing
459 * the complete list to the constructor for the "txt" attribute.
464 ILLEGAL_HEX
: "Illegal hex value entered",
465 SHOW_CONTROLS
: "Show color details",
466 HIDE_CONTROLS
: "Hide color details",
467 CURRENT_COLOR
: "Currently selected color: {rgb}",
468 CLOSEST_WEBSAFE
: "Closest websafe color: {rgb}. Click to select.",
481 * Constants for the default image locations for img tags that are
482 * generated by the control. They can be modified by passing the
483 * complete list to the contructor for the "images" attribute
488 PICKER_THUMB
: "../../build/colorpicker/assets/picker_thumb.png",
489 HUE_THUMB
: "../../build/colorpicker/assets/hue_thumb.png"
493 * Constants for the control's custom event names. subscribe
494 * to the rgbChange event instead.
505 * Constants for the control's default default values
514 * Constants for the control's configuration attributes
520 SATURATION
: "saturation",
529 PICKER_SIZE
: "pickersize",
530 SHOW_CONTROLS
: "showcontrols",
531 SHOW_RGB_CONTROLS
: "showrgbcontrols",
532 SHOW_HSV_CONTROLS
: "showhsvcontrols",
533 SHOW_HEX_CONTROLS
: "showhexcontrols",
534 SHOW_HEX_SUMMARY
: "showhexsummary",
535 SHOW_WEBSAFE
: "showwebsafe",
536 //SHOW_SUBMIT: "showsubmit",
537 CONTAINER
: "container",
539 ELEMENTS
: "elements",
546 * Sets the control to the specified rgb value and
547 * moves the sliders to the proper positions
549 * @param rgb {[int, int, int]} the rgb value
550 * @param silent {boolean} whether or not to fire the change event
552 proto
.setValue = function(rgb
, silent
) {
553 silent
= (silent
) || false;
554 this.set(this.OPT
.RGB
, rgb
, silent
);
555 _updateSliders
.call(this);
560 * @property hueSlider
561 * @type YAHOO.widget.Slider
563 proto
.hueSlider
= null;
567 * @property pickerSlider
568 * @type YAHOO.widget.Slider
570 proto
.pickerSlider
= null;
573 * Translates the slider value into hue, int[0,359]
576 * @return {int} the hue from 0 to 359
578 var _getH = function() {
579 var size
= this.get(this.OPT
.PICKER_SIZE
),
580 h
= (size
- this.hueSlider
.getValue()) / size
;
581 h
= Math
.round(h
*360);
582 return (h
=== 360) ? 0 : h
;
586 * Translates the slider value into saturation, int[0,1], left to right
589 * @return {int} the saturation from 0 to 1
591 var _getS = function() {
592 return this.pickerSlider
.getXValue() / this.get(this.OPT
.PICKER_SIZE
);
596 * Translates the slider value into value/brightness, int[0,1], top
600 * @return {int} the value from 0 to 1
602 var _getV = function() {
603 var size
= this.get(this.OPT
.PICKER_SIZE
);
604 return (size
- this.pickerSlider
.getYValue()) / size
;
608 * Updates the background of the swatch with the current rbg value.
609 * Also updates the websafe swatch to the closest websafe color
610 * @method _updateSwatch
613 var _updateSwatch = function() {
614 var rgb
= this.get(this.OPT
.RGB
),
615 websafe
= this.get(this.OPT
.WEBSAFE
),
616 el
= this.getElement(this.ID
.SWATCH
),
617 color
= rgb
.join(","),
618 txt
= this.get(this.OPT
.TXT
);
620 Dom
.setStyle(el
, "background-color", "rgb(" + color
+ ")");
621 el
.title
= lang
.substitute(txt
.CURRENT_COLOR
, {
622 "rgb": "#" + this.get(this.OPT
.HEX
)
626 el
= this.getElement(this.ID
.WEBSAFE_SWATCH
);
627 color
= websafe
.join(",");
629 Dom
.setStyle(el
, "background-color", "rgb(" + color
+ ")");
630 el
.title
= lang
.substitute(txt
.CLOSEST_WEBSAFE
, {
631 "rgb": "#" + Color
.rgb2hex(websafe
)
637 * Reads the sliders and converts the values to RGB, updating the
638 * internal state for all the individual form fields
639 * @method _getValuesFromSliders
642 var _getValuesFromSliders = function() {
643 var h
=_getH
.call(this), s
=_getS
.call(this), v
=_getV
.call(this);
645 rgb
= Color
.hsv2rgb(h
, s
, v
);
646 var websafe
= Color
.websafe(rgb
);
647 var hex
= Color
.rgb2hex(rgb
[0], rgb
[1], rgb
[2]);
649 this.set(this.OPT
.RGB
, rgb
);
653 * Updates the form field controls with the state data contained
655 * @method _updateFormFields
658 var _updateFormFields = function() {
659 this.getElement(this.ID
.H
).value
= this.get(this.OPT
.HUE
);
660 this.getElement(this.ID
.S
).value
= this.get(this.OPT
.SATURATION
);
661 this.getElement(this.ID
.V
).value
= this.get(this.OPT
.VALUE
);
662 this.getElement(this.ID
.R
).value
= this.get(this.OPT
.RED
);
663 this.getElement(this.ID
.R_HEX
).innerHTML
= Color
.dec2hex(this.get(this.OPT
.RED
));
664 this.getElement(this.ID
.G
).value
= this.get(this.OPT
.GREEN
);
665 this.getElement(this.ID
.G_HEX
).innerHTML
= Color
.dec2hex(this.get(this.OPT
.GREEN
));
666 this.getElement(this.ID
.B
).value
= this.get(this.OPT
.BLUE
);
667 this.getElement(this.ID
.B_HEX
).innerHTML
= Color
.dec2hex(this.get(this.OPT
.BLUE
));
668 this.getElement(this.ID
.HEX
).value
= this.get(this.OPT
.HEX
);
672 * Event handler for the hue slider.
673 * @method _onHueSliderChange
674 * @param newOffset {int} pixels from the start position
677 var _onHueSliderChange = function(newOffset
) {
679 var h
= _getH
.call(this);
680 this.set(this.OPT
.HUE
, h
, true);
682 // set picker background to the hue
683 var rgb
= Color
.hsv2rgb(h
, 1, 1);
684 var styleDef
= "rgb(" + rgb
.join(",") + ")";
686 Dom
.setStyle(this.getElement(this.ID
.PICKER_BG
), "background-color", styleDef
);
688 if (this.hueSlider
.valueChangeSource
=== this.hueSlider
.SOURCE_UI_EVENT
) {
689 _getValuesFromSliders
.call(this);
692 _updateFormFields
.call(this);
693 _updateSwatch
.call(this);
697 * Event handler for the picker slider, which controls the
698 * saturation and value/brightness.
699 * @method _onPickerSliderChange
700 * @param newOffset {{x: int, y: int}} x/y pixels from the start position
703 var _onPickerSliderChange = function(newOffset
) {
705 var s
=_getS
.call(this), v
=_getV
.call(this);
706 this.set(this.OPT
.SATURATION
, Math
.round(s
*100), true);
707 this.set(this.OPT
.VALUE
, Math
.round(v
*100), true);
709 if (this.pickerSlider
.valueChangeSource
=== this.pickerSlider
.SOURCE_UI_EVENT
) {
710 _getValuesFromSliders
.call(this);
713 _updateFormFields
.call(this);
714 _updateSwatch
.call(this);
719 * Key map to well-known commands for txt field input
720 * @method _getCommand
721 * @param e {Event} the keypress or keydown event
722 * @return {int} a command code
724 * <li>0 = not a number, letter in range, or special key</li>
725 * <li>1 = number</li>
726 * <li>2 = a-fA-F</li>
727 * <li>3 = increment (up arrow)</li>
728 * <li>4 = decrement (down arrow)</li>
729 * <li>5 = special key (tab, delete, return, escape, left, right)</li>
730 * <li>6 = return</li>
734 var _getCommand = function(e
) {
735 var c
= Event
.getCharCode(e
);
737 //alert(Event.getCharCode(e) + ", " + e.keyCode + ", " + e.charCode);
740 if (c
=== 38) { // up arrow
742 } else if (c
=== 13) { // return
744 } else if (c
=== 40) { // down array
746 } else if (c
>= 48 && c
<=57) { // 0-9
748 } else if (c
>= 97 && c
<=102) { // a-f
750 } else if (c
>= 65 && c
<=70) { // A-F
752 //} else if ("8, 9, 13, 27, 37, 39".indexOf(c) > -1 ||
753 // (c >= 112 && c <=123)) { // including F-keys
754 // tab, delete, return, escape, left, right
755 } else if ("8, 9, 13, 27, 37, 39".indexOf(c
) > -1) { // special chars
757 } else { // something we probably don't want
763 * Handle keypress on one of the rgb or hsv fields.
764 * @method _rgbFieldKeypress
765 * @param e {Event} the keypress event
766 * @param el {HTMLElement} the field
767 * @param prop {string} the key to the linked property
770 var _rgbFieldKeypress = function(e
, el
, prop
) {
771 var command
= _getCommand(e
);
772 var inc
= (e
.shiftKey
) ? 10 : 1;
774 case 6: // return, update the value
775 _useFieldValue
.apply(this, arguments
);
778 case 3: // up arrow, increment
779 this.set(prop
, Math
.min(this.get(prop
)+inc
, 255));
780 _updateFormFields
.call(this);
781 //Event.stopEvent(e);
783 case 4: // down arrow, decrement
784 this.set(prop
, Math
.max(this.get(prop
)-inc
, 0));
785 _updateFormFields
.call(this);
786 //Event.stopEvent(e);
795 * Handle keydown on the hex field
796 * @method _hexFieldKeypress
797 * @param e {Event} the keypress event
798 * @param el {HTMLElement} the field
799 * @param prop {string} the key to the linked property
802 var _hexFieldKeypress = function(e
, el
, prop
) {
803 var command
= _getCommand(e
);
804 if (command
=== 6) { // return, update the value
805 _useFieldValue
.apply(this, arguments
);
810 * Use the value of the text field to update the control
811 * @method _hexFieldKeypress
812 * @param e {Event} an event
813 * @param el {HTMLElement} the field
814 * @param prop {string} the key to the linked property
817 var _useFieldValue = function(e
, el
, prop
) {
820 if (prop
!== this.OPT
.HEX
) {
821 val
= parseInt(val
, 10);
824 if (val
!== this.get(prop
)) {
830 * Allows numbers and special chars only. Used for the
831 * rgb and hsv fields keypress handler.
832 * @method _numbersOnly
833 * @param e {Event} the event
835 * @return {boolean} false if we are canceling the event
837 var _numbersOnly = function(e
) {
838 return _hexOnly(e
, true);
842 * Allows numbers and special chars, and by default allows a-f.
843 * Used for the hex field keypress handler.
845 * @param e {Event} the event
846 * @param numbersOnly omits a-f if set to true
848 * @return {boolean} false if we are canceling the event
850 var _hexOnly = function(e
, numbersOnly
) {
851 var command
= _getCommand(e
);
854 case 5: // special char
857 case 2: // hex char (a-f)
858 if (numbersOnly
!== true) {
862 // fallthrough is intentional
864 default: // prevent alpha and punctuation
871 * Returns the element reference that is saved. The id can be either
872 * the element id, or the key for this id in the "id" config attribute.
873 * For instance, the host element id can be obtained by passing its
874 * id (default: "yui_picker") or by its key "YUI_PICKER".
875 * @param id {string} the element id, or key
876 * @return {HTMLElement} a reference to the element
878 proto
.getElement = function(id
) {
879 return this.get(this.OPT
.ELEMENTS
)[this.get(this.OPT
.IDS
)[id
]];
882 _createElements = function() {
883 var el
, child
, img
, fld
, i
,
884 ids
= this.get(this.OPT
.IDS
),
885 txt
= this.get(this.OPT
.TXT
),
886 images
= this.get(this.OPT
.IMAGES
),
887 Elem = function(type
, o
) {
888 var n
= document
.createElement(type
);
890 lang
.augmentObject(n
, o
, true);
894 RGBElem = function(type
, obj
) {
904 return new Elem(type
, o
);
907 var p
= this.get("element");
909 // Picker slider (S and V) ---------------------------------------------
911 el
= new Elem("div", {
912 id
: ids
[this.ID
.PICKER_BG
],
913 className
: "yui-picker-bg",
918 child
= new Elem("div", {
919 id
: ids
[this.ID
.PICKER_THUMB
],
920 className
: "yui-picker-thumb"
923 img
= new Elem("img", {
924 src
: images
.PICKER_THUMB
927 child
.appendChild(img
);
928 el
.appendChild(child
);
931 // Hue slider ---------------------------------------------
932 el
= new Elem("div", {
933 id
: ids
[this.ID
.HUE_BG
],
934 className
: "yui-picker-hue-bg",
939 child
= new Elem("div", {
940 id
: ids
[this.ID
.HUE_THUMB
],
941 className
: "yui-picker-hue-thumb"
944 img
= new Elem("img", {
945 src
: images
.HUE_THUMB
948 child
.appendChild(img
);
949 el
.appendChild(child
);
953 // controls ---------------------------------------------
955 el
= new Elem("div", {
956 id
: ids
[this.ID
.CONTROLS
],
957 className
: "yui-picker-controls"
964 el
= new Elem("div", {
968 child
= new Elem("a", {
969 id
: ids
[this.ID
.CONTROLS_LABEL
],
970 //className: "yui-picker-controls-label",
973 el
.appendChild(child
);
977 el
= new Elem("div", {
985 el
= new Elem("ul", {
986 id
: ids
[this.ID
.RGB_CONTROLS
],
987 className
: "yui-picker-rgb-controls"
990 child
= new Elem("li");
991 child
.appendChild(document
.createTextNode(txt
.R
+ " "));
993 fld
= new RGBElem("input", {
995 className
: "yui-picker-r"
998 child
.appendChild(fld
);
999 el
.appendChild(child
);
1001 child
= new Elem("li");
1002 child
.appendChild(document
.createTextNode(txt
.G
+ " "));
1004 fld
= new RGBElem("input", {
1006 className
: "yui-picker-g"
1009 child
.appendChild(fld
);
1010 el
.appendChild(child
);
1012 child
= new Elem("li");
1013 child
.appendChild(document
.createTextNode(txt
.B
+ " "));
1015 fld
= new RGBElem("input", {
1017 className
: "yui-picker-b"
1020 child
.appendChild(fld
);
1021 el
.appendChild(child
);
1026 el
= new Elem("ul", {
1027 id
: ids
[this.ID
.HSV_CONTROLS
],
1028 className
: "yui-picker-hsv-controls"
1031 child
= new Elem("li");
1032 child
.appendChild(document
.createTextNode(txt
.H
+ " "));
1034 fld
= new RGBElem("input", {
1036 className
: "yui-picker-h"
1039 child
.appendChild(fld
);
1040 child
.appendChild(document
.createTextNode(" " + txt
.DEG
));
1042 el
.appendChild(child
);
1044 child
= new Elem("li");
1045 child
.appendChild(document
.createTextNode(txt
.S
+ " "));
1047 fld
= new RGBElem("input", {
1049 className
: "yui-picker-s"
1052 child
.appendChild(fld
);
1053 child
.appendChild(document
.createTextNode(" " + txt
.PERCENT
));
1055 el
.appendChild(child
);
1057 child
= new Elem("li");
1058 child
.appendChild(document
.createTextNode(txt
.V
+ " "));
1060 fld
= new RGBElem("input", {
1062 className
: "yui-picker-v"
1065 child
.appendChild(fld
);
1066 child
.appendChild(document
.createTextNode(" " + txt
.PERCENT
));
1068 el
.appendChild(child
);
1074 el
= new Elem("ul", {
1075 id
: ids
[this.ID
.HEX_SUMMARY
],
1076 className
: "yui-picker-hex_summary"
1079 child
= new Elem("li", {
1080 id
: ids
[this.ID
.R_HEX
]
1082 el
.appendChild(child
);
1084 child
= new Elem("li", {
1085 id
: ids
[this.ID
.G_HEX
]
1087 el
.appendChild(child
);
1089 child
= new Elem("li", {
1090 id
: ids
[this.ID
.B_HEX
]
1092 el
.appendChild(child
);
1096 el
= new Elem("div", {
1097 id
: ids
[this.ID
.HEX_CONTROLS
],
1098 className
: "yui-picker-hex-controls"
1100 el
.appendChild(document
.createTextNode(txt
.HEX
+ " "));
1102 child
= new RGBElem("input", {
1103 id
: ids
[this.ID
.HEX
],
1104 className
: "yui-picker-hex",
1109 el
.appendChild(child
);
1112 p
= this.get("element");
1115 el
= new Elem("div", {
1116 id
: ids
[this.ID
.SWATCH
],
1117 className
: "yui-picker-swatch"
1123 el
= new Elem("div", {
1124 id
: ids
[this.ID
.WEBSAFE_SWATCH
],
1125 className
: "yui-picker-websafe-swatch"
1133 * Sets the initial state of the sliders
1134 * @method initPicker
1136 proto
.initPicker = function () {
1138 // bind all of our elements
1140 ids
= this.get(o
.IDS
),
1141 els
= this.get(o
.ELEMENTS
),
1144 // Add the default value as a key for each element for easier lookup
1145 for (i
in this.ID
) {
1146 if (lang
.hasOwnProperty(this.ID
, i
)) {
1147 ids
[this.ID
[i
]] = ids
[i
];
1151 // Check for picker element, if not there, create all of them
1152 el
= Dom
.get(ids
[this.ID
.PICKER_BG
]);
1154 _createElements
.call(this);
1159 if (lang
.hasOwnProperty(ids
, i
)) {
1161 el
= Dom
.get(ids
[i
]);
1163 // generate an id if the implementer passed in an element reference,
1164 // and the element did not have an id already
1165 id
= Dom
.generateId(el
);
1167 // update the id in case we generated the id
1168 ids
[i
] = id
; // key is WEBSAFE_SWATCH
1169 ids
[ids
[i
]] = id
; // key is websafe_swatch
1171 // store the dom ref
1176 // set the initial visibility state of our controls
1177 els
= [o
.SHOW_CONTROLS
,
1178 o
.SHOW_RGB_CONTROLS
,
1179 o
.SHOW_HSV_CONTROLS
,
1180 o
.SHOW_HEX_CONTROLS
,
1185 for (i
=0; i
<els
.length
; i
=i
+1) {
1186 this.set(els
[i
], this.get(els
[i
]));
1189 var s
= this.get(o
.PICKER_SIZE
);
1191 this.hueSlider
= Slider
.getVertSlider(this.getElement(this.ID
.HUE_BG
),
1192 this.getElement(this.ID
.HUE_THUMB
), 0, s
);
1193 this.hueSlider
.subscribe("change", _onHueSliderChange
, this, true);
1195 this.pickerSlider
= Slider
.getSliderRegion(this.getElement(this.ID
.PICKER_BG
),
1196 this.getElement(this.ID
.PICKER_THUMB
), 0, s
, 0, s
);
1197 this.pickerSlider
.subscribe("change", _onPickerSliderChange
, this, true);
1199 //_onHueSliderChange.call(this, 0);
1201 Event
.on(this.getElement(this.ID
.WEBSAFE_SWATCH
), "click", function(e
) {
1202 this.setValue(this.get(o
.WEBSAFE
));
1206 Event
.on(this.getElement(this.ID
.CONTROLS_LABEL
), "click", function(e
) {
1207 this.set(o
.SHOW_CONTROLS
, !this.get(o
.SHOW_CONTROLS
));
1208 Event
.preventDefault(e
);
1211 _attachRGBHSV
.call(this, this.ID
.R
, this.OPT
.RED
);
1212 _attachRGBHSV
.call(this, this.ID
.G
, this.OPT
.GREEN
);
1213 _attachRGBHSV
.call(this, this.ID
.B
, this.OPT
.BLUE
);
1214 _attachRGBHSV
.call(this, this.ID
.H
, this.OPT
.HUE
);
1215 _attachRGBHSV
.call(this, this.ID
.S
, this.OPT
.SATURATION
);
1216 _attachRGBHSV
.call(this, this.ID
.V
, this.OPT
.VALUE
);
1218 Event
.on(this.getElement(this.ID
.HEX
), "keydown", function(e
, me
) {
1219 _hexFieldKeypress
.call(me
, e
, this, me
.OPT
.HEX
);
1222 Event
.on(this.getElement(this.ID
.HEX
), "keypress", _hexOnly
, this);
1223 Event
.on(this.getElement(this.ID
.HEX
), "blur", function(e
, me
) {
1224 _useFieldValue
.call(me
, e
, this, me
.OPT
.HEX
);
1228 _attachRGBHSV = function(id
, config
) {
1229 Event
.on(this.getElement(id
), "keydown", function(e
, me
) {
1230 _rgbFieldKeypress
.call(me
, e
, this, config
);
1232 Event
.on(this.getElement(id
), "keypress", _numbersOnly
, this);
1233 Event
.on(this.getElement(id
), "blur", function(e
, me
) {
1234 _useFieldValue
.call(me
, e
, this, config
);
1240 * Sets up the config attributes and the change listeners for this
1242 * @method initAttributes
1243 * @param attr An object containing default attribute values
1245 proto
.initAttributes = function(attr
) {
1248 YAHOO
.widget
.ColorPicker
.superclass
.initAttributes
.call(this, attr
);
1251 * The size of the picker. Trying to change this is not recommended.
1252 * @config pickersize
1256 this.setAttributeConfig(this.OPT
.PICKER_SIZE
, {
1257 value
: attr
.size
|| this.DEFAULT
.PICKER_SIZE
1261 * The current hue value 0-360
1265 this.setAttributeConfig(this.OPT
.HUE
, {
1266 value
: attr
.hue
|| 0,
1267 validator
: lang
.isNumber
1271 * The current saturation value 0-100
1272 * @config saturation
1275 this.setAttributeConfig(this.OPT
.SATURATION
, {
1276 value
: attr
.saturation
|| 0,
1277 validator
: lang
.isNumber
1281 * The current value/brightness value 0-100
1285 this.setAttributeConfig(this.OPT
.VALUE
, {
1286 value
: attr
.value
|| 100,
1287 validator
: lang
.isNumber
1291 * The current red value 0-255
1295 this.setAttributeConfig(this.OPT
.RED
, {
1296 value
: attr
.red
|| 255,
1297 validator
: lang
.isNumber
1301 * The current green value 0-255
1305 this.setAttributeConfig(this.OPT
.GREEN
, {
1306 value
: attr
.red
|| 255,
1307 validator
: lang
.isNumber
1311 * The current blue value 0-255
1315 this.setAttributeConfig(this.OPT
.BLUE
, {
1316 value
: attr
.blue
|| 255,
1317 validator
: lang
.isNumber
1321 * The current hex value #000000-#FFFFFF, without the #
1325 this.setAttributeConfig(this.OPT
.HEX
, {
1326 value
: attr
.hex
|| "FFFFFF",
1327 validator
: lang
.isString
1331 * The current rgb value. Updates the state of all of the
1332 * other value fields. Read-only: use setValue to set the
1333 * controls rgb value.
1335 * @type [int, int, int]
1338 this.setAttributeConfig(this.OPT
.RGB
, {
1339 value
: attr
.rgb
|| [255,255,255],
1340 method: function(rgb
) {
1342 this.set(this.OPT
.RED
, rgb
[0], true);
1343 this.set(this.OPT
.GREEN
, rgb
[1], true);
1344 this.set(this.OPT
.BLUE
, rgb
[2], true);
1346 var websafe
= Color
.websafe(rgb
);
1347 this.set(this.OPT
.WEBSAFE
, websafe
, true);
1349 var hex
= Color
.rgb2hex(rgb
);
1350 this.set(this.OPT
.HEX
, hex
, true);
1352 var hsv
= Color
.rgb2hsv(rgb
);
1355 this.set(this.OPT
.HUE
, hsv
[0], true);
1356 this.set(this.OPT
.SATURATION
, Math
.round(hsv
[1]*100), true);
1357 this.set(this.OPT
.VALUE
, Math
.round(hsv
[2]*100), true);
1363 * If the color picker will live inside of a container object,
1364 * set, provide a reference to it so the control can use the
1365 * container's events.
1367 * @type YAHOO.widget.Panel
1369 this.setAttributeConfig(this.OPT
.CONTAINER
, {
1371 method: function(container
) {
1373 // Position can get out of sync when the
1374 // control is manipulated while display is
1375 // none. Resetting the slider constraints
1376 // when it is visible gets the state back in
1378 container
.showEvent
.subscribe(function() {
1379 // this.pickerSlider.thumb.resetConstraints();
1380 // this.hueSlider.thumb.resetConstraints();
1381 this.pickerSlider
.focus();
1387 * The closest current websafe value
1391 this.setAttributeConfig(this.OPT
.WEBSAFE
, {
1392 value
: attr
.websafe
|| [255,255,255]
1396 ids
= attr
.ids
|| lang
.merge({}, this.ID
);
1398 if (!attr
.ids
&& pickercount
> 1) {
1399 for (var i
in ids
) {
1400 if (lang
.hasOwnProperty(ids
, i
)) {
1401 ids
[i
] = ids
[i
] + pickercount
;
1408 * A list of element ids and/or element references used by the
1409 * control. The default is the this.ID list, and can be customized
1410 * by passing a list in the contructor
1412 * @type {referenceid: realid}
1415 this.setAttributeConfig(this.OPT
.IDS
, {
1421 * A list of txt strings for internationalization. Default
1427 this.setAttributeConfig(this.OPT
.TXT
, {
1428 value
: attr
.txt
|| this.TXT
,
1433 * The img src default list
1436 * @type {key: image}
1439 this.setAttributeConfig(this.OPT
.IMAGES
, {
1440 value
: attr
.images
|| this.IMAGE
,
1444 * The element refs used by this control. Set at initialization
1446 * @type {id: HTMLElement}
1449 this.setAttributeConfig(this.OPT
.ELEMENTS
, {
1455 * Returns the cached element reference. If the id is not a string, it
1456 * is assumed that it is an element and this is returned.
1457 * @param id {string|HTMLElement} the element key, id, or ref
1458 * @param on {boolean} hide or show. If true, show
1460 _hideShowEl = function(id
, on
) {
1461 var el
= (lang
.isString(id
) ? this.getElement(id
) : id
);
1462 //Dom.setStyle(id, "visibility", (on) ? "" : "hidden");
1463 Dom
.setStyle(el
, "display", (on
) ? "" : "none");
1467 * Hide/show the entire set of controls
1468 * @config showcontrols
1472 this.setAttributeConfig(this.OPT
.SHOW_CONTROLS
, {
1473 value
: (attr
.showcontrols
) || true,
1474 method: function(on
) {
1476 var el
= Dom
.getElementsByClassName("bd", "div",
1477 this.getElement(this.ID
.CONTROLS
))[0];
1479 _hideShowEl
.call(this, el
, on
);
1481 this.getElement(this.ID
.CONTROLS_LABEL
).innerHTML
=
1482 (on
) ? this.get(this.OPT
.TXT
).HIDE_CONTROLS
:
1483 this.get(this.OPT
.TXT
).SHOW_CONTROLS
;
1489 * Hide/show the rgb controls
1490 * @config showrgbcontrols
1494 this.setAttributeConfig(this.OPT
.SHOW_RGB_CONTROLS
, {
1495 value
: (attr
.showrgbcontrols
) || true,
1496 method: function(on
) {
1497 //Dom.setStyle(this.getElement(this.ID.RBG_CONTROLS), "visibility", (on) ? "" : "hidden");
1498 _hideShowEl
.call(this, this.ID
.RGB_CONTROLS
, on
);
1503 * Hide/show the hsv controls
1504 * @config showhsvcontrols
1508 this.setAttributeConfig(this.OPT
.SHOW_HSV_CONTROLS
, {
1509 value
: (attr
.showhsvcontrols
) || false,
1510 method: function(on
) {
1511 //Dom.setStyle(this.getElement(this.ID.HSV_CONTROLS), "visibility", (on) ? "" : "hidden");
1512 _hideShowEl
.call(this, this.ID
.HSV_CONTROLS
, on
);
1514 // can't show both the hsv controls and the rbg hex summary
1515 if (on
&& this.get(this.OPT
.SHOW_HEX_SUMMARY
)) {
1516 this.set(this.OPT
.SHOW_HEX_SUMMARY
, false);
1522 * Hide/show the hex controls
1523 * @config showhexcontrols
1527 this.setAttributeConfig(this.OPT
.SHOW_HEX_CONTROLS
, {
1528 value
: (attr
.showhexcontrols
) || false,
1529 method: function(on
) {
1530 _hideShowEl
.call(this, this.ID
.HEX_CONTROLS
, on
);
1535 * Hide/show the websafe swatch
1536 * @config showwebsafe
1540 this.setAttributeConfig(this.OPT
.SHOW_WEBSAFE
, {
1541 value
: (attr
.showwebsafe
) || true,
1542 method: function(on
) {
1543 _hideShowEl
.call(this, this.ID
.WEBSAFE_SWATCH
, on
);
1548 * Hide/show the hex summary
1549 * @config showhexsummary
1553 this.setAttributeConfig(this.OPT
.SHOW_HEX_SUMMARY
, {
1554 value
: (attr
.showhexsummary
) || true,
1555 method: function(on
) {
1556 _hideShowEl
.call(this, this.ID
.HEX_SUMMARY
, on
);
1558 // can't show both the hsv controls and the rbg hex summary
1559 if (on
&& this.get(this.OPT
.SHOW_HSV_CONTROLS
)) {
1560 this.set(this.OPT
.SHOW_HSV_CONTROLS
, false);
1564 this.setAttributeConfig(this.OPT
.ANIMATE
, {
1565 value
: (attr
.animate
) || true,
1566 method: function(on
) {
1567 this.pickerSlider
.animate
= on
;
1568 this.hueSlider
.animate
= on
;
1572 this.on(this.OPT
.HUE
+ "Change", _updateRGBFromHSV
, this, true);
1573 this.on(this.OPT
.SATURATION
+ "Change", _updateRGBFromHSV
, this, true);
1574 this.on(this.OPT
.VALUE
+ "Change", _updatePickerSlider
, this, true);
1576 this.on(this.OPT
.RED
+ "Change", _updateRGB
, this, true);
1577 this.on(this.OPT
.GREEN
+ "Change", _updateRGB
, this, true);
1578 this.on(this.OPT
.BLUE
+ "Change", _updateRGB
, this, true);
1580 this.on(this.OPT
.HEX
+ "Change", _updateHex
, this, true);
1586 * Updates the rgb attribute with the current state of the r,g,b
1587 * fields. This is invoked from change listeners on these
1588 * attributes to facilitate updating these values from the
1589 * individual form fields
1590 * @method _updateRGB
1593 var _updateRGB = function() {
1594 var rgb
= [this.get(this.OPT
.RED
),
1595 this.get(this.OPT
.GREEN
),
1596 this.get(this.OPT
.BLUE
)];
1598 this.set(this.OPT
.RGB
, rgb
);
1600 _updateSliders
.call(this);
1604 * Updates the RGB values from the current state of the HSV
1605 * values. Executed when the one of the HSV form fields are
1610 var _updateRGBFromHSV = function() {
1611 var hsv
= [this.get(this.OPT
.HUE
),
1612 this.get(this.OPT
.SATURATION
)/100,
1613 this.get(this.OPT
.VALUE
)/100];
1615 var rgb
= Color
.hsv2rgb(hsv
);
1617 this.set(this.OPT
.RGB
, rgb
);
1619 _updateSliders
.call(this);
1623 * Parses the hex string to normalize shorthand values, converts
1624 * the hex value to rgb and updates the rgb attribute (which
1625 * updates the state for all of the other values)
1629 var _updateHex = function() {
1631 var hex
= this.get(this.OPT
.HEX
), l
=hex
.length
;
1633 // support #369 -> #336699 shorthand
1635 var c
= hex
.split(""), i
;
1636 for (i
=0; i
<l
; i
=i
+1) {
1643 if (hex
.length
!== 6) {
1647 var rgb
= Color
.hex2rgb(hex
);
1652 //_updateSliders.call(this);
1657 * Moves the sliders into the position dictated by the current state
1659 * @method _updateSliders
1662 var _updateSliders = function() {
1663 _updateHueSlider
.call(this);
1664 _updatePickerSlider
.call(this);
1668 * Moves the hue slider into the position dictated by the current state
1670 * @method _updateHueSlider
1673 var _updateHueSlider = function() {
1674 var size
= this.get(this.OPT
.PICKER_SIZE
),
1675 h
= this.get(this.OPT
.HUE
);
1677 h
= size
- Math
.round(h
/ 360 * size
);
1679 // 0 is at the top and bottom of the hue slider. Always go to
1680 // the top so we don't end up sending the thumb to the bottom
1681 // when the value didn't actually change (e.g., a conversion
1682 // produced 360 instead of 0 and the value was already 0).
1687 this.hueSlider
.setValue(h
);
1691 * Moves the picker slider into the position dictated by the current state
1693 * @method _updatePickerSlider
1696 var _updatePickerSlider = function() {
1697 var size
= this.get(this.OPT
.PICKER_SIZE
),
1698 s
= this.get(this.OPT
.SATURATION
),
1699 v
= this.get(this.OPT
.VALUE
);
1701 s
= Math
.round(s
* size
/ 100);
1702 v
= Math
.round(size
- (v
* size
/ 100));
1705 this.pickerSlider
.setRegionValue(s
, v
);
1709 * Creates the host element if it doesn't exist
1710 * @method _createHostElement
1713 var _createHostElement = function() {
1714 var el
= document
.createElement('div');
1716 if (this.CSS
.BASE
) {
1717 el
.className
= this.CSS
.BASE
;
1725 YAHOO
.register("colorpicker", YAHOO
.widget
.ColorPicker
, {version
: "2.3.0", build
: "442"});