3da2de0d5b135bc4f09f09e1c96a1e5a2f0bd8a7ded38ad649b17785c595e8de7110b99dc2ef15ab1bf066a0c40ebf13cde7dc24828534cabeaa1b7521e040 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. var List = require('../../utils/list.js');
  2. var packNumber = require('./Number.js').pack;
  3. // http://www.w3.org/TR/css3-color/#svg-color
  4. var NAME_TO_HEX = {
  5. 'aliceblue': 'f0f8ff',
  6. 'antiquewhite': 'faebd7',
  7. 'aqua': '0ff',
  8. 'aquamarine': '7fffd4',
  9. 'azure': 'f0ffff',
  10. 'beige': 'f5f5dc',
  11. 'bisque': 'ffe4c4',
  12. 'black': '000',
  13. 'blanchedalmond': 'ffebcd',
  14. 'blue': '00f',
  15. 'blueviolet': '8a2be2',
  16. 'brown': 'a52a2a',
  17. 'burlywood': 'deb887',
  18. 'cadetblue': '5f9ea0',
  19. 'chartreuse': '7fff00',
  20. 'chocolate': 'd2691e',
  21. 'coral': 'ff7f50',
  22. 'cornflowerblue': '6495ed',
  23. 'cornsilk': 'fff8dc',
  24. 'crimson': 'dc143c',
  25. 'cyan': '0ff',
  26. 'darkblue': '00008b',
  27. 'darkcyan': '008b8b',
  28. 'darkgoldenrod': 'b8860b',
  29. 'darkgray': 'a9a9a9',
  30. 'darkgrey': 'a9a9a9',
  31. 'darkgreen': '006400',
  32. 'darkkhaki': 'bdb76b',
  33. 'darkmagenta': '8b008b',
  34. 'darkolivegreen': '556b2f',
  35. 'darkorange': 'ff8c00',
  36. 'darkorchid': '9932cc',
  37. 'darkred': '8b0000',
  38. 'darksalmon': 'e9967a',
  39. 'darkseagreen': '8fbc8f',
  40. 'darkslateblue': '483d8b',
  41. 'darkslategray': '2f4f4f',
  42. 'darkslategrey': '2f4f4f',
  43. 'darkturquoise': '00ced1',
  44. 'darkviolet': '9400d3',
  45. 'deeppink': 'ff1493',
  46. 'deepskyblue': '00bfff',
  47. 'dimgray': '696969',
  48. 'dimgrey': '696969',
  49. 'dodgerblue': '1e90ff',
  50. 'firebrick': 'b22222',
  51. 'floralwhite': 'fffaf0',
  52. 'forestgreen': '228b22',
  53. 'fuchsia': 'f0f',
  54. 'gainsboro': 'dcdcdc',
  55. 'ghostwhite': 'f8f8ff',
  56. 'gold': 'ffd700',
  57. 'goldenrod': 'daa520',
  58. 'gray': '808080',
  59. 'grey': '808080',
  60. 'green': '008000',
  61. 'greenyellow': 'adff2f',
  62. 'honeydew': 'f0fff0',
  63. 'hotpink': 'ff69b4',
  64. 'indianred': 'cd5c5c',
  65. 'indigo': '4b0082',
  66. 'ivory': 'fffff0',
  67. 'khaki': 'f0e68c',
  68. 'lavender': 'e6e6fa',
  69. 'lavenderblush': 'fff0f5',
  70. 'lawngreen': '7cfc00',
  71. 'lemonchiffon': 'fffacd',
  72. 'lightblue': 'add8e6',
  73. 'lightcoral': 'f08080',
  74. 'lightcyan': 'e0ffff',
  75. 'lightgoldenrodyellow': 'fafad2',
  76. 'lightgray': 'd3d3d3',
  77. 'lightgrey': 'd3d3d3',
  78. 'lightgreen': '90ee90',
  79. 'lightpink': 'ffb6c1',
  80. 'lightsalmon': 'ffa07a',
  81. 'lightseagreen': '20b2aa',
  82. 'lightskyblue': '87cefa',
  83. 'lightslategray': '789',
  84. 'lightslategrey': '789',
  85. 'lightsteelblue': 'b0c4de',
  86. 'lightyellow': 'ffffe0',
  87. 'lime': '0f0',
  88. 'limegreen': '32cd32',
  89. 'linen': 'faf0e6',
  90. 'magenta': 'f0f',
  91. 'maroon': '800000',
  92. 'mediumaquamarine': '66cdaa',
  93. 'mediumblue': '0000cd',
  94. 'mediumorchid': 'ba55d3',
  95. 'mediumpurple': '9370db',
  96. 'mediumseagreen': '3cb371',
  97. 'mediumslateblue': '7b68ee',
  98. 'mediumspringgreen': '00fa9a',
  99. 'mediumturquoise': '48d1cc',
  100. 'mediumvioletred': 'c71585',
  101. 'midnightblue': '191970',
  102. 'mintcream': 'f5fffa',
  103. 'mistyrose': 'ffe4e1',
  104. 'moccasin': 'ffe4b5',
  105. 'navajowhite': 'ffdead',
  106. 'navy': '000080',
  107. 'oldlace': 'fdf5e6',
  108. 'olive': '808000',
  109. 'olivedrab': '6b8e23',
  110. 'orange': 'ffa500',
  111. 'orangered': 'ff4500',
  112. 'orchid': 'da70d6',
  113. 'palegoldenrod': 'eee8aa',
  114. 'palegreen': '98fb98',
  115. 'paleturquoise': 'afeeee',
  116. 'palevioletred': 'db7093',
  117. 'papayawhip': 'ffefd5',
  118. 'peachpuff': 'ffdab9',
  119. 'peru': 'cd853f',
  120. 'pink': 'ffc0cb',
  121. 'plum': 'dda0dd',
  122. 'powderblue': 'b0e0e6',
  123. 'purple': '800080',
  124. 'rebeccapurple': '639',
  125. 'red': 'f00',
  126. 'rosybrown': 'bc8f8f',
  127. 'royalblue': '4169e1',
  128. 'saddlebrown': '8b4513',
  129. 'salmon': 'fa8072',
  130. 'sandybrown': 'f4a460',
  131. 'seagreen': '2e8b57',
  132. 'seashell': 'fff5ee',
  133. 'sienna': 'a0522d',
  134. 'silver': 'c0c0c0',
  135. 'skyblue': '87ceeb',
  136. 'slateblue': '6a5acd',
  137. 'slategray': '708090',
  138. 'slategrey': '708090',
  139. 'snow': 'fffafa',
  140. 'springgreen': '00ff7f',
  141. 'steelblue': '4682b4',
  142. 'tan': 'd2b48c',
  143. 'teal': '008080',
  144. 'thistle': 'd8bfd8',
  145. 'tomato': 'ff6347',
  146. 'turquoise': '40e0d0',
  147. 'violet': 'ee82ee',
  148. 'wheat': 'f5deb3',
  149. 'white': 'fff',
  150. 'whitesmoke': 'f5f5f5',
  151. 'yellow': 'ff0',
  152. 'yellowgreen': '9acd32'
  153. };
  154. var HEX_TO_NAME = {
  155. '800000': 'maroon',
  156. '800080': 'purple',
  157. '808000': 'olive',
  158. '808080': 'gray',
  159. '00ffff': 'cyan',
  160. 'f0ffff': 'azure',
  161. 'f5f5dc': 'beige',
  162. 'ffe4c4': 'bisque',
  163. '000000': 'black',
  164. '0000ff': 'blue',
  165. 'a52a2a': 'brown',
  166. 'ff7f50': 'coral',
  167. 'ffd700': 'gold',
  168. '008000': 'green',
  169. '4b0082': 'indigo',
  170. 'fffff0': 'ivory',
  171. 'f0e68c': 'khaki',
  172. '00ff00': 'lime',
  173. 'faf0e6': 'linen',
  174. '000080': 'navy',
  175. 'ffa500': 'orange',
  176. 'da70d6': 'orchid',
  177. 'cd853f': 'peru',
  178. 'ffc0cb': 'pink',
  179. 'dda0dd': 'plum',
  180. 'f00': 'red',
  181. 'ff0000': 'red',
  182. 'fa8072': 'salmon',
  183. 'a0522d': 'sienna',
  184. 'c0c0c0': 'silver',
  185. 'fffafa': 'snow',
  186. 'd2b48c': 'tan',
  187. '008080': 'teal',
  188. 'ff6347': 'tomato',
  189. 'ee82ee': 'violet',
  190. 'f5deb3': 'wheat',
  191. 'ffffff': 'white',
  192. 'ffff00': 'yellow'
  193. };
  194. function hueToRgb(p, q, t) {
  195. if (t < 0) {
  196. t += 1;
  197. }
  198. if (t > 1) {
  199. t -= 1;
  200. }
  201. if (t < 1 / 6) {
  202. return p + (q - p) * 6 * t;
  203. }
  204. if (t < 1 / 2) {
  205. return q;
  206. }
  207. if (t < 2 / 3) {
  208. return p + (q - p) * (2 / 3 - t) * 6;
  209. }
  210. return p;
  211. }
  212. function hslToRgb(h, s, l, a) {
  213. var r;
  214. var g;
  215. var b;
  216. if (s == 0) {
  217. r = g = b = l; // achromatic
  218. } else {
  219. var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
  220. var p = 2 * l - q;
  221. r = hueToRgb(p, q, h + 1 / 3);
  222. g = hueToRgb(p, q, h);
  223. b = hueToRgb(p, q, h - 1 / 3);
  224. }
  225. return [
  226. Math.round(r * 255),
  227. Math.round(g * 255),
  228. Math.round(b * 255),
  229. a
  230. ];
  231. }
  232. function toHex(value) {
  233. value = value.toString(16);
  234. return value.length === 1 ? '0' + value : value;
  235. }
  236. function parseFunctionArgs(functionArgs, count, rgb) {
  237. var argument = functionArgs.head;
  238. var args = [];
  239. while (argument !== null) {
  240. var argumentPart = argument.data.sequence.head;
  241. var wasValue = false;
  242. while (argumentPart !== null) {
  243. var value = argumentPart.data;
  244. var type = value.type;
  245. switch (type) {
  246. case 'Number':
  247. case 'Percentage':
  248. if (wasValue) {
  249. return;
  250. }
  251. wasValue = true;
  252. args.push({
  253. type: type,
  254. value: Number(value.value)
  255. });
  256. break;
  257. case 'Operator':
  258. if (wasValue || value.value !== '+') {
  259. return;
  260. }
  261. break;
  262. default:
  263. // something we couldn't understand
  264. return;
  265. }
  266. argumentPart = argumentPart.next;
  267. }
  268. argument = argument.next;
  269. }
  270. if (args.length !== count) {
  271. // invalid arguments count
  272. // TODO: remove those tokens
  273. return;
  274. }
  275. if (args.length === 4) {
  276. if (args[3].type !== 'Number') {
  277. // 4th argument should be a number
  278. // TODO: remove those tokens
  279. return;
  280. }
  281. args[3].type = 'Alpha';
  282. }
  283. if (rgb) {
  284. if (args[0].type !== args[1].type || args[0].type !== args[2].type) {
  285. // invalid color, numbers and percentage shouldn't be mixed
  286. // TODO: remove those tokens
  287. return;
  288. }
  289. } else {
  290. if (args[0].type !== 'Number' ||
  291. args[1].type !== 'Percentage' ||
  292. args[2].type !== 'Percentage') {
  293. // invalid color, for hsl values should be: number, percentage, percentage
  294. // TODO: remove those tokens
  295. return;
  296. }
  297. args[0].type = 'Angle';
  298. }
  299. return args.map(function(arg) {
  300. var value = Math.max(0, arg.value);
  301. switch (arg.type) {
  302. case 'Number':
  303. // fit value to [0..255] range
  304. value = Math.min(value, 255);
  305. break;
  306. case 'Percentage':
  307. // convert 0..100% to value in [0..255] range
  308. value = Math.min(value, 100) / 100;
  309. if (!rgb) {
  310. return value;
  311. }
  312. value = 255 * value;
  313. break;
  314. case 'Angle':
  315. // fit value to (-360..360) range
  316. return (((value % 360) + 360) % 360) / 360;
  317. case 'Alpha':
  318. // fit value to [0..1] range
  319. return Math.min(value, 1);
  320. }
  321. return Math.round(value);
  322. });
  323. }
  324. function compressFunction(node, item, list) {
  325. var functionName = node.name;
  326. var args;
  327. if (functionName === 'rgba' || functionName === 'hsla') {
  328. args = parseFunctionArgs(node.arguments, 4, functionName === 'rgba');
  329. if (!args) {
  330. // something went wrong
  331. return;
  332. }
  333. if (functionName === 'hsla') {
  334. args = hslToRgb.apply(null, args);
  335. node.name = 'rgba';
  336. }
  337. if (args[3] !== 1) {
  338. // replace argument values for normalized/interpolated
  339. node.arguments.each(function(argument) {
  340. var item = argument.sequence.head;
  341. if (item.data.type === 'Operator') {
  342. item = item.next;
  343. }
  344. argument.sequence = new List([{
  345. type: 'Number',
  346. info: item.data.info,
  347. value: packNumber(args.shift())
  348. }]);
  349. });
  350. return;
  351. }
  352. // otherwise convert to rgb, i.e. rgba(255, 0, 0, 1) -> rgb(255, 0, 0)
  353. functionName = 'rgb';
  354. }
  355. if (functionName === 'hsl') {
  356. args = args || parseFunctionArgs(node.arguments, 3, false);
  357. if (!args) {
  358. // something went wrong
  359. return;
  360. }
  361. // convert to rgb
  362. args = hslToRgb.apply(null, args);
  363. functionName = 'rgb';
  364. }
  365. if (functionName === 'rgb') {
  366. args = args || parseFunctionArgs(node.arguments, 3, true);
  367. if (!args) {
  368. // something went wrong
  369. return;
  370. }
  371. // check if color is not at the end and not followed by space
  372. var next = item.next;
  373. if (next && next.data.type !== 'Space') {
  374. list.insert(list.createItem({
  375. type: 'Space'
  376. }), next);
  377. }
  378. item.data = {
  379. type: 'Hash',
  380. info: node.info,
  381. value: toHex(args[0]) + toHex(args[1]) + toHex(args[2])
  382. };
  383. compressHex(item.data, item);
  384. }
  385. }
  386. function compressIdent(node, item) {
  387. if (this.declaration === null) {
  388. return;
  389. }
  390. var color = node.name.toLowerCase();
  391. if (NAME_TO_HEX.hasOwnProperty(color)) {
  392. var hex = NAME_TO_HEX[color];
  393. if (hex.length + 1 <= color.length) {
  394. // replace for shorter hex value
  395. item.data = {
  396. type: 'Hash',
  397. info: node.info,
  398. value: hex
  399. };
  400. } else {
  401. // special case for consistent colors
  402. if (color === 'grey') {
  403. color = 'gray';
  404. }
  405. // just replace value for lower cased name
  406. node.name = color;
  407. }
  408. }
  409. }
  410. function compressHex(node, item) {
  411. var color = node.value.toLowerCase();
  412. // #112233 -> #123
  413. if (color.length === 6 &&
  414. color[0] === color[1] &&
  415. color[2] === color[3] &&
  416. color[4] === color[5]) {
  417. color = color[0] + color[2] + color[4];
  418. }
  419. if (HEX_TO_NAME[color]) {
  420. item.data = {
  421. type: 'Identifier',
  422. info: node.info,
  423. name: HEX_TO_NAME[color]
  424. };
  425. } else {
  426. node.value = color;
  427. }
  428. }
  429. module.exports = {
  430. compressFunction: compressFunction,
  431. compressIdent: compressIdent,
  432. compressHex: compressHex
  433. };