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