2
Copyright (c) 2008, Yahoo! Inc. All rights reserved.
3
Code licensed under the BSD License:
4
http://developer.yahoo.net/yui/license.txt
8
* Provides color conversion and validation utils
9
* @class YAHOO.util.Color
10
* @namespace YAHOO.util
12
YAHOO.util.Color = function() {
14
var HCHARS="0123456789ABCDEF", lang=YAHOO.lang;
19
* Converts 0-1 to 0-255
21
* @param n {float} the number to convert
22
* @return {int} a number 0-255
24
real2dec: function(n) {
25
return Math.min(255, Math.round(n*256));
29
* Converts HSV (h[0-360], s[0-1]), v[0-1] to RGB [255,255,255]
31
* @param h {int|[int, float, float]} the hue, or an
32
* array containing all three parameters
33
* @param s {float} the saturation
34
* @param v {float} the value/brightness
35
* @return {[int, int, int]} the red, green, blue values in
38
hsv2rgb: function(h, s, v) {
40
if (lang.isArray(h)) {
41
return this.hsv2rgb.call(this, h[0], h[1], h[2]);
44
var r, g, b, i, f, p, q, t;
45
i = Math.floor((h/60)%6);
51
case 0: r=v; g=t; b=p; break;
52
case 1: r=q; g=v; b=p; break;
53
case 2: r=p; g=v; b=t; break;
54
case 3: r=p; g=q; b=v; break;
55
case 4: r=t; g=p; b=v; break;
56
case 5: r=v; g=p; b=q; break;
61
return [fn(r), fn(g), fn(b)];
65
* Converts to RGB [255,255,255] to HSV (h[0-360], s[0-1]), v[0-1]
67
* @param r {int|[int, int, int]} the red value, or an
68
* array containing all three parameters
69
* @param g {int} the green value
70
* @param b {int} the blue value
71
* @return {[int, float, float]} the value converted to hsv
73
rgb2hsv: function(r, g, b) {
75
if (lang.isArray(r)) {
76
return this.rgb2hsv.call(this, r[0], r[1], r[2]);
83
var min,max,delta,h,s,v;
84
min = Math.min(Math.min(r,g),b);
85
max = Math.max(Math.max(r,g),b);
90
case r: h=60*(g-b)/delta;
95
case g: h=(60*(b-r)/delta)+120; break;
96
case b: h=(60*(r-g)/delta)+240; break;
99
s = (max === 0) ? 0 : 1-(min/max);
101
var hsv = [Math.round(h), s, max];
108
* Converts decimal rgb values into a hex string
109
* 255,255,255 -> FFFFFF
111
* @param r {int|[int, int, int]} the red value, or an
112
* array containing all three parameters
113
* @param g {int} the green value
114
* @param b {int} the blue value
115
* @return {string} the hex string
117
rgb2hex: function(r, g, b) {
118
if (lang.isArray(r)) {
119
return this.rgb2hex.call(this, r[0], r[1], r[2]);
123
return f(r) + f(g) + f(b);
127
* Converts an int 0...255 to hex pair 00...FF
129
* @param n {int} the number to convert
130
* @return {string} the hex equivalent
132
dec2hex: function(n) {
134
n = (lang.isNumber(n)) ? n : 0;
135
n = (n > 255 || n < 0) ? 0 : n;
137
return HCHARS.charAt((n - n % 16) / 16) + HCHARS.charAt(n % 16);
141
* Converts a hex pair 00...FF to an int 0...255
143
* @param str {string} the hex pair to convert
144
* @return {int} the decimal
146
hex2dec: function(str) {
147
var f = function(c) {
148
return HCHARS.indexOf(c.toUpperCase());
153
return ((f(s[0]) * 16) + f(s[1]));
157
* Converts a hex string to rgb
159
* @param str {string} the hex string
160
* @return {[int, int, int]} an array containing the rgb values
162
hex2rgb: function(s) {
163
var f = this.hex2dec;
164
return [f(s.substr(0, 2)), f(s.substr(2, 2)), f(s.substr(4, 2))];
168
* Returns the closest websafe color to the supplied rgb value.
170
* @param r {int|[int, int, int]} the red value, or an
171
* array containing all three parameters
172
* @param g {int} the green value
173
* @param b {int} the blue value
174
* @return {[int, int, int]} an array containing the closes
175
* websafe rgb colors.
177
websafe: function(r, g, b) {
179
if (lang.isArray(r)) {
180
return this.websafe.call(this, r[0], r[1], r[2]);
183
// returns the closest match [0, 51, 102, 153, 204, 255]
184
var f = function(v) {
185
if (lang.isNumber(v)) {
186
v = Math.min(Math.max(0, v), 255);
188
for (i=0; i<256; i=i+51) {
190
if (v >= i && v <= next) {
191
return (v-i > 25) ? next : i;
194
YAHOO.log("Error calculating the websafe value for " + v, "warn");
200
return [f(r), f(g), f(b)];
208
var _pickercount = 0;
211
* The colorpicker module provides a widget for selecting colors
212
* @module colorpicker
213
* @requires yahoo, dom, event, element, slider
218
* Creates the host element if it doesn't exist
219
* @method _createHostElement
222
var _createHostElement = function() {
223
var el = document.createElement('div');
226
el.className = this.CSS.BASE;
233
* A widget to select colors
234
* @namespace YAHOO.widget
235
* @class YAHOO.widget.ColorPicker
236
* @extends YAHOO.util.Element
238
* @param {HTMLElement | String | Object} el(optional) The html
239
* element that represents the colorpicker, or the attribute object to use.
240
* An element will be created if none provided.
241
* @param {Object} attr (optional) A key map of the colorpicker's
242
* initial attributes. Ignored if first arg is attributes object.
244
YAHOO.widget.ColorPicker = function(el, attr) {
245
_pickercount = _pickercount + 1;
246
this.logger = new YAHOO.widget.LogWriter("ColorPicker");
248
if (arguments.length === 1 && !YAHOO.lang.isString(el) && !el.nodeName) {
249
attr = el; // treat first arg as attr object
250
el = attr.element || null;
253
if (!el && !attr.element) { // create if we dont have one
254
this.logger.log("creating host element");
255
el = _createHostElement.call(this, attr);
258
YAHOO.widget.ColorPicker.superclass.constructor.call(this, el, attr);
261
YAHOO.extend(YAHOO.widget.ColorPicker, YAHOO.util.Element);
263
var proto = YAHOO.widget.ColorPicker.prototype,
264
Slider=YAHOO.widget.Slider,
265
Color=YAHOO.util.Color,
266
Dom = YAHOO.util.Dom,
267
Event = YAHOO.util.Event,
269
sub = lang.substitute;
272
var b = "yui-picker";
275
* The element ids used by this control
282
* The id for the "red" form field
286
* @default yui-picker-r
291
* The id for the "red" hex pair output
295
* @default yui-picker-rhex
300
* The id for the "green" form field
304
* @default yui-picker-g
309
* The id for the "green" hex pair output
313
* @default yui-picker-ghex
319
* The id for the "blue" form field
323
* @default yui-picker-b
328
* The id for the "blue" hex pair output
332
* @default yui-picker-bhex
337
* The id for the "hue" form field
341
* @default yui-picker-h
346
* The id for the "saturation" form field
350
* @default yui-picker-s
355
* The id for the "value" form field
359
* @default yui-picker-v
364
* The id for the picker region slider
365
* @property ID.PICKER_BG
368
* @default yui-picker-bg
370
PICKER_BG: b + "-bg",
373
* The id for the picker region thumb
374
* @property ID.PICKER_THUMB
377
* @default yui-picker-thumb
379
PICKER_THUMB: b + "-thumb",
382
* The id for the hue slider
383
* @property ID.HUE_BG
386
* @default yui-picker-hue-bg
388
HUE_BG: b + "-hue-bg",
391
* The id for the hue thumb
392
* @property ID.HUE_THUMB
395
* @default yui-picker-hue-thumb
397
HUE_THUMB: b + "-hue-thumb",
400
* The id for the hex value form field
404
* @default yui-picker-hex
409
* The id for the color swatch
410
* @property ID.SWATCH
413
* @default yui-picker-swatch
415
SWATCH: b + "-swatch",
418
* The id for the websafe color swatch
419
* @property ID.WEBSAFE_SWATCH
422
* @default yui-picker-websafe-swatch
424
WEBSAFE_SWATCH: b + "-websafe-swatch",
427
* The id for the control details
428
* @property ID.CONTROLS
430
* @default yui-picker-controls
432
CONTROLS: b + "-controls",
435
* The id for the rgb controls
436
* @property ID.RGB_CONTROLS
438
* @default yui-picker-rgb-controls
440
RGB_CONTROLS: b + "-rgb-controls",
443
* The id for the hsv controls
444
* @property ID.HSV_CONTROLS
446
* @default yui-picker-hsv-controls
448
HSV_CONTROLS: b + "-hsv-controls",
451
* The id for the hsv controls
452
* @property ID.HEX_CONTROLS
454
* @default yui-picker-hex-controls
456
HEX_CONTROLS: b + "-hex-controls",
459
* The id for the hex summary
460
* @property ID.HEX_SUMMARY
462
* @default yui-picker-hex-summary
464
HEX_SUMMARY: b + "-hex-summary",
467
* The id for the controls section header
468
* @property ID.CONTROLS_LABEL
470
* @default yui-picker-controls-label
472
CONTROLS_LABEL: b + "-controls-label"
476
* Constants for any script-generated messages. The values here
477
* are the default messages. They can be updated by providing
478
* the complete list to the constructor for the "txt" attribute.
483
ILLEGAL_HEX: "Illegal hex value entered",
484
SHOW_CONTROLS: "Show color details",
485
HIDE_CONTROLS: "Hide color details",
486
CURRENT_COLOR: "Currently selected color: {rgb}",
487
CLOSEST_WEBSAFE: "Closest websafe color: {rgb}. Click to select.",
500
* Constants for the default image locations for img tags that are
501
* generated by the control. They can be modified by passing the
502
* complete list to the contructor for the "images" attribute
507
PICKER_THUMB: "../../build/colorpicker/assets/picker_thumb.png",
508
HUE_THUMB: "../../build/colorpicker/assets/hue_thumb.png"
512
* Constants for the control's custom event names. subscribe
513
* to the rgbChange event instead.
524
* Constants for the control's default default values
533
* Constants for the control's configuration attributes
539
SATURATION: "saturation",
548
PICKER_SIZE: "pickersize",
549
SHOW_CONTROLS: "showcontrols",
550
SHOW_RGB_CONTROLS: "showrgbcontrols",
551
SHOW_HSV_CONTROLS: "showhsvcontrols",
552
SHOW_HEX_CONTROLS: "showhexcontrols",
553
SHOW_HEX_SUMMARY: "showhexsummary",
554
SHOW_WEBSAFE: "showwebsafe",
555
//SHOW_SUBMIT: "showsubmit",
556
CONTAINER: "container",
558
ELEMENTS: "elements",
565
* Moves the hue slider into the position dictated by the current state
567
* @method _updateHueSlider
570
var _updateHueSlider = function() {
571
var size = this.get(this.OPT.PICKER_SIZE),
572
h = this.get(this.OPT.HUE);
574
h = size - Math.round(h / 360 * size);
576
// 0 is at the top and bottom of the hue slider. Always go to
577
// the top so we don't end up sending the thumb to the bottom
578
// when the value didn't actually change (e.g., a conversion
579
// produced 360 instead of 0 and the value was already 0).
583
this.logger.log("Hue slider is being set to " + h);
585
this.hueSlider.setValue(h);
589
* Moves the picker slider into the position dictated by the current state
591
* @method _updatePickerSlider
594
var _updatePickerSlider = function() {
595
var size = this.get(this.OPT.PICKER_SIZE),
596
s = this.get(this.OPT.SATURATION),
597
v = this.get(this.OPT.VALUE);
599
s = Math.round(s * size / 100);
600
v = Math.round(size - (v * size / 100));
602
this.logger.log("Setting picker slider to " + [s, v]);
604
this.pickerSlider.setRegionValue(s, v);
608
* Moves the sliders into the position dictated by the current state
610
* @method _updateSliders
613
var _updateSliders = function() {
614
_updateHueSlider.call(this);
615
_updatePickerSlider.call(this);
619
* Sets the control to the specified rgb value and
620
* moves the sliders to the proper positions
622
* @param rgb {[int, int, int]} the rgb value
623
* @param silent {boolean} whether or not to fire the change event
625
proto.setValue = function(rgb, silent) {
626
silent = (silent) || false;
627
this.set(this.OPT.RGB, rgb, silent);
628
_updateSliders.call(this);
633
* @property hueSlider
634
* @type YAHOO.widget.Slider
636
proto.hueSlider = null;
640
* @property pickerSlider
641
* @type YAHOO.widget.Slider
643
proto.pickerSlider = null;
646
* Translates the slider value into hue, int[0,359]
649
* @return {int} the hue from 0 to 359
651
var _getH = function() {
652
var size = this.get(this.OPT.PICKER_SIZE),
653
h = (size - this.hueSlider.getValue()) / size;
654
h = Math.round(h*360);
655
return (h === 360) ? 0 : h;
659
* Translates the slider value into saturation, int[0,1], left to right
662
* @return {int} the saturation from 0 to 1
664
var _getS = function() {
665
return this.pickerSlider.getXValue() / this.get(this.OPT.PICKER_SIZE);
669
* Translates the slider value into value/brightness, int[0,1], top
673
* @return {int} the value from 0 to 1
675
var _getV = function() {
676
var size = this.get(this.OPT.PICKER_SIZE);
677
return (size - this.pickerSlider.getYValue()) / size;
681
* Updates the background of the swatch with the current rbg value.
682
* Also updates the websafe swatch to the closest websafe color
683
* @method _updateSwatch
686
var _updateSwatch = function() {
687
var rgb = this.get(this.OPT.RGB),
688
websafe = this.get(this.OPT.WEBSAFE),
689
el = this.getElement(this.ID.SWATCH),
690
color = rgb.join(","),
691
txt = this.get(this.OPT.TXT);
693
Dom.setStyle(el, "background-color", "rgb(" + color + ")");
694
el.title = lang.substitute(txt.CURRENT_COLOR, {
695
"rgb": "#" + this.get(this.OPT.HEX)
699
el = this.getElement(this.ID.WEBSAFE_SWATCH);
700
color = websafe.join(",");
702
Dom.setStyle(el, "background-color", "rgb(" + color + ")");
703
el.title = lang.substitute(txt.CLOSEST_WEBSAFE, {
704
"rgb": "#" + Color.rgb2hex(websafe)
710
* Reads the sliders and converts the values to RGB, updating the
711
* internal state for all the individual form fields
712
* @method _getValuesFromSliders
715
var _getValuesFromSliders = function() {
716
var h=_getH.call(this), s=_getS.call(this), v=_getV.call(this);
717
YAHOO.log("hsv " + [h, s, v]);
719
var rgb = Color.hsv2rgb(h, s, v);
720
//var websafe = Color.websafe(rgb);
721
//var hex = Color.rgb2hex(rgb[0], rgb[1], rgb[2]);
723
this.set(this.OPT.RGB, rgb);
727
* Updates the form field controls with the state data contained
729
* @method _updateFormFields
732
var _updateFormFields = function() {
733
this.getElement(this.ID.H).value = this.get(this.OPT.HUE);
734
this.getElement(this.ID.S).value = this.get(this.OPT.SATURATION);
735
this.getElement(this.ID.V).value = this.get(this.OPT.VALUE);
736
this.getElement(this.ID.R).value = this.get(this.OPT.RED);
737
this.getElement(this.ID.R_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.RED));
738
this.getElement(this.ID.G).value = this.get(this.OPT.GREEN);
739
this.getElement(this.ID.G_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.GREEN));
740
this.getElement(this.ID.B).value = this.get(this.OPT.BLUE);
741
this.getElement(this.ID.B_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.BLUE));
742
this.getElement(this.ID.HEX).value = this.get(this.OPT.HEX);
746
* Event handler for the hue slider.
747
* @method _onHueSliderChange
748
* @param newOffset {int} pixels from the start position
751
var _onHueSliderChange = function(newOffset) {
752
this.logger.log("hue update: " + newOffset , "warn");
754
var h = _getH.call(this);
755
this.set(this.OPT.HUE, h, true);
757
// set picker background to the hue
758
var rgb = Color.hsv2rgb(h, 1, 1);
759
var styleDef = "rgb(" + rgb.join(",") + ")";
761
Dom.setStyle(this.getElement(this.ID.PICKER_BG), "background-color", styleDef);
763
if (this.hueSlider.valueChangeSource === this.hueSlider.SOURCE_UI_EVENT) {
764
_getValuesFromSliders.call(this);
767
_updateFormFields.call(this);
768
_updateSwatch.call(this);
772
* Event handler for the picker slider, which controls the
773
* saturation and value/brightness.
774
* @method _onPickerSliderChange
775
* @param newOffset {{x: int, y: int}} x/y pixels from the start position
778
var _onPickerSliderChange = function(newOffset) {
779
this.logger.log(sub("picker update [{x}, {y}]", newOffset));
781
var s=_getS.call(this), v=_getV.call(this);
782
this.set(this.OPT.SATURATION, Math.round(s*100), true);
783
this.set(this.OPT.VALUE, Math.round(v*100), true);
785
if (this.pickerSlider.valueChangeSource === this.pickerSlider.SOURCE_UI_EVENT) {
786
_getValuesFromSliders.call(this);
789
_updateFormFields.call(this);
790
_updateSwatch.call(this);
795
* Key map to well-known commands for txt field input
796
* @method _getCommand
797
* @param e {Event} the keypress or keydown event
798
* @return {int} a command code
800
* <li>0 = not a number, letter in range, or special key</li>
801
* <li>1 = number</li>
802
* <li>2 = a-fA-F</li>
803
* <li>3 = increment (up arrow)</li>
804
* <li>4 = decrement (down arrow)</li>
805
* <li>5 = special key (tab, delete, return, escape, left, right)</li>
806
* <li>6 = return</li>
810
var _getCommand = function(e) {
811
var c = Event.getCharCode(e);
813
//alert(Event.getCharCode(e) + ", " + e.keyCode + ", " + e.charCode);
816
if (c === 38) { // up arrow
818
} else if (c === 13) { // return
820
} else if (c === 40) { // down array
822
} else if (c >= 48 && c<=57) { // 0-9
824
} else if (c >= 97 && c<=102) { // a-f
826
} else if (c >= 65 && c<=70) { // A-F
828
//} else if ("8, 9, 13, 27, 37, 39".indexOf(c) > -1 ||
829
// (c >= 112 && c <=123)) { // including F-keys
830
// tab, delete, return, escape, left, right
831
} else if ("8, 9, 13, 27, 37, 39".indexOf(c) > -1) { // special chars
833
} else { // something we probably don't want
839
* Use the value of the text field to update the control
840
* @method _hexFieldKeypress
841
* @param e {Event} an event
842
* @param el {HTMLElement} the field
843
* @param prop {string} the key to the linked property
846
var _useFieldValue = function(e, el, prop) {
849
if (prop !== this.OPT.HEX) {
850
val = parseInt(val, 10);
853
if (val !== this.get(prop)) {
859
* Handle keypress on one of the rgb or hsv fields.
860
* @method _rgbFieldKeypress
861
* @param e {Event} the keypress event
862
* @param el {HTMLElement} the field
863
* @param prop {string} the key to the linked property
866
var _rgbFieldKeypress = function(e, el, prop) {
867
var command = _getCommand(e);
868
var inc = (e.shiftKey) ? 10 : 1;
870
case 6: // return, update the value
871
_useFieldValue.apply(this, arguments);
874
case 3: // up arrow, increment
875
this.set(prop, Math.min(this.get(prop)+inc, 255));
876
_updateFormFields.call(this);
877
//Event.stopEvent(e);
879
case 4: // down arrow, decrement
880
this.set(prop, Math.max(this.get(prop)-inc, 0));
881
_updateFormFields.call(this);
882
//Event.stopEvent(e);
891
* Handle keydown on the hex field
892
* @method _hexFieldKeypress
893
* @param e {Event} the keypress event
894
* @param el {HTMLElement} the field
895
* @param prop {string} the key to the linked property
898
var _hexFieldKeypress = function(e, el, prop) {
899
var command = _getCommand(e);
900
if (command === 6) { // return, update the value
901
_useFieldValue.apply(this, arguments);
906
* Allows numbers and special chars, and by default allows a-f.
907
* Used for the hex field keypress handler.
909
* @param e {Event} the event
910
* @param numbersOnly omits a-f if set to true
912
* @return {boolean} false if we are canceling the event
914
var _hexOnly = function(e, numbersOnly) {
915
var command = _getCommand(e);
918
case 5: // special char
921
case 2: // hex char (a-f)
922
if (numbersOnly !== true) {
926
// fallthrough is intentional
928
default: // prevent alpha and punctuation
935
* Allows numbers and special chars only. Used for the
936
* rgb and hsv fields keypress handler.
937
* @method _numbersOnly
938
* @param e {Event} the event
940
* @return {boolean} false if we are canceling the event
942
var _numbersOnly = function(e) {
943
return _hexOnly(e, true);
947
* Returns the element reference that is saved. The id can be either
948
* the element id, or the key for this id in the "id" config attribute.
949
* For instance, the host element id can be obtained by passing its
950
* id (default: "yui_picker") or by its key "YUI_PICKER".
951
* @param id {string} the element id, or key
952
* @return {HTMLElement} a reference to the element
954
proto.getElement = function(id) {
955
return this.get(this.OPT.ELEMENTS)[this.get(this.OPT.IDS)[id]];
958
var _createElements = function() {
959
this.logger.log("Building markup");
960
var el, child, img, fld, i,
961
ids = this.get(this.OPT.IDS),
962
txt = this.get(this.OPT.TXT),
963
images = this.get(this.OPT.IMAGES),
964
Elem = function(type, o) {
965
var n = document.createElement(type);
967
lang.augmentObject(n, o, true);
971
RGBElem = function(type, obj) {
981
return new Elem(type, o);
984
var p = this.get("element");
986
// Picker slider (S and V) ---------------------------------------------
988
el = new Elem("div", {
989
id: ids[this.ID.PICKER_BG],
990
className: "yui-picker-bg",
995
child = new Elem("div", {
996
id: ids[this.ID.PICKER_THUMB],
997
className: "yui-picker-thumb"
1000
img = new Elem("img", {
1001
src: images.PICKER_THUMB
1004
child.appendChild(img);
1005
el.appendChild(child);
1008
// Hue slider ---------------------------------------------
1009
el = new Elem("div", {
1010
id: ids[this.ID.HUE_BG],
1011
className: "yui-picker-hue-bg",
1016
child = new Elem("div", {
1017
id: ids[this.ID.HUE_THUMB],
1018
className: "yui-picker-hue-thumb"
1021
img = new Elem("img", {
1022
src: images.HUE_THUMB
1025
child.appendChild(img);
1026
el.appendChild(child);
1030
// controls ---------------------------------------------
1032
el = new Elem("div", {
1033
id: ids[this.ID.CONTROLS],
1034
className: "yui-picker-controls"
1041
el = new Elem("div", {
1045
child = new Elem("a", {
1046
id: ids[this.ID.CONTROLS_LABEL],
1047
//className: "yui-picker-controls-label",
1050
el.appendChild(child);
1054
el = new Elem("div", {
1062
el = new Elem("ul", {
1063
id: ids[this.ID.RGB_CONTROLS],
1064
className: "yui-picker-rgb-controls"
1067
child = new Elem("li");
1068
child.appendChild(document.createTextNode(txt.R + " "));
1070
fld = new RGBElem("input", {
1072
className: "yui-picker-r"
1075
child.appendChild(fld);
1076
el.appendChild(child);
1078
child = new Elem("li");
1079
child.appendChild(document.createTextNode(txt.G + " "));
1081
fld = new RGBElem("input", {
1083
className: "yui-picker-g"
1086
child.appendChild(fld);
1087
el.appendChild(child);
1089
child = new Elem("li");
1090
child.appendChild(document.createTextNode(txt.B + " "));
1092
fld = new RGBElem("input", {
1094
className: "yui-picker-b"
1097
child.appendChild(fld);
1098
el.appendChild(child);
1103
el = new Elem("ul", {
1104
id: ids[this.ID.HSV_CONTROLS],
1105
className: "yui-picker-hsv-controls"
1108
child = new Elem("li");
1109
child.appendChild(document.createTextNode(txt.H + " "));
1111
fld = new RGBElem("input", {
1113
className: "yui-picker-h"
1116
child.appendChild(fld);
1117
child.appendChild(document.createTextNode(" " + txt.DEG));
1119
el.appendChild(child);
1121
child = new Elem("li");
1122
child.appendChild(document.createTextNode(txt.S + " "));
1124
fld = new RGBElem("input", {
1126
className: "yui-picker-s"
1129
child.appendChild(fld);
1130
child.appendChild(document.createTextNode(" " + txt.PERCENT));
1132
el.appendChild(child);
1134
child = new Elem("li");
1135
child.appendChild(document.createTextNode(txt.V + " "));
1137
fld = new RGBElem("input", {
1139
className: "yui-picker-v"
1142
child.appendChild(fld);
1143
child.appendChild(document.createTextNode(" " + txt.PERCENT));
1145
el.appendChild(child);
1151
el = new Elem("ul", {
1152
id: ids[this.ID.HEX_SUMMARY],
1153
className: "yui-picker-hex_summary"
1156
child = new Elem("li", {
1157
id: ids[this.ID.R_HEX]
1159
el.appendChild(child);
1161
child = new Elem("li", {
1162
id: ids[this.ID.G_HEX]
1164
el.appendChild(child);
1166
child = new Elem("li", {
1167
id: ids[this.ID.B_HEX]
1169
el.appendChild(child);
1173
el = new Elem("div", {
1174
id: ids[this.ID.HEX_CONTROLS],
1175
className: "yui-picker-hex-controls"
1177
el.appendChild(document.createTextNode(txt.HEX + " "));
1179
child = new RGBElem("input", {
1180
id: ids[this.ID.HEX],
1181
className: "yui-picker-hex",
1186
el.appendChild(child);
1189
p = this.get("element");
1192
el = new Elem("div", {
1193
id: ids[this.ID.SWATCH],
1194
className: "yui-picker-swatch"
1200
el = new Elem("div", {
1201
id: ids[this.ID.WEBSAFE_SWATCH],
1202
className: "yui-picker-websafe-swatch"
1209
var _attachRGBHSV = function(id, config) {
1210
Event.on(this.getElement(id), "keydown", function(e, me) {
1211
_rgbFieldKeypress.call(me, e, this, config);
1213
Event.on(this.getElement(id), "keypress", _numbersOnly, this);
1214
Event.on(this.getElement(id), "blur", function(e, me) {
1215
_useFieldValue.call(me, e, this, config);
1221
* Updates the rgb attribute with the current state of the r,g,b
1222
* fields. This is invoked from change listeners on these
1223
* attributes to facilitate updating these values from the
1224
* individual form fields
1225
* @method _updateRGB
1228
var _updateRGB = function() {
1229
var rgb = [this.get(this.OPT.RED),
1230
this.get(this.OPT.GREEN),
1231
this.get(this.OPT.BLUE)];
1233
this.logger.log("RGB value set to " + rgb);
1234
this.set(this.OPT.RGB, rgb);
1236
_updateSliders.call(this);
1240
* Sets the initial state of the sliders
1241
* @method initPicker
1243
proto.initPicker = function () {
1245
// bind all of our elements
1247
ids = this.get(o.IDS),
1248
els = this.get(o.ELEMENTS),
1251
// Add the default value as a key for each element for easier lookup
1252
for (i in this.ID) {
1253
if (lang.hasOwnProperty(this.ID, i)) {
1254
ids[this.ID[i]] = ids[i];
1258
// Check for picker element, if not there, create all of them
1259
el = Dom.get(ids[this.ID.PICKER_BG]);
1261
_createElements.call(this);
1263
this.logger.log("Using pre-existing markup");
1267
if (lang.hasOwnProperty(ids, i)) {
1269
el = Dom.get(ids[i]);
1271
// generate an id if the implementer passed in an element reference,
1272
// and the element did not have an id already
1273
id = Dom.generateId(el);
1275
// update the id in case we generated the id
1276
ids[i] = id; // key is WEBSAFE_SWATCH
1277
ids[ids[i]] = id; // key is websafe_swatch
1279
// store the dom ref
1284
// set the initial visibility state of our controls
1285
els = [o.SHOW_CONTROLS,
1286
o.SHOW_RGB_CONTROLS,
1287
o.SHOW_HSV_CONTROLS,
1288
o.SHOW_HEX_CONTROLS,
1293
for (i=0; i<els.length; i=i+1) {
1294
this.set(els[i], this.get(els[i]));
1297
var s = this.get(o.PICKER_SIZE);
1298
this.logger.log("picker size" + s);
1300
this.hueSlider = Slider.getVertSlider(this.getElement(this.ID.HUE_BG),
1301
this.getElement(this.ID.HUE_THUMB), 0, s);
1302
this.hueSlider.subscribe("change", _onHueSliderChange, this, true);
1304
this.pickerSlider = Slider.getSliderRegion(this.getElement(this.ID.PICKER_BG),
1305
this.getElement(this.ID.PICKER_THUMB), 0, s, 0, s);
1306
this.pickerSlider.subscribe("change", _onPickerSliderChange, this, true);
1308
// Set the animate state
1309
this.set(o.ANIMATE,this.get(o.ANIMATE));
1311
//_onHueSliderChange.call(this, 0);
1313
Event.on(this.getElement(this.ID.WEBSAFE_SWATCH), "click", function(e) {
1314
this.setValue(this.get(o.WEBSAFE));
1318
Event.on(this.getElement(this.ID.CONTROLS_LABEL), "click", function(e) {
1319
this.set(o.SHOW_CONTROLS, !this.get(o.SHOW_CONTROLS));
1320
Event.preventDefault(e);
1323
_attachRGBHSV.call(this, this.ID.R, this.OPT.RED);
1324
_attachRGBHSV.call(this, this.ID.G, this.OPT.GREEN);
1325
_attachRGBHSV.call(this, this.ID.B, this.OPT.BLUE);
1326
_attachRGBHSV.call(this, this.ID.H, this.OPT.HUE);
1327
_attachRGBHSV.call(this, this.ID.S, this.OPT.SATURATION);
1328
_attachRGBHSV.call(this, this.ID.V, this.OPT.VALUE);
1330
Event.on(this.getElement(this.ID.HEX), "keydown", function(e, me) {
1331
_hexFieldKeypress.call(me, e, this, me.OPT.HEX);
1334
Event.on(this.getElement(this.ID.HEX), "keypress", _hexOnly, this);
1335
Event.on(this.getElement(this.ID.HEX), "blur", function(e, me) {
1336
_useFieldValue.call(me, e, this, me.OPT.HEX);
1339
_updateRGB.call(this);
1344
* Updates the RGB values from the current state of the HSV
1345
* values. Executed when the one of the HSV form fields are
1350
var _updateRGBFromHSV = function() {
1351
var hsv = [this.get(this.OPT.HUE),
1352
this.get(this.OPT.SATURATION)/100,
1353
this.get(this.OPT.VALUE)/100];
1355
var rgb = Color.hsv2rgb(hsv);
1357
this.logger.log("HSV converted to RGB " + hsv + " : " + rgb);
1358
this.set(this.OPT.RGB, rgb);
1360
_updateSliders.call(this);
1364
* Parses the hex string to normalize shorthand values, converts
1365
* the hex value to rgb and updates the rgb attribute (which
1366
* updates the state for all of the other values)
1370
var _updateHex = function() {
1372
var hex = this.get(this.OPT.HEX), l=hex.length;
1374
// support #369 -> #336699 shorthand
1376
var c = hex.split(""), i;
1377
for (i=0; i<l; i=i+1) {
1384
if (hex.length !== 6) {
1385
this.logger.log(this.get(this.TXT.ILLEGAL_HEX), "error");
1389
var rgb = Color.hex2rgb(hex);
1391
this.logger.log(sub("Hex value set to {hex} ({rgb})", {
1397
//_updateSliders.call(this);
1404
* Sets up the config attributes and the change listeners for this
1406
* @method initAttributes
1407
* @param attr An object containing default attribute values
1409
proto.initAttributes = function(attr) {
1412
YAHOO.widget.ColorPicker.superclass.initAttributes.call(this, attr);
1415
* The size of the picker. Trying to change this is not recommended.
1416
* @attribute pickersize
1420
this.setAttributeConfig(this.OPT.PICKER_SIZE, {
1421
value: attr.size || this.DEFAULT.PICKER_SIZE
1425
* The current hue value 0-360
1429
this.setAttributeConfig(this.OPT.HUE, {
1430
value: attr.hue || 0,
1431
validator: lang.isNumber
1435
* The current saturation value 0-100
1436
* @attribute saturation
1439
this.setAttributeConfig(this.OPT.SATURATION, {
1440
value: attr.saturation || 0,
1441
validator: lang.isNumber
1445
* The current value/brightness value 0-100
1449
this.setAttributeConfig(this.OPT.VALUE, {
1450
value: lang.isNumber(attr.value) ? attr.value : 100,
1451
validator: lang.isNumber
1455
* The current red value 0-255
1459
this.setAttributeConfig(this.OPT.RED, {
1460
value: lang.isNumber(attr.red) ? attr.red : 255,
1461
validator: lang.isNumber
1465
* The current green value 0-255
1469
this.setAttributeConfig(this.OPT.GREEN, {
1470
value: lang.isNumber(attr.green) ? attr.green : 255,
1471
validator: lang.isNumber
1475
* The current blue value 0-255
1479
this.setAttributeConfig(this.OPT.BLUE, {
1480
value: lang.isNumber(attr.blue) ? attr.blue : 255,
1481
validator: lang.isNumber
1485
* The current hex value #000000-#FFFFFF, without the #
1489
this.setAttributeConfig(this.OPT.HEX, {
1490
value: attr.hex || "FFFFFF",
1491
validator: lang.isString
1495
* The current rgb value. Updates the state of all of the
1496
* other value fields. Read-only: use setValue to set the
1497
* controls rgb value.
1499
* @type [int, int, int]
1502
this.setAttributeConfig(this.OPT.RGB, {
1503
value: attr.rgb || [255,255,255],
1504
method: function(rgb) {
1506
this.set(this.OPT.RED, rgb[0], true);
1507
this.set(this.OPT.GREEN, rgb[1], true);
1508
this.set(this.OPT.BLUE, rgb[2], true);
1510
var websafe = Color.websafe(rgb);
1511
this.set(this.OPT.WEBSAFE, websafe, true);
1513
var hex = Color.rgb2hex(rgb);
1514
this.set(this.OPT.HEX, hex, true);
1516
var hsv = Color.rgb2hsv(rgb);
1518
this.logger.log(sub("RGB value set to {rgb} (hsv: {hsv})", {
1519
"hsv": hsv, "rgb": rgb
1522
this.set(this.OPT.HUE, hsv[0], true);
1523
this.set(this.OPT.SATURATION, Math.round(hsv[1]*100), true);
1524
this.set(this.OPT.VALUE, Math.round(hsv[2]*100), true);
1530
* If the color picker will live inside of a container object,
1531
* set, provide a reference to it so the control can use the
1532
* container's events.
1533
* @attribute container
1534
* @type YAHOO.widget.Panel
1536
this.setAttributeConfig(this.OPT.CONTAINER, {
1538
method: function(container) {
1540
// Position can get out of sync when the
1541
// control is manipulated while display is
1542
// none. Resetting the slider constraints
1543
// when it is visible gets the state back in
1545
container.showEvent.subscribe(function() {
1546
// this.pickerSlider.thumb.resetConstraints();
1547
// this.hueSlider.thumb.resetConstraints();
1548
this.pickerSlider.focus();
1554
* The closest current websafe value
1555
* @attribute websafe
1558
this.setAttributeConfig(this.OPT.WEBSAFE, {
1559
value: attr.websafe || [255,255,255]
1563
var ids = attr.ids || lang.merge({}, this.ID);
1565
if (!attr.ids && _pickercount > 1) {
1566
for (var i in ids) {
1567
if (lang.hasOwnProperty(ids, i)) {
1568
ids[i] = ids[i] + _pickercount;
1575
* A list of element ids and/or element references used by the
1576
* control. The default is the this.ID list, and can be customized
1577
* by passing a list in the contructor
1579
* @type {referenceid: realid}
1582
this.setAttributeConfig(this.OPT.IDS, {
1588
* A list of txt strings for internationalization. Default
1594
this.setAttributeConfig(this.OPT.TXT, {
1595
value: attr.txt || this.TXT,
1600
* The img src default list
1603
* @type {key: image}
1606
this.setAttributeConfig(this.OPT.IMAGES, {
1607
value: attr.images || this.IMAGE,
1611
* The element refs used by this control. Set at initialization
1612
* @attribute elements
1613
* @type {id: HTMLElement}
1616
this.setAttributeConfig(this.OPT.ELEMENTS, {
1622
* Returns the cached element reference. If the id is not a string, it
1623
* is assumed that it is an element and this is returned.
1624
* @param id {string|HTMLElement} the element key, id, or ref
1625
* @param on {boolean} hide or show. If true, show
1627
var _hideShowEl = function(id, on) {
1628
var el = (lang.isString(id) ? this.getElement(id) : id);
1629
//Dom.setStyle(id, "visibility", (on) ? "" : "hidden");
1630
Dom.setStyle(el, "display", (on) ? "" : "none");
1634
* Hide/show the entire set of controls
1635
* @attribute showcontrols
1639
this.setAttributeConfig(this.OPT.SHOW_CONTROLS, {
1640
value: lang.isBoolean(attr.showcontrols) ? attr.showcontrols : true,
1641
method: function(on) {
1643
var el = Dom.getElementsByClassName("bd", "div",
1644
this.getElement(this.ID.CONTROLS))[0];
1646
_hideShowEl.call(this, el, on);
1648
this.getElement(this.ID.CONTROLS_LABEL).innerHTML =
1649
(on) ? this.get(this.OPT.TXT).HIDE_CONTROLS :
1650
this.get(this.OPT.TXT).SHOW_CONTROLS;
1656
* Hide/show the rgb controls
1657
* @attribute showrgbcontrols
1661
this.setAttributeConfig(this.OPT.SHOW_RGB_CONTROLS, {
1662
value: lang.isBoolean(attr.showrgbcontrols) ? attr.showrgbcontrols : true,
1663
method: function(on) {
1664
//Dom.setStyle(this.getElement(this.ID.RBG_CONTROLS), "visibility", (on) ? "" : "hidden");
1665
_hideShowEl.call(this, this.ID.RGB_CONTROLS, on);
1670
* Hide/show the hsv controls
1671
* @attribute showhsvcontrols
1675
this.setAttributeConfig(this.OPT.SHOW_HSV_CONTROLS, {
1676
value: lang.isBoolean(attr.showhsvcontrols) ?
1677
attr.showhsvcontrols : false,
1678
method: function(on) {
1679
//Dom.setStyle(this.getElement(this.ID.HSV_CONTROLS), "visibility", (on) ? "" : "hidden");
1680
_hideShowEl.call(this, this.ID.HSV_CONTROLS, on);
1682
// can't show both the hsv controls and the rbg hex summary
1683
if (on && this.get(this.OPT.SHOW_HEX_SUMMARY)) {
1684
this.set(this.OPT.SHOW_HEX_SUMMARY, false);
1690
* Hide/show the hex controls
1691
* @attribute showhexcontrols
1695
this.setAttributeConfig(this.OPT.SHOW_HEX_CONTROLS, {
1696
value: lang.isBoolean(attr.showhexcontrols) ?
1697
attr.showhexcontrols : false,
1698
method: function(on) {
1699
_hideShowEl.call(this, this.ID.HEX_CONTROLS, on);
1704
* Hide/show the websafe swatch
1705
* @attribute showwebsafe
1709
this.setAttributeConfig(this.OPT.SHOW_WEBSAFE, {
1710
value: lang.isBoolean(attr.showwebsafe) ? attr.showwebsafe : true,
1711
method: function(on) {
1712
_hideShowEl.call(this, this.ID.WEBSAFE_SWATCH, on);
1717
* Hide/show the hex summary
1718
* @attribute showhexsummary
1722
this.setAttributeConfig(this.OPT.SHOW_HEX_SUMMARY, {
1723
value: lang.isBoolean(attr.showhexsummary) ? attr.showhexsummary : true,
1724
method: function(on) {
1725
_hideShowEl.call(this, this.ID.HEX_SUMMARY, on);
1727
// can't show both the hsv controls and the rbg hex summary
1728
if (on && this.get(this.OPT.SHOW_HSV_CONTROLS)) {
1729
this.set(this.OPT.SHOW_HSV_CONTROLS, false);
1733
this.setAttributeConfig(this.OPT.ANIMATE, {
1734
value: lang.isBoolean(attr.animate) ? attr.animate : true,
1735
method: function(on) {
1736
this.pickerSlider.animate = on;
1737
this.hueSlider.animate = on;
1741
this.on(this.OPT.HUE + "Change", _updateRGBFromHSV, this, true);
1742
this.on(this.OPT.SATURATION + "Change", _updateRGBFromHSV, this, true);
1743
this.on(this.OPT.VALUE + "Change", _updatePickerSlider, this, true);
1745
this.on(this.OPT.RED + "Change", _updateRGB, this, true);
1746
this.on(this.OPT.GREEN + "Change", _updateRGB, this, true);
1747
this.on(this.OPT.BLUE + "Change", _updateRGB, this, true);
1749
this.on(this.OPT.HEX + "Change", _updateHex, this, true);
1755
YAHOO.register("colorpicker", YAHOO.widget.ColorPicker, {version: "2.6.0", build: "1321"});