color-bundle

Color conversion and manipulation with CSS string support (bundle)

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/387811/719499/color-bundle.js

  1. // Modified bundle of github.com/Qix-/color v3.1.2
  2. var Color = (function () {
  3. 'use strict';
  4.  
  5. var cssKeywords = {
  6. "aliceblue": [240, 248, 255],
  7. "antiquewhite": [250, 235, 215],
  8. "aqua": [0, 255, 255],
  9. "aquamarine": [127, 255, 212],
  10. "azure": [240, 255, 255],
  11. "beige": [245, 245, 220],
  12. "bisque": [255, 228, 196],
  13. "black": [0, 0, 0],
  14. "blanchedalmond": [255, 235, 205],
  15. "blue": [0, 0, 255],
  16. "blueviolet": [138, 43, 226],
  17. "brown": [165, 42, 42],
  18. "burlywood": [222, 184, 135],
  19. "cadetblue": [95, 158, 160],
  20. "chartreuse": [127, 255, 0],
  21. "chocolate": [210, 105, 30],
  22. "coral": [255, 127, 80],
  23. "cornflowerblue": [100, 149, 237],
  24. "cornsilk": [255, 248, 220],
  25. "crimson": [220, 20, 60],
  26. "cyan": [0, 255, 255],
  27. "darkblue": [0, 0, 139],
  28. "darkcyan": [0, 139, 139],
  29. "darkgoldenrod": [184, 134, 11],
  30. "darkgray": [169, 169, 169],
  31. "darkgreen": [0, 100, 0],
  32. "darkgrey": [169, 169, 169],
  33. "darkkhaki": [189, 183, 107],
  34. "darkmagenta": [139, 0, 139],
  35. "darkolivegreen": [85, 107, 47],
  36. "darkorange": [255, 140, 0],
  37. "darkorchid": [153, 50, 204],
  38. "darkred": [139, 0, 0],
  39. "darksalmon": [233, 150, 122],
  40. "darkseagreen": [143, 188, 143],
  41. "darkslateblue": [72, 61, 139],
  42. "darkslategray": [47, 79, 79],
  43. "darkslategrey": [47, 79, 79],
  44. "darkturquoise": [0, 206, 209],
  45. "darkviolet": [148, 0, 211],
  46. "deeppink": [255, 20, 147],
  47. "deepskyblue": [0, 191, 255],
  48. "dimgray": [105, 105, 105],
  49. "dimgrey": [105, 105, 105],
  50. "dodgerblue": [30, 144, 255],
  51. "firebrick": [178, 34, 34],
  52. "floralwhite": [255, 250, 240],
  53. "forestgreen": [34, 139, 34],
  54. "fuchsia": [255, 0, 255],
  55. "gainsboro": [220, 220, 220],
  56. "ghostwhite": [248, 248, 255],
  57. "gold": [255, 215, 0],
  58. "goldenrod": [218, 165, 32],
  59. "gray": [128, 128, 128],
  60. "green": [0, 128, 0],
  61. "greenyellow": [173, 255, 47],
  62. "grey": [128, 128, 128],
  63. "honeydew": [240, 255, 240],
  64. "hotpink": [255, 105, 180],
  65. "indianred": [205, 92, 92],
  66. "indigo": [75, 0, 130],
  67. "ivory": [255, 255, 240],
  68. "khaki": [240, 230, 140],
  69. "lavender": [230, 230, 250],
  70. "lavenderblush": [255, 240, 245],
  71. "lawngreen": [124, 252, 0],
  72. "lemonchiffon": [255, 250, 205],
  73. "lightblue": [173, 216, 230],
  74. "lightcoral": [240, 128, 128],
  75. "lightcyan": [224, 255, 255],
  76. "lightgoldenrodyellow": [250, 250, 210],
  77. "lightgray": [211, 211, 211],
  78. "lightgreen": [144, 238, 144],
  79. "lightgrey": [211, 211, 211],
  80. "lightpink": [255, 182, 193],
  81. "lightsalmon": [255, 160, 122],
  82. "lightseagreen": [32, 178, 170],
  83. "lightskyblue": [135, 206, 250],
  84. "lightslategray": [119, 136, 153],
  85. "lightslategrey": [119, 136, 153],
  86. "lightsteelblue": [176, 196, 222],
  87. "lightyellow": [255, 255, 224],
  88. "lime": [0, 255, 0],
  89. "limegreen": [50, 205, 50],
  90. "linen": [250, 240, 230],
  91. "magenta": [255, 0, 255],
  92. "maroon": [128, 0, 0],
  93. "mediumaquamarine": [102, 205, 170],
  94. "mediumblue": [0, 0, 205],
  95. "mediumorchid": [186, 85, 211],
  96. "mediumpurple": [147, 112, 219],
  97. "mediumseagreen": [60, 179, 113],
  98. "mediumslateblue": [123, 104, 238],
  99. "mediumspringgreen": [0, 250, 154],
  100. "mediumturquoise": [72, 209, 204],
  101. "mediumvioletred": [199, 21, 133],
  102. "midnightblue": [25, 25, 112],
  103. "mintcream": [245, 255, 250],
  104. "mistyrose": [255, 228, 225],
  105. "moccasin": [255, 228, 181],
  106. "navajowhite": [255, 222, 173],
  107. "navy": [0, 0, 128],
  108. "oldlace": [253, 245, 230],
  109. "olive": [128, 128, 0],
  110. "olivedrab": [107, 142, 35],
  111. "orange": [255, 165, 0],
  112. "orangered": [255, 69, 0],
  113. "orchid": [218, 112, 214],
  114. "palegoldenrod": [238, 232, 170],
  115. "palegreen": [152, 251, 152],
  116. "paleturquoise": [175, 238, 238],
  117. "palevioletred": [219, 112, 147],
  118. "papayawhip": [255, 239, 213],
  119. "peachpuff": [255, 218, 185],
  120. "peru": [205, 133, 63],
  121. "pink": [255, 192, 203],
  122. "plum": [221, 160, 221],
  123. "powderblue": [176, 224, 230],
  124. "purple": [128, 0, 128],
  125. "rebeccapurple": [102, 51, 153],
  126. "red": [255, 0, 0],
  127. "rosybrown": [188, 143, 143],
  128. "royalblue": [65, 105, 225],
  129. "saddlebrown": [139, 69, 19],
  130. "salmon": [250, 128, 114],
  131. "sandybrown": [244, 164, 96],
  132. "seagreen": [46, 139, 87],
  133. "seashell": [255, 245, 238],
  134. "sienna": [160, 82, 45],
  135. "silver": [192, 192, 192],
  136. "skyblue": [135, 206, 235],
  137. "slateblue": [106, 90, 205],
  138. "slategray": [112, 128, 144],
  139. "slategrey": [112, 128, 144],
  140. "snow": [255, 250, 250],
  141. "springgreen": [0, 255, 127],
  142. "steelblue": [70, 130, 180],
  143. "tan": [210, 180, 140],
  144. "teal": [0, 128, 128],
  145. "thistle": [216, 191, 216],
  146. "tomato": [255, 99, 71],
  147. "turquoise": [64, 224, 208],
  148. "violet": [238, 130, 238],
  149. "wheat": [245, 222, 179],
  150. "white": [255, 255, 255],
  151. "whitesmoke": [245, 245, 245],
  152. "yellow": [255, 255, 0],
  153. "yellowgreen": [154, 205, 50]
  154. };
  155.  
  156. var isArrayish = (function isArrayish(obj) {
  157. if (!obj || typeof obj === 'string') {
  158. return false;
  159. }
  160.  
  161. return obj instanceof Array || Array.isArray(obj) ||
  162. (obj.length >= 0 && (obj.splice instanceof Function ||
  163. (Object.getOwnPropertyDescriptor(obj, (obj.length - 1)) && obj.constructor.name !== 'String')));
  164. });
  165.  
  166. var concat = Array.prototype.concat;
  167. var slice = Array.prototype.slice;
  168.  
  169. var swizzle = function swizzle(args) {
  170. var results = [];
  171.  
  172. for (var i = 0, len = args.length; i < len; i++) {
  173. var arg = args[i];
  174.  
  175. if (isArrayish(arg)) {
  176. // http://jsperf.com/javascript-array-concat-vs-push/98
  177. results = concat.call(results, slice.call(arg));
  178. } else {
  179. results.push(arg);
  180. }
  181. }
  182.  
  183. return results;
  184. };
  185.  
  186. swizzle.wrap = function (fn) {
  187. return function () {
  188. return fn(swizzle(arguments));
  189. };
  190. };
  191.  
  192. /* MIT license */
  193.  
  194. var reverseNames = {};
  195.  
  196. // create a list of reverse color names
  197. for (var name in cssKeywords) {
  198. if (cssKeywords.hasOwnProperty(name)) {
  199. reverseNames[cssKeywords[name]] = name;
  200. }
  201. }
  202.  
  203. var cs = {
  204. to: {},
  205. get: {}
  206. };
  207.  
  208. cs.get = function (string) {
  209. var prefix = string.substring(0, 3).toLowerCase();
  210. var val;
  211. var model;
  212. switch (prefix) {
  213. case 'hsl':
  214. val = cs.get.hsl(string);
  215. model = 'hsl';
  216. break;
  217. case 'hwb':
  218. val = cs.get.hwb(string);
  219. model = 'hwb';
  220. break;
  221. default:
  222. val = cs.get.rgb(string);
  223. model = 'rgb';
  224. break;
  225. }
  226.  
  227. if (!val) {
  228. return null;
  229. }
  230.  
  231. return {model: model, value: val};
  232. };
  233.  
  234. cs.get.rgb = function (string) {
  235. if (!string) {
  236. return null;
  237. }
  238.  
  239. var abbr = /^#([a-f0-9]{3,4})$/i;
  240. var hex = /^#([a-f0-9]{6})([a-f0-9]{2})?$/i;
  241. var rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/;
  242. var per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/;
  243. var keyword = /(\D+)/;
  244.  
  245. var rgb = [0, 0, 0, 1];
  246. var match;
  247. var i;
  248. var hexAlpha;
  249.  
  250. if (match = string.match(hex)) {
  251. hexAlpha = match[2];
  252. match = match[1];
  253.  
  254. for (i = 0; i < 3; i++) {
  255. // https://jsperf.com/slice-vs-substr-vs-substring-methods-long-string/19
  256. var i2 = i * 2;
  257. rgb[i] = parseInt(match.slice(i2, i2 + 2), 16);
  258. }
  259.  
  260. if (hexAlpha) {
  261. rgb[3] = Math.round((parseInt(hexAlpha, 16) / 255) * 100) / 100;
  262. }
  263. } else if (match = string.match(abbr)) {
  264. match = match[1];
  265. hexAlpha = match[3];
  266.  
  267. for (i = 0; i < 3; i++) {
  268. rgb[i] = parseInt(match[i] + match[i], 16);
  269. }
  270.  
  271. if (hexAlpha) {
  272. rgb[3] = Math.round((parseInt(hexAlpha + hexAlpha, 16) / 255) * 100) / 100;
  273. }
  274. } else if (match = string.match(rgba)) {
  275. for (i = 0; i < 3; i++) {
  276. rgb[i] = parseInt(match[i + 1], 0);
  277. }
  278.  
  279. if (match[4]) {
  280. rgb[3] = parseFloat(match[4]);
  281. }
  282. } else if (match = string.match(per)) {
  283. for (i = 0; i < 3; i++) {
  284. rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
  285. }
  286.  
  287. if (match[4]) {
  288. rgb[3] = parseFloat(match[4]);
  289. }
  290. } else if (match = string.match(keyword)) {
  291. if (match[1] === 'transparent') {
  292. return [0, 0, 0, 0];
  293. }
  294.  
  295. rgb = cssKeywords[match[1]];
  296.  
  297. if (!rgb) {
  298. return null;
  299. }
  300.  
  301. rgb[3] = 1;
  302.  
  303. return rgb;
  304. } else {
  305. return null;
  306. }
  307.  
  308. for (i = 0; i < 3; i++) {
  309. rgb[i] = clamp(rgb[i], 0, 255);
  310. }
  311. rgb[3] = clamp(rgb[3], 0, 1);
  312.  
  313. return rgb;
  314. };
  315.  
  316. cs.get.hsl = function (string) {
  317. if (!string) {
  318. return null;
  319. }
  320.  
  321. var hsl = /^hsla?\(\s*([+-]?(?:\d*\.)?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/;
  322. var match = string.match(hsl);
  323.  
  324. if (match) {
  325. var alpha = parseFloat(match[4]);
  326. var h = (parseFloat(match[1]) + 360) % 360;
  327. var s = clamp(parseFloat(match[2]), 0, 100);
  328. var l = clamp(parseFloat(match[3]), 0, 100);
  329. var a = clamp(isNaN(alpha) ? 1 : alpha, 0, 1);
  330.  
  331. return [h, s, l, a];
  332. }
  333.  
  334. return null;
  335. };
  336.  
  337. cs.get.hwb = function (string) {
  338. if (!string) {
  339. return null;
  340. }
  341.  
  342. var hwb = /^hwb\(\s*([+-]?\d*[\.]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/;
  343. var match = string.match(hwb);
  344.  
  345. if (match) {
  346. var alpha = parseFloat(match[4]);
  347. var h = ((parseFloat(match[1]) % 360) + 360) % 360;
  348. var w = clamp(parseFloat(match[2]), 0, 100);
  349. var b = clamp(parseFloat(match[3]), 0, 100);
  350. var a = clamp(isNaN(alpha) ? 1 : alpha, 0, 1);
  351. return [h, w, b, a];
  352. }
  353.  
  354. return null;
  355. };
  356.  
  357. cs.to.hex = function () {
  358. var rgba = swizzle(arguments);
  359.  
  360. return (
  361. '#' +
  362. hexDouble(rgba[0]) +
  363. hexDouble(rgba[1]) +
  364. hexDouble(rgba[2]) +
  365. (rgba[3] < 1
  366. ? (hexDouble(Math.round(rgba[3] * 255)))
  367. : '')
  368. );
  369. };
  370.  
  371. cs.to.rgb = function () {
  372. var rgba = swizzle(arguments);
  373.  
  374. return rgba.length < 4 || rgba[3] === 1
  375. ? 'rgb(' + Math.round(rgba[0]) + ', ' + Math.round(rgba[1]) + ', ' + Math.round(rgba[2]) + ')'
  376. : 'rgba(' + Math.round(rgba[0]) + ', ' + Math.round(rgba[1]) + ', ' + Math.round(rgba[2]) + ', ' + rgba[3] + ')';
  377. };
  378.  
  379. cs.to.rgb.percent = function () {
  380. var rgba = swizzle(arguments);
  381.  
  382. var r = Math.round(rgba[0] / 255 * 100);
  383. var g = Math.round(rgba[1] / 255 * 100);
  384. var b = Math.round(rgba[2] / 255 * 100);
  385.  
  386. return rgba.length < 4 || rgba[3] === 1
  387. ? 'rgb(' + r + '%, ' + g + '%, ' + b + '%)'
  388. : 'rgba(' + r + '%, ' + g + '%, ' + b + '%, ' + rgba[3] + ')';
  389. };
  390.  
  391. cs.to.hsl = function () {
  392. var hsla = swizzle(arguments);
  393. return hsla.length < 4 || hsla[3] === 1
  394. ? 'hsl(' + hsla[0] + ', ' + hsla[1] + '%, ' + hsla[2] + '%)'
  395. : 'hsla(' + hsla[0] + ', ' + hsla[1] + '%, ' + hsla[2] + '%, ' + hsla[3] + ')';
  396. };
  397.  
  398. // hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax
  399. // (hwb have alpha optional & 1 is default value)
  400. cs.to.hwb = function () {
  401. var hwba = swizzle(arguments);
  402.  
  403. var a = '';
  404. if (hwba.length >= 4 && hwba[3] !== 1) {
  405. a = ', ' + hwba[3];
  406. }
  407.  
  408. return 'hwb(' + hwba[0] + ', ' + hwba[1] + '%, ' + hwba[2] + '%' + a + ')';
  409. };
  410.  
  411. cs.to.keyword = function (rgb) {
  412. return reverseNames[rgb.slice(0, 3)];
  413. };
  414.  
  415. // helpers
  416. function clamp(num, min, max) {
  417. return Math.min(Math.max(min, num), max);
  418. }
  419.  
  420. function hexDouble(num) {
  421. var str = num.toString(16).toUpperCase();
  422. return (str.length < 2) ? '0' + str : str;
  423. }
  424.  
  425. /* MIT license */
  426.  
  427. // NOTE: conversions should only return primitive values (i.e. arrays, or
  428. // values that give correct `typeof` results).
  429. // do not use box values types (i.e. Number(), String(), etc.)
  430.  
  431. var reverseKeywords = {};
  432. for (var key in cssKeywords) {
  433. if (cssKeywords.hasOwnProperty(key)) {
  434. reverseKeywords[cssKeywords[key]] = key;
  435. }
  436. }
  437.  
  438. var convert = {
  439. rgb: {channels: 3, labels: 'rgb'},
  440. hsl: {channels: 3, labels: 'hsl'},
  441. hsv: {channels: 3, labels: 'hsv'},
  442. hwb: {channels: 3, labels: 'hwb'},
  443. cmyk: {channels: 4, labels: 'cmyk'},
  444. xyz: {channels: 3, labels: 'xyz'},
  445. lab: {channels: 3, labels: 'lab'},
  446. lch: {channels: 3, labels: 'lch'},
  447. hex: {channels: 1, labels: ['hex']},
  448. keyword: {channels: 1, labels: ['keyword']},
  449. ansi16: {channels: 1, labels: ['ansi16']},
  450. ansi256: {channels: 1, labels: ['ansi256']},
  451. hcg: {channels: 3, labels: ['h', 'c', 'g']},
  452. apple: {channels: 3, labels: ['r16', 'g16', 'b16']},
  453. gray: {channels: 1, labels: ['gray']}
  454. };
  455.  
  456. // hide .channels and .labels properties
  457. for (var model in convert) {
  458. if (convert.hasOwnProperty(model)) {
  459. if (!('channels' in convert[model])) {
  460. throw new Error('missing channels property: ' + model);
  461. }
  462.  
  463. if (!('labels' in convert[model])) {
  464. throw new Error('missing channel labels property: ' + model);
  465. }
  466.  
  467. if (convert[model].labels.length !== convert[model].channels) {
  468. throw new Error('channel and label counts mismatch: ' + model);
  469. }
  470.  
  471. var channels = convert[model].channels;
  472. var labels = convert[model].labels;
  473. delete convert[model].channels;
  474. delete convert[model].labels;
  475. Object.defineProperty(convert[model], 'channels', {value: channels});
  476. Object.defineProperty(convert[model], 'labels', {value: labels});
  477. }
  478. }
  479.  
  480. convert.rgb.hsl = function (rgb) {
  481. var r = rgb[0] / 255;
  482. var g = rgb[1] / 255;
  483. var b = rgb[2] / 255;
  484. var min = Math.min(r, g, b);
  485. var max = Math.max(r, g, b);
  486. var delta = max - min;
  487. var h;
  488. var s;
  489. var l;
  490.  
  491. if (max === min) {
  492. h = 0;
  493. } else if (r === max) {
  494. h = (g - b) / delta;
  495. } else if (g === max) {
  496. h = 2 + (b - r) / delta;
  497. } else if (b === max) {
  498. h = 4 + (r - g) / delta;
  499. }
  500.  
  501. h = Math.min(h * 60, 360);
  502.  
  503. if (h < 0) {
  504. h += 360;
  505. }
  506.  
  507. l = (min + max) / 2;
  508.  
  509. if (max === min) {
  510. s = 0;
  511. } else if (l <= 0.5) {
  512. s = delta / (max + min);
  513. } else {
  514. s = delta / (2 - max - min);
  515. }
  516.  
  517. return [h, s * 100, l * 100];
  518. };
  519.  
  520. convert.rgb.hsv = function (rgb) {
  521. var rdif;
  522. var gdif;
  523. var bdif;
  524. var h;
  525. var s;
  526.  
  527. var r = rgb[0] / 255;
  528. var g = rgb[1] / 255;
  529. var b = rgb[2] / 255;
  530. var v = Math.max(r, g, b);
  531. var diff = v - Math.min(r, g, b);
  532. var diffc = function (c) {
  533. return (v - c) / 6 / diff + 1 / 2;
  534. };
  535.  
  536. if (diff === 0) {
  537. h = s = 0;
  538. } else {
  539. s = diff / v;
  540. rdif = diffc(r);
  541. gdif = diffc(g);
  542. bdif = diffc(b);
  543.  
  544. if (r === v) {
  545. h = bdif - gdif;
  546. } else if (g === v) {
  547. h = (1 / 3) + rdif - bdif;
  548. } else if (b === v) {
  549. h = (2 / 3) + gdif - rdif;
  550. }
  551. if (h < 0) {
  552. h += 1;
  553. } else if (h > 1) {
  554. h -= 1;
  555. }
  556. }
  557.  
  558. return [
  559. h * 360,
  560. s * 100,
  561. v * 100
  562. ];
  563. };
  564.  
  565. convert.rgb.hwb = function (rgb) {
  566. var r = rgb[0];
  567. var g = rgb[1];
  568. var b = rgb[2];
  569. var h = convert.rgb.hsl(rgb)[0];
  570. var w = 1 / 255 * Math.min(r, Math.min(g, b));
  571.  
  572. b = 1 - 1 / 255 * Math.max(r, Math.max(g, b));
  573.  
  574. return [h, w * 100, b * 100];
  575. };
  576.  
  577. convert.rgb.cmyk = function (rgb) {
  578. var r = rgb[0] / 255;
  579. var g = rgb[1] / 255;
  580. var b = rgb[2] / 255;
  581. var c;
  582. var m;
  583. var y;
  584. var k;
  585.  
  586. k = Math.min(1 - r, 1 - g, 1 - b);
  587. c = (1 - r - k) / (1 - k) || 0;
  588. m = (1 - g - k) / (1 - k) || 0;
  589. y = (1 - b - k) / (1 - k) || 0;
  590.  
  591. return [c * 100, m * 100, y * 100, k * 100];
  592. };
  593.  
  594. /**
  595. * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance
  596. * */
  597. function comparativeDistance(x, y) {
  598. return (
  599. Math.pow(x[0] - y[0], 2) +
  600. Math.pow(x[1] - y[1], 2) +
  601. Math.pow(x[2] - y[2], 2)
  602. );
  603. }
  604.  
  605. convert.rgb.keyword = function (rgb) {
  606. var reversed = reverseKeywords[rgb];
  607. if (reversed) {
  608. return reversed;
  609. }
  610.  
  611. var currentClosestDistance = Infinity;
  612. var currentClosestKeyword;
  613.  
  614. for (var keyword in cssKeywords) {
  615. if (cssKeywords.hasOwnProperty(keyword)) {
  616. var value = cssKeywords[keyword];
  617.  
  618. // Compute comparative distance
  619. var distance = comparativeDistance(rgb, value);
  620.  
  621. // Check if its less, if so set as closest
  622. if (distance < currentClosestDistance) {
  623. currentClosestDistance = distance;
  624. currentClosestKeyword = keyword;
  625. }
  626. }
  627. }
  628.  
  629. return currentClosestKeyword;
  630. };
  631.  
  632. convert.keyword.rgb = function (keyword) {
  633. return cssKeywords[keyword];
  634. };
  635.  
  636. convert.rgb.xyz = function (rgb) {
  637. var r = rgb[0] / 255;
  638. var g = rgb[1] / 255;
  639. var b = rgb[2] / 255;
  640.  
  641. // assume sRGB
  642. r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);
  643. g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);
  644. b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);
  645.  
  646. var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
  647. var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
  648. var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);
  649.  
  650. return [x * 100, y * 100, z * 100];
  651. };
  652.  
  653. convert.rgb.lab = function (rgb) {
  654. var xyz = convert.rgb.xyz(rgb);
  655. var x = xyz[0];
  656. var y = xyz[1];
  657. var z = xyz[2];
  658. var l;
  659. var a;
  660. var b;
  661.  
  662. x /= 95.047;
  663. y /= 100;
  664. z /= 108.883;
  665.  
  666. x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);
  667. y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);
  668. z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);
  669.  
  670. l = (116 * y) - 16;
  671. a = 500 * (x - y);
  672. b = 200 * (y - z);
  673.  
  674. return [l, a, b];
  675. };
  676.  
  677. convert.hsl.rgb = function (hsl) {
  678. var h = hsl[0] / 360;
  679. var s = hsl[1] / 100;
  680. var l = hsl[2] / 100;
  681. var t1;
  682. var t2;
  683. var t3;
  684. var rgb;
  685. var val;
  686.  
  687. if (s === 0) {
  688. val = l * 255;
  689. return [val, val, val];
  690. }
  691.  
  692. if (l < 0.5) {
  693. t2 = l * (1 + s);
  694. } else {
  695. t2 = l + s - l * s;
  696. }
  697.  
  698. t1 = 2 * l - t2;
  699.  
  700. rgb = [0, 0, 0];
  701. for (var i = 0; i < 3; i++) {
  702. t3 = h + 1 / 3 * -(i - 1);
  703. if (t3 < 0) {
  704. t3++;
  705. }
  706. if (t3 > 1) {
  707. t3--;
  708. }
  709.  
  710. if (6 * t3 < 1) {
  711. val = t1 + (t2 - t1) * 6 * t3;
  712. } else if (2 * t3 < 1) {
  713. val = t2;
  714. } else if (3 * t3 < 2) {
  715. val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
  716. } else {
  717. val = t1;
  718. }
  719.  
  720. rgb[i] = val * 255;
  721. }
  722.  
  723. return rgb;
  724. };
  725.  
  726. convert.hsl.hsv = function (hsl) {
  727. var h = hsl[0];
  728. var s = hsl[1] / 100;
  729. var l = hsl[2] / 100;
  730. var smin = s;
  731. var lmin = Math.max(l, 0.01);
  732. var sv;
  733. var v;
  734.  
  735. l *= 2;
  736. s *= (l <= 1) ? l : 2 - l;
  737. smin *= lmin <= 1 ? lmin : 2 - lmin;
  738. v = (l + s) / 2;
  739. sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s);
  740.  
  741. return [h, sv * 100, v * 100];
  742. };
  743.  
  744. convert.hsv.rgb = function (hsv) {
  745. var h = hsv[0] / 60;
  746. var s = hsv[1] / 100;
  747. var v = hsv[2] / 100;
  748. var hi = Math.floor(h) % 6;
  749.  
  750. var f = h - Math.floor(h);
  751. var p = 255 * v * (1 - s);
  752. var q = 255 * v * (1 - (s * f));
  753. var t = 255 * v * (1 - (s * (1 - f)));
  754. v *= 255;
  755.  
  756. switch (hi) {
  757. case 0:
  758. return [v, t, p];
  759. case 1:
  760. return [q, v, p];
  761. case 2:
  762. return [p, v, t];
  763. case 3:
  764. return [p, q, v];
  765. case 4:
  766. return [t, p, v];
  767. case 5:
  768. return [v, p, q];
  769. }
  770. };
  771.  
  772. convert.hsv.hsl = function (hsv) {
  773. var h = hsv[0];
  774. var s = hsv[1] / 100;
  775. var v = hsv[2] / 100;
  776. var vmin = Math.max(v, 0.01);
  777. var lmin;
  778. var sl;
  779. var l;
  780.  
  781. l = (2 - s) * v;
  782. lmin = (2 - s) * vmin;
  783. sl = s * vmin;
  784. sl /= (lmin <= 1) ? lmin : 2 - lmin;
  785. sl = sl || 0;
  786. l /= 2;
  787.  
  788. return [h, sl * 100, l * 100];
  789. };
  790.  
  791. // http://dev.w3.org/csswg/css-color/#hwb-to-rgb
  792. convert.hwb.rgb = function (hwb) {
  793. var h = hwb[0] / 360;
  794. var wh = hwb[1] / 100;
  795. var bl = hwb[2] / 100;
  796. var ratio = wh + bl;
  797. var i;
  798. var v;
  799. var f;
  800. var n;
  801.  
  802. // wh + bl cant be > 1
  803. if (ratio > 1) {
  804. wh /= ratio;
  805. bl /= ratio;
  806. }
  807.  
  808. i = Math.floor(6 * h);
  809. v = 1 - bl;
  810. f = 6 * h - i;
  811.  
  812. if ((i & 0x01) !== 0) {
  813. f = 1 - f;
  814. }
  815.  
  816. n = wh + f * (v - wh); // linear interpolation
  817.  
  818. var r;
  819. var g;
  820. var b;
  821. switch (i) {
  822. default:
  823. case 6:
  824. case 0: r = v; g = n; b = wh; break;
  825. case 1: r = n; g = v; b = wh; break;
  826. case 2: r = wh; g = v; b = n; break;
  827. case 3: r = wh; g = n; b = v; break;
  828. case 4: r = n; g = wh; b = v; break;
  829. case 5: r = v; g = wh; b = n; break;
  830. }
  831.  
  832. return [r * 255, g * 255, b * 255];
  833. };
  834.  
  835. convert.cmyk.rgb = function (cmyk) {
  836. var c = cmyk[0] / 100;
  837. var m = cmyk[1] / 100;
  838. var y = cmyk[2] / 100;
  839. var k = cmyk[3] / 100;
  840. var r;
  841. var g;
  842. var b;
  843.  
  844. r = 1 - Math.min(1, c * (1 - k) + k);
  845. g = 1 - Math.min(1, m * (1 - k) + k);
  846. b = 1 - Math.min(1, y * (1 - k) + k);
  847.  
  848. return [r * 255, g * 255, b * 255];
  849. };
  850.  
  851. convert.xyz.rgb = function (xyz) {
  852. var x = xyz[0] / 100;
  853. var y = xyz[1] / 100;
  854. var z = xyz[2] / 100;
  855. var r;
  856. var g;
  857. var b;
  858.  
  859. r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
  860. g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
  861. b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);
  862.  
  863. // assume sRGB
  864. r = r > 0.0031308
  865. ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
  866. : r * 12.92;
  867.  
  868. g = g > 0.0031308
  869. ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
  870. : g * 12.92;
  871.  
  872. b = b > 0.0031308
  873. ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
  874. : b * 12.92;
  875.  
  876. r = Math.min(Math.max(0, r), 1);
  877. g = Math.min(Math.max(0, g), 1);
  878. b = Math.min(Math.max(0, b), 1);
  879.  
  880. return [r * 255, g * 255, b * 255];
  881. };
  882.  
  883. convert.xyz.lab = function (xyz) {
  884. var x = xyz[0];
  885. var y = xyz[1];
  886. var z = xyz[2];
  887. var l;
  888. var a;
  889. var b;
  890.  
  891. x /= 95.047;
  892. y /= 100;
  893. z /= 108.883;
  894.  
  895. x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);
  896. y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);
  897. z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);
  898.  
  899. l = (116 * y) - 16;
  900. a = 500 * (x - y);
  901. b = 200 * (y - z);
  902.  
  903. return [l, a, b];
  904. };
  905.  
  906. convert.lab.xyz = function (lab) {
  907. var l = lab[0];
  908. var a = lab[1];
  909. var b = lab[2];
  910. var x;
  911. var y;
  912. var z;
  913.  
  914. y = (l + 16) / 116;
  915. x = a / 500 + y;
  916. z = y - b / 200;
  917.  
  918. var y2 = Math.pow(y, 3);
  919. var x2 = Math.pow(x, 3);
  920. var z2 = Math.pow(z, 3);
  921. y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787;
  922. x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787;
  923. z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787;
  924.  
  925. x *= 95.047;
  926. y *= 100;
  927. z *= 108.883;
  928.  
  929. return [x, y, z];
  930. };
  931.  
  932. convert.lab.lch = function (lab) {
  933. var l = lab[0];
  934. var a = lab[1];
  935. var b = lab[2];
  936. var hr;
  937. var h;
  938. var c;
  939.  
  940. hr = Math.atan2(b, a);
  941. h = hr * 360 / 2 / Math.PI;
  942.  
  943. if (h < 0) {
  944. h += 360;
  945. }
  946.  
  947. c = Math.sqrt(a * a + b * b);
  948.  
  949. return [l, c, h];
  950. };
  951.  
  952. convert.lch.lab = function (lch) {
  953. var l = lch[0];
  954. var c = lch[1];
  955. var h = lch[2];
  956. var a;
  957. var b;
  958. var hr;
  959.  
  960. hr = h / 360 * 2 * Math.PI;
  961. a = c * Math.cos(hr);
  962. b = c * Math.sin(hr);
  963.  
  964. return [l, a, b];
  965. };
  966.  
  967. convert.rgb.ansi16 = function (args) {
  968. var r = args[0];
  969. var g = args[1];
  970. var b = args[2];
  971. var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization
  972.  
  973. value = Math.round(value / 50);
  974.  
  975. if (value === 0) {
  976. return 30;
  977. }
  978.  
  979. var ansi = 30
  980. + ((Math.round(b / 255) << 2)
  981. | (Math.round(g / 255) << 1)
  982. | Math.round(r / 255));
  983.  
  984. if (value === 2) {
  985. ansi += 60;
  986. }
  987.  
  988. return ansi;
  989. };
  990.  
  991. convert.hsv.ansi16 = function (args) {
  992. // optimization here; we already know the value and don't need to get
  993. // it converted for us.
  994. return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]);
  995. };
  996.  
  997. convert.rgb.ansi256 = function (args) {
  998. var r = args[0];
  999. var g = args[1];
  1000. var b = args[2];
  1001.  
  1002. // we use the extended greyscale palette here, with the exception of
  1003. // black and white. normal palette only has 4 greyscale shades.
  1004. if (r === g && g === b) {
  1005. if (r < 8) {
  1006. return 16;
  1007. }
  1008.  
  1009. if (r > 248) {
  1010. return 231;
  1011. }
  1012.  
  1013. return Math.round(((r - 8) / 247) * 24) + 232;
  1014. }
  1015.  
  1016. var ansi = 16
  1017. + (36 * Math.round(r / 255 * 5))
  1018. + (6 * Math.round(g / 255 * 5))
  1019. + Math.round(b / 255 * 5);
  1020.  
  1021. return ansi;
  1022. };
  1023.  
  1024. convert.ansi16.rgb = function (args) {
  1025. var color = args % 10;
  1026.  
  1027. // handle greyscale
  1028. if (color === 0 || color === 7) {
  1029. if (args > 50) {
  1030. color += 3.5;
  1031. }
  1032.  
  1033. color = color / 10.5 * 255;
  1034.  
  1035. return [color, color, color];
  1036. }
  1037.  
  1038. var mult = (~~(args > 50) + 1) * 0.5;
  1039. var r = ((color & 1) * mult) * 255;
  1040. var g = (((color >> 1) & 1) * mult) * 255;
  1041. var b = (((color >> 2) & 1) * mult) * 255;
  1042.  
  1043. return [r, g, b];
  1044. };
  1045.  
  1046. convert.ansi256.rgb = function (args) {
  1047. // handle greyscale
  1048. if (args >= 232) {
  1049. var c = (args - 232) * 10 + 8;
  1050. return [c, c, c];
  1051. }
  1052.  
  1053. args -= 16;
  1054.  
  1055. var rem;
  1056. var r = Math.floor(args / 36) / 5 * 255;
  1057. var g = Math.floor((rem = args % 36) / 6) / 5 * 255;
  1058. var b = (rem % 6) / 5 * 255;
  1059.  
  1060. return [r, g, b];
  1061. };
  1062.  
  1063. convert.rgb.hex = function (args) {
  1064. var integer = ((Math.round(args[0]) & 0xFF) << 16)
  1065. + ((Math.round(args[1]) & 0xFF) << 8)
  1066. + (Math.round(args[2]) & 0xFF);
  1067.  
  1068. var string = integer.toString(16).toUpperCase();
  1069. return '000000'.substring(string.length) + string;
  1070. };
  1071.  
  1072. convert.hex.rgb = function (args) {
  1073. var match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);
  1074. if (!match) {
  1075. return [0, 0, 0];
  1076. }
  1077.  
  1078. var colorString = match[0];
  1079.  
  1080. if (match[0].length === 3) {
  1081. colorString = colorString.split('').map(function (char) {
  1082. return char + char;
  1083. }).join('');
  1084. }
  1085.  
  1086. var integer = parseInt(colorString, 16);
  1087. var r = (integer >> 16) & 0xFF;
  1088. var g = (integer >> 8) & 0xFF;
  1089. var b = integer & 0xFF;
  1090.  
  1091. return [r, g, b];
  1092. };
  1093.  
  1094. convert.rgb.hcg = function (rgb) {
  1095. var r = rgb[0] / 255;
  1096. var g = rgb[1] / 255;
  1097. var b = rgb[2] / 255;
  1098. var max = Math.max(Math.max(r, g), b);
  1099. var min = Math.min(Math.min(r, g), b);
  1100. var chroma = (max - min);
  1101. var grayscale;
  1102. var hue;
  1103.  
  1104. if (chroma < 1) {
  1105. grayscale = min / (1 - chroma);
  1106. } else {
  1107. grayscale = 0;
  1108. }
  1109.  
  1110. if (chroma <= 0) {
  1111. hue = 0;
  1112. } else
  1113. if (max === r) {
  1114. hue = ((g - b) / chroma) % 6;
  1115. } else
  1116. if (max === g) {
  1117. hue = 2 + (b - r) / chroma;
  1118. } else {
  1119. hue = 4 + (r - g) / chroma + 4;
  1120. }
  1121.  
  1122. hue /= 6;
  1123. hue %= 1;
  1124.  
  1125. return [hue * 360, chroma * 100, grayscale * 100];
  1126. };
  1127.  
  1128. convert.hsl.hcg = function (hsl) {
  1129. var s = hsl[1] / 100;
  1130. var l = hsl[2] / 100;
  1131. var c = 1;
  1132. var f = 0;
  1133.  
  1134. if (l < 0.5) {
  1135. c = 2.0 * s * l;
  1136. } else {
  1137. c = 2.0 * s * (1.0 - l);
  1138. }
  1139.  
  1140. if (c < 1.0) {
  1141. f = (l - 0.5 * c) / (1.0 - c);
  1142. }
  1143.  
  1144. return [hsl[0], c * 100, f * 100];
  1145. };
  1146.  
  1147. convert.hsv.hcg = function (hsv) {
  1148. var s = hsv[1] / 100;
  1149. var v = hsv[2] / 100;
  1150.  
  1151. var c = s * v;
  1152. var f = 0;
  1153.  
  1154. if (c < 1.0) {
  1155. f = (v - c) / (1 - c);
  1156. }
  1157.  
  1158. return [hsv[0], c * 100, f * 100];
  1159. };
  1160.  
  1161. convert.hcg.rgb = function (hcg) {
  1162. var h = hcg[0] / 360;
  1163. var c = hcg[1] / 100;
  1164. var g = hcg[2] / 100;
  1165.  
  1166. if (c === 0.0) {
  1167. return [g * 255, g * 255, g * 255];
  1168. }
  1169.  
  1170. var pure = [0, 0, 0];
  1171. var hi = (h % 1) * 6;
  1172. var v = hi % 1;
  1173. var w = 1 - v;
  1174. var mg = 0;
  1175.  
  1176. switch (Math.floor(hi)) {
  1177. case 0:
  1178. pure[0] = 1; pure[1] = v; pure[2] = 0; break;
  1179. case 1:
  1180. pure[0] = w; pure[1] = 1; pure[2] = 0; break;
  1181. case 2:
  1182. pure[0] = 0; pure[1] = 1; pure[2] = v; break;
  1183. case 3:
  1184. pure[0] = 0; pure[1] = w; pure[2] = 1; break;
  1185. case 4:
  1186. pure[0] = v; pure[1] = 0; pure[2] = 1; break;
  1187. default:
  1188. pure[0] = 1; pure[1] = 0; pure[2] = w;
  1189. }
  1190.  
  1191. mg = (1.0 - c) * g;
  1192.  
  1193. return [
  1194. (c * pure[0] + mg) * 255,
  1195. (c * pure[1] + mg) * 255,
  1196. (c * pure[2] + mg) * 255
  1197. ];
  1198. };
  1199.  
  1200. convert.hcg.hsv = function (hcg) {
  1201. var c = hcg[1] / 100;
  1202. var g = hcg[2] / 100;
  1203.  
  1204. var v = c + g * (1.0 - c);
  1205. var f = 0;
  1206.  
  1207. if (v > 0.0) {
  1208. f = c / v;
  1209. }
  1210.  
  1211. return [hcg[0], f * 100, v * 100];
  1212. };
  1213.  
  1214. convert.hcg.hsl = function (hcg) {
  1215. var c = hcg[1] / 100;
  1216. var g = hcg[2] / 100;
  1217.  
  1218. var l = g * (1.0 - c) + 0.5 * c;
  1219. var s = 0;
  1220.  
  1221. if (l > 0.0 && l < 0.5) {
  1222. s = c / (2 * l);
  1223. } else
  1224. if (l >= 0.5 && l < 1.0) {
  1225. s = c / (2 * (1 - l));
  1226. }
  1227.  
  1228. return [hcg[0], s * 100, l * 100];
  1229. };
  1230.  
  1231. convert.hcg.hwb = function (hcg) {
  1232. var c = hcg[1] / 100;
  1233. var g = hcg[2] / 100;
  1234. var v = c + g * (1.0 - c);
  1235. return [hcg[0], (v - c) * 100, (1 - v) * 100];
  1236. };
  1237.  
  1238. convert.hwb.hcg = function (hwb) {
  1239. var w = hwb[1] / 100;
  1240. var b = hwb[2] / 100;
  1241. var v = 1 - b;
  1242. var c = v - w;
  1243. var g = 0;
  1244.  
  1245. if (c < 1) {
  1246. g = (v - c) / (1 - c);
  1247. }
  1248.  
  1249. return [hwb[0], c * 100, g * 100];
  1250. };
  1251.  
  1252. convert.apple.rgb = function (apple) {
  1253. return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255];
  1254. };
  1255.  
  1256. convert.rgb.apple = function (rgb) {
  1257. return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535];
  1258. };
  1259.  
  1260. convert.gray.rgb = function (args) {
  1261. return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255];
  1262. };
  1263.  
  1264. convert.gray.hsl = convert.gray.hsv = function (args) {
  1265. return [0, 0, args[0]];
  1266. };
  1267.  
  1268. convert.gray.hwb = function (gray) {
  1269. return [0, 100, gray[0]];
  1270. };
  1271.  
  1272. convert.gray.cmyk = function (gray) {
  1273. return [0, 0, 0, gray[0]];
  1274. };
  1275.  
  1276. convert.gray.lab = function (gray) {
  1277. return [gray[0], 0, 0];
  1278. };
  1279.  
  1280. convert.gray.hex = function (gray) {
  1281. var val = Math.round(gray[0] / 100 * 255) & 0xFF;
  1282. var integer = (val << 16) + (val << 8) + val;
  1283.  
  1284. var string = integer.toString(16).toUpperCase();
  1285. return '000000'.substring(string.length) + string;
  1286. };
  1287.  
  1288. convert.rgb.gray = function (rgb) {
  1289. var val = (rgb[0] + rgb[1] + rgb[2]) / 3;
  1290. return [val / 255 * 100];
  1291. };
  1292.  
  1293. /*
  1294. this function routes a model to all other models.
  1295.  
  1296. all functions that are routed have a property `.conversion` attached
  1297. to the returned synthetic function. This property is an array
  1298. of strings, each with the steps in between the 'from' and 'to'
  1299. color models (inclusive).
  1300.  
  1301. conversions that are not possible simply are not included.
  1302. */
  1303.  
  1304. function buildGraph() {
  1305. var graph = {};
  1306. // https://jsperf.com/object-keys-vs-for-in-with-closure/3
  1307. var models = Object.keys(convert);
  1308.  
  1309. for (var len = models.length, i = 0; i < len; i++) {
  1310. graph[models[i]] = {
  1311. // http://jsperf.com/1-vs-infinity
  1312. // micro-opt, but this is simple.
  1313. distance: -1,
  1314. parent: null
  1315. };
  1316. }
  1317.  
  1318. return graph;
  1319. }
  1320.  
  1321. // https://en.wikipedia.org/wiki/Breadth-first_search
  1322. function deriveBFS(fromModel) {
  1323. var graph = buildGraph();
  1324. var queue = [fromModel]; // unshift -> queue -> pop
  1325.  
  1326. graph[fromModel].distance = 0;
  1327.  
  1328. while (queue.length) {
  1329. var current = queue.pop();
  1330. var adjacents = Object.keys(convert[current]);
  1331.  
  1332. for (var len = adjacents.length, i = 0; i < len; i++) {
  1333. var adjacent = adjacents[i];
  1334. var node = graph[adjacent];
  1335.  
  1336. if (node.distance === -1) {
  1337. node.distance = graph[current].distance + 1;
  1338. node.parent = current;
  1339. queue.unshift(adjacent);
  1340. }
  1341. }
  1342. }
  1343.  
  1344. return graph;
  1345. }
  1346.  
  1347. function link(from, to) {
  1348. return function (args) {
  1349. return to(from(args));
  1350. };
  1351. }
  1352.  
  1353. function wrapConversion(toModel, graph) {
  1354. var path = [graph[toModel].parent, toModel];
  1355. var fn = convert[graph[toModel].parent][toModel];
  1356.  
  1357. var cur = graph[toModel].parent;
  1358. while (graph[cur].parent) {
  1359. path.unshift(graph[cur].parent);
  1360. fn = link(convert[graph[cur].parent][cur], fn);
  1361. cur = graph[cur].parent;
  1362. }
  1363.  
  1364. fn.conversion = path;
  1365. return fn;
  1366. }
  1367.  
  1368. function route (fromModel) {
  1369. var graph = deriveBFS(fromModel);
  1370. var conversion = {};
  1371.  
  1372. var models = Object.keys(graph);
  1373. for (var len = models.length, i = 0; i < len; i++) {
  1374. var toModel = models[i];
  1375. var node = graph[toModel];
  1376.  
  1377. if (node.parent === null) {
  1378. // no possible conversion, or this node is the source model.
  1379. continue;
  1380. }
  1381.  
  1382. conversion[toModel] = wrapConversion(toModel, graph);
  1383. }
  1384.  
  1385. return conversion;
  1386. }
  1387.  
  1388. var convert$1 = {};
  1389.  
  1390. var models = Object.keys(convert);
  1391.  
  1392. function wrapRaw(fn) {
  1393. var wrappedFn = function (args) {
  1394. if (args === undefined || args === null) {
  1395. return args;
  1396. }
  1397.  
  1398. if (arguments.length > 1) {
  1399. args = Array.prototype.slice.call(arguments);
  1400. }
  1401.  
  1402. return fn(args);
  1403. };
  1404.  
  1405. // preserve .conversion property if there is one
  1406. if ('conversion' in fn) {
  1407. wrappedFn.conversion = fn.conversion;
  1408. }
  1409.  
  1410. return wrappedFn;
  1411. }
  1412.  
  1413. function wrapRounded(fn) {
  1414. var wrappedFn = function (args) {
  1415. if (args === undefined || args === null) {
  1416. return args;
  1417. }
  1418.  
  1419. if (arguments.length > 1) {
  1420. args = Array.prototype.slice.call(arguments);
  1421. }
  1422.  
  1423. var result = fn(args);
  1424.  
  1425. // we're assuming the result is an array here.
  1426. // see notice in conversions.js; don't use box types
  1427. // in conversion functions.
  1428. if (typeof result === 'object') {
  1429. for (var len = result.length, i = 0; i < len; i++) {
  1430. result[i] = Math.round(result[i]);
  1431. }
  1432. }
  1433.  
  1434. return result;
  1435. };
  1436.  
  1437. // preserve .conversion property if there is one
  1438. if ('conversion' in fn) {
  1439. wrappedFn.conversion = fn.conversion;
  1440. }
  1441.  
  1442. return wrappedFn;
  1443. }
  1444.  
  1445. models.forEach(function (fromModel) {
  1446. convert$1[fromModel] = {};
  1447.  
  1448. Object.defineProperty(convert$1[fromModel], 'channels', {value: convert[fromModel].channels});
  1449. Object.defineProperty(convert$1[fromModel], 'labels', {value: convert[fromModel].labels});
  1450.  
  1451. var routes = route(fromModel);
  1452. var routeModels = Object.keys(routes);
  1453.  
  1454. routeModels.forEach(function (toModel) {
  1455. var fn = routes[toModel];
  1456.  
  1457. convert$1[fromModel][toModel] = wrapRounded(fn);
  1458. convert$1[fromModel][toModel].raw = wrapRaw(fn);
  1459. });
  1460. });
  1461.  
  1462. var _slice = [].slice;
  1463.  
  1464. var skippedModels = [
  1465. // to be honest, I don't really feel like keyword belongs in color convert, but eh.
  1466. 'keyword',
  1467.  
  1468. // gray conflicts with some method names, and has its own method defined.
  1469. 'gray',
  1470.  
  1471. // shouldn't really be in color-convert either...
  1472. 'hex'
  1473. ];
  1474.  
  1475. var hashedModelKeys = {};
  1476. Object.keys(convert$1).forEach(function (model) {
  1477. hashedModelKeys[_slice.call(convert$1[model].labels).sort().join('')] = model;
  1478. });
  1479.  
  1480. var limiters = {};
  1481.  
  1482. function Color(obj, model) {
  1483. if (!(this instanceof Color)) {
  1484. return new Color(obj, model);
  1485. }
  1486.  
  1487. if (model && model in skippedModels) {
  1488. model = null;
  1489. }
  1490.  
  1491. if (model && !(model in convert$1)) {
  1492. throw new Error('Unknown model: ' + model);
  1493. }
  1494.  
  1495. var i;
  1496. var channels;
  1497.  
  1498. if (obj == null) { // eslint-disable-line no-eq-null,eqeqeq
  1499. this.model = 'rgb';
  1500. this.color = [0, 0, 0];
  1501. this.valpha = 1;
  1502. } else if (obj instanceof Color) {
  1503. this.model = obj.model;
  1504. this.color = obj.color.slice();
  1505. this.valpha = obj.valpha;
  1506. } else if (typeof obj === 'string') {
  1507. var result = cs.get(obj);
  1508. if (result === null) {
  1509. throw new Error('Unable to parse color from string: ' + obj);
  1510. }
  1511.  
  1512. this.model = result.model;
  1513. channels = convert$1[this.model].channels;
  1514. this.color = result.value.slice(0, channels);
  1515. this.valpha = typeof result.value[channels] === 'number' ? result.value[channels] : 1;
  1516. } else if (obj.length) {
  1517. this.model = model || 'rgb';
  1518. channels = convert$1[this.model].channels;
  1519. var newArr = _slice.call(obj, 0, channels);
  1520. this.color = zeroArray(newArr, channels);
  1521. this.valpha = typeof obj[channels] === 'number' ? obj[channels] : 1;
  1522. } else if (typeof obj === 'number') {
  1523. // this is always RGB - can be converted later on.
  1524. obj &= 0xFFFFFF;
  1525. this.model = 'rgb';
  1526. this.color = [
  1527. (obj >> 16) & 0xFF,
  1528. (obj >> 8) & 0xFF,
  1529. obj & 0xFF
  1530. ];
  1531. this.valpha = 1;
  1532. } else {
  1533. this.valpha = 1;
  1534.  
  1535. var keys = Object.keys(obj);
  1536. if ('alpha' in obj) {
  1537. keys.splice(keys.indexOf('alpha'), 1);
  1538. this.valpha = typeof obj.alpha === 'number' ? obj.alpha : 0;
  1539. }
  1540.  
  1541. var hashedKeys = keys.sort().join('');
  1542. if (!(hashedKeys in hashedModelKeys)) {
  1543. throw new Error('Unable to parse color from object: ' + JSON.stringify(obj));
  1544. }
  1545.  
  1546. this.model = hashedModelKeys[hashedKeys];
  1547.  
  1548. var labels = convert$1[this.model].labels;
  1549. var color = [];
  1550. for (i = 0; i < labels.length; i++) {
  1551. color.push(obj[labels[i]]);
  1552. }
  1553.  
  1554. this.color = zeroArray(color);
  1555. }
  1556.  
  1557. // perform limitations (clamping, etc.)
  1558. if (limiters[this.model]) {
  1559. channels = convert$1[this.model].channels;
  1560. for (i = 0; i < channels; i++) {
  1561. var limit = limiters[this.model][i];
  1562. if (limit) {
  1563. this.color[i] = limit(this.color[i]);
  1564. }
  1565. }
  1566. }
  1567.  
  1568. this.valpha = Math.max(0, Math.min(1, this.valpha));
  1569.  
  1570. if (Object.freeze) {
  1571. Object.freeze(this);
  1572. }
  1573. }
  1574.  
  1575. Color.namedColors = cssKeywords; // Modified from original
  1576.  
  1577. Color.prototype = {
  1578. toString: function () {
  1579. return this.string();
  1580. },
  1581.  
  1582. toJSON: function () {
  1583. return this[this.model]();
  1584. },
  1585.  
  1586. string: function (places) {
  1587. var self = this.model in cs.to ? this : this.rgb();
  1588. self = self.round(typeof places === 'number' ? places : 1);
  1589. var args = self.valpha === 1 ? self.color : self.color.concat(this.valpha);
  1590. return cs.to[self.model](args);
  1591. },
  1592.  
  1593. percentString: function (places) {
  1594. var self = this.rgb().round(typeof places === 'number' ? places : 1);
  1595. var args = self.valpha === 1 ? self.color : self.color.concat(this.valpha);
  1596. return cs.to.rgb.percent(args);
  1597. },
  1598.  
  1599. array: function () {
  1600. return this.valpha === 1 ? this.color.slice() : this.color.concat(this.valpha);
  1601. },
  1602.  
  1603. object: function () {
  1604. var result = {};
  1605. var channels = convert$1[this.model].channels;
  1606. var labels = convert$1[this.model].labels;
  1607.  
  1608. for (var i = 0; i < channels; i++) {
  1609. result[labels[i]] = this.color[i];
  1610. }
  1611.  
  1612. if (this.valpha !== 1) {
  1613. result.alpha = this.valpha;
  1614. }
  1615.  
  1616. return result;
  1617. },
  1618.  
  1619. unitArray: function () {
  1620. var rgb = this.rgb().color;
  1621. rgb[0] /= 255;
  1622. rgb[1] /= 255;
  1623. rgb[2] /= 255;
  1624.  
  1625. if (this.valpha !== 1) {
  1626. rgb.push(this.valpha);
  1627. }
  1628.  
  1629. return rgb;
  1630. },
  1631.  
  1632. unitObject: function () {
  1633. var rgb = this.rgb().object();
  1634. rgb.r /= 255;
  1635. rgb.g /= 255;
  1636. rgb.b /= 255;
  1637.  
  1638. if (this.valpha !== 1) {
  1639. rgb.alpha = this.valpha;
  1640. }
  1641.  
  1642. return rgb;
  1643. },
  1644.  
  1645. round: function (places) {
  1646. places = Math.max(places || 0, 0);
  1647. return new Color(this.color.map(roundToPlace(places)).concat(this.valpha), this.model);
  1648. },
  1649.  
  1650. alpha: function (val) {
  1651. if (arguments.length) {
  1652. return new Color(this.color.concat(Math.max(0, Math.min(1, val))), this.model);
  1653. }
  1654.  
  1655. return this.valpha;
  1656. },
  1657.  
  1658. // rgb
  1659. red: getset('rgb', 0, maxfn(255)),
  1660. green: getset('rgb', 1, maxfn(255)),
  1661. blue: getset('rgb', 2, maxfn(255)),
  1662.  
  1663. hue: getset(['hsl', 'hsv', 'hsl', 'hwb', 'hcg'], 0, function (val) { return ((val % 360) + 360) % 360; }), // eslint-disable-line brace-style
  1664.  
  1665. saturationl: getset('hsl', 1, maxfn(100)),
  1666. lightness: getset('hsl', 2, maxfn(100)),
  1667.  
  1668. saturationv: getset('hsv', 1, maxfn(100)),
  1669. value: getset('hsv', 2, maxfn(100)),
  1670.  
  1671. chroma: getset('hcg', 1, maxfn(100)),
  1672. gray: getset('hcg', 2, maxfn(100)),
  1673.  
  1674. white: getset('hwb', 1, maxfn(100)),
  1675. wblack: getset('hwb', 2, maxfn(100)),
  1676.  
  1677. cyan: getset('cmyk', 0, maxfn(100)),
  1678. magenta: getset('cmyk', 1, maxfn(100)),
  1679. yellow: getset('cmyk', 2, maxfn(100)),
  1680. black: getset('cmyk', 3, maxfn(100)),
  1681.  
  1682. x: getset('xyz', 0, maxfn(100)),
  1683. y: getset('xyz', 1, maxfn(100)),
  1684. z: getset('xyz', 2, maxfn(100)),
  1685.  
  1686. l: getset('lab', 0, maxfn(100)),
  1687. a: getset('lab', 1),
  1688. b: getset('lab', 2),
  1689.  
  1690. keyword: function (val) {
  1691. if (arguments.length) {
  1692. return new Color(val);
  1693. }
  1694.  
  1695. return convert$1[this.model].keyword(this.color);
  1696. },
  1697.  
  1698. hex: function (val) {
  1699. if (arguments.length) {
  1700. return new Color(val);
  1701. }
  1702.  
  1703. return cs.to.hex(this.rgb().round().color);
  1704. },
  1705.  
  1706. rgbNumber: function () {
  1707. var rgb = this.rgb().color;
  1708. return ((rgb[0] & 0xFF) << 16) | ((rgb[1] & 0xFF) << 8) | (rgb[2] & 0xFF);
  1709. },
  1710.  
  1711. luminosity: function () {
  1712. // http://www.w3.org/TR/WCAG20/#relativeluminancedef
  1713. var rgb = this.rgb().color;
  1714.  
  1715. var lum = [];
  1716. for (var i = 0; i < rgb.length; i++) {
  1717. var chan = rgb[i] / 255;
  1718. lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);
  1719. }
  1720.  
  1721. return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
  1722. },
  1723.  
  1724. contrast: function (color2) {
  1725. // http://www.w3.org/TR/WCAG20/#contrast-ratiodef
  1726. var lum1 = this.luminosity();
  1727. var lum2 = color2.luminosity();
  1728.  
  1729. if (lum1 > lum2) {
  1730. return (lum1 + 0.05) / (lum2 + 0.05);
  1731. }
  1732.  
  1733. return (lum2 + 0.05) / (lum1 + 0.05);
  1734. },
  1735.  
  1736. level: function (color2) {
  1737. var contrastRatio = this.contrast(color2);
  1738. if (contrastRatio >= 7.1) {
  1739. return 'AAA';
  1740. }
  1741.  
  1742. return (contrastRatio >= 4.5) ? 'AA' : '';
  1743. },
  1744.  
  1745. isDark: function () {
  1746. // YIQ equation from http://24ways.org/2010/calculating-color-contrast
  1747. var rgb = this.rgb().color;
  1748. var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
  1749. return yiq < 128;
  1750. },
  1751.  
  1752. isLight: function () {
  1753. return !this.isDark();
  1754. },
  1755.  
  1756. negate: function () {
  1757. var rgb = this.rgb();
  1758. for (var i = 0; i < 3; i++) {
  1759. rgb.color[i] = 255 - rgb.color[i];
  1760. }
  1761. return rgb;
  1762. },
  1763.  
  1764. lighten: function (ratio) {
  1765. var hsl = this.hsl();
  1766. hsl.color[2] += hsl.color[2] * ratio;
  1767. return hsl;
  1768. },
  1769.  
  1770. darken: function (ratio) {
  1771. var hsl = this.hsl();
  1772. hsl.color[2] -= hsl.color[2] * ratio;
  1773. return hsl;
  1774. },
  1775.  
  1776. saturate: function (ratio) {
  1777. var hsl = this.hsl();
  1778. hsl.color[1] += hsl.color[1] * ratio;
  1779. return hsl;
  1780. },
  1781.  
  1782. desaturate: function (ratio) {
  1783. var hsl = this.hsl();
  1784. hsl.color[1] -= hsl.color[1] * ratio;
  1785. return hsl;
  1786. },
  1787.  
  1788. whiten: function (ratio) {
  1789. var hwb = this.hwb();
  1790. hwb.color[1] += hwb.color[1] * ratio;
  1791. return hwb;
  1792. },
  1793.  
  1794. blacken: function (ratio) {
  1795. var hwb = this.hwb();
  1796. hwb.color[2] += hwb.color[2] * ratio;
  1797. return hwb;
  1798. },
  1799.  
  1800. grayscale: function () {
  1801. // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
  1802. var rgb = this.rgb().color;
  1803. var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
  1804. return Color.rgb(val, val, val);
  1805. },
  1806.  
  1807. fade: function (ratio) {
  1808. return this.alpha(this.valpha - (this.valpha * ratio));
  1809. },
  1810.  
  1811. opaquer: function (ratio) {
  1812. return this.alpha(this.valpha + (this.valpha * ratio));
  1813. },
  1814.  
  1815. rotate: function (degrees) {
  1816. var hsl = this.hsl();
  1817. var hue = hsl.color[0];
  1818. hue = (hue + degrees) % 360;
  1819. hue = hue < 0 ? 360 + hue : hue;
  1820. hsl.color[0] = hue;
  1821. return hsl;
  1822. },
  1823.  
  1824. mix: function (mixinColor, weight) {
  1825. // ported from sass implementation in C
  1826. // https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209
  1827. if (!mixinColor || !mixinColor.rgb) {
  1828. throw new Error('Argument to "mix" was not a Color instance, but rather an instance of ' + typeof mixinColor);
  1829. }
  1830. var color1 = mixinColor.rgb();
  1831. var color2 = this.rgb();
  1832. var p = weight === undefined ? 0.5 : weight;
  1833.  
  1834. var w = 2 * p - 1;
  1835. var a = color1.alpha() - color2.alpha();
  1836.  
  1837. var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
  1838. var w2 = 1 - w1;
  1839.  
  1840. return Color.rgb(
  1841. w1 * color1.red() + w2 * color2.red(),
  1842. w1 * color1.green() + w2 * color2.green(),
  1843. w1 * color1.blue() + w2 * color2.blue(),
  1844. color1.alpha() * p + color2.alpha() * (1 - p));
  1845. }
  1846. };
  1847.  
  1848. // model conversion methods and static constructors
  1849. Object.keys(convert$1).forEach(function (model) {
  1850. if (skippedModels.indexOf(model) !== -1) {
  1851. return;
  1852. }
  1853.  
  1854. var channels = convert$1[model].channels;
  1855.  
  1856. // conversion methods
  1857. Color.prototype[model] = function () {
  1858. if (this.model === model) {
  1859. return new Color(this);
  1860. }
  1861.  
  1862. if (arguments.length) {
  1863. return new Color(arguments, model);
  1864. }
  1865.  
  1866. var newAlpha = typeof arguments[channels] === 'number' ? channels : this.valpha;
  1867. return new Color(assertArray(convert$1[this.model][model].raw(this.color)).concat(newAlpha), model);
  1868. };
  1869.  
  1870. // 'static' construction methods
  1871. Color[model] = function (color) {
  1872. if (typeof color === 'number') {
  1873. color = zeroArray(_slice.call(arguments), channels);
  1874. }
  1875. return new Color(color, model);
  1876. };
  1877. });
  1878.  
  1879. function roundTo(num, places) {
  1880. return Number(num.toFixed(places));
  1881. }
  1882.  
  1883. function roundToPlace(places) {
  1884. return function (num) {
  1885. return roundTo(num, places);
  1886. };
  1887. }
  1888.  
  1889. function getset(model, channel, modifier) {
  1890. model = Array.isArray(model) ? model : [model];
  1891.  
  1892. model.forEach(function (m) {
  1893. (limiters[m] || (limiters[m] = []))[channel] = modifier;
  1894. });
  1895.  
  1896. model = model[0];
  1897.  
  1898. return function (val) {
  1899. var result;
  1900.  
  1901. if (arguments.length) {
  1902. if (modifier) {
  1903. val = modifier(val);
  1904. }
  1905.  
  1906. result = this[model]();
  1907. result.color[channel] = val;
  1908. return result;
  1909. }
  1910.  
  1911. result = this[model]().color[channel];
  1912. if (modifier) {
  1913. result = modifier(result);
  1914. }
  1915.  
  1916. return result;
  1917. };
  1918. }
  1919.  
  1920. function maxfn(max) {
  1921. return function (v) {
  1922. return Math.max(0, Math.min(max, v));
  1923. };
  1924. }
  1925.  
  1926. function assertArray(val) {
  1927. return Array.isArray(val) ? val : [val];
  1928. }
  1929.  
  1930. function zeroArray(arr, length) {
  1931. for (var i = 0; i < length; i++) {
  1932. if (typeof arr[i] !== 'number') {
  1933. arr[i] = 0;
  1934. }
  1935. }
  1936.  
  1937. return arr;
  1938. }
  1939.  
  1940. return Color;
  1941.  
  1942. }());