var Color = (function(window){
var Events = {
RGB_UPDATED : 'RGBUpdated',
HSL_UPDATED : 'HSLUpdated',
HSV_UPDATED : 'HSVUpdated',
HEX_UPDATED : 'HexUpdated',
INT_UPDATED : 'IntUpdated',
UPDATED : 'updated'
};
var namedColors = {
'transparent':'rgba(0, 0, 0, 0)','aliceblue':'#F0F8FF','antiquewhite':'#FAEBD7','aqua':'#00FFFF','aquamarine':'#7FFFD4',
'azure':'#F0FFFF','beige':'#F5F5DC','bisque':'#FFE4C4','black':'#000000','blanchedalmond':'#FFEBCD','blue':'#0000FF','blueviolet':'#8A2BE2',
'brown':'#A52A2A','burlywood':'#DEB887','cadetblue':'#5F9EA0','chartreuse':'#7FFF00','chocolate':'#D2691E','coral':'#FF7F50',
'cornflowerblue':'#6495ED','cornsilk':'#FFF8DC','crimson':'#DC143C','cyan':'#00FFFF','darkblue':'#00008B','darkcyan':'#008B8B','darkgoldenrod':'#B8860B',
'darkgray':'#A9A9A9','darkgrey':'#A9A9A9','darkgreen':'#006400','darkkhaki':'#BDB76B','darkmagenta':'#8B008B','darkolivegreen':'#556B2F',
'darkorange':'#FF8C00','darkorchid':'#9932CC','darkred':'#8B0000','darksalmon':'#E9967A','darkseagreen':'#8FBC8F','darkslateblue':'#483D8B',
'darkslategray':'#2F4F4F','darkslategrey':'#2F4F4F','darkturquoise':'#00CED1','darkviolet':'#9400D3','deeppink':'#FF1493','deepskyblue':'#00BFFF',
'dimgray':'#696969','dimgrey':'#696969','dodgerblue':'#1E90FF','firebrick':'#B22222','floralwhite':'#FFFAF0','forestgreen':'#228B22',
'fuchsia':'#FF00FF','gainsboro':'#DCDCDC','ghostwhite':'#F8F8FF','gold':'#FFD700','goldenrod':'#DAA520','gray':'#808080','grey':'#808080',
'green':'#008000','greenyellow':'#ADFF2F','honeydew':'#F0FFF0','hotpink':'#FF69B4','indianred':'#CD5C5C','indigo':'#4B0082','ivory':'#FFFFF0',
'khaki':'#F0E68C','lavender':'#E6E6FA','lavenderblush':'#FFF0F5','lawngreen':'#7CFC00','lemonchiffon':'#FFFACD','lightblue':'#ADD8E6',
'lightcoral':'#F08080','lightcyan':'#E0FFFF','lightgoldenrodyellow':'#FAFAD2','lightgray':'#D3D3D3','lightgrey':'#D3D3D3','lightgreen':'#90EE90',
'lightpink':'#FFB6C1','lightsalmon':'#FFA07A','lightseagreen':'#20B2AA','lightskyblue':'#87CEFA','lightslategray':'#778899',
'lightslategrey':'#778899','lightsteelblue':'#B0C4DE','lightyellow':'#FFFFE0','lime':'#00FF00','limegreen':'#32CD32','linen':'#FAF0E6',
'magenta':'#FF00FF','maroon':'#800000','mediumaquamarine':'#66CDAA','mediumblue':'#0000CD','mediumorchid':'#BA55D3','mediumpurple':'#9370D8',
'mediumseagreen':'#3CB371','mediumslateblue':'#7B68EE','mediumspringgreen':'#00FA9A','mediumturquoise':'#48D1CC','mediumvioletred':'#C71585',
'midnightblue':'#191970','mintcream':'#F5FFFA','mistyrose':'#FFE4E1','moccasin':'#FFE4B5','navajowhite':'#FFDEAD','navy':'#000080','oldlace':'#FDF5E6',
'olive':'#808000','olivedrab':'#6B8E23','orange':'#FFA500','orangered':'#FF4500','orchid':'#DA70D6','palegoldenrod':'#EEE8AA',
'palegreen':'#98FB98','paleturquoise':'#AFEEEE','palevioletred':'#D87093','papayawhip':'#FFEFD5','peachpuff':'#FFDAB9','peru':'#CD853F',
'pink':'#FFC0CB','plum':'#DDA0DD','powderblue':'#B0E0E6','purple':'#800080','red':'#FF0000','rosybrown':'#BC8F8F','royalblue':'#4169E1',
'saddlebrown':'#8B4513','salmon':'#FA8072','sandybrown':'#F4A460','seagreen':'#2E8B57','seashell':'#FFF5EE','sienna':'#A0522D','silver':'#C0C0C0',
'skyblue':'#87CEEB','slateblue':'#6A5ACD','slategray':'#708090','slategrey':'#708090','snow':'#FFFAFA','springgreen':'#00FF7F',
'steelblue':'#4682B4','tan':'#D2B48C','teal':'#008080','thistle':'#D8BFD8','tomato':'#FF6347','turquoise':'#40E0D0','violet':'#EE82EE'
};
// helpers
var absround = function(number){
return (0.5 + number) << 0;
};
var hue2rgb = function(a, b, c) { // http://www.w3.org/TR/css3-color/#hsl-color
if(c < 0) c += 1;
if(c > 1) c -= 1;
if(c < 1/6) return a + (b - a) * 6 * c;
if(c < 1/2) return b;
if(c < 2/3) return a + (b - a) * (2/3 - c) * 6;
return a;
};
var p2v = function(p){
return isPercent.test(p) ? absround(parseInt(p) * 2.55) : p;
};
var isNamedColor = function(key){
var lc = ('' + key).toLowerCase();
return namedColors.hasOwnProperty(lc)
? namedColors[lc]
: null;
};
// patterns
var isHex = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i;
var isHSL = /^hsla?\((\d{1,3}?),\s*(\d{1,3}%),\s*(\d{1,3}%)(,\s*[01]?\.?\d*)?\)$/;
var isRGB = /^rgba?\((\d{1,3}%?),\s*(\d{1,3}%?),\s*(\d{1,3}%?)(,\s*[01]?\.?\d*)?\)$/;
var isPercent = /^\d+(\.\d+)*%$/;
var hexBit = /([0-9a-f])/gi;
var leadHex = /^#/;
var matchHSL = /^hsla?\((\d{1,3}),\s*(\d{1,3})%,\s*(\d{1,3})%(,\s*([01]?\.?\d*))?\)$/;
var matchRGB = /^rgba?\((\d{1,3}%?),\s*(\d{1,3}%?),\s*(\d{1,3}%?)(,\s*([01]?\.?\d*))?\)$/;
/**
* Color instance - get, update and output a Color between structures.
* @constructor
* @param {mixed} value Accepts any valid CSS color value e.g., #FF9900, rgb(255, 153, 0), rgba(100%, 40%, 0%, 0.8);
* a hash with properties mapped to the Color instance e.g., red, green, saturation, brightness;
* another Color instance; a numeric color value; a named CSS color
* @class Instances of the Color class serve as abstract reprentations of the color itself, and don't need to be
* transformed from one format to another. A single Color instance can have any component (red, green, blue, hue, saturation, lightness, brightness,
* alpha) updated regardless of the source. Further, all other components will be normalized automatically. If a Color is instanced using a hex value,
* it can have it's lightness component updated directly despite lightness being a HSL component. Further, the same color instance can output it's
* component values in any format without any extra conversions. Conversion methods (getRGB, getHex) are provided just as helpers, and don't perform any
* actual transformations. They are not required for use or translation. The standard component parts are available as instance methods - passing a value
* argument to set, and each return the value as well (with or without setter arguments). These components perform transformations and dispatch events, and
* can be used without any sugar to manage the Color instance.
* Component methods include:
* .red()
* .green()
* .blue()
* .hue()
* .saturation()
* .lightness()
* .brightness()
* .hex()
* .decimal()
* </ul>
* @example
* // instancing...
* new Color();
* new Color('#FF9900');
* new Color(element.style.color);
* new Color('pink');
* new Color(123456);
* new Color({ red : 255, green : 100, blue : 0 });
* new Color(colorInstance);
* // usage...
* var color = new Color('#FF9900');
* color.brightness(20);
* element.style.backgroundColor = color;
* console.log(color.getRGB());
* console.log(color.saturation());
*/
function Color(value){
this._listeners = {};
this.subscribe(Events.RGB_UPDATED, this._RGBUpdated);
this.subscribe(Events.HEX_UPDATED, this._HEXUpdated);
this.subscribe(Events.HSL_UPDATED, this._HSLUpdated);
this.subscribe(Events.HSV_UPDATED, this._HSVUpdated);
this.subscribe(Events.INT_UPDATED, this._INTUpdated);
this.parse(value);
};
Color.prototype._decimal = 0; // 0 - 16777215
Color.prototype._hex = '#000000'; // #000000 - #FFFFFF
Color.prototype._red = 0; // 0 - 255
Color.prototype._green = 0; // 0 - 255
Color.prototype._blue = 0; // 0 - 255
Color.prototype._hue = 0; // 0 - 360
Color.prototype._saturation = 0; // 0 - 100
Color.prototype._lightness = 0; // 0 - 100
Color.prototype._brightness = 0; // 0 - 100
Color.prototype._alpha = 1; // 0 - 1
/**
* Convert mixed variable to Color component properties, and adopt those properties.
* @function
* @param {mixed} value Accepts any valid CSS color value e.g., #FF9900, rgb(255, 153, 0), rgba(100%, 40%, 0%, 0.8);
* a hash with properties mapped to the Color instance e.g., red, green, saturation, brightness;
* another Color instance; a numeric color value; a named CSS color
* @returns this
* @example
* var color = new Color();
* color.parse();
* color.parse('#FF9900');
* color.parse(element.style.color);
* color.parse('pink');
* color.parse(123456);
* color.parse({ red : 255, green : 100, blue : 0 });
* color.parse(colorInstance);
*/
Color.prototype.parse = function(value){
if(typeof value == 'undefined'){
return this;
};
switch(true){
case isFinite(value) :
this.decimal(value);
this.output = Color.INT;
return this;
case (value instanceof Color) :
this.copy(value);
return this;
default :
switch(typeof value) {
case 'object' :
this.set(value);
return this;
case 'string' :
switch(true){
case (namedColors.hasOwnProperty(value)) :
value = namedColors[value];
var stripped = value.replace(leadHex, '');
this.decimal(parseInt(stripped, 16));
return this;
case isHex.test(value) :
var stripped = value.replace(leadHex, '');
if(stripped.length == 3) {
stripped = stripped.replace(hexBit, '$1$1');
};
this.decimal(parseInt(stripped, 16));
return this;
case isRGB.test(value) :
var parts = value.match(matchRGB);
this.red(p2v(parts[1]));
this.green(p2v(parts[2]));
this.blue(p2v(parts[3]));
this.alpha(parseFloat(parts[5]) || 1);
this.output = (isPercent.test(parts[1]) ? 2 : 1) + (parts[5] ? 2 : 0);
return this;
case isHSL.test(value) :
var parts = value.match(matchHSL);
this.hue(parseInt(parts[1]));
this.saturation(parseInt(parts[2]));
this.lightness(parseInt(parts[3]));
this.alpha(parseFloat(parts[5]) || 1);
this.output = parts[5] ? 6: 5;
return this;
};
};
};
return this;
};
/**
* Create a duplicate of this Color instance
* @function
* @returns Color
*/
Color.prototype.clone = function(){
return new Color(this.decimal());
};
/**
* Copy values from another Color instance
* @function
* @param {Color} color Color instance to copy values from
* @returns this
*/
Color.prototype.copy = function(color){
return this.set(color.decimal());
};
/**
* Set a color component value
* @function
* @param {string|object|number} key Name of the color component to defined, or a hash of key:value pairs, or a single numeric value
* @param {string|number} value - Value of the color component to be set
* @returns this
* @example
* var color = new Color();
* color.set('lightness', 100);
* color.set({ red : 255, green : 100 });
* color.set(123456);
*/
Color.prototype.set = function(key, value){
if(arguments.length == 1){
if(typeof key == 'object'){
for(var p in key){
if(typeof this[p] == 'function'){
this[p](key[p]);
};
};
} else if(isFinite(key)){
this.decimal(key);
}
} else if(typeof this[key] == 'function'){
this[key](value);
};
return this;
};
/**
* sets the invoking Color instance component values to a point between the original value and the destination Color instance component value, multiplied by the factor
* @function
* @param {Color} destination Color instance to serve as the termination of the interpolation
* @param {number} factor 0-1, where 0 is the origin Color and 1 is the destination Color, and 0.5 is halfway between. This method will "blend" the colors.
* @returns this
* @example
* var orange = new Color('#FF9900');
* var white = new Color('#FFFFFF');
* orange.interpolate(white, 0.5);
*/
Color.prototype.interpolate = function(destination, factor){
if(!(destination instanceof Color)){
destination = new Color(destination);
};
this._red = absround( +(this._red) + (destination._red - this._red) * factor );
this._green = absround( +(this._green) + (destination._green - this._green) * factor );
this._blue = absround( +(this._blue) + (destination._blue - this._blue) * factor );
this._alpha = absround( +(this._alpha) + (destination._alpha - this._alpha) * factor );
this.broadcast(Events.RGB_UPDATED);
this.broadcast(Events.UPDATED);
return this;
};
Color.prototype._RGB2HSL = function(){
var r = this._red / 255;
var g = this._green / 255;
var b = this._blue / 255;
var max = Math.max(r, g, b);
var min = Math.min(r, g, b);
var l = (max + min) / 2;
var v = max;
if(max == min) {
this._hue = 0;
this._saturation = 0;
this._lightness = absround(l * 100);
this._brightness = absround(v * 100);
return;
};
var d = max - min;
var s = d / ( ( l <= 0.5) ? (max + min) : (2 - max - min) );
var h = ((max == r)
? (g - b) / d + (g < b ? 6 : 0)
: (max == g)
? ((b - r) / d + 2)
: ((r - g) / d + 4)) / 6;
this._hue = absround(h * 360);
this._saturation = absround(s * 100);
this._lightness = absround(l * 100);
this._brightness = absround(v * 100);
};
Color.prototype._HSL2RGB = function(){
var h = this._hue / 360;
var s = this._saturation / 100;
var l = this._lightness / 100;
var q = l < 0.5 ? l * (1 + s) : (l + s - l * s);
var p = 2 * l - q;
this._red = absround(hue2rgb(p, q, h + 1/3) * 255);
this._green = absround(hue2rgb(p, q, h) * 255);
this._blue = absround(hue2rgb(p, q, h - 1/3) * 255);
};
Color.prototype._HSV2RGB = function(){ // http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
var h = this._hue / 360;
var s = this._saturation / 100;
var v = this._brightness / 100;
var r = 0;
var g = 0;
var b = 0;
var i = Math.floor(h * 6);
var f = h * 6 - i;
var p = v * (1 - s);
var q = v * (1 - f * s);
var t = v * (1 - (1 - f) * s);
switch(i % 6){
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;
}
this._red = absround(r * 255);
this._green = absround(g * 255);
this._blue = absround(b * 255);
};
Color.prototype._INT2HEX = function(){
var x = this._decimal.toString(16);
x = '000000'.substr(0, 6 - x.length) + x;
this._hex = '#' + x.toUpperCase();
};
Color.prototype._INT2RGB = function(){
this._red = this._decimal >> 16;
this._green = (this._decimal >> 8) & 0xFF;
this._blue = this._decimal & 0xFF;
};
Color.prototype._HEX2INT = function(){
this._decimal = parseInt(this._hex, 16);
};
Color.prototype._RGB2INT = function(){
this._decimal = (this._red << 16 | (this._green << 8) & 0xffff | this._blue);
};
Color.prototype._RGBUpdated = function(){
this._RGB2INT(); // populate INT values
this._RGB2HSL(); // populate HSL values
this._INT2HEX(); // populate HEX values
};
Color.prototype._HSLUpdated = function(){
this._HSL2RGB(); // populate RGB values
this._RGB2INT(); // populate INT values
this._INT2HEX(); // populate HEX values
};
Color.prototype._HSVUpdated = function(){
this._HSV2RGB(); // populate RGB values
this._RGB2INT(); // populate INT values
this._INT2HEX(); // populate HEX values
};
Color.prototype._HEXUpdated = function(){
this._HEX2INT(); // populate INT values
this._INT2RGB(); // populate RGB values
this._RGB2HSL(); // populate HSL values
};
Color.prototype._INTUpdated = function(){
this._INT2RGB(); // populate RGB values
this._RGB2HSL(); // populate HSL values
this._INT2HEX(); // populate HEX values
};
Color.prototype._broadcastUpdate = function(){
this.broadcast(Event.UPDATED);
};
/**
* Set the decimal value of the color, updates all other components, and dispatches Event.UPDATED
* @function
* @param {number} value 0 (black) to 16777215 (white) - the decimal value to set
* @returns Number
* @example
* var color = new Color();
* color.decimal(123456);
*/
Color.prototype.decimal = function(value){
return this._handle('_decimal', value, Events.INT_UPDATED);
};
/**
* Set the hex value of the color, updates all other components, and dispatches Event.UPDATED
* @function
* @param {string} value Hex value to be set
* @returns String
* @example
* var color = new Color();
* color.hex('#FF9900');
* color.hex('#CCC');
*/
Color.prototype.hex = function(value){
return this._handle('_hex', value, Events.HEX_UPDATED);
};
/**
* Set the red component value of the color, updates all other components, and dispatches Event.UPDATED
* @function
* @param {number} value 0 - 255 red component value to set
* @returns Number
* @example
* var color = new Color();
* color.red(125);
*/
Color.prototype.red = function(value){
return this._handle('_red', value, Events.RGB_UPDATED);
};
/**
* Set the green component value of the color, updates all other components, and dispatches Event.UPDATED
* @function
* @param {number} value 0 - 255 green component value to set
* @returns Number
* @example
* var color = new Color();
* color.green(125);
*/
Color.prototype.green = function(value){
return this._handle('_green', value, Events.RGB_UPDATED);
};
/**
* Set the blue component value of the color, updates all other components, and dispatches Event.UPDATED
* @function
* @param {number} value 0 - 255 blue component value to set
* @returns Number
* @example
* var color = new Color();
* color.blue(125);
*/
Color.prototype.blue = function(value){
return this._handle('_blue', value, Events.RGB_UPDATED);
};
/**
* Set the hue component value of the color, updates all other components, and dispatches Event.UPDATED
* @function
* @param {number} value 0 - 360 hue component value to set
* @returns Number
* @example
* var color = new Color();
* color.hue(280);
*/
Color.prototype.hue = function(value){
return this._handle('_hue', value, Events.HSL_UPDATED);
};
/**
* Set the saturation component value of the color, updates all other components, and dispatches Event.UPDATED
* @function
* @param {number} value 0 - 100 saturation component value to set
* @returns Number
* @example
* var color = new Color();
* color.saturation(280);
*/
Color.prototype.saturation = function(value){
return this._handle('_saturation', value, Events.HSL_UPDATED);
};
/**
* Set the lightness component value of the color, updates all other components, and dispatches Event.UPDATED
* @function
* @param {number} value 0 - 100 lightness component value to set
* @returns Number
* @example
* var color = new Color();
* color.lightness(80);
*/
Color.prototype.lightness = function(value){
return this._handle('_lightness', value, Events.HSL_UPDATED);
};
/**
* Set the brightness component value of the color, updates all other components, and dispatches Event.UPDATED
* @function
* @param {number} value 0 - 100 brightness component value to set
* @returns Number
* @example
* var color = new Color();
* color.brightness(80);
*/
Color.prototype.brightness = function(value){
return this._handle('_brightness', value, Events.HSV_UPDATED);
};
/**
* Set the opacity value of the color, updates all other components, and dispatches Event.UPDATED
* @function
* @param {number} value 0 - 1 opacity component value to set
* @returns Number
* @example
* var color = new Color();
* color.alpha(0.5);
*/
Color.prototype.alpha = function(value){
return this._handle('_alpha', value);
};
Color.prototype._handle = function(prop, value, event){
if(typeof this[prop] != 'undefined'){
if(typeof value != 'undefined'){
if(value != this[prop]){
this[prop] = value;
if(event){
this.broadcast(event);
};
};
this.broadcast(Event.UPDATED);
};
};
return this[prop];
};
/**
* Returns a CSS-formatted hex string [e.g., #FF9900] from the Color's component values
* @function
* @returns String
* @example
* var color = new Color();
* element.style.backgroundColor = color.getHex();
*/
Color.prototype.getHex = function(){
return this._hex;
};
/**
* Returns a CSS-formatted RGB string [e.g., rgb(255, 153, 0)] from the Color's component values
* @function
* @returns String
* @example
* var color = new Color();
* element.style.backgroundColor = color.getRGB();
*/
Color.prototype.getRGB = function(){
var components = [absround(this._red), absround(this._green), absround(this._blue)];
return 'rgb(' + components.join(', ') + ')';
};
/**
* Returns a CSS-formatted percentile RGB string [e.g., rgb(100%, 50%, 0)] from the Color's component values
* @function
* @returns String
* @example
* var color = new Color();
* element.style.backgroundColor = color.getPRGB();
*/
Color.prototype.getPRGB = function(){
var components = [absround(100 * this._red / 255) + '%', absround(100 * this._green / 255) + '%', absround(100 * this._blue / 255) + '%'];
return 'rgb(' + components.join(', ') + ')';
};
/**
* Returns a CSS-formatted RGBA string [e.g., rgba(255, 153, 0, 0.5)] from the Color's component values
* @function
* @returns String
* @example
* var color = new Color();
* element.style.backgroundColor = color.getRGBA();
*/
Color.prototype.getRGBA = function(){
var components = [absround(this._red), absround(this._green), absround(this._blue), this._alpha];
return 'rgba(' + components.join(', ') + ')';
};
/**
* Returns a CSS-formatted percentile RGBA string [e.g., rgba(100%, 50%, 0%, 0.5)] from the Color's component values
* @function
* @returns String
* @example
* var color = new Color();
* element.style.backgroundColor = color.getPRGBA();
*/
Color.prototype.getPRGBA = function(){
var components = [absround(100 * this._red / 255) + '%', absround(100 * this._green / 255) + '%', absround(100 * this._blue / 255) + '%', this._alpha];
return 'rgba(' + components.join(', ') + ')';
};
/**
* Returns a CSS-formatted HSL string [e.g., hsl(360, 100%, 100%)] from the Color's component values
* @function
* @returns String
* @example
* var color = new Color();
* element.style.backgroundColor = color.getHSL();
*/
Color.prototype.getHSL = function(){
var components = [absround(this._hue), absround(this._saturation) + '%', absround(this._lightness) + '%'];
return 'hsl(' + components.join(', ') + ')';
};
/**
* Returns a CSS-formatted HSLA string [e.g., hsl(360, 100%, 100%, 0.5)] from the Color's component values
* @function
* @returns String
* @example
* var color = new Color();
* element.style.backgroundColor = color.getHSLA();
*/
Color.prototype.getHSLA = function(){
var components = [absround(this._hue), absround(this._saturation) + '%', absround(this._lightness) + '%', this._alpha];
return 'hsla(' + components.join(', ') + ')';
};
/**
* Returns a tokenized string from the Color's component values
* @function
* @param {string} string The string to return, with tokens expressed as %token% that are replaced with component values. Tokens are as follows:
* r : red
* g : green
* b : blue
* h : hue
* s : saturation
* l : lightness
* v : brightness
* a : alpha
* x : hex
* i : value
* @returns String
* @example
* var color = new Color('#FF9900');
* console.log(color.format('red=%r%, green=%g%, blue=%b%));
*/
Color.prototype.format = function(string){
var tokens = {
r : this._red,
g : this._green,
b : this._blue,
h : this._hue,
s : this._saturation,
l : this._lightness,
v : this._brightness,
a : this._alpha,
x : this._hex,
d : this._decimal
};
for(var token in tokens){
string = string.split('%' + token + '%').join(tokens[token]);
};
return string;
};
/**
* Sets the format used by the native toString method
* Color.HEX outputs #FF9900
* Color.RGB outputs rgb(255, 153, 0)
* Color.PRGB outputs rgb(100%, 50%, 0)
* Color.RGBA outputs rgba(255, 153, 0, 0.5)
* Color.PRGBA outputs rgba(100%, 50%, 0, 0.5)
* Color.HSL outputs hsl(360, 100%, 80%)
* Color.HSLA outputs hsla(360, 100%, 80%, 0.5)
* @example
* var color = new Color('#FF9900');
* color.format = Color.RGB;
* element.style.backgroundColor = color;
* element.style.color = color;
*/
Color.prototype.output = 0;
Color.HEX = 0; // toString returns hex: #ABC123
Color.RGB = 1; // toString returns rgb: rgb(0, 100, 255)
Color.PRGB = 2; // toString returns percent rgb: rgb(0%, 40%, 100%)
Color.RGBA = 3; // toString returns rgba: rgba(0, 100, 255, 0.5)
Color.PRGBA = 4; // toString returns percent rgba: rgba(0%, 40%, 100%, 0.5)
Color.HSL = 5; // toString returns hsl: hsl(360, 50%, 50%)
Color.HSLA = 6; // toString returns hsla: hsla(360, 50%, 50%, 0.5)
Color.INT = 7; // toString returns decimal value
Color.prototype.toString = function(){
switch(this.output){
case 0 : // Color.HEX
return this.getHex();
case 1 : // Color.RGB
return this.getRGB();
case 2 : // Color.PRGB
return this.getPRGB();
case 3 : // Color.RGBA
return this.getRGBA();
case 4 : // Color.PRGBA
return this.getPRGBA();
case 5 : // Color.HSL
return this.getHSL();
case 6 : // Color.HSLA
return this.getHSLA();
case 7 : // Color.INT
return this._decimal;
};
return this.getHex();
};
// Event Management
Color.prototype._listeners = null;
Color.prototype._isSubscribed = function(type){
return this._listeners[type] != null;
};
/**
* @function
* @param {string} type Event type to listen for
* @param {function} callback listener to register to the event
* @example
* var color = new Color();
* color.subscribe(Color.Event.UPDATED, function(){
* alert('this color has been updated');
* });
* color.red(255);
*/
Color.prototype.subscribe = function(type, callback){
if(!this._isSubscribed(type)) {
this._listeners[type] = [];
};
this._listeners[type].push(callback);
};
/**
* @function
* @param {string} type Event type to remove the listener from
* @param {function} callback listener to unregister from the event
* @example
* var color = new Color();
* var handler = function(){
* console.log('this color has been updated');
* });
* color.subscribe(Color.Event.UPDATED, handler);
* color.red(255);
* color.unsubscribe(Color.Event.UPDATED, handler);
* color.red(0);
*/
Color.prototype.unsubscribe = function(type, callback){
if(!this._isSubscribed(type)) {
return;
};
var stack = this._listeners[type];
for(var i = 0, l = stack.length; i < l; i++){
if(stack[i] === callback){
stack.splice(i, 1);
return this.unsubscribe(type, callback);
};
};
};
/**
* @function
* @param {string} type Event type to dispatch
* @param {array} params Array of arguments to pass to listener
* @example
* var color = new Color();
* var handler = function(a, b){
* console.log('a=' + a, 'b=' + b);
* });
* color.subscribe('arbitraryEvent', handler);
* color.broadcast('arbitraryEvent', ['A', 'B']);
*/
Color.prototype.broadcast = function(type, params){
if(!this._isSubscribed(type)) {
return;
}
var stack = this._listeners[type];
var l = stack.length;
for(var i = 0; i < l; i++) {
stack[i].apply(this, params);
}
};
/**
* Blends the color from it's current state to the target Color over the duration
* @function
* @param {number} duration duration of tween in millisecond
* @param {Color} color destination color
* @returns number
* @example
* var color = new Color('#FF9900');
* color.tween(2000, '#FFFFFF');
*/
Color.prototype.tween = function(duration, color){
if(!(color instanceof Color)){
color = new Color(color);
};
var start = +(new Date());
var ref = this;
this.broadcast('tweenStart');
var interval = setInterval(function(){
var ellapsed = +(new Date()) - start;
var delta = Math.min(1, ellapsed / duration);
ref.interpolate(color, delta);
ref.broadcast('tweenProgress');
if(delta == 1){
clearInterval(interval);
ref.broadcast('tweenComplete');
};
}, 20);
return interval; // return so it can be cancelled early
};
/**
* Binds the color to an object's property - whenever the Color is updated, the property will be set to the value of the Color instance's toString method
* @function
* @param {object} object Object containing the property to update
* @param {string} property Name of the property to update
* @example
* var color = new Color('#FF9900');
* color.bind(someElement.style, 'backgroundColor');
*/
Color.prototype.bind = function(object, property){
var ref = this;
this.subscribe('updated', function(){
object[property] = ref.toString();
});
};
/**
* [static] Returns a Color instance of a random color
* @function
* @returns Color
* @example
* var gray = Color.interpolate('#FFFFFF', '#000000', 0.5);
*/
Color.random = function(){
return new Color(absround(Math.random() * 16777215));
};
/**
* [static] Returns a Color instance bound to an object's property, set to the value of the property
* @function
* @param {object} object Object containing the property to update
* @param {string} property Name of the property to update
* @returns Color
* @example
* var color = Color.bind(someElement.style, 'backgroundColor');
*/
Color.bind = function(object, property){
var color = new Color(object[property]);
color.bind(object, property);
return color;
};
Color.Events = Events;
if (typeof define === 'function') {
define('Color', [], function() {
return Color;
});
};
return Color;
})(window);