rgba.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. /*!
  2. * Stylus - RGBA
  3. * Copyright (c) Automattic <developer.wordpress.com>
  4. * MIT Licensed
  5. */
  6. /**
  7. * Module dependencies.
  8. */
  9. var Node = require('./node')
  10. , HSLA = require('./hsla')
  11. , functions = require('../functions')
  12. , adjust = functions.adjust
  13. , nodes = require('./');
  14. /**
  15. * Initialize a new `RGBA` with the given r,g,b,a component values.
  16. *
  17. * @param {Number} r
  18. * @param {Number} g
  19. * @param {Number} b
  20. * @param {Number} a
  21. * @api public
  22. */
  23. var RGBA = exports = module.exports = function RGBA(r,g,b,a){
  24. Node.call(this);
  25. this.r = clamp(r);
  26. this.g = clamp(g);
  27. this.b = clamp(b);
  28. this.a = clampAlpha(a);
  29. this.name = '';
  30. this.rgba = this;
  31. };
  32. /**
  33. * Inherit from `Node.prototype`.
  34. */
  35. RGBA.prototype.__proto__ = Node.prototype;
  36. /**
  37. * Return an `RGBA` without clamping values.
  38. *
  39. * @param {Number} r
  40. * @param {Number} g
  41. * @param {Number} b
  42. * @param {Number} a
  43. * @return {RGBA}
  44. * @api public
  45. */
  46. RGBA.withoutClamping = function(r,g,b,a){
  47. var rgba = new RGBA(0,0,0,0);
  48. rgba.r = r;
  49. rgba.g = g;
  50. rgba.b = b;
  51. rgba.a = a;
  52. return rgba;
  53. };
  54. /**
  55. * Return a clone of this node.
  56. *
  57. * @return {Node}
  58. * @api public
  59. */
  60. RGBA.prototype.clone = function(){
  61. var clone = new RGBA(
  62. this.r
  63. , this.g
  64. , this.b
  65. , this.a);
  66. clone.raw = this.raw;
  67. clone.name = this.name;
  68. clone.lineno = this.lineno;
  69. clone.column = this.column;
  70. clone.filename = this.filename;
  71. return clone;
  72. };
  73. /**
  74. * Return a JSON representation of this node.
  75. *
  76. * @return {Object}
  77. * @api public
  78. */
  79. RGBA.prototype.toJSON = function(){
  80. return {
  81. __type: 'RGBA',
  82. r: this.r,
  83. g: this.g,
  84. b: this.b,
  85. a: this.a,
  86. raw: this.raw,
  87. name: this.name,
  88. lineno: this.lineno,
  89. column: this.column,
  90. filename: this.filename
  91. };
  92. };
  93. /**
  94. * Return true.
  95. *
  96. * @return {Boolean}
  97. * @api public
  98. */
  99. RGBA.prototype.toBoolean = function(){
  100. return nodes.true;
  101. };
  102. /**
  103. * Return `HSLA` representation.
  104. *
  105. * @return {HSLA}
  106. * @api public
  107. */
  108. RGBA.prototype.__defineGetter__('hsla', function(){
  109. return HSLA.fromRGBA(this);
  110. });
  111. /**
  112. * Return hash.
  113. *
  114. * @return {String}
  115. * @api public
  116. */
  117. RGBA.prototype.__defineGetter__('hash', function(){
  118. return this.toString();
  119. });
  120. /**
  121. * Add r,g,b,a to the current component values.
  122. *
  123. * @param {Number} r
  124. * @param {Number} g
  125. * @param {Number} b
  126. * @param {Number} a
  127. * @return {RGBA} new node
  128. * @api public
  129. */
  130. RGBA.prototype.add = function(r,g,b,a){
  131. return new RGBA(
  132. this.r + r
  133. , this.g + g
  134. , this.b + b
  135. , this.a + a);
  136. };
  137. /**
  138. * Subtract r,g,b,a from the current component values.
  139. *
  140. * @param {Number} r
  141. * @param {Number} g
  142. * @param {Number} b
  143. * @param {Number} a
  144. * @return {RGBA} new node
  145. * @api public
  146. */
  147. RGBA.prototype.sub = function(r,g,b,a){
  148. return new RGBA(
  149. this.r - r
  150. , this.g - g
  151. , this.b - b
  152. , a == 1 ? this.a : this.a - a);
  153. };
  154. /**
  155. * Multiply rgb components by `n`.
  156. *
  157. * @param {String} n
  158. * @return {RGBA} new node
  159. * @api public
  160. */
  161. RGBA.prototype.multiply = function(n){
  162. return new RGBA(
  163. this.r * n
  164. , this.g * n
  165. , this.b * n
  166. , this.a);
  167. };
  168. /**
  169. * Divide rgb components by `n`.
  170. *
  171. * @param {String} n
  172. * @return {RGBA} new node
  173. * @api public
  174. */
  175. RGBA.prototype.divide = function(n){
  176. return new RGBA(
  177. this.r / n
  178. , this.g / n
  179. , this.b / n
  180. , this.a);
  181. };
  182. /**
  183. * Operate on `right` with the given `op`.
  184. *
  185. * @param {String} op
  186. * @param {Node} right
  187. * @return {Node}
  188. * @api public
  189. */
  190. RGBA.prototype.operate = function(op, right){
  191. if ('in' != op) right = right.first
  192. switch (op) {
  193. case 'is a':
  194. if ('string' == right.nodeName && 'color' == right.string) {
  195. return nodes.true;
  196. }
  197. break;
  198. case '+':
  199. switch (right.nodeName) {
  200. case 'unit':
  201. var n = right.val;
  202. switch (right.type) {
  203. case '%': return adjust(this, new nodes.String('lightness'), right);
  204. case 'deg': return this.hsla.adjustHue(n).rgba;
  205. default: return this.add(n,n,n,0);
  206. }
  207. case 'rgba':
  208. return this.add(right.r, right.g, right.b, right.a);
  209. case 'hsla':
  210. return this.hsla.add(right.h, right.s, right.l);
  211. }
  212. break;
  213. case '-':
  214. switch (right.nodeName) {
  215. case 'unit':
  216. var n = right.val;
  217. switch (right.type) {
  218. case '%': return adjust(this, new nodes.String('lightness'), new nodes.Unit(-n, '%'));
  219. case 'deg': return this.hsla.adjustHue(-n).rgba;
  220. default: return this.sub(n,n,n,0);
  221. }
  222. case 'rgba':
  223. return this.sub(right.r, right.g, right.b, right.a);
  224. case 'hsla':
  225. return this.hsla.sub(right.h, right.s, right.l);
  226. }
  227. break;
  228. case '*':
  229. switch (right.nodeName) {
  230. case 'unit':
  231. return this.multiply(right.val);
  232. }
  233. break;
  234. case '/':
  235. switch (right.nodeName) {
  236. case 'unit':
  237. return this.divide(right.val);
  238. }
  239. break;
  240. }
  241. return Node.prototype.operate.call(this, op, right);
  242. };
  243. /**
  244. * Return #nnnnnn, #nnn, or rgba(n,n,n,n) string representation of the color.
  245. *
  246. * @return {String}
  247. * @api public
  248. */
  249. RGBA.prototype.toString = function(){
  250. function pad(n) {
  251. return n < 16
  252. ? '0' + n.toString(16)
  253. : n.toString(16);
  254. }
  255. // special case for transparent named color
  256. if ('transparent' == this.name)
  257. return this.name;
  258. if (1 == this.a) {
  259. var r = pad(this.r)
  260. , g = pad(this.g)
  261. , b = pad(this.b);
  262. // Compress
  263. if (r[0] == r[1] && g[0] == g[1] && b[0] == b[1]) {
  264. return '#' + r[0] + g[0] + b[0];
  265. } else {
  266. return '#' + r + g + b;
  267. }
  268. } else {
  269. return 'rgba('
  270. + this.r + ','
  271. + this.g + ','
  272. + this.b + ','
  273. + (+this.a.toFixed(3)) + ')';
  274. }
  275. };
  276. /**
  277. * Return a `RGBA` from the given `hsla`.
  278. *
  279. * @param {HSLA} hsla
  280. * @return {RGBA}
  281. * @api public
  282. */
  283. exports.fromHSLA = function(hsla){
  284. var h = hsla.h / 360
  285. , s = hsla.s / 100
  286. , l = hsla.l / 100
  287. , a = hsla.a;
  288. var m2 = l <= .5 ? l * (s + 1) : l + s - l * s
  289. , m1 = l * 2 - m2;
  290. var r = hue(h + 1/3) * 0xff
  291. , g = hue(h) * 0xff
  292. , b = hue(h - 1/3) * 0xff;
  293. function hue(h) {
  294. if (h < 0) ++h;
  295. if (h > 1) --h;
  296. if (h * 6 < 1) return m1 + (m2 - m1) * h * 6;
  297. if (h * 2 < 1) return m2;
  298. if (h * 3 < 2) return m1 + (m2 - m1) * (2/3 - h) * 6;
  299. return m1;
  300. }
  301. return new RGBA(r,g,b,a);
  302. };
  303. /**
  304. * Clamp `n` >= 0 and <= 255.
  305. *
  306. * @param {Number} n
  307. * @return {Number}
  308. * @api private
  309. */
  310. function clamp(n) {
  311. return Math.max(0, Math.min(n.toFixed(0), 255));
  312. }
  313. /**
  314. * Clamp alpha `n` >= 0 and <= 1.
  315. *
  316. * @param {Number} n
  317. * @return {Number}
  318. * @api private
  319. */
  320. function clampAlpha(n) {
  321. return Math.max(0, Math.min(n, 1));
  322. }