2a66a9b32b4f003b18be489ab09ca7a0d30036fddfe56bd8e2b2c6f267ad10d6e8615cfff2f131ccf1f7b2c1ced94e5c5dff293e09892f9f025406058663f8 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /* eslint-disable @typescript-eslint/no-redundant-type-constituents */
  2. import { convertHexToDecimal, hslToRgb, hsvToRgb, parseIntFromHex, rgbToRgb, } from './conversion.js';
  3. import { names } from './css-color-names.js';
  4. import { boundAlpha, convertToPercentage } from './util.js';
  5. /**
  6. * Given a string or object, convert that input to RGB
  7. *
  8. * Possible string inputs:
  9. * ```
  10. * "red"
  11. * "#f00" or "f00"
  12. * "#ff0000" or "ff0000"
  13. * "#ff000000" or "ff000000"
  14. * "rgb 255 0 0" or "rgb (255, 0, 0)"
  15. * "rgb 1.0 0 0" or "rgb (1, 0, 0)"
  16. * "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
  17. * "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
  18. * "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
  19. * "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
  20. * "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
  21. * ```
  22. */
  23. export function inputToRGB(color) {
  24. var rgb = { r: 0, g: 0, b: 0 };
  25. var a = 1;
  26. var s = null;
  27. var v = null;
  28. var l = null;
  29. var ok = false;
  30. var format = false;
  31. if (typeof color === 'string') {
  32. color = stringInputToObject(color);
  33. }
  34. if (typeof color === 'object') {
  35. if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
  36. rgb = rgbToRgb(color.r, color.g, color.b);
  37. ok = true;
  38. format = String(color.r).substr(-1) === '%' ? 'prgb' : 'rgb';
  39. }
  40. else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
  41. s = convertToPercentage(color.s);
  42. v = convertToPercentage(color.v);
  43. rgb = hsvToRgb(color.h, s, v);
  44. ok = true;
  45. format = 'hsv';
  46. }
  47. else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {
  48. s = convertToPercentage(color.s);
  49. l = convertToPercentage(color.l);
  50. rgb = hslToRgb(color.h, s, l);
  51. ok = true;
  52. format = 'hsl';
  53. }
  54. if (Object.prototype.hasOwnProperty.call(color, 'a')) {
  55. a = color.a;
  56. }
  57. }
  58. a = boundAlpha(a);
  59. return {
  60. ok: ok,
  61. format: color.format || format,
  62. r: Math.min(255, Math.max(rgb.r, 0)),
  63. g: Math.min(255, Math.max(rgb.g, 0)),
  64. b: Math.min(255, Math.max(rgb.b, 0)),
  65. a: a,
  66. };
  67. }
  68. // <http://www.w3.org/TR/css3-values/#integers>
  69. var CSS_INTEGER = '[-\\+]?\\d+%?';
  70. // <http://www.w3.org/TR/css3-values/#number-value>
  71. var CSS_NUMBER = '[-\\+]?\\d*\\.\\d+%?';
  72. // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
  73. var CSS_UNIT = "(?:".concat(CSS_NUMBER, ")|(?:").concat(CSS_INTEGER, ")");
  74. // Actual matching.
  75. // Parentheses and commas are optional, but not required.
  76. // Whitespace can take the place of commas or opening paren
  77. var PERMISSIVE_MATCH3 = "[\\s|\\(]+(".concat(CSS_UNIT, ")[,|\\s]+(").concat(CSS_UNIT, ")[,|\\s]+(").concat(CSS_UNIT, ")\\s*\\)?");
  78. var PERMISSIVE_MATCH4 = "[\\s|\\(]+(".concat(CSS_UNIT, ")[,|\\s]+(").concat(CSS_UNIT, ")[,|\\s]+(").concat(CSS_UNIT, ")[,|\\s]+(").concat(CSS_UNIT, ")\\s*\\)?");
  79. var matchers = {
  80. CSS_UNIT: new RegExp(CSS_UNIT),
  81. rgb: new RegExp('rgb' + PERMISSIVE_MATCH3),
  82. rgba: new RegExp('rgba' + PERMISSIVE_MATCH4),
  83. hsl: new RegExp('hsl' + PERMISSIVE_MATCH3),
  84. hsla: new RegExp('hsla' + PERMISSIVE_MATCH4),
  85. hsv: new RegExp('hsv' + PERMISSIVE_MATCH3),
  86. hsva: new RegExp('hsva' + PERMISSIVE_MATCH4),
  87. hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
  88. hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
  89. hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
  90. hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
  91. };
  92. /**
  93. * Permissive string parsing. Take in a number of formats, and output an object
  94. * based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`
  95. */
  96. export function stringInputToObject(color) {
  97. color = color.trim().toLowerCase();
  98. if (color.length === 0) {
  99. return false;
  100. }
  101. var named = false;
  102. if (names[color]) {
  103. color = names[color];
  104. named = true;
  105. }
  106. else if (color === 'transparent') {
  107. return { r: 0, g: 0, b: 0, a: 0, format: 'name' };
  108. }
  109. // Try to match string input using regular expressions.
  110. // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]
  111. // Just return an object and let the conversion functions handle that.
  112. // This way the result will be the same whether the tinycolor is initialized with string or object.
  113. var match = matchers.rgb.exec(color);
  114. if (match) {
  115. return { r: match[1], g: match[2], b: match[3] };
  116. }
  117. match = matchers.rgba.exec(color);
  118. if (match) {
  119. return { r: match[1], g: match[2], b: match[3], a: match[4] };
  120. }
  121. match = matchers.hsl.exec(color);
  122. if (match) {
  123. return { h: match[1], s: match[2], l: match[3] };
  124. }
  125. match = matchers.hsla.exec(color);
  126. if (match) {
  127. return { h: match[1], s: match[2], l: match[3], a: match[4] };
  128. }
  129. match = matchers.hsv.exec(color);
  130. if (match) {
  131. return { h: match[1], s: match[2], v: match[3] };
  132. }
  133. match = matchers.hsva.exec(color);
  134. if (match) {
  135. return { h: match[1], s: match[2], v: match[3], a: match[4] };
  136. }
  137. match = matchers.hex8.exec(color);
  138. if (match) {
  139. return {
  140. r: parseIntFromHex(match[1]),
  141. g: parseIntFromHex(match[2]),
  142. b: parseIntFromHex(match[3]),
  143. a: convertHexToDecimal(match[4]),
  144. format: named ? 'name' : 'hex8',
  145. };
  146. }
  147. match = matchers.hex6.exec(color);
  148. if (match) {
  149. return {
  150. r: parseIntFromHex(match[1]),
  151. g: parseIntFromHex(match[2]),
  152. b: parseIntFromHex(match[3]),
  153. format: named ? 'name' : 'hex',
  154. };
  155. }
  156. match = matchers.hex4.exec(color);
  157. if (match) {
  158. return {
  159. r: parseIntFromHex(match[1] + match[1]),
  160. g: parseIntFromHex(match[2] + match[2]),
  161. b: parseIntFromHex(match[3] + match[3]),
  162. a: convertHexToDecimal(match[4] + match[4]),
  163. format: named ? 'name' : 'hex8',
  164. };
  165. }
  166. match = matchers.hex3.exec(color);
  167. if (match) {
  168. return {
  169. r: parseIntFromHex(match[1] + match[1]),
  170. g: parseIntFromHex(match[2] + match[2]),
  171. b: parseIntFromHex(match[3] + match[3]),
  172. format: named ? 'name' : 'hex',
  173. };
  174. }
  175. return false;
  176. }
  177. /**
  178. * Check to see if it looks like a CSS unit
  179. * (see `matchers` above for definition).
  180. */
  181. export function isValidCSSUnit(color) {
  182. return Boolean(matchers.CSS_UNIT.exec(String(color)));
  183. }