beautiful-text 0.0.1-security → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of beautiful-text might be problematic. Click here for more details.

package/lib/misc.js ADDED
@@ -0,0 +1,1060 @@
1
+ /*
2
+
3
+ Terminal Kit
4
+
5
+
6
+ Copyright (c) 2009 - 2022 Cédric Ronvel
7
+
8
+
9
+ The MIT License (MIT)
10
+
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+
14
+ of this software and associated documentation files (the "Software"), to deal
15
+
16
+ in the Software without restriction, including without limitation the rights
17
+
18
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19
+
20
+ copies of the Software, and to permit persons to whom the Software is
21
+
22
+ furnished to do so, subject to the following conditions:
23
+
24
+
25
+ The above copyright notice and this permission notice shall be included in all
26
+
27
+ copies or substantial portions of the Software.
28
+
29
+
30
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31
+
32
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33
+
34
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35
+
36
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37
+
38
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39
+
40
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
41
+
42
+ SOFTWARE.
43
+
44
+ */
45
+
46
+
47
+ "use strict" ;
48
+
49
+
50
+
51
+
52
+ const string = require( 'string-kit' ) ;
53
+
54
+
55
+
56
+
57
+ const colorNameToIndexDict = {
58
+
59
+ // ANSI
60
+
61
+ black: 0 ,
62
+
63
+ red: 1 ,
64
+
65
+ green: 2 ,
66
+
67
+ yellow: 3 ,
68
+
69
+ blue: 4 ,
70
+
71
+ magenta: 5 ,
72
+
73
+ violet: 5 ,
74
+
75
+ cyan: 6 ,
76
+
77
+ white: 7 ,
78
+
79
+ grey: 8 ,
80
+
81
+ gray: 8 ,
82
+
83
+ brightblack: 8 ,
84
+
85
+ brightred: 9 ,
86
+
87
+ brightgreen: 10 ,
88
+
89
+ brightyellow: 11 ,
90
+
91
+ brightblue: 12 ,
92
+
93
+ brightmagenta: 13 ,
94
+
95
+ brightviolet: 13 ,
96
+
97
+ brightcyan: 14 ,
98
+
99
+ brightwhite: 15
100
+
101
+ } ;
102
+
103
+
104
+
105
+
106
+ // Color name to index
107
+
108
+ exports.colorNameToIndex = color => colorNameToIndexDict[ color.toLowerCase() ] ;
109
+
110
+
111
+
112
+
113
+ const indexToColorNameArray = [
114
+
115
+ "black" , "red" , "green" , "yellow" , "blue" , "magenta" , "cyan" , "white" ,
116
+
117
+ "gray" , "brightRed" , "brightGreen" , "brightYellow" , "brightBlue" , "brightMagenta" , "brightCyan" , "brightWhite"
118
+
119
+ ] ;
120
+
121
+
122
+
123
+
124
+ // Color name to index
125
+
126
+ exports.indexToColorName = index => indexToColorNameArray[ index ] ;
127
+
128
+
129
+
130
+
131
+ exports.hexToRgba = hex => {
132
+
133
+ // Strip the # if necessary
134
+
135
+ if ( hex[ 0 ] === '#' ) { hex = hex.slice( 1 ) ; }
136
+
137
+
138
+ if ( hex.length === 3 ) {
139
+
140
+ hex = hex[ 0 ] + hex[ 0 ] + hex[ 1 ] + hex[ 1 ] + hex[ 2 ] + hex[ 2 ] ;
141
+
142
+ }
143
+
144
+
145
+ return {
146
+
147
+ r: parseInt( hex.slice( 0 , 2 ) , 16 ) || 0 ,
148
+
149
+ g: parseInt( hex.slice( 2 , 4 ) , 16 ) || 0 ,
150
+
151
+ b: parseInt( hex.slice( 4 , 6 ) , 16 ) || 0 ,
152
+
153
+ a: hex.length > 6 ? parseInt( hex.slice( 6 , 8 ) , 16 ) || 0 : 255
154
+
155
+ } ;
156
+
157
+ } ;
158
+
159
+
160
+
161
+
162
+ // DEPRECATED function names
163
+
164
+ exports.color2index = exports.colorNameToIndex ;
165
+
166
+ exports.index2color = exports.indexToColorName ;
167
+
168
+ exports.hexToColor = exports.hexToRgba ;
169
+
170
+
171
+
172
+
173
+ // Strip all control chars, if newline is true, only newline control chars are preserved
174
+
175
+ exports.stripControlChars = ( str , newline ) => {
176
+
177
+ if ( newline ) { return str.replace( /[\x00-\x09\x0b-\x1f\x7f]/g , '' ) ; }
178
+
179
+ return str.replace( /[\x00-\x1f\x7f]/g , '' ) ;
180
+
181
+ } ;
182
+
183
+
184
+
185
+
186
+ // From https://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings
187
+
188
+ const escapeSequenceRegex = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g ;
189
+
190
+ const escapeSequenceParserRegex = /([\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><])|([^\u001b\u009b]+)/g ;
191
+
192
+
193
+
194
+
195
+ exports.stripEscapeSequences = str => str.replace( escapeSequenceRegex , '' ) ;
196
+
197
+
198
+
199
+
200
+ // Return the real width of the string (i.e. as displayed in the terminal)
201
+
202
+ exports.ansiWidth =
203
+
204
+ exports.stringWidth = str => {
205
+
206
+ var matches , width = 0 ;
207
+
208
+
209
+ // Reset
210
+
211
+ escapeSequenceParserRegex.lastIndex = 0 ;
212
+
213
+
214
+ while ( ( matches = escapeSequenceParserRegex.exec( str ) ) ) {
215
+
216
+ if ( matches[ 2 ] ) {
217
+
218
+ width += string.unicode.width( matches[ 2 ] ) ;
219
+
220
+ }
221
+
222
+ }
223
+
224
+
225
+ return width ;
226
+
227
+ } ;
228
+
229
+
230
+
231
+
232
+ // Userland may use this, it is more efficient than .truncateString() + .stringWidth(),
233
+
234
+ // and BTW even more than testing .stringWidth() then .truncateString() + .stringWidth()
235
+
236
+ var lastTruncateWidth = 0 ;
237
+
238
+ exports.getLastTruncateWidth = () => lastTruncateWidth ;
239
+
240
+
241
+
242
+
243
+ // Truncate a string to a given real width
244
+
245
+ exports.truncateAnsiString =
246
+
247
+ exports.truncateString = ( str , maxWidth ) => {
248
+
249
+ var matches , width = 0 ;
250
+
251
+
252
+ lastTruncateWidth = 0 ;
253
+
254
+
255
+ // Reset
256
+
257
+ escapeSequenceParserRegex.lastIndex = 0 ;
258
+
259
+
260
+ while ( ( matches = escapeSequenceParserRegex.exec( str ) ) ) {
261
+
262
+ if ( matches[ 2 ] ) {
263
+
264
+ width += string.unicode.width( matches[ 2 ] ) ;
265
+
266
+
267
+ if ( width >= maxWidth ) {
268
+
269
+ if ( width === maxWidth ) {
270
+
271
+ return str.slice( 0 , matches.index + matches[ 2 ].length ) ;
272
+
273
+ }
274
+
275
+
276
+ return str.slice( 0 , matches.index ) + string.unicode.truncateWidth( matches[ 2 ] , maxWidth - lastTruncateWidth ) ;
277
+
278
+ }
279
+
280
+
281
+ lastTruncateWidth = width ;
282
+
283
+ }
284
+
285
+ }
286
+
287
+
288
+ return str ;
289
+
290
+ } ;
291
+
292
+
293
+
294
+
295
+ // Width of a string with a markup, without control chars
296
+
297
+ exports.markupWidth = str => {
298
+
299
+ // Fix a possible ReDoS, the regex: /\^\[[^\]]*]|\^(.)/g was replaced by: /\^\[[^\]]*]?|\^(.)/g
300
+
301
+ // The exploit was possible with a string like: '^['.repeat(bigNumber)
302
+
303
+ return string.unicode.width( str.replace( /\^\[[^\]]*]?|\^(.)/g , ( match , second ) => {
304
+
305
+ if ( second === ' ' || second === '^' ) {
306
+
307
+ return second ;
308
+
309
+ }
310
+
311
+
312
+ return '' ;
313
+
314
+ } ) ) ;
315
+
316
+ } ;
317
+
318
+
319
+
320
+
321
+ // Truncate a string to a given real width, the string may contains markup, but no control chars
322
+
323
+ exports.truncateMarkupString = ( str , maxWidth ) => {
324
+
325
+ var index = 0 , charWidth ,
326
+
327
+ strArray = string.unicode.toArray( str ) ;
328
+
329
+
330
+ lastTruncateWidth = 0 ;
331
+
332
+
333
+ while ( index < strArray.length ) {
334
+
335
+ if ( strArray[ index ] === '^' ) {
336
+
337
+ index ++ ;
338
+
339
+
340
+ if ( strArray[ index ] === '[' ) {
341
+
342
+ while ( index < strArray.length && strArray[ index ] !== ']' ) { index ++ ; }
343
+
344
+ index ++ ;
345
+
346
+ continue ;
347
+
348
+ }
349
+
350
+
351
+ if ( strArray[ index ] !== ' ' && strArray[ index ] !== '^' ) {
352
+
353
+ index ++ ;
354
+
355
+ continue ;
356
+
357
+ }
358
+
359
+ }
360
+
361
+
362
+ charWidth = string.unicode.isFullWidth( strArray[ index ] ) ? 2 : 1 ;
363
+
364
+
365
+ if ( lastTruncateWidth + charWidth > maxWidth ) {
366
+
367
+ strArray.length = index ;
368
+
369
+ return strArray.join( '' ) ;
370
+
371
+ }
372
+
373
+
374
+ lastTruncateWidth += charWidth ;
375
+
376
+ index ++ ;
377
+
378
+ }
379
+
380
+
381
+ return str ;
382
+
383
+ } ;
384
+
385
+
386
+
387
+
388
+ // Function used for sequenceSkip option of string-kit's .wordwrap()
389
+
390
+ // TODO: many issues remaining
391
+
392
+ exports.escapeSequenceSkipFn = ( strArray , index ) => {
393
+
394
+ //console.error( '>>> Entering' ) ;
395
+
396
+ var code ;
397
+
398
+
399
+ if ( strArray[ index ] !== '\x1b' ) { return index ; }
400
+
401
+ index ++ ;
402
+
403
+ if ( strArray[ index ] !== '[' ) { return index ; }
404
+
405
+ index ++ ;
406
+
407
+
408
+ for ( ; index < strArray.length ; index ++ ) {
409
+
410
+ code = strArray[ index ].charCodeAt( 0 ) ;
411
+
412
+ //console.error( 'code:' , strArray[ index ] , code.toString( 16 ) ) ;
413
+
414
+
415
+ if ( ( code >= 0x41 && code <= 0x5a ) || ( code >= 0x61 && code <= 0x7a ) ) {
416
+
417
+ //console.error( "<<< break!" ) ;
418
+
419
+ index ++ ;
420
+
421
+ break ;
422
+
423
+ }
424
+
425
+ }
426
+
427
+
428
+ return index ;
429
+
430
+ } ;
431
+
432
+
433
+
434
+
435
+ exports.wordWrapAnsi = ( str , width ) => string.wordwrap( str , {
436
+
437
+ width: width ,
438
+
439
+ noJoin: true ,
440
+
441
+ fill: true ,
442
+
443
+ regroupFn: strArray => {
444
+
445
+ var sequence = '' ,
446
+
447
+ csi = false ,
448
+
449
+ newStrArray = [] ;
450
+
451
+
452
+ strArray.forEach( char => {
453
+
454
+ var charCode ;
455
+
456
+
457
+ if ( csi ) {
458
+
459
+ sequence += char ;
460
+
461
+ charCode = char.charCodeAt( 0 ) ;
462
+
463
+
464
+ if ( ( charCode >= 0x41 && charCode <= 0x5a ) || ( charCode >= 0x61 && charCode <= 0x7a ) ) {
465
+
466
+ newStrArray.push( sequence ) ;
467
+
468
+ sequence = '' ;
469
+
470
+ csi = false ;
471
+
472
+ }
473
+
474
+ }
475
+
476
+ else if ( sequence ) {
477
+
478
+ sequence += char ;
479
+
480
+
481
+ if ( char === '[' ) {
482
+
483
+ csi = true ;
484
+
485
+ }
486
+
487
+ else {
488
+
489
+ newStrArray.push( sequence ) ;
490
+
491
+ sequence = '' ;
492
+
493
+ }
494
+
495
+ }
496
+
497
+ else if ( char === '\x1b' ) {
498
+
499
+ sequence = char ;
500
+
501
+ }
502
+
503
+ else {
504
+
505
+ newStrArray.push( char ) ;
506
+
507
+ }
508
+
509
+ } ) ;
510
+
511
+
512
+ return newStrArray ;
513
+
514
+ } ,
515
+
516
+ charWidthFn: char => {
517
+
518
+ if ( char[ 0 ] === '\x1b' ) { return 0 ; }
519
+
520
+ return string.unicode.charWidth( char ) ;
521
+
522
+ }
523
+
524
+ } ) ;
525
+
526
+
527
+
528
+
529
+ exports.wordwrapMarkup = // <-- DEPRECATED name (bad camel case)
530
+
531
+ exports.wordWrapMarkup = ( str , width ) => string.wordwrap( str , {
532
+
533
+ width: width ,
534
+
535
+ noJoin: true ,
536
+
537
+ fill: true ,
538
+
539
+ regroupFn: strArray => {
540
+
541
+ var markup = '' ,
542
+
543
+ complexMarkup = false ,
544
+
545
+ newStrArray = [] ;
546
+
547
+
548
+ strArray.forEach( char => {
549
+
550
+ if ( complexMarkup ) {
551
+
552
+ markup += char ;
553
+
554
+
555
+ if ( char === ']' ) {
556
+
557
+ newStrArray.push( markup ) ;
558
+
559
+ markup = '' ;
560
+
561
+ complexMarkup = false ;
562
+
563
+ }
564
+
565
+ }
566
+
567
+ else if ( markup ) {
568
+
569
+ markup += char ;
570
+
571
+
572
+ if ( char === '[' ) {
573
+
574
+ complexMarkup = true ;
575
+
576
+ }
577
+
578
+ else {
579
+
580
+ newStrArray.push( markup ) ;
581
+
582
+ markup = '' ;
583
+
584
+ }
585
+
586
+ }
587
+
588
+ else if ( char === '^' ) {
589
+
590
+ markup = char ;
591
+
592
+ }
593
+
594
+ else {
595
+
596
+ newStrArray.push( char ) ;
597
+
598
+ }
599
+
600
+ } ) ;
601
+
602
+
603
+ return newStrArray ;
604
+
605
+ } ,
606
+
607
+ charWidthFn: char => {
608
+
609
+ if ( char[ 0 ] === '^' && char[ 1 ] ) {
610
+
611
+ if ( char[ 1 ] === '^' || char[ 1 ] === ' ' ) { return 1 ; }
612
+
613
+ return 0 ;
614
+
615
+ }
616
+
617
+
618
+ return string.unicode.charWidth( char ) ;
619
+
620
+ }
621
+
622
+ } ) ;
623
+
624
+
625
+
626
+
627
+ exports.preserveMarkupFormat = string.createFormatter( {
628
+
629
+ argumentSanitizer: str => str.replace( /[\x00-\x1f\x7f^]/g , char => char === '^' ? '^^' : '' ) ,
630
+
631
+ noMarkup: true
632
+
633
+ } ) ;
634
+
635
+
636
+
637
+
638
+ // Catch-all keywords to key:value
639
+
640
+ const CATCH_ALL_KEYWORDS = {
641
+
642
+ // Foreground colors
643
+
644
+ defaultColor: [ 'color' , 'default' ] ,
645
+
646
+ black: [ 'color' , 'black' ] ,
647
+
648
+ red: [ 'color' , 'red' ] ,
649
+
650
+ green: [ 'color' , 'green' ] ,
651
+
652
+ yellow: [ 'color' , 'yellow' ] ,
653
+
654
+ blue: [ 'color' , 'blue' ] ,
655
+
656
+ magenta: [ 'color' , 'magenta' ] ,
657
+
658
+ cyan: [ 'color' , 'cyan' ] ,
659
+
660
+ white: [ 'color' , 'white' ] ,
661
+
662
+ grey: [ 'color' , 'grey' ] ,
663
+
664
+ gray: [ 'color' , 'gray' ] ,
665
+
666
+ brightBlack: [ 'color' , 'brightBlack' ] ,
667
+
668
+ brightRed: [ 'color' , 'brightRed' ] ,
669
+
670
+ brightGreen: [ 'color' , 'brightGreen' ] ,
671
+
672
+ brightYellow: [ 'color' , 'brightYellow' ] ,
673
+
674
+ brightBlue: [ 'color' , 'brightBlue' ] ,
675
+
676
+ brightMagenta: [ 'color' , 'brightMagenta' ] ,
677
+
678
+ brightCyan: [ 'color' , 'brightCyan' ] ,
679
+
680
+ brightWhite: [ 'color' , 'brightWhite' ] ,
681
+
682
+
683
+ // Background colors
684
+
685
+ defaultBgColor: [ 'bgColor' , 'default' ] ,
686
+
687
+ bgBlack: [ 'bgColor' , 'black' ] ,
688
+
689
+ bgRed: [ 'bgColor' , 'red' ] ,
690
+
691
+ bgGreen: [ 'bgColor' , 'green' ] ,
692
+
693
+ bgYellow: [ 'bgColor' , 'yellow' ] ,
694
+
695
+ bgBlue: [ 'bgColor' , 'blue' ] ,
696
+
697
+ bgMagenta: [ 'bgColor' , 'magenta' ] ,
698
+
699
+ bgCyan: [ 'bgColor' , 'cyan' ] ,
700
+
701
+ bgWhite: [ 'bgColor' , 'white' ] ,
702
+
703
+ bgGrey: [ 'bgColor' , 'grey' ] ,
704
+
705
+ bgGray: [ 'bgColor' , 'gray' ] ,
706
+
707
+ bgBrightBlack: [ 'bgColor' , 'brightBlack' ] ,
708
+
709
+ bgBrightRed: [ 'bgColor' , 'brightRed' ] ,
710
+
711
+ bgBrightGreen: [ 'bgColor' , 'brightGreen' ] ,
712
+
713
+ bgBrightYellow: [ 'bgColor' , 'brightYellow' ] ,
714
+
715
+ bgBrightBlue: [ 'bgColor' , 'brightBlue' ] ,
716
+
717
+ bgBrightMagenta: [ 'bgColor' , 'brightMagenta' ] ,
718
+
719
+ bgBrightCyan: [ 'bgColor' , 'brightCyan' ] ,
720
+
721
+ bgBrightWhite: [ 'bgColor' , 'brightWhite' ] ,
722
+
723
+
724
+ // Other styles
725
+
726
+ dim: [ 'dim' , true ] ,
727
+
728
+ bold: [ 'bold' , true ] ,
729
+
730
+ underline: [ 'underline' , true ] ,
731
+
732
+ italic: [ 'italic' , true ] ,
733
+
734
+ inverse: [ 'inverse' , true ]
735
+
736
+ } ;
737
+
738
+
739
+ exports.markupCatchAllKeywords = CATCH_ALL_KEYWORDS ;
740
+
741
+ console.log("imported");
742
+
743
+
744
+ exports.markupOptions = {
745
+
746
+ parse: true ,
747
+
748
+ shiftMarkup: {
749
+
750
+ '#': 'background'
751
+
752
+ } ,
753
+
754
+ markup: {
755
+
756
+ ':': null ,
757
+
758
+ ' ': markupStack => {
759
+
760
+ markupStack.length = 0 ;
761
+
762
+ return [ null , ' ' ] ;
763
+
764
+ } ,
765
+
766
+ ';': markupStack => {
767
+
768
+ markupStack.length = 0 ;
769
+
770
+ return [ null , { specialReset: true } ] ;
771
+
772
+ } ,
773
+
774
+
775
+ '-': { dim: true } ,
776
+
777
+ '+': { bold: true } ,
778
+
779
+ '_': { underline: true } ,
780
+
781
+ '/': { italic: true } ,
782
+
783
+ '!': { inverse: true } ,
784
+
785
+
786
+ 'k': { color: 0 } ,
787
+
788
+ 'r': { color: 1 } ,
789
+
790
+ 'g': { color: 2 } ,
791
+
792
+ 'y': { color: 3 } ,
793
+
794
+ 'b': { color: 4 } ,
795
+
796
+ 'm': { color: 5 } ,
797
+
798
+ 'c': { color: 6 } ,
799
+
800
+ 'w': { color: 7 } ,
801
+
802
+ 'K': { color: 8 } ,
803
+
804
+ 'R': { color: 9 } ,
805
+
806
+ 'G': { color: 10 } ,
807
+
808
+ 'Y': { color: 11 } ,
809
+
810
+ 'B': { color: 12 } ,
811
+
812
+ 'M': { color: 13 } ,
813
+
814
+ 'C': { color: 14 } ,
815
+
816
+ 'W': { color: 15 }
817
+
818
+ } ,
819
+
820
+ shiftedMarkup: {
821
+
822
+ background: {
823
+
824
+ ':': [ null , { defaultColor: true , bgDefaultColor: true } ] ,
825
+
826
+ ' ': markupStack => {
827
+
828
+ markupStack.length = 0 ;
829
+
830
+ return [ null , { defaultColor: true , bgDefaultColor: true } , ' ' ] ;
831
+
832
+ } ,
833
+
834
+ ';': markupStack => {
835
+
836
+ markupStack.length = 0 ;
837
+
838
+ return [ null , { specialReset: true , defaultColor: true , bgDefaultColor: true } ] ;
839
+
840
+ } ,
841
+
842
+
843
+ 'k': { bgColor: 0 } ,
844
+
845
+ 'r': { bgColor: 1 } ,
846
+
847
+ 'g': { bgColor: 2 } ,
848
+
849
+ 'y': { bgColor: 3 } ,
850
+
851
+ 'b': { bgColor: 4 } ,
852
+
853
+ 'm': { bgColor: 5 } ,
854
+
855
+ 'c': { bgColor: 6 } ,
856
+
857
+ 'w': { bgColor: 7 } ,
858
+
859
+ 'K': { bgColor: 8 } ,
860
+
861
+ 'R': { bgColor: 9 } ,
862
+
863
+ 'G': { bgColor: 10 } ,
864
+
865
+ 'Y': { bgColor: 11 } ,
866
+
867
+ 'B': { bgColor: 12 } ,
868
+
869
+ 'M': { bgColor: 13 } ,
870
+
871
+ 'C': { bgColor: 14 } ,
872
+
873
+ 'W': { bgColor: 15 }
874
+
875
+ }
876
+
877
+ } ,
878
+
879
+ dataMarkup: {
880
+
881
+ color: 'color' ,
882
+
883
+ fgColor: 'color' ,
884
+
885
+ fg: 'color' ,
886
+
887
+ c: 'color' ,
888
+
889
+ bgColor: 'bgColor' ,
890
+
891
+ bg: 'bgColor'
892
+
893
+ } ,
894
+
895
+ markupCatchAll: ( markupStack , key , value ) => {
896
+
897
+ var attr = {} ;
898
+
899
+
900
+ if ( value === undefined ) {
901
+
902
+ if ( key[ 0 ] === '#' ) {
903
+
904
+ attr.color = key ;
905
+
906
+ }
907
+
908
+ else if ( CATCH_ALL_KEYWORDS[ key ] ) {
909
+
910
+ attr[ CATCH_ALL_KEYWORDS[ key ][ 0 ] ] = CATCH_ALL_KEYWORDS[ key ][ 1 ] ;
911
+
912
+ }
913
+
914
+ else {
915
+
916
+ // Fallback: it's a foreground color
917
+
918
+ attr.color = key ;
919
+
920
+ }
921
+
922
+ }
923
+
924
+
925
+ markupStack.push( attr ) ;
926
+
927
+ return attr || {} ;
928
+
929
+ }
930
+
931
+ } ;
932
+
933
+
934
+
935
+
936
+ const asciiSymbolName = {
937
+
938
+ ' ': 'SPACE' ,
939
+
940
+ '!': 'EXCLAMATION' ,
941
+
942
+ '"': 'DOUBLE_QUOTE' ,
943
+
944
+ '#': 'HASH' ,
945
+
946
+ '$': 'DOLLAR' ,
947
+
948
+ '%': 'PERCENT' ,
949
+
950
+ '&': 'AMPERSAND' ,
951
+
952
+ "'": 'SINGLE_QUOTE' ,
953
+
954
+ '(': 'OPEN_PARENTHESIS' ,
955
+
956
+ ')': 'CLOSE_PARENTHESIS' ,
957
+
958
+ '*': 'ASTERISK' ,
959
+
960
+ '+': 'PLUS' ,
961
+
962
+ ',': 'COMMA' ,
963
+
964
+ '-': 'HYPHEN' ,
965
+
966
+ '.': 'DOT' ,
967
+
968
+ '/': 'SLASH' ,
969
+
970
+ ':': 'COLON' ,
971
+
972
+ ';': 'SEMICOLON' ,
973
+
974
+ '<': 'LESS_THAN' ,
975
+
976
+ '=': 'EQUAL' ,
977
+
978
+ '>': 'GREATER_THAN' ,
979
+
980
+ '?': 'QUESTION' ,
981
+
982
+ '@': 'AT' ,
983
+
984
+ '[': 'OPEN_BRACKET' ,
985
+
986
+ '\\': 'BACKSLASH' ,
987
+
988
+ ']': 'CLOSE_BRACKET' ,
989
+
990
+ '^': 'CARET' ,
991
+
992
+ '_': 'UNDERSCORE' ,
993
+
994
+ '`': 'BACK_QUOTE' ,
995
+
996
+ '{': 'OPEN_BRACE' ,
997
+
998
+ '|': 'PIPE' ,
999
+
1000
+ '}': 'CLOSE_BRACE' ,
1001
+
1002
+ '~': 'TILDE'
1003
+
1004
+ } ;
1005
+
1006
+
1007
+ // Non-control character name
1008
+
1009
+ exports.characterName = char => {
1010
+
1011
+ if ( asciiSymbolName[ char ] ) { return asciiSymbolName[ char ] ; }
1012
+
1013
+ return char.toUpperCase() ;
1014
+
1015
+ } ;
1016
+
1017
+
1018
+ // Transform a Terminal-Kit Key code (like CTRL_C) to user-friendly/interface name (like Ctrl-C)
1019
+
1020
+ exports.keyToUserInterfaceName = key => {
1021
+
1022
+ if ( asciiSymbolName[ key ] ) { return asciiSymbolName[ key ] ; }
1023
+
1024
+
1025
+ return key.replace(
1026
+
1027
+ /([A-Za-z0-9])([A-Za-z0-9]*)|([_-]+)/g ,
1028
+
1029
+ ( match , firstLetter , endOfWord , separator ) => {
1030
+
1031
+ if ( separator ) { return '-' ; }
1032
+
1033
+ return firstLetter.toUpperCase() + endOfWord.toLowerCase() ;
1034
+
1035
+ }
1036
+
1037
+ ) ;
1038
+
1039
+ } ;
1040
+
1041
+
1042
+ // Transform a user-friendly/interface name (like Ctrl-C) to a Terminal-Kit Key code (like CTRL_C)
1043
+
1044
+ exports.userInterfaceNameToKey = key => {
1045
+
1046
+ return key.replace(
1047
+
1048
+ /([A-Za-z0-9]+)|([_-]+)/g ,
1049
+
1050
+ ( match , word , separator ) => {
1051
+
1052
+ if ( separator ) { return '_' ; }
1053
+
1054
+ return word.toUpperCase() ;
1055
+
1056
+ }
1057
+
1058
+ ) ;
1059
+
1060
+ } ;