]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/javascript/yui/build/colorpicker/colorpicker.js
Release 6.5.0
[Github/sugarcrm.git] / include / javascript / yui / build / colorpicker / colorpicker.js
1 /*
2 Copyright (c) 2011, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 2.9.0
6 */
7 /**
8  * Provides color conversion and validation utils
9  * @class YAHOO.util.Color
10  * @namespace YAHOO.util
11  */
12 YAHOO.util.Color = function() {
13
14     var ZERO     = "0",
15         isArray  = YAHOO.lang.isArray,
16         isNumber = YAHOO.lang.isNumber;
17
18     return {
19
20         /**
21          * Converts 0-1 to 0-255
22          * @method real2dec
23          * @param n {float} the number to convert
24          * @return {int} a number 0-255
25          */
26         real2dec: function(n) {
27             return Math.min(255, Math.round(n*256));
28         },
29
30         /**
31          * Converts HSV (h[0-360], s[0-1]), v[0-1] to RGB [255,255,255]
32          * @method hsv2rgb
33          * @param h {int|[int, float, float]} the hue, or an
34          *        array containing all three parameters
35          * @param s {float} the saturation
36          * @param v {float} the value/brightness
37          * @return {[int, int, int]} the red, green, blue values in
38          *          decimal.
39          */
40         hsv2rgb: function(h, s, v) { 
41
42             if (isArray(h)) {
43                 return this.hsv2rgb.call(this, h[0], h[1], h[2]);
44             }
45
46             var r, g, b,
47                 i = Math.floor((h/60)%6),
48                 f = (h/60)-i,
49                 p = v*(1-s),
50                 q = v*(1-f*s),
51                 t = v*(1-(1-f)*s),
52                 fn;
53
54             switch (i) {
55                 case 0: r=v; g=t; b=p; break;
56                 case 1: r=q; g=v; b=p; break;
57                 case 2: r=p; g=v; b=t; break;
58                 case 3: r=p; g=q; b=v; break;
59                 case 4: r=t; g=p; b=v; break;
60                 case 5: r=v; g=p; b=q; break;
61             }
62
63             fn=this.real2dec;
64
65             return [fn(r), fn(g), fn(b)];
66         },
67
68         /**
69          * Converts to RGB [255,255,255] to HSV (h[0-360], s[0-1]), v[0-1]
70          * @method rgb2hsv
71          * @param r {int|[int, int, int]} the red value, or an
72          *        array containing all three parameters
73          * @param g {int} the green value
74          * @param b {int} the blue value
75          * @return {[int, float, float]} the value converted to hsv
76          */
77         rgb2hsv: function(r, g, b) {
78
79             if (isArray(r)) {
80                 return this.rgb2hsv.apply(this, r);
81             }
82
83             r /= 255;
84             g /= 255;
85             b /= 255;
86
87             var h,s,
88                 min = Math.min(Math.min(r,g),b),
89                 max = Math.max(Math.max(r,g),b),
90                 delta = max-min,
91                 hsv;
92
93             switch (max) {
94                 case min: h=0; break;
95                 case r:   h=60*(g-b)/delta; 
96                           if (g<b) {
97                               h+=360;
98                           }
99                           break;
100                 case g:   h=(60*(b-r)/delta)+120; break;
101                 case b:   h=(60*(r-g)/delta)+240; break;
102             }
103             
104             s = (max === 0) ? 0 : 1-(min/max);
105
106             hsv = [Math.round(h), s, max];
107
108             return hsv;
109         },
110
111         /**
112          * Converts decimal rgb values into a hex string
113          * 255,255,255 -> FFFFFF
114          * @method rgb2hex
115          * @param r {int|[int, int, int]} the red value, or an
116          *        array containing all three parameters
117          * @param g {int} the green value
118          * @param b {int} the blue value
119          * @return {string} the hex string
120          */
121         rgb2hex: function(r, g, b) {
122             if (isArray(r)) {
123                 return this.rgb2hex.apply(this, r);
124             }
125
126             var f=this.dec2hex;
127             return f(r) + f(g) + f(b);
128         },
129      
130         /**
131          * Converts an int 0...255 to hex pair 00...FF
132          * @method dec2hex
133          * @param n {int} the number to convert
134          * @return {string} the hex equivalent
135          */
136         dec2hex: function(n) {
137             n = parseInt(n,10)|0;
138             n = (n > 255 || n < 0) ? 0 : n;
139
140             return (ZERO+n.toString(16)).slice(-2).toUpperCase();
141         },
142
143         /**
144          * Converts a hex pair 00...FF to an int 0...255 
145          * @method hex2dec
146          * @param str {string} the hex pair to convert
147          * @return {int} the decimal
148          */
149         hex2dec: function(str) {
150             return parseInt(str,16);
151         },
152
153         /**
154          * Converts a hex string to rgb
155          * @method hex2rgb
156          * @param str {string} the hex string
157          * @return {[int, int, int]} an array containing the rgb values
158          */
159         hex2rgb: function(s) { 
160             var f = this.hex2dec;
161             return [f(s.slice(0, 2)), f(s.slice(2, 4)), f(s.slice(4, 6))];
162         },
163
164         /**
165          * Returns the closest websafe color to the supplied rgb value.
166          * @method websafe
167          * @param r {int|[int, int, int]} the red value, or an
168          *        array containing all three parameters
169          * @param g {int} the green value
170          * @param b {int} the blue value
171          * @return {[int, int, int]} an array containing the closes
172          *                           websafe rgb colors.
173          */
174         websafe: function(r, g, b) {
175
176             if (isArray(r)) {
177                 return this.websafe.apply(this, r);
178             }
179
180             // returns the closest match [0, 51, 102, 153, 204, 255]
181             var f = function(v) {
182                 if (isNumber(v)) {
183                     v = Math.min(Math.max(0, v), 255);
184                     var i, next;
185                     for (i=0; i<256; i=i+51) {
186                         next = i+51;
187                         if (v >= i && v <= next) {
188                             return (v-i > 25) ? next : i;
189                         }
190                     }
191                 }
192
193                 return v;
194             };
195
196             return [f(r), f(g), f(b)];
197         }
198     };
199 }();
200
201
202 /**
203  * The colorpicker module provides a widget for selecting colors
204  * @module colorpicker
205  * @requires yahoo, dom, event, element, slider
206  */
207 (function() {
208
209     var _pickercount = 0,
210         util   = YAHOO.util,
211         lang   = YAHOO.lang,
212         Slider = YAHOO.widget.Slider,
213         Color  = util.Color,
214         Dom    = util.Dom,
215         Event  = util.Event,
216         sub    = lang.substitute,
217         
218         b = "yui-picker";
219     
220
221     /**
222      * A widget to select colors
223      * @namespace YAHOO.widget
224      * @class YAHOO.widget.ColorPicker
225      * @extends YAHOO.util.Element
226      * @constructor
227      * @param {HTMLElement | String | Object} el(optional) The html 
228      * element that represents the colorpicker, or the attribute object to use. 
229      * An element will be created if none provided.
230      * @param {Object} attr (optional) A key map of the colorpicker's 
231      * initial attributes.  Ignored if first arg is attributes object.
232      */
233     function ColorPicker(el, attr) {
234         _pickercount = _pickercount + 1;
235         attr = attr || {};
236         if (arguments.length === 1 && !YAHOO.lang.isString(el) && !el.nodeName) {
237             attr = el; // treat first arg as attr object
238             el = attr.element || null;
239         }
240         
241         if (!el && !attr.element) { // create if we dont have one
242             el = this._createHostElement(attr);
243         }
244
245         ColorPicker.superclass.constructor.call(this, el, attr); 
246
247         this.initPicker();
248     }
249
250     YAHOO.extend(ColorPicker, YAHOO.util.Element, {
251     
252         /**
253          * The element ids used by this control
254          * @property ID
255          * @final
256          */
257         ID : {
258
259             /**
260              * The id for the "red" form field
261              * @property ID.R
262              * @type String
263              * @final
264              * @default yui-picker-r
265              */
266             R: b + "-r",
267
268             /**
269              * The id for the "red" hex pair output
270              * @property ID.R_HEX
271              * @type String
272              * @final
273              * @default yui-picker-rhex
274              */
275             R_HEX: b + "-rhex",
276
277             /**
278              * The id for the "green" form field
279              * @property ID.G
280              * @type String
281              * @final
282              * @default yui-picker-g
283              */
284             G: b + "-g",
285
286             /**
287              * The id for the "green" hex pair output
288              * @property ID.G_HEX
289              * @type String
290              * @final
291              * @default yui-picker-ghex
292              */
293             G_HEX: b + "-ghex",
294
295
296             /**
297              * The id for the "blue" form field
298              * @property ID.B
299              * @type String
300              * @final
301              * @default yui-picker-b
302              */
303             B: b + "-b",
304
305             /**
306              * The id for the "blue" hex pair output
307              * @property ID.B_HEX
308              * @type String
309              * @final
310              * @default yui-picker-bhex
311              */
312             B_HEX: b + "-bhex",
313
314             /**
315              * The id for the "hue" form field
316              * @property ID.H
317              * @type String
318              * @final
319              * @default yui-picker-h
320              */
321             H: b + "-h",
322
323             /**
324              * The id for the "saturation" form field
325              * @property ID.S
326              * @type String
327              * @final
328              * @default yui-picker-s
329              */
330             S: b + "-s",
331
332             /**
333              * The id for the "value" form field
334              * @property ID.V
335              * @type String
336              * @final
337              * @default yui-picker-v
338              */
339             V: b + "-v",
340
341             /**
342              * The id for the picker region slider
343              * @property ID.PICKER_BG
344              * @type String
345              * @final
346              * @default yui-picker-bg
347              */
348             PICKER_BG:      b + "-bg",
349
350             /**
351              * The id for the picker region thumb
352              * @property ID.PICKER_THUMB
353              * @type String
354              * @final
355              * @default yui-picker-thumb
356              */
357             PICKER_THUMB:   b + "-thumb",
358
359             /**
360              * The id for the hue slider
361              * @property ID.HUE_BG
362              * @type String
363              * @final
364              * @default yui-picker-hue-bg
365              */
366             HUE_BG:         b + "-hue-bg",
367
368             /**
369              * The id for the hue thumb
370              * @property ID.HUE_THUMB
371              * @type String
372              * @final
373              * @default yui-picker-hue-thumb
374              */
375             HUE_THUMB:      b + "-hue-thumb",
376
377             /**
378              * The id for the hex value form field
379              * @property ID.HEX
380              * @type String
381              * @final
382              * @default yui-picker-hex
383              */
384             HEX:            b + "-hex",
385
386             /**
387              * The id for the color swatch
388              * @property ID.SWATCH
389              * @type String
390              * @final
391              * @default yui-picker-swatch
392              */
393             SWATCH:         b + "-swatch",
394
395             /**
396              * The id for the websafe color swatch
397              * @property ID.WEBSAFE_SWATCH
398              * @type String
399              * @final
400              * @default yui-picker-websafe-swatch
401              */
402             WEBSAFE_SWATCH: b + "-websafe-swatch",
403
404             /**
405              * The id for the control details
406              * @property ID.CONTROLS
407              * @final
408              * @default yui-picker-controls
409              */
410             CONTROLS: b + "-controls",
411
412             /**
413              * The id for the rgb controls
414              * @property ID.RGB_CONTROLS
415              * @final
416              * @default yui-picker-rgb-controls
417              */
418             RGB_CONTROLS: b + "-rgb-controls",
419
420             /**
421              * The id for the hsv controls
422              * @property ID.HSV_CONTROLS
423              * @final
424              * @default yui-picker-hsv-controls
425              */
426             HSV_CONTROLS: b + "-hsv-controls",
427             
428             /**
429              * The id for the hsv controls
430              * @property ID.HEX_CONTROLS
431              * @final
432              * @default yui-picker-hex-controls
433              */
434             HEX_CONTROLS: b + "-hex-controls",
435
436             /**
437              * The id for the hex summary
438              * @property ID.HEX_SUMMARY
439              * @final
440              * @default yui-picker-hex-summary
441              */
442             HEX_SUMMARY: b + "-hex-summary",
443
444             /**
445              * The id for the controls section header
446              * @property ID.CONTROLS_LABEL
447              * @final
448              * @default yui-picker-controls-label
449              */
450             CONTROLS_LABEL: b + "-controls-label"
451         },
452
453         /**
454          * Constants for any script-generated messages.  The values here
455          * are the default messages.  They can be updated by providing
456          * the complete list to the constructor for the "txt" attribute.
457          * Note: the strings are added to the DOM as HTML.
458          * @property TXT
459          * @final
460          */
461         TXT : {
462             ILLEGAL_HEX: "Illegal hex value entered",
463             SHOW_CONTROLS: "Show color details",
464             HIDE_CONTROLS: "Hide color details",
465             CURRENT_COLOR: "Currently selected color: {rgb}",
466             CLOSEST_WEBSAFE: "Closest websafe color: {rgb}. Click to select.",
467             R: "R",
468             G: "G",
469             B: "B",
470             H: "H",
471             S: "S",
472             V: "V",
473             HEX: "#",
474             DEG: "\u00B0",
475             PERCENT: "%"
476         },
477
478         /**
479          * Constants for the default image locations for img tags that are
480          * generated by the control.  They can be modified by passing the
481          * complete list to the contructor for the "images" attribute
482          * @property IMAGE
483          * @final
484          */
485         IMAGE : {
486             PICKER_THUMB: "../../build/colorpicker/assets/picker_thumb.png",
487             HUE_THUMB: "../../build/colorpicker/assets/hue_thumb.png"
488         },
489
490         /**
491          * Constants for the control's default default values
492          * @property DEFAULT
493          * @final
494          */
495         DEFAULT : {
496             PICKER_SIZE: 180
497         },
498
499         /**
500          * Constants for the control's configuration attributes
501          * @property OPT
502          * @final
503          */
504         OPT : {
505             HUE         : "hue",
506             SATURATION  : "saturation",
507             VALUE       : "value",
508             RED     : "red",
509             GREEN   : "green",
510             BLUE    : "blue",
511             HSV     : "hsv",
512             RGB     : "rgb",
513             WEBSAFE : "websafe",
514             HEX     : "hex",
515             PICKER_SIZE       : "pickersize",
516             SHOW_CONTROLS     : "showcontrols",
517             SHOW_RGB_CONTROLS : "showrgbcontrols",
518             SHOW_HSV_CONTROLS : "showhsvcontrols",
519             SHOW_HEX_CONTROLS : "showhexcontrols",
520             SHOW_HEX_SUMMARY  : "showhexsummary",
521             SHOW_WEBSAFE      : "showwebsafe",
522             CONTAINER         : "container",
523             IDS      : "ids",
524             ELEMENTS : "elements",
525             TXT      : "txt",
526             IMAGES   : "images",
527             ANIMATE  : "animate"
528         },
529
530         /**
531          * Flag to allow individual UI updates to forego animation if available.
532          * True during construction for initial thumb placement.  Set to false
533          * after that.
534          *
535          * @property skipAnim
536          * @type Boolean
537          * @default true
538          */
539         skipAnim : true,
540
541         /**
542          * Creates the host element if it doesn't exist
543          * @method _createHostElement
544          * @protected
545          */
546         _createHostElement : function () {
547             var el = document.createElement('div');
548
549             if (this.CSS.BASE) {
550                 el.className = this.CSS.BASE;
551             }
552             
553             return el;
554         },
555
556         /**
557          * Moves the hue slider into the position dictated by the current state
558          * of the control
559          * @method _updateHueSlider
560          * @protected
561          */
562         _updateHueSlider : function() {
563             var size = this.get(this.OPT.PICKER_SIZE),
564                 h = this.get(this.OPT.HUE);
565
566             h = size - Math.round(h / 360 * size);
567             
568             // 0 is at the top and bottom of the hue slider.  Always go to
569             // the top so we don't end up sending the thumb to the bottom
570             // when the value didn't actually change (e.g., a conversion
571             // produced 360 instead of 0 and the value was already 0).
572             if (h === size) {
573                 h = 0;
574             }
575
576             this.hueSlider.setValue(h, this.skipAnim);
577         },
578
579         /**
580          * Moves the picker slider into the position dictated by the current state
581          * of the control
582          * @method _updatePickerSlider
583          * @protected
584          */
585         _updatePickerSlider : function() {
586             var size = this.get(this.OPT.PICKER_SIZE),
587                 s = this.get(this.OPT.SATURATION),
588                 v = this.get(this.OPT.VALUE);
589
590             s = Math.round(s * size / 100);
591             v = Math.round(size - (v * size / 100));
592
593
594             this.pickerSlider.setRegionValue(s, v, this.skipAnim);
595         },
596
597         /**
598          * Moves the sliders into the position dictated by the current state
599          * of the control
600          * @method _updateSliders
601          * @protected
602          */
603         _updateSliders : function() {
604             this._updateHueSlider();
605             this._updatePickerSlider();
606         },
607
608         /**
609          * Sets the control to the specified rgb value and
610          * moves the sliders to the proper positions
611          * @method setValue
612          * @param rgb {[int, int, int]} the rgb value
613          * @param silent {boolean} whether or not to fire the change event
614          */
615         setValue : function(rgb, silent) {
616             silent = (silent) || false;
617             this.set(this.OPT.RGB, rgb, silent);
618             this._updateSliders();
619         },
620
621         /**
622          * The hue slider
623          * @property hueSlider
624          * @type YAHOO.widget.Slider
625          */
626         hueSlider : null,
627         
628         /**
629          * The picker region
630          * @property pickerSlider
631          * @type YAHOO.widget.Slider
632          */
633         pickerSlider : null,
634
635         /**
636          * Translates the slider value into hue, int[0,359]
637          * @method _getH
638          * @protected
639          * @return {int} the hue from 0 to 359
640          */
641         _getH : function() {
642             var size = this.get(this.OPT.PICKER_SIZE),
643                 h = (size - this.hueSlider.getValue()) / size;
644             h = Math.round(h*360);
645             return (h === 360) ? 0 : h;
646         },
647
648         /**
649          * Translates the slider value into saturation, int[0,1], left to right
650          * @method _getS
651          * @protected
652          * @return {int} the saturation from 0 to 1
653          */
654         _getS : function() {
655             return this.pickerSlider.getXValue() / this.get(this.OPT.PICKER_SIZE);
656         },
657
658         /**
659          * Translates the slider value into value/brightness, int[0,1], top
660          * to bottom
661          * @method _getV
662          * @protected
663          * @return {int} the value from 0 to 1
664          */
665         _getV : function() {
666             var size = this.get(this.OPT.PICKER_SIZE);
667             return (size - this.pickerSlider.getYValue()) / size;
668         },
669
670         /**
671          * Updates the background of the swatch with the current rbg value.
672          * Also updates the websafe swatch to the closest websafe color
673          * @method _updateSwatch
674          * @protected
675          */
676         _updateSwatch : function() {
677             var rgb = this.get(this.OPT.RGB),
678                 websafe = this.get(this.OPT.WEBSAFE),
679                 el = this.getElement(this.ID.SWATCH),
680                 color = rgb.join(","),
681                 txt = this.get(this.OPT.TXT);
682
683             Dom.setStyle(el, "background-color", "rgb(" + color  + ")");
684             el.title = sub(txt.CURRENT_COLOR, {
685                     "rgb": "#" + this.get(this.OPT.HEX)
686                 });
687
688
689             el = this.getElement(this.ID.WEBSAFE_SWATCH);
690             color = websafe.join(",");
691
692             Dom.setStyle(el, "background-color", "rgb(" + color + ")");
693             el.title = sub(txt.CLOSEST_WEBSAFE, {
694                     "rgb": "#" + Color.rgb2hex(websafe)
695                 });
696
697         },
698
699         /**
700          * Reads the sliders and converts the values to RGB, updating the
701          * internal state for all the individual form fields
702          * @method _getValuesFromSliders
703          * @protected
704          */
705         _getValuesFromSliders : function() {
706             this.set(this.OPT.RGB, Color.hsv2rgb(this._getH(), this._getS(), this._getV()));
707         },
708
709         /**
710          * Updates the form field controls with the state data contained
711          * in the control.
712          * @method _updateFormFields
713          * @protected
714          */
715         _updateFormFields : function() {
716             this.getElement(this.ID.H).value = this.get(this.OPT.HUE);
717             this.getElement(this.ID.S).value = this.get(this.OPT.SATURATION);
718             this.getElement(this.ID.V).value = this.get(this.OPT.VALUE);
719             this.getElement(this.ID.R).value = this.get(this.OPT.RED);
720             this.getElement(this.ID.R_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.RED));
721             this.getElement(this.ID.G).value = this.get(this.OPT.GREEN);
722             this.getElement(this.ID.G_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.GREEN));
723             this.getElement(this.ID.B).value = this.get(this.OPT.BLUE);
724             this.getElement(this.ID.B_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.BLUE));
725             this.getElement(this.ID.HEX).value = this.get(this.OPT.HEX);
726         },
727
728         /**
729          * Event handler for the hue slider.
730          * @method _onHueSliderChange
731          * @param newOffset {int} pixels from the start position
732          * @protected
733          */
734         _onHueSliderChange : function(newOffset) {
735
736             var h        = this._getH(),
737                 rgb      = Color.hsv2rgb(h, 1, 1),
738                 styleDef = "rgb(" + rgb.join(",") + ")";
739
740             this.set(this.OPT.HUE, h, true);
741
742             // set picker background to the hue
743             Dom.setStyle(this.getElement(this.ID.PICKER_BG), "background-color", styleDef);
744
745             if (this.hueSlider.valueChangeSource !== Slider.SOURCE_SET_VALUE) {
746                 this._getValuesFromSliders();
747             }
748
749             this._updateFormFields();
750             this._updateSwatch();
751         },
752
753         /**
754          * Event handler for the picker slider, which controls the
755          * saturation and value/brightness.
756          * @method _onPickerSliderChange
757          * @param newOffset {{x: int, y: int}} x/y pixels from the start position
758          * @protected
759          */
760         _onPickerSliderChange : function(newOffset) {
761
762             var s=this._getS(), v=this._getV();
763             this.set(this.OPT.SATURATION, Math.round(s*100), true);
764             this.set(this.OPT.VALUE, Math.round(v*100), true);
765
766             if (this.pickerSlider.valueChangeSource !== Slider.SOURCE_SET_VALUE) {
767                 this._getValuesFromSliders();
768             }
769
770             this._updateFormFields();
771             this._updateSwatch();
772         },
773
774
775         /**
776          * Key map to well-known commands for txt field input
777          * @method _getCommand
778          * @param e {Event} the keypress or keydown event
779          * @return {int} a command code
780          * <ul>
781          * <li>0 = not a number, letter in range, or special key</li>
782          * <li>1 = number</li>
783          * <li>2 = a-fA-F</li>
784          * <li>3 = increment (up arrow)</li>
785          * <li>4 = decrement (down arrow)</li>
786          * <li>5 = special key (tab, delete, return, escape, left, right)</li> 
787          * <li>6 = return</li>
788          * </ul>
789          * @protected
790          */
791         _getCommand : function(e) {
792             var c = Event.getCharCode(e);
793
794             //alert(Event.getCharCode(e) + ", " + e.keyCode + ", " + e.charCode);
795
796             // special keys
797             if (c === 38) { // up arrow
798                 return 3;
799             } else if (c === 13) { // return
800                 return 6;
801             } else if (c === 40) { // down array
802                 return 4;
803             } else if (c >= 48 && c<=57) { // 0-9
804                 return 1;
805             } else if (c >= 97 && c<=102) { // a-f
806                 return 2;
807             } else if (c >= 65 && c<=70) { // A-F
808                 return 2;
809             //} else if ("8, 9, 13, 27, 37, 39".indexOf(c) > -1 || 
810             //              (c >= 112 && c <=123)) { // including F-keys
811             // tab, delete, return, escape, left, right or ctrl/meta sequences
812             } else if ("8, 9, 13, 27, 37, 39".indexOf(c) > -1 ||
813                        e.ctrlKey || e.metaKey) { // special chars
814                 return 5;
815             } else { // something we probably don't want
816                 return 0;
817             }
818         },
819
820         /**
821          * Use the value of the text field to update the control
822          * @method _useFieldValue
823          * @param e {Event} an event
824          * @param el {HTMLElement} the field
825          * @param prop {string} the key to the linked property
826          * @protected
827          */
828         _useFieldValue : function(e, el, prop) {
829             var val = el.value;
830
831             if (prop !== this.OPT.HEX) {
832                 val = parseInt(val, 10);
833             }
834
835             if (val !== this.get(prop)) {
836                 this.set(prop, val);
837             }
838         },
839
840         /**
841          * Handle keypress on one of the rgb or hsv fields.
842          * @method _rgbFieldKeypress
843          * @param e {Event} the keypress event
844          * @param el {HTMLElement} the field
845          * @param prop {string} the key to the linked property
846          * @protected
847          */
848         _rgbFieldKeypress : function(e, el, prop) {
849             var command = this._getCommand(e),
850                 inc = (e.shiftKey) ? 10 : 1;
851             switch (command) {
852                 case 6: // return, update the value
853                     this._useFieldValue.apply(this, arguments);
854                     break;
855                             
856                 case 3: // up arrow, increment
857                     this.set(prop, Math.min(this.get(prop)+inc, 255));
858                     this._updateFormFields();
859                     //Event.stopEvent(e);
860                     break;
861                 case 4: // down arrow, decrement
862                     this.set(prop, Math.max(this.get(prop)-inc, 0));
863                     this._updateFormFields();
864                     //Event.stopEvent(e);
865                     break;
866
867                 default:
868             }
869
870         },
871
872         /**
873          * Handle keydown on the hex field
874          * @method _hexFieldKeypress
875          * @param e {Event} the keypress event
876          * @param el {HTMLElement} the field
877          * @param prop {string} the key to the linked property
878          * @protected
879          */
880         _hexFieldKeypress : function(e, el, prop) {
881             var command = this._getCommand(e);
882             if (command === 6) { // return, update the value
883                 this._useFieldValue.apply(this, arguments);
884             }
885         },
886
887         /** 
888          * Allows numbers and special chars, and by default allows a-f.  
889          * Used for the hex field keypress handler.
890          * @method _hexOnly
891          * @param e {Event} the event
892          * @param numbersOnly omits a-f if set to true
893          * @protected
894          * @return {boolean} false if we are canceling the event
895          */
896         _hexOnly : function(e, numbersOnly) {
897             var command = this._getCommand(e);
898             switch (command) {
899                 case 6: // return
900                 case 5: // special char
901                 case 1: // number
902                     break;
903                 case 2: // hex char (a-f)
904                     if (numbersOnly !== true) {
905                         break;
906                     }
907
908                     // fallthrough is intentional
909
910                 default: // prevent alpha and punctuation
911                     Event.stopEvent(e);
912                     return false;
913             }
914         },
915
916         /** 
917          * Allows numbers and special chars only.  Used for the
918          * rgb and hsv fields keypress handler.
919          * @method _numbersOnly
920          * @param e {Event} the event
921          * @protected
922          * @return {boolean} false if we are canceling the event
923          */
924         _numbersOnly : function(e) {
925             return this._hexOnly(e, true);
926         },
927
928         /**
929          * Returns the element reference that is saved.  The id can be either
930          * the element id, or the key for this id in the "id" config attribute.
931          * For instance, the host element id can be obtained by passing its
932          * id (default: "yui_picker") or by its key "YUI_PICKER".
933          * @param id {string} the element id, or key 
934          * @return {HTMLElement} a reference to the element
935          */
936         getElement : function(id) { 
937             return this.get(this.OPT.ELEMENTS)[this.get(this.OPT.IDS)[id]]; 
938         },
939
940         _createElements : function() {
941             var el, child, img, fld, p,
942                 ids = this.get(this.OPT.IDS),
943                 txt = this.get(this.OPT.TXT),
944                 images = this.get(this.OPT.IMAGES),
945                 Elem = function(type, o) {
946                     var n = document.createElement(type);
947                     if (o) {
948                         lang.augmentObject(n, o, true);
949                     }
950                     return n;
951                 },
952                 RGBElem = function(type, obj) {
953                     var o = lang.merge({
954                             //type: "txt",
955                             autocomplete: "off",
956                             value: "0",
957                             size: 3,
958                             maxlength: 3
959                         }, obj);
960
961                     o.name = o.id;
962                     return new Elem(type, o);
963                 };
964
965             p = this.get("element");
966
967             // Picker slider (S and V) ---------------------------------------------
968
969             el = new Elem("div", {
970                 id: ids[this.ID.PICKER_BG],
971                 className: "yui-picker-bg",
972                 tabIndex: -1,
973                 hideFocus: true
974             });
975
976             child = new Elem("div", {
977                 id: ids[this.ID.PICKER_THUMB],
978                 className: "yui-picker-thumb"
979             });
980
981             img = new Elem("img", {
982                 src: images.PICKER_THUMB
983             });
984
985             child.appendChild(img);
986             el.appendChild(child);
987             p.appendChild(el);
988             
989             // Hue slider ---------------------------------------------
990             el = new Elem("div", {
991                 id: ids[this.ID.HUE_BG],
992                 className: "yui-picker-hue-bg",
993                 tabIndex: -1,
994                 hideFocus: true
995             });
996
997             child = new Elem("div", {
998                 id: ids[this.ID.HUE_THUMB],
999                 className: "yui-picker-hue-thumb"
1000             });
1001
1002             img = new Elem("img", {
1003                 src: images.HUE_THUMB
1004             });
1005
1006             child.appendChild(img);
1007             el.appendChild(child);
1008             p.appendChild(el);
1009
1010
1011             // controls ---------------------------------------------
1012
1013             el = new Elem("div", {
1014                 id: ids[this.ID.CONTROLS],
1015                 className: "yui-picker-controls"
1016             });
1017
1018             p.appendChild(el);
1019             p = el;
1020
1021                 // controls header
1022                 el = new Elem("div", {
1023                     className: "hd"
1024                 });
1025
1026                 child = new Elem("a", {
1027                     id: ids[this.ID.CONTROLS_LABEL],
1028                     //className: "yui-picker-controls-label",
1029                     href: "#"
1030                 });
1031                 el.appendChild(child);
1032                 p.appendChild(el);
1033
1034                 // bd
1035                 el = new Elem("div", {
1036                     className: "bd"
1037                 });
1038
1039                 p.appendChild(el);
1040                 p = el;
1041
1042                     // rgb
1043                     el = new Elem("ul", {
1044                         id: ids[this.ID.RGB_CONTROLS],
1045                         className: "yui-picker-rgb-controls"
1046                     });
1047
1048                     child = new Elem("li");
1049                     child.appendChild(document.createTextNode(txt.R + " "));
1050
1051                     fld = new RGBElem("input", {
1052                         id: ids[this.ID.R],
1053                         className: "yui-picker-r"
1054                     });
1055
1056                     child.appendChild(fld);
1057                     el.appendChild(child);
1058
1059                     child = new Elem("li");
1060                     child.appendChild(document.createTextNode(txt.G + " "));
1061
1062                     fld = new RGBElem("input", {
1063                         id: ids[this.ID.G],
1064                         className: "yui-picker-g"
1065                     });
1066
1067                     child.appendChild(fld);
1068                     el.appendChild(child);
1069
1070                     child = new Elem("li");
1071                     child.appendChild(document.createTextNode(txt.B + " "));
1072
1073                     fld = new RGBElem("input", {
1074                         id: ids[this.ID.B],
1075                         className: "yui-picker-b"
1076                     });
1077
1078                     child.appendChild(fld);
1079                     el.appendChild(child);
1080
1081                     p.appendChild(el);
1082
1083                     // hsv
1084                     el = new Elem("ul", {
1085                         id: ids[this.ID.HSV_CONTROLS],
1086                         className: "yui-picker-hsv-controls"
1087                     });
1088
1089                     child = new Elem("li");
1090                     child.appendChild(document.createTextNode(txt.H + " "));
1091
1092                     fld = new RGBElem("input", {
1093                         id: ids[this.ID.H],
1094                         className: "yui-picker-h"
1095                     });
1096
1097                     child.appendChild(fld);
1098                     child.appendChild(document.createTextNode(" " + txt.DEG));
1099
1100                     el.appendChild(child);
1101
1102                     child = new Elem("li");
1103                     child.appendChild(document.createTextNode(txt.S + " "));
1104
1105                     fld = new RGBElem("input", {
1106                         id: ids[this.ID.S],
1107                         className: "yui-picker-s"
1108                     });
1109
1110                     child.appendChild(fld);
1111                     child.appendChild(document.createTextNode(" " + txt.PERCENT));
1112
1113                     el.appendChild(child);
1114
1115                     child = new Elem("li");
1116                     child.appendChild(document.createTextNode(txt.V + " "));
1117
1118                     fld = new RGBElem("input", {
1119                         id: ids[this.ID.V],
1120                         className: "yui-picker-v"
1121                     });
1122
1123                     child.appendChild(fld);
1124                     child.appendChild(document.createTextNode(" " + txt.PERCENT));
1125
1126                     el.appendChild(child);
1127                     p.appendChild(el);
1128
1129
1130                     // hex summary
1131
1132                     el = new Elem("ul", {
1133                         id: ids[this.ID.HEX_SUMMARY],
1134                         className: "yui-picker-hex_summary"
1135                     });
1136
1137                     child = new Elem("li", {
1138                         id: ids[this.ID.R_HEX]
1139                     });
1140                     el.appendChild(child);
1141
1142                     child = new Elem("li", {
1143                         id: ids[this.ID.G_HEX]
1144                     });
1145                     el.appendChild(child);
1146
1147                     child = new Elem("li", {
1148                         id: ids[this.ID.B_HEX]
1149                     });
1150                     el.appendChild(child);
1151                     p.appendChild(el);
1152
1153                     // hex field
1154                     el = new Elem("div", {
1155                         id: ids[this.ID.HEX_CONTROLS],
1156                         className: "yui-picker-hex-controls"
1157                     });
1158                     el.appendChild(document.createTextNode(txt.HEX + " "));
1159
1160                     child = new RGBElem("input", {
1161                         id: ids[this.ID.HEX],
1162                         className: "yui-picker-hex",
1163                         size: 6,
1164                         maxlength: 6
1165                     });
1166
1167                     el.appendChild(child);
1168                     p.appendChild(el);
1169
1170                     p = this.get("element");
1171
1172                     // swatch
1173                     el = new Elem("div", {
1174                         id: ids[this.ID.SWATCH],
1175                         className: "yui-picker-swatch"
1176                     });
1177
1178                     p.appendChild(el);
1179
1180                     // websafe swatch
1181                     el = new Elem("div", {
1182                         id: ids[this.ID.WEBSAFE_SWATCH],
1183                         className: "yui-picker-websafe-swatch"
1184                     });
1185
1186                     p.appendChild(el);
1187
1188         },
1189
1190         _attachRGBHSV : function(id, config) {
1191             Event.on(this.getElement(id), "keydown", function(e, me) {
1192                     me._rgbFieldKeypress(e, this, config);
1193                 }, this);
1194             Event.on(this.getElement(id), "keypress", this._numbersOnly, this, true);
1195             Event.on(this.getElement(id), "blur", function(e, me) {
1196                     me._useFieldValue(e, this, config);
1197                 }, this);
1198         },
1199
1200
1201         /**
1202          * Updates the rgb attribute with the current state of the r,g,b
1203          * fields.  This is invoked from change listeners on these
1204          * attributes to facilitate updating these values from the
1205          * individual form fields
1206          * @method _updateRGB
1207          * @protected
1208          */
1209         _updateRGB : function() {
1210             var rgb = [this.get(this.OPT.RED), 
1211                        this.get(this.OPT.GREEN),
1212                        this.get(this.OPT.BLUE)];
1213
1214             this.set(this.OPT.RGB, rgb);
1215
1216             this._updateSliders();
1217         },
1218
1219         /**
1220          * Creates any missing DOM structure.
1221          *
1222          * @method _initElements
1223          * @protected
1224          */
1225         _initElements : function () {
1226             // bind all of our elements
1227             var o=this.OPT, 
1228                 ids = this.get(o.IDS), 
1229                 els = this.get(o.ELEMENTS), 
1230                       i, el, id;
1231
1232             // Add the default value as a key for each element for easier lookup
1233             for (i in this.ID) {
1234                 if (lang.hasOwnProperty(this.ID, i)) {
1235                     ids[this.ID[i]] = ids[i];
1236                 }
1237             }
1238
1239             // Check for picker element, if not there, create all of them
1240             el = Dom.get(ids[this.ID.PICKER_BG]);
1241             if (!el) {
1242                 this._createElements();
1243             } else {
1244             }
1245
1246             for (i in ids) {
1247                 if (lang.hasOwnProperty(ids, i)) {
1248                     // look for element
1249                     el = Dom.get(ids[i]);
1250
1251                     // generate an id if the implementer passed in an element reference,
1252                     // and the element did not have an id already
1253                     id = Dom.generateId(el);
1254
1255                     // update the id in case we generated the id
1256                     ids[i] = id; // key is WEBSAFE_SWATCH
1257                     ids[ids[i]] = id; // key is websafe_swatch
1258
1259                     // store the dom ref
1260                     els[id] = el;
1261                 }
1262             }
1263
1264         },
1265
1266         /**
1267          * Sets the initial state of the sliders
1268          * @method initPicker
1269          */
1270         initPicker : function () {
1271             this._initSliders();
1272             this._bindUI();
1273             this.syncUI(true);
1274         },
1275
1276         /**
1277          * Creates the Hue and Value/Saturation Sliders.
1278          *
1279          * @method _initSliders
1280          * @protected
1281          */
1282         _initSliders : function () {
1283             var ID = this.ID,
1284                 size = this.get(this.OPT.PICKER_SIZE);
1285
1286
1287             this.hueSlider = Slider.getVertSlider(
1288                 this.getElement(ID.HUE_BG), 
1289                 this.getElement(ID.HUE_THUMB), 0, size);
1290
1291             this.pickerSlider = Slider.getSliderRegion(
1292                 this.getElement(ID.PICKER_BG), 
1293                 this.getElement(ID.PICKER_THUMB), 0, size, 0, size);
1294
1295             // Apply animate attribute configuration
1296             this.set(this.OPT.ANIMATE, this.get(this.OPT.ANIMATE));
1297         },
1298
1299         /**
1300          * Adds event listeners to Sliders and UI elements.  Wires everything
1301          * up.
1302          *
1303          * @method _bindUI
1304          * @protected
1305          */
1306         _bindUI : function () {
1307             var ID = this.ID,
1308                 O  = this.OPT;
1309
1310             this.hueSlider.subscribe("change",
1311                 this._onHueSliderChange, this, true);
1312             this.pickerSlider.subscribe("change",
1313                 this._onPickerSliderChange, this, true);
1314
1315             Event.on(this.getElement(ID.WEBSAFE_SWATCH), "click", function(e) {
1316                    this.setValue(this.get(O.WEBSAFE));
1317                }, this, true);
1318
1319             Event.on(this.getElement(ID.CONTROLS_LABEL), "click", function(e) {
1320                    this.set(O.SHOW_CONTROLS, !this.get(O.SHOW_CONTROLS));
1321                    Event.preventDefault(e);
1322                }, this, true);
1323
1324             this._attachRGBHSV(ID.R, O.RED); 
1325             this._attachRGBHSV(ID.G, O.GREEN); 
1326             this._attachRGBHSV(ID.B, O.BLUE); 
1327             this._attachRGBHSV(ID.H, O.HUE); 
1328             this._attachRGBHSV(ID.S, O.SATURATION); 
1329             this._attachRGBHSV(ID.V, O.VALUE); 
1330
1331             Event.on(this.getElement(ID.HEX), "keydown", function(e, me) {
1332                     me._hexFieldKeypress(e, this, O.HEX);
1333                 }, this);
1334
1335             Event.on(this.getElement(this.ID.HEX), "keypress",
1336                 this._hexOnly, this,true);
1337             Event.on(this.getElement(this.ID.HEX), "blur", function(e, me) {
1338                     me._useFieldValue(e, this, O.HEX);
1339                 }, this);
1340         },
1341
1342         /**
1343          * Wrapper for _updateRGB, but allows non-animated update
1344          *
1345          * @method syncUI
1346          * @param skipAnim {Boolean} Omit Slider animation for this action
1347          */
1348         syncUI : function (skipAnim) {
1349             this.skipAnim = skipAnim;
1350             this._updateRGB();
1351             this.skipAnim = false;
1352         },
1353
1354
1355         /**
1356          * Updates the RGB values from the current state of the HSV
1357          * values.  Executed when the one of the HSV form fields are
1358          * updated
1359          * _updateRGBFromHSV
1360          * @protected
1361          */
1362         _updateRGBFromHSV : function() {
1363             var hsv = [this.get(this.OPT.HUE), 
1364                        this.get(this.OPT.SATURATION)/100,
1365                        this.get(this.OPT.VALUE)/100],
1366                 rgb = Color.hsv2rgb(hsv);
1367
1368             this.set(this.OPT.RGB, rgb);
1369
1370             this._updateSliders();
1371         },
1372
1373         /**
1374          * Parses the hex string to normalize shorthand values, converts
1375          * the hex value to rgb and updates the rgb attribute (which
1376          * updates the state for all of the other values)
1377          * method _updateHex
1378          * @protected
1379          */
1380         _updateHex : function() {
1381            
1382             var hex = this.get(this.OPT.HEX),
1383                 l   = hex.length,
1384                 c,i,rgb;
1385
1386             // support #369 -> #336699 shorthand
1387             if (l === 3) {
1388                 c = hex.split("");
1389                 for (i=0; i<l; i=i+1) {
1390                     c[i] = c[i] + c[i];
1391                 }
1392
1393                 hex = c.join("");
1394             }
1395
1396             if (hex.length !== 6) {
1397                 return false;
1398             }
1399
1400             rgb = Color.hex2rgb(hex);
1401
1402
1403             this.setValue(rgb);
1404         },
1405
1406
1407         /**
1408          * Returns the cached element reference.  If the id is not a string, it
1409          * is assumed that it is an element and this is returned.
1410          * @param id {string|HTMLElement} the element key, id, or ref
1411          * @param on {boolean} hide or show.  If true, show
1412          * @protected
1413          */
1414         _hideShowEl : function(id, on) {
1415             var el = (lang.isString(id) ? this.getElement(id) : id);
1416             Dom.setStyle(el, "display", (on) ? "" : "none");
1417         },
1418
1419
1420         /**
1421          * Sets up the config attributes and the change listeners for this
1422          * properties
1423          * @method initAttributes
1424          * @param attr An object containing default attribute values
1425          */
1426         initAttributes : function(attr) {
1427
1428             attr = attr || {};
1429             ColorPicker.superclass.initAttributes.call(this, attr);
1430             
1431             /**
1432              * The size of the picker. Trying to change this is not recommended.
1433              * @attribute pickersize
1434              * @default 180
1435              * @type int
1436              */
1437             this.setAttributeConfig(this.OPT.PICKER_SIZE, {
1438                     value: attr.size || this.DEFAULT.PICKER_SIZE
1439                 });
1440
1441             /**
1442              * The current hue value 0-360
1443              * @attribute hue
1444              * @type int
1445              */
1446             this.setAttributeConfig(this.OPT.HUE, {
1447                     value: attr.hue || 0,
1448                     validator: lang.isNumber
1449                 });
1450
1451             /**
1452              * The current saturation value 0-100
1453              * @attribute saturation
1454              * @type int
1455              */
1456             this.setAttributeConfig(this.OPT.SATURATION, {
1457                     value: attr.saturation || 0,
1458                     validator: lang.isNumber
1459                 });
1460
1461             /**
1462              * The current value/brightness value 0-100
1463              * @attribute value
1464              * @type int
1465              */
1466             this.setAttributeConfig(this.OPT.VALUE, {
1467                     value: lang.isNumber(attr.value) ? attr.value : 100,
1468                     validator: lang.isNumber
1469                 });
1470
1471             /**
1472              * The current red value 0-255
1473              * @attribute red
1474              * @type int
1475              */
1476             this.setAttributeConfig(this.OPT.RED, {
1477                     value: lang.isNumber(attr.red) ? attr.red : 255,
1478                     validator: lang.isNumber
1479                 });
1480
1481             /**
1482              * The current green value 0-255
1483              * @attribute green 
1484              * @type int
1485              */
1486             this.setAttributeConfig(this.OPT.GREEN, {
1487                     value: lang.isNumber(attr.green) ? attr.green : 255,
1488                     validator: lang.isNumber
1489                 });
1490
1491             /**
1492              * The current blue value 0-255
1493              * @attribute blue
1494              * @type int
1495              */
1496             this.setAttributeConfig(this.OPT.BLUE, {
1497                     value: lang.isNumber(attr.blue) ? attr.blue : 255,
1498                     validator: lang.isNumber
1499                 });
1500
1501             /**
1502              * The current hex value #000000-#FFFFFF, without the #
1503              * @attribute hex
1504              * @type string
1505              */
1506             this.setAttributeConfig(this.OPT.HEX, {
1507                     value: attr.hex || "FFFFFF",
1508                     validator: lang.isString
1509                 });
1510
1511             /**
1512              * The current rgb value.  Updates the state of all of the
1513              * other value fields.  Read-only: use setValue to set the
1514              * controls rgb value.
1515              * @attribute hex
1516              * @type [int, int, int]
1517              * @readonly
1518              */
1519             this.setAttributeConfig(this.OPT.RGB, {
1520                     value: attr.rgb || [255,255,255],
1521                     method: function(rgb) {
1522
1523                         this.set(this.OPT.RED, rgb[0], true);
1524                         this.set(this.OPT.GREEN, rgb[1], true);
1525                         this.set(this.OPT.BLUE, rgb[2], true);
1526
1527                         var websafe = Color.websafe(rgb),
1528                             hex = Color.rgb2hex(rgb),
1529                             hsv = Color.rgb2hsv(rgb);
1530
1531                         this.set(this.OPT.WEBSAFE, websafe, true);
1532                         this.set(this.OPT.HEX, hex, true);
1533
1534
1535
1536                         // fix bug #1754338 - when saturation is 0, hue is
1537                         // silently always set to 0, but input field not updated
1538                         if (hsv[1]) {
1539                             this.set(this.OPT.HUE, hsv[0], true);
1540                         }
1541                         this.set(this.OPT.SATURATION, Math.round(hsv[1]*100), true);
1542                         this.set(this.OPT.VALUE, Math.round(hsv[2]*100), true);
1543                     },
1544                     readonly: true
1545                 });
1546
1547             /**
1548              * If the color picker will live inside of a container object,
1549              * set, provide a reference to it so the control can use the
1550              * container's events.
1551              * @attribute container
1552              * @type YAHOO.widget.Panel
1553              */
1554             this.setAttributeConfig(this.OPT.CONTAINER, {
1555                         value: null,
1556                         method: function(container) {
1557                             if (container) {
1558                                 // Position can get out of sync when the
1559                                 // control is manipulated while display is
1560                                 // none.  Resetting the slider constraints
1561                                 // when it is visible gets the state back in
1562                                 // order.
1563                                 container.showEvent.subscribe(function() {
1564                                     // this.pickerSlider.thumb.resetConstraints();
1565                                     // this.hueSlider.thumb.resetConstraints();
1566                                     this.pickerSlider.focus();
1567                                 }, this, true);
1568                             }
1569                         }
1570                     });
1571             /**
1572              * The closest current websafe value
1573              * @attribute websafe
1574              * @type int
1575              */
1576             this.setAttributeConfig(this.OPT.WEBSAFE, {
1577                     value: attr.websafe || [255,255,255]
1578                 });
1579
1580
1581             var ids = attr.ids || lang.merge({}, this.ID), i;
1582
1583             if (!attr.ids && _pickercount > 1) {
1584                 for (i in ids) {
1585                     if (lang.hasOwnProperty(ids, i)) {
1586                         ids[i] = ids[i] + _pickercount;
1587                     }
1588                 }
1589             }
1590
1591
1592             /**
1593              * A list of element ids and/or element references used by the 
1594              * control.  The default is the this.ID list, and can be customized
1595              * by passing a list in the contructor
1596              * @attribute ids
1597              * @type {referenceid: realid}
1598              * @writeonce
1599              */
1600             this.setAttributeConfig(this.OPT.IDS, {
1601                     value: ids,
1602                     writeonce: true
1603                 });
1604
1605             /**
1606              * A list of txt strings for internationalization.  Default
1607              * is this.TXT
1608              * @attribute txt
1609              * @type {key: txt}
1610              * @writeonce
1611              */
1612             this.setAttributeConfig(this.OPT.TXT, {
1613                     value: attr.txt || this.TXT,
1614                     writeonce: true
1615                 });
1616
1617             /**
1618              * The img src default list
1619              * is this.IMAGES
1620              * @attribute images
1621              * @type {key: image}
1622              * @writeonce
1623              */
1624             this.setAttributeConfig(this.OPT.IMAGES, {
1625                     value: attr.images || this.IMAGE,
1626                     writeonce: true
1627                 });
1628             /**
1629              * The element refs used by this control.  Set at initialization
1630              * @attribute elements
1631              * @type {id: HTMLElement}
1632              * @readonly
1633              */
1634             this.setAttributeConfig(this.OPT.ELEMENTS, {
1635                     value: {},
1636                     readonly: true
1637                 });
1638
1639             /**
1640              * Hide/show the entire set of controls
1641              * @attribute showcontrols
1642              * @type boolean
1643              * @default true
1644              */
1645             this.setAttributeConfig(this.OPT.SHOW_CONTROLS, {
1646                     value: lang.isBoolean(attr.showcontrols) ? attr.showcontrols : true,
1647                     method: function(on) {
1648
1649                         var el = Dom.getElementsByClassName("bd", "div", 
1650                                 this.getElement(this.ID.CONTROLS))[0];
1651
1652                         this._hideShowEl(el, on);
1653
1654                         this.getElement(this.ID.CONTROLS_LABEL).innerHTML = 
1655                             (on) ? this.get(this.OPT.TXT).HIDE_CONTROLS :
1656                                    this.get(this.OPT.TXT).SHOW_CONTROLS;
1657
1658                     }
1659                 });
1660
1661             /**
1662              * Hide/show the rgb controls
1663              * @attribute showrgbcontrols
1664              * @type boolean
1665              * @default true
1666              */
1667             this.setAttributeConfig(this.OPT.SHOW_RGB_CONTROLS, {
1668                     value: lang.isBoolean(attr.showrgbcontrols) ? attr.showrgbcontrols : true,
1669                     method: function(on) {
1670                         this._hideShowEl(this.ID.RGB_CONTROLS, on);
1671                     }
1672                 });
1673
1674             /**
1675              * Hide/show the hsv controls
1676              * @attribute showhsvcontrols
1677              * @type boolean
1678              * @default false
1679              */
1680             this.setAttributeConfig(this.OPT.SHOW_HSV_CONTROLS, {
1681                     value: lang.isBoolean(attr.showhsvcontrols) ?
1682                                           attr.showhsvcontrols : false,
1683                     method: function(on) {
1684                         //Dom.setStyle(this.getElement(this.ID.HSV_CONTROLS), "visibility", (on) ? "" : "hidden");
1685                         this._hideShowEl(this.ID.HSV_CONTROLS, on);
1686
1687                         // can't show both the hsv controls and the rbg hex summary
1688                         if (on && this.get(this.OPT.SHOW_HEX_SUMMARY)) {
1689                             this.set(this.OPT.SHOW_HEX_SUMMARY, false);
1690                         }
1691                     }
1692                 });
1693
1694             /**
1695              * Hide/show the hex controls
1696              * @attribute showhexcontrols
1697              * @type boolean
1698              * @default true
1699              */
1700             this.setAttributeConfig(this.OPT.SHOW_HEX_CONTROLS, {
1701                     value: lang.isBoolean(attr.showhexcontrols) ?
1702                                           attr.showhexcontrols : false,
1703                     method: function(on) {
1704                         this._hideShowEl(this.ID.HEX_CONTROLS, on);
1705                     }
1706                 });
1707
1708             /**
1709              * Hide/show the websafe swatch
1710              * @attribute showwebsafe
1711              * @type boolean
1712              * @default true
1713              */
1714             this.setAttributeConfig(this.OPT.SHOW_WEBSAFE, {
1715                     value: lang.isBoolean(attr.showwebsafe) ? attr.showwebsafe : true,
1716                     method: function(on) {
1717                         this._hideShowEl(this.ID.WEBSAFE_SWATCH, on);
1718                     }
1719                 });
1720
1721             /**
1722              * Hide/show the hex summary
1723              * @attribute showhexsummary
1724              * @type boolean
1725              * @default true
1726              */
1727             this.setAttributeConfig(this.OPT.SHOW_HEX_SUMMARY, {
1728                     value: lang.isBoolean(attr.showhexsummary) ? attr.showhexsummary : true,
1729                     method: function(on) {
1730                         this._hideShowEl(this.ID.HEX_SUMMARY, on);
1731
1732                         // can't show both the hsv controls and the rbg hex summary
1733                         if (on && this.get(this.OPT.SHOW_HSV_CONTROLS)) {
1734                             this.set(this.OPT.SHOW_HSV_CONTROLS, false);
1735                         }
1736                     }
1737                 });
1738             this.setAttributeConfig(this.OPT.ANIMATE, {
1739                     value: lang.isBoolean(attr.animate) ? attr.animate : true,
1740                     method: function(on) {
1741                         if (this.pickerSlider) {
1742                             this.pickerSlider.animate = on;
1743                             this.hueSlider.animate = on;
1744                         }
1745                     }
1746                 });
1747
1748             this.on(this.OPT.HUE + "Change", this._updateRGBFromHSV, this, true);
1749             this.on(this.OPT.SATURATION + "Change", this._updateRGBFromHSV, this, true);
1750             this.on(this.OPT.VALUE + "Change", this._updateRGBFromHSV, this, true);
1751
1752             this.on(this.OPT.RED + "Change", this._updateRGB, this, true);
1753             this.on(this.OPT.GREEN + "Change", this._updateRGB, this, true);
1754             this.on(this.OPT.BLUE + "Change", this._updateRGB, this, true);
1755
1756             this.on(this.OPT.HEX + "Change", this._updateHex, this, true);
1757
1758             this._initElements();
1759         }
1760     });
1761
1762     YAHOO.widget.ColorPicker = ColorPicker;
1763 })();
1764 YAHOO.register("colorpicker", YAHOO.widget.ColorPicker, {version: "2.9.0", build: "2800"});