MDL-11082 Improved groups upgrade performance 1.8x -> 1.9; thanks Eloy for telling...
[moodle-pu.git] / lib / yui / colorpicker / colorpicker-beta.js
blob56d40f54123711f0e0dfc642fddae257173aabd1
1 /*
2 Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 2.3.0
6 */
7 /**
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;
16 return {
18 /**
19 * Converts 0-1 to 0-255
20 * @method real2dec
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));
28 /**
29 * Converts HSV (h[0-360], s[0-1]), v[0-1] to RGB [255,255,255]
30 * @method hsv2rgb
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
36 * decimal.
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);
46 f = (h/60)-i;
47 p = v*(1-s);
48 q = v*(1-f*s);
49 t = v*(1-(1-f)*s);
50 switch(i) {
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;
59 var fn=this.real2dec;
61 return [fn(r), fn(g), fn(b)];
64 /**
65 * Converts to RGB [255,255,255] to HSV (h[0-360], s[0-1]), v[0-1]
66 * @method rgb2hsv
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]);
79 r=r/255;
80 g=g/255;
81 b=b/255;
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);
86 delta = max-min;
88 switch (max) {
89 case min: h=0; break;
90 case r: h=60*(g-b)/delta;
91 if (g<b) {
92 h+=360;
94 break;
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];
103 return hsv;
108 * Converts decimal rgb values into a hex string
109 * 255,255,255 -> FFFFFF
110 * @method rgb2hex
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]);
122 var f=this.dec2hex;
123 return f(r) + f(g) + f(b);
127 * Converts an int 0...255 to hex pair 00...FF
128 * @method dec2hex
129 * @param n {int} the number to convert
130 * @return {string} the hex equivalent
132 dec2hex: function(n) {
133 n = parseInt(n, 10);
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
142 * @method hex2dec
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());
151 var s=str.split('');
153 return ((f(s[0]) * 16) + f(s[1]));
157 * Converts a hex string to rgb
158 * @method hex2rgb
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.
169 * @method websafe
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);
187 var i, next;
188 for (i=0; i<256; i=i+51) {
189 next = i+51;
190 if (v >= i && v <= next) {
191 return (v-i > 25) ? next : i;
196 return v;
199 return [f(r), f(g), f(b)];
202 }();
205 (function() {
207 var pickercount = 0;
210 * The colorpicker module provides a widget for selecting colors
211 * @module colorpicker
212 * @requires yahoo, dom, event, element, slider
216 * A widget to select colors
217 * @namespace YAHOO.widget
218 * @class YAHOO.widget.ColorPicker
219 * @extends YAHOO.util.Element
220 * @constructor
221 * @param {HTMLElement | String | Object} el(optional) The html
222 * element that represents the colorpicker, or the attribute object to use.
223 * An element will be created if none provided.
224 * @param {Object} attr (optional) A key map of the colorpicker's
225 * initial attributes. Ignored if first arg is attributes object.
227 YAHOO.widget.ColorPicker = function(el, attr) {
228 pickercount = pickercount + 1;
229 attr = attr || {};
230 if (arguments.length === 1 && !YAHOO.lang.isString(el) && !el.nodeName) {
231 attr = el; // treat first arg as attr object
232 el = attr.element || null;
235 if (!el && !attr.element) { // create if we dont have one
236 el = _createHostElement.call(this, attr);
239 YAHOO.widget.ColorPicker.superclass.constructor.call(this, el, attr);
242 YAHOO.extend(YAHOO.widget.ColorPicker, YAHOO.util.Element);
244 var proto = YAHOO.widget.ColorPicker.prototype,
245 Slider=YAHOO.widget.Slider,
246 Color=YAHOO.util.Color,
247 Dom = YAHOO.util.Dom,
248 Event = YAHOO.util.Event,
249 lang = YAHOO.lang,
250 sub = lang.substitute;
253 var b = "yui-picker";
256 * The element ids used by this control
257 * @property ID
258 * @final
260 proto.ID = {
263 * The id for the "red" form field
264 * @property ID.R
265 * @type String
266 * @final
267 * @default yui-picker-r
269 R: b + "-r",
272 * The id for the "red" hex pair output
273 * @property ID.R_HEX
274 * @type String
275 * @final
276 * @default yui-picker-rhex
278 R_HEX: b + "-rhex",
281 * The id for the "green" form field
282 * @property ID.G
283 * @type String
284 * @final
285 * @default yui-picker-g
287 G: b + "-g",
290 * The id for the "green" hex pair output
291 * @property ID.G_HEX
292 * @type String
293 * @final
294 * @default yui-picker-ghex
296 G_HEX: b + "-ghex",
300 * The id for the "blue" form field
301 * @property ID.B
302 * @type String
303 * @final
304 * @default yui-picker-b
306 B: b + "-b",
309 * The id for the "blue" hex pair output
310 * @property ID.B_HEX
311 * @type String
312 * @final
313 * @default yui-picker-bhex
315 B_HEX: b + "-bhex",
318 * The id for the "hue" form field
319 * @property ID.H
320 * @type String
321 * @final
322 * @default yui-picker-h
324 H: b + "-h",
327 * The id for the "saturation" form field
328 * @property ID.S
329 * @type String
330 * @final
331 * @default yui-picker-s
333 S: b + "-s",
336 * The id for the "value" form field
337 * @property ID.V
338 * @type String
339 * @final
340 * @default yui-picker-v
342 V: b + "-v",
345 * The id for the picker region slider
346 * @property ID.PICKER_BG
347 * @type String
348 * @final
349 * @default yui-picker-bg
351 PICKER_BG: b + "-bg",
354 * The id for the picker region thumb
355 * @property ID.PICKER_THUMB
356 * @type String
357 * @final
358 * @default yui-picker-thumb
360 PICKER_THUMB: b + "-thumb",
363 * The id for the hue slider
364 * @property ID.HUE_BG
365 * @type String
366 * @final
367 * @default yui-picker-hue-bg
369 HUE_BG: b + "-hue-bg",
372 * The id for the hue thumb
373 * @property ID.HUE_THUMB
374 * @type String
375 * @final
376 * @default yui-picker-hue-thumb
378 HUE_THUMB: b + "-hue-thumb",
381 * The id for the hex value form field
382 * @property ID.HEX
383 * @type String
384 * @final
385 * @default yui-picker-hex
387 HEX: b + "-hex",
390 * The id for the color swatch
391 * @property ID.SWATCH
392 * @type String
393 * @final
394 * @default yui-picker-swatch
396 SWATCH: b + "-swatch",
399 * The id for the websafe color swatch
400 * @property ID.WEBSAFE_SWATCH
401 * @type String
402 * @final
403 * @default yui-picker-websafe-swatch
405 WEBSAFE_SWATCH: b + "-websafe-swatch",
408 * The id for the control details
409 * @property ID.CONTROLS
410 * @final
411 * @default yui-picker-controls
413 CONTROLS: b + "-controls",
416 * The id for the rgb controls
417 * @property ID.RGB_CONTROLS
418 * @final
419 * @default yui-picker-rgb-controls
421 RGB_CONTROLS: b + "-rgb-controls",
424 * The id for the hsv controls
425 * @property ID.HSV_CONTROLS
426 * @final
427 * @default yui-picker-hsv-controls
429 HSV_CONTROLS: b + "-hsv-controls",
432 * The id for the hsv controls
433 * @property ID.HEX_CONTROLS
434 * @final
435 * @default yui-picker-hex-controls
437 HEX_CONTROLS: b + "-hex-controls",
440 * The id for the hex summary
441 * @property ID.HEX_SUMMARY
442 * @final
443 * @default yui-picker-hex-summary
445 HEX_SUMMARY: b + "-hex-summary",
448 * The id for the controls section header
449 * @property ID.CONTROLS_LABEL
450 * @final
451 * @default yui-picker-controls-label
453 CONTROLS_LABEL: b + "-controls-label"
457 * Constants for any script-generated messages. The values here
458 * are the default messages. They can be updated by providing
459 * the complete list to the constructor for the "txt" attribute.
460 * @property TXT
461 * @final
463 proto.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: "%"
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
487 proto.IMAGE = {
488 PICKER_THUMB: "../../build/colorpicker/assets/picker_thumb.png",
489 HUE_THUMB: "../../build/colorpicker/assets/hue_thumb.png"
493 * Constants for the control's custom event names. subscribe
494 * to the rgbChange event instead.
495 * @property EVENT
496 * @final
498 //proto.EVENT = {
499 //CHANGE: "change"
500 //};
502 //proto.CSS = { };
505 * Constants for the control's default default values
506 * @property DEFAULT
507 * @final
509 proto.DEFAULT = {
510 PICKER_SIZE: 180
514 * Constants for the control's configuration attributes
515 * @property OPT
516 * @final
518 proto.OPT = {
519 HUE: "hue",
520 SATURATION: "saturation",
521 VALUE: "value",
522 RED: "red",
523 GREEN: "green",
524 BLUE: "blue",
525 HSV: "hsv",
526 RGB: "rgb",
527 WEBSAFE: "websafe",
528 HEX: "hex",
529 PICKER_SIZE: "pickersize",
530 SHOW_CONTROLS: "showcontrols",
531 SHOW_RGB_CONTROLS: "showrgbcontrols",
532 SHOW_HSV_CONTROLS: "showhsvcontrols",
533 SHOW_HEX_CONTROLS: "showhexcontrols",
534 SHOW_HEX_SUMMARY: "showhexsummary",
535 SHOW_WEBSAFE: "showwebsafe",
536 //SHOW_SUBMIT: "showsubmit",
537 CONTAINER: "container",
538 IDS: "ids",
539 ELEMENTS: "elements",
540 TXT: "txt",
541 IMAGES: "images",
542 ANIMATE: "animate"
546 * Sets the control to the specified rgb value and
547 * moves the sliders to the proper positions
548 * @method setValue
549 * @param rgb {[int, int, int]} the rgb value
550 * @param silent {boolean} whether or not to fire the change event
552 proto.setValue = function(rgb, silent) {
553 silent = (silent) || false;
554 this.set(this.OPT.RGB, rgb, silent);
555 _updateSliders.call(this);
559 * The hue slider
560 * @property hueSlider
561 * @type YAHOO.widget.Slider
563 proto.hueSlider = null;
566 * The picker region
567 * @property pickerSlider
568 * @type YAHOO.widget.Slider
570 proto.pickerSlider = null;
573 * Translates the slider value into hue, int[0,359]
574 * @method _getH
575 * @private
576 * @return {int} the hue from 0 to 359
578 var _getH = function() {
579 var size = this.get(this.OPT.PICKER_SIZE),
580 h = (size - this.hueSlider.getValue()) / size;
581 h = Math.round(h*360);
582 return (h === 360) ? 0 : h;
586 * Translates the slider value into saturation, int[0,1], left to right
587 * @method _getS
588 * @private
589 * @return {int} the saturation from 0 to 1
591 var _getS = function() {
592 return this.pickerSlider.getXValue() / this.get(this.OPT.PICKER_SIZE);
596 * Translates the slider value into value/brightness, int[0,1], top
597 * to bottom
598 * @method _getV
599 * @private
600 * @return {int} the value from 0 to 1
602 var _getV = function() {
603 var size = this.get(this.OPT.PICKER_SIZE);
604 return (size - this.pickerSlider.getYValue()) / size;
608 * Updates the background of the swatch with the current rbg value.
609 * Also updates the websafe swatch to the closest websafe color
610 * @method _updateSwatch
611 * @private
613 var _updateSwatch = function() {
614 var rgb = this.get(this.OPT.RGB),
615 websafe = this.get(this.OPT.WEBSAFE),
616 el = this.getElement(this.ID.SWATCH),
617 color = rgb.join(","),
618 txt = this.get(this.OPT.TXT);
620 Dom.setStyle(el, "background-color", "rgb(" + color + ")");
621 el.title = lang.substitute(txt.CURRENT_COLOR, {
622 "rgb": "#" + this.get(this.OPT.HEX)
626 el = this.getElement(this.ID.WEBSAFE_SWATCH);
627 color = websafe.join(",");
629 Dom.setStyle(el, "background-color", "rgb(" + color + ")");
630 el.title = lang.substitute(txt.CLOSEST_WEBSAFE, {
631 "rgb": "#" + Color.rgb2hex(websafe)
637 * Reads the sliders and converts the values to RGB, updating the
638 * internal state for all the individual form fields
639 * @method _getValuesFromSliders
640 * @private
642 var _getValuesFromSliders = function() {
643 var h=_getH.call(this), s=_getS.call(this), v=_getV.call(this);
645 rgb = Color.hsv2rgb(h, s, v);
646 var websafe = Color.websafe(rgb);
647 var hex = Color.rgb2hex(rgb[0], rgb[1], rgb[2]);
649 this.set(this.OPT.RGB, rgb);
653 * Updates the form field controls with the state data contained
654 * in the control.
655 * @method _updateFormFields
656 * @private
658 var _updateFormFields = function() {
659 this.getElement(this.ID.H).value = this.get(this.OPT.HUE);
660 this.getElement(this.ID.S).value = this.get(this.OPT.SATURATION);
661 this.getElement(this.ID.V).value = this.get(this.OPT.VALUE);
662 this.getElement(this.ID.R).value = this.get(this.OPT.RED);
663 this.getElement(this.ID.R_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.RED));
664 this.getElement(this.ID.G).value = this.get(this.OPT.GREEN);
665 this.getElement(this.ID.G_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.GREEN));
666 this.getElement(this.ID.B).value = this.get(this.OPT.BLUE);
667 this.getElement(this.ID.B_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.BLUE));
668 this.getElement(this.ID.HEX).value = this.get(this.OPT.HEX);
672 * Event handler for the hue slider.
673 * @method _onHueSliderChange
674 * @param newOffset {int} pixels from the start position
675 * @private
677 var _onHueSliderChange = function(newOffset) {
679 var h = _getH.call(this);
680 this.set(this.OPT.HUE, h, true);
682 // set picker background to the hue
683 var rgb = Color.hsv2rgb(h, 1, 1);
684 var styleDef = "rgb(" + rgb.join(",") + ")";
686 Dom.setStyle(this.getElement(this.ID.PICKER_BG), "background-color", styleDef);
688 if (this.hueSlider.valueChangeSource === this.hueSlider.SOURCE_UI_EVENT) {
689 _getValuesFromSliders.call(this);
692 _updateFormFields.call(this);
693 _updateSwatch.call(this);
697 * Event handler for the picker slider, which controls the
698 * saturation and value/brightness.
699 * @method _onPickerSliderChange
700 * @param newOffset {{x: int, y: int}} x/y pixels from the start position
701 * @private
703 var _onPickerSliderChange = function(newOffset) {
705 var s=_getS.call(this), v=_getV.call(this);
706 this.set(this.OPT.SATURATION, Math.round(s*100), true);
707 this.set(this.OPT.VALUE, Math.round(v*100), true);
709 if (this.pickerSlider.valueChangeSource === this.pickerSlider.SOURCE_UI_EVENT) {
710 _getValuesFromSliders.call(this);
713 _updateFormFields.call(this);
714 _updateSwatch.call(this);
719 * Key map to well-known commands for txt field input
720 * @method _getCommand
721 * @param e {Event} the keypress or keydown event
722 * @return {int} a command code
723 * <ul>
724 * <li>0 = not a number, letter in range, or special key</li>
725 * <li>1 = number</li>
726 * <li>2 = a-fA-F</li>
727 * <li>3 = increment (up arrow)</li>
728 * <li>4 = decrement (down arrow)</li>
729 * <li>5 = special key (tab, delete, return, escape, left, right)</li>
730 * <li>6 = return</li>
731 * </ul>
732 * @private
734 var _getCommand = function(e) {
735 var c = Event.getCharCode(e);
737 //alert(Event.getCharCode(e) + ", " + e.keyCode + ", " + e.charCode);
739 // special keys
740 if (c === 38) { // up arrow
741 return 3;
742 } else if (c === 13) { // return
743 return 6;
744 } else if (c === 40) { // down array
745 return 4;
746 } else if (c >= 48 && c<=57) { // 0-9
747 return 1;
748 } else if (c >= 97 && c<=102) { // a-f
749 return 2;
750 } else if (c >= 65 && c<=70) { // A-F
751 return 2;
752 //} else if ("8, 9, 13, 27, 37, 39".indexOf(c) > -1 ||
753 // (c >= 112 && c <=123)) { // including F-keys
754 // tab, delete, return, escape, left, right
755 } else if ("8, 9, 13, 27, 37, 39".indexOf(c) > -1) { // special chars
756 return 5;
757 } else { // something we probably don't want
758 return 0;
763 * Handle keypress on one of the rgb or hsv fields.
764 * @method _rgbFieldKeypress
765 * @param e {Event} the keypress event
766 * @param el {HTMLElement} the field
767 * @param prop {string} the key to the linked property
768 * @private
770 var _rgbFieldKeypress = function(e, el, prop) {
771 var command = _getCommand(e);
772 var inc = (e.shiftKey) ? 10 : 1;
773 switch (command) {
774 case 6: // return, update the value
775 _useFieldValue.apply(this, arguments);
776 break;
778 case 3: // up arrow, increment
779 this.set(prop, Math.min(this.get(prop)+inc, 255));
780 _updateFormFields.call(this);
781 //Event.stopEvent(e);
782 break;
783 case 4: // down arrow, decrement
784 this.set(prop, Math.max(this.get(prop)-inc, 0));
785 _updateFormFields.call(this);
786 //Event.stopEvent(e);
787 break;
789 default:
795 * Handle keydown on the hex field
796 * @method _hexFieldKeypress
797 * @param e {Event} the keypress event
798 * @param el {HTMLElement} the field
799 * @param prop {string} the key to the linked property
800 * @private
802 var _hexFieldKeypress = function(e, el, prop) {
803 var command = _getCommand(e);
804 if (command === 6) { // return, update the value
805 _useFieldValue.apply(this, arguments);
810 * Use the value of the text field to update the control
811 * @method _hexFieldKeypress
812 * @param e {Event} an event
813 * @param el {HTMLElement} the field
814 * @param prop {string} the key to the linked property
815 * @private
817 var _useFieldValue = function(e, el, prop) {
818 var val = el.value;
820 if (prop !== this.OPT.HEX) {
821 val = parseInt(val, 10);
824 if (val !== this.get(prop)) {
825 this.set(prop, val);
829 /**
830 * Allows numbers and special chars only. Used for the
831 * rgb and hsv fields keypress handler.
832 * @method _numbersOnly
833 * @param e {Event} the event
834 * @private
835 * @return {boolean} false if we are canceling the event
837 var _numbersOnly = function(e) {
838 return _hexOnly(e, true);
841 /**
842 * Allows numbers and special chars, and by default allows a-f.
843 * Used for the hex field keypress handler.
844 * @method _hexOnly
845 * @param e {Event} the event
846 * @param numbersOnly omits a-f if set to true
847 * @private
848 * @return {boolean} false if we are canceling the event
850 var _hexOnly = function(e, numbersOnly) {
851 var command = _getCommand(e);
852 switch (command) {
853 case 6: // return
854 case 5: // special char
855 case 1: // number
856 break;
857 case 2: // hex char (a-f)
858 if (numbersOnly !== true) {
859 break;
862 // fallthrough is intentional
864 default: // prevent alpha and punctuation
865 Event.stopEvent(e);
866 return false;
871 * Returns the element reference that is saved. The id can be either
872 * the element id, or the key for this id in the "id" config attribute.
873 * For instance, the host element id can be obtained by passing its
874 * id (default: "yui_picker") or by its key "YUI_PICKER".
875 * @param id {string} the element id, or key
876 * @return {HTMLElement} a reference to the element
878 proto.getElement = function(id) {
879 return this.get(this.OPT.ELEMENTS)[this.get(this.OPT.IDS)[id]];
882 _createElements = function() {
883 var el, child, img, fld, i,
884 ids = this.get(this.OPT.IDS),
885 txt = this.get(this.OPT.TXT),
886 images = this.get(this.OPT.IMAGES),
887 Elem = function(type, o) {
888 var n = document.createElement(type);
889 if (o) {
890 lang.augmentObject(n, o, true);
892 return n;
894 RGBElem = function(type, obj) {
895 var o = lang.merge({
896 //type: "txt",
897 autocomplete: "off",
898 value: "0",
899 size: 3,
900 maxlength: 3
901 }, obj);
903 o.name = o.id;
904 return new Elem(type, o);
907 var p = this.get("element");
909 // Picker slider (S and V) ---------------------------------------------
911 el = new Elem("div", {
912 id: ids[this.ID.PICKER_BG],
913 className: "yui-picker-bg",
914 tabIndex: -1,
915 hideFocus: true
918 child = new Elem("div", {
919 id: ids[this.ID.PICKER_THUMB],
920 className: "yui-picker-thumb"
923 img = new Elem("img", {
924 src: images.PICKER_THUMB
927 child.appendChild(img);
928 el.appendChild(child);
929 p.appendChild(el);
931 // Hue slider ---------------------------------------------
932 el = new Elem("div", {
933 id: ids[this.ID.HUE_BG],
934 className: "yui-picker-hue-bg",
935 tabIndex: -1,
936 hideFocus: true
939 child = new Elem("div", {
940 id: ids[this.ID.HUE_THUMB],
941 className: "yui-picker-hue-thumb"
944 img = new Elem("img", {
945 src: images.HUE_THUMB
948 child.appendChild(img);
949 el.appendChild(child);
950 p.appendChild(el);
953 // controls ---------------------------------------------
955 el = new Elem("div", {
956 id: ids[this.ID.CONTROLS],
957 className: "yui-picker-controls"
960 p.appendChild(el);
961 p = el;
963 // controls header
964 el = new Elem("div", {
965 className: "hd"
968 child = new Elem("a", {
969 id: ids[this.ID.CONTROLS_LABEL],
970 //className: "yui-picker-controls-label",
971 href: "#"
973 el.appendChild(child);
974 p.appendChild(el);
976 // bd
977 el = new Elem("div", {
978 className: "bd"
981 p.appendChild(el);
982 p = el;
984 // rgb
985 el = new Elem("ul", {
986 id: ids[this.ID.RGB_CONTROLS],
987 className: "yui-picker-rgb-controls"
990 child = new Elem("li");
991 child.appendChild(document.createTextNode(txt.R + " "));
993 fld = new RGBElem("input", {
994 id: ids[this.ID.R],
995 className: "yui-picker-r"
998 child.appendChild(fld);
999 el.appendChild(child);
1001 child = new Elem("li");
1002 child.appendChild(document.createTextNode(txt.G + " "));
1004 fld = new RGBElem("input", {
1005 id: ids[this.ID.G],
1006 className: "yui-picker-g"
1009 child.appendChild(fld);
1010 el.appendChild(child);
1012 child = new Elem("li");
1013 child.appendChild(document.createTextNode(txt.B + " "));
1015 fld = new RGBElem("input", {
1016 id: ids[this.ID.B],
1017 className: "yui-picker-b"
1020 child.appendChild(fld);
1021 el.appendChild(child);
1023 p.appendChild(el);
1025 // hsv
1026 el = new Elem("ul", {
1027 id: ids[this.ID.HSV_CONTROLS],
1028 className: "yui-picker-hsv-controls"
1031 child = new Elem("li");
1032 child.appendChild(document.createTextNode(txt.H + " "));
1034 fld = new RGBElem("input", {
1035 id: ids[this.ID.H],
1036 className: "yui-picker-h"
1039 child.appendChild(fld);
1040 child.appendChild(document.createTextNode(" " + txt.DEG));
1042 el.appendChild(child);
1044 child = new Elem("li");
1045 child.appendChild(document.createTextNode(txt.S + " "));
1047 fld = new RGBElem("input", {
1048 id: ids[this.ID.S],
1049 className: "yui-picker-s"
1052 child.appendChild(fld);
1053 child.appendChild(document.createTextNode(" " + txt.PERCENT));
1055 el.appendChild(child);
1057 child = new Elem("li");
1058 child.appendChild(document.createTextNode(txt.V + " "));
1060 fld = new RGBElem("input", {
1061 id: ids[this.ID.V],
1062 className: "yui-picker-v"
1065 child.appendChild(fld);
1066 child.appendChild(document.createTextNode(" " + txt.PERCENT));
1068 el.appendChild(child);
1069 p.appendChild(el);
1072 // hex summary
1074 el = new Elem("ul", {
1075 id: ids[this.ID.HEX_SUMMARY],
1076 className: "yui-picker-hex_summary"
1079 child = new Elem("li", {
1080 id: ids[this.ID.R_HEX]
1082 el.appendChild(child);
1084 child = new Elem("li", {
1085 id: ids[this.ID.G_HEX]
1087 el.appendChild(child);
1089 child = new Elem("li", {
1090 id: ids[this.ID.B_HEX]
1092 el.appendChild(child);
1093 p.appendChild(el);
1095 // hex field
1096 el = new Elem("div", {
1097 id: ids[this.ID.HEX_CONTROLS],
1098 className: "yui-picker-hex-controls"
1100 el.appendChild(document.createTextNode(txt.HEX + " "));
1102 child = new RGBElem("input", {
1103 id: ids[this.ID.HEX],
1104 className: "yui-picker-hex",
1105 size: 6,
1106 maxlength: 6
1109 el.appendChild(child);
1110 p.appendChild(el);
1112 p = this.get("element");
1114 // swatch
1115 el = new Elem("div", {
1116 id: ids[this.ID.SWATCH],
1117 className: "yui-picker-swatch"
1120 p.appendChild(el);
1122 // websafe swatch
1123 el = new Elem("div", {
1124 id: ids[this.ID.WEBSAFE_SWATCH],
1125 className: "yui-picker-websafe-swatch"
1128 p.appendChild(el);
1133 * Sets the initial state of the sliders
1134 * @method initPicker
1136 proto.initPicker = function () {
1138 // bind all of our elements
1139 var o=this.OPT,
1140 ids = this.get(o.IDS),
1141 els = this.get(o.ELEMENTS),
1142 i, el, id;
1144 // Add the default value as a key for each element for easier lookup
1145 for (i in this.ID) {
1146 if (lang.hasOwnProperty(this.ID, i)) {
1147 ids[this.ID[i]] = ids[i];
1151 // Check for picker element, if not there, create all of them
1152 el = Dom.get(ids[this.ID.PICKER_BG]);
1153 if (!el) {
1154 _createElements.call(this);
1155 } else {
1158 for (i in ids) {
1159 if (lang.hasOwnProperty(ids, i)) {
1160 // look for element
1161 el = Dom.get(ids[i]);
1163 // generate an id if the implementer passed in an element reference,
1164 // and the element did not have an id already
1165 id = Dom.generateId(el);
1167 // update the id in case we generated the id
1168 ids[i] = id; // key is WEBSAFE_SWATCH
1169 ids[ids[i]] = id; // key is websafe_swatch
1171 // store the dom ref
1172 els[id] = el;
1176 // set the initial visibility state of our controls
1177 els = [o.SHOW_CONTROLS,
1178 o.SHOW_RGB_CONTROLS,
1179 o.SHOW_HSV_CONTROLS,
1180 o.SHOW_HEX_CONTROLS,
1181 o.SHOW_HEX_SUMMARY,
1182 o.SHOW_WEBSAFE
1185 for (i=0; i<els.length; i=i+1) {
1186 this.set(els[i], this.get(els[i]));
1189 var s = this.get(o.PICKER_SIZE);
1191 this.hueSlider = Slider.getVertSlider(this.getElement(this.ID.HUE_BG),
1192 this.getElement(this.ID.HUE_THUMB), 0, s);
1193 this.hueSlider.subscribe("change", _onHueSliderChange, this, true);
1195 this.pickerSlider = Slider.getSliderRegion(this.getElement(this.ID.PICKER_BG),
1196 this.getElement(this.ID.PICKER_THUMB), 0, s, 0, s);
1197 this.pickerSlider.subscribe("change", _onPickerSliderChange, this, true);
1199 //_onHueSliderChange.call(this, 0);
1201 Event.on(this.getElement(this.ID.WEBSAFE_SWATCH), "click", function(e) {
1202 this.setValue(this.get(o.WEBSAFE));
1203 //_updateSliders
1204 }, this, true);
1206 Event.on(this.getElement(this.ID.CONTROLS_LABEL), "click", function(e) {
1207 this.set(o.SHOW_CONTROLS, !this.get(o.SHOW_CONTROLS));
1208 Event.preventDefault(e);
1209 }, this, true);
1211 _attachRGBHSV.call(this, this.ID.R, this.OPT.RED);
1212 _attachRGBHSV.call(this, this.ID.G, this.OPT.GREEN);
1213 _attachRGBHSV.call(this, this.ID.B, this.OPT.BLUE);
1214 _attachRGBHSV.call(this, this.ID.H, this.OPT.HUE);
1215 _attachRGBHSV.call(this, this.ID.S, this.OPT.SATURATION);
1216 _attachRGBHSV.call(this, this.ID.V, this.OPT.VALUE);
1218 Event.on(this.getElement(this.ID.HEX), "keydown", function(e, me) {
1219 _hexFieldKeypress.call(me, e, this, me.OPT.HEX);
1220 }, this);
1222 Event.on(this.getElement(this.ID.HEX), "keypress", _hexOnly, this);
1223 Event.on(this.getElement(this.ID.HEX), "blur", function(e, me) {
1224 _useFieldValue.call(me, e, this, me.OPT.HEX);
1225 }, this);
1228 _attachRGBHSV = function(id, config) {
1229 Event.on(this.getElement(id), "keydown", function(e, me) {
1230 _rgbFieldKeypress.call(me, e, this, config);
1231 }, this);
1232 Event.on(this.getElement(id), "keypress", _numbersOnly, this);
1233 Event.on(this.getElement(id), "blur", function(e, me) {
1234 _useFieldValue.call(me, e, this, config);
1235 }, this);
1240 * Sets up the config attributes and the change listeners for this
1241 * properties
1242 * @method initAttributes
1243 * @param attr An object containing default attribute values
1245 proto.initAttributes = function(attr) {
1247 attr = attr || {};
1248 YAHOO.widget.ColorPicker.superclass.initAttributes.call(this, attr);
1251 * The size of the picker. Trying to change this is not recommended.
1252 * @config pickersize
1253 * @default 180
1254 * @type int
1256 this.setAttributeConfig(this.OPT.PICKER_SIZE, {
1257 value: attr.size || this.DEFAULT.PICKER_SIZE
1261 * The current hue value 0-360
1262 * @config hue
1263 * @type int
1265 this.setAttributeConfig(this.OPT.HUE, {
1266 value: attr.hue || 0,
1267 validator: lang.isNumber
1271 * The current saturation value 0-100
1272 * @config saturation
1273 * @type int
1275 this.setAttributeConfig(this.OPT.SATURATION, {
1276 value: attr.saturation || 0,
1277 validator: lang.isNumber
1281 * The current value/brightness value 0-100
1282 * @config value
1283 * @type int
1285 this.setAttributeConfig(this.OPT.VALUE, {
1286 value: attr.value || 100,
1287 validator: lang.isNumber
1291 * The current red value 0-255
1292 * @config red
1293 * @type int
1295 this.setAttributeConfig(this.OPT.RED, {
1296 value: attr.red || 255,
1297 validator: lang.isNumber
1301 * The current green value 0-255
1302 * @config green
1303 * @type int
1305 this.setAttributeConfig(this.OPT.GREEN, {
1306 value: attr.red || 255,
1307 validator: lang.isNumber
1311 * The current blue value 0-255
1312 * @config blue
1313 * @type int
1315 this.setAttributeConfig(this.OPT.BLUE, {
1316 value: attr.blue || 255,
1317 validator: lang.isNumber
1321 * The current hex value #000000-#FFFFFF, without the #
1322 * @config hex
1323 * @type string
1325 this.setAttributeConfig(this.OPT.HEX, {
1326 value: attr.hex || "FFFFFF",
1327 validator: lang.isString
1331 * The current rgb value. Updates the state of all of the
1332 * other value fields. Read-only: use setValue to set the
1333 * controls rgb value.
1334 * @config hex
1335 * @type [int, int, int]
1336 * @readonly
1338 this.setAttributeConfig(this.OPT.RGB, {
1339 value: attr.rgb || [255,255,255],
1340 method: function(rgb) {
1342 this.set(this.OPT.RED, rgb[0], true);
1343 this.set(this.OPT.GREEN, rgb[1], true);
1344 this.set(this.OPT.BLUE, rgb[2], true);
1346 var websafe = Color.websafe(rgb);
1347 this.set(this.OPT.WEBSAFE, websafe, true);
1349 var hex = Color.rgb2hex(rgb);
1350 this.set(this.OPT.HEX, hex, true);
1352 var hsv = Color.rgb2hsv(rgb);
1355 this.set(this.OPT.HUE, hsv[0], true);
1356 this.set(this.OPT.SATURATION, Math.round(hsv[1]*100), true);
1357 this.set(this.OPT.VALUE, Math.round(hsv[2]*100), true);
1359 readonly: true
1363 * If the color picker will live inside of a container object,
1364 * set, provide a reference to it so the control can use the
1365 * container's events.
1366 * @config container
1367 * @type YAHOO.widget.Panel
1369 this.setAttributeConfig(this.OPT.CONTAINER, {
1370 value: null,
1371 method: function(container) {
1372 if (container) {
1373 // Position can get out of sync when the
1374 // control is manipulated while display is
1375 // none. Resetting the slider constraints
1376 // when it is visible gets the state back in
1377 // order.
1378 container.showEvent.subscribe(function() {
1379 // this.pickerSlider.thumb.resetConstraints();
1380 // this.hueSlider.thumb.resetConstraints();
1381 this.pickerSlider.focus();
1382 }, this, true);
1387 * The closest current websafe value
1388 * @config websafe
1389 * @type int
1391 this.setAttributeConfig(this.OPT.WEBSAFE, {
1392 value: attr.websafe || [255,255,255]
1396 ids = attr.ids || lang.merge({}, this.ID);
1398 if (!attr.ids && pickercount > 1) {
1399 for (var i in ids) {
1400 if (lang.hasOwnProperty(ids, i)) {
1401 ids[i] = ids[i] + pickercount;
1408 * A list of element ids and/or element references used by the
1409 * control. The default is the this.ID list, and can be customized
1410 * by passing a list in the contructor
1411 * @config ids
1412 * @type {referenceid: realid}
1413 * @writeonce
1415 this.setAttributeConfig(this.OPT.IDS, {
1416 value: ids,
1417 writeonce: true
1421 * A list of txt strings for internationalization. Default
1422 * is this.TXT
1423 * @config txt
1424 * @type {key: txt}
1425 * @writeonce
1427 this.setAttributeConfig(this.OPT.TXT, {
1428 value: attr.txt || this.TXT,
1429 writeonce: true
1433 * The img src default list
1434 * is this.IMAGES
1435 * @config images
1436 * @type {key: image}
1437 * @writeonce
1439 this.setAttributeConfig(this.OPT.IMAGES, {
1440 value: attr.images || this.IMAGE,
1441 writeonce: true
1444 * The element refs used by this control. Set at initialization
1445 * @config elements
1446 * @type {id: HTMLElement}
1447 * @readonly
1449 this.setAttributeConfig(this.OPT.ELEMENTS, {
1450 value: {},
1451 readonly: true
1455 * Returns the cached element reference. If the id is not a string, it
1456 * is assumed that it is an element and this is returned.
1457 * @param id {string|HTMLElement} the element key, id, or ref
1458 * @param on {boolean} hide or show. If true, show
1459 * @private */
1460 _hideShowEl = function(id, on) {
1461 var el = (lang.isString(id) ? this.getElement(id) : id);
1462 //Dom.setStyle(id, "visibility", (on) ? "" : "hidden");
1463 Dom.setStyle(el, "display", (on) ? "" : "none");
1467 * Hide/show the entire set of controls
1468 * @config showcontrols
1469 * @type boolean
1470 * @default true
1472 this.setAttributeConfig(this.OPT.SHOW_CONTROLS, {
1473 value: (attr.showcontrols) || true,
1474 method: function(on) {
1476 var el = Dom.getElementsByClassName("bd", "div",
1477 this.getElement(this.ID.CONTROLS))[0];
1479 _hideShowEl.call(this, el, on);
1481 this.getElement(this.ID.CONTROLS_LABEL).innerHTML =
1482 (on) ? this.get(this.OPT.TXT).HIDE_CONTROLS :
1483 this.get(this.OPT.TXT).SHOW_CONTROLS;
1489 * Hide/show the rgb controls
1490 * @config showrgbcontrols
1491 * @type boolean
1492 * @default true
1494 this.setAttributeConfig(this.OPT.SHOW_RGB_CONTROLS, {
1495 value: (attr.showrgbcontrols) || true,
1496 method: function(on) {
1497 //Dom.setStyle(this.getElement(this.ID.RBG_CONTROLS), "visibility", (on) ? "" : "hidden");
1498 _hideShowEl.call(this, this.ID.RGB_CONTROLS, on);
1503 * Hide/show the hsv controls
1504 * @config showhsvcontrols
1505 * @type boolean
1506 * @default false
1508 this.setAttributeConfig(this.OPT.SHOW_HSV_CONTROLS, {
1509 value: (attr.showhsvcontrols) || false,
1510 method: function(on) {
1511 //Dom.setStyle(this.getElement(this.ID.HSV_CONTROLS), "visibility", (on) ? "" : "hidden");
1512 _hideShowEl.call(this, this.ID.HSV_CONTROLS, on);
1514 // can't show both the hsv controls and the rbg hex summary
1515 if (on && this.get(this.OPT.SHOW_HEX_SUMMARY)) {
1516 this.set(this.OPT.SHOW_HEX_SUMMARY, false);
1522 * Hide/show the hex controls
1523 * @config showhexcontrols
1524 * @type boolean
1525 * @default true
1527 this.setAttributeConfig(this.OPT.SHOW_HEX_CONTROLS, {
1528 value: (attr.showhexcontrols) || false,
1529 method: function(on) {
1530 _hideShowEl.call(this, this.ID.HEX_CONTROLS, on);
1535 * Hide/show the websafe swatch
1536 * @config showwebsafe
1537 * @type boolean
1538 * @default true
1540 this.setAttributeConfig(this.OPT.SHOW_WEBSAFE, {
1541 value: (attr.showwebsafe) || true,
1542 method: function(on) {
1543 _hideShowEl.call(this, this.ID.WEBSAFE_SWATCH, on);
1548 * Hide/show the hex summary
1549 * @config showhexsummary
1550 * @type boolean
1551 * @default true
1553 this.setAttributeConfig(this.OPT.SHOW_HEX_SUMMARY, {
1554 value: (attr.showhexsummary) || true,
1555 method: function(on) {
1556 _hideShowEl.call(this, this.ID.HEX_SUMMARY, on);
1558 // can't show both the hsv controls and the rbg hex summary
1559 if (on && this.get(this.OPT.SHOW_HSV_CONTROLS)) {
1560 this.set(this.OPT.SHOW_HSV_CONTROLS, false);
1564 this.setAttributeConfig(this.OPT.ANIMATE, {
1565 value: (attr.animate) || true,
1566 method: function(on) {
1567 this.pickerSlider.animate = on;
1568 this.hueSlider.animate = on;
1572 this.on(this.OPT.HUE + "Change", _updateRGBFromHSV, this, true);
1573 this.on(this.OPT.SATURATION + "Change", _updateRGBFromHSV, this, true);
1574 this.on(this.OPT.VALUE + "Change", _updatePickerSlider, this, true);
1576 this.on(this.OPT.RED + "Change", _updateRGB, this, true);
1577 this.on(this.OPT.GREEN + "Change", _updateRGB, this, true);
1578 this.on(this.OPT.BLUE + "Change", _updateRGB, this, true);
1580 this.on(this.OPT.HEX + "Change", _updateHex, this, true);
1582 this.initPicker();
1586 * Updates the rgb attribute with the current state of the r,g,b
1587 * fields. This is invoked from change listeners on these
1588 * attributes to facilitate updating these values from the
1589 * individual form fields
1590 * @method _updateRGB
1591 * @private
1593 var _updateRGB = function() {
1594 var rgb = [this.get(this.OPT.RED),
1595 this.get(this.OPT.GREEN),
1596 this.get(this.OPT.BLUE)];
1598 this.set(this.OPT.RGB, rgb);
1600 _updateSliders.call(this);
1604 * Updates the RGB values from the current state of the HSV
1605 * values. Executed when the one of the HSV form fields are
1606 * updated
1607 * _updateRGBFromHSV
1608 * @private
1610 var _updateRGBFromHSV = function() {
1611 var hsv = [this.get(this.OPT.HUE),
1612 this.get(this.OPT.SATURATION)/100,
1613 this.get(this.OPT.VALUE)/100];
1615 var rgb = Color.hsv2rgb(hsv);
1617 this.set(this.OPT.RGB, rgb);
1619 _updateSliders.call(this);
1623 * Parses the hex string to normalize shorthand values, converts
1624 * the hex value to rgb and updates the rgb attribute (which
1625 * updates the state for all of the other values)
1626 * method _updateHex
1627 * @private
1629 var _updateHex = function() {
1631 var hex = this.get(this.OPT.HEX), l=hex.length;
1633 // support #369 -> #336699 shorthand
1634 if (l === 3) {
1635 var c = hex.split(""), i;
1636 for (i=0; i<l; i=i+1) {
1637 c[i] = c[i] + c[i];
1640 hex = c.join("");
1643 if (hex.length !== 6) {
1644 return false;
1647 var rgb = Color.hex2rgb(hex);
1650 this.setValue(rgb);
1652 //_updateSliders.call(this);
1657 * Moves the sliders into the position dictated by the current state
1658 * of the control
1659 * @method _updateSliders
1660 * @private
1662 var _updateSliders = function() {
1663 _updateHueSlider.call(this);
1664 _updatePickerSlider.call(this);
1668 * Moves the hue slider into the position dictated by the current state
1669 * of the control
1670 * @method _updateHueSlider
1671 * @private
1673 var _updateHueSlider = function() {
1674 var size = this.get(this.OPT.PICKER_SIZE),
1675 h = this.get(this.OPT.HUE);
1677 h = size - Math.round(h / 360 * size);
1679 // 0 is at the top and bottom of the hue slider. Always go to
1680 // the top so we don't end up sending the thumb to the bottom
1681 // when the value didn't actually change (e.g., a conversion
1682 // produced 360 instead of 0 and the value was already 0).
1683 if (h === size) {
1684 h = 0;
1687 this.hueSlider.setValue(h);
1691 * Moves the picker slider into the position dictated by the current state
1692 * of the control
1693 * @method _updatePickerSlider
1694 * @private
1696 var _updatePickerSlider = function() {
1697 var size = this.get(this.OPT.PICKER_SIZE),
1698 s = this.get(this.OPT.SATURATION),
1699 v = this.get(this.OPT.VALUE);
1701 s = Math.round(s * size / 100);
1702 v = Math.round(size - (v * size / 100));
1705 this.pickerSlider.setRegionValue(s, v);
1709 * Creates the host element if it doesn't exist
1710 * @method _createHostElement
1711 * @private
1713 var _createHostElement = function() {
1714 var el = document.createElement('div');
1716 if (this.CSS.BASE) {
1717 el.className = this.CSS.BASE;
1720 return el;
1724 })();
1725 YAHOO.register("colorpicker", YAHOO.widget.ColorPicker, {version: "2.3.0", build: "442"});