368 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			368 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
 | |
| /*!
 | |
|  * 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('./');
 | |
| 
 | |
| /**
 | |
|  * 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
 | |
|  */
 | |
| 
 | |
| var RGBA = exports = module.exports = function RGBA(r,g,b,a){
 | |
|   Node.call(this);
 | |
|   this.r = clamp(r);
 | |
|   this.g = clamp(g);
 | |
|   this.b = clamp(b);
 | |
|   this.a = clampAlpha(a);
 | |
|   this.name = '';
 | |
|   this.rgba = this;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Inherit from `Node.prototype`.
 | |
|  */
 | |
| 
 | |
| RGBA.prototype.__proto__ = Node.prototype;
 | |
| 
 | |
| /**
 | |
|  * Return an `RGBA` without clamping values.
 | |
|  * 
 | |
|  * @param {Number} r
 | |
|  * @param {Number} g
 | |
|  * @param {Number} b
 | |
|  * @param {Number} a
 | |
|  * @return {RGBA}
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| RGBA.withoutClamping = function(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
 | |
|  */
 | |
| 
 | |
| RGBA.prototype.clone = function(){
 | |
|   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
 | |
|  */
 | |
| 
 | |
| RGBA.prototype.toJSON = function(){
 | |
|   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
 | |
|  */
 | |
| 
 | |
| RGBA.prototype.toBoolean = function(){
 | |
|   return nodes.true;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Return `HSLA` representation.
 | |
|  *
 | |
|  * @return {HSLA}
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| RGBA.prototype.__defineGetter__('hsla', function(){
 | |
|   return HSLA.fromRGBA(this);
 | |
| });
 | |
| 
 | |
| /**
 | |
|  * Return hash.
 | |
|  *
 | |
|  * @return {String}
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| RGBA.prototype.__defineGetter__('hash', function(){
 | |
|   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
 | |
|  */
 | |
| 
 | |
| RGBA.prototype.add = function(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
 | |
|  */
 | |
| 
 | |
| RGBA.prototype.sub = function(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
 | |
|  */
 | |
| 
 | |
| RGBA.prototype.multiply = function(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
 | |
|  */
 | |
| 
 | |
| RGBA.prototype.divide = function(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
 | |
|  */
 | |
| 
 | |
| RGBA.prototype.operate = function(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 Node.prototype.operate.call(this, op, right);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Return #nnnnnn, #nnn, or rgba(n,n,n,n) string representation of the color.
 | |
|  *
 | |
|  * @return {String}
 | |
|  * @api public
 | |
|  */
 | |
| 
 | |
| RGBA.prototype.toString = function(){
 | |
|   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
 | |
|  */
 | |
| 
 | |
| exports.fromHSLA = function(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));
 | |
| }
 |