Compare commits

..
1192 Commits
Author SHA1 Message Date
Paris Kasidiaris 479fb0e782 Bump version to 2.8.0
Signed-off-by: Paris Kasidiaris <paris@sourcelair.com>
2017-07-04 16:45:20 +00:00
Paris Kasidiaris 8ba11a2bd0 Merge pull request #752 from LucianBuzzo/751-IE11-demo
Add a Promise polyfill to the demo so that it works in IE11
2017-07-04 12:53:13 +03:00
Paris Kasidiaris abc3f3b94d Merge branch 'master' into 751-IE11-demo 2017-07-04 12:50:37 +03:00
Daniel Imms 67bc0456e0 Merge pull request #747 from anishathalye/master
Add scroll functionality for mobile
2017-07-03 21:48:00 -07:00
Daniel Imms 3e8ddd1966 Add jsdoc 2017-07-03 21:43:52 -07:00
Anish Athalye 6223342890 Add scroll functionality for mobile 2017-07-03 19:49:16 -07:00
Daniel Imms 9ddabb3fee Merge pull request #750 from irokas/Rows-fix
Set minimum scrollback value to number of rows if needed on resize
2017-07-03 18:23:48 -07:00
Daniel Imms af0030b580 Merge branch 'master' into Rows-fix 2017-07-03 18:14:31 -07:00
Paris Kasidiaris b93271d9cd Merge branch 'master' into 751-IE11-demo 2017-07-03 14:55:48 +03:00
Paris Kasidiaris 934ca6508f Merge pull request #754 from LucianBuzzo/753-ie11-terminal-cursor
Add one class at a time to element for IE11 compatibility
2017-07-03 14:55:01 +03:00
Lucian Buzzo 7e210c7e6b Add one class at a time to element for IE11 compatibility
Connects to #753

Element.classList.add() does not work with multiple arguments in IE.
2017-07-03 11:22:37 +01:00
Lucian Buzzo e4970266d8 Add a Promise polyfill to the demo so that it works in IE11
Connects to #751
2017-07-03 11:05:35 +01:00
irokas 0a5d7a7bd2 Set minimum scrollback value to number of rows if needed on resize 2017-07-02 21:10:18 +00:00
Daniel Imms 31aab8ea6f Merge pull request #746 from Tyriar/745_colon_word_sep
Remove colon from the selection word separators
2017-07-02 12:36:46 -07:00
Daniel Imms 857d9bcf2a Merge branch 'master' into 745_colon_word_sep 2017-07-02 12:30:45 -07:00
Daniel Imms 7463166aec Fix tests 2017-07-02 12:29:31 -07:00
Paris Kasidiaris f80159fedc Merge pull request #749 from irokas/fix-scrollback
Fix issue with small scrollback value
2017-07-02 14:39:07 +03:00
irokas 3180a6f6ec Add test for scrollback fix 2017-07-02 14:34:19 +03:00
irokas 35d26f1d27 Fix issue with small scrollback value.
Closes #506
2017-07-02 14:22:24 +03:00
Daniel Imms ca03f8928d Remove colon from the word separators
Fixes #745
2017-06-30 17:07:52 -07:00
Daniel Imms 0b3d1e50a8 Merge pull request #741 from AndrienkoAleksandr/codeCleanUp
Little code clean up.
2017-06-30 09:50:54 -07:00
Daniel Imms 401886c7c3 Merge branch 'master' into codeCleanUp 2017-06-30 09:46:54 -07:00
Daniel Imms 4eac7661c2 Merge pull request #733 from Tyriar/732_drop_selection_on_alt
Drop selection when the active buffer changes
2017-06-30 09:40:49 -07:00
Daniel Imms b8163cfbbf Merge branch 'master' into 732_drop_selection_on_alt 2017-06-30 09:36:26 -07:00
Paris Kasidiaris a9f82907ee Merge pull request #743 from nechitai/add-user
Add hexlet.io to the users list
2017-06-30 15:40:30 +03:00
Artem Arbatskiy 57f4ae5320 Add hexlet.io to the users list 2017-06-30 18:30:02 +07:00
Aleksandr Andriienko d1712424c8 Fix docs. 2017-06-30 09:57:24 +03:00
Oleksandr Andriienko 16f865987d Little code clean up. 2017-06-29 23:36:07 +03:00
Daniel Imms f071531494 Merge branch 'master' into 732_drop_selection_on_alt 2017-06-29 12:13:10 -07:00
Daniel Imms 618768c7ed Merge pull request #738 from Tyriar/737_crlf_windows
Use crlf when copying on Windows
2017-06-29 12:12:56 -07:00
Daniel Imms cfe22e6fd2 Use crlf when copying on Windows
Fixes #737
2017-06-29 07:38:41 -07:00
Daniel Imms 3961dd5a44 Merge branch 'master' into 732_drop_selection_on_alt 2017-06-28 15:21:27 -07:00
Daniel Imms 471e332036 Merge pull request #736 from AndrienkoAleksandr/master
Fix 'CR' issue on Linux based OS.
2017-06-28 12:45:16 -07:00
Oleksandr Andriienko 467e8c563a Fix up. 2017-06-28 00:48:13 +03:00
Oleksandr Andriienko 5971630f64 Simplify .gitattributes
Signed-off-by: Oleksandr Andriienko <oandriie@redhat.com>
2017-06-28 00:39:16 +03:00
Oleksandr Andriienko f7cc1058c7 Fix 'CR' issue on Linux based OS.
Signed-off-by: Oleksandr Andriienko <oandriie@redhat.com>
2017-06-27 23:25:01 +03:00
Daniel Imms 645a5920da Drop selection when the active buffer changes
Fixes #732
2017-06-27 10:13:16 -07:00
Paris Kasidiaris f95bc64971 Merge pull request #712 from chabou/customKeypressHandler
Add attachCustomKeyEventsHandler
2017-06-22 16:16:23 +03:00
Daniel Imms 0c7c307de3 Merge branch 'master' into customKeypressHandler 2017-06-21 08:51:15 -07:00
Daniel Imms 1c84881d00 Merge pull request #725 from Tyriar/724_fix_hasSelection
Have Terminal.hasSelection return false when no selection
2017-06-21 08:38:43 -07:00
Daniel Imms f1252abbbe Merge branch 'master' into 724_fix_hasSelection 2017-06-21 08:34:33 -07:00
Daniel Imms 11d6ca2aa1 Merge pull request #723 from Tyriar/699_linux_middle_click
Fix select and middle click to paste selection
2017-06-21 08:34:06 -07:00
Daniel Imms eb59b90790 Merge branch 'master' into 699_linux_middle_click 2017-06-21 08:29:54 -07:00
Daniel Imms 4c3aa97917 Merge pull request #719 from Tyriar/718_selection_on_top
Render selection on top of rows
2017-06-21 08:29:40 -07:00
Daniel Imms ba54f25b8c Merge branch 'master' into 718_selection_on_top 2017-06-21 08:25:05 -07:00
Daniel Imms af268f4968 Merge pull request #722 from Tyriar/721_firefox_contextmenu
Fix context menu in firefox
2017-06-21 08:24:52 -07:00
Daniel Imms 63d63c27e7 Ensure outside selection pastes are respected
We were resetting the selection on every auxclick, meaning outside
selections could not be pasted in when the terminal had a current
selection.
2017-06-20 11:17:12 -07:00
Daniel Imms ef1e2ab9e2 Use auxclick event 2017-06-20 10:49:58 -07:00
Daniel Imms 7b46940772 Have Terminal.hasSelection return false when no selection
Fixes #724
2017-06-20 10:38:33 -07:00
Daniel Imms 6f5f68df1a Add a comment 2017-06-20 10:24:16 -07:00
Daniel Imms dc165175cf Handle middle click to paste selection
This appears to work in Chrome but not Firefox
2017-06-20 10:18:29 -07:00
Daniel Imms 32f1782c84 Fix context menu in firefox
Fixes #721
2017-06-20 10:01:39 -07:00
Daniel Imms 8811d96a8b Fix selection going to clipboard
Part of #699
2017-06-19 15:45:18 -07:00
CHaBou d11d74688e Merge branch 'master' into customKeypressHandler 2017-06-19 22:10:03 +02:00
CHaBou 8e79d53d78 Add attachCustomKeyEventHandler()
Use a uniq customKeyEventHandler for keyDown and keyPress events
Mark and warn attachCustomKeydownHandler() as deprecated.
2017-06-19 22:03:19 +02:00
Daniel Imms 6dbc476cdc Allow mouse events to pass through selection 2017-06-19 11:26:52 -07:00
Daniel Imms 57921e3fe0 Render selection on top of rows
Fixes #718
2017-06-19 11:18:55 -07:00
Daniel Imms 75b74ac4ff Merge pull request #716 from Tyriar/697_select_to_word
Select to the word at the mouse after double click
2017-06-19 07:56:35 -07:00
Daniel Imms 1c659841fa Merge branch 'master' into 697_select_to_word 2017-06-19 07:47:40 -07:00
Daniel Imms 9573357778 Merge pull request #715 from Tyriar/709_improve_selection
Improve selection cell selection
2017-06-19 07:47:15 -07:00
Daniel Imms f11362a80d Merge branch 'master' into 709_improve_selection 2017-06-19 07:39:29 -07:00
Daniel Imms a01fbb636a Merge pull request #714 from Tyriar/713_dblclick_word_separators
Don't select colon, quotes or brackets when double clicking
2017-06-19 07:39:15 -07:00
Daniel Imms 320fb55ddc Use an enum for selection mode 2017-06-18 12:56:32 -07:00
Daniel Imms 4b170ca43f Select to the word at the mouse after double click
Fixes #697
2017-06-18 12:52:43 -07:00
Daniel Imms c6e7fb74ab Merge branch 'master' into 709_improve_selection 2017-06-18 12:03:30 -07:00
Daniel Imms b05814e6a5 Improve selection cell selection
Fixes #709
2017-06-18 12:02:58 -07:00
Daniel Imms 07882c42dc Also consider ' and " word separators
For example URIs in json
2017-06-18 11:51:00 -07:00
Daniel Imms a59b2f8433 Merge branch 'master' into 713_dblclick_word_separators 2017-06-18 11:08:22 -07:00
Daniel Imms 7f2e94c30c Don't select colon or brackets when double clicking
Fixes #713
2017-06-18 10:38:04 -07:00
CHaBou 95aed8fcb0 Add customKeypressHandler 2017-06-16 20:23:48 +02:00
Daniel Imms 663d3b24cd Merge pull request #711 from sourcelair/Tyriar-patch-1
Move latest addition to real world use cases to the end
2017-06-16 07:17:39 -07:00
Daniel Imms a87303f375 Move latest addition to real world use cases to the end
Seems a little unfair to not be at the end, to date it's sorted by date added.
2017-06-16 07:13:03 -07:00
Paris Kasidiaris 259858fac2 Merge pull request #710 from alex-kovoy/alex-kovoy-patch-1
Added 'Gravitational Teleport' to Real-world uses
2017-06-15 23:15:04 +01:00
Alexey Kontsevoy a4514ee98a Added 'Gravitational Teleport' to Real-world uses
We use xterm.js in Gravitational Teleport.
2017-06-15 18:05:07 -04:00
Daniel Imms d27b7bfa7a Merge pull request #707 from jdanyow/issue-683
normalize line endings to \r
2017-06-14 15:22:04 -07:00
Jeremy Danyow ce61795c38 normalize line endings to \r
fixes #683
2017-06-14 14:51:36 -07:00
Daniel Imms 9cc878cea7 Merge pull request #688 from Tyriar/642_link_wrapped_char_duplication
Fix issue with strings in wrapped spans being duplicated
2017-06-13 14:53:23 -07:00
Daniel Imms 087acf8a9a Merge branch 'master' into 642_link_wrapped_char_duplication 2017-06-13 14:45:17 -07:00
Daniel Imms 7612180276 Merge pull request #691 from Tyriar/mid_selection_single_element
Use a single element for the middle of the selection
2017-06-13 14:45:11 -07:00
Daniel Imms 201e3e4dab Merge branch 'master' into mid_selection_single_element 2017-06-13 14:40:48 -07:00
Daniel Imms 55f51ad169 Merge pull request #693 from Tyriar/443_retain_wrapped_lines_copy
Retain wrapped line state when copying
2017-06-13 14:40:41 -07:00
Daniel Imms 405d1ee52d Merge branch 'master' into 443_retain_wrapped_lines_copy 2017-06-13 14:32:01 -07:00
Daniel Imms 5f9ad5c19e Merge pull request #700 from Tyriar/698_mousedown_preventDefault
Prevent mousedown event default selection behavior
2017-06-13 14:31:50 -07:00
Daniel Imms c5cfa409b1 Merge branch 'master' into 698_mousedown_preventDefault 2017-06-12 14:53:48 -07:00
Daniel Imms 2a0f44b0fb Prevent mousedown event default selection behavior
Fixes #698
2017-06-12 14:51:58 -07:00
Daniel Imms 7d0e585c44 Merge branch 'master' into 642_link_wrapped_char_duplication 2017-06-11 11:34:22 -07:00
Daniel Imms a5ced5d909 Merge branch 'master' into mid_selection_single_element 2017-06-11 11:34:20 -07:00
Daniel Imms 0a99f65f01 Merge branch 'master' into 443_retain_wrapped_lines_copy 2017-06-11 11:34:17 -07:00
Daniel Imms c33814a7a6 Merge pull request #680 from Tyriar/set_row_height_explicitly
Set row height when char size is changed
2017-06-10 10:25:06 -07:00
Daniel Imms 0950369a5c Merge remote-tracking branch 'origin/master' into set_row_height_explicitly 2017-06-10 10:21:20 -07:00
Daniel Imms 346b5177bc Retain wrapped line state when copying
Fixes #443
2017-06-09 17:12:00 -07:00
Daniel Imms 9246d524cb Use a single element for the middle of the selection
This reduces the number of DOM nodes used for the selection dramatically.
2017-06-09 16:35:26 -07:00
Daniel Imms a889fef752 Merge pull request #670 from Tyriar/207_selection_manager
Reimplement selection in the terminal
2017-06-09 13:41:34 -07:00
Daniel Imms 6075498c24 Add class header comments 2017-06-09 12:47:16 -07:00
Daniel Imms ee1da73a69 Add regression test 2017-06-09 11:59:18 -07:00
Daniel Imms 29ac6a153e Fix issue with strings in wrapped spans being duplicated
This was happening because the linkifier assumed that top-level spans
in the terminal would only contain single text nodes

Fixes #642
2017-06-09 11:48:12 -07:00
Daniel Imms 264757e964 Use user-select supported by other browsers 2017-06-07 19:23:46 -07:00
Daniel Imms e88a9ffd89 Fix bug when selection end is less than start 2017-06-07 19:17:38 -07:00
Daniel Imms d56a611a19 Add .gitattributes file
Makes working on Windows less bothersome.
2017-06-07 19:16:59 -07:00
Daniel Imms 28ed7772c6 Select last character in bottom right in select all 2017-06-07 14:44:16 -07:00
Daniel Imms a047359f81 Add more selection manager/model tests 2017-06-07 14:37:09 -07:00
Daniel Imms b81c165bea Add SelectionModel tests 2017-06-07 14:15:18 -07:00
Daniel Imms 4405f5e163 Fix clearSelection in select all mode 2017-06-07 13:48:12 -07:00
Daniel Imms 2012c8c91d Move prepareTextForClipboard logic into SelectionManager 2017-06-07 13:42:08 -07:00
Daniel Imms f719a4e91b Fix tests 2017-06-07 13:20:44 -07:00
Daniel Imms 5d33727ae4 Add clearSelection API 2017-06-07 13:07:34 -07:00
Daniel Imms 1343b83fc3 Add hasSelection public API 2017-06-07 12:40:48 -07:00
Daniel Imms 9e47ec9af2 Add getSelectionText API
This allows consumers to implement their own copy functionality/keybindings
2017-06-07 11:59:28 -07:00
Daniel Imms 9c459aacc7 Explicitly size selectionContainer to allow for more flexible layouts 2017-06-07 11:48:31 -07:00
Daniel Imms 72724abac8 Ensure host program handles copy if mouseevents are active 2017-06-07 11:20:03 -07:00
Daniel Imms 9a86eeb333 Support copying in the alt buffer 2017-06-07 11:10:05 -07:00
Daniel Imms 46f6fd18b0 Merge remote-tracking branch 'ups/master' into set_row_height_explicitly 2017-06-06 14:34:07 -07:00
Daniel Imms 603f22a7bb Set row height when char size is changed
When zoomed in line-height and actual rendered row height differs. This
can be seen in the viewport being too large when zoomed in right now
but also has additional impacts in the new selection model #207
2017-06-06 14:31:57 -07:00
Daniel Imms 13c401cbaa Only refresh the selection on an animation frame 2017-06-06 10:52:42 -07:00
Daniel Imms b812991072 Fix multiple drag scroll intervals being registered 2017-06-06 10:40:22 -07:00
Daniel Imms 2b2431824f Fix select all with no start and include content below viewport 2017-06-06 23:13:50 -07:00
Daniel Imms 7147787441 Undo CiruclarList id changes, fix null check, lint 2017-06-06 22:59:16 -07:00
Daniel Imms ec3bf1139d Resolve TODOs 2017-06-06 22:33:32 -07:00
Daniel Imms d0b603d033 jsdoc all of SelectionManager 2017-06-06 21:55:52 -07:00
Daniel Imms 2621be819e Resolve more TODOs, add jsdoc 2017-06-06 21:43:19 -07:00
Daniel Imms f380153f42 Resolve some TODOs 2017-06-06 21:29:30 -07:00
Daniel Imms fd91c5e1a8 Add _selectLineAt test 2017-06-06 15:22:35 -07:00
Daniel Imms f61d852f3c Don't allow double click selection on empty row going out of viewport 2017-06-06 15:05:22 -07:00
Daniel Imms 5bc1112151 Select lines when dragging a triple click 2017-06-06 15:02:52 -07:00
Daniel Imms 59cc0b675e Merge remote-tracking branch 'ups/master' into 207_selection_manager 2017-06-06 14:35:06 -07:00
Daniel Imms db8ded2a02 Implement shift+click 2017-06-06 14:19:12 -07:00
Paris Kasidiaris 4b7279146a Bump version to 2.7.0
Signed-off-by: Paris Kasidiaris <paris@sourcelair.com>
2017-06-06 15:04:59 +00:00
Paris Kasidiaris c4a2f7911d Merge pull request #674 from sourcelair/issue-#671-restore-paste-event
Restore the "paste" terminal event
2017-06-06 11:23:18 +03:00
Paris Kasidiaris db9fe53af3 Merge branch 'master' into issue-#671-restore-paste-event 2017-06-06 11:16:16 +03:00
Daniel Imms d3865ad23f Add some SelectionManager selectWorkAt tests, fix lint 2017-06-01 17:36:47 -07:00
Daniel Imms 8b1067d180 Remove no longer valid clipboard test and add new one 2017-06-01 17:11:07 -07:00
Daniel Imms a53a0acd65 Fix issue with trimming whitespace 2017-06-01 17:04:59 -07:00
Daniel Imms 42724c7f42 Merge pull request #672 from Tyriar/662_parser_state_hack
Fix bug with parser state when using vtop
2017-05-27 15:07:52 -07:00
Paris Kasidiaris 616f069538 Fix #671 - Restore paste event 2017-05-27 15:30:26 +03:00
Daniel Imms 11cec31da6 Clean up wide char select word code 2017-05-25 21:12:44 -07:00
Daniel Imms d9991b25a4 Support wide char double click 2017-05-25 20:48:48 -07:00
Daniel Imms cb6533f3a6 Get double click selection for wide chars mostly working 2017-05-25 20:22:43 -07:00
Daniel Imms 54e7f65d4e Support selecting wide characters 2017-05-25 19:34:32 -07:00
Daniel Imms ec61f3acf0 Only trim the right whitespace from selection
Fixes #673
2017-05-25 18:52:06 -07:00
Daniel Imms 910aa1ad7f Properly prepare text for clipboard for context menu
This makes the context menu in macOS embed the right text
2017-05-25 18:12:03 -07:00
Daniel Imms 24bed01fa4 Support copy and paste via context menu 2017-05-25 16:17:43 -07:00
Daniel Imms f7d6ab5f2a Move the selection model to its own module 2017-05-25 13:20:57 -07:00
Daniel Imms 0716cffd7d Tidy up _areSelectionValuesReversed 2017-05-25 12:46:54 -07:00
Daniel Imms e29ab29441 Fix drag when selectioning via double/triple click 2017-05-25 12:46:08 -07:00
Daniel Imms 9f271de803 Implement triple click to select line 2017-05-25 12:27:47 -07:00
Daniel Imms 43c796a781 Support copying of select all text 2017-05-25 11:35:22 -07:00
Daniel Imms 25152e4441 Add select all API 2017-05-25 10:14:40 -07:00
Daniel Imms 3846fe0a7d Add null checks to fix trim related crash 2017-05-25 09:55:08 -07:00
Daniel Imms b3b2bd1faf Don't flag user scrolling if no scrolling happens 2017-05-25 09:51:13 -07:00
Daniel Imms 2c3d98abc8 Fix bug with parser state when using vtop
Fixes #662
2017-05-25 09:36:59 -07:00
Daniel Imms 0dc3dd0336 Initial scroll drag implementation 2017-05-23 11:20:55 -07:00
Daniel Imms c4762e7228 Merge remote-tracking branch 'ups/master' into 207_selection_manager 2017-05-22 19:51:42 -07:00
Daniel Imms 35b32b721e Merge pull request #668 from Tyriar/fix_pt_valid_var_declarations
Declare pt and valid vars in correct spot
2017-05-22 08:19:52 -07:00
Daniel Imms ccf1a047d3 Merge branch 'master' into fix_pt_valid_var_declarations 2017-05-22 08:03:53 -07:00
Daniel Imms db0338cbcf Merge pull request #669 from Tyriar/pull_getCoords_into_module
Pull get coords into module
2017-05-22 08:03:31 -07:00
Daniel Imms 06742eb507 Merge branch 'master' into pull_getCoords_into_module 2017-05-22 07:57:55 -07:00
Daniel Imms 0fab4ff5d1 Merge pull request #667 from Tyriar/add_missing_emit_arg
Add missing EventEmitter.emit args/types
2017-05-22 07:57:35 -07:00
Daniel Imms d2b0d514f8 Add missing semi-colons 2017-05-21 13:51:02 -07:00
Daniel Imms ab40908fa4 Disable selection manager in mouse mode
This allows the application to take full control of the selection
2017-05-21 12:35:07 -07:00
Daniel Imms a5c48c891e Temp fix until mouse getCoords PR merged 2017-05-21 12:16:36 -07:00
Daniel Imms 70e35de56b Polish comments and cap code 2017-05-21 12:05:30 -07:00
Daniel Imms c323e0ad4a jsdoc 2017-05-21 11:57:24 -07:00
Daniel Imms 4d32826891 Pull getCoords into a Mouse util module
This is part of the work to prepare for the upcoing selection changes

Related #207
2017-05-21 11:50:01 -07:00
Daniel Imms 13088f55a0 Declare pt and valid vars in correct spot 2017-05-21 11:27:57 -07:00
Daniel Imms ea008411e4 Add missing EventEmitter.emit args/types 2017-05-21 11:25:18 -07:00
Daniel Imms e63fdf580b Clean up 2017-05-20 15:21:12 -07:00
Daniel Imms 597c6939c8 Handle basic double click select 2017-05-20 00:17:23 -07:00
Daniel Imms 293ae18a11 Fix copy after scroll 2017-05-20 00:01:55 -07:00
Daniel Imms 32b34cbe4c Handle copy 2017-05-19 23:53:20 -07:00
Daniel Imms 2f6d05f99d Handle scrolling of selection 2017-05-19 23:32:26 -07:00
Daniel Imms ad3ae67e70 Get selection partially rendering 2017-05-19 23:29:51 -07:00
Daniel Imms b594407cd5 Tell renderer to draw selection 2017-05-19 20:06:05 -07:00
Daniel Imms 207c4cf961 More work on SelectionManager 2017-05-19 19:57:52 -07:00
Daniel Imms fe11837a61 Create new entries when shifting 2017-05-19 19:11:28 -07:00
Daniel Imms fee912023f Merge remote-tracking branch 'ups/master' into 207_selection_manager 2017-05-19 18:38:24 -07:00
Daniel Imms a95ac990ff Merge pull request #450 from Tyriar/449_keep_span_pool
Keep row spans in an object pool to reduce garbage collection by reusing DOM nodes
2017-05-16 10:12:27 -07:00
Daniel Imms aad8439542 Fix lint errors 2017-05-16 10:07:55 -07:00
Daniel Imms 1a777edffe Add missing return type 2017-05-16 10:04:12 -07:00
Daniel Imms 537018f71f DomElementObjectPool jsdoc 2017-05-16 10:03:56 -07:00
Daniel Imms 67ca3a9334 More string interpolation 2017-05-16 09:57:50 -07:00
Daniel Imms 5fb30979c1 Add jsdoc back, was lost in merge 2017-05-14 14:28:06 -07:00
Daniel Imms cf863d4849 Use string interpolation 2017-05-14 14:26:45 -07:00
Daniel Imms 47814847f9 Remove var usages 2017-05-14 12:47:17 -07:00
Daniel Imms 9ec8974545 More clean up 2017-05-14 12:40:45 -07:00
Daniel Imms c887c6e0f6 Clean up 2017-05-14 12:27:06 -07:00
Daniel Imms 6c8949bba1 Only wrap single width unicode chars 2017-05-14 12:20:01 -07:00
Daniel Imms 508d3ab68c Merge remote-tracking branch 'ups/master' into 449_keep_span_pool 2017-05-14 12:09:47 -07:00
Paris Kasidiaris 6dad013783 Merge pull request #659 from jdanyow/paste
Normalize line endings on paste
2017-05-11 12:28:38 +03:00
Jeremy Danyow 0532e5cbf0 Normalize line endings on paste 2017-05-09 15:21:56 -07:00
Daniel Imms abcdf3e252 Merge branch 'master' into 449_keep_span_pool 2017-05-08 11:35:42 -07:00
Daniel Imms 3baa6b9237 Wrap all characters temporarily in spans to force fixed with
Fixes #467
2017-05-07 16:08:01 -07:00
Daniel Imms 23e6a475ee Merge remote-tracking branch 'origin/master' into 449_keep_span_pool 2017-05-07 15:49:40 -07:00
Daniel Imms 65256c8751 Support an entry ID in CircularList
This is needed to keep track of which row is which outside of the class
2017-05-05 20:24:25 -07:00
Daniel Imms b36d8780fd Clean up 2017-05-05 17:40:23 -07:00
Daniel Imms 70fda99415 Basic fetching of coordinates 2017-05-05 17:36:15 -07:00
Paris Kasidiaris 2221f70ff0 Bump version to 2.6.0
Signed-off-by: Paris Kasidiaris <paris@sourcelair.com>
2017-05-04 15:38:52 +00:00
Daniel Imms daad8d9c04 Merge pull request #652 from Tyriar/651_fix_travis_fail
Force failing mocha tests to return a exit code of 1
2017-05-04 08:35:37 -07:00
Daniel Imms 03d26e39c7 Force failing mocha tests to return a exit code of 1
Fixes #651
2017-05-03 10:06:53 -07:00
Paris Kasidiaris 64d3c089a2 Merge pull request #647 from Martin1994/orion_use
Add "Eclipse Orion" to "Real-World Uses".
2017-05-02 00:37:38 +03:00
Martin Chloride 938c011e63 Added "Eclipse Orion" to "Real-World Uses". 2017-05-01 17:12:07 -04:00
Paris Kasidiaris 66f6b88333 Merge pull request #645 from sourcelair/issue-#640-do-not-focus-on-open
Add `focus` argument in `Terminal.prototype.open`
2017-05-01 01:07:00 +03:00
Paris Kasidiaris 35f746c925 Add focus argument in Terminal.prototype.open
Fix #640
2017-04-26 10:37:07 +00:00
Paris Kasidiaris 944da28089 Merge pull request #643 from jsmecham/patch-1
Add "Terminal for Atom" to Real-World Uses
2017-04-24 17:43:20 +03:00
Justin Mecham 8f3e2b645a Added "Terminal for Atom" to Real-World Uses. 2017-04-23 14:36:01 -04:00
Daniel Imms 82b2a5305f Merge pull request #639 from Tyriar/638_retain_cursor_blink_interface
Keep cursor blink interval after a reset
2017-04-21 17:59:51 -07:00
Daniel Imms 6a2852ee26 Merge branch 'master' into 638_retain_cursor_blink_interface 2017-04-21 17:53:49 -07:00
Daniel Imms 526d6c4da9 Merge pull request #641 from Tyriar/support_demo_cursorStyle
Support cursorStyle selection in demo
2017-04-20 10:33:30 -07:00
Daniel Imms 46d62f4972 Support cursorStyle selection in demo 2017-04-19 18:16:48 -07:00
Daniel Imms 5cafcdb695 Keep cursor blink interval after a reset
Fixes #638
2017-04-19 11:22:18 -07:00
Paris Kasidiaris 2674258ead Merge pull request #635 from rstudio/master
Update README with RStudio under real-world uses.
2017-04-15 01:15:27 +03:00
Gary Ritchie b38be3a1d0 Update README with RStudio under real-world uses. 2017-04-14 10:50:15 -07:00
Daniel Imms 18a9f05626 Merge pull request #628 from saulcosta18/patch-1
Added Codevolve.com to README
2017-04-06 13:01:16 -07:00
Saul Costa bf34980c43 Added Codevolve.com to README
We use xterm.js for our courses at Codevolve.
2017-04-06 12:50:41 -07:00
Paris Kasidiaris d2f8f9dc1a Bump version to 2.5.0
Signed-off-by: Paris Kasidiaris <paris@sourcelair.com>
2017-04-05 18:48:57 +00:00
Daniel Imms df13469b6e Merge pull request #627 from Tyriar/591_element_on_validation
Give API access to valid link elements
2017-04-05 11:34:34 -07:00
Daniel Imms 584ec681af Merge remote-tracking branch 'ups/master' into 591_element_on_validation 2017-04-04 09:50:04 -07:00
Daniel Imms 0c5b5cca4a Merge pull request #623 from Tyriar/612_multiple_links_in_row
Support multiple link matches in a single row
2017-04-04 09:41:19 -07:00
Daniel Imms 64663bf4a8 Fix tests 2017-04-04 09:35:52 -07:00
Daniel Imms fe8334a873 Merge remote-tracking branch 'ups/master' into 612_multiple_links_in_row 2017-04-04 09:33:53 -07:00
Daniel Imms c74fd40e2a Merge pull request #619 from Tyriar/618_register_link_before_dom
Allow links to be registered before terminal is attached to DOM
2017-04-04 09:13:09 -07:00
Daniel Imms 0f8ff67451 Merge branch 'master' into 618_register_link_before_dom 2017-04-04 09:02:37 -07:00
Daniel Imms 8b0032c210 Merge branch 'master' into 612_multiple_links_in_row 2017-04-04 09:02:35 -07:00
Daniel Imms d9254ef2d9 Merge pull request #626 from Tyriar/625_cursor_blink_perf
Use setInterval over animation for cursor blink
2017-04-04 09:02:27 -07:00
Daniel Imms 66be05a962 Merge branch 'master' into 612_multiple_links_in_row 2017-04-04 09:00:47 -07:00
Daniel Imms 0e85a09dd3 Merge branch 'master' into 618_register_link_before_dom 2017-04-04 08:58:43 -07:00
Daniel Imms 0c4ba9fd67 Clear cursor blink interval on blur 2017-04-03 15:09:38 -07:00
Daniel Imms aca81c764a Use setInterval over animation for cursor blink
Fixes #625
2017-04-03 14:49:21 -07:00
Daniel Imms 6b3ddb97d1 Merge pull request #624 from InDieTasten/patch-1
Fix typos in comment
2017-03-31 13:27:29 -07:00
InDieTasten 26ccf2a316 Fix typos in comment 2017-03-31 22:16:11 +02:00
Daniel Imms 361894194a Merge branch 'master' into 612_multiple_links_in_row 2017-03-31 03:06:23 -07:00
Daniel Imms 8c2db8ddb8 More tests 2017-03-31 02:10:07 -07:00
Daniel Imms 5546baa927 Fix edge case 2017-03-31 01:54:21 -07:00
Daniel Imms d814009721 Add tests 2017-03-31 01:48:51 -07:00
Daniel Imms 08fd050c3f Support multiple link matches in a single row
Fixes #612
2017-03-31 01:48:35 -07:00
Daniel Imms 0b096d7d55 Merge pull request #621 from coderaiser/patch-1
Added Cloud Commander to real world uses
2017-03-30 11:31:11 -07:00
coderaiser 44cfb2dc87 Added Cloud Commander to real world uses 2017-03-30 14:26:56 +03:00
Daniel Imms b0624cadb7 Allow links to be registered before terminal is attached to DOM
Fixes #618
2017-03-24 11:04:04 -07:00
Paris Kasidiaris 045f361b0d Merge pull request #616 from LucianBuzzo/crop-lines
Prevent line data from being discarded on resize.
2017-03-20 09:52:22 +00:00
Lucian Buzzo cb77b9dc1a Prevent line data from being discarded on resize. 2017-03-19 08:26:37 +00:00
Paris Kasidiaris 0723dac596 Merge pull request #615 from andfoy/patch-1
Added Spyder IDE Terminal as a xterm case-use application
2017-03-19 00:26:41 +00:00
Edgar Andrés Margffoy Tuay 365f9b4c1b Added Spyder IDE as a xterm case-use application 2017-03-18 18:05:05 -05:00
Daniel Imms 2cf90d90f7 Merge pull request #610 from Tyriar/non_draggable_links
Ensure terminal links are not draggable
2017-03-17 09:22:05 -07:00
Daniel Imms da14f64d1b Merge branch 'master' into non_draggable_links 2017-03-17 02:27:07 -07:00
Paris Kasidiaris ef1b1c8ffc Merge pull request #608 from sourcelair/add-logo
Add xterm.js logo
2017-03-17 08:18:08 +00:00
Daniel Imms adde8ab2c3 Ensure terminal links are not draggable 2017-03-16 12:07:25 -07:00
Paris Kasidiaris 22cc8079dc Add xterm.js logo 2017-03-16 08:54:07 +00:00
Daniel Imms 6d25706300 Merge pull request #606 from Tyriar/vscode22640_tilde_path
Allow ~ char in linkify path and query fragments
2017-03-15 18:01:47 -07:00
Paris Kasidiaris 81419c980b Merge pull request #603 from sourcelair/fix-blurred-terminal-cursor
Fix cursor style on blurred terminals
2017-03-15 22:09:18 +00:00
Paris Kasidiaris 097e21f641 Merge pull request #604 from sourcelair/rebuild-on-css
Rebuild xterm.js even when CSS changes
2017-03-15 22:09:03 +00:00
Daniel Imms b68180b97b Allow ~ char in linkify path and query fragments 2017-03-15 13:55:57 -07:00
Daniel Imms d9059d18c0 Merge pull request #600 from sourcelair/Tyriar-patch-1
Polish development and contribution section
2017-03-15 10:53:03 -07:00
Paris Kasidiaris 5225a6e27a Rebuild xterm.js even when CSS changes
Otherwise you had to run `npm build` manually every time you changed a CSS file to see the updated result in the demo.
2017-03-15 18:36:06 +02:00
Paris Kasidiaris 9055bace96 Fix cursor style on blurred terminals 2017-03-15 18:28:17 +02:00
Paris Kasidiaris eb25e88754 Merge pull request #598 from mikesir87/fix-fit-calculation
Fixed characterHeight calculation, which could cause overflowing
2017-03-15 14:34:13 +00:00
Michael Irwin 0cb8ecc6f7 Use boundingClientRect instead of style to get actual height
PR #598
2017-03-15 09:06:56 -04:00
Daniel Imms 8cf76cc6e9 Polish development and contribution section 2017-03-14 15:15:36 -07:00
Michael Irwin 3c3a646d42 Fixed characterHeight calculation, which could cause overflowing
The previous calculation was simply using the height of the letter
W, but wasn't taking into account that each row has a lineHeight,
which might be greater. If the lineHeight is .4px off, after many
rows, it will cause the proposedGeometry to have an extra row,
potentially causing hidden rows (based on layout).
2017-03-14 17:02:53 -04:00
Paris Kasidiaris ff29fd69a3 Merge pull request #572 from AndrienkoAleksandr/gulpImprove
Use tsconfig.json data in gulpfile.js instead of hardcoded values.
2017-03-11 13:21:47 +00:00
Daniel Imms 91994a399c Merge pull request #582 from tmyt/propose/row-height
Support checking height in checkBoldBroken
2017-03-10 12:53:12 -08:00
Daniel Imms bef7137c92 Fix bug 2017-03-09 10:47:09 -08:00
Daniel Imms 11f62bab34 Expose setHypertextValidationCallback 2017-03-09 10:39:08 -08:00
Daniel Imms b66776a047 Include element in validationCallback
Fixes #591
2017-03-09 10:23:59 -08:00
Daniel Imms 2c151f444c Merge pull request #589 from warpdesign/patch-1
FIXED: typo
2017-03-08 15:52:55 -08:00
Nicolas Ramz d8f8b060cb FIXED: typo
`<div id="terminal"></div>` wasn't used, instead, a new one was created and used since getElementById('#terminal') returned null.
2017-03-09 00:47:29 +01:00
Daniel Imms ea9376019c Merge pull request #586 from AndrienkoAleksandr/cleanUpTsConfig
Clean up tsconfig.json.
2017-03-08 12:02:09 -08:00
Daniel Imms 008d1df106 Merge remote-tracking branch 'up/master' into cleanUpTsConfig 2017-03-08 11:47:56 -08:00
Daniel Imms ec91a6c7f4 Merge pull request #588 from Tyriar/587_csi_3_J
Implement CSI 3 J - Erase saved lines
2017-03-08 11:08:46 -08:00
yutaka 4b2ae6a79f Use rendered height for detect broken bold font. 2017-03-08 11:30:23 +00:00
yutaka 2ec756fd6b Use offsetWidth instead of scrollWidth
scrollWidth does not work on Chrome with `display: inline` element.
2017-03-08 05:11:26 +00:00
yutaka f0d5b40121 Detect bold font was broken correctly. 2017-03-08 04:54:18 +00:00
yutaka 31a0996b2f Revert "Set charMeasure.height values to each row height"
This reverts commit 43cb4f471f.
2017-03-08 00:27:22 +00:00
Daniel Imms 0bbd8f0a16 Implement CSI 3 J - Erase saved lines
Fixes #587
2017-03-07 14:01:42 -08:00
Paris Kasidiaris b1619e42fd Bump version to 2.4.0
Signed-off-by: Paris Kasidiaris <paris@sourcelair.com>
2017-03-07 09:38:48 +00:00
Daniel Imms 4cfec2fc09 Merge pull request #579 from Tyriar/578_ctrl_click_links
Require ctrl for link clicks
2017-03-06 07:21:58 -08:00
Aleksandr Andrienko 3d721a95bb Clean up tsconfig.json.
'include' field in the tsconfig.json defines source scope (top level of this scope is "src" folder), so we do not need 'exclude' folders in the root of the project, because this folders had already excluded by 'include' scope.

Signed-off-by: Aleksandr Andrienko <aandrienko@codenvy.com>
2017-03-06 13:17:01 +02:00
Aleksandr Andrienko dc3a136704 Use tsconfig.json data in gulpfile.js instead of hardcoded values.
Use tsconfig.json data in gulpfile.js instead of hardcoded values. Little code clean up

Signed-off-by: Aleksandr Andrienko <aandrienko@codenvy.com>
2017-03-06 12:25:22 +02:00
Daniel Imms 235ae2fbb9 Fix http link handler logic 2017-03-04 15:02:08 -08:00
Daniel Imms 1f518b0b45 Allow http link handler to fallback to standard handler 2017-03-04 14:44:11 -08:00
Daniel Imms ab34d53d6b Remove unused import 2017-03-04 14:09:13 -08:00
Daniel Imms aafb5333b9 Give event to handler 2017-03-04 14:08:05 -08:00
Daniel Imms 5e2791f981 Revert "Add fail handler"
This reverts commit 1ecc5674ac.
2017-03-04 14:01:40 -08:00
Daniel Imms 1ecc5674ac Add fail handler 2017-03-04 14:01:34 -08:00
Daniel Imms c9ad3a595a Merge pull request #584 from coderaiser/patch-1
Fix demo: wrong pid on windows #567
2017-03-03 14:38:55 -08:00
coderaiser 43558fa1dc Fix demo: wrong pid on windows #567 2017-03-03 23:40:36 +02:00
Daniel Imms 15a867d2b7 Support cmd+click on mac 2017-03-03 12:37:34 -08:00
yutaka 43cb4f471f Set charMeasure.height values to each row height 2017-03-03 07:57:05 +00:00
Daniel Imms 012051c1db Fix matchIndex on http 2017-03-02 17:35:10 -08:00
Daniel Imms b5483898da Merge remote-tracking branch 'upstream/master' into 578_ctrl_click_links 2017-03-02 17:34:30 -08:00
Daniel Imms c7b4c2beaf Require ctrl for link clicks
Fixes #578
2017-03-02 17:33:58 -08:00
Daniel Imms 8693d067e3 Merge pull request #577 from Tyriar/549_link_priority
Implement link matcher priorities
2017-03-02 15:24:09 -08:00
Daniel Imms 78d5fc9506 Fix bug with priority 2017-03-02 12:05:34 -08:00
Daniel Imms 7ac4f1a9be Implement link matcher priorities
Fixes #549
2017-03-02 11:25:27 -08:00
Daniel Imms 670fb01fbf Merge pull request #566 from Tyriar/linkify_issues
Linkify issues
2017-03-02 09:02:24 -08:00
Daniel Imms ec9608d54b Merge pull request #574 from Tyriar/link_validation_fixes
Link validation fixes
2017-03-02 00:42:01 -08:00
Daniel Imms dcffaf1c8c Ensure linkElement exists before firing validation callback 2017-03-01 09:38:59 -08:00
Daniel Imms efac5293c0 Use text cursor for invalid links
Part of #570
2017-03-01 09:37:30 -08:00
Daniel Imms c7563346a2 Merge pull request #571 from Tyriar/570_link_validation
Implement link validation
2017-03-01 09:12:53 -08:00
Daniel Imms 69906532b1 Remove test harness 2017-02-27 15:44:10 -08:00
Daniel Imms 9404fae725 Merge remote-tracking branch 'upstream/master' into linkify_issues 2017-02-27 12:59:59 -08:00
Daniel Imms 3542f15575 Don't expose CharMeasure/Linkifier for tests 2017-02-27 12:57:18 -08:00
Daniel Imms 15d79143e2 Get rid of exposed test method (phantom is gone) 2017-02-27 12:56:54 -08:00
Daniel Imms f6dafee748 Remove dead code 2017-02-27 12:52:54 -08:00
Daniel Imms 356d86ae74 Merge remote-tracking branch 'upstream/master' into 570_link_validation 2017-02-27 12:47:53 -08:00
Daniel Imms e673c6ec97 Upgrade to TypeScript 2.2 2017-02-27 11:54:55 -08:00
Daniel Imms 3e5fcf7004 Merge pull request #569 from wavebeem/issue/562
Fixes #562 adds Windows demo setup help
2017-02-27 11:52:47 -08:00
Daniel Imms 96d2a2bc68 Pin TypeScript at v2.1 to fix PhantomJS error 2017-02-27 11:47:57 -08:00
Daniel Imms b401992178 Add jsdom types 2017-02-27 11:22:59 -08:00
Daniel Imms 4c34351383 Remove PhantomJS completely 2017-02-27 11:22:50 -08:00
Daniel Imms 202b54afbb Convert CharMeasure phantom test to jsdom 2017-02-27 11:18:00 -08:00
Daniel Imms 26ebc3d944 Use jsdom instead of phantomjs for linkifier test 2017-02-27 11:00:12 -08:00
Daniel Imms d0f36e9939 Pin typescript 2.1 to prevent exports error 2017-02-27 10:02:12 -08:00
Daniel Imms 4c99c032ca Add tests 2017-02-27 09:47:36 -08:00
Daniel Imms 6198556e50 Implement link validation
Fixes #570
2017-02-27 08:35:59 -08:00
Brian Mock 514eba0e4e Fixes #562 adds Windows demo setup help 2017-02-25 12:16:18 -08:00
Daniel Imms 7279ee0f5f Support % in URLs 2017-02-24 08:56:42 -08:00
Daniel Imms ccadf3fcca Support percent and numbers in query/hash fragments 2017-02-23 10:02:18 -08:00
Daniel Imms cb80ce0d2c Link . and - in hash fragments and query strings 2017-02-23 09:55:05 -08:00
Daniel Imms 9aae43961f Support linking hash fragments 2017-02-23 09:53:20 -08:00
Daniel Imms b810687163 Support linking = in query strings 2017-02-23 09:42:59 -08:00
Daniel Imms 28d4ec779d Fix localhost links 2017-02-23 09:37:35 -08:00
Paris Kasidiaris cf4da6c748 Merge pull request #565 from sourcelair/issue-#531-coveralls
Fix #531: Submit code coverage to coveralls.io
2017-02-22 19:15:04 +02:00
Paris Kasidiaris e569b5669a Add coveralls badge 2017-02-22 16:22:50 +02:00
Paris Kasidiaris 695a9523fe Fix #531: Submit code coverage to coveralls.io 2017-02-22 16:17:22 +02:00
Daniel Imms 01b8e28a7d Merge pull request #556 from Tyriar/555_fix_mouse_coords
Use CharMeasure to determine mouse coordinates
2017-02-20 03:17:50 -08:00
Daniel Imms e221030a2b Merge pull request #559 from Tyriar/558_linkify_bad_trim
Remove bad trim optimization from Linkifier
2017-02-20 03:17:42 -08:00
Daniel Imms 88dc697392 Merge pull request #557 from Tyriar/CharMeasure_aria_hidden
Hide CharMeasure element from assistive technologies
2017-02-20 03:17:34 -08:00
Daniel Imms 99a270216c Remove bad trim optimization from Linkifier
This prevents whitespace from going missing after links are applied.

Fixes #558
2017-02-19 15:17:03 -08:00
Daniel Imms d06c920a2d Hide CharMeasure element from assistive technologies 2017-02-19 13:20:36 -08:00
Daniel Imms 5452986ce3 Use CharMeasure to determine mouse coordinates
Fixes #555
2017-02-19 12:34:36 -08:00
Daniel Imms ae69841174 Merge pull request #538 from Tyriar/455_linkify
Implement web links and custom link matcher registration
2017-02-17 11:40:59 -08:00
Daniel Imms e6fc80c13b Fix link matcher iteration direction 2017-02-17 10:22:55 -08:00
Daniel Imms a10ee8f58c Revert priority change, http handler needs to be top 2017-02-17 10:18:46 -08:00
Daniel Imms 3f69da2455 Support query string in linkifier 2017-02-17 10:06:35 -08:00
Daniel Imms a14a0834e9 Merge remote-tracking branch 'upstream/master' into 455_linkify 2017-02-17 10:05:22 -08:00
Daniel Imms c4f431849b Add a null check on linkifyRow 2017-02-16 22:41:08 -08:00
Daniel Imms ade3389cea Merge pull request #535 from Tyriar/534_phantomjs
Add PhantomJS test support and CharMeasure tests
2017-02-15 13:29:50 -08:00
Paris Kasidiaris 6873ba0909 Merge pull request #541 from AndrienkoAleksandr/FixSoftReset
Fix 'Autowrap Mode' after 'Soft Terminal reset' command.
2017-02-15 11:24:42 +02:00
Daniel Imms c3f31b5f65 Apply link matchers in reverse
This enables consumers to have full control.
2017-02-14 20:06:15 -08:00
Daniel Imms 3b62aa444b Improve jsdoc 2017-02-14 19:45:22 -08:00
Daniel Imms fb2b10b565 Merge pull request #545 from Martin1994/blink_cursor_color
Let blink animation support style inheritance
2017-02-14 19:23:22 -08:00
Martin Wang 345553012f Let blink animation support style inheritance
With this change, blinking cursor will use the same font and background
color that set before.
2017-02-14 12:35:39 -05:00
Daniel Imms fddc13d98e Merge pull request #544 from LucianBuzzo/master
Make fit plugin guard against non-existent parent element.
2017-02-13 13:26:14 -08:00
Lucian Buzzo aeb9f798c9 Make fit plugin guard against non-existent parent element. 2017-02-13 21:01:28 +00:00
Paris Kasidiaris 8fd44266a2 Bump version to 2.3.2
Signed-off-by: Paris Kasidiaris <paris@sourcelair.com>
2017-02-13 18:05:47 +00:00
Paris Kasidiaris ef4155fbf6 Merge pull request #542 from Tyriar/539_fix_initial_viewport_sync
Perform initial viewport sync after CharMeasure is ready
2017-02-13 20:05:03 +02:00
Daniel Imms 92f27d3144 Fix tests 2017-02-13 09:48:22 -08:00
Daniel Imms 5183332f6e Next link matcher ID must be non-static 2017-02-10 12:29:36 -08:00
Daniel Imms ee1de0980f Perform initial viewport sync after CharMeasure is ready
Fixes #539
2017-02-10 09:55:35 -08:00
Aleksandr Andrienko c53183d94b Fix default 'wraparoundMode' value for 'Soft reset'() XTERM terminal. 2017-02-10 16:52:19 +02:00
Daniel Imms 1ee774d01d Remove linkify test 2017-02-09 22:03:31 -08:00
Daniel Imms 1c030f57ef Force a refresh after register/deregister link matcher 2017-02-09 21:12:28 -08:00
Daniel Imms c8bb321654 Get custom link matcher working 2017-02-09 21:08:50 -08:00
Daniel Imms a1d71c9198 Properly queue linkify after refresh 2017-02-09 20:35:06 -08:00
Daniel Imms 55cb43d70b Add license 2017-02-09 20:12:35 -08:00
Daniel Imms 551159e0c3 Remove linkify addon 2017-02-09 20:10:01 -08:00
Daniel Imms 3bf31aa478 Polish 2017-02-09 20:09:47 -08:00
Daniel Imms 1316682343 Merge pull request #532 from AndrienkoAleksandr/cleanUpDependency
Remove excess library.
2017-02-09 20:01:46 -08:00
Daniel Imms 7167b06bf8 Add custom link handlers, use generic way for http handler 2017-02-08 20:44:22 -08:00
Daniel Imms 34582f9f65 Remove logs 2017-02-08 20:21:18 -08:00
Daniel Imms 8dc9ec5733 Only show underline on hover 2017-02-08 20:20:18 -08:00
Daniel Imms e8adf8ad2b Use gulp-mocha and attach run both test type in npm test 2017-02-08 20:06:14 -08:00
Daniel Imms 1306431e7c Add PhantomJS test support and CharMeasure tests
Fixes #534
2017-02-08 19:53:32 -08:00
Daniel Imms f7bc0fba4d Remove unnecessary variables 2017-02-08 18:57:45 -08:00
Paris Kasidiaris 639a2fd509 Bump version to 2.3.1
Signed-off-by: Paris Kasidiaris <paris@sourcelair.com>
2017-02-08 23:51:47 +00:00
Paris Kasidiaris b00083e0e5 Merge pull request #533 from vincentwoo/patch-2
Fix missing Number.isNaN in IE
2017-02-09 01:46:00 +02:00
Vincent Woo c3256556aa Fix missing Number.isNaN in IE
See https://github.com/sourcelair/xterm.js/pull/525#issuecomment-278497452
2017-02-08 15:33:20 -08:00
Paris Kasidiaris 8dd11f5587 Bump version to 2.3.0
Signed-off-by: Paris Kasidiaris <paris@sourcelair.com>
2017-02-08 22:24:56 +00:00
Daniel Imms 0f3ee21d9d Enable custom http link handlers 2017-02-08 14:13:30 -08:00
Daniel Imms a489037ec4 Improve node insertion, support custom link handlers 2017-02-08 13:50:36 -08:00
Aleksandr Andrienko c48388af14 Remove excess library.
Tsify lib used for browserify typescript sources. But folder /lib consist of js files has already compiled from TypeScript sources (by gulp task tsc). So Tsify library does nothing in this case.

Signed-off-by: Aleksandr Andrienko <aandrienko@codenvy.com>
2017-02-08 23:15:54 +02:00
Daniel Imms 2207d356f2 Initial linkify implementation
Part of 455
2017-02-08 11:11:52 -08:00
Daniel Imms 795cdb52dc Merge pull request #530 from Tyriar/fix_tests_on_windows
Disable escape sequence tests on windows
2017-02-08 10:41:06 -08:00
Daniel Imms e79e67c3ce Disable escape sequence tests on windows 2017-02-07 21:17:47 -08:00
Daniel Imms e0639e1b62 Merge pull request #526 from sourcelair/Tyriar-patch-1
Add description to package.json
2017-02-06 16:40:29 -08:00
Daniel Imms 56b541b44f Add description to package.json
This should fix the ugly broken markdown at the top of npm
2017-02-06 13:56:52 -08:00
Paris Kasidiaris 03e46fbdee Merge pull request #525 from LucianBuzzo/resize
Fix issue when resizing hidden terminals
2017-02-05 17:59:54 +02:00
Lucian Buzzo ee0d6b4533 Fixed issue where terminal would become unusable if NaN values are sent
to resize method.
2017-02-05 12:45:55 +00:00
Daniel Imms 937a9bf433 Merge pull request #523 from sourcelair/Tyriar-patch-1
Add null check on Terminal.element
2017-02-02 08:53:40 -08:00
Paris Kasidiaris 3bcdc4a354 Merge pull request #524 from billchurch/master
Adding WebSSH2 to real-world uses
2017-02-01 23:51:23 +02:00
Bill Church 382cd0bbf4 Merge pull request #1 from billchurch/billchurch-patch-1
Update README.md
2017-02-01 16:42:39 -05:00
Bill Church e58a6975da Update README.md
Adding WebSSH2 to real-word uses
2017-02-01 16:41:47 -05:00
Daniel Imms 833eb5216b Add null check on Terminal.element
Since the Terminal can be used now before being attached, we should check element on destroy.

Related to #266
2017-02-01 11:50:52 -08:00
Daniel Imms 742d28fcc3 Merge pull request #522 from Tyriar/266_pull_renderer_out
Pull renderer out of xterm.js
2017-02-01 11:33:52 -08:00
Daniel Imms cc78fdd63c Hold on to renderer if a reset occurs 2017-02-01 11:19:54 -08:00
Daniel Imms 20f22fb658 Convert flags to enum 2017-02-01 11:18:44 -08:00
Daniel Imms 3c635f3ce5 Remove Terminal.queueRefresh, call refresh directly 2017-02-01 11:13:00 -08:00
Daniel Imms 9d8f6c9ae7 Polish 2017-02-01 10:30:58 -08:00
Daniel Imms cc6893d0f2 Merge remote-tracking branch 'upstream/master' into 266_pull_renderer_out 2017-02-01 10:29:11 -08:00
Daniel Imms ca4e99e810 Add license 2017-02-01 10:21:43 -08:00
Daniel Imms 8d7df670b4 Merge pull request #521 from Tyriar/517_dont_loop_animation_frame
Don't requeue animation frame if not needed
2017-01-31 23:51:51 -08:00
Daniel Imms 4de7ed46bf Merge pull request #520 from Tyriar/519_erase_null_check
Add null checks in eraseRight and eraseLeft
2017-01-31 23:51:40 -08:00
Daniel Imms f9aac1bcc6 Fix tests 2017-01-31 23:46:58 -08:00
Daniel Imms 92068f36f2 Pull renderer out of xterm.js 2017-01-31 22:45:28 -08:00
Daniel Imms 047729ee9e Don't requeue animation frame if not needed
Fixes #517
2017-01-31 21:53:29 -08:00
Daniel Imms 637a5bd9cc Don't requeue animation frame if not needed
Fixes #517
2017-01-31 21:23:21 -08:00
Daniel Imms 6cd5c2b846 Add null checks in eraseRight and eraseLeft
Fixes #519
2017-01-31 21:03:13 -08:00
Daniel Imms 365a9f949c Merge pull request #513 from Tyriar/511_useFlowControl_option
Add useFlowControl option
2017-01-27 08:23:13 -08:00
Paris Kasidiaris d6de156cd8 Merge pull request #514 from LucianBuzzo/master
[issue #164] Fix bug that can occur when setting a scroll region
2017-01-27 12:58:57 +00:00
Lucian Buzzo 23c9fca088 [issue #164] Fix bug that can occur when setting a scroll region 2017-01-27 11:25:12 +00:00
Daniel Imms c2a69721f3 Add useFlowControl option
Fixes #511
2017-01-26 21:14:07 -08:00
Daniel Imms d1ad8a7831 Merge pull request #500 from Tyriar/fix_vt_tests
Fix many vt tests
2017-01-26 08:55:42 -08:00
Daniel Imms cee2285818 Merge pull request #509 from Tyriar/vscode_debugger_support
Support debugging mocha tests in vscode
2017-01-26 08:55:22 -08:00
Daniel Imms ca19da16e0 Merge pull request #499 from Tyriar/481_character_sets
Add support for rest of VT100 character sets
2017-01-25 13:00:49 -08:00
Daniel Imms 5850cbcd05 Add comments explaining actions preventing x from wrapping 2017-01-25 10:34:43 -08:00
Daniel Imms b789a7e85b Fix failing test given new auto wraparound behavior 2017-01-25 10:32:20 -08:00
Daniel Imms 9f0dbdd5c7 Support debugging mocha tests from vscode 2017-01-25 10:25:28 -08:00
Daniel Imms 98614f26ef Merge pull request #508 from Tyriar/507_fix_lint_in_src_root
Run lint on src/*.ts and fix errors
2017-01-25 07:30:09 -08:00
Daniel Imms 3b16696672 Run lint on src/*.ts and fix errors
Fixes #507
2017-01-24 13:34:47 -08:00
Paris Kasidiaris 0219ad2ed8 Merge pull request #505 from vincentwoo/patch-1
Add CoderPad to real world use cases
2017-01-24 07:48:37 +00:00
Vincent Woo e771483041 Add CoderPad to real world use cases
Hi everyone,

I'm very pleased to say I have cut CoderPad.io over to using xterm.js (replacing term.js). I've very pleased with the work you've done on it. You've implemented the many terrible hacks on top of term.js I had to write, except you've actually implemented stuff the right way.

I look forward to your continued progress on the project!

Cheers,
Vincent
2017-01-23 21:32:14 -08:00
Daniel Imms 4a7c3320fb Merge pull request #504 from Tyriar/fix_mode_recursive_call
Use InputHandler.setMode/resetMode, not Terminal
2017-01-23 12:41:19 -08:00
Daniel Imms 9359dca0f3 Use InputHandler.setMode/resetMode, not Terminal 2017-01-23 11:35:33 -08:00
Paris Kasidiaris c8ccc411d4 Merge pull request #502 from vincentwoo/master
Bundle top-level .js and .json files in NPM
2017-01-23 12:38:35 +02:00
Vincent Woo 537c8bad10 Bundle top-level JSON with npm 2017-01-22 22:28:46 -08:00
Vincent Woo 1a55367fc9 Bundle top-level JS with npm 2017-01-22 22:26:40 -08:00
Daniel Imms 4f7ee7dab0 Skip broken tests 2017-01-20 23:38:56 -08:00
Daniel Imms da16dd7cae Mock viewport.syncScrollArea 2017-01-20 23:22:04 -08:00
Daniel Imms 2aa7608ac0 Fix VPR behavior
Don't wrap cursor to column 0 after a VPR, fixes 1 test
2017-01-20 23:14:08 -08:00
Daniel Imms 74c3a4a6f7 Fix CUD behavior
Don't wrap cursor to column 0 after a CUD, fixes 1 test
2017-01-20 23:12:54 -08:00
Daniel Imms d30fb89fde Fix CUB behavior
Don't wrap cursor to column 0 after a CUB, fixes 3 tests
2017-01-20 23:11:22 -08:00
Daniel Imms 1b344ce789 Implement CAN (Cancel, ^X)
This helps partially pass t0014-CAN.in, it won't work 100% however
due to the very complex parsing logic of vanilla xterm that
gnome-terminal doesn't even seem to match. xterm's source seems to
have multiple ignore cases for a set of characters such that an
escape sequence like \e!!!!!! prints nothing as the ! continually
gets ignored. Our current logic is we ignore the escape sequence
immediately in this case and print the !.
2017-01-20 23:03:38 -08:00
Daniel Imms 99fbcd3d65 Fix LF/VT/FF behavior
Don't wrap cursor to column 0 after an index, fixes 2 tests
2017-01-20 22:14:18 -08:00
Daniel Imms 72063329fe Fix index behavior
Don't wrap cursor to column 0 after an index, fixes 2 tests
2017-01-20 21:49:39 -08:00
Daniel Imms adaf604ce0 Right trim expected output, we don't care for now
This fixes 2 tests
2017-01-20 21:39:27 -08:00
Daniel Imms d5f0fff523 Enable other tests and fix viewport check 2017-01-20 21:22:16 -08:00
Daniel Imms 43c3349494 Prevent wraparound mode from overriding last character 2017-01-20 21:20:11 -08:00
Daniel Imms b5d16ed7d7 Use block comment for SCLD 2017-01-20 20:14:17 -08:00
Daniel Imms aab67c2ed9 Reference CHARSETS using codes 2017-01-20 20:12:47 -08:00
Daniel Imms 0461a822c2 Add character sets for most languages
Part of #481
2017-01-20 19:05:13 -08:00
Paris Kasidiaris 7b87e78ed3 Merge pull request #497 from sourcelair/issue-#488-tabstopwidth
Implement configurable tab stop width
2017-01-21 01:08:23 +02:00
Paris Kasidiaris 6e1628bc3b Fixes on demo based on configurable tabStopWidth 2017-01-21 00:32:39 +02:00
Daniel Imms 370c5a971d Merge pull request #498 from Tyriar/259_fix_setupStops_in_resize
Fix setupStops call in resize
2017-01-20 14:29:13 -08:00
Daniel Imms 9a86e4e109 Fix setupStops call in resize
Fixes #259
2017-01-20 14:23:45 -08:00
Paris Kasidiaris 8cb46f2726 Move tabStopWidth option callback into setOption 2017-01-20 16:19:19 +00:00
Paris Kasidiaris f4293a6dfa Fix #488: Implement configurable tab width 2017-01-20 16:18:19 +00:00
Daniel Imms 1c759f1d6f Merge pull request #495 from Tyriar/494_display_none_resize_fix
Only keep > 0 geometry in CharMeasure
2017-01-20 07:26:46 -08:00
Daniel Imms 6918e83787 Merge pull request #492 from Tyriar/480_cursor_style
Implement CSI Ps SP q Set cursor style
2017-01-20 07:26:36 -08:00
Daniel Imms 9626b1e39d Only keep > 0 geometry in CharMeasure
Fixes #494
2017-01-19 18:02:09 -08:00
Daniel Imms 9227aed906 Add InputHandler.setCursorStyle test 2017-01-18 10:30:35 -08:00
Daniel Imms 0bd469f505 Implement CSI Ps SP q Set cursor style
Fixes #480
2017-01-18 10:06:57 -08:00
Daniel Imms 5bd803382a Merge pull request #462 from Tyriar/459_parser__on_460
Parser refactor and optimization
2017-01-18 10:02:03 -08:00
Daniel Imms ef60e50e1d Add documentation 2017-01-18 09:35:27 -08:00
Daniel Imms ed1ba14c6b Merge pull request #490 from Tyriar/489_namespace_cursor_blink
Namespace cursor-blink CSS animations
2017-01-18 08:18:23 -08:00
Daniel Imms a4080d3706 Namespace cursor-blink CSS animations
Fixes #489
2017-01-18 08:06:41 -08:00
Daniel Imms e2b2f98011 Merge remote-tracking branch 'upstream/master' into 459_parser__on_460 2017-01-16 15:17:35 -08:00
Daniel Imms ed13218f90 Merge pull request #485 from Tyriar/remove_comments_in_build
Remove comments in build
2017-01-16 15:14:42 -08:00
Daniel Imms 1cdd2e00ab Remove parser states from xterm.js 2017-01-16 14:11:00 -08:00
Daniel Imms 94eb08fb73 Remove comments in build 2017-01-16 14:02:55 -08:00
Daniel Imms 5e1ccb358b Merge remote-tracking branch 'upstream/master' into 459_parser__on_460 2017-01-16 13:53:19 -08:00
Daniel Imms 259338b8c2 Merge pull request #482 from Tyriar/348_keep_selection_on_mod_key
Don't drop selection on modifier keyup
2017-01-16 03:49:19 -08:00
Daniel Imms a05bb50230 Don't drop selection on modifier keyup
Fixes #348
2017-01-15 21:41:42 -08:00
Daniel Imms 8a07b8c798 Merge pull request #442 from Tyriar/441_windows_support
Use node-pty for Windows support
2017-01-15 02:38:16 -08:00
Daniel Imms 4d5046b5e7 Merge pull request #479 from Tyriar/478_cursorBlink_refresh
Apply cursorBlink via setOption
2017-01-15 02:35:30 -08:00
Daniel Imms 12160a50cb Fake xterm.element in tests 2017-01-15 02:31:54 -08:00
Daniel Imms 375ccbd9e2 Merge remote-tracking branch 'upstream/master' into 478_cursorBlink_refresh 2017-01-15 02:22:45 -08:00
Daniel Imms cc0f0d94f6 Merge remote-tracking branch 'upstream/master' into 441_windows_support 2017-01-15 02:12:17 -08:00
Paris Kasidiaris 6309752208 Merge pull request #435 from sourcelair/issue-#359-gulp
Introduce build system based on Gulp
2017-01-15 11:49:32 +02:00
Daniel Imms 4448ebe391 Use charsets to map 2017-01-14 22:06:30 -08:00
Daniel Imms d1dac57f7a Move cursor blink style to class on .terminal 2017-01-14 21:26:49 -08:00
Daniel Imms 3422f84e00 Merge pull request #477 from Tyriar/476_scrollback_update
Apply scrollback via setOption
2017-01-14 10:46:54 -08:00
Paris Kasidiaris ac6faf3dd8 Stick gulp-sourcemaps to 1.9.1
Paths became absolute by default at https://github.com/floridoo/gulp-sourcemaps/commit/fb4027a496898b8b7992ea5af9b3d707f1d82d38
2017-01-14 19:52:07 +02:00
Daniel Imms 7679475bfe Apply cursorBlink via setOption
Fixes #478
2017-01-13 21:31:31 -08:00
Daniel Imms 5a932b2a20 Apply scrollback via setOption
Fixes #476
2017-01-13 21:27:19 -08:00
Daniel Imms b1ef934aea Merge pull request #474 from Tyriar/473_refresh_null_checks
Add null checks to refresh line and character fetches
2017-01-13 07:09:12 -08:00
Daniel Imms a2ab0ffc46 Merge pull request #471 from Tyriar/470_CharMeasure_in_helper
Move CharMeasure element to the helper container
2017-01-13 07:08:45 -08:00
Daniel Imms ecd7e69f1a Start adding ESC state handler 2017-01-12 22:36:48 -08:00
Daniel Imms fa3484cd14 Pull character add char code to input handler 2017-01-12 22:17:36 -08:00
Daniel Imms f4846aa154 Finish CSI codes 2017-01-12 21:58:50 -08:00
Daniel Imms 9b66208029 Add more CSI codes 2017-01-12 21:33:28 -08:00
Daniel Imms c43c3b411a Add more CSI codes 2017-01-12 20:51:15 -08:00
Daniel Imms f9a286a851 Convert more CSI codes 2017-01-12 20:32:15 -08:00
Daniel Imms 3de3912b96 Add another null check on children[y] 2017-01-12 11:23:17 -08:00
Daniel Imms 23169e89c8 Add null checks to refresh line and character fetches
Fixes #473
2017-01-12 11:14:14 -08:00
Daniel Imms 0f5f34e8f5 Move CharMeasure element to the helper container
Fixes #470
2017-01-12 10:47:57 -08:00
Daniel Imms 342e862d69 Merge remote-tracking branch 'upstream/master' into 459_parser__on_460 2017-01-11 13:43:33 -08:00
Paris Kasidiaris 081fe3f301 Fix #359 - Introduce build system based on Gulp
- Add Gulp and new dependencies to `package.json`
- Add `gulpfile.js` with four tasks:
    - `tsc`: For building TypeScript sources
    - `bundle`: For bundling JavaScript modules in a monolith
    - `sorcery`: For resolving the source map chains back to the original TypeScript files
    - `build` (`default`): Runs the whole `tsc` → `bundle` → `sorcery` chain
- Clean up `Dockerfile`, since `cpio` is not needed any more
- Clean up not needed dependencies from `package.json`
- Remove `bin/build`
- Update `bin/release` to use `npm run build` instead of `./bin/build`
2017-01-11 08:11:45 +00:00
Daniel Imms fc528f6c0c Merge pull request #447 from Tyriar/425_xon_xoff_on_280
Sync with pty process using XOFF and XON
2017-01-10 21:20:58 -08:00
Daniel Imms 411c974960 Merge pull request #446 from Tyriar/335_viewport_var
Remove var usage from Viewport
2017-01-10 20:24:22 -08:00
Daniel Imms 0ec125557a Merge remote-tracking branch 'upstream/master' into 425_xon_xoff_on_280 2017-01-10 20:24:07 -08:00
Daniel Imms 0b977fc35f Merge pull request #461 from Tyriar/460_escape_sequence_c0
Pull C0 escape sequences into its own file
2017-01-10 20:06:36 -08:00
Daniel Imms 0739a4be06 Merge remote-tracking branch 'upstream/master' into 425_xon_xoff_on_280 2017-01-10 20:04:26 -08:00
Daniel Imms c4de07135f Merge pull request #466 from Tyriar/465_async_CharMeasure_measure
Perform CharMeasure.measure async
2017-01-10 20:03:59 -08:00
Daniel Imms d52b53889e Merge remote-tracking branch 'upstream/master' into 335_viewport_var 2017-01-10 20:03:19 -08:00
Daniel Imms d9682bd6d6 Perform CharMeasure.measure async
This fixes the edge case where getBoundingClientRect was returning a width and
height of 0,0.

Fixes #465
2017-01-10 11:36:22 -08:00
Daniel Imms 411b80cdc0 Add CSI @, E, F, G 2017-01-09 23:06:36 -08:00
Daniel Imms db81c28bd7 Support more CSI codes, remove unused CSI helper functions 2017-01-09 22:57:38 -08:00
Daniel Imms c7fa2d55b4 Handle remaining CSI_PARAM cases and introduce the state 2017-01-09 16:07:27 -08:00
Daniel Imms 7572dd5fa6 Remove IInputHandler from CSI_PARAM lambdas 2017-01-09 15:39:56 -08:00
Daniel Imms 9942477bff Start converting CSI codes 2017-01-09 15:38:54 -08:00
Daniel Imms 635a4d7682 Handle CSI postfix 2017-01-09 15:19:29 -08:00
Daniel Imms 672dc1a251 Start pulling CSI handlers out 2017-01-09 15:17:15 -08:00
Daniel Imms 6deaaa8ef8 Move all parser state handling into the parser 2017-01-09 15:00:37 -08:00
Daniel Imms f4267c4f74 Merge remote-tracking branch 'upstream/master' into 459_parser__on_460 2017-01-09 14:50:28 -08:00
Daniel Imms 1848fff4c9 Pull charsets out of xterm.js 2017-01-09 14:21:59 -08:00
Daniel Imms 991b2843d3 Fix references to Terminaal and fix TS lint warnings 2017-01-09 13:57:58 -08:00
Daniel Imms 940bd2699e Fix wcwidth TS errors 2017-01-09 13:48:52 -08:00
Daniel Imms 4a27b9a53f Move wcwidth into Parser.ts 2017-01-09 13:46:08 -08:00
Daniel Imms a31921ae73 Move parsing logic into Parser.ts 2017-01-09 13:45:55 -08:00
Daniel Imms 27a7f1504c Merge pull request #453 from Tyriar/452_disable_stdin_option
Add disableStdin option
2017-01-09 09:53:22 -08:00
Paris Kasidiaris aee8ddeafb Merge pull request #464 from TylerJewell/patch-1
Add Codenvy to the list of xTerm supporters
2017-01-09 19:17:50 +02:00
Tyler Jewell 6c29ccfbd1 Add Codenvy to the list of xTerm supporters 2017-01-08 17:41:02 -08:00
Daniel Imms da7ea104de Merge remote-tracking branch 'upstream/master' into 460_escape_sequence_c0 2017-01-08 17:13:28 -08:00
Daniel Imms 665a11acc0 Add C0.BS handler back 2017-01-08 17:12:35 -08:00
Daniel Imms 6b5ced0078 Merge remote-tracking branch 'upstream/master' into 449_keep_span_pool 2017-01-08 17:09:23 -08:00
Daniel Imms dddc91fbcc Merge remote-tracking branch 'upstream/master' into 425_xon_xoff_on_280 2017-01-08 17:05:19 -08:00
Daniel Imms ecb892d995 Merge remote-tracking branch 'upstream/master' into 425_xon_xoff_on_280 2017-01-08 17:05:10 -08:00
Daniel Imms ada96a8373 Merge remote-tracking branch 'upstream/master' into 335_viewport_var 2017-01-08 17:04:18 -08:00
Daniel Imms cabe23b782 Merge pull request #436 from Tyriar/434_tmux_scroll_fix
Fix scroll in tmux with max scrollback
2017-01-08 17:02:44 -08:00
Daniel Imms b25bf9562e Merge pull request #457 from Tyriar/451_fix_events_after_reset
Keep previous terminal events after a reset
2017-01-08 17:00:55 -08:00
Daniel Imms e9da2a6330 Merge pull request #440 from Tyriar/439_wide_char_class
Style wide characters at charWidth*2 in DOM
2017-01-08 17:00:30 -08:00
Daniel Imms 9385b9309d Merge remote-tracking branch 'upstream/master' into 439_wide_char_class 2017-01-08 16:56:47 -08:00
Daniel Imms facc2659e7 Merge pull request #463 from sourcelair/revert-445-444_rate_limit_viewport_refresh
Revert "Rate limit Viewport.refresh"
2017-01-08 16:51:27 -08:00
Daniel Imms 06852cd904 Revert "Rate limit Viewport.refresh" 2017-01-08 16:46:23 -08:00
Daniel Imms a1ee8aefbb Merge remote-tracking branch 'upstream/master' into 439_wide_char_class 2017-01-08 16:32:51 -08:00
Daniel Imms eb3b3fa107 Merge pull request #437 from Tyriar/335_TS_Generic_Browser
Convert browser and generic to TS
2017-01-08 16:25:30 -08:00
Daniel Imms 3457b73ac8 Merge pull request #445 from Tyriar/444_rate_limit_viewport_refresh
Rate limit Viewport.refresh
2017-01-08 16:24:27 -08:00
Daniel Imms 28b38dc76d Merge pull request #438 from Tyriar/280_improve_refresh_queue
Improve refresh queue
2017-01-08 16:24:16 -08:00
Daniel Imms 659fb90c1c Handle C0 codes via handler map in Parser 2017-01-08 16:21:51 -08:00
Daniel Imms 04b1ebf1a7 Create InputHandler which performs C0 actions
Part of #459
2017-01-08 16:11:35 -08:00
Daniel Imms 1685cf2d8f Use namespace and const for C0 constants 2017-01-08 16:02:47 -08:00
Paris Kasidiaris 43d4d4f2bb Merge pull request #458 from TylerJewell/patch-1
Add additional products that use xTerm
2017-01-08 23:49:46 +00:00
Daniel Imms 3de3c0c6b6 Pull C0 escape sequences into its own file
Part of #460
2017-01-08 15:49:19 -08:00
Daniel Imms bebe43e5e1 Use node-pty as it's been published 2017-01-08 13:33:51 -08:00
Tyler Jewell 520f2bc9d6 Add additional products that use xTerm
Hi xTerm team. Over the past 6 months, we have been studying xTerm as a replacement for Eclipse Che's term.js. It's taken us a bit of an effort, but the work is now done!  You can expect that Eclipse Che engineers (abot 60 contributors) will make direct contributions back to xTerm ongoing now that we are fully integrated in. We integrate xTerm alongside GWT.  As we more natively integrate the solution, we'll make additional PRs directly back to the project.

As background on the research and integration work that we did, these are the postings from our engineering team's research from the past six months. Some of these issues have already been resolved, or in the process of being resolved.

We also plan a couple of blog posts to our forums (~250K followers) about xTerm integration later in Q1 after we make our 5.0.0 announcements. I could also add Codenvy to this PR as well since it has been released with the version based upon xTerm, but with Sourcelair being the initiators we didn't want to make it seem like we were trying to be competitive. This PR is about the promotion and success of xTerm, for which we are fully committed to.

## Xtermjs 
This document is result of investigation using xtemjs ui terminal instead of current term.js. It contains analyze pluses and minuses using xtermjs (release version 2.2.3) and technical problems.  Investigation issue: https://github.com/eclipse/che/issues/3210

## Description
For now we are using our own fork of the https://github.com/chjj/term.js for user interface websocket-terminal. But actually this project is no longer maintained. So we can not get new releases from this project and we need support this script on our own. Original project contains link for a maintained fork https://github.com/sourcelair/xterm.js. This fork uses MIT license and community intensive develops this project. It has such users like: Microsoft Visual Studio Code, SourceLair, ttyd. xterm.js had already done 46 releases https://github.com/sourcelair/xterm.js/releases.

## Technical Advantages of xTerm:
* xterm.js has js tests. We have not any test for old term.js.
* We can periodically update xtem.js by new release.
* Added parameter to cancel browser events.
* Default 256 colours moved from js to css.
* Added ability to set terminal theme.
* Improved resize mehanizm (added resize event).
* Added fit.ts script to fits terminal size to original height and width of parent div.
* Fixed lost text selection from current active line(when blink is enabled).
* Improved copy/paster mehanizm.
* Linkify URL feature.
* Implement moving back and forward across words with "Alt + ←" and "Alt + →" respectively.
* Improved special key handling.
* Added addons to exdens xterm.js
* Improved mehanizm copy/paster from clipboard.
* Fixed cross platform input problems(For IPad, Iphone, MacIntel, MacPPC and so on).
* Did some work to support UTF-8 symbols.
* Implemented scrollbar.
* Fixed incorrect mouse position for application with  pseudo-graphic user interface (for example Midnight Commander).
* Fix to prevent terminal scrolling when user is looking into scrollback(similar to gnome-terminal, if the user is scrolling up to look at past output and the currently running program adds output to the terminal, the viewport of xterm.js should not scroll and interrupt what they are looking at.)
* Make right-click work on all browsers.

##  Broken Changes: 
We can move on our changes to realize copy/paste by hotkeys Ctrl + C/Ctrl +V. But xterm.js support hotkeys more common for terminal: Ctrl + Insert and Shift + Insert and maybe that's enough.

Drop support for old mouse wheel APIs: all browsers have supported the WheelEvent (onwheel) for sometime now, since Firefox does not support onmousewheel which is also non-standard but works with the standard interface, it makes sense to drop support now.

## Browser Support
Since xterm.js is typically implemented as a developer tool, only modern browsers are supported officially. Here is a list of the versions to support:
Chrome 48+
Edge 13+
Firefox 44+
Internet Explorer 11+
Opera 35+
Safari 8+

Xterm.js works seamlessly in Electron apps and may even work on earlier versions of the browsers but these are the browsers xterm.js developers strive to keep working.

## Known major bugs:
https://github.com/sourcelair/xterm.js/issues/307
https://github.com/sourcelair/xterm.js/issues/362
https://github.com/sourcelair/xterm.js/issues/348
https://github.com/sourcelair/xterm.js/issues/325 Data loss when resizing terminal (but this bug is exist in the current terminal ui in the CHE too). Community has pull request to fix this issue for xtermjs https://github.com/sourcelair/xterm.js/pull/404 .
 
Additional information: 

Code base xterm.js consist of files written on native javascript and typescript. For manage js dependency and configuration used npm, typings, bower and node. Development tendency: rewrite xtemr.js completely on the Typescript(information about this included to the release notes 2.2.3).
2017-01-08 09:44:14 -08:00
Daniel Imms 996c25a364 Keep previous terminal events after a reset
This broke in the EventEmitter ts conversion, just reverts this line to what
it used to be.

Fixes #451
2017-01-07 16:04:49 -08:00
Daniel Imms d9d60063d7 Add disableStdin option
Fixes #452
2017-01-04 12:43:21 -08:00
Daniel Imms ff523bfc79 Fix tests 2017-01-04 10:30:04 -08:00
Daniel Imms c7e8498166 Clear row and return spans before generating row 2017-01-04 09:44:58 -08:00
Daniel Imms 2b4c019f13 Use object pool in refresh 2017-01-04 09:32:23 -08:00
Daniel Imms 335850380e Implement DomElementObjectPool with tests 2017-01-04 08:50:19 -08:00
Daniel Imms 94c01ec378 Fix tests 2017-01-04 08:08:59 -08:00
Daniel Imms 2b8820fdac Further tweaks, add write batching 2017-01-03 15:52:45 -08:00
Daniel Imms 0ec7b661f2 Tweak config values 2017-01-03 15:38:14 -08:00
Daniel Imms dc5efa886a Make innerWrite iterative 2017-01-03 15:29:23 -08:00
Daniel Imms e66b1c5704 Add write buffer pause and refresh frame skip 2017-01-03 15:26:41 -08:00
Daniel Imms 6b8c43ed7f Send \x13 when a write buffer threadhold is reached 2017-01-03 14:57:13 -08:00
Daniel Imms b9d374affa Add XON/XOFF and eparate write from processing
Part of #425
2017-01-03 14:57:04 -08:00
Daniel Imms d2ef19dffe Remove var usage from Viewport
Part of #335
2017-01-03 11:26:59 -08:00
Daniel Imms efdf37b887 jsdoc 2017-01-03 11:23:36 -08:00
Daniel Imms 6ffb8f545b Rate limit Viewport.refresh
This prevents 1000 scroll events from firing when the buffer is not full

Fixes #444
2017-01-03 11:18:38 -08:00
Daniel Imms 2e4147b4fa Use Tyriar/pty.js for Windows support
Fixes #441
2017-01-01 16:24:55 -08:00
Daniel Imms 07a8b8f4f7 Fix tests 2016-12-31 18:06:49 -08:00
Daniel Imms 74483fb294 Use CharMeasure in Viewport and to style wide chars
Fixes #439
2016-12-31 15:56:57 -08:00
Daniel Imms 4f18d842f9 Add CharMeasure util class 2016-12-31 15:46:17 -08:00
Daniel Imms 188e197e20 Wrap wide chars in a span/class
Part of #439
2016-12-31 15:19:54 -08:00
Daniel Imms 7234bfb6eb Move row evaluation into refreshLoop
This allows the refresh public API to still function
2016-12-31 14:58:17 -08:00
Daniel Imms 97feb3321c Improve refresh queue
Use requestAnimationFrame in addition to a queue to refresh every animation
frame but only when a refresh is needed.

Fixes #280
Fixes #290
2016-12-31 14:14:02 -08:00
Daniel Imms 3e1a607179 Convert browser and generic to TS
Part of #335
2016-12-31 07:36:56 -08:00
Daniel Imms c3f46e4a6b Fix scroll in tmux with max scrollback
I believe this is related to when scrollBottom is not the last row in the viewport

Fixes #434
2016-12-31 07:18:50 -08:00
Daniel Imms 1fbd3c8f65 Merge pull request #432 from Tyriar/335_TS_EventEmitter
Convert EventEmitter to TypeScript and add tests
2016-12-31 07:14:47 -08:00
Daniel Imms 965ebde22f Remove remaining removeListener ref 2016-12-31 07:11:47 -08:00
Daniel Imms 0d9d3aca1a Remove addListener/removeListener 2016-12-31 07:10:45 -08:00
Daniel Imms 937cf43e09 Remove redundant assignment in constructor 2016-12-31 07:07:45 -08:00
Daniel Imms 23472e6fb4 Merge pull request #431 from Tyriar/427_refresh_scroll_on_buffer_change
Sync scroll bar when alt/normal buffer is switched in
2016-12-30 04:42:36 -08:00
Antonis Kalipetis 445728b2a2 Merge pull request #403 from akalipetis/docker-enhancement
Improve the Docker image
2016-12-30 11:24:22 +02:00
Antonis Kalipetis 61c213f9e3 Ignore all the build directories in Docker
This allows for reproducible builds.
Also, fix Dockerfile to first build and then run tests and a typo in the entrypoint.

Thanks @BenHall for reporting the issue with the Docker build!
2016-12-29 16:13:58 +02:00
Daniel Imms dba432d1b6 Add tests 2016-12-29 05:09:47 -08:00
Daniel Imms 2f416371bd Convert EventEmitter to TS
Pat of #335
2016-12-29 04:53:57 -08:00
Daniel Imms 2bfbdb1c06 Sync scroll bar when alt/normal buffer is switched in
Fixes #427
2016-12-29 03:55:36 -08:00
Antonis Kalipetis a4c958c54f Fix EOL character in .editorconfig and update docker-compose 2016-12-28 18:49:36 +02:00
Antonis Kalipetis b2e257bf37 Remove entrypoint
Signed-off-by: Antonis Kalipetis <akalipetis@gmail.com>
2016-12-28 18:45:21 +02:00
Antonis Kalipetis 6b6314227e Improve the Docker image
1. Bump Node version to 6.9 - the latest LTS
2. Include the `cpio` binary, used during building
3. Ignore node_modules and .git directories for faster builds
4. Run the tests and build during the image build, to make sure the image is not built if these break
5. Make npm run dev the default command
6. Add an entrypoint to automatically install Node modules if they do nott exist

Signed-off-by: Antonis Kalipetis <akalipetis@gmail.com>
2016-12-28 18:45:18 +02:00
Antonis Kalipetis 1e0eb3784e Merge pull request #402 from akalipetis/hotfix-npm-scripts
Improve the package.json scripts
2016-12-28 18:44:20 +02:00
Antonis Kalipetis f00ea4bfac Add a Procfile with the npm start command
Also, update the Procfile.dev with the updated `npm run dev` command.

Signed-off-by: Antonis Kalipetis <akalipetis@gmail.com>
2016-12-28 18:12:23 +02:00
Antonis Kalipetis 0da4c9d90d Improve the package.json scripts
1. Include a `prestart` script, which runs the build
2. Make the `start` script just start the demo server
3. Add a new `dev` script, which contains the nodemon logic
4. nodemon now watches for changes in `*.ts` files and runs the start command

Signed-off-by: Antonis Kalipetis <akalipetis@gmail.com>
2016-12-28 18:12:20 +02:00
Daniel Imms df83298c54 Merge pull request #429 from Tyriar/428_scrollback_exception
Ensure ydisp does not drop below 0 in refresh
2016-12-28 05:59:43 -08:00
Daniel Imms 5d29d4ea22 Remove log 2016-12-28 03:41:48 -08:00
Daniel Imms a9e1ed5a72 Ensure ydisp does not drop below 0 in refresh
Fixes #428
Related to #385
2016-12-28 03:41:12 -08:00
Daniel Imms e6ad8d8b50 Merge pull request #422 from Tyriar/361_circular_list_scrollback
Use a circular list to manage buffer
2016-12-28 03:10:31 -08:00
Daniel Imms 50cfe2b21a Improve header comment 2016-12-27 13:40:52 -08:00
Daniel Imms d7f2ec89d7 Polish 2016-12-24 03:17:30 -08:00
Daniel Imms cc5ae8193b Fix insertLines as max buffer size 2016-12-24 00:26:43 -08:00
Daniel Imms 30a1f1ccb5 Fix issue with max buffer size after vim fix 2016-12-24 00:14:30 -08:00
Daniel Imms 6f7cb990ca Fix issue with vim scrolling 2016-12-24 00:04:31 -08:00
Paris Kasidiaris 06f5746c86 Merge pull request #423 from katacoda/katacoda-integration
Katacoda xterm.js integration is live
2016-12-22 23:30:00 +02:00
Ben Hall cd44fe7bcd Katacoda xterm.js integration is live 2016-12-22 20:10:33 +00:00
Ben Hall bb3d6ee72c Katacoda xterm.js integration is live 2016-12-22 12:01:44 +00:00
Daniel Imms bf16fdc01d Have reverseIndex use CircularList.shiftElements 🎉 2016-12-21 03:26:40 -08:00
Daniel Imms 969a2e953c Implement CircularList.shiftElements 2016-12-21 03:18:30 -08:00
Daniel Imms bfaf9cd6eb Doc and testing 2016-12-21 02:39:21 -08:00
Daniel Imms be2e4b5d85 Add missing return type 2016-12-21 02:11:14 -08:00
Daniel Imms cc0fc953cf Remove logs 2016-12-22 02:04:31 -08:00
Daniel Imms 3b35d12e60 Fix issue with the git log
The old code was assuming that the buffer was not going to change, this is not
true with the current impl though where the list is shifted and ybase and ydisp
need to be compensated for that.
2016-12-22 02:02:05 -08:00
Daniel Imms 4bc679b0b3 Merge remote-tracking branch 'upstream/master' into 361_circular_list_scrollback 2016-12-21 15:43:18 -08:00
Daniel Imms be56c72b5f Fix when scrollback limit is reached 2016-12-21 15:43:04 -08:00
Paris Kasidiaris db92840ffc Merge pull request #415 from saswatds/saswatds-patch-1
Update README.md
2016-12-19 15:47:19 +02:00
Paris Kasidiaris 593a4ec0b0 Merge pull request #417 from BobReid/os_x_bash_arrow_fix
changed escape sequence for alt-arrow to work on bash on os x
2016-12-19 15:45:57 +02:00
Saswat Das d018a547a2 Update README.md
Added Getting started content from the xtermjs.org website
2016-12-18 04:48:19 +05:30
Paris Kasidiaris a246200675 Merge pull request #419 from blink1073/clean-up-npm-publish
Clean up npm publish
2016-12-16 20:46:27 +00:00
Steven Silvester 1cb6517812 Revert changes to other published files 2016-12-16 14:41:29 -06:00
Bob Reid 2824da371f Added macOS check to escape sequences and updated tests 2016-12-16 15:03:49 -05:00
Steven Silvester 22eb86089f Clean up npm publish 2016-12-16 13:28:10 -06:00
Bob Reid 2e17366bce changed escape sequence for alt-arrow to work on bash on os x 2016-12-16 10:29:42 -05:00
Paris Kasidiaris ef407ae93a Bump version to 2.2.3
Signed-off-by: Paris Kasidiaris <paris@sourcelair.com>
2016-12-15 21:56:32 +00:00
Paris Kasidiaris 9032ce99d3 Merge pull request #416 from sourcelair/issue-#414-css-files-in-npm
Include appropriate files in npm.
2016-12-15 21:53:19 +00:00
Paris Kasidiaris adaaeb6f36 Include appropriate files in npm.
Fix #414
2016-12-15 23:49:43 +02:00
Paris Kasidiaris 8a146b5401 Bump version to 2.2.2
Signed-off-by: Paris Kasidiaris <paris@sourcelair.com>
2016-12-15 17:16:04 +00:00
Saswat Das 4ee1e00808 Update README.md
Adding a getting started instruction for adding xterm to a parent div and listening for data output from xterm
2016-12-15 21:26:04 +05:30
Paris Kasidiaris 3a6368ba6c Merge pull request #411 from sourcelair/issue-#408-tslint
Add tslint
2016-12-15 12:13:23 +00:00
Paris Kasidiaris fbc314a74b Parallelise build 2016-12-15 14:05:37 +02:00
Paris Kasidiaris 850a238ca2 Include lint test into travis 2016-12-15 14:02:38 +02:00
Paris Kasidiaris 20b6f20908 Make linter happy 2016-12-15 13:57:25 +02:00
Paris Kasidiaris 33f7d7649a Add tslint
Fix #408
2016-12-15 13:53:26 +02:00
Paris Kasidiaris fb4d263211 Merge pull request #409 from sourcelair/issue-#405-firefox-paste
Fix linux firefox paste
2016-12-14 10:38:30 +00:00
Paris Kasidiaris ab87dece0b Fix weird paste issue: #405
Fix #405
2016-12-14 01:45:53 +02:00
Paris Kasidiaris 69e027394d 😱 Fix wrong event type and remove useless interface 2016-12-14 01:43:32 +02:00
Paris Kasidiaris 6639b2c731 Merge pull request #407 from sourcelair/build-ts-into-lib
Always build TypeScript in `lib/`
2016-12-13 23:32:49 +00:00
Paris Kasidiaris e23956cd77 Do not publish test files to npm 2016-12-13 23:28:33 +00:00
Paris Kasidiaris 56ca7743a8 Always build TypeScript in lib/ 2016-12-14 01:10:03 +02:00
Paris Kasidiaris fb17d57549 Merge pull request #398 from budde377/feat-npm
Add prepublish script and change main
2016-12-13 22:59:58 +00:00
Christian Budde Christensen e7045f2b8a Add prepublish script and change main
Building TS before publish and pointing the main entry to the builded files.
2016-12-13 20:34:19 +01:00
Daniel Imms fd25bd7ca2 Merge remote-tracking branch 'upstream/master' into 361_circular_list_scrollback 2016-12-12 15:05:19 -08:00
Paris Kasidiaris 852f587bd2 Merge pull request #400 from arcanis/patch-1
Use a fixed size to account for the scrollbar
2016-12-11 10:25:35 +00:00
Maël Nison e0e6f5e302 Use a fixed size to account for the scrollbar 2016-12-10 14:46:01 +01:00
Paris Kasidiaris 06a5ffdd1a Merge pull request #399 from bottleofwater/patch-1
Don't apply the 32 delta when in sgr mode - fix #394
2016-12-09 10:12:42 +00:00
bottleofwater a75bafc4ba Don't apply the 32 delta when in sgr mode - fix #394 2016-12-09 10:52:09 +01:00
Paris Kasidiaris 60a59be4d3 Bump version to 2.2.1
Signed-off-by: Paris Kasidiaris <paris@sourcelair.com>
2016-12-07 18:43:03 +00:00
Paris Kasidiaris b76b2d01eb Checkout add-ons 2016-12-07 18:42:40 +00:00
Paris Kasidiaris cd70d298fc Bump version to 2.2.0
Signed-off-by: Paris Kasidiaris <paris@sourcelair.com>
2016-12-07 18:41:37 +00:00
Paris Kasidiaris 70d0ed3ebf Merge pull request #393 from sourcelair/migrate-clipboard-to-typescript
Migrate Clipboard handler to TypeScript
2016-12-07 18:37:00 +00:00
Paris Kasidiaris 5a5c6ab621 Merge pull request #392 from sourcelair/fix-readme-addon-path
Update path to addons in README.md
2016-12-06 22:08:30 +00:00
Paris Kasidiaris a34bb9724f Improve interfaces in Clipboard.ts 2016-12-07 00:07:50 +02:00
Paris Kasidiaris 824a9c6dbf Migrate Clipboard handler to TypeScript
Also apply JavaScript rules in `.editorconfig` in TypeScript files as well.

Part of #335
2016-12-06 10:01:06 +00:00
Paris Kasidiaris c9eaae6bd5 Update path to addons in README.md 2016-12-06 10:55:40 +02:00
Daniel Imms 9f5790fcaf Merge pull request #391 from Tyriar/357_textarea_top_zero
Position the textarea at top:0
2016-12-05 11:47:55 -08:00
Paris Kasidiaris 6aeb0435ce Merge pull request #390 from sourcelair/issue-#375-bring-build-dir-back
Bring back the `build` directory for temporary builds
2016-12-05 19:35:08 +00:00
Paris Kasidiaris 70ec2496db Remove export from build script 2016-12-05 21:29:50 +02:00
Daniel Imms 55314e0fc7 Position the textarea at top:0
This prevents the browser from scrolling to the top of the page if the terminal
can be scrolled out of view.

Fixes #357
2016-12-05 11:19:36 -08:00
Paris Kasidiaris 243d76617b Create the build directory if it does not exist already 2016-12-05 13:34:10 +02:00
Paris Kasidiaris 6cc66a0b20 Bring back the build directory for temporary builds
Fix #375
2016-12-05 13:17:13 +02:00
Paris Kasidiaris 1e928df531 Merge pull request #388 from sourcelair/issue-#366
Update documentation with cleaner license declaration
2016-12-01 19:41:58 +02:00
Daniel Imms db9bec02f5 Merge pull request #382 from sourcelair/Tyriar-patch-1
Add link to TravisCI badge
2016-12-01 09:19:06 -08:00
Daniel Imms 0dc033692f Merge pull request #383 from Tyriar/381_clean_out_on_build
Clean out/* on build in case src/* files were deleted
2016-12-01 09:18:56 -08:00
Paris Kasidiaris 1d30091188 Update documentation with cleaner license declaration
Fix #366
2016-12-01 13:37:20 +00:00
Daniel Imms 64f80f8623 Merge pull request #380 from Tyriar/378_handle_keypress_scroll
Scroll to bottom whenever handler is called
2016-11-28 15:55:27 -08:00
Daniel Imms 83b06c6bb0 Clean out/* on build in case src/* files were deleted
If files in src/ were compiled and then deleted they will hang around in out/
until deleted. This can potentially cause difficult to diagnose build/test
errors. This commit removes all files from out/ but keeps the folder to
prevent editor file watchers from seeing the folder was deleted and shifting
the file explorer around.

Fixed #381
2016-11-28 10:52:03 -08:00
Daniel Imms 5cbc2f0ea5 Add link to TravisCI badge 2016-11-28 10:46:19 -08:00
Daniel Imms 2bc8adee8c Scroll to bottom whenever handler is called
This catches all current and future cases for correctly scrolling to the bottom
on input by handling it in the handler immediately before emitting the data
event.

Fixes #378
2016-11-28 10:41:05 -08:00
Daniel Imms c6eb7f136c Merge pull request #371 from Tyriar/335_CompositionHelper_ts
Convert CompositionHelper to TypeScript
2016-11-28 08:21:12 -08:00
Daniel Imms 48b87c2f40 Merge remote-tracking branch 'upstream/master' into 335_CompositionHelper_ts 2016-11-28 08:16:38 -08:00
Daniel Imms ca818fbf04 Merge pull request #379 from Tyriar/378_cannot_scroll_up_1plus_pages
Only scroll to bottom during composition or result.key is evaluated
2016-11-28 08:07:00 -08:00
Daniel Imms 264fe4edc7 Merge pull request #372 from Tyriar/335_viewport_tidy
Clean up viewport more after TS conversion
2016-11-28 08:06:50 -08:00
Daniel Imms 317c0ec2de Merge pull request #377 from Tyriar/376_ctrl_backslash_sigquit
Implement ^\ String Terminator
2016-11-28 08:06:42 -08:00
Daniel Imms 607c819109 Progress 2016-11-27 03:27:36 -08:00
Daniel Imms 61fbbb0054 Add CiruclarList pop and splice 2016-11-27 02:20:53 -08:00
Daniel Imms 3b2e89d878 Only scroll to bottom during composition or result.key is evaluated
Fixes #378
2016-11-27 01:49:45 -08:00
Daniel Imms 49b76baf3f Add CircularList and basic tests
Part of #361
2016-11-26 16:57:15 -08:00
Daniel Imms 15a94240eb Implement ^\ String Terminator
Fixes #376
2016-11-26 14:59:53 -08:00
Daniel Imms f75b7db90e Move CompositionHelper header comment to class 2016-11-22 11:54:42 -08:00
Daniel Imms 81dc48ef00 Clean up viewport more after TS conversion
Part of #335
2016-11-22 11:53:45 -08:00
Daniel Imms 3638ef808b tsify CompositionHelper test 2016-11-22 11:50:08 -08:00
Daniel Imms f24cba722a Rename composition-helper-test.js -> CompositionHelper.test.ts 2016-11-22 11:48:16 -08:00
Daniel Imms 30fcdd6ccb tsify CompositionHelper 2016-11-22 11:47:18 -08:00
Daniel Imms b0df236dea Rename CompositionHelper.js -> CompositionHelper.ts 2016-11-22 11:36:34 -08:00
Daniel Imms d91e33ff39 Merge pull request #369 from Tyriar/368_custom_handler_before_scroll
Evaluate the custom keydown event handler before scrolling down
2016-11-22 10:29:43 -08:00
Daniel Imms 1ee316ca83 Merge remote-tracking branch 'upstream/master' into 368_custom_handler_before_scroll 2016-11-22 10:23:04 -08:00
Daniel Imms dcbf014402 Merge pull request #367 from Tyriar/363_modifier_keys_no_scroll
Prevent keyboard modifier events from scrolling down
2016-11-22 08:00:23 -08:00
Daniel Imms 20b0d4fdcc Merge pull request #370 from Tyriar/335_Viewport_ts
Convert Viewport to TypeScript
2016-11-22 07:59:46 -08:00
Daniel Imms 2da1255c26 Merge pull request #355 from Tyriar/354_fix_sourcemaps
Set up source maps
2016-11-21 15:27:38 -08:00
Daniel Imms 5bddb93745 Adopt more TS syntax in Viewport.test.ts 2016-11-21 14:44:10 -08:00
Daniel Imms 51932161af Move viewport-test.js to Viewport.test.ts 2016-11-21 14:39:58 -08:00
Daniel Imms e0d9871195 Convert Viewport to a TS class 2016-11-21 14:39:08 -08:00
Daniel Imms 634d4af4dd Rename Viewport.js -> Viewport.ts
Part of #335
2016-11-21 14:27:41 -08:00
Daniel Imms a507e4f002 Add test for not scrolling on custom handler 2016-11-21 14:23:28 -08:00
Daniel Imms c15fed382d Evaluate the custom keydown event handler before scrolling down
Fixes #368
2016-11-21 14:04:13 -08:00
Daniel Imms 9937d54410 Prevent keyboard modifier events from scrolling down
Fixes #363
2016-11-21 14:01:20 -08:00
Daniel Imms 64f6610f11 Merge pull request #353 from Tyriar/352_fix_demo_after_ts
Point to correct assets after TS build
2016-11-16 09:56:35 -08:00
Daniel Imms e6f21f0bfc Make all dist/ references in index.html absolute 2016-11-14 10:35:20 -08:00
Daniel Imms c63732ef5f Set up source maps
Fixes #354
2016-11-12 14:24:57 -08:00
Daniel Imms 99496de242 Point to correct assets after TS build 2016-11-12 14:01:38 -08:00
Daniel Imms 29cb576737 Merge pull request #296 from Tyriar/typescript_build_2
Move to TypeScript build
2016-11-12 13:51:12 -08:00
Daniel Imms bf4ef599ad Merge remote-tracking branch 'upstream/master' into typescript_build_2 2016-11-12 13:46:59 -08:00
Daniel Imms 5ddb33419e Merge pull request #1 from Tyriar/typescript_build_3
Refactor TS build and fix testing
2016-11-12 11:39:24 -08:00
Paris Kasidiaris bc10fbdfae Merge pull request #351 from yuvipanda/docsome
Add doc comments for *some* events
2016-11-12 12:53:02 +02:00
YuviPanda 6dcf7267a5 Add doc comment for scroll event 2016-11-11 16:28:25 -08:00
YuviPanda 5712365cac Add docstring for the open event 2016-11-11 16:27:10 -08:00
YuviPanda 1fc5a9aada Add docstring for the title event 2016-11-11 16:27:01 -08:00
Daniel Imms 38cd1bd6d7 Merge pull request #350 from sourcelair/Tyriar-patch-1
Make ttyd link bold like the others
2016-11-11 08:31:44 -08:00
Daniel Imms 771642d17d Make ttyd link bold like the others 2016-11-09 19:34:36 -08:00
Paris Kasidiaris bb19b2040b Merge pull request #347 from tsl0922/update-readme
Add ttyd to the real-world uses section of README
2016-11-09 14:52:56 +02:00
Shuanglei Tao 12840dae6c Add ttyd to the real-world uses section of README 2016-11-09 20:23:36 +08:00
Paris Kasidiaris 521d7dbdaa Bump version to 2.1.0
Signed-off-by: Paris Kasidiaris <paris@sourcelair.com>
2016-11-08 22:51:33 +00:00
Paris Kasidiaris 2cba2ff3d4 Merge pull request #345 from hiro-su/fix-ie11-right-click-paste
Fix the right click paste in IE11
2016-11-08 13:21:12 +02:00
hiro-su 547db92693 Fix the right click paste in IE11 2016-11-08 14:56:57 +09:00
Paris Kasidiaris bf334238b7 Merge pull request #339 from hiro-su/fix-ie11-copy-event
Fixed copy event in IE11
2016-11-05 15:27:50 +00:00
hiro-su 5808de648b Fixed copy event in IE11 2016-11-05 23:55:34 +09:00
Paris Kasidiaris 9ab0455b05 Merge pull request #342 from sourcelair/issue-#310-cross-browser-right-click
Make right-click work on all browsers
2016-11-04 20:40:53 +00:00
Paris Kasidiaris 55ab36662a Merge pull request #336 from sourcelair/issue-#216-no-auto-scroll-on-output
Stop auto scrolling to bottom on output
2016-11-04 19:55:07 +00:00
Paris Kasidiaris b1ad564b0d Actually fix tests 2016-11-04 21:48:53 +02:00
Paris Kasidiaris bc70b3b37d Fix tests 2016-11-04 19:36:30 +02:00
Paris Kasidiaris d426c99866 Move all browser/platform detecting in utils/Browser.js 2016-11-04 18:41:00 +02:00
Paris Kasidiaris 356377975b Fix right-click for Firefox and IE
Fix #310
2016-11-04 18:33:04 +02:00
Paris Kasidiaris 0de3d839e6 Improve experience and add test
- Scroll to bottom on keydown (when scrolled up)
- Add test to ensure above behavior
2016-11-04 12:29:17 +00:00
Paris Kasidiaris 0fcd6b1d22 Merge pull request #337 from IanLewis/master
Add main property to bower.json
2016-11-04 09:35:24 +00:00
Ian Lewis 99b5464392 Add main property to bower.json
This patch adds the main property to the project's bower.json for better
interoperability with tools like wiredep etc.
2016-11-03 17:56:58 +05:30
Paris Kasidiaris 70965a8fd6 Merge pull request #334 from hiro-su/fix-ie11-paste-key
Fixed paste key in IE11
2016-11-02 09:19:18 +00:00
hiro-su 77ca15497c Fixed paste key in IE11 2016-11-02 11:17:37 +09:00
Daniel Imms 56ecc77dfc Refactor TS build and fix testing
This change is largely just moving files with minor tweaks to them to fix,
the rest of the commit is build process changes:

- The addons/ and test/ dirs have been moved to src/
- The build directory has been removed
- TypeScript builds are output in out/, this is where tests are run
- The demo now relies on the dist/ build which is performed as part of ./bin/build
- Addons are now shipped under the ./build directory
2016-10-31 21:22:21 -07:00
Daniel Imms a995b610cf Remove typings (not needed in TS 2+) 2016-10-31 19:30:35 -07:00
Daniel Imms 332bc6be76 Merge remote-tracking branch 'upstream/master' into typescript_build_2 2016-10-31 19:29:36 -07:00
Paris Kasidiaris 26de9fe4c5 Merge pull request #332 from sourcelair/issue-#331-fix-no-selection-range
Consider click not on selection, when s.rangeCount is 0
2016-10-31 18:52:48 +00:00
Paris Kasidiaris 00dcb4f6e3 Consider click not on selection, when s.rangeCount is 0 2016-10-31 15:06:00 +02:00
Daniel Imms 996c641a79 Merge pull request #328 from Tyriar/327_fix_random_line_wrap
Force nowrap on each line
2016-10-27 07:52:48 -07:00
Daniel Imms a077860464 Force nowrap on each line
Fixes #327
2016-10-26 18:07:55 -07:00
Mikko Karvonen 5e68acfc76 Quick fix to prevent terminal scrolling when user is looking into scrollback. 2016-10-25 13:54:57 +03:00
Daniel Imms 269540bfed Merge pull request #323 from Tyriar/322_ime_bad_position
Fix the textarea in the composition view's pos when composing
2016-10-24 14:58:41 -07:00
Daniel Imms 868f15173d Fix the textarea in the composition view's pos when composing
Fixes #322
2016-10-24 11:06:25 -07:00
Paris Kasidiaris 06a93344bb Merge pull request #321 from sourcelair/issue-#313-document-terminal-constructor
Issue #313 document terminal constructor
2016-10-24 19:01:37 +03:00
Paris Kasidiaris e8e7870be2 Remove geometry option from terminal consttructor 2016-10-23 17:55:37 +03:00
Paris Kasidiaris a9417c6878 Fix term.geometry and document it in constructor options
Set `term.geometry` on terminal creation and on resize
2016-10-23 14:22:00 +00:00
Paris Kasidiaris 605d61b64f Ignore .env files that set the environment 2016-10-23 14:22:00 +00:00
Daniel Imms 8feaacf57e Merge pull request #320 from Tyriar/299_keep_textarea_in_viewport
Correctly size and position the composition view
2016-10-23 07:16:28 -07:00
Daniel Imms 553c651166 Correctly size and position the composition view 2016-10-22 16:27:17 -07:00
Daniel Imms 1ec0bac9c6 Keep composition textarea within viewport
Part of #299
2016-10-22 15:41:55 -07:00
Paris Kasidiaris b1c6ae8a06 Merge pull request #303 from staticfloat/sf/terminado
Add `Terminado` example
2016-10-17 21:03:49 +03:00
Daniel Imms 8a124d0b35 Merge pull request #308 from Tyriar/294_scroll
Implement scrollPages, scrollToTop, scrollToBottom and add scroll tests
2016-10-10 08:33:09 -07:00
Daniel Imms 0ad02a4afa Add note that negative scrolls up on scrollPages 2016-10-10 08:25:01 -07:00
Daniel Imms 9389f899d7 Merge pull request #309 from Tyriar/fix_indentation
Fix jsdoc indentation
2016-10-10 08:20:56 -07:00
Paris Kasidiaris 050a34921b Merge pull request #304 from sourcelair/update-addons-paths
Make add-ons load Terminal from dist
2016-10-10 10:12:50 +03:00
Daniel Imms 32e878db81 Fix jsdoc indentation 2016-10-09 13:23:22 -07:00
Daniel Imms 0bf7bf56a5 Add jsdoc to scrollTo functions 2016-10-09 13:20:33 -07:00
Daniel Imms e5d130b698 Add scrollToBottom 2016-10-09 13:19:11 -07:00
Daniel Imms 941e57a218 Add tests for scrollPages 2016-10-09 13:10:18 -07:00
Daniel Imms c02eea6ad3 Add tests for scrollDisp 2016-10-09 13:08:08 -07:00
Daniel Imms fe0d878b23 Implement scrollPages 2016-10-09 12:51:58 -07:00
Paris Kasidiaris 763f148fa1 Remove __dirname usage from loadAddon 2016-10-09 13:17:17 +03:00
Elliot Saba 8f0ada708f Make naming a little more self-consistent 2016-10-08 20:09:34 -07:00
Paris Kasidiaris 8117999ab3 Fix tests 2016-10-08 12:22:18 +03:00
Elliot Saba 10dd22a3d4 Rename Terminado attach and detach functions 2016-10-08 00:36:08 -07:00
Daniel Imms 738f37e86c Set travis to node 6 2016-10-07 23:01:20 -07:00
Daniel Imms 729c0abf04 Explicitly indicate test locations 2016-10-07 22:56:02 -07:00
Elliot Saba fe54da34f8 Add terminado.js, an addon for connecting to Terminado backends 2016-10-07 13:33:10 -07:00
Daniel Imms 09de0f31fa Merge pull request #301 from Tyriar/300_top_textarea_helper
Add top to textarea helper
2016-10-05 09:33:08 -07:00
Paris Kasidiaris 6ee524864e Make add-ons load the latest built version 2016-10-05 13:23:45 +03:00
Elliot Saba 8fd350f0fc Add Terminado example 2016-10-05 00:32:39 -07:00
Daniel Imms ebbeeefd86 Add top to textarea helper
Fixes #300
2016-10-04 19:03:15 -07:00
Paris Kasidiaris 76a890aa74 Bump version to 2.0.1
Signed-off-by: Paris Kasidiaris <paris@sourcelair.com>
2016-10-03 12:39:39 +00:00
Paris Kasidiaris 5ce522a401 Add built files 2016-10-03 12:39:14 +00:00
Paris Kasidiaris 68906b5def Bump version to 2.0.0
Signed-off-by: Paris Kasidiaris <paris@sourcelair.com>
2016-10-03 12:37:00 +00:00
Paris Kasidiaris dddaa16573 Merge pull request #298 from sourcelair/fix-release-scripts
Fix release scripts
2016-10-03 15:33:20 +03:00
Paris Kasidiaris d581559dd4 Fix release scripts 2016-10-03 15:32:14 +03:00
Paris Kasidiaris 3e77db8149 Merge pull request #269 from Tyriar/set_package_main
Set package.json main to build/xterm.js
2016-10-03 14:11:34 +03:00
Daniel Imms bd994f9e57 Install typings on build and add to git ignore 2016-10-01 12:22:57 -07:00
Paris Kasidiaris 9115846017 Merge pull request #293 from sourcelair/issue-#202-fix-paste
Allow right-click paste (again)
2016-10-01 10:39:46 +03:00
Daniel Imms 441009d359 Move to TypeScript build 2016-10-01 00:29:33 -07:00
Paris Kasidiaris ab7d63a8c8 Merge pull request #295 from sourcelair/update-copyright-text
Update copyright text in all files
2016-09-30 14:06:34 +03:00
Paris Kasidiaris a7acee726d Update copyright text in all files
sourceLair Limited → SourceLair Private Company
2016-09-30 13:52:55 +03:00
Paris Kasidiaris a6f047331f Add forgotten line to keep copied text 😅 2016-09-29 10:59:24 +03:00
Paris Kasidiaris a224acb50b Document new Clipboard module 2016-09-29 07:51:31 +00:00
Paris Kasidiaris 42a1e4ef9b Move clipboard into its own module 2016-09-29 07:51:07 +00:00
Paris Kasidiaris dff867b8a7 Fix right-click paste
Fix #202
2016-09-29 10:50:21 +03:00
Daniel Imms 8bf8147028 Merge pull request #289 from Tyriar/151_allow_wheel_in_application_mode
Allow wheel events in application mode
2016-09-25 04:44:53 -07:00
Daniel Imms ccf6d8f87d Merge pull request #287 from Tyriar/286_fix_scroll_in_app_mode
Sync scroll bar instead of disabling on enter app mode
2016-09-25 04:44:44 -07:00
Daniel Imms ac817ccc66 Merge pull request #288 from Tyriar/278_sync_scrollbar_after_reset
Sync scroll bar after reset
2016-09-25 02:44:56 -07:00
Daniel Imms 37c07eaaf9 Allow wheel events in application mode
This change allows wheel events in application mode which fixes mouse wheel
scrolling in oh-my-zsh and powershell for Linux (among others). Along with #287
a functional scroll bar will also be usable in those shells.

Fixes #151
2016-09-25 02:42:46 -07:00
Daniel Imms 71e20801f0 Fix tests 2016-09-25 02:07:53 -07:00
Daniel Imms bae1bd6a42 Fix tests 2016-09-25 02:02:05 -07:00
Daniel Imms c8b194931f Sync scroll bar after reset
Fixes #278
2016-09-25 01:56:03 -07:00
Daniel Imms c7a4881552 Sync scroll bar instead of disabling on enter app mode
Fixes #286
2016-09-25 01:50:49 -07:00
Paris Kasidiaris 26fd963fbd Merge pull request #283 from iMoses/master
Workaround a known bug in Babel JS
2016-09-22 14:46:02 +01:00
imoses 122a34b40e Merge branch 'master' of https://github.com/sourcelair/xterm.js 2016-09-22 16:28:20 +03:00
imoses 33abd69e85 Tests were failing locally due to a babel js bug - https://github.com/babel/babel/issues/3826
Fixed the tests to work around the problem and added /.idea/ to .gitignore for the benefit of contributors who uses intellij
2016-09-22 16:26:03 +03:00
Paris Kasidiaris 76a686beb2 Merge pull request #282 from sourcelair/get-option
Add `getOption` API
2016-09-21 17:14:39 +01:00
Paris Kasidiaris 53e8ac9b0c Stricter check for option existence 2016-09-21 19:09:55 +03:00
Paris Kasidiaris 4b459fe0fd Implement term.getOption 2016-09-21 13:56:03 +03:00
Daniel Imms be4c8341ba Merge pull request #275 from Tyriar/move_comments_inside_case
Move key handler comments inside case blocks
2016-09-18 13:49:52 -07:00
Daniel Imms a33bc027f1 Merge pull request #274 from Tyriar/205_cancel_shift_pg_up_down_event
Cancel keydown event on shift+page up/down
2016-09-18 13:49:38 -07:00
Daniel Imms 3c9d8b0d84 Merge pull request #276 from Tyriar/173_clear_api
Expose API to clear the terminal
2016-09-17 04:11:20 -07:00
Daniel Imms 26fc53990b Fix clear function when buffer is <= rows 2016-09-17 04:07:36 -07:00
Daniel Imms 852dac4d62 Don't clear prompt on clear if it already is 2016-09-17 03:59:42 -07:00
Daniel Imms 76719413f9 Expose API to clear the terminal
Fixes #173
2016-09-17 02:39:22 -07:00
Daniel Imms fca673d653 Move key handler comments inside case blocks
Improves readability
2016-09-17 01:11:43 -07:00
Daniel Imms 446c3958df Cancel keydown even on shift+page up/down
Fixes #205
2016-09-17 01:09:08 -07:00
Daniel Imms 59e72b8b07 Merge pull request #272 from Tyriar/271_setOption
Add setOption API
2016-09-16 02:17:28 -07:00
Daniel Imms 15e56bd809 Add @param to setOption 2016-09-16 02:03:31 -07:00
Daniel Imms ab5cc0ad05 Add setOption API
Fixes #271
2016-09-16 01:46:02 -07:00
Daniel Imms b89bce17ad Merge pull request #268 from Tyriar/267_fix_mouseup_focus
Fix focus on mouseup when mousedown was in another element
2016-09-16 01:23:57 -07:00
Daniel Imms 7fcbddac04 Set package.json main to build/xterm.js
build/xterm.js should be used when require is used when installed via npm.
2016-09-15 16:25:22 -07:00
Daniel Imms 4e1bbee67e Fix focus on mouseup when mousedown was in another element
Fixes #267
2016-09-15 13:50:39 -07:00
Paris Kasidiaris ecfa44b0e8 Merge pull request #263 from sourcelair/fix-indentation
Fix indentation of `src/xterm.js`
2016-09-05 13:18:49 +03:00
Paris Kasidiaris db76868c26 Fix indentation of src/xterm.js 2016-09-04 16:47:21 +03:00
Paris Kasidiaris b4c7989566 Merge pull request #252 from ayapi/modifier-keys
add modifier+delete, modifier+F1-F12 keys
2016-09-04 16:23:29 +03:00
Paris Kasidiaris 7b38b9ed57 Merge pull request #261 from yurovant/patch-1
Update README.md
2016-09-02 11:54:25 +03:00
Anton Yurovskykh c2fccc0350 Update README.md
Fix typo: is => if
2016-09-02 09:28:57 +03:00
Daniel Imms b56ad5e8fe Merge pull request #243 from Tyriar/242_enable_scroll_on_soft_reset
Disable application mode in viewport on soft reset
2016-08-31 10:40:54 -07:00
Daniel Imms abfcf27962 Merge pull request #256 from Tyriar/254_compositionhelper_module
Pull CompositionHelper into a module
2016-08-31 10:34:14 -07:00
Daniel Imms 6a04614549 Merge remote-tracking branch 'upstream/master' into 254_compositionhelper_module 2016-08-29 16:50:35 -07:00
Daniel Imms 89f981836f Merge pull request #255 from Tyriar/253_viewport_module
Pull Viewport into a module
2016-08-29 16:49:03 -07:00
Paris Kasidiaris 33d4d400f0 Merge pull request #257 from Tyriar/improve_copyright_line
Improve copyright line
2016-08-30 02:38:41 +03:00
Daniel Imms 749ed6372a Improve copyright line 2016-08-29 16:21:00 -07:00
Daniel Imms 81fccee906 Add copyright line 2016-08-29 16:20:11 -07:00
Daniel Imms 4caac78241 Improve copyright line 2016-08-29 16:19:27 -07:00
Daniel Imms 7ff03bb475 Pull Viewport into a module
Fixes #253
2016-08-29 16:17:59 -07:00
Daniel Imms 28c3a2026c Pull CompositionHelper into a module
Fixes #254
2016-08-29 16:15:28 -07:00
Paris Kasidiaris a9c85fe825 Merge pull request #251 from sourcelair/issue-#158-es2015
Introduce Babel as the build system, convert src/xterm.js to ES2015 and export EventEmitter
2016-08-30 01:59:17 +03:00
Paris Kasidiaris c46e799dd0 Update README.md to include xterm.js from new directory 2016-08-29 22:57:19 +03:00
Paris Kasidiaris ed1a31d1cf Make src/xterm.js ES2015 and export EventEmitter into its own file
Fix #158
2016-08-29 19:51:40 +00:00
ayapi 7f7156bafa add modifier+F1-F12 keys 2016-08-27 01:13:01 +09:00
ayapi 62ce64f97d add modifier+delete keys 2016-08-27 01:03:35 +09:00
Paris Kasidiaris 04a226f813 Merge pull request #249 from sourcelair/Tyriar-patch-3
Improve grammar
2016-08-26 03:40:16 +03:00
Daniel Imms 68b012ca06 Improve grammar 2016-08-25 14:46:20 -07:00
Paris Kasidiaris 220828f7db Bump version to 1.1.3
Signed-off-by: Paris Kasidiaris <paris@sourcelair.com>
2016-08-23 22:44:55 +00:00
Paris Kasidiaris 7df7f8f598 Merge pull request #246 from sourcelair/bin-release
Improve "prepare-release" and introduce "release" scripts
2016-08-24 01:47:17 +03:00
Paris Kasidiaris 05aff199b2 Improve "prepare-release" and introduce "release" scripts
Has to do with #244.
2016-08-24 01:43:37 +03:00
Daniel Imms fc22fc39e2 Disable application mode in viewport on soft reset
Fixes #242
2016-08-23 09:11:02 -07:00
Paris Kasidiaris 01c20807c2 Bump version to 1.1.2
Signed-off-by: Paris Kasidiaris <paris@sourcelair.com>
2016-08-22 14:26:27 +00:00
Paris Kasidiaris 243ce59806 Merge pull request #240 from sourcelair/issue/239
Fix alt + arrow key movement
2016-08-22 17:28:23 +03:00
Paris Kasidiaris 5d6e0356b3 Add tests for arrow key movements 2016-08-22 17:21:58 +03:00
Paris Kasidiaris 8324b43cc2 Fix Alt + Arrow movement
Fix #239
2016-08-22 17:17:58 +03:00
Paris Kasidiaris 4c3ccc264c Bump version to 1.1.1
Signed-off-by: Paris Kasidiaris <paris@sourcelair.com>
2016-08-22 13:11:54 +00:00
Paris Kasidiaris 197c83d993 Merge pull request #234 from Tyriar/233_fix_cursor_on_top_of_ime
Ensure composition view is on top of cursor
2016-08-22 16:14:15 +03:00
Paris Kasidiaris aaded29855 Merge pull request #238 from sourcelair/gitter-badge
Add "Chat on Gitter" badge
2016-08-22 14:47:21 +03:00
Paris Kasidiaris e5656b9982 Add "Chat on Gitter" badge 2016-08-22 14:40:34 +03:00
Paris c06b08df5b Bump version to 1.1.0
Signed-off-by: Paris <paris@sourcelair.com>
2016-08-22 11:22:21 +00:00
Daniel Imms fc16752cec Merge pull request #236 from Tyriar/235_fix_scroll_to_top_on_app_mode
Disable onScroll event during application mode
2016-08-21 00:58:47 -07:00
Daniel Imms 588d3b12e0 Disable onScroll event during application mode
Fixes #235
2016-08-19 12:52:13 -07:00
Daniel Imms 7de7bdac71 Ensure composition view is on top of cursor
Fixes #233
2016-08-18 11:28:10 -07:00
Paris Kasidiaris 6e8e78db57 Merge pull request #230 from ayapi/refresh-viewport-height
Refresh the height of the viewport when the line-height changed
2016-08-18 20:35:39 +03:00
ayapi dcc991bd5c refresh the height of the viewport when the line-height changed 2016-08-18 19:37:16 +09:00
Daniel Imms 04cb9f33ff Merge pull request #226 from Tyriar/208_fix_ime_popup_pos
Reposition textview after composition view
2016-08-17 08:50:41 -07:00
Paris Kasidiaris 9bd167c10d Merge pull request #231 from meefik/patch-1
Fix incorrect mouse position
2016-08-17 11:57:16 +03:00
Anton Skshidlevsky dc6aab88b0 Fix incorrect mouse position
Incorrect mouse position in Midnight Commander
2016-08-17 10:38:12 +03:00
Paris Kasidiaris 43fc6f1fa3 Merge pull request #228 from ayapi/restore-customkeydownhandler
restore customKeydownHandler on reset(RIS)
2016-08-16 17:20:40 +03:00
ayapi c2aa0d75ec restore customKeydownHandler on reset(RIS) 2016-08-13 22:44:56 +09:00
Daniel Imms c24ae84b00 Fix tests 2016-08-12 10:27:49 -07:00
Daniel Imms 92605a9734 Reposition textview after composition view
Call the function again via setTimeout to allow changes after composition
events. This prevents the IME windows jumping sometimes on thesecond key
press.

Fixes #208
2016-08-12 10:21:41 -07:00
Daniel Imms b32a34b97c Merge pull request #224 from sourcelair/Tyriar-patch-2
Sync scroll bar height if the viewport height changes
2016-08-09 09:03:16 -07:00
Daniel Imms 12923a6649 Sync scroll bar height if the viewport height changes 2016-08-08 14:41:39 -07:00
Daniel Imms e7c7160f85 Merge pull request #218 from Tyriar/77_scroll_bar
Implement scroll bar
2016-08-08 12:52:12 -07:00
Paris Kasidiaris 2a0962183e Merge pull request #223 from sourcelair/Tyriar-patch-1
By more explicit in security warning
2016-08-08 20:14:14 +01:00
Daniel Imms 95d9ad46cf By more explicit in security warning
Calling out that the insecurity is due to the barebones implementation
of the demo will prevent people thinking that the library is inherently
insecure.
2016-08-08 10:38:01 -07:00
Daniel Imms e60513f554 Fix outline showing up when clicking terminal 2016-08-08 10:22:34 -07:00
Daniel Imms a557a82b40 Fix scrollbar being transparent on OS X 2016-08-08 10:22:19 -07:00
Paris Kasidiaris 3dd6f368d6 Merge pull request #220 from sourcelair/improve-demo
Introduce resizing option in demo
2016-08-08 14:35:02 +01:00
Paris 5ce012bdac Add notice about security risks 2016-08-08 16:28:29 +03:00
Paris Kasidiaris a2338c49b9 Merge pull request #222 from sourcelair/signed-releases
Require release commits to be signed
2016-08-05 14:56:00 +01:00
Paris Kasidiaris dd6886f358 Merge pull request #221 from odino/patch-1
Update pty.js
2016-08-05 14:55:14 +01:00
Paris 5ddf6301c2 Add Sign-Off flag as well 2016-08-05 16:49:04 +03:00
Paris 14dd2c0afb Require release commits to be signed 2016-08-05 11:21:27 +00:00
Alessandro Nadalin fc2a0d3e65 Update pty.js 2016-08-05 13:59:21 +04:00
Paris 0f8fc63a5e First functional resizing demo
Fix #211
2016-08-05 12:28:22 +03:00
Paris f57a40eeb3 First batch of improvements to demo
- Used fetch tentatively
- Implemented resize endpoint
- Create terminals with the appropriate size
2016-08-05 08:46:49 +00:00
Daniel Imms 2c9c95f531 Add another test 2016-08-04 18:56:33 -07:00
Daniel Imms 7048f6edf7 Add some tests, fix app keypad mode bug 2016-08-04 18:34:04 -07:00
Paris Kasidiaris c333cfa073 Merge pull request #214 from mentos1386/mentos1386-linkify
Make linkify links open in new tab/window
2016-08-04 14:31:29 +01:00
Tine Jozelj 64d77d2491 Making it more human readable 2016-08-04 15:22:45 +02:00
Tine Jozelj 9604674c65 Default without target atribute 2016-08-04 15:05:09 +02:00
Tine Jozelj d9294779a6 Add target as option
You can now provide target argument if you want to modify target attribute on links. It defaults to `_self` to keep current behavior.
2016-08-04 14:58:42 +02:00
Paris Kasidiaris 3117fe9465 Merge pull request #219 from sourcelair/issue/198
Update README and remove screenshot
2016-08-04 11:07:20 +01:00
Paris 0a74b9aa3b Fix typos 2016-08-04 13:00:47 +03:00
Paris 7e90576391 Update README and remove screenshot 2016-08-04 12:01:37 +03:00
Daniel Imms 0a34885fbc Fix line-height after rowContainer moved out of viewport 2016-08-03 18:27:43 -07:00
Daniel Imms 61201525a8 Remaining jsdoc 2016-08-03 18:27:19 -07:00
Daniel Imms 1b886a4420 Disable the scroll bar when in application keypad mode 2016-08-03 18:12:44 -07:00
Daniel Imms 6f70984ab6 Drop support for old mouse wheel APIs
All browsers have supported the WheelEvent (onwheel) for sometime now, since
Firefox does not support onmousewheel which is also non-standard but works with
the standard interface, it makes sense to drop support now.
2016-08-03 13:41:55 -07:00
Daniel Imms 38fa2d78da Support DOMMouseScroll event (Firefox) 2016-08-03 13:00:07 -07:00
Daniel Imms fe905bace5 Prevent the page from scrolling when the terminal scrolls 2016-08-03 12:49:12 -07:00
Daniel Imms e7e1434293 Clean up, refresh sizes when font-size changes 2016-08-03 12:47:36 -07:00
Daniel Imms 363c647a5f Sync scroll bar on more sensible events 2016-08-03 11:27:16 -07:00
Daniel Imms aac60eb029 Add support for WheelEvent.deltaMode 2016-08-03 10:16:08 -07:00
Daniel Imms 06ca03ae9c Get it working 2016-08-02 18:36:24 -07:00
Daniel Imms ff927b8e79 Implement basic scroll bar 2016-08-02 16:59:50 -07:00
Daniel Imms 670b0d58c7 Add base viewport, set line height
Fixes #149
2016-08-02 12:42:13 -07:00
Tine Jozelj 3113cdf0ea Make linkify links open in new tab/window
I believe this behavior is more appropriate.
2016-08-02 15:57:15 +02:00
Paris Kasidiaris d2cef6f29d Merge pull request #210 from Tyriar/209_hide_textarea_when_not_composing
Hide textarea off-screen when not composing
2016-08-01 11:35:06 +03:00
Daniel Imms d62bd5f748 Get rid of rogue whitespace 2016-07-28 11:15:20 -07:00
Daniel Imms e6fd1381ce Hide textarea off-screen when not composing
Fixes #209
2016-07-28 11:13:24 -07:00
Daniel Imms 65b2be7831 Merge pull request #186 from Tyriar/118_support_custom_keydown_handler
Allow custom keydown handler to be attached
2016-07-22 05:21:28 -07:00
Daniel Imms e2d0de2686 Fix test 2016-07-22 05:18:28 -07:00
Daniel Imms 495b2d7fbb Merge remote-tracking branch 'upstream/master' into 118_support_custom_keydown_handler 2016-07-22 05:12:52 -07:00
Paris Kasidiaris 5e662d0eb7 Merge pull request #203 from Tyriar/124_fix_focus_blur
Correct blur/focus delegation
2016-07-22 14:26:48 +03:00
Daniel Imms 892db98c96 Correct blur/focus delegation
Focus and blur events on the Terminal object are delegated to the textarea
which handles everything.
2016-07-22 04:14:05 -07:00
Paris Kasidiaris 10fbf0dccc Merge pull request #175 from Tyriar/124_add_textarea_back
Add textarea back and support IMEs
2016-07-22 11:50:23 +03:00
Daniel Imms 4595a18197 Tidy up blur/focus handlers 2016-07-21 10:51:43 -07:00
Daniel Imms 577a3d81e5 Use explicit boolean comparison 2016-07-21 10:32:20 -07:00
Paris Kasidiaris 55fb5dbcb5 Merge pull request #200 from jerch/fix_193
fixing arrow key sequences
2016-07-21 15:00:17 +03:00
Jörg Breitbart 8e7eeff45c fixing test cases 2016-07-21 12:47:19 +02:00
Jörg Breitbart ec7130b46f fixing arrow key sequences 2016-07-21 12:39:36 +02:00
Paris Kasidiaris dc17f0d02e Merge pull request #192 from sourcelair/bump-version-1-0-0
Bump version to 1.0.0
2016-07-18 11:21:47 +03:00
Paris 14cd4315c7 Bump version to 1.0.0 2016-07-18 07:50:07 +00:00
Paris Kasidiaris 644d881787 Merge pull request #191 from sourcelair/prepare-release-script
Introduce prepare release script
2016-07-18 10:47:00 +03:00
Paris 7fdfc62237 Introduce prepare-release script 2016-07-18 10:43:35 +03:00
Paris 9fa0f00c23 Add AUTHORS and AUTHORS generator script 2016-07-18 10:04:04 +03:00
Paris Kasidiaris fe6aaa3664 Merge pull request #190 from sourcelair/contributing-md
Introduce CONTRIBUTING.md and Contributing section in README.md
2016-07-18 09:40:09 +03:00
Paris 6e6b527d6e Improve issue template 2016-07-18 09:36:39 +03:00
Paris 94d0009994 Add issue template 2016-07-18 09:36:01 +03:00
Paris 7cd99d1559 Add Development and Contribution section in README.md 2016-07-17 22:04:48 +03:00
Paris d91fa3b6fb Add CONTRIBUTING.md 2016-07-17 18:55:49 +00:00
Paris Kasidiaris 466dca361c Merge pull request #187 from Tyriar/remove_linkify_from_readme
Switch linkify for fit in readme
2016-07-17 21:46:19 +03:00
Paris Kasidiaris 8b24c39a1d Merge pull request #184 from Tyriar/patch-2
Add statement on browser support
2016-07-17 18:48:23 +03:00
Daniel Imms 9dd64dfb49 Switch linkify for fit in readme
Linkify doesn't work currently so it shouldn't be used as an example.
2016-07-15 16:41:40 -07:00
Daniel Imms 0010a5a109 Explicitly return from keydown 2016-07-15 16:33:30 -07:00
Daniel Imms 9cb5b005bb Add tests 2016-07-15 16:31:57 -07:00
Daniel Imms 06a3aa6cf8 Merge remote-tracking branch 'upstream/master' into 118_support_custom_keydown_handler 2016-07-15 16:21:06 -07:00
Daniel Imms d4e9d34d2d Allow custom keydown handler to be attached
Fixes #118
2016-07-15 16:16:37 -07:00
Daniel Imms a3a7017f6b Fix tests 2016-07-15 10:22:31 -07:00
Daniel Imms fedc1fd325 Fix keydown conditional 2016-07-15 10:17:29 -07:00
Daniel Imms 11d5f51e67 Merge remote-tracking branch 'upstream/master' into 124_add_textarea_back 2016-07-15 10:13:56 -07:00
Daniel Imms 2c810ce763 Add note about support intentions 2016-07-15 10:12:55 -07:00
Daniel Imms b61e81d5b4 Add latest Edge version as supported, alphabetize 2016-07-15 10:10:10 -07:00
Daniel Imms 534a9e7f5a Big clean up
Move keydown event to CompositionHelper
Move call in refresh to be handled with an event
2016-07-13 21:50:00 -07:00
Daniel Imms 14a62c0f8b Add test cases for Japanese 2016-07-13 21:22:27 -07:00
Daniel Imms 89d29bbc10 Fix non-composition char input after composition char + test 2016-07-13 21:02:43 -07:00
Daniel Imms ac8db6e8ec jsdoc handleAnyTextareaChanges 2016-07-13 20:52:55 -07:00
Daniel Imms 0a7c1bfd98 Position the textarea below the cursor during a composition
This will position any IME helper in the correct position
2016-07-13 20:46:43 -07:00
Daniel Imms 4efb8d24ac Don't wrap multi-character compositions 2016-07-13 20:34:55 -07:00
Daniel Imms 38d08873bc Don't finalize composition on modifier key presses 2016-07-13 20:34:43 -07:00
Daniel Imms 8074d754d3 Only position composition view if a cursor is visible 2016-07-13 20:12:49 -07:00
Daniel Imms f3db84decb Add statement on browser support
Fixes #183
2016-07-13 15:28:14 -07:00
Paris Kasidiaris 13644ce351 Merge pull request #178 from sourcelair/issue/169
Fix entering hash (#) on British keyboard layout
2016-07-13 20:18:41 +03:00
Daniel Imms e1c1b07a0b Add test cases for Korean 2016-07-13 08:37:24 -07:00
Paris 42ec3b492a Clarify isThirdLevelShift 2016-07-13 17:53:33 +03:00
Daniel Imms c18794f202 Merge remote-tracking branch 'upstream/master' into 124_add_textarea_back 2016-07-13 07:14:14 -07:00
Paris 0862fd1f59 Fix thirdLevelKey clause 2016-07-13 17:10:45 +03:00
Paris 6663a947ee Separate dummy keyDown and keyPress events in tests 2016-07-13 16:48:48 +03:00
Paris 3e8bb98197 Fix #169 2016-07-13 16:35:57 +03:00
Paris Kasidiaris 9b2f68a25d Merge pull request #177 from sourcelair/document-everything-part-2
Document all public methods of Xterm
2016-07-13 15:38:18 +03:00
Paris 11a7cd6983 Fix all leading whitespaces (eliminate tabs) 2016-07-13 13:24:53 +03:00
Paris 7d904e455d Fix leading whitespaces (eliminate tabs) 2016-07-13 13:22:51 +03:00
Paris c3cf6a22e9 Document all public methods 2016-07-13 13:14:02 +03:00
Paris 12a150a438 Document more public methods and transform comments to JSDoc compatible 2016-07-13 13:03:40 +03:00
Daniel Imms 8b46e84263 Use correct .terminal-cursor for composition view 2016-07-12 21:42:10 -07:00
Daniel Imms 237e6819bf Fix tests 2016-07-12 20:55:24 -07:00
Daniel Imms 8faea59e31 Handle non-composition input when IME is active 2016-07-12 20:51:55 -07:00
Daniel Imms cab79c473f jsdoc and tidy up 2016-07-12 20:26:18 -07:00
Daniel Imms c656ed04b0 Use handler not write, fix other edge cases 2016-07-12 20:19:26 -07:00
Daniel Imms 29000fb79e Refactor into CompositionHelper object 2016-07-12 19:14:56 -07:00
Daniel Imms 03fa017d4a Re-hide the textarea 2016-07-12 18:49:37 -07:00
Daniel Imms 26af6ffd4d Get IMEs working 2016-07-12 18:48:08 -07:00
Daniel Imms 7d8e28a7dd Merge remote-tracking branch 'upstream/master' into 124_add_textarea_back 2016-07-12 17:02:11 -07:00
Daniel Imms a52b7e7a4e Add starting point for IME support 2016-07-12 16:50:07 -07:00
Daniel Imms aaedcfc700 Support focus class on the .xterm element 2016-07-12 16:18:05 -07:00
Daniel Imms fc7b22dc48 Manually revert textarea changes 2016-07-12 16:03:31 -07:00
Paris Kasidiaris 5b44e36317 Merge pull request #161 from jerch/tests_xterm
xterm test cases
2016-07-13 00:46:44 +03:00
Jörg Breitbart 82f5de3bb9 Merge branch 'tests_xterm' of https://github.com/jerch/xterm.js into tests_xterm 2016-07-12 21:27:06 +02:00
Jörg Breitbart dcf30b40c0 cleanup xterm test file 2016-07-12 21:26:38 +02:00
Jörg Breitbart 3b89134c05 selecting sucessful tests 2016-07-12 21:26:38 +02:00
Jörg Breitbart d76d50677d adding xterm comparison tests 2016-07-12 21:26:38 +02:00
Paris Kasidiaris 8790a94a56 Merge pull request #165 from sourcelair/first-docs
Document several public methods
2016-07-10 03:57:05 +03:00
Jörg Breitbart 308535dc5c cleanup xterm test file 2016-07-09 14:57:57 +02:00
Paris Kasidiaris efe5b20b73 Merge pull request #168 from freebroccolo/master
⬆️ pty.js@0.3.1
2016-07-09 01:47:52 +03:00
Darin Morrison d1a6e8de3b ⬆️ pty.js@0.3.1 2016-07-07 12:42:49 -06:00
Paris f3cf646bbf Better document scrollDisp 2016-07-06 12:37:45 +03:00
Paris a39b309b74 Remove most public directives 2016-07-06 12:36:11 +03:00
Paris 1aeb562066 progress 2016-07-06 09:32:40 +00:00
Paris 107d1a1496 First chunk of documentation 2016-07-06 09:32:40 +00:00
Paris Kasidiaris 39edbfb655 Merge pull request #163 from jerch/fix_147_162
fixing #147 and #162
2016-07-06 12:29:01 +03:00
Jörg Breitbart 246b0edaa1 disable refresh for tests 2016-07-06 00:23:10 +02:00
Jörg Breitbart 4afa08da8d fixing #147 and #162 2016-07-05 23:54:15 +02:00
Jörg Breitbart dc5b525559 selecting sucessful tests 2016-07-05 14:55:03 +02:00
Paris Kasidiaris 30251e5d7c Merge pull request #160 from Tyriar/159_fix_nbsp_resize
Have ' ' chars get inserted correctly after a resize
2016-07-05 13:53:34 +03:00
Paris Kasidiaris 81e231ea20 Merge pull request #155 from Tyriar/154_fix_resize_at_top_exception
Fix exception when increasing size when viewport is at top of buffer
2016-07-05 12:56:45 +03:00
Jörg Breitbart 36930e9661 adding xterm comparison tests 2016-07-05 11:36:30 +02:00
Daniel Imms 838c11b69c Have ' ' chars get inserted correctly after a resize
Since the character spec didn't contain a character width it was being skipped
during refresh.

Fixes #159
2016-07-04 13:17:08 -07:00
Daniel Imms 05041722f1 Correct if condition 2016-07-04 12:15:47 -07:00
Paris Kasidiaris 36eb7efe5e Merge pull request #153 from sourcelair/fix/pasting
Fix Ctrl+Shift+V pasting
2016-06-30 14:29:28 +03:00
Daniel Imms 87c9370e8c Fix exception when increasing size when viewport is at top of buffer
Fixes #154
2016-06-30 02:05:06 -07:00
TDaglis 0b018fd4e9 Fix Ctrl+Shift+V pasting 2016-06-30 10:58:00 +03:00
Paris Kasidiaris 39c4356ef6 Merge pull request #148 from bfis/fix/MouseEvent_clone
Fix MouseEvent cloning
2016-06-27 16:04:33 +03:00
Benjamin Fischer 59b79e9b04 Fix MouseEvent cloning
Cloneing MouseEvent instances via __proto__ (lline 1099) caused the cloned object to be broken and throw "TypeError: Illegal invocation" during property access ("ev.shiftKey" line 1020).
2016-06-27 13:27:19 +02:00
Paris Kasidiaris b2e940f305 Merge pull request #144 from jerch/wcwidth
wcwidth calculation
2016-06-27 12:02:47 +03:00
Jörg Breitbart c3bc59b5e1 adding unicode test cases 2016-06-26 12:50:06 +02:00
Jörg Breitbart e3126ba3f8 cleanup 2016-06-25 10:12:21 +02:00
Jörg Breitbart f951abb7a9 wcwidth only for printables 2016-06-24 16:25:00 +02:00
Paris Kasidiaris 549a8cd31b Merge pull request #142 from Tyriar/patch-1
Only blink cursor if the terminal has focus
2016-06-24 05:58:38 -07:00
Jörg Breitbart 874ba72f4e wcwidth calculation 2016-06-24 14:16:12 +02:00
Daniel Imms ea1fd3d01b Only blink cursor if the terminal has focus
Fixes #141
2016-06-23 22:16:29 -07:00
Paris Kasidiaris 4c6b993458 Merge pull request #138 from dbkaplun/disable-ligatures
Disable ligatures which mess up rendering for certain fonts
2016-06-23 18:39:52 -07:00
Dan Kaplun 323cc44e0e Disable ligatures 2016-06-23 15:07:38 -07:00
Antonis Kalipetis 8bf81875ca Merge pull request #130 from Tyriar/windows_demo
Get demo working on Windows
2016-06-23 11:52:16 +03:00
Daniel Imms 15cf76c767 Bind to 127.0.0.1 on Windows only 2016-06-22 20:43:41 -07:00
Daniel Imms 3df272fb92 Merge remote-tracking branch 'upstream/master' into windows_demo 2016-06-22 20:36:05 -07:00
Paris Kasidiaris 04a8137f09 Merge pull request #137 from sourcelair/improve-paste
Fix cumbersome pasting with Cmd + V
2016-06-19 20:45:31 +03:00
Paris 3217a4bee4 Fix cumbersome pasting with Cmd + V
For some reason, setting the element's contentEditable value to true within a keyboard event, while the element has focus, did not allow pasting with clipboard, unless the element gets clicked explicitly.
2016-06-19 06:12:25 +03:00
Daniel Imms 2aab58473e Fix npm scripts 2016-06-16 16:31:01 -07:00
Daniel Imms 5d3bac6a86 Merge remote-tracking branch 'upstream/master' into windows_demo 2016-06-16 16:28:20 -07:00
Paris Kasidiaris 09d67a6c52 Merge pull request #131 from sourcelair/issue/120
Fix focus and blur events emission
2016-06-16 17:06:10 +03:00
Paris af7588ef16 On big refreshes remove term.rowContainer instead of term.element from DOM
Fix #120
2016-06-16 11:47:05 +03:00
Paris a6e85ad5f5 Progress with #120 2016-06-16 11:41:41 +03:00
Paris Kasidiaris d5e83902a5 Merge pull request #133 from Tyriar/132_fix_constant_focus
Only refresh when asked
2016-06-15 12:53:31 +03:00
Paris Kasidiaris 055614bbbc Merge pull request #121 from Tyriar/119_fix_cursor_blink
Fix cursor blinking when enabled
2016-06-15 09:51:49 +03:00
Daniel Imms 0d803ac895 Use CSS animations 2016-06-14 10:52:49 -07:00
Daniel Imms 253babff62 Merge remote-tracking branch 'upstream/master' into 119_fix_cursor_blink 2016-06-14 10:36:56 -07:00
Daniel Imms fac964bcf5 Only refresh when asked
Fixes #132
2016-06-14 10:33:24 -07:00
Paris Kasidiaris 02d809c194 Merge pull request #117 from Tyriar/86_protect_max_refresh
Bound refresh max rows to this.rows not this.lines
2016-06-14 11:11:04 +03:00
Paris Kasidiaris 5d1b6cfe97 Merge pull request #129 from sourcelair/docs
Improve docs building script and template
2016-06-14 10:56:08 +03:00
Paris Kasidiaris e37a9b62b7 Merge pull request #128 from Tyriar/127_debounce_refresh
Allow refresh to execute 30 times a second maximum
2016-06-14 10:49:53 +03:00
Daniel Imms 623853f3f4 Get demo working on Windows
- Removed bin bash scripts in favor of npm scripts so they work
  cross-platform
- Moved to use 127.0.0.1 as 0.0.0.0 doesn't work on Windows

Note that this doesn't work with the current pty.js as it doesn't build on
Windows.
2016-06-13 15:55:47 -07:00
Paris Kasidiaris 6c892e7161 Merge pull request #125 from sourcelair/addon-loader
Implement addon loader (CommonJS + RequireJS)
2016-06-14 00:58:23 +03:00
Paris be304c6ee7 Implement docs building 2016-06-14 00:21:17 +03:00
Paris 00f380a75c Implement test 2016-06-14 00:05:47 +03:00
Daniel Imms af29effbf3 Allow refresh to execute 30 times a second maximum
For commands that pass a significant amount of output to the write function,
this prevents the terminal maxing out the CPU and making the UI unresponsive.
While commands can still run beyond what they do on the terminal, it is far
better with a debounce in place as every single terminal manipulation does not
need to be constructed in the DOM.

A side-effect of this is that it makes ^C to interrupt a process seem more
responsive.

Fixes #127
Fixes #126
2016-06-13 12:40:56 -07:00
Paris 57300f5159 Implement addon loader (CommonJS + RequireJS)
Closes #96
2016-06-13 19:07:29 +03:00
Paris Kasidiaris 9d34ed8781 Merge pull request #122 from Tyriar/demo_options
Add basic options selection to demo
2016-06-13 15:03:10 +03:00
Paris Kasidiaris f3dd0a4ec8 Merge pull request #123 from Tyriar/jsdoc
Add some jsdoc for important functions
2016-06-13 14:58:17 +03:00
Antonis Kalipetis 9371520a73 Merge pull request #93 from Tyriar/linkify_ip_addr
Add IP addresses to linkify
2016-06-13 14:53:17 +03:00
Daniel Imms fd5be55d81 Add some jsdoc for important functions
Part of #13
2016-06-10 20:05:26 -07:00
Daniel Imms e0c0fb6917 Add basic options selection to demo
This will make it easier to test options without needing to modify the demo.
2016-06-10 19:15:58 -07:00
Daniel Imms f2f0f460c4 Add a bunch of tests 2016-06-10 18:53:32 -07:00
Daniel Imms 08928d8b8e Merge remote-tracking branch 'upstream/master' into linkify_ip_addr 2016-06-10 18:08:39 -07:00
Daniel Imms cb4728f8f5 Tidy up code 2016-06-10 18:05:24 -07:00
Daniel Imms 4a71a41380 Fix cursor blinking when enabled
Fixes #119
2016-06-10 18:05:13 -07:00
Daniel Imms 9c3b1105e2 Check optional port clause after domain too 2016-06-10 13:15:59 -07:00
Daniel Imms edf897182e Bound refresh max rows to this.rows not this.lines
this.row is the size of rows in the viewport, this.lines is the buffer. It's
only possible to refresh `0` to `this.rows - 1`.

Fixes #86
2016-06-10 13:08:43 -07:00
Paris Kasidiaris 7c1559b8ad Merge pull request #60 from runarberg/master
Fix cross platform input problems
2016-06-10 19:32:45 +03:00
runarberg b01165c1f8 Fix third level shifts for Mac OS and windows
ISO third level keys were not working. That prevented some inputting
important characters (like pipe `|` and caret `^`) on some keyboard
layouts.

Instead of parsing the user-agent string to find the users os, we now
look into the `platform` attribute of the `navigator` object.

Removed the hijacking of the command key `⌘` on Mac OS as the `Alt` key
on other systems.
2016-06-10 16:14:31 +00:00
Paris Kasidiaris b5d9c6b113 Merge pull request #115 from sourcelair/decimal-terminal-flags
Stop using binary literals
2016-06-10 19:07:25 +03:00
Paris 5a56849df8 Stop using binary literals. Non ES6 compatible 2016-06-10 19:02:49 +03:00
Paris Kasidiaris c908f849c5 Merge pull request #114 from sourcelair/fix-copy-nbsp
Stop copying non-breaking spaces into clipboard
2016-06-10 17:05:10 +03:00
Paris fed92ac5c8 Implement tests 2016-06-10 16:35:56 +03:00
Paris 00f4232ecc Export copied text processing to static method 2016-06-10 16:27:58 +03:00
Paris fa1cd89aa9 Fix copying of non-breaking spaces 2016-06-10 16:17:14 +03:00
Antonis Kalipetis 5193f4fae0 Merge pull request #109 from Tyriar/65_send_ctrl_l_r_key
Jump over words with ctrl+left/right
2016-06-10 15:57:10 +03:00
Daniel Imms 740ae96f5f Merge remote-tracking branch 'upstream/master' into 65_send_ctrl_l_r_key 2016-06-10 03:00:22 -07:00
Paris Kasidiaris 55ca998224 Merge pull request #110 from Tyriar/64_cursor_disappears_when_scrolling
Draw cursor at correct position when scrolling
2016-06-10 12:22:28 +03:00
Paris Kasidiaris fdff1ca793 Merge pull request #107 from runarberg/fix/ctrl-or-shift-insert-copy-pasting
Allow Ctrl/Shift + insert to copy/paste on windows and linux
2016-06-10 11:57:15 +03:00
Paris Kasidiaris fa729b8306 Merge pull request #112 from Tyriar/111_scroll_with_blank_lines_after_cursor
Improve scroll to work with blank lines after the cursor
2016-06-10 11:40:08 +03:00
Daniel Imms eee99f62e1 Improve scroll to work with blank lines after the cursor
This commit works fixes scrolling when there were blank lines after the cursor.
Here is what it does (blank rows are those added by running `clear`):

when increasing rows:
  if there are blank rows below the cursor:
    add a blank row to the bottom
  else if there is room in the buffer above the viewport
    scroll up
  else
    add a blank row to the bottom

when decreasing rows:
  if there are blank rows below the cursor:
    remove a blank row from the bottom
  else
    scroll down

Fixes #111
2016-06-09 21:13:48 -07:00
Daniel Imms da9f86f1a7 Draw cursor at correct position when scrolling
Fixes #64
2016-06-09 19:59:59 -07:00
Daniel Imms 0535f9425b More escape sequence tests 2016-06-09 19:25:29 -07:00
Daniel Imms c86fd87819 Add tests for F-keys 2016-06-09 19:00:40 -07:00
Daniel Imms 3a866cf202 Refactor escape sequence code and add tests 2016-06-09 18:41:54 -07:00
Daniel Imms e2aaa8d362 Jump over words with ctrl+left/right
Escape codes used: 5=ctrl, C=right, D=left

Fixes #65
2016-06-09 18:01:12 -07:00
runarberg 524db02228 Fix Ctrl/Shift + insert copy/paste
Many systems (including MS Windows and many linuxes) map `<Ctrl>` +
`<Insert>` to copy and `<Shift> + <Insert>` to paste. That serves as a
handy fallback when the more common `<Ctrl> + C` and `<Ctrl> + V`
keybindings have their default prevented to send signals to the
terminal.

Currently all keydown-events with the insert key send `\x1b[2~` to the
terminal. This commit won't send that key if either the `shiftKey` or
the `ctrlKey` are present. Instead it will enable `contentEditable` to
allow for pasting.
2016-06-09 13:45:02 +00:00
Paris Kasidiaris cf9d2348a1 Merge pull request #97 from Tyriar/redundant_resize
Don't resize when unnecessary
2016-06-09 10:53:16 +03:00
Paris Kasidiaris 92122bf29b Merge pull request #105 from sourcelair/issue/46
Introduce testing environment for xterm.js
2016-06-09 10:29:34 +03:00
Paris abb7a49584 Add CXX environment 2016-06-09 10:22:49 +03:00
Paris beff0db301 Add apt addon in travis for node-gyp dependency of pty.js 2016-06-09 10:18:39 +03:00
Paris ab1526b085 Add travis ci build status in xterm.js 2016-06-09 10:10:36 +03:00
Paris b91e737017 Add .travis.yml 2016-06-09 10:08:44 +03:00
Paris fa093e2bd2 Set up environment for testing 2016-06-09 10:04:53 +03:00
Paris Kasidiaris 2f001f8af9 Merge pull request #102 from Tyriar/dont_crash_demo_refresh
Don't crash demo server on refresh
2016-06-09 09:49:28 +03:00
Paris f87300d30e Remove old test-suite completely 2016-06-09 09:49:11 +03:00
Paris Kasidiaris 04ae96617d Merge pull request #101 from Tyriar/fix_resize_buffer
Fix buffer corruption after resizing rows
2016-06-09 09:13:06 +03:00
Daniel Imms 7a80efd8bd Don't crash demo server on refresh
Fixes #100
2016-06-08 19:28:55 -07:00
Daniel Imms df268ad5cd Fix buffer corruption after resizing rows
Lines from the buffer were incorrectly being removed when the viewport was
resized. This change removes the lines when the cursor is above them, if not it
shifts the viewport down. Some basic jsdoc comments were added to some Terminal
properties for future reference.

Fixes #98
Fixes #99
2016-06-08 19:08:32 -07:00
Daniel Imms e721bdc9f0 Don't resize when unnecessary 2016-06-08 17:38:34 -07:00
Paris Kasidiaris ae99ad83d0 Merge pull request #84 from sourcelair/fix/terminal-focus-event
Fix emission of terminal focus&blur events
2016-06-09 00:00:05 +03:00
Paris Kasidiaris c491537de1 Merge pull request #95 from sourcelair/issue/85
Stop marking spans as `inline-block`
2016-06-08 22:09:21 +03:00
Paris 383d59bbc3 Stop marking spans as inline-block (not needed after 670b179140)
Fix #85
2016-06-08 22:03:45 +03:00
Paris Kasidiaris 2471910244 Merge pull request #94 from Tyriar/patch-2
Add section on addons to README
2016-06-08 21:30:34 +03:00
Daniel Imms 2a36ff0839 Merge remote-tracking branch 'upstream/master' into patch-2 2016-06-07 23:34:35 -07:00
Paris Kasidiaris 136c838f30 Merge pull request #89 from Tyriar/readme_demo
Add demo section to README
2016-06-08 09:19:18 +03:00
Paris Kasidiaris bd3936cc9f Merge pull request #88 from Tyriar/patch-1
Add http:// to demo output address
2016-06-08 09:17:49 +03:00
Daniel Imms dab80b58ae Add section on addons to README 2016-06-07 22:31:39 -07:00
Daniel Imms 23cfa3d824 Add IP addresses to linkify
Fixes #91
2016-06-07 22:12:38 -07:00
Daniel Imms 289ff8092d Add demo section to README
Fixes #87
2016-06-07 17:08:37 -07:00
Daniel Imms 47b01786e2 Add http:// to demo output address
This makes it easy to launch the demo in a browser by right clicking the URL in
gnome-terminal
2016-06-07 16:43:33 -07:00
TDaglis 6bc1881727 Fix emission of terminal focus&blur events 2016-06-07 13:58:00 +03:00
Paris Kasidiaris 21d765b227 Merge pull request #82 from sourcelair/issue/79
Make cursor hollow on blur
2016-06-07 13:44:36 +03:00
Paris d82c008052 Make cursor hollow on blur
Fix #79
2016-06-07 10:42:21 +00:00
Paris Kasidiaris 11823ce890 Merge pull request #83 from sourcelair/issue/76
Set line height of terminal rows to normal
2016-06-07 13:39:47 +03:00
Paris 670b179140 Set line height of terminal rows to normal
Fix #76
2016-06-07 12:23:28 +03:00
Paris Kasidiaris 637c2eae43 Merge pull request #81 from sourcelair/bash-demo
Implement fully featured bash terminal demo with a static terminal fallback
2016-06-07 11:43:58 +03:00
Paris 53e8df40bb Implement fully featured terminal demo w/ static fallback 2016-06-07 08:41:41 +00:00
Paris Kasidiaris 180a7af75d Merge pull request #78 from sourcelair/drop-hidden-textarea
Drop hidden textarea for input handling
2016-06-07 09:36:44 +03:00
Paris 0fd0f45d8b Fix indentation 2016-06-06 19:38:13 +03:00
Paris 8754f8d895 Fix focus and blur events 2016-06-05 03:59:52 +00:00
Paris c02cc84498 Fix cut and copy events
- Do not actually cut on cut
- Strip trailing whitespaces on copy (fix #66)
2016-06-05 06:35:33 +03:00
Paris 647216876b Add a few hacks for better paste handling across browsers 2016-06-03 11:04:46 +03:00
Paris f3bd6145a5 Vanish selection when a key with data is being pressed 2016-06-02 13:31:38 +03:00
Paris 7320515820 Tiny styling update in row line height 2016-06-02 13:08:06 +03:00
Paris 8fa1a46584 Handle the paste event better 2016-06-02 13:05:58 +03:00
Paris fa7d214e78 Remove even more useless code 2016-06-02 12:35:03 +03:00
Paris 178b611b0c Remove useless methods 2016-06-02 12:27:43 +03:00
Paris Kasidiaris 1f28c22323 Merge pull request #74 from Tyriar/master
Remove extra whitespace from class names
2016-06-02 12:26:55 +03:00
Daniel Imms ae0f963799 Remove extra whitespace from class names
Use [].join to retain browser compatibility

Fixes #73
2016-06-02 02:17:03 -07:00
Paris 82cac90005 Remove helper styling from xterm.css 2016-06-02 12:02:51 +03:00
Paris 8ebfa27210 Merge branch 'master' of github.com:sourcelair/xterm.js into content-editable 2016-06-02 12:00:31 +03:00
Paris Kasidiaris 4babc92d7c Merge pull request #58 from sourcelair/issue/57
Proper rendering of inverse data in default colors
2016-05-30 23:37:01 +03:00
Paris 1a3846161a Proper rendering of inverse data in default colors
Fix #57
2016-05-30 16:03:26 +00:00
Paris Kasidiaris ff7d0e58dc Merge pull request #56 from sourcelair/issue/54
Explicitly adjust height of terminal rows and child elements
2016-05-30 14:52:59 +03:00
Paris 3d64f91a5c Properly adjust height of terminal rows and child elements
Fix #54
2016-05-26 11:41:42 +03:00
Paris Kasidiaris f980f32fe5 Merge pull request #53 from sourcelair/issue/52
Make all add-ons CommonJS importable
2016-05-04 11:24:36 +03:00
Paris dc67c94545 Change CommonJS package structure
Instead of making Xterm prototype extending optional, just require Xterm and pass it as parameter to the function just like it is being done with RequireJS
2016-05-03 12:48:16 +03:00
Paris fff56eea1b Extend Xterm prototype only when Xterm is a function
Add-ons attempted to extend the `Xterm` prototype no matter what. This was an issue when importing add-ons through CommonJS, where `Xterm` is not available in the global scope.
2016-05-03 12:36:21 +03:00
Paris bd2158613d Make all add-ons CommonJS importable
- Fix #52
- Rename `npm/nodejs project` documentation to `CommonJS environment`
2016-05-03 12:05:13 +03:00
Paris 537fbad705 Bump version to 0.33 2016-04-26 12:17:41 +03:00
Paris Kasidiaris 52cc8fa5fe Merge pull request #51 from blink1073/fix-blink
Fix cursor blink
2016-04-23 02:26:55 +03:00
Steven Silvester 7988f634f3 Handle focus clearing 2016-04-21 07:57:13 -05:00
Steven Silvester a5e34e2288 Fix cursor blink 2016-04-20 15:41:33 -05:00
Paris Kasidiaris ad3764ae4a Merge pull request #50 from blink1073/fix-resize
Use shift() when reducing lines to preserve lines up to cursor
2016-04-20 18:36:38 +03:00
Steven Silvester 685bc54bf2 Use shift to reduce lines to preserve lines up to cursor 2016-04-14 06:35:44 -05:00
Paris Kasidiaris a54eb8ac44 Merge pull request #49 from noseglid/nodejs-module
nodejs module definition
2016-04-12 14:18:03 +03:00
Alexander Olsson 6a87253d94 nodejs module definition
Allow importing xterm into  a nodejs application (for instance an electron
application, or perhaps a test environment using phantomjs which
also has a DOM),
2016-04-11 15:08:47 +02:00
Paris 6406a88ddf pointless 2016-04-08 18:10:27 +03:00
Paris 18b2a1c3b4 Handle drop event 2016-03-10 02:55:24 +02:00
Paris 58056df23d Start working on replacing textarea with contentEditable 2016-03-10 02:07:10 +02:00
Paris a88e27cc7d Bump version to 0.32 2016-02-09 14:50:34 +02:00
Paris 7cab2501da Merge branch 'master' of github.com:sourcelair/xterm.js 2016-02-09 12:49:21 +00:00
Paris dd93e62a41 Update README 2016-02-09 14:48:54 +02:00
Paris d238a1998c Update .gitignore to ingore npm logs 2016-02-09 14:42:33 +02:00
Paris Kasidiaris 6bbb21490c Merge pull request #45 from sourcelair/package-json
Package json
2016-02-06 18:41:54 +02:00
Paris 9f0fa2f815 Update package.json (name, repository, license) 2016-02-06 18:41:24 +02:00
Paris f5fb10063c Fix version 2016-01-09 19:43:56 +02:00
Paris 721bd272b5 Add package.json 2016-01-09 19:43:18 +02:00
Thanasis Daglis c170156147 Merge pull request #44 from sourcelair/issue/18
Cursor is now invisible in IE
2015-11-12 17:47:03 +02:00
TDaglis a2f6f2be03 added comment for fix
explained IE "hack" to fix textarea's blinking cursor
2015-11-12 15:05:51 +02:00
Thanasis Daglis 412260392f Merge pull request #43 from sourcelair/fix/marginal-terminal-width
Fixed marginal width geometry
2015-11-12 11:42:37 +02:00
TDaglis 5f8585a625 made cursor invisible in IE
fixes #18
2015-11-11 10:37:21 +02:00
TDaglis ee0d15c878 added *.css rules in .editorconfig for indent consistency 2015-11-11 10:36:20 +02:00
TDaglis 3f0b584f25 fixed marginal width geometry 2015-11-10 18:27:17 +02:00
Thanasis Daglis 41fd20c6e4 Merge pull request #42 from sourcelair/fix/fit-width
Fixed width of fit
2015-11-04 11:45:21 +02:00
Thanasis Daglis 01e9524098 Merge pull request #41 from sourcelair/fix/firefox-selection
Fixed selection in firefox
2015-11-04 11:45:09 +02:00
TDaglis d9d99c1c27 fixed width of fit
removed firefox hack since it is no longer needed
2015-11-03 14:55:24 +02:00
TDaglis f53f5735e1 added selection.type fallback check
for browsers that might not support Selection.isCollapsed property
2015-11-03 12:00:28 +02:00
TDaglis 7a88a4b0e9 fixed selection in firefox 2015-11-03 09:32:43 +00:00
Thanasis Daglis 85733fea56 Merge pull request #40 from sourcelair/fix/keydown-filtering
Better filtering of input
2015-10-29 18:36:19 +02:00
TDaglis 369e1f4c1e exclude alt and meta keys from ctrl default case 2015-10-29 18:34:20 +02:00
TDaglis 702fd24ce8 ignore ctrl+shift characters in keydown input 2015-10-29 18:27:46 +02:00
Thanasis Daglis 961e62207c Merge pull request #38 from sourcelair/fix/focus-events
Fix xterm focus events
2015-10-27 11:30:00 +02:00
TDaglis cc950614c5 blur textarea on terminal blur 2015-10-26 17:13:04 +02:00
TDaglis 556140e7d9 added .editorconfig 2015-10-26 17:07:34 +02:00
TDaglis 987f09d880 auto emit blur and focus of textarea 2015-10-26 15:07:15 +00:00
Thanasis Daglis 4cf2f9a847 Merge pull request #37 from sourcelair/fix/attach
Fixed typo and reference in attach addon
2015-10-22 12:17:24 +03:00
TDaglis 2fce845ea6 fixed typo and reference in attach addon
also bumped version to 0.31
2015-10-22 11:33:56 +03:00
Thanasis Daglis 9914fd9a38 Merge pull request #36 from sourcelair/linkify/enhance
Added lenient option to linkify
2015-10-22 10:26:44 +03:00
TDaglis a221a3c9f8 documented lenient option of linkify 2015-10-22 09:53:14 +03:00
TDaglis b1058b9fcc Added lenient option to linkify 2015-10-22 06:47:15 +00:00
Paris Kasidiaris 4c2508028a Merge pull request #32 from chani/patch-1
Update main.js
2015-09-08 17:29:06 +03:00
Jean Bruenn d516d4df7c Update main.js
just a tiny change, if you are using the demo file as example to start working with, prompt as variable might cause trouble if you do use prompt() rather use shellprompt as variable name instead of prompt. I'd suggest to rename term's prompt() as well to not cause confusion.
2015-09-08 14:08:44 +02:00
Paris Kasidiaris d20c36dff2 Merge pull request #31 from akalipetis/hotfix/ie
Fixed IE issues with classList.add
2015-08-25 15:37:29 +03:00
Antonis Kalipetis a523803d37 Fixed IE issues with classList.add
`classList.add` in IE does not take more than one parameter and this would not work. Split in 3 statements to fix it.
2015-08-25 14:40:00 +03:00
Paris bbb688313e [attach addon] Improve documentation 2015-04-13 19:47:04 +03:00
Paris d6c8369630 Update README.md 2015-04-13 19:21:47 +03:00
Paris 05eb5df93c Set up JSDoc documentation
Started working on #13
2015-04-13 19:18:09 +03:00
Paris 51fe17ee43 Mark version 0.30 2015-02-25 09:34:12 +02:00
Paris Kasidiaris 1d5737f843 Merge pull request #22 from sourcelair/issue/21
Introduce bufferred attaching to websocket
2015-02-25 09:31:35 +02:00
Paris 13103aecb2 [attach addon] Document with JSDoc 2015-02-24 22:28:47 +02:00
Paris e9d2c806ba [attach addon] Switch indentation to 4 spaces instead of 2 2015-02-24 20:57:28 +02:00
Paris 932ab00a53 Implement buffered attaching
Fixes #21
2015-02-24 20:55:52 +02:00
Paris Kasidiaris 6cfd957697 Merge pull request #20 from akalipetis/issue/19
Pressing Alt + ←/→ does not interfere now with browser
2015-01-28 11:52:55 +02:00
Antonis Kalipetis f00d8964c2 Replaced preventDefault with cancel 2015-01-28 11:20:47 +02:00
Antonis Kalipetis 9ef6b52352 Pressing Alt + ←/→ does not interfere now with browser
Previously, when on Chrome for Windows, if default was not prevented the browser history backward/forward was triggered.

Fixes #19
2015-01-28 11:04:38 +02:00
Paris Kasidiaris 2a9de5e5c0 Merge pull request #17 from sourcelair/issue/8
Bump to v0.29
2015-01-19 19:45:19 +02:00
paris 761e5db937 Merged with master 2015-01-19 19:45:03 +02:00
paris 391d8f4097 Bump version to 0.29 and copyright year to 2015 2015-01-19 19:07:28 +02:00
paris af39a3fcc0 Fix #8
Also fix some Internet Explorer incompatibilities
2015-01-19 19:06:17 +02:00
Paris Kasidiaris 69153c04a6 Merge pull request #16 from sourcelair/addon/linkify
Implement conversion of URLs to link elements in a terminal view
2015-01-15 12:14:23 +02:00
paris 85abfb8151 Bump version to 0.28 2015-01-06 13:12:31 +02:00
paris e100ee0cc0 [addon linkify] Further optimize URL algorithm 2015-01-06 13:03:07 +02:00
Paris Kasidiaris d5d48cd48a Merge pull request #15 from sourcelair/issue/14
Jump words back and forward with "Alt + ←" and "Alt + →"
2015-01-06 12:37:45 +02:00
paris 3e7ab290fa [addon linkify] Remove console.log 2015-01-05 16:07:29 +02:00
paris 43ac453db1 [addon linkify] Improve start and end clauses 2015-01-05 16:05:14 +02:00
paris d0d87505a9 [addon linkify] Improve URL to link conversion algorithm 2015-01-04 22:27:14 +02:00
paris c8a497eb82 [addon linkify] Fine-grained module and documented almost everything 2015-01-04 20:39:57 +02:00
paris 523ff9908f [addon linkify] Set up proper initialization of add-on
- Also created a demo page for the addon
2015-01-04 15:45:26 +02:00
paris 72ed03adcb Fix #14 2015-01-04 15:33:21 +02:00
paris b60903a2ae [addon linkify] Start working on "linkifying" URLs in the terminal 2015-01-04 15:08:36 +02:00
paris c5af270885 Mark version 0.27 2014-11-24 17:42:17 +02:00
Antonis Kalipetis 510990c312 Merge pull request #11 from sourcelair/issue/9
Fixed incorrect row/column calculation in Firefox
2014-11-24 17:36:18 +02:00
akalipetis 37be8e0f34 Fixed correct fitting of Terminal 2014-11-24 17:24:21 +02:00
paris 8dd80566d0 Fix #9
- Calculate character width by using the cursor instead of custom hack in fit addon
- Increment character width by 1 in Firefox, since it seems to have a bug in calculating width of specific elements
2014-11-17 17:08:14 +02:00
paris c6a7e15b03 [fit addon] Restore row content after proposing geometry
- Restore `subjectRow.innerHTML`, using the `contentBuffer`
- Bump version to 0.25
2014-11-17 12:36:13 +02:00
paris 87979aeed4 Bind window to AMD module
- Bind window to Xterm AMD module
- Fix typo in fit addon
- Bump version to 0.24
2014-11-17 12:28:52 +02:00
paris 615f633f25 [fit addon] Introduce proposeGeometry method
- Introduce new method to propose optimal geometry for the current terminal
- Bump version to 0.23
2014-11-17 12:21:05 +02:00
paris 567b39dc98 Fix #6 2014-09-22 05:48:05 +00:00
paris 0b2bdb182b Bump version to 0.22 2014-09-18 11:11:45 +00:00
paris 4ce1440fb8 [addon attach] Make reference to socket fail-safe 2014-09-18 11:11:29 +00:00
paris f24c020aca [addon attach] Fix reference error
Also bump version to 0.21
2014-09-18 10:15:10 +00:00
paris 3fca219404 [addon attach] Implement detach method (Bump to 0.20) 2014-09-18 10:02:29 +00:00
paris 6906bf3e7d Implement detach function 2014-09-18 10:01:54 +00:00
paris 670efc4454 Bump Bower version to 0.19 2014-09-18 09:06:53 +00:00
paris 74f9526177 [addon attach] Implement auto-detaching on socket close/error 2014-09-18 09:06:23 +00:00
paris 28b553caa6 Bump version to 0.18 2014-09-15 13:52:20 +00:00
paris 22ff176c6a Fix #5 2014-09-15 13:51:42 +00:00
paris d24e00554d Tiny README updates 2014-09-10 11:15:24 +00:00
paris bc67c788d2 Bump version to 0.17 2014-09-10 11:09:59 +00:00
paris 3f455f90d9 Fix #4 2014-09-10 11:09:23 +00:00
paris ede9ee416a Bump version to 0.16 2014-08-29 09:58:27 +00:00
paris 53f5672524 Fix #3 2014-08-29 09:57:53 +00:00
paris 9d00eec6c4 Fix #2 2014-08-27 09:50:09 +00:00
paris 3b1f1ddb2d Bump version to 0.15 2014-08-19 13:25:27 +00:00
paris a249d2714f Fix terminal fitting height 2014-08-19 13:24:43 +00:00
Paris Kasidiaris 3d40dcf3c5 Update company name 2014-08-18 11:00:49 +03:00
paris d07f61314b Bump version to 0.14 2014-08-16 03:53:52 +00:00
paris 7355a04274 Further fixing of xterm.fit 2014-08-16 03:53:22 +00:00
paris 3a782972e3 Bump version to 0.13 2014-08-16 03:25:46 +00:00
paris d12406bedc Fix wrong behavior of fit add-on 2014-08-16 03:25:13 +00:00
paris 561fc59dbc Added screenshot for docs
Bumped version to 0.12
2014-05-03 00:30:04 +00:00
paris ce1d788efe Bumped bower version to 0.11 2014-05-02 23:45:20 +00:00
paris 01e48b7406 Revamped the attach addon
Also updated docs with proper attach url
2014-05-02 23:45:00 +00:00
paris a1717fdd8d Update docs index 2014-05-02 23:21:07 +00:00
paris c9f5f235e2 Started documenting methods
Also added Getting Started section in docs
2014-05-02 22:51:38 +00:00
paris 21dde3cfa2 Updated version, in docs 2014-05-02 22:34:15 +00:00
paris 73bf6d1ce8 Started documenting events 2014-05-02 18:57:48 +00:00
paris 1b6ad0fd7b Bumped Bower version to 0.10 2014-04-11 02:12:37 +00:00
paris 189a556408 Moved fit into its own addon 2014-04-11 02:04:56 +00:00
paris fb1c484224 Added top: 0 to helpers 2014-04-10 21:45:54 +00:00
paris 59ebbbaaf0 Fix helper position 2014-04-10 18:48:36 +00:00
paris 6e6b5dad8c fit() uses offset height and width now 2014-04-10 18:36:37 +00:00
paris 0b603952c3 Removed debugging stuff
Bumped bower version to 0.9
2014-04-10 16:04:59 +00:00
244 changed files with 22646 additions and 5020 deletions
+16
View File
@@ -0,0 +1,16 @@
node_modules/
*.swp
.lock-wscript
lib/
Makefile.gyp
*.Makefile
*.target.gyp.mk
*.node
example/*.log
docs/
npm-debug.log
/.idea/
.env
build/
.vscode/
.git/
+17
View File
@@ -0,0 +1,17 @@
root = true
[*]
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf
[*.{j,t}s]
max_line_length = 100
[*.css]
indent_size = 4
[*.md]
trim_trailing_whitespace = false
+1
View File
@@ -0,0 +1 @@
* text=auto
+11
View File
@@ -0,0 +1,11 @@
<!-- Enter your issue description here -->
## Details
- Browser and browser version:
- OS version:
- xterm.js version:
### Steps to reproduce
1.
2.
+10 -3
View File
@@ -1,11 +1,18 @@
node_modules/
*.swp
build/*
.lock-wscript
out/
lib/
Makefile.gyp
*.Makefile
*.target.gyp.mk
*.node
example/*.log
docs/_build
docs/
npm-debug.log
/.idea/
.env
build/
.vscode/
# Directories needed for code coverage
/coverage/
+7
View File
@@ -0,0 +1,7 @@
Antonis Kalipetis <akalipetis@sourcelair.com>
Antonis Kalipetis <akalipetis@sourcelair.com> <akalipetis@gmail.com>
Daniel Imms <daimms@microsoft.com> <tyriar@tyriar.com>
Paris Kasidiaris <paris@sourcelair.com>
Paris Kasidiaris <pariskasidiaris@gmail.com>
Thanasis Daglis <thanasis@sourcelair.com>
Thanasis Daglis <thanasis@sourcelair.com> <ath.daglis@gmail.com>
+16
View File
@@ -0,0 +1,16 @@
node_modules/
*.swp
.lock-wscript
lib/*.test.js
lib/*.test.js.map
lib/test/
Makefile.gyp
*.Makefile
*.target.gyp.mk
*.node
example/*.log
docs/
npm-debug.log
/.idea/
.env
build/
+19
View File
@@ -0,0 +1,19 @@
language: node_js
node_js:
- 6
env:
global:
- CXX=g++-4.8
matrix:
- NPM_COMMAND=lint
- NPM_COMMAND=test
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
notifications:
email: false
script: npm run $NPM_COMMAND
after_success: npm run coveralls
+23
View File
@@ -0,0 +1,23 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Mocha Tests",
"cwd": "${workspaceRoot}",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/mocha",
"windows": {
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/mocha.cmd"
},
"runtimeArgs": [
"--colors",
"--recursive",
"${workspaceRoot}/lib"
],
"sourceMaps": true,
"outFiles": [ "${workspaceRoot}/lib/**/*.js" ],
"internalConsoleOptions": "openOnSessionStart"
}
]
}
+62
View File
@@ -0,0 +1,62 @@
List of xterm.js contributors. Updated before every release.
Aleksandr Andrienko <aandrienko@codenvy.com>
Aleksandr Andriienko <oandriie@redhat.com>
Alessandro Nadalin <alessandro.nadalin@gmail.com>
Alexander Olsson <noseglid@gmail.com>
Alexey Kontsevoy <alexey@gravitational.com>
Anish Athalye <me@anishathalye.com>
Antonis Kalipetis <akalipetis@sourcelair.com>
Anton Skshidlevsky <meefik@gmail.com>
Anton Yurovskykh <anton.yurovskykh@gmail.com>
Artem Arbatskiy <artyom.arbatskiy@gmail.com>
Austin Robertson <austinrobertson@gmail.com>
ayapi <colors.aya@gmail.com>
Ben Hall <ben@benhall.me.uk>
Benjamin Fischer <benjamin.fischer@rwth-aachen.de>
Bill Church <billchurch@users.noreply.github.com>
Bob Reid <bobreid@Bobs-MacBook-Pro.local>
bottleofwater <nison.mael+bottleofwater@gmail.com>
Brian Mock <brian@mockbrian.com>
Carson Anderson <carson@betterservers.com>
CHaBou <chabup@delean.fr>
Christian Budde Christensen <budde377@gmail.com>
Christopher Jeffrey <chjjeffrey@gmail.com>
coderaiser <mnemonic.enemy@gmail.com>
Daniel Imms <daimms@microsoft.com>
Daniel Risacher <drisacher@gmail.com>
Dan Kaplun <dbkaplun@twitch.tv>
Darin Morrison <freebroccolo@users.noreply.github.com>
Edgar Andrés Margffoy Tuay <andfoy@gmail.com>
Elliot Saba <staticfloat@gmail.com>
Gary Ritchie <gary@rstudio.com>
hiro-su <h.sugipon@gmail.com>
Ian Lewis <ianlewis@google.com>
imoses <ido@twiggle.com>
InDieTasten <indietasten@gmail.com>
irokas <akasidiari@inf.uth.gr>
Jean Bruenn <himself@jeanbruenn.info>
Jeremy Danyow <jedanyow@microsoft.com>
Jörg Breitbart <jerch@rockborn.de>
Justin Mecham <justin@mecham.me>
Lucian Buzzo <lucian.buzzo@gmail.com>
Maël Nison <nison.mael@gmail.com>
Martin Chloride <i@martincl2.me>
Martin Wang <jiahaow@ca.ibm.com>
Michael Irwin <mikesir87@gmail.com>
Mikko Karvonen <mikko.karvonen@arm.com>
Nicolas Ramz <nicolas.ramz@gmail.com>
Oleksandr Andriienko <oandriie@redhat.com>
Paris Kasidiaris <pariskasidiaris@gmail.com>
Paris Kasidiaris <paris@sourcelair.com>
runarberg <runar@greenqloud.com>
Saswat Das <saswatds@users.noreply.github.com>
Saul Costa <saul@codevolve.com>
Shuanglei Tao <tsl0922@gmail.com>
Steven Silvester <steven.silvester@ieee.org>
Thanasis Daglis <thanasis@sourcelair.com>
Tine Jozelj <tine.jozelj@outlook.com>
Tyler Jewell <tjewell@codenvy.com>
Vincent Woo <me@vincentwoo.com>
yutaka <shigure@refy.net>
YuviPanda <yuvipanda@gmail.com>
+52
View File
@@ -0,0 +1,52 @@
# How to contribute to xterm.js
- [Opening issues for bug reports or feature requests](#opening-issues)
- [Contributing code](#contributing-code)
## Opening issues
The preferred way to report bugs or request features is to use
[GitHub issues](http://github.com/sourcelair/xterm.js/issues). Before
opening an issue, read these pointers.
### Opening issues effectively
- Include information about **the browser in which the problem occurred**. Even
if you tested several browsers, and the problem occurred in all of them,
mention this fact in the bug report. Also include browser version numbers and
the operating system that you're on.
- Mention which release of xterm.js you're using. Preferably, try also with
the current HEAD of the master branch, to ensure the problem has not already been
fixed.
- Mention precisely what went wrong. What did you expect to happen? What happened instead? Describe the
exact steps a maintainer has to take to make the problem occur.
- If the problem can not be reproduced in the [demo of xterm.js](README.md#demo), please provide an HTML document that demonstrates the problem.
- Be polite. Issues with an indignant or belligerent tone tend to be moved to the
bottom of the pile.
## Contributing code
- Make sure you have a [GitHub account](https://github.com/join)
- Fork [xterm.js](https://github.com/sourcelair/xterm.js/)
([how to fork a repo](https://help.github.com/articles/fork-a-repo))
- Make your changes
- If your changes are easy to test or likely to regress, add tests. Tests go into `test`, directory.
- Follow the general code style of the rest of the project (see below).
- Submit a pull request
([how to create a pull request](https://help.github.com/articles/fork-a-repo)).
Don't put more than one feature/fix in a single pull request.
By contributing code to xterm.js you
- agree to license the contributed code under xterm.js' [MIT
license](LICENSE).
- confirm that you have the right to contribute and license the code
in question. (Either you hold all rights on the code, or the rights
holder has explicitly granted the right to use it like this,
through a compatible open source license or through a direct
agreement with you.)
+19
View File
@@ -0,0 +1,19 @@
FROM node:6.9
MAINTAINER Paris Kasidiaris <paris@sourcelair.com>
# Set the working directory
WORKDIR /usr/src/app
# Set an entrypoint, to automatically install node modules
ENTRYPOINT ["/bin/bash", "-c", "if [[ ! -d node_modules ]]; then npm install; fi; exec \"${@:0}\";"]
CMD ["npm", "run", "dev"]
# First, install dependencies to improve layer caching
COPY package.json /usr/src/app/
RUN npm install
# Add the code
COPY . /usr/src/app
# Run the tests and build, to make sure everything is working nicely
RUN npm run build && npm run test
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2014, sourceLair Limited (https://github.com/sourcelair/)
Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com)
Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/)
Permission is hereby granted, free of charge, to any person obtaining a copy
+1
View File
@@ -0,0 +1 @@
web: npm start
+1
View File
@@ -0,0 +1 @@
web: npm run dev
+134 -8
View File
@@ -1,14 +1,140 @@
# xterm.js
# [![xterm.js logo](logo.png)](https://xtermjs.org)
xterm, in the browser.
[![xterm.js build status](https://api.travis-ci.org/sourcelair/xterm.js.svg)](https://travis-ci.org/sourcelair/xterm.js) [![Coverage Status](https://coveralls.io/repos/github/sourcelair/xterm.js/badge.svg)](https://coveralls.io/github/sourcelair/xterm.js) [![Gitter](https://badges.gitter.im/sourcelair/xterm.js.svg)](https://gitter.im/sourcelair/xterm.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
### Contribution and License Agreement
Xterm.js is a terminal front-end component written in JavaScript that works in the browser.
If you contribute code to this project, you are implicitly allowing your code
to be distributed under the MIT license. You are also implicitly verifying that
all code is your original work.
It enables applications to provide fully featured terminals to their users and create great development experiences.
## License
## Features
- **Text-based application support**: Use xterm.js to work with applications like `bash`, `git` etc.
- **Curses-based application support**: Use xterm.js to work with applications like `vim`, `tmux` etc.
- **Mouse events support**: Xterm.js captures mouse events like click and scroll and passes them to the terminal's back-end controlling process
- **CJK (Chinese, Japanese, Korean) character support**: Xterm.js renders CJK characters seamlessly
- **IME support**: Insert international (including CJK) characters using IME input with your keyboard
- **Self-contained library**: Xterm.js works on its own. It does not require any external libraries like jQuery or React to work
- **Modular, event-based API**: Lets you build addons and themes with ease
## What xterm.js is not
- Xterm.js is not a terminal application that you can download and use on your computer
- Xterm.js is not `bash`. Xterm.js can be connected to processes like `bash` and let you interact with them (provide input, receive output)
## Real-world uses
Xterm.js is used in several world-class applications to provide great terminal experiences.
- [**SourceLair**](https://www.sourcelair.com/): In-browser IDE that provides its users with fully-featured Linux terminals based on xterm.js
- [**Microsoft Visual Studio Code**](http://code.visualstudio.com/): Modern, versatile and powerful open source code editor that provides an integrated terminal based on xterm.js
- [**ttyd**](https://github.com/tsl0922/ttyd): A command-line tool for sharing terminal over the web, with fully-featured terminal emulation based on xterm.js
- [**Katacoda**](https://www.katacoda.com/): Katacoda is an Interactive Learning Platform for software developers, covering the latest Cloud Native technologies.
- [**Eclipse Che**](http://www.eclipse.org/che): Developer workspace server, cloud IDE, and Eclipse next-generation IDE.
- [**Codenvy**](http://www.codenvy.com): Cloud workspaces for development teams.
- [**CoderPad**](https://coderpad.io): Online interviewing platform for programmers. Run code in many programming languages, with results displayed by `xterm.js`.
- [**WebSSH2**](https://github.com/billchurch/WebSSH2): A web based SSH2 client using `xterm.js`, socket.io, and ssh2.
- [**Spyder Terminal**](https://github.com/spyder-ide/spyder-terminal): A full fledged system terminal embedded on Spyder IDE.
- [**Cloud Commander**](https://cloudcmd.io "Cloud Commander"): Orthodox web file manager with console and editor.
- [**Codevolve**](https://www.codevolve.com "Codevolve"): Online platform for interactive coding and web development courses. Live container-backed terminal uses `xterm.js`.
- [**RStudio**](https://www.rstudio.com/products/RStudio "RStudio"): RStudio is an integrated development environment (IDE) for R.
- [**Terminal for Atom**](https://github.com/jsmecham/atom-terminal-tab): A simple terminal for the Atom text editor.
- [**Eclipse Orion**](https://orionhub.org): A modern, open source software development environment that runs in the cloud. Code, deploy and run in the cloud.
- [**Gravitational Teleport**](https://github.com/gravitational/teleport): Gravitational Teleport is a modern SSH server for remotely accessing clusters of Linux servers via SSH or HTTPS.
- [**Hexlet**](https://en.hexlet.io): Practical programming courses (JavaScript, PHP, Unix, databases, functional programming). A steady path from the first line of code to the first job.
Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it in our list.
## Browser Support
Since xterm.js is typically implemented as a developer tool, only modern browsers are supported officially. Here is a list of the versions we aim to support:
- Chrome 48+
- Edge 13+
- Firefox 44+
- Internet Explorer 11+
- Opera 35+
- Safari 8+
Xterm.js works seamlessly in Electron apps and may even work on earlier versions of the browsers but these are the browsers we strive to keep working.
## Demo
### Linux or macOS
Run the following commands:
```
$ npm install
$ npm start
```
Then open http://0.0.0.0:3000 in a web browser.
### Windows
First, ensure [node-gyp](https://github.com/nodejs/node-gyp) is installed and configured correctly, then run these commands.
Note: Do not use ConEmu, as it seems to break the demo for some reason.
```
> npm install
> npm start
```
Then open http://127.0.0.1:3000 in a web browser.
## Getting Started
To start using xterm.js on your browser, add the `xterm.js` and `xterm.css` to the head of your html page. Then create a `<div id="terminal"></div>` onto which xterm can attach itself.
```html
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="bower_components/xterm.js/dist/xterm.css" />
<script src="bower_components/xterm.js/dist/xterm.js"></script>
</head>
<body>
<div id="terminal"></div>
<script>
var term = new Terminal();
term.open(document.getElementById('terminal'));
term.write('Hello from \033[1;3;31mxterm.js\033[0m $ ')
</script>
</body>
</html>
```
Finally instantiate the `Terminal` object and then call the `open` function with the DOM object of the `div`.
## Addons
Addons are JavaScript modules that attach functions to the `Terminal` prototype to extend its functionality. There are a handful available in the main repository in the `dist/addons` directory, you can even write your own (though they may break when the internals of xterm.js change across versions).
To use an addon, just include the JavaScript file after xterm.js and before the `Terminal` object has been instantiated. The function should then be exposed on the `Terminal` object:
```html
<script src="node_modules/dist/xterm.js"></script>
<script src="node_modules/dist/addons/fit/fit.js"></script>
```
```js
var xterm = new Terminal();
// init code...
xterm.fit();
```
## Releases
Xterm.js follows a monthly release cycle roughly.
The existing releases are available at this GitHub repo's [Releases](https://github.com/sourcelair/xterm.js/releases), while the roadmap is available as [Milestones](https://github.com/sourcelair/xterm.js/milestones).
## Development and Contribution
Xterm.js is maintained by [SourceLair](https://www.sourcelair.com/) and a few external contributors, but we would love to receive contributions from everyone!
To contribute either code, documentation or issues to xterm.js please read the [Contributing document](CONTRIBUTING.md) beforehand. The development of xterm.js does not require any special tool. All you need is an editor that supports JavaScript/TypeScript and a browser. You will need Node.js installed locally to get all the features working in the demo.
## License Agreement
If you contribute code to this project, you are implicitly allowing your code to be distributed under the MIT license. You are also implicitly verifying that all code is your original work.
Copyright (c) 2014-2016, SourceLair, Private Company ([www.sourcelair.com](https://www.sourcelair.com/home)) (MIT License)
Copyright (c) 2014, sourceLair Limited (www.sourcelair.com) (MIT License)
Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
-23
View File
@@ -1,23 +0,0 @@
/*
* Implements the attach method, that
* attaches the terminal to a WebSocket stream.
*
* The bidirectional argument indicates, whether the terminal should
* send data to the socket as well and is true, by default.
*/
Terminal.prototype.attach = function (socket, bidirectional) {
var term = this;
bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional;
this.socket = socket;
socket.addEventListener('message', function (ev) {
term.write(ev.data);
});
if (bidirectional) {
this.on('data', function (data) {
socket.send(data);
});
}
}
-38
View File
@@ -1,38 +0,0 @@
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="../../src/xterm.css" />
<link rel="stylesheet" href="../../demo/style.css" />
<script src="../../src/xterm.js"></script>
<script src="attach.js"></script>
</head>
<body>
<form id="socket-form">
<input id="socket-url"
type="text"
placeholder="Enter socket url (e.g. ws://mysock)"
autofocus />
<button>
Attach
</button>
</form>
<div id="terminal-container"></div>
<script>
var term = new Terminal(),
container = document.getElementById('terminal-container'),
socketUrl = document.getElementById('socket-url'),
socketForm = document.getElementById('socket-form');
socketForm.addEventListener('submit', function (ev) {
ev.preventDefault();
var url = socketUrl.value,
sock = new WebSocket(url);
sock.addEventListener('open', function () {
term.attach(sock);
});
});
term.open(container);
</script>
</body>
</html>
-25
View File
@@ -1,25 +0,0 @@
/*
* Fullscreen addon for xterm.js
*
* Implements the toggleFullscreen function.
*
* If the `fullscreen` argument has been supplied, then
* if it is true, the fullscreen mode gets turned on,
* if it is false or null, the fullscreen mode gets turned off.
*
* If the `fullscreen` argument has not been supplied, the
* fullscreen mode is being toggled.
*/
Terminal.prototype.toggleFullscreen = function (fullscreen) {
var fn;
if (typeof fullscreen == 'undefined') {
fn = (this.element.classList.contains('fullscreen')) ? 'remove' : 'add';
} else if (!fullscreen) {
fn = 'remove';
} else {
fn = 'add';
}
this.element.classList[fn]('fullscreen');
}
+7
View File
@@ -0,0 +1,7 @@
#! /usr/bin/env sh
tail --lines=+3 AUTHORS > AUTHORS.tmp
git log --format='%aN <%aE>' >> AUTHORS.tmp
echo "List of xterm.js contributors. Updated before every release.\n" > AUTHORS
sort -u AUTHORS.tmp >> AUTHORS
rm -f AUTHORS.tmp
+34
View File
@@ -0,0 +1,34 @@
#! /usr/bin/env sh
# Usage: ./bin/prepare-release x.y.z
# x.y.z should be semver (e.g. 1.0.0)
set -e
NEW_VERSION=$1
CURRENT_PACKAGE_JSON_VERSION=$(cat package.json \
| grep version \
| head -1 \
| awk -F: '{ print $2 }' \
| sed 's/[",]//g' \
| tr -d '[[:space:]]')
CURRENT_BOWER_JSON_VERSION=$(cat bower.json \
| grep version \
| head -1 \
| awk -F: '{ print $2 }' \
| sed 's/[",]//g' \
| tr -d '[[:space:]]')
# Build xterm.js into `dist`
export BUILD_DIR=dist
npm run build
# Update AUTHORS file
sh bin/generate-authors
# Update version in package.json and bower.json
sed -i "s/\"version\": \"$CURRENT_PACKAGE_JSON_VERSION\"/\"version\": \"$NEW_VERSION\"/g" package.json
sed -i "s/\"version\": \"$CURRENT_BOWER_JSON_VERSION\"/\"version\": \"$NEW_VERSION\"/g" bower.json
git commit -S -s -a -m "Bump version to $NEW_VERSION"
git tag $NEW_VERSION
Executable
+22
View File
@@ -0,0 +1,22 @@
#! /usr/bin/env sh
# Usage: ./bin/release x.y.z
# x.y.z should be semver (e.g. 1.0.0)
set -e
if [ -z "$1" ];
then
echo "No version supplied. Please a version argument\n"
echo "Usage: $0 VERSION\n"
echo "Example: $0 1.0.0"
exit
fi
NEW_VERSION=$1
./bin/prepare-release $NEW_VERSION
git push && \
git push --tags && \
npm publish
+6 -2
View File
@@ -1,5 +1,9 @@
{
"name": "xterm.js",
"version": "0.8",
"ignore": ["demo", "docs", "test", ".gitignore"]
"version": "2.8.0",
"ignore": ["demo", "test", ".gitignore"],
"main": [
"dist/xterm.js",
"dist/xterm.css"
]
}
+84
View File
@@ -0,0 +1,84 @@
var express = require('express');
var app = express();
var expressWs = require('express-ws')(app);
var os = require('os');
var pty = require('node-pty');
var terminals = {},
logs = {};
app.use('/build', express.static(__dirname + '/../build'));
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});
app.get('/style.css', function(req, res){
res.sendFile(__dirname + '/style.css');
});
app.get('/main.js', function(req, res){
res.sendFile(__dirname + '/main.js');
});
app.post('/terminals', function (req, res) {
var cols = parseInt(req.query.cols),
rows = parseInt(req.query.rows),
term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], {
name: 'xterm-color',
cols: cols || 80,
rows: rows || 24,
cwd: process.env.PWD,
env: process.env
});
console.log('Created terminal with PID: ' + term.pid);
terminals[term.pid] = term;
logs[term.pid] = '';
term.on('data', function(data) {
logs[term.pid] += data;
});
res.send(term.pid.toString());
res.end();
});
app.post('/terminals/:pid/size', function (req, res) {
var pid = parseInt(req.params.pid),
cols = parseInt(req.query.cols),
rows = parseInt(req.query.rows),
term = terminals[pid];
term.resize(cols, rows);
console.log('Resized terminal ' + pid + ' to ' + cols + ' cols and ' + rows + ' rows.');
res.end();
});
app.ws('/terminals/:pid', function (ws, req) {
var term = terminals[parseInt(req.params.pid)];
console.log('Connected to terminal ' + term.pid);
ws.send(logs[term.pid]);
term.on('data', function(data) {
try {
ws.send(data);
} catch (ex) {
// The WebSocket is not open, ignore
}
});
ws.on('message', function(msg) {
term.write(msg);
});
ws.on('close', function () {
term.kill();
console.log('Closed terminal ' + term.pid);
// Clean things up
delete terminals[term.pid];
delete logs[term.pid];
});
});
var port = process.env.PORT || 3000,
host = os.platform() === 'win32' ? '127.0.0.1' : '0.0.0.0';
console.log('App listening to http://' + host + ':' + port);
app.listen(port, host);
+47 -9
View File
@@ -2,17 +2,55 @@
<html>
<head>
<title>xterm.js demo</title>
<link rel="stylesheet" href="../src/xterm.css" />
<link rel="stylesheet" href="../addon/fullscreen/fullscreen.css" />
<link rel="stylesheet" href="/build/xterm.css" />
<link rel="stylesheet" href="/build/addons/fullscreen/fullscreen.css" />
<link rel="stylesheet" href="style.css" />
<script src="../src/xterm.js"></script>
<script src="../addon/fullscreen/fullscreen.js"></script>
<script src="main.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-promise/4.1.1/es6-promise.auto.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/1.0.0/fetch.min.js"></script>
<script src="/build/xterm.js" ></script>
<script src="/build/addons/attach/attach.js" ></script>
<script src="/build/addons/fit/fit.js" ></script>
<script src="/build/addons/fullscreen/fullscreen.js" ></script>
</head>
<body>
<h1>
xterm.js: xterm, in the browser
</h1>
<h1>xterm.js: xterm, in the browser</h1>
<div id="terminal-container"></div>
<div>
<h2>Options</h2>
<p>
<label><input type="checkbox" id="option-cursor-blink"> cursorBlink</label>
</p>
<p>
<label>
cursorStyle
<select id="option-cursor-style">
<option value="block">block</option>
<option value="underline">underline</option>
<option value="bar">bar</option>
</select>
</label>
</p>
<p>
<label>scrollback <input type="number" id="option-scrollback" value="1000" /></label>
</p>
<p>
<label>tabStopWidth <input type="number" id="option-tabstopwidth" value="8" /></label>
</p>
<div>
<h3>Size</h3>
<div>
<div style="display: inline-block; margin-right: 16px;">
<label for="cols">Columns</label>
<input type="number" id="cols" />
</div>
<div style="display: inline-block; margin-right: 16px;">
<label for="rows">Rows</label>
<input type="number" id="rows" />
</div>
</div>
</div>
</div>
<p><strong>Attention:</strong> The demo is a barebones implementation and is designed for xterm.js evaluation purposes only. Exposing the demo to the public as is would introduce security risks for the host.</p>
<script src="main.js" defer ></script>
</body>
</html>
</html>
+133 -18
View File
@@ -1,26 +1,141 @@
var term,
protocol,
socketURL,
socket,
pid,
charWidth,
charHeight;
var terminalContainer = document.getElementById('terminal-container'),
term = new Terminal();
optionElements = {
cursorBlink: document.querySelector('#option-cursor-blink'),
cursorStyle: document.querySelector('#option-cursor-style'),
scrollback: document.querySelector('#option-scrollback'),
tabstopwidth: document.querySelector('#option-tabstopwidth')
},
colsElement = document.getElementById('cols'),
rowsElement = document.getElementById('rows');
term.open(terminalContainer);
term.fit();
function setTerminalSize () {
var cols = parseInt(colsElement.value, 10),
rows = parseInt(rowsElement.value, 10),
width = (cols * charWidth).toString() + 'px',
height = (rows * charHeight).toString() + 'px';
term.prompt = function () {
term.write('\r\n> ');
terminalContainer.style.width = width;
terminalContainer.style.height = height;
term.resize(cols, rows);
}
term.writeln('Welcome to xterm.js');
term.writeln('Just type some keys in the prompt below.');
term.writeln('');
term.prompt();
colsElement.addEventListener('change', setTerminalSize);
rowsElement.addEventListener('change', setTerminalSize);
term.on('key', function (key, ev) {
var printable = (!ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey);
optionElements.cursorBlink.addEventListener('change', function () {
term.setOption('cursorBlink', optionElements.cursorBlink.checked);
});
optionElements.cursorStyle.addEventListener('change', function () {
term.setOption('cursorStyle', optionElements.cursorStyle.value);
});
optionElements.scrollback.addEventListener('change', function () {
term.setOption('scrollback', parseInt(optionElements.scrollback.value, 10));
});
optionElements.tabstopwidth.addEventListener('change', function () {
term.setOption('tabStopWidth', parseInt(optionElements.tabstopwidth.value, 10));
});
if (ev.keyIdentifier == 'Enter') {
term.prompt();
} else if (ev.keyCode == 8) {
term.write('\b \b');
} else if (printable) {
term.write(key);
createTerminal();
function createTerminal() {
// Clean terminal
while (terminalContainer.children.length) {
terminalContainer.removeChild(terminalContainer.children[0]);
}
});
term = new Terminal({
cursorBlink: optionElements.cursorBlink.checked,
scrollback: parseInt(optionElements.scrollback.value, 10),
tabStopWidth: parseInt(optionElements.tabstopwidth.value, 10)
});
term.on('resize', function (size) {
if (!pid) {
return;
}
var cols = size.cols,
rows = size.rows,
url = '/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows;
fetch(url, {method: 'POST'});
});
protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/terminals/';
term.open(terminalContainer);
term.fit();
var initialGeometry = term.proposeGeometry(),
cols = initialGeometry.cols,
rows = initialGeometry.rows;
colsElement.value = cols;
rowsElement.value = rows;
fetch('/terminals?cols=' + cols + '&rows=' + rows, {method: 'POST'}).then(function (res) {
charWidth = Math.ceil(term.element.offsetWidth / cols);
charHeight = Math.ceil(term.element.offsetHeight / rows);
res.text().then(function (pid) {
window.pid = pid;
socketURL += pid;
socket = new WebSocket(socketURL);
socket.onopen = runRealTerminal;
socket.onclose = runFakeTerminal;
socket.onerror = runFakeTerminal;
});
});
}
function runRealTerminal() {
term.attach(socket);
term._initialized = true;
}
function runFakeTerminal() {
if (term._initialized) {
return;
}
term._initialized = true;
var shellprompt = '$ ';
term.prompt = function () {
term.write('\r\n' + shellprompt);
};
term.writeln('Welcome to xterm.js');
term.writeln('This is a local terminal emulation, without a real terminal in the back-end.');
term.writeln('Type some keys and commands to play around.');
term.writeln('');
term.prompt();
term.on('key', function (key, ev) {
var printable = (
!ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey
);
if (ev.keyCode == 13) {
term.prompt();
} else if (ev.keyCode == 8) {
// Do not delete the prompt
if (term.x > 2) {
term.write('\b \b');
}
} else if (printable) {
term.write(key);
}
});
term.on('paste', function (data, ev) {
term.write(data);
});
}
+2 -6
View File
@@ -9,8 +9,8 @@ h1 {
}
#terminal-container {
width: 960px;
height: 600px;
width: 800px;
height: 450px;
margin: 0 auto;
padding: 2px;
}
@@ -20,7 +20,3 @@ h1 {
color: #fafafa;
padding: 2px;
}
#terminal-container .terminal .terminal-cursor {
background-color: #fafafa;
}
+126
View File
@@ -0,0 +1,126 @@
/**
* Implements the attach method, that attaches the terminal to a WebSocket stream.
* @module xterm/addons/attach/attach
* @license MIT
*/
(function (attach) {
if (typeof exports === 'object' && typeof module === 'object') {
/*
* CommonJS environment
*/
module.exports = attach(require('../../xterm'));
} else if (typeof define == 'function') {
/*
* Require.js is available
*/
define(['../../xterm'], attach);
} else {
/*
* Plain browser environment
*/
attach(window.Terminal);
}
})(function (Xterm) {
'use strict';
var exports = {};
/**
* Attaches the given terminal to the given socket.
*
* @param {Xterm} term - The terminal to be attached to the given socket.
* @param {WebSocket} socket - The socket to attach the current terminal.
* @param {boolean} bidirectional - Whether the terminal should send data
* to the socket as well.
* @param {boolean} buffered - Whether the rendering of incoming data
* should happen instantly or at a maximum
* frequency of 1 rendering per 10ms.
*/
exports.attach = function (term, socket, bidirectional, buffered) {
bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional;
term.socket = socket;
term._flushBuffer = function () {
term.write(term._attachSocketBuffer);
term._attachSocketBuffer = null;
clearTimeout(term._attachSocketBufferTimer);
term._attachSocketBufferTimer = null;
};
term._pushToBuffer = function (data) {
if (term._attachSocketBuffer) {
term._attachSocketBuffer += data;
} else {
term._attachSocketBuffer = data;
setTimeout(term._flushBuffer, 10);
}
};
term._getMessage = function (ev) {
if (buffered) {
term._pushToBuffer(ev.data);
} else {
term.write(ev.data);
}
};
term._sendData = function (data) {
socket.send(data);
};
socket.addEventListener('message', term._getMessage);
if (bidirectional) {
term.on('data', term._sendData);
}
socket.addEventListener('close', term.detach.bind(term, socket));
socket.addEventListener('error', term.detach.bind(term, socket));
};
/**
* Detaches the given terminal from the given socket
*
* @param {Xterm} term - The terminal to be detached from the given socket.
* @param {WebSocket} socket - The socket from which to detach the current
* terminal.
*/
exports.detach = function (term, socket) {
term.off('data', term._sendData);
socket = (typeof socket == 'undefined') ? term.socket : socket;
if (socket) {
socket.removeEventListener('message', term._getMessage);
}
delete term.socket;
};
/**
* Attaches the current terminal to the given socket
*
* @param {WebSocket} socket - The socket to attach the current terminal.
* @param {boolean} bidirectional - Whether the terminal should send data
* to the socket as well.
* @param {boolean} buffered - Whether the rendering of incoming data
* should happen instantly or at a maximum
* frequency of 1 rendering per 10ms.
*/
Xterm.prototype.attach = function (socket, bidirectional, buffered) {
return exports.attach(this, socket, bidirectional, buffered);
};
/**
* Detaches the current terminal from the given socket.
*
* @param {WebSocket} socket - The socket from which to detach the current
* terminal.
*/
Xterm.prototype.detach = function (socket) {
return exports.detach(this, socket);
};
return exports;
});
+86
View File
@@ -0,0 +1,86 @@
/**
* Fit terminal columns and rows to the dimensions of its DOM element.
*
* ## Approach
* - Rows: Truncate the division of the terminal parent element height by the terminal row height.
*
* - Columns: Truncate the division of the terminal parent element width by the terminal character
* width (apply display: inline at the terminal row and truncate its width with the current
* number of columns).
* @module xterm/addons/fit/fit
* @license MIT
*/
(function (fit) {
if (typeof exports === 'object' && typeof module === 'object') {
/*
* CommonJS environment
*/
module.exports = fit(require('../../xterm'));
} else if (typeof define == 'function') {
/*
* Require.js is available
*/
define(['../../xterm'], fit);
} else {
/*
* Plain browser environment
*/
fit(window.Terminal);
}
})(function (Xterm) {
var exports = {};
exports.proposeGeometry = function (term) {
if (!term.element.parentElement) {
return null;
}
var parentElementStyle = window.getComputedStyle(term.element.parentElement),
parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')),
parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width')) - 17),
elementStyle = window.getComputedStyle(term.element),
elementPaddingVer = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom')),
elementPaddingHor = parseInt(elementStyle.getPropertyValue('padding-right')) + parseInt(elementStyle.getPropertyValue('padding-left')),
availableHeight = parentElementHeight - elementPaddingVer,
availableWidth = parentElementWidth - elementPaddingHor,
container = term.rowContainer,
subjectRow = term.rowContainer.firstElementChild,
contentBuffer = subjectRow.innerHTML,
characterHeight,
rows,
characterWidth,
cols,
geometry;
subjectRow.style.display = 'inline';
subjectRow.innerHTML = 'W'; // Common character for measuring width, although on monospace
characterWidth = subjectRow.getBoundingClientRect().width;
subjectRow.style.display = ''; // Revert style before calculating height, since they differ.
characterHeight = subjectRow.getBoundingClientRect().height;
subjectRow.innerHTML = contentBuffer;
rows = parseInt(availableHeight / characterHeight);
cols = parseInt(availableWidth / characterWidth);
geometry = {cols: cols, rows: rows};
return geometry;
};
exports.fit = function (term) {
var geometry = exports.proposeGeometry(term);
if (geometry) {
term.resize(geometry.cols, geometry.rows);
}
};
Xterm.prototype.proposeGeometry = function () {
return exports.proposeGeometry(this);
};
Xterm.prototype.fit = function () {
return exports.fit(this);
};
return exports;
});
@@ -7,4 +7,4 @@
width: auto;
height: auto;
z-index: 255;
}
}
+50
View File
@@ -0,0 +1,50 @@
/**
* Fullscreen addon for xterm.js
* @module xterm/addons/fullscreen/fullscreen
* @license MIT
*/
(function (fullscreen) {
if (typeof exports === 'object' && typeof module === 'object') {
/*
* CommonJS environment
*/
module.exports = fullscreen(require('../../xterm'));
} else if (typeof define == 'function') {
/*
* Require.js is available
*/
define(['../../xterm'], fullscreen);
} else {
/*
* Plain browser environment
*/
fullscreen(window.Terminal);
}
})(function (Xterm) {
var exports = {};
/**
* Toggle the given terminal's fullscreen mode.
* @param {Xterm} term - The terminal to toggle full screen mode
* @param {boolean} fullscreen - Toggle fullscreen on (true) or off (false)
*/
exports.toggleFullScreen = function (term, fullscreen) {
var fn;
if (typeof fullscreen == 'undefined') {
fn = (term.element.classList.contains('fullscreen')) ? 'remove' : 'add';
} else if (!fullscreen) {
fn = 'remove';
} else {
fn = 'add';
}
term.element.classList[fn]('fullscreen');
};
Xterm.prototype.toggleFullscreen = function (fullscreen) {
exports.toggleFullScreen(this, fullscreen);
};
return exports;
});
+207
View File
@@ -0,0 +1,207 @@
/**
* Methods for turning URL subscrings in the terminal's content into links (`a` DOM elements).
* @module xterm/addons/linkify/linkify
* @license MIT
*/
(function (linkify) {
if (typeof exports === 'object' && typeof module === 'object') {
/*
* CommonJS environment
*/
module.exports = linkify(require('../../xterm'));
} else if (typeof define == 'function') {
/*
* Require.js is available
*/
define(['../../xterm'], linkify);
} else {
/*
* Plain browser environment
*/
linkify(window.Terminal);
}
})(function (Xterm) {
'use strict';
var exports = {},
protocolClause = '(https?:\\/\\/)',
domainCharacterSet = '[\\da-z\\.-]+',
negatedDomainCharacterSet = '[^\\da-z\\.-]+',
domainBodyClause = '(' + domainCharacterSet + ')',
tldClause = '([a-z\\.]{2,6})',
ipClause = '((\\d{1,3}\\.){3}\\d{1,3})',
portClause = '(:\\d{1,5})',
hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + ')' + portClause + '?',
pathClause = '(\\/[\\/\\w\\.-]*)*',
negatedPathCharacterSet = '[^\\/\\w\\.-]+',
bodyClause = hostClause + pathClause,
start = '(?:^|' + negatedDomainCharacterSet + ')(',
end = ')($|' + negatedPathCharacterSet + ')',
lenientUrlClause = start + protocolClause + '?' + bodyClause + end,
strictUrlClause = start + protocolClause + bodyClause + end,
lenientUrlRegex = new RegExp(lenientUrlClause),
strictUrlRegex = new RegExp(strictUrlClause);
/**
* Converts all valid URLs found in the given terminal line into
* hyperlinks. The terminal line can be either the HTML element itself
* or the index of the termina line in the children of the terminal
* rows container.
*
* @param {Xterm} terminal - The terminal that owns the given line.
* @param {number|HTMLDivElement} line - The terminal line that should get
* "linkified".
* @param {boolean} lenient - The regex type that will be used to identify links. If lenient is
* false, the regex requires a protocol clause. Defaults to true.
* @param {string} target - Sets target="" attribute with value provided to links.
* Default doesn't set target attribute
* @emits linkify
* @emits linkify:line
*/
exports.linkifyTerminalLine = function (terminal, line, lenient, target) {
if (typeof line == 'number') {
line = terminal.rowContainer.children[line];
} else if (! (line instanceof HTMLDivElement)) {
var message = 'The "line" argument should be either a number';
message += ' or an HTMLDivElement';
throw new TypeError(message);
}
if (typeof target === 'undefined') {
target = '';
} else {
target = 'target="' + target + '"';
}
var buffer = document.createElement('span'),
nodes = line.childNodes;
for (var j=0; j<nodes.length; j++) {
var node = nodes[j],
match;
/**
* Since we cannot access the TextNode's HTML representation
* from the instance itself, we assign its data as textContent
* to a dummy buffer span, in order to retrieve the TextNode's
* HTML representation from the buffer's innerHTML.
*/
buffer.textContent = node.data;
var nodeHTML = buffer.innerHTML;
/**
* Apply function only on TextNodes
*/
if (node.nodeType != node.TEXT_NODE) {
continue;
}
var url = exports.findLinkMatch(node.data, lenient);
if (!url) {
continue;
}
var startsWithProtocol = new RegExp('^' + protocolClause),
urlHasProtocol = url.match(startsWithProtocol),
href = (urlHasProtocol) ? url : 'http://' + url,
link = '<a href="' + href + '" ' + target + '>' + url + '</a>',
newHTML = nodeHTML.replace(url, link);
line.innerHTML = line.innerHTML.replace(nodeHTML, newHTML);
}
/**
* This event gets emitted when conversion of all URL susbtrings
* to HTML anchor elements (links) has finished, for a specific
* line of the current Xterm instance.
*
* @event linkify:line
*/
terminal.emit('linkify:line', line);
};
/**
* Finds a link within a block of text.
*
* @param {string} text - The text to search .
* @param {boolean} lenient - Whether to use the lenient search.
* @return {string} A URL.
*/
exports.findLinkMatch = function (text, lenient) {
var match = text.match(lenient ? lenientUrlRegex : strictUrlRegex);
if (!match || match.length === 0) {
return null;
}
return match[1];
}
/**
* Converts all valid URLs found in the terminal view into hyperlinks.
*
* @param {Xterm} terminal - The terminal that should get "linkified".
* @param {boolean} lenient - The regex type that will be used to identify links. If lenient is
* false, the regex requires a protocol clause. Defaults to true.
* @param {string} target - Sets target="" attribute with value provided to links.
* Default doesn't set target attribute
* @emits linkify
* @emits linkify:line
*/
exports.linkify = function (terminal, lenient, target) {
var rows = terminal.rowContainer.children;
lenient = (typeof lenient == "boolean") ? lenient : true;
for (var i=0; i<rows.length; i++) {
var line = rows[i];
exports.linkifyTerminalLine(terminal, line, lenient, target);
}
/**
* This event gets emitted when conversion of all URL substrings to
* HTML anchor elements (links) has finished for the current Xterm
* instance's view.
*
* @event linkify
*/
terminal.emit('linkify');
};
/**
* Extend Xterm prototype.
*/
/**
* Converts all valid URLs found in the current terminal linte into
* hyperlinks.
*
* @memberof Xterm
* @param {number|HTMLDivElement} line - The terminal line that should get
* "linkified".
* @param {boolean} lenient - The regex type that will be used to identify links. If lenient is
* false, the regex requires a protocol clause. Defaults to true.
* @param {string} target - Sets target="" attribute with value provided to links.
* Default doesn't set target attribute
*/
Xterm.prototype.linkifyTerminalLine = function (line, lenient, target) {
return exports.linkifyTerminalLine(this, line, lenient, target);
};
/**
* Converts all valid URLs found in the current terminal into hyperlinks.
*
* @memberof Xterm
* @param {boolean} lenient - The regex type that will be used to identify links. If lenient is
* false, the regex requires a protocol clause. Defaults to true.
* @param {string} target - Sets target="" attribute with value provided to links.
* Default doesn't set target attribute
*/
Xterm.prototype.linkify = function (lenient, target) {
return exports.linkify(this, lenient, target);
};
return exports;
});
+135
View File
@@ -0,0 +1,135 @@
/**
* This module provides methods for attaching a terminal to a terminado WebSocket stream.
*
* @module xterm/addons/terminado/terminado
* @license MIT
*/
(function (attach) {
if (typeof exports === 'object' && typeof module === 'object') {
/*
* CommonJS environment
*/
module.exports = attach(require('../../xterm'));
} else if (typeof define == 'function') {
/*
* Require.js is available
*/
define(['../../xterm'], attach);
} else {
/*
* Plain browser environment
*/
attach(window.Terminal);
}
})(function (Xterm) {
'use strict';
var exports = {};
/**
* Attaches the given terminal to the given socket.
*
* @param {Xterm} term - The terminal to be attached to the given socket.
* @param {WebSocket} socket - The socket to attach the current terminal.
* @param {boolean} bidirectional - Whether the terminal should send data
* to the socket as well.
* @param {boolean} buffered - Whether the rendering of incoming data
* should happen instantly or at a maximum
* frequency of 1 rendering per 10ms.
*/
exports.terminadoAttach = function (term, socket, bidirectional, buffered) {
bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional;
term.socket = socket;
term._flushBuffer = function () {
term.write(term._attachSocketBuffer);
term._attachSocketBuffer = null;
clearTimeout(term._attachSocketBufferTimer);
term._attachSocketBufferTimer = null;
};
term._pushToBuffer = function (data) {
if (term._attachSocketBuffer) {
term._attachSocketBuffer += data;
} else {
term._attachSocketBuffer = data;
setTimeout(term._flushBuffer, 10);
}
};
term._getMessage = function (ev) {
var data = JSON.parse(ev.data)
if( data[0] == "stdout" ) {
if (buffered) {
term._pushToBuffer(data[1]);
} else {
term.write(data[1]);
}
}
};
term._sendData = function (data) {
socket.send(JSON.stringify(['stdin', data]));
};
term._setSize = function (size) {
socket.send(JSON.stringify(['set_size', size.rows, size.cols]));
};
socket.addEventListener('message', term._getMessage);
if (bidirectional) {
term.on('data', term._sendData);
}
term.on('resize', term._setSize);
socket.addEventListener('close', term.terminadoDetach.bind(term, socket));
socket.addEventListener('error', term.terminadoDetach.bind(term, socket));
};
/**
* Detaches the given terminal from the given socket
*
* @param {Xterm} term - The terminal to be detached from the given socket.
* @param {WebSocket} socket - The socket from which to detach the current
* terminal.
*/
exports.terminadoDetach = function (term, socket) {
term.off('data', term._sendData);
socket = (typeof socket == 'undefined') ? term.socket : socket;
if (socket) {
socket.removeEventListener('message', term._getMessage);
}
delete term.socket;
};
/**
* Attaches the current terminal to the given socket
*
* @param {WebSocket} socket - The socket to attach the current terminal.
* @param {boolean} bidirectional - Whether the terminal should send data
* to the socket as well.
* @param {boolean} buffered - Whether the rendering of incoming data
* should happen instantly or at a maximum
* frequency of 1 rendering per 10ms.
*/
Xterm.prototype.terminadoAttach = function (socket, bidirectional, buffered) {
return exports.terminadoAttach(this, socket, bidirectional, buffered);
};
/**
* Detaches the current terminal from the given socket.
*
* @param {WebSocket} socket - The socket from which to detach the current
* terminal.
*/
Xterm.prototype.terminadoDetach = function (socket) {
return exports.terminadoDetach(this, socket);
};
return exports;
});
+2270
View File
File diff suppressed because it is too large Load Diff
+4955
View File
File diff suppressed because it is too large Load Diff
+1
View File
File diff suppressed because one or more lines are too long
+9
View File
@@ -0,0 +1,9 @@
version: '2'
services:
web:
build: ./
volumes:
- ./:/usr/src/app
ports:
- 3000:3000
-177
View File
@@ -1,177 +0,0 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/xtermjs.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/xtermjs.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/xtermjs"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/xtermjs"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
-258
View File
@@ -1,258 +0,0 @@
# -*- coding: utf-8 -*-
#
# xterm.js documentation build configuration file, created by
# sphinx-quickstart on Tue Mar 25 17:38:05 2014.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'xterm.js'
copyright = u'2014, sourceLair Limited'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.1a'
# The full version, including alpha/beta/rc tags.
release = '0.1a'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'xtermjsdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'xtermjs.tex', u'xterm.js Documentation',
u'sourceLair Limited', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'xtermjs', u'xterm.js Documentation',
[u'sourceLair Limited'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'xtermjs', u'xterm.js Documentation',
u'sourceLair Limited', 'xtermjs', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
-22
View File
@@ -1,22 +0,0 @@
.. xterm.js documentation master file, created by
sphinx-quickstart on Tue Mar 25 17:38:05 2014.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to xterm.js's documentation!
====================================
Contents:
.. toctree::
:maxdepth: 2
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
+111
View File
@@ -0,0 +1,111 @@
const browserify = require('browserify');
const buffer = require('vinyl-buffer');
const coveralls = require('gulp-coveralls');
const fs = require('fs-extra');
const gulp = require('gulp');
const istanbul = require('gulp-istanbul');
const merge = require('merge-stream');
const mocha = require('gulp-mocha');
const sorcery = require('sorcery');
const source = require('vinyl-source-stream');
const sourcemaps = require('gulp-sourcemaps');
const ts = require('gulp-typescript');
let buildDir = process.env.BUILD_DIR || 'build';
let tsProject = ts.createProject('tsconfig.json');
let srcDir = tsProject.config.compilerOptions.rootDir;
let outDir = tsProject.config.compilerOptions.outDir;
/**
* Compile TypeScript sources to JavaScript files and create a source map file for each TypeScript
* file compiled.
*/
gulp.task('tsc', function () {
// Remove the ${outDir}/ directory to prevent confusion if files were deleted in ${srcDir}/
fs.emptyDirSync(`${outDir}`);
// Build all TypeScript files (including tests) to ${outDir}/, based on the configuration defined in
// `tsconfig.json`.
let tsResult = tsProject.src().pipe(sourcemaps.init()).pipe(tsProject());
let tsc = tsResult.js.pipe(sourcemaps.write('.', {includeContent: false, sourceRoot: ''})).pipe(gulp.dest(outDir));
// Copy all addons from ${srcDir}/ to ${outDir}/
let copyAddons = gulp.src(`${srcDir}/addons/**/*`).pipe(gulp.dest(`${outDir}/addons`));
// Copy stylesheets from ${srcDir}/ to ${outDir}/
let copyStylesheets = gulp.src(`${srcDir}/**/*.css`).pipe(gulp.dest(outDir));
return merge(tsc, copyAddons, copyStylesheets);
});
/**
* Bundle JavaScript files produced by the `tsc` task, into a single file named `xterm.js` with
* Browserify.
*/
gulp.task('browserify', ['tsc'], function() {
// Ensure that the build directory exists
fs.ensureDirSync(buildDir);
let browserifyOptions = {
basedir: buildDir,
debug: true,
entries: [`../${outDir}/xterm.js`],
standalone: 'Terminal',
cache: {},
packageCache: {}
};
let bundleStream = browserify(browserifyOptions)
.bundle()
.pipe(source('xterm.js'))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true, sourceRoot: '..'}))
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest(buildDir));
// Copy all add-ons from ${outDir}/ to buildDir
let copyAddons = gulp.src(`${outDir}/addons/**/*`).pipe(gulp.dest(`${buildDir}/addons`));
// Copy stylesheets from ${outDir}/ to ${buildDir}/
let copyStylesheets = gulp.src(`${outDir}/**/*.css`).pipe(gulp.dest(buildDir));
return merge(bundleStream, copyAddons, copyStylesheets);
});
gulp.task('instrument-test', function () {
return gulp.src([`${outDir}/**/*.js`])
// Covering files
.pipe(istanbul())
// Force `require` to return covered files
.pipe(istanbul.hookRequire());
});
gulp.task('mocha', ['instrument-test'], function () {
return gulp.src([`${outDir}/*test.js`, `${outDir}/**/*test.js`], {read: false})
.pipe(mocha())
.once('error', () => process.exit(1))
.pipe(istanbul.writeReports());
});
/**
* Use `sorcery` to resolve the source map chain and point back to the TypeScript files.
* (Without this task the source maps produced for the JavaScript bundle points into the
* compiled JavaScript files in ${outDir}/).
*/
gulp.task('sorcery', ['browserify'], function () {
var chain = sorcery.loadSync(`${buildDir}/xterm.js`);
chain.apply();
chain.writeSync();
});
/**
* Submit coverage results to coveralls.io
*/
gulp.task('coveralls', function () {
gulp.src('coverage/**/lcov.info')
.pipe(coveralls());
});
gulp.task('build', ['sorcery']);
gulp.task('test', ['mocha']);
gulp.task('default', ['build']);
+26
View File
@@ -0,0 +1,26 @@
{
"source": {
"include": [
"src/"
],
"exclude": [
"src/test/"
],
"excludePattern": "src/.+\\.test\\.(js|ts)$"
},
"opts": {
"readme": "README.md",
"template": "node_modules/docdash",
"encoding": "utf8",
"destination": "docs/",
"recurse": true,
"verbose": true
},
"plugins": [
"plugins/markdown"
],
"templates": {
"cleverLinks": false,
"monospaceLinks": false
}
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

+79
View File
@@ -0,0 +1,79 @@
{
"name": "xterm",
"description": "Full xterm terminal, in your browser",
"version": "2.8.0",
"ignore": [
"demo",
"test",
".gitignore"
],
"main": "lib/xterm.js",
"repository": "https://github.com/sourcelair/xterm.js",
"license": "MIT",
"files": [
"*.js",
"*.json",
"dist/*.css",
"dist/**/*.css",
"dist/*.js",
"dist/*.js.map",
"dist/**/*.js",
"dist/**/*.js.map",
"lib/*.css",
"lib/**/*.css",
"lib/*.js",
"lib/*.js.map",
"lib/**/*.js",
"lib/**/*.js.map",
"src/*.css",
"src/**/*.css",
"src/*.js",
"src/*.js.map",
"src/*.ts",
"src/**/*.js",
"src/**/*.js.map",
"src/**/*.ts"
],
"devDependencies": {
"@types/chai": "^3.4.34",
"@types/jsdom": "^2.0.30",
"@types/mocha": "^2.2.33",
"@types/node": "^6.0.41",
"browserify": "^13.1.0",
"chai": "3.5.0",
"docdash": "0.4.0",
"express": "4.13.4",
"express-ws": "2.0.0-rc.1",
"fs-extra": "^1.0.0",
"glob": "^7.0.5",
"gulp": "^3.9.1",
"gulp-cli": "^1.2.2",
"gulp-coveralls": "^0.1.4",
"gulp-istanbul": "^1.1.1",
"gulp-mocha": "^3.0.1",
"gulp-sourcemaps": "1.9.1",
"gulp-typescript": "^3.1.3",
"jsdoc": "3.4.3",
"jsdom": "^9.11.0",
"merge-stream": "^1.0.1",
"node-pty": "^0.4.1",
"nodemon": "1.10.2",
"sleep": "^3.0.1",
"sorcery": "^0.10.0",
"tslint": "^4.0.2",
"typescript": "~2.2.0",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0"
},
"scripts": {
"prestart": "npm run build",
"start": "node demo/app",
"dev": "nodemon -e js,ts,css --watch src --watch demo --exec npm start",
"lint": "tslint src/*.ts src/**/*.ts",
"test": "gulp test",
"build:docs": "jsdoc -c jsdoc.json",
"build": "gulp build",
"prepublish": "npm run build",
"coveralls": "gulp coveralls"
}
}
+252
View File
@@ -0,0 +1,252 @@
/**
* @license MIT
*/
/**
* The character sets supported by the terminal. These enable several languages
* to be represented within the terminal with only 8-bit encoding. See ISO 2022
* for a discussion on character sets. Only VT100 character sets are supported.
*/
export const CHARSETS: {[key: string]: {[key: string]: string}} = {};
/**
* The default character set, US.
*/
export const DEFAULT_CHARSET = CHARSETS['B'];
/**
* DEC Special Character and Line Drawing Set.
* Reference: http://vt100.net/docs/vt102-ug/table5-13.html
* A lot of curses apps use this if they see TERM=xterm.
* testing: echo -e '\e(0a\e(B'
* The xterm output sometimes seems to conflict with the
* reference above. xterm seems in line with the reference
* when running vttest however.
* The table below now uses xterm's output from vttest.
*/
CHARSETS['0'] = {
'`': '\u25c6', // '◆'
'a': '\u2592', // '▒'
'b': '\u0009', // '\t'
'c': '\u000c', // '\f'
'd': '\u000d', // '\r'
'e': '\u000a', // '\n'
'f': '\u00b0', // '°'
'g': '\u00b1', // '±'
'h': '\u2424', // '\u2424' (NL)
'i': '\u000b', // '\v'
'j': '\u2518', // '┘'
'k': '\u2510', // '┐'
'l': '\u250c', // '┌'
'm': '\u2514', // '└'
'n': '\u253c', // '┼'
'o': '\u23ba', // '⎺'
'p': '\u23bb', // '⎻'
'q': '\u2500', // '─'
'r': '\u23bc', // '⎼'
's': '\u23bd', // '⎽'
't': '\u251c', // '├'
'u': '\u2524', // '┤'
'v': '\u2534', // '┴'
'w': '\u252c', // '┬'
'x': '\u2502', // '│'
'y': '\u2264', // '≤'
'z': '\u2265', // '≥'
'{': '\u03c0', // 'π'
'|': '\u2260', // '≠'
'}': '\u00a3', // '£'
'~': '\u00b7' // '·'
};
/**
* British character set
* ESC (A
* Reference: http://vt100.net/docs/vt220-rm/table2-5.html
*/
CHARSETS['A'] = {
'#': '£'
};
/**
* United States character set
* ESC (B
*/
CHARSETS['B'] = null;
/**
* Dutch character set
* ESC (4
* Reference: http://vt100.net/docs/vt220-rm/table2-6.html
*/
CHARSETS['4'] = {
'#': '£',
'@': '¾',
'[': 'ij',
'\\': '½',
']': '|',
'{': '¨',
'|': 'f',
'}': '¼',
'~': '´'
};
/**
* Finnish character set
* ESC (C or ESC (5
* Reference: http://vt100.net/docs/vt220-rm/table2-7.html
*/
CHARSETS['C'] =
CHARSETS['5'] = {
'[': 'Ä',
'\\': 'Ö',
']': 'Å',
'^': 'Ü',
'`': 'é',
'{': 'ä',
'|': 'ö',
'}': 'å',
'~': 'ü'
};
/**
* French character set
* ESC (R
* Reference: http://vt100.net/docs/vt220-rm/table2-8.html
*/
CHARSETS['R'] = {
'#': '£',
'@': 'à',
'[': '°',
'\\': 'ç',
']': '§',
'{': 'é',
'|': 'ù',
'}': 'è',
'~': '¨'
};
/**
* French Canadian character set
* ESC (Q
* Reference: http://vt100.net/docs/vt220-rm/table2-9.html
*/
CHARSETS['Q'] = {
'@': 'à',
'[': 'â',
'\\': 'ç',
']': 'ê',
'^': 'î',
'`': 'ô',
'{': 'é',
'|': 'ù',
'}': 'è',
'~': 'û'
};
/**
* German character set
* ESC (K
* Reference: http://vt100.net/docs/vt220-rm/table2-10.html
*/
CHARSETS['K'] = {
'@': '§',
'[': 'Ä',
'\\': 'Ö',
']': 'Ü',
'{': 'ä',
'|': 'ö',
'}': 'ü',
'~': 'ß'
};
/**
* Italian character set
* ESC (Y
* Reference: http://vt100.net/docs/vt220-rm/table2-11.html
*/
CHARSETS['Y'] = {
'#': '£',
'@': '§',
'[': '°',
'\\': 'ç',
']': 'é',
'`': 'ù',
'{': 'à',
'|': 'ò',
'}': 'è',
'~': 'ì'
};
/**
* Norwegian/Danish character set
* ESC (E or ESC (6
* Reference: http://vt100.net/docs/vt220-rm/table2-12.html
*/
CHARSETS['E'] =
CHARSETS['6'] = {
'@': 'Ä',
'[': 'Æ',
'\\': 'Ø',
']': 'Å',
'^': 'Ü',
'`': 'ä',
'{': 'æ',
'|': 'ø',
'}': 'å',
'~': 'ü'
};
/**
* Spanish character set
* ESC (Z
* Reference: http://vt100.net/docs/vt220-rm/table2-13.html
*/
CHARSETS['Z'] = {
'#': '£',
'@': '§',
'[': '¡',
'\\': 'Ñ',
']': '¿',
'{': '°',
'|': 'ñ',
'}': 'ç'
};
/**
* Swedish character set
* ESC (H or ESC (7
* Reference: http://vt100.net/docs/vt220-rm/table2-14.html
*/
CHARSETS['H'] =
CHARSETS['7'] = {
'@': 'É',
'[': 'Ä',
'\\': 'Ö',
']': 'Å',
'^': 'Ü',
'`': 'é',
'{': 'ä',
'|': 'ö',
'}': 'å',
'~': 'ü'
};
/**
* Swiss character set
* ESC (=
* Reference: http://vt100.net/docs/vt220-rm/table2-15.html
*/
CHARSETS['='] = {
'#': 'ù',
'@': 'à',
'[': 'é',
'\\': 'ç',
']': 'ê',
'^': 'î',
'_': 'è',
'`': 'ô',
'{': 'ä',
'|': 'ö',
'}': 'ü',
'~': 'û'
};
+236
View File
@@ -0,0 +1,236 @@
import { assert } from 'chai';
import { CompositionHelper } from './CompositionHelper';
describe('CompositionHelper', () => {
let terminal;
let compositionHelper;
let compositionView;
let textarea;
let handledText;
beforeEach(() => {
compositionView = {
classList: {
add: () => {},
remove: () => {},
},
getBoundingClientRect: () => {
return { width: 0 };
},
style: {
left: 0,
top: 0
},
textContent: ''
};
textarea = {
value: '',
style: {
left: 0,
top: 0
}
};
terminal = {
element: {
querySelector: () => {
return { offsetLeft: 0, offsetTop: 0 };
}
},
handler: function (text) {
handledText += text;
}
};
handledText = '';
compositionHelper = new CompositionHelper(textarea, compositionView, terminal);
});
describe('Input', () => {
it('Should insert simple characters', function (done) {
// First character 'ㅇ'
compositionHelper.compositionstart();
compositionHelper.compositionupdate({ data: 'ㅇ' });
textarea.value = 'ㅇ';
setTimeout(() => { // wait for any textarea updates
compositionHelper.compositionend();
setTimeout(() => { // wait for any textarea updates
assert.equal(handledText, 'ㅇ');
// Second character 'ㅇ'
compositionHelper.compositionstart();
compositionHelper.compositionupdate({ data: 'ㅇ' });
textarea.value = 'ㅇㅇ';
setTimeout(() => { // wait for any textarea updates
compositionHelper.compositionend();
setTimeout(() => { // wait for any textarea updates
assert.equal(handledText, 'ㅇㅇ');
done();
}, 0);
}, 0);
}, 0);
}, 0);
});
it('Should insert complex characters', function (done) {
// First character '앙'
compositionHelper.compositionstart();
compositionHelper.compositionupdate({ data: 'ㅇ' });
textarea.value = 'ㅇ';
setTimeout(() => { // wait for any textarea updates
compositionHelper.compositionupdate({ data: '아' });
textarea.value = '아';
setTimeout(() => { // wait for any textarea updates
compositionHelper.compositionupdate({ data: '앙' });
textarea.value = '앙';
setTimeout(() => { // wait for any textarea updates
compositionHelper.compositionend();
setTimeout(() => { // wait for any textarea updates
assert.equal(handledText, '앙');
// Second character '앙'
compositionHelper.compositionstart();
compositionHelper.compositionupdate({ data: 'ㅇ' });
textarea.value = '앙ㅇ';
setTimeout(() => { // wait for any textarea updates
compositionHelper.compositionupdate({ data: '아' });
textarea.value = '앙아';
setTimeout(() => { // wait for any textarea updates
compositionHelper.compositionupdate({ data: '앙' });
textarea.value = '앙앙';
setTimeout(() => { // wait for any textarea updates
compositionHelper.compositionend();
setTimeout(() => { // wait for any textarea updates
assert.equal(handledText, '앙앙');
done();
}, 0);
}, 0);
}, 0);
}, 0);
}, 0);
}, 0);
}, 0);
}, 0);
});
it('Should insert complex characters that change with following character', function (done) {
// First character '아'
compositionHelper.compositionstart();
compositionHelper.compositionupdate({ data: 'ㅇ' });
textarea.value = 'ㅇ';
setTimeout(() => { // wait for any textarea updates
compositionHelper.compositionupdate({ data: '아' });
textarea.value = '아';
setTimeout(() => { // wait for any textarea updates
// Start second character '아' in first character
compositionHelper.compositionupdate({ data: '앙' });
textarea.value = '앙';
setTimeout(() => { // wait for any textarea updates
compositionHelper.compositionend();
compositionHelper.compositionstart();
compositionHelper.compositionupdate({ data: '아' });
textarea.value = '아아';
setTimeout(() => { // wait for any textarea updates
compositionHelper.compositionend();
setTimeout(() => { // wait for any textarea updates
assert.equal(handledText, '아아');
done();
}, 0);
}, 0);
}, 0);
}, 0);
}, 0);
});
it('Should insert multi-characters compositions', function (done) {
// First character 'だ'
compositionHelper.compositionstart();
compositionHelper.compositionupdate({ data: 'd' });
textarea.value = 'd';
setTimeout(() => { // wait for any textarea updates
compositionHelper.compositionupdate({ data: 'だ' });
textarea.value = 'だ';
setTimeout(() => { // wait for any textarea updates
// Second character 'あ'
compositionHelper.compositionupdate({ data: 'だあ' });
textarea.value = 'だあ';
setTimeout(() => { // wait for any textarea updates
compositionHelper.compositionend();
setTimeout(() => { // wait for any textarea updates
assert.equal(handledText, 'だあ');
done();
}, 0);
}, 0);
}, 0);
}, 0);
});
it('Should insert multi-character compositions that are converted to other characters with the same length', function (done) {
// First character 'だ'
compositionHelper.compositionstart();
compositionHelper.compositionupdate({ data: 'd' });
textarea.value = 'd';
setTimeout(() => { // wait for any textarea updates
compositionHelper.compositionupdate({ data: 'だ' });
textarea.value = 'だ';
setTimeout(() => { // wait for any textarea updates
// Second character 'ー'
compositionHelper.compositionupdate({ data: 'だー' });
textarea.value = 'だー';
setTimeout(() => { // wait for any textarea updates
// Convert to katakana 'ダー'
compositionHelper.compositionupdate({ data: 'ダー' });
textarea.value = 'ダー';
setTimeout(() => { // wait for any textarea updates
compositionHelper.compositionend();
setTimeout(() => { // wait for any textarea updates
assert.equal(handledText, 'ダー');
done();
}, 0);
}, 0);
}, 0);
}, 0);
}, 0);
});
it('Should insert multi-character compositions that are converted to other characters with different lengths', function (done) {
// First character 'い'
compositionHelper.compositionstart();
compositionHelper.compositionupdate({ data: 'い' });
textarea.value = 'い';
setTimeout(() => { // wait for any textarea updates
// Second character 'ま'
compositionHelper.compositionupdate({ data: 'いm' });
textarea.value = 'いm';
setTimeout(() => { // wait for any textarea updates
compositionHelper.compositionupdate({ data: 'いま' });
textarea.value = 'いま';
setTimeout(() => { // wait for any textarea updates
// Convert to kanji '今'
compositionHelper.compositionupdate({ data: '今' });
textarea.value = '今';
setTimeout(() => { // wait for any textarea updates
compositionHelper.compositionend();
setTimeout(() => { // wait for any textarea updates
assert.equal(handledText, '今');
done();
}, 0);
}, 0);
}, 0);
}, 0);
}, 0);
});
it('Should insert non-composition characters input immediately after composition characters', function (done) {
// First character 'ㅇ'
compositionHelper.compositionstart();
compositionHelper.compositionupdate({ data: 'ㅇ' });
textarea.value = 'ㅇ';
setTimeout(() => { // wait for any textarea updates
compositionHelper.compositionend();
// Second character '1' (a non-composition character)
textarea.value = 'ㅇ1';
setTimeout(() => { // wait for any textarea updates
assert.equal(handledText, 'ㅇ1');
done();
}, 0);
}, 0);
});
});
});
+229
View File
@@ -0,0 +1,229 @@
/**
* @license MIT
*/
import { ITerminal } from './Interfaces';
interface IPosition {
start: number;
end: number;
}
/**
* Encapsulates the logic for handling compositionstart, compositionupdate and compositionend
* events, displaying the in-progress composition to the UI and forwarding the final composition
* to the handler.
*/
export class CompositionHelper {
/**
* Whether input composition is currently happening, eg. via a mobile keyboard, speech input or
* IME. This variable determines whether the compositionText should be displayed on the UI.
*/
private isComposing: boolean;
/**
* The position within the input textarea's value of the current composition.
*/
private compositionPosition: IPosition;
/**
* Whether a composition is in the process of being sent, setting this to false will cancel any
* in-progress composition.
*/
private isSendingComposition: boolean;
/**
* Creates a new CompositionHelper.
* @param textarea The textarea that xterm uses for input.
* @param compositionView The element to display the in-progress composition in.
* @param terminal The Terminal to forward the finished composition to.
*/
constructor(
private textarea: HTMLTextAreaElement,
private compositionView: HTMLElement,
private terminal: ITerminal
) {
this.isComposing = false;
this.isSendingComposition = false;
this.compositionPosition = { start: null, end: null };
}
/**
* Handles the compositionstart event, activating the composition view.
*/
public compositionstart() {
this.isComposing = true;
this.compositionPosition.start = this.textarea.value.length;
this.compositionView.textContent = '';
this.compositionView.classList.add('active');
}
/**
* Handles the compositionupdate event, updating the composition view.
* @param {CompositionEvent} ev The event.
*/
public compositionupdate(ev: CompositionEvent) {
this.compositionView.textContent = ev.data;
this.updateCompositionElements();
setTimeout(() => {
this.compositionPosition.end = this.textarea.value.length;
}, 0);
}
/**
* Handles the compositionend event, hiding the composition view and sending the composition to
* the handler.
*/
public compositionend() {
this.finalizeComposition(true);
}
/**
* Handles the keydown event, routing any necessary events to the CompositionHelper functions.
* @param ev The keydown event.
* @return Whether the Terminal should continue processing the keydown event.
*/
public keydown(ev: KeyboardEvent) {
if (this.isComposing || this.isSendingComposition) {
if (ev.keyCode === 229) {
// Continue composing if the keyCode is the "composition character"
return false;
} else if (ev.keyCode === 16 || ev.keyCode === 17 || ev.keyCode === 18) {
// Continue composing if the keyCode is a modifier key
return false;
} else {
// Finish composition immediately. This is mainly here for the case where enter is
// pressed and the handler needs to be triggered before the command is executed.
this.finalizeComposition(false);
}
}
if (ev.keyCode === 229) {
// If the "composition character" is used but gets to this point it means a non-composition
// character (eg. numbers and punctuation) was pressed when the IME was active.
this.handleAnyTextareaChanges();
return false;
}
return true;
}
/**
* Finalizes the composition, resuming regular input actions. This is called when a composition
* is ending.
* @param waitForPropogation Whether to wait for events to propogate before sending
* the input. This should be false if a non-composition keystroke is entered before the
* compositionend event is triggered, such as enter, so that the composition is send before
* the command is executed.
*/
private finalizeComposition(waitForPropogation: boolean) {
this.compositionView.classList.remove('active');
this.isComposing = false;
this.clearTextareaPosition();
if (!waitForPropogation) {
// Cancel any delayed composition send requests and send the input immediately.
this.isSendingComposition = false;
const input = this.textarea.value.substring(this.compositionPosition.start, this.compositionPosition.end);
this.terminal.handler(input);
} else {
// Make a deep copy of the composition position here as a new compositionstart event may
// fire before the setTimeout executes.
const currentCompositionPosition = {
start: this.compositionPosition.start,
end: this.compositionPosition.end,
};
// Since composition* events happen before the changes take place in the textarea on most
// browsers, use a setTimeout with 0ms time to allow the native compositionend event to
// complete. This ensures the correct character is retrieved, this solution was used
// because:
// - The compositionend event's data property is unreliable, at least on Chromium
// - The last compositionupdate event's data property does not always accurately describe
// the character, a counter example being Korean where an ending consonsant can move to
// the following character if the following input is a vowel.
this.isSendingComposition = true;
setTimeout(() => {
// Ensure that the input has not already been sent
if (this.isSendingComposition) {
this.isSendingComposition = false;
let input;
if (this.isComposing) {
// Use the end position to get the string if a new composition has started.
input = this.textarea.value.substring(currentCompositionPosition.start, currentCompositionPosition.end);
} else {
// Don't use the end position here in order to pick up any characters after the
// composition has finished, for example when typing a non-composition character
// (eg. 2) after a composition character.
input = this.textarea.value.substring(currentCompositionPosition.start);
}
this.terminal.handler(input);
}
}, 0);
}
}
/**
* Apply any changes made to the textarea after the current event chain is allowed to complete.
* This should be called when not currently composing but a keydown event with the "composition
* character" (229) is triggered, in order to allow non-composition text to be entered when an
* IME is active.
*/
private handleAnyTextareaChanges() {
const oldValue = this.textarea.value;
setTimeout(() => {
// Ignore if a composition has started since the timeout
if (!this.isComposing) {
const newValue = this.textarea.value;
const diff = newValue.replace(oldValue, '');
if (diff.length > 0) {
this.terminal.handler(diff);
}
}
}, 0);
}
/**
* Positions the composition view on top of the cursor and the textarea just below it (so the
* IME helper dialog is positioned correctly).
* @param dontRecurse Whether to use setTimeout to recursively trigger another update, this is
* necessary as the IME events across browsers are not consistently triggered.
*/
public updateCompositionElements(dontRecurse?: boolean) {
if (!this.isComposing) {
return;
}
const cursor = <HTMLElement>this.terminal.element.querySelector('.terminal-cursor');
if (cursor) {
// Take .xterm-rows offsetTop into account as well in case it's positioned absolutely within
// the .xterm element.
const xtermRows = <HTMLElement>this.terminal.element.querySelector('.xterm-rows');
const cursorTop = xtermRows.offsetTop + cursor.offsetTop;
this.compositionView.style.left = cursor.offsetLeft + 'px';
this.compositionView.style.top = cursorTop + 'px';
this.compositionView.style.height = cursor.offsetHeight + 'px';
this.compositionView.style.lineHeight = cursor.offsetHeight + 'px';
// Sync the textarea to the exact position of the composition view so the IME knows where the
// text is.
const compositionViewBounds = this.compositionView.getBoundingClientRect();
this.textarea.style.left = cursor.offsetLeft + 'px';
this.textarea.style.top = cursorTop + 'px';
this.textarea.style.width = compositionViewBounds.width + 'px';
this.textarea.style.height = compositionViewBounds.height + 'px';
this.textarea.style.lineHeight = compositionViewBounds.height + 'px';
}
if (!dontRecurse) {
setTimeout(() => this.updateCompositionElements(true), 0);
}
};
/**
* Clears the textarea's position so that the cursor does not blink on IE.
* @private
*/
private clearTextareaPosition() {
this.textarea.style.left = '';
this.textarea.style.top = '';
};
}
+78
View File
@@ -0,0 +1,78 @@
/**
* @license MIT
*/
/**
* C0 control codes
* See = https://en.wikipedia.org/wiki/C0_and_C1_control_codes
*/
export namespace C0 {
/** Null (Caret = ^@, C = \0) */
export const NUL = '\x00';
/** Start of Heading (Caret = ^A) */
export const SOH = '\x01';
/** Start of Text (Caret = ^B) */
export const STX = '\x02';
/** End of Text (Caret = ^C) */
export const ETX = '\x03';
/** End of Transmission (Caret = ^D) */
export const EOT = '\x04';
/** Enquiry (Caret = ^E) */
export const ENQ = '\x05';
/** Acknowledge (Caret = ^F) */
export const ACK = '\x06';
/** Bell (Caret = ^G, C = \a) */
export const BEL = '\x07';
/** Backspace (Caret = ^H, C = \b) */
export const BS = '\x08';
/** Character Tabulation, Horizontal Tabulation (Caret = ^I, C = \t) */
export const HT = '\x09';
/** Line Feed (Caret = ^J, C = \n) */
export const LF = '\x0a';
/** Line Tabulation, Vertical Tabulation (Caret = ^K, C = \v) */
export const VT = '\x0b';
/** Form Feed (Caret = ^L, C = \f) */
export const FF = '\x0c';
/** Carriage Return (Caret = ^M, C = \r) */
export const CR = '\x0d';
/** Shift Out (Caret = ^N) */
export const SO = '\x0e';
/** Shift In (Caret = ^O) */
export const SI = '\x0f';
/** Data Link Escape (Caret = ^P) */
export const DLE = '\x10';
/** Device Control One (XON) (Caret = ^Q) */
export const DC1 = '\x11';
/** Device Control Two (Caret = ^R) */
export const DC2 = '\x12';
/** Device Control Three (XOFF) (Caret = ^S) */
export const DC3 = '\x13';
/** Device Control Four (Caret = ^T) */
export const DC4 = '\x14';
/** Negative Acknowledge (Caret = ^U) */
export const NAK = '\x15';
/** Synchronous Idle (Caret = ^V) */
export const SYN = '\x16';
/** End of Transmission Block (Caret = ^W) */
export const ETB = '\x17';
/** Cancel (Caret = ^X) */
export const CAN = '\x18';
/** End of Medium (Caret = ^Y) */
export const EM = '\x19';
/** Substitute (Caret = ^Z) */
export const SUB = '\x1a';
/** Escape (Caret = ^[, C = \e) */
export const ESC = '\x1b';
/** File Separator (Caret = ^\) */
export const FS = '\x1c';
/** Group Separator (Caret = ^]) */
export const GS = '\x1d';
/** Record Separator (Caret = ^^) */
export const RS = '\x1e';
/** Unit Separator (Caret = ^_) */
export const US = '\x1f';
/** Space */
export const SP = '\x20';
/** Delete (Caret = ^?) */
export const DEL = '\x7f';
};
+84
View File
@@ -0,0 +1,84 @@
import { assert } from 'chai';
import { EventEmitter } from './EventEmitter';
describe('EventEmitter', () => {
let eventEmitter: EventEmitter;
beforeEach(() => {
eventEmitter = new EventEmitter();
});
describe('once', () => {
it('should trigger the listener only once', () => {
let count = 0;
const listener = () => count++;
eventEmitter.once('test', listener);
eventEmitter.emit('test');
assert.equal(count, 1);
eventEmitter.emit('test');
assert.equal(count, 1);
});
});
describe('emit', () => {
it('should emit events to listeners', () => {
let count1 = 0;
let count2 = 0;
const listener1 = () => count1++;
const listener2 = () => count2++;
eventEmitter.on('test', listener1);
eventEmitter.on('test', listener2);
eventEmitter.emit('test');
assert.equal(count1, 1);
assert.equal(count2, 1);
eventEmitter.emit('test');
assert.equal(count1, 2);
assert.equal(count2, 2);
});
it('should manage multiple listener types', () => {
let count1 = 0;
let count2 = 0;
const listener1 = () => count1++;
const listener2 = () => count2++;
eventEmitter.on('test', listener1);
eventEmitter.on('foo', listener2);
eventEmitter.emit('test');
assert.equal(count1, 1);
assert.equal(count2, 0);
eventEmitter.emit('foo');
assert.equal(count1, 1);
assert.equal(count2, 1);
});
});
describe('listeners', () => {
it('should return listeners for the type requested', () => {
assert.equal(eventEmitter.listeners('test').length, 0);
const listener = () => {};
eventEmitter.on('test', listener);
assert.deepEqual(eventEmitter.listeners('test'), [listener]);
});
});
describe('off', () => {
it('should remove the specific listener', () => {
const listener1 = () => {};
const listener2 = () => {};
eventEmitter.on('foo', listener1);
eventEmitter.on('foo', listener2);
assert.equal(eventEmitter.listeners('foo').length, 2);
eventEmitter.off('foo', listener1);
assert.deepEqual(eventEmitter.listeners('foo'), [listener2]);
});
});
describe('removeAllListeners', () => {
it('should clear all listeners', () => {
eventEmitter.on('foo', () => {});
assert.equal(eventEmitter.listeners('foo').length, 1);
eventEmitter.removeAllListeners('foo');
assert.equal(eventEmitter.listeners('foo').length, 0);
});
});
});
+69
View File
@@ -0,0 +1,69 @@
/**
* @license MIT
*/
interface ListenerType {
(): void;
listener?: () => void;
};
export class EventEmitter {
private _events: {[type: string]: ListenerType[]};
constructor() {
// Restore the previous events if available, this will happen if the
// constructor is called multiple times on the same object (terminal reset).
this._events = this._events || {};
}
public on(type, listener): void {
this._events[type] = this._events[type] || [];
this._events[type].push(listener);
}
public off(type, listener): void {
if (!this._events[type]) {
return;
}
let obj = this._events[type];
let i = obj.length;
while (i--) {
if (obj[i] === listener || obj[i].listener === listener) {
obj.splice(i, 1);
return;
}
}
}
public removeAllListeners(type): void {
if (this._events[type]) {
delete this._events[type];
}
}
public once(type, listener): any {
function on() {
let args = Array.prototype.slice.call(arguments);
this.off(type, on);
return listener.apply(this, args);
}
(<any>on).listener = listener;
return this.on(type, on);
}
public emit(type: string, ...args: any[]): void {
if (!this._events[type]) {
return;
}
let obj = this._events[type];
for (let i = 0; i < obj.length; i++) {
obj[i].apply(this, args);
}
}
public listeners(type): ListenerType[] {
return this._events[type] || [];
}
}
+49
View File
@@ -0,0 +1,49 @@
import { assert } from 'chai';
import { InputHandler } from './InputHandler';
describe('InputHandler', () => {
describe('setCursorStyle', () => {
it('should call Terminal.setOption with correct params', () => {
let options = {};
let terminal = {
setOption: (option, value) => options[option] = value
};
let inputHandler = new InputHandler(terminal);
inputHandler.setCursorStyle([0]);
assert.equal(options['cursorStyle'], 'block');
assert.equal(options['cursorBlink'], true);
options = {};
inputHandler.setCursorStyle([1]);
assert.equal(options['cursorStyle'], 'block');
assert.equal(options['cursorBlink'], true);
options = {};
inputHandler.setCursorStyle([2]);
assert.equal(options['cursorStyle'], 'block');
assert.equal(options['cursorBlink'], false);
options = {};
inputHandler.setCursorStyle([3]);
assert.equal(options['cursorStyle'], 'underline');
assert.equal(options['cursorBlink'], true);
options = {};
inputHandler.setCursorStyle([4]);
assert.equal(options['cursorStyle'], 'underline');
assert.equal(options['cursorBlink'], false);
options = {};
inputHandler.setCursorStyle([5]);
assert.equal(options['cursorStyle'], 'bar');
assert.equal(options['cursorBlink'], true);
options = {};
inputHandler.setCursorStyle([6]);
assert.equal(options['cursorStyle'], 'bar');
assert.equal(options['cursorBlink'], false);
});
});
});
+1589
View File
File diff suppressed because it is too large Load Diff
+152
View File
@@ -0,0 +1,152 @@
/**
* @license MIT
*/
import { LinkMatcherOptions } from './Interfaces';
import { LinkMatcherHandler, LinkMatcherValidationCallback } from './Types';
export interface IBrowser {
isNode: boolean;
userAgent: string;
platform: string;
isFirefox: boolean;
isMSIE: boolean;
isMac: boolean;
isIpad: boolean;
isIphone: boolean;
isMSWindows: boolean;
}
export interface ITerminal {
element: HTMLElement;
rowContainer: HTMLElement;
selectionContainer: HTMLElement;
charMeasure: ICharMeasure;
textarea: HTMLTextAreaElement;
ybase: number;
ydisp: number;
lines: ICircularList<string>;
rows: number;
cols: number;
browser: IBrowser;
writeBuffer: string[];
children: HTMLElement[];
cursorHidden: boolean;
cursorState: number;
x: number;
y: number;
defAttr: number;
/**
* Emit the 'data' event and populate the given data.
* @param data The data to populate in the event.
*/
handler(data: string);
on(event: string, callback: () => void);
scrollDisp(disp: number, suppressScrollEvent: boolean);
cancel(ev: Event, force?: boolean);
log(text: string): void;
emit(event: string, data: any);
}
export interface ISelectionManager {
selectionText: string;
}
export interface ICharMeasure {
width: number;
height: number;
measure(): void;
}
export interface ILinkifier {
linkifyRow(rowIndex: number): void;
attachHypertextLinkHandler(handler: LinkMatcherHandler): void;
registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options?: LinkMatcherOptions): number;
deregisterLinkMatcher(matcherId: number): boolean;
}
interface ICircularList<T> {
length: number;
maxLength: number;
forEach(callbackfn: (value: T, index: number, array: T[]) => void): void;
get(index: number): T;
set(index: number, value: T): void;
push(value: T): void;
pop(): T;
splice(start: number, deleteCount: number, ...items: T[]): void;
trimStart(count: number): void;
shiftElements(start: number, count: number, offset: number): void;
}
export interface LinkMatcherOptions {
/**
* The index of the link from the regex.match(text) call. This defaults to 0
* (for regular expressions without capture groups).
*/
matchIndex?: number;
/**
* A callback that validates an individual link, returning true if valid and
* false if invalid.
*/
validationCallback?: LinkMatcherValidationCallback;
/**
* The priority of the link matcher, this defines the order in which the link
* matcher is evaluated relative to others, from highest to lowest. The
* default value is 0.
*/
priority?: number;
}
/**
* Handles actions generated by the parser.
*/
export interface IInputHandler {
addChar(char: string, code: number): void;
/** C0 BEL */ bell(): void;
/** C0 LF */ lineFeed(): void;
/** C0 CR */ carriageReturn(): void;
/** C0 BS */ backspace(): void;
/** C0 HT */ tab(): void;
/** C0 SO */ shiftOut(): void;
/** C0 SI */ shiftIn(): void;
/** CSI @ */ insertChars(params?: number[]): void;
/** CSI A */ cursorUp(params?: number[]): void;
/** CSI B */ cursorDown(params?: number[]): void;
/** CSI C */ cursorForward(params?: number[]): void;
/** CSI D */ cursorBackward(params?: number[]): void;
/** CSI E */ cursorNextLine(params?: number[]): void;
/** CSI F */ cursorPrecedingLine(params?: number[]): void;
/** CSI G */ cursorCharAbsolute(params?: number[]): void;
/** CSI H */ cursorPosition(params?: number[]): void;
/** CSI I */ cursorForwardTab(params?: number[]): void;
/** CSI J */ eraseInDisplay(params?: number[]): void;
/** CSI K */ eraseInLine(params?: number[]): void;
/** CSI L */ insertLines(params?: number[]): void;
/** CSI M */ deleteLines(params?: number[]): void;
/** CSI P */ deleteChars(params?: number[]): void;
/** CSI S */ scrollUp(params?: number[]): void;
/** CSI T */ scrollDown(params?: number[]): void;
/** CSI X */ eraseChars(params?: number[]): void;
/** CSI Z */ cursorBackwardTab(params?: number[]): void;
/** CSI ` */ charPosAbsolute(params?: number[]): void;
/** CSI a */ HPositionRelative(params?: number[]): void;
/** CSI b */ repeatPrecedingCharacter(params?: number[]): void;
/** CSI c */ sendDeviceAttributes(params?: number[]): void;
/** CSI d */ linePosAbsolute(params?: number[]): void;
/** CSI e */ VPositionRelative(params?: number[]): void;
/** CSI f */ HVPosition(params?: number[]): void;
/** CSI g */ tabClear(params?: number[]): void;
/** CSI h */ setMode(params?: number[]): void;
/** CSI l */ resetMode(params?: number[]): void;
/** CSI m */ charAttributes(params?: number[]): void;
/** CSI n */ deviceStatus(params?: number[]): void;
/** CSI p */ softReset(params?: number[]): void;
/** CSI q */ setCursorStyle(params?: number[]): void;
/** CSI r */ setScrollRegion(params?: number[]): void;
/** CSI s */ saveCursor(params?: number[]): void;
/** CSI u */ restoreCursor(params?: number[]): void;
}
+193
View File
@@ -0,0 +1,193 @@
/**
* @license MIT
*/
import jsdom = require('jsdom');
import { assert } from 'chai';
import { ITerminal, ILinkifier } from './Interfaces';
import { Linkifier } from './Linkifier';
import { LinkMatcher } from './Types';
class TestLinkifier extends Linkifier {
constructor() {
Linkifier.TIME_BEFORE_LINKIFY = 0;
super();
}
public get linkMatchers(): LinkMatcher[] { return this._linkMatchers; }
}
describe('Linkifier', () => {
let window: Window;
let document: Document;
let container: HTMLElement;
let rows: HTMLElement[];
let linkifier: TestLinkifier;
beforeEach(done => {
jsdom.env('', (err, w) => {
window = w;
document = window.document;
linkifier = new TestLinkifier();
done();
});
});
function addRow(html: string) {
const element = document.createElement('div');
element.innerHTML = html;
container.appendChild(element);
rows.push(element);
}
describe('before attachToDom', () => {
it('should allow link matcher registration', done => {
assert.doesNotThrow(() => {
const linkMatcherId = linkifier.registerLinkMatcher(/foo/, () => {});
assert.isTrue(linkifier.deregisterLinkMatcher(linkMatcherId));
done();
});
});
});
describe('after attachToDom', () => {
beforeEach(() => {
rows = [];
linkifier.attachToDom(document, rows);
container = document.createElement('div');
document.body.appendChild(container);
});
function clickElement(element: Node) {
const event = document.createEvent('MouseEvent');
event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
element.dispatchEvent(event);
}
function assertLinkifiesEntireRow(uri: string, done: MochaDone) {
addRow(uri);
linkifier.linkifyRow(0);
setTimeout(() => {
assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
assert.equal((<HTMLElement>rows[0].firstChild).textContent, uri);
done();
}, 0);
}
describe('http links', () => {
function assertLinkifiesEntireRow(uri: string, done: MochaDone) {
addRow(uri);
linkifier.linkifyRow(0);
setTimeout(() => {
assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
assert.equal((<HTMLElement>rows[0].firstChild).textContent, uri);
done();
}, 0);
}
it('should allow ~ character in URI path', done => assertLinkifiesEntireRow('http://foo.com/a~b#c~d?e~f', done));
});
describe('link matcher', () => {
function assertLinkifiesRow(rowText: string, linkMatcherRegex: RegExp, expectedHtml: string, done: MochaDone) {
addRow(rowText);
linkifier.registerLinkMatcher(linkMatcherRegex, () => {});
linkifier.linkifyRow(0);
// Allow linkify to happen
setTimeout(() => {
assert.equal(rows[0].innerHTML, expectedHtml);
done();
}, 0);
}
it('should match a single link', done => {
assertLinkifiesRow('foo', /foo/, '<a>foo</a>', done);
});
it('should match a single link at the start of a text node', done => {
assertLinkifiesRow('foo bar', /foo/, '<a>foo</a> bar', done);
});
it('should match a single link in the middle of a text node', done => {
assertLinkifiesRow('foo bar baz', /bar/, 'foo <a>bar</a> baz', done);
});
it('should match a single link at the end of a text node', done => {
assertLinkifiesRow('foo bar', /bar/, 'foo <a>bar</a>', done);
});
it('should match a link after a link at the start of a text node', done => {
assertLinkifiesRow('foo bar', /foo|bar/, '<a>foo</a> <a>bar</a>', done);
});
it('should match a link after a link in the middle of a text node', done => {
assertLinkifiesRow('foo bar baz', /bar|baz/, 'foo <a>bar</a> <a>baz</a>', done);
});
it('should match a link immediately after a link at the end of a text node', done => {
assertLinkifiesRow('<span>foo bar</span>baz', /bar|baz/, '<span>foo <a>bar</a></span><a>baz</a>', done);
});
it('should not duplicate text after a unicode character (wrapped in a span)', done => {
// This is a regression test for an issue that came about when using
// an oh-my-zsh theme that added the large blue diamond unicode
// character (U+1F537) which caused the path to be duplicated. See #642.
assertLinkifiesRow('echo \'<span class="xterm-normal-char">🔷</span>foo\'', /foo/, 'echo \'<span class="xterm-normal-char">🔷</span><a>foo</a>\'', done);
});
});
describe('validationCallback', () => {
it('should enable link if true', done => {
addRow('test');
linkifier.registerLinkMatcher(/test/, () => done(), {
validationCallback: (url, element, cb) => {
cb(true);
assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
setTimeout(() => clickElement(rows[0].firstChild), 0);
}
});
linkifier.linkifyRow(0);
});
it('should disable link if false', done => {
addRow('test');
linkifier.registerLinkMatcher(/test/, () => assert.fail(), {
validationCallback: (url, element, cb) => {
cb(false);
assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
setTimeout(() => clickElement(rows[0].firstChild), 0);
}
});
linkifier.linkifyRow(0);
// Allow time for the click to be performed
setTimeout(() => done(), 10);
});
it('should trigger for multiple link matches on one row', done => {
addRow('test test');
let count = 0;
linkifier.registerLinkMatcher(/test/, () => assert.fail(), {
validationCallback: (url, element, cb) => {
count += 1;
if (count === 2) {
done();
}
cb(false);
}
});
linkifier.linkifyRow(0);
});
});
describe('priority', () => {
it('should order the list from highest priority to lowest #1', () => {
const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: 1 });
const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: -1 });
assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [aId, 0, bId]);
});
it('should order the list from highest priority to lowest #2', () => {
const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: -1 });
const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: 1 });
assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [bId, 0, aId]);
});
it('should order items of equal priority in the order they are added', () => {
const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: 0 });
const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: 0 });
assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [0, aId, bId]);
});
});
});
});
+363
View File
@@ -0,0 +1,363 @@
/**
* @license MIT
*/
import { LinkMatcherOptions } from './Interfaces';
import { LinkMatcher, LinkMatcherHandler, LinkMatcherValidationCallback } from './Types';
const INVALID_LINK_CLASS = 'xterm-invalid-link';
const protocolClause = '(https?:\\/\\/)';
const domainCharacterSet = '[\\da-z\\.-]+';
const negatedDomainCharacterSet = '[^\\da-z\\.-]+';
const domainBodyClause = '(' + domainCharacterSet + ')';
const tldClause = '([a-z\\.]{2,6})';
const ipClause = '((\\d{1,3}\\.){3}\\d{1,3})';
const localHostClause = '(localhost)';
const portClause = '(:\\d{1,5})';
const hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?';
const pathClause = '(\\/[\\/\\w\\.\\-%~]*)*';
const queryStringHashFragmentCharacterSet = '[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&\'*+,:;~\\=\\.\\-]*';
const queryStringClause = '(\\?' + queryStringHashFragmentCharacterSet + ')?';
const hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?';
const negatedPathCharacterSet = '[^\\/\\w\\.\\-%]+';
const bodyClause = hostClause + pathClause + queryStringClause + hashFragmentClause;
const start = '(?:^|' + negatedDomainCharacterSet + ')(';
const end = ')($|' + negatedPathCharacterSet + ')';
const strictUrlRegex = new RegExp(start + protocolClause + bodyClause + end);
/**
* The ID of the built in http(s) link matcher.
*/
const HYPERTEXT_LINK_MATCHER_ID = 0;
/**
* The Linkifier applies links to rows shortly after they have been refreshed.
*/
export class Linkifier {
/**
* The time to wait after a row is changed before it is linkified. This prevents
* the costly operation of searching every row multiple times, potentially a
* huge amount of times.
*/
protected static TIME_BEFORE_LINKIFY = 200;
protected _linkMatchers: LinkMatcher[];
private _document: Document;
private _rows: HTMLElement[];
private _rowTimeoutIds: number[];
private _nextLinkMatcherId = HYPERTEXT_LINK_MATCHER_ID;
constructor() {
this._rowTimeoutIds = [];
this._linkMatchers = [];
this.registerLinkMatcher(strictUrlRegex, null, { matchIndex: 1 });
}
/**
* Attaches the linkifier to the DOM, enabling linkification.
* @param document The document object.
* @param rows The array of rows to apply links to.
*/
public attachToDom(document: Document, rows: HTMLElement[]) {
this._document = document;
this._rows = rows;
}
/**
* Queues a row for linkification.
* @param {number} rowIndex The index of the row to linkify.
*/
public linkifyRow(rowIndex: number): void {
// Don't attempt linkify if not yet attached to DOM
if (!this._document) {
return;
}
const timeoutId = this._rowTimeoutIds[rowIndex];
if (timeoutId) {
clearTimeout(timeoutId);
}
this._rowTimeoutIds[rowIndex] = setTimeout(this._linkifyRow.bind(this, rowIndex), Linkifier.TIME_BEFORE_LINKIFY);
}
/**
* Attaches a handler for hypertext links, overriding default <a> behavior
* for standard http(s) links.
* @param {LinkHandler} handler The handler to use, this can be cleared with
* null.
*/
public setHypertextLinkHandler(handler: LinkMatcherHandler): void {
this._linkMatchers[HYPERTEXT_LINK_MATCHER_ID].handler = handler;
}
/**
* Attaches a validation callback for hypertext links.
* @param {LinkMatcherValidationCallback} callback The callback to use, this
* can be cleared with null.
*/
public setHypertextValidationCallback(callback: LinkMatcherValidationCallback): void {
this._linkMatchers[HYPERTEXT_LINK_MATCHER_ID].validationCallback = callback;
}
/**
* Registers a link matcher, allowing custom link patterns to be matched and
* handled.
* @param {RegExp} regex The regular expression to search for, specifically
* this searches the textContent of the rows. You will want to use \s to match
* a space ' ' character for example.
* @param {LinkHandler} handler The callback when the link is called.
* @param {LinkMatcherOptions} [options] Options for the link matcher.
* @return {number} The ID of the new matcher, this can be used to deregister.
*/
public registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options: LinkMatcherOptions = {}): number {
if (this._nextLinkMatcherId !== HYPERTEXT_LINK_MATCHER_ID && !handler) {
throw new Error('handler must be defined');
}
const matcher: LinkMatcher = {
id: this._nextLinkMatcherId++,
regex,
handler,
matchIndex: options.matchIndex,
validationCallback: options.validationCallback,
priority: options.priority || 0
};
this._addLinkMatcherToList(matcher);
return matcher.id;
}
/**
* Inserts a link matcher to the list in the correct position based on the
* priority of each link matcher. New link matchers of equal priority are
* considered after older link matchers.
* @param matcher The link matcher to be added.
*/
private _addLinkMatcherToList(matcher: LinkMatcher): void {
if (this._linkMatchers.length === 0) {
this._linkMatchers.push(matcher);
return;
}
for (let i = this._linkMatchers.length - 1; i >= 0; i--) {
if (matcher.priority <= this._linkMatchers[i].priority) {
this._linkMatchers.splice(i + 1, 0, matcher);
return;
}
}
this._linkMatchers.splice(0, 0, matcher);
}
/**
* Deregisters a link matcher if it has been registered.
* @param {number} matcherId The link matcher's ID (returned after register)
* @return {boolean} Whether a link matcher was found and deregistered.
*/
public deregisterLinkMatcher(matcherId: number): boolean {
// ID 0 is the hypertext link matcher which cannot be deregistered
for (let i = 1; i < this._linkMatchers.length; i++) {
if (this._linkMatchers[i].id === matcherId) {
this._linkMatchers.splice(i, 1);
return true;
}
}
return false;
}
/**
* Linkifies a row.
* @param {number} rowIndex The index of the row to linkify.
*/
private _linkifyRow(rowIndex: number): void {
const row = this._rows[rowIndex];
if (!row) {
return;
}
const text = row.textContent;
for (let i = 0; i < this._linkMatchers.length; i++) {
const matcher = this._linkMatchers[i];
const linkElements = this._doLinkifyRow(row, matcher);
if (linkElements.length > 0) {
// Fire validation callback
if (matcher.validationCallback) {
for (let j = 0; j < linkElements.length; j++) {
const element = linkElements[j];
matcher.validationCallback(element.textContent, element, isValid => {
if (!isValid) {
element.classList.add(INVALID_LINK_CLASS);
}
});
}
}
// Only allow a single LinkMatcher to trigger on any given row.
return;
}
}
}
/**
* Linkifies a row given a specific handler.
* @param {HTMLElement} row The row to linkify.
* @param {LinkMatcher} matcher The link matcher for this line.
* @return The link element(s) that were added.
*/
private _doLinkifyRow(row: HTMLElement, matcher: LinkMatcher): HTMLElement[] {
// Iterate over nodes as we want to consider text nodes
let result = [];
const isHttpLinkMatcher = matcher.id === HYPERTEXT_LINK_MATCHER_ID;
const nodes = row.childNodes;
// Find the first match
let match = row.textContent.match(matcher.regex);
if (!match || match.length === 0) {
return result;
}
let uri = match[typeof matcher.matchIndex !== 'number' ? 0 : matcher.matchIndex];
// Set the next searches start index
let rowStartIndex = match.index + uri.length;
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
const searchIndex = node.textContent.indexOf(uri);
if (searchIndex >= 0) {
const linkElement = this._createAnchorElement(uri, matcher.handler, isHttpLinkMatcher);
if (node.textContent.length === uri.length) {
// Matches entire string
if (node.nodeType === 3 /*Node.TEXT_NODE*/) {
this._replaceNode(node, linkElement);
} else {
const element = (<HTMLElement>node);
if (element.nodeName === 'A') {
// This row has already been linkified
return result;
}
element.innerHTML = '';
element.appendChild(linkElement);
}
} else if (node.childNodes.length > 1) {
// Matches part of string in an element with multiple child nodes
for (let j = 0; j < node.childNodes.length; j++) {
const childNode = node.childNodes[j];
const childSearchIndex = childNode.textContent.indexOf(uri);
if (childSearchIndex !== -1) {
// Match found in currentNode
this._replaceNodeSubstringWithNode(childNode, linkElement, uri, childSearchIndex);
// Don't need to count nodesAdded by replacing the node as this
// is a child node, not a top-level node.
break;
}
}
} else {
// Matches part of string in a single text node
const nodesAdded = this._replaceNodeSubstringWithNode(node, linkElement, uri, searchIndex);
// No need to consider the new nodes
i += nodesAdded;
}
result.push(linkElement);
// Find the next match
match = row.textContent.substring(rowStartIndex).match(matcher.regex);
if (!match || match.length === 0) {
return result;
}
uri = match[typeof matcher.matchIndex !== 'number' ? 0 : matcher.matchIndex];
rowStartIndex += match.index + uri.length;
}
}
return result;
}
/**
* Creates a link anchor element.
* @param {string} uri The uri of the link.
* @return {HTMLAnchorElement} The link.
*/
private _createAnchorElement(uri: string, handler: LinkMatcherHandler, isHypertextLinkHandler: boolean): HTMLAnchorElement {
const element = this._document.createElement('a');
element.textContent = uri;
element.draggable = false;
if (isHypertextLinkHandler) {
element.href = uri;
// Force link on another tab so work is not lost
element.target = '_blank';
element.addEventListener('click', (event: MouseEvent) => {
if (handler) {
return handler(event, uri);
}
});
} else {
element.addEventListener('click', (event: MouseEvent) => {
// Don't execute the handler if the link is flagged as invalid
if (element.classList.contains(INVALID_LINK_CLASS)) {
return;
}
return handler(event, uri);
});
}
return element;
}
/**
* Replace a node with 1 or more other nodes.
* @param {Node} oldNode The node to replace.
* @param {Node[]} newNodes The new nodes to insert in order.
*/
private _replaceNode(oldNode: Node, ...newNodes: Node[]): void {
const parent = oldNode.parentNode;
for (let i = 0; i < newNodes.length; i++) {
parent.insertBefore(newNodes[i], oldNode);
}
parent.removeChild(oldNode);
}
/**
* Replace a substring within a node with a new node.
* @param {Node} targetNode The target node; either a text node or a <span>
* containing a single text node.
* @param {Node} newNode The new node to insert.
* @param {string} substring The substring to replace.
* @param {number} substringIndex The index of the substring within the string.
* @return The number of nodes to skip when searching for the next uri.
*/
private _replaceNodeSubstringWithNode(targetNode: Node, newNode: Node, substring: string, substringIndex: number): number {
// If the targetNode is a non-text node with a single child, make the child
// the new targetNode.
if (targetNode.childNodes.length === 1) {
targetNode = targetNode.childNodes[0];
}
// The targetNode will be either a text node or a <span>. The text node
// (targetNode or its only-child) needs to be replaced with newNode plus new
// text nodes potentially on either side.
if (targetNode.nodeType !== 3/*Node.TEXT_NODE*/) {
throw new Error('targetNode must be a text node or only contain a single text node');
}
const fullText = targetNode.textContent;
if (substringIndex === 0) {
// Replace with <newNode><textnode>
const rightText = fullText.substring(substring.length);
const rightTextNode = this._document.createTextNode(rightText);
this._replaceNode(targetNode, newNode, rightTextNode);
return 0;
}
if (substringIndex === targetNode.textContent.length - substring.length) {
// Replace with <textnode><newNode>
const leftText = fullText.substring(0, substringIndex);
const leftTextNode = this._document.createTextNode(leftText);
this._replaceNode(targetNode, leftTextNode, newNode);
return 0;
}
// Replace with <textnode><newNode><textnode>
const leftText = fullText.substring(0, substringIndex);
const leftTextNode = this._document.createTextNode(leftText);
const rightText = fullText.substring(substringIndex + substring.length);
const rightTextNode = this._document.createTextNode(rightText);
this._replaceNode(targetNode, leftTextNode, newNode, rightTextNode);
return 1;
}
}
+638
View File
@@ -0,0 +1,638 @@
/**
* @license MIT
*/
import { C0 } from './EscapeSequences';
import { IInputHandler } from './Interfaces';
import { CHARSETS, DEFAULT_CHARSET } from './Charsets';
const normalStateHandler: {[key: string]: (parser: Parser, handler: IInputHandler) => void} = {};
normalStateHandler[C0.BEL] = (parser, handler) => handler.bell();
normalStateHandler[C0.LF] = (parser, handler) => handler.lineFeed();
normalStateHandler[C0.VT] = normalStateHandler[C0.LF];
normalStateHandler[C0.FF] = normalStateHandler[C0.LF];
normalStateHandler[C0.CR] = (parser, handler) => handler.carriageReturn();
normalStateHandler[C0.BS] = (parser, handler) => handler.backspace();
normalStateHandler[C0.HT] = (parser, handler) => handler.tab();
normalStateHandler[C0.SO] = (parser, handler) => handler.shiftOut();
normalStateHandler[C0.SI] = (parser, handler) => handler.shiftIn();
normalStateHandler[C0.ESC] = (parser, handler) => parser.setState(ParserState.ESCAPED);
// TODO: Remove terminal when parser owns params and currentParam
const escapedStateHandler: {[key: string]: (parser: Parser, terminal: any) => void} = {};
escapedStateHandler['['] = (parser, terminal) => {
// ESC [ Control Sequence Introducer (CSI is 0x9b)
terminal.params = [];
terminal.currentParam = 0;
parser.setState(ParserState.CSI_PARAM);
};
escapedStateHandler[']'] = (parser, terminal) => {
// ESC ] Operating System Command (OSC is 0x9d)
terminal.params = [];
terminal.currentParam = 0;
parser.setState(ParserState.OSC);
};
escapedStateHandler['P'] = (parser, terminal) => {
// ESC P Device Control String (DCS is 0x90)
terminal.params = [];
terminal.currentParam = 0;
parser.setState(ParserState.DCS);
};
escapedStateHandler['_'] = (parser, terminal) => {
// ESC _ Application Program Command ( APC is 0x9f).
parser.setState(ParserState.IGNORE);
};
escapedStateHandler['^'] = (parser, terminal) => {
// ESC ^ Privacy Message ( PM is 0x9e).
parser.setState(ParserState.IGNORE);
};
escapedStateHandler['c'] = (parser, terminal) => {
// ESC c Full Reset (RIS).
terminal.reset();
};
escapedStateHandler['E'] = (parser, terminal) => {
// ESC E Next Line ( NEL is 0x85).
terminal.x = 0;
terminal.index();
parser.setState(ParserState.NORMAL);
};
escapedStateHandler['D'] = (parser, terminal) => {
// ESC D Index ( IND is 0x84).
terminal.index();
parser.setState(ParserState.NORMAL);
};
escapedStateHandler['M'] = (parser, terminal) => {
// ESC M Reverse Index ( RI is 0x8d).
terminal.reverseIndex();
parser.setState(ParserState.NORMAL);
};
escapedStateHandler['%'] = (parser, terminal) => {
// ESC % Select default/utf-8 character set.
// @ = default, G = utf-8
terminal.setgLevel(0);
terminal.setgCharset(0, DEFAULT_CHARSET); // US (default)
parser.setState(ParserState.NORMAL);
parser.skipNextChar();
};
escapedStateHandler[C0.CAN] = (parser) => parser.setState(ParserState.NORMAL);
const csiParamStateHandler: {[key: string]: (parser: Parser) => void} = {};
csiParamStateHandler['?'] = (parser) => parser.setPrefix('?');
csiParamStateHandler['>'] = (parser) => parser.setPrefix('>');
csiParamStateHandler['!'] = (parser) => parser.setPrefix('!');
csiParamStateHandler['0'] = (parser) => parser.setParam(parser.getParam() * 10);
csiParamStateHandler['1'] = (parser) => parser.setParam(parser.getParam() * 10 + 1);
csiParamStateHandler['2'] = (parser) => parser.setParam(parser.getParam() * 10 + 2);
csiParamStateHandler['3'] = (parser) => parser.setParam(parser.getParam() * 10 + 3);
csiParamStateHandler['4'] = (parser) => parser.setParam(parser.getParam() * 10 + 4);
csiParamStateHandler['5'] = (parser) => parser.setParam(parser.getParam() * 10 + 5);
csiParamStateHandler['6'] = (parser) => parser.setParam(parser.getParam() * 10 + 6);
csiParamStateHandler['7'] = (parser) => parser.setParam(parser.getParam() * 10 + 7);
csiParamStateHandler['8'] = (parser) => parser.setParam(parser.getParam() * 10 + 8);
csiParamStateHandler['9'] = (parser) => parser.setParam(parser.getParam() * 10 + 9);
csiParamStateHandler['$'] = (parser) => parser.setPostfix('$');
csiParamStateHandler['"'] = (parser) => parser.setPostfix('"');
csiParamStateHandler[' '] = (parser) => parser.setPostfix(' ');
csiParamStateHandler['\''] = (parser) => parser.setPostfix('\'');
csiParamStateHandler[';'] = (parser) => parser.finalizeParam();
csiParamStateHandler[C0.CAN] = (parser) => parser.setState(ParserState.NORMAL);
const csiStateHandler: {[key: string]: (handler: IInputHandler, params: number[], prefix: string, postfix: string, parser: Parser) => void} = {};
csiStateHandler['@'] = (handler, params, prefix) => handler.insertChars(params);
csiStateHandler['A'] = (handler, params, prefix) => handler.cursorUp(params);
csiStateHandler['B'] = (handler, params, prefix) => handler.cursorDown(params);
csiStateHandler['C'] = (handler, params, prefix) => handler.cursorForward(params);
csiStateHandler['D'] = (handler, params, prefix) => handler.cursorBackward(params);
csiStateHandler['E'] = (handler, params, prefix) => handler.cursorNextLine(params);
csiStateHandler['F'] = (handler, params, prefix) => handler.cursorPrecedingLine(params);
csiStateHandler['G'] = (handler, params, prefix) => handler.cursorCharAbsolute(params);
csiStateHandler['H'] = (handler, params, prefix) => handler.cursorPosition(params);
csiStateHandler['I'] = (handler, params, prefix) => handler.cursorForwardTab(params);
csiStateHandler['J'] = (handler, params, prefix) => handler.eraseInDisplay(params);
csiStateHandler['K'] = (handler, params, prefix) => handler.eraseInLine(params);
csiStateHandler['L'] = (handler, params, prefix) => handler.insertLines(params);
csiStateHandler['M'] = (handler, params, prefix) => handler.deleteLines(params);
csiStateHandler['P'] = (handler, params, prefix) => handler.deleteChars(params);
csiStateHandler['S'] = (handler, params, prefix) => handler.scrollUp(params);
csiStateHandler['T'] = (handler, params, prefix) => {
if (params.length < 2 && !prefix) {
handler.scrollDown(params);
}
};
csiStateHandler['X'] = (handler, params, prefix) => handler.eraseChars(params);
csiStateHandler['Z'] = (handler, params, prefix) => handler.cursorBackwardTab(params);
csiStateHandler['`'] = (handler, params, prefix) => handler.charPosAbsolute(params);
csiStateHandler['a'] = (handler, params, prefix) => handler.HPositionRelative(params);
csiStateHandler['b'] = (handler, params, prefix) => handler.repeatPrecedingCharacter(params);
csiStateHandler['c'] = (handler, params, prefix) => handler.sendDeviceAttributes(params);
csiStateHandler['d'] = (handler, params, prefix) => handler.linePosAbsolute(params);
csiStateHandler['e'] = (handler, params, prefix) => handler.VPositionRelative(params);
csiStateHandler['f'] = (handler, params, prefix) => handler.HVPosition(params);
csiStateHandler['g'] = (handler, params, prefix) => handler.tabClear(params);
csiStateHandler['h'] = (handler, params, prefix) => handler.setMode(params);
csiStateHandler['l'] = (handler, params, prefix) => handler.resetMode(params);
csiStateHandler['m'] = (handler, params, prefix) => handler.charAttributes(params);
csiStateHandler['n'] = (handler, params, prefix) => handler.deviceStatus(params);
csiStateHandler['p'] = (handler, params, prefix) => {
switch (prefix) {
case '!': handler.softReset(params); break;
}
};
csiStateHandler['q'] = (handler, params, prefix, postfix) => {
if (postfix === ' ') {
handler.setCursorStyle(params);
}
};
csiStateHandler['r'] = (handler, params) => handler.setScrollRegion(params);
csiStateHandler['s'] = (handler, params) => handler.saveCursor(params);
csiStateHandler['u'] = (handler, params) => handler.restoreCursor(params);
csiStateHandler[C0.CAN] = (handler, params, prefix, postfix, parser) => parser.setState(ParserState.NORMAL);
enum ParserState {
NORMAL = 0,
ESCAPED = 1,
CSI_PARAM = 2,
CSI = 3,
OSC = 4,
CHARSET = 5,
DCS = 6,
IGNORE = 7
}
/**
* The terminal's parser, all input into the terminal goes through the parser
* which parses and defers the actual input handling the the IInputHandler
* specified in the constructor.
*/
export class Parser {
private _state: ParserState;
private _position: number;
// TODO: Remove terminal when handler can do everything
constructor(
private _inputHandler: IInputHandler,
private _terminal: any
) {
this._state = ParserState.NORMAL;
}
/**
* Parse and handle data.
*
* @param data The data to parse.
*/
public parse(data: string): ParserState {
let l = data.length, j, cs, ch, code, low;
this._position = 0;
// apply leftover surrogate high from last write
if (this._terminal.surrogate_high) {
data = this._terminal.surrogate_high + data;
this._terminal.surrogate_high = '';
}
for (; this._position < l; this._position++) {
ch = data[this._position];
// FIXME: higher chars than 0xa0 are not allowed in escape sequences
// --> maybe move to default
code = data.charCodeAt(this._position);
if (0xD800 <= code && code <= 0xDBFF) {
// we got a surrogate high
// get surrogate low (next 2 bytes)
low = data.charCodeAt(this._position + 1);
if (isNaN(low)) {
// end of data stream, save surrogate high
this._terminal.surrogate_high = ch;
continue;
}
code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
ch += data.charAt(this._position + 1);
}
// surrogate low - already handled above
if (0xDC00 <= code && code <= 0xDFFF)
continue;
switch (this._state) {
case ParserState.NORMAL:
if (ch in normalStateHandler) {
normalStateHandler[ch](this, this._inputHandler);
} else {
this._inputHandler.addChar(ch, code);
}
break;
case ParserState.ESCAPED:
if (ch in escapedStateHandler) {
escapedStateHandler[ch](this, this._terminal);
// Skip switch as it was just handled
break;
}
switch (ch) {
// ESC (,),*,+,-,. Designate G0-G2 Character Set.
case '(': // <-- this seems to get all the attention
case ')':
case '*':
case '+':
case '-':
case '.':
switch (ch) {
case '(':
this._terminal.gcharset = 0;
break;
case ')':
this._terminal.gcharset = 1;
break;
case '*':
this._terminal.gcharset = 2;
break;
case '+':
this._terminal.gcharset = 3;
break;
case '-':
this._terminal.gcharset = 1;
break;
case '.':
this._terminal.gcharset = 2;
break;
}
this._state = ParserState.CHARSET;
break;
// Designate G3 Character Set (VT300).
// A = ISO Latin-1 Supplemental.
// Not implemented.
case '/':
this._terminal.gcharset = 3;
this._state = ParserState.CHARSET;
this._position--;
break;
// ESC N
// Single Shift Select of G2 Character Set
// ( SS2 is 0x8e). This affects next character only.
case 'N':
break;
// ESC O
// Single Shift Select of G3 Character Set
// ( SS3 is 0x8f). This affects next character only.
case 'O':
break;
// ESC n
// Invoke the G2 Character Set as GL (LS2).
case 'n':
this._terminal.setgLevel(2);
break;
// ESC o
// Invoke the G3 Character Set as GL (LS3).
case 'o':
this._terminal.setgLevel(3);
break;
// ESC |
// Invoke the G3 Character Set as GR (LS3R).
case '|':
this._terminal.setgLevel(3);
break;
// ESC }
// Invoke the G2 Character Set as GR (LS2R).
case '}':
this._terminal.setgLevel(2);
break;
// ESC ~
// Invoke the G1 Character Set as GR (LS1R).
case '~':
this._terminal.setgLevel(1);
break;
// ESC 7 Save Cursor (DECSC).
case '7':
this._inputHandler.saveCursor();
this._state = ParserState.NORMAL;
break;
// ESC 8 Restore Cursor (DECRC).
case '8':
this._inputHandler.restoreCursor();
this._state = ParserState.NORMAL;
break;
// ESC # 3 DEC line height/width
case '#':
this._state = ParserState.NORMAL;
this._position++;
break;
// ESC H Tab Set (HTS is 0x88).
case 'H':
this._terminal.tabSet();
this._state = ParserState.NORMAL;
break;
// ESC = Application Keypad (DECKPAM).
case '=':
this._terminal.log('Serial port requested application keypad.');
this._terminal.applicationKeypad = true;
this._terminal.viewport.syncScrollArea();
this._state = ParserState.NORMAL;
break;
// ESC > Normal Keypad (DECKPNM).
case '>':
this._terminal.log('Switching back to normal keypad.');
this._terminal.applicationKeypad = false;
this._terminal.viewport.syncScrollArea();
this._state = ParserState.NORMAL;
break;
default:
this._state = ParserState.NORMAL;
this._terminal.error('Unknown ESC control: %s.', ch);
break;
}
break;
case ParserState.CHARSET:
if (ch in CHARSETS) {
cs = CHARSETS[ch];
if (ch === '/') { // ISOLatin is actually /A
this.skipNextChar();
}
} else {
cs = DEFAULT_CHARSET;
}
this._terminal.setgCharset(this._terminal.gcharset, cs);
this._terminal.gcharset = null;
this._state = ParserState.NORMAL;
break;
case ParserState.OSC:
// OSC Ps ; Pt ST
// OSC Ps ; Pt BEL
// Set Text Parameters.
if (ch === C0.ESC || ch === C0.BEL) {
if (ch === C0.ESC) this._position++;
this._terminal.params.push(this._terminal.currentParam);
switch (this._terminal.params[0]) {
case 0:
case 1:
case 2:
if (this._terminal.params[1]) {
this._terminal.title = this._terminal.params[1];
this._terminal.handleTitle(this._terminal.title);
}
break;
case 3:
// set X property
break;
case 4:
case 5:
// change dynamic colors
break;
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
case 18:
case 19:
// change dynamic ui colors
break;
case 46:
// change log file
break;
case 50:
// dynamic font
break;
case 51:
// emacs shell
break;
case 52:
// manipulate selection data
break;
case 104:
case 105:
case 110:
case 111:
case 112:
case 113:
case 114:
case 115:
case 116:
case 117:
case 118:
// reset colors
break;
}
this._terminal.params = [];
this._terminal.currentParam = 0;
this._state = ParserState.NORMAL;
} else {
if (!this._terminal.params.length) {
if (ch >= '0' && ch <= '9') {
this._terminal.currentParam =
this._terminal.currentParam * 10 + ch.charCodeAt(0) - 48;
} else if (ch === ';') {
this._terminal.params.push(this._terminal.currentParam);
this._terminal.currentParam = '';
}
} else {
this._terminal.currentParam += ch;
}
}
break;
case ParserState.CSI_PARAM:
if (ch in csiParamStateHandler) {
csiParamStateHandler[ch](this);
break;
}
this.finalizeParam();
// Fall through the CSI as this character should be the CSI code.
this._state = ParserState.CSI;
case ParserState.CSI:
if (ch in csiStateHandler) {
csiStateHandler[ch](this._inputHandler, this._terminal.params, this._terminal.prefix, this._terminal.postfix, this);
} else {
this._terminal.error('Unknown CSI code: %s.', ch);
}
this._state = ParserState.NORMAL;
this._terminal.prefix = '';
this._terminal.postfix = '';
break;
case ParserState.DCS:
if (ch === C0.ESC || ch === C0.BEL) {
if (ch === C0.ESC) this._position++;
let pt;
let valid: boolean;
switch (this._terminal.prefix) {
// User-Defined Keys (DECUDK).
case '':
break;
// Request Status String (DECRQSS).
// test: echo -e '\eP$q"p\e\\'
case '$q':
pt = this._terminal.currentParam;
valid = false;
switch (pt) {
// DECSCA
case '"q':
pt = '0"q';
break;
// DECSCL
case '"p':
pt = '61"p';
break;
// DECSTBM
case 'r':
pt = ''
+ (this._terminal.scrollTop + 1)
+ ';'
+ (this._terminal.scrollBottom + 1)
+ 'r';
break;
// SGR
case 'm':
pt = '0m';
break;
default:
this._terminal.error('Unknown DCS Pt: %s.', pt);
pt = '';
break;
}
this._terminal.send(C0.ESC + 'P' + +valid + '$r' + pt + C0.ESC + '\\');
break;
// Set Termcap/Terminfo Data (xterm, experimental).
case '+p':
break;
// Request Termcap/Terminfo String (xterm, experimental)
// Regular xterm does not even respond to this sequence.
// This can cause a small glitch in vim.
// test: echo -ne '\eP+q6b64\e\\'
case '+q':
pt = this._terminal.currentParam;
valid = false;
this._terminal.send(C0.ESC + 'P' + +valid + '+r' + pt + C0.ESC + '\\');
break;
default:
this._terminal.error('Unknown DCS prefix: %s.', this._terminal.prefix);
break;
}
this._terminal.currentParam = 0;
this._terminal.prefix = '';
this._state = ParserState.NORMAL;
} else if (!this._terminal.currentParam) {
if (!this._terminal.prefix && ch !== '$' && ch !== '+') {
this._terminal.currentParam = ch;
} else if (this._terminal.prefix.length === 2) {
this._terminal.currentParam = ch;
} else {
this._terminal.prefix += ch;
}
} else {
this._terminal.currentParam += ch;
}
break;
case ParserState.IGNORE:
// For PM and APC.
if (ch === C0.ESC || ch === C0.BEL) {
if (ch === C0.ESC) this._position++;
this._state = ParserState.NORMAL;
}
break;
}
}
return this._state;
}
/**
* Set the parser's current parsing state.
*
* @param state The new state.
*/
public setState(state: ParserState): void {
this._state = state;
}
/**
* Sets the parsier's current prefix. CSI codes can have prefixes of '?', '>'
* or '!'.
*
* @param prefix The prefix.
*/
public setPrefix(prefix: string): void {
this._terminal.prefix = prefix;
}
/**
* Sets the parsier's current prefix. CSI codes can have postfixes of '$',
* '"', ' ', '\''.
*
* @param postfix The postfix.
*/
public setPostfix(postfix: string): void {
this._terminal.postfix = postfix;
}
/**
* Sets the parser's current parameter.
*
* @param param the parameter.
*/
public setParam(param: number) {
this._terminal.currentParam = param;
}
/**
* Gets the parser's current parameter.
*/
public getParam(): number {
return this._terminal.currentParam;
}
/**
* Finalizes the parser's current parameter, adding it to the list of
* parameters and setting the new current parameter to 0.
*/
public finalizeParam(): void {
this._terminal.params.push(this._terminal.currentParam);
this._terminal.currentParam = 0;
}
/**
* Tell the parser to skip the next character.
*/
public skipNextChar(): void {
this._position++;
}
/**
* Tell the parser to repeat parsing the current character (for example if it
* needs parsing using a different state.
*/
// public repeatChar(): void {
// this._position--;
// }
}
+398
View File
@@ -0,0 +1,398 @@
/**
* @license MIT
*/
import { ITerminal } from './Interfaces';
import { DomElementObjectPool } from './utils/DomElementObjectPool';
/**
* The maximum number of refresh frames to skip when the write buffer is non-
* empty. Note that these frames may be intermingled with frames that are
* skipped via requestAnimationFrame's mechanism.
*/
const MAX_REFRESH_FRAME_SKIP = 5;
/**
* Flags used to render terminal text properly.
*/
enum FLAGS {
BOLD = 1,
UNDERLINE = 2,
BLINK = 4,
INVERSE = 8,
INVISIBLE = 16
};
let brokenBold: boolean = null;
export class Renderer {
/** A queue of the rows to be refreshed */
private _refreshRowsQueue: {start: number, end: number}[] = [];
private _refreshFramesSkipped = 0;
private _refreshAnimationFrame = null;
private _spanElementObjectPool = new DomElementObjectPool('span');
constructor(private _terminal: ITerminal) {
// Figure out whether boldness affects
// the character width of monospace fonts.
if (brokenBold === null) {
brokenBold = checkBoldBroken((<any>this._terminal).element);
}
this._spanElementObjectPool = new DomElementObjectPool('span');
// TODO: Pull more DOM interactions into Renderer.constructor, element for
// example should be owned by Renderer (and also exposed by Terminal due to
// to established public API).
}
/**
* Queues a refresh between two rows (inclusive), to be done on next animation
* frame.
* @param {number} start The start row.
* @param {number} end The end row.
*/
public queueRefresh(start: number, end: number): void {
this._refreshRowsQueue.push({ start: start, end: end });
if (!this._refreshAnimationFrame) {
this._refreshAnimationFrame = window.requestAnimationFrame(this._refreshLoop.bind(this));
}
}
/**
* Performs the refresh loop callback, calling refresh only if a refresh is
* necessary before queueing up the next one.
*/
private _refreshLoop(): void {
// Skip MAX_REFRESH_FRAME_SKIP frames if the writeBuffer is non-empty as it
// will need to be immediately refreshed anyway. This saves a lot of
// rendering time as the viewport DOM does not need to be refreshed, no
// scroll events, no layouts, etc.
const skipFrame = this._terminal.writeBuffer.length > 0 && this._refreshFramesSkipped++ <= MAX_REFRESH_FRAME_SKIP;
if (skipFrame) {
this._refreshAnimationFrame = window.requestAnimationFrame(this._refreshLoop.bind(this));
return;
}
this._refreshFramesSkipped = 0;
let start;
let end;
if (this._refreshRowsQueue.length > 4) {
// Just do a full refresh when 5+ refreshes are queued
start = 0;
end = this._terminal.rows - 1;
} else {
// Get start and end rows that need refreshing
start = this._refreshRowsQueue[0].start;
end = this._refreshRowsQueue[0].end;
for (let i = 1; i < this._refreshRowsQueue.length; i++) {
if (this._refreshRowsQueue[i].start < start) {
start = this._refreshRowsQueue[i].start;
}
if (this._refreshRowsQueue[i].end > end) {
end = this._refreshRowsQueue[i].end;
}
}
}
this._refreshRowsQueue = [];
this._refreshAnimationFrame = null;
this._refresh(start, end);
}
/**
* Refreshes (re-renders) terminal content within two rows (inclusive)
*
* Rendering Engine:
*
* In the screen buffer, each character is stored as a an array with a character
* and a 32-bit integer:
* - First value: a utf-16 character.
* - Second value:
* - Next 9 bits: background color (0-511).
* - Next 9 bits: foreground color (0-511).
* - Next 14 bits: a mask for misc. flags:
* - 1=bold
* - 2=underline
* - 4=blink
* - 8=inverse
* - 16=invisible
*
* @param {number} start The row to start from (between 0 and terminal's height terminal - 1)
* @param {number} end The row to end at (between fromRow and terminal's height terminal - 1)
*/
private _refresh(start: number, end: number): void {
// If this is a big refresh, remove the terminal rows from the DOM for faster calculations
let parent;
if (end - start >= this._terminal.rows / 2) {
parent = this._terminal.element.parentNode;
if (parent) {
this._terminal.element.removeChild(this._terminal.rowContainer);
}
}
let width = this._terminal.cols;
let y = start;
if (end >= this._terminal.rows) {
this._terminal.log('`end` is too large. Most likely a bad CSR.');
end = this._terminal.rows - 1;
}
for (; y <= end; y++) {
let row = y + this._terminal.ydisp;
let line = this._terminal.lines.get(row);
let x;
if (this._terminal.y === y - (this._terminal.ybase - this._terminal.ydisp) &&
this._terminal.cursorState &&
!this._terminal.cursorHidden) {
x = this._terminal.x;
} else {
x = -1;
}
let attr = this._terminal.defAttr;
const documentFragment = document.createDocumentFragment();
let innerHTML = '';
let currentElement;
// Return the row's spans to the pool
while (this._terminal.children[y].children.length) {
const child = this._terminal.children[y].children[0];
this._terminal.children[y].removeChild(child);
this._spanElementObjectPool.release(<HTMLElement>child);
}
for (let i = 0; i < width; i++) {
// TODO: Could data be a more specific type?
let data: any = line[i][0];
const ch = line[i][1];
const ch_width: any = line[i][2];
if (!ch_width) {
continue;
}
if (i === x) {
data = -1;
}
if (data !== attr) {
if (attr !== this._terminal.defAttr) {
if (innerHTML) {
currentElement.innerHTML = innerHTML;
innerHTML = '';
}
documentFragment.appendChild(currentElement);
currentElement = null;
}
if (data !== this._terminal.defAttr) {
if (innerHTML && !currentElement) {
currentElement = this._spanElementObjectPool.acquire();
}
if (currentElement) {
if (innerHTML) {
currentElement.innerHTML = innerHTML;
innerHTML = '';
}
documentFragment.appendChild(currentElement);
}
currentElement = this._spanElementObjectPool.acquire();
if (data === -1) {
currentElement.classList.add('reverse-video');
currentElement.classList.add('terminal-cursor');
} else {
let bg = data & 0x1ff;
let fg = (data >> 9) & 0x1ff;
let flags = data >> 18;
if (flags & FLAGS.BOLD) {
if (!brokenBold) {
currentElement.classList.add('xterm-bold');
}
// See: XTerm*boldColors
if (fg < 8) {
fg += 8;
}
}
if (flags & FLAGS.UNDERLINE) {
currentElement.classList.add('xterm-underline');
}
if (flags & FLAGS.BLINK) {
currentElement.classList.add('xterm-blink');
}
// If inverse flag is on, then swap the foreground and background variables.
if (flags & FLAGS.INVERSE) {
let temp = bg;
bg = fg;
fg = temp;
// Should inverse just be before the above boldColors effect instead?
if ((flags & 1) && fg < 8) {
fg += 8;
}
}
if (flags & FLAGS.INVISIBLE) {
currentElement.classList.add('xterm-hidden');
}
/**
* Weird situation: Invert flag used black foreground and white background results
* in invalid background color, positioned at the 256 index of the 256 terminal
* color map. Pin the colors manually in such a case.
*
* Source: https://github.com/sourcelair/xterm.js/issues/57
*/
if (flags & FLAGS.INVERSE) {
if (bg === 257) {
bg = 15;
}
if (fg === 256) {
fg = 0;
}
}
if (bg < 256) {
currentElement.classList.add(`xterm-bg-color-${bg}`);
}
if (fg < 256) {
currentElement.classList.add(`xterm-color-${fg}`);
}
}
}
}
if (ch_width === 2) {
// Wrap wide characters so they're sized correctly. It's more difficult to release these
// from the object pool so just create new ones via innerHTML.
innerHTML += `<span class="xterm-wide-char">${ch}</span>`;
} else if (ch.charCodeAt(0) > 255) {
// Wrap any non-wide unicode character as some fonts size them badly
innerHTML += `<span class="xterm-normal-char">${ch}</span>`;
} else {
switch (ch) {
case '&':
innerHTML += '&amp;';
break;
case '<':
innerHTML += '&lt;';
break;
case '>':
innerHTML += '&gt;';
break;
default:
if (ch <= ' ') {
innerHTML += '&nbsp;';
} else {
innerHTML += ch;
}
break;
}
}
attr = data;
}
if (innerHTML && !currentElement) {
currentElement = this._spanElementObjectPool.acquire();
}
if (currentElement) {
if (innerHTML) {
currentElement.innerHTML = innerHTML;
innerHTML = '';
}
documentFragment.appendChild(currentElement);
currentElement = null;
}
this._terminal.children[y].appendChild(documentFragment);
}
if (parent) {
this._terminal.element.appendChild(this._terminal.rowContainer);
}
this._terminal.emit('refresh', {element: this._terminal.element, start: start, end: end});
};
/**
* Refreshes the selection in the DOM.
* @param start The selection start.
* @param end The selection end.
*/
public refreshSelection(start: [number, number], end: [number, number]) {
// Remove all selections
while (this._terminal.selectionContainer.children.length) {
this._terminal.selectionContainer.removeChild(this._terminal.selectionContainer.children[0]);
}
// Selection does not exist
if (!start || !end) {
return;
}
// Translate from buffer position to viewport position
const viewportStartRow = start[1] - this._terminal.ydisp;
const viewportEndRow = end[1] - this._terminal.ydisp;
const viewportCappedStartRow = Math.max(viewportStartRow, 0);
const viewportCappedEndRow = Math.min(viewportEndRow, this._terminal.rows - 1);
// No need to draw the selection
if (viewportCappedStartRow >= this._terminal.rows || viewportCappedEndRow < 0) {
return;
}
// Create the selections
const documentFragment = document.createDocumentFragment();
// Draw first row
const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;
const endCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : this._terminal.cols;
documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow, startCol, endCol));
// Draw middle rows
const middleRowsCount = viewportCappedEndRow - viewportCappedStartRow - 1;
documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow + 1, 0, this._terminal.cols, middleRowsCount));
// Draw final row
if (viewportCappedStartRow !== viewportCappedEndRow) {
// Only draw viewportEndRow if it's not the same as viewporttartRow
const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._terminal.cols;
documentFragment.appendChild(this._createSelectionElement(viewportCappedEndRow, 0, endCol));
}
this._terminal.selectionContainer.appendChild(documentFragment);
}
/**
* Creates a selection element at the specified position.
* @param row The row of the selection.
* @param colStart The start column.
* @param colEnd The end columns.
*/
private _createSelectionElement(row: number, colStart: number, colEnd: number, rowCount: number = 1): HTMLElement {
const element = document.createElement('div');
element.style.height = `${rowCount * this._terminal.charMeasure.height}px`;
element.style.top = `${row * this._terminal.charMeasure.height}px`;
element.style.left = `${colStart * this._terminal.charMeasure.width}px`;
element.style.width = `${this._terminal.charMeasure.width * (colEnd - colStart)}px`;
return element;
}
}
// If bold is broken, we can't use it in the terminal.
function checkBoldBroken(terminal) {
const document = terminal.ownerDocument;
const el = document.createElement('span');
el.innerHTML = 'hello world';
terminal.appendChild(el);
const w1 = el.offsetWidth;
const h1 = el.offsetHeight;
el.style.fontWeight = 'bold';
const w2 = el.offsetWidth;
const h2 = el.offsetHeight;
terminal.removeChild(el);
return w1 !== w2 || h1 !== h2;
}
+220
View File
@@ -0,0 +1,220 @@
/**
* @license MIT
*/
import jsdom = require('jsdom');
import { assert } from 'chai';
import { ITerminal } from './Interfaces';
import { CharMeasure } from './utils/CharMeasure';
import { CircularList } from './utils/CircularList';
import { SelectionManager } from './SelectionManager';
import { SelectionModel } from './SelectionModel';
class TestSelectionManager extends SelectionManager {
constructor(
terminal: ITerminal,
buffer: CircularList<any>,
rowContainer: HTMLElement,
charMeasure: CharMeasure
) {
super(terminal, buffer, rowContainer, charMeasure);
}
public get model(): SelectionModel { return this._model; }
public selectLineAt(line: number): void { this._selectLineAt(line); }
public selectWordAt(coords: [number, number]): void { this._selectWordAt(coords); }
// Disable DOM interaction
public enable(): void {}
public disable(): void {}
public refresh(): void {}
}
describe('SelectionManager', () => {
let window: Window;
let document: Document;
let terminal: ITerminal;
let buffer: CircularList<any>;
let rowContainer: HTMLElement;
let selectionManager: TestSelectionManager;
beforeEach(done => {
jsdom.env('', (err, w) => {
window = w;
document = window.document;
buffer = new CircularList<any>(100);
terminal = <any>{ cols: 80, rows: 2 };
selectionManager = new TestSelectionManager(terminal, buffer, rowContainer, null);
done();
});
});
function stringToRow(text: string): [number, string, number][] {
let result: [number, string, number][] = [];
for (let i = 0; i < text.length; i++) {
result.push([0, text.charAt(i), 1]);
}
return result;
}
describe('_selectWordAt', () => {
it('should expand selection for normal width chars', () => {
buffer.push(stringToRow('foo bar'));
selectionManager.selectWordAt([0, 0]);
assert.equal(selectionManager.selectionText, 'foo');
selectionManager.selectWordAt([1, 0]);
assert.equal(selectionManager.selectionText, 'foo');
selectionManager.selectWordAt([2, 0]);
assert.equal(selectionManager.selectionText, 'foo');
selectionManager.selectWordAt([3, 0]);
assert.equal(selectionManager.selectionText, ' ');
selectionManager.selectWordAt([4, 0]);
assert.equal(selectionManager.selectionText, 'bar');
selectionManager.selectWordAt([5, 0]);
assert.equal(selectionManager.selectionText, 'bar');
selectionManager.selectWordAt([6, 0]);
assert.equal(selectionManager.selectionText, 'bar');
});
it('should expand selection for whitespace', () => {
buffer.push(stringToRow('a b'));
selectionManager.selectWordAt([0, 0]);
assert.equal(selectionManager.selectionText, 'a');
selectionManager.selectWordAt([1, 0]);
assert.equal(selectionManager.selectionText, ' ');
selectionManager.selectWordAt([2, 0]);
assert.equal(selectionManager.selectionText, ' ');
selectionManager.selectWordAt([3, 0]);
assert.equal(selectionManager.selectionText, ' ');
selectionManager.selectWordAt([4, 0]);
assert.equal(selectionManager.selectionText, 'b');
});
it('should expand selection for wide characters', () => {
// Wide characters use a special format
buffer.push([
[null, '中', 2],
[null, '', 0],
[null, '文', 2],
[null, '', 0],
[null, ' ', 1],
[null, 'a', 1],
[null, '中', 2],
[null, '', 0],
[null, '文', 2],
[null, '', 0],
[null, 'b', 1],
[null, ' ', 1],
[null, 'f', 1],
[null, 'o', 1],
[null, 'o', 1]
]);
// Ensure wide characters take up 2 columns
selectionManager.selectWordAt([0, 0]);
assert.equal(selectionManager.selectionText, '中文');
selectionManager.selectWordAt([1, 0]);
assert.equal(selectionManager.selectionText, '中文');
selectionManager.selectWordAt([2, 0]);
assert.equal(selectionManager.selectionText, '中文');
selectionManager.selectWordAt([3, 0]);
assert.equal(selectionManager.selectionText, '中文');
selectionManager.selectWordAt([4, 0]);
assert.equal(selectionManager.selectionText, ' ');
// Ensure wide characters work when wrapped in normal width characters
selectionManager.selectWordAt([5, 0]);
assert.equal(selectionManager.selectionText, 'a中文b');
selectionManager.selectWordAt([6, 0]);
assert.equal(selectionManager.selectionText, 'a中文b');
selectionManager.selectWordAt([7, 0]);
assert.equal(selectionManager.selectionText, 'a中文b');
selectionManager.selectWordAt([8, 0]);
assert.equal(selectionManager.selectionText, 'a中文b');
selectionManager.selectWordAt([9, 0]);
assert.equal(selectionManager.selectionText, 'a中文b');
selectionManager.selectWordAt([10, 0]);
assert.equal(selectionManager.selectionText, 'a中文b');
selectionManager.selectWordAt([11, 0]);
assert.equal(selectionManager.selectionText, ' ');
// Ensure normal width characters work fine in a line containing wide characters
selectionManager.selectWordAt([12, 0]);
assert.equal(selectionManager.selectionText, 'foo');
selectionManager.selectWordAt([13, 0]);
assert.equal(selectionManager.selectionText, 'foo');
selectionManager.selectWordAt([14, 0]);
assert.equal(selectionManager.selectionText, 'foo');
});
it('should select up to non-path characters that are commonly adjacent to paths', () => {
buffer.push(stringToRow('(cd)[ef]{gh}\'ij"'));
selectionManager.selectWordAt([0, 0]);
assert.equal(selectionManager.selectionText, '(cd');
selectionManager.selectWordAt([1, 0]);
assert.equal(selectionManager.selectionText, 'cd');
selectionManager.selectWordAt([2, 0]);
assert.equal(selectionManager.selectionText, 'cd');
selectionManager.selectWordAt([3, 0]);
assert.equal(selectionManager.selectionText, 'cd)');
selectionManager.selectWordAt([4, 0]);
assert.equal(selectionManager.selectionText, '[ef');
selectionManager.selectWordAt([5, 0]);
assert.equal(selectionManager.selectionText, 'ef');
selectionManager.selectWordAt([6, 0]);
assert.equal(selectionManager.selectionText, 'ef');
selectionManager.selectWordAt([7, 0]);
assert.equal(selectionManager.selectionText, 'ef]');
selectionManager.selectWordAt([8, 0]);
assert.equal(selectionManager.selectionText, '{gh');
selectionManager.selectWordAt([9, 0]);
assert.equal(selectionManager.selectionText, 'gh');
selectionManager.selectWordAt([10, 0]);
assert.equal(selectionManager.selectionText, 'gh');
selectionManager.selectWordAt([11, 0]);
assert.equal(selectionManager.selectionText, 'gh}');
selectionManager.selectWordAt([12, 0]);
assert.equal(selectionManager.selectionText, '\'ij');
selectionManager.selectWordAt([13, 0]);
assert.equal(selectionManager.selectionText, 'ij');
selectionManager.selectWordAt([14, 0]);
assert.equal(selectionManager.selectionText, 'ij');
selectionManager.selectWordAt([15, 0]);
assert.equal(selectionManager.selectionText, 'ij"');
});
});
describe('_selectLineAt', () => {
it('should select the entire line', () => {
buffer.push(stringToRow('foo bar'));
selectionManager.selectLineAt(0);
assert.equal(selectionManager.selectionText, 'foo bar', 'The selected text is correct');
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 0]);
assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 0], 'The actual selection spans the entire column');
});
});
describe('selectAll', () => {
it('should select the entire buffer, beyond the viewport', () => {
buffer.push(stringToRow('1'));
buffer.push(stringToRow('2'));
buffer.push(stringToRow('3'));
buffer.push(stringToRow('4'));
buffer.push(stringToRow('5'));
selectionManager.selectAll();
terminal.ybase = buffer.length - terminal.rows;
assert.equal(selectionManager.selectionText, '1\n2\n3\n4\n5');
});
});
describe('hasSelection', () => {
it('should return whether there is a selection', () => {
selectionManager.model.selectionStart = [0, 0];
selectionManager.model.selectionStartLength = 0;
assert.equal(selectionManager.hasSelection, false);
selectionManager.model.selectionEnd = [0, 0];
assert.equal(selectionManager.hasSelection, false);
selectionManager.model.selectionEnd = [1, 0];
assert.equal(selectionManager.hasSelection, true);
selectionManager.model.selectionEnd = [0, 1];
assert.equal(selectionManager.hasSelection, true);
selectionManager.model.selectionEnd = [1, 1];
assert.equal(selectionManager.hasSelection, true);
});
});
});
+718
View File
@@ -0,0 +1,718 @@
/**
* @license MIT
*/
import * as Mouse from './utils/Mouse';
import * as Browser from './utils/Browser';
import { CharMeasure } from './utils/CharMeasure';
import { CircularList } from './utils/CircularList';
import { EventEmitter } from './EventEmitter';
import { ITerminal } from './Interfaces';
import { SelectionModel } from './SelectionModel';
/**
* The number of pixels the mouse needs to be above or below the viewport in
* order to scroll at the maximum speed.
*/
const DRAG_SCROLL_MAX_THRESHOLD = 50;
/**
* The maximum scrolling speed
*/
const DRAG_SCROLL_MAX_SPEED = 15;
/**
* The number of milliseconds between drag scroll updates.
*/
const DRAG_SCROLL_INTERVAL = 50;
/**
* The amount of time before mousedown events are no longer stacked to create
* double/triple click events.
*/
const CLEAR_MOUSE_DOWN_TIME = 400;
/**
* The number of pixels in each direction that the mouse must move before
* mousedown events are no longer stacked to create double/triple click events.
*/
const CLEAR_MOUSE_DISTANCE = 10;
/**
* A string containing all characters that are considered word separated by the
* double click to select work logic.
*/
const WORD_SEPARATORS = ' ()[]{}\'"';
// TODO: Move these constants elsewhere, they belong in a buffer or buffer
// data/line class.
const LINE_DATA_CHAR_INDEX = 1;
const LINE_DATA_WIDTH_INDEX = 2;
const NON_BREAKING_SPACE_CHAR = String.fromCharCode(160);
const ALL_NON_BREAKING_SPACE_REGEX = new RegExp(NON_BREAKING_SPACE_CHAR, 'g');
/**
* Represents a position of a word on a line.
*/
interface IWordPosition {
start: number;
length: number;
}
/**
* A selection mode, this drives how the selection behaves on mouse move.
*/
enum SelectionMode {
NORMAL,
WORD,
LINE
}
/**
* A class that manages the selection of the terminal. With help from
* SelectionModel, SelectionManager handles with all logic associated with
* dealing with the selection, including handling mouse interaction, wide
* characters and fetching the actual text within the selection. Rendering is
* not handled by the SelectionManager but a 'refresh' event is fired when the
* selection is ready to be redrawn.
*/
export class SelectionManager extends EventEmitter {
protected _model: SelectionModel;
/**
* The amount to scroll every drag scroll update (depends on how far the mouse
* drag is above or below the terminal).
*/
private _dragScrollAmount: number;
/**
* The last time the mousedown event fired, this is used to track double and
* triple clicks.
*/
private _lastMouseDownTime: number;
/**
* The last position the mouse was clicked [x, y].
*/
private _lastMousePosition: [number, number];
/**
* The number of clicks of the mousedown event. This is used to keep track of
* double and triple clicks.
*/
private _clickCount: number;
/**
* The current selection mode.
*/
private _activeSelectionMode: SelectionMode;
/**
* A setInterval timer that is active while the mouse is down whose callback
* scrolls the viewport when necessary.
*/
private _dragScrollIntervalTimer: NodeJS.Timer;
/**
* The animation frame ID used for refreshing the selection.
*/
private _refreshAnimationFrame: number;
private _bufferTrimListener: any;
private _mouseMoveListener: EventListener;
private _mouseDownListener: EventListener;
private _mouseUpListener: EventListener;
constructor(
private _terminal: ITerminal,
private _buffer: CircularList<any>,
private _rowContainer: HTMLElement,
private _charMeasure: CharMeasure
) {
super();
this._initListeners();
this.enable();
this._model = new SelectionModel(_terminal);
this._lastMouseDownTime = 0;
this._activeSelectionMode = SelectionMode.NORMAL;
}
/**
* Initializes listener variables.
*/
private _initListeners() {
this._bufferTrimListener = (amount: number) => this._onTrim(amount);
this._mouseMoveListener = event => this._onMouseMove(<MouseEvent>event);
this._mouseDownListener = event => this._onMouseDown(<MouseEvent>event);
this._mouseUpListener = event => this._onMouseUp(<MouseEvent>event);
}
/**
* Disables the selection manager. This is useful for when terminal mouse
* are enabled.
*/
public disable() {
this.clearSelection();
this._buffer.off('trim', this._bufferTrimListener);
this._rowContainer.removeEventListener('mousedown', this._mouseDownListener);
}
/**
* Enable the selection manager.
*/
public enable() {
// Only adjust the selection on trim, shiftElements is rarely used (only in
// reverseIndex) and delete in a splice is only ever used when the same
// number of elements was just added. Given this is could actually be
// beneficial to leave the selection as is for these cases.
this._buffer.on('trim', this._bufferTrimListener);
this._rowContainer.addEventListener('mousedown', this._mouseDownListener);
}
/**
* Sets the active buffer, this should be called when the alt buffer is
* switched in or out.
* @param buffer The active buffer.
*/
public setBuffer(buffer: CircularList<any>): void {
this._buffer = buffer;
this.clearSelection();
}
/**
* Gets whether there is an active text selection.
*/
public get hasSelection(): boolean {
const start = this._model.finalSelectionStart;
const end = this._model.finalSelectionEnd;
if (!start || !end) {
return false;
}
return start[0] !== end[0] || start[1] !== end[1];
}
/**
* Gets the text currently selected.
*/
public get selectionText(): string {
const start = this._model.finalSelectionStart;
const end = this._model.finalSelectionEnd;
if (!start || !end) {
return '';
}
// Get first row
const startRowEndCol = start[1] === end[1] ? end[0] : null;
let result: string[] = [];
result.push(this._translateBufferLineToString(this._buffer.get(start[1]), true, start[0], startRowEndCol));
// Get middle rows
for (let i = start[1] + 1; i <= end[1] - 1; i++) {
const bufferLine = this._buffer.get(i);
const lineText = this._translateBufferLineToString(bufferLine, true);
if (bufferLine.isWrapped) {
result[result.length - 1] += lineText;
} else {
result.push(lineText);
}
}
// Get final row
if (start[1] !== end[1]) {
const bufferLine = this._buffer.get(end[1]);
const lineText = this._translateBufferLineToString(bufferLine, true, 0, end[0]);
if (bufferLine.isWrapped) {
result[result.length - 1] += lineText;
} else {
result.push(lineText);
}
}
// Format string by replacing non-breaking space chars with regular spaces
// and joining the array into a multi-line string.
const formattedResult = result.map(line => {
return line.replace(ALL_NON_BREAKING_SPACE_REGEX, ' ');
}).join(Browser.isMSWindows ? '\r\n' : '\n');
return formattedResult;
}
/**
* Clears the current terminal selection.
*/
public clearSelection(): void {
this._model.clearSelection();
this._removeMouseDownListeners();
this.refresh();
}
/**
* Translates a buffer line to a string, with optional start and end columns.
* Wide characters will count as two columns in the resulting string. This
* function is useful for getting the actual text underneath the raw selection
* position.
* @param line The line being translated.
* @param trimRight Whether to trim whitespace to the right.
* @param startCol The column to start at.
* @param endCol The column to end at.
*/
private _translateBufferLineToString(line: any, trimRight: boolean, startCol: number = 0, endCol: number = null): string {
// TODO: This function should live in a buffer or buffer line class
// Get full line
let lineString = '';
let widthAdjustedStartCol = startCol;
let widthAdjustedEndCol = endCol;
for (let i = 0; i < line.length; i++) {
const char = line[i];
lineString += char[LINE_DATA_CHAR_INDEX];
// Adjust start and end cols for wide characters if they affect their
// column indexes
if (char[LINE_DATA_WIDTH_INDEX] === 0) {
if (startCol >= i) {
widthAdjustedStartCol--;
}
if (endCol >= i) {
widthAdjustedEndCol--;
}
}
}
// Calculate the final end col by trimming whitespace on the right of the
// line if needed.
let finalEndCol = widthAdjustedEndCol || line.length;
if (trimRight) {
const rightWhitespaceIndex = lineString.search(/\s+$/);
if (rightWhitespaceIndex !== -1) {
finalEndCol = Math.min(finalEndCol, rightWhitespaceIndex);
}
// Return the empty string if only trimmed whitespace is selected
if (finalEndCol <= widthAdjustedStartCol) {
return '';
}
}
return lineString.substring(widthAdjustedStartCol, finalEndCol);
}
/**
* Queues a refresh, redrawing the selection on the next opportunity.
* @param isNewSelection Whether the selection should be registered as a new
* selection on Linux.
*/
public refresh(isNewSelection?: boolean): void {
// Queue the refresh for the renderer
if (!this._refreshAnimationFrame) {
this._refreshAnimationFrame = window.requestAnimationFrame(() => this._refresh());
}
// If the platform is Linux and the refresh call comes from a mouse event,
// we need to update the selection for middle click to paste selection.
if (Browser.isLinux && isNewSelection) {
const selectionText = this.selectionText;
if (selectionText.length) {
this.emit('newselection', this.selectionText);
}
}
}
/**
* Fires the refresh event, causing consumers to pick it up and redraw the
* selection state.
*/
private _refresh(): void {
this._refreshAnimationFrame = null;
this.emit('refresh', { start: this._model.finalSelectionStart, end: this._model.finalSelectionEnd });
}
/**
* Selects all text within the terminal.
*/
public selectAll(): void {
this._model.isSelectAllActive = true;
this.refresh();
}
/**
* Handle the buffer being trimmed, adjust the selection position.
* @param amount The amount the buffer is being trimmed.
*/
private _onTrim(amount: number) {
const needsRefresh = this._model.onTrim(amount);
if (needsRefresh) {
this.refresh();
}
}
/**
* Gets the 0-based [x, y] buffer coordinates of the current mouse event.
* @param event The mouse event.
*/
private _getMouseBufferCoords(event: MouseEvent): [number, number] {
const coords = Mouse.getCoords(event, this._rowContainer, this._charMeasure, this._terminal.cols, this._terminal.rows, true);
// Convert to 0-based
coords[0]--;
coords[1]--;
// Convert viewport coords to buffer coords
coords[1] += this._terminal.ydisp;
return coords;
}
/**
* Gets the amount the viewport should be scrolled based on how far out of the
* terminal the mouse is.
* @param event The mouse event.
*/
private _getMouseEventScrollAmount(event: MouseEvent): number {
let offset = Mouse.getCoordsRelativeToElement(event, this._rowContainer)[1];
const terminalHeight = this._terminal.rows * this._charMeasure.height;
if (offset >= 0 && offset <= terminalHeight) {
return 0;
}
if (offset > terminalHeight) {
offset -= terminalHeight;
}
offset = Math.min(Math.max(offset, -DRAG_SCROLL_MAX_THRESHOLD), DRAG_SCROLL_MAX_THRESHOLD);
offset /= DRAG_SCROLL_MAX_THRESHOLD;
return (offset / Math.abs(offset)) + Math.round(offset * (DRAG_SCROLL_MAX_SPEED - 1));
}
/**
* Handles te mousedown event, setting up for a new selection.
* @param event The mousedown event.
*/
private _onMouseDown(event: MouseEvent) {
// Only action the primary button
if (event.button !== 0) {
return;
}
// Tell the browser not to start a regular selection
event.preventDefault();
// Reset drag scroll state
this._dragScrollAmount = 0;
this._setMouseClickCount(event);
if (event.shiftKey) {
this._onShiftClick(event);
} else {
if (this._clickCount === 1) {
this._onSingleClick(event);
} else if (this._clickCount === 2) {
this._onDoubleClick(event);
} else if (this._clickCount === 3) {
this._onTripleClick(event);
}
}
this._addMouseDownListeners();
this.refresh(true);
}
/**
* Adds listeners when mousedown is triggered.
*/
private _addMouseDownListeners(): void {
// Listen on the document so that dragging outside of viewport works
this._rowContainer.ownerDocument.addEventListener('mousemove', this._mouseMoveListener);
this._rowContainer.ownerDocument.addEventListener('mouseup', this._mouseUpListener);
this._dragScrollIntervalTimer = setInterval(() => this._dragScroll(), DRAG_SCROLL_INTERVAL);
}
/**
* Removes the listeners that are registered when mousedown is triggered.
*/
private _removeMouseDownListeners(): void {
this._rowContainer.ownerDocument.removeEventListener('mousemove', this._mouseMoveListener);
this._rowContainer.ownerDocument.removeEventListener('mouseup', this._mouseUpListener);
clearInterval(this._dragScrollIntervalTimer);
this._dragScrollIntervalTimer = null;
}
/**
* Performs a shift click, setting the selection end position to the mouse
* position.
* @param event The mouse event.
*/
private _onShiftClick(event: MouseEvent): void {
if (this._model.selectionStart) {
this._model.selectionEnd = this._getMouseBufferCoords(event);
}
}
/**
* Performs a single click, resetting relevant state and setting the selection
* start position.
* @param event The mouse event.
*/
private _onSingleClick(event: MouseEvent): void {
this._model.selectionStartLength = 0;
this._model.isSelectAllActive = false;
this._activeSelectionMode = SelectionMode.NORMAL;
this._model.selectionStart = this._getMouseBufferCoords(event);
if (this._model.selectionStart) {
this._model.selectionEnd = null;
// If the mouse is over the second half of a wide character, adjust the
// selection to cover the whole character
const char = this._buffer.get(this._model.selectionStart[1])[this._model.selectionStart[0]];
if (char[LINE_DATA_WIDTH_INDEX] === 0) {
this._model.selectionStart[0]++;
}
}
}
/**
* Performs a double click, selecting the current work.
* @param event The mouse event.
*/
private _onDoubleClick(event: MouseEvent): void {
const coords = this._getMouseBufferCoords(event);
if (coords) {
this._activeSelectionMode = SelectionMode.WORD;
this._selectWordAt(coords);
}
}
/**
* Performs a triple click, selecting the current line and activating line
* select mode.
* @param event The mouse event.
*/
private _onTripleClick(event: MouseEvent): void {
const coords = this._getMouseBufferCoords(event);
if (coords) {
this._activeSelectionMode = SelectionMode.LINE;
this._selectLineAt(coords[1]);
}
}
/**
* Sets the number of clicks for the current mousedown event based on the time
* and position of the last mousedown event.
* @param event The mouse event.
*/
private _setMouseClickCount(event: MouseEvent): void {
let currentTime = (new Date()).getTime();
if (currentTime - this._lastMouseDownTime > CLEAR_MOUSE_DOWN_TIME || this._distanceFromLastMousePosition(event) > CLEAR_MOUSE_DISTANCE) {
this._clickCount = 0;
}
this._lastMouseDownTime = currentTime;
this._lastMousePosition = [event.pageX, event.pageY];
this._clickCount++;
}
/**
* Gets the maximum number of pixels in each direction the mouse has moved.
* @param event The mouse event.
*/
private _distanceFromLastMousePosition(event: MouseEvent): number {
const result = Math.max(
Math.abs(this._lastMousePosition[0] - event.pageX),
Math.abs(this._lastMousePosition[1] - event.pageY));
return result;
}
/**
* Handles the mousemove event when the mouse button is down, recording the
* end of the selection and refreshing the selection.
* @param event The mousemove event.
*/
private _onMouseMove(event: MouseEvent) {
// Record the previous position so we know whether to redraw the selection
// at the end.
const previousSelectionEnd = this._model.selectionEnd ? [this._model.selectionEnd[0], this._model.selectionEnd[1]] : null;
// Set the initial selection end based on the mouse coordinates
this._model.selectionEnd = this._getMouseBufferCoords(event);
// Select the entire line if line select mode is active.
if (this._activeSelectionMode === SelectionMode.LINE) {
if (this._model.selectionEnd[1] < this._model.selectionStart[1]) {
this._model.selectionEnd[0] = 0;
} else {
this._model.selectionEnd[0] = this._terminal.cols;
}
} else if (this._activeSelectionMode === SelectionMode.WORD) {
this._selectToWordAt(this._model.selectionEnd);
}
// Determine the amount of scrolling that will happen.
this._dragScrollAmount = this._getMouseEventScrollAmount(event);
// If the cursor was above or below the viewport, make sure it's at the
// start or end of the viewport respectively.
if (this._dragScrollAmount > 0) {
this._model.selectionEnd[0] = this._terminal.cols - 1;
} else if (this._dragScrollAmount < 0) {
this._model.selectionEnd[0] = 0;
}
// If the character is a wide character include the cell to the right in the
// selection. Note that selections at the very end of the line will never
// have a character.
if (this._model.selectionEnd[1] < this._buffer.length) {
const char = this._buffer.get(this._model.selectionEnd[1])[this._model.selectionEnd[0]];
if (char && char[2] === 0) {
this._model.selectionEnd[0]++;
}
}
// Only draw here if the selection changes.
if (!previousSelectionEnd ||
previousSelectionEnd[0] !== this._model.selectionEnd[0] ||
previousSelectionEnd[1] !== this._model.selectionEnd[1]) {
this.refresh(true);
}
}
/**
* The callback that occurs every DRAG_SCROLL_INTERVAL ms that does the
* scrolling of the viewport.
*/
private _dragScroll() {
if (this._dragScrollAmount) {
this._terminal.scrollDisp(this._dragScrollAmount, false);
// Re-evaluate selection
if (this._dragScrollAmount > 0) {
this._model.selectionEnd = [this._terminal.cols - 1, this._terminal.ydisp + this._terminal.rows];
} else {
this._model.selectionEnd = [0, this._terminal.ydisp];
}
this.refresh();
}
}
/**
* Handles the mouseup event, removing the mousedown listeners.
* @param event The mouseup event.
*/
private _onMouseUp(event: MouseEvent) {
this._removeMouseDownListeners();
}
/**
* Converts a viewport column to the character index on the buffer line, the
* latter takes into account wide characters.
* @param coords The coordinates to find the 2 index for.
*/
private _convertViewportColToCharacterIndex(bufferLine: any, coords: [number, number]): number {
let charIndex = coords[0];
for (let i = 0; coords[0] >= i; i++) {
const char = bufferLine[i];
if (char[LINE_DATA_WIDTH_INDEX] === 0) {
charIndex--;
}
}
return charIndex;
}
/**
* Gets positional information for the word at the coordinated specified.
* @param coords The coordinates to get the word at.
*/
private _getWordAt(coords: [number, number]): IWordPosition {
const bufferLine = this._buffer.get(coords[1]);
const line = this._translateBufferLineToString(bufferLine, false);
// Get actual index, taking into consideration wide characters
let endIndex = this._convertViewportColToCharacterIndex(bufferLine, coords);
let startIndex = endIndex;
// Record offset to be used later
const charOffset = coords[0] - startIndex;
let leftWideCharCount = 0;
let rightWideCharCount = 0;
if (line.charAt(startIndex) === ' ') {
// Expand until non-whitespace is hit
while (startIndex > 0 && line.charAt(startIndex - 1) === ' ') {
startIndex--;
}
while (endIndex < line.length && line.charAt(endIndex + 1) === ' ') {
endIndex++;
}
} else {
// Expand until whitespace is hit. This algorithm works by scanning left
// and right from the starting position, keeping both the index format
// (line) and the column format (bufferLine) in sync. When a wide
// character is hit, it is recorded and the column index is adjusted.
let startCol = coords[0];
let endCol = coords[0];
// Consider the initial position, skip it and increment the wide char
// variable
if (bufferLine[startCol][LINE_DATA_WIDTH_INDEX] === 0) {
leftWideCharCount++;
startCol--;
}
if (bufferLine[endCol][LINE_DATA_WIDTH_INDEX] === 2) {
rightWideCharCount++;
endCol++;
}
// Expand the string in both directions until a space is hit
while (startIndex > 0 && !this._isCharWordSeparator(line.charAt(startIndex - 1))) {
if (bufferLine[startCol - 1][LINE_DATA_WIDTH_INDEX] === 0) {
// If the next character is a wide char, record it and skip the column
leftWideCharCount++;
startCol--;
}
startIndex--;
startCol--;
}
while (endIndex + 1 < line.length && !this._isCharWordSeparator(line.charAt(endIndex + 1))) {
if (bufferLine[endCol + 1][LINE_DATA_WIDTH_INDEX] === 2) {
// If the next character is a wide char, record it and skip the column
rightWideCharCount++;
endCol++;
}
endIndex++;
endCol++;
}
}
const start = startIndex + charOffset - leftWideCharCount;
const length = Math.min(endIndex - startIndex + leftWideCharCount + rightWideCharCount + 1/*include endIndex char*/, this._terminal.cols);
return {start, length};
}
/**
* Selects the word at the coordinates specified.
* @param coords The coordinates to get the word at.
*/
protected _selectWordAt(coords: [number, number]): void {
const wordPosition = this._getWordAt(coords);
this._model.selectionStart = [wordPosition.start, coords[1]];
this._model.selectionStartLength = wordPosition.length;
}
/**
* Sets the selection end to the word at the coordinated specified.
* @param coords The coordinates to get the word at.
*/
private _selectToWordAt(coords: [number, number]): void {
const wordPosition = this._getWordAt(coords);
this._model.selectionEnd = [this._model.areSelectionValuesReversed() ? wordPosition.start : (wordPosition.start + wordPosition.length), coords[1]];
}
/**
* Gets whether the character is considered a word separator by the select
* word logic.
* @param char The character to check.
*/
private _isCharWordSeparator(char: string): boolean {
return WORD_SEPARATORS.indexOf(char) >= 0;
}
/**
* Selects the line specified.
* @param line The line index.
*/
protected _selectLineAt(line: number): void {
this._model.selectionStart = [0, line];
this._model.selectionStartLength = this._terminal.cols;
}
}
+131
View File
@@ -0,0 +1,131 @@
/**
* @license MIT
*/
import { assert } from 'chai';
import { ITerminal } from './Interfaces';
import { SelectionModel } from './SelectionModel';
class TestSelectionModel extends SelectionModel {
constructor(
terminal: ITerminal
) {
super(terminal);
}
}
describe('SelectionManager', () => {
let window: Window;
let document: Document;
let terminal: ITerminal;
let model: TestSelectionModel;
beforeEach(() => {
terminal = <any>{ cols: 80, rows: 2, ybase: 0 };
model = new TestSelectionModel(terminal);
});
describe('clearSelection', () => {
it('should clear the final selection', () => {
model.selectionStart = [0, 0];
model.selectionEnd = [10, 2];
assert.deepEqual(model.finalSelectionStart, [0, 0]);
assert.deepEqual(model.finalSelectionEnd, [10, 2]);
model.clearSelection();
assert.deepEqual(model.finalSelectionStart, null);
assert.deepEqual(model.finalSelectionEnd, null);
});
});
describe('areSelectionValuesReversed', () => {
it('should return true when the selection end is before selection start', () => {
model.selectionStart = [1, 0];
model.selectionEnd = [0, 0];
assert.equal(model.areSelectionValuesReversed(), true);
model.selectionStart = [10, 2];
model.selectionEnd = [0, 0];
assert.equal(model.areSelectionValuesReversed(), true);
});
it('should return false when the selection end is after selection start', () => {
model.selectionStart = [0, 0];
model.selectionEnd = [1, 0];
assert.equal(model.areSelectionValuesReversed(), false);
model.selectionStart = [0, 0];
model.selectionEnd = [10, 2];
assert.equal(model.areSelectionValuesReversed(), false);
});
});
describe('onTrim', () => {
it('should trim a portion of the selection when a part of it is trimmed', () => {
model.selectionStart = [0, 0];
model.selectionEnd = [10, 2];
model.onTrim(1);
assert.deepEqual(model.finalSelectionStart, [0, 0]);
assert.deepEqual(model.finalSelectionEnd, [10, 1]);
model.onTrim(1);
assert.deepEqual(model.finalSelectionStart, [0, 0]);
assert.deepEqual(model.finalSelectionEnd, [10, 0]);
});
it('should clear selection when it is trimmed in its entirety', () => {
model.selectionStart = [0, 0];
model.selectionEnd = [10, 0];
model.onTrim(1);
assert.deepEqual(model.finalSelectionStart, null);
assert.deepEqual(model.finalSelectionEnd, null);
});
});
describe('finalSelectionStart', () => {
it('should return the start of the buffer if select all is active', () => {
model.isSelectAllActive = true;
assert.deepEqual(model.finalSelectionStart, [0, 0]);
});
it('should return selection start if there is no selection end', () => {
model.selectionStart = [2, 2];
assert.deepEqual(model.finalSelectionStart, [2, 2]);
});
it('should return selection end if values are reversed', () => {
model.selectionStart = [2, 2];
model.selectionEnd = [3, 2];
assert.deepEqual(model.finalSelectionStart, [2, 2]);
model.selectionEnd = [1, 2];
assert.deepEqual(model.finalSelectionStart, [1, 2]);
});
});
describe('finalSelectionEnd', () => {
it('should return the end of the buffer if select all is active', () => {
model.isSelectAllActive = true;
assert.deepEqual(model.finalSelectionEnd, [80, 1]);
});
it('should return null if there is no selection start', () => {
assert.equal(model.finalSelectionEnd, null);
model.selectionEnd = [1, 2];
assert.equal(model.finalSelectionEnd, null);
});
it('should return selection start + length if there is no selection end', () => {
model.selectionStart = [2, 2];
model.selectionStartLength = 2;
assert.deepEqual(model.finalSelectionEnd, [4, 2]);
});
it('should return selection start + length if values are reversed', () => {
model.selectionStart = [2, 2];
model.selectionStartLength = 2;
model.selectionEnd = [2, 1];
assert.deepEqual(model.finalSelectionEnd, [4, 2]);
});
it('should return selection start + length if selection end is inside the start selection', () => {
model.selectionStart = [2, 2];
model.selectionStartLength = 2;
model.selectionEnd = [3, 2];
assert.deepEqual(model.finalSelectionEnd, [4, 2]);
});
it('should return selection end if selection end is after selection start + length', () => {
model.selectionStart = [2, 2];
model.selectionStartLength = 2;
model.selectionEnd = [5, 2];
assert.deepEqual(model.finalSelectionEnd, [5, 2]);
});
});
});
+128
View File
@@ -0,0 +1,128 @@
/**
* @license MIT
*/
import { ITerminal } from './Interfaces';
/**
* Represents a selection within the buffer. This model only cares about column
* and row coordinates, not wide characters.
*/
export class SelectionModel {
/**
* Whether select all is currently active.
*/
public isSelectAllActive: boolean;
/**
* The [x, y] position the selection starts at.
*/
public selectionStart: [number, number];
/**
* The minimal length of the selection from the start position. When double
* clicking on a word, the word will be selected which makes the selection
* start at the start of the word and makes this variable the length.
*/
public selectionStartLength: number;
/**
* The [x, y] position the selection ends at.
*/
public selectionEnd: [number, number];
constructor(
private _terminal: ITerminal
) {
this.clearSelection();
}
/**
* Clears the current selection.
*/
public clearSelection(): void {
this.selectionStart = null;
this.selectionEnd = null;
this.isSelectAllActive = false;
this.selectionStartLength = 0;
}
/**
* The final selection start, taking into consideration select all.
*/
public get finalSelectionStart(): [number, number] {
if (this.isSelectAllActive) {
return [0, 0];
}
if (!this.selectionEnd || !this.selectionStart) {
return this.selectionStart;
}
return this.areSelectionValuesReversed() ? this.selectionEnd : this.selectionStart;
}
/**
* The final selection end, taking into consideration select all, double click
* word selection and triple click line selection.
*/
public get finalSelectionEnd(): [number, number] {
if (this.isSelectAllActive) {
return [this._terminal.cols, this._terminal.ybase + this._terminal.rows - 1];
}
if (!this.selectionStart) {
return null;
}
// Use the selection start if the end doesn't exist or they're reversed
if (!this.selectionEnd || this.areSelectionValuesReversed()) {
return [this.selectionStart[0] + this.selectionStartLength, this.selectionStart[1]];
}
// Ensure the the word/line is selected after a double/triple click
if (this.selectionStartLength) {
// Select the larger of the two when start and end are on the same line
if (this.selectionEnd[1] === this.selectionStart[1]) {
return [Math.max(this.selectionStart[0] + this.selectionStartLength, this.selectionEnd[0]), this.selectionEnd[1]];
}
}
return this.selectionEnd;
}
/**
* Returns whether the selection start and end are reversed.
*/
public areSelectionValuesReversed(): boolean {
const start = this.selectionStart;
const end = this.selectionEnd;
return start[1] > end[1] || (start[1] === end[1] && start[0] > end[0]);
}
/**
* Handle the buffer being trimmed, adjust the selection position.
* @param amount The amount the buffer is being trimmed.
* @return Whether a refresh is necessary.
*/
public onTrim(amount: number): boolean {
// Adjust the selection position based on the trimmed amount.
if (this.selectionStart) {
this.selectionStart[1] -= amount;
}
if (this.selectionEnd) {
this.selectionEnd[1] -= amount;
}
// The selection has moved off the buffer, clear it.
if (this.selectionEnd && this.selectionEnd[1] < 0) {
this.clearSelection();
return true;
}
// If the selection start is trimmed, ensure the start column is 0.
if (this.selectionStart && this.selectionStart[1] < 0) {
this.selectionStart[1] = 0;
}
return false;
}
}
+14
View File
@@ -0,0 +1,14 @@
/**
* @license MIT
*/
export type LinkMatcher = {
id: number,
regex: RegExp,
handler: LinkMatcherHandler,
matchIndex?: number,
validationCallback?: LinkMatcherValidationCallback,
priority?: number
};
export type LinkMatcherHandler = (event: MouseEvent, uri: string) => boolean | void;
export type LinkMatcherValidationCallback = (uri: string, element: HTMLElement, callback: (isValid: boolean) => void) => void;
+92
View File
@@ -0,0 +1,92 @@
import { assert } from 'chai';
import { Viewport } from './Viewport';
describe('Viewport', () => {
let terminal;
let viewportElement;
let selectionContainer;
let charMeasure;
let viewport;
let scrollAreaElement;
const CHARACTER_HEIGHT = 10;
beforeEach(() => {
terminal = {
lines: [],
rows: 0,
ydisp: 0,
on: () => {},
rowContainer: {
style: {
lineHeight: 0
}
},
selectionContainer: {
style: {
height: 0
}
}
};
viewportElement = {
addEventListener: () => {},
style: {
height: 0,
lineHeight: 0
}
};
scrollAreaElement = {
style: {
height: 0
}
};
charMeasure = {
height: CHARACTER_HEIGHT
};
viewport = new Viewport(terminal, viewportElement, scrollAreaElement, charMeasure);
});
describe('refresh', () => {
it('should set the line-height of the terminal', done => {
// Allow CharMeasure to be initialized
setTimeout(() => {
assert.equal(viewportElement.style.lineHeight, CHARACTER_HEIGHT + 'px');
assert.equal(terminal.rowContainer.style.lineHeight, CHARACTER_HEIGHT + 'px');
charMeasure.height = 1;
viewport.refresh();
assert.equal(viewportElement.style.lineHeight, '1px');
assert.equal(terminal.rowContainer.style.lineHeight, '1px');
done();
}, 0);
});
it('should set the height of the viewport when the line-height changed', () => {
terminal.lines.push('');
terminal.lines.push('');
terminal.rows = 1;
viewport.refresh();
assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
charMeasure.height = 20;
viewport.refresh();
assert.equal(viewportElement.style.height, 20 + 'px');
});
});
describe('syncScrollArea', () => {
it('should sync the scroll area', done => {
// Allow CharMeasure to be initialized
setTimeout(() => {
terminal.lines.push('');
terminal.rows = 1;
assert.equal(scrollAreaElement.style.height, 0 * CHARACTER_HEIGHT + 'px');
viewport.syncScrollArea();
assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
assert.equal(scrollAreaElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
terminal.lines.push('');
viewport.syncScrollArea();
assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
assert.equal(scrollAreaElement.style.height, 2 * CHARACTER_HEIGHT + 'px');
done();
}, 0);
});
});
});
+147
View File
@@ -0,0 +1,147 @@
/**
* @license MIT
*/
import { ITerminal } from './Interfaces';
import { CharMeasure } from './utils/CharMeasure';
/**
* Represents the viewport of a terminal, the visible area within the larger buffer of output.
* Logic for the virtual scroll bar is included in this object.
*/
export class Viewport {
private currentRowHeight: number;
private lastRecordedBufferLength: number;
private lastRecordedViewportHeight: number;
private lastTouchY: number;
/**
* Creates a new Viewport.
* @param terminal The terminal this viewport belongs to.
* @param viewportElement The DOM element acting as the viewport.
* @param scrollArea The DOM element acting as the scroll area.
* @param charMeasureElement A DOM element used to measure the character size of. the terminal.
*/
constructor(
private terminal: ITerminal,
private viewportElement: HTMLElement,
private scrollArea: HTMLElement,
private charMeasure: CharMeasure
) {
this.currentRowHeight = 0;
this.lastRecordedBufferLength = 0;
this.lastRecordedViewportHeight = 0;
this.terminal.on('scroll', this.syncScrollArea.bind(this));
this.terminal.on('resize', this.syncScrollArea.bind(this));
this.viewportElement.addEventListener('scroll', this.onScroll.bind(this));
// Perform this async to ensure the CharMeasure is ready.
setTimeout(() => this.syncScrollArea(), 0);
}
/**
* Refreshes row height, setting line-height, viewport height and scroll area height if
* necessary.
* @param charSize A character size measurement bounding rect object, if it doesn't exist it will
* be created.
*/
private refresh(): void {
if (this.charMeasure.height > 0) {
const rowHeightChanged = this.charMeasure.height !== this.currentRowHeight;
if (rowHeightChanged) {
this.currentRowHeight = this.charMeasure.height;
this.viewportElement.style.lineHeight = this.charMeasure.height + 'px';
this.terminal.rowContainer.style.lineHeight = this.charMeasure.height + 'px';
}
const viewportHeightChanged = this.lastRecordedViewportHeight !== this.terminal.rows;
if (rowHeightChanged || viewportHeightChanged) {
this.lastRecordedViewportHeight = this.terminal.rows;
this.viewportElement.style.height = this.charMeasure.height * this.terminal.rows + 'px';
this.terminal.selectionContainer.style.height = this.viewportElement.style.height;
}
this.scrollArea.style.height = (this.charMeasure.height * this.lastRecordedBufferLength) + 'px';
}
}
/**
* Updates dimensions and synchronizes the scroll area if necessary.
*/
public syncScrollArea(): void {
if (this.lastRecordedBufferLength !== this.terminal.lines.length) {
// If buffer height changed
this.lastRecordedBufferLength = this.terminal.lines.length;
this.refresh();
} else if (this.lastRecordedViewportHeight !== this.terminal.rows) {
// If viewport height changed
this.refresh();
} else {
// If size has changed, refresh viewport
if (this.charMeasure.height !== this.currentRowHeight) {
this.refresh();
}
}
// Sync scrollTop
const scrollTop = this.terminal.ydisp * this.currentRowHeight;
if (this.viewportElement.scrollTop !== scrollTop) {
this.viewportElement.scrollTop = scrollTop;
}
}
/**
* Handles scroll events on the viewport, calculating the new viewport and requesting the
* terminal to scroll to it.
* @param ev The scroll event.
*/
private onScroll(ev: Event) {
const newRow = Math.round(this.viewportElement.scrollTop / this.currentRowHeight);
const diff = newRow - this.terminal.ydisp;
this.terminal.scrollDisp(diff, true);
}
/**
* Handles mouse wheel events by adjusting the viewport's scrollTop and delegating the actual
* scrolling to `onScroll`, this event needs to be attached manually by the consumer of
* `Viewport`.
* @param ev The mouse wheel event.
*/
public onWheel(ev: WheelEvent) {
if (ev.deltaY === 0) {
// Do nothing if it's not a vertical scroll event
return;
}
// Fallback to WheelEvent.DOM_DELTA_PIXEL
let multiplier = 1;
if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) {
multiplier = this.currentRowHeight;
} else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
multiplier = this.currentRowHeight * this.terminal.rows;
}
this.viewportElement.scrollTop += ev.deltaY * multiplier;
// Prevent the page from scrolling when the terminal scrolls
ev.preventDefault();
};
/**
* Handles the touchstart event, recording the touch occurred.
* @param ev The touch event.
*/
public onTouchStart(ev: TouchEvent) {
this.lastTouchY = ev.touches[0].pageY;
};
/**
* Handles the touchmove event, scrolling the viewport if the position shifted.
* @param ev The touch event.
*/
public onTouchMove(ev: TouchEvent) {
let deltaY = this.lastTouchY - ev.touches[0].pageY;
this.lastTouchY = ev.touches[0].pageY;
if (deltaY === 0) {
return;
}
this.viewportElement.scrollTop += deltaY;
ev.preventDefault();
};
}
+126
View File
@@ -0,0 +1,126 @@
/**
* Implements the attach method, that attaches the terminal to a WebSocket stream.
* @module xterm/addons/attach/attach
* @license MIT
*/
(function (attach) {
if (typeof exports === 'object' && typeof module === 'object') {
/*
* CommonJS environment
*/
module.exports = attach(require('../../xterm'));
} else if (typeof define == 'function') {
/*
* Require.js is available
*/
define(['../../xterm'], attach);
} else {
/*
* Plain browser environment
*/
attach(window.Terminal);
}
})(function (Xterm) {
'use strict';
var exports = {};
/**
* Attaches the given terminal to the given socket.
*
* @param {Xterm} term - The terminal to be attached to the given socket.
* @param {WebSocket} socket - The socket to attach the current terminal.
* @param {boolean} bidirectional - Whether the terminal should send data
* to the socket as well.
* @param {boolean} buffered - Whether the rendering of incoming data
* should happen instantly or at a maximum
* frequency of 1 rendering per 10ms.
*/
exports.attach = function (term, socket, bidirectional, buffered) {
bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional;
term.socket = socket;
term._flushBuffer = function () {
term.write(term._attachSocketBuffer);
term._attachSocketBuffer = null;
clearTimeout(term._attachSocketBufferTimer);
term._attachSocketBufferTimer = null;
};
term._pushToBuffer = function (data) {
if (term._attachSocketBuffer) {
term._attachSocketBuffer += data;
} else {
term._attachSocketBuffer = data;
setTimeout(term._flushBuffer, 10);
}
};
term._getMessage = function (ev) {
if (buffered) {
term._pushToBuffer(ev.data);
} else {
term.write(ev.data);
}
};
term._sendData = function (data) {
socket.send(data);
};
socket.addEventListener('message', term._getMessage);
if (bidirectional) {
term.on('data', term._sendData);
}
socket.addEventListener('close', term.detach.bind(term, socket));
socket.addEventListener('error', term.detach.bind(term, socket));
};
/**
* Detaches the given terminal from the given socket
*
* @param {Xterm} term - The terminal to be detached from the given socket.
* @param {WebSocket} socket - The socket from which to detach the current
* terminal.
*/
exports.detach = function (term, socket) {
term.off('data', term._sendData);
socket = (typeof socket == 'undefined') ? term.socket : socket;
if (socket) {
socket.removeEventListener('message', term._getMessage);
}
delete term.socket;
};
/**
* Attaches the current terminal to the given socket
*
* @param {WebSocket} socket - The socket to attach the current terminal.
* @param {boolean} bidirectional - Whether the terminal should send data
* to the socket as well.
* @param {boolean} buffered - Whether the rendering of incoming data
* should happen instantly or at a maximum
* frequency of 1 rendering per 10ms.
*/
Xterm.prototype.attach = function (socket, bidirectional, buffered) {
return exports.attach(this, socket, bidirectional, buffered);
};
/**
* Detaches the current terminal from the given socket.
*
* @param {WebSocket} socket - The socket from which to detach the current
* terminal.
*/
Xterm.prototype.detach = function (socket) {
return exports.detach(this, socket);
};
return exports;
});
+93
View File
@@ -0,0 +1,93 @@
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="../../src/xterm.css" />
<link rel="stylesheet" href="../../demo/style.css" />
<script src="../../src/xterm.js"></script>
<script src="attach.js"></script>
<style>
body {
color: #111;
}
h1, h2 {
color: #444;
border-bottom: 1px solid #ddd;
text-align: left;
}
form {
margin-bottom: 32px;
}
input, button {
line-height: 22px;
font-size: 16px;
display: inline-block;
border-radius: 2px;
border: 1px solid #ccc;
}
input {
height: 22px;
padding-left: 4px;
padding-right: 4px;
}
button {
height: 28px;
background-color: #ccc;
cursor: pointer;
color: #333;
}
.container {
max-width: 900px;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="container">
<h1>
xterm.js: socket attach
</h1>
<p>
Attach the terminal to a WebSocket terminal stream with ease. Perfect for attaching to your
Docker containers.
</p>
<h2>
Socket information
</h2>
<form id="socket-form">
<input id="socket-url"
type="text"
placeholder="Enter socket url (e.g. ws://mysock)"
autofocus />
<button>
Attach
</button>
</form>
<div id="terminal-container"></div>
</div>
<script>
var term = new Terminal(),
container = document.getElementById('terminal-container'),
socketUrl = document.getElementById('socket-url'),
socketForm = document.getElementById('socket-form');
socketForm.addEventListener('submit', function (ev) {
ev.preventDefault();
var url = socketUrl.value,
sock = new WebSocket(url);
sock.addEventListener('open', function () {
term.attach(sock);
});
});
term.open(container);
</script>
</body>
</html>
+5
View File
@@ -0,0 +1,5 @@
{
"name": "xterm.attach",
"main": "attach.js",
"private": true
}
+86
View File
@@ -0,0 +1,86 @@
/**
* Fit terminal columns and rows to the dimensions of its DOM element.
*
* ## Approach
* - Rows: Truncate the division of the terminal parent element height by the terminal row height.
*
* - Columns: Truncate the division of the terminal parent element width by the terminal character
* width (apply display: inline at the terminal row and truncate its width with the current
* number of columns).
* @module xterm/addons/fit/fit
* @license MIT
*/
(function (fit) {
if (typeof exports === 'object' && typeof module === 'object') {
/*
* CommonJS environment
*/
module.exports = fit(require('../../xterm'));
} else if (typeof define == 'function') {
/*
* Require.js is available
*/
define(['../../xterm'], fit);
} else {
/*
* Plain browser environment
*/
fit(window.Terminal);
}
})(function (Xterm) {
var exports = {};
exports.proposeGeometry = function (term) {
if (!term.element.parentElement) {
return null;
}
var parentElementStyle = window.getComputedStyle(term.element.parentElement),
parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')),
parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width')) - 17),
elementStyle = window.getComputedStyle(term.element),
elementPaddingVer = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom')),
elementPaddingHor = parseInt(elementStyle.getPropertyValue('padding-right')) + parseInt(elementStyle.getPropertyValue('padding-left')),
availableHeight = parentElementHeight - elementPaddingVer,
availableWidth = parentElementWidth - elementPaddingHor,
container = term.rowContainer,
subjectRow = term.rowContainer.firstElementChild,
contentBuffer = subjectRow.innerHTML,
characterHeight,
rows,
characterWidth,
cols,
geometry;
subjectRow.style.display = 'inline';
subjectRow.innerHTML = 'W'; // Common character for measuring width, although on monospace
characterWidth = subjectRow.getBoundingClientRect().width;
subjectRow.style.display = ''; // Revert style before calculating height, since they differ.
characterHeight = subjectRow.getBoundingClientRect().height;
subjectRow.innerHTML = contentBuffer;
rows = parseInt(availableHeight / characterHeight);
cols = parseInt(availableWidth / characterWidth);
geometry = {cols: cols, rows: rows};
return geometry;
};
exports.fit = function (term) {
var geometry = exports.proposeGeometry(term);
if (geometry) {
term.resize(geometry.cols, geometry.rows);
}
};
Xterm.prototype.proposeGeometry = function () {
return exports.proposeGeometry(this);
};
Xterm.prototype.fit = function () {
return exports.fit(this);
};
return exports;
});
+5
View File
@@ -0,0 +1,5 @@
{
"name": "xterm.fit",
"main": "fit.js",
"private": true
}
+10
View File
@@ -0,0 +1,10 @@
.xterm.fullscreen {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: auto;
height: auto;
z-index: 255;
}
+50
View File
@@ -0,0 +1,50 @@
/**
* Fullscreen addon for xterm.js
* @module xterm/addons/fullscreen/fullscreen
* @license MIT
*/
(function (fullscreen) {
if (typeof exports === 'object' && typeof module === 'object') {
/*
* CommonJS environment
*/
module.exports = fullscreen(require('../../xterm'));
} else if (typeof define == 'function') {
/*
* Require.js is available
*/
define(['../../xterm'], fullscreen);
} else {
/*
* Plain browser environment
*/
fullscreen(window.Terminal);
}
})(function (Xterm) {
var exports = {};
/**
* Toggle the given terminal's fullscreen mode.
* @param {Xterm} term - The terminal to toggle full screen mode
* @param {boolean} fullscreen - Toggle fullscreen on (true) or off (false)
*/
exports.toggleFullScreen = function (term, fullscreen) {
var fn;
if (typeof fullscreen == 'undefined') {
fn = (term.element.classList.contains('fullscreen')) ? 'remove' : 'add';
} else if (!fullscreen) {
fn = 'remove';
} else {
fn = 'add';
}
term.element.classList[fn]('fullscreen');
};
Xterm.prototype.toggleFullscreen = function (fullscreen) {
exports.toggleFullScreen(this, fullscreen);
};
return exports;
});
+5
View File
@@ -0,0 +1,5 @@
{
"name": "xterm.fullscreen",
"main": "fullscreen.js",
"private": true
}
+5
View File
@@ -0,0 +1,5 @@
{
"name": "xterm.terminado",
"main": "terminado.js",
"private": true
}
+135
View File
@@ -0,0 +1,135 @@
/**
* This module provides methods for attaching a terminal to a terminado WebSocket stream.
*
* @module xterm/addons/terminado/terminado
* @license MIT
*/
(function (attach) {
if (typeof exports === 'object' && typeof module === 'object') {
/*
* CommonJS environment
*/
module.exports = attach(require('../../xterm'));
} else if (typeof define == 'function') {
/*
* Require.js is available
*/
define(['../../xterm'], attach);
} else {
/*
* Plain browser environment
*/
attach(window.Terminal);
}
})(function (Xterm) {
'use strict';
var exports = {};
/**
* Attaches the given terminal to the given socket.
*
* @param {Xterm} term - The terminal to be attached to the given socket.
* @param {WebSocket} socket - The socket to attach the current terminal.
* @param {boolean} bidirectional - Whether the terminal should send data
* to the socket as well.
* @param {boolean} buffered - Whether the rendering of incoming data
* should happen instantly or at a maximum
* frequency of 1 rendering per 10ms.
*/
exports.terminadoAttach = function (term, socket, bidirectional, buffered) {
bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional;
term.socket = socket;
term._flushBuffer = function () {
term.write(term._attachSocketBuffer);
term._attachSocketBuffer = null;
clearTimeout(term._attachSocketBufferTimer);
term._attachSocketBufferTimer = null;
};
term._pushToBuffer = function (data) {
if (term._attachSocketBuffer) {
term._attachSocketBuffer += data;
} else {
term._attachSocketBuffer = data;
setTimeout(term._flushBuffer, 10);
}
};
term._getMessage = function (ev) {
var data = JSON.parse(ev.data)
if( data[0] == "stdout" ) {
if (buffered) {
term._pushToBuffer(data[1]);
} else {
term.write(data[1]);
}
}
};
term._sendData = function (data) {
socket.send(JSON.stringify(['stdin', data]));
};
term._setSize = function (size) {
socket.send(JSON.stringify(['set_size', size.rows, size.cols]));
};
socket.addEventListener('message', term._getMessage);
if (bidirectional) {
term.on('data', term._sendData);
}
term.on('resize', term._setSize);
socket.addEventListener('close', term.terminadoDetach.bind(term, socket));
socket.addEventListener('error', term.terminadoDetach.bind(term, socket));
};
/**
* Detaches the given terminal from the given socket
*
* @param {Xterm} term - The terminal to be detached from the given socket.
* @param {WebSocket} socket - The socket from which to detach the current
* terminal.
*/
exports.terminadoDetach = function (term, socket) {
term.off('data', term._sendData);
socket = (typeof socket == 'undefined') ? term.socket : socket;
if (socket) {
socket.removeEventListener('message', term._getMessage);
}
delete term.socket;
};
/**
* Attaches the current terminal to the given socket
*
* @param {WebSocket} socket - The socket to attach the current terminal.
* @param {boolean} bidirectional - Whether the terminal should send data
* to the socket as well.
* @param {boolean} buffered - Whether the rendering of incoming data
* should happen instantly or at a maximum
* frequency of 1 rendering per 10ms.
*/
Xterm.prototype.terminadoAttach = function (socket, bidirectional, buffered) {
return exports.terminadoAttach(this, socket, bidirectional, buffered);
};
/**
* Detaches the current terminal from the given socket.
*
* @param {WebSocket} socket - The socket from which to detach the current
* terminal.
*/
Xterm.prototype.terminadoDetach = function (socket) {
return exports.terminadoDetach(this, socket);
};
return exports;
});
+14
View File
@@ -0,0 +1,14 @@
import { assert } from 'chai';
import * as Terminal from '../xterm';
import * as Clipboard from './Clipboard';
describe('evaluatePastedTextProcessing', function () {
it('should replace carriage return + line feed with line feed on windows', function () {
const pastedText = 'foo\r\nbar\r\n',
processedText = Clipboard.prepareTextForTerminal(pastedText, false),
windowsProcessedText = Clipboard.prepareTextForTerminal(pastedText, true);
assert.equal(processedText, 'foo\r\nbar\r\n');
assert.equal(windowsProcessedText, 'foo\rbar\r');
});
});
+116
View File
@@ -0,0 +1,116 @@
/**
* Clipboard handler module: exports methods for handling all clipboard-related events in the
* terminal.
* @module xterm/handlers/Clipboard
* @license MIT
*/
import { ITerminal, ISelectionManager } from '../Interfaces';
interface IWindow extends Window {
clipboardData?: {
getData(format: string): string;
setData(format: string, data: string);
};
}
declare var window: IWindow;
/**
* Prepares text to be pasted into the terminal by normalizing the line endings
* @param text The pasted text that needs processing before inserting into the terminal
*/
export function prepareTextForTerminal(text: string, isMSWindows: boolean): string {
if (isMSWindows) {
return text.replace(/\r?\n/g, '\r');
}
return text;
}
/**
* Binds copy functionality to the given terminal.
* @param {ClipboardEvent} ev The original copy event to be handled
*/
export function copyHandler(ev: ClipboardEvent, term: ITerminal, selectionManager: ISelectionManager) {
if (term.browser.isMSIE) {
window.clipboardData.setData('Text', selectionManager.selectionText);
} else {
ev.clipboardData.setData('text/plain', selectionManager.selectionText);
}
// Prevent or the original text will be copied.
ev.preventDefault();
}
/**
* Redirect the clipboard's data to the terminal's input handler.
* @param {ClipboardEvent} ev The original paste event to be handled
* @param {Terminal} term The terminal on which to apply the handled paste event
*/
export function pasteHandler(ev: ClipboardEvent, term: ITerminal) {
ev.stopPropagation();
let text: string;
let dispatchPaste = function(text) {
text = prepareTextForTerminal(text, term.browser.isMSWindows);
term.handler(text);
term.textarea.value = '';
term.emit('paste', text);
return term.cancel(ev);
};
if (term.browser.isMSIE) {
if (window.clipboardData) {
text = window.clipboardData.getData('Text');
dispatchPaste(text);
}
} else {
if (ev.clipboardData) {
text = ev.clipboardData.getData('text/plain');
dispatchPaste(text);
}
}
}
/**
* Moves the textarea under the mouse cursor and focuses it.
* @param ev The original right click event to be handled.
* @param textarea The terminal's textarea.
*/
export function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextAreaElement) {
// Bring textarea at the cursor position
textarea.style.position = 'fixed';
textarea.style.width = '20px';
textarea.style.height = '20px';
textarea.style.left = (ev.clientX - 10) + 'px';
textarea.style.top = (ev.clientY - 10) + 'px';
textarea.style.zIndex = '1000';
textarea.focus();
// Reset the terminal textarea's styling
setTimeout(function () {
textarea.style.position = null;
textarea.style.width = null;
textarea.style.height = null;
textarea.style.left = null;
textarea.style.top = null;
textarea.style.zIndex = null;
}, 4);
}
/**
* Bind to right-click event and allow right-click copy and paste.
* @param ev The original right click event to be handled.
* @param textarea The terminal's textarea.
* @param selectionManager The terminal's selection manager.
*/
export function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement, selectionManager: ISelectionManager) {
moveTextAreaUnderMouseCursor(ev, textarea);
// Get textarea ready to copy from the context menu
textarea.value = selectionManager.selectionText;
textarea.select();
}
+10
View File
@@ -0,0 +1,10 @@
var assert = require('chai').assert;
var Terminal = require('../../xterm');
describe('xterm.js addons', function() {
it('should load addons with Terminal.loadAddon', function () {
Terminal.loadAddon('attach');
// Test that addon was loaded successfully
assert.equal(typeof Terminal.prototype.attach, 'function');
});
});
+133
View File
@@ -0,0 +1,133 @@
var glob = require('glob');
var fs = require('fs');
var os = require('os');
var pty = require('node-pty');
var sleep = require('sleep');
var Terminal = require('../xterm');
if (os.platform() === 'win32') {
// Skip tests on Windows since pty.open isn't supported
return;
}
var CONSOLE_LOG = console.log;
// expect files need terminal at 80x25!
var COLS = 80;
var ROWS = 25;
/** some helpers for pty interaction */
// we need a pty in between to get the termios decorations
// for the basic test cases a raw pty device is enough
var primitive_pty = pty.native.open(COLS, ROWS);
// fake sychronous pty write - read
// we just pipe the data from slave to master as a child program would do
// pty.js opens pipe fds with O_NONBLOCK
// just wait 10ms instead of setting fds to blocking mode
function pty_write_read(s) {
fs.writeSync(primitive_pty.slave, s);
sleep.usleep(10000);
var b = Buffer(64000);
var bytes = fs.readSync(primitive_pty.master, b, 0, 64000);
return b.toString('utf8', 0, bytes);
}
// make sure raw pty is at x=0 and has no pending data
function pty_reset() {
pty_write_read('\r\n');
}
/* debug helpers */
// generate colorful noisy output to compare xterm and emulator cell states
function formatError(in_, out_, expected) {
function addLineNumber(start, color) {
var counter = start || 0;
return function(s) {
counter += 1;
return '\x1b[33m' + (' ' + counter).slice(-2) + color + s;
}
}
var line80 = '12345678901234567890123456789012345678901234567890123456789012345678901234567890';
var s = '';
s += '\n\x1b[34m' + JSON.stringify(in_);
s += '\n\x1b[33m ' + line80 + '\n';
s += out_.split('\n').map(addLineNumber(0, '\x1b[31m')).join('\n');
s += '\n\x1b[33m ' + line80 + '\n';
s += expected.split('\n').map(addLineNumber(0, '\x1b[32m')).join('\n');
return s;
}
// simple debug output of terminal cells
function terminalToString(term) {
var result = '';
var line_s = '';
for (var line = term.ybase; line < term.ybase + term.rows; line++) {
line_s = '';
for (var cell=0; cell<term.cols; ++cell) {
line_s += term.lines.get(line)[cell][1];
}
// rtrim empty cells as xterm does
line_s = line_s.replace(/\s+$/, '');
result += line_s;
result += '\n';
}
return result;
}
/** tests */
describe('xterm output comparison', function() {
var xterm;
beforeEach(function () {
xterm = new Terminal(COLS, ROWS);
xterm.refresh = function() {};
xterm.viewport = {
syncScrollArea: function() {}
};
});
// omit stack trace for escape sequence files
Error.stackTraceLimit = 0;
var files = glob.sync('**/escape_sequence_files/*.in');
// only successful tests for now
var skip = [
10, 16, 17, 19, 32, 33, 34, 35, 36, 39,
40, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61,
63, 68
];
for (var i = 0; i < files.length; i++) {
if (skip.indexOf(i) >= 0) {
continue;
}
(function(filename) {
it(filename.split('/').slice(-1)[0], function () {
pty_reset();
var in_file = fs.readFileSync(filename, 'utf8');
var from_pty = pty_write_read(in_file);
// uncomment this to get log from terminal
//console.log = function(){};
// Perform a synchronous .write(data)
xterm.writeBuffer.push(from_pty);
xterm.innerWrite();
var from_emulator = terminalToString(xterm);
console.log = CONSOLE_LOG;
var expected = fs.readFileSync(filename.split('.')[0] + '.text', 'utf8');
// Some of the tests have whitespace on the right of lines, we trim all the linex
// from xterm.js so ignore this for now at least.
var expectedRightTrimmed = expected.split('\n').map(function (l) {
return l.replace(/\s+$/, '');
}).join('\n');
if (from_emulator != expectedRightTrimmed) {
// uncomment to get noisy output
throw new Error(formatError(in_file, from_emulator, expected));
// throw new Error('mismatch');
}
});
})(files[i]);
}
});
+21
View File
@@ -0,0 +1,21 @@
All tests are made for 80x25 terminal. Make sure to run tests with 80x25.
Create .text files from xterm (expected output)
- open xterm
- resize xterm to 80x25
- run `python run_tests.py`
- copy & paste whole window output into editor
- add 26th empty line (due to line handling in toString) - not a bug, a feature ;)
- advance to next test with ^D
Known problems
##############
t0031-HBP:
- no documentation at all about CSIj found - skipping
t0050-ICH:
- bug in xterm? (cant ICH last real char, always sticks to last col)
- text used from https://github.com/MarkLodato/vt100-parser/blob/master/test/t0050-ICH.text
@@ -0,0 +1,6 @@
!"#$%&'()*+,-./
0123456789:;<=>?
@ABCDEFGHIJKLMNO
PQRSTUVWXYZ[\]^_
`abcdefghijklmno
pqrstuvwxyz{|}~
@@ -0,0 +1,25 @@
!"#$%&'()*+,-./
0123456789:;<=>?
@ABCDEFGHIJKLMNO
PQRSTUVWXYZ[\]^_
`abcdefghijklmno
pqrstuvwxyz{|}~
@@ -0,0 +1,95 @@
!
"
#
$
%
&
'
(
)
*
+
,
-
.
/
0
1
2
3
4
5
6
7
8
9
:
;
<
=
>
?
@
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
[
\
]
^
_
`
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
{
|
}
~
@@ -0,0 +1,25 @@
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
{
|
}
~
@@ -0,0 +1 @@
abcdefghijklmnopqrstuvwxyz0123456789
@@ -0,0 +1,25 @@
abcdefghijklmnopqrstuvwxyz0123456789
@@ -0,0 +1,83 @@
a
ab
abc
abcd
abcde
abcdef
abcdefg
abcdefgh
abcdefghi
abcdefghij
abcdefghijk
abcdefghijkl
abcdefghijklm
abcdefghijklmn
abcdefghijklmno
abcdefghijklmnop
abcdefghijklmnopq
abcdefghijklmnopqr
abcdefghijklmnopqrs
abcdefghijklmnopqrst
abcdefghijklmnopqrstu
abcdefghijklmnopqrstuv
abcdefghijklmnopqrstuvw
abcdefghijklmnopqrstuvwx
abcdefghijklmnopqrstuvwxy
abcdefghijklmnopqrstuvwxyz
abcdefghijklmnopqrstuvwxyzA
abcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABC
abcdefghijklmnopqrstuvwxyzABCD
abcdefghijklmnopqrstuvwxyzABCDE
abcdefghijklmnopqrstuvwxyzABCDEF
abcdefghijklmnopqrstuvwxyzABCDEFG
abcdefghijklmnopqrstuvwxyzABCDEFGH
abcdefghijklmnopqrstuvwxyzABCDEFGHI
abcdefghijklmnopqrstuvwxyzABCDEFGHIJ
abcdefghijklmnopqrstuvwxyzABCDEFGHIJK
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLM
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRS
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRST
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTU
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890a
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890ab
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abc
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcd
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcde
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdef
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefg
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefgh
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghi
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghij
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijk
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijkl
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmn
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqr
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqrs
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqrst
@@ -0,0 +1,25 @@
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890a
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890ab
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abc
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcd
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcde
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdef
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefg
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefgh
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghi
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghij
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijk
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijkl
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmn
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq
r
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq
rs
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq
rst
@@ -0,0 +1,26 @@
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@@ -0,0 +1,25 @@
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@@ -0,0 +1,83 @@
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
0
1
2
3
4
5
6
7
8
9
0
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
@@ -0,0 +1,25 @@
7
8
9
0
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
@@ -0,0 +1,7 @@
1 x
2 x
3 x
4 x
5 x
6 x
7 x
@@ -0,0 +1,25 @@
x
x2
x 3
x 4
x 5
x 6
x 7
@@ -0,0 +1,82 @@
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
b a
@@ -0,0 +1,25 @@
a b
a b
a b
a b
a b
a b
a b
a b
a b
a b
a b
a b
a b
a b
a b
a b
a b
a b
a b
a b
a
ab
@@ -0,0 +1 @@
aDbDcDdDeDfDgDhDiDjDkDlDmDnDoDpDqDrDsDtDuDvDwDxDyDzDADBDCDDDEDFDGDHDIDJDKDLDMDNDODPDQDRDSDTDUDVDWDXDYDZD0D1D2D3D4D5D6D7D8D9D0DaDbDcDdDeDfDgDhDiDjDkDlDmDnDoDpDqDrDsDt
@@ -0,0 +1,25 @@
7
8
9
0
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
@@ -0,0 +1,8 @@
0 space:
1 space:
2 space:
3 space:
70 space:
71 space:
72 space:
73 space:
@@ -0,0 +1,25 @@
0 space:
1 space:
2 space:
3 space:
70 space:
71 space:
72 space:
73 space:
@@ -0,0 +1,7 @@
abcdefghijklmnopqrstuvwxyz!
abc@
#
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop$
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq%
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqr^
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqrs&

Some files were not shown because too many files have changed in this diff Show More