not really known
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1194 lines
40 KiB

  1. // TinyColor v1.4.1
  2. // https://github.com/bgrins/TinyColor
  3. // Brian Grinstead, MIT License
  4. (function(Math) {
  5. var trimLeft = /^\s+/,
  6. trimRight = /\s+$/,
  7. tinyCounter = 0,
  8. mathRound = Math.round,
  9. mathMin = Math.min,
  10. mathMax = Math.max,
  11. mathRandom = Math.random;
  12. function tinycolor (color, opts) {
  13. color = (color) ? color : '';
  14. opts = opts || { };
  15. // If input is already a tinycolor, return itself
  16. if (color instanceof tinycolor) {
  17. return color;
  18. }
  19. // If we are called as a function, call using new instead
  20. if (!(this instanceof tinycolor)) {
  21. return new tinycolor(color, opts);
  22. }
  23. var rgb = inputToRGB(color);
  24. this._originalInput = color,
  25. this._r = rgb.r,
  26. this._g = rgb.g,
  27. this._b = rgb.b,
  28. this._a = rgb.a,
  29. this._roundA = mathRound(100*this._a) / 100,
  30. this._format = opts.format || rgb.format;
  31. this._gradientType = opts.gradientType;
  32. // Don't let the range of [0,255] come back in [0,1].
  33. // Potentially lose a little bit of precision here, but will fix issues where
  34. // .5 gets interpreted as half of the total, instead of half of 1
  35. // If it was supposed to be 128, this was already taken care of by `inputToRgb`
  36. if (this._r < 1) { this._r = mathRound(this._r); }
  37. if (this._g < 1) { this._g = mathRound(this._g); }
  38. if (this._b < 1) { this._b = mathRound(this._b); }
  39. this._ok = rgb.ok;
  40. this._tc_id = tinyCounter++;
  41. }
  42. tinycolor.prototype = {
  43. isDark: function() {
  44. return this.getBrightness() < 128;
  45. },
  46. isLight: function() {
  47. return !this.isDark();
  48. },
  49. isValid: function() {
  50. return this._ok;
  51. },
  52. getOriginalInput: function() {
  53. return this._originalInput;
  54. },
  55. getFormat: function() {
  56. return this._format;
  57. },
  58. getAlpha: function() {
  59. return this._a;
  60. },
  61. getBrightness: function() {
  62. //http://www.w3.org/TR/AERT#color-contrast
  63. var rgb = this.toRgb();
  64. return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
  65. },
  66. getLuminance: function() {
  67. //http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
  68. var rgb = this.toRgb();
  69. var RsRGB, GsRGB, BsRGB, R, G, B;
  70. RsRGB = rgb.r/255;
  71. GsRGB = rgb.g/255;
  72. BsRGB = rgb.b/255;
  73. if (RsRGB <= 0.03928) {R = RsRGB / 12.92;} else {R = Math.pow(((RsRGB + 0.055) / 1.055), 2.4);}
  74. if (GsRGB <= 0.03928) {G = GsRGB / 12.92;} else {G = Math.pow(((GsRGB + 0.055) / 1.055), 2.4);}
  75. if (BsRGB <= 0.03928) {B = BsRGB / 12.92;} else {B = Math.pow(((BsRGB + 0.055) / 1.055), 2.4);}
  76. return (0.2126 * R) + (0.7152 * G) + (0.0722 * B);
  77. },
  78. setAlpha: function(value) {
  79. this._a = boundAlpha(value);
  80. this._roundA = mathRound(100*this._a) / 100;
  81. return this;
  82. },
  83. toHsv: function() {
  84. var hsv = rgbToHsv(this._r, this._g, this._b);
  85. return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };
  86. },
  87. toHsvString: function() {
  88. var hsv = rgbToHsv(this._r, this._g, this._b);
  89. var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);
  90. return (this._a == 1) ?
  91. "hsv(" + h + ", " + s + "%, " + v + "%)" :
  92. "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")";
  93. },
  94. toHsl: function() {
  95. var hsl = rgbToHsl(this._r, this._g, this._b);
  96. return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };
  97. },
  98. toHslString: function() {
  99. var hsl = rgbToHsl(this._r, this._g, this._b);
  100. var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);
  101. return (this._a == 1) ?
  102. "hsl(" + h + ", " + s + "%, " + l + "%)" :
  103. "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")";
  104. },
  105. toHex: function(allow3Char) {
  106. return rgbToHex(this._r, this._g, this._b, allow3Char);
  107. },
  108. toHexString: function(allow3Char) {
  109. return '#' + this.toHex(allow3Char);
  110. },
  111. toHex8: function(allow4Char) {
  112. return rgbaToHex(this._r, this._g, this._b, this._a, allow4Char);
  113. },
  114. toHex8String: function(allow4Char) {
  115. return '#' + this.toHex8(allow4Char);
  116. },
  117. toRgb: function() {
  118. return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };
  119. },
  120. toRgbString: function() {
  121. return (this._a == 1) ?
  122. "rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" :
  123. "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")";
  124. },
  125. toPercentageRgb: function() {
  126. return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a };
  127. },
  128. toPercentageRgbString: function() {
  129. return (this._a == 1) ?
  130. "rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" :
  131. "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")";
  132. },
  133. toName: function() {
  134. if (this._a === 0) {
  135. return "transparent";
  136. }
  137. if (this._a < 1) {
  138. return false;
  139. }
  140. return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;
  141. },
  142. toFilter: function(secondColor) {
  143. var hex8String = '#' + rgbaToArgbHex(this._r, this._g, this._b, this._a);
  144. var secondHex8String = hex8String;
  145. var gradientType = this._gradientType ? "GradientType = 1, " : "";
  146. if (secondColor) {
  147. var s = tinycolor(secondColor);
  148. secondHex8String = '#' + rgbaToArgbHex(s._r, s._g, s._b, s._a);
  149. }
  150. return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")";
  151. },
  152. toString: function(format) {
  153. var formatSet = !!format;
  154. format = format || this._format;
  155. var formattedString = false;
  156. var hasAlpha = this._a < 1 && this._a >= 0;
  157. var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "hex4" || format === "hex8" || format === "name");
  158. if (needsAlphaFormat) {
  159. // Special case for "transparent", all other non-alpha formats
  160. // will return rgba when there is transparency.
  161. if (format === "name" && this._a === 0) {
  162. return this.toName();
  163. }
  164. return this.toRgbString();
  165. }
  166. if (format === "rgb") {
  167. formattedString = this.toRgbString();
  168. }
  169. if (format === "prgb") {
  170. formattedString = this.toPercentageRgbString();
  171. }
  172. if (format === "hex" || format === "hex6") {
  173. formattedString = this.toHexString();
  174. }
  175. if (format === "hex3") {
  176. formattedString = this.toHexString(true);
  177. }
  178. if (format === "hex4") {
  179. formattedString = this.toHex8String(true);
  180. }
  181. if (format === "hex8") {
  182. formattedString = this.toHex8String();
  183. }
  184. if (format === "name") {
  185. formattedString = this.toName();
  186. }
  187. if (format === "hsl") {
  188. formattedString = this.toHslString();
  189. }
  190. if (format === "hsv") {
  191. formattedString = this.toHsvString();
  192. }
  193. return formattedString || this.toHexString();
  194. },
  195. clone: function() {
  196. return tinycolor(this.toString());
  197. },
  198. _applyModification: function(fn, args) {
  199. var color = fn.apply(null, [this].concat([].slice.call(args)));
  200. this._r = color._r;
  201. this._g = color._g;
  202. this._b = color._b;
  203. this.setAlpha(color._a);
  204. return this;
  205. },
  206. lighten: function() {
  207. return this._applyModification(lighten, arguments);
  208. },
  209. brighten: function() {
  210. return this._applyModification(brighten, arguments);
  211. },
  212. darken: function() {
  213. return this._applyModification(darken, arguments);
  214. },
  215. desaturate: function() {
  216. return this._applyModification(desaturate, arguments);
  217. },
  218. saturate: function() {
  219. return this._applyModification(saturate, arguments);
  220. },
  221. greyscale: function() {
  222. return this._applyModification(greyscale, arguments);
  223. },
  224. spin: function() {
  225. return this._applyModification(spin, arguments);
  226. },
  227. _applyCombination: function(fn, args) {
  228. return fn.apply(null, [this].concat([].slice.call(args)));
  229. },
  230. analogous: function() {
  231. return this._applyCombination(analogous, arguments);
  232. },
  233. complement: function() {
  234. return this._applyCombination(complement, arguments);
  235. },
  236. monochromatic: function() {
  237. return this._applyCombination(monochromatic, arguments);
  238. },
  239. splitcomplement: function() {
  240. return this._applyCombination(splitcomplement, arguments);
  241. },
  242. triad: function() {
  243. return this._applyCombination(triad, arguments);
  244. },
  245. tetrad: function() {
  246. return this._applyCombination(tetrad, arguments);
  247. }
  248. };
  249. // If input is an object, force 1 into "1.0" to handle ratios properly
  250. // String input requires "1.0" as input, so 1 will be treated as 1
  251. tinycolor.fromRatio = function(color, opts) {
  252. if (typeof color == "object") {
  253. var newColor = {};
  254. for (var i in color) {
  255. if (color.hasOwnProperty(i)) {
  256. if (i === "a") {
  257. newColor[i] = color[i];
  258. }
  259. else {
  260. newColor[i] = convertToPercentage(color[i]);
  261. }
  262. }
  263. }
  264. color = newColor;
  265. }
  266. return tinycolor(color, opts);
  267. };
  268. // Given a string or object, convert that input to RGB
  269. // Possible string inputs:
  270. //
  271. // "red"
  272. // "#f00" or "f00"
  273. // "#ff0000" or "ff0000"
  274. // "#ff000000" or "ff000000"
  275. // "rgb 255 0 0" or "rgb (255, 0, 0)"
  276. // "rgb 1.0 0 0" or "rgb (1, 0, 0)"
  277. // "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
  278. // "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
  279. // "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
  280. // "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
  281. // "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
  282. //
  283. function inputToRGB(color) {
  284. var rgb = { r: 0, g: 0, b: 0 };
  285. var a = 1;
  286. var s = null;
  287. var v = null;
  288. var l = null;
  289. var ok = false;
  290. var format = false;
  291. if (typeof color == "string") {
  292. color = stringInputToObject(color);
  293. }
  294. if (typeof color == "object") {
  295. if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
  296. rgb = rgbToRgb(color.r, color.g, color.b);
  297. ok = true;
  298. format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb";
  299. }
  300. else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
  301. s = convertToPercentage(color.s);
  302. v = convertToPercentage(color.v);
  303. rgb = hsvToRgb(color.h, s, v);
  304. ok = true;
  305. format = "hsv";
  306. }
  307. else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {
  308. s = convertToPercentage(color.s);
  309. l = convertToPercentage(color.l);
  310. rgb = hslToRgb(color.h, s, l);
  311. ok = true;
  312. format = "hsl";
  313. }
  314. if (color.hasOwnProperty("a")) {
  315. a = color.a;
  316. }
  317. }
  318. a = boundAlpha(a);
  319. return {
  320. ok: ok,
  321. format: color.format || format,
  322. r: mathMin(255, mathMax(rgb.r, 0)),
  323. g: mathMin(255, mathMax(rgb.g, 0)),
  324. b: mathMin(255, mathMax(rgb.b, 0)),
  325. a: a
  326. };
  327. }
  328. // Conversion Functions
  329. // --------------------
  330. // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:
  331. // <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>
  332. // `rgbToRgb`
  333. // Handle bounds / percentage checking to conform to CSS color spec
  334. // <http://www.w3.org/TR/css3-color/>
  335. // *Assumes:* r, g, b in [0, 255] or [0, 1]
  336. // *Returns:* { r, g, b } in [0, 255]
  337. function rgbToRgb(r, g, b){
  338. return {
  339. r: bound01(r, 255) * 255,
  340. g: bound01(g, 255) * 255,
  341. b: bound01(b, 255) * 255
  342. };
  343. }
  344. // `rgbToHsl`
  345. // Converts an RGB color value to HSL.
  346. // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]
  347. // *Returns:* { h, s, l } in [0,1]
  348. function rgbToHsl(r, g, b) {
  349. r = bound01(r, 255);
  350. g = bound01(g, 255);
  351. b = bound01(b, 255);
  352. var max = mathMax(r, g, b), min = mathMin(r, g, b);
  353. var h, s, l = (max + min) / 2;
  354. if(max == min) {
  355. h = s = 0; // achromatic
  356. }
  357. else {
  358. var d = max - min;
  359. s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
  360. switch(max) {
  361. case r: h = (g - b) / d + (g < b ? 6 : 0); break;
  362. case g: h = (b - r) / d + 2; break;
  363. case b: h = (r - g) / d + 4; break;
  364. }
  365. h /= 6;
  366. }
  367. return { h: h, s: s, l: l };
  368. }
  369. // `hslToRgb`
  370. // Converts an HSL color value to RGB.
  371. // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]
  372. // *Returns:* { r, g, b } in the set [0, 255]
  373. function hslToRgb(h, s, l) {
  374. var r, g, b;
  375. h = bound01(h, 360);
  376. s = bound01(s, 100);
  377. l = bound01(l, 100);
  378. function hue2rgb(p, q, t) {
  379. if(t < 0) t += 1;
  380. if(t > 1) t -= 1;
  381. if(t < 1/6) return p + (q - p) * 6 * t;
  382. if(t < 1/2) return q;
  383. if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
  384. return p;
  385. }
  386. if(s === 0) {
  387. r = g = b = l; // achromatic
  388. }
  389. else {
  390. var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
  391. var p = 2 * l - q;
  392. r = hue2rgb(p, q, h + 1/3);
  393. g = hue2rgb(p, q, h);
  394. b = hue2rgb(p, q, h - 1/3);
  395. }
  396. return { r: r * 255, g: g * 255, b: b * 255 };
  397. }
  398. // `rgbToHsv`
  399. // Converts an RGB color value to HSV
  400. // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
  401. // *Returns:* { h, s, v } in [0,1]
  402. function rgbToHsv(r, g, b) {
  403. r = bound01(r, 255);
  404. g = bound01(g, 255);
  405. b = bound01(b, 255);
  406. var max = mathMax(r, g, b), min = mathMin(r, g, b);
  407. var h, s, v = max;
  408. var d = max - min;
  409. s = max === 0 ? 0 : d / max;
  410. if(max == min) {
  411. h = 0; // achromatic
  412. }
  413. else {
  414. switch(max) {
  415. case r: h = (g - b) / d + (g < b ? 6 : 0); break;
  416. case g: h = (b - r) / d + 2; break;
  417. case b: h = (r - g) / d + 4; break;
  418. }
  419. h /= 6;
  420. }
  421. return { h: h, s: s, v: v };
  422. }
  423. // `hsvToRgb`
  424. // Converts an HSV color value to RGB.
  425. // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
  426. // *Returns:* { r, g, b } in the set [0, 255]
  427. function hsvToRgb(h, s, v) {
  428. h = bound01(h, 360) * 6;
  429. s = bound01(s, 100);
  430. v = bound01(v, 100);
  431. var i = Math.floor(h),
  432. f = h - i,
  433. p = v * (1 - s),
  434. q = v * (1 - f * s),
  435. t = v * (1 - (1 - f) * s),
  436. mod = i % 6,
  437. r = [v, q, p, p, t, v][mod],
  438. g = [t, v, v, q, p, p][mod],
  439. b = [p, p, t, v, v, q][mod];
  440. return { r: r * 255, g: g * 255, b: b * 255 };
  441. }
  442. // `rgbToHex`
  443. // Converts an RGB color to hex
  444. // Assumes r, g, and b are contained in the set [0, 255]
  445. // Returns a 3 or 6 character hex
  446. function rgbToHex(r, g, b, allow3Char) {
  447. var hex = [
  448. pad2(mathRound(r).toString(16)),
  449. pad2(mathRound(g).toString(16)),
  450. pad2(mathRound(b).toString(16))
  451. ];
  452. // Return a 3 character hex if possible
  453. if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {
  454. return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
  455. }
  456. return hex.join("");
  457. }
  458. // `rgbaToHex`
  459. // Converts an RGBA color plus alpha transparency to hex
  460. // Assumes r, g, b are contained in the set [0, 255] and
  461. // a in [0, 1]. Returns a 4 or 8 character rgba hex
  462. function rgbaToHex(r, g, b, a, allow4Char) {
  463. var hex = [
  464. pad2(mathRound(r).toString(16)),
  465. pad2(mathRound(g).toString(16)),
  466. pad2(mathRound(b).toString(16)),
  467. pad2(convertDecimalToHex(a))
  468. ];
  469. // Return a 4 character hex if possible
  470. if (allow4Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1) && hex[3].charAt(0) == hex[3].charAt(1)) {
  471. return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0);
  472. }
  473. return hex.join("");
  474. }
  475. // `rgbaToArgbHex`
  476. // Converts an RGBA color to an ARGB Hex8 string
  477. // Rarely used, but required for "toFilter()"
  478. function rgbaToArgbHex(r, g, b, a) {
  479. var hex = [
  480. pad2(convertDecimalToHex(a)),
  481. pad2(mathRound(r).toString(16)),
  482. pad2(mathRound(g).toString(16)),
  483. pad2(mathRound(b).toString(16))
  484. ];
  485. return hex.join("");
  486. }
  487. // `equals`
  488. // Can be called with any tinycolor input
  489. tinycolor.equals = function (color1, color2) {
  490. if (!color1 || !color2) { return false; }
  491. return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();
  492. };
  493. tinycolor.random = function() {
  494. return tinycolor.fromRatio({
  495. r: mathRandom(),
  496. g: mathRandom(),
  497. b: mathRandom()
  498. });
  499. };
  500. // Modification Functions
  501. // ----------------------
  502. // Thanks to less.js for some of the basics here
  503. // <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>
  504. function desaturate(color, amount) {
  505. amount = (amount === 0) ? 0 : (amount || 10);
  506. var hsl = tinycolor(color).toHsl();
  507. hsl.s -= amount / 100;
  508. hsl.s = clamp01(hsl.s);
  509. return tinycolor(hsl);
  510. }
  511. function saturate(color, amount) {
  512. amount = (amount === 0) ? 0 : (amount || 10);
  513. var hsl = tinycolor(color).toHsl();
  514. hsl.s += amount / 100;
  515. hsl.s = clamp01(hsl.s);
  516. return tinycolor(hsl);
  517. }
  518. function greyscale(color) {
  519. return tinycolor(color).desaturate(100);
  520. }
  521. function lighten (color, amount) {
  522. amount = (amount === 0) ? 0 : (amount || 10);
  523. var hsl = tinycolor(color).toHsl();
  524. hsl.l += amount / 100;
  525. hsl.l = clamp01(hsl.l);
  526. return tinycolor(hsl);
  527. }
  528. function brighten(color, amount) {
  529. amount = (amount === 0) ? 0 : (amount || 10);
  530. var rgb = tinycolor(color).toRgb();
  531. rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100))));
  532. rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100))));
  533. rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100))));
  534. return tinycolor(rgb);
  535. }
  536. function darken (color, amount) {
  537. amount = (amount === 0) ? 0 : (amount || 10);
  538. var hsl = tinycolor(color).toHsl();
  539. hsl.l -= amount / 100;
  540. hsl.l = clamp01(hsl.l);
  541. return tinycolor(hsl);
  542. }
  543. // Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.
  544. // Values outside of this range will be wrapped into this range.
  545. function spin(color, amount) {
  546. var hsl = tinycolor(color).toHsl();
  547. var hue = (hsl.h + amount) % 360;
  548. hsl.h = hue < 0 ? 360 + hue : hue;
  549. return tinycolor(hsl);
  550. }
  551. // Combination Functions
  552. // ---------------------
  553. // Thanks to jQuery xColor for some of the ideas behind these
  554. // <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>
  555. function complement(color) {
  556. var hsl = tinycolor(color).toHsl();
  557. hsl.h = (hsl.h + 180) % 360;
  558. return tinycolor(hsl);
  559. }
  560. function triad(color) {
  561. var hsl = tinycolor(color).toHsl();
  562. var h = hsl.h;
  563. return [
  564. tinycolor(color),
  565. tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),
  566. tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })
  567. ];
  568. }
  569. function tetrad(color) {
  570. var hsl = tinycolor(color).toHsl();
  571. var h = hsl.h;
  572. return [
  573. tinycolor(color),
  574. tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),
  575. tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),
  576. tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })
  577. ];
  578. }
  579. function splitcomplement(color) {
  580. var hsl = tinycolor(color).toHsl();
  581. var h = hsl.h;
  582. return [
  583. tinycolor(color),
  584. tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),
  585. tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})
  586. ];
  587. }
  588. function analogous(color, results, slices) {
  589. results = results || 6;
  590. slices = slices || 30;
  591. var hsl = tinycolor(color).toHsl();
  592. var part = 360 / slices;
  593. var ret = [tinycolor(color)];
  594. for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {
  595. hsl.h = (hsl.h + part) % 360;
  596. ret.push(tinycolor(hsl));
  597. }
  598. return ret;
  599. }
  600. function monochromatic(color, results) {
  601. results = results || 6;
  602. var hsv = tinycolor(color).toHsv();
  603. var h = hsv.h, s = hsv.s, v = hsv.v;
  604. var ret = [];
  605. var modification = 1 / results;
  606. while (results--) {
  607. ret.push(tinycolor({ h: h, s: s, v: v}));
  608. v = (v + modification) % 1;
  609. }
  610. return ret;
  611. }
  612. // Utility Functions
  613. // ---------------------
  614. tinycolor.mix = function(color1, color2, amount) {
  615. amount = (amount === 0) ? 0 : (amount || 50);
  616. var rgb1 = tinycolor(color1).toRgb();
  617. var rgb2 = tinycolor(color2).toRgb();
  618. var p = amount / 100;
  619. var rgba = {
  620. r: ((rgb2.r - rgb1.r) * p) + rgb1.r,
  621. g: ((rgb2.g - rgb1.g) * p) + rgb1.g,
  622. b: ((rgb2.b - rgb1.b) * p) + rgb1.b,
  623. a: ((rgb2.a - rgb1.a) * p) + rgb1.a
  624. };
  625. return tinycolor(rgba);
  626. };
  627. // Readability Functions
  628. // ---------------------
  629. // <http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef (WCAG Version 2)
  630. // `contrast`
  631. // Analyze the 2 colors and returns the color contrast defined by (WCAG Version 2)
  632. tinycolor.readability = function(color1, color2) {
  633. var c1 = tinycolor(color1);
  634. var c2 = tinycolor(color2);
  635. return (Math.max(c1.getLuminance(),c2.getLuminance())+0.05) / (Math.min(c1.getLuminance(),c2.getLuminance())+0.05);
  636. };
  637. // `isReadable`
  638. // Ensure that foreground and background color combinations meet WCAG2 guidelines.
  639. // The third argument is an optional Object.
  640. // the 'level' property states 'AA' or 'AAA' - if missing or invalid, it defaults to 'AA';
  641. // the 'size' property states 'large' or 'small' - if missing or invalid, it defaults to 'small'.
  642. // If the entire object is absent, isReadable defaults to {level:"AA",size:"small"}.
  643. // *Example*
  644. // tinycolor.isReadable("#000", "#111") => false
  645. // tinycolor.isReadable("#000", "#111",{level:"AA",size:"large"}) => false
  646. tinycolor.isReadable = function(color1, color2, wcag2) {
  647. var readability = tinycolor.readability(color1, color2);
  648. var wcag2Parms, out;
  649. out = false;
  650. wcag2Parms = validateWCAG2Parms(wcag2);
  651. switch (wcag2Parms.level + wcag2Parms.size) {
  652. case "AAsmall":
  653. case "AAAlarge":
  654. out = readability >= 4.5;
  655. break;
  656. case "AAlarge":
  657. out = readability >= 3;
  658. break;
  659. case "AAAsmall":
  660. out = readability >= 7;
  661. break;
  662. }
  663. return out;
  664. };
  665. // `mostReadable`
  666. // Given a base color and a list of possible foreground or background
  667. // colors for that base, returns the most readable color.
  668. // Optionally returns Black or White if the most readable color is unreadable.
  669. // *Example*
  670. // tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:false}).toHexString(); // "#112255"
  671. // tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:true}).toHexString(); // "#ffffff"
  672. // tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"large"}).toHexString(); // "#faf3f3"
  673. // tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"small"}).toHexString(); // "#ffffff"
  674. tinycolor.mostReadable = function(baseColor, colorList, args) {
  675. var bestColor = null;
  676. var bestScore = 0;
  677. var readability;
  678. var includeFallbackColors, level, size ;
  679. args = args || {};
  680. includeFallbackColors = args.includeFallbackColors ;
  681. level = args.level;
  682. size = args.size;
  683. for (var i= 0; i < colorList.length ; i++) {
  684. readability = tinycolor.readability(baseColor, colorList[i]);
  685. if (readability > bestScore) {
  686. bestScore = readability;
  687. bestColor = tinycolor(colorList[i]);
  688. }
  689. }
  690. if (tinycolor.isReadable(baseColor, bestColor, {"level":level,"size":size}) || !includeFallbackColors) {
  691. return bestColor;
  692. }
  693. else {
  694. args.includeFallbackColors=false;
  695. return tinycolor.mostReadable(baseColor,["#fff", "#000"],args);
  696. }
  697. };
  698. // Big List of Colors
  699. // ------------------
  700. // <http://www.w3.org/TR/css3-color/#svg-color>
  701. var names = tinycolor.names = {
  702. aliceblue: "f0f8ff",
  703. antiquewhite: "faebd7",
  704. aqua: "0ff",
  705. aquamarine: "7fffd4",
  706. azure: "f0ffff",
  707. beige: "f5f5dc",
  708. bisque: "ffe4c4",
  709. black: "000",
  710. blanchedalmond: "ffebcd",
  711. blue: "00f",
  712. blueviolet: "8a2be2",
  713. brown: "a52a2a",
  714. burlywood: "deb887",
  715. burntsienna: "ea7e5d",
  716. cadetblue: "5f9ea0",
  717. chartreuse: "7fff00",
  718. chocolate: "d2691e",
  719. coral: "ff7f50",
  720. cornflowerblue: "6495ed",
  721. cornsilk: "fff8dc",
  722. crimson: "dc143c",
  723. cyan: "0ff",
  724. darkblue: "00008b",
  725. darkcyan: "008b8b",
  726. darkgoldenrod: "b8860b",
  727. darkgray: "a9a9a9",
  728. darkgreen: "006400",
  729. darkgrey: "a9a9a9",
  730. darkkhaki: "bdb76b",
  731. darkmagenta: "8b008b",
  732. darkolivegreen: "556b2f",
  733. darkorange: "ff8c00",
  734. darkorchid: "9932cc",
  735. darkred: "8b0000",
  736. darksalmon: "e9967a",
  737. darkseagreen: "8fbc8f",
  738. darkslateblue: "483d8b",
  739. darkslategray: "2f4f4f",
  740. darkslategrey: "2f4f4f",
  741. darkturquoise: "00ced1",
  742. darkviolet: "9400d3",
  743. deeppink: "ff1493",
  744. deepskyblue: "00bfff",
  745. dimgray: "696969",
  746. dimgrey: "696969",
  747. dodgerblue: "1e90ff",
  748. firebrick: "b22222",
  749. floralwhite: "fffaf0",
  750. forestgreen: "228b22",
  751. fuchsia: "f0f",
  752. gainsboro: "dcdcdc",
  753. ghostwhite: "f8f8ff",
  754. gold: "ffd700",
  755. goldenrod: "daa520",
  756. gray: "808080",
  757. green: "008000",
  758. greenyellow: "adff2f",
  759. grey: "808080",
  760. honeydew: "f0fff0",
  761. hotpink: "ff69b4",
  762. indianred: "cd5c5c",
  763. indigo: "4b0082",
  764. ivory: "fffff0",
  765. khaki: "f0e68c",
  766. lavender: "e6e6fa",
  767. lavenderblush: "fff0f5",
  768. lawngreen: "7cfc00",
  769. lemonchiffon: "fffacd",
  770. lightblue: "add8e6",
  771. lightcoral: "f08080",
  772. lightcyan: "e0ffff",
  773. lightgoldenrodyellow: "fafad2",
  774. lightgray: "d3d3d3",
  775. lightgreen: "90ee90",
  776. lightgrey: "d3d3d3",
  777. lightpink: "ffb6c1",
  778. lightsalmon: "ffa07a",
  779. lightseagreen: "20b2aa",
  780. lightskyblue: "87cefa",
  781. lightslategray: "789",
  782. lightslategrey: "789",
  783. lightsteelblue: "b0c4de",
  784. lightyellow: "ffffe0",
  785. lime: "0f0",
  786. limegreen: "32cd32",
  787. linen: "faf0e6",
  788. magenta: "f0f",
  789. maroon: "800000",
  790. mediumaquamarine: "66cdaa",
  791. mediumblue: "0000cd",
  792. mediumorchid: "ba55d3",
  793. mediumpurple: "9370db",
  794. mediumseagreen: "3cb371",
  795. mediumslateblue: "7b68ee",
  796. mediumspringgreen: "00fa9a",
  797. mediumturquoise: "48d1cc",
  798. mediumvioletred: "c71585",
  799. midnightblue: "191970",
  800. mintcream: "f5fffa",
  801. mistyrose: "ffe4e1",
  802. moccasin: "ffe4b5",
  803. navajowhite: "ffdead",
  804. navy: "000080",
  805. oldlace: "fdf5e6",
  806. olive: "808000",
  807. olivedrab: "6b8e23",
  808. orange: "ffa500",
  809. orangered: "ff4500",
  810. orchid: "da70d6",
  811. palegoldenrod: "eee8aa",
  812. palegreen: "98fb98",
  813. paleturquoise: "afeeee",
  814. palevioletred: "db7093",
  815. papayawhip: "ffefd5",
  816. peachpuff: "ffdab9",
  817. peru: "cd853f",
  818. pink: "ffc0cb",
  819. plum: "dda0dd",
  820. powderblue: "b0e0e6",
  821. purple: "800080",
  822. rebeccapurple: "663399",
  823. red: "f00",
  824. rosybrown: "bc8f8f",
  825. royalblue: "4169e1",
  826. saddlebrown: "8b4513",
  827. salmon: "fa8072",
  828. sandybrown: "f4a460",
  829. seagreen: "2e8b57",
  830. seashell: "fff5ee",
  831. sienna: "a0522d",
  832. silver: "c0c0c0",
  833. skyblue: "87ceeb",
  834. slateblue: "6a5acd",
  835. slategray: "708090",
  836. slategrey: "708090",
  837. snow: "fffafa",
  838. springgreen: "00ff7f",
  839. steelblue: "4682b4",
  840. tan: "d2b48c",
  841. teal: "008080",
  842. thistle: "d8bfd8",
  843. tomato: "ff6347",
  844. turquoise: "40e0d0",
  845. violet: "ee82ee",
  846. wheat: "f5deb3",
  847. white: "fff",
  848. whitesmoke: "f5f5f5",
  849. yellow: "ff0",
  850. yellowgreen: "9acd32"
  851. };
  852. // Make it easy to access colors via `hexNames[hex]`
  853. var hexNames = tinycolor.hexNames = flip(names);
  854. // Utilities
  855. // ---------
  856. // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`
  857. function flip(o) {
  858. var flipped = { };
  859. for (var i in o) {
  860. if (o.hasOwnProperty(i)) {
  861. flipped[o[i]] = i;
  862. }
  863. }
  864. return flipped;
  865. }
  866. // Return a valid alpha value [0,1] with all invalid values being set to 1
  867. function boundAlpha(a) {
  868. a = parseFloat(a);
  869. if (isNaN(a) || a < 0 || a > 1) {
  870. a = 1;
  871. }
  872. return a;
  873. }
  874. // Take input from [0, n] and return it as [0, 1]
  875. function bound01(n, max) {
  876. if (isOnePointZero(n)) { n = "100%"; }
  877. var processPercent = isPercentage(n);
  878. n = mathMin(max, mathMax(0, parseFloat(n)));
  879. // Automatically convert percentage into number
  880. if (processPercent) {
  881. n = parseInt(n * max, 10) / 100;
  882. }
  883. // Handle floating point rounding errors
  884. if ((Math.abs(n - max) < 0.000001)) {
  885. return 1;
  886. }
  887. // Convert into [0, 1] range if it isn't already
  888. return (n % max) / parseFloat(max);
  889. }
  890. // Force a number between 0 and 1
  891. function clamp01(val) {
  892. return mathMin(1, mathMax(0, val));
  893. }
  894. // Parse a base-16 hex value into a base-10 integer
  895. function parseIntFromHex(val) {
  896. return parseInt(val, 16);
  897. }
  898. // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
  899. // <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
  900. function isOnePointZero(n) {
  901. return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1;
  902. }
  903. // Check to see if string passed in is a percentage
  904. function isPercentage(n) {
  905. return typeof n === "string" && n.indexOf('%') != -1;
  906. }
  907. // Force a hex value to have 2 characters
  908. function pad2(c) {
  909. return c.length == 1 ? '0' + c : '' + c;
  910. }
  911. // Replace a decimal with it's percentage value
  912. function convertToPercentage(n) {
  913. if (n <= 1) {
  914. n = (n * 100) + "%";
  915. }
  916. return n;
  917. }
  918. // Converts a decimal to a hex value
  919. function convertDecimalToHex(d) {
  920. return Math.round(parseFloat(d) * 255).toString(16);
  921. }
  922. // Converts a hex value to a decimal
  923. function convertHexToDecimal(h) {
  924. return (parseIntFromHex(h) / 255);
  925. }
  926. var matchers = (function() {
  927. // <http://www.w3.org/TR/css3-values/#integers>
  928. var CSS_INTEGER = "[-\\+]?\\d+%?";
  929. // <http://www.w3.org/TR/css3-values/#number-value>
  930. var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";
  931. // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
  932. var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";
  933. // Actual matching.
  934. // Parentheses and commas are optional, but not required.
  935. // Whitespace can take the place of commas or opening paren
  936. var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
  937. var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
  938. return {
  939. CSS_UNIT: new RegExp(CSS_UNIT),
  940. rgb: new RegExp("rgb" + PERMISSIVE_MATCH3),
  941. rgba: new RegExp("rgba" + PERMISSIVE_MATCH4),
  942. hsl: new RegExp("hsl" + PERMISSIVE_MATCH3),
  943. hsla: new RegExp("hsla" + PERMISSIVE_MATCH4),
  944. hsv: new RegExp("hsv" + PERMISSIVE_MATCH3),
  945. hsva: new RegExp("hsva" + PERMISSIVE_MATCH4),
  946. hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
  947. hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
  948. hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
  949. hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
  950. };
  951. })();
  952. // `isValidCSSUnit`
  953. // Take in a single string / number and check to see if it looks like a CSS unit
  954. // (see `matchers` above for definition).
  955. function isValidCSSUnit(color) {
  956. return !!matchers.CSS_UNIT.exec(color);
  957. }
  958. // `stringInputToObject`
  959. // Permissive string parsing. Take in a number of formats, and output an object
  960. // based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`
  961. function stringInputToObject(color) {
  962. color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();
  963. var named = false;
  964. if (names[color]) {
  965. color = names[color];
  966. named = true;
  967. }
  968. else if (color == 'transparent') {
  969. return { r: 0, g: 0, b: 0, a: 0, format: "name" };
  970. }
  971. // Try to match string input using regular expressions.
  972. // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]
  973. // Just return an object and let the conversion functions handle that.
  974. // This way the result will be the same whether the tinycolor is initialized with string or object.
  975. var match;
  976. if ((match = matchers.rgb.exec(color))) {
  977. return { r: match[1], g: match[2], b: match[3] };
  978. }
  979. if ((match = matchers.rgba.exec(color))) {
  980. return { r: match[1], g: match[2], b: match[3], a: match[4] };
  981. }
  982. if ((match = matchers.hsl.exec(color))) {
  983. return { h: match[1], s: match[2], l: match[3] };
  984. }
  985. if ((match = matchers.hsla.exec(color))) {
  986. return { h: match[1], s: match[2], l: match[3], a: match[4] };
  987. }
  988. if ((match = matchers.hsv.exec(color))) {
  989. return { h: match[1], s: match[2], v: match[3] };
  990. }
  991. if ((match = matchers.hsva.exec(color))) {
  992. return { h: match[1], s: match[2], v: match[3], a: match[4] };
  993. }
  994. if ((match = matchers.hex8.exec(color))) {
  995. return {
  996. r: parseIntFromHex(match[1]),
  997. g: parseIntFromHex(match[2]),
  998. b: parseIntFromHex(match[3]),
  999. a: convertHexToDecimal(match[4]),
  1000. format: named ? "name" : "hex8"
  1001. };
  1002. }
  1003. if ((match = matchers.hex6.exec(color))) {
  1004. return {
  1005. r: parseIntFromHex(match[1]),
  1006. g: parseIntFromHex(match[2]),
  1007. b: parseIntFromHex(match[3]),
  1008. format: named ? "name" : "hex"
  1009. };
  1010. }
  1011. if ((match = matchers.hex4.exec(color))) {
  1012. return {
  1013. r: parseIntFromHex(match[1] + '' + match[1]),
  1014. g: parseIntFromHex(match[2] + '' + match[2]),
  1015. b: parseIntFromHex(match[3] + '' + match[3]),
  1016. a: convertHexToDecimal(match[4] + '' + match[4]),
  1017. format: named ? "name" : "hex8"
  1018. };
  1019. }
  1020. if ((match = matchers.hex3.exec(color))) {
  1021. return {
  1022. r: parseIntFromHex(match[1] + '' + match[1]),
  1023. g: parseIntFromHex(match[2] + '' + match[2]),
  1024. b: parseIntFromHex(match[3] + '' + match[3]),
  1025. format: named ? "name" : "hex"
  1026. };
  1027. }
  1028. return false;
  1029. }
  1030. function validateWCAG2Parms(parms) {
  1031. // return valid WCAG2 parms for isReadable.
  1032. // If input parms are invalid, return {"level":"AA", "size":"small"}
  1033. var level, size;
  1034. parms = parms || {"level":"AA", "size":"small"};
  1035. level = (parms.level || "AA").toUpperCase();
  1036. size = (parms.size || "small").toLowerCase();
  1037. if (level !== "AA" && level !== "AAA") {
  1038. level = "AA";
  1039. }
  1040. if (size !== "small" && size !== "large") {
  1041. size = "small";
  1042. }
  1043. return {"level":level, "size":size};
  1044. }
  1045. // Node: Export function
  1046. if (typeof module !== "undefined" && module.exports) {
  1047. module.exports = tinycolor;
  1048. }
  1049. // AMD/requirejs: Define the module
  1050. else if (typeof define === 'function' && define.amd) {
  1051. define(function () {return tinycolor;});
  1052. }
  1053. // Browser: Expose to window
  1054. else {
  1055. window.tinycolor = tinycolor;
  1056. }
  1057. })(Math);