blob: ef3a14341261999c19f31fe9500b0bf1d687981a [file] [log] [blame]
/*
Copyright (c) 2008, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.net/yui/license.txt
version: 2.6.0
*/
/**
* Provides color conversion and validation utils
* @class YAHOO.util.Color
* @namespace YAHOO.util
*/
YAHOO.util.Color = function() {
var HCHARS="0123456789ABCDEF", lang=YAHOO.lang;
return {
/**
* Converts 0-1 to 0-255
* @method real2dec
* @param n {float} the number to convert
* @return {int} a number 0-255
*/
real2dec: function(n) {
return Math.min(255, Math.round(n*256));
},
/**
* Converts HSV (h[0-360], s[0-1]), v[0-1] to RGB [255,255,255]
* @method hsv2rgb
* @param h {int|[int, float, float]} the hue, or an
* array containing all three parameters
* @param s {float} the saturation
* @param v {float} the value/brightness
* @return {[int, int, int]} the red, green, blue values in
* decimal.
*/
hsv2rgb: function(h, s, v) {
if (lang.isArray(h)) {
return this.hsv2rgb.call(this, h[0], h[1], h[2]);
}
var r, g, b, i, f, p, q, t;
i = Math.floor((h/60)%6);
f = (h/60)-i;
p = v*(1-s);
q = v*(1-f*s);
t = v*(1-(1-f)*s);
switch(i) {
case 0: r=v; g=t; b=p; break;
case 1: r=q; g=v; b=p; break;
case 2: r=p; g=v; b=t; break;
case 3: r=p; g=q; b=v; break;
case 4: r=t; g=p; b=v; break;
case 5: r=v; g=p; b=q; break;
}
var fn=this.real2dec;
return [fn(r), fn(g), fn(b)];
},
/**
* Converts to RGB [255,255,255] to HSV (h[0-360], s[0-1]), v[0-1]
* @method rgb2hsv
* @param r {int|[int, int, int]} the red value, or an
* array containing all three parameters
* @param g {int} the green value
* @param b {int} the blue value
* @return {[int, float, float]} the value converted to hsv
*/
rgb2hsv: function(r, g, b) {
if (lang.isArray(r)) {
return this.rgb2hsv.call(this, r[0], r[1], r[2]);
}
r=r/255;
g=g/255;
b=b/255;
var min,max,delta,h,s,v;
min = Math.min(Math.min(r,g),b);
max = Math.max(Math.max(r,g),b);
delta = max-min;
switch (max) {
case min: h=0; break;
case r: h=60*(g-b)/delta;
if (g<b) {
h+=360;
}
break;
case g: h=(60*(b-r)/delta)+120; break;
case b: h=(60*(r-g)/delta)+240; break;
}
s = (max === 0) ? 0 : 1-(min/max);
var hsv = [Math.round(h), s, max];
return hsv;
},
/**
* Converts decimal rgb values into a hex string
* 255,255,255 -> FFFFFF
* @method rgb2hex
* @param r {int|[int, int, int]} the red value, or an
* array containing all three parameters
* @param g {int} the green value
* @param b {int} the blue value
* @return {string} the hex string
*/
rgb2hex: function(r, g, b) {
if (lang.isArray(r)) {
return this.rgb2hex.call(this, r[0], r[1], r[2]);
}
var f=this.dec2hex;
return f(r) + f(g) + f(b);
},
/**
* Converts an int 0...255 to hex pair 00...FF
* @method dec2hex
* @param n {int} the number to convert
* @return {string} the hex equivalent
*/
dec2hex: function(n) {
n = parseInt(n, 10);
n = (lang.isNumber(n)) ? n : 0;
n = (n > 255 || n < 0) ? 0 : n;
return HCHARS.charAt((n - n % 16) / 16) + HCHARS.charAt(n % 16);
},
/**
* Converts a hex pair 00...FF to an int 0...255
* @method hex2dec
* @param str {string} the hex pair to convert
* @return {int} the decimal
*/
hex2dec: function(str) {
var f = function(c) {
return HCHARS.indexOf(c.toUpperCase());
};
var s=str.split('');
return ((f(s[0]) * 16) + f(s[1]));
},
/**
* Converts a hex string to rgb
* @method hex2rgb
* @param str {string} the hex string
* @return {[int, int, int]} an array containing the rgb values
*/
hex2rgb: function(s) {
var f = this.hex2dec;
return [f(s.substr(0, 2)), f(s.substr(2, 2)), f(s.substr(4, 2))];
},
/**
* Returns the closest websafe color to the supplied rgb value.
* @method websafe
* @param r {int|[int, int, int]} the red value, or an
* array containing all three parameters
* @param g {int} the green value
* @param b {int} the blue value
* @return {[int, int, int]} an array containing the closes
* websafe rgb colors.
*/
websafe: function(r, g, b) {
if (lang.isArray(r)) {
return this.websafe.call(this, r[0], r[1], r[2]);
}
// returns the closest match [0, 51, 102, 153, 204, 255]
var f = function(v) {
if (lang.isNumber(v)) {
v = Math.min(Math.max(0, v), 255);
var i, next;
for (i=0; i<256; i=i+51) {
next = i+51;
if (v >= i && v <= next) {
return (v-i > 25) ? next : i;
}
}
}
return v;
};
return [f(r), f(g), f(b)];
}
};
}();
(function() {
var _pickercount = 0;
/**
* The colorpicker module provides a widget for selecting colors
* @module colorpicker
* @requires yahoo, dom, event, element, slider
*/
/**
* Creates the host element if it doesn't exist
* @method _createHostElement
* @private
*/
var _createHostElement = function() {
var el = document.createElement('div');
if (this.CSS.BASE) {
el.className = this.CSS.BASE;
}
return el;
};
/**
* A widget to select colors
* @namespace YAHOO.widget
* @class YAHOO.widget.ColorPicker
* @extends YAHOO.util.Element
* @constructor
* @param {HTMLElement | String | Object} el(optional) The html
* element that represents the colorpicker, or the attribute object to use.
* An element will be created if none provided.
* @param {Object} attr (optional) A key map of the colorpicker's
* initial attributes. Ignored if first arg is attributes object.
*/
YAHOO.widget.ColorPicker = function(el, attr) {
_pickercount = _pickercount + 1;
attr = attr || {};
if (arguments.length === 1 && !YAHOO.lang.isString(el) && !el.nodeName) {
attr = el; // treat first arg as attr object
el = attr.element || null;
}
if (!el && !attr.element) { // create if we dont have one
el = _createHostElement.call(this, attr);
}
YAHOO.widget.ColorPicker.superclass.constructor.call(this, el, attr);
};
YAHOO.extend(YAHOO.widget.ColorPicker, YAHOO.util.Element);
var proto = YAHOO.widget.ColorPicker.prototype,
Slider=YAHOO.widget.Slider,
Color=YAHOO.util.Color,
Dom = YAHOO.util.Dom,
Event = YAHOO.util.Event,
lang = YAHOO.lang,
sub = lang.substitute;
var b = "yui-picker";
/**
* The element ids used by this control
* @property ID
* @final
*/
proto.ID = {
/**
* The id for the "red" form field
* @property ID.R
* @type String
* @final
* @default yui-picker-r
*/
R: b + "-r",
/**
* The id for the "red" hex pair output
* @property ID.R_HEX
* @type String
* @final
* @default yui-picker-rhex
*/
R_HEX: b + "-rhex",
/**
* The id for the "green" form field
* @property ID.G
* @type String
* @final
* @default yui-picker-g
*/
G: b + "-g",
/**
* The id for the "green" hex pair output
* @property ID.G_HEX
* @type String
* @final
* @default yui-picker-ghex
*/
G_HEX: b + "-ghex",
/**
* The id for the "blue" form field
* @property ID.B
* @type String
* @final
* @default yui-picker-b
*/
B: b + "-b",
/**
* The id for the "blue" hex pair output
* @property ID.B_HEX
* @type String
* @final
* @default yui-picker-bhex
*/
B_HEX: b + "-bhex",
/**
* The id for the "hue" form field
* @property ID.H
* @type String
* @final
* @default yui-picker-h
*/
H: b + "-h",
/**
* The id for the "saturation" form field
* @property ID.S
* @type String
* @final
* @default yui-picker-s
*/
S: b + "-s",
/**
* The id for the "value" form field
* @property ID.V
* @type String
* @final
* @default yui-picker-v
*/
V: b + "-v",
/**
* The id for the picker region slider
* @property ID.PICKER_BG
* @type String
* @final
* @default yui-picker-bg
*/
PICKER_BG: b + "-bg",
/**
* The id for the picker region thumb
* @property ID.PICKER_THUMB
* @type String
* @final
* @default yui-picker-thumb
*/
PICKER_THUMB: b + "-thumb",
/**
* The id for the hue slider
* @property ID.HUE_BG
* @type String
* @final
* @default yui-picker-hue-bg
*/
HUE_BG: b + "-hue-bg",
/**
* The id for the hue thumb
* @property ID.HUE_THUMB
* @type String
* @final
* @default yui-picker-hue-thumb
*/
HUE_THUMB: b + "-hue-thumb",
/**
* The id for the hex value form field
* @property ID.HEX
* @type String
* @final
* @default yui-picker-hex
*/
HEX: b + "-hex",
/**
* The id for the color swatch
* @property ID.SWATCH
* @type String
* @final
* @default yui-picker-swatch
*/
SWATCH: b + "-swatch",
/**
* The id for the websafe color swatch
* @property ID.WEBSAFE_SWATCH
* @type String
* @final
* @default yui-picker-websafe-swatch
*/
WEBSAFE_SWATCH: b + "-websafe-swatch",
/**
* The id for the control details
* @property ID.CONTROLS
* @final
* @default yui-picker-controls
*/
CONTROLS: b + "-controls",
/**
* The id for the rgb controls
* @property ID.RGB_CONTROLS
* @final
* @default yui-picker-rgb-controls
*/
RGB_CONTROLS: b + "-rgb-controls",
/**
* The id for the hsv controls
* @property ID.HSV_CONTROLS
* @final
* @default yui-picker-hsv-controls
*/
HSV_CONTROLS: b + "-hsv-controls",
/**
* The id for the hsv controls
* @property ID.HEX_CONTROLS
* @final
* @default yui-picker-hex-controls
*/
HEX_CONTROLS: b + "-hex-controls",
/**
* The id for the hex summary
* @property ID.HEX_SUMMARY
* @final
* @default yui-picker-hex-summary
*/
HEX_SUMMARY: b + "-hex-summary",
/**
* The id for the controls section header
* @property ID.CONTROLS_LABEL
* @final
* @default yui-picker-controls-label
*/
CONTROLS_LABEL: b + "-controls-label"
};
/**
* Constants for any script-generated messages. The values here
* are the default messages. They can be updated by providing
* the complete list to the constructor for the "txt" attribute.
* @property TXT
* @final
*/
proto.TXT = {
ILLEGAL_HEX: "Illegal hex value entered",
SHOW_CONTROLS: "Show color details",
HIDE_CONTROLS: "Hide color details",
CURRENT_COLOR: "Currently selected color: {rgb}",
CLOSEST_WEBSAFE: "Closest websafe color: {rgb}. Click to select.",
R: "R",
G: "G",
B: "B",
H: "H",
S: "S",
V: "V",
HEX: "#",
DEG: "\u00B0",
PERCENT: "%"
};
/**
* Constants for the default image locations for img tags that are
* generated by the control. They can be modified by passing the
* complete list to the contructor for the "images" attribute
* @property IMAGE
* @final
*/
proto.IMAGE = {
PICKER_THUMB: "../../build/colorpicker/assets/picker_thumb.png",
HUE_THUMB: "../../build/colorpicker/assets/hue_thumb.png"
};
/*
* Constants for the control's custom event names. subscribe
* to the rgbChange event instead.
* @property EVENT
* @final
*/
//proto.EVENT = {
//CHANGE: "change"
//};
//proto.CSS = { };
/**
* Constants for the control's default default values
* @property DEFAULT
* @final
*/
proto.DEFAULT = {
PICKER_SIZE: 180
};
/**
* Constants for the control's configuration attributes
* @property OPT
* @final
*/
proto.OPT = {
HUE: "hue",
SATURATION: "saturation",
VALUE: "value",
RED: "red",
GREEN: "green",
BLUE: "blue",
HSV: "hsv",
RGB: "rgb",
WEBSAFE: "websafe",
HEX: "hex",
PICKER_SIZE: "pickersize",
SHOW_CONTROLS: "showcontrols",
SHOW_RGB_CONTROLS: "showrgbcontrols",
SHOW_HSV_CONTROLS: "showhsvcontrols",
SHOW_HEX_CONTROLS: "showhexcontrols",
SHOW_HEX_SUMMARY: "showhexsummary",
SHOW_WEBSAFE: "showwebsafe",
//SHOW_SUBMIT: "showsubmit",
CONTAINER: "container",
IDS: "ids",
ELEMENTS: "elements",
TXT: "txt",
IMAGES: "images",
ANIMATE: "animate"
};
/**
* Moves the hue slider into the position dictated by the current state
* of the control
* @method _updateHueSlider
* @private
*/
var _updateHueSlider = function() {
var size = this.get(this.OPT.PICKER_SIZE),
h = this.get(this.OPT.HUE);
h = size - Math.round(h / 360 * size);
// 0 is at the top and bottom of the hue slider. Always go to
// the top so we don't end up sending the thumb to the bottom
// when the value didn't actually change (e.g., a conversion
// produced 360 instead of 0 and the value was already 0).
if (h === size) {
h = 0;
}
this.hueSlider.setValue(h);
};
/**
* Moves the picker slider into the position dictated by the current state
* of the control
* @method _updatePickerSlider
* @private
*/
var _updatePickerSlider = function() {
var size = this.get(this.OPT.PICKER_SIZE),
s = this.get(this.OPT.SATURATION),
v = this.get(this.OPT.VALUE);
s = Math.round(s * size / 100);
v = Math.round(size - (v * size / 100));
this.pickerSlider.setRegionValue(s, v);
};
/**
* Moves the sliders into the position dictated by the current state
* of the control
* @method _updateSliders
* @private
*/
var _updateSliders = function() {
_updateHueSlider.call(this);
_updatePickerSlider.call(this);
};
/**
* Sets the control to the specified rgb value and
* moves the sliders to the proper positions
* @method setValue
* @param rgb {[int, int, int]} the rgb value
* @param silent {boolean} whether or not to fire the change event
*/
proto.setValue = function(rgb, silent) {
silent = (silent) || false;
this.set(this.OPT.RGB, rgb, silent);
_updateSliders.call(this);
};
/**
* The hue slider
* @property hueSlider
* @type YAHOO.widget.Slider
*/
proto.hueSlider = null;
/**
* The picker region
* @property pickerSlider
* @type YAHOO.widget.Slider
*/
proto.pickerSlider = null;
/**
* Translates the slider value into hue, int[0,359]
* @method _getH
* @private
* @return {int} the hue from 0 to 359
*/
var _getH = function() {
var size = this.get(this.OPT.PICKER_SIZE),
h = (size - this.hueSlider.getValue()) / size;
h = Math.round(h*360);
return (h === 360) ? 0 : h;
};
/**
* Translates the slider value into saturation, int[0,1], left to right
* @method _getS
* @private
* @return {int} the saturation from 0 to 1
*/
var _getS = function() {
return this.pickerSlider.getXValue() / this.get(this.OPT.PICKER_SIZE);
};
/**
* Translates the slider value into value/brightness, int[0,1], top
* to bottom
* @method _getV
* @private
* @return {int} the value from 0 to 1
*/
var _getV = function() {
var size = this.get(this.OPT.PICKER_SIZE);
return (size - this.pickerSlider.getYValue()) / size;
};
/**
* Updates the background of the swatch with the current rbg value.
* Also updates the websafe swatch to the closest websafe color
* @method _updateSwatch
* @private
*/
var _updateSwatch = function() {
var rgb = this.get(this.OPT.RGB),
websafe = this.get(this.OPT.WEBSAFE),
el = this.getElement(this.ID.SWATCH),
color = rgb.join(","),
txt = this.get(this.OPT.TXT);
Dom.setStyle(el, "background-color", "rgb(" + color + ")");
el.title = lang.substitute(txt.CURRENT_COLOR, {
"rgb": "#" + this.get(this.OPT.HEX)
});
el = this.getElement(this.ID.WEBSAFE_SWATCH);
color = websafe.join(",");
Dom.setStyle(el, "background-color", "rgb(" + color + ")");
el.title = lang.substitute(txt.CLOSEST_WEBSAFE, {
"rgb": "#" + Color.rgb2hex(websafe)
});
};
/**
* Reads the sliders and converts the values to RGB, updating the
* internal state for all the individual form fields
* @method _getValuesFromSliders
* @private
*/
var _getValuesFromSliders = function() {
var h=_getH.call(this), s=_getS.call(this), v=_getV.call(this);
var rgb = Color.hsv2rgb(h, s, v);
//var websafe = Color.websafe(rgb);
//var hex = Color.rgb2hex(rgb[0], rgb[1], rgb[2]);
this.set(this.OPT.RGB, rgb);
};
/**
* Updates the form field controls with the state data contained
* in the control.
* @method _updateFormFields
* @private
*/
var _updateFormFields = function() {
this.getElement(this.ID.H).value = this.get(this.OPT.HUE);
this.getElement(this.ID.S).value = this.get(this.OPT.SATURATION);
this.getElement(this.ID.V).value = this.get(this.OPT.VALUE);
this.getElement(this.ID.R).value = this.get(this.OPT.RED);
this.getElement(this.ID.R_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.RED));
this.getElement(this.ID.G).value = this.get(this.OPT.GREEN);
this.getElement(this.ID.G_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.GREEN));
this.getElement(this.ID.B).value = this.get(this.OPT.BLUE);
this.getElement(this.ID.B_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.BLUE));
this.getElement(this.ID.HEX).value = this.get(this.OPT.HEX);
};
/**
* Event handler for the hue slider.
* @method _onHueSliderChange
* @param newOffset {int} pixels from the start position
* @private
*/
var _onHueSliderChange = function(newOffset) {
var h = _getH.call(this);
this.set(this.OPT.HUE, h, true);
// set picker background to the hue
var rgb = Color.hsv2rgb(h, 1, 1);
var styleDef = "rgb(" + rgb.join(",") + ")";
Dom.setStyle(this.getElement(this.ID.PICKER_BG), "background-color", styleDef);
if (this.hueSlider.valueChangeSource === this.hueSlider.SOURCE_UI_EVENT) {
_getValuesFromSliders.call(this);
}
_updateFormFields.call(this);
_updateSwatch.call(this);
};
/**
* Event handler for the picker slider, which controls the
* saturation and value/brightness.
* @method _onPickerSliderChange
* @param newOffset {{x: int, y: int}} x/y pixels from the start position
* @private
*/
var _onPickerSliderChange = function(newOffset) {
var s=_getS.call(this), v=_getV.call(this);
this.set(this.OPT.SATURATION, Math.round(s*100), true);
this.set(this.OPT.VALUE, Math.round(v*100), true);
if (this.pickerSlider.valueChangeSource === this.pickerSlider.SOURCE_UI_EVENT) {
_getValuesFromSliders.call(this);
}
_updateFormFields.call(this);
_updateSwatch.call(this);
};
/**
* Key map to well-known commands for txt field input
* @method _getCommand
* @param e {Event} the keypress or keydown event
* @return {int} a command code
* <ul>
* <li>0 = not a number, letter in range, or special key</li>
* <li>1 = number</li>
* <li>2 = a-fA-F</li>
* <li>3 = increment (up arrow)</li>
* <li>4 = decrement (down arrow)</li>
* <li>5 = special key (tab, delete, return, escape, left, right)</li>
* <li>6 = return</li>
* </ul>
* @private
*/
var _getCommand = function(e) {
var c = Event.getCharCode(e);
//alert(Event.getCharCode(e) + ", " + e.keyCode + ", " + e.charCode);
// special keys
if (c === 38) { // up arrow
return 3;
} else if (c === 13) { // return
return 6;
} else if (c === 40) { // down array
return 4;
} else if (c >= 48 && c<=57) { // 0-9
return 1;
} else if (c >= 97 && c<=102) { // a-f
return 2;
} else if (c >= 65 && c<=70) { // A-F
return 2;
//} else if ("8, 9, 13, 27, 37, 39".indexOf(c) > -1 ||
// (c >= 112 && c <=123)) { // including F-keys
// tab, delete, return, escape, left, right
} else if ("8, 9, 13, 27, 37, 39".indexOf(c) > -1) { // special chars
return 5;
} else { // something we probably don't want
return 0;
}
};
/**
* Use the value of the text field to update the control
* @method _hexFieldKeypress
* @param e {Event} an event
* @param el {HTMLElement} the field
* @param prop {string} the key to the linked property
* @private
*/
var _useFieldValue = function(e, el, prop) {
var val = el.value;
if (prop !== this.OPT.HEX) {
val = parseInt(val, 10);
}
if (val !== this.get(prop)) {
this.set(prop, val);
}
};
/**
* Handle keypress on one of the rgb or hsv fields.
* @method _rgbFieldKeypress
* @param e {Event} the keypress event
* @param el {HTMLElement} the field
* @param prop {string} the key to the linked property
* @private
*/
var _rgbFieldKeypress = function(e, el, prop) {
var command = _getCommand(e);
var inc = (e.shiftKey) ? 10 : 1;
switch (command) {
case 6: // return, update the value
_useFieldValue.apply(this, arguments);
break;
case 3: // up arrow, increment
this.set(prop, Math.min(this.get(prop)+inc, 255));
_updateFormFields.call(this);
//Event.stopEvent(e);
break;
case 4: // down arrow, decrement
this.set(prop, Math.max(this.get(prop)-inc, 0));
_updateFormFields.call(this);
//Event.stopEvent(e);
break;
default:
}
};
/**
* Handle keydown on the hex field
* @method _hexFieldKeypress
* @param e {Event} the keypress event
* @param el {HTMLElement} the field
* @param prop {string} the key to the linked property
* @private
*/
var _hexFieldKeypress = function(e, el, prop) {
var command = _getCommand(e);
if (command === 6) { // return, update the value
_useFieldValue.apply(this, arguments);
}
};
/**
* Allows numbers and special chars, and by default allows a-f.
* Used for the hex field keypress handler.
* @method _hexOnly
* @param e {Event} the event
* @param numbersOnly omits a-f if set to true
* @private
* @return {boolean} false if we are canceling the event
*/
var _hexOnly = function(e, numbersOnly) {
var command = _getCommand(e);
switch (command) {
case 6: // return
case 5: // special char
case 1: // number
break;
case 2: // hex char (a-f)
if (numbersOnly !== true) {
break;
}
// fallthrough is intentional
default: // prevent alpha and punctuation
Event.stopEvent(e);
return false;
}
};
/**
* Allows numbers and special chars only. Used for the
* rgb and hsv fields keypress handler.
* @method _numbersOnly
* @param e {Event} the event
* @private
* @return {boolean} false if we are canceling the event
*/
var _numbersOnly = function(e) {
return _hexOnly(e, true);
};
/**
* Returns the element reference that is saved. The id can be either
* the element id, or the key for this id in the "id" config attribute.
* For instance, the host element id can be obtained by passing its
* id (default: "yui_picker") or by its key "YUI_PICKER".
* @param id {string} the element id, or key
* @return {HTMLElement} a reference to the element
*/
proto.getElement = function(id) {
return this.get(this.OPT.ELEMENTS)[this.get(this.OPT.IDS)[id]];
};
var _createElements = function() {
var el, child, img, fld, i,
ids = this.get(this.OPT.IDS),
txt = this.get(this.OPT.TXT),
images = this.get(this.OPT.IMAGES),
Elem = function(type, o) {
var n = document.createElement(type);
if (o) {
lang.augmentObject(n, o, true);
}
return n;
},
RGBElem = function(type, obj) {
var o = lang.merge({
//type: "txt",
autocomplete: "off",
value: "0",
size: 3,
maxlength: 3
}, obj);
o.name = o.id;
return new Elem(type, o);
};
var p = this.get("element");
// Picker slider (S and V) ---------------------------------------------
el = new Elem("div", {
id: ids[this.ID.PICKER_BG],
className: "yui-picker-bg",
tabIndex: -1,
hideFocus: true
});
child = new Elem("div", {
id: ids[this.ID.PICKER_THUMB],
className: "yui-picker-thumb"
});
img = new Elem("img", {
src: images.PICKER_THUMB
});
child.appendChild(img);
el.appendChild(child);
p.appendChild(el);
// Hue slider ---------------------------------------------
el = new Elem("div", {
id: ids[this.ID.HUE_BG],
className: "yui-picker-hue-bg",
tabIndex: -1,
hideFocus: true
});
child = new Elem("div", {
id: ids[this.ID.HUE_THUMB],
className: "yui-picker-hue-thumb"
});
img = new Elem("img", {
src: images.HUE_THUMB
});
child.appendChild(img);
el.appendChild(child);
p.appendChild(el);
// controls ---------------------------------------------
el = new Elem("div", {
id: ids[this.ID.CONTROLS],
className: "yui-picker-controls"
});
p.appendChild(el);
p = el;
// controls header
el = new Elem("div", {
className: "hd"
});
child = new Elem("a", {
id: ids[this.ID.CONTROLS_LABEL],
//className: "yui-picker-controls-label",
href: "#"
});
el.appendChild(child);
p.appendChild(el);
// bd
el = new Elem("div", {
className: "bd"
});
p.appendChild(el);
p = el;
// rgb
el = new Elem("ul", {
id: ids[this.ID.RGB_CONTROLS],
className: "yui-picker-rgb-controls"
});
child = new Elem("li");
child.appendChild(document.createTextNode(txt.R + " "));
fld = new RGBElem("input", {
id: ids[this.ID.R],
className: "yui-picker-r"
});
child.appendChild(fld);
el.appendChild(child);
child = new Elem("li");
child.appendChild(document.createTextNode(txt.G + " "));
fld = new RGBElem("input", {
id: ids[this.ID.G],
className: "yui-picker-g"
});
child.appendChild(fld);
el.appendChild(child);
child = new Elem("li");
child.appendChild(document.createTextNode(txt.B + " "));
fld = new RGBElem("input", {
id: ids[this.ID.B],
className: "yui-picker-b"
});
child.appendChild(fld);
el.appendChild(child);
p.appendChild(el);
// hsv
el = new Elem("ul", {
id: ids[this.ID.HSV_CONTROLS],
className: "yui-picker-hsv-controls"
});
child = new Elem("li");
child.appendChild(document.createTextNode(txt.H + " "));
fld = new RGBElem("input", {
id: ids[this.ID.H],
className: "yui-picker-h"
});
child.appendChild(fld);
child.appendChild(document.createTextNode(" " + txt.DEG));
el.appendChild(child);
child = new Elem("li");
child.appendChild(document.createTextNode(txt.S + " "));
fld = new RGBElem("input", {
id: ids[this.ID.S],
className: "yui-picker-s"
});
child.appendChild(fld);
child.appendChild(document.createTextNode(" " + txt.PERCENT));
el.appendChild(child);
child = new Elem("li");
child.appendChild(document.createTextNode(txt.V + " "));
fld = new RGBElem("input", {
id: ids[this.ID.V],
className: "yui-picker-v"
});
child.appendChild(fld);
child.appendChild(document.createTextNode(" " + txt.PERCENT));
el.appendChild(child);
p.appendChild(el);
// hex summary
el = new Elem("ul", {
id: ids[this.ID.HEX_SUMMARY],
className: "yui-picker-hex_summary"
});
child = new Elem("li", {
id: ids[this.ID.R_HEX]
});
el.appendChild(child);
child = new Elem("li", {
id: ids[this.ID.G_HEX]
});
el.appendChild(child);
child = new Elem("li", {
id: ids[this.ID.B_HEX]
});
el.appendChild(child);
p.appendChild(el);
// hex field
el = new Elem("div", {
id: ids[this.ID.HEX_CONTROLS],
className: "yui-picker-hex-controls"
});
el.appendChild(document.createTextNode(txt.HEX + " "));
child = new RGBElem("input", {
id: ids[this.ID.HEX],
className: "yui-picker-hex",
size: 6,
maxlength: 6
});
el.appendChild(child);
p.appendChild(el);
p = this.get("element");
// swatch
el = new Elem("div", {
id: ids[this.ID.SWATCH],
className: "yui-picker-swatch"
});
p.appendChild(el);
// websafe swatch
el = new Elem("div", {
id: ids[this.ID.WEBSAFE_SWATCH],
className: "yui-picker-websafe-swatch"
});
p.appendChild(el);
};
var _attachRGBHSV = function(id, config) {
Event.on(this.getElement(id), "keydown", function(e, me) {
_rgbFieldKeypress.call(me, e, this, config);
}, this);
Event.on(this.getElement(id), "keypress", _numbersOnly, this);
Event.on(this.getElement(id), "blur", function(e, me) {
_useFieldValue.call(me, e, this, config);
}, this);
};
/**
* Updates the rgb attribute with the current state of the r,g,b
* fields. This is invoked from change listeners on these
* attributes to facilitate updating these values from the
* individual form fields
* @method _updateRGB
* @private
*/
var _updateRGB = function() {
var rgb = [this.get(this.OPT.RED),
this.get(this.OPT.GREEN),
this.get(this.OPT.BLUE)];
this.set(this.OPT.RGB, rgb);
_updateSliders.call(this);
};
/**
* Sets the initial state of the sliders
* @method initPicker
*/
proto.initPicker = function () {
// bind all of our elements
var o=this.OPT,
ids = this.get(o.IDS),
els = this.get(o.ELEMENTS),
i, el, id;
// Add the default value as a key for each element for easier lookup
for (i in this.ID) {
if (lang.hasOwnProperty(this.ID, i)) {
ids[this.ID[i]] = ids[i];
}
}
// Check for picker element, if not there, create all of them
el = Dom.get(ids[this.ID.PICKER_BG]);
if (!el) {
_createElements.call(this);
} else {
}
for (i in ids) {
if (lang.hasOwnProperty(ids, i)) {
// look for element
el = Dom.get(ids[i]);
// generate an id if the implementer passed in an element reference,
// and the element did not have an id already
id = Dom.generateId(el);
// update the id in case we generated the id
ids[i] = id; // key is WEBSAFE_SWATCH
ids[ids[i]] = id; // key is websafe_swatch
// store the dom ref
els[id] = el;
}
}
// set the initial visibility state of our controls
els = [o.SHOW_CONTROLS,
o.SHOW_RGB_CONTROLS,
o.SHOW_HSV_CONTROLS,
o.SHOW_HEX_CONTROLS,
o.SHOW_HEX_SUMMARY,
o.SHOW_WEBSAFE
];
for (i=0; i<els.length; i=i+1) {
this.set(els[i], this.get(els[i]));
}
var s = this.get(o.PICKER_SIZE);
this.hueSlider = Slider.getVertSlider(this.getElement(this.ID.HUE_BG),
this.getElement(this.ID.HUE_THUMB), 0, s);
this.hueSlider.subscribe("change", _onHueSliderChange, this, true);
this.pickerSlider = Slider.getSliderRegion(this.getElement(this.ID.PICKER_BG),
this.getElement(this.ID.PICKER_THUMB), 0, s, 0, s);
this.pickerSlider.subscribe("change", _onPickerSliderChange, this, true);
// Set the animate state
this.set(o.ANIMATE,this.get(o.ANIMATE));
//_onHueSliderChange.call(this, 0);
Event.on(this.getElement(this.ID.WEBSAFE_SWATCH), "click", function(e) {
this.setValue(this.get(o.WEBSAFE));
//_updateSliders
}, this, true);
Event.on(this.getElement(this.ID.CONTROLS_LABEL), "click", function(e) {
this.set(o.SHOW_CONTROLS, !this.get(o.SHOW_CONTROLS));
Event.preventDefault(e);
}, this, true);
_attachRGBHSV.call(this, this.ID.R, this.OPT.RED);
_attachRGBHSV.call(this, this.ID.G, this.OPT.GREEN);
_attachRGBHSV.call(this, this.ID.B, this.OPT.BLUE);
_attachRGBHSV.call(this, this.ID.H, this.OPT.HUE);
_attachRGBHSV.call(this, this.ID.S, this.OPT.SATURATION);
_attachRGBHSV.call(this, this.ID.V, this.OPT.VALUE);
Event.on(this.getElement(this.ID.HEX), "keydown", function(e, me) {
_hexFieldKeypress.call(me, e, this, me.OPT.HEX);
}, this);
Event.on(this.getElement(this.ID.HEX), "keypress", _hexOnly, this);
Event.on(this.getElement(this.ID.HEX), "blur", function(e, me) {
_useFieldValue.call(me, e, this, me.OPT.HEX);
}, this);
_updateRGB.call(this);
};
/**
* Updates the RGB values from the current state of the HSV
* values. Executed when the one of the HSV form fields are
* updated
* _updateRGBFromHSV
* @private
*/
var _updateRGBFromHSV = function() {
var hsv = [this.get(this.OPT.HUE),
this.get(this.OPT.SATURATION)/100,
this.get(this.OPT.VALUE)/100];
var rgb = Color.hsv2rgb(hsv);
this.set(this.OPT.RGB, rgb);
_updateSliders.call(this);
};
/**
* Parses the hex string to normalize shorthand values, converts
* the hex value to rgb and updates the rgb attribute (which
* updates the state for all of the other values)
* method _updateHex
* @private
*/
var _updateHex = function() {
var hex = this.get(this.OPT.HEX), l=hex.length;
// support #369 -> #336699 shorthand
if (l === 3) {
var c = hex.split(""), i;
for (i=0; i<l; i=i+1) {
c[i] = c[i] + c[i];
}
hex = c.join("");
}
if (hex.length !== 6) {
return false;
}
var rgb = Color.hex2rgb(hex);
this.setValue(rgb);
//_updateSliders.call(this);
};
/**
* Sets up the config attributes and the change listeners for this
* properties
* @method initAttributes
* @param attr An object containing default attribute values
*/
proto.initAttributes = function(attr) {
attr = attr || {};
YAHOO.widget.ColorPicker.superclass.initAttributes.call(this, attr);
/**
* The size of the picker. Trying to change this is not recommended.
* @attribute pickersize
* @default 180
* @type int
*/
this.setAttributeConfig(this.OPT.PICKER_SIZE, {
value: attr.size || this.DEFAULT.PICKER_SIZE
});
/**
* The current hue value 0-360
* @attribute hue
* @type int
*/
this.setAttributeConfig(this.OPT.HUE, {
value: attr.hue || 0,
validator: lang.isNumber
});
/**
* The current saturation value 0-100
* @attribute saturation
* @type int
*/
this.setAttributeConfig(this.OPT.SATURATION, {
value: attr.saturation || 0,
validator: lang.isNumber
});
/**
* The current value/brightness value 0-100
* @attribute value
* @type int
*/
this.setAttributeConfig(this.OPT.VALUE, {
value: lang.isNumber(attr.value) ? attr.value : 100,
validator: lang.isNumber
});
/**
* The current red value 0-255
* @attribute red
* @type int
*/
this.setAttributeConfig(this.OPT.RED, {
value: lang.isNumber(attr.red) ? attr.red : 255,
validator: lang.isNumber
});
/**
* The current green value 0-255
* @attribute green
* @type int
*/
this.setAttributeConfig(this.OPT.GREEN, {
value: lang.isNumber(attr.green) ? attr.green : 255,
validator: lang.isNumber
});
/**
* The current blue value 0-255
* @attribute blue
* @type int
*/
this.setAttributeConfig(this.OPT.BLUE, {
value: lang.isNumber(attr.blue) ? attr.blue : 255,
validator: lang.isNumber
});
/**
* The current hex value #000000-#FFFFFF, without the #
* @attribute hex
* @type string
*/
this.setAttributeConfig(this.OPT.HEX, {
value: attr.hex || "FFFFFF",
validator: lang.isString
});
/**
* The current rgb value. Updates the state of all of the
* other value fields. Read-only: use setValue to set the
* controls rgb value.
* @attribute hex
* @type [int, int, int]
* @readonly
*/
this.setAttributeConfig(this.OPT.RGB, {
value: attr.rgb || [255,255,255],
method: function(rgb) {
this.set(this.OPT.RED, rgb[0], true);
this.set(this.OPT.GREEN, rgb[1], true);
this.set(this.OPT.BLUE, rgb[2], true);
var websafe = Color.websafe(rgb);
this.set(this.OPT.WEBSAFE, websafe, true);
var hex = Color.rgb2hex(rgb);
this.set(this.OPT.HEX, hex, true);
var hsv = Color.rgb2hsv(rgb);
this.set(this.OPT.HUE, hsv[0], true);
this.set(this.OPT.SATURATION, Math.round(hsv[1]*100), true);
this.set(this.OPT.VALUE, Math.round(hsv[2]*100), true);
},
readonly: true
});
/**
* If the color picker will live inside of a container object,
* set, provide a reference to it so the control can use the
* container's events.
* @attribute container
* @type YAHOO.widget.Panel
*/
this.setAttributeConfig(this.OPT.CONTAINER, {
value: null,
method: function(container) {
if (container) {
// Position can get out of sync when the
// control is manipulated while display is
// none. Resetting the slider constraints
// when it is visible gets the state back in
// order.
container.showEvent.subscribe(function() {
// this.pickerSlider.thumb.resetConstraints();
// this.hueSlider.thumb.resetConstraints();
this.pickerSlider.focus();
}, this, true);
}
}
});
/**
* The closest current websafe value
* @attribute websafe
* @type int
*/
this.setAttributeConfig(this.OPT.WEBSAFE, {
value: attr.websafe || [255,255,255]
});
var ids = attr.ids || lang.merge({}, this.ID);
if (!attr.ids && _pickercount > 1) {
for (var i in ids) {
if (lang.hasOwnProperty(ids, i)) {
ids[i] = ids[i] + _pickercount;
}
}
}
/**
* A list of element ids and/or element references used by the
* control. The default is the this.ID list, and can be customized
* by passing a list in the contructor
* @attribute ids
* @type {referenceid: realid}
* @writeonce
*/
this.setAttributeConfig(this.OPT.IDS, {
value: ids,
writeonce: true
});
/**
* A list of txt strings for internationalization. Default
* is this.TXT
* @attribute txt
* @type {key: txt}
* @writeonce
*/
this.setAttributeConfig(this.OPT.TXT, {
value: attr.txt || this.TXT,
writeonce: true
});
/**
* The img src default list
* is this.IMAGES
* @attribute images
* @type {key: image}
* @writeonce
*/
this.setAttributeConfig(this.OPT.IMAGES, {
value: attr.images || this.IMAGE,
writeonce: true
});
/**
* The element refs used by this control. Set at initialization
* @attribute elements
* @type {id: HTMLElement}
* @readonly
*/
this.setAttributeConfig(this.OPT.ELEMENTS, {
value: {},
readonly: true
});
/**
* Returns the cached element reference. If the id is not a string, it
* is assumed that it is an element and this is returned.
* @param id {string|HTMLElement} the element key, id, or ref
* @param on {boolean} hide or show. If true, show
* @private */
var _hideShowEl = function(id, on) {
var el = (lang.isString(id) ? this.getElement(id) : id);
//Dom.setStyle(id, "visibility", (on) ? "" : "hidden");
Dom.setStyle(el, "display", (on) ? "" : "none");
};
/**
* Hide/show the entire set of controls
* @attribute showcontrols
* @type boolean
* @default true
*/
this.setAttributeConfig(this.OPT.SHOW_CONTROLS, {
value: lang.isBoolean(attr.showcontrols) ? attr.showcontrols : true,
method: function(on) {
var el = Dom.getElementsByClassName("bd", "div",
this.getElement(this.ID.CONTROLS))[0];
_hideShowEl.call(this, el, on);
this.getElement(this.ID.CONTROLS_LABEL).innerHTML =
(on) ? this.get(this.OPT.TXT).HIDE_CONTROLS :
this.get(this.OPT.TXT).SHOW_CONTROLS;
}
});
/**
* Hide/show the rgb controls
* @attribute showrgbcontrols
* @type boolean
* @default true
*/
this.setAttributeConfig(this.OPT.SHOW_RGB_CONTROLS, {
value: lang.isBoolean(attr.showrgbcontrols) ? attr.showrgbcontrols : true,
method: function(on) {
//Dom.setStyle(this.getElement(this.ID.RBG_CONTROLS), "visibility", (on) ? "" : "hidden");
_hideShowEl.call(this, this.ID.RGB_CONTROLS, on);
}
});
/**
* Hide/show the hsv controls
* @attribute showhsvcontrols
* @type boolean
* @default false
*/
this.setAttributeConfig(this.OPT.SHOW_HSV_CONTROLS, {
value: lang.isBoolean(attr.showhsvcontrols) ?
attr.showhsvcontrols : false,
method: function(on) {
//Dom.setStyle(this.getElement(this.ID.HSV_CONTROLS), "visibility", (on) ? "" : "hidden");
_hideShowEl.call(this, this.ID.HSV_CONTROLS, on);
// can't show both the hsv controls and the rbg hex summary
if (on && this.get(this.OPT.SHOW_HEX_SUMMARY)) {
this.set(this.OPT.SHOW_HEX_SUMMARY, false);
}
}
});
/**
* Hide/show the hex controls
* @attribute showhexcontrols
* @type boolean
* @default true
*/
this.setAttributeConfig(this.OPT.SHOW_HEX_CONTROLS, {
value: lang.isBoolean(attr.showhexcontrols) ?
attr.showhexcontrols : false,
method: function(on) {
_hideShowEl.call(this, this.ID.HEX_CONTROLS, on);
}
});
/**
* Hide/show the websafe swatch
* @attribute showwebsafe
* @type boolean
* @default true
*/
this.setAttributeConfig(this.OPT.SHOW_WEBSAFE, {
value: lang.isBoolean(attr.showwebsafe) ? attr.showwebsafe : true,
method: function(on) {
_hideShowEl.call(this, this.ID.WEBSAFE_SWATCH, on);
}
});
/**
* Hide/show the hex summary
* @attribute showhexsummary
* @type boolean
* @default true
*/
this.setAttributeConfig(this.OPT.SHOW_HEX_SUMMARY, {
value: lang.isBoolean(attr.showhexsummary) ? attr.showhexsummary : true,
method: function(on) {
_hideShowEl.call(this, this.ID.HEX_SUMMARY, on);
// can't show both the hsv controls and the rbg hex summary
if (on && this.get(this.OPT.SHOW_HSV_CONTROLS)) {
this.set(this.OPT.SHOW_HSV_CONTROLS, false);
}
}
});
this.setAttributeConfig(this.OPT.ANIMATE, {
value: lang.isBoolean(attr.animate) ? attr.animate : true,
method: function(on) {
this.pickerSlider.animate = on;
this.hueSlider.animate = on;
}
});
this.on(this.OPT.HUE + "Change", _updateRGBFromHSV, this, true);
this.on(this.OPT.SATURATION + "Change", _updateRGBFromHSV, this, true);
this.on(this.OPT.VALUE + "Change", _updatePickerSlider, this, true);
this.on(this.OPT.RED + "Change", _updateRGB, this, true);
this.on(this.OPT.GREEN + "Change", _updateRGB, this, true);
this.on(this.OPT.BLUE + "Change", _updateRGB, this, true);
this.on(this.OPT.HEX + "Change", _updateHex, this, true);
this.initPicker();
};
})();
YAHOO.register("colorpicker", YAHOO.widget.ColorPicker, {version: "2.6.0", build: "1321"});