├── npm ├── cjs │ ├── package.json │ └── test_template.js ├── esm │ ├── package.json │ ├── test_template.js │ └── tinycolor.js ├── package.json ├── package-lock.json ├── LICENSE ├── dist │ └── tinycolor-min.js ├── README.md └── deno_asserts@0.168.0.mjs ├── .gitignore ├── deno.json ├── LICENSE ├── .github └── workflows │ └── deno.yml ├── demo ├── demo.css └── server.jsx ├── index.html ├── dist └── tinycolor-min.js ├── README.md └── mod.js /npm/cjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "commonjs" 3 | } 4 | -------------------------------------------------------------------------------- /npm/esm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | ehthumbs.db 3 | Icon? 4 | Thumbs.db 5 | node_modules 6 | .idea 7 | .vscode 8 | cov_profile 9 | cov_profile.lcov 10 | -------------------------------------------------------------------------------- /npm/esm/test_template.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated. 2 | // Ideally it wouldn't exist, but it's here to test cjs in node 3 | // Changes should go into ./test.js, and if new assertions are needed 4 | // they'll need to be shimmed here as well 5 | import tinycolor from "./tinycolor.js"; 6 | import { Deno, testDefinitions } from "@deno/shim-deno-test"; 7 | const { assertEquals, assert, assertThrows } = await import( 8 | "../deno_asserts@0.168.0.mjs" 9 | ); 10 | async function runDenoTests() { 11 | for (const test of testDefinitions) { 12 | if (test.ignore) { 13 | console.log(`Ignoring ${test.name}`); 14 | continue; 15 | } 16 | console.log(`Running ${test.name}`); 17 | await test.fn(); 18 | console.log(`> Passed ${test.name}`); 19 | } 20 | } 21 | 22 | // CONTENT_GOES_HERE 23 | 24 | runDenoTests(); 25 | -------------------------------------------------------------------------------- /npm/cjs/test_template.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated. 2 | // Ideally it wouldn't exist, but it's here to test cjs in node 3 | // Changes should go into ./test.js, and if new assertions are needed 4 | // they'll need to be shimmed here as well 5 | const tinycolor = require("./tinycolor.js"); 6 | const { Deno, testDefinitions } = require("@deno/shim-deno-test"); 7 | async function runDenoTests() { 8 | for (const test of testDefinitions) { 9 | if (test.ignore) { 10 | console.log(`Ignoring ${test.name}`); 11 | continue; 12 | } 13 | console.log(`Running ${test.name}`); 14 | await test.fn(); 15 | console.log(`> Passed ${test.name}`); 16 | } 17 | } 18 | (async () => { 19 | const { assertEquals, assert, assertThrows } = await import( 20 | "../deno_asserts@0.168.0.mjs" 21 | ); 22 | 23 | // CONTENT_GOES_HERE 24 | 25 | runDenoTests(); 26 | })(); 27 | -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "test": "deno test", 4 | "dev": "deno test --watch test.js", 5 | "coverage": "deno test --coverage=cov_profile && deno coverage cov_profile --lcov --output=cov_profile.lcov && genhtml -o cov_profile/html cov_profile.lcov && open cov_profile/html/TinyColor/mod.js.gcov.html", 6 | "serve": "deno run --watch --allow-env --allow-net=unpkg.com,0.0.0.0 --allow-read=. demo/server.jsx", 7 | "test-npm": "npm install --prefix npm && npm test --prefix npm", 8 | "build": "deno task test && deno run --allow-env --allow-read --allow-write=. build.js && deno task test-npm", 9 | "publish": "deno task build && cd npm && npm publish --access public", 10 | "publish-beta": "deno task build && cd npm && npm publish --tag beta --access public" 11 | }, 12 | "test": { 13 | "files": { 14 | "exclude": [ 15 | "npm" 16 | ] 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /npm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.6.0", 3 | "name": "tinycolor2", 4 | "description": "Fast Color Parsing and Manipulation", 5 | "url": "http://bgrins.github.com/TinyColor", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/bgrins/TinyColor.git" 10 | }, 11 | "keywords": [ 12 | "color", 13 | "parser", 14 | "tinycolor" 15 | ], 16 | "author": "Brian Grinstead (http://briangrinstead.com)", 17 | "bugs": { 18 | "url": "https://github.com/bgrins/TinyColor/issues" 19 | }, 20 | "module": "./esm/tinycolor.js", 21 | "main": "./cjs/tinycolor.js", 22 | "browser": "./cjs/tinycolor.js", 23 | "exports": { 24 | ".": { 25 | "import": "./esm/tinycolor.js", 26 | "require": "./cjs/tinycolor.js" 27 | } 28 | }, 29 | "scripts": { 30 | "test": "node cjs/test.js && node esm/test.js" 31 | }, 32 | "devDependencies": { 33 | "@deno/shim-deno-test": "^0.4.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /npm/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tinycolor2", 3 | "version": "1.6.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "tinycolor2", 9 | "version": "1.6.0", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@deno/shim-deno-test": "^0.4.0" 13 | } 14 | }, 15 | "node_modules/@deno/shim-deno-test": { 16 | "version": "0.4.0", 17 | "resolved": "https://registry.npmjs.org/@deno/shim-deno-test/-/shim-deno-test-0.4.0.tgz", 18 | "integrity": "sha512-oYWcD7CpERZy/TXMTM9Tgh1HD/POHlbY9WpzmAk+5H8DohcxG415Qws8yLGlim3EaKBT2v3lJv01x4G0BosnaQ==", 19 | "dev": true 20 | } 21 | }, 22 | "dependencies": { 23 | "@deno/shim-deno-test": { 24 | "version": "0.4.0", 25 | "resolved": "https://registry.npmjs.org/@deno/shim-deno-test/-/shim-deno-test-0.4.0.tgz", 26 | "integrity": "sha512-oYWcD7CpERZy/TXMTM9Tgh1HD/POHlbY9WpzmAk+5H8DohcxG415Qws8yLGlim3EaKBT2v3lJv01x4G0BosnaQ==", 27 | "dev": true 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c), Brian Grinstead, http://briangrinstead.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /npm/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c), Brian Grinstead, http://briangrinstead.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /.github/workflows/deno.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | 6 | # This workflow will install Deno then run `deno lint` and `deno test`. 7 | # For more information see: https://github.com/denoland/setup-deno 8 | 9 | name: Deno 10 | 11 | on: 12 | push: 13 | branches: ["master"] 14 | pull_request: 15 | branches: ["master"] 16 | 17 | permissions: 18 | contents: read 19 | 20 | jobs: 21 | test: 22 | runs-on: ubuntu-latest 23 | 24 | steps: 25 | - name: Setup repo 26 | uses: actions/checkout@v3 27 | 28 | - name: Setup Deno 29 | # uses: denoland/setup-deno@v1 30 | uses: denoland/setup-deno@9db7f66e8e16b5699a514448ce994936c63f0d54 31 | with: 32 | deno-version: v1.x 33 | 34 | # Uncomment this step to verify the use of 'deno fmt' on each commit. 35 | # - name: Verify formatting 36 | # run: deno fmt --check 37 | 38 | # - name: Run linter 39 | # run: deno lint 40 | 41 | - name: Build & Run Tests 42 | run: deno task build 43 | -------------------------------------------------------------------------------- /demo/demo.css: -------------------------------------------------------------------------------- 1 | 2 | html { font-size: 100%; overflow-y: scroll; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } 3 | body { font-size: 15px; line-height: 1.231; margin: 4px; background-color: #FCFFFD; } 4 | body, button, input, select, textarea { font-family: Droid Sans, Verdana, sans-serif; color: #222; } 5 | a { color: #33f; } 6 | table th { width: 200px; text-align: right; padding-right:10px; font-family: Georgia serif;} 7 | #container { width: 90%; margin:0 auto; } 8 | #code-output { border: solid 5px #333; margin:15px; border-top-width: 40px; } 9 | #color { width: 200px; height: 2em;} 10 | #inputter a { padding:3px; } 11 | .invisible { visibility: hidden; } 12 | h1 { font-size: 3.8em; color: #293c11; font-family:Tahoma; margin: 3px 0; padding:0; } 13 | h1 .small { font-size: 0.4em; } 14 | h1 a { text-decoration: none } 15 | h2 { font-size: 1.5em; color: #293c11; margin: 0;margin-left: 10px; } 16 | h3 { color: #293c11; } 17 | .description { font-size: 1.2em; margin-bottom: 30px; margin-top: 30px; font-style: italic;} 18 | .download { float: right; } 19 | pre { background: #000; color: #fff; padding: 15px;} 20 | hr { border: 0; width: 80%; border-bottom: 1px solid #aaa} 21 | .footer { text-align:center; padding-top:30px; font-style: italic; } 22 | 23 | #filter-output div, #combine-output span { width: 40px; height: 40px; display:inline-block; margin: 10px; } -------------------------------------------------------------------------------- /demo/server.jsx: -------------------------------------------------------------------------------- 1 | /** @jsxImportSource https://esm.sh/preact */ 2 | import { ImageResponse } from "https://deno.land/x/og_edge/mod.ts"; 3 | import { h } from "https://esm.sh/preact"; 4 | import render from "https://esm.sh/preact-render-to-string"; 5 | import { serve } from "https://deno.land/std@0.126.0/http/server.ts"; 6 | import { serveFile } from "https://deno.land/std@0.140.0/http/file_server.ts"; 7 | import tinycolor from "../mod.js"; 8 | 9 | function color_detail(color) { 10 | return { 11 | hex: color.toHexString(), 12 | hex8: color.toHex8String(), 13 | rgb: color.toRgbString(), 14 | hsl: color.toHslString(), 15 | hsv: color.toHsvString(), 16 | name: color.toName() || "none", 17 | format: color.getFormat(), 18 | lighten: color.lighten(20).toHexString(), 19 | darken: color.darken(20).toHexString(), 20 | saturate: color.saturate(20).toHexString(), 21 | desaturate: color.desaturate(20).toHexString(), 22 | greyscale: color.greyscale().toHexString(), 23 | brighten: color.brighten(20).toHexString(), 24 | triad: color.triad().map((c) => c.toHexString()), 25 | tetrad: color.tetrad().map((c) => c.toHexString()), 26 | monochromatic: color.monochromatic().map((c) => c.toHexString()), 27 | analogous: color.analogous().map((c) => c.toHexString()), 28 | splitcomplement: color.splitcomplement().map((c) => c.toHexString()), 29 | }; 30 | } 31 | async function api_handler(req, input) { 32 | const color = tinycolor(input); 33 | if (!color.isValid()) { 34 | return new Response( 35 | JSON.stringify({ 36 | error: "Invalid color", 37 | }), 38 | { 39 | status: 400, 40 | headers: { 41 | "Content-Type": "application/json", 42 | }, 43 | } 44 | ); 45 | } 46 | return new Response( 47 | JSON.stringify(Object.assign({ input }, color_detail(color)), null, 2), 48 | { 49 | headers: { 50 | "Content-Type": "application/json", 51 | }, 52 | } 53 | ); 54 | } 55 | async function image_handler(req, input) { 56 | const color = tinycolor(input || "lavender"); 57 | console.log(input, color.toHexString()); 58 | const { searchParams } = new URL(req.url); 59 | const fontSize = searchParams.get("fontSize") || 60; 60 | const width = searchParams.get("width") || 1200; 61 | const height = searchParams.get("height") || 627; 62 | const debug = searchParams.get("debug") || false; 63 | const detail = color_detail(color); 64 | console.log(detail); 65 | if (!color.isValid()) { 66 | return new Response("Invalid color", { status: 400 }); 67 | } 68 | 69 | let textColor = tinycolor.mostReadable(color, ["#333", "#ddd"]); 70 | return new ImageResponse( 71 | ( 72 |
85 |
{detail.name}
86 |
{detail.hex}
87 |
{detail.rgb}
88 |
{detail.hsl}
89 |
{detail.hsv}
90 |
91 | ), 92 | { 93 | debug, 94 | width, 95 | height, 96 | } 97 | ); 98 | } 99 | 100 | const port = 8080; 101 | const handler = (req) => { 102 | const url = new URL(req.url); 103 | const { pathname } = url; 104 | if (pathname === "/") { 105 | return serveFile(req, "./index.html"); 106 | } 107 | if (["/demo/demo.css", "/tinycolor.js"].includes(pathname)) { 108 | return serveFile(req, `.${pathname}`); 109 | } 110 | if (pathname.startsWith("/api/")) { 111 | const input = pathname.replace("/api/", ""); 112 | return api_handler(req, input); 113 | } 114 | if (pathname.startsWith("/graphic/")) { 115 | const input = pathname.replace("/graphic/", ""); 116 | return image_handler(req, input); 117 | } 118 | return new Response("Not Found", { status: 404 }); 119 | }; 120 | 121 | console.log(`HTTP webserver running. Access it at: http://localhost:8080/`); 122 | await serve(handler, { port }); 123 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | TinyColor - Fast, small color manipulation in JavaScript 8 | 9 | 10 | 11 | 102 | 103 | 104 | 105 |
106 |

TinyColor

107 |

Fast, small color manipulation and conversion for JavaScript

108 | 109 |

110 | TinyColor is a micro framework for inputting colors and 111 | outputting colors as different formats. 112 | Input is meant to be as permissive as possible. 113 |

114 | 115 |

Usage Documentation

116 |

Read all the documentation on the TinyColor project page on 117 | github.

118 | 119 |

Tests

120 |

View the tests.

121 | 122 |

Demo

123 |
124 |
125 |

126 | Enter a color: 127 |

128 |

129 | Or try these: 130 | red 131 | 0f0 132 | rgb 255 128 128 133 | hsl(0, 100%, 50%) 134 | hsv 0, 100%, 50% 135 |

136 |

And I'll tell you what I know about it:

137 |
138 | 139 |

140 | 
141 | 			
142 | 143 | 144 | 145 | 148 | 149 | 150 | 151 | 154 | 155 | 156 | 157 | 160 | 161 | 162 | 163 | 166 | 167 | 168 | 169 | 172 | 173 | 174 | 175 | 178 | 179 | 180 | 181 | 184 | 185 |
Lighten 146 |
147 |
Darken 152 |
153 |
Saturate 158 |
159 |
Desaturate 164 |
165 |
Greyscale 170 |
171 |
Brighten 176 |
177 |
Most Readable 182 |
183 |
186 |
187 | 188 |
189 | 190 | 191 | 192 | 195 | 196 | 197 | 198 | 201 | 202 | 203 | 204 | 207 | 208 | 209 | 210 | 213 | 214 | 215 | 216 | 219 | 220 |
Triad 193 |
194 |
Tetrad 199 |
200 |
Monochromatic 205 |
206 |
Analogous 211 |
212 |
Split Complements 217 |
218 |
221 |
222 |
223 | 224 | 225 |

Credit

226 |

227 | Developed by Brian Grinstead. Big thanks to the following places: 228 |

229 | 240 | 241 |
242 | 243 | 244 | -------------------------------------------------------------------------------- /dist/tinycolor-min.js: -------------------------------------------------------------------------------- 1 | !function(t,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):(t="undefined"!=typeof globalThis?globalThis:t||self).tinycolor=r()}(this,(function(){"use strict";function t(r){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},t(r)}var r=/^\s+/,e=/\s+$/;function n(a,i){if(i=i||{},(a=a||"")instanceof n)return a;if(!(this instanceof n))return new n(a,i);var o=function(n){var a={r:0,g:0,b:0},i=1,o=null,h=null,s=null,f=!1,u=!1;"string"==typeof n&&(n=function(t){t=t.replace(r,"").replace(e,"").toLowerCase();var n,a=!1;if(y[t])t=y[t],a=!0;else if("transparent"==t)return{r:0,g:0,b:0,a:0,format:"name"};if(n=T.rgb.exec(t))return{r:n[1],g:n[2],b:n[3]};if(n=T.rgba.exec(t))return{r:n[1],g:n[2],b:n[3],a:n[4]};if(n=T.hsl.exec(t))return{h:n[1],s:n[2],l:n[3]};if(n=T.hsla.exec(t))return{h:n[1],s:n[2],l:n[3],a:n[4]};if(n=T.hsv.exec(t))return{h:n[1],s:n[2],v:n[3]};if(n=T.hsva.exec(t))return{h:n[1],s:n[2],v:n[3],a:n[4]};if(n=T.hex8.exec(t))return{r:w(n[1]),g:w(n[2]),b:w(n[3]),a:F(n[4]),format:a?"name":"hex8"};if(n=T.hex6.exec(t))return{r:w(n[1]),g:w(n[2]),b:w(n[3]),format:a?"name":"hex"};if(n=T.hex4.exec(t))return{r:w(n[1]+""+n[1]),g:w(n[2]+""+n[2]),b:w(n[3]+""+n[3]),a:F(n[4]+""+n[4]),format:a?"name":"hex8"};if(n=T.hex3.exec(t))return{r:w(n[1]+""+n[1]),g:w(n[2]+""+n[2]),b:w(n[3]+""+n[3]),format:a?"name":"hex"};return!1}(n));"object"==t(n)&&(E(n.r)&&E(n.g)&&E(n.b)?(l=n.r,c=n.g,d=n.b,a={r:255*A(l,255),g:255*A(c,255),b:255*A(d,255)},f=!0,u="%"===String(n.r).substr(-1)?"prgb":"rgb"):E(n.h)&&E(n.s)&&E(n.v)?(o=H(n.s),h=H(n.v),a=function(t,r,e){t=6*A(t,360),r=A(r,100),e=A(e,100);var n=Math.floor(t),a=t-n,i=e*(1-r),o=e*(1-a*r),h=e*(1-(1-a)*r),s=n%6,f=[e,o,i,i,h,e][s],u=[h,e,e,o,i,i][s],l=[i,i,h,e,e,o][s];return{r:255*f,g:255*u,b:255*l}}(n.h,o,h),f=!0,u="hsv"):E(n.h)&&E(n.s)&&E(n.l)&&(o=H(n.s),s=H(n.l),a=function(t,r,e){var n,a,i;function o(t,r,e){return e<0&&(e+=1),e>1&&(e-=1),e<1/6?t+6*(r-t)*e:e<.5?r:e<2/3?t+(r-t)*(2/3-e)*6:t}if(t=A(t,360),r=A(r,100),e=A(e,100),0===r)n=a=i=e;else{var h=e<.5?e*(1+r):e+r-e*r,s=2*e-h;n=o(s,h,t+1/3),a=o(s,h,t),i=o(s,h,t-1/3)}return{r:255*n,g:255*a,b:255*i}}(n.h,o,s),f=!0,u="hsl"),n.hasOwnProperty("a")&&(i=n.a));var l,c,d;return i=x(i),{ok:f,format:n.format||u,r:Math.min(255,Math.max(a.r,0)),g:Math.min(255,Math.max(a.g,0)),b:Math.min(255,Math.max(a.b,0)),a:i}}(a);this._originalInput=a,this._r=o.r,this._g=o.g,this._b=o.b,this._a=o.a,this._roundA=Math.round(100*this._a)/100,this._format=i.format||o.format,this._gradientType=i.gradientType,this._r<1&&(this._r=Math.round(this._r)),this._g<1&&(this._g=Math.round(this._g)),this._b<1&&(this._b=Math.round(this._b)),this._ok=o.ok}function a(t,r,e){t=A(t,255),r=A(r,255),e=A(e,255);var n,a,i=Math.max(t,r,e),o=Math.min(t,r,e),h=(i+o)/2;if(i==o)n=a=0;else{var s=i-o;switch(a=h>.5?s/(2-i-o):s/(i+o),i){case t:n=(r-e)/s+(r>1)+720)%360;--r;)a.h=(a.h+i)%360,o.push(n(a));return o}function v(t,r){r=r||6;for(var e=n(t).toHsv(),a=e.h,i=e.s,o=e.v,h=[],s=1/r;r--;)h.push(n({h:a,s:i,v:o})),o=(o+s)%1;return h}n.prototype={isDark:function(){return this.getBrightness()<128},isLight:function(){return!this.isDark()},isValid:function(){return this._ok},getOriginalInput:function(){return this._originalInput},getFormat:function(){return this._format},getAlpha:function(){return this._a},getBrightness:function(){var t=this.toRgb();return(299*t.r+587*t.g+114*t.b)/1e3},getLuminance:function(){var t,r,e,n=this.toRgb();return t=n.r/255,r=n.g/255,e=n.b/255,.2126*(t<=.03928?t/12.92:Math.pow((t+.055)/1.055,2.4))+.7152*(r<=.03928?r/12.92:Math.pow((r+.055)/1.055,2.4))+.0722*(e<=.03928?e/12.92:Math.pow((e+.055)/1.055,2.4))},setAlpha:function(t){return this._a=x(t),this._roundA=Math.round(100*this._a)/100,this},toHsv:function(){var t=i(this._r,this._g,this._b);return{h:360*t.h,s:t.s,v:t.v,a:this._a}},toHsvString:function(){var t=i(this._r,this._g,this._b),r=Math.round(360*t.h),e=Math.round(100*t.s),n=Math.round(100*t.v);return 1==this._a?"hsv("+r+", "+e+"%, "+n+"%)":"hsva("+r+", "+e+"%, "+n+"%, "+this._roundA+")"},toHsl:function(){var t=a(this._r,this._g,this._b);return{h:360*t.h,s:t.s,l:t.l,a:this._a}},toHslString:function(){var t=a(this._r,this._g,this._b),r=Math.round(360*t.h),e=Math.round(100*t.s),n=Math.round(100*t.l);return 1==this._a?"hsl("+r+", "+e+"%, "+n+"%)":"hsla("+r+", "+e+"%, "+n+"%, "+this._roundA+")"},toHex:function(t){return o(this._r,this._g,this._b,t)},toHexString:function(t){return"#"+this.toHex(t)},toHex8:function(t){return function(t,r,e,n,a){var i=[S(Math.round(t).toString(16)),S(Math.round(r).toString(16)),S(Math.round(e).toString(16)),S(R(n))];if(a&&i[0].charAt(0)==i[0].charAt(1)&&i[1].charAt(0)==i[1].charAt(1)&&i[2].charAt(0)==i[2].charAt(1)&&i[3].charAt(0)==i[3].charAt(1))return i[0].charAt(0)+i[1].charAt(0)+i[2].charAt(0)+i[3].charAt(0);return i.join("")}(this._r,this._g,this._b,this._a,t)},toHex8String:function(t){return"#"+this.toHex8(t)},toRgb:function(){return{r:Math.round(this._r),g:Math.round(this._g),b:Math.round(this._b),a:this._a}},toRgbString:function(){return 1==this._a?"rgb("+Math.round(this._r)+", "+Math.round(this._g)+", "+Math.round(this._b)+")":"rgba("+Math.round(this._r)+", "+Math.round(this._g)+", "+Math.round(this._b)+", "+this._roundA+")"},toPercentageRgb:function(){return{r:Math.round(100*A(this._r,255))+"%",g:Math.round(100*A(this._g,255))+"%",b:Math.round(100*A(this._b,255))+"%",a:this._a}},toPercentageRgbString:function(){return 1==this._a?"rgb("+Math.round(100*A(this._r,255))+"%, "+Math.round(100*A(this._g,255))+"%, "+Math.round(100*A(this._b,255))+"%)":"rgba("+Math.round(100*A(this._r,255))+"%, "+Math.round(100*A(this._g,255))+"%, "+Math.round(100*A(this._b,255))+"%, "+this._roundA+")"},toName:function(){return 0===this._a?"transparent":!(this._a<1)&&(M[o(this._r,this._g,this._b,!0)]||!1)},toFilter:function(t){var r="#"+h(this._r,this._g,this._b,this._a),e=r,a=this._gradientType?"GradientType = 1, ":"";if(t){var i=n(t);e="#"+h(i._r,i._g,i._b,i._a)}return"progid:DXImageTransform.Microsoft.gradient("+a+"startColorstr="+r+",endColorstr="+e+")"},toString:function(t){var r=!!t;t=t||this._format;var e=!1,n=this._a<1&&this._a>=0;return r||!n||"hex"!==t&&"hex6"!==t&&"hex3"!==t&&"hex4"!==t&&"hex8"!==t&&"name"!==t?("rgb"===t&&(e=this.toRgbString()),"prgb"===t&&(e=this.toPercentageRgbString()),"hex"!==t&&"hex6"!==t||(e=this.toHexString()),"hex3"===t&&(e=this.toHexString(!0)),"hex4"===t&&(e=this.toHex8String(!0)),"hex8"===t&&(e=this.toHex8String()),"name"===t&&(e=this.toName()),"hsl"===t&&(e=this.toHslString()),"hsv"===t&&(e=this.toHsvString()),e||this.toHexString()):"name"===t&&0===this._a?this.toName():this.toRgbString()},clone:function(){return n(this.toString())},_applyModification:function(t,r){var e=t.apply(null,[this].concat([].slice.call(r)));return this._r=e._r,this._g=e._g,this._b=e._b,this.setAlpha(e._a),this},lighten:function(){return this._applyModification(l,arguments)},brighten:function(){return this._applyModification(c,arguments)},darken:function(){return this._applyModification(d,arguments)},desaturate:function(){return this._applyModification(s,arguments)},saturate:function(){return this._applyModification(f,arguments)},greyscale:function(){return this._applyModification(u,arguments)},spin:function(){return this._applyModification(g,arguments)},_applyCombination:function(t,r){return t.apply(null,[this].concat([].slice.call(r)))},analogous:function(){return this._applyCombination(_,arguments)},complement:function(){return this._applyCombination(b,arguments)},monochromatic:function(){return this._applyCombination(v,arguments)},splitcomplement:function(){return this._applyCombination(p,arguments)},triad:function(){return this._applyCombination(m,[3])},tetrad:function(){return this._applyCombination(m,[4])}},n.fromRatio=function(r,e){if("object"==t(r)){var a={};for(var i in r)r.hasOwnProperty(i)&&(a[i]="a"===i?r[i]:H(r[i]));r=a}return n(r,e)},n.equals=function(t,r){return!(!t||!r)&&n(t).toRgbString()==n(r).toRgbString()},n.random=function(){return n.fromRatio({r:Math.random(),g:Math.random(),b:Math.random()})},n.mix=function(t,r,e){e=0===e?0:e||50;var a=n(t).toRgb(),i=n(r).toRgb(),o=e/100;return n({r:(i.r-a.r)*o+a.r,g:(i.g-a.g)*o+a.g,b:(i.b-a.b)*o+a.b,a:(i.a-a.a)*o+a.a})},n.readability=function(t,r){var e=n(t),a=n(r);return(Math.max(e.getLuminance(),a.getLuminance())+.05)/(Math.min(e.getLuminance(),a.getLuminance())+.05)},n.isReadable=function(t,r,e){var a,i,o=n.readability(t,r);switch(i=!1,(a=function(t){var r,e;r=((t=t||{level:"AA",size:"small"}).level||"AA").toUpperCase(),e=(t.size||"small").toLowerCase(),"AA"!==r&&"AAA"!==r&&(r="AA");"small"!==e&&"large"!==e&&(e="small");return{level:r,size:e}}(e)).level+a.size){case"AAsmall":case"AAAlarge":i=o>=4.5;break;case"AAlarge":i=o>=3;break;case"AAAsmall":i=o>=7}return i},n.mostReadable=function(t,r,e){var a,i,o,h,s=null,f=0;i=(e=e||{}).includeFallbackColors,o=e.level,h=e.size;for(var u=0;uf&&(f=a,s=n(r[u]));return n.isReadable(t,s,{level:o,size:h})||!i?s:(e.includeFallbackColors=!1,n.mostReadable(t,["#fff","#000"],e))};var y=n.names={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"0ff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000",blanchedalmond:"ffebcd",blue:"00f",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",burntsienna:"ea7e5d",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"0ff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkgrey:"a9a9a9",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkslategrey:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dimgrey:"696969",dodgerblue:"1e90ff",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"f0f",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",grey:"808080",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgray:"d3d3d3",lightgreen:"90ee90",lightgrey:"d3d3d3",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslategray:"789",lightslategrey:"789",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"0f0",limegreen:"32cd32",linen:"faf0e6",magenta:"f0f",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370db",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"db7093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",rebeccapurple:"663399",red:"f00",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",slategrey:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",wheat:"f5deb3",white:"fff",whitesmoke:"f5f5f5",yellow:"ff0",yellowgreen:"9acd32"},M=n.hexNames=function(t){var r={};for(var e in t)t.hasOwnProperty(e)&&(r[t[e]]=e);return r}(y);function x(t){return t=parseFloat(t),(isNaN(t)||t<0||t>1)&&(t=1),t}function A(t,r){(function(t){return"string"==typeof t&&-1!=t.indexOf(".")&&1===parseFloat(t)})(t)&&(t="100%");var e=function(t){return"string"==typeof t&&-1!=t.indexOf("%")}(t);return t=Math.min(r,Math.max(0,parseFloat(t))),e&&(t=parseInt(t*r,10)/100),Math.abs(t-r)<1e-6?1:t%r/parseFloat(r)}function k(t){return Math.min(1,Math.max(0,t))}function w(t){return parseInt(t,16)}function S(t){return 1==t.length?"0"+t:""+t}function H(t){return t<=1&&(t=100*t+"%"),t}function R(t){return Math.round(255*parseFloat(t)).toString(16)}function F(t){return w(t)/255}var C,q,N,T=(q="[\\s|\\(]+("+(C="(?:[-\\+]?\\d*\\.\\d+%?)|(?:[-\\+]?\\d+%?)")+")[,|\\s]+("+C+")[,|\\s]+("+C+")\\s*\\)?",N="[\\s|\\(]+("+C+")[,|\\s]+("+C+")[,|\\s]+("+C+")[,|\\s]+("+C+")\\s*\\)?",{CSS_UNIT:new RegExp(C),rgb:new RegExp("rgb"+q),rgba:new RegExp("rgba"+N),hsl:new RegExp("hsl"+q),hsla:new RegExp("hsla"+N),hsv:new RegExp("hsv"+q),hsva:new RegExp("hsva"+N),hex3:/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex6:/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,hex4:/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex8:/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/});function E(t){return!!T.CSS_UNIT.exec(t)}return n})); -------------------------------------------------------------------------------- /npm/dist/tinycolor-min.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated. 2 | // It's here at this path for backwards compatibility for links to it 3 | // but the npm package now exports both CJS and ESM. 4 | // See https://github.com/bgrins/TinyColor/ for instructions. 5 | !function(t,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):(t="undefined"!=typeof globalThis?globalThis:t||self).tinycolor=r()}(this,(function(){"use strict";function t(r){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},t(r)}var r=/^\s+/,e=/\s+$/;function n(a,i){if(i=i||{},(a=a||"")instanceof n)return a;if(!(this instanceof n))return new n(a,i);var o=function(n){var a={r:0,g:0,b:0},i=1,o=null,h=null,s=null,f=!1,u=!1;"string"==typeof n&&(n=function(t){t=t.replace(r,"").replace(e,"").toLowerCase();var n,a=!1;if(y[t])t=y[t],a=!0;else if("transparent"==t)return{r:0,g:0,b:0,a:0,format:"name"};if(n=T.rgb.exec(t))return{r:n[1],g:n[2],b:n[3]};if(n=T.rgba.exec(t))return{r:n[1],g:n[2],b:n[3],a:n[4]};if(n=T.hsl.exec(t))return{h:n[1],s:n[2],l:n[3]};if(n=T.hsla.exec(t))return{h:n[1],s:n[2],l:n[3],a:n[4]};if(n=T.hsv.exec(t))return{h:n[1],s:n[2],v:n[3]};if(n=T.hsva.exec(t))return{h:n[1],s:n[2],v:n[3],a:n[4]};if(n=T.hex8.exec(t))return{r:w(n[1]),g:w(n[2]),b:w(n[3]),a:F(n[4]),format:a?"name":"hex8"};if(n=T.hex6.exec(t))return{r:w(n[1]),g:w(n[2]),b:w(n[3]),format:a?"name":"hex"};if(n=T.hex4.exec(t))return{r:w(n[1]+""+n[1]),g:w(n[2]+""+n[2]),b:w(n[3]+""+n[3]),a:F(n[4]+""+n[4]),format:a?"name":"hex8"};if(n=T.hex3.exec(t))return{r:w(n[1]+""+n[1]),g:w(n[2]+""+n[2]),b:w(n[3]+""+n[3]),format:a?"name":"hex"};return!1}(n));"object"==t(n)&&(E(n.r)&&E(n.g)&&E(n.b)?(l=n.r,c=n.g,d=n.b,a={r:255*A(l,255),g:255*A(c,255),b:255*A(d,255)},f=!0,u="%"===String(n.r).substr(-1)?"prgb":"rgb"):E(n.h)&&E(n.s)&&E(n.v)?(o=H(n.s),h=H(n.v),a=function(t,r,e){t=6*A(t,360),r=A(r,100),e=A(e,100);var n=Math.floor(t),a=t-n,i=e*(1-r),o=e*(1-a*r),h=e*(1-(1-a)*r),s=n%6,f=[e,o,i,i,h,e][s],u=[h,e,e,o,i,i][s],l=[i,i,h,e,e,o][s];return{r:255*f,g:255*u,b:255*l}}(n.h,o,h),f=!0,u="hsv"):E(n.h)&&E(n.s)&&E(n.l)&&(o=H(n.s),s=H(n.l),a=function(t,r,e){var n,a,i;function o(t,r,e){return e<0&&(e+=1),e>1&&(e-=1),e<1/6?t+6*(r-t)*e:e<.5?r:e<2/3?t+(r-t)*(2/3-e)*6:t}if(t=A(t,360),r=A(r,100),e=A(e,100),0===r)n=a=i=e;else{var h=e<.5?e*(1+r):e+r-e*r,s=2*e-h;n=o(s,h,t+1/3),a=o(s,h,t),i=o(s,h,t-1/3)}return{r:255*n,g:255*a,b:255*i}}(n.h,o,s),f=!0,u="hsl"),n.hasOwnProperty("a")&&(i=n.a));var l,c,d;return i=x(i),{ok:f,format:n.format||u,r:Math.min(255,Math.max(a.r,0)),g:Math.min(255,Math.max(a.g,0)),b:Math.min(255,Math.max(a.b,0)),a:i}}(a);this._originalInput=a,this._r=o.r,this._g=o.g,this._b=o.b,this._a=o.a,this._roundA=Math.round(100*this._a)/100,this._format=i.format||o.format,this._gradientType=i.gradientType,this._r<1&&(this._r=Math.round(this._r)),this._g<1&&(this._g=Math.round(this._g)),this._b<1&&(this._b=Math.round(this._b)),this._ok=o.ok}function a(t,r,e){t=A(t,255),r=A(r,255),e=A(e,255);var n,a,i=Math.max(t,r,e),o=Math.min(t,r,e),h=(i+o)/2;if(i==o)n=a=0;else{var s=i-o;switch(a=h>.5?s/(2-i-o):s/(i+o),i){case t:n=(r-e)/s+(r>1)+720)%360;--r;)a.h=(a.h+i)%360,o.push(n(a));return o}function v(t,r){r=r||6;for(var e=n(t).toHsv(),a=e.h,i=e.s,o=e.v,h=[],s=1/r;r--;)h.push(n({h:a,s:i,v:o})),o=(o+s)%1;return h}n.prototype={isDark:function(){return this.getBrightness()<128},isLight:function(){return!this.isDark()},isValid:function(){return this._ok},getOriginalInput:function(){return this._originalInput},getFormat:function(){return this._format},getAlpha:function(){return this._a},getBrightness:function(){var t=this.toRgb();return(299*t.r+587*t.g+114*t.b)/1e3},getLuminance:function(){var t,r,e,n=this.toRgb();return t=n.r/255,r=n.g/255,e=n.b/255,.2126*(t<=.03928?t/12.92:Math.pow((t+.055)/1.055,2.4))+.7152*(r<=.03928?r/12.92:Math.pow((r+.055)/1.055,2.4))+.0722*(e<=.03928?e/12.92:Math.pow((e+.055)/1.055,2.4))},setAlpha:function(t){return this._a=x(t),this._roundA=Math.round(100*this._a)/100,this},toHsv:function(){var t=i(this._r,this._g,this._b);return{h:360*t.h,s:t.s,v:t.v,a:this._a}},toHsvString:function(){var t=i(this._r,this._g,this._b),r=Math.round(360*t.h),e=Math.round(100*t.s),n=Math.round(100*t.v);return 1==this._a?"hsv("+r+", "+e+"%, "+n+"%)":"hsva("+r+", "+e+"%, "+n+"%, "+this._roundA+")"},toHsl:function(){var t=a(this._r,this._g,this._b);return{h:360*t.h,s:t.s,l:t.l,a:this._a}},toHslString:function(){var t=a(this._r,this._g,this._b),r=Math.round(360*t.h),e=Math.round(100*t.s),n=Math.round(100*t.l);return 1==this._a?"hsl("+r+", "+e+"%, "+n+"%)":"hsla("+r+", "+e+"%, "+n+"%, "+this._roundA+")"},toHex:function(t){return o(this._r,this._g,this._b,t)},toHexString:function(t){return"#"+this.toHex(t)},toHex8:function(t){return function(t,r,e,n,a){var i=[S(Math.round(t).toString(16)),S(Math.round(r).toString(16)),S(Math.round(e).toString(16)),S(R(n))];if(a&&i[0].charAt(0)==i[0].charAt(1)&&i[1].charAt(0)==i[1].charAt(1)&&i[2].charAt(0)==i[2].charAt(1)&&i[3].charAt(0)==i[3].charAt(1))return i[0].charAt(0)+i[1].charAt(0)+i[2].charAt(0)+i[3].charAt(0);return i.join("")}(this._r,this._g,this._b,this._a,t)},toHex8String:function(t){return"#"+this.toHex8(t)},toRgb:function(){return{r:Math.round(this._r),g:Math.round(this._g),b:Math.round(this._b),a:this._a}},toRgbString:function(){return 1==this._a?"rgb("+Math.round(this._r)+", "+Math.round(this._g)+", "+Math.round(this._b)+")":"rgba("+Math.round(this._r)+", "+Math.round(this._g)+", "+Math.round(this._b)+", "+this._roundA+")"},toPercentageRgb:function(){return{r:Math.round(100*A(this._r,255))+"%",g:Math.round(100*A(this._g,255))+"%",b:Math.round(100*A(this._b,255))+"%",a:this._a}},toPercentageRgbString:function(){return 1==this._a?"rgb("+Math.round(100*A(this._r,255))+"%, "+Math.round(100*A(this._g,255))+"%, "+Math.round(100*A(this._b,255))+"%)":"rgba("+Math.round(100*A(this._r,255))+"%, "+Math.round(100*A(this._g,255))+"%, "+Math.round(100*A(this._b,255))+"%, "+this._roundA+")"},toName:function(){return 0===this._a?"transparent":!(this._a<1)&&(M[o(this._r,this._g,this._b,!0)]||!1)},toFilter:function(t){var r="#"+h(this._r,this._g,this._b,this._a),e=r,a=this._gradientType?"GradientType = 1, ":"";if(t){var i=n(t);e="#"+h(i._r,i._g,i._b,i._a)}return"progid:DXImageTransform.Microsoft.gradient("+a+"startColorstr="+r+",endColorstr="+e+")"},toString:function(t){var r=!!t;t=t||this._format;var e=!1,n=this._a<1&&this._a>=0;return r||!n||"hex"!==t&&"hex6"!==t&&"hex3"!==t&&"hex4"!==t&&"hex8"!==t&&"name"!==t?("rgb"===t&&(e=this.toRgbString()),"prgb"===t&&(e=this.toPercentageRgbString()),"hex"!==t&&"hex6"!==t||(e=this.toHexString()),"hex3"===t&&(e=this.toHexString(!0)),"hex4"===t&&(e=this.toHex8String(!0)),"hex8"===t&&(e=this.toHex8String()),"name"===t&&(e=this.toName()),"hsl"===t&&(e=this.toHslString()),"hsv"===t&&(e=this.toHsvString()),e||this.toHexString()):"name"===t&&0===this._a?this.toName():this.toRgbString()},clone:function(){return n(this.toString())},_applyModification:function(t,r){var e=t.apply(null,[this].concat([].slice.call(r)));return this._r=e._r,this._g=e._g,this._b=e._b,this.setAlpha(e._a),this},lighten:function(){return this._applyModification(l,arguments)},brighten:function(){return this._applyModification(c,arguments)},darken:function(){return this._applyModification(d,arguments)},desaturate:function(){return this._applyModification(s,arguments)},saturate:function(){return this._applyModification(f,arguments)},greyscale:function(){return this._applyModification(u,arguments)},spin:function(){return this._applyModification(g,arguments)},_applyCombination:function(t,r){return t.apply(null,[this].concat([].slice.call(r)))},analogous:function(){return this._applyCombination(_,arguments)},complement:function(){return this._applyCombination(b,arguments)},monochromatic:function(){return this._applyCombination(v,arguments)},splitcomplement:function(){return this._applyCombination(p,arguments)},triad:function(){return this._applyCombination(m,[3])},tetrad:function(){return this._applyCombination(m,[4])}},n.fromRatio=function(r,e){if("object"==t(r)){var a={};for(var i in r)r.hasOwnProperty(i)&&(a[i]="a"===i?r[i]:H(r[i]));r=a}return n(r,e)},n.equals=function(t,r){return!(!t||!r)&&n(t).toRgbString()==n(r).toRgbString()},n.random=function(){return n.fromRatio({r:Math.random(),g:Math.random(),b:Math.random()})},n.mix=function(t,r,e){e=0===e?0:e||50;var a=n(t).toRgb(),i=n(r).toRgb(),o=e/100;return n({r:(i.r-a.r)*o+a.r,g:(i.g-a.g)*o+a.g,b:(i.b-a.b)*o+a.b,a:(i.a-a.a)*o+a.a})},n.readability=function(t,r){var e=n(t),a=n(r);return(Math.max(e.getLuminance(),a.getLuminance())+.05)/(Math.min(e.getLuminance(),a.getLuminance())+.05)},n.isReadable=function(t,r,e){var a,i,o=n.readability(t,r);switch(i=!1,(a=function(t){var r,e;r=((t=t||{level:"AA",size:"small"}).level||"AA").toUpperCase(),e=(t.size||"small").toLowerCase(),"AA"!==r&&"AAA"!==r&&(r="AA");"small"!==e&&"large"!==e&&(e="small");return{level:r,size:e}}(e)).level+a.size){case"AAsmall":case"AAAlarge":i=o>=4.5;break;case"AAlarge":i=o>=3;break;case"AAAsmall":i=o>=7}return i},n.mostReadable=function(t,r,e){var a,i,o,h,s=null,f=0;i=(e=e||{}).includeFallbackColors,o=e.level,h=e.size;for(var u=0;uf&&(f=a,s=n(r[u]));return n.isReadable(t,s,{level:o,size:h})||!i?s:(e.includeFallbackColors=!1,n.mostReadable(t,["#fff","#000"],e))};var y=n.names={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"0ff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000",blanchedalmond:"ffebcd",blue:"00f",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",burntsienna:"ea7e5d",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"0ff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkgrey:"a9a9a9",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkslategrey:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dimgrey:"696969",dodgerblue:"1e90ff",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"f0f",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",grey:"808080",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgray:"d3d3d3",lightgreen:"90ee90",lightgrey:"d3d3d3",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslategray:"789",lightslategrey:"789",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"0f0",limegreen:"32cd32",linen:"faf0e6",magenta:"f0f",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370db",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"db7093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",rebeccapurple:"663399",red:"f00",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",slategrey:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",wheat:"f5deb3",white:"fff",whitesmoke:"f5f5f5",yellow:"ff0",yellowgreen:"9acd32"},M=n.hexNames=function(t){var r={};for(var e in t)t.hasOwnProperty(e)&&(r[t[e]]=e);return r}(y);function x(t){return t=parseFloat(t),(isNaN(t)||t<0||t>1)&&(t=1),t}function A(t,r){(function(t){return"string"==typeof t&&-1!=t.indexOf(".")&&1===parseFloat(t)})(t)&&(t="100%");var e=function(t){return"string"==typeof t&&-1!=t.indexOf("%")}(t);return t=Math.min(r,Math.max(0,parseFloat(t))),e&&(t=parseInt(t*r,10)/100),Math.abs(t-r)<1e-6?1:t%r/parseFloat(r)}function k(t){return Math.min(1,Math.max(0,t))}function w(t){return parseInt(t,16)}function S(t){return 1==t.length?"0"+t:""+t}function H(t){return t<=1&&(t=100*t+"%"),t}function R(t){return Math.round(255*parseFloat(t)).toString(16)}function F(t){return w(t)/255}var C,q,N,T=(q="[\\s|\\(]+("+(C="(?:[-\\+]?\\d*\\.\\d+%?)|(?:[-\\+]?\\d+%?)")+")[,|\\s]+("+C+")[,|\\s]+("+C+")\\s*\\)?",N="[\\s|\\(]+("+C+")[,|\\s]+("+C+")[,|\\s]+("+C+")[,|\\s]+("+C+")\\s*\\)?",{CSS_UNIT:new RegExp(C),rgb:new RegExp("rgb"+q),rgba:new RegExp("rgba"+N),hsl:new RegExp("hsl"+q),hsla:new RegExp("hsla"+N),hsv:new RegExp("hsv"+q),hsva:new RegExp("hsva"+N),hex3:/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex6:/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,hex4:/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex8:/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/});function E(t){return!!T.CSS_UNIT.exec(t)}return n})); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TinyColor 2 | 3 | ## JavaScript color tooling 4 | 5 | TinyColor is a small, fast library for color manipulation and conversion in JavaScript. It allows many forms of input, while providing color conversions and other color utility functions. It has no dependencies. 6 | 7 | ## Including in node 8 | 9 | `tinycolor` can be installed from npm: 10 | 11 | npm install tinycolor2 12 | 13 | Then it can be used in your script like so: 14 | 15 | ```js 16 | var tinycolor = require("tinycolor2"); 17 | var color = tinycolor("red"); 18 | ``` 19 | 20 | Or in a module like so: 21 | 22 | ```js 23 | import tinycolor from "tinycolor2"; 24 | var color = tinycolor("red"); 25 | ``` 26 | 27 | ## Including in a browser 28 | 29 | The package can be bundled from npm, but if you prefer to download it locally you have two choices: 30 | 31 | ### ESM 32 | 33 | It can be used as a module by downloading [npm/esm/tinycolor.js](https://github.com/bgrins/TinyColor/blob/master/npm/esm/tinycolor.js) or using https://esm.sh/tinycolor2. 34 | 35 | ```html 36 | 40 | ``` 41 | 42 | ### UMD 43 | 44 | You can use it directly in a script tag by downloading the UMD file from [npm/cjs/tinycolor.js](https://github.com/bgrins/TinyColor/blob/master/npm/cjs/tinycolor.js): 45 | 46 | ```html 47 | 48 | 51 | ``` 52 | 53 | ## Usage 54 | 55 | Call `tinycolor(input)` or `new tinycolor(input)`, and you will have an object with the following properties. See Accepted String Input and Accepted Object Input below for more information about what is accepted. 56 | 57 | ## Accepted String Input 58 | 59 | The string parsing is very permissive. It is meant to make typing a color as input as easy as possible. All commas, percentages, parenthesis are optional, and most input allow either 0-1, 0%-100%, or 0-n (where n is either 100, 255, or 360 depending on the value). 60 | 61 | HSL and HSV both require either 0%-100% or 0-1 for the `S`/`L`/`V` properties. The `H` (hue) can have values between 0%-100% or 0-360. 62 | 63 | RGB input requires either 0-255 or 0%-100%. 64 | 65 | If you call `tinycolor.fromRatio`, RGB and Hue input can also accept 0-1. 66 | 67 | Here are some examples of string input: 68 | 69 | ### Hex, 8-digit (RGBA) Hex 70 | ```js 71 | tinycolor("#000"); 72 | tinycolor("000"); 73 | tinycolor("#369C"); 74 | tinycolor("369C"); 75 | tinycolor("#f0f0f6"); 76 | tinycolor("f0f0f6"); 77 | tinycolor("#f0f0f688"); 78 | tinycolor("f0f0f688"); 79 | ``` 80 | ### RGB, RGBA 81 | ```js 82 | tinycolor("rgb (255, 0, 0)"); 83 | tinycolor("rgb 255 0 0"); 84 | tinycolor("rgba (255, 0, 0, .5)"); 85 | tinycolor({ r: 255, g: 0, b: 0 }); 86 | tinycolor.fromRatio({ r: 1, g: 0, b: 0 }); 87 | tinycolor.fromRatio({ r: .5, g: .5, b: .5 }); 88 | ``` 89 | ### HSL, HSLA 90 | ```js 91 | tinycolor("hsl(0, 100%, 50%)"); 92 | tinycolor("hsla(0, 100%, 50%, .5)"); 93 | tinycolor("hsl(0, 100%, 50%)"); 94 | tinycolor("hsl 0 1.0 0.5"); 95 | tinycolor({ h: 0, s: 1, l: .5 }); 96 | tinycolor.fromRatio({ h: 1, s: 0, l: 0 }); 97 | tinycolor.fromRatio({ h: .5, s: .5, l: .5 }); 98 | ``` 99 | ### HSV, HSVA 100 | ```js 101 | tinycolor("hsv(0, 100%, 100%)"); 102 | tinycolor("hsva(0, 100%, 100%, .5)"); 103 | tinycolor("hsv (0 100% 100%)"); 104 | tinycolor("hsv 0 1 1"); 105 | tinycolor({ h: 0, s: 100, v: 100 }); 106 | tinycolor.fromRatio({ h: 1, s: 0, v: 0 }); 107 | tinycolor.fromRatio({ h: .5, s: .5, v: .5 }); 108 | ``` 109 | ### Named 110 | 111 | Case insenstive names are accepted, using the [list of colors in the CSS spec](https://www.w3.org/TR/css-color-4/#named-colors). 112 | 113 | ```js 114 | tinycolor("RED"); 115 | tinycolor("blanchedalmond"); 116 | tinycolor("darkblue"); 117 | ``` 118 | ### Accepted Object Input 119 | 120 | If you are calling this from code, you may want to use object input. Here are some examples of the different types of accepted object inputs: 121 | 122 | { r: 255, g: 0, b: 0 } 123 | { r: 255, g: 0, b: 0, a: .5 } 124 | { h: 0, s: 100, l: 50 } 125 | { h: 0, s: 100, v: 100 } 126 | 127 | ## Methods 128 | 129 | ### getFormat 130 | 131 | Returns the format used to create the tinycolor instance 132 | ```js 133 | var color = tinycolor("red"); 134 | color.getFormat(); // "name" 135 | color = tinycolor({r:255, g:255, b:255}); 136 | color.getFormat(); // "rgb" 137 | ``` 138 | 139 | ### getOriginalInput 140 | 141 | Returns the input passed into the constructor used to create the tinycolor instance 142 | ```js 143 | var color = tinycolor("red"); 144 | color.getOriginalInput(); // "red" 145 | color = tinycolor({r:255, g:255, b:255}); 146 | color.getOriginalInput(); // "{r: 255, g: 255, b: 255}" 147 | ``` 148 | 149 | ### isValid 150 | 151 | Return a boolean indicating whether the color was successfully parsed. Note: if the color is not valid then it will act like `black` when being used with other methods. 152 | ```js 153 | var color1 = tinycolor("red"); 154 | color1.isValid(); // true 155 | color1.toHexString(); // "#ff0000" 156 | 157 | var color2 = tinycolor("not a color"); 158 | color2.isValid(); // false 159 | color2.toString(); // "#000000" 160 | ``` 161 | ### getBrightness 162 | 163 | Returns the perceived brightness of a color, from `0-255`, as defined by [Web Content Accessibility Guidelines (Version 1.0)](http://www.w3.org/TR/AERT#color-contrast). 164 | ```js 165 | var color1 = tinycolor("#fff"); 166 | color1.getBrightness(); // 255 167 | 168 | var color2 = tinycolor("#000"); 169 | color2.getBrightness(); // 0 170 | ``` 171 | ### isLight 172 | 173 | Return a boolean indicating whether the color's perceived brightness is light. 174 | ```js 175 | var color1 = tinycolor("#fff"); 176 | color1.isLight(); // true 177 | 178 | var color2 = tinycolor("#000"); 179 | color2.isLight(); // false 180 | ``` 181 | ### isDark 182 | 183 | Return a boolean indicating whether the color's perceived brightness is dark. 184 | ```js 185 | var color1 = tinycolor("#fff"); 186 | color1.isDark(); // false 187 | 188 | var color2 = tinycolor("#000"); 189 | color2.isDark(); // true 190 | ``` 191 | ### getLuminance 192 | 193 | Returns the perceived luminance of a color, from `0-1` as defined by [Web Content Accessibility Guidelines (Version 2.0).](http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef) 194 | ```js 195 | var color1 = tinycolor("#fff"); 196 | color1.getLuminance(); // 1 197 | 198 | var color2 = tinycolor("#000"); 199 | color2.getLuminance(); // 0 200 | ``` 201 | ### getAlpha 202 | 203 | Returns the alpha value of a color, from `0-1`. 204 | ```js 205 | var color1 = tinycolor("rgba(255, 0, 0, .5)"); 206 | color1.getAlpha(); // 0.5 207 | 208 | var color2 = tinycolor("rgb(255, 0, 0)"); 209 | color2.getAlpha(); // 1 210 | 211 | var color3 = tinycolor("transparent"); 212 | color3.getAlpha(); // 0 213 | ``` 214 | ### setAlpha 215 | 216 | Sets the alpha value on a current color. Accepted range is in between `0-1`. 217 | ```js 218 | var color = tinycolor("red"); 219 | color.getAlpha(); // 1 220 | color.setAlpha(.5); 221 | color.getAlpha(); // .5 222 | color.toRgbString(); // "rgba(255, 0, 0, .5)" 223 | ``` 224 | ### String Representations 225 | 226 | The following methods will return a property for the `alpha` value, which can be ignored: `toHsv`, `toHsl`, `toRgb` 227 | 228 | ### toHsv 229 | ```js 230 | var color = tinycolor("red"); 231 | color.toHsv(); // { h: 0, s: 1, v: 1, a: 1 } 232 | ``` 233 | ### toHsvString 234 | ```js 235 | var color = tinycolor("red"); 236 | color.toHsvString(); // "hsv(0, 100%, 100%)" 237 | color.setAlpha(0.5); 238 | color.toHsvString(); // "hsva(0, 100%, 100%, 0.5)" 239 | ``` 240 | ### toHsl 241 | ```js 242 | var color = tinycolor("red"); 243 | color.toHsl(); // { h: 0, s: 1, l: 0.5, a: 1 } 244 | ``` 245 | ### toHslString 246 | ```js 247 | var color = tinycolor("red"); 248 | color.toHslString(); // "hsl(0, 100%, 50%)" 249 | color.setAlpha(0.5); 250 | color.toHslString(); // "hsla(0, 100%, 50%, 0.5)" 251 | ``` 252 | ### toHex 253 | ```js 254 | var color = tinycolor("red"); 255 | color.toHex(); // "ff0000" 256 | ``` 257 | ### toHexString 258 | ```js 259 | var color = tinycolor("red"); 260 | color.toHexString(); // "#ff0000" 261 | ``` 262 | ### toHex8 263 | ```js 264 | var color = tinycolor("red"); 265 | color.toHex8(); // "ff0000ff" 266 | ``` 267 | ### toHex8String 268 | ```js 269 | var color = tinycolor("red"); 270 | color.toHex8String(); // "#ff0000ff" 271 | ``` 272 | ### toRgb 273 | ```js 274 | var color = tinycolor("red"); 275 | color.toRgb(); // { r: 255, g: 0, b: 0, a: 1 } 276 | ``` 277 | ### toRgbString 278 | ```js 279 | var color = tinycolor("red"); 280 | color.toRgbString(); // "rgb(255, 0, 0)" 281 | color.setAlpha(0.5); 282 | color.toRgbString(); // "rgba(255, 0, 0, 0.5)" 283 | ``` 284 | ### toPercentageRgb 285 | ```js 286 | var color = tinycolor("red"); 287 | color.toPercentageRgb() // { r: "100%", g: "0%", b: "0%", a: 1 } 288 | ``` 289 | ### toPercentageRgbString 290 | ```js 291 | var color = tinycolor("red"); 292 | color.toPercentageRgbString(); // "rgb(100%, 0%, 0%)" 293 | color.setAlpha(0.5); 294 | color.toPercentageRgbString(); // "rgba(100%, 0%, 0%, 0.5)" 295 | ``` 296 | ### toName 297 | ```js 298 | var color = tinycolor("red"); 299 | color.toName(); // "red" 300 | ``` 301 | ### toFilter 302 | ``` 303 | var color = tinycolor("red"); 304 | color.toFilter(); // "progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffff0000,endColorstr=#ffff0000)" 305 | ``` 306 | ### toString 307 | 308 | Print to a string, depending on the input format. You can also override this by passing one of `"rgb", "prgb", "hex6", "hex3", "hex8", "name", "hsl", "hsv"` into the function. 309 | ```js 310 | var color1 = tinycolor("red"); 311 | color1.toString(); // "red" 312 | color1.toString("hsv"); // "hsv(0, 100%, 100%)" 313 | 314 | var color2 = tinycolor("rgb(255, 0, 0)"); 315 | color2.toString(); // "rgb(255, 0, 0)" 316 | color2.setAlpha(.5); 317 | color2.toString(); // "rgba(255, 0, 0, 0.5)" 318 | ``` 319 | ### Color Modification 320 | 321 | These methods manipulate the current color, and return it for chaining. For instance: 322 | ```js 323 | tinycolor("red").lighten().desaturate().toHexString() // "#f53d3d" 324 | ``` 325 | ### lighten 326 | 327 | `lighten: function(amount = 10) -> TinyColor`. Lighten the color a given amount, from 0 to 100. Providing 100 will always return white. 328 | ```js 329 | tinycolor("#f00").lighten().toString(); // "#ff3333" 330 | tinycolor("#f00").lighten(100).toString(); // "#ffffff" 331 | ``` 332 | ### brighten 333 | 334 | `brighten: function(amount = 10) -> TinyColor`. Brighten the color a given amount, from 0 to 100. 335 | ```js 336 | tinycolor("#f00").brighten().toString(); // "#ff1919" 337 | ``` 338 | ### darken 339 | 340 | `darken: function(amount = 10) -> TinyColor`. Darken the color a given amount, from 0 to 100. Providing 100 will always return black. 341 | ```js 342 | tinycolor("#f00").darken().toString(); // "#cc0000" 343 | tinycolor("#f00").darken(100).toString(); // "#000000" 344 | ``` 345 | ### desaturate 346 | 347 | `desaturate: function(amount = 10) -> TinyColor`. Desaturate the color a given amount, from 0 to 100. Providing 100 will is the same as calling `greyscale`. 348 | ```js 349 | tinycolor("#f00").desaturate().toString(); // "#f20d0d" 350 | tinycolor("#f00").desaturate(100).toString(); // "#808080" 351 | ``` 352 | ### saturate 353 | 354 | `saturate: function(amount = 10) -> TinyColor`. Saturate the color a given amount, from 0 to 100. 355 | ```js 356 | tinycolor("hsl(0, 10%, 50%)").saturate().toString(); // "hsl(0, 20%, 50%)" 357 | ``` 358 | ### greyscale 359 | 360 | `greyscale: function() -> TinyColor`. Completely desaturates a color into greyscale. Same as calling `desaturate(100)`. 361 | ```js 362 | tinycolor("#f00").greyscale().toString(); // "#808080" 363 | ``` 364 | ### spin 365 | 366 | `spin: function(amount = 0) -> TinyColor`. Spin the hue a given amount, from -360 to 360. Calling with 0, 360, or -360 will do nothing (since it sets the hue back to what it was before). 367 | ```js 368 | tinycolor("#f00").spin(180).toString(); // "#00ffff" 369 | tinycolor("#f00").spin(-90).toString(); // "#7f00ff" 370 | tinycolor("#f00").spin(90).toString(); // "#80ff00" 371 | 372 | // spin(0) and spin(360) do nothing 373 | tinycolor("#f00").spin(0).toString(); // "#ff0000" 374 | tinycolor("#f00").spin(360).toString(); // "#ff0000" 375 | ``` 376 | ### Color Combinations 377 | 378 | Combination functions return an array of TinyColor objects unless otherwise noted. 379 | 380 | ### analogous 381 | 382 | `analogous: function(, results = 6, slices = 30) -> array`. 383 | ```js 384 | var colors = tinycolor("#f00").analogous(); 385 | 386 | colors.map(function(t) { return t.toHexString(); }); // [ "#ff0000", "#ff0066", "#ff0033", "#ff0000", "#ff3300", "#ff6600" ] 387 | ``` 388 | ### monochromatic 389 | 390 | `monochromatic: function(, results = 6) -> array`. 391 | ```js 392 | var colors = tinycolor("#f00").monochromatic(); 393 | 394 | colors.map(function(t) { return t.toHexString(); }); // [ "#ff0000", "#2a0000", "#550000", "#800000", "#aa0000", "#d40000" ] 395 | ``` 396 | ### splitcomplement 397 | 398 | `splitcomplement: function() -> array`. 399 | ```js 400 | var colors = tinycolor("#f00").splitcomplement(); 401 | 402 | colors.map(function(t) { return t.toHexString(); }); // [ "#ff0000", "#ccff00", "#0066ff" ] 403 | ``` 404 | ### triad 405 | 406 | `triad: function() -> array`. 407 | ```js 408 | var colors = tinycolor("#f00").triad(); 409 | 410 | colors.map(function(t) { return t.toHexString(); }); // [ "#ff0000", "#00ff00", "#0000ff" ] 411 | ``` 412 | ### tetrad 413 | 414 | `tetrad: function() -> array`. 415 | ```js 416 | var colors = tinycolor("#f00").tetrad(); 417 | 418 | colors.map(function(t) { return t.toHexString(); }); // [ "#ff0000", "#80ff00", "#00ffff", "#7f00ff" ] 419 | 420 | ``` 421 | ### complement 422 | 423 | `complement: function() -> TinyColor`. 424 | ```js 425 | tinycolor("#f00").complement().toHexString(); // "#00ffff" 426 | ``` 427 | ## Color Utilities 428 | ```js 429 | tinycolor.equals(color1, color2) 430 | tinycolor.mix(color1, color2, amount = 50) 431 | ``` 432 | ### random 433 | 434 | Returns a random color. 435 | ```js 436 | var color = tinycolor.random(); 437 | color.toRgb(); // "{r: 145, g: 40, b: 198, a: 1}" 438 | ``` 439 | 440 | ### Readability 441 | 442 | TinyColor assesses readability based on the [Web Content Accessibility Guidelines (Version 2.0)](http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef). 443 | 444 | #### readability 445 | 446 | `readability: function(TinyColor, TinyColor) -> Object`. 447 | Returns the contrast ratio between two colors. 448 | ```js 449 | tinycolor.readability("#000", "#000"); // 1 450 | tinycolor.readability("#000", "#111"); // 1.1121078324840545 451 | tinycolor.readability("#000", "#fff"); // 21 452 | ``` 453 | Use the values in your own calculations, or use one of the convenience functions below. 454 | 455 | #### isReadable 456 | 457 | `isReadable: function(TinyColor, TinyColor, Object) -> Boolean`. Ensure that foreground and background color combinations meet WCAG guidelines. `Object` is optional, defaulting to `{level: "AA",size: "small"}`. `level` can be `"AA"` or "AAA" and `size` can be `"small"` or `"large"`. 458 | 459 | Here are links to read more about the [AA](http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html) and [AAA](http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast7.html) requirements. 460 | ```js 461 | tinycolor.isReadable("#000", "#111", {}); // false 462 | tinycolor.isReadable("#ff0088", "#5c1a72",{level:"AA",size:"small"}); //false 463 | tinycolor.isReadable("#ff0088", "#5c1a72",{level:"AA",size:"large"}), //true 464 | ``` 465 | #### mostReadable 466 | 467 | `mostReadable: function(TinyColor, [TinyColor, Tinycolor ...], Object) -> Boolean`. 468 | Given a base color and a list of possible foreground or background colors for that base, returns the most readable color. 469 | If none of the colors in the list is readable, `mostReadable` will return the better of black or white if `includeFallbackColors:true`. 470 | ```js 471 | tinycolor.mostReadable("#000", ["#f00", "#0f0", "#00f"]).toHexString(); // "#00ff00" 472 | tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:false}).toHexString(); // "#112255" 473 | tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:true}).toHexString(); // "#ffffff" 474 | tinycolor.mostReadable("#ff0088", ["#2e0c3a"],{includeFallbackColors:true,level:"AAA",size:"large"}).toHexString() // "#2e0c3a", 475 | tinycolor.mostReadable("#ff0088", ["#2e0c3a"],{includeFallbackColors:true,level:"AAA",size:"small"}).toHexString() // "#000000", 476 | ``` 477 | See [index.html](https://github.com/bgrins/TinyColor/blob/master/index.html) in the project for a demo. 478 | 479 | ## Common operations 480 | 481 | ### clone 482 | 483 | `clone: function() -> TinyColor`. 484 | Instantiate a new TinyColor object with the same color. Any changes to the new one won't affect the old one. 485 | ```js 486 | var color1 = tinycolor("#F00"); 487 | var color2 = color1.clone(); 488 | color2.setAlpha(.5); 489 | 490 | color1.toString(); // "#ff0000" 491 | color2.toString(); // "rgba(255, 0, 0, 0.5)" 492 | ``` 493 | -------------------------------------------------------------------------------- /npm/README.md: -------------------------------------------------------------------------------- 1 | # TinyColor 2 | 3 | ## JavaScript color tooling 4 | 5 | TinyColor is a small, fast library for color manipulation and conversion in JavaScript. It allows many forms of input, while providing color conversions and other color utility functions. It has no dependencies. 6 | 7 | ## Including in node 8 | 9 | `tinycolor` can be installed from npm: 10 | 11 | npm install tinycolor2 12 | 13 | Then it can be used in your script like so: 14 | 15 | ```js 16 | var tinycolor = require("tinycolor2"); 17 | var color = tinycolor("red"); 18 | ``` 19 | 20 | Or in a module like so: 21 | 22 | ```js 23 | import tinycolor from "tinycolor2"; 24 | var color = tinycolor("red"); 25 | ``` 26 | 27 | ## Including in a browser 28 | 29 | The package can be bundled from npm, but if you prefer to download it locally you have two choices: 30 | 31 | ### ESM 32 | 33 | It can be used as a module by downloading [npm/esm/tinycolor.js](https://github.com/bgrins/TinyColor/blob/master/npm/esm/tinycolor.js) or using https://esm.sh/tinycolor2. 34 | 35 | ```html 36 | 40 | ``` 41 | 42 | ### UMD 43 | 44 | You can use it directly in a script tag by downloading the UMD file from [npm/cjs/tinycolor.js](https://github.com/bgrins/TinyColor/blob/master/npm/cjs/tinycolor.js): 45 | 46 | ```html 47 | 48 | 51 | ``` 52 | 53 | ## Usage 54 | 55 | Call `tinycolor(input)` or `new tinycolor(input)`, and you will have an object with the following properties. See Accepted String Input and Accepted Object Input below for more information about what is accepted. 56 | 57 | ## Accepted String Input 58 | 59 | The string parsing is very permissive. It is meant to make typing a color as input as easy as possible. All commas, percentages, parenthesis are optional, and most input allow either 0-1, 0%-100%, or 0-n (where n is either 100, 255, or 360 depending on the value). 60 | 61 | HSL and HSV both require either 0%-100% or 0-1 for the `S`/`L`/`V` properties. The `H` (hue) can have values between 0%-100% or 0-360. 62 | 63 | RGB input requires either 0-255 or 0%-100%. 64 | 65 | If you call `tinycolor.fromRatio`, RGB and Hue input can also accept 0-1. 66 | 67 | Here are some examples of string input: 68 | 69 | ### Hex, 8-digit (RGBA) Hex 70 | ```js 71 | tinycolor("#000"); 72 | tinycolor("000"); 73 | tinycolor("#369C"); 74 | tinycolor("369C"); 75 | tinycolor("#f0f0f6"); 76 | tinycolor("f0f0f6"); 77 | tinycolor("#f0f0f688"); 78 | tinycolor("f0f0f688"); 79 | ``` 80 | ### RGB, RGBA 81 | ```js 82 | tinycolor("rgb (255, 0, 0)"); 83 | tinycolor("rgb 255 0 0"); 84 | tinycolor("rgba (255, 0, 0, .5)"); 85 | tinycolor({ r: 255, g: 0, b: 0 }); 86 | tinycolor.fromRatio({ r: 1, g: 0, b: 0 }); 87 | tinycolor.fromRatio({ r: .5, g: .5, b: .5 }); 88 | ``` 89 | ### HSL, HSLA 90 | ```js 91 | tinycolor("hsl(0, 100%, 50%)"); 92 | tinycolor("hsla(0, 100%, 50%, .5)"); 93 | tinycolor("hsl(0, 100%, 50%)"); 94 | tinycolor("hsl 0 1.0 0.5"); 95 | tinycolor({ h: 0, s: 1, l: .5 }); 96 | tinycolor.fromRatio({ h: 1, s: 0, l: 0 }); 97 | tinycolor.fromRatio({ h: .5, s: .5, l: .5 }); 98 | ``` 99 | ### HSV, HSVA 100 | ```js 101 | tinycolor("hsv(0, 100%, 100%)"); 102 | tinycolor("hsva(0, 100%, 100%, .5)"); 103 | tinycolor("hsv (0 100% 100%)"); 104 | tinycolor("hsv 0 1 1"); 105 | tinycolor({ h: 0, s: 100, v: 100 }); 106 | tinycolor.fromRatio({ h: 1, s: 0, v: 0 }); 107 | tinycolor.fromRatio({ h: .5, s: .5, v: .5 }); 108 | ``` 109 | ### Named 110 | 111 | Case insenstive names are accepted, using the [list of colors in the CSS spec](https://www.w3.org/TR/css-color-4/#named-colors). 112 | 113 | ```js 114 | tinycolor("RED"); 115 | tinycolor("blanchedalmond"); 116 | tinycolor("darkblue"); 117 | ``` 118 | ### Accepted Object Input 119 | 120 | If you are calling this from code, you may want to use object input. Here are some examples of the different types of accepted object inputs: 121 | 122 | { r: 255, g: 0, b: 0 } 123 | { r: 255, g: 0, b: 0, a: .5 } 124 | { h: 0, s: 100, l: 50 } 125 | { h: 0, s: 100, v: 100 } 126 | 127 | ## Methods 128 | 129 | ### getFormat 130 | 131 | Returns the format used to create the tinycolor instance 132 | ```js 133 | var color = tinycolor("red"); 134 | color.getFormat(); // "name" 135 | color = tinycolor({r:255, g:255, b:255}); 136 | color.getFormat(); // "rgb" 137 | ``` 138 | 139 | ### getOriginalInput 140 | 141 | Returns the input passed into the constructor used to create the tinycolor instance 142 | ```js 143 | var color = tinycolor("red"); 144 | color.getOriginalInput(); // "red" 145 | color = tinycolor({r:255, g:255, b:255}); 146 | color.getOriginalInput(); // "{r: 255, g: 255, b: 255}" 147 | ``` 148 | 149 | ### isValid 150 | 151 | Return a boolean indicating whether the color was successfully parsed. Note: if the color is not valid then it will act like `black` when being used with other methods. 152 | ```js 153 | var color1 = tinycolor("red"); 154 | color1.isValid(); // true 155 | color1.toHexString(); // "#ff0000" 156 | 157 | var color2 = tinycolor("not a color"); 158 | color2.isValid(); // false 159 | color2.toString(); // "#000000" 160 | ``` 161 | ### getBrightness 162 | 163 | Returns the perceived brightness of a color, from `0-255`, as defined by [Web Content Accessibility Guidelines (Version 1.0)](http://www.w3.org/TR/AERT#color-contrast). 164 | ```js 165 | var color1 = tinycolor("#fff"); 166 | color1.getBrightness(); // 255 167 | 168 | var color2 = tinycolor("#000"); 169 | color2.getBrightness(); // 0 170 | ``` 171 | ### isLight 172 | 173 | Return a boolean indicating whether the color's perceived brightness is light. 174 | ```js 175 | var color1 = tinycolor("#fff"); 176 | color1.isLight(); // true 177 | 178 | var color2 = tinycolor("#000"); 179 | color2.isLight(); // false 180 | ``` 181 | ### isDark 182 | 183 | Return a boolean indicating whether the color's perceived brightness is dark. 184 | ```js 185 | var color1 = tinycolor("#fff"); 186 | color1.isDark(); // false 187 | 188 | var color2 = tinycolor("#000"); 189 | color2.isDark(); // true 190 | ``` 191 | ### getLuminance 192 | 193 | Returns the perceived luminance of a color, from `0-1` as defined by [Web Content Accessibility Guidelines (Version 2.0).](http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef) 194 | ```js 195 | var color1 = tinycolor("#fff"); 196 | color1.getLuminance(); // 1 197 | 198 | var color2 = tinycolor("#000"); 199 | color2.getLuminance(); // 0 200 | ``` 201 | ### getAlpha 202 | 203 | Returns the alpha value of a color, from `0-1`. 204 | ```js 205 | var color1 = tinycolor("rgba(255, 0, 0, .5)"); 206 | color1.getAlpha(); // 0.5 207 | 208 | var color2 = tinycolor("rgb(255, 0, 0)"); 209 | color2.getAlpha(); // 1 210 | 211 | var color3 = tinycolor("transparent"); 212 | color3.getAlpha(); // 0 213 | ``` 214 | ### setAlpha 215 | 216 | Sets the alpha value on a current color. Accepted range is in between `0-1`. 217 | ```js 218 | var color = tinycolor("red"); 219 | color.getAlpha(); // 1 220 | color.setAlpha(.5); 221 | color.getAlpha(); // .5 222 | color.toRgbString(); // "rgba(255, 0, 0, .5)" 223 | ``` 224 | ### String Representations 225 | 226 | The following methods will return a property for the `alpha` value, which can be ignored: `toHsv`, `toHsl`, `toRgb` 227 | 228 | ### toHsv 229 | ```js 230 | var color = tinycolor("red"); 231 | color.toHsv(); // { h: 0, s: 1, v: 1, a: 1 } 232 | ``` 233 | ### toHsvString 234 | ```js 235 | var color = tinycolor("red"); 236 | color.toHsvString(); // "hsv(0, 100%, 100%)" 237 | color.setAlpha(0.5); 238 | color.toHsvString(); // "hsva(0, 100%, 100%, 0.5)" 239 | ``` 240 | ### toHsl 241 | ```js 242 | var color = tinycolor("red"); 243 | color.toHsl(); // { h: 0, s: 1, l: 0.5, a: 1 } 244 | ``` 245 | ### toHslString 246 | ```js 247 | var color = tinycolor("red"); 248 | color.toHslString(); // "hsl(0, 100%, 50%)" 249 | color.setAlpha(0.5); 250 | color.toHslString(); // "hsla(0, 100%, 50%, 0.5)" 251 | ``` 252 | ### toHex 253 | ```js 254 | var color = tinycolor("red"); 255 | color.toHex(); // "ff0000" 256 | ``` 257 | ### toHexString 258 | ```js 259 | var color = tinycolor("red"); 260 | color.toHexString(); // "#ff0000" 261 | ``` 262 | ### toHex8 263 | ```js 264 | var color = tinycolor("red"); 265 | color.toHex8(); // "ff0000ff" 266 | ``` 267 | ### toHex8String 268 | ```js 269 | var color = tinycolor("red"); 270 | color.toHex8String(); // "#ff0000ff" 271 | ``` 272 | ### toRgb 273 | ```js 274 | var color = tinycolor("red"); 275 | color.toRgb(); // { r: 255, g: 0, b: 0, a: 1 } 276 | ``` 277 | ### toRgbString 278 | ```js 279 | var color = tinycolor("red"); 280 | color.toRgbString(); // "rgb(255, 0, 0)" 281 | color.setAlpha(0.5); 282 | color.toRgbString(); // "rgba(255, 0, 0, 0.5)" 283 | ``` 284 | ### toPercentageRgb 285 | ```js 286 | var color = tinycolor("red"); 287 | color.toPercentageRgb() // { r: "100%", g: "0%", b: "0%", a: 1 } 288 | ``` 289 | ### toPercentageRgbString 290 | ```js 291 | var color = tinycolor("red"); 292 | color.toPercentageRgbString(); // "rgb(100%, 0%, 0%)" 293 | color.setAlpha(0.5); 294 | color.toPercentageRgbString(); // "rgba(100%, 0%, 0%, 0.5)" 295 | ``` 296 | ### toName 297 | ```js 298 | var color = tinycolor("red"); 299 | color.toName(); // "red" 300 | ``` 301 | ### toFilter 302 | ``` 303 | var color = tinycolor("red"); 304 | color.toFilter(); // "progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffff0000,endColorstr=#ffff0000)" 305 | ``` 306 | ### toString 307 | 308 | Print to a string, depending on the input format. You can also override this by passing one of `"rgb", "prgb", "hex6", "hex3", "hex8", "name", "hsl", "hsv"` into the function. 309 | ```js 310 | var color1 = tinycolor("red"); 311 | color1.toString(); // "red" 312 | color1.toString("hsv"); // "hsv(0, 100%, 100%)" 313 | 314 | var color2 = tinycolor("rgb(255, 0, 0)"); 315 | color2.toString(); // "rgb(255, 0, 0)" 316 | color2.setAlpha(.5); 317 | color2.toString(); // "rgba(255, 0, 0, 0.5)" 318 | ``` 319 | ### Color Modification 320 | 321 | These methods manipulate the current color, and return it for chaining. For instance: 322 | ```js 323 | tinycolor("red").lighten().desaturate().toHexString() // "#f53d3d" 324 | ``` 325 | ### lighten 326 | 327 | `lighten: function(amount = 10) -> TinyColor`. Lighten the color a given amount, from 0 to 100. Providing 100 will always return white. 328 | ```js 329 | tinycolor("#f00").lighten().toString(); // "#ff3333" 330 | tinycolor("#f00").lighten(100).toString(); // "#ffffff" 331 | ``` 332 | ### brighten 333 | 334 | `brighten: function(amount = 10) -> TinyColor`. Brighten the color a given amount, from 0 to 100. 335 | ```js 336 | tinycolor("#f00").brighten().toString(); // "#ff1919" 337 | ``` 338 | ### darken 339 | 340 | `darken: function(amount = 10) -> TinyColor`. Darken the color a given amount, from 0 to 100. Providing 100 will always return black. 341 | ```js 342 | tinycolor("#f00").darken().toString(); // "#cc0000" 343 | tinycolor("#f00").darken(100).toString(); // "#000000" 344 | ``` 345 | ### desaturate 346 | 347 | `desaturate: function(amount = 10) -> TinyColor`. Desaturate the color a given amount, from 0 to 100. Providing 100 will is the same as calling `greyscale`. 348 | ```js 349 | tinycolor("#f00").desaturate().toString(); // "#f20d0d" 350 | tinycolor("#f00").desaturate(100).toString(); // "#808080" 351 | ``` 352 | ### saturate 353 | 354 | `saturate: function(amount = 10) -> TinyColor`. Saturate the color a given amount, from 0 to 100. 355 | ```js 356 | tinycolor("hsl(0, 10%, 50%)").saturate().toString(); // "hsl(0, 20%, 50%)" 357 | ``` 358 | ### greyscale 359 | 360 | `greyscale: function() -> TinyColor`. Completely desaturates a color into greyscale. Same as calling `desaturate(100)`. 361 | ```js 362 | tinycolor("#f00").greyscale().toString(); // "#808080" 363 | ``` 364 | ### spin 365 | 366 | `spin: function(amount = 0) -> TinyColor`. Spin the hue a given amount, from -360 to 360. Calling with 0, 360, or -360 will do nothing (since it sets the hue back to what it was before). 367 | ```js 368 | tinycolor("#f00").spin(180).toString(); // "#00ffff" 369 | tinycolor("#f00").spin(-90).toString(); // "#7f00ff" 370 | tinycolor("#f00").spin(90).toString(); // "#80ff00" 371 | 372 | // spin(0) and spin(360) do nothing 373 | tinycolor("#f00").spin(0).toString(); // "#ff0000" 374 | tinycolor("#f00").spin(360).toString(); // "#ff0000" 375 | ``` 376 | ### Color Combinations 377 | 378 | Combination functions return an array of TinyColor objects unless otherwise noted. 379 | 380 | ### analogous 381 | 382 | `analogous: function(, results = 6, slices = 30) -> array`. 383 | ```js 384 | var colors = tinycolor("#f00").analogous(); 385 | 386 | colors.map(function(t) { return t.toHexString(); }); // [ "#ff0000", "#ff0066", "#ff0033", "#ff0000", "#ff3300", "#ff6600" ] 387 | ``` 388 | ### monochromatic 389 | 390 | `monochromatic: function(, results = 6) -> array`. 391 | ```js 392 | var colors = tinycolor("#f00").monochromatic(); 393 | 394 | colors.map(function(t) { return t.toHexString(); }); // [ "#ff0000", "#2a0000", "#550000", "#800000", "#aa0000", "#d40000" ] 395 | ``` 396 | ### splitcomplement 397 | 398 | `splitcomplement: function() -> array`. 399 | ```js 400 | var colors = tinycolor("#f00").splitcomplement(); 401 | 402 | colors.map(function(t) { return t.toHexString(); }); // [ "#ff0000", "#ccff00", "#0066ff" ] 403 | ``` 404 | ### triad 405 | 406 | `triad: function() -> array`. 407 | ```js 408 | var colors = tinycolor("#f00").triad(); 409 | 410 | colors.map(function(t) { return t.toHexString(); }); // [ "#ff0000", "#00ff00", "#0000ff" ] 411 | ``` 412 | ### tetrad 413 | 414 | `tetrad: function() -> array`. 415 | ```js 416 | var colors = tinycolor("#f00").tetrad(); 417 | 418 | colors.map(function(t) { return t.toHexString(); }); // [ "#ff0000", "#80ff00", "#00ffff", "#7f00ff" ] 419 | 420 | ``` 421 | ### complement 422 | 423 | `complement: function() -> TinyColor`. 424 | ```js 425 | tinycolor("#f00").complement().toHexString(); // "#00ffff" 426 | ``` 427 | ## Color Utilities 428 | ```js 429 | tinycolor.equals(color1, color2) 430 | tinycolor.mix(color1, color2, amount = 50) 431 | ``` 432 | ### random 433 | 434 | Returns a random color. 435 | ```js 436 | var color = tinycolor.random(); 437 | color.toRgb(); // "{r: 145, g: 40, b: 198, a: 1}" 438 | ``` 439 | 440 | ### Readability 441 | 442 | TinyColor assesses readability based on the [Web Content Accessibility Guidelines (Version 2.0)](http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef). 443 | 444 | #### readability 445 | 446 | `readability: function(TinyColor, TinyColor) -> Object`. 447 | Returns the contrast ratio between two colors. 448 | ```js 449 | tinycolor.readability("#000", "#000"); // 1 450 | tinycolor.readability("#000", "#111"); // 1.1121078324840545 451 | tinycolor.readability("#000", "#fff"); // 21 452 | ``` 453 | Use the values in your own calculations, or use one of the convenience functions below. 454 | 455 | #### isReadable 456 | 457 | `isReadable: function(TinyColor, TinyColor, Object) -> Boolean`. Ensure that foreground and background color combinations meet WCAG guidelines. `Object` is optional, defaulting to `{level: "AA",size: "small"}`. `level` can be `"AA"` or "AAA" and `size` can be `"small"` or `"large"`. 458 | 459 | Here are links to read more about the [AA](http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html) and [AAA](http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast7.html) requirements. 460 | ```js 461 | tinycolor.isReadable("#000", "#111", {}); // false 462 | tinycolor.isReadable("#ff0088", "#5c1a72",{level:"AA",size:"small"}); //false 463 | tinycolor.isReadable("#ff0088", "#5c1a72",{level:"AA",size:"large"}), //true 464 | ``` 465 | #### mostReadable 466 | 467 | `mostReadable: function(TinyColor, [TinyColor, Tinycolor ...], Object) -> Boolean`. 468 | Given a base color and a list of possible foreground or background colors for that base, returns the most readable color. 469 | If none of the colors in the list is readable, `mostReadable` will return the better of black or white if `includeFallbackColors:true`. 470 | ```js 471 | tinycolor.mostReadable("#000", ["#f00", "#0f0", "#00f"]).toHexString(); // "#00ff00" 472 | tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:false}).toHexString(); // "#112255" 473 | tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:true}).toHexString(); // "#ffffff" 474 | tinycolor.mostReadable("#ff0088", ["#2e0c3a"],{includeFallbackColors:true,level:"AAA",size:"large"}).toHexString() // "#2e0c3a", 475 | tinycolor.mostReadable("#ff0088", ["#2e0c3a"],{includeFallbackColors:true,level:"AAA",size:"small"}).toHexString() // "#000000", 476 | ``` 477 | See [index.html](https://github.com/bgrins/TinyColor/blob/master/index.html) in the project for a demo. 478 | 479 | ## Common operations 480 | 481 | ### clone 482 | 483 | `clone: function() -> TinyColor`. 484 | Instantiate a new TinyColor object with the same color. Any changes to the new one won't affect the old one. 485 | ```js 486 | var color1 = tinycolor("#F00"); 487 | var color2 = color1.clone(); 488 | color2.setAlpha(.5); 489 | 490 | color1.toString(); // "#ff0000" 491 | color2.toString(); // "rgba(255, 0, 0, 0.5)" 492 | ``` 493 | -------------------------------------------------------------------------------- /npm/deno_asserts@0.168.0.mjs: -------------------------------------------------------------------------------- 1 | // deno-fmt-ignore-file 2 | // deno-lint-ignore-file 3 | // This code was bundled using `deno bundle` and it's not recommended to edit it manually 4 | // Using `deno bundle https://deno.land/std@0.168.0/testing/asserts.ts > npm/deno_asserts@0.168.0.mjs` 5 | 6 | const { Deno } = globalThis; 7 | const noColor = typeof Deno?.noColor === "boolean" ? Deno.noColor : true; 8 | let enabled = !noColor; 9 | function code(open, close) { 10 | return { 11 | open: `\x1b[${open.join(";")}m`, 12 | close: `\x1b[${close}m`, 13 | regexp: new RegExp(`\\x1b\\[${close}m`, "g") 14 | }; 15 | } 16 | function run(str, code) { 17 | return enabled ? `${code.open}${str.replace(code.regexp, code.open)}${code.close}` : str; 18 | } 19 | function bold(str) { 20 | return run(str, code([ 21 | 1 22 | ], 22)); 23 | } 24 | function red(str) { 25 | return run(str, code([ 26 | 31 27 | ], 39)); 28 | } 29 | function green(str) { 30 | return run(str, code([ 31 | 32 32 | ], 39)); 33 | } 34 | function white(str) { 35 | return run(str, code([ 36 | 37 37 | ], 39)); 38 | } 39 | function gray(str) { 40 | return brightBlack(str); 41 | } 42 | function brightBlack(str) { 43 | return run(str, code([ 44 | 90 45 | ], 39)); 46 | } 47 | function bgRed(str) { 48 | return run(str, code([ 49 | 41 50 | ], 49)); 51 | } 52 | function bgGreen(str) { 53 | return run(str, code([ 54 | 42 55 | ], 49)); 56 | } 57 | const ANSI_PATTERN = new RegExp([ 58 | "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", 59 | "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))" 60 | ].join("|"), "g"); 61 | function stripColor(string) { 62 | return string.replace(ANSI_PATTERN, ""); 63 | } 64 | var DiffType; 65 | (function(DiffType) { 66 | DiffType["removed"] = "removed"; 67 | DiffType["common"] = "common"; 68 | DiffType["added"] = "added"; 69 | })(DiffType || (DiffType = {})); 70 | const REMOVED = 1; 71 | const COMMON = 2; 72 | const ADDED = 3; 73 | function createCommon(A, B, reverse) { 74 | const common = []; 75 | if (A.length === 0 || B.length === 0) return []; 76 | for(let i = 0; i < Math.min(A.length, B.length); i += 1){ 77 | if (A[reverse ? A.length - i - 1 : i] === B[reverse ? B.length - i - 1 : i]) { 78 | common.push(A[reverse ? A.length - i - 1 : i]); 79 | } else { 80 | return common; 81 | } 82 | } 83 | return common; 84 | } 85 | function diff(A, B) { 86 | const prefixCommon = createCommon(A, B); 87 | const suffixCommon = createCommon(A.slice(prefixCommon.length), B.slice(prefixCommon.length), true).reverse(); 88 | A = suffixCommon.length ? A.slice(prefixCommon.length, -suffixCommon.length) : A.slice(prefixCommon.length); 89 | B = suffixCommon.length ? B.slice(prefixCommon.length, -suffixCommon.length) : B.slice(prefixCommon.length); 90 | const swapped = B.length > A.length; 91 | [A, B] = swapped ? [ 92 | B, 93 | A 94 | ] : [ 95 | A, 96 | B 97 | ]; 98 | const M = A.length; 99 | const N = B.length; 100 | if (!M && !N && !suffixCommon.length && !prefixCommon.length) return []; 101 | if (!N) { 102 | return [ 103 | ...prefixCommon.map((c)=>({ 104 | type: DiffType.common, 105 | value: c 106 | })), 107 | ...A.map((a)=>({ 108 | type: swapped ? DiffType.added : DiffType.removed, 109 | value: a 110 | })), 111 | ...suffixCommon.map((c)=>({ 112 | type: DiffType.common, 113 | value: c 114 | })) 115 | ]; 116 | } 117 | const offset = N; 118 | const delta = M - N; 119 | const size = M + N + 1; 120 | const fp = Array.from({ 121 | length: size 122 | }, ()=>({ 123 | y: -1, 124 | id: -1 125 | })); 126 | const routes = new Uint32Array((M * N + size + 1) * 2); 127 | const diffTypesPtrOffset = routes.length / 2; 128 | let ptr = 0; 129 | let p = -1; 130 | function backTrace(A, B, current, swapped) { 131 | const M = A.length; 132 | const N = B.length; 133 | const result = []; 134 | let a = M - 1; 135 | let b = N - 1; 136 | let j = routes[current.id]; 137 | let type = routes[current.id + diffTypesPtrOffset]; 138 | while(true){ 139 | if (!j && !type) break; 140 | const prev = j; 141 | if (type === 1) { 142 | result.unshift({ 143 | type: swapped ? DiffType.removed : DiffType.added, 144 | value: B[b] 145 | }); 146 | b -= 1; 147 | } else if (type === 3) { 148 | result.unshift({ 149 | type: swapped ? DiffType.added : DiffType.removed, 150 | value: A[a] 151 | }); 152 | a -= 1; 153 | } else { 154 | result.unshift({ 155 | type: DiffType.common, 156 | value: A[a] 157 | }); 158 | a -= 1; 159 | b -= 1; 160 | } 161 | j = routes[prev]; 162 | type = routes[prev + diffTypesPtrOffset]; 163 | } 164 | return result; 165 | } 166 | function createFP(slide, down, k, M) { 167 | if (slide && slide.y === -1 && down && down.y === -1) { 168 | return { 169 | y: 0, 170 | id: 0 171 | }; 172 | } 173 | if (down && down.y === -1 || k === M || (slide && slide.y) > (down && down.y) + 1) { 174 | const prev = slide.id; 175 | ptr++; 176 | routes[ptr] = prev; 177 | routes[ptr + diffTypesPtrOffset] = ADDED; 178 | return { 179 | y: slide.y, 180 | id: ptr 181 | }; 182 | } else { 183 | const prev1 = down.id; 184 | ptr++; 185 | routes[ptr] = prev1; 186 | routes[ptr + diffTypesPtrOffset] = REMOVED; 187 | return { 188 | y: down.y + 1, 189 | id: ptr 190 | }; 191 | } 192 | } 193 | function snake(k, slide, down, _offset, A, B) { 194 | const M = A.length; 195 | const N = B.length; 196 | if (k < -N || M < k) return { 197 | y: -1, 198 | id: -1 199 | }; 200 | const fp = createFP(slide, down, k, M); 201 | while(fp.y + k < M && fp.y < N && A[fp.y + k] === B[fp.y]){ 202 | const prev = fp.id; 203 | ptr++; 204 | fp.id = ptr; 205 | fp.y += 1; 206 | routes[ptr] = prev; 207 | routes[ptr + diffTypesPtrOffset] = COMMON; 208 | } 209 | return fp; 210 | } 211 | while(fp[delta + offset].y < N){ 212 | p = p + 1; 213 | for(let k = -p; k < delta; ++k){ 214 | fp[k + offset] = snake(k, fp[k - 1 + offset], fp[k + 1 + offset], offset, A, B); 215 | } 216 | for(let k1 = delta + p; k1 > delta; --k1){ 217 | fp[k1 + offset] = snake(k1, fp[k1 - 1 + offset], fp[k1 + 1 + offset], offset, A, B); 218 | } 219 | fp[delta + offset] = snake(delta, fp[delta - 1 + offset], fp[delta + 1 + offset], offset, A, B); 220 | } 221 | return [ 222 | ...prefixCommon.map((c)=>({ 223 | type: DiffType.common, 224 | value: c 225 | })), 226 | ...backTrace(A, B, fp[delta + offset], swapped), 227 | ...suffixCommon.map((c)=>({ 228 | type: DiffType.common, 229 | value: c 230 | })) 231 | ]; 232 | } 233 | function diffstr(A, B) { 234 | function unescape(string) { 235 | return string.replaceAll("\b", "\\b").replaceAll("\f", "\\f").replaceAll("\t", "\\t").replaceAll("\v", "\\v").replaceAll(/\r\n|\r|\n/g, (str)=>str === "\r" ? "\\r" : str === "\n" ? "\\n\n" : "\\r\\n\r\n"); 236 | } 237 | function tokenize(string, { wordDiff =false } = {}) { 238 | if (wordDiff) { 239 | const tokens = string.split(/([^\S\r\n]+|[()[\]{}'"\r\n]|\b)/); 240 | const words = /^[a-zA-Z\u{C0}-\u{FF}\u{D8}-\u{F6}\u{F8}-\u{2C6}\u{2C8}-\u{2D7}\u{2DE}-\u{2FF}\u{1E00}-\u{1EFF}]+$/u; 241 | for(let i = 0; i < tokens.length - 1; i++){ 242 | if (!tokens[i + 1] && tokens[i + 2] && words.test(tokens[i]) && words.test(tokens[i + 2])) { 243 | tokens[i] += tokens[i + 2]; 244 | tokens.splice(i + 1, 2); 245 | i--; 246 | } 247 | } 248 | return tokens.filter((token)=>token); 249 | } else { 250 | const tokens1 = [], lines = string.split(/(\n|\r\n)/); 251 | if (!lines[lines.length - 1]) { 252 | lines.pop(); 253 | } 254 | for(let i1 = 0; i1 < lines.length; i1++){ 255 | if (i1 % 2) { 256 | tokens1[tokens1.length - 1] += lines[i1]; 257 | } else { 258 | tokens1.push(lines[i1]); 259 | } 260 | } 261 | return tokens1; 262 | } 263 | } 264 | function createDetails(line, tokens) { 265 | return tokens.filter(({ type })=>type === line.type || type === DiffType.common).map((result, i, t)=>{ 266 | if (result.type === DiffType.common && t[i - 1] && t[i - 1]?.type === t[i + 1]?.type && /\s+/.test(result.value)) { 267 | result.type = t[i - 1].type; 268 | } 269 | return result; 270 | }); 271 | } 272 | const diffResult = diff(tokenize(`${unescape(A)}\n`), tokenize(`${unescape(B)}\n`)); 273 | const added = [], removed = []; 274 | for (const result of diffResult){ 275 | if (result.type === DiffType.added) { 276 | added.push(result); 277 | } 278 | if (result.type === DiffType.removed) { 279 | removed.push(result); 280 | } 281 | } 282 | const aLines = added.length < removed.length ? added : removed; 283 | const bLines = aLines === removed ? added : removed; 284 | for (const a of aLines){ 285 | let tokens = [], b; 286 | while(bLines.length){ 287 | b = bLines.shift(); 288 | tokens = diff(tokenize(a.value, { 289 | wordDiff: true 290 | }), tokenize(b?.value ?? "", { 291 | wordDiff: true 292 | })); 293 | if (tokens.some(({ type , value })=>type === DiffType.common && value.trim().length)) { 294 | break; 295 | } 296 | } 297 | a.details = createDetails(a, tokens); 298 | if (b) { 299 | b.details = createDetails(b, tokens); 300 | } 301 | } 302 | return diffResult; 303 | } 304 | function createColor(diffType, { background =false } = {}) { 305 | background = false; 306 | switch(diffType){ 307 | case DiffType.added: 308 | return (s)=>background ? bgGreen(white(s)) : green(bold(s)); 309 | case DiffType.removed: 310 | return (s)=>background ? bgRed(white(s)) : red(bold(s)); 311 | default: 312 | return white; 313 | } 314 | } 315 | function createSign(diffType) { 316 | switch(diffType){ 317 | case DiffType.added: 318 | return "+ "; 319 | case DiffType.removed: 320 | return "- "; 321 | default: 322 | return " "; 323 | } 324 | } 325 | function buildMessage(diffResult, { stringDiff =false } = {}) { 326 | const messages = [], diffMessages = []; 327 | messages.push(""); 328 | messages.push(""); 329 | messages.push(` ${gray(bold("[Diff]"))} ${red(bold("Actual"))} / ${green(bold("Expected"))}`); 330 | messages.push(""); 331 | messages.push(""); 332 | diffResult.forEach((result)=>{ 333 | const c = createColor(result.type); 334 | const line = result.details?.map((detail)=>detail.type !== DiffType.common ? createColor(detail.type, { 335 | background: true 336 | })(detail.value) : detail.value).join("") ?? result.value; 337 | diffMessages.push(c(`${createSign(result.type)}${line}`)); 338 | }); 339 | messages.push(...stringDiff ? [ 340 | diffMessages.join("") 341 | ] : diffMessages); 342 | messages.push(""); 343 | return messages; 344 | } 345 | function format(v) { 346 | const { Deno } = globalThis; 347 | return typeof Deno?.inspect === "function" ? Deno.inspect(v, { 348 | depth: Infinity, 349 | sorted: true, 350 | trailingComma: true, 351 | compact: false, 352 | iterableLimit: Infinity, 353 | getters: true 354 | }) : `"${String(v).replace(/(?=["\\])/g, "\\")}"`; 355 | } 356 | const CAN_NOT_DISPLAY = "[Cannot display]"; 357 | class AssertionError extends Error { 358 | name = "AssertionError"; 359 | constructor(message){ 360 | super(message); 361 | } 362 | } 363 | function isKeyedCollection(x) { 364 | return [ 365 | Symbol.iterator, 366 | "size" 367 | ].every((k)=>k in x); 368 | } 369 | function equal(c, d) { 370 | const seen = new Map(); 371 | return function compare(a, b) { 372 | if (a && b && (a instanceof RegExp && b instanceof RegExp || a instanceof URL && b instanceof URL)) { 373 | return String(a) === String(b); 374 | } 375 | if (a instanceof Date && b instanceof Date) { 376 | const aTime = a.getTime(); 377 | const bTime = b.getTime(); 378 | if (Number.isNaN(aTime) && Number.isNaN(bTime)) { 379 | return true; 380 | } 381 | return aTime === bTime; 382 | } 383 | if (typeof a === "number" && typeof b === "number") { 384 | return Number.isNaN(a) && Number.isNaN(b) || a === b; 385 | } 386 | if (Object.is(a, b)) { 387 | return true; 388 | } 389 | if (a && typeof a === "object" && b && typeof b === "object") { 390 | if (a && b && !constructorsEqual(a, b)) { 391 | return false; 392 | } 393 | if (a instanceof WeakMap || b instanceof WeakMap) { 394 | if (!(a instanceof WeakMap && b instanceof WeakMap)) return false; 395 | throw new TypeError("cannot compare WeakMap instances"); 396 | } 397 | if (a instanceof WeakSet || b instanceof WeakSet) { 398 | if (!(a instanceof WeakSet && b instanceof WeakSet)) return false; 399 | throw new TypeError("cannot compare WeakSet instances"); 400 | } 401 | if (seen.get(a) === b) { 402 | return true; 403 | } 404 | if (Object.keys(a || {}).length !== Object.keys(b || {}).length) { 405 | return false; 406 | } 407 | seen.set(a, b); 408 | if (isKeyedCollection(a) && isKeyedCollection(b)) { 409 | if (a.size !== b.size) { 410 | return false; 411 | } 412 | let unmatchedEntries = a.size; 413 | for (const [aKey, aValue] of a.entries()){ 414 | for (const [bKey, bValue] of b.entries()){ 415 | if (aKey === aValue && bKey === bValue && compare(aKey, bKey) || compare(aKey, bKey) && compare(aValue, bValue)) { 416 | unmatchedEntries--; 417 | break; 418 | } 419 | } 420 | } 421 | return unmatchedEntries === 0; 422 | } 423 | const merged = { 424 | ...a, 425 | ...b 426 | }; 427 | for (const key of [ 428 | ...Object.getOwnPropertyNames(merged), 429 | ...Object.getOwnPropertySymbols(merged) 430 | ]){ 431 | if (!compare(a && a[key], b && b[key])) { 432 | return false; 433 | } 434 | if (key in a && !(key in b) || key in b && !(key in a)) { 435 | return false; 436 | } 437 | } 438 | if (a instanceof WeakRef || b instanceof WeakRef) { 439 | if (!(a instanceof WeakRef && b instanceof WeakRef)) return false; 440 | return compare(a.deref(), b.deref()); 441 | } 442 | return true; 443 | } 444 | return false; 445 | }(c, d); 446 | } 447 | function constructorsEqual(a, b) { 448 | return a.constructor === b.constructor || a.constructor === Object && !b.constructor || !a.constructor && b.constructor === Object; 449 | } 450 | function assert(expr, msg = "") { 451 | if (!expr) { 452 | throw new AssertionError(msg); 453 | } 454 | } 455 | function assertFalse(expr, msg = "") { 456 | if (expr) { 457 | throw new AssertionError(msg); 458 | } 459 | } 460 | function assertEquals(actual, expected, msg) { 461 | if (equal(actual, expected)) { 462 | return; 463 | } 464 | let message = ""; 465 | const actualString = format(actual); 466 | const expectedString = format(expected); 467 | try { 468 | const stringDiff = typeof actual === "string" && typeof expected === "string"; 469 | const diffResult = stringDiff ? diffstr(actual, expected) : diff(actualString.split("\n"), expectedString.split("\n")); 470 | const diffMsg = buildMessage(diffResult, { 471 | stringDiff 472 | }).join("\n"); 473 | message = `Values are not equal:\n${diffMsg}`; 474 | } catch { 475 | message = `\n${red(CAN_NOT_DISPLAY)} + \n\n`; 476 | } 477 | if (msg) { 478 | message = msg; 479 | } 480 | throw new AssertionError(message); 481 | } 482 | function assertNotEquals(actual, expected, msg) { 483 | if (!equal(actual, expected)) { 484 | return; 485 | } 486 | let actualString; 487 | let expectedString; 488 | try { 489 | actualString = String(actual); 490 | } catch { 491 | actualString = "[Cannot display]"; 492 | } 493 | try { 494 | expectedString = String(expected); 495 | } catch { 496 | expectedString = "[Cannot display]"; 497 | } 498 | if (!msg) { 499 | msg = `actual: ${actualString} expected not to be: ${expectedString}`; 500 | } 501 | throw new AssertionError(msg); 502 | } 503 | function assertStrictEquals(actual, expected, msg) { 504 | if (Object.is(actual, expected)) { 505 | return; 506 | } 507 | let message; 508 | if (msg) { 509 | message = msg; 510 | } else { 511 | const actualString = format(actual); 512 | const expectedString = format(expected); 513 | if (actualString === expectedString) { 514 | const withOffset = actualString.split("\n").map((l)=>` ${l}`).join("\n"); 515 | message = `Values have the same structure but are not reference-equal:\n\n${red(withOffset)}\n`; 516 | } else { 517 | try { 518 | const stringDiff = typeof actual === "string" && typeof expected === "string"; 519 | const diffResult = stringDiff ? diffstr(actual, expected) : diff(actualString.split("\n"), expectedString.split("\n")); 520 | const diffMsg = buildMessage(diffResult, { 521 | stringDiff 522 | }).join("\n"); 523 | message = `Values are not strictly equal:\n${diffMsg}`; 524 | } catch { 525 | message = `\n${red(CAN_NOT_DISPLAY)} + \n\n`; 526 | } 527 | } 528 | } 529 | throw new AssertionError(message); 530 | } 531 | function assertNotStrictEquals(actual, expected, msg) { 532 | if (!Object.is(actual, expected)) { 533 | return; 534 | } 535 | throw new AssertionError(msg ?? `Expected "actual" to be strictly unequal to: ${format(actual)}\n`); 536 | } 537 | function assertAlmostEquals(actual, expected, tolerance = 1e-7, msg) { 538 | if (Object.is(actual, expected)) { 539 | return; 540 | } 541 | const delta = Math.abs(expected - actual); 542 | if (delta <= tolerance) { 543 | return; 544 | } 545 | const f = (n)=>Number.isInteger(n) ? n : n.toExponential(); 546 | throw new AssertionError(msg ?? `actual: "${f(actual)}" expected to be close to "${f(expected)}": \ 547 | delta "${f(delta)}" is greater than "${f(tolerance)}"`); 548 | } 549 | function assertInstanceOf(actual, expectedType, msg = "") { 550 | if (!msg) { 551 | const expectedTypeStr = expectedType.name; 552 | let actualTypeStr = ""; 553 | if (actual === null) { 554 | actualTypeStr = "null"; 555 | } else if (actual === undefined) { 556 | actualTypeStr = "undefined"; 557 | } else if (typeof actual === "object") { 558 | actualTypeStr = actual.constructor?.name ?? "Object"; 559 | } else { 560 | actualTypeStr = typeof actual; 561 | } 562 | if (expectedTypeStr == actualTypeStr) { 563 | msg = `Expected object to be an instance of "${expectedTypeStr}".`; 564 | } else if (actualTypeStr == "function") { 565 | msg = `Expected object to be an instance of "${expectedTypeStr}" but was not an instanced object.`; 566 | } else { 567 | msg = `Expected object to be an instance of "${expectedTypeStr}" but was "${actualTypeStr}".`; 568 | } 569 | } 570 | assert(actual instanceof expectedType, msg); 571 | } 572 | function assertNotInstanceOf(actual, unexpectedType, msg = `Expected object to not be an instance of "${typeof unexpectedType}"`) { 573 | assertFalse(actual instanceof unexpectedType, msg); 574 | } 575 | function assertExists(actual, msg) { 576 | if (actual === undefined || actual === null) { 577 | if (!msg) { 578 | msg = `actual: "${actual}" expected to not be null or undefined`; 579 | } 580 | throw new AssertionError(msg); 581 | } 582 | } 583 | function assertStringIncludes(actual, expected, msg) { 584 | if (!actual.includes(expected)) { 585 | if (!msg) { 586 | msg = `actual: "${actual}" expected to contain: "${expected}"`; 587 | } 588 | throw new AssertionError(msg); 589 | } 590 | } 591 | function assertArrayIncludes(actual, expected, msg) { 592 | const missing = []; 593 | for(let i = 0; i < expected.length; i++){ 594 | let found = false; 595 | for(let j = 0; j < actual.length; j++){ 596 | if (equal(expected[i], actual[j])) { 597 | found = true; 598 | break; 599 | } 600 | } 601 | if (!found) { 602 | missing.push(expected[i]); 603 | } 604 | } 605 | if (missing.length === 0) { 606 | return; 607 | } 608 | if (!msg) { 609 | msg = `actual: "${format(actual)}" expected to include: "${format(expected)}"\nmissing: ${format(missing)}`; 610 | } 611 | throw new AssertionError(msg); 612 | } 613 | function assertMatch(actual, expected, msg) { 614 | if (!expected.test(actual)) { 615 | if (!msg) { 616 | msg = `actual: "${actual}" expected to match: "${expected}"`; 617 | } 618 | throw new AssertionError(msg); 619 | } 620 | } 621 | function assertNotMatch(actual, expected, msg) { 622 | if (expected.test(actual)) { 623 | if (!msg) { 624 | msg = `actual: "${actual}" expected to not match: "${expected}"`; 625 | } 626 | throw new AssertionError(msg); 627 | } 628 | } 629 | function assertObjectMatch(actual, expected) { 630 | function filter(a, b) { 631 | const seen = new WeakMap(); 632 | return fn(a, b); 633 | function fn(a, b) { 634 | if (seen.has(a) && seen.get(a) === b) { 635 | return a; 636 | } 637 | seen.set(a, b); 638 | const filtered = {}; 639 | const entries = [ 640 | ...Object.getOwnPropertyNames(a), 641 | ...Object.getOwnPropertySymbols(a) 642 | ].filter((key)=>key in b).map((key)=>[ 643 | key, 644 | a[key] 645 | ]); 646 | for (const [key, value] of entries){ 647 | if (Array.isArray(value)) { 648 | const subset = b[key]; 649 | if (Array.isArray(subset)) { 650 | filtered[key] = fn({ 651 | ...value 652 | }, { 653 | ...subset 654 | }); 655 | continue; 656 | } 657 | } else if (value instanceof RegExp) { 658 | filtered[key] = value; 659 | continue; 660 | } else if (typeof value === "object") { 661 | const subset1 = b[key]; 662 | if (typeof subset1 === "object" && subset1) { 663 | if (value instanceof Map && subset1 instanceof Map) { 664 | filtered[key] = new Map([ 665 | ...value 666 | ].filter(([k])=>subset1.has(k)).map(([k, v])=>[ 667 | k, 668 | typeof v === "object" ? fn(v, subset1.get(k)) : v 669 | ])); 670 | continue; 671 | } 672 | if (value instanceof Set && subset1 instanceof Set) { 673 | filtered[key] = new Set([ 674 | ...value 675 | ].filter((v)=>subset1.has(v))); 676 | continue; 677 | } 678 | filtered[key] = fn(value, subset1); 679 | continue; 680 | } 681 | } 682 | filtered[key] = value; 683 | } 684 | return filtered; 685 | } 686 | } 687 | return assertEquals(filter(actual, expected), filter(expected, expected)); 688 | } 689 | function fail(msg) { 690 | assert(false, `Failed assertion${msg ? `: ${msg}` : "."}`); 691 | } 692 | function assertIsError(error, ErrorClass, msgIncludes, msg) { 693 | if (error instanceof Error === false) { 694 | throw new AssertionError(`Expected "error" to be an Error object.`); 695 | } 696 | if (ErrorClass && !(error instanceof ErrorClass)) { 697 | msg = `Expected error to be instance of "${ErrorClass.name}", but was "${typeof error === "object" ? error?.constructor?.name : "[not an object]"}"${msg ? `: ${msg}` : "."}`; 698 | throw new AssertionError(msg); 699 | } 700 | if (msgIncludes && (!(error instanceof Error) || !stripColor(error.message).includes(stripColor(msgIncludes)))) { 701 | msg = `Expected error message to include "${msgIncludes}", but got "${error instanceof Error ? error.message : "[not an Error]"}"${msg ? `: ${msg}` : "."}`; 702 | throw new AssertionError(msg); 703 | } 704 | } 705 | function assertThrows(fn, errorClassOrMsg, msgIncludesOrMsg, msg) { 706 | let ErrorClass = undefined; 707 | let msgIncludes = undefined; 708 | let err; 709 | if (typeof errorClassOrMsg !== "string") { 710 | if (errorClassOrMsg === undefined || errorClassOrMsg.prototype instanceof Error || errorClassOrMsg.prototype === Error.prototype) { 711 | ErrorClass = errorClassOrMsg; 712 | msgIncludes = msgIncludesOrMsg; 713 | } else { 714 | msg = msgIncludesOrMsg; 715 | } 716 | } else { 717 | msg = errorClassOrMsg; 718 | } 719 | let doesThrow = false; 720 | const msgToAppendToError = msg ? `: ${msg}` : "."; 721 | try { 722 | fn(); 723 | } catch (error) { 724 | if (ErrorClass) { 725 | if (error instanceof Error === false) { 726 | throw new AssertionError("A non-Error object was thrown."); 727 | } 728 | assertIsError(error, ErrorClass, msgIncludes, msg); 729 | } 730 | err = error; 731 | doesThrow = true; 732 | } 733 | if (!doesThrow) { 734 | msg = `Expected function to throw${msgToAppendToError}`; 735 | throw new AssertionError(msg); 736 | } 737 | return err; 738 | } 739 | async function assertRejects(fn, errorClassOrMsg, msgIncludesOrMsg, msg) { 740 | let ErrorClass = undefined; 741 | let msgIncludes = undefined; 742 | let err; 743 | if (typeof errorClassOrMsg !== "string") { 744 | if (errorClassOrMsg === undefined || errorClassOrMsg.prototype instanceof Error || errorClassOrMsg.prototype === Error.prototype) { 745 | ErrorClass = errorClassOrMsg; 746 | msgIncludes = msgIncludesOrMsg; 747 | } 748 | } else { 749 | msg = errorClassOrMsg; 750 | } 751 | let doesThrow = false; 752 | let isPromiseReturned = false; 753 | const msgToAppendToError = msg ? `: ${msg}` : "."; 754 | try { 755 | const possiblePromise = fn(); 756 | if (possiblePromise && typeof possiblePromise === "object" && typeof possiblePromise.then === "function") { 757 | isPromiseReturned = true; 758 | await possiblePromise; 759 | } 760 | } catch (error) { 761 | if (!isPromiseReturned) { 762 | throw new AssertionError(`Function throws when expected to reject${msgToAppendToError}`); 763 | } 764 | if (ErrorClass) { 765 | if (error instanceof Error === false) { 766 | throw new AssertionError("A non-Error object was rejected."); 767 | } 768 | assertIsError(error, ErrorClass, msgIncludes, msg); 769 | } 770 | err = error; 771 | doesThrow = true; 772 | } 773 | if (!doesThrow) { 774 | throw new AssertionError(`Expected function to reject${msgToAppendToError}`); 775 | } 776 | return err; 777 | } 778 | function unimplemented(msg) { 779 | throw new AssertionError(msg || "unimplemented"); 780 | } 781 | function unreachable() { 782 | throw new AssertionError("unreachable"); 783 | } 784 | export { AssertionError as AssertionError }; 785 | export { equal as equal }; 786 | export { assert as assert }; 787 | export { assertFalse as assertFalse }; 788 | export { assertEquals as assertEquals }; 789 | export { assertNotEquals as assertNotEquals }; 790 | export { assertStrictEquals as assertStrictEquals }; 791 | export { assertNotStrictEquals as assertNotStrictEquals }; 792 | export { assertAlmostEquals as assertAlmostEquals }; 793 | export { assertInstanceOf as assertInstanceOf }; 794 | export { assertNotInstanceOf as assertNotInstanceOf }; 795 | export { assertExists as assertExists }; 796 | export { assertStringIncludes as assertStringIncludes }; 797 | export { assertArrayIncludes as assertArrayIncludes }; 798 | export { assertMatch as assertMatch }; 799 | export { assertNotMatch as assertNotMatch }; 800 | export { assertObjectMatch as assertObjectMatch }; 801 | export { fail as fail }; 802 | export { assertIsError as assertIsError }; 803 | export { assertThrows as assertThrows }; 804 | export { assertRejects as assertRejects }; 805 | export { unimplemented as unimplemented }; 806 | export { unreachable as unreachable }; 807 | 808 | -------------------------------------------------------------------------------- /npm/esm/tinycolor.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated. It's used to publish ESM to npm. 2 | function _typeof(obj) { 3 | "@babel/helpers - typeof"; 4 | 5 | return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { 6 | return typeof obj; 7 | } : function (obj) { 8 | return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; 9 | }, _typeof(obj); 10 | } 11 | 12 | // https://github.com/bgrins/TinyColor 13 | // Brian Grinstead, MIT License 14 | 15 | var trimLeft = /^\s+/; 16 | var trimRight = /\s+$/; 17 | function tinycolor(color, opts) { 18 | color = color ? color : ""; 19 | opts = opts || {}; 20 | 21 | // If input is already a tinycolor, return itself 22 | if (color instanceof tinycolor) { 23 | return color; 24 | } 25 | // If we are called as a function, call using new instead 26 | if (!(this instanceof tinycolor)) { 27 | return new tinycolor(color, opts); 28 | } 29 | var rgb = inputToRGB(color); 30 | this._originalInput = color, this._r = rgb.r, this._g = rgb.g, this._b = rgb.b, this._a = rgb.a, this._roundA = Math.round(100 * this._a) / 100, this._format = opts.format || rgb.format; 31 | this._gradientType = opts.gradientType; 32 | 33 | // Don't let the range of [0,255] come back in [0,1]. 34 | // Potentially lose a little bit of precision here, but will fix issues where 35 | // .5 gets interpreted as half of the total, instead of half of 1 36 | // If it was supposed to be 128, this was already taken care of by `inputToRgb` 37 | if (this._r < 1) this._r = Math.round(this._r); 38 | if (this._g < 1) this._g = Math.round(this._g); 39 | if (this._b < 1) this._b = Math.round(this._b); 40 | this._ok = rgb.ok; 41 | } 42 | tinycolor.prototype = { 43 | isDark: function isDark() { 44 | return this.getBrightness() < 128; 45 | }, 46 | isLight: function isLight() { 47 | return !this.isDark(); 48 | }, 49 | isValid: function isValid() { 50 | return this._ok; 51 | }, 52 | getOriginalInput: function getOriginalInput() { 53 | return this._originalInput; 54 | }, 55 | getFormat: function getFormat() { 56 | return this._format; 57 | }, 58 | getAlpha: function getAlpha() { 59 | return this._a; 60 | }, 61 | getBrightness: function getBrightness() { 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 getLuminance() { 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 setAlpha(value) { 79 | this._a = boundAlpha(value); 80 | this._roundA = Math.round(100 * this._a) / 100; 81 | return this; 82 | }, 83 | toHsv: function toHsv() { 84 | var hsv = rgbToHsv(this._r, this._g, this._b); 85 | return { 86 | h: hsv.h * 360, 87 | s: hsv.s, 88 | v: hsv.v, 89 | a: this._a 90 | }; 91 | }, 92 | toHsvString: function toHsvString() { 93 | var hsv = rgbToHsv(this._r, this._g, this._b); 94 | var h = Math.round(hsv.h * 360), 95 | s = Math.round(hsv.s * 100), 96 | v = Math.round(hsv.v * 100); 97 | return this._a == 1 ? "hsv(" + h + ", " + s + "%, " + v + "%)" : "hsva(" + h + ", " + s + "%, " + v + "%, " + this._roundA + ")"; 98 | }, 99 | toHsl: function toHsl() { 100 | var hsl = rgbToHsl(this._r, this._g, this._b); 101 | return { 102 | h: hsl.h * 360, 103 | s: hsl.s, 104 | l: hsl.l, 105 | a: this._a 106 | }; 107 | }, 108 | toHslString: function toHslString() { 109 | var hsl = rgbToHsl(this._r, this._g, this._b); 110 | var h = Math.round(hsl.h * 360), 111 | s = Math.round(hsl.s * 100), 112 | l = Math.round(hsl.l * 100); 113 | return this._a == 1 ? "hsl(" + h + ", " + s + "%, " + l + "%)" : "hsla(" + h + ", " + s + "%, " + l + "%, " + this._roundA + ")"; 114 | }, 115 | toHex: function toHex(allow3Char) { 116 | return rgbToHex(this._r, this._g, this._b, allow3Char); 117 | }, 118 | toHexString: function toHexString(allow3Char) { 119 | return "#" + this.toHex(allow3Char); 120 | }, 121 | toHex8: function toHex8(allow4Char) { 122 | return rgbaToHex(this._r, this._g, this._b, this._a, allow4Char); 123 | }, 124 | toHex8String: function toHex8String(allow4Char) { 125 | return "#" + this.toHex8(allow4Char); 126 | }, 127 | toRgb: function toRgb() { 128 | return { 129 | r: Math.round(this._r), 130 | g: Math.round(this._g), 131 | b: Math.round(this._b), 132 | a: this._a 133 | }; 134 | }, 135 | toRgbString: function toRgbString() { 136 | return this._a == 1 ? "rgb(" + Math.round(this._r) + ", " + Math.round(this._g) + ", " + Math.round(this._b) + ")" : "rgba(" + Math.round(this._r) + ", " + Math.round(this._g) + ", " + Math.round(this._b) + ", " + this._roundA + ")"; 137 | }, 138 | toPercentageRgb: function toPercentageRgb() { 139 | return { 140 | r: Math.round(bound01(this._r, 255) * 100) + "%", 141 | g: Math.round(bound01(this._g, 255) * 100) + "%", 142 | b: Math.round(bound01(this._b, 255) * 100) + "%", 143 | a: this._a 144 | }; 145 | }, 146 | toPercentageRgbString: function toPercentageRgbString() { 147 | return this._a == 1 ? "rgb(" + Math.round(bound01(this._r, 255) * 100) + "%, " + Math.round(bound01(this._g, 255) * 100) + "%, " + Math.round(bound01(this._b, 255) * 100) + "%)" : "rgba(" + Math.round(bound01(this._r, 255) * 100) + "%, " + Math.round(bound01(this._g, 255) * 100) + "%, " + Math.round(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")"; 148 | }, 149 | toName: function toName() { 150 | if (this._a === 0) { 151 | return "transparent"; 152 | } 153 | if (this._a < 1) { 154 | return false; 155 | } 156 | return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false; 157 | }, 158 | toFilter: function toFilter(secondColor) { 159 | var hex8String = "#" + rgbaToArgbHex(this._r, this._g, this._b, this._a); 160 | var secondHex8String = hex8String; 161 | var gradientType = this._gradientType ? "GradientType = 1, " : ""; 162 | if (secondColor) { 163 | var s = tinycolor(secondColor); 164 | secondHex8String = "#" + rgbaToArgbHex(s._r, s._g, s._b, s._a); 165 | } 166 | return "progid:DXImageTransform.Microsoft.gradient(" + gradientType + "startColorstr=" + hex8String + ",endColorstr=" + secondHex8String + ")"; 167 | }, 168 | toString: function toString(format) { 169 | var formatSet = !!format; 170 | format = format || this._format; 171 | var formattedString = false; 172 | var hasAlpha = this._a < 1 && this._a >= 0; 173 | var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "hex4" || format === "hex8" || format === "name"); 174 | if (needsAlphaFormat) { 175 | // Special case for "transparent", all other non-alpha formats 176 | // will return rgba when there is transparency. 177 | if (format === "name" && this._a === 0) { 178 | return this.toName(); 179 | } 180 | return this.toRgbString(); 181 | } 182 | if (format === "rgb") { 183 | formattedString = this.toRgbString(); 184 | } 185 | if (format === "prgb") { 186 | formattedString = this.toPercentageRgbString(); 187 | } 188 | if (format === "hex" || format === "hex6") { 189 | formattedString = this.toHexString(); 190 | } 191 | if (format === "hex3") { 192 | formattedString = this.toHexString(true); 193 | } 194 | if (format === "hex4") { 195 | formattedString = this.toHex8String(true); 196 | } 197 | if (format === "hex8") { 198 | formattedString = this.toHex8String(); 199 | } 200 | if (format === "name") { 201 | formattedString = this.toName(); 202 | } 203 | if (format === "hsl") { 204 | formattedString = this.toHslString(); 205 | } 206 | if (format === "hsv") { 207 | formattedString = this.toHsvString(); 208 | } 209 | return formattedString || this.toHexString(); 210 | }, 211 | clone: function clone() { 212 | return tinycolor(this.toString()); 213 | }, 214 | _applyModification: function _applyModification(fn, args) { 215 | var color = fn.apply(null, [this].concat([].slice.call(args))); 216 | this._r = color._r; 217 | this._g = color._g; 218 | this._b = color._b; 219 | this.setAlpha(color._a); 220 | return this; 221 | }, 222 | lighten: function lighten() { 223 | return this._applyModification(_lighten, arguments); 224 | }, 225 | brighten: function brighten() { 226 | return this._applyModification(_brighten, arguments); 227 | }, 228 | darken: function darken() { 229 | return this._applyModification(_darken, arguments); 230 | }, 231 | desaturate: function desaturate() { 232 | return this._applyModification(_desaturate, arguments); 233 | }, 234 | saturate: function saturate() { 235 | return this._applyModification(_saturate, arguments); 236 | }, 237 | greyscale: function greyscale() { 238 | return this._applyModification(_greyscale, arguments); 239 | }, 240 | spin: function spin() { 241 | return this._applyModification(_spin, arguments); 242 | }, 243 | _applyCombination: function _applyCombination(fn, args) { 244 | return fn.apply(null, [this].concat([].slice.call(args))); 245 | }, 246 | analogous: function analogous() { 247 | return this._applyCombination(_analogous, arguments); 248 | }, 249 | complement: function complement() { 250 | return this._applyCombination(_complement, arguments); 251 | }, 252 | monochromatic: function monochromatic() { 253 | return this._applyCombination(_monochromatic, arguments); 254 | }, 255 | splitcomplement: function splitcomplement() { 256 | return this._applyCombination(_splitcomplement, arguments); 257 | }, 258 | // Disabled until https://github.com/bgrins/TinyColor/issues/254 259 | // polyad: function (number) { 260 | // return this._applyCombination(polyad, [number]); 261 | // }, 262 | triad: function triad() { 263 | return this._applyCombination(polyad, [3]); 264 | }, 265 | tetrad: function tetrad() { 266 | return this._applyCombination(polyad, [4]); 267 | } 268 | }; 269 | 270 | // If input is an object, force 1 into "1.0" to handle ratios properly 271 | // String input requires "1.0" as input, so 1 will be treated as 1 272 | tinycolor.fromRatio = function (color, opts) { 273 | if (_typeof(color) == "object") { 274 | var newColor = {}; 275 | for (var i in color) { 276 | if (color.hasOwnProperty(i)) { 277 | if (i === "a") { 278 | newColor[i] = color[i]; 279 | } else { 280 | newColor[i] = convertToPercentage(color[i]); 281 | } 282 | } 283 | } 284 | color = newColor; 285 | } 286 | return tinycolor(color, opts); 287 | }; 288 | 289 | // Given a string or object, convert that input to RGB 290 | // Possible string inputs: 291 | // 292 | // "red" 293 | // "#f00" or "f00" 294 | // "#ff0000" or "ff0000" 295 | // "#ff000000" or "ff000000" 296 | // "rgb 255 0 0" or "rgb (255, 0, 0)" 297 | // "rgb 1.0 0 0" or "rgb (1, 0, 0)" 298 | // "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1" 299 | // "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1" 300 | // "hsl(0, 100%, 50%)" or "hsl 0 100% 50%" 301 | // "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1" 302 | // "hsv(0, 100%, 100%)" or "hsv 0 100% 100%" 303 | // 304 | function inputToRGB(color) { 305 | var rgb = { 306 | r: 0, 307 | g: 0, 308 | b: 0 309 | }; 310 | var a = 1; 311 | var s = null; 312 | var v = null; 313 | var l = null; 314 | var ok = false; 315 | var format = false; 316 | if (typeof color == "string") { 317 | color = stringInputToObject(color); 318 | } 319 | if (_typeof(color) == "object") { 320 | if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) { 321 | rgb = rgbToRgb(color.r, color.g, color.b); 322 | ok = true; 323 | format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb"; 324 | } else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) { 325 | s = convertToPercentage(color.s); 326 | v = convertToPercentage(color.v); 327 | rgb = hsvToRgb(color.h, s, v); 328 | ok = true; 329 | format = "hsv"; 330 | } else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) { 331 | s = convertToPercentage(color.s); 332 | l = convertToPercentage(color.l); 333 | rgb = hslToRgb(color.h, s, l); 334 | ok = true; 335 | format = "hsl"; 336 | } 337 | if (color.hasOwnProperty("a")) { 338 | a = color.a; 339 | } 340 | } 341 | a = boundAlpha(a); 342 | return { 343 | ok: ok, 344 | format: color.format || format, 345 | r: Math.min(255, Math.max(rgb.r, 0)), 346 | g: Math.min(255, Math.max(rgb.g, 0)), 347 | b: Math.min(255, Math.max(rgb.b, 0)), 348 | a: a 349 | }; 350 | } 351 | 352 | // Conversion Functions 353 | // -------------------- 354 | 355 | // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from: 356 | // 357 | 358 | // `rgbToRgb` 359 | // Handle bounds / percentage checking to conform to CSS color spec 360 | // 361 | // *Assumes:* r, g, b in [0, 255] or [0, 1] 362 | // *Returns:* { r, g, b } in [0, 255] 363 | function rgbToRgb(r, g, b) { 364 | return { 365 | r: bound01(r, 255) * 255, 366 | g: bound01(g, 255) * 255, 367 | b: bound01(b, 255) * 255 368 | }; 369 | } 370 | 371 | // `rgbToHsl` 372 | // Converts an RGB color value to HSL. 373 | // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1] 374 | // *Returns:* { h, s, l } in [0,1] 375 | function rgbToHsl(r, g, b) { 376 | r = bound01(r, 255); 377 | g = bound01(g, 255); 378 | b = bound01(b, 255); 379 | var max = Math.max(r, g, b), 380 | min = Math.min(r, g, b); 381 | var h, 382 | s, 383 | l = (max + min) / 2; 384 | if (max == min) { 385 | h = s = 0; // achromatic 386 | } else { 387 | var d = max - min; 388 | s = l > 0.5 ? d / (2 - max - min) : d / (max + min); 389 | switch (max) { 390 | case r: 391 | h = (g - b) / d + (g < b ? 6 : 0); 392 | break; 393 | case g: 394 | h = (b - r) / d + 2; 395 | break; 396 | case b: 397 | h = (r - g) / d + 4; 398 | break; 399 | } 400 | h /= 6; 401 | } 402 | return { 403 | h: h, 404 | s: s, 405 | l: l 406 | }; 407 | } 408 | 409 | // `hslToRgb` 410 | // Converts an HSL color value to RGB. 411 | // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100] 412 | // *Returns:* { r, g, b } in the set [0, 255] 413 | function hslToRgb(h, s, l) { 414 | var r, g, b; 415 | h = bound01(h, 360); 416 | s = bound01(s, 100); 417 | l = bound01(l, 100); 418 | function hue2rgb(p, q, t) { 419 | if (t < 0) t += 1; 420 | if (t > 1) t -= 1; 421 | if (t < 1 / 6) return p + (q - p) * 6 * t; 422 | if (t < 1 / 2) return q; 423 | if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; 424 | return p; 425 | } 426 | if (s === 0) { 427 | r = g = b = l; // achromatic 428 | } else { 429 | var q = l < 0.5 ? l * (1 + s) : l + s - l * s; 430 | var p = 2 * l - q; 431 | r = hue2rgb(p, q, h + 1 / 3); 432 | g = hue2rgb(p, q, h); 433 | b = hue2rgb(p, q, h - 1 / 3); 434 | } 435 | return { 436 | r: r * 255, 437 | g: g * 255, 438 | b: b * 255 439 | }; 440 | } 441 | 442 | // `rgbToHsv` 443 | // Converts an RGB color value to HSV 444 | // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1] 445 | // *Returns:* { h, s, v } in [0,1] 446 | function rgbToHsv(r, g, b) { 447 | r = bound01(r, 255); 448 | g = bound01(g, 255); 449 | b = bound01(b, 255); 450 | var max = Math.max(r, g, b), 451 | min = Math.min(r, g, b); 452 | var h, 453 | s, 454 | v = max; 455 | var d = max - min; 456 | s = max === 0 ? 0 : d / max; 457 | if (max == min) { 458 | h = 0; // achromatic 459 | } else { 460 | switch (max) { 461 | case r: 462 | h = (g - b) / d + (g < b ? 6 : 0); 463 | break; 464 | case g: 465 | h = (b - r) / d + 2; 466 | break; 467 | case b: 468 | h = (r - g) / d + 4; 469 | break; 470 | } 471 | h /= 6; 472 | } 473 | return { 474 | h: h, 475 | s: s, 476 | v: v 477 | }; 478 | } 479 | 480 | // `hsvToRgb` 481 | // Converts an HSV color value to RGB. 482 | // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100] 483 | // *Returns:* { r, g, b } in the set [0, 255] 484 | function hsvToRgb(h, s, v) { 485 | h = bound01(h, 360) * 6; 486 | s = bound01(s, 100); 487 | v = bound01(v, 100); 488 | var i = Math.floor(h), 489 | f = h - i, 490 | p = v * (1 - s), 491 | q = v * (1 - f * s), 492 | t = v * (1 - (1 - f) * s), 493 | mod = i % 6, 494 | r = [v, q, p, p, t, v][mod], 495 | g = [t, v, v, q, p, p][mod], 496 | b = [p, p, t, v, v, q][mod]; 497 | return { 498 | r: r * 255, 499 | g: g * 255, 500 | b: b * 255 501 | }; 502 | } 503 | 504 | // `rgbToHex` 505 | // Converts an RGB color to hex 506 | // Assumes r, g, and b are contained in the set [0, 255] 507 | // Returns a 3 or 6 character hex 508 | function rgbToHex(r, g, b, allow3Char) { 509 | var hex = [pad2(Math.round(r).toString(16)), pad2(Math.round(g).toString(16)), pad2(Math.round(b).toString(16))]; 510 | 511 | // Return a 3 character hex if possible 512 | 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)) { 513 | return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0); 514 | } 515 | return hex.join(""); 516 | } 517 | 518 | // `rgbaToHex` 519 | // Converts an RGBA color plus alpha transparency to hex 520 | // Assumes r, g, b are contained in the set [0, 255] and 521 | // a in [0, 1]. Returns a 4 or 8 character rgba hex 522 | function rgbaToHex(r, g, b, a, allow4Char) { 523 | var hex = [pad2(Math.round(r).toString(16)), pad2(Math.round(g).toString(16)), pad2(Math.round(b).toString(16)), pad2(convertDecimalToHex(a))]; 524 | 525 | // Return a 4 character hex if possible 526 | 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)) { 527 | return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0); 528 | } 529 | return hex.join(""); 530 | } 531 | 532 | // `rgbaToArgbHex` 533 | // Converts an RGBA color to an ARGB Hex8 string 534 | // Rarely used, but required for "toFilter()" 535 | function rgbaToArgbHex(r, g, b, a) { 536 | var hex = [pad2(convertDecimalToHex(a)), pad2(Math.round(r).toString(16)), pad2(Math.round(g).toString(16)), pad2(Math.round(b).toString(16))]; 537 | return hex.join(""); 538 | } 539 | 540 | // `equals` 541 | // Can be called with any tinycolor input 542 | tinycolor.equals = function (color1, color2) { 543 | if (!color1 || !color2) return false; 544 | return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString(); 545 | }; 546 | tinycolor.random = function () { 547 | return tinycolor.fromRatio({ 548 | r: Math.random(), 549 | g: Math.random(), 550 | b: Math.random() 551 | }); 552 | }; 553 | 554 | // Modification Functions 555 | // ---------------------- 556 | // Thanks to less.js for some of the basics here 557 | // 558 | 559 | function _desaturate(color, amount) { 560 | amount = amount === 0 ? 0 : amount || 10; 561 | var hsl = tinycolor(color).toHsl(); 562 | hsl.s -= amount / 100; 563 | hsl.s = clamp01(hsl.s); 564 | return tinycolor(hsl); 565 | } 566 | function _saturate(color, amount) { 567 | amount = amount === 0 ? 0 : amount || 10; 568 | var hsl = tinycolor(color).toHsl(); 569 | hsl.s += amount / 100; 570 | hsl.s = clamp01(hsl.s); 571 | return tinycolor(hsl); 572 | } 573 | function _greyscale(color) { 574 | return tinycolor(color).desaturate(100); 575 | } 576 | function _lighten(color, amount) { 577 | amount = amount === 0 ? 0 : amount || 10; 578 | var hsl = tinycolor(color).toHsl(); 579 | hsl.l += amount / 100; 580 | hsl.l = clamp01(hsl.l); 581 | return tinycolor(hsl); 582 | } 583 | function _brighten(color, amount) { 584 | amount = amount === 0 ? 0 : amount || 10; 585 | var rgb = tinycolor(color).toRgb(); 586 | rgb.r = Math.max(0, Math.min(255, rgb.r - Math.round(255 * -(amount / 100)))); 587 | rgb.g = Math.max(0, Math.min(255, rgb.g - Math.round(255 * -(amount / 100)))); 588 | rgb.b = Math.max(0, Math.min(255, rgb.b - Math.round(255 * -(amount / 100)))); 589 | return tinycolor(rgb); 590 | } 591 | function _darken(color, amount) { 592 | amount = amount === 0 ? 0 : amount || 10; 593 | var hsl = tinycolor(color).toHsl(); 594 | hsl.l -= amount / 100; 595 | hsl.l = clamp01(hsl.l); 596 | return tinycolor(hsl); 597 | } 598 | 599 | // Spin takes a positive or negative amount within [-360, 360] indicating the change of hue. 600 | // Values outside of this range will be wrapped into this range. 601 | function _spin(color, amount) { 602 | var hsl = tinycolor(color).toHsl(); 603 | var hue = (hsl.h + amount) % 360; 604 | hsl.h = hue < 0 ? 360 + hue : hue; 605 | return tinycolor(hsl); 606 | } 607 | 608 | // Combination Functions 609 | // --------------------- 610 | // Thanks to jQuery xColor for some of the ideas behind these 611 | // 612 | 613 | function _complement(color) { 614 | var hsl = tinycolor(color).toHsl(); 615 | hsl.h = (hsl.h + 180) % 360; 616 | return tinycolor(hsl); 617 | } 618 | function polyad(color, number) { 619 | if (isNaN(number) || number <= 0) { 620 | throw new Error("Argument to polyad must be a positive number"); 621 | } 622 | var hsl = tinycolor(color).toHsl(); 623 | var result = [tinycolor(color)]; 624 | var step = 360 / number; 625 | for (var i = 1; i < number; i++) { 626 | result.push(tinycolor({ 627 | h: (hsl.h + i * step) % 360, 628 | s: hsl.s, 629 | l: hsl.l 630 | })); 631 | } 632 | return result; 633 | } 634 | function _splitcomplement(color) { 635 | var hsl = tinycolor(color).toHsl(); 636 | var h = hsl.h; 637 | return [tinycolor(color), tinycolor({ 638 | h: (h + 72) % 360, 639 | s: hsl.s, 640 | l: hsl.l 641 | }), tinycolor({ 642 | h: (h + 216) % 360, 643 | s: hsl.s, 644 | l: hsl.l 645 | })]; 646 | } 647 | function _analogous(color, results, slices) { 648 | results = results || 6; 649 | slices = slices || 30; 650 | var hsl = tinycolor(color).toHsl(); 651 | var part = 360 / slices; 652 | var ret = [tinycolor(color)]; 653 | for (hsl.h = (hsl.h - (part * results >> 1) + 720) % 360; --results;) { 654 | hsl.h = (hsl.h + part) % 360; 655 | ret.push(tinycolor(hsl)); 656 | } 657 | return ret; 658 | } 659 | function _monochromatic(color, results) { 660 | results = results || 6; 661 | var hsv = tinycolor(color).toHsv(); 662 | var h = hsv.h, 663 | s = hsv.s, 664 | v = hsv.v; 665 | var ret = []; 666 | var modification = 1 / results; 667 | while (results--) { 668 | ret.push(tinycolor({ 669 | h: h, 670 | s: s, 671 | v: v 672 | })); 673 | v = (v + modification) % 1; 674 | } 675 | return ret; 676 | } 677 | 678 | // Utility Functions 679 | // --------------------- 680 | 681 | tinycolor.mix = function (color1, color2, amount) { 682 | amount = amount === 0 ? 0 : amount || 50; 683 | var rgb1 = tinycolor(color1).toRgb(); 684 | var rgb2 = tinycolor(color2).toRgb(); 685 | var p = amount / 100; 686 | var rgba = { 687 | r: (rgb2.r - rgb1.r) * p + rgb1.r, 688 | g: (rgb2.g - rgb1.g) * p + rgb1.g, 689 | b: (rgb2.b - rgb1.b) * p + rgb1.b, 690 | a: (rgb2.a - rgb1.a) * p + rgb1.a 691 | }; 692 | return tinycolor(rgba); 693 | }; 694 | 695 | // Readability Functions 696 | // --------------------- 697 | // false 716 | // tinycolor.isReadable("#000", "#111",{level:"AA",size:"large"}) => false 717 | tinycolor.isReadable = function (color1, color2, wcag2) { 718 | var readability = tinycolor.readability(color1, color2); 719 | var wcag2Parms, out; 720 | out = false; 721 | wcag2Parms = validateWCAG2Parms(wcag2); 722 | switch (wcag2Parms.level + wcag2Parms.size) { 723 | case "AAsmall": 724 | case "AAAlarge": 725 | out = readability >= 4.5; 726 | break; 727 | case "AAlarge": 728 | out = readability >= 3; 729 | break; 730 | case "AAAsmall": 731 | out = readability >= 7; 732 | break; 733 | } 734 | return out; 735 | }; 736 | 737 | // `mostReadable` 738 | // Given a base color and a list of possible foreground or background 739 | // colors for that base, returns the most readable color. 740 | // Optionally returns Black or White if the most readable color is unreadable. 741 | // *Example* 742 | // tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:false}).toHexString(); // "#112255" 743 | // tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:true}).toHexString(); // "#ffffff" 744 | // tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"large"}).toHexString(); // "#faf3f3" 745 | // tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"small"}).toHexString(); // "#ffffff" 746 | tinycolor.mostReadable = function (baseColor, colorList, args) { 747 | var bestColor = null; 748 | var bestScore = 0; 749 | var readability; 750 | var includeFallbackColors, level, size; 751 | args = args || {}; 752 | includeFallbackColors = args.includeFallbackColors; 753 | level = args.level; 754 | size = args.size; 755 | for (var i = 0; i < colorList.length; i++) { 756 | readability = tinycolor.readability(baseColor, colorList[i]); 757 | if (readability > bestScore) { 758 | bestScore = readability; 759 | bestColor = tinycolor(colorList[i]); 760 | } 761 | } 762 | if (tinycolor.isReadable(baseColor, bestColor, { 763 | level: level, 764 | size: size 765 | }) || !includeFallbackColors) { 766 | return bestColor; 767 | } else { 768 | args.includeFallbackColors = false; 769 | return tinycolor.mostReadable(baseColor, ["#fff", "#000"], args); 770 | } 771 | }; 772 | 773 | // Big List of Colors 774 | // ------------------ 775 | // 776 | var names = tinycolor.names = { 777 | aliceblue: "f0f8ff", 778 | antiquewhite: "faebd7", 779 | aqua: "0ff", 780 | aquamarine: "7fffd4", 781 | azure: "f0ffff", 782 | beige: "f5f5dc", 783 | bisque: "ffe4c4", 784 | black: "000", 785 | blanchedalmond: "ffebcd", 786 | blue: "00f", 787 | blueviolet: "8a2be2", 788 | brown: "a52a2a", 789 | burlywood: "deb887", 790 | burntsienna: "ea7e5d", 791 | cadetblue: "5f9ea0", 792 | chartreuse: "7fff00", 793 | chocolate: "d2691e", 794 | coral: "ff7f50", 795 | cornflowerblue: "6495ed", 796 | cornsilk: "fff8dc", 797 | crimson: "dc143c", 798 | cyan: "0ff", 799 | darkblue: "00008b", 800 | darkcyan: "008b8b", 801 | darkgoldenrod: "b8860b", 802 | darkgray: "a9a9a9", 803 | darkgreen: "006400", 804 | darkgrey: "a9a9a9", 805 | darkkhaki: "bdb76b", 806 | darkmagenta: "8b008b", 807 | darkolivegreen: "556b2f", 808 | darkorange: "ff8c00", 809 | darkorchid: "9932cc", 810 | darkred: "8b0000", 811 | darksalmon: "e9967a", 812 | darkseagreen: "8fbc8f", 813 | darkslateblue: "483d8b", 814 | darkslategray: "2f4f4f", 815 | darkslategrey: "2f4f4f", 816 | darkturquoise: "00ced1", 817 | darkviolet: "9400d3", 818 | deeppink: "ff1493", 819 | deepskyblue: "00bfff", 820 | dimgray: "696969", 821 | dimgrey: "696969", 822 | dodgerblue: "1e90ff", 823 | firebrick: "b22222", 824 | floralwhite: "fffaf0", 825 | forestgreen: "228b22", 826 | fuchsia: "f0f", 827 | gainsboro: "dcdcdc", 828 | ghostwhite: "f8f8ff", 829 | gold: "ffd700", 830 | goldenrod: "daa520", 831 | gray: "808080", 832 | green: "008000", 833 | greenyellow: "adff2f", 834 | grey: "808080", 835 | honeydew: "f0fff0", 836 | hotpink: "ff69b4", 837 | indianred: "cd5c5c", 838 | indigo: "4b0082", 839 | ivory: "fffff0", 840 | khaki: "f0e68c", 841 | lavender: "e6e6fa", 842 | lavenderblush: "fff0f5", 843 | lawngreen: "7cfc00", 844 | lemonchiffon: "fffacd", 845 | lightblue: "add8e6", 846 | lightcoral: "f08080", 847 | lightcyan: "e0ffff", 848 | lightgoldenrodyellow: "fafad2", 849 | lightgray: "d3d3d3", 850 | lightgreen: "90ee90", 851 | lightgrey: "d3d3d3", 852 | lightpink: "ffb6c1", 853 | lightsalmon: "ffa07a", 854 | lightseagreen: "20b2aa", 855 | lightskyblue: "87cefa", 856 | lightslategray: "789", 857 | lightslategrey: "789", 858 | lightsteelblue: "b0c4de", 859 | lightyellow: "ffffe0", 860 | lime: "0f0", 861 | limegreen: "32cd32", 862 | linen: "faf0e6", 863 | magenta: "f0f", 864 | maroon: "800000", 865 | mediumaquamarine: "66cdaa", 866 | mediumblue: "0000cd", 867 | mediumorchid: "ba55d3", 868 | mediumpurple: "9370db", 869 | mediumseagreen: "3cb371", 870 | mediumslateblue: "7b68ee", 871 | mediumspringgreen: "00fa9a", 872 | mediumturquoise: "48d1cc", 873 | mediumvioletred: "c71585", 874 | midnightblue: "191970", 875 | mintcream: "f5fffa", 876 | mistyrose: "ffe4e1", 877 | moccasin: "ffe4b5", 878 | navajowhite: "ffdead", 879 | navy: "000080", 880 | oldlace: "fdf5e6", 881 | olive: "808000", 882 | olivedrab: "6b8e23", 883 | orange: "ffa500", 884 | orangered: "ff4500", 885 | orchid: "da70d6", 886 | palegoldenrod: "eee8aa", 887 | palegreen: "98fb98", 888 | paleturquoise: "afeeee", 889 | palevioletred: "db7093", 890 | papayawhip: "ffefd5", 891 | peachpuff: "ffdab9", 892 | peru: "cd853f", 893 | pink: "ffc0cb", 894 | plum: "dda0dd", 895 | powderblue: "b0e0e6", 896 | purple: "800080", 897 | rebeccapurple: "663399", 898 | red: "f00", 899 | rosybrown: "bc8f8f", 900 | royalblue: "4169e1", 901 | saddlebrown: "8b4513", 902 | salmon: "fa8072", 903 | sandybrown: "f4a460", 904 | seagreen: "2e8b57", 905 | seashell: "fff5ee", 906 | sienna: "a0522d", 907 | silver: "c0c0c0", 908 | skyblue: "87ceeb", 909 | slateblue: "6a5acd", 910 | slategray: "708090", 911 | slategrey: "708090", 912 | snow: "fffafa", 913 | springgreen: "00ff7f", 914 | steelblue: "4682b4", 915 | tan: "d2b48c", 916 | teal: "008080", 917 | thistle: "d8bfd8", 918 | tomato: "ff6347", 919 | turquoise: "40e0d0", 920 | violet: "ee82ee", 921 | wheat: "f5deb3", 922 | white: "fff", 923 | whitesmoke: "f5f5f5", 924 | yellow: "ff0", 925 | yellowgreen: "9acd32" 926 | }; 927 | 928 | // Make it easy to access colors via `hexNames[hex]` 929 | var hexNames = tinycolor.hexNames = flip(names); 930 | 931 | // Utilities 932 | // --------- 933 | 934 | // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }` 935 | function flip(o) { 936 | var flipped = {}; 937 | for (var i in o) { 938 | if (o.hasOwnProperty(i)) { 939 | flipped[o[i]] = i; 940 | } 941 | } 942 | return flipped; 943 | } 944 | 945 | // Return a valid alpha value [0,1] with all invalid values being set to 1 946 | function boundAlpha(a) { 947 | a = parseFloat(a); 948 | if (isNaN(a) || a < 0 || a > 1) { 949 | a = 1; 950 | } 951 | return a; 952 | } 953 | 954 | // Take input from [0, n] and return it as [0, 1] 955 | function bound01(n, max) { 956 | if (isOnePointZero(n)) n = "100%"; 957 | var processPercent = isPercentage(n); 958 | n = Math.min(max, Math.max(0, parseFloat(n))); 959 | 960 | // Automatically convert percentage into number 961 | if (processPercent) { 962 | n = parseInt(n * max, 10) / 100; 963 | } 964 | 965 | // Handle floating point rounding errors 966 | if (Math.abs(n - max) < 0.000001) { 967 | return 1; 968 | } 969 | 970 | // Convert into [0, 1] range if it isn't already 971 | return n % max / parseFloat(max); 972 | } 973 | 974 | // Force a number between 0 and 1 975 | function clamp01(val) { 976 | return Math.min(1, Math.max(0, val)); 977 | } 978 | 979 | // Parse a base-16 hex value into a base-10 integer 980 | function parseIntFromHex(val) { 981 | return parseInt(val, 16); 982 | } 983 | 984 | // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1 985 | // 986 | function isOnePointZero(n) { 987 | return typeof n == "string" && n.indexOf(".") != -1 && parseFloat(n) === 1; 988 | } 989 | 990 | // Check to see if string passed in is a percentage 991 | function isPercentage(n) { 992 | return typeof n === "string" && n.indexOf("%") != -1; 993 | } 994 | 995 | // Force a hex value to have 2 characters 996 | function pad2(c) { 997 | return c.length == 1 ? "0" + c : "" + c; 998 | } 999 | 1000 | // Replace a decimal with it's percentage value 1001 | function convertToPercentage(n) { 1002 | if (n <= 1) { 1003 | n = n * 100 + "%"; 1004 | } 1005 | return n; 1006 | } 1007 | 1008 | // Converts a decimal to a hex value 1009 | function convertDecimalToHex(d) { 1010 | return Math.round(parseFloat(d) * 255).toString(16); 1011 | } 1012 | // Converts a hex value to a decimal 1013 | function convertHexToDecimal(h) { 1014 | return parseIntFromHex(h) / 255; 1015 | } 1016 | var matchers = function () { 1017 | // 1018 | var CSS_INTEGER = "[-\\+]?\\d+%?"; 1019 | 1020 | // 1021 | var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?"; 1022 | 1023 | // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome. 1024 | var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")"; 1025 | 1026 | // Actual matching. 1027 | // Parentheses and commas are optional, but not required. 1028 | // Whitespace can take the place of commas or opening paren 1029 | var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; 1030 | var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; 1031 | return { 1032 | CSS_UNIT: new RegExp(CSS_UNIT), 1033 | rgb: new RegExp("rgb" + PERMISSIVE_MATCH3), 1034 | rgba: new RegExp("rgba" + PERMISSIVE_MATCH4), 1035 | hsl: new RegExp("hsl" + PERMISSIVE_MATCH3), 1036 | hsla: new RegExp("hsla" + PERMISSIVE_MATCH4), 1037 | hsv: new RegExp("hsv" + PERMISSIVE_MATCH3), 1038 | hsva: new RegExp("hsva" + PERMISSIVE_MATCH4), 1039 | hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, 1040 | hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/, 1041 | hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, 1042 | hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/ 1043 | }; 1044 | }(); 1045 | 1046 | // `isValidCSSUnit` 1047 | // Take in a single string / number and check to see if it looks like a CSS unit 1048 | // (see `matchers` above for definition). 1049 | function isValidCSSUnit(color) { 1050 | return !!matchers.CSS_UNIT.exec(color); 1051 | } 1052 | 1053 | // `stringInputToObject` 1054 | // Permissive string parsing. Take in a number of formats, and output an object 1055 | // based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}` 1056 | function stringInputToObject(color) { 1057 | color = color.replace(trimLeft, "").replace(trimRight, "").toLowerCase(); 1058 | var named = false; 1059 | if (names[color]) { 1060 | color = names[color]; 1061 | named = true; 1062 | } else if (color == "transparent") { 1063 | return { 1064 | r: 0, 1065 | g: 0, 1066 | b: 0, 1067 | a: 0, 1068 | format: "name" 1069 | }; 1070 | } 1071 | 1072 | // Try to match string input using regular expressions. 1073 | // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360] 1074 | // Just return an object and let the conversion functions handle that. 1075 | // This way the result will be the same whether the tinycolor is initialized with string or object. 1076 | var match; 1077 | if (match = matchers.rgb.exec(color)) { 1078 | return { 1079 | r: match[1], 1080 | g: match[2], 1081 | b: match[3] 1082 | }; 1083 | } 1084 | if (match = matchers.rgba.exec(color)) { 1085 | return { 1086 | r: match[1], 1087 | g: match[2], 1088 | b: match[3], 1089 | a: match[4] 1090 | }; 1091 | } 1092 | if (match = matchers.hsl.exec(color)) { 1093 | return { 1094 | h: match[1], 1095 | s: match[2], 1096 | l: match[3] 1097 | }; 1098 | } 1099 | if (match = matchers.hsla.exec(color)) { 1100 | return { 1101 | h: match[1], 1102 | s: match[2], 1103 | l: match[3], 1104 | a: match[4] 1105 | }; 1106 | } 1107 | if (match = matchers.hsv.exec(color)) { 1108 | return { 1109 | h: match[1], 1110 | s: match[2], 1111 | v: match[3] 1112 | }; 1113 | } 1114 | if (match = matchers.hsva.exec(color)) { 1115 | return { 1116 | h: match[1], 1117 | s: match[2], 1118 | v: match[3], 1119 | a: match[4] 1120 | }; 1121 | } 1122 | if (match = matchers.hex8.exec(color)) { 1123 | return { 1124 | r: parseIntFromHex(match[1]), 1125 | g: parseIntFromHex(match[2]), 1126 | b: parseIntFromHex(match[3]), 1127 | a: convertHexToDecimal(match[4]), 1128 | format: named ? "name" : "hex8" 1129 | }; 1130 | } 1131 | if (match = matchers.hex6.exec(color)) { 1132 | return { 1133 | r: parseIntFromHex(match[1]), 1134 | g: parseIntFromHex(match[2]), 1135 | b: parseIntFromHex(match[3]), 1136 | format: named ? "name" : "hex" 1137 | }; 1138 | } 1139 | if (match = matchers.hex4.exec(color)) { 1140 | return { 1141 | r: parseIntFromHex(match[1] + "" + match[1]), 1142 | g: parseIntFromHex(match[2] + "" + match[2]), 1143 | b: parseIntFromHex(match[3] + "" + match[3]), 1144 | a: convertHexToDecimal(match[4] + "" + match[4]), 1145 | format: named ? "name" : "hex8" 1146 | }; 1147 | } 1148 | if (match = matchers.hex3.exec(color)) { 1149 | return { 1150 | r: parseIntFromHex(match[1] + "" + match[1]), 1151 | g: parseIntFromHex(match[2] + "" + match[2]), 1152 | b: parseIntFromHex(match[3] + "" + match[3]), 1153 | format: named ? "name" : "hex" 1154 | }; 1155 | } 1156 | return false; 1157 | } 1158 | function validateWCAG2Parms(parms) { 1159 | // return valid WCAG2 parms for isReadable. 1160 | // If input parms are invalid, return {"level":"AA", "size":"small"} 1161 | var level, size; 1162 | parms = parms || { 1163 | level: "AA", 1164 | size: "small" 1165 | }; 1166 | level = (parms.level || "AA").toUpperCase(); 1167 | size = (parms.size || "small").toLowerCase(); 1168 | if (level !== "AA" && level !== "AAA") { 1169 | level = "AA"; 1170 | } 1171 | if (size !== "small" && size !== "large") { 1172 | size = "small"; 1173 | } 1174 | return { 1175 | level: level, 1176 | size: size 1177 | }; 1178 | } 1179 | 1180 | export { tinycolor as default }; 1181 | -------------------------------------------------------------------------------- /mod.js: -------------------------------------------------------------------------------- 1 | // https://github.com/bgrins/TinyColor 2 | // Brian Grinstead, MIT License 3 | 4 | const trimLeft = /^\s+/; 5 | const trimRight = /\s+$/; 6 | 7 | export default function tinycolor(color, opts) { 8 | color = color ? color : ""; 9 | opts = opts || {}; 10 | 11 | // If input is already a tinycolor, return itself 12 | if (color instanceof tinycolor) { 13 | return color; 14 | } 15 | // If we are called as a function, call using new instead 16 | if (!(this instanceof tinycolor)) { 17 | return new tinycolor(color, opts); 18 | } 19 | 20 | var rgb = inputToRGB(color); 21 | (this._originalInput = color), 22 | (this._r = rgb.r), 23 | (this._g = rgb.g), 24 | (this._b = rgb.b), 25 | (this._a = rgb.a), 26 | (this._roundA = Math.round(100 * this._a) / 100), 27 | (this._format = opts.format || rgb.format); 28 | this._gradientType = opts.gradientType; 29 | 30 | // Don't let the range of [0,255] come back in [0,1]. 31 | // Potentially lose a little bit of precision here, but will fix issues where 32 | // .5 gets interpreted as half of the total, instead of half of 1 33 | // If it was supposed to be 128, this was already taken care of by `inputToRgb` 34 | if (this._r < 1) this._r = Math.round(this._r); 35 | if (this._g < 1) this._g = Math.round(this._g); 36 | if (this._b < 1) this._b = Math.round(this._b); 37 | 38 | this._ok = rgb.ok; 39 | } 40 | 41 | tinycolor.prototype = { 42 | isDark: function () { 43 | return this.getBrightness() < 128; 44 | }, 45 | isLight: function () { 46 | return !this.isDark(); 47 | }, 48 | isValid: function () { 49 | return this._ok; 50 | }, 51 | getOriginalInput: function () { 52 | return this._originalInput; 53 | }, 54 | getFormat: function () { 55 | return this._format; 56 | }, 57 | getAlpha: function () { 58 | return this._a; 59 | }, 60 | getBrightness: function () { 61 | //http://www.w3.org/TR/AERT#color-contrast 62 | var rgb = this.toRgb(); 63 | return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000; 64 | }, 65 | getLuminance: function () { 66 | //http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef 67 | var rgb = this.toRgb(); 68 | var RsRGB, GsRGB, BsRGB, R, G, B; 69 | RsRGB = rgb.r / 255; 70 | GsRGB = rgb.g / 255; 71 | BsRGB = rgb.b / 255; 72 | 73 | if (RsRGB <= 0.03928) R = RsRGB / 12.92; 74 | else R = Math.pow((RsRGB + 0.055) / 1.055, 2.4); 75 | if (GsRGB <= 0.03928) G = GsRGB / 12.92; 76 | else G = Math.pow((GsRGB + 0.055) / 1.055, 2.4); 77 | if (BsRGB <= 0.03928) B = BsRGB / 12.92; 78 | else B = Math.pow((BsRGB + 0.055) / 1.055, 2.4); 79 | return 0.2126 * R + 0.7152 * G + 0.0722 * B; 80 | }, 81 | setAlpha: function (value) { 82 | this._a = boundAlpha(value); 83 | this._roundA = Math.round(100 * this._a) / 100; 84 | return this; 85 | }, 86 | toHsv: function () { 87 | var hsv = rgbToHsv(this._r, this._g, this._b); 88 | return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a }; 89 | }, 90 | toHsvString: function () { 91 | var hsv = rgbToHsv(this._r, this._g, this._b); 92 | var h = Math.round(hsv.h * 360), 93 | s = Math.round(hsv.s * 100), 94 | v = Math.round(hsv.v * 100); 95 | return this._a == 1 96 | ? "hsv(" + h + ", " + s + "%, " + v + "%)" 97 | : "hsva(" + h + ", " + s + "%, " + v + "%, " + this._roundA + ")"; 98 | }, 99 | toHsl: function () { 100 | var hsl = rgbToHsl(this._r, this._g, this._b); 101 | return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a }; 102 | }, 103 | toHslString: function () { 104 | var hsl = rgbToHsl(this._r, this._g, this._b); 105 | var h = Math.round(hsl.h * 360), 106 | s = Math.round(hsl.s * 100), 107 | l = Math.round(hsl.l * 100); 108 | return this._a == 1 109 | ? "hsl(" + h + ", " + s + "%, " + l + "%)" 110 | : "hsla(" + h + ", " + s + "%, " + l + "%, " + this._roundA + ")"; 111 | }, 112 | toHex: function (allow3Char) { 113 | return rgbToHex(this._r, this._g, this._b, allow3Char); 114 | }, 115 | toHexString: function (allow3Char) { 116 | return "#" + this.toHex(allow3Char); 117 | }, 118 | toHex8: function (allow4Char) { 119 | return rgbaToHex(this._r, this._g, this._b, this._a, allow4Char); 120 | }, 121 | toHex8String: function (allow4Char) { 122 | return "#" + this.toHex8(allow4Char); 123 | }, 124 | toRgb: function () { 125 | return { 126 | r: Math.round(this._r), 127 | g: Math.round(this._g), 128 | b: Math.round(this._b), 129 | a: this._a, 130 | }; 131 | }, 132 | toRgbString: function () { 133 | return this._a == 1 134 | ? "rgb(" + 135 | Math.round(this._r) + 136 | ", " + 137 | Math.round(this._g) + 138 | ", " + 139 | Math.round(this._b) + 140 | ")" 141 | : "rgba(" + 142 | Math.round(this._r) + 143 | ", " + 144 | Math.round(this._g) + 145 | ", " + 146 | Math.round(this._b) + 147 | ", " + 148 | this._roundA + 149 | ")"; 150 | }, 151 | toPercentageRgb: function () { 152 | return { 153 | r: Math.round(bound01(this._r, 255) * 100) + "%", 154 | g: Math.round(bound01(this._g, 255) * 100) + "%", 155 | b: Math.round(bound01(this._b, 255) * 100) + "%", 156 | a: this._a, 157 | }; 158 | }, 159 | toPercentageRgbString: function () { 160 | return this._a == 1 161 | ? "rgb(" + 162 | Math.round(bound01(this._r, 255) * 100) + 163 | "%, " + 164 | Math.round(bound01(this._g, 255) * 100) + 165 | "%, " + 166 | Math.round(bound01(this._b, 255) * 100) + 167 | "%)" 168 | : "rgba(" + 169 | Math.round(bound01(this._r, 255) * 100) + 170 | "%, " + 171 | Math.round(bound01(this._g, 255) * 100) + 172 | "%, " + 173 | Math.round(bound01(this._b, 255) * 100) + 174 | "%, " + 175 | this._roundA + 176 | ")"; 177 | }, 178 | toName: function () { 179 | if (this._a === 0) { 180 | return "transparent"; 181 | } 182 | 183 | if (this._a < 1) { 184 | return false; 185 | } 186 | 187 | return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false; 188 | }, 189 | toFilter: function (secondColor) { 190 | var hex8String = "#" + rgbaToArgbHex(this._r, this._g, this._b, this._a); 191 | var secondHex8String = hex8String; 192 | var gradientType = this._gradientType ? "GradientType = 1, " : ""; 193 | 194 | if (secondColor) { 195 | var s = tinycolor(secondColor); 196 | secondHex8String = "#" + rgbaToArgbHex(s._r, s._g, s._b, s._a); 197 | } 198 | 199 | return ( 200 | "progid:DXImageTransform.Microsoft.gradient(" + 201 | gradientType + 202 | "startColorstr=" + 203 | hex8String + 204 | ",endColorstr=" + 205 | secondHex8String + 206 | ")" 207 | ); 208 | }, 209 | toString: function (format) { 210 | var formatSet = !!format; 211 | format = format || this._format; 212 | 213 | var formattedString = false; 214 | var hasAlpha = this._a < 1 && this._a >= 0; 215 | var needsAlphaFormat = 216 | !formatSet && 217 | hasAlpha && 218 | (format === "hex" || 219 | format === "hex6" || 220 | format === "hex3" || 221 | format === "hex4" || 222 | format === "hex8" || 223 | format === "name"); 224 | 225 | if (needsAlphaFormat) { 226 | // Special case for "transparent", all other non-alpha formats 227 | // will return rgba when there is transparency. 228 | if (format === "name" && this._a === 0) { 229 | return this.toName(); 230 | } 231 | return this.toRgbString(); 232 | } 233 | if (format === "rgb") { 234 | formattedString = this.toRgbString(); 235 | } 236 | if (format === "prgb") { 237 | formattedString = this.toPercentageRgbString(); 238 | } 239 | if (format === "hex" || format === "hex6") { 240 | formattedString = this.toHexString(); 241 | } 242 | if (format === "hex3") { 243 | formattedString = this.toHexString(true); 244 | } 245 | if (format === "hex4") { 246 | formattedString = this.toHex8String(true); 247 | } 248 | if (format === "hex8") { 249 | formattedString = this.toHex8String(); 250 | } 251 | if (format === "name") { 252 | formattedString = this.toName(); 253 | } 254 | if (format === "hsl") { 255 | formattedString = this.toHslString(); 256 | } 257 | if (format === "hsv") { 258 | formattedString = this.toHsvString(); 259 | } 260 | 261 | return formattedString || this.toHexString(); 262 | }, 263 | clone: function () { 264 | return tinycolor(this.toString()); 265 | }, 266 | 267 | _applyModification: function (fn, args) { 268 | var color = fn.apply(null, [this].concat([].slice.call(args))); 269 | this._r = color._r; 270 | this._g = color._g; 271 | this._b = color._b; 272 | this.setAlpha(color._a); 273 | return this; 274 | }, 275 | lighten: function () { 276 | return this._applyModification(lighten, arguments); 277 | }, 278 | brighten: function () { 279 | return this._applyModification(brighten, arguments); 280 | }, 281 | darken: function () { 282 | return this._applyModification(darken, arguments); 283 | }, 284 | desaturate: function () { 285 | return this._applyModification(desaturate, arguments); 286 | }, 287 | saturate: function () { 288 | return this._applyModification(saturate, arguments); 289 | }, 290 | greyscale: function () { 291 | return this._applyModification(greyscale, arguments); 292 | }, 293 | spin: function () { 294 | return this._applyModification(spin, arguments); 295 | }, 296 | 297 | _applyCombination: function (fn, args) { 298 | return fn.apply(null, [this].concat([].slice.call(args))); 299 | }, 300 | analogous: function () { 301 | return this._applyCombination(analogous, arguments); 302 | }, 303 | complement: function () { 304 | return this._applyCombination(complement, arguments); 305 | }, 306 | monochromatic: function () { 307 | return this._applyCombination(monochromatic, arguments); 308 | }, 309 | splitcomplement: function () { 310 | return this._applyCombination(splitcomplement, arguments); 311 | }, 312 | // Disabled until https://github.com/bgrins/TinyColor/issues/254 313 | // polyad: function (number) { 314 | // return this._applyCombination(polyad, [number]); 315 | // }, 316 | triad: function () { 317 | return this._applyCombination(polyad, [3]); 318 | }, 319 | tetrad: function () { 320 | return this._applyCombination(polyad, [4]); 321 | }, 322 | }; 323 | 324 | // If input is an object, force 1 into "1.0" to handle ratios properly 325 | // String input requires "1.0" as input, so 1 will be treated as 1 326 | tinycolor.fromRatio = function (color, opts) { 327 | if (typeof color == "object") { 328 | var newColor = {}; 329 | for (var i in color) { 330 | if (color.hasOwnProperty(i)) { 331 | if (i === "a") { 332 | newColor[i] = color[i]; 333 | } else { 334 | newColor[i] = convertToPercentage(color[i]); 335 | } 336 | } 337 | } 338 | color = newColor; 339 | } 340 | 341 | return tinycolor(color, opts); 342 | }; 343 | 344 | // Given a string or object, convert that input to RGB 345 | // Possible string inputs: 346 | // 347 | // "red" 348 | // "#f00" or "f00" 349 | // "#ff0000" or "ff0000" 350 | // "#ff000000" or "ff000000" 351 | // "rgb 255 0 0" or "rgb (255, 0, 0)" 352 | // "rgb 1.0 0 0" or "rgb (1, 0, 0)" 353 | // "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1" 354 | // "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1" 355 | // "hsl(0, 100%, 50%)" or "hsl 0 100% 50%" 356 | // "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1" 357 | // "hsv(0, 100%, 100%)" or "hsv 0 100% 100%" 358 | // 359 | function inputToRGB(color) { 360 | var rgb = { r: 0, g: 0, b: 0 }; 361 | var a = 1; 362 | var s = null; 363 | var v = null; 364 | var l = null; 365 | var ok = false; 366 | var format = false; 367 | 368 | if (typeof color == "string") { 369 | color = stringInputToObject(color); 370 | } 371 | 372 | if (typeof color == "object") { 373 | if ( 374 | isValidCSSUnit(color.r) && 375 | isValidCSSUnit(color.g) && 376 | isValidCSSUnit(color.b) 377 | ) { 378 | rgb = rgbToRgb(color.r, color.g, color.b); 379 | ok = true; 380 | format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb"; 381 | } else if ( 382 | isValidCSSUnit(color.h) && 383 | isValidCSSUnit(color.s) && 384 | isValidCSSUnit(color.v) 385 | ) { 386 | s = convertToPercentage(color.s); 387 | v = convertToPercentage(color.v); 388 | rgb = hsvToRgb(color.h, s, v); 389 | ok = true; 390 | format = "hsv"; 391 | } else if ( 392 | isValidCSSUnit(color.h) && 393 | isValidCSSUnit(color.s) && 394 | isValidCSSUnit(color.l) 395 | ) { 396 | s = convertToPercentage(color.s); 397 | l = convertToPercentage(color.l); 398 | rgb = hslToRgb(color.h, s, l); 399 | ok = true; 400 | format = "hsl"; 401 | } 402 | 403 | if (color.hasOwnProperty("a")) { 404 | a = color.a; 405 | } 406 | } 407 | 408 | a = boundAlpha(a); 409 | 410 | return { 411 | ok: ok, 412 | format: color.format || format, 413 | r: Math.min(255, Math.max(rgb.r, 0)), 414 | g: Math.min(255, Math.max(rgb.g, 0)), 415 | b: Math.min(255, Math.max(rgb.b, 0)), 416 | a: a, 417 | }; 418 | } 419 | 420 | // Conversion Functions 421 | // -------------------- 422 | 423 | // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from: 424 | // 425 | 426 | // `rgbToRgb` 427 | // Handle bounds / percentage checking to conform to CSS color spec 428 | // 429 | // *Assumes:* r, g, b in [0, 255] or [0, 1] 430 | // *Returns:* { r, g, b } in [0, 255] 431 | function rgbToRgb(r, g, b) { 432 | return { 433 | r: bound01(r, 255) * 255, 434 | g: bound01(g, 255) * 255, 435 | b: bound01(b, 255) * 255, 436 | }; 437 | } 438 | 439 | // `rgbToHsl` 440 | // Converts an RGB color value to HSL. 441 | // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1] 442 | // *Returns:* { h, s, l } in [0,1] 443 | function rgbToHsl(r, g, b) { 444 | r = bound01(r, 255); 445 | g = bound01(g, 255); 446 | b = bound01(b, 255); 447 | 448 | var max = Math.max(r, g, b), 449 | min = Math.min(r, g, b); 450 | var h, 451 | s, 452 | l = (max + min) / 2; 453 | 454 | if (max == min) { 455 | h = s = 0; // achromatic 456 | } else { 457 | var d = max - min; 458 | s = l > 0.5 ? d / (2 - max - min) : d / (max + min); 459 | switch (max) { 460 | case r: 461 | h = (g - b) / d + (g < b ? 6 : 0); 462 | break; 463 | case g: 464 | h = (b - r) / d + 2; 465 | break; 466 | case b: 467 | h = (r - g) / d + 4; 468 | break; 469 | } 470 | 471 | h /= 6; 472 | } 473 | 474 | return { h: h, s: s, l: l }; 475 | } 476 | 477 | // `hslToRgb` 478 | // Converts an HSL color value to RGB. 479 | // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100] 480 | // *Returns:* { r, g, b } in the set [0, 255] 481 | function hslToRgb(h, s, l) { 482 | var r, g, b; 483 | 484 | h = bound01(h, 360); 485 | s = bound01(s, 100); 486 | l = bound01(l, 100); 487 | 488 | function hue2rgb(p, q, t) { 489 | if (t < 0) t += 1; 490 | if (t > 1) t -= 1; 491 | if (t < 1 / 6) return p + (q - p) * 6 * t; 492 | if (t < 1 / 2) return q; 493 | if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; 494 | return p; 495 | } 496 | 497 | if (s === 0) { 498 | r = g = b = l; // achromatic 499 | } else { 500 | var q = l < 0.5 ? l * (1 + s) : l + s - l * s; 501 | var p = 2 * l - q; 502 | r = hue2rgb(p, q, h + 1 / 3); 503 | g = hue2rgb(p, q, h); 504 | b = hue2rgb(p, q, h - 1 / 3); 505 | } 506 | 507 | return { r: r * 255, g: g * 255, b: b * 255 }; 508 | } 509 | 510 | // `rgbToHsv` 511 | // Converts an RGB color value to HSV 512 | // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1] 513 | // *Returns:* { h, s, v } in [0,1] 514 | function rgbToHsv(r, g, b) { 515 | r = bound01(r, 255); 516 | g = bound01(g, 255); 517 | b = bound01(b, 255); 518 | 519 | var max = Math.max(r, g, b), 520 | min = Math.min(r, g, b); 521 | var h, 522 | s, 523 | v = max; 524 | 525 | var d = max - min; 526 | s = max === 0 ? 0 : d / max; 527 | 528 | if (max == min) { 529 | h = 0; // achromatic 530 | } else { 531 | switch (max) { 532 | case r: 533 | h = (g - b) / d + (g < b ? 6 : 0); 534 | break; 535 | case g: 536 | h = (b - r) / d + 2; 537 | break; 538 | case b: 539 | h = (r - g) / d + 4; 540 | break; 541 | } 542 | h /= 6; 543 | } 544 | return { h: h, s: s, v: v }; 545 | } 546 | 547 | // `hsvToRgb` 548 | // Converts an HSV color value to RGB. 549 | // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100] 550 | // *Returns:* { r, g, b } in the set [0, 255] 551 | function hsvToRgb(h, s, v) { 552 | h = bound01(h, 360) * 6; 553 | s = bound01(s, 100); 554 | v = bound01(v, 100); 555 | 556 | var i = Math.floor(h), 557 | f = h - i, 558 | p = v * (1 - s), 559 | q = v * (1 - f * s), 560 | t = v * (1 - (1 - f) * s), 561 | mod = i % 6, 562 | r = [v, q, p, p, t, v][mod], 563 | g = [t, v, v, q, p, p][mod], 564 | b = [p, p, t, v, v, q][mod]; 565 | 566 | return { r: r * 255, g: g * 255, b: b * 255 }; 567 | } 568 | 569 | // `rgbToHex` 570 | // Converts an RGB color to hex 571 | // Assumes r, g, and b are contained in the set [0, 255] 572 | // Returns a 3 or 6 character hex 573 | function rgbToHex(r, g, b, allow3Char) { 574 | var hex = [ 575 | pad2(Math.round(r).toString(16)), 576 | pad2(Math.round(g).toString(16)), 577 | pad2(Math.round(b).toString(16)), 578 | ]; 579 | 580 | // Return a 3 character hex if possible 581 | if ( 582 | allow3Char && 583 | hex[0].charAt(0) == hex[0].charAt(1) && 584 | hex[1].charAt(0) == hex[1].charAt(1) && 585 | hex[2].charAt(0) == hex[2].charAt(1) 586 | ) { 587 | return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0); 588 | } 589 | 590 | return hex.join(""); 591 | } 592 | 593 | // `rgbaToHex` 594 | // Converts an RGBA color plus alpha transparency to hex 595 | // Assumes r, g, b are contained in the set [0, 255] and 596 | // a in [0, 1]. Returns a 4 or 8 character rgba hex 597 | function rgbaToHex(r, g, b, a, allow4Char) { 598 | var hex = [ 599 | pad2(Math.round(r).toString(16)), 600 | pad2(Math.round(g).toString(16)), 601 | pad2(Math.round(b).toString(16)), 602 | pad2(convertDecimalToHex(a)), 603 | ]; 604 | 605 | // Return a 4 character hex if possible 606 | if ( 607 | allow4Char && 608 | hex[0].charAt(0) == hex[0].charAt(1) && 609 | hex[1].charAt(0) == hex[1].charAt(1) && 610 | hex[2].charAt(0) == hex[2].charAt(1) && 611 | hex[3].charAt(0) == hex[3].charAt(1) 612 | ) { 613 | return ( 614 | hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0) 615 | ); 616 | } 617 | 618 | return hex.join(""); 619 | } 620 | 621 | // `rgbaToArgbHex` 622 | // Converts an RGBA color to an ARGB Hex8 string 623 | // Rarely used, but required for "toFilter()" 624 | function rgbaToArgbHex(r, g, b, a) { 625 | var hex = [ 626 | pad2(convertDecimalToHex(a)), 627 | pad2(Math.round(r).toString(16)), 628 | pad2(Math.round(g).toString(16)), 629 | pad2(Math.round(b).toString(16)), 630 | ]; 631 | 632 | return hex.join(""); 633 | } 634 | 635 | // `equals` 636 | // Can be called with any tinycolor input 637 | tinycolor.equals = function (color1, color2) { 638 | if (!color1 || !color2) return false; 639 | return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString(); 640 | }; 641 | 642 | tinycolor.random = function () { 643 | return tinycolor.fromRatio({ 644 | r: Math.random(), 645 | g: Math.random(), 646 | b: Math.random(), 647 | }); 648 | }; 649 | 650 | // Modification Functions 651 | // ---------------------- 652 | // Thanks to less.js for some of the basics here 653 | // 654 | 655 | function desaturate(color, amount) { 656 | amount = amount === 0 ? 0 : amount || 10; 657 | var hsl = tinycolor(color).toHsl(); 658 | hsl.s -= amount / 100; 659 | hsl.s = clamp01(hsl.s); 660 | return tinycolor(hsl); 661 | } 662 | 663 | function saturate(color, amount) { 664 | amount = amount === 0 ? 0 : amount || 10; 665 | var hsl = tinycolor(color).toHsl(); 666 | hsl.s += amount / 100; 667 | hsl.s = clamp01(hsl.s); 668 | return tinycolor(hsl); 669 | } 670 | 671 | function greyscale(color) { 672 | return tinycolor(color).desaturate(100); 673 | } 674 | 675 | function lighten(color, amount) { 676 | amount = amount === 0 ? 0 : amount || 10; 677 | var hsl = tinycolor(color).toHsl(); 678 | hsl.l += amount / 100; 679 | hsl.l = clamp01(hsl.l); 680 | return tinycolor(hsl); 681 | } 682 | 683 | function brighten(color, amount) { 684 | amount = amount === 0 ? 0 : amount || 10; 685 | var rgb = tinycolor(color).toRgb(); 686 | rgb.r = Math.max(0, Math.min(255, rgb.r - Math.round(255 * -(amount / 100)))); 687 | rgb.g = Math.max(0, Math.min(255, rgb.g - Math.round(255 * -(amount / 100)))); 688 | rgb.b = Math.max(0, Math.min(255, rgb.b - Math.round(255 * -(amount / 100)))); 689 | return tinycolor(rgb); 690 | } 691 | 692 | function darken(color, amount) { 693 | amount = amount === 0 ? 0 : amount || 10; 694 | var hsl = tinycolor(color).toHsl(); 695 | hsl.l -= amount / 100; 696 | hsl.l = clamp01(hsl.l); 697 | return tinycolor(hsl); 698 | } 699 | 700 | // Spin takes a positive or negative amount within [-360, 360] indicating the change of hue. 701 | // Values outside of this range will be wrapped into this range. 702 | function spin(color, amount) { 703 | var hsl = tinycolor(color).toHsl(); 704 | var hue = (hsl.h + amount) % 360; 705 | hsl.h = hue < 0 ? 360 + hue : hue; 706 | return tinycolor(hsl); 707 | } 708 | 709 | // Combination Functions 710 | // --------------------- 711 | // Thanks to jQuery xColor for some of the ideas behind these 712 | // 713 | 714 | function complement(color) { 715 | var hsl = tinycolor(color).toHsl(); 716 | hsl.h = (hsl.h + 180) % 360; 717 | return tinycolor(hsl); 718 | } 719 | 720 | function polyad(color, number) { 721 | if (isNaN(number) || number <= 0) { 722 | throw new Error("Argument to polyad must be a positive number"); 723 | } 724 | var hsl = tinycolor(color).toHsl(); 725 | var result = [tinycolor(color)]; 726 | var step = 360 / number; 727 | for (var i = 1; i < number; i++) { 728 | result.push(tinycolor({ h: (hsl.h + i * step) % 360, s: hsl.s, l: hsl.l })); 729 | } 730 | 731 | return result; 732 | } 733 | 734 | function splitcomplement(color) { 735 | var hsl = tinycolor(color).toHsl(); 736 | var h = hsl.h; 737 | return [ 738 | tinycolor(color), 739 | tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l }), 740 | tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l }), 741 | ]; 742 | } 743 | 744 | function analogous(color, results, slices) { 745 | results = results || 6; 746 | slices = slices || 30; 747 | 748 | var hsl = tinycolor(color).toHsl(); 749 | var part = 360 / slices; 750 | var ret = [tinycolor(color)]; 751 | 752 | for (hsl.h = (hsl.h - ((part * results) >> 1) + 720) % 360; --results; ) { 753 | hsl.h = (hsl.h + part) % 360; 754 | ret.push(tinycolor(hsl)); 755 | } 756 | return ret; 757 | } 758 | 759 | function monochromatic(color, results) { 760 | results = results || 6; 761 | var hsv = tinycolor(color).toHsv(); 762 | var h = hsv.h, 763 | s = hsv.s, 764 | v = hsv.v; 765 | var ret = []; 766 | var modification = 1 / results; 767 | 768 | while (results--) { 769 | ret.push(tinycolor({ h: h, s: s, v: v })); 770 | v = (v + modification) % 1; 771 | } 772 | 773 | return ret; 774 | } 775 | 776 | // Utility Functions 777 | // --------------------- 778 | 779 | tinycolor.mix = function (color1, color2, amount) { 780 | amount = amount === 0 ? 0 : amount || 50; 781 | 782 | var rgb1 = tinycolor(color1).toRgb(); 783 | var rgb2 = tinycolor(color2).toRgb(); 784 | 785 | var p = amount / 100; 786 | 787 | var rgba = { 788 | r: (rgb2.r - rgb1.r) * p + rgb1.r, 789 | g: (rgb2.g - rgb1.g) * p + rgb1.g, 790 | b: (rgb2.b - rgb1.b) * p + rgb1.b, 791 | a: (rgb2.a - rgb1.a) * p + rgb1.a, 792 | }; 793 | 794 | return tinycolor(rgba); 795 | }; 796 | 797 | // Readability Functions 798 | // --------------------- 799 | // false 821 | // tinycolor.isReadable("#000", "#111",{level:"AA",size:"large"}) => false 822 | tinycolor.isReadable = function (color1, color2, wcag2) { 823 | var readability = tinycolor.readability(color1, color2); 824 | var wcag2Parms, out; 825 | 826 | out = false; 827 | 828 | wcag2Parms = validateWCAG2Parms(wcag2); 829 | switch (wcag2Parms.level + wcag2Parms.size) { 830 | case "AAsmall": 831 | case "AAAlarge": 832 | out = readability >= 4.5; 833 | break; 834 | case "AAlarge": 835 | out = readability >= 3; 836 | break; 837 | case "AAAsmall": 838 | out = readability >= 7; 839 | break; 840 | } 841 | return out; 842 | }; 843 | 844 | // `mostReadable` 845 | // Given a base color and a list of possible foreground or background 846 | // colors for that base, returns the most readable color. 847 | // Optionally returns Black or White if the most readable color is unreadable. 848 | // *Example* 849 | // tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:false}).toHexString(); // "#112255" 850 | // tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:true}).toHexString(); // "#ffffff" 851 | // tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"large"}).toHexString(); // "#faf3f3" 852 | // tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"small"}).toHexString(); // "#ffffff" 853 | tinycolor.mostReadable = function (baseColor, colorList, args) { 854 | var bestColor = null; 855 | var bestScore = 0; 856 | var readability; 857 | var includeFallbackColors, level, size; 858 | args = args || {}; 859 | includeFallbackColors = args.includeFallbackColors; 860 | level = args.level; 861 | size = args.size; 862 | 863 | for (var i = 0; i < colorList.length; i++) { 864 | readability = tinycolor.readability(baseColor, colorList[i]); 865 | if (readability > bestScore) { 866 | bestScore = readability; 867 | bestColor = tinycolor(colorList[i]); 868 | } 869 | } 870 | 871 | if ( 872 | tinycolor.isReadable(baseColor, bestColor, { 873 | level: level, 874 | size: size, 875 | }) || 876 | !includeFallbackColors 877 | ) { 878 | return bestColor; 879 | } else { 880 | args.includeFallbackColors = false; 881 | return tinycolor.mostReadable(baseColor, ["#fff", "#000"], args); 882 | } 883 | }; 884 | 885 | // Big List of Colors 886 | // ------------------ 887 | // 888 | var names = (tinycolor.names = { 889 | aliceblue: "f0f8ff", 890 | antiquewhite: "faebd7", 891 | aqua: "0ff", 892 | aquamarine: "7fffd4", 893 | azure: "f0ffff", 894 | beige: "f5f5dc", 895 | bisque: "ffe4c4", 896 | black: "000", 897 | blanchedalmond: "ffebcd", 898 | blue: "00f", 899 | blueviolet: "8a2be2", 900 | brown: "a52a2a", 901 | burlywood: "deb887", 902 | burntsienna: "ea7e5d", 903 | cadetblue: "5f9ea0", 904 | chartreuse: "7fff00", 905 | chocolate: "d2691e", 906 | coral: "ff7f50", 907 | cornflowerblue: "6495ed", 908 | cornsilk: "fff8dc", 909 | crimson: "dc143c", 910 | cyan: "0ff", 911 | darkblue: "00008b", 912 | darkcyan: "008b8b", 913 | darkgoldenrod: "b8860b", 914 | darkgray: "a9a9a9", 915 | darkgreen: "006400", 916 | darkgrey: "a9a9a9", 917 | darkkhaki: "bdb76b", 918 | darkmagenta: "8b008b", 919 | darkolivegreen: "556b2f", 920 | darkorange: "ff8c00", 921 | darkorchid: "9932cc", 922 | darkred: "8b0000", 923 | darksalmon: "e9967a", 924 | darkseagreen: "8fbc8f", 925 | darkslateblue: "483d8b", 926 | darkslategray: "2f4f4f", 927 | darkslategrey: "2f4f4f", 928 | darkturquoise: "00ced1", 929 | darkviolet: "9400d3", 930 | deeppink: "ff1493", 931 | deepskyblue: "00bfff", 932 | dimgray: "696969", 933 | dimgrey: "696969", 934 | dodgerblue: "1e90ff", 935 | firebrick: "b22222", 936 | floralwhite: "fffaf0", 937 | forestgreen: "228b22", 938 | fuchsia: "f0f", 939 | gainsboro: "dcdcdc", 940 | ghostwhite: "f8f8ff", 941 | gold: "ffd700", 942 | goldenrod: "daa520", 943 | gray: "808080", 944 | green: "008000", 945 | greenyellow: "adff2f", 946 | grey: "808080", 947 | honeydew: "f0fff0", 948 | hotpink: "ff69b4", 949 | indianred: "cd5c5c", 950 | indigo: "4b0082", 951 | ivory: "fffff0", 952 | khaki: "f0e68c", 953 | lavender: "e6e6fa", 954 | lavenderblush: "fff0f5", 955 | lawngreen: "7cfc00", 956 | lemonchiffon: "fffacd", 957 | lightblue: "add8e6", 958 | lightcoral: "f08080", 959 | lightcyan: "e0ffff", 960 | lightgoldenrodyellow: "fafad2", 961 | lightgray: "d3d3d3", 962 | lightgreen: "90ee90", 963 | lightgrey: "d3d3d3", 964 | lightpink: "ffb6c1", 965 | lightsalmon: "ffa07a", 966 | lightseagreen: "20b2aa", 967 | lightskyblue: "87cefa", 968 | lightslategray: "789", 969 | lightslategrey: "789", 970 | lightsteelblue: "b0c4de", 971 | lightyellow: "ffffe0", 972 | lime: "0f0", 973 | limegreen: "32cd32", 974 | linen: "faf0e6", 975 | magenta: "f0f", 976 | maroon: "800000", 977 | mediumaquamarine: "66cdaa", 978 | mediumblue: "0000cd", 979 | mediumorchid: "ba55d3", 980 | mediumpurple: "9370db", 981 | mediumseagreen: "3cb371", 982 | mediumslateblue: "7b68ee", 983 | mediumspringgreen: "00fa9a", 984 | mediumturquoise: "48d1cc", 985 | mediumvioletred: "c71585", 986 | midnightblue: "191970", 987 | mintcream: "f5fffa", 988 | mistyrose: "ffe4e1", 989 | moccasin: "ffe4b5", 990 | navajowhite: "ffdead", 991 | navy: "000080", 992 | oldlace: "fdf5e6", 993 | olive: "808000", 994 | olivedrab: "6b8e23", 995 | orange: "ffa500", 996 | orangered: "ff4500", 997 | orchid: "da70d6", 998 | palegoldenrod: "eee8aa", 999 | palegreen: "98fb98", 1000 | paleturquoise: "afeeee", 1001 | palevioletred: "db7093", 1002 | papayawhip: "ffefd5", 1003 | peachpuff: "ffdab9", 1004 | peru: "cd853f", 1005 | pink: "ffc0cb", 1006 | plum: "dda0dd", 1007 | powderblue: "b0e0e6", 1008 | purple: "800080", 1009 | rebeccapurple: "663399", 1010 | red: "f00", 1011 | rosybrown: "bc8f8f", 1012 | royalblue: "4169e1", 1013 | saddlebrown: "8b4513", 1014 | salmon: "fa8072", 1015 | sandybrown: "f4a460", 1016 | seagreen: "2e8b57", 1017 | seashell: "fff5ee", 1018 | sienna: "a0522d", 1019 | silver: "c0c0c0", 1020 | skyblue: "87ceeb", 1021 | slateblue: "6a5acd", 1022 | slategray: "708090", 1023 | slategrey: "708090", 1024 | snow: "fffafa", 1025 | springgreen: "00ff7f", 1026 | steelblue: "4682b4", 1027 | tan: "d2b48c", 1028 | teal: "008080", 1029 | thistle: "d8bfd8", 1030 | tomato: "ff6347", 1031 | turquoise: "40e0d0", 1032 | violet: "ee82ee", 1033 | wheat: "f5deb3", 1034 | white: "fff", 1035 | whitesmoke: "f5f5f5", 1036 | yellow: "ff0", 1037 | yellowgreen: "9acd32", 1038 | }); 1039 | 1040 | // Make it easy to access colors via `hexNames[hex]` 1041 | var hexNames = (tinycolor.hexNames = flip(names)); 1042 | 1043 | // Utilities 1044 | // --------- 1045 | 1046 | // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }` 1047 | function flip(o) { 1048 | var flipped = {}; 1049 | for (var i in o) { 1050 | if (o.hasOwnProperty(i)) { 1051 | flipped[o[i]] = i; 1052 | } 1053 | } 1054 | return flipped; 1055 | } 1056 | 1057 | // Return a valid alpha value [0,1] with all invalid values being set to 1 1058 | function boundAlpha(a) { 1059 | a = parseFloat(a); 1060 | 1061 | if (isNaN(a) || a < 0 || a > 1) { 1062 | a = 1; 1063 | } 1064 | 1065 | return a; 1066 | } 1067 | 1068 | // Take input from [0, n] and return it as [0, 1] 1069 | function bound01(n, max) { 1070 | if (isOnePointZero(n)) n = "100%"; 1071 | 1072 | var processPercent = isPercentage(n); 1073 | n = Math.min(max, Math.max(0, parseFloat(n))); 1074 | 1075 | // Automatically convert percentage into number 1076 | if (processPercent) { 1077 | n = parseInt(n * max, 10) / 100; 1078 | } 1079 | 1080 | // Handle floating point rounding errors 1081 | if (Math.abs(n - max) < 0.000001) { 1082 | return 1; 1083 | } 1084 | 1085 | // Convert into [0, 1] range if it isn't already 1086 | return (n % max) / parseFloat(max); 1087 | } 1088 | 1089 | // Force a number between 0 and 1 1090 | function clamp01(val) { 1091 | return Math.min(1, Math.max(0, val)); 1092 | } 1093 | 1094 | // Parse a base-16 hex value into a base-10 integer 1095 | function parseIntFromHex(val) { 1096 | return parseInt(val, 16); 1097 | } 1098 | 1099 | // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1 1100 | // 1101 | function isOnePointZero(n) { 1102 | return typeof n == "string" && n.indexOf(".") != -1 && parseFloat(n) === 1; 1103 | } 1104 | 1105 | // Check to see if string passed in is a percentage 1106 | function isPercentage(n) { 1107 | return typeof n === "string" && n.indexOf("%") != -1; 1108 | } 1109 | 1110 | // Force a hex value to have 2 characters 1111 | function pad2(c) { 1112 | return c.length == 1 ? "0" + c : "" + c; 1113 | } 1114 | 1115 | // Replace a decimal with it's percentage value 1116 | function convertToPercentage(n) { 1117 | if (n <= 1) { 1118 | n = n * 100 + "%"; 1119 | } 1120 | 1121 | return n; 1122 | } 1123 | 1124 | // Converts a decimal to a hex value 1125 | function convertDecimalToHex(d) { 1126 | return Math.round(parseFloat(d) * 255).toString(16); 1127 | } 1128 | // Converts a hex value to a decimal 1129 | function convertHexToDecimal(h) { 1130 | return parseIntFromHex(h) / 255; 1131 | } 1132 | 1133 | var matchers = (function () { 1134 | // 1135 | var CSS_INTEGER = "[-\\+]?\\d+%?"; 1136 | 1137 | // 1138 | var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?"; 1139 | 1140 | // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome. 1141 | var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")"; 1142 | 1143 | // Actual matching. 1144 | // Parentheses and commas are optional, but not required. 1145 | // Whitespace can take the place of commas or opening paren 1146 | var PERMISSIVE_MATCH3 = 1147 | "[\\s|\\(]+(" + 1148 | CSS_UNIT + 1149 | ")[,|\\s]+(" + 1150 | CSS_UNIT + 1151 | ")[,|\\s]+(" + 1152 | CSS_UNIT + 1153 | ")\\s*\\)?"; 1154 | var PERMISSIVE_MATCH4 = 1155 | "[\\s|\\(]+(" + 1156 | CSS_UNIT + 1157 | ")[,|\\s]+(" + 1158 | CSS_UNIT + 1159 | ")[,|\\s]+(" + 1160 | CSS_UNIT + 1161 | ")[,|\\s]+(" + 1162 | CSS_UNIT + 1163 | ")\\s*\\)?"; 1164 | 1165 | return { 1166 | CSS_UNIT: new RegExp(CSS_UNIT), 1167 | rgb: new RegExp("rgb" + PERMISSIVE_MATCH3), 1168 | rgba: new RegExp("rgba" + PERMISSIVE_MATCH4), 1169 | hsl: new RegExp("hsl" + PERMISSIVE_MATCH3), 1170 | hsla: new RegExp("hsla" + PERMISSIVE_MATCH4), 1171 | hsv: new RegExp("hsv" + PERMISSIVE_MATCH3), 1172 | hsva: new RegExp("hsva" + PERMISSIVE_MATCH4), 1173 | hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, 1174 | hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/, 1175 | hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, 1176 | hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/, 1177 | }; 1178 | })(); 1179 | 1180 | // `isValidCSSUnit` 1181 | // Take in a single string / number and check to see if it looks like a CSS unit 1182 | // (see `matchers` above for definition). 1183 | function isValidCSSUnit(color) { 1184 | return !!matchers.CSS_UNIT.exec(color); 1185 | } 1186 | 1187 | // `stringInputToObject` 1188 | // Permissive string parsing. Take in a number of formats, and output an object 1189 | // based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}` 1190 | function stringInputToObject(color) { 1191 | color = color.replace(trimLeft, "").replace(trimRight, "").toLowerCase(); 1192 | var named = false; 1193 | if (names[color]) { 1194 | color = names[color]; 1195 | named = true; 1196 | } else if (color == "transparent") { 1197 | return { r: 0, g: 0, b: 0, a: 0, format: "name" }; 1198 | } 1199 | 1200 | // Try to match string input using regular expressions. 1201 | // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360] 1202 | // Just return an object and let the conversion functions handle that. 1203 | // This way the result will be the same whether the tinycolor is initialized with string or object. 1204 | var match; 1205 | if ((match = matchers.rgb.exec(color))) { 1206 | return { r: match[1], g: match[2], b: match[3] }; 1207 | } 1208 | if ((match = matchers.rgba.exec(color))) { 1209 | return { r: match[1], g: match[2], b: match[3], a: match[4] }; 1210 | } 1211 | if ((match = matchers.hsl.exec(color))) { 1212 | return { h: match[1], s: match[2], l: match[3] }; 1213 | } 1214 | if ((match = matchers.hsla.exec(color))) { 1215 | return { h: match[1], s: match[2], l: match[3], a: match[4] }; 1216 | } 1217 | if ((match = matchers.hsv.exec(color))) { 1218 | return { h: match[1], s: match[2], v: match[3] }; 1219 | } 1220 | if ((match = matchers.hsva.exec(color))) { 1221 | return { h: match[1], s: match[2], v: match[3], a: match[4] }; 1222 | } 1223 | if ((match = matchers.hex8.exec(color))) { 1224 | return { 1225 | r: parseIntFromHex(match[1]), 1226 | g: parseIntFromHex(match[2]), 1227 | b: parseIntFromHex(match[3]), 1228 | a: convertHexToDecimal(match[4]), 1229 | format: named ? "name" : "hex8", 1230 | }; 1231 | } 1232 | if ((match = matchers.hex6.exec(color))) { 1233 | return { 1234 | r: parseIntFromHex(match[1]), 1235 | g: parseIntFromHex(match[2]), 1236 | b: parseIntFromHex(match[3]), 1237 | format: named ? "name" : "hex", 1238 | }; 1239 | } 1240 | if ((match = matchers.hex4.exec(color))) { 1241 | return { 1242 | r: parseIntFromHex(match[1] + "" + match[1]), 1243 | g: parseIntFromHex(match[2] + "" + match[2]), 1244 | b: parseIntFromHex(match[3] + "" + match[3]), 1245 | a: convertHexToDecimal(match[4] + "" + match[4]), 1246 | format: named ? "name" : "hex8", 1247 | }; 1248 | } 1249 | if ((match = matchers.hex3.exec(color))) { 1250 | return { 1251 | r: parseIntFromHex(match[1] + "" + match[1]), 1252 | g: parseIntFromHex(match[2] + "" + match[2]), 1253 | b: parseIntFromHex(match[3] + "" + match[3]), 1254 | format: named ? "name" : "hex", 1255 | }; 1256 | } 1257 | 1258 | return false; 1259 | } 1260 | 1261 | function validateWCAG2Parms(parms) { 1262 | // return valid WCAG2 parms for isReadable. 1263 | // If input parms are invalid, return {"level":"AA", "size":"small"} 1264 | var level, size; 1265 | parms = parms || { level: "AA", size: "small" }; 1266 | level = (parms.level || "AA").toUpperCase(); 1267 | size = (parms.size || "small").toLowerCase(); 1268 | if (level !== "AA" && level !== "AAA") { 1269 | level = "AA"; 1270 | } 1271 | if (size !== "small" && size !== "large") { 1272 | size = "small"; 1273 | } 1274 | return { level: level, size: size }; 1275 | } 1276 | --------------------------------------------------------------------------------