UNPKG

stylus

Version:

Robust, expressive, and feature-rich CSS superset

365 lines (320 loc) 7 kB
/*! * Stylus - RGBA * Copyright (c) Automattic <developer.wordpress.com> * MIT Licensed */ /** * Module dependencies. */ var Node = require('./node') , HSLA = require('./hsla') , functions = require('../functions') , adjust = functions.adjust , nodes = require('./'); exports = module.exports = class RGBA extends Node { /** * Initialize a new `RGBA` with the given r,g,b,a component values. * * @param {Number} r * @param {Number} g * @param {Number} b * @param {Number} a * @api public */ constructor(r, g, b, a) { super(); this.r = clamp(r); this.g = clamp(g); this.b = clamp(b); this.a = clampAlpha(a); this.name = ''; this.rgba = this; } /** * Return an `RGBA` without clamping values. * * @param {Number} r * @param {Number} g * @param {Number} b * @param {Number} a * @return {RGBA} * @api public */ static withoutClamping(r, g, b, a) { var rgba = new RGBA(0, 0, 0, 0); rgba.r = r; rgba.g = g; rgba.b = b; rgba.a = a; return rgba; }; /** * Return a clone of this node. * * @return {Node} * @api public */ clone() { var clone = new RGBA( this.r , this.g , this.b , this.a); clone.raw = this.raw; clone.name = this.name; clone.lineno = this.lineno; clone.column = this.column; clone.filename = this.filename; return clone; }; /** * Return a JSON representation of this node. * * @return {Object} * @api public */ toJSON() { return { __type: 'RGBA', r: this.r, g: this.g, b: this.b, a: this.a, raw: this.raw, name: this.name, lineno: this.lineno, column: this.column, filename: this.filename }; }; /** * Return true. * * @return {Boolean} * @api public */ toBoolean() { return nodes.true; }; /** * Return `HSLA` representation. * * @return {HSLA} * @api public */ get hsla() { return HSLA.fromRGBA(this); }; /** * Return hash. * * @return {String} * @api public */ get hash() { return this.toString(); }; /** * Add r,g,b,a to the current component values. * * @param {Number} r * @param {Number} g * @param {Number} b * @param {Number} a * @return {RGBA} new node * @api public */ add(r, g, b, a) { return new RGBA( this.r + r , this.g + g , this.b + b , this.a + a); }; /** * Subtract r,g,b,a from the current component values. * * @param {Number} r * @param {Number} g * @param {Number} b * @param {Number} a * @return {RGBA} new node * @api public */ sub(r, g, b, a) { return new RGBA( this.r - r , this.g - g , this.b - b , a == 1 ? this.a : this.a - a); }; /** * Multiply rgb components by `n`. * * @param {String} n * @return {RGBA} new node * @api public */ multiply(n) { return new RGBA( this.r * n , this.g * n , this.b * n , this.a); }; /** * Divide rgb components by `n`. * * @param {String} n * @return {RGBA} new node * @api public */ divide(n) { return new RGBA( this.r / n , this.g / n , this.b / n , this.a); }; /** * Operate on `right` with the given `op`. * * @param {String} op * @param {Node} right * @return {Node} * @api public */ operate(op, right) { if ('in' != op) right = right.first switch (op) { case 'is a': if ('string' == right.nodeName && 'color' == right.string) { return nodes.true; } break; case '+': switch (right.nodeName) { case 'unit': var n = right.val; switch (right.type) { case '%': return adjust(this, new nodes.String('lightness'), right); case 'deg': return this.hsla.adjustHue(n).rgba; default: return this.add(n, n, n, 0); } case 'rgba': return this.add(right.r, right.g, right.b, right.a); case 'hsla': return this.hsla.add(right.h, right.s, right.l); } break; case '-': switch (right.nodeName) { case 'unit': var n = right.val; switch (right.type) { case '%': return adjust(this, new nodes.String('lightness'), new nodes.Unit(-n, '%')); case 'deg': return this.hsla.adjustHue(-n).rgba; default: return this.sub(n, n, n, 0); } case 'rgba': return this.sub(right.r, right.g, right.b, right.a); case 'hsla': return this.hsla.sub(right.h, right.s, right.l); } break; case '*': switch (right.nodeName) { case 'unit': return this.multiply(right.val); } break; case '/': switch (right.nodeName) { case 'unit': return this.divide(right.val); } break; } return super.operate(op, right); }; /** * Return #nnnnnn, #nnn, or rgba(n,n,n,n) string representation of the color. * * @return {String} * @api public */ toString() { function pad(n) { return n < 16 ? '0' + n.toString(16) : n.toString(16); } // special case for transparent named color if ('transparent' == this.name) return this.name; if (1 == this.a) { var r = pad(this.r) , g = pad(this.g) , b = pad(this.b); // Compress if (r[0] == r[1] && g[0] == g[1] && b[0] == b[1]) { return '#' + r[0] + g[0] + b[0]; } else { return '#' + r + g + b; } } else { return 'rgba(' + this.r + ',' + this.g + ',' + this.b + ',' + (+this.a.toFixed(3)) + ')'; } }; /** * Return a `RGBA` from the given `hsla`. * * @param {HSLA} hsla * @return {RGBA} * @api public */ static fromHSLA(hsla) { var h = hsla.h / 360 , s = hsla.s / 100 , l = hsla.l / 100 , a = hsla.a; var m2 = l <= .5 ? l * (s + 1) : l + s - l * s , m1 = l * 2 - m2; var r = hue(h + 1 / 3) * 0xff , g = hue(h) * 0xff , b = hue(h - 1 / 3) * 0xff; function hue(h) { if (h < 0) ++h; if (h > 1) --h; if (h * 6 < 1) return m1 + (m2 - m1) * h * 6; if (h * 2 < 1) return m2; if (h * 3 < 2) return m1 + (m2 - m1) * (2 / 3 - h) * 6; return m1; } return new RGBA(r, g, b, a); }; }; /** * Clamp `n` >= 0 and <= 255. * * @param {Number} n * @return {Number} * @api private */ function clamp(n) { return Math.max(0, Math.min(n.toFixed(0), 255)); } /** * Clamp alpha `n` >= 0 and <= 1. * * @param {Number} n * @return {Number} * @api private */ function clampAlpha(n) { return Math.max(0, Math.min(n, 1)); }