Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b7279146a | ||
|
|
c4a2f7911d | ||
|
|
db9fe53af3 | ||
|
|
42724c7f42 | ||
|
|
616f069538 | ||
|
|
2c3d98abc8 | ||
|
|
35b32b721e | ||
|
|
ccf1a047d3 | ||
|
|
db0338cbcf | ||
|
|
06742eb507 | ||
|
|
0fab4ff5d1 | ||
|
|
d2b0d514f8 | ||
|
|
70e35de56b | ||
|
|
c323e0ad4a | ||
|
|
4d32826891 | ||
|
|
13088f55a0 | ||
|
|
ea008411e4 | ||
|
|
a95ac990ff | ||
|
|
aad8439542 | ||
|
|
1a777edffe | ||
|
|
537018f71f | ||
|
|
67ca3a9334 | ||
|
|
5fb30979c1 | ||
|
|
cf863d4849 | ||
|
|
47814847f9 | ||
|
|
9ec8974545 | ||
|
|
c887c6e0f6 | ||
|
|
6c8949bba1 | ||
|
|
508d3ab68c | ||
|
|
6dad013783 | ||
|
|
0532e5cbf0 | ||
|
|
abcdf3e252 | ||
|
|
3baa6b9237 | ||
|
|
23e6a475ee | ||
|
|
2221f70ff0 | ||
|
|
daad8d9c04 | ||
|
|
03d26e39c7 | ||
|
|
64d3c089a2 | ||
|
|
938c011e63 | ||
|
|
66f6b88333 | ||
|
|
35f746c925 | ||
|
|
944da28089 | ||
|
|
8f3e2b645a | ||
|
|
82b2a5305f | ||
|
|
6a2852ee26 | ||
|
|
526d6c4da9 | ||
|
|
46d62f4972 | ||
|
|
5cafcdb695 | ||
|
|
2674258ead | ||
|
|
b38be3a1d0 | ||
|
|
18a9f05626 | ||
|
|
bf34980c43 | ||
|
|
d2f8f9dc1a | ||
|
|
df13469b6e | ||
|
|
584ec681af | ||
|
|
0c5b5cca4a | ||
|
|
64663bf4a8 | ||
|
|
fe8334a873 | ||
|
|
c74fd40e2a | ||
|
|
0f8ff67451 | ||
|
|
8b0032c210 | ||
|
|
d9254ef2d9 | ||
|
|
66be05a962 | ||
|
|
0e85a09dd3 | ||
|
|
0c4ba9fd67 | ||
|
|
aca81c764a | ||
|
|
6b3ddb97d1 | ||
|
|
26ccf2a316 | ||
|
|
361894194a | ||
|
|
8c2db8ddb8 | ||
|
|
5546baa927 | ||
|
|
d814009721 | ||
|
|
08fd050c3f | ||
|
|
0b096d7d55 | ||
|
|
44cfb2dc87 | ||
|
|
b0624cadb7 | ||
|
|
045f361b0d | ||
|
|
cb77b9dc1a | ||
|
|
0723dac596 | ||
|
|
365f9b4c1b | ||
|
|
2cf90d90f7 | ||
|
|
da14f64d1b | ||
|
|
ef1b1c8ffc | ||
|
|
adde8ab2c3 | ||
|
|
22cc8079dc | ||
|
|
6d25706300 | ||
|
|
81419c980b | ||
|
|
097e21f641 | ||
|
|
b68180b97b | ||
|
|
d9059d18c0 | ||
|
|
5225a6e27a | ||
|
|
9055bace96 | ||
|
|
eb25e88754 | ||
|
|
0cb8ecc6f7 | ||
|
|
8cf76cc6e9 | ||
|
|
3c3a646d42 | ||
|
|
ff29fd69a3 | ||
|
|
91994a399c | ||
|
|
bef7137c92 | ||
|
|
11f62bab34 | ||
|
|
b66776a047 | ||
|
|
2c151f444c | ||
|
|
d8f8b060cb | ||
|
|
ea9376019c | ||
|
|
008d1df106 | ||
|
|
ec91a6c7f4 | ||
|
|
4b2ae6a79f | ||
|
|
2ec756fd6b | ||
|
|
f0d5b40121 | ||
|
|
31a0996b2f | ||
|
|
0bbd8f0a16 | ||
|
|
3d721a95bb | ||
|
|
dc3a136704 | ||
|
|
43cb4f471f | ||
|
|
6b5ced0078 | ||
|
|
c7e8498166 | ||
|
|
2b4c019f13 | ||
|
|
335850380e |
@@ -22,24 +22,34 @@ Daniel Imms <daimms@microsoft.com>
|
|||||||
Daniel Risacher <drisacher@gmail.com>
|
Daniel Risacher <drisacher@gmail.com>
|
||||||
Dan Kaplun <dbkaplun@twitch.tv>
|
Dan Kaplun <dbkaplun@twitch.tv>
|
||||||
Darin Morrison <freebroccolo@users.noreply.github.com>
|
Darin Morrison <freebroccolo@users.noreply.github.com>
|
||||||
|
Edgar Andrés Margffoy Tuay <andfoy@gmail.com>
|
||||||
Elliot Saba <staticfloat@gmail.com>
|
Elliot Saba <staticfloat@gmail.com>
|
||||||
|
Gary Ritchie <gary@rstudio.com>
|
||||||
hiro-su <h.sugipon@gmail.com>
|
hiro-su <h.sugipon@gmail.com>
|
||||||
Ian Lewis <ianlewis@google.com>
|
Ian Lewis <ianlewis@google.com>
|
||||||
imoses <ido@twiggle.com>
|
imoses <ido@twiggle.com>
|
||||||
|
InDieTasten <indietasten@gmail.com>
|
||||||
Jean Bruenn <himself@jeanbruenn.info>
|
Jean Bruenn <himself@jeanbruenn.info>
|
||||||
|
Jeremy Danyow <jedanyow@microsoft.com>
|
||||||
Jörg Breitbart <jerch@rockborn.de>
|
Jörg Breitbart <jerch@rockborn.de>
|
||||||
|
Justin Mecham <justin@mecham.me>
|
||||||
Lucian Buzzo <lucian.buzzo@gmail.com>
|
Lucian Buzzo <lucian.buzzo@gmail.com>
|
||||||
Maël Nison <nison.mael@gmail.com>
|
Maël Nison <nison.mael@gmail.com>
|
||||||
|
Martin Chloride <i@martincl2.me>
|
||||||
Martin Wang <jiahaow@ca.ibm.com>
|
Martin Wang <jiahaow@ca.ibm.com>
|
||||||
|
Michael Irwin <mikesir87@gmail.com>
|
||||||
Mikko Karvonen <mikko.karvonen@arm.com>
|
Mikko Karvonen <mikko.karvonen@arm.com>
|
||||||
|
Nicolas Ramz <nicolas.ramz@gmail.com>
|
||||||
Paris Kasidiaris <pariskasidiaris@gmail.com>
|
Paris Kasidiaris <pariskasidiaris@gmail.com>
|
||||||
Paris Kasidiaris <paris@sourcelair.com>
|
Paris Kasidiaris <paris@sourcelair.com>
|
||||||
runarberg <runar@greenqloud.com>
|
runarberg <runar@greenqloud.com>
|
||||||
Saswat Das <saswatds@users.noreply.github.com>
|
Saswat Das <saswatds@users.noreply.github.com>
|
||||||
|
Saul Costa <saul@codevolve.com>
|
||||||
Shuanglei Tao <tsl0922@gmail.com>
|
Shuanglei Tao <tsl0922@gmail.com>
|
||||||
Steven Silvester <steven.silvester@ieee.org>
|
Steven Silvester <steven.silvester@ieee.org>
|
||||||
Thanasis Daglis <thanasis@sourcelair.com>
|
Thanasis Daglis <thanasis@sourcelair.com>
|
||||||
Tine Jozelj <tine.jozelj@outlook.com>
|
Tine Jozelj <tine.jozelj@outlook.com>
|
||||||
Tyler Jewell <tjewell@codenvy.com>
|
Tyler Jewell <tjewell@codenvy.com>
|
||||||
Vincent Woo <me@vincentwoo.com>
|
Vincent Woo <me@vincentwoo.com>
|
||||||
|
yutaka <shigure@refy.net>
|
||||||
YuviPanda <yuvipanda@gmail.com>
|
YuviPanda <yuvipanda@gmail.com>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# xterm.js
|
# [](https://xtermjs.org)
|
||||||
|
|
||||||
[](https://travis-ci.org/sourcelair/xterm.js) [](https://coveralls.io/github/sourcelair/xterm.js) [](https://gitter.im/sourcelair/xterm.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
[](https://travis-ci.org/sourcelair/xterm.js) [](https://coveralls.io/github/sourcelair/xterm.js) [](https://gitter.im/sourcelair/xterm.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||||
|
|
||||||
@@ -30,6 +30,12 @@ Xterm.js is used in several world-class applications to provide great terminal e
|
|||||||
- [**Codenvy**](http://www.codenvy.com): Cloud workspaces for development teams.
|
- [**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`.
|
- [**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.
|
- [**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.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
@@ -86,7 +92,7 @@ To start using xterm.js on your browser, add the `xterm.js` and `xterm.css` to t
|
|||||||
<div id="terminal"></div>
|
<div id="terminal"></div>
|
||||||
<script>
|
<script>
|
||||||
var term = new Terminal();
|
var term = new Terminal();
|
||||||
term.open(document.getElementById('#terminal'));
|
term.open(document.getElementById('terminal'));
|
||||||
term.write('Hello from \033[1;3;31mxterm.js\033[0m $ ')
|
term.write('Hello from \033[1;3;31mxterm.js\033[0m $ ')
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
@@ -121,27 +127,7 @@ The existing releases are available at this GitHub repo's [Releases](https://git
|
|||||||
|
|
||||||
Xterm.js is maintained by [SourceLair](https://www.sourcelair.com/) and a few external contributors, but we would love to receive contributions from everyone!
|
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) before.
|
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.
|
||||||
|
|
||||||
The development of xterm.js does not require any special tool. All you need is an editor that supports JavaScript and a browser (if you would like to run the demo you will need Node.js to get all features).
|
|
||||||
|
|
||||||
It is recommended though to use a development tool that uses xterm.js internally, to develop for xterm.js. [Eating our own dogfood](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) has been proved extremely beneficial for this project. Known tools that use xterm.js internally are:
|
|
||||||
|
|
||||||
#### [SourceLair](https://www.sourcelair.com)
|
|
||||||
|
|
||||||
Visit https://lair.io/sourcelair/xterm and follow the instructions. All development will happen in your browser.
|
|
||||||
|
|
||||||
#### [Visual Studio Code](http://code.visualstudio.com/)
|
|
||||||
|
|
||||||
[Download Visual Studio Code](http://code.visualstudio.com/Download), clone xterm.js and you are all set.
|
|
||||||
|
|
||||||
#### [Eclipse Che](http://www.eclipse.org/che)
|
|
||||||
|
|
||||||
You can start Eclipse Che with `docker run eclipse/che start`.
|
|
||||||
|
|
||||||
#### [Codenvy](http://www.codenvy.io)
|
|
||||||
|
|
||||||
You can create a trial account or install an enterprise version with `docker run codenvy/cli start`.
|
|
||||||
|
|
||||||
## License Agreement
|
## License Agreement
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "xterm.js",
|
"name": "xterm.js",
|
||||||
"version": "2.4.0",
|
"version": "2.7.0",
|
||||||
"ignore": ["demo", "test", ".gitignore"],
|
"ignore": ["demo", "test", ".gitignore"],
|
||||||
"main": [
|
"main": [
|
||||||
"dist/xterm.js",
|
"dist/xterm.js",
|
||||||
|
|||||||
@@ -19,6 +19,16 @@
|
|||||||
<p>
|
<p>
|
||||||
<label><input type="checkbox" id="option-cursor-blink"> cursorBlink</label>
|
<label><input type="checkbox" id="option-cursor-blink"> cursorBlink</label>
|
||||||
</p>
|
</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>
|
<p>
|
||||||
<label>scrollback <input type="number" id="option-scrollback" value="1000" /></label>
|
<label>scrollback <input type="number" id="option-scrollback" value="1000" /></label>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ var term,
|
|||||||
var terminalContainer = document.getElementById('terminal-container'),
|
var terminalContainer = document.getElementById('terminal-container'),
|
||||||
optionElements = {
|
optionElements = {
|
||||||
cursorBlink: document.querySelector('#option-cursor-blink'),
|
cursorBlink: document.querySelector('#option-cursor-blink'),
|
||||||
|
cursorStyle: document.querySelector('#option-cursor-style'),
|
||||||
scrollback: document.querySelector('#option-scrollback'),
|
scrollback: document.querySelector('#option-scrollback'),
|
||||||
tabstopwidth: document.querySelector('#option-tabstopwidth')
|
tabstopwidth: document.querySelector('#option-tabstopwidth')
|
||||||
},
|
},
|
||||||
@@ -32,6 +33,9 @@ rowsElement.addEventListener('change', setTerminalSize);
|
|||||||
optionElements.cursorBlink.addEventListener('change', function () {
|
optionElements.cursorBlink.addEventListener('change', function () {
|
||||||
term.setOption('cursorBlink', optionElements.cursorBlink.checked);
|
term.setOption('cursorBlink', optionElements.cursorBlink.checked);
|
||||||
});
|
});
|
||||||
|
optionElements.cursorStyle.addEventListener('change', function () {
|
||||||
|
term.setOption('cursorStyle', optionElements.cursorStyle.value);
|
||||||
|
});
|
||||||
optionElements.scrollback.addEventListener('change', function () {
|
optionElements.scrollback.addEventListener('change', function () {
|
||||||
term.setOption('scrollback', parseInt(optionElements.scrollback.value, 10));
|
term.setOption('scrollback', parseInt(optionElements.scrollback.value, 10));
|
||||||
});
|
});
|
||||||
|
|||||||
Vendored
+1
-1
@@ -56,7 +56,7 @@
|
|||||||
subjectRow.innerHTML = 'W'; // Common character for measuring width, although on monospace
|
subjectRow.innerHTML = 'W'; // Common character for measuring width, although on monospace
|
||||||
characterWidth = subjectRow.getBoundingClientRect().width;
|
characterWidth = subjectRow.getBoundingClientRect().width;
|
||||||
subjectRow.style.display = ''; // Revert style before calculating height, since they differ.
|
subjectRow.style.display = ''; // Revert style before calculating height, since they differ.
|
||||||
characterHeight = parseInt(subjectRow.offsetHeight);
|
characterHeight = subjectRow.getBoundingClientRect().height;
|
||||||
subjectRow.innerHTML = contentBuffer;
|
subjectRow.innerHTML = contentBuffer;
|
||||||
|
|
||||||
rows = parseInt(availableHeight / characterHeight);
|
rows = parseInt(availableHeight / characterHeight);
|
||||||
|
|||||||
Vendored
+11
-17
@@ -86,7 +86,7 @@
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar) .terminal-cursor {
|
.terminal.focus:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar) .terminal-cursor {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
@@ -97,16 +97,9 @@
|
|||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar).focus.xterm-cursor-blink .terminal-cursor {
|
.terminal:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar).focus.xterm-cursor-blink-on .terminal-cursor {
|
||||||
animation: xterm-cursor-blink 1.2s infinite step-end;
|
background-color: transparent;
|
||||||
}
|
color: inherit;
|
||||||
|
|
||||||
@keyframes xterm-cursor-blink {
|
|
||||||
0% { }
|
|
||||||
50% {
|
|
||||||
background-color: transparent;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal.xterm-cursor-style-bar .terminal-cursor,
|
.terminal.xterm-cursor-style-bar .terminal-cursor,
|
||||||
@@ -132,13 +125,13 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
}
|
}
|
||||||
|
.terminal.xterm-cursor-style-bar.focus.xterm-cursor-blink.xterm-cursor-blink-on .terminal-cursor::before,
|
||||||
|
.terminal.xterm-cursor-style-underline.focus.xterm-cursor-blink.xterm-cursor-blink-on .terminal-cursor::before {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
.terminal.xterm-cursor-style-bar.focus.xterm-cursor-blink .terminal-cursor::before,
|
.terminal.xterm-cursor-style-bar.focus.xterm-cursor-blink .terminal-cursor::before,
|
||||||
.terminal.xterm-cursor-style-underline.focus.xterm-cursor-blink .terminal-cursor::before {
|
.terminal.xterm-cursor-style-underline.focus.xterm-cursor-blink .terminal-cursor::before {
|
||||||
animation: xterm-cursor-non-bar-blink 1.2s infinite step-end;
|
background-color: #fff;
|
||||||
}
|
|
||||||
@keyframes xterm-cursor-non-bar-blink {
|
|
||||||
0% { background-color: #fff; }
|
|
||||||
50% { background-color: transparent; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal .composition-view {
|
.terminal .composition-view {
|
||||||
@@ -160,7 +153,8 @@
|
|||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal .xterm-wide-char {
|
.terminal .xterm-wide-char,
|
||||||
|
.terminal .xterm-normal-char {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+372
-194
@@ -1,5 +1,6 @@
|
|||||||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Terminal = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Terminal = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||||
"use strict";
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.CHARSETS = {};
|
exports.CHARSETS = {};
|
||||||
exports.DEFAULT_CHARSET = exports.CHARSETS['B'];
|
exports.DEFAULT_CHARSET = exports.CHARSETS['B'];
|
||||||
exports.CHARSETS['0'] = {
|
exports.CHARSETS['0'] = {
|
||||||
@@ -162,6 +163,7 @@ exports.CHARSETS['='] = {
|
|||||||
|
|
||||||
},{}],2:[function(require,module,exports){
|
},{}],2:[function(require,module,exports){
|
||||||
"use strict";
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
var CompositionHelper = (function () {
|
var CompositionHelper = (function () {
|
||||||
function CompositionHelper(textarea, compositionView, terminal) {
|
function CompositionHelper(textarea, compositionView, terminal) {
|
||||||
this.textarea = textarea;
|
this.textarea = textarea;
|
||||||
@@ -288,6 +290,7 @@ exports.CompositionHelper = CompositionHelper;
|
|||||||
|
|
||||||
},{}],3:[function(require,module,exports){
|
},{}],3:[function(require,module,exports){
|
||||||
"use strict";
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
var C0;
|
var C0;
|
||||||
(function (C0) {
|
(function (C0) {
|
||||||
C0.NUL = '\x00';
|
C0.NUL = '\x00';
|
||||||
@@ -331,6 +334,7 @@ var C0;
|
|||||||
|
|
||||||
},{}],4:[function(require,module,exports){
|
},{}],4:[function(require,module,exports){
|
||||||
"use strict";
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
;
|
;
|
||||||
var EventEmitter = (function () {
|
var EventEmitter = (function () {
|
||||||
function EventEmitter() {
|
function EventEmitter() {
|
||||||
@@ -368,10 +372,13 @@ var EventEmitter = (function () {
|
|||||||
return this.on(type, on);
|
return this.on(type, on);
|
||||||
};
|
};
|
||||||
EventEmitter.prototype.emit = function (type) {
|
EventEmitter.prototype.emit = function (type) {
|
||||||
|
var args = [];
|
||||||
|
for (var _i = 1; _i < arguments.length; _i++) {
|
||||||
|
args[_i - 1] = arguments[_i];
|
||||||
|
}
|
||||||
if (!this._events[type]) {
|
if (!this._events[type]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var args = Array.prototype.slice.call(arguments, 1);
|
|
||||||
var obj = this._events[type];
|
var obj = this._events[type];
|
||||||
for (var i = 0; i < obj.length; i++) {
|
for (var i = 0; i < obj.length; i++) {
|
||||||
obj[i].apply(this, args);
|
obj[i].apply(this, args);
|
||||||
@@ -388,6 +395,7 @@ exports.EventEmitter = EventEmitter;
|
|||||||
|
|
||||||
},{}],5:[function(require,module,exports){
|
},{}],5:[function(require,module,exports){
|
||||||
"use strict";
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
var EscapeSequences_1 = require("./EscapeSequences");
|
var EscapeSequences_1 = require("./EscapeSequences");
|
||||||
var Charsets_1 = require("./Charsets");
|
var Charsets_1 = require("./Charsets");
|
||||||
var InputHandler = (function () {
|
var InputHandler = (function () {
|
||||||
@@ -632,7 +640,12 @@ var InputHandler = (function () {
|
|||||||
this._terminal.eraseLine(j);
|
this._terminal.eraseLine(j);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
;
|
var scrollBackSize = this._terminal.lines.length - this._terminal.rows;
|
||||||
|
if (scrollBackSize > 0) {
|
||||||
|
this._terminal.lines.trimStart(scrollBackSize);
|
||||||
|
this._terminal.ybase = Math.max(this._terminal.ybase - scrollBackSize, 0);
|
||||||
|
this._terminal.ydisp = Math.max(this._terminal.ydisp - scrollBackSize, 0);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1314,6 +1327,7 @@ var wcwidth = (function (opts) {
|
|||||||
|
|
||||||
},{"./Charsets":1,"./EscapeSequences":3}],6:[function(require,module,exports){
|
},{"./Charsets":1,"./EscapeSequences":3}],6:[function(require,module,exports){
|
||||||
"use strict";
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
var INVALID_LINK_CLASS = 'xterm-invalid-link';
|
var INVALID_LINK_CLASS = 'xterm-invalid-link';
|
||||||
var protocolClause = '(https?:\\/\\/)';
|
var protocolClause = '(https?:\\/\\/)';
|
||||||
var domainCharacterSet = '[\\da-z\\.-]+';
|
var domainCharacterSet = '[\\da-z\\.-]+';
|
||||||
@@ -1324,8 +1338,8 @@ var ipClause = '((\\d{1,3}\\.){3}\\d{1,3})';
|
|||||||
var localHostClause = '(localhost)';
|
var localHostClause = '(localhost)';
|
||||||
var portClause = '(:\\d{1,5})';
|
var portClause = '(:\\d{1,5})';
|
||||||
var hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?';
|
var hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?';
|
||||||
var pathClause = '(\\/[\\/\\w\\.\\-%]*)*';
|
var pathClause = '(\\/[\\/\\w\\.\\-%~]*)*';
|
||||||
var queryStringHashFragmentCharacterSet = '[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&\'*+,:;\\=\\.\\-]*';
|
var queryStringHashFragmentCharacterSet = '[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&\'*+,:;~\\=\\.\\-]*';
|
||||||
var queryStringClause = '(\\?' + queryStringHashFragmentCharacterSet + ')?';
|
var queryStringClause = '(\\?' + queryStringHashFragmentCharacterSet + ')?';
|
||||||
var hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?';
|
var hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?';
|
||||||
var negatedPathCharacterSet = '[^\\/\\w\\.\\-%]+';
|
var negatedPathCharacterSet = '[^\\/\\w\\.\\-%]+';
|
||||||
@@ -1335,24 +1349,32 @@ var end = ')($|' + negatedPathCharacterSet + ')';
|
|||||||
var strictUrlRegex = new RegExp(start + protocolClause + bodyClause + end);
|
var strictUrlRegex = new RegExp(start + protocolClause + bodyClause + end);
|
||||||
var HYPERTEXT_LINK_MATCHER_ID = 0;
|
var HYPERTEXT_LINK_MATCHER_ID = 0;
|
||||||
var Linkifier = (function () {
|
var Linkifier = (function () {
|
||||||
function Linkifier(document, rows) {
|
function Linkifier() {
|
||||||
this._nextLinkMatcherId = HYPERTEXT_LINK_MATCHER_ID;
|
this._nextLinkMatcherId = HYPERTEXT_LINK_MATCHER_ID;
|
||||||
this._document = document;
|
|
||||||
this._rows = rows;
|
|
||||||
this._rowTimeoutIds = [];
|
this._rowTimeoutIds = [];
|
||||||
this._linkMatchers = [];
|
this._linkMatchers = [];
|
||||||
this.registerLinkMatcher(strictUrlRegex, null, { matchIndex: 1 });
|
this.registerLinkMatcher(strictUrlRegex, null, { matchIndex: 1 });
|
||||||
}
|
}
|
||||||
|
Linkifier.prototype.attachToDom = function (document, rows) {
|
||||||
|
this._document = document;
|
||||||
|
this._rows = rows;
|
||||||
|
};
|
||||||
Linkifier.prototype.linkifyRow = function (rowIndex) {
|
Linkifier.prototype.linkifyRow = function (rowIndex) {
|
||||||
|
if (!this._document) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
var timeoutId = this._rowTimeoutIds[rowIndex];
|
var timeoutId = this._rowTimeoutIds[rowIndex];
|
||||||
if (timeoutId) {
|
if (timeoutId) {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
}
|
}
|
||||||
this._rowTimeoutIds[rowIndex] = setTimeout(this._linkifyRow.bind(this, rowIndex), Linkifier.TIME_BEFORE_LINKIFY);
|
this._rowTimeoutIds[rowIndex] = setTimeout(this._linkifyRow.bind(this, rowIndex), Linkifier.TIME_BEFORE_LINKIFY);
|
||||||
};
|
};
|
||||||
Linkifier.prototype.attachHypertextLinkHandler = function (handler) {
|
Linkifier.prototype.setHypertextLinkHandler = function (handler) {
|
||||||
this._linkMatchers[HYPERTEXT_LINK_MATCHER_ID].handler = handler;
|
this._linkMatchers[HYPERTEXT_LINK_MATCHER_ID].handler = handler;
|
||||||
};
|
};
|
||||||
|
Linkifier.prototype.setHypertextValidationCallback = function (callback) {
|
||||||
|
this._linkMatchers[HYPERTEXT_LINK_MATCHER_ID].validationCallback = callback;
|
||||||
|
};
|
||||||
Linkifier.prototype.registerLinkMatcher = function (regex, handler, options) {
|
Linkifier.prototype.registerLinkMatcher = function (regex, handler, options) {
|
||||||
if (options === void 0) { options = {}; }
|
if (options === void 0) { options = {}; }
|
||||||
if (this._nextLinkMatcherId !== HYPERTEXT_LINK_MATCHER_ID && !handler) {
|
if (this._nextLinkMatcherId !== HYPERTEXT_LINK_MATCHER_ID && !handler) {
|
||||||
@@ -1397,35 +1419,42 @@ var Linkifier = (function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var text = row.textContent;
|
var text = row.textContent;
|
||||||
var _loop_1 = function (i) {
|
|
||||||
var matcher = this_1._linkMatchers[i];
|
|
||||||
var uri = this_1._findLinkMatch(text, matcher.regex, matcher.matchIndex);
|
|
||||||
if (uri) {
|
|
||||||
var linkElement_1 = this_1._doLinkifyRow(rowIndex, uri, matcher.handler, matcher.id === HYPERTEXT_LINK_MATCHER_ID);
|
|
||||||
if (linkElement_1 && matcher.validationCallback) {
|
|
||||||
matcher.validationCallback(uri, function (isValid) {
|
|
||||||
if (!isValid) {
|
|
||||||
linkElement_1.classList.add(INVALID_LINK_CLASS);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return { value: void 0 };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var this_1 = this;
|
|
||||||
for (var i = 0; i < this._linkMatchers.length; i++) {
|
for (var i = 0; i < this._linkMatchers.length; i++) {
|
||||||
var state_1 = _loop_1(i);
|
var matcher = this._linkMatchers[i];
|
||||||
if (typeof state_1 === "object")
|
var linkElements = this._doLinkifyRow(row, matcher);
|
||||||
return state_1.value;
|
if (linkElements.length > 0) {
|
||||||
|
if (matcher.validationCallback) {
|
||||||
|
var _loop_1 = function (j) {
|
||||||
|
var element = linkElements[j];
|
||||||
|
matcher.validationCallback(element.textContent, element, function (isValid) {
|
||||||
|
if (!isValid) {
|
||||||
|
element.classList.add(INVALID_LINK_CLASS);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
for (var j = 0; j < linkElements.length; j++) {
|
||||||
|
_loop_1(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Linkifier.prototype._doLinkifyRow = function (rowIndex, uri, handler, isHttpLinkMatcher) {
|
Linkifier.prototype._doLinkifyRow = function (row, matcher) {
|
||||||
var nodes = this._rows[rowIndex].childNodes;
|
var result = [];
|
||||||
|
var isHttpLinkMatcher = matcher.id === HYPERTEXT_LINK_MATCHER_ID;
|
||||||
|
var nodes = row.childNodes;
|
||||||
|
var match = row.textContent.match(matcher.regex);
|
||||||
|
if (!match || match.length === 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
var uri = match[typeof matcher.matchIndex !== 'number' ? 0 : matcher.matchIndex];
|
||||||
|
var rowStartIndex = match.index + uri.length;
|
||||||
for (var i = 0; i < nodes.length; i++) {
|
for (var i = 0; i < nodes.length; i++) {
|
||||||
var node = nodes[i];
|
var node = nodes[i];
|
||||||
var searchIndex = node.textContent.indexOf(uri);
|
var searchIndex = node.textContent.indexOf(uri);
|
||||||
if (searchIndex >= 0) {
|
if (searchIndex >= 0) {
|
||||||
var linkElement = this._createAnchorElement(uri, handler, isHttpLinkMatcher);
|
var linkElement = this._createAnchorElement(uri, matcher.handler, isHttpLinkMatcher);
|
||||||
if (node.textContent.length === uri.length) {
|
if (node.textContent.length === uri.length) {
|
||||||
if (node.nodeType === 3) {
|
if (node.nodeType === 3) {
|
||||||
this._replaceNode(node, linkElement);
|
this._replaceNode(node, linkElement);
|
||||||
@@ -1433,29 +1462,31 @@ var Linkifier = (function () {
|
|||||||
else {
|
else {
|
||||||
var element = node;
|
var element = node;
|
||||||
if (element.nodeName === 'A') {
|
if (element.nodeName === 'A') {
|
||||||
return;
|
return result;
|
||||||
}
|
}
|
||||||
element.innerHTML = '';
|
element.innerHTML = '';
|
||||||
element.appendChild(linkElement);
|
element.appendChild(linkElement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this._replaceNodeSubstringWithNode(node, linkElement, uri, searchIndex);
|
var nodesAdded = this._replaceNodeSubstringWithNode(node, linkElement, uri, searchIndex);
|
||||||
|
i += nodesAdded;
|
||||||
}
|
}
|
||||||
return linkElement;
|
result.push(linkElement);
|
||||||
|
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;
|
||||||
Linkifier.prototype._findLinkMatch = function (text, regex, matchIndex) {
|
|
||||||
var match = text.match(regex);
|
|
||||||
if (!match || match.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return match[typeof matchIndex !== 'number' ? 0 : matchIndex];
|
|
||||||
};
|
};
|
||||||
Linkifier.prototype._createAnchorElement = function (uri, handler, isHypertextLinkHandler) {
|
Linkifier.prototype._createAnchorElement = function (uri, handler, isHypertextLinkHandler) {
|
||||||
var element = this._document.createElement('a');
|
var element = this._document.createElement('a');
|
||||||
element.textContent = uri;
|
element.textContent = uri;
|
||||||
|
element.draggable = false;
|
||||||
if (isHypertextLinkHandler) {
|
if (isHypertextLinkHandler) {
|
||||||
element.href = uri;
|
element.href = uri;
|
||||||
element.target = '_blank';
|
element.target = '_blank';
|
||||||
@@ -1491,27 +1522,28 @@ var Linkifier = (function () {
|
|||||||
if (node.nodeType !== 3) {
|
if (node.nodeType !== 3) {
|
||||||
node = node.childNodes[0];
|
node = node.childNodes[0];
|
||||||
}
|
}
|
||||||
if (node.childNodes.length === 0 && node.nodeType !== Node.TEXT_NODE) {
|
if (node.childNodes.length === 0 && node.nodeType !== 3) {
|
||||||
throw new Error('targetNode must be a text node or only contain a single text node');
|
throw new Error('targetNode must be a text node or only contain a single text node');
|
||||||
}
|
}
|
||||||
var fullText = node.textContent;
|
var fullText = node.textContent;
|
||||||
if (substringIndex === 0) {
|
if (substringIndex === 0) {
|
||||||
var rightText = fullText.substring(substring.length);
|
var rightText_1 = fullText.substring(substring.length);
|
||||||
var rightTextNode = this._document.createTextNode(rightText);
|
var rightTextNode_1 = this._document.createTextNode(rightText_1);
|
||||||
this._replaceNode(node, newNode, rightTextNode);
|
this._replaceNode(node, newNode, rightTextNode_1);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
else if (substringIndex === targetNode.textContent.length - substring.length) {
|
if (substringIndex === targetNode.textContent.length - substring.length) {
|
||||||
var leftText = fullText.substring(0, substringIndex);
|
var leftText_1 = fullText.substring(0, substringIndex);
|
||||||
var leftTextNode = this._document.createTextNode(leftText);
|
var leftTextNode_1 = this._document.createTextNode(leftText_1);
|
||||||
this._replaceNode(node, leftTextNode, newNode);
|
this._replaceNode(node, leftTextNode_1, newNode);
|
||||||
}
|
return 0;
|
||||||
else {
|
|
||||||
var leftText = fullText.substring(0, substringIndex);
|
|
||||||
var leftTextNode = this._document.createTextNode(leftText);
|
|
||||||
var rightText = fullText.substring(substringIndex + substring.length);
|
|
||||||
var rightTextNode = this._document.createTextNode(rightText);
|
|
||||||
this._replaceNode(node, leftTextNode, newNode, rightTextNode);
|
|
||||||
}
|
}
|
||||||
|
var leftText = fullText.substring(0, substringIndex);
|
||||||
|
var leftTextNode = this._document.createTextNode(leftText);
|
||||||
|
var rightText = fullText.substring(substringIndex + substring.length);
|
||||||
|
var rightTextNode = this._document.createTextNode(rightText);
|
||||||
|
this._replaceNode(node, leftTextNode, newNode, rightTextNode);
|
||||||
|
return 1;
|
||||||
};
|
};
|
||||||
return Linkifier;
|
return Linkifier;
|
||||||
}());
|
}());
|
||||||
@@ -1522,6 +1554,7 @@ exports.Linkifier = Linkifier;
|
|||||||
|
|
||||||
},{}],7:[function(require,module,exports){
|
},{}],7:[function(require,module,exports){
|
||||||
"use strict";
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
var EscapeSequences_1 = require("./EscapeSequences");
|
var EscapeSequences_1 = require("./EscapeSequences");
|
||||||
var Charsets_1 = require("./Charsets");
|
var Charsets_1 = require("./Charsets");
|
||||||
var normalStateHandler = {};
|
var normalStateHandler = {};
|
||||||
@@ -1898,11 +1931,14 @@ var Parser = (function () {
|
|||||||
if (ch === EscapeSequences_1.C0.ESC || ch === EscapeSequences_1.C0.BEL) {
|
if (ch === EscapeSequences_1.C0.ESC || ch === EscapeSequences_1.C0.BEL) {
|
||||||
if (ch === EscapeSequences_1.C0.ESC)
|
if (ch === EscapeSequences_1.C0.ESC)
|
||||||
this._position++;
|
this._position++;
|
||||||
|
var pt = void 0;
|
||||||
|
var valid = void 0;
|
||||||
switch (this._terminal.prefix) {
|
switch (this._terminal.prefix) {
|
||||||
case '':
|
case '':
|
||||||
break;
|
break;
|
||||||
case '$q':
|
case '$q':
|
||||||
var pt = this._terminal.currentParam, valid = false;
|
pt = this._terminal.currentParam;
|
||||||
|
valid = false;
|
||||||
switch (pt) {
|
switch (pt) {
|
||||||
case '"q':
|
case '"q':
|
||||||
pt = '0"q';
|
pt = '0"q';
|
||||||
@@ -1930,8 +1966,8 @@ var Parser = (function () {
|
|||||||
case '+p':
|
case '+p':
|
||||||
break;
|
break;
|
||||||
case '+q':
|
case '+q':
|
||||||
pt = this._terminal.currentParam
|
pt = this._terminal.currentParam;
|
||||||
, valid = false;
|
valid = false;
|
||||||
this._terminal.send(EscapeSequences_1.C0.ESC + 'P' + +valid + '+r' + pt + EscapeSequences_1.C0.ESC + '\\');
|
this._terminal.send(EscapeSequences_1.C0.ESC + 'P' + +valid + '+r' + pt + EscapeSequences_1.C0.ESC + '\\');
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -1966,6 +2002,7 @@ var Parser = (function () {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return this._state;
|
||||||
};
|
};
|
||||||
Parser.prototype.setState = function (state) {
|
Parser.prototype.setState = function (state) {
|
||||||
this._state = state;
|
this._state = state;
|
||||||
@@ -1997,6 +2034,8 @@ exports.Parser = Parser;
|
|||||||
|
|
||||||
},{"./Charsets":1,"./EscapeSequences":3}],8:[function(require,module,exports){
|
},{"./Charsets":1,"./EscapeSequences":3}],8:[function(require,module,exports){
|
||||||
"use strict";
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
var DomElementObjectPool_1 = require("./utils/DomElementObjectPool");
|
||||||
var MAX_REFRESH_FRAME_SKIP = 5;
|
var MAX_REFRESH_FRAME_SKIP = 5;
|
||||||
var FLAGS;
|
var FLAGS;
|
||||||
(function (FLAGS) {
|
(function (FLAGS) {
|
||||||
@@ -2014,9 +2053,11 @@ var Renderer = (function () {
|
|||||||
this._refreshRowsQueue = [];
|
this._refreshRowsQueue = [];
|
||||||
this._refreshFramesSkipped = 0;
|
this._refreshFramesSkipped = 0;
|
||||||
this._refreshAnimationFrame = null;
|
this._refreshAnimationFrame = null;
|
||||||
|
this._spanElementObjectPool = new DomElementObjectPool_1.DomElementObjectPool('span');
|
||||||
if (brokenBold === null) {
|
if (brokenBold === null) {
|
||||||
brokenBold = checkBoldBroken(this._terminal.document);
|
brokenBold = checkBoldBroken(this._terminal.element);
|
||||||
}
|
}
|
||||||
|
this._spanElementObjectPool = new DomElementObjectPool_1.DomElementObjectPool('span');
|
||||||
}
|
}
|
||||||
Renderer.prototype.queueRefresh = function (start, end) {
|
Renderer.prototype.queueRefresh = function (start, end) {
|
||||||
this._refreshRowsQueue.push({ start: start, end: end });
|
this._refreshRowsQueue.push({ start: start, end: end });
|
||||||
@@ -2054,80 +2095,102 @@ var Renderer = (function () {
|
|||||||
this._refresh(start, end);
|
this._refresh(start, end);
|
||||||
};
|
};
|
||||||
Renderer.prototype._refresh = function (start, end) {
|
Renderer.prototype._refresh = function (start, end) {
|
||||||
var x, y, i, line, out, ch, ch_width, width, data, attr, bg, fg, flags, row, parent, focused = document.activeElement;
|
var parent;
|
||||||
if (end - start >= this._terminal.rows / 2) {
|
if (end - start >= this._terminal.rows / 2) {
|
||||||
parent = this._terminal.element.parentNode;
|
parent = this._terminal.element.parentNode;
|
||||||
if (parent) {
|
if (parent) {
|
||||||
this._terminal.element.removeChild(this._terminal.rowContainer);
|
this._terminal.element.removeChild(this._terminal.rowContainer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
width = this._terminal.cols;
|
var width = this._terminal.cols;
|
||||||
y = start;
|
var y = start;
|
||||||
if (end >= this._terminal.rows) {
|
if (end >= this._terminal.rows) {
|
||||||
this._terminal.log('`end` is too large. Most likely a bad CSR.');
|
this._terminal.log('`end` is too large. Most likely a bad CSR.');
|
||||||
end = this._terminal.rows - 1;
|
end = this._terminal.rows - 1;
|
||||||
}
|
}
|
||||||
for (; y <= end; y++) {
|
for (; y <= end; y++) {
|
||||||
row = y + this._terminal.ydisp;
|
var row = y + this._terminal.ydisp;
|
||||||
line = this._terminal.lines.get(row);
|
var line = this._terminal.lines.get(row);
|
||||||
if (!line || !this._terminal.children[y]) {
|
var x = void 0;
|
||||||
continue;
|
if (this._terminal.y === y - (this._terminal.ybase - this._terminal.ydisp) &&
|
||||||
}
|
this._terminal.cursorState &&
|
||||||
out = '';
|
!this._terminal.cursorHidden) {
|
||||||
if (this._terminal.y === y - (this._terminal.ybase - this._terminal.ydisp)
|
|
||||||
&& this._terminal.cursorState
|
|
||||||
&& !this._terminal.cursorHidden) {
|
|
||||||
x = this._terminal.x;
|
x = this._terminal.x;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
x = -1;
|
x = -1;
|
||||||
}
|
}
|
||||||
attr = this._terminal.defAttr;
|
var attr = this._terminal.defAttr;
|
||||||
i = 0;
|
var documentFragment = document.createDocumentFragment();
|
||||||
for (; i < width; i++) {
|
var innerHTML = '';
|
||||||
if (!line[i]) {
|
var currentElement = void 0;
|
||||||
|
while (this._terminal.children[y].children.length) {
|
||||||
|
var child = this._terminal.children[y].children[0];
|
||||||
|
this._terminal.children[y].removeChild(child);
|
||||||
|
this._spanElementObjectPool.release(child);
|
||||||
|
}
|
||||||
|
for (var i = 0; i < width; i++) {
|
||||||
|
var data = line[i][0];
|
||||||
|
var ch = line[i][1];
|
||||||
|
var ch_width = line[i][2];
|
||||||
|
if (!ch_width) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
data = line[i][0];
|
if (i === x) {
|
||||||
ch = line[i][1];
|
|
||||||
ch_width = line[i][2];
|
|
||||||
if (!ch_width)
|
|
||||||
continue;
|
|
||||||
if (i === x)
|
|
||||||
data = -1;
|
data = -1;
|
||||||
|
}
|
||||||
if (data !== attr) {
|
if (data !== attr) {
|
||||||
if (attr !== this._terminal.defAttr) {
|
if (attr !== this._terminal.defAttr) {
|
||||||
out += '</span>';
|
if (innerHTML) {
|
||||||
|
currentElement.innerHTML = innerHTML;
|
||||||
|
innerHTML = '';
|
||||||
|
}
|
||||||
|
documentFragment.appendChild(currentElement);
|
||||||
|
currentElement = null;
|
||||||
}
|
}
|
||||||
if (data !== this._terminal.defAttr) {
|
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) {
|
if (data === -1) {
|
||||||
out += '<span class="reverse-video terminal-cursor">';
|
currentElement.classList.add('reverse-video', 'terminal-cursor');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var classNames = [];
|
var bg = data & 0x1ff;
|
||||||
bg = data & 0x1ff;
|
var fg = (data >> 9) & 0x1ff;
|
||||||
fg = (data >> 9) & 0x1ff;
|
var flags = data >> 18;
|
||||||
flags = data >> 18;
|
|
||||||
if (flags & FLAGS.BOLD) {
|
if (flags & FLAGS.BOLD) {
|
||||||
if (!brokenBold) {
|
if (!brokenBold) {
|
||||||
classNames.push('xterm-bold');
|
currentElement.classList.add('xterm-bold');
|
||||||
}
|
}
|
||||||
if (fg < 8)
|
if (fg < 8) {
|
||||||
fg += 8;
|
fg += 8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (flags & FLAGS.UNDERLINE) {
|
if (flags & FLAGS.UNDERLINE) {
|
||||||
classNames.push('xterm-underline');
|
currentElement.classList.add('xterm-underline');
|
||||||
}
|
}
|
||||||
if (flags & FLAGS.BLINK) {
|
if (flags & FLAGS.BLINK) {
|
||||||
classNames.push('xterm-blink');
|
currentElement.classList.add('xterm-blink');
|
||||||
}
|
}
|
||||||
if (flags & FLAGS.INVERSE) {
|
if (flags & FLAGS.INVERSE) {
|
||||||
bg = [fg, fg = bg][0];
|
var temp = bg;
|
||||||
if ((flags & 1) && fg < 8)
|
bg = fg;
|
||||||
|
fg = temp;
|
||||||
|
if ((flags & 1) && fg < 8) {
|
||||||
fg += 8;
|
fg += 8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (flags & FLAGS.INVISIBLE) {
|
if (flags & FLAGS.INVISIBLE) {
|
||||||
classNames.push('xterm-hidden');
|
currentElement.classList.add('xterm-hidden');
|
||||||
}
|
}
|
||||||
if (flags & FLAGS.INVERSE) {
|
if (flags & FLAGS.INVERSE) {
|
||||||
if (bg === 257) {
|
if (bg === 257) {
|
||||||
@@ -2138,50 +2201,55 @@ var Renderer = (function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (bg < 256) {
|
if (bg < 256) {
|
||||||
classNames.push('xterm-bg-color-' + bg);
|
currentElement.classList.add("xterm-bg-color-" + bg);
|
||||||
}
|
}
|
||||||
if (fg < 256) {
|
if (fg < 256) {
|
||||||
classNames.push('xterm-color-' + fg);
|
currentElement.classList.add("xterm-color-" + fg);
|
||||||
}
|
}
|
||||||
out += '<span';
|
|
||||||
if (classNames.length) {
|
|
||||||
out += ' class="' + classNames.join(' ') + '"';
|
|
||||||
}
|
|
||||||
out += '>';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ch_width === 2) {
|
if (ch_width === 2) {
|
||||||
out += '<span class="xterm-wide-char">';
|
innerHTML += "<span class=\"xterm-wide-char\">" + ch + "</span>";
|
||||||
}
|
}
|
||||||
switch (ch) {
|
else if (ch.charCodeAt(0) > 255) {
|
||||||
case '&':
|
innerHTML += "<span class=\"xterm-normal-char\">" + ch + "</span>";
|
||||||
out += '&';
|
|
||||||
break;
|
|
||||||
case '<':
|
|
||||||
out += '<';
|
|
||||||
break;
|
|
||||||
case '>':
|
|
||||||
out += '>';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (ch <= ' ') {
|
|
||||||
out += ' ';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
out += ch;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (ch_width === 2) {
|
else {
|
||||||
out += '</span>';
|
switch (ch) {
|
||||||
|
case '&':
|
||||||
|
innerHTML += '&';
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
innerHTML += '<';
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
innerHTML += '>';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (ch <= ' ') {
|
||||||
|
innerHTML += ' ';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
innerHTML += ch;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
attr = data;
|
attr = data;
|
||||||
}
|
}
|
||||||
if (attr !== this._terminal.defAttr) {
|
if (innerHTML && !currentElement) {
|
||||||
out += '</span>';
|
currentElement = this._spanElementObjectPool.acquire();
|
||||||
}
|
}
|
||||||
this._terminal.children[y].innerHTML = out;
|
if (currentElement) {
|
||||||
|
if (innerHTML) {
|
||||||
|
currentElement.innerHTML = innerHTML;
|
||||||
|
innerHTML = '';
|
||||||
|
}
|
||||||
|
documentFragment.appendChild(currentElement);
|
||||||
|
currentElement = null;
|
||||||
|
}
|
||||||
|
this._terminal.children[y].appendChild(documentFragment);
|
||||||
}
|
}
|
||||||
if (parent) {
|
if (parent) {
|
||||||
this._terminal.element.appendChild(this._terminal.rowContainer);
|
this._terminal.element.appendChild(this._terminal.rowContainer);
|
||||||
@@ -2192,22 +2260,25 @@ var Renderer = (function () {
|
|||||||
return Renderer;
|
return Renderer;
|
||||||
}());
|
}());
|
||||||
exports.Renderer = Renderer;
|
exports.Renderer = Renderer;
|
||||||
function checkBoldBroken(document) {
|
function checkBoldBroken(terminal) {
|
||||||
var body = document.getElementsByTagName('body')[0];
|
var document = terminal.ownerDocument;
|
||||||
var el = document.createElement('span');
|
var el = document.createElement('span');
|
||||||
el.innerHTML = 'hello world';
|
el.innerHTML = 'hello world';
|
||||||
body.appendChild(el);
|
terminal.appendChild(el);
|
||||||
var w1 = el.scrollWidth;
|
var w1 = el.offsetWidth;
|
||||||
|
var h1 = el.offsetHeight;
|
||||||
el.style.fontWeight = 'bold';
|
el.style.fontWeight = 'bold';
|
||||||
var w2 = el.scrollWidth;
|
var w2 = el.offsetWidth;
|
||||||
body.removeChild(el);
|
var h2 = el.offsetHeight;
|
||||||
return w1 !== w2;
|
terminal.removeChild(el);
|
||||||
|
return w1 !== w2 || h1 !== h2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{}],9:[function(require,module,exports){
|
},{"./utils/DomElementObjectPool":14}],9:[function(require,module,exports){
|
||||||
"use strict";
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
var Viewport = (function () {
|
var Viewport = (function () {
|
||||||
function Viewport(terminal, viewportElement, scrollArea, charMeasure) {
|
function Viewport(terminal, viewportElement, scrollArea, charMeasure) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
@@ -2285,6 +2356,7 @@ exports.Viewport = Viewport;
|
|||||||
|
|
||||||
},{}],10:[function(require,module,exports){
|
},{}],10:[function(require,module,exports){
|
||||||
"use strict";
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
function prepareTextForClipboard(text) {
|
function prepareTextForClipboard(text) {
|
||||||
var space = String.fromCharCode(32), nonBreakingSpace = String.fromCharCode(160), allNonBreakingSpaces = new RegExp(nonBreakingSpace, 'g'), processedText = text.split('\n').map(function (line) {
|
var space = String.fromCharCode(32), nonBreakingSpace = String.fromCharCode(160), allNonBreakingSpaces = new RegExp(nonBreakingSpace, 'g'), processedText = text.split('\n').map(function (line) {
|
||||||
var processedLine = line.replace(/\s+$/g, '').replace(allNonBreakingSpaces, space);
|
var processedLine = line.replace(/\s+$/g, '').replace(allNonBreakingSpaces, space);
|
||||||
@@ -2293,6 +2365,13 @@ function prepareTextForClipboard(text) {
|
|||||||
return processedText;
|
return processedText;
|
||||||
}
|
}
|
||||||
exports.prepareTextForClipboard = prepareTextForClipboard;
|
exports.prepareTextForClipboard = prepareTextForClipboard;
|
||||||
|
function prepareTextForTerminal(text, isMSWindows) {
|
||||||
|
if (isMSWindows) {
|
||||||
|
return text.replace(/\r?\n/g, '\n');
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
exports.prepareTextForTerminal = prepareTextForTerminal;
|
||||||
function copyHandler(ev, term) {
|
function copyHandler(ev, term) {
|
||||||
var copiedText = window.getSelection().toString(), text = prepareTextForClipboard(copiedText);
|
var copiedText = window.getSelection().toString(), text = prepareTextForClipboard(copiedText);
|
||||||
if (term.browser.isMSIE) {
|
if (term.browser.isMSIE) {
|
||||||
@@ -2308,8 +2387,10 @@ function pasteHandler(ev, term) {
|
|||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
var text;
|
var text;
|
||||||
var dispatchPaste = function (text) {
|
var dispatchPaste = function (text) {
|
||||||
|
text = prepareTextForTerminal(text, term.browser.isMSWindows);
|
||||||
term.handler(text);
|
term.handler(text);
|
||||||
term.textarea.value = '';
|
term.textarea.value = '';
|
||||||
|
term.emit('paste', text);
|
||||||
return term.cancel(ev);
|
return term.cancel(ev);
|
||||||
};
|
};
|
||||||
if (term.browser.isMSIE) {
|
if (term.browser.isMSIE) {
|
||||||
@@ -2366,6 +2447,7 @@ exports.rightClickHandler = rightClickHandler;
|
|||||||
|
|
||||||
},{}],11:[function(require,module,exports){
|
},{}],11:[function(require,module,exports){
|
||||||
"use strict";
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
var Generic_1 = require("./Generic");
|
var Generic_1 = require("./Generic");
|
||||||
var isNode = (typeof navigator === 'undefined') ? true : false;
|
var isNode = (typeof navigator === 'undefined') ? true : false;
|
||||||
var userAgent = (isNode) ? 'node' : navigator.userAgent;
|
var userAgent = (isNode) ? 'node' : navigator.userAgent;
|
||||||
@@ -2379,13 +2461,19 @@ exports.isMSWindows = Generic_1.contains(['Windows', 'Win16', 'Win32', 'WinCE'],
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{"./Generic":14}],12:[function(require,module,exports){
|
},{"./Generic":15}],12:[function(require,module,exports){
|
||||||
"use strict";
|
"use strict";
|
||||||
var __extends = (this && this.__extends) || function (d, b) {
|
var __extends = (this && this.__extends) || (function () {
|
||||||
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
var extendStatics = Object.setPrototypeOf ||
|
||||||
function __() { this.constructor = d; }
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
||||||
};
|
return function (d, b) {
|
||||||
|
extendStatics(d, b);
|
||||||
|
function __() { this.constructor = d; }
|
||||||
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
var EventEmitter_js_1 = require("../EventEmitter.js");
|
var EventEmitter_js_1 = require("../EventEmitter.js");
|
||||||
var CharMeasure = (function (_super) {
|
var CharMeasure = (function (_super) {
|
||||||
__extends(CharMeasure, _super);
|
__extends(CharMeasure, _super);
|
||||||
@@ -2444,6 +2532,7 @@ exports.CharMeasure = CharMeasure;
|
|||||||
|
|
||||||
},{"../EventEmitter.js":4}],13:[function(require,module,exports){
|
},{"../EventEmitter.js":4}],13:[function(require,module,exports){
|
||||||
"use strict";
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
var CircularList = (function () {
|
var CircularList = (function () {
|
||||||
function CircularList(maxLength) {
|
function CircularList(maxLength) {
|
||||||
this._array = new Array(maxLength);
|
this._array = new Array(maxLength);
|
||||||
@@ -2582,6 +2671,54 @@ exports.CircularList = CircularList;
|
|||||||
|
|
||||||
},{}],14:[function(require,module,exports){
|
},{}],14:[function(require,module,exports){
|
||||||
"use strict";
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
var DomElementObjectPool = (function () {
|
||||||
|
function DomElementObjectPool(type) {
|
||||||
|
this.type = type;
|
||||||
|
this._type = type;
|
||||||
|
this._pool = [];
|
||||||
|
this._inUse = {};
|
||||||
|
}
|
||||||
|
DomElementObjectPool.prototype.acquire = function () {
|
||||||
|
var element;
|
||||||
|
if (this._pool.length === 0) {
|
||||||
|
element = this._createNew();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
element = this._pool.pop();
|
||||||
|
}
|
||||||
|
this._inUse[element.getAttribute(DomElementObjectPool.OBJECT_ID_ATTRIBUTE)] = element;
|
||||||
|
return element;
|
||||||
|
};
|
||||||
|
DomElementObjectPool.prototype.release = function (element) {
|
||||||
|
if (!this._inUse[element.getAttribute(DomElementObjectPool.OBJECT_ID_ATTRIBUTE)]) {
|
||||||
|
throw new Error('Could not release an element not yet acquired');
|
||||||
|
}
|
||||||
|
delete this._inUse[element.getAttribute(DomElementObjectPool.OBJECT_ID_ATTRIBUTE)];
|
||||||
|
this._cleanElement(element);
|
||||||
|
this._pool.push(element);
|
||||||
|
};
|
||||||
|
DomElementObjectPool.prototype._createNew = function () {
|
||||||
|
var element = document.createElement(this._type);
|
||||||
|
var id = DomElementObjectPool._objectCount++;
|
||||||
|
element.setAttribute(DomElementObjectPool.OBJECT_ID_ATTRIBUTE, id.toString(10));
|
||||||
|
return element;
|
||||||
|
};
|
||||||
|
DomElementObjectPool.prototype._cleanElement = function (element) {
|
||||||
|
element.className = '';
|
||||||
|
element.innerHTML = '';
|
||||||
|
};
|
||||||
|
return DomElementObjectPool;
|
||||||
|
}());
|
||||||
|
DomElementObjectPool.OBJECT_ID_ATTRIBUTE = 'data-obj-id';
|
||||||
|
DomElementObjectPool._objectCount = 0;
|
||||||
|
exports.DomElementObjectPool = DomElementObjectPool;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},{}],15:[function(require,module,exports){
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
function contains(arr, el) {
|
function contains(arr, el) {
|
||||||
return arr.indexOf(el) >= 0;
|
return arr.indexOf(el) >= 0;
|
||||||
}
|
}
|
||||||
@@ -2590,8 +2727,43 @@ exports.contains = contains;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{}],15:[function(require,module,exports){
|
},{}],16:[function(require,module,exports){
|
||||||
"use strict";
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
function getCoords(event, rowContainer, charMeasure) {
|
||||||
|
if (event.pageX == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var x = event.pageX;
|
||||||
|
var y = event.pageY;
|
||||||
|
var el = rowContainer;
|
||||||
|
while (el && el !== self.document.documentElement) {
|
||||||
|
x -= el.offsetLeft;
|
||||||
|
y -= el.offsetTop;
|
||||||
|
el = 'offsetParent' in el ? el.offsetParent : el.parentElement;
|
||||||
|
}
|
||||||
|
x = Math.ceil(x / charMeasure.width);
|
||||||
|
y = Math.ceil(y / charMeasure.height);
|
||||||
|
return [x, y];
|
||||||
|
}
|
||||||
|
exports.getCoords = getCoords;
|
||||||
|
function getRawByteCoords(event, rowContainer, charMeasure, colCount, rowCount) {
|
||||||
|
var coords = getCoords(event, rowContainer, charMeasure);
|
||||||
|
var x = coords[0];
|
||||||
|
var y = coords[1];
|
||||||
|
x = Math.min(Math.max(x, 0), colCount);
|
||||||
|
y = Math.min(Math.max(y, 0), rowCount);
|
||||||
|
x += 32;
|
||||||
|
y += 32;
|
||||||
|
return { x: x, y: y };
|
||||||
|
}
|
||||||
|
exports.getRawByteCoords = getRawByteCoords;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},{}],17:[function(require,module,exports){
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
var CompositionHelper_1 = require("./CompositionHelper");
|
var CompositionHelper_1 = require("./CompositionHelper");
|
||||||
var EventEmitter_1 = require("./EventEmitter");
|
var EventEmitter_1 = require("./EventEmitter");
|
||||||
var Viewport_1 = require("./Viewport");
|
var Viewport_1 = require("./Viewport");
|
||||||
@@ -2604,9 +2776,11 @@ var Renderer_1 = require("./Renderer");
|
|||||||
var Linkifier_1 = require("./Linkifier");
|
var Linkifier_1 = require("./Linkifier");
|
||||||
var CharMeasure_1 = require("./utils/CharMeasure");
|
var CharMeasure_1 = require("./utils/CharMeasure");
|
||||||
var Browser = require("./utils/Browser");
|
var Browser = require("./utils/Browser");
|
||||||
|
var Mouse_1 = require("./utils/Mouse");
|
||||||
var document = (typeof window != 'undefined') ? window.document : null;
|
var document = (typeof window != 'undefined') ? window.document : null;
|
||||||
var WRITE_BUFFER_PAUSE_THRESHOLD = 5;
|
var WRITE_BUFFER_PAUSE_THRESHOLD = 5;
|
||||||
var WRITE_BATCH_SIZE = 300;
|
var WRITE_BATCH_SIZE = 300;
|
||||||
|
var CURSOR_BLINK_INTERVAL = 600;
|
||||||
function Terminal(options) {
|
function Terminal(options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
if (!(this instanceof Terminal)) {
|
if (!(this instanceof Terminal)) {
|
||||||
@@ -2664,6 +2838,7 @@ function Terminal(options) {
|
|||||||
this.scrollTop = 0;
|
this.scrollTop = 0;
|
||||||
this.scrollBottom = this.rows - 1;
|
this.scrollBottom = this.rows - 1;
|
||||||
this.customKeydownHandler = null;
|
this.customKeydownHandler = null;
|
||||||
|
this.cursorBlinkInterval = null;
|
||||||
this.applicationKeypad = false;
|
this.applicationKeypad = false;
|
||||||
this.applicationCursor = false;
|
this.applicationCursor = false;
|
||||||
this.originMode = false;
|
this.originMode = false;
|
||||||
@@ -2702,8 +2877,7 @@ function Terminal(options) {
|
|||||||
this.inputHandler = new InputHandler_1.InputHandler(this);
|
this.inputHandler = new InputHandler_1.InputHandler(this);
|
||||||
this.parser = new Parser_1.Parser(this.inputHandler, this);
|
this.parser = new Parser_1.Parser(this.inputHandler, this);
|
||||||
this.renderer = this.renderer || null;
|
this.renderer = this.renderer || null;
|
||||||
this.linkifier = this.linkifier || null;
|
this.linkifier = this.linkifier || new Linkifier_1.Linkifier();
|
||||||
;
|
|
||||||
this.writeBuffer = [];
|
this.writeBuffer = [];
|
||||||
this.writeInProgress = false;
|
this.writeInProgress = false;
|
||||||
this.xoffSentToCatchUp = false;
|
this.xoffSentToCatchUp = false;
|
||||||
@@ -2835,7 +3009,7 @@ Terminal.prototype.setOption = function (key, value) {
|
|||||||
this.options[key] = value;
|
this.options[key] = value;
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'cursorBlink':
|
case 'cursorBlink':
|
||||||
this.element.classList.toggle('xterm-cursor-blink', value);
|
this.setCursorBlinking(value);
|
||||||
break;
|
break;
|
||||||
case 'cursorStyle':
|
case 'cursorStyle':
|
||||||
this.element.classList.toggle("xterm-cursor-style-underline", value === 'underline');
|
this.element.classList.toggle("xterm-cursor-style-underline", value === 'underline');
|
||||||
@@ -2846,6 +3020,26 @@ Terminal.prototype.setOption = function (key, value) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Terminal.prototype.restartCursorBlinking = function () {
|
||||||
|
this.setCursorBlinking(this.options.cursorBlink);
|
||||||
|
};
|
||||||
|
Terminal.prototype.setCursorBlinking = function (enabled) {
|
||||||
|
this.element.classList.toggle('xterm-cursor-blink', enabled);
|
||||||
|
this.clearCursorBlinkingInterval();
|
||||||
|
if (enabled) {
|
||||||
|
var self = this;
|
||||||
|
this.cursorBlinkInterval = setInterval(function () {
|
||||||
|
self.element.classList.toggle('xterm-cursor-blink-on');
|
||||||
|
}, CURSOR_BLINK_INTERVAL);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Terminal.prototype.clearCursorBlinkingInterval = function () {
|
||||||
|
this.element.classList.remove('xterm-cursor-blink-on');
|
||||||
|
if (this.cursorBlinkInterval) {
|
||||||
|
clearInterval(this.cursorBlinkInterval);
|
||||||
|
this.cursorBlinkInterval = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
Terminal.bindFocus = function (term) {
|
Terminal.bindFocus = function (term) {
|
||||||
on(term.textarea, 'focus', function (ev) {
|
on(term.textarea, 'focus', function (ev) {
|
||||||
if (term.sendFocus) {
|
if (term.sendFocus) {
|
||||||
@@ -2853,6 +3047,7 @@ Terminal.bindFocus = function (term) {
|
|||||||
}
|
}
|
||||||
term.element.classList.add('focus');
|
term.element.classList.add('focus');
|
||||||
term.showCursor();
|
term.showCursor();
|
||||||
|
term.restartCursorBlinking.apply(term);
|
||||||
Terminal.focus = term;
|
Terminal.focus = term;
|
||||||
term.emit('focus', { terminal: term });
|
term.emit('focus', { terminal: term });
|
||||||
});
|
});
|
||||||
@@ -2867,6 +3062,7 @@ Terminal.bindBlur = function (term) {
|
|||||||
term.send(EscapeSequences_1.C0.ESC + '[O');
|
term.send(EscapeSequences_1.C0.ESC + '[O');
|
||||||
}
|
}
|
||||||
term.element.classList.remove('focus');
|
term.element.classList.remove('focus');
|
||||||
|
term.clearCursorBlinkingInterval.apply(term);
|
||||||
Terminal.focus = null;
|
Terminal.focus = null;
|
||||||
term.emit('blur', { terminal: term });
|
term.emit('blur', { terminal: term });
|
||||||
});
|
});
|
||||||
@@ -2940,7 +3136,7 @@ Terminal.prototype.insertRow = function (row) {
|
|||||||
this.children.push(row);
|
this.children.push(row);
|
||||||
return row;
|
return row;
|
||||||
};
|
};
|
||||||
Terminal.prototype.open = function (parent) {
|
Terminal.prototype.open = function (parent, focus) {
|
||||||
var self = this, i = 0, div;
|
var self = this, i = 0, div;
|
||||||
this.parent = parent || this.parent;
|
this.parent = parent || this.parent;
|
||||||
if (!this.parent) {
|
if (!this.parent) {
|
||||||
@@ -2953,7 +3149,7 @@ Terminal.prototype.open = function (parent) {
|
|||||||
this.element.classList.add('terminal');
|
this.element.classList.add('terminal');
|
||||||
this.element.classList.add('xterm');
|
this.element.classList.add('xterm');
|
||||||
this.element.classList.add('xterm-theme-' + this.theme);
|
this.element.classList.add('xterm-theme-' + this.theme);
|
||||||
this.element.classList.toggle('xterm-cursor-blink', this.options.cursorBlink);
|
this.setCursorBlinking(this.options.cursorBlink);
|
||||||
this.element.style.height;
|
this.element.style.height;
|
||||||
this.element.setAttribute('tabindex', 0);
|
this.element.setAttribute('tabindex', 0);
|
||||||
this.viewportElement = document.createElement('div');
|
this.viewportElement = document.createElement('div');
|
||||||
@@ -2966,7 +3162,7 @@ Terminal.prototype.open = function (parent) {
|
|||||||
this.rowContainer.classList.add('xterm-rows');
|
this.rowContainer.classList.add('xterm-rows');
|
||||||
this.element.appendChild(this.rowContainer);
|
this.element.appendChild(this.rowContainer);
|
||||||
this.children = [];
|
this.children = [];
|
||||||
this.linkifier = new Linkifier_1.Linkifier(document, this.children);
|
this.linkifier.attachToDom(document, this.children);
|
||||||
this.helperContainer = document.createElement('div');
|
this.helperContainer = document.createElement('div');
|
||||||
this.helperContainer.classList.add('xterm-helpers');
|
this.helperContainer.classList.add('xterm-helpers');
|
||||||
this.element.appendChild(this.helperContainer);
|
this.element.appendChild(this.helperContainer);
|
||||||
@@ -3002,7 +3198,16 @@ Terminal.prototype.open = function (parent) {
|
|||||||
this.renderer = new Renderer_1.Renderer(this);
|
this.renderer = new Renderer_1.Renderer(this);
|
||||||
this.refresh(0, this.rows - 1);
|
this.refresh(0, this.rows - 1);
|
||||||
this.initGlobal();
|
this.initGlobal();
|
||||||
this.focus();
|
if (typeof focus == 'undefined') {
|
||||||
|
var message = 'You did not pass the `focus` argument in `Terminal.prototype.open()`.\n';
|
||||||
|
message += 'The `focus` argument now defaults to `true` but starting with xterm.js 3.0 ';
|
||||||
|
message += 'it will default to `false`.';
|
||||||
|
console.warn(message);
|
||||||
|
focus = true;
|
||||||
|
}
|
||||||
|
if (focus) {
|
||||||
|
this.focus();
|
||||||
|
}
|
||||||
on(this.element, 'click', function () {
|
on(this.element, 'click', function () {
|
||||||
var selection = document.getSelection(), collapsed = selection.isCollapsed, isRange = typeof collapsed == 'boolean' ? !collapsed : selection.type == 'Range';
|
var selection = document.getSelection(), collapsed = selection.isCollapsed, isRange = typeof collapsed == 'boolean' ? !collapsed : selection.type == 'Range';
|
||||||
if (!isRange) {
|
if (!isRange) {
|
||||||
@@ -3025,14 +3230,16 @@ Terminal.loadAddon = function (addon, callback) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
Terminal.prototype.updateCharSizeCSS = function () {
|
Terminal.prototype.updateCharSizeCSS = function () {
|
||||||
this.charSizeStyleElement.textContent = '.xterm-wide-char{width:' + (this.charMeasure.width * 2) + 'px;}';
|
this.charSizeStyleElement.textContent =
|
||||||
|
".xterm-wide-char{width:" + this.charMeasure.width * 2 + "px;}" +
|
||||||
|
(".xterm-normal-char{width:" + this.charMeasure.width + "px;}");
|
||||||
};
|
};
|
||||||
Terminal.prototype.bindMouse = function () {
|
Terminal.prototype.bindMouse = function () {
|
||||||
var el = this.element, self = this, pressed = 32;
|
var el = this.element, self = this, pressed = 32;
|
||||||
function sendButton(ev) {
|
function sendButton(ev) {
|
||||||
var button, pos;
|
var button, pos;
|
||||||
button = getButton(ev);
|
button = getButton(ev);
|
||||||
pos = getCoords(ev);
|
pos = Mouse_1.getRawByteCoords(ev, self.rowContainer, self.charMeasure, self.cols, self.rows);
|
||||||
if (!pos)
|
if (!pos)
|
||||||
return;
|
return;
|
||||||
sendEvent(button, pos);
|
sendEvent(button, pos);
|
||||||
@@ -3049,7 +3256,7 @@ Terminal.prototype.bindMouse = function () {
|
|||||||
}
|
}
|
||||||
function sendMove(ev) {
|
function sendMove(ev) {
|
||||||
var button = pressed, pos;
|
var button = pressed, pos;
|
||||||
pos = getCoords(ev);
|
pos = Mouse_1.getRawByteCoords(ev, self.rowContainer, self.charMeasure, self.cols, self.rows);
|
||||||
if (!pos)
|
if (!pos)
|
||||||
return;
|
return;
|
||||||
button += 32;
|
button += 32;
|
||||||
@@ -3188,38 +3395,6 @@ Terminal.prototype.bindMouse = function () {
|
|||||||
button = (32 + (mod << 2)) + button;
|
button = (32 + (mod << 2)) + button;
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
function getCoords(ev) {
|
|
||||||
var x, y, w, h, el;
|
|
||||||
if (ev.pageX == null)
|
|
||||||
return;
|
|
||||||
x = ev.pageX;
|
|
||||||
y = ev.pageY;
|
|
||||||
el = self.element;
|
|
||||||
while (el && el !== self.document.documentElement) {
|
|
||||||
x -= el.offsetLeft;
|
|
||||||
y -= el.offsetTop;
|
|
||||||
el = 'offsetParent' in el
|
|
||||||
? el.offsetParent
|
|
||||||
: el.parentNode;
|
|
||||||
}
|
|
||||||
x = Math.ceil(x / self.charMeasure.width);
|
|
||||||
y = Math.ceil(y / self.charMeasure.height);
|
|
||||||
if (x < 0)
|
|
||||||
x = 0;
|
|
||||||
if (x > self.cols)
|
|
||||||
x = self.cols;
|
|
||||||
if (y < 0)
|
|
||||||
y = 0;
|
|
||||||
if (y > self.rows)
|
|
||||||
y = self.rows;
|
|
||||||
x += 32;
|
|
||||||
y += 32;
|
|
||||||
return {
|
|
||||||
x: x,
|
|
||||||
y: y,
|
|
||||||
type: 'wheel'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
on(el, 'mousedown', function (ev) {
|
on(el, 'mousedown', function (ev) {
|
||||||
if (!self.mouseEvents)
|
if (!self.mouseEvents)
|
||||||
return;
|
return;
|
||||||
@@ -3375,7 +3550,8 @@ Terminal.prototype.innerWrite = function () {
|
|||||||
}
|
}
|
||||||
this.refreshStart = this.y;
|
this.refreshStart = this.y;
|
||||||
this.refreshEnd = this.y;
|
this.refreshEnd = this.y;
|
||||||
this.parser.parse(data);
|
var state = this.parser.parse(data);
|
||||||
|
this.parser.setState(state);
|
||||||
this.updateRange(this.y);
|
this.updateRange(this.y);
|
||||||
this.refresh(this.refreshStart, this.refreshEnd);
|
this.refresh(this.refreshStart, this.refreshEnd);
|
||||||
}
|
}
|
||||||
@@ -3395,11 +3571,18 @@ Terminal.prototype.writeln = function (data) {
|
|||||||
Terminal.prototype.attachCustomKeydownHandler = function (customKeydownHandler) {
|
Terminal.prototype.attachCustomKeydownHandler = function (customKeydownHandler) {
|
||||||
this.customKeydownHandler = customKeydownHandler;
|
this.customKeydownHandler = customKeydownHandler;
|
||||||
};
|
};
|
||||||
Terminal.prototype.attachHypertextLinkHandler = function (handler) {
|
Terminal.prototype.setHypertextLinkHandler = function (handler) {
|
||||||
if (!this.linkifier) {
|
if (!this.linkifier) {
|
||||||
throw new Error('Cannot attach a hypertext link handler before Terminal.open is called');
|
throw new Error('Cannot attach a hypertext link handler before Terminal.open is called');
|
||||||
}
|
}
|
||||||
this.linkifier.attachHypertextLinkHandler(handler);
|
this.linkifier.setHypertextLinkHandler(handler);
|
||||||
|
this.refresh(0, this.rows - 1);
|
||||||
|
};
|
||||||
|
Terminal.prototype.setHypertextValidationCallback = function (handler) {
|
||||||
|
if (!this.linkifier) {
|
||||||
|
throw new Error('Cannot attach a hypertext validation callback before Terminal.open is called');
|
||||||
|
}
|
||||||
|
this.linkifier.setHypertextValidationCallback(handler);
|
||||||
this.refresh(0, this.rows - 1);
|
this.refresh(0, this.rows - 1);
|
||||||
};
|
};
|
||||||
Terminal.prototype.registerLinkMatcher = function (regex, handler, options) {
|
Terminal.prototype.registerLinkMatcher = function (regex, handler, options) {
|
||||||
@@ -3420,6 +3603,7 @@ Terminal.prototype.keyDown = function (ev) {
|
|||||||
if (this.customKeydownHandler && this.customKeydownHandler(ev) === false) {
|
if (this.customKeydownHandler && this.customKeydownHandler(ev) === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
this.restartCursorBlinking();
|
||||||
if (!this.compositionHelper.keydown.bind(this.compositionHelper)(ev)) {
|
if (!this.compositionHelper.keydown.bind(this.compositionHelper)(ev)) {
|
||||||
if (this.ybase !== this.ydisp) {
|
if (this.ybase !== this.ydisp) {
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
@@ -3814,14 +3998,6 @@ Terminal.prototype.resize = function (x, y) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
i = this.lines.length;
|
|
||||||
while (i--) {
|
|
||||||
while (this.lines.get(i).length > x) {
|
|
||||||
this.lines.get(i).pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.cols = x;
|
this.cols = x;
|
||||||
this.setupStops(this.cols);
|
this.setupStops(this.cols);
|
||||||
j = this.rows;
|
j = this.rows;
|
||||||
@@ -4023,8 +4199,10 @@ Terminal.prototype.reset = function () {
|
|||||||
this.options.rows = this.rows;
|
this.options.rows = this.rows;
|
||||||
this.options.cols = this.cols;
|
this.options.cols = this.cols;
|
||||||
var customKeydownHandler = this.customKeydownHandler;
|
var customKeydownHandler = this.customKeydownHandler;
|
||||||
|
var cursorBlinkInterval = this.cursorBlinkInterval;
|
||||||
Terminal.call(this, this.options);
|
Terminal.call(this, this.options);
|
||||||
this.customKeydownHandler = customKeydownHandler;
|
this.customKeydownHandler = customKeydownHandler;
|
||||||
|
this.cursorBlinkInterval = cursorBlinkInterval;
|
||||||
this.refresh(0, this.rows - 1);
|
this.refresh(0, this.rows - 1);
|
||||||
this.viewport.syncScrollArea();
|
this.viewport.syncScrollArea();
|
||||||
};
|
};
|
||||||
@@ -4135,6 +4313,6 @@ module.exports = Terminal;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{"./CompositionHelper":2,"./EscapeSequences":3,"./EventEmitter":4,"./InputHandler":5,"./Linkifier":6,"./Parser":7,"./Renderer":8,"./Viewport":9,"./handlers/Clipboard":10,"./utils/Browser":11,"./utils/CharMeasure":12,"./utils/CircularList":13}]},{},[15])(15)
|
},{"./CompositionHelper":2,"./EscapeSequences":3,"./EventEmitter":4,"./InputHandler":5,"./Linkifier":6,"./Parser":7,"./Renderer":8,"./Viewport":9,"./handlers/Clipboard":10,"./utils/Browser":11,"./utils/CharMeasure":12,"./utils/CircularList":13,"./utils/Mouse":16}]},{},[17])(17)
|
||||||
});
|
});
|
||||||
//# sourceMappingURL=xterm.js.map
|
//# sourceMappingURL=xterm.js.map
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+21
-19
@@ -13,27 +13,28 @@ const ts = require('gulp-typescript');
|
|||||||
|
|
||||||
|
|
||||||
let buildDir = process.env.BUILD_DIR || 'build';
|
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
|
* Compile TypeScript sources to JavaScript files and create a source map file for each TypeScript
|
||||||
* file compiled.
|
* file compiled.
|
||||||
*/
|
*/
|
||||||
gulp.task('tsc', function () {
|
gulp.task('tsc', function () {
|
||||||
// Remove the lib/ directory to prevent confusion if files were deleted in src/
|
// Remove the ${outDir}/ directory to prevent confusion if files were deleted in ${srcDir}/
|
||||||
fs.emptyDirSync('lib');
|
fs.emptyDirSync(`${outDir}`);
|
||||||
|
|
||||||
// Build all TypeScript files (including tests) to lib/, based on the configuration defined in
|
// Build all TypeScript files (including tests) to ${outDir}/, based on the configuration defined in
|
||||||
// `tsconfig.json`.
|
// `tsconfig.json`.
|
||||||
let tsProject = ts.createProject('tsconfig.json');
|
|
||||||
let tsResult = tsProject.src().pipe(sourcemaps.init()).pipe(tsProject());
|
let tsResult = tsProject.src().pipe(sourcemaps.init()).pipe(tsProject());
|
||||||
let tsc = tsResult.js.pipe(sourcemaps.write('.', {includeContent: false, sourceRoot: ''})).pipe(gulp.dest('lib'));
|
let tsc = tsResult.js.pipe(sourcemaps.write('.', {includeContent: false, sourceRoot: ''})).pipe(gulp.dest(outDir));
|
||||||
|
|
||||||
// Copy all addons from src/ to lib/
|
// Copy all addons from ${srcDir}/ to ${outDir}/
|
||||||
let copyAddons = gulp.src('src/addons/**/*').pipe(gulp.dest('lib/addons'));
|
let copyAddons = gulp.src(`${srcDir}/addons/**/*`).pipe(gulp.dest(`${outDir}/addons`));
|
||||||
|
|
||||||
// Copy stylesheets from src/ to lib/
|
// Copy stylesheets from ${srcDir}/ to ${outDir}/
|
||||||
let copyStylesheets = gulp.src('src/**/*.css').pipe(gulp.dest('lib'));
|
let copyStylesheets = gulp.src(`${srcDir}/**/*.css`).pipe(gulp.dest(outDir));
|
||||||
|
|
||||||
return merge(tsc, copyAddons, copyStylesheets);
|
return merge(tsc, copyAddons, copyStylesheets);
|
||||||
});
|
});
|
||||||
@@ -49,7 +50,7 @@ gulp.task('browserify', ['tsc'], function() {
|
|||||||
let browserifyOptions = {
|
let browserifyOptions = {
|
||||||
basedir: buildDir,
|
basedir: buildDir,
|
||||||
debug: true,
|
debug: true,
|
||||||
entries: ['../lib/xterm.js'],
|
entries: [`../${outDir}/xterm.js`],
|
||||||
standalone: 'Terminal',
|
standalone: 'Terminal',
|
||||||
cache: {},
|
cache: {},
|
||||||
packageCache: {}
|
packageCache: {}
|
||||||
@@ -62,17 +63,17 @@ gulp.task('browserify', ['tsc'], function() {
|
|||||||
.pipe(sourcemaps.write('./'))
|
.pipe(sourcemaps.write('./'))
|
||||||
.pipe(gulp.dest(buildDir));
|
.pipe(gulp.dest(buildDir));
|
||||||
|
|
||||||
// Copy all add-ons from lib/ to buildDir
|
// Copy all add-ons from ${outDir}/ to buildDir
|
||||||
let copyAddons = gulp.src('lib/addons/**/*').pipe(gulp.dest(`${buildDir}/addons`));
|
let copyAddons = gulp.src(`${outDir}/addons/**/*`).pipe(gulp.dest(`${buildDir}/addons`));
|
||||||
|
|
||||||
// Copy stylesheets from src/ to lib/
|
// Copy stylesheets from ${outDir}/ to ${buildDir}/
|
||||||
let copyStylesheets = gulp.src('lib/**/*.css').pipe(gulp.dest(buildDir));
|
let copyStylesheets = gulp.src(`${outDir}/**/*.css`).pipe(gulp.dest(buildDir));
|
||||||
|
|
||||||
return merge(bundleStream, copyAddons, copyStylesheets);
|
return merge(bundleStream, copyAddons, copyStylesheets);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('instrument-test', function () {
|
gulp.task('instrument-test', function () {
|
||||||
return gulp.src(['lib/**/*.js'])
|
return gulp.src([`${outDir}/**/*.js`])
|
||||||
// Covering files
|
// Covering files
|
||||||
.pipe(istanbul())
|
.pipe(istanbul())
|
||||||
// Force `require` to return covered files
|
// Force `require` to return covered files
|
||||||
@@ -80,19 +81,20 @@ gulp.task('instrument-test', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('mocha', ['instrument-test'], function () {
|
gulp.task('mocha', ['instrument-test'], function () {
|
||||||
return gulp.src(['lib/*test.js', 'lib/**/*test.js'], {read: false})
|
return gulp.src([`${outDir}/*test.js`, `${outDir}/**/*test.js`], {read: false})
|
||||||
.pipe(mocha())
|
.pipe(mocha())
|
||||||
|
.once('error', () => process.exit(1))
|
||||||
.pipe(istanbul.writeReports());
|
.pipe(istanbul.writeReports());
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use `sorcery` to resolve the source map chain and point back to the TypeScript files.
|
* 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
|
* (Without this task the source maps produced for the JavaScript bundle points into the
|
||||||
* compiled JavaScript files in lib/).
|
* compiled JavaScript files in ${outDir}/).
|
||||||
*/
|
*/
|
||||||
gulp.task('sorcery', ['browserify'], function () {
|
gulp.task('sorcery', ['browserify'], function () {
|
||||||
var chain = sorcery.loadSync(`${buildDir}/xterm.js`);
|
var chain = sorcery.loadSync(`${buildDir}/xterm.js`);
|
||||||
var map = chain.apply();
|
chain.apply();
|
||||||
chain.writeSync();
|
chain.writeSync();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "xterm",
|
"name": "xterm",
|
||||||
"description": "Full xterm terminal, in your browser",
|
"description": "Full xterm terminal, in your browser",
|
||||||
"version": "2.4.0",
|
"version": "2.7.0",
|
||||||
"ignore": [
|
"ignore": [
|
||||||
"demo",
|
"demo",
|
||||||
"test",
|
"test",
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"prestart": "npm run build",
|
"prestart": "npm run build",
|
||||||
"start": "node demo/app",
|
"start": "node demo/app",
|
||||||
"dev": "nodemon -e js,ts --watch src --watch demo --exec npm start",
|
"dev": "nodemon -e js,ts,css --watch src --watch demo --exec npm start",
|
||||||
"lint": "tslint src/*.ts src/**/*.ts",
|
"lint": "tslint src/*.ts src/**/*.ts",
|
||||||
"test": "gulp test",
|
"test": "gulp test",
|
||||||
"build:docs": "jsdoc -c jsdoc.json",
|
"build:docs": "jsdoc -c jsdoc.json",
|
||||||
|
|||||||
+1
-4
@@ -53,14 +53,11 @@ export class EventEmitter {
|
|||||||
return this.on(type, on);
|
return this.on(type, on);
|
||||||
}
|
}
|
||||||
|
|
||||||
public emit(type): void {
|
public emit(type: string, ...args: any[]): void {
|
||||||
if (!this._events[type]) {
|
if (!this._events[type]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let args = Array.prototype.slice.call(arguments, 1);
|
|
||||||
let obj = this._events[type];
|
let obj = this._events[type];
|
||||||
|
|
||||||
for (let i = 0; i < obj.length; i++) {
|
for (let i = 0; i < obj.length; i++) {
|
||||||
obj[i].apply(this, args);
|
obj[i].apply(this, args);
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-1
@@ -385,7 +385,13 @@ export class InputHandler implements IInputHandler {
|
|||||||
while (j--) this._terminal.eraseLine(j);
|
while (j--) this._terminal.eraseLine(j);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
; // no saved lines
|
// Clear scrollback (everything not in viewport)
|
||||||
|
const scrollBackSize = this._terminal.lines.length - this._terminal.rows;
|
||||||
|
if (scrollBackSize > 0) {
|
||||||
|
this._terminal.lines.trimStart(scrollBackSize);
|
||||||
|
this._terminal.ybase = Math.max(this._terminal.ybase - scrollBackSize, 0);
|
||||||
|
this._terminal.ydisp = Math.max(this._terminal.ydisp - scrollBackSize, 0);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+139
-51
@@ -8,9 +8,9 @@ import { Linkifier } from './Linkifier';
|
|||||||
import { LinkMatcher } from './Types';
|
import { LinkMatcher } from './Types';
|
||||||
|
|
||||||
class TestLinkifier extends Linkifier {
|
class TestLinkifier extends Linkifier {
|
||||||
constructor(document: Document, rows: HTMLElement[]) {
|
constructor() {
|
||||||
Linkifier.TIME_BEFORE_LINKIFY = 0;
|
Linkifier.TIME_BEFORE_LINKIFY = 0;
|
||||||
super(document, rows);
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get linkMatchers(): LinkMatcher[] { return this._linkMatchers; }
|
public get linkMatchers(): LinkMatcher[] { return this._linkMatchers; }
|
||||||
@@ -25,75 +25,163 @@ describe('Linkifier', () => {
|
|||||||
let linkifier: TestLinkifier;
|
let linkifier: TestLinkifier;
|
||||||
|
|
||||||
beforeEach(done => {
|
beforeEach(done => {
|
||||||
rows = [];
|
|
||||||
jsdom.env('', (err, w) => {
|
jsdom.env('', (err, w) => {
|
||||||
window = w;
|
window = w;
|
||||||
document = window.document;
|
document = window.document;
|
||||||
linkifier = new TestLinkifier(document, rows);
|
linkifier = new TestLinkifier();
|
||||||
container = document.createElement('div');
|
|
||||||
document.body.appendChild(container);
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function addRow(text: string) {
|
function addRow(html: string) {
|
||||||
const element = document.createElement('div');
|
const element = document.createElement('div');
|
||||||
element.textContent = text;
|
element.innerHTML = html;
|
||||||
container.appendChild(element);
|
container.appendChild(element);
|
||||||
rows.push(element);
|
rows.push(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
function clickElement(element: Node) {
|
describe('before attachToDom', () => {
|
||||||
const event = document.createEvent('MouseEvent');
|
it('should allow link matcher registration', done => {
|
||||||
event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
assert.doesNotThrow(() => {
|
||||||
element.dispatchEvent(event);
|
const linkMatcherId = linkifier.registerLinkMatcher(/foo/, () => {});
|
||||||
}
|
assert.isTrue(linkifier.deregisterLinkMatcher(linkMatcherId));
|
||||||
|
done();
|
||||||
describe('validationCallback', () => {
|
|
||||||
it('should enable link if true', done => {
|
|
||||||
addRow('test');
|
|
||||||
linkifier.registerLinkMatcher(/test/, () => done(), {
|
|
||||||
validationCallback: (url, 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, 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);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('priority', () => {
|
describe('after attachToDom', () => {
|
||||||
it('should order the list from highest priority to lowest #1', () => {
|
beforeEach(() => {
|
||||||
const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: 1 });
|
rows = [];
|
||||||
const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: -1 });
|
linkifier.attachToDom(document, rows);
|
||||||
assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [aId, 0, bId]);
|
container = document.createElement('div');
|
||||||
|
document.body.appendChild(container);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should order the list from highest priority to lowest #2', () => {
|
function clickElement(element: Node) {
|
||||||
const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: -1 });
|
const event = document.createEvent('MouseEvent');
|
||||||
const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: 1 });
|
event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
||||||
assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [bId, 0, aId]);
|
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));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should order items of equal priority in the order they are added', () => {
|
describe('link matcher', () => {
|
||||||
const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: 0 });
|
function assertLinkifiesRow(rowText: string, linkMatcherRegex: RegExp, expectedHtml: string, done: MochaDone) {
|
||||||
const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: 0 });
|
addRow(rowText);
|
||||||
assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [0, aId, bId]);
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+89
-51
@@ -16,8 +16,8 @@ const ipClause = '((\\d{1,3}\\.){3}\\d{1,3})';
|
|||||||
const localHostClause = '(localhost)';
|
const localHostClause = '(localhost)';
|
||||||
const portClause = '(:\\d{1,5})';
|
const portClause = '(:\\d{1,5})';
|
||||||
const hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?';
|
const hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?';
|
||||||
const pathClause = '(\\/[\\/\\w\\.\\-%]*)*';
|
const pathClause = '(\\/[\\/\\w\\.\\-%~]*)*';
|
||||||
const queryStringHashFragmentCharacterSet = '[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&\'*+,:;\\=\\.\\-]*';
|
const queryStringHashFragmentCharacterSet = '[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&\'*+,:;~\\=\\.\\-]*';
|
||||||
const queryStringClause = '(\\?' + queryStringHashFragmentCharacterSet + ')?';
|
const queryStringClause = '(\\?' + queryStringHashFragmentCharacterSet + ')?';
|
||||||
const hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?';
|
const hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?';
|
||||||
const negatedPathCharacterSet = '[^\\/\\w\\.\\-%]+';
|
const negatedPathCharacterSet = '[^\\/\\w\\.\\-%]+';
|
||||||
@@ -37,8 +37,8 @@ const HYPERTEXT_LINK_MATCHER_ID = 0;
|
|||||||
export class Linkifier {
|
export class Linkifier {
|
||||||
/**
|
/**
|
||||||
* The time to wait after a row is changed before it is linkified. This prevents
|
* The time to wait after a row is changed before it is linkified. This prevents
|
||||||
* the costly operation of searching every row multiple times, pntentially a
|
* the costly operation of searching every row multiple times, potentially a
|
||||||
* huge aount of times.
|
* huge amount of times.
|
||||||
*/
|
*/
|
||||||
protected static TIME_BEFORE_LINKIFY = 200;
|
protected static TIME_BEFORE_LINKIFY = 200;
|
||||||
|
|
||||||
@@ -49,19 +49,32 @@ export class Linkifier {
|
|||||||
private _rowTimeoutIds: number[];
|
private _rowTimeoutIds: number[];
|
||||||
private _nextLinkMatcherId = HYPERTEXT_LINK_MATCHER_ID;
|
private _nextLinkMatcherId = HYPERTEXT_LINK_MATCHER_ID;
|
||||||
|
|
||||||
constructor(document: Document, rows: HTMLElement[]) {
|
constructor() {
|
||||||
this._document = document;
|
|
||||||
this._rows = rows;
|
|
||||||
this._rowTimeoutIds = [];
|
this._rowTimeoutIds = [];
|
||||||
this._linkMatchers = [];
|
this._linkMatchers = [];
|
||||||
this.registerLinkMatcher(strictUrlRegex, null, { matchIndex: 1 });
|
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.
|
* Queues a row for linkification.
|
||||||
* @param {number} rowIndex The index of the row to linkify.
|
* @param {number} rowIndex The index of the row to linkify.
|
||||||
*/
|
*/
|
||||||
public linkifyRow(rowIndex: number): void {
|
public linkifyRow(rowIndex: number): void {
|
||||||
|
// Don't attempt linkify if not yet attached to DOM
|
||||||
|
if (!this._document) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const timeoutId = this._rowTimeoutIds[rowIndex];
|
const timeoutId = this._rowTimeoutIds[rowIndex];
|
||||||
if (timeoutId) {
|
if (timeoutId) {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
@@ -75,10 +88,19 @@ export class Linkifier {
|
|||||||
* @param {LinkHandler} handler The handler to use, this can be cleared with
|
* @param {LinkHandler} handler The handler to use, this can be cleared with
|
||||||
* null.
|
* null.
|
||||||
*/
|
*/
|
||||||
public attachHypertextLinkHandler(handler: LinkMatcherHandler): void {
|
public setHypertextLinkHandler(handler: LinkMatcherHandler): void {
|
||||||
this._linkMatchers[HYPERTEXT_LINK_MATCHER_ID].handler = handler;
|
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
|
* Registers a link matcher, allowing custom link patterns to be matched and
|
||||||
* handled.
|
* handled.
|
||||||
@@ -155,16 +177,18 @@ export class Linkifier {
|
|||||||
const text = row.textContent;
|
const text = row.textContent;
|
||||||
for (let i = 0; i < this._linkMatchers.length; i++) {
|
for (let i = 0; i < this._linkMatchers.length; i++) {
|
||||||
const matcher = this._linkMatchers[i];
|
const matcher = this._linkMatchers[i];
|
||||||
const uri = this._findLinkMatch(text, matcher.regex, matcher.matchIndex);
|
const linkElements = this._doLinkifyRow(row, matcher);
|
||||||
if (uri) {
|
if (linkElements.length > 0) {
|
||||||
const linkElement = this._doLinkifyRow(rowIndex, uri, matcher.handler, matcher.id === HYPERTEXT_LINK_MATCHER_ID);
|
|
||||||
// Fire validation callback
|
// Fire validation callback
|
||||||
if (linkElement && matcher.validationCallback) {
|
if (matcher.validationCallback) {
|
||||||
matcher.validationCallback(uri, isValid => {
|
for (let j = 0; j < linkElements.length; j++) {
|
||||||
if (!isValid) {
|
const element = linkElements[j];
|
||||||
linkElement.classList.add(INVALID_LINK_CLASS);
|
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.
|
// Only allow a single LinkMatcher to trigger on any given row.
|
||||||
return;
|
return;
|
||||||
@@ -174,54 +198,61 @@ export class Linkifier {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Linkifies a row given a specific handler.
|
* Linkifies a row given a specific handler.
|
||||||
* @param {number} rowIndex The index of the row to linkify.
|
* @param {HTMLElement} row The row to linkify.
|
||||||
* @param {string} uri The uri that has been found.
|
* @param {LinkMatcher} matcher The link matcher for this line.
|
||||||
* @param {handler} handler The handler to trigger when the link is triggered.
|
|
||||||
* @return The link element if it was added, otherwise undefined.
|
* @return The link element if it was added, otherwise undefined.
|
||||||
*/
|
*/
|
||||||
private _doLinkifyRow(rowIndex: number, uri: string, handler: LinkMatcherHandler, isHttpLinkMatcher: boolean): HTMLElement {
|
private _doLinkifyRow(row: HTMLElement, matcher: LinkMatcher): HTMLElement[] {
|
||||||
// Iterate over nodes as we want to consider text nodes
|
// Iterate over nodes as we want to consider text nodes
|
||||||
const nodes = this._rows[rowIndex].childNodes;
|
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++) {
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
const node = nodes[i];
|
const node = nodes[i];
|
||||||
const searchIndex = node.textContent.indexOf(uri);
|
const searchIndex = node.textContent.indexOf(uri);
|
||||||
if (searchIndex >= 0) {
|
if (searchIndex >= 0) {
|
||||||
const linkElement = this._createAnchorElement(uri, handler, isHttpLinkMatcher);
|
const linkElement = this._createAnchorElement(uri, matcher.handler, isHttpLinkMatcher);
|
||||||
if (node.textContent.length === uri.length) {
|
if (node.textContent.length === uri.length) {
|
||||||
// Matches entire string
|
// Matches entire string
|
||||||
|
|
||||||
if (node.nodeType === 3 /*Node.TEXT_NODE*/) {
|
if (node.nodeType === 3 /*Node.TEXT_NODE*/) {
|
||||||
this._replaceNode(node, linkElement);
|
this._replaceNode(node, linkElement);
|
||||||
} else {
|
} else {
|
||||||
const element = (<HTMLElement>node);
|
const element = (<HTMLElement>node);
|
||||||
if (element.nodeName === 'A') {
|
if (element.nodeName === 'A') {
|
||||||
// This row has already been linkified
|
// This row has already been linkified
|
||||||
return;
|
return result;
|
||||||
}
|
}
|
||||||
element.innerHTML = '';
|
element.innerHTML = '';
|
||||||
element.appendChild(linkElement);
|
element.appendChild(linkElement);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Matches part of string
|
// Matches part of string
|
||||||
this._replaceNodeSubstringWithNode(node, linkElement, uri, searchIndex);
|
const nodesAdded = this._replaceNodeSubstringWithNode(node, linkElement, uri, searchIndex);
|
||||||
|
// No need to consider the new nodes
|
||||||
|
i += nodesAdded;
|
||||||
}
|
}
|
||||||
return linkElement;
|
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;
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds a link match in a piece of text.
|
|
||||||
* @param {string} text The text to search.
|
|
||||||
* @param {number} matchIndex The regex match index of the link.
|
|
||||||
* @return {string} The matching URI or null if not found.
|
|
||||||
*/
|
|
||||||
private _findLinkMatch(text: string, regex: RegExp, matchIndex?: number): string {
|
|
||||||
const match = text.match(regex);
|
|
||||||
if (!match || match.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return match[typeof matchIndex !== 'number' ? 0 : matchIndex];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -232,6 +263,7 @@ export class Linkifier {
|
|||||||
private _createAnchorElement(uri: string, handler: LinkMatcherHandler, isHypertextLinkHandler: boolean): HTMLAnchorElement {
|
private _createAnchorElement(uri: string, handler: LinkMatcherHandler, isHypertextLinkHandler: boolean): HTMLAnchorElement {
|
||||||
const element = this._document.createElement('a');
|
const element = this._document.createElement('a');
|
||||||
element.textContent = uri;
|
element.textContent = uri;
|
||||||
|
element.draggable = false;
|
||||||
if (isHypertextLinkHandler) {
|
if (isHypertextLinkHandler) {
|
||||||
element.href = uri;
|
element.href = uri;
|
||||||
// Force link on another tab so work is not lost
|
// Force link on another tab so work is not lost
|
||||||
@@ -273,8 +305,9 @@ export class Linkifier {
|
|||||||
* @param {Node} newNode The new node to insert.
|
* @param {Node} newNode The new node to insert.
|
||||||
* @param {string} substring The substring to replace.
|
* @param {string} substring The substring to replace.
|
||||||
* @param {number} substringIndex The index of the substring within the string.
|
* @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): void {
|
private _replaceNodeSubstringWithNode(targetNode: Node, newNode: Node, substring: string, substringIndex: number): number {
|
||||||
let node = targetNode;
|
let node = targetNode;
|
||||||
if (node.nodeType !== 3/*Node.TEXT_NODE*/) {
|
if (node.nodeType !== 3/*Node.TEXT_NODE*/) {
|
||||||
node = node.childNodes[0];
|
node = node.childNodes[0];
|
||||||
@@ -283,7 +316,7 @@ export class Linkifier {
|
|||||||
// The targetNode will be either a text node or a <span>. The text node
|
// 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
|
// (targetNode or its only-child) needs to be replaced with newNode plus new
|
||||||
// text nodes potentially on either side.
|
// text nodes potentially on either side.
|
||||||
if (node.childNodes.length === 0 && node.nodeType !== Node.TEXT_NODE) {
|
if (node.childNodes.length === 0 && node.nodeType !== 3/*Node.TEXT_NODE*/) {
|
||||||
throw new Error('targetNode must be a text node or only contain a single text node');
|
throw new Error('targetNode must be a text node or only contain a single text node');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,18 +327,23 @@ export class Linkifier {
|
|||||||
const rightText = fullText.substring(substring.length);
|
const rightText = fullText.substring(substring.length);
|
||||||
const rightTextNode = this._document.createTextNode(rightText);
|
const rightTextNode = this._document.createTextNode(rightText);
|
||||||
this._replaceNode(node, newNode, rightTextNode);
|
this._replaceNode(node, newNode, rightTextNode);
|
||||||
} else if (substringIndex === targetNode.textContent.length - substring.length) {
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (substringIndex === targetNode.textContent.length - substring.length) {
|
||||||
// Replace with <textnode><newNode>
|
// Replace with <textnode><newNode>
|
||||||
const leftText = fullText.substring(0, substringIndex);
|
const leftText = fullText.substring(0, substringIndex);
|
||||||
const leftTextNode = this._document.createTextNode(leftText);
|
const leftTextNode = this._document.createTextNode(leftText);
|
||||||
this._replaceNode(node, leftTextNode, newNode);
|
this._replaceNode(node, leftTextNode, newNode);
|
||||||
} else {
|
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(node, leftTextNode, newNode, rightTextNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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(node, leftTextNode, newNode, rightTextNode);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-6
@@ -181,7 +181,7 @@ export class Parser {
|
|||||||
*
|
*
|
||||||
* @param data The data to parse.
|
* @param data The data to parse.
|
||||||
*/
|
*/
|
||||||
public parse(data: string) {
|
public parse(data: string): ParserState {
|
||||||
let l = data.length, j, cs, ch, code, low;
|
let l = data.length, j, cs, ch, code, low;
|
||||||
|
|
||||||
this._position = 0;
|
this._position = 0;
|
||||||
@@ -471,6 +471,8 @@ export class Parser {
|
|||||||
case ParserState.DCS:
|
case ParserState.DCS:
|
||||||
if (ch === C0.ESC || ch === C0.BEL) {
|
if (ch === C0.ESC || ch === C0.BEL) {
|
||||||
if (ch === C0.ESC) this._position++;
|
if (ch === C0.ESC) this._position++;
|
||||||
|
let pt;
|
||||||
|
let valid: boolean;
|
||||||
|
|
||||||
switch (this._terminal.prefix) {
|
switch (this._terminal.prefix) {
|
||||||
// User-Defined Keys (DECUDK).
|
// User-Defined Keys (DECUDK).
|
||||||
@@ -480,8 +482,8 @@ export class Parser {
|
|||||||
// Request Status String (DECRQSS).
|
// Request Status String (DECRQSS).
|
||||||
// test: echo -e '\eP$q"p\e\\'
|
// test: echo -e '\eP$q"p\e\\'
|
||||||
case '$q':
|
case '$q':
|
||||||
let pt = this._terminal.currentParam
|
pt = this._terminal.currentParam;
|
||||||
, valid = false;
|
valid = false;
|
||||||
|
|
||||||
switch (pt) {
|
switch (pt) {
|
||||||
// DECSCA
|
// DECSCA
|
||||||
@@ -526,9 +528,8 @@ export class Parser {
|
|||||||
// This can cause a small glitch in vim.
|
// This can cause a small glitch in vim.
|
||||||
// test: echo -ne '\eP+q6b64\e\\'
|
// test: echo -ne '\eP+q6b64\e\\'
|
||||||
case '+q':
|
case '+q':
|
||||||
// TODO: Don't declare pt twice
|
pt = this._terminal.currentParam;
|
||||||
/*let*/ pt = this._terminal.currentParam
|
valid = false;
|
||||||
, valid = false;
|
|
||||||
|
|
||||||
this._terminal.send(C0.ESC + 'P' + +valid + '+r' + pt + C0.ESC + '\\');
|
this._terminal.send(C0.ESC + 'P' + +valid + '+r' + pt + C0.ESC + '\\');
|
||||||
break;
|
break;
|
||||||
@@ -563,6 +564,7 @@ export class Parser {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return this._state;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+118
-85
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ITerminal } from './Interfaces';
|
import { ITerminal } from './Interfaces';
|
||||||
|
import { DomElementObjectPool } from './utils/DomElementObjectPool';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum number of refresh frames to skip when the write buffer is non-
|
* The maximum number of refresh frames to skip when the write buffer is non-
|
||||||
@@ -30,12 +31,15 @@ export class Renderer {
|
|||||||
private _refreshFramesSkipped = 0;
|
private _refreshFramesSkipped = 0;
|
||||||
private _refreshAnimationFrame = null;
|
private _refreshAnimationFrame = null;
|
||||||
|
|
||||||
|
private _spanElementObjectPool = new DomElementObjectPool('span');
|
||||||
|
|
||||||
constructor(private _terminal: ITerminal) {
|
constructor(private _terminal: ITerminal) {
|
||||||
// Figure out whether boldness affects
|
// Figure out whether boldness affects
|
||||||
// the character width of monospace fonts.
|
// the character width of monospace fonts.
|
||||||
if (brokenBold === null) {
|
if (brokenBold === null) {
|
||||||
brokenBold = checkBoldBroken((<any>this._terminal).document);
|
brokenBold = checkBoldBroken((<any>this._terminal).element);
|
||||||
}
|
}
|
||||||
|
this._spanElementObjectPool = new DomElementObjectPool('span');
|
||||||
|
|
||||||
// TODO: Pull more DOM interactions into Renderer.constructor, element for
|
// TODO: Pull more DOM interactions into Renderer.constructor, element for
|
||||||
// example should be owned by Renderer (and also exposed by Terminal due to
|
// example should be owned by Renderer (and also exposed by Terminal due to
|
||||||
@@ -117,9 +121,8 @@ export class Renderer {
|
|||||||
* @param {number} end The row to end at (between fromRow 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 {
|
private _refresh(start: number, end: number): void {
|
||||||
let x, y, i, line, out, ch, ch_width, width, data, attr, bg, fg, flags, row, parent, focused = document.activeElement;
|
|
||||||
|
|
||||||
// If this is a big refresh, remove the terminal rows from the DOM for faster calculations
|
// 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) {
|
if (end - start >= this._terminal.rows / 2) {
|
||||||
parent = this._terminal.element.parentNode;
|
parent = this._terminal.element.parentNode;
|
||||||
if (parent) {
|
if (parent) {
|
||||||
@@ -127,8 +130,8 @@ export class Renderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
width = this._terminal.cols;
|
let width = this._terminal.cols;
|
||||||
y = start;
|
let y = start;
|
||||||
|
|
||||||
if (end >= this._terminal.rows) {
|
if (end >= this._terminal.rows) {
|
||||||
this._terminal.log('`end` is too large. Most likely a bad CSR.');
|
this._terminal.log('`end` is too large. Most likely a bad CSR.');
|
||||||
@@ -136,80 +139,104 @@ export class Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (; y <= end; y++) {
|
for (; y <= end; y++) {
|
||||||
row = y + this._terminal.ydisp;
|
let row = y + this._terminal.ydisp;
|
||||||
|
|
||||||
line = this._terminal.lines.get(row);
|
let line = this._terminal.lines.get(row);
|
||||||
if (!line || !this._terminal.children[y]) {
|
|
||||||
// Continue if the line is not available, this means a resize is currently in progress
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
out = '';
|
|
||||||
|
|
||||||
if (this._terminal.y === y - (this._terminal.ybase - this._terminal.ydisp)
|
let x;
|
||||||
&& this._terminal.cursorState
|
if (this._terminal.y === y - (this._terminal.ybase - this._terminal.ydisp) &&
|
||||||
&& !this._terminal.cursorHidden) {
|
this._terminal.cursorState &&
|
||||||
|
!this._terminal.cursorHidden) {
|
||||||
x = this._terminal.x;
|
x = this._terminal.x;
|
||||||
} else {
|
} else {
|
||||||
x = -1;
|
x = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
attr = this._terminal.defAttr;
|
let attr = this._terminal.defAttr;
|
||||||
i = 0;
|
|
||||||
|
|
||||||
for (; i < width; i++) {
|
const documentFragment = document.createDocumentFragment();
|
||||||
if (!line[i]) {
|
let innerHTML = '';
|
||||||
// Continue if the character is not available, this means a resize is currently in progress
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
data = line[i][0];
|
|
||||||
ch = line[i][1];
|
|
||||||
ch_width = line[i][2];
|
|
||||||
if (!ch_width)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (i === x) data = -1;
|
if (i === x) {
|
||||||
|
data = -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (data !== attr) {
|
if (data !== attr) {
|
||||||
if (attr !== this._terminal.defAttr) {
|
if (attr !== this._terminal.defAttr) {
|
||||||
out += '</span>';
|
if (innerHTML) {
|
||||||
|
currentElement.innerHTML = innerHTML;
|
||||||
|
innerHTML = '';
|
||||||
|
}
|
||||||
|
documentFragment.appendChild(currentElement);
|
||||||
|
currentElement = null;
|
||||||
}
|
}
|
||||||
if (data !== this._terminal.defAttr) {
|
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) {
|
if (data === -1) {
|
||||||
out += '<span class="reverse-video terminal-cursor">';
|
currentElement.classList.add('reverse-video', 'terminal-cursor');
|
||||||
} else {
|
} else {
|
||||||
let classNames = [];
|
let bg = data & 0x1ff;
|
||||||
|
let fg = (data >> 9) & 0x1ff;
|
||||||
bg = data & 0x1ff;
|
let flags = data >> 18;
|
||||||
fg = (data >> 9) & 0x1ff;
|
|
||||||
flags = data >> 18;
|
|
||||||
|
|
||||||
if (flags & FLAGS.BOLD) {
|
if (flags & FLAGS.BOLD) {
|
||||||
if (!brokenBold) {
|
if (!brokenBold) {
|
||||||
classNames.push('xterm-bold');
|
currentElement.classList.add('xterm-bold');
|
||||||
}
|
}
|
||||||
// See: XTerm*boldColors
|
// See: XTerm*boldColors
|
||||||
if (fg < 8) fg += 8;
|
if (fg < 8) {
|
||||||
|
fg += 8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & FLAGS.UNDERLINE) {
|
if (flags & FLAGS.UNDERLINE) {
|
||||||
classNames.push('xterm-underline');
|
currentElement.classList.add('xterm-underline');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & FLAGS.BLINK) {
|
if (flags & FLAGS.BLINK) {
|
||||||
classNames.push('xterm-blink');
|
currentElement.classList.add('xterm-blink');
|
||||||
}
|
}
|
||||||
|
|
||||||
// If inverse flag is on, then swap the foreground and background variables.
|
// If inverse flag is on, then swap the foreground and background variables.
|
||||||
if (flags & FLAGS.INVERSE) {
|
if (flags & FLAGS.INVERSE) {
|
||||||
/* One-line variable swap in JavaScript: http://stackoverflow.com/a/16201730 */
|
let temp = bg;
|
||||||
bg = [fg, fg = bg][0];
|
bg = fg;
|
||||||
// Should inverse just be before the
|
fg = temp;
|
||||||
// above boldColors effect instead?
|
// Should inverse just be before the above boldColors effect instead?
|
||||||
if ((flags & 1) && fg < 8) fg += 8;
|
if ((flags & 1) && fg < 8) {
|
||||||
|
fg += 8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & FLAGS.INVISIBLE) {
|
if (flags & FLAGS.INVISIBLE) {
|
||||||
classNames.push('xterm-hidden');
|
currentElement.classList.add('xterm-hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -229,55 +256,60 @@ export class Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bg < 256) {
|
if (bg < 256) {
|
||||||
classNames.push('xterm-bg-color-' + bg);
|
currentElement.classList.add(`xterm-bg-color-${bg}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fg < 256) {
|
if (fg < 256) {
|
||||||
classNames.push('xterm-color-' + fg);
|
currentElement.classList.add(`xterm-color-${fg}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
out += '<span';
|
|
||||||
if (classNames.length) {
|
|
||||||
out += ' class="' + classNames.join(' ') + '"';
|
|
||||||
}
|
|
||||||
out += '>';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ch_width === 2) {
|
if (ch_width === 2) {
|
||||||
out += '<span class="xterm-wide-char">';
|
// 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.
|
||||||
switch (ch) {
|
innerHTML += `<span class="xterm-wide-char">${ch}</span>`;
|
||||||
case '&':
|
} else if (ch.charCodeAt(0) > 255) {
|
||||||
out += '&';
|
// Wrap any non-wide unicode character as some fonts size them badly
|
||||||
break;
|
innerHTML += `<span class="xterm-normal-char">${ch}</span>`;
|
||||||
case '<':
|
} else {
|
||||||
out += '<';
|
switch (ch) {
|
||||||
break;
|
case '&':
|
||||||
case '>':
|
innerHTML += '&';
|
||||||
out += '>';
|
break;
|
||||||
break;
|
case '<':
|
||||||
default:
|
innerHTML += '<';
|
||||||
if (ch <= ' ') {
|
break;
|
||||||
out += ' ';
|
case '>':
|
||||||
} else {
|
innerHTML += '>';
|
||||||
out += ch;
|
break;
|
||||||
}
|
default:
|
||||||
break;
|
if (ch <= ' ') {
|
||||||
}
|
innerHTML += ' ';
|
||||||
if (ch_width === 2) {
|
} else {
|
||||||
out += '</span>';
|
innerHTML += ch;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
attr = data;
|
attr = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attr !== this._terminal.defAttr) {
|
if (innerHTML && !currentElement) {
|
||||||
out += '</span>';
|
currentElement = this._spanElementObjectPool.acquire();
|
||||||
|
}
|
||||||
|
if (currentElement) {
|
||||||
|
if (innerHTML) {
|
||||||
|
currentElement.innerHTML = innerHTML;
|
||||||
|
innerHTML = '';
|
||||||
|
}
|
||||||
|
documentFragment.appendChild(currentElement);
|
||||||
|
currentElement = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._terminal.children[y].innerHTML = out;
|
this._terminal.children[y].appendChild(documentFragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parent) {
|
if (parent) {
|
||||||
@@ -289,16 +321,17 @@ export class Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// if bold is broken, we can't
|
// If bold is broken, we can't use it in the terminal.
|
||||||
// use it in the terminal.
|
function checkBoldBroken(terminal) {
|
||||||
function checkBoldBroken(document) {
|
const document = terminal.ownerDocument;
|
||||||
const body = document.getElementsByTagName('body')[0];
|
|
||||||
const el = document.createElement('span');
|
const el = document.createElement('span');
|
||||||
el.innerHTML = 'hello world';
|
el.innerHTML = 'hello world';
|
||||||
body.appendChild(el);
|
terminal.appendChild(el);
|
||||||
const w1 = el.scrollWidth;
|
const w1 = el.offsetWidth;
|
||||||
|
const h1 = el.offsetHeight;
|
||||||
el.style.fontWeight = 'bold';
|
el.style.fontWeight = 'bold';
|
||||||
const w2 = el.scrollWidth;
|
const w2 = el.offsetWidth;
|
||||||
body.removeChild(el);
|
const h2 = el.offsetHeight;
|
||||||
return w1 !== w2;
|
terminal.removeChild(el);
|
||||||
|
return w1 !== w2 || h1 !== h2;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ export type LinkMatcher = {
|
|||||||
priority?: number
|
priority?: number
|
||||||
};
|
};
|
||||||
export type LinkMatcherHandler = (event: MouseEvent, uri: string) => boolean | void;
|
export type LinkMatcherHandler = (event: MouseEvent, uri: string) => boolean | void;
|
||||||
export type LinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void;
|
export type LinkMatcherValidationCallback = (uri: string, element: HTMLElement, callback: (isValid: boolean) => void) => void;
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
subjectRow.innerHTML = 'W'; // Common character for measuring width, although on monospace
|
subjectRow.innerHTML = 'W'; // Common character for measuring width, although on monospace
|
||||||
characterWidth = subjectRow.getBoundingClientRect().width;
|
characterWidth = subjectRow.getBoundingClientRect().width;
|
||||||
subjectRow.style.display = ''; // Revert style before calculating height, since they differ.
|
subjectRow.style.display = ''; // Revert style before calculating height, since they differ.
|
||||||
characterHeight = parseInt(subjectRow.offsetHeight);
|
characterHeight = subjectRow.getBoundingClientRect().height;
|
||||||
subjectRow.innerHTML = contentBuffer;
|
subjectRow.innerHTML = contentBuffer;
|
||||||
|
|
||||||
rows = parseInt(availableHeight / characterHeight);
|
rows = parseInt(availableHeight / characterHeight);
|
||||||
|
|||||||
@@ -16,3 +16,14 @@ describe('evaluateCopiedTextProcessing', function () {
|
|||||||
assert.equal(processedText.indexOf(nonBreakingSpace), -1);
|
assert.equal(processedText.indexOf(nonBreakingSpace), -1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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\nbar\n');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -38,6 +38,17 @@ export function prepareTextForClipboard(text: string): string {
|
|||||||
return processedText;
|
return processedText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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, '\n');
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds copy functionality to the given terminal.
|
* Binds copy functionality to the given terminal.
|
||||||
* @param {ClipboardEvent} ev The original copy event to be handled
|
* @param {ClipboardEvent} ev The original copy event to be handled
|
||||||
@@ -68,8 +79,11 @@ export function pasteHandler(ev: ClipboardEvent, term: ITerminal) {
|
|||||||
let text: string;
|
let text: string;
|
||||||
|
|
||||||
let dispatchPaste = function(text) {
|
let dispatchPaste = function(text) {
|
||||||
|
text = prepareTextForTerminal(text, term.browser.isMSWindows);
|
||||||
term.handler(text);
|
term.handler(text);
|
||||||
term.textarea.value = '';
|
term.textarea.value = '';
|
||||||
|
term.emit('paste', text);
|
||||||
|
|
||||||
return term.cancel(ev);
|
return term.cancel(ev);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -21,7 +21,8 @@ describe('xterm.js', function() {
|
|||||||
};
|
};
|
||||||
xterm.element = {
|
xterm.element = {
|
||||||
classList: {
|
classList: {
|
||||||
toggle: function(){}
|
toggle: function(){},
|
||||||
|
remove: function(){}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { assert } from 'chai';
|
||||||
|
import { DomElementObjectPool } from './DomElementObjectPool';
|
||||||
|
|
||||||
|
class MockDocument {
|
||||||
|
private _attr: {[key: string]: string} = {};
|
||||||
|
constructor() {}
|
||||||
|
public getAttribute(key: string): string { return this._attr[key]; };
|
||||||
|
public setAttribute(key: string, value: string): void { this._attr[key] = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('DomElementObjectPool', () => {
|
||||||
|
let pool: DomElementObjectPool;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
pool = new DomElementObjectPool('span');
|
||||||
|
(<any>global).document = {
|
||||||
|
createElement: () => new MockDocument()
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should acquire distinct elements', () => {
|
||||||
|
const element1 = pool.acquire();
|
||||||
|
const element2 = pool.acquire();
|
||||||
|
assert.notEqual(element1, element2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should acquire released elements', () => {
|
||||||
|
const element = pool.acquire();
|
||||||
|
pool.release(element);
|
||||||
|
assert.equal(pool.acquire(), element);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle a series of acquisitions and releases', () => {
|
||||||
|
const element1 = pool.acquire();
|
||||||
|
const element2 = pool.acquire();
|
||||||
|
pool.release(element1);
|
||||||
|
assert.equal(pool.acquire(), element1);
|
||||||
|
pool.release(element1);
|
||||||
|
pool.release(element2);
|
||||||
|
assert.equal(pool.acquire(), element2);
|
||||||
|
assert.equal(pool.acquire(), element1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when releasing an element that was not acquired', () => {
|
||||||
|
assert.throws(() => pool.release(document.createElement('span')));
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
* @module xterm/utils/DomElementObjectPool
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object pool that manages acquisition and releasing of DOM elements for
|
||||||
|
* when reuse is desirable.
|
||||||
|
*/
|
||||||
|
export class DomElementObjectPool {
|
||||||
|
private static readonly OBJECT_ID_ATTRIBUTE = 'data-obj-id';
|
||||||
|
|
||||||
|
private static _objectCount = 0;
|
||||||
|
|
||||||
|
private _type: string;
|
||||||
|
private _pool: HTMLElement[];
|
||||||
|
private _inUse: {[key: string]: HTMLElement};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param type The DOM element type (div, span, etc.).
|
||||||
|
*/
|
||||||
|
constructor(private type: string) {
|
||||||
|
this._type = type;
|
||||||
|
this._pool = [];
|
||||||
|
this._inUse = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acquire an element from the pool, creating it if the pool is empty.
|
||||||
|
*/
|
||||||
|
public acquire(): HTMLElement {
|
||||||
|
let element: HTMLElement;
|
||||||
|
if (this._pool.length === 0) {
|
||||||
|
element = this._createNew();
|
||||||
|
} else {
|
||||||
|
element = this._pool.pop();
|
||||||
|
}
|
||||||
|
this._inUse[element.getAttribute(DomElementObjectPool.OBJECT_ID_ATTRIBUTE)] = element;
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release an element back into the pool. It's up to the caller of this
|
||||||
|
* function to ensure that all external references to the element have been
|
||||||
|
* removed.
|
||||||
|
* @param element The element being released.
|
||||||
|
*/
|
||||||
|
public release(element: HTMLElement): void {
|
||||||
|
if (!this._inUse[element.getAttribute(DomElementObjectPool.OBJECT_ID_ATTRIBUTE)]) {
|
||||||
|
throw new Error('Could not release an element not yet acquired');
|
||||||
|
}
|
||||||
|
delete this._inUse[element.getAttribute(DomElementObjectPool.OBJECT_ID_ATTRIBUTE)];
|
||||||
|
this._cleanElement(element);
|
||||||
|
this._pool.push(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new element for the pool.
|
||||||
|
*/
|
||||||
|
private _createNew(): HTMLElement {
|
||||||
|
const element = document.createElement(this._type);
|
||||||
|
const id = DomElementObjectPool._objectCount++;
|
||||||
|
element.setAttribute(DomElementObjectPool.OBJECT_ID_ATTRIBUTE, id.toString(10));
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets an element back to a "clean state".
|
||||||
|
* @param element The element to be cleaned.
|
||||||
|
*/
|
||||||
|
private _cleanElement(element: HTMLElement): void {
|
||||||
|
element.className = '';
|
||||||
|
element.innerHTML = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CharMeasure } from './CharMeasure';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets coordinates within the terminal for a particular mouse event. The result
|
||||||
|
* is returned as an array in the form [x, y] instead of an object as it's a
|
||||||
|
* little faster and this function is used in some low level code.
|
||||||
|
* @param event The mouse event.
|
||||||
|
* @param rowContainer The terminal's row container.
|
||||||
|
* @param charMeasure The char measure object used to determine character sizes.
|
||||||
|
*/
|
||||||
|
export function getCoords(event: MouseEvent, rowContainer: HTMLElement, charMeasure: CharMeasure): [number, number] {
|
||||||
|
// Ignore browsers that don't support MouseEvent.pageX
|
||||||
|
if (event.pageX == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let x = event.pageX;
|
||||||
|
let y = event.pageY;
|
||||||
|
let el = rowContainer;
|
||||||
|
|
||||||
|
// Converts the coordinates from being relative to the document to being
|
||||||
|
// relative to the terminal.
|
||||||
|
while (el && el !== self.document.documentElement) {
|
||||||
|
x -= el.offsetLeft;
|
||||||
|
y -= el.offsetTop;
|
||||||
|
el = 'offsetParent' in el ? <HTMLElement>el.offsetParent : <HTMLElement>el.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to cols/rows
|
||||||
|
x = Math.ceil(x / charMeasure.width);
|
||||||
|
y = Math.ceil(y / charMeasure.height);
|
||||||
|
|
||||||
|
return [x, y];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets coordinates within the terminal for a particular mouse event, wrapping
|
||||||
|
* them to the bounds of the terminal and adding 32 to both the x and y values
|
||||||
|
* as expected by xterm.
|
||||||
|
* @param event The mouse event.
|
||||||
|
* @param rowContainer The terminal's row container.
|
||||||
|
* @param charMeasure The char measure object used to determine character sizes.
|
||||||
|
* @param colCount The number of columns in the terminal.
|
||||||
|
* @param rowCount The number of rows in the terminal.
|
||||||
|
*/
|
||||||
|
export function getRawByteCoords(event: MouseEvent, rowContainer: HTMLElement, charMeasure: CharMeasure, colCount: number, rowCount: number): { x: number, y: number } {
|
||||||
|
const coords = getCoords(event, rowContainer, charMeasure);
|
||||||
|
let x = coords[0];
|
||||||
|
let y = coords[1];
|
||||||
|
|
||||||
|
// Ensure coordinates are within the terminal viewport.
|
||||||
|
x = Math.min(Math.max(x, 0), colCount);
|
||||||
|
y = Math.min(Math.max(y, 0), rowCount);
|
||||||
|
|
||||||
|
// xterm sends raw bytes and starts at 32 (SP) for each.
|
||||||
|
x += 32;
|
||||||
|
y += 32;
|
||||||
|
|
||||||
|
return { x, y };
|
||||||
|
}
|
||||||
+11
-17
@@ -86,7 +86,7 @@
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar) .terminal-cursor {
|
.terminal.focus:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar) .terminal-cursor {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
@@ -97,16 +97,9 @@
|
|||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar).focus.xterm-cursor-blink .terminal-cursor {
|
.terminal:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar).focus.xterm-cursor-blink-on .terminal-cursor {
|
||||||
animation: xterm-cursor-blink 1.2s infinite step-end;
|
background-color: transparent;
|
||||||
}
|
color: inherit;
|
||||||
|
|
||||||
@keyframes xterm-cursor-blink {
|
|
||||||
0% { }
|
|
||||||
50% {
|
|
||||||
background-color: transparent;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal.xterm-cursor-style-bar .terminal-cursor,
|
.terminal.xterm-cursor-style-bar .terminal-cursor,
|
||||||
@@ -132,13 +125,13 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
}
|
}
|
||||||
|
.terminal.xterm-cursor-style-bar.focus.xterm-cursor-blink.xterm-cursor-blink-on .terminal-cursor::before,
|
||||||
|
.terminal.xterm-cursor-style-underline.focus.xterm-cursor-blink.xterm-cursor-blink-on .terminal-cursor::before {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
.terminal.xterm-cursor-style-bar.focus.xterm-cursor-blink .terminal-cursor::before,
|
.terminal.xterm-cursor-style-bar.focus.xterm-cursor-blink .terminal-cursor::before,
|
||||||
.terminal.xterm-cursor-style-underline.focus.xterm-cursor-blink .terminal-cursor::before {
|
.terminal.xterm-cursor-style-underline.focus.xterm-cursor-blink .terminal-cursor::before {
|
||||||
animation: xterm-cursor-non-bar-blink 1.2s infinite step-end;
|
background-color: #fff;
|
||||||
}
|
|
||||||
@keyframes xterm-cursor-non-bar-blink {
|
|
||||||
0% { background-color: #fff; }
|
|
||||||
50% { background-color: transparent; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal .composition-view {
|
.terminal .composition-view {
|
||||||
@@ -160,7 +153,8 @@
|
|||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal .xterm-wide-char {
|
.terminal .xterm-wide-char,
|
||||||
|
.terminal .xterm-normal-char {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+92
-65
@@ -24,6 +24,7 @@ import { CharMeasure } from './utils/CharMeasure';
|
|||||||
import * as Browser from './utils/Browser';
|
import * as Browser from './utils/Browser';
|
||||||
import * as Keyboard from './utils/Keyboard';
|
import * as Keyboard from './utils/Keyboard';
|
||||||
import { CHARSETS } from './Charsets';
|
import { CHARSETS } from './Charsets';
|
||||||
|
import { getRawByteCoords } from './utils/Mouse';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Terminal Emulation References:
|
* Terminal Emulation References:
|
||||||
@@ -52,6 +53,13 @@ var WRITE_BUFFER_PAUSE_THRESHOLD = 5;
|
|||||||
*/
|
*/
|
||||||
var WRITE_BATCH_SIZE = 300;
|
var WRITE_BATCH_SIZE = 300;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time between cursor blinks. This is driven by JS rather than a CSS
|
||||||
|
* animation due to a bug in Chromium that causes it to use excessive CPU time.
|
||||||
|
* See https://github.com/Microsoft/vscode/issues/22900
|
||||||
|
*/
|
||||||
|
var CURSOR_BLINK_INTERVAL = 600;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Terminal
|
* Terminal
|
||||||
*/
|
*/
|
||||||
@@ -159,6 +167,7 @@ function Terminal(options) {
|
|||||||
this.scrollTop = 0;
|
this.scrollTop = 0;
|
||||||
this.scrollBottom = this.rows - 1;
|
this.scrollBottom = this.rows - 1;
|
||||||
this.customKeydownHandler = null;
|
this.customKeydownHandler = null;
|
||||||
|
this.cursorBlinkInterval = null;
|
||||||
|
|
||||||
// modes
|
// modes
|
||||||
this.applicationKeypad = false;
|
this.applicationKeypad = false;
|
||||||
@@ -211,7 +220,7 @@ function Terminal(options) {
|
|||||||
this.parser = new Parser(this.inputHandler, this);
|
this.parser = new Parser(this.inputHandler, this);
|
||||||
// Reuse renderer if the Terminal is being recreated via a Terminal.reset call.
|
// Reuse renderer if the Terminal is being recreated via a Terminal.reset call.
|
||||||
this.renderer = this.renderer || null;
|
this.renderer = this.renderer || null;
|
||||||
this.linkifier = this.linkifier || null;;
|
this.linkifier = this.linkifier || new Linkifier();
|
||||||
|
|
||||||
// user input states
|
// user input states
|
||||||
this.writeBuffer = [];
|
this.writeBuffer = [];
|
||||||
@@ -423,7 +432,7 @@ Terminal.prototype.setOption = function(key, value) {
|
|||||||
this[key] = value;
|
this[key] = value;
|
||||||
this.options[key] = value;
|
this.options[key] = value;
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'cursorBlink': this.element.classList.toggle('xterm-cursor-blink', value); break;
|
case 'cursorBlink': this.setCursorBlinking(value); break;
|
||||||
case 'cursorStyle':
|
case 'cursorStyle':
|
||||||
// Style 'block' applies with no class
|
// Style 'block' applies with no class
|
||||||
this.element.classList.toggle(`xterm-cursor-style-underline`, value === 'underline');
|
this.element.classList.toggle(`xterm-cursor-style-underline`, value === 'underline');
|
||||||
@@ -433,6 +442,29 @@ Terminal.prototype.setOption = function(key, value) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Terminal.prototype.restartCursorBlinking = function () {
|
||||||
|
this.setCursorBlinking(this.options.cursorBlink);
|
||||||
|
};
|
||||||
|
|
||||||
|
Terminal.prototype.setCursorBlinking = function (enabled) {
|
||||||
|
this.element.classList.toggle('xterm-cursor-blink', enabled);
|
||||||
|
this.clearCursorBlinkingInterval();
|
||||||
|
if (enabled) {
|
||||||
|
var self = this;
|
||||||
|
this.cursorBlinkInterval = setInterval(function () {
|
||||||
|
self.element.classList.toggle('xterm-cursor-blink-on');
|
||||||
|
}, CURSOR_BLINK_INTERVAL);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Terminal.prototype.clearCursorBlinkingInterval = function () {
|
||||||
|
this.element.classList.remove('xterm-cursor-blink-on');
|
||||||
|
if (this.cursorBlinkInterval) {
|
||||||
|
clearInterval(this.cursorBlinkInterval);
|
||||||
|
this.cursorBlinkInterval = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds the desired focus behavior on a given terminal object.
|
* Binds the desired focus behavior on a given terminal object.
|
||||||
*
|
*
|
||||||
@@ -445,6 +477,7 @@ Terminal.bindFocus = function (term) {
|
|||||||
}
|
}
|
||||||
term.element.classList.add('focus');
|
term.element.classList.add('focus');
|
||||||
term.showCursor();
|
term.showCursor();
|
||||||
|
term.restartCursorBlinking.apply(term);
|
||||||
Terminal.focus = term;
|
Terminal.focus = term;
|
||||||
term.emit('focus', {terminal: term});
|
term.emit('focus', {terminal: term});
|
||||||
});
|
});
|
||||||
@@ -469,6 +502,7 @@ Terminal.bindBlur = function (term) {
|
|||||||
term.send(C0.ESC + '[O');
|
term.send(C0.ESC + '[O');
|
||||||
}
|
}
|
||||||
term.element.classList.remove('focus');
|
term.element.classList.remove('focus');
|
||||||
|
term.clearCursorBlinkingInterval.apply(term);
|
||||||
Terminal.focus = null;
|
Terminal.focus = null;
|
||||||
term.emit('blur', {terminal: term});
|
term.emit('blur', {terminal: term});
|
||||||
});
|
});
|
||||||
@@ -574,8 +608,9 @@ Terminal.prototype.insertRow = function (row) {
|
|||||||
* Opens the terminal within an element.
|
* Opens the terminal within an element.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} parent The element to create the terminal within.
|
* @param {HTMLElement} parent The element to create the terminal within.
|
||||||
|
* @param {boolean} focus Focus the terminal, after it gets instantiated in the DOM
|
||||||
*/
|
*/
|
||||||
Terminal.prototype.open = function(parent) {
|
Terminal.prototype.open = function(parent, focus) {
|
||||||
var self=this, i=0, div;
|
var self=this, i=0, div;
|
||||||
|
|
||||||
this.parent = parent || this.parent;
|
this.parent = parent || this.parent;
|
||||||
@@ -594,9 +629,9 @@ Terminal.prototype.open = function(parent) {
|
|||||||
this.element.classList.add('terminal');
|
this.element.classList.add('terminal');
|
||||||
this.element.classList.add('xterm');
|
this.element.classList.add('xterm');
|
||||||
this.element.classList.add('xterm-theme-' + this.theme);
|
this.element.classList.add('xterm-theme-' + this.theme);
|
||||||
this.element.classList.toggle('xterm-cursor-blink', this.options.cursorBlink);
|
this.setCursorBlinking(this.options.cursorBlink);
|
||||||
|
|
||||||
this.element.style.height
|
this.element.style.height;
|
||||||
this.element.setAttribute('tabindex', 0);
|
this.element.setAttribute('tabindex', 0);
|
||||||
|
|
||||||
this.viewportElement = document.createElement('div');
|
this.viewportElement = document.createElement('div');
|
||||||
@@ -612,7 +647,7 @@ Terminal.prototype.open = function(parent) {
|
|||||||
this.rowContainer.classList.add('xterm-rows');
|
this.rowContainer.classList.add('xterm-rows');
|
||||||
this.element.appendChild(this.rowContainer);
|
this.element.appendChild(this.rowContainer);
|
||||||
this.children = [];
|
this.children = [];
|
||||||
this.linkifier = new Linkifier(document, this.children);
|
this.linkifier.attachToDom(document, this.children);
|
||||||
|
|
||||||
// Create the container that will hold helpers like the textarea for
|
// Create the container that will hold helpers like the textarea for
|
||||||
// capturing DOM Events. Then produce the helpers.
|
// capturing DOM Events. Then produce the helpers.
|
||||||
@@ -663,8 +698,23 @@ Terminal.prototype.open = function(parent) {
|
|||||||
// need to be taken on the document.
|
// need to be taken on the document.
|
||||||
this.initGlobal();
|
this.initGlobal();
|
||||||
|
|
||||||
// Ensure there is a Terminal.focus.
|
/**
|
||||||
this.focus();
|
* Automatic focus functionality.
|
||||||
|
* TODO: Default to `false` starting with xterm.js 3.0.
|
||||||
|
*/
|
||||||
|
if (typeof focus == 'undefined') {
|
||||||
|
let message = 'You did not pass the `focus` argument in `Terminal.prototype.open()`.\n';
|
||||||
|
|
||||||
|
message += 'The `focus` argument now defaults to `true` but starting with xterm.js 3.0 ';
|
||||||
|
message += 'it will default to `false`.';
|
||||||
|
|
||||||
|
console.warn(message);
|
||||||
|
focus = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (focus) {
|
||||||
|
this.focus();
|
||||||
|
}
|
||||||
|
|
||||||
on(this.element, 'click', function() {
|
on(this.element, 'click', function() {
|
||||||
var selection = document.getSelection(),
|
var selection = document.getSelection(),
|
||||||
@@ -711,7 +761,9 @@ Terminal.loadAddon = function(addon, callback) {
|
|||||||
* character width has been changed.
|
* character width has been changed.
|
||||||
*/
|
*/
|
||||||
Terminal.prototype.updateCharSizeCSS = function() {
|
Terminal.prototype.updateCharSizeCSS = function() {
|
||||||
this.charSizeStyleElement.textContent = '.xterm-wide-char{width:' + (this.charMeasure.width * 2) + 'px;}';
|
this.charSizeStyleElement.textContent =
|
||||||
|
`.xterm-wide-char{width:${this.charMeasure.width * 2}px;}` +
|
||||||
|
`.xterm-normal-char{width:${this.charMeasure.width}px;}`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -738,7 +790,7 @@ Terminal.prototype.bindMouse = function() {
|
|||||||
button = getButton(ev);
|
button = getButton(ev);
|
||||||
|
|
||||||
// get mouse coordinates
|
// get mouse coordinates
|
||||||
pos = getCoords(ev);
|
pos = getRawByteCoords(ev, self.rowContainer, self.charMeasure, self.cols, self.rows);
|
||||||
if (!pos) return;
|
if (!pos) return;
|
||||||
|
|
||||||
sendEvent(button, pos);
|
sendEvent(button, pos);
|
||||||
@@ -766,7 +818,7 @@ Terminal.prototype.bindMouse = function() {
|
|||||||
var button = pressed
|
var button = pressed
|
||||||
, pos;
|
, pos;
|
||||||
|
|
||||||
pos = getCoords(ev);
|
pos = getRawByteCoords(ev, self.rowContainer, self.charMeasure, self.cols, self.rows);
|
||||||
if (!pos) return;
|
if (!pos) return;
|
||||||
|
|
||||||
// buttons marked as motions
|
// buttons marked as motions
|
||||||
@@ -941,50 +993,6 @@ Terminal.prototype.bindMouse = function() {
|
|||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
// mouse coordinates measured in cols/rows
|
|
||||||
function getCoords(ev) {
|
|
||||||
var x, y, w, h, el;
|
|
||||||
|
|
||||||
// ignore browsers without pageX for now
|
|
||||||
if (ev.pageX == null) return;
|
|
||||||
|
|
||||||
x = ev.pageX;
|
|
||||||
y = ev.pageY;
|
|
||||||
el = self.element;
|
|
||||||
|
|
||||||
// should probably check offsetParent
|
|
||||||
// but this is more portable
|
|
||||||
while (el && el !== self.document.documentElement) {
|
|
||||||
x -= el.offsetLeft;
|
|
||||||
y -= el.offsetTop;
|
|
||||||
el = 'offsetParent' in el
|
|
||||||
? el.offsetParent
|
|
||||||
: el.parentNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert to cols/rows
|
|
||||||
x = Math.ceil(x / self.charMeasure.width);
|
|
||||||
y = Math.ceil(y / self.charMeasure.height);
|
|
||||||
|
|
||||||
// be sure to avoid sending
|
|
||||||
// bad positions to the program
|
|
||||||
if (x < 0) x = 0;
|
|
||||||
if (x > self.cols) x = self.cols;
|
|
||||||
if (y < 0) y = 0;
|
|
||||||
if (y > self.rows) y = self.rows;
|
|
||||||
|
|
||||||
// xterm sends raw bytes and
|
|
||||||
// starts at 32 (SP) for each.
|
|
||||||
x += 32;
|
|
||||||
y += 32;
|
|
||||||
|
|
||||||
return {
|
|
||||||
x: x,
|
|
||||||
y: y,
|
|
||||||
type: 'wheel'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
on(el, 'mousedown', function(ev) {
|
on(el, 'mousedown', function(ev) {
|
||||||
if (!self.mouseEvents) return;
|
if (!self.mouseEvents) return;
|
||||||
|
|
||||||
@@ -1244,7 +1252,13 @@ Terminal.prototype.innerWrite = function() {
|
|||||||
this.refreshStart = this.y;
|
this.refreshStart = this.y;
|
||||||
this.refreshEnd = this.y;
|
this.refreshEnd = this.y;
|
||||||
|
|
||||||
this.parser.parse(data);
|
// HACK: Set the parser state based on it's state at the time of return.
|
||||||
|
// This works around the bug #662 which saw the parser state reset in the
|
||||||
|
// middle of parsing escape sequence in two chunks. For some reason the
|
||||||
|
// state of the parser resets to 0 after exiting parser.parse. This change
|
||||||
|
// just sets the state back based on the correct return statement.
|
||||||
|
var state = this.parser.parse(data);
|
||||||
|
this.parser.setState(state);
|
||||||
|
|
||||||
this.updateRange(this.y);
|
this.updateRange(this.y);
|
||||||
this.refresh(this.refreshStart, this.refreshEnd);
|
this.refresh(this.refreshStart, this.refreshEnd);
|
||||||
@@ -1286,11 +1300,26 @@ Terminal.prototype.attachCustomKeydownHandler = function(customKeydownHandler) {
|
|||||||
* reconstructed. Calling this with null will remove the handler.
|
* reconstructed. Calling this with null will remove the handler.
|
||||||
* @param {LinkHandler} handler The handler callback function.
|
* @param {LinkHandler} handler The handler callback function.
|
||||||
*/
|
*/
|
||||||
Terminal.prototype.attachHypertextLinkHandler = function(handler) {
|
Terminal.prototype.setHypertextLinkHandler = function(handler) {
|
||||||
if (!this.linkifier) {
|
if (!this.linkifier) {
|
||||||
throw new Error('Cannot attach a hypertext link handler before Terminal.open is called');
|
throw new Error('Cannot attach a hypertext link handler before Terminal.open is called');
|
||||||
}
|
}
|
||||||
this.linkifier.attachHypertextLinkHandler(handler);
|
this.linkifier.setHypertextLinkHandler(handler);
|
||||||
|
// Refresh to force links to refresh
|
||||||
|
this.refresh(0, this.rows - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches a validation callback for hypertext links. This is useful to use
|
||||||
|
* validation logic or to do something with the link's element and url.
|
||||||
|
* @param {LinkMatcherValidationCallback} callback The callback to use, this can
|
||||||
|
* be cleared with null.
|
||||||
|
*/
|
||||||
|
Terminal.prototype.setHypertextValidationCallback = function(handler) {
|
||||||
|
if (!this.linkifier) {
|
||||||
|
throw new Error('Cannot attach a hypertext validation callback before Terminal.open is called');
|
||||||
|
}
|
||||||
|
this.linkifier.setHypertextValidationCallback(handler);
|
||||||
// Refresh to force links to refresh
|
// Refresh to force links to refresh
|
||||||
this.refresh(0, this.rows - 1);
|
this.refresh(0, this.rows - 1);
|
||||||
}
|
}
|
||||||
@@ -1336,6 +1365,8 @@ Terminal.prototype.keyDown = function(ev) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.restartCursorBlinking();
|
||||||
|
|
||||||
if (!this.compositionHelper.keydown.bind(this.compositionHelper)(ev)) {
|
if (!this.compositionHelper.keydown.bind(this.compositionHelper)(ev)) {
|
||||||
if (this.ybase !== this.ydisp) {
|
if (this.ybase !== this.ydisp) {
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
@@ -1805,14 +1836,8 @@ Terminal.prototype.resize = function(x, y) {
|
|||||||
this.lines.get(i).push(ch);
|
this.lines.get(i).push(ch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // (j > x)
|
|
||||||
i = this.lines.length;
|
|
||||||
while (i--) {
|
|
||||||
while (this.lines.get(i).length > x) {
|
|
||||||
this.lines.get(i).pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cols = x;
|
this.cols = x;
|
||||||
this.setupStops(this.cols);
|
this.setupStops(this.cols);
|
||||||
|
|
||||||
@@ -2149,8 +2174,10 @@ Terminal.prototype.reset = function() {
|
|||||||
this.options.rows = this.rows;
|
this.options.rows = this.rows;
|
||||||
this.options.cols = this.cols;
|
this.options.cols = this.cols;
|
||||||
var customKeydownHandler = this.customKeydownHandler;
|
var customKeydownHandler = this.customKeydownHandler;
|
||||||
|
var cursorBlinkInterval = this.cursorBlinkInterval;
|
||||||
Terminal.call(this, this.options);
|
Terminal.call(this, this.options);
|
||||||
this.customKeydownHandler = customKeydownHandler;
|
this.customKeydownHandler = customKeydownHandler;
|
||||||
|
this.cursorBlinkInterval = cursorBlinkInterval;
|
||||||
this.refresh(0, this.rows - 1);
|
this.refresh(0, this.rows - 1);
|
||||||
this.viewport.syncScrollArea();
|
this.viewport.syncScrollArea();
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-8
@@ -12,13 +12,6 @@
|
|||||||
"src/**/*"
|
"src/**/*"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"src/addons/**/*",
|
"src/addons/**/*"
|
||||||
"build",
|
|
||||||
"demo",
|
|
||||||
"dist",
|
|
||||||
"out",
|
|
||||||
"test",
|
|
||||||
"node_modules",
|
|
||||||
"docs"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user