terminal-kit
Advanced tools
Comparing version 1.49.4 to 3.0.1
@@ -28,3 +28,3 @@ module.exports = { | ||
'no-unused-vars': 'warn' , // During development phase, it's boring to clean unused var since they can be used later | ||
'no-lonely-if': 'error' , | ||
'no-lonely-if': 'off' , // Can hurt semantic programming | ||
'no-nested-ternary': 'off' , // Now I use the streamlined ternary operator a lot | ||
@@ -66,5 +66,3 @@ 'no-shadow': 'warn' , | ||
} ] , | ||
'newline-per-chained-call': [ 'error', { | ||
'ignoreChainWithDepth': 2 | ||
} ] , | ||
'newline-per-chained-call': 'off', | ||
'no-multi-spaces': 'off' , | ||
@@ -71,0 +69,0 @@ 'block-spacing': 'error' , |
@@ -33,2 +33,3 @@ | ||
* [Bar](Bar.md#top) | ||
* [Border](Border.md#top) | ||
* [Button](Button.md#top) | ||
@@ -41,2 +42,4 @@ * [ColumnMenu](ColumnMenu.md#top) | ||
* [InlineInput](InlineInput.md#top) | ||
* [InlineFileInput](InlineFileInput.md#top) | ||
* [InlineMenu](InlineMenu.md#top) | ||
* [LabeledInput](LabeledInput.md#top) | ||
@@ -43,0 +46,0 @@ * [Layout](Layout.md#top) |
@@ -30,3 +30,3 @@ | ||
* [.draw()](#ref.Element.draw) | ||
* [.redraw()](#ref.Element.redraw) | ||
* [.outerDraw()](#ref.Element.outerDraw) | ||
* [.drawCursor()](#ref.Element.drawCursor) | ||
@@ -122,3 +122,3 @@ * [.saveCursor()](#ref.Element.saveCursor) | ||
default: false. **NOTE:** not all widget support markup or ansi! | ||
* dontDraw `boolean` when set, the content's update does not trigger the *redraw* of the *element* | ||
* dontDraw `boolean` when set, the content's update does not trigger the *draw*/*outerDraw* of the *element* | ||
@@ -132,5 +132,5 @@ Set the content of this *element*. | ||
* dontDraw `boolean` when set (default: false) the element is not redrawn (it will be made visible the next time something trigger a *redraw*) | ||
* dontDraw `boolean` when set (default: false) the element is not drawn/outerDrawn (it will be made visible the next time something trigger a *outerDraw*) | ||
Turn the element visibility **on** and redraw it immediately (unless the `dontDraw` option is on). | ||
Turn the element visibility **on** and outerDraw it immediately (unless the `dontDraw` option is on). | ||
@@ -142,5 +142,5 @@ | ||
* dontDraw `boolean` when set (default: false) the element is not redrawn (it will be hidden the next time something trigger a *redraw* on its parent) | ||
* dontDraw `boolean` when set (default: false) the element is not drawn/outerDrawn (it will be hidden the next time something trigger a *outerDraw* on its parent) | ||
Turn the element visibility **off** and redraw its parent immediately (unless the `dontDraw` option is on). | ||
Turn the element visibility **off** and outerDraw its parent immediately (unless the `dontDraw` option is on). | ||
@@ -158,6 +158,6 @@ | ||
<a name="ref.Element.redraw"></a> | ||
### .redraw( [force] ) | ||
<a name="ref.Element.outerDraw"></a> | ||
### .outerDraw( [force] ) | ||
* force `boolean` **INTERNAL** when set (default: false) the element is *redrawn* even if it is hidden: i.e. the parent is redrawn, | ||
* force `boolean` **INTERNAL** when set (default: false) the element is *outerDrawn* even if it is hidden: i.e. the parent is outerDrawn, | ||
it would effectively clear an hidden element from its parent | ||
@@ -167,3 +167,3 @@ | ||
While `.draw()` is used when drawing the current *element* is enough (the *element* has not moved, and has not been resized), | ||
`.redraw()` is used it is necessary to draw the closest ancestor which is a container. | ||
`.outerDraw()` is used when it is necessary to draw the closest ancestor which is a container. | ||
@@ -170,0 +170,0 @@ It is called internally/automatically, userland code should not be bothered with that, except in rare use-cases. |
@@ -554,21 +554,23 @@ | ||
```js | ||
var term = require( 'terminal-kit' ).terminal ; | ||
async function func() { | ||
var term = require( 'terminal-kit' ).terminal ; | ||
var history = [ 'John' , 'Jack' , 'Joey' , 'Billy' , 'Bob' ] ; | ||
var history = [ 'John' , 'Jack' , 'Joey' , 'Billy' , 'Bob' ] ; | ||
var autoComplete = [ | ||
'Barack Obama' , 'George W. Bush' , 'Bill Clinton' , 'George Bush' , | ||
'Ronald W. Reagan' , 'Jimmy Carter' , 'Gerald Ford' , 'Richard Nixon' , | ||
'Lyndon Johnson' , 'John F. Kennedy' , 'Dwight Eisenhower' , | ||
'Harry Truman' , 'Franklin Roosevelt' | ||
] ; | ||
var autoComplete = [ | ||
'Barack Obama' , 'George W. Bush' , 'Bill Clinton' , 'George Bush' , | ||
'Ronald W. Reagan' , 'Jimmy Carter' , 'Gerald Ford' , 'Richard Nixon' , | ||
'Lyndon Johnson' , 'John F. Kennedy' , 'Dwight Eisenhower' , | ||
'Harry Truman' , 'Franklin Roosevelt' | ||
] ; | ||
term( 'Please enter your name: ' ) ; | ||
term( 'Please enter your name: ' ) ; | ||
var input = await term.inputField( | ||
{ history: history , autoComplete: autoComplete , autoCompleteMenu: true } | ||
).promise ; | ||
var input = await term.inputField( | ||
{ history: history , autoComplete: autoComplete , autoCompleteMenu: true } | ||
).promise ; | ||
term.green( "\nYour name is '%s'\n" , input ) ; | ||
process.exit() ; | ||
term.green( "\nYour name is '%s'\n" , input ) ; | ||
process.exit() ; | ||
} | ||
``` | ||
@@ -575,0 +577,0 @@ |
@@ -88,3 +88,3 @@ | ||
* *endOfLine*: move the cursor at the end of input, default: END | ||
* *copyClipboard*: copy to clipboard (rely on xclip), default: CTRL_O | ||
* *copyClipboard*: copy to clipboard (rely on xclip), default: CTRL_Y | ||
* *pasteClipboard*: paste from clipboard (rely on xclip), default: CTRL_P | ||
@@ -91,0 +91,0 @@ |
@@ -14,4 +14,12 @@ | ||
Complex markup is used to lift the single character limitation, it starts with `^[` followed by the command, and ends with `]`. | ||
For example, the string `"This is ^[green]green^ and this is ^[red]red^ !"` would output the word 'green' in green and 'red' in red, | ||
when passed to most Terminal-kit functions. | ||
Complex markup supports the `key:value` format, the previous markup is equivalent to: `"This is ^[fg:green]green^ and this is ^[fg:red]red^ !"`. | ||
In that example, *fg* was for *ForeGround color*, in the same vein *bg* can be used to set up the *BackGround color*: `"This is ^[bg:green]green^ and this is ^[bg:red]red^ !"`. | ||
If the terminal support *true color*, you can use hexadecimal color code (`#` followed by 3 or 6 hexadecimal characters), e.g.: `"This is ^[#f9a]pink^ !"`. | ||
#### Special markup | ||
@@ -73,1 +81,17 @@ | ||
#### Complex markup *key:value* format | ||
By keys: | ||
* `fg` (or aliases: `fgColor`, `color`, `c`) set the foreground color, the value can be one of the ANSI color (*red*, *brightRed*, etc), | ||
it can also be any color declared in a *Palette* for methods of object supporting `Palette`, it can be a color-code (e.g.: `#aa5577`) | ||
if both the terminal and the method support *true-color*. | ||
* `bg` (or alias: `bgColor`) set the background color, the supported values are exactly the same than for *foreground color*. | ||
#### Complex markup **NOT** in the *key:value* format | ||
Any ANSI color (*red*, *brightRed*, etc) or color code (e.g.: `#aa5577`) will be considered as the value for the *foreground color*. | ||
@@ -71,3 +71,3 @@ | ||
* *scrollRight*: scroll right, default: RIGHT | ||
* *copyClipboard*: copy to clipboard, default: CTRL_O | ||
* *copyClipboard*: copy to clipboard, default: CTRL_Y | ||
@@ -96,2 +96,3 @@ | ||
if set, it is possible to scroll down until the bottom of the content reaches the top of the textBox | ||
* tabWidth `number` (default: 4) number of cells (=spaces) for the tab character | ||
* lineWrap `boolean` when set, the text content is wrapped to the next line instead of being clipped by the textBox border | ||
@@ -168,3 +169,3 @@ * wordWrap `boolean` like `lineWrap` but is word-aware, i.e. it doesn't split words | ||
* content `string` the text-content to prepend | ||
* dontDraw `boolean` if set, don't redraw the widget (default: false, redraw) | ||
* dontDraw `boolean` if set, don't outerDraw the widget (default: false, outerDraw) | ||
@@ -179,3 +180,3 @@ Prepend text-content at the begining of the current content. It supports markup or ansi if the textBox was instanciated with the `contentHasMarkup` options on. | ||
* content `string` the text-content to append | ||
* dontDraw `boolean` if set, don't redraw the widget (default: false, redraw) | ||
* dontDraw `boolean` if set, don't outerDraw the widget (default: false, outerDraw) | ||
@@ -190,3 +191,3 @@ Append text-content at the end of the current content. It supports markup or ansi if the textBox was instanciated with the `contentHasMarkup` options on. | ||
* content `string` the text-content to append | ||
* dontDraw `boolean` if set, don't redraw the widget (default: false, redraw) | ||
* dontDraw `boolean` if set, don't outerDraw the widget (default: false, outerDraw) | ||
@@ -215,3 +216,3 @@ This method is almost like [.appendContent()](ref.TextBox.appendContent), but more suitable for logging. | ||
default: false. | ||
* dontDraw `boolean` if set, don't redraw the widget (default: false, redraw) | ||
* dontDraw `boolean` if set, don't outerDraw the widget (default: false, outerDraw) | ||
@@ -218,0 +219,0 @@ It set the alternate text-content, work like its [.setContent()](#ref.Element.setContent) counterpart. |
@@ -567,7 +567,9 @@ | ||
<a name="ref.TextBuffer.load"></a> | ||
### .load( filepath , callback ) | ||
### .load( filepath , [callback] ) | ||
* filepath `string` the path of the file to load | ||
* callback `Function( error )` completion callback | ||
* callback `Function( error )` completion callback, DEPRECATED: prefer the Promise interface. | ||
**If callback is omitted**, it returns a Promise on completion. | ||
This erases all contents (text, attr and misc) and loads the content of the file (which is a text file). | ||
@@ -578,8 +580,10 @@ | ||
<a name="ref.TextBuffer.save"></a> | ||
### .save( filepath , callback ) | ||
### .save( filepath , [callback] ) | ||
* filepath `string` the path of the file to save into | ||
* callback `Function( error )` completion callback | ||
* callback `Function( error )` completion callback, DEPRECATED: prefer the Promise interface. | ||
**If callback is omitted**, it returns a Promise on completion. | ||
This saves the raw text content into a file. | ||
@@ -36,2 +36,10 @@ | ||
* [.setCellContent()](#ref.TextTable.setCellContent) | ||
* [.setCellAttr()](#ref.TextTable.setCellAttr) | ||
* [.resetCellAttr()](#ref.TextTable.resetCellAttr) | ||
* [.setRowAttr()](#ref.TextTable.setRowAttr) | ||
* [.resetRowAttr()](#ref.TextTable.resetRowAttr) | ||
* [.setColumnAttr()](#ref.TextTable.setColumnAttr) | ||
* [.resetColumnAttr()](#ref.TextTable.resetColumnAttr) | ||
* [.setTableAttr()](#ref.TextTable.setTableAttr) | ||
* [.resetTableAttr()](#ref.TextTable.resetTableAttr) | ||
@@ -107,3 +115,4 @@ * Inherit methods and properties from [Element](Element.md#ref.Element.toc) | ||
* content `string` the new content for this table cell | ||
* dontDraw `boolean` when set, the cell content's update does not trigger the *redraw* of the *textTable* | ||
* dontDraw `boolean` when set, the cell content's update does not trigger the *outerDraw* of the *textTable* (or of the cell's *textBox* | ||
if *dontUpdateLayout* is set) | ||
* dontUpdateLayout `boolean` when set, the table layout is not updated | ||
@@ -115,1 +124,87 @@ | ||
<a name="ref.TextTable.setCellAttr"></a> | ||
### .setCellAttr( x , y , textAttr , [voidAttr] , [dontDraw] ) | ||
* x,y `number` the cell coordinate to modify | ||
* textAttr `object` generic/default attributes for the cell's content (*textBox*) | ||
* voidAttr `object` attributes for the area of the cell (*textBox*) without any text content, default to the *textAttr* argument | ||
* dontDraw `boolean` when set, the cell attr's update does not trigger the *outerDraw* of the cell's *textBox* | ||
This update an existing cell text attribute. | ||
<a name="ref.TextTable.resetCellAttr"></a> | ||
### .resetCellAttr( x , y , [dontDraw] ) | ||
* x,y `number` the cell coordinate to reset | ||
* dontDraw `boolean` when set, the cell attr's update does not trigger the *outerDraw* of the cell's *textBox* | ||
This reset an existing cell text attribute to what it should be, based upon the constructor's parameters. | ||
<a name="ref.TextTable.setRowAttr"></a> | ||
### .setRowAttr( y , textAttr , [voidAttr] , [dontDraw] ) | ||
* y `number` the row's index to modify | ||
* textAttr `object` generic/default attributes for the row's cell's content (*textBox*) | ||
* voidAttr `object` attributes for the area of the cell (*textBox*) without any text content, default to the *textAttr* argument | ||
* dontDraw `boolean` when set, the cell attr's update does not trigger the *outerDraw* of the *textTable* | ||
This update all cells' text attribute of a row. | ||
<a name="ref.TextTable.resetRowAttr"></a> | ||
### .resetRowAttr( y , [dontDraw] ) | ||
* y `number` the row's index to reset | ||
* dontDraw `boolean` when set, the cell attr's update does not trigger the *outerDraw* of the *textTable* | ||
This reset all cells' text attribute of a row to what it should be, based upon the constructor's parameters. | ||
<a name="ref.TextTable.setColumnAttr"></a> | ||
### .setColumnAttr( x , textAttr , voidAttr , [dontDraw] ) | ||
* x `number` the column's index to modify | ||
* textAttr `object` generic/default attributes for the column's cell's content (*textBox*) | ||
* voidAttr `object` attributes for the area of the cell (*textBox*) without any text content, default to the *textAttr* argument | ||
* dontDraw `boolean` when set, the cell attr's update does not trigger the *outerDraw* of the *textTable* | ||
This update all cells's text attribute of a column. | ||
<a name="ref.TextTable.resetColumnAttr"></a> | ||
### .resetColumnAttr( x , [dontDraw] ) | ||
* x `number` the column's index to reset | ||
* dontDraw `boolean` when set, the cell attr's update does not trigger the *outerDraw* of the *textTable* | ||
This reset all cells' text attribute of a column to what it should be, based upon the constructor's parameters. | ||
<a name="ref.TextTable.setTableAttr"></a> | ||
### .setTableAttr( textAttr , voidAttr , [dontDraw] ) | ||
* textAttr `object` generic/default attributes for the table's cell's content (*textBox*) | ||
* voidAttr `object` attributes for the area of the cell (*textBox*) without any text content, default to the *textAttr* argument | ||
* dontDraw `boolean` when set, the cell attr's update does not trigger the *outerDraw* of the *textTable* | ||
This update all cells's text attribute. | ||
<a name="ref.TextTable.resetTableAttr"></a> | ||
### .resetTableAttr( [dontDraw] ) | ||
* dontDraw `boolean` when set, the cell attr's update does not trigger the *outerDraw* of the *textTable* | ||
This reset all cells's text attribute to what it should be, based upon the constructor's parameters. | ||
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -36,3 +36,3 @@ The MIT License (MIT) | ||
module.exports = function autoComplete( array , startString , returnAlternatives , prefix , postfix ) { | ||
var i , j , exitLoop , candidate = [] , completed = startString , hasCompleted = false ; | ||
var i , j , exitLoop , candidates = [] , completed = startString , hasCompleted = false ; | ||
@@ -43,11 +43,11 @@ if ( ! prefix ) { prefix = '' ; } | ||
for ( i = 0 ; i < array.length ; i ++ ) { | ||
if ( array[ i ].slice( 0 , startString.length ) === startString ) { candidate.push( array[ i ] ) ; } | ||
if ( array[ i ].slice( 0 , startString.length ) === startString ) { candidates.push( array[ i ] ) ; } | ||
} | ||
if ( ! candidate.length ) { return prefix + completed + postfix ; } | ||
if ( ! candidates.length ) { return prefix + completed + postfix ; } | ||
if ( candidate.length === 1 ) { return prefix + candidate[ 0 ] + postfix ; } | ||
if ( candidates.length === 1 ) { return prefix + candidates[ 0 ] + postfix ; } | ||
// Multiple candidate, complete only the part they have in common | ||
// Multiple candidates, complete only the part they have in common | ||
@@ -58,5 +58,5 @@ j = startString.length ; | ||
for ( j = startString.length ; j < candidate[ 0 ].length ; j ++ ) { | ||
for ( i = 1 ; i < candidate.length ; i ++ ) { | ||
if ( candidate[ i ][ j ] !== candidate[ 0 ][ j ] ) { exitLoop = true ; break ; } | ||
for ( j = startString.length ; j < candidates[ 0 ].length ; j ++ ) { | ||
for ( i = 1 ; i < candidates.length ; i ++ ) { | ||
if ( candidates[ i ][ j ] !== candidates[ 0 ][ j ] ) { exitLoop = true ; break ; } | ||
} | ||
@@ -66,3 +66,3 @@ | ||
completed += candidate[ 0 ][ j ] ; | ||
completed += candidates[ 0 ][ j ] ; | ||
hasCompleted = true ; | ||
@@ -72,5 +72,5 @@ } | ||
if ( returnAlternatives && ! hasCompleted ) { | ||
candidate.prefix = prefix ; | ||
candidate.postfix = postfix ; | ||
return candidate ; | ||
candidates.prefix = prefix ; | ||
candidates.postfix = postfix ; | ||
return candidates ; | ||
} | ||
@@ -77,0 +77,0 @@ |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -68,3 +68,8 @@ The MIT License (MIT) | ||
if ( platform === 'darwin' ) { appId = path.parse( appId ).name ; } | ||
if ( platform === 'darwin' ) { | ||
appId = path.parse( appId ).name ; | ||
} | ||
else if ( platform === 'android' && process.env.TERMUX_VERSION ) { | ||
appId = 'termux' ; | ||
} | ||
@@ -175,2 +180,7 @@ // safe is true if we are sure about our guess | ||
// Android | ||
case 'termux' : | ||
break ; | ||
default : | ||
@@ -177,0 +187,0 @@ if ( ! appId ) { generic = 'unknown' ; } |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -31,2 +31,3 @@ The MIT License (MIT) | ||
const Element = require( './Element.js' ) ; | ||
const Text = require( './Text.js' ) ; | ||
@@ -76,7 +77,6 @@ const spChars = require( '../spChars.js' ) ; | ||
module.exports = AnimatedText ; | ||
Element.inherit( AnimatedText , Text ) ; | ||
AnimatedText.prototype = Object.create( Text.prototype ) ; | ||
AnimatedText.prototype.constructor = AnimatedText ; | ||
AnimatedText.prototype.elementType = 'AnimatedText' ; | ||
AnimatedText.prototype.inlineCursorRestoreAfterDraw = true ; | ||
@@ -83,0 +83,0 @@ |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -69,9 +69,6 @@ The MIT License (MIT) | ||
module.exports = Bar ; | ||
Element.inherit( Bar ) ; | ||
Bar.prototype = Object.create( Element.prototype ) ; | ||
Bar.prototype.constructor = Bar ; | ||
Bar.prototype.elementType = 'Bar' ; | ||
Bar.prototype.preDrawSelf = function() { | ||
@@ -83,3 +80,3 @@ var index , x , fullCells , emptyCells , partialCellRate , | ||
innerSize = this.outputWidth - 2 , | ||
rate = this.value - this.minValue / ( this.maxValue - this.minValue ) ; | ||
rate = ( this.value - this.minValue ) / ( this.maxValue - this.minValue ) ; | ||
@@ -86,0 +83,0 @@ if ( ! rate || rate < 0 ) { rate = 0 ; } |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -57,2 +57,3 @@ The MIT License (MIT) | ||
this.buttons = [] ; | ||
this.hotkeyToButtonIndex = new Map() ; | ||
this.focusChild = null ; | ||
@@ -95,3 +96,2 @@ | ||
this.onSubmenuSubmit = this.onSubmenuSubmit.bind( this ) ; | ||
this.onKey = this.onKey.bind( this ) ; | ||
this.onWheel = this.onWheel.bind( this ) ; | ||
@@ -104,26 +104,26 @@ this.onFocus = this.onFocus.bind( this ) ; | ||
this.buttonFocusAttr = options.buttonFocusAttr || this.defaultOptions.buttonFocusAttr || { bgColor: 'white' , color: 'black' , bold: true } ; | ||
this.buttonDisabledAttr = options.buttonDisabledAttr || this.defaultOptions.buttonDisabledAttr || { bgColor: 'black' , color: 'brightBlack' , bold: true } ; | ||
this.buttonSubmittedAttr = options.buttonSubmittedAttr || this.defaultOptions.buttonSubmittedAttr || { bgColor: 'brightBlack' , color: 'brightWhite' , bold: true } ; | ||
this.turnedOnBlurAttr = options.turnedOnBlurAttr || this.defaultOptions.turnedOnBlurAttr || { bgColor: 'cyan' } ; | ||
this.turnedOnFocusAttr = options.turnedOnFocusAttr || this.defaultOptions.turnedOnFocusAttr || { bgColor: 'brightCyan' , bold: true } ; | ||
this.turnedOffBlurAttr = options.turnedOffBlurAttr || this.defaultOptions.turnedOffBlurAttr || { bgColor: 'gray' , dim: true } ; | ||
this.turnedOffFocusAttr = options.turnedOffFocusAttr || this.defaultOptions.turnedOffFocusAttr || { bgColor: 'white' , color: 'black' , bold: true } ; | ||
this.buttonDisabledAttr = options.buttonDisabledAttr || this.defaultOptions.buttonDisabledAttr || { bgColor: 'black' , color: 'gray' , bold: true } ; | ||
this.buttonSubmittedAttr = options.buttonSubmittedAttr || this.defaultOptions.buttonSubmittedAttr || { bgColor: 'gray' , color: 'brightWhite' , bold: true } ; | ||
this.buttonTurnedOnBlurAttr = options.buttonTurnedOnBlurAttr || this.defaultOptions.buttonTurnedOnBlurAttr || { bgColor: 'cyan' } ; | ||
this.buttonTurnedOnFocusAttr = options.buttonTurnedOnFocusAttr || this.defaultOptions.buttonTurnedOnFocusAttr || { bgColor: 'brightCyan' , color: 'gray' , bold: true } ; | ||
this.buttonTurnedOffBlurAttr = options.buttonTurnedOffBlurAttr || this.defaultOptions.buttonTurnedOffBlurAttr || { bgColor: 'gray' , dim: true } ; | ||
this.buttonTurnedOffFocusAttr = options.buttonTurnedOffFocusAttr || this.defaultOptions.buttonTurnedOffFocusAttr || { bgColor: 'white' , color: 'black' , bold: true } ; | ||
// Padding | ||
this.blurLeftPadding = options.blurLeftPadding || options.leftPadding || '' ; | ||
this.blurRightPadding = options.blurRightPadding || options.rightPadding || '' ; | ||
this.focusLeftPadding = options.focusLeftPadding || options.leftPadding || '' ; | ||
this.focusRightPadding = options.focusRightPadding || options.rightPadding || '' ; | ||
this.disabledLeftPadding = options.disabledLeftPadding || options.leftPadding || '' ; | ||
this.disabledRightPadding = options.disabledRightPadding || options.rightPadding || '' ; | ||
this.submittedLeftPadding = options.submittedLeftPadding || options.leftPadding || '' ; | ||
this.submittedRightPadding = options.submittedRightPadding || options.rightPadding || '' ; | ||
this.turnedOnFocusLeftPadding = options.turnedOnFocusLeftPadding || options.turnedOnLeftPadding || options.leftPadding || '' ; | ||
this.turnedOnFocusRightPadding = options.turnedOnFocusRightPadding || options.turnedOnRightPadding || options.rightPadding || '' ; | ||
this.turnedOffFocusLeftPadding = options.turnedOffFocusLeftPadding || options.turnedOffLeftPadding || options.leftPadding || '' ; | ||
this.turnedOffFocusRightPadding = options.turnedOffFocusRightPadding || options.turnedOffRightPadding || options.rightPadding || '' ; | ||
this.turnedOnBlurLeftPadding = options.turnedOnBlurLeftPadding || options.turnedOnLeftPadding || options.leftPadding || '' ; | ||
this.turnedOnBlurRightPadding = options.turnedOnBlurRightPadding || options.turnedOnRightPadding || options.rightPadding || '' ; | ||
this.turnedOffBlurLeftPadding = options.turnedOffBlurLeftPadding || options.turnedOffLeftPadding || options.leftPadding || '' ; | ||
this.turnedOffBlurRightPadding = options.turnedOffBlurRightPadding || options.turnedOffRightPadding || options.rightPadding || '' ; | ||
this.blurLeftPadding = options.blurLeftPadding ?? options.leftPadding ?? '' ; | ||
this.blurRightPadding = options.blurRightPadding ?? options.rightPadding ?? '' ; | ||
this.focusLeftPadding = options.focusLeftPadding ?? options.leftPadding ?? '' ; | ||
this.focusRightPadding = options.focusRightPadding ?? options.rightPadding ?? '' ; | ||
this.disabledLeftPadding = options.disabledLeftPadding ?? options.leftPadding ?? '' ; | ||
this.disabledRightPadding = options.disabledRightPadding ?? options.rightPadding ?? '' ; | ||
this.submittedLeftPadding = options.submittedLeftPadding ?? options.leftPadding ?? '' ; | ||
this.submittedRightPadding = options.submittedRightPadding ?? options.rightPadding ?? '' ; | ||
this.turnedOnBlurLeftPadding = options.turnedOnBlurLeftPadding ?? options.turnedOnLeftPadding ?? this.blurLeftPadding ; | ||
this.turnedOnBlurRightPadding = options.turnedOnBlurRightPadding ?? options.turnedOnRightPadding ?? this.blurRightPadding ; | ||
this.turnedOffBlurLeftPadding = options.turnedOffBlurLeftPadding ?? options.turnedOffLeftPadding ?? this.blurLeftPadding ; | ||
this.turnedOffBlurRightPadding = options.turnedOffBlurRightPadding ?? options.turnedOffRightPadding ?? this.blurRightPadding ; | ||
this.turnedOnFocusLeftPadding = options.turnedOnFocusLeftPadding ?? options.turnedOnLeftPadding ?? this.focusLeftPadding ; | ||
this.turnedOnFocusRightPadding = options.turnedOnFocusRightPadding ?? options.turnedOnRightPadding ?? this.focusRightPadding ; | ||
this.turnedOffFocusLeftPadding = options.turnedOffFocusLeftPadding ?? options.turnedOffLeftPadding ?? this.focusLeftPadding ; | ||
this.turnedOffFocusRightPadding = options.turnedOffFocusRightPadding ?? options.turnedOffRightPadding ?? this.focusRightPadding ; | ||
this.paddingHasMarkup = !! options.paddingHasMarkup ; | ||
@@ -143,7 +143,6 @@ | ||
module.exports = BaseMenu ; | ||
Element.inherit( BaseMenu ) ; | ||
BaseMenu.prototype = Object.create( Element.prototype ) ; | ||
BaseMenu.prototype.constructor = BaseMenu ; | ||
BaseMenu.prototype.elementType = 'BaseMenu' ; | ||
BaseMenu.prototype.needInput = true ; | ||
@@ -157,6 +156,2 @@ | ||
this.off( 'key' , this.onKey ) ; | ||
this.off( 'wheel' , this.onWheel ) ; | ||
this.off( 'focus' , this.onFocus ) ; | ||
Element.prototype.destroy.call( this , isSubDestroy , noDraw ) ; | ||
@@ -262,60 +257,2 @@ } ; | ||
BaseMenu.prototype.onKey = function( key , trash , data ) { | ||
switch( this.keyBindings[ key ] ) { | ||
case 'previous' : | ||
this.focusChild = this.focusPreviousChild( ! this.maxPage ) ; | ||
if ( this.focusChild === this.children[ 0 ] && this.maxPage && this.page > 0 ) { | ||
this.previousPage( 'backCycle' ) ; | ||
} | ||
break ; | ||
case 'next' : | ||
this.focusChild = this.focusNextChild( ! this.maxPage ) ; | ||
if ( this.focusChild === this.children[ this.children.length - 1 ] && this.maxPage && this.page < this.maxPage ) { | ||
this.nextPage( 'cycle' ) ; | ||
} | ||
break ; | ||
case 'previousPage' : | ||
if ( this.maxPage && this.page > 0 ) { | ||
this.previousPage( 'backCycle' ) ; | ||
} | ||
break ; | ||
case 'nextPage' : | ||
if ( this.maxPage && this.page < this.maxPage ) { | ||
this.nextPage( 'cycle' ) ; | ||
} | ||
break ; | ||
case 'firstPage' : | ||
if ( this.maxPage && this.page !== 0 ) { | ||
this.toPage( 0 , 'backCycle' ) ; | ||
} | ||
break ; | ||
case 'lastPage' : | ||
if ( this.maxPage && this.page !== this.maxPage ) { | ||
this.toPage( this.maxPage , 'cycle' ) ; | ||
} | ||
break ; | ||
case 'parentMenu' : | ||
if ( this.isSubmenu ) { | ||
// Back up the parent, because current instance can be destroyed by parent.closeSubmenu() | ||
let parent = this.parent ; | ||
if ( this.parent.submenuOptions.hideParent ) { this.parent.closeSubmenu() ; } | ||
parent.document.giveFocusTo( parent ) ; | ||
} | ||
break ; | ||
case 'submenu' : | ||
//if ( this.hasSubmenu && this.focusChild?.def?.items ) { | ||
if ( this.hasSubmenu && this.focusChild && this.focusChild.def && this.focusChild.def.items ) { | ||
this.openSubmenu( this.focusChild.value , this.focusChild ) ; | ||
if ( this.submenu ) { this.document.giveFocusTo( this.submenu ) ; } | ||
} | ||
break ; | ||
default : | ||
return ; // Bubble up | ||
} | ||
return true ; // Do not bubble up | ||
} ; | ||
BaseMenu.prototype.onWheel = function( data ) { | ||
@@ -335,4 +272,10 @@ if ( data.yDirection < 0 ) { this.previousPage( 'backCycle' ) ; } | ||
process.nextTick( () => { | ||
if ( this.focusChild && ! this.focusChild.destroyed ) { this.document.giveFocusTo( this.focusChild , 'delegate' ) ; } | ||
else { this.focusChild = this.focusNextChild() ; } | ||
let forceType = type === 'clear' ? type : undefined ; | ||
if ( this.focusChild && ! this.focusChild.destroyed ) { | ||
this.document.giveFocusTo( this.focusChild , forceType || 'delegate' ) ; | ||
} | ||
else { | ||
this.focusChild = this.focusNextChild( undefined , forceType ) ; | ||
} | ||
} ) ; | ||
@@ -363,3 +306,3 @@ } | ||
else { | ||
this.emit( 'submit' , buttonValue , action , this ) ; | ||
this.emit( 'submit' , buttonValue , action , this , button ) ; | ||
} | ||
@@ -383,3 +326,3 @@ } | ||
else { | ||
this.emit( 'blinked' , buttonValue , action , this ) ; | ||
this.emit( 'blinked' , buttonValue , action , this , button ) ; | ||
} | ||
@@ -407,4 +350,4 @@ } | ||
BaseMenu.prototype.onSubmenuSubmit = function( buttonValue , action , button ) { | ||
button.once( 'blinked' , ( buttonValue_ , reserved , button_ ) => { | ||
BaseMenu.prototype.onSubmenuSubmit = function( buttonValue , action , submenu , button ) { | ||
submenu.once( 'blinked' , ( buttonValue_ , reserved , submenu_ , button_ ) => { | ||
if ( this.submenuOptions.closeOn === 'childSubmit' ) { | ||
@@ -414,6 +357,6 @@ this.closeSubmenu() ; | ||
} | ||
this.emit( 'blinked' , buttonValue_ , reserved , this ) ; | ||
this.emit( 'blinked' , buttonValue_ , reserved , this , button ) ; | ||
} ) ; | ||
this.emit( 'submit' , buttonValue , action , this ) ; | ||
this.emit( 'submit' , buttonValue , action , this , button ) ; | ||
} ; | ||
@@ -460,3 +403,2 @@ | ||
//this.submenu = new ColumnMenu( Object.assign( {} , this.submenuOptions , { | ||
this.submenu = new this.constructor( Object.assign( {} , this.submenuOptions , { | ||
@@ -475,3 +417,5 @@ internal: true , | ||
this.redraw() ; | ||
// Draw instead of outerDraw? | ||
//this.draw() ; | ||
this.outerDraw() ; | ||
@@ -483,2 +427,5 @@ if ( this.submenuOptions.focusOnOpen ) { | ||
this.submenu.on( 'submit' , this.onSubmenuSubmit ) ; | ||
// Re-emit itemFocus from child | ||
this.submenu.on( 'itemFocus' , ( ... args ) => this.emit( 'itemFocus' , ... args ) ) ; | ||
} ; | ||
@@ -500,3 +447,2 @@ | ||
BaseMenu.prototype.defaultOptions = {} ; | ||
BaseMenu.prototype.keyBindings = {} ; | ||
BaseMenu.prototype.buttonKeyBindings = {} ; | ||
@@ -510,1 +456,80 @@ BaseMenu.prototype.buttonActionKeyBindings = {} ; | ||
const userActions = BaseMenu.prototype.userActions ; | ||
userActions.character = | ||
userActions.specialKey = function( key ) { | ||
var index = this.hotkeyToButtonIndex.get( key ) ; | ||
if ( index !== undefined && this.buttons[ index ] ) { | ||
if ( this.document ) { | ||
this.document.giveFocusTo( this.buttons[ index ] ) ; | ||
} | ||
this.buttons[ index ].submit() ; | ||
} | ||
else { | ||
// Force bubble | ||
return false ; | ||
} | ||
} ; | ||
userActions.previous = function() { | ||
this.focusChild = this.focusPreviousChild( ! this.maxPage ) ; | ||
if ( this.focusChild === this.children[ 0 ] && this.maxPage && this.page > 0 ) { | ||
this.previousPage( 'backCycle' ) ; | ||
} | ||
} ; | ||
userActions.next = function() { | ||
this.focusChild = this.focusNextChild( ! this.maxPage ) ; | ||
if ( this.focusChild === this.children[ this.children.length - 1 ] && this.maxPage && this.page < this.maxPage ) { | ||
this.nextPage( 'cycle' ) ; | ||
} | ||
} ; | ||
userActions.previousPage = function() { | ||
if ( this.maxPage && this.page > 0 ) { | ||
this.previousPage( 'backCycle' ) ; | ||
} | ||
} ; | ||
userActions.nextPage = function() { | ||
if ( this.maxPage && this.page < this.maxPage ) { | ||
this.nextPage( 'cycle' ) ; | ||
} | ||
} ; | ||
userActions.firstPage = function() { | ||
if ( this.maxPage && this.page !== 0 ) { | ||
this.toPage( 0 , 'backCycle' ) ; | ||
} | ||
} ; | ||
userActions.lastPage = function() { | ||
if ( this.maxPage && this.page !== this.maxPage ) { | ||
this.toPage( this.maxPage , 'cycle' ) ; | ||
} | ||
} ; | ||
userActions.parentMenu = function() { | ||
if ( this.isSubmenu ) { | ||
// Back up the parent, because current instance can be destroyed by parent.closeSubmenu() | ||
let parent = this.parent ; | ||
if ( this.parent.submenuOptions.hideParent ) { this.parent.closeSubmenu() ; } | ||
parent.document.giveFocusTo( parent ) ; | ||
} | ||
else { | ||
return false ; | ||
} | ||
} ; | ||
userActions.submenu = function() { | ||
if ( this.hasSubmenu && this.focusChild?.def?.items ) { | ||
this.openSubmenu( this.focusChild.value , this.focusChild ) ; | ||
if ( this.submenu ) { this.document.giveFocusTo( this.submenu ) ; } | ||
} | ||
else { | ||
return false ; | ||
} | ||
} ; | ||
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -43,59 +43,36 @@ The MIT License (MIT) | ||
this.blurContent = | ||
options.blurContent ? ( Array.isArray( options.blurContent ) ? options.blurContent : [ options.blurContent ] ) : | ||
options.content ; | ||
this.blurLeftPadding = options.blurLeftPadding ?? options.leftPadding ?? '' ; | ||
this.blurRightPadding = options.blurRightPadding ?? options.rightPadding ?? '' ; | ||
this.focusLeftPadding = options.focusLeftPadding ?? options.leftPadding ?? '' ; | ||
this.focusRightPadding = options.focusRightPadding ?? options.rightPadding ?? '' ; | ||
this.disabledLeftPadding = options.disabledLeftPadding ?? options.leftPadding ?? '' ; | ||
this.disabledRightPadding = options.disabledRightPadding ?? options.rightPadding ?? '' ; | ||
this.submittedLeftPadding = options.submittedLeftPadding ?? options.leftPadding ?? '' ; | ||
this.submittedRightPadding = options.submittedRightPadding ?? options.rightPadding ?? '' ; | ||
this.focusContent = | ||
options.focusContent ? ( Array.isArray( options.focusContent ) ? options.focusContent : [ options.focusContent ] ) : | ||
options.content ; | ||
// Used by ToggleButton, it's easier to move the functionality here to compute size at only one place | ||
this.turnedOnBlurLeftPadding = options.turnedOnBlurLeftPadding ?? options.turnedOnLeftPadding ?? this.blurLeftPadding ; | ||
this.turnedOnBlurRightPadding = options.turnedOnBlurRightPadding ?? options.turnedOnRightPadding ?? this.blurRightPadding ; | ||
this.turnedOffBlurLeftPadding = options.turnedOffBlurLeftPadding ?? options.turnedOffLeftPadding ?? this.blurLeftPadding ; | ||
this.turnedOffBlurRightPadding = options.turnedOffBlurRightPadding ?? options.turnedOffRightPadding ?? this.blurRightPadding ; | ||
this.turnedOnFocusLeftPadding = options.turnedOnFocusLeftPadding ?? options.turnedOnLeftPadding ?? this.focusLeftPadding ; | ||
this.turnedOnFocusRightPadding = options.turnedOnFocusRightPadding ?? options.turnedOnRightPadding ?? this.focusRightPadding ; | ||
this.turnedOffFocusLeftPadding = options.turnedOffFocusLeftPadding ?? options.turnedOffLeftPadding ?? this.focusLeftPadding ; | ||
this.turnedOffFocusRightPadding = options.turnedOffFocusRightPadding ?? options.turnedOffRightPadding ?? this.focusRightPadding ; | ||
this.disabledContent = | ||
options.disabledContent ? ( Array.isArray( options.disabledContent ) ? options.disabledContent : [ options.disabledContent ] ) : | ||
options.content ; | ||
this.contentHasMarkup = !! options.contentHasMarkup ; // Force this now, instead of Element behavior | ||
this.paddingHasMarkup = !! options.paddingHasMarkup ; | ||
this.submittedContent = | ||
options.submittedContent ? ( Array.isArray( options.submittedContent ) ? options.submittedContent : [ options.submittedContent ] ) : | ||
options.content ; | ||
// Set by .setContent() | ||
this.content = | ||
this.blurContent = this.focusContent = | ||
this.disabledContent = this.submittedContent = | ||
this.turnedOnBlurContent = this.turnedOffBlurContent = | ||
this.turnedOnFocusContent = this.turnedOffFocusContent = '' ; | ||
this.turnedOnBlurContent = | ||
options.turnedOnBlurContent ? ( Array.isArray( options.turnedOnBlurContent ) ? options.turnedOnBlurContent : [ options.turnedOnBlurContent ] ) : | ||
options.turnedOnContent ? ( Array.isArray( options.turnedOnContent ) ? options.turnedOnContent : [ options.turnedOnContent ] ) : | ||
options.content ; | ||
// We need to compute that now | ||
if ( this.setContent === Button.prototype.setContent ) { | ||
this.setContent( options , this.contentHasMarkup , true , true ) ; | ||
} | ||
this.turnedOffBlurContent = | ||
options.turnedOffBlurContent ? ( Array.isArray( options.turnedOffBlurContent ) ? options.turnedOffBlurContent : [ options.turnedOffBlurContent ] ) : | ||
options.turnedOffContent ? ( Array.isArray( options.turnedOffContent ) ? options.turnedOffContent : [ options.turnedOffContent ] ) : | ||
options.content ; | ||
this.turnedOnFocusContent = | ||
options.turnedOnFocusContent ? ( Array.isArray( options.turnedOnFocusContent ) ? options.turnedOnFocusContent : [ options.turnedOnFocusContent ] ) : | ||
options.turnedOnContent ? ( Array.isArray( options.turnedOnContent ) ? options.turnedOnContent : [ options.turnedOnContent ] ) : | ||
options.content ; | ||
this.turnedOffFocusContent = | ||
options.turnedOffFocusContent ? ( Array.isArray( options.turnedOffFocusContent ) ? options.turnedOffFocusContent : [ options.turnedOffFocusContent ] ) : | ||
options.turnedOffContent ? ( Array.isArray( options.turnedOffContent ) ? options.turnedOffContent : [ options.turnedOffContent ] ) : | ||
options.content ; | ||
this.blurLeftPadding = options.blurLeftPadding || options.leftPadding || '' ; | ||
this.blurRightPadding = options.blurRightPadding || options.rightPadding || '' ; | ||
this.focusLeftPadding = options.focusLeftPadding || options.leftPadding || '' ; | ||
this.focusRightPadding = options.focusRightPadding || options.rightPadding || '' ; | ||
this.disabledLeftPadding = options.disabledLeftPadding || options.leftPadding || '' ; | ||
this.disabledRightPadding = options.disabledRightPadding || options.rightPadding || '' ; | ||
this.submittedLeftPadding = options.submittedLeftPadding || options.leftPadding || '' ; | ||
this.submittedRightPadding = options.submittedRightPadding || options.rightPadding || '' ; | ||
// Used by ToggleButton, it's easier to move the functionality here to compute size at only one place | ||
this.turnedOnBlurLeftPadding = options.turnedOnBlurLeftPadding || options.turnedOnLeftPadding || options.leftPadding || '' ; | ||
this.turnedOnBlurRightPadding = options.turnedOnBlurRightPadding || options.turnedOnRightPadding || options.rightPadding || '' ; | ||
this.turnedOffBlurLeftPadding = options.turnedOffBlurLeftPadding || options.turnedOffLeftPadding || options.leftPadding || '' ; | ||
this.turnedOffBlurRightPadding = options.turnedOffBlurRightPadding || options.turnedOffRightPadding || options.rightPadding || '' ; | ||
this.turnedOnFocusLeftPadding = options.turnedOnFocusLeftPadding || options.turnedOnLeftPadding || options.leftPadding || '' ; | ||
this.turnedOnFocusRightPadding = options.turnedOnFocusRightPadding || options.turnedOnRightPadding || options.rightPadding || '' ; | ||
this.turnedOffFocusLeftPadding = options.turnedOffFocusLeftPadding || options.turnedOffLeftPadding || options.leftPadding || '' ; | ||
this.turnedOffFocusRightPadding = options.turnedOffFocusRightPadding || options.turnedOffRightPadding || options.rightPadding || '' ; | ||
this.paddingHasMarkup = !! options.paddingHasMarkup ; | ||
// Used by menus, to assign nextPage/previousPage action | ||
@@ -110,6 +87,2 @@ this.internalRole = options.internalRole || null ; | ||
if ( this.setContent === Button.prototype.setContent ) { | ||
this.setContent( options.content || '' , options.contentHasMarkup , true , true ) ; | ||
} | ||
this.blurAttr = options.blurAttr || { bgColor: 'brightBlack' } ; | ||
@@ -120,3 +93,3 @@ this.focusAttr = options.focusAttr || { bgColor: 'blue' } ; | ||
this.turnedOnBlurAttr = options.turnedOnBlurAttr || { bgColor: 'cyan' } ; | ||
this.turnedOnFocusAttr = options.turnedOnFocusAttr || { bgColor: 'brightCyan' , bold: true } ; | ||
this.turnedOnFocusAttr = options.turnedOnFocusAttr || { bgColor: 'brightCyan' , color: 'gray' , bold: true } ; | ||
this.turnedOffBlurAttr = options.turnedOffBlurAttr || { bgColor: 'gray' , dim: true } ; | ||
@@ -134,3 +107,2 @@ this.turnedOffFocusAttr = options.turnedOffFocusAttr || { bgColor: 'white' , color: 'black' , bold: true } ; | ||
this.onKey = this.onKey.bind( this ) ; | ||
this.onShortcut = this.onShortcut.bind( this ) ; | ||
@@ -158,7 +130,6 @@ this.onFocus = this.onFocus.bind( this ) ; | ||
module.exports = Button ; | ||
Element.inherit( Button , Text ) ; | ||
Button.prototype = Object.create( Text.prototype ) ; | ||
Button.prototype.constructor = Button ; | ||
Button.prototype.elementType = 'Button' ; | ||
Button.prototype.needInput = true ; | ||
@@ -180,12 +151,5 @@ | ||
Button.prototype.destroy = function( isSubDestroy , noDraw = false ) { | ||
if ( this.destroyed ) { return ; } | ||
this.off( 'key' , this.onKey ) ; | ||
this.off( 'shortcut' , this.onShortcut ) ; | ||
this.off( 'focus' , this.onFocus ) ; | ||
this.off( 'click' , this.onClick ) ; | ||
this.off( 'hover' , this.onHover ) ; | ||
Element.prototype.destroy.call( this , isSubDestroy , noDraw ) ; | ||
// Utility function | ||
Button.prototype._toContentArray = function( content ) { | ||
return ! this.forceContentArray || Array.isArray( content ) ? content : [ content || '' ] ; | ||
} ; | ||
@@ -196,12 +160,45 @@ | ||
Button.prototype.setContent = function( content , hasMarkup , dontDraw = false , dontResize = false ) { | ||
Element.prototype.setContent.call( this , content , hasMarkup , true , true ) ; | ||
this.contentHasMarkup = hasMarkup ; | ||
this.blurContent = this.focusContent = | ||
this.disabledContent = this.submittedContent = | ||
this.turnedOnBlurContent = this.turnedOffBlurContent = | ||
this.turnedOnFocusContent = this.turnedOffFocusContent = | ||
this.content ; | ||
if ( ! content || typeof content !== 'object' || Array.isArray( content ) ) { | ||
this.content = this._toContentArray( content ) ; | ||
this.contentWidth = Element.computeContentWidth( this.content , this.contentHasMarkup ) ; | ||
this.blurContent = this.focusContent = | ||
this.disabledContent = this.submittedContent = | ||
this.turnedOnBlurContent = this.turnedOffBlurContent = | ||
this.turnedOnFocusContent = this.turnedOffFocusContent = | ||
this.content ; | ||
} | ||
else { | ||
if ( content.internal ) { | ||
// This is called from the constructor using the options argument | ||
this.content = this._toContentArray( content.content ) ; | ||
this.blurContent = this._toContentArray( content.blurContent ?? this.content ) ; | ||
this.focusContent = this._toContentArray( content.focusContent ?? this.content ) ; | ||
this.disabledContent = this._toContentArray( content.disabledContent ?? this.content ) ; | ||
this.submittedContent = this._toContentArray( content.submittedContent ?? this.content ) ; | ||
this.turnedOnBlurContent = this._toContentArray( content.turnedOnBlurContent ?? content.turnedOnContent ?? this.content ) ; | ||
this.turnedOffBlurContent = this._toContentArray( content.turnedOffBlurContent ?? content.turnedOffContent ?? this.content ) ; | ||
this.turnedOnFocusContent = this._toContentArray( content.turnedOnFocusContent ?? content.turnedOnContent ?? this.content ) ; | ||
this.turnedOffFocusContent = this._toContentArray( content.turnedOffFocusContent ?? content.turnedOffContent ?? this.content ) ; | ||
} | ||
else { | ||
// Regular call (user, normal case) | ||
this.content = this._toContentArray( content.default ) ; | ||
this.blurContent = this._toContentArray( content.blur ?? this.content ) ; | ||
this.focusContent = this._toContentArray( content.focus ?? this.content ) ; | ||
this.disabledContent = this._toContentArray( content.disabled ?? this.content ) ; | ||
this.submittedContent = this._toContentArray( content.submitted ?? this.content ) ; | ||
this.turnedOnBlurContent = this._toContentArray( content.turnedOnBlur ?? content.turnedOn ?? this.content ) ; | ||
this.turnedOffBlurContent = this._toContentArray( content.turnedOffBlur ?? content.turnedOff ?? this.content ) ; | ||
this.turnedOnFocusContent = this._toContentArray( content.turnedOnFocus ?? content.turnedOn ?? this.content ) ; | ||
this.turnedOffFocusContent = this._toContentArray( content.turnedOffFocus ?? content.turnedOff ?? this.content ) ; | ||
} | ||
this.computeContentWidth( content , this.contentHasMarkup ) ; | ||
} | ||
if ( ! dontResize && this.resizeOnContent ) { this.resizeOnContent() ; } | ||
if ( ! dontDraw ) { this.redraw() ; } | ||
if ( ! dontDraw ) { this.outerDraw() ; } | ||
} ; | ||
@@ -211,2 +208,19 @@ | ||
Button.prototype.computeContentWidth = function() { | ||
this.contentWidth = Math.max( | ||
Element.computeContentWidth( this.blurContent , this.contentHasMarkup ) , | ||
Element.computeContentWidth( this.focusContent , this.contentHasMarkup ) , | ||
Element.computeContentWidth( this.disabledContent , this.contentHasMarkup ) , | ||
Element.computeContentWidth( this.submittedContent , this.contentHasMarkup ) , | ||
Element.computeContentWidth( this.turnedOnFocusContent , this.contentHasMarkup ) , | ||
Element.computeContentWidth( this.turnedOffFocusContent , this.contentHasMarkup ) , | ||
Element.computeContentWidth( this.turnedOnBlurContent , this.contentHasMarkup ) , | ||
Element.computeContentWidth( this.turnedOffBlurContent , this.contentHasMarkup ) | ||
) ; | ||
return this.contentWidth ; | ||
} ; | ||
Button.prototype.computeRequiredWidth = function() { | ||
@@ -274,3 +288,2 @@ return ( | ||
Button.prototype.onFocus = function( focus , type ) { | ||
this.hasFocus = focus ; | ||
this.updateStatus() ; | ||
@@ -328,17 +341,2 @@ this.draw() ; | ||
Button.prototype.onKey = function( key , altKeys , data ) { | ||
switch( this.keyBindings[ key ] ) { | ||
case 'submit' : | ||
if ( this.disabled || this.submitted ) { break ; } | ||
this.submit( this.actionKeyBindings[ key ] ) ; | ||
break ; | ||
default : | ||
return ; // Bubble up | ||
} | ||
return true ; // Do not bubble up | ||
} ; | ||
Button.prototype.onHover = function( data ) { | ||
@@ -381,1 +379,10 @@ if ( this.disabled || this.submitted ) { return ; } | ||
const userActions = Button.prototype.userActions ; | ||
userActions.submit = function( key ) { | ||
if ( this.disabled || this.submitted ) { return ; } | ||
this.submit( this.actionKeyBindings[ key ] ) ; | ||
} ; | ||
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -89,9 +89,6 @@ The MIT License (MIT) | ||
module.exports = ColumnMenu ; | ||
Element.inherit( ColumnMenu , BaseMenu ) ; | ||
ColumnMenu.prototype = Object.create( BaseMenu.prototype ) ; | ||
ColumnMenu.prototype.constructor = ColumnMenu ; | ||
ColumnMenu.prototype.elementType = 'ColumnMenu' ; | ||
ColumnMenu.prototype.inlineNewLine = true ; | ||
@@ -108,6 +105,6 @@ ColumnMenu.prototype.ButtonClass = Button ; | ||
buttonSubmittedAttr: { bgColor: 'gray' , color: 'brightWhite' , bold: true } , | ||
turnedOnBlurAttr: { bgColor: 'cyan' } , | ||
turnedOnFocusAttr: { bgColor: 'brightCyan' , bold: true } , | ||
turnedOffBlurAttr: { bgColor: 'gray' , dim: true } , | ||
turnedOffFocusAttr: { bgColor: 'white' , color: 'black' , bold: true } | ||
buttonTurnedOnBlurAttr: { bgColor: 'cyan' } , | ||
buttonTurnedOnFocusAttr: { bgColor: 'brightCyan' , color: 'gray' , bold: true } , | ||
buttonTurnedOffBlurAttr: { bgColor: 'gray' , dim: true } , | ||
buttonTurnedOffFocusAttr: { bgColor: 'white' , color: 'black' , bold: true } | ||
} ; | ||
@@ -117,14 +114,2 @@ | ||
ColumnMenu.prototype.destroy = function( isSubDestroy , noDraw = false ) { | ||
if ( this.destroyed ) { return ; } | ||
this.off( 'key' , this.onKey ) ; | ||
this.off( 'focus' , this.onFocus ) ; | ||
this.off( 'parentResize' , this.onParentResize ) ; | ||
Element.prototype.destroy.call( this , isSubDestroy , noDraw ) ; | ||
} ; | ||
ColumnMenu.prototype.keyBindings = { | ||
@@ -346,7 +331,12 @@ UP: 'previous' , | ||
ButtonConstructor = def.internalRole ? Button : this.ButtonClass ; | ||
ButtonConstructor = def.internalRole ? Button : | ||
def.type === 'button' ? Button : | ||
def.type === 'toggle' ? ToggleButton : | ||
this.ButtonClass ?? Button ; | ||
isToggle = ButtonConstructor === ToggleButton || ButtonConstructor.prototype instanceof ToggleButton ; | ||
key = def.key ; // For ToggleButton | ||
value = this.childUseParentKeyValue && key && this.value && typeof this.value === 'object' ? this.value[ key ] : def.value ; | ||
value = this.childUseParentKeyValue && key && this.value && typeof this.value === 'object' ? this.value[ key ] : | ||
def.value === undefined && ! isToggle && def.key ? def.key : | ||
def.value ; | ||
@@ -388,6 +378,6 @@ if ( index % 2 ) { | ||
submittedAttr: def.submittedAttr || this.buttonSubmittedAttr , | ||
turnedOnFocusAttr: def.turnedOnFocusAttr || this.turnedOnFocusAttr , | ||
turnedOffFocusAttr: def.turnedOffFocusAttr || this.turnedOffFocusAttr , | ||
turnedOnBlurAttr: def.turnedOnBlurAttr || this.turnedOnBlurAttr , | ||
turnedOffBlurAttr: def.turnedOffBlurAttr || this.turnedOffBlurAttr , | ||
turnedOnFocusAttr: def.turnedOnFocusAttr || this.buttonTurnedOnFocusAttr , | ||
turnedOffFocusAttr: def.turnedOffFocusAttr || this.buttonTurnedOffFocusAttr , | ||
turnedOnBlurAttr: def.turnedOnBlurAttr || this.buttonTurnedOnBlurAttr , | ||
turnedOffBlurAttr: def.turnedOffBlurAttr || this.buttonTurnedOffBlurAttr , | ||
@@ -433,3 +423,3 @@ blurLeftPadding: this.blurLeftPadding , | ||
// Set outputHeight to the correct value | ||
if ( buttonOffsetY < this.outputHeight ) { this.needRedraw = true ; } | ||
if ( buttonOffsetY < this.outputHeight ) { this.needOuterDraw = true ; } | ||
this.pageHeight = this.outputHeight = buttonOffsetY ; | ||
@@ -436,0 +426,0 @@ } ; |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -31,2 +31,3 @@ The MIT License (MIT) | ||
const Element = require( './Element.js' ) ; | ||
const ColumnMenu = require( './ColumnMenu.js' ) ; | ||
@@ -59,9 +60,6 @@ const ToggleButton = require( './ToggleButton.js' ) ; | ||
module.exports = ColumnMenuMulti ; | ||
Element.inherit( ColumnMenuMulti , ColumnMenu ) ; | ||
ColumnMenuMulti.prototype = Object.create( ColumnMenu.prototype ) ; | ||
ColumnMenuMulti.prototype.constructor = ColumnMenuMulti ; | ||
ColumnMenuMulti.prototype.elementType = 'ColumnMenuMulti' ; | ||
ColumnMenuMulti.prototype.ButtonClass = ToggleButton ; | ||
@@ -85,4 +83,5 @@ ColumnMenuMulti.prototype.childUseParentKeyValue = true ; | ||
this.value = {} ; | ||
if ( Array.isArray( value ) || value instanceof Set ) { | ||
this.value = {} ; | ||
for ( let key of value ) { | ||
@@ -95,3 +94,5 @@ if ( key && typeof key === 'string' ) { | ||
else { | ||
this.value = value ; | ||
for ( let key in value ) { | ||
this.value[ key ] = !! value[ key ] ; | ||
} | ||
} | ||
@@ -104,3 +105,3 @@ | ||
if ( button.internalRole || ! button.key || ! ( button instanceof ToggleButton ) ) { return ; } | ||
button.setValue( this.value[ button.key ] ) ; | ||
button.setValue( !! this.value[ button.key ] , true , true ) ; | ||
} ) ; | ||
@@ -113,44 +114,16 @@ | ||
ColumnMenuMulti.prototype.onKey = function( key , altKeys , data ) { | ||
switch( this.keyBindings[ key ] ) { | ||
case 'previous' : | ||
this.focusChild = this.focusPreviousChild( ! this.maxPage ) ; | ||
if ( this.focusChild === this.children[ 0 ] && this.maxPage && this.page > 0 ) { | ||
this.previousPage( 'backCycle' ) ; | ||
} | ||
break ; | ||
case 'next' : | ||
this.focusChild = this.focusNextChild( ! this.maxPage ) ; | ||
if ( this.focusChild === this.children[ this.children.length - 1 ] && this.maxPage && this.page < this.maxPage ) { | ||
this.nextPage( 'cycle' ) ; | ||
} | ||
break ; | ||
case 'previousPage' : | ||
if ( this.maxPage && this.page > 0 ) { | ||
this.previousPage( 'backCycle' ) ; | ||
} | ||
break ; | ||
case 'nextPage' : | ||
if ( this.maxPage && this.page < this.maxPage ) { | ||
this.nextPage( 'cycle' ) ; | ||
} | ||
break ; | ||
case 'firstPage' : | ||
if ( this.maxPage && this.page !== 0 ) { | ||
this.toPage( 0 , 'backCycle' ) ; | ||
} | ||
break ; | ||
case 'lastPage' : | ||
if ( this.maxPage && this.page !== this.maxPage ) { | ||
this.toPage( this.maxPage , 'cycle' ) ; | ||
} | ||
break ; | ||
case 'submit' : | ||
this.emit( 'submit' , this.value , undefined , this ) ; | ||
break ; | ||
default : | ||
return ; // Bubble up | ||
} | ||
ColumnMenuMulti.prototype.setKeyValue = function( key , value , noDraw ) { | ||
if ( ! key ) { return ; } | ||
return true ; // Do not bubble up | ||
this.value[ key ] = !! value ; | ||
// Can be called during init, before .initPage() is called | ||
if ( ! this.buttons ) { return ; } | ||
this.buttons.forEach( button => { | ||
if ( button.internalRole || button.key !== key || ! ( button instanceof ToggleButton ) ) { return ; } | ||
button.setValue( !! value , true , true ) ; | ||
} ) ; | ||
if ( ! noDraw ) { this.draw() ; } | ||
} ; | ||
@@ -169,3 +142,3 @@ | ||
default : | ||
this.emit( 'submit' , this.value , action , this ) ; | ||
this.emit( 'submit' , this.value , action , this , button ) ; | ||
} | ||
@@ -185,1 +158,9 @@ } ; | ||
const userActions = ColumnMenuMulti.prototype.userActions ; | ||
userActions.submit = function() { | ||
this.emit( 'submit' , this.value , undefined , this ) ; | ||
} ; | ||
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -36,7 +36,9 @@ The MIT License (MIT) | ||
const Element = require( './Element.js' ) ; | ||
const Slider = require( './Slider.js' ) ; | ||
const ScreenBuffer = require( '../ScreenBuffer.js' ) ; | ||
// Avoid requiring Slider at top-level, it could cause circular require troubles | ||
//const Slider = require( './Slider.js' ) ; | ||
function Container( options ) { | ||
@@ -49,3 +51,2 @@ // Clone options if necessary | ||
this.onKey = this.onKey.bind( this ) ; | ||
this.onClick = this.onClick.bind( this ) ; | ||
@@ -105,25 +106,13 @@ this.onDrag = this.onDrag.bind( this ) ; | ||
module.exports = Container ; | ||
Element.inherit( Container ) ; | ||
Container.prototype = Object.create( Element.prototype ) ; | ||
Container.prototype.constructor = Container ; | ||
Container.prototype.elementType = 'Container' ; | ||
Container.prototype.isContainer = true ; | ||
Container.prototype.containerBorderSize = 0 ; | ||
const termkit = require( '../termkit.js' ) ; | ||
Container.prototype.destroy = function( isSubDestroy , noDraw = false ) { | ||
if ( this.destroyed ) { return ; } | ||
this.off( 'key' , this.onKey ) ; | ||
this.off( 'click' , this.onClick ) ; | ||
this.off( 'drag' , this.onDrag ) ; | ||
this.off( 'wheel' , this.onWheel ) ; | ||
Element.prototype.destroy.call( this , isSubDestroy , noDraw ) ; | ||
} ; | ||
Container.prototype.keyBindings = { | ||
@@ -144,4 +133,6 @@ UP: 'tinyScrollUp' , | ||
Container.prototype.initChildren = function() { | ||
// Avoid requiring Slider at top-level, it could cause circular require troubles | ||
if ( this.hasVScrollBar ) { | ||
this.vScrollBarSlider = new Slider( { | ||
this.vScrollBarSlider = new termkit.Slider( { | ||
internal: true , | ||
@@ -163,3 +154,3 @@ parent: this , | ||
if ( this.hasHScrollBar ) { | ||
this.hScrollBarSlider = new Slider( { | ||
this.hScrollBarSlider = new termkit.Slider( { | ||
internal: true , | ||
@@ -233,3 +224,3 @@ parent: this , | ||
if ( ! noDraw ) { this.redraw() ; } | ||
if ( ! noDraw ) { this.outerDraw() ; } | ||
} ; | ||
@@ -325,45 +316,2 @@ | ||
Container.prototype.onKey = function( key , trash , data ) { | ||
switch( this.keyBindings[ key ] ) { | ||
case 'tinyScrollUp' : | ||
this.scroll( 0 , Math.ceil( this.viewportHeight / 5 ) ) ; | ||
break ; | ||
case 'tinyScrollDown' : | ||
this.scroll( 0 , -Math.ceil( this.viewportHeight / 5 ) ) ; | ||
break ; | ||
case 'scrollUp' : | ||
this.scroll( 0 , Math.ceil( this.viewportHeight / 2 ) ) ; | ||
break ; | ||
case 'scrollDown' : | ||
this.scroll( 0 , -Math.ceil( this.viewportHeight / 2 ) ) ; | ||
break ; | ||
case 'scrollLeft' : | ||
this.scroll( Math.ceil( this.viewportWidth / 2 ) , 0 ) ; | ||
break ; | ||
case 'scrollRight' : | ||
this.scroll( -Math.ceil( this.viewportWidth / 2 ) , 0 ) ; | ||
break ; | ||
case 'scrollTop' : | ||
this.scrollToTop() ; | ||
break ; | ||
case 'scrollBottom' : | ||
this.scrollToBottom() ; | ||
break ; | ||
default : | ||
return ; // Bubble up | ||
} | ||
return true ; // Do not bubble up | ||
} ; | ||
Container.prototype.onClick = function( data ) { | ||
@@ -396,1 +344,37 @@ // It is susceptible to click event only when it is scrollable | ||
const userActions = Container.prototype.userActions ; | ||
userActions.tinyScrollUp = function() { | ||
this.scroll( 0 , Math.ceil( this.viewportHeight / 5 ) ) ; | ||
} ; | ||
userActions.tinyScrollDown = function() { | ||
this.scroll( 0 , -Math.ceil( this.viewportHeight / 5 ) ) ; | ||
} ; | ||
userActions.scrollUp = function() { | ||
this.scroll( 0 , Math.ceil( this.viewportHeight / 2 ) ) ; | ||
} ; | ||
userActions.scrollDown = function() { | ||
this.scroll( 0 , -Math.ceil( this.viewportHeight / 2 ) ) ; | ||
} ; | ||
userActions.scrollLeft = function() { | ||
this.scroll( Math.ceil( this.viewportWidth / 2 ) , 0 ) ; | ||
} ; | ||
userActions.scrollRight = function() { | ||
this.scroll( -Math.ceil( this.viewportWidth / 2 ) , 0 ) ; | ||
} ; | ||
userActions.scrollTop = function() { | ||
this.scrollToTop() ; | ||
} ; | ||
userActions.scrollBottom = function() { | ||
this.scrollToBottom() ; | ||
} ; | ||
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -109,5 +109,11 @@ The MIT License (MIT) | ||
this.elementByShortcut = {} ; | ||
this.documentClipboards = {} ; | ||
//* | ||
this.setClipboard = Promise.debounceUpdate( async ( str , source ) => { | ||
this.getSystemClipboard = Promise.debounceDelay( 500 , async ( source ) => { | ||
if ( ! this.outputDst.getClipboard ) { return '' ; } | ||
return this.outputDst.getClipboard( source ) ; | ||
} ) ; | ||
this.setSystemClipboard = Promise.debounceUpdate( async ( str , source ) => { | ||
if ( ! this.outputDst.setClipboard ) { return ; } | ||
@@ -120,5 +126,8 @@ await this.outputDst.setClipboard( str , source ) ; | ||
this.getClipboard = Promise.debounceDelay( 500 , async ( source ) => { | ||
if ( ! this.outputDst.getClipboard ) { return '' ; } | ||
return this.outputDst.getClipboard( source ) ; | ||
this.clearSystemClipboard = Promise.debounceUpdate( async ( str , source ) => { | ||
if ( ! this.outputDst.setClipboard ) { return ; } | ||
await this.outputDst.setClipboard( '' , source ) ; | ||
// Avoid running too much xclip shell command | ||
await Promise.resolveTimeout( 500 ) ; | ||
} ) ; | ||
@@ -136,10 +145,6 @@ //*/ | ||
module.exports = Document ; | ||
Element.inherit( Document , Container ) ; | ||
//Document.prototype = Object.create( Element.prototype ) ; | ||
Document.prototype = Object.create( Container.prototype ) ; | ||
Document.prototype.constructor = Document ; | ||
Document.prototype.elementType = 'Document' ; | ||
Document.prototype.destroy = function( isSubDestroy , noDraw = false ) { | ||
@@ -155,4 +160,4 @@ if ( this.destroyed ) { return ; } | ||
this.eventSource = null ; | ||
this.setClipboard = null ; | ||
this.getClipboard = null ; | ||
this.setSystemClipboard = null ; | ||
this.getSystemClipboard = null ; | ||
} ; | ||
@@ -190,5 +195,4 @@ | ||
Document.prototype.giveFocusTo = function( element , type ) { | ||
Document.prototype.giveFocusTo = function( element , type = 'direct' ) { | ||
if ( ! ( element instanceof Element ) ) { throw new TypeError( '' + element + ' is not an instance of Element.' ) ; } | ||
if ( ! type ) { type = 'direct' ; } | ||
if ( this.isAncestorOf( element ) ) { return this.giveFocusTo_( element , type ) ; } | ||
@@ -203,4 +207,9 @@ } ; | ||
if ( this.focusElement !== element ) { | ||
if ( this.focusElement ) { this.focusElement.emit( 'focus' , false , type , this.focusElement ) ; } | ||
if ( this.focusElement ) { | ||
this.focusElement.hasFocus = false ; | ||
this.focusElement.emit( 'focus' , false , type , this.focusElement ) ; | ||
} | ||
this.focusElement = element ; | ||
this.focusElement.hasFocus = true ; | ||
this.focusElement.emit( 'focus' , true , type , this.focusElement ) ; | ||
@@ -398,2 +407,10 @@ } | ||
// TODOC | ||
Document.prototype.setMetaKeyPrefix = function( prefix , remove ) { this.eventSource.setMetaKeyPrefix( prefix , remove ) ; } ; | ||
Document.prototype.getDocumentClipboard = function( key = 'content' ) { return this.documentClipboards[ key ] ; } ; | ||
Document.prototype.setDocumentClipboard = function( value , key = 'content' ) { this.documentClipboards[ key ] = '' + value ; } ; | ||
Document.prototype.clearDocumentClipboard = function( value , key = 'content' ) { delete this.documentClipboards[ key ] ; } ; | ||
Document.prototype.createShortcuts = function( element , ... keys ) { | ||
@@ -508,25 +525,2 @@ if ( element.document !== this ) { return ; } | ||
Document.prototype.mouseMotionStart = function( data ) { | ||
var matches ; | ||
this.motionData.motion = true ; | ||
this.motionData.xFrom = data.xFrom ; | ||
this.motionData.yFrom = data.yFrom ; | ||
this.motionData.x = data.xFrom ; // We use xFrom/yFrom, .mouseMotion() will update it using x/y, setting dx/dy to the delta | ||
this.motionData.y = data.yFrom ; | ||
//this.motionData.element = matches[ 0 ].element ; | ||
//this.motionData.localDx = matches[ 0 ].x - data.xFrom ; | ||
//this.motionData.localDy = matches[ 0 ].y - data.yFrom ; | ||
//matches[ 0 ].element.emit( 'motionStart' , { x: matches[ 0 ].x , y: matches[ 0 ].y } , matches[ 0 ].element ) ; | ||
} ; | ||
Document.prototype.mouseMotionEnd = function() { | ||
this.motionData.motion = false ; | ||
} ; | ||
// Also called from within .mouseDrag() | ||
@@ -581,3 +575,77 @@ Document.prototype.mouseMotion = function( data , exclude = null ) { | ||
Document.prototype.mouseMotionStart = function( data ) { | ||
var matches ; | ||
this.motionData.motion = true ; | ||
this.motionData.xFrom = data.xFrom ; | ||
this.motionData.yFrom = data.yFrom ; | ||
this.motionData.x = data.xFrom ; // We use xFrom/yFrom, .mouseMotion() will update it using x/y, setting dx/dy to the delta | ||
this.motionData.y = data.yFrom ; | ||
//this.motionData.element = matches[ 0 ].element ; | ||
//this.motionData.localDx = matches[ 0 ].x - data.xFrom ; | ||
//this.motionData.localDy = matches[ 0 ].y - data.yFrom ; | ||
//matches[ 0 ].element.emit( 'motionStart' , { x: matches[ 0 ].x , y: matches[ 0 ].y } , matches[ 0 ].element ) ; | ||
} ; | ||
Document.prototype.mouseMotionEnd = function() { | ||
this.motionData.motion = false ; | ||
} ; | ||
Document.prototype.mouseDrag = function( data ) { | ||
var starting = false ; | ||
//console.error( "Drag event:" , JSON.stringify( data ) ) ; | ||
if ( ! this.draggingData.dragging ) { | ||
starting = true ; | ||
this.mouseDragStart( data ) ; | ||
} | ||
this.draggingData.dx = data.x - this.draggingData.x ; | ||
this.draggingData.dy = data.y - this.draggingData.y ; | ||
this.draggingData.x = data.x ; | ||
this.draggingData.y = data.y ; | ||
// Newest Gnome-Terminal send drag event even when no progress have been made, this check avoid useless computing. | ||
if ( ! starting && ! this.draggingData.dx && ! this.draggingData.dy ) { return ; } | ||
// To send a 'drag' event, the origin of the drag should be on the same element | ||
if ( this.draggingData.element ) { | ||
let emitDrag = true ; | ||
if ( ! this.draggingData.element.outerDrag ) { | ||
let matches = this.childrenAt( data.x - this.outputX , data.y - this.outputY , COMMON_MOUSE_AWARE_FILTER ) ; | ||
//console.error( "\tDrag event test:" , this.draggingData.element.debugId() , matches.map( m => m.element.debugId() ) ) ; | ||
emitDrag = matches.some( m => m.element === this.draggingData.element ) ; | ||
} | ||
if ( emitDrag ) { | ||
this.draggingData.element.emit( | ||
'drag' , | ||
{ | ||
xFrom: this.draggingData.xFrom + this.draggingData.localDx , | ||
yFrom: this.draggingData.yFrom + this.draggingData.localDy , | ||
x: data.x + this.draggingData.localDx , | ||
y: data.y + this.draggingData.localDy , | ||
dx: this.draggingData.dx , | ||
dy: this.draggingData.dy | ||
} , | ||
this.draggingData.element | ||
) ; | ||
} | ||
} | ||
// Call .mouseMotion() but exclude the current dragged element | ||
this.mouseMotion( data , this.draggingData.element ) ; | ||
} ; | ||
Document.prototype.mouseDragStart = function( data ) { | ||
//console.error( "Drag START event:" , JSON.stringify( data ) ) ; | ||
var matches ; | ||
@@ -594,2 +662,6 @@ | ||
if ( ! matches.length ) { | ||
this.draggingData.element = null ; | ||
this.draggingData.localDx = null ; | ||
this.draggingData.localDy = null ; | ||
if ( this.hoverElement ) { | ||
@@ -613,2 +685,3 @@ this.hoverElement.emit( 'leave' ) ; | ||
Document.prototype.mouseDragEnd = function( data ) { | ||
//console.error( "Drag END event:" , JSON.stringify( data ) ) ; | ||
if ( this.draggingData.element ) { | ||
@@ -632,42 +705,2 @@ this.draggingData.element.emit( | ||
Document.prototype.mouseDrag = function( data ) { | ||
var starting = false ; | ||
if ( ! this.draggingData.dragging ) { | ||
starting = true ; | ||
this.mouseDragStart( data ) ; | ||
} | ||
//console.error( "mouseDrag" , data ) ; | ||
this.draggingData.dx = data.x - this.draggingData.x ; | ||
this.draggingData.dy = data.y - this.draggingData.y ; | ||
this.draggingData.x = data.x ; | ||
this.draggingData.y = data.y ; | ||
// Newest Gnome-Terminal send drag event even when no progress have been made, this check avoid useless computing. | ||
if ( ! starting && ! this.draggingData.dx && ! this.draggingData.dy ) { return ; } | ||
// To send a 'drag' event, the origin of the drag should be on the same element | ||
if ( this.draggingData.element ) { | ||
this.draggingData.element.emit( | ||
'drag' , | ||
{ | ||
xFrom: this.draggingData.xFrom + this.draggingData.localDx , | ||
yFrom: this.draggingData.yFrom + this.draggingData.localDy , | ||
x: data.x + this.draggingData.localDx , | ||
y: data.y + this.draggingData.localDy , | ||
dx: this.draggingData.dx , | ||
dy: this.draggingData.dy | ||
} , | ||
this.draggingData.element | ||
) ; | ||
} | ||
// Call .mouseMotion() but exclude the current dragged element | ||
this.mouseMotion( data , this.draggingData.element ) ; | ||
} ; | ||
Document.prototype.mouseWheel = function( data ) { | ||
@@ -674,0 +707,0 @@ //var matches = this.childrenAt( data.x - this.outputX , data.y - this.outputY , COMMON_MOUSE_AWARE_FILTER ) ; |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -31,5 +31,6 @@ The MIT License (MIT) | ||
//const Element = require( './Element.js' ) ; | ||
const Element = require( './Element.js' ) ; | ||
const ToggleButton = require( './ToggleButton.js' ) ; | ||
const RowMenu = require( './RowMenu.js' ) ; | ||
const ColumnMenu = require( './ColumnMenu.js' ) ; | ||
const ColumnMenuMixed = require( './ColumnMenuMixed.js' ) ; | ||
@@ -45,2 +46,9 @@ | ||
if ( options.value && typeof options.value === 'object' ) { | ||
this.setValue( options.value , true ) ; | ||
} | ||
else { | ||
this.value = {} ; | ||
} | ||
RowMenu.call( this , options ) ; | ||
@@ -51,18 +59,22 @@ | ||
this.columnMenu = null ; | ||
this.columnButtonBlurAttr = options.buttonBlurAttr || { bgColor: 'gray' , color: 'white' , bold: true } ; | ||
this.columnButtonFocusAttr = options.buttonFocusAttr || { bgColor: 'blue' , color: 'white' , bold: true } ; | ||
this.columnButtonBlurAttr = options.buttonBlurAttr || { bgColor: 'brightBlack' , color: 'white' , bold: true } ; | ||
this.columnButtonTurnedOnBlurAttr = options.buttonTurnedOnBlurAttr || { bgColor: 'gray' , color: 'white' , bold: true } ; | ||
this.columnButtonTurnedOnFocusAttr = options.buttonTurnedOnFocusAttr || { bgColor: 'blue' , color: 'white' , bold: true } ; | ||
this.columnButtonTurnedOffBlurAttr = options.buttonTurnedOffBlurAttr || { bgColor: 'gray' , color: 'white' , dim: true } ; | ||
this.columnButtonTurnedOffFocusAttr = options.buttonTurnedOffFocusAttr || { bgColor: 'blue' , color: 'white' , dim: true } ; | ||
this.clearColumnMenuOnSubmit = !! options.clearColumnMenuOnSubmit ; | ||
this.lastFocusButton = null ; | ||
this.onClickOut = this.onClickOut.bind( this ) ; | ||
this.onButtonFocus = this.onButtonFocus.bind( this ) ; | ||
this.onButtonSubmit = this.onButtonSubmit.bind( this ) ; | ||
this.onColumnMenuSubmit = this.onColumnMenuSubmit.bind( this ) ; | ||
//this.onColumnMenuFocus = this.onColumnMenuFocus.bind( this ) ; | ||
// Bounded by BaseMenu: | ||
//this.onButtonSubmit = this.onButtonSubmit.bind( this ) ; | ||
//this.onButtonFocus = this.onButtonFocus.bind( this ) ; | ||
this.on( 'clickOut' , this.onClickOut ) ; | ||
for ( i = 0 , iMax = this.buttons.length ; i < iMax ; i ++ ) { | ||
this.buttons[ i ].on( 'focus' , this.onButtonFocus ) ; | ||
} | ||
// Only draw if we are not a superclass of the object | ||
@@ -73,19 +85,6 @@ if ( this.elementType === 'DropDownMenu' && ! options.noDraw ) { this.draw() ; } | ||
module.exports = DropDownMenu ; | ||
Element.inherit( DropDownMenu , RowMenu ) ; | ||
DropDownMenu.prototype = Object.create( RowMenu.prototype ) ; | ||
DropDownMenu.prototype.constructor = DropDownMenu ; | ||
DropDownMenu.prototype.elementType = 'DropDownMenu' ; | ||
DropDownMenu.prototype.destroy = function( isSubDestroy , noDraw = false ) { | ||
if ( this.destroyed ) { return ; } | ||
this.off( 'clickOut' , this.onClickOut ) ; | ||
RowMenu.prototype.destroy.call( this , isSubDestroy , noDraw ) ; | ||
} ; | ||
DropDownMenu.prototype.keyBindings = { | ||
@@ -104,7 +103,5 @@ LEFT: 'previous' , | ||
DropDownMenu.prototype.dropDown = function( index , x , y , submittedButtonValue , submittedButtonAction ) { | ||
DropDownMenu.prototype.dropDown = function( index , x , y , submittedButtonValue , submittedButtonAction , button ) { | ||
var itemsDef = this.itemsDef[ index ].items ; | ||
//console.error( "Submit!" , button.childId ) ; | ||
if ( this.columnMenu ) { | ||
@@ -120,3 +117,3 @@ // Already dropped down? Nothing to do! | ||
// Top-button without submenu that have a 'topSubmit' flag on submits themselves | ||
this.emit( 'submit' , submittedButtonValue , submittedButtonAction , this ) ; | ||
this.emit( 'submit' , submittedButtonValue , submittedButtonAction , this , button ) ; | ||
} | ||
@@ -127,4 +124,6 @@ | ||
var hasToggle = itemsDef.some( def => def.type === 'toggle' ) ; | ||
// Make the ColumnMenu a child of the button, so focus cycle will work as expected | ||
this.columnMenu = new ColumnMenu( { | ||
var columnMenuOptions = { | ||
internal: true , | ||
@@ -138,6 +137,21 @@ parent: this.children[ index ] , | ||
items: itemsDef , | ||
value: this.value , | ||
buttonFocusAttr: this.columnButtonFocusAttr , | ||
buttonBlurAttr: this.columnButtonBlurAttr | ||
} ) ; | ||
buttonBlurAttr: this.columnButtonBlurAttr , | ||
buttonTurnedOnBlurAttr: this.columnButtonTurnedOnBlurAttr , | ||
buttonTurnedOnFocusAttr: this.columnButtonTurnedOnFocusAttr , | ||
buttonTurnedOffBlurAttr: this.columnButtonTurnedOffBlurAttr , | ||
buttonTurnedOffFocusAttr: this.columnButtonTurnedOffFocusAttr | ||
} ; | ||
if ( hasToggle ) { | ||
columnMenuOptions.leftPadding = ' ' ; | ||
//columnMenuOptions.turnedOnLeftPadding = ' ✓ ' ; | ||
//columnMenuOptions.turnedOffLeftPadding = ' ✗ ' ; | ||
columnMenuOptions.turnedOnLeftPadding = ' ☑ ' ; | ||
columnMenuOptions.turnedOffLeftPadding = ' ☐ ' ; | ||
} | ||
this.columnMenu = new ColumnMenuMixed( columnMenuOptions ) ; | ||
this.columnMenu.on( 'submit' , this.onColumnMenuSubmit ) ; | ||
@@ -154,6 +168,11 @@ //this.columnMenu.on( 'focus' , this.onColumnMenuFocus ) ; | ||
DropDownMenu.prototype.clearColumnMenu = function() { | ||
DropDownMenu.prototype.clearColumnMenu = function( focusHeadButton = false ) { | ||
if ( ! this.columnMenu ) { return false ; } | ||
this.columnMenu.destroy() ; | ||
this.columnMenu = null ; | ||
if ( focusHeadButton && this.lastFocusButton ) { | ||
this.document.giveFocusTo( this.lastFocusButton , 'clear' ) ; | ||
} | ||
return true ; | ||
@@ -164,2 +183,21 @@ } ; | ||
DropDownMenu.prototype.setValue = function( value , noDraw ) { | ||
if ( ! value || typeof value !== 'object' ) { return ; } | ||
this.value = {} ; | ||
for ( let key in value ) { this.value[ key ] = !! value[ key ] ; } | ||
if ( this.columnMenu ) { this.columnMenu.setValue( value , noDraw ) ; } | ||
} ; | ||
DropDownMenu.prototype.setKeyValue = function( key , value , noDraw ) { | ||
if ( ! key ) { return ; } | ||
this.value[ key ] = !! value ; | ||
if ( this.columnMenu ) { this.columnMenu.setKeyValue( key , value , noDraw ) ; } | ||
} ; | ||
DropDownMenu.prototype.setDropDownItem = function( topItemValue , dropDownItemValue , itemOptions ) { | ||
@@ -194,3 +232,3 @@ var topItem = this.itemsDef.find( e => e.value === topItemValue ) ; | ||
DropDownMenu.prototype.onButtonSubmit = function( buttonValue , action , button ) { | ||
this.dropDown( button.childId , button.outputX , button.outputY + 1 , buttonValue , action ) ; | ||
this.dropDown( button.childId , button.outputX , button.outputY + 1 , buttonValue , action , button ) ; | ||
} ; | ||
@@ -201,3 +239,7 @@ | ||
DropDownMenu.prototype.onButtonFocus = function( focus , type , button ) { | ||
if ( focus ) { this.dropDown( button.childId , button.outputX , button.outputY + 1 ) ; } | ||
this.lastFocusButton = button ; | ||
if ( focus && type !== 'clear' ) { | ||
this.dropDown( button.childId , button.outputX , button.outputY + 1 ) ; | ||
} | ||
} ; | ||
@@ -207,9 +249,22 @@ | ||
DropDownMenu.prototype.onColumnMenuSubmit = function( buttonValue , action , button ) { | ||
button.once( 'blinked' , ( buttonValue_ , reserved , button_ ) => { | ||
if ( this.clearColumnMenuOnSubmit ) { this.clearColumnMenu() ; } | ||
this.emit( 'blinked' , buttonValue_ , reserved , this ) ; | ||
} ) ; | ||
DropDownMenu.prototype.onColumnMenuSubmit = function( buttonValue , action , columnMenu , button ) { | ||
//console.error( "DropDownMenu#onColumnMenuSubmit()" , buttonValue , action , columnMenu?.elementType , button?.elementType ) ; | ||
if ( button instanceof ToggleButton ) { | ||
//console.error( ">>> is ToggleButton" ) ; | ||
if ( button.key ) { | ||
this.value[ button.key ] = button.value ; | ||
} | ||
this.emit( 'submit' , buttonValue , action , this ) ; | ||
if ( this.clearColumnMenuOnSubmit ) { | ||
setTimeout( () => this.clearColumnMenu( true ) , 400 ) ; | ||
} | ||
} | ||
else { | ||
columnMenu.once( 'blinked' , ( buttonValue_ , reserved , columnMenu_ , button_ ) => { | ||
if ( this.clearColumnMenuOnSubmit ) { this.clearColumnMenu( true ) ; } | ||
this.emit( 'blinked' , buttonValue_ , reserved , this , button_ ) ; | ||
} ) ; | ||
} | ||
this.emit( 'submit' , buttonValue , action , this , button ) ; | ||
} ; | ||
@@ -219,26 +274,30 @@ | ||
DropDownMenu.prototype.onKey = function( key , trash , data ) { | ||
switch( this.keyBindings[ key ] ) { | ||
case 'previous' : | ||
this.focusChild = this.focusPreviousChild() ; | ||
//this.clearColumnMenu() ; | ||
break ; | ||
case 'next' : | ||
this.focusChild = this.focusNextChild() ; | ||
//this.clearColumnMenu() ; | ||
break ; | ||
case 'dropDown' : | ||
if ( this.columnMenu ) { this.columnMenu.focusNextChild() ; } | ||
//this.focusChild = this.focusNextChild() ; | ||
//this.clearColumnMenu() ; | ||
break ; | ||
case 'clearColumnMenu' : | ||
// Bubble up only if something was cleared | ||
return this.clearColumnMenu() ; | ||
default : | ||
return ; // Bubble up | ||
const userActions = DropDownMenu.prototype.userActions ; | ||
userActions.previous = function() { | ||
this.focusChild = this.focusPreviousChild() ; | ||
//this.clearColumnMenu() ; | ||
} ; | ||
userActions.next = function() { | ||
this.focusChild = this.focusNextChild() ; | ||
//this.clearColumnMenu() ; | ||
} ; | ||
userActions.dropDown = function() { | ||
if ( this.columnMenu ) { | ||
this.columnMenu.focusNextChild() ; | ||
} | ||
else if ( this.lastFocusButton ) { | ||
this.dropDown( this.lastFocusButton.childId , this.lastFocusButton.outputX , this.lastFocusButton.outputY + 1 ) ; | ||
} | ||
return true ; // Do not bubble up | ||
//this.focusChild = this.focusNextChild() ; | ||
//this.clearColumnMenu() ; | ||
} ; | ||
userActions.clearColumnMenu = function() { | ||
// Bubble up only if something was cleared | ||
return this.clearColumnMenu( true ) ; | ||
} ; | ||
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -31,4 +31,7 @@ The MIT License (MIT) | ||
const Element = require( './Element.js' ) ; | ||
const TextBox = require( './TextBox.js' ) ; | ||
const string = require( 'string-kit' ) ; | ||
const Promise = require( 'seventh' ) ; | ||
@@ -46,7 +49,11 @@ | ||
this.onKey = this.onKey.bind( this ) ; | ||
this.onFocus = this.onFocus.bind( this ) ; | ||
//this.onClick = this.onClick.bind( this ) ; | ||
this.onDragEnd = this.onDragEnd.bind( this ) ; | ||
this.onMiddleClick = this.onMiddleClick.bind( this ) ; | ||
this.debounceTimeout = options.debounceTimeout ?? 100 ; | ||
this.editionUpdateDebounced = | ||
this.debounceTimeout ? Promise.debounceUpdate( { delay: this.debounceTimeout } , this.editionUpdate ) : | ||
this.editionUpdate ; | ||
if ( options.keyBindings ) { this.keyBindings = options.keyBindings ; } | ||
@@ -59,7 +66,10 @@ | ||
this.on( 'key' , this.onKey ) ; | ||
this.on( 'focus' , this.onFocus ) ; | ||
//this.on( 'click' , this.onClick ) ; | ||
this.on( 'dragEnd' , this.onDragEnd ) ; | ||
this.on( 'middleClick' , this.onMiddleClick ) ; | ||
if ( this.setContent === EditableTextBox.prototype.setContent ) { | ||
this.setContent( options.content , options.contentHasMarkup , true ) ; | ||
} | ||
// Only draw if we are not a superclass of the object | ||
@@ -70,7 +80,6 @@ if ( this.elementType === 'EditableTextBox' && ! options.noDraw ) { this.draw() ; } | ||
module.exports = EditableTextBox ; | ||
Element.inherit( EditableTextBox , TextBox ) ; | ||
EditableTextBox.prototype = Object.create( TextBox.prototype ) ; | ||
EditableTextBox.prototype.constructor = EditableTextBox ; | ||
EditableTextBox.prototype.elementType = 'EditableTextBox' ; | ||
EditableTextBox.prototype.needInput = true ; | ||
@@ -80,16 +89,4 @@ | ||
EditableTextBox.prototype.destroy = function( isSubDestroy , noDraw = false ) { | ||
if ( this.destroyed ) { return ; } | ||
this.off( 'key' , this.onKey ) ; | ||
this.off( 'focus' , this.onFocus ) ; | ||
//this.off( 'click' , this.onClick ) ; | ||
this.off( 'middleClick' , this.onMiddleClick ) ; | ||
TextBox.prototype.destroy.call( this , isSubDestroy , noDraw ) ; | ||
} ; | ||
EditableTextBox.prototype.keyBindings = { | ||
CTRL_K: 'meta' , | ||
ENTER: 'newLine' , | ||
@@ -99,2 +96,3 @@ KP_ENTER: 'newLine' , | ||
DELETE: 'delete' , | ||
CTRL_DELETE: 'deleteLine' , | ||
LEFT: 'backward' , | ||
@@ -111,4 +109,24 @@ RIGHT: 'forward' , | ||
PAGE_DOWN: 'scrollDown' , | ||
CTRL_O: 'copyClipboard' , | ||
CTRL_P: 'pasteClipboard' | ||
META_HOME: 'scrollToCursor' , | ||
CTRL_B: 'startOfSelection' , | ||
CTRL_E: 'endOfSelection' , | ||
SHIFT_LEFT: 'expandSelectionBackward' , | ||
SHIFT_RIGHT: 'expandSelectionForward' , | ||
SHIFT_UP: 'expandSelectionUp' , | ||
SHIFT_DOWN: 'expandSelectionDown' , | ||
CTRL_SHIFT_LEFT: 'expandSelectionStartOfWord' , | ||
CTRL_SHIFT_RIGHT: 'expandSelectionEndOfWord' , | ||
// T for Transfer | ||
CTRL_T: 'moveSelection' , | ||
ALT_T: 'copyToDocumentClipboard' , | ||
META_T: 'copyToSystemClipboard' , | ||
// P for Paste / Put | ||
CTRL_P: 'pasteSelection' , | ||
ALT_P: 'pasteDocumentClipboard' , | ||
META_P: 'pasteSystemClipboard' , | ||
// D for Delete | ||
CTRL_D: 'deleteSelection' , | ||
ALT_D: 'clearDocumentClipboard' , | ||
META_D: 'clearSystemClipboard' | ||
} ; | ||
@@ -118,141 +136,114 @@ | ||
EditableTextBox.prototype.drawSelfCursor = function() { | ||
this.textBuffer.drawCursor() ; | ||
} ; | ||
EditableTextBox.prototype.insert = function( str , selectIt = false , internal = false ) { | ||
let x = this.textBuffer.cx , | ||
y = this.textBuffer.cy ; | ||
let count = this.textBuffer.insert( str , this.textAttr ) ; | ||
if ( ! internal ) { | ||
if ( selectIt ) { | ||
this.textBuffer.setSelectionRegion( { | ||
xmin: x , ymin: y , xmax: this.textBuffer.cx , ymax: this.textBuffer.cy | ||
} ) ; | ||
} | ||
EditableTextBox.prototype.getValue = TextBox.prototype.getContent ; | ||
this.editionUpdateDebounced() ; | ||
} | ||
else if ( selectIt ) { | ||
this.textBuffer.setSelectionRegion( { | ||
xmin: x , ymin: y , xmax: this.textBuffer.cx , ymax: this.textBuffer.cy | ||
} ) ; | ||
} | ||
this.emit( 'change' , { | ||
type: 'insert' , | ||
insertedString: str , | ||
count , | ||
internal , | ||
startPosition: { x , y } , | ||
endPosition: { x: this.textBuffer.cx , y: this.textBuffer.cy } | ||
} ) ; | ||
} ; | ||
EditableTextBox.prototype.setValue = function( value , dontDraw ) { | ||
return TextBox.prototype.setContent.call( value , false , dontDraw ) ; | ||
} ; | ||
EditableTextBox.prototype.deleteSelection = function( internal = false ) { | ||
if ( ! this.textBuffer.selectionRegion ) { return ; } | ||
var { xmin , xmax , ymin , ymax } = this.textBuffer.selectionRegion ; | ||
var deleted = this.textBuffer.deleteSelection( true ) ; | ||
EditableTextBox.prototype.onKey = function( key , trash , data ) { | ||
var dy ; | ||
if ( deleted && deleted.count ) { | ||
if ( ! internal ) { | ||
this.textBuffer.cx = xmin ; | ||
this.textBuffer.cy = ymin ; | ||
if ( data && data.isCharacter ) { | ||
this.textBuffer.insert( key , this.textAttr ) ; | ||
this.textBuffer.runStateMachine() ; | ||
this.autoScrollAndDraw() ; | ||
this.editionUpdateDebounced() ; | ||
} | ||
this.emit( 'change' , { | ||
type: 'delete' , | ||
count: deleted.count , | ||
internal , | ||
deletedString: deleted.string , | ||
startPosition: { x: xmin , y: ymin } , | ||
endPosition: { x: xmin , y: ymin } | ||
} ) ; | ||
} | ||
else { | ||
// Here we have a special key | ||
} ; | ||
switch( this.keyBindings[ key ] ) { | ||
case 'newLine' : | ||
this.textBuffer.newLine() ; | ||
this.textBuffer.runStateMachine() ; | ||
this.autoScrollAndDraw() ; | ||
break ; | ||
case 'backDelete' : | ||
this.textBuffer.backDelete() ; | ||
this.textBuffer.runStateMachine() ; | ||
this.autoScrollAndDraw() ; | ||
break ; | ||
case 'delete' : | ||
this.textBuffer.delete() ; | ||
this.textBuffer.runStateMachine() ; | ||
this.autoScrollAndDraw() ; | ||
break ; | ||
EditableTextBox.prototype.deleteRegion = function( region , internal = false ) { | ||
var { xmin , xmax , ymin , ymax } = region ; | ||
var deleted = this.textBuffer.deleteRegion( region , true ) ; | ||
case 'backward' : | ||
this.textBuffer.moveBackward() ; | ||
this.autoScrollAndDrawCursor() ; | ||
break ; | ||
if ( deleted && deleted.count ) { | ||
if ( ! internal ) { | ||
this.textBuffer.cx = xmin ; | ||
this.textBuffer.cy = ymin ; | ||
case 'forward' : | ||
this.textBuffer.moveForward() ; | ||
this.autoScrollAndDrawCursor() ; | ||
break ; | ||
this.editionUpdateDebounced() ; | ||
} | ||
case 'startOfWord' : | ||
this.textBuffer.moveToStartOfWord() ; | ||
this.autoScrollAndDrawCursor() ; | ||
break ; | ||
this.emit( 'change' , { | ||
type: 'delete' , | ||
count: deleted.count , | ||
internal , | ||
deletedString: deleted.string , | ||
startPosition: { x: xmin , y: ymin } , | ||
endPosition: { x: xmin , y: ymin } | ||
} ) ; | ||
} | ||
} ; | ||
case 'endOfWord' : | ||
this.textBuffer.moveToEndOfWord() ; | ||
this.autoScrollAndDrawCursor() ; | ||
break ; | ||
case 'startOfLine' : | ||
this.textBuffer.moveToColumn( 0 ) ; | ||
this.autoScrollAndDrawCursor() ; | ||
break ; | ||
case 'endOfLine' : | ||
this.textBuffer.moveToEndOfLine() ; | ||
this.autoScrollAndDrawCursor() ; | ||
break ; | ||
EditableTextBox.prototype.drawSelfCursor = function() { | ||
this.textBuffer.drawCursor() ; | ||
} ; | ||
case 'down' : | ||
this.textBuffer.moveDown() ; | ||
this.autoScrollAndDrawCursor() ; | ||
break ; | ||
case 'up' : | ||
this.textBuffer.moveUp() ; | ||
this.autoScrollAndDrawCursor() ; | ||
break ; | ||
case 'left' : | ||
this.textBuffer.moveLeft() ; | ||
this.autoScrollAndDrawCursor() ; | ||
break ; | ||
EditableTextBox.prototype.getValue = TextBox.prototype.getContent ; | ||
case 'right' : | ||
this.textBuffer.moveRight() ; | ||
this.autoScrollAndDrawCursor() ; | ||
break ; | ||
case 'tab' : | ||
this.textBuffer.insert( '\t' , this.textAttr ) ; | ||
this.textBuffer.runStateMachine() ; | ||
this.autoScrollAndDraw() ; | ||
break ; | ||
case 'scrollUp' : | ||
dy = Math.ceil( this.outputHeight / 2 ) ; | ||
this.textBuffer.move( 0 , -dy ) ; | ||
this.scroll( 0 , dy ) ; | ||
break ; | ||
EditableTextBox.prototype.setValue = function( value , dontDraw ) { | ||
return this.setContent( value , false , dontDraw ) ; | ||
} ; | ||
case 'scrollDown' : | ||
dy = -Math.ceil( this.outputHeight / 2 ) ; | ||
this.textBuffer.move( 0 , -dy ) ; | ||
this.scroll( 0 , dy ) ; | ||
break ; | ||
case 'pasteClipboard' : | ||
if ( this.document ) { | ||
this.document.getClipboard().then( str => { | ||
if ( str ) { | ||
this.textBuffer.insert( str , this.textAttr ) ; | ||
this.textBuffer.runStateMachine() ; | ||
this.autoScrollAndDraw() ; | ||
} | ||
} ) | ||
.catch( () => undefined ) ; | ||
} | ||
break ; | ||
case 'copyClipboard' : | ||
if ( this.document ) { | ||
this.document.setClipboard( this.textBuffer.getSelectionText() ).catch( () => undefined ) ; | ||
} | ||
break ; | ||
default : | ||
return ; // Bubble up | ||
} | ||
// Called when something was edited, usually requiring to run state machine, auto-scroll and draw. | ||
// Usually, editionUpdateDebounced is called instead. | ||
// Sync, but return a promise (needed for Promise.debounceUpdate()) | ||
EditableTextBox.prototype.editionUpdate = function() { | ||
if ( this.stateMachine ) { | ||
this.textBuffer.runStateMachine() ; | ||
} | ||
return true ; // Do not bubble up | ||
this.autoScrollAndDraw() ; | ||
return Promise.resolved ; | ||
} ; | ||
@@ -263,3 +254,2 @@ | ||
EditableTextBox.prototype.onFocus = function( focus , type ) { | ||
this.hasFocus = focus ; | ||
this.updateStatus() ; | ||
@@ -272,7 +262,32 @@ this.draw() ; | ||
EditableTextBox.prototype.onClick = function( data ) { | ||
if ( ! this.hasFocus ) { | ||
//console.error( "ETB Click:" , data ) ; | ||
if ( this.hasFocus ) { | ||
this.textBuffer.moveTo( data.x - this.scrollX , data.y - this.scrollY ) ; | ||
if ( this.textBuffer.selectionRegion ) { | ||
this.textBuffer.resetSelectionRegion() ; | ||
this.draw() ; | ||
} | ||
else { | ||
this.drawCursor() ; | ||
} | ||
} | ||
else { | ||
this.document.giveFocusTo( this , 'select' ) ; | ||
} | ||
else { | ||
this.textBuffer.moveTo( data.x - this.scrollX , data.y - this.scrollY ) ; | ||
} ; | ||
EditableTextBox.prototype.onDragEnd = function( data ) { | ||
if ( this.hasFocus ) { | ||
if ( data.yFrom < data.y || ( data.yFrom === data.y && data.xFrom <= data.x ) ) { | ||
// Forward selection, put the cursor one cell to the right | ||
this.textBuffer.moveTo( data.x - this.scrollX + 1 , data.y - this.scrollY ) ; | ||
} | ||
else { | ||
// Backward selection, put the cursor one the current cell | ||
this.textBuffer.moveTo( data.x - this.scrollX , data.y - this.scrollY ) ; | ||
} | ||
this.drawCursor() ; | ||
@@ -293,8 +308,4 @@ } | ||
if ( this.document ) { | ||
this.document.getClipboard( 'primary' ).then( str => { | ||
if ( str ) { | ||
this.textBuffer.insert( str , this.textAttr ) ; | ||
this.textBuffer.runStateMachine() ; | ||
this.autoScrollAndDraw() ; | ||
} | ||
this.document.getSystemClipboard( 'primary' ).then( str => { | ||
if ( str ) { this.insert( str ) ; } | ||
//else { this.drawCursor() ; } | ||
@@ -312,1 +323,477 @@ } ) | ||
const userActions = EditableTextBox.prototype.userActions ; | ||
userActions.character = function( key ) { | ||
var x = this.textBuffer.cx , | ||
y = this.textBuffer.cy ; | ||
var count = this.textBuffer.insert( key , this.textAttr ) ; | ||
this.editionUpdateDebounced() ; | ||
this.emit( 'change' , { | ||
type: 'insert' , | ||
insertedString: key , | ||
count , | ||
internal: false , | ||
startPosition: { x , y } , | ||
endPosition: { x: this.textBuffer.cx , y: this.textBuffer.cy } | ||
} ) ; | ||
} ; | ||
userActions.newLine = function() { | ||
var insertedString = '\n' , | ||
count = 1 , | ||
x = this.textBuffer.cx , | ||
y = this.textBuffer.cy ; | ||
this.textBuffer.newLine() ; | ||
this.editionUpdateDebounced() ; | ||
this.emit( 'change' , { | ||
type: 'insert' , | ||
insertedString , | ||
count , | ||
internal: false , | ||
startPosition: { x , y } , | ||
endPosition: { x: this.textBuffer.cx , y: this.textBuffer.cy } | ||
} ) ; | ||
} ; | ||
userActions.tab = function() { | ||
var x = this.textBuffer.cx , | ||
y = this.textBuffer.cy ; | ||
this.textBuffer.insert( '\t' , this.textAttr ) ; | ||
this.editionUpdateDebounced() ; | ||
this.emit( 'change' , { | ||
type: 'insert' , | ||
insertedString: '\t' , | ||
count: 1 , | ||
internal: false , | ||
startPosition: { x , y } , | ||
endPosition: { x: this.textBuffer.cx , y: this.textBuffer.cy } | ||
} ) ; | ||
} ; | ||
userActions.delete = function() { | ||
var x = this.textBuffer.cx , | ||
y = this.textBuffer.cy , | ||
selectionRegion = this.textBuffer.selectionRegion ; | ||
if ( selectionRegion && selectionRegion.ymin === y && selectionRegion.xmin === x ) { | ||
// Instead, delete the whole selection | ||
this.deleteSelection() ; | ||
return ; | ||
} | ||
var deleted = this.textBuffer.delete( 1 , true ) ; | ||
this.editionUpdateDebounced() ; | ||
if ( deleted && deleted.count ) { | ||
this.emit( 'change' , { | ||
type: 'delete' , | ||
count: deleted.count , | ||
internal: false , | ||
deletedString: deleted.string , | ||
startPosition: { x , y } , | ||
endPosition: { x: this.textBuffer.cx , y: this.textBuffer.cy } | ||
} ) ; | ||
} | ||
} ; | ||
userActions.backDelete = function() { | ||
var x = this.textBuffer.cx , | ||
y = this.textBuffer.cy , | ||
selectionRegion = this.textBuffer.selectionRegion ; | ||
if ( selectionRegion ) { | ||
let coord = this.textBuffer.oneStepBackward() ; | ||
if ( selectionRegion.ymax === coord.y && selectionRegion.xmax === coord.x ) { | ||
// Instead, delete the whole selection | ||
this.deleteSelection() ; | ||
return ; | ||
} | ||
} | ||
var deleted = this.textBuffer.backDelete( 1 , true ) ; | ||
this.editionUpdateDebounced() ; | ||
if ( deleted && deleted.count ) { | ||
this.emit( 'change' , { | ||
type: 'backDelete' , | ||
count: deleted.count , | ||
internal: false , | ||
deletedString: deleted.string , | ||
startPosition: { x , y } , | ||
endPosition: { x: this.textBuffer.cx , y: this.textBuffer.cy } | ||
} ) ; | ||
} | ||
} ; | ||
userActions.deleteLine = function() { | ||
var y = this.textBuffer.cy ; | ||
var deleted = this.textBuffer.deleteLine( true ) ; | ||
this.editionUpdateDebounced() ; | ||
if ( deleted && deleted.count ) { | ||
this.emit( 'change' , { | ||
type: 'delete' , | ||
count: deleted.count , | ||
internal: false , | ||
deletedString: deleted.string , | ||
startPosition: { x: 0 , y } , | ||
endPosition: { x: 0 , y: this.textBuffer.cy } | ||
} ) ; | ||
} | ||
} ; | ||
userActions.backward = function() { | ||
this.textBuffer.moveBackward() ; | ||
this.autoScrollAndSmartDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.forward = function() { | ||
this.textBuffer.moveForward() ; | ||
this.autoScrollAndSmartDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.startOfWord = function() { | ||
this.textBuffer.moveToStartOfWord() ; | ||
this.autoScrollAndSmartDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.endOfWord = function() { | ||
this.textBuffer.moveToEndOfWord() ; | ||
this.autoScrollAndSmartDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.startOfLine = function() { | ||
this.textBuffer.moveToColumn( 0 ) ; | ||
this.autoScrollAndSmartDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
// Start of line, but if already at cx = 0, move to the first non-white char (skip indent), | ||
// Also known as “smart home”. | ||
userActions.smartStartOfLine = function() { | ||
if ( this.textBuffer.cx !== 0 ) { | ||
this.textBuffer.moveToColumn( 0 ) ; | ||
} | ||
else { | ||
let cy = this.textBuffer.cy ; | ||
this.textBuffer.moveForward( ( char , x , y ) => y !== cy || ( char !== ' ' && char !== '\t' ) ) ; | ||
if ( this.textBuffer.cy !== cy ) { | ||
// Line has changed, it was an empty line: fallback! | ||
this.textBuffer.moveTo( 0 , cy ) ; | ||
} | ||
} | ||
this.autoScrollAndSmartDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.endOfLine = function() { | ||
this.textBuffer.moveToEndOfLine() ; | ||
this.autoScrollAndSmartDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.down = function() { | ||
this.textBuffer.moveDown() ; | ||
this.autoScrollAndSmartDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.up = function() { | ||
this.textBuffer.moveUp() ; | ||
this.autoScrollAndSmartDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.left = function() { | ||
this.textBuffer.moveLeft() ; | ||
this.autoScrollAndSmartDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.right = function() { | ||
this.textBuffer.moveRight() ; | ||
this.autoScrollAndSmartDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.scrollUp = function() { | ||
var dy = Math.ceil( this.outputHeight / 2 ) ; | ||
this.textBuffer.move( 0 , -dy ) ; | ||
this.scroll( 0 , dy , true ) ; this.autoScrollAndDraw() ; | ||
//this.scroll( 0 , dy ) ; this.autoScrollAndSmartDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.scrollDown = function() { | ||
var dy = -Math.ceil( this.outputHeight / 2 ) ; | ||
this.textBuffer.move( 0 , -dy ) ; | ||
this.scroll( 0 , dy , true ) ; this.autoScrollAndDraw() ; | ||
//this.scroll( 0 , dy ) ; this.autoScrollAndSmartDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.scrollTop = function() { | ||
this.textBuffer.moveTo( 0 , 0 ) ; | ||
this.scrollTo( 0 , 0 ) ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.scrollBottom = function() { | ||
this.textBuffer.moveTo( 0 , this.textBuffer.buffer.length - 1 ) ; | ||
this.autoScrollAndSmartDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.scrollToCursor = function() { | ||
this.autoScrollAndDraw() ; | ||
} ; | ||
userActions.expandSelectionBackward = function() { | ||
var selection = this.textBuffer.selectionRegion , | ||
cx = this.textBuffer.cx , | ||
cy = this.textBuffer.cy , | ||
oneStepBackward = this.textBuffer.oneStepBackward() ; | ||
if ( selection && selection.xmin === cx && selection.ymin === cy ) { | ||
// Can expand | ||
this.textBuffer.moveBackward() ; | ||
this.textBuffer.startOfSelection() ; | ||
} | ||
else if ( selection && selection.xmax === oneStepBackward.x && selection.ymax === oneStepBackward.y ) { | ||
// Can contract | ||
this.textBuffer.moveBackward() ; | ||
this.textBuffer.endOfSelection() ; | ||
} | ||
else { | ||
// Start a new selection | ||
this.textBuffer.endOfSelection() ; | ||
this.textBuffer.moveBackward() ; | ||
this.textBuffer.startOfSelection() ; | ||
} | ||
this.autoScrollAndDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.expandSelectionStartOfWord = function() { | ||
var selection = this.textBuffer.selectionRegion , | ||
cx = this.textBuffer.cx , | ||
cy = this.textBuffer.cy , | ||
oneStepBackward = this.textBuffer.oneStepBackward() ; | ||
if ( selection && selection.xmin === cx && selection.ymin === cy ) { | ||
// Can expand | ||
this.textBuffer.moveToStartOfWord() ; | ||
this.textBuffer.startOfSelection() ; | ||
} | ||
else if ( selection && selection.xmax === oneStepBackward.x && selection.ymax === oneStepBackward.y ) { | ||
// Can contract | ||
this.textBuffer.moveToStartOfWord() ; | ||
this.textBuffer.endOfSelection() ; | ||
} | ||
else { | ||
// Start a new selection | ||
this.textBuffer.endOfSelection() ; | ||
this.textBuffer.moveToStartOfWord() ; | ||
this.textBuffer.startOfSelection() ; | ||
} | ||
this.autoScrollAndDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.expandSelectionUp = function() { | ||
var selection = this.textBuffer.selectionRegion , | ||
cx = this.textBuffer.cx , | ||
cy = this.textBuffer.cy , | ||
oneStepBackward = this.textBuffer.oneStepBackward() ; | ||
if ( selection && selection.xmin === cx && selection.ymin === cy ) { | ||
// Can expand | ||
this.textBuffer.moveUp() ; | ||
this.textBuffer.startOfSelection() ; | ||
} | ||
else if ( | ||
selection && selection.xmax === oneStepBackward.x && selection.ymax === oneStepBackward.y | ||
// Check that there is at least one line of selection | ||
&& ( selection.ymin < oneStepBackward.y - 1 || ( selection.ymin === oneStepBackward.y - 1 && selection.xmin <= oneStepBackward.x ) ) | ||
) { | ||
// Can contract | ||
this.textBuffer.moveUp() ; | ||
this.textBuffer.endOfSelection() ; | ||
} | ||
else { | ||
// Start a new selection | ||
this.textBuffer.endOfSelection() ; | ||
this.textBuffer.moveUp() ; | ||
this.textBuffer.startOfSelection() ; | ||
} | ||
this.autoScrollAndDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.expandSelectionForward = function() { | ||
var selection = this.textBuffer.selectionRegion , | ||
cx = this.textBuffer.cx , | ||
cy = this.textBuffer.cy , | ||
oneStepBackward = this.textBuffer.oneStepBackward() ; | ||
if ( selection && selection.xmax === oneStepBackward.x && selection.ymax === oneStepBackward.y ) { | ||
// Can expand | ||
this.textBuffer.moveForward() ; | ||
this.textBuffer.endOfSelection() ; | ||
} | ||
else if ( selection && selection.xmin === cx && selection.ymin === cy ) { | ||
// Can contract | ||
this.textBuffer.moveForward() ; | ||
this.textBuffer.startOfSelection() ; | ||
} | ||
else { | ||
// Start a new selection | ||
this.textBuffer.startOfSelection() ; | ||
this.textBuffer.moveForward() ; | ||
this.textBuffer.endOfSelection() ; | ||
} | ||
this.autoScrollAndDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.expandSelectionEndOfWord = function() { | ||
var selection = this.textBuffer.selectionRegion , | ||
cx = this.textBuffer.cx , | ||
cy = this.textBuffer.cy , | ||
oneStepBackward = this.textBuffer.oneStepBackward() ; | ||
if ( selection && selection.xmax === oneStepBackward.x && selection.ymax === oneStepBackward.y ) { | ||
// Can expand | ||
this.textBuffer.moveToEndOfWord() ; | ||
this.textBuffer.endOfSelection() ; | ||
} | ||
else if ( selection && selection.xmin === cx && selection.ymin === cy ) { | ||
// Can contract | ||
this.textBuffer.moveToEndOfWord() ; | ||
this.textBuffer.startOfSelection() ; | ||
} | ||
else { | ||
// Start a new selection | ||
this.textBuffer.startOfSelection() ; | ||
this.textBuffer.moveToEndOfWord() ; | ||
this.textBuffer.endOfSelection() ; | ||
} | ||
this.autoScrollAndDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.expandSelectionDown = function() { | ||
var selection = this.textBuffer.selectionRegion , | ||
cx = this.textBuffer.cx , | ||
cy = this.textBuffer.cy , | ||
oneStepBackward = this.textBuffer.oneStepBackward() ; | ||
if ( selection && selection.xmax === oneStepBackward.x && selection.ymax === oneStepBackward.y ) { | ||
// Can expand | ||
this.textBuffer.moveDown() ; | ||
this.textBuffer.endOfSelection() ; | ||
} | ||
else if ( selection && selection.xmin === cx && selection.ymin === cy | ||
&& ( selection.ymax > cy + 1 || ( selection.ymax === cy + 1 && selection.xmax >= cx - 1 ) ) | ||
) { | ||
// Can contract | ||
this.textBuffer.moveDown() ; | ||
this.textBuffer.startOfSelection() ; | ||
} | ||
else { | ||
// Start a new selection | ||
this.textBuffer.startOfSelection() ; | ||
this.textBuffer.moveDown() ; | ||
this.textBuffer.endOfSelection() ; | ||
} | ||
this.autoScrollAndDraw() ; | ||
this.emit( 'cursorMove' ) ; | ||
} ; | ||
userActions.startOfSelection = function() { | ||
this.textBuffer.startOfSelection() ; | ||
this.draw() ; | ||
} ; | ||
userActions.endOfSelection = function() { | ||
this.textBuffer.endOfSelection() ; | ||
this.draw() ; | ||
} ; | ||
userActions.moveSelection = function() { | ||
var str = this.textBuffer.getSelectionText() ; | ||
if ( ! str ) { return ; } | ||
this.deleteSelection( true ) ; | ||
this.insert( str , true ) ; | ||
} ; | ||
userActions.pasteSelection = function() { | ||
var str = this.textBuffer.getSelectionText() ; | ||
if ( str ) { this.insert( str ) ; } | ||
} ; | ||
userActions.pasteDocumentClipboard = function() { | ||
if ( this.document ) { | ||
let str = this.document.getDocumentClipboard() ; | ||
if ( str && typeof str === 'string' ) { | ||
this.insert( str ) ; | ||
} | ||
} | ||
} ; | ||
userActions.pasteSystemClipboard = function() { | ||
if ( this.document ) { | ||
this.document.getSystemClipboard() | ||
.then( str => { | ||
if ( str && typeof str === 'string' ) { | ||
this.insert( str ) ; | ||
} | ||
} ) | ||
.catch( () => undefined ) ; | ||
} | ||
} ; | ||
userActions.deleteSelection = function() { | ||
this.deleteSelection() ; | ||
} ; | ||
userActions.clearDocumentClipboard = function() { | ||
if ( this.document ) { | ||
this.document.clearDocumentClipboard( this.textBuffer.getSelectionText() ) ; | ||
} | ||
} ; | ||
userActions.clearSystemClipboard = function() { | ||
if ( this.document ) { | ||
this.document.clearSystemClipboard( this.textBuffer.getSelectionText() ).catch( () => undefined ) ; | ||
} | ||
} ; | ||
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -35,10 +35,22 @@ The MIT License (MIT) | ||
// Avoid requiring Document at top-level, it could cause circular require troubles | ||
//const Document = require( './Document.js' ) ; | ||
var autoId = 0 ; | ||
function Element( options = {} ) { | ||
this.setInterruptible( true ) ; | ||
this.uid = autoId ++ ; // Useful for debugging | ||
this.parent = options.parent && options.parent.elementType ? options.parent : null ; | ||
//console.error( "Creating " + this.elementType + " #" + this.uid + ( this.parent ? " (from parent " + this.parent.elementType + " #" + this.parent.uid + ")" : '' ) ) ; | ||
this.document = null ; | ||
this.destroyed = false ; | ||
// Event handler bindings | ||
this.onKey = this.onKey.bind( this ) ; | ||
this.inlineTerm = options.inlineTerm || null ; // inline mode, with this terminal as output | ||
@@ -66,5 +78,6 @@ this.strictInline = !! ( | ||
this.content = '' ; | ||
this.contentHasMarkup = false ; | ||
this.contentWidth = 0 ; | ||
// Default value (ensure it's not already set) | ||
this.content = this.content ?? '' ; | ||
this.contentHasMarkup = this.contentHasMarkup ?? false ; | ||
this.contentWidth = this.contentWidth ?? 0 ; | ||
@@ -97,3 +110,3 @@ if ( this.setContent === Element.prototype.setContent ) { | ||
// Used by .updateDraw() | ||
this.needRedraw = false ; | ||
this.needOuterDraw = false ; | ||
@@ -106,3 +119,2 @@ this.savedCursorX = 0 ; | ||
this.zChildren = [] ; // like children, but ordered by zIndex | ||
//this.onKey = this.onKey.bind( this ) , writable: true } , | ||
@@ -130,7 +142,10 @@ // Children needs an inputDst, by default, everything is the same as for output (except for Container) | ||
const termkit = require( '../termkit.js' ) ; | ||
// Destroy the element and all its children, detaching them and removing listeners | ||
Element.prototype.destroy = function( isSubDestroy = false , noDraw = false ) { | ||
if ( this.destroyed ) { return ; } | ||
//console.error( "Destroying" , this.elementType , this.uid , this.key ) ; | ||
@@ -144,2 +159,3 @@ var i , iMax , document = this.document ; | ||
this.removeAllListeners() ; | ||
this.children.length = 0 ; | ||
@@ -165,6 +181,22 @@ this.zChildren.length = 0 ; | ||
Element.inherit = function( Class , FromClass = Element ) { | ||
Class.prototype = Object.create( FromClass.prototype ) ; | ||
Class.prototype.constructor = Class ; | ||
Class.prototype.elementType = Class.name ; | ||
Class.prototype.userActions = Object.create( FromClass.prototype.userActions ) ; | ||
Class.prototype.userActions.__parent = FromClass.prototype.userActions ; | ||
} ; | ||
// Debug function | ||
Element.prototype.debugId = function() { return this.elementType + '#' + this.uid ; } ; | ||
Element.prototype.show = function( noDraw = false ) { | ||
if ( ! this.hidden ) { return this ; } | ||
this.hidden = false ; | ||
if ( ! noDraw ) { this.redraw() ; } | ||
if ( ! noDraw ) { this.outerDraw() ; } | ||
return this ; | ||
@@ -180,4 +212,4 @@ } ; | ||
if ( ! noDraw ) { | ||
// .redraw() with the 'force' option on, because .redraw() does nothing if the element is hidden, but here we want to clear it from its parent | ||
this.redraw( true ) ; | ||
// .outerDraw() with the 'force' option on, because .outerDraw() does nothing if the element is hidden, but here we want to clear it from its parent | ||
this.outerDraw( true ) ; | ||
} | ||
@@ -256,3 +288,2 @@ | ||
for ( i = 0 , iMax = this.children.length ; i < iMax ; i ++ ) { | ||
//console.error( ">>>" , i , iMax ) ; | ||
this.children[ i ].recursiveFixAttachment( document ) ; | ||
@@ -293,2 +324,10 @@ } | ||
// Resize the element to its content | ||
Element.prototype.resizeToContent = function() { | ||
this.outputWidth = this.contentWidth ; | ||
this.outputHeight = this.contentHeight ; | ||
} ; | ||
// Sort zChildren, only necessary when a child zIndex changed | ||
@@ -364,3 +403,3 @@ Element.prototype.zSort = function() { | ||
return ( | ||
hasMarkup === 'ansi' ? Math.max( ... content.map( line => misc.ansiWidth( line ) ) ) : | ||
hasMarkup === 'ansi' || hasMarkup === 'legacyAnsi' ? Math.max( ... content.map( line => misc.ansiWidth( line ) ) ) : | ||
hasMarkup ? Math.max( ... content.map( line => misc.markupWidth( line ) ) ) : | ||
@@ -372,3 +411,3 @@ Math.max( ... content.map( line => string.unicode.width( line ) ) ) | ||
return ( | ||
hasMarkup === 'ansi' ? misc.ansiWidth( content ) : | ||
hasMarkup === 'ansi' || hasMarkup === 'legacyAnsi' ? misc.ansiWidth( content ) : | ||
hasMarkup ? misc.markupWidth( content ) : | ||
@@ -385,3 +424,3 @@ string.unicode.width( content ) | ||
if ( hasMarkup === 'ansi' ) { | ||
if ( hasMarkup === 'ansi' || hasMarkup === 'legacyAnsi' ) { | ||
str = misc.truncateAnsiString( content , maxWidth ) ; | ||
@@ -404,3 +443,3 @@ lastTruncateWidth = misc.getLastTruncateWidth() ; | ||
Element.wordWrapContent = ( content , width , hasMarkup ) => | ||
hasMarkup === 'ansi' ? misc.wordWrapAnsi( content , width ) : | ||
hasMarkup === 'ansi' || hasMarkup === 'legacyAnsi' ? misc.wordWrapAnsi( content , width ) : | ||
hasMarkup ? misc.wordWrapMarkup( content , width ) : | ||
@@ -416,6 +455,17 @@ string.wordwrap( content , { width , fill: true , noJoin: true } ) ; | ||
this.contentHasMarkup = hasMarkup ; | ||
var oldContentWidth = this.contentWidth ; | ||
this.contentWidth = Element.computeContentWidth( content , this.contentHasMarkup ) ; | ||
if ( ! dontResize && this.resizeOnContent ) { this.resizeOnContent() ; } | ||
if ( ! dontDraw ) { this.redraw() ; } | ||
if ( ! dontDraw ) { | ||
// This condition is bad, and is only relevant for Text ATM, | ||
// Should find a clean way to split outputWidth/outputHeight from a sort of wantedWidth/wantedHeight | ||
if ( this.contentWidth !== oldContentWidth && ( this.contentWidth > this.outputWidth || this.resizeOnContent ) ) { | ||
this.outerDraw() ; | ||
} | ||
else { | ||
this.draw() ; | ||
} | ||
} | ||
} ; | ||
@@ -500,3 +550,3 @@ | ||
Element.prototype.focusNextChild = function( loop = true ) { | ||
Element.prototype.focusNextChild = function( loop = true , type = 'cycle' ) { | ||
var index , startingIndex , focusAware ; | ||
@@ -520,3 +570,3 @@ | ||
focusAware = this.document.giveFocusTo_( this.children[ index ] , 'cycle' ) ; | ||
focusAware = this.document.giveFocusTo_( this.children[ index ] , type ) ; | ||
@@ -636,2 +686,3 @@ // Exit if the focus was given to a focus-aware element or if we have done a full loop already | ||
Element.prototype.draw = function( isInitialInlineDraw = false ) { | ||
//console.error( "\n----------------------------\nCalling .draw() for" , this.debugId() , new Error( 'trace:' ) ) ; | ||
if ( ! this.document || this.hidden ) { return this ; } | ||
@@ -659,6 +710,6 @@ | ||
// If it has, then it is necessary to draw the closest ancestor which is a container. | ||
// /!\ THIS METHOD IS WRONG: it should draw the parent container, but don't redraw any children of its children Container | ||
// /!\ Maybe rename this .outerDraw() or .parentDraw() | ||
// /!\ IS THIS METHOD WRONG? it should draw the parent container, but don't redraw any children of its children Container | ||
// Option 'force' redraw even if the element is hidden, in fact it is used by the .hide() method to effectively hide the element on the parent container. | ||
Element.prototype.redraw = function( force = false ) { | ||
Element.prototype.redraw = // DEPRECATED name, use .outerDraw() | ||
Element.prototype.outerDraw = function( force = false ) { | ||
if ( ! this.document || ( this.hidden && ! force ) ) { return this ; } | ||
@@ -668,3 +719,2 @@ | ||
//console.error( "parentContainer:" , container ) ; | ||
if ( ! container ) { this.draw() ; } | ||
@@ -678,7 +728,7 @@ else { container.draw() ; } | ||
// Hard to find a good name, .draw() or .redraw() depending on what have been updated | ||
// Hard to find a good name, .draw() or .outerDraw() depending on what have been updated | ||
Element.prototype.updateDraw = function() { | ||
if ( this.needRedraw ) { this.redraw() ; } | ||
if ( this.needOuterDraw ) { this.outerDraw() ; } | ||
else { this.draw() ; } | ||
this.needRedraw = false ; | ||
this.needOuterDraw = false ; | ||
} ; | ||
@@ -695,3 +745,2 @@ | ||
if ( this.preDrawSelf ) { | ||
//console.error( 'preDrawSelf: ' , this.elementType , this.id ) ; | ||
this.preDrawSelf( ! isSubcall ) ; | ||
@@ -706,3 +755,2 @@ } | ||
if ( isSubcall && this.postDrawSelf ) { | ||
//console.error( 'postDrawSelf: ' , this.elementType , this.id ) ; | ||
this.postDrawSelf( ! isSubcall ) ; | ||
@@ -718,7 +766,5 @@ } | ||
Element.prototype.ascendantDraw = function() { | ||
//console.error( '\nascendantDraw: ' , this.elementType , this.id ) ; | ||
var currentElement ; | ||
if ( this.postDrawSelf && ! this.hidden ) { | ||
//console.error( 'postDrawSelf: ' , this.elementType , this.id ) ; | ||
this.postDrawSelf( true ) ; | ||
@@ -733,3 +779,2 @@ } | ||
if ( currentElement.outputDst !== currentElement.inputDst && currentElement.postDrawSelf && ! currentElement.hidden ) { | ||
//console.error( 'postDrawSelf: ' , currentElement.elementType , currentElement.id ) ; | ||
currentElement.postDrawSelf( false ) ; | ||
@@ -767,2 +812,23 @@ } | ||
// TODOC | ||
Element.prototype.bindKey = function( key , action ) { this.keyBindings[ key ] = action ; } ; | ||
// TODOC | ||
Element.prototype.getKeyBinding = function( key ) { return this.keyBindings[ key ] ?? null ; } ; | ||
// TODOC | ||
Element.prototype.getKeyBindings = function( key ) { return Object.assign( {} , this.keyBindings ) ; } ; | ||
// TODOC | ||
Element.prototype.getActionBinding = function( action , ui = false ) { | ||
var keys = [] ; | ||
for ( let key in this.keyBindings ) { | ||
if ( this.keyBindings[ key ] === action ) { | ||
keys.push( ui ? misc.keyToUserInterfaceName( key ) : key ) ; | ||
} | ||
} | ||
return keys ; | ||
} ; | ||
// For inline widget, having eventually a document just for him, that fit its own size | ||
@@ -813,6 +879,2 @@ Element.createInline = async function( term , Type , options ) { | ||
let scrollY = position.y + element.outputHeight - term.height ; | ||
//console.error( "INLINE -- element.outputWidth" , element.outputWidth ) ; | ||
//console.error( "INLINE -- element.outputHeight" , element.outputHeight ) ; | ||
//console.error( "INLINE -- element.outputY" , element.outputY ) ; | ||
//console.error( "INLINE -- scrollY" , scrollY ) ; | ||
@@ -826,2 +888,6 @@ if ( scrollY > 0 ) { | ||
if ( element.inlineResizeToContent ) { | ||
element.resizeToContent() ; | ||
} | ||
var documentOptions = { | ||
@@ -841,3 +907,3 @@ internal: true , | ||
var document = new Document( documentOptions ) ; | ||
var document = new termkit.Document( documentOptions ) ; | ||
@@ -859,2 +925,36 @@ document.attach( element ) ; | ||
// Default 'key' event management, suitable for almost all use-case, but could be derivated if needed | ||
Element.prototype.onKey = function( key , trash , data ) { | ||
var action = this.keyBindings[ key ] ; | ||
//console.error( this.debugId() , "Key:" , key , "Actions:" , action , !! this.userActions?.[ action ] ) ; // action && this.userActions[ action ] ? "fn: " + this.userActions[ action ].toString() : '' ) ; | ||
if ( action ) { | ||
if ( action === 'meta' ) { | ||
if ( this.document ) { | ||
this.document.setMetaKeyPrefix( 'META' , 'CTRL' ) ; | ||
} | ||
return true ; // Do not bubble up | ||
} | ||
else if ( this.userActions[ action ] ) { | ||
// Do not bubble up except if explicitly false | ||
return ( this.userActions[ action ].call( this , key , trash , data ) ?? true ) || undefined ; | ||
} | ||
} | ||
else if ( data && data.isCharacter ) { | ||
if ( this.userActions.character ) { | ||
// Do not bubble up except if explicitly false | ||
return ( this.userActions.character.call( this , key , trash , data ) ?? true ) || undefined ; | ||
} | ||
} | ||
else if ( this.userActions.specialKey ) { | ||
// Do not bubble up except if explicitly false | ||
return ( this.userActions.specialKey.call( this , key , trash , data ) ?? true ) || undefined ; | ||
} | ||
// Nothing found, bubble up | ||
return ; | ||
} ; | ||
// Should be redefined | ||
@@ -875,4 +975,6 @@ Element.prototype.isContainer = false ; // boolean, true if it's a container, having a different inputDst and outputDst and local coords | ||
Element.prototype.needInput = false ; // no need for input by default (used to configure inline mode) | ||
Element.prototype.outerDrag = false ; // boolean, true if drag event are sent when out of bounds (e.g. useful for moving windows) | ||
const Document = require( './Document.js' ) ; | ||
Element.prototype.keyBindings = {} ; // object, store key bindings, the key is a Terminal Kit key code, the value is an user-action name | ||
Element.prototype.userActions = {} ; // object, the key is an user-action name, the value is a function... THIS IS INHERITED | ||
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -54,3 +54,2 @@ The MIT License (MIT) | ||
this.onButtonSubmit = this.onButtonSubmit.bind( this ) ; | ||
this.onKey = this.onKey.bind( this ) ; | ||
this.onFocus = this.onFocus.bind( this ) ; | ||
@@ -83,7 +82,6 @@ | ||
module.exports = Form ; | ||
Element.inherit( Form ) ; | ||
Form.prototype = Object.create( Element.prototype ) ; | ||
Form.prototype.constructor = Form ; | ||
Form.prototype.elementType = 'Form' ; | ||
Form.prototype.needInput = true ; | ||
@@ -93,13 +91,2 @@ | ||
Form.prototype.destroy = function( isSubDestroy , noDraw = false ) { | ||
if ( this.destroyed ) { return ; } | ||
this.off( 'key' , this.onKey ) ; | ||
this.off( 'focus' , this.onFocus ) ; | ||
Element.prototype.destroy.call( this , isSubDestroy , noDraw ) ; | ||
} ; | ||
Form.prototype.keyBindings = { | ||
@@ -291,19 +278,2 @@ LEFT: 'previous' , | ||
Form.prototype.onKey = function( key , altKeys , data ) { | ||
switch( this.keyBindings[ key ] ) { | ||
case 'previous' : | ||
this.focusChild = this.focusPreviousChild() ; | ||
break ; | ||
case 'next' : | ||
this.focusChild = this.focusNextChild() ; | ||
break ; | ||
default : | ||
return ; // Bubble up | ||
} | ||
return true ; // Do not bubble up | ||
} ; | ||
Form.prototype.onFocus = function( focus , type ) { | ||
@@ -323,6 +293,18 @@ if ( type === 'cycle' || type === 'backCycle' ) { return ; } | ||
Form.prototype.onButtonSubmit = function( buttonValue , action ) { | ||
Form.prototype.onButtonSubmit = function( buttonValue , action , button ) { | ||
this.submitValue = buttonValue ; | ||
this.emit( 'submit' , this.getValue() , action , this ) ; | ||
this.emit( 'submit' , this.getValue() , action , this , button ) ; | ||
} ; | ||
const userActions = Form.prototype.userActions ; | ||
userActions.previous = function() { | ||
this.focusChild = this.focusPreviousChild() ; | ||
} ; | ||
userActions.next = function() { | ||
this.focusChild = this.focusNextChild() ; | ||
} ; | ||
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -31,6 +31,8 @@ The MIT License (MIT) | ||
const Promise = require( 'seventh' ) ; | ||
const Element = require( './Element.js' ) ; | ||
const TextBox = require( './TextBox.js' ) ; | ||
const EditableTextBox = require( './EditableTextBox.js' ) ; | ||
const RowMenu = require( './RowMenu.js' ) ; | ||
const Promise = require( 'seventh' ) ; | ||
const string = require( 'string-kit' ) ; | ||
@@ -51,3 +53,2 @@ const computeAutoCompleteArray = require( '../autoComplete.js' ) ; | ||
* editing actions: deleteAllBefore, deleteAllAfter, deletePreviousWord, deleteNextWord | ||
* meta key for more keyboard commands, e.g.: maybe something like CTRL_K <key> --> META_<key> | ||
* allow placeholder to be used as default (submitting without actually entering anything) when appropriate | ||
@@ -78,2 +79,3 @@ * disable echoing (no output and no cursor movements) | ||
this.onAutoCompleteMenuSubmit = this.onAutoCompleteMenuSubmit.bind( this ) ; | ||
this.onAutoCompleteMenuItemFocus = this.onAutoCompleteMenuItemFocus.bind( this ) ; | ||
this.onAutoCompleteMenuCancel = this.onAutoCompleteMenuCancel.bind( this ) ; | ||
@@ -123,2 +125,4 @@ | ||
this.noEmpty = !! options.noEmpty ; // if set, do not submit empty string | ||
this.disabled = !! options.disabled ; | ||
@@ -131,2 +135,3 @@ this.submitted = !! options.submitted ; | ||
this.useAutoCompleteHint = !! ( this.autoComplete && ( options.useAutoCompleteHint || options.autoCompleteHint ) ) ; | ||
this.autoCompleteHintMinInput = options.autoCompleteHintMinInput || 1 ; // number of input chars before starting to hint | ||
this.useAutoCompleteMenu = !! ( this.autoComplete && ( options.useAutoCompleteMenu || options.autoCompleteMenu ) ) ; | ||
@@ -136,2 +141,5 @@ this.autoCompleteMenu = null ; | ||
this.autoCompleteRightPart = null ; | ||
this.autoCompleteCursorCell = null ; | ||
this.autoCompleteMenuPrefix = null ; | ||
this.autoCompleteMenuPostfix = null ; | ||
@@ -156,7 +164,6 @@ this.menuOptions = Object.assign( {} , this.defaultMenuOptions , options.menu ) ; | ||
module.exports = InlineInput ; | ||
Element.inherit( InlineInput , EditableTextBox ) ; | ||
InlineInput.prototype = Object.create( EditableTextBox.prototype ) ; | ||
InlineInput.prototype.constructor = InlineInput ; | ||
InlineInput.prototype.elementType = 'InlineInput' ; | ||
// Has a fallback textBuffer for hint/placeholder | ||
@@ -185,2 +192,3 @@ InlineInput.prototype.useAltTextBuffer = true ; | ||
InlineInput.prototype.keyBindings = { | ||
CTRL_K: 'meta' , | ||
ENTER: 'submit' , | ||
@@ -201,4 +209,17 @@ KP_ENTER: 'submit' , | ||
END: 'endOfLine' , | ||
CTRL_O: 'copyClipboard' , | ||
CTRL_P: 'pasteClipboard' | ||
CTRL_B: 'startOfSelection' , | ||
CTRL_E: 'endOfSelection' , | ||
// T for Transfer | ||
//CTRL_T: 'moveSelection' , // TODO | ||
ALT_T: 'copyToDocumentClipboard' , | ||
META_T: 'copyToSystemClipboard' , | ||
// P for Paste / Put | ||
CTRL_P: 'pasteSelection' , | ||
ALT_P: 'pasteDocumentClipboard' , | ||
META_P: 'pasteSystemClipboard' , | ||
// D for Delete | ||
//CTRL_D: 'deleteSelection' , // TODO | ||
ALT_D: 'clearDocumentClipboard' , | ||
META_D: 'clearSystemClipboard' | ||
} ; | ||
@@ -208,2 +229,11 @@ | ||
InlineInput.prototype.insert = function( str ) { | ||
this.textBuffer.insert( str , this.textAttr ) ; | ||
this.textBuffer.runStateMachine() ; | ||
if ( this.useAutoCompleteHint ) { this.runAutoCompleteHint( this.autoComplete ) ; } // async | ||
else { this.autoResizeAndDraw() ; } | ||
} ; | ||
InlineInput.prototype.preDrawSelf = function() { | ||
@@ -247,3 +277,9 @@ /* | ||
InlineInput.prototype.runAutoCompleteHint = async function( autoComplete ) { | ||
//console.error( "bob, please") | ||
if ( this.textBuffer.cy === 0 && this.textBuffer.cx < this.autoCompleteHintMinInput ) { | ||
// Not enough input for starting to hint | ||
this.altTextBuffer.setText( '' ) ; | ||
this.autoResizeAndDraw() ; | ||
return ; | ||
} | ||
var autoCompleted ; | ||
@@ -255,27 +291,35 @@ | ||
this.altTextBuffer.setText( '' ) ; | ||
this.autoResizeAndDraw() ; | ||
return ; | ||
} | ||
if ( Array.isArray( autoComplete ) ) { | ||
autoCompleted = computeAutoCompleteArray( autoComplete , leftPart , false ) ; | ||
} | ||
else if ( typeof autoComplete === 'function' ) { | ||
autoCompleted = await autoComplete( leftPart , false ) ; | ||
} | ||
else { | ||
if ( Array.isArray( autoComplete ) ) { | ||
autoCompleted = computeAutoCompleteArray( autoComplete , leftPart , false ) ; | ||
} | ||
else if ( typeof autoComplete === 'function' ) { | ||
autoCompleted = await autoComplete( leftPart , false ) ; | ||
} | ||
else { | ||
this.altTextBuffer.setText( '' ) ; | ||
this.autoResizeAndDraw() ; | ||
return ; | ||
} | ||
if ( Array.isArray( autoCompleted ) ) { | ||
if ( ! autoCompleted.length || autoCompleted.length > 1 ) { | ||
this.altTextBuffer.setText( '' ) ; | ||
this.autoResizeAndDraw() ; | ||
return ; | ||
} | ||
if ( Array.isArray( autoCompleted ) ) { | ||
if ( ! autoCompleted.length ) { return ; } | ||
autoCompleted = autoCompleted[ 0 ] ; | ||
} | ||
autoCompleted = ( autoCompleted.prefix ?? '' ) + autoCompleted[ 0 ] + ( autoCompleted.postfix ?? '' ) ; | ||
} | ||
if ( autoCompleted === leftPart ) { | ||
this.altTextBuffer.setText( '' ) ; | ||
} | ||
else { | ||
this.altTextBuffer.setText( autoCompleted ) ; | ||
//this.altTextBuffer.runStateMachine() ; | ||
} | ||
if ( autoCompleted === leftPart ) { | ||
this.altTextBuffer.setText( '' ) ; | ||
} | ||
else { | ||
this.altTextBuffer.setText( autoCompleted ) ; | ||
//this.altTextBuffer.runStateMachine() ; | ||
} | ||
@@ -290,2 +334,3 @@ this.autoResizeAndDraw() ; | ||
this.autoCompleteCursorCell = this.textBuffer.getCursorCell() ; | ||
[ this.autoCompleteLeftPart , this.autoCompleteRightPart ] = this.textBuffer.getCursorSplittedText() ; | ||
@@ -311,3 +356,3 @@ | ||
autoCompleted = autoCompleted[ 0 ] ; | ||
autoCompleted = ( autoCompleted.prefix ?? '' ) + autoCompleted[ 0 ] + ( autoCompleted.postfix ?? '' ) ; | ||
} | ||
@@ -321,4 +366,13 @@ | ||
InlineInput.prototype.runAutoCompleted = async function( autoCompleted ) { | ||
this.textBuffer.setText( autoCompleted + this.autoCompleteRightPart ) ; | ||
this.textBuffer.setCursorOffset( autoCompleted.length ) ; | ||
if ( autoCompleted.startsWith( this.autoCompleteLeftPart ) ) { | ||
this.textBuffer.insert( autoCompleted.slice( this.autoCompleteLeftPart.length ) ) ; | ||
if ( ! this.textBuffer.updateCursorFromCell( this.autoCompleteCursorCell ) ) { | ||
this.textBuffer.moveToEndOfBuffer() ; | ||
} | ||
} | ||
else { | ||
this.textBuffer.setText( autoCompleted + this.autoCompleteRightPart ) ; | ||
this.textBuffer.moveToEndOfBuffer() ; | ||
} | ||
this.textBuffer.runStateMachine() ; | ||
@@ -349,2 +403,5 @@ this.autoResizeAndDraw() ; | ||
this.autoCompleteMenuPrefix = items.prefix ?? '' ; | ||
this.autoCompleteMenuPostfix = items.postfix ?? '' ; | ||
this.document.giveFocusTo( this.autoCompleteMenu ) ; | ||
@@ -354,2 +411,3 @@ | ||
this.autoCompleteMenu.once( 'cancel' , this.onAutoCompleteMenuCancel ) ; | ||
this.autoCompleteMenu.on( 'itemFocus' , this.onAutoCompleteMenuItemFocus ) ; | ||
} ; | ||
@@ -360,4 +418,9 @@ | ||
InlineInput.prototype.onAutoCompleteMenuSubmit = function( selectedText ) { | ||
selectedText = this.autoCompleteMenuPrefix + selectedText + this.autoCompleteMenuPostfix ; | ||
this.autoCompleteMenu.destroy() ; | ||
this.autoCompleteMenu = null ; | ||
this.autoCompleteMenuPrefix = null ; | ||
this.autoCompleteMenuPostfix = null ; | ||
this.document.giveFocusTo( this ) ; | ||
@@ -369,205 +432,116 @@ this.runAutoCompleted( selectedText ) ; | ||
InlineInput.prototype.onAutoCompleteMenuCancel = function() { | ||
this.autoCompleteMenu.destroy() ; | ||
this.autoCompleteMenu = null ; | ||
this.document.giveFocusTo( this ) ; | ||
} ; | ||
InlineInput.prototype.onAutoCompleteMenuItemFocus = function( selectedText , focus ) { | ||
if ( ! focus || this.autoCompleteRightPart ) { return ; } | ||
selectedText = this.autoCompleteMenuPrefix + selectedText + this.autoCompleteMenuPostfix ; | ||
InlineInput.prototype.onKey = function( key , trash , data ) { | ||
if ( this.autoCompleteMenu ) { | ||
// If the autoCompleteMenu is on, force a cancel | ||
this.autoCompleteMenu.emit( 'cancel' ) ; | ||
if ( selectedText === this.autoCompleteLeftPart ) { | ||
this.altTextBuffer.setText( '' ) ; | ||
} | ||
if ( data && data.isCharacter ) { | ||
if ( this.placeholder ) { | ||
// Remove the placeholder on the first input | ||
this.placeholder = null ; | ||
this.setAltContent( '' , false , true ) ; | ||
} | ||
this.textBuffer.insert( key , this.textAttr ) ; | ||
this.textBuffer.runStateMachine() ; | ||
if ( this.useAutoCompleteHint ) { this.runAutoCompleteHint( this.autoComplete ) ; } | ||
else { this.autoResizeAndDraw() ; } | ||
else { | ||
this.altTextBuffer.setText( selectedText ) ; | ||
//this.altTextBuffer.runStateMachine() ; | ||
} | ||
else { | ||
// Here we have a special key | ||
switch( this.keyBindings[ key ] ) { | ||
case 'submit' : | ||
if ( this.disabled || this.submitted || this.canceled ) { break ; } | ||
//this.submitted = true ; | ||
this.emit( 'submit' , this.getValue() , undefined , this ) ; | ||
break ; | ||
this.autoResizeAndDraw() ; | ||
} ; | ||
case 'cancel' : | ||
if ( ! this.cancelable || this.disabled || this.canceled ) { break ; } | ||
//this.canceled = true ; | ||
this.emit( 'cancel' , this ) ; | ||
break ; | ||
case 'autoComplete' : | ||
if ( ! this.autoComplete ) { break ; } | ||
this.runAutoComplete( this.autoComplete ) ; | ||
break ; | ||
case 'historyAutoComplete' : | ||
if ( ! this.autoComplete ) { break ; } | ||
this.runAutoComplete( this.history ) ; | ||
break ; | ||
InlineInput.prototype.onAutoCompleteMenuCancel = function() { | ||
this.autoCompleteMenu.destroy() ; | ||
this.autoCompleteMenu = null ; | ||
this.document.giveFocusTo( this ) ; | ||
} ; | ||
case 'historyPrevious' : | ||
if ( this.contentIndex <= 0 ) { break ; } | ||
this.contentArray[ this.contentIndex ] = this.getContent() ; | ||
this.contentIndex -- ; | ||
this.setContent( this.contentArray[ this.contentIndex ] ) ; | ||
this.textBuffer.runStateMachine() ; | ||
this.autoResizeAndDraw() ; | ||
break ; | ||
case 'historyNext' : | ||
if ( this.contentIndex >= this.contentArray.length - 1 ) { break ; } | ||
this.contentArray[ this.contentIndex ] = this.getContent() ; | ||
this.contentIndex ++ ; | ||
this.setContent( this.contentArray[ this.contentIndex ] ) ; | ||
this.textBuffer.runStateMachine() ; | ||
this.autoResizeAndDraw() ; | ||
break ; | ||
case 'backDelete' : | ||
this.textBuffer.backDelete() ; | ||
this.textBuffer.runStateMachine() ; | ||
if ( this.useAutoCompleteHint ) { this.runAutoCompleteHint( this.autoComplete ) ; } | ||
else { this.autoResizeAndDraw() ; } | ||
break ; | ||
// Can be derived (e.g. by InlineFileInput) | ||
InlineInput.prototype.submit = function() { | ||
if ( this.disabled || this.submitted || this.canceled ) { return ; } | ||
case 'delete' : | ||
this.textBuffer.delete() ; | ||
this.textBuffer.runStateMachine() ; | ||
if ( this.useAutoCompleteHint ) { this.runAutoCompleteHint( this.autoComplete ) ; } | ||
else { this.autoResizeAndDraw() ; } | ||
break ; | ||
var value = this.getValue() ; | ||
if ( this.noEmpty && ! value ) { return ; } | ||
case 'backward' : | ||
this.textBuffer.moveBackward() ; | ||
this.autoResizeAndDrawCursor() ; | ||
break ; | ||
//this.submitted = true ; | ||
this.emit( 'submit' , value , undefined , this ) ; | ||
} ; | ||
case 'forward' : | ||
this.textBuffer.moveForward() ; | ||
this.autoResizeAndDrawCursor() ; | ||
break ; | ||
case 'startOfWord' : | ||
this.textBuffer.moveToStartOfWord() ; | ||
this.autoResizeAndDrawCursor() ; | ||
break ; | ||
case 'endOfWord' : | ||
this.textBuffer.moveToEndOfWord() ; | ||
this.autoResizeAndDrawCursor() ; | ||
break ; | ||
InlineInput.prototype.onKey = function( key , trash , data ) { | ||
if ( this.autoCompleteMenu ) { | ||
// If the autoCompleteMenu is on, force a cancel | ||
this.autoCompleteMenu.emit( 'cancel' ) ; | ||
} | ||
case 'startOfLine' : | ||
this.textBuffer.moveToColumn( 0 ) ; | ||
this.autoResizeAndDrawCursor() ; | ||
break ; | ||
return Element.prototype.onKey.call( this , key , trash , data ) ; | ||
} ; | ||
case 'endOfLine' : | ||
this.textBuffer.moveToEndOfLine() ; | ||
this.autoResizeAndDrawCursor() ; | ||
break ; | ||
case 'left' : | ||
this.textBuffer.moveLeft() ; | ||
this.autoResizeAndDrawCursor() ; | ||
break ; | ||
case 'right' : | ||
this.textBuffer.moveRight() ; | ||
this.autoResizeAndDrawCursor() ; | ||
break ; | ||
const userActions = InlineInput.prototype.userActions ; | ||
case 'pasteClipboard' : | ||
if ( this.document ) { | ||
this.document.getClipboard().then( str => { | ||
if ( str ) { | ||
this.textBuffer.insert( str , this.textAttr ) ; | ||
this.textBuffer.runStateMachine() ; | ||
if ( this.useAutoCompleteHint ) { this.runAutoCompleteHint( this.autoComplete ) ; } | ||
else { this.autoResizeAndDraw() ; } | ||
} | ||
} ) | ||
.catch( () => undefined ) ; | ||
} | ||
break ; | ||
case 'copyClipboard' : | ||
if ( this.document ) { | ||
this.document.setClipboard( this.textBuffer.getSelectionText() ).catch( () => undefined ) ; | ||
} | ||
break ; | ||
default : | ||
return ; // Bubble up | ||
} | ||
userActions.character = function( key , trash , data ) { | ||
if ( this.placeholder ) { | ||
// Remove the placeholder on the first user input | ||
this.placeholder = null ; | ||
this.setAltContent( '' , false , true ) ; | ||
} | ||
return true ; // Do not bubble up | ||
this.insert( key ) ; | ||
} ; | ||
userActions.submit = function() { | ||
this.submit() ; | ||
} ; | ||
userActions.cancel = function() { | ||
if ( ! this.cancelable || this.disabled || this.canceled ) { return ; } | ||
//this.canceled = true ; | ||
this.emit( 'cancel' , this ) ; | ||
} ; | ||
/* | ||
InlineInput.prototype.onFocus = function( focus , type ) { | ||
this.hasFocus = focus ; | ||
this.updateStatus() ; | ||
this.draw() ; | ||
userActions.autoComplete = function() { | ||
if ( ! this.autoComplete ) { return ; } | ||
this.runAutoComplete( this.autoComplete ) ; | ||
} ; | ||
*/ | ||
userActions.historyAutoComplete = function() { | ||
if ( ! this.autoComplete ) { return ; } | ||
this.runAutoComplete( this.history ) ; | ||
} ; | ||
/* | ||
InlineInput.prototype.onClick = function( data ) { | ||
if ( ! this.hasFocus ) { | ||
this.document.giveFocusTo( this , 'select' ) ; | ||
} | ||
else { | ||
this.textBuffer.moveTo( data.x - this.scrollX , data.y - this.scrollY ) ; | ||
this.drawCursor() ; | ||
} | ||
userActions.historyPrevious = function() { | ||
if ( this.contentIndex <= 0 ) { return ; } | ||
this.contentArray[ this.contentIndex ] = this.getContent() ; | ||
this.contentIndex -- ; | ||
this.setContent( this.contentArray[ this.contentIndex ] ) ; | ||
this.textBuffer.runStateMachine() ; | ||
this.autoResizeAndDraw() ; | ||
} ; | ||
*/ | ||
/* | ||
InlineInput.prototype.onMiddleClick = function( data ) { | ||
if ( ! this.hasFocus ) { | ||
this.document.giveFocusTo( this , 'select' ) ; | ||
} | ||
userActions.historyNext = function() { | ||
if ( this.contentIndex >= this.contentArray.length - 1 ) { return ; } | ||
this.contentArray[ this.contentIndex ] = this.getContent() ; | ||
this.contentIndex ++ ; | ||
this.setContent( this.contentArray[ this.contentIndex ] ) ; | ||
this.textBuffer.runStateMachine() ; | ||
this.autoResizeAndDraw() ; | ||
} ; | ||
// Do not moveTo, it's quite boring | ||
//this.textBuffer.moveTo( data.x , data.y ) ; | ||
userActions.backDelete = function() { | ||
this.textBuffer.backDelete() ; | ||
this.textBuffer.runStateMachine() ; | ||
if ( this.document ) { | ||
this.document.getClipboard( 'primary' ).then( str => { | ||
if ( str ) { | ||
this.textBuffer.insert( str , this.textAttr ) ; | ||
this.textBuffer.runStateMachine() ; | ||
this.autoResizeAndDraw() ; | ||
} | ||
//else { this.drawCursor() ; } | ||
} ) | ||
.catch( () => undefined ) ; | ||
} | ||
//else { this.drawCursor() ; } | ||
if ( this.useAutoCompleteHint ) { this.runAutoCompleteHint( this.autoComplete ) ; } // async | ||
else { this.autoResizeAndDraw() ; } | ||
} ; | ||
*/ | ||
userActions.delete = function() { | ||
this.textBuffer.delete() ; | ||
this.textBuffer.runStateMachine() ; | ||
// There isn't much to do ATM | ||
//InlineInput.prototype.updateStatus = function() {} ; | ||
if ( this.useAutoCompleteHint ) { this.runAutoCompleteHint( this.autoComplete ) ; } // async | ||
else { this.autoResizeAndDraw() ; } | ||
} ; | ||
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -65,3 +65,3 @@ The MIT License (MIT) | ||
this.turnedOnBlurAttr = options.turnedOnBlurAttr || { bgColor: 'cyan' } ; | ||
this.turnedOnFocusAttr = options.turnedOnFocusAttr || { bgColor: 'brightCyan' , bold: true } ; | ||
this.turnedOnFocusAttr = options.turnedOnFocusAttr || { bgColor: 'brightCyan' , color: 'gray' , bold: true } ; | ||
this.turnedOffBlurAttr = options.turnedOffBlurAttr || { bgColor: 'gray' , dim: true } ; | ||
@@ -93,3 +93,2 @@ this.turnedOffFocusAttr = options.turnedOffFocusAttr || { bgColor: 'white' , color: 'black' , bold: true } ; | ||
this.onKey = this.onKey.bind( this ) ; | ||
this.onFocus = this.onFocus.bind( this ) ; | ||
@@ -111,7 +110,6 @@ this.onClick = this.onClick.bind( this ) ; | ||
module.exports = LabeledInput ; | ||
Element.inherit( LabeledInput ) ; | ||
LabeledInput.prototype = Object.create( Element.prototype ) ; | ||
LabeledInput.prototype.constructor = LabeledInput ; | ||
LabeledInput.prototype.elementType = 'LabeledInput' ; | ||
LabeledInput.prototype.needInput = true ; | ||
@@ -123,15 +121,2 @@ LabeledInput.prototype.noChildFocus = true ; | ||
LabeledInput.prototype.destroy = function( isSubDestroy , noDraw = false ) { | ||
if ( this.destroyed ) { return ; } | ||
this.off( 'key' , this.onKey ) ; | ||
this.off( 'focus' , this.onFocus ) ; | ||
this.off( 'click' , this.onClick ) ; | ||
if ( this.input ) { this.off( 'submit' , this.onInputSubmit ) ; } | ||
Element.prototype.destroy.call( this , isSubDestroy , noDraw ) ; | ||
} ; | ||
LabeledInput.prototype.keyBindings = { | ||
@@ -147,2 +132,3 @@ ENTER: 'submit' , | ||
LabeledInput.prototype.editableTextBoxKeyBindings = { | ||
CTRL_K: 'meta' , | ||
BACKSPACE: 'backDelete' , | ||
@@ -156,4 +142,17 @@ DELETE: 'delete' , | ||
END: 'endOfLine' , | ||
CTRL_O: 'copyClipboard' , | ||
CTRL_P: 'pasteClipboard' | ||
CTRL_B: 'startOfSelection' , | ||
CTRL_E: 'endOfSelection' , | ||
// T for Transfer | ||
CTRL_T: 'moveSelection' , | ||
ALT_T: 'copyToDocumentClipboard' , | ||
META_T: 'copyToSystemClipboard' , | ||
// P for Paste / Put | ||
CTRL_P: 'pasteSelection' , | ||
ALT_P: 'pasteDocumentClipboard' , | ||
META_P: 'pasteSystemClipboard' , | ||
// D for Delete | ||
CTRL_D: 'deleteSelection' , | ||
ALT_D: 'clearDocumentClipboard' , | ||
META_D: 'clearSystemClipboard' | ||
} ; | ||
@@ -170,4 +169,10 @@ | ||
PAGE_DOWN: 'scrollDown' , | ||
CTRL_O: 'copyClipboard' , | ||
CTRL_P: 'pasteClipboard' | ||
CTRL_B: 'startOfSelection' , | ||
CTRL_E: 'endOfSelection' , | ||
CTRL_K: 'meta' , | ||
// We copy vi/vim here, that use 'y' for copy (yank) and 'p' for paste (put) | ||
CTRL_Y: 'copy' , | ||
META_Y: 'copyClipboard' , | ||
CTRL_P: 'paste' , | ||
META_P: 'pasteClipboard' | ||
} ) ; | ||
@@ -348,12 +353,3 @@ | ||
switch( this.keyBindings[ key ] ) { | ||
case 'submit' : | ||
this.emit( 'submit' , this.getValue() , undefined , this ) ; | ||
break ; | ||
default : | ||
return ; | ||
} | ||
return true ; // Do not bubble up | ||
return Element.prototype.onKey.call( this , key , altKeys , data ) ; | ||
} ; | ||
@@ -370,4 +366,2 @@ | ||
LabeledInput.prototype.onFocus = function( focus , type ) { | ||
this.hasFocus = focus ; | ||
if ( type === 'delegate' ) { return ; } | ||
@@ -403,1 +397,9 @@ | ||
const userActions = LabeledInput.prototype.userActions ; | ||
userActions.submit = function() { | ||
this.emit( 'submit' , this.getValue() , undefined , this ) ; | ||
} ; | ||
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -69,19 +69,6 @@ The MIT License (MIT) | ||
module.exports = Layout ; | ||
Element.inherit( Layout ) ; | ||
Layout.prototype = Object.create( Element.prototype ) ; | ||
Layout.prototype.constructor = Layout ; | ||
Layout.prototype.elementType = 'Layout' ; | ||
Layout.prototype.destroy = function( isSubDestroy , noDraw = false ) { | ||
if ( this.destroyed ) { return ; } | ||
this.off( 'parentResize' , this.onParentResize ) ; | ||
Element.prototype.destroy.call( this , isSubDestroy , noDraw ) ; | ||
} ; | ||
Layout.prototype.computeBoundingBoxes = function() { | ||
@@ -88,0 +75,0 @@ var computed = this.computed = {} ; |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -59,2 +59,3 @@ The MIT License (MIT) | ||
this.justify = !! options.justify ; | ||
this.leftMargin = this.leftMargin ?? 0 ; // useful for InlineMenu: it's the place where the prompt is put | ||
@@ -73,9 +74,6 @@ this.separator = options.separator || options.buttonSeparator || ' ' ; | ||
module.exports = RowMenu ; | ||
Element.inherit( RowMenu , BaseMenu ) ; | ||
RowMenu.prototype = Object.create( BaseMenu.prototype ) ; | ||
RowMenu.prototype.constructor = RowMenu ; | ||
RowMenu.prototype.elementType = 'RowMenu' ; | ||
RowMenu.prototype.inlineNewLine = true ; | ||
@@ -95,13 +93,2 @@ RowMenu.prototype.ButtonClass = Button ; | ||
RowMenu.prototype.destroy = function( isSubDestroy , noDraw = false ) { | ||
if ( this.destroyed ) { return ; } | ||
this.off( 'key' , this.onKey ) ; | ||
this.off( 'focus' , this.onFocus ) ; | ||
Element.prototype.destroy.call( this , isSubDestroy , noDraw ) ; | ||
} ; | ||
RowMenu.prototype.keyBindings = { | ||
@@ -160,3 +147,3 @@ LEFT: 'previous' , | ||
if ( this.buttonPaddingWidth > this.outputWidth ) { | ||
if ( this.buttonPaddingWidth > this.outputWidth - this.leftMargin ) { | ||
// The padding itself is bigger than the width... so what should we do? | ||
@@ -195,5 +182,5 @@ return ; | ||
+ ( isLastItem ? 0 : this.nextPageDef.width + this.separatorWidth ) | ||
- this.outputWidth ; | ||
- this.outputWidth - this.leftMargin ; | ||
//console.error( "overflow",overflow,pageWidth,def.width,isLastItem,this.nextPageDef.width,this.separatorWidth,this.outputWidth); | ||
//console.error( "overflow",overflow,pageWidth,def.width,isLastItem,this.nextPageDef.width,this.separatorWidth,this.outputWidth,this.leftMargin); | ||
if ( overflow > 0 ) { | ||
@@ -207,3 +194,3 @@ if ( pageItemCount ) { | ||
+ ( isLastItem ? 0 : this.nextPageDef.width + this.separatorWidth ) | ||
- this.outputWidth ; | ||
- this.outputWidth - this.leftMargin ; | ||
} | ||
@@ -236,3 +223,3 @@ | ||
pageDef.justifyWidth = Math.max( 0 , | ||
this.justify ? ( this.outputWidth - pageDef.buttonsAndSeparatorsWidth ) / ( pageDef.length - 1 ) | ||
this.justify ? ( this.outputWidth - this.leftMargin - pageDef.buttonsAndSeparatorsWidth ) / ( pageDef.length - 1 ) | ||
: 0 | ||
@@ -252,3 +239,3 @@ ) ; | ||
justifyWidthError = 0 , | ||
buttonOffsetX = 0 , | ||
buttonOffsetX = this.leftMargin , | ||
buttonOffsetY = 0 ; | ||
@@ -260,2 +247,3 @@ | ||
this.buttons.length = 0 ; | ||
this.hotkeyToButtonIndex.clear() ; | ||
@@ -300,6 +288,6 @@ //console.error( "pageDef.justifyWidth" , pageDef.justifyWidth ) ; | ||
submittedAttr: def.submittedAttr || this.buttonSubmittedAttr , | ||
turnedOnFocusAttr: def.turnedOnFocusAttr || this.turnedOnFocusAttr , | ||
turnedOffFocusAttr: def.turnedOffFocusAttr || this.turnedOffFocusAttr , | ||
turnedOnBlurAttr: def.turnedOnBlurAttr || this.turnedOnBlurAttr , | ||
turnedOffBlurAttr: def.turnedOffBlurAttr || this.turnedOffBlurAttr , | ||
turnedOnFocusAttr: def.turnedOnFocusAttr || this.buttonTurnedOnFocusAttr , | ||
turnedOffFocusAttr: def.turnedOffFocusAttr || this.buttonTurnedOffFocusAttr , | ||
turnedOnBlurAttr: def.turnedOnBlurAttr || this.buttonTurnedOnBlurAttr , | ||
turnedOffBlurAttr: def.turnedOffBlurAttr || this.buttonTurnedOffBlurAttr , | ||
@@ -337,2 +325,11 @@ blurLeftPadding: this.blurLeftPadding , | ||
if ( def.hotkey ) { | ||
if ( Array.isArray( def.hotkey ) ) { | ||
def.hotkey.forEach( hotkey => this.hotkeyToButtonIndex.set( hotkey , index ) ) ; | ||
} | ||
else { | ||
this.hotkeyToButtonIndex.set( def.hotkey , index ) ; | ||
} | ||
} | ||
if ( isToggle ) { | ||
@@ -350,3 +347,3 @@ this.buttons[ index ].on( 'toggle' , this.onButtonToggle ) ; | ||
// Set outputWidth to the correct value | ||
//if ( buttonOffsetX < this.outputWidth ) { this.needRedraw = true ; } | ||
//if ( buttonOffsetX < this.outputWidth ) { this.needOuterDraw = true ; } | ||
//this.pageWidth = buttonOffsetX ; | ||
@@ -360,3 +357,3 @@ //this.outputWidth = buttonOffsetY ; | ||
//console.error( string.format( "Call preDrawSelf(), page %i" , this.page )); | ||
this.outputDst.put( { x: this.outputX , y: this.outputY , attr: this.backgroundAttr } , ' '.repeat( this.outputWidth ) ) ; | ||
this.outputDst.put( { x: this.outputX + this.leftMargin , y: this.outputY , attr: this.backgroundAttr } , ' '.repeat( this.outputWidth - this.leftMargin ) ) ; | ||
@@ -363,0 +360,0 @@ if ( this.separator ) { |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -87,9 +87,6 @@ The MIT License (MIT) | ||
module.exports = SelectList ; | ||
Element.inherit( SelectList , ColumnMenu ) ; | ||
SelectList.prototype = Object.create( ColumnMenu.prototype ) ; | ||
SelectList.prototype.constructor = SelectList ; | ||
SelectList.prototype.elementType = 'SelectList' ; | ||
SelectList.prototype.defaultOptions = { | ||
@@ -103,3 +100,3 @@ buttonBlurAttr: { bgColor: 'gray' , color: 'white' , bold: true } , | ||
turnedOnBlurAttr: { bgColor: 'cyan' } , | ||
turnedOnFocusAttr: { bgColor: 'brightCyan' , bold: true } , | ||
turnedOnFocusAttr: { bgColor: 'brightCyan' , color: 'gray' , bold: true } , | ||
turnedOffBlurAttr: { bgColor: 'gray' , dim: true } , | ||
@@ -122,12 +119,2 @@ turnedOffFocusAttr: { bgColor: 'white' , color: 'black' , bold: true } , | ||
SelectList.prototype.destroy = function( isSubDestroy , noDraw = false ) { | ||
if ( this.destroyed ) { return ; } | ||
this.off( 'clickOut' , this.onClickOut ) ; | ||
ColumnMenu.prototype.destroy.call( this , isSubDestroy , noDraw ) ; | ||
} ; | ||
SelectList.prototype.toggle = function( showMenu = null , noDraw = false ) { | ||
@@ -164,3 +151,3 @@ var i , iMax ; | ||
this.redraw() ; | ||
this.outerDraw() ; | ||
} ; | ||
@@ -225,3 +212,3 @@ | ||
this.select( button ) ; | ||
this.emit( 'submit' , buttonValue , action , this ) ; | ||
this.emit( 'submit' , buttonValue , action , this , button ) ; | ||
} | ||
@@ -228,0 +215,0 @@ } ; |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -80,9 +80,6 @@ The MIT License (MIT) | ||
module.exports = SelectListMulti ; | ||
Element.inherit( SelectListMulti , ColumnMenuMulti ) ; | ||
SelectListMulti.prototype = Object.create( ColumnMenuMulti.prototype ) ; | ||
SelectListMulti.prototype.constructor = SelectListMulti ; | ||
SelectListMulti.prototype.elementType = 'SelectListMulti' ; | ||
SelectListMulti.prototype.defaultOptions = { | ||
@@ -96,3 +93,3 @@ buttonBlurAttr: { bgColor: 'gray' , color: 'white' , bold: true } , | ||
turnedOnBlurAttr: { bgColor: 'cyan' } , | ||
turnedOnFocusAttr: { bgColor: 'brightCyan' , bold: true } , | ||
turnedOnFocusAttr: { bgColor: 'brightCyan' , color: 'gray' , bold: true } , | ||
turnedOffBlurAttr: { bgColor: 'gray' , dim: true } , | ||
@@ -115,12 +112,2 @@ turnedOffFocusAttr: { bgColor: 'white' , color: 'black' , bold: true } , | ||
SelectListMulti.prototype.destroy = function( isSubDestroy , noDraw = false ) { | ||
if ( this.destroyed ) { return ; } | ||
this.off( 'clickOut' , this.onClickOut ) ; | ||
ColumnMenuMulti.prototype.destroy.call( this , isSubDestroy , noDraw ) ; | ||
} ; | ||
SelectListMulti.prototype.toggle = function( showMenu = null , noDraw = false ) { | ||
@@ -147,3 +134,3 @@ var i , iMax ; | ||
this.redraw() ; | ||
this.outerDraw() ; | ||
} ; | ||
@@ -166,7 +153,7 @@ | ||
if ( ! this.showMenu ) { | ||
this.emit( 'submit' , this.value , action , this ) ; | ||
this.emit( 'submit' , this.value , action , this , button ) ; | ||
} | ||
break ; | ||
default : | ||
this.emit( 'submit' , this.value , action , this ) ; | ||
this.emit( 'submit' , this.value , action , this , button ) ; | ||
} | ||
@@ -173,0 +160,0 @@ } ; |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -84,8 +84,8 @@ The MIT License (MIT) | ||
module.exports = Slider ; | ||
Element.inherit( Slider ) ; | ||
Slider.prototype = Object.create( Element.prototype ) ; | ||
Slider.prototype.constructor = Slider ; | ||
Slider.prototype.elementType = 'Slider' ; | ||
Slider.prototype.needInput = true ; | ||
Slider.prototype.outerDrag = true ; | ||
@@ -116,14 +116,2 @@ | ||
Slider.prototype.destroy = function( isSubDestroy , noDraw = false ) { | ||
if ( this.destroyed ) { return ; } | ||
this.off( 'click' , this.onClick ) ; | ||
this.off( 'drag' , this.onDrag ) ; | ||
this.off( 'wheel' , this.onWheel ) ; | ||
Element.prototype.destroy.call( this , isSubDestroy , noDraw ) ; | ||
} ; | ||
// Create Buttons automatically | ||
@@ -295,2 +283,14 @@ Slider.prototype.initChildren = function() { | ||
Slider.prototype.getValue = function() { | ||
return this.rateToValue( this.slideRate ) ; | ||
} ; | ||
Slider.prototype.setValue = function( value , internalAndNoDraw ) { | ||
return this.setSlideRate( this.valueToRate( value ) , internalAndNoDraw ) ; | ||
} ; | ||
Slider.prototype.getHandleOffset = function() { return this.handleOffset ; } ; | ||
@@ -314,14 +314,2 @@ Slider.prototype.getSlideRate = function() { return this.slideRate ; } ; | ||
Slider.prototype.getValue = function() { | ||
return this.rateToValue( this.slideRate ) ; | ||
} ; | ||
Slider.prototype.setValue = function( value , internalAndNoDraw ) { | ||
return this.setSlideRate( this.valueToRate( value ) , internalAndNoDraw ) ; | ||
} ; | ||
Slider.prototype.onClick = function( data ) { | ||
@@ -328,0 +316,0 @@ if ( ! this.hasFocus ) { this.document.giveFocusTo( this , 'select' ) ; } |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -48,6 +48,7 @@ The MIT License (MIT) | ||
// Usually done by the Element's constructor, but it's required now | ||
this.content = options.content ; | ||
this.contentHasMarkup = !! options.contentHasMarkup ; | ||
// Also check that sub-class hasn't defined it yet... | ||
this.content = this.content ?? options.content ; | ||
this.contentHasMarkup = this.contentHasMarkup ?? options.contentHasMarkup ; | ||
// For width and height, we centralize here works for sub-class having animations | ||
// For width and height, we centralize here the work for sub-class having animations | ||
if ( ! options.width ) { | ||
@@ -65,7 +66,6 @@ options.width = this.computeRequiredWidth() ; | ||
module.exports = Text ; | ||
Element.inherit( Text ) ; | ||
Text.prototype = Object.create( Element.prototype ) ; | ||
Text.prototype.constructor = Text ; | ||
Text.prototype.elementType = 'Text' ; | ||
Text.prototype.forceContentArray = true ; | ||
@@ -99,2 +99,3 @@ | ||
Text.prototype.resizeOnContent = function() { | ||
// /!\ .width and .height are unused ATM | ||
this.width = this.computeRequiredWidth( this.content , this.contentHasMarkup ) ; | ||
@@ -101,0 +102,0 @@ this.height = this.computeRequiredHeight( this.content , this.contentHasMarkup ) ; |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -49,3 +49,2 @@ The MIT License (MIT) | ||
this.onKey = this.onKey.bind( this ) ; | ||
this.onClick = this.onClick.bind( this ) ; | ||
@@ -72,5 +71,10 @@ this.onDrag = this.onDrag.bind( this ) ; | ||
this.autoScrollContextLines = options.autoScrollContextLines ?? 0 ; | ||
this.autoScrollContextColumns = options.autoScrollContextColumns ?? 1 ; | ||
// Right shift of the first-line, may be useful for prompt, or continuing another box in the flow | ||
this.firstLineRightShift = options.firstLineRightShift || 0 ; | ||
this.tabWidth = options.tabWidth || 4 ; // How many cells (=spaces) for the tab character | ||
this.wordWrap = !! ( options.wordWrap || options.wordwrap ) ; | ||
@@ -108,9 +112,6 @@ this.lineWrap = !! ( options.lineWrap || this.wordWrap ) ; | ||
module.exports = TextBox ; | ||
Element.inherit( TextBox ) ; | ||
TextBox.prototype = Object.create( Element.prototype ) ; | ||
TextBox.prototype.constructor = TextBox ; | ||
TextBox.prototype.elementType = 'TextBox' ; | ||
// Support for strictInline mode | ||
@@ -121,17 +122,4 @@ TextBox.prototype.strictInlineSupport = true ; | ||
TextBox.prototype.destroy = function( isSubDestroy , noDraw = false ) { | ||
if ( this.destroyed ) { return ; } | ||
this.off( 'key' , this.onKey ) ; | ||
this.off( 'click' , this.onClick ) ; | ||
this.off( 'drag' , this.onDrag ) ; | ||
this.off( 'wheel' , this.onWheel ) ; | ||
this.off( 'parentResize' , this.onParentResize ) ; | ||
Element.prototype.destroy.call( this , isSubDestroy , noDraw ) ; | ||
} ; | ||
TextBox.prototype.keyBindings = { | ||
CTRL_K: 'meta' , | ||
UP: 'tinyScrollUp' , | ||
@@ -146,3 +134,6 @@ DOWN: 'tinyScrollDown' , | ||
RIGHT: 'scrollRight' , | ||
CTRL_O: 'copyClipboard' | ||
// T for Transfer | ||
ALT_T: 'copyToDocumentClipboard' , | ||
META_T: 'copyToSystemClipboard' | ||
} ; | ||
@@ -161,2 +152,3 @@ | ||
firstLineRightShift: this.firstLineRightShift , | ||
tabWidth: this.tabWidth , | ||
lineWrapWidth: this.lineWrap ? this.textAreaWidth : null , | ||
@@ -175,4 +167,3 @@ wordWrap: this.wordWrap , | ||
this.textBuffer.setDefaultAttr( this.textAttr ) ; | ||
this.textBuffer.setVoidAttr( this.voidAttr ) ; | ||
this.setAttr( undefined , undefined , true ) ; | ||
@@ -183,2 +174,3 @@ | ||
firstLineRightShift: this.firstLineRightShift , | ||
tabWidth: this.tabWidth , | ||
lineWrapWidth: this.lineWrap ? this.textAreaWidth : null , | ||
@@ -195,4 +187,3 @@ wordWrap: this.wordWrap , | ||
this.altTextBuffer.setDefaultAttr( this.altTextAttr ) ; | ||
this.altTextBuffer.setVoidAttr( this.voidAttr ) ; | ||
this.setAltAttr() ; | ||
this.textBuffer.setVoidTextBuffer( this.altTextBuffer ) ; | ||
@@ -305,3 +296,3 @@ } | ||
// It's best to force the dst now, because it avoids to set textBuffer.dst everytime it changes, | ||
// and it could be changed by userland (so hard to keep it in sync without setters/getters) | ||
// and it could be changed by userland (so it's hard to keep it in sync without setters/getters) | ||
this.textBuffer.draw( { dst: this.outputDst } ) ; | ||
@@ -312,21 +303,2 @@ } ; | ||
TextBox.prototype.scroll = function( dx , dy , dontDraw = false ) { | ||
return this.scrollTo( dx ? this.scrollX + dx : null , dy ? this.scrollY + dy : null , dontDraw ) ; | ||
} ; | ||
TextBox.prototype.scrollToTop = function( dontDraw = false ) { | ||
return this.scrollTo( null , 0 , dontDraw ) ; | ||
} ; | ||
TextBox.prototype.scrollToBottom = function( dontDraw = false ) { | ||
// Ignore extra scrolling here | ||
return this.scrollTo( null , this.textAreaHeight - this.textBuffer.buffer.length , dontDraw ) ; | ||
} ; | ||
TextBox.prototype.scrollTo = function( x , y , noDraw = false ) { | ||
@@ -363,21 +335,39 @@ if ( ! this.scrollable ) { return ; } | ||
TextBox.prototype.scroll = function( dx , dy , dontDraw = false ) { | ||
return this.scrollTo( dx ? this.scrollX + dx : null , dy ? this.scrollY + dy : null , dontDraw ) ; | ||
} ; | ||
TextBox.prototype.scrollToTop = function( dontDraw = false ) { | ||
return this.scrollTo( null , 0 , dontDraw ) ; | ||
} ; | ||
TextBox.prototype.autoScrollAndDraw = function( onlyDrawCursor = false ) { | ||
var x , y ; | ||
TextBox.prototype.scrollToBottom = function( dontDraw = false ) { | ||
// Ignore extra scrolling here | ||
return this.scrollTo( null , this.textAreaHeight - this.textBuffer.buffer.length , dontDraw ) ; | ||
} ; | ||
// We use cx-1 because at least we want to see the char just before the cursor (backspace, etc...) | ||
// But do nothing if there is no scrolling yet (do not set x to 0 if it's unnecessary) | ||
if ( this.textBuffer.cx - 1 < -this.scrollX && this.scrollX !== 0 ) { | ||
x = -Math.max( 0 , this.textBuffer.cx - 1 ) ; | ||
TextBox.prototype.autoScrollAndDraw = function( onlyDrawCursorExceptIfScrolled = false , noDraw = false ) { | ||
var x , y , | ||
contextColumns = Math.min( Math.floor( this.textAreaWidth / 2 ) , this.autoScrollContextColumns ) , | ||
contextLines = Math.min( Math.floor( this.textAreaHeight / 2 ) , this.autoScrollContextLines ) ; | ||
// Do nothing if there is no scrolling yet (do not set x to 0 if it's unnecessary) | ||
if ( this.textBuffer.cx < -this.scrollX + contextColumns && this.scrollX !== 0 ) { | ||
// The cursor will be on left of the viewport | ||
x = Math.min( 0 , -this.textBuffer.cx + contextColumns ) ; | ||
} | ||
else if ( this.textBuffer.cx > this.textAreaWidth - this.scrollX - 1 ) { | ||
x = this.textAreaWidth - 1 - this.textBuffer.cx ; | ||
else if ( this.textBuffer.cx > this.textAreaWidth - this.scrollX - 1 - contextColumns ) { | ||
// The cursor will be on right of the viewport | ||
x = this.textAreaWidth - 1 - this.textBuffer.cx - contextColumns ; | ||
} | ||
if ( this.textBuffer.cy < -this.scrollY ) { | ||
y = -this.textBuffer.cy ; | ||
if ( this.textBuffer.cy < -this.scrollY + contextLines ) { | ||
// The cursor will be on top of the viewport | ||
y = Math.min( 0 , -this.textBuffer.cy + contextLines ) ; | ||
} | ||
else if ( this.textBuffer.cy > this.textAreaHeight - this.scrollY - 1 ) { | ||
y = this.textAreaHeight - 1 - this.textBuffer.cy ; | ||
else if ( this.textBuffer.cy > this.textAreaHeight - this.scrollY - 1 - contextLines ) { | ||
// The cursor will be at the bottom of the viewport | ||
y = this.textAreaHeight - 1 - this.textBuffer.cy - contextLines ; | ||
} | ||
@@ -387,5 +377,5 @@ | ||
// .scrollTo() call .draw(), so no need to do that here... | ||
this.scrollTo( x , y ) ; | ||
this.scrollTo( x , y , noDraw ) ; | ||
} | ||
else if ( ! onlyDrawCursor ) { | ||
else if ( ! onlyDrawCursorExceptIfScrolled ) { | ||
this.draw() ; | ||
@@ -398,6 +388,13 @@ } | ||
TextBox.prototype.autoScrollAndSmartDraw = function() { return this.autoScrollAndDraw( true ) ; } ; | ||
TextBox.prototype.autoScrollAndDrawCursor = function() { | ||
return this.autoScrollAndDraw( true ) ; | ||
TextBox.prototype.setAttr = function( textAttr = this.textAttr , voidAttr = this.voidAttr , dontDraw = false , dontSetContent = false ) { | ||
this.textAttr = textAttr ; | ||
this.voidAttr = voidAttr ; | ||
this.textBuffer.setDefaultAttr( this.textAttr ) ; | ||
this.textBuffer.setVoidAttr( this.voidAttr ) ; | ||
if ( ! dontSetContent ) { this.setContent( this.content , this.contentHasMarkup , dontDraw ) ; } | ||
} ; | ||
@@ -407,4 +404,6 @@ | ||
TextBox.prototype.getContentSize = function() { | ||
return this.textBuffer.getContentSize() ; | ||
TextBox.prototype.setAltAttr = function( altTextAttr = this.altTextAttr ) { | ||
this.altTextAttr = altTextAttr ; | ||
this.altTextBuffer.setDefaultAttr( this.altTextAttr ) ; | ||
this.altTextBuffer.setVoidAttr( this.voidAttr ) ; | ||
} ; | ||
@@ -414,5 +413,4 @@ | ||
TextBox.prototype.getContent = function() { | ||
return this.textBuffer.getText() ; | ||
} ; | ||
TextBox.prototype.getContentSize = function() { return this.textBuffer.getContentSize() ; } ; | ||
TextBox.prototype.getContent = function() { return this.textBuffer.getText() ; } ; | ||
@@ -443,3 +441,3 @@ | ||
this.drawCursor() ; | ||
this.redraw() ; | ||
this.outerDraw() ; | ||
} | ||
@@ -478,3 +476,3 @@ } ; | ||
this.drawCursor() ; | ||
this.redraw() ; | ||
this.outerDraw() ; | ||
} | ||
@@ -505,6 +503,8 @@ } ; | ||
case 'appendLog' : | ||
// Like 'append' but add a newLine if the last line is not empty, and also check if we need to scroll | ||
scroll = this.textBuffer.buffer.length <= this.textAreaHeight || this.scrollY <= this.textAreaHeight - this.textBuffer.buffer.length ; | ||
content = '\n' + content ; | ||
this.textBuffer.moveToEndOfBuffer() ; | ||
if ( this.textBuffer.cx ) { content = '\n' + content ; } | ||
this.content += content ; | ||
this.textBuffer.append( content , this.contentHasMarkup , this.textAttr ) ; | ||
this.textBuffer.insert( content , this.contentHasMarkup , this.textAttr ) ; | ||
break ; | ||
@@ -536,3 +536,3 @@ case 'append' : | ||
this.draw() ; | ||
//this.redraw() ; | ||
//this.outerDraw() ; | ||
} | ||
@@ -543,47 +543,11 @@ } ; | ||
TextBox.prototype.onKey = function( key , trash , data ) { | ||
switch( this.keyBindings[ key ] ) { | ||
case 'tinyScrollUp' : | ||
this.scroll( 0 , Math.ceil( this.textAreaHeight / 5 ) ) ; | ||
break ; | ||
// TODOC | ||
TextBox.prototype.setTabWidth = function( tabWidth , internal = false ) { | ||
this.tabWidth = + tabWidth || 4 ; | ||
this.textBuffer.setTabWidth( this.tabWidth ) ; | ||
if ( this.altTextBuffer ) { this.altTextBuffer.setTabWidth( this.tabWidth ) ; } | ||
case 'tinyScrollDown' : | ||
this.scroll( 0 , -Math.ceil( this.textAreaHeight / 5 ) ) ; | ||
break ; | ||
case 'scrollUp' : | ||
this.scroll( 0 , Math.ceil( this.textAreaHeight / 2 ) ) ; | ||
break ; | ||
case 'scrollDown' : | ||
this.scroll( 0 , -Math.ceil( this.textAreaHeight / 2 ) ) ; | ||
break ; | ||
case 'scrollLeft' : | ||
this.scroll( Math.ceil( this.textAreaWidth / 2 ) , 0 ) ; | ||
break ; | ||
case 'scrollRight' : | ||
this.scroll( -Math.ceil( this.textAreaWidth / 2 ) , 0 ) ; | ||
break ; | ||
case 'scrollTop' : | ||
this.scrollToTop() ; | ||
break ; | ||
case 'scrollBottom' : | ||
this.scrollToBottom() ; | ||
break ; | ||
case 'copyClipboard' : | ||
if ( this.document ) { | ||
this.document.setClipboard( this.textBuffer.getSelectionText() ).catch( () => undefined ) ; | ||
} | ||
break ; | ||
default : | ||
return ; // Bubble up | ||
if ( ! internal ) { | ||
this.draw() ; | ||
} | ||
return true ; // Do not bubble up | ||
} ; | ||
@@ -593,6 +557,10 @@ | ||
TextBox.prototype.onClick = function( data ) { | ||
// It is susceptible to click event only when it is scrollable | ||
if ( this.scrollable && ! this.hasFocus ) { | ||
this.document.giveFocusTo( this , 'select' ) ; | ||
// TODOC | ||
TextBox.prototype.setStateMachine = function( stateMachine , internal = false ) { | ||
this.stateMachine = stateMachine ; | ||
this.textBuffer.stateMachine = this.stateMachine ; | ||
if ( this.stateMachine && ! internal ) { | ||
this.textBuffer.runStateMachine() ; | ||
this.draw() ; | ||
} | ||
@@ -616,3 +584,21 @@ } ; | ||
TextBox.prototype.onClick = function( data ) { | ||
if ( this.hasFocus ) { | ||
if ( this.textBuffer.selectionRegion ) { | ||
this.textBuffer.resetSelectionRegion() ; | ||
this.draw() ; | ||
} | ||
} | ||
else { | ||
if ( this.scrollable ) { | ||
// It is susceptible to click event only when it is scrollable | ||
this.document.giveFocusTo( this , 'select' ) ; | ||
} | ||
} | ||
} ; | ||
TextBox.prototype.onDrag = function( data ) { | ||
//console.error( "TB Drag:" , data ) ; | ||
var xmin , ymin , xmax , ymax ; | ||
@@ -645,3 +631,3 @@ | ||
if ( this.document ) { | ||
this.document.setClipboard( this.textBuffer.getSelectionText() , 'primary' ).catch( () => undefined ) ; | ||
this.document.setSystemClipboard( this.textBuffer.getSelectionText() , 'primary' ).catch( () => undefined ) ; | ||
} | ||
@@ -671,1 +657,57 @@ | ||
const userActions = TextBox.prototype.userActions ; | ||
userActions.tinyScrollUp = function() { | ||
this.scroll( 0 , Math.ceil( this.textAreaHeight / 5 ) ) ; | ||
this.emit( 'scroll' ) ; | ||
} ; | ||
userActions.tinyScrollDown = function() { | ||
this.scroll( 0 , -Math.ceil( this.textAreaHeight / 5 ) ) ; | ||
this.emit( 'scroll' ) ; | ||
} ; | ||
userActions.scrollUp = function() { | ||
this.scroll( 0 , Math.ceil( this.textAreaHeight / 2 ) ) ; | ||
this.emit( 'scroll' ) ; | ||
} ; | ||
userActions.scrollDown = function() { | ||
this.scroll( 0 , -Math.ceil( this.textAreaHeight / 2 ) ) ; | ||
this.emit( 'scroll' ) ; | ||
} ; | ||
userActions.scrollLeft = function() { | ||
this.scroll( Math.ceil( this.textAreaWidth / 2 ) , 0 ) ; | ||
this.emit( 'scroll' ) ; | ||
} ; | ||
userActions.scrollRight = function() { | ||
this.scroll( -Math.ceil( this.textAreaWidth / 2 ) , 0 ) ; | ||
this.emit( 'scroll' ) ; | ||
} ; | ||
userActions.scrollTop = function() { | ||
this.scrollToTop() ; | ||
this.emit( 'scroll' ) ; | ||
} ; | ||
userActions.scrollBottom = function() { | ||
this.scrollToBottom() ; | ||
this.emit( 'scroll' ) ; | ||
} ; | ||
userActions.copyToDocumentClipboard = function() { | ||
if ( this.document ) { | ||
this.document.setDocumentClipboard( this.textBuffer.getSelectionText() ) ; | ||
} | ||
} ; | ||
userActions.copyToSystemClipboard = function() { | ||
if ( this.document ) { | ||
this.document.setSystemClipboard( this.textBuffer.getSelectionText() ).catch( () => undefined ) ; | ||
} | ||
} ; | ||
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -45,2 +45,7 @@ The MIT License (MIT) | ||
this.cellContents = options.cellContents ; // Should be an array of array of text | ||
// This replace .contentWidth/.contentHeight for cell-only size (without shrinking/expanding/fitting) | ||
this.rawContentWidth = 0 ; | ||
this.rawContentHeight = 0 ; | ||
this.contentHasMarkup = options.contentHasMarkup ; | ||
@@ -79,2 +84,12 @@ | ||
/* | ||
// Select attr | ||
// /!\ NOT IMPLEMENTED YET /!\ | ||
// Would allow one to navigate the table (it could be useful for making editable cells) | ||
this.selectedTextAttr = options.selectedTextAttr || null ; | ||
this.selectedVoidAttr = options.selectedVoidAttr || null ; | ||
this.selectable = options.selectable || null ; // Can be 'row', 'column' or 'cell' | ||
this.selectedX = this.selectedY = 0 ; | ||
*/ | ||
this.expandToWidth = options.expandToWidth !== undefined ? !! options.expandToWidth : !! options.fit ; | ||
@@ -99,3 +114,3 @@ this.shrinkToWidth = options.shrinkToWidth !== undefined ? !! options.shrinkToWidth : !! options.fit ; | ||
if ( typeof options.borderChars === 'object' ) { | ||
this.borderChars = options.borderChars ; | ||
this.borderChars = boxesChars.__fix__( options.borderChars ) ; | ||
} | ||
@@ -111,5 +126,2 @@ else if ( typeof options.borderChars === 'string' && boxesChars[ options.borderChars ] ) { | ||
if ( ! options.width ) { this.outputWidth = this.contentWidth ; } | ||
if ( ! options.height ) { this.outputHeight = this.contentHeight ; } | ||
// Only draw if we are not a superclass of the object | ||
@@ -120,12 +132,10 @@ if ( this.elementType === 'TextTable' && ! options.noDraw ) { this.draw() ; } | ||
module.exports = TextTable ; | ||
Element.inherit( TextTable ) ; | ||
TextTable.prototype = Object.create( Element.prototype ) ; | ||
TextTable.prototype.constructor = TextTable ; | ||
TextTable.prototype.elementType = 'TextTable' ; | ||
// Support for strictInline mode | ||
TextTable.prototype.strictInlineSupport = true ; | ||
TextTable.prototype.staticInline = true ; | ||
TextTable.prototype.inlineResizeToContent = true ; | ||
@@ -144,7 +154,42 @@ | ||
// For instance, .cellContents is rather useless, but we still update it | ||
// Save cell content | ||
this.cellContents[ y ][ x ] = content ; | ||
textBox.setContent( content , this.contentHasMarkup , true ) ; | ||
if ( ! dontUpdateLayout ) { this.computeCells() ; } | ||
if ( ! dontUpdateLayout ) { | ||
this.computeCells() ; | ||
if ( ! dontDraw ) { this.draw() ; } | ||
} | ||
else { | ||
if ( ! dontDraw ) { textBox.draw() ; } | ||
} | ||
} ; | ||
TextTable.prototype.setCellAttr = function( x , y , textAttr , voidAttr , dontDraw = false ) { | ||
var textBox = this.textBoxes[ y ] && this.textBoxes[ y ][ x ] ; | ||
if ( ! textBox ) { return ; } | ||
if ( voidAttr === undefined ) { voidAttr = textAttr ; } | ||
textBox.setAttr( textAttr , voidAttr , dontDraw ) ; | ||
} ; | ||
TextTable.prototype.resetCellAttr = function( x , y , dontDraw = false ) { | ||
var textBox = this.textBoxes[ y ] && this.textBoxes[ y ][ x ] ; | ||
if ( ! textBox ) { return ; } | ||
var textAttr = this.getTextAttrForCell( x , y ) , | ||
voidAttr = this.getVoidAttrForCell( x , y , textAttr ) ; | ||
textBox.setAttr( textAttr , voidAttr , dontDraw ) ; | ||
} ; | ||
TextTable.prototype.setRowAttr = function( y , textAttr , voidAttr , dontDraw = false ) { | ||
for ( let x = 0 ; x < this.columnCount ; x ++ ) { this.setCellAttr( x , y , textAttr , voidAttr , true ) ; } | ||
if ( ! dontDraw ) { this.draw() ; } | ||
@@ -155,2 +200,69 @@ } ; | ||
TextTable.prototype.resetRowAttr = function( y , dontDraw = false ) { | ||
for ( let x = 0 ; x < this.columnCount ; x ++ ) { this.resetCellAttr( x , y , true ) ; } | ||
if ( ! dontDraw ) { this.draw() ; } | ||
} ; | ||
TextTable.prototype.setColumnAttr = function( x , textAttr , voidAttr , dontDraw = false ) { | ||
for ( let y = 0 ; y < this.rowCount ; y ++ ) { this.setCellAttr( x , y , textAttr , voidAttr , true ) ; } | ||
if ( ! dontDraw ) { this.draw() ; } | ||
} ; | ||
TextTable.prototype.resetColumnAttr = function( x , dontDraw = false ) { | ||
for ( let y = 0 ; y < this.rowCount ; y ++ ) { this.resetCellAttr( x , y , true ) ; } | ||
if ( ! dontDraw ) { this.draw() ; } | ||
} ; | ||
TextTable.prototype.setTableAttr = function( textAttr , voidAttr , dontDraw = false ) { | ||
for ( let y = 0 ; y < this.rowCount ; y ++ ) { | ||
for ( let x = 0 ; x < this.columnCount ; x ++ ) { this.setCellAttr( x , y , textAttr , voidAttr , true ) ; } | ||
} | ||
if ( ! dontDraw ) { this.draw() ; } | ||
} ; | ||
TextTable.prototype.resetTableAttr = function( dontDraw = false ) { | ||
for ( let y = 0 ; y < this.rowCount ; y ++ ) { | ||
for ( let x = 0 ; x < this.columnCount ; x ++ ) { this.resetCellAttr( x , y , true ) ; } | ||
} | ||
if ( ! dontDraw ) { this.draw() ; } | ||
} ; | ||
TextTable.prototype.getTextAttrForCell = function( x , y ) { | ||
return this.firstCellTextAttr && ! x && ! y ? this.firstCellTextAttr : | ||
this.firstRowTextAttr && ! y ? this.firstRowTextAttr : | ||
this.firstColumnTextAttr && ! x ? this.firstColumnTextAttr : | ||
this.evenCellTextAttr && ! ( x % 2 ) && ! ( y % 2 ) ? this.evenCellTextAttr : | ||
this.checkerEvenCellTextAttr && ! ( ( x + y ) % 2 ) ? this.checkerEvenCellTextAttr : | ||
this.evenRowTextAttr && ! ( y % 2 ) ? this.evenRowTextAttr : | ||
this.evenColumnTextAttr && ! ( y % 2 ) ? this.evenColumnTextAttr : | ||
this.textAttr ; | ||
} ; | ||
TextTable.prototype.getVoidAttrForCell = function( x , y , textAttr ) { | ||
return this.firstCellVoidAttr && ! x && ! y ? this.firstCellVoidAttr : | ||
this.firstRowVoidAttr && ! y ? this.firstRowVoidAttr : | ||
this.firstColumnVoidAttr && ! x ? this.firstColumnVoidAttr : | ||
this.evenCellVoidAttr && ! ( x % 2 ) && ! ( y % 2 ) ? this.evenCellVoidAttr : | ||
this.checkerEvenCellVoidAttr && ! ( ( x + y ) % 2 ) ? this.checkerEvenCellVoidAttr : | ||
this.evenRowVoidAttr && ! ( y % 2 ) ? this.evenRowVoidAttr : | ||
this.evenColumnVoidAttr && ! ( y % 2 ) ? this.evenColumnVoidAttr : | ||
this.voidAttr || textAttr ; | ||
} ; | ||
TextTable.prototype.initChildren = function() { | ||
@@ -172,22 +284,5 @@ var row , cellContent , textAttr , voidAttr ; | ||
textAttr = | ||
this.firstCellTextAttr && ! x && ! y ? this.firstCellTextAttr : | ||
this.firstRowTextAttr && ! y ? this.firstRowTextAttr : | ||
this.firstColumnTextAttr && ! x ? this.firstColumnTextAttr : | ||
this.evenCellTextAttr && ! ( x % 2 ) && ! ( y % 2 ) ? this.evenCellTextAttr : | ||
this.checkerEvenCellTextAttr && ! ( ( x + y ) % 2 ) ? this.checkerEvenCellTextAttr : | ||
this.evenRowTextAttr && ! ( y % 2 ) ? this.evenRowTextAttr : | ||
this.evenColumnTextAttr && ! ( y % 2 ) ? this.evenColumnTextAttr : | ||
this.textAttr ; | ||
textAttr = this.getTextAttrForCell( x , y ) ; | ||
voidAttr = this.getVoidAttrForCell( x , y , textAttr ) ; | ||
voidAttr = | ||
this.firstCellVoidAttr && ! x && ! y ? this.firstCellVoidAttr : | ||
this.firstRowVoidAttr && ! y ? this.firstRowVoidAttr : | ||
this.firstColumnVoidAttr && ! x ? this.firstColumnVoidAttr : | ||
this.evenCellVoidAttr && ! ( x % 2 ) && ! ( y % 2 ) ? this.evenCellVoidAttr : | ||
this.checkerEvenCellVoidAttr && ! ( ( x + y ) % 2 ) ? this.checkerEvenCellVoidAttr : | ||
this.evenRowVoidAttr && ! ( y % 2 ) ? this.evenRowVoidAttr : | ||
this.evenColumnVoidAttr && ! ( y % 2 ) ? this.evenColumnVoidAttr : | ||
this.voidAttr || textAttr ; | ||
this.textBoxes[ y ][ x ] = new TextBox( { | ||
@@ -241,3 +336,3 @@ internal: true , | ||
this.contentWidth = + this.hasBorder ; // +true = 1 | ||
this.rawContentWidth = + this.hasBorder ; // +true = 1 | ||
@@ -253,10 +348,14 @@ for ( x = 0 ; x < this.columnCount ; x ++ ) { | ||
this.columnWidths[ x ] = max ; | ||
this.contentWidth += max + this.hasBorder ; // +true = 1 | ||
this.rawContentWidth += max + this.hasBorder ; // +true = 1 | ||
} | ||
if ( this.expandToWidth && this.contentWidth < this.outputWidth ) { | ||
this.expand( this.contentWidth , this.outputWidth , this.columnWidths ) ; | ||
this.contentWidth = this.rawContentWidth ; | ||
if ( this.expandToWidth && this.rawContentWidth < this.outputWidth ) { | ||
this.expand( this.rawContentWidth , this.outputWidth , this.columnWidths ) ; | ||
this.contentWidth = this.outputWidth ; | ||
} | ||
else if ( this.shrinkToWidth && this.contentWidth > this.outputWidth ) { | ||
this.shrink( this.contentWidth , this.outputWidth , this.columnWidths ) ; | ||
else if ( this.shrinkToWidth && this.rawContentWidth > this.outputWidth ) { | ||
this.shrink( this.rawContentWidth , this.outputWidth , this.columnWidths ) ; | ||
this.contentWidth = this.outputWidth ; | ||
return true ; | ||
@@ -273,3 +372,3 @@ } | ||
this.contentHeight = + this.hasBorder ; // +true = 1 | ||
this.rawContentHeight = + this.hasBorder ; // +true = 1 | ||
@@ -285,12 +384,18 @@ for ( y = 0 ; y < this.rowCount ; y ++ ) { | ||
this.rowHeights[ y ] = max ; | ||
this.contentHeight += max + this.hasBorder ; // +true = 1 | ||
this.rawContentHeight += max + this.hasBorder ; // +true = 1 | ||
} | ||
if ( this.expandToHeight && this.contentHeight < this.outputHeight ) { | ||
this.expand( this.contentHeight , this.outputHeight , this.rowHeights ) ; | ||
this.contentHeight = this.rawContentHeight ; | ||
if ( this.expandToHeight && this.rawContentHeight < this.outputHeight ) { | ||
this.expand( this.rawContentHeight , this.outputHeight , this.rowHeights ) ; | ||
this.contentHeight = this.outputHeight ; | ||
} | ||
else if ( this.shrinkToHeight && this.contentHeight > this.outputHeight ) { | ||
this.shrink( this.contentHeight , this.outputHeight , this.rowHeights ) ; | ||
else if ( this.shrinkToHeight && this.rawContentHeight > this.outputHeight ) { | ||
this.shrink( this.rawContentHeight , this.outputHeight , this.rowHeights ) ; | ||
this.contentHeight = this.outputHeight ; | ||
return true ; | ||
} | ||
return false ; | ||
} ; | ||
@@ -332,4 +437,2 @@ | ||
//console.log( contentSize , outputSize , delta ) ; | ||
while ( delta > 0 ) { | ||
@@ -437,4 +540,2 @@ max = 0 ; | ||
//console.log( this.columnWidths , this.rowHeights , this.columnCount , this.rowCount ) ; | ||
y = this.outputY ; | ||
@@ -441,0 +542,0 @@ |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -52,9 +52,6 @@ The MIT License (MIT) | ||
module.exports = ToggleButton ; | ||
Element.inherit( ToggleButton , Button ) ; | ||
ToggleButton.prototype = Object.create( Button.prototype ) ; | ||
ToggleButton.prototype.constructor = ToggleButton ; | ||
ToggleButton.prototype.elementType = 'ToggleButton' ; | ||
ToggleButton.prototype.keyBindings = { | ||
@@ -73,3 +70,5 @@ ENTER: 'toggle' , | ||
ToggleButton.prototype.setValue = function( value , noDraw ) { | ||
ToggleButton.prototype.getValue = function() { return this.value ; } ; | ||
ToggleButton.prototype.setValue = function( value , noDraw , noEmit ) { | ||
value = !! value ; | ||
@@ -79,4 +78,8 @@ if ( this.value === value ) { return ; } | ||
this.updateStatus() ; | ||
this.emit( 'toggle' , this.value , undefined , this ) ; | ||
this.emit( this.value ? 'turnOn' : 'turnOff' , this.value , undefined , this ) ; | ||
if ( ! noEmit ) { | ||
this.emit( 'toggle' , this.value , undefined , this ) ; | ||
this.emit( this.value ? 'turnOn' : 'turnOff' , this.value , undefined , this ) ; | ||
} | ||
if ( ! noDraw ) { this.draw() ; } | ||
@@ -96,2 +99,3 @@ } ; | ||
this.attr = this.disabledAttr ; | ||
this.content = this.disabledContent ; | ||
this.leftPadding = this.disabledLeftPadding ; | ||
@@ -103,2 +107,3 @@ this.rightPadding = this.disabledRightPadding ; | ||
this.attr = this.turnedOnFocusAttr ; | ||
this.content = this.turnedOnFocusContent ; | ||
this.leftPadding = this.turnedOnFocusLeftPadding ; | ||
@@ -109,2 +114,3 @@ this.rightPadding = this.turnedOnFocusRightPadding ; | ||
this.attr = this.turnedOffFocusAttr ; | ||
this.content = this.turnedOffFocusContent ; | ||
this.leftPadding = this.turnedOffFocusLeftPadding ; | ||
@@ -116,2 +122,3 @@ this.rightPadding = this.turnedOffFocusRightPadding ; | ||
this.attr = this.turnedOnBlurAttr ; | ||
this.content = this.turnedOnBlurContent ; | ||
this.leftPadding = this.turnedOnBlurLeftPadding ; | ||
@@ -122,2 +129,3 @@ this.rightPadding = this.turnedOnBlurRightPadding ; | ||
this.attr = this.turnedOffBlurAttr ; | ||
this.content = this.turnedOffBlurContent ; | ||
this.leftPadding = this.turnedOffBlurLeftPadding ; | ||
@@ -130,21 +138,2 @@ this.rightPadding = this.turnedOffBlurRightPadding ; | ||
ToggleButton.prototype.onKey = function( key , altKeys , data ) { | ||
switch( this.keyBindings[ key ] ) { | ||
case 'toggle' : | ||
if ( this.disabled ) { break ; } | ||
this.toggle() ; | ||
break ; | ||
case 'submit' : | ||
if ( this.disabled ) { break ; } | ||
this.emit( 'submit' , this.key ? { [ this.key ]: this.value } : this.value , this.actionKeyBindings[ key ] , this ) ; | ||
break ; | ||
default : | ||
return ; // Bubble up | ||
} | ||
return true ; // Do not bubble up | ||
} ; | ||
ToggleButton.prototype.onHover = function( data ) { | ||
@@ -171,1 +160,15 @@ if ( this.disabled ) { return ; } | ||
const userActions = ToggleButton.prototype.userActions ; | ||
userActions.toggle = function() { | ||
if ( this.disabled ) { return ; } | ||
this.toggle() ; | ||
} ; | ||
userActions.submit = function( key ) { | ||
if ( this.disabled ) { return ; } | ||
this.emit( 'submit' , this.key ? { [ this.key ]: this.value } : this.value , this.actionKeyBindings[ key ] , this ) ; | ||
} ; | ||
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -33,3 +33,3 @@ The MIT License (MIT) | ||
const Container = require( './Container.js' ) ; | ||
const boxesChars = require( '../spChars.js' ).box ; | ||
const framesChars = require( '../spChars.js' ).box ; | ||
@@ -49,10 +49,13 @@ | ||
this.boxChars = boxesChars.double ; | ||
this.frameChars = framesChars.double ; | ||
if ( options.boxChars ) { | ||
if ( typeof options.boxChars === 'object' ) { | ||
this.boxChars = options.boxChars ; | ||
// Backward compatibility, boxChars is DEPRECATED | ||
if ( options.boxChars && ! options.frameChars ) { options.frameChars = options.boxChars ; } | ||
if ( options.frameChars ) { | ||
if ( typeof options.frameChars === 'object' ) { | ||
this.frameChars = options.frameChars ; | ||
} | ||
else if ( typeof options.boxChars === 'string' && boxesChars[ options.boxChars ] ) { | ||
this.boxChars = boxesChars[ options.boxChars ] ; | ||
else if ( typeof options.frameChars === 'string' && framesChars[ options.frameChars ] ) { | ||
this.frameChars = framesChars[ options.frameChars ] ; | ||
} | ||
@@ -66,8 +69,8 @@ } | ||
module.exports = Window ; | ||
Element.inherit( Window , Container ) ; | ||
Window.prototype = Object.create( Container.prototype ) ; | ||
Window.prototype.constructor = Window ; | ||
Window.prototype.elementType = 'Window' ; | ||
Window.prototype.containerBorderSize = 1 ; | ||
Window.prototype.outerDrag = true ; | ||
@@ -77,3 +80,3 @@ | ||
Window.prototype.preDrawSelf = function() { | ||
var y , title , titleWidth , | ||
var y , title , titleWidth , vFrame , | ||
titleMaxWidth = this.outputWidth - 8 ; | ||
@@ -88,6 +91,6 @@ | ||
{ x: this.outputX , y: this.outputY , markup: this.contentHasMarkup } , | ||
this.boxChars.topLeft + this.boxChars.horizontal | ||
this.frameChars.topLeft + this.frameChars.horizontal | ||
+ '[' + title + ']' | ||
+ this.boxChars.horizontal.repeat( this.outputWidth - 5 - titleWidth ) | ||
+ this.boxChars.topRight | ||
+ this.frameChars.horizontal.repeat( this.outputWidth - 5 - titleWidth ) | ||
+ this.frameChars.topRight | ||
) ; | ||
@@ -98,3 +101,3 @@ } | ||
{ x: this.outputX , y: this.outputY } , | ||
this.boxChars.topLeft + this.boxChars.horizontal.repeat( this.outputWidth - 2 ) + this.boxChars.topRight | ||
this.frameChars.topLeft + this.frameChars.horizontal.repeat( this.outputWidth - 2 ) + this.frameChars.topRight | ||
) ; | ||
@@ -106,10 +109,9 @@ } | ||
{ x: this.outputX , y: this.outputY + this.outputHeight - 1 } , | ||
this.boxChars.bottomLeft + this.boxChars.horizontal.repeat( this.outputWidth - 2 ) + this.boxChars.bottomRight | ||
this.frameChars.bottomLeft + this.frameChars.horizontal.repeat( this.outputWidth - 2 ) + this.frameChars.bottomRight | ||
) ; | ||
// Draw the left and right border | ||
for ( y = this.outputY + 1 ; y < this.outputY + this.outputHeight - 1 ; y ++ ) { | ||
this.outputDst.put( { x: this.outputX , y: y } , this.boxChars.vertical ) ; | ||
this.outputDst.put( { x: this.outputX + this.outputWidth - 1 , y: y } , this.boxChars.vertical ) ; | ||
} | ||
vFrame = this.frameChars.vertical.repeat( this.outputHeight - 2 ) ; | ||
this.outputDst.put( { x: this.outputX , y: this.outputY + 1 , direction: 'down' } , vFrame ) ; | ||
this.outputDst.put( { x: this.outputX + this.outputWidth - 1 , y: this.outputY + 1 , direction: 'down' } , vFrame ) ; | ||
@@ -116,0 +118,0 @@ Container.prototype.preDrawSelf.call( this ) ; |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -31,6 +31,4 @@ The MIT License (MIT) | ||
var Promise = require( 'seventh' ) ; | ||
var autoComplete = require( './autoComplete.js' ) ; | ||
var fs = require( 'fs' ) ; | ||
var path = require( 'path' ) ; | ||
const fileHelpers = require( './fileHelpers.js' ) ; | ||
const path = require( 'path' ) ; | ||
@@ -42,132 +40,38 @@ | ||
*/ | ||
module.exports = function fileInput( options , callback ) { | ||
module.exports = async function fileInput( options , callback ) { | ||
var baseDir , autoCompleteFileOptions , inputFieldOptions , input ; | ||
if ( typeof options === 'function' ) { callback = options ; options = {} ; } | ||
if ( ! options || typeof options !== 'object' ) { options = {} ; } | ||
var baseDir ; | ||
try { | ||
baseDir = await fileHelpers.resolveBaseDir( options.baseDir ) ; | ||
var promise = new Promise() ; | ||
autoCompleteFileOptions = { baseDir } ; | ||
if ( options.baseDir ) { | ||
baseDir = path.resolve( options.baseDir ) ; | ||
// Transmit options to inputField() | ||
inputFieldOptions = Object.assign( {} , options , { | ||
autoComplete: inputString => fileHelpers.autoCompleteFile( inputString , autoCompleteFileOptions ) , | ||
autoCompleteMenu: true , | ||
minLength: 1 | ||
} ) ; | ||
if ( ! path.isAbsolute( baseDir ) ) { | ||
fs.realpath( options.baseDir , ( error , resolvedPath ) => { | ||
if ( error ) { | ||
if ( callback ) { callback( error ) ; } | ||
else { promise.reject( error ) ; } | ||
return ; | ||
} | ||
input = await this.inputField( inputFieldOptions ).promise ; | ||
} | ||
catch ( error ) { | ||
if ( callback ) { callback( error ) ; return ; } | ||
throw error ; | ||
} | ||
options.baseDir = resolvedPath ; | ||
this.fileInput( options ).then( | ||
input => { | ||
if ( callback ) { callback( input ) ; } | ||
else { promise.resolve( input ) ; } | ||
} , | ||
error_ => { | ||
if ( callback ) { callback( error_ ) ; } | ||
else { promise.reject( error_ ) ; } | ||
} | ||
) ; | ||
} ) ; | ||
return promise ; | ||
} | ||
if ( ! input && typeof input !== 'string' ) { | ||
input = undefined ; | ||
} | ||
else { | ||
baseDir = process.cwd() ; | ||
input = path.resolve( path.isAbsolute( input ) ? input : baseDir + input ) ; | ||
} | ||
if ( baseDir[ baseDir.length - 1 ] !== '/' ) { baseDir += '/' ; } | ||
if ( callback ) { callback( undefined , input ) ; } | ||
var autoCompleter = async function autoCompleter( inputString ) { | ||
var inputDir , inputFile , currentDir , files , completion ; | ||
if ( inputString[ inputString.length - 1 ] === '/' ) { | ||
inputDir = inputString ; | ||
inputFile = '' ; | ||
} | ||
else { | ||
inputDir = path.dirname( inputString ) ; | ||
inputDir = inputDir === '.' ? '' : inputDir + '/' ; | ||
inputFile = path.basename( inputString ) ; | ||
} | ||
// If the input start with a '/', then forget about the baseDir | ||
if ( path.isAbsolute( inputString ) ) { currentDir = inputDir ; } | ||
else { currentDir = baseDir + inputDir ; } | ||
//console.error( "### '" + inputDir +"' '"+ inputFile +"' '"+ currentDir + "'" ) ; | ||
try { | ||
files = await readdir( currentDir ) ; | ||
} | ||
catch ( error ) { | ||
return inputString ; | ||
} | ||
if ( ! Array.isArray( files ) || ! files.length ) { return inputString ; } | ||
completion = autoComplete( files , inputFile , true ) ; | ||
// force inputField() to prefix that *AFTER* singleLineMenu() | ||
if ( Array.isArray( completion ) ) { completion.prefix = inputDir ; } | ||
else { completion = path.normalize( inputDir + completion ) ; } | ||
return completion ; | ||
} ; | ||
// Transmit options to inputField() | ||
options = Object.assign( {} , options , { autoComplete: autoCompleter , autoCompleteMenu: true , minLength: 1 } ) ; | ||
this.inputField( options ).promise.then( | ||
input => { | ||
if ( ! input && typeof input !== 'string' ) { | ||
input = undefined ; | ||
} | ||
else { | ||
input = path.resolve( path.isAbsolute( input ) ? input : baseDir + input ) ; | ||
} | ||
if ( callback ) { callback( undefined , input ) ; } | ||
else { promise.resolve( input ) ; } | ||
} , | ||
error => { | ||
if ( callback ) { callback( error ) ; } | ||
else { promise.reject( error ) ; } | ||
} | ||
) ; | ||
return promise ; | ||
return input ; | ||
} ; | ||
// Like fs.readdir(), but performs fs.stat() for each file in order to add a '/' to directories | ||
function readdir( dir ) { | ||
var promise = new Promise() ; | ||
if ( dir[ dir.length - 1 ] !== '/' ) { dir += '/' ; } | ||
fs.readdir( dir , ( error , files ) => { | ||
if ( error ) { promise.reject( error ) ; return ; } | ||
Promise.map( files , file => { | ||
return new Promise( ( resolve , reject ) => { | ||
fs.lstat( dir + file , ( error_ , stats ) => { | ||
if ( error_ ) { reject( error_ ) ; return ; } | ||
if ( stats.isDirectory() ) { file += '/' ; } | ||
resolve( file ) ; | ||
} ) ; | ||
} ) ; | ||
} ) | ||
.toPromise( promise ) ; | ||
} ) ; | ||
return promise ; | ||
} | ||
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -37,7 +37,7 @@ The MIT License (MIT) | ||
var net = require( 'net' ) ; | ||
var NextGenEvents = require( 'nextgen-events' ) ; | ||
var termkit = require( './termkit.js' ) ; | ||
const net = require( 'net' ) ; | ||
const NextGenEvents = require( 'nextgen-events' ) ; | ||
const termkit = require( './termkit.js' ) ; | ||
var gpm = {} ; | ||
const gpm = {} ; | ||
module.exports = gpm ; | ||
@@ -55,3 +55,3 @@ | ||
// Can't figure out the usage of the GPM_MAGIC constant ATM | ||
var gpmMagic = Buffer.allocUnsafe( 4 ) ; | ||
const gpmMagic = Buffer.allocUnsafe( 4 ) ; | ||
gpmMagic.writeUInt32LE( 0x47706D4C , 0 ) ; | ||
@@ -62,3 +62,3 @@ | ||
// Return a Buffer containing a Gpm_Connect structure, using a pid and a ttyIndex | ||
gpm.connectStructureBuffer = function connectStructureBuffer( gpmConnect ) { | ||
gpm.connectStructureBuffer = gpmConnect => { | ||
var buffer = Buffer.allocUnsafe( 16 ) ; | ||
@@ -87,3 +87,3 @@ | ||
// Extract a Gpm_Event from a Buffer | ||
gpm.eventStructure = function eventStructure( buffer ) { | ||
gpm.eventStructure = buffer => { | ||
var event = {} ; | ||
@@ -159,3 +159,3 @@ | ||
// Create a new GPM Handler | ||
gpm.createHandler = function createHandler( options ) { | ||
gpm.createHandler = function( options ) { | ||
if ( ! options || typeof options !== 'object' ) { options = {} ; } | ||
@@ -225,3 +225,3 @@ | ||
// End/Close the underlying connection | ||
gpm.Handler.prototype.close = function handlerClose() { | ||
gpm.Handler.prototype.close = function() { | ||
if ( this.socket ) { | ||
@@ -236,3 +236,3 @@ this.socket.destroy() ; | ||
// Transform raw GPM event to terminal-kit event | ||
gpm.raw2terminalKitEvent = function raw2terminalKitEvent( event ) { | ||
gpm.raw2terminalKitEvent = function( event ) { | ||
var name ; | ||
@@ -265,2 +265,1 @@ | ||
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -31,8 +31,8 @@ The MIT License (MIT) | ||
var ndarray = require( 'ndarray' ) ; | ||
var Promise = require( 'seventh' ) ; | ||
const ndarray = require( 'ndarray' ) ; | ||
const Promise = require( 'seventh' ) ; | ||
var image = {} ; | ||
const image = {} ; | ||
module.exports = image ; | ||
@@ -43,2 +43,3 @@ | ||
var getPixels ; | ||
if ( global.IS_BROWSER ) { getPixels = require( '@cronvel/get-pixels' ) ; } | ||
@@ -45,0 +46,0 @@ |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
338
lib/misc.js
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -35,7 +35,2 @@ The MIT License (MIT) | ||
const misc = {} ; | ||
module.exports = misc ; | ||
const colorNameToIndexDict = { | ||
@@ -68,3 +63,3 @@ // ANSI | ||
// Color name to index | ||
misc.colorNameToIndex = color => colorNameToIndexDict[ color.toLowerCase() ] ; | ||
exports.colorNameToIndex = color => colorNameToIndexDict[ color.toLowerCase() ] ; | ||
@@ -81,7 +76,7 @@ | ||
// Color name to index | ||
misc.indexToColorName = index => indexToColorNameArray[ index ] ; | ||
exports.indexToColorName = index => indexToColorNameArray[ index ] ; | ||
misc.hexToRgba = hex => { | ||
exports.hexToRgba = hex => { | ||
// Strip the # if necessary | ||
@@ -95,6 +90,6 @@ if ( hex[ 0 ] === '#' ) { hex = hex.slice( 1 ) ; } | ||
return { | ||
r: parseInt( hex.slice( 0 , 2 ) , 16 ) , | ||
g: parseInt( hex.slice( 2 , 4 ) , 16 ) , | ||
b: parseInt( hex.slice( 4 , 6 ) , 16 ) , | ||
a: hex.length > 6 ? parseInt( hex.slice( 6 , 8 ) , 16 ) : 255 | ||
r: parseInt( hex.slice( 0 , 2 ) , 16 ) || 0 , | ||
g: parseInt( hex.slice( 2 , 4 ) , 16 ) || 0 , | ||
b: parseInt( hex.slice( 4 , 6 ) , 16 ) || 0 , | ||
a: hex.length > 6 ? parseInt( hex.slice( 6 , 8 ) , 16 ) || 0 : 255 | ||
} ; | ||
@@ -106,5 +101,5 @@ } ; | ||
// DEPRECATED function names | ||
misc.color2index = misc.colorNameToIndex ; | ||
misc.index2color = misc.indexToColorName ; | ||
misc.hexToColor = misc.hexToRgba ; | ||
exports.color2index = exports.colorNameToIndex ; | ||
exports.index2color = exports.indexToColorName ; | ||
exports.hexToColor = exports.hexToRgba ; | ||
@@ -114,3 +109,3 @@ | ||
// Strip all control chars, if newline is true, only newline control chars are preserved | ||
misc.stripControlChars = ( str , newline ) => { | ||
exports.stripControlChars = ( str , newline ) => { | ||
if ( newline ) { return str.replace( /[\x00-\x09\x0b-\x1f\x7f]/g , '' ) ; } | ||
@@ -128,3 +123,3 @@ return str.replace( /[\x00-\x1f\x7f]/g , '' ) ; | ||
misc.stripEscapeSequences = str => str.replace( escapeSequenceRegex , '' ) ; | ||
exports.stripEscapeSequences = str => str.replace( escapeSequenceRegex , '' ) ; | ||
@@ -134,4 +129,4 @@ | ||
// Return the real width of the string (i.e. as displayed in the terminal) | ||
misc.ansiWidth = | ||
misc.stringWidth = str => { | ||
exports.ansiWidth = | ||
exports.stringWidth = str => { | ||
var matches , width = 0 ; | ||
@@ -156,3 +151,3 @@ | ||
var lastTruncateWidth = 0 ; | ||
misc.getLastTruncateWidth = () => lastTruncateWidth ; | ||
exports.getLastTruncateWidth = () => lastTruncateWidth ; | ||
@@ -162,4 +157,4 @@ | ||
// Truncate a string to a given real width | ||
misc.truncateAnsiString = | ||
misc.truncateString = ( str , maxWidth ) => { | ||
exports.truncateAnsiString = | ||
exports.truncateString = ( str , maxWidth ) => { | ||
var matches , width = 0 ; | ||
@@ -193,5 +188,7 @@ | ||
// width of a string with a markup, without control chars | ||
misc.markupWidth = str => { | ||
return string.unicode.width( str.replace( /\^\[[^\]]*]|\^(.)/g , ( match , second ) => { | ||
// Width of a string with a markup, without control chars | ||
exports.markupWidth = str => { | ||
// Fix a possible ReDoS, the regex: /\^\[[^\]]*]|\^(.)/g was replaced by: /\^\[[^\]]*]?|\^(.)/g | ||
// The exploit was possible with a string like: '^['.repeat(bigNumber) | ||
return string.unicode.width( str.replace( /\^\[[^\]]*]?|\^(.)/g , ( match , second ) => { | ||
if ( second === ' ' || second === '^' ) { | ||
@@ -208,3 +205,3 @@ return second ; | ||
// Truncate a string to a given real width, the string may contains markup, but no control chars | ||
misc.truncateMarkupString = ( str , maxWidth ) => { | ||
exports.truncateMarkupString = ( str , maxWidth ) => { | ||
var index = 0 , charWidth , | ||
@@ -249,3 +246,3 @@ strArray = string.unicode.toArray( str ) ; | ||
// TODO: many issues remaining | ||
misc.escapeSequenceSkipFn = ( strArray , index ) => { | ||
exports.escapeSequenceSkipFn = ( strArray , index ) => { | ||
//console.error( '>>> Entering' ) ; | ||
@@ -275,3 +272,3 @@ var code ; | ||
misc.wordWrapAnsi = ( str , width ) => string.wordwrap( str , { | ||
exports.wordWrapAnsi = ( str , width ) => string.wordwrap( str , { | ||
width: width , | ||
@@ -327,4 +324,4 @@ noJoin: true , | ||
misc.wordwrapMarkup = // <-- DEPRECATED | ||
misc.wordWrapMarkup = ( str , width ) => string.wordwrap( str , { | ||
exports.wordwrapMarkup = // <-- DEPRECATED name (bad camel case) | ||
exports.wordWrapMarkup = ( str , width ) => string.wordwrap( str , { | ||
width: width , | ||
@@ -381,3 +378,3 @@ noJoin: true , | ||
misc.preserveMarkupFormat = string.createFormatter( { | ||
exports.preserveMarkupFormat = string.createFormatter( { | ||
argumentSanitizer: str => str.replace( /[\x00-\x1f\x7f^]/g , char => char === '^' ? '^^' : '' ) , | ||
@@ -389,8 +386,60 @@ noMarkup: true | ||
misc.markupOptions = { | ||
complexMarkupAliases: { | ||
c: 'color' , | ||
fg: 'color' , | ||
bg: 'bgColor' | ||
} , | ||
// Catch-all keywords to key:value | ||
const CATCH_ALL_KEYWORDS = { | ||
// Foreground colors | ||
defaultColor: [ 'color' , 'default' ] , | ||
black: [ 'color' , 'black' ] , | ||
red: [ 'color' , 'red' ] , | ||
green: [ 'color' , 'green' ] , | ||
yellow: [ 'color' , 'yellow' ] , | ||
blue: [ 'color' , 'blue' ] , | ||
magenta: [ 'color' , 'magenta' ] , | ||
cyan: [ 'color' , 'cyan' ] , | ||
white: [ 'color' , 'white' ] , | ||
grey: [ 'color' , 'grey' ] , | ||
gray: [ 'color' , 'gray' ] , | ||
brightBlack: [ 'color' , 'brightBlack' ] , | ||
brightRed: [ 'color' , 'brightRed' ] , | ||
brightGreen: [ 'color' , 'brightGreen' ] , | ||
brightYellow: [ 'color' , 'brightYellow' ] , | ||
brightBlue: [ 'color' , 'brightBlue' ] , | ||
brightMagenta: [ 'color' , 'brightMagenta' ] , | ||
brightCyan: [ 'color' , 'brightCyan' ] , | ||
brightWhite: [ 'color' , 'brightWhite' ] , | ||
// Background colors | ||
defaultBgColor: [ 'bgColor' , 'default' ] , | ||
bgBlack: [ 'bgColor' , 'black' ] , | ||
bgRed: [ 'bgColor' , 'red' ] , | ||
bgGreen: [ 'bgColor' , 'green' ] , | ||
bgYellow: [ 'bgColor' , 'yellow' ] , | ||
bgBlue: [ 'bgColor' , 'blue' ] , | ||
bgMagenta: [ 'bgColor' , 'magenta' ] , | ||
bgCyan: [ 'bgColor' , 'cyan' ] , | ||
bgWhite: [ 'bgColor' , 'white' ] , | ||
bgGrey: [ 'bgColor' , 'grey' ] , | ||
bgGray: [ 'bgColor' , 'gray' ] , | ||
bgBrightBlack: [ 'bgColor' , 'brightBlack' ] , | ||
bgBrightRed: [ 'bgColor' , 'brightRed' ] , | ||
bgBrightGreen: [ 'bgColor' , 'brightGreen' ] , | ||
bgBrightYellow: [ 'bgColor' , 'brightYellow' ] , | ||
bgBrightBlue: [ 'bgColor' , 'brightBlue' ] , | ||
bgBrightMagenta: [ 'bgColor' , 'brightMagenta' ] , | ||
bgBrightCyan: [ 'bgColor' , 'brightCyan' ] , | ||
bgBrightWhite: [ 'bgColor' , 'brightWhite' ] , | ||
// Other styles | ||
dim: [ 'dim' , true ] , | ||
bold: [ 'bold' , true ] , | ||
underline: [ 'underline' , true ] , | ||
italic: [ 'italic' , true ] , | ||
inverse: [ 'inverse' , true ] | ||
} ; | ||
exports.markupCatchAllKeywords = CATCH_ALL_KEYWORDS ; | ||
exports.markupOptions = { | ||
parse: true , | ||
shiftMarkup: { | ||
@@ -400,5 +449,11 @@ '#': 'background' | ||
markup: { | ||
':': { reset: true } , | ||
' ': { reset: true , raw: ' ' } , | ||
';': { reset: true , special: true } , // "Special reset" can reset forced attr (Document-model) | ||
':': null , | ||
' ': markupStack => { | ||
markupStack.length = 0 ; | ||
return [ null , ' ' ] ; | ||
} , | ||
';': markupStack => { | ||
markupStack.length = 0 ; | ||
return [ null , { specialReset: true } ] ; | ||
} , | ||
@@ -430,8 +485,10 @@ '-': { dim: true } , | ||
background: { | ||
':': { reset: true , defaultColor: true , bgDefaultColor: true } , | ||
' ': { | ||
reset: true , defaultColor: true , bgDefaultColor: true , raw: ' ' | ||
':': [ null , { defaultColor: true , bgDefaultColor: true } ] , | ||
' ': markupStack => { | ||
markupStack.length = 0 ; | ||
return [ null , { defaultColor: true , bgDefaultColor: true } , ' ' ] ; | ||
} , | ||
';': { | ||
reset: true , special: true , defaultColor: true , bgDefaultColor: true | ||
';': markupStack => { | ||
markupStack.length = 0 ; | ||
return [ null , { specialReset: true , defaultColor: true , bgDefaultColor: true } ] ; | ||
} , | ||
@@ -456,39 +513,30 @@ | ||
} | ||
} | ||
} ; | ||
} , | ||
dataMarkup: { | ||
color: 'color' , | ||
fgColor: 'color' , | ||
fg: 'color' , | ||
c: 'color' , | ||
bgColor: 'bgColor' , | ||
bg: 'bgColor' | ||
} , | ||
markupCatchAll: ( markupStack , key , value ) => { | ||
var attr = {} ; | ||
// /!\ Should be moved to string-kit once finished /!\ | ||
const parseMarkupRegexp = /\^\[([^\]]*)]|\^(.)|([^^]+)/g ; | ||
misc.parseMarkup = ( str , options ) => { | ||
var complex , markup , raw , match , | ||
base = options.markup , | ||
output = [] ; | ||
parseMarkupRegexp.lastIndex = 0 ; | ||
while ( ( match = parseMarkupRegexp.exec( str ) ) ) { | ||
[ , complex , markup , raw ] = match ; | ||
if ( complex ) { | ||
var custom = {} ; | ||
complex.split( ',' ).forEach( part => { | ||
var [ k , v ] = part.split( ':' ) ; | ||
if ( options.complexMarkupAliases[ k ] ) { k = options.complexMarkupAliases[ k ] ; } | ||
custom[ k ] = v || true ; | ||
} ) ; | ||
output.push( { markup: custom } ) ; | ||
if ( value === undefined ) { | ||
if ( key[ 0 ] === '#' ) { | ||
attr.color = key ; | ||
} | ||
else if ( CATCH_ALL_KEYWORDS[ key ] ) { | ||
attr[ CATCH_ALL_KEYWORDS[ key ][ 0 ] ] = CATCH_ALL_KEYWORDS[ key ][ 1 ] ; | ||
} | ||
else { | ||
// Fallback: it's a foreground color | ||
attr.color = key ; | ||
} | ||
} | ||
else if ( raw ) { output.push( raw ) ; } | ||
else if ( markup === '^' ) { output.push( '^' ) ; } | ||
else if ( options.shiftMarkup[ markup ] ) { base = options.shiftedMarkup[ options.shiftMarkup[ markup ] ] ; continue ; } | ||
else if ( base[ markup ] ) { output.push( { markup: base[ markup ] } ) ; } | ||
base = options.markup ; | ||
markupStack.push( attr ) ; | ||
return attr || {} ; | ||
} | ||
return output ; | ||
} ; | ||
@@ -498,83 +546,67 @@ | ||
const ANSI_CODES = { | ||
'0': { reset: true } , | ||
const asciiSymbolName = { | ||
' ': 'SPACE' , | ||
'!': 'EXCLAMATION' , | ||
'"': 'DOUBLE_QUOTE' , | ||
'#': 'HASH' , | ||
'$': 'DOLLAR' , | ||
'%': 'PERCENT' , | ||
'&': 'AMPERSAND' , | ||
"'": 'SINGLE_QUOTE' , | ||
'(': 'OPEN_PARENTHESIS' , | ||
')': 'CLOSE_PARENTHESIS' , | ||
'*': 'ASTERISK' , | ||
'+': 'PLUS' , | ||
',': 'COMMA' , | ||
'-': 'HYPHEN' , | ||
'.': 'DOT' , | ||
'/': 'SLASH' , | ||
':': 'COLON' , | ||
';': 'SEMICOLON' , | ||
'<': 'LESS_THAN' , | ||
'=': 'EQUAL' , | ||
'>': 'GREATER_THAN' , | ||
'?': 'QUESTION' , | ||
'@': 'AT' , | ||
'[': 'OPEN_BRACKET' , | ||
'\\': 'BACKSLASH' , | ||
']': 'CLOSE_BRACKET' , | ||
'^': 'CARET' , | ||
'_': 'UNDERSCORE' , | ||
'`': 'BACK_QUOTE' , | ||
'{': 'OPEN_BRACE' , | ||
'|': 'PIPE' , | ||
'}': 'CLOSE_BRACE' , | ||
'~': 'TILDE' | ||
} ; | ||
'1': { bold: true } , | ||
'2': { dim: true } , | ||
'22': { bold: false , dim: false } , | ||
'3': { italic: true } , | ||
'23': { italic: false } , | ||
'4': { underline: true } , | ||
'24': { underline: false } , | ||
'5': { blink: true } , | ||
'25': { blink: false } , | ||
'7': { inverse: true } , | ||
'27': { inverse: false } , | ||
'8': { hidden: true } , | ||
'28': { hidden: false } , | ||
'9': { strike: true } , | ||
'29': { strike: false } , | ||
// Non-control character name | ||
exports.characterName = char => { | ||
if ( asciiSymbolName[ char ] ) { return asciiSymbolName[ char ] ; } | ||
return char.toUpperCase() ; | ||
} ; | ||
'30': { color: 0 } , | ||
'31': { color: 1 } , | ||
'32': { color: 2 } , | ||
'33': { color: 3 } , | ||
'34': { color: 4 } , | ||
'35': { color: 5 } , | ||
'36': { color: 6 } , | ||
'37': { color: 7 } , | ||
'39:': { defaultColor: true } , | ||
// Transform a Terminal-Kit Key code (like CTRL_C) to user-friendly/interface name (like Ctrl-C) | ||
exports.keyToUserInterfaceName = key => { | ||
if ( asciiSymbolName[ key ] ) { return asciiSymbolName[ key ] ; } | ||
'90': { color: 8 } , | ||
'91': { color: 9 } , | ||
'92': { color: 10 } , | ||
'93': { color: 11 } , | ||
'94': { color: 12 } , | ||
'95': { color: 13 } , | ||
'96': { color: 14 } , | ||
'97': { color: 15 } , | ||
'40': { bgColor: 0 } , | ||
'41': { bgColor: 1 } , | ||
'42': { bgColor: 2 } , | ||
'43': { bgColor: 3 } , | ||
'44': { bgColor: 4 } , | ||
'45': { bgColor: 5 } , | ||
'46': { bgColor: 6 } , | ||
'47': { bgColor: 7 } , | ||
'49:': { bgDefaultColor: true } , | ||
'100': { bgColor: 8 } , | ||
'101': { bgColor: 9 } , | ||
'102': { bgColor: 10 } , | ||
'103': { bgColor: 11 } , | ||
'104': { bgColor: 12 } , | ||
'105': { bgColor: 13 } , | ||
'106': { bgColor: 14 } , | ||
'107': { bgColor: 15 } | ||
return key.replace( | ||
/([A-Za-z0-9])([A-Za-z0-9]*)|([_-]+)/g , | ||
( match , firstLetter , endOfWord , separator ) => { | ||
if ( separator ) { return '-' ; } | ||
return firstLetter.toUpperCase() + endOfWord.toLowerCase() ; | ||
} | ||
) ; | ||
} ; | ||
// /!\ Should be moved to string-kit once finished /!\ | ||
const parseAnsiRegexp = /\x1b\[([0-9;]+)m|(.[^\x1b]*)/g ; | ||
misc.parseAnsi = str => { | ||
var match , ansiCodes , raw , output = [] ; | ||
parseAnsiRegexp.lastIndex = 0 ; | ||
while ( ( match = parseAnsiRegexp.exec( str ) ) ) { | ||
[ , ansiCodes , raw ] = match ; | ||
if ( raw ) { output.push( raw ) ; } | ||
else { | ||
ansiCodes.split( /;/g ).forEach( ansiCode => { | ||
if ( ANSI_CODES[ ansiCode ] ) { output.push( { markup: ANSI_CODES[ ansiCode ] } ) ; } | ||
} ) ; | ||
// Transform a user-friendly/interface name (like Ctrl-C) to a Terminal-Kit Key code (like CTRL_C) | ||
exports.userInterfaceNameToKey = key => { | ||
return key.replace( | ||
/([A-Za-z0-9]+)|([_-]+)/g , | ||
( match , word , separator ) => { | ||
if ( separator ) { return '_' ; } | ||
return word.toUpperCase() ; | ||
} | ||
} | ||
return output ; | ||
) ; | ||
} ; | ||
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -217,4 +217,4 @@ The MIT License (MIT) | ||
for ( j = 2 ; j >= -3 ; j -- ) { | ||
if ( j > 0 ) { | ||
@@ -238,3 +238,2 @@ lightnessMark = '+'.repeat( j ) ; | ||
} | ||
} | ||
@@ -241,0 +240,0 @@ } ; |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -264,3 +264,3 @@ The MIT License (MIT) | ||
ScreenBuffer.prototype.preserveMarkupFormat = misc.preserveMarkupFormat ; | ||
ScreenBuffer.prototype.markupOptions = misc.markupOptions ; | ||
ScreenBuffer.prototype.parseMarkup = string.markupMethod.bind( misc.markupOptions ) ; | ||
@@ -276,3 +276,4 @@ | ||
* y: bypass this.cy | ||
* markup: boolean or 'ansi', true if the text contains markup that should be interpreted, 'ansi' if it contains ansi code | ||
* markup: boolean or 'ansi' or 'legacyAnsi', true if the text contains markup that should be interpreted, | ||
'ansi' if it contains ansi code, 'legacyAnsi' is bold is bright fg and blink is bright bg | ||
* attr: standard attributes | ||
@@ -288,3 +289,3 @@ * resumeAttr: attr to resume to | ||
ScreenBuffer.prototype.put = function( options , str , ... args ) { | ||
var parser , startX , startY , x , y , dx , dy , baseAttr , attr , attrObject , wrap ; | ||
var startX , startY , x , y , dx , dy , baseAttr , attr , attrObject , wrap ; | ||
@@ -344,10 +345,7 @@ // Manage options | ||
if ( args.length ) { | ||
str = ! options.markup || options.markup === 'ansi' ? string.format( str , ... args ) : | ||
this.preserveMarkupFormat( str , ... args ) ; | ||
str = options.markup === true ? this.preserveMarkupFormat( str , ... args ) : string.format( str , ... args ) ; | ||
} | ||
parser = ! options.markup ? null : | ||
options.markup === 'ansi' ? termkit.parseAnsi : | ||
termkit.parseMarkup ; | ||
// The processing of raw chunk of text | ||
@@ -448,25 +446,33 @@ var processRaw = part => { | ||
else { | ||
attrObject = this.attr2object( attr ) ; | ||
parser( str , this.markupOptions ).forEach( part => { | ||
if ( typeof part === 'string' ) { | ||
processRaw( part ) ; | ||
} | ||
else { | ||
if ( part.markup.reset ) { | ||
attr = part.markup.special ? this.DEFAULT_ATTR : baseAttr ; | ||
attrObject = this.attr2object( attr ) ; | ||
} | ||
else { | ||
Object.assign( attrObject , part.markup ) ; | ||
const defaultAttrObject = this.attr2object( this.DEFAULT_ATTR ) ; | ||
const baseAttrObject = this.attr2object( baseAttr ) ; | ||
// Remove incompatible flags | ||
if ( attrObject.defaultColor && attrObject.color ) { delete attrObject.defaultColor ; } | ||
if ( attrObject.bgDefaultColor && attrObject.bgColor ) { delete attrObject.bgDefaultColor ; } | ||
let legacyColor = false ; | ||
let parts = null ; | ||
attr = this.object2attr( attrObject ) ; | ||
} | ||
switch ( options.markup ) { | ||
case 'ansi' : | ||
parts = string.ansi.parse( str ) ; | ||
break ; | ||
case 'legacyAnsi' : | ||
parts = string.ansi.parse( str ) ; | ||
legacyColor = true ; | ||
break ; | ||
case true : | ||
parts = this.parseMarkup( str ) ; | ||
break ; | ||
} | ||
if ( part.markup.raw ) { | ||
processRaw( part.markup.raw ) ; | ||
} | ||
parts.forEach( part => { | ||
attrObject = Object.assign( {} , part.specialReset ? defaultAttrObject : baseAttrObject , part ) ; | ||
delete attrObject.text ; | ||
// Remove incompatible flags | ||
if ( attrObject.defaultColor && attrObject.color ) { delete attrObject.defaultColor ; } | ||
if ( attrObject.bgDefaultColor && attrObject.bgColor ) { delete attrObject.bgDefaultColor ; } | ||
attr = this.object2attr( attrObject , undefined , legacyColor ) ; | ||
if ( part.text ) { | ||
processRaw( part.text ) ; | ||
} | ||
@@ -1409,4 +1415,4 @@ } ) ; | ||
ScreenBuffer.object2attr = function( object , colorNameToIndex ) { | ||
var attr = 0 ; | ||
ScreenBuffer.object2attr = function( object , colorNameToIndex , legacyColor = false ) { | ||
var attr = 0 , brightFg = false , brightBg = false ; | ||
@@ -1416,2 +1422,18 @@ if ( ! object || typeof object !== 'object' ) { object = {} ; } | ||
// Style part | ||
if ( object.bold ) { | ||
if ( legacyColor ) { brightFg = true ; } | ||
else { attr |= BOLD ; } | ||
} | ||
if ( object.dim ) { attr |= DIM ; } | ||
if ( object.italic ) { attr |= ITALIC ; } | ||
if ( object.underline ) { attr |= UNDERLINE ; } | ||
if ( object.blink ) { | ||
if ( legacyColor ) { brightBg = true ; } | ||
else { attr |= BLINK ; } | ||
} | ||
if ( object.inverse ) { attr |= INVERSE ; } | ||
if ( object.hidden ) { attr |= HIDDEN ; } | ||
if ( object.strike ) { attr |= STRIKE ; } | ||
// Color part | ||
@@ -1429,2 +1451,6 @@ if ( typeof object.color === 'string' ) { | ||
object.color = Math.floor( object.color ) ; | ||
if ( legacyColor && object.color <= 15 ) { | ||
if ( brightFg && object.color <= 7 ) { object.color += 8 ; } | ||
else if ( ! brightFg && object.color >= 8 ) { object.color -= 8 ; } | ||
} | ||
} | ||
@@ -1446,2 +1472,6 @@ | ||
object.bgColor = Math.floor( object.bgColor ) ; | ||
if ( legacyColor && object.bgColor <= 15 ) { | ||
if ( brightBg && object.bgColor <= 7 ) { object.bgColor += 8 ; } | ||
else if ( ! brightBg && object.bgColor >= 8 ) { object.bgColor -= 8 ; } | ||
} | ||
} | ||
@@ -1455,12 +1485,2 @@ | ||
// Style part | ||
if ( object.bold ) { attr |= BOLD ; } | ||
if ( object.dim ) { attr |= DIM ; } | ||
if ( object.italic ) { attr |= ITALIC ; } | ||
if ( object.underline ) { attr |= UNDERLINE ; } | ||
if ( object.blink ) { attr |= BLINK ; } | ||
if ( object.inverse ) { attr |= INVERSE ; } | ||
if ( object.hidden ) { attr |= HIDDEN ; } | ||
if ( object.strike ) { attr |= STRIKE ; } | ||
// Blending part | ||
@@ -1476,4 +1496,4 @@ if ( object.transparency ) { attr |= TRANSPARENCY ; } | ||
ScreenBuffer.prototype.object2attr = function( object ) { | ||
return ScreenBuffer.object2attr( object , this.palette && this.palette.colorNameToIndex ) ; | ||
ScreenBuffer.prototype.object2attr = function( object , colorNameToIndex = this.palette && this.palette.colorNameToIndex , legacyColor = false ) { | ||
return ScreenBuffer.object2attr( object , colorNameToIndex , legacyColor ) ; | ||
} ; | ||
@@ -1564,5 +1584,4 @@ | ||
// Add the selection flag, i.e. the INVERSE flag | ||
ScreenBuffer.attrSelect = ScreenBuffer.prototype.attrSelect = attr => attr | INVERSE ; | ||
ScreenBuffer.attrUnselect = ScreenBuffer.prototype.attrUnselect = attr => attr & ~ INVERSE ; | ||
// Used by TextBuffer for selection | ||
ScreenBuffer.attrInverse = ScreenBuffer.prototype.attrInverse = attr => attr ^ INVERSE ; | ||
@@ -1634,2 +1653,3 @@ | ||
ScreenBuffer.DEFAULT_ATTR = // <- used by TextBuffer | ||
ScreenBuffer.prototype.DEFAULT_ATTR = ScreenBuffer.object2attr( { defaultColor: true , bgDefaultColor: true } ) ; | ||
@@ -1636,0 +1656,0 @@ |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -32,2 +32,3 @@ The MIT License (MIT) | ||
const ScreenBuffer = require( './ScreenBuffer.js' ) ; | ||
const misc = require( './misc.js' ) ; | ||
@@ -843,4 +844,11 @@ const fs = require( 'fs' ) ; | ||
if ( object.color && typeof object.color === 'object' ) { | ||
// Color part | ||
// Color part | ||
if ( typeof object.color === 'string' ) { | ||
let color = misc.hexToRgba( object.color ) ; | ||
attr[ BPOS_R ] = color.r ; | ||
attr[ BPOS_G ] = color.g ; | ||
attr[ BPOS_B ] = color.b ; | ||
attr[ BPOS_A ] = color.a ; | ||
} | ||
else if ( object.color && typeof object.color === 'object' ) { | ||
attr[ BPOS_R ] = + object.color.r || 0 ; | ||
@@ -858,4 +866,11 @@ attr[ BPOS_G ] = + object.color.g || 0 ; | ||
if ( object.bgColor && typeof object.bgColor === 'object' ) { | ||
// Background color part | ||
// Background color part | ||
if ( typeof object.bgColor === 'string' ) { | ||
let color = misc.hexToRgba( object.bgColor ) ; | ||
attr[ BPOS_BG_R ] = color.r ; | ||
attr[ BPOS_BG_G ] = color.g ; | ||
attr[ BPOS_BG_B ] = color.b ; | ||
attr[ BPOS_BG_A ] = color.a ; | ||
} | ||
else if ( object.bgColor && typeof object.bgColor === 'object' ) { | ||
attr[ BPOS_BG_R ] = + object.bgColor.r || 0 ; | ||
@@ -950,5 +965,4 @@ attr[ BPOS_BG_G ] = + object.bgColor.g || 0 ; | ||
// Add the selection flag, i.e. the INVERSE flag | ||
ScreenBufferHD.attrSelect = ScreenBufferHD.prototype.attrSelect = attr => { attr[ BPOS_STYLE ] |= INVERSE ; return attr ; } ; | ||
ScreenBufferHD.attrUnselect = ScreenBufferHD.prototype.attrUnselect = attr => { attr[ BPOS_STYLE ] &= ~ INVERSE ; return attr ; } ; | ||
// Used by TextBuffer for selection | ||
ScreenBufferHD.attrInverse = ScreenBufferHD.prototype.attrInverse = attr => { attr[ BPOS_STYLE ] ^= INVERSE ; return attr ; } ; | ||
@@ -1035,2 +1049,3 @@ | ||
ScreenBufferHD.DEFAULT_ATTR = // <- used by TextBuffer | ||
ScreenBufferHD.prototype.DEFAULT_ATTR = ScreenBufferHD.object2attr( { | ||
@@ -1037,0 +1052,0 @@ color: { |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -42,2 +42,3 @@ The MIT License (MIT) | ||
password: '●' , // Currently: the same as blackCircle | ||
ellispsis: '…' , | ||
@@ -103,2 +104,15 @@ forwardSingleQuote: '´' , // Altgr + , | ||
box: { | ||
__fix__: object => ( { | ||
vertical: object.vertical || ' ' , | ||
horizontal: object.horizontal || ' ' , | ||
topLeft: object.topLeft || ' ' , | ||
topRight: object.topRight || ' ' , | ||
bottomLeft: object.bottomLeft || ' ' , | ||
bottomRight: object.bottomRight || ' ' , | ||
topTee: object.topTee || ' ' , | ||
bottomTee: object.bottomTee || ' ' , | ||
leftTee: object.leftTee || ' ' , | ||
rightTee: object.rightTee || ' ' , | ||
cross: object.cross || ' ' | ||
} ) , | ||
plain: { | ||
@@ -105,0 +119,0 @@ vertical: '█' , |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -41,3 +41,3 @@ The MIT License (MIT) | ||
LEFT: '\x1b[D' , | ||
DELETE: '\x7f' , | ||
BACKSPACE: '\x7f' , | ||
SHIFT_F1: '\x1b[1;2P' , | ||
@@ -44,0 +44,0 @@ SHIFT_F2: '\x1b[1;2Q' , |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -645,2 +645,6 @@ The MIT License (MIT) | ||
ALT_LEFT: '\x1b[1;3D' , | ||
ALT_SHIFT_UP: '\x1b[1;4A' , | ||
ALT_SHIFT_DOWN: '\x1b[1;4B' , | ||
ALT_SHIFT_RIGHT: '\x1b[1;4C' , | ||
ALT_SHIFT_LEFT: '\x1b[1;4D' , | ||
CTRL_UP: '\x1b[1;5A' , | ||
@@ -650,2 +654,6 @@ CTRL_DOWN: '\x1b[1;5B' , | ||
CTRL_LEFT: '\x1b[1;5D' , | ||
CTRL_SHIFT_UP: '\x1b[1;6A' , | ||
CTRL_SHIFT_DOWN: '\x1b[1;6B' , | ||
CTRL_SHIFT_RIGHT: '\x1b[1;6C' , | ||
CTRL_SHIFT_LEFT: '\x1b[1;6D' , | ||
@@ -652,0 +660,0 @@ //BACKSPACE: '\x7f' , |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -42,3 +42,3 @@ The MIT License (MIT) | ||
/* | ||
Since there is a lot of hack with the Terminal instance creation, we can't use the 'new' operator at all... | ||
Since there is a lot of hacks with the Terminal instance creation, we can't use the 'new' operator at all... | ||
*/ | ||
@@ -99,2 +99,4 @@ function Terminal( ... args ) { return Terminal.create( ... args ) ; } | ||
term.prependStdinChunk = null ; | ||
term.metaKeyPrefix = null ; | ||
term.metaKeyRemove = null ; | ||
@@ -264,3 +266,2 @@ term.lock = {} ; | ||
Object.keys( term.esc ).forEach( ( key ) => { | ||
if ( ! term.esc[ key ] || typeof term.esc[ key ] !== 'object' ) { | ||
@@ -406,5 +407,83 @@ console.error( "Bad escape sequence entry '" + key + "' using termconfig: '" + term.termconfigFile + "'." ) ; | ||
} | ||
} , | ||
dataMarkup: { | ||
fg: ( markupStack , key , value ) => { | ||
var str ; | ||
if ( typeof value === 'string' && value[ 0 ] === '#' ) { | ||
let hex = value.slice( 1 ) ; | ||
if ( hex.length === 3 ) { hex = hex[ 0 ] + hex[ 0 ] + hex[ 1 ] + hex[ 1 ] + hex[ 2 ] + hex[ 2 ] ; } | ||
let r = parseInt( hex.slice( 0 , 2 ) , 16 ) || 0 ; | ||
let g = parseInt( hex.slice( 2 , 4 ) , 16 ) || 0 ; | ||
let b = parseInt( hex.slice( 4 , 6 ) , 16 ) || 0 ; | ||
str = term.optimized.color24bits( r , g , b ) ; | ||
} | ||
else { | ||
str = term.str.color( value ) ; | ||
} | ||
markupStack.push( str ) ; | ||
return str ; | ||
} , | ||
bg: ( markupStack , key , value ) => { | ||
var str ; | ||
if ( typeof value === 'string' && value[ 0 ] === '#' ) { | ||
let hex = value.slice( 1 ) ; | ||
if ( hex.length === 3 ) { hex = hex[ 0 ] + hex[ 0 ] + hex[ 1 ] + hex[ 1 ] + hex[ 2 ] + hex[ 2 ] ; } | ||
let r = parseInt( hex.slice( 0 , 2 ) , 16 ) || 0 ; | ||
let g = parseInt( hex.slice( 2 , 4 ) , 16 ) || 0 ; | ||
let b = parseInt( hex.slice( 4 , 6 ) , 16 ) || 0 ; | ||
str = term.optimized.bgColor24bits( r , g , b ) ; | ||
} | ||
else { | ||
str = term.str.bgColor( value ) ; | ||
} | ||
markupStack.push( str ) ; | ||
return str ; | ||
} | ||
} , | ||
markupCatchAll: ( markupStack , key , value ) => { | ||
term.optimized.bgColor24bits ; | ||
var str = '' ; | ||
if ( value === undefined ) { | ||
if ( key[ 0 ] === '#' ) { | ||
return term.formatConfig.dataMarkup.fg( markupStack , 'fg' , key ) ; | ||
} | ||
else if ( termkit.markupCatchAllKeywords[ key ] ) { | ||
switch ( termkit.markupCatchAllKeywords[ key ][ 0 ] ) { | ||
case 'color' : | ||
return term.formatConfig.dataMarkup.fg( markupStack , 'fg' , termkit.markupCatchAllKeywords[ key ][ 1 ] ) ; | ||
case 'bgColor' : | ||
return term.formatConfig.dataMarkup.bg( markupStack , 'bg' , termkit.markupCatchAllKeywords[ key ][ 1 ] ) ; | ||
case 'dim' : | ||
str = term.str.dim() ; | ||
break ; | ||
case 'bold' : | ||
str = term.str.bold() ; | ||
break ; | ||
case 'underline' : | ||
str = term.str.underline() ; | ||
break ; | ||
case 'italic' : | ||
str = term.str.italic() ; | ||
break ; | ||
case 'inverse' : | ||
str = term.str.inverse() ; | ||
break ; | ||
} | ||
} | ||
} | ||
markupStack.push( str ) ; | ||
return str ; | ||
} | ||
} ; | ||
// Aliases... | ||
term.formatConfig.dataMarkup.color = term.formatConfig.dataMarkup.fgColor = term.formatConfig.dataMarkup.c = term.formatConfig.dataMarkup.fg ; | ||
term.formatConfig.dataMarkup.bgColor = term.formatConfig.dataMarkup.bg ; | ||
term.formatConfig.rawMarkupConfig = Object.create( term.formatConfig ) ; | ||
@@ -525,3 +604,3 @@ term.formatConfig.rawMarkupConfig.startingMarkupReset = false ; | ||
// CAUTION: 'options' MUST NOT BE OVERWRITTEN! | ||
// It is binded at the function creation and contains function specificities! | ||
// It is bound at the function creation and contains function specificities! | ||
function applyEscape( options , ... args ) { | ||
@@ -940,2 +1019,6 @@ var fn , newOptions , wrapOptions ; | ||
} | ||
else if ( ! this.isTTY ) { | ||
// If it's not a TTY, we are probably piping to a file, in that case the size is virtually infinite | ||
this.width = this.height = Infinity ; | ||
} | ||
@@ -1114,3 +1197,3 @@ this.emit( 'resize' , this.width , this.height ) ; | ||
bytes = i ; | ||
this.emit( 'key' , keymap.name , keymap.matches , { isCharacter: false , code: startBuffer } ) ; | ||
this.emitKey( keymap.name , keymap.matches , { isCharacter: false , code: startBuffer } ) ; | ||
} | ||
@@ -1186,3 +1269,3 @@ | ||
this.emit( 'key' , char , [ char ] , { isCharacter: true , codepoint: codepoint , code: buffer } ) ; | ||
this.emitKey( char , [ char ] , { isCharacter: true , codepoint: codepoint , code: buffer } ) ; | ||
} | ||
@@ -1192,3 +1275,3 @@ else { | ||
char = String.fromCharCode( chunk[ index ] ) ; | ||
this.emit( 'key' , char , [ char ] , { isCharacter: true , codepoint: chunk[ index ] , code: chunk[ index ] } ) ; | ||
this.emitKey( char , [ char ] , { isCharacter: true , codepoint: chunk[ index ] , code: chunk[ index ] } ) ; | ||
} | ||
@@ -1209,2 +1292,41 @@ | ||
// Internal? | ||
notChainable.emitKey = function( name , matches , data ) { | ||
if ( this.metaKeyPrefix ) { | ||
data.meta = this.metaKeyPrefix ; | ||
data.isCharacter = false ; | ||
if ( this.metaKeyRemove ) { | ||
let regexp = new RegExp( '(^|_)' + this.metaKeyRemove + '_' ) ; | ||
// We force using a match that contains the key to remove, | ||
// e.g.: META + RETURN/CTRL_M would produce META_M instead of META_RETURN | ||
if ( matches.length > 1 ) { | ||
let index = matches.findIndex( oneMatch => oneMatch.match( regexp ) ) ; | ||
regexp.lastIndex = 0 ; | ||
if ( index >= 0 ) { name = matches[ index ] ; } | ||
} | ||
name = name.replace( regexp , '$1' ) ; | ||
} | ||
name = this.metaKeyPrefix + '_' + termkit.characterName( name ) ; | ||
this.metaKeyPrefix = null ; | ||
this.metaKeyRemove = null ; | ||
} | ||
this.emit( 'key' , name , matches , data ) ; | ||
} ; | ||
// TODOC | ||
notChainable.setMetaKeyPrefix = function( prefix , remove ) { | ||
this.metaKeyPrefix = prefix && typeof prefix === 'string' ? prefix : null ; | ||
this.metaKeyRemove = remove && typeof remove === 'string' ? remove : null ; | ||
} ; | ||
/* | ||
@@ -1237,3 +1359,3 @@ * options `false` or `Object` where: | ||
var disable = () => { | ||
// Very important: removing all listeners don't switch back to pause mode. | ||
// Very important: removing all listeners doesn't switch back to pause mode. | ||
// This is some nasty Node.js quirks (the documentation pleads for backward compatibility). | ||
@@ -1240,0 +1362,0 @@ this.stdin.pause() ; |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -103,3 +103,3 @@ The MIT License (MIT) | ||
function parseTermBuffer( bufPair , termName ) { | ||
var i , j , getInt , intSize , | ||
var i , getInt , intSize , | ||
buf = bufPair.buf , | ||
@@ -106,0 +106,0 @@ offset = 0 ; |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -31,2 +31,12 @@ The MIT License (MIT) | ||
const path = require( 'path' ) ; | ||
if ( process.browser || require.cache[ path.join( __dirname , 'termkit-no-lazy-require.js' ) ] ) { | ||
console.log( 'using termkit-no-lazy-require.js' ) ; | ||
module.exports = require( './termkit-no-lazy-require.js' ) ; | ||
return ; | ||
} | ||
const termkit = {} ; | ||
@@ -90,2 +100,4 @@ module.exports = termkit ; | ||
InlineInput: './document/InlineInput.js' , | ||
InlineFileInput: './document/InlineFileInput.js' , | ||
InlineMenu: './document/InlineMenu.js' , | ||
Form: './document/Form.js' , | ||
@@ -95,2 +107,3 @@ RowMenu: './document/RowMenu.js' , | ||
ColumnMenuMulti: './document/ColumnMenuMulti.js' , | ||
ColumnMenuMixed: './document/ColumnMenuMixed.js' , | ||
SelectList: './document/SelectList.js' , | ||
@@ -101,2 +114,3 @@ SelectListMulti: './document/SelectListMulti.js' , | ||
Layout: './document/Layout.js' , | ||
Border: './document/Border.js' , | ||
Window: './document/Window.js' , | ||
@@ -103,0 +117,0 @@ |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -31,2 +31,4 @@ The MIT License (MIT) | ||
const misc = require( './misc.js' ) ; | ||
const fs = require( 'fs' ) ; | ||
@@ -71,3 +73,3 @@ const string = require( 'string-kit' ) ; | ||
this.tabWidth = options.tabWidth || 4 ; | ||
this.tabWidth = + options.tabWidth || 4 ; | ||
this.forceInBound = !! options.forceInBound ; | ||
@@ -103,9 +105,13 @@ | ||
TextBuffer.prototype.parseMarkup = string.markupMethod.bind( misc.markupOptions ) ; | ||
function Cell( char = ' ' , attr = null , misc = null ) { | ||
this.char = char || ' ' ; | ||
this.filler = char === null ; | ||
// Special: if positive or 0, it's the width of the char, if -1 it's an anti-filler, if -2 it's a filler | ||
function Cell( char = ' ' , special = 1 , attr = null , misc_ = null ) { | ||
this.char = char ; | ||
this.width = special >= 0 ? special : -special - 1 ; | ||
this.filler = special < 0 ; // note: antiFiller ARE filler | ||
this.attr = attr ; | ||
this.misc = misc ; | ||
this.misc = misc_ ; | ||
} | ||
@@ -128,2 +134,103 @@ | ||
// TODOC | ||
TextBuffer.prototype.getLineText = function( y = this.cy ) { | ||
if ( y >= this.buffer.length ) { return null ; } | ||
if ( ! this.buffer[ y ] ) { this.buffer[ y ] = [] ; } | ||
return string.unicode.fromCells( this.buffer[ y ] ) ; | ||
} ; | ||
// TODOC | ||
// Get the indentation part of the line, return null if the line is empty (no char or no non-space char) | ||
TextBuffer.prototype.getLineIndent = function( y = this.cy ) { | ||
if ( ! this.buffer[ y ] ) { return null ; } | ||
var x , xmin , xmax , cell , | ||
indent = '' ; | ||
for ( x = 0 , xmax = this.buffer[ y ].length - 1 ; x <= xmax ; x ++ ) { | ||
cell = this.buffer[ y ][ x ] ; | ||
if ( ! cell.filler ) { | ||
if ( cell.char === '\t' || cell.char === ' ' ) { | ||
indent += cell.char ; | ||
} | ||
else if ( cell.char === '\n' ) { | ||
return null ; | ||
} | ||
else { | ||
return indent ; | ||
} | ||
} | ||
} | ||
return null ; | ||
} ; | ||
// TODOC | ||
// Count characters in this line, excluding fillers | ||
TextBuffer.prototype.getLineCharCount = function( y = this.cy ) { | ||
if ( y >= this.buffer.length ) { return null ; } | ||
if ( ! this.buffer[ y ] ) { this.buffer[ y ] = [] ; } | ||
return this.getCellsCharCount( this.buffer[ y ] ) ; | ||
} ; | ||
// internal | ||
TextBuffer.prototype.getCellsCharCount = function( cells ) { | ||
var count = 0 ; | ||
for ( let cell of cells ) { | ||
if ( ! cell.filler ) { count ++ ; } | ||
} | ||
return count ; | ||
} ; | ||
// TODOC | ||
// Remove spaces and tabs at the end of the line | ||
TextBuffer.prototype.removeTrailingSpaces = function( y = this.cy , x = null , dry = false ) { | ||
if ( y >= this.buffer.length ) { return '' ; } | ||
if ( ! this.buffer[ y ] ) { this.buffer[ y ] = [] ; } | ||
var line = this.buffer[ y ] ; | ||
x = x ?? line.length - 1 ; | ||
if ( x < 0 || x >= line.length ) { return '' ; } | ||
var deletedStr = '' , | ||
hasNL = line[ x ].char === '\n' ; | ||
if ( hasNL ) { | ||
x -- ; | ||
} | ||
for ( ; x >= 0 ; x -- ) { | ||
if ( line[ x ].filler ) { continue ; } | ||
let char = line[ x ].char ; | ||
if ( char === ' ' || char === '\t' ) { | ||
deletedStr = char + deletedStr ; | ||
} | ||
else { | ||
break ; | ||
} | ||
} | ||
if ( deletedStr && ! dry ) { | ||
line.splice( x + 1 , deletedStr.length ) ; | ||
} | ||
return deletedStr ; | ||
} ; | ||
// TODOC | ||
// Get the text, but separate before the cursor and after the cursor | ||
@@ -160,6 +267,10 @@ TextBuffer.prototype.getCursorSplittedText = function() { | ||
var parser = ! hasMarkup ? null : | ||
hasMarkup === 'ansi' ? termkit.parseAnsi : | ||
termkit.parseMarkup ; | ||
var legacyColor = false , parser = null ; | ||
switch ( hasMarkup ) { | ||
case 'ansi' : parser = string.ansi.parse ; break ; | ||
case 'legacyAnsi' : parser = string.ansi.parse ; legacyColor = true ; break ; | ||
case true : parser = this.parseMarkup ; break ; | ||
} | ||
if ( baseAttr === undefined ) { baseAttr = this.defaultAttr ; } | ||
@@ -173,7 +284,7 @@ if ( typeof baseAttr === 'object' ) { baseAttr = this.object2attr( baseAttr ) ; } | ||
var index = this.buffer.length ; | ||
this.buffer[ index ] = this.lineToCells( line , parser , baseAttr , 0 ) ; | ||
this.buffer[ index ] = this.lineToCells( line , parser , baseAttr , 0 , legacyColor ) ; | ||
// /!\ Warning /!\ string.unicode.toCells() strips '\n', so we need to restore it at the end of the line | ||
if ( line[ line.length - 1 ] === '\n' ) { | ||
this.buffer[ index ].push( new Cell( '\n' , baseAttr ) ) ; | ||
this.buffer[ index ].push( new Cell( '\n' , 1 , baseAttr ) ) ; | ||
} | ||
@@ -184,2 +295,4 @@ | ||
} ) ; | ||
this.selectionRegion = null ; | ||
} ; | ||
@@ -190,37 +303,25 @@ | ||
// Internal, transform a line of text, with or without markup to cells... | ||
TextBuffer.prototype.lineToCells = function( line , parser , baseAttr , offset = 0 ) { | ||
var attr = baseAttr , | ||
attrObject , cells ; | ||
TextBuffer.prototype.lineToCells = function( line , parser , baseAttr , offset = 0 , legacyColor = false ) { | ||
if ( ! parser ) { | ||
return string.unicode.toCells( Cell , line , this.tabWidth , offset , attr ) ; | ||
return string.unicode.toCells( Cell , line , this.tabWidth , offset , baseAttr ) ; | ||
} | ||
// Reset attr at each end of line | ||
attr = baseAttr ; | ||
attrObject = this.ScreenBuffer.attr2object( attr ) ; | ||
cells = [] ; | ||
var attr , attrObject , | ||
cells = [] ; | ||
parser( line , termkit.markupOptions ).forEach( part => { | ||
if ( typeof part === 'string' ) { | ||
cells.push( ... string.unicode.toCells( Cell , part , this.tabWidth , offset + cells.length , attr ) ) ; | ||
return ; | ||
} | ||
const defaultAttrObject = this.ScreenBuffer.attr2object( this.ScreenBuffer.DEFAULT_ATTR ) ; | ||
const baseAttrObject = this.ScreenBuffer.attr2object( baseAttr ) ; | ||
if ( part.markup.reset ) { | ||
attr = baseAttr ; | ||
attrObject = this.ScreenBuffer.attr2object( attr ) ; | ||
} | ||
else { | ||
Object.assign( attrObject , part.markup ) ; | ||
parser( line ).forEach( part => { | ||
attrObject = Object.assign( {} , part.specialReset ? defaultAttrObject : baseAttrObject , part ) ; | ||
delete attrObject.text ; | ||
// Remove incompatible flags | ||
if ( attrObject.defaultColor && attrObject.color !== undefined ) { delete attrObject.defaultColor ; } | ||
if ( attrObject.bgDefaultColor && attrObject.bgColor !== undefined ) { delete attrObject.bgDefaultColor ; } | ||
// Remove incompatible flags | ||
if ( attrObject.defaultColor && attrObject.color ) { delete attrObject.defaultColor ; } | ||
if ( attrObject.bgDefaultColor && attrObject.bgColor ) { delete attrObject.bgDefaultColor ; } | ||
attr = this.object2attr( attrObject ) ; | ||
} | ||
attr = this.object2attr( attrObject , undefined , legacyColor ) ; | ||
if ( part.markup.raw ) { | ||
cells.push( ... string.unicode.toCells( Cell , part.markup.raw , this.tabWidth , offset + cells.length , attr ) ) ; | ||
if ( part.text ) { | ||
cells.push( ... string.unicode.toCells( Cell , part.text , this.tabWidth , offset + cells.length , attr ) ) ; | ||
} | ||
@@ -262,7 +363,7 @@ } ) ; | ||
// Cursor offset in the text-content (excluding fillers) | ||
TextBuffer.prototype.getCursorOffset = function() { | ||
// TODOC | ||
TextBuffer.prototype.coordinateToOffset = function( px , py ) { | ||
var x , y , line , offset = 0 ; | ||
for ( y = 0 ; y < this.cy ; y ++ ) { | ||
for ( y = 0 ; y < py ; y ++ ) { | ||
line = this.buffer[ y ] ; | ||
@@ -275,5 +376,5 @@ if ( ! line ) { continue ; } | ||
line = this.buffer[ this.cy ] ; | ||
line = this.buffer[ py ] ; | ||
if ( line ) { | ||
for ( x = 0 ; x < this.cx && x < line.length ; x ++ ) { | ||
for ( x = 0 ; x < px && x < line.length ; x ++ ) { | ||
if ( ! line[ x ].filler ) { offset ++ ; } | ||
@@ -288,41 +389,46 @@ } | ||
// Set the cursor position (cx,cy) depending on the offset in the text-content (excludind fillers) | ||
TextBuffer.prototype.setCursorOffset = function( offset ) { | ||
var line ; | ||
// Cursor offset in the text-content (excluding fillers) | ||
TextBuffer.prototype.getCursorOffset = function() { | ||
return this.coordinateToOffset( this.cx , this.cy ) ; | ||
} ; | ||
//console.error( "Entering" , offset ) ; | ||
this.cy = this.cx = 0 ; | ||
if ( offset <= 0 ) { return ; } | ||
while ( this.cy < this.buffer.length ) { | ||
this.cx = 0 ; | ||
line = this.buffer[ this.cy ] ; | ||
//console.error( " iter cy" , offset , this.cy , this.cx , "---" , line.length ) ; | ||
// TODOC | ||
TextBuffer.prototype.offsetToCoordinate = function( offset ) { | ||
var line , | ||
x = 0 , | ||
y = 0 ; | ||
if ( offset < 0 ) { return ; } | ||
while ( y < this.buffer.length ) { | ||
x = 0 ; | ||
line = this.buffer[ y ] ; | ||
//console.error( " iter cy" , offset , y , x , "---" , line.length ) ; | ||
if ( ! line ) { continue ; } | ||
while ( this.cx < line.length ) { | ||
//console.error( " iter cx" , offset , this.cy , this.cx ) ; | ||
if ( line[ this.cx ].filler ) { | ||
this.cx ++ ; | ||
} | ||
else { | ||
offset -- ; | ||
this.cx ++ ; | ||
while ( x < line.length ) { | ||
//console.error( " iter cx" , offset , y , x ) ; | ||
if ( ! line[ x ].filler ) { | ||
if ( offset <= 0 ) { | ||
if ( this.cx === line.length && line[ line.length - 1 ].char === '\n' ) { | ||
if ( x === line.length && line[ line.length - 1 ].char === '\n' ) { | ||
//console.error( " Exit with \\n" ) ; | ||
this.cx = 0 ; | ||
this.cy ++ ; | ||
x = 0 ; | ||
y ++ ; | ||
} | ||
//console.error( "Exit" , this.cy , this.cx ) ; | ||
return ; | ||
//console.error( "Exit" , y , x ) ; | ||
return { x , y } ; | ||
} | ||
offset -- ; | ||
} | ||
x ++ ; | ||
} | ||
this.cy ++ ; | ||
y ++ ; | ||
} | ||
//console.error( "End of input" , offset , this.cy , this.cx ) ; | ||
//console.error( "End of input" , offset , y , x ) ; | ||
} ; | ||
@@ -332,10 +438,37 @@ | ||
// Set the cursor position (cx,cy) depending on the offset in the text-content (excluding fillers) | ||
TextBuffer.prototype.setCursorOffset = function( offset ) { | ||
var coord = this.offsetToCoordinate() ; | ||
if ( ! coord ) { return ; } | ||
this.cx = coord.x ; | ||
this.cy = coord.y ; | ||
} ; | ||
// TODOC | ||
TextBuffer.prototype.setTabWidth = function( tabWidth ) { | ||
this.tabWidth = + tabWidth || 4 ; | ||
this.reTab() ; | ||
} ; | ||
// TODOC | ||
TextBuffer.prototype.reTab = function() { | ||
for ( let y = 0 ; y < this.buffer.length ; y ++ ) { | ||
this.reTabLine( 0 , y ) ; | ||
} | ||
} ; | ||
// Recompute tabs | ||
TextBuffer.prototype.reTabLine = function( startAt = 0 ) { | ||
TextBuffer.prototype.reTabLine = function( startAt = 0 , y = this.cy ) { | ||
var length , cell , index , fillSize , input , output , | ||
linePosition = startAt ; | ||
if ( this.buffer[ this.cy ] === undefined ) { this.buffer[ this.cy ] = [] ; } | ||
if ( this.buffer[ y ] === undefined ) { this.buffer[ y ] = [] ; } | ||
input = this.buffer[ this.cy ] ; | ||
input = this.buffer[ y ] ; | ||
output = input.slice( 0 , startAt ) ; | ||
@@ -353,5 +486,3 @@ length = input.length ; | ||
while ( fillSize -- ) { | ||
// /!\ First or second? | ||
//output.push( new Cell( null ) ) ; | ||
output.push( new Cell( null , cell.attr , cell.misc ) ) ; | ||
output.push( new Cell( ' ' , -2 , cell.attr , cell.misc ) ) ; | ||
} | ||
@@ -368,3 +499,3 @@ | ||
this.buffer[ this.cy ] = output ; | ||
this.buffer[ y ] = output ; | ||
} ; | ||
@@ -625,3 +756,3 @@ | ||
if ( ! this.buffer[ y ][ x ] ) { this.buffer[ y ][ x ] = new Cell( ' ' , attr ) ; } | ||
if ( ! this.buffer[ y ][ x ] ) { this.buffer[ y ][ x ] = new Cell( ' ' , 1 , attr ) ; } | ||
else { this.buffer[ y ][ x ].attr = attr ; } | ||
@@ -666,8 +797,21 @@ } ; | ||
TextBuffer.prototype.isInSelection = function( x = this.cx , y = this.cy ) { | ||
if ( ! this.selectionRegion ) { return false ; } | ||
return this.isInRegion( this.selectionRegion , x , y ) ; | ||
} ; | ||
TextBuffer.prototype.isInRegion = function( region , x = this.cx , y = this.cy ) { | ||
return ( | ||
y >= region.ymin && y <= region.ymax | ||
&& ( y !== region.ymin || x >= region.xmin ) | ||
&& ( y !== region.ymax || x <= region.xmax ) | ||
) ; | ||
} ; | ||
TextBuffer.prototype.setSelectionRegion = function( region ) { | ||
if ( this.selectionRegion ) { | ||
// Start by unhilighting existing selection | ||
this.hilightSelection( false ) ; | ||
} | ||
else { | ||
if ( ! this.selectionRegion ) { | ||
this.selectionRegion = {} ; | ||
@@ -677,4 +821,8 @@ } | ||
if ( region.xmin !== undefined && region.ymin !== undefined ) { | ||
if ( region.xmin < 0 ) { region.xmin = 0 ; } | ||
if ( region.ymin < 0 ) { region.ymin = 0 ; } | ||
this.selectionRegion.xmin = region.xmin ; | ||
this.selectionRegion.ymin = region.ymin ; | ||
this.selectionRegion.cellMin = this.buffer[ region.ymin ]?.[ region.xmin ] ?? null ; | ||
} | ||
@@ -685,5 +833,17 @@ | ||
this.selectionRegion.ymax = region.ymax ; | ||
this.selectionRegion.cellMax = this.buffer[ region.ymax ]?.[ region.xmax ] ?? null ; | ||
} | ||
} ; | ||
this.hilightSelection() ; | ||
// TODOC | ||
TextBuffer.prototype.startOfSelection = function() { | ||
if ( ! this.selectionRegion ) { | ||
this.selectionRegion = {} ; | ||
} | ||
this.selectionRegion.xmin = this.cx ; | ||
this.selectionRegion.ymin = this.cy ; | ||
this.selectionRegion.cellMin = this.buffer[ this.cy ]?.[ this.cx ] ?? null ; | ||
} ; | ||
@@ -693,8 +853,19 @@ | ||
TextBuffer.prototype.resetSelectionRegion = function() { | ||
if ( ! this.selectionRegion ) { return ; } | ||
// TODOC | ||
TextBuffer.prototype.endOfSelection = function() { | ||
var coord = this.oneStepBackward() ; | ||
// Start by unhilighting existing selection | ||
this.hilightSelection( false ) ; | ||
this.selectionRegion = null ; | ||
if ( ! this.selectionRegion ) { | ||
this.selectionRegion = {} ; | ||
} | ||
if ( ! coord ) { | ||
// Start of the file | ||
this.selectionRegion = null ; | ||
return ; | ||
} | ||
this.selectionRegion.xmax = coord.x ; | ||
this.selectionRegion.ymax = coord.y ; | ||
this.selectionRegion.cellMax = this.buffer[ coord.y ]?.[ coord.x ] ?? null ; | ||
} ; | ||
@@ -704,25 +875,76 @@ | ||
// Internal | ||
TextBuffer.prototype.hilightSelection = function( turnOn = true ) { | ||
var x , y , xmin , xmax , ymax , | ||
region = this.selectionRegion ; | ||
// TODOC | ||
// Reset the region by scanning for the starting and ending cell | ||
// If cursorCell is set, set cursor position to this cell | ||
TextBuffer.prototype.updateSelectionFromCells = function( cursorCell = null ) { | ||
if ( ! this.selectionRegion ) { return ; } | ||
if ( ! this.selectionRegion.cellMin || ! this.selectionRegion.cellMax ) { | ||
this.selectionRegion = null ; | ||
return ; | ||
} | ||
if ( ! region || region.xmin === undefined || region.ymin === undefined || region.xmax === undefined || region.ymax === undefined ) { | ||
var xmin , xmax , ymin , ymax ; | ||
for ( let y = 0 ; y < this.buffer.length ; y ++ ) { | ||
let currentLine = this.buffer[ y ] ; | ||
if ( ! currentLine ) { continue ; } | ||
for ( let x = 0 ; x < currentLine.length ; x ++ ) { | ||
if ( currentLine[ x ] === this.selectionRegion.cellMin ) { | ||
xmin = x ; | ||
ymin = y ; | ||
} | ||
if ( currentLine[ x ] === this.selectionRegion.cellMax ) { | ||
xmax = x ; | ||
ymax = y ; | ||
} | ||
if ( cursorCell && currentLine[ x ] === cursorCell ) { | ||
this.cx = x ; | ||
this.cy = y ; | ||
} | ||
} | ||
} | ||
if ( ymin === undefined || ymax === undefined ) { | ||
this.selectionRegion = null ; | ||
return ; | ||
} | ||
ymax = Math.min( region.ymax , this.buffer.length - 1 ) ; | ||
this.selectionRegion.xmin = xmin ; | ||
this.selectionRegion.xmax = xmax ; | ||
this.selectionRegion.ymin = ymin ; | ||
this.selectionRegion.ymax = ymax ; | ||
} ; | ||
for ( y = region.ymin ; y <= ymax ; y ++ ) { | ||
if ( ! this.buffer[ y ] ) { this.buffer[ y ] = [] ; } | ||
xmin = y === region.ymin ? region.xmin : 0 ; | ||
xmax = y === region.ymax ? Math.min( region.xmax , this.buffer[ y ].length - 1 ) : this.buffer[ y ].length - 1 ; | ||
for ( x = xmin ; x <= xmax ; x ++ ) { | ||
this.buffer[ y ][ x ].attr = turnOn ? | ||
this.ScreenBuffer.attrSelect( this.buffer[ y ][ x ].attr ) : | ||
this.ScreenBuffer.attrUnselect( this.buffer[ y ][ x ].attr ) ; | ||
// TODOC | ||
// Return a Cell instance that is at the cursor location, or null if none | ||
TextBuffer.prototype.getCursorCell = function() { | ||
return this.buffer[ this.cy ]?.[ this.cx ] ?? null ; | ||
} ; | ||
// TODOC | ||
// Return true if found, else return false | ||
TextBuffer.prototype.updateCursorFromCell = function( cursorCell ) { | ||
if ( ! cursorCell ) { return false ; } | ||
for ( let y = 0 ; y < this.buffer.length ; y ++ ) { | ||
let currentLine = this.buffer[ y ] ; | ||
if ( ! currentLine ) { continue ; } | ||
for ( let x = 0 ; x < currentLine.length ; x ++ ) { | ||
if ( cursorCell && currentLine[ x ] === cursorCell ) { | ||
this.cx = x ; | ||
this.cy = y ; | ||
return true ; | ||
} | ||
} | ||
} | ||
return false ; | ||
} ; | ||
@@ -732,9 +954,23 @@ | ||
TextBuffer.prototype.resetSelectionRegion = function() { | ||
if ( ! this.selectionRegion ) { return ; } | ||
this.selectionRegion = null ; | ||
} ; | ||
TextBuffer.prototype.getSelectionText = function() { | ||
return this.getRegionText( this.selectionRegion ) ; | ||
} ; | ||
// TODOC | ||
TextBuffer.prototype.getRegionText = function( region , structured = false ) { | ||
var x , y , xmin , xmax , ymax , cell , | ||
str = '' , | ||
region = this.selectionRegion ; | ||
count = 0 , | ||
str = '' ; | ||
if ( ! region || region.xmin === undefined || region.ymin === undefined || region.xmax === undefined || region.ymax === undefined ) { | ||
return str ; | ||
return ; | ||
} | ||
@@ -752,6 +988,10 @@ | ||
cell = this.buffer[ y ][ x ] ; | ||
str += cell.filler ? '' : cell.char ; | ||
if ( ! cell.filler ) { | ||
str += cell.char ; | ||
count ++ ; | ||
} | ||
} | ||
} | ||
if ( structured ) { return { string: str , count } ; } | ||
return str ; | ||
@@ -762,2 +1002,76 @@ } ; | ||
// TODOC | ||
TextBuffer.prototype.deleteSelection = function( getDeleted = false ) { | ||
if ( ! this.selectionRegion ) { return ; } | ||
var region = this.selectionRegion ; | ||
this.selectionRegion = null ; // unselect now | ||
return this.deleteRegion( region , getDeleted ) ; | ||
} ; | ||
// TODOC | ||
// Delete current line | ||
TextBuffer.prototype.deleteRegion = function( region , getDeleted = false ) { | ||
var x , y , xmin , xmax , ymax , currentLine , tabIndex , deleted , cursorCell ; | ||
if ( ! region || region.xmin === undefined || region.ymin === undefined || region.xmax === undefined || region.ymax === undefined ) { | ||
return ; | ||
} | ||
cursorCell = this.buffer[ this.cy ]?.[ this.cx ] ?? null ; | ||
if ( getDeleted ) { | ||
deleted = this.getRegionText( region , true ) ; | ||
} | ||
ymax = Math.min( region.ymax , this.buffer.length - 1 ) ; | ||
y = region.ymin ; | ||
currentLine = this.buffer[ y ] ; | ||
if ( y === ymax ) { | ||
if ( ! this.buffer[ y ] ) { return deleted ; } | ||
xmin = region.xmin ; | ||
xmax = Math.min( region.xmax , currentLine.length - 1 ) ; | ||
currentLine.splice( xmin , xmax - xmin + 1 ) ; | ||
} | ||
else { | ||
let lastLine = this.buffer[ ymax ] ; | ||
// First, remove next lines | ||
this.buffer.splice( y + 1 , ymax - y ) ; | ||
xmin = region.xmin ; | ||
currentLine.splice( xmin , currentLine.length - xmin ) ; | ||
if ( lastLine && lastLine.length ) { | ||
xmax = Math.min( region.xmax , lastLine.length - 1 ) ; | ||
lastLine.splice( 0 , xmax + 1 ) ; | ||
if ( lastLine.length ) { | ||
currentLine.splice( currentLine.length , 0 , ... lastLine ) ; | ||
} | ||
} | ||
} | ||
if ( y < this.buffer.length - 1 && ( ! currentLine.length || currentLine[ currentLine.length - 1 ].char !== '\n' ) ) { | ||
this.joinLine( true , y ) ; | ||
} | ||
tabIndex = this.indexOfCharInLine( currentLine , '\t' , region.xmin ) ; | ||
if ( tabIndex !== -1 ) { this.reTabLine( tabIndex , y ) ; } | ||
if ( this.selectionRegion ) { | ||
this.updateSelectionFromCells( cursorCell ) ; | ||
} | ||
else if ( cursorCell ) { | ||
this.updateCursorFromCell( cursorCell ) ; | ||
} | ||
return deleted ; | ||
} ; | ||
// Misc data are lazily created | ||
@@ -781,3 +1095,3 @@ TextBuffer.prototype.getMisc = function() { | ||
TextBuffer.prototype.iterate = function( options , callback ) { | ||
var x , y , yMax , offset = 0 , length ; | ||
var x , y , yMax , cell , lastNonFillerCell , offset = 0 , length ; | ||
@@ -792,16 +1106,24 @@ if ( typeof options === 'function' ) { callback = options ; options = {} ; } | ||
length = this.buffer[ y ].length ; | ||
lastNonFillerCell = null ; | ||
for ( x = 0 ; x < length ; x ++ ) { | ||
if ( this.buffer[ y ][ x ].filler ) { continue ; } | ||
cell = this.buffer[ y ][ x ] ; | ||
if ( cell.filler ) { | ||
if ( options.fillerCopyAttr && lastNonFillerCell ) { | ||
cell.attr = lastNonFillerCell.attr ; | ||
} | ||
} | ||
else { | ||
callback( { | ||
offset: offset , | ||
x: x , | ||
y: y , | ||
text: cell.char , | ||
attr: cell.attr , | ||
misc: cell.misc | ||
} ) ; | ||
callback( { | ||
offset: offset , | ||
x: x , | ||
y: y , | ||
text: this.buffer[ y ][ x ].char , | ||
attr: this.buffer[ y ][ x ].attr , | ||
misc: this.buffer[ y ][ x ].misc | ||
} ) ; | ||
offset ++ ; | ||
offset ++ ; | ||
lastNonFillerCell = cell ; | ||
} | ||
} | ||
@@ -827,5 +1149,15 @@ } | ||
// Move to the left to the leading cell of a full-width char | ||
TextBuffer.prototype.moveToLeadingFullWidth = function() { | ||
var currentLine = this.buffer[ this.cy ] ; | ||
while ( this.cx && currentLine?.[ this.cx ]?.filler && currentLine?.[ this.cx ]?.width === 0 ) { this.cx -- ; } | ||
} ; | ||
TextBuffer.prototype.moveTo = function( x , y ) { | ||
this.cx = x >= 0 ? x : 0 ; | ||
this.cy = y >= 0 ? y : 0 ; | ||
if ( this.forceInBound ) { this.moveInBound( true ) ; } | ||
this.moveToLeadingFullWidth() ; | ||
} ; | ||
@@ -844,2 +1176,3 @@ | ||
if ( this.forceInBound ) { this.moveInBound( true ) ; } | ||
this.moveToLeadingFullWidth() ; | ||
} ; | ||
@@ -852,2 +1185,3 @@ | ||
if ( this.forceInBound ) { this.moveInBound( true ) ; } | ||
this.moveToLeadingFullWidth() ; | ||
} ; | ||
@@ -860,2 +1194,3 @@ | ||
if ( this.forceInBound ) { this.moveInBound( true ) ; } | ||
this.moveToLeadingFullWidth() ; | ||
} ; | ||
@@ -867,2 +1202,6 @@ | ||
this.cx ++ ; | ||
var currentLine = this.buffer[ this.cy ] ; | ||
while ( currentLine?.[ this.cx ]?.filler && currentLine?.[ this.cx ]?.width === 0 ) { this.cx ++ ; } | ||
if ( this.forceInBound ) { this.moveInBound( true ) ; } | ||
@@ -895,3 +1234,11 @@ } ; | ||
if ( ! currentLine[ this.cx ] || ( ! currentLine[ this.cx ].filler && ( ! testFn || testFn( currentLine[ this.cx ].char ) ) ) ) { break ; } | ||
if ( | ||
! currentLine[ this.cx ] | ||
|| ( | ||
! currentLine[ this.cx ].filler | ||
&& ( ! testFn || testFn( currentLine[ this.cx ].char , this.cx , this.cy ) ) | ||
) | ||
) { | ||
break ; | ||
} | ||
} | ||
@@ -929,3 +1276,3 @@ | ||
|| ( | ||
( ! currentLine[ this.cx ].filler || currentLine[ this.cx ].char !== '\n' ) | ||
! currentLine[ this.cx ].filler | ||
&& ( ! testFn || testFn( currentLine[ this.cx ].char ) ) | ||
@@ -962,3 +1309,2 @@ ) | ||
return false ; | ||
} ) ; | ||
@@ -1049,5 +1395,6 @@ } ; | ||
TextBuffer.prototype.insert = function( text , hasMarkup , attr ) { | ||
var lines , index , length , parser ; | ||
var lines , index , length , | ||
count = 0 ; | ||
if ( ! text ) { return ; } | ||
if ( ! text ) { return count ; } | ||
@@ -1059,6 +1406,10 @@ if ( typeof hasMarkup !== 'boolean' && typeof hasMarkup !== 'string' ) { | ||
parser = ! hasMarkup ? null : | ||
hasMarkup === 'ansi' ? termkit.parseAnsi : | ||
termkit.parseMarkup ; | ||
var legacyColor = false , parser = null ; | ||
switch ( hasMarkup ) { | ||
case 'ansi' : parser = string.ansi.parse ; break ; | ||
case 'legacyAnsi' : parser = string.ansi.parse ; legacyColor = true ; break ; | ||
case true : parser = this.parseMarkup ; break ; | ||
} | ||
lines = text.split( '\n' ) ; | ||
@@ -1072,8 +1423,13 @@ length = lines.length ; | ||
this.inlineInsert( lines[ 0 ] , parser , attr ) ; | ||
count += this.inlineInsert( lines[ 0 ] , parser , attr ) ; | ||
for ( index = 1 ; index < length ; index ++ ) { | ||
this.newLine( true ) ; | ||
this.inlineInsert( lines[ index ] , parser , attr ) ; | ||
count ++ ; | ||
count += this.inlineInsert( lines[ index ] , parser , attr , legacyColor ) ; | ||
} | ||
if ( this.selectionRegion ) { this.updateSelectionFromCells() ; } | ||
return count ; | ||
} ; | ||
@@ -1099,4 +1455,5 @@ | ||
// Insert inline chars (no control chars) | ||
TextBuffer.prototype.inlineInsert = function( text , parser , attr ) { | ||
var currentLine , currentLineLength , hasNL , nlCell , tabIndex , fillSize , cells ; | ||
TextBuffer.prototype.inlineInsert = function( text , parser , attr , legacyColor = false ) { | ||
var currentLine , currentLineLength , hasNL , nlCell , tabIndex , fillSize , cells , cellsCharCount , | ||
count = 0 ; | ||
@@ -1107,3 +1464,4 @@ this.moveForward( undefined , true ) ; // just skip filler char | ||
//cells = string.unicode.toCells( Cell , text , this.tabWidth , this.cx , attr ) ; | ||
cells = this.lineToCells( text , parser , attr , this.cx ) ; | ||
cells = this.lineToCells( text , parser , attr , this.cx , legacyColor ) ; | ||
cellsCharCount = this.getCellsCharCount( cells ) ; | ||
@@ -1114,3 +1472,4 @@ // Is this a new line? | ||
while ( this.buffer.length < this.cy ) { | ||
this.buffer.push( [ new Cell( '\n' , this.defaultAttr ) ] ) ; | ||
this.buffer.push( [ new Cell( '\n' , 1 , this.defaultAttr ) ] ) ; | ||
count ++ ; | ||
} | ||
@@ -1125,3 +1484,4 @@ | ||
) { | ||
this.buffer[ this.cy - 1 ].push( new Cell( '\n' , this.defaultAttr ) ) ; | ||
this.buffer[ this.cy - 1 ].push( new Cell( '\n' , 1 , this.defaultAttr ) ) ; | ||
count ++ ; | ||
} | ||
@@ -1139,6 +1499,8 @@ | ||
if ( hasNL ) { | ||
currentLine.splice( currentLineLength - 1 , 0 , new Cell( ' ' , this.defaultAttr ) , ... cells ) ; | ||
currentLine.splice( currentLineLength - 1 , 0 , new Cell( ' ' , 1 , this.defaultAttr ) , ... cells ) ; | ||
count += 1 + cellsCharCount ; | ||
} | ||
else { | ||
currentLine.push( ... cells ) ; | ||
count += cellsCharCount ; | ||
} | ||
@@ -1148,2 +1510,3 @@ } | ||
currentLine.splice( this.cx , 0 , ... cells ) ; | ||
count += cellsCharCount ; | ||
} | ||
@@ -1154,9 +1517,21 @@ // this.cx > currentLineLength | ||
nlCell = currentLine.pop() ; | ||
while ( fillSize -- ) { currentLine.push( new Cell( ' ' , this.defaultAttr ) ) ; } | ||
while ( fillSize -- ) { | ||
currentLine.push( new Cell( ' ' , 1 , this.defaultAttr ) ) ; | ||
count ++ ; | ||
} | ||
currentLine.push( ... cells , nlCell ) ; | ||
count += cellsCharCount ; | ||
} | ||
else { | ||
fillSize = this.cx - currentLineLength ; | ||
while ( fillSize -- ) { currentLine.push( new Cell( ' ' , this.defaultAttr ) ) ; } | ||
while ( fillSize -- ) { | ||
currentLine.push( new Cell( ' ' , 1 , this.defaultAttr ) ) ; | ||
count ++ ; | ||
} | ||
currentLine.push( ... cells ) ; | ||
count += cellsCharCount ; | ||
} | ||
@@ -1172,2 +1547,4 @@ | ||
if ( tabIndex !== -1 ) { this.reTabLine( tabIndex ) ; } | ||
return count ; | ||
} ; | ||
@@ -1191,7 +1568,6 @@ | ||
// /!\ Bug with tabs and count > 1 !!! /!\ | ||
// Delete chars | ||
TextBuffer.prototype.delete = function( count ) { | ||
var currentLine , inlineCount ; | ||
TextBuffer.prototype.delete = function( count , getDeleted = false ) { | ||
var currentLine , inlineCount , fillerCount , hasNL , removedCells , | ||
deleted = getDeleted ? { string: '' , count: 0 } : undefined ; | ||
@@ -1212,5 +1588,8 @@ if ( count === undefined ) { count = 1 ; } | ||
// If we are already at the end of the buffer... | ||
if ( this.cy >= this.buffer.length || | ||
( this.cy === this.buffer.length - 1 && this.cx >= currentLine.length ) ) { | ||
return ; | ||
if ( | ||
this.cy >= this.buffer.length || | ||
( this.cy === this.buffer.length - 1 && this.cx >= currentLine.length ) | ||
) { | ||
if ( this.selectionRegion ) { this.updateSelectionFromCells() ; } | ||
return deleted ; | ||
} | ||
@@ -1224,11 +1603,17 @@ | ||
// Compute inline delete | ||
//inlineCount = Math.min( count , currentLine.length - this.cx ) ; | ||
inlineCount = this.countInlineForward( count ) ; | ||
hasNL = currentLine[ currentLine.length - 1 ]?.char === '\n' ; | ||
fillerCount = this.countInlineForwardFiller( count ) ; | ||
inlineCount = Math.min( count + fillerCount , currentLine.length - hasNL - this.cx ) ; | ||
// Apply inline delete | ||
if ( inlineCount > 0 ) { | ||
currentLine.splice( this.cx , inlineCount ) ; | ||
removedCells = currentLine.splice( this.cx , inlineCount ) ; | ||
if ( getDeleted ) { | ||
removedCells = removedCells.filter( cell => ! cell.filler ) ; | ||
deleted.string += removedCells.map( cell => cell.char ).join( '' ) ; | ||
deleted.count += removedCells.length ; | ||
} | ||
} | ||
count -= inlineCount ; | ||
count -= inlineCount - fillerCount ; | ||
} | ||
@@ -1238,3 +1623,9 @@ } | ||
if ( count > 0 ) { | ||
if ( this.joinLine( true ) ) { count -- ; } | ||
if ( this.joinLine( true ) ) { | ||
count -- ; | ||
if ( getDeleted ) { | ||
deleted.string += '\n' ; | ||
deleted.count ++ ; | ||
} | ||
} | ||
} | ||
@@ -1250,2 +1641,6 @@ } | ||
this.reTabLine() ; // Do it every time, before finding a better way to do it | ||
if ( this.selectionRegion ) { this.updateSelectionFromCells() ; } | ||
return deleted ; | ||
} ; | ||
@@ -1255,7 +1650,7 @@ | ||
// /!\ Bug with tabs and count > 1 !!! /!\ | ||
// Delete backward chars | ||
TextBuffer.prototype.backDelete = function( count ) { | ||
var currentLine , inlineCount , tabIndex ; | ||
TextBuffer.prototype.backDelete = function( count , getDeleted = false ) { | ||
//console.error( ">>> backDelete:" , count ) ; | ||
var currentLine , inlineCount , fillerCount , tabIndex , removedCells , | ||
deleted = getDeleted ? { string: '' , count: 0 } : undefined ; | ||
@@ -1276,3 +1671,6 @@ if ( count === undefined ) { count = 1 ; } | ||
// If we are already at the begining of the buffer... | ||
if ( this.cy === 0 && this.cx === 0 ) { return ; } | ||
if ( this.cy === 0 && this.cx === 0 ) { | ||
if ( this.selectionRegion ) { this.updateSelectionFromCells() ; } | ||
return deleted ; | ||
} | ||
@@ -1292,11 +1690,18 @@ if ( currentLine ) { | ||
// Compute inline delete | ||
inlineCount = this.countInlineBackward( count ) ; | ||
fillerCount = this.countInlineBackwardFiller( count ) ; | ||
inlineCount = Math.min( count + fillerCount , this.cx ) ; | ||
//console.error( "inlineCount:" , inlineCount , fillerCount , this.cx , this.cx - inlineCount ) ; | ||
// Apply inline delete | ||
if ( inlineCount > 0 ) { | ||
currentLine.splice( this.cx - inlineCount , inlineCount ) ; | ||
removedCells = currentLine.splice( this.cx - inlineCount , inlineCount ) ; | ||
if ( getDeleted ) { | ||
removedCells = removedCells.filter( cell => ! cell.filler ) ; | ||
deleted.string = removedCells.map( cell => cell.char ).join( '' ) + deleted.string ; | ||
deleted.count += removedCells.length ; | ||
} | ||
this.cx -= inlineCount ; | ||
} | ||
count -= inlineCount ; | ||
count -= inlineCount - fillerCount ; | ||
} | ||
@@ -1307,3 +1712,9 @@ | ||
this.cx = currentLine ? currentLine.length : 0 ; | ||
if ( this.joinLine( true ) ) { count -- ; } | ||
if ( this.joinLine( true ) ) { | ||
count -- ; | ||
if ( getDeleted ) { | ||
deleted.string = '\n' + deleted.string ; | ||
deleted.count ++ ; | ||
} | ||
} | ||
} | ||
@@ -1319,2 +1730,6 @@ } | ||
this.reTabLine( tabIndex ) ; // Do it every time, before finding a better way to do it | ||
if ( this.selectionRegion ) { this.updateSelectionFromCells() ; } | ||
return deleted ; | ||
} ; | ||
@@ -1325,10 +1740,18 @@ | ||
// Fix a backward counter, get an additional count for each null char encountered | ||
TextBuffer.prototype.countInlineBackward = function( count ) { | ||
var c , x ; | ||
TextBuffer.prototype.countInlineBackwardFiller = function( count ) { | ||
var x , cell , | ||
filler = 0 ; | ||
for ( x = this.cx - 1 , c = 0 ; x >= 0 && c < count ; x -- , c ++ ) { | ||
if ( this.buffer[ this.cy ][ x ] && this.buffer[ this.cy ][ x ].filler ) { count ++ ; } | ||
for ( x = this.cx - 1 ; x >= 0 && count ; x -- ) { | ||
cell = this.buffer[ this.cy ][ x ] ; | ||
if ( cell && cell.filler ) { | ||
filler ++ ; | ||
} | ||
else { | ||
count -- ; | ||
} | ||
} | ||
return c ; | ||
return filler ; | ||
} ; | ||
@@ -1339,10 +1762,19 @@ | ||
// Fix a forward counter, get an additional count for each null char encountered | ||
TextBuffer.prototype.countInlineForward = function( count ) { | ||
var c , x , xMax = this.buffer[ this.cy ].length ; | ||
TextBuffer.prototype.countInlineForwardFiller = function( count ) { | ||
var x , cell , | ||
xMax = this.buffer[ this.cy ].length , | ||
filler = 0 ; | ||
for ( x = this.cx , c = 0 ; x < xMax && c < count ; x ++ , c ++ ) { | ||
if ( this.buffer[ this.cy ][ x + 1 ] && this.buffer[ this.cy ][ x + 1 ].filler ) { count ++ ; } | ||
for ( x = this.cx ; x < xMax && count ; x ++ ) { | ||
cell = this.buffer[ this.cy ][ x + 1 ] ; | ||
if ( cell && cell.filler ) { | ||
filler ++ ; | ||
} | ||
else { | ||
count -- ; | ||
} | ||
} | ||
return c ; | ||
return filler ; | ||
} ; | ||
@@ -1368,3 +1800,3 @@ | ||
currentLine.push( new Cell( '\n' , this.defaultAttr ) ) ; | ||
currentLine.push( new Cell( '\n' , 1 , this.defaultAttr ) ) ; | ||
@@ -1383,2 +1815,4 @@ this.buffer.splice( this.cy + 1 , 0 , nextLine ) ; | ||
if ( tabIndex !== -1 ) { this.reTabLine( tabIndex ) ; } | ||
if ( this.selectionRegion ) { this.updateSelectionFromCells() ; } | ||
} | ||
@@ -1389,12 +1823,19 @@ } ; | ||
TextBuffer.prototype.joinLine = function( internalCall ) { | ||
var tabIndex , currentLine , | ||
// If y is specified, we are not joining on current cursor | ||
TextBuffer.prototype.joinLine = function( internalCall , y ) { | ||
var tabIndex , currentLine , x , | ||
updateCursor = false , | ||
hasDeleted = false ; | ||
if ( y === undefined ) { | ||
y = this.cy ; | ||
updateCursor = true ; | ||
} | ||
if ( ! internalCall && this.forceInBound ) { this.moveInBound() ; } | ||
if ( this.buffer[ this.cy ] === undefined ) { this.buffer[ this.cy ] = [] ; } | ||
if ( this.buffer[ this.cy + 1 ] === undefined ) { this.buffer[ this.cy + 1 ] = [] ; } | ||
if ( this.buffer[ y ] === undefined ) { this.buffer[ y ] = [] ; } | ||
if ( this.buffer[ y + 1 ] === undefined ) { this.buffer[ y + 1 ] = [] ; } | ||
currentLine = this.buffer[ this.cy ] ; | ||
currentLine = this.buffer[ y ] ; | ||
@@ -1407,7 +1848,8 @@ if ( currentLine.length && currentLine[ currentLine.length - 1 ].char === '\n' ) { | ||
this.cx = currentLine.length ; | ||
x = currentLine.length ; | ||
if ( updateCursor ) { this.cx = x ; } | ||
currentLine.splice( currentLine.length , 0 , ... this.buffer[ this.cy + 1 ] ) ; | ||
currentLine.splice( currentLine.length , 0 , ... this.buffer[ y + 1 ] ) ; | ||
this.buffer.splice( this.cy + 1 , 1 ) ; | ||
this.buffer.splice( y + 1 , 1 ) ; | ||
@@ -1419,4 +1861,6 @@ // Patch tab if needed | ||
tabIndex = this.indexOfCharInLine( currentLine , '\t' , this.cx ) ; | ||
if ( tabIndex !== -1 ) { this.reTabLine( tabIndex ) ; } | ||
tabIndex = this.indexOfCharInLine( currentLine , '\t' , x ) ; | ||
if ( tabIndex !== -1 ) { this.reTabLine( tabIndex , y ) ; } | ||
if ( this.selectionRegion ) { this.updateSelectionFromCells() ; } | ||
} | ||
@@ -1429,2 +1873,108 @@ | ||
// TODOC | ||
// Delete current line | ||
TextBuffer.prototype.deleteLine = function( getDeleted = false ) { | ||
var currentLine , inlineCount , fillerCount , hasNL , removedCells , deleted ; | ||
if ( this.forceInBound ) { this.moveInBound() ; } | ||
if ( this.cy >= this.buffer.length ) { return ; } | ||
if ( getDeleted ) { | ||
deleted = { | ||
count: this.getLineCharCount() , | ||
string: this.getLineText() | ||
} ; | ||
} | ||
this.buffer.splice( this.cy , 1 ) ; | ||
if ( this.selectionRegion ) { this.updateSelectionFromCells() ; } | ||
return deleted ; | ||
} ; | ||
// TODOC | ||
// Return a region where the searchString is found | ||
TextBuffer.prototype.findNext = function( searchString , startPosition , reverse ) { | ||
var index , startAt , endAt , | ||
text = this.getText() , | ||
// /!\ another function MUST BE used once unicode composition will be supported | ||
// It is meant to produce the exact same cell size | ||
size = string.unicode.toArray( searchString ).length ; | ||
reverse = !! reverse ; | ||
if ( reverse ) { | ||
startPosition = startPosition ? startPosition - size : text.length - size ; | ||
} | ||
else { | ||
startPosition = startPosition ?? 0 ; | ||
} | ||
index = reverse ? text.lastIndexOf( searchString , startPosition ) : | ||
text.indexOf( searchString , startPosition ) ; | ||
if ( index === -1 ) { return ; } | ||
startAt = this.offsetToCoordinate( index ) ; | ||
endAt = this.offsetToCoordinate( index + size - 1 ) ; | ||
return { | ||
xmin: startAt.x , | ||
ymin: startAt.y , | ||
xmax: endAt.x , | ||
ymax: endAt.y | ||
} ; | ||
} ; | ||
// TODOC | ||
TextBuffer.prototype.findPrevious = function( searchString , startPosition ) { | ||
return this.findNext( searchString , startPosition , true ) ; | ||
} ; | ||
// TODOC | ||
// Return a region where the regexp match, the region also have a 'match' property with the result of the regexp#exec(). | ||
// Can't be reversed due to how regexp works, except by searching for all match beforehand | ||
TextBuffer.prototype.regexpFindNext = function( regexp , startPosition = 0 ) { | ||
var index , startAt , endAt , size , match , | ||
text = this.getText() ; | ||
if ( typeof regexp === 'string' ) { | ||
regexp = new RegExp( regexp , 'gu' ) ; | ||
} | ||
else { | ||
// Force global and unicode | ||
regexp.global = true ; | ||
regexp.unicode = true ; | ||
} | ||
regexp.lastIndex = startPosition ; | ||
match = regexp.exec( text ) ; | ||
if ( ! match ) { return ; } | ||
// /!\ another function MUST BE used once unicode composition will be supported | ||
// It is meant to produce the exact same cell size | ||
size = string.unicode.toArray( match[ 0 ] ).length ; | ||
startAt = this.offsetToCoordinate( match.index ) ; | ||
endAt = this.offsetToCoordinate( match.index + size - 1 ) ; | ||
return { | ||
xmin: startAt.x , | ||
ymin: startAt.y , | ||
xmax: endAt.x , | ||
ymax: endAt.y , | ||
match | ||
} ; | ||
} ; | ||
/* | ||
@@ -1505,2 +2055,3 @@ A TextBuffer can only draw to a ScreenBuffer. | ||
voidAttr: this.voidAttr , | ||
inverseRegion: this.selectionRegion , | ||
writeAttr: | ||
@@ -1535,3 +2086,6 @@ this.ScreenBuffer === termkit.ScreenBuffer ? | ||
//console.error( "blitter line" , p.srcY ) ; | ||
var srcRShift , srcX , srcXmax , srcExistingXmax , dstOffset , cells , cell , attr , charCode ; | ||
var srcRShift , srcX , srcXmax , srcExistingXmax , dstOffset , cells , cell , attr , char , charCode , | ||
invRegion = p.context.inverseRegion , | ||
invXmin = Infinity , | ||
invXmax = -Infinity ; | ||
@@ -1554,2 +2108,7 @@ //if ( ! global.deb ) { global.deb = [] ; } | ||
if ( invRegion && p.srcY >= invRegion.ymin && p.srcY <= invRegion.ymax ) { | ||
invXmin = p.srcY === invRegion.ymin ? invRegion.xmin : -Infinity ; | ||
invXmax = p.srcY === invRegion.ymax ? invRegion.xmax : Infinity ; | ||
} | ||
// Write existing cells | ||
@@ -1560,17 +2119,23 @@ for ( ; srcX <= srcExistingXmax ; srcX ++ , dstOffset += this.ScreenBuffer.prototype.ITEM_SIZE ) { | ||
// Write the attributes | ||
p.context.writeAttr( p.context.dstBuffer , cell.attr , dstOffset ) ; | ||
if ( p.context.forceChar ) { | ||
// Write the forced character (i.e. hidden) | ||
p.context.dstBuffer.write( p.context.forceChar , dstOffset + this.ScreenBuffer.prototype.ATTR_SIZE , this.ScreenBuffer.prototype.CHAR_SIZE ) ; | ||
// Use a forced character (i.e. hidden) | ||
attr = cell.attr ; | ||
char = p.context.forceChar ; | ||
} | ||
else if ( ( charCode = cell.char.charCodeAt( 0 ) ) < 0x20 || charCode === 0x7f ) { | ||
// Replace the control char by a white space | ||
p.context.dstBuffer.write( ' ' , dstOffset + this.ScreenBuffer.prototype.ATTR_SIZE , this.ScreenBuffer.prototype.CHAR_SIZE ) ; | ||
} | ||
else { | ||
// Write the character | ||
p.context.dstBuffer.write( cell.char , dstOffset + this.ScreenBuffer.prototype.ATTR_SIZE , this.ScreenBuffer.prototype.CHAR_SIZE ) ; | ||
attr = | ||
cell.width === 2 ? cell.attr | this.ScreenBuffer.prototype.LEADING_FULLWIDTH : | ||
cell.width === 0 ? cell.attr | this.ScreenBuffer.prototype.TRAILING_FULLWIDTH : | ||
cell.attr ; | ||
char = | ||
( ( charCode = cell.char.charCodeAt( 0 ) ) < 0x20 || charCode === 0x7f ) ? ' ' : | ||
cell.char ; | ||
} | ||
if ( srcX >= invXmin && srcX <= invXmax ) { attr = this.ScreenBuffer.attrInverse( attr ) ; } | ||
// Write the attributes | ||
p.context.writeAttr( p.context.dstBuffer , attr , dstOffset ) ; | ||
// Write the char | ||
p.context.dstBuffer.write( char , dstOffset + this.ScreenBuffer.prototype.ATTR_SIZE , this.ScreenBuffer.prototype.CHAR_SIZE ) ; | ||
} | ||
@@ -1594,13 +2159,14 @@ } | ||
attr = | ||
cell.width === 2 ? cell.attr | this.ScreenBuffer.prototype.LEADING_FULLWIDTH : | ||
cell.width === 0 ? cell.attr | this.ScreenBuffer.prototype.TRAILING_FULLWIDTH : | ||
cell.attr ; | ||
char = | ||
( ( charCode = cell.char.charCodeAt( 0 ) ) < 0x20 || charCode === 0x7f ) ? ' ' : | ||
cell.char ; | ||
// Write the attributes | ||
p.context.writeAttr( p.context.dstBuffer , cell.attr , dstOffset ) ; | ||
if ( ( charCode = cell.char.charCodeAt( 0 ) ) < 0x20 || charCode === 0x7f ) { | ||
// Replace the control char by a white space | ||
p.context.dstBuffer.write( ' ' , dstOffset + this.ScreenBuffer.prototype.ATTR_SIZE , this.ScreenBuffer.prototype.CHAR_SIZE ) ; | ||
} | ||
else { | ||
// Write the character | ||
p.context.dstBuffer.write( cell.char , dstOffset + this.ScreenBuffer.prototype.ATTR_SIZE , this.ScreenBuffer.prototype.CHAR_SIZE ) ; | ||
} | ||
p.context.writeAttr( p.context.dstBuffer , attr , dstOffset ) ; | ||
// Write the char | ||
p.context.dstBuffer.write( char , dstOffset + this.ScreenBuffer.prototype.ATTR_SIZE , this.ScreenBuffer.prototype.CHAR_SIZE ) ; | ||
} | ||
@@ -1628,12 +2194,21 @@ } | ||
// Naive loading | ||
TextBuffer.prototype.load = function( path , callback ) { | ||
// Using callback is DEPRECATED. | ||
TextBuffer.prototype.load = async function( path , callback ) { | ||
var content ; | ||
this.buffer[ 0 ] = [] ; | ||
this.buffer.length = 1 ; | ||
// Naive file loading, optimization are for later | ||
fs.readFile( path , ( error , data ) => { | ||
if ( error ) { callback( error ) ; return ; } | ||
this.setText( data.toString() ) ; | ||
callback() ; | ||
} ) ; | ||
// Naive file loading, should be optimized later | ||
try { | ||
content = await fs.promises.readFile( path ) ; | ||
} | ||
catch ( error ) { | ||
if ( callback ) { callback( error ) ; return ; } | ||
throw error ; | ||
} | ||
this.setText( content.toString() ) ; | ||
if ( callback ) { callback() ; return ; } | ||
} ; | ||
@@ -1644,8 +2219,14 @@ | ||
// Naive saving | ||
TextBuffer.prototype.save = function( path , callback ) { | ||
// Using callback is DEPRECATED. | ||
TextBuffer.prototype.save = async function( path , callback ) { | ||
// Naive file saving, optimization are for later | ||
fs.writeFile( path , this.getText() , ( error ) => { | ||
if ( error ) { callback( error ) ; return ; } | ||
callback() ; | ||
} ) ; | ||
try { | ||
await fs.promises.writeFile( path , this.getText() ) ; | ||
} | ||
catch ( error ) { | ||
if ( callback ) { callback( error ) ; return ; } | ||
throw error ; | ||
} | ||
if ( callback ) { callback() ; return ; } | ||
} ; | ||
@@ -1655,4 +2236,10 @@ | ||
TextBuffer.prototype.object2attr = function( attrObject ) { | ||
return this.ScreenBuffer.object2attr( attrObject , this.palette && this.palette.colorNameToIndex ) ; | ||
/* Utilities */ | ||
TextBuffer.prototype.object2attr = function( attrObject , colorNameToIndex = this.palette?.colorNameToIndex , legacyColor = false ) { | ||
return this.ScreenBuffer.object2attr( attrObject , colorNameToIndex , legacyColor ) ; | ||
} ; | ||
@@ -1662,4 +2249,20 @@ | ||
// TODOC | ||
// A small utility function that returns the coordinate one step backward, if needed it point to the end of the previous line | ||
TextBuffer.prototype.oneStepBackward = function( x = this.cx , y = this.cy ) { | ||
x -- ; | ||
if ( x < 0 ) { | ||
y -- ; | ||
if ( y < 0 ) { return null ; } | ||
x = this.buffer[ y ].length - 1 ; | ||
} | ||
return { x , y } ; | ||
} ; | ||
/* API for the text-machine module */ | ||
@@ -1674,5 +2277,5 @@ | ||
this.iterate( { finalCall: true } , data => { | ||
data.textBuffer = this ; | ||
this.stateMachine.pushEvent( data.text , data ) ; | ||
this.iterate( { finalCall: true , fillerCopyAttr: true } , context => { | ||
context.textBuffer = this ; | ||
this.stateMachine.pushEvent( context.text , context ) ; | ||
} ) ; | ||
@@ -1689,3 +2292,3 @@ } ; | ||
TextMachineApi.style = ( context , style ) => { | ||
if ( context.x === null ) { return ; } // This is a newline or end of buffer character, there is no style to apply here | ||
if ( ! context || context.x === null ) { return ; } // This is a newline or end of buffer character, there is no style to apply here | ||
if ( ! style.code ) { style.code = context.textBuffer.ScreenBuffer.object2attr( style ) ; } // cache it now | ||
@@ -1698,22 +2301,11 @@ | ||
TextMachineApi.startingStyle = ( context , style ) => { | ||
if ( ! context.startingContext || context.startingContext.x === null ) { return ; } | ||
if ( ! style.code ) { style.code = context.textBuffer.ScreenBuffer.object2attr( style ) ; } // cache it now | ||
TextMachineApi.blockStyle = function( startingContext , endingContext , style ) { | ||
if ( ! startingContext || ! endingContext || startingContext.x === null || endingContext.x === null ) { return ; } | ||
if ( ! style.code ) { style.code = startingContext.textBuffer.ScreenBuffer.object2attr( style ) ; } // cache it now | ||
context.textBuffer.setAttrCodeAt( style.code , context.startingContext.x , context.startingContext.y ) ; | ||
} ; | ||
TextMachineApi.openingStyle = TextMachineApi.startingStyle ; | ||
TextMachineApi.blockStyle = function( context , style ) { | ||
if ( context.x === null || ! context.startingContext || context.startingContext.x === null ) { return ; } | ||
if ( ! style.code ) { style.code = context.textBuffer.ScreenBuffer.object2attr( style ) ; } // cache it now | ||
context.textBuffer.setAttrCodeRegion( style.code , { | ||
xmin: context.startingContext.x , | ||
xmax: context.x , | ||
ymin: context.startingContext.y , | ||
ymax: context.y | ||
startingContext.textBuffer.setAttrCodeRegion( style.code , { | ||
xmin: startingContext.x , | ||
xmax: endingContext.x , | ||
ymin: startingContext.y , | ||
ymax: endingContext.y | ||
} ) ; | ||
@@ -1724,10 +2316,10 @@ } ; | ||
TextMachineApi.hint = function( context , hints ) { | ||
var misc ; | ||
TextMachineApi.hint = function( context , buffer , hints ) { | ||
if ( ! context || context.x === null || context.y === null ) { return ; } | ||
if ( hints[ context.buffer ] ) { | ||
misc = context.textBuffer.getMiscAt( context.x , context.y ) ; | ||
if ( misc ) { misc.hint = hints[ context.buffer ] ; } | ||
if ( hints[ buffer ] ) { | ||
let misc_ = context.textBuffer.getMiscAt( context.x , context.y ) ; | ||
if ( misc_ ) { misc_.hint = hints[ buffer ] ; } | ||
} | ||
} ; | ||
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -31,3 +31,2 @@ The MIT License (MIT) | ||
const termkit = require( '../termkit.js' ) ; | ||
const ScreenBuffer = require( '../ScreenBuffer.js' ) ; | ||
@@ -34,0 +33,0 @@ const Rect = require( '../Rect.js' ) ; |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
/* | ||
Terminal Kit | ||
Copyright (c) 2009 - 2021 Cédric Ronvel | ||
Copyright (c) 2009 - 2022 Cédric Ronvel | ||
@@ -6,0 +6,0 @@ The MIT License (MIT) |
{ | ||
"name": "terminal-kit", | ||
"version": "1.49.4", | ||
"version": "3.0.1", | ||
"description": "256 colors, keys and mouse, input field, progress bars, screen buffer (including 32-bit composition and image loading), text buffer, and many more... Whether you just need colors and styles, build a simple interactive command line tool or a complexe terminal app: this is the absolute terminal lib for Node.js!", | ||
@@ -10,13 +10,13 @@ "main": "lib/termkit.js", | ||
"engines": { | ||
"node": ">=10.0.0" | ||
"node": ">=16.13.0" | ||
}, | ||
"dependencies": { | ||
"@cronvel/get-pixels": "^3.4.0", | ||
"chroma-js": "^2.1.0", | ||
"@cronvel/get-pixels": "^3.4.1", | ||
"chroma-js": "^2.4.2", | ||
"lazyness": "^1.2.0", | ||
"ndarray": "^1.0.19", | ||
"nextgen-events": "^1.3.4", | ||
"seventh": "^0.7.40", | ||
"string-kit": "^0.11.9", | ||
"tree-kit": "^0.7.0" | ||
"nextgen-events": "^1.5.3", | ||
"seventh": "^0.9.2", | ||
"string-kit": "^0.17.10", | ||
"tree-kit": "^0.8.1" | ||
}, | ||
@@ -66,3 +66,3 @@ "scripts": { | ||
2009, | ||
2021 | ||
2022 | ||
], | ||
@@ -69,0 +69,0 @@ "owner": "Cédric Ronvel" |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses eval() which is a dangerous function. This prevents the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 2 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
High entropy strings
Supply chain riskContains high entropy strings. This could be a sign of encrypted data, leaked secrets or obfuscated code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
4423086
75105
53
177
2
17
+ Addedseventh@0.9.2(transitive)
+ Addedstring-kit@0.17.10(transitive)
+ Addedtree-kit@0.8.7(transitive)
- Removedseventh@0.7.40(transitive)
- Removedstring-kit@0.11.10(transitive)
- Removedtree-kit@0.7.5(transitive)
Updated@cronvel/get-pixels@^3.4.1
Updatedchroma-js@^2.4.2
Updatednextgen-events@^1.5.3
Updatedseventh@^0.9.2
Updatedstring-kit@^0.17.10
Updatedtree-kit@^0.8.1