index.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /* MIT license */
  2. var colorNames = require('color-name');
  3. var swizzle = require('simple-swizzle');
  4. var hasOwnProperty = Object.hasOwnProperty;
  5. var reverseNames = Object.create(null);
  6. // create a list of reverse color names
  7. for (var name in colorNames) {
  8. if (hasOwnProperty.call(colorNames, name)) {
  9. reverseNames[colorNames[name]] = name;
  10. }
  11. }
  12. var cs = module.exports = {
  13. to: {},
  14. get: {}
  15. };
  16. cs.get = function (string) {
  17. var prefix = string.substring(0, 3).toLowerCase();
  18. var val;
  19. var model;
  20. switch (prefix) {
  21. case 'hsl':
  22. val = cs.get.hsl(string);
  23. model = 'hsl';
  24. break;
  25. case 'hwb':
  26. val = cs.get.hwb(string);
  27. model = 'hwb';
  28. break;
  29. default:
  30. val = cs.get.rgb(string);
  31. model = 'rgb';
  32. break;
  33. }
  34. if (!val) {
  35. return null;
  36. }
  37. return {model: model, value: val};
  38. };
  39. cs.get.rgb = function (string) {
  40. if (!string) {
  41. return null;
  42. }
  43. var abbr = /^#([a-f0-9]{3,4})$/i;
  44. var hex = /^#([a-f0-9]{6})([a-f0-9]{2})?$/i;
  45. var rgba = /^rgba?\(\s*([+-]?\d+)(?=[\s,])\s*(?:,\s*)?([+-]?\d+)(?=[\s,])\s*(?:,\s*)?([+-]?\d+)\s*(?:[,|\/]\s*([+-]?[\d\.]+)(%?)\s*)?\)$/;
  46. var per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,?\s*([+-]?[\d\.]+)\%\s*,?\s*([+-]?[\d\.]+)\%\s*(?:[,|\/]\s*([+-]?[\d\.]+)(%?)\s*)?\)$/;
  47. var keyword = /^(\w+)$/;
  48. var rgb = [0, 0, 0, 1];
  49. var match;
  50. var i;
  51. var hexAlpha;
  52. if (match = string.match(hex)) {
  53. hexAlpha = match[2];
  54. match = match[1];
  55. for (i = 0; i < 3; i++) {
  56. // https://jsperf.com/slice-vs-substr-vs-substring-methods-long-string/19
  57. var i2 = i * 2;
  58. rgb[i] = parseInt(match.slice(i2, i2 + 2), 16);
  59. }
  60. if (hexAlpha) {
  61. rgb[3] = parseInt(hexAlpha, 16) / 255;
  62. }
  63. } else if (match = string.match(abbr)) {
  64. match = match[1];
  65. hexAlpha = match[3];
  66. for (i = 0; i < 3; i++) {
  67. rgb[i] = parseInt(match[i] + match[i], 16);
  68. }
  69. if (hexAlpha) {
  70. rgb[3] = parseInt(hexAlpha + hexAlpha, 16) / 255;
  71. }
  72. } else if (match = string.match(rgba)) {
  73. for (i = 0; i < 3; i++) {
  74. rgb[i] = parseInt(match[i + 1], 0);
  75. }
  76. if (match[4]) {
  77. if (match[5]) {
  78. rgb[3] = parseFloat(match[4]) * 0.01;
  79. } else {
  80. rgb[3] = parseFloat(match[4]);
  81. }
  82. }
  83. } else if (match = string.match(per)) {
  84. for (i = 0; i < 3; i++) {
  85. rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
  86. }
  87. if (match[4]) {
  88. if (match[5]) {
  89. rgb[3] = parseFloat(match[4]) * 0.01;
  90. } else {
  91. rgb[3] = parseFloat(match[4]);
  92. }
  93. }
  94. } else if (match = string.match(keyword)) {
  95. if (match[1] === 'transparent') {
  96. return [0, 0, 0, 0];
  97. }
  98. if (!hasOwnProperty.call(colorNames, match[1])) {
  99. return null;
  100. }
  101. rgb = colorNames[match[1]];
  102. rgb[3] = 1;
  103. return rgb;
  104. } else {
  105. return null;
  106. }
  107. for (i = 0; i < 3; i++) {
  108. rgb[i] = clamp(rgb[i], 0, 255);
  109. }
  110. rgb[3] = clamp(rgb[3], 0, 1);
  111. return rgb;
  112. };
  113. cs.get.hsl = function (string) {
  114. if (!string) {
  115. return null;
  116. }
  117. var hsl = /^hsla?\(\s*([+-]?(?:\d{0,3}\.)?\d+)(?:deg)?\s*,?\s*([+-]?[\d\.]+)%\s*,?\s*([+-]?[\d\.]+)%\s*(?:[,|\/]\s*([+-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:[eE][+-]?\d+)?)\s*)?\)$/;
  118. var match = string.match(hsl);
  119. if (match) {
  120. var alpha = parseFloat(match[4]);
  121. var h = ((parseFloat(match[1]) % 360) + 360) % 360;
  122. var s = clamp(parseFloat(match[2]), 0, 100);
  123. var l = clamp(parseFloat(match[3]), 0, 100);
  124. var a = clamp(isNaN(alpha) ? 1 : alpha, 0, 1);
  125. return [h, s, l, a];
  126. }
  127. return null;
  128. };
  129. cs.get.hwb = function (string) {
  130. if (!string) {
  131. return null;
  132. }
  133. var hwb = /^hwb\(\s*([+-]?\d{0,3}(?:\.\d+)?)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:[eE][+-]?\d+)?)\s*)?\)$/;
  134. var match = string.match(hwb);
  135. if (match) {
  136. var alpha = parseFloat(match[4]);
  137. var h = ((parseFloat(match[1]) % 360) + 360) % 360;
  138. var w = clamp(parseFloat(match[2]), 0, 100);
  139. var b = clamp(parseFloat(match[3]), 0, 100);
  140. var a = clamp(isNaN(alpha) ? 1 : alpha, 0, 1);
  141. return [h, w, b, a];
  142. }
  143. return null;
  144. };
  145. cs.to.hex = function () {
  146. var rgba = swizzle(arguments);
  147. return (
  148. '#' +
  149. hexDouble(rgba[0]) +
  150. hexDouble(rgba[1]) +
  151. hexDouble(rgba[2]) +
  152. (rgba[3] < 1
  153. ? (hexDouble(Math.round(rgba[3] * 255)))
  154. : '')
  155. );
  156. };
  157. cs.to.rgb = function () {
  158. var rgba = swizzle(arguments);
  159. return rgba.length < 4 || rgba[3] === 1
  160. ? 'rgb(' + Math.round(rgba[0]) + ', ' + Math.round(rgba[1]) + ', ' + Math.round(rgba[2]) + ')'
  161. : 'rgba(' + Math.round(rgba[0]) + ', ' + Math.round(rgba[1]) + ', ' + Math.round(rgba[2]) + ', ' + rgba[3] + ')';
  162. };
  163. cs.to.rgb.percent = function () {
  164. var rgba = swizzle(arguments);
  165. var r = Math.round(rgba[0] / 255 * 100);
  166. var g = Math.round(rgba[1] / 255 * 100);
  167. var b = Math.round(rgba[2] / 255 * 100);
  168. return rgba.length < 4 || rgba[3] === 1
  169. ? 'rgb(' + r + '%, ' + g + '%, ' + b + '%)'
  170. : 'rgba(' + r + '%, ' + g + '%, ' + b + '%, ' + rgba[3] + ')';
  171. };
  172. cs.to.hsl = function () {
  173. var hsla = swizzle(arguments);
  174. return hsla.length < 4 || hsla[3] === 1
  175. ? 'hsl(' + hsla[0] + ', ' + hsla[1] + '%, ' + hsla[2] + '%)'
  176. : 'hsla(' + hsla[0] + ', ' + hsla[1] + '%, ' + hsla[2] + '%, ' + hsla[3] + ')';
  177. };
  178. // hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax
  179. // (hwb have alpha optional & 1 is default value)
  180. cs.to.hwb = function () {
  181. var hwba = swizzle(arguments);
  182. var a = '';
  183. if (hwba.length >= 4 && hwba[3] !== 1) {
  184. a = ', ' + hwba[3];
  185. }
  186. return 'hwb(' + hwba[0] + ', ' + hwba[1] + '%, ' + hwba[2] + '%' + a + ')';
  187. };
  188. cs.to.keyword = function (rgb) {
  189. return reverseNames[rgb.slice(0, 3)];
  190. };
  191. // helpers
  192. function clamp(num, min, max) {
  193. return Math.min(Math.max(min, num), max);
  194. }
  195. function hexDouble(num) {
  196. var str = Math.round(num).toString(16).toUpperCase();
  197. return (str.length < 2) ? '0' + str : str;
  198. }