console-toolkit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +34 -0
- package/README.md +92 -0
- package/package.json +59 -0
- package/src/alphanumeric/arrows.js +31 -0
- package/src/alphanumeric/fractions.js +82 -0
- package/src/alphanumeric/number-formatters.js +131 -0
- package/src/alphanumeric/roman.js +86 -0
- package/src/alphanumeric/unicode-cultural-numbers.js +68 -0
- package/src/alphanumeric/unicode-letters.js +63 -0
- package/src/alphanumeric/unicode-numbers.js +75 -0
- package/src/alphanumeric/utils.js +44 -0
- package/src/ansi/csi.js +67 -0
- package/src/ansi/index.js +20 -0
- package/src/ansi/sgr-constants.js +99 -0
- package/src/ansi/sgr-state.js +398 -0
- package/src/ansi/sgr.js +214 -0
- package/src/box.js +253 -0
- package/src/charts/bars/block-frac-grouped.js +6 -0
- package/src/charts/bars/block-frac.js +36 -0
- package/src/charts/bars/block-grouped.js +6 -0
- package/src/charts/bars/block.js +43 -0
- package/src/charts/bars/draw-grouped.js +39 -0
- package/src/charts/bars/draw-stacked.js +24 -0
- package/src/charts/bars/frac-grouped.js +33 -0
- package/src/charts/bars/plain-grouped.js +6 -0
- package/src/charts/bars/plain.js +63 -0
- package/src/charts/columns/block-frac-grouped.js +6 -0
- package/src/charts/columns/block-frac.js +30 -0
- package/src/charts/columns/block-grouped.js +6 -0
- package/src/charts/columns/block.js +37 -0
- package/src/charts/columns/draw-grouped.js +48 -0
- package/src/charts/columns/draw-stacked.js +31 -0
- package/src/charts/columns/frac-grouped.js +27 -0
- package/src/charts/columns/plain-grouped.js +6 -0
- package/src/charts/columns/plain.js +39 -0
- package/src/charts/themes/default.js +12 -0
- package/src/charts/themes/rainbow-reversed.js +5 -0
- package/src/charts/themes/rainbow.js +8 -0
- package/src/charts/utils.js +75 -0
- package/src/draw-block-frac.js +33 -0
- package/src/draw-block.js +55 -0
- package/src/meta.js +41 -0
- package/src/output/show.js +40 -0
- package/src/output/updater.js +82 -0
- package/src/output/writer.js +131 -0
- package/src/panel.js +748 -0
- package/src/plot/bitmap.js +108 -0
- package/src/plot/draw-line.js +26 -0
- package/src/plot/draw-rect.js +216 -0
- package/src/plot/index.js +24 -0
- package/src/plot/to-quads.js +32 -0
- package/src/spinner/index.js +8 -0
- package/src/spinner/spin.js +51 -0
- package/src/spinner/spinner.js +75 -0
- package/src/spinner/spinners.js +65 -0
- package/src/strings.js +72 -0
- package/src/style.js +620 -0
- package/src/symbols.js +131 -0
- package/src/table/draw-borders.js +87 -0
- package/src/table/index.js +7 -0
- package/src/table/table.js +330 -0
- package/src/themes/blocks/unicode-half.js +9 -0
- package/src/themes/blocks/unicode-thin.js +9 -0
- package/src/themes/lines/ascii-compact.js +11 -0
- package/src/themes/lines/ascii-dots.js +9 -0
- package/src/themes/lines/ascii-girder.js +9 -0
- package/src/themes/lines/ascii-github.js +11 -0
- package/src/themes/lines/ascii-reddit.js +11 -0
- package/src/themes/lines/ascii-rounded.js +11 -0
- package/src/themes/lines/ascii.js +11 -0
- package/src/themes/lines/unicode-bold.js +15 -0
- package/src/themes/lines/unicode-rounded.js +15 -0
- package/src/themes/lines/unicode.js +15 -0
- package/src/themes/utils.js +38 -0
- package/src/turtle/draw-line-art.js +46 -0
- package/src/turtle/draw-unicode.js +33 -0
- package/src/turtle/index.js +12 -0
- package/src/turtle/turtle.js +286 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
This library is available under the terms of the modified BSD license. No external contributions
|
|
2
|
+
are allowed under licenses which are fundamentally incompatible with the BSD license that this library is distributed under.
|
|
3
|
+
|
|
4
|
+
The text of the BSD license is reproduced below.
|
|
5
|
+
|
|
6
|
+
-------------------------------------------------------------------------------
|
|
7
|
+
The "New" BSD License:
|
|
8
|
+
**********************
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2005-2024, Eugene Lazutkin
|
|
11
|
+
All rights reserved.
|
|
12
|
+
|
|
13
|
+
Redistribution and use in source and binary forms, with or without
|
|
14
|
+
modification, are permitted provided that the following conditions are met:
|
|
15
|
+
|
|
16
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
17
|
+
list of conditions and the following disclaimer.
|
|
18
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
19
|
+
this list of conditions and the following disclaimer in the documentation
|
|
20
|
+
and/or other materials provided with the distribution.
|
|
21
|
+
* Neither the name of Eugene Lazutkin nor the names of other contributors
|
|
22
|
+
may be used to endorse or promote products derived from this software
|
|
23
|
+
without specific prior written permission.
|
|
24
|
+
|
|
25
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
26
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
27
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
28
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
|
29
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
30
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
31
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
32
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
33
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
34
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# console-toolkit [![NPM version][npm-img]][npm-url]
|
|
2
|
+
|
|
3
|
+
[npm-img]: https://img.shields.io/npm/v/console-toolkit.svg
|
|
4
|
+
[npm-url]: https://npmjs.org/package/console-toolkit
|
|
5
|
+
|
|
6
|
+
`console-toolkit` is a set of tools to create rich CLI-based applications. It provides:
|
|
7
|
+
|
|
8
|
+
* Styles based on [ANSI escape sequences](https://en.wikipedia.org/wiki/ANSI_escape_code):
|
|
9
|
+
* [SGR](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR): colors and text styles
|
|
10
|
+
* [CSI](https://en.wikipedia.org/wiki/ANSI_escape_code#CSIsection): cursor and screen control
|
|
11
|
+
* Bitmap graphics
|
|
12
|
+
* Vector graphics based on [Turtle graphics](https://en.wikipedia.org/wiki/Turtle_graphics)
|
|
13
|
+
* Curated sets of Unicode symbols
|
|
14
|
+
* Tables with themes
|
|
15
|
+
* Bar and column charts with themes
|
|
16
|
+
* Various helpers and examples
|
|
17
|
+
|
|
18
|
+
## Visual examples
|
|
19
|
+
|
|
20
|
+
### Memory watcher
|
|
21
|
+
|
|
22
|
+

|
|
23
|
+
|
|
24
|
+
### Waveform
|
|
25
|
+
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
### Table + chart
|
|
29
|
+
|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
### Turtle graphics
|
|
33
|
+
|
|
34
|
+

|
|
35
|
+
|
|
36
|
+
## Code example
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
import style, {c} from 'console-toolkit/style.js';
|
|
40
|
+
import drawChart from 'console-toolkit/charts/bars/plain.js';
|
|
41
|
+
import lineTheme from 'console-toolkit/themes/lines/unicode-rounded.js';
|
|
42
|
+
import makeTable from 'console-toolkit/table';
|
|
43
|
+
|
|
44
|
+
// styles
|
|
45
|
+
|
|
46
|
+
console.log(style.bold + 'Hello, ' + style.bright.cyan + 'world!' + style.reset.all);
|
|
47
|
+
|
|
48
|
+
console.log(style.bold.text('Hello, ') + style.bright.cyan.bold.text('world!'));
|
|
49
|
+
|
|
50
|
+
const redBg = style.bg.red;
|
|
51
|
+
console.log(redBg.bold.text('Hello, ') + redBg.bright.cyan.bold.text('world!'));
|
|
52
|
+
|
|
53
|
+
console.log(c`{{bold}}Hello, {{bright.cyan}}world!`);
|
|
54
|
+
|
|
55
|
+
// chart
|
|
56
|
+
|
|
57
|
+
const chart = drawChart([[2, 1, 2], [5, 1, 4], [1, 1], [3, 1, 3]], 50);
|
|
58
|
+
for (const line of chart) console.log(line);
|
|
59
|
+
|
|
60
|
+
// table
|
|
61
|
+
|
|
62
|
+
const tableData = [
|
|
63
|
+
['Name', 'Value'],
|
|
64
|
+
['Bill', 33],
|
|
65
|
+
['Jill', 42]
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const table = makeTable(tableData, lineTheme);
|
|
69
|
+
for (const line of table.toStrings()) console.log(line);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The output of the code is:
|
|
73
|
+
|
|
74
|
+

|
|
75
|
+
|
|
76
|
+
## Installation
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm install --save console-toolkit
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Documentation
|
|
83
|
+
|
|
84
|
+
See [wiki](https://github.com/uhop/console-toolkit/wiki) for more details.
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
BSD 3-Clause License
|
|
89
|
+
|
|
90
|
+
## Release history
|
|
91
|
+
|
|
92
|
+
- 1.0.0: *Initial release.*
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "console-toolkit",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Toolkit to produce a fancy console output (boxes, tables, charts, colors).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"module": "src/index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
"./*": "./src/*",
|
|
10
|
+
"./ansi": "./src/ansi/index.js",
|
|
11
|
+
"./plot": "./src/plot/index.js",
|
|
12
|
+
"./spinner": "./src/spinner/index.js",
|
|
13
|
+
"./table": "./src/table/index.js",
|
|
14
|
+
"./turtle": "./src/turtle/index.js",
|
|
15
|
+
"./box": "./src/box.js",
|
|
16
|
+
"./panel": "./src/panel.js",
|
|
17
|
+
"./strings": "./src/strings.js",
|
|
18
|
+
"./style": "./src/style.js"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"test": "tape6 --flags FO"
|
|
22
|
+
},
|
|
23
|
+
"github": "http://github.com/uhop/console-toolkit",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/uhop/console-toolkit.git"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"console",
|
|
30
|
+
"color",
|
|
31
|
+
"colors",
|
|
32
|
+
"box",
|
|
33
|
+
"boxes",
|
|
34
|
+
"table",
|
|
35
|
+
"tables",
|
|
36
|
+
"chart",
|
|
37
|
+
"charts",
|
|
38
|
+
"turtle",
|
|
39
|
+
"CLI",
|
|
40
|
+
"TUI"
|
|
41
|
+
],
|
|
42
|
+
"author": "Eugene Lazutkin <eugene.lazutkin@gmail.com> (https://www.lazutkin.com/)",
|
|
43
|
+
"license": "BSD-3-Clause",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/uhop/console-toolkit/issues"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/uhop/console-toolkit#readme",
|
|
48
|
+
"files": [
|
|
49
|
+
"./src"
|
|
50
|
+
],
|
|
51
|
+
"tape6": {
|
|
52
|
+
"tests": [
|
|
53
|
+
"/tests/test-*.*js"
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"tape-six": "^0.9.3"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const LEFT = 0,
|
|
2
|
+
UP = 1,
|
|
3
|
+
RIGHT = 2,
|
|
4
|
+
DOWN = 3;
|
|
5
|
+
|
|
6
|
+
const toSymbol = (...args) => args.map(x => (typeof x == 'number' ? String.fromCodePoint(x) : x));
|
|
7
|
+
const makeArrows = codePoint => toSymbol(codePoint, codePoint + 1, codePoint + 2, codePoint + 3);
|
|
8
|
+
const makeArrowsH = codePoint => toSymbol(codePoint, ' ', codePoint + 1, ' ');
|
|
9
|
+
|
|
10
|
+
// Arrows
|
|
11
|
+
|
|
12
|
+
export const simple = makeArrows(0x2190);
|
|
13
|
+
export const withStroke = makeArrowsH(0x219a);
|
|
14
|
+
export const wave = makeArrowsH(0x219c);
|
|
15
|
+
export const twoHeaded = makeArrows(0x219e);
|
|
16
|
+
export const withTail = makeArrowsH(0x21a2);
|
|
17
|
+
export const fromBar = makeArrows(0x21a4);
|
|
18
|
+
export const withLoop = makeArrowsH(0x21ab);
|
|
19
|
+
export const double = makeArrows(0x21d0);
|
|
20
|
+
export const triple = makeArrowsH(0x21da);
|
|
21
|
+
export const squiggle = makeArrowsH(0x21dc);
|
|
22
|
+
export const dashed = makeArrows(0x21e0);
|
|
23
|
+
export const toBar = makeArrowsH(0x21e4);
|
|
24
|
+
export const white = makeArrows(0x21e6);
|
|
25
|
+
export const withVStroke = makeArrowsH(0x21f7);
|
|
26
|
+
export const withDoubleVStroke = makeArrowsH(0x21fa);
|
|
27
|
+
export const openHeaded = makeArrowsH(0x21fd);
|
|
28
|
+
|
|
29
|
+
export const withBarbUp = toSymbol(0x21bc, 0x21be, 0x21c1, 0x21c3);
|
|
30
|
+
export const withBarbDown = toSymbol(0x21bd, 0x21bf, 0x21c0, 0x21c2);
|
|
31
|
+
export const doubleWithStroke = toSymbol(0x21cd, ' ', 0x21cf, ' ');
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export const fractions = [
|
|
2
|
+
[1, 7, '\u2150'],
|
|
3
|
+
[1, 9, '\u2151'],
|
|
4
|
+
[1, 10, '\u2152'],
|
|
5
|
+
[1, 3, '\u2153'],
|
|
6
|
+
[2, 3, '\u2154'],
|
|
7
|
+
[1, 5, '\u2155'],
|
|
8
|
+
[2, 5, '\u2156'],
|
|
9
|
+
[3, 5, '\u2157'],
|
|
10
|
+
[4, 5, '\u2158'],
|
|
11
|
+
[1, 6, '\u2159'],
|
|
12
|
+
[5, 6, '\u215A'],
|
|
13
|
+
[1, 8, '\u215B'],
|
|
14
|
+
[3, 8, '\u215C'],
|
|
15
|
+
[5, 8, '\u215D'],
|
|
16
|
+
[7, 8, '\u215E'],
|
|
17
|
+
[0, 3, '\u2189'],
|
|
18
|
+
[1, 4, '\u00BC'],
|
|
19
|
+
[1, 2, '\u00BD'],
|
|
20
|
+
[3, 4, '\u00BE']
|
|
21
|
+
]
|
|
22
|
+
.map(x => ({numerator: x[0], denominator: x[1], symbol: x[2], value: x[0] / x[1]}))
|
|
23
|
+
.sort((a, b) => a.value - b.value);
|
|
24
|
+
|
|
25
|
+
const pick = denominator => fractions.filter(x => !(denominator % x.denominator));
|
|
26
|
+
|
|
27
|
+
export const thirds = pick(3);
|
|
28
|
+
export const quarters = [{numerator: 0, denominator: 4, symbol: '0', value: 0}, ...pick(4)];
|
|
29
|
+
export const fifths = [{numerator: 0, denominator: 5, symbol: '0', value: 0}, ...pick(5)];
|
|
30
|
+
export const sixths = pick(6);
|
|
31
|
+
export const eighths = [{numerator: 0, denominator: 8, symbol: '0', value: 0}, ...pick(8)];
|
|
32
|
+
|
|
33
|
+
export {quarters as fourths};
|
|
34
|
+
|
|
35
|
+
const binarySearch = (sortedArray, lessFn, from = 0, to = sortedArray.length) => {
|
|
36
|
+
let i = from,
|
|
37
|
+
j = to;
|
|
38
|
+
while (j - i > 0) {
|
|
39
|
+
const m = (i + j) >> 1;
|
|
40
|
+
if (lessFn(sortedArray[m])) i = m + 1;
|
|
41
|
+
else j = m;
|
|
42
|
+
}
|
|
43
|
+
return j;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const one = {value: 1, symbol: '1'};
|
|
47
|
+
|
|
48
|
+
const findSymbol = (fractions, value, useFractionForZero) => {
|
|
49
|
+
if (value < 0) return '';
|
|
50
|
+
let int = Math.floor(value);
|
|
51
|
+
value -= int;
|
|
52
|
+
|
|
53
|
+
let index = binarySearch(fractions, x => x.value < value);
|
|
54
|
+
|
|
55
|
+
let chosen = fractions[0];
|
|
56
|
+
if (index === 0) return useFractionForZero && chosen.symbol !== '0' ? (int || '') + chosen.symbol : String(int);
|
|
57
|
+
|
|
58
|
+
const upper = index === fractions.length ? one : fractions[index],
|
|
59
|
+
lower = fractions[index - 1];
|
|
60
|
+
|
|
61
|
+
if (upper.value - value < value - lower.value) {
|
|
62
|
+
chosen = upper;
|
|
63
|
+
} else {
|
|
64
|
+
chosen = lower;
|
|
65
|
+
--index;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (index === 0) return useFractionForZero && chosen.symbol !== '0' ? (int || '') + chosen.symbol : String(int);
|
|
69
|
+
if (chosen.symbol === '1') return String(int + 1);
|
|
70
|
+
if (chosen.symbol === '0') return String(int);
|
|
71
|
+
|
|
72
|
+
return (int || '') + chosen.symbol;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const getFraction = (value, useFractionForZero) => findSymbol(fractions, value, useFractionForZero);
|
|
76
|
+
export const getThirds = (value, useFractionForZero) => findSymbol(thirds, value, useFractionForZero);
|
|
77
|
+
export const getQuarters = (value, useFractionForZero) => findSymbol(quarters, value, useFractionForZero);
|
|
78
|
+
export const getFifths = (value, useFractionForZero) => findSymbol(fifths, value, useFractionForZero);
|
|
79
|
+
export const getSixths = (value, useFractionForZero) => findSymbol(sixths, value, useFractionForZero);
|
|
80
|
+
export const getEighths = (value, useFractionForZero) => findSymbol(eighths, value, useFractionForZero);
|
|
81
|
+
|
|
82
|
+
export {getQuarters as getFourths};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// Initially copied from https://github.com/uhop/nano-bench
|
|
2
|
+
|
|
3
|
+
const units = ['s', 'ms', 'μs', 'ns', 'ps'],
|
|
4
|
+
unicodeUnits = ['s', '㎳', '㎲', '㎱', '㎰'];
|
|
5
|
+
|
|
6
|
+
export const prepareTimeFormat = (values, scale = 1, useUnicode) => {
|
|
7
|
+
let mx = -1000,
|
|
8
|
+
mn = 1000;
|
|
9
|
+
for (let i = 0; i < values.length; ++i) {
|
|
10
|
+
const p = Math.floor(Math.log(values[i] / scale) / Math.LN10);
|
|
11
|
+
if (isFinite(p)) {
|
|
12
|
+
if (mx < p) mx = p;
|
|
13
|
+
if (mn > p) mn = p;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (mx < mn) mn = mx = -6;
|
|
17
|
+
const digits = Math.max(mx - mn + 1, 2);
|
|
18
|
+
scale = 1 / scale;
|
|
19
|
+
// TODO: get rid of the loop below
|
|
20
|
+
let i = 0;
|
|
21
|
+
for (; mx < 0 && i < units.length - 1; ++i, mx += 3, scale *= 1000);
|
|
22
|
+
return {scale, precision: digits - mx, unit: (useUnicode ? unicodeUnits : units)[i]};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const formatTime = (value, format) => {
|
|
26
|
+
let result = (value * format.scale).toFixed(format.precision);
|
|
27
|
+
if (format.precision > 0) result = result.replace(/\.0+$/, '');
|
|
28
|
+
return result + format.unit;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const putCommasIn = (s, options) => {
|
|
32
|
+
if (s.length < 4) return s;
|
|
33
|
+
const comma = options?.comma || ',',
|
|
34
|
+
r = s.length % 3;
|
|
35
|
+
return (
|
|
36
|
+
(r ? s.slice(0, r) + comma : '') +
|
|
37
|
+
s
|
|
38
|
+
.slice(r)
|
|
39
|
+
.replace(/(\d{3})/g, '$1' + comma)
|
|
40
|
+
.slice(0, -1)
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const formatInteger = (value, options) =>
|
|
45
|
+
isNaN(value)
|
|
46
|
+
? ''
|
|
47
|
+
: (value < 0 ? '-' : options?.keepSign ? '+' : '') + putCommasIn(Math.abs(value).toFixed(0), options);
|
|
48
|
+
|
|
49
|
+
export const formatNumber = (value, options) => {
|
|
50
|
+
if (isNaN(value)) return '';
|
|
51
|
+
const decimals = options?.decimals ?? 0;
|
|
52
|
+
let sign = options?.keepSign ? '+' : '';
|
|
53
|
+
if (value < 0) {
|
|
54
|
+
value = -value;
|
|
55
|
+
sign = '-';
|
|
56
|
+
}
|
|
57
|
+
const s = value.toFixed(decimals);
|
|
58
|
+
if (decimals < 1) return sign + putCommasIn(s, options);
|
|
59
|
+
let fraction = s.slice(-decimals);
|
|
60
|
+
if (!options?.keepFractionAsIs) fraction = fraction.replace(/0+$/, '');
|
|
61
|
+
const dot = options?.dot ?? '.';
|
|
62
|
+
return sign + putCommasIn(s.slice(0, -decimals - 1), options) + (fraction ? dot + fraction : '');
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const exp = [0, 0, 0, 0, 3, 3, 6, 6, 6, 9, 9, 9, 12];
|
|
66
|
+
const abbr = '***k**M**G**T';
|
|
67
|
+
|
|
68
|
+
export const abbrNumber = (value, options) => {
|
|
69
|
+
if (isNaN(value)) return '';
|
|
70
|
+
const decimals = options?.decimals ?? 0;
|
|
71
|
+
let sign = options?.keepSign ? '+' : '';
|
|
72
|
+
if (value < 0) {
|
|
73
|
+
value = -value;
|
|
74
|
+
sign = '-';
|
|
75
|
+
}
|
|
76
|
+
if (value <= 1) {
|
|
77
|
+
let t1 = value.toString(),
|
|
78
|
+
t2 = value.toFixed(decimals);
|
|
79
|
+
return sign + (t1.length < t2.length ? t1 : t2);
|
|
80
|
+
}
|
|
81
|
+
const digits = Math.min(Math.floor(Math.log(value) / Math.LN10), exp.length - 1),
|
|
82
|
+
e = exp[digits],
|
|
83
|
+
s = Math.round(value / Math.pow(10, e - decimals)).toFixed(0);
|
|
84
|
+
if (decimals < 1) return sign + putCommasIn(s, options) + ((e && abbr.charAt(e)) || '');
|
|
85
|
+
let fraction = s.slice(-decimals);
|
|
86
|
+
if (!options?.keepFractionAsIs) fraction = fraction.replace(/0+$/, '');
|
|
87
|
+
const dot = options?.dot ?? '.';
|
|
88
|
+
return (
|
|
89
|
+
sign +
|
|
90
|
+
putCommasIn(s.slice(0, -decimals), options) +
|
|
91
|
+
(fraction ? dot + fraction : '') +
|
|
92
|
+
((e && abbr.charAt(e)) || '')
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const simplifyExponent = (s, {keepExpPlus} = {}) =>
|
|
97
|
+
String(s).replace(new RegExp('\\.?0*e' + (keepExpPlus ? '' : '\\+?'), 'i'), 'e');
|
|
98
|
+
|
|
99
|
+
export const compareDifference = (a, b) => {
|
|
100
|
+
// works only on positive numbers
|
|
101
|
+
a = Math.abs(a);
|
|
102
|
+
b = Math.abs(b);
|
|
103
|
+
|
|
104
|
+
const less = a < b;
|
|
105
|
+
if (!less) [a, b] = [b, a];
|
|
106
|
+
|
|
107
|
+
if (a === b) return {less, equality: true};
|
|
108
|
+
|
|
109
|
+
const absDiff = b - a,
|
|
110
|
+
diff = absDiff / a;
|
|
111
|
+
if (diff === Infinity) return {less, infinity: true};
|
|
112
|
+
|
|
113
|
+
if (diff < 2) {
|
|
114
|
+
const percentage = diff * 100;
|
|
115
|
+
if (percentage < 0.001) return {less, equality: true};
|
|
116
|
+
if (percentage < 1) return {less, percentage: formatNumber(percentage, {decimals: 3})};
|
|
117
|
+
if (percentage < 10) return {less, percentage: formatNumber(percentage, {decimals: 2})};
|
|
118
|
+
if (percentage < 100) return {less, percentage: formatNumber(percentage, {decimals: 1})};
|
|
119
|
+
return {less, percentage: formatNumber(percentage, {decimals: 0})};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const ratio = b / a;
|
|
123
|
+
|
|
124
|
+
if (ratio < 10000) {
|
|
125
|
+
if (ratio < 10) return {less, ratio: formatNumber(ratio, {decimals: 2})};
|
|
126
|
+
if (ratio < 100) return {less, ratio: formatNumber(ratio, {decimals: 1})};
|
|
127
|
+
return {less, ratio: formatNumber(ratio, {decimals: 0})};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {less, ratio: simplifyExponent(ratio.toPrecision(2))};
|
|
131
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {transcodeTables} from './unicode-numbers.js';
|
|
2
|
+
|
|
3
|
+
const {roman, romanLower} = transcodeTables;
|
|
4
|
+
|
|
5
|
+
const indexL = 12,
|
|
6
|
+
indexC = 13,
|
|
7
|
+
indexD = 14,
|
|
8
|
+
indexM = 15,
|
|
9
|
+
upperICodePoint = roman.get(1).codePointAt(0),
|
|
10
|
+
upperL = String.fromCodePoint(upperICodePoint + indexL),
|
|
11
|
+
upperC = String.fromCodePoint(upperICodePoint + indexC),
|
|
12
|
+
upperD = String.fromCodePoint(upperICodePoint + indexD),
|
|
13
|
+
upperM = String.fromCodePoint(upperICodePoint + indexM),
|
|
14
|
+
lowerICodePoint = romanLower.get(1).codePointAt(0),
|
|
15
|
+
lowerL = String.fromCodePoint(lowerICodePoint + indexL),
|
|
16
|
+
lowerC = String.fromCodePoint(lowerICodePoint + indexC),
|
|
17
|
+
lowerD = String.fromCodePoint(lowerICodePoint + indexD),
|
|
18
|
+
lowerM = String.fromCodePoint(lowerICodePoint + indexM);
|
|
19
|
+
|
|
20
|
+
const romanGroup = (value, one, five, ten) => {
|
|
21
|
+
if (value <= 3) return one.repeat(value);
|
|
22
|
+
if (value == 4) return one + five;
|
|
23
|
+
if (value <= 8) return five + one.repeat(value - 5);
|
|
24
|
+
return one + ten;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const toRoman = value => {
|
|
28
|
+
value = Math.round(value);
|
|
29
|
+
if (value < 1 || value > 3999)
|
|
30
|
+
throw new Error(`Should be a positive integer not exceeding 3999 instead of "${value}"`);
|
|
31
|
+
|
|
32
|
+
const result = [romanGroup(value % 10, 'I', 'V', 'X')];
|
|
33
|
+
value = Math.floor(value / 10);
|
|
34
|
+
|
|
35
|
+
while (value) {
|
|
36
|
+
result.push(romanGroup(value % 10, 'X', 'L', 'C'));
|
|
37
|
+
value = Math.floor(value / 10);
|
|
38
|
+
if (!value) break;
|
|
39
|
+
|
|
40
|
+
result.push(romanGroup(value % 10, 'C', 'D', 'M'));
|
|
41
|
+
value = Math.floor(value / 10);
|
|
42
|
+
if (!value) break;
|
|
43
|
+
|
|
44
|
+
result.push(romanGroup(value % 10, 'M'));
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return result.reverse().join('');
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const toRomanUnicodeFn = (roman, L, C, D, M) => value => {
|
|
52
|
+
value = Math.round(value);
|
|
53
|
+
if (value < 1 || value > 3999)
|
|
54
|
+
throw new Error(`Should be a positive integer not exceeding 3999 instead of "${value}"`);
|
|
55
|
+
|
|
56
|
+
const result = [];
|
|
57
|
+
|
|
58
|
+
while (value) {
|
|
59
|
+
const last2 = value % 100;
|
|
60
|
+
if (last2 <= 12) {
|
|
61
|
+
if (last2) result.push(roman.get(last2));
|
|
62
|
+
value = Math.floor(value / 100);
|
|
63
|
+
} else {
|
|
64
|
+
const digit = value % 10;
|
|
65
|
+
if (digit) result.push(roman.get(digit));
|
|
66
|
+
value = Math.floor(value / 10);
|
|
67
|
+
if (!value) break;
|
|
68
|
+
|
|
69
|
+
result.push(romanGroup(value % 10, roman.get(10), L, C));
|
|
70
|
+
value = Math.floor(value / 10);
|
|
71
|
+
}
|
|
72
|
+
if (!value) break;
|
|
73
|
+
|
|
74
|
+
result.push(romanGroup(value % 10, C, D, M));
|
|
75
|
+
value = Math.floor(value / 10);
|
|
76
|
+
if (!value) break;
|
|
77
|
+
|
|
78
|
+
result.push(romanGroup(value % 10, M));
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return result.reverse().join('');
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const toRomanUnicode = toRomanUnicodeFn(roman, upperL, upperC, upperD, upperM);
|
|
86
|
+
export const toRomanLowerUnicode = toRomanUnicodeFn(romanLower, lowerL, lowerC, lowerD, lowerM);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {SymbolRange} from './utils.js';
|
|
2
|
+
|
|
3
|
+
export const transcodeTables = {
|
|
4
|
+
arabicIndic: new SymbolRange('٠'),
|
|
5
|
+
arabicIndicExtended: new SymbolRange('۰'),
|
|
6
|
+
tamil: new SymbolRange('௦', 0, 10),
|
|
7
|
+
nko: new SymbolRange('߀'),
|
|
8
|
+
devanagari: new SymbolRange('०'),
|
|
9
|
+
bengali: new SymbolRange('০'),
|
|
10
|
+
gurmukhi: new SymbolRange('੦'),
|
|
11
|
+
gujarati: new SymbolRange('૦'),
|
|
12
|
+
oriya: new SymbolRange('୦'),
|
|
13
|
+
telugu: new SymbolRange('౦'),
|
|
14
|
+
kannada: new SymbolRange('೦'),
|
|
15
|
+
malayalam: new SymbolRange('൦', 0, 10),
|
|
16
|
+
thai: new SymbolRange('๐'),
|
|
17
|
+
lao: new SymbolRange('໐'),
|
|
18
|
+
myanmar: new SymbolRange('၀'),
|
|
19
|
+
myanmarShan: new SymbolRange('႐'),
|
|
20
|
+
ethiopic: new SymbolRange('፩', 1, 10),
|
|
21
|
+
khmer: new SymbolRange('០'),
|
|
22
|
+
mongolian: new SymbolRange('᠐'),
|
|
23
|
+
limbu: new SymbolRange('᥆'),
|
|
24
|
+
balinese: new SymbolRange('᭐'),
|
|
25
|
+
sundanese: new SymbolRange('᮰'),
|
|
26
|
+
lepcha: new SymbolRange('᱀'),
|
|
27
|
+
vai: new SymbolRange('꘠'),
|
|
28
|
+
saurashtra: new SymbolRange('꣐'),
|
|
29
|
+
javanese: new SymbolRange('꧐'),
|
|
30
|
+
cham: new SymbolRange('꩐'),
|
|
31
|
+
osmanya: new SymbolRange('𐒠'),
|
|
32
|
+
kharoshthi: new SymbolRange('𐩀', 1, 4),
|
|
33
|
+
rumi: new SymbolRange('𐹠', 1, 10),
|
|
34
|
+
brahmi: new SymbolRange('𑁦'),
|
|
35
|
+
brahmiNumbers: new SymbolRange('𑁒', 1, 10),
|
|
36
|
+
chakma: new SymbolRange('𑄶'),
|
|
37
|
+
sharada: new SymbolRange('𑇐'),
|
|
38
|
+
khudawadi: new SymbolRange('𑋰'),
|
|
39
|
+
newa: new SymbolRange('𑑐'),
|
|
40
|
+
tirhuta: new SymbolRange('𑓐'),
|
|
41
|
+
modi: new SymbolRange('𑙐'),
|
|
42
|
+
takri: new SymbolRange('𑛀'),
|
|
43
|
+
ahom: new SymbolRange('𑜰', 0, 10),
|
|
44
|
+
bhaiksuki: new SymbolRange('𑱐'),
|
|
45
|
+
bhaiksukiNumbers: new SymbolRange('𑱚', 1, 10),
|
|
46
|
+
// kawi: new SymbolRange('𑽐'),
|
|
47
|
+
mro: new SymbolRange('𖩠'),
|
|
48
|
+
// tangsa: new SymbolRange('𖫀'),
|
|
49
|
+
medefaidrin: new SymbolRange('𖺀', 0, 19),
|
|
50
|
+
wancho: new SymbolRange('𞋰'),
|
|
51
|
+
adlam: new SymbolRange('𞥐'),
|
|
52
|
+
aegean: new SymbolRange('𐄇', 1, 10),
|
|
53
|
+
palmyren: new SymbolRange('𐡹', 1, 5),
|
|
54
|
+
nabataean: new SymbolRange('𐢧', 1, 4),
|
|
55
|
+
oldPersian: new SymbolRange('𐏑', 1, 2),
|
|
56
|
+
imperialAramaic: new SymbolRange('𐡘', 1, 3),
|
|
57
|
+
meroiticCursive: new SymbolRange('𐧀', 1, 10),
|
|
58
|
+
inscriptionalParthian: new SymbolRange('𐭘', 1, 4),
|
|
59
|
+
inscriptionalPahlavi: new SymbolRange('𐭸', 1, 4),
|
|
60
|
+
psalterPahlavi: new SymbolRange('𐮩', 1, 4),
|
|
61
|
+
oldSogdian: new SymbolRange('𐼝', 1, 5),
|
|
62
|
+
indicSiyaq: new SymbolRange('𞱱', 1, 10),
|
|
63
|
+
indicSiyaqPrefixed: new SymbolRange('𞲣', 1, 9),
|
|
64
|
+
indicSiyaqAlternate: new SymbolRange('𞲱', 1, 2),
|
|
65
|
+
// ottomanSiyaq: new SymbolRange('𞴁', 1, 10),
|
|
66
|
+
// ottomanSiyaqAlternative: new SymbolRange('𞴯', 2, 10),
|
|
67
|
+
copticEpact: new SymbolRange('𐋡', 1, 10)
|
|
68
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import {SymbolRange, transcode as internalTranscode} from './utils.js';
|
|
2
|
+
|
|
3
|
+
const range = (fromCapital, fromSmall) =>
|
|
4
|
+
[fromCapital && new SymbolRange(fromCapital, 0, 25, 'A'), fromSmall && new SymbolRange(fromSmall, 0, 25, 'a')].filter(
|
|
5
|
+
x => x
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
export const transcodeTables = {
|
|
9
|
+
bold: range('\u{1D400}', '\u{1D41A}'),
|
|
10
|
+
italic: range('\u{1D434}', '\u{1D44E}'),
|
|
11
|
+
boldItalic: range('\u{1D468}', '\u{1D482}'),
|
|
12
|
+
sansSerif: range('\u{1D5A0}', '\u{1D5BA}'),
|
|
13
|
+
sansSerifBold: range('\u{1D5D4}', '\u{1D5EE}'),
|
|
14
|
+
sansSerifItalic: range('\u{1D608}', '\u{1D622}'),
|
|
15
|
+
sansSerifBoldItalic: range('\u{1D63C}', '\u{1D656}'),
|
|
16
|
+
script: range('\u{1D49C}', '\u{1D4B6}'),
|
|
17
|
+
scriptBold: range('\u{1D4D0}', '\u{1D4EA}'),
|
|
18
|
+
fraktur: range('\u{1D504}', '\u{1D51E}'),
|
|
19
|
+
frakturBold: range('\u{1D56C}', '\u{1D586}'),
|
|
20
|
+
mono: range('\u{1D670}', '\u{1D68A}'),
|
|
21
|
+
doubleStruck: range('\u{1D538}', '\u{1D552}'),
|
|
22
|
+
// embellishments
|
|
23
|
+
parens: range('\u{1F110}', '\u{249C}'),
|
|
24
|
+
circled: range('\u{24B6}', '\u{24D0}'),
|
|
25
|
+
squared: range('\u{1F130}', ''),
|
|
26
|
+
negativeCircled: range('\u{1F150}', ''),
|
|
27
|
+
negativeSquared: range('\u{1F170}', ''),
|
|
28
|
+
regionalIndicators: range('\u{1F1E6}', '')
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// patches as suggested in https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols
|
|
32
|
+
|
|
33
|
+
transcodeTables.script[0].overlay = {
|
|
34
|
+
B: '\u{212C}',
|
|
35
|
+
E: '\u{2130}',
|
|
36
|
+
F: '\u{2131}',
|
|
37
|
+
H: '\u{210B}',
|
|
38
|
+
I: '\u{2110}',
|
|
39
|
+
L: '\u{2112}',
|
|
40
|
+
M: '\u{2133}',
|
|
41
|
+
R: '\u{211B}'
|
|
42
|
+
};
|
|
43
|
+
transcodeTables.fraktur[0].overlay = {C: '\u{212D}', H: '\u{210C}', I: '\u{2111}', R: '\u{211C}', Z: '\u{2128}'};
|
|
44
|
+
transcodeTables.doubleStruck[0].overlay = {
|
|
45
|
+
C: '\u{2102}',
|
|
46
|
+
H: '\u{210D}',
|
|
47
|
+
N: '\u{2115}',
|
|
48
|
+
P: '\u{2119}',
|
|
49
|
+
Q: '\u{211A}',
|
|
50
|
+
R: '\u{211D}',
|
|
51
|
+
Z: '\u{2124}'
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
transcodeTables.italic[1].overlay = {h: '\u{210E}'};
|
|
55
|
+
transcodeTables.script[1].overlay = {e: '\u{212F}', g: '\u{210A}', o: '\u{2134}'};
|
|
56
|
+
|
|
57
|
+
// API
|
|
58
|
+
|
|
59
|
+
export const transcode = (s, name, options) => {
|
|
60
|
+
let tables = typeof name == 'string' ? transcodeTables[name] : name;
|
|
61
|
+
if (!tables) throw new Error(`There is no transcode table "${name}"`);
|
|
62
|
+
return internalTranscode(s, tables, options);
|
|
63
|
+
};
|