toloframework
Version:
Javascript/HTML/CSS compiler for Firefox OS or nodewebkit apps using modules in the nodejs style.
332 lines (289 loc) • 7.26 kB
JavaScript
"use strict";
/**
* @class Color
* Fast color manipulations.
* Attributes R (red), G (green), B (blue), A (alpha), H (hue), S
* (saturation), and L (luminance) are all floats between 0 and 1.
*/
var Color = function(code) {
this.R = 0;
this.G = 0;
this.B = 0;
this.A = 0;
this.H = 0;
this.S = 0;
this.L = 0;
if( typeof code === 'string' ) {
this.parse( code );
}
};
module.exports = Color;
/**
* Uses R, G and B.
* @return {float} The luminance of this color, to be used in
* grayscale, for instance.
*/
Color.prototype.luminance = luminance;
/**
* Uses R, G, B and A.
* @return {string} CSS color format. For instance: `#FE1795DD`
*/
Color.prototype.stringify = stringify;
/**
* @param {string} text - A CSS representation for this color.
* @return {array} `[red, green, blue, alpha]` with every item between
* 0 and 1.
*/
Color.prototype.parse = parse;
/**
* @return {Color} A clone of this object
*/
Color.prototype.copy = copy;
/**
* Read H,S,L and write R,G,B.
*/
Color.prototype.hsl2rgb = hsl2rgb;
/**
* Read R,G,B and write H,S,L.
*/
Color.prototype.rgb2hsl = rgb2hsl;
var instance = new Color();
Color.instance = instance;
Color.parse = staticParse;
Color.luminance = staticLuminance;
/**
* @param {float} red - Red value between 0 and 1.
* @param {float} green - Green value between 0 and 1.
* @param {float} blue - Blue value between 0 and 1.
* @return {Color} A new color.
*/
Color.newRGB = newRGB;
/**
* @param {float} red - Red value between 0 and 1.
* @param {float} green - Green value between 0 and 1.
* @param {float} blue - Blue value between 0 and 1.
* @param {float} alpha - Alpha value between 0 and 1.
* @return {Color} A new color.
*/
Color.newRGBA = newRGBA;
// ##################
// # Implementation #
// ##################
var INV6 = 1 / 6;
var INV15 = 1 / 15;
var INV99 = 1 / 99;
var INV255 = 1 / 255;
var INV359 = 1 / 359;
function rgb2hsl() {
var R = this.R;
var G = this.G;
var B = this.B;
var min = Math.min( R, G, B );
var max = Math.max( R, G, B );
var delta = max - min;
this.L = 0.5 * (max + min);
if( delta < 0.000001 ) {
this.H = 0;
this.S = 0;
}
else {
this.S = delta / ( 1 - Math.abs( 2 * this.L - 1 ) );
if( max === R ) {
if( G >= B ) {
this.H = INV6 * ((G - B) / delta);
} else {
this.H = 1 - INV6 * ((B - G) / delta);
}
}
else if( max === G ) {
this.H = INV6 * (2 + (B - R) / delta);
}
else {
this.H = INV6 * (4 + (R - G) / delta);
}
}
}
/**
* @see https://en.wikipedia.org/wiki/HSL_and_HSV#Converting_to_RGB
*/
function hsl2rgb() {
var H = 6 * this.H;
var S = this.S;
var L = this.L;
var R, G, B;
var chroma = ( 1 - Math.abs( 2 * L - 1 ) ) * S;
var x = chroma * ( 1 - Math.abs( H % 2 - 1 ) );
if( H < 3 ) {
if( H < 1 ) {
R = chroma;
G = x;
B = 0;
}
else if( H < 2 ) {
R = x;
G = chroma;
B = 0;
}
else {
// H == 2.
R = 0;
G = chroma;
B = x;
}
}
else if( H < 4 ) {
R = 0;
G = x;
B = chroma;
}
else if( H < 5 ) {
R = x;
G = 0;
B = chroma;
}
else {
R = chroma;
G = 0;
B = x;
}
var shift = L - chroma * 0.5;
this.R = R + shift;
this.G = G + shift;
this.B = B + shift;
}
/**
* @see https://en.wikipedia.org/wiki/Grayscale
*/
function luminance() {
return 0.2126 * this.R + 0.7152 * this.G + 0.0722 * this.B;
}
function stringify() {
var color = "#" + hexa2( this.R * 255 ) + hexa2( this.G * 255 ) + hexa2( this.B * 255 );
if( this.A < 1 ) {
color += hexa2( this.A * 255 );
}
return color;
}
function parse( text ) {
if( typeof text !== 'string' ) text = '#000';
var input = text.trim().toUpperCase();
if( parseHexa.call( this, input ) ) return true;
if( parseRGB.call( this, input ) ) return true;
if( parseRGBA.call( this, input ) ) return true;
if( parseHSL.call( this, input ) ) return true;
// @TODO hsl and hsla.
return false;
}
function parseHexa( text ) {
if( text.charAt( 0 ) !== '#' ) return false;
var R = 0, G = 0, B = 0, A = 1;
switch( text.length ) {
case 4:
R = parseInt( text.charAt( 1 ), 16 ) * INV15;
G = parseInt( text.charAt( 2 ), 16 ) * INV15;
B = parseInt( text.charAt( 3 ), 16 ) * INV15;
break;
case 5:
R = parseInt( text.charAt( 1 ), 16 ) * INV15;
G = parseInt( text.charAt( 2 ), 16 ) * INV15;
B = parseInt( text.charAt( 3 ), 16 ) * INV15;
A = parseInt( text.charAt( 4 ), 16 ) * INV15;
break;
case 7:
R = parseInt( text.substr( 1, 2 ), 16 ) * INV255;
G = parseInt( text.substr( 3, 2 ), 16 ) * INV255;
B = parseInt( text.substr( 5, 2 ), 16 ) * INV255;
break;
case 9:
R = parseInt( text.substr( 1, 2 ), 16 ) * INV255;
G = parseInt( text.substr( 3, 2 ), 16 ) * INV255;
B = parseInt( text.substr( 5, 2 ), 16 ) * INV255;
A = parseInt( text.substr( 7, 2 ), 16 ) * INV255;
break;
}
if( isNaN(R) || isNaN(G) || isNaN(B) || isNaN(A) ) {
this.R = this.G = this.B = this.A = 0;
} else {
this.R = R;
this.G = G;
this.B = B;
this.A = A;
};
return true;
}
var RX_RGB = /^RGB[\s\(]+([0-9]+)[^0-9]+([0-9]+)[^0-9]+([0-9]+)/;
function parseRGB( text ) {
var m = RX_RGB.exec( text );
if( !m ) return false;
this.R = clamp01( parseInt( m[1] ) * INV255 );
this.G = clamp01( parseInt( m[2] ) * INV255 );
this.B = clamp01( parseInt( m[3] ) * INV255 );
this.A = 1;
return true;
}
var RX_RGBA = /^RGBA[\s\(]+([0-9]+)[^0-9]+([0-9]+)[^0-9]+([0-9]+)[^0-9\.]+([0-9\.]+)/;
function parseRGBA( text ) {
var m = RX_RGBA.exec( text );
if( !m ) return false;
this.R = clamp01( parseInt( m[1] ) * INV255 );
this.G = clamp01( parseInt( m[2] ) * INV255 );
this.B = clamp01( parseInt( m[3] ) * INV255 );
this.A = clamp01( parseFloat( m[4] ) );
return true;
}
var RX_HSL = /^HSL[\s\(]+([0-9]+)[^0-9]+([0-9]+)[^0-9]+([0-9]+)/;
function parseHSL( text ) {
var m = RX_HSL.exec( text );
if( !m ) return false;
this.H = clamp01( parseInt( m[1] ) * INV359 );
this.S = clamp01( parseInt( m[2] ) * INV99 );
this.L = clamp01( parseInt( m[3] ) * INV99 );
this.A = 1;
this.hsl2rgb();
return true;
}
function hexa2( value ) {
var out = Math.floor( value ).toString( 16 );
if( out.length < 2 ) out = "0" + out;
return out;
}
function copy() {
var newColor = new Color();
newColor.R = this.R;
newColor.G = this.G;
newColor.B = this.B;
newColor.A = this.A;
newColor.H = this.H;
newColor.S = this.S;
newColor.L = this.L;
return newColor;
}
function newRGB(red, green, blue) {
var color = new Color();
color.R = red;
color.G = red;
color.B = red;
return color;
}
function newRGBA(red, green, blue, alpha) {
var color = new Color();
color.R = red;
color.G = red;
color.B = red;
color.A = red;
return color;
}
function clamp01( v ) {
if( v < 0 ) return 0;
if( v > 1 ) return 1;
return v;
}
function staticParse( text ) {
var color = new Color();
color.parse( text );
return color;
}
function staticLuminance( text ) {
var color = staticParse( text );
return color.luminance();
}