toloframework
Version:
Javascript/HTML/CSS compiler for Firefox OS or nodewebkit apps using modules in the nodejs style.
383 lines (326 loc) • 8.54 kB
JavaScript
"use strict";
/** @module tfw.color */
require('tfw.color', function (require, module, exports) {
var _ = function () {
var D = {
"en": {},
"fr": {}
},
X = require("$").intl;
function _() {
return X(D, arguments);
}
_.all = D;
return _;
}();
"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.
*/
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 #
* ##################
*/
/**
* @constructor
* @param {string} code - CSS color code.
*/
function Color(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);
}
}
var INV6 = 1 / 6,
INV15 = 1 / 15,
INV99 = 1 / 99,
INV255 = 1 / 255,
INV359 = 1 / 359;
/**
* Read R,G,B and write H,S,L.
* @returns {undefined}
*/
function rgb2hsl() {
var R = this.R,
G = this.G,
B = this.B,
min = Math.min(R, G, B),
max = Math.max(R, G, B),
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);
}
}
}
/**
* @returns {undefined}
* @see https://en.wikipedia.org/wiki/HSL_and_HSV#Converting_to_RGB
*/
function hsl2rgb() {
var H = 6 * this.H,
S = this.S,
L = this.L,
chroma = (1 - Math.abs(2 * L - 1)) * S,
x = chroma * (1 - Math.abs(H % 2 - 1));
var R = 0,
G = 0,
B = 0;
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;
}
/**
* @returns {undefined}
* @see https://en.wikipedia.org/wiki/Grayscale
*/
function luminance() {
return 0.2126 * this.R + 0.7152 * this.G + 0.0722 * this.B;
}
/**
* @returns {string} String value of the color. `#fd45a7`.
*/
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 "#".concat(color);
}
/**
* @param {string} _text - CSS color code.
* @returns {boolean} Success?
*/
function parse(_text) {
var text = typeof _text !== 'undefined' ? _text : '#000000',
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;
}
/**
* @param {string} text - CSS color code.
* @returns {boolean} Success?
*/
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;
default:
}
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();
}
module.exports._ = _;
});