├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── lib ├── cjs │ └── invert.js ├── esm │ └── invert.js ├── invert.d.ts ├── invert.js ├── invert.js.map ├── invert.min.js └── invert.min.js.map ├── package-lock.json ├── package.json ├── src └── invert.ts ├── test ├── anim │ ├── animation.psd │ └── invert-animation.gif ├── browser │ ├── invert.html │ ├── invert.require.html │ ├── require.js │ ├── simple.js │ └── style.css ├── import.test.ts └── unit.test.ts ├── tsconfig.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [{package.json,*.xml}] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /backup/ 2 | /node_modules/ 3 | npm-debug.log 4 | tmp 5 | TODO.md 6 | .DS_Store 7 | /test/coverage/ 8 | /lib/.* 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '8' 4 | - '6' 5 | before_script: cd $TRAVIS_BUILD_DIR 6 | script: 7 | - npm run build 8 | - npm run cover 9 | - npm run coveralls 10 | notifications: 11 | email: 12 | # recipients: 13 | # - one@example.com 14 | on_success: never # default: change 15 | on_failure: always # default: always 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2019, Onur Yıldırım . All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # invert-color 2 | 3 | [![build-status](https://img.shields.io/travis/onury/invert-color.svg?branch=master&style=flat-square)](https://travis-ci.org/onury/invert-color) 4 | [![Coverage Status](https://coveralls.io/repos/github/onury/invert-color/badge.svg?branch=master&style=flat-square)](https://coveralls.io/github/onury/invert-color?branch=master) 5 | [![Known Vulnerabilities](https://snyk.io/test/github/onury/invert-color/badge.svg?style=flat-square)](https://snyk.io/test/github/onury/invert-color) 6 | [![npm](http://img.shields.io/npm/v/invert-color.svg?style=flat-square)](https://www.npmjs.com/package/invert-color) 7 | [![release](https://img.shields.io/github/release/onury/invert-color.svg?style=flat-square)](https://github.com/onury/invert-color) 8 | [![downloads](http://img.shields.io/npm/dm/invert-color.svg?style=flat-square)](https://www.npmjs.com/package/invert-color) 9 | [![license](http://img.shields.io/npm/l/invert-color.svg?style=flat-square)](https://github.com/onury/invert-color/blob/master/LICENSE) 10 | [![typescript](https://img.shields.io/badge/written%20in-%20TypeScript%20-6575ff.svg?style=flat-square)](https://www.typescriptlang.org) 11 | 12 | > © 2021, Onur Yıldırım ([@onury](https://github.com/onury)). MIT License. 13 | 14 | Generates inverted (opposite) version of the given color. (<1KB) 15 | 16 | _This passes a long test suite of **Adobe Photoshop CC** inverted colors... 17 | Generating exactly the same results with it._ 18 | 19 | ![Invert Animation](https://github.com/onury/invert-color/blob/master/test/anim/invert-animation.gif?raw=true) 20 | 21 | ## Usage 22 | 23 | `npm i invert-color` 24 | 25 | ```js 26 | // Node, CommonJS 27 | const invert = require('invert-color'); 28 | // ES2015, JSNext 29 | import invert from 'invert-color'; 30 | // TypeScript 31 | import invert, { RGB, RgbArray, HexColor, BlackWhite } from 'invert-color'; 32 | ``` 33 | For UMD in browser, use `lib/invert.min.js`. 34 | See [other exports](https://github.com/onury/invert-color/tree/master/lib). 35 | 36 | ### `invert(color[, bw])` 37 | 38 | - **`color`** : `String|Array|Object` 39 | Color in HEX string, RGB array or RGB object to be inverted. 40 | - **`bw`** : `Boolean|Object` 41 | Optional. A boolean value indicating whether the output should be amplified to black (`#000000`) or white (`#ffffff`), according to the luminance of the original color. You can set custom black/white values (and/or luminance threshold) by passing an object. 42 | 43 | 44 | ```js 45 | invert('#000') // —> #ffffff 46 | invert('#282b35') // —> #d7d4ca 47 | 48 | // input color as RGB array or object 49 | invert([69, 191, 189]) // —> #ba4042 50 | invert({ r: 249, g: 119, b: 121 }) // —> #068886 51 | 52 | // amplify to black or white 53 | invert('#282b35', true) // —> #ffffff 54 | 55 | // amplify to custom black or white color 56 | invert('#282b35', { black: '#3a3a3a', white: '#fafafa' }) // —> #fafafa 57 | 58 | // amplify with custom luminance threshold (default is invert.defaultThreshold = ~0.179) 59 | invert('#282b35', { black: '#3a3a3a', white: '#fafafa', threshold: 0.01 }) // —> #3a3a3a 60 | ``` 61 | 62 | ### `invert.asRGB(color[, bw])` 63 | Invert and output result as RGB **object**. 64 | 65 | ```js 66 | invert.asRGB('#fff') // —> { r: 0, g: 0, b: 0 } 67 | ``` 68 | 69 | ### `invert.asRgbArray(color[, bw])` 70 | Invert and output result as RGB **array**. 71 | 72 | ```js 73 | invert.asRgbArray('#000') // —> [255, 255, 255] 74 | ``` 75 | 76 | **`bw` option** 77 | 78 | This is useful in case, you need to create contrast (i.e. background vs foreground, for better readability). The animation at the top is a demonstration. 79 | 80 | ## Contributing 81 | 82 | Clone original project: 83 | 84 | ```sh 85 | git clone https://github.com/onury/invert-color.git 86 | ``` 87 | 88 | Install (dev) dependencies: 89 | 90 | ```sh 91 | npm install 92 | ``` 93 | 94 | Add tests into [test/unit.test.ts](test/unit.test.ts) and run: 95 | 96 | ```sh 97 | npm run cover 98 | ``` 99 | 100 | Travis build should pass, coverage should not degrade. 101 | 102 | ## Change-Log 103 | 104 | ### v2.0.0 (2018-11-09) 105 | - **Breaking**: In order to be consistent; now using **default export** only. Added ESM, UMD, CommonJS bundles (with rollup). See Usage section. 106 | - In addition to `main`, `package.json` now also defines `module`, `jsnext:main` and `browser`. 107 | - Added `threshold: number` to `BlackWhite` options (interface). Fixes [#16](https://github.com/onury/invert-color/issues/16). 108 | - Added `invert.defaultThreshold` constant. 109 | 110 | ### v1.5.0 (2018-08-22) 111 | 112 | - Re-written in TypeScript. 113 | - Added `.asRGB()` - alias of `.asRgbObject()`. 114 | 115 | ### v1.2.3 (2018-04-05) 116 | 117 | - Better error messages. (PR [#9](https://github.com/onury/invert-color/pull/9) by [@CAYdenberg](https://github.com/CAYdenberg)) Fixes [#8](https://github.com/onury/invert-color/issues/8). 118 | 119 | ### v1.2.2 (2017-12-07) 120 | 121 | - **Fixed** an issue with UMD output. Fixes [#7](https://github.com/onury/invert-color/issues/7). 122 | - **(Dev)** Adapted webpack for UMD. 123 | 124 | ### v1.2.0 (2017-11-24) 125 | 126 | - **Added** UMD support. (PR [#6](https://github.com/onury/invert-color/pull/6) by [@criography](https://github.com/criography) - revised for latest Babel.) 127 | - (Dev) Migrated tests to Jest (dropped Jasmine). 128 | 129 | ### v1.1.0 (2017-11-07) 130 | 131 | - **Added** ability to customize black/white color values. (PR [#3](https://github.com/onury/invert-color/pull/3) by [@BrainCrumbz](https://github.com/BrainCrumbz)) 132 | - **Fixed** typo. (PR [#1](https://github.com/onury/invert-color/pull/1) by [@villfa](https://github.com/villfa)) 133 | - Minor revisions. 134 | 135 | ### v1.0.0 (2017-08-22) 136 | 137 | - Initial version. 138 | 139 | ## License 140 | 141 | [MIT][license]. 142 | 143 | 144 | [license]:https://github.com/onury/invert-color/blob/master/LICENSE 145 | -------------------------------------------------------------------------------- /lib/cjs/invert.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // ------------------------------- 3 | // TYPES / INTERFACES 4 | // ------------------------------- 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | // ------------------------------- 7 | // CONSTANTS 8 | // ------------------------------- 9 | var DEFAULT_THRESHOLD = Math.sqrt(1.05 * 0.05) - 0.05; 10 | var RE_HEX = /^(?:[0-9a-f]{3}){1,2}$/i; 11 | var DEFAULT_BW = { 12 | black: '#000000', 13 | white: '#ffffff', 14 | threshold: DEFAULT_THRESHOLD 15 | }; 16 | // ------------------------------- 17 | // HELPER METHODS 18 | // ------------------------------- 19 | function padz(str, len) { 20 | if (len === void 0) { len = 2; } 21 | return (new Array(len).join('0') + str).slice(-len); 22 | } 23 | function hexToRgbArray(hex) { 24 | if (hex.slice(0, 1) === '#') 25 | hex = hex.slice(1); 26 | if (!RE_HEX.test(hex)) 27 | throw new Error("Invalid HEX color: \"" + hex + "\""); 28 | // normalize / convert 3-chars hex to 6-chars. 29 | if (hex.length === 3) { 30 | hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; 31 | } 32 | return [ 33 | parseInt(hex.slice(0, 2), 16), 34 | parseInt(hex.slice(2, 4), 16), 35 | parseInt(hex.slice(4, 6), 16) // b 36 | ]; 37 | } 38 | function toRGB(c) { 39 | return { r: c[0], g: c[1], b: c[2] }; 40 | } 41 | function toRgbArray(c) { 42 | if (!c) 43 | throw new Error('Invalid color value'); 44 | if (Array.isArray(c)) 45 | return c; 46 | return typeof c === 'string' ? hexToRgbArray(c) : [c.r, c.g, c.b]; 47 | } 48 | // http://stackoverflow.com/a/3943023/112731 49 | function getLuminance(c) { 50 | var i, x; 51 | var a = []; // so we don't mutate 52 | for (i = 0; i < c.length; i++) { 53 | x = c[i] / 255; 54 | a[i] = x <= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4); 55 | } 56 | return 0.2126 * a[0] + 0.7152 * a[1] + 0.0722 * a[2]; 57 | } 58 | function invertToBW(color, bw, asArr) { 59 | var options = (bw === true) 60 | ? DEFAULT_BW 61 | : Object.assign({}, DEFAULT_BW, bw); 62 | return getLuminance(color) > options.threshold 63 | ? (asArr ? hexToRgbArray(options.black) : options.black) 64 | : (asArr ? hexToRgbArray(options.white) : options.white); 65 | } 66 | // ------------------------------- 67 | // PUBLIC MEMBERS 68 | // ------------------------------- 69 | /** 70 | * Generates inverted (opposite) version of the given color. 71 | * @param {Color} color - Color to be inverted. 72 | * @param {BlackWhite|boolean} [bw=false] - Whether to amplify the inversion to 73 | * black or white. Provide an object to customize black/white colors. 74 | * @returns {HexColor} - Hexadecimal representation of the inverted color. 75 | */ 76 | function invert(color, bw) { 77 | if (bw === void 0) { bw = false; } 78 | color = toRgbArray(color); 79 | if (bw) 80 | return invertToBW(color, bw); 81 | return '#' + color.map(function (c) { return padz((255 - c).toString(16)); }).join(''); 82 | } 83 | /** 84 | * Utility methods to generate inverted version of a color. 85 | * @namespace 86 | */ 87 | (function (invert) { 88 | /** 89 | * Generates inverted (opposite) version of the given color, as a RGB object. 90 | * @alias invert.asRgbObject 91 | * @param {Color} color - Color to be inverted. 92 | * @param {BlackWhite|boolean} [bw] - Whether to amplify the inversion to 93 | * black or white. Provide an object to customize black/white colors. 94 | * @returns {RGB} - RGB object representation of the inverted color. 95 | */ 96 | function asRGB(color, bw) { 97 | color = toRgbArray(color); 98 | var list = bw 99 | ? invertToBW(color, bw, true) 100 | : color.map(function (c) { return 255 - c; }); 101 | return toRGB(list); 102 | } 103 | invert.asRGB = asRGB; 104 | /** 105 | * Generates inverted (opposite) version of the given color, as a RGB array. 106 | * @param {Color} color - Color to be inverted. 107 | * @param {BlackWhite|boolean} [bw] - Whether to amplify the inversion to 108 | * black or white. Provide an object to customize black/white colors. 109 | * @returns {RGB} - RGB array representation of the inverted color. 110 | */ 111 | function asRgbArray(color, bw) { 112 | color = toRgbArray(color); 113 | return bw 114 | ? invertToBW(color, bw, true) 115 | : color.map(function (c) { return 255 - c; }); 116 | } 117 | invert.asRgbArray = asRgbArray; 118 | /** 119 | * Default luminance threshold used for amplifying inversion to black and 120 | * white. 121 | * @type {number} 122 | */ 123 | invert.defaultThreshold = DEFAULT_THRESHOLD; 124 | /** 125 | * Alias of `.asRGB()` 126 | */ 127 | invert.asRgbObject = asRGB; 128 | })(invert || (invert = {})); 129 | // ------------------------------- 130 | // EXPORT 131 | // ------------------------------- 132 | exports.default = invert; 133 | -------------------------------------------------------------------------------- /lib/esm/invert.js: -------------------------------------------------------------------------------- 1 | // ------------------------------- 2 | // TYPES / INTERFACES 3 | // ------------------------------- 4 | // ------------------------------- 5 | // CONSTANTS 6 | // ------------------------------- 7 | const DEFAULT_THRESHOLD = Math.sqrt(1.05 * 0.05) - 0.05; 8 | const RE_HEX = /^(?:[0-9a-f]{3}){1,2}$/i; 9 | const DEFAULT_BW = { 10 | black: '#000000', 11 | white: '#ffffff', 12 | threshold: DEFAULT_THRESHOLD 13 | }; 14 | // ------------------------------- 15 | // HELPER METHODS 16 | // ------------------------------- 17 | function padz(str, len = 2) { 18 | return (new Array(len).join('0') + str).slice(-len); 19 | } 20 | function hexToRgbArray(hex) { 21 | if (hex.slice(0, 1) === '#') 22 | hex = hex.slice(1); 23 | if (!RE_HEX.test(hex)) 24 | throw new Error(`Invalid HEX color: "${hex}"`); 25 | // normalize / convert 3-chars hex to 6-chars. 26 | if (hex.length === 3) { 27 | hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; 28 | } 29 | return [ 30 | parseInt(hex.slice(0, 2), 16), 31 | parseInt(hex.slice(2, 4), 16), 32 | parseInt(hex.slice(4, 6), 16) // b 33 | ]; 34 | } 35 | function toRGB(c) { 36 | return { r: c[0], g: c[1], b: c[2] }; 37 | } 38 | function toRgbArray(c) { 39 | if (!c) 40 | throw new Error('Invalid color value'); 41 | if (Array.isArray(c)) 42 | return c; 43 | return typeof c === 'string' ? hexToRgbArray(c) : [c.r, c.g, c.b]; 44 | } 45 | // http://stackoverflow.com/a/3943023/112731 46 | function getLuminance(c) { 47 | let i, x; 48 | const a = []; // so we don't mutate 49 | for (i = 0; i < c.length; i++) { 50 | x = c[i] / 255; 51 | a[i] = x <= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4); 52 | } 53 | return 0.2126 * a[0] + 0.7152 * a[1] + 0.0722 * a[2]; 54 | } 55 | function invertToBW(color, bw, asArr) { 56 | const options = (bw === true) 57 | ? DEFAULT_BW 58 | : Object.assign({}, DEFAULT_BW, bw); 59 | return getLuminance(color) > options.threshold 60 | ? (asArr ? hexToRgbArray(options.black) : options.black) 61 | : (asArr ? hexToRgbArray(options.white) : options.white); 62 | } 63 | // ------------------------------- 64 | // PUBLIC MEMBERS 65 | // ------------------------------- 66 | /** 67 | * Generates inverted (opposite) version of the given color. 68 | * @param {Color} color - Color to be inverted. 69 | * @param {BlackWhite|boolean} [bw=false] - Whether to amplify the inversion to 70 | * black or white. Provide an object to customize black/white colors. 71 | * @returns {HexColor} - Hexadecimal representation of the inverted color. 72 | */ 73 | function invert(color, bw = false) { 74 | color = toRgbArray(color); 75 | if (bw) 76 | return invertToBW(color, bw); 77 | return '#' + color.map(c => padz((255 - c).toString(16))).join(''); 78 | } 79 | /** 80 | * Utility methods to generate inverted version of a color. 81 | * @namespace 82 | */ 83 | (function (invert) { 84 | /** 85 | * Generates inverted (opposite) version of the given color, as a RGB object. 86 | * @alias invert.asRgbObject 87 | * @param {Color} color - Color to be inverted. 88 | * @param {BlackWhite|boolean} [bw] - Whether to amplify the inversion to 89 | * black or white. Provide an object to customize black/white colors. 90 | * @returns {RGB} - RGB object representation of the inverted color. 91 | */ 92 | function asRGB(color, bw) { 93 | color = toRgbArray(color); 94 | const list = bw 95 | ? invertToBW(color, bw, true) 96 | : color.map(c => 255 - c); 97 | return toRGB(list); 98 | } 99 | invert.asRGB = asRGB; 100 | /** 101 | * Generates inverted (opposite) version of the given color, as a RGB array. 102 | * @param {Color} color - Color to be inverted. 103 | * @param {BlackWhite|boolean} [bw] - Whether to amplify the inversion to 104 | * black or white. Provide an object to customize black/white colors. 105 | * @returns {RGB} - RGB array representation of the inverted color. 106 | */ 107 | function asRgbArray(color, bw) { 108 | color = toRgbArray(color); 109 | return bw 110 | ? invertToBW(color, bw, true) 111 | : color.map(c => 255 - c); 112 | } 113 | invert.asRgbArray = asRgbArray; 114 | /** 115 | * Default luminance threshold used for amplifying inversion to black and 116 | * white. 117 | * @type {number} 118 | */ 119 | invert.defaultThreshold = DEFAULT_THRESHOLD; 120 | /** 121 | * Alias of `.asRGB()` 122 | */ 123 | invert.asRgbObject = asRGB; 124 | })(invert || (invert = {})); 125 | // ------------------------------- 126 | // EXPORT 127 | // ------------------------------- 128 | export default invert; 129 | -------------------------------------------------------------------------------- /lib/invert.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * RGB object type with red, green and blue components. 3 | */ 4 | export declare type RGB = { 5 | r: number; 6 | g: number; 7 | b: number; 8 | }; 9 | /** 10 | * RGB list (array) type with red, green and blue components. 11 | */ 12 | export declare type RgbArray = [number, number, number]; 13 | /** 14 | * Hexadecimal representation of a color. 15 | */ 16 | export declare type HexColor = string; 17 | /** 18 | * Color represented as hexadecimal value or as RGB object or list. 19 | */ 20 | export declare type Color = RGB | RgbArray | HexColor; 21 | /** 22 | * Interface for defining black and white colors; used to amplify the contrast 23 | * of the color inversion. 24 | */ 25 | export interface BlackWhite { 26 | black: HexColor; 27 | white: HexColor; 28 | threshold?: number; 29 | } 30 | /** 31 | * Generates inverted (opposite) version of the given color. 32 | * @param {Color} color - Color to be inverted. 33 | * @param {BlackWhite|boolean} [bw=false] - Whether to amplify the inversion to 34 | * black or white. Provide an object to customize black/white colors. 35 | * @returns {HexColor} - Hexadecimal representation of the inverted color. 36 | */ 37 | declare function invert(color: Color, bw?: BlackWhite | boolean): HexColor; 38 | /** 39 | * Utility methods to generate inverted version of a color. 40 | * @namespace 41 | */ 42 | declare namespace invert { 43 | /** 44 | * Generates inverted (opposite) version of the given color, as a RGB object. 45 | * @alias invert.asRgbObject 46 | * @param {Color} color - Color to be inverted. 47 | * @param {BlackWhite|boolean} [bw] - Whether to amplify the inversion to 48 | * black or white. Provide an object to customize black/white colors. 49 | * @returns {RGB} - RGB object representation of the inverted color. 50 | */ 51 | function asRGB(color: Color, bw?: BlackWhite | boolean): RGB; 52 | /** 53 | * Generates inverted (opposite) version of the given color, as a RGB array. 54 | * @param {Color} color - Color to be inverted. 55 | * @param {BlackWhite|boolean} [bw] - Whether to amplify the inversion to 56 | * black or white. Provide an object to customize black/white colors. 57 | * @returns {RGB} - RGB array representation of the inverted color. 58 | */ 59 | function asRgbArray(color: Color, bw?: BlackWhite | boolean): RgbArray; 60 | /** 61 | * Default luminance threshold used for amplifying inversion to black and 62 | * white. 63 | * @type {number} 64 | */ 65 | const defaultThreshold: number; 66 | /** 67 | * Alias of `.asRGB()` 68 | */ 69 | const asRgbObject: typeof asRGB; 70 | } 71 | export default invert; 72 | -------------------------------------------------------------------------------- /lib/invert.js: -------------------------------------------------------------------------------- 1 | /*! @license https://github.com/onury/invert-color */ 2 | (function (global, factory) { 3 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 4 | typeof define === 'function' && define.amd ? define(factory) : 5 | (global.invert = factory()); 6 | }(this, (function () { 'use strict'; 7 | 8 | // ------------------------------- 9 | // TYPES / INTERFACES 10 | // ------------------------------- 11 | // ------------------------------- 12 | // CONSTANTS 13 | // ------------------------------- 14 | var DEFAULT_THRESHOLD = Math.sqrt(1.05 * 0.05) - 0.05; 15 | var RE_HEX = /^(?:[0-9a-f]{3}){1,2}$/i; 16 | var DEFAULT_BW = { 17 | black: '#000000', 18 | white: '#ffffff', 19 | threshold: DEFAULT_THRESHOLD 20 | }; 21 | // ------------------------------- 22 | // HELPER METHODS 23 | // ------------------------------- 24 | function padz(str, len) { 25 | if (len === void 0) { len = 2; } 26 | return (new Array(len).join('0') + str).slice(-len); 27 | } 28 | function hexToRgbArray(hex) { 29 | if (hex.slice(0, 1) === '#') 30 | hex = hex.slice(1); 31 | if (!RE_HEX.test(hex)) 32 | throw new Error("Invalid HEX color: \"" + hex + "\""); 33 | // normalize / convert 3-chars hex to 6-chars. 34 | if (hex.length === 3) { 35 | hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; 36 | } 37 | return [ 38 | parseInt(hex.slice(0, 2), 16), 39 | parseInt(hex.slice(2, 4), 16), 40 | parseInt(hex.slice(4, 6), 16) // b 41 | ]; 42 | } 43 | function toRGB(c) { 44 | return { r: c[0], g: c[1], b: c[2] }; 45 | } 46 | function toRgbArray(c) { 47 | if (!c) 48 | throw new Error('Invalid color value'); 49 | if (Array.isArray(c)) 50 | return c; 51 | return typeof c === 'string' ? hexToRgbArray(c) : [c.r, c.g, c.b]; 52 | } 53 | // http://stackoverflow.com/a/3943023/112731 54 | function getLuminance(c) { 55 | var i, x; 56 | var a = []; // so we don't mutate 57 | for (i = 0; i < c.length; i++) { 58 | x = c[i] / 255; 59 | a[i] = x <= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4); 60 | } 61 | return 0.2126 * a[0] + 0.7152 * a[1] + 0.0722 * a[2]; 62 | } 63 | function invertToBW(color, bw, asArr) { 64 | var options = (bw === true) 65 | ? DEFAULT_BW 66 | : Object.assign({}, DEFAULT_BW, bw); 67 | return getLuminance(color) > options.threshold 68 | ? (asArr ? hexToRgbArray(options.black) : options.black) 69 | : (asArr ? hexToRgbArray(options.white) : options.white); 70 | } 71 | // ------------------------------- 72 | // PUBLIC MEMBERS 73 | // ------------------------------- 74 | /** 75 | * Generates inverted (opposite) version of the given color. 76 | * @param {Color} color - Color to be inverted. 77 | * @param {BlackWhite|boolean} [bw=false] - Whether to amplify the inversion to 78 | * black or white. Provide an object to customize black/white colors. 79 | * @returns {HexColor} - Hexadecimal representation of the inverted color. 80 | */ 81 | function invert(color, bw) { 82 | if (bw === void 0) { bw = false; } 83 | color = toRgbArray(color); 84 | if (bw) 85 | return invertToBW(color, bw); 86 | return '#' + color.map(function (c) { return padz((255 - c).toString(16)); }).join(''); 87 | } 88 | /** 89 | * Utility methods to generate inverted version of a color. 90 | * @namespace 91 | */ 92 | (function (invert) { 93 | /** 94 | * Generates inverted (opposite) version of the given color, as a RGB object. 95 | * @alias invert.asRgbObject 96 | * @param {Color} color - Color to be inverted. 97 | * @param {BlackWhite|boolean} [bw] - Whether to amplify the inversion to 98 | * black or white. Provide an object to customize black/white colors. 99 | * @returns {RGB} - RGB object representation of the inverted color. 100 | */ 101 | function asRGB(color, bw) { 102 | color = toRgbArray(color); 103 | var list = bw 104 | ? invertToBW(color, bw, true) 105 | : color.map(function (c) { return 255 - c; }); 106 | return toRGB(list); 107 | } 108 | invert.asRGB = asRGB; 109 | /** 110 | * Generates inverted (opposite) version of the given color, as a RGB array. 111 | * @param {Color} color - Color to be inverted. 112 | * @param {BlackWhite|boolean} [bw] - Whether to amplify the inversion to 113 | * black or white. Provide an object to customize black/white colors. 114 | * @returns {RGB} - RGB array representation of the inverted color. 115 | */ 116 | function asRgbArray(color, bw) { 117 | color = toRgbArray(color); 118 | return bw 119 | ? invertToBW(color, bw, true) 120 | : color.map(function (c) { return 255 - c; }); 121 | } 122 | invert.asRgbArray = asRgbArray; 123 | /** 124 | * Default luminance threshold used for amplifying inversion to black and 125 | * white. 126 | * @type {number} 127 | */ 128 | invert.defaultThreshold = DEFAULT_THRESHOLD; 129 | /** 130 | * Alias of `.asRGB()` 131 | */ 132 | invert.asRgbObject = asRGB; 133 | })(invert || (invert = {})); 134 | // ------------------------------- 135 | // EXPORT 136 | // ------------------------------- 137 | var invert$1 = invert; 138 | 139 | return invert$1; 140 | 141 | }))); 142 | //# sourceMappingURL=invert.js.map 143 | -------------------------------------------------------------------------------- /lib/invert.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"invert.js","sources":["invert.js"],"sourcesContent":["// -------------------------------\n// TYPES / INTERFACES\n// -------------------------------\n// -------------------------------\n// CONSTANTS\n// -------------------------------\nvar DEFAULT_THRESHOLD = Math.sqrt(1.05 * 0.05) - 0.05;\nvar RE_HEX = /^(?:[0-9a-f]{3}){1,2}$/i;\nvar DEFAULT_BW = {\n black: '#000000',\n white: '#ffffff',\n threshold: DEFAULT_THRESHOLD\n};\n// -------------------------------\n// HELPER METHODS\n// -------------------------------\nfunction padz(str, len) {\n if (len === void 0) { len = 2; }\n return (new Array(len).join('0') + str).slice(-len);\n}\nfunction hexToRgbArray(hex) {\n if (hex.slice(0, 1) === '#')\n hex = hex.slice(1);\n if (!RE_HEX.test(hex))\n throw new Error(\"Invalid HEX color: \\\"\" + hex + \"\\\"\");\n // normalize / convert 3-chars hex to 6-chars.\n if (hex.length === 3) {\n hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];\n }\n return [\n parseInt(hex.slice(0, 2), 16),\n parseInt(hex.slice(2, 4), 16),\n parseInt(hex.slice(4, 6), 16) // b\n ];\n}\nfunction toRGB(c) {\n return { r: c[0], g: c[1], b: c[2] };\n}\nfunction toRgbArray(c) {\n if (!c)\n throw new Error('Invalid color value');\n if (Array.isArray(c))\n return c;\n return typeof c === 'string' ? hexToRgbArray(c) : [c.r, c.g, c.b];\n}\n// http://stackoverflow.com/a/3943023/112731\nfunction getLuminance(c) {\n var i, x;\n var a = []; // so we don't mutate\n for (i = 0; i < c.length; i++) {\n x = c[i] / 255;\n a[i] = x <= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);\n }\n return 0.2126 * a[0] + 0.7152 * a[1] + 0.0722 * a[2];\n}\nfunction invertToBW(color, bw, asArr) {\n var options = (bw === true)\n ? DEFAULT_BW\n : Object.assign({}, DEFAULT_BW, bw);\n return getLuminance(color) > options.threshold\n ? (asArr ? hexToRgbArray(options.black) : options.black)\n : (asArr ? hexToRgbArray(options.white) : options.white);\n}\n// -------------------------------\n// PUBLIC MEMBERS\n// -------------------------------\n/**\n * Generates inverted (opposite) version of the given color.\n * @param {Color} color - Color to be inverted.\n * @param {BlackWhite|boolean} [bw=false] - Whether to amplify the inversion to\n * black or white. Provide an object to customize black/white colors.\n * @returns {HexColor} - Hexadecimal representation of the inverted color.\n */\nfunction invert(color, bw) {\n if (bw === void 0) { bw = false; }\n color = toRgbArray(color);\n if (bw)\n return invertToBW(color, bw);\n return '#' + color.map(function (c) { return padz((255 - c).toString(16)); }).join('');\n}\n/**\n * Utility methods to generate inverted version of a color.\n * @namespace\n */\n(function (invert) {\n /**\n * Generates inverted (opposite) version of the given color, as a RGB object.\n * @alias invert.asRgbObject\n * @param {Color} color - Color to be inverted.\n * @param {BlackWhite|boolean} [bw] - Whether to amplify the inversion to\n * black or white. Provide an object to customize black/white colors.\n * @returns {RGB} - RGB object representation of the inverted color.\n */\n function asRGB(color, bw) {\n color = toRgbArray(color);\n var list = bw\n ? invertToBW(color, bw, true)\n : color.map(function (c) { return 255 - c; });\n return toRGB(list);\n }\n invert.asRGB = asRGB;\n /**\n * Generates inverted (opposite) version of the given color, as a RGB array.\n * @param {Color} color - Color to be inverted.\n * @param {BlackWhite|boolean} [bw] - Whether to amplify the inversion to\n * black or white. Provide an object to customize black/white colors.\n * @returns {RGB} - RGB array representation of the inverted color.\n */\n function asRgbArray(color, bw) {\n color = toRgbArray(color);\n return bw\n ? invertToBW(color, bw, true)\n : color.map(function (c) { return 255 - c; });\n }\n invert.asRgbArray = asRgbArray;\n /**\n * Default luminance threshold used for amplifying inversion to black and\n * white.\n * @type {number}\n */\n invert.defaultThreshold = DEFAULT_THRESHOLD;\n /**\n * Alias of `.asRGB()`\n */\n invert.asRgbObject = asRGB;\n})(invert || (invert = {}));\n// -------------------------------\n// EXPORT\n// -------------------------------\nexport default invert;\n"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /lib/invert.min.js: -------------------------------------------------------------------------------- 1 | /*! @license https://github.com/onury/invert-color */ 2 | !function(r,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):r.invert=n()}(this,function(){"use strict";var t=Math.sqrt(1.05*.05)-.05,n=/^(?:[0-9a-f]{3}){1,2}$/i,i={black:"#000000",white:"#ffffff",threshold:t};function o(r){if("#"===r.slice(0,1)&&(r=r.slice(1)),!n.test(r))throw new Error('Invalid HEX color: "'+r+'"');return 3===r.length&&(r=r[0]+r[0]+r[1]+r[1]+r[2]+r[2]),[parseInt(r.slice(0,2),16),parseInt(r.slice(2,4),16),parseInt(r.slice(4,6),16)]}function f(r){if(!r)throw new Error("Invalid color value");return Array.isArray(r)?r:"string"==typeof r?o(r):[r.r,r.g,r.b]}function u(r,n,t){var e=!0===n?i:Object.assign({},i,n);return function(r){var n,t,e=[];for(n=0;ne.threshold?t?o(e.black):e.black:t?o(e.white):e.white}function r(r,n){return void 0===n&&(n=!1),r=f(r),n?u(r,n):"#"+r.map(function(r){return n=(255-r).toString(16),void 0===t&&(t=2),(new Array(t).join("0")+n).slice(-t);var n,t}).join("")}return function(r){function n(r,n){r=f(r);var t,e=n?u(r,n,!0):r.map(function(r){return 255-r});return{r:(t=e)[0],g:t[1],b:t[2]}}r.asRGB=n,r.asRgbArray=function(r,n){return r=f(r),n?u(r,n,!0):r.map(function(r){return 255-r})},r.defaultThreshold=t,r.asRgbObject=n}(r||(r={})),r}); -------------------------------------------------------------------------------- /lib/invert.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["invert.js"],"names":["global","factory","exports","module","define","amd","invert","this","DEFAULT_THRESHOLD","Math","sqrt","RE_HEX","DEFAULT_BW","black","white","threshold","hexToRgbArray","hex","slice","test","Error","length","parseInt","toRgbArray","c","Array","isArray","r","g","b","invertToBW","color","bw","asArr","options","Object","assign","i","x","a","pow","getLuminance","map","str","toString","len","join","asRGB","list","asRgbArray","defaultThreshold","asRgbObject"],"mappings":";CACC,SAAUA,EAAQC,GACI,iBAAZC,SAA0C,oBAAXC,OAAyBA,OAAOD,QAAUD,IAC9D,mBAAXG,QAAyBA,OAAOC,IAAMD,OAAOH,GACnDD,EAAOM,OAASL,IAHrB,CAIEM,KAAM,WAAe,aAQnB,IAAIC,EAAoBC,KAAKC,KAAK,KAAO,KAAQ,IAC7CC,EAAS,0BACTC,EAAa,CACbC,MAAO,UACPC,MAAO,UACPC,UAAWP,GASf,SAASQ,EAAcC,GAGnB,GAFwB,MAApBA,EAAIC,MAAM,EAAG,KACbD,EAAMA,EAAIC,MAAM,KACfP,EAAOQ,KAAKF,GACb,MAAM,IAAIG,MAAM,uBAA0BH,EAAM,KAKpD,OAHmB,IAAfA,EAAII,SACJJ,EAAMA,EAAI,GAAKA,EAAI,GAAKA,EAAI,GAAKA,EAAI,GAAKA,EAAI,GAAKA,EAAI,IAEpD,CACHK,SAASL,EAAIC,MAAM,EAAG,GAAI,IAC1BI,SAASL,EAAIC,MAAM,EAAG,GAAI,IAC1BI,SAASL,EAAIC,MAAM,EAAG,GAAI,KAMlC,SAASK,EAAWC,GAChB,IAAKA,EACD,MAAM,IAAIJ,MAAM,uBACpB,OAAIK,MAAMC,QAAQF,GACPA,EACS,iBAANA,EAAiBR,EAAcQ,GAAK,CAACA,EAAEG,EAAGH,EAAEI,EAAGJ,EAAEK,GAYnE,SAASC,EAAWC,EAAOC,EAAIC,GAC3B,IAAIC,GAAkB,IAAPF,EACTpB,EACAuB,OAAOC,OAAO,GAAIxB,EAAYoB,GACpC,OAbJ,SAAsBR,GAClB,IAAIa,EAAGC,EACHC,EAAI,GACR,IAAKF,EAAI,EAAGA,EAAIb,EAAEH,OAAQgB,IACtBC,EAAId,EAAEa,GAAK,IACXE,EAAEF,GAAKC,GAAK,OAAUA,EAAI,MAAQ7B,KAAK+B,KAAKF,EAAI,MAAS,MAAO,KAEpE,MAAO,MAASC,EAAE,GAAK,MAASA,EAAE,GAAK,MAASA,EAAE,GAM3CE,CAAaV,GAASG,EAAQnB,UAC9BkB,EAAQjB,EAAckB,EAAQrB,OAASqB,EAAQrB,MAC/CoB,EAAQjB,EAAckB,EAAQpB,OAASoB,EAAQpB,MAY1D,SAASR,EAAOyB,EAAOC,GAGnB,YAFW,IAAPA,IAAiBA,GAAK,GAC1BD,EAAQR,EAAWQ,GACfC,EACOF,EAAWC,EAAOC,GACtB,IAAMD,EAAMW,IAAI,SAAUlB,GAAK,OA9D5BmB,GA8DyC,IAAMnB,GAAGoB,SAAS,SA7DzD,IAARC,IAAkBA,EAAM,IACpB,IAAIpB,MAAMoB,GAAKC,KAAK,KAAOH,GAAKzB,OAAO2B,GAFnD,IAAcF,EAAKE,IA8D+DC,KAAK,IAqDvF,OA/CA,SAAWxC,GASP,SAASyC,EAAMhB,EAAOC,GAClBD,EAAQR,EAAWQ,GACnB,IA5DOP,EA4DHwB,EAAOhB,EACLF,EAAWC,EAAOC,GAAI,GACtBD,EAAMW,IAAI,SAAUlB,GAAK,OAAO,IAAMA,IAC5C,MA9DG,CAAEG,GADEH,EA+DMwB,GA9DH,GAAIpB,EAAGJ,EAAE,GAAIK,EAAGL,EAAE,IAgEhClB,EAAOyC,MAAQA,EAcfzC,EAAO2C,WANP,SAAoBlB,EAAOC,GAEvB,OADAD,EAAQR,EAAWQ,GACZC,EACDF,EAAWC,EAAOC,GAAI,GACtBD,EAAMW,IAAI,SAAUlB,GAAK,OAAO,IAAMA,KAQhDlB,EAAO4C,iBAAmB1C,EAI1BF,EAAO6C,YAAcJ,EAxCzB,CAyCGzC,IAAWA,EAAS,KAIRA"} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "invert-color", 3 | "version": "2.0.0", 4 | "description": "Generates inverted (opposite) version of the given color.", 5 | "repository": "onury/invert-color", 6 | "author": "Onur Yildirim ", 7 | "license": "MIT", 8 | "main": "lib/invert.js", 9 | "browser": "lib/invert.min.js", 10 | "module": "lib/esm/invert.js", 11 | "jsnext:main": "lib/esm/invert.js", 12 | "types": "./lib/invert.d.ts", 13 | "files": [ 14 | "lib", 15 | "LICENSE" 16 | ], 17 | "scripts": { 18 | "clean": "rimraf ./lib", 19 | "lint": "tslint ./src/**/*.ts", 20 | "build:esm": "tsc --module es2015 --target es2015 --outDir lib/esm", 21 | "build:cjs": "tsc --module commonjs --target es5 --outDir lib/cjs", 22 | "build:es-for-umd": "tsc --module es2015 --target es5 --outDir lib", 23 | "build:umd": "rollup lib/invert.js --format umd --name invert --exports default --sourcemap --banner '/*! @license https://github.com/onury/invert-color */' --file lib/invert.js", 24 | "build:umd:min": "cd lib && uglifyjs --compress --mangle --source-map --comments -o invert.min.js -- invert.js", 25 | "build": "npm run clean && npm run lint && npm run build:esm && npm run build:cjs && npm run build:es-for-umd && npm run build:umd && npm run build:umd:min", 26 | "test": "jest --verbose --no-cache", 27 | "cover": "jest --coverage --verbose --no-cache", 28 | "coveralls": "cat test/coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js -v" 29 | }, 30 | "jest": { 31 | "testEnvironment": "node", 32 | "preset": "ts-jest", 33 | "moduleFileExtensions": [ 34 | "ts", 35 | "tsx", 36 | "js", 37 | "jsx", 38 | "json", 39 | "node" 40 | ], 41 | "roots": [ 42 | "/lib", 43 | "/test" 44 | ], 45 | "testPathIgnorePatterns": [ 46 | "/node_modules/", 47 | "/backup/", 48 | "/test/coverage/", 49 | "/anim/" 50 | ], 51 | "collectCoverageFrom": [ 52 | "src/invert.ts" 53 | ], 54 | "coverageDirectory": "./test/coverage" 55 | }, 56 | "keywords": [ 57 | "color", 58 | "invert", 59 | "convert", 60 | "generate", 61 | "opposite", 62 | "hex", 63 | "rgb" 64 | ], 65 | "devDependencies": { 66 | "@types/jest": "^23.3.9", 67 | "@types/node": "^10.12.3", 68 | "coveralls": "^3.0.2", 69 | "fork-ts-checker-webpack-plugin": "^0.4.15", 70 | "jest": "^23.6.0", 71 | "jest-cli": "^23.6.0", 72 | "rimraf": "^2.6.2", 73 | "rollup": "^0.67.0", 74 | "ts-jest": "^23.10.4", 75 | "tslint": "^5.11.0", 76 | "typescript": "^3.1.6", 77 | "uglifyjs": "^2.4.11" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/invert.ts: -------------------------------------------------------------------------------- 1 | // ------------------------------- 2 | // TYPES / INTERFACES 3 | // ------------------------------- 4 | 5 | /** 6 | * RGB object type with red, green and blue components. 7 | */ 8 | export type RGB = { 9 | r: number; 10 | g: number; 11 | b: number; 12 | } 13 | /** 14 | * RGB list (array) type with red, green and blue components. 15 | */ 16 | export type RgbArray = [number, number, number]; 17 | /** 18 | * Hexadecimal representation of a color. 19 | */ 20 | export type HexColor = string; 21 | /** 22 | * Color represented as hexadecimal value or as RGB object or list. 23 | */ 24 | export type Color = RGB | RgbArray | HexColor; 25 | /** 26 | * Interface for defining black and white colors; used to amplify the contrast 27 | * of the color inversion. 28 | */ 29 | export interface BlackWhite { 30 | black: HexColor; 31 | white: HexColor; 32 | threshold?: number 33 | } 34 | 35 | // ------------------------------- 36 | // CONSTANTS 37 | // ------------------------------- 38 | 39 | const DEFAULT_THRESHOLD = Math.sqrt(1.05 * 0.05) - 0.05; 40 | const RE_HEX = /^(?:[0-9a-f]{3}){1,2}$/i; 41 | const DEFAULT_BW: BlackWhite = { 42 | black: '#000000', 43 | white: '#ffffff', 44 | threshold: DEFAULT_THRESHOLD 45 | }; 46 | 47 | // ------------------------------- 48 | // HELPER METHODS 49 | // ------------------------------- 50 | 51 | function padz(str: string, len: number = 2): string { 52 | return (new Array(len).join('0') + str).slice(-len); 53 | } 54 | 55 | function hexToRgbArray(hex: string): RgbArray { 56 | if (hex.slice(0, 1) === '#') hex = hex.slice(1); 57 | if (!RE_HEX.test(hex)) throw new Error(`Invalid HEX color: "${hex}"`); 58 | // normalize / convert 3-chars hex to 6-chars. 59 | if (hex.length === 3) { 60 | hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; 61 | } 62 | return [ 63 | parseInt(hex.slice(0, 2), 16), // r 64 | parseInt(hex.slice(2, 4), 16), // g 65 | parseInt(hex.slice(4, 6), 16) // b 66 | ]; 67 | } 68 | 69 | function toRGB(c: RgbArray): RGB { 70 | return { r: c[0], g: c[1], b: c[2] }; 71 | } 72 | 73 | function toRgbArray(c: Color): RgbArray { 74 | if (!c) throw new Error('Invalid color value'); 75 | if (Array.isArray(c)) return c as RgbArray; 76 | return typeof c === 'string' ? hexToRgbArray(c) : [c.r, c.g, c.b]; 77 | } 78 | 79 | // http://stackoverflow.com/a/3943023/112731 80 | function getLuminance(c: RgbArray): number { 81 | let i, x; 82 | const a = []; // so we don't mutate 83 | for (i = 0; i < c.length; i++) { 84 | x = c[i] / 255; 85 | a[i] = x <= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4); 86 | } 87 | return 0.2126 * a[0] + 0.7152 * a[1] + 0.0722 * a[2]; 88 | } 89 | 90 | function invertToBW(color, bw: BlackWhite | boolean, asArr?: boolean): RgbArray | HexColor { 91 | const options = (bw === true) 92 | ? DEFAULT_BW 93 | : Object.assign({}, DEFAULT_BW, bw); 94 | return getLuminance(color) > options.threshold 95 | ? (asArr ? hexToRgbArray(options.black) : options.black) 96 | : (asArr ? hexToRgbArray(options.white) : options.white); 97 | } 98 | 99 | // ------------------------------- 100 | // PUBLIC MEMBERS 101 | // ------------------------------- 102 | 103 | /** 104 | * Generates inverted (opposite) version of the given color. 105 | * @param {Color} color - Color to be inverted. 106 | * @param {BlackWhite|boolean} [bw=false] - Whether to amplify the inversion to 107 | * black or white. Provide an object to customize black/white colors. 108 | * @returns {HexColor} - Hexadecimal representation of the inverted color. 109 | */ 110 | function invert(color: Color, bw: BlackWhite | boolean = false): HexColor { 111 | color = toRgbArray(color); 112 | if (bw) return invertToBW(color, bw) as HexColor; 113 | return '#' + color.map(c => padz((255 - c).toString(16))).join(''); 114 | } 115 | 116 | /** 117 | * Utility methods to generate inverted version of a color. 118 | * @namespace 119 | */ 120 | namespace invert { 121 | 122 | /** 123 | * Generates inverted (opposite) version of the given color, as a RGB object. 124 | * @alias invert.asRgbObject 125 | * @param {Color} color - Color to be inverted. 126 | * @param {BlackWhite|boolean} [bw] - Whether to amplify the inversion to 127 | * black or white. Provide an object to customize black/white colors. 128 | * @returns {RGB} - RGB object representation of the inverted color. 129 | */ 130 | export function asRGB(color: Color, bw?: BlackWhite | boolean): RGB { 131 | color = toRgbArray(color); 132 | const list: RgbArray = bw 133 | ? invertToBW(color, bw, true) as RgbArray 134 | : color.map(c => 255 - c) as RgbArray; 135 | return toRGB(list); 136 | } 137 | 138 | /** 139 | * Generates inverted (opposite) version of the given color, as a RGB array. 140 | * @param {Color} color - Color to be inverted. 141 | * @param {BlackWhite|boolean} [bw] - Whether to amplify the inversion to 142 | * black or white. Provide an object to customize black/white colors. 143 | * @returns {RGB} - RGB array representation of the inverted color. 144 | */ 145 | export function asRgbArray(color: Color, bw?: BlackWhite | boolean): RgbArray { 146 | color = toRgbArray(color); 147 | return bw 148 | ? invertToBW(color, bw, true) as RgbArray 149 | : color.map(c => 255 - c) as RgbArray; 150 | } 151 | 152 | /** 153 | * Default luminance threshold used for amplifying inversion to black and 154 | * white. 155 | * @type {number} 156 | */ 157 | export const defaultThreshold = DEFAULT_THRESHOLD; 158 | 159 | /** 160 | * Alias of `.asRGB()` 161 | */ 162 | export const asRgbObject = asRGB; 163 | } 164 | 165 | // ------------------------------- 166 | // EXPORT 167 | // ------------------------------- 168 | 169 | export default invert; 170 | -------------------------------------------------------------------------------- /test/anim/animation.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onury/invert-color/026ad954b592b1fc9c7428206dfdf5110ff797be/test/anim/animation.psd -------------------------------------------------------------------------------- /test/anim/invert-animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onury/invert-color/026ad954b592b1fc9c7428206dfdf5110ff797be/test/anim/invert-animation.gif -------------------------------------------------------------------------------- /test/browser/invert.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | invert-color 6 | 7 | 8 | 9 | 14 | 15 | 16 | 17 | Run npm test for actual tests.
This is just to check whether bundling works.
18 |



19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /test/browser/invert.require.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | invert-color (require) 6 | 7 | 8 | 9 | 14 | 15 | 16 | 17 | Run npm test for actual tests.
This is just to check whether bundling works.
18 |



19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /test/browser/require.js: -------------------------------------------------------------------------------- 1 | /** vim: et:ts=4:sw=4:sts=4 2 | * @license RequireJS 2.3.6 Copyright jQuery Foundation and other contributors. 3 | * Released under MIT license, https://github.com/requirejs/requirejs/blob/master/LICENSE 4 | */ 5 | var requirejs,require,define;!function(global,setTimeout){var req,s,head,baseElement,dataMain,src,interactiveScript,currentlyAddingScript,mainScript,subPath,version="2.3.6",commentRegExp=/\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/gm,cjsRequireRegExp=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,jsSuffixRegExp=/\.js$/,currDirRegExp=/^\.\//,op=Object.prototype,ostring=op.toString,hasOwn=op.hasOwnProperty,isBrowser=!("undefined"==typeof window||"undefined"==typeof navigator||!window.document),isWebWorker=!isBrowser&&"undefined"!=typeof importScripts,readyRegExp=isBrowser&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,defContextName="_",isOpera="undefined"!=typeof opera&&"[object Opera]"===opera.toString(),contexts={},cfg={},globalDefQueue=[],useInteractive=!1;function commentReplace(e,t){return t||""}function isFunction(e){return"[object Function]"===ostring.call(e)}function isArray(e){return"[object Array]"===ostring.call(e)}function each(e,t){var i;if(e)for(i=0;i { 13 | let args = test[1]; 14 | let result = invert(...args); 15 | let c = result.toLowerCase() === test[0].toLowerCase() ? 'green' : 'red'; 16 | html.push(`
invert(${args.join(', ')}) = ${result}
`); 17 | }); 18 | return html.join(''); 19 | }, 20 | 21 | output(invert) { 22 | const box = document.getElementById('box'); 23 | const type = Object.prototype.toString.call(invert).match(/\s(\w+)/i)[1].toLowerCase(); 24 | if (type === 'function') { 25 | box.innerHTML = simpleTest.run(invert); 26 | } else if (type === 'object') { 27 | box.innerHTML = '
Using default export: invert.default

'; 28 | box.innerHTML += simpleTest.run(invert.default); 29 | } else { 30 | box.innerHTML = `
type(invert) === ${object}
`; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /test/browser/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #e4e4e4; 3 | text-align: center; 4 | margin-top: 50px; 5 | font-family: Cousine, "Fira Mono", monospace; 6 | font-size: 16px; 7 | line-height: 1.7em; 8 | } 9 | code { 10 | font-size: 14.5px; 11 | background: rgb(48, 43, 54); 12 | color: rgb(255, 0, 119); 13 | padding: 2px 5px 3px 5px; 14 | border-radius: 3px; 15 | } -------------------------------------------------------------------------------- /test/import.test.ts: -------------------------------------------------------------------------------- 1 | import invert from '..'; 2 | 3 | describe('test: import / require', () => { 4 | 5 | function checkObj(obj) { 6 | expect(typeof obj).toEqual('function'); 7 | expect(typeof obj.defaultThreshold).toEqual('number'); 8 | expect(typeof obj.asRGB).toEqual('function'); 9 | expect(typeof obj.asRgbArray).toEqual('function'); 10 | expect(obj(('#000'))).toEqual('#ffffff'); 11 | } 12 | 13 | test('TS » import invert from', () => { 14 | let err = null; 15 | try { 16 | checkObj(invert); 17 | } catch (e) { 18 | err = e; 19 | } 20 | expect(err).toEqual(null); 21 | }); 22 | 23 | test('UMD » import(\'../lib/invert\') » default', async () => { 24 | expect.assertions(6); 25 | let err = null; 26 | try { 27 | const inv = await import('../lib/invert'); 28 | checkObj(inv.default); 29 | } catch (e) { 30 | err = e; 31 | } 32 | expect(err).toEqual(null); 33 | }); 34 | 35 | test('UMD » require(\'../lib/invert\')', () => { 36 | let err = null; 37 | try { 38 | const inv = require('../lib/invert'); 39 | checkObj(inv); 40 | } catch (e) { 41 | err = e; 42 | } 43 | expect(err).toEqual(null); 44 | }); 45 | 46 | test('UMD » require(\'../lib/invert.min\')', () => { 47 | let err = null; 48 | try { 49 | const inv = require('../lib/invert.min'); 50 | checkObj(inv); 51 | } catch (e) { 52 | err = e; 53 | } 54 | expect(err).toEqual(null); 55 | }); 56 | 57 | test('CJS » require(\'../lib/cjs/invert\') » default', () => { 58 | let err = null; 59 | try { 60 | const inv = require('../lib/cjs/invert'); 61 | checkObj(inv.default); 62 | } catch (e) { 63 | err = e; 64 | } 65 | expect(err).toEqual(null); 66 | }); 67 | 68 | }); -------------------------------------------------------------------------------- /test/unit.test.ts: -------------------------------------------------------------------------------- 1 | import invert, { RGB, RgbArray, HexColor, BlackWhite } from '../src/invert'; 2 | const { defaultThreshold } = invert; 3 | 4 | /** 5 | * Test Suite 6 | */ 7 | describe('test: invert-color', () => { 8 | 9 | const A_BLACK: RgbArray = [0, 0, 0]; 10 | const A_WHITE: RgbArray = [255, 255, 255]; 11 | const O_BLACK: RGB = { r: 0, g: 0, b: 0 }; 12 | const O_WHITE: RGB = { r: 255, g: 255, b: 255 }; 13 | const CUSTOM_BLACK: HexColor = '#303030'; 14 | const CUSTOM_WHITE: HexColor = '#fafafa'; 15 | const A_CUSTOM_BLACK: RgbArray = [48, 48, 48]; 16 | const O_CUSTOM_BLACK: RGB = { r: 48, g: 48, b: 48 }; 17 | const A_CUSTOM_WHITE: RgbArray = [250, 250, 250]; 18 | const O_CUSTOM_WHITE: RGB = { r: 250, g: 250, b: 250 }; 19 | const CUSTOM_BW_COLORS: BlackWhite = { 20 | black: CUSTOM_BLACK, 21 | white: CUSTOM_WHITE 22 | }; 23 | 24 | test('invert & match photoshop inverted colors', () => { 25 | // ORIGINAL PHOTOSHOP 26 | // COLORS INVERTED 27 | // --------------------------------------------- 28 | expect(invert('#201395')).toEqual('#dfec6a'); 29 | expect(invert('#840133')).toEqual('#7bfecc'); 30 | expect(invert('#6ec6c8')).toEqual('#913937'); 31 | expect(invert('#7fa1d3')).toEqual('#805e2c'); 32 | expect(invert('#e0c04e')).toEqual('#1f3fb1'); 33 | expect(invert('#3ad673')).toEqual('#c5298c'); 34 | expect(invert('#edffe7')).toEqual('#120018'); 35 | expect(invert('#a8f2f0')).toEqual('#570d0f'); 36 | expect(invert('#da6aaa')).toEqual('#259555'); 37 | expect(invert('#f9c6be')).toEqual('#063941'); 38 | expect(invert('#2c2ea2')).toEqual('#d3d15d'); 39 | expect(invert('#53456a')).toEqual('#acba95'); 40 | expect(invert('#ab1b77')).toEqual('#54e488'); 41 | expect(invert('#9288a4')).toEqual('#6d775b'); 42 | expect(invert('#cf4a78')).toEqual('#30b587'); 43 | expect(invert('#463069')).toEqual('#b9cf96'); 44 | expect(invert('#ac6d63')).toEqual('#53929c'); 45 | expect(invert('#be5a33')).toEqual('#41a5cc'); 46 | expect(invert('#a07c96')).toEqual('#5f8369'); 47 | expect(invert('#710cd1')).toEqual('#8ef32e'); 48 | expect(invert('#676693')).toEqual('#98996c'); 49 | expect(invert('#230be2')).toEqual('#dcf41d'); 50 | expect(invert('#9481a4')).toEqual('#6b7e5b'); 51 | expect(invert('#490cf8')).toEqual('#b6f307'); 52 | expect(invert('#389847')).toEqual('#c767b8'); 53 | expect(invert('#4898c2')).toEqual('#b7673d'); 54 | expect(invert('#71d449')).toEqual('#8e2bb6'); 55 | expect(invert('#61ad88')).toEqual('#9e5277'); 56 | expect(invert('#bd3a5b')).toEqual('#42c5a4'); 57 | expect(invert('#e32ac1')).toEqual('#1cd53e'); 58 | expect(invert('#ac3ba9')).toEqual('#53c456'); 59 | expect(invert('#c78ef0')).toEqual('#38710f'); 60 | expect(invert('#48bdda')).toEqual('#b74225'); 61 | expect(invert('#7855ae')).toEqual('#87aa51'); 62 | expect(invert('#bf9845')).toEqual('#4067ba'); 63 | expect(invert('#b2b766')).toEqual('#4d4899'); 64 | expect(invert('#6ca3d9')).toEqual('#935c26'); 65 | expect(invert('#b0af42')).toEqual('#4f50bd'); 66 | expect(invert('#9fec76')).toEqual('#601389'); 67 | expect(invert('#de79f1')).toEqual('#21860e'); 68 | expect(invert('#5b7b0a')).toEqual('#a484f5'); 69 | expect(invert('#27a5ec')).toEqual('#d85a13'); 70 | expect(invert('#a3375e')).toEqual('#5cc8a1'); 71 | expect(invert('#414176')).toEqual('#bebe89'); 72 | expect(invert('#cde92f')).toEqual('#3216d0'); 73 | expect(invert('#d13eb4')).toEqual('#2ec14b'); 74 | expect(invert('#ee7d54')).toEqual('#1182ab'); 75 | expect(invert('#35b9dc')).toEqual('#ca4623'); 76 | expect(invert('#bf137b')).toEqual('#40ec84'); 77 | expect(invert('#b7027c')).toEqual('#48fd83'); 78 | expect(invert('#000')).toEqual('#ffffff'); 79 | expect(invert('#fff')).toEqual('#000000'); 80 | expect(invert('#282b35')).toEqual('#d7d4ca'); 81 | expect(invert('#951a9d')).toEqual('#6ae562'); 82 | expect(invert('#566394')).toEqual('#a99c6b'); 83 | }); 84 | 85 | test('accept [r,g,b] or {r,g,b}', () => { 86 | expect(invert([0, 0, 0])).toEqual('#ffffff'); 87 | expect(invert([255, 255, 255])).toEqual('#000000'); 88 | expect(invert([249, 119, 121])).toEqual('#068886'); 89 | expect(invert([69, 191, 189])).toEqual('#ba4042'); 90 | expect(invert([191, 19, 123])).toEqual('#40ec84'); 91 | 92 | expect(invert({ r: 0, g: 0, b: 0 })).toEqual('#ffffff'); 93 | expect(invert({ r: 255, g: 255, b: 255 })).toEqual('#000000'); 94 | expect(invert({ r: 249, g: 119, b: 121 })).toEqual('#068886'); 95 | expect(invert({ r: 69, g: 191, b: 189 })).toEqual('#ba4042'); 96 | expect(invert({ r: 191, g: 19, b: 123 })).toEqual('#40ec84'); 97 | }); 98 | 99 | test('invert to black or white', () => { 100 | expect(invert('#631746', true)).toEqual('#ffffff'); 101 | expect(invert('#655c42', true)).toEqual('#ffffff'); 102 | expect(invert('#166528', true)).toEqual('#ffffff'); 103 | expect(invert('#4c2946', true)).toEqual('#ffffff'); 104 | expect(invert('#002d26', true)).toEqual('#ffffff'); 105 | expect(invert('#e71398', true)).toEqual('#000000'); 106 | expect(invert('#3ab3af', true)).toEqual('#000000'); 107 | expect(invert('#76ff98', true)).toEqual('#000000'); 108 | expect(invert('#bbb962', true)).toEqual('#000000'); 109 | expect(invert('#52838b', true)).toEqual('#000000'); 110 | expect(invert('#000', true)).toEqual('#ffffff'); 111 | expect(invert('#fff', true)).toEqual('#000000'); 112 | expect(invert('#ffffff', true)).toEqual('#000000'); 113 | }); 114 | 115 | test('invert to custom black and white colors', () => { 116 | expect(invert('#631746', CUSTOM_BW_COLORS)).toEqual(CUSTOM_WHITE); 117 | expect(invert('#655c42', CUSTOM_BW_COLORS)).toEqual(CUSTOM_WHITE); 118 | expect(invert('#166528', CUSTOM_BW_COLORS)).toEqual(CUSTOM_WHITE); 119 | expect(invert('#4c2946', CUSTOM_BW_COLORS)).toEqual(CUSTOM_WHITE); 120 | expect(invert('#002d26', CUSTOM_BW_COLORS)).toEqual(CUSTOM_WHITE); 121 | expect(invert('#e71398', CUSTOM_BW_COLORS)).toEqual(CUSTOM_BLACK); 122 | expect(invert('#3ab3af', CUSTOM_BW_COLORS)).toEqual(CUSTOM_BLACK); 123 | expect(invert('#76ff98', CUSTOM_BW_COLORS)).toEqual(CUSTOM_BLACK); 124 | expect(invert('#bbb962', CUSTOM_BW_COLORS)).toEqual(CUSTOM_BLACK); 125 | expect(invert('#52838b', CUSTOM_BW_COLORS)).toEqual(CUSTOM_BLACK); 126 | expect(invert('#000', CUSTOM_BW_COLORS)).toEqual(CUSTOM_WHITE); 127 | expect(invert('#fff', CUSTOM_BW_COLORS)).toEqual(CUSTOM_BLACK); 128 | }); 129 | 130 | test('support true/false/object for black and white parameter', () => { 131 | expect(invert('#201395', true)).toEqual('#ffffff'); 132 | expect(invert('#201395', false)).toEqual('#dfec6a'); 133 | expect(invert('#201395', CUSTOM_BW_COLORS)).toEqual(CUSTOM_WHITE); 134 | }); 135 | 136 | test('invert to/from array/object to B/W', () => { 137 | // this test also checks for object mutation 138 | 139 | // hex as array 140 | expect(invert.asRgbArray('#631746', true)).toEqual(A_WHITE); 141 | expect(invert.asRgbArray('#655c42', true)).toEqual(A_WHITE); 142 | expect(invert.asRgbArray('#e71398', true)).toEqual(A_BLACK); 143 | 144 | expect(invert.asRgbArray('#282b35', false)).toEqual([215, 212, 202]); 145 | 146 | // hex as object 147 | expect(invert.asRGB('#4c2946', true)).toEqual(O_WHITE); 148 | expect(invert.asRGB('#002d26', true)).toEqual(O_WHITE); 149 | expect(invert.asRGB('#76ff98', true)).toEqual(O_BLACK); 150 | 151 | expect(invert.asRGB('#a8f2f0', false)).toEqual({ b: 15, g: 13, r: 87 }); 152 | expect(invert.asRGB('#282b35', false)).toEqual({ r: 215, g: 212, b: 202 }); 153 | 154 | // array as array 155 | expect(invert.asRgbArray(A_WHITE, true)).toEqual(A_BLACK); 156 | expect(invert.asRgbArray(A_BLACK, true)).toEqual(A_WHITE); 157 | 158 | // object as array 159 | expect(invert.asRgbArray(O_BLACK, true)).toEqual(A_WHITE); 160 | expect(invert.asRgbArray(O_WHITE, true)).toEqual(A_BLACK); 161 | 162 | // object as object 163 | expect(invert.asRGB(O_BLACK, true)).toEqual(O_WHITE); 164 | expect(invert.asRGB(O_WHITE, true)).toEqual(O_BLACK); 165 | }); 166 | 167 | test('invert to/from array/object to custom B/W', () => { 168 | // this test also checks for object mutation 169 | 170 | // hex as array 171 | expect(invert.asRgbArray('#631746', CUSTOM_BW_COLORS)).toEqual(A_CUSTOM_WHITE); 172 | expect(invert.asRgbArray('#655c42', CUSTOM_BW_COLORS)).toEqual(A_CUSTOM_WHITE); 173 | expect(invert.asRgbArray('#e71398', CUSTOM_BW_COLORS)).toEqual(A_CUSTOM_BLACK); 174 | 175 | // hex as object 176 | expect(invert.asRGB('#4c2946', CUSTOM_BW_COLORS)).toEqual(O_CUSTOM_WHITE); 177 | expect(invert.asRGB('#002d26', CUSTOM_BW_COLORS)).toEqual(O_CUSTOM_WHITE); 178 | expect(invert.asRGB('#76ff98', CUSTOM_BW_COLORS)).toEqual(O_CUSTOM_BLACK); 179 | 180 | // array as array 181 | expect(invert.asRgbArray(A_WHITE, CUSTOM_BW_COLORS)).toEqual(A_CUSTOM_BLACK); 182 | expect(invert.asRgbArray(A_BLACK, CUSTOM_BW_COLORS)).toEqual(A_CUSTOM_WHITE); 183 | 184 | // object as array 185 | expect(invert.asRgbArray(O_BLACK, CUSTOM_BW_COLORS)).toEqual(A_CUSTOM_WHITE); 186 | expect(invert.asRgbArray(O_WHITE, CUSTOM_BW_COLORS)).toEqual(A_CUSTOM_BLACK); 187 | 188 | // object as object 189 | expect(invert.asRGB(O_BLACK, CUSTOM_BW_COLORS)).toEqual(O_CUSTOM_WHITE); 190 | expect(invert.asRGB(O_WHITE, CUSTOM_BW_COLORS)).toEqual(O_CUSTOM_BLACK); 191 | }); 192 | 193 | test('invert to custom B/W with custom threshold', () => { 194 | const color = '#929292'; 195 | const threshold = t => invert(color, { black: CUSTOM_BLACK, white: CUSTOM_WHITE, threshold: t }); 196 | const withDefaultThreshold = threshold(defaultThreshold); 197 | expect(withDefaultThreshold).toEqual(invert(color, CUSTOM_BW_COLORS)); // no threshold defined 198 | expect(withDefaultThreshold).toEqual(CUSTOM_BLACK); 199 | expect(threshold(0)).toEqual(CUSTOM_BLACK); 200 | expect(threshold(0.1)).toEqual(CUSTOM_BLACK); 201 | expect(threshold(0.2)).toEqual(CUSTOM_BLACK); 202 | expect(threshold(0.24)).toEqual(CUSTOM_BLACK); 203 | expect(threshold(0.28)).toEqual(CUSTOM_BLACK); 204 | expect(threshold(0.3)).toEqual(CUSTOM_WHITE); 205 | expect(threshold(0.5)).toEqual(CUSTOM_WHITE); 206 | expect(threshold(1)).toEqual(CUSTOM_WHITE); 207 | }); 208 | 209 | test('modulo exceeding RGB comps', () => { 210 | expect(invert([300, 300, 300])).toEqual('#2d2d2d'); 211 | expect(invert({ r: -46, g: -46, b: -46 })).toEqual('#2d2d2d'); 212 | }); 213 | 214 | test('throw on invalid hex', () => { 215 | expect(() => { invert('#'); }).toThrow(); 216 | expect(() => { invert('12'); }).toThrow(); 217 | expect(() => { invert('#ff'); }).toThrow(); 218 | expect(() => { invert('#1234'); }).toThrow(); 219 | expect(() => { invert('12345'); }).toThrow(); 220 | expect(() => { invert('1234567'); }).toThrow(); 221 | expect(() => { invert('1#3'); }).toThrow(); 222 | expect(() => { invert('##631746', true); }).toThrow(); 223 | }); 224 | 225 | test('throw on other invalid color values', () => { 226 | expect(() => { invert(null, true); }).toThrow('Invalid color value'); 227 | }); 228 | 229 | test('not throw for valid hex with/out # prefix', () => { 230 | expect(() => { invert('123'); }).not.toThrow(); 231 | expect(() => { invert('123456'); }).not.toThrow(); 232 | expect(() => { invert('#aba'); }).not.toThrow(); 233 | }); 234 | 235 | }); 236 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "allowJs": false, 5 | "allowUnreachableCode": false, 6 | "allowUnusedLabels": false, 7 | "alwaysStrict": true, 8 | "charset": "utf8", 9 | "checkJs": false, 10 | "declaration": true, 11 | "declarationDir": "./lib", 12 | "diagnostics": false, 13 | "disableSizeLimit": false, 14 | "downlevelIteration": false, 15 | "emitBOM": false, 16 | "emitDeclarationOnly": false, 17 | "emitDecoratorMetadata": true, 18 | "esModuleInterop": true, 19 | "experimentalDecorators": true, 20 | "forceConsistentCasingInFileNames": false, 21 | "importHelpers": false, 22 | "inlineSourceMap": false, 23 | "inlineSources": false, 24 | "isolatedModules": false, 25 | "lib": [ 26 | "es2015", 27 | "es2016", 28 | "es2017", 29 | "dom" 30 | ], 31 | "listEmittedFiles": false, 32 | "listFiles": false, 33 | "locale": "en-us", 34 | "maxNodeModuleJsDepth": 0, 35 | "module": "ES2015", 36 | "moduleResolution": "Node", 37 | "noEmit": false, 38 | "noEmitHelpers": false, 39 | "noEmitOnError": false, 40 | "noErrorTruncation": false, 41 | "noFallthroughCasesInSwitch": true, 42 | "noImplicitAny": false, 43 | "noImplicitReturns": true, 44 | "noImplicitThis": false, 45 | "noImplicitUseStrict": false, 46 | "noLib": false, 47 | "noResolve": false, 48 | "noStrictGenericChecks": false, 49 | "noUnusedLocals": true, 50 | "noUnusedParameters": false, 51 | "outDir": "./src", 52 | "preserveConstEnums": false, 53 | "preserveSymlinks": false, 54 | "preserveWatchOutput": false, 55 | "pretty": true, 56 | "removeComments": false, 57 | "skipLibCheck": false, 58 | "sourceMap": false, 59 | "strict": false, 60 | "strictFunctionTypes": false, 61 | "strictPropertyInitialization": false, 62 | "strictNullChecks": false, 63 | "stripInternal": false, 64 | "suppressExcessPropertyErrors": false, 65 | "suppressImplicitAnyIndexErrors": true, 66 | "target": "ES2015", // "es5" 67 | "traceResolution": false, 68 | "typeRoots": [ 69 | "./node_modules/@types", 70 | "./node_modules" 71 | ], 72 | "types": ["node", "jest"] 73 | }, 74 | "include": [ 75 | "./src/**/*.ts" 76 | ], 77 | "exclude": [ 78 | "./node_modules", 79 | "./lib", 80 | "./dev", 81 | "./docs", 82 | "./test", 83 | "./tasks", 84 | "./backup" 85 | ] 86 | } 87 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [true, "check-space"], 5 | "indent": [true, "spaces"], 6 | "no-duplicate-variable": true, 7 | "no-eval": true, 8 | "no-internal-module": true, 9 | "no-trailing-whitespace": true, 10 | "no-var-keyword": true, 11 | "one-line": [true, "check-open-brace", "check-whitespace"], 12 | "quotemark": [true, "single"], 13 | "semicolon": false, 14 | "triple-equals": [true, "allow-null-check"], 15 | "typedef-whitespace": [true, { 16 | "call-signature": "nospace", 17 | "index-signature": "nospace", 18 | "parameter": "nospace", 19 | "property-declaration": "nospace", 20 | "variable-declaration": "nospace" 21 | }], 22 | "variable-name": [true, "ban-keywords"], 23 | "whitespace": [ 24 | true, 25 | "check-branch", 26 | "check-decl", 27 | "check-operator", 28 | "check-separator" 29 | ] 30 | }, 31 | "jsRules": { 32 | "indent": [true, "spaces"], 33 | "no-duplicate-variable": true, 34 | "no-eval": true, 35 | "no-trailing-whitespace": true, 36 | "one-line": [true, "check-open-brace", "check-whitespace"], 37 | "quotemark": [true, "double"], 38 | "semicolon": false, 39 | "triple-equals": [true, "allow-null-check"], 40 | "variable-name": [true, "ban-keywords"], 41 | "whitespace": [true, 42 | "check-branch", 43 | "check-decl", 44 | "check-operator", 45 | "check-separator", 46 | "check-type" 47 | ] 48 | } 49 | } 50 | --------------------------------------------------------------------------------