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.
Files changed (78) hide show
  1. package/LICENSE +34 -0
  2. package/README.md +92 -0
  3. package/package.json +59 -0
  4. package/src/alphanumeric/arrows.js +31 -0
  5. package/src/alphanumeric/fractions.js +82 -0
  6. package/src/alphanumeric/number-formatters.js +131 -0
  7. package/src/alphanumeric/roman.js +86 -0
  8. package/src/alphanumeric/unicode-cultural-numbers.js +68 -0
  9. package/src/alphanumeric/unicode-letters.js +63 -0
  10. package/src/alphanumeric/unicode-numbers.js +75 -0
  11. package/src/alphanumeric/utils.js +44 -0
  12. package/src/ansi/csi.js +67 -0
  13. package/src/ansi/index.js +20 -0
  14. package/src/ansi/sgr-constants.js +99 -0
  15. package/src/ansi/sgr-state.js +398 -0
  16. package/src/ansi/sgr.js +214 -0
  17. package/src/box.js +253 -0
  18. package/src/charts/bars/block-frac-grouped.js +6 -0
  19. package/src/charts/bars/block-frac.js +36 -0
  20. package/src/charts/bars/block-grouped.js +6 -0
  21. package/src/charts/bars/block.js +43 -0
  22. package/src/charts/bars/draw-grouped.js +39 -0
  23. package/src/charts/bars/draw-stacked.js +24 -0
  24. package/src/charts/bars/frac-grouped.js +33 -0
  25. package/src/charts/bars/plain-grouped.js +6 -0
  26. package/src/charts/bars/plain.js +63 -0
  27. package/src/charts/columns/block-frac-grouped.js +6 -0
  28. package/src/charts/columns/block-frac.js +30 -0
  29. package/src/charts/columns/block-grouped.js +6 -0
  30. package/src/charts/columns/block.js +37 -0
  31. package/src/charts/columns/draw-grouped.js +48 -0
  32. package/src/charts/columns/draw-stacked.js +31 -0
  33. package/src/charts/columns/frac-grouped.js +27 -0
  34. package/src/charts/columns/plain-grouped.js +6 -0
  35. package/src/charts/columns/plain.js +39 -0
  36. package/src/charts/themes/default.js +12 -0
  37. package/src/charts/themes/rainbow-reversed.js +5 -0
  38. package/src/charts/themes/rainbow.js +8 -0
  39. package/src/charts/utils.js +75 -0
  40. package/src/draw-block-frac.js +33 -0
  41. package/src/draw-block.js +55 -0
  42. package/src/meta.js +41 -0
  43. package/src/output/show.js +40 -0
  44. package/src/output/updater.js +82 -0
  45. package/src/output/writer.js +131 -0
  46. package/src/panel.js +748 -0
  47. package/src/plot/bitmap.js +108 -0
  48. package/src/plot/draw-line.js +26 -0
  49. package/src/plot/draw-rect.js +216 -0
  50. package/src/plot/index.js +24 -0
  51. package/src/plot/to-quads.js +32 -0
  52. package/src/spinner/index.js +8 -0
  53. package/src/spinner/spin.js +51 -0
  54. package/src/spinner/spinner.js +75 -0
  55. package/src/spinner/spinners.js +65 -0
  56. package/src/strings.js +72 -0
  57. package/src/style.js +620 -0
  58. package/src/symbols.js +131 -0
  59. package/src/table/draw-borders.js +87 -0
  60. package/src/table/index.js +7 -0
  61. package/src/table/table.js +330 -0
  62. package/src/themes/blocks/unicode-half.js +9 -0
  63. package/src/themes/blocks/unicode-thin.js +9 -0
  64. package/src/themes/lines/ascii-compact.js +11 -0
  65. package/src/themes/lines/ascii-dots.js +9 -0
  66. package/src/themes/lines/ascii-girder.js +9 -0
  67. package/src/themes/lines/ascii-github.js +11 -0
  68. package/src/themes/lines/ascii-reddit.js +11 -0
  69. package/src/themes/lines/ascii-rounded.js +11 -0
  70. package/src/themes/lines/ascii.js +11 -0
  71. package/src/themes/lines/unicode-bold.js +15 -0
  72. package/src/themes/lines/unicode-rounded.js +15 -0
  73. package/src/themes/lines/unicode.js +15 -0
  74. package/src/themes/utils.js +38 -0
  75. package/src/turtle/draw-line-art.js +46 -0
  76. package/src/turtle/draw-unicode.js +33 -0
  77. package/src/turtle/index.js +12 -0
  78. 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
+ ![Memory watcher](https://github.com/uhop/console-toolkit/wiki/images/example-memory.png)
23
+
24
+ ### Waveform
25
+
26
+ ![Waveform](https://github.com/uhop/console-toolkit/wiki/images/example-waveform.png)
27
+
28
+ ### Table + chart
29
+
30
+ ![Table + chart](https://github.com/uhop/console-toolkit/wiki/images/example-table-chart.png)
31
+
32
+ ### Turtle graphics
33
+
34
+ ![Turtle graphics](https://github.com/uhop/console-toolkit/wiki/images/example-turtle.png)
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
+ ![Code example](https://github.com/uhop/console-toolkit/wiki/images/example-code.png)
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
+ };